[llvm] [BPF] Visit nested map array during BTF generation (PR #150608)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jul 28 04:11:55 PDT 2025


https://github.com/mtardy updated https://github.com/llvm/llvm-project/pull/150608

>From d488456699d70f3675a1a4830f47b73c96f4c42c Mon Sep 17 00:00:00 2001
From: Mahe Tardy <mahe.tardy at gmail.com>
Date: Thu, 24 Jul 2025 17:47:50 +0000
Subject: [PATCH 1/2] [BPF] Visit nested map array during BTF generation

Fixes missing inner map struct type definitions [^1]. We should visit
the type of nested array of maps like we do for global maps. We visit
the derived type of the array directly in visitMapDefType.

It ressembles and works with commit 0d21c956a5c1 ("[BPF] Handle nested
wrapper structs in BPF map definition traversal (#144097)") which
focused on directly nested wrapper structs.

Before that patch, this ARRAY_OF_MAPS definition would lead to the BTF
information include the 'missing_type' as "FWD 'missing_type'
fwd_kind=struct":

	struct missing_type { uint64_t foo; };
	struct {
		__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
		[...]
		__array(
			values, struct {
				[...]
				__type(value, struct missing_type);
			});
	} map SEC(".maps");

Which lead to errors while trying to load the map:

	libbpf: map 'outer_map.inner': can't determine value size for type [N]: -22.

To solve this issue, users had to use the struct in a dummy variable or
in a dummy function for the BTF to be generated correctly [^2].

[^1]: https://lore.kernel.org/netdev/aH_cGvgC20iD8qs9@gmail.com/T/#u
[^2]: https://github.com/cilium/ebpf/discussions/1658#discussioncomment-12491339

Co-authored-by: Eduard Zingerman <eddyz87 at gmail.com>
Signed-off-by: Mahe Tardy <mahe.tardy at gmail.com>
---
 llvm/lib/Target/BPF/BTFDebug.cpp              | 48 +++++++-----
 .../CodeGen/BPF/BTF/map-def-nested-array.ll   | 75 +++++++++++++++++++
 2 files changed, 103 insertions(+), 20 deletions(-)
 create mode 100644 llvm/test/CodeGen/BPF/BTF/map-def-nested-array.ll

diff --git a/llvm/lib/Target/BPF/BTFDebug.cpp b/llvm/lib/Target/BPF/BTFDebug.cpp
index 1e29a0f1e85a1..6ee5667902837 100644
--- a/llvm/lib/Target/BPF/BTFDebug.cpp
+++ b/llvm/lib/Target/BPF/BTFDebug.cpp
@@ -957,13 +957,13 @@ void BTFDebug::visitMapDefType(const DIType *Ty, uint32_t &TypeId) {
     return;
   }
 
-  // MapDef type may be a struct type or a non-pointer derived type
+  // MapDef type may be a struct type or a derived type
   const DIType *OrigTy = Ty;
   while (auto *DTy = dyn_cast<DIDerivedType>(Ty)) {
     auto Tag = DTy->getTag();
     if (Tag != dwarf::DW_TAG_typedef && Tag != dwarf::DW_TAG_const_type &&
         Tag != dwarf::DW_TAG_volatile_type &&
-        Tag != dwarf::DW_TAG_restrict_type)
+        Tag != dwarf::DW_TAG_restrict_type && Tag != dwarf::DW_TAG_pointer_type)
       break;
     Ty = DTy->getBaseType();
   }
@@ -973,26 +973,34 @@ void BTFDebug::visitMapDefType(const DIType *Ty, uint32_t &TypeId) {
     return;
 
   auto Tag = CTy->getTag();
-  if (Tag != dwarf::DW_TAG_structure_type || CTy->isForwardDecl())
+  if ((Tag != dwarf::DW_TAG_structure_type &&
+       Tag != dwarf::DW_TAG_array_type) ||
+      CTy->isForwardDecl())
     return;
 
-  // Visit all struct members to ensure their types are visited.
-  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, TypeId);
-    } else {
-      visitTypeEntry(MemberBaseType);
+  // Visit potential nested map array
+  if (CTy->getTag() == dwarf::DW_TAG_array_type) {
+    // Jump to the element type of the array
+    visitMapDefType(CTy->getBaseType(), TypeId);
+  } else {
+    // Visit all struct members to ensure their types are visited.
+    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, TypeId);
+      } else {
+        visitTypeEntry(MemberBaseType);
+      }
     }
   }
 
