[llvm] [LLE][LoopVersioning] Form LCSSA in Loop Versioning Pass before extending exit PHIs (PR #202296)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 8 02:06:02 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Amit Tiwari (loopacino)
<details>
<summary>Changes</summary>
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()`.
---
Full diff: https://github.com/llvm/llvm-project/pull/202296.diff
2 Files Affected:
- (modified) llvm/lib/Transforms/Utils/LoopVersioning.cpp (+4-3)
- (added) llvm/test/Transforms/LoopLoadElim/non-lcssa-input.ll (+40)
``````````diff
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();
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
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/202296
More information about the llvm-commits
mailing list