Skip to content

Commit 3e9589c

Browse files
committed
trans: Reimplement unwinding on MSVC
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).
1 parent d1cace1 commit 3e9589c

File tree

21 files changed

+1131
-425
lines changed

21 files changed

+1131
-425
lines changed

src/libcore/intrinsics.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,15 @@ extern "rust-intrinsic" {
552552
pub fn discriminant_value<T>(v: &T) -> u64;
553553

554554
/// Rust's "try catch" construct which invokes the function pointer `f` with
555-
/// the data pointer `data`, returning the exception payload if an exception
556-
/// is thrown (aka the thread panics).
555+
/// the data pointer `data`.
556+
///
557+
/// The third pointer is a target-specific data pointer which is filled in
558+
/// with the specifics of the exception that occurred. For examples on Unix
559+
/// platforms this is a `*mut *mut T` which is filled in by the compiler and
560+
/// on MSVC it's `*mut [usize; 2]`. For more information see the compiler's
561+
/// source as well as std's catch implementation.
562+
#[cfg(not(stage0))]
563+
pub fn try(f: fn(*mut u8), data: *mut u8, local_ptr: *mut u8) -> i32;
564+
#[cfg(stage0)]
557565
pub fn try(f: fn(*mut u8), data: *mut u8) -> *mut u8;
558566
}

src/librustc_back/target/x86_64_pc_windows_msvc.rs

