[llvm] Form LCSSA in Loop Versioning Pass before extending exit PHIs (PR #202296)

Amit Tiwari via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 8 02:05:20 PDT 2026


https://github.com/loopacino created https://github.com/llvm/llvm-project/pull/202296

LoopVersioning utility `versionLoop()` expects the input form to be LCSSA. However, passes like Loop distribution, Loop-load-elimination (in this case, LLE) etc., may invoke versioning with a non-LCSSA IR. Versioning updates exit-block PHIs to handle both the original loop and the cloned loop. However, a raw non-LCSSA use in the exit block (such as `switch i1 %C`) still points to the original loop’s `%C`. On the cloned-loop path, control reaches the exit block without executing the original `%C`, resulting in error: `Instruction does not dominate all uses!`.

The fix is to form LCSSA at the start of `LoopVersioning::versionLoop()`.

>From 1eecf034b2a27d42c50a0e86953e935936566974 Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Mon, 25 May 2026 11:28:47 -0400
Subject: [PATCH 1/2] lcssa'ed_lle_loops

---
 llvm/lib/Transforms/Utils/LoopVersioning.cpp | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/LoopVersioning.cpp b/llvm/lib/Transforms/Utils/LoopVersioning.cpp
index b90466a8c49cf..61ef945bb8cbd 100644
--- a/llvm/lib/Transforms/Utils/LoopVersioning.cpp
+++ b/llvm/lib/Transforms/Utils/LoopVersioning.cpp
@@ -53,6 +53,10 @@ void LoopVersioning::versionLoop(
   assert(VersionedLoop->isLoopSimplifyForm() &&
          "Loop is not in loop-simplify form");
 
+  // Form LCSSA on the input loop before doing anything else.
+  if (!VersionedLoop->isLCSSAForm(*DT))
+    formLCSSARecursively(*VersionedLoop, *DT, LI, SE);
+
   Value *MemRuntimeCheck;
   Value *SCEVRuntimeCheck;
   Value *RuntimeCheck = nullptr;
@@ -292,9 +296,6 @@ bool runImpl(LoopInfo *LI, LoopAccessInfoManager &LAIs, DominatorTree *DT,
     if (!LAI.hasConvergentOp() &&
         (LAI.getNumRuntimePointerChecks() ||
          !LAI.getPSE().getPredicate().isAlwaysTrue())) {
-      if (!L->isLCSSAForm(*DT))
-       formLCSSARecursively(*L, *DT, LI, SE);
-
       LoopVersioning LVer(LAI, LAI.getRuntimePointerChecking()->getChecks(), L,
                           LI, DT, SE);
       LVer.versionLoop();

>From 478d0fbc4a02aabc8ca52197bd02ee1d3969d1b4 Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Mon, 8 Jun 2026 04:38:30 -0400
Subject: [PATCH 2/2] test_non-lcssa

---
 .../LoopLoadElim/non-lcssa-input.ll           | 40 +++++++++++++++++++
 1 file changed, 40 insertions(+)
 create mode 100644 llvm/test/Transforms/LoopLoadElim/non-lcssa-input.ll

diff --git a/llvm/test/Transforms/LoopLoadElim/non-lcssa-input.ll b/llvm/test/Transforms/LoopLoadElim/non-lcssa-input.ll
new file mode 100644
index 0000000000000..1e31c17c7f416
--- /dev/null
+++ b/llvm/test/Transforms/LoopLoadElim/non-lcssa-input.ll
@@ -0,0 +1,40 @@
+; RUN: opt -passes=loop-load-elim -S < %s | FileCheck %s
+
+; LoopLoadElimination could call LoopVersioning on non-LCSSA loops.
+; A direct exit-block use of %C then remained tied to the original loop,
+; which stopped dominating the shared exit after cloning. Forming LCSSA in
+; versionLoop() rewrites the branch through an exit PHI; Usage of raw %C and %C.exit is needed
+; to trigger the old buggy path.
+
+define void @non_lcssa_exit_use(ptr nocapture %a, i64 %n) {
+; CHECK-LABEL: @non_lcssa_exit_use(
+; CHECK:       for.body.lver.check:
+; CHECK:       for.end:
+; CHECK:         %C.lcssa = phi i1
+; CHECK:         br i1 %C.lcssa,
+; CHECK-NOT:     br i1 %C,
+entry:
+  %G = getelementptr i32, ptr %a, i64 -1
+  br label %for.body
+
+for.body:
+  %iv = phi i64 [ %iv.next, %for.body ], [ 0, %entry ]
+  %arrayidx = getelementptr inbounds i32, ptr %G, i64 %iv
+  %load = load i32, ptr %arrayidx, align 4
+  %mul = mul i32 %load, 3
+  %arrayidx2 = getelementptr inbounds i32, ptr %a, i64 %iv
+  store i32 %mul, ptr %arrayidx2, align 4
+  %iv.next = add i64 %iv, 1
+  %C = icmp sgt i64 %iv.next, %n
+  br i1 %C, label %for.end, label %for.body
+
+for.end:
+  %C.exit = phi i1 [ %C, %for.body ]
+  br i1 %C, label %exit.a, label %exit.b
+
+exit.a:
+  ret void
+
+exit.b:
+  ret void
+}



More information about the llvm-commits mailing list