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

via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 21 22:21:17 PDT 2026


Author: Dark Steve
Date: 2026-09-22T10:51:12+05:30
New Revision: 5587ab44938b396a01268f118fad8b32a59104a3

URL: https://github.com/llvm/llvm-project/commit/5587ab44938b396a01268f118fad8b32a59104a3
DIFF: https://github.com/llvm/llvm-project/commit/5587ab44938b396a01268f118fad8b32a59104a3.diff

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

Trivial unswitch bails on a common loop shape: a loop-invariant branch
in the header that exits the loop returning a loop-carried value.

```
  int acc = 0;
  for (...) {
    if (c)
      return acc;
    acc++;
  }
```

After loop-rotate the `if` sits in the header, and the exit reads the
header PHI `%acc`. Trivial unswitch hoists the branch into the
preheader, so the exit value must be available there. It only accepts
loop-invariant values, and `%acc` is not one, so it gives up. But `%acc`
is safe here: the branch is invariant, so if the loop exits it exits on
the first iteration, before the latch runs. On that edge `%acc` still
holds its entry value, 0.

This is not specific to the header. The same holds for any invariant
branch whose block dominates the latch, so every header PHI still holds
its entry value.

Allow the unswitch in these cases. When an exit's LCSSA incoming is a
header PHI (and the branch's block dominates the latch), replace it with
the value the PHI takes from the preheader (its entry value), which
keeps SSA valid.

Added: 
    llvm/test/Transforms/SimpleLoopUnswitch/trivial-unswitch-header-phi.ll

Modified: 
    llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
    llvm/test/Transforms/SimpleLoopUnswitch/nontrivial-unswitch-trivial-select.ll
    llvm/test/Transforms/SimpleLoopUnswitch/pr138509.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp b/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
index 045c259f0d63b..310ef7c4f3548 100644
--- a/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
+++ b/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
@@ -256,20 +256,38 @@ static void replaceLoopInvariantUses(const Loop &L, Value *Invariant,
   }
 }
 
