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
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: $$@)
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))
/// 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;
}
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;
) }
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;
}
}
bitflags! {
- flags Attribute : u32 {
+ flags Attribute : u64 {
const ZExt = 1 << 0,
const SExt = 1 << 1,
const NoReturn = 1 << 2,
const ReturnsTwice = 1 << 29,
const UWTable = 1 << 30,
const NonLazyBind = 1 << 31,
+ const OptimizeNone = 1 << 42,
}
}
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)
}
}
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)
}
}
}
+ 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 {
}, 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,
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;
&[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);
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);
// 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();
use middle::subst::{self, Substs};
use trans::base;
use trans::build;
+use trans::callee;
use trans::cleanup;
use trans::consts;
use trans::datum;
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
dbg_cx: Option<debuginfo::CrateDebugContext<'tcx>>,
eh_personality: RefCell<Option<ValueRef>>,
+ rust_try_fn: RefCell<Option<ValueRef>>,
intrinsics: RefCell<FnvHashMap<&'static str, ValueRef>>,
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()),
&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
}
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.
/// 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 {
/// 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(|_|{
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
#![allow(non_upper_case_globals)]
+use arena::TypedArena;
use llvm;
use llvm::{SequentiallyConsistent, Acquire, Release, AtomicXchg, ValueRef, TypeKind};
use middle::subst;
use trans::common::*;
use trans::datum::*;
use trans::debuginfo::DebugLoc;
+use trans::declare;
use trans::expr;
use trans::glue;
use trans::type_of::*;
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;
}
}
+ 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,
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"));
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
+}
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);
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);
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);
// 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 {
}
}
-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();
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,
-> 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,
}
}
- #[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,
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,
-> 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,
}
}
- #[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,
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
}
}
- #[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
}
type _Unwind_Personality_Fn =
- extern "C" fn(
+ extern fn(
version: c_int,
actions: uw::_Unwind_Action,
exception_class: uw::_Unwind_Exception_Class,
context: *mut uw::_Unwind_Context
) -> uw::_Unwind_Reason_Code;
- extern "C" {
+ extern {
fn __gcc_personality_seh0(
exceptionRecord: *mut EXCEPTION_RECORD,
establisherFrame: *mut c_void,
) -> 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,
}
}
- #[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,
use panicking;
use fmt;
use intrinsics;
-use libc::c_void;
use mem;
use sync::atomic::{self, Ordering};
use sys_common::mutex::Mutex;
/// 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
// `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(())
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()(); }
}
// 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;
}
}
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);
// 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
}
+++ /dev/null
-; 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(...)
+++ /dev/null
-; 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
+++ /dev/null
-; 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
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);