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

Reply via email to