[llvm] 2dbff02 - [LoopInterchange] Fix handling of PHI which refers to another PHI (#194364)

via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 30 04:07:32 PDT 2026


Author: Ryotaro Kasuga
Date: 2026-04-30T11:07:27Z
New Revision: 2dbff02fad7e12ed3b15c72a2cb1a68e89c630c4

URL: https://github.com/llvm/llvm-project/commit/2dbff02fad7e12ed3b15c72a2cb1a68e89c630c4
DIFF: https://github.com/llvm/llvm-project/commit/2dbff02fad7e12ed3b15c72a2cb1a68e89c630c4.diff

LOG: [LoopInterchange] Fix handling of PHI which refers to another PHI (#194364)

In the transformation phase, at first LoopInterchange moves several
instructions in the inner loop into the new latch block. The
instructions used as incoming values to the induction variables from the
latch block are the targets of this movement. Previously, this process
could result in an infinite loop when a PHI node refers to another PHI
node, as in the following example:

```
%i = phi i64 [ 0, %entry ], [ %i.inc, %latch ]
%j = phi i64 [ 0, %entry ], [ %i, %latch ]
```

The root cause was that `%i` enqueued for processing because it is used
by `%j`.
This patch fixes the issue by preventing induction variables from being
enqueued into the movement list.
Fix #193733

Added: 
    llvm/test/Transforms/LoopInterchange/phi-to-phi.ll

Modified: 
    llvm/lib/Transforms/Scalar/LoopInterchange.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
index 49a9e77ef8deb..91e2510c33851 100644
--- a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
@@ -1935,7 +1935,6 @@ bool LoopInterchangeTransform::transform(
     reduction2Memory();
 
   if (InnerLoop->getSubLoops().empty()) {
-    BasicBlock *InnerLoopPreHeader = InnerLoop->getLoopPreheader();
     LLVM_DEBUG(dbgs() << "Splitting the inner loop latch\n");
     auto &InductionPHIs = LIL.getInnerLoopInductions();
     if (InductionPHIs.empty()) {
@@ -1945,12 +1944,13 @@ bool LoopInterchangeTransform::transform(
 
     SmallVector<Instruction *, 8> InnerIndexVarList;
     for (PHINode *CurInductionPHI : InductionPHIs) {
-      if (CurInductionPHI->getIncomingBlock(0) == InnerLoopPreHeader)
-        InnerIndexVarList.push_back(
-            dyn_cast<Instruction>(CurInductionPHI->getIncomingValue(1)));
-      else
-        InnerIndexVarList.push_back(
-            dyn_cast<Instruction>(CurInductionPHI->getIncomingValue(0)));
+      Instruction *IncomingValue = dyn_cast<Instruction>(
+          CurInductionPHI->getIncomingValueForBlock(InnerLoop->getLoopLatch()));
+      assert(IncomingValue &&
+             "Incoming value from loop latch doesn't an instruction");
+      if (is_contained(InductionPHIs, IncomingValue))
+        continue;
+      InnerIndexVarList.push_back(IncomingValue);
     }
 
     // Create a new latch block for the inner loop. We split at the

diff  --git a/llvm/test/Transforms/LoopInterchange/phi-to-phi.ll b/llvm/test/Transforms/LoopInterchange/phi-to-phi.ll
new file mode 100644
index 0000000000000..8fb336e1cac88
--- /dev/null
+++ b/llvm/test/Transforms/LoopInterchange/phi-to-phi.ll
@@ -0,0 +1,62 @@
+; 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 -verify-dom-info -verify-loop-info -verify-scev -verify-loop-lcssa -S | FileCheck %s
+
+; Check that `%k`, which refers to another phi node, is processed correctly.
+
+define void @f(ptr %A) {
+; CHECK-LABEL: define void @f(
+; 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_INC:%.*]], %[[OUTER_LATCH:.*]] ], [ 0, %[[OUTER_HEADER_PREHEADER]] ]
+; CHECK-NEXT:    br label %[[INNER_SPLIT1:.*]]
+; CHECK:       [[INNER_PREHEADER]]:
+; CHECK-NEXT:    br label %[[INNER:.*]]
+; CHECK:       [[INNER]]:
+; CHECK-NEXT:    [[J:%.*]] = phi i64 [ [[TMP0:%.*]], %[[INNER_SPLIT:.*]] ], [ 0, %[[INNER_PREHEADER]] ]
+; CHECK-NEXT:    [[K:%.*]] = phi i64 [ [[J]], %[[INNER_SPLIT]] ], [ -1, %[[INNER_PREHEADER]] ]
+; CHECK-NEXT:    br label %[[OUTER_HEADER_PREHEADER]]
+; CHECK:       [[INNER_SPLIT1]]:
+; CHECK-NEXT:    [[GEP:%.*]] = getelementptr [10 x i64], ptr [[A]], i64 [[I]], i64 [[J]]
+; CHECK-NEXT:    store i64 [[K]], ptr [[GEP]], align 4
+; CHECK-NEXT:    [[J_INC:%.*]] = add i64 [[J]], 1
+; CHECK-NEXT:    [[EC_J:%.*]] = icmp eq i64 [[J_INC]], 10
+; CHECK-NEXT:    br label %[[OUTER_LATCH]]
+; CHECK:       [[INNER_SPLIT]]:
+; CHECK-NEXT:    [[TMP0]] = add i64 [[J]], 1
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i64 [[TMP0]], 10
+; CHECK-NEXT:    br i1 [[TMP1]], label %[[EXIT:.*]], label %[[INNER]]
+; CHECK:       [[OUTER_LATCH]]:
+; CHECK-NEXT:    [[I_INC]] = add i64 [[I]], 1
+; CHECK-NEXT:    [[EC_I:%.*]] = icmp eq i64 [[I_INC]], 10
+; CHECK-NEXT:    br i1 [[EC_I]], label %[[INNER_SPLIT]], 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 ]
+  %k = phi i64 [ -1, %outer.header ], [ %j, %inner ]
+  %gep = getelementptr [10 x i64], ptr %A, i64 %i, i64 %j
+  store i64 %k, ptr %gep
+  %j.inc = add i64 %j, 1
+  %ec.j = icmp eq i64 %j.inc, 10
+  br i1 %ec.j, label %outer.latch, label %inner
+
+outer.latch:
+  %i.inc = add i64 %i, 1
+  %ec.i = icmp eq i64 %i.inc, 10
+  br i1 %ec.i, label %exit, label %outer.header
+
+exit:
+  ret void
+}


        


More information about the llvm-commits mailing list