Index: lib/CodeGen/AsmPrinter/DwarfDebug.h =================================================================== --- lib/CodeGen/AsmPrinter/DwarfDebug.h +++ lib/CodeGen/AsmPrinter/DwarfDebug.h @@ -209,6 +209,7 @@ /// Collection of abstract variables. DenseMap> AbstractVariables; + unsigned PrevAVSize; SmallVector, 64> ConcreteVariables; /// Collection of DebugLocEntry. Stored in a linked list so that DIELocLists Index: lib/CodeGen/AsmPrinter/DwarfDebug.cpp =================================================================== --- lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -1145,6 +1145,11 @@ if (LScopes.empty()) return; + // We should not see any new abstract variables, in a -gmlt compilation where + // the function has no abstract scopes. Remember the current size so we can + // verify this. + PrevAVSize = AbstractVariables.size(); + // Set DwarfDwarfCompileUnitID in MCContext to the Compile Unit this function // belongs to so that we add to the correct per-cu line table in the // non-asm case. @@ -1217,10 +1222,13 @@ TheCU.getCUNode()->getEmissionKind() == DICompileUnit::LineTablesOnly && LScopes.getAbstractScopesList().empty() && !IsDarwin) { assert(InfoHolder.getScopeVariables().empty()); - assert(DbgValues.empty()); - // FIXME: This wouldn't be true in LTO with a -g (with inlining) CU followed - // by a -gmlt CU. Add a test and remove this assertion. - assert(AbstractVariables.empty()); + // With LTO, a function compiled -gmlt could inline a function compiled -g, + // which could have all real instructions optimized/folded away, which in + // turn means no abstract scopes would be created. But the DbgValues will + // still be there. Forget them. + DbgValues.clear(); + // With -gmlt and no inlined scopes, expect no new abstract variables. + assert(AbstractVariables.size() == PrevAVSize); PrevLabel = nullptr; CurFn = nullptr; DebugHandlerBase::endFunction(MF); Index: test/DebugInfo/X86/dbg-abstract-vars-g-gmlt.ll =================================================================== --- test/DebugInfo/X86/dbg-abstract-vars-g-gmlt.ll +++ test/DebugInfo/X86/dbg-abstract-vars-g-gmlt.ll @@ -0,0 +1,161 @@ +; RUN: llc < %s -filetype=obj | llvm-dwarfdump - -debug-dump=info | FileCheck %s +; +; The IR file was created as follows: +; +; clang -fno-exceptions -O2 -emit-llvm -S -DP1 -g AbstVar.cpp -o AbstVar-1.ll +; clang -fno-exceptions -O2 -emit-llvm -S -gmlt AbstVar.cpp -o AbstVar-2.ll +; llvm-link AbstVar-1.ll AbstVar-2.ll -S -o AbstVar-link.ll +; opt -std-link-opts AbstVar-link.ll -S -o AbstVar-opt.ll +; +; The resulting IR file can then be hand-edited to remove the definition of +; _ZN5AlphaC2Ev and its alias _ZN5AlphaC1Ev (the ctors for "Alpha") along with +; some irrelevant function attributes. +; +; The important point about the IR is that the _GLOBAL_... function has inlined +; a call to _ZN5AlphaC2Ev, with reference to its "this" parameter, which will +; cause us to create an abstract scope containing the abstract variable; and +; later, we have function _ZN5BravoD2Ev, which was compiled -gmlt, but should +; not mind that there are abstract variables from the prior function. +; +; We verify that debug info for _ZN5AlphaC2Ev appears, with its parameter, +; and that later we have an inlined scope referring to this info. +; +; CHECK: 0x[[ALPHA:[0-9a-f]+]]: DW_TAG_subprogram +; CHECK-NEXT: DW_AT_name {{.*}} "Alpha" +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_TAG_formal_parameter +; +; CHECK: 0x[[ALPHAC:[0-9a-f]+]]: DW_TAG_subprogram +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_linkage_name {{.*}} "_ZN5AlphaC2Ev" +; CHECK-NEXT: DW_AT_specification {{.*}} {0x[[ALPHA]]} +; +; CHECK: DW_AT_abstract_origin {{.*}}[[ALPHAC]] "_ZN5AlphaC2Ev" + +; AbstVar.cpp: +; -------------------------------------------------- +; struct Alpha { +; Alpha(); +; int m_a; +; }; +; #ifdef P1 +; Alpha::Alpha() : m_a(0) {} +; #else +; extern void foo(void); +; struct Bravo { +; ~Bravo() { foo(); } +; }; +; struct Charlie { +; Charlie() { m_c = new Alpha(); } +; Alpha* m_c; +; static Bravo s_b; +; }; +; Bravo Charlie::s_b; +; Charlie iCharlie; +; #endif + +; ModuleID = 'AbstVar-link.ll' +source_filename = "llvm-link" +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%struct.Bravo = type { i8 } +%struct.Charlie = type { %struct.Alpha* } +%struct.Alpha = type { i32 } + +$_ZN5BravoD2Ev = comdat any + +@_ZN7Charlie3s_bE = global %struct.Bravo zeroinitializer, align 1 +@iCharlie = local_unnamed_addr global %struct.Charlie zeroinitializer, align 8 +@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @_GLOBAL__sub_I_AbstVar.cpp, i8* null }] +@__dso_handle = external global i8 + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.value(metadata, i64, metadata, metadata) #1 + +; Function Attrs: nounwind uwtable +define internal void @_GLOBAL__sub_I_AbstVar.cpp() #0 section ".text.startup" !dbg !28 { +entry: + %0 = tail call i32 @__cxa_atexit(void (i8*)* bitcast (void (%struct.Bravo*)* @_ZN5BravoD2Ev to void (i8*)*), i8* getelementptr inbounds (%struct.Bravo, %struct.Bravo* @_ZN7Charlie3s_bE, i64 0, i32 0), i8* nonnull @__dso_handle) #2, !dbg !30 + %call.i.i = tail call i8* @_Znwm(i64 4) #5, !dbg !33 + %m_a.i = bitcast i8* %call.i.i to i32*, !dbg !39 + store i32 0, i32* %m_a.i, align 4, !dbg !39, !tbaa !22 + store i8* %call.i.i, i8** bitcast (%struct.Charlie* @iCharlie to i8**), align 8, !dbg !42, !tbaa !43 + ret void +} + +; Function Attrs: nounwind uwtable +define linkonce_odr void @_ZN5BravoD2Ev(%struct.Bravo* %this) unnamed_addr #0 comdat align 2 !dbg !46 { +entry: + tail call void @_Z3foov() #2, !dbg !47 + ret void, !dbg !48 +} + +; Function Attrs: nounwind +declare i32 @__cxa_atexit(void (i8*)*, i8*, i8*) local_unnamed_addr #2 + +; Function Attrs: nobuiltin +declare noalias nonnull i8* @_Znwm(i64) local_unnamed_addr #3 + +declare void @_Z3foov() local_unnamed_addr #4 + +attributes #0 = { nounwind uwtable } +attributes #1 = { nounwind readnone } +attributes #2 = { nounwind } +attributes #3 = { nobuiltin } +attributes #4 = { nounwind } +attributes #5 = { builtin nounwind } + +!llvm.dbg.cu = !{!0, !3} +!llvm.ident = !{!4, !4} +!llvm.module.flags = !{!5, !6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 5.0.0 (trunk 293467)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) +!1 = !DIFile(filename: "AbstVar.cpp", directory: "/home/probinson/projects/scratch/pr31437") +!2 = !{} +!3 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 5.0.0 (trunk 293467)", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, enums: !2) +!4 = !{!"clang version 5.0.0 (trunk 293467)"} +!5 = !{i32 2, !"Dwarf Version", i32 4} +!6 = !{i32 2, !"Debug Info Version", i32 3} +!7 = distinct !DISubprogram(name: "Alpha", linkageName: "_ZN5AlphaC2Ev", scope: !8, file: !1, line: 6, type: !13, isLocal: false, isDefinition: true, scopeLine: 6, flags: DIFlagPrototyped, isOptimized: true, unit: !0, declaration: !12, variables: !16) +!8 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Alpha", file: !1, line: 1, size: 32, elements: !9, identifier: "_ZTS5Alpha") +!9 = !{!10, !12} +!10 = !DIDerivedType(tag: DW_TAG_member, name: "m_a", scope: !8, file: !1, line: 3, baseType: !11, size: 32) +!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!12 = !DISubprogram(name: "Alpha", scope: !8, file: !1, line: 2, type: !13, isLocal: false, isDefinition: false, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: true) +!13 = !DISubroutineType(types: !14) +!14 = !{null, !15} +!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !8, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!16 = !{!17} +!17 = !DILocalVariable(name: "this", arg: 1, scope: !7, type: !18, flags: DIFlagArtificial | DIFlagObjectPointer) +!18 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !8, size: 64) +!19 = !DIExpression() +!20 = !DILocation(line: 0, scope: !7) +!21 = !DILocation(line: 6, column: 18, scope: !7) +!22 = !{!23, !24, i64 0} +!23 = !{!"_ZTS5Alpha", !24, i64 0} +!24 = !{!"int", !25, i64 0} +!25 = !{!"omnipotent char", !26, i64 0} +!26 = !{!"Simple C++ TBAA"} +!27 = !DILocation(line: 6, column: 26, scope: !7) +!28 = distinct !DISubprogram(linkageName: "_GLOBAL__sub_I_AbstVar.cpp", scope: !1, file: !1, type: !29, isLocal: true, isDefinition: true, flags: DIFlagArtificial, isOptimized: true, unit: !3, variables: !2) +!29 = !DISubroutineType(types: !2) +!30 = !DILocation(line: 17, column: 16, scope: !31, inlinedAt: !32) +!31 = distinct !DISubprogram(name: "__cxx_global_var_init", scope: !1, file: !1, line: 17, type: !29, isLocal: true, isDefinition: true, scopeLine: 17, flags: DIFlagPrototyped, isOptimized: true, unit: !3, variables: !2) +!32 = distinct !DILocation(line: 0, scope: !28) +!33 = !DILocation(line: 13, column: 21, scope: !34, inlinedAt: !35) +!34 = distinct !DISubprogram(name: "Charlie", scope: !1, file: !1, line: 13, type: !29, isLocal: false, isDefinition: true, scopeLine: 13, flags: DIFlagPrototyped, isOptimized: true, unit: !3, variables: !2) +!35 = distinct !DILocation(line: 18, column: 9, scope: !36, inlinedAt: !37) +!36 = distinct !DISubprogram(name: "__cxx_global_var_init.1", scope: !1, file: !1, line: 18, type: !29, isLocal: true, isDefinition: true, scopeLine: 18, flags: DIFlagPrototyped, isOptimized: true, unit: !3, variables: !2) +!37 = distinct !DILocation(line: 0, scope: !38) +!38 = !DILexicalBlockFile(scope: !28, file: !1, discriminator: 1) +!39 = !DILocation(line: 6, column: 18, scope: !7, inlinedAt: !40) +!40 = distinct !DILocation(line: 13, column: 25, scope: !41, inlinedAt: !35) +!41 = !DILexicalBlockFile(scope: !34, file: !1, discriminator: 1) +!42 = !DILocation(line: 13, column: 19, scope: !34, inlinedAt: !35) +!43 = !{!44, !45, i64 0} +!44 = !{!"_ZTS7Charlie", !45, i64 0} +!45 = !{!"any pointer", !25, i64 0} +!46 = distinct !DISubprogram(name: "~Bravo", scope: !1, file: !1, line: 10, type: !29, isLocal: false, isDefinition: true, scopeLine: 10, flags: DIFlagPrototyped, isOptimized: true, unit: !3, variables: !2) +!47 = !DILocation(line: 10, column: 14, scope: !46) +!48 = !DILocation(line: 10, column: 21, scope: !46) Index: test/DebugInfo/X86/dbg-value-g-gmlt.ll =================================================================== --- test/DebugInfo/X86/dbg-value-g-gmlt.ll +++ test/DebugInfo/X86/dbg-value-g-gmlt.ll @@ -0,0 +1,111 @@ +; RUN: llc -filetype=obj < %s | llvm-dwarfdump - -debug-dump=info | FileCheck %s +; +; If a function compiled -gmlt inlines a function compiled -g, and the inlined +; function is folded/optimized away, it could have a dangling dbg.value +; instruction with no proper scope. Make sure we don't create an empty scope, +; or crash due to the lack of a scope. +; +; IR file generated as follows: +; clang -c -g -O2 -emit-llvm -S foo.cpp -o foo.ll +; clang -c -gmlt -O2 -emit-llvm -S bar.cpp -o bar.ll +; # Order matters, bar.ll must go first. +; llvm-link bar.ll foo.ll -S -o link-mix.ll +; opt link-mix.ll -std-link-opts -S -o opt-mix.ll +; The opt-mix.ll file can then be hand-edited to remove function foo and +; eliminate a pile of unnecessary function attributes. +; That probably would allow removing some of the debug-info metadata too, +; if you were really ambitious. +; +; The important points about the test IR are that: +; - bar() has inlined foo(); +; - the inlined copy of foo() is optimized down to a GEP constant expression; +; - bar() still has a dbg.value call for foo's formal parameter. +; +; foo.cpp: +; -------------------------------------------------- +; static const char *default_value = "Brexit"; +; static const char *strings[2] = {"Ciao", "Gatto"}; +; const char * foo(int param) { +; switch (param) { +; case 0: return strings[0]; +; } +; return default_value; +; } +; -------------------------------------------------- +; bar.cpp: +; -------------------------------------------------- +; extern const char * foo(int); +; const char *bar() { +; return foo(0); +; } +; -------------------------------------------------- + +; Because bar.cpp was compiled -gmlt and has no inlined scopes, +; its compilation unit should have nothing in it. +; CHECK: DW_TAG_compile_unit +; CHECK-NOT: DW_TAG +; CHECK: DW_AT_name {{.*}} "bar.cpp" +; CHECK-NOT: DW_TAG +; CHECK: DW_TAG_compile_unit + +; ModuleID = 'link-mix.ll' +source_filename = "llvm-link" +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@.str = private unnamed_addr constant [5 x i8] c"Ciao\00", align 1 +@.str.2 = private unnamed_addr constant [7 x i8] c"Brexit\00", align 1 + +; Function Attrs: nounwind readnone uwtable +define i8* @_Z3barv() local_unnamed_addr #0 !dbg !19 { +entry: + tail call void @llvm.dbg.value(metadata i32 0, i64 0, metadata !21, metadata !27), !dbg !28 + ret i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), !dbg !30 +} + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.value(metadata, i64, metadata, metadata) #1 + +attributes #0 = { nounwind readnone } +attributes #1 = { nounwind readnone } + +!llvm.dbg.cu = !{!0, !3} +!llvm.ident = !{!16, !16} +!llvm.module.flags = !{!17, !18} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 5.0.0 (trunk 293467)", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, enums: !2) +!1 = !DIFile(filename: "bar.cpp", directory: "/home/probinson/projects/scratch/pr31437") +!2 = !{} +!3 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !4, producer: "clang version 5.0.0 (trunk 293467)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, globals: !5) +!4 = !DIFile(filename: "foo.cpp", directory: "/home/probinson/projects/scratch/pr31437") +!5 = !{!6, !14} +!6 = !DIGlobalVariableExpression(var: !7) +!7 = distinct !DIGlobalVariable(name: "strings", linkageName: "_ZL7strings", scope: !3, file: !4, line: 2, type: !8, isLocal: true, isDefinition: true) +!8 = !DICompositeType(tag: DW_TAG_array_type, baseType: !9, size: 128, elements: !12) +!9 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !10, size: 64) +!10 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !11) +!11 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) +!12 = !{!13} +!13 = !DISubrange(count: 2) +!14 = !DIGlobalVariableExpression(var: !15) +!15 = distinct !DIGlobalVariable(name: "default_value", linkageName: "_ZL13default_value", scope: !3, file: !4, line: 1, type: !9, isLocal: true, isDefinition: true) +!16 = !{!"clang version 5.0.0 (trunk 293467)"} +!17 = !{i32 2, !"Dwarf Version", i32 4} +!18 = !{i32 2, !"Debug Info Version", i32 3} +!19 = distinct !DISubprogram(name: "bar", scope: !1, file: !1, line: 2, type: !20, isLocal: false, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !2) +!20 = !DISubroutineType(types: !2) +!21 = !DILocalVariable(name: "param", arg: 1, scope: !22, file: !4, line: 3, type: !25) +!22 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !4, file: !4, line: 3, type: !23, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: true, unit: !3, variables: !26) +!23 = !DISubroutineType(types: !24) +!24 = !{!9, !25} +!25 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!26 = !{!21} +!27 = !DIExpression() +!28 = !DILocation(line: 3, column: 22, scope: !22, inlinedAt: !29) +!29 = distinct !DILocation(line: 3, column: 10, scope: !19) +!30 = !DILocation(line: 3, column: 3, scope: !19) +!31 = !DILocation(line: 3, column: 22, scope: !22) +!32 = !DILocation(line: 4, column: 3, scope: !22) +!33 = !DILocation(line: 5, column: 11, scope: !34) +!34 = distinct !DILexicalBlock(scope: !22, file: !4, line: 4, column: 18) +!35 = !DILocation(line: 8, column: 1, scope: !22)