Skip to content

Commit ede6591

Browse files
committed
Implement exception handling support
1 parent b2cafc9 commit ede6591

File tree

8 files changed

+675
-58
lines changed

8 files changed

+675
-58
lines changed

src/abi/mod.rs

Lines changed: 151 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ mod returning;
77
use std::borrow::Cow;
88
use std::mem;
99

10-
use cranelift_codegen::ir::{ArgumentPurpose, SigRef};
10+
use cranelift_codegen::ir::{ArgumentPurpose, BlockArg, ExceptionTableData, ExceptionTag, SigRef};
1111
use cranelift_codegen::isa::CallConv;
1212
use cranelift_module::ModuleError;
1313
use rustc_abi::{CanonAbi, ExternAbi, X86Call};
@@ -20,10 +20,12 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
2020
use rustc_session::Session;
2121
use rustc_span::source_map::Spanned;
2222
use rustc_target::callconv::{FnAbi, PassMode};
23-
use smallvec::SmallVec;
23+
use smallvec::{SmallVec, smallvec};
2424

2525
use self::pass_mode::*;
2626
pub(crate) use self::returning::codegen_return;
27+
use crate::base::codegen_unwind_terminate;
28+
use crate::debuginfo::EXCEPTION_HANDLER_CLEANUP;
2729
use crate::prelude::*;
2830

2931
fn clif_sig_from_fn_abi<'tcx>(
@@ -380,7 +382,7 @@ pub(crate) fn codegen_terminator_call<'tcx>(
380382
args: &[Spanned<Operand<'tcx>>],
381383
destination: Place<'tcx>,
382384
target: Option<BasicBlock>,
383-
_unwind: UnwindAction,
385+
unwind: UnwindAction,
384386
) {
385387
let func = codegen_operand(fx, func);
386388
let fn_sig = func.layout().ty.fn_sig(fx.tcx);
@@ -515,12 +517,6 @@ pub(crate) fn codegen_terminator_call<'tcx>(
515517
let args = args;
516518
assert_eq!(fn_abi.args.len(), args.len());
517519

518-
#[derive(Copy, Clone)]
519-
enum CallTarget {
520-
Direct(FuncRef),
521-
Indirect(SigRef, Value),
522-
}
523-
524520
let (func_ref, first_arg_override) = match instance {
525521
// Trait object call
526522
Some(Instance { def: InstanceKind::Virtual(_, idx), .. }) => {
@@ -582,18 +578,12 @@ pub(crate) fn codegen_terminator_call<'tcx>(
582578
adjust_call_for_c_variadic(fx, &fn_abi, source_info, func_ref, &mut call_args);
583579
}
584580

585-
let call_inst = match func_ref {
586-
CallTarget::Direct(func_ref) => fx.bcx.ins().call(func_ref, &call_args),
587-
CallTarget::Indirect(sig, func_ptr) => {
588-
fx.bcx.ins().call_indirect(sig, func_ptr, &call_args)
589-
}
590-
};
591-
592581
if fx.clif_comments.enabled() {
593-
with_no_trimmed_paths!(fx.add_comment(call_inst, format!("abi: {:?}", fn_abi)));
582+
let nop_inst = fx.bcx.ins().nop();
583+
with_no_trimmed_paths!(fx.add_post_comment(nop_inst, format!("abi: {:?}", fn_abi)));
594584
}
595585

596-
fx.bcx.func.dfg.inst_results(call_inst).iter().copied().collect::<SmallVec<[Value; 2]>>()
586+
codegen_call_with_unwind_action(fx, source_info.span, func_ref, unwind, &call_args, None)
597587
});
598588

