Bug type: crash
Component: LoopDeletion
Bug location: llvm/lib/Transforms/Scalar/LoopDeletion.cpp: deleteLoopIfDead
Issue title:  LLVM ERROR: Broken module found, compilation aborted!
Labels: loopoptim
crash
Reported by: TatyanaDoubts

The bug was found by FuzzGen IR test generator

Run opt with -passes=loop-deletion 
https://godbolt.org/z/16zoTYEqx

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" personality ptr null {
bb:
  br i1 false, label %bb1, label %bb4

bb1:                                              ; preds = %bb
  br label %bb2

bb2:                                              ; preds = %bb1, %bb2
  %invoke = invoke double null(i64 0)
          to label %bb2 unwind label %bb3

bb3:                                              ; preds = %bb2
  %landingpad = landingpad { ptr, i32 }
          cleanup
  ret void

bb4:                                              ; preds = %bb
  ret void
}
```
Stack dump:
```
0.	Program arguments: /opt/compiler-explorer/clang-assertions-trunk/bin/opt -o /app/output.s -S -passes=loop-deletion <source>
 #0 0x0000000003597b18 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x3597b18)
 #1 0x000000000359526c SignalHandler(int) Signals.cpp:0:0
 #2 0x00007fee38042520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #3 0x00007fee380969fc pthread_kill (/lib/x86_64-linux-gnu/libc.so.6+0x969fc)
 #4 0x00007fee38042476 gsignal (/lib/x86_64-linux-gnu/libc.so.6+0x42476)
 #5 0x00007fee380287f3 abort (/lib/x86_64-linux-gnu/libc.so.6+0x287f3)
 #6 0x00000000007905f3 llvm::json::operator==(llvm::json::Value const&, llvm::json::Value const&) (.cold) JSON.cpp:0:0
 #7 0x00000000034dea68 (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x34dea68)
 #8 0x0000000002f6f993 (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x2f6f993)
 #9 0x000000000088bbae llvm::detail::PassModel<llvm::Module, llvm::VerifierPass, llvm::PreservedAnalyses, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x88bbae)
#10 0x0000000002f34d6c llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x2f34d6c)
#11 0x0000000000895cca 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+0x895cca)
#12 0x000000000079c483 main (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x79c483)
#13 0x00007fee38029d90 (/lib/x86_64-linux-gnu/libc.so.6+0x29d90)
#14 0x00007fee38029e40 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e40)
#15 0x000000000088af1e _start (/opt/compiler-explorer/clang-assertions-trunk/bin/opt+0x88af1e)
Program terminated with signal: SIGSEGV
Compiler returned: 139
```

Comments: author: Nirhar
body: Just looked at the IR after loop deletion and before the verify pass, and looks like loop-deletion incorrectly considered the block with the landingpad as the exit block of the loop:
```
define void @wombat() gc "statepoint-example" personality ptr null {
bb:
  br i1 false, label %bb1, label %bb4

bb1:                                              ; preds = %bb
  br label %bb3

bb3:                                              ; preds = %bb1
  %landingpad = landingpad { ptr, i32 }
          cleanup
  ret void

bb4:                                              ; preds = %bb
  ret void
}

