Issue 185155
Summary [CoroCleanup] Erasing noop destroy invoke leaves stale unwind predecessor and crashes SimplifyCFG
Labels new issue
Assignees
Reporter cardigan1008
    Here is a crash case when reviewing https://github.com/llvm/llvm-project/pull/179431:

```llvm
define void @erase(i1 %cond) personality i32 0 {
entry:
  br i1 %cond, label %coro, label %other

coro:
  %frame = call noundef ptr @llvm.coro.noop()
 %resume = call ptr @llvm.coro.subfn.addr(ptr %frame, i8 0)
  call fastcc void %resume(ptr %frame)
  %destroy = call ptr @llvm.coro.subfn.addr(ptr %frame, i8 1)
  invoke fastcc void %destroy(ptr %frame)
          to label %done unwind label %unwind

done: ; preds = %coro, %other
  ret void

unwind: ; preds = %coro, %other
  %phi = phi i32 [ 0, %coro ], [ 1, %other ]
  %pad = landingpad { ptr, i32 }
          catch ptr null
  call void @terminate()
  unreachable

other:
  invoke void @terminate() to label %done unwind label %unwind
}

; Function Attrs: noreturn
declare void @terminate() #0

; Function Attrs: nounwind memory(none)
declare ptr @llvm.coro.noop() #1

; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: read)
declare ptr @llvm.coro.subfn.addr(ptr nocapture readonly, i8) #2

attributes #0 = { noreturn }
attributes #1 = { nounwind memory(none) }
attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: read) }
```

Crash:

```sh
opt: /data/yunboni/utils/compilers/repos/llvm-project/llvm/include/llvm/Support/Casting.h:656: decltype(auto) llvm::dyn_cast(From *) [To = llvm::BranchInst, From = llvm::Instruction]: Assertion `detail::isPresent(Val) && "dyn_cast on a non-existent value"' failed.
```

Backtrace:

```sh
Stack dump:
0.	Program arguments: /data/yunboni/utils/compilers/llvm_latest/bin/opt -S -passes=coro-cleanup src.ll
1.	Running pass "coro-cleanup" on module "src.ll"
2.	Running pass "simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-arithmetic;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;no-sink-common-insts;speculate-blocks;simplify-cond-branch;no-speculate-unpredictables>" on function "erase"
 #0 0x000072ab1c668a68 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/data/yunboni/utils/compilers/llvm_latest/bin/../lib/libLLVM.so.23.0git+0x868a68)
 #1 0x000072ab1c6658e5 llvm::sys::RunSignalHandlers() (/data/yunboni/utils/compilers/llvm_latest/bin/../lib/libLLVM.so.23.0git+0x8658e5)
 #2 0x000072ab1c6698c1 SignalHandler(int, siginfo_t*, void*) Signals.cpp:0:0
 #3 0x000072ab1b645330 (/lib/x86_64-linux-gnu/libc.so.6+0x45330)
 #4 0x000072ab1b69eb2c __pthread_kill_implementation ./nptl/pthread_kill.c:44:76
 #5 0x000072ab1b69eb2c __pthread_kill_internal ./nptl/pthread_kill.c:78:10
 #6 0x000072ab1b69eb2c pthread_kill ./nptl/pthread_kill.c:89:10
 #7 0x000072ab1b64527e raise ./signal/../sysdeps/posix/raise.c:27:6
 #8 0x000072ab1b6288ff abort ./stdlib/abort.c:81:7
 #9 0x000072ab1b62881b _nl_load_domain ./intl/loadmsgcat.c:1177:9
