[llvm] [LoopSplitUtils] Revert, removing from tree (PR #214577)

Ramkumar Ramachandra via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 10 02:37:14 PDT 2026


https://github.com/artagnon updated https://github.com/llvm/llvm-project/pull/214577

>From 8927d92392ebc92b91287ecca0715751d130650a Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <artagnon at tenstorrent.com>
Date: Thu, 6 Aug 2026 21:39:34 +0100
Subject: [PATCH] [LoopSplitUtils] Revert, removing from tree

This reverts commits:

- 2354dce21 ([Transforms][Utils] Add LoopSplitUtils for iteration-space
  loop splitting, #205995)
- 8f1efc26 ([Transforms][Utils] Preserve branch weights in
  LoopSplitUtils, #213626)
- 49ace5ab ([Transforms][Utils] Test for branch weight preservation in
  LoopSplitUtils, #213647)

Removing LoopSplitUtils from the tree completely, as several crashes
were uncovered after it was added. A highly reduced initial version with
much better test coverage is proposed for the re-land.
---
 llvm/include/llvm/IR/ValueMap.h               |  11 -
 .../llvm/Transforms/Utils/LoopSplitUtils.h    | 154 -----
 .../Transforms/Utils/LoopSplitUtilsPass.h     |  30 -
 llvm/lib/Passes/PassBuilder.cpp               |   1 -
 llvm/lib/Passes/PassRegistry.def              |   1 -
 llvm/lib/Transforms/Utils/CMakeLists.txt      |   2 -
 llvm/lib/Transforms/Utils/LoopSplitUtils.cpp  | 630 ------------------
 .../Transforms/Utils/LoopSplitUtilsPass.cpp   | 148 ----
 llvm/test/Transforms/LoopSplit/basic.ll       |  61 --
 .../Transforms/LoopSplit/branch-weights.ll    |  67 --
 .../LoopSplit/constant-trip-count.ll          |  89 ---
 llvm/test/Transforms/LoopSplit/descending.ll  |  60 --
 .../LoopSplit/empty-leading-partition.ll      |  71 --
 .../Transforms/LoopSplit/four-partitions.ll   |  88 ---
 .../LoopSplit/multiple-partitions.ll          |  75 ---
 llvm/test/Transforms/LoopSplit/nested-loop.ll |  88 ---
 .../Transforms/LoopSplit/optional-guard.ll    |  70 --
 .../LoopSplit/partition-value-map.ll          |  44 --
 llvm/test/Transforms/LoopSplit/reduction.ll   |  71 --
 19 files changed, 1761 deletions(-)
 delete mode 100644 llvm/include/llvm/Transforms/Utils/LoopSplitUtils.h
 delete mode 100644 llvm/include/llvm/Transforms/Utils/LoopSplitUtilsPass.h
 delete mode 100644 llvm/lib/Transforms/Utils/LoopSplitUtils.cpp
 delete mode 100644 llvm/lib/Transforms/Utils/LoopSplitUtilsPass.cpp
 delete mode 100644 llvm/test/Transforms/LoopSplit/basic.ll
 delete mode 100644 llvm/test/Transforms/LoopSplit/branch-weights.ll
 delete mode 100644 llvm/test/Transforms/LoopSplit/constant-trip-count.ll
 delete mode 100644 llvm/test/Transforms/LoopSplit/descending.ll
 delete mode 100644 llvm/test/Transforms/LoopSplit/empty-leading-partition.ll
 delete mode 100644 llvm/test/Transforms/LoopSplit/four-partitions.ll
 delete mode 100644 llvm/test/Transforms/LoopSplit/multiple-partitions.ll
 delete mode 100644 llvm/test/Transforms/LoopSplit/nested-loop.ll
 delete mode 100644 llvm/test/Transforms/LoopSplit/optional-guard.ll
 delete mode 100644 llvm/test/Transforms/LoopSplit/partition-value-map.ll
 delete mode 100644 llvm/test/Transforms/LoopSplit/reduction.ll

diff --git a/llvm/include/llvm/IR/ValueMap.h b/llvm/include/llvm/IR/ValueMap.h
index 1b9ade90293d8..435347be3f3c1 100644
--- a/llvm/include/llvm/IR/ValueMap.h
+++ b/llvm/include/llvm/IR/ValueMap.h
@@ -169,17 +169,6 @@ class ValueMap {
     return I != Map.end() ? I->second : ValueT();
   }
 
-  /// Return the entry for the specified key, or \p Default. This variant is
-  /// useful, because `lookup` cannot be used with non-default-constructible
-  /// values.
-  template <typename U = std::remove_cv_t<ValueT>>
-  ValueT lookup_or(const KeyT &Val, U &&Default) const {
-    typename MapT::const_iterator I = Map.find_as(Val);
-    if (I != Map.end())
-      return I->second;
-    return std::forward<U>(Default);
-  }
-
   // Inserts key,value pair into the map if the key isn't already in the map.
   // If the key is already in the map, it returns false and doesn't update the
   // value.
diff --git a/llvm/include/llvm/Transforms/Utils/LoopSplitUtils.h b/llvm/include/llvm/Transforms/Utils/LoopSplitUtils.h
deleted file mode 100644
index f01a8c5d74662..0000000000000
--- a/llvm/include/llvm/Transforms/Utils/LoopSplitUtils.h
+++ /dev/null
@@ -1,154 +0,0 @@
-//===- LoopSplitUtils.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 LoopSplitUtils.cpp for the structure produced.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TRANSFORMS_UTILS_LOOPSPLITUTILS_H
-#define LLVM_TRANSFORMS_UTILS_LOOPSPLITUTILS_H
-
-#include "llvm/ADT/SmallVector.h"
-#include "llvm/Analysis/LoopInfo.h"
-#include "llvm/Support/Compiler.h"
-#include "llvm/Transforms/Utils/ValueMapper.h"
-#include <memory>
-
-namespace llvm {
-
-class DominatorTree;
-class SCEV;
-class SCEVExpander;
-class ScalarEvolution;
-
-/// Splits a counted loop into a chain of per-partition sub-loops.
-///
-/// Usage:
-/// \code
-///   LoopSplitUtils LSU(L, LI, SE, DT);
-///   if (!LSU.isLegal())
-///     return false;
-///   LSU.addPartition(S0, E0);   // one call per partition, in order
-///   LSU.addPartition(S1, E1);
-///   LSU.split();
-/// \endcode
-class LoopSplitUtils {
-public:
-  LLVM_ABI LoopSplitUtils(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 a unique
-  /// unit-step integer induction and a computable trip count. 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);
-  }
-
-  /// 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).
-  ///
-  /// 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.
-  ///
-  /// 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.
-  LLVM_ABI void avoidPartitionGuard(unsigned PartitionIndex);
-
-  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();
-
-  /// 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().
-  LLVM_ABI Value *getPartitionValue(Value *V, unsigned PartitionIndex) const;
-
-  /// Return the original-to-clone value map for the partition at
-  /// \p PartitionIndex, for callers that want to remap many values. Null for
-  /// partition 0 (identity) and for any partition that was not cloned.
-  LLVM_ABI const ValueToValueMapTy *
-  getPartitionValueMap(unsigned PartitionIndex) const;
-
-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() = default;
-    PartitionInfo(const SCEV *StartExpr, const SCEV *EndExpr)
-        : StartExpr(StartExpr), EndExpr(EndExpr) {}
-
-    // Set by addPartition() / avoidPartitionGuard() before split():
-    const SCEV *StartExpr = nullptr; // inclusive iteration range [Start, End].
-    const SCEV *EndExpr = nullptr;
-    bool Guarded = true; // emit an entry guard?
-
-    // Filled in by split():
-    std::unique_ptr<ValueToValueMapTy> VMap; // null for partition 0 (identity).
-    Value *StartVal = nullptr;               // expanded start.
-    Value *SelEnd = nullptr;                 // clamped end min(End, indEnd).
-    bool Empty = false;                      // provably zero-iteration.
-    BasicBlock *GuardBlock = nullptr;
-    BasicBlock *Preheader = nullptr;
-    BasicBlock *Exit = nullptr;
-    Loop *SubLoop = nullptr;
-    Value *LatchIndOp = nullptr; // induction operand of the latch compare.
-  };
-
-  /// Per-split() scratch threaded through the phase helpers (the escaping
-  /// values, new blocks, etc.). 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().
-  Value *LatchIndOperand = nullptr; // induction operand of the latch compare.
-  bool InductionIsSigned = false;   // iteration ordering signedness.
-  const SCEV *InductionEnd = nullptr;
-
-  /// One record per partition, in add order.
-  SmallVector<PartitionInfo, 4> Partitions;
-
-  // split() phase helpers, run in order; each is documented at its definition.
-  /// Collect loop-carried and live-out values and split off the final exit.
-  void collectEscapingValues(SplitState &S);
-  /// Expand each partition's start and clamped end into the entry guard.
-  void expandPartitionBounds(SplitState &S, SCEVExpander &Expander);
-  /// Pass 1: clone each later partition's sub-loop and create its guard/exit.
-  void clonePartitions(SplitState &S);
-  /// Pass 2: emit each guard, clamp each latch, and chain the partitions.
-  void chainPartitions(SplitState &S);
-  /// Rebuild SSA for every escaping value with a per-value SSAUpdater.
-  void reconstructSSA(SplitState &S);
-};
-
-} // namespace llvm
-
-#endif // LLVM_TRANSFORMS_UTILS_LOOPSPLITUTILS_H
diff --git a/llvm/include/llvm/Transforms/Utils/LoopSplitUtilsPass.h b/llvm/include/llvm/Transforms/Utils/LoopSplitUtilsPass.h
deleted file mode 100644
index 4a744d5fb7a22..0000000000000
--- a/llvm/include/llvm/Transforms/Utils/LoopSplitUtilsPass.h
+++ /dev/null
@@ -1,30 +0,0 @@
-//===- LoopSplitUtilsPass.h - Test driver for LoopSplitUtils ----*- 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 LoopSplitUtils 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_LOOPSPLITUTILSPASS_H
-#define LLVM_TRANSFORMS_UTILS_LOOPSPLITUTILSPASS_H
-
-#include "llvm/IR/PassManager.h"
-#include "llvm/Support/Compiler.h"
-
-namespace llvm {
-
-class LoopSplitUtilsPass : public PassInfoMixin<LoopSplitUtilsPass> {
-public:
-  LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
-};
-
-} // namespace llvm
-
-#endif // LLVM_TRANSFORMS_UTILS_LOOPSPLITUTILSPASS_H
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index db4b92811f57e..6abaa77f9065f 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -379,7 +379,6 @@
 #include "llvm/Transforms/Utils/InstructionNamer.h"
 #include "llvm/Transforms/Utils/LibCallsShrinkWrap.h"
 #include "llvm/Transforms/Utils/LoopSimplify.h"
-#include "llvm/Transforms/Utils/LoopSplitUtilsPass.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 5e592945d0de7..177d8ecd3508d 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -478,7 +478,6 @@ 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-utils", LoopSplitUtilsPass())
 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 8858582506329..68f48b757eb6d 100644
--- a/llvm/lib/Transforms/Utils/CMakeLists.txt
+++ b/llvm/lib/Transforms/Utils/CMakeLists.txt
@@ -49,8 +49,6 @@ add_llvm_component_library(LLVMTransformUtils
   LoopPeel.cpp
   LoopRotationUtils.cpp
   LoopSimplify.cpp
-  LoopSplitUtils.cpp
-  LoopSplitUtilsPass.cpp
   LoopUnroll.cpp
   LoopUnrollAndJam.cpp
   LoopUnrollRuntime.cpp
diff --git a/llvm/lib/Transforms/Utils/LoopSplitUtils.cpp b/llvm/lib/Transforms/Utils/LoopSplitUtils.cpp
deleted file mode 100644
index db07e3f571f66..0000000000000
--- a/llvm/lib/Transforms/Utils/LoopSplitUtils.cpp
+++ /dev/null
@@ -1,630 +0,0 @@
-//===- LoopSplitUtils.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 LoopSplitUtils.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 = min(E_i, E):
-//
-//   guard0:                            ; every S_i and sel_i is computed here
-//     if (S0 <= sel0) goto preheader0 else goto guard1   ; default guard check
-//   loop0: ...                         ; latch stops at sel0
-//   exit0 -> guard1
-//   guard1:
-//     if (S1 <= sel1) goto preheader1 else goto guard2   ; default guard check
-//   loop1: ...                         ; latch stops at sel1
-//   exit1 -> guard2
-//     ...
-//   final.exit:                        ; merges every partition's live-outs
-//
-// Each guard holds the "S_i <= sel_i" check and skips an empty partition by
-// falling through to the next guard. The check is replaced by an unconditional
-// branch when a partition is proven empty (to the next guard) or the caller
-// exempts it via avoidPartitionGuard() (to its preheader). All S_i/sel_i are
-// materialized once in guard0; the end clamp keeps the "runs at least once"
-// iteration in the right partition; live-outs are rebuilt one SSAUpdater each.
-//
-// A descending (step -1) loop uses the same structure mirrored: partitions run
-// high-to-low and the empty test, clamp, and predicates flip (>=/>).
-//
-// Usage guidelines:
-//  - Caller bounds must not wrap the induction type. The clamp absorbs a bound
-//    past the runtime trip count, but a Start +/- offset that overshoots the
-//    type extreme wraps in the bound arithmetic and cannot be repaired here.
-//  - Bounds must be loop-invariant: they are expanded in guard0 (the
-//  preheader),
-//    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.
-//  - A caller that drops a guard via avoidPartitionGuard() must itself ensure
-//    that partition runs at least once, or the result is a spurious iteration.
-//
-//===----------------------------------------------------------------------===//
-
-#include "llvm/Transforms/Utils/LoopSplitUtils.h"
-#include "llvm/ADT/DenseMap.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/CFG.h"
-#include "llvm/IR/Constants.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/SSAUpdater.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-utils"
-
-//===----------------------------------------------------------------------===//
-// LoopSplitUtils - construction, partition list, induction analysis
-//===----------------------------------------------------------------------===//
-
-/// Per-split() scratch shared by the phase helpers; lives for one split() call.
-struct LoopSplitUtils::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 live-outs merge.
-  Loop *OuterLoop = nullptr;       // parent of the new blocks, if any.
-  PHINode *Induction = nullptr;    // the loop's induction variable.
-  bool Descending = false;         // step is negative (loop counts down).
-  bool LatchComparesPHI = false;   // latch compares the PHI, not the step.
-
-  /// A value that must be reconstructed after cloning because it is
-  /// loop-carried (feeds a later partition), live-out (used after the loop), or
-  /// both.
-  struct EscapingValue {
-    EscapingValue() = default;
-    EscapingValue(Value *Def) : Def(Def) {}
-
-    /// The value as it exists in partition 0 (the original).
-    Value *Def = nullptr;
-    /// The carried header PHI in partition 0, or null if \c Def needs no
-    /// per-partition start value seeded.
-    PHINode *CarriedHeaderPHI = nullptr;
-    /// 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).
-    SmallVector<Value *, 4> PerPartitionDef;
-    SmallVector<PHINode *, 4> PerPartitionPHI;
-  };
-
-  /// Values that must survive across partitions (carried and/or live-out).
-  SmallVector<EscapingValue, 8> Escaping;
-
-  EscapingValue &addEscaping(Value *Def) { return Escaping.emplace_back(Def); }
-};
-
-// Record a new partition with the given inclusive iteration range.
-void LoopSplitUtils::addPartition(const SCEV *Start, const SCEV *End) {
-  Partitions.emplace_back(Start, End);
-}
-
-// Mark a partition so split() emits no entry guard for it.
-void LoopSplitUtils::avoidPartitionGuard(unsigned PartitionIndex) {
-  assert(PartitionIndex < Partitions.size() &&
-         "avoidPartitionGuard() called for an unknown partition");
-  Partitions[PartitionIndex].Guarded = false;
-}
-
-// Return a partition's original-to-clone map, or null if it has none.
-const ValueToValueMapTy *
-LoopSplitUtils::getPartitionValueMap(unsigned PartitionIndex) const {
-  if (PartitionIndex >= Partitions.size())
-    return nullptr;
-  return Partitions[PartitionIndex].VMap.get();
-}
-
-// Look up the counterpart of an original value in a given partition.
-Value *LoopSplitUtils::getPartitionValue(Value *V,
-                                         unsigned PartitionIndex) const {
-  assert(PartitionIndex < getNumPartitions() && "partition index out of range");
-  // Partition 0 reuses the original loop: every value maps to itself.
-  if (PartitionIndex == 0)
-    return V;
-  const ValueToValueMapTy *VMap = getPartitionValueMap(PartitionIndex);
-  if (!VMap)
-    return nullptr;
-  return VMap->lookup(V);
-}
-
-// Find the induction variable and the latch operand it is compared against;
-// returns the induction's add-recurrence, or null if the loop is unsuitable.
-// On success \p LatchIndOperand is set to the compared induction operand.
-static const SCEVAddRecExpr *analyzeInduction(Loop *L, ScalarEvolution *SE,
-                                              Value *&LatchIndOperand) {
-  ICmpInst *LatchCmp = L->getLatchCmpInst();
-
-  // SCEV's induction variable, restricted to a unit-step affine recurrence.
-  PHINode *Induction = L->getInductionVariable(*SE);
-  if (!Induction)
-    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;
-
-  // Select the compare operand that is the induction (PHI or its step).
-  if (LatchCmp->getOperand(0) == Induction ||
-      LatchCmp->getOperand(0) == StepInst)
-    LatchIndOperand = LatchCmp->getOperand(0);
-  else if (LatchCmp->getOperand(1) == Induction ||
-           LatchCmp->getOperand(1) == StepInst)
-    LatchIndOperand = LatchCmp->getOperand(1);
-  else
-    return nullptr;
-  return AR;
-}
-
-// 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;
-}
-
-// Check every structural precondition and record the induction analysis.
-bool LoopSplitUtils::isLegal() {
-  // Require a bottom-tested single-exit loop in LCSSA form with a preheader.
-  if (!L->getLoopPreheader() || !L->getLoopLatch() || !L->getExitingBlock() ||
-      !L->getExitBlock() || L->getExitingBlock() != L->getLoopLatch() ||
-      !L->isLCSSAForm(*DT)) {
-    LLVM_DEBUG(dbgs() << DEBUG_TYPE ": loop not in expected form\n");
-    return false;
-  }
-
-  // The latch compare must exist and reside in the latch.
-  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;
-  }
-
-  // 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, LatchIndOperand);
-  if (!IndAR) {
-    LLVM_DEBUG(dbgs() << DEBUG_TYPE
-               ": no unique unit-step integer induction\n");
-    return false;
-  }
-
-  std::optional<bool> Signed = computeSignedness(L, IndAR);
-  if (!Signed)
-    return false;
-  InductionIsSigned = *Signed;
-
-  InductionEnd = IndAR->evaluateAtIteration(BTC, *SE);
-  // Start and end must share the induction type; reject any width mismatch.
-  if (InductionEnd->getType() != IndAR->getStart()->getType()) {
-    LLVM_DEBUG(dbgs() << DEBUG_TYPE ": induction end/start type mismatch\n");
-    return false;
-  }
-  return true;
-}
-
-//===----------------------------------------------------------------------===//
-// Transform
-//===----------------------------------------------------------------------===//
-
-// Latch "keep iterating" predicate (ascending </<=, descending >/>=); inclusive
-// when the latch compares the step value, strict when it compares the PHI.
-static ICmpInst::Predicate continuePredicate(bool Signed, bool Descending,
-                                             bool Inclusive) {
-  if (Descending)
-    return Inclusive ? (Signed ? ICmpInst::ICMP_SGE : ICmpInst::ICMP_UGE)
-                     : (Signed ? ICmpInst::ICMP_SGT : ICmpInst::ICMP_UGT);
-  return Inclusive ? (Signed ? ICmpInst::ICMP_SLE : ICmpInst::ICMP_ULE)
-                   : (Signed ? ICmpInst::ICMP_SLT : ICmpInst::ICMP_ULT);
-}
-
-// Guard "enter this partition" predicate: Start <= sel ascending, Start >= sel
-// descending.
-static ICmpInst::Predicate guardPredicate(bool Signed, bool Descending) {
-  if (Descending)
-    return Signed ? ICmpInst::ICMP_SGE : ICmpInst::ICMP_UGE;
-  return Signed ? ICmpInst::ICMP_SLE : ICmpInst::ICMP_ULE;
-}
-
-static void buildEntryGuard(BasicBlock *&Preheader, BasicBlock *&EntryGuard,
-                            DominatorTree *DT, LoopInfo *LI);
-
-// Drive the whole transform: set up scratch state and run each phase in order.
-bool LoopSplitUtils::split() {
-  PHINode *Induction = L->getInductionVariable(*SE);
-  assert(Induction && "split() requires a successful isLegal()");
-  if (getNumPartitions() < 2)
-    return false;
-
-  if (!L->hasDedicatedExits() &&
-      !formDedicatedExitBlocks(L, DT, LI, /*MSSAU=*/nullptr,
-                               /*PreserveLCSSA=*/true))
-    return false;
-
-  SplitState S;
-  // Partition 0 reuses the original loop; record its preheader/exit/guard up
-  // front.
-  PartitionInfo &P0 = Partitions[0];
-  P0.Preheader = L->getLoopPreheader();
-  P0.Exit = L->getExitBlock();
-  P0.SubLoop = L;
-  P0.LatchIndOp = LatchIndOperand;
-  S.OuterLoop = LI->getLoopFor(P0.Exit);
-  S.Induction = Induction;
-  // Derive the iteration direction and latch shape once, before transforming.
-  const auto *IndAR = cast<SCEVAddRecExpr>(SE->getSCEV(Induction));
-  S.Descending = cast<SCEVConstant>(IndAR->getStepRecurrence(*SE))
-                     ->getValue()
-                     ->isMinusOne();
-  S.LatchComparesPHI = (LatchIndOperand == Induction);
-
-  collectEscapingValues(S);
-  buildEntryGuard(P0.Preheader, P0.GuardBlock, DT, LI);
-
-  // Keep the expander (and its cleaner) alive for the whole transform: the
-  // bounds it materializes are consumed by the later phases. If we bail before
-  // committing, the cleaner reclaims the expanded instructions; on success we
-  // mark them used so they are kept.
-  SCEVExpander Expander(*SE, DEBUG_TYPE);
-  SCEVExpanderCleaner ExpanderCleaner(Expander);
-  expandPartitionBounds(S, Expander);
-  clonePartitions(S);
-  chainPartitions(S);
-  reconstructSSA(S);
-  ExpanderCleaner.markResultUsed();
-  return true;
-}
-
-// Find loop-carried and live-out values and split the final-exit block off the
-// loop exit, seeding partition 0's slots for each escaping value.
-void LoopSplitUtils::collectEscapingValues(SplitState &S) {
-  BasicBlock *Latch = L->getLoopLatch();
-  BasicBlock *OrigExit = Partitions[0].Exit;
-  BasicBlock *OrigPreheader = Partitions[0].Preheader;
-
-  // Separate FinalExit from the loop exit. Split at begin() so the LCSSA PHIs
-  // move into FinalExit (SplitBlock would advance past them).
-  S.FinalExit = OrigExit->splitBasicBlock(OrigExit->begin(), "ls.final.exit");
-  if (S.OuterLoop)
-    S.OuterLoop->addBasicBlockToLoop(S.FinalExit, *LI);
-  // splitBasicBlock does not update the dominator tree; the new exit's sole
-  // predecessor is the original exit block.
-  DT->addNewBlock(S.FinalExit, OrigExit);
-
-  // (1) Carried values: each non-induction header PHI whose backedge value
-  // differs from its initial value must resume in later partitions.
-  DenseMap<Value *, unsigned> CarriedDefToEscapingIdx;
-  for (PHINode &HeaderPHI : L->getHeader()->phis()) {
-    if (&HeaderPHI == S.Induction)
-      continue;
-    Value *CarriedValue = HeaderPHI.getIncomingValueForBlock(Latch);
-    Value *InitialValue = HeaderPHI.getIncomingValueForBlock(OrigPreheader);
-    if (CarriedValue == InitialValue)
-      continue; // invariant and equal to the initial value: nothing to carry.
-    auto &EV = S.addEscaping(CarriedValue);
-    EV.CarriedHeaderPHI = &HeaderPHI;
-    // Track in-loop carried defs so a matching live-out in (2) merges onto
-    // them.
-    if (auto *CarriedInst = dyn_cast<Instruction>(CarriedValue);
-        CarriedInst && L->contains(CarriedInst))
-      CarriedDefToEscapingIdx[CarriedValue] = S.Escaping.size() - 1;
-  }
-
-  // (2) Live-outs: dissolve each LCSSA PHI into its def and mark it escaping,
-  // merging onto a pass-(1) entry if also carried. Uses are repaired later.
-  for (PHINode &LCSSAPhi : make_early_inc_range(S.FinalExit->phis())) {
-    assert(LCSSAPhi.getNumIncomingValues() == 1 &&
-           "exit block not in LCSSA form");
-    Value *LiveOutDef = LCSSAPhi.getIncomingValue(0);
-    auto Existing = CarriedDefToEscapingIdx.find(LiveOutDef);
-    auto &EV = Existing != CarriedDefToEscapingIdx.end()
-                   ? S.Escaping[Existing->second]
-                   : S.addEscaping(LiveOutDef);
-    EV.EscapesOutside = true;
-    LCSSAPhi.replaceAllUsesWith(LiveOutDef);
-    LCSSAPhi.eraseFromParent();
-  }
-
-  // Seed partition 0 with the originals; later partitions are filled when
-  // cloned.
-  const unsigned N = getNumPartitions();
-  for (auto &EV : S.Escaping) {
-    EV.PerPartitionDef.assign(N, nullptr);
-    EV.PerPartitionPHI.assign(N, nullptr);
-    EV.PerPartitionDef[0] = EV.Def;
-    EV.PerPartitionPHI[0] = EV.CarriedHeaderPHI;
-  }
-}
-
-// Insert the entry guard ahead of partition 0's preheader and update the
-// dominator tree. On return \p Preheader is the clean preheader and
-// \p EntryGuard is the new guard block dominating the chain.
-static void buildEntryGuard(BasicBlock *&Preheader, BasicBlock *&EntryGuard,
-                            DominatorTree *DT, LoopInfo *LI) {
-  // Split the preheader: the upper half becomes the guard dominating the chain,
-  // the lower half a clean preheader.
-  BasicBlock *NewPreheader =
-      SplitBlock(Preheader, Preheader->getTerminator(), DT, LI);
-  EntryGuard = Preheader;
-  Preheader = NewPreheader;
-  // Move the original preheader's name onto the new preheader, then name the
-  // guard.
-  Preheader->takeName(EntryGuard);
-  EntryGuard->setName("ls.guard0");
-}
-
-// Materialize each partition's start and clamped end in the entry guard and
-// flag the partitions that are provably empty at compile time.
-void LoopSplitUtils::expandPartitionBounds(SplitState &S,
-                                           SCEVExpander &Expander) {
-  Type *IndTy = S.Induction->getType();
-  Instruction *EntryGuardTerm = Partitions[0].GuardBlock->getTerminator();
-
-  // Expand all partition bounds in the entry guard, which dominates the whole
-  // chain (a skipped partition bypasses the original preheader).
-  const unsigned N = getNumPartitions();
-  for (unsigned I = 0; I < N; ++I) {
-    PartitionInfo &P = Partitions[I];
-
-    // Provably empty when Start overshoots End by exactly one step.
-    // Compile-time only: a runtime overshoot wraps at the type extreme 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();
-      P.Empty = S.Descending ? W.isAllOnes() : W.isOne();
-    }
-
-    P.StartVal = Expander.expandCodeFor(P.StartExpr, IndTy, EntryGuardTerm);
-
-    // Clamp the end to the induction end (min ascending, max descending) so a
-    // short trip count keeps the last iteration in the right partition.
-    const SCEV *ClampedEndSCEV;
-    if (S.Descending)
-      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);
-    P.SelEnd = Expander.expandCodeFor(ClampedEndSCEV, IndTy, EntryGuardTerm);
-  }
-}
-
-// Pass 1: clone each later partition's sub-loop and create its guard and exit
-// blocks (partition 0 reuses the original loop).
-void LoopSplitUtils::clonePartitions(SplitState &S) {
-  Function &F = *L->getHeader()->getParent();
-  LLVMContext &Ctx = F.getContext();
-
-  const unsigned N = getNumPartitions();
-  // Partition 0 reuses the original loop; clone the rest off its preheader.
-  BasicBlock *OrigPreheader = Partitions[0].Preheader;
-
-  for (unsigned I = 1; I < N; ++I) {
-    PartitionInfo &P = Partitions[I];
-    // Persist this partition's original-to-clone map so callers can later
-    // query the counterpart of an original loop value (getPartitionValue()).
-    P.VMap = std::make_unique<ValueToValueMapTy>();
-    ValueToValueMapTy &VMap = *P.VMap;
-    SmallVector<BasicBlock *, 8> ClonedBlocks;
-    Loop *PL = cloneLoopWithPreheader(S.FinalExit, OrigPreheader, L, VMap,
-                                      ".ls" + Twine(I), LI, DT, ClonedBlocks);
-    remapInstructionsInBlocks(ClonedBlocks, VMap);
-    BasicBlock *PHi = PL->getLoopPreheader();
-
-    BasicBlock *Exiti =
-        BasicBlock::Create(Ctx, "ls.exit" + Twine(I), &F, S.FinalExit);
-    BasicBlock *Guardi =
-        BasicBlock::Create(Ctx, "ls.guard" + Twine(I), &F, PHi);
-    if (S.OuterLoop) {
-      S.OuterLoop->addBasicBlockToLoop(Exiti, *LI);
-      S.OuterLoop->addBasicBlockToLoop(Guardi, *LI);
-    }
-    // Placeholder terminators; both are re-pointed at the merge in pass 2.
-    UncondBrInst::Create(S.FinalExit, Exiti);
-    UncondBrInst::Create(S.FinalExit, Guardi);
-
-    // Seed the clone's induction PHI with this partition's start value.
-    auto *ClonedInduction = cast<PHINode>(VMap[S.Induction]);
-    ClonedInduction->setIncomingValueForBlock(PHi, P.StartVal);
-
-    P.GuardBlock = Guardi;
-    P.Preheader = PHi;
-    P.Exit = Exiti;
-    P.SubLoop = PL;
-    P.LatchIndOp = VMap.lookup_or(LatchIndOperand, LatchIndOperand);
-
-    for (auto &EV : S.Escaping) {
-      EV.PerPartitionDef[I] = VMap.lookup_or(EV.Def, EV.Def);
-      if (EV.CarriedHeaderPHI)
-        EV.PerPartitionPHI[I] = cast<PHINode>(VMap[EV.CarriedHeaderPHI]);
-    }
-  }
-}
-
-// Replace a partition's latch test so it iterates only within [start, SelEnd].
-static void rewriteLatch(Loop *PL, Value *IndOp, Value *SelEnd,
-                         BasicBlock *Exit, bool Signed, bool Descending,
-                         bool LatchComparesPHI) {
-  auto *Term = cast<CondBrInst>(PL->getLoopLatch()->getTerminator());
-  auto *Cmp = cast<ICmpInst>(Term->getCondition());
-  IRBuilder<> B(Cmp);
-  Value *Bound = SelEnd;
-  if (Bound->getType() != IndOp->getType())
-    Bound = B.CreateIntCast(Bound, IndOp->getType(), Signed);
-  // Strict when the PHI itself is compared, inclusive when the step value is.
-  ICmpInst::Predicate Pred = continuePredicate(Signed, Descending,
-                                               /*Inclusive=*/!LatchComparesPHI);
-  Value *NewCmp = B.CreateICmp(Pred, IndOp, Bound, "itr.chk");
-  B.SetInsertPoint(Term);
-  auto *NewBr = B.CreateCondBr(NewCmp, PL->getHeader(), Exit);
-  // Carry the original latch's weights over, mapping by which successor stayed
-  // in the loop.
-  uint64_t TrueW, FalseW;
-  if (extractBranchWeights(*Term, TrueW, FalseW)) {
-    bool Succ0InLoop = PL->contains(Term->getSuccessor(0));
-    setFittedBranchWeights(
-        *NewBr, {Succ0InLoop ? TrueW : FalseW, Succ0InLoop ? FalseW : TrueW},
-        /*IsExpected=*/false);
-  }
-  Term->eraseFromParent();
-  if (Cmp->use_empty())
-    Cmp->eraseFromParent();
-}
-
-// Pass 2: emit each partition's guard branch, clamp its latch, wire the
-// partitions into a chain, and update the dominator tree.
-void LoopSplitUtils::chainPartitions(SplitState &S) {
-  const ICmpInst::Predicate GuardPred =
-      guardPredicate(InductionIsSigned, S.Descending);
-
-  // Emit each guard, clamp each latch, and chain partitions; a skipped
-  // partition falls through to the next guard.
-  const unsigned N = getNumPartitions();
-
-  // Enters unconditionally when the caller opted out of the guard and the
-  // partition is not provably empty; a proven-empty partition always skips.
-  auto EntersUnconditionally = [](const PartitionInfo &P) {
-    return !P.Empty && !P.Guarded;
-  };
-
-  // Where control goes when partition Idx is skipped or after it finishes: the
-  // next partition's guard, or the final merge block for the last partition.
-  auto MergeTargetAfter = [&](unsigned Idx) -> BasicBlock * {
-    bool IsLastPartition = Idx + 1 == N;
-    return IsLastPartition ? S.FinalExit : Partitions[Idx + 1].GuardBlock;
-  };
-
-  for (unsigned I = 0; I < N; ++I) {
-    PartitionInfo &P = Partitions[I];
-    BasicBlock *MergeAfter = MergeTargetAfter(I);
-
-    Instruction *GuardTerm = P.GuardBlock->getTerminator();
-    IRBuilder<> B(GuardTerm);
-    if (P.Empty) {
-      // Provably empty: skip to the next partition. The unreachable loop body
-      // is removed by later passes.
-      B.CreateBr(MergeAfter);
-    } else if (!P.Guarded) {
-      // Caller guaranteed at least one iteration: enter unconditionally. The
-      // skip edge to MergeAfter is omitted (see DT update below).
-      B.CreateBr(P.Preheader);
-    } else {
-      Value *Enter = B.CreateICmp(GuardPred, P.StartVal, P.SelEnd, "itr.chk");
-      auto *GuardBr = B.CreateCondBr(Enter, P.Preheader, MergeAfter);
-      // New control flow with no source profile; record the weights as unknown
-      // so profile-tracking passes are not misled.
-      setExplicitlyUnknownBranchWeightsIfProfiled(*GuardBr, DEBUG_TYPE);
-    }
-    GuardTerm->eraseFromParent();
-
-    rewriteLatch(P.SubLoop, P.LatchIndOp, P.SelEnd, P.Exit, InductionIsSigned,
-                 S.Descending, S.LatchComparesPHI);
-    P.Exit->getTerminator()->setSuccessor(0, MergeAfter);
-  }
-
-  // Patch the dominator tree directly: a merge target is dominated by the prior
-  // partition's exit when it enters unconditionally, otherwise by its guard.
-  auto MergeTargetIDom = [&](const PartitionInfo &P) {
-    return EntersUnconditionally(P) ? P.Exit : P.GuardBlock;
-  };
-
-  for (unsigned I = 1; I < N; ++I) {
-    PartitionInfo &Prev = Partitions[I - 1];
-    PartitionInfo &Cur = Partitions[I];
-    DT->addNewBlock(Cur.GuardBlock, MergeTargetIDom(Prev));
-    DT->changeImmediateDominator(Cur.Preheader, Cur.GuardBlock);
-    DT->addNewBlock(Cur.Exit, Cur.SubLoop->getLoopLatch());
-  }
-  // The final exit is the last partition's merge target.
-  DT->changeImmediateDominator(S.FinalExit, MergeTargetIDom(Partitions.back()));
-}
-
-// Rebuild SSA for every escaping value, repairing outside uses and seeding each
-// later partition's carried PHI, using one SSAUpdater per value.
-void LoopSplitUtils::reconstructSSA(SplitState &S) {
-  const unsigned N = getNumPartitions();
-  for (auto &EV : S.Escaping) {
-    SSAUpdater Updater;
-    Updater.Initialize(EV.Def->getType(), EV.Def->getName());
-
-    // Value before any partition runs: carried PHI's initial value, else
-    // poison.
-    Value *Init = EV.CarriedHeaderPHI
-                      ? EV.CarriedHeaderPHI->getIncomingValueForBlock(
-                            Partitions[0].Preheader)
-                      : PoisonValue::get(EV.Def->getType());
-    Updater.AddAvailableValue(Partitions[0].GuardBlock, Init);
-    for (unsigned I = 0; I < N; ++I)
-      Updater.AddAvailableValue(Partitions[I].Exit, EV.PerPartitionDef[I]);
-
-    // Repair outside uses before the carried-PHI seeds add new in-clone uses.
-    // make_early_inc_range advances past each use before RewriteUse() unlinks
-    // it from Def's use-list, so the rewrite cannot invalidate the iteration.
-    if (EV.EscapesOutside)
-      for (Use &U : make_early_inc_range(EV.Def->uses()))
-        if (auto *User = dyn_cast<Instruction>(U.getUser()))
-          if (!L->contains(User))
-            Updater.RewriteUse(U);
-
-    // Seed each later partition's carried PHI from the preceding partitions.
-    if (EV.CarriedHeaderPHI)
-      for (unsigned I = 1; I < N; ++I) {
-        PHINode *CarriedPHI = EV.PerPartitionPHI[I];
-        int PreheaderEntryIdx =
-            CarriedPHI->getBasicBlockIndex(Partitions[I].Preheader);
-        assert(PreheaderEntryIdx >= 0 && "cloned preheader edge missing");
-        Updater.RewriteUse(CarriedPHI->getOperandUse(PreheaderEntryIdx));
-      }
-  }
-}
diff --git a/llvm/lib/Transforms/Utils/LoopSplitUtilsPass.cpp b/llvm/lib/Transforms/Utils/LoopSplitUtilsPass.cpp
deleted file mode 100644
index ccdc42bc64ed7..0000000000000
--- a/llvm/lib/Transforms/Utils/LoopSplitUtilsPass.cpp
+++ /dev/null
@@ -1,148 +0,0 @@
-//===- LoopSplitUtilsPass.cpp - Test driver for LoopSplitUtils ------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-//
-// This pass drives LoopSplitUtils from `opt` for testing. For every eligible
-// loop it builds partitions from the -loop-split-points offsets and splits the
-// loop.
-//
-//===----------------------------------------------------------------------===//
-
-#include "llvm/Transforms/Utils/LoopSplitUtilsPass.h"
-#include "llvm/ADT/SmallVector.h"
-#include "llvm/Analysis/LoopInfo.h"
-#include "llvm/Analysis/ScalarEvolution.h"
-#include "llvm/Analysis/ScalarEvolutionExpressions.h"
-#include "llvm/Analysis/ScalarEvolutionPatternMatch.h"
-#include "llvm/IR/Dominators.h"
-#include "llvm/IR/Function.h"
-#include "llvm/IR/ValueHandle.h"
-#include "llvm/Support/CommandLine.h"
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/raw_ostream.h"
-#include "llvm/Transforms/Utils/LoopSplitUtils.h"
-
-using namespace llvm;
-using namespace llvm::SCEVPatternMatch;
-
-#define DEBUG_TYPE "loop-split-utils"
-
-static cl::list<unsigned>
-    SplitPoints("loop-split-points",
-                cl::desc("Iteration offsets (relative to the induction start) "
-                         "at which to split each loop"),
-                cl::CommaSeparated);
-
-static cl::list<unsigned> UnguardedPartitions(
-    "loop-split-unguarded",
-    cl::desc("Partition indices whose entry guard is omitted (the caller "
-             "guarantees they run at least one iteration)"),
-    cl::CommaSeparated);
-
-/// Build the partition list for \p L from the command-line split offsets and
-/// run the transform. Returns true if the loop was split.
-static bool splitLoop(Loop *L, ScalarEvolution &SE, DominatorTree &DT,
-                      LoopInfo &LI) {
-  LoopSplitUtils LSU(L, &LI, &SE, &DT);
-  if (!LSU.isLegal()) {
-    LLVM_DEBUG(dbgs() << DEBUG_TYPE ": loop is not legal for splitting\n");
-    return false;
-  }
-
-  const SCEV *IndVarSCEV = SE.getSCEV(LSU.getInductionVariable());
-  const SCEV *Start;
-  const APInt *StepC;
-  if (!match(IndVarSCEV,
-             m_scev_AffineAddRec(m_SCEV(Start), m_scev_APInt(StepC))))
-    return false;
-  auto *IndAR = cast<SCEVAddRecExpr>(IndVarSCEV);
-
-  const SCEV *BTC = SE.getBackedgeTakenCount(L);
-  const SCEV *End = IndAR->evaluateAtIteration(BTC, SE);
-  Type *Ty = Start->getType();
-  if (End->getType() != Ty)
-    End = SE.getTruncateExpr(End, Ty);
-
-  // Build boundaries in iteration order, stepping away from Start by each
-  // offset (down for a descending loop). Each offset opens a new partition at
-  // iteration `Start +/- offset`; the previous partition ends one step before.
-  bool Descending = StepC->isAllOnes();
-
-  const SCEV *PrevStart = Start;
-  const SCEV *One = SE.getOne(Ty);
-  for (unsigned Offset : SplitPoints) {
-    const SCEV *Off = SE.getConstant(Ty, Offset);
-    const SCEV *Point =
-        Descending ? SE.getMinusSCEV(Start, Off) : SE.getAddExpr(Start, Off);
-    const SCEV *PrevEnd =
-        Descending ? SE.getAddExpr(Point, One) : SE.getMinusSCEV(Point, One);
-    LSU.addPartition(PrevStart, PrevEnd);
-    PrevStart = Point;
-  }
-  // The final partition runs to the iteration-space end.
-  LSU.addPartition(PrevStart, End);
-
-  // Suppress guards for the partitions the caller listed (out-of-range indices
-  // are ignored).
-  for (unsigned Idx : UnguardedPartitions)
-    if (Idx < LSU.getNumPartitions())
-      LSU.avoidPartitionGuard(Idx);
-
-  if (LSU.getNumPartitions() < 2)
-    return false;
-
-  // Snapshot the original loop's named instructions before the transform so we
-  // can query their per-partition counterparts afterwards (handles track any
-  // that the transform deletes). Only used to print the debug map below.
-  [[maybe_unused]] SmallVector<WeakTrackingVH, 16> OrigValues;
-  LLVM_DEBUG({
-    for (BasicBlock *BB : L->blocks())
-      for (Instruction &I : *BB)
-        if (I.hasName())
-          OrigValues.push_back(&I);
-  });
-
-  if (!LSU.split())
-    return false;
-
-  LLVM_DEBUG({
-    const unsigned N = LSU.getNumPartitions();
-    for (unsigned P = 0; P < N; ++P) {
-      dbgs() << "LS-MAP partition " << P << ":\n";
-      for (WeakTrackingVH &VH : OrigValues) {
-        if (!VH)
-          continue;
-        Value *M = LSU.getPartitionValue(VH, P);
-        dbgs() << "LS-MAP   " << VH->getName() << " -> "
-               << (M ? M->getName() : "<none>") << "\n";
-      }
-    }
-  });
-  return true;
-}
-
-PreservedAnalyses LoopSplitUtilsPass::run(Function &F,
-                                          FunctionAnalysisManager &AM) {
-  if (SplitPoints.empty())
-    return PreservedAnalyses::all();
-
-  auto &LI = AM.getResult<LoopAnalysis>(F);
-  auto &SE = AM.getResult<ScalarEvolutionAnalysis>(F);
-  auto &DT = AM.getResult<DominatorTreeAnalysis>(F);
-
-  // Collect the original top-level loops up front; the transform creates new
-  // sub-loops that we must not revisit.
-  SmallVector<Loop *, 4> Worklist(LI.begin(), LI.end());
-
-  bool Changed = false;
-  for (Loop *L : Worklist) {
-    SE.forgetLoop(L);
-    Changed |= splitLoop(L, SE, DT, LI);
-  }
-
-  return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all();
-}
diff --git a/llvm/test/Transforms/LoopSplit/basic.ll b/llvm/test/Transforms/LoopSplit/basic.ll
deleted file mode 100644
index 69b17f76f8f0b..0000000000000
--- a/llvm/test/Transforms/LoopSplit/basic.ll
+++ /dev/null
@@ -1,61 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
-; RUN: opt -passes=loop-split-utils -loop-split-points=50 -S < %s | FileCheck %s
-
-; A single counted loop split into two partitions at iteration 50.
-
-define void @basic(ptr %a, i64 %n) {
-; CHECK-LABEL: define void @basic(
-; CHECK-SAME: ptr [[A:%.*]], i64 [[N:%.*]]) {
-; CHECK-NEXT:  [[LS_GUARD0:.*:]]
-; CHECK-NEXT:    [[SMAX:%.*]] = call i64 @llvm.smax.i64(i64 [[N]], i64 1)
-; CHECK-NEXT:    [[TMP0:%.*]] = add nsw i64 [[SMAX]], -1
-; CHECK-NEXT:    [[SMIN:%.*]] = call i64 @llvm.smin.i64(i64 [[TMP0]], i64 49)
-; CHECK-NEXT:    [[ITR_CHK:%.*]] = icmp sle i64 0, [[SMIN]]
-; CHECK-NEXT:    br i1 [[ITR_CHK]], label %[[ENTRY:.*]], label %[[LS_GUARD1:.*]]
-; CHECK:       [[ENTRY]]:
-; CHECK-NEXT:    br label %[[LOOP:.*]]
-; CHECK:       [[LOOP]]:
-; CHECK-NEXT:    [[I:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[I_NEXT:%.*]], %[[LOOP]] ]
-; CHECK-NEXT:    [[P:%.*]] = getelementptr i64, ptr [[A]], i64 [[I]]
-; CHECK-NEXT:    store i64 [[I]], ptr [[P]], align 4
-; CHECK-NEXT:    [[I_NEXT]] = add i64 [[I]], 1
-; CHECK-NEXT:    [[ITR_CHK1:%.*]] = icmp sle i64 [[I_NEXT]], [[SMIN]]
-; CHECK-NEXT:    br i1 [[ITR_CHK1]], label %[[LOOP]], label %[[EXIT:.*]]
-; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    br label %[[LS_GUARD1]]
-; CHECK:       [[LS_GUARD1]]:
-; CHECK-NEXT:    [[ITR_CHK2:%.*]] = icmp sle i64 50, [[TMP0]]
-; CHECK-NEXT:    br i1 [[ITR_CHK2]], label %[[ENTRY_LS1:.*]], label %[[LS_FINAL_EXIT:.*]]
-; CHECK:       [[ENTRY_LS1]]:
-; CHECK-NEXT:    br label %[[LOOP_LS1:.*]]
-; CHECK:       [[LOOP_LS1]]:
-; CHECK-NEXT:    [[I_LS1:%.*]] = phi i64 [ 50, %[[ENTRY_LS1]] ], [ [[I_NEXT_LS1:%.*]], %[[LOOP_LS1]] ]
-; CHECK-NEXT:    [[P_LS1:%.*]] = getelementptr i64, ptr [[A]], i64 [[I_LS1]]
-; CHECK-NEXT:    store i64 [[I_LS1]], ptr [[P_LS1]], align 4
-; CHECK-NEXT:    [[I_NEXT_LS1]] = add i64 [[I_LS1]], 1
-; CHECK-NEXT:    [[ITR_CHK3:%.*]] = icmp sle i64 [[I_NEXT_LS1]], [[TMP0]]
-; CHECK-NEXT:    br i1 [[ITR_CHK3]], label %[[LOOP_LS1]], label %[[LS_EXIT1:.*]]
-; CHECK:       [[LS_EXIT1]]:
-; CHECK-NEXT:    br label %[[LS_FINAL_EXIT]]
-; CHECK:       [[LS_FINAL_EXIT]]:
-; CHECK-NEXT:    ret void
-;
-; Each partition's latch is clamped to min(partitionEnd, loopEnd), emitted as a
-; signed-min intrinsic for a signed induction.
-; A later partition is entered only if a runtime check passes, and a second,
-; cloned loop drives the remaining iterations.
-; The transform builds a dedicated final-exit block joining all partitions.
-entry:
-  br label %loop
-
-loop:
-  %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
-  %p = getelementptr i64, ptr %a, i64 %i
-  store i64 %i, ptr %p
-  %i.next = add i64 %i, 1
-  %c = icmp slt i64 %i.next, %n
-  br i1 %c, label %loop, label %exit
-
-exit:
-  ret void
-}
diff --git a/llvm/test/Transforms/LoopSplit/branch-weights.ll b/llvm/test/Transforms/LoopSplit/branch-weights.ll
deleted file mode 100644
index a92ec7dc3cd70..0000000000000
--- a/llvm/test/Transforms/LoopSplit/branch-weights.ll
+++ /dev/null
@@ -1,67 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
-; RUN: opt -passes=loop-split-utils -loop-split-points=50 -S < %s | FileCheck %s
-
-; The original latch carries branch weights; splitting must propagate them onto
-; each partition's clamped latch. The newly created partition guards have no
-; source profile, so in a profiled function they are marked with unknown
-; weights.
-
-define void @basic(ptr %a, i64 %n) !prof !0 {
-; CHECK-LABEL: define void @basic(
-; CHECK-SAME: ptr [[A:%.*]], i64 [[N:%.*]]) !prof [[PROF0:![0-9]+]] {
-; CHECK-NEXT:  [[LS_GUARD0:.*:]]
-; CHECK-NEXT:    [[SMAX:%.*]] = call i64 @llvm.smax.i64(i64 [[N]], i64 1)
-; CHECK-NEXT:    [[TMP0:%.*]] = add nsw i64 [[SMAX]], -1
-; CHECK-NEXT:    [[SMIN:%.*]] = call i64 @llvm.smin.i64(i64 [[TMP0]], i64 49)
-; CHECK-NEXT:    [[ITR_CHK:%.*]] = icmp sle i64 0, [[SMIN]]
-; CHECK-NEXT:    br i1 [[ITR_CHK]], label %[[ENTRY:.*]], label %[[LS_GUARD1:.*]], !prof [[PROF1:![0-9]+]]
-; CHECK:       [[ENTRY]]:
-; CHECK-NEXT:    br label %[[LOOP:.*]]
-; CHECK:       [[LOOP]]:
-; CHECK-NEXT:    [[I:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[I_NEXT:%.*]], %[[LOOP]] ]
-; CHECK-NEXT:    [[P:%.*]] = getelementptr i64, ptr [[A]], i64 [[I]]
-; CHECK-NEXT:    store i64 [[I]], ptr [[P]], align 4
-; CHECK-NEXT:    [[I_NEXT]] = add i64 [[I]], 1
-; CHECK-NEXT:    [[ITR_CHK1:%.*]] = icmp sle i64 [[I_NEXT]], [[SMIN]]
-; CHECK-NEXT:    br i1 [[ITR_CHK1]], label %[[LOOP]], label %[[EXIT:.*]], !prof [[PROF2:![0-9]+]]
-; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    br label %[[LS_GUARD1]]
-; CHECK:       [[LS_GUARD1]]:
-; CHECK-NEXT:    [[ITR_CHK2:%.*]] = icmp sle i64 50, [[TMP0]]
-; CHECK-NEXT:    br i1 [[ITR_CHK2]], label %[[ENTRY_LS1:.*]], label %[[LS_FINAL_EXIT:.*]], !prof [[PROF1]]
-; CHECK:       [[ENTRY_LS1]]:
-; CHECK-NEXT:    br label %[[LOOP_LS1:.*]]
-; CHECK:       [[LOOP_LS1]]:
-; CHECK-NEXT:    [[I_LS1:%.*]] = phi i64 [ 50, %[[ENTRY_LS1]] ], [ [[I_NEXT_LS1:%.*]], %[[LOOP_LS1]] ]
-; CHECK-NEXT:    [[P_LS1:%.*]] = getelementptr i64, ptr [[A]], i64 [[I_LS1]]
-; CHECK-NEXT:    store i64 [[I_LS1]], ptr [[P_LS1]], align 4
-; CHECK-NEXT:    [[I_NEXT_LS1]] = add i64 [[I_LS1]], 1
-; CHECK-NEXT:    [[ITR_CHK3:%.*]] = icmp sle i64 [[I_NEXT_LS1]], [[TMP0]]
-; CHECK-NEXT:    br i1 [[ITR_CHK3]], label %[[LOOP_LS1]], label %[[LS_EXIT1:.*]], !prof [[PROF2]]
-; CHECK:       [[LS_EXIT1]]:
-; CHECK-NEXT:    br label %[[LS_FINAL_EXIT]]
-; CHECK:       [[LS_FINAL_EXIT]]:
-; CHECK-NEXT:    ret void
-;
-entry:
-  br label %loop
-
-loop:
-  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
-  %p = getelementptr i64, ptr %a, i64 %iv
-  store i64 %iv, ptr %p
-  %iv.next = add i64 %iv, 1
-  %ec = icmp slt i64 %iv.next, %n
-  br i1 %ec, label %loop, label %exit, !prof !1
-
-exit:
-  ret void
-}
-
-!0 = !{!"function_entry_count", i64 1000}
-!1 = !{!"branch_weights", i32 100, i32 1}
-;.
-; CHECK: [[PROF0]] = !{!"function_entry_count", i64 1000}
-; CHECK: [[PROF1]] = !{!"unknown", !"loop-split-utils"}
-; CHECK: [[PROF2]] = !{!"branch_weights", i32 100, i32 1}
-;.
diff --git a/llvm/test/Transforms/LoopSplit/constant-trip-count.ll b/llvm/test/Transforms/LoopSplit/constant-trip-count.ll
deleted file mode 100644
index 19d553767e71a..0000000000000
--- a/llvm/test/Transforms/LoopSplit/constant-trip-count.ll
+++ /dev/null
@@ -1,89 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
-; RUN: opt -passes=loop-split-utils -loop-split-points=50 -S < %s \
-; RUN:   | FileCheck %s --check-prefixes=VALID
-; RUN: opt -passes=loop-split-utils -loop-split-points=200 -S < %s \
-; RUN:   | FileCheck %s --check-prefixes=INVALID
-
-; With a constant trip count (100) the per-partition bound and clamp arithmetic
-; folds at compile time. A split point of 50 lands inside [0,99] and yields two
-; non-empty partitions [0,49] and [50,99]. A split point of 200 is past the last
-; iteration, so the second partition's `Start <= End` guard folds to a constant
-; false and that partition is never entered.
-
-define void @constant_tc(ptr %a) {
-; VALID-LABEL: define void @constant_tc(
-; VALID-SAME: ptr [[A:%.*]]) {
-; VALID-NEXT:  [[LS_GUARD0:.*:]]
-; VALID-NEXT:    br i1 true, label %[[ENTRY:.*]], label %[[LS_GUARD1:.*]]
-; VALID:       [[ENTRY]]:
-; VALID-NEXT:    br label %[[LOOP:.*]]
-; VALID:       [[LOOP]]:
-; VALID-NEXT:    [[I:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[I_NEXT:%.*]], %[[LOOP]] ]
-; VALID-NEXT:    [[P:%.*]] = getelementptr i64, ptr [[A]], i64 [[I]]
-; VALID-NEXT:    store i64 [[I]], ptr [[P]], align 4
-; VALID-NEXT:    [[I_NEXT]] = add i64 [[I]], 1
-; VALID-NEXT:    [[ITR_CHK:%.*]] = icmp sle i64 [[I_NEXT]], 49
-; VALID-NEXT:    br i1 [[ITR_CHK]], label %[[LOOP]], label %[[EXIT:.*]]
-; VALID:       [[EXIT]]:
-; VALID-NEXT:    br label %[[LS_GUARD1]]
-; VALID:       [[LS_GUARD1]]:
-; VALID-NEXT:    br i1 true, label %[[ENTRY_LS1:.*]], label %[[LS_FINAL_EXIT:.*]]
-; VALID:       [[ENTRY_LS1]]:
-; VALID-NEXT:    br label %[[LOOP_LS1:.*]]
-; VALID:       [[LOOP_LS1]]:
-; VALID-NEXT:    [[I_LS1:%.*]] = phi i64 [ 50, %[[ENTRY_LS1]] ], [ [[I_NEXT_LS1:%.*]], %[[LOOP_LS1]] ]
-; VALID-NEXT:    [[P_LS1:%.*]] = getelementptr i64, ptr [[A]], i64 [[I_LS1]]
-; VALID-NEXT:    store i64 [[I_LS1]], ptr [[P_LS1]], align 4
-; VALID-NEXT:    [[I_NEXT_LS1]] = add i64 [[I_LS1]], 1
-; VALID-NEXT:    [[ITR_CHK1:%.*]] = icmp sle i64 [[I_NEXT_LS1]], 99
-; VALID-NEXT:    br i1 [[ITR_CHK1]], label %[[LOOP_LS1]], label %[[LS_EXIT1:.*]]
-; VALID:       [[LS_EXIT1]]:
-; VALID-NEXT:    br label %[[LS_FINAL_EXIT]]
-; VALID:       [[LS_FINAL_EXIT]]:
-; VALID-NEXT:    ret void
-;
-; INVALID-LABEL: define void @constant_tc(
-; INVALID-SAME: ptr [[A:%.*]]) {
-; INVALID-NEXT:  [[LS_GUARD0:.*:]]
-; INVALID-NEXT:    br i1 true, label %[[ENTRY:.*]], label %[[LS_GUARD1:.*]]
-; INVALID:       [[ENTRY]]:
-; INVALID-NEXT:    br label %[[LOOP:.*]]
-; INVALID:       [[LOOP]]:
-; INVALID-NEXT:    [[I:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[I_NEXT:%.*]], %[[LOOP]] ]
-; INVALID-NEXT:    [[P:%.*]] = getelementptr i64, ptr [[A]], i64 [[I]]
-; INVALID-NEXT:    store i64 [[I]], ptr [[P]], align 4
-; INVALID-NEXT:    [[I_NEXT]] = add i64 [[I]], 1
-; INVALID-NEXT:    [[ITR_CHK:%.*]] = icmp sle i64 [[I_NEXT]], 99
-; INVALID-NEXT:    br i1 [[ITR_CHK]], label %[[LOOP]], label %[[EXIT:.*]]
-; INVALID:       [[EXIT]]:
-; INVALID-NEXT:    br label %[[LS_GUARD1]]
-; INVALID:       [[LS_GUARD1]]:
-; INVALID-NEXT:    br i1 false, label %[[ENTRY_LS1:.*]], label %[[LS_FINAL_EXIT:.*]]
-; INVALID:       [[ENTRY_LS1]]:
-; INVALID-NEXT:    br label %[[LOOP_LS1:.*]]
-; INVALID:       [[LOOP_LS1]]:
-; INVALID-NEXT:    [[I_LS1:%.*]] = phi i64 [ 200, %[[ENTRY_LS1]] ], [ [[I_NEXT_LS1:%.*]], %[[LOOP_LS1]] ]
-; INVALID-NEXT:    [[P_LS1:%.*]] = getelementptr i64, ptr [[A]], i64 [[I_LS1]]
-; INVALID-NEXT:    store i64 [[I_LS1]], ptr [[P_LS1]], align 4
-; INVALID-NEXT:    [[I_NEXT_LS1]] = add i64 [[I_LS1]], 1
-; INVALID-NEXT:    [[ITR_CHK1:%.*]] = icmp sle i64 [[I_NEXT_LS1]], 99
-; INVALID-NEXT:    br i1 [[ITR_CHK1]], label %[[LOOP_LS1]], label %[[LS_EXIT1:.*]]
-; INVALID:       [[LS_EXIT1]]:
-; INVALID-NEXT:    br label %[[LS_FINAL_EXIT]]
-; INVALID:       [[LS_FINAL_EXIT]]:
-; INVALID-NEXT:    ret void
-;
-entry:
-  br label %loop
-
-loop:
-  %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
-  %p = getelementptr i64, ptr %a, i64 %i
-  store i64 %i, ptr %p
-  %i.next = add i64 %i, 1
-  %c = icmp slt i64 %i.next, 100
-  br i1 %c, label %loop, label %exit
-
-exit:
-  ret void
-}
diff --git a/llvm/test/Transforms/LoopSplit/descending.ll b/llvm/test/Transforms/LoopSplit/descending.ll
deleted file mode 100644
index 2acddcd7e93b2..0000000000000
--- a/llvm/test/Transforms/LoopSplit/descending.ll
+++ /dev/null
@@ -1,60 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
-; RUN: opt -passes=loop-split-utils -loop-split-points=50 -S < %s | FileCheck %s
-
-; A signed counting-down loop (step -1) split into two partitions. The guard and
-; latch predicates flip to >=, and the end clamp uses smax instead of smin.
-
-define void @descending(ptr %a, i64 %n) {
-; CHECK-LABEL: define void @descending(
-; CHECK-SAME: ptr [[A:%.*]], i64 [[N:%.*]]) {
-; CHECK-NEXT:  [[LS_GUARD0:.*:]]
-; CHECK-NEXT:    [[TMP0:%.*]] = add i64 [[N]], -49
-; CHECK-NEXT:    [[TMP1:%.*]] = add i64 [[N]], -1
-; CHECK-NEXT:    [[SMIN:%.*]] = call i64 @llvm.smin.i64(i64 [[TMP1]], i64 -1)
-; CHECK-NEXT:    [[TMP2:%.*]] = add nsw i64 [[SMIN]], 1
-; CHECK-NEXT:    [[SMAX:%.*]] = call i64 @llvm.smax.i64(i64 [[TMP0]], i64 [[TMP2]])
-; CHECK-NEXT:    [[TMP3:%.*]] = add i64 [[N]], -50
-; CHECK-NEXT:    [[ITR_CHK:%.*]] = icmp sge i64 [[N]], [[SMAX]]
-; CHECK-NEXT:    br i1 [[ITR_CHK]], label %[[ENTRY:.*]], label %[[LS_GUARD1:.*]]
-; CHECK:       [[ENTRY]]:
-; CHECK-NEXT:    br label %[[LOOP:.*]]
-; CHECK:       [[LOOP]]:
-; CHECK-NEXT:    [[I:%.*]] = phi i64 [ [[N]], %[[ENTRY]] ], [ [[I_NEXT:%.*]], %[[LOOP]] ]
-; CHECK-NEXT:    [[P:%.*]] = getelementptr i64, ptr [[A]], i64 [[I]]
-; CHECK-NEXT:    store i64 [[I]], ptr [[P]], align 4
-; CHECK-NEXT:    [[I_NEXT]] = add i64 [[I]], -1
-; CHECK-NEXT:    [[ITR_CHK1:%.*]] = icmp sge i64 [[I_NEXT]], [[SMAX]]
-; CHECK-NEXT:    br i1 [[ITR_CHK1]], label %[[LOOP]], label %[[EXIT:.*]]
-; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    br label %[[LS_GUARD1]]
-; CHECK:       [[LS_GUARD1]]:
-; CHECK-NEXT:    [[ITR_CHK2:%.*]] = icmp sge i64 [[TMP3]], [[TMP2]]
-; CHECK-NEXT:    br i1 [[ITR_CHK2]], label %[[ENTRY_LS1:.*]], label %[[LS_FINAL_EXIT:.*]]
-; CHECK:       [[ENTRY_LS1]]:
-; CHECK-NEXT:    br label %[[LOOP_LS1:.*]]
-; CHECK:       [[LOOP_LS1]]:
-; CHECK-NEXT:    [[I_LS1:%.*]] = phi i64 [ [[TMP3]], %[[ENTRY_LS1]] ], [ [[I_NEXT_LS1:%.*]], %[[LOOP_LS1]] ]
-; CHECK-NEXT:    [[P_LS1:%.*]] = getelementptr i64, ptr [[A]], i64 [[I_LS1]]
-; CHECK-NEXT:    store i64 [[I_LS1]], ptr [[P_LS1]], align 4
-; CHECK-NEXT:    [[I_NEXT_LS1]] = add i64 [[I_LS1]], -1
-; CHECK-NEXT:    [[ITR_CHK3:%.*]] = icmp sge i64 [[I_NEXT_LS1]], [[TMP2]]
-; CHECK-NEXT:    br i1 [[ITR_CHK3]], label %[[LOOP_LS1]], label %[[LS_EXIT1:.*]]
-; CHECK:       [[LS_EXIT1]]:
-; CHECK-NEXT:    br label %[[LS_FINAL_EXIT]]
-; CHECK:       [[LS_FINAL_EXIT]]:
-; CHECK-NEXT:    ret void
-;
-entry:
-  br label %loop
-
-loop:
-  %i = phi i64 [ %n, %entry ], [ %i.next, %loop ]
-  %p = getelementptr i64, ptr %a, i64 %i
-  store i64 %i, ptr %p
-  %i.next = add i64 %i, -1
-  %c = icmp sge i64 %i.next, 0
-  br i1 %c, label %loop, label %exit
-
-exit:
-  ret void
-}
diff --git a/llvm/test/Transforms/LoopSplit/empty-leading-partition.ll b/llvm/test/Transforms/LoopSplit/empty-leading-partition.ll
deleted file mode 100644
index d547ed7df6c93..0000000000000
--- a/llvm/test/Transforms/LoopSplit/empty-leading-partition.ll
+++ /dev/null
@@ -1,71 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
-; RUN: opt -passes=loop-split-utils -loop-split-points=0 -S < %s | FileCheck %s
-
-; A leading split point of 0 makes partition 0 empty ([Start, Start-1]). The
-; loop is bottom-tested, so without an entry guard partition 0 would still run
-; one iteration and the loop would execute its first iteration twice. The empty
-; partition is detected at compile time (its width S - E folds to a constant)
-; and skipped: partition 0's guard unconditionally falls through to partition 1,
-; which runs the whole iteration space and seeds from the chain-entry value (0).
-
-define i64 @reduction(ptr %a, i64 %n) {
-; CHECK-LABEL: define i64 @reduction(
-; CHECK-SAME: ptr [[A:%.*]], i64 [[N:%.*]]) {
-; CHECK-NEXT:  [[LS_GUARD0:.*]]:
-; CHECK-NEXT:    [[SMAX:%.*]] = call i64 @llvm.smax.i64(i64 [[N]], i64 1)
-; CHECK-NEXT:    [[TMP0:%.*]] = add nsw i64 [[SMAX]], -1
-; CHECK-NEXT:    br label %[[LS_GUARD1:.*]]
-; CHECK:       [[ENTRY:.*]]:
-; CHECK-NEXT:    br label %[[LOOP:.*]]
-; CHECK:       [[LOOP]]:
-; CHECK-NEXT:    [[I:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[I_NEXT:%.*]], %[[LOOP]] ]
-; CHECK-NEXT:    [[SUM:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[SUM_NEXT:%.*]], %[[LOOP]] ]
-; CHECK-NEXT:    [[P:%.*]] = getelementptr i64, ptr [[A]], i64 [[I]]
-; CHECK-NEXT:    [[V:%.*]] = load i64, ptr [[P]], align 4
-; CHECK-NEXT:    [[SUM_NEXT]] = add i64 [[SUM]], [[V]]
-; CHECK-NEXT:    [[I_NEXT]] = add i64 [[I]], 1
-; CHECK-NEXT:    [[ITR_CHK:%.*]] = icmp sle i64 [[I_NEXT]], -1
-; CHECK-NEXT:    br i1 [[ITR_CHK]], label %[[LOOP]], label %[[EXIT:.*]]
-; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    br label %[[LS_GUARD1]]
-; CHECK:       [[LS_GUARD1]]:
-; CHECK-NEXT:    [[SUM_NEXT4:%.*]] = phi i64 [ [[SUM_NEXT]], %[[EXIT]] ], [ 0, %[[LS_GUARD0]] ]
-; CHECK-NEXT:    [[ITR_CHK1:%.*]] = icmp sle i64 0, [[TMP0]]
-; CHECK-NEXT:    br i1 [[ITR_CHK1]], label %[[ENTRY_LS1:.*]], label %[[LS_FINAL_EXIT:.*]]
-; CHECK:       [[ENTRY_LS1]]:
-; CHECK-NEXT:    br label %[[LOOP_LS1:.*]]
-; CHECK:       [[LOOP_LS1]]:
-; CHECK-NEXT:    [[I_LS1:%.*]] = phi i64 [ 0, %[[ENTRY_LS1]] ], [ [[I_NEXT_LS1:%.*]], %[[LOOP_LS1]] ]
-; CHECK-NEXT:    [[SUM_LS1:%.*]] = phi i64 [ [[SUM_NEXT4]], %[[ENTRY_LS1]] ], [ [[SUM_NEXT_LS1:%.*]], %[[LOOP_LS1]] ]
-; CHECK-NEXT:    [[P_LS1:%.*]] = getelementptr i64, ptr [[A]], i64 [[I_LS1]]
-; CHECK-NEXT:    [[V_LS1:%.*]] = load i64, ptr [[P_LS1]], align 4
-; CHECK-NEXT:    [[SUM_NEXT_LS1]] = add i64 [[SUM_LS1]], [[V_LS1]]
-; CHECK-NEXT:    [[I_NEXT_LS1]] = add i64 [[I_LS1]], 1
-; CHECK-NEXT:    [[ITR_CHK2:%.*]] = icmp sle i64 [[I_NEXT_LS1]], [[TMP0]]
-; CHECK-NEXT:    br i1 [[ITR_CHK2]], label %[[LOOP_LS1]], label %[[LS_EXIT1:.*]]
-; CHECK:       [[LS_EXIT1]]:
-; CHECK-NEXT:    br label %[[LS_FINAL_EXIT]]
-; CHECK:       [[LS_FINAL_EXIT]]:
-; CHECK-NEXT:    [[SUM_NEXT3:%.*]] = phi i64 [ [[SUM_NEXT_LS1]], %[[LS_EXIT1]] ], [ [[SUM_NEXT4]], %[[LS_GUARD1]] ]
-; CHECK-NEXT:    ret i64 [[SUM_NEXT3]]
-;
-; The empty leading partition is never entered: an unconditional fall-through.
-; The original loop body is now dead (unreachable), proving it is not executed.
-; The surviving partition runs the full range, seeded from the chain-entry value.
-entry:
-  br label %loop
-
-loop:
-  %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
-  %sum = phi i64 [ 0, %entry ], [ %sum.next, %loop ]
-  %p = getelementptr i64, ptr %a, i64 %i
-  %v = load i64, ptr %p
-  %sum.next = add i64 %sum, %v
-  %i.next = add i64 %i, 1
-  %c = icmp slt i64 %i.next, %n
-  br i1 %c, label %loop, label %exit
-
-exit:
-  %sum.lcssa = phi i64 [ %sum.next, %loop ]
-  ret i64 %sum.lcssa
-}
diff --git a/llvm/test/Transforms/LoopSplit/four-partitions.ll b/llvm/test/Transforms/LoopSplit/four-partitions.ll
deleted file mode 100644
index 8a0d7009fb973..0000000000000
--- a/llvm/test/Transforms/LoopSplit/four-partitions.ll
+++ /dev/null
@@ -1,88 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
-; RUN: opt -passes=loop-split-utils -loop-split-points=100,200,300 -S < %s | FileCheck %s
-
-; A minimal counted loop split into four partitions: [0,99], [100,199],
-; [200,299] and [300,n-1]. Three clones (loop.ls1, loop.ls2, loop.ls3) chain
-; through their guards into a single dedicated final-exit block.
-
-define void @four(ptr %a, i64 %n) {
-; CHECK-LABEL: define void @four(
-; CHECK-SAME: ptr [[A:%.*]], i64 [[N:%.*]]) {
-; CHECK-NEXT:  [[LS_GUARD0:.*:]]
-; CHECK-NEXT:    [[SMAX:%.*]] = call i64 @llvm.smax.i64(i64 [[N]], i64 1)
-; CHECK-NEXT:    [[TMP0:%.*]] = add nsw i64 [[SMAX]], -1
-; CHECK-NEXT:    [[SMIN2:%.*]] = call i64 @llvm.smin.i64(i64 [[TMP0]], i64 99)
-; CHECK-NEXT:    [[SMIN:%.*]] = call i64 @llvm.smin.i64(i64 [[TMP0]], i64 199)
-; CHECK-NEXT:    [[SMIN1:%.*]] = call i64 @llvm.smin.i64(i64 [[TMP0]], i64 299)
-; CHECK-NEXT:    [[ITR_CHK1:%.*]] = icmp sle i64 0, [[SMIN2]]
-; CHECK-NEXT:    br i1 [[ITR_CHK1]], label %[[ENTRY:.*]], label %[[LS_GUARD1:.*]]
-; CHECK:       [[ENTRY]]:
-; CHECK-NEXT:    br label %[[LOOP:.*]]
-; CHECK:       [[LOOP]]:
-; CHECK-NEXT:    [[I:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[I_NEXT:%.*]], %[[LOOP]] ]
-; CHECK-NEXT:    [[P:%.*]] = getelementptr i64, ptr [[A]], i64 [[I]]
-; CHECK-NEXT:    store i64 [[I]], ptr [[P]], align 4
-; CHECK-NEXT:    [[I_NEXT]] = add i64 [[I]], 1
-; CHECK-NEXT:    [[ITR_CHK:%.*]] = icmp sle i64 [[I_NEXT]], [[SMIN2]]
-; CHECK-NEXT:    br i1 [[ITR_CHK]], label %[[LOOP]], label %[[EXIT:.*]]
-; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    br label %[[LS_GUARD1]]
-; CHECK:       [[LS_GUARD1]]:
-; CHECK-NEXT:    [[ITR_CHK2:%.*]] = icmp sle i64 100, [[SMIN]]
-; CHECK-NEXT:    br i1 [[ITR_CHK2]], label %[[ENTRY_LS1:.*]], label %[[LS_GUARD2:.*]]
-; CHECK:       [[ENTRY_LS1]]:
-; CHECK-NEXT:    br label %[[LOOP_LS1:.*]]
-; CHECK:       [[LOOP_LS1]]:
-; CHECK-NEXT:    [[I_LS1:%.*]] = phi i64 [ 100, %[[ENTRY_LS1]] ], [ [[I_NEXT_LS1:%.*]], %[[LOOP_LS1]] ]
-; CHECK-NEXT:    [[P_LS1:%.*]] = getelementptr i64, ptr [[A]], i64 [[I_LS1]]
-; CHECK-NEXT:    store i64 [[I_LS1]], ptr [[P_LS1]], align 4
-; CHECK-NEXT:    [[I_NEXT_LS1]] = add i64 [[I_LS1]], 1
-; CHECK-NEXT:    [[ITR_CHK3:%.*]] = icmp sle i64 [[I_NEXT_LS1]], [[SMIN]]
-; CHECK-NEXT:    br i1 [[ITR_CHK3]], label %[[LOOP_LS1]], label %[[LS_EXIT1:.*]]
-; CHECK:       [[LS_EXIT1]]:
-; CHECK-NEXT:    br label %[[LS_GUARD2]]
-; CHECK:       [[LS_GUARD2]]:
-; CHECK-NEXT:    [[ITR_CHK4:%.*]] = icmp sle i64 200, [[SMIN1]]
-; CHECK-NEXT:    br i1 [[ITR_CHK4]], label %[[ENTRY_LS2:.*]], label %[[LS_GUARD3:.*]]
-; CHECK:       [[ENTRY_LS2]]:
-; CHECK-NEXT:    br label %[[LOOP_LS2:.*]]
-; CHECK:       [[LOOP_LS2]]:
-; CHECK-NEXT:    [[I_LS2:%.*]] = phi i64 [ 200, %[[ENTRY_LS2]] ], [ [[I_NEXT_LS2:%.*]], %[[LOOP_LS2]] ]
-; CHECK-NEXT:    [[P_LS2:%.*]] = getelementptr i64, ptr [[A]], i64 [[I_LS2]]
-; CHECK-NEXT:    store i64 [[I_LS2]], ptr [[P_LS2]], align 4
-; CHECK-NEXT:    [[I_NEXT_LS2]] = add i64 [[I_LS2]], 1
-; CHECK-NEXT:    [[ITR_CHK5:%.*]] = icmp sle i64 [[I_NEXT_LS2]], [[SMIN1]]
-; CHECK-NEXT:    br i1 [[ITR_CHK5]], label %[[LOOP_LS2]], label %[[LS_EXIT2:.*]]
-; CHECK:       [[LS_EXIT2]]:
-; CHECK-NEXT:    br label %[[LS_GUARD3]]
-; CHECK:       [[LS_GUARD3]]:
-; CHECK-NEXT:    [[ITR_CHK6:%.*]] = icmp sle i64 300, [[TMP0]]
-; CHECK-NEXT:    br i1 [[ITR_CHK6]], label %[[ENTRY_LS3:.*]], label %[[LS_FINAL_EXIT:.*]]
-; CHECK:       [[ENTRY_LS3]]:
-; CHECK-NEXT:    br label %[[LOOP_LS3:.*]]
-; CHECK:       [[LOOP_LS3]]:
-; CHECK-NEXT:    [[I_LS3:%.*]] = phi i64 [ 300, %[[ENTRY_LS3]] ], [ [[I_NEXT_LS3:%.*]], %[[LOOP_LS3]] ]
-; CHECK-NEXT:    [[P_LS3:%.*]] = getelementptr i64, ptr [[A]], i64 [[I_LS3]]
-; CHECK-NEXT:    store i64 [[I_LS3]], ptr [[P_LS3]], align 4
-; CHECK-NEXT:    [[I_NEXT_LS3]] = add i64 [[I_LS3]], 1
-; CHECK-NEXT:    [[ITR_CHK7:%.*]] = icmp sle i64 [[I_NEXT_LS3]], [[TMP0]]
-; CHECK-NEXT:    br i1 [[ITR_CHK7]], label %[[LOOP_LS3]], label %[[LS_EXIT3:.*]]
-; CHECK:       [[LS_EXIT3]]:
-; CHECK-NEXT:    br label %[[LS_FINAL_EXIT]]
-; CHECK:       [[LS_FINAL_EXIT]]:
-; CHECK-NEXT:    ret void
-;
-entry:
-  br label %loop
-
-loop:
-  %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
-  %p = getelementptr i64, ptr %a, i64 %i
-  store i64 %i, ptr %p
-  %i.next = add i64 %i, 1
-  %c = icmp slt i64 %i.next, %n
-  br i1 %c, label %loop, label %exit
-
-exit:
-  ret void
-}
diff --git a/llvm/test/Transforms/LoopSplit/multiple-partitions.ll b/llvm/test/Transforms/LoopSplit/multiple-partitions.ll
deleted file mode 100644
index ac4fcbda6dc82..0000000000000
--- a/llvm/test/Transforms/LoopSplit/multiple-partitions.ll
+++ /dev/null
@@ -1,75 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
-; RUN: opt -passes=loop-split-utils -loop-split-points=50,100 -S < %s | FileCheck %s
-
-; A loop split into three partitions: [0,49], [50,99], [100,n-1].
-
-define void @three_way(ptr %a, i64 %n) {
-; CHECK-LABEL: define void @three_way(
-; CHECK-SAME: ptr [[A:%.*]], i64 [[N:%.*]]) {
-; CHECK-NEXT:  [[LS_GUARD0:.*:]]
-; CHECK-NEXT:    [[SMAX:%.*]] = call i64 @llvm.smax.i64(i64 [[N]], i64 1)
-; CHECK-NEXT:    [[TMP0:%.*]] = add nsw i64 [[SMAX]], -1
-; CHECK-NEXT:    [[SMIN:%.*]] = call i64 @llvm.smin.i64(i64 [[TMP0]], i64 49)
-; CHECK-NEXT:    [[SMIN1:%.*]] = call i64 @llvm.smin.i64(i64 [[TMP0]], i64 99)
-; CHECK-NEXT:    [[ITR_CHK:%.*]] = icmp sle i64 0, [[SMIN]]
-; CHECK-NEXT:    br i1 [[ITR_CHK]], label %[[ENTRY:.*]], label %[[LS_GUARD1:.*]]
-; CHECK:       [[ENTRY]]:
-; CHECK-NEXT:    br label %[[LOOP:.*]]
-; CHECK:       [[LOOP]]:
-; CHECK-NEXT:    [[I:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[I_NEXT:%.*]], %[[LOOP]] ]
-; CHECK-NEXT:    [[P:%.*]] = getelementptr i64, ptr [[A]], i64 [[I]]
-; CHECK-NEXT:    store i64 [[I]], ptr [[P]], align 4
-; CHECK-NEXT:    [[I_NEXT]] = add i64 [[I]], 1
-; CHECK-NEXT:    [[ITR_CHK2:%.*]] = icmp sle i64 [[I_NEXT]], [[SMIN]]
-; CHECK-NEXT:    br i1 [[ITR_CHK2]], label %[[LOOP]], label %[[EXIT:.*]]
-; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    br label %[[LS_GUARD1]]
-; CHECK:       [[LS_GUARD1]]:
-; CHECK-NEXT:    [[ITR_CHK3:%.*]] = icmp sle i64 50, [[SMIN1]]
-; CHECK-NEXT:    br i1 [[ITR_CHK3]], label %[[ENTRY_LS1:.*]], label %[[LS_GUARD2:.*]]
-; CHECK:       [[ENTRY_LS1]]:
-; CHECK-NEXT:    br label %[[LOOP_LS1:.*]]
-; CHECK:       [[LOOP_LS1]]:
-; CHECK-NEXT:    [[I_LS1:%.*]] = phi i64 [ 50, %[[ENTRY_LS1]] ], [ [[I_NEXT_LS1:%.*]], %[[LOOP_LS1]] ]
-; CHECK-NEXT:    [[P_LS1:%.*]] = getelementptr i64, ptr [[A]], i64 [[I_LS1]]
-; CHECK-NEXT:    store i64 [[I_LS1]], ptr [[P_LS1]], align 4
-; CHECK-NEXT:    [[I_NEXT_LS1]] = add i64 [[I_LS1]], 1
-; CHECK-NEXT:    [[ITR_CHK4:%.*]] = icmp sle i64 [[I_NEXT_LS1]], [[SMIN1]]
-; CHECK-NEXT:    br i1 [[ITR_CHK4]], label %[[LOOP_LS1]], label %[[LS_EXIT1:.*]]
-; CHECK:       [[LS_EXIT1]]:
-; CHECK-NEXT:    br label %[[LS_GUARD2]]
-; CHECK:       [[LS_GUARD2]]:
-; CHECK-NEXT:    [[ITR_CHK5:%.*]] = icmp sle i64 100, [[TMP0]]
-; CHECK-NEXT:    br i1 [[ITR_CHK5]], label %[[ENTRY_LS2:.*]], label %[[LS_FINAL_EXIT:.*]]
-; CHECK:       [[ENTRY_LS2]]:
-; CHECK-NEXT:    br label %[[LOOP_LS2:.*]]
-; CHECK:       [[LOOP_LS2]]:
-; CHECK-NEXT:    [[I_LS2:%.*]] = phi i64 [ 100, %[[ENTRY_LS2]] ], [ [[I_NEXT_LS2:%.*]], %[[LOOP_LS2]] ]
-; CHECK-NEXT:    [[P_LS2:%.*]] = getelementptr i64, ptr [[A]], i64 [[I_LS2]]
-; CHECK-NEXT:    store i64 [[I_LS2]], ptr [[P_LS2]], align 4
-; CHECK-NEXT:    [[I_NEXT_LS2]] = add i64 [[I_LS2]], 1
-; CHECK-NEXT:    [[ITR_CHK6:%.*]] = icmp sle i64 [[I_NEXT_LS2]], [[TMP0]]
-; CHECK-NEXT:    br i1 [[ITR_CHK6]], label %[[LOOP_LS2]], label %[[LS_EXIT2:.*]]
-; CHECK:       [[LS_EXIT2]]:
-; CHECK-NEXT:    br label %[[LS_FINAL_EXIT]]
-; CHECK:       [[LS_FINAL_EXIT]]:
-; CHECK-NEXT:    ret void
-;
-; Each partition is entered only if a runtime min/iteration check passes; the
-; min(partitionEnd, loopEnd) clamp is a signed-min intrinsic.
-; The original loop plus two clones cover [0,49], [50,99] and [100,n-1], and a
-; dedicated final-exit block joins all three partitions.
-entry:
-  br label %loop
-
-loop:
-  %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
-  %p = getelementptr i64, ptr %a, i64 %i
-  store i64 %i, ptr %p
-  %i.next = add i64 %i, 1
-  %c = icmp slt i64 %i.next, %n
-  br i1 %c, label %loop, label %exit
-
-exit:
-  ret void
-}
diff --git a/llvm/test/Transforms/LoopSplit/nested-loop.ll b/llvm/test/Transforms/LoopSplit/nested-loop.ll
deleted file mode 100644
index a4cb06feb7aa2..0000000000000
--- a/llvm/test/Transforms/LoopSplit/nested-loop.ll
+++ /dev/null
@@ -1,88 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
-; RUN: opt -passes=loop-split-utils -loop-split-points=50 -S < %s | FileCheck %s
-
-; Only the outer (top-level) loop is a split candidate. Splitting it clones the
-; whole loop body, including the inner loop, into the later partition. This
-; exercises that cloning a loop containing a subloop does not crash and that the
-; inner loop is duplicated wholesale rather than split.
-
-define void @nested(ptr %a, i64 %n, i64 %m) {
-; CHECK-LABEL: define void @nested(
-; CHECK-SAME: ptr [[A:%.*]], i64 [[N:%.*]], i64 [[M:%.*]]) {
-; CHECK-NEXT:  [[LS_GUARD0:.*:]]
-; CHECK-NEXT:    [[SMAX:%.*]] = call i64 @llvm.smax.i64(i64 [[N]], i64 1)
-; CHECK-NEXT:    [[TMP0:%.*]] = add nsw i64 [[SMAX]], -1
-; CHECK-NEXT:    [[SMIN:%.*]] = call i64 @llvm.smin.i64(i64 [[TMP0]], i64 49)
-; CHECK-NEXT:    [[ITR_CHK:%.*]] = icmp sle i64 0, [[SMIN]]
-; CHECK-NEXT:    br i1 [[ITR_CHK]], label %[[ENTRY:.*]], label %[[LS_GUARD1:.*]]
-; CHECK:       [[ENTRY]]:
-; CHECK-NEXT:    br label %[[OUTER_HEADER:.*]]
-; CHECK:       [[OUTER_HEADER]]:
-; CHECK-NEXT:    [[I:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[I_NEXT:%.*]], %[[OUTER_LATCH:.*]] ]
-; CHECK-NEXT:    br label %[[INNER_HEADER:.*]]
-; CHECK:       [[INNER_HEADER]]:
-; CHECK-NEXT:    [[J:%.*]] = phi i64 [ 0, %[[OUTER_HEADER]] ], [ [[J_NEXT:%.*]], %[[INNER_HEADER]] ]
-; CHECK-NEXT:    [[BASE:%.*]] = mul i64 [[I]], [[M]]
-; CHECK-NEXT:    [[IDX:%.*]] = add i64 [[BASE]], [[J]]
-; CHECK-NEXT:    [[P:%.*]] = getelementptr i64, ptr [[A]], i64 [[IDX]]
-; CHECK-NEXT:    store i64 [[IDX]], ptr [[P]], align 4
-; CHECK-NEXT:    [[J_NEXT]] = add i64 [[J]], 1
-; CHECK-NEXT:    [[IC:%.*]] = icmp slt i64 [[J_NEXT]], [[M]]
-; CHECK-NEXT:    br i1 [[IC]], label %[[INNER_HEADER]], label %[[OUTER_LATCH]]
-; CHECK:       [[OUTER_LATCH]]:
-; CHECK-NEXT:    [[I_NEXT]] = add i64 [[I]], 1
-; CHECK-NEXT:    [[ITR_CHK1:%.*]] = icmp sle i64 [[I_NEXT]], [[SMIN]]
-; CHECK-NEXT:    br i1 [[ITR_CHK1]], label %[[OUTER_HEADER]], label %[[EXIT:.*]]
-; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    br label %[[LS_GUARD1]]
-; CHECK:       [[LS_GUARD1]]:
-; CHECK-NEXT:    [[ITR_CHK2:%.*]] = icmp sle i64 50, [[TMP0]]
-; CHECK-NEXT:    br i1 [[ITR_CHK2]], label %[[ENTRY_LS1:.*]], label %[[LS_FINAL_EXIT:.*]]
-; CHECK:       [[ENTRY_LS1]]:
-; CHECK-NEXT:    br label %[[OUTER_HEADER_LS1:.*]]
-; CHECK:       [[OUTER_HEADER_LS1]]:
-; CHECK-NEXT:    [[I_LS1:%.*]] = phi i64 [ 50, %[[ENTRY_LS1]] ], [ [[I_NEXT_LS1:%.*]], %[[OUTER_LATCH_LS1:.*]] ]
-; CHECK-NEXT:    br label %[[INNER_HEADER_LS1:.*]]
-; CHECK:       [[INNER_HEADER_LS1]]:
-; CHECK-NEXT:    [[J_LS1:%.*]] = phi i64 [ 0, %[[OUTER_HEADER_LS1]] ], [ [[J_NEXT_LS1:%.*]], %[[INNER_HEADER_LS1]] ]
-; CHECK-NEXT:    [[BASE_LS1:%.*]] = mul i64 [[I_LS1]], [[M]]
-; CHECK-NEXT:    [[IDX_LS1:%.*]] = add i64 [[BASE_LS1]], [[J_LS1]]
-; CHECK-NEXT:    [[P_LS1:%.*]] = getelementptr i64, ptr [[A]], i64 [[IDX_LS1]]
-; CHECK-NEXT:    store i64 [[IDX_LS1]], ptr [[P_LS1]], align 4
-; CHECK-NEXT:    [[J_NEXT_LS1]] = add i64 [[J_LS1]], 1
-; CHECK-NEXT:    [[IC_LS1:%.*]] = icmp slt i64 [[J_NEXT_LS1]], [[M]]
-; CHECK-NEXT:    br i1 [[IC_LS1]], label %[[INNER_HEADER_LS1]], label %[[OUTER_LATCH_LS1]]
-; CHECK:       [[OUTER_LATCH_LS1]]:
-; CHECK-NEXT:    [[I_NEXT_LS1]] = add i64 [[I_LS1]], 1
-; CHECK-NEXT:    [[ITR_CHK3:%.*]] = icmp sle i64 [[I_NEXT_LS1]], [[TMP0]]
-; CHECK-NEXT:    br i1 [[ITR_CHK3]], label %[[OUTER_HEADER_LS1]], label %[[LS_EXIT1:.*]]
-; CHECK:       [[LS_EXIT1]]:
-; CHECK-NEXT:    br label %[[LS_FINAL_EXIT]]
-; CHECK:       [[LS_FINAL_EXIT]]:
-; CHECK-NEXT:    ret void
-;
-entry:
-  br label %outer.header
-
-outer.header:
-  %i = phi i64 [ 0, %entry ], [ %i.next, %outer.latch ]
-  br label %inner.header
-
-inner.header:
-  %j = phi i64 [ 0, %outer.header ], [ %j.next, %inner.header ]
-  %base = mul i64 %i, %m
-  %idx = add i64 %base, %j
-  %p = getelementptr i64, ptr %a, i64 %idx
-  store i64 %idx, ptr %p
-  %j.next = add i64 %j, 1
-  %ic = icmp slt i64 %j.next, %m
-  br i1 %ic, label %inner.header, label %outer.latch
-
-outer.latch:
-  %i.next = add i64 %i, 1
-  %c = icmp slt i64 %i.next, %n
-  br i1 %c, label %outer.header, label %exit
-
-exit:
-  ret void
-}
diff --git a/llvm/test/Transforms/LoopSplit/optional-guard.ll b/llvm/test/Transforms/LoopSplit/optional-guard.ll
deleted file mode 100644
index 82ea84e8dce11..0000000000000
--- a/llvm/test/Transforms/LoopSplit/optional-guard.ll
+++ /dev/null
@@ -1,70 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
-; RUN: opt -passes=loop-split-utils -loop-split-points=4 -loop-split-unguarded=0 \
-; RUN:   -verify-dom-info -verify-loop-info -S < %s | FileCheck %s
-
-; Per-partition entry guards are optional. The split loop is bottom-tested, so
-; by default every partition is guarded by a `Start <= End` check that skips a
-; zero-iteration partition. A caller that can prove a partition runs at least
-; once may omit its guard (here partition 0, via -loop-split-unguarded=0): that
-; guard then enters the sub-loop unconditionally and has no skip edge to the
-; next partition. Partition 1 keeps its guard. The dominator tree is maintained
-; incrementally for the missing skip edge, so -verify-dom-info must pass.
-
-define i64 @reduction(ptr %a, i64 %n) {
-; CHECK-LABEL: define i64 @reduction(
-; CHECK-SAME: ptr [[A:%.*]], i64 [[N:%.*]]) {
-; CHECK-NEXT:  [[LS_GUARD0:.*:]]
-; CHECK-NEXT:    [[SMAX:%.*]] = call i64 @llvm.smax.i64(i64 [[N]], i64 1)
-; CHECK-NEXT:    [[TMP0:%.*]] = add nsw i64 [[SMAX]], -1
-; CHECK-NEXT:    [[SMIN:%.*]] = call i64 @llvm.smin.i64(i64 [[TMP0]], i64 3)
-; CHECK-NEXT:    br label %[[ENTRY:.*]]
-; CHECK:       [[ENTRY]]:
-; CHECK-NEXT:    br label %[[LOOP:.*]]
-; CHECK:       [[LOOP]]:
-; CHECK-NEXT:    [[I:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[I_NEXT:%.*]], %[[LOOP]] ]
-; CHECK-NEXT:    [[SUM:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[SUM_NEXT:%.*]], %[[LOOP]] ]
-; CHECK-NEXT:    [[P:%.*]] = getelementptr i64, ptr [[A]], i64 [[I]]
-; CHECK-NEXT:    [[V:%.*]] = load i64, ptr [[P]], align 4
-; CHECK-NEXT:    [[SUM_NEXT]] = add i64 [[SUM]], [[V]]
-; CHECK-NEXT:    [[I_NEXT]] = add i64 [[I]], 1
-; CHECK-NEXT:    [[ITR_CHK:%.*]] = icmp sle i64 [[I_NEXT]], [[SMIN]]
-; CHECK-NEXT:    br i1 [[ITR_CHK]], label %[[LOOP]], label %[[EXIT:.*]]
-; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    br label %[[LS_GUARD1:.*]]
-; CHECK:       [[LS_GUARD1]]:
-; CHECK-NEXT:    [[ITR_CHK1:%.*]] = icmp sle i64 4, [[TMP0]]
-; CHECK-NEXT:    br i1 [[ITR_CHK1]], label %[[ENTRY_LS1:.*]], label %[[LS_FINAL_EXIT:.*]]
-; CHECK:       [[ENTRY_LS1]]:
-; CHECK-NEXT:    br label %[[LOOP_LS1:.*]]
-; CHECK:       [[LOOP_LS1]]:
-; CHECK-NEXT:    [[I_LS1:%.*]] = phi i64 [ 4, %[[ENTRY_LS1]] ], [ [[I_NEXT_LS1:%.*]], %[[LOOP_LS1]] ]
-; CHECK-NEXT:    [[SUM_LS1:%.*]] = phi i64 [ [[SUM_NEXT]], %[[ENTRY_LS1]] ], [ [[SUM_NEXT_LS1:%.*]], %[[LOOP_LS1]] ]
-; CHECK-NEXT:    [[P_LS1:%.*]] = getelementptr i64, ptr [[A]], i64 [[I_LS1]]
-; CHECK-NEXT:    [[V_LS1:%.*]] = load i64, ptr [[P_LS1]], align 4
-; CHECK-NEXT:    [[SUM_NEXT_LS1]] = add i64 [[SUM_LS1]], [[V_LS1]]
-; CHECK-NEXT:    [[I_NEXT_LS1]] = add i64 [[I_LS1]], 1
-; CHECK-NEXT:    [[ITR_CHK2:%.*]] = icmp sle i64 [[I_NEXT_LS1]], [[TMP0]]
-; CHECK-NEXT:    br i1 [[ITR_CHK2]], label %[[LOOP_LS1]], label %[[LS_EXIT1:.*]]
-; CHECK:       [[LS_EXIT1]]:
-; CHECK-NEXT:    br label %[[LS_FINAL_EXIT]]
-; CHECK:       [[LS_FINAL_EXIT]]:
-; CHECK-NEXT:    [[SUM_NEXT3:%.*]] = phi i64 [ [[SUM_NEXT_LS1]], %[[LS_EXIT1]] ], [ [[SUM_NEXT]], %[[LS_GUARD1]] ]
-; CHECK-NEXT:    ret i64 [[SUM_NEXT3]]
-;
-entry:
-  br label %loop
-
-loop:
-  %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
-  %sum = phi i64 [ 0, %entry ], [ %sum.next, %loop ]
-  %p = getelementptr i64, ptr %a, i64 %i
-  %v = load i64, ptr %p
-  %sum.next = add i64 %sum, %v
-  %i.next = add i64 %i, 1
-  %c = icmp slt i64 %i.next, %n
-  br i1 %c, label %loop, label %exit
-
-exit:
-  %sum.lcssa = phi i64 [ %sum.next, %loop ]
-  ret i64 %sum.lcssa
-}
diff --git a/llvm/test/Transforms/LoopSplit/partition-value-map.ll b/llvm/test/Transforms/LoopSplit/partition-value-map.ll
deleted file mode 100644
index a7f5df90b156f..0000000000000
--- a/llvm/test/Transforms/LoopSplit/partition-value-map.ll
+++ /dev/null
@@ -1,44 +0,0 @@
-; REQUIRES: asserts
-; RUN: opt -passes=loop-split-utils -loop-split-points=4,8 \
-; RUN:   -debug-only=loop-split-utils -disable-output < %s 2>&1 | FileCheck %s
-
-; LoopSplitUtils preserves the original-to-clone value map for every partition
-; and exposes it through getPartitionValue(). Partition 0 reuses the original
-; loop (identity mapping); later partitions return the cloned counterpart, named
-; with a `.lsN` suffix. The latch compare (%c) is rewritten rather than cloned,
-; so it has no surviving counterpart and is omitted from every partition.
-
-define i32 @sum(ptr %a, i32 %n) {
-entry:
-  br label %loop
-
-loop:
-  %i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
-  %acc = phi i32 [ 0, %entry ], [ %acc.next, %loop ]
-  %p = getelementptr inbounds i32, ptr %a, i32 %i
-  %v = load i32, ptr %p
-  %acc.next = add i32 %acc, %v
-  %i.next = add i32 %i, 1
-  %c = icmp slt i32 %i.next, %n
-  br i1 %c, label %loop, label %exit
-
-exit:
-  %lcssa = phi i32 [ %acc.next, %loop ]
-  ret i32 %lcssa
-}
-
-; CHECK: LS-MAP partition 0:
-; CHECK:   i -> i
-; CHECK:   acc -> acc
-; CHECK:   acc.next -> acc.next
-; CHECK:   i.next -> i.next
-; CHECK: LS-MAP partition 1:
-; CHECK:   i -> i.ls1
-; CHECK:   acc -> acc.ls1
-; CHECK:   acc.next -> acc.next.ls1
-; CHECK:   i.next -> i.next.ls1
-; CHECK: LS-MAP partition 2:
-; CHECK:   i -> i.ls2
-; CHECK:   acc -> acc.ls2
-; CHECK:   acc.next -> acc.next.ls2
-; CHECK:   i.next -> i.next.ls2
diff --git a/llvm/test/Transforms/LoopSplit/reduction.ll b/llvm/test/Transforms/LoopSplit/reduction.ll
deleted file mode 100644
index c9cd745469323..0000000000000
--- a/llvm/test/Transforms/LoopSplit/reduction.ll
+++ /dev/null
@@ -1,71 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
-; RUN: opt -passes=loop-split-utils -loop-split-points=50 -S < %s | FileCheck %s
-
-; A reduction (live-out) is correctly threaded across the two partitions: the
-; second partition seeds its accumulator with the first partition's join value
-; and a dedicated final-exit block merges the per-partition results.
-
-define i64 @reduction(ptr %a, i64 %n) {
-; CHECK-LABEL: define i64 @reduction(
-; CHECK-SAME: ptr [[A:%.*]], i64 [[N:%.*]]) {
-; CHECK-NEXT:  [[LS_GUARD0:.*]]:
-; CHECK-NEXT:    [[SMAX:%.*]] = call i64 @llvm.smax.i64(i64 [[N]], i64 1)
-; CHECK-NEXT:    [[TMP0:%.*]] = add nsw i64 [[SMAX]], -1
-; CHECK-NEXT:    [[SMIN:%.*]] = call i64 @llvm.smin.i64(i64 [[TMP0]], i64 49)
-; CHECK-NEXT:    [[ITR_CHK:%.*]] = icmp sle i64 0, [[SMIN]]
-; CHECK-NEXT:    br i1 [[ITR_CHK]], label %[[ENTRY:.*]], label %[[LS_GUARD1:.*]]
-; CHECK:       [[ENTRY]]:
-; CHECK-NEXT:    br label %[[LOOP:.*]]
-; CHECK:       [[LOOP]]:
-; CHECK-NEXT:    [[I:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[I_NEXT:%.*]], %[[LOOP]] ]
-; CHECK-NEXT:    [[SUM:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[SUM_NEXT:%.*]], %[[LOOP]] ]
-; CHECK-NEXT:    [[P:%.*]] = getelementptr i64, ptr [[A]], i64 [[I]]
-; CHECK-NEXT:    [[V:%.*]] = load i64, ptr [[P]], align 4
-; CHECK-NEXT:    [[SUM_NEXT]] = add i64 [[SUM]], [[V]]
-; CHECK-NEXT:    [[I_NEXT]] = add i64 [[I]], 1
-; CHECK-NEXT:    [[ITR_CHK1:%.*]] = icmp sle i64 [[I_NEXT]], [[SMIN]]
-; CHECK-NEXT:    br i1 [[ITR_CHK1]], label %[[LOOP]], label %[[EXIT:.*]]
-; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    br label %[[LS_GUARD1]]
-; CHECK:       [[LS_GUARD1]]:
-; CHECK-NEXT:    [[SUM_NEXT5:%.*]] = phi i64 [ [[SUM_NEXT]], %[[EXIT]] ], [ 0, %[[LS_GUARD0]] ]
-; CHECK-NEXT:    [[ITR_CHK2:%.*]] = icmp sle i64 50, [[TMP0]]
-; CHECK-NEXT:    br i1 [[ITR_CHK2]], label %[[ENTRY_LS1:.*]], label %[[LS_FINAL_EXIT:.*]]
-; CHECK:       [[ENTRY_LS1]]:
-; CHECK-NEXT:    br label %[[LOOP_LS1:.*]]
-; CHECK:       [[LOOP_LS1]]:
-; CHECK-NEXT:    [[I_LS1:%.*]] = phi i64 [ 50, %[[ENTRY_LS1]] ], [ [[I_NEXT_LS1:%.*]], %[[LOOP_LS1]] ]
-; CHECK-NEXT:    [[SUM_LS1:%.*]] = phi i64 [ [[SUM_NEXT5]], %[[ENTRY_LS1]] ], [ [[SUM_NEXT_LS1:%.*]], %[[LOOP_LS1]] ]
-; CHECK-NEXT:    [[P_LS1:%.*]] = getelementptr i64, ptr [[A]], i64 [[I_LS1]]
-; CHECK-NEXT:    [[V_LS1:%.*]] = load i64, ptr [[P_LS1]], align 4
-; CHECK-NEXT:    [[SUM_NEXT_LS1]] = add i64 [[SUM_LS1]], [[V_LS1]]
-; CHECK-NEXT:    [[I_NEXT_LS1]] = add i64 [[I_LS1]], 1
-; CHECK-NEXT:    [[ITR_CHK3:%.*]] = icmp sle i64 [[I_NEXT_LS1]], [[TMP0]]
-; CHECK-NEXT:    br i1 [[ITR_CHK3]], label %[[LOOP_LS1]], label %[[LS_EXIT1:.*]]
-; CHECK:       [[LS_EXIT1]]:
-; CHECK-NEXT:    br label %[[LS_FINAL_EXIT]]
-; CHECK:       [[LS_FINAL_EXIT]]:
-; CHECK-NEXT:    [[SUM_NEXT4:%.*]] = phi i64 [ [[SUM_NEXT_LS1]], %[[LS_EXIT1]] ], [ [[SUM_NEXT5]], %[[LS_GUARD1]] ]
-; CHECK-NEXT:    ret i64 [[SUM_NEXT4]]
-;
-; The second partition's entry guard joins the first partition's result with the
-; chain-entry value (used when the first partition runs zero iterations).
-; The cloned partition seeds its accumulator from that join value.
-; A dedicated final-exit block merges the per-partition results.
-entry:
-  br label %loop
-
-loop:
-  %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
-  %sum = phi i64 [ 0, %entry ], [ %sum.next, %loop ]
-  %p = getelementptr i64, ptr %a, i64 %i
-  %v = load i64, ptr %p
-  %sum.next = add i64 %sum, %v
-  %i.next = add i64 %i, 1
-  %c = icmp slt i64 %i.next, %n
-  br i1 %c, label %loop, label %exit
-
-exit:
-  %sum.lcssa = phi i64 [ %sum.next, %loop ]
-  ret i64 %sum.lcssa
-}



More information about the llvm-commits mailing list