From 1c65094d5b578789e36ecb02e45232324c03afdd Mon Sep 17 00:00:00 2001 From: Frederic Riss Date: Tue, 21 Jul 2015 22:41:43 +0000 Subject: [PATCH] [dsymutil] Implement ODR uniquing for C++ code. This optimization allows the DWARF linker to reuse definition of types it has emitted in previous CUs rather than reemitting them in each CU that references them. The size and link time gains are huge. For example when linking the DWARF for a debug build of clang, this generates a ~150M dwarf file instead of a ~700M one (the numbers date back a bit and must not be totally accurate these days). As with all the other parts of the llvm-dsymutil codebase, the goal is to keep bit-for-bit compatibility with dsymutil-classic. The code is littered with a lot of FIXMEs that should be addressed once we can get rid of the compatibilty goal. llvm-svn: 242847 --- llvm/test/tools/dsymutil/Inputs/odr-types.h | 51 +++ llvm/test/tools/dsymutil/Inputs/odr1.cpp | 11 + llvm/test/tools/dsymutil/Inputs/odr1.ll | 153 ++++++++ llvm/test/tools/dsymutil/Inputs/odr2.cpp | 7 + llvm/test/tools/dsymutil/Inputs/odr2.ll | 99 ++++++ llvm/test/tools/dsymutil/Inputs/odr3.cpp | 7 + llvm/test/tools/dsymutil/Inputs/odr3.ll | 106 ++++++ llvm/test/tools/dsymutil/Inputs/odr4.cpp | 8 + llvm/test/tools/dsymutil/Inputs/odr4.ll | 43 +++ llvm/test/tools/dsymutil/Inputs/odr5.cpp | 7 + llvm/test/tools/dsymutil/Inputs/odr5.ll | 101 ++++++ llvm/test/tools/dsymutil/Inputs/odr6.cpp | 7 + llvm/test/tools/dsymutil/Inputs/odr6.ll | 100 ++++++ llvm/test/tools/dsymutil/Inputs/odr7.cpp | 5 + llvm/test/tools/dsymutil/Inputs/odr7.ll | 62 ++++ llvm/test/tools/dsymutil/X86/odr-1.test | 423 ++++++++++++++++++++++ llvm/tools/dsymutil/DwarfLinker.cpp | 524 +++++++++++++++++++++++++--- llvm/tools/dsymutil/dsymutil.cpp | 6 + llvm/tools/dsymutil/dsymutil.h | 1 + 19 files changed, 1680 insertions(+), 41 deletions(-) create mode 100644 llvm/test/tools/dsymutil/Inputs/odr-types.h create mode 100644 llvm/test/tools/dsymutil/Inputs/odr1.cpp create mode 100644 llvm/test/tools/dsymutil/Inputs/odr1.ll create mode 100644 llvm/test/tools/dsymutil/Inputs/odr2.cpp create mode 100644 llvm/test/tools/dsymutil/Inputs/odr2.ll create mode 100644 llvm/test/tools/dsymutil/Inputs/odr3.cpp create mode 100644 llvm/test/tools/dsymutil/Inputs/odr3.ll create mode 100644 llvm/test/tools/dsymutil/Inputs/odr4.cpp create mode 100644 llvm/test/tools/dsymutil/Inputs/odr4.ll create mode 100644 llvm/test/tools/dsymutil/Inputs/odr5.cpp create mode 100644 llvm/test/tools/dsymutil/Inputs/odr5.ll create mode 100644 llvm/test/tools/dsymutil/Inputs/odr6.cpp create mode 100644 llvm/test/tools/dsymutil/Inputs/odr6.ll create mode 100644 llvm/test/tools/dsymutil/Inputs/odr7.cpp create mode 100644 llvm/test/tools/dsymutil/Inputs/odr7.ll create mode 100644 llvm/test/tools/dsymutil/X86/odr-1.test diff --git a/llvm/test/tools/dsymutil/Inputs/odr-types.h b/llvm/test/tools/dsymutil/Inputs/odr-types.h new file mode 100644 index 0000000..09057dc --- /dev/null +++ b/llvm/test/tools/dsymutil/Inputs/odr-types.h @@ -0,0 +1,51 @@ +struct S { + int I; + + void incr() __attribute__((always_inline)) { I++; } + void incr(int Add) __attribute__((always_inline)) { I += Add; } + + typedef int SInt; + + struct Nested { + double D; + + template void init(T Val) { D = double(Val); } + }; + + Nested D; + +public: + int foo() { return I; } +}; + +typedef S AliasForS; + +namespace N { +class C { + AliasForS S; +}; +} + +namespace N { +namespace N { +class C { + int S; +}; +} +} + +namespace { + class AnonC { + }; +} + +union U { + class C {} C; + struct S {} S; +}; + +inline int func() { + struct CInsideFunc { int i; }; + auto functor = []() { CInsideFunc dummy; return dummy.i; }; + return functor(); +} diff --git a/llvm/test/tools/dsymutil/Inputs/odr1.cpp b/llvm/test/tools/dsymutil/Inputs/odr1.cpp new file mode 100644 index 0000000..722247b --- /dev/null +++ b/llvm/test/tools/dsymutil/Inputs/odr1.cpp @@ -0,0 +1,11 @@ +#include "odr-types.h" + +int foo() { + AliasForS s; + N::C nc; + N::N::C nnc; + AnonC ac; + U u; + + return func(); +} diff --git a/llvm/test/tools/dsymutil/Inputs/odr1.ll b/llvm/test/tools/dsymutil/Inputs/odr1.ll new file mode 100644 index 0000000..818e0ef --- /dev/null +++ b/llvm/test/tools/dsymutil/Inputs/odr1.ll @@ -0,0 +1,153 @@ +; Generated from odr1.cpp and odr-types.h by running: +; clang -emit-llvm -g -S -std=c++11 odr1.cpp +; ModuleID = 'odr1.cpp' +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.11.0" + +%struct.S = type { i32, %"struct.S::Nested" } +%"struct.S::Nested" = type { double } +%"class.N::C" = type { %struct.S } +%"class.N::N::C" = type { i32 } +%"class.(anonymous namespace)::AnonC" = type { i8 } +%union.U = type { %"class.U::C" } +%"class.U::C" = type { i8 } +%class.anon = type { i8 } +%struct.CInsideFunc = type { i32 } + +; Function Attrs: ssp uwtable +define i32 @_Z3foov() #0 { +entry: + %s = alloca %struct.S, align 8 + %nc = alloca %"class.N::C", align 8 + %nnc = alloca %"class.N::N::C", align 4 + %ac = alloca %"class.(anonymous namespace)::AnonC", align 1 + %u = alloca %union.U, align 1 + call void @llvm.dbg.declare(metadata %struct.S* %s, metadata !59, metadata !60), !dbg !61 + call void @llvm.dbg.declare(metadata %"class.N::C"* %nc, metadata !62, metadata !60), !dbg !63 + call void @llvm.dbg.declare(metadata %"class.N::N::C"* %nnc, metadata !64, metadata !60), !dbg !65 + call void @llvm.dbg.declare(metadata %"class.(anonymous namespace)::AnonC"* %ac, metadata !66, metadata !60), !dbg !69 + call void @llvm.dbg.declare(metadata %union.U* %u, metadata !70, metadata !60), !dbg !71 + %call = call i32 @_Z4funcv(), !dbg !72 + ret i32 %call, !dbg !73 +} + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +; Function Attrs: inlinehint ssp uwtable +define linkonce_odr i32 @_Z4funcv() #2 { +entry: + %functor = alloca %class.anon, align 1 + call void @llvm.dbg.declare(metadata %class.anon* %functor, metadata !74, metadata !60), !dbg !75 + %call = call i32 @_ZZ4funcvENKUlvE_clEv(%class.anon* %functor), !dbg !76 + ret i32 %call, !dbg !77 +} + +; Function Attrs: inlinehint nounwind ssp uwtable +define linkonce_odr i32 @_ZZ4funcvENKUlvE_clEv(%class.anon* %this) #3 align 2 { +entry: + %this.addr = alloca %class.anon*, align 8 + %dummy = alloca %struct.CInsideFunc, align 4 + store %class.anon* %this, %class.anon** %this.addr, align 8 + call void @llvm.dbg.declare(metadata %class.anon** %this.addr, metadata !78, metadata !60), !dbg !80 + %this1 = load %class.anon*, %class.anon** %this.addr + call void @llvm.dbg.declare(metadata %struct.CInsideFunc* %dummy, metadata !81, metadata !60), !dbg !82 + %i = getelementptr inbounds %struct.CInsideFunc, %struct.CInsideFunc* %dummy, i32 0, i32 0, !dbg !83 + %0 = load i32, i32* %i, align 4, !dbg !83 + ret i32 %0, !dbg !84 +} + +attributes #0 = { ssp uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+cx16,+sse,+sse2,+sse3,+ssse3" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind readnone } +attributes #2 = { inlinehint ssp uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+cx16,+sse,+sse2,+sse3,+ssse3" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #3 = { inlinehint nounwind ssp uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+cx16,+sse,+sse2,+sse3,+ssse3" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!55, !56, !57} +!llvm.ident = !{!58} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 3.8.0 (trunk 242534)", isOptimized: false, runtimeVersion: 0, emissionKind: 1, enums: !2, retainedTypes: !3, subprograms: !52) +!1 = !DIFile(filename: "odr1.cpp", directory: "/Inputs") +!2 = !{} +!3 = !{!4, !20, !24, !29, !33, !37, !38, !39, !49} +!4 = !DICompositeType(tag: DW_TAG_structure_type, name: "S", file: !5, line: 1, size: 128, align: 64, elements: !6, identifier: "_ZTS1S") +!5 = !DIFile(filename: "./odr-types.h", directory: "/Inputs") +!6 = !{!7, !9, !10, !14, !17} +!7 = !DIDerivedType(tag: DW_TAG_member, name: "I", scope: !"_ZTS1S", file: !5, line: 2, baseType: !8, size: 32, align: 32) +!8 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +!9 = !DIDerivedType(tag: DW_TAG_member, name: "D", scope: !"_ZTS1S", file: !5, line: 15, baseType: !"_ZTSN1S6NestedE", size: 64, align: 64, offset: 64) +!10 = !DISubprogram(name: "incr", linkageName: "_ZN1S4incrEv", scope: !"_ZTS1S", file: !5, line: 4, type: !11, isLocal: false, isDefinition: false, scopeLine: 4, flags: DIFlagPrototyped, isOptimized: false) +!11 = !DISubroutineType(types: !12) +!12 = !{null, !13} +!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !"_ZTS1S", size: 64, align: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!14 = !DISubprogram(name: "incr", linkageName: "_ZN1S4incrEi", scope: !"_ZTS1S", file: !5, line: 5, type: !15, isLocal: false, isDefinition: false, scopeLine: 5, flags: DIFlagPrototyped, isOptimized: false) +!15 = !DISubroutineType(types: !16) +!16 = !{null, !13, !8} +!17 = !DISubprogram(name: "foo", linkageName: "_ZN1S3fooEv", scope: !"_ZTS1S", file: !5, line: 18, type: !18, isLocal: false, isDefinition: false, scopeLine: 18, flags: DIFlagPrototyped, isOptimized: false) +!18 = !DISubroutineType(types: !19) +!19 = !{!8, !13} +!20 = !DICompositeType(tag: DW_TAG_structure_type, name: "Nested", scope: !"_ZTS1S", file: !5, line: 9, size: 64, align: 64, elements: !21, identifier: "_ZTSN1S6NestedE") +!21 = !{!22} +!22 = !DIDerivedType(tag: DW_TAG_member, name: "D", scope: !"_ZTSN1S6NestedE", file: !5, line: 10, baseType: !23, size: 64, align: 64) +!23 = !DIBasicType(name: "double", size: 64, align: 64, encoding: DW_ATE_float) +!24 = !DICompositeType(tag: DW_TAG_class_type, name: "C", scope: !25, file: !5, line: 24, size: 128, align: 64, elements: !26, identifier: "_ZTSN1N1CE") +!25 = !DINamespace(name: "N", scope: null, file: !5, line: 23) +!26 = !{!27} +!27 = !DIDerivedType(tag: DW_TAG_member, name: "S", scope: !"_ZTSN1N1CE", file: !5, line: 25, baseType: !28, size: 128, align: 64) +!28 = !DIDerivedType(tag: DW_TAG_typedef, name: "AliasForS", file: !5, line: 21, baseType: !"_ZTS1S") +!29 = !DICompositeType(tag: DW_TAG_class_type, name: "C", scope: !30, file: !5, line: 31, size: 32, align: 32, elements: !31, identifier: "_ZTSN1N1N1CE") +!30 = !DINamespace(name: "N", scope: !25, file: !5, line: 30) +!31 = !{!32} +!32 = !DIDerivedType(tag: DW_TAG_member, name: "S", scope: !"_ZTSN1N1N1CE", file: !5, line: 32, baseType: !8, size: 32, align: 32) +!33 = !DICompositeType(tag: DW_TAG_union_type, name: "U", file: !5, line: 42, size: 8, align: 8, elements: !34, identifier: "_ZTS1U") +!34 = !{!35, !36} +!35 = !DIDerivedType(tag: DW_TAG_member, name: "C", scope: !"_ZTS1U", file: !5, line: 43, baseType: !"_ZTSN1U1CE", size: 8, align: 8) +!36 = !DIDerivedType(tag: DW_TAG_member, name: "S", scope: !"_ZTS1U", file: !5, line: 44, baseType: !"_ZTSN1U1SE", size: 8, align: 8) +!37 = !DICompositeType(tag: DW_TAG_class_type, name: "C", scope: !"_ZTS1U", file: !5, line: 43, size: 8, align: 8, elements: !2, identifier: "_ZTSN1U1CE") +!38 = !DICompositeType(tag: DW_TAG_structure_type, name: "S", scope: !"_ZTS1U", file: !5, line: 44, size: 8, align: 8, elements: !2, identifier: "_ZTSN1U1SE") +!39 = !DICompositeType(tag: DW_TAG_class_type, scope: !40, file: !5, line: 49, size: 8, align: 8, elements: !43, identifier: "_ZTSZ4funcvEUlvE_") +!40 = !DISubprogram(name: "func", linkageName: "_Z4funcv", scope: !5, file: !5, line: 47, type: !41, isLocal: false, isDefinition: true, scopeLine: 47, flags: DIFlagPrototyped, isOptimized: false, function: i32 ()* @_Z4funcv, variables: !2) +!41 = !DISubroutineType(types: !42) +!42 = !{!8} +!43 = !{!44} +!44 = !DISubprogram(name: "operator()", scope: !"_ZTSZ4funcvEUlvE_", file: !5, line: 49, type: !45, isLocal: false, isDefinition: false, scopeLine: 49, flags: DIFlagPublic | DIFlagPrototyped, isOptimized: false) +!45 = !DISubroutineType(types: !46) +!46 = !{!8, !47} +!47 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !48, size: 64, align: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!48 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !"_ZTSZ4funcvEUlvE_") +!49 = !DICompositeType(tag: DW_TAG_structure_type, name: "CInsideFunc", scope: !40, file: !5, line: 48, size: 32, align: 32, elements: !50, identifier: "_ZTSZ4funcvE11CInsideFunc") +!50 = !{!51} +!51 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !"_ZTSZ4funcvE11CInsideFunc", file: !5, line: 48, baseType: !8, size: 32, align: 32) +!52 = !{!53, !40, !54} +!53 = !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !1, file: !1, line: 3, type: !41, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: false, function: i32 ()* @_Z3foov, variables: !2) +!54 = !DISubprogram(name: "operator()", linkageName: "_ZZ4funcvENKUlvE_clEv", scope: !"_ZTSZ4funcvEUlvE_", file: !5, line: 49, type: !45, isLocal: false, isDefinition: true, scopeLine: 49, flags: DIFlagPrototyped, isOptimized: false, function: i32 (%class.anon*)* @_ZZ4funcvENKUlvE_clEv, declaration: !44, variables: !2) +!55 = !{i32 2, !"Dwarf Version", i32 2} +!56 = !{i32 2, !"Debug Info Version", i32 3} +!57 = !{i32 1, !"PIC Level", i32 2} +!58 = !{!"clang version 3.8.0 (trunk 242534)"} +!59 = !DILocalVariable(tag: DW_TAG_auto_variable, name: "s", scope: !53, file: !1, line: 4, type: !28) +!60 = !DIExpression() +!61 = !DILocation(line: 4, column: 12, scope: !53) +!62 = !DILocalVariable(tag: DW_TAG_auto_variable, name: "nc", scope: !53, file: !1, line: 5, type: !"_ZTSN1N1CE") +!63 = !DILocation(line: 5, column: 7, scope: !53) +!64 = !DILocalVariable(tag: DW_TAG_auto_variable, name: "nnc", scope: !53, file: !1, line: 6, type: !"_ZTSN1N1N1CE") +!65 = !DILocation(line: 6, column: 10, scope: !53) +!66 = !DILocalVariable(tag: DW_TAG_auto_variable, name: "ac", scope: !53, file: !1, line: 7, type: !67) +!67 = !DICompositeType(tag: DW_TAG_class_type, name: "AnonC", scope: !68, file: !5, line: 38, size: 8, align: 8, elements: !2) +!68 = !DINamespace(scope: null, file: !5, line: 37) +!69 = !DILocation(line: 7, column: 8, scope: !53) +!70 = !DILocalVariable(tag: DW_TAG_auto_variable, name: "u", scope: !53, file: !1, line: 8, type: !"_ZTS1U") +!71 = !DILocation(line: 8, column: 4, scope: !53) +!72 = !DILocation(line: 10, column: 9, scope: !53) +!73 = !DILocation(line: 10, column: 2, scope: !53) +!74 = !DILocalVariable(tag: DW_TAG_auto_variable, name: "functor", scope: !40, file: !5, line: 49, type: !"_ZTSZ4funcvEUlvE_") +!75 = !DILocation(line: 49, column: 7, scope: !40) +!76 = !DILocation(line: 50, column: 9, scope: !40) +!77 = !DILocation(line: 50, column: 2, scope: !40) +!78 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "this", arg: 1, scope: !54, type: !79, flags: DIFlagArtificial | DIFlagObjectPointer) +!79 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !48, size: 64, align: 64) +!80 = !DILocation(line: 0, scope: !54) +!81 = !DILocalVariable(tag: DW_TAG_auto_variable, name: "dummy", scope: !54, file: !5, line: 49, type: !"_ZTSZ4funcvE11CInsideFunc") +!82 = !DILocation(line: 49, column: 36, scope: !54) +!83 = !DILocation(line: 49, column: 56, scope: !54) +!84 = !DILocation(line: 49, column: 43, scope: !54) diff --git a/llvm/test/tools/dsymutil/Inputs/odr2.cpp b/llvm/test/tools/dsymutil/Inputs/odr2.cpp new file mode 100644 index 0000000..4c26fb2 --- /dev/null +++ b/llvm/test/tools/dsymutil/Inputs/odr2.cpp @@ -0,0 +1,7 @@ +#include "odr-types.h" + +int bar() { + S s; + s.incr(); + return s.foo(); +} diff --git a/llvm/test/tools/dsymutil/Inputs/odr2.ll b/llvm/test/tools/dsymutil/Inputs/odr2.ll new file mode 100644 index 0000000..da66a2d --- /dev/null +++ b/llvm/test/tools/dsymutil/Inputs/odr2.ll @@ -0,0 +1,99 @@ +; Generated from odr2.cpp and odr-types.h by running: +; clang -emit-llvm -g -S -std=c++11 odr2.cpp +; ModuleID = 'odr2.cpp' +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.11.0" + +%struct.S = type { i32, %"struct.S::Nested" } +%"struct.S::Nested" = type { double } + +; Function Attrs: ssp uwtable +define i32 @_Z3barv() #0 { +entry: + %this.addr.i = alloca %struct.S*, align 8 + %s = alloca %struct.S, align 8 + call void @llvm.dbg.declare(metadata %struct.S* %s, metadata !34, metadata !35), !dbg !36 + store %struct.S* %s, %struct.S** %this.addr.i, align 8, !dbg !37 + %this1.i = load %struct.S*, %struct.S** %this.addr.i, !dbg !37 + %I.i = getelementptr inbounds %struct.S, %struct.S* %this1.i, i32 0, i32 0, !dbg !38 + %0 = load i32, i32* %I.i, align 4, !dbg !40 + %inc.i = add nsw i32 %0, 1, !dbg !40 + store i32 %inc.i, i32* %I.i, align 4, !dbg !40 + %call = call i32 @_ZN1S3fooEv(%struct.S* %s), !dbg !41 + call void @llvm.dbg.declare(metadata %struct.S** %this.addr.i, metadata !42, metadata !35), !dbg !44 + ret i32 %call, !dbg !45 +} + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +; Function Attrs: nounwind ssp uwtable +define linkonce_odr i32 @_ZN1S3fooEv(%struct.S* %this) #2 align 2 { +entry: + %this.addr = alloca %struct.S*, align 8 + store %struct.S* %this, %struct.S** %this.addr, align 8 + call void @llvm.dbg.declare(metadata %struct.S** %this.addr, metadata !46, metadata !35), !dbg !47 + %this1 = load %struct.S*, %struct.S** %this.addr + %I = getelementptr inbounds %struct.S, %struct.S* %this1, i32 0, i32 0, !dbg !48 + %0 = load i32, i32* %I, align 4, !dbg !48 + ret i32 %0, !dbg !49 +} + +attributes #0 = { ssp uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+cx16,+sse,+sse2,+sse3,+ssse3" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind readnone } +attributes #2 = { nounwind ssp uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+cx16,+sse,+sse2,+sse3,+ssse3" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!30, !31, !32} +!llvm.ident = !{!33} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 3.8.0 (trunk 242534)", isOptimized: false, runtimeVersion: 0, emissionKind: 1, enums: !2, retainedTypes: !3, subprograms: !24) +!1 = !DIFile(filename: "odr2.cpp", directory: "/Inputs") +!2 = !{} +!3 = !{!4, !20} +!4 = !DICompositeType(tag: DW_TAG_structure_type, name: "S", file: !5, line: 1, size: 128, align: 64, elements: !6, identifier: "_ZTS1S") +!5 = !DIFile(filename: "./odr-types.h", directory: "/Inputs") +!6 = !{!7, !9, !10, !14, !17} +!7 = !DIDerivedType(tag: DW_TAG_member, name: "I", scope: !"_ZTS1S", file: !5, line: 2, baseType: !8, size: 32, align: 32) +!8 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +!9 = !DIDerivedType(tag: DW_TAG_member, name: "D", scope: !"_ZTS1S", file: !5, line: 15, baseType: !"_ZTSN1S6NestedE", size: 64, align: 64, offset: 64) +!10 = !DISubprogram(name: "incr", linkageName: "_ZN1S4incrEv", scope: !"_ZTS1S", file: !5, line: 4, type: !11, isLocal: false, isDefinition: false, scopeLine: 4, flags: DIFlagPrototyped, isOptimized: false) +!11 = !DISubroutineType(types: !12) +!12 = !{null, !13} +!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !"_ZTS1S", size: 64, align: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!14 = !DISubprogram(name: "incr", linkageName: "_ZN1S4incrEi", scope: !"_ZTS1S", file: !5, line: 5, type: !15, isLocal: false, isDefinition: false, scopeLine: 5, flags: DIFlagPrototyped, isOptimized: false) +!15 = !DISubroutineType(types: !16) +!16 = !{null, !13, !8} +!17 = !DISubprogram(name: "foo", linkageName: "_ZN1S3fooEv", scope: !"_ZTS1S", file: !5, line: 18, type: !18, isLocal: false, isDefinition: false, scopeLine: 18, flags: DIFlagPrototyped, isOptimized: false) +!18 = !DISubroutineType(types: !19) +!19 = !{!8, !13} +!20 = !DICompositeType(tag: DW_TAG_structure_type, name: "Nested", scope: !"_ZTS1S", file: !5, line: 9, size: 64, align: 64, elements: !21, identifier: "_ZTSN1S6NestedE") +!21 = !{!22} +!22 = !DIDerivedType(tag: DW_TAG_member, name: "D", scope: !"_ZTSN1S6NestedE", file: !5, line: 10, baseType: !23, size: 64, align: 64) +!23 = !DIBasicType(name: "double", size: 64, align: 64, encoding: DW_ATE_float) +!24 = !{!25, !28, !29} +!25 = !DISubprogram(name: "bar", linkageName: "_Z3barv", scope: !1, file: !1, line: 3, type: !26, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: false, function: i32 ()* @_Z3barv, variables: !2) +!26 = !DISubroutineType(types: !27) +!27 = !{!8} +!28 = !DISubprogram(name: "incr", linkageName: "_ZN1S4incrEv", scope: !"_ZTS1S", file: !5, line: 4, type: !11, isLocal: false, isDefinition: true, scopeLine: 4, flags: DIFlagPrototyped, isOptimized: false, declaration: !10, variables: !2) +!29 = !DISubprogram(name: "foo", linkageName: "_ZN1S3fooEv", scope: !"_ZTS1S", file: !5, line: 18, type: !18, isLocal: false, isDefinition: true, scopeLine: 18, flags: DIFlagPrototyped, isOptimized: false, function: i32 (%struct.S*)* @_ZN1S3fooEv, declaration: !17, variables: !2) +!30 = !{i32 2, !"Dwarf Version", i32 2} +!31 = !{i32 2, !"Debug Info Version", i32 3} +!32 = !{i32 1, !"PIC Level", i32 2} +!33 = !{!"clang version 3.8.0 (trunk 242534)"} +!34 = !DILocalVariable(tag: DW_TAG_auto_variable, name: "s", scope: !25, file: !1, line: 4, type: !"_ZTS1S") +!35 = !DIExpression() +!36 = !DILocation(line: 4, column: 4, scope: !25) +!37 = !DILocation(line: 5, column: 2, scope: !25) +!38 = !DILocation(line: 4, column: 47, scope: !28, inlinedAt: !39) +!39 = distinct !DILocation(line: 5, column: 2, scope: !25) +!40 = !DILocation(line: 4, column: 48, scope: !28, inlinedAt: !39) +!41 = !DILocation(line: 6, column: 9, scope: !25) +!42 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "this", arg: 1, scope: !28, type: !43, flags: DIFlagArtificial | DIFlagObjectPointer) +!43 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !"_ZTS1S", size: 64, align: 64) +!44 = !DILocation(line: 0, scope: !28, inlinedAt: !39) +!45 = !DILocation(line: 6, column: 2, scope: !25) +!46 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "this", arg: 1, scope: !29, type: !43, flags: DIFlagArtificial | DIFlagObjectPointer) +!47 = !DILocation(line: 0, scope: !29) +!48 = !DILocation(line: 18, column: 21, scope: !29) +!49 = !DILocation(line: 18, column: 14, scope: !29) diff --git a/llvm/test/tools/dsymutil/Inputs/odr3.cpp b/llvm/test/tools/dsymutil/Inputs/odr3.cpp new file mode 100644 index 0000000..1200c67 --- /dev/null +++ b/llvm/test/tools/dsymutil/Inputs/odr3.cpp @@ -0,0 +1,7 @@ +#include "odr-types.h" + +int bar() { + S s; + s.incr(42); + return s.foo(); +} diff --git a/llvm/test/tools/dsymutil/Inputs/odr3.ll b/llvm/test/tools/dsymutil/Inputs/odr3.ll new file mode 100644 index 0000000..d0fd239 --- /dev/null +++ b/llvm/test/tools/dsymutil/Inputs/odr3.ll @@ -0,0 +1,106 @@ +; Generated from odr3.cpp and odr-types.h by running: +; clang -emit-llvm -g -S -std=c++11 odr3.cpp +; ModuleID = 'odr3.cpp' +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.11.0" + +%struct.S = type { i32, %"struct.S::Nested" } +%"struct.S::Nested" = type { double } + +; Function Attrs: ssp uwtable +define i32 @_Z3barv() #0 { +entry: + %this.addr.i = alloca %struct.S*, align 8 + %Add.addr.i = alloca i32, align 4 + %s = alloca %struct.S, align 8 + call void @llvm.dbg.declare(metadata %struct.S* %s, metadata !34, metadata !35), !dbg !36 + store %struct.S* %s, %struct.S** %this.addr.i, align 8, !dbg !37 + store i32 42, i32* %Add.addr.i, align 4, !dbg !37 + %this1.i = load %struct.S*, %struct.S** %this.addr.i, !dbg !37 + %0 = load i32, i32* %Add.addr.i, align 4, !dbg !38 + %I.i = getelementptr inbounds %struct.S, %struct.S* %this1.i, i32 0, i32 0, !dbg !40 + %1 = load i32, i32* %I.i, align 4, !dbg !41 + %add.i = add nsw i32 %1, %0, !dbg !41 + store i32 %add.i, i32* %I.i, align 4, !dbg !41 + %call = call i32 @_ZN1S3fooEv(%struct.S* %s), !dbg !42 + call void @llvm.dbg.declare(metadata %struct.S** %this.addr.i, metadata !43, metadata !35), !dbg !45 + call void @llvm.dbg.declare(metadata i32* %Add.addr.i, metadata !46, metadata !35), !dbg !47 + ret i32 %call, !dbg !48 +} + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +; Function Attrs: nounwind ssp uwtable +define linkonce_odr i32 @_ZN1S3fooEv(%struct.S* %this) #2 align 2 { +entry: + %this.addr = alloca %struct.S*, align 8 + store %struct.S* %this, %struct.S** %this.addr, align 8 + call void @llvm.dbg.declare(metadata %struct.S** %this.addr, metadata !49, metadata !35), !dbg !50 + %this1 = load %struct.S*, %struct.S** %this.addr + %I = getelementptr inbounds %struct.S, %struct.S* %this1, i32 0, i32 0, !dbg !51 + %0 = load i32, i32* %I, align 4, !dbg !51 + ret i32 %0, !dbg !52 +} + +attributes #0 = { ssp uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+cx16,+sse,+sse2,+sse3,+ssse3" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind readnone } +attributes #2 = { nounwind ssp uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+cx16,+sse,+sse2,+sse3,+ssse3" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!30, !31, !32} +!llvm.ident = !{!33} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 3.8.0 (trunk 242534)", isOptimized: false, runtimeVersion: 0, emissionKind: 1, enums: !2, retainedTypes: !3, subprograms: !24) +!1 = !DIFile(filename: "odr3.cpp", directory: "/Inputs") +!2 = !{} +!3 = !{!4, !20} +!4 = !DICompositeType(tag: DW_TAG_structure_type, name: "S", file: !5, line: 1, size: 128, align: 64, elements: !6, identifier: "_ZTS1S") +!5 = !DIFile(filename: "./odr-types.h", directory: "/Inputs") +!6 = !{!7, !9, !10, !14, !17} +!7 = !DIDerivedType(tag: DW_TAG_member, name: "I", scope: !"_ZTS1S", file: !5, line: 2, baseType: !8, size: 32, align: 32) +!8 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +!9 = !DIDerivedType(tag: DW_TAG_member, name: "D", scope: !"_ZTS1S", file: !5, line: 15, baseType: !"_ZTSN1S6NestedE", size: 64, align: 64, offset: 64) +!10 = !DISubprogram(name: "incr", linkageName: "_ZN1S4incrEv", scope: !"_ZTS1S", file: !5, line: 4, type: !11, isLocal: false, isDefinition: false, scopeLine: 4, flags: DIFlagPrototyped, isOptimized: false) +!11 = !DISubroutineType(types: !12) +!12 = !{null, !13} +!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !"_ZTS1S", size: 64, align: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!14 = !DISubprogram(name: "incr", linkageName: "_ZN1S4incrEi", scope: !"_ZTS1S", file: !5, line: 5, type: !15, isLocal: false, isDefinition: false, scopeLine: 5, flags: DIFlagPrototyped, isOptimized: false) +!15 = !DISubroutineType(types: !16) +!16 = !{null, !13, !8} +!17 = !DISubprogram(name: "foo", linkageName: "_ZN1S3fooEv", scope: !"_ZTS1S", file: !5, line: 18, type: !18, isLocal: false, isDefinition: false, scopeLine: 18, flags: DIFlagPrototyped, isOptimized: false) +!18 = !DISubroutineType(types: !19) +!19 = !{!8, !13} +!20 = !DICompositeType(tag: DW_TAG_structure_type, name: "Nested", scope: !"_ZTS1S", file: !5, line: 9, size: 64, align: 64, elements: !21, identifier: "_ZTSN1S6NestedE") +!21 = !{!22} +!22 = !DIDerivedType(tag: DW_TAG_member, name: "D", scope: !"_ZTSN1S6NestedE", file: !5, line: 10, baseType: !23, size: 64, align: 64) +!23 = !DIBasicType(name: "double", size: 64, align: 64, encoding: DW_ATE_float) +!24 = !{!25, !28, !29} +!25 = !DISubprogram(name: "bar", linkageName: "_Z3barv", scope: !1, file: !1, line: 3, type: !26, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: false, function: i32 ()* @_Z3barv, variables: !2) +!26 = !DISubroutineType(types: !27) +!27 = !{!8} +!28 = !DISubprogram(name: "incr", linkageName: "_ZN1S4incrEi", scope: !"_ZTS1S", file: !5, line: 5, type: !15, isLocal: false, isDefinition: true, scopeLine: 5, flags: DIFlagPrototyped, isOptimized: false, declaration: !14, variables: !2) +!29 = !DISubprogram(name: "foo", linkageName: "_ZN1S3fooEv", scope: !"_ZTS1S", file: !5, line: 18, type: !18, isLocal: false, isDefinition: true, scopeLine: 18, flags: DIFlagPrototyped, isOptimized: false, function: i32 (%struct.S*)* @_ZN1S3fooEv, declaration: !17, variables: !2) +!30 = !{i32 2, !"Dwarf Version", i32 2} +!31 = !{i32 2, !"Debug Info Version", i32 3} +!32 = !{i32 1, !"PIC Level", i32 2} +!33 = !{!"clang version 3.8.0 (trunk 242534)"} +!34 = !DILocalVariable(tag: DW_TAG_auto_variable, name: "s", scope: !25, file: !1, line: 4, type: !"_ZTS1S") +!35 = !DIExpression() +!36 = !DILocation(line: 4, column: 4, scope: !25) +!37 = !DILocation(line: 5, column: 2, scope: !25) +!38 = !DILocation(line: 5, column: 59, scope: !28, inlinedAt: !39) +!39 = distinct !DILocation(line: 5, column: 2, scope: !25) +!40 = !DILocation(line: 5, column: 54, scope: !28, inlinedAt: !39) +!41 = !DILocation(line: 5, column: 56, scope: !28, inlinedAt: !39) +!42 = !DILocation(line: 6, column: 9, scope: !25) +!43 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "this", arg: 1, scope: !28, type: !44, flags: DIFlagArtificial | DIFlagObjectPointer) +!44 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !"_ZTS1S", size: 64, align: 64) +!45 = !DILocation(line: 0, scope: !28, inlinedAt: !39) +!46 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "Add", arg: 2, scope: !28, file: !5, line: 5, type: !8) +!47 = !DILocation(line: 5, column: 16, scope: !28, inlinedAt: !39) +!48 = !DILocation(line: 6, column: 2, scope: !25) +!49 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "this", arg: 1, scope: !29, type: !44, flags: DIFlagArtificial | DIFlagObjectPointer) +!50 = !DILocation(line: 0, scope: !29) +!51 = !DILocation(line: 18, column: 21, scope: !29) +!52 = !DILocation(line: 18, column: 14, scope: !29) diff --git a/llvm/test/tools/dsymutil/Inputs/odr4.cpp b/llvm/test/tools/dsymutil/Inputs/odr4.cpp new file mode 100644 index 0000000..948dbdb --- /dev/null +++ b/llvm/test/tools/dsymutil/Inputs/odr4.cpp @@ -0,0 +1,8 @@ +namespace { + class AnonC { + }; +} + +void baz() { + AnonC ac; +} diff --git a/llvm/test/tools/dsymutil/Inputs/odr4.ll b/llvm/test/tools/dsymutil/Inputs/odr4.ll new file mode 100644 index 0000000..ecaf85b --- /dev/null +++ b/llvm/test/tools/dsymutil/Inputs/odr4.ll @@ -0,0 +1,43 @@ +; Generated from odr4.cpp and odr-types.h by running: +; clang -emit-llvm -g -S -std=c++11 odr4.cpp +; ModuleID = 'odr4.cpp' +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.11.0" + +%"class.(anonymous namespace)::AnonC" = type { i8 } + +; Function Attrs: nounwind ssp uwtable +define void @_Z3bazv() #0 { +entry: + %ac = alloca %"class.(anonymous namespace)::AnonC", align 1 + call void @llvm.dbg.declare(metadata %"class.(anonymous namespace)::AnonC"* %ac, metadata !11, metadata !14), !dbg !15 + ret void, !dbg !16 +} + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +attributes #0 = { nounwind ssp uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+cx16,+sse,+sse2,+sse3,+ssse3" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind readnone } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!7, !8, !9} +!llvm.ident = !{!10} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 3.8.0 (trunk 242534)", isOptimized: false, runtimeVersion: 0, emissionKind: 1, enums: !2, subprograms: !3) +!1 = !DIFile(filename: "odr4.cpp", directory: "/Inputs") +!2 = !{} +!3 = !{!4} +!4 = !DISubprogram(name: "baz", linkageName: "_Z3bazv", scope: !1, file: !1, line: 6, type: !5, isLocal: false, isDefinition: true, scopeLine: 6, flags: DIFlagPrototyped, isOptimized: false, function: void ()* @_Z3bazv, variables: !2) +!5 = !DISubroutineType(types: !6) +!6 = !{null} +!7 = !{i32 2, !"Dwarf Version", i32 2} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !{i32 1, !"PIC Level", i32 2} +!10 = !{!"clang version 3.8.0 (trunk 242534)"} +!11 = !DILocalVariable(tag: DW_TAG_auto_variable, name: "ac", scope: !4, file: !1, line: 7, type: !12) +!12 = !DICompositeType(tag: DW_TAG_class_type, name: "AnonC", scope: !13, file: !1, line: 2, size: 8, align: 8, elements: !2) +!13 = !DINamespace(scope: null, file: !1, line: 1) +!14 = !DIExpression() +!15 = !DILocation(line: 7, column: 8, scope: !4) +!16 = !DILocation(line: 8, column: 1, scope: !4) diff --git a/llvm/test/tools/dsymutil/Inputs/odr5.cpp b/llvm/test/tools/dsymutil/Inputs/odr5.cpp new file mode 100644 index 0000000..fd69423 --- /dev/null +++ b/llvm/test/tools/dsymutil/Inputs/odr5.cpp @@ -0,0 +1,7 @@ +#include "odr-types.h" + +double baz() { + S::Nested d; + d.init(0); + return d.D; +} diff --git a/llvm/test/tools/dsymutil/Inputs/odr5.ll b/llvm/test/tools/dsymutil/Inputs/odr5.ll new file mode 100644 index 0000000..7b738be --- /dev/null +++ b/llvm/test/tools/dsymutil/Inputs/odr5.ll @@ -0,0 +1,101 @@ +; Generated from odr5.cpp and odr-types.h by running: +; clang -emit-llvm -g -S -std=c++11 odr5.cpp +; ModuleID = 'odr5.cpp' +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.11.0" + +%"struct.S::Nested" = type { double } + +; Function Attrs: ssp uwtable +define double @_Z3bazv() #0 { +entry: + %d = alloca %"struct.S::Nested", align 8 + call void @llvm.dbg.declare(metadata %"struct.S::Nested"* %d, metadata !39, metadata !40), !dbg !41 + call void @_ZN1S6Nested4initIiEEvT_(%"struct.S::Nested"* %d, i32 0), !dbg !42 + %D = getelementptr inbounds %"struct.S::Nested", %"struct.S::Nested"* %d, i32 0, i32 0, !dbg !43 + %0 = load double, double* %D, align 8, !dbg !43 + ret double %0, !dbg !44 +} + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +; Function Attrs: nounwind ssp uwtable +define linkonce_odr void @_ZN1S6Nested4initIiEEvT_(%"struct.S::Nested"* %this, i32 %Val) #2 align 2 { +entry: + %this.addr = alloca %"struct.S::Nested"*, align 8 + %Val.addr = alloca i32, align 4 + store %"struct.S::Nested"* %this, %"struct.S::Nested"** %this.addr, align 8 + call void @llvm.dbg.declare(metadata %"struct.S::Nested"** %this.addr, metadata !45, metadata !40), !dbg !47 + store i32 %Val, i32* %Val.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %Val.addr, metadata !48, metadata !40), !dbg !49 + %this1 = load %"struct.S::Nested"*, %"struct.S::Nested"** %this.addr + %0 = load i32, i32* %Val.addr, align 4, !dbg !50 + %conv = sitofp i32 %0 to double, !dbg !50 + %D = getelementptr inbounds %"struct.S::Nested", %"struct.S::Nested"* %this1, i32 0, i32 0, !dbg !51 + store double %conv, double* %D, align 8, !dbg !52 + ret void, !dbg !53 +} + +attributes #0 = { ssp uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+cx16,+sse,+sse2,+sse3,+ssse3" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind readnone } +attributes #2 = { nounwind ssp uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+cx16,+sse,+sse2,+sse3,+ssse3" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!35, !36, !37} +!llvm.ident = !{!38} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 3.8.0 (trunk 242534)", isOptimized: false, runtimeVersion: 0, emissionKind: 1, enums: !2, retainedTypes: !3, subprograms: !24) +!1 = !DIFile(filename: "odr5.cpp", directory: "/Inputs") +!2 = !{} +!3 = !{!4, !20, !23} +!4 = !DICompositeType(tag: DW_TAG_structure_type, name: "S", file: !5, line: 1, size: 128, align: 64, elements: !6, identifier: "_ZTS1S") +!5 = !DIFile(filename: "./odr-types.h", directory: "/Inputs") +!6 = !{!7, !9, !10, !14, !17} +!7 = !DIDerivedType(tag: DW_TAG_member, name: "I", scope: !"_ZTS1S", file: !5, line: 2, baseType: !8, size: 32, align: 32) +!8 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +!9 = !DIDerivedType(tag: DW_TAG_member, name: "D", scope: !"_ZTS1S", file: !5, line: 15, baseType: !"_ZTSN1S6NestedE", size: 64, align: 64, offset: 64) +!10 = !DISubprogram(name: "incr", linkageName: "_ZN1S4incrEv", scope: !"_ZTS1S", file: !5, line: 4, type: !11, isLocal: false, isDefinition: false, scopeLine: 4, flags: DIFlagPrototyped, isOptimized: false) +!11 = !DISubroutineType(types: !12) +!12 = !{null, !13} +!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !"_ZTS1S", size: 64, align: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!14 = !DISubprogram(name: "incr", linkageName: "_ZN1S4incrEi", scope: !"_ZTS1S", file: !5, line: 5, type: !15, isLocal: false, isDefinition: false, scopeLine: 5, flags: DIFlagPrototyped, isOptimized: false) +!15 = !DISubroutineType(types: !16) +!16 = !{null, !13, !8} +!17 = !DISubprogram(name: "foo", linkageName: "_ZN1S3fooEv", scope: !"_ZTS1S", file: !5, line: 18, type: !18, isLocal: false, isDefinition: false, scopeLine: 18, flags: DIFlagPrototyped, isOptimized: false) +!18 = !DISubroutineType(types: !19) +!19 = !{!8, !13} +!20 = !DICompositeType(tag: DW_TAG_structure_type, name: "Nested", scope: !"_ZTS1S", file: !5, line: 9, size: 64, align: 64, elements: !21, identifier: "_ZTSN1S6NestedE") +!21 = !{!22} +!22 = !DIDerivedType(tag: DW_TAG_member, name: "D", scope: !"_ZTSN1S6NestedE", file: !5, line: 10, baseType: !23, size: 64, align: 64) +!23 = !DIBasicType(name: "double", size: 64, align: 64, encoding: DW_ATE_float) +!24 = !{!25, !28} +!25 = !DISubprogram(name: "baz", linkageName: "_Z3bazv", scope: !1, file: !1, line: 3, type: !26, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: false, function: double ()* @_Z3bazv, variables: !2) +!26 = !DISubroutineType(types: !27) +!27 = !{!23} +!28 = !DISubprogram(name: "init", linkageName: "_ZN1S6Nested4initIiEEvT_", scope: !"_ZTSN1S6NestedE", file: !5, line: 12, type: !29, isLocal: false, isDefinition: true, scopeLine: 12, flags: DIFlagPrototyped, isOptimized: false, function: void (%"struct.S::Nested"*, i32)* @_ZN1S6Nested4initIiEEvT_, templateParams: !32, declaration: !34, variables: !2) +!29 = !DISubroutineType(types: !30) +!30 = !{null, !31, !8} +!31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !"_ZTSN1S6NestedE", size: 64, align: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!32 = !{!33} +!33 = !DITemplateTypeParameter(name: "T", type: !8) +!34 = !DISubprogram(name: "init", linkageName: "_ZN1S6Nested4initIiEEvT_", scope: !"_ZTSN1S6NestedE", file: !5, line: 12, type: !29, isLocal: false, isDefinition: false, scopeLine: 12, flags: DIFlagPrototyped, isOptimized: false, templateParams: !32) +!35 = !{i32 2, !"Dwarf Version", i32 2} +!36 = !{i32 2, !"Debug Info Version", i32 3} +!37 = !{i32 1, !"PIC Level", i32 2} +!38 = !{!"clang version 3.8.0 (trunk 242534)"} +!39 = !DILocalVariable(tag: DW_TAG_auto_variable, name: "d", scope: !25, file: !1, line: 4, type: !"_ZTSN1S6NestedE") +!40 = !DIExpression() +!41 = !DILocation(line: 4, column: 12, scope: !25) +!42 = !DILocation(line: 5, column: 2, scope: !25) +!43 = !DILocation(line: 6, column: 11, scope: !25) +!44 = !DILocation(line: 6, column: 2, scope: !25) +!45 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "this", arg: 1, scope: !28, type: !46, flags: DIFlagArtificial | DIFlagObjectPointer) +!46 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !"_ZTSN1S6NestedE", size: 64, align: 64) +!47 = !DILocation(line: 0, scope: !28) +!48 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "Val", arg: 2, scope: !28, file: !5, line: 12, type: !8) +!49 = !DILocation(line: 12, column: 36, scope: !28) +!50 = !DILocation(line: 12, column: 54, scope: !28) +!51 = !DILocation(line: 12, column: 43, scope: !28) +!52 = !DILocation(line: 12, column: 45, scope: !28) +!53 = !DILocation(line: 12, column: 60, scope: !28) diff --git a/llvm/test/tools/dsymutil/Inputs/odr6.cpp b/llvm/test/tools/dsymutil/Inputs/odr6.cpp new file mode 100644 index 0000000..2893856 --- /dev/null +++ b/llvm/test/tools/dsymutil/Inputs/odr6.cpp @@ -0,0 +1,7 @@ +#include "odr-types.h" + +double baz() { + S::Nested d; + d.init(0.0); + return d.D; +} diff --git a/llvm/test/tools/dsymutil/Inputs/odr6.ll b/llvm/test/tools/dsymutil/Inputs/odr6.ll new file mode 100644 index 0000000..c17099f --- /dev/null +++ b/llvm/test/tools/dsymutil/Inputs/odr6.ll @@ -0,0 +1,100 @@ +; Generated from odr6.cpp and odr-types.h by running: +; clang -emit-llvm -g -S -std=c++11 odr6.cpp +; ModuleID = 'odr6.cpp' +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.11.0" + +%"struct.S::Nested" = type { double } + +; Function Attrs: ssp uwtable +define double @_Z3bazv() #0 { +entry: + %d = alloca %"struct.S::Nested", align 8 + call void @llvm.dbg.declare(metadata %"struct.S::Nested"* %d, metadata !39, metadata !40), !dbg !41 + call void @_ZN1S6Nested4initIdEEvT_(%"struct.S::Nested"* %d, double 0.000000e+00), !dbg !42 + %D = getelementptr inbounds %"struct.S::Nested", %"struct.S::Nested"* %d, i32 0, i32 0, !dbg !43 + %0 = load double, double* %D, align 8, !dbg !43 + ret double %0, !dbg !44 +} + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +; Function Attrs: nounwind ssp uwtable +define linkonce_odr void @_ZN1S6Nested4initIdEEvT_(%"struct.S::Nested"* %this, double %Val) #2 align 2 { +entry: + %this.addr = alloca %"struct.S::Nested"*, align 8 + %Val.addr = alloca double, align 8 + store %"struct.S::Nested"* %this, %"struct.S::Nested"** %this.addr, align 8 + call void @llvm.dbg.declare(metadata %"struct.S::Nested"** %this.addr, metadata !45, metadata !40), !dbg !47 + store double %Val, double* %Val.addr, align 8 + call void @llvm.dbg.declare(metadata double* %Val.addr, metadata !48, metadata !40), !dbg !49 + %this1 = load %"struct.S::Nested"*, %"struct.S::Nested"** %this.addr + %0 = load double, double* %Val.addr, align 8, !dbg !50 + %D = getelementptr inbounds %"struct.S::Nested", %"struct.S::Nested"* %this1, i32 0, i32 0, !dbg !51 + store double %0, double* %D, align 8, !dbg !52 + ret void, !dbg !53 +} + +attributes #0 = { ssp uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+cx16,+sse,+sse2,+sse3,+ssse3" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind readnone } +attributes #2 = { nounwind ssp uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+cx16,+sse,+sse2,+sse3,+ssse3" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!35, !36, !37} +!llvm.ident = !{!38} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 3.8.0 (trunk 242534)", isOptimized: false, runtimeVersion: 0, emissionKind: 1, enums: !2, retainedTypes: !3, subprograms: !24) +!1 = !DIFile(filename: "odr6.cpp", directory: "/Inputs") +!2 = !{} +!3 = !{!4, !20, !23} +!4 = !DICompositeType(tag: DW_TAG_structure_type, name: "S", file: !5, line: 1, size: 128, align: 64, elements: !6, identifier: "_ZTS1S") +!5 = !DIFile(filename: "./odr-types.h", directory: "/Inputs") +!6 = !{!7, !9, !10, !14, !17} +!7 = !DIDerivedType(tag: DW_TAG_member, name: "I", scope: !"_ZTS1S", file: !5, line: 2, baseType: !8, size: 32, align: 32) +!8 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +!9 = !DIDerivedType(tag: DW_TAG_member, name: "D", scope: !"_ZTS1S", file: !5, line: 15, baseType: !"_ZTSN1S6NestedE", size: 64, align: 64, offset: 64) +!10 = !DISubprogram(name: "incr", linkageName: "_ZN1S4incrEv", scope: !"_ZTS1S", file: !5, line: 4, type: !11, isLocal: false, isDefinition: false, scopeLine: 4, flags: DIFlagPrototyped, isOptimized: false) +!11 = !DISubroutineType(types: !12) +!12 = !{null, !13} +!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !"_ZTS1S", size: 64, align: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!14 = !DISubprogram(name: "incr", linkageName: "_ZN1S4incrEi", scope: !"_ZTS1S", file: !5, line: 5, type: !15, isLocal: false, isDefinition: false, scopeLine: 5, flags: DIFlagPrototyped, isOptimized: false) +!15 = !DISubroutineType(types: !16) +!16 = !{null, !13, !8} +!17 = !DISubprogram(name: "foo", linkageName: "_ZN1S3fooEv", scope: !"_ZTS1S", file: !5, line: 18, type: !18, isLocal: false, isDefinition: false, scopeLine: 18, flags: DIFlagPrototyped, isOptimized: false) +!18 = !DISubroutineType(types: !19) +!19 = !{!8, !13} +!20 = !DICompositeType(tag: DW_TAG_structure_type, name: "Nested", scope: !"_ZTS1S", file: !5, line: 9, size: 64, align: 64, elements: !21, identifier: "_ZTSN1S6NestedE") +!21 = !{!22} +!22 = !DIDerivedType(tag: DW_TAG_member, name: "D", scope: !"_ZTSN1S6NestedE", file: !5, line: 10, baseType: !23, size: 64, align: 64) +!23 = !DIBasicType(name: "double", size: 64, align: 64, encoding: DW_ATE_float) +!24 = !{!25, !28} +!25 = !DISubprogram(name: "baz", linkageName: "_Z3bazv", scope: !1, file: !1, line: 3, type: !26, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: false, function: double ()* @_Z3bazv, variables: !2) +!26 = !DISubroutineType(types: !27) +!27 = !{!23} +!28 = !DISubprogram(name: "init", linkageName: "_ZN1S6Nested4initIdEEvT_", scope: !"_ZTSN1S6NestedE", file: !5, line: 12, type: !29, isLocal: false, isDefinition: true, scopeLine: 12, flags: DIFlagPrototyped, isOptimized: false, function: void (%"struct.S::Nested"*, double)* @_ZN1S6Nested4initIdEEvT_, templateParams: !32, declaration: !34, variables: !2) +!29 = !DISubroutineType(types: !30) +!30 = !{null, !31, !23} +!31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !"_ZTSN1S6NestedE", size: 64, align: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!32 = !{!33} +!33 = !DITemplateTypeParameter(name: "T", type: !23) +!34 = !DISubprogram(name: "init", linkageName: "_ZN1S6Nested4initIdEEvT_", scope: !"_ZTSN1S6NestedE", file: !5, line: 12, type: !29, isLocal: false, isDefinition: false, scopeLine: 12, flags: DIFlagPrototyped, isOptimized: false, templateParams: !32) +!35 = !{i32 2, !"Dwarf Version", i32 2} +!36 = !{i32 2, !"Debug Info Version", i32 3} +!37 = !{i32 1, !"PIC Level", i32 2} +!38 = !{!"clang version 3.8.0 (trunk 242534)"} +!39 = !DILocalVariable(tag: DW_TAG_auto_variable, name: "d", scope: !25, file: !1, line: 4, type: !"_ZTSN1S6NestedE") +!40 = !DIExpression() +!41 = !DILocation(line: 4, column: 12, scope: !25) +!42 = !DILocation(line: 5, column: 2, scope: !25) +!43 = !DILocation(line: 6, column: 11, scope: !25) +!44 = !DILocation(line: 6, column: 2, scope: !25) +!45 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "this", arg: 1, scope: !28, type: !46, flags: DIFlagArtificial | DIFlagObjectPointer) +!46 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !"_ZTSN1S6NestedE", size: 64, align: 64) +!47 = !DILocation(line: 0, scope: !28) +!48 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "Val", arg: 2, scope: !28, file: !5, line: 12, type: !23) +!49 = !DILocation(line: 12, column: 36, scope: !28) +!50 = !DILocation(line: 12, column: 54, scope: !28) +!51 = !DILocation(line: 12, column: 43, scope: !28) +!52 = !DILocation(line: 12, column: 45, scope: !28) +!53 = !DILocation(line: 12, column: 60, scope: !28) diff --git a/llvm/test/tools/dsymutil/Inputs/odr7.cpp b/llvm/test/tools/dsymutil/Inputs/odr7.cpp new file mode 100644 index 0000000..6056b48 --- /dev/null +++ b/llvm/test/tools/dsymutil/Inputs/odr7.cpp @@ -0,0 +1,5 @@ +#include "odr-types.h" + +void foo() { + S::Nested N; +} diff --git a/llvm/test/tools/dsymutil/Inputs/odr7.ll b/llvm/test/tools/dsymutil/Inputs/odr7.ll new file mode 100644 index 0000000..b4df117 --- /dev/null +++ b/llvm/test/tools/dsymutil/Inputs/odr7.ll @@ -0,0 +1,62 @@ +; Generated from odr7.cpp and odr-types.h by running: +; clang -emit-llvm -g -S -std=c++11 odr7.cpp +; ModuleID = 'odr7.cpp' +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.11.0" + +%"struct.S::Nested" = type { double } + +; Function Attrs: nounwind ssp uwtable +define void @_Z3foov() #0 { +entry: + %N = alloca %"struct.S::Nested", align 8 + call void @llvm.dbg.declare(metadata %"struct.S::Nested"* %N, metadata !32, metadata !33), !dbg !34 + ret void, !dbg !35 +} + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +attributes #0 = { nounwind ssp uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+cx16,+sse,+sse2,+sse3,+ssse3" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind readnone } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!28, !29, !30} +!llvm.ident = !{!31} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 3.8.0 (trunk 242534)", isOptimized: false, runtimeVersion: 0, emissionKind: 1, enums: !2, retainedTypes: !3, subprograms: !24) +!1 = !DIFile(filename: "odr7.cpp", directory: "/Inputs") +!2 = !{} +!3 = !{!4, !20} +!4 = !DICompositeType(tag: DW_TAG_structure_type, name: "S", file: !5, line: 1, size: 128, align: 64, elements: !6, identifier: "_ZTS1S") +!5 = !DIFile(filename: "./odr-types.h", directory: "/Inputs") +!6 = !{!7, !9, !10, !14, !17} +!7 = !DIDerivedType(tag: DW_TAG_member, name: "I", scope: !"_ZTS1S", file: !5, line: 2, baseType: !8, size: 32, align: 32) +!8 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +!9 = !DIDerivedType(tag: DW_TAG_member, name: "D", scope: !"_ZTS1S", file: !5, line: 15, baseType: !"_ZTSN1S6NestedE", size: 64, align: 64, offset: 64) +!10 = !DISubprogram(name: "incr", linkageName: "_ZN1S4incrEv", scope: !"_ZTS1S", file: !5, line: 4, type: !11, isLocal: false, isDefinition: false, scopeLine: 4, flags: DIFlagPrototyped, isOptimized: false) +!11 = !DISubroutineType(types: !12) +!12 = !{null, !13} +!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !"_ZTS1S", size: 64, align: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!14 = !DISubprogram(name: "incr", linkageName: "_ZN1S4incrEi", scope: !"_ZTS1S", file: !5, line: 5, type: !15, isLocal: false, isDefinition: false, scopeLine: 5, flags: DIFlagPrototyped, isOptimized: false) +!15 = !DISubroutineType(types: !16) +!16 = !{null, !13, !8} +!17 = !DISubprogram(name: "foo", linkageName: "_ZN1S3fooEv", scope: !"_ZTS1S", file: !5, line: 18, type: !18, isLocal: false, isDefinition: false, scopeLine: 18, flags: DIFlagPrototyped, isOptimized: false) +!18 = !DISubroutineType(types: !19) +!19 = !{!8, !13} +!20 = !DICompositeType(tag: DW_TAG_structure_type, name: "Nested", scope: !"_ZTS1S", file: !5, line: 9, size: 64, align: 64, elements: !21, identifier: "_ZTSN1S6NestedE") +!21 = !{!22} +!22 = !DIDerivedType(tag: DW_TAG_member, name: "D", scope: !"_ZTSN1S6NestedE", file: !5, line: 10, baseType: !23, size: 64, align: 64) +!23 = !DIBasicType(name: "double", size: 64, align: 64, encoding: DW_ATE_float) +!24 = !{!25} +!25 = !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !1, file: !1, line: 3, type: !26, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: false, function: void ()* @_Z3foov, variables: !2) +!26 = !DISubroutineType(types: !27) +!27 = !{null} +!28 = !{i32 2, !"Dwarf Version", i32 2} +!29 = !{i32 2, !"Debug Info Version", i32 3} +!30 = !{i32 1, !"PIC Level", i32 2} +!31 = !{!"clang version 3.8.0 (trunk 242534)"} +!32 = !DILocalVariable(tag: DW_TAG_auto_variable, name: "N", scope: !25, file: !1, line: 4, type: !"_ZTSN1S6NestedE") +!33 = !DIExpression() +!34 = !DILocation(line: 4, column: 12, scope: !25) +!35 = !DILocation(line: 5, column: 1, scope: !25) diff --git a/llvm/test/tools/dsymutil/X86/odr-1.test b/llvm/test/tools/dsymutil/X86/odr-1.test new file mode 100644 index 0000000..b650dc0 --- /dev/null +++ b/llvm/test/tools/dsymutil/X86/odr-1.test @@ -0,0 +1,423 @@ +# REQUIRES: object-emission +# RUN: rm -rf %t +# RUN: mkdir -p %t +# RUN: llc -filetype=obj %p/../Inputs/odr1.ll -o %t/odr1.o +# RUN: llc -filetype=obj %p/../Inputs/odr2.ll -o %t/odr2.o +# RUN: llc -filetype=obj %p/../Inputs/odr3.ll -o %t/odr3.o +# RUN: llc -filetype=obj %p/../Inputs/odr4.ll -o %t/odr4.o +# RUN: llc -filetype=obj %p/../Inputs/odr5.ll -o %t/odr5.o +# RUN: llc -filetype=obj %p/../Inputs/odr6.ll -o %t/odr6.o +# RUN: llc -filetype=obj %p/../Inputs/odr7.ll -o %t/odr7.o +# RUN: llvm-dsymutil -oso-prepend-path=%t -y %s -o - | llvm-dwarfdump -debug-dump=info - | FileCheck -check-prefix=ODR -check-prefix=CHECK %s +# RUN: llvm-dsymutil -oso-prepend-path=%t -y %s -no-odr -o - | llvm-dwarfdump -debug-dump=info - | FileCheck -check-prefix=NOODR -check-prefix=CHECK %s + +# Totally made up debug map to test ODR uniquing + +--- +triple: 'x86_64-unknown-unknown-macho' +objects: + - filename: odr1.o + symbols: + - { sym: __Z3foov, objAddr: 0x0, binAddr: 0x1000, size: 0x12 } + - { sym: __Z4funcv, objAddr: 0x0, binAddr: 0x2000, size: 0x12 } + - { sym: __ZZ4funcvENKUlvE_clEv, objAddr: 0x0, binAddr: 0x3000, size: 0x12 } + +# Check that all our types are in the first CU. + +# CHECK: TAG_compile_unit +# CHECK-NOT: {{DW_TAG|NULL}} +# CHECK: AT_name{{.*}}"odr1.cpp" + +# This is "struct S" + +# CHECK: 0x[[S:[0-9a-f]*]]:{{.*}}DW_TAG_structure_type +# CHECK-NEXT: DW_AT_name{{.*}}"S" +# CHECK-NOT: NULL +# CHECK: 0x[[NESTED:[0-9a-f]*]]:{{.*}}DW_TAG_structure_type +# CHECK-NOT: {{DW_TAG|NULL}} +# CHECK: DW_AT_name{{.*}}"Nested" +# CHECK: NULL +# CHECK: DW_TAG_subprogram +# CHECK: DW_AT_MIPS_linkage_name{{.*}}"_ZN1S4incrEv" +# CHECK: NULL +# CHECK: DW_TAG_subprogram +# CHECK: DW_AT_MIPS_linkage_name{{.*}}"_ZN1S4incrEi" +# CHECK: NULL +# CHECK: DW_TAG_subprogram +# CHECK: DW_AT_MIPS_linkage_name{{.*}}"_ZN1S3fooEv" +# CHECK: NULL +# CHECK: NULL + +# This is "class N::C" + +# CHECK: DW_TAG_namespace +# CHECK-NEXT: DW_AT_name{{.*}}"N" +# CHECK-NOT: NULL +# CHECK: 0x[[NC:[0-9a-f]*]]:{{.*}}DW_TAG_class_type +# CHECK-NEXT: DW_AT_name{{.*}}"C" +# CHECK: NULL + +# This is "class N::N::C" + +# CHECK: DW_TAG_namespace +# CHECK-NEXT: DW_AT_name{{.*}}"N" +# CHECK-NOT: NULL +# CHECK: 0x[[NNC:[0-9a-f]*]]:{{.*}}DW_TAG_class_type +# CHECK-NEXT: DW_AT_name{{.*}}"C" +# CHECK: NULL +# CHECK: NULL +# CHECK: NULL + +# This is "AliasForS" +# CHECK: 0x[[ALIASFORS:[0-9a-f]*]]:{{.*}}DW_TAG_typedef +# CHECK-NEXT: DW_AT_type{{.*}}[[S]] +# CHECK-NEXT: DW_AT_name{{.*}}"AliasForS" + +# This is "union U" + +# CHECK: 0x[[U:[0-9a-f]*]]:{{.*}}DW_TAG_union_type +# CHECK-NEXT: DW_AT_name{{.*}}"U" +# CHECK-NOT: NULL +# CHECK: 0x[[UC:[0-9a-f]*]]:{{.*}}DW_TAG_class_type +# CHECK-NOT: NULL +# CHECK: 0x[[US:[0-9a-f]*]]:{{.*}}DW_TAG_structure_type +# CHECK: NULL + +# This is "func" free function + +# CHECK: DW_TAG_subprogram +# CHECK-NOT: {{DW_TAG|NULL}} +# CHECK: DW_AT_name{{.*}}"func" +# CHECK: 0x[[CINSIDEFUNC:[0-9a-f]*]]:{{.*}}DW_TAG_structure_type +# CHECK-NEXT: DW_AT_name{{.*}}"CInsideFunc" + +# This is "(anonymous namespace)::AnonC" + +# CHECK: DW_TAG_namespace +# CHECK-NOT: {{DW_AT_name|NULL|DW_TAG}} +# CHECK: 0x[[ANONC:[0-9a-f]*]]:{{.*}}DW_TAG_class_type +# CHECK-NEXT: DW_AT_name{{.*}}"AnonC" + + - filename: odr1.o + symbols: + - { sym: __Z3foov, objAddr: 0x0, binAddr: 0x4000, size: 0x12 } + - { sym: __Z4funcv, objAddr: 0x0, binAddr: 0x5000, size: 0x12 } + - { sym: __ZZ4funcvENKUlvE_clEv, objAddr: 0x0, binAddr: 0x6000, size: 0x12 } + +# We relink the same file a second time. In the ODR case, everything (except for the +# union for now) should be uniqued. In the non-ODR case, we should get every type +# duplicated. + +# CHECK: TAG_compile_unit +# CHECK-NOT: {{DW_TAG|NULL}} +# CHECK: AT_name{{.*}}"odr1.cpp" + +# ODR: DW_TAG_union_type +# ODR-NEXT: DW_AT_name{{.*}}"U" +# Types defined inside the union should be uniqued: +# ODR: DW_TAG_member +# ODR-NEXT: DW_AT_name{{.*}}"C" +# ODR-NOT: {{NULL|DW_TAG}} +# ODR: DW_AT_type{{.*}}[[UC]] +# ODR: DW_TAG_member +# ODR-NEXT: DW_AT_name{{.*}}"S" +# ODR-NOT: {{NULL|DW_TAG}} +# ODR: DW_AT_type{{.*}}[[US]] + +# Skip func +# ODR: DW_TAG_subprogram +# ODR-NOT: {{NULL|DW_TAG}} +# ODR: DW_AT_name{{.*}}"func" +# ODR: NULL + +# ODR: DW_TAG_subprogram +# ODR-NOT: {{NULL|DW_TAG}} +# ODR: DW_AT_name{{.*}}"foo" +# ODR-NOT: NULL +# ODR: DW_TAG_variable +# ODR-NOT: {{DW_TAG|NULL}} +# ODR: DW_AT_name{{.*}}"s" +# ODR-NOT: {{DW_TAG|NULL}} +# ODR: DW_AT_type{{.*}}[[ALIASFORS]] +# ODR: DW_TAG_variable +# ODR-NOT: {{DW_TAG|NULL}} +# ODR: DW_AT_name{{.*}}"nc" +# ODR-NOT: {{DW_TAG|NULL}} +# ODR: DW_AT_type{{.*}}[[NC]] +# ODR: DW_TAG_variable +# ODR-NOT: {{DW_TAG|NULL}} +# ODR: DW_AT_name{{.*}}"nnc" +# ODR-NOT: {{DW_TAG|NULL}} +# ODR: DW_AT_type{{.*}}[[NNC]] +# ODR: DW_TAG_variable +# ODR-NOT: {{DW_TAG|NULL}} +# ODR: DW_AT_name{{.*}}"ac" +# ODR-NOT: {{DW_TAG|NULL}} +# ODR: DW_AT_type{{.*}}[[ANONC]] + +# ODR: DW_TAG_subprogram +# ODR-NOT: {{NULL|DW_TAG}} +# ODR: linkage_name{{.*}}"_ZZ4funcvENKUlvE_clEv" +# ODR-NOT: NULL +# ODR: DW_TAG_variable +# ODR-NOT: DW_TAG +# ODR: DW_AT_name{{.*}}"dummy" +# ODR-NOT: NULL +# ODR: DW_AT_type{{.*}}[[CINSIDEFUNC]] + +# With no ODR uniquing, we should get copies of all the types: + +# This is "struct S" +# NOODR: 0x[[DUP_S:[0-9a-f]*]]:{{.*}}DW_TAG_structure_type +# NOODR-NEXT: DW_AT_name{{.*}}"S" + +# This is "class N::C" +# NOODR: DW_TAG_namespace +# NOODR-NEXT: DW_AT_name{{.*}}"N" +# NOODR: 0x[[DUP_NC:[0-9a-f]*]]:{{.*}}DW_TAG_class_type +# NOODR-NEXT: DW_AT_name{{.*}}"C" + +# This is "class N::N::C" +# NOODR: DW_TAG_namespace +# NOODR-NEXT: DW_AT_name{{.*}}"N" +# NOODR: 0x[[DUP_NNC:[0-9a-f]*]]:{{.*}}DW_TAG_class_type +# NOODR-NEXT: DW_AT_name{{.*}}"C" + +# This is "AliasForS" +# NOODR: 0x[[DUP_ALIASFORS:[0-9a-f]*]]:{{.*}}DW_TAG_typedef +# NOODR-NOT: {{NULL|DW_TAG}} +# NOODR: DW_AT_name{{.*}}"AliasForS" + +# This is "union U" + +# NOODR: 0x[[U:[0-9a-f]*]]:{{.*}}DW_TAG_union_type +# NOODR-NEXT: DW_AT_name{{.*}}"U" +# NOODR-NOT: NULL +# NOODR: 0x[[DUP_UC:[0-9a-f]*]]:{{.*}}DW_TAG_class_type +# NOODR-NOT: NULL +# NOODR: 0x[[DUP_US:[0-9a-f]*]]:{{.*}}DW_TAG_structure_type +# NOODR: NULL + +# This is "func" free function + +# NOODR: DW_TAG_subprogram +# NOODR-NOT: {{DW_TAG|NULL}} +# NOODR: DW_AT_name{{.*}}"func" +# NOODR: 0x[[DUP_CINSIDEFUNC:[0-9a-f]*]]:{{.*}}DW_TAG_structure_type +# NOODR-NEXT: DW_AT_name{{.*}}"CInsideFunc" + +# NOODR: DW_TAG_subprogram +# NOODR-NOT: {{NULL|DW_TAG}} +# NOODR: DW_AT_name{{.*}}"foo" +# NOODR-NOT: NULL +# NOODR: DW_TAG_variable +# NOODR-NOT: {{DW_TAG|NULL}} +# NOODR: DW_AT_name{{.*}}"s" +# NOODR-NOT: {{DW_TAG|NULL}} +# NOODR: DW_AT_type{{.*}}[[DUP_ALIASFORS]] +# NOODR: DW_TAG_variable +# NOODR-NOT: {{DW_TAG|NULL}} +# NOODR: DW_AT_name{{.*}}"nc" +# NOODR-NOT: {{DW_TAG|NULL}} +# NOODR: DW_AT_type{{.*}}[[DUP_NC]] +# NOODR: DW_TAG_variable +# NOODR-NOT: {{DW_TAG|NULL}} +# NOODR: DW_AT_name{{.*}}"nnc" +# NOODR-NOT: {{DW_TAG|NULL}} +# NOODR: DW_AT_type{{.*}}[[DUP_NNC]] +# NOODR: DW_TAG_variable +# NOODR-NOT: {{DW_TAG|NULL}} +# NOODR: DW_AT_name{{.*}}"ac" +# NOODR-NOT: {{DW_TAG|NULL}} +# NOODR: DW_AT_type{{.*}}0x[[DUP_ANONC:[0-9a-f]*]] + +# This is the lanbda inside func + +# NOODR: DW_TAG_subprogram +# NOODR-NOT: {{NULL|DW_TAG}} +# NOODR: linkage_name{{.*}}"_ZZ4funcvENKUlvE_clEv" +# NOODR-NOT: NULL +# NOODR: DW_TAG_variable +# NOODR-NOT: DW_TAG +# NOODR: DW_AT_name{{.*}}"dummy" +# NOODR-NOT: NULL +# NOODR: DW_AT_type{{.*}}[[DUP_CINSIDEFUNC]] + +# This is "(anonymous namespace)::AnonC" + +# NOODR: DW_TAG_namespace +# NOODR-NOT: {{DW_AT_name|NULL|DW_TAG}} +# NOODR: 0x[[DUP_ANONC]]:{{.*}}DW_TAG_class_type +# NOODR-NEXT: DW_AT_name{{.*}}"AnonC" + + - filename: odr2.o + symbols: + - { sym: __ZN1S3fooEv, objAddr: 0x0, binAddr: 0x7000, size: 0x12 } + - { sym: __Z3barv, objAddr: 0x0, binAddr: 0x8000, size: 0x12 } + - filename: odr3.o + symbols: + - { sym: __ZN1S3fooEv, objAddr: 0x0, binAddr: 0x8000, size: 0x12 } + - { sym: __Z3barv, objAddr: 0x0, binAddr: 0x9000, size: 0x12 } + +# odr2.cpp and odr3.cpp test that a simple overloaded function doesn't break the +# uniquing (contrary to what we'll see with template/artificial) functions. + +# CHECK: TAG_compile_unit +# CHECK-NOT: {{DW_TAG|NULL}} +# CHECK: AT_name{{.*}}"odr2.cpp" + +# NO-ODR: DW_TAG_structure_type +# ODR-NOT: DW_TAG_structure_type + +# ODR: DW_TAG_subprogram +# ODR: DW_AT_specification{{.*}}4incr +# ODR: DW_TAG_formal_parameter +# ODR-NEXT: DW_AT_name{{.*}}"this" +# ODR-NEXT: DW_AT_type{{.*}}0x00000000[[S_PTR:[0-9a-f]*]] +# ODR: 0x[[S_PTR]]:{{.*}}DW_TAG_pointer_type +# ODR-NEXT: DW_AT_type{{.*}}[[S]] +# ODR: DW_TAG_subprogram +# ODR-NOT: {{DW_TAG|NULL}} +# ODR: DW_AT_name{{.*}}"bar" +# ODR-NOT: NULL +# ODR: DW_TAG_variable +# ODR-NOT: {{DW_TAG|NULL}} +# ODR: DW_AT_type{{.*}}[[S]] +# ODR-NOT NULL +# DOR: DW_TAG_inlined_subroutine +# ODR-NOT NULL +# ODR: DW_TAG_formal_parameter +# ODR-NOT {{NULL|DW_TAG}} +# ODR: DW_AT_type{{.*}}[[S_PTR]] + +# CHECK: TAG_compile_unit +# CHECK-NOT: {{DW_TAG|NULL}} +# CHECK: AT_name{{.*}}"odr3.cpp" + +# NO-ODR: DW_TAG_structure_type +# ODR-NOT: DW_TAG_structure_type + +# ODR: DW_TAG_subprogram +# ODR: DW_AT_specification{{.*}}4incr +# ODR: DW_TAG_formal_parameter +# ODR-NEXT: DW_AT_name{{.*}}"this" +# ODR-NEXT: DW_AT_type{{.*}}0x00000000[[S_PTR2:[0-9a-f]*]] +# ODR: 0x[[S_PTR2]]:{{.*}}DW_TAG_pointer_type +# ODR-NEXT: DW_AT_type{{.*}}[[S]] +# ODR: DW_TAG_subprogram +# ODR-NOT: {{DW_TAG|NULL}} +# ODR: DW_AT_name{{.*}}"bar" +# ODR-NOT: NULL +# ODR: DW_TAG_variable +# ODR-NOT: {{DW_TAG|NULL}} +# ODR: DW_AT_type{{.*}}[[S]] +# ODR-NOT NULL +# DOR: DW_TAG_inlined_subroutine +# ODR-NOT NULL +# ODR: DW_TAG_formal_parameter +# ODR-NOT {{NULL|DW_TAG}} +# ODR: DW_AT_type{{.*}}[[S_PTR2]] + + - filename: odr4.o + symbols: + - { sym: __Z3bazv, objAddr: 0x0, binAddr: 0xa000, size: 0x12 } + +# odr4.cpp helps check that anonymous namespaces with similarly named contents do +# not get uniqued. + +# CHECK: TAG_compile_unit +# CHECK-NOT: {{DW_TAG|NULL}} +# CHECK: AT_name{{.*}}"odr4.cpp" + +# CHECK: DW_TAG_subprogram +# CHECK-NOT: NULL +# CHECK: DW_TAG_variable +# CHECK-NOT: DW_TAG +# ODR: DW_AT_type{{.*}}[[LOCALANONC:........]]) +# NOODR: DW_AT_type{{.*}}[[LOCALANONC:........]]}) + +# CHECK: DW_TAG_namespace +# CHECK-NOT: DW_AT_name +# CHECK: [[LOCALANONC]]{{.*}}DW_TAG_class_type +# CHECK-NOT: {{NULL|DW_TAG}} +# CHECK: DW_AT_name{{.*}}"AnonC" + + - filename: odr5.o + symbols: + - { sym: __Z3bazv, objAddr: 0x0, binAddr: 0xb000, size: 0x12 } + - { sym: __ZN1S6Nested4initIiEEvT_, objAddr: 0x0, binAddr: 0xc000, size: 0x12 } + - filename: odr6.o + symbols: + - { sym: __Z3bazv, objAddr: 0x0, binAddr: 0xd000, size: 0x12 } + - { sym: __ZN1S6Nested4initIdEEvT_, objAddr: 0x0, binAddr: 0xe000, size: 0x12 } + +# odr5.cpp and odr6.cpp instanciate a template member function of the S class. +# They instanciate it with different types. As DWARF only describes the actual +# intances, these members aren't described in the uniqued class definition of +# odr1.cpp. Both these files should contain a new copy of S' definition with +# the template instance included. + +# CHECK: TAG_compile_unit +# CHECK-NOT: {{DW_TAG|NULL}} +# CHECK: AT_name{{.*}}"odr5.cpp" + +# CHECK: 0x{{[0-9a-f]*}}:{{.*}}DW_TAG_structure_type +# CHECK-NEXT: DW_AT_name{{.*}}"S" +# CHECK-NOT: NULL +# CHECK: 0x[[NESTED2:[0-9a-f]*]]:{{.*}}DW_TAG_structure_type +# CHECK-NOT: {{DW_TAG|NULL}} +# CHECK: DW_AT_name{{.*}}"Nested" +# CHECK-NOT: NULL +# CHECK: 0x[[INITTEMPLATE:[0-9a-f]*]]:{{.*}}DW_TAG_subprogram +# CHECK-NEXT:{{.*}}"_ZN1S6Nested4init + +# CHECK: DW_AT_specification{{.*}}[[INITTEMPLATE]] +# CHECK: DW_TAG_formal_parameter +# CHECK-NOT: DW_TAG +# CHECK: DW_AT_type{{.*}}[[NESTED_PTR:[0-9a-f]{8}]]{{[}]?}}) + +# CHECK: 0x[[NESTED_PTR]]{{.*}}DW_TAG_pointer_type +# ODR-NEXT: DW_AT_type{{.*}}[[NESTED]] +# NOODR-NEXT: DW_AT_type{{.*}}[[NESTED2]] + +# CHECK: TAG_compile_unit +# CHECK-NOT: {{DW_TAG|NULL}} +# CHECK: AT_name{{.*}}"odr6.cpp" + +# CHECK: 0x{{[0-9a-f]*}}:{{.*}}DW_TAG_structure_type +# CHECK-NEXT: DW_AT_name{{.*}}"S" +# CHECK-NOT: NULL +# CHECK: 0x[[NESTED3:[0-9a-f]*]]:{{.*}}DW_TAG_structure_type +# CHECK-NOT: {{DW_TAG|NULL}} +# CHECK: DW_AT_name{{.*}}"Nested" +# CHECK-NOT: NULL +# CHECK: 0x[[INITTEMPLATE2:[0-9a-f]*]]:{{.*}}DW_TAG_subprogram +# CHECK-NEXT:{{.*}}"_ZN1S6Nested4init + +# CHECK: DW_AT_specification{{.*}}[[INITTEMPLATE2]] +# CHECK: DW_TAG_formal_parameter +# CHECK-NOT: DW_TAG +# CHECK: DW_AT_type{{.*}}[[NESTED_PTR2:[0-9a-f]{8}]]{{[}]?}}) + +# CHECK: 0x[[NESTED_PTR2]]{{.*}}DW_TAG_pointer_type +# ODR-NEXT: DW_AT_type{{.*}}[[NESTED]] +# NOODR-NEXT: DW_AT_type{{.*}}[[NESTED3]] + + - filename: odr7.o + symbols: + - { sym: __Z3foov, objAddr: 0x0, binAddr: 0xf000, size: 0x12 } + +# Check that a reference to a nested class correctly refers to the original +# definition + +# CHECK: TAG_compile_unit +# CHECK-NOT: {{DW_TAG|NULL}} +# CHECK: AT_name{{.*}}"odr7.cpp" + +# ODR: DW_TAG_subprogram +# ODR-NOT: NULL +# ODR: DW_TAG_variable +# ODR-NOT: DW_TAG +# ODR: DW_AT_type{{.*}}[[NESTED]] +... + diff --git a/llvm/tools/dsymutil/DwarfLinker.cpp b/llvm/tools/dsymutil/DwarfLinker.cpp index 86c22dd..211f6cb 100644 --- a/llvm/tools/dsymutil/DwarfLinker.cpp +++ b/llvm/tools/dsymutil/DwarfLinker.cpp @@ -15,6 +15,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/DIE.h" +#include "llvm/Config/config.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" @@ -80,6 +81,112 @@ struct PatchLocation { } }; +class CompileUnit; +struct DeclMapInfo; +class NonRelocatableStringpool; + +/// A DeclContext is a named program scope that is used for ODR +/// uniquing of types. +/// The set of DeclContext for the ODR-subject parts of a Dwarf link +/// is expanded (and uniqued) with each new object file processed. We +/// need to determine the context of each DIE in an linked object file +/// to see if the corresponding type has already been emitted. +/// +/// The contexts are conceptually organised as a tree (eg. a function +/// scope is contained in a namespace scope that contains other +/// scopes), but storing/accessing them in an actual tree is too +/// inefficient: we need to be able to very quickly query a context +/// for a given child context by name. Storing a StringMap in each +/// DeclContext would be too space inefficient. +/// The solution here is to give each DeclContext a link to its parent +/// (this allows to walk up the tree), but to query the existance of a +/// specific DeclContext using a separate DenseMap keyed on the hash +/// of the fully qualified name of the context. +class DeclContext { + unsigned QualifiedNameHash; + uint32_t Line; + uint32_t ByteSize; + uint16_t Tag; + StringRef Name; + StringRef File; + const DeclContext &Parent; + const DWARFDebugInfoEntryMinimal *LastSeenDIE; + uint32_t LastSeenCompileUnitID; + uint32_t CanonicalDIEOffset; + + friend DeclMapInfo; + +public: + typedef DenseSet Map; + + DeclContext() + : QualifiedNameHash(0), Line(0), ByteSize(0), + Tag(dwarf::DW_TAG_compile_unit), Name(), File(), Parent(*this), + LastSeenDIE(nullptr), LastSeenCompileUnitID(0), CanonicalDIEOffset(0) {} + + DeclContext(unsigned Hash, uint32_t Line, uint32_t ByteSize, uint16_t Tag, + StringRef Name, StringRef File, const DeclContext &Parent, + const DWARFDebugInfoEntryMinimal *LastSeenDIE = nullptr, + unsigned CUId = 0) + : QualifiedNameHash(Hash), Line(Line), ByteSize(ByteSize), Tag(Tag), + Name(Name), File(File), Parent(Parent), LastSeenDIE(LastSeenDIE), + LastSeenCompileUnitID(CUId), CanonicalDIEOffset(0) {} + + uint32_t getQualifiedNameHash() const { return QualifiedNameHash; } + + bool setLastSeenDIE(CompileUnit &U, const DWARFDebugInfoEntryMinimal *Die); + + uint32_t getCanonicalDIEOffset() const { return CanonicalDIEOffset; } + void setCanonicalDIEOffset(uint32_t Offset) { CanonicalDIEOffset = Offset; } + + uint16_t getTag() const { return Tag; } + StringRef getName() const { return Name; } +}; + +/// Info type for the DenseMap storing the DeclContext pointers. +struct DeclMapInfo : private DenseMapInfo { + using DenseMapInfo::getEmptyKey; + using DenseMapInfo::getTombstoneKey; + + static unsigned getHashValue(const DeclContext *Ctxt) { + return Ctxt->QualifiedNameHash; + } + + static bool isEqual(const DeclContext *LHS, const DeclContext *RHS) { + if (RHS == getEmptyKey() || RHS == getTombstoneKey()) + return RHS == LHS; + return LHS->QualifiedNameHash == RHS->QualifiedNameHash && + LHS->Line == RHS->Line && LHS->ByteSize == RHS->ByteSize && + LHS->Name.data() == RHS->Name.data() && + LHS->File.data() == RHS->File.data() && + LHS->Parent.QualifiedNameHash == RHS->Parent.QualifiedNameHash; + } +}; + +/// This class gives a tree-like API to the DenseMap that stores the +/// DeclContext objects. It also holds the BumpPtrAllocator where +/// these objects will be allocated. +class DeclContextTree { + BumpPtrAllocator Allocator; + DeclContext Root; + DeclContext::Map Contexts; + +public: + /// Get the child of \a Context described by \a DIE in \a Unit. The + /// required strings will be interned in \a StringPool. + /// \returns The child DeclContext along with one bit that is set if + /// this context is invalid. + /// FIXME: the invalid bit along the return value is to emulate some + /// dsymutil-classic functionality. See the fucntion definition for + /// a more thorough discussion of its use. + PointerIntPair + getChildDeclContext(DeclContext &Context, + const DWARFDebugInfoEntryMinimal *DIE, CompileUnit &Unit, + NonRelocatableStringpool &StringPool); + + DeclContext &getRoot() { return Root; } +}; + /// \brief Stores all information relating to a compile unit, be it in /// its original instance in the object file to its brand new cloned /// and linked DIE tree. @@ -88,16 +195,26 @@ public: /// \brief Information gathered about a DIE in the object file. struct DIEInfo { int64_t AddrAdjust; ///< Address offset to apply to the described entity. + DeclContext *Ctxt; ///< ODR Declaration context. DIE *Clone; ///< Cloned version of that DIE. uint32_t ParentIdx; ///< The index of this DIE's parent. bool Keep; ///< Is the DIE part of the linked output? bool InDebugMap; ///< Was this DIE's entity found in the map? }; - CompileUnit(DWARFUnit &OrigUnit, unsigned ID) + CompileUnit(DWARFUnit &OrigUnit, unsigned ID, bool CanUseODR) : OrigUnit(OrigUnit), ID(ID), LowPc(UINT64_MAX), HighPc(0), RangeAlloc(), Ranges(RangeAlloc) { Info.resize(OrigUnit.getNumDIEs()); + + const auto *CUDie = OrigUnit.getUnitDIE(false); + unsigned Lang = CUDie->getAttributeValueAsUnsignedConstant( + &OrigUnit, dwarf::DW_AT_language, 0); + HasODR = CanUseODR && (Lang == dwarf::DW_LANG_C_plus_plus || + Lang == dwarf::DW_LANG_C_plus_plus_03 || + Lang == dwarf::DW_LANG_C_plus_plus_11 || + Lang == dwarf::DW_LANG_C_plus_plus_14 || + Lang == dwarf::DW_LANG_ObjC_plus_plus); } CompileUnit(CompileUnit &&RHS) @@ -116,6 +233,8 @@ public: DIE *getOutputUnitDIE() const { return CUDie; } void setOutputUnitDIE(DIE *Die) { CUDie = Die; } + bool hasODR() const { return HasODR; } + DIEInfo &getInfo(unsigned Idx) { return Info[Idx]; } const DIEInfo &getInfo(unsigned Idx) const { return Info[Idx]; } @@ -147,9 +266,10 @@ public: /// \brief Keep track of a forward reference to DIE \p Die in \p /// RefUnit by \p Attr. The attribute should be fixed up later to - /// point to the absolute offset of \p Die in the debug_info section. + /// point to the absolute offset of \p Die in the debug_info section + /// or to the canonical offset of \p Ctxt if it is non-null. void noteForwardReference(DIE *Die, const CompileUnit *RefUnit, - PatchLocation Attr); + DeclContext *Ctxt, PatchLocation Attr); /// \brief Apply all fixups recored by noteForwardReference(). void fixupForwardReferences(); @@ -190,11 +310,27 @@ public: const std::vector &getPubnames() const { return Pubnames; } const std::vector &getPubtypes() const { return Pubtypes; } + /// Get the full path for file \a FileNum in the line table + const char *getResolvedPath(unsigned FileNum) { + if (FileNum >= ResolvedPaths.size()) + return nullptr; + return ResolvedPaths[FileNum].size() ? ResolvedPaths[FileNum].c_str() + : nullptr; + } + + /// Set the fully resolved path for the line-table's file \a FileNum + /// to \a Path. + void setResolvedPath(unsigned FileNum, const std::string &Path) { + if (ResolvedPaths.size() <= FileNum) + ResolvedPaths.resize(FileNum + 1); + ResolvedPaths[FileNum] = Path; + } + private: DWARFUnit &OrigUnit; unsigned ID; - std::vector Info; ///< DIE info indexed by DIE index. - DIE *CUDie; ///< Root of the linked DIE tree. + std::vector Info; ///< DIE info indexed by DIE index. + DIE *CUDie; ///< Root of the linked DIE tree. uint64_t StartOffset; uint64_t NextUnitOffset; @@ -208,8 +344,8 @@ private: /// The offsets for the attributes in this array couldn't be set while /// cloning because for cross-cu forward refences the target DIE's /// offset isn't known you emit the reference attribute. - std::vector> - ForwardDIEReferences; + std::vector> ForwardDIEReferences; FunctionIntervals::Allocator RangeAlloc; /// \brief The ranges in that interval map are the PC ranges for @@ -236,6 +372,12 @@ private: std::vector Pubnames; std::vector Pubtypes; /// @} + + /// Cached resolved paths from the line table. + std::vector ResolvedPaths; + + /// Is this unit subject to the ODR rule? + bool HasODR; }; uint64_t CompileUnit::computeNextUnitOffset() { @@ -251,8 +393,8 @@ uint64_t CompileUnit::computeNextUnitOffset() { /// \brief Keep track of a forward cross-cu reference from this unit /// to \p Die that lives in \p RefUnit. void CompileUnit::noteForwardReference(DIE *Die, const CompileUnit *RefUnit, - PatchLocation Attr) { - ForwardDIEReferences.emplace_back(Die, RefUnit, Attr); + DeclContext *Ctxt, PatchLocation Attr) { + ForwardDIEReferences.emplace_back(Die, RefUnit, Ctxt, Attr); } /// \brief Apply all fixups recorded by noteForwardReference(). @@ -261,8 +403,12 @@ void CompileUnit::fixupForwardReferences() { DIE *RefDie; const CompileUnit *RefUnit; PatchLocation Attr; - std::tie(RefDie, RefUnit, Attr) = Ref; - Attr.set(RefDie->getOffset() + RefUnit->getStartOffset()); + DeclContext *Ctxt; + std::tie(RefDie, RefUnit, Ctxt, Attr) = Ref; + if (Ctxt && Ctxt->getCanonicalDIEOffset()) + Attr.set(Ctxt->getCanonicalDIEOffset()); + else + Attr.set(RefDie->getOffset() + RefUnit->getStartOffset()); } } @@ -324,6 +470,12 @@ public: /// one. uint32_t getStringOffset(StringRef S); + /// \brief Get permanent storage for \p S (but do not necessarily + /// emit \p S in the output section). + /// \returns The StringRef that points to permanent storage to use + /// in place of \p S. + StringRef internString(StringRef S); + // \brief Return the first entry of the string table. const MapTy::MapEntryTy *getFirstEntry() const { return getNextEntry(&Sentinel); @@ -367,6 +519,16 @@ uint32_t NonRelocatableStringpool::getStringOffset(StringRef S) { return It->getValue().first; } +/// \brief Put \p S into the StringMap so that it gets permanent +/// storage, but do not actually link it in the chain of elements +/// that go into the output section. A latter call to +/// getStringOffset() with the same string will chain it though. +StringRef NonRelocatableStringpool::internString(StringRef S) { + std::pair Entry(0, nullptr); + auto InsertResult = Strings.insert(std::make_pair(S, Entry)); + return InsertResult.first->getKey(); +}; + /// \brief The Dwarf streaming logic /// /// All interactions with the MC layer that is used to build the debug @@ -1089,6 +1251,7 @@ private: TF_InFunctionScope = 1 << 1, ///< Current scope is a fucntion scope. TF_DependencyWalk = 1 << 2, ///< Walking the dependencies of a kept DIE. TF_ParentWalk = 1 << 3, ///< Walking up the parents of a kept DIE. + TF_ODR = 1 << 4, ///< Use the ODR whhile keeping dependants. }; /// \brief Mark the passed DIE as well as all the ones it depends on @@ -1096,7 +1259,7 @@ private: void keepDIEAndDenpendencies(const DWARFDebugInfoEntryMinimal &DIE, CompileUnit::DIEInfo &MyInfo, const DebugMapObject &DMO, CompileUnit &CU, - unsigned Flags); + bool UseODR); unsigned shouldKeepDIE(const DWARFDebugInfoEntryMinimal &DIE, CompileUnit &Unit, CompileUnit::DIEInfo &MyInfo, @@ -1227,11 +1390,14 @@ private: BumpPtrAllocator DIEAlloc; /// @} + /// ODR Contexts for that link. + DeclContextTree ODRContexts; + /// \defgroup Helpers Various helper methods. /// /// @{ const DWARFDebugInfoEntryMinimal * - resolveDIEReference(DWARFFormValue &RefValue, const DWARFUnit &Unit, + resolveDIEReference(const DWARFFormValue &RefValue, const DWARFUnit &Unit, const DWARFDebugInfoEntryMinimal &DIE, CompileUnit *&ReferencedCU); @@ -1296,7 +1462,7 @@ CompileUnit *DwarfLinker::getUnitForOffset(unsigned Offset) { /// CompileUnit which is stored into \p ReferencedCU. /// \returns null if resolving fails for any reason. const DWARFDebugInfoEntryMinimal *DwarfLinker::resolveDIEReference( - DWARFFormValue &RefValue, const DWARFUnit &Unit, + const DWARFFormValue &RefValue, const DWARFUnit &Unit, const DWARFDebugInfoEntryMinimal &DIE, CompileUnit *&RefCU) { assert(RefValue.isFormClass(DWARFFormValue::FC_Reference)); uint64_t RefOffset = *RefValue.getAsReference(&Unit); @@ -1309,6 +1475,220 @@ const DWARFDebugInfoEntryMinimal *DwarfLinker::resolveDIEReference( return nullptr; } +/// \returns whether the passed \a Attr type might contain a DIE +/// reference suitable for ODR uniquing. +static bool isODRAttribute(uint16_t Attr) { + switch (Attr) { + default: + return false; + case dwarf::DW_AT_type: + case dwarf::DW_AT_containing_type: + case dwarf::DW_AT_specification: + case dwarf::DW_AT_abstract_origin: + case dwarf::DW_AT_import: + return true; + } + llvm_unreachable("Improper attribute."); +} + +/// Set the last DIE/CU a context was seen in and, possibly invalidate +/// the context if it is ambiguous. +/// +/// In the current implementation, we don't handle overloaded +/// functions well, because the argument types are not taken into +/// account when computing the DeclContext tree. +/// +/// Some of this is mitigated byt using mangled names that do contain +/// the arguments types, but sometimes (eg. with function templates) +/// we don't have that. In that case, just do not unique anything that +/// refers to the contexts we are not able to distinguish. +/// +/// If a context that is not a namespace appears twice in the same CU, +/// we know it is ambiguous. Make it invalid. +bool DeclContext::setLastSeenDIE(CompileUnit &U, + const DWARFDebugInfoEntryMinimal *Die) { + if (LastSeenCompileUnitID == U.getUniqueID()) { + DWARFUnit &OrigUnit = U.getOrigUnit(); + uint32_t FirstIdx = OrigUnit.getDIEIndex(LastSeenDIE); + U.getInfo(FirstIdx).Ctxt = nullptr; + return false; + } + + LastSeenCompileUnitID = U.getUniqueID(); + LastSeenDIE = Die; + return true; +} + +/// Get the child context of \a Context corresponding to \a DIE. +/// +/// \returns the child context or null if we shouldn't track children +/// contexts. It also returns an additional bit meaning 'invalid'. An +/// invalid context means it shouldn't be considered for uniquing, but +/// its not returning null, because some children of that context +/// might be uniquing candidates. +/// FIXME: this is for dsymutil-classic compatibility, I don't think +/// it buys us much. +PointerIntPair DeclContextTree::getChildDeclContext( + DeclContext &Context, const DWARFDebugInfoEntryMinimal *DIE, CompileUnit &U, + NonRelocatableStringpool &StringPool) { + unsigned Tag = DIE->getTag(); + + // FIXME: dsymutil-classic compat: We should bail out here if we + // have a specification or an abstract_origin. We will get the + // parent context wrong here. + + switch (Tag) { + default: + // By default stop gathering child contexts. + return PointerIntPair(nullptr); + case dwarf::DW_TAG_compile_unit: + // FIXME: Add support for DW_TAG_module. + return PointerIntPair(&Context); + case dwarf::DW_TAG_subprogram: + // Do not unique anything inside CU local functions. + if ((Context.getTag() == dwarf::DW_TAG_namespace || + Context.getTag() == dwarf::DW_TAG_compile_unit) && + !DIE->getAttributeValueAsUnsignedConstant(&U.getOrigUnit(), + dwarf::DW_AT_external, 0)) + return PointerIntPair(nullptr); + // Fallthrough + case dwarf::DW_TAG_member: + case dwarf::DW_TAG_namespace: + case dwarf::DW_TAG_structure_type: + case dwarf::DW_TAG_class_type: + case dwarf::DW_TAG_union_type: + case dwarf::DW_TAG_enumeration_type: + case dwarf::DW_TAG_typedef: + // Artificial things might be ambiguous, because they might be + // created on demand. For example implicitely defined constructors + // are ambiguous because of the way we identify contexts, and they + // won't be generated everytime everywhere. + if (DIE->getAttributeValueAsUnsignedConstant(&U.getOrigUnit(), + dwarf::DW_AT_artificial, 0)) + return PointerIntPair(nullptr); + break; + } + + const char *Name = DIE->getName(&U.getOrigUnit(), DINameKind::LinkageName); + const char *ShortName = DIE->getName(&U.getOrigUnit(), DINameKind::ShortName); + StringRef NameRef; + StringRef ShortNameRef; + StringRef FileRef; + + if (Name) + NameRef = StringPool.internString(Name); + else if (Tag == dwarf::DW_TAG_namespace) + // FIXME: For dsymutil-classic compatibility. I think uniquing + // within anonymous namespaces is wrong. There is no ODR guarantee + // there. + NameRef = StringPool.internString("(anonymous namespace)"); + + if (ShortName && ShortName != Name) + ShortNameRef = StringPool.internString(ShortName); + else + ShortNameRef = NameRef; + + if (Tag != dwarf::DW_TAG_class_type && Tag != dwarf::DW_TAG_structure_type && + Tag != dwarf::DW_TAG_union_type && + Tag != dwarf::DW_TAG_enumeration_type && NameRef.empty()) + return PointerIntPair(nullptr); + + std::string File; + unsigned Line = 0; + unsigned ByteSize = 0; + + // Gather some discriminating data about the DeclContext we will be + // creating: File, line number and byte size. This shouldn't be + // necessary, because the ODR is just about names, but given that we + // do some approximations with overloaded functions and anonymous + // namespaces, use these additional data points to make the process safer. + ByteSize = DIE->getAttributeValueAsUnsignedConstant( + &U.getOrigUnit(), dwarf::DW_AT_byte_size, UINT64_MAX); + if (Tag != dwarf::DW_TAG_namespace || !Name) { + if (unsigned FileNum = DIE->getAttributeValueAsUnsignedConstant( + &U.getOrigUnit(), dwarf::DW_AT_decl_file, 0)) { + if (const auto *LT = U.getOrigUnit().getContext().getLineTableForUnit( + &U.getOrigUnit())) { + // FIXME: dsymutil-classic compatibility. I'd rather not + // unique anything in anonymous namespaces, but if we do, then + // verify that the file and line correspond. + if (!Name && Tag == dwarf::DW_TAG_namespace) + FileNum = 1; + + // FIXME: Passing U.getOrigUnit().getCompilationDir() + // instead of "" would allow more uniquing, but for now, do + // it this way to match dsymutil-classic. + if (LT->getFileNameByIndex( + FileNum, "", + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, + File)) { + Line = DIE->getAttributeValueAsUnsignedConstant( + &U.getOrigUnit(), dwarf::DW_AT_decl_line, 0); +#ifdef HAVE_REALPATH + // Cache the resolved paths, because calling realpath is expansive. + if (const char *ResolvedPath = U.getResolvedPath(FileNum)) { + File = ResolvedPath; + } else { + char RealPath[PATH_MAX + 1]; + RealPath[PATH_MAX] = 0; + if (::realpath(File.c_str(), RealPath)) + File = RealPath; + U.setResolvedPath(FileNum, File); + } +#endif + FileRef = StringPool.internString(File); + } + } + } + } + + if (!Line && NameRef.empty()) + return PointerIntPair(nullptr); + + // FIXME: dsymutil-classic compat won't unique the same type + // presented once as a struct and once as a class. Use the Tag in + // the fully qualified name hash to get the same effect. + // We hash NameRef, which is the mangled name, in order to get most + // overloaded functions resolvec correctly. + unsigned Hash = hash_combine(Context.getQualifiedNameHash(), Tag, NameRef); + + // FIXME: dsymutil-classic compatibility: when we don't have a name, + // use the filename. + if (Tag == dwarf::DW_TAG_namespace && NameRef == "(anonymous namespace)") + Hash = hash_combine(Hash, FileRef); + + // Now look if this context already exists. + DeclContext Key(Hash, Line, ByteSize, Tag, NameRef, FileRef, Context); + auto ContextIter = Contexts.find(&Key); + + if (ContextIter == Contexts.end()) { + // The context wasn't found. + bool Inserted; + DeclContext *NewContext = + new (Allocator) DeclContext(Hash, Line, ByteSize, Tag, NameRef, FileRef, + Context, DIE, U.getUniqueID()); + std::tie(ContextIter, Inserted) = Contexts.insert(NewContext); + assert(Inserted && "Failed to insert DeclContext"); + (void)Inserted; + } else if (Tag != dwarf::DW_TAG_namespace && + !(*ContextIter)->setLastSeenDIE(U, DIE)) { + // The context was found, but it is ambiguous with another context + // in the same file. Mark it invalid. + return PointerIntPair(*ContextIter, /* Invalid= */ 1); + } + + assert(ContextIter != Contexts.end()); + // FIXME: dsymutil-classic compatibility. Union types aren't + // uniques, but their children might be. + if ((Tag == dwarf::DW_TAG_subprogram && + Context.getTag() != dwarf::DW_TAG_structure_type && + Context.getTag() != dwarf::DW_TAG_class_type) || + (Tag == dwarf::DW_TAG_union_type)) + return PointerIntPair(*ContextIter, /* Invalid= */ 1); + + return PointerIntPair(*ContextIter); +} + /// \brief Get the potential name and mangled name for the entity /// described by \p Die and store them in \Info if they are not /// already there. @@ -1355,14 +1735,30 @@ bool DwarfLinker::createStreamer(Triple TheTriple, StringRef OutputFilename) { /// \brief Recursive helper to gather the child->parent relationships in the /// original compile unit. static void gatherDIEParents(const DWARFDebugInfoEntryMinimal *DIE, - unsigned ParentIdx, CompileUnit &CU) { + unsigned ParentIdx, CompileUnit &CU, + DeclContext *CurrentDeclContext, + NonRelocatableStringpool &StringPool, + DeclContextTree &Contexts) { unsigned MyIdx = CU.getOrigUnit().getDIEIndex(DIE); - CU.getInfo(MyIdx).ParentIdx = ParentIdx; + CompileUnit::DIEInfo &Info = CU.getInfo(MyIdx); + + Info.ParentIdx = ParentIdx; + if (CU.hasODR()) { + if (CurrentDeclContext) { + auto PtrInvalidPair = Contexts.getChildDeclContext(*CurrentDeclContext, + DIE, CU, StringPool); + CurrentDeclContext = PtrInvalidPair.getPointer(); + Info.Ctxt = + PtrInvalidPair.getInt() ? nullptr : PtrInvalidPair.getPointer(); + } else + Info.Ctxt = CurrentDeclContext = nullptr; + } if (DIE->hasChildren()) for (auto *Child = DIE->getFirstChild(); Child && !Child->isNULL(); Child = Child->getSibling()) - gatherDIEParents(Child, MyIdx, CU); + gatherDIEParents(Child, MyIdx, CU, CurrentDeclContext, StringPool, + Contexts); } static bool dieNeedsChildrenToBeMeaningful(uint32_t Tag) { @@ -1380,6 +1776,12 @@ static bool dieNeedsChildrenToBeMeaningful(uint32_t Tag) { llvm_unreachable("Invalid Tag"); } +static unsigned getRefAddrSize(const DWARFUnit &U) { + if (U.getVersion() == 2) + return U.getAddressByteSize(); + return 4; +} + void DwarfLinker::startDebugObject(DWARFContext &Dwarf, DebugMapObject &Obj) { Units.reserve(Dwarf.getNumCompileUnits()); NextValidReloc = 0; @@ -1686,15 +2088,16 @@ unsigned DwarfLinker::shouldKeepDIE(const DWARFDebugInfoEntryMinimal &DIE, void DwarfLinker::keepDIEAndDenpendencies(const DWARFDebugInfoEntryMinimal &DIE, CompileUnit::DIEInfo &MyInfo, const DebugMapObject &DMO, - CompileUnit &CU, unsigned Flags) { + CompileUnit &CU, bool UseODR) { const DWARFUnit &Unit = CU.getOrigUnit(); MyInfo.Keep = true; // First mark all the parent chain as kept. unsigned AncestorIdx = MyInfo.ParentIdx; while (!CU.getInfo(AncestorIdx).Keep) { + unsigned ODRFlag = UseODR ? TF_ODR : 0; lookForDIEsToKeep(*Unit.getDIEAtIndex(AncestorIdx), DMO, CU, - TF_ParentWalk | TF_Keep | TF_DependencyWalk); + TF_ParentWalk | TF_Keep | TF_DependencyWalk | ODRFlag); AncestorIdx = CU.getInfo(AncestorIdx).ParentIdx; } @@ -1715,9 +2118,27 @@ void DwarfLinker::keepDIEAndDenpendencies(const DWARFDebugInfoEntryMinimal &DIE, Val.extractValue(Data, &Offset, &Unit); CompileUnit *ReferencedCU; - if (const auto *RefDIE = resolveDIEReference(Val, Unit, DIE, ReferencedCU)) + if (const auto *RefDIE = + resolveDIEReference(Val, Unit, DIE, ReferencedCU)) { + uint32_t RefIdx = ReferencedCU->getOrigUnit().getDIEIndex(RefDIE); + CompileUnit::DIEInfo &Info = ReferencedCU->getInfo(RefIdx); + // If the referenced DIE has a DeclContext that has already been + // emitted, then do not keep the one in this CU. We'll link to + // the canonical DIE in cloneDieReferenceAttribute. + // FIXME: compatibility with dsymutil-classic. UseODR shouldn't + // be necessary and could be advantageously replaced by + // ReferencedCU->hasODR() && CU.hasODR(). + // FIXME: compatibility with dsymutil-classic. There is no + // reason not to unique ref_addr references. + if (AttrSpec.Form != dwarf::DW_FORM_ref_addr && UseODR && Info.Ctxt && + Info.Ctxt != ReferencedCU->getInfo(Info.ParentIdx).Ctxt && + Info.Ctxt->getCanonicalDIEOffset() && isODRAttribute(AttrSpec.Attr)) + continue; + + unsigned ODRFlag = UseODR ? TF_ODR : 0; lookForDIEsToKeep(*RefDIE, DMO, *ReferencedCU, - TF_Keep | TF_DependencyWalk); + TF_Keep | TF_DependencyWalk | ODRFlag); + } } } @@ -1752,9 +2173,10 @@ void DwarfLinker::lookForDIEsToKeep(const DWARFDebugInfoEntryMinimal &DIE, Flags = shouldKeepDIE(DIE, CU, MyInfo, Flags); // If it is a newly kept DIE mark it as well as all its dependencies as kept. - if (!AlreadyKept && (Flags & TF_Keep)) - keepDIEAndDenpendencies(DIE, MyInfo, DMO, CU, Flags); - + if (!AlreadyKept && (Flags & TF_Keep)) { + bool UseOdr = (Flags & TF_DependencyWalk) ? (Flags & TF_ODR) : CU.hasODR(); + keepDIEAndDenpendencies(DIE, MyInfo, DMO, CU, UseOdr); + } // The TF_ParentWalk flag tells us that we are currently walking up // the parent chain of a required DIE, and we don't want to mark all // the children of the parents as kept (consider for example a @@ -1823,24 +2245,34 @@ unsigned DwarfLinker::cloneDieReferenceAttribute( DIE &Die, const DWARFDebugInfoEntryMinimal &InputDIE, AttributeSpec AttrSpec, unsigned AttrSize, const DWARFFormValue &Val, CompileUnit &Unit) { - uint32_t Ref = *Val.getAsReference(&Unit.getOrigUnit()); + const DWARFUnit &U = Unit.getOrigUnit(); + uint32_t Ref = *Val.getAsReference(&U); DIE *NewRefDie = nullptr; CompileUnit *RefUnit = nullptr; - const DWARFDebugInfoEntryMinimal *RefDie = nullptr; - - if (!(RefUnit = getUnitForOffset(Ref)) || - !(RefDie = RefUnit->getOrigUnit().getDIEForOffset(Ref))) { - const char *AttributeString = dwarf::AttributeString(AttrSpec.Attr); - if (!AttributeString) - AttributeString = "DW_AT_???"; - reportWarning(Twine("Missing DIE for ref in attribute ") + AttributeString + - ". Dropping.", - &Unit.getOrigUnit(), &InputDIE); + DeclContext *Ctxt = nullptr; + + const DWARFDebugInfoEntryMinimal *RefDie = + resolveDIEReference(Val, U, InputDIE, RefUnit); + + // If the referenced DIE is not found, drop the attribute. + if (!RefDie) return 0; - } unsigned Idx = RefUnit->getOrigUnit().getDIEIndex(RefDie); CompileUnit::DIEInfo &RefInfo = RefUnit->getInfo(Idx); + + // If we already have emitted an equivalent DeclContext, just point + // at it. + if (isODRAttribute(AttrSpec.Attr)) { + Ctxt = RefInfo.Ctxt; + if (Ctxt && Ctxt->getCanonicalDIEOffset()) { + DIEInteger Attr(Ctxt->getCanonicalDIEOffset()); + Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), + dwarf::DW_FORM_ref_addr, Attr); + return getRefAddrSize(U); + } + } + if (!RefInfo.Clone) { assert(Ref > InputDIE.getOffset()); // We haven't cloned this DIE yet. Just create an empty one and @@ -1849,7 +2281,8 @@ unsigned DwarfLinker::cloneDieReferenceAttribute( } NewRefDie = RefInfo.Clone; - if (AttrSpec.Form == dwarf::DW_FORM_ref_addr) { + if (AttrSpec.Form == dwarf::DW_FORM_ref_addr || + (Unit.hasODR() && isODRAttribute(AttrSpec.Attr))) { // We cannot currently rely on a DIEEntry to emit ref_addr // references, because the implementation calls back to DwarfDebug // to find the unit offset. (We don't have a DwarfDebug) @@ -1867,11 +2300,11 @@ unsigned DwarfLinker::cloneDieReferenceAttribute( // A forward reference. Note and fixup later. Attr = 0xBADDEF; Unit.noteForwardReference( - NewRefDie, RefUnit, + NewRefDie, RefUnit, Ctxt, Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), dwarf::DW_FORM_ref_addr, DIEInteger(Attr))); } - return AttrSize; + return getRefAddrSize(U); } Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), @@ -2150,6 +2583,14 @@ DIE *DwarfLinker::cloneDIE(const DWARFDebugInfoEntryMinimal &InputDIE, Die = Info.Clone = DIE::get(DIEAlloc, dwarf::Tag(InputDIE.getTag())); assert(Die->getTag() == InputDIE.getTag()); Die->setOffset(OutOffset); + if (Unit.hasODR() && Die->getTag() != dwarf::DW_TAG_namespace && Info.Ctxt && + Info.Ctxt != Unit.getInfo(Info.ParentIdx).Ctxt && + !Info.Ctxt->getCanonicalDIEOffset()) { + // We are about to emit a DIE that is the root of its own valid + // DeclContext tree. Make the current offset the canonical offset + // for this context. + Info.Ctxt->setCanonicalDIEOffset(OutOffset + Unit.getStartOffset()); + } // Extract and clone every attribute. DataExtractor Data = U.getDebugInfoExtractor(); @@ -2611,8 +3052,9 @@ bool DwarfLinker::link(const DebugMap &Map) { outs() << "Input compilation unit:"; CUDie->dump(outs(), CU.get(), 0); } - Units.emplace_back(*CU, UnitID++); - gatherDIEParents(CUDie, 0, Units.back()); + Units.emplace_back(*CU, UnitID++, !Options.NoODR); + gatherDIEParents(CUDie, 0, Units.back(), &ODRContexts.getRoot(), + StringPool, ODRContexts); } // Then mark all the DIEs that need to be present in the linked diff --git a/llvm/tools/dsymutil/dsymutil.cpp b/llvm/tools/dsymutil/dsymutil.cpp index 5009193..f063fb5 100644 --- a/llvm/tools/dsymutil/dsymutil.cpp +++ b/llvm/tools/dsymutil/dsymutil.cpp @@ -47,6 +47,11 @@ static opt desc("Do the link in memory, but do not emit the result file."), init(false)); +static opt + NoODR("no-odr", + desc("Do not use ODR (One Definition Rule) for type uniquing."), + init(false)); + static opt DumpDebugMap( "dump-debug-map", desc("Parse and dump the debug map to standard output. Not DWARF link " @@ -71,6 +76,7 @@ int main(int argc, char **argv) { Options.Verbose = Verbose; Options.NoOutput = NoOutput; + Options.NoODR = NoODR; llvm::InitializeAllTargetInfos(); llvm::InitializeAllTargetMCs(); diff --git a/llvm/tools/dsymutil/dsymutil.h b/llvm/tools/dsymutil/dsymutil.h index 4089187..cd530a1 100644 --- a/llvm/tools/dsymutil/dsymutil.h +++ b/llvm/tools/dsymutil/dsymutil.h @@ -27,6 +27,7 @@ namespace dsymutil { struct LinkOptions { bool Verbose; ///< Verbosity bool NoOutput; ///< Skip emitting output + bool NoODR; ///< Do not unique types according to ODR LinkOptions() : Verbose(false), NoOutput(false) {} }; -- 2.7.4