Skip to content

Commit c35820b

Browse files
committed
Implement exception handling support
1 parent d0ee10d commit c35820b

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;
@@ -83,6 +83,9 @@ pub(crate) fn codegen_fn<'tcx>(
8383
None
8484
};
8585

86+
let exception_slot = Variable::from_u32(0);
87+
bcx.declare_var(exception_slot, pointer_type);
88+
8689
let mut fx = FunctionCx {
8790
cx,
8891
module,
@@ -101,9 +104,10 @@ pub(crate) fn codegen_fn<'tcx>(
101104
block_map,
102105
local_map: IndexVec::with_capacity(mir.local_decls.len()),
103106
caller_location: None, // set by `codegen_fn_prelude`
107+
exception_slot,
104108

105109
clif_comments,
106-
next_ssa_var: 0,
110+
next_ssa_var: 1, // var0 is used for the exception slot
107111
};
108112

109113
tcx.prof.generic_activity("codegen clif ir").run(|| codegen_fn_body(&mut fx, start_block));
@@ -296,11 +300,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
296300
}
297301

298302
if bb_data.is_cleanup {
299-
// Unwinding after panicking is not supported
300-
continue;
301-
302-
// FIXME Once unwinding is supported and Cranelift supports marking blocks as cold, do
303-
// so for cleanup blocks.
303+
fx.bcx.set_cold_block(block);
304304
}
305305

306306
fx.bcx.ins().nop();
@@ -534,7 +534,13 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
534534
codegen_unwind_terminate(fx, source_info.span, *reason);
535535
}
536536
TerminatorKind::UnwindResume => {
537-
// FIXME implement unwinding
537+
let exception_ptr = fx.bcx.use_var(fx.exception_slot);
538+
fx.lib_call(
539+
"_Unwind_Resume",
540+
vec![AbiParam::new(fx.pointer_type)],
541+
vec![],
542+
&[exception_ptr],
543+
);
538544
fx.bcx.ins().trap(TrapCode::user(1 /* unreachable */).unwrap());
539545
}
540546
TerminatorKind::Unreachable => {
@@ -1101,7 +1107,7 @@ fn codegen_panic_inner<'tcx>(
11011107
fx: &mut FunctionCx<'_, '_, 'tcx>,
11021108
lang_item: rustc_hir::LangItem,
11031109
args: &[Value],
1104-
_unwind: UnwindAction,
1110+
unwind: UnwindAction,
11051111
span: Span,
11061112
) {
11071113
fx.bcx.set_cold_block(fx.bcx.current_block().unwrap());
@@ -1117,14 +1123,23 @@ fn codegen_panic_inner<'tcx>(
11171123

11181124
let symbol_name = fx.tcx.symbol_name(instance).name;
11191125

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

1122-
fx.lib_call(
1123-
symbol_name,
1124-
args.iter().map(|&arg| AbiParam::new(fx.bcx.func.dfg.value_type(arg))).collect(),
1125-
vec![],
1126-
args,
1127-
);
1142+
codegen_call_with_unwind_action(fx, span, CallTarget::Direct(func_ref), unwind, &args, None);
11281143

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

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)