[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