https://github.com/caniko updated https://github.com/llvm/llvm-project/pull/183929
>From 6b1627bad65b4b6dd34b74b57ed918c00ebd00b6 Mon Sep 17 00:00:00 2001 From: "Can H. Tartanoglu" <[email protected]> Date: Sat, 28 Feb 2026 17:32:44 +0100 Subject: [PATCH] [Clang][LLVM] Add -gbtf: target-independent BTF emission for ELF targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split BTFDebug into a target-independent base class (llvm/CodeGen/) and a BPF-specific subclass (BPFBTFDebug) that handles CO-RE relocations, .maps section processing, and BPF instruction lowering. The base BTFDebug class can now emit .BTF and .BTF.ext sections for any ELF target (e.g. x86_64, aarch64), gated on the "BTF" module flag set by Clang's new -gbtf driver option. BTF coexists with DWARF — both consume the same IR debug metadata independently. This enables native BTF emission without the pahole DWARF-to-BTF conversion step currently required for Linux kernel builds. --- clang/include/clang/Basic/DebugOptions.def | 3 + clang/include/clang/Options/Options.td | 4 + clang/lib/CodeGen/CodeGenModule.cpp | 4 + clang/lib/Driver/ToolChains/Clang.cpp | 15 + clang/test/CodeGen/btf-module-flag.c | 11 + clang/test/Driver/gbtf.c | 27 ++ .../BPF => include/llvm/CodeGen}/BTFDebug.h | 58 +-- llvm/include/llvm/IR/Module.h | 3 + llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 8 + .../BPF => CodeGen/AsmPrinter}/BTFDebug.cpp | 382 ++---------------- llvm/lib/CodeGen/AsmPrinter/CMakeLists.txt | 2 + llvm/lib/IR/Module.cpp | 7 + llvm/lib/Target/BPF/BPFAsmPrinter.cpp | 6 +- llvm/lib/Target/BPF/BPFAsmPrinter.h | 4 +- llvm/lib/Target/BPF/BPFBTFDebug.cpp | 313 ++++++++++++++ llvm/lib/Target/BPF/BPFBTFDebug.h | 66 +++ llvm/lib/Target/BPF/CMakeLists.txt | 2 +- llvm/test/CodeGen/X86/BTF/btf-with-dwarf.ll | 33 ++ llvm/test/CodeGen/X86/BTF/enum.ll | 59 +++ llvm/test/CodeGen/X86/BTF/func.ll | 73 ++++ llvm/test/CodeGen/X86/BTF/global-var.ll | 68 ++++ llvm/test/CodeGen/X86/BTF/int.ll | 42 ++ llvm/test/CodeGen/X86/BTF/no-btf-flag.ll | 28 ++ llvm/test/CodeGen/X86/BTF/struct.ll | 70 ++++ 24 files changed, 898 insertions(+), 390 deletions(-) create mode 100644 clang/test/CodeGen/btf-module-flag.c create mode 100644 clang/test/Driver/gbtf.c rename llvm/{lib/Target/BPF => include/llvm/CodeGen}/BTFDebug.h (90%) rename llvm/lib/{Target/BPF => CodeGen/AsmPrinter}/BTFDebug.cpp (74%) create mode 100644 llvm/lib/Target/BPF/BPFBTFDebug.cpp create mode 100644 llvm/lib/Target/BPF/BPFBTFDebug.h create mode 100644 llvm/test/CodeGen/X86/BTF/btf-with-dwarf.ll create mode 100644 llvm/test/CodeGen/X86/BTF/enum.ll create mode 100644 llvm/test/CodeGen/X86/BTF/func.ll create mode 100644 llvm/test/CodeGen/X86/BTF/global-var.ll create mode 100644 llvm/test/CodeGen/X86/BTF/int.ll create mode 100644 llvm/test/CodeGen/X86/BTF/no-btf-flag.ll create mode 100644 llvm/test/CodeGen/X86/BTF/struct.ll diff --git a/clang/include/clang/Basic/DebugOptions.def b/clang/include/clang/Basic/DebugOptions.def index 604e87e615a69..73e1dfb4e4c23 100644 --- a/clang/include/clang/Basic/DebugOptions.def +++ b/clang/include/clang/Basic/DebugOptions.def @@ -116,6 +116,9 @@ VALUE_DEBUGOPT(DwarfVersion, 3, 0, Compatible) /// CodeView and DWARF into the same object. DEBUGOPT(EmitCodeView, 1, 0, Compatible) +/// Whether we should emit BTF debug information. BTF can coexist with DWARF. +DEBUGOPT(EmitBTF, 1, 0, Compatible) + /// Whether to emit the .debug$H section containing hashes of CodeView types. DEBUGOPT(CodeViewGHash, 1, 0, Compatible) diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index 1021d95e4005b..9a2e61d30a870 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -4978,6 +4978,10 @@ def gcodeview : Flag<["-"], "gcodeview">, Group<g_Group>, HelpText<"Generate CodeView debug information">, Visibility<[ClangOption, CC1Option, CC1AsOption, CLOption, DXCOption]>, MarshallingInfoFlag<CodeGenOpts<"EmitCodeView">>; +def gbtf : Flag<["-"], "gbtf">, Group<g_Group>, + HelpText<"Generate BTF debug information">, + Visibility<[ClangOption, CC1Option, CC1AsOption]>, + MarshallingInfoFlag<CodeGenOpts<"EmitBTF">>; defm codeview_ghash : BoolOption<"g", "codeview-ghash", CodeGenOpts<"CodeViewGHash">, DefaultFalse, PosFlag<SetTrue, [], [ClangOption, CC1Option], diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index c31bcabe49016..614c2c31534c1 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1138,6 +1138,10 @@ void CodeGenModule::Release() { // Indicate that we want CodeView in the metadata. getModule().addModuleFlag(llvm::Module::Warning, "CodeView", 1); } + if (CodeGenOpts.EmitBTF) { + // Indicate that we want BTF debug info in the metadata. + getModule().addModuleFlag(llvm::Module::Warning, "BTF", 1); + } if (CodeGenOpts.CodeViewGHash) { getModule().addModuleFlag(llvm::Module::Warning, "CodeViewGHash", 1); } diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 7e544bd1042ea..02dd263e59d7b 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -4480,6 +4480,18 @@ renderDebugOptions(const ToolChain &TC, const Driver &D, const llvm::Triple &T, if (const Arg *A = Args.getLastArg(options::OPT_gcodeview)) EmitCodeView = checkDebugInfoOption(A, Args, D, TC); + bool EmitBTF = false; + if (const Arg *A = Args.getLastArg(options::OPT_gbtf)) + EmitBTF = checkDebugInfoOption(A, Args, D, TC); + + // BTF reads IR debug metadata, which requires DWARF generation. + if (EmitBTF) { + if (DebugInfoKind == llvm::codegenoptions::NoDebugInfo) + DebugInfoKind = llvm::codegenoptions::LimitedDebugInfo; + if (!EmitDwarf) + EmitDwarf = true; + } + // If the user asked for debug info but did not explicitly specify -gcodeview // or -gdwarf, ask the toolchain for the default format. if (!EmitCodeView && !EmitDwarf && @@ -4624,6 +4636,9 @@ renderDebugOptions(const ToolChain &TC, const Driver &D, const llvm::Triple &T, options::OPT_gno_codeview_command_line); } + if (EmitBTF) + CmdArgs.push_back("-gbtf"); + Args.addOptOutFlag(CmdArgs, options::OPT_ginline_line_tables, options::OPT_gno_inline_line_tables); diff --git a/clang/test/CodeGen/btf-module-flag.c b/clang/test/CodeGen/btf-module-flag.c new file mode 100644 index 0000000000000..cf9fdb2c76704 --- /dev/null +++ b/clang/test/CodeGen/btf-module-flag.c @@ -0,0 +1,11 @@ +// Verify that -gbtf sets the "BTF" module flag in LLVM IR. +// +// RUN: %clang -target x86_64-linux-gnu -gbtf -g -S -emit-llvm -o - %s \ +// RUN: | FileCheck %s --check-prefix=BTF +// RUN: %clang -target x86_64-linux-gnu -g -S -emit-llvm -o - %s \ +// RUN: | FileCheck %s --check-prefix=NO-BTF + +// BTF: !{i32 2, !"BTF", i32 1} +// NO-BTF-NOT: !"BTF" + +int main(void) { return 0; } diff --git a/clang/test/Driver/gbtf.c b/clang/test/Driver/gbtf.c new file mode 100644 index 0000000000000..6fd25ceb95d95 --- /dev/null +++ b/clang/test/Driver/gbtf.c @@ -0,0 +1,27 @@ +// Test the -gbtf flag behavior in the Clang driver. +// +// -gbtf forwards to cc1. +// RUN: %clang -target x86_64-linux-gnu -gbtf -### %s 2>&1 \ +// RUN: | FileCheck -check-prefix=BTF %s +// +// -gbtf alone implies debug info (LimitedDebugInfo). +// RUN: %clang -target x86_64-linux-gnu -gbtf -### %s 2>&1 \ +// RUN: | FileCheck -check-prefix=BTF-DEBUGINFO %s +// +// -gbtf with explicit -g also works. +// RUN: %clang -target x86_64-linux-gnu -g -gbtf -### %s 2>&1 \ +// RUN: | FileCheck -check-prefix=BTF %s +// +// Without -gbtf, no -gbtf in cc1 args. +// RUN: %clang -target x86_64-linux-gnu -g -### %s 2>&1 \ +// RUN: | FileCheck -check-prefix=NO-BTF %s +// +// -gbtf works on aarch64 too (any ELF target). +// RUN: %clang -target aarch64-linux-gnu -gbtf -### %s 2>&1 \ +// RUN: | FileCheck -check-prefix=BTF %s + +// BTF: "-gbtf" +// BTF-DEBUGINFO: "-debug-info-kind= +// NO-BTF-NOT: "-gbtf" + +int main(void) { return 0; } diff --git a/llvm/lib/Target/BPF/BTFDebug.h b/llvm/include/llvm/CodeGen/BTFDebug.h similarity index 90% rename from llvm/lib/Target/BPF/BTFDebug.h rename to llvm/include/llvm/CodeGen/BTFDebug.h index 75858fcc8bfde..925af5d2f8eed 100644 --- a/llvm/lib/Target/BPF/BTFDebug.h +++ b/llvm/include/llvm/CodeGen/BTFDebug.h @@ -1,4 +1,4 @@ -//===- BTFDebug.h -----------------------------------------------*- C++ -*-===// +//===- BTFDebug.h - BTF Debug Info Emission --------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,10 +9,15 @@ /// \file /// This file contains support for writing BTF debug info. /// +/// BTF (BPF Type Format) is a compact debug info format originally designed +/// for BPF programs but useful for any ELF target. This target-independent +/// implementation can be used by any backend to emit .BTF and .BTF.ext +/// sections. +/// //===----------------------------------------------------------------------===// -#ifndef LLVM_LIB_TARGET_BPF_BTFDEBUG_H -#define LLVM_LIB_TARGET_BPF_BTFDEBUG_H +#ifndef LLVM_CODEGEN_BTFDEBUG_H +#define LLVM_CODEGEN_BTFDEBUG_H #include "llvm/ADT/StringMap.h" #include "llvm/CodeGen/DebugHandlerBase.h" @@ -53,7 +58,7 @@ class BTFTypeBase { virtual uint32_t getSize() { return BTF::CommonTypeSize; } /// Complete BTF type generation after all related DebugInfo types /// have been visited so their BTF type id's are available - /// for cross referece. + /// for cross reference. virtual void completeType(BTFDebug &BDebug) {} /// Emit types for this BTF type entry. virtual void emitType(MCStreamer &OS); @@ -286,13 +291,17 @@ struct BTFFieldReloc { }; /// Collect and emit BTF information. +/// +/// This is a target-independent base class that handles the core BTF +/// generation from LLVM IR debug metadata. Target-specific backends +/// (e.g., BPF) can subclass this to add features like CO-RE relocations. class BTFDebug : public DebugHandlerBase { +protected: MCStreamer &OS; bool SkipInstruction; 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; @@ -303,11 +312,10 @@ class BTFDebug : public DebugHandlerBase { std::map<std::string, std::unique_ptr<BTFKindDataSec>, std::less<>> DataSecEntries; std::vector<BTFTypeStruct *> StructTypes; - std::map<const GlobalVariable *, std::pair<int64_t, uint32_t>> PatchImms; + std::set<const Function *> ProtoFunctions; std::map<const DICompositeType *, std::vector<std::pair<const DIDerivedType *, BTFTypeDerived *>>> FixupDerivedTypes; - std::set<const Function *>ProtoFunctions; /// Add types to TypeEntries. /// @{ @@ -336,7 +344,6 @@ class BTFDebug : public DebugHandlerBase { void visitEnumType(const DICompositeType *ETy, uint32_t &TypeId); void visitDerivedType(const DIDerivedType *DTy, uint32_t &TypeId, bool CheckPointer, bool SeenPointer); - void visitMapDefType(const DIType *Ty, uint32_t &TypeId); /// @} /// Check whether the type is a forward declaration candidate or not. @@ -351,14 +358,13 @@ class BTFDebug : public DebugHandlerBase { uint32_t Column); /// Generate types and variables for globals. - void processGlobals(bool ProcessingMapDef); + virtual void processGlobals(); - /// Process global variable initializer in pursuit for function - /// pointers. + /// Scan a global variable initializer for function references. void processGlobalInitializer(const Constant *C); - /// Generate types for function prototypes. - void processFuncPrototypes(const Function *); + /// Generate BTF types for extern function prototypes. + void processFuncPrototypes(const Function *F); /// Generate types for decl annotations. void processDeclAnnotations(DINodeArray Annotations, uint32_t BaseTypeId, @@ -368,24 +374,12 @@ class BTFDebug : public DebugHandlerBase { uint32_t processDISubprogram(const DISubprogram *SP, uint32_t ProtoTypeId, uint8_t Scope); - /// Generate BTF type_tag's. If BaseTypeId is nonnegative, the last - /// BTF type_tag in the chain points to BaseTypeId. Otherwise, it points to - /// the base type of DTy. Return the type id of the first BTF type_tag - /// in the chain. If no type_tag's are generated, a negative value - /// is returned. + /// Generate BTF type_tag's. int genBTFTypeTags(const DIDerivedType *DTy, int BaseTypeId); - /// Generate one field relocation record. - void generatePatchImmReloc(const MCSymbol *ORSym, uint32_t RootId, - const GlobalVariable *, bool IsAma); - /// Populating unprocessed type on demand. unsigned populateType(const DIType *Ty); - /// Process global variables referenced by relocation instructions - /// and extern function references. - void processGlobalValue(const MachineOperand &MO); - /// Emit common header of .BTF and .BTF.ext sections. void emitCommonHeader(); @@ -393,7 +387,7 @@ class BTFDebug : public DebugHandlerBase { void emitBTFSection(); /// Emit the .BTF.ext section. - void emitBTFExtSection(); + virtual void emitBTFExtSection(); protected: /// Gather pre-function debug information. @@ -405,8 +399,8 @@ class BTFDebug : public DebugHandlerBase { public: BTFDebug(AsmPrinter *AP); - /// - bool InstLower(const MachineInstr *MI, MCInst &OutMI); + /// Strip DW_TAG_atomic_type wrapper if present. + static const DIType *tryRemoveAtomicType(const DIType *Ty); /// Get the special array index type id. uint32_t getArrayIndexTypeId() { @@ -425,6 +419,12 @@ class BTFDebug : public DebugHandlerBase { return DIToIdMap[Ty]; } + /// Called at the beginning of instruction processing, before line info. + /// Subclasses can override to handle target-specific instruction processing + /// (e.g., CO-RE relocations) that must add strings to the string table + /// before line info strings. + virtual void processBeginInstruction(const MachineInstr *MI) {} + /// Process beginning of an instruction. void beginInstruction(const MachineInstr *MI) override; diff --git a/llvm/include/llvm/IR/Module.h b/llvm/include/llvm/IR/Module.h index 7156a83c9f3cc..dfaf80c9560d4 100644 --- a/llvm/include/llvm/IR/Module.h +++ b/llvm/include/llvm/IR/Module.h @@ -908,6 +908,9 @@ class LLVM_ABI Module { /// Returns zero if not present in module. unsigned getCodeViewFlag() const; + /// Returns true if BTF debug info emission is requested via module flags. + bool getBTFFlag() const; + /// @} /// @name Utility functions for querying and setting PIC level /// @{ diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index 083b83567e47f..b6d1136e5277a 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -37,6 +37,7 @@ #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/CodeGen/BasicBlockSectionsProfileReader.h" +#include "llvm/CodeGen/BTFDebug.h" #include "llvm/CodeGen/GCMetadata.h" #include "llvm/CodeGen/GCMetadataPrinter.h" #include "llvm/CodeGen/LazyMachineBlockFrequencyInfo.h" @@ -645,6 +646,13 @@ bool AsmPrinter::doInitialization(Module &M) { Handlers.push_back(std::unique_ptr<DwarfDebug>(DD)); } } + // Emit BTF debug info if requested (via -gbtf). BTF can coexist with + // DWARF — both consume the same IR debug metadata independently. + // Only for ELF targets (BTF uses ELF sections). BPF target handles + // BTF emission through its own AsmPrinter with BPFBTFDebug. + if (M.getBTFFlag() && Target.isOSBinFormatELF() && + !Target.isBPF() && hasDebugInfo()) + Handlers.push_back(std::make_unique<BTFDebug>(this)); } if (M.getNamedMetadata(PseudoProbeDescMetadataName)) diff --git a/llvm/lib/Target/BPF/BTFDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/BTFDebug.cpp similarity index 74% rename from llvm/lib/Target/BPF/BTFDebug.cpp rename to llvm/lib/CodeGen/AsmPrinter/BTFDebug.cpp index 46a1df28b8f1d..2a6879384e022 100644 --- a/llvm/lib/Target/BPF/BTFDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/BTFDebug.cpp @@ -6,19 +6,19 @@ // //===----------------------------------------------------------------------===// // -// This file contains support for writing BTF debug info. +// This file contains the target-independent implementation of BTF debug info +// generation. It handles converting LLVM IR debug metadata into BTF type +// information and emitting .BTF and .BTF.ext ELF sections. // //===----------------------------------------------------------------------===// -#include "BTFDebug.h" -#include "BPF.h" -#include "BPFCORE.h" -#include "MCTargetDesc/BPFMCTargetDesc.h" +#include "llvm/CodeGen/BTFDebug.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/CodeGen/MachineOperand.h" +#include "llvm/IR/Constants.h" #include "llvm/IR/Module.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCObjectFileInfo.h" @@ -29,6 +29,7 @@ #include "llvm/Support/LineIterator.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Target/TargetLoweringObjectFile.h" +#include "llvm/Target/TargetMachine.h" #include <optional> using namespace llvm; @@ -38,12 +39,12 @@ static const char *BTFKindStr[] = { #include "llvm/DebugInfo/BTF/BTF.def" }; -static const DIType *tryRemoveAtomicType(const DIType *Ty) { +const DIType *BTFDebug::tryRemoveAtomicType(const DIType *Ty) { if (!Ty) return Ty; - auto DerivedTy = dyn_cast<DIDerivedType>(Ty); - if (DerivedTy && DerivedTy->getTag() == dwarf::DW_TAG_atomic_type) - return DerivedTy->getBaseType(); + if (auto *DerivedTy = dyn_cast<DIDerivedType>(Ty)) + if (DerivedTy->getTag() == dwarf::DW_TAG_atomic_type) + return DerivedTy->getBaseType(); return Ty; } @@ -101,13 +102,6 @@ void BTFTypeDerived::completeType(BTFDebug &BDebug) { case BTF::BTF_KIND_CONST: case BTF::BTF_KIND_VOLATILE: case BTF::BTF_KIND_RESTRICT: - // Debug info might contain names for these types, but given that we want - // to keep BTF minimal and naming reference types doesn't bring any value - // (what matters is the completeness of the base type), we don't emit them. - // - // Furthermore, the Linux kernel refuses to load BPF programs that contain - // BTF with these types named: - // https://elixir.bootlin.com/linux/v6.17.1/source/kernel/bpf/btf.c#L2586 BTFType.NameOff = 0; break; default: @@ -119,7 +113,7 @@ void BTFTypeDerived::completeType(BTFDebug &BDebug) { return; // The base type for PTR/CONST/VOLATILE could be void. - const DIType *ResolvedType = tryRemoveAtomicType(DTy->getBaseType()); + const DIType *ResolvedType = BTFDebug::tryRemoveAtomicType(DTy->getBaseType()); if (!ResolvedType) { assert((Kind == BTF::BTF_KIND_PTR || Kind == BTF::BTF_KIND_CONST || Kind == BTF::BTF_KIND_VOLATILE) && @@ -322,14 +316,6 @@ void BTFTypeStruct::completeType(BTFDebug &BDebug) { BTFType.NameOff = BDebug.addString(STy->getName()); if (STy->getTag() == dwarf::DW_TAG_variant_part) { - // Variant parts might have a discriminator, which has its own memory - // location, and variants, which share the memory location afterwards. LLVM - // DI doesn't consider discriminator as an element and instead keeps - // it as a separate reference. - // To keep BTF simple, let's represent the structure as an union with - // discriminator as the first element. - // The offsets inside variant types are already handled correctly in the - // DI. const auto *DTy = STy->getDiscriminator(); if (DTy) { struct BTF::BTFMember Discriminator; @@ -359,7 +345,7 @@ void BTFTypeStruct::completeType(BTFDebug &BDebug) { } else { BTFMember.Offset = DDTy->getOffsetInBits(); } - const auto *BaseTy = tryRemoveAtomicType(DDTy->getBaseType()); + const auto *BaseTy = BTFDebug::tryRemoveAtomicType(DDTy->getBaseType()); BTFMember.Type = BDebug.getTypeId(BaseTy); break; } @@ -390,11 +376,6 @@ void BTFTypeStruct::emitType(MCStreamer &OS) { std::string BTFTypeStruct::getName() { return std::string(STy->getName()); } -/// 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 -/// are empty for pointee of function pointer case, and are valid names -/// for subprogram. BTFTypeFuncProto::BTFTypeFuncProto( const DISubroutineType *STy, uint32_t VLen, const std::unordered_map<uint32_t, StringRef> &FuncArgNames) @@ -409,15 +390,13 @@ void BTFTypeFuncProto::completeType(BTFDebug &BDebug) { IsCompleted = true; DITypeArray Elements = STy->getTypeArray(); - auto RetType = tryRemoveAtomicType(Elements[0]); + auto RetType = BTFDebug::tryRemoveAtomicType(Elements[0]); BTFType.Type = RetType ? BDebug.getTypeId(RetType) : 0; BTFType.NameOff = 0; - // For null parameter which is typically the last one - // to represent the vararg, encode the NameOff/Type to be 0. for (unsigned I = 1, N = Elements.size(); I < N; ++I) { struct BTF::BTFParam Param; - auto Element = tryRemoveAtomicType(Elements[I]); + auto Element = BTFDebug::tryRemoveAtomicType(Elements[I]); if (Element) { Param.NameOff = BDebug.addString(FuncArgNames[I]); Param.Type = BDebug.getTypeId(Element); @@ -550,7 +529,7 @@ void BTFTypeTypeTag::completeType(BTFDebug &BDebug) { IsCompleted = true; BTFType.NameOff = BDebug.addString(Tag); if (DTy) { - const DIType *ResolvedType = tryRemoveAtomicType(DTy->getBaseType()); + const DIType *ResolvedType = BTFDebug::tryRemoveAtomicType(DTy->getBaseType()); if (!ResolvedType) BTFType.Type = 0; else @@ -574,8 +553,7 @@ uint32_t BTFStringTable::addString(StringRef S) { BTFDebug::BTFDebug(AsmPrinter *AP) : DebugHandlerBase(AP), OS(*Asm->OutStreamer), SkipInstruction(false), - LineInfoGenerated(false), SecNameOff(0), ArrayIndexTypeId(0), - MapDefNotCollected(true) { + LineInfoGenerated(false), SecNameOff(0), ArrayIndexTypeId(0) { addString("\0"); } @@ -605,8 +583,6 @@ void BTFDebug::visitBasicType(const DIBasicType *BTy, uint32_t &TypeId) { case dwarf::DW_ATE_signed_char: case dwarf::DW_ATE_unsigned: case dwarf::DW_ATE_unsigned_char: - // Create a BTF type instance for this DIBasicType and put it into - // DIToIdMap for cross-type reference check. TypeEntry = std::make_unique<BTFTypeInt>( Encoding, BTy->getSizeInBits(), BTy->getOffsetInBits(), BTy->getName()); break; @@ -631,10 +607,6 @@ void BTFDebug::visitSubroutineType( if (VLen > BTF::MAX_VLEN) return; - // Subprogram has a valid non-zero-length name, and the pointee of - // a function pointer has an empty name. The subprogram type will - // not be added to DIToIdMap as it should not be referenced by - // any other types. auto TypeEntry = std::make_unique<BTFTypeFuncProto>(STy, VLen, FuncArgNames); if (ForSubprog) TypeId = addType(std::move(TypeEntry)); // For subprogram @@ -690,8 +662,6 @@ int BTFDebug::genBTFTypeTags(const DIDerivedType *DTy, int BaseTypeId) { SmallVector<const MDString *, 4> MDStrs; DINodeArray Annots = DTy->getAnnotations(); if (Annots) { - // For type with "int __tag1 __tag2 *p", the MDStrs will have - // content: [__tag1, __tag2]. for (const Metadata *Annotations : Annots->operands()) { const MDNode *MD = cast<MDNode>(Annotations); const MDString *Name = cast<MDString>(MD->getOperand(0)); @@ -704,10 +674,6 @@ int BTFDebug::genBTFTypeTags(const DIDerivedType *DTy, int BaseTypeId) { if (MDStrs.size() == 0) return -1; - // With MDStrs [__tag1, __tag2], the output type chain looks like - // PTR -> __tag2 -> __tag1 -> BaseType - // In the below, we construct BTF types with the order of __tag1, __tag2 - // and PTR. unsigned TmpTypeId; std::unique_ptr<BTFTypeTypeTag> TypeEntry; if (BaseTypeId >= 0) @@ -730,9 +696,6 @@ void BTFDebug::visitStructType(const DICompositeType *CTy, bool IsStruct, uint32_t &TypeId) { const DINodeArray Elements = CTy->getElements(); uint32_t VLen = Elements.size(); - // Variant parts might have a discriminator. LLVM DI doesn't consider it as - // an element and instead keeps it as a separate reference. But we represent - // it as an element in BTF. if (CTy->getTag() == dwarf::DW_TAG_variant_part) { const auto *DTy = CTy->getDiscriminator(); if (DTy) { @@ -801,8 +764,6 @@ void BTFDebug::visitArrayType(const DICompositeType *CTy, uint32_t &TypeId) { auto *CI = dyn_cast<ConstantInt *>(SR->getCount()); int64_t Count = CI->getSExtValue(); - // For struct s { int b; char c[]; }, the c[] will be represented - // as an array with Count = -1. auto TypeEntry = std::make_unique<BTFTypeArray>(ElemTypeId, Count >= 0 ? Count : 0); @@ -833,8 +794,6 @@ void BTFDebug::visitEnumType(const DICompositeType *CTy, uint32_t &TypeId) { bool IsSigned = false; unsigned NumBits = 32; - // No BaseType implies forward declaration in which case a - // BTFTypeEnum with Vlen = 0 is emitted. if (CTy->getBaseType() != nullptr) { const auto *BTy = cast<DIBasicType>(CTy->getBaseType()); IsSigned = BTy->getEncoding() == dwarf::DW_ATE_signed || @@ -850,7 +809,6 @@ void BTFDebug::visitEnumType(const DICompositeType *CTy, uint32_t &TypeId) { auto TypeEntry = std::make_unique<BTFTypeEnum64>(CTy, VLen, IsSigned); TypeId = addType(std::move(TypeEntry), CTy); } - // No need to visit base type as BTF does not encode it. } /// Handle structure/union forward declarations. @@ -868,7 +826,6 @@ void BTFDebug::visitCompositeType(const DICompositeType *CTy, case dwarf::DW_TAG_structure_type: case dwarf::DW_TAG_union_type: case dwarf::DW_TAG_variant_part: - // Handle forward declaration differently as it does not have members. if (CTy->isForwardDecl()) visitFwdDeclType(CTy, Tag == dwarf::DW_TAG_union_type, TypeId); else @@ -905,8 +862,6 @@ void BTFDebug::visitDerivedType(const DIDerivedType *DTy, uint32_t &TypeId, return visitTypeEntry(DTy->getBaseType(), TypeId, CheckPointer, SeenPointer); - /// 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 && !DTy->getAnnotations(); } @@ -915,9 +870,6 @@ void BTFDebug::visitDerivedType(const DIDerivedType *DTy, uint32_t &TypeId, const DIType *Base = DTy->getBaseType(); if (Base) { if (IsForwardDeclCandidate(Base)) { - /// 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 = std::make_unique<BTFTypeDerived>(DTy, Tag, true); auto &Fixup = FixupDerivedTypes[cast<DICompositeType>(Base)]; Fixup.push_back(std::make_pair(DTy, TypeEntry.get())); @@ -957,47 +909,11 @@ void BTFDebug::visitDerivedType(const DIDerivedType *DTy, uint32_t &TypeId, visitTypeEntry(DTy->getBaseType(), TempTypeId, CheckPointer, SeenPointer); } -/// Visit a type entry. CheckPointer is true if the type has -/// one of its predecessors as one struct/union member. SeenPointer -/// is true if CheckPointer is true and one of its predecessors -/// is a pointer. The goal of CheckPointer and SeenPointer is to -/// do pruning for struct/union types so some of these types -/// will not be emitted in BTF and rather forward declarations -/// will be generated. void BTFDebug::visitTypeEntry(const DIType *Ty, uint32_t &TypeId, bool CheckPointer, bool SeenPointer) { if (!Ty || DIToIdMap.find(Ty) != DIToIdMap.end()) { TypeId = DIToIdMap[Ty]; - // To handle the case like the following: - // struct t; - // typedef struct t _t; - // struct s1 { _t *c; }; - // int test1(struct s1 *arg) { ... } - // - // struct t { int a; int b; }; - // struct s2 { _t c; } - // int test2(struct s2 *arg) { ... } - // - // During traversing test1() argument, "_t" is recorded - // in DIToIdMap and a forward declaration fixup is created - // for "struct t" to avoid pointee type traversal. - // - // During traversing test2() argument, even if we see "_t" is - // already defined, we should keep moving to eventually - // bring in types for "struct t". Otherwise, the "struct s2" - // definition won't be correct. - // - // In the above, we have following debuginfo: - // {ptr, struct_member} -> typedef -> struct - // and BTF type for 'typedef' is generated while 'struct' may - // be in FixUp. But let us generalize the above to handle - // {different types} -> [various derived types]+ -> another type. - // For example, - // {func_param, struct_member} -> const -> ptr -> volatile -> struct - // We will traverse const/ptr/volatile which already have corresponding - // BTF types and generate type for 'struct' which might be in Fixup - // state. if (Ty && (!CheckPointer || !SeenPointer)) { if (const auto *DTy = dyn_cast<DIDerivedType>(Ty)) { while (DTy) { @@ -1043,55 +959,6 @@ void BTFDebug::visitTypeEntry(const DIType *Ty) { 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; - } - - uint32_t TmpId; - switch (Ty->getTag()) { - case dwarf::DW_TAG_typedef: - case dwarf::DW_TAG_const_type: - case dwarf::DW_TAG_volatile_type: - case dwarf::DW_TAG_restrict_type: - case dwarf::DW_TAG_pointer_type: - visitMapDefType(dyn_cast<DIDerivedType>(Ty)->getBaseType(), TmpId); - break; - case dwarf::DW_TAG_array_type: - // Visit nested map array and jump to the element type - visitMapDefType(dyn_cast<DICompositeType>(Ty)->getBaseType(), TmpId); - break; - case dwarf::DW_TAG_structure_type: { - // Visit all struct members to ensure their types are visited. - const auto *CTy = cast<DICompositeType>(Ty); - const DINodeArray Elements = CTy->getElements(); - for (const auto *Element : Elements) { - const auto *MemberType = cast<DIDerivedType>(Element); - const DIType *MemberBaseType = MemberType->getBaseType(); - // If the member is a composite type, that may indicate the currently - // visited composite type is a wrapper, and the member represents the - // actual map definition. - // In that case, visit the member with `visitMapDefType` instead of - // `visitTypeEntry`, treating it specifically as a map definition rather - // than as a regular composite type. - const auto *MemberCTy = dyn_cast<DICompositeType>(MemberBaseType); - if (MemberCTy) { - visitMapDefType(MemberBaseType, TmpId); - } else { - visitTypeEntry(MemberBaseType); - } - } - break; - } - default: - break; - } - - // Visit this type, struct or a const/typedef/volatile/restrict type - visitTypeEntry(Ty, TypeId, false, false); -} - /// Read file contents from the actual file or from the source std::string BTFDebug::populateFileContent(const DIFile *File) { std::string FileName; @@ -1296,30 +1163,6 @@ 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. @@ -1366,8 +1209,6 @@ void BTFDebug::endFunctionImpl(const MachineFunction *MF) { SecNameOff = 0; } -/// On-demand populate types as requested from abstract member -/// accessing or preserve debuginfo type. unsigned BTFDebug::populateType(const DIType *Ty) { unsigned Id; visitTypeEntry(Ty, Id, false, false); @@ -1376,62 +1217,6 @@ unsigned BTFDebug::populateType(const DIType *Ty) { return Id; } -/// Generate a struct member field relocation. -void BTFDebug::generatePatchImmReloc(const MCSymbol *ORSym, uint32_t RootId, - const GlobalVariable *GVar, bool IsAma) { - BTFFieldReloc FieldReloc; - FieldReloc.Label = ORSym; - FieldReloc.TypeID = RootId; - - StringRef AccessPattern = GVar->getName(); - size_t FirstDollar = AccessPattern.find_first_of('$'); - if (IsAma) { - size_t FirstColon = AccessPattern.find_first_of(':'); - size_t SecondColon = AccessPattern.find_first_of(':', FirstColon + 1); - StringRef IndexPattern = AccessPattern.substr(FirstDollar + 1); - StringRef RelocKindStr = AccessPattern.substr(FirstColon + 1, - SecondColon - FirstColon); - StringRef PatchImmStr = AccessPattern.substr(SecondColon + 1, - FirstDollar - SecondColon); - - FieldReloc.OffsetNameOff = addString(IndexPattern); - FieldReloc.RelocKind = std::stoull(std::string(RelocKindStr)); - PatchImms[GVar] = std::make_pair(std::stoll(std::string(PatchImmStr)), - FieldReloc.RelocKind); - } else { - StringRef RelocStr = AccessPattern.substr(FirstDollar + 1); - FieldReloc.OffsetNameOff = addString("0"); - FieldReloc.RelocKind = std::stoull(std::string(RelocStr)); - PatchImms[GVar] = std::make_pair(RootId, FieldReloc.RelocKind); - } - FieldRelocTable[SecNameOff].push_back(FieldReloc); -} - -void BTFDebug::processGlobalValue(const MachineOperand &MO) { - // check whether this is a candidate or not - if (MO.isGlobal()) { - const GlobalValue *GVal = MO.getGlobal(); - auto *GVar = dyn_cast<GlobalVariable>(GVal); - if (!GVar) { - // Not a global variable. Maybe an extern function reference. - processFuncPrototypes(dyn_cast<Function>(GVal)); - return; - } - - if (!GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr) && - !GVar->hasAttribute(BPFCoreSharedInfo::TypeIdAttr)) - return; - - MCSymbol *ORSym = OS.getContext().createTempSymbol(); - OS.emitLabel(ORSym); - - MDNode *MDN = GVar->getMetadata(LLVMContext::MD_preserve_access_index); - uint32_t RootId = populateType(dyn_cast<DIType>(MDN)); - generatePatchImmReloc(ORSym, RootId, GVar, - GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr)); - } -} - void BTFDebug::beginInstruction(const MachineInstr *MI) { DebugHandlerBase::beginInstruction(MI); @@ -1456,36 +1241,11 @@ void BTFDebug::beginInstruction(const MachineInstr *MI) { } } - if (MI->getOpcode() == BPF::LD_imm64) { - // If the insn is "r2 = LD_imm64 @<an AmaAttr global>", - // add this insn into the .BTF.ext FieldReloc subsection. - // Relocation looks like: - // . SecName: - // . InstOffset - // . TypeID - // . OffSetNameOff - // . RelocType - // Later, the insn is replaced with "r2 = <offset>" - // where "<offset>" equals to the offset based on current - // type definitions. - // - // If the insn is "r2 = LD_imm64 @<an TypeIdAttr global>", - // The LD_imm64 result will be replaced with a btf type id. - processGlobalValue(MI->getOperand(1)); - } else if (MI->getOpcode() == BPF::CORE_LD64 || - MI->getOpcode() == BPF::CORE_LD32 || - MI->getOpcode() == BPF::CORE_ST || - MI->getOpcode() == BPF::CORE_SHIFT) { - // relocation insn is a load, store or shift insn. - processGlobalValue(MI->getOperand(3)); - } else if (MI->getOpcode() == BPF::JAL) { - // check extern function references - const MachineOperand &MO = MI->getOperand(0); - if (MO.isGlobal()) { - processFuncPrototypes(dyn_cast<Function>(MO.getGlobal())); - } - } + // Let subclasses process target-specific instructions before line info, + // to preserve string table ordering (type names before file paths). + processBeginInstruction(MI); + // Line info tracking. if (!CurMI) // no debug info return; @@ -1518,11 +1278,9 @@ void BTFDebug::beginInstruction(const MachineInstr *MI) { PrevInstLoc = DL; } -void BTFDebug::processGlobals(bool ProcessingMapDef) { - // Collect all types referenced by globals. +void BTFDebug::processGlobals() { const Module *M = MMI->getModule(); for (const GlobalVariable &Global : M->globals()) { - // Decide the section name. StringRef SecName; std::optional<SectionKind> GVKind; @@ -1539,7 +1297,8 @@ void BTFDebug::processGlobals(bool ProcessingMapDef) { SecName = Sec->getName(); } - if (ProcessingMapDef != SecName.starts_with(".maps")) + // Skip .maps sections (BPF-specific). + if (SecName.starts_with(".maps")) continue; // Create a .rodata datasec if the global variable is an initialized @@ -1565,22 +1324,12 @@ void BTFDebug::processGlobals(bool ProcessingMapDef) { DIGlobalVariable *DIGlobal = nullptr; for (auto *GVE : GVs) { DIGlobal = GVE->getVariable(); - if (SecName.starts_with(".maps")) - visitMapDefType(DIGlobal->getType(), GVTypeId); - else { - const DIType *Ty = tryRemoveAtomicType(DIGlobal->getType()); - visitTypeEntry(Ty, GVTypeId, false, false); - } + const DIType *Ty = tryRemoveAtomicType(DIGlobal->getType()); + visitTypeEntry(Ty, GVTypeId, false, false); break; } // Only support the following globals: - // . static variables - // . non-static weak or non-weak global variables - // . weak or non-weak extern global variables - // Whether DataSec is readonly or not can be found from corresponding ELF - // section flags. Whether a BTF_KIND_VAR is a weak symbol or not - // can be found from the corresponding ELF symbol table. auto Linkage = Global.getLinkage(); if (Linkage != GlobalValue::InternalLinkage && Linkage != GlobalValue::ExternalLinkage && @@ -1604,16 +1353,13 @@ void BTFDebug::processGlobals(bool ProcessingMapDef) { processDeclAnnotations(DIGlobal->getAnnotations(), VarId, -1); - // An empty SecName means an extern variable without section attribute. if (SecName.empty()) continue; - // Find or create a DataSec auto [It, Inserted] = DataSecEntries.try_emplace(std::string(SecName)); if (Inserted) It->second = std::make_unique<BTFKindDataSec>(Asm, std::string(SecName)); - // Calculate symbol size const DataLayout &DL = Global.getDataLayout(); uint32_t Size = Global.getGlobalSize(DL); @@ -1624,17 +1370,6 @@ void BTFDebug::processGlobals(bool ProcessingMapDef) { } } -/// Process global variable initializer in pursuit for function -/// pointers. Add discovered (extern) functions to BTF. Some (extern) -/// functions might have been missed otherwise. Every symbol needs BTF -/// info when linking with bpftool. Primary use case: "static" -/// initialization of BPF maps. -/// -/// struct { -/// __uint(type, BPF_MAP_TYPE_PROG_ARRAY); -/// ... -/// } prog_map SEC(".maps") = { .values = { extern_func } }; -/// void BTFDebug::processGlobalInitializer(const Constant *C) { if (auto *Fn = dyn_cast<Function>(C)) processFuncPrototypes(Fn); @@ -1644,54 +1379,6 @@ void BTFDebug::processGlobalInitializer(const Constant *C) { } } -/// 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) { - if (!GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr) && - !GVar->hasAttribute(BPFCoreSharedInfo::TypeIdAttr)) - return false; - - // Emit "mov ri, <imm>" - auto [Imm, Reloc] = PatchImms[GVar]; - if (Reloc == BTF::ENUM_VALUE_EXISTENCE || Reloc == BTF::ENUM_VALUE || - Reloc == BTF::BTF_TYPE_ID_LOCAL || Reloc == BTF::BTF_TYPE_ID_REMOTE) - OutMI.setOpcode(BPF::LD_imm64); - else - OutMI.setOpcode(BPF::MOV_ri); - OutMI.addOperand(MCOperand::createReg(MI->getOperand(0).getReg())); - OutMI.addOperand(MCOperand::createImm(Imm)); - return true; - } - } - } else if (MI->getOpcode() == BPF::CORE_LD64 || - MI->getOpcode() == BPF::CORE_LD32 || - MI->getOpcode() == BPF::CORE_ST || - MI->getOpcode() == BPF::CORE_SHIFT) { - const MachineOperand &MO = MI->getOperand(3); - if (MO.isGlobal()) { - const GlobalValue *GVal = MO.getGlobal(); - auto *GVar = dyn_cast<GlobalVariable>(GVal); - if (GVar && GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr)) { - uint32_t Imm = PatchImms[GVar].first; - OutMI.setOpcode(MI->getOperand(1).getImm()); - if (MI->getOperand(0).isImm()) - OutMI.addOperand(MCOperand::createImm(MI->getOperand(0).getImm())); - else - OutMI.addOperand(MCOperand::createReg(MI->getOperand(0).getReg())); - OutMI.addOperand(MCOperand::createReg(MI->getOperand(2).getReg())); - OutMI.addOperand(MCOperand::createImm(Imm)); - return true; - } - } - } - return false; -} - void BTFDebug::processFuncPrototypes(const Function *F) { if (!F) return; @@ -1722,26 +1409,11 @@ void BTFDebug::processFuncPrototypes(const Function *F) { } 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); - - // In case that BPF_TRAP usage is removed during machine-level optimization, - // generate btf for BPF_TRAP function here. - for (const Function &F : *MMI->getModule()) { - if (F.getName() == BPF_TRAP) - processFuncPrototypes(&F); - } + processGlobals(); for (auto &DataSec : DataSecEntries) addType(std::move(DataSec.second)); - // Fixups for (auto &Fixup : FixupDerivedTypes) { const DICompositeType *CTy = Fixup.first; StringRef TypeName = CTy->getName(); @@ -1773,7 +1445,7 @@ void BTFDebug::endModule() { } } - // Complete BTF type cross refereences. + // Complete BTF type cross references. for (const auto &TypeEntry : TypeEntries) TypeEntry->completeType(*this); diff --git a/llvm/lib/CodeGen/AsmPrinter/CMakeLists.txt b/llvm/lib/CodeGen/AsmPrinter/CMakeLists.txt index 58fab8b634ad6..00fccd3113db1 100644 --- a/llvm/lib/CodeGen/AsmPrinter/CMakeLists.txt +++ b/llvm/lib/CodeGen/AsmPrinter/CMakeLists.txt @@ -6,6 +6,7 @@ add_llvm_component_library(LLVMAsmPrinter AsmPrinter.cpp AsmPrinterDwarf.cpp AsmPrinterInlineAsm.cpp + BTFDebug.cpp DbgEntityHistoryCalculator.cpp DebugHandlerBase.cpp DebugLocStream.cpp @@ -36,6 +37,7 @@ add_llvm_component_library(LLVMAsmPrinter CodeGen CodeGenTypes Core + DebugInfoBTF DebugInfoCodeView DebugInfoDWARF DebugInfoDWARFLowLevel diff --git a/llvm/lib/IR/Module.cpp b/llvm/lib/IR/Module.cpp index 11dc68e0e4751..bd4d0c343e35c 100644 --- a/llvm/lib/IR/Module.cpp +++ b/llvm/lib/IR/Module.cpp @@ -611,6 +611,13 @@ unsigned Module::getCodeViewFlag() const { return cast<ConstantInt>(Val->getValue())->getZExtValue(); } +bool Module::getBTFFlag() const { + auto *Val = cast_or_null<ConstantAsMetadata>(getModuleFlag("BTF")); + if (!Val) + return false; + return cast<ConstantInt>(Val->getValue())->getZExtValue() != 0; +} + unsigned Module::getInstructionCount() const { unsigned NumInstrs = 0; for (const Function &F : FunctionList) diff --git a/llvm/lib/Target/BPF/BPFAsmPrinter.cpp b/llvm/lib/Target/BPF/BPFAsmPrinter.cpp index abe081c0c76fd..b67db52ab1542 100644 --- a/llvm/lib/Target/BPF/BPFAsmPrinter.cpp +++ b/llvm/lib/Target/BPF/BPFAsmPrinter.cpp @@ -15,7 +15,7 @@ #include "BPF.h" #include "BPFInstrInfo.h" #include "BPFMCInstLower.h" -#include "BTFDebug.h" +#include "BPFBTFDebug.h" #include "MCTargetDesc/BPFInstPrinter.h" #include "TargetInfo/BPFTargetInfo.h" #include "llvm/BinaryFormat/ELF.h" @@ -45,8 +45,8 @@ bool BPFAsmPrinter::doInitialization(Module &M) { // Only emit BTF when debuginfo available. if (MAI->doesSupportDebugInformation() && !M.debug_compile_units().empty()) { - BTF = new BTFDebug(this); - Handlers.push_back(std::unique_ptr<BTFDebug>(BTF)); + BTF = new BPFBTFDebug(this); + Handlers.push_back(std::unique_ptr<BPFBTFDebug>(BTF)); } return false; diff --git a/llvm/lib/Target/BPF/BPFAsmPrinter.h b/llvm/lib/Target/BPF/BPFAsmPrinter.h index 75a1d7ed9f884..2a61ac6ea731f 100644 --- a/llvm/lib/Target/BPF/BPFAsmPrinter.h +++ b/llvm/lib/Target/BPF/BPFAsmPrinter.h @@ -10,7 +10,7 @@ #define LLVM_LIB_TARGET_BPF_BPFASMPRINTER_H #include "BPFTargetMachine.h" -#include "BTFDebug.h" +#include "BPFBTFDebug.h" #include "llvm/CodeGen/AsmPrinter.h" namespace llvm { @@ -37,7 +37,7 @@ class BPFAsmPrinter : public AsmPrinter { static char ID; private: - BTFDebug *BTF; + BPFBTFDebug *BTF; TargetMachine &TM; bool SawTrapCall = false; diff --git a/llvm/lib/Target/BPF/BPFBTFDebug.cpp b/llvm/lib/Target/BPF/BPFBTFDebug.cpp new file mode 100644 index 0000000000000..626c7756b04a0 --- /dev/null +++ b/llvm/lib/Target/BPF/BPFBTFDebug.cpp @@ -0,0 +1,313 @@ +//===- BPFBTFDebug.cpp - BPF-specific BTF Generator -----------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// BPF-specific BTF debug info generation. Handles CO-RE relocations, +// .maps section processing, and BPF instruction lowering. +// +//===----------------------------------------------------------------------===// + +#include "BPFBTFDebug.h" +#include "BPF.h" +#include "BPFCORE.h" +#include "MCTargetDesc/BPFMCTargetDesc.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/IR/Module.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/Target/TargetLoweringObjectFile.h" + +using namespace llvm; + +BPFBTFDebug::BPFBTFDebug(AsmPrinter *AP) + : BTFDebug(AP), MapDefNotCollected(true) {} + +void BPFBTFDebug::visitMapDefType(const DIType *Ty, uint32_t &TypeId) { + if (!Ty || DIToIdMap.find(Ty) != DIToIdMap.end()) { + TypeId = DIToIdMap[Ty]; + return; + } + + uint32_t TmpId; + switch (Ty->getTag()) { + case dwarf::DW_TAG_typedef: + case dwarf::DW_TAG_const_type: + case dwarf::DW_TAG_volatile_type: + case dwarf::DW_TAG_restrict_type: + case dwarf::DW_TAG_pointer_type: + visitMapDefType(dyn_cast<DIDerivedType>(Ty)->getBaseType(), TmpId); + break; + case dwarf::DW_TAG_array_type: + // Visit nested map array and jump to the element type + visitMapDefType(dyn_cast<DICompositeType>(Ty)->getBaseType(), TmpId); + break; + case dwarf::DW_TAG_structure_type: { + const DICompositeType *CTy = dyn_cast<DICompositeType>(Ty); + const DINodeArray Elements = CTy->getElements(); + for (const auto *Element : Elements) { + const auto *MemberType = cast<DIDerivedType>(Element); + const DIType *MemberBaseType = + tryRemoveAtomicType(MemberType->getBaseType()); + + // Check if the visited composite type is a wrapper, and the member + // represents the actual map definition. + const auto *MemberCTy = dyn_cast<DICompositeType>(MemberBaseType); + if (MemberCTy) { + visitMapDefType(MemberBaseType, TmpId); + } else { + visitTypeEntry(MemberBaseType); + } + } + break; + } + default: + break; + } + + // Visit this type, struct or a const/typedef/volatile/restrict type + visitTypeEntry(Ty, TypeId, false, false); +} + +void BPFBTFDebug::processMapDefGlobals() { + const Module *M = MMI->getModule(); + for (const GlobalVariable &Global : M->globals()) { + StringRef SecName; + std::optional<SectionKind> GVKind; + + if (!Global.isDeclarationForLinker()) + GVKind = TargetLoweringObjectFile::getKindForGlobal(&Global, Asm->TM); + + if (Global.isDeclarationForLinker()) + SecName = Global.hasSection() ? Global.getSection() : ""; + else if (GVKind->isCommon()) + SecName = ".bss"; + else { + TargetLoweringObjectFile *TLOF = Asm->TM.getObjFileLowering(); + MCSection *Sec = TLOF->SectionForGlobal(&Global, Asm->TM); + SecName = Sec->getName(); + } + + // Only process .maps globals in this pass. + if (!SecName.starts_with(".maps")) + continue; + + SmallVector<DIGlobalVariableExpression *, 1> GVs; + Global.getDebugInfo(GVs); + if (GVs.size() == 0) + continue; + + uint32_t GVTypeId = 0; + DIGlobalVariable *DIGlobal = nullptr; + for (auto *GVE : GVs) { + DIGlobal = GVE->getVariable(); + visitMapDefType(DIGlobal->getType(), GVTypeId); + break; + } + + auto Linkage = Global.getLinkage(); + if (Linkage != GlobalValue::InternalLinkage && + Linkage != GlobalValue::ExternalLinkage && + Linkage != GlobalValue::WeakAnyLinkage && + Linkage != GlobalValue::WeakODRLinkage && + Linkage != GlobalValue::ExternalWeakLinkage) + continue; + + uint32_t GVarInfo; + if (Linkage == GlobalValue::InternalLinkage) { + GVarInfo = BTF::VAR_STATIC; + } else if (Global.hasInitializer()) { + GVarInfo = BTF::VAR_GLOBAL_ALLOCATED; + } else { + GVarInfo = BTF::VAR_GLOBAL_EXTERNAL; + } + + auto VarEntry = + std::make_unique<BTFKindVar>(Global.getName(), GVTypeId, GVarInfo); + uint32_t VarId = addType(std::move(VarEntry)); + + processDeclAnnotations(DIGlobal->getAnnotations(), VarId, -1); + + if (SecName.empty()) + continue; + + auto [It, Inserted] = DataSecEntries.try_emplace(std::string(SecName)); + if (Inserted) + It->second = std::make_unique<BTFKindDataSec>(Asm, std::string(SecName)); + + const DataLayout &DL = Global.getDataLayout(); + uint32_t Size = Global.getGlobalSize(DL); + + It->second->addDataSecEntry(VarId, Asm->getSymbol(&Global), Size); + + if (Global.hasInitializer()) + processGlobalInitializer(Global.getInitializer()); + } +} + +void BPFBTFDebug::processGlobals() { + // First process .maps globals if not yet done. + if (MapDefNotCollected) { + processMapDefGlobals(); + MapDefNotCollected = false; + } + // Then process all non-.maps globals via the base class. + BTFDebug::processGlobals(); +} + +void BPFBTFDebug::generatePatchImmReloc(const MCSymbol *ORSym, uint32_t RootId, + const GlobalVariable *GVar, + bool IsAma) { + BTFFieldReloc FieldReloc; + FieldReloc.Label = ORSym; + FieldReloc.TypeID = RootId; + + StringRef AccessPattern = GVar->getName(); + size_t FirstDollar = AccessPattern.find_first_of('$'); + if (IsAma) { + size_t FirstColon = AccessPattern.find_first_of(':'); + size_t SecondColon = AccessPattern.find_first_of(':', FirstColon + 1); + StringRef IndexPattern = AccessPattern.substr(FirstDollar + 1); + StringRef RelocKindStr = AccessPattern.substr(FirstColon + 1, + SecondColon - FirstColon); + StringRef PatchImmStr = AccessPattern.substr(SecondColon + 1, + FirstDollar - SecondColon); + + FieldReloc.OffsetNameOff = addString(IndexPattern); + FieldReloc.RelocKind = std::stoull(std::string(RelocKindStr)); + PatchImms[GVar] = std::make_pair(std::stoll(std::string(PatchImmStr)), + FieldReloc.RelocKind); + } else { + StringRef RelocStr = AccessPattern.substr(FirstDollar + 1); + FieldReloc.OffsetNameOff = addString("0"); + FieldReloc.RelocKind = std::stoull(std::string(RelocStr)); + PatchImms[GVar] = std::make_pair(RootId, FieldReloc.RelocKind); + } + FieldRelocTable[SecNameOff].push_back(FieldReloc); +} + +void BPFBTFDebug::processGlobalValue(const MachineOperand &MO) { + if (MO.isGlobal()) { + const GlobalValue *GVal = MO.getGlobal(); + auto *GVar = dyn_cast<GlobalVariable>(GVal); + if (!GVar) { + processFuncPrototypes(dyn_cast<Function>(GVal)); + return; + } + + if (!GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr) && + !GVar->hasAttribute(BPFCoreSharedInfo::TypeIdAttr)) + return; + + MCSymbol *ORSym = OS.getContext().createTempSymbol(); + OS.emitLabel(ORSym); + + MDNode *MDN = GVar->getMetadata(LLVMContext::MD_preserve_access_index); + uint32_t RootId = populateType(dyn_cast<DIType>(MDN)); + generatePatchImmReloc(ORSym, RootId, GVar, + GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr)); + } +} + +void BPFBTFDebug::beginFunctionImpl(const MachineFunction *MF) { + if (MapDefNotCollected) { + processMapDefGlobals(); + MapDefNotCollected = false; + } + + BTFDebug::beginFunctionImpl(MF); +} + +void BPFBTFDebug::processBeginInstruction(const MachineInstr *MI) { + // Handle CO-RE and extern function relocations. + if (MI->getOpcode() == BPF::LD_imm64) { + processGlobalValue(MI->getOperand(1)); + } else if (MI->getOpcode() == BPF::CORE_LD64 || + MI->getOpcode() == BPF::CORE_LD32 || + MI->getOpcode() == BPF::CORE_ST || + MI->getOpcode() == BPF::CORE_SHIFT) { + processGlobalValue(MI->getOperand(3)); + } else if (MI->getOpcode() == BPF::JAL) { + const MachineOperand &MO = MI->getOperand(0); + if (MO.isGlobal()) { + processFuncPrototypes(dyn_cast<Function>(MO.getGlobal())); + } + } +} + +bool BPFBTFDebug::InstLower(const MachineInstr *MI, MCInst &OutMI) { + if (MI->getOpcode() == BPF::LD_imm64) { + const MachineOperand &MO = MI->getOperand(1); + if (MO.isGlobal()) { + const GlobalVariable *GVar = dyn_cast<GlobalVariable>(MO.getGlobal()); + if (GVar) { + // Emit "mov ri, <imm>" for patched insns. + auto IMGIt = PatchImms.find(GVar); + if (IMGIt != PatchImms.end()) { + auto Imm = IMGIt->second; + auto Reloc = Imm.second; + if (Reloc == BTF::ENUM_VALUE_EXISTENCE || Reloc == BTF::ENUM_VALUE || + Reloc == BTF::BTF_TYPE_ID_LOCAL || Reloc == BTF::BTF_TYPE_ID_REMOTE) + OutMI.setOpcode(BPF::LD_imm64); + else + OutMI.setOpcode(BPF::MOV_ri); + OutMI.addOperand(MCOperand::createReg(MI->getOperand(0).getReg())); + OutMI.addOperand(MCOperand::createImm(Imm.first)); + return true; + } + } + } + } else if (MI->getOpcode() == BPF::CORE_LD64 || + MI->getOpcode() == BPF::CORE_LD32 || + MI->getOpcode() == BPF::CORE_ST || + MI->getOpcode() == BPF::CORE_SHIFT) { + const MachineOperand &MO = MI->getOperand(3); + if (MO.isGlobal()) { + const GlobalVariable *GVar = dyn_cast<GlobalVariable>(MO.getGlobal()); + if (GVar) { + auto IMGIt = PatchImms.find(GVar); + if (IMGIt != PatchImms.end()) { + uint32_t Imm = IMGIt->second.first; + OutMI.setOpcode(MI->getOperand(2).getImm()); + if (MI->getOpcode() == BPF::CORE_ST) { + OutMI.addOperand( + MCOperand::createReg(MI->getOperand(0).getReg())); + OutMI.addOperand(MCOperand::createImm(Imm)); + OutMI.addOperand( + MCOperand::createReg(MI->getOperand(1).getReg())); + } else if (MI->getOpcode() == BPF::CORE_SHIFT) { + OutMI.addOperand( + MCOperand::createReg(MI->getOperand(0).getReg())); + OutMI.addOperand(MCOperand::createImm(Imm)); + } else { + OutMI.addOperand( + MCOperand::createReg(MI->getOperand(0).getReg())); + OutMI.addOperand(MCOperand::createImm(Imm)); + OutMI.addOperand( + MCOperand::createReg(MI->getOperand(1).getReg())); + } + return true; + } + } + } + } + return false; +} + +void BPFBTFDebug::endModule() { + // BPF_TRAP may lose its call site during MachineIR optimization, + // so ensure its prototype is always emitted. + for (const Function &F : *MMI->getModule()) { + if (F.getName() == BPF_TRAP) + processFuncPrototypes(&F); + } + + BTFDebug::endModule(); +} diff --git a/llvm/lib/Target/BPF/BPFBTFDebug.h b/llvm/lib/Target/BPF/BPFBTFDebug.h new file mode 100644 index 0000000000000..996b6a2debf6d --- /dev/null +++ b/llvm/lib/Target/BPF/BPFBTFDebug.h @@ -0,0 +1,66 @@ +//===- BPFBTFDebug.h - BPF-specific BTF Debug Info ---------------*- 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// BPF-specific BTF debug info emission. Extends the target-independent +/// BTFDebug with CO-RE relocations, .maps section handling, and BPF +/// instruction processing. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_BPF_BPFBTFDEBUG_H +#define LLVM_LIB_TARGET_BPF_BPFBTFDEBUG_H + +#include "llvm/CodeGen/BTFDebug.h" + +namespace llvm { + +class MCInst; + +/// BPF-specific BTF debug handler. +/// +/// Extends the target-independent BTFDebug with: +/// - CO-RE (Compile-Once Run-Everywhere) field relocations +/// - .maps section handling for BPF map definitions +/// - BPF instruction lowering for patchable instructions +class BPFBTFDebug : public BTFDebug { + bool MapDefNotCollected; + std::map<const GlobalVariable *, std::pair<int64_t, uint32_t>> PatchImms; + + /// Visit a .maps type definition. + void visitMapDefType(const DIType *Ty, uint32_t &TypeId); + + /// Process global variable references from BPF instructions (CO-RE). + void processGlobalValue(const MachineOperand &MO); + + /// Generate a field relocation record for CO-RE. + void generatePatchImmReloc(const MCSymbol *ORSym, uint32_t RootId, + const GlobalVariable *GVar, bool IsAma); + + /// Process .maps globals separately. + void processMapDefGlobals(); + + /// Process all globals including .maps handling. + void processGlobals() override; + +protected: + void beginFunctionImpl(const MachineFunction *MF) override; + +public: + BPFBTFDebug(AsmPrinter *AP); + + /// Emit proper patchable instructions (CO-RE lowering). + bool InstLower(const MachineInstr *MI, MCInst &OutMI); + + void processBeginInstruction(const MachineInstr *MI) override; + void endModule() override; +}; + +} // end namespace llvm + +#endif diff --git a/llvm/lib/Target/BPF/CMakeLists.txt b/llvm/lib/Target/BPF/CMakeLists.txt index fa539a0a7b806..28ba3bbdc4fc6 100644 --- a/llvm/lib/Target/BPF/CMakeLists.txt +++ b/llvm/lib/Target/BPF/CMakeLists.txt @@ -26,6 +26,7 @@ add_llvm_target(BPFCodeGen BPFAdjustOpt.cpp BPFAsmPrinter.cpp BPFASpaceCastSimplifyPass.cpp + BPFBTFDebug.cpp BPFCheckAndAdjustIR.cpp BPFFrameLowering.cpp BPFInstrInfo.cpp @@ -43,7 +44,6 @@ add_llvm_target(BPFCodeGen BPFMIPeephole.cpp BPFMIChecking.cpp BPFMISimplifyPatchable.cpp - BTFDebug.cpp LINK_COMPONENTS Analysis diff --git a/llvm/test/CodeGen/X86/BTF/btf-with-dwarf.ll b/llvm/test/CodeGen/X86/BTF/btf-with-dwarf.ll new file mode 100644 index 0000000000000..f7dba88ae27a2 --- /dev/null +++ b/llvm/test/CodeGen/X86/BTF/btf-with-dwarf.ll @@ -0,0 +1,33 @@ +; RUN: llc -mtriple=x86_64-linux-gnu -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s + +; Verify that BTF and DWARF coexist: both .BTF and .debug_info sections +; are emitted when the BTF module flag is set alongside DWARF metadata. +; +; Source: +; void f1(void) {} +; Compilation flag: +; clang -target x86_64-linux-gnu -g -gbtf -S -emit-llvm t.c + +define dso_local void @f1() !dbg !7 { + ret void, !dbg !10 +} + +; Both DWARF and BTF sections must be present. +; CHECK-DAG: .section .debug_info +; CHECK-DAG: .section .BTF,"",@progbits +; CHECK-DAG: .section .BTF.ext,"",@progbits + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5, !6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) +!1 = !DIFile(filename: "t.c", directory: "/tmp") +!2 = !{} +!3 = !{i32 2, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{i32 4, !"BTF", i32 1} +!7 = distinct !DISubprogram(name: "f1", scope: !1, file: !1, line: 1, type: !8, isLocal: false, isDefinition: true, scopeLine: 1, flags: DIFlagPrototyped, isOptimized: true, unit: !0, retainedNodes: !2) +!8 = !DISubroutineType(types: !9) +!9 = !{null} +!10 = !DILocation(line: 1, column: 16, scope: !7) diff --git a/llvm/test/CodeGen/X86/BTF/enum.ll b/llvm/test/CodeGen/X86/BTF/enum.ll new file mode 100644 index 0000000000000..18faf01aa552b --- /dev/null +++ b/llvm/test/CodeGen/X86/BTF/enum.ll @@ -0,0 +1,59 @@ +; RUN: llc -mtriple=x86_64-linux-gnu -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s + +; Verify BTF_KIND_ENUM emission on x86_64. +; +; Source: +; enum color { RED, GREEN, BLUE }; +; enum color a; +; Compilation flag: +; clang -target x86_64-linux-gnu -g -gbtf -S -emit-llvm t.c + +@a = dso_local local_unnamed_addr global i32 0, align 4, !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 76 +; CHECK-NEXT: .long 76 +; CHECK-NEXT: .long 29 +; CHECK-NEXT: .long 1 # BTF_KIND_ENUM(id = 1) +; CHECK-NEXT: .long 100663299 # 0x6000003 +; CHECK-NEXT: .long 4 +; CHECK-NEXT: .long 7 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 11 +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long 17 +; CHECK-NEXT: .long 2 +; CHECK: .byte 0 # string offset=0 +; CHECK-NEXT: .ascii "color" # string offset=1 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .ascii "RED" # string offset=7 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .ascii "GREEN" # string offset=11 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .ascii "BLUE" # string offset=17 +; CHECK-NEXT: .byte 0 + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!12, !13, !14, !15} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "a", scope: !2, file: !3, line: 2, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, nameTableKind: None) +!3 = !DIFile(filename: "t.c", directory: "/tmp") +!4 = !{!6} +!5 = !{!0} +!6 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "color", file: !3, line: 1, baseType: !7, size: 32, elements: !8) +!7 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned) +!8 = !{!9, !10, !11} +!9 = !DIEnumerator(name: "RED", value: 0) +!10 = !DIEnumerator(name: "GREEN", value: 1) +!11 = !DIEnumerator(name: "BLUE", value: 2) +!12 = !{i32 2, !"Dwarf Version", i32 4} +!13 = !{i32 2, !"Debug Info Version", i32 3} +!14 = !{i32 1, !"wchar_size", i32 4} +!15 = !{i32 4, !"BTF", i32 1} diff --git a/llvm/test/CodeGen/X86/BTF/func.ll b/llvm/test/CodeGen/X86/BTF/func.ll new file mode 100644 index 0000000000000..c52648c5eb120 --- /dev/null +++ b/llvm/test/CodeGen/X86/BTF/func.ll @@ -0,0 +1,73 @@ +; RUN: llc -mtriple=x86_64-linux-gnu -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s + +; Verify BTF function types and .BTF.ext FuncInfo/LineInfo on x86_64. +; +; Source: +; void f1(void) {} +; Compilation flag: +; clang -target x86_64-linux-gnu -g -gbtf -S -emit-llvm t.c + +define dso_local void @f1() !dbg !7 { + ret void, !dbg !10 +} + +; 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 24 +; CHECK-NEXT: .long 24 +; CHECK-NEXT: .long 19 +; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 1) +; CHECK-NEXT: .long 218103808 # 0xd000000 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 1 # BTF_KIND_FUNC(id = 2) +; CHECK-NEXT: .long 201326593 # 0xc000001 +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .byte 0 # string offset=0 +; CHECK-NEXT: .ascii "f1" # string offset=1 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .ascii ".text" # string offset=4 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .ascii "/tmp/t.c" # string offset=10 +; CHECK-NEXT: .byte 0 +; CHECK: .section .BTF.ext,"",@progbits +; CHECK-NEXT: .short 60319 # 0xeb9f +; CHECK-NEXT: .byte 1 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .long 32 +; 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 8 # FuncInfo +; 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 4 # LineInfo section string offset=4 +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long .Ltmp{{[0-9]+}} +; CHECK-NEXT: .long 10 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 1040 # Line 1 Col 16 + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5, !6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) +!1 = !DIFile(filename: "t.c", directory: "/tmp") +!2 = !{} +!3 = !{i32 2, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{i32 4, !"BTF", i32 1} +!7 = distinct !DISubprogram(name: "f1", scope: !1, file: !1, line: 1, type: !8, isLocal: false, isDefinition: true, scopeLine: 1, flags: DIFlagPrototyped, isOptimized: true, unit: !0, retainedNodes: !2) +!8 = !DISubroutineType(types: !9) +!9 = !{null} +!10 = !DILocation(line: 1, column: 16, scope: !7) diff --git a/llvm/test/CodeGen/X86/BTF/global-var.ll b/llvm/test/CodeGen/X86/BTF/global-var.ll new file mode 100644 index 0000000000000..19af68d9799d2 --- /dev/null +++ b/llvm/test/CodeGen/X86/BTF/global-var.ll @@ -0,0 +1,68 @@ +; RUN: llc -mtriple=x86_64-linux-gnu -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s + +; Verify BTF_KIND_VAR, BTF_KIND_DATASEC, and .BTF.ext FuncInfo/LineInfo +; for a complete translation unit on x86_64. +; +; Source: +; int g = 5; +; int test(void) { return g; } +; Compilation flag: +; clang -target x86_64-linux-gnu -g -gbtf -S -emit-llvm t.c + +@g = dso_local global i32 5, align 4, !dbg !0 + +define dso_local i32 @test() !dbg !11 { + %1 = load i32, ptr @g, align 4, !dbg !14 + ret i32 %1, !dbg !15 +} + +; .BTF section with types +; CHECK: .section .BTF,"",@progbits +; CHECK-NEXT: .short 60319 # 0xeb9f +; CHECK-NEXT: .byte 1 +; CHECK-NEXT: .byte 0 +; CHECK: .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: .long 5 # BTF_KIND_FUNC(id = 3) +; CHECK: .long 25 # BTF_KIND_VAR(id = 4) +; CHECK-NEXT: .long 234881024 # 0xe000000 +; CHECK-NEXT: .long 2 +; CHECK-NEXT: .long 1 +; CHECK: .long 27 # BTF_KIND_DATASEC(id = 5) +; CHECK-NEXT: .long 251658241 # 0xf000001 +; CHECK: .ascii "int" +; CHECK: .ascii "test" +; CHECK: .ascii ".data" + +; .BTF.ext section with FuncInfo and LineInfo +; CHECK: .section .BTF.ext,"",@progbits +; CHECK-NEXT: .short 60319 # 0xeb9f +; CHECK: .long 8 # FuncInfo +; CHECK: .long .Lfunc_begin0 +; CHECK-NEXT: .long 3 +; CHECK: .long 16 # LineInfo + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!7, !8, !9, !10} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "g", scope: !2, file: !3, line: 1, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, nameTableKind: None) +!3 = !DIFile(filename: "t.c", directory: "/tmp") +!4 = !{} +!5 = !{!0} +!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!7 = !{i32 2, !"Dwarf Version", i32 4} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !{i32 1, !"wchar_size", i32 4} +!10 = !{i32 4, !"BTF", i32 1} +!11 = distinct !DISubprogram(name: "test", scope: !3, file: !3, line: 2, type: !12, isLocal: false, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: true, unit: !2, retainedNodes: !4) +!12 = !DISubroutineType(types: !13) +!13 = !{!6} +!14 = !DILocation(line: 2, column: 25, scope: !11) +!15 = !DILocation(line: 2, column: 18, scope: !11) diff --git a/llvm/test/CodeGen/X86/BTF/int.ll b/llvm/test/CodeGen/X86/BTF/int.ll new file mode 100644 index 0000000000000..cf461e0c5e938 --- /dev/null +++ b/llvm/test/CodeGen/X86/BTF/int.ll @@ -0,0 +1,42 @@ +; RUN: llc -mtriple=x86_64-linux-gnu -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s + +; Verify that target-independent BTF emission works on x86_64. +; +; Source: +; int a; +; Compilation flag: +; clang -target x86_64-linux-gnu -g -gbtf -S -emit-llvm t.c + +@a = common dso_local local_unnamed_addr global i32 0, align 4, !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 16 +; CHECK-NEXT: .long 16 +; CHECK-NEXT: .long 5 +; 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: .byte 0 # string offset=0 +; CHECK-NEXT: .ascii "int" # string offset=1 +; CHECK-NEXT: .byte 0 + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!7, !8, !9, !10} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "a", scope: !2, file: !3, line: 1, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, nameTableKind: None) +!3 = !DIFile(filename: "t.c", directory: "/tmp") +!4 = !{} +!5 = !{!0} +!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!7 = !{i32 2, !"Dwarf Version", i32 4} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !{i32 1, !"wchar_size", i32 4} +!10 = !{i32 4, !"BTF", i32 1} diff --git a/llvm/test/CodeGen/X86/BTF/no-btf-flag.ll b/llvm/test/CodeGen/X86/BTF/no-btf-flag.ll new file mode 100644 index 0000000000000..8db56c6b9a2cd --- /dev/null +++ b/llvm/test/CodeGen/X86/BTF/no-btf-flag.ll @@ -0,0 +1,28 @@ +; RUN: llc -mtriple=x86_64-linux-gnu -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s + +; Verify that without the "BTF" module flag, no .BTF sections are emitted +; for non-BPF targets, even when debug info is present. +; +; Source: +; int a; +; Compilation flag: +; clang -target x86_64-linux-gnu -g -S -emit-llvm t.c (no -gbtf) + +@a = common dso_local local_unnamed_addr global i32 0, align 4, !dbg !0 + +; CHECK-NOT: .section .BTF +; CHECK-NOT: .section .BTF.ext + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!7, !8, !9} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "a", scope: !2, file: !3, line: 1, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, nameTableKind: None) +!3 = !DIFile(filename: "t.c", directory: "/tmp") +!4 = !{} +!5 = !{!0} +!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!7 = !{i32 2, !"Dwarf Version", i32 4} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !{i32 1, !"wchar_size", i32 4} diff --git a/llvm/test/CodeGen/X86/BTF/struct.ll b/llvm/test/CodeGen/X86/BTF/struct.ll new file mode 100644 index 0000000000000..ef15469c64f9c --- /dev/null +++ b/llvm/test/CodeGen/X86/BTF/struct.ll @@ -0,0 +1,70 @@ +; RUN: llc -mtriple=x86_64-linux-gnu -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s + +; Verify BTF_KIND_STRUCT emission on x86_64. +; +; Source: +; struct t1 {char m1; int n1;} a; +; Compilation flag: +; clang -target x86_64-linux-gnu -g -gbtf -S -emit-llvm t.c + +%struct.t1 = type { i8, i32 } + +@a = common dso_local local_unnamed_addr global %struct.t1 zeroinitializer, align 4, !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 68 +; CHECK-NEXT: .long 68 +; CHECK-NEXT: .long 19 +; CHECK-NEXT: .long 1 # BTF_KIND_STRUCT(id = 1) +; CHECK-NEXT: .long 67108866 # 0x4000002 +; CHECK-NEXT: .long 8 +; CHECK-NEXT: .long 4 +; CHECK-NEXT: .long 2 +; CHECK-NEXT: .long 0 # 0x0 +; CHECK-NEXT: .long 7 +; CHECK-NEXT: .long 3 +; CHECK-NEXT: .long 32 # 0x20 +; CHECK-NEXT: .long 10 # BTF_KIND_INT(id = 2) +; CHECK-NEXT: .long 16777216 # 0x1000000 +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long 16777224 # 0x1000008 +; 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: .byte 0 # string offset=0 +; CHECK-NEXT: .ascii "t1" # string offset=1 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .ascii "m1" # string offset=4 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .ascii "n1" # string offset=7 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .ascii "char" # string offset=10 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .ascii "int" # string offset=15 +; CHECK-NEXT: .byte 0 + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!12, !13, !14, !15} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "a", scope: !2, file: !3, line: 1, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, nameTableKind: None) +!3 = !DIFile(filename: "t.c", directory: "/tmp") +!4 = !{} +!5 = !{!0} +!6 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t1", file: !3, line: 1, size: 64, elements: !7) +!7 = !{!8, !10} +!8 = !DIDerivedType(tag: DW_TAG_member, name: "m1", scope: !6, file: !3, line: 1, baseType: !9, size: 8) +!9 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) +!10 = !DIDerivedType(tag: DW_TAG_member, name: "n1", scope: !6, file: !3, line: 1, baseType: !11, size: 32, offset: 32) +!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!12 = !{i32 2, !"Dwarf Version", i32 4} +!13 = !{i32 2, !"Debug Info Version", i32 3} +!14 = !{i32 1, !"wchar_size", i32 4} +!15 = !{i32 4, !"BTF", i32 1} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
