Skip to content

Commit

Permalink
Auto merge of #27210 - vadimcn:win64-eh-pers, r=alexcrichton
Browse files Browse the repository at this point in the history
After this change, the only remaining symbol we are pulling from libgcc on Win64 is `__chkstk_ms` - the stack probing routine.
  • Loading branch information
bors committed Aug 3, 2015
2 parents d877e65 + 96d1db2 commit ceded6a
Show file tree
Hide file tree
Showing 25 changed files with 667 additions and 237 deletions.
1 change: 1 addition & 0 deletions src/doc/trpl/lang-items.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ fn main(argc: isize, argv: *const *const u8) -> isize {
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
#[lang = "eh_personality"] extern fn eh_personality() {}
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
# #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {}
```

Note the use of `abort`: the `exchange_malloc` lang item is assumed to
Expand Down
3 changes: 3 additions & 0 deletions src/doc/trpl/no-stdlib.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ fn start(_argc: isize, _argv: *const *const u8) -> isize {
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
#[lang = "eh_personality"] extern fn eh_personality() {}
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
# #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {}
# // fn main() {} tricked you, rustdoc!
```

Expand All @@ -63,6 +64,7 @@ pub extern fn main(argc: i32, argv: *const *const u8) -> i32 {
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
#[lang = "eh_personality"] extern fn eh_personality() {}
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
# #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {}
# // fn main() {} tricked you, rustdoc!
```

Expand Down Expand Up @@ -150,6 +152,7 @@ extern fn panic_fmt(args: &core::fmt::Arguments,
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
#[lang = "eh_personality"] extern fn eh_personality() {}
# #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {}
# #[start] fn start(argc: isize, argv: *const *const u8) -> isize { 0 }
# fn main() {}
```
Expand Down
1 change: 1 addition & 0 deletions src/librustc/middle/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ lets_do_this! {

EhPersonalityLangItem, "eh_personality", eh_personality;
EhPersonalityCatchLangItem, "eh_personality_catch", eh_personality_catch;
EhUnwindResumeLangItem, "eh_unwind_resume", eh_unwind_resume;
MSVCTryFilterLangItem, "msvc_try_filter", msvc_try_filter;

ExchangeHeapLangItem, "exchange_heap", exchange_heap;
Expand Down
5 changes: 5 additions & 0 deletions src/librustc/middle/weak_lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ pub fn check_crate(krate: &ast::Crate,
if items.eh_personality().is_none() {
items.missing.push(lang_items::EhPersonalityLangItem);
}
if sess.target.target.options.custom_unwind_resume &
items.eh_unwind_resume().is_none() {
items.missing.push(lang_items::EhUnwindResumeLangItem);
}

{
let mut cx = Context { sess: sess, items: items };
Expand Down Expand Up @@ -122,4 +126,5 @@ weak_lang_items! {
panic_fmt, PanicFmtLangItem, rust_begin_unwind;
stack_exhausted, StackExhaustedLangItem, rust_stack_exhausted;
eh_personality, EhPersonalityLangItem, rust_eh_personality;
eh_unwind_resume, EhUnwindResumeLangItem, rust_eh_unwind_resume;
}
6 changes: 6 additions & 0 deletions src/librustc_back/target/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,11 @@ pub struct TargetOptions {
/// currently only "gnu" is used to fall into LLVM. Unknown strings cause
/// the system linker to be used.
pub archive_format: String,
/// Whether the target uses a custom unwind resumption routine.
/// By default LLVM lowers `resume` instructions into calls to `_Unwind_Resume`
/// defined in libgcc. If this option is enabled, the target must provide
/// `eh_unwind_resume` lang item.
pub custom_unwind_resume: bool,
}

impl Default for TargetOptions {
Expand Down Expand Up @@ -209,6 +214,7 @@ impl Default for TargetOptions {
pre_link_objects: Vec::new(),
post_link_objects: Vec::new(),
archive_format: String::new(),
custom_unwind_resume: false,
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/librustc_back/target/x86_64_pc_windows_gnu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub fn target() -> Target {
// On Win64 unwinding is handled by the OS, so we can link libgcc statically.
base.pre_link_args.push("-static-libgcc".to_string());
base.pre_link_args.push("-m64".to_string());
base.custom_unwind_resume = true;

Target {
llvm_target: "x86_64-pc-windows-gnu".to_string(),
Expand Down
6 changes: 6 additions & 0 deletions src/librustc_trans/trans/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2171,6 +2171,12 @@ fn finish_register_fn(ccx: &CrateContext, sym: String, node_id: ast::NodeId,
llvm::SetDLLStorageClass(llfn, llvm::DLLExportStorageClass);
}
}
if ccx.tcx().lang_items.eh_unwind_resume() == Some(def) {
llvm::SetLinkage(llfn, llvm::ExternalLinkage);
if ccx.use_dll_storage_attrs() {
llvm::SetDLLStorageClass(llfn, llvm::DLLExportStorageClass);
}
}
}

fn register_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
Expand Down
2 changes: 2 additions & 0 deletions src/librustc_trans/trans/cleanup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,8 @@ 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
49 changes: 49 additions & 0 deletions src/librustc_trans/trans/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,55 @@ 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,
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())
}
};

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

// Basic block context. We create a block context for each basic block
Expand Down
6 changes: 6 additions & 0 deletions src/librustc_trans/trans/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ pub struct LocalCrateContext<'tcx> {

eh_personality: 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,6 +467,7 @@ impl<'tcx> LocalCrateContext<'tcx> {
dbg_cx: dbg_cx,
eh_personality: 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 @@ -735,6 +737,10 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
&self.local.rust_try_fn
}

pub fn unwind_resume_hooked<'a>(&'a self) -> &'a Cell<bool> {
&self.local.unwind_resume_hooked
}

fn intrinsics<'a>(&'a self) -> &'a RefCell<FnvHashMap<&'static str, ValueRef>> {
&self.local.intrinsics
}
Expand Down
151 changes: 47 additions & 104 deletions src/librustc_trans/trans/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use llvm::{SequentiallyConsistent, Acquire, Release, AtomicXchg, ValueRef, TypeK
use middle::subst;
use middle::subst::FnSpace;
use trans::adt;
use trans::attributes;
use trans::base::*;
use trans::build::*;
use trans::callee;
Expand Down Expand Up @@ -1159,26 +1160,14 @@ fn trans_msvc_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
// of exceptions (e.g. the normal semantics of LLVM's landingpad and invoke
// instructions).
//
// This translation is a little surprising for two reasons:
// This translation is a little surprising because
// we always call a shim function instead of inlining the call to `invoke`
// manually here. This is done because in LLVM we're only allowed to have one
// personality per function definition. The call to the `try` intrinsic is
// being inlined into the function calling it, and that function may already
// have other personality functions in play. By calling a shim we're
// guaranteed that our shim will have the right personality function.
//
// 1. We always call a shim function instead of inlining the call to `invoke`
// manually here. This is done because in LLVM we're only allowed to have one
// personality per function definition. The call to the `try` intrinsic is
// being inlined into the function calling it, and that function may already
// have other personality functions in play. By calling a shim we're
// guaranteed that our shim will have the right personality function.
//
// 2. Instead of making one shim (explained above), we make two shims! The
// reason for this has to do with the technical details about the
// implementation of unwinding in the runtime, but the tl;dr; is that the
// outer shim's personality function says "catch rust exceptions" and the
// inner shim's landing pad will not `resume` the exception being thrown.
// This means that the outer shim's landing pad is never run and the inner
// shim's return value is the return value of the whole call.
//
// The double-shim aspect is currently done for implementation ease on the
// runtime side of things, and more info can be found in
// src/libstd/rt/unwind/gcc.rs.
fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
func: ValueRef,
data: ValueRef,
Expand All @@ -1188,108 +1177,63 @@ fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
let ccx = bcx.ccx();
let dloc = DebugLoc::None;

// Type indicator for the exception being thrown, not entirely sure
// what's going on here but it's what all the examples in LLVM use.
let lpad_ty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)],
false);
// Translates the shims described above:
//
// bcx:
// invoke %func(%args...) normal %normal unwind %catch
//
// normal:
// ret null
//
// catch:
// (ptr, _) = landingpad
// ret ptr

