Skip to content

Commit 6b01b46

Browse files
committed
[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
1 parent 10c10f2 commit 6b01b46

File tree

9 files changed

+400
-77
lines changed

9 files changed

+400
-77
lines changed

llvm/lib/Target/BPF/BPF.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ namespace llvm {
1616
class BPFTargetMachine;
1717

1818
ModulePass *createBPFAbstractMemberAccess(BPFTargetMachine *TM);
19+
ModulePass *createBPFPreserveDIType();
1920

2021
FunctionPass *createBPFISelDag(BPFTargetMachine &TM);
2122
FunctionPass *createBPFMISimplifyPatchablePass();
@@ -25,6 +26,7 @@ FunctionPass *createBPFMIPreEmitPeepholePass();
2526
FunctionPass *createBPFMIPreEmitCheckingPass();
2627

2728
void initializeBPFAbstractMemberAccessPass(PassRegistry&);
29+
void initializeBPFPreserveDITypePass(PassRegistry&);
2830
void initializeBPFMISimplifyPatchablePass(PassRegistry&);
2931
void initializeBPFMIPeepholePass(PassRegistry&);
3032
void initializeBPFMIPeepholeTruncElimPass(PassRegistry&);

llvm/lib/Target/BPF/BPFCORE.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,30 @@ namespace llvm {
1313

1414
class BPFCoreSharedInfo {
1515
public:
16-
enum OffsetRelocKind : uint32_t {
16+
enum PatchableRelocKind : uint32_t {
1717
FIELD_BYTE_OFFSET = 0,
1818
FIELD_BYTE_SIZE,
1919
FIELD_EXISTENCE,
2020
FIELD_SIGNEDNESS,
2121
FIELD_LSHIFT_U64,
2222
FIELD_RSHIFT_U64,
23+
BTF_TYPE_ID_LOCAL,
24+
BTF_TYPE_ID_REMOTE,
2325

2426
MAX_FIELD_RELOC_KIND,
2527
};
28+
29+
enum BTFTypeIdFlag : uint32_t {
30+
BTF_TYPE_ID_LOCAL_RELOC = 0,
31+
BTF_TYPE_ID_REMOTE_RELOC,
32+
33+
MAX_BTF_TYPE_ID_FLAG,
34+
};
35+
2636
/// The attribute attached to globals representing a field access
2737
static const std::string AmaAttr;
38+
/// The attribute attached to globals representing a type id
39+
static const std::string TypeIdAttr;
2840
};
2941

3042
} // namespace llvm

llvm/lib/Target/BPF/BPFMISimplifyPatchable.cpp

Lines changed: 45 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
// r1 = <calculated field_info>
2323
// add r3, struct_base_reg, r1
2424
//
25+
// This pass also removes the intermediate load generated in IR pass for
26+
// __builtin_btf_type_id() intrinsic.
27+
//
2528
//===----------------------------------------------------------------------===//
2629

2730
#include "BPF.h"
@@ -55,10 +58,10 @@ struct BPFMISimplifyPatchable : public MachineFunctionPass {
5558
bool removeLD(void);
5659
void processCandidate(MachineRegisterInfo *MRI, MachineBasicBlock &MBB,
5760
MachineInstr &MI, Register &SrcReg, Register &DstReg,
58-
const GlobalValue *GVal);
61+
const GlobalValue *GVal, bool IsAma);
5962
void processDstReg(MachineRegisterInfo *MRI, Register &DstReg,
6063
Register &SrcReg, const GlobalValue *GVal,
61-
bool doSrcRegProp);
64+
bool doSrcRegProp, bool IsAma);
6265
void processInst(MachineRegisterInfo *MRI, MachineInstr *Inst,
6366
MachineOperand *RelocOp, const GlobalValue *GVal);
6467
void checkADDrr(MachineRegisterInfo *MRI, MachineOperand *RelocOp,
@@ -155,25 +158,27 @@ void BPFMISimplifyPatchable::checkShift(MachineRegisterInfo *MRI,
155158

156159
void BPFMISimplifyPatchable::processCandidate(MachineRegisterInfo *MRI,
157160
MachineBasicBlock &MBB, MachineInstr &MI, Register &SrcReg,
158-
Register &DstReg, const GlobalValue *GVal) {
161+
Register &DstReg, const GlobalValue *GVal, bool IsAma) {
159162
if (MRI->getRegClass(DstReg) == &BPF::GPR32RegClass) {
160-
// We can optimize such a pattern:
161-
// %1:gpr = LD_imm64 @"llvm.s:0:4$0:2"
162-
// %2:gpr32 = LDW32 %1:gpr, 0
163-
// %3:gpr = SUBREG_TO_REG 0, %2:gpr32, %subreg.sub_32
164-
// %4:gpr = ADD_rr %0:gpr, %3:gpr
165-
// or similar patterns below for non-alu32 case.
166-
auto Begin = MRI->use_begin(DstReg), End = MRI->use_end();
167-
decltype(End) NextI;
168-
for (auto I = Begin; I != End; I = NextI) {
169-
NextI = std::next(I);
170-
if (!MRI->getUniqueVRegDef(I->getReg()))
171-
continue;
172-
173-
unsigned Opcode = I->getParent()->getOpcode();
174-
if (Opcode == BPF::SUBREG_TO_REG) {
175-
Register TmpReg = I->getParent()->getOperand(0).getReg();
176-
processDstReg(MRI, TmpReg, DstReg, GVal, false);
163+
if (IsAma) {
164+
// We can optimize such a pattern:
165+
// %1:gpr = LD_imm64 @"llvm.s:0:4$0:2"
166+
// %2:gpr32 = LDW32 %1:gpr, 0
167+
// %3:gpr = SUBREG_TO_REG 0, %2:gpr32, %subreg.sub_32
168+
// %4:gpr = ADD_rr %0:gpr, %3:gpr
169+
// or similar patterns below for non-alu32 case.
170+
auto Begin = MRI->use_begin(DstReg), End = MRI->use_end();
171+
decltype(End) NextI;
172+
for (auto I = Begin; I != End; I = NextI) {
173+
NextI = std::next(I);
174+
if (!MRI->getUniqueVRegDef(I->getReg()))
175+
continue;
176+
177+
unsigned Opcode = I->getParent()->getOpcode();
178+
if (Opcode == BPF::SUBREG_TO_REG) {
179+
Register TmpReg = I->getParent()->getOperand(0).getReg();
180+
processDstReg(MRI, TmpReg, DstReg, GVal, false, IsAma);
181+
}
177182
}
178183
}
179184

@@ -183,12 +188,12 @@ void BPFMISimplifyPatchable::processCandidate(MachineRegisterInfo *MRI,
183188
}
184189

185190
// All uses of DstReg replaced by SrcReg
186-
processDstReg(MRI, DstReg, SrcReg, GVal, true);
191+
processDstReg(MRI, DstReg, SrcReg, GVal, true, IsAma);
187192
}
188193

189194
void BPFMISimplifyPatchable::processDstReg(MachineRegisterInfo *MRI,
190195
Register &DstReg, Register &SrcReg, const GlobalValue *GVal,
191-
bool doSrcRegProp) {
196+
bool doSrcRegProp, bool IsAma) {
192197
auto Begin = MRI->use_begin(DstReg), End = MRI->use_end();
193198
decltype(End) NextI;
194199
for (auto I = Begin; I != End; I = NextI) {
@@ -197,7 +202,7 @@ void BPFMISimplifyPatchable::processDstReg(MachineRegisterInfo *MRI,
197202
I->setReg(SrcReg);
198203

199204
// The candidate needs to have a unique definition.
200-
if (MRI->getUniqueVRegDef(I->getReg()))
205+
if (IsAma && MRI->getUniqueVRegDef(I->getReg()))
201206
processInst(MRI, I->getParent(), &*I, GVal);
202207
}
203208
}
@@ -269,28 +274,26 @@ bool BPFMISimplifyPatchable::removeLD() {
269274
if (!DefInst)
270275
continue;
271276

272-
bool IsCandidate = false;
273-
const GlobalValue *GVal = nullptr;
274-
if (DefInst->getOpcode() == BPF::LD_imm64) {
275-
const MachineOperand &MO = DefInst->getOperand(1);
276-
if (MO.isGlobal()) {
277-
GVal = MO.getGlobal();
278-
auto *GVar = dyn_cast<GlobalVariable>(GVal);
279-
if (GVar) {
280-
// Global variables representing structure offset or
281-
// patchable extern globals.
282-
if (GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr)) {
283-
assert(MI.getOperand(2).getImm() == 0);
284-
IsCandidate = true;
285-
}
286-
}
287-
}
288-
}
277+
if (DefInst->getOpcode() != BPF::LD_imm64)
278+
continue;
279+
280+
const MachineOperand &MO = DefInst->getOperand(1);
281+
if (!MO.isGlobal())
282+
continue;
283+
284+
const GlobalValue *GVal = MO.getGlobal();
285+
auto *GVar = dyn_cast<GlobalVariable>(GVal);
286+
if (!GVar)
287+
continue;
289288

290-
if (!IsCandidate)
289+
// Global variables representing structure offset or type id.
290+
bool IsAma = false;
291+
if (GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr))
292+
IsAma = true;
293+
else if (!GVar->hasAttribute(BPFCoreSharedInfo::TypeIdAttr))
291294
continue;
292295

293-
processCandidate(MRI, MBB, MI, SrcReg, DstReg, GVal);
296+
processCandidate(MRI, MBB, MI, SrcReg, DstReg, GVal, IsAma);
294297

295298
ToErase = &MI;
296299
Changed = true;
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
//===----------- BPFPreserveDIType.cpp - Preserve DebugInfo Types ---------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Preserve Debuginfo types encoded in __builtin_btf_type_id() metadata.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "BPF.h"
14+
#include "BPFCORE.h"
15+
#include "llvm/IR/DebugInfoMetadata.h"
16+
#include "llvm/IR/GlobalVariable.h"
17+
#include "llvm/IR/Instruction.h"
18+
#include "llvm/IR/Instructions.h"
19+
#include "llvm/IR/Module.h"
20+
#include "llvm/IR/Type.h"
21+
#include "llvm/IR/User.h"
22+
#include "llvm/IR/Value.h"
23+
#include "llvm/Pass.h"
24+
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
25+
26+
#define DEBUG_TYPE "bpf-preserve-di-type"
27+
28+
namespace llvm {
29+
const std::string BPFCoreSharedInfo::TypeIdAttr = "btf_type_id";
30+
} // namespace llvm
31+
32+
using namespace llvm;
33+
34+
namespace {
35+
36+
class BPFPreserveDIType final : public ModulePass {
37+
StringRef getPassName() const override {
38+
return "BPF Preserve DebugInfo Type";
39+
}
40+
41+
bool runOnModule(Module &M) override;
42+
43+
public:
44+
static char ID;
45+
BPFPreserveDIType() : ModulePass(ID) {}
46+
47+
private:
48+
bool doTransformation(Module &M);
49+
};
50+
} // End anonymous namespace
51+
52+
char BPFPreserveDIType::ID = 0;
53+
INITIALIZE_PASS(BPFPreserveDIType, DEBUG_TYPE, "preserve debuginfo type", false,
54+
false)
55+
56+
ModulePass *llvm::createBPFPreserveDIType() { return new BPFPreserveDIType(); }
57+
58+
bool BPFPreserveDIType::runOnModule(Module &M) {
59+
LLVM_DEBUG(dbgs() << "********** preserve debuginfo type **********\n");
60+
61+
// Bail out if no debug info.
62+
if (M.debug_compile_units().empty())
63+
return false;
64+
65+
return doTransformation(M);
66+
}
67+
68+
bool BPFPreserveDIType::doTransformation(Module &M) {
69+
std::vector<CallInst *> PreserveDITypeCalls;
70+
71+
for (auto &F : M) {
72+
for (auto &BB : F) {
73+
for (auto &I : BB) {
74+
auto *Call = dyn_cast<CallInst>(&I);
75+
if (!Call)
76+
continue;
77+
78+
const auto *GV = dyn_cast<GlobalValue>(Call->getCalledOperand());
79+
if (!GV)
80+
continue;
81+
82+
if (GV->getName().startswith("llvm.bpf.btf.type.id")) {
83+
if (!Call->getMetadata(LLVMContext::MD_preserve_access_index))
84+
report_fatal_error(
85+
"Missing metadata for llvm.bpf.btf.type.id intrinsic");
86+
PreserveDITypeCalls.push_back(Call);
87+
}
88+
}
89+
}
90+
}
91+
92+
if (PreserveDITypeCalls.empty())
93+
return false;
94+
95+
std::string BaseName = "llvm.btf_type_id.";
96+
int Count = 0;
97+
for (auto Call : PreserveDITypeCalls) {
98+
const ConstantInt *Flag = dyn_cast<ConstantInt>(Call->getArgOperand(2));
99+
assert(Flag);
100+
uint64_t FlagValue = Flag->getValue().getZExtValue();
101+
102+
if (FlagValue >= BPFCoreSharedInfo::MAX_BTF_TYPE_ID_FLAG)
103+
report_fatal_error("Incorrect flag for llvm.bpf.btf.type.id intrinsic");
104+
105+
uint32_t Reloc;
106+
if (FlagValue == BPFCoreSharedInfo::BTF_TYPE_ID_LOCAL_RELOC)
107+
Reloc = BPFCoreSharedInfo::BTF_TYPE_ID_LOCAL;
108+
else
109+
Reloc = BPFCoreSharedInfo::BTF_TYPE_ID_REMOTE;
110+
111+
BasicBlock *BB = Call->getParent();
112+
IntegerType *VarType = Type::getInt32Ty(BB->getContext());
113+
std::string GVName = BaseName + std::to_string(Count) + "$" +
114+
std::to_string(Reloc);
115+
GlobalVariable *GV =
116+
new GlobalVariable(M, VarType, false, GlobalVariable::ExternalLinkage,
117+
NULL, GVName);
118+
GV->addAttribute(BPFCoreSharedInfo::TypeIdAttr);
119+
MDNode *MD = Call->getMetadata(LLVMContext::MD_preserve_access_index);
120+
GV->setMetadata(LLVMContext::MD_preserve_access_index, MD);
121+
122+
// Load the global variable which represents the type info.
123+
auto *LDInst = new LoadInst(Type::getInt32Ty(BB->getContext()), GV, "",
124+
Call);
125+
Call->replaceAllUsesWith(LDInst);
126+
Call->eraseFromParent();
127+
Count++;
128+
}
129+
130+
return true;
131+
}

llvm/lib/Target/BPF/BPFTargetMachine.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeBPFTarget() {
3535

3636
PassRegistry &PR = *PassRegistry::getPassRegistry();
3737
initializeBPFAbstractMemberAccessPass(PR);
38+
initializeBPFPreserveDITypePass(PR);
3839
initializeBPFMIPeepholePass(PR);
3940
initializeBPFMIPeepholeTruncElimPass(PR);
4041
}
@@ -96,6 +97,7 @@ TargetPassConfig *BPFTargetMachine::createPassConfig(PassManagerBase &PM) {
9697
void BPFPassConfig::addIRPasses() {
9798

9899
addPass(createBPFAbstractMemberAccess(&getBPFTargetMachine()));
100+
addPass(createBPFPreserveDIType());
99101

100102
TargetPassConfig::addIRPasses();
101103
}

0 commit comments

Comments
 (0)