Index: llvm/test/tools/llvm-dwarfdump/X86/locstats-for-absctract-origin-vars.yaml =================================================================== --- llvm/test/tools/llvm-dwarfdump/X86/locstats-for-absctract-origin-vars.yaml +++ llvm/test/tools/llvm-dwarfdump/X86/locstats-for-absctract-origin-vars.yaml @@ -117,10 +117,12 @@ ## DW_TAG_variable <--(0x000000f8) ## DW_AT_decl_file (0x01) ## DW_AT_decl_line (1) +## DW_TAG_subprogram +## DW_AT_abstract_origin (0x000000f0) -# CHECK: "version": 7, -# CHECK: "#variables processed by location statistics": 15, -# CHECK: "#variables with 0% of parent scope covered by DW_AT_location": 11, +# CHECK: "version": 8, +# CHECK: "#variables processed by location statistics": 17, +# CHECK: "#variables with 0% of parent scope covered by DW_AT_location": 13, # CHECK: "#variables with 100% of parent scope covered by DW_AT_location": 4, --- !ELF @@ -263,6 +265,12 @@ Attributes: - Attribute: DW_AT_abstract_origin Form: DW_FORM_ref4 + - Code: 17 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_abstract_origin + Form: DW_FORM_ref4 debug_info: - Version: 4 AbbrOffset: 0x00 @@ -409,4 +417,7 @@ - Value: 1 ## DW_AT_decl_line - AbbrCode: 0 ## NULL - AbbrCode: 0 ## NULL + - AbbrCode: 17 ## DW_TAG_subprogram + Values: + - Value: 0xf0 ## DW_AT_abstract_origin - AbbrCode: 0 ## NULL Index: llvm/test/tools/llvm-dwarfdump/X86/statistics-dwo.test =================================================================== --- llvm/test/tools/llvm-dwarfdump/X86/statistics-dwo.test +++ llvm/test/tools/llvm-dwarfdump/X86/statistics-dwo.test @@ -69,7 +69,7 @@ # } # -CHECK: "version": 7, +CHECK: "version": 8, CHECK: "#functions": 3, CHECK: "#functions with location": 3, CHECK: "#inlined functions": 7, Index: llvm/test/tools/llvm-dwarfdump/X86/statistics-v3.test =================================================================== --- llvm/test/tools/llvm-dwarfdump/X86/statistics-v3.test +++ llvm/test/tools/llvm-dwarfdump/X86/statistics-v3.test @@ -64,7 +64,7 @@ # } # -CHECK: "version": 7, +CHECK: "version": 8, CHECK: "#functions": 3, CHECK: "#functions with location": 3, CHECK: "#inlined functions": 8, Index: llvm/test/tools/llvm-dwarfdump/X86/statistics.ll =================================================================== --- llvm/test/tools/llvm-dwarfdump/X86/statistics.ll +++ llvm/test/tools/llvm-dwarfdump/X86/statistics.ll @@ -1,6 +1,6 @@ ; RUN: llc -O0 %s -o - -filetype=obj \ ; RUN: | llvm-dwarfdump -statistics - | FileCheck %s -; CHECK: "version": 7, +; CHECK: "version": 8, ; namespace test { ; extern int a; Index: llvm/test/tools/llvm-dwarfdump/X86/stats-scope-bytes-covered.yaml =================================================================== --- llvm/test/tools/llvm-dwarfdump/X86/stats-scope-bytes-covered.yaml +++ llvm/test/tools/llvm-dwarfdump/X86/stats-scope-bytes-covered.yaml @@ -33,7 +33,7 @@ ## DW_AT_location (0x00000023: ## [0x0000000000000003, 0x0000000000000005): DW_OP_reg2 RCX) -# CHECK: "version": 7, +# CHECK: "version": 8, # CHECK: "sum_all_variables(#bytes in parent scope)": 12, # CHECK: "sum_all_variables(#bytes in any scope covered by DW_AT_location)": 8 # CHECK: "sum_all_variables(#bytes in parent scope covered by DW_AT_location)": 4 Index: llvm/tools/llvm-dwarfdump/Statistics.cpp =================================================================== --- llvm/tools/llvm-dwarfdump/Statistics.cpp +++ llvm/tools/llvm-dwarfdump/Statistics.cpp @@ -30,11 +30,12 @@ constexpr unsigned ZeroCoverageBucket = 0; /// This represents variables DIE offsets. -using InlinedVarsTy = llvm::SmallVector; +using AbstractOriginVarsTy = llvm::SmallVector; /// This maps function DIE offset to its variables. -using InlinedVarsTyMap = llvm::DenseMap; -/// This represents inlined_subroutine DIE offsets. -using InlinedFnInstacesTy = llvm::SmallVector; +using AbstractOriginVarsTyMap = + llvm::DenseMap; +/// This represents function DIE offsets containing an abstract_origin. +using AbstractOriginFnInstacesTy = llvm::SmallVector; /// Holds statistics for one function (or other entity that has a PC range and /// contains variables, such as a compile unit). @@ -220,13 +221,12 @@ } /// Collect debug info quality metrics for one DIE. -static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, - std::string VarPrefix, uint64_t BytesInScope, - uint32_t InlineDepth, - StringMap &FnStatMap, - GlobalStats &GlobalStats, - LocationStats &LocStats, - InlinedVarsTy *InlinedVariables) { +static void +collectStatsForDie(DWARFDie Die, std::string FnPrefix, std::string VarPrefix, + uint64_t BytesInScope, uint32_t InlineDepth, + StringMap &FnStatMap, + GlobalStats &GlobalStats, LocationStats &LocStats, + AbstractOriginVarsTy *AbstractOriginVariables) { const dwarf::Tag Tag = Die.getTag(); // Skip CU node. if (Tag == dwarf::DW_TAG_compile_unit) @@ -275,21 +275,19 @@ if (Die.findRecursively(dwarf::DW_AT_type)) HasType = true; - // Check if it is an inlined variable. if (Die.find(dwarf::DW_AT_abstract_origin)) { - if (Die.find(dwarf::DW_AT_location) || - Die.find(dwarf::DW_AT_const_value)) { - if (InlinedVariables) { - auto Offset = Die.find(dwarf::DW_AT_abstract_origin); - // Do not track this inlined var any more, since it has location - // coverage. - llvm::erase_value(*InlinedVariables, (*Offset).getRawUValue()); - } - } else { - // The locstats will be handled at the end of - // the collectStatsRecursive(). - DeferLocStats = true; + if (Die.find(dwarf::DW_AT_location) || Die.find(dwarf::DW_AT_const_value)) { + if (AbstractOriginVariables) { + auto Offset = Die.find(dwarf::DW_AT_abstract_origin); + // Do not track this variable any more, since it has location + // coverage. + llvm::erase_value(*AbstractOriginVariables, (*Offset).getRawUValue()); } + } else { + // The locstats will be handled at the end of + // the collectStatsRecursive(). + DeferLocStats = true; + } } auto IsEntryValue = [&](ArrayRef D) -> bool { @@ -425,33 +423,31 @@ } } -/// Recursively collect variables from subprogram with -/// DW_AT_inline attribute. -static void collectInlinedFnInfo(DWARFDie Die, - uint64_t SPOffset, - InlinedVarsTyMap &GlobalInlinedFnInfo) { +/// Recursively collect variables from subprogram with DW_AT_inline attribute. +static void +collectAbstractOriginFnInfo(DWARFDie Die, uint64_t SPOffset, + AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo) { DWARFDie Child = Die.getFirstChild(); while (Child) { const dwarf::Tag ChildTag = Child.getTag(); if (ChildTag == dwarf::DW_TAG_formal_parameter || ChildTag == dwarf::DW_TAG_variable) - GlobalInlinedFnInfo[SPOffset].push_back(Child.getOffset()); + GlobalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset()); else if (ChildTag == dwarf::DW_TAG_lexical_block) - collectInlinedFnInfo(Child, SPOffset, GlobalInlinedFnInfo); + collectAbstractOriginFnInfo(Child, SPOffset, GlobalAbstractOriginFnInfo); Child = Child.getSibling(); } } /// Recursively collect debug info quality metrics. -static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, - std::string VarPrefix, uint64_t BytesInScope, - uint32_t InlineDepth, - StringMap &FnStatMap, - GlobalStats &GlobalStats, - LocationStats &LocStats, - InlinedVarsTyMap &GlobalInlinedFnInfo, - InlinedFnInstacesTy &InlinedFnsToBeProcessed, - InlinedVarsTy *InlinedVarsPtr = nullptr) { +static void collectStatsRecursive( + DWARFDie Die, std::string FnPrefix, std::string VarPrefix, + uint64_t BytesInScope, uint32_t InlineDepth, + StringMap &FnStatMap, GlobalStats &GlobalStats, + LocationStats &LocStats, + AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo, + AbstractOriginFnInstacesTy &AbstractOriginFnsToBeProcessed, + AbstractOriginVarsTy *AbstractOriginVarsPtr = nullptr) { // Skip NULL nodes. if (Die.isNULL()) return; @@ -462,24 +458,32 @@ return; // Handle any kind of lexical scope. + const bool IsPotentialAbstractOrigin = + Die.find(dwarf::DW_AT_abstract_origin) != None; const bool IsFunction = Tag == dwarf::DW_TAG_subprogram; const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block; const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine; - InlinedVarsTy InlinedVars; + + // We want to know how many variables (with abstract_origin) don't have + // location info. + const bool IsCandidateForZeroLocCovTracking = + (IsInlinedFunction || (IsFunction && IsPotentialAbstractOrigin)); + + AbstractOriginVarsTy AbstractOriginVars; // Get the vars of the inlined fn, so the locstats // reports the missing vars (with coverage 0%). - if (IsInlinedFunction) { + if (IsCandidateForZeroLocCovTracking) { auto OffsetFn = Die.find(dwarf::DW_AT_abstract_origin); if (OffsetFn) { uint64_t OffsetOfInlineFnCopy = (*OffsetFn).getRawUValue(); - if (GlobalInlinedFnInfo.count(OffsetOfInlineFnCopy)) { - InlinedVars = GlobalInlinedFnInfo[OffsetOfInlineFnCopy]; - InlinedVarsPtr = &InlinedVars; + if (GlobalAbstractOriginFnInfo.count(OffsetOfInlineFnCopy)) { + AbstractOriginVars = GlobalAbstractOriginFnInfo[OffsetOfInlineFnCopy]; + AbstractOriginVarsPtr = &AbstractOriginVars; } else { // This means that the DW_AT_inline fn copy is out of order, - // so this inlined instance will be processed later. - InlinedFnsToBeProcessed.push_back(Die.getOffset()); - InlinedVarsPtr = nullptr; + // so this abstract origin instance will be processed later. + AbstractOriginFnsToBeProcessed.push_back(Die.getOffset()); + AbstractOriginVarsPtr = nullptr; } } } @@ -516,7 +520,7 @@ // for inlined instancies. if (Die.find(dwarf::DW_AT_inline)) { uint64_t SPOffset = Die.getOffset(); - collectInlinedFnInfo(Die, SPOffset, GlobalInlinedFnInfo); + collectAbstractOriginFnInfo(Die, SPOffset, GlobalAbstractOriginFnInfo); return; } @@ -548,7 +552,7 @@ } else { // Not a scope, visit the Die itself. It could be a variable. collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth, - FnStatMap, GlobalStats, LocStats, InlinedVarsPtr); + FnStatMap, GlobalStats, LocStats, AbstractOriginVarsPtr); } // Set InlineDepth correctly for child recursion @@ -568,25 +572,25 @@ if (Child.getTag() == dwarf::DW_TAG_formal_parameter) ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.'; - collectStatsRecursive(Child, FnPrefix, ChildVarPrefix, BytesInScope, - InlineDepth, FnStatMap, GlobalStats, LocStats, - GlobalInlinedFnInfo, InlinedFnsToBeProcessed, - InlinedVarsPtr); + collectStatsRecursive( + Child, FnPrefix, ChildVarPrefix, BytesInScope, InlineDepth, FnStatMap, + GlobalStats, LocStats, GlobalAbstractOriginFnInfo, + AbstractOriginFnsToBeProcessed, AbstractOriginVarsPtr); Child = Child.getSibling(); } - if (!IsInlinedFunction) + if (!IsCandidateForZeroLocCovTracking) return; - // After we have processed all vars of the inlined function, - // we want to know how many variables have no location. - for (auto Offset : InlinedVars) { + // After we have processed all vars of the inlined function (or function with + // an abstract_origin) we want to know how many variables have no location. + for (auto Offset : AbstractOriginVars) { LocStats.NumVarParam++; LocStats.VarParamLocStats[ZeroCoverageBucket]++; - auto InlineDie = Die.getDwarfUnit()->getDIEForOffset(Offset); - if (!InlineDie) + auto FnDie = Die.getDwarfUnit()->getDIEForOffset(Offset); + if (!FnDie) continue; - auto Tag = InlineDie.getTag(); + auto Tag = FnDie.getTag(); if (Tag == dwarf::DW_TAG_formal_parameter) { LocStats.NumParam++; LocStats.ParamLocStats[ZeroCoverageBucket]++; @@ -646,11 +650,12 @@ J.attribute((Twine("#bytes in ") + It.first).str(), int64_t(It.second)); } -/// Stop tracking inlined variables with a location. +/// Stop tracking variables that contain abstract_origin with a location. /// This is used for out-of-order DW_AT_inline subprograms only. -static void updateInlinedVarsCovInfo(DWARFDie InlinedFnDie, - InlinedVarsTy &InlinedVars) { - DWARFDie Child = InlinedFnDie.getFirstChild(); +static void +updateAbstractOriginVarsCovInfo(DWARFDie AbstractOriginFnDie, + AbstractOriginVarsTy &AbstractOriginVars) { + DWARFDie Child = AbstractOriginFnDie.getFirstChild(); while (Child) { const dwarf::Tag ChildTag = Child.getTag(); if ((ChildTag == dwarf::DW_TAG_formal_parameter || @@ -659,31 +664,34 @@ Child.find(dwarf::DW_AT_const_value))) { auto OffsetVar = Child.find(dwarf::DW_AT_abstract_origin); if (OffsetVar) - llvm::erase_value(InlinedVars, (*OffsetVar).getRawUValue()); + llvm::erase_value(AbstractOriginVars, (*OffsetVar).getRawUValue()); } else if (ChildTag == dwarf::DW_TAG_lexical_block) - updateInlinedVarsCovInfo(Child, InlinedVars); + updateAbstractOriginVarsCovInfo(Child, AbstractOriginVars); Child = Child.getSibling(); } } /// Collect zero location coverage for inlined variables which refer to /// a DW_AT_inline copy of subprogram that is out of order in the DWARF. -static void -collectZeroCovInlinedVars(DWARFUnit *DwUnit, GlobalStats &GlobalStats, - LocationStats &LocStats, - InlinedVarsTyMap &GlobalInlinedFnInfo, - InlinedFnInstacesTy &InlinedFnsToBeProcessed) { - for (auto FnOffset : InlinedFnsToBeProcessed) { - DWARFDie InlinedFnDie = DwUnit->getDIEForOffset(FnOffset); - auto InlinedCopy = InlinedFnDie.find(dwarf::DW_AT_abstract_origin); - InlinedVarsTy InlinedVars; - if (!InlinedCopy) +/// Also cover the variables of a concrete function (represented with +/// the DW_TAG_subprogram) with an abstract_origin attribute. +static void collectZeroCovAbstractOriginVars( + DWARFUnit *DwUnit, GlobalStats &GlobalStats, LocationStats &LocStats, + AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo, + AbstractOriginFnInstacesTy &AbstractOriginFnsToBeProcessed) { + for (auto FnOffset : AbstractOriginFnsToBeProcessed) { + DWARFDie AbstractOriginFnDie = DwUnit->getDIEForOffset(FnOffset); + auto ConcreteFnCopy = + AbstractOriginFnDie.find(dwarf::DW_AT_abstract_origin); + AbstractOriginVarsTy AbstractOriginVars; + if (!ConcreteFnCopy) continue; - InlinedVars = GlobalInlinedFnInfo[(*InlinedCopy).getRawUValue()]; - updateInlinedVarsCovInfo(InlinedFnDie, InlinedVars); + AbstractOriginVars = + GlobalAbstractOriginFnInfo[(*ConcreteFnCopy).getRawUValue()]; + updateAbstractOriginVarsCovInfo(AbstractOriginFnDie, AbstractOriginVars); - for (auto Offset : InlinedVars) { + for (auto Offset : AbstractOriginVars) { LocStats.NumVarParam++; LocStats.VarParamLocStats[ZeroCoverageBucket]++; auto Tag = DwUnit->getDIEForOffset(Offset).getTag(); @@ -720,19 +728,20 @@ // These variables are being reset for each CU, since there could be // a situation where we have two subprogram DIEs with the same offsets // in two diferent CUs, and we can end up using wrong variables info - // when trying to resolve abstract_orign attribute. + // when trying to resolve abstract_origin attribute. // TODO: Handle LTO cases where the abstract origin of // the function is in a different CU than the one it's // referenced from or inlined into. - InlinedVarsTyMap GlobalInlinedFnInfo; - InlinedFnInstacesTy InlinedFnsToBeProcessed; + AbstractOriginVarsTyMap GlobalAbstractOriginFnInfo; + AbstractOriginFnInstacesTy AbstractOriginFnsToBeProcessed; collectStatsRecursive(CUDie, "/", "g", 0, 0, Statistics, GlobalStats, - LocStats, GlobalInlinedFnInfo, - InlinedFnsToBeProcessed); + LocStats, GlobalAbstractOriginFnInfo, + AbstractOriginFnsToBeProcessed); - collectZeroCovInlinedVars(CUDie.getDwarfUnit(), GlobalStats, LocStats, - GlobalInlinedFnInfo, InlinedFnsToBeProcessed); + collectZeroCovAbstractOriginVars(CUDie.getDwarfUnit(), GlobalStats, + LocStats, GlobalAbstractOriginFnInfo, + AbstractOriginFnsToBeProcessed); } } @@ -743,7 +752,7 @@ /// The version number should be increased every time the algorithm is changed /// (including bug fixes). New metrics may be added without increasing the /// version. - unsigned Version = 7; + unsigned Version = 8; unsigned VarParamTotal = 0; unsigned VarParamUnique = 0; unsigned VarParamWithLoc = 0;