[llvm] [Transforms][Utils] Add LoopSplitUtils, a reusable iteration-space loop splitter (PR #209142)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Jul 13 04:28:56 PDT 2026
github-actions[bot] wrote:
<!--LLVM CODE FORMAT COMMENT: {clang-format}-->
:warning: C/C++ code formatter, clang-format found issues in your code. :warning:
<details>
<summary>
You can test this locally with the following command:
</summary>
``````````bash
git-clang-format --diff origin/main HEAD --extensions h,cpp -- llvm/include/llvm/Transforms/Utils/LoopSplitTestPass.h llvm/include/llvm/Transforms/Utils/LoopSplitUtils.h llvm/lib/Transforms/Utils/LoopSplitTestPass.cpp llvm/lib/Transforms/Utils/LoopSplitUtils.cpp llvm/lib/Passes/PassBuilder.cpp llvm/lib/Transforms/Scalar/InductiveRangeCheckElimination.cpp llvm/lib/Transforms/Scalar/LoopBoundSplit.cpp --diff_from_common_commit
``````````
:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:
</details>
<details>
<summary>
View the diff from clang-format here.
</summary>
``````````diff
diff --git a/llvm/include/llvm/Transforms/Utils/LoopSplitUtils.h b/llvm/include/llvm/Transforms/Utils/LoopSplitUtils.h
index fa3bf42aa..75d116696 100644
--- a/llvm/include/llvm/Transforms/Utils/LoopSplitUtils.h
+++ b/llvm/include/llvm/Transforms/Utils/LoopSplitUtils.h
@@ -81,18 +81,22 @@ public:
/// Append an inclusive partition range [Start, End] in iteration order.
/// Partitions must tile the whole space: first Start = induction start, each
- /// later Start = previous End +/- step, last End = induction end (desc: S >= E).
+ /// later Start = previous End +/- step, last End = induction end (desc: S >=
+ /// E).
///
/// Bounds must be loop-invariant and representable in the induction type
/// without wrapping: a Start +/- offset that wraps past TYPE_MAX/MIN/0 looks
- /// in-range and silently miscompiles. See LoopSplitUtils.cpp for the rationale.
+ /// in-range and silently miscompiles. See LoopSplitUtils.cpp for the
+ /// rationale.
///
- /// Every partition is guarded by default; use avoidPartitionGuard() to opt out.
+ /// Every partition is guarded by default; use avoidPartitionGuard() to opt
+ /// out.
LLVM_ABI void addPartition(const SCEV *Start, const SCEV *End);
- /// Suppress the entry guard for partition \p PartitionIndex (already added). Use
- /// only for a partition the caller can prove runs at least once; for a runtime-
- /// empty partition this is incorrect and yields one spurious iteration.
+ /// Suppress the entry guard for partition \p PartitionIndex (already added).
+ /// Use only for a partition the caller can prove runs at least once; for a
+ /// runtime- empty partition this is incorrect and yields one spurious
+ /// iteration.
LLVM_ABI void avoidPartitionGuard(unsigned PartitionIndex);
unsigned getNumPartitions() const { return Partitions.size(); }
@@ -106,8 +110,9 @@ public:
LLVM_ABI bool split();
/// Return the counterpart of original-loop value \p V in partition
- /// \p PartitionIndex (0-based). Partition 0 maps values to themselves; a later
- /// partition returns the clone, or null if not cloned. Valid only after split().
+ /// \p PartitionIndex (0-based). Partition 0 maps values to themselves; a
+ /// later partition returns the clone, or null if not cloned. Valid only after
+ /// split().
LLVM_ABI Value *getPartitionValue(const Value *V,
unsigned PartitionIndex) const;
@@ -150,7 +155,8 @@ private:
ScalarEvolution *SE;
DominatorTree *DT;
bool AllowUncomputableTripCount = false; // opt-in to the fallback below.
- bool AllowTruncatedLatchCompare = false; // opt-in to a trunc(iv) exit compare.
+ bool AllowTruncatedLatchCompare =
+ false; // opt-in to a trunc(iv) exit compare.
// Induction analysis, populated by isLegal().
PHINode *Induction = nullptr;
@@ -159,10 +165,10 @@ private:
ICmpInst *LatchCmp = nullptr; // the exiting block's exit compare.
Value *LatchIndOperand = nullptr; // induction operand of the exit compare.
bool LatchUsesInductionPHI =
- false; // exit test compares the PHI, not the step.
- bool InductionIsSigned = false; // iteration ordering signedness.
- bool InductionIsDescending = false; // step is negative (loop counts down).
- APInt InductionStep; // signed constant step, induction width.
+ false; // exit test compares the PHI, not the step.
+ bool InductionIsSigned = false; // iteration ordering signedness.
+ bool InductionIsDescending = false; // step is negative (loop counts down).
+ APInt InductionStep; // signed constant step, induction width.
const SCEV *InductionEnd = nullptr;
// Uncomputable-trip-count fallback state, set by isLegal() only when there is
diff --git a/llvm/lib/Transforms/Scalar/InductiveRangeCheckElimination.cpp b/llvm/lib/Transforms/Scalar/InductiveRangeCheckElimination.cpp
index d56cd093d..8f183e5ef 100644
--- a/llvm/lib/Transforms/Scalar/InductiveRangeCheckElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/InductiveRangeCheckElimination.cpp
@@ -1060,8 +1060,8 @@ static bool constrainLoopWithLoopSplitUtils(
const SCEV *StartS = SE.getSCEV(LS.IndVarStart);
if (StartS->getType() != IndTy) {
- // Narrow-latch: widen LoopStructure's narrower-typed start to the induction/
- // range type (mirroring LoopConstrainer's NoopOrExtend).
+ // Narrow-latch: widen LoopStructure's narrower-typed start to the
+ // induction/ range type (mirroring LoopConstrainer's NoopOrExtend).
auto *StartTy = dyn_cast<IntegerType>(StartS->getType());
if (!StartTy || StartTy->getBitWidth() > IndTy->getBitWidth())
return false;
@@ -1070,8 +1070,9 @@ static bool constrainLoopWithLoopSplitUtils(
if (StartS->getType() != IndTy)
return false;
// Inclusive end of the tiled iteration space: the last counted value in exact
- // mode; with no computable trip count the invariant bound stands in (the final
- // partition keeps the original latch, so its end is only a placeholder).
+ // mode; with no computable trip count the invariant bound stands in (the
+ // final partition keeps the original latch, so its end is only a
+ // placeholder).
const SCEV *EndIncl = LSU.isUncomputableTripCountMode()
? LSU.getInductionBound()
: LSU.getInductionEnd();
@@ -1089,8 +1090,9 @@ static bool constrainLoopWithLoopSplitUtils(
LLVMContext &Context = L->getHeader()->getContext();
for (InductiveRangeCheck &IRC : RangeChecksToEliminate) {
Use *U = IRC.getCheckUse();
- Value *Folded = IRC.getPassingDirection() ? ConstantInt::getTrue(Context)
- : ConstantInt::getFalse(Context);
+ Value *Folded = IRC.getPassingDirection()
+ ? ConstantInt::getTrue(Context)
+ : ConstantInt::getFalse(Context);
U->set(Folded);
}
return true;
@@ -1290,7 +1292,8 @@ bool InductiveRangeCheckElimination::run(
// unconstrained, no fallback), OFF uses the legacy LoopConstrainer below.
if (UseLoopSplitUtils) {
if (constrainLoopWithLoopSplitUtils(L, LI, SE, DT, LS, *MaybeSR, RangeTy,
- LPMAddNewLoop, RangeChecksToEliminate)) {
+ LPMAddNewLoop,
+ RangeChecksToEliminate)) {
LLVM_DEBUG(dbgs() << "irce: constrained loop via LoopSplitUtils\n");
return true;
}
diff --git a/llvm/lib/Transforms/Scalar/LoopBoundSplit.cpp b/llvm/lib/Transforms/Scalar/LoopBoundSplit.cpp
index 9c97cc340..38ee1bc8d 100644
--- a/llvm/lib/Transforms/Scalar/LoopBoundSplit.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopBoundSplit.cpp
@@ -320,7 +320,8 @@ splitLoopBoundWithLoopSplitUtils(Loop &L, DominatorTree &DT, LoopInfo &LI,
// The split condition is normalized to "AddRec < SplitBound", so the pre-loop
// covers iterations with induction < SplitBound and the post-loop the rest.
- // findSplitCandidate guarantees SplitBound > start, so the pre-loop is nonempty.
+ // findSplitCandidate guarantees SplitBound > start, so the pre-loop is
+ // nonempty.
const SCEV *Step = IndAR->getStepRecurrence(SE);
const SCEV *Start0 = IndAR->getStart();
diff --git a/llvm/lib/Transforms/Utils/LoopSplitTestPass.cpp b/llvm/lib/Transforms/Utils/LoopSplitTestPass.cpp
index 088fd5f28..c9f4fac98 100644
--- a/llvm/lib/Transforms/Utils/LoopSplitTestPass.cpp
+++ b/llvm/lib/Transforms/Utils/LoopSplitTestPass.cpp
@@ -43,16 +43,18 @@ static cl::list<unsigned> UnguardedPartitions(
static cl::opt<bool> PrintPartitionMap(
"loop-split-print-partition-map",
- cl::desc("After splitting, print each original loop instruction's "
- "counterpart in every partition (LoopSplitUtils::getPartitionValue)"),
+ cl::desc(
+ "After splitting, print each original loop instruction's "
+ "counterpart in every partition (LoopSplitUtils::getPartitionValue)"),
cl::init(false));
static cl::opt<bool> AllowUncomputableTripCount(
"loop-split-allow-uncomputable-trip-count",
- cl::desc("Opt in to LoopSplitUtils' uncomputable-trip-count fallback, which "
- "splits a multi-exit loop whose counted exit has no computable trip "
- "count (e.g. an ascending non-unit-step loop with a symbolic bound) "
- "by keeping the original latch on the final partition"),
+ cl::desc(
+ "Opt in to LoopSplitUtils' uncomputable-trip-count fallback, which "
+ "splits a multi-exit loop whose counted exit has no computable trip "
+ "count (e.g. an ascending non-unit-step loop with a symbolic bound) "
+ "by keeping the original latch on the final partition"),
cl::init(false));
/// Build the partition list for \p L from the command-line split offsets and
diff --git a/llvm/lib/Transforms/Utils/LoopSplitUtils.cpp b/llvm/lib/Transforms/Utils/LoopSplitUtils.cpp
index c6b8d77fb..7794356bb 100644
--- a/llvm/lib/Transforms/Utils/LoopSplitUtils.cpp
+++ b/llvm/lib/Transforms/Utils/LoopSplitUtils.cpp
@@ -102,8 +102,8 @@ struct LoopSplitUtils::SplitState {
/// True if \c Def is used outside the loop and must be merged at the final
/// exit.
bool EscapesOutside = false;
- /// \c Def and \c CarriedHeaderPHI cloned into each partition (index 0 is the
- /// original; \c PerPartitionPHI[0] is unused).
+ /// \c Def and \c CarriedHeaderPHI cloned into each partition (index 0 is
+ /// the original; \c PerPartitionPHI[0] is unused).
SmallVector<Value *, 4> PerPartitionDef;
SmallVector<PHINode *, 4> PerPartitionPHI;
};
@@ -309,7 +309,8 @@ bool LoopSplitUtils::isLegal() {
return false;
}
// Normalize the predicate to read "IndOp <pred> Bound" meaning "continue",
- // accounting for operand order and which branch successor stays in the loop.
+ // accounting for operand order and which branch successor stays in the
+ // loop.
ICmpInst::Predicate Raw =
LatchCmp->getOperand(0) == LatchIndOperand
? LatchCmp->getPredicate()
@@ -326,9 +327,10 @@ bool LoopSplitUtils::isLegal() {
return true;
}
- // Narrow latch: the trip count is in the narrower latch type but the induction
- // is wider. Zero-extend the count to the induction type (a trip count is a
- // non-negative index, so the extend is exact) for evaluateAtIteration.
+ // Narrow latch: the trip count is in the narrower latch type but the
+ // induction is wider. Zero-extend the count to the induction type (a trip
+ // count is a non-negative index, so the extend is exact) for
+ // evaluateAtIteration.
if (AllowTruncatedLatchCompare &&
BTC->getType() != IndAR->getStart()->getType())
BTC = SE->getNoopOrZeroExtend(BTC, IndAR->getStart()->getType());
@@ -429,9 +431,9 @@ bool LoopSplitUtils::split() {
// Cannot peel an EH pad; those are handled conservatively upstream.
if (S.ExitBlock->isEHPad())
return false;
- BasicBlock *Dedicated =
- SplitBlockPredecessors(S.ExitBlock, {ExitingBlock}, ".ls.counted.exit",
- DT, LI, /*MSSAU=*/nullptr, /*PreserveLCSSA=*/true);
+ BasicBlock *Dedicated = SplitBlockPredecessors(
+ S.ExitBlock, {ExitingBlock}, ".ls.counted.exit", DT, LI,
+ /*MSSAU=*/nullptr, /*PreserveLCSSA=*/true);
if (!Dedicated)
return false;
S.ExitBlock = Dedicated;
@@ -474,7 +476,8 @@ void LoopSplitUtils::collectEscapingValues(SplitState &S) {
// (1) Carried values: each header PHI whose backedge value differs from its
// initial value must resume in later partitions. The induction PHI is now
- // included -- carrying its runtime value lets a non-unit stride tile correctly.
+ // included -- carrying its runtime value lets a non-unit stride tile
+ // correctly.
DenseMap<Value *, unsigned> CarriedDefToEscapingIdx;
for (PHINode &HeaderPHI : L->getHeader()->phis()) {
Value *BackedgeValue = HeaderPHI.getIncomingValueForBlock(Latch);
@@ -547,8 +550,9 @@ void LoopSplitUtils::expandPartitionBounds(SplitState &S) {
for (unsigned I = 0; I < N; ++I) {
PartitionInfo &P = Partitions[I];
- // Provably empty when Start overshoots End by one step (Start - End == step).
- // Compile-time only: a runtime overshoot wraps and would falsely enter.
+ // Provably empty when Start overshoots End by one step (Start - End ==
+ // step). Compile-time only: a runtime overshoot wraps and would falsely
+ // enter.
const SCEV *PartWidth = SE->getMinusSCEV(P.StartExpr, P.EndExpr);
if (auto *PartWidthConst = dyn_cast<SCEVConstant>(PartWidth)) {
const APInt &W = PartWidthConst->getAPInt();
@@ -569,11 +573,13 @@ void LoopSplitUtils::expandPartitionBounds(SplitState &S) {
// short trip count keeps the last iteration in the right partition.
const SCEV *ClampedEndSCEV;
if (InductionIsDescending)
- ClampedEndSCEV = InductionIsSigned ? SE->getSMaxExpr(P.EndExpr, InductionEnd)
- : SE->getUMaxExpr(P.EndExpr, InductionEnd);
+ ClampedEndSCEV = InductionIsSigned
+ ? SE->getSMaxExpr(P.EndExpr, InductionEnd)
+ : SE->getUMaxExpr(P.EndExpr, InductionEnd);
else
- ClampedEndSCEV = InductionIsSigned ? SE->getSMinExpr(P.EndExpr, InductionEnd)
- : SE->getUMinExpr(P.EndExpr, InductionEnd);
+ ClampedEndSCEV = InductionIsSigned
+ ? SE->getSMinExpr(P.EndExpr, InductionEnd)
+ : SE->getUMinExpr(P.EndExpr, InductionEnd);
P.SelEnd = Expander.expandCodeFor(ClampedEndSCEV, IndTy, EntryGuardTerm);
}
}
@@ -664,8 +670,9 @@ void LoopSplitUtils::rewriteLatch(Loop *PL, BasicBlock *ExitingBlk,
IRBuilder<> B(Cmp);
// Uncomputable-trip-count mode: keep the loop's counted condition so it is
- // driven by the original latch. The final partition uses it alone; an interior
- // one ANDs it with the clamp to its boundary to hand off to the next first.
+ // driven by the original latch. The final partition uses it alone; an
+ // interior one ANDs it with the clamp to its boundary to hand off to the next
+ // first.
if (UncomputableTripCountMode) {
ICmpInst::Predicate OrigPred =
static_cast<ICmpInst::Predicate>(CountedContinuePred);
@@ -694,8 +701,8 @@ void LoopSplitUtils::rewriteLatch(Loop *PL, BasicBlock *ExitingBlk,
if (Bound->getType() != IndOp->getType())
Bound = B.CreateIntCast(Bound, IndOp->getType(), InductionIsSigned);
// The clamp keeps iterations with induction <= SelEnd. The continue test is
- // inclusive iff (compares PHI) == (test precedes body) -- so bottom-tested+PHI
- // and top-tested+step are strict, the other two inclusive.
+ // inclusive iff (compares PHI) == (test precedes body) -- so
+ // bottom-tested+PHI and top-tested+step are strict, the other two inclusive.
bool Inclusive = LatchUsesInductionPHI == IsTopTested;
ICmpInst::Predicate Pred =
continuePredicate(InductionIsSigned, InductionIsDescending, Inclusive);
@@ -713,9 +720,9 @@ void LoopSplitUtils::chainPartitions(SplitState &S) {
const ICmpInst::Predicate GuardPred =
guardPredicate(InductionIsSigned, InductionIsDescending);
- // Uncomputable-trip-count mode has no exact end for partition 0's usual guard.
- // A bottom-tested loop always runs its first iteration, so partition 0 enters
- // unconditionally (top-tested is conditional, so its guard is kept).
+ // Uncomputable-trip-count mode has no exact end for partition 0's usual
+ // guard. A bottom-tested loop always runs its first iteration, so partition 0
+ // enters unconditionally (top-tested is conditional, so its guard is kept).
if (UncomputableTripCountMode && !IsTopTested && getNumPartitions() > 0)
Partitions[0].Guarded = false;
@@ -754,8 +761,9 @@ void LoopSplitUtils::chainPartitions(SplitState &S) {
Value *Enter;
if (UncomputableTripCountMode) {
// No exact end: enter iff the original loop runs this partition's first
- // iteration. Map the start to the value the counted compare inspects for
- // that iteration, then apply the original predicate against the bound.
+ // iteration. Map the start to the value the counted compare inspects
+ // for that iteration, then apply the original predicate against the
+ // bound.
int DeltaSteps =
(LatchUsesInductionPHI ? 0 : 1) - (IsTopTested ? 0 : 1);
Value *GuardVal = P.StartVal;
@@ -790,8 +798,8 @@ void LoopSplitUtils::chainPartitions(SplitState &S) {
PartitionInfo &Cur = Partitions[I];
DT->addNewBlock(Cur.GuardBlock, MergeTargetIDom(Prev));
DT->changeImmediateDominator(Cur.Preheader, Cur.GuardBlock);
- // The partition's exit is reached only from its exiting block, so that block
- // is its immediate dominator.
+ // The partition's exit is reached only from its exiting block, so that
+ // block is its immediate dominator.
DT->addNewBlock(Cur.Exit, Cur.ExitingBlk);
}
// The final exit is the last partition's merge target.
``````````
</details>
https://github.com/llvm/llvm-project/pull/209142
More information about the llvm-commits
mailing list