-/// Check that all the LCSSA PHI nodes in the loop exit block have trivial
-/// incoming values along this edge.
-static bool areLoopExitPHIsLoopInvariant(const Loop &L,
-                                         const BasicBlock &ExitingBB,
-                                         const BasicBlock &ExitBB) {
+/// Return true if \p V is a PHI node in the header of \p L.
+static bool isLoopHeaderPHI(const Loop &L, const Value *V) {
+  const auto *PN = dyn_cast<PHINode>(V);
+  return PN && PN->getParent() == L.getHeader();
+}
+
+/// Return the value \p V holds on entry to \p L. For a header PHI that is its
+/// incoming value from the preheader; any other value is returned unchanged.
+static Value *getLoopEntryValue(const Loop &L, Value *V) {
+  if (!isLoopHeaderPHI(L, V))
+    return V;
+  return cast<PHINode>(V)->getIncomingValueForBlock(L.getLoopPreheader());
+}
+
+/// Check that all the LCSSA PHI nodes in \p ExitBB have trivial incoming values
+/// along the edge from \p ExitingBB, i.e. values that are still correct if the
+/// loop is not entered.
+///
+/// Only loop invariant values are trivial by default. If \p AllowHeaderPHIs is
+/// set, a PHI in the loop header counts as trivial too.
+static bool areLoopExitPHIsTrivial(const Loop &L, const BasicBlock &ExitingBB,
+                                   const BasicBlock &ExitBB,
+                                   bool AllowHeaderPHIs = false) {
   for (const Instruction &I : ExitBB) {
     auto *PN = dyn_cast<PHINode>(&I);
     if (!PN)
       // No more PHIs to check.
       return true;
 
-    // If the incoming value for this edge isn't loop invariant the unswitch
-    // won't be trivial.
-    if (!L.isLoopInvariant(PN->getIncomingValueForBlock(&ExitingBB)))
+    const Value *Incoming = PN->getIncomingValueForBlock(&ExitingBB);
+    if (!L.isLoopInvariant(Incoming) &&
+        !(AllowHeaderPHIs && isLoopHeaderPHI(L, Incoming)))
       return false;
   }
   llvm_unreachable("Basic blocks should never be empty!");
@@ -396,7 +414,8 @@ static void buildPartialInvariantUnswitchConditionalBranch(
 /// PHI nodes in that block such that what were LCSSA PHI nodes become trivial
 /// PHI nodes from the old preheader that now contains the unswitched
 /// terminator.
-static void rewritePHINodesForUnswitchedExitBlock(BasicBlock &UnswitchedBB,
+static void rewritePHINodesForUnswitchedExitBlock(const Loop &L,
+                                                  BasicBlock &UnswitchedBB,
                                                   BasicBlock &OldExitingBB,
                                                   BasicBlock &OldPH) {
   for (PHINode &PN : UnswitchedBB.phis()) {
@@ -407,6 +426,7 @@ static void rewritePHINodesForUnswitchedExitBlock(BasicBlock &UnswitchedBB,
       assert(PN.getIncomingBlock(i) == &OldExitingBB &&
              "Found incoming block 
diff erent from unique predecessor!");
       PN.setIncomingBlock(i, &OldPH);
+      PN.setIncomingValue(i, getLoopEntryValue(L, PN.getIncomingValue(i)));
     }
   }
 }
@@ -418,11 +438,9 @@ static void rewritePHINodesForUnswitchedExitBlock(BasicBlock &UnswitchedBB,
 /// LCSSA PHI nodes in it to remove the unswitched edge and introduces PHI
 /// nodes into the unswitched basic block to select between the value in the
 /// old preheader and the loop exit.
-static void rewritePHINodesForExitAndUnswitchedBlocks(BasicBlock &ExitBB,
-                                                      BasicBlock &UnswitchedBB,
-                                                      BasicBlock &OldExitingBB,
-                                                      BasicBlock &OldPH,
-                                                      bool FullUnswitch) {
+static void rewritePHINodesForExitAndUnswitchedBlocks(
+    const Loop &L, BasicBlock &ExitBB, BasicBlock &UnswitchedBB,
+    BasicBlock &OldExitingBB, BasicBlock &OldPH, bool FullUnswitch) {
   assert(&ExitBB != &UnswitchedBB &&
          "Must have 
diff erent loop exit and unswitched blocks!");
   BasicBlock::iterator InsertPt = UnswitchedBB.begin();
@@ -449,7 +467,7 @@ static void rewritePHINodesForExitAndUnswitchedBlocks(BasicBlock &ExitBB,
         // No more edge from the old exiting block to the exit block.
         PN.removeIncomingValue(i);
 
-      NewPN->addIncoming(Incoming, &OldPH);
+      NewPN->addIncoming(getLoopEntryValue(L, Incoming), &OldPH);
     }
 
     // Now replace the old PHI with the new one and wire the old one in as an
@@ -598,7 +616,7 @@ static bool unswitchTrivialBranch(Loop &L, CondBrInst &BI, DominatorTree &DT,
   // Redirecting the latch edge to the exit block will cause us to skip latch
   // instructions. This can only be done if the latch instructions don't have
   // side effects and don't have any convergent instructions.
-  if (LatchIdx && areLoopExitPHIsLoopInvariant(L, *LoopLatch, *ULExit) &&
+  if (LatchIdx && areLoopExitPHIsTrivial(L, *LoopLatch, *ULExit) &&
       !llvm::any_of(*LoopLatch, [](Instruction &I) {
         if (const auto *CB = dyn_cast<CallBase>(&I))
           if (CB->isConvergent())
@@ -647,8 +665,12 @@ 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.
+  if (!ModifiedBranch && !areLoopExitPHIsTrivial(L, *ParentBB, *LoopExitBB,
+                                                 /*AllowHeaderPHIs=*/true)) {
     LLVM_DEBUG(dbgs() << "   Loop exit PHI's aren't loop-invariant!\n");
     return false;
   }
@@ -785,9 +807,9 @@ static bool unswitchTrivialBranch(Loop &L, CondBrInst &BI, DominatorTree &DT,
 
   // Rewrite the relevant PHI nodes.
   if (UnswitchedBB == LoopExitBB)
-    rewritePHINodesForUnswitchedExitBlock(*UnswitchedBB, *ParentBB, *OldPH);
+    rewritePHINodesForUnswitchedExitBlock(L, *UnswitchedBB, *ParentBB, *OldPH);
   else
-    rewritePHINodesForExitAndUnswitchedBlocks(*LoopExitBB, *UnswitchedBB,
+    rewritePHINodesForExitAndUnswitchedBlocks(L, *LoopExitBB, *UnswitchedBB,
                                               *ParentBB, *OldPH, FullUnswitch);
 
   // The constant we can replace all of our invariants with inside the loop
@@ -864,7 +886,7 @@ static bool unswitchTrivialSwitch(Loop &L, SwitchInst &SI, DominatorTree &DT,
     if (L.contains(&BBToCheck))
       return false;
     // BBToCheck is not trivial to unswitch if its phis aren't loop invariant.
-    if (!areLoopExitPHIsLoopInvariant(L, *ParentBB, BBToCheck))
+    if (!areLoopExitPHIsTrivial(L, *ParentBB, BBToCheck))
       return false;
     // We do not unswitch a block that only has an unreachable statement, as
     // it's possible this is a previously unswitched block. Only unswitch if
@@ -986,11 +1008,12 @@ static bool unswitchTrivialSwitch(Loop &L, SwitchInst &SI, DominatorTree &DT,
   if (DefaultExitBB) {
     if (pred_empty(DefaultExitBB)) {
       UnswitchedExitBBs.insert(DefaultExitBB);
-      rewritePHINodesForUnswitchedExitBlock(*DefaultExitBB, *ParentBB, *OldPH);
+      rewritePHINodesForUnswitchedExitBlock(L, *DefaultExitBB, *ParentBB,
+                                            *OldPH);
     } else {
       auto *SplitBB =
           SplitBlock(DefaultExitBB, DefaultExitBB->begin(), &DT, &LI, MSSAU);
-      rewritePHINodesForExitAndUnswitchedBlocks(*DefaultExitBB, *SplitBB,
+      rewritePHINodesForExitAndUnswitchedBlocks(L, *DefaultExitBB, *SplitBB,
                                                 *ParentBB, *OldPH,
                                                 /*FullUnswitch*/ true);
       DefaultExitBB = SplitExitBBMap[DefaultExitBB] = SplitBB;
@@ -1007,7 +1030,7 @@ static bool unswitchTrivialSwitch(Loop &L, SwitchInst &SI, DominatorTree &DT,
     if (pred_empty(ExitBB)) {
       // Only rewrite once.
       if (UnswitchedExitBBs.insert(ExitBB).second)
-        rewritePHINodesForUnswitchedExitBlock(*ExitBB, *ParentBB, *OldPH);
+        rewritePHINodesForUnswitchedExitBlock(L, *ExitBB, *ParentBB, *OldPH);
       continue;
     }
 
@@ -1017,7 +1040,7 @@ static bool unswitchTrivialSwitch(Loop &L, SwitchInst &SI, DominatorTree &DT,
     if (!SplitExitBB) {
       // If this is the first time we see this, do the split and remember it.
       SplitExitBB = SplitBlock(ExitBB, ExitBB->begin(), &DT, &LI, MSSAU);
-      rewritePHINodesForExitAndUnswitchedBlocks(*ExitBB, *SplitExitBB,
+      rewritePHINodesForExitAndUnswitchedBlocks(L, *ExitBB, *SplitExitBB,
                                                 *ParentBB, *OldPH,
                                                 /*FullUnswitch*/ true);
     }

diff  --git a/llvm/test/Transforms/SimpleLoopUnswitch/nontrivial-unswitch-trivial-select.ll b/llvm/test/Transforms/SimpleLoopUnswitch/nontrivial-unswitch-trivial-select.ll
index 5280aa7d3e284..16871d0379c87 100644
--- a/llvm/test/Transforms/SimpleLoopUnswitch/nontrivial-unswitch-trivial-select.ll
+++ b/llvm/test/Transforms/SimpleLoopUnswitch/nontrivial-unswitch-trivial-select.ll
@@ -89,19 +89,14 @@ define i32 @unswitch_trivial_select_cmp_outside(i32 %x) {
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[X:%.*]], 100
 ; CHECK-NEXT:    br i1 [[C]], label [[ENTRY_SPLIT_US:%.*]], label [[ENTRY_SPLIT:%.*]]
-; CHECK:       entry.split.us:
-; CHECK-NEXT:    br label [[LOOP_US:%.*]]
-; CHECK:       loop.us:
-; CHECK-NEXT:    [[P_US:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT_US]] ], [ 35, [[LOOP_US]] ]
-; CHECK-NEXT:    br label [[LOOP_US]]
 ; CHECK:       entry.split:
 ; CHECK-NEXT:    br label [[LOOP:%.*]]
 ; CHECK:       loop:
-; CHECK-NEXT:    [[P:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT]] ]
-; CHECK-NEXT:    [[SPEC_SELECT:%.*]] = select i1 false, i1 true, i1 false
-; CHECK-NEXT:    br label [[EXIT:%.*]]
+; CHECK-NEXT:    [[P:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT_US]] ], [ 35, [[LOOP]] ]
+; CHECK-NEXT:    [[SPEC_SELECT:%.*]] = select i1 true, i1 true, i1 false
+; CHECK-NEXT:    br label [[LOOP]]
 ; CHECK:       exit:
-; CHECK-NEXT:    [[LCSSA:%.*]] = phi i32 [ [[P]], [[LOOP]] ]
+; CHECK-NEXT:    [[LCSSA:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ]
 ; CHECK-NEXT:    ret i32 [[LCSSA]]
 ;
 entry:

diff  --git a/llvm/test/Transforms/SimpleLoopUnswitch/pr138509.ll b/llvm/test/Transforms/SimpleLoopUnswitch/pr138509.ll
index 2ce0cb5c3f299..a0c5bc7a6531f 100644
--- a/llvm/test/Transforms/SimpleLoopUnswitch/pr138509.ll
+++ b/llvm/test/Transforms/SimpleLoopUnswitch/pr138509.ll
@@ -8,7 +8,7 @@
 ; RUN:     -disable-output 2>&1 | sort -b -k 1 | FileCheck %s --check-prefixes=LOOP-DIV-2
 
 ; LOOP-DIV-1-COUNT-6: Loop at depth 1 containing:
-; LOOP-DIV-2-COUNT-12: Loop at depth 1 containing:
+; LOOP-DIV-2-COUNT-11: Loop at depth 1 containing:
 
 @a = global i32 0, align 4
 @b = global i32 0, align 4

diff  --git a/llvm/test/Transforms/SimpleLoopUnswitch/trivial-unswitch-header-phi.ll b/llvm/test/Transforms/SimpleLoopUnswitch/trivial-unswitch-header-phi.ll
new file mode 100644
index 0000000000000..8af526198b6c7
--- /dev/null
+++ b/llvm/test/Transforms/SimpleLoopUnswitch/trivial-unswitch-header-phi.ll
@@ -0,0 +1,272 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -passes='loop(simple-loop-unswitch),verify<loops>' -S < %s | FileCheck %s
+; RUN: opt -verify-memoryssa -passes='loop-mssa(simple-loop-unswitch),verify<loops>' -S < %s | FileCheck %s
+
+; Trivially unswitchable header branch whose exit LCSSA phi uses a header phi.
+define i32 @test_unswitch_header_phi(i1 %c) {
+; CHECK-LABEL: @test_unswitch_header_phi(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br i1 [[C:%.*]], label [[ENTRY_SPLIT:%.*]], label [[EXIT:%.*]]
+; CHECK:       entry.split:
+; CHECK-NEXT:    br label [[HEADER:%.*]]
+; CHECK:       header:
+; CHECK-NEXT:    [[ACC:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT]] ], [ [[NEXT:%.*]], [[LATCH:%.*]] ]
+; CHECK-NEXT:    br label [[LATCH]]
+; CHECK:       latch:
+; CHECK-NEXT:    [[NEXT]] = add i32 [[ACC]], 1
+; CHECK-NEXT:    br label [[HEADER]]
+; CHECK:       exit:
+; CHECK-NEXT:    [[R:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+entry:
+  br label %header
+
+header:
+  %acc = phi i32 [ 0, %entry ], [ %next, %latch ]
+  br i1 %c, label %latch, label %exit
+
+latch:
+  %next = add i32 %acc, 1
+  br label %header
+
+exit:
+  %r = phi i32 [ %acc, %header ]
+  ret i32 %r
+}
+
+; Header branch whose exit LCSSA phi uses a non-phi header value: not trivially
+; unswitchable, so the loop is left unchanged.
+define i32 @test_no_unswitch_header_nonphi(i1 %c) {
+; CHECK-LABEL: @test_no_unswitch_header_nonphi(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br label [[HEADER:%.*]]
+; CHECK:       header:
+; CHECK-NEXT:    [[ACC:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[NEXT:%.*]], [[LATCH:%.*]] ]
+; CHECK-NEXT:    [[SUM:%.*]] = add i32 [[ACC]], 5
+; CHECK-NEXT:    br i1 [[C:%.*]], label [[LATCH]], label [[EXIT:%.*]]
+; CHECK:       latch:
+; CHECK-NEXT:    [[NEXT]] = add i32 [[ACC]], 1
+; CHECK-NEXT:    br label [[HEADER]]
+; CHECK:       exit:
+; CHECK-NEXT:    [[R:%.*]] = phi i32 [ [[SUM]], [[HEADER]] ]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+entry:
+  br label %header
+
+header:
+  %acc = phi i32 [ 0, %entry ], [ %next, %latch ]
+  %sum = add i32 %acc, 5
+  br i1 %c, label %latch, label %exit
+
+latch:
+  %next = add i32 %acc, 1
+  br label %header
+
+exit:
+  %r = phi i32 [ %sum, %header ]
+  ret i32 %r
+}
+
+; Trivially unswitchable header branch whose exit has two LCSSA phis: one with a
+; loop-invariant incoming, one with a header phi incoming.
+define i32 @test_unswitch_header_phi_mixed(i1 %c, i32 %inv) {
+; CHECK-LABEL: @test_unswitch_header_phi_mixed(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br i1 [[C:%.*]], label [[ENTRY_SPLIT:%.*]], label [[EXIT:%.*]]
+; CHECK:       entry.split:
+; CHECK-NEXT:    br label [[HEADER:%.*]]
+; CHECK:       header:
+; CHECK-NEXT:    [[ACC:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT]] ], [ [[NEXT:%.*]], [[LATCH:%.*]] ]
+; CHECK-NEXT:    br label [[LATCH]]
+; CHECK:       latch:
+; CHECK-NEXT:    [[NEXT]] = add i32 [[ACC]], 1
+; CHECK-NEXT:    br label [[HEADER]]
+; CHECK:       exit:
+; CHECK-NEXT:    [[R:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ]
+; CHECK-NEXT:    [[S:%.*]] = phi i32 [ [[INV:%.*]], [[ENTRY]] ]
+; CHECK-NEXT:    [[SUM:%.*]] = add i32 [[R]], [[S]]
+; CHECK-NEXT:    ret i32 [[SUM]]
+;
+entry:
+  br label %header
+
+header:
+  %acc = phi i32 [ 0, %entry ], [ %next, %latch ]
+  br i1 %c, label %latch, label %exit
+
+latch:
+  %next = add i32 %acc, 1
+  br label %header
+
+exit:
+  %r = phi i32 [ %acc, %header ]
+  %s = phi i32 [ %inv, %header ]
+  %sum = add i32 %r, %s
+  ret i32 %sum
+}
+
+; Trivially unswitchable header branch whose exit LCSSA phi uses a header phi
+; whose entry value is defined in the preheader, not a constant.
+define i32 @test_unswitch_header_phi_nonconst(i1 %c, i32 %x) {
+; CHECK-LABEL: @test_unswitch_header_phi_nonconst(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[INIT:%.*]] = add i32 [[X:%.*]], 3
+; CHECK-NEXT:    br i1 [[C:%.*]], label [[ENTRY_SPLIT:%.*]], label [[EXIT:%.*]]
+; CHECK:       entry.split:
+; CHECK-NEXT:    br label [[HEADER:%.*]]
+; CHECK:       header:
+; CHECK-NEXT:    [[ACC:%.*]] = phi i32 [ [[INIT]], [[ENTRY_SPLIT]] ], [ [[NEXT:%.*]], [[LATCH:%.*]] ]
+; CHECK-NEXT:    br label [[LATCH]]
+; CHECK:       latch:
+; CHECK-NEXT:    [[NEXT]] = add i32 [[ACC]], 1
+; CHECK-NEXT:    br label [[HEADER]]
+; CHECK:       exit:
+; CHECK-NEXT:    [[R:%.*]] = phi i32 [ [[INIT]], [[ENTRY:%.*]] ]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+entry:
+  %init = add i32 %x, 3
+  br label %header
+
+header:
+  %acc = phi i32 [ %init, %entry ], [ %next, %latch ]
+  br i1 %c, label %latch, label %exit
+
+latch:
+  %next = add i32 %acc, 1
+  br label %header
+
+exit:
+  %r = phi i32 [ %acc, %header ]
+  ret i32 %r
+}
+
+; Trivially unswitchable branch that is not in the header but dominates the
+; latch, whose exit LCSSA phi uses a header phi. The header branch unswitches
+; first, then %mid is reached and also unswitches.
+define i32 @test_unswitch_nonheader_phi(i1 %c1, i1 %c2) {
+; CHECK-LABEL: @test_unswitch_nonheader_phi(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br i1 [[C1:%.*]], label [[EXIT1:%.*]], label [[ENTRY_SPLIT:%.*]]
+; CHECK:       entry.split:
+; CHECK-NEXT:    br i1 [[C2:%.*]], label [[EXIT2:%.*]], label [[ENTRY_SPLIT_SPLIT:%.*]]
+; CHECK:       entry.split.split:
+; CHECK-NEXT:    br label [[HEADER:%.*]]
+; CHECK:       header:
+; CHECK-NEXT:    [[ACC:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT_SPLIT]] ], [ [[NEXT:%.*]], [[LATCH:%.*]] ]
+; CHECK-NEXT:    br label [[MID:%.*]]
+; CHECK:       mid:
+; CHECK-NEXT:    br label [[LATCH]]
+; CHECK:       latch:
+; CHECK-NEXT:    [[NEXT]] = add i32 [[ACC]], 1
+; CHECK-NEXT:    br label [[HEADER]]
+; CHECK:       exit1:
+; CHECK-NEXT:    ret i32 100
+; CHECK:       exit2:
+; CHECK-NEXT:    [[R:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT]] ]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+entry:
+  br label %header
+
+header:
+  %acc = phi i32 [ 0, %entry ], [ %next, %latch ]
+  br i1 %c1, label %exit1, label %mid
+
+mid:
+  br i1 %c2, label %exit2, label %latch
+
+latch:
+  %next = add i32 %acc, 1
+  br label %header
+
+exit1:
+  ret i32 100
+
+exit2:
+  %r = phi i32 [ %acc, %mid ]
+  ret i32 %r
+}
+
+; Trivially unswitchable header branch whose exit LCSSA reads header phi %acc.
+; The exit is also reached from the latch, so it is split on unswitch.
+define i32 @test_unswitch_header_phi_split_exit(i1 %c, i32 %n) {
+; CHECK-LABEL: @test_unswitch_header_phi_split_exit(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br i1 [[C:%.*]], label [[EXIT_SPLIT:%.*]], label [[ENTRY_SPLIT:%.*]]
+; CHECK:       entry.split:
+; CHECK-NEXT:    br label [[HEADER:%.*]]
+; CHECK:       header:
+; CHECK-NEXT:    [[ACC:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT]] ], [ [[NEXT:%.*]], [[LATCH:%.*]] ]
+; CHECK-NEXT:    br label [[LATCH]]
+; CHECK:       latch:
+; CHECK-NEXT:    [[NEXT]] = add i32 [[ACC]], 1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[NEXT]], [[N:%.*]]
+; CHECK-NEXT:    br i1 [[CMP]], label [[HEADER]], label [[EXIT:%.*]]
+; CHECK:       exit:
+; CHECK-NEXT:    [[R:%.*]] = phi i32 [ [[NEXT]], [[LATCH]] ]
+; CHECK-NEXT:    br label [[EXIT_SPLIT]]
+; CHECK:       exit.split:
+; CHECK-NEXT:    [[R_SPLIT:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[R]], [[EXIT]] ]
+; CHECK-NEXT:    ret i32 [[R_SPLIT]]
+;
+entry:
+  br label %header
+
+header:
+  %acc = phi i32 [ 0, %entry ], [ %next, %latch ]
+  br i1 %c, label %exit, label %latch
+
+latch:
+  %next = add i32 %acc, 1
+  %cmp = icmp slt i32 %next, %n
+  br i1 %cmp, label %header, label %exit
+
+exit:
+  %r = phi i32 [ %acc, %header ], [ %next, %latch ]
+  ret i32 %r
+}
+
+; Trivially unswitchable header branch whose exit LCSSA reads header phi %acc.
+; The condition is an OR of invariant %a and variant %b, so only %a is peeled.
+define i32 @test_unswitch_header_phi_partial(i1 %a) {
+; CHECK-LABEL: @test_unswitch_header_phi_partial(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A_FR:%.*]] = freeze i1 [[A:%.*]]
+; CHECK-NEXT:    br i1 [[A_FR]], label [[EXIT_SPLIT:%.*]], label [[ENTRY_SPLIT:%.*]]
+; CHECK:       entry.split:
+; CHECK-NEXT:    br label [[HEADER:%.*]]
+; CHECK:       header:
+; CHECK-NEXT:    [[ACC:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT]] ], [ [[NEXT:%.*]], [[LATCH:%.*]] ]
+; CHECK-NEXT:    [[B:%.*]] = icmp eq i32 [[ACC]], 5
+; CHECK-NEXT:    [[OR:%.*]] = or i1 false, [[B]]
+; CHECK-NEXT:    br i1 [[OR]], label [[EXIT:%.*]], label [[LATCH]]
+; CHECK:       latch:
+; CHECK-NEXT:    [[NEXT]] = add i32 [[ACC]], 1
+; CHECK-NEXT:    br label [[HEADER]]
+; CHECK:       exit:
+; CHECK-NEXT:    [[R:%.*]] = phi i32 [ [[ACC]], [[HEADER]] ]
+; CHECK-NEXT:    br label [[EXIT_SPLIT]]
+; CHECK:       exit.split:
+; CHECK-NEXT:    [[R_SPLIT:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[R]], [[EXIT]] ]
+; CHECK-NEXT:    ret i32 [[R_SPLIT]]
+;
+entry:
+  br label %header
+
+header:
+  %acc = phi i32 [ 0, %entry ], [ %next, %latch ]
+  %b = icmp eq i32 %acc, 5
+  %or = or i1 %a, %b
+  br i1 %or, label %exit, label %latch
+
+latch:
+  %next = add i32 %acc, 1
+  br label %header
+
+exit:
+  %r = phi i32 [ %acc, %header ]
+  ret i32 %r
+}


        


More information about the llvm-commits mailing list