-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ use target::Target;
1313
pub fn target() -> Target {
1414
let mut base = super::windows_msvc_base::opts();
1515
base.cpu = "x86-64".to_string();
16-
base.custom_unwind_resume = true;
1716

1817
Target {
1918
llvm_target: "x86_64-pc-windows-msvc".to_string(),

src/librustc_llvm/lib.rs

+78-14
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,9 @@ pub type SMDiagnosticRef = *mut SMDiagnostic_opaque;
544544
#[allow(missing_copy_implementations)]
545545
pub enum RustArchiveMember_opaque {}
546546
pub type RustArchiveMemberRef = *mut RustArchiveMember_opaque;
547+
#[allow(missing_copy_implementations)]
548+
pub enum OperandBundleDef_opaque {}
549+
pub type OperandBundleDefRef = *mut OperandBundleDef_opaque;
547550

548551
pub type DiagnosticHandler = unsafe extern "C" fn(DiagnosticInfoRef, *mut c_void);
549552
pub type InlineAsmDiagHandler = unsafe extern "C" fn(SMDiagnosticRef, *const c_void, c_uint);
@@ -1149,14 +1152,15 @@ extern {
11491152
Addr: ValueRef,
11501153
NumDests: c_uint)
11511154
-> ValueRef;
1152-
pub fn LLVMBuildInvoke(B: BuilderRef,
1153-
Fn: ValueRef,
1154-
Args: *const ValueRef,
1155-
NumArgs: c_uint,
1156-
Then: BasicBlockRef,
1157-
Catch: BasicBlockRef,
1158-
Name: *const c_char)
1159-
-> ValueRef;
1155+
pub fn LLVMRustBuildInvoke(B: BuilderRef,
1156+
Fn: ValueRef,
1157+
Args: *const ValueRef,
1158+
NumArgs: c_uint,
1159+
Then: BasicBlockRef,
1160+
Catch: BasicBlockRef,
1161+
Bundle: OperandBundleDefRef,
1162+
Name: *const c_char)
1163+
-> ValueRef;
11601164
pub fn LLVMRustBuildLandingPad(B: BuilderRef,
11611165
Ty: TypeRef,
11621166
PersFn: ValueRef,
@@ -1167,6 +1171,31 @@ extern {
11671171
pub fn LLVMBuildResume(B: BuilderRef, Exn: ValueRef) -> ValueRef;
11681172
pub fn LLVMBuildUnreachable(B: BuilderRef) -> ValueRef;
11691173

1174+
pub fn LLVMRustBuildCleanupPad(B: BuilderRef,
1175+
ParentPad: ValueRef,
1176+
ArgCnt: c_uint,
1177+
Args: *const ValueRef,
1178+
Name: *const c_char) -> ValueRef;
1179+
pub fn LLVMRustBuildCleanupRet(B: BuilderRef,
1180+
CleanupPad: ValueRef,
1181+
UnwindBB: BasicBlockRef) -> ValueRef;
1182+
pub fn LLVMRustBuildCatchPad(B: BuilderRef,
1183+
ParentPad: ValueRef,
1184+
ArgCnt: c_uint,
1185+
Args: *const ValueRef,
1186+
Name: *const c_char) -> ValueRef;
1187+
pub fn LLVMRustBuildCatchRet(B: BuilderRef,
1188+
Pad: ValueRef,
1189+
BB: BasicBlockRef) -> ValueRef;
1190+
pub fn LLVMRustBuildCatchSwitch(Builder: BuilderRef,
1191+
ParentPad: ValueRef,
1192+
BB: BasicBlockRef,
1193+
NumHandlers: c_uint,
1194+
Name: *const c_char) -> ValueRef;
1195+
pub fn LLVMRustAddHandler(CatchSwitch: ValueRef,
1196+
Handler: BasicBlockRef);
1197+
pub fn LLVMRustSetPersonalityFn(B: BuilderRef, Pers: ValueRef);
1198+
11701199
/* Add a case to the switch instruction */
11711200
pub fn LLVMAddCase(Switch: ValueRef,
11721201
OnVal: ValueRef,
@@ -1476,12 +1505,13 @@ extern {
14761505
/* Miscellaneous instructions */
14771506
pub fn LLVMBuildPhi(B: BuilderRef, Ty: TypeRef, Name: *const c_char)
14781507
-> ValueRef;
1479-
pub fn LLVMBuildCall(B: BuilderRef,
1480-
Fn: ValueRef,
1481-
Args: *const ValueRef,
1482-
NumArgs: c_uint,
1483-
Name: *const c_char)
1484-
-> ValueRef;
1508+
pub fn LLVMRustBuildCall(B: BuilderRef,
1509+
Fn: ValueRef,
1510+
Args: *const ValueRef,
1511+
NumArgs: c_uint,
1512+
Bundle: OperandBundleDefRef,
1513+
Name: *const c_char)
1514+
-> ValueRef;
14851515
pub fn LLVMBuildSelect(B: BuilderRef,
14861516
If: ValueRef,
14871517
Then: ValueRef,
@@ -2126,6 +2156,12 @@ extern {
21262156
pub fn LLVMRustSetDataLayoutFromTargetMachine(M: ModuleRef,
21272157
TM: TargetMachineRef);
21282158
pub fn LLVMRustGetModuleDataLayout(M: ModuleRef) -> TargetDataRef;
2159+
2160+
pub fn LLVMRustBuildOperandBundleDef(Name: *const c_char,
2161+
Inputs: *const ValueRef,
2162+
NumInputs: c_uint)
2163+
-> OperandBundleDefRef;
2164+
pub fn LLVMRustFreeOperandBundleDef(Bundle: OperandBundleDefRef);
21292165
}
21302166

21312167
#[cfg(have_component_x86)]
@@ -2418,6 +2454,34 @@ pub fn last_error() -> Option<String> {
24182454
}
24192455
}
24202456

2457+
pub struct OperandBundleDef {
2458+
inner: OperandBundleDefRef,
2459+
}
2460+
2461+
impl OperandBundleDef {
2462+
pub fn new(name: &str, vals: &[ValueRef]) -> OperandBundleDef {
2463+
let name = CString::new(name).unwrap();
2464+
let def = unsafe {
2465+
LLVMRustBuildOperandBundleDef(name.as_ptr(),
2466+
vals.as_ptr(),
2467+
vals.len() as c_uint)
2468+
};
2469+
OperandBundleDef { inner: def }
2470+
}
2471+
2472+
pub fn raw(&self) -> OperandBundleDefRef {
2473+
self.inner
2474+
}
2475+
}
2476+
2477+
impl Drop for OperandBundleDef {
2478+
fn drop(&mut self) {
2479+
unsafe {
2480+
LLVMRustFreeOperandBundleDef(self.inner);
2481+
}
2482+
}
2483+
}
2484+
24212485
// The module containing the native LLVM dependencies, generated by the build system
24222486
// Note that this must come after the rustllvm extern declaration so that
24232487
// parts of LLVM that rustllvm depends on aren't thrown away by the linker.

src/librustc_trans/back/archive.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ impl<'a> ArchiveBuilder<'a> {
334334
// all SYMDEF files as these are just magical placeholders which get
335335
// re-created when we make a new archive anyway.
336336
for file in archive.iter() {
337-
let file = try!(file.map_err(string2io));
337+
let file = try!(file.map_err(string_to_io_error));
338338
if !is_relevant_child(&file) {
339339
continue
340340
}
@@ -455,7 +455,7 @@ impl<'a> ArchiveBuilder<'a> {
455455
unsafe {
456456
if let Some(archive) = self.src_archive() {
457457
for child in archive.iter() {
458-
let child = try!(child.map_err(string2io));
458+
let child = try!(child.map_err(string_to_io_error));
459459
let child_name = match child.name() {
460460
Some(s) => s,
461461
None => continue,
@@ -484,7 +484,7 @@ impl<'a> ArchiveBuilder<'a> {
484484
}
485485
Addition::Archive { archive, archive_name: _, mut skip } => {
486486
for child in archive.iter() {
487-
let child = try!(child.map_err(string2io));
487+
let child = try!(child.map_err(string_to_io_error));
488488
if !is_relevant_child(&child) {
489489
continue
490490
}
@@ -541,6 +541,6 @@ impl<'a> ArchiveBuilder<'a> {
541541
}
542542
}
543543

544-
fn string2io(s: String) -> io::Error {
544+
fn string_to_io_error(s: String) -> io::Error {
545545
io::Error::new(io::ErrorKind::Other, format!("bad archive: {}", s))
546546
}

src/librustc_trans/trans/base.rs

+13-25
Original file line numberDiff line numberDiff line change
@@ -958,23 +958,11 @@ pub fn invoke<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
958958
/// currently uses SEH-ish unwinding with DWARF info tables to the side (same as
959959
/// 64-bit MinGW) instead of "full SEH".
960960
pub fn wants_msvc_seh(sess: &Session) -> bool {
961-
sess.target.target.options.is_like_msvc && sess.target.target.arch == "x86"
961+
sess.target.target.options.is_like_msvc
962962
}
963963

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

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

11241112
pub fn raw_block<'blk, 'tcx>(fcx: &'blk FunctionContext<'blk, 'tcx>,
1125-
is_lpad: bool,
11261113
llbb: BasicBlockRef)
11271114
-> Block<'blk, 'tcx> {
1128-
common::BlockS::new(llbb, is_lpad, None, fcx)
1115+
common::BlockS::new(llbb, None, fcx)
11291116
}
11301117

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

13041291
/// In general, when we create an scratch value in an alloca, the
@@ -1372,7 +1359,7 @@ pub fn alloca_dropped<'blk, 'tcx>(cx: Block<'blk, 'tcx>, ty: Ty<'tcx>, name: &st
13721359
// Block, which we do not have for `alloca_insert_pt`).
13731360
core_lifetime_emit(cx.ccx(), p, Lifetime::Start, |ccx, size, lifetime_start| {
13741361
let ptr = b.pointercast(p, Type::i8p(ccx));
1375-
b.call(lifetime_start, &[C_u64(ccx, size), ptr], None);
1362+
b.call(lifetime_start, &[C_u64(ccx, size), ptr], None, None);
13761363
});
13771364
memfill(&b, p, ty, adt::DTOR_DONE);
13781365
p
@@ -1594,7 +1581,7 @@ pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
15941581
alloca_insert_pt: Cell::new(None),
15951582
llreturn: Cell::new(None),
15961583
needs_ret_allocas: nested_returns,
1597-
personality: Cell::new(None),
1584+
landingpad_alloca: Cell::new(None),
15981585
caller_expects_out_pointer: uses_outptr,
15991586
lllocals: RefCell::new(NodeMap()),
16001587
llupvars: RefCell::new(NodeMap()),
@@ -1873,7 +1860,7 @@ pub fn finish_fn<'blk, 'tcx>(fcx: &'blk FunctionContext<'blk, 'tcx>,
18731860
if !last_bcx.terminated.get() {
18741861
Br(last_bcx, llreturn, DebugLoc::None);
18751862
}
1876-
raw_block(fcx, false, llreturn)
1863+
raw_block(fcx, llreturn)
18771864
}
18781865
None => last_bcx,
18791866
};
@@ -2663,11 +2650,12 @@ pub fn create_entry_wrapper(ccx: &CrateContext, sp: Span, main_llfn: ValueRef) {
26632650
(rust_main, args)
26642651
};
26652652

2666-
let result = llvm::LLVMBuildCall(bld,
2667-
start_fn,
2668-
args.as_ptr(),
2669-
args.len() as c_uint,
2670-
noname());
2653+
let result = llvm::LLVMRustBuildCall(bld,
2654+
start_fn,
2655+
args.as_ptr(),
2656+
args.len() as c_uint,
2657+
0 as *mut _,
2658+
noname());
26712659

26722660
llvm::LLVMBuildRet(bld, result);
26732661
}

src/librustc_trans/trans/build.rs

+56-3
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,9 @@ pub fn Invoke(cx: Block,
150150
cx.val_to_string(fn_),
151151
args.iter().map(|a| cx.val_to_string(*a)).collect::<Vec<String>>().join(", "));
152152
debug_loc.apply(cx.fcx);
153-
B(cx).invoke(fn_, args, then, catch, attributes)
153+
let lpad = cx.lpad.borrow();
154+
let bundle = lpad.as_ref().and_then(|b| b.bundle());
155+
B(cx).invoke(fn_, args, then, catch, bundle, attributes)
154156
}
155157

156158
pub fn Unreachable(cx: Block) {
@@ -914,7 +916,9 @@ pub fn Call(cx: Block,
914916
return _UndefReturn(cx, fn_);
915917
}
916918
debug_loc.apply(cx.fcx);
917-
B(cx).call(fn_, args, attributes)
919+
let lpad = cx.lpad.borrow();
920+
let bundle = lpad.as_ref().and_then(|b| b.bundle());
921+
B(cx).call(fn_, args, bundle, attributes)
918922
}
919923

920924
pub fn CallWithConv(cx: Block,
@@ -928,7 +932,9 @@ pub fn CallWithConv(cx: Block,
928932
return _UndefReturn(cx, fn_);
929933
}
930934
debug_loc.apply(cx.fcx);
931-
B(cx).call_with_conv(fn_, args, conv, attributes)
935+
let lpad = cx.lpad.borrow();
936+
let bundle = lpad.as_ref().and_then(|b| b.bundle());
937+
B(cx).call_with_conv(fn_, args, conv, bundle, attributes)
932938
}
933939

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

1059+
pub fn SetPersonalityFn(cx: Block, f: ValueRef) {
1060+
B(cx).set_personality_fn(f)
1061+
}
1062+
10531063
pub fn Resume(cx: Block, exn: ValueRef) -> ValueRef {
10541064
check_not_terminated(cx);
10551065
terminate(cx, "Resume");
@@ -1068,3 +1078,46 @@ pub fn AtomicRMW(cx: Block, op: AtomicBinOp,
10681078
order: AtomicOrdering) -> ValueRef {
10691079
B(cx).atomic_rmw(op, dst, src, order)
10701080
}
1081+
1082+
pub fn CleanupPad(cx: Block,
1083+
parent: Option<ValueRef>,
1084+
args: &[ValueRef]) -> ValueRef {
1085+
check_not_terminated(cx);
1086+
assert!(!cx.unreachable.get());
1087+
B(cx).cleanup_pad(parent, args)
1088+
}
1089+
1090+
pub fn CleanupRet(cx: Block,
1091+
cleanup: ValueRef,
1092+
unwind: Option<BasicBlockRef>) -> ValueRef {
1093+
check_not_terminated(cx);
1094+
terminate(cx, "CleanupRet");
1095+
B(cx).cleanup_ret(cleanup, unwind)
1096+
}
1097+
1098+
pub fn CatchPad(cx: Block,
1099+
parent: ValueRef,
1100+
args: &[ValueRef]) -> ValueRef {
1101+
check_not_terminated(cx);
1102+
assert!(!cx.unreachable.get());
1103+
B(cx).catch_pad(parent, args)
1104+
}
1105+
1106+
pub fn CatchRet(cx: Block, pad: ValueRef, unwind: BasicBlockRef) -> ValueRef {
1107+
check_not_terminated(cx);
1108+
terminate(cx, "CatchRet");
1109+
B(cx).catch_ret(pad, unwind)
1110+
}
1111+
1112+
pub fn CatchSwitch(cx: Block,
1113+
parent: Option<ValueRef>,
1114+
unwind: Option<BasicBlockRef>,
1115+
num_handlers: usize) -> ValueRef {
1116+
check_not_terminated(cx);
1117+
terminate(cx, "CatchSwitch");
1118+
B(cx).catch_switch(parent, unwind, num_handlers)
1119+
}
1120+
1121+
pub fn AddHandler(cx: Block, catch_switch: ValueRef, handler: BasicBlockRef) {
1122+
B(cx).add_handler(catch_switch, handler)
1123+
}

0 commit comments

Comments
 (0)