[llvm] [LoopInterchange] Reject inner preheader PHIs with non-identical incoming values (PR #203842)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 15 01:30:22 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Madhur Amilkanthwar (madhur13490)

<details>
<summary>Changes</summary>


When the outer header branches to the inner preheader via duplicate edges, the preheader can contain PHIs with more than one incoming entry. The transform removes such PHIs by substituting the first incoming value for all uses, which is only correct when every incoming value is the same. Bail out of interchange when a preheader PHI has distinct incoming values.

Also relax the assert in the same code path to match: accept equivalent multi-entry PHIs instead of requiring exactly one entry.

Fixes #<!-- -->203466

---
Full diff: https://github.com/llvm/llvm-project/pull/203842.diff


2 Files Affected:

- (modified) llvm/lib/Transforms/Scalar/LoopInterchange.cpp (+21-2) 
- (added) llvm/test/Transforms/LoopInterchange/inner-preheader-multi-entry-phi.ll (+72) 


``````````diff
diff --git a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
index 3da4e99860e45..1478ee4d9cffc 100644
--- a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
@@ -1531,6 +1531,25 @@ bool LoopInterchangeLegality::canInterchangeLoops(unsigned InnerLoopId,
     return false;
   }
 
+  BasicBlock *InnerLoopPreHeader = InnerLoop->getLoopPreheader();
+  // Inner-preheader PHIs are removed by substituting each with its first
+  // incoming value. That is only correct when all incoming values are the
+  // same; a PHI with different incoming values would produce the wrong result.
+  if (InnerLoopPreHeader != OuterLoop->getHeader() &&
+      any_of(InnerLoopPreHeader->phis(),
+             [](PHINode &PHI) { return !all_equal(PHI.incoming_values()); })) {
+    LLVM_DEBUG(dbgs() << "Found unsupported PHI nodes in inner loop "
+                         "preheader.\n");
+    ORE->emit([&]() {
+      return OptimizationRemarkMissed(DEBUG_TYPE, "UnsupportedPreheaderPHI",
+                                      InnerLoop->getStartLoc(),
+                                      InnerLoop->getHeader())
+             << "Cannot interchange loops because unsupported PHI nodes found "
+                "in inner loop preheader.";
+    });
+    return false;
+  }
+
   if (!areInnerLoopLatchPHIsSupported(OuterLoop, InnerLoop)) {
     LLVM_DEBUG(dbgs() << "Found unsupported PHI nodes in inner loop latch.\n");
     ORE->emit([&]() {
@@ -2159,8 +2178,8 @@ bool LoopInterchangeTransform::transform(
   if (InnerLoopPreHeader != OuterLoopHeader) {
     // Eliminate PHIs in the inner-loop preheader.
     for (PHINode &P : make_early_inc_range(InnerLoopPreHeader->phis())) {
-      assert(P.getNumIncomingValues() == 1 &&
-             "Expected single-incoming PHIs in inner loop preheader");
+      assert(all_equal(P.incoming_values()) &&
+             "Expected equivalent incoming values in inner loop preheader");
       P.replaceAllUsesWith(P.getIncomingValue(0));
       P.eraseFromParent();
     }
diff --git a/llvm/test/Transforms/LoopInterchange/inner-preheader-multi-entry-phi.ll b/llvm/test/Transforms/LoopInterchange/inner-preheader-multi-entry-phi.ll
new file mode 100644
index 0000000000000..7d88d48bdee3a
--- /dev/null
+++ b/llvm/test/Transforms/LoopInterchange/inner-preheader-multi-entry-phi.ll
@@ -0,0 +1,72 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=loop-interchange -loop-interchange-profitabilities=ignore \
+; RUN:   -verify-dom-info -verify-loop-info -verify-loop-lcssa -S %s \
+; RUN:   | FileCheck %s
+
+; The outer header branches to the inner preheader via duplicate edges, giving
+; the preheader a PHI with two identical incoming entries. Interchange must
+; succeed: both entries carry the same value so the PHI can be safely removed.
+
+define void @multi_entry_phi(ptr noalias %A, i1 %c) {
+; CHECK-LABEL: define void @multi_entry_phi(
+; CHECK-SAME: ptr noalias [[A:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    br label %[[INNER_PH:.*]]
+; 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 i1 [[C]], label %[[INNER_SPLIT1:.*]], label %[[INNER_SPLIT1]]
+; CHECK:       [[INNER_PH]]:
+; CHECK-NEXT:    br label %[[INNER:.*]]
+; CHECK:       [[INNER]]:
+; CHECK-NEXT:    [[J:%.*]] = phi i64 [ 0, %[[INNER_PH]] ], [ [[TMP0:%.*]], %[[INNER_SPLIT:.*]] ]
+; CHECK-NEXT:    br label %[[OUTER_HEADER_PREHEADER]]
+; CHECK:       [[INNER_SPLIT1]]:
+; CHECK-NEXT:    [[IDX:%.*]] = mul i64 [[J]], 10
+; CHECK-NEXT:    [[OFF:%.*]] = add i64 [[IDX]], [[I]]
+; CHECK-NEXT:    [[GEP:%.*]] = getelementptr inbounds i32, ptr [[A]], i64 [[OFF]]
+; CHECK-NEXT:    store i32 0, 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 i1 %c, label %inner.ph, label %inner.ph
+
+inner.ph:
+  %p = phi i64 [ 42, %outer.header ], [ 42, %outer.header ]
+  br label %inner
+
+inner:
+  %j = phi i64 [ 0, %inner.ph ], [ %j.inc, %inner ]
+  %idx = mul i64 %j, 10
+  %off = add i64 %idx, %i
+  %gep = getelementptr inbounds i32, ptr %A, i64 %off
+  store i32 0, ptr %gep, align 4
+  %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
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/203842


More information about the llvm-commits mailing list