// Define the "inner try" shim
let rust_try_inner = declare::define_internal_rust_fn(ccx,
"__rust_try_inner",
try_fn_ty);
trans_rust_try(ccx, rust_try_inner, lpad_ty, bcx.fcx.eh_personality(),
output, dloc, &mut |bcx, then, catch| {
let func = llvm::get_param(rust_try_inner, 0);
let data = llvm::get_param(rust_try_inner, 1);
Invoke(bcx, func, &[data], then.llbb, catch.llbb, None, dloc);
C_null(Type::i8p(ccx))
});

// Define the "outer try" shim.
let rust_try = declare::define_internal_rust_fn(ccx, "__rust_try",
try_fn_ty);
let rust_try = declare::define_internal_rust_fn(ccx, "__rust_try", try_fn_ty);
attributes::emit_uwtable(rust_try, true);
let catch_pers = match bcx.tcx().lang_items.eh_personality_catch() {
Some(did) => callee::trans_fn_ref(ccx, did, ExprId(0),
bcx.fcx.param_substs).val,
None => bcx.tcx().sess.bug("eh_personality_catch not defined"),
};
trans_rust_try(ccx, rust_try, lpad_ty, catch_pers, output, dloc,
&mut |bcx, then, catch| {
let func = llvm::get_param(rust_try, 0);
let data = llvm::get_param(rust_try, 1);
Invoke(bcx, rust_try_inner, &[func, data], then.llbb, catch.llbb,
None, dloc)
});
return rust_try
});

