diff options
| author | Amanieu d'Antras <amanieu@gmail.com> | 2020-03-21 07:50:38 +0000 |
|---|---|---|
| committer | Amanieu d'Antras <amanieu@gmail.com> | 2020-08-27 21:08:30 +0100 |
| commit | 239f833ed1842ce7ce5c9989871a9ce9b1ea3546 (patch) | |
| tree | 78aa987848dfbed8edd6def955ab5625acc1ef17 /src/librustc_codegen_llvm | |
| parent | 118860a7e76daaac3564c7655d46ac65a14fc612 (diff) | |
| download | rust-239f833ed1842ce7ce5c9989871a9ce9b1ea3546.tar.gz rust-239f833ed1842ce7ce5c9989871a9ce9b1ea3546.zip | |
Abort when catch_unwind catches a foreign exception
Diffstat (limited to 'src/librustc_codegen_llvm')
| -rw-r--r-- | src/librustc_codegen_llvm/context.rs | 21 | ||||
| -rw-r--r-- | src/librustc_codegen_llvm/intrinsic.rs | 136 |
2 files changed, 135 insertions, 22 deletions
diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs index 26707fdf839..1c51a9df5d8 100644 --- a/src/librustc_codegen_llvm/context.rs +++ b/src/librustc_codegen_llvm/context.rs @@ -82,6 +82,7 @@ pub struct CodegenCx<'ll, 'tcx> { pub dbg_cx: Option<debuginfo::CrateDebugContext<'ll, 'tcx>>, eh_personality: Cell<Option<&'ll Value>>, + eh_catch_typeinfo: Cell<Option<&'ll Value>>, pub rust_try_fn: Cell<Option<&'ll Value>>, intrinsics: RefCell<FxHashMap<&'static str, &'ll Value>>, @@ -311,6 +312,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { coverage_cx, dbg_cx, eh_personality: Cell::new(None), + eh_catch_typeinfo: Cell::new(None), rust_try_fn: Cell::new(None), intrinsics: Default::default(), local_gen_sym_counter: Cell::new(0), @@ -819,6 +821,25 @@ impl CodegenCx<'b, 'tcx> { } None } + + crate fn eh_catch_typeinfo(&self) -> &'b Value { + if let Some(eh_catch_typeinfo) = self.eh_catch_typeinfo.get() { + return eh_catch_typeinfo; + } + let tcx = self.tcx; + assert!(self.sess().target.target.options.is_like_emscripten); + let eh_catch_typeinfo = match tcx.lang_items().eh_catch_typeinfo() { + Some(def_id) => self.get_static(def_id), + _ => { + let ty = self + .type_struct(&[self.type_ptr_to(self.type_isize()), self.type_i8p()], false); + self.declare_global("rust_eh_catch_typeinfo", ty) + } + }; + let eh_catch_typeinfo = self.const_bitcast(eh_catch_typeinfo, self.type_i8p()); + self.eh_catch_typeinfo.set(Some(eh_catch_typeinfo)); + eh_catch_typeinfo + } } impl<'b, 'tcx> CodegenCx<'b, 'tcx> { diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index bb79a52dcf9..c1dfb83b135 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -855,6 +855,8 @@ fn try_intrinsic( bx.store(bx.const_i32(0), dest, ret_align); } else if wants_msvc_seh(bx.sess()) { codegen_msvc_try(bx, try_func, data, catch_func, dest); + } else if bx.sess().target.target.options.is_like_emscripten { + codegen_emcc_try(bx, try_func, data, catch_func, dest); } else { codegen_gnu_try(bx, try_func, data, catch_func, dest); } @@ -880,7 +882,8 @@ fn codegen_msvc_try( let mut normal = bx.build_sibling_block("normal"); let mut catchswitch = bx.build_sibling_block("catchswitch"); - let mut catchpad = bx.build_sibling_block("catchpad"); + let mut catchpad_rust = bx.build_sibling_block("catchpad_rust"); + let mut catchpad_foreign = bx.build_sibling_block("catchpad_foreign"); let mut caught = bx.build_sibling_block("caught"); let try_func = llvm::get_param(bx.llfn(), 0); @@ -890,21 +893,26 @@ fn codegen_msvc_try( // We're generating an IR snippet that looks like: // // declare i32 @rust_try(%try_func, %data, %catch_func) { - // %slot = alloca u8* + // %slot = alloca i8* // invoke %try_func(%data) to label %normal unwind label %catchswitch // // normal: // ret i32 0 // // catchswitch: - // %cs = catchswitch within none [%catchpad] unwind to caller + // %cs = catchswitch within none [%catchpad_rust, %catchpad_foreign] unwind to caller // - // catchpad: - // %tok = catchpad within %cs [%type_descriptor, 0, %slot] + // catchpad_rust: + // %tok = catchpad within %cs [%type_descriptor, 8, %slot] // %ptr = load %slot // call %catch_func(%data, %ptr) // catchret from %tok to label %caught // + // catchpad_foreign: + // %tok = catchpad within %cs [null, 64, null] + // call %catch_func(%data, null) + // catchret from %tok to label %caught + // // caught: // ret i32 1 // } @@ -912,13 +920,11 @@ fn codegen_msvc_try( // This structure follows the basic usage of throw/try/catch in LLVM. // For example, compile this C++ snippet to see what LLVM generates: // - // #include <stdint.h> - // // struct rust_panic { // rust_panic(const rust_panic&); // ~rust_panic(); // - // uint64_t x[2]; + // void* x[2]; // }; // // int __rust_try( @@ -932,6 +938,9 @@ fn codegen_msvc_try( // } catch(rust_panic& a) { // catch_func(data, &a); // return 1; + // } catch(...) { + // catch_func(data, NULL); + // return 1; // } // } // @@ -942,8 +951,9 @@ fn codegen_msvc_try( normal.ret(bx.const_i32(0)); - let cs = catchswitch.catch_switch(None, None, 1); - catchswitch.add_handler(cs, catchpad.llbb()); + let cs = catchswitch.catch_switch(None, None, 2); + catchswitch.add_handler(cs, catchpad_rust.llbb()); + catchswitch.add_handler(cs, catchpad_foreign.llbb()); // We can't use the TypeDescriptor defined in libpanic_unwind because it // might be in another DLL and the SEH encoding only supports specifying @@ -977,11 +987,17 @@ fn codegen_msvc_try( // // Source: MicrosoftCXXABI::getAddrOfCXXCatchHandlerType in clang let flags = bx.const_i32(8); - let funclet = catchpad.catch_pad(cs, &[tydesc, flags, slot]); - let ptr = catchpad.load(slot, ptr_align); - catchpad.call(catch_func, &[data, ptr], Some(&funclet)); - - catchpad.catch_ret(&funclet, caught.llbb()); + let funclet = catchpad_rust.catch_pad(cs, &[tydesc, flags, slot]); + let ptr = catchpad_rust.load(slot, ptr_align); + catchpad_rust.call(catch_func, &[data, ptr], Some(&funclet)); + catchpad_rust.catch_ret(&funclet, caught.llbb()); + + // The flag value of 64 indicates a "catch-all". + let flags = bx.const_i32(64); + let null = bx.const_null(bx.type_i8p()); + let funclet = catchpad_foreign.catch_pad(cs, &[null, flags, null]); + catchpad_foreign.call(catch_func, &[data, null], Some(&funclet)); + catchpad_foreign.catch_ret(&funclet, caught.llbb()); caught.ret(bx.const_i32(1)); }); @@ -1044,13 +1060,7 @@ fn codegen_gnu_try( // rust_try ignores the selector. let lpad_ty = bx.type_struct(&[bx.type_i8p(), bx.type_i32()], false); let vals = catch.landing_pad(lpad_ty, bx.eh_personality(), 1); - let tydesc = match bx.tcx().lang_items().eh_catch_typeinfo() { - Some(tydesc) => { - let tydesc = bx.get_static(tydesc); - bx.bitcast(tydesc, bx.type_i8p()) - } - None => bx.const_null(bx.type_i8p()), - }; + let tydesc = bx.const_null(bx.type_i8p()); catch.add_clause(vals, tydesc); let ptr = catch.extract_value(vals, 0); catch.call(catch_func, &[data, ptr], None); @@ -1064,6 +1074,88 @@ fn codegen_gnu_try( bx.store(ret, dest, i32_align); } +// Variant of codegen_gnu_try used for emscripten where Rust panics are +// implemented using C++ exceptions. Here we use exceptions of a specific type +// (`struct rust_panic`) to represent Rust panics. +fn codegen_emcc_try( + bx: &mut Builder<'a, 'll, 'tcx>, + try_func: &'ll Value, + data: &'ll Value, + catch_func: &'ll Value, + dest: &'ll Value, +) { + let llfn = get_rust_try_fn(bx, &mut |mut bx| { + // Codegens the shims described above: + // + // bx: + // invoke %try_func(%data) normal %normal unwind %catch + // + // normal: + // ret 0 + // + // catch: + // (%ptr, %selector) = landingpad + // %rust_typeid = @llvm.eh.typeid.for(@_ZTI10rust_panic) + // %is_rust_panic = %selector == %rust_typeid + // %catch_data = alloca { i8*, i8 } + // %catch_data[0] = %ptr + // %catch_data[1] = %is_rust_panic + // call %catch_func(%data, %catch_data) + // ret 1 + + bx.sideeffect(); + + let mut then = bx.build_sibling_block("then"); + let mut catch = bx.build_sibling_block("catch"); + + let try_func = llvm::get_param(bx.llfn(), 0); + let data = llvm::get_param(bx.llfn(), 1); + let catch_func = llvm::get_param(bx.llfn(), 2); + bx.invoke(try_func, &[data], then.llbb(), catch.llbb(), None); + then.ret(bx.const_i32(0)); + + // Type indicator for the exception being thrown. + // + // The first value in this tuple is a pointer to the exception object + // being thrown. The second value is a "selector" indicating which of + // the landing pad clauses the exception's type had been matched to. + let tydesc = bx.eh_catch_typeinfo(); + let lpad_ty = bx.type_struct(&[bx.type_i8p(), bx.type_i32()], false); + let vals = catch.landing_pad(lpad_ty, bx.eh_personality(), 2); + catch.add_clause(vals, tydesc); + catch.add_clause(vals, bx.const_null(bx.type_i8p())); + let ptr = catch.extract_value(vals, 0); + let selector = catch.extract_value(vals, 1); + + // Check if the typeid we got is the one for a Rust panic. + let llvm_eh_typeid_for = bx.get_intrinsic("llvm.eh.typeid.for"); + let rust_typeid = catch.call(llvm_eh_typeid_for, &[tydesc], None); + let is_rust_panic = catch.icmp(IntPredicate::IntEQ, selector, rust_typeid); + let is_rust_panic = catch.zext(is_rust_panic, bx.type_bool()); + + // We need to pass two values to catch_func (ptr and is_rust_panic), so + // create an alloca and pass a pointer to that. + let ptr_align = bx.tcx().data_layout.pointer_align.abi; + let i8_align = bx.tcx().data_layout.i8_align.abi; + let catch_data = + catch.alloca(bx.type_struct(&[bx.type_i8p(), bx.type_bool()], false), ptr_align); + let catch_data_0 = catch.inbounds_gep(catch_data, &[bx.const_usize(0), bx.const_usize(0)]); + catch.store(ptr, catch_data_0, ptr_align); + let catch_data_1 = catch.inbounds_gep(catch_data, &[bx.const_usize(0), bx.const_usize(1)]); + catch.store(is_rust_panic, catch_data_1, i8_align); + let catch_data = catch.bitcast(catch_data, bx.type_i8p()); + + catch.call(catch_func, &[data, catch_data], None); + catch.ret(bx.const_i32(1)); + }); + + // Note that no invoke is used here because by definition this function + // can't panic (that's what it's catching). + let ret = bx.call(llfn, &[try_func, data, catch_func], None); + let i32_align = bx.tcx().data_layout.i32_align.abi; + bx.store(ret, dest, i32_align); +} + // Helper function to give a Block to a closure to codegen a shim function. // This is currently primarily used for the `try` intrinsic functions above. fn gen_fn<'ll, 'tcx>( |
