[llvm] [AMDGPU] Optimize PreRARematStage candidate initialization (PR #220924)

via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 9 03:43:12 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-amdgpu

Author: Frederik Harwath (frederik-h)

<details>
<summary>Changes</summary>

The computation of the LiveIn/LiveOut sets in
PreRARematStage::ScoredRemat::init showed up as a significant compile time bottleneck in code using kernels from composable_kernel.

Compute the LiveIn/LiveOut sets for all candidates in a single iteration of the all regions and DAG LiveIns/LiveOuts without iterating the candidates.

---
Full diff: https://github.com/llvm/llvm-project/pull/220924.diff


2 Files Affected:

- (modified) llvm/lib/Target/AMDGPU/GCNSchedStrategy.cpp (+39-22) 
- (modified) llvm/lib/Target/AMDGPU/GCNSchedStrategy.h (+9-5) 


``````````diff
diff --git a/llvm/lib/Target/AMDGPU/GCNSchedStrategy.cpp b/llvm/lib/Target/AMDGPU/GCNSchedStrategy.cpp
index 3dd468772e9b1..6c3a893c9c1e2 100644
--- a/llvm/lib/Target/AMDGPU/GCNSchedStrategy.cpp
+++ b/llvm/lib/Target/AMDGPU/GCNSchedStrategy.cpp
@@ -1538,7 +1538,9 @@ bool PreRARematStage::initGCNSchedStage() {
   // Collect candidates. We have more restrictions on what we can track here
   // compared to the rematerializer.
   SmallVector<ScoredRemat, 8> Candidates;
-  SmallVector<unsigned> CandidateOrder;
+  DenseMap<Register, unsigned> DefRegToCandIdx;
+  const unsigned NumRegions = DAG.Regions.size();
+
   for (unsigned RegIdx = 0, E = Remater.getNumRegs(); RegIdx < E; ++RegIdx) {
     const Rematerializer::Reg &CandReg = Remater.getReg(RegIdx);
 
@@ -1592,12 +1594,40 @@ bool PreRARematStage::initGCNSchedStage() {
                      }))
       continue;
 
-    MarkedRegs.insert(CandReg.getDefReg());
-    ScoredRemat &Cand = Candidates.emplace_back();
-    Cand.init(RegIdx, FreqInfo, Remater, DAG);
+    Register DefReg = CandReg.getDefReg();
+    MarkedRegs.insert(DefReg);
+    DefRegToCandIdx[DefReg] = Candidates.size();
+    Candidates.emplace_back(RegIdx, NumRegions);
+  }
+
+  // Initialize the LiveIn and LiveOut sets of all
+  // candidates.
+  //
+  // This used to be part of ScoredRemat::init, but this choice
+  // implied iterating all regions for each candidate. The runtime of
+  // the initialization was O(NumCandidates * NumRegions) whereas this
+  // way it is O(NumRegions + NumLiveIns + NumLiveOuts) (assuming
+  // constant time map lookups).
+  for (unsigned I = 0; I < NumRegions; ++I) {
+    for (const auto &[Reg, Mask] : DAG.LiveIns[I]) {
+      if (auto It = DefRegToCandIdx.find(Reg); It != DefRegToCandIdx.end())
+        Candidates[It->second].LiveIn.set(I);
+    }
+    for (const auto &[Reg, Mask] :
+         DAG.RegionLiveOuts.getLiveRegsForRegionIdx(I)) {
+      if (auto It = DefRegToCandIdx.find(Reg); It != DefRegToCandIdx.end())
+        Candidates[It->second].LiveOut.set(I);
+    }
+  }
+
+  // Finish initializing candidates.
+  SmallVector<unsigned> CandidateOrder;
+  for (unsigned CandIdx = 0; CandIdx < Candidates.size(); ++CandIdx) {
+    ScoredRemat &Cand = Candidates[CandIdx];
+    Cand.init(FreqInfo, Remater, DAG);
     Cand.update(TargetRegions, RPTargets, FreqInfo, !TargetOcc);
     if (!Cand.hasNullScore())
-      CandidateOrder.push_back(Candidates.size() - 1);
+      CandidateOrder.push_back(CandIdx);
   }
 
   if (TargetOcc) {
@@ -3052,36 +3082,23 @@ PreRARematStage::ScoredRemat::FreqInfo::FreqInfo(
   }
 }
 
