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 --- /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 --- a/llvm/tools/llvm-dwarfdump/Statistics.cpp +++ b/llvm/tools/llvm-dwarfdump/Statistics.cpp @@ -157,6 +157,39 @@ 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 keep the same among different instances of the same entity. +/// Constructing the ID this way cannot garantee full uniqueness (in case of +/// entities with the same name and accidentally matching or missed +/// declaration info). +static std::string constructDieID(DWARFDie Die, + StringRef Prefix = StringRef()) { + std::string IDStr; + llvm::raw_string_ostream ID(IDStr); + ID << Prefix + << Die.getName(DINameKind::LinkageName) << ":"; + + 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); + } + if (File.empty()) + ID << "/"; + else + ID << File; + + ID << ":" + << dwarf::toUnsigned(Die.findRecursively(dwarf::DW_AT_decl_line), 0); + ID.str(); + return IDStr; +} /// Collect debug info quality metrics for one DIE. static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, @@ -270,10 +303,10 @@ 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,15 +391,13 @@ // 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)) @@ -375,10 +406,11 @@ 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) {