Bug type: crash
Component: LoopInterchange
Bug location: llvm/lib/Transforms/Scalar/LoopInterchange.cpp: LoopInterchangeTransform::adjustLoopBranches
moveLCSSAPhis
Issue title: opt -passes=loop-interchange fails with "Instruction does not dominate all uses!"
Labels: loopoptim
crash
generated by fuzzer
Reported by: mikaelholmen

llvm commit: cca769ac3f1
Reproduce with:
```opt -passes=loop-interchange bbi-110771.ll -o /dev/null```
Result:
```
Instruction does not dominate all uses!
  %cond329.us.us.lcssa = phi i16 [ %cond329.us.us, %for.cond.cleanup295.us ]
  %.us-phi.us = phi i16 [ %cond329.us.us.lcssa, %for.cond.cleanup295.split.us.us ]
LLVM ERROR: Broken module found, compilation aborted!
```

[bbi-110771.ll.gz](https://github.com/user-attachments/files/22464426/bbi-110771.ll.gz)

Comments: author: nikic
body: cc @sjoerdmeijer 
author: sjoerdmeijer
body: Thanks for the CC. 
I will self-assign this, and fix this. 
author: sjoerdmeijer
body: @mikaelholmen : I've just picked this up, and was wondering if you happen to have the input source code? It's a bit of a weird case. There is a phi node with no uses in a latch block, and I was wondering how we ended up with this. I.e., I am not sure if interchange should be resilient to this (probably), but I am also wondering if something else has gone wrong earlier. 
author: mikaelholmen
body: @sjoerdmeijer Yes, I have the original (generated) C program. However, it's in a C dialect only supported by our downstream compiler so it's not something you could use anyway.
I'll see if I can reduce the C input program though and share something more that makes sense.

Note that we've seen this with a non-standard opt pipeline ```-passes='function-simplification<O2>,loop(simple-loop-unswitch<nontrivial>,loop-interchange)' ```, so it's possible that it's only seen when some standard passes are not run.
author: sjoerdmeijer
body: Ok, great, thanks, also for the context. I think it now makes sense now what I am seeing. I will now start looking into interchange. 

Just dumping this here if it helps and maybe for completeness, here's the test case with reshuffled blocks and renamed variables and labels because I couldn't see what was going on with all the long block names:

```
define i16 @main() {
entry:
  br label %BB1

BB1:                ; preds = %BB5, %entry
  %i = phi i64 [ 1, %entry ], [ %i.next, %BB5 ]
  br label %BB2

BB2:                                ; preds = %BB3, %BB1
  %j = phi i16 [ 0, %BB1 ], [ %j.next, %BB3 ]
  %arrayidx.us.us = getelementptr i16, ptr null, i16 %j
  %0 = load i16, ptr %arrayidx.us.us, align 1
  %cond329.us.us = select i1 false, i16 0, i16 0
  br label %BB3

BB3:                                ; preds = %BB2
  %j.next = add i16 %j, 1
  br i1 true, label %BB4, label %BB2

BB4:                  ; preds = %BB3
  br label %BB5

BB5:                           ; preds = %BB4
  %UNUSED = phi i16 [ %cond329.us.us, %BB4 ]
  %i.next = add i64 %i, 1
  %cmp286.us = icmp ugt i64 %i, 0
  br i1 %cmp286.us, label %BB1, label %exit

exit:                     ; preds = %BB5
  ret i16 0

; uselistorder directives
  uselistorder i64 %i, { 1, 0 }
}
```
author: mikaelholmen
body: Ok, here is something. I don't know if this helps in any way but I'll attach some files.

Reduced C program containing two nested loops: r.c

Clang output (run for my target, then slightly cleaned): r.ll

Then I ran ```opt -passes="function-simplification<O2>" r.ll -S -o r2.ll``` for my target producing: r2.ll
(If I don't specify my target here something gets different in the output and it won't expose the crash in the next step.)

Now you can reproduce the crash with:
 ```opt -passes="loop(simple-loop-unswitch<nontrivial>,loop-interchange)" r2.ll -S -o r3.ll```

[r2.ll.gz](https://github.com/user-attachments/files/22489655/r2.ll.gz)
[r.ll.gz](https://github.com/user-attachments/files/22489653/r.ll.gz)
[r.c.gz](https://github.com/user-attachments/files/22489654/r.c.gz)
author: sjoerdmeijer
body: Thanks a lot, going to take a look!

Fix patch:
commit b90a8d385e0d35a2e870aa7b4cf16ee37df10851
Author: Sjoerd Meijer <smeijer@nvidia.com>
Date:   Mon Oct 20 10:23:36 2025 +0100

    [LoopInterchange] Add simplifyLCSSAPhis: remove phi from non-exit bb  (#160889)
    
    This deals with a corner case of LCSSA phi nodes in the outer loop latch
    block: the loop was in LCSSA form, some transformations can come along
    (e.g. unswitch) and create an empty block:
    
         BB4:
           br label %BB5
         BB5:
           %old.cond.lcssa = phi i16 [ %cond, %BB4 ]
           br outer.header
    
    Interchange then brings it in LCSSA form again and we get:
    
         BB4:
           %new.cond.lcssa = phi i16 [ %cond, %BB3 ]
           br label %BB5
         BB5:
           %old.cond.lcssa = phi i16 [ %new.cond.lcssa, %BB4 ]
    
    Which means that we have a chain of LCSSA phi nodes from %new.cond.lcssa
    to %old.cond.lcssa. The problem is that interchange can reoder blocks
    BB4 and BB5 placing the use before the def if we don't check this. The
    solution is to simplify lcssa phis, and remove them from non-exit blocks
    if they are 1-input phi nodes.
    
    Fixes #160068

diff --git a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
index 28ae4f0a0aad..9aaf6a5aa4d6 100644
--- a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
@@ -43,6 +43,7 @@
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Transforms/Scalar/LoopPassManager.h"
 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
+#include "llvm/Transforms/Utils/Local.h"
 #include "llvm/Transforms/Utils/LoopUtils.h"
 #include <cassert>
 #include <utility>
@@ -1872,6 +1873,51 @@ static void moveLCSSAPhis(BasicBlock *InnerExit, BasicBlock *InnerHeader,
   InnerLatch->replacePhiUsesWith(InnerLatch, OuterLatch);
 }
 
+/// This deals with a corner case when a LCSSA phi node appears in a non-exit
+/// block: the outer loop latch block does not need to be exit block of the
+/// inner loop. Consider a loop that was in LCSSA form, but then some
+/// transformation like loop-unswitch comes along and creates an empty block,
+/// where BB5 in this example is the outer loop latch block:
+///
+///   BB4:
+///     br label %BB5
+///   BB5:
+///     %old.cond.lcssa = phi i16 [ %cond, %BB4 ]
+///     br outer.header
+///
+/// Interchange then brings it in LCSSA form again resulting in this chain of
+/// single-input phi nodes:
+///
+///   BB4:
+///     %new.cond.lcssa = phi i16 [ %cond, %BB3 ]
+///     br label %BB5
+///   BB5:
+///     %old.cond.lcssa = phi i16 [ %new.cond.lcssa, %BB4 ]
+///
+/// The problem is that interchange can reoder blocks BB4 and BB5 placing the
+/// use before the def if we don't check this. The solution is to simplify
+/// lcssa phi nodes (remove) if they appear in non-exit blocks.
+///
+static void simplifyLCSSAPhis(Loop *OuterLoop, Loop *InnerLoop) {
+  BasicBlock *InnerLoopExit = InnerLoop->getExitBlock();
+  BasicBlock *OuterLoopLatch = OuterLoop->getLoopLatch();
+
+  // Do not modify lcssa phis where they actually belong, i.e. in exit blocks.
+  if (OuterLoopLatch == InnerLoopExit)
+    return;
+
+  // Collect and remove phis in non-exit blocks if they have 1 input.
+  SmallVector<PHINode *, 8> Phis(
+      llvm::make_pointer_range(OuterLoopLatch->phis()));
+  for (PHINode *Phi : Phis) {
+    assert(Phi->getNumIncomingValues() == 1 && "Single input phi expected");
+    LLVM_DEBUG(dbgs() << "Removing 1-input phi in non-exit block: " << *Phi
+                      << "\n");
+    Phi->replaceAllUsesWith(Phi->getIncomingValue(0));
+    Phi->eraseFromParent();
+  }
+}
+
 bool LoopInterchangeTransform::adjustLoopBranches() {
   LLVM_DEBUG(dbgs() << "adjustLoopBranches called\n");
   std::vector<DominatorTree::UpdateType> DTUpdates;
@@ -1882,6 +1928,9 @@ bool LoopInterchangeTransform::adjustLoopBranches() {
   assert(OuterLoopPreHeader != OuterLoop->getHeader() &&
          InnerLoopPreHeader != InnerLoop->getHeader() && OuterLoopPreHeader &&
          InnerLoopPreHeader && "Guaranteed by loop-simplify form");
+
+  simplifyLCSSAPhis(OuterLoop, InnerLoop);
+
   // Ensure that both preheaders do not contain PHI nodes and have single
   // predecessors. This allows us to move them easily. We use
   // InsertPreHeaderForLoop to create an 'extra' preheader, if the existing


Regression tests:
file: llvm/test/Transforms/LoopInterchange/lcssa-phi-outer-latch.ll
commands: opt < %s -passes=loop-interchange -cache-line-size=64 -verify-dom-info -verify-loop-info -verify-scev -verify-loop-lcssa -S
tests: test_name: <module>
test_body: 
; This test is checking that blocks outer.body and outer.latch, where outer.body is the exit
; block of the inner loop and outer.latch the latch of the outer loop, correctly
; deal with the phi-node use-def chain %new.cond.lcssa -> %old.cond.lcssa. What we expect
; here is that block outer.latch does not contain a phi node, because it is a single input
; phi in a non-exit block.

target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"

define i16 @main(ptr %a) {
;
entry:
  br label %outer.header

outer.header:
  %i = phi i64 [ 1, %entry ], [ %i.next, %outer.latch ]
  br label %inner.header

inner.header:
  %j = phi i16 [ 0, %outer.header ], [ %j.next, %inner.latch ]
  %arrayidx.us.us = getelementptr i16, ptr %a, i16 %j
  %0 = load i16, ptr %arrayidx.us.us, align 1
  %cond = select i1 false, i16 0, i16 0
  br label %inner.latch

inner.latch:
  %j.next = add i16 %j, 1
  br i1 true, label %outer.body, label %inner.header

outer.body:
  %new.cond.lcssa = phi i16 [ %cond, %inner.latch ]
  br label %outer.latch

outer.latch:
  %old.cond.lcssa = phi i16 [ %new.cond.lcssa, %outer.body ]
  %i.next = add i64 %i, 1
  %cmp286.us = icmp ugt i64 %i, 0
  br i1 %cmp286.us, label %outer.header, label %exit

exit:
  ret i16 %old.cond.lcssa
}