599589
if let Some(dest) = target {
@@ -703,7 +693,7 @@ pub(crate) fn codegen_drop<'tcx>(
703693
source_info: mir::SourceInfo,
704694
drop_place: CPlace<'tcx>,
705695
target: BasicBlock,
706-
_unwind: UnwindAction,
696+
unwind: UnwindAction,
707697
) {
708698
let ty = drop_place.layout().ty;
709699
let drop_instance = Instance::resolve_drop_in_place(fx.tcx, ty);
@@ -749,9 +739,14 @@ pub(crate) fn codegen_drop<'tcx>(
749739

750740
let sig = clif_sig_from_fn_abi(fx.tcx, fx.target_config.default_call_conv, &fn_abi);
751741
let sig = fx.bcx.import_signature(sig);
752-
// FIXME implement cleanup on exceptions
753-
fx.bcx.ins().call_indirect(sig, drop_fn, &[ptr]);
754-
fx.bcx.ins().jump(ret_block, &[]);
742+
codegen_call_with_unwind_action(
743+
fx,
744+
source_info.span,
745+
CallTarget::Indirect(sig, drop_fn),
746+
unwind,
747+
&[ptr],
748+
Some(ret_block),
749+
);
755750
}
756751
ty::Dynamic(_, _, ty::DynStar) => {
757752
// IN THIS ARM, WE HAVE:
@@ -794,9 +789,14 @@ pub(crate) fn codegen_drop<'tcx>(
794789

795790
let sig = clif_sig_from_fn_abi(fx.tcx, fx.target_config.default_call_conv, &fn_abi);
796791
let sig = fx.bcx.import_signature(sig);
797-
fx.bcx.ins().call_indirect(sig, drop_fn, &[data]);
798-
// FIXME implement cleanup on exceptions
799-
fx.bcx.ins().jump(ret_block, &[]);
792+
codegen_call_with_unwind_action(
793+
fx,
794+
source_info.span,
795+
CallTarget::Indirect(sig, drop_fn),
796+
unwind,
797+
&[data],
798+
Some(ret_block),
799+
);
800800
}
801801
_ => {
802802
assert!(!matches!(drop_instance.def, InstanceKind::Virtual(_, _)));
@@ -821,9 +821,132 @@ pub(crate) fn codegen_drop<'tcx>(
821821
}
822822

