2 use rustc_ast
::InlineAsmOptions
;
3 use rustc_middle
::mir
::*;
4 use rustc_middle
::ty
::layout
;
5 use rustc_middle
::ty
::{self, TyCtxt}
;
6 use rustc_target
::spec
::abi
::Abi
;
7 use rustc_target
::spec
::PanicStrategy
;
9 /// A pass that runs which is targeted at ensuring that codegen guarantees about
10 /// unwinding are upheld for compilations of panic=abort programs.
12 /// When compiling with panic=abort codegen backends generally want to assume
13 /// that all Rust-defined functions do not unwind, and it's UB if they actually
14 /// do unwind. Foreign functions, however, can be declared as "may unwind" via
15 /// their ABI (e.g. `extern "C-unwind"`). To uphold the guarantees that
16 /// Rust-defined functions never unwind a well-behaved Rust program needs to
17 /// catch unwinding from foreign functions and force them to abort.
19 /// This pass walks over all functions calls which may possibly unwind,
20 /// and if any are found sets their cleanup to a block that aborts the process.
21 /// This forces all unwinds, in panic=abort mode happening in foreign code, to
22 /// trigger a process abort.
24 pub struct AbortUnwindingCalls
;
26 impl<'tcx
> MirPass
<'tcx
> for AbortUnwindingCalls
{
27 fn run_pass(&self, tcx
: TyCtxt
<'tcx
>, body
: &mut Body
<'tcx
>) {
28 let def_id
= body
.source
.def_id();
29 let kind
= tcx
.def_kind(def_id
);
31 // We don't simplify the MIR of constants at this time because that
32 // namely results in a cyclic query when we call `tcx.type_of` below.
33 if !kind
.is_fn_like() {
37 // Here we test for this function itself whether its ABI allows
39 let body_ty
= tcx
.type_of(def_id
).skip_binder();
40 let body_abi
= match body_ty
.kind() {
41 ty
::FnDef(..) => body_ty
.fn_sig(tcx
).abi(),
42 ty
::Closure(..) => Abi
::RustCall
,
43 ty
::Generator(..) => Abi
::Rust
,
44 _
=> span_bug
!(body
.span
, "unexpected body ty: {:?}", body_ty
),
46 let body_can_unwind
= layout
::fn_can_unwind(tcx
, Some(def_id
), body_abi
);
48 // Look in this function body for any basic blocks which are terminated
49 // with a function call, and whose function we're calling may unwind.
50 // This will filter to functions with `extern "C-unwind"` ABIs, for
52 let mut calls_to_terminate
= Vec
::new();
53 let mut cleanups_to_remove
= Vec
::new();
54 for (id
, block
) in body
.basic_blocks
.iter_enumerated() {
58 let Some(terminator
) = &block
.terminator
else { continue }
;
59 let span
= terminator
.source_info
.span
;
61 let call_can_unwind
= match &terminator
.kind
{
62 TerminatorKind
::Call { func, .. }
=> {
63 let ty
= func
.ty(body
, tcx
);
64 let sig
= ty
.fn_sig(tcx
);
65 let fn_def_id
= match ty
.kind() {
67 &ty
::FnDef(def_id
, _
) => Some(def_id
),
68 _
=> span_bug
!(span
, "invalid callee of type {:?}", ty
),
70 layout
::fn_can_unwind(tcx
, fn_def_id
, sig
.abi())
72 TerminatorKind
::Drop { .. }
=> {
73 tcx
.sess
.opts
.unstable_opts
.panic_in_drop
== PanicStrategy
::Unwind
74 && layout
::fn_can_unwind(tcx
, None
, Abi
::Rust
)
76 TerminatorKind
::Assert { .. }
| TerminatorKind
::FalseUnwind { .. }
=> {
77 layout
::fn_can_unwind(tcx
, None
, Abi
::Rust
)
79 TerminatorKind
::InlineAsm { options, .. }
=> {
80 options
.contains(InlineAsmOptions
::MAY_UNWIND
)
82 _
if terminator
.unwind().is_some() => {
83 span_bug
!(span
, "unexpected terminator that may unwind {:?}", terminator
)
88 // If this function call can't unwind, then there's no need for it
89 // to have a landing pad. This means that we can remove any cleanup
92 cleanups_to_remove
.push(id
);
96 // Otherwise if this function can unwind, then if the outer function
97 // can also unwind there's nothing to do. If the outer function
98 // can't unwind, however, we need to change the landing pad for this
99 // function call to one that aborts.
100 if !body_can_unwind
{
101 calls_to_terminate
.push(id
);
105 for id
in calls_to_terminate
{
106 let cleanup
= body
.basic_blocks_mut()[id
].terminator_mut().unwind_mut().unwrap();
107 *cleanup
= UnwindAction
::Terminate(UnwindTerminateReason
::Abi
);
110 for id
in cleanups_to_remove
{
111 let cleanup
= body
.basic_blocks_mut()[id
].terminator_mut().unwind_mut().unwrap();
112 *cleanup
= UnwindAction
::Unreachable
;
115 // We may have invalidated some `cleanup` blocks so clean those up now.
116 super::simplify
::remove_dead_blocks(tcx
, body
);