Skip to content

Commit

Permalink
trans: Reimplement unwinding on MSVC
Browse files Browse the repository at this point in the history
This commit transitions the compiler to using the new exception handling
instructions in LLVM for implementing unwinding for MSVC. This affects both 32
and 64-bit MSVC as they're both now using SEH-based strategies. In terms of
standard library support, lots more details about how SEH unwinding is
implemented can be found in the commits.

In terms of trans, this change necessitated a few modifications:

* Branches were added to detect when the old landingpad instruction is used or
  the new cleanuppad instruction is used to `trans::cleanup`.
* The return value from `cleanuppad` is not stored in an `alloca` (because it
  cannot be).
* Each block in trans now has an `Option<LandingPad>` instead of `is_lpad: bool`
  for indicating whether it's in a landing pad or not. The new exception
  handling intrinsics require that on MSVC each `call` inside of a landing pad
  is annotated with which landing pad that it's in. This change to the basic
  block means that whenever a `call` or `invoke` instruction is generated we
  know whether to annotate it as part of a cleanuppad or not.
* Lots of modifications were made to the instruction builders to construct the
  new instructions as well as pass the tagging information for the call/invoke
  instructions.
* The translation of the `try` intrinsics for MSVC has been overhauled to use
  the new `catchpad` instruction. The filter function is now also a
  rustc-generated function instead of a purely libstd-defined function. The
  libstd definition still exists, it just has a stable ABI across architectures
  and leaves some of the really weird implementation details to the compiler
  (e.g. the `localescape` and `localrecover` intrinsics).
  • Loading branch information
