Skip to content

Commit

Permalink
Revisit implementation of custom unwind resume;
Browse files Browse the repository at this point in the history
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`.
  • Loading branch information
vadimcn committed Oct 19, 2015
1 parent 4af89fe commit 1da4662
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 54 deletions.
13 changes: 13 additions & 0 deletions src/librustc_trans/trans/base.rs
Expand Up @@ -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();
Expand Down
4 changes: 1 addition & 3 deletions src/librustc_trans/trans/cleanup.rs
Expand Up @@ -732,7 +732,7 @@ impl<'blk, 'tcx> CleanupHelperMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx
"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;
}
Expand Down Expand Up @@ -845,8 +845,6 @@ impl<'blk, 'tcx> CleanupHelperMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx

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();
Expand Down
68 changes: 24 additions & 44 deletions src/librustc_trans/trans/common.rs
Expand Up @@ -561,53 +561,33 @@ impl<'a, 'tcx> FunctionContext<'a, 'tcx> {
}
}

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

Expand Down
12 changes: 6 additions & 6 deletions src/librustc_trans/trans/context.rs
Expand Up @@ -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>>,

Expand Down Expand Up @@ -466,8 +466,8 @@ impl<'tcx> LocalCrateContext<'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()),
Expand Down Expand Up @@ -728,12 +728,12 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
&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>> {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_trans/trans/intrinsic.rs
Expand Up @@ -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);
Expand Down

0 comments on commit 1da4662

Please sign in to comment.