#10 0x000072ab1b63b517 (/lib/x86_64-linux-gnu/libc.so.6+0x3b517)
#11 0x000072ab1d73d0ee llvm::GetIfCondition(llvm::BasicBlock*, llvm::BasicBlock*&, llvm::BasicBlock*&) (/data/yunboni/utils/compilers/llvm_latest/bin/../lib/libLLVM.so.23.0git+0x193d0ee)
#12 0x000072ab1d8b2c4d (anonymous namespace)::SimplifyCFGOpt::run(llvm::BasicBlock*) SimplifyCFG.cpp:0:0
#13 0x000072ab1d8b05dc llvm::simplifyCFG(llvm::BasicBlock*, llvm::TargetTransformInfo const&, llvm::DomTreeUpdater*, llvm::SimplifyCFGOptions const&, llvm::ArrayRef<llvm::WeakVH>) (/data/yunboni/utils/compilers/llvm_latest/bin/../lib/libLLVM.so.23.0git+0x1ab05dc)
#14 0x000072ab1de50cab iterativelySimplifyCFG(llvm::Function&, llvm::TargetTransformInfo const&, llvm::DomTreeUpdater*, llvm::SimplifyCFGOptions const&) SimplifyCFGPass.cpp:0:0
#15 0x000072ab1de5051b simplifyFunctionCFGImpl(llvm::Function&, llvm::TargetTransformInfo const&, llvm::DominatorTree*, llvm::SimplifyCFGOptions const&) SimplifyCFGPass.cpp:0:0
#16 0x000072ab1de4ed15 llvm::SimplifyCFGPass::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/data/yunboni/utils/compilers/llvm_latest/bin/../lib/libLLVM.so.23.0git+0x204ed15)
#17 0x000072ab1e48638d llvm::detail::PassModel<llvm::Function, llvm::SimplifyCFGPass, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/data/yunboni/utils/compilers/llvm_latest/bin/../lib/libLLVM.so.23.0git+0x268638d)
#18 0x000072ab1c89c42a llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/data/yunboni/utils/compilers/llvm_latest/bin/../lib/libLLVM.so.23.0git+0xa9c42a)
#19 0x000072ab1e4854cd llvm::CoroCleanupPass::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/data/yunboni/utils/compilers/llvm_latest/bin/../lib/libLLVM.so.23.0git+0x26854cd)
#20 0x000072ab1f7ad43d llvm::detail::PassModel<llvm::Module, llvm::CoroCleanupPass, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/data/yunboni/utils/compilers/llvm_latest/bin/../lib/libLLVM.so.23.0git+0x39ad43d)
#21 0x000072ab1c89b1aa llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/data/yunboni/utils/compilers/llvm_latest/bin/../lib/libLLVM.so.23.0git+0xa9b1aa)
#22 0x000058c1f4104922 llvm::runPassPipeline(llvm::StringRef, llvm::Module&, llvm::TargetMachine*, llvm::TargetLibraryInfoImpl*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::StringRef, llvm::ArrayRef<llvm::PassPlugin>, llvm::ArrayRef<std::function<void (llvm::PassBuilder&)>>, llvm::opt_tool::OutputKind, llvm::opt_tool::VerifierKind, bool, bool, bool, bool, bool, bool, bool, bool) (/data/yunboni/utils/compilers/llvm_latest/bin/opt+0x25922)
#23 0x000058c1f40f78e0 optMain (/data/yunboni/utils/compilers/llvm_latest/bin/opt+0x188e0)
#24 0x000072ab1b62a1ca __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:74:3
#25 0x000072ab1b62a28b call_init ./csu/../csu/libc-start.c:128:20
#26 0x000072ab1b62a28b __libc_start_main ./csu/../csu/libc-start.c:347:5
#27 0x000058c1f40f0e35 _start (/data/yunboni/utils/compilers/llvm_latest/bin/opt+0x11e35)
```

LLVM version: https://github.com/llvm/llvm-project/commit/5948beeb527c005f2b97e2c8b79e547d93e163bc

> Note: This is a review assisted with a self-built agent. The reproducer was validated manually. Please let me know if anything is wrong.

**Bug Triggering Analysis:**

The provided test case triggers the bug because it contains an `invoke` instruction that calls the destroy function of a `coro.noop`. The unwind destination of this `invoke` is a block (`%unwind`) that has a PHI node. When the `coro-cleanup` pass processes this `invoke`, it replaces it with a branch to the normal destination (`%done`) and erases the `invoke`. However, it does not update the PHI node in the `%unwind` block to remove the incoming value from the `%coro` block. This results in a malformed CFG where the `%unwind` block still expects an incoming value from `%coro`, but `%coro` is no longer a predecessor. The subsequent `SimplifyCFG` pass crashes when it tries to process this malformed CFG.

**Fix Weakness Analysis:**

The weakness in the fix is in the `tryEraseCallInvoke` function. When it handles an `InvokeInst`, it correctly creates a branch to the normal destination and erases the invoke, but it completely forgets to update the unwind destination block. Specifically, it should call `II->getUnwindDest()->removePredecessor(II->getParent())` before erasing the invoke. This is a common pitfall when manually manipulating the CFG in LLVM passes.

cc @NewSigma 

_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs

Reply via email to