Skip to content

Add support for wasm exception handling to Emscripten target #131830

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 7, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion compiler/rustc_codegen_llvm/src/llvm_util.rs
Original file line number Diff line number Diff line change
@@ -109,7 +109,10 @@ unsafe fn configure_llvm(sess: &Session) {
add("-wasm-enable-eh", false);
}

if sess.target.os == "emscripten" && sess.panic_strategy() == PanicStrategy::Unwind {
if sess.target.os == "emscripten"
&& !sess.opts.unstable_opts.emscripten_wasm_eh
&& sess.panic_strategy() == PanicStrategy::Unwind
{
add("-enable-emscripten-cxx-exceptions", false);
}

8 changes: 5 additions & 3 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
@@ -2466,10 +2466,12 @@ fn add_order_independent_options(
}

if sess.target.os == "emscripten" {
cmd.cc_arg("-s").cc_arg(if sess.panic_strategy() == PanicStrategy::Abort {
"DISABLE_EXCEPTION_CATCHING=1"
cmd.cc_arg(if sess.panic_strategy() == PanicStrategy::Abort {
"-sDISABLE_EXCEPTION_CATCHING=1"
} else if sess.opts.unstable_opts.emscripten_wasm_eh {
"-fwasm-exceptions"
} else {
"DISABLE_EXCEPTION_CATCHING=0"
"-sDISABLE_EXCEPTION_CATCHING=0"
});
}

3 changes: 2 additions & 1 deletion compiler/rustc_codegen_ssa/src/base.rs
Original file line number Diff line number Diff line change
@@ -388,7 +388,8 @@ pub(crate) fn build_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
// exceptions. This means that the VM does the unwinding for
// us
pub fn wants_wasm_eh(sess: &Session) -> bool {
sess.target.is_like_wasm && sess.target.os != "emscripten"
sess.target.is_like_wasm
&& (sess.target.os != "emscripten" || sess.opts.unstable_opts.emscripten_wasm_eh)
}

/// Returns `true` if this session's target will use SEH-based unwinding.
1 change: 1 addition & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
@@ -37,6 +37,7 @@ const GATED_CFGS: &[GatedCfg] = &[
(sym::sanitizer_cfi_normalize_integers, sym::cfg_sanitizer_cfi, Features::cfg_sanitizer_cfi),
// this is consistent with naming of the compiler flag it's for
(sym::fmt_debug, sym::fmt_debug, Features::fmt_debug),
(sym::emscripten_wasm_eh, sym::cfg_emscripten_wasm_eh, Features::cfg_emscripten_wasm_eh),
];

/// Find a gated cfg determined by the `pred`icate which is given the cfg's name.
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
@@ -202,6 +202,8 @@ declare_features! (
(internal, allow_internal_unstable, "1.0.0", None),
/// Allows using anonymous lifetimes in argument-position impl-trait.
(unstable, anonymous_lifetime_in_impl_trait, "1.63.0", None),
/// Allows access to the emscripten_wasm_eh config, used by panic_unwind and unwind
(internal, cfg_emscripten_wasm_eh, "CURRENT_RUSTC_VERSION", None),
/// Allows identifying the `compiler_builtins` crate.
(internal, compiler_builtins, "1.13.0", None),
/// Allows writing custom MIR
1 change: 1 addition & 0 deletions compiler/rustc_interface/src/tests.rs
Original file line number Diff line number Diff line change
@@ -782,6 +782,7 @@ fn test_unstable_options_tracking_hash() {
tracked!(dwarf_version, Some(5));
tracked!(embed_source, true);
tracked!(emit_thin_lto, false);
tracked!(emscripten_wasm_eh, true);
tracked!(export_executable_symbols, true);
tracked!(fewer_names, Some(true));
tracked!(fixed_x18, true);
5 changes: 4 additions & 1 deletion compiler/rustc_passes/src/weak_lang_items.rs
Original file line number Diff line number Diff line change
@@ -26,7 +26,10 @@ pub(crate) fn check_crate(
if items.eh_personality().is_none() {
items.missing.push(LangItem::EhPersonality);
}
if tcx.sess.target.os == "emscripten" && items.eh_catch_typeinfo().is_none() {
if tcx.sess.target.os == "emscripten"
&& items.eh_catch_typeinfo().is_none()
&& !tcx.sess.opts.unstable_opts.emscripten_wasm_eh
{
items.missing.push(LangItem::EhCatchTypeinfo);
}

5 changes: 5 additions & 0 deletions compiler/rustc_session/src/config/cfg.rs
Original file line number Diff line number Diff line change
@@ -143,6 +143,7 @@ pub(crate) fn disallow_cfgs(sess: &Session, user_cfgs: &Cfg) {
| (sym::target_has_atomic_load_store, Some(_))
| (sym::target_thread_local, None) => disallow(cfg, "--target"),
(sym::fmt_debug, None | Some(_)) => disallow(cfg, "-Z fmt-debug"),
(sym::emscripten_wasm_eh, None | Some(_)) => disallow(cfg, "-Z emscripten_wasm_eh"),
_ => {}
}
}
@@ -295,6 +296,10 @@ pub(crate) fn default_configuration(sess: &Session) -> Cfg {
ins_none!(sym::ub_checks);
}

// Nightly-only implementation detail for the `panic_unwind` and `unwind` crates.
if sess.is_nightly_build() && sess.opts.unstable_opts.emscripten_wasm_eh {
ins_none!(sym::emscripten_wasm_eh);
}
ret
}

2 changes: 2 additions & 0 deletions compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
@@ -1771,6 +1771,8 @@ options! {
"emit a section containing stack size metadata (default: no)"),
emit_thin_lto: bool = (true, parse_bool, [TRACKED],
"emit the bc module with thin LTO info (default: yes)"),
emscripten_wasm_eh: bool = (false, parse_bool, [TRACKED],
"Use WebAssembly error handling for wasm32-unknown-emscripten"),
enforce_type_length_limit: bool = (false, parse_bool, [TRACKED],
"enforce the type length limit when monomorphizing instances in codegen"),
export_executable_symbols: bool = (false, parse_bool, [TRACKED],
2 changes: 2 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
@@ -570,6 +570,7 @@ symbols! {
cfg_autodiff_fallback,
cfg_boolean_literals,
cfg_doctest,
cfg_emscripten_wasm_eh,
cfg_eval,
cfg_fmt_debug,
cfg_hide,
@@ -823,6 +824,7 @@ symbols! {
emit_enum_variant_arg,
emit_struct,
emit_struct_field,
emscripten_wasm_eh,
enable,
encode,
end,
2 changes: 1 addition & 1 deletion library/panic_unwind/Cargo.toml
Original file line number Diff line number Diff line change
@@ -23,4 +23,4 @@ libc = { version = "0.2", default-features = false }

[lints.rust.unexpected_cfgs]
level = "warn"
check-cfg = []
check-cfg = ['cfg(emscripten_wasm_eh)']
3 changes: 2 additions & 1 deletion library/panic_unwind/src/lib.rs
Original file line number Diff line number Diff line change
@@ -25,13 +25,14 @@
// `real_imp` is unused with Miri, so silence warnings.
#![cfg_attr(miri, allow(dead_code))]
#![allow(internal_features)]
#![cfg_attr(not(bootstrap), feature(cfg_emscripten_wasm_eh))]

use alloc::boxed::Box;
use core::any::Any;
use core::panic::PanicPayload;

cfg_if::cfg_if! {
if #[cfg(target_os = "emscripten")] {
if #[cfg(all(target_os = "emscripten", not(emscripten_wasm_eh)))] {
#[path = "emcc.rs"]
mod imp;
} else if #[cfg(target_os = "hermit")] {
2 changes: 1 addition & 1 deletion library/unwind/Cargo.toml
Original file line number Diff line number Diff line change
@@ -37,4 +37,4 @@ system-llvm-libunwind = []

[lints.rust.unexpected_cfgs]
level = "warn"
check-cfg = []
check-cfg = ['cfg(emscripten_wasm_eh)']
3 changes: 2 additions & 1 deletion library/unwind/src/lib.rs
Original file line number Diff line number Diff line change
@@ -4,10 +4,11 @@
#![feature(staged_api)]
#![cfg_attr(not(target_env = "msvc"), feature(libc))]
#![cfg_attr(
all(target_family = "wasm", not(target_os = "emscripten")),
all(target_family = "wasm", any(not(target_os = "emscripten"), emscripten_wasm_eh)),
feature(simd_wasm64, wasm_exception_handling_intrinsics)
)]
#![allow(internal_features)]
#![cfg_attr(not(bootstrap), feature(cfg_emscripten_wasm_eh))]

// Force libc to be included even if unused. This is required by many platforms.
#[cfg(not(all(windows, target_env = "msvc")))]
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# `emscripten-wasm-eh`

Use the WebAssembly exception handling ABI to unwind for the
`wasm32-unknown-emscripten`. If compiling with this setting, the `emcc` linker
should be invoked with `-fwasm-exceptions`. If linking with C/C++ files, the
C/C++ files should also be compiled with `-fwasm-exceptions`.
File renamed without changes.
65 changes: 65 additions & 0 deletions tests/codegen/emscripten-catch-unwind-wasm-eh.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//@ compile-flags: -O --target wasm32-unknown-emscripten -Z emscripten-wasm-eh
//@ needs-llvm-components: webassembly

// Emscripten catch_unwind using wasm exceptions

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

#[lang = "sized"]
trait Sized {}
#[lang = "freeze"]
trait Freeze {}
#[lang = "copy"]
trait Copy {}

impl<T> Copy for *mut T {}

#[rustc_intrinsic]
fn size_of<T>() -> usize {
loop {}
}

extern "rust-intrinsic" {
fn catch_unwind(
try_fn: fn(_: *mut u8),
data: *mut u8,
catch_fn: fn(_: *mut u8, _: *mut u8),
) -> i32;
}

// CHECK-LABEL: @ptr_size
#[no_mangle]
pub fn ptr_size() -> usize {
// CHECK: ret [[PTR_SIZE:.*]]
size_of::<*mut u8>()
}

// CHECK-LABEL: @test_catch_unwind
#[no_mangle]
pub unsafe fn test_catch_unwind(
try_fn: fn(_: *mut u8),
data: *mut u8,
catch_fn: fn(_: *mut u8, _: *mut u8),
) -> i32 {
// CHECK: start:
// CHECK: invoke void %try_fn(ptr %data)
// CHECK: to label %__rust_try.exit unwind label %catchswitch.i
// CHECK: catchswitch.i: ; preds = %start
// CHECK: %catchswitch1.i = catchswitch within none [label %catchpad.i] unwind to caller

// CHECK: catchpad.i: ; preds = %catchswitch.i
// CHECK: %catchpad2.i = catchpad within %catchswitch1.i [ptr null]
// CHECK: %0 = tail call ptr @llvm.wasm.get.exception(token %catchpad2.i)
// CHECK: %1 = tail call i32 @llvm.wasm.get.ehselector(token %catchpad2.i)
// CHECK: call void %catch_fn(ptr %data, ptr %0) [ "funclet"(token %catchpad2.i) ]
// CHECK: catchret from %catchpad2.i to label %__rust_try.exit

// CHECK: __rust_try.exit: ; preds = %start, %catchpad.i
// CHECK: %common.ret.op.i = phi i32 [ 0, %start ], [ 1, %catchpad.i ]
// CHECK: ret i32 %common.ret.op.i

catch_unwind(try_fn, data, catch_fn)
}
8 changes: 8 additions & 0 deletions tests/ui/cfg/disallowed-cli-cfgs.emscripten_wasm_eh_.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: unexpected `--cfg emscripten_wasm_eh` flag
|
= note: config `emscripten_wasm_eh` is only supposed to be controlled by `-Z emscripten_wasm_eh`
= note: manually setting a built-in cfg can and does create incoherent behaviors
= note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default

error: aborting due to 1 previous error

2 changes: 2 additions & 0 deletions tests/ui/cfg/disallowed-cli-cfgs.rs
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@
//@ revisions: target_has_atomic_equal_alignment_ target_has_atomic_load_store_
//@ revisions: target_thread_local_ relocation_model_
//@ revisions: fmt_debug_
//@ revisions: emscripten_wasm_eh_

//@ [overflow_checks_]compile-flags: --cfg overflow_checks
//@ [debug_assertions_]compile-flags: --cfg debug_assertions
@@ -33,5 +34,6 @@
//@ [target_thread_local_]compile-flags: --cfg target_thread_local
//@ [relocation_model_]compile-flags: --cfg relocation_model="a"
//@ [fmt_debug_]compile-flags: --cfg fmt_debug="shallow"
//@ [emscripten_wasm_eh_]compile-flags: --cfg emscripten_wasm_eh

fn main() {}
4 changes: 4 additions & 0 deletions tests/ui/feature-gates/feature-gate-cfg-emscripten-wasm-eh.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
//@ compile-flags: --check-cfg=cfg(emscripten_wasm_eh)
#[cfg(not(emscripten_wasm_eh))]
//~^ `cfg(emscripten_wasm_eh)` is experimental
fn main() {}
12 changes: 12 additions & 0 deletions tests/ui/feature-gates/feature-gate-cfg-emscripten-wasm-eh.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
error[E0658]: `cfg(emscripten_wasm_eh)` is experimental and subject to change
--> $DIR/feature-gate-cfg-emscripten-wasm-eh.rs:2:11
|
LL | #[cfg(not(emscripten_wasm_eh))]
| ^^^^^^^^^^^^^^^^^^
|
= help: add `#![feature(cfg_emscripten_wasm_eh)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0658`.