Skip to content

Commit 82730b4

Browse files
committed
wasm exception handling
1 parent 35cdb28 commit 82730b4

File tree

5 files changed

+117
-14
lines changed

5 files changed

+117
-14
lines changed

compiler/rustc_codegen_llvm/src/context.rs

+12-7
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::llvm_util;
88
use crate::type_::Type;
99
use crate::value::Value;
1010

11-
use rustc_codegen_ssa::base::wants_msvc_seh;
11+
use rustc_codegen_ssa::base::{wants_msvc_seh, wants_wasm_eh};
1212
use rustc_codegen_ssa::traits::*;
1313
use rustc_data_structures::base_n;
1414
use rustc_data_structures::fx::FxHashMap;
@@ -532,19 +532,24 @@ impl<'ll, 'tcx> MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> {
532532
if let Some(llpersonality) = self.eh_personality.get() {
533533
return llpersonality;
534534
}
535+
536+
let name = if wants_msvc_seh(self.sess()) {
537+
Some("__CxxFrameHandler3")
538+
} else if wants_wasm_eh(self.sess()) {
539+
Some("__gxx_wasm_personality_v0")
540+
} else {
541+
None
542+
};
543+
535544
let tcx = self.tcx;
536545
let llfn = match tcx.lang_items().eh_personality() {
537-
Some(def_id) if !wants_msvc_seh(self.sess()) => self.get_fn_addr(
546+
Some(def_id) if name.is_none() => self.get_fn_addr(
538547
ty::Instance::resolve(tcx, ty::ParamEnv::reveal_all(), def_id, ty::List::empty())
539548
.unwrap()
540549
.unwrap(),
541550
),
542551
_ => {
543-
let name = if wants_msvc_seh(self.sess()) {
544-
"__CxxFrameHandler3"
545-
} else {
546-
"rust_eh_personality"
547-
};
552+
let name = name.unwrap_or("rust_eh_personality");
548553
if let Some(llfn) = self.get_declared_value(name) {
549554
llfn
550555
} else {

compiler/rustc_codegen_llvm/src/intrinsic.rs

+77-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::type_of::LayoutLlvmExt;
77
use crate::va_arg::emit_va_arg;
88
use crate::value::Value;
99

10-
use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh};
10+
use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh, wants_wasm_eh};
1111
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
1212
use rustc_codegen_ssa::errors::{ExpectedPointerMutability, InvalidMonomorphization};
1313
use rustc_codegen_ssa::mir::operand::OperandRef;
@@ -452,6 +452,8 @@ fn try_intrinsic<'ll>(
452452
bx.store(bx.const_i32(0), dest, ret_align);
453453
} else if wants_msvc_seh(bx.sess()) {
454454
codegen_msvc_try(bx, try_func, data, catch_func, dest);
455+
} else if wants_wasm_eh(bx.sess()) {
456+
codegen_wasm_try(bx, try_func, data, catch_func, dest);
455457
} else if bx.sess().target.os == "emscripten" {
456458
codegen_emcc_try(bx, try_func, data, catch_func, dest);
457459
} else {
@@ -610,6 +612,80 @@ fn codegen_msvc_try<'ll>(
610612
bx.store(ret, dest, i32_align);
611613
}
612614

615+
// WASM's definition of the `rust_try` function.
616+
fn codegen_wasm_try<'ll>(
617+
bx: &mut Builder<'_, 'll, '_>,
618+
try_func: &'ll Value,
619+
data: &'ll Value,
620+
catch_func: &'ll Value,
621+
dest: &'ll Value,
622+
) {
623+
let (llty, llfn) = get_rust_try_fn(bx, &mut |mut bx| {
624+
bx.set_personality_fn(bx.eh_personality());
625+
626+
let normal = bx.append_sibling_block("normal");
627+
let catchswitch = bx.append_sibling_block("catchswitch");
628+
let catchpad = bx.append_sibling_block("catchpad");
629+
let caught = bx.append_sibling_block("caught");
630+
631+
let try_func = llvm::get_param(bx.llfn(), 0);
632+
let data = llvm::get_param(bx.llfn(), 1);
633+
let catch_func = llvm::get_param(bx.llfn(), 2);
634+
635+
// We're generating an IR snippet that looks like:
636+
//
637+
// declare i32 @rust_try(%try_func, %data, %catch_func) {
638+
// %slot = alloca i8*
639+
// invoke %try_func(%data) to label %normal unwind label %catchswitch
640+
//
641+
// normal:
642+
// ret i32 0
643+
//
644+
// catchswitch:
645+
// %cs = catchswitch within none [%catchpad_rust] unwind to caller
646+
//
647+
// catchpad:
648+
// %tok = catchpad within %cs [null]
649+
// %ptr = call @llvm.wasm.get.exception(token %tok)
650+
// %sel = call @llvm.wasm.get.ehselector(token %tok)
651+
// call %catch_func(%data, %ptr)
652+
// catchret from %tok to label %caught
653+
//
654+
// caught:
655+
// ret i32 1
656+
// }
657+
//
658+
let try_func_ty = bx.type_func(&[bx.type_i8p()], bx.type_void());
659+
bx.invoke(try_func_ty, None, None, try_func, &[data], normal, catchswitch, None);
660+
661+
bx.switch_to_block(normal);
662+
bx.ret(bx.const_i32(0));
663+
664+
bx.switch_to_block(catchswitch);
665+
let cs = bx.catch_switch(None, None, &[catchpad]);
666+
667+
bx.switch_to_block(catchpad);
668+
let null = bx.const_null(bx.type_i8p());
669+
let funclet = bx.catch_pad(cs, &[null]);
670+
671+
let ptr = bx.call_intrinsic("llvm.wasm.get.exception", &[funclet.cleanuppad()]);
672+
let _sel = bx.call_intrinsic("llvm.wasm.get.ehselector", &[funclet.cleanuppad()]);
673+
674+
let catch_ty = bx.type_func(&[bx.type_i8p(), bx.type_i8p()], bx.type_void());
675+
bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet));
676+
bx.catch_ret(&funclet, caught);
677+
678+
bx.switch_to_block(caught);
679+
bx.ret(bx.const_i32(1));
680+
});
681+
682+
// Note that no invoke is used here because by definition this function
683+
// can't panic (that's what it's catching).
684+
let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None);
685+
let i32_align = bx.tcx().data_layout.i32_align.abi;
686+
bx.store(ret, dest, i32_align);
687+
}
688+
613689
// Definition of the standard `try` function for Rust using the GNU-like model
614690
// of exceptions (e.g., the normal semantics of LLVM's `landingpad` and `invoke`
615691
// instructions).