diff --git a/llvm/test/CodeGen/BPF/BTF/map-def-nested-array.ll b/llvm/test/CodeGen/BPF/BTF/map-def-nested-array.ll
new file mode 100644
index 0000000000000..ec66227ebd28c
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/BTF/map-def-nested-array.ll
@@ -0,0 +1,75 @@
+; RUN: llc -mtriple=bpfel -mcpu=v3 -filetype=obj -o %t1 %s
+; RUN: llvm-objcopy --dump-section='.BTF'=%t2 %t1
+; RUN: %python %p/print_btf.py %t2 | FileCheck -check-prefixes=CHECK-BTF-SHORT %s
+; RUN: %python %p/print_btf.py %t2 | FileCheck -check-prefixes=CHECK-BTF %s
+; Source:
+;  struct nested_value_type {
+;  	int a1;
+;  };
+;  struct map_type {
+;  	struct {
+;  		struct nested_value_type *value;
+;  	} *values[];
+;  };
+; Compilation flags:
+;   clang -target bpf -g -O2 -S -emit-llvm prog.c
+
+; ModuleID = 'prog.c'
+source_filename = "prog.c"
+target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
+target triple = "bpf"
+
+%struct.map_type = type { [0 x ptr] }
+
+ at array_of_maps = dso_local local_unnamed_addr global %struct.map_type zeroinitializer, section ".maps", align 8, !dbg !0
+
+; We expect no forward declarations.
+;
+; CHECK-BTF-SHORT-NOT: FWD
+
+; Assert the whole BTF.
+;
+; CHECK-BTF: [1] PTR '(anon)' type_id=2
+; CHECK-BTF-NEXT: [2] STRUCT 'nested_value_type' size=4 vlen=1
+; CHECK-BTF-NEXT:         'a1' type_id=3 bits_offset=0
+; CHECK-BTF-NEXT: [3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
+; CHECK-BTF-NEXT: [4] PTR '(anon)' type_id=5
+; CHECK-BTF-NEXT: [5] STRUCT '(anon)' size=8 vlen=1
+; CHECK-BTF-NEXT:         'value' type_id=1 bits_offset=0
+; CHECK-BTF-NEXT: [6] ARRAY '(anon)' type_id=4 index_type_id=7 nr_elems=0
+; CHECK-BTF-NEXT: [7] INT '__ARRAY_SIZE_TYPE__' size=4 bits_offset=0 nr_bits=32 encoding=(none)
+; CHECK-BTF-NEXT: [8] STRUCT 'map_type' size=0 vlen=1
+; CHECK-BTF-NEXT:         'values' type_id=6 bits_offset=0
+; CHECK-BTF-NEXT: [9] VAR 'array_of_maps' type_id=8, linkage=global
+; CHECK-BTF-NEXT: [10] DATASEC '.maps' size=0 vlen=1
+; CHECK-BTF-NEXT:         type_id=9 offset=0 size=0
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!20, !21, !22, !23}
+!llvm.ident = !{!24}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "array_of_maps", scope: !2, file: !3, line: 9, type: !5, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang version 22.0.0git (git at github.com:llvm/llvm-project.git ed93eaa421b714028b85cc887d80c45991d7207f)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "prog.c", directory: "/home/mtardy/llvm-bug-repro", checksumkind: CSK_MD5, checksum: "9381d9e83e9c0b235a14704224815e96")
+!4 = !{!0}
+!5 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "map_type", file: !3, line: 4, elements: !6)
+!6 = !{!7}
+!7 = !DIDerivedType(tag: DW_TAG_member, name: "values", scope: !5, file: !3, line: 7, baseType: !8)
+!8 = !DICompositeType(tag: DW_TAG_array_type, baseType: !9, elements: !18)
+!9 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !10, size: 64)
+!10 = distinct !DICompositeType(tag: DW_TAG_structure_type, scope: !5, file: !3, line: 5, size: 64, elements: !11)
+!11 = !{!12}
+!12 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !10, file: !3, line: 6, baseType: !13, size: 64)
+!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64)
+!14 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "nested_value_type", file: !3, line: 1, size: 32, elements: !15)
+!15 = !{!16}
+!16 = !DIDerivedType(tag: DW_TAG_member, name: "a1", scope: !14, file: !3, line: 2, baseType: !17, size: 32)
+!17 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!18 = !{!19}
+!19 = !DISubrange(count: -1)
+!20 = !{i32 7, !"Dwarf Version", i32 5}
+!21 = !{i32 2, !"Debug Info Version", i32 3}
+!22 = !{i32 1, !"wchar_size", i32 4}
+!23 = !{i32 7, !"frame-pointer", i32 2}
+!24 = !{!"clang version 22.0.0git (git at github.com:llvm/llvm-project.git ed93eaa421b714028b85cc887d80c45991d7207f)"}

