[llvm] [CodeGen] Eliminate redundant spills at control-flow joins (PR #226852)

via llvm-commits llvm-commits at lists.llvm.org
Sun Sep 27 17:14:52 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-amdgpu

Author: Yaxun (Sam) Liu (yxsamliu)

<details>
<summary>Changes</summary>

A join can spill a value that an incoming path just reloaded from the
same slot. For a reload used only by that spill:

  left:  v = reload slot  -> left:
  right: v = ...          -> right: v = ...; spill v, slot
  join:  spill v, slot    -> join:

Move the spill to the incoming path that still needs it and remove the
dead reload. If both paths already have the value in the slot, remove
the join spill without inserting another store.


---

Patch is 75.18 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/226852.diff


10 Files Affected:

- (modified) llvm/include/llvm/CodeGen/LiveDebugVariables.h (+7-2) 
- (modified) llvm/include/llvm/CodeGen/Spiller.h (+2) 
- (modified) llvm/lib/CodeGen/InlineSpiller.cpp (+278-1) 
- (modified) llvm/lib/CodeGen/LiveDebugVariables.cpp (+84-5) 
- (modified) llvm/lib/CodeGen/RegAllocBasic.cpp (+3-2) 
- (modified) llvm/lib/CodeGen/RegAllocGreedy.cpp (+2-2) 
- (added) llvm/test/CodeGen/AMDGPU/inline-spiller-join-cleanup-debug.mir (+196) 
- (added) llvm/test/CodeGen/AMDGPU/inline-spiller-join-cleanup.mir (+643) 
- (modified) llvm/unittests/CodeGen/CMakeLists.txt (+1) 
- (added) llvm/unittests/CodeGen/LiveDebugVariablesTest.cpp (+124) 


``````````diff
diff --git a/llvm/include/llvm/CodeGen/LiveDebugVariables.h b/llvm/include/llvm/CodeGen/LiveDebugVariables.h
index d9d2a2f6a08e6..232baf1c6170f 100644
--- a/llvm/include/llvm/CodeGen/LiveDebugVariables.h
+++ b/llvm/include/llvm/CodeGen/LiveDebugVariables.h
@@ -42,11 +42,16 @@ class LiveDebugVariables {
 
   LLVM_ABI void analyze(MachineFunction &MF, LiveIntervals *LIS);
   /// splitRegister - Move any user variables in OldReg to the live ranges in
-  /// NewRegs where they are live. Mark the values as unavailable where no new
-  /// register is live.
+  /// NewRegs where they are live. Keep other locations in OldReg for spill-slot
+  /// rewriting. NewRegs must not include OldReg.
   LLVM_ABI void splitRegister(Register OldReg, ArrayRef<Register> NewRegs,
                               LiveIntervals &LIS);
 
+  /// Mark locations outside Reg's remaining live interval as unavailable after
+  /// deleting definitions. Call splitRegister first for any split components.
+  /// Reg must not describe a value kept in a spill slot.
+  LLVM_ABI void shrinkRegister(Register Reg);
+
   /// emitDebugValues - Emit new DBG_VALUE instructions reflecting the changes
   /// that happened during register allocation.
   /// @param VRM Rename virtual registers according to map.
diff --git a/llvm/include/llvm/CodeGen/Spiller.h b/llvm/include/llvm/CodeGen/Spiller.h
index 5434801727454..207b9f935ceff 100644
--- a/llvm/include/llvm/CodeGen/Spiller.h
+++ b/llvm/include/llvm/CodeGen/Spiller.h
@@ -20,6 +20,7 @@ class MachineFunctionPass;
 class VirtRegMap;
 class VirtRegAuxInfo;
 class LiveIntervals;
+class LiveDebugVariables;
 class LiveRegMatrix;
 class LiveStacks;
 class MachineDominatorTree;
@@ -53,6 +54,7 @@ class LLVM_ABI Spiller {
     LiveStacks &LSS;
     MachineDominatorTree &MDT;
     const MachineBlockFrequencyInfo &MBFI;
+    LiveDebugVariables *DebugVars = nullptr;
   };
 };
 
diff --git a/llvm/lib/CodeGen/InlineSpiller.cpp b/llvm/lib/CodeGen/InlineSpiller.cpp
index f3682a2e24808..619a49546f33e 100644
--- a/llvm/lib/CodeGen/InlineSpiller.cpp
+++ b/llvm/lib/CodeGen/InlineSpiller.cpp
@@ -21,6 +21,7 @@
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/Statistic.h"
+#include "llvm/CodeGen/LiveDebugVariables.h"
 #include "llvm/CodeGen/LiveInterval.h"
 #include "llvm/CodeGen/LiveIntervals.h"
 #include "llvm/CodeGen/LiveRangeEdit.h"
@@ -29,12 +30,15 @@
 #include "llvm/CodeGen/MachineBasicBlock.h"
 #include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
 #include "llvm/CodeGen/MachineDominators.h"
+#include "llvm/CodeGen/MachineFrameInfo.h"
 #include "llvm/CodeGen/MachineFunction.h"
 #include "llvm/CodeGen/MachineInstr.h"
 #include "llvm/CodeGen/MachineInstrBuilder.h"
 #include "llvm/CodeGen/MachineInstrBundle.h"
+#include "llvm/CodeGen/MachineMemOperand.h"
 #include "llvm/CodeGen/MachineOperand.h"
 #include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/CodeGen/PseudoSourceValue.h"
 #include "llvm/CodeGen/SlotIndexes.h"
 #include "llvm/CodeGen/Spiller.h"
 #include "llvm/CodeGen/StackMaps.h"
@@ -75,7 +79,21 @@ RestrictStatepointRemat("restrict-statepoint-remat",
                        cl::init(false), cl::Hidden,
                        cl::desc("Restrict remat for statepoint operands"));
 
+static cl::opt<bool> EnableSpillJoinCleanup(
+    "enable-spill-join-cleanup", cl::Hidden, cl::init(true),
+    cl::desc("Eliminate partially redundant stores in the inline spiller"));
+
+STATISTIC(NumJoinSpillsRemoved, "Number of spill stores removed from joins");
+STATISTIC(NumJoinReloadsRemoved, "Number of dead join spill reloads removed");
+
 namespace {
+struct SpillAccess {
+  Register Reg;
+  int FI;
+  TypeSize Bytes = TypeSize::getZero();
+  unsigned DataOp;
+};
+
 class HoistSpillHelper : private LiveRangeEdit::Delegate {
   MachineFunction &MF;
   LiveIntervals &LIS;
@@ -87,6 +105,8 @@ class HoistSpillHelper : private LiveRangeEdit::Delegate {
   const TargetRegisterInfo &TRI;
   const MachineBlockFrequencyInfo &MBFI;
   LiveRegMatrix *Matrix;
+  LiveDebugVariables *DebugVars;
+  SmallSetVector<Register, 8> ShrunkRegs;
 
   InsertPointAnalysis IPA;
 
@@ -128,13 +148,25 @@ class HoistSpillHelper : private LiveRangeEdit::Delegate {
                       SmallVectorImpl<MachineInstr *> &SpillsToRm,
                       DenseMap<MachineBasicBlock *, Register> &SpillsToIns);
 
+  bool getSpillAccess(const MachineInstr &MI, bool IsLoad,
+                      SpillAccess &Access) const;
+  bool clobbersSpillInputs(const MachineInstr &MI,
+                           const MachineInstr &Store) const;
+  MachineInstr *findSpillReload(MachineBasicBlock &Pred,
+                                const MachineInstr &Store,
+                                const SpillAccess &Access) const;
+  bool canSpillBeforeTerminator(MachineBasicBlock &Pred,
+                                const MachineInstr &Store, Register Reg) const;
+  void eliminateJoinSpills(LiveRangeEdit &Edit);
+
 public:
   HoistSpillHelper(const Spiller::RequiredAnalyses &Analyses,
                    MachineFunction &mf, VirtRegMap &vrm, LiveRegMatrix *matrix)
       : MF(mf), LIS(Analyses.LIS), LSS(Analyses.LSS), MDT(Analyses.MDT),
         VRM(vrm), MRI(mf.getRegInfo()), TII(*mf.getSubtarget().getInstrInfo()),
         TRI(*mf.getSubtarget().getRegisterInfo()), MBFI(Analyses.MBFI),
-        Matrix(matrix), IPA(LIS, mf.getNumBlockIDs()) {}
+        Matrix(matrix), DebugVars(Analyses.DebugVars),
+        IPA(LIS, mf.getNumBlockIDs()) {}
 
   void addToMergeableSpills(MachineInstr &Spill, int StackSlot,
                             Register Original);
@@ -1793,6 +1825,236 @@ void HoistSpillHelper::runHoistSpills(
   }
 }
 
+bool HoistSpillHelper::getSpillAccess(const MachineInstr &MI, bool IsLoad,
+                                      SpillAccess &Access) const {
+  if (MI.isBundled() || MI.getFlag(MachineInstr::FrameSetup) ||
+      MI.getFlag(MachineInstr::FrameDestroy) || MI.hasOrderedMemoryRef() ||
+      MI.memoperands().size() != 1 || MI.mayLoad() != IsLoad ||
+      MI.mayStore() == IsLoad)
+    return false;
+
+  // The target queries certify a pure stack access, even when a spill pseudo
+  // carries a conservative unmodeled-side-effects flag.
+  Access.Bytes = TypeSize::getZero();
+  Access.Reg = IsLoad ? TII.isLoadFromStackSlot(MI, Access.FI, Access.Bytes)
+                      : TII.isStoreToStackSlot(MI, Access.FI, Access.Bytes);
+  if (!Access.Reg || !Access.Reg.isVirtual() || !Access.Bytes ||
+      Access.Bytes.isScalable() || Access.FI < 0 ||
+      !MF.getFrameInfo().isSpillSlotObjectIndex(Access.FI))
+    return false;
+
+  const MachineMemOperand &MMO = **MI.memoperands_begin();
+  const auto *PSV =
+      dyn_cast_or_null<FixedStackPseudoSourceValue>(MMO.getPseudoValue());
+  if (!PSV || PSV->getFrameIndex() != Access.FI || MMO.getOffset() != 0 ||
+      MMO.getSize() != LocationSize::precise(Access.Bytes))
+    return false;
+
+  const TargetRegisterClass *RC = MRI.getRegClass(Access.Reg);
+  if (TRI.getRegSizeInBits(*RC) != Access.Bytes * 8)
+    return false;
+
+  // Require a complete value and no hidden definitions. Comparing the other
+  // operands below also covers target addressing and predication operands.
+  bool FoundData = false;
+  for (unsigned I = 0, E = MI.getNumOperands(); I != E; ++I) {
+    const MachineOperand &MO = MI.getOperand(I);
+    if (MO.isRegMask())
+      return false;
+    if (!MO.isReg())
+      continue;
+    if (MO.getSubReg() || MO.isUndef() || MO.isInternalRead())
+      return false;
+    if (MO.getReg() == Access.Reg) {
+      if (FoundData || MO.isDef() != IsLoad || MO.isImplicit())
+        return false;
+      FoundData = true;
+      Access.DataOp = I;
+    } else if (MO.isDef() || (MO.getReg() && (!MO.getReg().isPhysical() ||
+                                              !MRI.isReserved(MO.getReg())))) {
+      return false;
+    }
+  }
+  return FoundData;
+}
+
+bool HoistSpillHelper::clobbersSpillInputs(const MachineInstr &MI,
+                                           const MachineInstr &Store) const {
+  for (const MachineOperand &MO : Store.operands()) {
+    if (MO.isReg() && MO.getReg() && MI.modifiesRegister(MO.getReg(), &TRI))
+      return true;
+  }
+  return false;
+}
+
+MachineInstr *
+HoistSpillHelper::findSpillReload(MachineBasicBlock &Pred,
+                                  const MachineInstr &Store,
+                                  const SpillAccess &Access) const {
+  Register Reg = Access.Reg;
+  unsigned Count = 0;
+  for (MachineInstr &MI : llvm::reverse(Pred)) {
+    if (++Count > 64 || MI.isBundled() || MI.isCall() || MI.isInlineAsm() ||
+        MI.getFlag(MachineInstr::FrameSetup) ||
+        MI.getFlag(MachineInstr::FrameDestroy) || MI.mayStore() ||
+        MI.hasOrderedMemoryRef())
+      return nullptr;
+
+    if (MI.isFullCopy() && MI.getNumOperands() == 2 &&
+        MI.getOperand(0).getReg() == Reg &&
+        MI.getOperand(1).getReg().isVirtual() && !MI.getOperand(1).isUndef() &&
+        TRI.getRegSizeInBits(*MRI.getRegClass(MI.getOperand(1).getReg())) ==
+            Access.Bytes * 8) {
+      Reg = MI.getOperand(1).getReg();
+      continue;
+    }
+
+    SpillAccess Load;
+    bool IsReload = getSpillAccess(MI, true, Load);
+    if (MI.hasUnmodeledSideEffects() && !IsReload)
+      return nullptr;
+    if (IsReload && Load.Reg == Reg && Load.FI == Access.FI &&
+        Load.Bytes == Access.Bytes &&
+        MI.getNumOperands() == Store.getNumOperands()) {
+      SmallVector<const MachineOperand *, 8> LoadOps, StoreOps;
+      for (unsigned I = 0, E = MI.getNumOperands(); I != E; ++I) {
+        if (I != Load.DataOp)
+          LoadOps.push_back(&MI.getOperand(I));
+        if (I != Access.DataOp)
+          StoreOps.push_back(&Store.getOperand(I));
+      }
+      if (llvm::all_of(llvm::zip(LoadOps, StoreOps), [](const auto &Ops) {
+            return std::get<0>(Ops)->isIdenticalTo(*std::get<1>(Ops));
+          }))
+        return &MI;
+    }
+    if (MI.modifiesRegister(Reg, &TRI) || clobbersSpillInputs(MI, Store))
+      return nullptr;
+  }
+  return nullptr;
+}
+
+bool HoistSpillHelper::canSpillBeforeTerminator(MachineBasicBlock &Pred,
+                                                const MachineInstr &Store,
+                                                Register Reg) const {
+  auto Insert = Pred.getFirstTerminator();
+  SlotIndex End = LIS.getMBBEndIdx(&Pred);
+  SlotIndex Idx = Insert == Pred.end() ? End.getPrevSlot()
+                                       : LIS.getInstructionIndex(*Insert);
+  const LiveInterval &LI = LIS.getInterval(Reg);
+  if (!LI.liveAt(Idx) || LI.getVNInfoAt(Idx) != LI.getVNInfoBefore(End))
+    return false;
+  if (LI.hasSubRanges()) {
+    LaneBitmask Covered;
+    for (const LiveInterval::SubRange &SR : LI.subranges()) {
+      if (!SR.liveAt(Idx) || SR.getVNInfoAt(Idx) != SR.getVNInfoBefore(End))
+        return false;
+      Covered |= SR.LaneMask;
+    }
+    if (Covered != MRI.getMaxLaneMaskForVReg(Reg))
+      return false;
+  }
+  for (const MachineInstr &MI : make_range(Insert, Pred.end()))
+    if (!MI.isBranch() || MI.isBundled() || MI.hasUnmodeledSideEffects() ||
+        MI.mayLoadOrStore() || clobbersSpillInputs(MI, Store))
+      return false;
+  return true;
+}
+
+/// Distribute a join spill onto the incoming path that still needs it. The
+/// other path already has the value in the slot, so its reload may become dead.
+void HoistSpillHelper::eliminateJoinSpills(LiveRangeEdit &Edit) {
+  // Allocators using this cleanup must maintain the collected debug locations.
+  if (!DebugVars || MF.exposesReturnsTwice())
+    return;
+
+  // The dominance-based hoisting phase is finished. Do not retain its pointers
+  // while dead-definition elimination deletes or rewrites instructions.
+  MergeableSpills.clear();
+  for (MachineBasicBlock &MBB : MF) {
+    if (MBB.pred_size() != 2 || MBB.isEHPad() || MBB.isEHScopeReturnBlock() ||
+        llvm::any_of(MBB.predecessors(), [&](const auto *Pred) {
+          return Pred == &MBB || Pred->succ_size() != 1 || Pred->isEHPad();
+        }))
+      continue;
+
+    for (unsigned Count = 0; Count != 16 && !MBB.empty(); ++Count) {
+      MachineInstr &Store = MBB.front();
+      SpillAccess Access;
+      if (!getSpillAccess(Store, false, Access) ||
+          !LSS.hasInterval(Access.FI) || !LIS.hasInterval(Access.Reg) ||
+          (!VRM.hasPhys(Access.Reg) &&
+           !PendingReassignments.contains(Access.Reg)))
+        break;
+      SlotIndex StoreIdx = LIS.getInstructionIndex(Store);
+      const LiveInterval &LI = LIS.getInterval(Access.Reg);
+      VNInfo *VNI = LI.getVNInfoAt(StoreIdx);
+      if (!VNI || !VNI->isPHIDef() || VNI->def != LIS.getMBBStartIdx(&MBB))
+        break;
+
+      SmallVector<SlotIndex, 2> Reloads;
+      MachineBasicBlock *NeedsStore = nullptr;
+      bool Legal = true;
+      for (MachineBasicBlock *Pred : MBB.predecessors()) {
+        if (!canSpillBeforeTerminator(*Pred, Store, Access.Reg)) {
+          Legal = false;
+          break;
+        }
+        if (MachineInstr *Load = findSpillReload(*Pred, Store, Access))
+          Reloads.push_back(LIS.getInstructionIndex(*Load));
+        else
+          NeedsStore = Pred;
+      }
+      if (!Legal || Reloads.empty())
+        break;
+
+      LiveInterval &StackLI = LSS.getInterval(Access.FI);
+      VNInfo *StackVNI = StackLI.getValNumInfo(0);
+      // Preserve the slot through the join after removing its store, including
+      // gaps where a predecessor's reload used to end the slot's lifetime.
+      StackLI.addSegment(LiveRange::Segment(LIS.getMBBStartIdx(&MBB),
+                                            StoreIdx.getRegSlot(), StackVNI));
+      for (SlotIndex Idx : Reloads)
+        StackLI.addSegment(LiveRange::Segment(
+            Idx.getRegSlot(),
+            LIS.getMBBEndIdx(LIS.getInstructionFromIndex(Idx)->getParent()),
+            StackVNI));
+      if (NeedsStore) {
+        MachineInstr *Clone = MF.CloneMachineInstr(&Store);
+        Clone->clearKillInfo();
+        NeedsStore->insert(NeedsStore->getFirstTerminator(), Clone);
+        SlotIndex NewIdx = LIS.InsertMachineInstrInMaps(*Clone);
+        // The store starts the slot's lifetime on this predecessor. Its old
+        // live range need not cover the incoming value of the register.
+        StackLI.addSegment(LiveRange::Segment(
+            NewIdx.getRegSlot(), LIS.getMBBEndIdx(NeedsStore), StackVNI));
+      }
+      LLVM_DEBUG(dbgs() << "Removing join spill: " << Store);
+      Store.setDesc(TII.get(TargetOpcode::KILL));
+      SmallVector<MachineInstr *, 2> Dead{&Store};
+      Edit.eliminateDeadDefs(Dead, {});
+      ++NumJoinSpillsRemoved;
+
+      for (SlotIndex Idx : Reloads) {
+        MachineInstr *Load = LIS.getInstructionFromIndex(Idx);
+        if (!Load) {
+          ++NumJoinReloadsRemoved;
+          continue;
+        }
+        if (!Load->allDefsAreDead())
+          continue;
+        // Target-certified pure reload pseudos can carry conservative side
+        // effect flags. KILL lets LiveRangeEdit remove their dead definitions.
+        LLVM_DEBUG(dbgs() << "Removing join reload: " << *Load);
+        Load->setDesc(TII.get(TargetOpcode::KILL));
+        Dead.push_back(Load);
+        Edit.eliminateDeadDefs(Dead, {});
+        ++NumJoinReloadsRemoved;
+      }
+    }
+  }
+}
+
 /// For spills with equal values, remove redundant spills and hoist those left
 /// to less hot spots.
 ///
@@ -1887,6 +2149,9 @@ void HoistSpillHelper::hoistAllSpills() {
     Edit.eliminateDeadDefs(SpillsToRm, {});
   }
 
+  if (EnableSpillJoinCleanup)
+    eliminateJoinSpills(Edit);
+
   // Flush vregs that were unassigned from the matrix during shrinking but
   // were not split (so LRE_DidCloneVirtReg never re-assigned them).
   for (auto &[VReg, PhysReg] : PendingReassignments) {
@@ -1895,6 +2160,10 @@ void HoistSpillHelper::hoistAllSpills() {
     Matrix->assign(LIS.getInterval(VReg), PhysReg);
   }
   PendingReassignments.clear();
+
+  for (Register Reg : ShrunkRegs)
+    DebugVars->shrinkRegister(Reg);
+  ShrunkRegs.clear();
 }
 
 /// Called when a virtual register's live interval is about to be shrunk.
@@ -1902,6 +2171,9 @@ void HoistSpillHelper::hoistAllSpills() {
 /// later LRE_DidCloneVirtReg or by hoistAllSpills' flush, and stash the
 /// physreg in PendingReassignments since the unassign clears VRM.
 void HoistSpillHelper::LRE_WillShrinkVirtReg(Register VirtReg) {
+  if (EnableSpillJoinCleanup && DebugVars &&
+      (VRM.hasPhys(VirtReg) || PendingReassignments.contains(VirtReg)))
+    ShrunkRegs.insert(VirtReg);
   if (!Matrix || !VRM.hasPhys(VirtReg) || !LIS.hasInterval(VirtReg))
     return;
 
@@ -1915,6 +2187,9 @@ void HoistSpillHelper::LRE_WillShrinkVirtReg(Register VirtReg) {
 /// Forcibly remove the register from LiveRegMatrix before it's deleted,
 /// preventing dangling pointers.
 bool HoistSpillHelper::LRE_CanEraseVirtReg(Register VirtReg) {
+  if (EnableSpillJoinCleanup && DebugVars &&
+      (VRM.hasPhys(VirtReg) || PendingReassignments.contains(VirtReg)))
+    ShrunkRegs.insert(VirtReg);
   PendingReassignments.erase(VirtReg);
   if (Matrix && VRM.hasPhys(VirtReg)) {
     const LiveInterval &LI = LIS.getInterval(VirtReg);
@@ -1926,6 +2201,8 @@ bool HoistSpillHelper::LRE_CanEraseVirtReg(Register VirtReg) {
 /// For VirtReg clone, the \p New register should have the same physreg or
 /// stackslot as the \p old register.
 void HoistSpillHelper::LRE_DidCloneVirtReg(Register New, Register Old) {
+  if (EnableSpillJoinCleanup && DebugVars)
+    DebugVars->splitRegister(Old, {New}, LIS);
   // New is freshly created by LiveRangeEdit::eliminateDeadDefs and its interval
   // is guaranteed to exist on every path below.
   assert(LIS.hasInterval(New) && "Cloned vreg without live interval");
diff --git a/llvm/lib/CodeGen/LiveDebugVariables.cpp b/llvm/lib/CodeGen/LiveDebugVariables.cpp
index b4347cad2c1b2..e954f9db97cb0 100644
--- a/llvm/lib/CodeGen/LiveDebugVariables.cpp
+++ b/llvm/lib/CodeGen/LiveDebugVariables.cpp
@@ -61,6 +61,7 @@
 #include <map>
 #include <memory>
 #include <optional>
+#include <tuple>
 #include <utility>
 
 using namespace llvm;
@@ -472,6 +473,8 @@ class UserValue {
   bool splitRegister(Register OldReg, ArrayRef<Register> NewRegs,
                      LiveIntervals &LIS);
 
+  void shrinkRegister(Register Reg, const LiveInterval *LI);
+
   /// Rewrite virtual register locations according to the provided virtual
   /// register map. Record the stack slot offsets for the locations that
   /// were spilled.
@@ -665,6 +668,8 @@ class LiveDebugVariables::LDVImpl {
   /// Replace all references to OldReg with NewRegs.
   void splitRegister(Register OldReg, ArrayRef<Register> NewRegs);
 
+  void shrinkRegister(Register Reg);
+
   /// Recreate DBG_VALUE instruction from data structures.
   void emitDebugValues(VirtRegMap *VRM);
 
@@ -1484,6 +1489,80 @@ UserValue::splitRegister(Register OldReg, ArrayRef<Register> NewRegs,
   return DidChange;
 }
 
+void UserValue::shrinkRegister(Register Reg, const LiveInterval *LI) {
+  auto IsRegLocation = [&](unsigned LocNo) {
+    return LocNo != UndefLocNo && locations[LocNo].isReg() &&
+           locations[LocNo].getReg() == Reg;
+  };
+  bool Changed = false;
+  for (LocMap::iterator It = locInts.begin(); It.valid();) {
+    SlotIndex Start = It.start(), Stop = It.stop();
+    auto Seg = LI ? LI->find(Start) : LiveRange::const_iterator();
+    if (!llvm::any_of(It.value().loc_nos(), IsRegLocation) ||
+        (LI && Seg != LI->end() && Seg->start <= Start && Seg->end >= Stop)) {
+      ++It;
+      continue;
+    }
+
+    DbgVariableValue Value = It.value();
+    SmallVector<unsigned, 4> LocNos;
+    for (unsigned LocNo : Value.loc_nos())
+      LocNos.push_back(IsRegLocation(LocNo) ? UndefLocNo : LocNo);
+    DbgVariableValue Unavailable(LocNos, Value.getWasIndirect(),
+                                 Value.getWasList(), *Value.getExpression());
+
+    // Replace only affected intervals. All locations for Reg share these gaps,
+    // including multiple subregister operands in a DBG_VALUE_LIST.
+    It.erase();
+    if (LI) {
+      for (; Seg != LI->end() && Seg->start < Stop; ++Seg) {
+        if (Start < Seg->start) {
+          locInts.insert(Start, Seg->start, Unavailable);
+          Start = Seg->start;
+        }
+        SlotIndex End = std::min(Stop, Seg->end);
+        locInt...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/226852


More information about the llvm-commits mailing list