https://github.com/caniko created
https://github.com/llvm/llvm-project/pull/183929
## Summary
This makes LLVM's BTF debug info emission work for any ELF target, not just
BPF. The Clang driver gains a new `-gbtf` flag (matching GCC) that causes the
compiler to emit `.BTF` and `.BTF.ext` sections directly from IR debug metadata
— no `pahole` step required.
Companion to #183915 which adds BTF merging/dedup in lld. Together they enable
native BTF for Linux kernel builds with Clang, including LTO + Rust
configurations where pahole's DWARF→BTF conversion currently breaks.
## What's here
**BTFDebug split** — the existing monolithic `BTFDebug` (1800 lines in
`lib/Target/BPF/`) is split into:
- **Target-independent base** (`llvm/include/llvm/CodeGen/BTFDebug.h`,
`llvm/lib/CodeGen/AsmPrinter/BTFDebug.cpp`) — all type visiting from IR debug
metadata, string table, `.BTF` section emission, `.BTF.ext` FuncInfo + LineInfo
subsections, global variable processing. No target dependencies.
- **BPF subclass** (`BPFBTFDebug` in `lib/Target/BPF/`) — CO-RE field
relocations, `.maps` section handling, BPF instruction lowering
(`InstLower()`). These are BPF-only concepts.
**Clang driver** — `-gbtf` flag that:
- Implies debug info (LimitedDebugInfo if none specified)
- Implies DWARF generation (BTF reads the same IR metadata)
- Sets a `"BTF"` module flag, read by the backend
**AsmPrinter integration** — the base `AsmPrinter::doInitialization()`
instantiates `BTFDebug` when it sees `getBTFFlag() && isOSBinFormatELF() &&
!isBPF() && hasDebugInfo()`. BPF keeps using its own `BPFBTFDebug` through
`BPFAsmPrinter`. BTF and DWARF coexist — both consume IR metadata
independently, same as CodeView + DWARF on Windows.
**Tests**:
- 7 X86 BTF lit tests (`llvm/test/CodeGen/X86/BTF/`): int, struct, enum, func,
global-var, no-flag gating, BTF+DWARF coexistence
- Clang driver test (`clang/test/Driver/gbtf.c`): flag forwarding, debug info
implication, negative case
- Clang codegen test (`clang/test/CodeGen/btf-module-flag.c`): module flag
emission
- 104/104 existing BPF BTF lit tests pass unchanged
## Motivation
The kernel's Kconfig has:
```
config RUST
depends on !DEBUG_INFO_BTF || (PAHOLE_HAS_LANG_EXCLUDE && !LTO)
```
`DEBUG_INFO_BTF=y` depends on pahole for DWARF→BTF conversion, which is slow,
fragile with LTO, and doesn't handle Rust well. With compiler-emitted BTF + lld
merging (#183915), the pahole dependency goes away and this Kconfig restriction
can be lifted.
## Design notes
The trickiest part of the split was preserving string table ordering. BPF's
`beginInstruction()` processes CO-RE relocations (adding type name strings)
*before* constructing line info (adding filename/line strings). The refactored
base class calls a `processBeginInstruction()` virtual hook before line info,
so BPF's string ordering is preserved and all 104 existing tests pass without
modification.
For non-BPF targets, `.BTF.ext` only contains FuncInfo and LineInfo — no
FieldReloc subsection, since CO-RE is a BPF-only concept.
I'd appreciate review from @yonghong-song and @eddyz87 on the BTFDebug split —
the BPF subclass should be functionally identical to the original code.
cc @4ast @anakryiko
>From 1fbee53378d216e2c8c00da170d8ec1152848bf5 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..e38d481473c2f 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/CodeGen/AsmPrinter.h"
+#include "llvm/CodeGen/BTFDebug.h"
#include "CodeViewDebug.h"
#include "DwarfDebug.h"
#include "DwarfException.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..e97e939410a64 100644
--- a/llvm/lib/CodeGen/AsmPrinter/CMakeLists.txt
+++ b/llvm/lib/CodeGen/AsmPrinter/CMakeLists.txt
@@ -4,6 +4,7 @@ add_llvm_component_library(LLVMAsmPrinter
AIXException.cpp
ARMException.cpp
AsmPrinter.cpp
+ BTFDebug.cpp
AsmPrinterDwarf.cpp
AsmPrinterInlineAsm.cpp
DbgEntityHistoryCalculator.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..24d521b5a7cd9 100644
--- a/llvm/lib/Target/BPF/CMakeLists.txt
+++ b/llvm/lib/Target/BPF/CMakeLists.txt
@@ -43,7 +43,7 @@ add_llvm_target(BPFCodeGen
BPFMIPeephole.cpp
BPFMIChecking.cpp
BPFMISimplifyPatchable.cpp
- BTFDebug.cpp
+ BPFBTFDebug.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