Bug type: crash
Component: SimplifyIndVar
Bug location: llvm/lib/Transforms/Utils/SimplifyIndVar.cpp: SimplifyIndvar::replaceIVUserWithLoopInvariant
Issue title: Assertion `L->isRecursivelyLCSSAForm(*DT, *LI) && "Requested to preserve LCSSA, but it's already broken."' failed.
Labels: loopoptim
crash
Reported by: TatyanaDoubts

Run opt with -passes=loop-unroll

https://godbolt.org/z/GcEW4dhMh

Test.ll
```
; ModuleID = './reduced.ll'
source_filename = "./reduced.ll"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128-ni:1-p2:32:8:8:32-ni:2"
target triple = "x86_64-unknown-linux-gnu"

define void @wombat() gc "statepoint-example" !prof !0 {
bb:
  br label %bb1

bb1:                                              ; preds = %bb12, %bb
  %phi = phi i32 [ 1, %bb ], [ 0, %bb12 ]
  br label %bb2

bb2:                                              ; preds = %bb11, %bb1
  %phi3 = phi i64 [ 0, %bb1 ], [ %sext, %bb11 ]
  br label %bb4

bb4:                                              ; preds = %bb9, %bb2
  %phi5 = phi i64 [ %phi3, %bb2 ], [ %sext, %bb9 ]
  %phi6 = phi i32 [ 1, %bb2 ], [ %add10, %bb9 ]
  %trunc = trunc i64 %phi5 to i32
  br i1 true, label %bb9, label %bb7

bb7:                                              ; preds = %bb4
  %phi8 = phi i32 [ %trunc, %bb4 ]
  ret void

bb9:                                              ; preds = %bb4
  %add = add i32 1, %phi
  %sext = sext i32 %add to i64
  %add10 = add i32 %phi6, 1
  %icmp = icmp ugt i32 %add10, 2
  br i1 %icmp, label %bb11, label %bb4

bb11:                                             ; preds = %bb9
  br i1 false, label %bb12, label %bb2

bb12:                                             ; preds = %bb12, %bb11
  br i1 false, label %bb1, label %bb12, !prof !1
}

!0 = !{!"function_entry_count", i64 32768}
!1 = !{!"branch_weights", i32 11, i32 1}
```
Stack dump:
```
0.	Program arguments: /opt/compiler-explorer/clang-assertions-trunk/bin/opt -o /app/output.s -S -S -passes=loop-unroll <source>
 #0 0x000000000359f928 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x359f928)
 #1 0x000000000359d07c SignalHandler(int) Signals.cpp:0:0
 #2 0x00007fe5f8e42520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #3 0x00007fe5f8e969fc pthread_kill (/lib/x86_64-linux-gnu/libc.so.6+0x969fc)
 #4 0x00007fe5f8e42476 gsignal (/lib/x86_64-linux-gnu/libc.so.6+0x42476)
 #5 0x00007fe5f8e287f3 abort (/lib/x86_64-linux-gnu/libc.so.6+0x287f3)
 #6 0x00007fe5f8e2871b (/lib/x86_64-linux-gnu/libc.so.6+0x2871b)
 #7 0x00007fe5f8e39e96 (/lib/x86_64-linux-gnu/libc.so.6+0x39e96)
 #8 0x00000000036a9544 llvm::simplifyLoop(llvm::Loop*, llvm::DominatorTree*, llvm::LoopInfo*, llvm::ScalarEvolution*, llvm::AssumptionCache*, llvm::MemorySSAUpdater*, bool) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x36a9544)
 #9 0x000000000369b9d6 llvm::peelLoop(llvm::Loop*, unsigned int, llvm::LoopInfo*, llvm::ScalarEvolution*, llvm::DominatorTree&, llvm::AssumptionCache*, bool, llvm::ValueMap<llvm::Value const*, llvm::WeakTrackingVH, llvm::ValueMapConfig<llvm::Value const*, llvm::sys::SmartMutex<false>>>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x369b9d6)
