Add UnwindAction::Terminate
This commit is contained in:
parent
5e6ed132fa
commit
0a5dac3062
16 changed files with 126 additions and 121 deletions
|
@ -170,22 +170,17 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
|
|||
|
||||
let unwind_block = match unwind {
|
||||
mir::UnwindAction::Cleanup(cleanup) => Some(self.llbb_with_cleanup(fx, cleanup)),
|
||||
_ if fx.mir[self.bb].is_cleanup
|
||||
&& fn_abi.can_unwind
|
||||
&& !base::wants_msvc_seh(fx.cx.tcx().sess) =>
|
||||
{
|
||||
// Exception must not propagate out of the execution of a cleanup (doing so
|
||||
// can cause undefined behaviour). We insert a double unwind guard for
|
||||
// functions that can potentially unwind to protect against this.
|
||||
//
|
||||
// This is not necessary for SEH which does not use successive unwinding
|
||||
// like Itanium EH. EH frames in SEH are different from normal function
|
||||
// frames and SEH will abort automatically if an exception tries to
|
||||
// propagate out from cleanup.
|
||||
Some(fx.double_unwind_guard())
|
||||
}
|
||||
mir::UnwindAction::Continue => None,
|
||||
mir::UnwindAction::Unreachable => None,
|
||||
mir::UnwindAction::Terminate => {
|
||||
if fx.mir[self.bb].is_cleanup && base::wants_msvc_seh(fx.cx.tcx().sess) {
|
||||
// SEH will abort automatically if an exception tries to
|
||||
// propagate out from cleanup.
|
||||
None
|
||||
} else {
|
||||
Some(fx.terminate_block())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(unwind_block) = unwind_block {
|
||||
|
@ -253,7 +248,14 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
|
|||
instance: Instance<'_>,
|
||||
mergeable_succ: bool,
|
||||
) -> MergingSucc {
|
||||
if let mir::UnwindAction::Cleanup(cleanup) = unwind {
|
||||
let unwind_target = match unwind {
|
||||
mir::UnwindAction::Cleanup(cleanup) => Some(self.llbb_with_cleanup(fx, cleanup)),
|
||||
mir::UnwindAction::Terminate => Some(fx.terminate_block()),
|
||||
mir::UnwindAction::Continue => None,
|
||||
mir::UnwindAction::Unreachable => None,
|
||||
};
|
||||
|
||||
if let Some(cleanup) = unwind_target {
|
||||
let ret_llbb = if let Some(target) = destination {
|
||||
fx.llbb(target)
|
||||
} else {
|
||||
|
@ -266,7 +268,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
|
|||
options,
|
||||
line_spans,
|
||||
instance,
|
||||
Some((ret_llbb, self.llbb_with_cleanup(fx, cleanup), self.funclet(fx))),
|
||||
Some((ret_llbb, cleanup, self.funclet(fx))),
|
||||
);
|
||||
MergingSucc::False
|
||||
} else {
|
||||
|
@ -1551,62 +1553,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
fn landing_pad_for_uncached(&mut self, bb: mir::BasicBlock) -> Bx::BasicBlock {
|
||||
let llbb = self.llbb(bb);
|
||||
if base::wants_msvc_seh(self.cx.sess()) {
|
||||
let funclet;
|
||||
let ret_llbb;
|
||||
match self.mir[bb].terminator.as_ref().map(|t| &t.kind) {
|
||||
// This is a basic block that we're aborting the program for,
|
||||
// notably in an `extern` function. These basic blocks are inserted
|
||||
// so that we assert that `extern` functions do indeed not panic,
|
||||
// and if they do we abort the process.
|
||||
//
|
||||
// On MSVC these are tricky though (where we're doing funclets). If
|
||||
// we were to do a cleanuppad (like below) the normal functions like
|
||||
// `longjmp` would trigger the abort logic, terminating the
|
||||
// program. Instead we insert the equivalent of `catch(...)` for C++
|
||||
// which magically doesn't trigger when `longjmp` files over this
|
||||
// frame.
|
||||
//
|
||||
// Lots more discussion can be found on #48251 but this codegen is
|
||||
// modeled after clang's for:
|
||||
//
|
||||
// try {
|
||||
// foo();
|
||||
// } catch (...) {
|
||||
// bar();
|
||||
// }
|
||||
Some(&mir::TerminatorKind::Abort) => {
|
||||
let cs_llbb =
|
||||
Bx::append_block(self.cx, self.llfn, &format!("cs_funclet{:?}", bb));
|
||||
let cp_llbb =
|
||||
Bx::append_block(self.cx, self.llfn, &format!("cp_funclet{:?}", bb));
|
||||
ret_llbb = cs_llbb;
|
||||
|
||||
let mut cs_bx = Bx::build(self.cx, cs_llbb);
|
||||
let cs = cs_bx.catch_switch(None, None, &[cp_llbb]);
|
||||
|
||||
// The "null" here is actually a RTTI type descriptor for the
|
||||
// C++ personality function, but `catch (...)` has no type so
|
||||
// it's null. The 64 here is actually a bitfield which
|
||||
// represents that this is a catch-all block.
|
||||
let mut cp_bx = Bx::build(self.cx, cp_llbb);
|
||||
let null = cp_bx.const_null(
|
||||
cp_bx.type_i8p_ext(cp_bx.cx().data_layout().instruction_address_space),
|
||||
);
|
||||
let sixty_four = cp_bx.const_i32(64);
|
||||
funclet = cp_bx.catch_pad(cs, &[null, sixty_four, null]);
|
||||
cp_bx.br(llbb);
|
||||
}
|
||||
_ => {
|
||||
let cleanup_llbb =
|
||||
Bx::append_block(self.cx, self.llfn, &format!("funclet_{:?}", bb));
|
||||
ret_llbb = cleanup_llbb;
|
||||
let mut cleanup_bx = Bx::build(self.cx, cleanup_llbb);
|
||||
funclet = cleanup_bx.cleanup_pad(None, &[]);
|
||||
cleanup_bx.br(llbb);
|
||||
}
|
||||
}
|
||||
let cleanup_bb = Bx::append_block(self.cx, self.llfn, &format!("funclet_{:?}", bb));
|
||||
let mut cleanup_bx = Bx::build(self.cx, cleanup_bb);
|
||||
let funclet = cleanup_bx.cleanup_pad(None, &[]);
|
||||
cleanup_bx.br(llbb);
|
||||
self.funclets[bb] = Some(funclet);
|
||||
ret_llbb
|
||||
cleanup_bb
|
||||
} else {
|
||||
let cleanup_llbb = Bx::append_block(self.cx, self.llfn, "cleanup");
|
||||
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> {
|
|||
})
|
||||
}
|
||||
|
||||
fn double_unwind_guard(&mut self) -> Bx::BasicBlock {
|
||||
self.double_unwind_guard.unwrap_or_else(|| {
|
||||
assert!(!base::wants_msvc_seh(self.cx.sess()));
|
||||
fn terminate_block(&mut self) -> Bx::BasicBlock {
|
||||
self.terminate_block.unwrap_or_else(|| {
|
||||
let funclet;
|
||||
let llbb;
|
||||
let mut bx;
|
||||
if base::wants_msvc_seh(self.cx.sess()) {
|
||||
// This is a basic block that we're aborting the program for,
|
||||
// notably in an `extern` function. These basic blocks are inserted
|
||||
// so that we assert that `extern` functions do indeed not panic,
|
||||
// and if they do we abort the process.
|
||||
//
|
||||
// On MSVC these are tricky though (where we're doing funclets). If
|
||||
// we were to do a cleanuppad (like below) the normal functions like
|
||||
// `longjmp` would trigger the abort logic, terminating the
|
||||
// program. Instead we insert the equivalent of `catch(...)` for C++
|
||||
// which magically doesn't trigger when `longjmp` files over this
|
||||
// frame.
|
||||
//
|
||||
// Lots more discussion can be found on #48251 but this codegen is
|
||||
// modeled after clang's for:
|
||||
//
|
||||
// try {
|
||||
// foo();
|
||||
// } catch (...) {
|
||||
// bar();
|
||||
// }
|
||||
llbb = Bx::append_block(self.cx, self.llfn, "cs_terminate");
|
||||
let cp_llbb = Bx::append_block(self.cx, self.llfn, "cp_terminate");
|
||||
|
||||
let mut cs_bx = Bx::build(self.cx, llbb);
|
||||
let cs = cs_bx.catch_switch(None, None, &[llbb]);
|
||||
|
||||
// The "null" here is actually a RTTI type descriptor for the
|
||||
// C++ personality function, but `catch (...)` has no type so
|
||||
// it's null. The 64 here is actually a bitfield which
|
||||
// represents that this is a catch-all block.
|
||||
bx = Bx::build(self.cx, cp_llbb);
|
||||
let null =
|
||||
bx.const_null(bx.type_i8p_ext(bx.cx().data_layout().instruction_address_space));
|
||||
let sixty_four = bx.const_i32(64);
|
||||
funclet = Some(bx.catch_pad(cs, &[null, sixty_four, null]));
|
||||
} else {
|
||||
llbb = Bx::append_block(self.cx, self.llfn, "terminate");
|
||||
bx = Bx::build(self.cx, llbb);
|
||||
|
||||
let llpersonality = self.cx.eh_personality();
|
||||
bx.cleanup_landing_pad(llpersonality);
|
||||
|
||||
funclet = None;
|
||||
}
|
||||
|
||||
let llbb = Bx::append_block(self.cx, self.llfn, "abort");
|
||||
let mut bx = Bx::build(self.cx, llbb);
|
||||
self.set_debug_loc(&mut bx, mir::SourceInfo::outermost(self.mir.span));
|
||||
|
||||
let llpersonality = self.cx.eh_personality();
|
||||
bx.cleanup_landing_pad(llpersonality);
|
||||
|
||||
let (fn_abi, fn_ptr) = common::build_langcall(&bx, None, LangItem::PanicCannotUnwind);
|
||||
let fn_ty = bx.fn_decl_backend_type(&fn_abi);
|
||||
|
||||
let llret = bx.call(fn_ty, Some(&fn_abi), fn_ptr, &[], None);
|
||||
let llret = bx.call(fn_ty, Some(&fn_abi), fn_ptr, &[], funclet.as_ref());
|
||||
bx.do_not_inline(llret);
|
||||
|
||||
bx.unreachable();
|
||||
|
||||
self.double_unwind_guard = Some(llbb);
|
||||
self.terminate_block = Some(llbb);
|
||||
llbb
|
||||
})
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
|
|||
unreachable_block: Option<Bx::BasicBlock>,
|
||||
|
||||
/// Cached double unwind guarding block
|
||||
double_unwind_guard: Option<Bx::BasicBlock>,
|
||||
terminate_block: Option<Bx::BasicBlock>,
|
||||
|
||||
/// The location where each MIR arg/var/tmp/ret is stored. This is
|
||||
/// usually an `PlaceRef` representing an alloca, but not always:
|
||||
|
@ -189,7 +189,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||
personality_slot: None,
|
||||
cached_llbbs,
|
||||
unreachable_block: None,
|
||||
double_unwind_guard: None,
|
||||
terminate_block: None,
|
||||
cleanup_kinds,
|
||||
landing_pads: IndexVec::from_elem(None, &mir.basic_blocks),
|
||||
funclets: IndexVec::from_fn_n(|_| None, mir.basic_blocks.len()),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue