[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
This commit is contained in:
Yonghong Song 2020-02-11 16:55:22 -08:00
parent 10c10f2419
commit 6b01b46538
9 changed files with 400 additions and 77 deletions

View File

@ -16,6 +16,7 @@ namespace llvm {
class BPFTargetMachine;
ModulePass *createBPFAbstractMemberAccess(BPFTargetMachine *TM);
ModulePass *createBPFPreserveDIType();
FunctionPass *createBPFISelDag(BPFTargetMachine &TM);
FunctionPass *createBPFMISimplifyPatchablePass();
@ -25,6 +26,7 @@ FunctionPass *createBPFMIPreEmitPeepholePass();
FunctionPass *createBPFMIPreEmitCheckingPass();
void initializeBPFAbstractMemberAccessPass(PassRegistry&);
void initializeBPFPreserveDITypePass(PassRegistry&);
void initializeBPFMISimplifyPatchablePass(PassRegistry&);
void initializeBPFMIPeepholePass(PassRegistry&);
void initializeBPFMIPeepholeTruncElimPass(PassRegistry&);

View File

@ -13,18 +13,30 @@ namespace llvm {
class BPFCoreSharedInfo {
public:
enum OffsetRelocKind : uint32_t {
enum PatchableRelocKind : uint32_t {
FIELD_BYTE_OFFSET = 0,
FIELD_BYTE_SIZE,
FIELD_EXISTENCE,
FIELD_SIGNEDNESS,
FIELD_LSHIFT_U64,
FIELD_RSHIFT_U64,
BTF_TYPE_ID_LOCAL,
BTF_TYPE_ID_REMOTE,
MAX_FIELD_RELOC_KIND,
};
enum BTFTypeIdFlag : uint32_t {
BTF_TYPE_ID_LOCAL_RELOC = 0,
BTF_TYPE_ID_REMOTE_RELOC,
MAX_BTF_TYPE_ID_FLAG,
};
/// The attribute attached to globals representing a field access
static const std::string AmaAttr;
/// The attribute attached to globals representing a type id
static const std::string TypeIdAttr;
};
} // namespace llvm

View File

@ -22,6 +22,9 @@
// 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"
@ -55,10 +58,10 @@ private:
bool removeLD(void);
void processCandidate(MachineRegisterInfo *MRI, MachineBasicBlock &MBB,
MachineInstr &MI, Register &SrcReg, Register &DstReg,
const GlobalValue *GVal);
const GlobalValue *GVal, bool IsAma);
void processDstReg(MachineRegisterInfo *MRI, Register &DstReg,
Register &SrcReg, const GlobalValue *GVal,
bool doSrcRegProp);
bool doSrcRegProp, bool IsAma);
void processInst(MachineRegisterInfo *MRI, MachineInstr *Inst,
MachineOperand *RelocOp, const GlobalValue *GVal);
void checkADDrr(MachineRegisterInfo *MRI, MachineOperand *RelocOp,
@ -155,25 +158,27 @@ void BPFMISimplifyPatchable::checkShift(MachineRegisterInfo *MRI,
void BPFMISimplifyPatchable::processCandidate(MachineRegisterInfo *MRI,
MachineBasicBlock &MBB, MachineInstr &MI, Register &SrcReg,
Register &DstReg, const GlobalValue *GVal) {
Register &DstReg, const GlobalValue *GVal, bool IsAma) {
if (MRI->getRegClass(DstReg) == &BPF::GPR32RegClass) {
// 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;
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);
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);
}
}
}
@ -183,12 +188,12 @@ void BPFMISimplifyPatchable::processCandidate(MachineRegisterInfo *MRI,
}
// All uses of DstReg replaced by SrcReg
processDstReg(MRI, DstReg, SrcReg, GVal, true);
processDstReg(MRI, DstReg, SrcReg, GVal, true, IsAma);
}
void BPFMISimplifyPatchable::processDstReg(MachineRegisterInfo *MRI,
Register &DstReg, Register &SrcReg, const GlobalValue *GVal,
bool doSrcRegProp) {
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) {
@ -197,7 +202,7 @@ void BPFMISimplifyPatchable::processDstReg(MachineRegisterInfo *MRI,
I->setReg(SrcReg);
// The candidate needs to have a unique definition.
if (MRI->getUniqueVRegDef(I->getReg()))
if (IsAma && MRI->getUniqueVRegDef(I->getReg()))
processInst(MRI, I->getParent(), &*I, GVal);
}
}
@ -269,28 +274,26 @@ bool BPFMISimplifyPatchable::removeLD() {
if (!DefInst)
continue;
bool IsCandidate = false;
const GlobalValue *GVal = nullptr;
if (DefInst->getOpcode() == BPF::LD_imm64) {
const MachineOperand &MO = DefInst->getOperand(1);
if (MO.isGlobal()) {
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)
if (DefInst->getOpcode() != BPF::LD_imm64)
continue;
processCandidate(MRI, MBB, MI, SrcReg, DstReg, GVal);
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;

View File

@ -0,0 +1,131 @@
//===----------- BPFPreserveDIType.cpp - Preserve DebugInfo Types ---------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Preserve Debuginfo types encoded in __builtin_btf_type_id() metadata.
//
//===----------------------------------------------------------------------===//
#include "BPF.h"
#include "BPFCORE.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/User.h"
#include "llvm/IR/Value.h"
#include "llvm/Pass.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#define DEBUG_TYPE "bpf-preserve-di-type"
namespace llvm {
const std::string BPFCoreSharedInfo::TypeIdAttr = "btf_type_id";
} // namespace llvm
using namespace llvm;
namespace {
class BPFPreserveDIType final : public ModulePass {
StringRef getPassName() const override {
return "BPF Preserve DebugInfo Type";
}
bool runOnModule(Module &M) override;
public:
static char ID;
BPFPreserveDIType() : ModulePass(ID) {}
private:
bool doTransformation(Module &M);
};
} // End anonymous namespace
char BPFPreserveDIType::ID = 0;
INITIALIZE_PASS(BPFPreserveDIType, DEBUG_TYPE, "preserve debuginfo type", false,
false)
ModulePass *llvm::createBPFPreserveDIType() { return new BPFPreserveDIType(); }
bool BPFPreserveDIType::runOnModule(Module &M) {
LLVM_DEBUG(dbgs() << "********** preserve debuginfo type **********\n");
// Bail out if no debug info.
if (M.debug_compile_units().empty())
return false;
return doTransformation(M);
}
bool BPFPreserveDIType::doTransformation(Module &M) {
std::vector<CallInst *> PreserveDITypeCalls;
for (auto &F : M) {
for (auto &BB : F) {
for (auto &I : BB) {
auto *Call = dyn_cast<CallInst>(&I);
if (!Call)
continue;
const auto *GV = dyn_cast<GlobalValue>(Call->getCalledOperand());
if (!GV)
continue;
if (GV->getName().startswith("llvm.bpf.btf.type.id")) {
if (!Call->getMetadata(LLVMContext::MD_preserve_access_index))
report_fatal_error(
"Missing metadata for llvm.bpf.btf.type.id intrinsic");
PreserveDITypeCalls.push_back(Call);
}
}
}
}
if (PreserveDITypeCalls.empty())
return false;
std::string BaseName = "llvm.btf_type_id.";
int Count = 0;
for (auto Call : PreserveDITypeCalls) {
const ConstantInt *Flag = dyn_cast<ConstantInt>(Call->getArgOperand(2));
assert(Flag);
uint64_t FlagValue = Flag->getValue().getZExtValue();
if (FlagValue >= BPFCoreSharedInfo::MAX_BTF_TYPE_ID_FLAG)
report_fatal_error("Incorrect flag for llvm.bpf.btf.type.id intrinsic");
uint32_t Reloc;
if (FlagValue == BPFCoreSharedInfo::BTF_TYPE_ID_LOCAL_RELOC)
Reloc = BPFCoreSharedInfo::BTF_TYPE_ID_LOCAL;
else
Reloc = BPFCoreSharedInfo::BTF_TYPE_ID_REMOTE;
BasicBlock *BB = Call->getParent();
IntegerType *VarType = Type::getInt32Ty(BB->getContext());
std::string GVName = BaseName + std::to_string(Count) + "$" +
std::to_string(Reloc);
GlobalVariable *GV =
new GlobalVariable(M, VarType, false, GlobalVariable::ExternalLinkage,
NULL, GVName);
GV->addAttribute(BPFCoreSharedInfo::TypeIdAttr);
MDNode *MD = Call->getMetadata(LLVMContext::MD_preserve_access_index);
GV->setMetadata(LLVMContext::MD_preserve_access_index, MD);
// Load the global variable which represents the type info.
auto *LDInst = new LoadInst(Type::getInt32Ty(BB->getContext()), GV, "",
Call);
Call->replaceAllUsesWith(LDInst);
Call->eraseFromParent();
Count++;
}
return true;
}

View File

@ -35,6 +35,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeBPFTarget() {
PassRegistry &PR = *PassRegistry::getPassRegistry();
initializeBPFAbstractMemberAccessPass(PR);
initializeBPFPreserveDITypePass(PR);
initializeBPFMIPeepholePass(PR);
initializeBPFMIPeepholeTruncElimPass(PR);
}
@ -96,6 +97,7 @@ TargetPassConfig *BPFTargetMachine::createPassConfig(PassManagerBase &PM) {
void BPFPassConfig::addIRPasses() {
addPass(createBPFAbstractMemberAccess(&getBPFTargetMachine()));
addPass(createBPFPreserveDIType());
TargetPassConfig::addIRPasses();
}

View File

@ -933,9 +933,9 @@ void BTFDebug::endFunctionImpl(const MachineFunction *MF) {
SecNameOff = 0;
}
/// On-demand populate struct types as requested from abstract member
/// accessing.
unsigned BTFDebug::populateStructType(const DIType *Ty) {
/// On-demand populate types as requested from abstract member
/// accessing or preserve debuginfo type.
unsigned BTFDebug::populateType(const DIType *Ty) {
unsigned Id;
visitTypeEntry(Ty, Id, false, false);
for (const auto &TypeEntry : TypeEntries)
@ -944,24 +944,32 @@ unsigned BTFDebug::populateStructType(const DIType *Ty) {
}
/// Generate a struct member field relocation.
void BTFDebug::generateFieldReloc(const MCSymbol *ORSym, DIType *RootTy,
StringRef AccessPattern) {
unsigned RootId = populateStructType(RootTy);
size_t FirstDollar = AccessPattern.find_first_of('$');
size_t FirstColon = AccessPattern.find_first_of(':');
size_t SecondColon = AccessPattern.find_first_of(':', FirstColon + 1);
StringRef IndexPattern = AccessPattern.substr(FirstDollar + 1);
StringRef RelocKindStr = AccessPattern.substr(FirstColon + 1,
SecondColon - FirstColon);
StringRef PatchImmStr = AccessPattern.substr(SecondColon + 1,
FirstDollar - SecondColon);
void BTFDebug::generatePatchImmReloc(const MCSymbol *ORSym, uint32_t RootId,
const GlobalVariable *GVar, bool IsAma) {
BTFFieldReloc FieldReloc;
FieldReloc.Label = ORSym;
FieldReloc.OffsetNameOff = addString(IndexPattern);
FieldReloc.TypeID = RootId;
FieldReloc.RelocKind = std::stoull(std::string(RelocKindStr));
PatchImms[AccessPattern.str()] = std::stoul(std::string(PatchImmStr));
StringRef AccessPattern = GVar->getName();
size_t FirstDollar = AccessPattern.find_first_of('$');
if (IsAma) {
size_t FirstColon = AccessPattern.find_first_of(':');
size_t SecondColon = AccessPattern.find_first_of(':', FirstColon + 1);
StringRef IndexPattern = AccessPattern.substr(FirstDollar + 1);
StringRef RelocKindStr = AccessPattern.substr(FirstColon + 1,
SecondColon - FirstColon);
StringRef PatchImmStr = AccessPattern.substr(SecondColon + 1,
FirstDollar - SecondColon);
FieldReloc.OffsetNameOff = addString(IndexPattern);
FieldReloc.RelocKind = std::stoull(std::string(RelocKindStr));
PatchImms[GVar] = std::stoul(std::string(PatchImmStr));
} else {
StringRef RelocStr = AccessPattern.substr(FirstDollar + 1);
FieldReloc.OffsetNameOff = addString("0");
FieldReloc.RelocKind = std::stoull(std::string(RelocStr));
PatchImms[GVar] = RootId;
}
FieldRelocTable[SecNameOff].push_back(FieldReloc);
}
@ -970,14 +978,20 @@ void BTFDebug::processReloc(const MachineOperand &MO) {
if (MO.isGlobal()) {
const GlobalValue *GVal = MO.getGlobal();
auto *GVar = dyn_cast<GlobalVariable>(GVal);
if (GVar && GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr)) {
MCSymbol *ORSym = OS.getContext().createTempSymbol();
OS.emitLabel(ORSym);
if (!GVar)
return;
MDNode *MDN = GVar->getMetadata(LLVMContext::MD_preserve_access_index);
DIType *Ty = dyn_cast<DIType>(MDN);
generateFieldReloc(ORSym, Ty, GVar->getName());
}
if (!GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr) &&
!GVar->hasAttribute(BPFCoreSharedInfo::TypeIdAttr))
return;
MCSymbol *ORSym = OS.getContext().createTempSymbol();
OS.emitLabel(ORSym);
MDNode *MDN = GVar->getMetadata(LLVMContext::MD_preserve_access_index);
uint32_t RootId = populateType(dyn_cast<DIType>(MDN));
generatePatchImmReloc(ORSym, RootId, GVar,
GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr));
}
}
@ -1013,6 +1027,9 @@ void BTFDebug::beginInstruction(const MachineInstr *MI) {
// Later, the insn is replaced with "r2 = <offset>"
// where "<offset>" equals to the offset based on current
// type definitions.
//
// If the insn is "r2 = LD_imm64 @<an TypeIdAttr global>",
// The LD_imm64 result will be replaced with a btf type id.
processReloc(MI->getOperand(1));
} else if (MI->getOpcode() == BPF::CORE_MEM ||
MI->getOpcode() == BPF::CORE_ALU32_MEM ||
@ -1145,9 +1162,15 @@ bool BTFDebug::InstLower(const MachineInstr *MI, MCInst &OutMI) {
if (MO.isGlobal()) {
const GlobalValue *GVal = MO.getGlobal();
auto *GVar = dyn_cast<GlobalVariable>(GVal);
if (GVar && GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr)) {
// Emit "mov ri, <imm>" for patched immediate.
uint32_t Imm = PatchImms[GVar->getName().str()];
if (GVar) {
// Emit "mov ri, <imm>"
uint32_t Imm;
if (GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr) ||
GVar->hasAttribute(BPFCoreSharedInfo::TypeIdAttr))
Imm = PatchImms[GVar];
else
return false;
OutMI.setOpcode(BPF::MOV_ri);
OutMI.addOperand(MCOperand::createReg(MI->getOperand(0).getReg()));
OutMI.addOperand(MCOperand::createImm(Imm));
@ -1162,7 +1185,7 @@ bool BTFDebug::InstLower(const MachineInstr *MI, MCInst &OutMI) {
const GlobalValue *GVal = MO.getGlobal();
auto *GVar = dyn_cast<GlobalVariable>(GVal);
if (GVar && GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr)) {
uint32_t Imm = PatchImms[GVar->getName().str()];
uint32_t Imm = PatchImms[GVar];
OutMI.setOpcode(MI->getOperand(1).getImm());
if (MI->getOperand(0).isImm())
OutMI.addOperand(MCOperand::createImm(MI->getOperand(0).getImm()));

View File

@ -26,6 +26,7 @@ namespace llvm {
class AsmPrinter;
class BTFDebug;
class DIType;
class GlobalVariable;
class MCStreamer;
class MCSymbol;
class MachineFunction;
@ -250,7 +251,7 @@ class BTFDebug : public DebugHandlerBase {
StringMap<std::vector<std::string>> FileContent;
std::map<std::string, std::unique_ptr<BTFKindDataSec>> DataSecEntries;
std::vector<BTFTypeStruct *> StructTypes;
std::map<std::string, uint32_t> PatchImms;
std::map<const GlobalVariable *, uint32_t> PatchImms;
std::map<StringRef, std::pair<bool, std::vector<BTFTypeDerived *>>>
FixupDerivedTypes;
std::set<const Function *>ProtoFunctions;
@ -300,11 +301,11 @@ class BTFDebug : public DebugHandlerBase {
void processFuncPrototypes(const Function *);
/// Generate one field relocation record.
void generateFieldReloc(const MCSymbol *ORSym, DIType *RootTy,
StringRef AccessPattern);
void generatePatchImmReloc(const MCSymbol *ORSym, uint32_t RootId,
const GlobalVariable *, bool IsAma);
/// Populating unprocessed struct type.
unsigned populateStructType(const DIType *Ty);
/// Populating unprocessed type on demand.
unsigned populateType(const DIType *Ty);
/// Process relocation instructions.
void processReloc(const MachineOperand &MO);

View File

@ -20,6 +20,7 @@ add_llvm_target(BPFCodeGen
BPFISelDAGToDAG.cpp
BPFISelLowering.cpp
BPFMCInstLower.cpp
BPFPreserveDIType.cpp
BPFRegisterInfo.cpp
BPFSelectionDAGInfo.cpp
BPFSubtarget.cpp

View File

@ -0,0 +1,148 @@
; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
; RUN: llc -march=bpfel -mattr=+alu32 -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
; RUN: llc -march=bpfeb -mattr=+alu32 -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
;
; Source code:
; static int (*bpf_log)(unsigned tid, void *data, int data_size) = (void *)999;
; struct {
; char f1[100];
; typeof(3) f2;
; } tmp__abc = {1, 3};
; void prog1() {
; bpf_log(__builtin_btf_type_id(tmp__abc, 0), &tmp__abc, sizeof(tmp__abc));
; }
; void prog2() {
; bpf_log(__builtin_btf_type_id(&tmp__abc, 1), &tmp__abc, sizeof(tmp__abc));
; }
; void prog3() {
; bpf_log(__builtin_btf_type_id(tmp__abc.f1[3], 1), &tmp__abc, sizeof(tmp__abc));
; }
; Compilation flag:
; clang -target bpf -O2 -g -S -emit-llvm test.c
%struct.anon = type { [100 x i8], i32 }
@tmp__abc = dso_local global { <{ i8, i8, [98 x i8] }>, i32 } { <{ i8, i8, [98 x i8] }> <{ i8 1, i8 3, [98 x i8] zeroinitializer }>, i32 0 }, align 4, !dbg !0
; Function Attrs: nounwind
define dso_local void @prog1() local_unnamed_addr #0 !dbg !28 {
entry:
%0 = tail call i32 @llvm.bpf.btf.type.id.p0s_struct.anons.i32(%struct.anon* bitcast ({ <{ i8, i8, [98 x i8] }>, i32 }* @tmp__abc to %struct.anon*), i32 1, i64 0), !dbg !31, !llvm.preserve.access.index !7
%call = tail call i32 inttoptr (i64 999 to i32 (i32, i8*, i32)*)(i32 %0, i8* getelementptr inbounds ({ <{ i8, i8, [98 x i8] }>, i32 }, { <{ i8, i8, [98 x i8] }>, i32 }* @tmp__abc, i64 0, i32 0, i32 0), i32 104) #2, !dbg !32
ret void, !dbg !33
}
; Function Attrs: nounwind readnone
declare i32 @llvm.bpf.btf.type.id.p0s_struct.anons.i32(%struct.anon*, i32, i64) #1
; Function Attrs: nounwind
define dso_local void @prog2() local_unnamed_addr #0 !dbg !34 {
entry:
%0 = tail call i32 @llvm.bpf.btf.type.id.p0s_struct.anons.i32(%struct.anon* bitcast ({ <{ i8, i8, [98 x i8] }>, i32 }* @tmp__abc to %struct.anon*), i32 0, i64 1), !dbg !35, !llvm.preserve.access.index !6
%call = tail call i32 inttoptr (i64 999 to i32 (i32, i8*, i32)*)(i32 %0, i8* getelementptr inbounds ({ <{ i8, i8, [98 x i8] }>, i32 }, { <{ i8, i8, [98 x i8] }>, i32 }* @tmp__abc, i64 0, i32 0, i32 0), i32 104) #2, !dbg !36
ret void, !dbg !37
}
; Function Attrs: nounwind
define dso_local void @prog3() local_unnamed_addr #0 !dbg !38 {
entry:
%0 = tail call i32 @llvm.bpf.btf.type.id.p0i8.i32(i8* getelementptr inbounds ({ <{ i8, i8, [98 x i8] }>, i32 }, { <{ i8, i8, [98 x i8] }>, i32 }* @tmp__abc, i64 0, i32 0, i32 2, i64 1), i32 1, i64 1), !dbg !39, !llvm.preserve.access.index !11
%call = tail call i32 inttoptr (i64 999 to i32 (i32, i8*, i32)*)(i32 %0, i8* getelementptr inbounds ({ <{ i8, i8, [98 x i8] }>, i32 }, { <{ i8, i8, [98 x i8] }>, i32 }* @tmp__abc, i64 0, i32 0, i32 0), i32 104) #2, !dbg !40
ret void, !dbg !41
}
; CHECK-LABEL: prog1
; CHECK: r1 = 3
; CHECK-LABEL: prog2
; CHECK: r1 = 10
; CHECK-LABEL: prog3
; CHECK: r1 = 4
;
; CHECK: .long 0 # BTF_KIND_STRUCT(id = 3)
; CHECK-NEXT: .long 67108866 # 0x4000002
; CHECK-NEXT: .long 104
; CHECK-NEXT: .long 13
; CHECK-NEXT: .long 5
; CHECK-NEXT: .long 0 # 0x0
; CHECK-NEXT: .long 16
; CHECK-NEXT: .long 7
; CHECK-NEXT: .long 800 # 0x320
; CHECK-NEXT: .long 19 # BTF_KIND_INT(id = 4)
; CHECK-NEXT: .long 16777216 # 0x1000000
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long 16777224 # 0x1000008
; CHECK: .long 0 # BTF_KIND_PTR(id = 10)
; CHECK-NEXT: .long 33554432 # 0x2000000
; CHECK-NEXT: .long 3
; CHECK: .long 16 # FieldReloc
; CHECK-NEXT: .long {{[0-9]+}} # Field reloc section string offset={{[0-9]+}}
; CHECK-NEXT: .long 3
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
; CHECK-NEXT: .long 3
; CHECK-NEXT: .long {{[0-9]+}}
; CHECK-NEXT: .long 6
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
; CHECK-NEXT: .long 10
; CHECK-NEXT: .long {{[0-9]+}}
; CHECK-NEXT: .long 7
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
; CHECK-NEXT: .long 4
; CHECK-NEXT: .long {{[0-9]+}}
; CHECK-NEXT: .long 7
; Function Attrs: nounwind readnone
declare i32 @llvm.bpf.btf.type.id.p0i8.i32(i8*, i32, i64) #1
attributes #0 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { nounwind readnone }
attributes #2 = { nounwind }
!llvm.dbg.cu = !{!2}
!llvm.module.flags = !{!24, !25, !26}
!llvm.ident = !{!27}
!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
!1 = distinct !DIGlobalVariable(name: "tmp__abc", scope: !2, file: !3, line: 5, type: !7, isLocal: false, isDefinition: true)
!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 11.0.0 (https://github.com/llvm/llvm-project.git 95253d8f16b8085b4b85cb3a6106ccbfe8a6d9b2)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, retainedTypes: !5, globals: !16, splitDebugInlining: false, nameTableKind: None)
!3 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/core")
!4 = !{}
!5 = !{!6, !11}
!6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64)
!7 = distinct !DICompositeType(tag: DW_TAG_structure_type, file: !3, line: 2, size: 832, elements: !8)
!8 = !{!9, !14}
!9 = !DIDerivedType(tag: DW_TAG_member, name: "f1", scope: !7, file: !3, line: 3, baseType: !10, size: 800)
!10 = !DICompositeType(tag: DW_TAG_array_type, baseType: !11, size: 800, elements: !12)
!11 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
!12 = !{!13}
!13 = !DISubrange(count: 100)
!14 = !DIDerivedType(tag: DW_TAG_member, name: "f2", scope: !7, file: !3, line: 4, baseType: !15, size: 32, offset: 800)
!15 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!16 = !{!0, !17}
!17 = !DIGlobalVariableExpression(var: !18, expr: !DIExpression())
!18 = distinct !DIGlobalVariable(name: "bpf_log", scope: !2, file: !3, line: 1, type: !19, isLocal: true, isDefinition: true)
!19 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !20, size: 64)
!20 = !DISubroutineType(types: !21)
!21 = !{!15, !22, !23, !15}
!22 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
!23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)
!24 = !{i32 7, !"Dwarf Version", i32 4}
!25 = !{i32 2, !"Debug Info Version", i32 3}
!26 = !{i32 1, !"wchar_size", i32 4}
!27 = !{!"clang version 11.0.0 (https://github.com/llvm/llvm-project.git 95253d8f16b8085b4b85cb3a6106ccbfe8a6d9b2)"}
!28 = distinct !DISubprogram(name: "prog1", scope: !3, file: !3, line: 6, type: !29, scopeLine: 6, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !4)
!29 = !DISubroutineType(types: !30)
!30 = !{null}
!31 = !DILocation(line: 7, column: 11, scope: !28)
!32 = !DILocation(line: 7, column: 3, scope: !28)
!33 = !DILocation(line: 8, column: 1, scope: !28)
!34 = distinct !DISubprogram(name: "prog2", scope: !3, file: !3, line: 9, type: !29, scopeLine: 9, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !4)
!35 = !DILocation(line: 10, column: 11, scope: !34)
!36 = !DILocation(line: 10, column: 3, scope: !34)
!37 = !DILocation(line: 11, column: 1, scope: !34)
!38 = distinct !DISubprogram(name: "prog3", scope: !3, file: !3, line: 12, type: !29, scopeLine: 12, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !4)
!39 = !DILocation(line: 13, column: 11, scope: !38)
!40 = !DILocation(line: 13, column: 3, scope: !38)
!41 = !DILocation(line: 14, column: 1, scope: !38)