[llvm] [SLSR] Skipping rewriting based on liveness (PR #218470)
Igor Wodiany via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 24 02:51:34 PDT 2026
================
@@ -1411,6 +1423,380 @@ bool StraightLineStrengthReduceLegacyPass::runOnFunction(Function &F) {
return StraightLineStrengthReduce(DL, DT, SE, TTI).runOnFunction(F);
}
+namespace {
+
+class RewriteFilter {
+ // Recondiser rewriting a basic block holding many distinct SLSR bases.
+
+ // Each basis contributes one extended live range no matter how many
+ // candidates are rewritten against it in a basic block.
+ // If the number of bases is above this threshold do a check if the
+ // liveness of the block has increase a lot by SLSR.
+ static constexpr unsigned MinDistinctBasisesToFilter = 16;
+
+public:
+ using Candidate = StraightLineStrengthReduce::Candidate;
+
+ RewriteFilter(const Function *F,
+ DenseMap<Instruction *, Candidate *> &PickedCandidateMap,
+ const TargetTransformInfo *TTI)
+ : F(F), PickedCandidateMap(PickedCandidateMap), TTI(TTI) {}
+
+ // Return the candidates whose rewrite would push their block's register
+ // pressure past what the target can allocate.
+ DenseSet<const Instruction *> run() {
+ DenseSet<const Instruction *> InstsToSkip;
+ if (!EnableRewriteFilter || PickedCandidateMap.empty())
+ return InstsToSkip;
+
+ // Without a budget there is nothing to compare the pressure against, so
+ // check for one before paying for the liveness and pressure analyses.
+ std::optional<unsigned> Budget = getRegisterBudget();
+ if (!Budget)
+ return InstsToSkip;
+
+ buildBBToNumCandsAndBasises(PickedCandidateMap);
+ if (MaxNumBasisesInBB <= MinDistinctBasisesToFilter)
+ return InstsToSkip;
+
+ // Compute live-in and live-out of each BB in CFG
+ buildBBToLiveness(*F);
+
+ DEBUG_SLSR_REWRITE_FILTER(dbgs() << "-- Max liveness of BBs -- \n");
+ SmallPtrSet<const BasicBlock *, 8> BBsToSkip;
+ for (auto &BB : *F) {
+
+ auto It = BBToNumCandsAndBasises.find(&BB);
+ if (It == BBToNumCandsAndBasises.end() ||
+ It->second.second <= MinDistinctBasisesToFilter)
+ continue;
+
+ const BlockLiveness &BL = getLiveness(&BB);
+ auto [MaxLiveness, MaxLivenessWithSLSR] =
+ maxLivenessInBlockBackward(BB, BL.LiveIn, BL.LiveOut);
+ DEBUG_SLSR_REWRITE_FILTER(dbgs() << "MaxLiveness:" << BB.getName()
+ << ": (" << MaxLiveness << ", "
+ << MaxLivenessWithSLSR << ")" << "\n");
+
+ if (!rewriteWouldOverflowBudget(MaxLiveness, MaxLivenessWithSLSR,
+ *Budget))
+ continue;
+
+ DEBUG_SLSR_REWRITE_FILTER(
+ dbgs() << "Skipping BB from SLSR: " << BB.getName() << "\n");
+ BBsToSkip.insert(&BB);
+ } // Done with BBs
+
+ // One pass over the candidates rather than over the instructions of every
+ // skipped block. A skipped block is one of the larger blocks in the
+ // function, while the candidate map is small by comparison.
+ for (const auto &It : PickedCandidateMap)
+ if (BBsToSkip.contains(It.first->getParent()) &&
+ InstsToSkip.insert(It.first).second)
+ NumFilteredCandidates++;
+
+ return InstsToSkip;
+ }
+
+private:
+ const Function *F;
+ DenseMap<Instruction *, Candidate *> &PickedCandidateMap;
+ const TargetTransformInfo *TTI;
+
+ DenseMap<const BasicBlock *, std::pair<unsigned, unsigned>>
+ BBToNumCandsAndBasises;
+ unsigned MaxNumBasisesInBB = 0;
+
+ using ValueSet = SmallPtrSet<const Value *, 32>;
+ struct BlockLiveness {
+ ValueSet LiveIn;
+ ValueSet LiveOut;
+ };
+
+ DenseMap<const BasicBlock *, BlockLiveness> BBToLiveness;
+ // The liveness scan asks for the weight of every live value at every
+ // instruction, so memoize on the type, which is all the weight depends on.
+ mutable DenseMap<Type *, unsigned> WeightCache;
+
+ // Liveness is only computed for functions that pass the candidate-count
+ // gate. Blocks of the remaining functions read as having nothing live across
+ // their boundaries, which is why no rewrite is suppressed in that case.
+ const BlockLiveness &getLiveness(const BasicBlock *BB) const {
+ static const BlockLiveness Empty;
+ auto It = BBToLiveness.find(BB);
+ return It == BBToLiveness.end() ? Empty : It->second;
+ }
+
+ std::optional<unsigned> getRegisterBudget() const {
+ std::optional<unsigned> Budget = TTI->getRegisterBudget(*F);
+ if (Budget && *Budget == 0)
+ return std::nullopt;
+ return Budget;
+ }
+
+ // Return true if rewriting every candidate in a block, taking its peak
+ // pressure from \p Before to \p After, would ask the allocator for more
+ // registers than \p Budget.
+ bool rewriteWouldOverflowBudget(unsigned Before, unsigned After,
+ unsigned Budget) const {
+ // Leave the allocator some slack: it also has to satisfy register class
+ // and ABI constraints that this estimate knows nothing about.
+ constexpr double SafeRatio = 0.9;
+ unsigned SafeBudget = static_cast<unsigned>(Budget * SafeRatio);
+
+ // There is headroom, so however much the rewrite adds is irrelevant.
+ if (After <= SafeBudget)
+ return false;
+
+ // The rewrite is what takes the block over.
+ if (Before <= SafeBudget)
+ return true;
+
+ // Already over budget. SLSR can still lower pressure here, so only refuse
+ // rewrites that make it meaningfully worse.
+ constexpr unsigned AbsDelta = 4;
----------------
IgWod wrote:
Isn't it potentially too specific to AMDGPU?
https://github.com/llvm/llvm-project/pull/218470
More information about the llvm-commits
mailing list