[llvm] 48686f8 - [LoopInterchange] Do not interchange guarded imperfect loop nests (#201504)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Jun 4 23:03:42 PDT 2026
Author: Madhur Amilkanthwar
Date: 2026-06-05T06:03:37Z
New Revision: 48686f88f06f358145e4f177dd7c6b3ba1f93ae5
URL: https://github.com/llvm/llvm-project/commit/48686f88f06f358145e4f177dd7c6b3ba1f93ae5
DIFF: https://github.com/llvm/llvm-project/commit/48686f88f06f358145e4f177dd7c6b3ba1f93ae5.diff
LOG: [LoopInterchange] Do not interchange guarded imperfect loop nests (#201504)
When the outer-loop header conditionally branches to the outer latch,
that branch guards the inner loop, so the inner loop runs only on a
subset of the outer iterations. Interchanging such a nest moves the
inner loop outside the guard and runs it on every outer iteration,
including the guarded-off ones. That is incorrect when the inner loop
relies on the guard to terminate (e.g. an eq/ne exit whose trip count is
degenerate once the guard is false): the extra runs do not terminate, so
the program hangs at run time. Reject these guarded, imperfect nests in
tightlyNested().
This patch takes conservative approach to fix the crash and keep
upstream sane. In the long term, we need to see permissible cases under
which we can still interchange. I believe this is first good step.
Most importantly, three existing tests
(`currentLimitation.ll`, `loop-interchange-optimization-remarks.ll`,
`lcssa-preheader.ll`) were going through this unsafe transform; **these
tests would no longer interchange with this patch.**
I verified that they were hanging after illegal loop interchange with
`lli`: the un-interchanged function returns but the interchanged one
spins for a degenerate input (interchange_01(0, 2), test02(0, 2),
lcssa_08(1, 0)).
Fixes #201273
Assisted by Cursor.
Added:
llvm/test/Transforms/LoopInterchange/guarded-inner-loop.ll
Modified:
llvm/lib/Transforms/Scalar/LoopInterchange.cpp
llvm/test/Transforms/LoopInterchange/currentLimitation.ll
llvm/test/Transforms/LoopInterchange/lcssa-preheader.ll
llvm/test/Transforms/LoopInterchange/loop-interchange-optimization-remarks.ll
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
index 28eef9c2790de..b2e3bbe516ed8 100644
--- a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
@@ -824,12 +824,26 @@ bool LoopInterchangeLegality::tightlyNested(Loop *OuterLoop, Loop *InnerLoop) {
<< "' and '" << InnerLoop->getName()
<< "' are tightly nested\n");
- // A perfectly nested loop will not have any branch in between the outer and
- // inner block i.e. outer header will branch to either inner preheader and
- // outerloop latch.
+ // In a perfectly nested loop the outer header branches only into the inner
+ // loop. If it can also reach the outer latch, it conditionally guards the
+ // inner loop (an imperfect nest), so the inner loop runs on only a subset of
+ // the outer iterations. Interchanging such a nest would run the inner loop on
+ // every outer iteration, including the guarded-off ones, which is illegal
+ // when the inner loop relies on the guard to terminate (e.g. an eq/ne exit
+ // whose trip count is degenerate once the guard is false). Reject by allowing
+ // the outer header to branch only into the inner loop.
+ //
+ // TODO: This is conservative. A guarded nest is still safe to interchange
+ // when the inner loop has a computable trip count that is empty exactly when
+ // the guard is false, e.g.:
+ // for (i = 0; i < N; i++)
+ // if (M > 0) // loop-invariant guard
+ // for (j = 0; j < M; j++) // empty when M <= 0
+ // A[j][i] = ...;
+ // Interchanging is legal here because the inner loop runs zero times on the
+ // guarded-off iterations.
for (BasicBlock *Succ : successors(OuterLoopHeader))
- if (Succ != InnerLoopPreHeader && Succ != InnerLoop->getHeader() &&
- Succ != OuterLoopLatch)
+ if (Succ != InnerLoopPreHeader && Succ != InnerLoop->getHeader())
return false;
LLVM_DEBUG(dbgs() << "Checking instructions in Loop header and Loop latch\n");
diff --git a/llvm/test/Transforms/LoopInterchange/currentLimitation.ll b/llvm/test/Transforms/LoopInterchange/currentLimitation.ll
index 66e509d03d944..898627b05eacb 100644
--- a/llvm/test/Transforms/LoopInterchange/currentLimitation.ll
+++ b/llvm/test/Transforms/LoopInterchange/currentLimitation.ll
@@ -14,16 +14,21 @@ target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
@C = common global [100 x [100 x i64]] zeroinitializer
;;--------------------------------------Test case 01------------------------------------
-;; This loop can be interchanged with -da-disable-delinearization-checks, otherwise it cannot
-;; be interchanged due to dependence.
+;; Without -da-disable-delinearization-checks this is not interchanged due to
+;; dependence. Even with that flag it must not be interchanged: it is a guarded,
+;; imperfect nest (see below).
;; for(int i=0;i<N-1;i++)
-;; for(int j=1;j<N-1;j++)
-;; A[j+1][i+1] = A[j+1][i+1] + k;
+;; if(N-1>1) // guard: inner loop skipped when N<=2
+;; for(int j=1;j<N-1;j++)
+;; A[j+1][i+1] = A[j+1][i+1] + k;
; CHECK: Name: Dependence
; CHECK-NEXT: Function: interchange_01
-; DELIN: Name: Interchanged
+; Guarded nest: %for.cond1.preheader runs the inner loop only when
+; %cmp324 = (N-1 > 1). The inner exit (inner IV == N-2) only terminates under
+; that guard, so interchanging it loops forever for N == 2.
+; DELIN: Name: NotTightlyNested
; DELIN-NEXT: Function: interchange_01
define void @interchange_01(i32 %k, i32 %N) {
entry:
diff --git a/llvm/test/Transforms/LoopInterchange/guarded-inner-loop.ll b/llvm/test/Transforms/LoopInterchange/guarded-inner-loop.ll
new file mode 100644
index 0000000000000..681dca5bbcba3
--- /dev/null
+++ b/llvm/test/Transforms/LoopInterchange/guarded-inner-loop.ll
@@ -0,0 +1,77 @@
+; RUN: opt < %s -passes=loop-interchange \
+; RUN: -loop-interchange-profitabilities=ignore \
+; RUN: -pass-remarks-missed='loop-interchange' -disable-output \
+; RUN: -pass-remarks-output=%t
+; RUN: FileCheck -input-file=%t %s
+
+; The middle loop %for.j guards the inner loop %for.k: %for.k runs only when
+; %i != 0 (the outer loop's IV), and its exit %k.next == %i is only well-defined
+; under that guard. Interchanging %for.j and %for.k would run %for.k on every
+; iteration and spin when %i == 0, so the pass must not interchange this nest.
+;
+; Pseudo code:
+; for (i = 0; i < 3; i++)
+; for (j = 0; j < 2; j++)
+; if (i != 0) // guard on the outer IV
+; for (k = 0; ; k++) { // terminates only when i != 0
+; y[j][i][k] = x[i][k][j] + w[i][k][j];
+; if (k + 1 == i) break;
+; }
+
+; CHECK: --- !Missed
+; CHECK-NEXT: Pass: loop-interchange
+; CHECK-NEXT: Name: NotTightlyNested
+; CHECK-NEXT: Function: main
+
+ at x = global [3 x [3 x [3 x i32]]] zeroinitializer
+ at w = global [3 x [3 x [3 x i32]]] zeroinitializer
+ at y = global [3 x [3 x [3 x i32]]] zeroinitializer
+
+define i32 @main() {
+entry:
+ br label %for.i
+
+for.i:
+ %i = phi i32 [ %i.next, %for.i.inc ], [ 0, %entry ]
+ %i.is.zero = icmp eq i32 %i, 0
+ %xbase = getelementptr [9 x i32], ptr @x, i32 %i
+ %wbase = getelementptr [9 x i32], ptr @w, i32 %i
+ %ybase = getelementptr [3 x i32], ptr @y, i32 %i
+ br label %for.j
+
+for.j:
+ %j = phi i32 [ %j.next, %for.j.inc ], [ 0, %for.i ]
+ br i1 %i.is.zero, label %for.j.inc, label %for.k.ph
+
+for.k.ph:
+ %xp = getelementptr i32, ptr %xbase, i32 %j
+ %wp = getelementptr i32, ptr %wbase, i32 %j
+ %yp = getelementptr [9 x i32], ptr %ybase, i32 %j
+ br label %for.k
+
+for.k:
+ %k = phi i32 [ 0, %for.k.ph ], [ %k.next, %for.k ]
+ %xk = getelementptr [3 x i32], ptr %xp, i32 %k
+ %xv = load i32, ptr %xk, align 4
+ %wk = getelementptr [3 x i32], ptr %wp, i32 %k
+ %wv = load i32, ptr %wk, align 4
+ %add = add i32 %xv, %wv
+ %yk = getelementptr i32, ptr %yp, i32 %k
+ store i32 %add, ptr %yk, align 4
+ %k.next = add i32 %k, 1
+ %k.done = icmp eq i32 %k.next, %i
+ br i1 %k.done, label %for.j.inc, label %for.k
+
+for.j.inc:
+ %j.next = add i32 %j, 1
+ %j.cmp = icmp eq i32 %j, 0
+ br i1 %j.cmp, label %for.j, label %for.i.inc
+
+for.i.inc:
+ %i.next = add i32 %i, 1
+ %i.done = icmp eq i32 %i.next, 3
+ br i1 %i.done, label %exit, label %for.i
+
+exit:
+ ret i32 0
+}
diff --git a/llvm/test/Transforms/LoopInterchange/lcssa-preheader.ll b/llvm/test/Transforms/LoopInterchange/lcssa-preheader.ll
index 66078a857fe3c..1e251bed0d5f7 100644
--- a/llvm/test/Transforms/LoopInterchange/lcssa-preheader.ll
+++ b/llvm/test/Transforms/LoopInterchange/lcssa-preheader.ll
@@ -8,13 +8,16 @@ target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
; int temp[16][16];
; int res[16][16];
; for(int i = 0; i < n; i++) {
-; for(int j = 0; j < m; j++)
-; res[j][i] = temp[j][i];
+; if (m > 0) // guard: inner loop skipped when m <= 0
+; for(int j = 0; j < m; j++)
+; res[j][i] = temp[j][i];
; }
; }
-;; This loop can be interchanged with -da-disable-delinearization-checks, otherwise it cannot
-;; be interchanged due to dependence.
+;; Not interchanged by default (dependence). Even with
+;; -da-disable-delinearization-checks it must not be: %outer.header runs the
+;; inner loop only when %cmp222 = (m >s 0), and its exit (iv.next != m)
+;; only terminates then, so interchanging it loops forever for m == 0.
define void @lcssa_08(i32 %n, i32 %m) {;
; CHECK-LABEL: define void @lcssa_08(
; CHECK-SAME: i32 [[N:%.*]], i32 [[M:%.*]]) {
@@ -59,39 +62,32 @@ define void @lcssa_08(i32 %n, i32 %m) {;
; CHECK-DELIN-NEXT: [[TEMP:%.*]] = alloca [16 x [16 x i32]], align 4
; CHECK-DELIN-NEXT: [[RES:%.*]] = alloca [16 x [16 x i32]], align 4
; CHECK-DELIN-NEXT: [[CMP24:%.*]] = icmp sgt i32 [[N]], 0
-; CHECK-DELIN-NEXT: br i1 [[CMP24]], label %[[INNER_PREHEADER:.*]], label %[[FOR_COND_CLEANUP:.*]]
-; CHECK-DELIN: [[OUTER_PREHEADER:.*]]:
+; CHECK-DELIN-NEXT: br i1 [[CMP24]], label %[[OUTER_PREHEADER:.*]], label %[[FOR_COND_CLEANUP:.*]]
+; CHECK-DELIN: [[OUTER_PREHEADER]]:
+; CHECK-DELIN-NEXT: [[WIDE_TRIP_COUNT29:%.*]] = zext i32 [[N]] to i64
; CHECK-DELIN-NEXT: br label %[[OUTER_HEADER:.*]]
; CHECK-DELIN: [[OUTER_HEADER]]:
; CHECK-DELIN-NEXT: [[INDVARS_IV27:%.*]] = phi i64 [ 0, %[[OUTER_PREHEADER]] ], [ [[INDVARS_IV_NEXT28:%.*]], %[[OUTER_LATCH:.*]] ]
; CHECK-DELIN-NEXT: [[CMP222:%.*]] = icmp sgt i32 [[M]], 0
-; CHECK-DELIN-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext i32 [[M]] to i64
-; CHECK-DELIN-NEXT: br i1 [[CMP222]], label %[[INNER_FOR_BODY_SPLIT1:.*]], label %[[INNER_FOR_BODY_SPLIT:.*]]
+; CHECK-DELIN-NEXT: br i1 [[CMP222]], label %[[INNER_PREHEADER:.*]], label %[[OUTER_LATCH]]
; CHECK-DELIN: [[INNER_PREHEADER]]:
-; CHECK-DELIN-NEXT: [[WIDE_TRIP_COUNT29:%.*]] = zext i32 [[N]] to i64
+; CHECK-DELIN-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext i32 [[M]] to i64
; CHECK-DELIN-NEXT: br label %[[INNER_FOR_BODY:.*]]
; CHECK-DELIN: [[INNER_FOR_BODY]]:
-; CHECK-DELIN-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, %[[INNER_PREHEADER]] ], [ [[TMP1:%.*]], %[[INNER_FOR_BODY_SPLIT]] ]
-; CHECK-DELIN-NEXT: br label %[[OUTER_PREHEADER]]
-; CHECK-DELIN: [[INNER_FOR_BODY_SPLIT1]]:
+; CHECK-DELIN-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, %[[INNER_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], %[[INNER_FOR_BODY]] ]
; CHECK-DELIN-NEXT: [[ARRAYIDX6:%.*]] = getelementptr inbounds [16 x [16 x i32]], ptr [[TEMP]], i64 0, i64 [[INDVARS_IV]], i64 [[INDVARS_IV27]]
; CHECK-DELIN-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARRAYIDX6]], align 4
; CHECK-DELIN-NEXT: [[ARRAYIDX8:%.*]] = getelementptr inbounds [16 x [16 x i32]], ptr [[RES]], i64 0, i64 [[INDVARS_IV]], i64 [[INDVARS_IV27]]
; CHECK-DELIN-NEXT: store i32 [[TMP0]], ptr [[ARRAYIDX8]], align 4
-; CHECK-DELIN-NEXT: [[INDVARS_IV_NEXT:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 1
+; CHECK-DELIN-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
; CHECK-DELIN-NEXT: [[EXITCOND:%.*]] = icmp ne i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]]
-; CHECK-DELIN-NEXT: br label %[[INNER_CRIT_EDGE:.*]]
-; CHECK-DELIN: [[INNER_FOR_BODY_SPLIT]]:
-; CHECK-DELIN-NEXT: [[WIDE_TRIP_COUNT_LCSSA:%.*]] = phi i64 [ [[WIDE_TRIP_COUNT]], %[[OUTER_LATCH]] ], [ [[WIDE_TRIP_COUNT]], %[[OUTER_HEADER]] ]
-; CHECK-DELIN-NEXT: [[TMP1]] = add nuw nsw i64 [[INDVARS_IV]], 1
-; CHECK-DELIN-NEXT: [[TMP2:%.*]] = icmp ne i64 [[TMP1]], [[WIDE_TRIP_COUNT_LCSSA]]
-; CHECK-DELIN-NEXT: br i1 [[TMP2]], label %[[INNER_FOR_BODY]], label %[[OUTER_CRIT_EDGE:.*]]
+; CHECK-DELIN-NEXT: br i1 [[EXITCOND]], label %[[INNER_FOR_BODY]], label %[[INNER_CRIT_EDGE:.*]]
; CHECK-DELIN: [[INNER_CRIT_EDGE]]:
; CHECK-DELIN-NEXT: br label %[[OUTER_LATCH]]
; CHECK-DELIN: [[OUTER_LATCH]]:
; CHECK-DELIN-NEXT: [[INDVARS_IV_NEXT28]] = add nuw nsw i64 [[INDVARS_IV27]], 1
; CHECK-DELIN-NEXT: [[EXITCOND30:%.*]] = icmp ne i64 [[INDVARS_IV_NEXT28]], [[WIDE_TRIP_COUNT29]]
-; CHECK-DELIN-NEXT: br i1 [[EXITCOND30]], label %[[OUTER_HEADER]], label %[[INNER_FOR_BODY_SPLIT]]
+; CHECK-DELIN-NEXT: br i1 [[EXITCOND30]], label %[[OUTER_HEADER]], label %[[OUTER_CRIT_EDGE:.*]]
; CHECK-DELIN: [[OUTER_CRIT_EDGE]]:
; CHECK-DELIN-NEXT: br label %[[FOR_COND_CLEANUP]]
; CHECK-DELIN: [[FOR_COND_CLEANUP]]:
@@ -156,21 +152,21 @@ define void @test2(i32 %N) {
; CHECK-NEXT: [[BB:.*]]:
; CHECK-NEXT: br label %[[OUTER_HEADER:.*]]
; CHECK: [[OUTER_HEADER]]:
-; CHECK-NEXT: [[OUTER_IV:%.*]] = phi i64 [ 0, %[[BB]] ], [ [[OUTER_IV_NEXT:%.*]], %[[EXIT:.*]] ]
+; CHECK-NEXT: [[OUTER_IV:%.*]] = phi i64 [ 0, %[[BB]] ], [ [[OUTER_IV_NEXT:%.*]], %[[OUTER_LATCH:.*]] ]
; CHECK-NEXT: [[N_EXT:%.*]] = sext i32 [[N]] to i64
; CHECK-NEXT: br label %[[INNER:.*]]
; CHECK: [[INNER]]:
-; CHECK-NEXT: [[INNER_IV:%.*]] = phi i64 [ 0, %[[OUTER_HEADER]] ], [ [[TMP0:%.*]], %[[INNER]] ]
-; CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds [2 x i16], ptr @global, i64 [[INNER_IV]], i64 [[OUTER_IV]]
-; CHECK-NEXT: store i16 0, ptr [[TMP8]], align 2
-; CHECK-NEXT: [[TMP0]] = add nsw i64 [[INNER_IV]], 1
-; CHECK-NEXT: [[TMP1:%.*]] = icmp ne i64 [[TMP0]], [[N_EXT]]
-; CHECK-NEXT: br i1 [[TMP1]], label %[[INNER]], label %[[EXIT]]
-; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[INNER_IV:%.*]] = phi i64 [ 0, %[[OUTER_HEADER]] ], [ [[INNER_IV_NEXT:%.*]], %[[INNER]] ]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [2 x i16], ptr @global, i64 [[INNER_IV]], i64 [[OUTER_IV]]
+; CHECK-NEXT: store i16 0, ptr [[GEP]], align 2
+; CHECK-NEXT: [[INNER_IV_NEXT]] = add nsw i64 [[INNER_IV]], 1
+; CHECK-NEXT: [[C_1:%.*]] = icmp ne i64 [[INNER_IV_NEXT]], [[N_EXT]]
+; CHECK-NEXT: br i1 [[C_1]], label %[[INNER]], label %[[OUTER_LATCH]]
+; CHECK: [[OUTER_LATCH]]:
; CHECK-NEXT: [[OUTER_IV_NEXT]] = add nsw i64 [[OUTER_IV]], 1
; CHECK-NEXT: [[C_2:%.*]] = icmp ne i64 [[OUTER_IV]], [[N_EXT]]
-; CHECK-NEXT: br i1 [[C_2]], label %[[OUTER_HEADER]], label %[[EXIT1:.*]]
-; CHECK: [[EXIT1]]:
+; CHECK-NEXT: br i1 [[C_2]], label %[[OUTER_HEADER]], label %[[EXIT:.*]]
+; CHECK: [[EXIT]]:
; CHECK-NEXT: ret void
;
; CHECK-DELIN-LABEL: define void @test2(
@@ -189,8 +185,8 @@ define void @test2(i32 %N) {
; CHECK-DELIN-NEXT: [[INNER_IV:%.*]] = phi i64 [ [[TMP0:%.*]], %[[INNER_SPLIT:.*]] ], [ 0, %[[INNER_PREHEADER]] ]
; CHECK-DELIN-NEXT: br label %[[OUTER_HEADER_PREHEADER]]
; CHECK-DELIN: [[INNER_SPLIT1]]:
-; CHECK-DELIN-NEXT: [[TMP8:%.*]] = getelementptr inbounds [2 x i16], ptr @global, i64 [[INNER_IV]], i64 [[OUTER_IV]]
-; CHECK-DELIN-NEXT: store i16 0, ptr [[TMP8]], align 2
+; CHECK-DELIN-NEXT: [[GEP:%.*]] = getelementptr inbounds [2 x i16], ptr @global, i64 [[INNER_IV]], i64 [[OUTER_IV]]
+; CHECK-DELIN-NEXT: store i16 0, ptr [[GEP]], align 2
; CHECK-DELIN-NEXT: [[INNER_IV_NEXT:%.*]] = add nsw i64 [[INNER_IV]], 1
; CHECK-DELIN-NEXT: [[C_1:%.*]] = icmp ne i64 [[INNER_IV_NEXT]], [[N_EXT]]
; CHECK-DELIN-NEXT: br label %[[OUTER_LATCH]]
diff --git a/llvm/test/Transforms/LoopInterchange/loop-interchange-optimization-remarks.ll b/llvm/test/Transforms/LoopInterchange/loop-interchange-optimization-remarks.ll
index 22b6efee6e11e..b656f15bb92d1 100644
--- a/llvm/test/Transforms/LoopInterchange/loop-interchange-optimization-remarks.ll
+++ b/llvm/test/Transforms/LoopInterchange/loop-interchange-optimization-remarks.ll
@@ -92,12 +92,12 @@ for.end19:
; DELIN-NEXT: ...
;;--------------------------------------Test case 02------------------------------------
-;; [FIXME] This loop though valid is currently not interchanged due to the
-;; limitation that we cannot split the inner loop latch due to multiple use of inner induction
-;; variable.(used to increment the loop counter and to access A[j+1][i+1]
+;; A guarded, imperfect nest that must not be interchanged; see the
+;; explanation above the DELIN checks below.
;; for(int i=0;i<N-1;i++)
-;; for(int j=1;j<N-1;j++)
-;; A[j+1][i+1] = A[j+1][i+1] + k;
+;; if(N-1>1) // guard: inner loop skipped when N<=2
+;; for(int j=1;j<N-1;j++)
+;; A[j+1][i+1] = A[j+1][i+1] + k;
define void @test02(i32 %k, i32 %N) {
entry:
@@ -159,12 +159,15 @@ define void @test02(i32 %k, i32 %N) {
; DELIN-NEXT: - String: Computed dependence info, invoking the transform.
; DELIN-NEXT: ...
-; DELIN: --- !Passed
+; Guarded nest: %for.cond1.preheader runs the inner loop only when
+; %cmp324 = (N-1 > 1). The inner exit (inner IV == N-2) only terminates under
+; that guard, so interchanging it loops forever for N == 2.
+; DELIN: --- !Missed
; DELIN-NEXT: Pass: loop-interchange
-; DELIN-NEXT: Name: Interchanged
+; DELIN-NEXT: Name: NotTightlyNested
; DELIN-NEXT: Function: test02
; DELIN-NEXT: Args:
-; DELIN-NEXT: - String: Loop interchanged with enclosing loop.
+; DELIN-NEXT: - String: Cannot interchange loops because they are not tightly nested.
; DELIN-NEXT: ...
;;-----------------------------------Test case 03-------------------------------
More information about the llvm-commits
mailing list