mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 20:23:11 +01:00
bed00d3292
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
315 lines
11 KiB
C++
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();
|
|
}
|