diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2019-08-20 20:50:12 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2019-08-20 20:50:12 +0000 |
commit | e6d1592492a3a379186bfb02bd0f4eda0669c0d5 (patch) | |
tree | 599ab169a01f1c86eda9adc774edaedde2f2db5b /lib/Target/BPF/BPFMISimplifyPatchable.cpp | |
parent | 1a56a5ead7a2e84bee8240f5f6b033b5f1707154 (diff) |
Diffstat (limited to 'lib/Target/BPF/BPFMISimplifyPatchable.cpp')
-rw-r--r-- | lib/Target/BPF/BPFMISimplifyPatchable.cpp | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/lib/Target/BPF/BPFMISimplifyPatchable.cpp b/lib/Target/BPF/BPFMISimplifyPatchable.cpp new file mode 100644 index 000000000000..e9114d7187e3 --- /dev/null +++ b/lib/Target/BPF/BPFMISimplifyPatchable.cpp @@ -0,0 +1,163 @@ +//===----- 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 either present a AMA (abstruct member access) or +// a patchable extern variable. And these two kinds of accesses +// are 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 offset> or <to_be_patched_extern_val> +// add r3, struct_base_reg, r1 +// or +// ld_imm64 r1, <to_be_patched_extern_val> +// 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" + +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; + + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(1).getReg(); + int64_t ImmVal = MI.getOperand(2).getImm(); + + 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(ImmVal == 0); + IsCandidate = true; + } else if (!GVar->hasInitializer() && GVar->hasExternalLinkage() && + GVar->getSection() == + BPFCoreSharedInfo::PatchableExtSecName) { + if (ImmVal == 0) + IsCandidate = true; + else + errs() << "WARNING: unhandled patchable extern " + << GVar->getName() << " with load offset " << ImmVal + << "\n"; + } + } + } + } + + 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(); +} |