diff --git a/llvm/lib/Target/BPF/BTFDebug.h b/llvm/lib/Target/BPF/BTFDebug.h --- a/llvm/lib/Target/BPF/BTFDebug.h +++ b/llvm/lib/Target/BPF/BTFDebug.h @@ -289,7 +289,8 @@ std::map> DataSecEntries; std::vector StructTypes; std::map> PatchImms; - std::map>> + std::map>> FixupDerivedTypes; std::setProtoFunctions; @@ -341,6 +342,13 @@ void processDeclAnnotations(DINodeArray Annotations, uint32_t BaseTypeId, int ComponentId); + /// 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. + int genBTFTypeTags(const DIDerivedType *DTy, int BaseTypeId); + /// Generate one field relocation record. void generatePatchImmReloc(const MCSymbol *ORSym, uint32_t RootId, const GlobalVariable *, bool IsAma); diff --git a/llvm/lib/Target/BPF/BTFDebug.cpp b/llvm/lib/Target/BPF/BTFDebug.cpp --- a/llvm/lib/Target/BPF/BTFDebug.cpp +++ b/llvm/lib/Target/BPF/BTFDebug.cpp @@ -552,6 +552,46 @@ } } +/// Generate btf_type_tag chains. +int BTFDebug::genBTFTypeTags(const DIDerivedType *DTy, int BaseTypeId) { + SmallVector 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(Annotations); + const MDString *Name = cast(MD->getOperand(0)); + if (!Name->getString().equals("btf_type_tag")) + continue; + MDStrs.push_back(cast(MD->getOperand(1))); + } + } + + 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 TypeEntry; + if (BaseTypeId >= 0) + TypeEntry = + std::make_unique(BaseTypeId, MDStrs[0]->getString()); + else + TypeEntry = std::make_unique(DTy, MDStrs[0]->getString()); + TmpTypeId = addType(std::move(TypeEntry)); + + for (unsigned I = 1; I < MDStrs.size(); I++) { + const MDString *Value = MDStrs[I]; + TypeEntry = std::make_unique(TmpTypeId, Value->getString()); + TmpTypeId = addType(std::move(TypeEntry)); + } + return TmpTypeId; +} + /// Handle structure/union types. void BTFDebug::visitStructType(const DICompositeType *CTy, bool IsStruct, uint32_t &TypeId) { @@ -684,9 +724,8 @@ /// pointee type will be replaced with either a real type or /// a forward declaration. auto TypeEntry = std::make_unique(DTy, Tag, true); - auto &Fixup = FixupDerivedTypes[CTy->getName()]; - Fixup.first = CTag == dwarf::DW_TAG_union_type; - Fixup.second.push_back(TypeEntry.get()); + auto &Fixup = FixupDerivedTypes[CTy]; + Fixup.push_back(std::make_pair(DTy, TypeEntry.get())); TypeId = addType(std::move(TypeEntry), DTy); return; } @@ -695,34 +734,8 @@ } if (Tag == dwarf::DW_TAG_pointer_type) { - SmallVector 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(Annotations); - const MDString *Name = cast(MD->getOperand(0)); - if (!Name->getString().equals("btf_type_tag")) - continue; - MDStrs.push_back(cast(MD->getOperand(1))); - } - } - - if (MDStrs.size() > 0) { - // 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. - auto TypeEntry = - std::make_unique(DTy, MDStrs[0]->getString()); - unsigned TmpTypeId = addType(std::move(TypeEntry)); - for (unsigned I = 1; I < MDStrs.size(); I++) { - const MDString *Value = MDStrs[I]; - TypeEntry = - std::make_unique(TmpTypeId, Value->getString()); - TmpTypeId = addType(std::move(TypeEntry)); - } + int TmpTypeId = genBTFTypeTags(DTy, -1); + if (TmpTypeId >= 0) { auto TypeDEntry = std::make_unique(TmpTypeId, Tag, DTy->getName()); TypeId = addType(std::move(TypeDEntry), DTy); @@ -1480,8 +1493,9 @@ // Fixups for (auto &Fixup : FixupDerivedTypes) { - StringRef TypeName = Fixup.first; - bool IsUnion = Fixup.second.first; + const DICompositeType *CTy = Fixup.first; + StringRef TypeName = CTy->getName(); + bool IsUnion = CTy->getTag() == dwarf::DW_TAG_union_type; // Search through struct types uint32_t StructTypeId = 0; @@ -1497,8 +1511,15 @@ StructTypeId = addType(std::move(FwdTypeEntry)); } - for (auto &DType : Fixup.second.second) { - DType->setPointeeType(StructTypeId); + for (auto &TypeInfo : Fixup.second) { + const DIDerivedType *DTy = TypeInfo.first; + BTFTypeDerived *BDType = TypeInfo.second; + + int TmpTypeId = genBTFTypeTags(DTy, StructTypeId); + if (TmpTypeId >= 0) + BDType->setPointeeType(TmpTypeId); + else + BDType->setPointeeType(StructTypeId); } } diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-fixup-fwd.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-fixup-fwd.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/BPF/BTF/type-tag-fixup-fwd.ll @@ -0,0 +1,131 @@ +; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s +; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s +; +; Source: +; #define __tag1 __attribute__((btf_type_tag("tag1"))) +; #define __tag2 __attribute__((btf_type_tag("tag2"))) +; +; struct foo; +; struct map_value { +; struct foo __tag2 __tag1 *ptr; +; }; +; void func(struct map_value *); +; void test(void) +; { +; struct map_value v = {}; +; func(&v); +; } +; Compilation flag: +; clang -target bpf -O2 -g -S -emit-llvm test.c + +%struct.map_value = type { %struct.foo* } +%struct.foo = type opaque + +; Function Attrs: nounwind +define dso_local void @test() local_unnamed_addr #0 !dbg !7 { +entry: + %v = alloca %struct.map_value, align 8 + %0 = bitcast %struct.map_value* %v to i8*, !dbg !20 + call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %0) #4, !dbg !20 + call void @llvm.dbg.declare(metadata %struct.map_value* %v, metadata !11, metadata !DIExpression()), !dbg !21 + %1 = bitcast %struct.map_value* %v to i64*, !dbg !21 + store i64 0, i64* %1, align 8, !dbg !21 + call void @func(%struct.map_value* noundef nonnull %v) #4, !dbg !22 + call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %0) #4, !dbg !23 + ret void, !dbg !23 +} + +; CHECK: .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: .long 0 # BTF_KIND_FUNC_PROTO(id = 3) +; CHECK-NEXT: .long 218103809 # 0xd000001 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 4 +; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 4) +; CHECK-NEXT: .long 33554432 # 0x2000000 +; CHECK-NEXT: .long 5 +; CHECK-NEXT: .long 62 # BTF_KIND_STRUCT(id = 5) +; CHECK-NEXT: .long 67108865 # 0x4000001 +; CHECK-NEXT: .long 8 +; CHECK-NEXT: .long 72 +; CHECK-NEXT: .long 8 +; CHECK-NEXT: .long 0 # 0x0 +; CHECK-NEXT: .long 76 # BTF_KIND_TYPE_TAG(id = 6) +; CHECK-NEXT: .long 301989888 # 0x12000000 +; CHECK-NEXT: .long 9 +; CHECK-NEXT: .long 81 # BTF_KIND_TYPE_TAG(id = 7) +; CHECK-NEXT: .long 301989888 # 0x12000000 +; CHECK-NEXT: .long 6 +; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 8) +; CHECK-NEXT: .long 33554432 # 0x2000000 +; CHECK-NEXT: .long 7 +; CHECK-NEXT: .long 86 # BTF_KIND_FWD(id = 9) +; CHECK-NEXT: .long 117440512 # 0x7000000 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 90 # BTF_KIND_FUNC(id = 10) +; CHECK-NEXT: .long 201326594 # 0xc000002 +; CHECK-NEXT: .long 3 + +; CHECK: .ascii "test" # string offset=1 +; CHECK: .ascii "map_value" # string offset=62 +; CHECK: .ascii "ptr" # string offset=72 +; CHECK: .ascii "tag2" # string offset=76 +; CHECK: .ascii "tag1" # string offset=81 +; CHECK: .ascii "foo" # string offset=86 +; CHECK: .ascii "func" # string offset=90 + +; Function Attrs: argmemonly mustprogress nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 + +; Function Attrs: mustprogress nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) #2 + +declare !dbg !24 dso_local void @func(%struct.map_value* noundef) local_unnamed_addr #3 + +; Function Attrs: argmemonly mustprogress nofree nosync nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 + +attributes #0 = { nounwind "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } +attributes #1 = { argmemonly mustprogress nofree nosync nounwind willreturn } +attributes #2 = { mustprogress nofree nosync nounwind readnone speculatable willreturn } +attributes #3 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } +attributes #4 = { nounwind } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 15.0.0 (https://github.com/llvm/llvm-project.git 25e8505f515bc9ef6c13527ffc4a902bae3a9071)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/llvm/btf_tag_type", checksumkind: CSK_MD5, checksum: "7735a89e98603fee29d352a8e9db5acb") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 7, !"frame-pointer", i32 2} +!6 = !{!"clang version 15.0.0 (https://github.com/llvm/llvm-project.git 25e8505f515bc9ef6c13527ffc4a902bae3a9071)"} +!7 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 9, type: !8, scopeLine: 10, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !10) +!8 = !DISubroutineType(types: !9) +!9 = !{null} +!10 = !{!11} +!11 = !DILocalVariable(name: "v", scope: !7, file: !1, line: 11, type: !12) +!12 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "map_value", file: !1, line: 5, size: 64, elements: !13) +!13 = !{!14} +!14 = !DIDerivedType(tag: DW_TAG_member, name: "ptr", scope: !12, file: !1, line: 6, baseType: !15, size: 64) +!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64, annotations: !17) +!16 = !DICompositeType(tag: DW_TAG_structure_type, name: "foo", file: !1, line: 4, flags: DIFlagFwdDecl) +!17 = !{!18, !19} +!18 = !{!"btf_type_tag", !"tag2"} +!19 = !{!"btf_type_tag", !"tag1"} +!20 = !DILocation(line: 11, column: 9, scope: !7) +!21 = !DILocation(line: 11, column: 26, scope: !7) +!22 = !DILocation(line: 12, column: 9, scope: !7) +!23 = !DILocation(line: 13, column: 1, scope: !7) +!24 = !DISubprogram(name: "func", scope: !1, file: !1, line: 8, type: !25, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !28) +!25 = !DISubroutineType(types: !26) +!26 = !{null, !27} +!27 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64) +!28 = !{} diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-fixup-resolved.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-fixup-resolved.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/BPF/BTF/type-tag-fixup-resolved.ll @@ -0,0 +1,151 @@ +; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s +; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s +; +; Source: +; #define __tag1 __attribute__((btf_type_tag("tag1"))) +; #define __tag2 __attribute__((btf_type_tag("tag2"))) +; +; struct foo { +; int i; +; }; +; struct map_value { +; struct foo __tag2 __tag1 *ptr; +; }; +; void func(struct map_value *, struct foo *); +; void test(void) +; { +; struct map_value v = {}; +; func(&v, v.ptr); +; } +; Compilation flag: +; clang -target bpf -O2 -g -S -emit-llvm test.c + +%struct.map_value = type { %struct.foo* } +%struct.foo = type { i32 } + +; Function Attrs: nounwind +define dso_local void @test() local_unnamed_addr #0 !dbg !7 { +entry: + %v = alloca %struct.map_value, align 8 + %0 = bitcast %struct.map_value* %v to i8*, !dbg !23 + call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %0) #4, !dbg !23 + call void @llvm.dbg.declare(metadata %struct.map_value* %v, metadata !11, metadata !DIExpression()), !dbg !24 + %1 = bitcast %struct.map_value* %v to i64*, !dbg !24 + store i64 0, i64* %1, align 8, !dbg !24 + call void @func(%struct.map_value* noundef nonnull %v, %struct.foo* noundef null) #4, !dbg !25 + call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %0) #4, !dbg !26 + ret void, !dbg !26 +} + +; CHECK: .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: .long 0 # BTF_KIND_FUNC_PROTO(id = 3) +; CHECK-NEXT: .long 218103810 # 0xd000002 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 4 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 7 +; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 4) +; CHECK-NEXT: .long 33554432 # 0x2000000 +; CHECK-NEXT: .long 5 +; CHECK-NEXT: .long 63 # BTF_KIND_STRUCT(id = 5) +; CHECK-NEXT: .long 67108865 # 0x4000001 +; CHECK-NEXT: .long 8 +; CHECK-NEXT: .long 73 +; CHECK-NEXT: .long 6 +; CHECK-NEXT: .long 0 # 0x0 +; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 6) +; CHECK-NEXT: .long 33554432 # 0x2000000 +; CHECK-NEXT: .long 12 +; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 7) +; CHECK-NEXT: .long 33554432 # 0x2000000 +; CHECK-NEXT: .long 8 +; CHECK-NEXT: .long 77 # BTF_KIND_STRUCT(id = 8) +; CHECK-NEXT: .long 67108865 # 0x4000001 +; CHECK-NEXT: .long 4 +; CHECK-NEXT: .long 81 +; CHECK-NEXT: .long 9 +; CHECK-NEXT: .long 0 # 0x0 +; CHECK-NEXT: .long 83 # BTF_KIND_INT(id = 9) +; CHECK-NEXT: .long 16777216 # 0x1000000 +; CHECK-NEXT: .long 4 +; CHECK-NEXT: .long 16777248 # 0x1000020 +; CHECK-NEXT: .long 87 # BTF_KIND_FUNC(id = 10) +; CHECK-NEXT: .long 201326594 # 0xc000002 +; CHECK-NEXT: .long 3 +; CHECK-NEXT: .long 92 # BTF_KIND_TYPE_TAG(id = 11) +; CHECK-NEXT: .long 301989888 # 0x12000000 +; CHECK-NEXT: .long 8 +; CHECK-NEXT: .long 97 # BTF_KIND_TYPE_TAG(id = 12) +; CHECK-NEXT: .long 301989888 # 0x12000000 +; CHECK-NEXT: .long 11 + +; CHECK: .ascii "test" # string offset=1 +; CHECK: .ascii "map_value" # string offset=63 +; CHECK: .ascii "ptr" # string offset=73 +; CHECK: .ascii "foo" # string offset=77 +; CHECK: .byte 105 # string offset=81 +; CHECK: .ascii "int" # string offset=83 +; CHECK: .ascii "func" # string offset=87 +; CHECK: .ascii "tag2" # string offset=92 +; CHECK: .ascii "tag1" # string offset=97 + +; Function Attrs: argmemonly mustprogress nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 + +; Function Attrs: mustprogress nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) #2 + +declare !dbg !27 dso_local void @func(%struct.map_value* noundef, %struct.foo* noundef) local_unnamed_addr #3 + +; Function Attrs: argmemonly mustprogress nofree nosync nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 + +attributes #0 = { nounwind "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } +attributes #1 = { argmemonly mustprogress nofree nosync nounwind willreturn } +attributes #2 = { mustprogress nofree nosync nounwind readnone speculatable willreturn } +attributes #3 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } +attributes #4 = { nounwind } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 15.0.0 (https://github.com/llvm/llvm-project.git 25e8505f515bc9ef6c13527ffc4a902bae3a9071)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.c", directory: "/tmp//home/yhs/work/tests/llvm/btf_tag_type", checksumkind: CSK_MD5, checksum: "8b3b8281c3b4240403467e0c9461251d") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 7, !"frame-pointer", i32 2} +!6 = !{!"clang version 15.0.0 (https://github.com/llvm/llvm-project.git 25e8505f515bc9ef6c13527ffc4a902bae3a9071)"} +!7 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 11, type: !8, scopeLine: 12, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !10) +!8 = !DISubroutineType(types: !9) +!9 = !{null} +!10 = !{!11} +!11 = !DILocalVariable(name: "v", scope: !7, file: !1, line: 13, type: !12) +!12 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "map_value", file: !1, line: 7, size: 64, elements: !13) +!13 = !{!14} +!14 = !DIDerivedType(tag: DW_TAG_member, name: "ptr", scope: !12, file: !1, line: 8, baseType: !15, size: 64) +!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64, annotations: !20) +!16 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "foo", file: !1, line: 4, size: 32, elements: !17) +!17 = !{!18} +!18 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !16, file: !1, line: 5, baseType: !19, size: 32) +!19 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!20 = !{!21, !22} +!21 = !{!"btf_type_tag", !"tag2"} +!22 = !{!"btf_type_tag", !"tag1"} +!23 = !DILocation(line: 13, column: 9, scope: !7) +!24 = !DILocation(line: 13, column: 26, scope: !7) +!25 = !DILocation(line: 14, column: 9, scope: !7) +!26 = !DILocation(line: 15, column: 1, scope: !7) +!27 = !DISubprogram(name: "func", scope: !1, file: !1, line: 10, type: !28, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !32) +!28 = !DISubroutineType(types: !29) +!29 = !{null, !30, !31} +!30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64) +!31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64) +!32 = !{}