1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-26 04:32:44 +01:00
llvm-mirror/lib/Target/BPF/BPFMISimplifyPatchable.cpp
Yonghong Song bed00d3292 [BPF] preserve debuginfo types for builtin __builtin__btf_type_id()
The builtin function
  u32 btf_type_id = __builtin_btf_type_id(param, 0)
can help preserve type info for the following use case:
  extern void foo(..., void *data, int size);
  int test(...) {
    struct t { int a; int b; int c; } d;
    d.a = ...; d.b = ...; d.c = ...;
    foo(..., &d, sizeof(d));
  }

The function "foo" in the above only see raw data and does not
know what type of the data is. In certain cases, e.g., logging,
the additional type information will help pretty print.

This patch handles the builtin in BPF backend. It includes
an IR pass to translate the IR intrinsic to a load of
a global variable which carries the metadata, and an MI
pass to remove the intermediate load of the global variable.
Finally, in AsmPrinter pass, proper instruction are generated.

In the above example, the second argument for __builtin_btf_type_id()
is 0, which means a relocation for local adjustment,
i.e., w.r.t. bpf program BTF change,  will be generated.
The value 1 for the second argument means
a relocation for remote adjustment, e.g., against vmlinux.

Differential Revision: https://reviews.llvm.org/D74572
2020-05-15 08:00:44 -07:00

315 lines
11 KiB
C++

