Index: llvm/include/llvm/CodeGen/DbgEntityHistoryCalculator.h =================================================================== --- llvm/include/llvm/CodeGen/DbgEntityHistoryCalculator.h +++ llvm/include/llvm/CodeGen/DbgEntityHistoryCalculator.h @@ -12,6 +12,7 @@ #include "llvm/ADT/MapVector.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/LexicalScopes.h" #include namespace llvm { @@ -52,6 +53,8 @@ /// register-described debug values that have their end index /// set to this entry's position in the entry vector. class Entry { + friend DbgValueHistoryMap; + public: enum EntryKind { DbgValue, Clobber }; @@ -89,6 +92,8 @@ return Entries[Index]; } + /// Drop location ranges which exist entirely outside each variable's scope. + void trimLocationRanges(const MachineFunction &MF, LexicalScopes &LScopes); bool empty() const { return VarEntries.empty(); } void clear() { VarEntries.clear(); } EntriesMap::const_iterator begin() const { return VarEntries.begin(); } Index: llvm/lib/CodeGen/AsmPrinter/DbgEntityHistoryCalculator.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DbgEntityHistoryCalculator.cpp +++ llvm/lib/CodeGen/AsmPrinter/DbgEntityHistoryCalculator.cpp @@ -8,9 +8,11 @@ #include "llvm/CodeGen/DbgEntityHistoryCalculator.h" #include "llvm/ADT/BitVector.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/LexicalScopes.h" #include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineInstr.h" @@ -89,6 +91,192 @@ EndIndex = Index; } +using OrderMap = DenseMap; +/// Number instructions so that we can compare instruction positions within MF. +/// Meta instructions are given the same nubmer as the preceding instruction. +/// Because the block ordering will not change it is possible (and safe) to +/// compare instruction positions between blocks. +static void numberInstructions(const MachineFunction &MF, OrderMap &Ordering) { + // We give meta instructions the same number as the peceding instruction + // because this function is written for the task of comparing positions of + // variable location ranges against scope ranges. To reflect what we'll see + // in the binary, when we look at location ranges we must consider all + // DBG_VALUEs between two real instructions at the same position. And a + // scope range which ends on a meta instruction should be considered to end + // at the last seen real instruction. E.g. + // + // 1 instruction p Both the variable location for x and for y start + // 1 DBG_VALUE for "x" after instruction p so we give them all the same + // 1 DBG_VALUE for "y" number. If a scope range ends at DBG_VALUE for "y", + // 2 instruction q we should treat it as ending after instruction p + // because it will be the last real instruction in the + // range. DBG_VALUEs at or after this position for + // variables declared in the scope will have no effect. + unsigned position = 0; + for (const MachineBasicBlock &MBB : MF) + for (const MachineInstr &MI : MBB) + Ordering[&MI] = MI.isMetaInstruction() ? position : ++position; +} + +/// Check if instruction A comes before B. Meta instructions have the same +/// position as the preceding non-meta instruction. See numberInstructions for +/// more info. +static bool isBefore(const MachineInstr *A, const MachineInstr *B, + const OrderMap &Ordering) { + return Ordering.lookup(A) < Ordering.lookup(B); +} + +/// Check if the instruction range [StartMI, EndMI] intersects any instruction +/// range in Ranges. EndMI can be nullptr to indicate that the range is +/// unbounded. Assumes Ranges is ordered and disjoint. Returns true and points +/// to the first intersecting scope range if one exists. +static Optional::iterator> +intersects(const MachineInstr *StartMI, const MachineInstr *EndMI, + const ArrayRef &Ranges, const OrderMap &Ordering) { + for (auto RangesI = Ranges.begin(), RangesE = Ranges.end(); + RangesI != RangesE; ++RangesI) { + if (EndMI && isBefore(EndMI, RangesI->first, Ordering)) + return None; + if (EndMI && !isBefore(RangesI->second, EndMI, Ordering)) + return RangesI; + if (isBefore(StartMI, RangesI->second, Ordering)) + return RangesI; + } + return None; +} + +void DbgValueHistoryMap::trimLocationRanges(const MachineFunction &MF, + LexicalScopes &LScopes) { + OrderMap Ordering; + numberInstructions(MF, Ordering); + + // The indices of the entries we're going to remove for each variable. + SmallVector ToRemove; + // Entry reference count for each variable. Clobbers left with no references + // will be removed. + SmallVector ReferenceCount; + // Entries reference other entries by index. Offsets is used to remap these + // references if any entries are removed. + SmallVector Offsets; + + for (auto &Record : VarEntries) { + auto &HistoryMapEntries = Record.second; + if (HistoryMapEntries.empty()) + continue; + + InlinedEntity Entity = Record.first; + const DILocalVariable *LocalVar = cast(Entity.first); + + LexicalScope *Scope = nullptr; + if (const DILocation *InlinedAt = Entity.second) { + Scope = LScopes.findInlinedScope(LocalVar->getScope(), InlinedAt); + } else { + Scope = LScopes.findLexicalScope(LocalVar->getScope()); + // Ignore variables for non-inlined function level scopes. The scope + // ranges (from scope->getRanges()) will not include any instructions + // before the first one with a debug-location, which could cause us to + // incorrectly drop a location. We could introduce special casing for + // these variables, but it doesn't seem worth it because no out-of-scope + // locations have been observed for variables declared in function level + // scopes. + if (Scope && + (Scope->getScopeNode() == Scope->getScopeNode()->getSubprogram()) && + (Scope->getScopeNode() == LocalVar->getScope())) + continue; + } + + // If there is no scope for the variable then something has probably gone + // wrong. + if (!Scope) + continue; + + ToRemove.clear(); + // Zero the reference counts. + ReferenceCount.assign(HistoryMapEntries.size(), 0); + // Index of the DBG_VALUE which marks the start of the current location + // range. + EntryIndex StartIndex = 0; + ArrayRef ScopeRanges(Scope->getRanges()); + for (auto EI = HistoryMapEntries.begin(), EE = HistoryMapEntries.end(); + EI != EE; ++EI, ++StartIndex) { + // Only DBG_VALUEs can open location ranges so skip anything else. + if (!EI->isDbgValue()) + continue; + + // Index of the entry which closes this range. + EntryIndex EndIndex = EI->getEndIndex(); + // If this range is closed bump the reference count of the closing entry. + if (EndIndex != NoEntry) + ReferenceCount[EndIndex] += 1; + // Skip this location range if the opening entry is still referenced. It + // may close a location range which intersects a scope range. + // TODO: We could be 'smarter' and trim these kinds of ranges such that + // they do not leak out of the scope ranges if they partially overlap. + if (ReferenceCount[StartIndex] > 0) + continue; + + const MachineInstr *StartMI = EI->getInstr(); + const MachineInstr *EndMI = EndIndex != NoEntry + ? HistoryMapEntries[EndIndex].getInstr() + : nullptr; + // Check if the location range [StartMI, EndMI] intersects with any scope + // range for the variable. + if (auto R = intersects(StartMI, EndMI, ScopeRanges, Ordering)) { + // Adjust ScopeRanges to exclude ranges which subsequent location ranges + // cannot possibly intersect. + ScopeRanges = ArrayRef(R.getValue(), ScopeRanges.end()); + } else { + // If the location range does not intersect any scope range then the + // DBG_VALUE which opened this location range is usless, mark it for + // removal. + ToRemove.push_back(StartIndex); + // Because we'll be removing this entry we need to update the reference + // count of the closing entry, if one exists. + if (EndIndex != NoEntry) + ReferenceCount[EndIndex] -= 1; + } + } + + // If there is nothing to remove then jump to next variable. + if (ToRemove.empty()) + continue; + + // Mark clobbers that will no longer close any location ranges for removal. + for (size_t i = 0; i < HistoryMapEntries.size(); ++i) + if (ReferenceCount[i] <= 0 && HistoryMapEntries[i].isClobber()) + ToRemove.push_back(i); + + std::sort(ToRemove.begin(), ToRemove.end()); + + // Build an offset map so we can update the EndIndex of the remaining + // entries. + // Zero the offsets. + Offsets.assign(HistoryMapEntries.size(), 0); + size_t CurOffset = 0; + auto ToRemoveItr = ToRemove.begin(); + for (size_t EntryIdx = *ToRemoveItr; EntryIdx < HistoryMapEntries.size(); + ++EntryIdx) { + // Check if this is an entry which will be removed. + if (ToRemoveItr != ToRemove.end() && *ToRemoveItr == EntryIdx) { + ++ToRemoveItr; + ++CurOffset; + } + Offsets[EntryIdx] = CurOffset; + } + + // Update the EndIndex of the entries to account for those which will be + // removed. + for (auto &Entry : HistoryMapEntries) + if (Entry.isClosed()) + Entry.EndIndex -= Offsets[Entry.EndIndex]; + + // Now actually remove the entries. Iterate backwards so that our remaining + // ToRemove indices are valid after each erase. + for (auto Itr = ToRemove.rbegin(), End = ToRemove.rend(); Itr != End; ++Itr) + HistoryMapEntries.erase(HistoryMapEntries.begin() + *Itr); + } +} + void DbgLabelInstrMap::addInstr(InlinedEntity Label, const MachineInstr &MI) { assert(MI.isDebugLabel() && "not a DBG_LABEL"); LabelInstr[Label] = &MI; Index: llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp +++ llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp @@ -21,11 +21,16 @@ #include "llvm/CodeGen/TargetSubtargetInfo.h" #include "llvm/IR/DebugInfo.h" #include "llvm/MC/MCStreamer.h" +#include "llvm/Support/CommandLine.h" using namespace llvm; #define DEBUG_TYPE "dwarfdebug" +/// If true, we drop variable location ranges which exist entirely outside the +/// variable's lexical scope instruction ranges. +static cl::opt TrimVarLocs("trim-var-locs", cl::Hidden, cl::init(true)); + Optional DbgVariableLocation::extractFromMachineInstruction( const MachineInstr &Instruction) { @@ -191,6 +196,8 @@ assert(DbgLabels.empty() && "DbgLabels map wasn't cleaned!"); calculateDbgEntityHistory(MF, Asm->MF->getSubtarget().getRegisterInfo(), DbgValues, DbgLabels); + if (TrimVarLocs) + DbgValues.trimLocationRanges(*MF, LScopes); LLVM_DEBUG(DbgValues.dump()); // Request labels for the full history. Index: llvm/test/DebugInfo/ARM/PR26163.ll =================================================================== --- llvm/test/DebugInfo/ARM/PR26163.ll +++ llvm/test/DebugInfo/ARM/PR26163.ll @@ -8,10 +8,14 @@ ; but it is what is currently being emitted. Any change here needs to be ; intentional, so the test is very specific. ; -; CHECK: DW_TAG_inlined_subroutine -; CHECK: DW_TAG_variable -; CHECK: DW_AT_location ({{.*}} -; CHECK-NEXT: [0x00000004, 0x00000014): DW_OP_lit0, DW_OP_stack_value, DW_OP_piece 0x4) +; The variable is given a single location instead of a location list entry +; because the function validThroughout has a special code path for single +; locations with a constant value that start in the prologue. +; +; CHECK: DW_TAG_inlined_subroutine +; CHECK: DW_TAG_variable +; CHECK-NEXT: DW_AT_location (DW_OP_lit0, DW_OP_stack_value, DW_OP_piece 0x4) +; CHECK-NEXT DW_AT_name ("i4") ; Created form the following test case (PR26163) with ; clang -cc1 -triple armv4t--freebsd11.0-gnueabi -emit-obj -debug-info-kind=standalone -O2 -x c test.c Index: llvm/test/DebugInfo/COFF/register-variables.ll =================================================================== --- llvm/test/DebugInfo/COFF/register-variables.ll +++ llvm/test/DebugInfo/COFF/register-variables.ll @@ -60,13 +60,11 @@ ; ASM: .cv_def_range [[p_ecx_esi]] [[func_end]], reg, 23 ; ASM: .short 4414 # Record kind: S_LOCAL ; ASM: .asciz "c" -; ASM: .cv_def_range [[after_if]] [[func_finished]], reg, 17 ; ASM: .short 4414 # Record kind: S_LOCAL ; ASM: .asciz "a" ; ASM: .cv_def_range [[after_je]] [[after_inc_eax]], reg, 17 ; ASM: .short 4414 # Record kind: S_LOCAL ; ASM: .asciz "b" -; ASM: .cv_def_range [[after_if]] [[after_if]], reg, 17 ; Note: "b" is a victim of tail de-duplication / branch folding. @@ -109,18 +107,11 @@ ; OBJ: } ; OBJ: LocalSym { ; OBJ: Type: int (0x74) -; OBJ: Flags [ (0x0) +; OBJ: Flags [ (0x100) +; OBJ: IsOptimizedOut (0x100) ; OBJ: ] ; OBJ: VarName: c ; OBJ: } -; OBJ: DefRangeRegisterSym { -; OBJ: Register: EAX (0x11) -; OBJ: LocalVariableAddrRange { -; OBJ: OffsetStart: .text+0x1A -; OBJ: ISectStart: 0x0 -; OBJ: Range: 0xC -; OBJ: } -; OBJ: } ; OBJ: LocalSym { ; OBJ: Type: int (0x74) ; OBJ: Flags [ (0x0) Index: llvm/test/DebugInfo/X86/live-debug-variables.ll =================================================================== --- llvm/test/DebugInfo/X86/live-debug-variables.ll +++ llvm/test/DebugInfo/X86/live-debug-variables.ll @@ -1,4 +1,5 @@ -; RUN: llc -mtriple=x86_64-linux-gnu -filetype=obj -o - %s | llvm-dwarfdump -debug-loc - | FileCheck %s +; RUN: llc -mtriple=x86_64-linux-gnu -filetype=obj -o - %s | llvm-dwarfdump -name i4 - \ +; RUN: | FileCheck %s ; The test inlines the function F four times, with each inlined variable for ; "i4" sharing the same virtual register. This means the live interval of the @@ -22,12 +23,13 @@ ; F(a,b,c,d,e); ; } -; CHECK: .debug_loc contents: -; CHECK-NEXT: 0x00000000: -; We currently emit an entry for the function prologue, too, which could be optimized away. -; CHECK: (0x0000000000000018, 0x0000000000000072): DW_OP_reg3 RBX -; We should only have one entry inside the function. -; CHECK-NOT: : +; Ignore the abstract entry. +; CHECK: DW_TAG_formal_parameter +; Check concrete entry has a single location. +; CHECK: DW_TAG_formal_parameter +; CHECK-NEXT: DW_AT_location (DW_OP_reg3 RBX) +; CHECK-NEXT: DW_AT_abstract_origin +; CHECK-NOT: DW_TAG_formal_parameter declare i32 @foobar(i32, i32, i32, i32, i32) Index: llvm/test/DebugInfo/X86/trim-var-locs.mir =================================================================== --- /dev/null +++ llvm/test/DebugInfo/X86/trim-var-locs.mir @@ -0,0 +1,121 @@ +# RUN: llc %s --start-after=livedebugvalues -filetype=obj -o - \ +# RUN: | llvm-dwarfdump - -name local* -regex \ +# RUN: | FileCheck %s +# +# Test that the -trim-var-locs option (enabled by default) works correctly. +# Test directives and comments inline. + +--- | + target triple = "x86_64-unknown-linux-gnu" + define dso_local i32 @fun() local_unnamed_addr !dbg !7 { + entry: + ret i32 0 + } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4, !5} + !llvm.ident = !{!6} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) + !1 = !DIFile(filename: "example.c", directory: "/") + !2 = !{} + !3 = !{i32 7, !"Dwarf Version", i32 4} + !4 = !{i32 2, !"Debug Info Version", i32 3} + !5 = !{i32 1, !"wchar_size", i32 4} + !6 = !{!"clang version 11.0.0"} + !8 = !DISubroutineType(types: !9) + !9 = !{!10} + !10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !11 = !{!12, !13, !25} + !22 = !DISubroutineType(types: !23) + !23 = !{!10, !10} + ; --- Important metadata --- + !7 = distinct !DISubprogram(name: "fun", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) + !24 = distinct !DILexicalBlock(scope: !7, file: !1, line: 9, column: 3) + !14 = distinct !DILexicalBlock(scope: !7, file: !1, line: 4, column: 3) + !12 = !DILocalVariable(name: "locala", scope: !7, file: !1, line: 1, type: !10) + !13 = !DILocalVariable(name: "localb", scope: !14, file: !1, line: 2, type: !10) + !25 = !DILocalVariable(name: "localc", scope: !24, file: !1, line: 3, type: !10) + !15 = !DILocation(line: 1, column: 0, scope: !7) + !18 = !DILocation(line: 2, column: 1, scope: !14) + !26 = !DILocation(line: 3, column: 1, scope: !24) +... +--- +name: fun +body: | + bb.0.entry: + ; This is the scope and variable structure: + ; int fun() { // scope fun !7 + ; int locala; // scope fun !7, var locala !12, debug-location !15 + ; { int localb; } // scope fun:block !14, var localb !13, debug-location !18 + ; { int localc; } // scope fun:block !24, var localc !25, debug-location !26 + ; } + ; + ; (1) Check that a variable location range found in implied scope fun !7 is + ; not trimmed. + ; + ; CHECK: DW_TAG_variable + ; CHECK-NEXT: DW_AT_location + ; CHECK-NEXT: DW_OP_reg0 RAX + ; CHECK-NEXT: DW_AT_name ("locala") + ; + ; scope fun !7 is implied as we're in function fun and haven't seen a debug-location + $eax = MOV32ri 0 + ; locala range 1 start in implicit scope fun !7 + DBG_VALUE $eax, $noreg, !12, !DIExpression(), debug-location !15 + $edi = MOV32ri 1 + ; locala range 1 clobber in implicit scope fun !7 + $eax = MOV32ri 2 + ; scope fun !7 explicit start + $edi = MOV32ri 3, debug-location !15 + + ; (2) Check that a variable location range found outside lexical block is + ; trimmed. See check directives for (3). + ; + ; localb range 1 start in scope fun !7 (outside block !14). + DBG_VALUE $eax, $noreg, !13, !DIExpression(), debug-location !18 + ; localb range 1 clobber in scope fun !7 + $edi = MOV32ri 4, debug-location !15 + + ; (3) Check that a variable location range which overlaps the entire lexical + ; block is not trimmed. + ; + ; CHECK: DW_TAG_variable + ; CHECK-NEXT: DW_AT_location + ; CHECK-NEXT: DW_OP_reg5 RDI + ; CHECK-NEXT: DW_AT_name ("localb") + ; + ; localb range 2 clobber in scope fun !7 (outside block !14) + DBG_VALUE $edi, $noreg, !13, !DIExpression(), debug-location !18 + ; scope block !14 start (and only instruction) + $edi = MOV32ri 5, debug-location !18 + + ; (4) Check that a variable location range in scope fun !7 (outside block + ; !14) is trimmed. See check directives for (3). + ; + ; localb range 3 starts after scope !14 (prev instr is last in scope) + DBG_VALUE $rax, $noreg, !13, !DIExpression(), debug-location !18 + ; scope block !14 end + $edi = MOV32ri 6, debug-location !15 + + ; (5) Check that a variable location range found between disjoint scope + ; ranges is trimmed. + ; + ; CHECK: DW_TAG_variable + ; CHECK-NOT: DW_AT_location + ; CHECK-NEXT: DW_AT_name ("localc") + ; + ; scope fun !7 + $edi = MOV32ri 6, debug-location !15 + ; scope block !24 start and end range 1 + $edi = MOV32ri 7, debug-location !26 + ; localc range 1 start in scope !7 + DBG_VALUE $edi, $noreg, !25, !DIExpression(), debug-location !18 + ; localc range 1 clobber in scope !7 + $edi = MOV32ri 8, debug-location !15 + ; scoope block !24 start and end range 2 + $edi = MOV32ri 9, debug-location !26 + + ; scope fun !7 + RETQ debug-location !15 +...