[llvm] [RegisterCoalescer] Materialize undef subrange PHI inputs (PR #204867)

Lukas Sommer via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 29 02:07:17 PDT 2026


================
@@ -3692,6 +3833,682 @@ bool RegisterCoalescer::isHighCostLiveInterval(LiveInterval &LI) {
   return true;
 }
 
+static LaneBitmask
+getDefMaskForUndefPHIRepairSubReg(const TargetRegisterInfo &TRI,
+                                  LaneBitmask MaxMask, unsigned SubReg) {
+  // If no subregister is used, the mask is the full mask of the register class
+  // as given by MaxMask
+  if (SubReg == 0)
+    return MaxMask;
+
+  // Otherwise ask the target for the lane mask for the given subregister and
+  // make sure it is a subset of the maximum mask for the register class
+  LaneBitmask DefMask = TRI.getSubRegIndexLaneMask(SubReg);
+  return DefMask & MaxMask;
+}
+
+static bool getUndefPHIRepairSubRegIndexes(const TargetRegisterInfo &TRI,
+                                           const TargetRegisterClass *RC,
+                                           LaneBitmask MissingMask,
+                                           LaneBitmask AllowedMask,
+                                           SmallVectorImpl<unsigned> &Indexes) {
+  assert((MissingMask & ~AllowedMask).none() &&
+         "missing lanes must be legal to repair");
+
+  // Find the best subregister indexes to cover the missing lanes where each
+  // index covers as many missing lanes as possible, and as few extra lanes as
+  // possible. When legal single-lane indexes exist, this can fall back to one
+  // index per missing lane.
+  LaneBitmask LanesLeft = MissingMask;
+  while (LanesLeft.any()) {
+    unsigned BestIdx = 0;
+    unsigned BestCover = 0;
+    unsigned BestExtra = std::numeric_limits<unsigned>::max();
+
+    for (unsigned Idx = 1, E = TRI.getNumSubRegIndices(); Idx < E; ++Idx) {
+      // Skip subregister indexes that don't belong to the register class
+      if (TRI.getSubClassWithSubReg(RC, Idx) != RC)
+        continue;
+
+      LaneBitmask SubRegMask = TRI.getSubRegIndexLaneMask(Idx);
+      LaneBitmask Covered = SubRegMask & LanesLeft;
+
+      // The subregister doesn't cover any of the lanes we're still missing
+      if (Covered.none())
+        continue;
+
+      // The chosen subregister would clobber lanes that are not allowed to be
+      // defined by the repair
+      if ((SubRegMask & ~AllowedMask).any())
+        continue;
+
+      // Skip subregisters that overlap with missing lanes we have already
+      // covered with other subregisters
+      if ((SubRegMask & (MissingMask & ~LanesLeft)).any())
+        continue;
+
+      unsigned Cover = Covered.getNumLanes();
+      unsigned Extra = (SubRegMask & ~LanesLeft).getNumLanes();
+      if (Cover > BestCover || (Cover == BestCover && Extra < BestExtra)) {
+        BestIdx = Idx;
+        BestCover = Cover;
+        BestExtra = Extra;
+      }
+    }
+
+    if (BestIdx == 0)
+      return false;
+
+    Indexes.push_back(BestIdx);
+    LanesLeft &= ~TRI.getSubRegIndexLaneMask(BestIdx);
+  }
+
+  return true;
+}
+
+static void copyLiveInterval(LiveInterval &Dst, const LiveInterval &Src,
+                             BumpPtrAllocator &Allocator) {
+  Dst.assign(Src, Allocator);
+  for (const LiveInterval::SubRange &SR : Src.subranges())
+    Dst.createSubRangeFrom(Allocator, SR.LaneMask, SR);
+}
+
+static void addUndefPHIRepair(SmallVectorImpl<UndefPHIRepair> &Repairs,
+                              MachineBasicBlock *PredMBB,
+                              MachineBasicBlock *SuccMBB,
+                              LaneBitmask LaneMask) {
+  // A predecessor can be missing several lanes for the same successor PHI.
+  // Combining them lets us emit the fewest IMPLICIT_DEFs that cover the
+  // register class instead of repairing each subrange independently.
+  for (UndefPHIRepair &R : Repairs) {
+    if (R.PredMBB == PredMBB && R.SuccMBB == SuccMBB) {
+      R.LaneMask |= LaneMask;
+      return;
+    }
+  }
+  Repairs.push_back({PredMBB, SuccMBB, LaneMask});
+}
+
+static void collectUndefPHIRepairs(
+    LiveInterval &LI, const MachineRegisterInfo &MRI,
+    const SlotIndexes &Indexes, SmallVectorImpl<UndefPHIRepair> &Repairs,
+    LaneBitmask TreatLiveOutAsMissing = LaneBitmask::getNone()) {
+  LaneBitmask VRegMask = MRI.getMaxLaneMaskForVReg(LI.reg());
+
+  for (LiveInterval::SubRange &SR : LI.subranges()) {
+    if (SR.empty())
+      continue;
+
+    // Check if the lanes of this subrange are marked as undefined by a
+    // subregister read-undef definition. We can use that to determine whether
+    // a repair will really be needed further down
+    SmallVector<SlotIndex, 4> Undefs;
+    LaneBitmask UndefMask = SR.LaneMask & VRegMask;
+    if (UndefMask.any())
+      LI.computeSubRangeUndefs(Undefs, UndefMask, MRI, Indexes);
+
+    for (const VNInfo *VNI : SR.valnos) {
+      if (VNI->isUnused() || !VNI->isPHIDef())
+        continue;
+
+      MachineBasicBlock *MBB = Indexes.getMBBFromIndex(VNI->def);
+      for (MachineBasicBlock *PredMBB : MBB->predecessors()) {
+        SlotIndex PredEnd = Indexes.getMBBEndIdx(PredMBB);
+
+        // For the pre-coalescing check, treat any lane of this subrange that
+        // overlaps with TreatLiveOutAsMissing as missing
+        bool ForceMissing =
+            (SR.LaneMask & TreatLiveOutAsMissing & VRegMask).any();
+
+        // PHI subranges are live-in to the block only if the same lane is live
+        // out of each predecessor. A missing live-out lane means this edge has
+        // an undef PHI input that must be made explicit by an IMPLICIT_DEF
+        if (!ForceMissing && SR.liveAt(PredEnd.getPrevSlot()))
+          continue;
+
+        // No need to repair anything if the predecessor is dominated by all the
+        // blocks corresponding to the SlotIndexes in Undefs
+        if (!ForceMissing &&
+            LiveRangeCalc::isJointlyDominated(PredMBB, Undefs, Indexes))
+          continue;
+
+        LLVM_DEBUG({
+          if (ForceMissing)
+            dbgs() << "\t\tTreating shrink-marked PHI lanes as missing on "
+                   << printMBBReference(*PredMBB) << " -> "
+                   << printMBBReference(*MBB) << ": "
+                   << PrintLaneMask(SR.LaneMask & TreatLiveOutAsMissing &
+                                    VRegMask)
+                   << '\n';
+        });
+
+        addUndefPHIRepair(Repairs, PredMBB, MBB, SR.LaneMask);
+      }
+    }
+  }
+}
+
+static bool repairNeedsEdgeSplit(const LiveInterval &LI,
+                                 const SlotIndexes &Indexes,
+                                 const UndefPHIRepair &Repair) {
+  // No need to split if the edge recorded by Repair is the only outgoing edge
+  // from the predecessor
+  if (Repair.PredMBB->succ_size() <= 1)
+    return false;
+
+  // The predecessor has multiple successors. Inserting an IMPLICIT_DEF would
+  // affect all outgoing edges, not just the one edge whose PHI input is undef.
+  // If the main interval is live at the end of the predecessor, there is a real
+  // live out value for that vreg on some outgoing path. Require an edge split
+  // to avoid clobbering that value.
+  SlotIndex LastPredSlot = Indexes.getMBBEndIdx(Repair.PredMBB).getPrevSlot();
+  return LI.liveAt(LastPredSlot);
+}
+
+static bool containsPHIDef(const LiveRange &LR) {
+  for (const VNInfo *VNI : LR.valnos)
+    if (!VNI->isUnused() && VNI->isPHIDef())
+      return true;
+  return false;
----------------
sommerlukas wrote:

Nit: You could use `llvm::any_of` here.

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


More information about the llvm-commits mailing list