From 8573c28a514f5d06bd5a8854093e461fa6e467a4 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Mon, 29 Mar 2021 15:02:25 -0700 Subject: [PATCH] Add debug support for set types This commit adds debugging support for set types defined in languages such as Pascal and Modula-2. Patch by Peter McKinna! Differential Revision: https://reviews.llvm.org/D76115 --- llvm/include/llvm/BinaryFormat/Dwarf.def | 2 +- llvm/include/llvm/IR/DIBuilder.h | 12 +++ llvm/lib/IR/DIBuilder.cpp | 12 +++ llvm/lib/IR/Verifier.cpp | 18 +++- llvm/test/DebugInfo/Generic/set.ll | 116 +++++++++++++++++++++++ llvm/test/Verifier/set1.ll | 63 ++++++++++++ llvm/unittests/IR/DebugInfoTest.cpp | 19 ++++ 7 files changed, 240 insertions(+), 2 deletions(-) create mode 100644 llvm/test/DebugInfo/Generic/set.ll create mode 100644 llvm/test/Verifier/set1.ll diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.def b/llvm/include/llvm/BinaryFormat/Dwarf.def index dedbaa184334..c863cf7d66b4 100644 --- a/llvm/include/llvm/BinaryFormat/Dwarf.def +++ b/llvm/include/llvm/BinaryFormat/Dwarf.def @@ -162,7 +162,7 @@ HANDLE_DW_TAG(0x001c, inheritance, 2, DWARF, DW_KIND_NONE) HANDLE_DW_TAG(0x001d, inlined_subroutine, 2, DWARF, DW_KIND_NONE) HANDLE_DW_TAG(0x001e, module, 2, DWARF, DW_KIND_NONE) HANDLE_DW_TAG(0x001f, ptr_to_member_type, 2, DWARF, DW_KIND_TYPE) -HANDLE_DW_TAG(0x0020, set_type, 2, DWARF, DW_KIND_NONE) +HANDLE_DW_TAG(0x0020, set_type, 2, DWARF, DW_KIND_TYPE) HANDLE_DW_TAG(0x0021, subrange_type, 2, DWARF, DW_KIND_TYPE) HANDLE_DW_TAG(0x0022, with_stmt, 2, DWARF, DW_KIND_NONE) HANDLE_DW_TAG(0x0023, access_declaration, 2, DWARF, DW_KIND_NONE) diff --git a/llvm/include/llvm/IR/DIBuilder.h b/llvm/include/llvm/IR/DIBuilder.h index e0238567f251..9cb1729cfe9b 100644 --- a/llvm/include/llvm/IR/DIBuilder.h +++ b/llvm/include/llvm/IR/DIBuilder.h @@ -538,6 +538,18 @@ namespace llvm { uint64_t SizeInBits, uint32_t AlignInBits, DINodeArray Elements, DIType *UnderlyingType, StringRef UniqueIdentifier = "", bool IsScoped = false); + /// Create debugging information entry for a set. + /// \param Scope Scope in which this set is defined. + /// \param Name Set name. + /// \param File File where this set is defined. + /// \param LineNumber Line number. + /// \param SizeInBits Set size. + /// \param AlignInBits Set alignment. + /// \param Ty Base type of the set. + DIDerivedType *createSetType(DIScope *Scope, StringRef Name, DIFile *File, + unsigned LineNo, uint64_t SizeInBits, + uint32_t AlignInBits, DIType *Ty); + /// Create subroutine type. /// \param ParameterTypes An array of subroutine parameter types. This /// includes return type at 0th index. diff --git a/llvm/lib/IR/DIBuilder.cpp b/llvm/lib/IR/DIBuilder.cpp index 5104dc349d0b..1256749b7497 100644 --- a/llvm/lib/IR/DIBuilder.cpp +++ b/llvm/lib/IR/DIBuilder.cpp @@ -525,6 +525,18 @@ DICompositeType *DIBuilder::createEnumerationType( return CTy; } +DIDerivedType *DIBuilder::createSetType(DIScope *Scope, StringRef Name, + DIFile *File, unsigned LineNo, + uint64_t SizeInBits, + uint32_t AlignInBits, DIType *Ty) { + auto *R = + DIDerivedType::get(VMContext, dwarf::DW_TAG_set_type, Name, File, LineNo, + getNonCompileUnitScope(Scope), Ty, SizeInBits, + AlignInBits, 0, None, DINode::FlagZero); + trackIfUnresolved(R); + return R; +} + DICompositeType *DIBuilder::createArrayType( uint64_t Size, uint32_t AlignInBits, DIType *Ty, DINodeArray Subscripts, PointerUnion DL, diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index b6952f703041..84f95f726b8e 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -1000,13 +1000,29 @@ void Verifier::visitDIDerivedType(const DIDerivedType &N) { N.getTag() == dwarf::DW_TAG_atomic_type || N.getTag() == dwarf::DW_TAG_member || N.getTag() == dwarf::DW_TAG_inheritance || - N.getTag() == dwarf::DW_TAG_friend, + N.getTag() == dwarf::DW_TAG_friend || + N.getTag() == dwarf::DW_TAG_set_type, "invalid tag", &N); if (N.getTag() == dwarf::DW_TAG_ptr_to_member_type) { AssertDI(isType(N.getRawExtraData()), "invalid pointer to member type", &N, N.getRawExtraData()); } + if (N.getTag() == dwarf::DW_TAG_set_type) { + if (auto *T = N.getRawBaseType()) { + auto *Enum = dyn_cast_or_null(T); + auto *Basic = dyn_cast_or_null(T); + AssertDI( + (Enum && Enum->getTag() == dwarf::DW_TAG_enumeration_type) || + (Basic && (Basic->getEncoding() == dwarf::DW_ATE_unsigned || + Basic->getEncoding() == dwarf::DW_ATE_signed || + Basic->getEncoding() == dwarf::DW_ATE_unsigned_char || + Basic->getEncoding() == dwarf::DW_ATE_signed_char || + Basic->getEncoding() == dwarf::DW_ATE_boolean)), + "invalid set base type", &N, T); + } + } + AssertDI(isScope(N.getRawScope()), "invalid scope", &N, N.getRawScope()); AssertDI(isType(N.getRawBaseType()), "invalid base type", &N, N.getRawBaseType()); diff --git a/llvm/test/DebugInfo/Generic/set.ll b/llvm/test/DebugInfo/Generic/set.ll new file mode 100644 index 000000000000..21a106713d2f --- /dev/null +++ b/llvm/test/DebugInfo/Generic/set.ll @@ -0,0 +1,116 @@ +; Test set representation in DWARF debug info: + +; RUN: llc -debugger-tune=gdb -dwarf-version=4 -filetype=obj -o %t.o < %s +; RUN: llvm-dwarfdump -debug-info %t.o | FileCheck %s --check-prefix=CHECK + +; ModuleID = 'Main.mb' +source_filename = "../src/Main.m3" +target datalayout = "e-m:e-p:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" + +%M_Const_struct = type { [7 x i8], [1 x i8], [4 x i8], [4 x i8], i8* (i64)*, i8*, void ()*, i8*, [8 x i8], [14 x i8], [2 x i8] } +%M_Main_struct = type { i8*, [32 x i8], i8*, [24 x i8], i8*, [8 x i8], i8* (i64)*, i64, [8 x i8], i8* ()*, i8*, [8 x i8], i8* ()*, [8 x i8] } + +@M_Const = internal constant %M_Const_struct { [7 x i8] c"Main_M3", [1 x i8] zeroinitializer, [4 x i8] c"Test", [4 x i8] zeroinitializer, i8* (i64)* @Main_M3, i8* getelementptr inbounds (%M_Const_struct, %M_Const_struct* @M_Const, i32 0, i32 0, i32 0), void ()* @Main__Test, i8* getelementptr inbounds (i8, i8* getelementptr inbounds (%M_Const_struct, %M_Const_struct* @M_Const, i32 0, i32 0, i32 0), i64 8), [8 x i8] zeroinitializer, [14 x i8] c"../src/Main.m3", [2 x i8] zeroinitializer }, align 8 +@M_Main = internal global %M_Main_struct { i8* getelementptr inbounds (i8, i8* getelementptr inbounds (%M_Const_struct, %M_Const_struct* @M_Const, i32 0, i32 0, i32 0), i64 56), [32 x i8] zeroinitializer, i8* getelementptr inbounds (i8, i8* getelementptr inbounds (%M_Const_struct, %M_Const_struct* @M_Const, i32 0, i32 0, i32 0), i64 16), [24 x i8] zeroinitializer, i8* getelementptr inbounds (i8, i8* bitcast (%M_Main_struct* @M_Main to i8*), i64 104), [8 x i8] zeroinitializer, i8* (i64)* @Main_M3, i64 3, [8 x i8] zeroinitializer, i8* ()* @Main_I3, i8* getelementptr inbounds (i8, i8* bitcast (%M_Main_struct* @M_Main to i8*), i64 128), [8 x i8] zeroinitializer, i8* ()* @RTHooks_I3, [8 x i8] zeroinitializer }, align 8 +@m3_jmpbuf_size = external global i64, align 8 + +declare i8* @Main_I3() + +declare i8* @RTHooks_I3() + +; Function Attrs: uwtable +define void @Main__Test() #0 !dbg !5 { +entry: + %as = alloca i64, align 8 + %bs = alloca i64, align 8 + br label %second, !dbg !21 + +second: ; preds = %entry + call void @llvm.dbg.declare(metadata i64* %as, metadata !22, metadata !DIExpression()), !dbg !25 + call void @llvm.dbg.declare(metadata i64* %bs, metadata !26, metadata !DIExpression()), !dbg !25 + store i64 36028797018972298, i64* %as, align 8, !dbg !28 + store i64 197, i64* %bs, align 8, !dbg !29 + ret void, !dbg !21 +} + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +declare i8* @alloca() + +; Function Attrs: uwtable +define i8* @Main_M3(i64 %mode) #0 !dbg !30 { +entry: + %mode1 = alloca i64, align 8 + store i64 %mode, i64* %mode1, align 8 + br label %second, !dbg !36 + +second: ; preds = %entry + call void @llvm.dbg.declare(metadata i64* %mode1, metadata !37, metadata !DIExpression()), !dbg !38 + %v.3 = load i64, i64* %mode1, align 8, !dbg !38 + %icmp = icmp eq i64 %v.3, 0, !dbg !38 + br i1 %icmp, label %if_1, label %else_1, !dbg !38 + +else_1: ; preds = %second + call void @Main__Test(), !dbg !36 + br label %if_1, !dbg !36 + +if_1: ; preds = %else_1, %second + ret i8* bitcast (%M_Main_struct* @M_Main to i8*), !dbg !36 +} + +attributes #0 = { uwtable "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nofree nosync nounwind readnone speculatable willreturn } + +!llvm.ident = !{!0} +!llvm.dbg.cu = !{!1} +!llvm.module.flags = !{!18, !19, !20} + +!0 = !{!"versions- cm3: d5.10.0 llvm: 9.0"} +!1 = distinct !DICompileUnit(language: DW_LANG_Modula3, file: !2, producer: "cm3", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !3) +!2 = !DIFile(filename: "Main.m3", directory: "/home/cm3/settest/src") +!3 = !{!4} +!4 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "Enum", scope: !5, file: !2, line: 11, size: 8, align: 8, elements: !9) +!5 = distinct !DISubprogram(name: "Test", linkageName: "Main__Test", scope: !2, file: !2, line: 11, type: !6, scopeLine: 11, spFlags: DISPFlagDefinition, unit: !1, retainedNodes: !8) +!6 = !DISubroutineType(types: !7) +!7 = !{null} +!8 = !{} +!9 = !{!10, !11, !12, !13, !14, !15, !16, !17} +!10 = !DIEnumerator(name: "alpha", value: 0) +!11 = !DIEnumerator(name: "beta", value: 1) +!12 = !DIEnumerator(name: "gamma", value: 2) +!13 = !DIEnumerator(name: "delta", value: 3) +!14 = !DIEnumerator(name: "epsilon", value: 4) +!15 = !DIEnumerator(name: "theta", value: 5) +!16 = !DIEnumerator(name: "psi", value: 6) +!17 = !DIEnumerator(name: "zeta", value: 7) +!18 = !{i64 2, !"Dwarf Version", i64 4} +!19 = !{i64 2, !"Debug Info Version", i64 3} +!20 = !{i64 2, !"wchar_size", i64 2} +!21 = !DILocation(line: 20, scope: !5) +!22 = !DILocalVariable(name: "as", scope: !5, file: !2, line: 11, type: !23) +!23 = !DIDerivedType(tag: DW_TAG_set_type, name: "SS", scope: !2, file: !2, line: 11, baseType: !24, size: 64, align: 64) +!24 = !DIBasicType(name: "SR", size: 8, encoding: DW_ATE_signed) +; CHECK: DW_TAG_set_type +; CHECK: DW_AT_type{{.*}}"SR" +; CHECK: DW_AT_name ("SS") +; CHECK: DW_AT_byte_size (0x08) +!25 = !DILocation(line: 11, scope: !5) +!26 = !DILocalVariable(name: "bs", scope: !5, file: !2, line: 11, type: !27) +!27 = !DIDerivedType(tag: DW_TAG_set_type, name: "ST", scope: !2, file: !2, line: 11, baseType: !4, size: 64, align: 64) +; CHECK: DW_TAG_set_type +; CHECK: DW_AT_type{{.*}}"Enum" +; CHECK: DW_AT_name ("ST") +; CHECK: DW_AT_byte_size (0x08) +!28 = !DILocation(line: 17, scope: !5) +!29 = !DILocation(line: 18, scope: !5) +!30 = distinct !DISubprogram(name: "Main_M3", linkageName: "Main_M3", scope: !2, file: !2, line: 22, type: !31, scopeLine: 22, spFlags: DISPFlagDefinition, unit: !1, retainedNodes: !8) +!31 = !DISubroutineType(types: !32) +!32 = !{!33, !35} +!33 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "ADDR", baseType: !34, size: 64, align: 64) +!34 = !DICompositeType(tag: DW_TAG_class_type, name: "ADDR__HeapObject", scope: !5, file: !2, line: 22, size: 64, align: 64, elements: !7, identifier: "AJWxb1") +!35 = !DIBasicType(name: "INTEGER", size: 64, encoding: DW_ATE_signed) +!36 = !DILocation(line: 23, scope: !30) +!37 = !DILocalVariable(name: "mode", arg: 1, scope: !30, file: !2, line: 22, type: !35) +!38 = !DILocation(line: 22, scope: !30) diff --git a/llvm/test/Verifier/set1.ll b/llvm/test/Verifier/set1.ll new file mode 100644 index 000000000000..9f8f116b9e37 --- /dev/null +++ b/llvm/test/Verifier/set1.ll @@ -0,0 +1,63 @@ +; RUN: llvm-as -disable-output <%s 2>&1 | FileCheck %s + +define void @Main__Test() #0 !dbg !17 { +entry: + %as = alloca i64, align 8 + %bs = alloca i64, align 8 + br label %second, !dbg !21 + +second: ; preds = %entry + call void @llvm.dbg.declare(metadata i64* %as, metadata !22, metadata !DIExpression()), !dbg !25 + call void @llvm.dbg.declare(metadata i64* %bs, metadata !26, metadata !DIExpression()), !dbg !25 + store i64 36028797018972298, i64* %as, align 8, !dbg !28 + store i64 85, i64* %bs, align 8, !dbg !29 + ret void, !dbg !21 +} + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +!llvm.ident = !{!0} +!llvm.dbg.cu = !{!1} +!llvm.module.flags = !{!14, !15, !16} + +!0 = !{!"versions- cm3: d5.10.0 llvm: 12.0"} +!1 = distinct !DICompileUnit(language: DW_LANG_Modula3, file: !2, producer: "cm3", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !3) +!2 = !DIFile(filename: "Main.m3", directory: "/home/peter/cm3/settest/src") +!3 = !{!4} +!4 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "Enum", scope: !2, file: !2, line: 11, size: 8, align: 8, elements: !5) +!5 = !{!6, !7, !8, !9, !10, !11, !12, !13} +!6 = !DIEnumerator(name: "alpha", value: 0) +!7 = !DIEnumerator(name: "beta", value: 1) +!8 = !DIEnumerator(name: "gamma", value: 2) +!9 = !DIEnumerator(name: "delta", value: 3) +!10 = !DIEnumerator(name: "epsilon", value: 4) +!11 = !DIEnumerator(name: "theta", value: 5) +!12 = !DIEnumerator(name: "psi", value: 6) +!13 = !DIEnumerator(name: "zeta", value: 7) +!14 = !{i64 2, !"Dwarf Version", i64 4} +!15 = !{i64 2, !"Debug Info Version", i64 3} +!16 = !{i64 2, !"wchar_size", i64 2} +!17 = distinct !DISubprogram(name: "Test", linkageName: "Main__Test", scope: !2, file: !2, line: 11, type: !18, scopeLine: 11, spFlags: DISPFlagDefinition, unit: !1, retainedNodes: !20) +!18 = !DISubroutineType(types: !19) +!19 = !{null} +!20 = !{} +!21 = !DILocation(line: 20, scope: !17) +!22 = !DILocalVariable(name: "as", scope: !17, file: !2, line: 11, type: !23) +; CHECK: invalid set base type +!23 = !DIDerivedType(tag: DW_TAG_set_type, name: "SS", scope: !2, file: !2, line: 11, baseType: !24, size: 64, align: 64) +!24 = !DIBasicType(name: "SR", size: 8, encoding: DW_ATE_signed) +!25 = !DILocation(line: 11, scope: !17) +!26 = !DILocalVariable(name: "bs", scope: !17, file: !2, line: 11, type: !27) +!27 = !DIDerivedType(tag: DW_TAG_set_type, name: "ST", scope: !2, file: !2, line: 11, baseType: !23, size: 64, align: 64) +!28 = !DILocation(line: 17, scope: !17) +!29 = !DILocation(line: 18, scope: !17) +!30 = distinct !DISubprogram(name: "Main_M3", linkageName: "Main_M3", scope: !2, file: !2, line: 22, type: !31, scopeLine: 22, spFlags: DISPFlagDefinition, unit: !1, retainedNodes: !20) +!31 = !DISubroutineType(types: !32) +!32 = !{!33, !35} +!33 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "ADDR", baseType: !34, size: 64, align: 64) +!34 = !DICompositeType(tag: DW_TAG_class_type, name: "ADDR__HeapObject", scope: !2, file: !2, line: 22, size: 64, align: 64, elements: !19, identifier: "AJWxb1") +!35 = !DIBasicType(name: "INTEGER", size: 64, encoding: DW_ATE_signed) +!36 = !DILocation(line: 23, scope: !30) +!37 = !DILocalVariable(name: "mode", arg: 1, scope: !30, file: !2, line: 22, type: !35) +!38 = !DILocation(line: 22, scope: !30) diff --git a/llvm/unittests/IR/DebugInfoTest.cpp b/llvm/unittests/IR/DebugInfoTest.cpp index 58936fb2b00c..d3e025a6edce 100644 --- a/llvm/unittests/IR/DebugInfoTest.cpp +++ b/llvm/unittests/IR/DebugInfoTest.cpp @@ -230,4 +230,23 @@ TEST(DIBuilder, CreateFortranArrayTypeWithAttributes) { DIVariable::deleteTemporary(DataLocation); } +DISubprogram *getSubprogram() { + LLVMContext Context; + return DISubprogram::getDistinct(Context, nullptr, "", "", nullptr, 0, + nullptr, 0, nullptr, 0, 0, DINode::FlagZero, + DISubprogram::SPFlagZero, nullptr); +} + +TEST(DIBuilder, CreateSetType) { + LLVMContext Ctx; + std::unique_ptr M(new Module("MyModule", Ctx)); + DIBuilder DIB(*M); + DIScope *Scope = getSubprogram(); + DIType *Type = DIB.createBasicType("Int", 64, dwarf::DW_ATE_signed); + DIFile *F = DIB.createFile("main.c", "/"); + + DIDerivedType *SetType = DIB.createSetType(Scope, "set1", F, 1, 64, 64, Type); + EXPECT_TRUE(isa_and_nonnull(SetType)); +} + } // end namespace