-void PreRARematStage::ScoredRemat::init(RegisterIdx RegIdx,
-                                        const FreqInfo &Freq,
+void PreRARematStage::ScoredRemat::init(const FreqInfo &Freq,
                                         const Rematerializer &Remater,
                                         GCNScheduleDAGMILive &DAG) {
-  this->RegIdx = RegIdx;
-  const unsigned NumRegions = DAG.Regions.size();
-  LiveIn.resize(NumRegions);
-  LiveOut.resize(NumRegions);
-  Live.resize(NumRegions);
-  UnpredictableRPSave.resize(NumRegions);
-
   const Rematerializer::Reg &Reg = Remater.getReg(RegIdx);
   Register DefReg = Reg.getDefReg();
   assert(Reg.Uses.size() == 1 && "expected users in single region");
   const unsigned UseRegion = Reg.Uses.begin()->first;
 
-  // Mark regions in which the rematerializable register is live.
-  for (unsigned I = 0, E = NumRegions; I != E; ++I) {
-    if (DAG.LiveIns[I].contains(DefReg))
-      LiveIn.set(I);
-    if (DAG.RegionLiveOuts.getLiveRegsForRegionIdx(I).contains(DefReg))
-      LiveOut.set(I);
+  Live |= LiveIn;
+  Live |= LiveOut;
 
+  for (unsigned I : Live.set_bits()) {
     // If the register is both unused and live-through in the region, the
     // latter's RP is guaranteed to decrease.
     if (!LiveIn[I] || !LiveOut[I] || I == UseRegion)
       UnpredictableRPSave.set(I);
   }
-  Live |= LiveIn;
-  Live |= LiveOut;
   RPSave.inc(DefReg, LaneBitmask::getNone(), Reg.Mask, DAG.MRI);
 
   // Get frequencies of defining and using regions. A rematerialization from the
diff --git a/llvm/lib/Target/AMDGPU/GCNSchedStrategy.h b/llvm/lib/Target/AMDGPU/GCNSchedStrategy.h
index 2059f4e6479ff..b49f541504965 100644
--- a/llvm/lib/Target/AMDGPU/GCNSchedStrategy.h
+++ b/llvm/lib/Target/AMDGPU/GCNSchedStrategy.h
@@ -575,6 +575,10 @@ class PreRARematStage : public GCNSchedStage {
     /// candidate.
     GCNRegPressure RPSave;
 
+    ScoredRemat(RegisterIdx RegIdx, unsigned NumRegions)
+        : RegIdx(RegIdx), LiveIn(NumRegions), LiveOut(NumRegions),
+          Live(NumRegions), UnpredictableRPSave(NumRegions) {}
+
     /// Execution frequency information required by scoring heuristics.
     /// Frequencies are scaled down if they are high to avoid overflow/underflow
     /// when combining them.
@@ -590,11 +594,11 @@ class PreRARematStage : public GCNSchedStage {
       static const uint64_t ScaleFactor = 1024;
     };
 
-    /// Initializes the candidate with state-independent characteristics for
-    /// rematerializable register with index handle \p RegIdx. This doesn't
-    /// update the actual score (call \ref update for this).
-    void init(RegisterIdx RegIdx, const FreqInfo &Freq,
-              const Rematerializer &Remater, GCNScheduleDAGMILive &DAG);
+    /// Initializes the candidate with state-independent characteristics.
+    /// This doesn't update the actual score (call \ref update for this).
+    /// Note: LiveIn/LiveOut must be pre-populated before calling this.
+    void init(const FreqInfo &Freq, const Rematerializer &Remater,
+              GCNScheduleDAGMILive &DAG);
 
     /// Rematerializes the candidate using the \p Remater.
     void rematerialize(Rematerializer &Remater) const;

``````````

</details>


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


More information about the llvm-commits mailing list