Revisit implementation of custom unwind resume;

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`.
This commit is contained in:
Vadim Chugunov 2015-10-18 14:17:34 -07:00
parent 4af89fe317
commit 1da466253c
5 changed files with 46 additions and 55 deletions

View File

@ -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();

View File

@ -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();

View File

@ -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);
}
}

View File

@ -146,8 +146,8 @@ pub struct LocalCrateContext<'tcx> {
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>>,
@ -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<Option<ValueRef>> {
&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>> {

View File

@ -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);