namespace llvm {
class BPFTargetMachine;
+ModulePass *createBPFAbstractMemberAccess();
+
FunctionPass *createBPFISelDag(BPFTargetMachine &TM);
+FunctionPass *createBPFMISimplifyPatchablePass();
FunctionPass *createBPFMIPeepholePass();
FunctionPass *createBPFMIPreEmitPeepholePass();
FunctionPass *createBPFMIPreEmitCheckingPass();
+void initializeBPFAbstractMemberAccessPass(PassRegistry&);
+void initializeBPFMISimplifyPatchablePass(PassRegistry&);
void initializeBPFMIPeepholePass(PassRegistry&);
void initializeBPFMIPreEmitPeepholePass(PassRegistry&);
void initializeBPFMIPreEmitCheckingPass(PassRegistry&);
--- /dev/null
+//===------ BPFAbstractMemberAccess.cpp - Abstracting Member Accesses -----===//
+//
+// 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 abstracted struct/union member accesses in order to support
+// compile-once run-everywhere (CO-RE). The CO-RE intends to compile the program
+// which can run on different kernels. In particular, if bpf program tries to
+// access a particular kernel data structure member, the details of the
+// intermediate member access will be remembered so bpf loader can do
+// necessary adjustment right before program loading.
+//
+// For example,
+//
+// struct s {
+// int a;
+// int b;
+// };
+// struct t {
+// struct s c;
+// int d;
+// };
+// struct t e;
+//
+// For the member access e.c.b, the compiler will generate code
+// &e + 4
+//
+// The compile-once run-everywhere instead generates the following code
+// r = 4
+// &e + r
+// The "4" in "r = 4" can be changed based on a particular kernel version.
+// For example, on a particular kernel version, if struct s is changed to
+//
+// struct s {
+// int new_field;
+// int a;
+// int b;
+// }
+//
+// By repeating the member access on the host, the bpf loader can
+// adjust "r = 4" as "r = 8".
+//
+// This feature relies on the following three intrinsic calls:
+// addr = preserve_array_access_index(base, dimension, index)
+// addr = preserve_union_access_index(base, di_index)
+// !llvm.preserve.access.index <union_ditype>
+// addr = preserve_struct_access_index(base, gep_index, di_index)
+// !llvm.preserve.access.index <struct_ditype>
+//
+//===----------------------------------------------------------------------===//
+
+#include "BPF.h"
+#include "BPFCORE.h"
+#include "BPFTargetMachine.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-abstract-member-access"
+
+namespace llvm {
+const std::string BPFCoreSharedInfo::AmaAttr = "btf_ama";
+const std::string BPFCoreSharedInfo::PatchableExtSecName =
+ ".BPF.patchable_externs";
+} // namespace llvm
+
+using namespace llvm;
+
+namespace {
+
+class BPFAbstractMemberAccess final : public ModulePass {
+ StringRef getPassName() const override {
+ return "BPF Abstract Member Access";
+ }
+
+ bool runOnModule(Module &M) override;
+
+public:
+ static char ID;
+ BPFAbstractMemberAccess() : ModulePass(ID) {}
+
+private:
+ enum : uint32_t {
+ BPFPreserveArrayAI = 1,
+ BPFPreserveUnionAI = 2,
+ BPFPreserveStructAI = 3,
+ };
+
+ std::map<std::string, GlobalVariable *> GEPGlobals;
+ // A map to link preserve_*_access_index instrinsic calls.
+ std::map<CallInst *, std::pair<CallInst *, uint32_t>> AIChain;
+ // A map to hold all the base preserve_*_access_index instrinsic calls.
+ // The base call is not an input of any other preserve_*_access_index
+ // intrinsics.
+ std::map<CallInst *, uint32_t> BaseAICalls;
+
+ bool doTransformation(Module &M);
+
+ void traceAICall(CallInst *Call, uint32_t Kind);
+ void traceBitCast(BitCastInst *BitCast, CallInst *Parent, uint32_t Kind);
+ void traceGEP(GetElementPtrInst *GEP, CallInst *Parent, uint32_t Kind);
+ void collectAICallChains(Module &M, Function &F);
+
+ bool IsPreserveDIAccessIndexCall(const CallInst *Call, uint32_t &Kind);
+ bool removePreserveAccessIndexIntrinsic(Module &M);
+ void replaceWithGEP(std::vector<CallInst *> &CallList,
+ uint32_t NumOfZerosIndex, uint32_t DIIndex);
+
+ Value *computeBaseAndAccessStr(CallInst *Call, std::string &AccessStr,
+ std::string &AccessKey, uint32_t Kind,
+ MDNode *&TypeMeta);
+ bool getAccessIndex(const Value *IndexValue, uint64_t &AccessIndex);
+ bool transformGEPChain(Module &M, CallInst *Call, uint32_t Kind);
+};
+} // End anonymous namespace
+
+char BPFAbstractMemberAccess::ID = 0;
+INITIALIZE_PASS(BPFAbstractMemberAccess, DEBUG_TYPE,
+ "abstracting struct/union member accessees", false, false)
+
+ModulePass *llvm::createBPFAbstractMemberAccess() {
+ return new BPFAbstractMemberAccess();
+}
+
+bool BPFAbstractMemberAccess::runOnModule(Module &M) {
+ LLVM_DEBUG(dbgs() << "********** Abstract Member Accesses **********\n");
+
+ // Bail out if no debug info.
+ if (empty(M.debug_compile_units()))
+ return false;
+
+ return doTransformation(M);
+}
+
+/// Check whether a call is a preserve_*_access_index intrinsic call or not.
+bool BPFAbstractMemberAccess::IsPreserveDIAccessIndexCall(const CallInst *Call,
+ uint32_t &Kind) {
+ if (!Call)
+ return false;
+
+ const auto *GV = dyn_cast<GlobalValue>(Call->getCalledValue());
+ if (!GV)
+ return false;
+ if (GV->getName().startswith("llvm.preserve.array.access.index")) {
+ Kind = BPFPreserveArrayAI;
+ return true;
+ }
+ if (GV->getName().startswith("llvm.preserve.union.access.index")) {
+ Kind = BPFPreserveUnionAI;
+ return true;
+ }
+ if (GV->getName().startswith("llvm.preserve.struct.access.index")) {
+ Kind = BPFPreserveStructAI;
+ return true;
+ }
+
+ return false;
+}
+
+void BPFAbstractMemberAccess::replaceWithGEP(std::vector<CallInst *> &CallList,
+ uint32_t DimensionIndex,
+ uint32_t GEPIndex) {
+ for (auto Call : CallList) {
+ uint32_t Dimension = 1;
+ if (DimensionIndex > 0)
+ Dimension = cast<ConstantInt>(Call->getArgOperand(DimensionIndex))
+ ->getZExtValue();
+
+ Constant *Zero =
+ ConstantInt::get(Type::getInt32Ty(Call->getParent()->getContext()), 0);
+ SmallVector<Value *, 4> IdxList;
+ for (unsigned I = 0; I < Dimension; ++I)
+ IdxList.push_back(Zero);
+ IdxList.push_back(Call->getArgOperand(GEPIndex));
+
+ auto *GEP = GetElementPtrInst::CreateInBounds(Call->getArgOperand(0),
+ IdxList, "", Call);
+ Call->replaceAllUsesWith(GEP);
+ Call->eraseFromParent();
+ }
+}
+
+bool BPFAbstractMemberAccess::removePreserveAccessIndexIntrinsic(Module &M) {
+ std::vector<CallInst *> PreserveArrayIndexCalls;
+ std::vector<CallInst *> PreserveUnionIndexCalls;
+ std::vector<CallInst *> PreserveStructIndexCalls;
+ bool Found = false;
+
+ for (Function &F : M)
+ for (auto &BB : F)
+ for (auto &I : BB) {
+ auto *Call = dyn_cast<CallInst>(&I);
+ uint32_t Kind;
+ if (!IsPreserveDIAccessIndexCall(Call, Kind))
+ continue;
+
+ Found = true;
+ if (Kind == BPFPreserveArrayAI)
+ PreserveArrayIndexCalls.push_back(Call);
+ else if (Kind == BPFPreserveUnionAI)
+ PreserveUnionIndexCalls.push_back(Call);
+ else
+ PreserveStructIndexCalls.push_back(Call);
+ }
+
+ // do the following transformation:
+ // . addr = preserve_array_access_index(base, dimension, index)
+ // is transformed to
+ // addr = GEP(base, dimenion's zero's, index)
+ // . addr = preserve_union_access_index(base, di_index)
+ // is transformed to
+ // addr = base, i.e., all usages of "addr" are replaced by "base".
+ // . addr = preserve_struct_access_index(base, gep_index, di_index)
+ // is transformed to
+ // addr = GEP(base, 0, gep_index)
+ replaceWithGEP(PreserveArrayIndexCalls, 1, 2);
+ replaceWithGEP(PreserveStructIndexCalls, 0, 1);
+ for (auto Call : PreserveUnionIndexCalls) {
+ Call->replaceAllUsesWith(Call->getArgOperand(0));
+ Call->eraseFromParent();
+ }
+
+ return Found;
+}
+
+void BPFAbstractMemberAccess::traceAICall(CallInst *Call, uint32_t Kind) {
+ for (User *U : Call->users()) {
+ Instruction *Inst = dyn_cast<Instruction>(U);
+ if (!Inst)
+ continue;
+
+ if (auto *BI = dyn_cast<BitCastInst>(Inst)) {
+ traceBitCast(BI, Call, Kind);
+ } else if (auto *CI = dyn_cast<CallInst>(Inst)) {
+ uint32_t CIKind;
+ if (IsPreserveDIAccessIndexCall(CI, CIKind)) {
+ AIChain[CI] = std::make_pair(Call, Kind);
+ traceAICall(CI, CIKind);
+ } else {
+ BaseAICalls[Call] = Kind;
+ }
+ } else if (auto *GI = dyn_cast<GetElementPtrInst>(Inst)) {
+ if (GI->hasAllZeroIndices())
+ traceGEP(GI, Call, Kind);
+ else
+ BaseAICalls[Call] = Kind;
+ }
+ }
+}
+
+void BPFAbstractMemberAccess::traceBitCast(BitCastInst *BitCast,
+ CallInst *Parent, uint32_t Kind) {
+ for (User *U : BitCast->users()) {
+ Instruction *Inst = dyn_cast<Instruction>(U);
+ if (!Inst)
+ continue;
+
+ if (auto *BI = dyn_cast<BitCastInst>(Inst)) {
+ traceBitCast(BI, Parent, Kind);
+ } else if (auto *CI = dyn_cast<CallInst>(Inst)) {
+ uint32_t CIKind;
+ if (IsPreserveDIAccessIndexCall(CI, CIKind)) {
+ AIChain[CI] = std::make_pair(Parent, Kind);
+ traceAICall(CI, CIKind);
+ } else {
+ BaseAICalls[Parent] = Kind;
+ }
+ } else if (auto *GI = dyn_cast<GetElementPtrInst>(Inst)) {
+ if (GI->hasAllZeroIndices())
+ traceGEP(GI, Parent, Kind);
+ else
+ BaseAICalls[Parent] = Kind;
+ }
+ }
+}
+
+void BPFAbstractMemberAccess::traceGEP(GetElementPtrInst *GEP, CallInst *Parent,
+ uint32_t Kind) {
+ for (User *U : GEP->users()) {
+ Instruction *Inst = dyn_cast<Instruction>(U);
+ if (!Inst)
+ continue;
+
+ if (auto *BI = dyn_cast<BitCastInst>(Inst)) {
+ traceBitCast(BI, Parent, Kind);
+ } else if (auto *CI = dyn_cast<CallInst>(Inst)) {
+ uint32_t CIKind;
+ if (IsPreserveDIAccessIndexCall(CI, CIKind)) {
+ AIChain[CI] = std::make_pair(Parent, Kind);
+ traceAICall(CI, CIKind);
+ } else {
+ BaseAICalls[Parent] = Kind;
+ }
+ } else if (auto *GI = dyn_cast<GetElementPtrInst>(Inst)) {
+ if (GI->hasAllZeroIndices())
+ traceGEP(GI, Parent, Kind);
+ else
+ BaseAICalls[Parent] = Kind;
+ }
+ }
+}
+
+void BPFAbstractMemberAccess::collectAICallChains(Module &M, Function &F) {
+ AIChain.clear();
+ BaseAICalls.clear();
+
+ for (auto &BB : F)
+ for (auto &I : BB) {
+ uint32_t Kind;
+ auto *Call = dyn_cast<CallInst>(&I);
+ if (!IsPreserveDIAccessIndexCall(Call, Kind) ||
+ AIChain.find(Call) != AIChain.end())
+ continue;
+
+ traceAICall(Call, Kind);
+ }
+}
+
+/// Get access index from the preserve_*_access_index intrinsic calls.
+bool BPFAbstractMemberAccess::getAccessIndex(const Value *IndexValue,
+ uint64_t &AccessIndex) {
+ const ConstantInt *CV = dyn_cast<ConstantInt>(IndexValue);
+ if (!CV)
+ return false;
+
+ AccessIndex = CV->getValue().getZExtValue();
+ return true;
+}
+
+/// Compute the base of the whole preserve_*_access_index chains, i.e., the base
+/// pointer of the first preserve_*_access_index call, and construct the access
+/// string, which will be the name of a global variable.
+Value *BPFAbstractMemberAccess::computeBaseAndAccessStr(CallInst *Call,
+ std::string &AccessStr,
+ std::string &AccessKey,
+ uint32_t Kind,
+ MDNode *&TypeMeta) {
+ Value *Base = nullptr;
+ std::vector<uint64_t> AccessIndices;
+ uint64_t TypeNameIndex = 0;
+ std::string LastTypeName;
+
+ while (Call) {
+ // Base of original corresponding GEP
+ Base = Call->getArgOperand(0);
+
+ // Type Name
+ std::string TypeName;
+ MDNode *MDN;
+ if (Kind == BPFPreserveUnionAI || Kind == BPFPreserveStructAI) {
+ MDN = Call->getMetadata(LLVMContext::MD_preserve_access_index);
+ if (!MDN)
+ return nullptr;
+
+ DIType *Ty = dyn_cast<DIType>(MDN);
+ if (!Ty)
+ return nullptr;
+
+ TypeName = Ty->getName();
+ }
+
+ // Access Index
+ uint64_t AccessIndex;
+ uint32_t ArgIndex = (Kind == BPFPreserveUnionAI) ? 1 : 2;
+ if (!getAccessIndex(Call->getArgOperand(ArgIndex), AccessIndex))
+ return nullptr;
+
+ AccessIndices.push_back(AccessIndex);
+ if (TypeName.size()) {
+ TypeNameIndex = AccessIndices.size() - 1;
+ LastTypeName = TypeName;
+ TypeMeta = MDN;
+ }
+
+ Kind = AIChain[Call].second;
+ Call = AIChain[Call].first;
+ }
+
+ // The intial type name is required.
+ // FIXME: if the initial type access is an array index, e.g.,
+ // &a[3].b.c, only one dimentional array is supported.
+ if (!LastTypeName.size() || AccessIndices.size() > TypeNameIndex + 2)
+ return nullptr;
+
+ // Construct the type string AccessStr.
+ for (unsigned I = 0; I < AccessIndices.size(); ++I)
+ AccessStr = std::to_string(AccessIndices[I]) + ":" + AccessStr;
+
+ if (TypeNameIndex == AccessIndices.size() - 1)
+ AccessStr = "0:" + AccessStr;
+
+ // Access key is the type name + access string, uniquely identifying
+ // one kernel memory access.
+ AccessKey = LastTypeName + ":" + AccessStr;
+
+ return Base;
+}
+
+/// Call/Kind is the base preserve_*_access_index() call. Attempts to do
+/// transformation to a chain of relocable GEPs.
+bool BPFAbstractMemberAccess::transformGEPChain(Module &M, CallInst *Call,
+ uint32_t Kind) {
+ std::string AccessStr, AccessKey;
+ MDNode *TypeMeta = nullptr;
+ Value *Base =
+ computeBaseAndAccessStr(Call, AccessStr, AccessKey, Kind, TypeMeta);
+ if (!Base)
+ return false;
+
+ // Do the transformation
+ // For any original GEP Call and Base %2 like
+ // %4 = bitcast %struct.net_device** %dev1 to i64*
+ // it is transformed to:
+ // %6 = load __BTF_0:sk_buff:0:0:2:0:
+ // %7 = bitcast %struct.sk_buff* %2 to i8*
+ // %8 = getelementptr i8, i8* %7, %6
+ // %9 = bitcast i8* %8 to i64*
+ // using %9 instead of %4
+ // The original Call inst is removed.
+ BasicBlock *BB = Call->getParent();
+ GlobalVariable *GV;
+
+ if (GEPGlobals.find(AccessKey) == GEPGlobals.end()) {
+ GV = new GlobalVariable(M, Type::getInt64Ty(BB->getContext()), false,
+ GlobalVariable::ExternalLinkage, NULL, AccessStr);
+ GV->addAttribute(BPFCoreSharedInfo::AmaAttr);
+ // Set the metadata (debuginfo types) for the global.
+ if (TypeMeta)
+ GV->setMetadata(LLVMContext::MD_preserve_access_index, TypeMeta);
+ GEPGlobals[AccessKey] = GV;
+ } else {
+ GV = GEPGlobals[AccessKey];
+ }
+
+ // Load the global variable.
+ auto *LDInst = new LoadInst(Type::getInt64Ty(BB->getContext()), GV);
+ BB->getInstList().insert(Call->getIterator(), LDInst);
+
+ // Generate a BitCast
+ auto *BCInst = new BitCastInst(Base, Type::getInt8PtrTy(BB->getContext()));
+ BB->getInstList().insert(Call->getIterator(), BCInst);
+
+ // Generate a GetElementPtr
+ auto *GEP = GetElementPtrInst::Create(Type::getInt8Ty(BB->getContext()),
+ BCInst, LDInst);
+ BB->getInstList().insert(Call->getIterator(), GEP);
+
+ // Generate a BitCast
+ auto *BCInst2 = new BitCastInst(GEP, Call->getType());
+ BB->getInstList().insert(Call->getIterator(), BCInst2);
+
+ Call->replaceAllUsesWith(BCInst2);
+ Call->eraseFromParent();
+
+ return true;
+}
+
+bool BPFAbstractMemberAccess::doTransformation(Module &M) {
+ bool Transformed = false;
+
+ for (Function &F : M) {
+ // Collect PreserveDIAccessIndex Intrinsic call chains.
+ // The call chains will be used to generate the access
+ // patterns similar to GEP.
+ collectAICallChains(M, F);
+
+ for (auto &C : BaseAICalls)
+ Transformed = transformGEPChain(M, C.first, C.second) || Transformed;
+ }
+
+ return removePreserveAccessIndexIntrinsic(M) || Transformed;
+}
public:
explicit BPFAsmPrinter(TargetMachine &TM,
std::unique_ptr<MCStreamer> Streamer)
- : AsmPrinter(TM, std::move(Streamer)) {}
+ : AsmPrinter(TM, std::move(Streamer)), BTF(nullptr) {}
StringRef getPassName() const override { return "BPF Assembly Printer"; }
bool doInitialization(Module &M) override;
const char *ExtraCode, raw_ostream &O) override;
void EmitInstruction(const MachineInstr *MI) override;
+
+private:
+ BTFDebug *BTF;
};
} // namespace
// Only emit BTF when debuginfo available.
if (MAI->doesSupportDebugInformation() && !empty(M.debug_compile_units())) {
- Handlers.emplace_back(llvm::make_unique<BTFDebug>(this), "emit",
- "Debug Info Emission", "BTF", "BTF Emission");
+ BTF = new BTFDebug(this);
+ Handlers.push_back(HandlerInfo(std::unique_ptr<BTFDebug>(BTF), "emit",
+ "Debug Info Emission", "BTF",
+ "BTF Emission"));
}
return false;
}
void BPFAsmPrinter::EmitInstruction(const MachineInstr *MI) {
-
- BPFMCInstLower MCInstLowering(OutContext, *this);
-
MCInst TmpInst;
- MCInstLowering.Lower(MI, TmpInst);
+
+ if (!BTF || !BTF->InstLower(MI, TmpInst)) {
+ BPFMCInstLower MCInstLowering(OutContext, *this);
+ MCInstLowering.Lower(MI, TmpInst);
+ }
EmitToStreamer(*OutStreamer, TmpInst);
}
--- /dev/null
+//===- BPFCORE.h - Common info for Compile-Once Run-EveryWhere -*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_BPF_BPFCORE_H
+#define LLVM_LIB_TARGET_BPF_BPFCORE_H
+
+namespace llvm {
+
+class BPFCoreSharedInfo {
+public:
+ /// The attribute attached to globals representing a member offset
+ static const std::string AmaAttr;
+ /// The section name to identify a patchable external global
+ static const std::string PatchableExtSecName;
+};
+
+} // namespace llvm
+
+#endif
--- /dev/null
+//===----- 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 either present a AMA (abstruct member access) or
+// a patchable extern variable. And these two kinds of accesses
+// are 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 offset> or <to_be_patched_extern_val>
+// add r3, struct_base_reg, r1
+// or
+// ld_imm64 r1, <to_be_patched_extern_val>
+// add r3, struct_base_reg, r1
+//
+//===----------------------------------------------------------------------===//
+
+#include "BPF.h"
+#include "BPFCORE.h"
+#include "BPFInstrInfo.h"
+#include "BPFTargetMachine.h"
+#include "llvm/CodeGen/MachineInstrBuilder.h"
+#include "llvm/CodeGen/MachineRegisterInfo.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);
+
+public:
+ // Main entry point for this pass.
+ bool runOnMachineFunction(MachineFunction &MF) override {
+ if (!skipFunction(MF.getFunction())) {
+ 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");
+}
+
+/// 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;
+
+ unsigned DstReg = MI.getOperand(0).getReg();
+ unsigned SrcReg = MI.getOperand(1).getReg();
+ int64_t ImmVal = MI.getOperand(2).getImm();
+
+ 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<GlobalVariable>(GVal);
+ if (GVar) {
+ // Global variables representing structure offset or
+ // patchable extern globals.
+ if (GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr)) {
+ assert(ImmVal == 0);
+ IsCandidate = true;
+ } else if (!GVar->hasInitializer() && GVar->hasExternalLinkage() &&
+ GVar->getSection() ==
+ BPFCoreSharedInfo::PatchableExtSecName) {
+ if (ImmVal == 0)
+ IsCandidate = true;
+ else
+ errs() << "WARNING: unhandled patchable extern "
+ << GVar->getName() << " with load offset " << ImmVal
+ << "\n";
+ }
+ }
+ }
+ }
+
+ if (!IsCandidate)
+ continue;
+
+ 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(BPFMISimplifyPatchable, DEBUG_TYPE,
+ "BPF PreEmit SimplifyPatchable", false, false)
+
+char BPFMISimplifyPatchable::ID = 0;
+FunctionPass *llvm::createBPFMISimplifyPatchablePass() {
+ return new BPFMISimplifyPatchable();
+}
RegisterTargetMachine<BPFTargetMachine> Z(getTheBPFTarget());
PassRegistry &PR = *PassRegistry::getPassRegistry();
+ initializeBPFAbstractMemberAccessPass(PR);
initializeBPFMIPeepholePass(PR);
}
static_cast<BPFMCAsmInfo *>(const_cast<MCAsmInfo *>(AsmInfo.get()));
MAI->setDwarfUsesRelocationsAcrossSections(!Subtarget.getUseDwarfRIS());
}
+
namespace {
// BPF Code Generator Pass Configuration Options.
class BPFPassConfig : public TargetPassConfig {
return getTM<BPFTargetMachine>();
}
+ void addIRPasses() override;
bool addInstSelector() override;
void addMachineSSAOptimization() override;
void addPreEmitPass() override;
return new BPFPassConfig(*this, PM);
}
+void BPFPassConfig::addIRPasses() {
+
+ addPass(createBPFAbstractMemberAccess());
+
+ TargetPassConfig::addIRPasses();
+}
+
// Install an instruction selector pass using
// the ISelDag to gen BPF code.
bool BPFPassConfig::addInstSelector() {
}
void BPFPassConfig::addMachineSSAOptimization() {
+ addPass(createBPFMISimplifyPatchablePass());
+
// The default implementation must be called first as we want eBPF
// Peephole ran at last.
TargetPassConfig::addMachineSSAOptimization();
///
/// The binary layout for .BTF.ext section:
/// struct ExtHeader
-/// FuncInfo and LineInfo subsections
+/// FuncInfo, LineInfo, OffsetReloc and ExternReloc subsections
/// The FuncInfo subsection is defined as below:
/// BTFFuncInfo Size
/// struct SecFuncInfo for ELF section #1
/// struct SecLineInfo for ELF section #2
/// A number of struct BPFLineInfo for ELF section #2
/// ...
+/// The OffsetReloc subsection is defined as below:
+/// BPFOffsetReloc Size
+/// struct SecOffsetReloc for ELF section #1
+/// A number of struct BPFOffsetReloc for ELF section #1
+/// struct SecOffsetReloc for ELF section #2
+/// A number of struct BPFOffsetReloc for ELF section #2
+/// ...
+/// The ExternReloc subsection is defined as below:
+/// BPFExternReloc Size
+/// struct SecExternReloc for ELF section #1
+/// A number of struct BPFExternReloc for ELF section #1
+/// struct SecExternReloc for ELF section #2
+/// A number of struct BPFExternReloc for ELF section #2
+/// ...
///
/// The section formats are also defined at
/// https://github.com/torvalds/linux/blob/master/include/uapi/linux/btf.h
/// Sizes in bytes of various things in the BTF format.
enum {
HeaderSize = 24,
- ExtHeaderSize = 24,
+ ExtHeaderSize = 40,
CommonTypeSize = 12,
BTFArraySize = 12,
BTFEnumSize = 8,
BTFDataSecVarSize = 12,
SecFuncInfoSize = 8,
SecLineInfoSize = 8,
+ SecOffsetRelocSize = 8,
+ SecExternRelocSize = 8,
BPFFuncInfoSize = 8,
- BPFLineInfoSize = 16
+ BPFLineInfoSize = 16,
+ BPFOffsetRelocSize = 12,
+ BPFExternRelocSize = 8,
};
/// The .BTF section header definition.
uint8_t Flags;
uint32_t HdrLen;
- uint32_t FuncInfoOff; ///< Offset of func info section
- uint32_t FuncInfoLen; ///< Length of func info section
- uint32_t LineInfoOff; ///< Offset of line info section
- uint32_t LineInfoLen; ///< Length of line info section
+ uint32_t FuncInfoOff; ///< Offset of func info section
+ uint32_t FuncInfoLen; ///< Length of func info section
+ uint32_t LineInfoOff; ///< Offset of line info section
+ uint32_t LineInfoLen; ///< Length of line info section
+ uint32_t OffsetRelocOff; ///< Offset of offset reloc section
+ uint32_t OffsetRelocLen; ///< Length of offset reloc section
+ uint32_t ExternRelocOff; ///< Offset of extern reloc section
+ uint32_t ExternRelocLen; ///< Length of extern reloc section
};
/// Specifying one function info.
/// Specifying line info's in one section.
struct SecLineInfo {
- uint32_t SecNameOff; ///< Section name index in the .BTF string tble
+ uint32_t SecNameOff; ///< Section name index in the .BTF string table
uint32_t NumLineInfo; ///< Number of line info's in this section
};
+/// Specifying one offset relocation.
+struct BPFOffsetReloc {
+ uint32_t InsnOffset; ///< Byte offset in this section
+ uint32_t TypeID; ///< TypeID for the relocation
+ uint32_t OffsetNameOff; ///< The string to traverse types
+};
+
+/// Specifying offset relocation's in one section.
+struct SecOffsetReloc {
+ uint32_t SecNameOff; ///< Section name index in the .BTF string table
+ uint32_t NumOffsetReloc; ///< Number of offset reloc's in this section
+};
+
+/// Specifying one offset relocation.
+struct BPFExternReloc {
+ uint32_t InsnOffset; ///< Byte offset in this section
+ uint32_t ExternNameOff; ///< The string for external variable
+};
+
+/// Specifying extern relocation's in one section.
+struct SecExternReloc {
+ uint32_t SecNameOff; ///< Section name index in the .BTF string table
+ uint32_t NumExternReloc; ///< Number of extern reloc's in this section
+};
+
} // End namespace BTF.
} // End namespace llvm.
//===----------------------------------------------------------------------===//
#include "BTFDebug.h"
+#include "BPF.h"
+#include "BPFCORE.h"
+#include "MCTargetDesc/BPFMCTargetDesc.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
OS.EmitIntValue(BTFType.Size, 4);
}
-BTFTypeDerived::BTFTypeDerived(const DIDerivedType *DTy, unsigned Tag)
- : DTy(DTy) {
+BTFTypeDerived::BTFTypeDerived(const DIDerivedType *DTy, unsigned Tag,
+ bool NeedsFixup)
+ : DTy(DTy), NeedsFixup(NeedsFixup) {
switch (Tag) {
case dwarf::DW_TAG_pointer_type:
Kind = BTF::BTF_KIND_PTR;
}
void BTFTypeDerived::completeType(BTFDebug &BDebug) {
+ if (IsCompleted)
+ return;
+ IsCompleted = true;
+
BTFType.NameOff = BDebug.addString(DTy->getName());
+ if (NeedsFixup)
+ return;
+
// The base type for PTR/CONST/VOLATILE could be void.
const DIType *ResolvedType = DTy->getBaseType();
if (!ResolvedType) {
void BTFTypeDerived::emitType(MCStreamer &OS) { BTFTypeBase::emitType(OS); }
+void BTFTypeDerived::setPointeeType(uint32_t PointeeType) {
+ BTFType.Type = PointeeType;
+}
+
/// Represent a struct/union forward declaration.
BTFTypeFwd::BTFTypeFwd(StringRef Name, bool IsUnion) : Name(Name) {
Kind = BTF::BTF_KIND_FWD;
}
void BTFTypeFwd::completeType(BTFDebug &BDebug) {
+ if (IsCompleted)
+ return;
+ IsCompleted = true;
+
BTFType.NameOff = BDebug.addString(Name);
}
}
void BTFTypeInt::completeType(BTFDebug &BDebug) {
+ if (IsCompleted)
+ return;
+ IsCompleted = true;
+
BTFType.NameOff = BDebug.addString(Name);
}
}
void BTFTypeEnum::completeType(BTFDebug &BDebug) {
+ if (IsCompleted)
+ return;
+ IsCompleted = true;
+
BTFType.NameOff = BDebug.addString(ETy->getName());
DINodeArray Elements = ETy->getElements();
}
}
-BTFTypeArray::BTFTypeArray(uint32_t ElemTypeId, uint32_t NumElems) {
+BTFTypeArray::BTFTypeArray(uint32_t ElemTypeId, uint32_t ElemSize,
+ uint32_t NumElems)
+ : ElemSize(ElemSize) {
Kind = BTF::BTF_KIND_ARRAY;
BTFType.NameOff = 0;
BTFType.Info = Kind << 24;
/// Represent a BTF array.
void BTFTypeArray::completeType(BTFDebug &BDebug) {
+ if (IsCompleted)
+ return;
+ IsCompleted = true;
// The IR does not really have a type for the index.
// A special type for array index should have been
OS.EmitIntValue(ArrayInfo.Nelems, 4);
}
+void BTFTypeArray::getLocInfo(uint32_t Loc, uint32_t &LocOffset,
+ uint32_t &ElementTypeId) {
+ ElementTypeId = ArrayInfo.ElemType;
+ LocOffset = Loc * ElemSize;
+}
+
/// Represent either a struct or a union.
BTFTypeStruct::BTFTypeStruct(const DICompositeType *STy, bool IsStruct,
bool HasBitField, uint32_t Vlen)
}
void BTFTypeStruct::completeType(BTFDebug &BDebug) {
+ if (IsCompleted)
+ return;
+ IsCompleted = true;
+
BTFType.NameOff = BDebug.addString(STy->getName());
// Add struct/union members.
}
}
+std::string BTFTypeStruct::getName() { return STy->getName(); }
+
+void BTFTypeStruct::getMemberInfo(uint32_t Loc, uint32_t &MemberOffset,
+ uint32_t &MemberType) {
+ MemberType = Members[Loc].Type;
+ MemberOffset =
+ HasBitField ? Members[Loc].Offset & 0xffffff : Members[Loc].Offset;
+}
+
+uint32_t BTFTypeStruct::getStructSize() { return STy->getSizeInBits() >> 3; }
+
/// The Func kind represents both subprogram and pointee of function
/// pointers. If the FuncName is empty, it represents a pointee of function
/// pointer. Otherwise, it represents a subprogram. The func arg names
}
void BTFTypeFuncProto::completeType(BTFDebug &BDebug) {
+ if (IsCompleted)
+ return;
+ IsCompleted = true;
+
DITypeRefArray Elements = STy->getTypeArray();
auto RetType = Elements[0];
BTFType.Type = RetType ? BDebug.getTypeId(RetType) : 0;
}
void BTFTypeFunc::completeType(BTFDebug &BDebug) {
+ if (IsCompleted)
+ return;
+ IsCompleted = true;
+
BTFType.NameOff = BDebug.addString(Name);
}
BTFDebug::BTFDebug(AsmPrinter *AP)
: DebugHandlerBase(AP), OS(*Asm->OutStreamer), SkipInstruction(false),
- LineInfoGenerated(false), SecNameOff(0), ArrayIndexTypeId(0) {
+ LineInfoGenerated(false), SecNameOff(0), ArrayIndexTypeId(0),
+ MapDefNotCollected(true) {
addString("\0");
}
auto TypeEntry =
llvm::make_unique<BTFTypeStruct>(CTy, IsStruct, HasBitField, VLen);
+ StructTypes.push_back(TypeEntry.get());
TypeId = addType(std::move(TypeEntry), CTy);
// Visit all struct members.
void BTFDebug::visitArrayType(const DICompositeType *CTy, uint32_t &TypeId) {
// Visit array element type.
- uint32_t ElemTypeId;
- visitTypeEntry(CTy->getBaseType(), ElemTypeId);
+ uint32_t ElemTypeId, ElemSize;
+ const DIType *ElemType = CTy->getBaseType();
+ visitTypeEntry(ElemType, ElemTypeId, false, false);
+ ElemSize = ElemType->getSizeInBits() >> 3;
if (!CTy->getSizeInBits()) {
- auto TypeEntry = llvm::make_unique<BTFTypeArray>(ElemTypeId, 0);
+ auto TypeEntry = llvm::make_unique<BTFTypeArray>(ElemTypeId, 0, 0);
+ ArrayTypes.push_back(TypeEntry.get());
ElemTypeId = addType(std::move(TypeEntry), CTy);
} else {
// Visit array dimensions.
auto *CI = SR->getCount().dyn_cast<ConstantInt *>();
int64_t Count = CI->getSExtValue();
- auto TypeEntry = llvm::make_unique<BTFTypeArray>(ElemTypeId, Count);
+ auto TypeEntry =
+ llvm::make_unique<BTFTypeArray>(ElemTypeId, ElemSize, Count);
+ ArrayTypes.push_back(TypeEntry.get());
if (I == 0)
ElemTypeId = addType(std::move(TypeEntry), CTy);
else
ElemTypeId = addType(std::move(TypeEntry));
+ ElemSize = ElemSize * Count;
}
}
}
}
/// Handle pointer, typedef, const, volatile, restrict and member types.
-void BTFDebug::visitDerivedType(const DIDerivedType *DTy, uint32_t &TypeId) {
+void BTFDebug::visitDerivedType(const DIDerivedType *DTy, uint32_t &TypeId,
+ bool CheckPointer, bool SeenPointer) {
unsigned Tag = DTy->getTag();
+ /// Try to avoid chasing pointees, esp. structure pointees which may
+ /// unnecessary bring in a lot of types.
+ if (CheckPointer && !SeenPointer) {
+ SeenPointer = Tag == dwarf::DW_TAG_pointer_type;
+ }
+
+ if (CheckPointer && SeenPointer) {
+ const DIType *Base = DTy->getBaseType();
+ if (Base) {
+ if (const auto *CTy = dyn_cast<DICompositeType>(Base)) {
+ auto CTag = CTy->getTag();
+ if ((CTag == dwarf::DW_TAG_structure_type ||
+ CTag == dwarf::DW_TAG_union_type) &&
+ !CTy->isForwardDecl()) {
+ /// Find a candidate, generate a fixup. Later on the struct/union
+ /// pointee type will be replaced with either a real type or
+ /// a forward declaration.
+ auto TypeEntry = llvm::make_unique<BTFTypeDerived>(DTy, Tag, true);
+ auto &Fixup = FixupDerivedTypes[CTy->getName()];
+ Fixup.first = CTag == dwarf::DW_TAG_union_type;
+ Fixup.second.push_back(TypeEntry.get());
+ TypeId = addType(std::move(TypeEntry), DTy);
+ return;
+ }
+ }
+ }
+ }
+
if (Tag == dwarf::DW_TAG_pointer_type || Tag == dwarf::DW_TAG_typedef ||
Tag == dwarf::DW_TAG_const_type || Tag == dwarf::DW_TAG_volatile_type ||
Tag == dwarf::DW_TAG_restrict_type) {
- auto TypeEntry = llvm::make_unique<BTFTypeDerived>(DTy, Tag);
+ auto TypeEntry = llvm::make_unique<BTFTypeDerived>(DTy, Tag, false);
TypeId = addType(std::move(TypeEntry), DTy);
} else if (Tag != dwarf::DW_TAG_member) {
return;
// Visit base type of pointer, typedef, const, volatile, restrict or
// struct/union member.
uint32_t TempTypeId = 0;
- visitTypeEntry(DTy->getBaseType(), TempTypeId);
+ if (Tag == dwarf::DW_TAG_member)
+ visitTypeEntry(DTy->getBaseType(), TempTypeId, true, false);
+ else
+ visitTypeEntry(DTy->getBaseType(), TempTypeId, CheckPointer, SeenPointer);
}
-void BTFDebug::visitTypeEntry(const DIType *Ty, uint32_t &TypeId) {
+void BTFDebug::visitTypeEntry(const DIType *Ty, uint32_t &TypeId,
+ bool CheckPointer, bool SeenPointer) {
if (!Ty || DIToIdMap.find(Ty) != DIToIdMap.end()) {
TypeId = DIToIdMap[Ty];
return;
else if (const auto *CTy = dyn_cast<DICompositeType>(Ty))
visitCompositeType(CTy, TypeId);
else if (const auto *DTy = dyn_cast<DIDerivedType>(Ty))
- visitDerivedType(DTy, TypeId);
+ visitDerivedType(DTy, TypeId, CheckPointer, SeenPointer);
else
llvm_unreachable("Unknown DIType");
}
void BTFDebug::visitTypeEntry(const DIType *Ty) {
uint32_t TypeId;
- visitTypeEntry(Ty, TypeId);
+ visitTypeEntry(Ty, TypeId, false, false);
+}
+
+void BTFDebug::visitMapDefType(const DIType *Ty, uint32_t &TypeId) {
+ if (!Ty || DIToIdMap.find(Ty) != DIToIdMap.end()) {
+ TypeId = DIToIdMap[Ty];
+ return;
+ }
+
+ // MapDef type is a struct type
+ const auto *CTy = dyn_cast<DICompositeType>(Ty);
+ if (!CTy)
+ return;
+
+ auto Tag = CTy->getTag();
+ if (Tag != dwarf::DW_TAG_structure_type || CTy->isForwardDecl())
+ return;
+
+ // Record this type
+ const DINodeArray Elements = CTy->getElements();
+ bool HasBitField = false;
+ for (const auto *Element : Elements) {
+ auto E = cast<DIDerivedType>(Element);
+ if (E->isBitField()) {
+ HasBitField = true;
+ break;
+ }
+ }
+
+ auto TypeEntry =
+ llvm::make_unique<BTFTypeStruct>(CTy, true, HasBitField, Elements.size());
+ StructTypes.push_back(TypeEntry.get());
+ TypeId = addType(std::move(TypeEntry), CTy);
+
+ // Visit all struct members
+ for (const auto *Element : Elements) {
+ const auto *MemberType = cast<DIDerivedType>(Element);
+ visitTypeEntry(MemberType->getBaseType());
+ }
}
/// Read file contents from the actual file or from the source
void BTFDebug::emitBTFExtSection() {
// Do not emit section if empty FuncInfoTable and LineInfoTable.
- if (!FuncInfoTable.size() && !LineInfoTable.size())
+ if (!FuncInfoTable.size() && !LineInfoTable.size() &&
+ !OffsetRelocTable.size() && !ExternRelocTable.size())
return;
MCContext &Ctx = OS.getContext();
// Account for FuncInfo/LineInfo record size as well.
uint32_t FuncLen = 4, LineLen = 4;
+ // Do not account for optional OffsetReloc/ExternReloc.
+ uint32_t OffsetRelocLen = 0, ExternRelocLen = 0;
for (const auto &FuncSec : FuncInfoTable) {
FuncLen += BTF::SecFuncInfoSize;
FuncLen += FuncSec.second.size() * BTF::BPFFuncInfoSize;
LineLen += BTF::SecLineInfoSize;
LineLen += LineSec.second.size() * BTF::BPFLineInfoSize;
}
+ for (const auto &OffsetRelocSec : OffsetRelocTable) {
+ OffsetRelocLen += BTF::SecOffsetRelocSize;
+ OffsetRelocLen += OffsetRelocSec.second.size() * BTF::BPFOffsetRelocSize;
+ }
+ for (const auto &ExternRelocSec : ExternRelocTable) {
+ ExternRelocLen += BTF::SecExternRelocSize;
+ ExternRelocLen += ExternRelocSec.second.size() * BTF::BPFExternRelocSize;
+ }
+
+ if (OffsetRelocLen)
+ OffsetRelocLen += 4;
+ if (ExternRelocLen)
+ ExternRelocLen += 4;
OS.EmitIntValue(0, 4);
OS.EmitIntValue(FuncLen, 4);
OS.EmitIntValue(FuncLen, 4);
OS.EmitIntValue(LineLen, 4);
+ OS.EmitIntValue(FuncLen + LineLen, 4);
+ OS.EmitIntValue(OffsetRelocLen, 4);
+ OS.EmitIntValue(FuncLen + LineLen + OffsetRelocLen, 4);
+ OS.EmitIntValue(ExternRelocLen, 4);
// Emit func_info table.
OS.AddComment("FuncInfo");
OS.EmitIntValue(LineInfo.LineNum << 10 | LineInfo.ColumnNum, 4);
}
}
+
+ // Emit offset reloc table.
+ if (OffsetRelocLen) {
+ OS.AddComment("OffsetReloc");
+ OS.EmitIntValue(BTF::BPFOffsetRelocSize, 4);
+ for (const auto &OffsetRelocSec : OffsetRelocTable) {
+ OS.AddComment("Offset reloc section string offset=" +
+ std::to_string(OffsetRelocSec.first));
+ OS.EmitIntValue(OffsetRelocSec.first, 4);
+ OS.EmitIntValue(OffsetRelocSec.second.size(), 4);
+ for (const auto &OffsetRelocInfo : OffsetRelocSec.second) {
+ Asm->EmitLabelReference(OffsetRelocInfo.Label, 4);
+ OS.EmitIntValue(OffsetRelocInfo.TypeID, 4);
+ OS.EmitIntValue(OffsetRelocInfo.OffsetNameOff, 4);
+ }
+ }
+ }
+
+ // Emit extern reloc table.
+ if (ExternRelocLen) {
+ OS.AddComment("ExternReloc");
+ OS.EmitIntValue(BTF::BPFExternRelocSize, 4);
+ for (const auto &ExternRelocSec : ExternRelocTable) {
+ OS.AddComment("Extern reloc section string offset=" +
+ std::to_string(ExternRelocSec.first));
+ OS.EmitIntValue(ExternRelocSec.first, 4);
+ OS.EmitIntValue(ExternRelocSec.second.size(), 4);
+ for (const auto &ExternRelocInfo : ExternRelocSec.second) {
+ Asm->EmitLabelReference(ExternRelocInfo.Label, 4);
+ OS.EmitIntValue(ExternRelocInfo.ExternNameOff, 4);
+ }
+ }
+ }
}
void BTFDebug::beginFunctionImpl(const MachineFunction *MF) {
}
SkipInstruction = false;
+ // Collect MapDef types. Map definition needs to collect
+ // pointee types. Do it first. Otherwise, for the following
+ // case:
+ // struct m { ...};
+ // struct t {
+ // struct m *key;
+ // };
+ // foo(struct t *arg);
+ //
+ // struct mapdef {
+ // ...
+ // struct m *key;
+ // ...
+ // } __attribute__((section(".maps"))) hash_map;
+ //
+ // If subroutine foo is traversed first, a type chain
+ // "ptr->struct m(fwd)" will be created and later on
+ // when traversing mapdef, since "ptr->struct m" exists,
+ // the traversal of "struct m" will be omitted.
+ if (MapDefNotCollected) {
+ processGlobals(true);
+ MapDefNotCollected = false;
+ }
+
// Collect all types locally referenced in this function.
// Use RetainedNodes so we can collect all argument names
// even if the argument is not used.
llvm::make_unique<BTFTypeFunc>(SP->getName(), ProtoTypeId);
uint32_t FuncTypeId = addType(std::move(FuncTypeEntry));
+ for (const auto &TypeEntry : TypeEntries)
+ TypeEntry->completeType(*this);
+
// Construct funcinfo and the first lineinfo for the function.
MCSymbol *FuncLabel = Asm->getFunctionBegin();
BTFFuncInfo FuncInfo;
SecNameOff = 0;
}
+/// On-demand populate struct types as requested from abstract member
+/// accessing.
+unsigned BTFDebug::populateStructType(const DIType *Ty) {
+ unsigned Id;
+ visitTypeEntry(Ty, Id, false, false);
+ for (const auto &TypeEntry : TypeEntries)
+ TypeEntry->completeType(*this);
+ return Id;
+}
+
+// Find struct/array debuginfo types given a type id.
+void BTFDebug::setTypeFromId(uint32_t TypeId, BTFTypeStruct **PrevStructType,
+ BTFTypeArray **PrevArrayType) {
+ for (const auto &StructType : StructTypes) {
+ if (StructType->getId() == TypeId) {
+ *PrevStructType = StructType;
+ return;
+ }
+ }
+ for (const auto &ArrayType : ArrayTypes) {
+ if (ArrayType->getId() == TypeId) {
+ *PrevArrayType = ArrayType;
+ return;
+ }
+ }
+}
+
+/// Generate a struct member offset relocation.
+void BTFDebug::generateOffsetReloc(const MachineInstr *MI,
+ const MCSymbol *ORSym, DIType *RootTy,
+ StringRef AccessPattern) {
+ BTFTypeStruct *PrevStructType = nullptr;
+ BTFTypeArray *PrevArrayType = nullptr;
+ unsigned RootId = populateStructType(RootTy);
+ setTypeFromId(RootId, &PrevStructType, &PrevArrayType);
+ unsigned RootTySize = PrevStructType->getStructSize();
+
+ BTFOffsetReloc OffsetReloc;
+ OffsetReloc.Label = ORSym;
+ OffsetReloc.OffsetNameOff = addString(AccessPattern.drop_back());
+ OffsetReloc.TypeID = RootId;
+
+ uint32_t Start = 0, End = 0, Offset = 0;
+ bool FirstAccess = true;
+ for (auto C : AccessPattern) {
+ if (C != ':') {
+ End++;
+ } else {
+ std::string SubStr = AccessPattern.substr(Start, End - Start);
+ int Loc = std::stoi(SubStr);
+
+ if (FirstAccess) {
+ Offset = Loc * RootTySize;
+ FirstAccess = false;
+ } else if (PrevStructType) {
+ uint32_t MemberOffset, MemberTypeId;
+ PrevStructType->getMemberInfo(Loc, MemberOffset, MemberTypeId);
+
+ Offset += MemberOffset >> 3;
+ PrevStructType = nullptr;
+ setTypeFromId(MemberTypeId, &PrevStructType, &PrevArrayType);
+ } else if (PrevArrayType) {
+ uint32_t LocOffset, ElementTypeId;
+ PrevArrayType->getLocInfo(Loc, LocOffset, ElementTypeId);
+
+ Offset += LocOffset;
+ PrevArrayType = nullptr;
+ setTypeFromId(ElementTypeId, &PrevStructType, &PrevArrayType);
+ }
+ Start = End + 1;
+ End = Start;
+ }
+ }
+ AccessOffsets[RootTy->getName().str() + ":" + AccessPattern.str()] = Offset;
+ OffsetRelocTable[SecNameOff].push_back(OffsetReloc);
+}
+
+void BTFDebug::processLDimm64(const MachineInstr *MI) {
+ // If the insn is an LD_imm64, the following two cases
+ // will generate an .BTF.ext record.
+ //
+ // If the insn is "r2 = LD_imm64 @__BTF_...",
+ // add this insn into the .BTF.ext OffsetReloc subsection.
+ // Relocation looks like:
+ // . SecName:
+ // . InstOffset
+ // . TypeID
+ // . OffSetNameOff
+ // 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 @VAR" and VAR is
+ // a patchable external global, add this insn into the .BTF.ext
+ // ExternReloc subsection.
+ // Relocation looks like:
+ // . SecName:
+ // . InstOffset
+ // . ExternNameOff
+ // Later, the insn is replaced with "r2 = <value>" or
+ // "LD_imm64 r2, <value>" where "<value>" = 0.
+
+ // check whether this is a candidate or not
+ const MachineOperand &MO = MI->getOperand(1);
+ 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);
+
+ MDNode *MDN = GVar->getMetadata(LLVMContext::MD_preserve_access_index);
+ DIType *Ty = dyn_cast<DIType>(MDN);
+ generateOffsetReloc(MI, ORSym, Ty, GVar->getName());
+ } else if (GVar && !GVar->hasInitializer() && GVar->hasExternalLinkage() &&
+ GVar->getSection() == BPFCoreSharedInfo::PatchableExtSecName) {
+ MCSymbol *ORSym = OS.getContext().createTempSymbol();
+ OS.EmitLabel(ORSym);
+
+ BTFExternReloc ExternReloc;
+ ExternReloc.Label = ORSym;
+ ExternReloc.ExternNameOff = addString(GVar->getName());
+ ExternRelocTable[SecNameOff].push_back(ExternReloc);
+ }
+ }
+}
+
void BTFDebug::beginInstruction(const MachineInstr *MI) {
DebugHandlerBase::beginInstruction(MI);
return;
}
+ if (MI->getOpcode() == BPF::LD_imm64)
+ processLDimm64(MI);
+
// Skip this instruction if no DebugLoc or the DebugLoc
// is the same as the previous instruction.
const DebugLoc &DL = MI->getDebugLoc();
PrevInstLoc = DL;
}
-void BTFDebug::processGlobals() {
+void BTFDebug::processGlobals(bool ProcessingMapDef) {
// Collect all types referenced by globals.
const Module *M = MMI->getModule();
for (const GlobalVariable &Global : M->globals()) {
if (!Global.hasInitializer() && Global.hasExternalLinkage())
continue;
+ // Decide the section name.
+ StringRef SecName;
+ if (Global.hasSection()) {
+ SecName = Global.getSection();
+ } else {
+ // data, bss, or readonly sections
+ if (Global.isConstant())
+ SecName = ".rodata";
+ else
+ SecName = Global.getInitializer()->isZeroValue() ? ".bss" : ".data";
+ }
+
+ if (ProcessingMapDef != SecName.startswith(".maps"))
+ continue;
+
SmallVector<DIGlobalVariableExpression *, 1> GVs;
Global.getDebugInfo(GVs);
uint32_t GVTypeId = 0;
for (auto *GVE : GVs) {
- visitTypeEntry(GVE->getVariable()->getType(), GVTypeId);
+ if (SecName.startswith(".maps"))
+ visitMapDefType(GVE->getVariable()->getType(), GVTypeId);
+ else
+ visitTypeEntry(GVE->getVariable()->getType(), GVTypeId, false, false);
break;
}
llvm::make_unique<BTFKindVar>(Global.getName(), GVTypeId, GVarInfo);
uint32_t VarId = addType(std::move(VarEntry));
- // Decide the section name.
- std::string SecName;
- if (Global.hasSection()) {
- SecName = Global.getSection().str();
- } else {
- // data, bss, or readonly sections
- if (Global.isConstant())
- SecName += ".rodata";
- else
- SecName += Global.getInitializer()->isZeroValue() ? ".bss" : ".data";
- }
-
// Find or create a DataSec
if (DataSecEntries.find(SecName) == DataSecEntries.end()) {
DataSecEntries[SecName] = llvm::make_unique<BTFKindDataSec>(Asm, SecName);
DataSecEntries[SecName]->addVar(VarId, Asm->getSymbol(&Global), Size);
}
+}
+
+/// Emit proper patchable instructions.
+bool BTFDebug::InstLower(const MachineInstr *MI, MCInst &OutMI) {
+ if (MI->getOpcode() == BPF::LD_imm64) {
+ const MachineOperand &MO = MI->getOperand(1);
+ if (MO.isGlobal()) {
+ const GlobalValue *GVal = MO.getGlobal();
+ auto *GVar = dyn_cast<GlobalVariable>(GVal);
+ if (GVar && GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr)) {
+ MDNode *MDN = GVar->getMetadata(LLVMContext::MD_preserve_access_index);
+ DIType *Ty = dyn_cast<DIType>(MDN);
+ std::string TypeName = Ty->getName();
+ int64_t Imm = AccessOffsets[TypeName + ":" + GVar->getName().str()];
+
+ // Emit "mov ri, <imm>" for abstract member accesses.
+ OutMI.setOpcode(BPF::MOV_ri);
+ OutMI.addOperand(MCOperand::createReg(MI->getOperand(0).getReg()));
+ OutMI.addOperand(MCOperand::createImm(Imm));
+ return true;
+ } else if (GVar && !GVar->hasInitializer() &&
+ GVar->hasExternalLinkage() &&
+ GVar->getSection() == BPFCoreSharedInfo::PatchableExtSecName) {
+ const IntegerType *IntTy = dyn_cast<IntegerType>(GVar->getValueType());
+ assert(IntTy);
+ // For patchable externals, emit "LD_imm64, ri, 0" if the external
+ // variable is 64bit width, emit "mov ri, 0" otherwise.
+ if (IntTy->getBitWidth() == 64)
+ OutMI.setOpcode(BPF::LD_imm64);
+ else
+ OutMI.setOpcode(BPF::MOV_ri);
+ OutMI.addOperand(MCOperand::createReg(MI->getOperand(0).getReg()));
+ OutMI.addOperand(MCOperand::createImm(0));
+ return true;
+ }
+ }
+ }
+ return false;
+}
+void BTFDebug::endModule() {
+ // Collect MapDef globals if not collected yet.
+ if (MapDefNotCollected) {
+ processGlobals(true);
+ MapDefNotCollected = false;
+ }
+
+ // Collect global types/variables except MapDef globals.
+ processGlobals(false);
for (auto &DataSec : DataSecEntries)
addType(std::move(DataSec.second));
-}
-void BTFDebug::endModule() {
- // Collect all global types/variables.
- processGlobals();
+ // Fixups
+ for (auto &Fixup : FixupDerivedTypes) {
+ StringRef TypeName = Fixup.first;
+ bool IsUnion = Fixup.second.first;
+
+ // Search through struct types
+ uint32_t StructTypeId = 0;
+ for (const auto &StructType : StructTypes) {
+ if (StructType->getName() == TypeName) {
+ StructTypeId = StructType->getId();
+ break;
+ }
+ }
+
+ if (StructTypeId == 0) {
+ auto FwdTypeEntry = llvm::make_unique<BTFTypeFwd>(TypeName, IsUnion);
+ StructTypeId = addType(std::move(FwdTypeEntry));
+ }
+
+ for (auto &DType : Fixup.second.second) {
+ DType->setPointeeType(StructTypeId);
+ }
+ }
// Complete BTF type cross refereences.
for (const auto &TypeEntry : TypeEntries)
class BTFTypeBase {
protected:
uint8_t Kind;
+ bool IsCompleted;
uint32_t Id;
struct BTF::CommonType BTFType;
public:
+ BTFTypeBase() : IsCompleted(false) {}
virtual ~BTFTypeBase() = default;
void setId(uint32_t Id) { this->Id = Id; }
uint32_t getId() { return Id; }
/// volatile, typedef and restrict.
class BTFTypeDerived : public BTFTypeBase {
const DIDerivedType *DTy;
+ bool NeedsFixup;
public:
- BTFTypeDerived(const DIDerivedType *Ty, unsigned Tag);
+ BTFTypeDerived(const DIDerivedType *Ty, unsigned Tag, bool NeedsFixup);
void completeType(BTFDebug &BDebug);
void emitType(MCStreamer &OS);
+ void setPointeeType(uint32_t PointeeType);
};
/// Handle struct or union forward declaration.
/// Handle array type.
class BTFTypeArray : public BTFTypeBase {
+ uint32_t ElemSize;
struct BTF::BTFArray ArrayInfo;
public:
- BTFTypeArray(uint32_t ElemTypeId, uint32_t NumElems);
+ BTFTypeArray(uint32_t ElemTypeId, uint32_t ElemSize, uint32_t NumElems);
uint32_t getSize() { return BTFTypeBase::getSize() + BTF::BTFArraySize; }
void completeType(BTFDebug &BDebug);
void emitType(MCStreamer &OS);
+ void getLocInfo(uint32_t Loc, uint32_t &LocOffset, uint32_t &ElementTypeId);
};
/// Handle struct/union type.
}
void completeType(BTFDebug &BDebug);
void emitType(MCStreamer &OS);
+ std::string getName();
+ void getMemberInfo(uint32_t Loc, uint32_t &Offset, uint32_t &MemberType);
+ uint32_t getStructSize();
};
/// Handle function pointer.
uint32_t ColumnNum; ///< the column number
};
+/// Represent one offset relocation.
+struct BTFOffsetReloc {
+ const MCSymbol *Label; ///< MCSymbol identifying insn for the reloc
+ uint32_t TypeID; ///< Type ID
+ uint32_t OffsetNameOff; ///< The string to traverse types
+};
+
+/// Represent one extern relocation.
+struct BTFExternReloc {
+ const MCSymbol *Label; ///< MCSymbol identifying insn for the reloc
+ uint32_t ExternNameOff; ///< The extern variable name
+};
+
/// Collect and emit BTF information.
class BTFDebug : public DebugHandlerBase {
MCStreamer &OS;
bool LineInfoGenerated;
uint32_t SecNameOff;
uint32_t ArrayIndexTypeId;
+ bool MapDefNotCollected;
BTFStringTable StringTable;
std::vector<std::unique_ptr<BTFTypeBase>> TypeEntries;
std::unordered_map<const DIType *, uint32_t> DIToIdMap;
std::map<uint32_t, std::vector<BTFFuncInfo>> FuncInfoTable;
std::map<uint32_t, std::vector<BTFLineInfo>> LineInfoTable;
+ std::map<uint32_t, std::vector<BTFOffsetReloc>> OffsetRelocTable;
+ std::map<uint32_t, std::vector<BTFExternReloc>> ExternRelocTable;
StringMap<std::vector<std::string>> FileContent;
- std::map<std::string, std::unique_ptr<BTFKindDataSec>>
- DataSecEntries;
+ std::map<std::string, std::unique_ptr<BTFKindDataSec>> DataSecEntries;
+ std::vector<BTFTypeStruct *> StructTypes;
+ std::vector<BTFTypeArray *> ArrayTypes;
+ std::map<std::string, int64_t> AccessOffsets;
+ std::map<StringRef, std::pair<bool, std::vector<BTFTypeDerived *>>>
+ FixupDerivedTypes;
/// Add types to TypeEntries.
/// @{
/// IR type visiting functions.
/// @{
void visitTypeEntry(const DIType *Ty);
- void visitTypeEntry(const DIType *Ty, uint32_t &TypeId);
+ void visitTypeEntry(const DIType *Ty, uint32_t &TypeId, bool CheckPointer,
+ bool SeenPointer);
void visitBasicType(const DIBasicType *BTy, uint32_t &TypeId);
void visitSubroutineType(
const DISubroutineType *STy, bool ForSubprog,
uint32_t &TypeId);
void visitArrayType(const DICompositeType *ATy, uint32_t &TypeId);
void visitEnumType(const DICompositeType *ETy, uint32_t &TypeId);
- void visitDerivedType(const DIDerivedType *DTy, uint32_t &TypeId);
+ void visitDerivedType(const DIDerivedType *DTy, uint32_t &TypeId,
+ bool CheckPointer, bool SeenPointer);
+ void visitMapDefType(const DIType *Ty, uint32_t &TypeId);
/// @}
/// Get the file content for the subprogram. Certain lines of the file
uint32_t Column);
/// Generate types and variables for globals.
- void processGlobals(void);
+ void processGlobals(bool ProcessingMapDef);
+
+ /// Generate one offset relocation record.
+ void generateOffsetReloc(const MachineInstr *MI, const MCSymbol *ORSym,
+ DIType *RootTy, StringRef AccessPattern);
+
+ /// Set the to-be-traversed Struct/Array Type based on TypeId.
+ void setTypeFromId(uint32_t TypeId, BTFTypeStruct **PrevStructType,
+ BTFTypeArray **PrevArrayType);
+
+ /// Populating unprocessed struct type.
+ unsigned populateStructType(const DIType *Ty);
+
+ /// Process LD_imm64 instructions.
+ void processLDimm64(const MachineInstr *MI);
/// Emit common header of .BTF and .BTF.ext sections.
void emitCommonHeader();
public:
BTFDebug(AsmPrinter *AP);
+ ///
+ bool InstLower(const MachineInstr *MI, MCInst &OutMI);
+
/// Get the special array index type id.
uint32_t getArrayIndexTypeId() {
assert(ArrayIndexTypeId);
add_public_tablegen_target(BPFCommonTableGen)
add_llvm_target(BPFCodeGen
+ BPFAbstrctMemberAccess.cpp
BPFAsmPrinter.cpp
BPFFrameLowering.cpp
BPFInstrInfo.cpp
BPFTargetMachine.cpp
BPFMIPeephole.cpp
BPFMIChecking.cpp
+ BPFMISimplifyPatchable.cpp
BTFDebug.cpp
)
; CHECK: '.BTF'
; CHECK-EL: 0x00000000 9feb0100 18000000 00000000 30000000
-; CHECK-EL: 0x00000010 30000000 33000000 2b000000 00000001
+; CHECK-EL: 0x00000010 30000000 33000000 01000000 00000001
; CHECK-EL: 0x00000020 04000000 20000001 00000000 0100000d
-; CHECK-EL: 0x00000030 01000000 2f000000 01000000 31000000
-; CHECK-EL: 0x00000040 0000000c 02000000 002e7465 7874002f
+; CHECK-EL: 0x00000030 01000000 05000000 01000000 07000000
+; CHECK-EL: 0x00000040 0000000c 02000000 00696e74 00610066
; CHECK-EB: 0x00000000 eb9f0100 00000018 00000000 00000030
-; CHECK-EB: 0x00000010 00000030 00000033 0000002b 01000000
+; CHECK-EB: 0x00000010 00000030 00000033 00000001 01000000
; CHECK-EB: 0x00000020 00000004 01000020 00000000 0d000001
-; CHECK-EB: 0x00000030 00000001 0000002f 00000001 00000031
-; CHECK-EB: 0x00000040 0c000000 00000002 002e7465 7874002f
-; CHECK: 0x00000050 746d702f 742e6300 696e7420 6628696e
-; CHECK: 0x00000060 74206129 207b2072 65747572 6e20613b
-; CHECK: 0x00000070 207d0069 6e740061 006600
+; CHECK-EB: 0x00000030 00000001 00000005 00000001 00000007
+; CHECK-EB: 0x00000040 0c000000 00000002 00696e74 00610066
+; CHECK: 0x00000050 002e7465 7874002f 746d702f 742e6300
+; CHECK: 0x00000060 696e7420 6628696e 74206129 207b2072
+; CHECK: 0x00000070 65747572 6e20613b 207d00
; CHECK: '.BTF.ext'
-; CHECK-EL: 0x00000000 9feb0100 18000000 00000000 14000000
-; CHECK-EL: 0x00000010 14000000 2c000000 08000000 01000000
-; CHECK-EL: 0x00000020 01000000 00000000 03000000 10000000
-; CHECK-EL: 0x00000030 01000000 02000000 00000000 07000000
-; CHECK-EL: 0x00000040 10000000 00040000 08000000 07000000
-; CHECK-EL: 0x00000050 10000000 10040000
-; CHECK-EB: 0x00000000 eb9f0100 00000018 00000000 00000014
-; CHECK-EB: 0x00000010 00000014 0000002c 00000008 00000001
-; CHECK-EB: 0x00000020 00000001 00000000 00000003 00000010
-; CHECK-EB: 0x00000030 00000001 00000002 00000000 00000007
-; CHECK-EB: 0x00000040 00000010 00000400 00000008 00000007
-; CHECK-EB: 0x00000050 00000010 00000410
+; CHECK-EL: 0x00000000 9feb0100 28000000 00000000 14000000
+; CHECK-EL: 0x00000010 14000000 2c000000 40000000 00000000
+; CHECK-EL: 0x00000020 40000000 00000000 08000000 09000000
+; CHECK-EL: 0x00000030 01000000 00000000 03000000 10000000
+; CHECK-EL: 0x00000040 09000000 02000000 00000000 0f000000
+; CHECK-EL: 0x00000050 18000000 00040000 08000000 0f000000
+; CHECK-EL: 0x00000060 18000000 10040000
+; CHECK-EB: 0x00000000 eb9f0100 00000028 00000000 00000014
+; CHECK-EB: 0x00000010 00000014 0000002c 00000040 00000000
+; CHECK-EB: 0x00000020 00000040 00000000 00000008 00000009
+; CHECK-EB: 0x00000030 00000001 00000000 00000003 00000010
+; CHECK-EB: 0x00000040 00000009 00000002 00000000 0000000f
+; CHECK-EB: 0x00000050 00000018 00000400 00000008 0000000f
+; CHECK-EB: 0x00000060 00000018 00000410
; Function Attrs: nounwind readnone speculatable
declare void @llvm.dbg.value(metadata, metadata, metadata) #1
; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 1)
; CHECK-NEXT: .long 218103808 # 0xd000000
; CHECK-NEXT: .long 2
-; CHECK-NEXT: .long 44 # BTF_KIND_INT(id = 2)
+; CHECK-NEXT: .long 1 # BTF_KIND_INT(id = 2)
; CHECK-NEXT: .long 16777216 # 0x1000000
; CHECK-NEXT: .long 4
; CHECK-NEXT: .long 16777248 # 0x1000020
-; CHECK-NEXT: .long 48 # BTF_KIND_FUNC(id = 3)
+; CHECK-NEXT: .long 5 # BTF_KIND_FUNC(id = 3)
; CHECK-NEXT: .long 201326592 # 0xc000000
; CHECK-NEXT: .long 1
; CHECK-NEXT: .byte 0 # string offset=0
-; CHECK-NEXT: .ascii ".text" # string offset=1
+; CHECK-NEXT: .ascii "int" # string offset=1
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "/home/yhs/work/tests/llvm/bug/test.c" # string offset=7
+; CHECK-NEXT: .ascii "foo" # string offset=5
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "int" # string offset=44
+; CHECK-NEXT: .ascii ".text" # string offset=9
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "foo" # string offset=48
+; CHECK-NEXT: .ascii "/home/yhs/work/tests/llvm/bug/test.c" # string offset=15
; CHECK-NEXT: .byte 0
attributes #0 = { norecurse nounwind readonly "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "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" }
; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 1)
; CHECK-NEXT: .long 218103808 # 0xd000000
; CHECK-NEXT: .long 2
-; CHECK-NEXT: .long 26 # BTF_KIND_INT(id = 2)
+; CHECK-NEXT: .long 1 # BTF_KIND_INT(id = 2)
; CHECK-NEXT: .long 16777216 # 0x1000000
; CHECK-NEXT: .long 4
; CHECK-NEXT: .long 16777248 # 0x1000020
-; CHECK-NEXT: .long 30 # BTF_KIND_FUNC(id = 3)
+; CHECK-NEXT: .long 5 # BTF_KIND_FUNC(id = 3)
; CHECK-NEXT: .long 201326592 # 0xc000000
; CHECK-NEXT: .long 1
; CHECK-NEXT: .byte 0 # string offset=0
-; CHECK-NEXT: .ascii ".text" # string offset=1
+; CHECK-NEXT: .ascii "int" # string offset=1
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "/home/yhs/ttmp/t.c" # string offset=7
+; CHECK-NEXT: .ascii "test" # string offset=5
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "int" # string offset=26
+; CHECK-NEXT: .ascii ".text" # string offset=10
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "test" # string offset=30
+; CHECK-NEXT: .ascii "/home/yhs/ttmp/t.c" # string offset=16
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .section .BTF.ext,"",@progbits
; CHECK-NEXT: .short 60319 # 0xeb9f
; CHECK-NEXT: .byte 1
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .long 24
+; CHECK-NEXT: .long 40
; CHECK-NEXT: .long 0
; CHECK-NEXT: .long 20
; CHECK-NEXT: .long 20
; CHECK-NEXT: .long 28
+; CHECK-NEXT: .long 48
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 48
+; CHECK-NEXT: .long 0
; CHECK-NEXT: .long 8 # FuncInfo
-; CHECK-NEXT: .long 1 # FuncInfo section string offset=1
+; CHECK-NEXT: .long 10 # FuncInfo section string offset=10
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long .Lfunc_begin{{[0-9]+}}
; CHECK-NEXT: .long 3
; CHECK-NEXT: .long 16 # LineInfo
-; CHECK-NEXT: .long 1 # LineInfo section string offset=1
+; CHECK-NEXT: .long 10 # LineInfo section string offset=10
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
-; CHECK-NEXT: .long 7
+; CHECK-NEXT: .long 16
; CHECK-NEXT: .long 0
; CHECK-NEXT: .long 1038 # Line 1 Col 14
; CHECK-NEXT: .long 104
; CHECK-NEXT: .long 104
; CHECK-NEXT: .long 32
-; CHECK-NEXT: .long 16 # BTF_KIND_INT(id = 1)
+; CHECK-NEXT: .long 1 # BTF_KIND_INT(id = 1)
; CHECK-NEXT: .long 16777216 # 0x1000000
; CHECK-NEXT: .long 4
; CHECK-NEXT: .long 16777248 # 0x1000020
; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 2)
; CHECK-NEXT: .long 218103809 # 0xd000001
; CHECK-NEXT: .long 0
-; CHECK-NEXT: .long 20
+; CHECK-NEXT: .long 5
; CHECK-NEXT: .long 1
-; CHECK-NEXT: .long 23 # BTF_KIND_FUNC(id = 3)
+; CHECK-NEXT: .long 8 # BTF_KIND_FUNC(id = 3)
; CHECK-NEXT: .long 201326592 # 0xc000000
; CHECK-NEXT: .long 2
; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 4)
; CHECK-NEXT: .long 8
; CHECK-NEXT: .long 29
; CHECK-NEXT: .long 4
-; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 0 # 0x0
; CHECK-NEXT: .byte 0 # string offset=0
-; CHECK-NEXT: .ascii ".text" # string offset=1
+; CHECK-NEXT: .ascii "int" # string offset=1
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "/tmp/t.c" # string offset=7
+; CHECK-NEXT: .ascii "p2" # string offset=5
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "int" # string offset=16
+; CHECK-NEXT: .ascii "f1" # string offset=8
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "p2" # string offset=20
+; CHECK-NEXT: .ascii ".text" # string offset=11
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "f1" # string offset=23
+; CHECK-NEXT: .ascii "/tmp/t.c" # string offset=17
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .ascii "t1" # string offset=26
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .short 60319 # 0xeb9f
; CHECK-NEXT: .byte 1
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .long 24
+; CHECK-NEXT: .long 40
; CHECK-NEXT: .long 0
; CHECK-NEXT: .long 20
; CHECK-NEXT: .long 20
; CHECK-NEXT: .long 28
+; CHECK-NEXT: .long 48
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 48
+; CHECK-NEXT: .long 0
; CHECK-NEXT: .long 8 # FuncInfo
-; CHECK-NEXT: .long 1 # FuncInfo section string offset=1
+; CHECK-NEXT: .long 11 # FuncInfo section string offset=11
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long .Lfunc_begin0
; CHECK-NEXT: .long 3
; CHECK-NEXT: .long 16 # LineInfo
-; CHECK-NEXT: .long 1 # LineInfo section string offset=1
+; CHECK-NEXT: .long 11 # LineInfo section string offset=11
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
-; CHECK-NEXT: .long 7
+; CHECK-NEXT: .long 17
; CHECK-NEXT: .long 0
; CHECK-NEXT: .long 3091 # Line 3 Col 19
; CHECK-NEXT: .long 48
; CHECK-NEXT: .long 48
; CHECK-NEXT: .long 26
-; CHECK-NEXT: .long 16 # BTF_KIND_INT(id = 1)
+; CHECK-NEXT: .long 1 # BTF_KIND_INT(id = 1)
; CHECK-NEXT: .long 16777216 # 0x1000000
; CHECK-NEXT: .long 4
; CHECK-NEXT: .long 16777248 # 0x1000020
; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 2)
; CHECK-NEXT: .long 218103809 # 0xd000001
; CHECK-NEXT: .long 1
-; CHECK-NEXT: .long 20
+; CHECK-NEXT: .long 5
; CHECK-NEXT: .long 1
-; CHECK-NEXT: .long 23 # BTF_KIND_FUNC(id = 3)
+; CHECK-NEXT: .long 8 # BTF_KIND_FUNC(id = 3)
; CHECK-NEXT: .long 201326592 # 0xc000000
; CHECK-NEXT: .long 2
; CHECK-NEXT: .byte 0 # string offset=0
-; CHECK-NEXT: .ascii ".text" # string offset=1
+; CHECK-NEXT: .ascii "int" # string offset=1
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "/tmp/t.c" # string offset=7
+; CHECK-NEXT: .ascii "a1" # string offset=5
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "int" # string offset=16
+; CHECK-NEXT: .ascii "f1" # string offset=8
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "a1" # string offset=20
+; CHECK-NEXT: .ascii ".text" # string offset=11
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "f1" # string offset=23
+; CHECK-NEXT: .ascii "/tmp/t.c" # string offset=17
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .section .BTF.ext,"",@progbits
; CHECK-NEXT: .short 60319 # 0xeb9f
; CHECK-NEXT: .byte 1
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .long 24
+; CHECK-NEXT: .long 40
; CHECK-NEXT: .long 0
; CHECK-NEXT: .long 20
; CHECK-NEXT: .long 20
; CHECK-NEXT: .long 44
+; CHECK-NEXT: .long 64
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 64
+; CHECK-NEXT: .long 0
; CHECK-NEXT: .long 8 # FuncInfo
-; CHECK-NEXT: .long 1 # FuncInfo section string offset=1
+; CHECK-NEXT: .long 11 # FuncInfo section string offset=11
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long .Lfunc_begin0
; CHECK-NEXT: .long 3
; CHECK-NEXT: .long 16 # LineInfo
-; CHECK-NEXT: .long 1 # LineInfo section string offset=1
+; CHECK-NEXT: .long 11 # LineInfo section string offset=11
; CHECK-NEXT: .long 2
; CHECK-NEXT: .long .Lfunc_begin0
-; CHECK-NEXT: .long 7
+; CHECK-NEXT: .long 17
; CHECK-NEXT: .long 0
; CHECK-NEXT: .long 1024 # Line 1 Col 0
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
-; CHECK-NEXT: .long 7
+; CHECK-NEXT: .long 17
; CHECK-NEXT: .long 0
; CHECK-NEXT: .long 1042 # Line 1 Col 18
; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 1)
; CHECK-NEXT: .long 218103808 # 0xd000000
; CHECK-NEXT: .long 0
-; CHECK-NEXT: .long 33 # BTF_KIND_FUNC(id = 2)
+; CHECK-NEXT: .long 1 # BTF_KIND_FUNC(id = 2)
; CHECK-NEXT: .long 201326592 # 0xc000000
; CHECK-NEXT: .long 1
; CHECK-NEXT: .byte 0 # string offset=0
-; CHECK-NEXT: .ascii ".text" # string offset=1
+; CHECK-NEXT: .byte 102 # string offset=1
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "/tmp/t.c" # string offset=7
+; CHECK-NEXT: .ascii ".text" # string offset=3
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "void f(void) { }" # string offset=16
+; CHECK-NEXT: .ascii "/tmp/t.c" # string offset=9
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .byte 102 # string offset=33
+; CHECK-NEXT: .ascii "void f(void) { }" # string offset=18
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .section .BTF.ext,"",@progbits
; CHECK-NEXT: .short 60319 # 0xeb9f
; CHECK-NEXT: .byte 1
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .long 24
+; CHECK-NEXT: .long 40
; CHECK-NEXT: .long 0
; CHECK-NEXT: .long 20
; CHECK-NEXT: .long 20
; CHECK-NEXT: .long 28
+; CHECK-NEXT: .long 48
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 48
+; CHECK-NEXT: .long 0
; CHECK-NEXT: .long 8 # FuncInfo
-; CHECK-NEXT: .long 1 # FuncInfo section string offset=1
+; CHECK-NEXT: .long 3 # FuncInfo section string offset=3
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long .Lfunc_begin0
; CHECK-NEXT: .long 2
; CHECK-NEXT: .long 16 # LineInfo
-; CHECK-NEXT: .long 1 # LineInfo section string offset=1
+; CHECK-NEXT: .long 3 # LineInfo section string offset=3
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
-; CHECK-NEXT: .long 7
-; CHECK-NEXT: .long 16
+; CHECK-NEXT: .long 9
+; CHECK-NEXT: .long 18
; CHECK-NEXT: .long 1040 # Line 1 Col 16
attributes #0 = { norecurse nounwind readnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "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" }
; CHECK-NEXT: .long 72
; CHECK-NEXT: .long 72
; CHECK-NEXT: .long 35
-; CHECK-NEXT: .long 16 # BTF_KIND_TYPEDEF(id = 1)
+; CHECK-NEXT: .long 1 # BTF_KIND_TYPEDEF(id = 1)
; CHECK-NEXT: .long 134217728 # 0x8000000
; CHECK-NEXT: .long 2
-; CHECK-NEXT: .long 22 # BTF_KIND_TYPEDEF(id = 2)
+; CHECK-NEXT: .long 7 # BTF_KIND_TYPEDEF(id = 2)
; CHECK-NEXT: .long 134217728 # 0x8000000
; CHECK-NEXT: .long 3
-; CHECK-NEXT: .long 27 # BTF_KIND_INT(id = 3)
+; CHECK-NEXT: .long 12 # BTF_KIND_INT(id = 3)
; CHECK-NEXT: .long 16777216 # 0x1000000
; CHECK-NEXT: .long 4
; CHECK-NEXT: .long 16777248 # 0x1000020
; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 4)
; CHECK-NEXT: .long 218103809 # 0xd000001
; CHECK-NEXT: .long 1
-; CHECK-NEXT: .long 31
+; CHECK-NEXT: .long 16
; CHECK-NEXT: .long 1
-; CHECK-NEXT: .long 33 # BTF_KIND_FUNC(id = 5)
+; CHECK-NEXT: .long 18 # BTF_KIND_FUNC(id = 5)
; CHECK-NEXT: .long 201326592 # 0xc000000
; CHECK-NEXT: .long 4
; CHECK-NEXT: .byte 0 # string offset=0
-; CHECK-NEXT: .ascii ".text" # string offset=1
+; CHECK-NEXT: .ascii "__int" # string offset=1
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "/tmp/t.c" # string offset=7
+; CHECK-NEXT: .ascii "_int" # string offset=7
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "__int" # string offset=16
+; CHECK-NEXT: .ascii "int" # string offset=12
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "_int" # string offset=22
+; CHECK-NEXT: .byte 97 # string offset=16
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "int" # string offset=27
+; CHECK-NEXT: .byte 102 # string offset=18
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .byte 97 # string offset=31
+; CHECK-NEXT: .ascii ".text" # string offset=20
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .byte 102 # string offset=33
+; CHECK-NEXT: .ascii "/tmp/t.c" # string offset=26
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .section .BTF.ext,"",@progbits
; CHECK-NEXT: .short 60319 # 0xeb9f
; CHECK-NEXT: .byte 1
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .long 24
+; CHECK-NEXT: .long 40
; CHECK-NEXT: .long 0
; CHECK-NEXT: .long 20
; CHECK-NEXT: .long 20
; CHECK-NEXT: .long 44
+; CHECK-NEXT: .long 64
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 64
+; CHECK-NEXT: .long 0
; CHECK-NEXT: .long 8 # FuncInfo
-; CHECK-NEXT: .long 1 # FuncInfo section string offset=1
+; CHECK-NEXT: .long 20 # FuncInfo section string offset=20
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long .Lfunc_begin0
; CHECK-NEXT: .long 5
; CHECK-NEXT: .long 16 # LineInfo
-; CHECK-NEXT: .long 1 # LineInfo section string offset=1
+; CHECK-NEXT: .long 20 # LineInfo section string offset=20
; CHECK-NEXT: .long 2
; CHECK-NEXT: .long .Lfunc_begin0
-; CHECK-NEXT: .long 7
+; CHECK-NEXT: .long 26
; CHECK-NEXT: .long 0
; CHECK-NEXT: .long 3072 # Line 3 Col 0
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
-; CHECK-NEXT: .long 7
+; CHECK-NEXT: .long 26
; CHECK-NEXT: .long 0
; CHECK-NEXT: .long 3092 # Line 3 Col 20
+
; Function Attrs: nounwind readnone speculatable
declare void @llvm.dbg.value(metadata, metadata, metadata) #1
; CHECK-NEXT: .long 48
; CHECK-NEXT: .long 48
; CHECK-NEXT: .long 26
-; CHECK-NEXT: .long 16 # BTF_KIND_INT(id = 1)
+; CHECK-NEXT: .long 1 # BTF_KIND_INT(id = 1)
; CHECK-NEXT: .long 16777216 # 0x1000000
; CHECK-NEXT: .long 4
; CHECK-NEXT: .long 16777248 # 0x1000020
; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 2)
; CHECK-NEXT: .long 218103809 # 0xd000001
; CHECK-NEXT: .long 1
-; CHECK-NEXT: .long 20
+; CHECK-NEXT: .long 5
; CHECK-NEXT: .long 1
-; CHECK-NEXT: .long 23 # BTF_KIND_FUNC(id = 3)
+; CHECK-NEXT: .long 8 # BTF_KIND_FUNC(id = 3)
; CHECK-NEXT: .long 201326592 # 0xc000000
; CHECK-NEXT: .long 2
; CHECK-NEXT: .byte 0 # string offset=0
-; CHECK-NEXT: .ascii ".text" # string offset=1
+; CHECK-NEXT: .ascii "int" # string offset=1
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "/tmp/t.c" # string offset=7
+; CHECK-NEXT: .ascii "a1" # string offset=5
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "int" # string offset=16
+; CHECK-NEXT: .ascii "f1" # string offset=8
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "a1" # string offset=20
+; CHECK-NEXT: .ascii ".text" # string offset=11
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "f1" # string offset=23
+; CHECK-NEXT: .ascii "/tmp/t.c" # string offset=17
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .section .BTF.ext,"",@progbits
; CHECK-NEXT: .short 60319 # 0xeb9f
; CHECK-NEXT: .byte 1
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .long 24
+; CHECK-NEXT: .long 40
; CHECK-NEXT: .long 0
; CHECK-NEXT: .long 20
; CHECK-NEXT: .long 20
; CHECK-NEXT: .long 28
+; CHECK-NEXT: .long 48
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 48
+; CHECK-NEXT: .long 0
; CHECK-NEXT: .long 8 # FuncInfo
-; CHECK-NEXT: .long 1 # FuncInfo section string offset=1
+; CHECK-NEXT: .long 11 # FuncInfo section string offset=11
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long .Lfunc_begin0
; CHECK-NEXT: .long 3
; CHECK-NEXT: .long 16 # LineInfo
-; CHECK-NEXT: .long 1 # LineInfo section string offset=1
+; CHECK-NEXT: .long 11 # LineInfo section string offset=11
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
-; CHECK-NEXT: .long 7
+; CHECK-NEXT: .long 17
; CHECK-NEXT: .long 0
; CHECK-NEXT: .long 1042 # Line 1 Col 18
; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 1)
; CHECK-NEXT: .long 218103808 # 0xd000000
; CHECK-NEXT: .long 0
-; CHECK-NEXT: .long 16 # BTF_KIND_FUNC(id = 2)
+; CHECK-NEXT: .long 1 # BTF_KIND_FUNC(id = 2)
; CHECK-NEXT: .long 201326592 # 0xc000000
; CHECK-NEXT: .long 1
; CHECK-NEXT: .byte 0 # string offset=0
-; CHECK-NEXT: .ascii ".text" # string offset=1
+; CHECK-NEXT: .ascii "f1" # string offset=1
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "/tmp/t.c" # string offset=7
+; CHECK-NEXT: .ascii ".text" # string offset=4
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "f1" # string offset=16
+; CHECK-NEXT: .ascii "/tmp/t.c" # string offset=10
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .section .BTF.ext,"",@progbits
; CHECK-NEXT: .short 60319 # 0xeb9f
; CHECK-NEXT: .byte 1
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .long 24
+; CHECK-NEXT: .long 40
; CHECK-NEXT: .long 0
; CHECK-NEXT: .long 20
; CHECK-NEXT: .long 20
; CHECK-NEXT: .long 28
+; CHECK-NEXT: .long 48
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 48
+; CHECK-NEXT: .long 0
; CHECK-NEXT: .long 8 # FuncInfo
-; CHECK-NEXT: .long 1 # FuncInfo section string offset=1
+; CHECK-NEXT: .long 4 # FuncInfo section string offset=4
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long .Lfunc_begin0
; CHECK-NEXT: .long 2
; CHECK-NEXT: .long 16 # LineInfo
-; CHECK-NEXT: .long 1 # LineInfo section string offset=1
+; CHECK-NEXT: .long 4 # LineInfo section string offset=4
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
-; CHECK-NEXT: .long 7
+; CHECK-NEXT: .long 10
; CHECK-NEXT: .long 0
; CHECK-NEXT: .long 1040 # Line 1 Col 16
; CHECK-NEXT: .long 64
; CHECK-NEXT: .long 64
; CHECK-NEXT: .long 59
-; CHECK-NEXT: .long 44 # BTF_KIND_INT(id = 1)
+; CHECK-NEXT: .long 1 # BTF_KIND_INT(id = 1)
; CHECK-NEXT: .long 16777216 # 0x1000000
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long 16777224 # 0x1000008
; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 2)
; CHECK-NEXT: .long 218103809 # 0xd000001
; CHECK-NEXT: .long 3
-; CHECK-NEXT: .long 49
+; CHECK-NEXT: .long 6
; CHECK-NEXT: .long 1
-; CHECK-NEXT: .long 51 # BTF_KIND_INT(id = 3)
+; CHECK-NEXT: .long 8 # BTF_KIND_INT(id = 3)
; CHECK-NEXT: .long 16777216 # 0x1000000
; CHECK-NEXT: .long 4
; CHECK-NEXT: .long 16777248 # 0x1000020
-; CHECK-NEXT: .long 55 # BTF_KIND_FUNC(id = 4)
+; CHECK-NEXT: .long 12 # BTF_KIND_FUNC(id = 4)
; CHECK-NEXT: .long 201326592 # 0xc000000
; CHECK-NEXT: .long 2
; CHECK-NEXT: .byte 0 # string offset=0
-; CHECK-NEXT: .ascii ".text" # string offset=1
+; CHECK-NEXT: .ascii "char" # string offset=1
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "/home/yhs/work/tests/llvm/bug/test.c" # string offset=7
+; CHECK-NEXT: .byte 97 # string offset=6
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "char" # string offset=44
+; CHECK-NEXT: .ascii "int" # string offset=8
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .byte 97 # string offset=49
+; CHECK-NEXT: .ascii "foo" # string offset=12
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "int" # string offset=51
+; CHECK-NEXT: .ascii ".text" # string offset=16
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "foo" # string offset=55
+; CHECK-NEXT: .ascii "/home/yhs/work/tests/llvm/bug/test.c" # string offset=22
; CHECK-NEXT: .byte 0
; Function Attrs: nounwind readnone speculatable
--- /dev/null
+; 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
+
+; Source code:
+; struct key_type {
+; int a;
+; int b;
+; };
+; struct map_type {
+; struct key_type *key;
+; unsigned *value;
+; };
+; struct map_type __attribute__((section(".maps"))) hash_map;
+; Compilation flag:
+; clang -target bpf -O2 -g -S -emit-llvm t.c
+
+%struct.map_type = type { %struct.key_type*, i32* }
+%struct.key_type = type { i32, i32 }
+
+@hash_map = dso_local local_unnamed_addr global %struct.map_type zeroinitializer, section ".maps", align 8, !dbg !0
+
+; CHECK: .section .BTF,"",@progbits
+; CHECK-NEXT: .short 60319 # 0xeb9f
+; CHECK-NEXT: .byte 1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .long 24
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 168
+; CHECK-NEXT: .long 168
+; CHECK-NEXT: .long 65
+; CHECK-NEXT: .long 1 # BTF_KIND_STRUCT(id = 1)
+; CHECK-NEXT: .long 67108866 # 0x4000002
+; CHECK-NEXT: .long 16
+; CHECK-NEXT: .long 10
+; CHECK-NEXT: .long 2
+; CHECK-NEXT: .long 0 # 0x0
+; CHECK-NEXT: .long 14
+; CHECK-NEXT: .long 5
+; CHECK-NEXT: .long 64 # 0x40
+; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 2)
+; CHECK-NEXT: .long 33554432 # 0x2000000
+; CHECK-NEXT: .long 3
+; CHECK-NEXT: .long 20 # BTF_KIND_STRUCT(id = 3)
+; CHECK-NEXT: .long 67108866 # 0x4000002
+; CHECK-NEXT: .long 8
+; CHECK-NEXT: .long 29
+; CHECK-NEXT: .long 4
+; CHECK-NEXT: .long 0 # 0x0
+; CHECK-NEXT: .long 31
+; CHECK-NEXT: .long 4
+; CHECK-NEXT: .long 32 # 0x20
+; CHECK-NEXT: .long 33 # BTF_KIND_INT(id = 4)
+; CHECK-NEXT: .long 16777216 # 0x1000000
+; CHECK-NEXT: .long 4
+; CHECK-NEXT: .long 16777248 # 0x1000020
+; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 5)
+; CHECK-NEXT: .long 33554432 # 0x2000000
+; CHECK-NEXT: .long 6
+; CHECK-NEXT: .long 37 # BTF_KIND_INT(id = 6)
+; CHECK-NEXT: .long 16777216 # 0x1000000
+; CHECK-NEXT: .long 4
+; CHECK-NEXT: .long 32 # 0x20
+; CHECK-NEXT: .long 50 # BTF_KIND_VAR(id = 7)
+; CHECK-NEXT: .long 234881024 # 0xe000000
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long 59 # BTF_KIND_DATASEC(id = 8)
+; CHECK-NEXT: .long 251658241 # 0xf000001
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 7
+; CHECK-NEXT: .long hash_map
+; CHECK-NEXT: .long 16
+; CHECK-NEXT: .byte 0 # string offset=0
+; CHECK-NEXT: .ascii "map_type" # string offset=1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "key" # string offset=10
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "value" # string offset=14
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "key_type" # string offset=20
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .byte 97 # string offset=29
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .byte 98 # string offset=31
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "int" # string offset=33
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "unsigned int" # string offset=37
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "hash_map" # string offset=50
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii ".maps" # string offset=59
+; CHECK-NEXT: .byte 0
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!18, !19, !20}
+!llvm.ident = !{!21}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "hash_map", scope: !2, file: !3, line: 9, type: !6, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 9.0.0 (trunk 364157) (llvm/trunk 364156)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, nameTableKind: None)
+!3 = !DIFile(filename: "t.c", directory: "/tmp/home/yhs/work/tests/llvm")
+!4 = !{}
+!5 = !{!0}
+!6 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "map_type", file: !3, line: 5, size: 128, elements: !7)
+!7 = !{!8, !15}
+!8 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !6, file: !3, line: 6, baseType: !9, size: 64)
+!9 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !10, size: 64)
+!10 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "key_type", file: !3, line: 1, size: 64, elements: !11)
+!11 = !{!12, !14}
+!12 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !10, file: !3, line: 2, baseType: !13, size: 32)
+!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!14 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !10, file: !3, line: 3, baseType: !13, size: 32, offset: 32)
+!15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !6, file: !3, line: 7, baseType: !16, size: 64, offset: 64)
+!16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64)
+!17 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+!18 = !{i32 2, !"Dwarf Version", i32 4}
+!19 = !{i32 2, !"Debug Info Version", i32 3}
+!20 = !{i32 1, !"wchar_size", i32 4}
+!21 = !{!"clang version 9.0.0 (trunk 364157) (llvm/trunk 364156)"}
--- /dev/null
+; 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
+
+; Source code:
+; struct t {
+; int a;
+; };
+; struct t2 {
+; struct t *f1;
+; };
+; struct t2 __attribute__((section("prune_types"))) g;
+; Compilation flag:
+; clang -target bpf -O2 -g -S -emit-llvm t.c
+
+%struct.t2 = type { %struct.t* }
+%struct.t = type { i32 }
+
+@g = dso_local local_unnamed_addr global %struct.t2 zeroinitializer, section "prune_types", align 8, !dbg !0
+
+; CHECK: .section .BTF,"",@progbits
+; CHECK-NEXT: .short 60319 # 0xeb9f
+; CHECK-NEXT: .byte 1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .long 24
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 88
+; CHECK-NEXT: .long 88
+; CHECK-NEXT: .long 23
+; CHECK-NEXT: .long 1 # BTF_KIND_STRUCT(id = 1)
+; CHECK-NEXT: .long 67108865 # 0x4000001
+; CHECK-NEXT: .long 8
+; CHECK-NEXT: .long 4
+; CHECK-NEXT: .long 2
+; CHECK-NEXT: .long 0 # 0x0
+; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 2)
+; CHECK-NEXT: .long 33554432 # 0x2000000
+; CHECK-NEXT: .long 5
+; CHECK-NEXT: .long 7 # BTF_KIND_VAR(id = 3)
+; CHECK-NEXT: .long 234881024 # 0xe000000
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long 9 # BTF_KIND_DATASEC(id = 4)
+; CHECK-NEXT: .long 251658241 # 0xf000001
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 3
+; CHECK-NEXT: .long g
+; CHECK-NEXT: .long 8
+; CHECK-NEXT: .long 21 # BTF_KIND_FWD(id = 5)
+; CHECK-NEXT: .long 117440512 # 0x7000000
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .byte 0 # string offset=0
+; CHECK-NEXT: .ascii "t2" # string offset=1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "f1" # string offset=4
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .byte 103 # string offset=7
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "prune_types" # string offset=9
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .byte 116 # string offset=21
+; CHECK-NEXT: .byte 0
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!14, !15, !16}
+!llvm.ident = !{!17}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "g", scope: !2, file: !3, line: 7, type: !6, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 9.0.0 (trunk 364157) (llvm/trunk 364156)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, nameTableKind: None)
+!3 = !DIFile(filename: "t.c", directory: "/tmp/home/yhs/work/tests/llvm")
+!4 = !{}
+!5 = !{!0}
+!6 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t2", file: !3, line: 4, size: 64, elements: !7)
+!7 = !{!8}
+!8 = !DIDerivedType(tag: DW_TAG_member, name: "f1", scope: !6, file: !3, line: 5, baseType: !9, size: 64)
+!9 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !10, size: 64)
+!10 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t", file: !3, line: 1, size: 32, elements: !11)
+!11 = !{!12}
+!12 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !10, file: !3, line: 2, baseType: !13, size: 32)
+!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!14 = !{i32 2, !"Dwarf Version", i32 4}
+!15 = !{i32 2, !"Debug Info Version", i32 3}
+!16 = !{i32 1, !"wchar_size", i32 4}
+!17 = !{!"clang version 9.0.0 (trunk 364157) (llvm/trunk 364156)"}
; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 1)
; CHECK-NEXT: .long 218103808 # 0xd000000
; CHECK-NEXT: .long 2
-; CHECK-NEXT: .long 45 # BTF_KIND_INT(id = 2)
+; CHECK-NEXT: .long 1 # BTF_KIND_INT(id = 2)
; CHECK-NEXT: .long 16777216 # 0x1000000
; CHECK-NEXT: .long 8
; CHECK-NEXT: .long 16777280 # 0x1000040
-; CHECK-NEXT: .long 54 # BTF_KIND_FUNC(id = 3)
+; CHECK-NEXT: .long 10 # BTF_KIND_FUNC(id = 3)
; CHECK-NEXT: .long 201326592 # 0xc000000
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long 0 # BTF_KIND_VOLATILE(id = 4)
; CHECK-NEXT: .long v4
; CHECK-NEXT: .long 8
; CHECK-NEXT: .byte 0 # string offset=0
-; CHECK-NEXT: .ascii ".text" # string offset=1
+; CHECK-NEXT: .ascii "long int" # string offset=1
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "/home/yhs/work/tests/llvm/bugs/test.c" # string offset=7
+; CHECK-NEXT: .ascii "foo" # string offset=10
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "long int" # string offset=45
+; CHECK-NEXT: .ascii ".text" # string offset=14
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "foo" # string offset=54
+; CHECK-NEXT: .ascii "/home/yhs/work/tests/llvm/bugs/test.c" # string offset=20
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .ascii "int" # string offset=58
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 1)
; CHECK-NEXT: .long 218103808 # 0xd000000
; CHECK-NEXT: .long 2
-; CHECK-NEXT: .long 44 # BTF_KIND_INT(id = 2)
+; CHECK-NEXT: .long 1 # BTF_KIND_INT(id = 2)
; CHECK-NEXT: .long 16777216 # 0x1000000
; CHECK-NEXT: .long 4
; CHECK-NEXT: .long 16777248 # 0x1000020
-; CHECK-NEXT: .long 48 # BTF_KIND_FUNC(id = 3)
+; CHECK-NEXT: .long 5 # BTF_KIND_FUNC(id = 3)
; CHECK-NEXT: .long 201326592 # 0xc000000
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long 0 # BTF_KIND_VOLATILE(id = 4)
; CHECK-NEXT: .long a
; CHECK-NEXT: .long 1
; CHECK-NEXT: .byte 0 # string offset=0
-; CHECK-NEXT: .ascii ".text" # string offset=1
+; CHECK-NEXT: .ascii "int" # string offset=1
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "/home/yhs/work/tests/llvm/bug/test.c" # string offset=7
+; CHECK-NEXT: .ascii "foo" # string offset=5
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "int" # string offset=44
+; CHECK-NEXT: .ascii ".text" # string offset=9
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "foo" # string offset=48
+; CHECK-NEXT: .ascii "/home/yhs/work/tests/llvm/bug/test.c" # string offset=15
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .ascii "short" # string offset=52
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 1)
; CHECK-NEXT: .long 218103808 # 0xd000000
; CHECK-NEXT: .long 2
-; CHECK-NEXT: .long 44 # BTF_KIND_INT(id = 2)
+; CHECK-NEXT: .long 1 # BTF_KIND_INT(id = 2)
; CHECK-NEXT: .long 16777216 # 0x1000000
; CHECK-NEXT: .long 4
; CHECK-NEXT: .long 16777248 # 0x1000020
-; CHECK-NEXT: .long 48 # BTF_KIND_FUNC(id = 3)
+; CHECK-NEXT: .long 5 # BTF_KIND_FUNC(id = 3)
; CHECK-NEXT: .long 201326592 # 0xc000000
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long 0 # BTF_KIND_VOLATILE(id = 4)
; CHECK-NEXT: .long a
; CHECK-NEXT: .long 1
; CHECK-NEXT: .byte 0 # string offset=0
-; CHECK-NEXT: .ascii ".text" # string offset=1
+; CHECK-NEXT: .ascii "int" # string offset=1
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "/home/yhs/work/tests/llvm/bug/test.c" # string offset=7
+; CHECK-NEXT: .ascii "foo" # string offset=5
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "int" # string offset=44
+; CHECK-NEXT: .ascii ".text" # string offset=9
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "foo" # string offset=48
+; CHECK-NEXT: .ascii "/home/yhs/work/tests/llvm/bug/test.c" # string offset=15
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .ascii "short" # string offset=52
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 1)
; CHECK-NEXT: .long 218103808 # 0xd000000
; CHECK-NEXT: .long 2
-; CHECK-NEXT: .long 44 # BTF_KIND_INT(id = 2)
+; CHECK-NEXT: .long 1 # BTF_KIND_INT(id = 2)
; CHECK-NEXT: .long 16777216 # 0x1000000
; CHECK-NEXT: .long 4
; CHECK-NEXT: .long 16777248 # 0x1000020
-; CHECK-NEXT: .long 48 # BTF_KIND_FUNC(id = 3)
+; CHECK-NEXT: .long 5 # BTF_KIND_FUNC(id = 3)
; CHECK-NEXT: .long 201326592 # 0xc000000
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long 0 # BTF_KIND_CONST(id = 4)
; CHECK-NEXT: .long a
; CHECK-NEXT: .long 1
; CHECK-NEXT: .byte 0 # string offset=0
-; CHECK-NEXT: .ascii ".text" # string offset=1
+; CHECK-NEXT: .ascii "int" # string offset=1
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "/home/yhs/work/tests/llvm/bug/test.c" # string offset=7
+; CHECK-NEXT: .ascii "foo" # string offset=5
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "int" # string offset=44
+; CHECK-NEXT: .ascii ".text" # string offset=9
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "foo" # string offset=48
+; CHECK-NEXT: .ascii "/home/yhs/work/tests/llvm/bug/test.c" # string offset=15
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .ascii "short" # string offset=52
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 1)
; CHECK-NEXT: .long 218103808 # 0xd000000
; CHECK-NEXT: .long 2
-; CHECK-NEXT: .long 44 # BTF_KIND_INT(id = 2)
+; CHECK-NEXT: .long 1 # BTF_KIND_INT(id = 2)
; CHECK-NEXT: .long 16777216 # 0x1000000
; CHECK-NEXT: .long 4
; CHECK-NEXT: .long 16777248 # 0x1000020
-; CHECK-NEXT: .long 48 # BTF_KIND_FUNC(id = 3)
+; CHECK-NEXT: .long 5 # BTF_KIND_FUNC(id = 3)
; CHECK-NEXT: .long 201326592 # 0xc000000
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long 0 # BTF_KIND_CONST(id = 4)
; CHECK-NEXT: .long a
; CHECK-NEXT: .long 1
; CHECK-NEXT: .byte 0 # string offset=0
-; CHECK-NEXT: .ascii ".text" # string offset=1
+; CHECK-NEXT: .ascii "int" # string offset=1
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "/home/yhs/work/tests/llvm/bug/test.c" # string offset=7
+; CHECK-NEXT: .ascii "foo" # string offset=5
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "int" # string offset=44
+; CHECK-NEXT: .ascii ".text" # string offset=9
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "foo" # string offset=48
+; CHECK-NEXT: .ascii "/home/yhs/work/tests/llvm/bug/test.c" # string offset=15
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .ascii "short" # string offset=52
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 1)
; CHECK-NEXT: .long 218103808 # 0xd000000
; CHECK-NEXT: .long 2
-; CHECK-NEXT: .long 44 # BTF_KIND_INT(id = 2)
+; CHECK-NEXT: .long 1 # BTF_KIND_INT(id = 2)
; CHECK-NEXT: .long 16777216 # 0x1000000
; CHECK-NEXT: .long 4
; CHECK-NEXT: .long 16777248 # 0x1000020
-; CHECK-NEXT: .long 48 # BTF_KIND_FUNC(id = 3)
+; CHECK-NEXT: .long 5 # BTF_KIND_FUNC(id = 3)
; CHECK-NEXT: .long 201326592 # 0xc000000
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long 0 # BTF_KIND_VOLATILE(id = 4)
; CHECK-NEXT: .long a
; CHECK-NEXT: .long 1
; CHECK-NEXT: .byte 0 # string offset=0
-; CHECK-NEXT: .ascii ".text" # string offset=1
+; CHECK-NEXT: .ascii "int" # string offset=1
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "/home/yhs/work/tests/llvm/bug/test.c" # string offset=7
+; CHECK-NEXT: .ascii "foo" # string offset=5
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "int" # string offset=44
+; CHECK-NEXT: .ascii ".text" # string offset=9
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "foo" # string offset=48
+; CHECK-NEXT: .ascii "/home/yhs/work/tests/llvm/bug/test.c" # string offset=15
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .ascii "short" # string offset=52
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 1)
; CHECK-NEXT: .long 218103808 # 0xd000000
; CHECK-NEXT: .long 2
-; CHECK-NEXT: .long 44 # BTF_KIND_INT(id = 2)
+; CHECK-NEXT: .long 1 # BTF_KIND_INT(id = 2)
; CHECK-NEXT: .long 16777216 # 0x1000000
; CHECK-NEXT: .long 4
; CHECK-NEXT: .long 16777248 # 0x1000020
-; CHECK-NEXT: .long 48 # BTF_KIND_FUNC(id = 3)
+; CHECK-NEXT: .long 5 # BTF_KIND_FUNC(id = 3)
; CHECK-NEXT: .long 201326592 # 0xc000000
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long 0 # BTF_KIND_VOLATILE(id = 4)
; CHECK-NEXT: .long sv
; CHECK-NEXT: .long 20
; CHECK-NEXT: .byte 0 # string offset=0
-; CHECK-NEXT: .ascii ".text" # string offset=1
+; CHECK-NEXT: .ascii "int" # string offset=1
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "/home/yhs/work/tests/llvm/bug/test.c" # string offset=7
+; CHECK-NEXT: .ascii "test" # string offset=5
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "int" # string offset=44
+; CHECK-NEXT: .ascii ".text" # string offset=10
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "test" # string offset=48
+; CHECK-NEXT: .ascii "/home/yhs/work/tests/llvm/bug/test.c" # string offset=16
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .byte 116 # string offset=53
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 1)
; CHECK-NEXT: .long 218103808 # 0xd000000
; CHECK-NEXT: .long 2
-; CHECK-NEXT: .long 44 # BTF_KIND_INT(id = 2)
+; CHECK-NEXT: .long 1 # BTF_KIND_INT(id = 2)
; CHECK-NEXT: .long 16777216 # 0x1000000
; CHECK-NEXT: .long 4
; CHECK-NEXT: .long 16777248 # 0x1000020
-; CHECK-NEXT: .long 48 # BTF_KIND_FUNC(id = 3)
+; CHECK-NEXT: .long 5 # BTF_KIND_FUNC(id = 3)
; CHECK-NEXT: .long 201326592 # 0xc000000
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long 0 # BTF_KIND_VOLATILE(id = 4)
; CHECK-NEXT: .long a
; CHECK-NEXT: .long 1
; CHECK-NEXT: .byte 0 # string offset=0
-; CHECK-NEXT: .ascii ".text" # string offset=1
+; CHECK-NEXT: .ascii "int" # string offset=1
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "/home/yhs/work/tests/llvm/bug/test.c" # string offset=7
+; CHECK-NEXT: .ascii "foo" # string offset=5
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "int" # string offset=44
+; CHECK-NEXT: .ascii ".text" # string offset=9
; CHECK-NEXT: .byte 0
-; CHECK-NEXT: .ascii "foo" # string offset=48
+; CHECK-NEXT: .ascii "/home/yhs/work/tests/llvm/bug/test.c" # string offset=15
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .ascii "short" # string offset=52
; CHECK-NEXT: .byte 0
--- /dev/null
+; 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
+; Source code:
+; struct sk_buff {
+; int i;
+; struct net_device *dev;
+; };
+; #define _(x) (__builtin_preserve_access_index(x))
+; static int (*bpf_probe_read)(void *dst, int size, void *unsafe_ptr)
+; = (void *) 4;
+;
+; int bpf_prog(struct sk_buff *ctx) {
+; struct net_device *dev = 0;
+; bpf_probe_read(&dev, sizeof(dev), _(&ctx->dev));
+; return dev != 0;
+; }
+; Compilation flag:
+; clang -target bpf -O2 -g -S -emit-llvm test.c
+
+%struct.sk_buff = type { i32, %struct.net_device* }
+%struct.net_device = type opaque
+
+; Function Attrs: nounwind
+define dso_local i32 @bpf_prog(%struct.sk_buff*) local_unnamed_addr #0 !dbg !15 {
+ %2 = alloca %struct.net_device*, align 8
+ call void @llvm.dbg.value(metadata %struct.sk_buff* %0, metadata !26, metadata !DIExpression()), !dbg !28
+ %3 = bitcast %struct.net_device** %2 to i8*, !dbg !29
+ call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %3) #4, !dbg !29
+ call void @llvm.dbg.value(metadata %struct.net_device* null, metadata !27, metadata !DIExpression()), !dbg !28
+ store %struct.net_device* null, %struct.net_device** %2, align 8, !dbg !30, !tbaa !31
+ %4 = tail call %struct.net_device** @llvm.preserve.struct.access.index.p0p0s_struct.net_devices.p0s_struct.sk_buffs(%struct.sk_buff* %0, i32 1, i32 1), !dbg !35, !llvm.preserve.access.index !19
+ %5 = bitcast %struct.net_device** %4 to i8*, !dbg !35
+ %6 = call i32 inttoptr (i64 4 to i32 (i8*, i32, i8*)*)(i8* nonnull %3, i32 8, i8* %5) #4, !dbg !36
+ %7 = load %struct.net_device*, %struct.net_device** %2, align 8, !dbg !37, !tbaa !31
+ call void @llvm.dbg.value(metadata %struct.net_device* %7, metadata !27, metadata !DIExpression()), !dbg !28
+ %8 = icmp ne %struct.net_device* %7, null, !dbg !38
+ %9 = zext i1 %8 to i32, !dbg !38
+ call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %3) #4, !dbg !39
+ ret i32 %9, !dbg !40
+}
+
+; CHECK: .section .BTF,"",@progbits
+; CHECK-NEXT: .short 60319 # 0xeb9f
+; CHECK-NEXT: .byte 1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .long 24
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 120
+; CHECK-NEXT: .long 120
+; CHECK-NEXT: .long 90
+; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 1)
+; CHECK-NEXT: .long 33554432 # 0x2000000
+; CHECK-NEXT: .long 2
+; CHECK-NEXT: .long 1 # BTF_KIND_STRUCT(id = 2)
+; CHECK-NEXT: .long 67108866 # 0x4000002
+; CHECK-NEXT: .long 16
+; CHECK-NEXT: .long 9
+; CHECK-NEXT: .long 3
+; CHECK-NEXT: .long 0 # 0x0
+; CHECK-NEXT: .long 11
+; CHECK-NEXT: .long 4
+; CHECK-NEXT: .long 64 # 0x40
+; CHECK-NEXT: .long 15 # BTF_KIND_INT(id = 3)
+; CHECK-NEXT: .long 16777216 # 0x1000000
+; CHECK-NEXT: .long 4
+; CHECK-NEXT: .long 16777248 # 0x1000020
+; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 4)
+; CHECK-NEXT: .long 33554432 # 0x2000000
+; CHECK-NEXT: .long 5
+; CHECK-NEXT: .long 19 # BTF_KIND_FWD(id = 5)
+; CHECK-NEXT: .long 117440512 # 0x7000000
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 6)
+; CHECK-NEXT: .long 218103809 # 0xd000001
+; CHECK-NEXT: .long 3
+; CHECK-NEXT: .long 30
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long 34 # BTF_KIND_FUNC(id = 7)
+; CHECK-NEXT: .long 201326592 # 0xc000000
+; CHECK-NEXT: .long 6
+; CHECK-NEXT: .byte 0 # string offset=0
+; CHECK-NEXT: .ascii "sk_buff" # string offset=1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .byte 105 # string offset=9
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "dev" # string offset=11
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "int" # string offset=15
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "net_device" # string offset=19
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "ctx" # string offset=30
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "bpf_prog" # string offset=34
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii ".text" # string offset=43
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "/tmp/home/yhs/work/tests/llvm/test.c" # string offset=49
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "0:1" # string offset=86
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .section .BTF.ext,"",@progbits
+; CHECK-NEXT: .short 60319 # 0xeb9f
+; CHECK-NEXT: .byte 1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .long 40
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 20
+; CHECK-NEXT: .long 20
+; CHECK-NEXT: .long 124
+; CHECK-NEXT: .long 144
+; CHECK-NEXT: .long 24
+; CHECK-NEXT: .long 168
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 8 # FuncInfo
+
+; CHECK: .long 12 # OffsetReloc
+; CHECK-NEXT: .long 43 # Offset reloc section string offset=43
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long .Ltmp2
+; CHECK-NEXT: .long 2
+; CHECK-NEXT: .long 86
+
+; Function Attrs: argmemonly nounwind
+declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
+
+; Function Attrs: nounwind readnone
+declare %struct.net_device** @llvm.preserve.struct.access.index.p0p0s_struct.net_devices.p0s_struct.sk_buffs(%struct.sk_buff*, i32 immarg, i32 immarg) #2
+
+; Function Attrs: argmemonly nounwind
+declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
+
+; Function Attrs: nounwind readnone speculatable
+declare void @llvm.dbg.value(metadata, metadata, metadata) #3
+
+attributes #0 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "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 = { argmemonly nounwind }
+attributes #2 = { nounwind readnone }
+attributes #3 = { nounwind readnone speculatable }
+attributes #4 = { nounwind }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!11, !12, !13}
+!llvm.ident = !{!14}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 9.0.0 (trunk 360739) (llvm/trunk 360747)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, globals: !3, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/llvm")
+!2 = !{}
+!3 = !{!4}
+!4 = !DIGlobalVariableExpression(var: !5, expr: !DIExpression())
+!5 = distinct !DIGlobalVariable(name: "bpf_probe_read", scope: !0, file: !1, line: 6, type: !6, isLocal: true, isDefinition: true)
+!6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64)
+!7 = !DISubroutineType(types: !8)
+!8 = !{!9, !10, !9, !10}
+!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!10 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)
+!11 = !{i32 2, !"Dwarf Version", i32 4}
+!12 = !{i32 2, !"Debug Info Version", i32 3}
+!13 = !{i32 1, !"wchar_size", i32 4}
+!14 = !{!"clang version 9.0.0 (trunk 360739) (llvm/trunk 360747)"}
+!15 = distinct !DISubprogram(name: "bpf_prog", scope: !1, file: !1, line: 9, type: !16, scopeLine: 9, flags: DIFlagPrototyped, isLocal: false, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !25)
+!16 = !DISubroutineType(types: !17)
+!17 = !{!9, !18}
+!18 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !19, size: 64)
+!19 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "sk_buff", file: !1, line: 1, size: 128, elements: !20)
+!20 = !{!21, !22}
+!21 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !19, file: !1, line: 2, baseType: !9, size: 32)
+!22 = !DIDerivedType(tag: DW_TAG_member, name: "dev", scope: !19, file: !1, line: 3, baseType: !23, size: 64, offset: 64)
+!23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !24, size: 64)
+!24 = !DICompositeType(tag: DW_TAG_structure_type, name: "net_device", file: !1, line: 3, flags: DIFlagFwdDecl)
+!25 = !{!26, !27}
+!26 = !DILocalVariable(name: "ctx", arg: 1, scope: !15, file: !1, line: 9, type: !18)
+!27 = !DILocalVariable(name: "dev", scope: !15, file: !1, line: 10, type: !23)
+!28 = !DILocation(line: 0, scope: !15)
+!29 = !DILocation(line: 10, column: 3, scope: !15)
+!30 = !DILocation(line: 10, column: 22, scope: !15)
+!31 = !{!32, !32, i64 0}
+!32 = !{!"any pointer", !33, i64 0}
+!33 = !{!"omnipotent char", !34, i64 0}
+!34 = !{!"Simple C/C++ TBAA"}
+!35 = !DILocation(line: 11, column: 37, scope: !15)
+!36 = !DILocation(line: 11, column: 3, scope: !15)
+!37 = !DILocation(line: 12, column: 10, scope: !15)
+!38 = !DILocation(line: 12, column: 14, scope: !15)
+!39 = !DILocation(line: 13, column: 1, scope: !15)
+!40 = !DILocation(line: 12, column: 3, scope: !15)
--- /dev/null
+; 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
+; Source code:
+; struct net_device {
+; int dev_id;
+; int others;
+; };
+; struct sk_buff {
+; int i;
+; struct net_device dev;
+; };
+; #define _(x) (__builtin_preserve_access_index(x))
+; static int (*bpf_probe_read)(void *dst, int size, void *unsafe_ptr)
+; = (void *) 4;
+;
+; int bpf_prog(struct sk_buff *ctx) {
+; int dev_id;
+; bpf_probe_read(&dev_id, sizeof(int), _(&ctx->dev.dev_id));
+; return dev_id;
+; }
+; Compilation flag:
+; clang -target bpf -O2 -g -S -emit-llvm test.c
+
+%struct.sk_buff = type { i32, %struct.net_device }
+%struct.net_device = type { i32, i32 }
+
+; Function Attrs: nounwind
+define dso_local i32 @bpf_prog(%struct.sk_buff*) local_unnamed_addr #0 !dbg !15 {
+ %2 = alloca i32, align 4
+ call void @llvm.dbg.value(metadata %struct.sk_buff* %0, metadata !28, metadata !DIExpression()), !dbg !30
+ %3 = bitcast i32* %2 to i8*, !dbg !31
+ call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %3) #4, !dbg !31
+ %4 = tail call %struct.net_device* @llvm.preserve.struct.access.index.p0s_struct.net_devices.p0s_struct.sk_buffs(%struct.sk_buff* %0, i32 1, i32 1), !dbg !32, !llvm.preserve.access.index !19
+ %5 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.net_devices(%struct.net_device* %4, i32 0, i32 0), !dbg !32, !llvm.preserve.access.index !23
+ %6 = bitcast i32* %5 to i8*, !dbg !32
+ %7 = call i32 inttoptr (i64 4 to i32 (i8*, i32, i8*)*)(i8* nonnull %3, i32 4, i8* %6) #4, !dbg !33
+ %8 = load i32, i32* %2, align 4, !dbg !34, !tbaa !35
+ call void @llvm.dbg.value(metadata i32 %8, metadata !29, metadata !DIExpression()), !dbg !30
+ call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %3) #4, !dbg !39
+ ret i32 %8, !dbg !40
+}
+
+; CHECK: .section .BTF,"",@progbits
+; CHECK-NEXT: .short 60319 # 0xeb9f
+; CHECK-NEXT: .byte 1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .long 24
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 132
+; CHECK-NEXT: .long 132
+; CHECK-NEXT: .long 106
+; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 1)
+; CHECK-NEXT: .long 33554432 # 0x2000000
+; CHECK-NEXT: .long 2
+; CHECK-NEXT: .long 1 # BTF_KIND_STRUCT(id = 2)
+; CHECK-NEXT: .long 67108866 # 0x4000002
+; CHECK-NEXT: .long 12
+; CHECK-NEXT: .long 9
+; CHECK-NEXT: .long 3
+; CHECK-NEXT: .long 0 # 0x0
+; CHECK-NEXT: .long 11
+; CHECK-NEXT: .long 4
+; CHECK-NEXT: .long 32 # 0x20
+; CHECK-NEXT: .long 15 # BTF_KIND_INT(id = 3)
+; CHECK-NEXT: .long 16777216 # 0x1000000
+; CHECK-NEXT: .long 4
+; CHECK-NEXT: .long 16777248 # 0x1000020
+; CHECK-NEXT: .long 19 # BTF_KIND_STRUCT(id = 4)
+; CHECK-NEXT: .long 67108866 # 0x4000002
+; CHECK-NEXT: .long 8
+; CHECK-NEXT: .long 30
+; CHECK-NEXT: .long 3
+; CHECK-NEXT: .long 0 # 0x0
+; CHECK-NEXT: .long 37
+; CHECK-NEXT: .long 3
+; CHECK-NEXT: .long 32 # 0x20
+; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 5)
+; CHECK-NEXT: .long 218103809 # 0xd000001
+; CHECK-NEXT: .long 3
+; CHECK-NEXT: .long 44
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long 48 # BTF_KIND_FUNC(id = 6)
+; CHECK-NEXT: .long 201326592 # 0xc000000
+; CHECK-NEXT: .long 5
+; CHECK-NEXT: .byte 0 # string offset=0
+; CHECK-NEXT: .ascii "sk_buff" # string offset=1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .byte 105 # string offset=9
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "dev" # string offset=11
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "int" # string offset=15
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "net_device" # string offset=19
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "dev_id" # string offset=30
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "others" # string offset=37
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "ctx" # string offset=44
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "bpf_prog" # string offset=48
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii ".text" # string offset=57
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "/tmp/home/yhs/work/tests/llvm/test.c" # string offset=63
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "0:1:0" # string offset=100
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .section .BTF.ext,"",@progbits
+; CHECK-NEXT: .short 60319 # 0xeb9f
+; CHECK-NEXT: .byte 1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .long 40
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 20
+; CHECK-NEXT: .long 20
+; CHECK-NEXT: .long 76
+; CHECK-NEXT: .long 96
+; CHECK-NEXT: .long 24
+; CHECK-NEXT: .long 120
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 8 # FuncInfo
+
+; CHECK: .long 12 # OffsetReloc
+; CHECK-NEXT: .long 57 # Offset reloc section string offset=57
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long .Ltmp2
+; CHECK-NEXT: .long 2
+; CHECK-NEXT: .long 100
+
+; Function Attrs: argmemonly nounwind
+declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
+
+; Function Attrs: nounwind readnone
+declare %struct.net_device* @llvm.preserve.struct.access.index.p0s_struct.net_devices.p0s_struct.sk_buffs(%struct.sk_buff*, i32 immarg, i32 immarg) #2
+
+; Function Attrs: nounwind readnone
+declare i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.net_devices(%struct.net_device*, i32 immarg, i32 immarg) #2
+
+; Function Attrs: argmemonly nounwind
+declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
+
+; Function Attrs: nounwind readnone speculatable
+declare void @llvm.dbg.value(metadata, metadata, metadata) #3
+
+attributes #0 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "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 = { argmemonly nounwind }
+attributes #2 = { nounwind readnone }
+attributes #3 = { nounwind readnone speculatable }
+attributes #4 = { nounwind }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!11, !12, !13}
+!llvm.ident = !{!14}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 9.0.0 (trunk 360739) (llvm/trunk 360747)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, globals: !3, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/llvm")
+!2 = !{}
+!3 = !{!4}
+!4 = !DIGlobalVariableExpression(var: !5, expr: !DIExpression())
+!5 = distinct !DIGlobalVariable(name: "bpf_probe_read", scope: !0, file: !1, line: 10, type: !6, isLocal: true, isDefinition: true)
+!6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64)
+!7 = !DISubroutineType(types: !8)
+!8 = !{!9, !10, !9, !10}
+!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!10 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)
+!11 = !{i32 2, !"Dwarf Version", i32 4}
+!12 = !{i32 2, !"Debug Info Version", i32 3}
+!13 = !{i32 1, !"wchar_size", i32 4}
+!14 = !{!"clang version 9.0.0 (trunk 360739) (llvm/trunk 360747)"}
+!15 = distinct !DISubprogram(name: "bpf_prog", scope: !1, file: !1, line: 13, type: !16, scopeLine: 13, flags: DIFlagPrototyped, isLocal: false, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !27)
+!16 = !DISubroutineType(types: !17)
+!17 = !{!9, !18}
+!18 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !19, size: 64)
+!19 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "sk_buff", file: !1, line: 5, size: 96, elements: !20)
+!20 = !{!21, !22}
+!21 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !19, file: !1, line: 6, baseType: !9, size: 32)
+!22 = !DIDerivedType(tag: DW_TAG_member, name: "dev", scope: !19, file: !1, line: 7, baseType: !23, size: 64, offset: 32)
+!23 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "net_device", file: !1, line: 1, size: 64, elements: !24)
+!24 = !{!25, !26}
+!25 = !DIDerivedType(tag: DW_TAG_member, name: "dev_id", scope: !23, file: !1, line: 2, baseType: !9, size: 32)
+!26 = !DIDerivedType(tag: DW_TAG_member, name: "others", scope: !23, file: !1, line: 3, baseType: !9, size: 32, offset: 32)
+!27 = !{!28, !29}
+!28 = !DILocalVariable(name: "ctx", arg: 1, scope: !15, file: !1, line: 13, type: !18)
+!29 = !DILocalVariable(name: "dev_id", scope: !15, file: !1, line: 14, type: !9)
+!30 = !DILocation(line: 0, scope: !15)
+!31 = !DILocation(line: 14, column: 3, scope: !15)
+!32 = !DILocation(line: 15, column: 40, scope: !15)
+!33 = !DILocation(line: 15, column: 3, scope: !15)
+!34 = !DILocation(line: 16, column: 10, scope: !15)
+!35 = !{!36, !36, i64 0}
+!36 = !{!"int", !37, i64 0}
+!37 = !{!"omnipotent char", !38, i64 0}
+!38 = !{!"Simple C/C++ TBAA"}
+!39 = !DILocation(line: 17, column: 1, scope: !15)
+!40 = !DILocation(line: 16, column: 3, scope: !15)
--- /dev/null
+; 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
+; Source code:
+; struct sk_buff {
+; int i;
+; struct {
+; int dev_id;
+; int others;
+; } dev[10];
+; };
+; #define _(x) (__builtin_preserve_access_index(x))
+; static int (*bpf_probe_read)(void *dst, int size, void *unsafe_ptr)
+; = (void *) 4;
+;
+; int bpf_prog(struct sk_buff *ctx) {
+; int dev_id;
+; bpf_probe_read(&dev_id, sizeof(int), _(&ctx->dev[5].dev_id));
+; return dev_id;
+; }
+; Compilation flag:
+; clang -target bpf -O2 -g -S -emit-llvm test.c
+
+%struct.sk_buff = type { i32, [10 x %struct.anon] }
+%struct.anon = type { i32, i32 }
+
+; Function Attrs: nounwind
+define dso_local i32 @bpf_prog(%struct.sk_buff*) local_unnamed_addr #0 !dbg !15 {
+ %2 = alloca i32, align 4
+ call void @llvm.dbg.value(metadata %struct.sk_buff* %0, metadata !31, metadata !DIExpression()), !dbg !33
+ %3 = bitcast i32* %2 to i8*, !dbg !34
+ call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %3) #4, !dbg !34
+ %4 = tail call [10 x %struct.anon]* @llvm.preserve.struct.access.index.p0a10s_struct.anons.p0s_struct.sk_buffs(%struct.sk_buff* %0, i32 1, i32 1), !dbg !35, !llvm.preserve.access.index !19
+ %5 = tail call %struct.anon* @llvm.preserve.array.access.index.p0s_struct.anons.p0a10s_struct.anons([10 x %struct.anon]* %4, i32 1, i32 5), !dbg !35
+ %6 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.anons(%struct.anon* %5, i32 0, i32 0), !dbg !35, !llvm.preserve.access.index !24
+ %7 = bitcast i32* %6 to i8*, !dbg !35
+ %8 = call i32 inttoptr (i64 4 to i32 (i8*, i32, i8*)*)(i8* nonnull %3, i32 4, i8* %7) #4, !dbg !36
+ %9 = load i32, i32* %2, align 4, !dbg !37, !tbaa !38
+ call void @llvm.dbg.value(metadata i32 %9, metadata !32, metadata !DIExpression()), !dbg !33
+ call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %3) #4, !dbg !42
+ ret i32 %9, !dbg !43
+}
+
+; CHECK: .section .BTF,"",@progbits
+; CHECK-NEXT: .short 60319 # 0xeb9f
+; CHECK-NEXT: .byte 1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .long 24
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 172
+; CHECK-NEXT: .long 172
+; CHECK-NEXT: .long 117
+; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 1)
+; CHECK-NEXT: .long 33554432 # 0x2000000
+; CHECK-NEXT: .long 2
+; CHECK-NEXT: .long 1 # BTF_KIND_STRUCT(id = 2)
+; CHECK-NEXT: .long 67108866 # 0x4000002
+; CHECK-NEXT: .long 84
+; CHECK-NEXT: .long 9
+; CHECK-NEXT: .long 3
+; CHECK-NEXT: .long 0 # 0x0
+; CHECK-NEXT: .long 11
+; CHECK-NEXT: .long 5
+; CHECK-NEXT: .long 32 # 0x20
+; CHECK-NEXT: .long 15 # BTF_KIND_INT(id = 3)
+; CHECK-NEXT: .long 16777216 # 0x1000000
+; CHECK-NEXT: .long 4
+; CHECK-NEXT: .long 16777248 # 0x1000020
+; CHECK-NEXT: .long 0 # BTF_KIND_STRUCT(id = 4)
+; CHECK-NEXT: .long 67108866 # 0x4000002
+; CHECK-NEXT: .long 8
+; CHECK-NEXT: .long 19
+; CHECK-NEXT: .long 3
+; CHECK-NEXT: .long 0 # 0x0
+; CHECK-NEXT: .long 26
+; CHECK-NEXT: .long 3
+; CHECK-NEXT: .long 32 # 0x20
+; CHECK-NEXT: .long 0 # BTF_KIND_ARRAY(id = 5)
+; CHECK-NEXT: .long 50331648 # 0x3000000
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 4
+; CHECK-NEXT: .long 6
+; CHECK-NEXT: .long 10
+; CHECK-NEXT: .long 33 # BTF_KIND_INT(id = 6)
+; CHECK-NEXT: .long 16777216 # 0x1000000
+; CHECK-NEXT: .long 4
+; CHECK-NEXT: .long 32 # 0x20
+; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 7)
+; CHECK-NEXT: .long 218103809 # 0xd000001
+; CHECK-NEXT: .long 3
+; CHECK-NEXT: .long 53
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long 57 # BTF_KIND_FUNC(id = 8)
+; CHECK-NEXT: .long 201326592 # 0xc000000
+; CHECK-NEXT: .long 7
+; CHECK-NEXT: .byte 0 # string offset=0
+; CHECK-NEXT: .ascii "sk_buff" # string offset=1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .byte 105 # string offset=9
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "dev" # string offset=11
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "int" # string offset=15
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "dev_id" # string offset=19
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "others" # string offset=26
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "__ARRAY_SIZE_TYPE__" # string offset=33
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "ctx" # string offset=53
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "bpf_prog" # string offset=57
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii ".text" # string offset=66
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "/tmp/home/yhs/work/tests/llvm/test.c" # string offset=72
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "0:1:5:0" # string offset=109
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .section .BTF.ext,"",@progbits
+; CHECK-NEXT: .short 60319 # 0xeb9f
+; CHECK-NEXT: .byte 1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .long 40
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 20
+; CHECK-NEXT: .long 20
+; CHECK-NEXT: .long 76
+; CHECK-NEXT: .long 96
+; CHECK-NEXT: .long 24
+; CHECK-NEXT: .long 120
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 8 # FuncInfo
+
+; CHECK: .long 12 # OffsetReloc
+; CHECK-NEXT: .long 66 # Offset reloc section string offset=66
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long .Ltmp2
+; CHECK-NEXT: .long 2
+; CHECK-NEXT: .long 109
+
+; Function Attrs: argmemonly nounwind
+declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
+
+; Function Attrs: nounwind readnone
+declare [10 x %struct.anon]* @llvm.preserve.struct.access.index.p0a10s_struct.anons.p0s_struct.sk_buffs(%struct.sk_buff*, i32 immarg, i32 immarg) #2
+
+; Function Attrs: nounwind readnone
+declare %struct.anon* @llvm.preserve.array.access.index.p0s_struct.anons.p0a10s_struct.anons([10 x %struct.anon]*, i32 immarg, i32 immarg) #2
+
+; Function Attrs: nounwind readnone
+declare i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.anons(%struct.anon*, i32 immarg, i32 immarg) #2
+
+; Function Attrs: argmemonly nounwind
+declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
+
+; Function Attrs: nounwind readnone speculatable
+declare void @llvm.dbg.value(metadata, metadata, metadata) #3
+
+attributes #0 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "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 = { argmemonly nounwind }
+attributes #2 = { nounwind readnone }
+attributes #3 = { nounwind readnone speculatable }
+attributes #4 = { nounwind }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!11, !12, !13}
+!llvm.ident = !{!14}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 9.0.0 (trunk 360739) (llvm/trunk 360747)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, globals: !3, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/llvm")
+!2 = !{}
+!3 = !{!4}
+!4 = !DIGlobalVariableExpression(var: !5, expr: !DIExpression())
+!5 = distinct !DIGlobalVariable(name: "bpf_probe_read", scope: !0, file: !1, line: 9, type: !6, isLocal: true, isDefinition: true)
+!6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64)
+!7 = !DISubroutineType(types: !8)
+!8 = !{!9, !10, !9, !10}
+!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!10 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)
+!11 = !{i32 2, !"Dwarf Version", i32 4}
+!12 = !{i32 2, !"Debug Info Version", i32 3}
+!13 = !{i32 1, !"wchar_size", i32 4}
+!14 = !{!"clang version 9.0.0 (trunk 360739) (llvm/trunk 360747)"}
+!15 = distinct !DISubprogram(name: "bpf_prog", scope: !1, file: !1, line: 12, type: !16, scopeLine: 12, flags: DIFlagPrototyped, isLocal: false, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !30)
+!16 = !DISubroutineType(types: !17)
+!17 = !{!9, !18}
+!18 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !19, size: 64)
+!19 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "sk_buff", file: !1, line: 1, size: 672, elements: !20)
+!20 = !{!21, !22}
+!21 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !19, file: !1, line: 2, baseType: !9, size: 32)
+!22 = !DIDerivedType(tag: DW_TAG_member, name: "dev", scope: !19, file: !1, line: 6, baseType: !23, size: 640, offset: 32)
+!23 = !DICompositeType(tag: DW_TAG_array_type, baseType: !24, size: 640, elements: !28)
+!24 = distinct !DICompositeType(tag: DW_TAG_structure_type, scope: !19, file: !1, line: 3, size: 64, elements: !25)
+!25 = !{!26, !27}
+!26 = !DIDerivedType(tag: DW_TAG_member, name: "dev_id", scope: !24, file: !1, line: 4, baseType: !9, size: 32)
+!27 = !DIDerivedType(tag: DW_TAG_member, name: "others", scope: !24, file: !1, line: 5, baseType: !9, size: 32, offset: 32)
+!28 = !{!29}
+!29 = !DISubrange(count: 10)
+!30 = !{!31, !32}
+!31 = !DILocalVariable(name: "ctx", arg: 1, scope: !15, file: !1, line: 12, type: !18)
+!32 = !DILocalVariable(name: "dev_id", scope: !15, file: !1, line: 13, type: !9)
+!33 = !DILocation(line: 0, scope: !15)
+!34 = !DILocation(line: 13, column: 3, scope: !15)
+!35 = !DILocation(line: 14, column: 40, scope: !15)
+!36 = !DILocation(line: 14, column: 3, scope: !15)
+!37 = !DILocation(line: 15, column: 10, scope: !15)
+!38 = !{!39, !39, i64 0}
+!39 = !{!"int", !40, i64 0}
+!40 = !{!"omnipotent char", !41, i64 0}
+!41 = !{!"Simple C/C++ TBAA"}
+!42 = !DILocation(line: 16, column: 1, scope: !15)
+!43 = !DILocation(line: 15, column: 3, scope: !15)
--- /dev/null
+; 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
+; Source code:
+; struct net_device {
+; int dev_id;
+; int others;
+; };
+; struct sk_buff {
+; int i;
+; struct net_device dev[10];
+; };
+; #define _(x) (__builtin_preserve_access_index(x))
+; static int (*bpf_probe_read)(void *dst, int size, void *unsafe_ptr)
+; = (void *) 4;
+;
+; int bpf_prog(struct sk_buff *ctx) {
+; int dev_id;
+; bpf_probe_read(&dev_id, sizeof(int), _(&ctx->dev[5].dev_id));
+; return dev_id;
+; }
+; Compilation flag:
+; clang -target bpf -O2 -g -S -emit-llvm test.c
+
+%struct.sk_buff = type { i32, [10 x %struct.net_device] }
+%struct.net_device = type { i32, i32 }
+
+; Function Attrs: nounwind
+define dso_local i32 @bpf_prog(%struct.sk_buff*) local_unnamed_addr #0 !dbg !15 {
+ %2 = alloca i32, align 4
+ call void @llvm.dbg.value(metadata %struct.sk_buff* %0, metadata !31, metadata !DIExpression()), !dbg !33
+ %3 = bitcast i32* %2 to i8*, !dbg !34
+ call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %3) #4, !dbg !34
+ %4 = tail call [10 x %struct.net_device]* @llvm.preserve.struct.access.index.p0a10s_struct.net_devices.p0s_struct.sk_buffs(%struct.sk_buff* %0, i32 1, i32 1), !dbg !35, !llvm.preserve.access.index !19
+ %5 = tail call %struct.net_device* @llvm.preserve.array.access.index.p0s_struct.net_devices.p0a10s_struct.net_devices([10 x %struct.net_device]* %4, i32 1, i32 5), !dbg !35
+ %6 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.net_devices(%struct.net_device* %5, i32 0, i32 0), !dbg !35, !llvm.preserve.access.index !24
+ %7 = bitcast i32* %6 to i8*, !dbg !35
+ %8 = call i32 inttoptr (i64 4 to i32 (i8*, i32, i8*)*)(i8* nonnull %3, i32 4, i8* %7) #4, !dbg !36
+ %9 = load i32, i32* %2, align 4, !dbg !37, !tbaa !38
+ call void @llvm.dbg.value(metadata i32 %9, metadata !32, metadata !DIExpression()), !dbg !33
+ call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %3) #4, !dbg !42
+ ret i32 %9, !dbg !43
+}
+
+; CHECK: .section .BTF,"",@progbits
+; CHECK-NEXT: .short 60319 # 0xeb9f
+; CHECK-NEXT: .byte 1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .long 24
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 172
+; CHECK-NEXT: .long 172
+; CHECK-NEXT: .long 128
+; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 1)
+; CHECK-NEXT: .long 33554432 # 0x2000000
+; CHECK-NEXT: .long 2
+; CHECK-NEXT: .long 1 # BTF_KIND_STRUCT(id = 2)
+; CHECK-NEXT: .long 67108866 # 0x4000002
+; CHECK-NEXT: .long 84
+; CHECK-NEXT: .long 9
+; CHECK-NEXT: .long 3
+; CHECK-NEXT: .long 0 # 0x0
+; CHECK-NEXT: .long 11
+; CHECK-NEXT: .long 5
+; CHECK-NEXT: .long 32 # 0x20
+; CHECK-NEXT: .long 15 # BTF_KIND_INT(id = 3)
+; CHECK-NEXT: .long 16777216 # 0x1000000
+; CHECK-NEXT: .long 4
+; CHECK-NEXT: .long 16777248 # 0x1000020
+; CHECK-NEXT: .long 19 # BTF_KIND_STRUCT(id = 4)
+; CHECK-NEXT: .long 67108866 # 0x4000002
+; CHECK-NEXT: .long 8
+; CHECK-NEXT: .long 30
+; CHECK-NEXT: .long 3
+; CHECK-NEXT: .long 0 # 0x0
+; CHECK-NEXT: .long 37
+; CHECK-NEXT: .long 3
+; CHECK-NEXT: .long 32 # 0x20
+; CHECK-NEXT: .long 0 # BTF_KIND_ARRAY(id = 5)
+; CHECK-NEXT: .long 50331648 # 0x3000000
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 4
+; CHECK-NEXT: .long 6
+; CHECK-NEXT: .long 10
+; CHECK-NEXT: .long 44 # BTF_KIND_INT(id = 6)
+; CHECK-NEXT: .long 16777216 # 0x1000000
+; CHECK-NEXT: .long 4
+; CHECK-NEXT: .long 32 # 0x20
+; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 7)
+; CHECK-NEXT: .long 218103809 # 0xd000001
+; CHECK-NEXT: .long 3
+; CHECK-NEXT: .long 64
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long 68 # BTF_KIND_FUNC(id = 8)
+; CHECK-NEXT: .long 201326592 # 0xc000000
+; CHECK-NEXT: .long 7
+; CHECK-NEXT: .byte 0 # string offset=0
+; CHECK-NEXT: .ascii "sk_buff" # string offset=1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .byte 105 # string offset=9
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "dev" # string offset=11
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "int" # string offset=15
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "net_device" # string offset=19
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "dev_id" # string offset=30
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "others" # string offset=37
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "__ARRAY_SIZE_TYPE__" # string offset=44
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "ctx" # string offset=64
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "bpf_prog" # string offset=68
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii ".text" # string offset=77
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "/tmp/home/yhs/work/tests/llvm/test.c" # string offset=83
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "0:1:5:0" # string offset=120
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .section .BTF.ext,"",@progbits
+; CHECK-NEXT: .short 60319 # 0xeb9f
+; CHECK-NEXT: .byte 1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .long 40
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 20
+; CHECK-NEXT: .long 20
+; CHECK-NEXT: .long 76
+; CHECK-NEXT: .long 96
+; CHECK-NEXT: .long 24
+; CHECK-NEXT: .long 120
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 8 # FuncInfo
+
+; CHECK: .long 12 # OffsetReloc
+; CHECK-NEXT: .long 77 # Offset reloc section string offset=77
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long .Ltmp2
+; CHECK-NEXT: .long 2
+; CHECK-NEXT: .long 120
+
+; Function Attrs: argmemonly nounwind
+declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
+
+; Function Attrs: nounwind readnone
+declare [10 x %struct.net_device]* @llvm.preserve.struct.access.index.p0a10s_struct.net_devices.p0s_struct.sk_buffs(%struct.sk_buff*, i32 immarg, i32 immarg) #2
+
+; Function Attrs: nounwind readnone
+declare %struct.net_device* @llvm.preserve.array.access.index.p0s_struct.net_devices.p0a10s_struct.net_devices([10 x %struct.net_device]*, i32 immarg, i32 immarg) #2
+
+; Function Attrs: nounwind readnone
+declare i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.net_devices(%struct.net_device*, i32 immarg, i32 immarg) #2
+
+; Function Attrs: argmemonly nounwind
+declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
+
+; Function Attrs: nounwind readnone speculatable
+declare void @llvm.dbg.value(metadata, metadata, metadata) #3
+
+attributes #0 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "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 = { argmemonly nounwind }
+attributes #2 = { nounwind readnone }
+attributes #3 = { nounwind readnone speculatable }
+attributes #4 = { nounwind }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!11, !12, !13}
+!llvm.ident = !{!14}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 9.0.0 (trunk 360739) (llvm/trunk 360747)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, globals: !3, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/llvm")
+!2 = !{}
+!3 = !{!4}
+!4 = !DIGlobalVariableExpression(var: !5, expr: !DIExpression())
+!5 = distinct !DIGlobalVariable(name: "bpf_probe_read", scope: !0, file: !1, line: 10, type: !6, isLocal: true, isDefinition: true)
+!6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64)
+!7 = !DISubroutineType(types: !8)
+!8 = !{!9, !10, !9, !10}
+!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!10 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)
+!11 = !{i32 2, !"Dwarf Version", i32 4}
+!12 = !{i32 2, !"Debug Info Version", i32 3}
+!13 = !{i32 1, !"wchar_size", i32 4}
+!14 = !{!"clang version 9.0.0 (trunk 360739) (llvm/trunk 360747)"}
+!15 = distinct !DISubprogram(name: "bpf_prog", scope: !1, file: !1, line: 13, type: !16, scopeLine: 13, flags: DIFlagPrototyped, isLocal: false, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !30)
+!16 = !DISubroutineType(types: !17)
+!17 = !{!9, !18}
+!18 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !19, size: 64)
+!19 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "sk_buff", file: !1, line: 5, size: 672, elements: !20)
+!20 = !{!21, !22}
+!21 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !19, file: !1, line: 6, baseType: !9, size: 32)
+!22 = !DIDerivedType(tag: DW_TAG_member, name: "dev", scope: !19, file: !1, line: 7, baseType: !23, size: 640, offset: 32)
+!23 = !DICompositeType(tag: DW_TAG_array_type, baseType: !24, size: 640, elements: !28)
+!24 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "net_device", file: !1, line: 1, size: 64, elements: !25)
+!25 = !{!26, !27}
+!26 = !DIDerivedType(tag: DW_TAG_member, name: "dev_id", scope: !24, file: !1, line: 2, baseType: !9, size: 32)
+!27 = !DIDerivedType(tag: DW_TAG_member, name: "others", scope: !24, file: !1, line: 3, baseType: !9, size: 32, offset: 32)
+!28 = !{!29}
+!29 = !DISubrange(count: 10)
+!30 = !{!31, !32}
+!31 = !DILocalVariable(name: "ctx", arg: 1, scope: !15, file: !1, line: 13, type: !18)
+!32 = !DILocalVariable(name: "dev_id", scope: !15, file: !1, line: 14, type: !9)
+!33 = !DILocation(line: 0, scope: !15)
+!34 = !DILocation(line: 14, column: 3, scope: !15)
+!35 = !DILocation(line: 15, column: 40, scope: !15)
+!36 = !DILocation(line: 15, column: 3, scope: !15)
+!37 = !DILocation(line: 16, column: 10, scope: !15)
+!38 = !{!39, !39, i64 0}
+!39 = !{!"int", !40, i64 0}
+!40 = !{!"omnipotent char", !41, i64 0}
+!41 = !{!"Simple C/C++ TBAA"}
+!42 = !DILocation(line: 17, column: 1, scope: !15)
+!43 = !DILocation(line: 16, column: 3, scope: !15)
--- /dev/null
+; 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
+; Source code:
+; union sk_buff {
+; int i;
+; struct {
+; int netid;
+; union {
+; int dev_id;
+; int others;
+; } dev;
+; } u;
+; };
+; #define _(x) (__builtin_preserve_access_index(x))
+; static int (*bpf_probe_read)(void *dst, int size, void *unsafe_ptr)
+; = (void *) 4;
+;
+; int bpf_prog(union sk_buff *ctx) {
+; int dev_id;
+; bpf_probe_read(&dev_id, sizeof(int), _(&ctx->u.dev.dev_id));
+; return dev_id;
+; }
+; Compilation flag:
+; clang -target bpf -O2 -g -S -emit-llvm test.c
+
+%union.sk_buff = type { %struct.anon }
+%struct.anon = type { i32, %union.anon }
+%union.anon = type { i32 }
+
+; Function Attrs: nounwind
+define dso_local i32 @bpf_prog(%union.sk_buff*) local_unnamed_addr #0 !dbg !15 {
+ %2 = alloca i32, align 4
+ call void @llvm.dbg.value(metadata %union.sk_buff* %0, metadata !32, metadata !DIExpression()), !dbg !34
+ %3 = bitcast i32* %2 to i8*, !dbg !35
+ call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %3) #4, !dbg !35
+ %4 = tail call %union.sk_buff* @llvm.preserve.union.access.index.p0s_union.sk_buffs.p0s_union.sk_buffs(%union.sk_buff* %0, i32 1), !dbg !36, !llvm.preserve.access.index !19
+ %5 = getelementptr inbounds %union.sk_buff, %union.sk_buff* %4, i64 0, i32 0, !dbg !36
+ %6 = tail call %union.anon* @llvm.preserve.struct.access.index.p0s_union.anons.p0s_struct.anons(%struct.anon* %5, i32 1, i32 1), !dbg !36, !llvm.preserve.access.index !23
+ %7 = tail call %union.anon* @llvm.preserve.union.access.index.p0s_union.anons.p0s_union.anons(%union.anon* %6, i32 0), !dbg !36, !llvm.preserve.access.index !27
+ %8 = bitcast %union.anon* %7 to i8*, !dbg !36
+ %9 = call i32 inttoptr (i64 4 to i32 (i8*, i32, i8*)*)(i8* nonnull %3, i32 4, i8* %8) #4, !dbg !37
+ %10 = load i32, i32* %2, align 4, !dbg !38, !tbaa !39
+ call void @llvm.dbg.value(metadata i32 %10, metadata !33, metadata !DIExpression()), !dbg !34
+ call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %3) #4, !dbg !43
+ ret i32 %10, !dbg !44
+}
+
+; CHECK: .section .BTF,"",@progbits
+; CHECK-NEXT: .short 60319 # 0xeb9f
+; CHECK-NEXT: .byte 1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .long 24
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 168
+; CHECK-NEXT: .long 168
+; CHECK-NEXT: .long 105
+; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 1)
+; CHECK-NEXT: .long 33554432 # 0x2000000
+; CHECK-NEXT: .long 2
+; CHECK-NEXT: .long 1 # BTF_KIND_UNION(id = 2)
+; CHECK-NEXT: .long 83886082 # 0x5000002
+; CHECK-NEXT: .long 8
+; CHECK-NEXT: .long 9
+; CHECK-NEXT: .long 3
+; CHECK-NEXT: .long 0 # 0x0
+; CHECK-NEXT: .long 11
+; CHECK-NEXT: .long 4
+; CHECK-NEXT: .long 0 # 0x0
+; CHECK-NEXT: .long 13 # BTF_KIND_INT(id = 3)
+; CHECK-NEXT: .long 16777216 # 0x1000000
+; CHECK-NEXT: .long 4
+; CHECK-NEXT: .long 16777248 # 0x1000020
+; CHECK-NEXT: .long 0 # BTF_KIND_STRUCT(id = 4)
+; CHECK-NEXT: .long 67108866 # 0x4000002
+; CHECK-NEXT: .long 8
+; CHECK-NEXT: .long 17
+; CHECK-NEXT: .long 3
+; CHECK-NEXT: .long 0 # 0x0
+; CHECK-NEXT: .long 23
+; CHECK-NEXT: .long 5
+; CHECK-NEXT: .long 32 # 0x20
+; CHECK-NEXT: .long 0 # BTF_KIND_UNION(id = 5)
+; CHECK-NEXT: .long 83886082 # 0x5000002
+; CHECK-NEXT: .long 4
+; CHECK-NEXT: .long 27
+; CHECK-NEXT: .long 3
+; CHECK-NEXT: .long 0 # 0x0
+; CHECK-NEXT: .long 34
+; CHECK-NEXT: .long 3
+; CHECK-NEXT: .long 0 # 0x0
+; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 6)
+; CHECK-NEXT: .long 218103809 # 0xd000001
+; CHECK-NEXT: .long 3
+; CHECK-NEXT: .long 41
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long 45 # BTF_KIND_FUNC(id = 7)
+; CHECK-NEXT: .long 201326592 # 0xc000000
+; CHECK-NEXT: .long 6
+; CHECK-NEXT: .byte 0 # string offset=0
+; CHECK-NEXT: .ascii "sk_buff" # string offset=1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .byte 105 # string offset=9
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .byte 117 # string offset=11
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "int" # string offset=13
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "netid" # string offset=17
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "dev" # string offset=23
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "dev_id" # string offset=27
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "others" # string offset=34
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "ctx" # string offset=41
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "bpf_prog" # string offset=45
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii ".text" # string offset=54
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "/tmp/home/yhs/work/tests/llvm/test.c" # string offset=60
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "0:1:1:0" # string offset=97
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .section .BTF.ext,"",@progbits
+; CHECK-NEXT: .short 60319 # 0xeb9f
+; CHECK-NEXT: .byte 1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .long 40
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 20
+; CHECK-NEXT: .long 20
+; CHECK-NEXT: .long 76
+; CHECK-NEXT: .long 96
+; CHECK-NEXT: .long 24
+; CHECK-NEXT: .long 120
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 8 # FuncInfo
+
+; CHECK: .long 12 # OffsetReloc
+; CHECK-NEXT: .long 54 # Offset reloc section string offset=54
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long .Ltmp2
+; CHECK-NEXT: .long 2
+; CHECK-NEXT: .long 97
+
+; Function Attrs: argmemonly nounwind
+declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
+
+; Function Attrs: nounwind readnone
+declare %union.sk_buff* @llvm.preserve.union.access.index.p0s_union.sk_buffs.p0s_union.sk_buffs(%union.sk_buff*, i32 immarg) #2
+
+; Function Attrs: nounwind readnone
+declare %union.anon* @llvm.preserve.struct.access.index.p0s_union.anons.p0s_struct.anons(%struct.anon*, i32 immarg, i32 immarg) #2
+
+; Function Attrs: nounwind readnone
+declare %union.anon* @llvm.preserve.union.access.index.p0s_union.anons.p0s_union.anons(%union.anon*, i32 immarg) #2
+
+; Function Attrs: argmemonly nounwind
+declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
+
+; Function Attrs: nounwind readnone speculatable
+declare void @llvm.dbg.value(metadata, metadata, metadata) #3
+
+attributes #0 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "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 = { argmemonly nounwind }
+attributes #2 = { nounwind readnone }
+attributes #3 = { nounwind readnone speculatable }
+attributes #4 = { nounwind }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!11, !12, !13}
+!llvm.ident = !{!14}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 9.0.0 (trunk 360739) (llvm/trunk 360747)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, globals: !3, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/llvm")
+!2 = !{}
+!3 = !{!4}
+!4 = !DIGlobalVariableExpression(var: !5, expr: !DIExpression())
+!5 = distinct !DIGlobalVariable(name: "bpf_probe_read", scope: !0, file: !1, line: 12, type: !6, isLocal: true, isDefinition: true)
+!6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64)
+!7 = !DISubroutineType(types: !8)
+!8 = !{!9, !10, !9, !10}
+!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!10 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)
+!11 = !{i32 2, !"Dwarf Version", i32 4}
+!12 = !{i32 2, !"Debug Info Version", i32 3}
+!13 = !{i32 1, !"wchar_size", i32 4}
+!14 = !{!"clang version 9.0.0 (trunk 360739) (llvm/trunk 360747)"}
+!15 = distinct !DISubprogram(name: "bpf_prog", scope: !1, file: !1, line: 15, type: !16, scopeLine: 15, flags: DIFlagPrototyped, isLocal: false, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !31)
+!16 = !DISubroutineType(types: !17)
+!17 = !{!9, !18}
+!18 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !19, size: 64)
+!19 = distinct !DICompositeType(tag: DW_TAG_union_type, name: "sk_buff", file: !1, line: 1, size: 64, elements: !20)
+!20 = !{!21, !22}
+!21 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !19, file: !1, line: 2, baseType: !9, size: 32)
+!22 = !DIDerivedType(tag: DW_TAG_member, name: "u", scope: !19, file: !1, line: 9, baseType: !23, size: 64)
+!23 = distinct !DICompositeType(tag: DW_TAG_structure_type, scope: !19, file: !1, line: 3, size: 64, elements: !24)
+!24 = !{!25, !26}
+!25 = !DIDerivedType(tag: DW_TAG_member, name: "netid", scope: !23, file: !1, line: 4, baseType: !9, size: 32)
+!26 = !DIDerivedType(tag: DW_TAG_member, name: "dev", scope: !23, file: !1, line: 8, baseType: !27, size: 32, offset: 32)
+!27 = distinct !DICompositeType(tag: DW_TAG_union_type, scope: !23, file: !1, line: 5, size: 32, elements: !28)
+!28 = !{!29, !30}
+!29 = !DIDerivedType(tag: DW_TAG_member, name: "dev_id", scope: !27, file: !1, line: 6, baseType: !9, size: 32)
+!30 = !DIDerivedType(tag: DW_TAG_member, name: "others", scope: !27, file: !1, line: 7, baseType: !9, size: 32)
+!31 = !{!32, !33}
+!32 = !DILocalVariable(name: "ctx", arg: 1, scope: !15, file: !1, line: 15, type: !18)
+!33 = !DILocalVariable(name: "dev_id", scope: !15, file: !1, line: 16, type: !9)
+!34 = !DILocation(line: 0, scope: !15)
+!35 = !DILocation(line: 16, column: 3, scope: !15)
+!36 = !DILocation(line: 17, column: 40, scope: !15)
+!37 = !DILocation(line: 17, column: 3, scope: !15)
+!38 = !DILocation(line: 18, column: 10, scope: !15)
+!39 = !{!40, !40, i64 0}
+!40 = !{!"int", !41, i64 0}
+!41 = !{!"omnipotent char", !42, i64 0}
+!42 = !{!"Simple C/C++ TBAA"}
+!43 = !DILocation(line: 19, column: 1, scope: !15)
+!44 = !DILocation(line: 18, column: 3, scope: !15)
--- /dev/null
+; 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
+; Source code:
+; extern __attribute__((section(".BPF.patchable_externs"))) char a;
+; int foo() { return a; }
+; Compilation flag:
+; clang -target bpf -O2 -g -S -emit-llvm test.c
+
+@a = external dso_local local_unnamed_addr global i8, section ".BPF.patchable_externs", align 1
+
+; Function Attrs: norecurse nounwind readonly
+define dso_local i32 @foo() local_unnamed_addr #0 !dbg !7 {
+ %1 = load i8, i8* @a, align 1, !dbg !11, !tbaa !12
+ %2 = sext i8 %1 to i32, !dbg !11
+; CHECK: r0 = 0
+; CHECK-NEXT: r0 <<= 56
+; CHECK-NEXT: r0 s>>= 56
+ ret i32 %2, !dbg !15
+}
+
+; CHECK: .section .BTF,"",@progbits
+; CHECK-NEXT: .short 60319 # 0xeb9f
+; CHECK-NEXT: .byte 1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .long 24
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 40
+; CHECK-NEXT: .long 40
+; CHECK-NEXT: .long 54
+; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 1)
+; CHECK-NEXT: .long 218103808 # 0xd000000
+; CHECK-NEXT: .long 2
+; CHECK-NEXT: .long 1 # BTF_KIND_INT(id = 2)
+; CHECK-NEXT: .long 16777216 # 0x1000000
+; CHECK-NEXT: .long 4
+; CHECK-NEXT: .long 16777248 # 0x1000020
+; CHECK-NEXT: .long 5 # BTF_KIND_FUNC(id = 3)
+; CHECK-NEXT: .long 201326592 # 0xc000000
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .byte 0 # string offset=0
+; CHECK-NEXT: .ascii "int" # string offset=1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "foo" # string offset=5
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii ".text" # string offset=9
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .byte 97 # string offset=15
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "/tmp/home/yhs/work/tests/llvm/test.c" # string offset=17
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .section .BTF.ext,"",@progbits
+; CHECK-NEXT: .short 60319 # 0xeb9f
+; CHECK-NEXT: .byte 1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .long 40
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 20
+; CHECK-NEXT: .long 20
+; CHECK-NEXT: .long 44
+; CHECK-NEXT: .long 64
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 64
+; CHECK-NEXT: .long 20
+; CHECK-NEXT: .long 8 # FuncInfo
+; CHECK-NEXT: .long 9 # FuncInfo section string offset=9
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long .Lfunc_begin0
+; CHECK-NEXT: .long 3
+; CHECK-NEXT: .long 16 # LineInfo
+; CHECK-NEXT: .long 9 # LineInfo section string offset=9
+; CHECK-NEXT: .long 2
+; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
+; CHECK-NEXT: .long 17
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 2068 # Line 2 Col 20
+; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
+; CHECK-NEXT: .long 17
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 2061 # Line 2 Col 13
+; CHECK-NEXT: .long 8 # ExternReloc
+; CHECK-NEXT: .long 9 # Extern reloc section string offset=9
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
+; CHECK-NEXT: .long 15
+
+attributes #0 = { norecurse nounwind readonly "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "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" }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3, !4, !5}
+!llvm.ident = !{!6}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 8.0.20181009 ", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/llvm")
+!2 = !{}
+!3 = !{i32 2, !"Dwarf Version", i32 4}
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!5 = !{i32 1, !"wchar_size", i32 4}
+!6 = !{!"clang version 8.0.20181009 "}
+!7 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 2, type: !8, isLocal: false, isDefinition: true, scopeLine: 2, isOptimized: true, unit: !0, retainedNodes: !2)
+!8 = !DISubroutineType(types: !9)
+!9 = !{!10}
+!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!11 = !DILocation(line: 2, column: 20, scope: !7)
+!12 = !{!13, !13, i64 0}
+!13 = !{!"omnipotent char", !14, i64 0}
+!14 = !{!"Simple C/C++ TBAA"}
+!15 = !DILocation(line: 2, column: 13, scope: !7)
--- /dev/null
+; 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
+; Source code:
+; extern __attribute__((section(".BPF.patchable_externs"))) unsigned a;
+; int foo() { return a; }
+; Compilation flag:
+; clang -target bpf -O2 -g -S -emit-llvm test.c
+
+@a = external dso_local local_unnamed_addr global i32, section ".BPF.patchable_externs", align 4
+
+; Function Attrs: norecurse nounwind readonly
+define dso_local i32 @foo() local_unnamed_addr #0 !dbg !7 {
+ %1 = load i32, i32* @a, align 4, !dbg !11, !tbaa !12
+; CHECK: r0 = 0
+; CHECK-NEXT: exit
+ ret i32 %1, !dbg !16
+}
+
+; CHECK: .section .BTF,"",@progbits
+; CHECK-NEXT: .short 60319 # 0xeb9f
+; CHECK-NEXT: .byte 1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .long 24
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 40
+; CHECK-NEXT: .long 40
+; CHECK-NEXT: .long 49
+; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 1)
+; CHECK-NEXT: .long 218103808 # 0xd000000
+; CHECK-NEXT: .long 2
+; CHECK-NEXT: .long 1 # BTF_KIND_INT(id = 2)
+; CHECK-NEXT: .long 16777216 # 0x1000000
+; CHECK-NEXT: .long 4
+; CHECK-NEXT: .long 16777248 # 0x1000020
+; CHECK-NEXT: .long 5 # BTF_KIND_FUNC(id = 3)
+; CHECK-NEXT: .long 201326592 # 0xc000000
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .byte 0 # string offset=0
+; CHECK-NEXT: .ascii "int" # string offset=1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "foo" # string offset=5
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii ".text" # string offset=9
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .byte 97 # string offset=15
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "/tmp/yhs/work/tests/llvm/test.c" # string offset=17
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .section .BTF.ext,"",@progbits
+; CHECK-NEXT: .short 60319 # 0xeb9f
+; CHECK-NEXT: .byte 1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .long 40
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 20
+; CHECK-NEXT: .long 20
+; CHECK-NEXT: .long 28
+; CHECK-NEXT: .long 48
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 48
+; CHECK-NEXT: .long 20
+; CHECK-NEXT: .long 8 # FuncInfo
+; CHECK-NEXT: .long 9 # FuncInfo section string offset=9
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long .Lfunc_begin0
+; CHECK-NEXT: .long 3
+; CHECK-NEXT: .long 16 # LineInfo
+; CHECK-NEXT: .long 9 # LineInfo section string offset=9
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
+; CHECK-NEXT: .long 17
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 2061 # Line 2 Col 13
+; CHECK-NEXT: .long 8 # ExternReloc
+; CHECK-NEXT: .long 9 # Extern reloc section string offset=9
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
+; CHECK-NEXT: .long 15
+
+attributes #0 = { norecurse nounwind readonly "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "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" }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3, !4, !5}
+!llvm.ident = !{!6}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 8.0.20181009 ", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/yhs/work/tests/llvm")
+!2 = !{}
+!3 = !{i32 2, !"Dwarf Version", i32 4}
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!5 = !{i32 1, !"wchar_size", i32 4}
+!6 = !{!"clang version 8.0.20181009 "}
+!7 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 2, type: !8, isLocal: false, isDefinition: true, scopeLine: 2, isOptimized: true, unit: !0, retainedNodes: !2)
+!8 = !DISubroutineType(types: !9)
+!9 = !{!10}
+!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!11 = !DILocation(line: 2, column: 20, scope: !7)
+!12 = !{!13, !13, i64 0}
+!13 = !{!"int", !14, i64 0}
+!14 = !{!"omnipotent char", !15, i64 0}
+!15 = !{!"Simple C/C++ TBAA"}
+!16 = !DILocation(line: 2, column: 13, scope: !7)
--- /dev/null
+; 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
+; Source code:
+; extern __attribute__((section(".BPF.patchable_externs"))) unsigned long long a;
+; int foo() { return a; }
+; Compilation flag:
+; clang -target bpf -O2 -g -S -emit-llvm test.c
+
+@a = external dso_local local_unnamed_addr global i64, section ".BPF.patchable_externs", align 8
+
+; Function Attrs: norecurse nounwind readonly
+define dso_local i32 @foo() local_unnamed_addr #0 !dbg !7 {
+ %1 = load i64, i64* @a, align 8, !dbg !11, !tbaa !12
+ %2 = trunc i64 %1 to i32, !dbg !11
+; CHECK: r0 = 0 ll
+; CHECK-NEXT: exit
+ ret i32 %2, !dbg !16
+}
+
+; CHECK: .section .BTF,"",@progbits
+; CHECK-NEXT: .short 60319 # 0xeb9f
+; CHECK-NEXT: .byte 1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .long 24
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 40
+; CHECK-NEXT: .long 40
+; CHECK-NEXT: .long 54
+; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 1)
+; CHECK-NEXT: .long 218103808 # 0xd000000
+; CHECK-NEXT: .long 2
+; CHECK-NEXT: .long 1 # BTF_KIND_INT(id = 2)
+; CHECK-NEXT: .long 16777216 # 0x1000000
+; CHECK-NEXT: .long 4
+; CHECK-NEXT: .long 16777248 # 0x1000020
+; CHECK-NEXT: .long 5 # BTF_KIND_FUNC(id = 3)
+; CHECK-NEXT: .long 201326592 # 0xc000000
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .byte 0 # string offset=0
+; CHECK-NEXT: .ascii "int" # string offset=1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "foo" # string offset=5
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii ".text" # string offset=9
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .byte 97 # string offset=15
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .ascii "/tmp/home/yhs/work/tests/llvm/test.c" # string offset=17
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .section .BTF.ext,"",@progbits
+; CHECK-NEXT: .short 60319 # 0xeb9f
+; CHECK-NEXT: .byte 1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .long 40
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 20
+; CHECK-NEXT: .long 20
+; CHECK-NEXT: .long 28
+; CHECK-NEXT: .long 48
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 48
+; CHECK-NEXT: .long 20
+; CHECK-NEXT: .long 8 # FuncInfo
+; CHECK-NEXT: .long 9 # FuncInfo section string offset=9
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long .Lfunc_begin0
+; CHECK-NEXT: .long 3
+; CHECK-NEXT: .long 16 # LineInfo
+; CHECK-NEXT: .long 9 # LineInfo section string offset=9
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
+; CHECK-NEXT: .long 17
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 2061 # Line 2 Col 13
+; CHECK-NEXT: .long 8 # ExternReloc
+; CHECK-NEXT: .long 9 # Extern reloc section string offset=9
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
+; CHECK-NEXT: .long 15
+
+attributes #0 = { norecurse nounwind readonly "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "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" }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3, !4, !5}
+!llvm.ident = !{!6}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 8.0.20181009 ", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/llvm")
+!2 = !{}
+!3 = !{i32 2, !"Dwarf Version", i32 4}
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!5 = !{i32 1, !"wchar_size", i32 4}
+!6 = !{!"clang version 8.0.20181009 "}
+!7 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 2, type: !8, isLocal: false, isDefinition: true, scopeLine: 2, isOptimized: true, unit: !0, retainedNodes: !2)
+!8 = !DISubroutineType(types: !9)
+!9 = !{!10}
+!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!11 = !DILocation(line: 2, column: 20, scope: !7)
+!12 = !{!13, !13, i64 0}
+!13 = !{!"long long", !14, i64 0}
+!14 = !{!"omnipotent char", !15, i64 0}
+!15 = !{!"Simple C/C++ TBAA"}
+!16 = !DILocation(line: 2, column: 13, scope: !7)
!11 = !{i32 2, !"Debug Info Version", i32 3}
!12 = !{i32 1, !"wchar_size", i32 4}
!13 = !{!"clang version 9.0.0 (trunk 360739) (llvm/trunk 360747)"}
-!14 = distinct !DISubprogram(name: "test", scope: !3, file: !3, line: 3, type: !15, scopeLine: 3, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !4)
+!14 = distinct !DISubprogram(name: "test", scope: !3, file: !3, line: 3, type: !15, scopeLine: 3, isLocal: false, isDefinition: true, isOptimized: true, unit: !2, retainedNodes: !4)
!15 = !DISubroutineType(types: !16)
!16 = !{!9}
!17 = !DILocation(line: 4, column: 10, scope: !14)