Rather than injecting a local `_Unwind_Resume` into the current translation unit,
just replace `resume` instruction with a direct call the the `eh_unwind_resume` lang item.
This is likely to be more robust in the face of future LLVM changes, and also allows us to delegate
work back to libgcc's `_Unwind_Resume`.
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();
"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;
}
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();
}
}
- /// 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,
+ // 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 fty = Type::variadic_func(&[], &Type::void(self.ccx));
- declare::declare_cfn(self.ccx, "rust_eh_unwind_resume", fty,
- self.ccx.tcx().mk_nil())
+ 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);
}
}
dbg_cx: Option<debuginfo::CrateDebugContext<'tcx>>,
eh_personality: RefCell<Option<ValueRef>>,
+ eh_unwind_resume: RefCell<Option<ValueRef>>,
rust_try_fn: RefCell<Option<ValueRef>>,
- unwind_resume_hooked: Cell<bool>,
intrinsics: RefCell<FnvHashMap<&'static str, ValueRef>>,
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()),
&self.local.eh_personality
}
- pub fn rust_try_fn<'a>(&'a self) -> &'a RefCell<Option<ValueRef>> {
- &self.local.rust_try_fn
+ pub fn eh_unwind_resume<'a>(&'a self) -> &'a RefCell<Option<ValueRef>> {
+ &self.local.eh_unwind_resume
}
- pub fn unwind_resume_hooked<'a>(&'a self) -> &'a Cell<bool> {
- &self.local.unwind_resume_hooked
+ 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>> {
// 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);