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:
parent
4af89fe317
commit
1da466253c
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>> {
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user