diff --git a/llvm/include/llvm/CodeGen/DbgEntityHistoryCalculator.h b/llvm/include/llvm/CodeGen/DbgEntityHistoryCalculator.h --- a/llvm/include/llvm/CodeGen/DbgEntityHistoryCalculator.h +++ b/llvm/include/llvm/CodeGen/DbgEntityHistoryCalculator.h @@ -24,6 +24,24 @@ class MachineInstr; class TargetRegisterInfo; +/// Record instruction ordering so we can query their relative positions within +/// a function. Meta instructions are given the same ordinal as the preceding +/// non-meta instruction. Class state is invalid if MF is modified after +/// calling initialize. +class InstructionOrdering { +public: + void initialize(const MachineFunction &MF); + void clear() { InstNumberMap.clear(); } + + /// Check if instruction \p A comes before \p B, where \p A and \p B both + /// belong to the MachineFunction passed to initialize(). + bool isBefore(const MachineInstr *A, const MachineInstr *B) const; + +private: + /// Each instruction is assigned an order number. + DenseMap InstNumberMap; +}; + /// For each user variable, keep a list of instruction ranges where this /// variable is accessible. The variables are listed in order of appearance. class DbgValueHistoryMap { @@ -93,7 +111,8 @@ } /// Drop location ranges which exist entirely outside each variable's scope. - void trimLocationRanges(const MachineFunction &MF, LexicalScopes &LScopes); + void trimLocationRanges(const MachineFunction &MF, LexicalScopes &LScopes, + const InstructionOrdering &Ordering); bool empty() const { return VarEntries.empty(); } void clear() { VarEntries.clear(); } EntriesMap::const_iterator begin() const { return VarEntries.begin(); } diff --git a/llvm/include/llvm/CodeGen/DebugHandlerBase.h b/llvm/include/llvm/CodeGen/DebugHandlerBase.h --- a/llvm/include/llvm/CodeGen/DebugHandlerBase.h +++ b/llvm/include/llvm/CodeGen/DebugHandlerBase.h @@ -110,6 +110,9 @@ virtual void endFunctionImpl(const MachineFunction *MF) = 0; virtual void skippedNonDebugFunction() {} +private: + InstructionOrdering InstOrdering; + // AsmPrinterHandler overrides. public: void beginInstruction(const MachineInstr *MI) override; @@ -129,8 +132,10 @@ /// If this type is derived from a base type then return base type size. static uint64_t getBaseTypeSize(const DIType *Ty); + + const InstructionOrdering &getInstOrdering() const { return InstOrdering; } }; -} +} // namespace llvm #endif diff --git a/llvm/lib/CodeGen/AsmPrinter/DbgEntityHistoryCalculator.cpp b/llvm/lib/CodeGen/AsmPrinter/DbgEntityHistoryCalculator.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DbgEntityHistoryCalculator.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DbgEntityHistoryCalculator.cpp @@ -53,24 +53,6 @@ : Register(); } -/// Record instruction ordering so we can query their relative positions within -/// a function. Meta instructions are given the same ordinal as the preceding -/// non-meta instruction. Class state is invalid if MF is modified after -/// calling initialize. -class InstructionOrdering { -public: - void initialize(const MachineFunction &MF); - void clear() { InstNumberMap.clear(); } - - /// Check if instruction \p A comes before \p B, where \p A and \p B both - /// belong to the MachineFunction passed to initialize(). - bool isBefore(const MachineInstr *A, const MachineInstr *B) const; - -private: - /// Each instruction is assigned an order number. - DenseMap InstNumberMap; -}; - void InstructionOrdering::initialize(const MachineFunction &MF) { // We give meta instructions the same ordinal as the preceding instruction // because this class is written for the task of comparing positions of @@ -161,11 +143,9 @@ return None; } -void DbgValueHistoryMap::trimLocationRanges(const MachineFunction &MF, - LexicalScopes &LScopes) { - InstructionOrdering Ordering; - Ordering.initialize(MF); - +void DbgValueHistoryMap::trimLocationRanges( + const MachineFunction &MF, LexicalScopes &LScopes, + const InstructionOrdering &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 diff --git a/llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp b/llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp @@ -196,8 +196,9 @@ assert(DbgLabels.empty() && "DbgLabels map wasn't cleaned!"); calculateDbgEntityHistory(MF, Asm->MF->getSubtarget().getRegisterInfo(), DbgValues, DbgLabels); + InstOrdering.initialize(*MF); if (TrimVarLocs) - DbgValues.trimLocationRanges(*MF, LScopes); + DbgValues.trimLocationRanges(*MF, LScopes, InstOrdering); LLVM_DEBUG(DbgValues.dump()); // Request labels for the full history. @@ -333,6 +334,7 @@ DbgLabels.clear(); LabelsBeforeInsn.clear(); LabelsAfterInsn.clear(); + InstOrdering.clear(); } void DebugHandlerBase::beginBasicBlock(const MachineBasicBlock &MBB) { diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -1518,7 +1518,8 @@ /// either open or otherwise rolls off the end of the scope. static bool validThroughout(LexicalScopes &LScopes, const MachineInstr *DbgValue, - const MachineInstr *RangeEnd) { + const MachineInstr *RangeEnd, + const InstructionOrdering &Ordering) { assert(DbgValue->getDebugLoc() && "DBG_VALUE without a debug location"); auto MBB = DbgValue->getParent(); auto DL = DbgValue->getDebugLoc(); @@ -1586,9 +1587,8 @@ // The location range and variable's enclosing scope are both contained within // MBB, test if location terminates before end of scope. - for (auto I = RangeEnd->getIterator(); I != MBB->end(); ++I) - if (&*I == LScopeEnd) - return false; + if (Ordering.isBefore(RangeEnd, LScopeEnd)) + return false; // There's a single location which starts at the scope start, and ends at or // after the scope end. @@ -1734,7 +1734,7 @@ return false; if (VeryLargeBlocks.count(StartDebugMI->getParent())) return false; - return validThroughout(LScopes, StartDebugMI, EndMI); + return validThroughout(LScopes, StartDebugMI, EndMI, getInstOrdering()); } DbgEntity *DwarfDebug::createConcreteEntity(DwarfCompileUnit &TheCU, @@ -1810,7 +1810,7 @@ const auto *End = SingleValueWithClobber ? HistoryMapEntries[1].getInstr() : nullptr; if (VeryLargeBlocks.count(MInsn->getParent()) == 0 && - validThroughout(LScopes, MInsn, End)) { + validThroughout(LScopes, MInsn, End, getInstOrdering())) { RegVar->initializeDbgValue(MInsn); continue; } diff --git a/llvm/test/DebugInfo/MIR/AArch64/implicit-def-dead-scope.mir b/llvm/test/DebugInfo/MIR/AArch64/implicit-def-dead-scope.mir --- a/llvm/test/DebugInfo/MIR/AArch64/implicit-def-dead-scope.mir +++ b/llvm/test/DebugInfo/MIR/AArch64/implicit-def-dead-scope.mir @@ -5,9 +5,8 @@ # encountering an IMPLICIT_DEF in its own lexical scope. # CHECK: .debug_info contents: -# CHECK: DW_TAG_formal_parameter -# CHECK: DW_AT_location [DW_FORM_sec_offset] -# CHECK-NEXT: DW_OP_lit0, DW_OP_stack_value +# CHECK: DW_TAG_formal_parameter [14] +# CHECK-NEXT: DW_AT_const_value [DW_FORM_udata] (0) # CHECK-NEXT: DW_AT_abstract_origin {{.*}} "name" --- | ; ModuleID = 't.ll' diff --git a/llvm/test/DebugInfo/MIR/X86/callsite-stack-value.mir b/llvm/test/DebugInfo/MIR/X86/callsite-stack-value.mir --- a/llvm/test/DebugInfo/MIR/X86/callsite-stack-value.mir +++ b/llvm/test/DebugInfo/MIR/X86/callsite-stack-value.mir @@ -2,8 +2,7 @@ # RUN: -emit-call-site-info | llvm-dwarfdump - | FileCheck %s -implicit-check-not=call_site_parameter # CHECK: DW_TAG_formal_parameter -# CHECK-NEXT: DW_AT_location -# CHECK-NEXT: DW_OP_reg17 +# CHECK-NEXT: DW_AT_location (DW_OP_reg17 XMM0) # struct S { # float w; diff --git a/llvm/test/DebugInfo/X86/single-location-2.mir b/llvm/test/DebugInfo/X86/single-location-2.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/single-location-2.mir @@ -0,0 +1,92 @@ +# RUN: llc %s --start-after=livedebugvalues -filetype=obj -o - \ +# RUN: | llvm-dwarfdump - -name local* -regex \ +# RUN: | FileCheck %s +# +## This tests certain single location detection functionality. The Test MIR +## is hand written. 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, splitDebugInlining: false, nameTableKind: None) + !1 = !DIFile(filename: "example.c", directory: "/") + !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) + !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) + !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) + !27 = !DILocalVariable(name: "tmp", scope: !14, file: !1, line: 2, 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 tmp; } // scope fun:block !14, var localb !27, debug-location !18 + ;; { int localc; } // scope fun:block !24, var localc !25, debug-location !26 + ;; } + ;; + ;; (1) Check that frame-setup instructions are not counted against + ;; locations being valid throughout the function call. + ; + ; CHECK: DW_TAG_variable + ; CHECK-NEXT: DW_AT_location (DW_OP_reg5 RDI) + ; CHECK-NEXT: DW_AT_name ("locala") + $rbp = frame-setup MOV64rr $rsp + DBG_VALUE $edi, $noreg, !12, !DIExpression(), debug-location !15 + $eax = MOV32ri 0, debug-location !15 + + ;; (2) The scope block ends with a meta instruction. A location range ends + ;; with the final non-meta instruction in the scope. Check that + ;; location is considered valid throughout. + ; + ; CHECK: DW_TAG_variable + ; CHECK-NEXT: DW_AT_location (DW_OP_reg2 RCX) + ; CHECK-NEXT: DW_AT_name ("localb") + ; + ;; start scope, start location range + DBG_VALUE $ecx, $noreg, !13, !DIExpression(), debug-location !18 + ;; end location range + $ecx = MOV32ri 1, debug-location !18 + ;; end scope + DBG_VALUE $noreg, $noreg, !27, !DIExpression(), debug-location !18 + + ;; (3) The final instruction in the scope closes a location range. Check + ;; that location is considered valid throughout. + ; + ; CHECK: DW_TAG_variable + ; CHECK-NEXT: DW_AT_location (DW_OP_reg4 RSI) + ; CHECK-NEXT: DW_AT_name ("localc") + ; + ;; start scope, start location range + DBG_VALUE $esi, $noreg, !25, !DIExpression(), debug-location !26 + ;; end scope, end location range + $esi = MOV32ri 2, debug-location !26 + + RETQ debug-location !15 +... diff --git a/llvm/test/DebugInfo/X86/trim-var-locs.mir b/llvm/test/DebugInfo/X86/trim-var-locs.mir --- a/llvm/test/DebugInfo/X86/trim-var-locs.mir +++ b/llvm/test/DebugInfo/X86/trim-var-locs.mir @@ -81,8 +81,7 @@ ; block is not trimmed. ; ; CHECK: DW_TAG_variable - ; CHECK-NEXT: DW_AT_location - ; CHECK-NEXT: DW_OP_reg5 RDI + ; CHECK-NEXT: DW_AT_location (DW_OP_reg5 RDI) ; CHECK-NEXT: DW_AT_name ("localb") ; ; localb range 2 clobber in scope fun !7 (outside block !14)