[llvm] [LoopUnroll] Bound automatic full-unroll cloning work (PR #211683)

via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 29 16:52:50 PDT 2026


https://github.com/khaki3 updated https://github.com/llvm/llvm-project/pull/211683

>From c9690386d28d9f19658065f7ae9c6f1db7883edb Mon Sep 17 00:00:00 2001
From: Kazuaki Matsumura <kmatsumura at nvidia.com>
Date: Thu, 23 Jul 2026 15:13:27 -0700
Subject: [PATCH 1/2] [LoopUnroll] Bound automatic full-unroll cloning work

Prevent precise profitability analysis from selecting automatic full unrolls whose transient cloning work can cause pathological compile times, while preserving explicit requests and target overrides.
---
 .../llvm/Analysis/TargetTransformInfo.h       |   3 +
 llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp |  29 +++-
 .../LoopUnroll/full-unroll-cloning-cost.ll    | 149 ++++++++++++++++++
 3 files changed, 178 insertions(+), 3 deletions(-)
 create mode 100644 llvm/test/Transforms/LoopUnroll/full-unroll-cloning-cost.ll

diff --git a/llvm/include/llvm/Analysis/TargetTransformInfo.h b/llvm/include/llvm/Analysis/TargetTransformInfo.h
index 107ae4dba5075..7f4baa6f24676 100644
--- a/llvm/include/llvm/Analysis/TargetTransformInfo.h
+++ b/llvm/include/llvm/Analysis/TargetTransformInfo.h
@@ -701,6 +701,9 @@ class TargetTransformInfo {
     /// applies even if full unrolling is selected. This allows a target to fall
     /// back to Partial unrolling if full unrolling is above FullUnrollMaxCount.
     unsigned FullUnrollMaxCount;
+    /// Set the maximum estimated instructions cloned by automatic full
+    /// unrolling. Set to UINT64_MAX to disable.
+    uint64_t FullUnrollMaxClonedInstructions;
     // Represents number of instructions optimized when "back edge"
     // becomes "fall through" in unrolled loop.
     // For now we count a conditional branch on a backedge and a comparison
diff --git a/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp b/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp
index ad3b123f3327c..66970b87fd955 100644
--- a/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp
@@ -122,6 +122,11 @@ static cl::opt<unsigned> UnrollFullMaxCount(
     cl::desc(
         "Set the max unroll count for full unrolling, for testing purposes"));
 
+static cl::opt<uint64_t> UnrollFullMaxClonedInstructions(
+    "unroll-full-max-cloned-instructions", cl::init(100'000), cl::Hidden,
+    cl::desc("Maximum estimated instructions cloned by automatic full "
+             "unrolling"));
+
 static cl::opt<bool>
     UnrollAllowPartial("unroll-allow-partial", cl::Hidden,
                        cl::desc("Allows loops to be partially unrolled until "
@@ -206,6 +211,7 @@ TargetTransformInfo::UnrollingPreferences llvm::gatherUnrollingPreferences(
   UP.MaxCount = std::numeric_limits<unsigned>::max();
   UP.MaxUpperBound = UnrollMaxUpperBound;
   UP.FullUnrollMaxCount = std::numeric_limits<unsigned>::max();
+  UP.FullUnrollMaxClonedInstructions = UnrollFullMaxClonedInstructions;
   UP.BEInsns = 2;
   UP.Partial = false;
   UP.Runtime = false;
@@ -249,6 +255,8 @@ TargetTransformInfo::UnrollingPreferences llvm::gatherUnrollingPreferences(
     UP.MaxUpperBound = UnrollMaxUpperBound;
   if (UnrollFullMaxCount.getNumOccurrences() > 0)
     UP.FullUnrollMaxCount = UnrollFullMaxCount;
+  if (UnrollFullMaxClonedInstructions.getNumOccurrences() > 0)
+    UP.FullUnrollMaxClonedInstructions = UnrollFullMaxClonedInstructions;
   if (UnrollAllowPartial.getNumOccurrences() > 0)
     UP.Partial = UnrollAllowPartial;
   if (UnrollAllowRemainder.getNumOccurrences() > 0)
@@ -905,7 +913,7 @@ static std::optional<unsigned> shouldFullUnroll(
     Loop *L, const TargetTransformInfo &TTI, DominatorTree &DT,
     ScalarEvolution &SE, const SmallPtrSetImpl<const Value *> &EphValues,
     const unsigned FullUnrollTripCount, const UnrollCostEstimator UCE,
-    const TargetTransformInfo::UnrollingPreferences &UP) {
+    const TargetTransformInfo::UnrollingPreferences &UP, bool ExplicitUnroll) {
   assert(FullUnrollTripCount && "should be non-zero!");
 
   if (FullUnrollTripCount > UP.FullUnrollMaxCount) {
@@ -918,6 +926,19 @@ static std::optional<unsigned> shouldFullUnroll(
   // When computing the unrolled size, note that BEInsns are not replicated
   // like the rest of the loop body.
   uint64_t UnrolledSize = UCE.getUnrolledLoopSize(UP, FullUnrollTripCount);
+  uint64_t LoopInstructionCount = 0;
+  for (const BasicBlock *BB : L->blocks())
+    LoopInstructionCount += BB->size();
+  uint64_t AdditionalCopies = FullUnrollTripCount - 1;
+  if (!ExplicitUnroll && UP.Threshold != NoThreshold && AdditionalCopies &&
+      LoopInstructionCount >
+          UP.FullUnrollMaxClonedInstructions / AdditionalCopies) {
+    LLVM_DEBUG(dbgs().indent(2)
+               << "Not unrolling: estimated cloned instructions exceed "
+               << UP.FullUnrollMaxClonedInstructions << ".\n");
+    return std::nullopt;
+  }
+
   if (UnrolledSize < UP.Threshold) {
     LLVM_DEBUG(dbgs().indent(2) << "Unrolling: size " << UnrolledSize
                                 << " < threshold " << UP.Threshold << ".\n");
@@ -1093,7 +1114,8 @@ unsigned llvm::computeUnrollCount(
   LLVM_DEBUG(dbgs().indent(1) << "Trying full unroll...\n");
   if (TripCount) {
     if (auto UnrollFactor =
-            shouldFullUnroll(L, TTI, DT, SE, EphValues, TripCount, UCE, UP))
+            shouldFullUnroll(L, TTI, DT, SE, EphValues, TripCount, UCE, UP,
+                             PInfo.ExplicitUnroll))
       return *UnrollFactor;
   }
 
@@ -1113,7 +1135,8 @@ unsigned llvm::computeUnrollCount(
   if (!TripCount && MaxTripCount && (UP.UpperBound || MaxOrZero) &&
       MaxTripCount <= UP.MaxUpperBound) {
     if (auto UnrollFactor =
-            shouldFullUnroll(L, TTI, DT, SE, EphValues, MaxTripCount, UCE, UP))
+            shouldFullUnroll(L, TTI, DT, SE, EphValues, MaxTripCount, UCE, UP,
+                             PInfo.ExplicitUnroll))
       return *UnrollFactor;
   }
 
diff --git a/llvm/test/Transforms/LoopUnroll/full-unroll-cloning-cost.ll b/llvm/test/Transforms/LoopUnroll/full-unroll-cloning-cost.ll
new file mode 100644
index 0000000000000..22dccbd84d9b0
--- /dev/null
+++ b/llvm/test/Transforms/LoopUnroll/full-unroll-cloning-cost.ll
@@ -0,0 +1,149 @@
+; RUN: opt < %s -passes='default<O2>' -disable-output
+; RUN: opt < %s -passes='require<opt-remark-emit>,loop(loop-unroll-full)' \
+; RUN:   -unroll-full-max-cloned-instructions=1 -unroll-peel-count=0 -S \
+; RUN:   | FileCheck %s
+; RUN: opt < %s -passes=loop-unroll \
+; RUN:   -unroll-full-max-cloned-instructions=35 -unroll-peel-count=0 -S \
+; RUN:   | FileCheck %s --check-prefix=BELOW
+; RUN: opt < %s -passes=loop-unroll \
+; RUN:   -unroll-full-max-cloned-instructions=36 -unroll-peel-count=0 -S \
+; RUN:   | FileCheck %s --check-prefix=AT
+; RUN: opt < %s -passes=loop-unroll \
+; RUN:   -unroll-full-max-cloned-instructions=1 -unroll-peel-count=0 -S \
+; RUN:   | FileCheck %s --check-prefix=EXPLICIT
+; RUN: llvm-extract -func=boundary %s -S \
+; RUN:   | opt -passes=loop-unroll -unroll-full-max-cloned-instructions=1 \
+; RUN:     -unroll-threshold=4294967295 -unroll-peel-count=0 -S \
+; RUN:   | FileCheck %s --check-prefix=NO-THRESHOLD
+;
+; CHECK-LABEL: define void @nested(
+; CHECK: loop.5:
+; CHECK: %iv.5 = phi i64
+;
+; BELOW-LABEL: define i32 @boundary(
+; BELOW: loop:
+; BELOW: %iv = phi i32
+;
+; AT-LABEL: define i32 @boundary(
+; AT-NOT: phi
+; AT: ret i32 10
+;
+; EXPLICIT-LABEL: define i32 @explicit_enable(
+; EXPLICIT-NOT: phi
+; EXPLICIT: ret i32 10
+;
+; NO-THRESHOLD-LABEL: define i32 @boundary(
+; NO-THRESHOLD-NOT: phi
+; NO-THRESHOLD: ret i32 10
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+define i32 @boundary() {
+entry:
+  br label %loop
+
+loop:
+  %iv = phi i32 [ 0, %entry ], [ %next, %loop ]
+  %next = add i32 %iv, 1
+  %continue = icmp ult i32 %next, 10
+  br i1 %continue, label %loop, label %exit
+
+exit:
+  ret i32 %next
+}
+
+define i32 @explicit_enable() {
+entry:
+  br label %loop
+
+loop:
+  %iv = phi i32 [ 0, %entry ], [ %next, %loop ]
+  %next = add i32 %iv, 1
+  %continue = icmp ult i32 %next, 10
+  br i1 %continue, label %loop, label %exit, !llvm.loop !0
+
+exit:
+  ret i32 %next
+}
+
+define void @nested(i32 %arg) {
+entry:
+  br label %loop.1
+
+loop.1:
+  %iv.1 = phi i64 [ %next.1, %loop.1.latch ], [ 0, %entry ]
+  %cmp.1 = icmp slt i64 %iv.1, 10
+  br i1 %cmp.1, label %loop.2, label %exit
+
+loop.2:
+  %iv.2 = phi i64 [ %next.2, %loop.2.latch ], [ 0, %loop.1 ]
+  %value.2 = phi i32 [ %next.value.2, %loop.2.latch ], [ 0, %loop.1 ]
+  %cmp.2 = icmp slt i64 %iv.2, 10
+  br i1 %cmp.2, label %body.2, label %loop.1.latch
+
+body.2:
+  %next.value.2 = add i32 %value.2, 1
+  %enter.3 = icmp slt i32 %value.2, 0
+  br i1 %enter.3, label %loop.3, label %loop.2.latch
+
+loop.3:
+  %iv.3 = phi i64 [ %next.3, %loop.3.latch ], [ 0, %body.2 ]
+  %cmp.3 = icmp slt i64 %iv.3, 10
+  br i1 %cmp.3, label %loop.4, label %loop.2.latch
+
+loop.4:
+  %iv.4 = phi i64 [ %next.4, %loop.4.latch ], [ 0, %loop.3 ]
+  %value.4 = phi i32 [ %next.value.4, %loop.4.latch ], [ 0, %loop.3 ]
+  %cmp.4 = icmp slt i64 %iv.4, 10
+  br i1 %cmp.4, label %body.4, label %loop.3.latch
+
+body.4:
+  %next.value.4 = add i32 %value.4, 1
+  %enter.5 = icmp slt i32 %value.4, 0
+  br i1 %enter.5, label %loop.5, label %loop.4.latch
+
+loop.5:
+  %iv.5 = phi i64 [ %next.5, %body.5 ], [ 0, %body.4 ]
+  %value.5 = phi i32 [ %arg, %body.5 ], [ 0, %body.4 ]
+  %cmp.5 = icmp slt i64 %iv.5, 10
+  br i1 %cmp.5, label %body.5, label %loop.4.latch
+
+body.5:
+  %negative = icmp slt i32 %value.5, 0
+  call void @llvm.assume(i1 %negative)
+  call void @llvm.stackrestore.p0(ptr null)
+  %next.5 = add i64 %iv.5, 1
+  br label %loop.5
+
+loop.4.latch:
+  %next.4 = add i64 %iv.4, 1
+  br label %loop.4
+
+loop.3.latch:
+  %next.3 = add i64 %iv.3, 1
+  br label %loop.3
+
+loop.2.latch:
+  %next.2 = add i64 %iv.2, 1
+  br label %loop.2
+
+loop.1.latch:
+  %next.1 = add i64 %iv.1, 1
+  br label %loop.1
+
+exit:
+  ret void
+}
+
+declare void @llvm.stackrestore.p0(ptr) #0
+declare void @llvm.assume(i1 noundef) #1
+
+attributes #0 = { nocallback nofree nosync nounwind willreturn }
+attributes #1 = {
+  nocallback nofree nosync nounwind willreturn
+  memory(inaccessiblemem: write)
+}
+
+!0 = distinct !{!0, !1}
+!1 = !{!"llvm.loop.unroll.enable"}

>From 91a20a9135d44e6d4bdd05dad1827a02dbd88f95 Mon Sep 17 00:00:00 2001
From: Kazuaki Matsumura <kmatsumura at nvidia.com>
Date: Wed, 29 Jul 2026 16:52:37 -0700
Subject: [PATCH 2/2] [LoopUnroll] Keep cloning budget as cl::opt, not in UP

Address review: drop FullUnrollMaxClonedInstructions from
UnrollingPreferences, reject before getUnrolledLoopSize, and tighten the
test comments / remove leftover datalayout and NO-THRESHOLD coverage.
---
 .../llvm/Analysis/TargetTransformInfo.h       |  3 -
 llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp | 56 ++++++++++---------
 .../LoopUnroll/full-unroll-cloning-cost.ll    | 22 ++++----
 3 files changed, 40 insertions(+), 41 deletions(-)

diff --git a/llvm/include/llvm/Analysis/TargetTransformInfo.h b/llvm/include/llvm/Analysis/TargetTransformInfo.h
index 7f4baa6f24676..107ae4dba5075 100644
--- a/llvm/include/llvm/Analysis/TargetTransformInfo.h
+++ b/llvm/include/llvm/Analysis/TargetTransformInfo.h
@@ -701,9 +701,6 @@ class TargetTransformInfo {
     /// applies even if full unrolling is selected. This allows a target to fall
     /// back to Partial unrolling if full unrolling is above FullUnrollMaxCount.
     unsigned FullUnrollMaxCount;
-    /// Set the maximum estimated instructions cloned by automatic full
-    /// unrolling. Set to UINT64_MAX to disable.
-    uint64_t FullUnrollMaxClonedInstructions;
     // Represents number of instructions optimized when "back edge"
     // becomes "fall through" in unrolled loop.
     // For now we count a conditional branch on a backedge and a comparison
diff --git a/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp b/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp
index 66970b87fd955..296c77f07c7ba 100644
--- a/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp
@@ -83,11 +83,10 @@ static cl::opt<unsigned>
     UnrollThreshold("unroll-threshold", cl::Hidden,
                     cl::desc("The cost threshold for loop unrolling"));
 
-static cl::opt<unsigned>
-    UnrollOptSizeThreshold(
-      "unroll-optsize-threshold", cl::init(0), cl::Hidden,
-      cl::desc("The cost threshold for loop unrolling when optimizing for "
-               "size"));
+static cl::opt<unsigned> UnrollOptSizeThreshold(
+    "unroll-optsize-threshold", cl::init(0), cl::Hidden,
+    cl::desc("The cost threshold for loop unrolling when optimizing for "
+             "size"));
 
 static cl::opt<unsigned> UnrollPartialThreshold(
     "unroll-partial-threshold", cl::Hidden,
@@ -157,9 +156,9 @@ static cl::opt<unsigned> FlatLoopTripCountThreshold(
              "threshold, the loop is considered as flat and will be less "
              "aggressively unrolled."));
 
-static cl::opt<bool> UnrollUnrollRemainder(
-  "unroll-remainder", cl::Hidden,
-  cl::desc("Allow the loop remainder to be unrolled."));
+static cl::opt<bool>
+    UnrollUnrollRemainder("unroll-remainder", cl::Hidden,
+                          cl::desc("Allow the loop remainder to be unrolled."));
 
 // This option isn't ever intended to be enabled, it serves to allow
 // experiments to check the assumptions about when this kind of revisit is
@@ -211,7 +210,6 @@ TargetTransformInfo::UnrollingPreferences llvm::gatherUnrollingPreferences(
   UP.MaxCount = std::numeric_limits<unsigned>::max();
   UP.MaxUpperBound = UnrollMaxUpperBound;
   UP.FullUnrollMaxCount = std::numeric_limits<unsigned>::max();
-  UP.FullUnrollMaxClonedInstructions = UnrollFullMaxClonedInstructions;
   UP.BEInsns = 2;
   UP.Partial = false;
   UP.Runtime = false;
@@ -255,8 +253,6 @@ TargetTransformInfo::UnrollingPreferences llvm::gatherUnrollingPreferences(
     UP.MaxUpperBound = UnrollMaxUpperBound;
   if (UnrollFullMaxCount.getNumOccurrences() > 0)
     UP.FullUnrollMaxCount = UnrollFullMaxCount;
-  if (UnrollFullMaxClonedInstructions.getNumOccurrences() > 0)
-    UP.FullUnrollMaxClonedInstructions = UnrollFullMaxClonedInstructions;
   if (UnrollAllowPartial.getNumOccurrences() > 0)
     UP.Partial = UnrollAllowPartial;
   if (UnrollAllowRemainder.getNumOccurrences() > 0)
@@ -405,9 +401,9 @@ static std::optional<EstimatedUnrollCost> analyzeLoopUnrollCost(
     assert(PHIUsedList.empty() && "Must start with an empty phi used list");
     CostWorklist.push_back(&RootI);
     TargetTransformInfo::TargetCostKind CostKind =
-      RootI.getFunction()->hasMinSize() ?
-      TargetTransformInfo::TCK_CodeSize :
-      TargetTransformInfo::TCK_SizeAndLatency;
+        RootI.getFunction()->hasMinSize()
+            ? TargetTransformInfo::TCK_CodeSize
+            : TargetTransformInfo::TCK_SizeAndLatency;
     for (;; --Iteration) {
       do {
         Instruction *I = CostWorklist.pop_back_val();
@@ -499,8 +495,9 @@ static std::optional<EstimatedUnrollCost> analyzeLoopUnrollCost(
              << "Starting LoopUnroll profitability analysis...\n");
 
   TargetTransformInfo::TargetCostKind CostKind =
-    L->getHeader()->getParent()->hasMinSize() ?
-    TargetTransformInfo::TCK_CodeSize : TargetTransformInfo::TCK_SizeAndLatency;
+      L->getHeader()->getParent()->hasMinSize()
+          ? TargetTransformInfo::TCK_CodeSize
+          : TargetTransformInfo::TCK_SizeAndLatency;
   // Simulate execution of each iteration of the loop counting instructions,
   // which would be simplified.
   // Since the same load will take different values on different iterations,
@@ -558,9 +555,10 @@ static std::optional<EstimatedUnrollCost> analyzeLoopUnrollCost(
         // and if the visitor returns true, mark the instruction as free after
         // unrolling and continue.
         bool IsFree = Analyzer.visit(I);
-        bool Inserted = InstCostMap.insert({&I, (int)Iteration,
-                                           (unsigned)IsFree,
-                                           /*IsCounted*/ false}).second;
+        bool Inserted = InstCostMap
+                            .insert({&I, (int)Iteration, (unsigned)IsFree,
+                                     /*IsCounted*/ false})
+                            .second;
         (void)Inserted;
         assert(Inserted && "Cannot have a state for an unvisited instruction!");
 
@@ -923,22 +921,27 @@ static std::optional<unsigned> shouldFullUnroll(
     return std::nullopt;
   }
 
-  // When computing the unrolled size, note that BEInsns are not replicated
-  // like the rest of the loop body.
-  uint64_t UnrolledSize = UCE.getUnrolledLoopSize(UP, FullUnrollTripCount);
+  // Bound estimated transient cloning work for automatic full unrolling.
+  // Explicit requests (e.g. llvm.loop.unroll.enable with a known trip count)
+  // bypass this compile-time guard; those still reach shouldFullUnroll because
+  // shouldPragmaUnroll only handles the upper-bound enable case.
   uint64_t LoopInstructionCount = 0;
   for (const BasicBlock *BB : L->blocks())
     LoopInstructionCount += BB->size();
   uint64_t AdditionalCopies = FullUnrollTripCount - 1;
   if (!ExplicitUnroll && UP.Threshold != NoThreshold && AdditionalCopies &&
       LoopInstructionCount >
-          UP.FullUnrollMaxClonedInstructions / AdditionalCopies) {
+          UnrollFullMaxClonedInstructions / AdditionalCopies) {
     LLVM_DEBUG(dbgs().indent(2)
                << "Not unrolling: estimated cloned instructions exceed "
-               << UP.FullUnrollMaxClonedInstructions << ".\n");
+               << UnrollFullMaxClonedInstructions << ".\n");
     return std::nullopt;
   }
 
+  // When computing the unrolled size, note that BEInsns are not replicated
+  // like the rest of the loop body.
+  uint64_t UnrolledSize = UCE.getUnrolledLoopSize(UP, FullUnrollTripCount);
+
   if (UnrolledSize < UP.Threshold) {
     LLVM_DEBUG(dbgs().indent(2) << "Unrolling: size " << UnrolledSize
                                 << " < threshold " << UP.Threshold << ".\n");
@@ -1771,8 +1774,9 @@ PreservedAnalyses LoopUnrollPass::run(Function &F,
   auto &MAMProxy = AM.getResult<ModuleAnalysisManagerFunctionProxy>(F);
   ProfileSummaryInfo *PSI =
       MAMProxy.getCachedResult<ProfileSummaryAnalysis>(*F.getParent());
-  auto *BFI = (PSI && PSI->hasProfileSummary()) ?
-      &AM.getResult<BlockFrequencyAnalysis>(F) : nullptr;
+  auto *BFI = (PSI && PSI->hasProfileSummary())
+                  ? &AM.getResult<BlockFrequencyAnalysis>(F)
+                  : nullptr;
 
   bool Changed = false;
 
diff --git a/llvm/test/Transforms/LoopUnroll/full-unroll-cloning-cost.ll b/llvm/test/Transforms/LoopUnroll/full-unroll-cloning-cost.ll
index 22dccbd84d9b0..3fba3f4409fa3 100644
--- a/llvm/test/Transforms/LoopUnroll/full-unroll-cloning-cost.ll
+++ b/llvm/test/Transforms/LoopUnroll/full-unroll-cloning-cost.ll
@@ -1,20 +1,25 @@
+; Compile-time smoke test for @nested: without the cloning budget,
+; default<O2> can hang or take a very long time. With the budget, this RUN
+; finishes quickly.
 ; RUN: opt < %s -passes='default<O2>' -disable-output
-; RUN: opt < %s -passes='require<opt-remark-emit>,loop(loop-unroll-full)' \
+;
+; With a tiny budget, @nested is not fully unrolled (residual innermost loop).
+; RUN: opt < %s -passes=loop-unroll \
 ; RUN:   -unroll-full-max-cloned-instructions=1 -unroll-peel-count=0 -S \
 ; RUN:   | FileCheck %s
+;
+; Exact budget boundary on @boundary (body size 4, trip 10 => 4*(10-1)=36).
 ; RUN: opt < %s -passes=loop-unroll \
 ; RUN:   -unroll-full-max-cloned-instructions=35 -unroll-peel-count=0 -S \
 ; RUN:   | FileCheck %s --check-prefix=BELOW
 ; RUN: opt < %s -passes=loop-unroll \
 ; RUN:   -unroll-full-max-cloned-instructions=36 -unroll-peel-count=0 -S \
 ; RUN:   | FileCheck %s --check-prefix=AT
+;
+; llvm.loop.unroll.enable bypasses the cloning budget (ExplicitUnroll).
 ; RUN: opt < %s -passes=loop-unroll \
 ; RUN:   -unroll-full-max-cloned-instructions=1 -unroll-peel-count=0 -S \
 ; RUN:   | FileCheck %s --check-prefix=EXPLICIT
-; RUN: llvm-extract -func=boundary %s -S \
-; RUN:   | opt -passes=loop-unroll -unroll-full-max-cloned-instructions=1 \
-; RUN:     -unroll-threshold=4294967295 -unroll-peel-count=0 -S \
-; RUN:   | FileCheck %s --check-prefix=NO-THRESHOLD
 ;
 ; CHECK-LABEL: define void @nested(
 ; CHECK: loop.5:
@@ -31,13 +36,6 @@
 ; EXPLICIT-LABEL: define i32 @explicit_enable(
 ; EXPLICIT-NOT: phi
 ; EXPLICIT: ret i32 10
-;
-; NO-THRESHOLD-LABEL: define i32 @boundary(
-; NO-THRESHOLD-NOT: phi
-; NO-THRESHOLD: ret i32 10
-
-target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
-target triple = "x86_64-unknown-linux-gnu"
 
 define i32 @boundary() {
 entry:



More information about the llvm-commits mailing list