Description
- Miscompiled:
void loud();
void throw_and_catch(void *ex) {
try {
__builtin_wasm_throw(0, ex);
} catch (...) {
loud();
}
}
The generated IR is
define hidden void @throw_and_catch(void*)(ptr noundef %ex) {
entry:
%ex.addr = alloca ptr, align 4
store ptr %ex, ptr %ex.addr, align 4
%0 = load ptr, ptr %ex.addr, align 4
call void @llvm.wasm.throw(i32 0, ptr %0)
ret void
}
without a catchpad.
https://godbolt.org/z/Po668Yxaz
- Broken IR:
void loud();
void my_throw(void *ex) {
__builtin_wasm_throw(0, ex);
}
void throw_and_catch(void *ex) {
try {
my_throw(ex);
} catch (...) {
loud();
}
}
InlinerPass replaces
invoke void @my_throw(void*)(ptr noundef %ex)
to label %try.cont unwind label %catch.dispatch
(correct, working) with
invoke void @llvm.wasm.throw(i32 0, ptr %ex)
to label %.noexc unwind label %catch.dispatch
(broken, llvm.wasm.throw
cannot be invoked), which halts the backend.
https://godbolt.org/z/rMEd1x79z
As far as I'm aware, llvm.wasm.throw
is the only function that can unwind but cannot be invoked, making this troublesome for external frontends like rustc, not just for optimization passes. Ideally it would become invocable.