823823
let func_ref = fx.get_function_ref(drop_instance);
824-
fx.bcx.ins().call(func_ref, &call_args);
825-
// FIXME implement cleanup on exceptions
826-
fx.bcx.ins().jump(ret_block, &[]);
824+
codegen_call_with_unwind_action(
825+
fx,
826+
source_info.span,
827+
CallTarget::Direct(func_ref),
828+
unwind,
829+
&call_args,
830+
Some(ret_block),
831+
);
832+
}
833+
}
834+
}
835+
}
836+
837+
#[derive(Copy, Clone)]
838+
pub(crate) enum CallTarget {
839+
Direct(FuncRef),
840+
Indirect(SigRef, Value),
841+
}
842+
843+
pub(crate) fn codegen_call_with_unwind_action(
844+
fx: &mut FunctionCx<'_, '_, '_>,
845+
span: Span,
846+
func_ref: CallTarget,
847+
unwind: UnwindAction,
848+
call_args: &[Value],
849+
target_block: Option<Block>,
850+
) -> SmallVec<[Value; 2]> {
851+
let sig_ref = match func_ref {
852+
CallTarget::Direct(func_ref) => fx.bcx.func.dfg.ext_funcs[func_ref].signature,
853+
CallTarget::Indirect(sig_ref, _func_ptr) => sig_ref,
854+
};
855+
856+
if target_block.is_some() {
857+
assert!(fx.bcx.func.dfg.signatures[sig_ref].returns.is_empty());
858+
}
859+
match unwind {
860+
UnwindAction::Continue | UnwindAction::Unreachable => {
861+
let call_inst = match func_ref {
862+
CallTarget::Direct(func_ref) => fx.bcx.ins().call(func_ref, &call_args),
863+
CallTarget::Indirect(sig, func_ptr) => {
864+
fx.bcx.ins().call_indirect(sig, func_ptr, &call_args)
865+
}
866+
};
867+
868+
if let Some(target_block) = target_block {
869+
fx.bcx.ins().jump(target_block, &[]);
870+
smallvec![]
871+
} else {
872+
fx.bcx
873+
.func
874+
.dfg
875+
.inst_results(call_inst)
876+
.iter()
877+
.copied()
878+
.collect::<SmallVec<[Value; 2]>>()
879+
}
880+
}
881+
UnwindAction::Cleanup(_) | UnwindAction::Terminate(_) => {
882+
let returns_types = fx.bcx.func.dfg.signatures[sig_ref]
883+
.returns
884+
.iter()
885+
.map(|return_param| return_param.value_type)
886+
.collect::<Vec<_>>();
887+
888+
let fallthrough_block = fx.bcx.create_block();
889+
let fallthrough_block_call_args = returns_types
890+
.iter()
891+
.enumerate()
892+
.map(|(i, _)| BlockArg::TryCallRet(i.try_into().unwrap()))
893+
.collect::<Vec<_>>();
894+
let fallthrough_block_call = fx.bcx.func.dfg.block_call(
895+
target_block.unwrap_or(fallthrough_block),
896+
&fallthrough_block_call_args,
897+
);
898+
let pre_cleanup_block = fx.bcx.create_block();
899+
let pre_cleanup_block_call =
900+
fx.bcx.func.dfg.block_call(pre_cleanup_block, &[BlockArg::TryCallExn(0)]);
901+
let exception_table = fx.bcx.func.dfg.exception_tables.push(ExceptionTableData::new(
902+
sig_ref,
903+
fallthrough_block_call,
904+
[(
905+
Some(ExceptionTag::with_number(EXCEPTION_HANDLER_CLEANUP).unwrap()),
906+
pre_cleanup_block_call,
907+
)],
908+
));
909+
910+
match func_ref {
911+
CallTarget::Direct(func_ref) => {
912+
fx.bcx.ins().try_call(func_ref, &call_args, exception_table);
913+
}
914+
CallTarget::Indirect(_sig, func_ptr) => {
915+
fx.bcx.ins().try_call_indirect(func_ptr, &call_args, exception_table);
916+
}
917+
}
918+
919+
fx.bcx.seal_block(pre_cleanup_block);
920+
fx.bcx.switch_to_block(pre_cleanup_block);
921+
fx.bcx.set_cold_block(pre_cleanup_block);
922+
match unwind {
923+
UnwindAction::Continue | UnwindAction::Unreachable => unreachable!(),
924+
UnwindAction::Cleanup(cleanup) => {
925+
let exception_ptr =
926+
fx.bcx.append_block_param(pre_cleanup_block, fx.pointer_type);
927+
fx.bcx.def_var(fx.exception_slot, exception_ptr);
928+
let cleanup_block = fx.get_block(cleanup);
929+
fx.bcx.ins().jump(cleanup_block, &[]);
930+
}
931+
UnwindAction::Terminate(reason) => {
932+
// FIXME dedup terminate blocks
933+
fx.bcx.append_block_param(pre_cleanup_block, fx.pointer_type);
934+
935+
codegen_unwind_terminate(fx, span, reason);
936+
}
937+
}
938+
939+
if target_block.is_none() {
940+
fx.bcx.seal_block(fallthrough_block);
941+
fx.bcx.switch_to_block(fallthrough_block);
942+
let returns = returns_types
943+
.into_iter()
944+
.map(|ty| fx.bcx.append_block_param(fallthrough_block, ty))
945+
.collect();
946+
fx.bcx.ins().nop();
947+
returns
948+
} else {
949+
smallvec![]
827950
}
828951
}
829952
}

src/base.rs

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use cranelift_codegen::CodegenError;
44
use cranelift_codegen::ir::UserFuncName;
5-
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
5+
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
66
use cranelift_module::ModuleError;
77
use rustc_ast::InlineAsmOptions;
88
use rustc_codegen_ssa::base::is_call_from_compiler_builtins_to_upstream_monomorphization;
@@ -85,6 +85,9 @@ pub(crate) fn codegen_fn<'tcx>(
8585
None
8686
};
8787

88+
let exception_slot = Variable::from_u32(0);
89+
bcx.declare_var(exception_slot, pointer_type);
90+
8891
let mut fx = FunctionCx {
8992
cx,
9093
module,
@@ -103,9 +106,10 @@ pub(crate) fn codegen_fn<'tcx>(
103106
block_map,
104107
local_map: IndexVec::with_capacity(mir.local_decls.len()),
105108
caller_location: None, // set by `codegen_fn_prelude`
109+
exception_slot,
106110

107111
clif_comments,
108-
next_ssa_var: 0,
112+
next_ssa_var: 1, // var0 is used for the exception slot
109113
inline_asm: String::new(),
110114
inline_asm_index: 0,
111115
};
@@ -304,11 +308,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
304308
}
305309

