diff --git a/clang/include/clang/Basic/BuiltinsBPF.def b/clang/include/clang/Basic/BuiltinsBPF.def --- a/clang/include/clang/Basic/BuiltinsBPF.def +++ b/clang/include/clang/Basic/BuiltinsBPF.def @@ -20,5 +20,8 @@ // Get record field information. TARGET_BUILTIN(__builtin_preserve_field_info, "Ui.", "t", "") +// Get BTF type id. +TARGET_BUILTIN(__builtin_btf_type_id, "v.", "t", "") + #undef BUILTIN #undef TARGET_BUILTIN diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -9703,33 +9703,77 @@ Value *CodeGenFunction::EmitBPFBuiltinExpr(unsigned BuiltinID, const CallExpr *E) { - assert(BuiltinID == BPF::BI__builtin_preserve_field_info && - "unexpected ARM builtin"); + assert((BuiltinID == BPF::BI__builtin_preserve_field_info || + BuiltinID == BPF::BI__builtin_btf_type_id) && + "unexpected BPF builtin"); - const Expr *Arg = E->getArg(0); - bool IsBitField = Arg->IgnoreParens()->getObjectKind() == OK_BitField; + switch (BuiltinID) { + default: + llvm_unreachable("Unexpected BPF builtin"); + case BPF::BI__builtin_preserve_field_info: { + const Expr *Arg = E->getArg(0); + bool IsBitField = Arg->IgnoreParens()->getObjectKind() == OK_BitField; - if (!getDebugInfo()) { - CGM.Error(E->getExprLoc(), "using builtin_preserve_field_info() without -g"); - return IsBitField ? EmitLValue(Arg).getBitFieldPointer() - : EmitLValue(Arg).getPointer(*this); - } + if (!getDebugInfo()) { + CGM.Error(E->getExprLoc(), "using builtin_preserve_field_info() without -g"); + return IsBitField ? EmitLValue(Arg).getBitFieldPointer() + : EmitLValue(Arg).getPointer(*this); + } - // Enable underlying preserve_*_access_index() generation. - bool OldIsInPreservedAIRegion = IsInPreservedAIRegion; - IsInPreservedAIRegion = true; - Value *FieldAddr = IsBitField ? EmitLValue(Arg).getBitFieldPointer() - : EmitLValue(Arg).getPointer(*this); - IsInPreservedAIRegion = OldIsInPreservedAIRegion; + // Enable underlying preserve_*_access_index() generation. + bool OldIsInPreservedAIRegion = IsInPreservedAIRegion; + IsInPreservedAIRegion = true; + Value *FieldAddr = IsBitField ? EmitLValue(Arg).getBitFieldPointer() + : EmitLValue(Arg).getPointer(*this); + IsInPreservedAIRegion = OldIsInPreservedAIRegion; + + ConstantInt *C = cast(EmitScalarExpr(E->getArg(1))); + Value *InfoKind = ConstantInt::get(Int64Ty, C->getSExtValue()); + + // Built the IR for the preserve_field_info intrinsic. + llvm::Function *FnGetFieldInfo = llvm::Intrinsic::getDeclaration( + &CGM.getModule(), llvm::Intrinsic::bpf_preserve_field_info, + {FieldAddr->getType()}); + return Builder.CreateCall(FnGetFieldInfo, {FieldAddr, InfoKind}); + } + case BPF::BI__builtin_btf_type_id: { + Value *FieldVal = nullptr; + bool IsLValue = E->getArg(0)->isLValue(); + if (IsLValue) + FieldVal = EmitLValue(E->getArg(0)).getPointer(*this); + else + FieldVal = EmitScalarExpr(E->getArg(0)); - ConstantInt *C = cast(EmitScalarExpr(E->getArg(1))); - Value *InfoKind = ConstantInt::get(Int64Ty, C->getSExtValue()); + if (!getDebugInfo()) { + CGM.Error(E->getExprLoc(), "using builtin_preserve_field_info() without -g"); + return nullptr; + } - // Built the IR for the preserve_field_info intrinsic. - llvm::Function *FnGetFieldInfo = llvm::Intrinsic::getDeclaration( - &CGM.getModule(), llvm::Intrinsic::bpf_preserve_field_info, - {FieldAddr->getType()}); - return Builder.CreateCall(FnGetFieldInfo, {FieldAddr, InfoKind}); + llvm::DIType *DbgInfo = + getDebugInfo()->getOrCreateStandaloneType(E->getArg(0)->getType(), + E->getArg(0)->getExprLoc()); + + // Built the IR for the btf_type_id intrinsic. + // We also put whether this is a LValue or not in the IR builtin function + // to distinguish the following two cases: + // struct t v; + // __builtin_btf_type_id(v); <---- Call 1: LValue + // __builtin_btf_type_id(&v); <---- Call 2: not a LValue + // Internally, Call 2 is converted to Call 1 and the compiler may then + // do CSE on two calls. + // We do not want to mark __builtin_btf_type_id() may access arbitrary + // memory as it may hinder some optimization. Put an additional + // argument in IR intrinsic to indicate whether this is a LValue or + // not should solve this problem and prevent CSE. + Constant *CV = ConstantInt::get(IntTy, IsLValue); + llvm::Function *FnBtfTypeId = llvm::Intrinsic::getDeclaration( + &CGM.getModule(), llvm::Intrinsic::bpf_btf_type_id, + {FieldVal->getType(), CV->getType()}); + CallInst *Fn = Builder.CreateCall(FnBtfTypeId, {FieldVal, CV}); + Fn->setMetadata(LLVMContext::MD_preserve_access_index, DbgInfo); + return Fn; + } + } } llvm::Value *CodeGenFunction:: diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -2284,9 +2284,17 @@ bool Sema::CheckBPFBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { - assert(BuiltinID == BPF::BI__builtin_preserve_field_info && + assert((BuiltinID == BPF::BI__builtin_preserve_field_info || + BuiltinID == BPF::BI__builtin_btf_type_id) && "unexpected ARM builtin"); + if (BuiltinID == BPF::BI__builtin_btf_type_id) { + if (checkArgCount(*this, TheCall, 1)) + return true; + TheCall->setType(Context.UnsignedIntTy); + return false; + } + if (checkArgCount(*this, TheCall, 2)) return true; diff --git a/llvm/include/llvm/IR/IntrinsicsBPF.td b/llvm/include/llvm/IR/IntrinsicsBPF.td --- a/llvm/include/llvm/IR/IntrinsicsBPF.td +++ b/llvm/include/llvm/IR/IntrinsicsBPF.td @@ -23,4 +23,6 @@ def int_bpf_preserve_field_info : GCCBuiltin<"__builtin_bpf_preserve_field_info">, Intrinsic<[llvm_i32_ty], [llvm_anyptr_ty, llvm_i64_ty], [IntrNoMem, ImmArg<1>]>; + def int_bpf_btf_type_id : GCCBuiltin<"__builtin_bpf_btf_type_id">, + Intrinsic<[llvm_i32_ty], [llvm_any_ty, llvm_any_ty], [IntrNoMem]>; } diff --git a/llvm/lib/Target/BPF/BPF.h b/llvm/lib/Target/BPF/BPF.h --- a/llvm/lib/Target/BPF/BPF.h +++ b/llvm/lib/Target/BPF/BPF.h @@ -16,8 +16,10 @@ class BPFTargetMachine; ModulePass *createBPFAbstractMemberAccess(BPFTargetMachine *TM); +ModulePass *createBPFPreserveDIType(); FunctionPass *createBPFISelDag(BPFTargetMachine &TM); +FunctionPass *createBPFMIPreserveDITypePass(); FunctionPass *createBPFMISimplifyPatchablePass(); FunctionPass *createBPFMIPeepholePass(); FunctionPass *createBPFMIPeepholeTruncElimPass(); @@ -25,6 +27,8 @@ FunctionPass *createBPFMIPreEmitCheckingPass(); void initializeBPFAbstractMemberAccessPass(PassRegistry&); +void initializeBPFPreserveDITypePass(PassRegistry&); +void initializeBPFMIPreserveDITypePass(PassRegistry&); void initializeBPFMISimplifyPatchablePass(PassRegistry&); void initializeBPFMIPeepholePass(PassRegistry&); void initializeBPFMIPeepholeTruncElimPass(PassRegistry&); diff --git a/llvm/lib/Target/BPF/BPFCORE.h b/llvm/lib/Target/BPF/BPFCORE.h --- a/llvm/lib/Target/BPF/BPFCORE.h +++ b/llvm/lib/Target/BPF/BPFCORE.h @@ -13,18 +13,21 @@ 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, MAX_FIELD_RELOC_KIND, }; /// 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 PditAttr; }; } // namespace llvm diff --git a/llvm/lib/Target/BPF/BPFMIPreserveDIType.cpp b/llvm/lib/Target/BPF/BPFMIPreserveDIType.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/BPF/BPFMIPreserveDIType.cpp @@ -0,0 +1,118 @@ +//===------- BPFMIPreserveDIType.cpp - MI 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 +// +//===----------------------------------------------------------------------===// +// +// This pass 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-preserve-di-type" + +namespace { + +struct BPFMIPreserveDIType : public MachineFunctionPass { + + static char ID; + + BPFMIPreserveDIType() : MachineFunctionPass(ID) { + initializeBPFMIPreserveDITypePass(*PassRegistry::getPassRegistry()); + } + +private: + bool removeLD(MachineFunction &MF); + +public: + // Main entry point for this pass. + bool runOnMachineFunction(MachineFunction &MF) override { + if (skipFunction(MF.getFunction())) + return false; + + return removeLD(MF); + } +}; + +bool BPFMIPreserveDIType::removeLD(MachineFunction &MF) { + const BPFInstrInfo *TII = MF.getSubtarget().getInstrInfo(); + MachineRegisterInfo &MRI = MF.getRegInfo(); + MachineInstr *ToErase = nullptr; + bool Changed = false; + + for (MachineBasicBlock &MBB : MF) { + for (MachineInstr &MI : MBB) { + if (ToErase) { + ToErase->eraseFromParent(); + ToErase = nullptr; + } + + if (MI.getOpcode() != BPF::LDW && MI.getOpcode() != BPF::LDW32) + 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; + + 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(GVal); + if (GVar && GVar->hasAttribute(BPFCoreSharedInfo::PditAttr)) { + IsCandidate = true; + } + } + } + if (IsCandidate) { + if (MRI.getRegClass(DstReg) == &BPF::GPR32RegClass) { + BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(BPF::COPY), DstReg) + .addReg(SrcReg, 0, BPF::sub_32); + } else { + 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(BPFMIPreserveDIType, DEBUG_TYPE, "BPF MI PreserveDIType", false, + false) + +char BPFMIPreserveDIType::ID = 0; +FunctionPass *llvm::createBPFMIPreserveDITypePass() { + return new BPFMIPreserveDIType(); +} diff --git a/llvm/lib/Target/BPF/BPFPreserveDIType.cpp b/llvm/lib/Target/BPF/BPFPreserveDIType.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/BPF/BPFPreserveDIType.cpp @@ -0,0 +1,116 @@ +//===------------- BPFPreserveType.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_bpf_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::PditAttr = "btf_pidt"; +} // 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 PreserveDITypeCalls; + + for (auto &F : M) { + for (auto &BB : F) { + for (auto &I : BB) { + auto *Call = dyn_cast(&I); + if (!Call) + continue; + + const auto *GV = dyn_cast(Call->getCalledValue()); + 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.pdit."; + int Count = 0; + for (auto Call : PreserveDITypeCalls) { + BasicBlock *BB = Call->getParent(); + IntegerType *VarType = Type::getInt32Ty(BB->getContext()); + GlobalVariable *GV = + new GlobalVariable(M, VarType, false, GlobalVariable::ExternalLinkage, + NULL, BaseName + std::to_string(Count)); + GV->addAttribute(BPFCoreSharedInfo::PditAttr); + 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); + BB->getInstList().insert(Call->getIterator(), LDInst); + Call->replaceAllUsesWith(LDInst); + Call->eraseFromParent(); + Count++; + } + + return true; +} diff --git a/llvm/lib/Target/BPF/BPFTargetMachine.cpp b/llvm/lib/Target/BPF/BPFTargetMachine.cpp --- a/llvm/lib/Target/BPF/BPFTargetMachine.cpp +++ b/llvm/lib/Target/BPF/BPFTargetMachine.cpp @@ -35,6 +35,7 @@ PassRegistry &PR = *PassRegistry::getPassRegistry(); initializeBPFAbstractMemberAccessPass(PR); + initializeBPFPreserveDITypePass(PR); initializeBPFMIPeepholePass(PR); initializeBPFMIPeepholeTruncElimPass(PR); } @@ -96,6 +97,7 @@ void BPFPassConfig::addIRPasses() { addPass(createBPFAbstractMemberAccess(&getBPFTargetMachine())); + addPass(createBPFPreserveDIType()); TargetPassConfig::addIRPasses(); } @@ -110,6 +112,7 @@ void BPFPassConfig::addMachineSSAOptimization() { addPass(createBPFMISimplifyPatchablePass()); + addPass(createBPFMIPreserveDITypePass()); // The default implementation must be called first as we want eBPF // Peephole ran at last. diff --git a/llvm/lib/Target/BPF/BTFDebug.h b/llvm/lib/Target/BPF/BTFDebug.h --- a/llvm/lib/Target/BPF/BTFDebug.h +++ b/llvm/lib/Target/BPF/BTFDebug.h @@ -253,6 +253,7 @@ std::map>> FixupDerivedTypes; std::setProtoFunctions; + std::map PreservedDITypes; /// Add types to TypeEntries. /// @{ @@ -299,11 +300,11 @@ void processFuncPrototypes(const Function *); /// Generate one field relocation record. - void generateFieldReloc(const MCSymbol *ORSym, DIType *RootTy, + void generateFieldReloc(const MCSymbol *ORSym, uint32_t RootId, StringRef AccessPattern); - /// 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); diff --git a/llvm/lib/Target/BPF/BTFDebug.cpp b/llvm/lib/Target/BPF/BTFDebug.cpp --- a/llvm/lib/Target/BPF/BTFDebug.cpp +++ b/llvm/lib/Target/BPF/BTFDebug.cpp @@ -928,9 +928,9 @@ 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) @@ -939,9 +939,8 @@ } /// Generate a struct member field relocation. -void BTFDebug::generateFieldReloc(const MCSymbol *ORSym, DIType *RootTy, +void BTFDebug::generateFieldReloc(const MCSymbol *ORSym, uint32_t RootId, 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); @@ -965,13 +964,28 @@ if (MO.isGlobal()) { const GlobalValue *GVal = MO.getGlobal(); auto *GVar = dyn_cast(GVal); - if (GVar && GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr)) { - MCSymbol *ORSym = OS.getContext().createTempSymbol(); - OS.EmitLabel(ORSym); + if (!GVar) + return; + + if (!GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr) && + !GVar->hasAttribute(BPFCoreSharedInfo::PditAttr)) + return; - MDNode *MDN = GVar->getMetadata(LLVMContext::MD_preserve_access_index); - DIType *Ty = dyn_cast(MDN); - generateFieldReloc(ORSym, Ty, GVar->getName()); + MCSymbol *ORSym = OS.getContext().createTempSymbol(); + OS.EmitLabel(ORSym); + + MDNode *MDN = GVar->getMetadata(LLVMContext::MD_preserve_access_index); + uint32_t RootId = populateType(dyn_cast(MDN)); + if (GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr)) { + generateFieldReloc(ORSym, RootId, GVar->getName()); + } else if (GVar->hasAttribute(BPFCoreSharedInfo::PditAttr)) { + BTFFieldReloc FieldReloc; + FieldReloc.Label = ORSym; + FieldReloc.OffsetNameOff = 0; + FieldReloc.TypeID = RootId; + FieldReloc.RelocKind = BPFCoreSharedInfo::BTF_TYPE_ID; + FieldRelocTable[SecNameOff].push_back(FieldReloc); + PreservedDITypes[GVar->getName()] = RootId; } } } @@ -1008,6 +1022,9 @@ // Later, the insn is replaced with "r2 = " // where "" equals to the offset based on current // type definitions. + // + // If the insn is "r2 = LD_imm64 @", + // 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 || @@ -1140,9 +1157,16 @@ if (MO.isGlobal()) { const GlobalValue *GVal = MO.getGlobal(); auto *GVar = dyn_cast(GVal); - if (GVar && GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr)) { - // Emit "mov ri, " for patched immediate. - uint32_t Imm = PatchImms[GVar->getName().str()]; + if (GVar) { + // Emit "mov ri, " + uint32_t Imm; + if (GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr)) + Imm = PatchImms[GVar->getName().str()]; + else if (GVar->hasAttribute(BPFCoreSharedInfo::PditAttr)) + Imm = PreservedDITypes[GVar->getName()]; + else + return false; + OutMI.setOpcode(BPF::MOV_ri); OutMI.addOperand(MCOperand::createReg(MI->getOperand(0).getReg())); OutMI.addOperand(MCOperand::createImm(Imm)); diff --git a/llvm/lib/Target/BPF/CMakeLists.txt b/llvm/lib/Target/BPF/CMakeLists.txt --- a/llvm/lib/Target/BPF/CMakeLists.txt +++ b/llvm/lib/Target/BPF/CMakeLists.txt @@ -20,12 +20,14 @@ BPFISelDAGToDAG.cpp BPFISelLowering.cpp BPFMCInstLower.cpp + BPFPreserveDIType.cpp BPFRegisterInfo.cpp BPFSelectionDAGInfo.cpp BPFSubtarget.cpp BPFTargetMachine.cpp BPFMIPeephole.cpp BPFMIChecking.cpp + BPFMIPreserveDIType.cpp BPFMISimplifyPatchable.cpp BTFDebug.cpp ) diff --git a/llvm/test/CodeGen/BPF/BTF/builtin-btf-type-id.ll b/llvm/test/CodeGen/BPF/BTF/builtin-btf-type-id.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/BPF/BTF/builtin-btf-type-id.ll @@ -0,0 +1,147 @@ +; 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), &tmp__abc, sizeof(tmp__abc)); +; } +; void prog2() { +; bpf_log(__builtin_btf_type_id(&tmp__abc), &tmp__abc, sizeof(tmp__abc)); +; } +; void prog3() { +; bpf_log(__builtin_btf_type_id(tmp__abc.f1[3]), &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), !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) #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), !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), !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 +; CHECK-NEXT: .long 6 +; CHECK-NEXT: .long .Ltmp{{[0-9]+}} +; CHECK-NEXT: .long 10 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 6 +; CHECK-NEXT: .long .Ltmp{{[0-9]+}} +; CHECK-NEXT: .long 4 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 6 + +; Function Attrs: nounwind readnone +declare i32 @llvm.bpf.btf.type.id.p0i8.i32(i8*, i32) #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 189ae9d45901971ef2fda7599be9e32eb37d89ad)", 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/llvm/log") +!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 189ae9d45901971ef2fda7599be9e32eb37d89ad)"} +!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)