[llvm] [LoopInterchange] Check all inner-exit LCSSA PHIs (PR #200860)

Madhur Amilkanthwar via llvm-commits llvm-commits at lists.llvm.org
Wed Jun 3 20:47:10 PDT 2026


https://github.com/madhur13490 updated https://github.com/llvm/llvm-project/pull/200860

>From c83bbd00e5bb7e997a7872cf490bc95f41313edf Mon Sep 17 00:00:00 2001
From: Madhur Amilkanthwar <madhura at nvidia.com>
Date: Mon, 1 Jun 2026 09:08:41 -0700
Subject: [PATCH 1/3] [LoopInterchange] Check all inner-exit LCSSA PHIs

areInnerLoopExitPHIsSupported() returned true as soon as it saw the
reduction LCSSA PHI, skipping the user-check for any later LCSSA PHIs.
If one had a non-PHI user, legality wrongly succeeded and the
transform hit a cast<PHINode> assertion. Use continue so the remaining
PHIs are still validated.

Fixes #200811.
---
 .../lib/Transforms/Scalar/LoopInterchange.cpp |  4 +-
 .../reduction2mem-extra-lcssa.ll              | 54 +++++++++++++++++++
 2 files changed, 57 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/Transforms/LoopInterchange/reduction2mem-extra-lcssa.ll

diff --git a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
index 9d424ae13e28a..89071fd0ce88d 100644
--- a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
@@ -1371,8 +1371,10 @@ areInnerLoopExitPHIsSupported(Loop *OuterL, Loop *InnerL,
     // from the loop latch.
     if (PHI.getNumIncomingValues() > 1)
       return false;
+    // The reduction LCSSA PHI's store user is rewritten by reduction2Memory();
+    // skip its user-check but keep validating the remaining LCSSA PHIs.
     if (&PHI == LcssaReduction)
-      return true;
+      continue;
     if (any_of(PHI.users(), [&Reductions, OuterL](User *U) {
           PHINode *PN = dyn_cast<PHINode>(U);
           if (!PN)
diff --git a/llvm/test/Transforms/LoopInterchange/reduction2mem-extra-lcssa.ll b/llvm/test/Transforms/LoopInterchange/reduction2mem-extra-lcssa.ll
new file mode 100644
index 0000000000000..55ef6e87a0b04
--- /dev/null
+++ b/llvm/test/Transforms/LoopInterchange/reduction2mem-extra-lcssa.ll
@@ -0,0 +1,54 @@
+; Non-reduction LCSSA PHIs in the inner-loop exit must still be validated
+; even when a reduction LCSSA PHI is present. Used to crash.
+;
+; RUN: opt < %s -passes=loop-interchange -loop-interchange-reduction-to-mem \
+; RUN:   -pass-remarks-missed=loop-interchange -pass-remarks-output=%t -S \
+; RUN:   | FileCheck -check-prefix=IR %s
+; RUN: FileCheck --input-file=%t %s
+
+; CHECK: --- !Missed
+; CHECK-NEXT: Pass:            loop-interchange
+; CHECK-NEXT: Name:            UnsupportedExitPHI
+; CHECK-NEXT: Function:        reduction_lcssa_with_non_phi_user
+; CHECK-NEXT: Args:
+; CHECK-NEXT:   - String:          Found unsupported PHI node in loop exit.
+
+ at A = common global [100 x [100 x i32]] zeroinitializer
+ at sum = common global [100 x i32] zeroinitializer
+
+; IR-LABEL: @reduction_lcssa_with_non_phi_user(
+; IR-NOT: split
+define void @reduction_lcssa_with_non_phi_user() {
+entry:
+  br label %outer.header
+
+outer.header:
+  %i = phi i64 [ 0, %entry ], [ %i.next, %outer.latch ]
+  %sum.ptr = getelementptr inbounds [100 x i32], ptr @sum, i64 0, i64 %i
+  br label %inner
+
+inner:
+  %j = phi i64 [ 0, %outer.header ], [ %j.next, %inner ]
+  %red = phi i32 [ 0, %outer.header ], [ %add, %inner ]
+  %ptr = getelementptr inbounds [100 x [100 x i32]], ptr @A, i64 0, i64 %j, i64 %i
+  %val = load i32, ptr %ptr
+  %add = add i32 %red, %val
+  %j.next = add nuw nsw i64 %j, 1
+  %exitcond.inner = icmp eq i64 %j, 99
+  br i1 %exitcond.inner, label %inner.exit, label %inner
+
+inner.exit:
+  %red.lcssa = phi i32 [ %add, %inner ]
+  %j.lcssa = phi i64 [ %j, %inner ]
+  store i32 %red.lcssa, ptr %sum.ptr
+  %use.j = add i64 %j.lcssa, 1
+  br label %outer.latch
+
+outer.latch:
+  %i.next = add nuw nsw i64 %i, 1
+  %exitcond.outer = icmp eq i64 %i, 99
+  br i1 %exitcond.outer, label %exit, label %outer.header
+
+exit:
+  ret void
+}

>From 910e80b35aac58a5928e14be3fc2831f217f1c11 Mon Sep 17 00:00:00 2001
From: Madhur Amilkanthwar <madhura at nvidia.com>
Date: Tue, 2 Jun 2026 23:51:27 -0700
Subject: [PATCH 2/3] fixup! address review comments

---
 .../reduction2mem-extra-lcssa.ll              | 32 +++++++++++++++++--
 1 file changed, 30 insertions(+), 2 deletions(-)

diff --git a/llvm/test/Transforms/LoopInterchange/reduction2mem-extra-lcssa.ll b/llvm/test/Transforms/LoopInterchange/reduction2mem-extra-lcssa.ll
index 55ef6e87a0b04..14cab447fdace 100644
--- a/llvm/test/Transforms/LoopInterchange/reduction2mem-extra-lcssa.ll
+++ b/llvm/test/Transforms/LoopInterchange/reduction2mem-extra-lcssa.ll
@@ -1,3 +1,4 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals none --version 6
 ; Non-reduction LCSSA PHIs in the inner-loop exit must still be validated
 ; even when a reduction LCSSA PHI is present. Used to crash.
 ;
@@ -16,9 +17,36 @@
 @A = common global [100 x [100 x i32]] zeroinitializer
 @sum = common global [100 x i32] zeroinitializer
 
-; IR-LABEL: @reduction_lcssa_with_non_phi_user(
-; IR-NOT: split
 define void @reduction_lcssa_with_non_phi_user() {
+; IR-LABEL: define void @reduction_lcssa_with_non_phi_user() {
+; IR-NEXT:  [[ENTRY:.*]]:
+; IR-NEXT:    br label %[[OUTER_HEADER:.*]]
+; IR:       [[OUTER_HEADER]]:
+; IR-NEXT:    [[I:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[I_NEXT:%.*]], %[[OUTER_LATCH:.*]] ]
+; IR-NEXT:    [[SUM_PTR:%.*]] = getelementptr inbounds [100 x i32], ptr @sum, i64 0, i64 [[I]]
+; IR-NEXT:    br label %[[INNER:.*]]
+; IR:       [[INNER]]:
+; IR-NEXT:    [[J:%.*]] = phi i64 [ 0, %[[OUTER_HEADER]] ], [ [[J_NEXT:%.*]], %[[INNER]] ]
+; IR-NEXT:    [[RED:%.*]] = phi i32 [ 0, %[[OUTER_HEADER]] ], [ [[ADD:%.*]], %[[INNER]] ]
+; IR-NEXT:    [[PTR:%.*]] = getelementptr inbounds [100 x [100 x i32]], ptr @A, i64 0, i64 [[J]], i64 [[I]]
+; IR-NEXT:    [[VAL:%.*]] = load i32, ptr [[PTR]], align 4
+; IR-NEXT:    [[ADD]] = add i32 [[RED]], [[VAL]]
+; IR-NEXT:    [[J_NEXT]] = add nuw nsw i64 [[J]], 1
+; IR-NEXT:    [[EXITCOND_INNER:%.*]] = icmp eq i64 [[J]], 99
+; IR-NEXT:    br i1 [[EXITCOND_INNER]], label %[[INNER_EXIT:.*]], label %[[INNER]]
+; IR:       [[INNER_EXIT]]:
+; IR-NEXT:    [[RED_LCSSA:%.*]] = phi i32 [ [[ADD]], %[[INNER]] ]
+; IR-NEXT:    [[J_LCSSA:%.*]] = phi i64 [ [[J]], %[[INNER]] ]
+; IR-NEXT:    store i32 [[RED_LCSSA]], ptr [[SUM_PTR]], align 4
+; IR-NEXT:    [[USE_J:%.*]] = add i64 [[J_LCSSA]], 1
+; IR-NEXT:    br label %[[OUTER_LATCH]]
+; IR:       [[OUTER_LATCH]]:
+; IR-NEXT:    [[I_NEXT]] = add nuw nsw i64 [[I]], 1
+; IR-NEXT:    [[EXITCOND_OUTER:%.*]] = icmp eq i64 [[I]], 99
+; IR-NEXT:    br i1 [[EXITCOND_OUTER]], label %[[EXIT:.*]], label %[[OUTER_HEADER]]
+; IR:       [[EXIT]]:
+; IR-NEXT:    ret void
+;
 entry:
   br label %outer.header
 

>From cc387396d9971bd5e6c91e055c390631472ab6de Mon Sep 17 00:00:00 2001
From: Madhur Amilkanthwar <madhura at nvidia.com>
Date: Wed, 3 Jun 2026 20:45:52 -0700
Subject: [PATCH 3/3] fixup! use simplified GEPs

---
 .../LoopInterchange/reduction2mem-extra-lcssa.ll          | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/llvm/test/Transforms/LoopInterchange/reduction2mem-extra-lcssa.ll b/llvm/test/Transforms/LoopInterchange/reduction2mem-extra-lcssa.ll
index 14cab447fdace..80fd658fc5b15 100644
--- a/llvm/test/Transforms/LoopInterchange/reduction2mem-extra-lcssa.ll
+++ b/llvm/test/Transforms/LoopInterchange/reduction2mem-extra-lcssa.ll
@@ -23,12 +23,12 @@ define void @reduction_lcssa_with_non_phi_user() {
 ; IR-NEXT:    br label %[[OUTER_HEADER:.*]]
 ; IR:       [[OUTER_HEADER]]:
 ; IR-NEXT:    [[I:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[I_NEXT:%.*]], %[[OUTER_LATCH:.*]] ]
-; IR-NEXT:    [[SUM_PTR:%.*]] = getelementptr inbounds [100 x i32], ptr @sum, i64 0, i64 [[I]]
+; IR-NEXT:    [[SUM_PTR:%.*]] = getelementptr inbounds i32, ptr @sum, i64 [[I]]
 ; IR-NEXT:    br label %[[INNER:.*]]
 ; IR:       [[INNER]]:
 ; IR-NEXT:    [[J:%.*]] = phi i64 [ 0, %[[OUTER_HEADER]] ], [ [[J_NEXT:%.*]], %[[INNER]] ]
 ; IR-NEXT:    [[RED:%.*]] = phi i32 [ 0, %[[OUTER_HEADER]] ], [ [[ADD:%.*]], %[[INNER]] ]
-; IR-NEXT:    [[PTR:%.*]] = getelementptr inbounds [100 x [100 x i32]], ptr @A, i64 0, i64 [[J]], i64 [[I]]
+; IR-NEXT:    [[PTR:%.*]] = getelementptr inbounds [100 x i32], ptr @A, i64 [[J]], i64 [[I]]
 ; IR-NEXT:    [[VAL:%.*]] = load i32, ptr [[PTR]], align 4
 ; IR-NEXT:    [[ADD]] = add i32 [[RED]], [[VAL]]
 ; IR-NEXT:    [[J_NEXT]] = add nuw nsw i64 [[J]], 1
@@ -52,13 +52,13 @@ entry:
 
 outer.header:
   %i = phi i64 [ 0, %entry ], [ %i.next, %outer.latch ]
-  %sum.ptr = getelementptr inbounds [100 x i32], ptr @sum, i64 0, i64 %i
+  %sum.ptr = getelementptr inbounds i32, ptr @sum, i64 %i
   br label %inner
 
 inner:
   %j = phi i64 [ 0, %outer.header ], [ %j.next, %inner ]
   %red = phi i32 [ 0, %outer.header ], [ %add, %inner ]
-  %ptr = getelementptr inbounds [100 x [100 x i32]], ptr @A, i64 0, i64 %j, i64 %i
+  %ptr = getelementptr inbounds [100 x i32], ptr @A, i64 %j, i64 %i
   %val = load i32, ptr %ptr
   %add = add i32 %red, %val
   %j.next = add nuw nsw i64 %j, 1



More information about the llvm-commits mailing list