#10 0x00000000033f771b tryToUnrollLoop(llvm::Loop*, llvm::DominatorTree&, llvm::LoopInfo*, llvm::ScalarEvolution&, llvm::TargetTransformInfo const&, llvm::AssumptionCache&, llvm::OptimizationRemarkEmitter&, llvm::BlockFrequencyInfo*, llvm::ProfileSummaryInfo*, bool, int, bool, bool, bool, std::optional<unsigned int>, std::optional<unsigned int>, std::optional<bool>, std::optional<bool>, std::optional<bool>, std::optional<bool>, std::optional<bool>, std::optional<unsigned int>) LoopUnrollPass.cpp:0:0
#11 0x00000000033fa420 llvm::LoopUnrollPass::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x33fa420)
#12 0x00000000037a6b8e llvm::detail::PassModel<llvm::Function, llvm::LoopUnrollPass, llvm::PreservedAnalyses, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x37a6b8e)
#13 0x0000000000d8693d llvm::detail::PassModel<llvm::Function, llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>, llvm::PreservedAnalyses, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0xd8693d)
#14 0x0000000002f3dfee llvm::ModuleToFunctionPassAdaptor::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x2f3dfee)
#15 0x0000000000d6ac7e llvm::detail::PassModel<llvm::Module, llvm::ModuleToFunctionPassAdaptor, llvm::PreservedAnalyses, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0xd6ac7e)
#16 0x0000000002f3bf9c llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x2f3bf9c)
#17 0x0000000000895e8a llvm::runPassPipeline(llvm::StringRef, llvm::Module&, llvm::TargetMachine*, llvm::TargetLibraryInfoImpl*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::StringRef, llvm::ArrayRef<llvm::PassPlugin>, llvm::opt_tool::OutputKind, llvm::opt_tool::VerifierKind, bool, bool, bool, bool, bool, bool, bool) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x895e8a)
#18 0x000000000079c6d3 main (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x79c6d3)
#19 0x00007fe5f8e29d90 (/lib/x86_64-linux-gnu/libc.so.6+0x29d90)
#20 0x00007fe5f8e29e40 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e40)
#21 0x000000000088b0de _start (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x88b0de)
Program terminated with signal: SIGSEGV
Compiler returned: 139
```

Comments: author: vedantparanjape-amd
body: I looked at this, it seems peelLoop call destroys the LCSSA form, and after that calls simplifyLoop with PreserveLCSSA = true. I did the following change to confirm it.

```c
diff --git a/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp b/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp
index 7cfeb019af97..be73d58796be 100644
--- a/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp
@@ -1270,6 +1270,7 @@ tryToUnrollLoop(Loop *L, DominatorTree &DT, LoopInfo *LI, ScalarEvolution &SE,
     });

     ValueToValueMapTy VMap;