// Note that no invoke is used here because by definition this function
// can't panic (that's what it's catching).
let ret = Call(bcx, llfn, &[func, data], None, dloc);
Store(bcx, ret, dest);
return bcx;

// Translates both the inner and outer shims described above. The only
// difference between these two is the function invoked and the personality
// involved, so a common routine is shared.
//
// bcx:
// invoke %func(%args...) normal %normal unwind %unwind
//
// normal:
// ret null
//
// unwind:
// (ptr, _) = landingpad
// br (ptr != null), done, reraise
//
// done:
// ret ptr
//
// reraise:
// resume
//
// Note that the branch checking for `null` here isn't actually necessary,
// it's just an unfortunate hack to make sure that LLVM doesn't optimize too
// much. If this were not present, then LLVM would correctly deduce that our
// inner shim should be tagged with `nounwind` (as it catches all
// exceptions) and then the outer shim's `invoke` will be translated to just
// a simple call, destroying that entry for the personality function.
//
// To ensure that both shims always have an `invoke` this check against null
// confuses LLVM enough to the point that it won't infer `nounwind` and
// we'll proceed as normal.
fn trans_rust_try<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
llfn: ValueRef,
lpad_ty: Type,
personality: ValueRef,
output: ty::FnOutput<'tcx>,
dloc: DebugLoc,
invoke: &mut FnMut(Block, Block, Block) -> ValueRef) {
let (fcx, block_arena);
block_arena = TypedArena::new();
fcx = new_fn_ctxt(ccx, llfn, ast::DUMMY_NODE_ID, false,
fcx = new_fn_ctxt(ccx, rust_try, ast::DUMMY_NODE_ID, false,
output, ccx.tcx().mk_substs(Substs::trans_empty()),
None, &block_arena);
let bcx = init_function(&fcx, true, output);
let then = bcx.fcx.new_temp_block("then");
let catch = bcx.fcx.new_temp_block("catch");
let reraise = bcx.fcx.new_temp_block("reraise");
let catch_return = bcx.fcx.new_temp_block("catch-return");

let invoke_ret = invoke(bcx, then, catch);
Ret(then, invoke_ret, dloc);
let vals = LandingPad(catch, lpad_ty, personality, 1);
let func = llvm::get_param(rust_try, 0);
let data = llvm::get_param(rust_try, 1);
Invoke(bcx, func, &[data], then.llbb, catch.llbb, None, dloc);
Ret(then, C_null(Type::i8p(ccx)), dloc);

// Type indicator for the exception being thrown.
// The first value in this tuple is a pointer to the exception object being thrown.
// The second value is a "selector" indicating which of the landing pad clauses
// the exception's type had been matched to. rust_try ignores the selector.
let lpad_ty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)],
false);
let vals = LandingPad(catch, lpad_ty, catch_pers, 1);
AddClause(catch, vals, C_null(Type::i8p(ccx)));
let ptr = ExtractValue(catch, vals, 0);
let valid = ICmp(catch, llvm::IntNE, ptr, C_null(Type::i8p(ccx)), dloc);
CondBr(catch, valid, catch_return.llbb, reraise.llbb, dloc);
Ret(catch_return, ptr, dloc);
Resume(reraise, vals);
}
Ret(catch, ptr, dloc);
fcx.cleanup();

return rust_try
});

// Note that no invoke is used here because by definition this function
// can't panic (that's what it's catching).
let ret = Call(bcx, llfn, &[func, data], None, dloc);
Store(bcx, ret, dest);
return bcx;
}

// Helper to generate the `Ty` associated with `rust_Try`
// Helper to generate the `Ty` associated with `rust_try`
fn get_rust_try_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
f: &mut FnMut(Ty<'tcx>,
ty::FnOutput<'tcx>) -> ValueRef)
Expand All @@ -1299,8 +1243,7 @@ fn get_rust_try_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
return llfn
}

// Define the types up front for the signatures of the rust_try and
// rust_try_inner functions.
// Define the type up front for the signature of the rust_try function.
let tcx = ccx.tcx();
let i8p = tcx.mk_mut_ptr(tcx.types.i8);
let fn_ty = tcx.mk_bare_fn(ty::BareFnTy {
Expand Down

0 comments on commit ceded6a

Please sign in to comment.