trans: Move rust_try into the compiler
This commit moves the IR files in the distribution, rust_try.ll, rust_try_msvc_64.ll, and rust_try_msvc_32.ll into the compiler from the main distribution. There's a few reasons for this change: * LLVM changes its IR syntax from time to time, so it's very difficult to have these files build across many LLVM versions simultaneously. We'll likely want to retain this ability for quite some time into the future. * The implementation of these files is closely tied to the compiler and runtime itself, so it makes sense to fold it into a location which can do more platform-specific checks for various implementation details (such as MSVC 32 vs 64-bit). * This removes LLVM as a build-time dependency of the standard library. This may end up becoming very useful if we move towards building the standard library with Cargo. In the immediate future, however, this commit should restore compatibility with LLVM 3.5 and 3.6.
This commit is contained in:
parent
39d4faf989
commit
c35b2bd226
18
mk/rt.mk
18
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))
|
||||
|
|
|
@ -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<T>(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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -142,6 +142,7 @@ pub struct LocalCrateContext<'tcx> {
|
|||
dbg_cx: Option<debuginfo::CrateDebugContext<'tcx>>,
|
||||
|
||||
eh_personality: RefCell<Option<ValueRef>>,
|
||||
rust_try_fn: RefCell<Option<ValueRef>>,
|
||||
|
||||
intrinsics: RefCell<FnvHashMap<&'static str, ValueRef>>,
|
||||
|
||||
|
@ -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<Option<ValueRef>> {
|
||||
&self.local.rust_try_fn
|
||||
}
|
||||
|
||||
fn intrinsics<'a>(&'a self) -> &'a RefCell<FnvHashMap<&'static str, ValueRef>> {
|
||||
&self.local.intrinsics
|
||||
}
|
||||
|
@ -923,6 +929,7 @@ fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option<ValueRef
|
|||
ifn!("llvm.lifetime.end", fn(t_i64, i8p) -> 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.
|
||||
|
|
|
@ -176,8 +176,8 @@ pub fn define_global(ccx: &CrateContext, name: &str, ty: Type) -> Option<ValueRe
|
|||
/// 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_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv, fn_type: Type,
|
||||
output: ty::FnOutput) -> Option<ValueRef> {
|
||||
pub fn define_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv,
|
||||
fn_type: Type, output: ty::FnOutput) -> Option<ValueRef> {
|
||||
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<ValueRef> {
|
||||
/// 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<ValueRef> {
|
||||
debug!("get_defined_value(name={:?})", name);
|
||||
let namebuf = CString::new(name).unwrap_or_else(|_|{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<Any + Send + 'static>) -> ! {
|
|||
}
|
||||
}
|
||||
|
||||
pub unsafe fn cleanup(ptr: *mut c_void) -> Box<Any + Send + 'static> {
|
||||
pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send + 'static> {
|
||||
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,
|
||||
|
@ -99,8 +100,7 @@ pub mod eabi {
|
|||
}
|
||||
|
||||
#[lang = "eh_personality"]
|
||||
#[no_mangle] // referenced from rust_try.ll
|
||||
#[allow(private_no_mangle_fns)]
|
||||
#[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,
|
||||
|
@ -152,8 +153,8 @@ pub mod eabi {
|
|||
}
|
||||
|
||||
#[lang = "eh_personality"]
|
||||
#[no_mangle] // referenced from rust_try.ll
|
||||
pub extern "C" fn rust_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,7 +198,7 @@ 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)
|
||||
|
@ -204,9 +206,8 @@ pub mod eabi {
|
|||
}
|
||||
|
||||
#[lang = "eh_personality"]
|
||||
#[no_mangle] // referenced from rust_try.ll
|
||||
#[allow(private_no_mangle_fns)]
|
||||
extern "C" fn rust_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,
|
||||
|
@ -292,9 +294,8 @@ pub mod eabi {
|
|||
}
|
||||
|
||||
#[lang = "eh_personality"]
|
||||
#[no_mangle] // referenced from rust_try.ll
|
||||
#[allow(private_no_mangle_fns)]
|
||||
extern "C" fn rust_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,
|
||||
|
|
|
@ -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: FnOnce()>(f: F) -> Result<(), Box<Any + Send>> {
|
||||
let mut f = Some(f);
|
||||
return inner_try(try_fn::<F>, &mut f as *mut _ as *mut c_void);
|
||||
return inner_try(try_fn::<F>, &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: FnOnce()>(f: F) -> Result<(), Box<Any + Send>> {
|
|||
// `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<Any + Send>> {
|
||||
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: FnOnce()>(f: F) -> Result<(), Box<Any + Send>> {
|
|||
Err(imp::cleanup(ep))
|
||||
}
|
||||
}
|
||||
#[cfg(stage0)]
|
||||
unsafe fn inner_try(f: fn(*mut u8), data: *mut u8)
|
||||
-> Result<(), Box<Any + Send>> {
|
||||
Ok(f(data))
|
||||
}
|
||||
|
||||
extern fn try_fn<F: FnOnce()>(opt_closure: *mut c_void) {
|
||||
fn try_fn<F: FnOnce()>(opt_closure: *mut u8) {
|
||||
let opt_closure = opt_closure as *mut Option<F>;
|
||||
unsafe { (*opt_closure).take().unwrap()(); }
|
||||
}
|
||||
|
@ -163,8 +168,8 @@ pub unsafe fn try<F: FnOnce()>(f: F) -> Result<(), Box<Any + Send>> {
|
|||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! {
|
|||
rtabort!("could not unwind stack");
|
||||
}
|
||||
|
||||
pub unsafe fn cleanup(ptr: *mut c_void) -> Box<Any + Send + 'static> {
|
||||
pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send + 'static> {
|
||||
// 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
|
||||
}
|
||||
|
|
|
@ -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 <LICENSE-APACHE or
|
||||
; http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
; <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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(...)
|
|
@ -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 <LICENSE-APACHE or
|
||||
; http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
; <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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
|
|
@ -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 <LICENSE-APACHE or
|
||||
; http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
; <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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
|
|
@ -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<Function>(Fn);
|
||||
AttrBuilder B;
|
||||
B.addRawValue(Val);
|
||||
|
|
Loading…
Reference in New Issue