Skip to content

Commit c44026c

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 `landingpad` is no longer stored in an `alloca`, but rather held directly in an LLVM value. This is because the token from `cleanuppad` *cannot* be stored in an `alloca`, so the same strategy is used in both halves. * 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 a6612ce commit c44026c

File tree

17 files changed

+1001
-381
lines changed

17 files changed

+1001
-381
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
@@ -533,6 +533,9 @@ pub type SMDiagnosticRef = *mut SMDiagnostic_opaque;
533533
#[allow(missing_copy_implementations)]
534534
pub enum RustArchiveMember_opaque {}
535535
pub type RustArchiveMemberRef = *mut RustArchiveMember_opaque;
536+
#[allow(missing_copy_implementations)]
537+
pub enum OperandBundleDef_opaque {}
538+
pub type OperandBundleDefRef = *mut OperandBundleDef_opaque;
536539

537540
pub type DiagnosticHandler = unsafe extern "C" fn(DiagnosticInfoRef, *mut c_void);
538541
pub type InlineAsmDiagHandler = unsafe extern "C" fn(SMDiagnosticRef, *const c_void, c_uint);
@@ -1138,14 +1141,15 @@ extern {
11381141
Addr: ValueRef,
11391142
NumDests: c_uint)
11401143
-> ValueRef;
1141-
pub fn LLVMBuildInvoke(B: BuilderRef,
1142-
Fn: ValueRef,
1143-
Args: *const ValueRef,
1144-
NumArgs: c_uint,
1145-
Then: BasicBlockRef,
1146-
Catch: BasicBlockRef,
1147-
Name: *const c_char)
1148-
-> ValueRef;
1144+
pub fn LLVMRustBuildInvoke(B: BuilderRef,
1145+
Fn: ValueRef,
1146+
Args: *const ValueRef,
1147+
NumArgs: c_uint,
1148+
Then: BasicBlockRef,
1149+
Catch: BasicBlockRef,
1150+
Bundle: OperandBundleDefRef,
1151+
Name: *const c_char)
1152+
-> ValueRef;
11491153
pub fn LLVMRustBuildLandingPad(B: BuilderRef,
11501154
Ty: TypeRef,
11511155
PersFn: ValueRef,
@@ -1156,6 +1160,31 @@ extern {
11561160
pub fn LLVMBuildResume(B: BuilderRef, Exn: ValueRef) -> ValueRef;
11571161
pub fn LLVMBuildUnreachable(B: BuilderRef) -> ValueRef;
11581162

1163+
pub fn LLVMRustBuildCleanupPad(B: BuilderRef,
1164+
ParentPad: ValueRef,
1165+
ArgCnt: c_uint,
1166+
Args: *const ValueRef,
1167+
Name: *const c_char) -> ValueRef;
1168+
pub fn LLVMRustBuildCleanupRet(B: BuilderRef,
1169+
CleanupPad: ValueRef,
1170+
UnwindBB: BasicBlockRef) -> ValueRef;
1171+
pub fn LLVMRustBuildCatchPad(B: BuilderRef,
1172+
ParentPad: ValueRef,
1173+
ArgCnt: c_uint,
1174+
Args: *const ValueRef,
1175+
Name: *const c_char) -> ValueRef;
1176+
pub fn LLVMRustBuildCatchRet(B: BuilderRef,
1177+
Pad: ValueRef,
1178+
BB: BasicBlockRef) -> ValueRef;
1179+
pub fn LLVMRustBuildCatchSwitch(Builder: BuilderRef,
1180+
ParentPad: ValueRef,
1181+
BB: BasicBlockRef,
1182+
NumHandlers: c_uint,
1183+
Name: *const c_char) -> ValueRef;
1184+
pub fn LLVMRustAddHandler(CatchSwitch: ValueRef,
1185+
Handler: BasicBlockRef);
1186+
pub fn LLVMRustSetPersonalityFn(B: BuilderRef, Pers: ValueRef);
1187+
11591188
/* Add a case to the switch instruction */
11601189
pub fn LLVMAddCase(Switch: ValueRef,
11611190
OnVal: ValueRef,
@@ -1465,12 +1494,13 @@ extern {
14651494
/* Miscellaneous instructions */
14661495
pub fn LLVMBuildPhi(B: BuilderRef, Ty: TypeRef, Name: *const c_char)
14671496
-> ValueRef;
1468-
pub fn LLVMBuildCall(B: BuilderRef,
1469-
Fn: ValueRef,
1470-
Args: *const ValueRef,
1471-
NumArgs: c_uint,
1472-
Name: *const c_char)
1473-
-> ValueRef;
1497+
pub fn LLVMRustBuildCall(B: BuilderRef,
1498+
Fn: ValueRef,
1499+
Args: *const ValueRef,
1500+
NumArgs: c_uint,
1501+
Bundle: OperandBundleDefRef,
1502+
Name: *const c_char)
1503+
-> ValueRef;
14741504
pub fn LLVMBuildSelect(B: BuilderRef,
14751505
If: ValueRef,
14761506
Then: ValueRef,
@@ -2112,6 +2142,12 @@ extern {
21122142
pub fn LLVMRustSetDataLayoutFromTargetMachine(M: ModuleRef,
21132143
TM: TargetMachineRef);
21142144
pub fn LLVMRustGetModuleDataLayout(M: ModuleRef) -> TargetDataRef;
2145+
2146+
pub fn LLVMRustBuildOperandBundleDef(Name: *const c_char,
2147+
Inputs: *const ValueRef,
2148+
NumInputs: c_uint)
2149+
-> OperandBundleDefRef;
2150+
pub fn LLVMRustFreeOperandBundleDef(Bundle: OperandBundleDefRef);
21152151
}
21162152

21172153
#[cfg(have_component_x86)]
@@ -2404,6 +2440,34 @@ pub fn last_error() -> Option<String> {
24042440
}
24052441
}
24062442

2443+
pub struct OperandBundleDef {
2444+
inner: OperandBundleDefRef,
2445+
}
2446+
2447+
impl OperandBundleDef {
2448+
pub fn new(name: &str, vals: &[ValueRef]) -> OperandBundleDef {
2449+
let name = CString::new(name).unwrap();
2450+
let def = unsafe {
2451+
LLVMRustBuildOperandBundleDef(name.as_ptr(),
2452+
vals.as_ptr(),
2453+
vals.len() as c_uint)
2454+
};
2455+
OperandBundleDef { inner: def }
2456+
}
2457+
2458+
pub fn raw(&self) -> OperandBundleDefRef {
2459+
self.inner
2460+
}
2461+
}
2462+
2463+
impl Drop for OperandBundleDef {
2464+
fn drop(&mut self) {
2465+
unsafe {
2466+
LLVMRustFreeOperandBundleDef(self.inner);
2467+
}
2468+
}
2469+
}
2470+
24072471
// The module containing the native LLVM dependencies, generated by the build system
24082472
// Note that this must come after the rustllvm extern declaration so that
24092473
// parts of LLVM that rustllvm depends on aren't thrown away by the linker.

src/librustc_trans/trans/base.rs

+14-19
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ use trans::common::{Block, C_bool, C_bytes_in_context, C_i32, C_int, C_uint, C_i
5959
use trans::common::{C_null, C_struct_in_context, C_u64, C_u8, C_undef};
6060
use trans::common::{CrateContext, DropFlagHintsMap, Field, FunctionContext};
6161
use trans::common::{Result, NodeIdAndSpan, VariantInfo};
62-
use trans::common::{node_id_type, return_type_is_void};
62+
use trans::common::{node_id_type, return_type_is_void, LandingPad};
6363
use trans::common::{type_is_immediate, type_is_zero_size, val_ty};
6464
use trans::common;
6565
use trans::consts;
@@ -954,21 +954,16 @@ pub fn invoke<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
954954
/// currently uses SEH-ish unwinding with DWARF info tables to the side (same as
955955
/// 64-bit MinGW) instead of "full SEH".
956956
pub fn wants_msvc_seh(sess: &Session) -> bool {
957-
sess.target.target.options.is_like_msvc && sess.target.target.arch == "x86"
957+
sess.target.target.options.is_like_msvc
958958
}
959959

960960
pub fn need_invoke(bcx: Block) -> bool {
961-
// FIXME(#25869) currently SEH-based unwinding is pretty buggy in LLVM and
962-
// is being overhauled as this is being written. Until that
963-
// time such that upstream LLVM's implementation is more solid
964-
// and we start binding it we need to skip invokes for any
965-
// target which wants SEH-based unwinding.
966-
if bcx.sess().no_landing_pads() || wants_msvc_seh(bcx.sess()) {
961+
if bcx.sess().no_landing_pads() {
967962
return false;
968963
}
969964

970965
// Avoid using invoke if we are already inside a landing pad.
971-
if bcx.is_lpad {
966+
if bcx.lpad.is_some() {
972967
return false;
973968
}
974969

@@ -1112,10 +1107,10 @@ pub fn init_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, local: &hir::Local) -> Blo
11121107
}
11131108

11141109
pub fn raw_block<'blk, 'tcx>(fcx: &'blk FunctionContext<'blk, 'tcx>,
1115-
is_lpad: bool,
1110+
lpad: Option<LandingPad>,
11161111
llbb: BasicBlockRef)
11171112
-> Block<'blk, 'tcx> {
1118-
common::BlockS::new(llbb, is_lpad, None, fcx)
1113+
common::BlockS::new(llbb, lpad, None, fcx)
11191114
}
11201115

11211116
pub fn with_cond<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>, val: ValueRef, f: F) -> Block<'blk, 'tcx>
@@ -1273,7 +1268,7 @@ fn memfill<'a, 'tcx>(b: &Builder<'a, 'tcx>, llptr: ValueRef, ty: Ty<'tcx>, byte:
12731268
let volatile = C_bool(ccx, false);
12741269
b.call(llintrinsicfn,
12751270
&[llptr, llzeroval, size, align, volatile],
1276-
None);
1271+
None, None);
12771272
}
12781273

12791274
pub fn alloc_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, name: &str) -> ValueRef {
@@ -1500,7 +1495,6 @@ pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
15001495
alloca_insert_pt: Cell::new(None),
15011496
llreturn: Cell::new(None),
15021497
needs_ret_allocas: nested_returns,
1503-
personality: Cell::new(None),
15041498
caller_expects_out_pointer: uses_outptr,
15051499
lllocals: RefCell::new(NodeMap()),
15061500
llupvars: RefCell::new(NodeMap()),
@@ -1752,7 +1746,7 @@ pub fn finish_fn<'blk, 'tcx>(fcx: &'blk FunctionContext<'blk, 'tcx>,
17521746
if !last_bcx.terminated.get() {
17531747
Br(last_bcx, llreturn, DebugLoc::None);
17541748
}
1755-
raw_block(fcx, false, llreturn)
1749+
raw_block(fcx, None, llreturn)
17561750
}
17571751
None => last_bcx,
17581752
};
@@ -2537,11 +2531,12 @@ pub fn create_entry_wrapper(ccx: &CrateContext, sp: Span, main_llfn: ValueRef) {
25372531
(rust_main, args)
25382532
};
25392533

2540-
let result = llvm::LLVMBuildCall(bld,
2541-
start_fn,
2542-
args.as_ptr(),
2543-
args.len() as c_uint,
2544-
noname());
2534+
let result = llvm::LLVMRustBuildCall(bld,
2535+
start_fn,
2536+
args.as_ptr(),
2537+
args.len() as c_uint,
2538+
0 as *mut _,
2539+
noname());
25452540

25462541
llvm::LLVMBuildRet(bld, result);
25472542
}

src/librustc_trans/trans/build.rs

+53-3
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,8 @@ 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 bundle = cx.lpad.as_ref().and_then(|b| b.bundle());
154+
B(cx).invoke(fn_, args, then, catch, bundle, attributes)
154155
}
155156

156157
pub fn Unreachable(cx: Block) {
@@ -914,7 +915,8 @@ pub fn Call(cx: Block,
914915
return _UndefReturn(cx, fn_);
915916
}
916917
debug_loc.apply(cx.fcx);
917-
B(cx).call(fn_, args, attributes)
918+
let bundle = cx.lpad.as_ref().and_then(|b| b.bundle());
919+
B(cx).call(fn_, args, bundle, attributes)
918920
}
919921

920922
pub fn CallWithConv(cx: Block,
@@ -928,7 +930,8 @@ pub fn CallWithConv(cx: Block,
928930
return _UndefReturn(cx, fn_);
929931
}
930932
debug_loc.apply(cx.fcx);
931-
B(cx).call_with_conv(fn_, args, conv, attributes)
933+
let bundle = cx.lpad.as_ref().and_then(|b| b.bundle());
934+
B(cx).call_with_conv(fn_, args, conv, bundle, attributes)
932935
}
933936

934937
pub fn AtomicFence(cx: Block, order: AtomicOrdering, scope: SynchronizationScope) {
@@ -1050,6 +1053,10 @@ pub fn SetCleanup(cx: Block, landing_pad: ValueRef) {
10501053
B(cx).set_cleanup(landing_pad)
10511054
}
10521055

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

0 commit comments

Comments
 (0)