From 5d53fd7e0d1aa29464248c15f24ee829e8816d45 Mon Sep 17 00:00:00 2001 From: Vadim Chugunov Date: Sun, 15 Dec 2013 17:17:07 -0800 Subject: [PATCH] Stop using C++ exceptions for stack unwinding. --- mk/rt.mk | 22 +-- src/etc/mklldeps.py | 7 + src/librustc/back/link.rs | 13 +- src/librustc/back/upcall.rs | 35 ----- src/librustc/lib.rs | 3 +- src/librustc/lib/llvm.rs | 1 - src/librustc/middle/lang_items.rs | 4 +- src/librustc/middle/trans/base.rs | 10 +- src/librustc/middle/trans/context.rs | 3 - src/libstd/rt/mod.rs | 3 + src/libstd/rt/task.rs | 68 +-------- src/libstd/rt/unwind.rs | 199 +++++++++++++++++++++++++++ src/rt/rust_cxx_glue.cpp | 31 ----- src/rt/rust_try.ll | 19 +++ 14 files changed, 261 insertions(+), 157 deletions(-) delete mode 100644 src/librustc/back/upcall.rs create mode 100644 src/libstd/rt/unwind.rs delete mode 100644 src/rt/rust_cxx_glue.cpp create mode 100644 src/rt/rust_try.ll diff --git a/mk/rt.mk b/mk/rt.mk index f27ed8714f471..83887aa07e288 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -72,9 +72,6 @@ RUNTIME_CXXFLAGS_$(1)_$(2) = -D_RUST_STAGE1 endif endif -RUNTIME_CXXS_$(1)_$(2) := \ - rt/rust_cxx_glue.cpp - RUNTIME_CS_$(1)_$(2) := \ rt/rust_builtin.c \ rt/rust_upcall.c \ @@ -82,6 +79,9 @@ RUNTIME_CS_$(1)_$(2) := \ rt/rust_android_dummy.c \ rt/rust_test_helpers.c +RUNTIME_LL_$(1)_$(2) := \ + rt/rust_try.ll + # stage0 remove this after the next snapshot %.cpp: @touch tmp/foo.o @@ -94,18 +94,17 @@ RT_BUILD_DIR_$(1)_$(2) := $$(RT_OUTPUT_DIR_$(1))/stage$(2) RUNTIME_DEF_$(1)_$(2) := $$(RT_OUTPUT_DIR_$(1))/rustrt$$(CFG_DEF_SUFFIX_$(1)) RUNTIME_INCS_$(1)_$(2) := -I $$(S)src/rt -I $$(S)src/rt/isaac -I $$(S)src/rt/uthash \ -I $$(S)src/rt/arch/$$(HOST_$(1)) -RUNTIME_OBJS_$(1)_$(2) := $$(RUNTIME_CXXS_$(1)_$(2):rt/%.cpp=$$(RT_BUILD_DIR_$(1)_$(2))/%.o) \ +RUNTIME_OBJS_$(1)_$(2) := \ $$(RUNTIME_CS_$(1)_$(2):rt/%.c=$$(RT_BUILD_DIR_$(1)_$(2))/%.o) \ - $$(RUNTIME_S_$(1)_$(2):rt/%.S=$$(RT_BUILD_DIR_$(1)_$(2))/%.o) + $$(RUNTIME_S_$(1)_$(2):rt/%.S=$$(RT_BUILD_DIR_$(1)_$(2))/%.o) \ + $$(RUNTIME_LL_$(1)_$(2):rt/%.ll=$$(RT_BUILD_DIR_$(1)_$(2))/%.o) + ALL_OBJ_FILES += $$(RUNTIME_OBJS_$(1)_$(2)) MORESTACK_OBJS_$(1)_$(2) := $$(RT_BUILD_DIR_$(1)_$(2))/arch/$$(HOST_$(1))/morestack.o ALL_OBJ_FILES += $$(MORESTACK_OBJS_$(1)_$(2)) -$$(RT_BUILD_DIR_$(1)_$(2))/rust_cxx_glue.o: rt/rust_cxx_glue.cpp $$(MKFILE_DEPS) - @$$(call E, compile: $$@) - $$(Q)$$(call CFG_COMPILE_CXX_$(1), $$@, $$(RUNTIME_INCS_$(1)_$(2)) \ - $$(SNAP_DEFINES) $$(RUNTIME_CXXFLAGS_$(1)_$(2))) $$< +LLVM_LLC = $(CFG_LLVM_INST_DIR_$(CFG_BUILD))/bin/llc -filetype=obj -o $$1 $$2 $$(RT_BUILD_DIR_$(1)_$(2))/%.o: rt/%.c $$(MKFILE_DEPS) @$$(call E, compile: $$@) @@ -117,6 +116,11 @@ $$(RT_BUILD_DIR_$(1)_$(2))/%.o: rt/%.S $$(MKFILE_DEPS) \ @$$(call E, compile: $$@) $$(Q)$$(call CFG_ASSEMBLE_$(1),$$@,$$<) +$$(RT_BUILD_DIR_$(1)_$(2))/%.o: rt/%.ll $$(MKFILE_DEPS) \ + $$(LLVM_CONFIG_$$(CFG_BUILD)) + @$$(call E, compile: $$@) + $$(Q)$$(call LLVM_LLC,$$@,$$<) + $$(RT_BUILD_DIR_$(1)_$(2))/arch/$$(HOST_$(1))/libmorestack.a: $$(MORESTACK_OBJS_$(1)_$(2)) @$$(call E, link: $$@) $$(Q)$(AR_$(1)) rcs $$@ $$^ diff --git a/src/etc/mklldeps.py b/src/etc/mklldeps.py index 3b2cef3c9e1cb..5dd8145a9527d 100644 --- a/src/etc/mklldeps.py +++ b/src/etc/mklldeps.py @@ -59,6 +59,13 @@ for lib in out.strip().split(' '): lib = lib[2:] # chop of the leading '-l' f.write("#[link(name = \"" + lib + "\", kind = \"static\")]\n") + + # LLVM depends on C++ runtime, so we link it in (statically). + # Because our linker driver is gcc, not g++, it won't understand the -static-libstdc++ option, + # so we need to pass "-Bstatic -lstdc++ -Bdynamic" directly to ld. + f.write("#[link_args = \"-Xlinker -Bstatic -Xlinker -lstdc++ -Xlinker -Bdynamic\"]\n") + if os == 'win32': f.write("#[link(name = \"imagehlp\")]\n") + f.write("extern {}\n") diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index 6647a21e26a66..4f5b8ce0cbfe8 100644 --- a/src/librustc/back/link.rs +++ b/src/librustc/back/link.rs @@ -702,10 +702,7 @@ pub fn get_cc_prog(sess: Session) -> ~str { // In the future, FreeBSD will use clang as default compiler. // It would be flexible to use cc (system's default C compiler) // instead of hard-coded gcc. - // For win32, there is no cc command, so we add a condition to make it use - // g++. We use g++ rather than gcc because it automatically adds linker - // options required for generation of dll modules that correctly register - // stack unwind tables. + // For win32, there is no cc command, so we add a condition to make it use gcc. match sess.targ_cfg.os { abi::OsAndroid => match sess.opts.android_cross_path { Some(ref path) => format!("{}/bin/arm-linux-androideabi-gcc", *path), @@ -714,7 +711,7 @@ pub fn get_cc_prog(sess: Session) -> ~str { (--android-cross-path)") } }, - abi::OsWin32 => ~"g++", + abi::OsWin32 => ~"gcc", _ => ~"cc", } } @@ -1023,6 +1020,12 @@ fn link_args(sess: Session, } } + if sess.targ_cfg.os == abi::OsWin32 { + // Make sure that we link to the dynamic libgcc, otherwise cross-module + // DWARF stack unwinding will not work. + args.push(~"-shared-libgcc"); + } + add_local_native_libraries(&mut args, sess); add_upstream_rust_crates(&mut args, sess, dylib, tmpdir); add_upstream_native_libraries(&mut args, sess); diff --git a/src/librustc/back/upcall.rs b/src/librustc/back/upcall.rs deleted file mode 100644 index 730ceba12c786..0000000000000 --- a/src/librustc/back/upcall.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - - -use driver::session; -use middle::trans::base; -use middle::trans::type_::Type; -use lib::llvm::{ModuleRef, ValueRef}; - -pub struct Upcalls { - rust_personality: ValueRef, -} - -macro_rules! upcall ( - (nothrow fn $name:ident -> $ret:expr) => ({ - let fn_ty = Type::func([], &$ret); - let decl = base::decl_cdecl_fn(llmod, ~"upcall_" + stringify!($name), fn_ty); - base::set_no_unwind(decl); - decl - }) -) - -pub fn declare_upcalls(_targ_cfg: @session::config, - llmod: ModuleRef) -> @Upcalls { - @Upcalls { - rust_personality: upcall!(nothrow fn rust_personality -> Type::i32()), - } -} diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 49288e35f4476..64e0b72ee7d7f 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -16,7 +16,7 @@ #[crate_type = "dylib"]; #[crate_type = "rlib"]; -#[feature(macro_rules, globs, struct_variant, managed_boxes)]; +#[feature(macro_rules, globs, struct_variant, managed_boxes, link_args)]; extern mod extra; extern mod syntax; @@ -92,7 +92,6 @@ pub mod back { pub mod link; pub mod manifest; pub mod abi; - pub mod upcall; pub mod arm; pub mod mips; pub mod x86; diff --git a/src/librustc/lib/llvm.rs b/src/librustc/lib/llvm.rs index 7db4a06e6c02b..754b5c8fb0807 100644 --- a/src/librustc/lib/llvm.rs +++ b/src/librustc/lib/llvm.rs @@ -319,7 +319,6 @@ pub mod llvm { // automatically updated whenever LLVM is updated to include an up-to-date // set of the libraries we need to link to LLVM for. #[link(name = "rustllvm", kind = "static")] - #[link(name = "stdc++")] extern { /* Create and destroy contexts. */ pub fn LLVMContextCreate() -> ContextRef; diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 97b6d10e05d0a..6fc077876a70d 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -208,7 +208,7 @@ pub fn collect_language_items(crate: &ast::Crate, } lets_do_this! { - There are 42 lang items. + There are 43 lang items. // ID, Variant name, Name, Method name; 0, FreezeTraitLangItem, "freeze", freeze_trait; @@ -261,5 +261,7 @@ lets_do_this! { 40, EventLoopFactoryLangItem, "event_loop_factory", event_loop_factory; 41, TypeIdLangItem, "type_id", type_id; + + 42, EhPersonalityLangItem, "eh_personality", eh_personality_fn; } diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 7dbe760e0277d..47b7c6a9c3188 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -37,6 +37,7 @@ use metadata::{csearch, cstore, encoder}; use middle::astencode; use middle::lang_items::{LangItem, ExchangeMallocFnLangItem, StartFnLangItem}; use middle::lang_items::{MallocFnLangItem, ClosureExchangeMallocFnLangItem}; +use middle::lang_items::{EhPersonalityLangItem}; use middle::trans::_match; use middle::trans::adt; use middle::trans::base; @@ -1028,10 +1029,10 @@ pub fn get_landing_pad(bcx: @mut Block) -> BasicBlockRef { // this represents but it's determined by the personality function and // this is what the EH proposal example uses. let llretty = Type::struct_([Type::i8p(), Type::i32()], false); - // The exception handling personality function. This is the C++ - // personality function __gxx_personality_v0, wrapped in our naming - // convention. - let personality = bcx.ccx().upcalls.rust_personality; + // The exception handling personality function. + let personality = callee::trans_fn_ref(bcx, + langcall(bcx, None, "", EhPersonalityLangItem), + 0).llfn; // The only landing pad clause will be 'cleanup' let llretval = LandingPad(pad_bcx, llretty, personality, 1u); // The landing pad block is a cleanup @@ -3197,6 +3198,7 @@ pub fn trans_crate(sess: session::Session, reachable.push(ccx.crate_map_name.to_owned()); reachable.push(~"main"); reachable.push(~"rust_stack_exhausted"); + reachable.push(~"eh_rust_personality_catch"); // referenced from rt/rust_try.ll return CrateTranslation { context: llcx, diff --git a/src/librustc/middle/trans/context.rs b/src/librustc/middle/trans/context.rs index 167b17ba95c6c..aca267023c7e9 100644 --- a/src/librustc/middle/trans/context.rs +++ b/src/librustc/middle/trans/context.rs @@ -9,7 +9,6 @@ // except according to those terms. -use back::{upcall}; use driver::session; use lib::llvm::{ContextRef, ModuleRef, ValueRef}; use lib::llvm::{llvm, TargetData, TypeNames}; @@ -105,7 +104,6 @@ pub struct CrateContext { tcx: ty::ctxt, maps: astencode::Maps, stats: @mut Stats, - upcalls: @upcall::Upcalls, tydesc_type: Type, int_type: Type, opaque_vec_type: Type, @@ -233,7 +231,6 @@ impl CrateContext { llvm_insns: HashMap::new(), fn_stats: ~[] }, - upcalls: upcall::declare_upcalls(targ_cfg, llmod), tydesc_type: tydesc_type, int_type: int_type, opaque_vec_type: opaque_vec_type, diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index 5d2179e8b9693..df1ebeb6407aa 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -173,6 +173,9 @@ mod local_ptr; /// Bindings to pthread/windows thread-local storage. mod thread_local_storage; +/// Stack unwinding +pub mod unwind; + /// Just stuff mod util; diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index 3299caa089aba..30e05e9091f3e 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -18,10 +18,9 @@ use super::local_heap::LocalHeap; use prelude::*; use borrow; -use cast::transmute; use cleanup; use io::Writer; -use libc::{c_void, uintptr_t, c_char, size_t}; +use libc::{c_char, size_t}; use local_data; use option::{Option, Some, None}; use rt::borrowck::BorrowRecord; @@ -33,8 +32,8 @@ use rt::local::Local; use rt::logging::StdErrLogger; use rt::sched::{Scheduler, SchedHandle}; use rt::stack::{StackSegment, StackPool}; +use rt::unwind::Unwinder; use send_str::SendStr; -use task::TaskResult; use unstable::finally::Finally; use unstable::mutex::Mutex; @@ -91,21 +90,6 @@ pub enum SchedHome { pub struct GarbageCollector; pub struct LocalStorage(Option); -pub struct Unwinder { - unwinding: bool, - cause: Option<~Any> -} - -impl Unwinder { - fn result(&mut self) -> TaskResult { - if self.unwinding { - Err(self.cause.take().unwrap()) - } else { - Ok(()) - } - } -} - impl Task { // A helper to build a new task using the dynamically found @@ -452,54 +436,6 @@ impl Coroutine { } - -// Just a sanity check to make sure we are catching a Rust-thrown exception -static UNWIND_TOKEN: uintptr_t = 839147; - -impl Unwinder { - pub fn try(&mut self, f: ||) { - use unstable::raw::Closure; - - unsafe { - let closure: Closure = transmute(f); - let code = transmute(closure.code); - let env = transmute(closure.env); - - let token = rust_try(try_fn, code, env); - assert!(token == 0 || token == UNWIND_TOKEN); - } - - extern fn try_fn(code: *c_void, env: *c_void) { - unsafe { - let closure: Closure = Closure { - code: transmute(code), - env: transmute(env), - }; - let closure: || = transmute(closure); - closure(); - } - } - - extern { - fn rust_try(f: extern "C" fn(*c_void, *c_void), - code: *c_void, - data: *c_void) -> uintptr_t; - } - } - - pub fn begin_unwind(&mut self, cause: ~Any) -> ! { - self.unwinding = true; - self.cause = Some(cause); - unsafe { - rust_begin_unwind(UNWIND_TOKEN); - return transmute(()); - } - extern { - fn rust_begin_unwind(token: uintptr_t); - } - } -} - /// This function is invoked from rust's current __morestack function. Segmented /// stacks are currently not enabled as segmented stacks, but rather one giant /// stack segment. This means that whenever we run out of stack, we want to diff --git a/src/libstd/rt/unwind.rs b/src/libstd/rt/unwind.rs new file mode 100644 index 0000000000000..767144b70655a --- /dev/null +++ b/src/libstd/rt/unwind.rs @@ -0,0 +1,199 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::*; +use cast::transmute; +use task::TaskResult; +use libc::{abort, c_void, c_int}; +use self::libunwind::*; + +mod libunwind { + //! Unwind library interface + + #[allow(non_camel_case_types)]; + + use libc::{uintptr_t, uint64_t}; + + #[repr(C)] + pub enum _Unwind_Reason_Code { + _URC_NO_REASON = 0, + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, + _URC_FATAL_PHASE2_ERROR = 2, + _URC_FATAL_PHASE1_ERROR = 3, + _URC_NORMAL_STOP = 4, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8, + } + + #[repr(C)] + pub enum _Unwind_Action + { + _UA_SEARCH_PHASE = 1, + _UA_CLEANUP_PHASE = 2, + _UA_HANDLER_FRAME = 4, + _UA_FORCE_UNWIND = 8, + _UA_END_OF_STACK = 16, + } + + pub type _Unwind_Exception_Class = uint64_t; + + pub type _Unwind_Word = uintptr_t; + + pub struct _Unwind_Exception { + exception_class: _Unwind_Exception_Class, + exception_cleanup: _Unwind_Exception_Cleanup_Fn, + private_1: _Unwind_Word, + private_2: _Unwind_Word, + } + + pub enum _Unwind_Context {} + + pub type _Unwind_Exception_Cleanup_Fn = extern "C" fn(unwind_code: _Unwind_Reason_Code, + exception: *_Unwind_Exception); + + extern "C" { + pub fn _Unwind_RaiseException(exception: *_Unwind_Exception) -> _Unwind_Reason_Code; + pub fn _Unwind_DeleteException(exception: *_Unwind_Exception); + } +} + +pub struct Unwinder { + unwinding: bool, + cause: Option<~Any> +} + +impl Unwinder { + + pub fn try(&mut self, f: ||) { + use unstable::raw::Closure; + + unsafe { + let closure: Closure = transmute(f); + let code = transmute(closure.code); + let env = transmute(closure.env); + + let ep = rust_try(try_fn, code, env); + if !ep.is_null() { + rtdebug!("Caught {}", (*ep).exception_class); + _Unwind_DeleteException(ep); + } + } + + extern fn try_fn(code: *c_void, env: *c_void) { + unsafe { + let closure: Closure = Closure { + code: transmute(code), + env: transmute(env), + }; + let closure: || = transmute(closure); + closure(); + } + } + + extern { + fn rust_try(f: extern "C" fn(*c_void, *c_void), + code: *c_void, + data: *c_void) -> *_Unwind_Exception; + } + } + + pub fn begin_unwind(&mut self, cause: ~Any) -> ! { + rtdebug!("begin_unwind()"); + + self.unwinding = true; + self.cause = Some(cause); + + unsafe { + let exception = ~_Unwind_Exception { + exception_class: rust_exception_class(), + exception_cleanup: exception_cleanup, + private_1: 0, + private_2: 0 + }; + let error = _Unwind_RaiseException(transmute(exception)); + error!("Could not unwind stack, error = {}", error as int) + abort(); + } + + extern "C" fn exception_cleanup(_unwind_code: _Unwind_Reason_Code, + exception: *_Unwind_Exception) { + rtdebug!("exception_cleanup()"); + unsafe { + let _: ~_Unwind_Exception = transmute(exception); + } + } + } + + pub fn result(&mut self) -> TaskResult { + if self.unwinding { + Err(self.cause.take().unwrap()) + } else { + Ok(()) + } + } +} + +// We could implement the personality routine in pure Rust, however, __gcc_personality_v0 +// (the "C" personality) already does everything we need in terms of invoking cleanup +// landing pads. It also handles some system-dependent corner cases, which is nice. +// The only thing it doesn't do, is exception catching, but we can fix that (see below). +extern "C" { + fn __gcc_personality_v0(version: c_int, + actions: _Unwind_Action, + exception_class: _Unwind_Exception_Class, + ue_header: *_Unwind_Exception, + context: *_Unwind_Context) -> _Unwind_Reason_Code; +} + +// This variant is referenced from all cleanup landing pads +#[lang="eh_personality"] +#[doc(hidden)] +#[cfg(not(test))] +pub extern "C" fn eh_rust_personality(version: c_int, + actions: _Unwind_Action, + exception_class: _Unwind_Exception_Class, + ue_header: *_Unwind_Exception, + context: *_Unwind_Context) -> _Unwind_Reason_Code { + unsafe { + __gcc_personality_v0(version, actions, exception_class, ue_header, context) + } +} + +// And this one is used in one place only: rust_try() (in rt/rust_try.ll). +#[no_mangle] +#[doc(hidden)] +#[cfg(not(test))] +pub extern "C" fn eh_rust_personality_catch(version: c_int, + actions: _Unwind_Action, + exception_class: _Unwind_Exception_Class, + ue_header: *_Unwind_Exception, + context: *_Unwind_Context) -> _Unwind_Reason_Code { + if (actions as c_int & _UA_SEARCH_PHASE as c_int) != 0 { + // Always catch + _URC_HANDLER_FOUND + } + else { + // In cleanup phase delegate to __gcc_personality_v0 to install the landing pad context + unsafe { + __gcc_personality_v0(version, actions, exception_class, ue_header, context) + } + } +} + +// can this be made a static var somehow? +fn rust_exception_class() -> _Unwind_Exception_Class { + let bytes = bytes!("MOZ\0RUST"); + unsafe { + let ptr: *_Unwind_Exception_Class = transmute(bytes.as_ptr()); + *ptr + } +} diff --git a/src/rt/rust_cxx_glue.cpp b/src/rt/rust_cxx_glue.cpp deleted file mode 100644 index b44d29642c4b7..0000000000000 --- a/src/rt/rust_cxx_glue.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -/* Foreign builtins which require C++ */ - -#include "rust_globals.h" - -typedef void *(rust_try_fn)(void*, void*); - -extern "C" CDECL uintptr_t -rust_try(rust_try_fn f, void *fptr, void *env) { - try { - f(fptr, env); - } catch (uintptr_t token) { - assert(token != 0); - return token; - } - return 0; -} - -extern "C" CDECL void -rust_begin_unwind(uintptr_t token) { - throw token; -} diff --git a/src/rt/rust_try.ll b/src/rt/rust_try.ll new file mode 100644 index 0000000000000..ae28b08bcca68 --- /dev/null +++ b/src/rt/rust_try.ll @@ -0,0 +1,19 @@ +declare i32 @eh_rust_personality_catch(...) + +define i8* @rust_try(void (i8*,i8*)* %f, i8* %fptr, i8* %env) { + + invoke void %f(i8* %fptr, i8* %env) + to label %normal + unwind label %catch + +normal: + ret i8* null + +catch: + %1 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @eh_rust_personality_catch to i8*) + catch i8* null + + ; return pointer to the exception object + %2 = extractvalue { i8*, i32 } %1, 0 + ret i8* %2 +}