From 9806b39dae18982864dc3d9cdb2871d9590e5408 Mon Sep 17 00:00:00 2001 From: Kristina Bessonova Date: Tue, 14 Jan 2020 20:36:30 +0200 Subject: [PATCH] [llvm-dwarfdump][Statistics] Distinguish functions/variables with same name across different CUs Different variables and functions might have the same name in different CU. To calculate 'Availability' metric more accurate (i.e. to avoid getting availability above 100%), we need to have some additional logic to distinguish between them. The patch introduces a DIE identifier that consists of a function/variable name and declaration information: a filename and a line number. This allows distinguishing different functions/variables (different means declared in different files/lines) with the same name, keeping duplicates counted as duplicates. Reviewed by: aprantl, djtodoro Differential Revision: https://reviews.llvm.org/D72797 --- .../X86/stats-multiple-cu-same-name.ll | 89 ++++++++++++++++++++++ llvm/tools/llvm-dwarfdump/Statistics.cpp | 49 +++++++++--- 2 files changed, 127 insertions(+), 11 deletions(-) create mode 100644 llvm/test/tools/llvm-dwarfdump/X86/stats-multiple-cu-same-name.ll diff --git a/llvm/test/tools/llvm-dwarfdump/X86/stats-multiple-cu-same-name.ll b/llvm/test/tools/llvm-dwarfdump/X86/stats-multiple-cu-same-name.ll new file mode 100644 index 0000000..bfcc1b8 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfdump/X86/stats-multiple-cu-same-name.ll @@ -0,0 +1,89 @@ +; RUN: llc -O0 %s -o - -filetype=obj \ +; RUN: | llvm-dwarfdump -statistics - | FileCheck %s + +; Test that statistics distinguish functions with the same name. + +; CHECK: "source functions":4, +; CHECK-SAME: "unique source variables":2 +; CHECK-SAME: "source variables":2 + +; $ cat test1.cpp +; static int foo(int a) { +; return a; +; } +; int boo() { return foo(42); } +; +; $ cat test2.cpp +; static int foo(int a) { +; return a; +; } +; int bar() { return foo(42); } + +source_filename = "llvm-link" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; Function Attrs: noinline optnone uwtable +define dso_local i32 @_Z3boov() !dbg !9 { +entry: + %call = call i32 @_ZL3fooi(i32 42), !dbg !13 + ret i32 %call +} +; Function Attrs: noinline nounwind optnone uwtable +define internal i32 @_ZL3fooi(i32 %a) !dbg !15 { +entry: + %a.addr = alloca i32, align 4 + store i32 %a, i32* %a.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %a.addr, metadata !18, metadata !DIExpression()), !dbg !19 + %0 = load i32, i32* %a.addr, align 4, !dbg !20 + ret i32 %0 +} +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) +; Function Attrs: noinline optnone uwtable +define dso_local i32 @_Z3barv() !dbg !22 { +entry: + %call = call i32 @_ZL3fooi.1(i32 442), !dbg !23 + ret i32 %call +} +; Function Attrs: noinline nounwind optnone uwtable +define internal i32 @_ZL3fooi.1(i32 %a) !dbg !25 { +entry: + %a.addr = alloca i32, align 4 + store i32 %a, i32* %a.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %a.addr, metadata !26, metadata !DIExpression()), !dbg !27 + %0 = load i32, i32* %a.addr, align 4, !dbg !28 + %mul = mul nsw i32 %0, 2 + ret i32 %mul +} + +!llvm.dbg.cu = !{!0, !3} +!llvm.ident = !{!5, !5} +!llvm.module.flags = !{!6, !7, !8} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 10.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test1.cpp", directory: "/") +!2 = !{} +!3 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !4, producer: "clang version 10.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!4 = !DIFile(filename: "test2.cpp", directory: "/") +!5 = !{!"clang version 10.0.0"} +!6 = !{i32 7, !"Dwarf Version", i32 4} +!7 = !{i32 2, !"Debug Info Version", i32 3} +!8 = !{i32 1, !"wchar_size", i32 4} +!9 = distinct !DISubprogram(name: "boo", linkageName: "_Z3boov", scope: !1, file: !1, line: 5, type: !10, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!10 = !DISubroutineType(types: !11) +!11 = !{!12} +!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!13 = !DILocation(line: 6, column: 10, scope: !9) +!15 = distinct !DISubprogram(name: "foo", linkageName: "_ZL3fooi", scope: !1, file: !1, line: 1, type: !16, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !0, retainedNodes: !2) +!16 = !DISubroutineType(types: !17) +!17 = !{!12, !12} +!18 = !DILocalVariable(name: "a", arg: 1, scope: !15, file: !1, line: 1, type: !12) +!19 = !DILocation(line: 1, column: 20, scope: !15) +!20 = !DILocation(line: 2, column: 10, scope: !15) +!22 = distinct !DISubprogram(name: "bar", linkageName: "_Z3barv", scope: !4, file: !4, line: 5, type: !10, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !2) +!23 = !DILocation(line: 6, column: 10, scope: !22) +!25 = distinct !DISubprogram(name: "foo", linkageName: "_ZL3fooi", scope: !4, file: !4, line: 1, type: !16, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !3, retainedNodes: !2) +!26 = !DILocalVariable(name: "a", arg: 1, scope: !25, file: !4, line: 1, type: !12) +!27 = !DILocation(line: 1, column: 20, scope: !25) +!28 = !DILocation(line: 2, column: 10, scope: !25) diff --git a/llvm/tools/llvm-dwarfdump/Statistics.cpp b/llvm/tools/llvm-dwarfdump/Statistics.cpp index a183501..c77e9e4 100644 --- a/llvm/tools/llvm-dwarfdump/Statistics.cpp +++ b/llvm/tools/llvm-dwarfdump/Statistics.cpp @@ -157,6 +157,35 @@ static void collectLocStats(uint64_t BytesCovered, uint64_t BytesInScope, else if (IsLocalVar) VarLocStats[CoverageBucket]++; } +/// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName +/// and DeclLine. The identifier aims to be unique for any unique entities, +/// but keeping the same among different instances of the same entity. +static std::string constructDieID(DWARFDie Die, + StringRef Prefix = StringRef()) { + std::string IDStr; + llvm::raw_string_ostream ID(IDStr); + ID << Prefix + << Die.getName(DINameKind::LinkageName); + + // Prefix + Name is enough for local variables and parameters. + if (!Prefix.empty() && !Prefix.equals("g")) + return ID.str(); + + auto DeclFile = Die.findRecursively(dwarf::DW_AT_decl_file); + std::string File; + if (DeclFile) { + DWARFUnit *U = Die.getDwarfUnit(); + if (const auto *LT = U->getContext().getLineTableForUnit(U)) + if (LT->getFileNameByIndex( + dwarf::toUnsigned(DeclFile, 0), U->getCompilationDir(), + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File)) + File = sys::path::filename(File); + } + ID << ":" << (File.empty() ? "/" : File); + ID << ":" + << dwarf::toUnsigned(Die.findRecursively(dwarf::DW_AT_decl_line), 0); + return ID.str(); +} /// Collect debug info quality metrics for one DIE. static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, @@ -270,10 +299,10 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, if (DWARFDie D = Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin)) Die = D; - // 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); + + std::string VarID = constructDieID(Die, VarPrefix); + FnStats.VarsInFunction.insert(VarID); + if (BytesInScope) { FnStats.TotalVarWithLoc += (unsigned)HasLoc; // Turns out we have a lot of ranges that extend past the lexical scope. @@ -358,27 +387,25 @@ static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, // Count the function. if (!IsBlock) { - StringRef Name = Die.getName(DINameKind::LinkageName); - if (Name.empty()) - Name = Die.getName(DINameKind::ShortName); - FnPrefix = Name; // Skip over abstract origins. if (Die.find(dwarf::DW_AT_inline)) return; + std::string FnID = constructDieID(Die); // We've seen an (inlined) instance of this function. - auto &FnStats = FnStatMap[Name]; + auto &FnStats = FnStatMap[FnID]; + FnStats.IsFunction = true; if (IsInlinedFunction) { FnStats.NumFnInlined++; if (Die.findRecursively(dwarf::DW_AT_abstract_origin)) FnStats.NumAbstractOrigins++; } - FnStats.IsFunction = true; if (BytesInThisScope && !IsInlinedFunction) FnStats.HasPCAddresses = true; - std::string FnName = StringRef(Die.getName(DINameKind::ShortName)); if (Die.findRecursively(dwarf::DW_AT_decl_file) && Die.findRecursively(dwarf::DW_AT_decl_line)) FnStats.HasSourceLocation = true; + // Update function prefix. + FnPrefix = FnID; } if (BytesInThisScope) { -- 2.7.4