Skip to content

Commit

Permalink
Generate correct terminate block under Wasm EH
Browse files Browse the repository at this point in the history
This fixes failing LLVM assertions during insnsel.

Improves rust-lang#135665.
  • Loading branch information
purplesyringa committed Feb 6, 2025
1 parent 66d6064 commit a983b58
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 6 deletions.
29 changes: 23 additions & 6 deletions compiler/rustc_codegen_ssa/src/mir/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1703,15 +1703,32 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let mut cs_bx = Bx::build(self.cx, llbb);
let cs = cs_bx.catch_switch(None, None, &[cp_llbb]);

// The "null" here is actually a RTTI type descriptor for the
// C++ personality function, but `catch (...)` has no type so
// it's null. The 64 here is actually a bitfield which
// represents that this is a catch-all block.
bx = Bx::build(self.cx, cp_llbb);
let null =
bx.const_null(bx.type_ptr_ext(bx.cx().data_layout().instruction_address_space));
let sixty_four = bx.const_i32(64);
funclet = Some(bx.catch_pad(cs, &[null, sixty_four, null]));

// The `null` in first argument here is actually a RTTI type
// descriptor for the C++ personality function, but `catch (...)`
// has no type so it's null.
let args = if base::wants_msvc_seh(self.cx.sess()) {
// This bitmask is a single `HT_IsStdDotDot` flag, which
// represents that this is a C++-style `catch (...)` block that
// only captures programmatic exceptions, not all SEH
// exceptions. The second `null` points to a non-existent
// `alloca` instruction, which an LLVM pass would inline into
// the initial SEH frame allocation.
let adjectives = bx.const_i32(0x40);
&[null, adjectives, null] as &[_]
} else {
// Specifying more arguments than necessary usually doesn't
// hurt, but the `WasmEHPrepare` LLVM pass does not recognize
// anything other than a single `null` as a `catch (...)` block,
// leading to problems down the line during instruction
// selection.
&[null] as &[_]
};

funclet = Some(bx.catch_pad(cs, args));
} else {
llbb = Bx::append_block(self.cx, self.llfn, "terminate");
bx = Bx::build(self.cx, llbb);
Expand Down
37 changes: 37 additions & 0 deletions tests/codegen/terminating-catchpad.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//@ revisions: emscripten wasi seh
//@[emscripten] compile-flags: --target wasm32-unknown-emscripten -Z emscripten-wasm-eh
//@[wasi] compile-flags: --target wasm32-wasip1 -C panic=unwind
//@[seh] compile-flags: --target x86_64-pc-windows-msvc
//@[emscripten] needs-llvm-components: webassembly
//@[wasi] needs-llvm-components: webassembly
//@[seh] needs-llvm-components: x86

// Ensure a catch-all generates:
// - `catchpad ... [ptr null]` on Wasm (otherwise LLVM gets confused)
// - `catchpad ... [ptr null, i32 64, ptr null]` on Windows (otherwise we catch SEH exceptions)

#![feature(no_core, lang_items, rustc_attrs)]
#![crate_type = "lib"]
#![no_std]
#![no_core]

#[lang = "sized"]
trait Sized {}

unsafe extern "C-unwind" {
safe fn unwinds();
}

#[lang = "panic_cannot_unwind"]
fn panic_cannot_unwind() -> ! {
loop {}
}

#[no_mangle]
#[rustc_nounwind]
pub fn doesnt_unwind() {
// emscripten: %catchpad = catchpad within %catchswitch [ptr null]
// wasi: %catchpad = catchpad within %catchswitch [ptr null]
// seh: %catchpad = catchpad within %catchswitch [ptr null, i32 64, ptr null]
unwinds();
}

0 comments on commit a983b58

Please sign in to comment.