[llvm] [BPF] Visit nested map array during BTF generation (PR #150608)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 25 04:46:49 PDT 2025
https://github.com/mtardy created https://github.com/llvm/llvm-project/pull/150608
Fixes missing inner map struct type definitions [^1]. We should visit the type of nested array of maps like we do for global maps. This patch adds a boolean to convey the information to visitTypeEntry and visitDerivedType that the pointee is a map definition and should be treated as such.
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
>From feec10823af0fbc477a808ee81bf433e6aae633d 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] [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. This patch
adds a boolean to convey the information to visitTypeEntry and
visitDerivedType that the pointee is a map definition and should be
treated as such.
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
Signed-off-by: Mahe Tardy <mahe.tardy at gmail.com>
---
llvm/lib/Target/BPF/BTFDebug.cpp | 83 +++++++++++--------
llvm/lib/Target/BPF/BTFDebug.h | 4 +-
.../CodeGen/BPF/BTF/map-def-nested-array.ll | 75 +++++++++++++++++
3 files changed, 127 insertions(+), 35 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..5ad16397eb124 100644
--- a/llvm/lib/Target/BPF/BTFDebug.cpp
+++ b/llvm/lib/Target/BPF/BTFDebug.cpp
@@ -707,7 +707,7 @@ void BTFDebug::visitArrayType(const DICompositeType *CTy, uint32_t &TypeId) {
// Visit array element type.
uint32_t ElemTypeId;
const DIType *ElemType = CTy->getBaseType();
- visitTypeEntry(ElemType, ElemTypeId, false, false);
+ visitTypeEntry(ElemType, ElemTypeId, false, false, false);
// Visit array dimensions.
DINodeArray Elements = CTy->getElements();
@@ -806,12 +806,13 @@ bool BTFDebug::IsForwardDeclCandidate(const DIType *Base) {
/// Handle pointer, typedef, const, volatile, restrict and member types.
void BTFDebug::visitDerivedType(const DIDerivedType *DTy, uint32_t &TypeId,
- bool CheckPointer, bool SeenPointer) {
+ bool CheckPointer, bool SeenPointer,
+ bool NestedMap) {
unsigned Tag = DTy->getTag();
if (Tag == dwarf::DW_TAG_atomic_type)
- return visitTypeEntry(DTy->getBaseType(), TypeId, CheckPointer,
- SeenPointer);
+ return visitTypeEntry(DTy->getBaseType(), TypeId, CheckPointer, SeenPointer,
+ NestedMap);
/// Try to avoid chasing pointees, esp. structure pointees which may
/// unnecessary bring in a lot of types.
@@ -859,10 +860,15 @@ void BTFDebug::visitDerivedType(const DIDerivedType *DTy, uint32_t &TypeId,
// Visit base type of pointer, typedef, const, volatile, restrict or
// struct/union member.
uint32_t TempTypeId = 0;
- if (Tag == dwarf::DW_TAG_member)
- visitTypeEntry(DTy->getBaseType(), TempTypeId, true, false);
- else
- visitTypeEntry(DTy->getBaseType(), TempTypeId, CheckPointer, SeenPointer);
+ if (Tag == dwarf::DW_TAG_member) {
+ visitTypeEntry(DTy->getBaseType(), TempTypeId, true, false, NestedMap);
+ } else {
+ if (NestedMap)
+ visitMapDefType(DTy->getBaseType(), TempTypeId);
+ else
+ visitTypeEntry(DTy->getBaseType(), TempTypeId, CheckPointer, SeenPointer,
+ NestedMap);
+ }
}
/// Visit a type entry. CheckPointer is true if the type has
@@ -873,7 +879,8 @@ void BTFDebug::visitDerivedType(const DIDerivedType *DTy, uint32_t &TypeId,
/// 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) {
+ bool CheckPointer, bool SeenPointer,
+ bool NestedMap) {
if (!Ty || DIToIdMap.find(Ty) != DIToIdMap.end()) {
TypeId = DIToIdMap[Ty];
@@ -923,7 +930,8 @@ void BTFDebug::visitTypeEntry(const DIType *Ty, uint32_t &TypeId,
break;
}
uint32_t TmpTypeId;
- visitTypeEntry(BaseTy, TmpTypeId, CheckPointer, SeenPointer);
+ visitTypeEntry(BaseTy, TmpTypeId, CheckPointer, SeenPointer,
+ NestedMap);
break;
}
}
@@ -941,14 +949,14 @@ void BTFDebug::visitTypeEntry(const DIType *Ty, uint32_t &TypeId,
else if (const auto *CTy = dyn_cast<DICompositeType>(Ty))
visitCompositeType(CTy, TypeId);
else if (const auto *DTy = dyn_cast<DIDerivedType>(Ty))
- visitDerivedType(DTy, TypeId, CheckPointer, SeenPointer);
+ visitDerivedType(DTy, TypeId, CheckPointer, SeenPointer, NestedMap);
else
llvm_unreachable("Unknown DIType");
}
void BTFDebug::visitTypeEntry(const DIType *Ty) {
uint32_t TypeId;
- visitTypeEntry(Ty, TypeId, false, false);
+ visitTypeEntry(Ty, TypeId, false, false, false);
}
void BTFDebug::visitMapDefType(const DIType *Ty, uint32_t &TypeId) {
@@ -973,31 +981,40 @@ 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
+ const DIType *ElementType = CTy->getBaseType();
+ visitTypeEntry(ElementType, TypeId, false, false, true);
+ } 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);
+ }
}
}
// Visit this type, struct or a const/typedef/volatile/restrict type
- visitTypeEntry(OrigTy, TypeId, false, false);
+ visitTypeEntry(OrigTy, TypeId, false, false, false);
}
/// Read file contents from the actual file or from the source
@@ -1275,7 +1292,7 @@ void BTFDebug::endFunctionImpl(const MachineFunction *MF) {
/// accessing or preserve debuginfo type.
unsigned BTFDebug::populateType(const DIType *Ty) {
unsigned Id;
- visitTypeEntry(Ty, Id, false, false);
+ visitTypeEntry(Ty, Id, false, false, false);
for (const auto &TypeEntry : TypeEntries)
TypeEntry->completeType(*this);
return Id;
@@ -1474,7 +1491,7 @@ void BTFDebug::processGlobals(bool ProcessingMapDef) {
visitMapDefType(DIGlobal->getType(), GVTypeId);
else {
const DIType *Ty = tryRemoveAtomicType(DIGlobal->getType());
- visitTypeEntry(Ty, GVTypeId, false, false);
+ visitTypeEntry(Ty, GVTypeId, false, false, false);
}
break;
}
diff --git a/llvm/lib/Target/BPF/BTFDebug.h b/llvm/lib/Target/BPF/BTFDebug.h
index 75858fcc8bfde..687e5e0199404 100644
--- a/llvm/lib/Target/BPF/BTFDebug.h
+++ b/llvm/lib/Target/BPF/BTFDebug.h
@@ -321,7 +321,7 @@ class BTFDebug : public DebugHandlerBase {
/// @{
void visitTypeEntry(const DIType *Ty);
void visitTypeEntry(const DIType *Ty, uint32_t &TypeId, bool CheckPointer,
- bool SeenPointer);
+ bool SeenPointer, bool NestedMap);
void visitBasicType(const DIBasicType *BTy, uint32_t &TypeId);
void visitSubroutineType(
const DISubroutineType *STy, bool ForSubprog,
@@ -335,7 +335,7 @@ class BTFDebug : public DebugHandlerBase {
void visitArrayType(const DICompositeType *ATy, uint32_t &TypeId);
void visitEnumType(const DICompositeType *ETy, uint32_t &TypeId);
void visitDerivedType(const DIDerivedType *DTy, uint32_t &TypeId,
- bool CheckPointer, bool SeenPointer);
+ bool CheckPointer, bool SeenPointer, bool NestedMap);
void visitMapDefType(const DIType *Ty, uint32_t &TypeId);
/// @}
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..15bc1a30c4aed
--- /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=5
+; CHECK-BTF-NEXT: [2] PTR '(anon)' type_id=3
+; CHECK-BTF-NEXT: [3] STRUCT 'nested_value_type' size=4 vlen=1
+; CHECK-BTF-NEXT: 'a1' type_id=4 bits_offset=0
+; CHECK-BTF-NEXT: [4] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
+; CHECK-BTF-NEXT: [5] STRUCT '(anon)' size=8 vlen=1
+; CHECK-BTF-NEXT: 'value' type_id=2 bits_offset=0
+; CHECK-BTF-NEXT: [6] ARRAY '(anon)' type_id=1 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)"}
More information about the llvm-commits
mailing list