Skip to content

rustc_codegen_ssa: eagerly create landing pad blocks. #84699

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions compiler/rustc_codegen_ssa/src/mir/analyze.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<mir::BasicBlock> {
match self {
CleanupKind::NotCleanup => None,
CleanupKind::Funclet => Some(for_bb),
CleanupKind::Funclet { .. } => Some(for_bb),
CleanupKind::Internal { funclet } => Some(funclet),
}
}
Expand Down Expand Up @@ -374,7 +381,7 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKi
"cleanup_kinds: {:?}/{:?} registering {:?} as funclet",
bb, data, unwind
);
result[unwind] = CleanupKind::Funclet;
result[unwind] = CleanupKind::Funclet { unwind_may_land_here: true };
}
}
}
Expand Down Expand Up @@ -405,7 +412,7 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKi
for (bb, data) in traversal::reverse_postorder(mir) {
let funclet = match result[bb] {
CleanupKind::NotCleanup => continue,
CleanupKind::Funclet => bb,
CleanupKind::Funclet { .. } => bb,
CleanupKind::Internal { funclet } => funclet,
};

Expand All @@ -414,14 +421,16 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKi
bb, data, result[bb], funclet
);

// FIXME(eddyb) ensure there are no unwind/cleanup edges *between*
// cleanup blocks, as everything else assumes they're impossible.
for &succ in data.terminator().successors() {
let kind = result[succ];
debug!("cleanup_kinds: propagating {:?} to {:?}/{:?}", funclet, succ, kind);
match kind {
CleanupKind::NotCleanup => {
result[succ] = CleanupKind::Internal { funclet };
}
CleanupKind::Funclet => {
CleanupKind::Funclet { .. } => {
if funclet != succ {
set_successor(funclet, succ);
}
Expand All @@ -435,7 +444,7 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKi
"promoting {:?} to a funclet and updating {:?}",
succ, succ_funclet
);
result[succ] = CleanupKind::Funclet;
result[succ] = CleanupKind::Funclet { unwind_may_land_here: false };
set_successor(succ_funclet, succ);
set_successor(funclet, succ);
}
Expand Down
78 changes: 17 additions & 61 deletions compiler/rustc_codegen_ssa/src/mir/block.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use super::analyze::CleanupKind;
use super::operand::OperandRef;
use super::operand::OperandValue::{Immediate, Pair, Ref};
use super::place::PlaceRef;
Expand All @@ -20,8 +21,8 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, Instance, Ty, TypeFoldable};
use rustc_span::source_map::Span;
use rustc_span::{sym, Symbol};
use rustc_target::abi;
use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode};
use rustc_target::abi::{self, LayoutOf};
use rustc_target::spec::abi::Abi;

/// Used by `FunctionCx::codegen_terminator` for emitting common patterns
Expand Down Expand Up @@ -49,16 +50,22 @@ impl<'a, 'tcx> 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),
}
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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");
Expand Down
74 changes: 58 additions & 16 deletions compiler/rustc_codegen_ssa/src/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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<PlaceRef<'tcx, Bx::Value>>,

/// A `Block` for each MIR `BasicBlock`
Expand Down Expand Up @@ -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::BasicBlock, Bx::BasicBlock> = mir
let block_llbbs: IndexVec<mir::BasicBlock, Bx::BasicBlock> = mir
.basic_blocks()
.indices()
.map(|bb| {
Expand All @@ -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,
Expand Down Expand Up @@ -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<mir::BasicBlock, CleanupKind>,
block_bxs: &IndexVec<mir::BasicBlock, Bx::BasicBlock>,
block_llbbs: &IndexVec<mir::BasicBlock, Bx::BasicBlock>,
personality_slot: Option<PlaceRef<'tcx, Bx::Value>>,
) -> (
IndexVec<mir::BasicBlock, Option<Bx::BasicBlock>>,
IndexVec<mir::BasicBlock, Option<Bx::Funclet>>,
) {
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
Expand All @@ -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());
Expand All @@ -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()
}
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_codegen_ssa/src/traits/type_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down