[llvm] [LoopInterchange] Bail out when outer loop latch PHI has non-PHI user (PR #201923)
Ryotaro Kasuga via llvm-commits
llvm-commits at lists.llvm.org
Thu Jun 11 04:56:32 PDT 2026
https://github.com/kasuga-fj updated https://github.com/llvm/llvm-project/pull/201923
>From 0661044cd85ca3ed94665a33f34b416da460fc82 Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Sat, 6 Jun 2026 04:17:32 +0900
Subject: [PATCH] [LoopInterchange] Bail out when outer loop latch PHI has
non-PHI user
---
.../lib/Transforms/Scalar/LoopInterchange.cpp | 26 ++++-
.../LoopInterchange/non-phi-uses-lcssa-phi.ll | 98 ++++++++++++++-----
2 files changed, 96 insertions(+), 28 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
index b2e3bbe516ed8..336446e1cd4fc 100644
--- a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
@@ -1593,6 +1593,26 @@ bool LoopInterchangeLegality::canInterchangeLoops(unsigned InnerLoopId,
return false;
}
+ // Regarding def-use chains that begin at an LCSSA PHI in the inner loop exit
+ // and end at any instruction in the outer loop latch, we currently support
+ // only the case where the chain contains only PHI nodes. Since we already
+ // call `tightlyNested()`, we know that if there is a def-use chain that we
+ // don't support (i.e., a chain that contains a non-PHI user), then the
+ // non-PHI user must be in the outer loop latch.
+ if (InnerLoop->getExitBlock() != OuterLoop->getLoopLatch())
+ for (PHINode &PHI : OuterLoop->getLoopLatch()->phis())
+ if (any_of(PHI.users(), [](const User *U) { return !isa<PHINode>(U); })) {
+ LLVM_DEBUG(dbgs() << "Outer loop latch PHI has a non-PHI user.\n");
+ ORE->emit([&]() {
+ return OptimizationRemarkMissed(DEBUG_TYPE, "UnsupportedLatchPHI",
+ OuterLoop->getStartLoc(),
+ OuterLoop->getHeader())
+ << "Cannot interchange loops because an outer loop latch PHI "
+ "node has a non-PHI user.";
+ });
+ return false;
+ }
+
return true;
}
@@ -2245,14 +2265,12 @@ static void moveLCSSAPhis(BasicBlock *InnerExit, BasicBlock *InnerHeader,
assert(all_of(P.users(),
[OuterHeader, OuterExit, IncI, InnerHeader](User *U) {
- if (!isa<PHINode>(U))
- return true;
return (cast<PHINode>(U)->getParent() == OuterHeader &&
IncI->getParent() == InnerHeader) ||
cast<PHINode>(U)->getParent() == OuterExit;
}) &&
- "Can only replace phis iff the phi-uses are in the loop nest exit "
- "or the incoming value is defined in the inner header (it will "
+ "Can only replace phis iff the uses are in the loop nest exit or "
+ "the incoming value is defined in the inner header (it will "
"dominate all loop blocks after interchanging)");
P.replaceAllUsesWith(IncI);
P.eraseFromParent();
diff --git a/llvm/test/Transforms/LoopInterchange/non-phi-uses-lcssa-phi.ll b/llvm/test/Transforms/LoopInterchange/non-phi-uses-lcssa-phi.ll
index 60d920013d8b8..0b445823c666b 100644
--- a/llvm/test/Transforms/LoopInterchange/non-phi-uses-lcssa-phi.ll
+++ b/llvm/test/Transforms/LoopInterchange/non-phi-uses-lcssa-phi.ll
@@ -1,46 +1,35 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
; RUN: opt < %s -passes=loop-interchange -loop-interchange-profitabilities=ignore -S | FileCheck %s
-; This is a test for the assert that checks that we only replace phis iff the
-; phi-uses are in the loop nest exit or the incoming value is defined in the
-; inner header. We only want to check this when the user is a phi, which does
-; not always need to be the case.
+; Test cases that contain non-phi users of LCSSA phis (or phis that refer to
+; LCSSA phis). At the moment we don't support such cases.
define i64 @non_phi_lcssa_phi_user(ptr %A) {
; CHECK-LABEL: define i64 @non_phi_lcssa_phi_user(
; CHECK-SAME: ptr [[A:%.*]]) {
-; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: br label %[[INNER_PREHEADER:.*]]
-; CHECK: [[OUTER_HEADER_PREHEADER:.*]]:
-; CHECK-NEXT: br label %[[OUTER_HEADER:.*]]
-; CHECK: [[OUTER_HEADER]]:
-; CHECK-NEXT: [[I:%.*]] = phi i64 [ [[I_NEXT:%.*]], %[[OUTER_LATCH:.*]] ], [ 0, %[[OUTER_HEADER_PREHEADER]] ]
-; CHECK-NEXT: br label %[[INNER_SPLIT1:.*]]
-; CHECK: [[INNER_PREHEADER]]:
+; CHECK-NEXT: [[INNER_PREHEADER:.*]]:
; CHECK-NEXT: br label %[[INNER:.*]]
; CHECK: [[INNER]]:
-; CHECK-NEXT: [[J:%.*]] = phi i64 [ [[TMP1:%.*]], %[[INNER_SPLIT:.*]] ], [ 0, %[[INNER_PREHEADER]] ]
-; CHECK-NEXT: br label %[[OUTER_HEADER_PREHEADER]]
-; CHECK: [[INNER_SPLIT1]]:
+; CHECK-NEXT: [[I:%.*]] = phi i64 [ 0, %[[INNER_PREHEADER]] ], [ [[I_NEXT:%.*]], %[[OUTER_LATCH:.*]] ]
+; CHECK-NEXT: br label %[[OUTER_HEADER_PREHEADER:.*]]
+; CHECK: [[OUTER_HEADER_PREHEADER]]:
+; CHECK-NEXT: [[J:%.*]] = phi i64 [ 0, %[[INNER]] ], [ [[TMP1:%.*]], %[[OUTER_HEADER_PREHEADER]] ]
; CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds [100 x i32], ptr [[A]], i64 [[J]], i64 [[I]]
; CHECK-NEXT: store i32 0, ptr [[PTR]], align 4
-; CHECK-NEXT: [[J_NEXT:%.*]] = add i64 [[J]], 1
-; CHECK-NEXT: [[EXITCOND_INNER:%.*]] = icmp eq i64 [[J]], 99
-; CHECK-NEXT: br label %[[INNER_EXIT:.*]]
-; CHECK: [[INNER_SPLIT]]:
-; CHECK-NEXT: [[TMP0:%.*]] = phi i64 [ [[USE_J:%.*]], %[[OUTER_LATCH]] ]
; CHECK-NEXT: [[TMP1]] = add i64 [[J]], 1
; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i64 [[J]], 99
-; CHECK-NEXT: br i1 [[TMP2]], label %[[EXIT:.*]], label %[[INNER]]
+; CHECK-NEXT: br i1 [[TMP2]], label %[[INNER_EXIT:.*]], label %[[OUTER_HEADER_PREHEADER]]
; CHECK: [[INNER_EXIT]]:
+; CHECK-NEXT: [[J_LCSSA:%.*]] = phi i64 [ [[J]], %[[OUTER_HEADER_PREHEADER]] ]
; CHECK-NEXT: br label %[[OUTER_LATCH]]
; CHECK: [[OUTER_LATCH]]:
-; CHECK-NEXT: [[USE_J]] = add i64 [[J]], 1
+; CHECK-NEXT: [[J_LATCH:%.*]] = phi i64 [ [[J_LCSSA]], %[[INNER_EXIT]] ]
+; CHECK-NEXT: [[USE_J:%.*]] = add i64 [[J_LATCH]], 1
; CHECK-NEXT: [[I_NEXT]] = add i64 [[I]], 1
; CHECK-NEXT: [[EXITCOND_OUTER:%.*]] = icmp eq i64 [[I]], 99
-; CHECK-NEXT: br i1 [[EXITCOND_OUTER]], label %[[INNER_SPLIT]], label %[[OUTER_HEADER]]
+; CHECK-NEXT: br i1 [[EXITCOND_OUTER]], label %[[EXIT:.*]], label %[[INNER]]
; CHECK: [[EXIT]]:
-; CHECK-NEXT: [[RESULT:%.*]] = phi i64 [ [[TMP0]], %[[INNER_SPLIT]] ]
+; CHECK-NEXT: [[RESULT:%.*]] = phi i64 [ [[USE_J]], %[[OUTER_LATCH]] ]
; CHECK-NEXT: ret i64 [[RESULT]]
;
entry:
@@ -73,3 +62,64 @@ exit:
%result = phi i64 [ %use.j, %outer.latch ]
ret i64 %result
}
+
+define void @non_phi_lcssa_phi_user2(ptr %A) {
+; CHECK-LABEL: define void @non_phi_lcssa_phi_user2(
+; CHECK-SAME: ptr [[A:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[OUTER_HEADER:.*]]
+; CHECK: [[OUTER_HEADER]]:
+; CHECK-NEXT: [[I:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[I_INC:%.*]], %[[OUTER_LATCH:.*]] ]
+; CHECK-NEXT: br label %[[INNER:.*]]
+; CHECK: [[INNER]]:
+; CHECK-NEXT: [[J:%.*]] = phi i64 [ 0, %[[OUTER_HEADER]] ], [ [[J_INC:%.*]], %[[INNER]] ]
+; CHECK-NEXT: [[P:%.*]] = getelementptr [4 x i8], ptr [[A]], i64 [[J]], i64 [[I]]
+; CHECK-NEXT: [[V:%.*]] = load i8, ptr [[P]], align 1
+; CHECK-NEXT: [[ADD:%.*]] = add i8 [[V]], 1
+; CHECK-NEXT: store i8 [[ADD]], ptr [[P]], align 1
+; CHECK-NEXT: [[J_INC]] = add i64 [[J]], 1
+; CHECK-NEXT: [[EC_J:%.*]] = icmp eq i64 [[J_INC]], 4
+; CHECK-NEXT: br i1 [[EC_J]], label %[[OUTER_BODY:.*]], label %[[INNER]]
+; CHECK: [[OUTER_BODY]]:
+; CHECK-NEXT: [[LCSSA_ADD_0:%.*]] = phi i8 [ [[ADD]], %[[INNER]] ]
+; CHECK-NEXT: br label %[[OUTER_LATCH]]
+; CHECK: [[OUTER_LATCH]]:
+; CHECK-NEXT: [[LCSSA_ADD_1:%.*]] = phi i8 [ [[LCSSA_ADD_0]], %[[OUTER_BODY]] ]
+; CHECK-NEXT: [[USE:%.*]] = add i8 [[LCSSA_ADD_1]], 1
+; CHECK-NEXT: [[I_INC]] = add i64 [[I]], 1
+; CHECK-NEXT: [[EC_I:%.*]] = icmp eq i64 [[I_INC]], 4
+; CHECK-NEXT: br i1 [[EC_I]], label %[[EXIT:.*]], label %[[OUTER_HEADER]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %outer.header
+
+outer.header:
+ %i = phi i64 [ 0, %entry ], [ %i.inc, %outer.latch ]
+ br label %inner
+
+inner:
+ %j = phi i64 [ 0, %outer.header ],[ %j.inc, %inner ]
+ %p = getelementptr [4 x i8], ptr %A, i64 %j, i64 %i
+ %v = load i8, ptr %p
+ %add = add i8 %v, 1
+ store i8 %add, ptr %p
+ %j.inc = add i64 %j, 1
+ %ec.j = icmp eq i64 %j.inc, 4
+ br i1 %ec.j, label %outer.body, label %inner
+
+outer.body:
+ %lcssa.add.0 = phi i8 [ %add, %inner ]
+ br label %outer.latch
+
+outer.latch:
+ %lcssa.add.1 = phi i8 [ %lcssa.add.0, %outer.body ]
+ %use = add i8 %lcssa.add.1, 1
+ %i.inc = add i64 %i, 1
+ %ec.i = icmp eq i64 %i.inc, 4
+ br i1 %ec.i, label %exit, label %outer.header
+
+exit:
+ ret void
+}
More information about the llvm-commits
mailing list