[llvm] ee3eee7 - [DebugInfo][InstrRef] Track values fused into stack spills

Jeremy Morse via llvm-commits llvm-commits at lists.llvm.org
Mon Oct 25 07:15:29 PDT 2021


Author: Jeremy Morse
Date: 2021-10-25T15:14:53+01:00
New Revision: ee3eee71e41547050040a8d1ef44b38f6d22f0d2

URL: https://github.com/llvm/llvm-project/commit/ee3eee71e41547050040a8d1ef44b38f6d22f0d2
DIFF: https://github.com/llvm/llvm-project/commit/ee3eee71e41547050040a8d1ef44b38f6d22f0d2.diff

LOG: [DebugInfo][InstrRef] Track values fused into stack spills

During register allocation, some instructions can have stack spills fused
into them. It means that when vregs are allocated on the stack we can
convert:

    SETCCr %0
    DBG_VALUE %0

to

    SETCCm %stack.0
    DBG_VALUE %stack.0

Unfortunately instruction referencing finds this harder: a store to the
stack doesn't have a specific operand number, therefore we don't substitute
the old operand for a new operand, and the location is dropped. This patch
implements a solution: just recognise the memory operand attached to an
instruction with a Special Number (TM), and record a substitution between
the old value and the new one.

This patch adds substitution code to InlineSpiller to record such fused
spills, and tracking in InstrRefBasedLDV to recognise such values, and
produce the value numbers for them. Everything to do with the movement of
stack-defined values is already handled in InstrRefBasedLDV.

Differential Revision: https://reviews.llvm.org/D111317

Added: 
    llvm/test/DebugInfo/MIR/InstrRef/memory-operand-folding-tieddef.mir
    llvm/test/DebugInfo/MIR/InstrRef/memory-operand-folding.mir
    llvm/test/DebugInfo/MIR/InstrRef/memory-operand-load-folding.mir
    llvm/test/DebugInfo/MIR/InstrRef/memory-operand-tracking.mir

Modified: 
    llvm/include/llvm/CodeGen/MachineFunction.h
    llvm/lib/CodeGen/InlineSpiller.cpp
    llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
    llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h
    llvm/lib/CodeGen/MachineFunction.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/CodeGen/MachineFunction.h b/llvm/include/llvm/CodeGen/MachineFunction.h
index eb1ed444b77c5..dcbd19ac6b5ae 100644
--- a/llvm/include/llvm/CodeGen/MachineFunction.h
+++ b/llvm/include/llvm/CodeGen/MachineFunction.h
@@ -544,6 +544,10 @@ class LLVM_EXTERNAL_VISIBILITY MachineFunction {
   /// instruction referencing.
   bool useDebugInstrRef() const;
 
+  /// A reserved operand number representing the instructions memory operand,
+  /// for instructions that have a stack spill fused into them.
+  const static unsigned int DebugOperandMemNumber;
+
   MachineFunction(Function &F, const LLVMTargetMachine &Target,
                   const TargetSubtargetInfo &STI, unsigned FunctionNum,
                   MachineModuleInfo &MMI);

diff  --git a/llvm/lib/CodeGen/InlineSpiller.cpp b/llvm/lib/CodeGen/InlineSpiller.cpp
index 71e91b445d9ab..992b2e6287425 100644
--- a/llvm/lib/CodeGen/InlineSpiller.cpp
+++ b/llvm/lib/CodeGen/InlineSpiller.cpp
@@ -928,6 +928,39 @@ foldMemoryOperand(ArrayRef<std::pair<MachineInstr *, unsigned>> Ops,
   // 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();
+    }
+  } else if (MI->peekDebugInstrNum()) {
+    // This is a debug-labelled instruction, but the operand being folded isn't
+    // at operand zero. Most likely this means it's a load being folded in.
+    // Substitute any register defs from operand zero up to the one being
+    // folded -- past that point, we don't know what the new operand indexes
+    // will be.
+    MF.substituteDebugValuesForInst(*MI, *FoldMI, Ops[0].second);
+  }
+
   MI->eraseFromParent();
 
   // Insert any new instructions other than FoldMI into the LIS maps.

diff  --git a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
index dda9b876a7f38..ab1100f56160e 100644
--- a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
+++ b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
@@ -884,6 +884,27 @@ InstrRefBasedLDV::extractSpillBaseRegAndOffset(const MachineInstr &MI) {
   return MTracker->getOrTrackSpillLoc({Reg, Offset});
 }
 
