blob: 9c689aed6417835c969d869ebde69ce4b51c76b1 [file] [log] [blame]
//===----- BPFMISimplifyPatchable.cpp - MI Simplify Patchable Insts -------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This pass targets a subset of instructions like below
// ld_imm64 r1, @global
// ldd r2, r1, 0
// add r3, struct_base_reg, r2
//
// Here @global should represent an AMA (abstruct member access).
// Such an access is subject to bpf load time patching. After this pass, the
// code becomes
// ld_imm64 r1, @global
// add r3, struct_base_reg, r1
//
// Eventually, at BTF output stage, a relocation record will be generated
// for ld_imm64 which should be replaced later by bpf loader:
// r1 = <calculated field_info>
// add r3, struct_base_reg, r1
//
//===----------------------------------------------------------------------===//
#include "BPF.h"
#include "BPFCORE.h"
#include "BPFInstrInfo.h"
#include "BPFTargetMachine.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/Support/Debug.h"
using namespace llvm;
#define DEBUG_TYPE "bpf-mi-simplify-patchable"
namespace {
struct BPFMISimplifyPatchable : public MachineFunctionPass {
static char ID;
const BPFInstrInfo *TII;
MachineFunction *MF;
BPFMISimplifyPatchable() : MachineFunctionPass(ID) {
initializeBPFMISimplifyPatchablePass(*PassRegistry::getPassRegistry());
}
private:
// Initialize class variables.
void initialize(MachineFunction &MFParm);
bool removeLD(void);
public:
// Main entry point for this pass.
bool runOnMachineFunction(MachineFunction &MF) override {
if (!skipFunction(MF.getFunction())) {
initialize(MF);
}
return removeLD();
}
};
// Initialize class variables.
void BPFMISimplifyPatchable::initialize(MachineFunction &MFParm) {
MF = &MFParm;
TII = MF->getSubtarget<BPFSubtarget>().getInstrInfo();
LLVM_DEBUG(dbgs() << "*** BPF simplify patchable insts pass ***\n\n");
}
/// Remove unneeded Load instructions.
bool BPFMISimplifyPatchable::removeLD() {
MachineRegisterInfo *MRI = &MF->getRegInfo();
MachineInstr *ToErase = nullptr;
bool Changed = false;
for (MachineBasicBlock &MBB : *MF) {
for (MachineInstr &MI : MBB) {
if (ToErase) {
ToErase->eraseFromParent();
ToErase = nullptr;
}
// Ensure the register format is LOAD <reg>, <reg>, 0
if (MI.getOpcode() != BPF::LDD && MI.getOpcode() != BPF::LDW &&
MI.getOpcode() != BPF::LDH && MI.getOpcode() != BPF::LDB &&
MI.getOpcode() != BPF::LDW32 && MI.getOpcode() != BPF::LDH32 &&
MI.getOpcode() != BPF::LDB32)
continue;
if (!MI.getOperand(0).isReg() || !MI.getOperand(1).isReg())
continue;
if (!MI.getOperand(2).isImm() || MI.getOperand(2).getImm())
continue;
Register DstReg = MI.getOperand(0).getReg();
Register SrcReg = MI.getOperand(1).getReg();
MachineInstr *DefInst = MRI->getUniqueVRegDef(SrcReg);
if (!DefInst)
continue;
bool IsCandidate = false;
if (DefInst->getOpcode() == BPF::LD_imm64) {
const MachineOperand &MO = DefInst->getOperand(1);
if (MO.isGlobal()) {
const GlobalValue *GVal = MO.getGlobal();
auto *GVar = dyn_cast<GlobalVariable>(GVal);
if (GVar) {
// Global variables representing structure offset or
// patchable extern globals.
if (GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr)) {
assert(MI.getOperand(2).getImm() == 0);
IsCandidate = true;
}
}
}
}
if (!IsCandidate)
continue;
auto Begin = MRI->use_begin(DstReg), End = MRI->use_end();
decltype(End) NextI;
for (auto I = Begin; I != End; I = NextI) {
NextI = std::next(I);
I->setReg(SrcReg);
}
ToErase = &MI;
Changed = true;
}
}
return Changed;
}
} // namespace
INITIALIZE_PASS(BPFMISimplifyPatchable, DEBUG_TYPE,
"BPF PreEmit SimplifyPatchable", false, false)
char BPFMISimplifyPatchable::ID = 0;
FunctionPass *llvm::createBPFMISimplifyPatchablePass() {
return new BPFMISimplifyPatchable();
}