alexcrichton committed Jan 30, 2016
1 parent d1cace1 commit 3e9589c
Show file tree
Hide file tree
Showing 21 changed files with 1,131 additions and 425 deletions.
12 changes: 10 additions & 2 deletions src/libcore/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,15 @@ extern "rust-intrinsic" {
pub fn discriminant_value<T>(v: &T) -> u64;

/// Rust's "try catch" construct which invokes the function pointer `f` with
/// the data pointer `data`, returning the exception payload if an exception
/// is thrown (aka the thread panics).
/// the data pointer `data`.
///
/// The third pointer is a target-specific data pointer which is filled in
/// with the specifics of the exception that occurred. For examples on Unix
/// platforms this is a `*mut *mut T` which is filled in by the compiler and
/// on MSVC it's `*mut [usize; 2]`. For more information see the compiler's
/// source as well as std's catch implementation.
#[cfg(not(stage0))]
pub fn try(f: fn(*mut u8), data: *mut u8, local_ptr: *mut u8) -> i32;
#[cfg(stage0)]
pub fn try(f: fn(*mut u8), data: *mut u8) -> *mut u8;
}
1 change: 0 additions & 1 deletion src/librustc_back/target/x86_64_pc_windows_msvc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use target::Target;
pub fn target() -> Target {
let mut base = super::windows_msvc_base::opts();
base.cpu = "x86-64".to_string();
base.custom_unwind_resume = true;

Target {
llvm_target: "x86_64-pc-windows-msvc".to_string(),
Expand Down
92 changes: 78 additions & 14 deletions src/librustc_llvm/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,9 @@ pub type SMDiagnosticRef = *mut SMDiagnostic_opaque;
#[allow(missing_copy_implementations)]
pub enum RustArchiveMember_opaque {}
pub type RustArchiveMemberRef = *mut RustArchiveMember_opaque;
#[allow(missing_copy_implementations)]
pub enum OperandBundleDef_opaque {}
pub type OperandBundleDefRef = *mut OperandBundleDef_opaque;

pub type DiagnosticHandler = unsafe extern "C" fn(DiagnosticInfoRef, *mut c_void);
pub type InlineAsmDiagHandler = unsafe extern "C" fn(SMDiagnosticRef, *const c_void, c_uint);
Expand Down Expand Up @@ -1149,14 +1152,15 @@ extern {
Addr: ValueRef,
NumDests: c_uint)
-> ValueRef;
pub fn LLVMBuildInvoke(B: BuilderRef,
Fn: ValueRef,
Args: *const ValueRef,
NumArgs: c_uint,
Then: BasicBlockRef,
Catch: BasicBlockRef,
Name: *const c_char)
-> ValueRef;
pub fn LLVMRustBuildInvoke(B: BuilderRef,
Fn: ValueRef,
Args: *const ValueRef,
NumArgs: c_uint,
Then: BasicBlockRef,
Catch: BasicBlockRef,
Bundle: OperandBundleDefRef,
Name: *const c_char)
-> ValueRef;
pub fn LLVMRustBuildLandingPad(B: BuilderRef,
Ty: TypeRef,
PersFn: ValueRef,
Expand All @@ -1167,6 +1171,31 @@ extern {
pub fn LLVMBuildResume(B: BuilderRef, Exn: ValueRef) -> ValueRef;
pub fn LLVMBuildUnreachable(B: BuilderRef) -> ValueRef;

pub fn LLVMRustBuildCleanupPad(B: BuilderRef,
ParentPad: ValueRef,
ArgCnt: c_uint,
Args: *const ValueRef,
Name: *const c_char) -> ValueRef;
pub fn LLVMRustBuildCleanupRet(B: BuilderRef,
CleanupPad: ValueRef,
UnwindBB: BasicBlockRef) -> ValueRef;
pub fn LLVMRustBuildCatchPad(B: BuilderRef,
ParentPad: ValueRef,
ArgCnt: c_uint,
Args: *const ValueRef,
Name: *const c_char) -> ValueRef;
pub fn LLVMRustBuildCatchRet(B: BuilderRef,
Pad: ValueRef,
BB: BasicBlockRef) -> ValueRef;
pub fn LLVMRustBuildCatchSwitch(Builder: BuilderRef,
ParentPad: ValueRef,
BB: BasicBlockRef,
NumHandlers: c_uint,
Name: *const c_char) -> ValueRef;
pub fn LLVMRustAddHandler(CatchSwitch: ValueRef,
Handler: BasicBlockRef);
pub fn LLVMRustSetPersonalityFn(B: BuilderRef, Pers: ValueRef);

/* Add a case to the switch instruction */
pub fn LLVMAddCase(Switch: ValueRef,
OnVal: ValueRef,
Expand Down Expand Up @@ -1476,12 +1505,13 @@ extern {
/* Miscellaneous instructions */
pub fn LLVMBuildPhi(B: BuilderRef, Ty: TypeRef, Name: *const c_char)
-> ValueRef;
pub fn LLVMBuildCall(B: BuilderRef,
Fn: ValueRef,
Args: *const ValueRef,
NumArgs: c_uint,
Name: *const c_char)
-> ValueRef;
pub fn LLVMRustBuildCall(B: BuilderRef,
Fn: ValueRef,
Args: *const ValueRef,
NumArgs: c_uint,
Bundle: OperandBundleDefRef,
Name: *const c_char)
-> ValueRef;
pub fn LLVMBuildSelect(B: BuilderRef,
If: ValueRef,
Then: ValueRef,
Expand Down Expand Up @@ -2126,6 +2156,12 @@ extern {
pub fn LLVMRustSetDataLayoutFromTargetMachine(M: ModuleRef,
TM: TargetMachineRef);
pub fn LLVMRustGetModuleDataLayout(M: ModuleRef) -> TargetDataRef;

pub fn LLVMRustBuildOperandBundleDef(Name: *const c_char,
Inputs: *const ValueRef,
NumInputs: c_uint)
-> OperandBundleDefRef;
pub fn LLVMRustFreeOperandBundleDef(Bundle: OperandBundleDefRef);
}

#[cfg(have_component_x86)]
Expand Down Expand Up @@ -2418,6 +2454,34 @@ pub fn last_error() -> Option<String> {
}
}

pub struct OperandBundleDef {
inner: OperandBundleDefRef,
}

impl OperandBundleDef {
pub fn new(name: &str, vals: &[ValueRef]) -> OperandBundleDef {
let name = CString::new(name).unwrap();
let def = unsafe {
LLVMRustBuildOperandBundleDef(name.as_ptr(),
vals.as_ptr(),
vals.len() as c_uint)
};
OperandBundleDef { inner: def }
}

pub fn raw(&self) -> OperandBundleDefRef {
self.inner
}
}

impl Drop for OperandBundleDef {
fn drop(&mut self) {
unsafe {
LLVMRustFreeOperandBundleDef(self.inner);
}
}
}

// The module containing the native LLVM dependencies, generated by the build system
// Note that this must come after the rustllvm extern declaration so that
// parts of LLVM that rustllvm depends on aren't thrown away by the linker.
Expand Down
8 changes: 4 additions & 4 deletions src/librustc_trans/back/archive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ impl<'a> ArchiveBuilder<'a> {
// all SYMDEF files as these are just magical placeholders which get
// re-created when we make a new archive anyway.
for file in archive.iter() {
let file = try!(file.map_err(string2io));
let file = try!(file.map_err(string_to_io_error));
if !is_relevant_child(&file) {
continue
}
Expand Down Expand Up @@ -455,7 +455,7 @@ impl<'a> ArchiveBuilder<'a> {
unsafe {
if let Some(archive) = self.src_archive() {
for child in archive.iter() {
let child = try!(child.map_err(string2io));
let child = try!(child.map_err(string_to_io_error));
let child_name = match child.name() {
Some(s) => s,
None => continue,
Expand Down Expand Up @@ -484,7 +484,7 @@ impl<'a> ArchiveBuilder<'a> {
}
Addition::Archive { archive, archive_name: _, mut skip } => {
for child in archive.iter() {
let child = try!(child.map_err(string2io));
let child = try!(child.map_err(string_to_io_error));
if !is_relevant_child(&child) {
continue
}
Expand Down Expand Up @@ -541,6 +541,6 @@ impl<'a> ArchiveBuilder<'a> {
}
}

fn string2io(s: String) -> io::Error {
fn string_to_io_error(s: String) -> io::Error {
io::Error::new(io::ErrorKind::Other, format!("bad archive: {}", s))
}
38 changes: 13 additions & 25 deletions src/librustc_trans/trans/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -958,23 +958,11 @@ pub fn invoke<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
/// currently uses SEH-ish unwinding with DWARF info tables to the side (same as
/// 64-bit MinGW) instead of "full SEH".
pub fn wants_msvc_seh(sess: &Session) -> bool {
sess.target.target.options.is_like_msvc && sess.target.target.arch == "x86"
sess.target.target.options.is_like_msvc
}

pub fn avoid_invoke(bcx: Block) -> bool {
// FIXME(#25869) currently SEH-based unwinding is pretty buggy in LLVM and
// is being overhauled as this is being written. Until that
// time such that upstream LLVM's implementation is more solid
// and we start binding it we need to skip invokes for any
// target which wants SEH-based unwinding.
if bcx.sess().no_landing_pads() || wants_msvc_seh(bcx.sess()) {
true
} else if bcx.is_lpad {
// Avoid using invoke if we are already inside a landing pad.
true
} else {
false
}
bcx.sess().no_landing_pads() || bcx.lpad.borrow().is_some()
}

pub fn need_invoke(bcx: Block) -> bool {
Expand Down Expand Up @@ -1122,10 +1110,9 @@ pub fn init_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, local: &hir::Local) -> Blo
}

pub fn raw_block<'blk, 'tcx>(fcx: &'blk FunctionContext<'blk, 'tcx>,
is_lpad: bool,
llbb: BasicBlockRef)
-> Block<'blk, 'tcx> {
common::BlockS::new(llbb, is_lpad, None, fcx)
common::BlockS::new(llbb, None, fcx)
}

pub fn with_cond<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>, val: ValueRef, f: F) -> Block<'blk, 'tcx>
Expand Down Expand Up @@ -1298,7 +1285,7 @@ fn memfill<'a, 'tcx>(b: &Builder<'a, 'tcx>, llptr: ValueRef, ty: Ty<'tcx>, byte:
let volatile = C_bool(ccx, false);
b.call(llintrinsicfn,
&[llptr, llzeroval, size, align, volatile],
None);
None, None);
}

/// In general, when we create an scratch value in an alloca, the
Expand Down Expand Up @@ -1372,7 +1359,7 @@ pub fn alloca_dropped<'blk, 'tcx>(cx: Block<'blk, 'tcx>, ty: Ty<'tcx>, name: &st
// Block, which we do not have for `alloca_insert_pt`).
core_lifetime_emit(cx.ccx(), p, Lifetime::Start, |ccx, size, lifetime_start| {
let ptr = b.pointercast(p, Type::i8p(ccx));
b.call(lifetime_start, &[C_u64(ccx, size), ptr], None);
b.call(lifetime_start, &[C_u64(ccx, size), ptr], None, None);
});
memfill(&b, p, ty, adt::DTOR_DONE);
p
Expand Down Expand Up @@ -1594,7 +1581,7 @@ pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
alloca_insert_pt: Cell::new(None),
llreturn: Cell::new(None),
needs_ret_allocas: nested_returns,
personality: Cell::new(None),
landingpad_alloca: Cell::new(None),
caller_expects_out_pointer: uses_outptr,
lllocals: RefCell::new(NodeMap()),
llupvars: RefCell::new(NodeMap()),
Expand Down Expand Up @@ -1873,7 +1860,7 @@ pub fn finish_fn<'blk, 'tcx>(fcx: &'blk FunctionContext<'blk, 'tcx>,
if !last_bcx.terminated.get() {
Br(last_bcx, llreturn, DebugLoc::None);
}
raw_block(fcx, false, llreturn)
raw_block(fcx, llreturn)
}
None => last_bcx,
};
Expand Down Expand Up @@ -2663,11 +2650,12 @@ pub fn create_entry_wrapper(ccx: &CrateContext, sp: Span, main_llfn: ValueRef) {
(rust_main, args)
};

let result = llvm::LLVMBuildCall(bld,
start_fn,
args.as_ptr(),
args.len() as c_uint,
noname());
let result = llvm::LLVMRustBuildCall(bld,
start_fn,
args.as_ptr(),
args.len() as c_uint,
0 as *mut _,
noname());

llvm::LLVMBuildRet(bld, result);
}
Expand Down
59 changes: 56 additions & 3 deletions src/librustc_trans/trans/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,9 @@ pub fn Invoke(cx: Block,
cx.val_to_string(fn_),
args.iter().map(|a| cx.val_to_string(*a)).collect::<Vec<String>>().join(", "));
debug_loc.apply(cx.fcx);
B(cx).invoke(fn_, args, then, catch, attributes)
let lpad = cx.lpad.borrow();
let bundle = lpad.as_ref().and_then(|b| b.bundle());
B(cx).invoke(fn_, args, then, catch, bundle, attributes)
}

pub fn Unreachable(cx: Block) {
Expand Down Expand Up @@ -914,7 +916,9 @@ pub fn Call(cx: Block,
return _UndefReturn(cx, fn_);
}
debug_loc.apply(cx.fcx);
B(cx).call(fn_, args, attributes)
let lpad = cx.lpad.borrow();
let bundle = lpad.as_ref().and_then(|b| b.bundle());
B(cx).call(fn_, args, bundle, attributes)
}

pub fn CallWithConv(cx: Block,
Expand All @@ -928,7 +932,9 @@ pub fn CallWithConv(cx: Block,
return _UndefReturn(cx, fn_);
}
debug_loc.apply(cx.fcx);
B(cx).call_with_conv(fn_, args, conv, attributes)
let lpad = cx.lpad.borrow();
let bundle = lpad.as_ref().and_then(|b| b.bundle());
B(cx).call_with_conv(fn_, args, conv, bundle, attributes)
}

pub fn AtomicFence(cx: Block, order: AtomicOrdering, scope: SynchronizationScope) {
Expand Down Expand Up @@ -1050,6 +1056,10 @@ pub fn SetCleanup(cx: Block, landing_pad: ValueRef) {
B(cx).set_cleanup(landing_pad)
}

pub fn SetPersonalityFn(cx: Block, f: ValueRef) {
B(cx).set_personality_fn(f)
}

pub fn Resume(cx: Block, exn: ValueRef) -> ValueRef {
check_not_terminated(cx);
terminate(cx, "Resume");
Expand All @@ -1068,3 +1078,46 @@ pub fn AtomicRMW(cx: Block, op: AtomicBinOp,
order: AtomicOrdering) -> ValueRef {
B(cx).atomic_rmw(op, dst, src, order)
}

pub fn CleanupPad(cx: Block,
parent: Option<ValueRef>,
args: &[ValueRef]) -> ValueRef {
check_not_terminated(cx);
assert!(!cx.unreachable.get());
B(cx).cleanup_pad(parent, args)
}

pub fn CleanupRet(cx: Block,
cleanup: ValueRef,
unwind: Option<BasicBlockRef>) -> ValueRef {
check_not_terminated(cx);
terminate(cx, "CleanupRet");
B(cx).cleanup_ret(cleanup, unwind)
}

pub fn CatchPad(cx: Block,
parent: ValueRef,
args: &[ValueRef]) -> ValueRef {
check_not_terminated(cx);
assert!(!cx.unreachable.get());
B(cx).catch_pad(parent, args)
}

pub fn CatchRet(cx: Block, pad: ValueRef, unwind: BasicBlockRef) -> ValueRef {
check_not_terminated(cx);
terminate(cx, "CatchRet");
B(cx).catch_ret(pad, unwind)
}

pub fn CatchSwitch(cx: Block,
parent: Option<ValueRef>,
unwind: Option<BasicBlockRef>,
num_handlers: usize) -> ValueRef {
check_not_terminated(cx);
terminate(cx, "CatchSwitch");
B(cx).catch_switch(parent, unwind, num_handlers)
}

pub fn AddHandler(cx: Block, catch_switch: ValueRef, handler: BasicBlockRef) {
B(cx).add_handler(catch_switch, handler)
}
Loading

0 comments on commit 3e9589c

Please sign in to comment.