[llvm] [Transforms][Utils] Add LoopSplit for iteration-space loop splitting (PR #217232)

via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 19 01:03:08 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Ashutosh Nema (nema-ashutosh)

<details>
<summary>Changes</summary>

Split a counted loop into a chain of per-partition sub-loops covering contiguous slices of the iteration space, each guarded by an entry check with its latch clamped to its slice.

Reintroduce a reduced version of LoopSplitUtils. Handles a unit-step integer induction in either direction and both signed and unsigned orderings; loops with exit values or loop-carried values are rejected.

Driven for testing by opt -passes=loop-split.

---

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


18 Files Affected:

- (added) llvm/include/llvm/Transforms/Utils/LoopSplit.h (+132) 
- (added) llvm/include/llvm/Transforms/Utils/LoopSplitPass.h (+30) 
- (modified) llvm/lib/Passes/PassBuilder.cpp (+1) 
- (modified) llvm/lib/Passes/PassRegistry.def (+1) 
- (modified) llvm/lib/Transforms/Utils/CMakeLists.txt (+2) 
- (added) llvm/lib/Transforms/Utils/LoopSplit.cpp (+527) 
- (added) llvm/lib/Transforms/Utils/LoopSplitPass.cpp (+143) 
- (added) llvm/test/Transforms/LoopSplit/basic.ll (+263) 
- (added) llvm/test/Transforms/LoopSplit/branch-weights.ll (+70) 
- (added) llvm/test/Transforms/LoopSplit/constant-trip-count.ll (+153) 
- (added) llvm/test/Transforms/LoopSplit/descending.ll (+170) 
- (added) llvm/test/Transforms/LoopSplit/loop-depth.ll (+638) 
- (added) llvm/test/Transforms/LoopSplit/multiple-partitions.ll (+155) 
- (added) llvm/test/Transforms/LoopSplit/oversized-split-offset.ll (+87) 
- (added) llvm/test/Transforms/LoopSplit/saturating-latch.ll (+222) 
- (added) llvm/test/Transforms/LoopSplit/sequential-loops.ll (+107) 
- (added) llvm/test/Transforms/LoopSplit/unsupported-forms.ll (+879) 
- (added) llvm/test/Transforms/LoopSplit/wrapping-iteration-space.ll (+139) 


``````````diff
diff --git a/llvm/include/llvm/Transforms/Utils/LoopSplit.h b/llvm/include/llvm/Transforms/Utils/LoopSplit.h
new file mode 100644
index 0000000000000..b075d76b843ad
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Utils/LoopSplit.h
@@ -0,0 +1,132 @@
+//===- LoopSplit.h - Split a loop's iteration space -------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Splits a counted loop's iteration space into a chain of per-partition
+// sub-loops. See LoopSplit.cpp for the structure produced.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_UTILS_LOOPSPLIT_H
+#define LLVM_TRANSFORMS_UTILS_LOOPSPLIT_H
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Support/Compiler.h"
+
+namespace llvm {
+
+class DominatorTree;
+class SCEV;
+class SCEVExpander;
+class ScalarEvolution;
+
+/// Splits a counted loop into a chain of per-partition sub-loops.
+///
+/// Usage:
+/// \code
+///   LoopSplit LS(L, LI, SE, DT);
+///   if (!LS.isLegal())
+///     return false;
+///   LS.addPartition(S0, E0);   // one call per partition, in order
+///   LS.addPartition(S1, E1);
+///   LS.split();
+/// \endcode
+class LoopSplit {
+public:
+  LLVM_ABI LoopSplit(Loop *L, LoopInfo *LI, ScalarEvolution *SE,
+                     DominatorTree *DT)
+      : L(L), LI(LI), SE(SE), DT(DT) {}
+
+  /// Analyze \p L and return true if it is a counted loop this utility can
+  /// split: a bottom-tested single-exit loop in LCSSA form with dedicated
+  /// exits, no loop-carried and no escaping values, a unique unit-step integer
+  /// induction, and a computable trip count that cannot wrap. Must succeed
+  /// before split().
+  LLVM_ABI bool isLegal();
+
+  /// Return the loop's induction variable. Valid only after isLegal() succeeds.
+  LLVM_ABI PHINode *getInductionVariable() const {
+    return L->getInductionVariable(*SE);
+  }
+
+  /// The induction value on the last iteration, which the final partition must
+  /// end at. Valid only after isLegal() succeeds.
+  LLVM_ABI const SCEV *getInductionEnd() const { return InductionEnd; }
+
+  /// 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).
+  ///
+  /// Both bounds must have the induction type and be loop-invariant. They must
+  /// also stay within the iteration space, extended by the one step past its
+  /// start that an empty partition needs; isLegal() has proven that much
+  /// representable. Reaching further wraps past TYPE_MAX/MIN/0 into a bound
+  /// that still looks in range, which silently miscompiles. See LoopSplit.cpp
+  /// for the rationale.
+  LLVM_ABI void addPartition(const SCEV *Start, const SCEV *End);
+
+  LLVM_ABI unsigned getNumPartitions() const { return Partitions.size(); }
+
+  /// Perform the split. Requires a successful isLegal() and at least two
+  /// partitions. Returns true if the loop was rewritten.
+  LLVM_ABI bool split();
+
+private:
+  /// Everything known about one partition: the caller-supplied range plus the
+  /// state split() derives. Indexed by partition number in \c Partitions.
+  struct PartitionInfo {
+    PartitionInfo(const SCEV *StartExpr, const SCEV *EndExpr)
+        : StartExpr(StartExpr), EndExpr(EndExpr) {}
+
+    // Set by addPartition() before split():
+    const SCEV *StartExpr; // inclusive iteration range [Start, End].
+    const SCEV *EndExpr;
+
+    // Filled in by split():
+    Value *StartVal = nullptr; // expanded start.
+    Value *SelEnd = nullptr;   // clamped end min(End, indEnd).
+    BasicBlock *GuardBlock = nullptr;
+    BasicBlock *Preheader = nullptr;
+    BasicBlock *Exit = nullptr;
+    Loop *SubLoop = nullptr;
+    PHINode *IndPHI = nullptr; // this partition's induction variable.
+  };
+
+  /// Per-split() scratch threaded through the phase helpers: the blocks the
+  /// transform creates. A pure transform internal, so it is defined in the
+  /// implementation file.
+  struct SplitState;
+
+  Loop *L;
+  LoopInfo *LI;
+  ScalarEvolution *SE;
+  DominatorTree *DT;
+
+  // Induction analysis, populated by isLegal().
+  const SCEV *InductionEnd = nullptr; // value on the last iteration.
+  bool InductionIsSigned = false;     // iteration ordering signedness.
+  bool Descending = false;            // step is -1 (the loop counts down).
+
+  /// One record per partition, in add order.
+  SmallVector<PartitionInfo, 4> Partitions;
+
+  // split() phase helpers, run in order; each is documented at its definition.
+  /// Split the final exit off the loop exit block.
+  void splitFinalExit(SplitState &S);
+  /// Expand each partition's start and clamped end into the entry guard.
+  void expandPartitionBounds(SplitState &S, SCEVExpander &Expander);
+  /// Clone each later partition's sub-loop and create its guard/exit.
+  void clonePartitions(SplitState &S);
+  /// Emit each guard, clamp each latch, and chain the partitions.
+  void chainPartitions(SplitState &S);
+};
+
+} // namespace llvm
+
+#endif // LLVM_TRANSFORMS_UTILS_LOOPSPLIT_H
diff --git a/llvm/include/llvm/Transforms/Utils/LoopSplitPass.h b/llvm/include/llvm/Transforms/Utils/LoopSplitPass.h
new file mode 100644
index 0000000000000..14dacfa8b5b7f
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Utils/LoopSplitPass.h
@@ -0,0 +1,30 @@
+//===- LoopSplitPass.h - Test driver for LoopSplit --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// A command-line driven pass used to exercise the LoopSplit utility from `opt`.
+// The split points are provided via the -loop-split-points option as iteration
+// offsets relative to the induction start.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_UTILS_LOOPSPLITPASS_H
+#define LLVM_TRANSFORMS_UTILS_LOOPSPLITPASS_H
+
+#include "llvm/IR/PassManager.h"
+#include "llvm/Support/Compiler.h"
+
+namespace llvm {
+
+class LoopSplitPass : public PassInfoMixin<LoopSplitPass> {
+public:
+  LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
+};
+
+} // namespace llvm
+
+#endif // LLVM_TRANSFORMS_UTILS_LOOPSPLITPASS_H
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 6abaa77f9065f..f87fb821a5884 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -379,6 +379,7 @@
 #include "llvm/Transforms/Utils/InstructionNamer.h"
 #include "llvm/Transforms/Utils/LibCallsShrinkWrap.h"
 #include "llvm/Transforms/Utils/LoopSimplify.h"
+#include "llvm/Transforms/Utils/LoopSplitPass.h"
 #include "llvm/Transforms/Utils/LoopVersioning.h"
 #include "llvm/Transforms/Utils/LowerCommentStringPass.h"
 #include "llvm/Transforms/Utils/LowerGlobalDtors.h"
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 177d8ecd3508d..a5db77f1dfc74 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -478,6 +478,7 @@ FUNCTION_PASS("loop-fusion", LoopFusePass())
 FUNCTION_PASS("loop-load-elim", LoopLoadEliminationPass())
 FUNCTION_PASS("loop-simplify", LoopSimplifyPass())
 FUNCTION_PASS("loop-sink", LoopSinkPass())
+FUNCTION_PASS("loop-split", LoopSplitPass())
 FUNCTION_PASS("loop-versioning", LoopVersioningPass())
 FUNCTION_PASS("lower-atomic", LowerAtomicPass())
 FUNCTION_PASS("lower-constant-intrinsics", LowerConstantIntrinsicsPass())
diff --git a/llvm/lib/Transforms/Utils/CMakeLists.txt b/llvm/lib/Transforms/Utils/CMakeLists.txt
index 68f48b757eb6d..3ee5bbc9765b2 100644
--- a/llvm/lib/Transforms/Utils/CMakeLists.txt
+++ b/llvm/lib/Transforms/Utils/CMakeLists.txt
@@ -49,6 +49,8 @@ add_llvm_component_library(LLVMTransformUtils
   LoopPeel.cpp
   LoopRotationUtils.cpp
   LoopSimplify.cpp
+  LoopSplit.cpp
+  LoopSplitPass.cpp
   LoopUnroll.cpp
   LoopUnrollAndJam.cpp
   LoopUnrollRuntime.cpp
diff --git a/llvm/lib/Transforms/Utils/LoopSplit.cpp b/llvm/lib/Transforms/Utils/LoopSplit.cpp
new file mode 100644
index 0000000000000..b4eaf7de3fdd0
--- /dev/null
+++ b/llvm/lib/Transforms/Utils/LoopSplit.cpp
@@ -0,0 +1,527 @@
+//===- LoopSplit.cpp - Split a loop's iteration space ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Splits a counted loop's iteration space into a chain of per-partition
+// sub-loops. See LoopSplit.h for the high-level usage guidelines.
+//
+// Structure produced for partitions [S0,E0], [S1,E1], ... where E is the loop's
+// last iteration and each clamped end sel_i is min(E_i, E), or max descending:
+//
+//   guard0:                            ; every S_i and sel_i is computed here
+//     if (S0 <= sel0) goto preheader0 else goto guard1
+//   loop0: ...                         ; latch iterates while i < sel0
+//   exit0 -> guard1
+//   guard1:
+//     if (S1 <= sel1) goto preheader1 else goto guard2
+//   loop1: ...                         ; latch iterates while i < sel1
+//   exit1 -> guard2
+//     ...
+//   final.exit:
+//
+// Each guard holds the "S_i <= sel_i" check and skips an empty partition by
+// falling through to the next guard. All S_i/sel_i are materialized once in
+// guard0, and the end clamp keeps the "runs at least once" iteration in the
+// right partition.
+//
+// The latch keeps iterating while the value the next iteration would use is
+// still in the partition. That is written as the strict "i < sel_i" on the
+// induction PHI rather than "i + 1 <= sel_i" on the step value; the two agree
+// because isLegal() has established that the space does not wrap, and the
+// strict form never forms i + 1, so it remains a real test even when sel_i is
+// the last value of the type, where the inclusive one would be a tautology and
+// the partition would never exit.
+//
+// A descending (step -1) loop uses the same structure mirrored: partitions run
+// high-to-low and the clamp and predicates flip (>=/>).
+//
+// Usage guidelines:
+//  - Caller bounds must not wrap the induction type. The clamp absorbs a bound
+//    past the runtime trip count, and isLegal() reserves the one step past the
+//    induction start that an empty partition needs, but a bound reaching any
+//    further wraps in the bound arithmetic and cannot be repaired here.
+//  - Bounds must be loop-invariant: they are expanded in guard0, so a bound
+//    depending on a value defined inside the loop cannot be placed.
+//  - The partitions must tile the original iteration space exactly -- same
+//    iterations, same order -- so the split preserves program behaviour.
+//
+// The transform is structural: inside a partition it only seeds the induction
+// PHI with that partition's start and replaces the latch test. It never
+// rebuilds a value that flows between partitions, so no SSA reconstruction is
+// needed.
+//
+// Not yet supported, and rejected by isLegal(): loop-carried values, values
+// that escape the loop (exit values), non-unit and non-integer inductions,
+// top-tested loops, and multiple exits. Also rejected is an induction start at
+// the extreme of the iteration direction, which leaves nowhere to put a
+// boundary. An induction *end* at that extreme is fine, because the latch stays
+// strict.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/Utils/LoopSplit.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/ScalarEvolution.h"
+#include "llvm/Analysis/ScalarEvolutionExpressions.h"
+#include "llvm/Analysis/ScalarEvolutionPatternMatch.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/ProfDataUtils.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Transforms/Utils/BasicBlockUtils.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/LoopUtils.h"
+#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h"
+#include "llvm/Transforms/Utils/ValueMapper.h"
+#include <optional>
+
+using namespace llvm;
+using namespace llvm::SCEVPatternMatch;
+
+#define DEBUG_TYPE "loop-split"
+
+//===----------------------------------------------------------------------===//
+// LoopSplit - construction, partition list, induction analysis
+//===----------------------------------------------------------------------===//
+
+/// Per-split() scratch shared by the phase helpers; lives for one split() call.
+/// Everything derived from the induction lives on LoopSplit itself, filled in
+/// by isLegal(); this holds only what the transform creates.
+struct LoopSplit::SplitState {
+  // Partition 0 reuses the original loop's preheader, exit, and entry guard;
+  // those blocks live in Partitions[0] rather than being duplicated here.
+  BasicBlock *FinalExit = nullptr; // where the partition chain converges.
+  Loop *OuterLoop = nullptr;       // parent of the new blocks, if any.
+  PHINode *Induction = nullptr;    // the loop's induction variable.
+};
+
+// Record a new partition with the given inclusive iteration range.
+void LoopSplit::addPartition(const SCEV *Start, const SCEV *End) {
+  assert(InductionEnd && "addPartition() requires a successful isLegal()");
+  // The bounds are combined with the induction end and expanded in its type. A
+  // mismatch would otherwise surface either as a bare "Operand types don't
+  // match!" from inside ScalarEvolution, or worse, as a silent cast.
+  assert(Start->getType() == InductionEnd->getType() &&
+         End->getType() == InductionEnd->getType() &&
+         "partition bounds must have the induction type");
+  Partitions.emplace_back(Start, End);
+}
+
+// Return the induction's add-recurrence, or null unless the induction is an
+// integer with a unit step that the latch compares.
+static const SCEVAddRecExpr *analyzeInduction(Loop *L, ScalarEvolution *SE) {
+  ICmpInst *LatchCmp = L->getLatchCmpInst();
+
+  // SCEV's induction variable, restricted to a unit-step affine recurrence.
+  PHINode *Induction = L->getInductionVariable(*SE);
+  if (!Induction)
+    return nullptr;
+  // Partition bounds are integer arithmetic on the induction type, so a loop
+  // whose only induction is a pointer is out of scope.
+  if (!Induction->getType()->isIntegerTy())
+    return nullptr;
+  const SCEV *IndSCEV = SE->getSCEV(Induction);
+  // Match an affine add-recurrence and capture its constant step; accept a unit
+  // step in either direction: +1 (ascending) or -1 (descending).
+  const APInt *Step;
+  if (!match(IndSCEV, m_scev_AffineAddRec(m_SCEV(), m_scev_APInt(Step))))
+    return nullptr;
+  if (!Step->isOne() && !Step->isAllOnes())
+    return nullptr;
+  const auto *AR = cast<SCEVAddRecExpr>(IndSCEV);
+
+  // The induction's "next" value (i + 1), produced in the latch.
+  auto *StepInst = dyn_cast<Instruction>(
+      Induction->getIncomingValueForBlock(L->getLoopLatch()));
+  if (!StepInst)
+    return nullptr;
+
+  // One compare operand must be the induction, either the PHI or its step. The
+  // rebuilt latch always compares the PHI, so which operand it was is not used.
+  if (LatchCmp->getOperand(0) == Induction ||
+      LatchCmp->getOperand(0) == StepInst ||
+      LatchCmp->getOperand(1) == Induction ||
+      LatchCmp->getOperand(1) == StepInst)
+    return AR;
+  return nullptr;
+}
+
+// Decide whether the iteration ordering is signed or unsigned; returns the
+// signedness, or nullopt if it cannot be proven.
+static std::optional<bool> computeSignedness(Loop *L,
+                                             const SCEVAddRecExpr *IndAR) {
+  ICmpInst::Predicate P = L->getLatchCmpInst()->getPredicate();
+  // A relational predicate gives the ordering directly; for eq/ne fall back to
+  // the recurrence's no-wrap flags.
+  if (ICmpInst::isRelational(P))
+    return ICmpInst::isSigned(P);
+  if (IndAR->hasNoSignedWrap())
+    return true;
+  if (IndAR->hasNoUnsignedWrap())
+    return false;
+  LLVM_DEBUG(dbgs() << DEBUG_TYPE
+             ": cannot prove iteration ordering signedness\n");
+  return std::nullopt;
+}
+
+// Prove \p Pred between \p LHS and \p RHS on entry to \p L, retrying with the
+// loop guards folded in so a bound fixed by a dominating condition is seen.
+static bool isEntryGuardedByCond(ScalarEvolution &SE, Loop *L,
+                                 ICmpInst::Predicate Pred, const SCEV *LHS,
+                                 const SCEV *RHS) {
+  if (SE.isLoopEntryGuardedByCond(L, Pred, LHS, RHS))
+    return true;
+  return SE.isLoopEntryGuardedByCond(L, Pred, SE.applyLoopGuards(LHS, L),
+                                     SE.applyLoopGuards(RHS, L));
+}
+
+// Check every structural precondition and record the induction analysis.
+bool LoopSplit::isLegal() {
+  // Require a bottom-tested single-exit loop in LCSSA form. Simplify form gives
+  // the preheader, single latch and dedicated exits; the rest pin the exit to
+  // the latch, so the latch compare can be rewritten per partition.
+  if (!L->isLoopSimplifyForm() || !L->isLCSSAForm(*DT) ||
+      L->getExitingBlock() != L->getLoopLatch() || !L->getExitBlock()) {
+    LLVM_DEBUG(dbgs() << DEBUG_TYPE ": loop not in expected form\n");
+    return false;
+  }
+
+  // The latch compare must exist and reside in the latch: it is rewritten in
+  // place, once per partition.
+  ICmpInst *LatchCmp = L->getLatchCmpInst();
+  if (!LatchCmp || LatchCmp->getParent() != L->getLoopLatch()) {
+    LLVM_DEBUG(dbgs() << DEBUG_TYPE ": latch compare not in the loop latch\n");
+    return false;
+  }
+
+  // Exit values are unsupported. Look for an LCSSA PHI and for a use outside
+  // the loop: a token-like type cannot appear in a PHI, so LCSSA can leave a
+  // value escaping with no PHI to find.
+  if (!L->getExitBlock()->phis().empty()) {
+    LLVM_DEBUG(dbgs() << DEBUG_TYPE ": loop has exit values\n");
+    return false;
+  }
+  for (BasicBlock *BB : L->blocks())
+    for (Instruction &I : *BB)
+      for (User *U : I.users())
+        if (auto *UI = dyn_cast<Instruction>(U); UI && !L->contains(UI)) {
+          LLVM_DEBUG(dbgs() << DEBUG_TYPE ": loop has exit values\n");
+          return false;
+        }
+
+  // Splitting a loop clones it, so cloning must be safe.
+  if (!L->isSafeToClone()) {
+    LLVM_DEBUG(dbgs() << DEBUG_TYPE ": loop not safe to clone\n");
+    return false;
+  }
+
+  // A computable backedge-taken count fixes the iteration space we rebuild.
+  const SCEV *BTC = SE->getBackedgeTakenCount(L);
+  if (isa<SCEVCouldNotCompute>(BTC)) {
+    LLVM_DEBUG(dbgs() << DEBUG_TYPE ": loop trip count uncomputable\n");
+    return false;
+  }
+
+  const SCEVAddRecExpr *IndAR = analyzeInduction(L, SE);
+  if (!IndAR) {
+    LLVM_DEBUG(dbgs() << DEBUG_TYPE
+               ": no unique unit-step integer induction\n");
+    return false;
+  }
+
+  PHINode *Induction = L->getInductionVariable(*SE);
+
+  // Loop-carried values are unsupported: a later partition would have to resume
+  // the previous one's value, which needs SSA reconstruction. The induction is
+  // the exception, seeded per partition from its own start bound.
+  for (PHINode &HeaderPHI : L->getHeader()->phis())
+    if (&HeaderPHI != Induction) {
+      LLVM_DEBUG(dbgs() << DEBUG_TYPE ": loop has carried values\n");
+      return false;
+    }
+
+  std::optional<bool> Signed = computeSignedness(L, IndAR);
+  if (!Signed)
+    return false;
+  InductionIsSigned = *Signed;
+  Descending =
+      cast<SCEVConstant>(IndAR->getStepRecurrence(*SE))->getAPInt().isAllOnes();
+
+  // Start and end must share the induction type; reject any width mi...
[truncated]

``````````

</details>


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


More information about the llvm-commits mailing list