Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 0a5dac3

Browse files
committed
Add UnwindAction::Terminate
1 parent 5e6ed13 commit 0a5dac3

File tree

16 files changed

+126
-121
lines changed

16 files changed

+126
-121
lines changed

compiler/rustc_borrowck/src/type_check/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1663,8 +1663,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
16631663
}
16641664
self.assert_iscleanup(body, ctxt, unwind, true);
16651665
}
1666-
UnwindAction::Continue => (),
1667-
UnwindAction::Unreachable => (),
1666+
UnwindAction::Continue => {
1667+
if is_cleanup {
1668+
span_mirbug!(self, ctxt, "unwind on cleanup block")
1669+
}
1670+
}
1671+
UnwindAction::Unreachable | UnwindAction::Terminate => (),
16681672
}
16691673
}
16701674

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 75 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -170,22 +170,17 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
170170

171171
let unwind_block = match unwind {
172172
mir::UnwindAction::Cleanup(cleanup) => Some(self.llbb_with_cleanup(fx, cleanup)),
173-
_ if fx.mir[self.bb].is_cleanup
174-
&& fn_abi.can_unwind
175-
&& !base::wants_msvc_seh(fx.cx.tcx().sess) =>
176-
{
177-
// Exception must not propagate out of the execution of a cleanup (doing so
178-
// can cause undefined behaviour). We insert a double unwind guard for
179-
// functions that can potentially unwind to protect against this.
180-
//
181-
// This is not necessary for SEH which does not use successive unwinding
182-
// like Itanium EH. EH frames in SEH are different from normal function
183-
// frames and SEH will abort automatically if an exception tries to
184-
// propagate out from cleanup.
185-
Some(fx.double_unwind_guard())
186-
}
187173
mir::UnwindAction::Continue => None,
188174
mir::UnwindAction::Unreachable => None,
175+
mir::UnwindAction::Terminate => {
176+
if fx.mir[self.bb].is_cleanup && base::wants_msvc_seh(fx.cx.tcx().sess) {
177+
// SEH will abort automatically if an exception tries to
178+
// propagate out from cleanup.
179+
None
180+
} else {
181+
Some(fx.terminate_block())
182+
}
183+
}
189184
};
190185