//===----- 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
//
// This pass also removes the intermediate load generated in IR pass for
// __builtin_btf_type_id() intrinsic.
//
//===----------------------------------------------------------------------===//
#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);
void processCandidate(MachineRegisterInfo *MRI, MachineBasicBlock &MBB,
MachineInstr &MI, Register &SrcReg, Register &DstReg,
const GlobalValue *GVal, bool IsAma);
void processDstReg(MachineRegisterInfo *MRI, Register &DstReg,
Register &SrcReg, const GlobalValue *GVal,
bool doSrcRegProp, bool IsAma);
void processInst(MachineRegisterInfo *MRI, MachineInstr *Inst,
MachineOperand *RelocOp, const GlobalValue *GVal);
void checkADDrr(MachineRegisterInfo *MRI, MachineOperand *RelocOp,
const GlobalValue *GVal);
void checkShift(MachineRegisterInfo *MRI, MachineBasicBlock &MBB,
MachineOperand *RelocOp, const GlobalValue *GVal,
unsigned Opcode);
public:
// Main entry point for this pass.
bool runOnMachineFunction(MachineFunction &MF) override {
if (skipFunction(MF.getFunction()))
return false;
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");
}
void BPFMISimplifyPatchable::checkADDrr(MachineRegisterInfo *MRI,
MachineOperand *RelocOp, const GlobalValue *GVal) {
const MachineInstr *Inst = RelocOp->getParent();
const MachineOperand *Op1 = &Inst->getOperand(1);
const MachineOperand *Op2 = &Inst->getOperand(2);
const MachineOperand *BaseOp = (RelocOp == Op1) ? Op2 : Op1;
// Go through all uses of %1 as in %1 = ADD_rr %2, %3
const MachineOperand Op0 = Inst->getOperand(0);
auto Begin = MRI->use_begin(Op0.getReg()), End = MRI->use_end();
decltype(End) NextI;
for (auto I = Begin; I != End; I = NextI) {
NextI = std::next(I);
// The candidate needs to have a unique definition.
if (!MRI->getUniqueVRegDef(I->getReg()))
continue;
MachineInstr *DefInst = I->getParent();
unsigned Opcode = DefInst->getOpcode();
unsigned COREOp;
if (Opcode == BPF::LDB || Opcode == BPF::LDH || Opcode == BPF::LDW ||
Opcode == BPF::LDD || Opcode == BPF::STB || Opcode == BPF::STH ||
Opcode == BPF::STW || Opcode == BPF::STD)
COREOp = BPF::CORE_MEM;
else if (Opcode == BPF::LDB32 || Opcode == BPF::LDH32 ||
Opcode == BPF::LDW32 || Opcode == BPF::STB32 ||
Opcode == BPF::STH32 || Opcode == BPF::STW32)
COREOp = BPF::CORE_ALU32_MEM;
else
continue;
// It must be a form of %2 = *(type *)(%1 + 0) or *(type *)(%1 + 0) = %2.
const MachineOperand &ImmOp = DefInst->getOperand(2);
if (!ImmOp.isImm() || ImmOp.getImm() != 0)
continue;
// Reject the form:
// %1 = ADD_rr %2, %3
// *(type *)(%2 + 0) = %1
if (Opcode == BPF::STB || Opcode == BPF::STH || Opcode == BPF::STW ||
Opcode == BPF::STD || Opcode == BPF::STB32 || Opcode == BPF::STH32 ||
Opcode == BPF::STW32) {
const MachineOperand &Opnd = DefInst->getOperand(0);
if (Opnd.isReg() && Opnd.getReg() == I->getReg())
continue;
}
BuildMI(*DefInst->getParent(), *DefInst, DefInst->getDebugLoc(), TII->get(COREOp))
.add(DefInst->getOperand(0)).addImm(Opcode).add(*BaseOp)
.addGlobalAddress(GVal);
DefInst->eraseFromParent();
}
}
void BPFMISimplifyPatchable::checkShift(MachineRegisterInfo *MRI,
MachineBasicBlock &MBB, MachineOperand *RelocOp, const GlobalValue *GVal,
unsigned Opcode) {
// Relocation operand should be the operand #2.
MachineInstr *Inst = RelocOp->getParent();
if (RelocOp != &Inst->getOperand(2))
return;
BuildMI(MBB, *Inst, Inst->getDebugLoc(), TII->get(BPF::CORE_SHIFT))
.add(Inst->getOperand(0)).addImm(Opcode)
.add(Inst->getOperand(1)).addGlobalAddress(GVal);
Inst->eraseFromParent();
}
void BPFMISimplifyPatchable::processCandidate(MachineRegisterInfo *MRI,
MachineBasicBlock &MBB, MachineInstr &MI, Register &SrcReg,
Register &DstReg, const GlobalValue *GVal, bool IsAma) {
if (MRI->getRegClass(DstReg) == &BPF::GPR32RegClass) {
if (IsAma) {
// We can optimize such a pattern:
// %1:gpr = LD_imm64 @"llvm.s:0:4$0:2"
// %2:gpr32 = LDW32 %1:gpr, 0
// %3:gpr = SUBREG_TO_REG 0, %2:gpr32, %subreg.sub_32
// %4:gpr = ADD_rr %0:gpr, %3:gpr
// or similar patterns below for non-alu32 case.
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);
if (!MRI->getUniqueVRegDef(I->getReg()))
continue;
unsigned Opcode = I->getParent()->getOpcode();
if (Opcode == BPF::SUBREG_TO_REG) {
Register TmpReg = I->getParent()->getOperand(0).getReg();
processDstReg(MRI, TmpReg, DstReg, GVal, false, IsAma);
}
}
}
BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(BPF::COPY), DstReg)
.addReg(SrcReg, 0, BPF::sub_32);
return;
}
// All uses of DstReg replaced by SrcReg
processDstReg(MRI, DstReg, SrcReg, GVal, true, IsAma);
}
void BPFMISimplifyPatchable::processDstReg(MachineRegisterInfo *MRI,
Register &DstReg, Register &SrcReg, const GlobalValue *GVal,
bool doSrcRegProp, bool IsAma) {
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);
if (doSrcRegProp)
I->setReg(SrcReg);
// The candidate needs to have a unique definition.
if (IsAma && MRI->getUniqueVRegDef(I->getReg()))
processInst(MRI, I->getParent(), &*I, GVal);
}
}
// Check to see whether we could do some optimization
// to attach relocation to downstream dependent instructions.
// Two kinds of patterns are recognized below:
// Pattern 1:
// %1 = LD_imm64 @"llvm.b:0:4$0:1" <== patch_imm = 4
// %2 = LDD %1, 0 <== this insn will be removed
// %3 = ADD_rr %0, %2
// %4 = LDW[32] %3, 0 OR STW[32] %4, %3, 0
// The `%4 = ...` will be transformed to
// CORE_[ALU32_]MEM(%4, mem_opcode, %0, @"llvm.b:0:4$0:1")
// and later on, BTF emit phase will translate to
// %4 = LDW[32] %0, 4 STW[32] %4, %0, 4
// and attach a relocation to it.
// Pattern 2:
// %15 = LD_imm64 @"llvm.t:5:63$0:2" <== relocation type 5
// %16 = LDD %15, 0 <== this insn will be removed
// %17 = SRA_rr %14, %16
// The `%17 = ...` will be transformed to
// %17 = CORE_SHIFT(SRA_ri, %14, @"llvm.t:5:63$0:2")
// and later on, BTF emit phase will translate to
// %r4 = SRA_ri %r4, 63
void BPFMISimplifyPatchable::processInst(MachineRegisterInfo *MRI,
MachineInstr *Inst, MachineOperand *RelocOp, const GlobalValue *GVal) {
unsigned Opcode = Inst->getOpcode();
if (Opcode == BPF::ADD_rr)
checkADDrr(MRI, RelocOp, GVal);
else if (Opcode == BPF::SLL_rr)
checkShift(MRI, *Inst->getParent(), RelocOp, GVal, BPF::SLL_ri);
else if (Opcode == BPF::SRA_rr)
checkShift(MRI, *Inst->getParent(), RelocOp, GVal, BPF::SRA_ri);
else if (Opcode == BPF::SRL_rr)
checkShift(MRI, *Inst->getParent(), RelocOp, GVal, BPF::SRL_ri);
}
/// 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;
if (DefInst->getOpcode() != BPF::LD_imm64)
continue;
const MachineOperand &MO = DefInst->getOperand(1);
if (!MO.isGlobal())
continue;
const GlobalValue *GVal = MO.getGlobal();
auto *GVar = dyn_cast<GlobalVariable>(GVal);
if (!GVar)
continue;
// Global variables representing structure offset or type id.
bool IsAma = false;
if (GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr))
IsAma = true;
else if (!GVar->hasAttribute(BPFCoreSharedInfo::TypeIdAttr))
continue;
processCandidate(MRI, MBB, MI, SrcReg, DstReg, GVal, IsAma);
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();
}