$28 = void
```
The problem seems to be with `Loop::getUniqueExitBlock` from https://github.com/llvm/llvm-project/blob/b6fc463d4c0b00e0741776f9d41b47f532a80b9e/llvm/lib/Transforms/Scalar/LoopDeletion.cpp#L453

which returns the landingpad as the exit block of the loop. Perhaps this must be altered to consider that unwind edges are not real edges in the DAG. 



author: Nirhar
body: I'm not sure who are the right persons to consult on this. @EugeneZelenko I leave it to you for you to add them.
author: nikic
body: @Nirhar Isn't this already guarded by https://github.com/llvm/llvm-project/blob/b6fc463d4c0b00e0741776f9d41b47f532a80b9e/llvm/lib/Transforms/Scalar/LoopDeletion.cpp#L492?
author: nikic
body: Ah, this case hits the isLoopNeverExecuted() branch. So that check should just get moved higher, so it covers that case as well.
author: Nirhar
body: @nikic Ah yes that makes sense. I'll land a patch for this soon!

author: Nirhar
body: @nikic On second thought, moving the landing pad check higher will not delete the loop, given that this loop never gets executed as well. Isn't it a better solution to delete the loop as well as delete the landing pad instruction?

Essentially I propose to first check if the loop never gets executed. If yes, delete the loop. Next check if the exit block is a landing pad used exclusively by the loop. If yes, delete the landing pad as well. Is this solution acceptable to you?

Also is it correct to call the landing pad as an `ExitBlock`? In my opinion, semantically the landingpad should not be the block that `L->getUniqueExitBlock` returns given that the landingpad is not a part of the loop's logic. Thus in this case according to me `L->getUniqueExitBlock` should have returned a nullptr, and we should have hit the branch https://github.com/llvm/llvm-project/blob/b6fc463d4c0b00e0741776f9d41b47f532a80b9e/llvm/lib/Transforms/Scalar/LoopDeletion.cpp#L485 
author: nikic
body: > On second thought, moving the landing pad check higher will not delete the loop, given that this loop never gets executed as well. Isn't it a better solution to delete the loop as well as delete the landing pad instruction?

This is a degenerate case, it is not worthwhile to try and optimize it. It's possible to handle this case, but it's much harder than you think. The current code will always replace a loop with a branch to the exit, even if the loop is known to never execute, because this preserves the outer loop structure. If we replaced the entire loop with unreachable (which is what would really be necessary to handle the landingpad case) we may also break an outer loop and would have to perform corresponding loop info updates.

> Also is it correct to call the landing pad as an ExitBlock? 

Note that ExitBlock and ExitingBlock are different things. And ExitingBlock is part of the loop and branches to an ExitBlock not part of the loop. So yes, landingpad is a valid ExitBlock here.
author: Nirhar
body: Understood, thanks for the clarification @nikic !

Fix patch:
commit a0b911745494e3c2f53a27f23c536314818ce0b1
Author: Manish Kausik H <46352931+Nirhar@users.noreply.github.com>
Date:   Fri Jan 19 20:00:20 2024 +0530

    LoopDeletion: Move EH pad check before the isLoopNeverExecuted Check (#78189)
    
    This commit modifies `LoopDeletion::deleteLoopIfDead` to check if the
    exit block of a loop is an EH pad before checking if the loop gets
    executed. This handles the case where an unreachable loop has a
    landingpad as an Exit block, and the loop gets deleted, leaving leaving
    the landingpad without an edge from an unwind clause.
    
    Fixes #76852.

diff --git a/llvm/lib/Transforms/Scalar/LoopDeletion.cpp b/llvm/lib/Transforms/Scalar/LoopDeletion.cpp
index c041e3621a16..bfe9374cf2f8 100644
--- a/llvm/lib/Transforms/Scalar/LoopDeletion.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopDeletion.cpp
@@ -452,6 +452,13 @@ static LoopDeletionResult deleteLoopIfDead(Loop *L, DominatorTree &DT,
 
   BasicBlock *ExitBlock = L->getUniqueExitBlock();
 
+  // We can't directly branch to an EH pad. Don't bother handling this edge
+  // case.
+  if (ExitBlock && ExitBlock->isEHPad()) {
+    LLVM_DEBUG(dbgs() << "Cannot delete loop exiting to EH pad.\n");
+    return LoopDeletionResult::Unmodified;
+  }
+
   if (ExitBlock && isLoopNeverExecuted(L)) {
     LLVM_DEBUG(dbgs() << "Loop is proven to never execute, delete it!\n");
     // We need to forget the loop before setting the incoming values of the exit
@@ -487,13 +494,6 @@ static LoopDeletionResult deleteLoopIfDead(Loop *L, DominatorTree &DT,
     return LoopDeletionResult::Unmodified;
   }
 
-  // We can't directly branch to an EH pad. Don't bother handling this edge
-  // case.
-  if (ExitBlock && ExitBlock->isEHPad()) {
-    LLVM_DEBUG(dbgs() << "Cannot delete loop exiting to EH pad.\n");
-    return LoopDeletionResult::Unmodified;
-  }
-
   // Finally, we have to check that the loop really is dead.
   bool Changed = false;
   if (!isLoopDead(L, SE, ExitingBlocks, ExitBlock, Changed, Preheader, LI)) {


Regression tests:
file: llvm/test/Transforms/LoopDeletion/loop-with-ehpad-not-executed.ll
commands: opt %s -passes=loop-deletion -S
tests: test_name: <module>
test_body: 
define void @wombat() personality ptr null {
;
bb:
  br i1 false, label %bb1, label %bb4

bb1:                                              ; preds = %bb
  br label %bb2

bb2:                                              ; preds = %bb1, %bb2
  %invoke = invoke double null()
  to label %bb2 unwind label %bb3

bb3:                                              ; preds = %bb2
  %landingpad = landingpad { ptr, i32 }
  cleanup
  ret void

bb4:                                              ; preds = %bb
  ret void
}