191186
if let Some(unwind_block) = unwind_block {
@@ -253,7 +248,14 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
253248
instance: Instance<'_>,
254249
mergeable_succ: bool,
255250
) -> MergingSucc {
256-
if let mir::UnwindAction::Cleanup(cleanup) = unwind {
251+
let unwind_target = match unwind {
252+
mir::UnwindAction::Cleanup(cleanup) => Some(self.llbb_with_cleanup(fx, cleanup)),
253+
mir::UnwindAction::Terminate => Some(fx.terminate_block()),
254+
mir::UnwindAction::Continue => None,
255+
mir::UnwindAction::Unreachable => None,
256+
};
257+
258+
if let Some(cleanup) = unwind_target {
257259
let ret_llbb = if let Some(target) = destination {
258260
fx.llbb(target)
259261
} else {
@@ -266,7 +268,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
266268
options,
267269
line_spans,
268270
instance,
269-
Some((ret_llbb, self.llbb_with_cleanup(fx, cleanup), self.funclet(fx))),
271+
Some((ret_llbb, cleanup, self.funclet(fx))),
270272
);
271273
MergingSucc::False
272274
} else {
@@ -1551,62 +1553,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
15511553
fn landing_pad_for_uncached(&mut self, bb: mir::BasicBlock) -> Bx::BasicBlock {
15521554
let llbb = self.llbb(bb);
15531555
if base::wants_msvc_seh(self.cx.sess()) {
1554-
let funclet;
1555-
let ret_llbb;
1556-
match self.mir[bb].terminator.as_ref().map(|t| &t.kind) {
1557-
// This is a basic block that we're aborting the program for,
1558-
// notably in an `extern` function. These basic blocks are inserted
1559-
// so that we assert that `extern` functions do indeed not panic,
1560-
// and if they do we abort the process.
1561-
//
1562-
// On MSVC these are tricky though (where we're doing funclets). If
1563-
// we were to do a cleanuppad (like below) the normal functions like
1564-
// `longjmp` would trigger the abort logic, terminating the
1565-
// program. Instead we insert the equivalent of `catch(...)` for C++
1566-
// which magically doesn't trigger when `longjmp` files over this
1567-
// frame.
1568-
//
1569-
// Lots more discussion can be found on #48251 but this codegen is
1570-
// modeled after clang's for:
1571-
//
1572-
// try {
1573-
// foo();
1574-
// } catch (...) {
1575-
// bar();
1576-
// }
1577-
Some(&mir::TerminatorKind::Abort) => {
1578-
let cs_llbb =
1579-
Bx::append_block(self.cx, self.llfn, &format!("cs_funclet{:?}", bb));
1580-
let cp_llbb =
1581-
Bx::append_block(self.cx, self.llfn, &format!("cp_funclet{:?}", bb));
1582-
ret_llbb = cs_llbb;
1583-
1584-
let mut cs_bx = Bx::build(self.cx, cs_llbb);
1585-
let cs = cs_bx.catch_switch(None, None, &[cp_llbb]);
1586-
1587-
// The "null" here is actually a RTTI type descriptor for the
1588-
// C++ personality function, but `catch (...)` has no type so
1589-
// it's null. The 64 here is actually a bitfield which
1590-
// represents that this is a catch-all block.
1591-
let mut cp_bx = Bx::build(self.cx, cp_llbb);
1592-
let null = cp_bx.const_null(
1593-
cp_bx.type_i8p_ext(cp_bx.cx().data_layout().instruction_address_space),
1594-
);
1595-
let sixty_four = cp_bx.const_i32(64);
1596-
funclet = cp_bx.catch_pad(cs, &[null, sixty_four, null]);
1597-
cp_bx.br(llbb);
1598-
}
1599-
_ => {
1600-
let cleanup_llbb =
1601-
Bx::append_block(self.cx, self.llfn, &format!("funclet_{:?}", bb));
1602-
ret_llbb = cleanup_llbb;
1603-
let mut cleanup_bx = Bx::build(self.cx, cleanup_llbb);
1604-
funclet = cleanup_bx.cleanup_pad(None, &[]);
1605-
cleanup_bx.br(llbb);
1606-
}
1607-
}
1556+
let cleanup_bb = Bx::append_block(self.cx, self.llfn, &format!("funclet_{:?}", bb));
1557+
let mut cleanup_bx = Bx::build(self.cx, cleanup_bb);
1558+
let funclet = cleanup_bx.cleanup_pad(None, &[]);
1559+
cleanup_bx.br(llbb);
16081560
self.funclets[bb] = Some(funclet);
1609-
ret_llbb
1561+
cleanup_bb
16101562
} else {
16111563
let cleanup_llbb = Bx::append_block(self.cx, self.llfn, "cleanup");
16121564
let mut cleanup_bx = Bx::build(self.cx, cleanup_llbb);
@@ -1633,26 +1585,68 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
16331585
})
16341586
}
16351587

1636-
fn double_unwind_guard(&mut self) -> Bx::BasicBlock {
1637-
self.double_unwind_guard.unwrap_or_else(|| {
1638-
assert!(!base::wants_msvc_seh(self.cx.sess()));
1588+
fn terminate_block(&mut self) -> Bx::BasicBlock {
1589+
self.terminate_block.unwrap_or_else(|| {
1590+
let funclet;
1591+
let llbb;
1592+
let mut bx;
1593+
if base::wants_msvc_seh(self.cx.sess()) {
1594+
// This is a basic block that we're aborting the program for,
1595+
// notably in an `extern` function. These basic blocks are inserted
1596+
// so that we assert that `extern` functions do indeed not panic,
1597+
// and if they do we abort the process.
1598+
//
1599+
// On MSVC these are tricky though (where we're doing funclets). If
1600+
// we were to do a cleanuppad (like below) the normal functions like
1601+
// `longjmp` would trigger the abort logic, terminating the
1602+
// program. Instead we insert the equivalent of `catch(...)` for C++
1603+
// which magically doesn't trigger when `longjmp` files over this
1604+
// frame.
1605+
//
1606+
// Lots more discussion can be found on #48251 but this codegen is
1607+
// modeled after clang's for:
1608+
//
1609+
// try {
1610+
// foo();
1611+
// } catch (...) {
1612+
// bar();
1613+
// }
1614+
llbb = Bx::append_block(self.cx, self.llfn, "cs_terminate");
1615+
let cp_llbb = Bx::append_block(self.cx, self.llfn, "cp_terminate");
1616+
1617+
let mut cs_bx = Bx::build(self.cx, llbb);
1618+
let cs = cs_bx.catch_switch(None, None, &[llbb]);
1619+
1620+
// The "null" here is actually a RTTI type descriptor for the
1621+
// C++ personality function, but `catch (...)` has no type so
1622+
// it's null. The 64 here is actually a bitfield which
1623+
// represents that this is a catch-all block.
1624+
bx = Bx::build(self.cx, cp_llbb);
1625+
let null =
1626+
bx.const_null(bx.type_i8p_ext(bx.cx().data_layout().instruction_address_space));
1627+
let sixty_four = bx.const_i32(64);
1628+
funclet = Some(bx.catch_pad(cs, &[null, sixty_four, null]));
1629+
} else {
1630+
llbb = Bx::append_block(self.cx, self.llfn, "terminate");
1631+
bx = Bx::build(self.cx, llbb);
1632+
1633+
let llpersonality = self.cx.eh_personality();
1634+
bx.cleanup_landing_pad(llpersonality);
16391635

1640-
let llbb = Bx::append_block(self.cx, self.llfn, "abort");
1641-
let mut bx = Bx::build(self.cx, llbb);
1642-
self.set_debug_loc(&mut bx, mir::SourceInfo::outermost(self.mir.span));
1636+
funclet = None;
1637+
}
16431638

1644-
let llpersonality = self.cx.eh_personality();
1645-
bx.cleanup_landing_pad(llpersonality);
1639+
self.set_debug_loc(&mut bx, mir::SourceInfo::outermost(self.mir.span));
16461640

16471641
let (fn_abi, fn_ptr) = common::build_langcall(&bx, None, LangItem::PanicCannotUnwind);
16481642
let fn_ty = bx.fn_decl_backend_type(&fn_abi);
16491643

1650-
let llret = bx.call(fn_ty, Some(&fn_abi), fn_ptr, &[], None);
1644+
let llret = bx.call(fn_ty, Some(&fn_abi), fn_ptr, &[], funclet.as_ref());
16511645
bx.do_not_inline(llret);
16521646

16531647
bx.unreachable();
16541648

1655-
self.double_unwind_guard = Some(llbb);
1649+
self.terminate_block = Some(llbb);
16561650
llbb
16571651
})
16581652
}

compiler/rustc_codegen_ssa/src/mir/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
7474
unreachable_block: Option<Bx::BasicBlock>,
7575

7676
/// Cached double unwind guarding block
77-
double_unwind_guard: Option<Bx::BasicBlock>,
77+
terminate_block: Option<Bx::BasicBlock>,
7878

7979
/// The location where each MIR arg/var/tmp/ret is stored. This is
8080
/// usually an `PlaceRef` representing an alloca, but not always:
@@ -189,7 +189,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
189189
personality_slot: None,
190190
cached_llbbs,
191191
unreachable_block: None,
192-
double_unwind_guard: None,
192+
terminate_block: None,
193193
cleanup_kinds,
194194
landing_pads: IndexVec::from_elem(None, &mir.basic_blocks),
195195
funclets: IndexVec::from_fn_n(|_| None, mir.basic_blocks.len()),

compiler/rustc_const_eval/src/interpret/eval_context.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
736736
mir::UnwindAction::Unreachable => {
737737
throw_ub_format!("unwinding past a stack frame that does not allow unwinding")
738738
}
739+
mir::UnwindAction::Terminate => {
740+
M::abort(self, "panic in a function that cannot unwind".to_owned())?;
741+
}
739742
};
740743
Ok(())
741744
}

compiler/rustc_middle/src/mir/patch.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub struct MirPatch<'tcx> {
1414
resume_block: Option<BasicBlock>,
1515
// Only for unreachable in cleanup path.
1616
unreachable_block: Option<BasicBlock>,
17+
terminate_block: Option<BasicBlock>,
1718
body_span: Span,
1819
next_local: usize,
1920
}
@@ -28,6 +29,7 @@ impl<'tcx> MirPatch<'tcx> {
2829
next_local: body.local_decls.len(),
2930
resume_block: None,
3031
unreachable_block: None,
32+
terminate_block: None,
3133
body_span: body.span,
3234
};
3335

compiler/rustc_middle/src/mir/syntax.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,10 @@ pub enum UnwindAction {
763763
Continue,
764764
/// Triggers undefined behavior if unwind happens.
765765
Unreachable,
766+
/// Terminates the execution if unwind happens.
767+
///
768+
/// Depending on the platform and situation this may cause a non-unwindable panic or abort.
769+
Terminate,
766770
/// Cleanups to be done.
767771
Cleanup(BasicBlock),
768772
}

compiler/rustc_middle/src/mir/terminator.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ impl<'tcx> Debug for TerminatorKind<'tcx> {
274274
// Not needed or included in successors
275275
None | Some(UnwindAction::Continue) | Some(UnwindAction::Cleanup(_)) => None,
276276
Some(UnwindAction::Unreachable) => Some("unwind unreachable"),
277+
Some(UnwindAction::Terminate) => Some("unwind terminate"),
277278
};
278279

279280
match (successor_count, unwind) {

compiler/rustc_mir_build/src/build/expr/into.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
469469
} else {
470470
Some(destination_block)
471471
},
472-
unwind: UnwindAction::Continue,
472+
unwind: if options.contains(InlineAsmOptions::MAY_UNWIND) {
473+
UnwindAction::Continue
474+
} else {
475+
UnwindAction::Unreachable
476+
},
473477
},
474478
);
475479
if options.contains(InlineAsmOptions::MAY_UNWIND) {

compiler/rustc_mir_build/src/build/scope.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ impl DropTree {
369369
let terminator = TerminatorKind::Drop {
370370
target: blocks[drop_data.1].unwrap(),
371371
// The caller will handle this if needed.
372-
unwind: UnwindAction::Continue,
372+
unwind: UnwindAction::Terminate,
373373
place: drop_data.0.local.into(),
374374
};
375375
cfg.terminate(block, drop_data.0.source_info, terminator);

compiler/rustc_mir_dataflow/src/elaborate_drops.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ impl Unwind {
8080
fn into_action(self) -> UnwindAction {
8181
match self {
8282
Unwind::To(bb) => UnwindAction::Cleanup(bb),
83-
Unwind::InCleanup => UnwindAction::Continue,
83+
Unwind::InCleanup => UnwindAction::Terminate,
8484
}
8585
}
8686

@@ -946,7 +946,7 @@ where
946946
args,
947947
destination: unit_temp,
948948
target: Some(target),
949-
unwind: UnwindAction::Unreachable,
949+
unwind: UnwindAction::Terminate,
950950
from_hir_call: false,
951951
fn_span: self.source_info.span,
952952
}; // FIXME(#43234)

compiler/rustc_mir_transform/src/abort_unwinding_calls.rs

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,6 @@ impl<'tcx> MirPass<'tcx> for AbortUnwindingCalls {
3434
return;
3535
}
3636

37-
// This pass only runs on functions which themselves cannot unwind,
38-
// forcibly changing the body of the function to structurally provide
39-
// this guarantee by aborting on an unwind. If this function can unwind,
40-
// then there's nothing to do because it already should work correctly.
41-
//
4237
// Here we test for this function itself whether its ABI allows
4338
// unwinding or not.
4439
let body_ty = tcx.type_of(def_id).skip_binder();
@@ -107,26 +102,9 @@ impl<'tcx> MirPass<'tcx> for AbortUnwindingCalls {
107102
}
108103
}
109104

110-
// For call instructions which need to be terminated, we insert a
111-
// singular basic block which simply terminates, and then configure the
112-
// `cleanup` attribute for all calls we found to this basic block we
113-
// insert which means that any unwinding that happens in the functions
114-
// will force an abort of the process.
115-
if !calls_to_terminate.is_empty() {
116-
let bb = BasicBlockData {
117-
statements: Vec::new(),
118-
is_cleanup: true,
119-
terminator: Some(Terminator {
120-
source_info: SourceInfo::outermost(body.span),
121-
kind: TerminatorKind::Abort,
122-
}),
123-
};
124-
let abort_bb = body.basic_blocks_mut().push(bb);
125-
126-
for bb in calls_to_terminate {
127-
let cleanup = body.basic_blocks_mut()[bb].terminator_mut().unwind_mut().unwrap();
128-
*cleanup = UnwindAction::Cleanup(abort_bb);
129-
}
105+
for id in calls_to_terminate {
106+
let cleanup = body.basic_blocks_mut()[id].terminator_mut().unwind_mut().unwrap();
107+
*cleanup = UnwindAction::Terminate;
130108
}
131109

132110
for id in cleanups_to_remove {

compiler/rustc_mir_transform/src/elaborate_drops.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,9 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
417417
UnwindAction::Unreachable => {
418418
Unwind::To(self.patch.unreachable_block())
419419
}
420-
UnwindAction::Terminate => Unwind::To(self.patch.terminate_block()),
420+
UnwindAction::Terminate => {
421+
Unwind::To(self.patch.terminate_block())
422+
}
421423
}
422424
};
423425
elaborate_drop(
@@ -558,7 +560,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
558560
if let TerminatorKind::Call {
559561
destination,
560562
target: Some(_),
561-
unwind: UnwindAction::Continue,
563+
unwind: UnwindAction::Continue | UnwindAction::Unreachable | UnwindAction::Terminate,
562564
..
563565
} = data.terminator().kind
564566
{

compiler/rustc_mir_transform/src/generator.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,6 +1064,7 @@ fn elaborate_generator_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
10641064
UnwindAction::Cleanup(tgt) => tgt,
10651065
UnwindAction::Continue => elaborator.patch.resume_block(),
10661066
UnwindAction::Unreachable => elaborator.patch.unreachable_block(),
1067+
UnwindAction::Terminate => elaborator.patch.terminate_block(),
10671068
})
10681069
};
10691070
elaborate_drop(

0 commit comments

Comments
 (0)