diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 9fa1aaf76f8..b37a0f6318c 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -944,6 +944,19 @@ pub fn call_lifetime_end(cx: Block, ptr: ValueRef) { Call(cx, lifetime_end, &[C_u64(ccx, size), ptr], None, DebugLoc::None); } +// Generates code for resumption of unwind at the end of a landing pad. +pub fn trans_unwind_resume(bcx: Block, lpval: ValueRef) { + if !bcx.sess().target.target.options.custom_unwind_resume { + Resume(bcx, lpval); + } else { + let exc_ptr = ExtractValue(bcx, lpval, 0); + let llunwresume = bcx.fcx.eh_unwind_resume(); + Call(bcx, llunwresume, &[exc_ptr], None, DebugLoc::None); + Unreachable(bcx); + } +} + + pub fn call_memcpy(cx: Block, dst: ValueRef, src: ValueRef, n_bytes: ValueRef, align: u32) { let _icx = push_ctxt("call_memcpy"); let ccx = cx.ccx(); diff --git a/src/librustc_trans/trans/cleanup.rs b/src/librustc_trans/trans/cleanup.rs index d226bc3f155..ffdc2701f81 100644 --- a/src/librustc_trans/trans/cleanup.rs +++ b/src/librustc_trans/trans/cleanup.rs @@ -732,7 +732,7 @@ fn trans_cleanups_to_exit_scope(&'blk self, "create_landing_pad() should have set this"); let lp = build::Load(prev_bcx, personality); base::call_lifetime_end(prev_bcx, personality); - build::Resume(prev_bcx, lp); + base::trans_unwind_resume(prev_bcx, lp); prev_llbb = prev_bcx.llbb; break; } @@ -845,8 +845,6 @@ fn get_or_create_landing_pad(&'blk self) -> BasicBlockRef { debug!("get_or_create_landing_pad"); - self.inject_unwind_resume_hook(); - // Check if a landing pad block exists; if not, create one. { let mut scopes = self.scopes.borrow_mut(); diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index d160465c619..b39b7818a63 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -561,53 +561,33 @@ pub fn eh_personality(&self) -> ValueRef { } } - /// By default, LLVM lowers `resume` instructions into calls to `_Unwind_Resume` - /// defined in libgcc, however, unlike personality routines, there is no easy way to - /// override that symbol. This method injects a local-scoped `_Unwind_Resume` function - /// which immediately defers to the user-defined `eh_unwind_resume` lang item. - pub fn inject_unwind_resume_hook(&self) { - let ccx = self.ccx; - if !ccx.sess().target.target.options.custom_unwind_resume || - ccx.unwind_resume_hooked().get() { - return; - } - - let new_resume = match ccx.tcx().lang_items.eh_unwind_resume() { - Some(did) => callee::trans_fn_ref(ccx, did, ExprId(0), &self.param_substs).val, - None => { - let fty = Type::variadic_func(&[], &Type::void(self.ccx)); - declare::declare_cfn(self.ccx, "rust_eh_unwind_resume", fty, - self.ccx.tcx().mk_nil()) + // Returns a ValueRef of the "eh_unwind_resume" lang item if one is defined, + // otherwise declares it as an external funtion. + pub fn eh_unwind_resume(&self) -> ValueRef { + use trans::attributes; + assert!(self.ccx.sess().target.target.options.custom_unwind_resume); + match self.ccx.tcx().lang_items.eh_unwind_resume() { + Some(def_id) => { + callee::trans_fn_ref(self.ccx, def_id, ExprId(0), + self.param_substs).val + } + None => { + let mut unwresume = self.ccx.eh_unwind_resume().borrow_mut(); + match *unwresume { + Some(llfn) => llfn, + None => { + let fty = Type::func(&[Type::i8p(self.ccx)], &Type::void(self.ccx)); + let llfn = declare::declare_fn(self.ccx, + "rust_eh_unwind_resume", + llvm::CCallConv, + fty, ty::FnDiverging); + attributes::unwind(llfn, true); + *unwresume = Some(llfn); + llfn + } + } } - }; - - unsafe { - let resume_type = Type::func(&[Type::i8(ccx).ptr_to()], &Type::void(ccx)); - let old_resume = llvm::LLVMAddFunction(ccx.llmod(), - "_Unwind_Resume\0".as_ptr() as *const _, - resume_type.to_ref()); - llvm::SetLinkage(old_resume, llvm::InternalLinkage); - let llbb = llvm::LLVMAppendBasicBlockInContext(ccx.llcx(), - old_resume, - "\0".as_ptr() as *const _); - let builder = ccx.builder(); - builder.position_at_end(llbb); - builder.call(new_resume, &[llvm::LLVMGetFirstParam(old_resume)], None); - builder.unreachable(); // it should never return - - // Until DwarfEHPrepare pass has run, _Unwind_Resume is not referenced by any live code - // and is subject to dead code elimination. Here we add _Unwind_Resume to @llvm.globals - // to prevent that. - let i8p_ty = Type::i8p(ccx); - let used_ty = Type::array(&i8p_ty, 1); - let used = llvm::LLVMAddGlobal(ccx.llmod(), used_ty.to_ref(), - "llvm.used\0".as_ptr() as *const _); - let old_resume = llvm::LLVMConstBitCast(old_resume, i8p_ty.to_ref()); - llvm::LLVMSetInitializer(used, C_array(i8p_ty, &[old_resume])); - llvm::SetLinkage(used, llvm::AppendingLinkage); - llvm::LLVMSetSection(used, "llvm.metadata\0".as_ptr() as *const _) } - ccx.unwind_resume_hooked().set(true); } } diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs index 94ce92ab972..0f7ea334c88 100644 --- a/src/librustc_trans/trans/context.rs +++ b/src/librustc_trans/trans/context.rs @@ -146,8 +146,8 @@ pub struct LocalCrateContext<'tcx> { dbg_cx: Option>, eh_personality: RefCell>, + eh_unwind_resume: RefCell>, rust_try_fn: RefCell>, - unwind_resume_hooked: Cell, intrinsics: RefCell>, @@ -466,8 +466,8 @@ fn new<'a>(shared: &SharedCrateContext<'a, 'tcx>, closure_vals: RefCell::new(FnvHashMap()), dbg_cx: dbg_cx, eh_personality: RefCell::new(None), + eh_unwind_resume: RefCell::new(None), rust_try_fn: RefCell::new(None), - unwind_resume_hooked: Cell::new(false), intrinsics: RefCell::new(FnvHashMap()), n_llvm_insns: Cell::new(0), trait_cache: RefCell::new(FnvHashMap()), @@ -728,12 +728,12 @@ pub fn eh_personality<'a>(&'a self) -> &'a RefCell> { &self.local.eh_personality } - pub fn rust_try_fn<'a>(&'a self) -> &'a RefCell> { - &self.local.rust_try_fn + pub fn eh_unwind_resume<'a>(&'a self) -> &'a RefCell> { + &self.local.eh_unwind_resume } - pub fn unwind_resume_hooked<'a>(&'a self) -> &'a Cell { - &self.local.unwind_resume_hooked + pub fn rust_try_fn<'a>(&'a self) -> &'a RefCell> { + &self.local.rust_try_fn } fn intrinsics<'a>(&'a self) -> &'a RefCell> { diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs index 5e781bdf330..b8fa826b939 100644 --- a/src/librustc_trans/trans/intrinsic.rs +++ b/src/librustc_trans/trans/intrinsic.rs @@ -1315,7 +1315,7 @@ fn trans_msvc_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // 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); + trans_unwind_resume(catch_resume, vals); // On the successful branch we just return null. Ret(then, C_null(Type::i8p(ccx)), dloc);