306310
if bb_data.is_cleanup {
307-
// Unwinding after panicking is not supported
308-
continue;
309-
310-
// FIXME Once unwinding is supported and Cranelift supports marking blocks as cold, do
311-
// so for cleanup blocks.
311+
fx.bcx.set_cold_block(block);
312312
}
313313

314314
fx.bcx.ins().nop();
@@ -542,7 +542,13 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
542542
codegen_unwind_terminate(fx, source_info.span, *reason);
543543
}
544544
TerminatorKind::UnwindResume => {
545-
// FIXME implement unwinding
545+
let exception_ptr = fx.bcx.use_var(fx.exception_slot);
546+
fx.lib_call(
547+
"_Unwind_Resume",
548+
vec![AbiParam::new(fx.pointer_type)],
549+
vec![],
550+
&[exception_ptr],
551+
);
546552
fx.bcx.ins().trap(TrapCode::user(1 /* unreachable */).unwrap());
547553
}
548554
TerminatorKind::Unreachable => {
@@ -1109,7 +1115,7 @@ fn codegen_panic_inner<'tcx>(
11091115
fx: &mut FunctionCx<'_, '_, 'tcx>,
11101116
lang_item: rustc_hir::LangItem,
11111117
args: &[Value],
1112-
_unwind: UnwindAction,
1118+
unwind: UnwindAction,
11131119
span: Span,
11141120
) {
11151121
fx.bcx.set_cold_block(fx.bcx.current_block().unwrap());
@@ -1125,14 +1131,23 @@ fn codegen_panic_inner<'tcx>(
11251131

11261132
let symbol_name = fx.tcx.symbol_name(instance).name;
11271133

1128-
// FIXME implement cleanup on exceptions
1134+
let sig = Signature {
1135+
params: args.iter().map(|&arg| AbiParam::new(fx.bcx.func.dfg.value_type(arg))).collect(),
1136+
returns: vec![],
1137+
call_conv: fx.target_config.default_call_conv,
1138+
};
1139+
let func_id = fx.module.declare_function(symbol_name, Linkage::Import, &sig).unwrap();
1140+
let func_ref = fx.module.declare_func_in_func(func_id, &mut fx.bcx.func);
1141+
if fx.clif_comments.enabled() {
1142+
fx.add_comment(func_ref, format!("{:?}", symbol_name));
1143+
}
1144+
1145+
let nop_inst = fx.bcx.ins().nop();
1146+
if fx.clif_comments.enabled() {
1147+
fx.add_comment(nop_inst, format!("panic {}", symbol_name));
1148+
}
11291149

1130-
fx.lib_call(
1131-
symbol_name,
1132-
args.iter().map(|&arg| AbiParam::new(fx.bcx.func.dfg.value_type(arg))).collect(),
1133-
vec![],
1134-
args,
1135-
);
1150+
codegen_call_with_unwind_action(fx, span, CallTarget::Direct(func_ref), unwind, &args, None);
11361151

11371152
fx.bcx.ins().trap(TrapCode::user(1 /* unreachable */).unwrap());
11381153
}

src/common.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use cranelift_codegen::isa::TargetFrontendConfig;
2-
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
2+
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
33
use rustc_abi::{Float, Integer, Primitive};
44
use rustc_index::IndexVec;
55
use rustc_middle::ty::TypeFoldable;
@@ -288,6 +288,9 @@ pub(crate) struct FunctionCx<'m, 'clif, 'tcx: 'm> {
288288
/// When `#[track_caller]` is used, the implicit caller location is stored in this variable.
289289
pub(crate) caller_location: Option<CValue<'tcx>>,
290290

291+
/// During cleanup the exception pointer will be stored in this variable.
292+
pub(crate) exception_slot: Variable,
293+
291294
pub(crate) clif_comments: crate::pretty_clif::CommentWriter,
292295

293296
/// This should only be accessed by `CPlace::new_var`.

0 commit comments

Comments
 (0)