Index: llvm/include/llvm/CodeGen/MachineFunction.h =================================================================== --- llvm/include/llvm/CodeGen/MachineFunction.h +++ llvm/include/llvm/CodeGen/MachineFunction.h @@ -544,6 +544,10 @@ /// instruction referencing. bool useDebugInstrRef() const; + /// A reserved operand number representing the instructions memory operand, + /// for instructions that have a stack spill fused into them. + static unsigned int DebugOperandMemNumber; + MachineFunction(Function &F, const LLVMTargetMachine &Target, const TargetSubtargetInfo &STI, unsigned FunctionNum, MachineModuleInfo &MMI); Index: llvm/lib/CodeGen/InlineSpiller.cpp =================================================================== --- llvm/lib/CodeGen/InlineSpiller.cpp +++ llvm/lib/CodeGen/InlineSpiller.cpp @@ -928,6 +928,32 @@ // Update the call site info. if (MI->isCandidateForCallSiteEntry()) MI->getMF()->moveCallSiteInfo(MI, FoldMI); + + // If we've folded a store into an instruction labelled with debug-info, + // record a substitution from the old operand to the memory operand. Handle + // the simple common case where operand 0 is the one being folded, plus when + // the destination operand is also a tied def. More values could be + // substituted / preserved with more analysis. + if (MI->peekDebugInstrNum() && Ops[0].second == 0) { + // Helper lambda. + auto MakeSubstitution = [this,FoldMI,MI,&Ops]() { + // Substitute old operand zero to the new instructions memory operand. + unsigned OldOperandNum = Ops[0].second; + unsigned NewNum = FoldMI->getDebugInstrNum(); + unsigned OldNum = MI->getDebugInstrNum(); + MF.makeDebugValueSubstitution({OldNum, OldOperandNum}, + {NewNum, MachineFunction::DebugOperandMemNumber}); + }; + + const MachineOperand &Op0 = MI->getOperand(Ops[0].second); + if (Ops.size() == 1 && Op0.isDef()) { + MakeSubstitution(); + } else if (Ops.size() == 2 && Op0.isDef() && MI->getOperand(1).isTied() && + Op0.getReg() == MI->getOperand(1).getReg()) { + MakeSubstitution(); + } + } + MI->eraseFromParent(); // Insert any new instructions other than FoldMI into the LIS maps. Index: llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h =================================================================== --- llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h +++ llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h @@ -984,6 +984,21 @@ void dump_mloc_transfer(const MLocTransferMap &mloc_transfer) const; bool isCalleeSaved(LocIdx L) const; + + bool hasFoldedStackStore(const MachineInstr &MI) { + // Instruction must have a memory operand that's a stack slot, and isn't + // aliased, meaning it's a spill from regalloc instead of a variable. + // If it's aliased, we can't guarantee its value. + if (!MI.hasOneMemOperand()) + return false; + auto *MemOperand = *MI.memoperands_begin(); + return MemOperand->isStore() && + MemOperand->getPseudoValue() && + MemOperand->getPseudoValue()->kind() == PseudoSourceValue::FixedStack + && !MemOperand->getPseudoValue()->isAliased(MFI); + } + + Optional findLocationForMemOperand(const MachineInstr &MI); }; } // namespace LiveDebugValues Index: llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp =================================================================== --- llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp +++ llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp @@ -884,6 +884,27 @@ return MTracker->getOrTrackSpillLoc({Reg, Offset}); } +Optional InstrRefBasedLDV::findLocationForMemOperand(const MachineInstr &MI) { + SpillLocationNo SpillLoc = extractSpillBaseRegAndOffset(MI); + + // Where in the stack slot is this value defined -- i.e., what size of value + // is this? An important question, because it could be loaded into a register + // from the stack at some point. Happily the memory operand will tell us + // the size written to the stack. + auto *MemOperand = *MI.memoperands_begin(); + unsigned SizeInBits = MemOperand->getSizeInBits(); + + // Find that position in the stack indexes we're tracking. + auto IdxIt = MTracker->StackSlotIdxes.find({SizeInBits, 0}); + if (IdxIt == MTracker->StackSlotIdxes.end()) + // That index is not tracked. This is suprising, and unlikely to ever + // occur, but the safe action is to indicate the variable is optimised out. + return None; + + unsigned SpillID = MTracker->getSpillIDWithIdx(SpillLoc, IdxIt->second); + return MTracker->getSpillMLoc(SpillID); +} + /// End all previous ranges related to @MI and start a new range from @MI /// if it is a DBG_VALUE instr. bool InstrRefBasedLDV::transferDebugValue(const MachineInstr &MI) { @@ -1010,16 +1031,25 @@ const MachineInstr &TargetInstr = *InstrIt->second.first; uint64_t BlockNo = TargetInstr.getParent()->getNumber(); - // Pick out the designated operand. - assert(OpNo < TargetInstr.getNumOperands()); - const MachineOperand &MO = TargetInstr.getOperand(OpNo); - - // Today, this can only be a register. - assert(MO.isReg() && MO.isDef()); - - unsigned LocID = MTracker->getLocID(MO.getReg()); - LocIdx L = MTracker->LocIDToLocIdx[LocID]; - NewID = ValueIDNum(BlockNo, InstrIt->second.second, L); + // Pick out the designated operand. It might be a memory reference, if + // a register def was folded into a stack store. + if (OpNo == MachineFunction::DebugOperandMemNumber && + TargetInstr.hasOneMemOperand()) { + Optional L = findLocationForMemOperand(TargetInstr); + if (L) + NewID = ValueIDNum(BlockNo, InstrIt->second.second, *L); + } else if (OpNo != MachineFunction::DebugOperandMemNumber) { + assert(OpNo < TargetInstr.getNumOperands()); + const MachineOperand &MO = TargetInstr.getOperand(OpNo); + + // Today, this can only be a register. + assert(MO.isReg() && MO.isDef()); + + unsigned LocID = MTracker->getLocID(MO.getReg()); + LocIdx L = MTracker->LocIDToLocIdx[LocID]; + NewID = ValueIDNum(BlockNo, InstrIt->second.second, L); + } + // else: NewID is left as None. } else if (PHIIt != DebugPHINumToValue.end() && PHIIt->InstrNum == InstNo) { // It's actually a PHI value. Which value it is might not be obvious, use // the resolver helper to find out. @@ -1278,6 +1308,16 @@ for (auto *MO : RegMaskPtrs) MTracker->writeRegMask(MO, CurBB, CurInst); + // If this instruction writes to a spill slot, def that slot. + if (hasFoldedStackStore(MI)) { + SpillLocationNo SpillNo = extractSpillBaseRegAndOffset(MI); + for (unsigned int I = 0; I < MTracker->NumSlotIdxes; ++I) { + unsigned SpillID = MTracker->getSpillIDWithIdx(SpillNo, I); + LocIdx L = MTracker->getSpillMLoc(SpillID); + MTracker->setMLoc(L, ValueIDNum(CurBB, CurInst, L)); + } + } + if (!TTracker) return; @@ -1303,6 +1343,16 @@ if (MO->clobbersPhysReg(Reg)) TTracker->clobberMloc(L.Idx, MI.getIterator(), false); } + + // Tell TTracker about any folded stack store. + if (hasFoldedStackStore(MI)) { + SpillLocationNo SpillNo = extractSpillBaseRegAndOffset(MI); + for (unsigned int I = 0; I < MTracker->NumSlotIdxes; ++I) { + unsigned SpillID = MTracker->getSpillIDWithIdx(SpillNo, I); + LocIdx L = MTracker->getSpillMLoc(SpillID); + TTracker->clobberMloc(L, MI.getIterator(), true); + } + } } void InstrRefBasedLDV::performCopy(Register SrcRegNum, Register DstRegNum) { @@ -1342,6 +1392,12 @@ if (!MI.hasOneMemOperand()) return false; + // Reject any memory operand that's aliased -- we can't guarantee its value. + auto MMOI = MI.memoperands_begin(); + const PseudoSourceValue *PVal = (*MMOI)->getPseudoValue(); + if (PVal->isAliased(MFI)) + return false; + if (!MI.getSpillSize(TII) && !MI.getFoldedSpillSize(TII)) return false; // This is not a spill instruction, since no valid size was // returned from either function. @@ -1393,6 +1449,13 @@ LLVM_DEBUG(dbgs() << "Examining instruction: "; MI.dump();); + // Strictly limit ourselves to plain loads and stores, not all instructions + // that can access the stack. + int FIDummy; + if (!TII->isStoreToStackSlotPostFE(MI, FIDummy) && + !TII->isLoadFromStackSlotPostFE(MI, FIDummy)) + return false; + // First, if there are any DBG_VALUEs pointing at a spill slot that is // written to, terminate that variable location. The value in memory // will have changed. DbgEntityHistoryCalculator doesn't try to detect this. Index: llvm/lib/CodeGen/MachineFunction.cpp =================================================================== --- llvm/lib/CodeGen/MachineFunction.cpp +++ llvm/lib/CodeGen/MachineFunction.cpp @@ -974,6 +974,9 @@ unsigned Subreg) { // Catch any accidental self-loops. assert(A.first != B.first); + // Don't allow any substitutions _from_ the memory operand number. + assert(A.second != DebugOperandMemNumber); + DebugValueSubstitutions.push_back({A, B, Subreg}); } @@ -1239,6 +1242,9 @@ return false; } +// Use one million as a high / reserved number. +unsigned MachineFunction::DebugOperandMemNumber = 1000000; + /// \} //===----------------------------------------------------------------------===// Index: llvm/test/DebugInfo/MIR/InstrRef/memory-operand-folding-tieddef.mir =================================================================== --- /dev/null +++ llvm/test/DebugInfo/MIR/InstrRef/memory-operand-folding-tieddef.mir @@ -0,0 +1,175 @@ +# RUN: llc %s -o - -experimental-debug-variable-locations \ +# RUN: -start-before=x86-flags-copy-lowering -stop-after=virtregrewriter \ +# RUN: | FileCheck %s +# +# This test is for stack spill folding -- the INC32r near the end of the MIR +# below show be morphed into an INC32m by the register allocator, making it +# load-operate-store to %stack.0. We should track this fact in the substitution +# table, by adding a substitution to the memory-operand operand number. +# +# The INC32r is a tied-def instruction. +# +# NB: This test would more ideally start at phi-node-elimination, where +# register allocation begins, however for some reason the INC32r gets +# transformed into a different instruction if we do that. Start at the last +# optimisation pass before regalloc instead. +# +# CHECK: debugValueSubstitutions: +# CHECK-NEXT: - { srcinst: 1, srcop: 0, dstinst: 2, dstop: 1000000, subreg: 0 } +# CHECK-LABEL: bb.5: +# CHECK: INC32m %stack.0, {{.*}} debug-instr-number 2, +--- | + ; ModuleID = 'reduced.ll' + source_filename = "reduced.ll" + target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + + %"class.llvm::APInt" = type { i32, %union.anon } + %union.anon = type { i64 } + + define void @_ZNK4llvm5APInt5magicEv() local_unnamed_addr align 2 !dbg !7 { + ret void + } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4, !5} + !llvm.ident = !{!6} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) + !1 = !DIFile(filename: "test.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"} + !7 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 3, type: !8, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) + !8 = !DISubroutineType(types: !9) + !9 = !{!10, !11, !11} + !10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !11 = !DIBasicType(name: "short", size: 16, encoding: DW_ATE_signed) + !12 = !DILocalVariable(name: "bar", arg: 1, scope: !7, file: !1, line: 3, type: !11) + !13 = !DILocation(line: 0, scope: !7) + !14 = !DILocalVariable(name: "baz", arg: 2, scope: !7, file: !1, line: 3, type: !11) + !15 = distinct !DILexicalBlock(scope: !7, file: !1, line: 8, column: 7) + +... +--- +name: _ZNK4llvm5APInt5magicEv +alignment: 16 +tracksRegLiveness: true +registers: + - { id: 0, class: gr64 } + - { id: 1, class: gr32 } + - { id: 2, class: gr64 } + - { id: 3, class: gr32 } + - { id: 4, class: gr64 } + - { id: 5, class: gr32 } + - { id: 6, class: gr32 } + - { id: 7, class: gr32 } + - { id: 8, class: gr64 } + - { id: 9, class: gr32 } + - { id: 10, class: gr64 } + - { id: 11, class: gr32 } + - { id: 12, class: gr64 } + - { id: 13, class: gr32 } + - { id: 14, class: gr64 } + - { id: 15, class: gr32 } + - { id: 16, class: gr64 } + - { id: 17, class: gr64 } + - { id: 18, class: gr64 } + - { id: 19, class: gr32 } + - { id: 20, class: gr8 } + - { id: 21, class: gr32 } + - { id: 22, class: gr64 } + - { id: 23, class: gr64 } + - { id: 24, class: gr64 } + - { id: 25, class: gr64 } + - { id: 26, class: gr64 } + - { id: 27, class: gr32 } + - { id: 28, class: gr8 } + - { id: 29, class: gr64 } + - { id: 30, class: gr64 } + - { id: 31, class: gr64 } + - { id: 32, class: gr64 } + - { id: 33, class: gr64 } + - { id: 34, class: gr64 } + - { id: 35, class: gr64 } + - { id: 36, class: gr64 } + - { id: 37, class: gr64 } + - { id: 38, class: gr8 } +frameInfo: + maxAlignment: 1 + hasCalls: true +machineFunctionInfo: {} +body: | + bb.0: + %15:gr32 = IMPLICIT_DEF + %14:gr64 = IMPLICIT_DEF + %17:gr64 = IMPLICIT_DEF + %18:gr64 = IMPLICIT_DEF + %19:gr32 = MOV32r0 implicit-def dead $eflags + %22:gr64 = IMPLICIT_DEF + %23:gr64 = IMPLICIT_DEF + %24:gr64 = IMPLICIT_DEF + %25:gr64 = IMPLICIT_DEF + %26:gr64 = IMPLICIT_DEF + %30:gr64 = IMPLICIT_DEF + %31:gr64 = IMPLICIT_DEF + %32:gr64 = IMPLICIT_DEF + %33:gr64 = IMPLICIT_DEF + %34:gr64 = IMPLICIT_DEF + %35:gr64 = IMPLICIT_DEF + %36:gr64 = IMPLICIT_DEF + %37:gr64 = IMPLICIT_DEF + %21:gr32 = IMPLICIT_DEF + + bb.1: + %0:gr64 = PHI %14, %bb.0, %12, %bb.5 + %1:gr32 = PHI %15, %bb.0, %11, %bb.5 + %2:gr64 = PHI %14, %bb.0, %10, %bb.5 + %3:gr32 = PHI %15, %bb.0, %9, %bb.5 + %4:gr64 = PHI %14, %bb.0, %8, %bb.5 + %5:gr32 = PHI %15, %bb.0, %7, %bb.5, debug-location !13 + %6:gr32 = PHI %15, %bb.0, %13, %bb.5, debug-location !13 + %16:gr64 = ADD64rr %4, %4, implicit-def dead $eflags, debug-location !13 + MOV32mr %17, 1, $noreg, 0, $noreg, %5, debug-location !13 :: (store (s32) into `i32* undef`, align 8) + MOV64mr %18, 1, $noreg, 0, $noreg, killed %16, debug-location !13 :: (store (s64) into `i64* undef`) + %20:gr8 = COPY %19.sub_8bit + TEST8rr %20, %20, implicit-def $eflags, debug-location !13 + JCC_1 %bb.3, 5, implicit $eflags, debug-location !13 + JMP_1 %bb.2, debug-location !13 + + bb.2: + + bb.3: + successors: %bb.4, %bb.5 + + %7:gr32 = PHI %5, %bb.1, %21, %bb.2, debug-location !13 + MOV32mr %22, 1, $noreg, 0, $noreg, %7, debug-location !13 :: (store (s32) into `i32* undef`, align 8) + %8:gr64 = MOV64rm %23, 1, $noreg, 0, $noreg, debug-location !13 :: (load (s64) from `i64* undef`) + MOV32mr %24, 1, $noreg, 0, $noreg, %3, debug-location !13 :: (store (s32) into `i32* undef`, align 8) + MOV64mi32 %25, 1, $noreg, 0, $noreg, 0, debug-location !13 :: (store (s64) into `i64* undef`) + %28:gr8 = COPY %19.sub_8bit + TEST8rr %28, %28, implicit-def $eflags, debug-location !13 + JCC_1 %bb.5, 5, implicit $eflags, debug-location !13 + JMP_1 %bb.4, debug-location !13 + + bb.4: + %29:gr64 = ADD64rr %2, %2, implicit-def dead $eflags, debug-location !13 + MOV64mr %30, 1, $noreg, 0, $noreg, killed %29, debug-location !13 :: (store (s64) into `i64* undef`) + + bb.5: + %9:gr32 = MOV32rm %26, 1, $noreg, 0, $noreg, debug-location !13 :: (load (s32) from `i32* undef`, align 8) + %10:gr64 = MOV64rm %31, 1, $noreg, 0, $noreg, debug-location !13 :: (load (s64) from `i64* undef`) + %12:gr64 = ADD64rr %0, %0, implicit-def dead $eflags, debug-location !13 + MOV32mr %32, 1, $noreg, 0, $noreg, %1, debug-location !13 :: (store (s32) into `i32* undef`, align 8) + MOV64mr %33, 1, $noreg, 0, $noreg, %12, debug-location !13 :: (store (s64) into `i64* undef`) + %11:gr32 = MOV32rm %34, 1, $noreg, 0, $noreg, debug-location !13 :: (load (s32) from `i32* undef`, align 8) + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !13 + $rdi = COPY %35, debug-location !13 + $rsi = COPY %36, debug-location !13 + CALL64r %37, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $rsi, implicit-def $rsp, implicit-def $ssp, implicit-def $al, debug-location !13 + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !13 + %13:gr32 = INC32r %6, implicit-def dead $eflags, debug-instr-number 1, debug-location !13 + DBG_INSTR_REF 1, 0, !12, !DIExpression(), debug-location !13 + JMP_1 %bb.1, debug-location !13 +... Index: llvm/test/DebugInfo/MIR/InstrRef/memory-operand-folding.mir =================================================================== --- /dev/null +++ llvm/test/DebugInfo/MIR/InstrRef/memory-operand-folding.mir @@ -0,0 +1,259 @@ +# RUN: llc %s -o - -experimental-debug-variable-locations \ +# RUN: -start-before=x86-flags-copy-lowering -stop-after=virtregrewriter \ +# RUN: | FileCheck %s +# +# This test is for stack spill folding -- the SETCC near the start of the MIR +# below show be morphed into an SETCCm by the register allocator, making it +# store to %stack.0. We should track this fact in the substitution table, by +# adding a substitution to the memory-operand operand number. +# +# This is a single operand spill -- there's a separate test for tied def ones. +# +# Ideally this test would be shorter; however, it needs to be sufficiently +# complex to force the register allocator to spill something, so there's a +# limit. +# +# CHECK: debugValueSubstitutions: +# CHECK-NEXT: - { srcinst: 1, srcop: 0, dstinst: 2, dstop: 1000000, subreg: 0 } +# CHECK-LABEL: bb.0: +# CHECK: SETCCm %stack.0, {{.*}} debug-instr-number 2 +--- | + target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + + define internal fastcc void @beans(i32 %Kind) unnamed_addr align 2 !dbg !7 { + ret void + } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4, !5} + !llvm.ident = !{!6} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) + !1 = !DIFile(filename: "test.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"} + !7 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 3, type: !8, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) + !8 = !DISubroutineType(types: !9) + !9 = !{!10, !11, !11} + !10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !11 = !DIBasicType(name: "short", size: 16, encoding: DW_ATE_signed) + !12 = !DILocalVariable(name: "bar", arg: 1, scope: !7, file: !1, line: 3, type: !11) + !13 = !DILocation(line: 0, scope: !7) + !14 = !DILocalVariable(name: "baz", arg: 2, scope: !7, file: !1, line: 3, type: !11) + !15 = distinct !DILexicalBlock(scope: !7, file: !1, line: 8, column: 7) + + +... +--- +name: beans +alignment: 16 +tracksRegLiveness: true +registers: + - { id: 0, class: gr8 } + - { id: 1, class: gr8 } + - { id: 2, class: gr8 } + - { id: 3, class: gr32 } + - { id: 4, class: gr64 } + - { id: 5, class: gr8 } + - { id: 6, class: gr32 } + - { id: 7, class: gr32 } + - { id: 8, class: gr32 } + - { id: 9, class: gr32 } + - { id: 10, class: gr32 } + - { id: 11, class: gr64 } + - { id: 12, class: gr64 } + - { id: 13, class: gr32 } + - { id: 14, class: gr8 } + - { id: 15, class: gr8 } + - { id: 16, class: gr32 } + - { id: 17, class: gr64 } + - { id: 18, class: gr8 } + - { id: 19, class: gr8 } + - { id: 20, class: gr32 } + - { id: 21, class: gr64 } + - { id: 22, class: gr32 } + - { id: 23, class: gr32 } + - { id: 24, class: gr64 } + - { id: 25, class: gr32 } + - { id: 26, class: gr64 } + - { id: 27, class: gr8 } + - { id: 28, class: gr64_nosp } + - { id: 29, class: gr32 } + - { id: 30, class: gr8 } + - { id: 31, class: gr8 } + - { id: 32, class: gr64 } + - { id: 33, class: gr8 } + - { id: 34, class: gr64 } + - { id: 35, class: gr64 } + - { id: 36, class: gr8 } + - { id: 37, class: gr64 } + - { id: 38, class: gr8 } + - { id: 39, class: gr32 } + - { id: 40, class: gr32 } + - { id: 41, class: gr8 } + - { id: 42, class: gr8 } + - { id: 43, class: gr8 } +liveins: + - { reg: '$edi', virtual-reg: '%7' } +frameInfo: + maxAlignment: 1 + hasCalls: true +machineFunctionInfo: {} +jumpTable: + kind: block-address + entries: + - id: 0 + blocks: [ '%bb.11', '%bb.7', '%bb.11', '%bb.7', '%bb.12', + '%bb.7', '%bb.7', '%bb.7', '%bb.7', '%bb.7', '%bb.7', + '%bb.7', '%bb.7', '%bb.7', '%bb.7', '%bb.13', + '%bb.7', '%bb.7', '%bb.7', '%bb.7', '%bb.7', '%bb.7', + '%bb.7', '%bb.7', '%bb.7', '%bb.7', '%bb.7', '%bb.7', + '%bb.7', '%bb.7', '%bb.7', '%bb.7', '%bb.7', '%bb.7', + '%bb.7', '%bb.7', '%bb.7', '%bb.7', '%bb.7', '%bb.7', + '%bb.8' ] +body: | + bb.0: + successors: %bb.2(0x20000000), %bb.14(0x60000000) + liveins: $edi + + %7:gr32 = COPY $edi + CMP32ri8 %7, 4, implicit-def $eflags + %0:gr8 = SETCCr 4, implicit $eflags, debug-instr-number 1 + CMP32ri8 %7, 2, implicit-def $eflags + %1:gr8 = SETCCr 4, implicit $eflags + CMP32ri8 %7, 1, implicit-def $eflags + %2:gr8 = SETCCr 4, implicit $eflags + %11:gr64 = IMPLICIT_DEF + %12:gr64 = IMPLICIT_DEF + %5:gr8 = MOV8rm %12, 1, $noreg, 0, $noreg, debug-location !13 :: (load (s8) from `i8* undef`, align 8) + %13:gr32 = MOV32r0 implicit-def dead $eflags + %14:gr8 = COPY %13.sub_8bit + TEST8rr %14, %14, implicit-def $eflags, debug-location !13 + JCC_1 %bb.2, 5, implicit $eflags, debug-location !13 + JMP_1 %bb.14, debug-location !13 + + bb.14: + successors: %bb.2(0x2aaaaaab), %bb.1(0x55555555) + + CMP8ri %5, 70, implicit-def $eflags, debug-location !13 + JCC_1 %bb.2, 5, implicit $eflags, debug-location !13 + JMP_1 %bb.1, debug-location !13 + + bb.1: + + bb.2: + successors: %bb.4(0x20000000), %bb.15(0x60000000) + + %4:gr64 = MOV64rm %11, 1, $noreg, 0, $noreg, debug-location !13 :: (load (s64) from `%"class.llvm::Instruction"** undef`) + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !13 + %16:gr32 = IMPLICIT_DEF + $edi = COPY %16, debug-location !13 + %17:gr64 = IMPLICIT_DEF + CALL64r killed %17, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit-def $rsp, implicit-def $ssp, implicit-def $al, debug-location !13 + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !13 + %18:gr8 = COPY $al, debug-location !13 + TEST8ri %18, 1, implicit-def $eflags, debug-location !13 + JCC_1 %bb.4, 5, implicit $eflags, debug-location !13 + JMP_1 %bb.15, debug-location !13 + + bb.15: + successors: %bb.4(0x2aaaaaab), %bb.3(0x55555555) + + CMP8ri %5, 70, implicit-def $eflags, debug-location !13 + JCC_1 %bb.4, 4, implicit $eflags, debug-location !13 + JMP_1 %bb.3, debug-location !13 + + bb.3: + + bb.4: + successors: %bb.5, %bb.6 + + %21:gr64 = IMPLICIT_DEF + %20:gr32 = MOVZX32rm8 killed %21, 1, $noreg, 0, $noreg, debug-location !13 :: (load (s8) from `i32* undef`, align 8) + %6:gr32 = nsw DEC32r %20, implicit-def dead $eflags, debug-location !13 + CMP32ri8 %6, 5, implicit-def $eflags, debug-location !13 + JCC_1 %bb.6, 7, implicit $eflags, debug-location !13 + JMP_1 %bb.5, debug-location !13 + + bb.5: + %24:gr64 = IMPLICIT_DEF + %23:gr32 = MOVZX32rm8 %24, 1, $noreg, 0, $noreg, debug-location !13 :: (load (s8) from `i8* undef`, align 8) + %25:gr32 = nsw ADD32ri8 %23, -22, implicit-def dead $eflags, debug-location !13 + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !13 + $edi = COPY %25, debug-location !13 + %26:gr64 = IMPLICIT_DEF + CALL64r %26, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit-def $rsp, implicit-def $ssp, implicit-def $al, debug-location !13 + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !13 + + bb.6: + successors: %bb.7(0x0aaaaaab), %bb.16(0x75555555) + + %31:gr8 = IMPLICIT_DEF + CMP8ri %31, 40, implicit-def $eflags, debug-location !13 + JCC_1 %bb.7, 7, implicit $eflags, debug-location !13 + + bb.16: + successors: %bb.11(0x2e8ba2ea), %bb.7(0x0ba2e8ba), %bb.12(0x1745d174), %bb.13(0x1745d174), %bb.8(0x1745d174) + + %29:gr32 = MOV32r0 implicit-def dead $eflags + %28:gr64_nosp = SUBREG_TO_REG 0, %29, %subreg.sub_32bit + JMP64m $noreg, 8, %28, %jump-table.0, $noreg :: (load (s64) from jump-table) + + bb.7: + %43:gr8 = IMPLICIT_DEF + $al = COPY %43 + RET 0, $al + + bb.8: + successors: %bb.10(0x20000000), %bb.17(0x60000000) + + CMP32ri8 %6, 5, implicit-def $eflags, debug-location !13 + JCC_1 %bb.10, 7, implicit $eflags, debug-location !13 + JMP_1 %bb.17, debug-location !13 + + bb.17: + successors: %bb.10(0x2aaaaaab), %bb.9(0x55555555) + + %3:gr32 = ADD32ri8 %7, -7, implicit-def dead $eflags + CMP32ri8 %3, 3, implicit-def $eflags, debug-location !13 + JCC_1 %bb.10, 2, implicit $eflags, debug-location !13 + JMP_1 %bb.9, debug-location !13 + + bb.9: + %41:gr8 = IMPLICIT_DEF + $al = COPY %41 + RET 0, $al + + bb.10: + %42:gr8 = IMPLICIT_DEF + $al = COPY %42 + RET 0, $al + + bb.11: + %37:gr64 = IMPLICIT_DEF + MOV8mr killed %37, 1, $noreg, 0, $noreg, %2, debug-location !13 :: (store (s8) into `i8* undef`, align 8) + %38:gr8 = IMPLICIT_DEF + $al = COPY %38 + RET 0, $al + + bb.12: + %34:gr64 = IMPLICIT_DEF + MOV8mr %34, 1, $noreg, 0, $noreg, %1, debug-location !13 :: (store (s8) into `i8* undef`, align 8) + %35:gr64 = IMPLICIT_DEF + MOV64mr %35, 1, $noreg, 0, $noreg, %4, debug-location !13 :: (store (s64) into `%"class.llvm::Instruction"** undef`) + %36:gr8 = IMPLICIT_DEF + $al = COPY %36 + RET 0, $al + + bb.13: + DBG_INSTR_REF 1, 0, !12, !DIExpression(), debug-location !13 + %32:gr64 = IMPLICIT_DEF + MOV8mr killed %32, 1, $noreg, 0, $noreg, %0, debug-location !13 :: (store (s8) into `i8* undef`, align 8) + %33:gr8 = IMPLICIT_DEF + $al = COPY %33 + RET 0, $al + +... Index: llvm/test/DebugInfo/MIR/InstrRef/memory-operand-load-folding.mir =================================================================== --- /dev/null +++ llvm/test/DebugInfo/MIR/InstrRef/memory-operand-load-folding.mir @@ -0,0 +1,131 @@ +# RUN: llc %s -o - -experimental-debug-variable-locations \ +# RUN: -start-before=phi-node-elimination -stop-after=virtregrewriter \ +# RUN: | FileCheck %s +# +# Test that when a load gets folded into an instruction (the CVTTSS2SIrr below) +# that the debug-instr-number is preserved, with a substitution. +# +# CHECK: debugValueSubstitutions: +# CHECK-NEXT: - { srcinst: 1, srcop: 0, dstinst: 2, dstop: 0, subreg: 0 } +# CHECK-LABEL: bb.1.sw.bb: +# CHECK: renamable $eax = nofpexcept CVTTSS2SIrm %stack.0, +# CHECK-SAME: debug-instr-number 2 :: (load (s32) from %stack.0) +--- | + ; ModuleID = 'lol.ll' + source_filename = "reduced.ll" + 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" + + define i32 @foo(i32 %i, float %f) local_unnamed_addr { + if.then: + %call = tail call i32 (i32, ...) undef(i32 %i) + %cond = icmp eq i32 %call, 1 + br i1 %cond, label %sw.bb, label %sw.epilog + + sw.bb: ; preds = %if.then + %conv = fptosi float %f to i8 + call void @llvm.dbg.value(metadata i8 %conv, metadata !6, metadata !DIExpression()), !dbg !17 + %tobool.not = icmp eq i8 %conv, 0 + br i1 %tobool.not, label %if.end, label %sw.epilog + + if.end: ; preds = %sw.bb + tail call void (...) undef() + br label %sw.epilog + + sw.epilog: ; preds = %if.then, %if.end, %sw.bb + ret i32 undef + } + + ; Function Attrs: nofree nosync nounwind readnone speculatable willreturn + declare void @llvm.dbg.value(metadata, metadata, metadata) #0 + + attributes #0 = { nofree nosync nounwind readnone speculatable willreturn } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!2, !3, !4, !5} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) + !1 = !DIFile(filename: "/tmp/test.c", directory: ".") + !2 = !{i32 7, !"Dwarf Version", i32 4} + !3 = !{i32 2, !"Debug Info Version", i32 3} + !4 = !{i32 1, !"wchar_size", i32 4} + !5 = !{i32 7, !"uwtable", i32 1} + !6 = !DILocalVariable(name: "b", scope: !7, file: !8, line: 15, type: !16) + !7 = distinct !DISubprogram(name: "foo", scope: !8, file: !8, line: 8, type: !9, scopeLine: 9, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !15) + !8 = !DIFile(filename: "/tmp/test.c", directory: "") + !9 = !DISubroutineType(types: !10) + !10 = !{!11, !11, !12, !13} + !11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !12 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float) + !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) + !14 = !DIBasicType(name: "short", size: 16, encoding: DW_ATE_signed) + !15 = !{} + !16 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) + !17 = !DILocation(line: 0, scope: !7) + +... +--- +name: foo +tracksRegLiveness: true +registers: + - { id: 0, class: gr32, preferred-register: '' } + - { id: 1, class: fr32, preferred-register: '' } + - { id: 2, class: gr32, preferred-register: '' } + - { id: 3, class: gr8, preferred-register: '' } + - { id: 4, class: gr64, preferred-register: '' } + - { id: 5, class: gr32, preferred-register: '' } + - { id: 6, class: gr32, preferred-register: '' } + - { id: 7, class: gr32, preferred-register: '' } + - { id: 8, class: gr8, preferred-register: '' } + - { id: 9, class: gr32, preferred-register: '' } + - { id: 10, class: gr8, preferred-register: '' } + - { id: 11, class: gr64, preferred-register: '' } + - { id: 12, class: gr32, preferred-register: '' } +liveins: + - { reg: '$edi', virtual-reg: '%0' } + - { reg: '$xmm0', virtual-reg: '%1' } +frameInfo: + hasCalls: true +body: | + bb.0.if.then: + successors: %bb.1(0x40000000), %bb.3(0x40000000) + liveins: $edi, $xmm0 + + %1:fr32 = COPY killed $xmm0 + %0:gr32 = COPY killed $edi + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %2:gr32 = MOV32r0 implicit-def dead $eflags + %3:gr8 = COPY killed %2.sub_8bit + $edi = COPY killed %0 + $al = COPY killed %3 + CALL64r undef %4:gr64, csr_64, implicit $rsp, implicit $ssp, implicit killed $edi, implicit killed $al, implicit-def $rsp, implicit-def $ssp, implicit-def $eax + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %5:gr32 = COPY killed $eax + CMP32ri8 killed %5, 1, implicit-def $eflags + JCC_1 %bb.3, 5, implicit killed $eflags + JMP_1 %bb.1 + + bb.1.sw.bb: + successors: %bb.2(0x30000000), %bb.3(0x50000000) + + %7:gr32 = nofpexcept CVTTSS2SIrr killed %1, implicit $mxcsr, debug-instr-number 1 + %8:gr8 = COPY killed %7.sub_8bit + DBG_INSTR_REF 2, 0, !6, !DIExpression(), debug-location !17 + TEST8rr killed %8, %8, implicit-def $eflags + JCC_1 %bb.3, 5, implicit killed $eflags + JMP_1 %bb.2 + + bb.2.if.end: + successors: %bb.3(0x80000000) + + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %9:gr32 = MOV32r0 implicit-def dead $eflags + %10:gr8 = COPY killed %9.sub_8bit + $al = COPY killed %10 + CALL64r undef %11:gr64, csr_64, implicit $rsp, implicit $ssp, implicit killed $al, implicit-def $rsp, implicit-def $ssp + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + + bb.3.sw.epilog: + RET 0, undef $eax + +... Index: llvm/test/DebugInfo/MIR/InstrRef/memory-operand-tracking.mir =================================================================== --- /dev/null +++ llvm/test/DebugInfo/MIR/InstrRef/memory-operand-tracking.mir @@ -0,0 +1,66 @@ +# RUN: llc %s -march=x86-64 -run-pass=livedebugvalues -experimental-debug-variable-locations -o - 2>&1 | FileCheck %s +# +# Test that memory operands of instructions are interpreted by LiveDebugValues: +# if an instruction reference is substituted to a memory operand, we should be +# able to emit a DBG_VALUE referring to its slot. +# +# In addition, further instructions that write to the same stack slot should +# be recognised as clobbering the value in that slot. +--- | + 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: 2, srcop: 0, dstinst: 3, dstop: 1000000, subreg: 0 } +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 + DBG_PHI $rax, 1 + MOV64mr $rsp, 1, $noreg, 16, $noreg, $rdi :: (store 8 into %stack.0) + $rax = MOV64ri 0, debug-location !12 + INC32m $rsp, 1, $noreg, 4, $noreg, implicit-def dead $eflags, debug-instr-number 3, debug-location !DILocation(line: 0, scope: !7) :: (store (s32) into %stack.0) + DBG_INSTR_REF 2, 0, !11, !DIExpression(), debug-location !12 + ; CHECK: DBG_INSTR_REF 2, 0 + ; CHECK-NEXT: DBG_VALUE $rsp + ;; We should _not_ be able to find the old value. + DBG_INSTR_REF 1, 0, !11, !DIExpression(), debug-location !12 + ; CHECK: DBG_INSTR_REF 1, 0 + ; CHECK-NEXT: DBG_VALUE $noreg + INC32m $rsp, 1, $noreg, 4, $noreg, implicit-def dead $eflags, debug-location !12 :: (store (s32) into %stack.0) + ;; The above INC32m should be detected as clobbering the stack location, + ;; even though it isn't debug labelled. + DBG_INSTR_REF 2, 0, !11, !DIExpression(), debug-location !12 + ; CHECK: DBG_INSTR_REF 2, 0 + ; CHECK-NEXT: DBG_VALUE $noreg + $rax = MOV64rm $rsp, 1, $noreg, 8, $noreg :: (load 8 from %stack.0) + RETQ $rax, debug-location !12 +...