[llvm] [WebAssembly] Handle irreducible EH pad backedges (PR #204631)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 7 12:02:54 PDT 2026
================
----------------
hertelukas wrote:
>From my understanding, the only way this can happen is if `Pred->isEHPad()` [here](https://github.com/llvm/llvm-project/pull/204631/changes#diff-dccdac231f946dd8ba16ea582ef922a904314381ea7eb43e064d148757e3e93fR381) is true.
And this leads to various different crashes:
<details>
<summary>E.g., in <tt>WebAssembly CFG Sort</tt></summary>
```llvm
target datalayout = "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-i128:128-f128:64-n32:64-S128-ni:1:10:20"
target triple = "wasm64-unknown-unknown"
define void @test_ehpad_pred() #0 personality ptr @__gxx_wasm_personality_v0 {
entry:
invoke void @foo()
to label %loop_body unwind label %outer.dispatch
outer.dispatch:
%outer = catchswitch within none [label %outer.catch] unwind to caller
outer.catch:
%outer.pad = catchpad within %outer [ptr null]
catchret from %outer.pad to label %loop_body
loop_body:
invoke void @foo()
to label %exit unwind label %inner.dispatch
inner.dispatch:
%inner = catchswitch within none [label %inner.catch] unwind label %outer.dispatch
inner.catch:
%inner.pad = catchpad within %inner [ptr null]
invoke void @foo() [ "funclet"(token %inner.pad) ]
to label %inner.cont unwind label %outer.dispatch
inner.cont:
catchret from %inner.pad to label %loop_body
exit:
ret void
}
declare void @foo()
declare i32 @__gxx_wasm_personality_v0(...)
attributes #0 = { "target-features"="+exception-handling" }
```
leads to
```
llc: /home/lukas/Documents/Programming/llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyCFGSort.cpp:317: void sortBlocks(MachineFunction &, const MachineLoopInfo &, const WebAssemblyExceptionInfo &, MachineDominatorTree &): Assertion `(Pred->getNumber() < MBB.getNumber() || Region->contains(Pred)) && "Loop header predecessors must be loop predecessors or " "backedges"' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace and instructions to reproduce the bug.
Stack dump:
0. Program arguments: ./build/relwithdbg/bin/llc --wasm-enable-eh /tmp/cfg_sort.ll
1. Running pass 'Function Pass Manager' on module '/tmp/cfg_sort.ll'.
2. Running pass 'WebAssembly CFG Sort' on function '@test_ehpad_pred'
```
</details>
<details>
<summary>Or directly in <tt>WebAssembly Fix Irreducible Control Flow</tt> (taken from #208409) </summary>
```llvm
target datalayout = "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-i128:128-f128:64-n32:64-S128-ni:1:10:20"
target triple = "wasm64-unknown-emscripten"
declare i32 @__gxx_wasm_personality_v0(...)
declare ptr @llvm.wasm.get.exception(token)
declare i32 @llvm.wasm.get.ehselector(token)
declare i32 @f(ptr)
declare void @g(ptr)
declare i1 @h(i32)
define void @crash() #0 personality ptr @__gxx_wasm_personality_v0 {
entry:
%0 = invoke i32 @f(ptr null)
to label %cont unwind label %cleanup.outer
cont:
invoke void @g(ptr null)
to label %exit2 unwind label %catch.dispatch
cleanup.outer: ; unwinds INTO the catchswitch below
%pad = cleanuppad within none []
cleanupret from %pad unwind label %catch.dispatch
catch.dispatch:
%cs = catchswitch within none [label %catch.start] unwind label %cleanup.final
catch.start:
%cp = catchpad within %cs [ptr null, ptr null, ptr null]
%exn = tail call ptr @llvm.wasm.get.exception(token %cp)
%sel = tail call i32 @llvm.wasm.get.ehselector(token %cp)
catchret from %cp to label %after.catch
after.catch: ; unwind edge goes BACK to cleanup.outer (cycle)
%1 = invoke i1 @h(i32 0)
to label %exit1 unwind label %cleanup.outer
exit1:
ret void
exit2:
ret void
cleanup.final:
%pad2 = cleanuppad within none []
ret void
}
attributes #0 = { "target-features"="+exception-handling" }
```
leads to:
```
llc: /home/lukas/Documents/Programming/llvm-project/llvm/include/llvm/ADT/DenseMap.h:232: const ValueT &llvm::DenseMapBase<llvm::DenseMap<llvm::MachineBasicBlock *, (anonymous namespace)::ReachabilityNode *>, llvm::MachineBasicBlock *, (anonymous namespace)::ReachabilityNode *, llvm::DenseMapInfo<llvm::MachineBasicBlock *>, llvm::detail::DenseMapPair<llvm::MachineBasicBlock *, (anonymous namespace)::ReachabilityNode *>>::at(const_arg_type_t<KeyT>) const [DerivedT = llvm::DenseMap<llvm::MachineBasicBlock *, (anonymous namespace)::ReachabilityNode *>, KeyT = llvm::MachineBasicBlock *, ValueT = (anonymous namespace)::ReachabilityNode *, KeyInfoT = llvm::DenseMapInfo<llvm::MachineBasicBlock *>, BucketT = llvm::detail::DenseMapPair<llvm::MachineBasicBlock *, (anonymous namespace)::ReachabilityNode *>]: Assertion `Iter != this->end() && "DenseMap::at failed due to a missing key"' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace and instructions to reproduce the bug.
Stack dump:
0. Program arguments: ./build/relwithdbg/bin/llc --wasm-enable-eh /tmp/emscripten.ll
1. Running pass 'Function Pass Manager' on module '/tmp/emscripten.ll'.
2. Running pass 'WebAssembly Fix Irreducible Control Flow' on function '@crash'
```
</details>
I debugged neither further. That is why I'm unsure whether it is a good idea to just give up and leave the CFG in a (for WebAssembly) invalid state. I think it's probably best to either explicitly throw an error here or implement support for EHPad predecessors, as this leads to crashes in real programs.
https://github.com/llvm/llvm-project/pull/204631
More information about the llvm-commits
mailing list