From 45adab87a56b12b411d3cce66b8a18942f7ac179 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Wed, 13 May 2026 12:16:15 -0700 Subject: [PATCH] Pass user-provided llvm_args after target spec llvm_args. This ensures that user arguments can override target arguments without being silently ignored. --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 5 +- tests/assembly-llvm/wasm_legacy_eh.rs | 80 ++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 tests/assembly-llvm/wasm_legacy_eh.rs diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index bf9db092ab7f5..eb1efa31ae4ab 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -66,7 +66,10 @@ unsafe fn configure_llvm(sess: &Session) { let cg_opts = sess.opts.cg.llvm_args.iter().map(AsRef::as_ref); let tg_opts = sess.target.llvm_args.iter().map(AsRef::as_ref); - let sess_args = cg_opts.chain(tg_opts); + // Target-spec args are passed to LLVM before user `-Cllvm-args`. LLVM's + // `cl::opt` parser is last-wins, so this lets `-Cllvm-args=...` override + // a value already set in the target spec (e.g. `-wasm-use-legacy-eh`). + let sess_args = tg_opts.chain(cg_opts); let user_specified_args: FxHashSet<_> = sess_args.clone().map(|s| llvm_arg_to_arg_name(s)).filter(|s| !s.is_empty()).collect(); diff --git a/tests/assembly-llvm/wasm_legacy_eh.rs b/tests/assembly-llvm/wasm_legacy_eh.rs new file mode 100644 index 0000000000000..fa4332904cad6 --- /dev/null +++ b/tests/assembly-llvm/wasm_legacy_eh.rs @@ -0,0 +1,80 @@ +//@ only-wasm32 +//@ assembly-output: emit-asm +//@ compile-flags: -C target-feature=+exception-handling +//@ compile-flags: -C panic=unwind +//@ compile-flags: -C llvm-args=-wasm-use-legacy-eh=true + +// Verifies that user-supplied `-Cllvm-args` can override an LLVM argument that +// was set in the target spec. The `wasm32-unknown-unknown` target spec pins +// `-wasm-use-legacy-eh=false`; this test asserts that passing the opposite +// value via `-Cllvm-args` switches code generation back to the legacy +// exception-handling instructions. + +#![crate_type = "lib"] +#![feature(core_intrinsics)] + +extern "C-unwind" { + fn may_panic(); +} + +extern "C" { + fn log_number(number: usize); +} + +struct LogOnDrop; + +impl Drop for LogOnDrop { + fn drop(&mut self) { + unsafe { + log_number(0); + } + } +} + +// CHECK-LABEL: test_cleanup: +#[no_mangle] +pub fn test_cleanup() { + let _log_on_drop = LogOnDrop; + unsafe { + may_panic(); + } + + // CHECK-NOT: try_table + // CHECK-NOT: catch_all_ref + // CHECK-NOT: throw_ref + // CHECK-NOT: call + // CHECK: try + // CHECK: call may_panic + // CHECK: catch_all + // CHECK: rethrow + // CHECK: end_try +} + +// CHECK-LABEL: test_rtry: +#[no_mangle] +pub fn test_rtry() { + unsafe { + core::intrinsics::catch_unwind( + |_| { + may_panic(); + }, + core::ptr::null_mut(), + |data, exception| { + log_number(data as usize); + log_number(exception as usize); + }, + ); + } + + // CHECK-NOT: try_table + // CHECK-NOT: catch_all_ref + // CHECK-NOT: throw_ref + // CHECK-NOT: call + // CHECK: try + // CHECK: call may_panic + // CHECK: catch + // CHECK: call log_number + // CHECK: call log_number + // CHECK-NOT: rethrow + // CHECK: end_try +}