[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