compiler/rustc_codegen_ssa/src/base.rs

+14
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,13 @@ pub fn cast_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
357357
}
358358
}
359359

360+
// Returns `true` if this session's target will use native wasm
361+
// exceptions. This means that the VM does the unwinding for
362+
// us
363+
pub fn wants_wasm_eh(sess: &Session) -> bool {
364+
sess.target.is_like_wasm && sess.target.os != "emscripten"
365+
}
366+
360367
/// Returns `true` if this session's target will use SEH-based unwinding.
361368
///
362369
/// This is only true for MSVC targets, and even then the 64-bit MSVC target
@@ -366,6 +373,13 @@ pub fn wants_msvc_seh(sess: &Session) -> bool {
366373
sess.target.is_like_msvc
367374
}
368375

376+
/// Returns `true` if this session's target requires the new exception
377+
/// handling LLVM IR instructions (catchpad / cleanuppad / ... instead
378+
/// of landingpad)
379+
pub fn wants_new_eh_instructions(sess: &Session) -> bool {
380+
wants_wasm_eh(sess) || wants_msvc_seh(sess)
381+
}
382+
369383
pub fn memcpy_ty<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
370384
bx: &mut Bx,
371385
dst: Bx::Value,

compiler/rustc_codegen_ssa/src/mir/block.rs

+12-5
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
7979
lltarget = fx.landing_pad_for(target);
8080
}
8181
if is_cleanupret {
82-
// MSVC cross-funclet jump - need a trampoline
83-
debug_assert!(base::wants_msvc_seh(fx.cx.tcx().sess));
82+
// Cross-funclet jump - need a trampoline
83+
debug_assert!(base::wants_new_eh_instructions(fx.cx.tcx().sess));
8484
debug!("llbb_with_cleanup: creating cleanup trampoline for {:?}", target);
8585
let name = &format!("{:?}_cleanup_trampoline_{:?}", self.bb, target);
8686
let trampoline_llbb = Bx::append_block(fx.cx, fx.llfn, name);
@@ -177,9 +177,16 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
177177
mir::UnwindAction::Continue => None,
178178
mir::UnwindAction::Unreachable => None,
179179
mir::UnwindAction::Terminate => {
180-
if fx.mir[self.bb].is_cleanup && base::wants_msvc_seh(fx.cx.tcx().sess) {
181-
// SEH will abort automatically if an exception tries to
180+
if fx.mir[self.bb].is_cleanup && base::wants_new_eh_instructions(fx.cx.tcx().sess) {
181+
// MSVC SEH will abort automatically if an exception tries to
182182
// propagate out from cleanup.
183+
184+
// FIXME(@mirkootter): For wasm, we currently do not support terminate during
185+
// cleanup, because this requires a few more changes: The current code
186+
// caches the `terminate_block` for each function; funclet based code - however -
187+
// requires a different terminate_block for each funclet
188+
// Until this is implemented, we just do not unwind inside cleanup blocks
189+
183190
None
184191
} else {
185192
Some(fx.terminate_block())
@@ -1528,7 +1535,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
15281535
// FIXME(eddyb) rename this to `eh_pad_for_uncached`.
15291536
fn landing_pad_for_uncached(&mut self, bb: mir::BasicBlock) -> Bx::BasicBlock {
15301537
let llbb = self.llbb(bb);
1531-
if base::wants_msvc_seh(self.cx.sess()) {
1538+
if base::wants_new_eh_instructions(self.cx.sess()) {
15321539
let cleanup_bb = Bx::append_block(self.cx, self.llfn, &format!("funclet_{:?}", bb));
15331540
let mut cleanup_bx = Bx::build(self.cx, cleanup_bb);
15341541
let funclet = cleanup_bx.cleanup_pad(None, &[]);

compiler/rustc_codegen_ssa/src/mir/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,8 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
169169
start_bx.set_personality_fn(cx.eh_personality());
170170
}
171171

172-
let cleanup_kinds = base::wants_msvc_seh(cx.tcx().sess).then(|| analyze::cleanup_kinds(&mir));
172+
let cleanup_kinds =
173+
base::wants_new_eh_instructions(cx.tcx().sess).then(|| analyze::cleanup_kinds(&mir));
173174

174175
let cached_llbbs: IndexVec<mir::BasicBlock, CachedLlbb<Bx::BasicBlock>> =
175176
mir.basic_blocks

0 commit comments

Comments
 (0)