[llvm] [AMDGPU] Factor MFMA-form copy analysis into shared copy-plan analysis (PR #209438)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 14 04:00:19 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-amdgpu

Author: xgxanq

<details>
<summary>Changes</summary>



---

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


4 Files Affected:

- (modified) llvm/lib/Target/AMDGPU/GCNSchedStrategy.cpp (+561-163) 
- (modified) llvm/lib/Target/AMDGPU/GCNSchedStrategy.h (+90-5) 
- (added) llvm/test/CodeGen/AMDGPU/rewrite-mfma-form-safe-guard.mir (+919) 
- (modified) llvm/test/CodeGen/AMDGPU/sched_mfma_rewrite_copies.mir (+107-63) 


``````````diff
diff --git a/llvm/lib/Target/AMDGPU/GCNSchedStrategy.cpp b/llvm/lib/Target/AMDGPU/GCNSchedStrategy.cpp
index a4f854beaeebe..313671f83c655 100644
--- a/llvm/lib/Target/AMDGPU/GCNSchedStrategy.cpp
+++ b/llvm/lib/Target/AMDGPU/GCNSchedStrategy.cpp
@@ -35,6 +35,7 @@
 #include "llvm/CodeGen/MachineBasicBlock.h"
 #include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
 #include "llvm/CodeGen/MachineBranchProbabilityInfo.h"
+#include "llvm/CodeGen/MachineDominators.h"
 #include "llvm/CodeGen/MachineOperand.h"
 #include "llvm/CodeGen/RegisterClassInfo.h"
 #include "llvm/CodeGen/Rematerializer.h"
@@ -1315,48 +1316,108 @@ bool GCNSchedStage::initGCNSchedStage() {
   return true;
 }
 
-void RewriteMFMAFormStage::findReachingDefs(
-    MachineOperand &UseMO, LiveIntervals *LIS,
-    SmallVectorImpl<SlotIndex> &DefIdxs) {
-  MachineInstr *UseMI = UseMO.getParent();
-  LiveInterval &UseLI = LIS->getInterval(UseMO.getReg());
-  VNInfo *VNI = UseLI.getVNInfoAt(LIS->getInstructionIndex(*UseMI));
+static void collectReachingDefsInRange(const LiveRange &QR,
+                                       MachineBasicBlock *UseMBB,
+                                       SlotIndex UseIdx,
+                                       const LiveIntervals *LIS,
+                                       SmallSet<SlotIndex, 8> &ResultSet) {
+  const VNInfo *VNI = QR.getVNInfoAt(UseIdx);
+  if (!VNI)
+    return;
 
-  // If the def is not a PHI, then it must be the only reaching def.
   if (!VNI->isPHIDef()) {
-    DefIdxs.push_back(VNI->def);
+    ResultSet.insert(VNI->def);
     return;
   }
 
-  SmallPtrSet<MachineBasicBlock *, 8> Visited = {UseMI->getParent()};
+  SmallPtrSet<MachineBasicBlock *, 8> Visited;
   SmallVector<MachineBasicBlock *, 8> Worklist;
-
-  // Mark the predecessor blocks for traversal
-  for (MachineBasicBlock *PredMBB : UseMI->getParent()->predecessors()) {
-    Worklist.push_back(PredMBB);
-    Visited.insert(PredMBB);
-  }
+  for (MachineBasicBlock *PredMBB : UseMBB->predecessors())
+    if (Visited.insert(PredMBB).second)
+      Worklist.push_back(PredMBB);
 
   while (!Worklist.empty()) {
     MachineBasicBlock *CurrMBB = Worklist.pop_back_val();
+    SlotIndex CurrMBBEnd = LIS->getMBBEndIdx(CurrMBB).getPrevSlot();
+    const VNInfo *PredVNI = QR.getVNInfoAt(CurrMBBEnd);
+    if (!PredVNI)
+      continue;
 
-    SlotIndex CurrMBBEnd = LIS->getMBBEndIdx(CurrMBB);
-    VNInfo *VNI = UseLI.getVNInfoAt(CurrMBBEnd.getPrevSlot());
-
-    MachineBasicBlock *DefMBB = LIS->getMBBFromIndex(VNI->def);
-
-    // If there is a def in this block, then add it to the list. This is the
-    // reaching def of this path.
-    if (!VNI->isPHIDef()) {
-      DefIdxs.push_back(VNI->def);
+    if (!PredVNI->isPHIDef()) {
+      // Skip self-referential defs (src2 == dst): the MFMA's own result must
+      // not appear as a reaching def of its own src2 operand.
+      if (SlotIndex::isSameInstr(PredVNI->def, UseIdx))
+        continue;
+      ResultSet.insert(PredVNI->def);
       continue;
     }
 
-    for (MachineBasicBlock *PredMBB : DefMBB->predecessors()) {
+    MachineBasicBlock *DefMBB = LIS->getMBBFromIndex(PredVNI->def);
+    for (MachineBasicBlock *PredMBB : DefMBB->predecessors())
       if (Visited.insert(PredMBB).second)
         Worklist.push_back(PredMBB);
+  }
+}
+
+void RewriteMFMAFormStage::findReachingDefs(
+    MachineOperand &UseMO, LiveIntervals *LIS,
+    SmallVectorImpl<SlotIndex> &DefIdxs) {
+  MachineInstr *UseMI = UseMO.getParent();
+  Register UseReg = UseMO.getReg();
+  unsigned UseSubReg = UseMO.getSubReg();
+
+  if (!UseReg.isVirtual() || !LIS->hasInterval(UseReg))
+    return;
+
+  LiveInterval &UseLI = LIS->getInterval(UseReg);
+  SlotIndex UseIdx = LIS->getInstructionIndex(*UseMI);
+
+  // Use a set to deduplicate: a full-reg def appears in every SubRange but
+  // must be inserted into DefIdxs only once.
+  SmallSet<SlotIndex, 8> ResultSet;
+
+  if (UseLI.hasSubRanges()) {
+    const TargetRegisterInfo *TRI = DAG.MRI.getTargetRegisterInfo();
+
+    if (UseSubReg) {
+      // Find the SubRange whose LaneMask fully covers the operand's lanes.
+      // If none does (e.g. register initialised via per-lane subreg writes),
+      // fall back to all overlapping SubRanges to capture every reaching def.
+      LaneBitmask UseLanes = TRI->getSubRegIndexLaneMask(UseSubReg);
+      bool FoundFullCoverage = false;
+      for (LiveInterval::SubRange &SR : UseLI.subranges()) {
+        if ((SR.LaneMask & UseLanes) == UseLanes) {
+          collectReachingDefsInRange(SR, UseMI->getParent(), UseIdx, LIS,
+                                     ResultSet);
+          FoundFullCoverage = true;
+          break;
+        }
+      }
+      if (!FoundFullCoverage) {
+        for (LiveInterval::SubRange &SR : UseLI.subranges())
+          if ((SR.LaneMask & UseLanes).any())
+            collectReachingDefsInRange(SR, UseMI->getParent(), UseIdx, LIS,
+                                       ResultSet);
+      }
+    } else {
+      // Full-reg use: query every subrange so that partial (subreg) defs on
+      // different lanes are all captured.
+      for (LiveInterval::SubRange &SR : UseLI.subranges())
+        collectReachingDefsInRange(SR, UseMI->getParent(), UseIdx, LIS,
+                                   ResultSet);
+      // If the register has SubRanges but none covers the use point,
+      // LiveIntervals is malformed: a full-width def must appear in at least
+      // one SubRange.
+      assert(!ResultSet.empty() &&
+             "hasSubRanges() but no SubRange live at full-reg use: "
+             "LiveInterval construction is inconsistent");
     }
+  } else {
+    collectReachingDefsInRange(UseLI, UseMI->getParent(), UseIdx, LIS,
+                               ResultSet);
   }
+
+  DefIdxs.append(ResultSet.begin(), ResultSet.end());
 }
 
 void RewriteMFMAFormStage::findReachingUses(
@@ -1365,6 +1426,12 @@ void RewriteMFMAFormStage::findReachingUses(
   SlotIndex DefIdx = LIS->getInstructionIndex(*DefMI);
   for (MachineOperand &UseMO :
        DAG.MRI.use_nodbg_operands(DefMI->getOperand(0).getReg())) {
+    // Skip implicit operands: partial subreg defs carry an implicit use of the
+    // full register for RMW lane preservation, not as a real consumer of the
+    // MFMA dst value. Treating them as reaching uses inserts a spurious bridge
+    // copy before the partial def, corrupting MappedReg's live range.
+    if (UseMO.isImplicit())
+      continue;
     SmallVector<SlotIndex, 8> ReachingDefIndexes;
     findReachingDefs(UseMO, LIS, ReachingDefIndexes);
 
@@ -2290,12 +2357,29 @@ void GCNSchedStage::modifyRegionSchedule(unsigned RegionIdx,
   DAG.Regions[RegionIdx].first = MIOrder.front();
 }
 
+static unsigned getDefSubReg(const MachineInstr &MI, Register Reg) {
+  for (const MachineOperand &MO : MI.operands())
+    if (MO.isReg() && MO.isDef() && MO.getReg() == Reg)
+      return MO.getSubReg();
+  return AMDGPU::NoSubRegister;
+}
+
+/// Returns true if \p MI is a class-agnostic subreg writer (COPY or AV_MOV).
+/// These lower to v_accvgpr_write after AGPR reclassification and are legal.
+static bool isAgnosticSubregWriter(const MachineInstr *MI) {
+  if (MI->isCopy())
+    return true;
+  unsigned Opc = MI->getOpcode();
+  return Opc == AMDGPU::AV_MOV_B32_IMM_PSEUDO ||
+         Opc == AMDGPU::AV_MOV_B64_IMM_PSEUDO;
+}
+
 /// Returns true if reaching def \p RD will be in AGPR form after the rewrite
 /// and so needs no bridge copy: a candidate MFMA in \p RewriteSet, an
 /// AV_MOV_*_IMM_PSEUDO, or a copy from a candidate src2 reg in \p CandSrc2Regs.
 /// A non-candidate MFMA stays in VGPR form and still needs a bridge.
 static bool isReachingDefAGPRForm(
-    MachineInstr *RD, const SmallPtrSetImpl<MachineInstr *> &RewriteSet,
+    MachineInstr *RD, const SmallSetVector<MachineInstr *, 16> &RewriteSet,
     const DenseSet<Register> &CandSrc2Regs, const SIInstrInfo &TII) {
   if (TII.isMAI(*RD))
     return RewriteSet.contains(RD);
@@ -2309,9 +2393,14 @@ static bool isReachingDefAGPRForm(
 
 bool RewriteMFMAFormStage::hasUseRequiringVGPR(
     ArrayRef<SlotIndex> Src2ReachingDefs,
-    const SmallPtrSetImpl<MachineInstr *> &RewriteSet) {
+    const SmallSetVector<MachineInstr *, 16> &RewriteSet) {
   for (SlotIndex RDIdx : Src2ReachingDefs) {
     const MachineInstr *RD = DAG.LIS->getInstructionFromIndex(RDIdx);
+    // A rewritable candidate's dst becomes AGPR and Case 2 bridges its non-MAI
+    // uses, so it imposes no VGPR constraint on src2. Excluded MFMAs are no
+    // longer in RewriteSet.
+    if (TII->isMAI(*RD) && RewriteSet.contains(RD))
+      continue;
     SmallVector<MachineOperand *, 8> ReachingUses;
     findReachingUses(RD, DAG.LIS, ReachingUses);
     for (const MachineOperand *UseMO : ReachingUses) {
@@ -2354,116 +2443,462 @@ bool RewriteMFMAFormStage::isRewriteCandidate(MachineInstr *MI) const {
     return false;
   if (AMDGPU::getMFMASrcCVDstAGPROp(MI->getOpcode()) == -1)
     return false;
-  // Reject candidates whose users force an unavoidable bridge copy.
-  Register DstReg = MI->getOperand(0).getReg();
-  for (const MachineOperand &Use : DAG.MRI.use_nodbg_operands(DstReg)) {
-    if (!TII->isMAI(*Use.getParent()) && !Use.getParent()->isCopy())
-      return false;
-  }
   return true;
 }
 
-bool RewriteMFMAFormStage::initHeuristics(
-    std::vector<std::pair<MachineInstr *, unsigned>> &RewriteCands,
-    DenseMap<MachineBasicBlock *, std::set<Register>> &CopyForUse,
-    SmallPtrSetImpl<MachineInstr *> &CopyForDef) {
-  bool Changed = false;
+bool RewriteMFMAFormStage::hasSrc2BridgeConflict(
+    ArrayRef<SlotIndex> DefIdxs, Register Src2Reg,
+    const SmallPtrSetImpl<const MachineBasicBlock *> &BridgeBlocks,
+    const SmallPtrSetImpl<const MachineBasicBlock *> &RedefPartialBlocks)
+    const {
+  auto &MDT = DAG.LIS->getDomTree();
 
-  // Collect the candidate group, its members share AGPR-form operands
-  // post-rewrite, so reaching defs feeding any member don't need bridge copy.
-  SmallPtrSet<MachineInstr *, 16> RewriteSet;
-  DenseSet<Register> CandSrc2Regs;
-  for (MachineBasicBlock &MBB : MF) {
-    for (MachineInstr &MI : MBB) {
-      if (!isRewriteCandidate(&MI))
+  SmallVector<MachineInstr *, 8> MAIMIs;
+  SmallVector<MachineInstr *, 8> NonMAIMIs;
+  for (SlotIndex SI : DefIdxs) {
+    MachineInstr *MI = DAG.LIS->getInstructionFromIndex(SI);
+    if (TII->isMFMA(*MI))
+      MAIMIs.push_back(MI);
+    else
+      NonMAIMIs.push_back(MI);
+  }
+
+  if (NonMAIMIs.empty())
+    return false; // All reaching defs are MAI; no bridge copies needed.
+
+  // Check 1: MAI def dominates non-MAI def.  The MFMA writes all lanes (AGPR),
+  // then a non-MAI def partially overwrites some; a bridge copy there would be
+  // a read-modify-write on AGPR, which the bridge mechanism cannot implement.
+  bool SkipCheck2 = false;
+  if (!MAIMIs.empty()) {
+    for (MachineInstr *M : MAIMIs)
+      for (MachineInstr *N : NonMAIMIs)
+        if (MDT.dominates(M, N))
+          return true;
+
+    // Early-safe: if every MAI reaching def is a candidate and parallel to
+    // every non-MAI def (Check 1 ruled out MAI dominating non-MAI; here the
+    // reverse), then %MappedReg is defined on every path to a use -- a
+    // candidate MAI gives an AGPR value directly, a non-MAI def carries a
+    // bridge copy -- so Check 2 can be skipped.  Check 3 is independent and
+    // must still run.
+    bool AllParallelAndCandidates = true;
+    for (MachineInstr *M : MAIMIs) {
+      if (!isRewriteCandidate(M)) {
+        AllParallelAndCandidates = false;
+        break;
+      }
+      for (MachineInstr *N : NonMAIMIs) {
+        if (MDT.dominates(N, M)) {
+          AllParallelAndCandidates = false;
+          break;
+        }
+      }
+      if (!AllParallelAndCandidates)
+        break;
+    }
+    if (AllParallelAndCandidates)
+      SkipCheck2 = true;
+  }
+
+  // Check 2: every entry->use path of Src2Reg must hit a bridge block (a
+  // non-MAI def block where rewrite() inserts the bridge copy, or the use block
+  // itself), else %MappedReg is undefined on the bypassing path.  Bridge blocks
+  // must *jointly* cover every path (dominance is too strict), so a backward
+  // walk that reaches entry without crossing one proves an uncovered path.
+  // Every use counts, incl. a plain COPY.  BridgeBlocks: see
+  // computeExclusionSet.
+  auto CoveredByBridgeSet = [&](const MachineBasicBlock *UseBlock) {
+    if (BridgeBlocks.contains(UseBlock))
+      return true;
+    SmallPtrSet<const MachineBasicBlock *, 16> Visited;
+    SmallVector<const MachineBasicBlock *, 16> Worklist(UseBlock->pred_begin(),
+                                                        UseBlock->pred_end());
+    while (!Worklist.empty()) {
+      const MachineBasicBlock *B = Worklist.pop_back_val();
+      if (BridgeBlocks.contains(B))
+        continue; // This path is covered; do not walk past the bridge block.
+      if (B->pred_empty())
+        return false; // Reached entry without crossing a bridge block.
+      if (!Visited.insert(B).second)
         continue;
-      RewriteSet.insert(&MI);
-      MachineOperand *Src2 = TII->getNamedOperand(MI, AMDGPU::OpName::src2);
-      if (Src2 && Src2->isReg())
-        CandSrc2Regs.insert(Src2->getReg());
+      Worklist.append(B->pred_begin(), B->pred_end());
+    }
+    return true;
+  };
+
+  if (!SkipCheck2) {
+    for (const MachineOperand &UseMO : DAG.MRI.use_nodbg_operands(Src2Reg)) {
+      const MachineBasicBlock *UseBlock = UseMO.getParent()->getParent();
+      if (!CoveredByBridgeSet(UseBlock))
+        return true;
+    }
+  }
+
+  // Check 3: original-register connectivity.  rewrite() redirects Src2Reg's
+  // uses to %MappedReg, so non-AGPR-form partial subreg defs in >=2 blocks with
+  // none dominating all uses split Src2Reg's live interval into disconnected
+  // fragments.  AGPR-form defs are absent from RedefPartialBlocks (skipped by
+  // Case1), preserving the diamond joint-coverage case.
+  if (RedefPartialBlocks.size() >= 2) {
+    bool SingleDominator = false;
+    for (const MachineBasicBlock *B : RedefPartialBlocks) {
+      bool DomAllUses = true;
+      for (const MachineOperand &UseMO : DAG.MRI.use_nodbg_operands(Src2Reg)) {
+        if (!MDT.dominates(B, UseMO.getParent()->getParent())) {
+          DomAllUses = false;
+          break;
+        }
+      }
+      if (DomAllUses) {
+        SingleDominator = true;
+        break;
+      }
+    }
+    if (!SingleDominator)
+      return true;
+  }
+
+  return false;
+}
+
+void RewriteMFMAFormStage::propagateExclusionForward(
+    MachineInstr *Root, SmallPtrSetImpl<MachineInstr *> &ExcludedMFMAs) {
+  SmallVector<MachineInstr *, 8> Worklist = {Root};
+  while (!Worklist.empty()) {
+    MachineInstr *ExclMI = Worklist.pop_back_val();
+    MachineOperand &DstMO = ExclMI->getOperand(0);
+    if (!DstMO.isReg() || !DstMO.getReg().isVirtual())
+      continue;
+    Register DstReg = DstMO.getReg();
+    for (MachineOperand &UseMO : DAG.MRI.use_nodbg_operands(DstReg)) {
+      MachineInstr *UserMI = UseMO.getParent();
+      if (!isRewriteCandidate(UserMI))
+        continue;
+      MachineOperand *UserSrc2 =
+          TII->getNamedOperand(*UserMI, AMDGPU::OpName::src2);
+      if (!UserSrc2 || !UserSrc2->isReg() || UserSrc2->getReg() != DstReg)
+        continue;
+      if (ExcludedMFMAs.insert(UserMI).second) {
+        LLVM_DEBUG(dbgs() << "[initHeuristics] exclude downstream MFMA "
+                             "(src2 = excluded MFMA dst): "
+                          << *UserMI);
+        Worklist.push_back(UserMI);
+      }
+    }
+  }
+}
+
+// rewrite() reclassifies DstReg to AGPR so the MFMA writes its result directly
+// into AGPR (no post-MFMA copy).  That is legal only if every def and use of
+// DstReg across its live range supports AGPR-class operands; check that here.
+bool RewriteMFMAFormStage::hasDstSubregConflict(Register DstReg,
+                                                MachineInstr *MFMA) {
+  SmallVector<MachineOperand *, 8> DstReachingUses;
+  findReachingUses(MFMA, DAG.LIS, DstReachingUses);
+  SmallPtrSet<MachineInstr *, 8> CheckedDefs;
+  SmallVector<MachineInstr *, 4> SafeSubregDefs;
+
+  // Phase 1: classify subreg reaching defs.
+  // Non-agnostic subreg def → immediate conflict.
+  // Agnostic (COPY/AV_MOV) subreg def → defer to orphan-use check.
+  for (MachineOperand *RUOp : DstReachingUses) {
+    SmallVector<SlotIndex, 8> ReachingDefs;
+    findReachingDefs(*RUOp, DAG.LIS, ReachingDefs);
+    for (SlotIndex RDIdx : ReachingDefs) {
+      MachineInstr *RD = DAG.LIS->getInstructionFromIndex(RDIdx);
+      if (!CheckedDefs.insert(RD).second)
+        continue;
+      if (getDefSubReg(*RD, DstReg) == AMDGPU::NoSubRegister || TII->isMAI(*RD))
+        continue;
+      if (!isAgnosticSubregWriter(RD))
+        return true;
+      SafeSubregDefs.push_back(RD);
+    }
+  }
+
+  // Phase 2: agnostic subreg defs lower to v_accvgpr_write after reclassify,
+  // so their orphan uses read an AGPR sub-register.  Only COPY and AV_MOV can
+  // legally source an AGPR sub-register; anything else is a conflict.
+  for (MachineInstr *RD : SafeSubregDefs) {
+    SlotIndex RDIdx = DAG.LIS->getInstructionIndex(*RD);
+    for (MachineOperand &UseMO : DAG.MRI.use_nodbg_operands(DstReg)) {
+      if (UseMO.isImplicit() || TII->isMAI(*UseMO.getParent()))
+        continue;
+      SmallVector<SlotIndex, 8> UseReachingDefs;
+      findReachingDefs(UseMO, DAG.LIS, UseReachingDefs);
+      if (any_of(UseReachingDefs,
+                 [RDIdx](SlotIndex SI) {
+                   return SlotIndex::isSameInstr(SI, RDIdx);
+                 }) &&
+          !isAgnosticSubregWriter(UseMO.getParent()))
+        return true;
     }
   }
+  return false;
+}
 
-  // Prepare for the heuristics
-  for (MachineBasicBlock &MBB : MF) {
-    for (MachineInstr &MI : MBB) {
-      if (!isRewriteCandidate(&MI))
+SmallPtrSet<MachineInstr *, 16> RewriteMFMAFormStage::computeExclusionSet(
+    const SmallSetVector<MachineInstr *, 16> &RewriteSet) {
+  // Per-MI checks, cheapest-first: (1) hasSrc2BridgeConflict, then
+  // (2) hasDstSubregConflict (skipped when 1 already excludes).  Exclusion
+  // propagates forward (dst->src2 via propagateExclusionForward) and backward
+  // (MAI reaching-defs of a conflicted src2).
+  SmallPtrSet<MachineInstr *, 16> ExcludedMFMAs;
+
+  // Per src2 register, union reaching-def blocks across all candidates (one
+  // candidate sees only one path): non-MFMA -> Src2BridgeBlocks (Check 2), plus
+  // non-AGPR-form partial-subreg -> Src2RedefPartialBlocks (Check 3). Reaching-
+  // def (not all-def) keeps bypass-killed and entry IMPLICIT_DEFs out.
+  DenseMap<Register, SmallPtrSet<const MachineBasicBlock *, 8>>
+      Src2BridgeBlocks;
+  DenseMap<Register, SmallPtrSet<const MachineBasicBlock *, 8>>
+      Src2RedefPartialBlocks;
+
+  // Rebuild the block maps from the still-live candidates \p Active: excluding
+  // a candidate makes its src2/MAI def non-AGPR-form (isReachingDefAGPRForm
+  // reads Active), which can add new Check 3 redef sources -- so the full
+  // pre-exclusion set would miss them.
+  auto recomputeBlocks = [&](const SmallSetVector<MachineInstr *, 16> &Active) {
+    Src2BridgeBlocks.clear();
+    Src2RedefPartialBlocks.clear();
+    DenseSet<Register> CandSrc2Regs;
+    for (MachineInstr *MI : Active) {
+      MachineOperand *Src2 = TII->getNamedOperand(*MI, AMDGPU::OpName::src2);
+      if (Src2 && Src2->isReg())
+        CandSrc2Regs.insert(Src2->getReg());
+    }
+    for (MachineInstr *MI : Active) {
+      MachineOperand *Src2 = TII->getNamedOperand(*MI, AMDGPU::OpName::src2);
+      if (!Src2 || !Src2->isReg())
         continue;
+      Register Src2Reg = Src2->getReg();
+      auto &Blocks = Src2BridgeBlocks[Src2Reg];
+      auto &RedefBlocks = Src2RedefPartialBlocks[Src2Reg];
+      SmallVector<SlotIndex, 8> Src2Defs;
+      findReachingDefs(*Src2, DAG.LIS, Src2Defs);
+      for (SlotIndex SI : Src2Defs) {
+        MachineInstr *RD = DAG.LIS->getInstructionFromIndex(SI);
+        if (!RD || TII->isMFMA(*RD))
+          continue;
+        Blocks.insert(RD->getParent());
+        // Check 3 fragmentation source: non-AGPR-form partial-subreg defs only
+        // (AGPR-form skipped by Case1; full-width defs stay connected).
+        if (!isReachingDefAGPRForm(RD, Active, CandSrc2Regs, *TII) &&
+            getDefSubReg(*RD, Src2R...
[truncated]

``````````

</details>


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


More information about the llvm-commits mailing list