diff options
| author | Gary Guo <gary@garyguo.net> | 2022-10-10 22:40:40 +0100 |
|---|---|---|
| committer | Gary Guo <gary@garyguo.net> | 2023-04-06 09:34:16 +0100 |
| commit | 0a5dac3062ffc80a283943e49e2ada08f0b0eaf7 (patch) | |
| tree | 5110a2f63038db9e3e03883b3d3b5685452a5719 /compiler/rustc_codegen_ssa/src/mir | |
| parent | 5e6ed132fa8a1bd1ec6b365e61e9e290528c90f5 (diff) | |
| download | rust-0a5dac3062ffc80a283943e49e2ada08f0b0eaf7.tar.gz rust-0a5dac3062ffc80a283943e49e2ada08f0b0eaf7.zip | |
Add `UnwindAction::Terminate`
Diffstat (limited to 'compiler/rustc_codegen_ssa/src/mir')
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/mir/block.rs | 156 | ||||
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/mir/mod.rs | 4 |
2 files changed, 77 insertions, 83 deletions
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 66bec6f30de..5bc78e5dbac 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -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); - 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)); + funclet = None; + } - let llpersonality = self.cx.eh_personality(); - bx.cleanup_landing_pad(llpersonality); + self.set_debug_loc(&mut bx, mir::SourceInfo::outermost(self.mir.span)); 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 }) } diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 189549953d9..0b4456dba30 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -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()), |
