From 4f6ce420fd01368e07945601554163663b4ace08 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Fri, 21 Sep 2018 21:59:34 +0000 Subject: [PATCH] llvm-dwarfdump --statistics: Unique abstract origins across multiple CUs. Instead of indexing local variables by DIE offset, use the variable name + the path through the lexical block tree. This makes the lookup key consistent across duplicate abstract origins in different CUs. llvm-svn: 342776 --- .../X86/stats-inlining-multi-cu.ll | 193 ++++++++++++++++++ .../X86/stats-inlining-single-cu.ll | 185 +++++++++++++++++ tools/llvm-dwarfdump/Statistics.cpp | 69 ++++--- 3 files changed, 424 insertions(+), 23 deletions(-) create mode 100644 test/tools/llvm-dwarfdump/X86/stats-inlining-multi-cu.ll create mode 100644 test/tools/llvm-dwarfdump/X86/stats-inlining-single-cu.ll diff --git a/test/tools/llvm-dwarfdump/X86/stats-inlining-multi-cu.ll b/test/tools/llvm-dwarfdump/X86/stats-inlining-multi-cu.ll new file mode 100644 index 00000000000..f8656e6a7b7 --- /dev/null +++ b/test/tools/llvm-dwarfdump/X86/stats-inlining-multi-cu.ll @@ -0,0 +1,193 @@ +; RUN: llc -O0 %s -o - -filetype=obj \ +; RUN: | llvm-dwarfdump -statistics - | FileCheck %s + +; Test that abstract origins in multiple CUs are uniqued. + +; CHECK: "source functions":4, +; CHECK-SAME: "inlined functions":5, +; CHECK-SAME: "unique source variables":4 +; CHECK-SAME: "source variables":6 +; CHECK-SAME: "variables with location":6 + +;header.h: +;extern "C" int getchar(); +;template T __attribute__((always_inline)) inlined() { +; if (getchar()=='a') { +; int i = getchar(); +; return i; +; } else { +; int i = 'a'; +; return i; +; } +;} +;b.cpp: +;#include +;int b() { +; int b = inlined(); +; return b+1; +;} +;a.cpp +;#include +;int b(); +;int a() { +; int a = inlined(); +; return a+1; +;} +; +;int main() { +; return a() + b(); +;} + +; ModuleID = 'linked.ll' +source_filename = "llvm-link" +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.14.0" + +; Function Attrs: noinline optnone ssp uwtable +define i32 @_Z1av() #0 !dbg !10 { +entry: + %retval.i = alloca i32, align 4 + %i.i = alloca i32, align 4 + call void @llvm.dbg.declare(metadata i32* %i.i, metadata !14, metadata !DIExpression()), !dbg !21 + %i2.i = alloca i32, align 4 + call void @llvm.dbg.declare(metadata i32* %i2.i, metadata !23, metadata !DIExpression()), !dbg !25 + %a = alloca i32, align 4 + call void @llvm.dbg.declare(metadata i32* %a, metadata !26, metadata !DIExpression()), !dbg !27 + %call.i = call i32 @getchar(), !dbg !28 + %cmp.i = icmp eq i32 %call.i, 97, !dbg !28 + br i1 %cmp.i, label %if.then.i, label %if.else.i, !dbg !29 + +if.then.i: ; preds = %entry + %call1.i = call i32 @getchar(), !dbg !21 + store i32 %call1.i, i32* %i.i, align 4, !dbg !21 + %0 = load i32, i32* %i.i, align 4, !dbg !30 + store i32 %0, i32* %retval.i, align 4, !dbg !30 + br label %_Z7inlinedIiET_v.exit, !dbg !30 + +if.else.i: ; preds = %entry + store i32 97, i32* %i2.i, align 4, !dbg !25 + %1 = load i32, i32* %i2.i, align 4, !dbg !31 + store i32 %1, i32* %retval.i, align 4, !dbg !31 + br label %_Z7inlinedIiET_v.exit, !dbg !31 + +_Z7inlinedIiET_v.exit: ; preds = %if.else.i, %if.then.i + %2 = load i32, i32* %retval.i, align 4, !dbg !32 + store i32 %2, i32* %a, align 4, !dbg !27 + %3 = load i32, i32* %a, align 4, !dbg !33 + %add = add nsw i32 %3, 1, !dbg !33 + ret i32 %add, !dbg !33 +} + +; Function Attrs: nounwind readnone speculatable +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +declare i32 @getchar() + +; Function Attrs: noinline norecurse optnone ssp uwtable +define i32 @main() #3 !dbg !34 { +entry: + %retval = alloca i32, align 4 + store i32 0, i32* %retval, align 4 + %call = call i32 @_Z1av(), !dbg !35 + %call1 = call i32 @_Z1bv(), !dbg !35 + %add = add nsw i32 %call, %call1, !dbg !35 + ret i32 %add, !dbg !35 +} + +; Function Attrs: noinline optnone ssp uwtable +define i32 @_Z1bv() #0 !dbg !36 { +entry: + %retval.i = alloca i32, align 4 + %i.i = alloca i32, align 4 + call void @llvm.dbg.declare(metadata i32* %i.i, metadata !37, metadata !DIExpression()), !dbg !41 + %i2.i = alloca i32, align 4 + call void @llvm.dbg.declare(metadata i32* %i2.i, metadata !43, metadata !DIExpression()), !dbg !45 + %b = alloca i32, align 4 + call void @llvm.dbg.declare(metadata i32* %b, metadata !46, metadata !DIExpression()), !dbg !47 + %call.i = call i32 @getchar(), !dbg !48 + %cmp.i = icmp eq i32 %call.i, 97, !dbg !48 + br i1 %cmp.i, label %if.then.i, label %if.else.i, !dbg !49 + +if.then.i: ; preds = %entry + %call1.i = call i32 @getchar(), !dbg !41 + store i32 %call1.i, i32* %i.i, align 4, !dbg !41 + %0 = load i32, i32* %i.i, align 4, !dbg !50 + store i32 %0, i32* %retval.i, align 4, !dbg !50 + br label %_Z7inlinedIiET_v.exit, !dbg !50 + +if.else.i: ; preds = %entry + store i32 97, i32* %i2.i, align 4, !dbg !45 + %1 = load i32, i32* %i2.i, align 4, !dbg !51 + store i32 %1, i32* %retval.i, align 4, !dbg !51 + br label %_Z7inlinedIiET_v.exit, !dbg !51 + +_Z7inlinedIiET_v.exit: ; preds = %if.else.i, %if.then.i + %2 = load i32, i32* %retval.i, align 4, !dbg !52 + store i32 %2, i32* %b, align 4, !dbg !47 + %3 = load i32, i32* %b, align 4, !dbg !53 + %add = add nsw i32 %3, 1, !dbg !53 + ret i32 %add, !dbg !53 +} + +attributes #0 = { noinline optnone ssp uwtable } +attributes #1 = { nounwind readnone speculatable } +attributes #3 = { noinline norecurse optnone ssp uwtable } + +!llvm.dbg.cu = !{!0, !3} +!llvm.ident = !{!5, !5} +!llvm.module.flags = !{!6, !7, !8, !9} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 8.0.0 (trunk 340541) (llvm/trunk 340540)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) +!1 = !DIFile(filename: "a.cpp", directory: "/tmp") +!2 = !{} +!3 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !4, producer: "clang version 8.0.0 (trunk 340541) (llvm/trunk 340540)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) +!4 = !DIFile(filename: "b.cpp", directory: "/tmp") +!5 = !{!"clang version 8.0.0 (trunk 340541) (llvm/trunk 340540)"} +!6 = !{i32 2, !"Dwarf Version", i32 4} +!7 = !{i32 2, !"Debug Info Version", i32 3} +!8 = !{i32 1, !"wchar_size", i32 4} +!9 = !{i32 7, !"PIC Level", i32 2} +!10 = distinct !DISubprogram(name: "a", linkageName: "_Z1av", scope: !1, file: !1, line: 3, type: !11, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: false, unit: !0, retainedNodes: !2) +!11 = !DISubroutineType(types: !12) +!12 = !{!13} +!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!14 = !DILocalVariable(name: "i", scope: !15, file: !16, line: 4, type: !13) +!15 = distinct !DILexicalBlock(scope: !17, file: !16, line: 3) +!16 = !DIFile(filename: "./header.h", directory: "/tmp") +!17 = distinct !DILexicalBlock(scope: !18, file: !16, line: 3) +!18 = distinct !DISubprogram(name: "inlined", linkageName: "_Z7inlinedIiET_v", scope: !16, file: !16, line: 2, type: !11, isLocal: false, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: false, unit: !0, templateParams: !19, retainedNodes: !2) +!19 = !{!20} +!20 = !DITemplateTypeParameter(name: "T", type: !13) +!21 = !DILocation(line: 4, scope: !15, inlinedAt: !22) +!22 = distinct !DILocation(line: 4, scope: !10) +!23 = !DILocalVariable(name: "i", scope: !24, file: !16, line: 7, type: !13) +!24 = distinct !DILexicalBlock(scope: !17, file: !16, line: 6) +!25 = !DILocation(line: 7, scope: !24, inlinedAt: !22) +!26 = !DILocalVariable(name: "a", scope: !10, file: !1, line: 4, type: !13) +!27 = !DILocation(line: 4, scope: !10) +!28 = !DILocation(line: 3, scope: !17, inlinedAt: !22) +!29 = !DILocation(line: 3, scope: !18, inlinedAt: !22) +!30 = !DILocation(line: 5, scope: !15, inlinedAt: !22) +!31 = !DILocation(line: 8, scope: !24, inlinedAt: !22) +!32 = !DILocation(line: 10, scope: !18, inlinedAt: !22) +!33 = !DILocation(line: 5, scope: !10) +!34 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 12, type: !11, isLocal: false, isDefinition: true, scopeLine: 12, flags: DIFlagPrototyped, isOptimized: false, unit: !0, retainedNodes: !2) +!35 = !DILocation(line: 13, scope: !34) +!36 = distinct !DISubprogram(name: "b", linkageName: "_Z1bv", scope: !4, file: !4, line: 2, type: !11, isLocal: false, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: false, unit: !3, retainedNodes: !2) +!37 = !DILocalVariable(name: "i", scope: !38, file: !16, line: 4, type: !13) +!38 = distinct !DILexicalBlock(scope: !39, file: !16, line: 3) +!39 = distinct !DILexicalBlock(scope: !40, file: !16, line: 3) +!40 = distinct !DISubprogram(name: "inlined", linkageName: "_Z7inlinedIiET_v", scope: !16, file: !16, line: 2, type: !11, isLocal: false, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: false, unit: !3, templateParams: !19, retainedNodes: !2) +!41 = !DILocation(line: 4, scope: !38, inlinedAt: !42) +!42 = distinct !DILocation(line: 3, scope: !36) +!43 = !DILocalVariable(name: "i", scope: !44, file: !16, line: 7, type: !13) +!44 = distinct !DILexicalBlock(scope: !39, file: !16, line: 6) +!45 = !DILocation(line: 7, scope: !44, inlinedAt: !42) +!46 = !DILocalVariable(name: "b", scope: !36, file: !4, line: 3, type: !13) +!47 = !DILocation(line: 3, scope: !36) +!48 = !DILocation(line: 3, scope: !39, inlinedAt: !42) +!49 = !DILocation(line: 3, scope: !40, inlinedAt: !42) +!50 = !DILocation(line: 5, scope: !38, inlinedAt: !42) +!51 = !DILocation(line: 8, scope: !44, inlinedAt: !42) +!52 = !DILocation(line: 10, scope: !40, inlinedAt: !42) +!53 = !DILocation(line: 4, scope: !36) diff --git a/test/tools/llvm-dwarfdump/X86/stats-inlining-single-cu.ll b/test/tools/llvm-dwarfdump/X86/stats-inlining-single-cu.ll new file mode 100644 index 00000000000..14ef7096132 --- /dev/null +++ b/test/tools/llvm-dwarfdump/X86/stats-inlining-single-cu.ll @@ -0,0 +1,185 @@ +; RUN: llc -O0 %s -o - -filetype=obj \ +; RUN: | llvm-dwarfdump -statistics - | FileCheck %s + +; This test serves as a baseline / sanity-check for stats-inlining-multi-cu.ll +; The results for both tests should be identical. + +; CHECK: "source functions":4, +; CHECK-SAME: "inlined functions":5, +; CHECK-SAME: "unique source variables":4 +; CHECK-SAME: "source variables":6 +; CHECK-SAME: "variables with location":6 + +;header.h: +;extern "C" int getchar(); +;template T __attribute__((always_inline)) inlined() { +; if (getchar()=='a') { +; int i = getchar(); +; return i; +; } else { +; int i = 'a'; +; return i; +; } +;} +;ab.cpp +;#include +;int b(); +;int a() { +; int a = inlined(); +; return a+1; +;} +; +;int b() { +; int b = inlined(); +; return b+1; +;} +;int main() { +; return a() + b(); +;} + + +; ModuleID = 'a.cpp' +source_filename = "a.cpp" +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.14.0" + +; Function Attrs: noinline optnone ssp uwtable +define i32 @_Z1av() #0 !dbg !8 { +entry: + %retval.i = alloca i32, align 4 + %i.i = alloca i32, align 4 + call void @llvm.dbg.declare(metadata i32* %i.i, metadata !12, metadata !DIExpression()), !dbg !19 + %i2.i = alloca i32, align 4 + call void @llvm.dbg.declare(metadata i32* %i2.i, metadata !21, metadata !DIExpression()), !dbg !23 + %a = alloca i32, align 4 + call void @llvm.dbg.declare(metadata i32* %a, metadata !24, metadata !DIExpression()), !dbg !25 + %call.i = call i32 @getchar(), !dbg !26 + %cmp.i = icmp eq i32 %call.i, 97, !dbg !26 + br i1 %cmp.i, label %if.then.i, label %if.else.i, !dbg !27 + +if.then.i: ; preds = %entry + %call1.i = call i32 @getchar(), !dbg !19 + store i32 %call1.i, i32* %i.i, align 4, !dbg !19 + %0 = load i32, i32* %i.i, align 4, !dbg !28 + store i32 %0, i32* %retval.i, align 4, !dbg !28 + br label %_Z7inlinedIiET_v.exit, !dbg !28 + +if.else.i: ; preds = %entry + store i32 97, i32* %i2.i, align 4, !dbg !23 + %1 = load i32, i32* %i2.i, align 4, !dbg !29 + store i32 %1, i32* %retval.i, align 4, !dbg !29 + br label %_Z7inlinedIiET_v.exit, !dbg !29 + +_Z7inlinedIiET_v.exit: ; preds = %if.then.i, %if.else.i + %2 = load i32, i32* %retval.i, align 4, !dbg !30 + store i32 %2, i32* %a, align 4, !dbg !25 + %3 = load i32, i32* %a, align 4, !dbg !31 + %add = add nsw i32 %3, 1, !dbg !31 + ret i32 %add, !dbg !31 +} + +; Function Attrs: nounwind readnone speculatable +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +; Function Attrs: noinline optnone ssp uwtable +define i32 @_Z1bv() #0 !dbg !32 { +entry: + %retval.i = alloca i32, align 4 + %i.i = alloca i32, align 4 + call void @llvm.dbg.declare(metadata i32* %i.i, metadata !12, metadata !DIExpression()), !dbg !33 + %i2.i = alloca i32, align 4 + call void @llvm.dbg.declare(metadata i32* %i2.i, metadata !21, metadata !DIExpression()), !dbg !35 + %b = alloca i32, align 4 + call void @llvm.dbg.declare(metadata i32* %b, metadata !36, metadata !DIExpression()), !dbg !37 + %call.i = call i32 @getchar(), !dbg !38 + %cmp.i = icmp eq i32 %call.i, 97, !dbg !38 + br i1 %cmp.i, label %if.then.i, label %if.else.i, !dbg !39 + +if.then.i: ; preds = %entry + %call1.i = call i32 @getchar(), !dbg !33 + store i32 %call1.i, i32* %i.i, align 4, !dbg !33 + %0 = load i32, i32* %i.i, align 4, !dbg !40 + store i32 %0, i32* %retval.i, align 4, !dbg !40 + br label %_Z7inlinedIiET_v.exit, !dbg !40 + +if.else.i: ; preds = %entry + store i32 97, i32* %i2.i, align 4, !dbg !35 + %1 = load i32, i32* %i2.i, align 4, !dbg !41 + store i32 %1, i32* %retval.i, align 4, !dbg !41 + br label %_Z7inlinedIiET_v.exit, !dbg !41 + +_Z7inlinedIiET_v.exit: ; preds = %if.then.i, %if.else.i + %2 = load i32, i32* %retval.i, align 4, !dbg !42 + store i32 %2, i32* %b, align 4, !dbg !37 + %3 = load i32, i32* %b, align 4, !dbg !43 + %add = add nsw i32 %3, 1, !dbg !43 + ret i32 %add, !dbg !43 +} + +; Function Attrs: noinline norecurse optnone ssp uwtable +define i32 @main() #2 !dbg !44 { +entry: + %retval = alloca i32, align 4 + store i32 0, i32* %retval, align 4 + %call = call i32 @_Z1av(), !dbg !45 + %call1 = call i32 @_Z1bv(), !dbg !45 + %add = add nsw i32 %call, %call1, !dbg !45 + ret i32 %add, !dbg !45 +} + +declare i32 @getchar() + +attributes #0 = { noinline optnone ssp uwtable } +attributes #1 = { nounwind readnone speculatable } +attributes #2 = { noinline norecurse optnone ssp uwtable } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5, !6} +!llvm.ident = !{!7} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 8.0.0 (trunk 340541) (llvm/trunk 340540)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) +!1 = !DIFile(filename: "a.cpp", directory: "/tmp") +!2 = !{} +!3 = !{i32 2, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{i32 7, !"PIC Level", i32 2} +!7 = !{!"clang version 8.0.0 (trunk 340541) (llvm/trunk 340540)"} +!8 = distinct !DISubprogram(name: "a", linkageName: "_Z1av", scope: !1, file: !1, line: 3, type: !9, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: false, unit: !0, retainedNodes: !2) +!9 = !DISubroutineType(types: !10) +!10 = !{!11} +!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!12 = !DILocalVariable(name: "i", scope: !13, file: !14, line: 4, type: !11) +!13 = distinct !DILexicalBlock(scope: !15, file: !14, line: 3) +!14 = !DIFile(filename: "./header.h", directory: "/tmp") +!15 = distinct !DILexicalBlock(scope: !16, file: !14, line: 3) +!16 = distinct !DISubprogram(name: "inlined", linkageName: "_Z7inlinedIiET_v", scope: !14, file: !14, line: 2, type: !9, isLocal: false, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: false, unit: !0, templateParams: !17, retainedNodes: !2) +!17 = !{!18} +!18 = !DITemplateTypeParameter(name: "T", type: !11) +!19 = !DILocation(line: 4, scope: !13, inlinedAt: !20) +!20 = distinct !DILocation(line: 4, scope: !8) +!21 = !DILocalVariable(name: "i", scope: !22, file: !14, line: 7, type: !11) +!22 = distinct !DILexicalBlock(scope: !15, file: !14, line: 6) +!23 = !DILocation(line: 7, scope: !22, inlinedAt: !20) +!24 = !DILocalVariable(name: "a", scope: !8, file: !1, line: 4, type: !11) +!25 = !DILocation(line: 4, scope: !8) +!26 = !DILocation(line: 3, scope: !15, inlinedAt: !20) +!27 = !DILocation(line: 3, scope: !16, inlinedAt: !20) +!28 = !DILocation(line: 5, scope: !13, inlinedAt: !20) +!29 = !DILocation(line: 8, scope: !22, inlinedAt: !20) +!30 = !DILocation(line: 10, scope: !16, inlinedAt: !20) +!31 = !DILocation(line: 5, scope: !8) +!32 = distinct !DISubprogram(name: "b", linkageName: "_Z1bv", scope: !1, file: !1, line: 8, type: !9, isLocal: false, isDefinition: true, scopeLine: 8, flags: DIFlagPrototyped, isOptimized: false, unit: !0, retainedNodes: !2) +!33 = !DILocation(line: 4, scope: !13, inlinedAt: !34) +!34 = distinct !DILocation(line: 9, scope: !32) +!35 = !DILocation(line: 7, scope: !22, inlinedAt: !34) +!36 = !DILocalVariable(name: "b", scope: !32, file: !1, line: 9, type: !11) +!37 = !DILocation(line: 9, scope: !32) +!38 = !DILocation(line: 3, scope: !15, inlinedAt: !34) +!39 = !DILocation(line: 3, scope: !16, inlinedAt: !34) +!40 = !DILocation(line: 5, scope: !13, inlinedAt: !34) +!41 = !DILocation(line: 8, scope: !22, inlinedAt: !34) +!42 = !DILocation(line: 10, scope: !16, inlinedAt: !34) +!43 = !DILocation(line: 10, scope: !32) +!44 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 12, type: !9, isLocal: false, isDefinition: true, scopeLine: 12, flags: DIFlagPrototyped, isOptimized: false, unit: !0, retainedNodes: !2) +!45 = !DILocation(line: 13, scope: !44) diff --git a/tools/llvm-dwarfdump/Statistics.cpp b/tools/llvm-dwarfdump/Statistics.cpp index 5af853d4ef2..a2b4de7cfed 100644 --- a/tools/llvm-dwarfdump/Statistics.cpp +++ b/tools/llvm-dwarfdump/Statistics.cpp @@ -1,4 +1,6 @@ #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringSet.h" #include "llvm/DebugInfo/DIContext.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" @@ -18,7 +20,7 @@ struct PerFunctionStats { /// Number of constants with location across all inlined instances. unsigned ConstantMembers = 0; /// List of all Variables in this function. - SmallDenseSet VarsInFunction; + StringSet<> VarsInFunction; /// Compile units also cover a PC range, but have this flag set to false. bool IsFunction = false; }; @@ -46,19 +48,31 @@ static uint64_t getLowPC(DWARFDie Die) { } /// Collect debug info quality metrics for one DIE. -static void collectStatsForDie(DWARFDie Die, std::string Prefix, - uint64_t ScopeLowPC, uint64_t BytesInScope, +static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, + std::string VarPrefix, uint64_t ScopeLowPC, + uint64_t BytesInScope, StringMap &FnStatMap, GlobalStats &GlobalStats) { bool HasLoc = false; uint64_t BytesCovered = 0; uint64_t OffsetToFirstDefinition = 0; + + if (Die.getTag() != dwarf::DW_TAG_formal_parameter && + Die.getTag() != dwarf::DW_TAG_variable && + Die.getTag() != dwarf::DW_TAG_member) { + // Not a variable or constant member. + return; + } + if (Die.find(dwarf::DW_AT_const_value)) { // This catches constant members *and* variables. HasLoc = true; BytesCovered = BytesInScope; - } else if (Die.getTag() == dwarf::DW_TAG_variable || - Die.getTag() == dwarf::DW_TAG_formal_parameter) { + } else { + if (Die.getTag() == dwarf::DW_TAG_member) { + // Non-const member. + return; + } // Handle variables and function arguments. auto FormValue = Die.find(dwarf::DW_AT_location); HasLoc = FormValue.hasValue(); @@ -86,19 +100,17 @@ static void collectStatsForDie(DWARFDie Die, std::string Prefix, BytesCovered = BytesInScope; } } - } else { - // Not a variable or constant member. - return; } // Collect PC range coverage data. - auto &FnStats = FnStatMap[Prefix]; + auto &FnStats = FnStatMap[FnPrefix]; if (DWARFDie D = Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin)) Die = D; - // This is a unique ID for the variable inside the current object file. - unsigned CanonicalDieOffset = Die.getOffset(); - FnStats.VarsInFunction.insert(CanonicalDieOffset); + // By using the variable name + the path through the lexical block tree, the + // keys are consistent across duplicate abstract origins in different CUs. + std::string VarName = StringRef(Die.getName(DINameKind::ShortName)); + FnStats.VarsInFunction.insert(VarPrefix+VarName); if (BytesInScope) { FnStats.TotalVarWithLoc += (unsigned)HasLoc; // Adjust for the fact the variables often start their lifetime in the @@ -115,14 +127,21 @@ static void collectStatsForDie(DWARFDie Die, std::string Prefix, } /// Recursively collect debug info quality metrics. -static void collectStatsRecursive(DWARFDie Die, std::string Prefix, - uint64_t ScopeLowPC, uint64_t BytesInScope, +static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, + std::string VarPrefix, uint64_t ScopeLowPC, + uint64_t BytesInScope, StringMap &FnStatMap, GlobalStats &GlobalStats) { // Handle any kind of lexical scope. if (Die.getTag() == dwarf::DW_TAG_subprogram || Die.getTag() == dwarf::DW_TAG_inlined_subroutine || Die.getTag() == dwarf::DW_TAG_lexical_block) { + + // Reset VarPrefix when entering a new function. + if (Die.getTag() == dwarf::DW_TAG_subprogram || + Die.getTag() == dwarf::DW_TAG_inlined_subroutine) + VarPrefix = "v"; + // Ignore forward declarations. if (Die.find(dwarf::DW_AT_declaration)) return; @@ -132,7 +151,7 @@ static void collectStatsRecursive(DWARFDie Die, std::string Prefix, StringRef Name = Die.getName(DINameKind::LinkageName); if (Name.empty()) Name = Die.getName(DINameKind::ShortName); - Prefix = Name; + FnPrefix = Name; // Skip over abstract origins. if (Die.find(dwarf::DW_AT_inline)) return; @@ -159,15 +178,20 @@ static void collectStatsRecursive(DWARFDie Die, std::string Prefix, BytesInScope = BytesInThisScope; } else { // Not a scope, visit the Die itself. It could be a variable. - collectStatsForDie(Die, Prefix, ScopeLowPC, BytesInScope, FnStatMap, - GlobalStats); + collectStatsForDie(Die, FnPrefix, VarPrefix, ScopeLowPC, BytesInScope, + FnStatMap, GlobalStats); } // Traverse children. + unsigned LexicalBlockIndex = 0; DWARFDie Child = Die.getFirstChild(); while (Child) { - collectStatsRecursive(Child, Prefix, ScopeLowPC, BytesInScope, FnStatMap, - GlobalStats); + std::string ChildVarPrefix = VarPrefix; + if (Child.getTag() == dwarf::DW_TAG_lexical_block) + ChildVarPrefix += toHex(LexicalBlockIndex++) + '.'; + + collectStatsRecursive(Child, FnPrefix, ChildVarPrefix, ScopeLowPC, + BytesInScope, FnStatMap, GlobalStats); Child = Child.getSibling(); } } @@ -200,7 +224,7 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, StringMap Statistics; for (const auto &CU : static_cast(&DICtx)->compile_units()) if (DWARFDie CUDie = CU->getUnitDIE(false)) - collectStatsRecursive(CUDie, "/", 0, 0, Statistics, GlobalStats); + collectStatsRecursive(CUDie, "/", "g", 0, 0, Statistics, GlobalStats); /// The version number should be increased every time the algorithm is changed /// (including bug fixes). New metrics may be added without increasing the @@ -218,9 +242,8 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, VarWithLoc += Stats.TotalVarWithLoc + Constants; VarTotal += TotalVars + Constants; VarUnique += Stats.VarsInFunction.size(); - LLVM_DEBUG(for (auto V - : Stats.VarsInFunction) llvm::dbgs() - << Entry.getKey() << ": " << V << "\n"); + LLVM_DEBUG(for (auto &V : Stats.VarsInFunction) llvm::dbgs() + << Entry.getKey() << ": " << V.getKey() << "\n"); NumFunctions += Stats.IsFunction; NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined; }