+Optional<LocIdx> 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 @@ bool InstrRefBasedLDV::transferDebugInstrRef(MachineInstr &MI,
     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<LocIdx> 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 @@ void InstrRefBasedLDV::transferRegisterDef(MachineInstr &MI) {
   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 @@ void InstrRefBasedLDV::transferRegisterDef(MachineInstr &MI) {
       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 @@ bool InstrRefBasedLDV::isSpillInstruction(const MachineInstr &MI,
   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 @@ bool InstrRefBasedLDV::transferSpillOrRestoreInst(MachineInstr &MI) {
 
   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.

diff  --git a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h
index 48a03f83553e1..c9e44c9449fac 100644
--- a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h
+++ b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h
@@ -984,6 +984,21 @@ class InstrRefBasedLDV : public LDVImpl {
   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<LocIdx> findLocationForMemOperand(const MachineInstr &MI);
 };
 
 } // namespace LiveDebugValues

diff  --git a/llvm/lib/CodeGen/MachineFunction.cpp b/llvm/lib/CodeGen/MachineFunction.cpp
index 4a838b82fb845..d8f786e9cae0e 100644
--- a/llvm/lib/CodeGen/MachineFunction.cpp
+++ b/llvm/lib/CodeGen/MachineFunction.cpp
@@ -974,6 +974,9 @@ void MachineFunction::makeDebugValueSubstitution(DebugInstrOperandPair A,
                                                  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 @@ bool MachineFunction::useDebugInstrRef() const {
   return false;
 }
 
+// Use one million as a high / reserved number.
+const unsigned MachineFunction::DebugOperandMemNumber = 1000000;
+
 /// \}
 
 //===----------------------------------------------------------------------===//

diff  --git a/llvm/test/DebugInfo/MIR/InstrRef/memory-operand-folding-tieddef.mir b/llvm/test/DebugInfo/MIR/InstrRef/memory-operand-folding-tieddef.mir
new file mode 100644
index 0000000000000..76bd8e239de4f
--- /dev/null
+++ b/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 
diff erent 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
+...

diff  --git a/llvm/test/DebugInfo/MIR/InstrRef/memory-operand-folding.mir b/llvm/test/DebugInfo/MIR/InstrRef/memory-operand-folding.mir
new file mode 100644
index 0000000000000..3a7eaa1fbb5e8
--- /dev/null
+++ b/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
+
+...

diff  --git a/llvm/test/DebugInfo/MIR/InstrRef/memory-operand-load-folding.mir b/llvm/test/DebugInfo/MIR/InstrRef/memory-operand-load-folding.mir
new file mode 100644
index 0000000000000..981de3d0279ec
--- /dev/null
+++ b/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
+
+...

diff  --git a/llvm/test/DebugInfo/MIR/InstrRef/memory-operand-tracking.mir b/llvm/test/DebugInfo/MIR/InstrRef/memory-operand-tracking.mir
new file mode 100644
index 0000000000000..861647fd7966e
--- /dev/null
+++ b/llvm/test/DebugInfo/MIR/InstrRef/memory-operand-tracking.mir
@@ -0,0 +1,82 @@
+# 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 }
+- { srcinst: 4, srcop: 0, dstinst: 5, 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
+    ;; Test that the old value (from the DBG_PHI) is not tracked anywhere. It
+    ;; should not be considered as being on the stack any more.
+    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
+
+    ;; Store another debug-labelled value to the stack,
+    INC32m $rsp, 1, $noreg, 4, $noreg, implicit-def dead $eflags, debug-instr-number 5, debug-location !DILocation(line: 0, scope: !7) :: (store (s32) into %stack.0)
+    ;; Point the variable at that value.
+    DBG_INSTR_REF 4, 0, !11, !DIExpression(), debug-location !12
+    ; CHECK:      DBG_INSTR_REF 4, 0,
+    ; CHECK-NEXT: DBG_VALUE $rsp
+    ;; Overwrite the stack: LiveDebugValues should explicitly undef the stack
+    ;; location with DBG_VALUE $noreg, as DbgEntityHistoryCalculator doesn't
+    ;; look at the stack.
+    INC32m $rsp, 1, $noreg, 4, $noreg, implicit-def dead $eflags, debug-location !DILocation(line: 0, scope: !7) :: (store (s32) into %stack.0)
+    ; CHECK:      INC32m $rsp
+    ; CHECK-NEXT: DBG_VALUE $noreg
+
+    $rax = MOV64rm $rsp, 1, $noreg, 8, $noreg :: (load 8 from %stack.0)
+    RETQ $rax, debug-location !12
+...


        


More information about the llvm-commits mailing list