+    dbgs() << "is lcssa form (before peel): " << L->isRecursivelyLCSSAForm(DT, *LI) << "\n";
     if (peelLoop(L, PP.PeelCount, LI, &SE, DT, &AC, PreserveLCSSA, VMap)) {
       simplifyLoopAfterUnroll(L, true, LI, &SE, &DT, &AC, &TTI);
       // If the loop was peeled, we already "used up" the profile information
diff --git a/llvm/lib/Transforms/Utils/LoopPeel.cpp b/llvm/lib/Transforms/Utils/LoopPeel.cpp
index f76fa3bb6c61..47775ef70dc0 100644
--- a/llvm/lib/Transforms/Utils/LoopPeel.cpp
+++ b/llvm/lib/Transforms/Utils/LoopPeel.cpp
@@ -1054,6 +1054,7 @@ bool llvm::peelLoop(Loop *L, unsigned PeelCount, LoopInfo *LI,
   assert(DT.verify(DominatorTree::VerificationLevel::Fast));
 #endif

+  dbgs() << "is lcssa form (after peel): " << L->isRecursivelyLCSSAForm(DT, *LI) << "\n";
   // FIXME: Incrementally update loop-simplify
   simplifyLoop(L, &DT, LI, SE, AC, nullptr, PreserveLCSSA);
```
The output from it was as follows:
```bash
is lcssa form (before peel): 1
is lcssa form (after peel): 0
```
As for the fix, adding a simple check for LCSSA and if it's not in form, forming it helped solve this bug. Does this look like a correct fix ?

```c
diff --git a/llvm/lib/Transforms/Utils/LoopPeel.cpp b/llvm/lib/Transforms/Utils/LoopPeel.cpp
index f76fa3bb6c61..3e0545656d49 100644
--- a/llvm/lib/Transforms/Utils/LoopPeel.cpp
+++ b/llvm/lib/Transforms/Utils/LoopPeel.cpp
@@ -1054,6 +1054,8 @@ bool llvm::peelLoop(Loop *L, unsigned PeelCount, LoopInfo *LI,
   assert(DT.verify(DominatorTree::VerificationLevel::Fast));
 #endif

+  if (PreserveLCSSA && !L->isRecursivelyLCSSAForm(DT, *LI))
+    formLCSSARecursively(*L, DT, LI, SE);
   // FIXME: Incrementally update loop-simplify
   simplifyLoop(L, &DT, LI, SE, AC, nullptr, PreserveLCSSA);
```
CC: @fhahn / @nikic 
author: vedantparanjape-amd
body: @TatyanaDoubts thanks for the report, it is now fixed.

Fix patch:
commit e209178d6402348414b69941c77d621919b3b7ab
Author: Vedant Paranjape <vedant.paranjape@amd.com>
Date:   Wed Feb 21 17:51:56 2024 +0530

    [SimplifyIndVar] LCSSA form is destroyed by simplifyLoopIVs, preserve it (#78696)
    
    In LoopUnroll, peelLoop is called on the loop. After the loop is peeled
    it calls simplifyLoopAfterUnroll on the loop. This call to
    simplifyLoopAfterUnroll doesn't preserve the LCSSA form of the parent
    loop and thus during the next call to peelLoop the LCSSA form is already
    broken.
    
    LoopPeel util takes in the PreserveLCSSA argument and it passes
    on the same argument to simplifyLoop which checks if the loop is in a
    valid LCSSA form, when (PreserveLCSSA = true).
    
    This causes an assert in simplifyLoop when (PreserveLCSSA = true), as
    during the last call LCSSA for the loop wasn't preserved, and thus
    crashes at the following assert.
    
    assert(L->isRecursivelyLCSSAForm(*DT, *LI) &&
                "Requested to preserve LCSSA, but it's already broken.");
    
    Upon debugging, it is evident that simplifyLoopIVs call inside
    simplifyLoopAfterUnroll breaks the LCSSA form. This patch fixes
    llvm#77118, it checks if the replacement of IV Users with Loop Invariant
    preserves the LCSSA form. If it does not, it emits the required LCSSA
    Phi instructions.

diff --git a/llvm/lib/Transforms/Utils/SimplifyIndVar.cpp b/llvm/lib/Transforms/Utils/SimplifyIndVar.cpp
index 66bba1ca2f1d..297cfe5124d8 100644
--- a/llvm/lib/Transforms/Utils/SimplifyIndVar.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyIndVar.cpp
@@ -25,6 +25,7 @@
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Transforms/Utils/Local.h"
+#include "llvm/Transforms/Utils/LoopUtils.h"
 #include "llvm/Transforms/Utils/ScalarEvolutionExpander.h"
 
 using namespace llvm;
@@ -643,10 +644,21 @@ bool SimplifyIndvar::replaceIVUserWithLoopInvariant(Instruction *I) {
   }
 
   auto *Invariant = Rewriter.expandCodeFor(S, I->getType(), IP);
+  bool NeedToEmitLCSSAPhis = false;
+  if (!LI->replacementPreservesLCSSAForm(I, Invariant))
+    NeedToEmitLCSSAPhis = true;
 
   I->replaceAllUsesWith(Invariant);
   LLVM_DEBUG(dbgs() << "INDVARS: Replace IV user: " << *I
                     << " with loop invariant: " << *S << '\n');
+
+  if (NeedToEmitLCSSAPhis) {
+    SmallVector<Instruction *, 1> NeedsLCSSAPhis;
+    NeedsLCSSAPhis.push_back(cast<Instruction>(Invariant));
+    formLCSSAForInstructions(NeedsLCSSAPhis, *DT, *LI, SE);
+    LLVM_DEBUG(dbgs() << " INDVARS: Replacement breaks LCSSA form"
+                      << " inserting LCSSA Phis" << '\n');
+  }
   ++NumFoldedUser;
   Changed = true;
   DeadInsts.emplace_back(I);


Regression tests:
file: llvm/test/Transforms/LoopUnroll/gh-issue77118-broken-lcssa-form.ll
commands: opt -passes=loop-unroll -unroll-peel-count=2 -S -disable-output -debug-only=loop-unroll < %s 2>&1
tests: test_name: <module>
test_body: ; REQUIRES: asserts

define void @test() {
entry:
  br label %loop1

loop1:
  %phi = phi i32 [ 1, %entry ], [ 0, %loop1.latch ]
  br label %loop2

loop2:
  %phi3 = phi i64 [ 0, %loop1 ], [ %sext, %loop2.latch ]
  br label %loop3

loop3:
  %phi5 = phi i64 [ %phi3, %loop2 ], [ %sext, %loop3.latch ]
  %phi6 = phi i32 [ 1, %loop2 ], [ %add10, %loop3.latch ]
  %trunc = trunc i64 %phi5 to i32
  br i1 true, label %loop3.latch, label %exit

loop3.latch:
  %add = add i32 1, %phi
  %sext = sext i32 %add to i64
  %add10 = add i32 %phi6, 1
  %icmp = icmp ugt i32 %add10, 2
  br i1 %icmp, label %loop2.latch, label %loop3

loop2.latch:
  br i1 false, label %loop4.preheader, label %loop2

loop4.preheader:
  br label %loop4

loop4:
  br i1 false, label %loop1.latch, label %loop4

loop1.latch:
  br label %loop1

exit:
  %phi8 = phi i32 [ %trunc, %loop3 ]
  ret void
}