diff --git a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp --- a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp +++ b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp @@ -1829,9 +1829,13 @@ // recorded in the value substitution table. Apply any substitutions to // the instruction / operand number in this DBG_INSTR_REF. auto Sub = MF.DebugValueSubstitutions.find(std::make_pair(InstNo, OpNo)); + // Collect any subregister extractions performed during optimization. + SmallVector SeenSubregs; while (Sub != MF.DebugValueSubstitutions.end()) { - InstNo = Sub->second.Dest.first; - OpNo = Sub->second.Dest.second; + std::tie(InstNo, OpNo) = Sub->second.Dest; + unsigned Subreg = Sub->second.Subreg; + if (Subreg) + SeenSubregs.push_back(Subreg); Sub = MF.DebugValueSubstitutions.find(std::make_pair(InstNo, OpNo)); } @@ -1865,6 +1869,77 @@ MI, InstNo); } + // Apply any subregister extractions, in reverse. We might have seen code + // like this: + // CALL64 @foo, implicit-def $rax + // %0:gr64 = COPY $rax + // %1:gr32 = COPY %0.sub_32bit + // %2:gr16 = COPY %1.sub_16bit + // %3:gr8 = COPY %2.sub_8bit + // In which case each copy would have been recorded as a substitution with + // a subregister qualifier. Apply those qualifiers now. + if (NewID && !SeenSubregs.empty()) { + unsigned Offset = 0; + unsigned Size = 0; + + // Look at each subregister that we passed through, and progressively + // narrow in, accumulating any offsets that occur. Substitutions should + // only ever be the same or narrower width than what they read from; + // iterate in reverse order so that we go from wide to small. + for (unsigned Subreg : reverse(SeenSubregs)) { + unsigned ThisSize = TRI->getSubRegIdxSize(Subreg); + unsigned ThisOffset = TRI->getSubRegIdxOffset(Subreg); + Offset += ThisOffset; + Size = (Size == 0) ? ThisSize : std::min(Size, ThisSize); + } + + // If that worked, look for an appropriate subregister with the register + // where the define happens. Don't look at values that were defined during + // a stack write: we can't currently express register locations within + // spills. + LocIdx L = NewID->getLoc(); + if (NewID && !MTracker->isSpill(L)) { + // Find the register class for the register where this def happened. + // FIXME: no index for this? + Register Reg = MTracker->LocIdxToLocID[L]; + const TargetRegisterClass *TRC = nullptr; + for (auto *TRCI : TRI->regclasses()) + if (TRCI->contains(Reg)) + TRC = TRCI; + assert(TRC && "Couldn't find target register class?"); + + // If the register we have isn't the right size or in the right place, + // Try to find a subregister inside it. + unsigned MainRegSize = TRI->getRegSizeInBits(*TRC); + if (Size != MainRegSize || Offset) { + // Enumerate all subregisters, searching. + Register NewReg = 0; + for (MCSubRegIterator SRI(Reg, TRI, false); SRI.isValid(); ++SRI) { + unsigned Subreg = TRI->getSubRegIndex(Reg, *SRI); + unsigned SubregSize = TRI->getSubRegIdxSize(Subreg); + unsigned SubregOffset = TRI->getSubRegIdxOffset(Subreg); + if (SubregSize == Size && SubregOffset == Offset) { + NewReg = *SRI; + break; + } + } + + // If we didn't find anything: there's no way to express our value. + if (!NewReg) { + NewID = None; + } else { + // Re-state the value as being defined within the subregister + // that we found. + LocIdx NewLoc = MTracker->lookupOrTrackRegister(NewReg); + NewID = ValueIDNum(NewID->getBlock(), NewID->getInst(), NewLoc); + } + } + } else { + // If we can't handle subregisters, unset the new value. + NewID = None; + } + } + // We, we have a value number or None. Tell the variable value tracker about // it. The rest of this LiveDebugValues implementation acts exactly the same // for DBG_INSTR_REFs as DBG_VALUEs (just, the former can refer to values that diff --git a/llvm/test/DebugInfo/MIR/InstrRef/livedebugvalues_subreg_substitutions.mir b/llvm/test/DebugInfo/MIR/InstrRef/livedebugvalues_subreg_substitutions.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/MIR/InstrRef/livedebugvalues_subreg_substitutions.mir @@ -0,0 +1,106 @@ +# RUN: llc %s -march=x86-64 -run-pass=livedebugvalues -experimental-debug-variable-locations -o - 2>&1 | FileCheck %s +# +# Test that when we have a subregister qualifiers in substitutions, that +# InstrRefBasedLDV correctly applies them to the variable location. Below, a +# call defines all of $rax, but the variable locations should only apply to +# the low order 8 bits. +--- | + define i8 @test(i32 %bar) local_unnamed_addr !dbg !7 { + entry: + ret i8 0, !dbg !12 + } + + declare dso_local void @ext(i64) + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4, !5, !6} + !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug) + !1 = !DIFile(filename: "foo.cpp", directory: ".") + !2 = !DIBasicType(name: "int", size: 8, encoding: DW_ATE_signed) + !3 = !{i32 2, !"Dwarf Version", i32 4} + !4 = !{i32 2, !"Debug Info Version", i32 3} + !5 = !{i32 1, !"wchar_size", i32 2} + !6 = !{i32 7, !"PIC Level", i32 2} + !7 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: !1, file: !1, line: 6, type: !8, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !10) + !8 = !DISubroutineType(types: !9) + !9 = !{!2, !2} + !10 = !{!11} + !11 = !DILocalVariable(name: "baz", scope: !7, file: !1, line: 7, type: !2) + !12 = !DILocation(line: 10, scope: !7) +... +--- +name: test +tracksRegLiveness: true +liveins: + - { reg: '$rdi', virtual-reg: '' } +debugValueSubstitutions: + - { srcinst: 1, srcop: 0, dstinst: 2, dstop: 0, subreg: 1 } # sub_8bit + - { srcinst: 2, srcop: 0, dstinst: 3, dstop: 0, subreg: 4 } # sub_16bit + - { srcinst: 3, srcop: 0, dstinst: 4, dstop: 5, subreg: 6 } # sub_32bit + # Substitution involving sub_8bit_hi, should land in $ah + - { srcinst: 5, srcop: 0, dstinst: 6, dstop: 0, subreg: 2 } # sub_8bit_hi + - { srcinst: 6, srcop: 0, dstinst: 7, dstop: 0, subreg: 4 } # sub_16bit + - { srcinst: 7, srcop: 0, dstinst: 4, dstop: 5, subreg: 6 } # sub_32bit + # Several redundant substitutions, representing extractions from a small + # register, followed by larger spurious ones, for example: + # %0:gr64 = COPY $rax + # %1:gr32 = COPY %0.sub_32bit + # %2:gr16 = COPY %1.sub_16bit + # %3:gr64 = SUBREG_TO_REG %2, sub_8bit_hi + # %4:gr32 = COPY %3.sub_32bit + # %5:gr16 = COPY %2.sub_16bit + # Should still come out as ah. + - { srcinst: 8, srcop: 0, dstinst: 9, dstop: 0, subreg: 4 } # sub_16bit + - { srcinst: 9, srcop: 0, dstinst: 10,dstop: 0, subreg: 6 } # sub_32bit + - { srcinst: 10,srcop: 0, dstinst: 11,dstop: 0, subreg: 2 } # sub_8bit_hi + - { srcinst: 11,srcop: 0, dstinst: 12,dstop: 0, subreg: 4 } # sub_16bit + - { srcinst: 12,srcop: 0, dstinst: 4, dstop: 5, subreg: 6 } # sub_32bit + # If some kind of really mal-formed code appears that extracts the high bits + # out of a too-small location, we should drop it. It's not clear whether this + # scenario could ever happen; but if it did, best to not emit a known bad + # variable location. Should generate a DBG_VALUE $noreg. + - { srcinst: 13, srcop: 0, dstinst: 14,dstop: 0, subreg: 5 } # sub_16bit_hi + - { srcinst: 14, srcop: 0, dstinst: 15,dstop: 0, subreg: 6 } # sub_32bit + - { srcinst: 15, srcop: 0, dstinst: 4, dstop: 5, subreg: 1 } # sub_8bit +stack: + - { id: 0, name: '', type: spill-slot, offset: -16, size: 8, alignment: 8, + stack-id: default, callee-saved-register: '', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } +body: | + bb.0: + liveins: $rdi, $rax + CALL64pcrel32 @ext, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit-def $rax, debug-instr-number 4, debug-location !12 + ; CHECK: CALL64pcrel32 + DBG_INSTR_REF 1, 0, !11, !DIExpression(), debug-location !12 + ; CHECK-NEXT: DBG_INSTR_REF 1, 0 + ; CHECK-NEXT: DBG_VALUE $al + DBG_INSTR_REF 5, 0, !11, !DIExpression(), debug-location !12 + ; CHECK-NEXT: DBG_INSTR_REF 5, 0 + ; CHECK-NEXT: DBG_VALUE $ah + DBG_INSTR_REF 8, 0, !11, !DIExpression(), debug-location !12 + ; CHECK-NEXT: DBG_INSTR_REF 8, 0 + ; CHECK-NEXT: DBG_VALUE $ah + DBG_INSTR_REF 13, 0, !11, !DIExpression(), debug-location !12 + ; CHECK-NEXT: DBG_INSTR_REF 13, 0 + ; CHECK-NEXT: DBG_VALUE $noreg + MOV64mr $rsp, 1, $noreg, 16, $noreg, $rax :: (store 8 into %stack.0) + $rax = MOV64ri 0, debug-location !12 + ; CHECK: $rax = MOV64ri 0 + ; The value is now located in a spill slot; currently InstrRefBasedLDV + ; can't express subregister locations inside spills. These should all + ; end up being $noreg, but could be improved in the future. + DBG_INSTR_REF 1, 0, !11, !DIExpression(), debug-location !12 + ; CHECK-NEXT: DBG_INSTR_REF 1, 0 + ; CHECK-NEXT: DBG_VALUE $noreg + DBG_INSTR_REF 5, 0, !11, !DIExpression(), debug-location !12 + ; CHECK-NEXT: DBG_INSTR_REF 5, 0 + ; CHECK-NEXT: DBG_VALUE $noreg + DBG_INSTR_REF 8, 0, !11, !DIExpression(), debug-location !12 + ; CHECK-NEXT: DBG_INSTR_REF 8, 0 + ; CHECK-NEXT: DBG_VALUE $noreg + DBG_INSTR_REF 13, 0, !11, !DIExpression(), debug-location !12 + ; CHECK-NEXT: DBG_INSTR_REF 13, 0 + ; CHECK-NEXT: DBG_VALUE $noreg + $rax = MOV64rm $rsp, 1, $noreg, 8, $noreg :: (load 8 from %stack.0) + RETQ $rax, debug-location !12 +...