>From 75e6c574b51477c4b9d411c5228f5bd55beeb041 Mon Sep 17 00:00:00 2001
From: Eduard Zingerman <eddyz87 at gmail.com>
Date: Fri, 25 Jul 2025 17:19:42 -0700
Subject: [PATCH 2/2] [BPF] Reorganize BTFDebug::visitMapDefType for better
 readability

Kernel BPF selftests passing. No change in BTF generated for kernel
selftests, modulo some reorderings.

Signed-off-by: Mahe Tardy <mahe.tardy at gmail.com>
---
 llvm/lib/Target/BPF/BTFDebug.cpp | 50 ++++++++++++++------------------
 1 file changed, 21 insertions(+), 29 deletions(-)

diff --git a/llvm/lib/Target/BPF/BTFDebug.cpp b/llvm/lib/Target/BPF/BTFDebug.cpp
index 6ee5667902837..584b6ff543c28 100644
--- a/llvm/lib/Target/BPF/BTFDebug.cpp
+++ b/llvm/lib/Target/BPF/BTFDebug.cpp
@@ -957,38 +957,26 @@ void BTFDebug::visitMapDefType(const DIType *Ty, uint32_t &TypeId) {
     return;
   }
 
-  // MapDef type may be a struct type or a derived type
-  const DIType *OrigTy = Ty;
-  while (auto *DTy = dyn_cast<DIDerivedType>(Ty)) {
-    auto Tag = DTy->getTag();
-    if (Tag != dwarf::DW_TAG_typedef && Tag != dwarf::DW_TAG_const_type &&
-        Tag != dwarf::DW_TAG_volatile_type &&
-        Tag != dwarf::DW_TAG_restrict_type && Tag != dwarf::DW_TAG_pointer_type)
-      break;
-    Ty = DTy->getBaseType();
-  }
-
-  const auto *CTy = dyn_cast<DICompositeType>(Ty);
-  if (!CTy)
-    return;
-
-  auto Tag = CTy->getTag();
-  if ((Tag != dwarf::DW_TAG_structure_type &&
-       Tag != dwarf::DW_TAG_array_type) ||
-      CTy->isForwardDecl())
-    return;
-
-  // Visit potential nested map array
-  if (CTy->getTag() == dwarf::DW_TAG_array_type) {
-    // Jump to the element type of the array
-    visitMapDefType(CTy->getBaseType(), TypeId);
-  } else {
+  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.
@@ -997,15 +985,19 @@ void BTFDebug::visitMapDefType(const DIType *Ty, uint32_t &TypeId) {
       // than as a regular composite type.
       const auto *MemberCTy = dyn_cast<DICompositeType>(MemberBaseType);
       if (MemberCTy) {
-        visitMapDefType(MemberBaseType, TypeId);
+        visitMapDefType(MemberBaseType, TmpId);
       } else {
         visitTypeEntry(MemberBaseType);
       }
     }
+    break;
+  }
+  default:
+    break;
   }
 
   // Visit this type, struct or a const/typedef/volatile/restrict type
-  visitTypeEntry(OrigTy, TypeId, false, false);
+  visitTypeEntry(Ty, TypeId, false, false);
 }
 
 /// Read file contents from the actual file or from the source



More information about the llvm-commits mailing list