Skip to content

llvm.wasm.throw is inconsistently considered non-unwinding or invocable #124710

@purplesyringa

Description

@purplesyringa
  1. 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

  1. 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.

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions