diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 38e928145a816..4bd44c4959e72 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -333,15 +333,22 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx> #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum CleanupKind { NotCleanup, - Funclet, - Internal { funclet: mir::BasicBlock }, + Funclet { + /// If `true`, there is at least one unwind/cleanup edge to this block. + /// On GNU, landing pads are only needed for unwind/cleanup edges, while + /// on MSVC, every funclet has to start with a cleanup pad. + unwind_may_land_here: bool, + }, + Internal { + funclet: mir::BasicBlock, + }, } impl CleanupKind { pub fn funclet_bb(self, for_bb: mir::BasicBlock) -> Option { match self { CleanupKind::NotCleanup => None, - CleanupKind::Funclet => Some(for_bb), + CleanupKind::Funclet { .. } => Some(for_bb), CleanupKind::Internal { funclet } => Some(funclet), } } @@ -374,7 +381,7 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec) -> IndexVec continue, - CleanupKind::Funclet => bb, + CleanupKind::Funclet { .. } => bb, CleanupKind::Internal { funclet } => funclet, }; @@ -414,6 +421,8 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec) -> IndexVec { result[succ] = CleanupKind::Internal { funclet }; } - CleanupKind::Funclet => { + CleanupKind::Funclet { .. } => { if funclet != succ { set_successor(funclet, succ); } @@ -435,7 +444,7 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec TerminatorCodegenHelper<'tcx> { ) -> (Bx::BasicBlock, bool) { let span = self.terminator.source_info.span; let lltarget = fx.blocks[target]; - let target_funclet = fx.cleanup_kinds[target].funclet_bb(target); - match (self.funclet_bb, target_funclet) { + let target_cleanup_kind = fx.cleanup_kinds[target]; + match (self.funclet_bb, target_cleanup_kind.funclet_bb(target)) { (None, None) => (lltarget, false), (Some(f), Some(t_f)) if f == t_f || !base::wants_msvc_seh(fx.cx.tcx().sess) => { (lltarget, false) } - // jump *into* cleanup - need a landing pad if GNU - (None, Some(_)) => (fx.landing_pad_to(target), false), + // Unwind *into* cleanup - need a landing/cleanup pad. + (None, Some(_)) => { + assert_eq!( + target_cleanup_kind, + CleanupKind::Funclet { unwind_may_land_here: true } + ); + (fx.landing_pads[target].unwrap(), false) + } (Some(_), None) => span_bug!(span, "{:?} - jump out of cleanup?", self.terminator), - (Some(_), Some(_)) => (fx.landing_pad_to(target), true), + (Some(_), Some(_)) => (fx.landing_pads[target].unwrap(), true), } } @@ -155,14 +162,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if let Some(funclet) = helper.funclet(self) { bx.cleanup_ret(funclet, None); } else { - let slot = self.get_personality_slot(&mut bx); + // FIXME(eddyb) consider not using `PlaceRef` for `slot`, to avoid + // having to take apart (and later put back together) the pair. + let slot = self.personality_slot.unwrap(); let lp0 = slot.project_field(&mut bx, 0); let lp0 = bx.load_operand(lp0).immediate(); let lp1 = slot.project_field(&mut bx, 1); let lp1 = bx.load_operand(lp1).immediate(); slot.storage_dead(&mut bx); - let mut lp = bx.const_undef(self.landing_pad_type()); + let mut lp = bx.cx().const_undef(bx.cx().type_landing_pad()); lp = bx.insert_value(lp, lp0, 0); lp = bx.insert_value(lp, lp1, 1); bx.resume(lp); @@ -1178,59 +1187,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.caller_location.unwrap_or_else(|| span_to_caller_location(source_info.span)) } - fn get_personality_slot(&mut self, bx: &mut Bx) -> PlaceRef<'tcx, Bx::Value> { - let cx = bx.cx(); - if let Some(slot) = self.personality_slot { - slot - } else { - let layout = cx.layout_of( - cx.tcx().intern_tup(&[cx.tcx().mk_mut_ptr(cx.tcx().types.u8), cx.tcx().types.i32]), - ); - let slot = PlaceRef::alloca(bx, layout); - self.personality_slot = Some(slot); - slot - } - } - - /// Returns the landing-pad wrapper around the given basic block. - /// - /// No-op in MSVC SEH scheme. - fn landing_pad_to(&mut self, target_bb: mir::BasicBlock) -> Bx::BasicBlock { - if let Some(block) = self.landing_pads[target_bb] { - return block; - } - - let block = self.blocks[target_bb]; - let landing_pad = self.landing_pad_uncached(block); - self.landing_pads[target_bb] = Some(landing_pad); - landing_pad - } - - fn landing_pad_uncached(&mut self, target_bb: Bx::BasicBlock) -> Bx::BasicBlock { - if base::wants_msvc_seh(self.cx.sess()) { - span_bug!(self.mir.span, "landing pad was not inserted?") - } - - let mut bx = self.new_block("cleanup"); - - let llpersonality = self.cx.eh_personality(); - let llretty = self.landing_pad_type(); - let lp = bx.landing_pad(llretty, llpersonality, 1); - bx.set_cleanup(lp); - - let slot = self.get_personality_slot(&mut bx); - slot.storage_live(&mut bx); - Pair(bx.extract_value(lp, 0), bx.extract_value(lp, 1)).store(&mut bx, slot); - - bx.br(target_bb); - bx.llbb() - } - - fn landing_pad_type(&self) -> Bx::Type { - let cx = self.cx; - cx.type_struct(&[cx.type_i8p(), cx.type_i32()], false) - } - fn unreachable_block(&mut self) -> Bx::BasicBlock { self.unreachable_block.unwrap_or_else(|| { let mut bx = self.new_block("unreachable"); diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 91df67b53d21f..dc35df2c9d7ec 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -6,7 +6,7 @@ use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TypeFoldable}; use rustc_target::abi::call::{FnAbi, PassMode}; -use rustc_target::abi::HasDataLayout; +use rustc_target::abi::{HasDataLayout, LayoutOf}; use std::iter; @@ -40,7 +40,7 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> { /// value used for C++ unwinding, which must filter by type: we /// don't really care about it very much. Anyway, this value /// contains an alloca into which the personality is stored and - /// then later loaded when generating the DIVERGE_BLOCK. + /// then later loaded when generating `resume` terminators. personality_slot: Option>, /// A `Block` for each MIR `BasicBlock` @@ -145,15 +145,29 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let mut bx = Bx::new_block(cx, llfn, "start"); - if mir.basic_blocks().iter().any(|bb| bb.is_cleanup) { + let personality_slot = if mir.basic_blocks().iter().any(|bb| bb.is_cleanup) { bx.set_personality_fn(cx.eh_personality()); - } + + if base::wants_msvc_seh(bx.sess()) { + // MSVC doesn't use landing pads. + None + } else { + // FIXME(eddyb) the use of `PlaceRef` requires a Rust type, and taking apart + // (and later putting back together) the pair, instead of treating it opaquely. + let layout = cx.layout_of( + cx.tcx().intern_tup(&[cx.tcx().mk_mut_ptr(cx.tcx().types.u8), cx.tcx().types.i32]), + ); + Some(PlaceRef::alloca(&mut bx, layout)) + } + } else { + None + }; let cleanup_kinds = analyze::cleanup_kinds(&mir); // Allocate a `Block` for every basic block, except // the start block, if nothing loops back to it. let reentrant_start_block = !mir.predecessors()[mir::START_BLOCK].is_empty(); - let block_bxs: IndexVec = mir + let block_llbbs: IndexVec = mir .basic_blocks() .indices() .map(|bb| { @@ -165,15 +179,16 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( }) .collect(); - let (landing_pads, funclets) = create_funclets(&mir, &mut bx, &cleanup_kinds, &block_bxs); + let (landing_pads, funclets) = + landing_pads_and_funclets(&mir, &mut bx, &cleanup_kinds, &block_llbbs, personality_slot); let mut fx = FunctionCx { instance, mir, llfn, fn_abi, cx, - personality_slot: None, - blocks: block_bxs, + personality_slot, + blocks: block_llbbs, unreachable_block: None, cleanup_kinds, landing_pads, @@ -273,24 +288,51 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } } -fn create_funclets<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( +fn landing_pads_and_funclets<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( mir: &'tcx mir::Body<'tcx>, bx: &mut Bx, cleanup_kinds: &IndexVec, - block_bxs: &IndexVec, + block_llbbs: &IndexVec, + personality_slot: Option>, ) -> ( IndexVec>, IndexVec>, ) { - iter::zip(block_bxs.iter_enumerated(), cleanup_kinds) + iter::zip(block_llbbs.iter_enumerated(), cleanup_kinds) .map(|((bb, &llbb), cleanup_kind)| { match *cleanup_kind { - CleanupKind::Funclet if base::wants_msvc_seh(bx.sess()) => {} + CleanupKind::Funclet { .. } if base::wants_msvc_seh(bx.sess()) => { + // (cont'd below, outside the `match`) + } + + // GNU requires a landing pad *only* when unwinding into + // cleanup blocks, not when branching between them. + CleanupKind::Funclet { unwind_may_land_here: true } => { + let mut bx = bx.build_sibling_block("cleanup"); + let pad_bb = bx.llbb(); + + let llpersonality = bx.cx().eh_personality(); + let llretty = bx.cx().type_landing_pad(); + let lp = bx.landing_pad(llretty, llpersonality, 1); + bx.set_cleanup(lp); + + // FIXME(eddyb) consider not using `PlaceRef` for `slot`, to avoid + // having to take apart (and later put back together) the pair. + let slot = personality_slot.unwrap(); + slot.storage_live(&mut bx); + OperandValue::Pair(bx.extract_value(lp, 0), bx.extract_value(lp, 1)) + .store(&mut bx, slot); + + bx.br(llbb); + + return (Some(pad_bb), None); + } + _ => return (None, None), } let funclet; - let ret_llbb; + let pad_llbb; match 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 @@ -315,7 +357,7 @@ fn create_funclets<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( Some(&mir::TerminatorKind::Abort) => { let mut cs_bx = bx.build_sibling_block(&format!("cs_funclet{:?}", bb)); let mut cp_bx = bx.build_sibling_block(&format!("cp_funclet{:?}", bb)); - ret_llbb = cs_bx.llbb(); + pad_llbb = cs_bx.llbb(); let cs = cs_bx.catch_switch(None, None, 1); cs_bx.add_handler(cs, cp_bx.llbb()); @@ -333,13 +375,13 @@ fn create_funclets<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } _ => { let mut cleanup_bx = bx.build_sibling_block(&format!("funclet_{:?}", bb)); - ret_llbb = cleanup_bx.llbb(); + pad_llbb = cleanup_bx.llbb(); funclet = cleanup_bx.cleanup_pad(None, &[]); cleanup_bx.br(llbb); } }; - (Some(ret_llbb), Some(funclet)) + (Some(pad_llbb), Some(funclet)) }) .unzip() } diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index 634a20bda9bfd..9e6fb2a35cc0a 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -50,6 +50,11 @@ pub trait DerivedTypeMethods<'tcx>: BaseTypeMethods<'tcx> + MiscMethods<'tcx> { self.type_ptr_to_ext(self.type_i8(), address_space) } + // FIXME(eddyb) maybe the backend should get to control this? + fn type_landing_pad(&self) -> Self::Type { + self.type_struct(&[self.type_i8p(), self.type_i32()], false) + } + fn type_int(&self) -> Self::Type { match &self.sess().target.c_int_width[..] { "16" => self.type_i16(),