diff --git a/mk/rt.mk b/mk/rt.mk index c70f9e8a37add..69277e774e43b 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -54,15 +54,6 @@ NATIVE_DEPS_miniz_$(1) = miniz.c NATIVE_DEPS_rust_builtin_$(1) := rust_builtin.c \ rust_android_dummy.c NATIVE_DEPS_rustrt_native_$(1) := arch/$$(HOST_$(1))/record_sp.S -ifeq ($$(findstring msvc,$(1)),msvc) -ifeq ($$(findstring i686,$(1)),i686) -NATIVE_DEPS_rustrt_native_$(1) += rust_try_msvc_32.ll -else -NATIVE_DEPS_rustrt_native_$(1) += rust_try_msvc_64.ll -endif -else -NATIVE_DEPS_rustrt_native_$(1) += rust_try.ll -endif NATIVE_DEPS_rust_test_helpers_$(1) := rust_test_helpers.c NATIVE_DEPS_morestack_$(1) := arch/$$(HOST_$(1))/morestack.S @@ -76,14 +67,6 @@ NATIVE_DEPS_morestack_$(1) := arch/$$(HOST_$(1))/morestack.S RT_OUTPUT_DIR_$(1) := $(1)/rt -$$(RT_OUTPUT_DIR_$(1))/%.o: $(S)src/rt/%.ll $$(MKFILE_DEPS) \ - $$(LLVM_CONFIG_$$(CFG_BUILD)) - @mkdir -p $$(@D) - @$$(call E, compile: $$@) - $$(Q)$$(LLC_$$(CFG_BUILD)) $$(CFG_LLC_FLAGS_$(1)) \ - -filetype=obj -mtriple=$$(CFG_LLVM_TARGET_$(1)) \ - -relocation-model=pic -o $$@ $$< - $$(RT_OUTPUT_DIR_$(1))/%.o: $(S)src/rt/%.c $$(MKFILE_DEPS) @mkdir -p $$(@D) @$$(call E, compile: $$@) @@ -122,7 +105,6 @@ define THIRD_PARTY_LIB OBJS_$(2)_$(1) := $$(NATIVE_DEPS_$(2)_$(1):%=$$(RT_OUTPUT_DIR_$(1))/%) OBJS_$(2)_$(1) := $$(OBJS_$(2)_$(1):.c=.o) OBJS_$(2)_$(1) := $$(OBJS_$(2)_$(1):.cpp=.o) -OBJS_$(2)_$(1) := $$(OBJS_$(2)_$(1):.ll=.o) OBJS_$(2)_$(1) := $$(OBJS_$(2)_$(1):.S=.o) NATIVE_$(2)_$(1) := $$(call CFG_STATIC_LIB_NAME_$(1),$(2)) $$(RT_OUTPUT_DIR_$(1))/$$(NATIVE_$(2)_$(1)): $$(OBJS_$(2)_$(1)) diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 74901553149ab..ef022179772c4 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -602,4 +602,10 @@ extern "rust-intrinsic" { /// Returns the value of the discriminant for the variant in 'v', /// cast to a `u64`; if `T` has no discriminant, returns 0. pub fn discriminant_value(v: &T) -> u64; + + /// Rust's "try catch" construct which invokes the function pointer `f` with + /// the data pointer `data`, returning the exception payload if an exception + /// is thrown (aka the thread panics). + #[cfg(not(stage0))] + pub fn try(f: fn(*mut u8), data: *mut u8) -> *mut u8; } diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index cf528e0c8a914..f7cd94f30af12 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -326,6 +326,8 @@ lets_do_this! { StartFnLangItem, "start", start_fn; EhPersonalityLangItem, "eh_personality", eh_personality; + EhPersonalityCatchLangItem, "eh_personality_catch", eh_personality_catch; + MSVCTryFilterLangItem, "msvc_try_filter", msvc_try_filter; ExchangeHeapLangItem, "exchange_heap", exchange_heap; OwnedBoxLangItem, "owned_box", owned_box; diff --git a/src/librustc/middle/weak_lang_items.rs b/src/librustc/middle/weak_lang_items.rs index 60a9ffc7d2e13..72fda9a7ae06a 100644 --- a/src/librustc/middle/weak_lang_items.rs +++ b/src/librustc/middle/weak_lang_items.rs @@ -119,7 +119,7 @@ impl<'a, 'v> Visitor<'v> for Context<'a> { ) } weak_lang_items! { - panic_fmt, PanicFmtLangItem, rust_begin_unwind; + panic_fmt, PanicFmtLangItem, rust_begin_unwind; stack_exhausted, StackExhaustedLangItem, rust_stack_exhausted; eh_personality, EhPersonalityLangItem, rust_eh_personality; } diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 7734704b021a8..83f8619c5eeab 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -134,7 +134,7 @@ pub enum DLLStorageClassTypes { } bitflags! { - flags Attribute : u32 { + flags Attribute : u64 { const ZExt = 1 << 0, const SExt = 1 << 1, const NoReturn = 1 << 2, @@ -161,6 +161,7 @@ bitflags! { const ReturnsTwice = 1 << 29, const UWTable = 1 << 30, const NonLazyBind = 1 << 31, + const OptimizeNone = 1 << 42, } } @@ -2193,7 +2194,8 @@ pub fn ConstFCmp(pred: RealPredicate, v1: ValueRef, v2: ValueRef) -> ValueRef { pub fn SetFunctionAttribute(fn_: ValueRef, attr: Attribute) { unsafe { - LLVMAddFunctionAttribute(fn_, FunctionIndex as c_uint, attr.bits() as uint64_t) + LLVMAddFunctionAttribute(fn_, FunctionIndex as c_uint, + attr.bits() as uint64_t) } } diff --git a/src/librustc_trans/trans/build.rs b/src/librustc_trans/trans/build.rs index 3e4452a23b9f7..5a3fcc8d27f3c 100644 --- a/src/librustc_trans/trans/build.rs +++ b/src/librustc_trans/trans/build.rs @@ -1042,6 +1042,10 @@ pub fn LandingPad(cx: Block, ty: Type, pers_fn: ValueRef, B(cx).landing_pad(ty, pers_fn, num_clauses, cx.fcx.llfn) } +pub fn AddClause(cx: Block, landing_pad: ValueRef, clause: ValueRef) { + B(cx).add_clause(landing_pad, clause) +} + pub fn SetCleanup(cx: Block, landing_pad: ValueRef) { B(cx).set_cleanup(landing_pad) } diff --git a/src/librustc_trans/trans/builder.rs b/src/librustc_trans/trans/builder.rs index e39fc18dc7bf1..107ae378ac446 100644 --- a/src/librustc_trans/trans/builder.rs +++ b/src/librustc_trans/trans/builder.rs @@ -937,6 +937,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } + pub fn add_clause(&self, landing_pad: ValueRef, clause: ValueRef) { + unsafe { + llvm::LLVMAddClause(landing_pad, clause); + } + } + pub fn set_cleanup(&self, landing_pad: ValueRef) { self.count_insn("setcleanup"); unsafe { diff --git a/src/librustc_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs index debc8dd59c04c..7900000d3a9df 100644 --- a/src/librustc_trans/trans/callee.rs +++ b/src/librustc_trans/trans/callee.rs @@ -620,16 +620,17 @@ pub fn trans_lang_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, }, ArgVals(args), dest) } -/// This behemoth of a function translates function calls. Unfortunately, in order to generate more -/// efficient LLVM output at -O0, it has quite a complex signature (refactoring this into two -/// functions seems like a good idea). +/// This behemoth of a function translates function calls. Unfortunately, in +/// order to generate more efficient LLVM output at -O0, it has quite a complex +/// signature (refactoring this into two functions seems like a good idea). /// -/// In particular, for lang items, it is invoked with a dest of None, and in that case the return -/// value contains the result of the fn. The lang item must not return a structural type or else -/// all heck breaks loose. +/// In particular, for lang items, it is invoked with a dest of None, and in +/// that case the return value contains the result of the fn. The lang item must +/// not return a structural type or else all heck breaks loose. /// -/// For non-lang items, `dest` is always Some, and hence the result is written into memory -/// somewhere. Nonetheless we return the actual return value of the function. +/// For non-lang items, `dest` is always Some, and hence the result is written +/// into memory somewhere. Nonetheless we return the actual return value of the +/// function. pub fn trans_call_inner<'a, 'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>, debug_loc: DebugLoc, get_callee: F, diff --git a/src/librustc_trans/trans/cleanup.rs b/src/librustc_trans/trans/cleanup.rs index 1891320313a85..37722d5a549fe 100644 --- a/src/librustc_trans/trans/cleanup.rs +++ b/src/librustc_trans/trans/cleanup.rs @@ -122,11 +122,9 @@ pub use self::Heap::*; use llvm::{BasicBlockRef, ValueRef}; use trans::base; use trans::build; -use trans::callee; use trans::common; -use trans::common::{Block, FunctionContext, ExprId, NodeIdAndSpan}; +use trans::common::{Block, FunctionContext, NodeIdAndSpan}; use trans::debuginfo::{DebugLoc, ToDebugLoc}; -use trans::declare; use trans::glue; use middle::region; use trans::type_::Type; @@ -833,53 +831,7 @@ impl<'blk, 'tcx> CleanupHelperMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx &[Type::i8p(self.ccx), Type::i32(self.ccx)], false); - // The exception handling personality function. - // - // If our compilation unit has the `eh_personality` lang item somewhere - // within it, then we just need to translate that. Otherwise, we're - // building an rlib which will depend on some upstream implementation of - // this function, so we just codegen a generic reference to it. We don't - // specify any of the types for the function, we just make it a symbol - // that LLVM can later use. - // - // Note that MSVC is a little special here in that we don't use the - // `eh_personality` lang item at all. Currently LLVM has support for - // both Dwarf and SEH unwind mechanisms for MSVC targets and uses the - // *name of the personality function* to decide what kind of unwind side - // tables/landing pads to emit. It looks like Dwarf is used by default, - // injecting a dependency on the `_Unwind_Resume` symbol for resuming - // an "exception", but for MSVC we want to force SEH. This means that we - // can't actually have the personality function be our standard - // `rust_eh_personality` function, but rather we wired it up to the - // CRT's custom personality function, which forces LLVM to consider - // landing pads as "landing pads for SEH". - let target = &self.ccx.sess().target.target; - let llpersonality = match pad_bcx.tcx().lang_items.eh_personality() { - Some(def_id) if !target.options.is_like_msvc => { - callee::trans_fn_ref(pad_bcx.ccx(), def_id, ExprId(0), - pad_bcx.fcx.param_substs).val - } - _ => { - let mut personality = self.ccx.eh_personality().borrow_mut(); - match *personality { - Some(llpersonality) => llpersonality, - None => { - let name = if !target.options.is_like_msvc { - "rust_eh_personality" - } else if target.arch == "x86" { - "_except_handler3" - } else { - "__C_specific_handler" - }; - let fty = Type::variadic_func(&[], &Type::i32(self.ccx)); - let f = declare::declare_cfn(self.ccx, name, fty, - self.ccx.tcx().types.i32); - *personality = Some(f); - f - } - } - } - }; + let llpersonality = pad_bcx.fcx.eh_personality(); // The only landing pad clause will be 'cleanup' let llretval = build::LandingPad(pad_bcx, llretty, llpersonality, 1); diff --git a/src/librustc_trans/trans/closure.rs b/src/librustc_trans/trans/closure.rs index d813e9dbf40fa..f00029ec2ff93 100644 --- a/src/librustc_trans/trans/closure.rs +++ b/src/librustc_trans/trans/closure.rs @@ -163,11 +163,10 @@ pub fn get_or_create_declaration_if_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tc mangle_internal_name_by_path_and_seq(path, "closure") }); - // Currently there’s only a single user of get_or_create_declaration_if_closure and it - // unconditionally defines the function, therefore we use define_* here. - let llfn = declare::define_internal_rust_fn(ccx, &symbol[..], function_type).unwrap_or_else(||{ - ccx.sess().bug(&format!("symbol `{}` already defined", symbol)); - }); + // Currently there’s only a single user of + // get_or_create_declaration_if_closure and it unconditionally defines the + // function, therefore we use define_* here. + let llfn = declare::define_internal_rust_fn(ccx, &symbol[..], function_type); // set an inline hint for all closures attributes::inline(llfn, attributes::InlineAttr::Hint); @@ -388,11 +387,8 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( // Create the by-value helper. let function_name = link::mangle_internal_name_by_type_and_seq(ccx, llonce_fn_ty, "once_shim"); - let lloncefn = declare::define_internal_rust_fn(ccx, &function_name[..], llonce_fn_ty) - .unwrap_or_else(||{ - ccx.sess().bug(&format!("symbol `{}` already defined", function_name)); - }); - + let lloncefn = declare::define_internal_rust_fn(ccx, &function_name, + llonce_fn_ty); let sig = tcx.erase_late_bound_regions(&llonce_bare_fn_ty.sig); let (block_arena, fcx): (TypedArena<_>, FunctionContext); block_arena = TypedArena::new(); diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index d7d3be699cb90..1e87053c2ae63 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -25,6 +25,7 @@ use middle::lang_items::LangItem; use middle::subst::{self, Substs}; use trans::base; use trans::build; +use trans::callee; use trans::cleanup; use trans::consts; use trans::datum; @@ -479,6 +480,56 @@ impl<'a, 'tcx> FunctionContext<'a, 'tcx> { pub fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool { type_needs_drop_given_env(self.ccx.tcx(), ty, &self.param_env) } + + pub fn eh_personality(&self) -> ValueRef { + // The exception handling personality function. + // + // If our compilation unit has the `eh_personality` lang item somewhere + // within it, then we just need to translate that. Otherwise, we're + // building an rlib which will depend on some upstream implementation of + // this function, so we just codegen a generic reference to it. We don't + // specify any of the types for the function, we just make it a symbol + // that LLVM can later use. + // + // Note that MSVC is a little special here in that we don't use the + // `eh_personality` lang item at all. Currently LLVM has support for + // both Dwarf and SEH unwind mechanisms for MSVC targets and uses the + // *name of the personality function* to decide what kind of unwind side + // tables/landing pads to emit. It looks like Dwarf is used by default, + // injecting a dependency on the `_Unwind_Resume` symbol for resuming + // an "exception", but for MSVC we want to force SEH. This means that we + // can't actually have the personality function be our standard + // `rust_eh_personality` function, but rather we wired it up to the + // CRT's custom personality function, which forces LLVM to consider + // landing pads as "landing pads for SEH". + let target = &self.ccx.sess().target.target; + match self.ccx.tcx().lang_items.eh_personality() { + Some(def_id) if !target.options.is_like_msvc => { + callee::trans_fn_ref(self.ccx, def_id, ExprId(0), + self.param_substs).val + } + _ => { + let mut personality = self.ccx.eh_personality().borrow_mut(); + match *personality { + Some(llpersonality) => llpersonality, + None => { + let name = if !target.options.is_like_msvc { + "rust_eh_personality" + } else if target.arch == "x86" { + "_except_handler3" + } else { + "__C_specific_handler" + }; + let fty = Type::variadic_func(&[], &Type::i32(self.ccx)); + let f = declare::declare_cfn(self.ccx, name, fty, + self.ccx.tcx().types.i32); + *personality = Some(f); + f + } + } + } + } + } } // Basic block context. We create a block context for each basic block diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs index 5a4bd7ff3a184..760a4ae827aac 100644 --- a/src/librustc_trans/trans/context.rs +++ b/src/librustc_trans/trans/context.rs @@ -142,6 +142,7 @@ pub struct LocalCrateContext<'tcx> { dbg_cx: Option>, eh_personality: RefCell>, + rust_try_fn: RefCell>, intrinsics: RefCell>, @@ -461,6 +462,7 @@ impl<'tcx> LocalCrateContext<'tcx> { closure_vals: RefCell::new(FnvHashMap()), dbg_cx: dbg_cx, eh_personality: RefCell::new(None), + rust_try_fn: RefCell::new(None), intrinsics: RefCell::new(FnvHashMap()), n_llvm_insns: Cell::new(0), trait_cache: RefCell::new(FnvHashMap()), @@ -726,6 +728,10 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { &self.local.eh_personality } + pub fn rust_try_fn<'a>(&'a self) -> &'a RefCell> { + &self.local.rust_try_fn + } + fn intrinsics<'a>(&'a self) -> &'a RefCell> { &self.local.intrinsics } @@ -923,6 +929,7 @@ fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option void); ifn!("llvm.expect.i1", fn(i1, i1) -> i1); + ifn!("llvm.eh.typeid.for", fn(i8p) -> t_i32); // Some intrinsics were introduced in later versions of LLVM, but they have // fallbacks in libc or libm and such. diff --git a/src/librustc_trans/trans/declare.rs b/src/librustc_trans/trans/declare.rs index b29da9d560fea..c802de91e38b3 100644 --- a/src/librustc_trans/trans/declare.rs +++ b/src/librustc_trans/trans/declare.rs @@ -176,8 +176,8 @@ pub fn define_global(ccx: &CrateContext, name: &str, ty: Type) -> Option Option { +pub fn define_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv, + fn_type: Type, output: ty::FnOutput) -> Option { if get_defined_value(ccx, name).is_some() { None } else { @@ -224,20 +224,21 @@ pub fn define_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str, /// Declare a Rust function with an intention to define it. /// /// Use this function when you intend to define a function. This function will -/// return None if the name already has a definition associated with it. In that -/// case an error should be reported to the user, because it usually happens due -/// to user’s fault (e.g. misuse of #[no_mangle] or #[export_name] attributes). -pub fn define_internal_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str, - fn_type: ty::Ty<'tcx>) -> Option { +/// return panic if the name already has a definition associated with it. This +/// can happen with #[no_mangle] or #[export_name], for example. +pub fn define_internal_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + name: &str, + fn_type: ty::Ty<'tcx>) -> ValueRef { if get_defined_value(ccx, name).is_some() { - None + ccx.sess().fatal(&format!("symbol `{}` already defined", name)) } else { - Some(declare_internal_rust_fn(ccx, name, fn_type)) + declare_internal_rust_fn(ccx, name, fn_type) } } -/// Get defined or externally defined (AvailableExternally linkage) value by name. +/// Get defined or externally defined (AvailableExternally linkage) value by +/// name. fn get_defined_value(ccx: &CrateContext, name: &str) -> Option { debug!("get_defined_value(name={:?})", name); let namebuf = CString::new(name).unwrap_or_else(|_|{ diff --git a/src/librustc_trans/trans/foreign.rs b/src/librustc_trans/trans/foreign.rs index 9e8c0189a9762..e102e3cd062be 100644 --- a/src/librustc_trans/trans/foreign.rs +++ b/src/librustc_trans/trans/foreign.rs @@ -627,9 +627,7 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ccx.tcx().map.path_to_string(id), id, t); - let llfn = declare::define_internal_rust_fn(ccx, &ps[..], t).unwrap_or_else(||{ - ccx.sess().bug(&format!("symbol `{}` already defined", ps)); - }); + let llfn = declare::define_internal_rust_fn(ccx, &ps, t); attributes::from_fn_attrs(ccx, attrs, llfn); base::trans_fn(ccx, decl, body, llfn, param_substs, id, &[]); llfn diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs index b449c3ad060b8..e78218fd10dd8 100644 --- a/src/librustc_trans/trans/intrinsic.rs +++ b/src/librustc_trans/trans/intrinsic.rs @@ -10,6 +10,7 @@ #![allow(non_upper_case_globals)] +use arena::TypedArena; use llvm; use llvm::{SequentiallyConsistent, Acquire, Release, AtomicXchg, ValueRef, TypeKind}; use middle::subst; @@ -23,6 +24,7 @@ use trans::cleanup::CleanupMethods; use trans::common::*; use trans::datum::*; use trans::debuginfo::DebugLoc; +use trans::declare; use trans::expr; use trans::glue; use trans::type_of::*; @@ -31,7 +33,8 @@ use trans::machine; use trans::machine::llsize_of; use trans::type_::Type; use middle::ty::{self, Ty, HasTypeFlags}; -use syntax::abi::RustIntrinsic; +use middle::subst::Substs; +use syntax::abi::{self, RustIntrinsic}; use syntax::ast; use syntax::parse::token; @@ -302,6 +305,42 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, } } + let call_debug_location = DebugLoc::At(call_info.id, call_info.span); + + // For `try` we need some custom control flow + if &name[..] == "try" { + if let callee::ArgExprs(ref exprs) = args { + let (func, data) = if exprs.len() != 2 { + ccx.sess().bug("expected two exprs as arguments for \ + `try` intrinsic"); + } else { + (&exprs[0], &exprs[1]) + }; + + // translate arguments + let func = unpack_datum!(bcx, expr::trans(bcx, func)); + let func = unpack_datum!(bcx, func.to_rvalue_datum(bcx, "func")); + let data = unpack_datum!(bcx, expr::trans(bcx, data)); + let data = unpack_datum!(bcx, data.to_rvalue_datum(bcx, "data")); + + let dest = match dest { + expr::SaveIn(d) => d, + expr::Ignore => alloc_ty(bcx, tcx.mk_mut_ptr(tcx.types.i8), + "try_result"), + }; + + // do the invoke + bcx = try_intrinsic(bcx, func.val, data.val, dest, + call_debug_location); + + fcx.pop_and_trans_custom_cleanup_scope(bcx, cleanup_scope); + return Result::new(bcx, dest); + } else { + ccx.sess().bug("expected two exprs as arguments for \ + `try` intrinsic"); + } + } + // Push the arguments. let mut llargs = Vec::new(); bcx = callee::trans_args(bcx, @@ -314,8 +353,6 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, fcx.scopes.borrow_mut().last_mut().unwrap().drop_non_lifetime_clean(); - let call_debug_location = DebugLoc::At(call_info.id, call_info.span); - // These are the only intrinsic functions that diverge. if &name[..] == "abort" { let llfn = ccx.get_intrinsic(&("llvm.trap")); @@ -989,3 +1026,304 @@ fn with_overflow_intrinsic<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ret } } + +fn try_intrinsic<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + func: ValueRef, + data: ValueRef, + dest: ValueRef, + dloc: DebugLoc) -> Block<'blk, 'tcx> { + if bcx.sess().no_landing_pads() { + Call(bcx, func, &[data], None, dloc); + Store(bcx, C_null(Type::i8p(bcx.ccx())), dest); + bcx + } else if bcx.sess().target.target.options.is_like_msvc { + trans_msvc_try(bcx, func, data, dest, dloc) + } else { + trans_gnu_try(bcx, func, data, dest, dloc) + } +} + +// MSVC's definition of the `rust_try` function. The exact implementation here +// is a little different than the GNU (standard) version below, not only because +// of the personality function but also because of the other fiddly bits about +// SEH. LLVM also currently requires us to structure this a very particular way +// as explained below. +// +// Like with the GNU version we generate a shim wrapper +fn trans_msvc_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + func: ValueRef, + data: ValueRef, + dest: ValueRef, + dloc: DebugLoc) -> Block<'blk, 'tcx> { + let llfn = get_rust_try_fn(bcx.fcx, &mut |try_fn_ty, output| { + let ccx = bcx.ccx(); + let dloc = DebugLoc::None; + let rust_try = declare::define_internal_rust_fn(ccx, "__rust_try", + try_fn_ty); + let (fcx, block_arena); + block_arena = TypedArena::new(); + fcx = new_fn_ctxt(ccx, rust_try, ast::DUMMY_NODE_ID, false, + output, ccx.tcx().mk_substs(Substs::trans_empty()), + None, &block_arena); + let bcx = init_function(&fcx, true, output); + let then = fcx.new_temp_block("then"); + let catch = fcx.new_temp_block("catch"); + let catch_return = fcx.new_temp_block("catch-return"); + let catch_resume = fcx.new_temp_block("catch-resume"); + let personality = fcx.eh_personality(); + + let eh_typeid_for = ccx.get_intrinsic(&"llvm.eh.typeid.for"); + let rust_try_filter = match bcx.tcx().lang_items.msvc_try_filter() { + Some(did) => callee::trans_fn_ref(ccx, did, ExprId(0), + bcx.fcx.param_substs).val, + None => bcx.sess().bug("msvc_try_filter not defined"), + }; + + // Type indicator for the exception being thrown, not entirely sure + // what's going on here but it's what all the examples in LLVM use. + let lpad_ty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], + false); + + llvm::SetFunctionAttribute(rust_try, llvm::Attribute::NoInline); + llvm::SetFunctionAttribute(rust_try, llvm::Attribute::OptimizeNone); + let func = llvm::get_param(rust_try, 0); + let data = llvm::get_param(rust_try, 1); + + // Invoke the function, specifying our two temporary landing pads as the + // ext point. After the invoke we've terminated our basic block. + Invoke(bcx, func, &[data], then.llbb, catch.llbb, None, dloc); + + // All the magic happens in this landing pad, and this is basically the + // only landing pad in rust tagged with "catch" to indicate that we're + // catching an exception. The other catch handlers in the GNU version + // below just catch *all* exceptions, but that's because most exceptions + // are already filtered out by the gnu personality function. + // + // For MSVC we're just using a standard personality function that we + // can't customize (e.g. _except_handler3 or __C_specific_handler), so + // we need to do the exception filtering ourselves. This is currently + // performed by the `__rust_try_filter` function. This function, + // specified in the landingpad instruction, will be invoked by Windows + // SEH routines and will return whether the exception in question can be + // caught (aka the Rust runtime is the one that threw the exception). + // + // To get this to compile (currently LLVM segfaults if it's not in this + // particular structure), when the landingpad is executing we test to + // make sure that the ID of the exception being thrown is indeed the one + // that we were expecting. If it's not, we resume the exception, and + // otherwise we return the pointer that we got Full disclosure: It's not + // clear to me what this `llvm.eh.typeid` stuff is doing *other* then + // just allowing LLVM to compile this file without segfaulting. I would + // expect the entire landing pad to just be: + // + // %vals = landingpad ... + // %ehptr = extractvalue { i8*, i32 } %vals, 0 + // ret i8* %ehptr + // + // but apparently LLVM chokes on this, so we do the more complicated + // thing to placate it. + let vals = LandingPad(catch, lpad_ty, personality, 1); + let rust_try_filter = BitCast(catch, rust_try_filter, Type::i8p(ccx)); + AddClause(catch, vals, rust_try_filter); + let ehptr = ExtractValue(catch, vals, 0); + let sel = ExtractValue(catch, vals, 1); + let filter_sel = Call(catch, eh_typeid_for, &[rust_try_filter], None, + dloc); + let is_filter = ICmp(catch, llvm::IntEQ, sel, filter_sel, dloc); + CondBr(catch, is_filter, catch_return.llbb, catch_resume.llbb, dloc); + + // Our "catch-return" basic block is where we've determined that we + // actually need to catch this exception, in which case we just return + // the exception pointer. + Ret(catch_return, ehptr, dloc); + + // The "catch-resume" block is where we're running this landing pad but + // we actually need to not catch the exception, so just resume the + // exception to return. + Resume(catch_resume, vals); + + // On the successful branch we just return null. + Ret(then, C_null(Type::i8p(ccx)), dloc); + + return rust_try + }); + + // Note that no invoke is used here because by definition this function + // can't panic (that's what it's catching). + let ret = Call(bcx, llfn, &[func, data], None, dloc); + Store(bcx, ret, dest); + return bcx; +} + +// Definition of the standard "try" function for Rust using the GNU-like model +// of exceptions (e.g. the normal semantics of LLVM's landingpad and invoke +// instructions). +// +// This translation is a little surprising for two reasons: +// +// 1. We always call a shim function instead of inlining the call to `invoke` +// manually here. This is done because in LLVM we're only allowed to have one +// personality per function definition. The call to the `try` intrinsic is +// being inlined into the function calling it, and that function may already +// have other personality functions in play. By calling a shim we're +// guaranteed that our shim will have the right personality function. +// +// 2. Instead of making one shim (explained above), we make two shims! The +// reason for this has to do with the technical details about the +// implementation of unwinding in the runtime, but the tl;dr; is that the +// outer shim's personality function says "catch rust exceptions" and the +// inner shim's landing pad will not `resume` the exception being thrown. +// This means that the outer shim's landing pad is never run and the inner +// shim's return value is the return value of the whole call. +// +// The double-shim aspect is currently done for implementation ease on the +// runtime side of things, and more info can be found in +// src/libstd/rt/unwind/gcc.rs. +fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + func: ValueRef, + data: ValueRef, + dest: ValueRef, + dloc: DebugLoc) -> Block<'blk, 'tcx> { + let llfn = get_rust_try_fn(bcx.fcx, &mut |try_fn_ty, output| { + let ccx = bcx.ccx(); + let dloc = DebugLoc::None; + + // Type indicator for the exception being thrown, not entirely sure + // what's going on here but it's what all the examples in LLVM use. + let lpad_ty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], + false); + + // Define the "inner try" shim + let rust_try_inner = declare::define_internal_rust_fn(ccx, + "__rust_try_inner", + try_fn_ty); + trans_rust_try(ccx, rust_try_inner, lpad_ty, bcx.fcx.eh_personality(), + output, dloc, &mut |bcx, then, catch| { + let func = llvm::get_param(rust_try_inner, 0); + let data = llvm::get_param(rust_try_inner, 1); + Invoke(bcx, func, &[data], then.llbb, catch.llbb, None, dloc); + C_null(Type::i8p(ccx)) + }); + + // Define the "outer try" shim. + let rust_try = declare::define_internal_rust_fn(ccx, "__rust_try", + try_fn_ty); + let catch_pers = match bcx.tcx().lang_items.eh_personality_catch() { + Some(did) => callee::trans_fn_ref(ccx, did, ExprId(0), + bcx.fcx.param_substs).val, + None => bcx.tcx().sess.bug("eh_personality_catch not defined"), + }; + trans_rust_try(ccx, rust_try, lpad_ty, catch_pers, output, dloc, + &mut |bcx, then, catch| { + let func = llvm::get_param(rust_try, 0); + let data = llvm::get_param(rust_try, 1); + Invoke(bcx, rust_try_inner, &[func, data], then.llbb, catch.llbb, + None, dloc) + }); + return rust_try + }); + + // Note that no invoke is used here because by definition this function + // can't panic (that's what it's catching). + let ret = Call(bcx, llfn, &[func, data], None, dloc); + Store(bcx, ret, dest); + return bcx; + + // Translates both the inner and outer shims described above. The only + // difference between these two is the function invoked and the personality + // involved, so a common routine is shared. + // + // bcx: + // invoke %func(%args...) normal %normal unwind %unwind + // + // normal: + // ret null + // + // unwind: + // (ptr, _) = landingpad + // br (ptr != null), done, reraise + // + // done: + // ret ptr + // + // reraise: + // resume + // + // Note that the branch checking for `null` here isn't actually necessary, + // it's just an unfortunate hack to make sure that LLVM doesn't optimize too + // much. If this were not present, then LLVM would correctly deduce that our + // inner shim should be tagged with `nounwind` (as it catches all + // exceptions) and then the outer shim's `invoke` will be translated to just + // a simple call, destroying that entry for the personality function. + // + // To ensure that both shims always have an `invoke` this check against null + // confuses LLVM enough to the point that it won't infer `nounwind` and + // we'll proceed as normal. + fn trans_rust_try<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + llfn: ValueRef, + lpad_ty: Type, + personality: ValueRef, + output: ty::FnOutput<'tcx>, + dloc: DebugLoc, + invoke: &mut FnMut(Block, Block, Block) -> ValueRef) { + let (fcx, block_arena); + block_arena = TypedArena::new(); + fcx = new_fn_ctxt(ccx, llfn, ast::DUMMY_NODE_ID, false, + output, ccx.tcx().mk_substs(Substs::trans_empty()), + None, &block_arena); + let bcx = init_function(&fcx, true, output); + let then = bcx.fcx.new_temp_block("then"); + let catch = bcx.fcx.new_temp_block("catch"); + let reraise = bcx.fcx.new_temp_block("reraise"); + let catch_return = bcx.fcx.new_temp_block("catch-return"); + + let invoke_ret = invoke(bcx, then, catch); + Ret(then, invoke_ret, dloc); + let vals = LandingPad(catch, lpad_ty, personality, 1); + AddClause(catch, vals, C_null(Type::i8p(ccx))); + let ptr = ExtractValue(catch, vals, 0); + let valid = ICmp(catch, llvm::IntNE, ptr, C_null(Type::i8p(ccx)), dloc); + CondBr(catch, valid, catch_return.llbb, reraise.llbb, dloc); + Ret(catch_return, ptr, dloc); + Resume(reraise, vals); + } +} + +// Helper to generate the `Ty` associated with `rust_Try` +fn get_rust_try_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>, + f: &mut FnMut(Ty<'tcx>, + ty::FnOutput<'tcx>) -> ValueRef) + -> ValueRef { + let ccx = fcx.ccx; + if let Some(llfn) = *ccx.rust_try_fn().borrow() { + return llfn + } + + // Define the types up front for the signatures of the rust_try and + // rust_try_inner functions. + let tcx = ccx.tcx(); + let i8p = tcx.mk_mut_ptr(tcx.types.i8); + let fn_ty = tcx.mk_bare_fn(ty::BareFnTy { + unsafety: ast::Unsafety::Unsafe, + abi: abi::Rust, + sig: ty::Binder(ty::FnSig { + inputs: vec![i8p], + output: ty::FnOutput::FnConverging(tcx.mk_nil()), + variadic: false, + }), + }); + let fn_ty = tcx.mk_fn(None, fn_ty); + let output = ty::FnOutput::FnConverging(i8p); + let try_fn_ty = tcx.mk_bare_fn(ty::BareFnTy { + unsafety: ast::Unsafety::Unsafe, + abi: abi::Rust, + sig: ty::Binder(ty::FnSig { + inputs: vec![fn_ty, i8p], + output: output, + variadic: false, + }), + }); + let rust_try = f(tcx.mk_fn(None, try_fn_ty), output); + *ccx.rust_try_fn().borrow_mut() = Some(rust_try); + return rust_try +} diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs index 1fa996f76b9a2..8901361b27976 100644 --- a/src/librustc_trans/trans/meth.rs +++ b/src/librustc_trans/trans/meth.rs @@ -550,9 +550,7 @@ fn trans_object_shim<'a, 'tcx>( let shim_fn_ty = tcx.mk_fn(None, fty); let method_bare_fn_ty = tcx.mk_fn(None, method_ty); let function_name = link::mangle_internal_name_by_type_and_seq(ccx, shim_fn_ty, "object_shim"); - let llfn = declare::define_internal_rust_fn(ccx, &function_name, shim_fn_ty).unwrap_or_else(||{ - ccx.sess().bug(&format!("symbol `{}` already defined", function_name)); - }); + let llfn = declare::define_internal_rust_fn(ccx, &function_name, shim_fn_ty); let sig = ccx.tcx().erase_late_bound_regions(&fty.sig); diff --git a/src/librustc_trans/trans/monomorphize.rs b/src/librustc_trans/trans/monomorphize.rs index 98fe57ec31446..217181da1421a 100644 --- a/src/librustc_trans/trans/monomorphize.rs +++ b/src/librustc_trans/trans/monomorphize.rs @@ -137,10 +137,9 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let lldecl = if abi != abi::Rust { foreign::decl_rust_fn_with_foreign_abi(ccx, mono_ty, &s[..]) } else { - // FIXME(nagisa): perhaps needs a more fine grained selection? See setup_lldecl below. - declare::define_internal_rust_fn(ccx, &s[..], mono_ty).unwrap_or_else(||{ - ccx.sess().bug(&format!("symbol `{}` already defined", s)); - }) + // FIXME(nagisa): perhaps needs a more fine grained selection? See + // setup_lldecl below. + declare::define_internal_rust_fn(ccx, &s, mono_ty) }; ccx.monomorphized().borrow_mut().insert(hash_id.take().unwrap(), lldecl); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 9042cedccc857..17140db904f62 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -5096,6 +5096,21 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) { ty::BrAnon(0))), param(ccx, 0))], tcx.types.u64), + "try" => { + let mut_u8 = tcx.mk_mut_ptr(tcx.types.u8); + let fn_ty = ty::BareFnTy { + unsafety: ast::Unsafety::Normal, + abi: abi::Rust, + sig: ty::Binder(FnSig { + inputs: vec![mut_u8], + output: ty::FnOutput::FnConverging(tcx.mk_nil()), + variadic: false, + }), + }; + let fn_ty = tcx.mk_bare_fn(fn_ty); + (0, vec![tcx.mk_fn(None, fn_ty), mut_u8], mut_u8) + } + ref other => { span_err!(tcx.sess, it.span, E0093, "unrecognized intrinsic function: `{}`", *other); diff --git a/src/libstd/rt/unwind/gcc.rs b/src/libstd/rt/unwind/gcc.rs index 84c6d6864a9e5..87941e79b2f7d 100644 --- a/src/libstd/rt/unwind/gcc.rs +++ b/src/libstd/rt/unwind/gcc.rs @@ -8,10 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![allow(private_no_mangle_fns)] + use prelude::v1::*; use any::Any; -use libc::c_void; use rt::libunwind as uw; struct Exception { @@ -41,7 +42,7 @@ pub unsafe fn panic(data: Box) -> ! { } } -pub unsafe fn cleanup(ptr: *mut c_void) -> Box { +pub unsafe fn cleanup(ptr: *mut u8) -> Box { let my_ep = ptr as *mut Exception; rtdebug!("caught {}", (*my_ep).uwe.exception_class); let cause = (*my_ep).cause.take(); @@ -89,7 +90,7 @@ pub mod eabi { use rt::libunwind as uw; use libc::c_int; - extern "C" { + extern { fn __gcc_personality_v0(version: c_int, actions: uw::_Unwind_Action, exception_class: uw::_Unwind_Exception_Class, @@ -98,9 +99,8 @@ pub mod eabi { -> uw::_Unwind_Reason_Code; } - #[lang="eh_personality"] - #[no_mangle] // referenced from rust_try.ll - #[allow(private_no_mangle_fns)] + #[lang = "eh_personality"] + #[no_mangle] extern fn rust_eh_personality( version: c_int, actions: uw::_Unwind_Action, @@ -115,8 +115,9 @@ pub mod eabi { } } - #[no_mangle] // referenced from rust_try.ll - pub extern "C" fn rust_eh_personality_catch( + #[cfg_attr(not(stage0), lang = "eh_personality_catch")] + #[no_mangle] + pub extern fn rust_eh_personality_catch( _version: c_int, actions: uw::_Unwind_Action, _exception_class: uw::_Unwind_Exception_Class, @@ -142,7 +143,7 @@ pub mod eabi { use rt::libunwind as uw; use libc::c_int; - extern "C" { + extern { fn __gcc_personality_sj0(version: c_int, actions: uw::_Unwind_Action, exception_class: uw::_Unwind_Exception_Class, @@ -151,9 +152,9 @@ pub mod eabi { -> uw::_Unwind_Reason_Code; } - #[lang="eh_personality"] - #[no_mangle] // referenced from rust_try.ll - pub extern "C" fn rust_eh_personality( + #[lang = "eh_personality"] + #[no_mangle] + pub extern fn rust_eh_personality( version: c_int, actions: uw::_Unwind_Action, exception_class: uw::_Unwind_Exception_Class, @@ -167,8 +168,9 @@ pub mod eabi { } } - #[no_mangle] // referenced from rust_try.ll - pub extern "C" fn rust_eh_personality_catch( + #[cfg_attr(not(stage0), lang = "eh_personality_catch")] + #[no_mangle] + pub extern fn rust_eh_personality_catch( _version: c_int, actions: uw::_Unwind_Action, _exception_class: uw::_Unwind_Exception_Class, @@ -196,17 +198,16 @@ pub mod eabi { use rt::libunwind as uw; use libc::c_int; - extern "C" { + extern { fn __gcc_personality_v0(state: uw::_Unwind_State, ue_header: *mut uw::_Unwind_Exception, context: *mut uw::_Unwind_Context) -> uw::_Unwind_Reason_Code; } - #[lang="eh_personality"] - #[no_mangle] // referenced from rust_try.ll - #[allow(private_no_mangle_fns)] - extern "C" fn rust_eh_personality( + #[lang = "eh_personality"] + #[no_mangle] + extern fn rust_eh_personality( state: uw::_Unwind_State, ue_header: *mut uw::_Unwind_Exception, context: *mut uw::_Unwind_Context @@ -217,8 +218,9 @@ pub mod eabi { } } - #[no_mangle] // referenced from rust_try.ll - pub extern "C" fn rust_eh_personality_catch( + #[cfg_attr(not(stage0), lang = "eh_personality_catch")] + #[no_mangle] + pub extern fn rust_eh_personality_catch( state: uw::_Unwind_State, _ue_header: *mut uw::_Unwind_Exception, _context: *mut uw::_Unwind_Context @@ -266,7 +268,7 @@ pub mod eabi { } type _Unwind_Personality_Fn = - extern "C" fn( + extern fn( version: c_int, actions: uw::_Unwind_Action, exception_class: uw::_Unwind_Exception_Class, @@ -274,7 +276,7 @@ pub mod eabi { context: *mut uw::_Unwind_Context ) -> uw::_Unwind_Reason_Code; - extern "C" { + extern { fn __gcc_personality_seh0( exceptionRecord: *mut EXCEPTION_RECORD, establisherFrame: *mut c_void, @@ -291,10 +293,9 @@ pub mod eabi { ) -> EXCEPTION_DISPOSITION; } - #[lang="eh_personality"] - #[no_mangle] // referenced from rust_try.ll - #[allow(private_no_mangle_fns)] - extern "C" fn rust_eh_personality( + #[lang = "eh_personality"] + #[no_mangle] + extern fn rust_eh_personality( exceptionRecord: *mut EXCEPTION_RECORD, establisherFrame: *mut c_void, contextRecord: *mut CONTEXT, @@ -307,15 +308,16 @@ pub mod eabi { } } - #[no_mangle] // referenced from rust_try.ll - pub extern "C" fn rust_eh_personality_catch( + #[cfg_attr(not(stage0), lang = "eh_personality_catch")] + #[no_mangle] + pub extern fn rust_eh_personality_catch( exceptionRecord: *mut EXCEPTION_RECORD, establisherFrame: *mut c_void, contextRecord: *mut CONTEXT, dispatcherContext: *mut DISPATCHER_CONTEXT ) -> EXCEPTION_DISPOSITION { - extern "C" fn inner( + extern fn inner( _version: c_int, actions: uw::_Unwind_Action, _exception_class: uw::_Unwind_Exception_Class, diff --git a/src/libstd/rt/unwind/mod.rs b/src/libstd/rt/unwind/mod.rs index c403976745aa4..db2310ba361b3 100644 --- a/src/libstd/rt/unwind/mod.rs +++ b/src/libstd/rt/unwind/mod.rs @@ -69,7 +69,6 @@ use cmp; use panicking; use fmt; use intrinsics; -use libc::c_void; use mem; use sync::atomic::{self, Ordering}; use sys_common::mutex::Mutex; @@ -127,7 +126,7 @@ extern {} /// run. pub unsafe fn try(f: F) -> Result<(), Box> { let mut f = Some(f); - return inner_try(try_fn::, &mut f as *mut _ as *mut c_void); + return inner_try(try_fn::, &mut f as *mut _ as *mut u8); // If an inner function were not used here, then this generic function `try` // uses the native symbol `rust_try`, for which the code is statically @@ -140,11 +139,12 @@ pub unsafe fn try(f: F) -> Result<(), Box> { // `dllexport`, but it's easier to not have conditional `src/rt/rust_try.ll` // files and instead just have this non-generic shim the compiler can take // care of exposing correctly. - unsafe fn inner_try(f: extern fn(*mut c_void), data: *mut c_void) + #[cfg(not(stage0))] + unsafe fn inner_try(f: fn(*mut u8), data: *mut u8) -> Result<(), Box> { let prev = PANICKING.with(|s| s.get()); PANICKING.with(|s| s.set(false)); - let ep = rust_try(f, data); + let ep = intrinsics::try(f, data); PANICKING.with(|s| s.set(prev)); if ep.is_null() { Ok(()) @@ -152,8 +152,13 @@ pub unsafe fn try(f: F) -> Result<(), Box> { Err(imp::cleanup(ep)) } } + #[cfg(stage0)] + unsafe fn inner_try(f: fn(*mut u8), data: *mut u8) + -> Result<(), Box> { + Ok(f(data)) + } - extern fn try_fn(opt_closure: *mut c_void) { + fn try_fn(opt_closure: *mut u8) { let opt_closure = opt_closure as *mut Option; unsafe { (*opt_closure).take().unwrap()(); } } @@ -163,8 +168,8 @@ pub unsafe fn try(f: F) -> Result<(), Box> { // When f(...) returns normally, the return value is null. // When f(...) throws, the return value is a pointer to the caught // exception object. - fn rust_try(f: extern fn(*mut c_void), - data: *mut c_void) -> *mut c_void; + fn rust_try(f: extern fn(*mut u8), + data: *mut u8) -> *mut u8; } } diff --git a/src/libstd/rt/unwind/seh.rs b/src/libstd/rt/unwind/seh.rs index 632ab4f8e2537..ed44f9a8bda94 100644 --- a/src/libstd/rt/unwind/seh.rs +++ b/src/libstd/rt/unwind/seh.rs @@ -102,7 +102,7 @@ pub unsafe fn panic(data: Box) -> ! { rtabort!("could not unwind stack"); } -pub unsafe fn cleanup(ptr: *mut c_void) -> Box { +pub unsafe fn cleanup(ptr: *mut u8) -> Box { // The `ptr` here actually corresponds to the code of the exception, and our // real data is stored in our thread local. rtassert!(ptr as DWORD == RUST_PANIC); @@ -135,8 +135,9 @@ fn rust_eh_personality() { // to ensure that it's code is RUST_PANIC, which was set by the call to // `RaiseException` above in the `panic` function. #[no_mangle] +#[lang = "msvc_try_filter"] pub extern fn __rust_try_filter(eh_ptrs: *mut EXCEPTION_POINTERS, - _rbp: *mut c_void) -> i32 { + _rbp: *mut u8) -> i32 { unsafe { ((*(*eh_ptrs).ExceptionRecord).ExceptionCode == RUST_PANIC) as i32 } diff --git a/src/rt/rust_try.ll b/src/rt/rust_try.ll deleted file mode 100644 index 8643131d0fb74..0000000000000 --- a/src/rt/rust_try.ll +++ /dev/null @@ -1,54 +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. - -; Rust's try-catch -; When f(...) returns normally, the return value is null. -; When f(...) throws, the return value is a pointer to the caught exception object. - -; See also: libstd/rt/unwind/mod.rs - -define i8* @rust_try(void (i8*)* %f, i8* %env) - personality i8* bitcast (i32 (...)* @rust_eh_personality_catch to i8*) -{ - - %1 = invoke i8* @rust_try_inner(void (i8*)* %f, i8* %env) - to label %normal - unwind label %catch - -normal: - ret i8* %1 - -catch: - landingpad { i8*, i32 } catch i8* null - ; rust_try_inner's landing pad does not resume unwinds, so execution will - ; never reach here - ret i8* null -} - -define internal i8* @rust_try_inner(void (i8*)* %f, i8* %env) - personality i8* bitcast (i32 (...)* @rust_eh_personality to i8*) -{ - - invoke void %f(i8* %env) - to label %normal - unwind label %catch - -normal: - ret i8* null - -catch: - %1 = landingpad { i8*, i32 } catch i8* null - ; extract and return pointer to the exception object - %2 = extractvalue { i8*, i32 } %1, 0 - ret i8* %2 -} - -declare i32 @rust_eh_personality(...) -declare i32 @rust_eh_personality_catch(...) diff --git a/src/rt/rust_try_msvc_32.ll b/src/rt/rust_try_msvc_32.ll deleted file mode 100644 index bdee53b136e10..0000000000000 --- a/src/rt/rust_try_msvc_32.ll +++ /dev/null @@ -1,42 +0,0 @@ -; Copyright 2015 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. - -; For more comments about what's going on here see rust_try_msvc_64.ll. The only -; difference between that and this file is the personality function used as it's -; different for 32-bit MSVC than it is for 64-bit. - -define i8* @rust_try(void (i8*)* %f, i8* %env) - personality i8* bitcast (i32 (...)* @_except_handler3 to i8*) -{ - invoke void %f(i8* %env) - to label %normal - unwind label %catch - -normal: - ret i8* null -catch: - %vals = landingpad { i8*, i32 } - catch i8* bitcast (i32 (i8*, i8*)* @__rust_try_filter to i8*) - %ehptr = extractvalue { i8*, i32 } %vals, 0 - %sel = extractvalue { i8*, i32 } %vals, 1 - %filter_sel = call i32 @llvm.eh.typeid.for(i8* bitcast (i32 (i8*, i8*)* @__rust_try_filter to i8*)) - %is_filter = icmp eq i32 %sel, %filter_sel - br i1 %is_filter, label %catch-return, label %catch-resume - -catch-return: - ret i8* %ehptr - -catch-resume: - resume { i8*, i32 } %vals -} - -declare i32 @_except_handler3(...) -declare i32 @__rust_try_filter(i8*, i8*) -declare i32 @llvm.eh.typeid.for(i8*) readnone nounwind diff --git a/src/rt/rust_try_msvc_64.ll b/src/rt/rust_try_msvc_64.ll deleted file mode 100644 index c38e6081bf2d3..0000000000000 --- a/src/rt/rust_try_msvc_64.ll +++ /dev/null @@ -1,80 +0,0 @@ -; Copyright 2015 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. - -; 64-bit MSVC's definition of the `rust_try` function. This function can't be -; defined in Rust as it's a "try-catch" block that's not expressible in Rust's -; syntax, so we're using LLVM to produce an object file with the associated -; handler. -; -; To use the correct system implementation details, this file is separate from -; the standard rust_try.ll as we need specifically use the __C_specific_handler -; personality function or otherwise LLVM doesn't emit SEH handling tables. -; There's also a few fiddly bits about SEH right now in LLVM that require us to -; structure this a fairly particular way! -; -; See also: src/libstd/rt/unwind/seh.rs - -define i8* @rust_try(void (i8*)* %f, i8* %env) - personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) -{ - invoke void %f(i8* %env) - to label %normal - unwind label %catch - -normal: - ret i8* null - -; Here's where most of the magic happens, this is the only landing pad in rust -; tagged with "catch" to indicate that we're catching an exception. The other -; catch handlers in rust_try.ll just catch *all* exceptions, but that's because -; most exceptions are already filtered out by their personality function. -; -; For MSVC we're just using a standard personality function that we can't -; customize, so we need to do the exception filtering ourselves, and this is -; currently performed by the `__rust_try_filter` function. This function, -; specified in the landingpad instruction, will be invoked by Windows SEH -; routines and will return whether the exception in question can be caught (aka -; the Rust runtime is the one that threw the exception). -; -; To get this to compile (currently LLVM segfaults if it's not in this -; particular structure), when the landingpad is executing we test to make sure -; that the ID of the exception being thrown is indeed the one that we were -; expecting. If it's not, we resume the exception, and otherwise we return the -; pointer that we got -; -; Full disclosure: It's not clear to me what this `llvm.eh.typeid` stuff is -; doing *other* then just allowing LLVM to compile this file without -; segfaulting. I would expect the entire landing pad to just be: -; -; %vals = landingpad ... -; %ehptr = extractvalue { i8*, i32 } %vals, 0 -; ret i8* %ehptr -; -; but apparently LLVM chokes on this, so we do the more complicated thing to -; placate it. -catch: - %vals = landingpad { i8*, i32 } - catch i8* bitcast (i32 (i8*, i8*)* @__rust_try_filter to i8*) - %ehptr = extractvalue { i8*, i32 } %vals, 0 - %sel = extractvalue { i8*, i32 } %vals, 1 - %filter_sel = call i32 @llvm.eh.typeid.for(i8* bitcast (i32 (i8*, i8*)* @__rust_try_filter to i8*)) - %is_filter = icmp eq i32 %sel, %filter_sel - br i1 %is_filter, label %catch-return, label %catch-resume - -catch-return: - ret i8* %ehptr - -catch-resume: - resume { i8*, i32 } %vals -} - -declare i32 @__C_specific_handler(...) -declare i32 @__rust_try_filter(i8*, i8*) -declare i32 @llvm.eh.typeid.for(i8*) readnone nounwind diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 163e95b890f4b..5007af0e777b8 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -120,7 +120,8 @@ extern "C" void LLVMAddDereferenceableCallSiteAttr(LLVMValueRef Instr, unsigned idx, B))); } -extern "C" void LLVMAddFunctionAttribute(LLVMValueRef Fn, unsigned index, uint64_t Val) { +extern "C" void LLVMAddFunctionAttribute(LLVMValueRef Fn, unsigned index, + uint64_t Val) { Function *A = unwrap(Fn); AttrBuilder B; B.addRawValue(Val);