[llvm] [SimpleLoopUnswitch] Allow trivial unswitch when exit LCSSA is a header PHI (PR #222962)

Dark Steve via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 11 20:36:30 PDT 2026


================
@@ -647,8 +657,14 @@ static bool unswitchTrivialBranch(Loop &L, CondBrInst &BI, DominatorTree &DT,
   }
   auto *ContinueBB = BI.getSuccessor(1 - LoopExitSuccIdx);
   auto *ParentBB = BI.getParent();
-  if (!ModifiedBranch &&
-      !areLoopExitPHIsLoopInvariant(L, *ParentBB, *LoopExitBB)) {
+
+  // If the exit incomings aren't loop-invariant, the unswitch is still trivial
+  // when branch dominates the latch and every non-invariant incoming is a
+  // header PHI. Those incomings are repaired after unswitching.
+  // Branch always dominates the latch as guaranteed by the caller.
+  bool TrivialFromHeader = false;
+  if (!ModifiedBranch && !areLoopExitPHIsLoopInvariant(
----------------
PrasoonMishra wrote:

The utility is used in three places: two in `unswitchTrivialBranch` and one in `unswitchTrivialSwitch`. They aren't asking the same question, which is why I only relaxed one of them.

`areLoopExitPHIsLoopInvariant(L, *LoopLatch, *ULExit)` guard redirecting the branch's latch edge to the unique latch exit. The value that must appear on the bypass edge is the one at the last iteration, loop-invariance is exactly what makes that equal to the value at entry. Relaxing this to accept a header PHI would substitute the entry value and would be incorrect.

```
define i32 @g(i1 %c, i32 %n) {
entry:
  br label %header
header:
  %acc = phi i32 [ 0, %entry ], [ %accn, %latch ]
  %i   = phi i32 [ 0, %entry ], [ %in, %latch ]
  br i1 %c, label %latch, label %other
other:
  br label %latch
latch:
  %accn = add i32 %acc, 1
  %in   = add i32 %i, 1
  %cmp  = icmp slt i32 %in, %n
  br i1 %cmp, label %header, label %ulexit
ulexit:
  %r = phi i32 [ %acc, %latch ]
  ret i32 %r
}
```

With `%c = true`, `%n = 5` this returns `4`. Accepting `%acc` there and substituting its entry value returns `0`, because the loop now exits before %acc has been incremented. That would be wrong.

The call site I changed is the place where branch is hoisted into the preheader, so on the bypass edge the loop has not run and a header PHI still holds its entry value.

`unswitchTrivialSwitch` is the same situation as the one I changed, a switch version of the motivating loop which I did not extended. Happy to extend that as a follow-up or here,

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


More information about the llvm-commits mailing list