Skip to content

Commit ac3a8f9

Browse files
committed
rustc_codegen_ssa: eagerly create landing pad blocks.
1 parent b849326 commit ac3a8f9

File tree

4 files changed

+96
-84
lines changed

4 files changed

+96
-84
lines changed

compiler/rustc_codegen_ssa/src/mir/analyze.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -333,15 +333,22 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
333333
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
334334
pub enum CleanupKind {
335335
NotCleanup,
336-
Funclet,
337-
Internal { funclet: mir::BasicBlock },
336+
Funclet {
337+
/// If `true`, there is at least one unwind/cleanup edge to this block.
338+
/// On GNU, landing pads are only needed for unwind/cleanup edges, while
339+
/// on MSVC, every funclet has to start with a cleanup pad.
340+
unwind_may_land_here: bool,
341+
},
342+
Internal {
343+
funclet: mir::BasicBlock,
344+
},
338345
}
339346

340347
impl CleanupKind {
341348
pub fn funclet_bb(self, for_bb: mir::BasicBlock) -> Option<mir::BasicBlock> {
342349
match self {
343350
CleanupKind::NotCleanup => None,
344-
CleanupKind::Funclet => Some(for_bb),
351+
CleanupKind::Funclet { .. } => Some(for_bb),
345352
CleanupKind::Internal { funclet } => Some(funclet),
346353
}
347354
}
@@ -374,7 +381,7 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKi
374381
"cleanup_kinds: {:?}/{:?} registering {:?} as funclet",
375382
bb, data, unwind
376383
);
377-
result[unwind] = CleanupKind::Funclet;
384+
result[unwind] = CleanupKind::Funclet { unwind_may_land_here: true };
378385
}
379386
}
380387
}
@@ -405,7 +412,7 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKi
405412
for (bb, data) in traversal::reverse_postorder(mir) {
406413
let funclet = match result[bb] {
407414
CleanupKind::NotCleanup => continue,
408-
CleanupKind::Funclet => bb,
415+
CleanupKind::Funclet { .. } => bb,
409416
CleanupKind::Internal { funclet } => funclet,
410417
};
411418

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

424+
// FIXME(eddyb) ensure there are no unwind/cleanup edges *between*
425+
// cleanup blocks, as everything else assumes they're impossible.
417426
for &succ in data.terminator().successors() {
418427
let kind = result[succ];
419428
debug!("cleanup_kinds: propagating {:?} to {:?}/{:?}", funclet, succ, kind);
420429
match kind {
421430
CleanupKind::NotCleanup => {
422431
result[succ] = CleanupKind::Internal { funclet };
423432
}
424-
CleanupKind::Funclet => {
433+
CleanupKind::Funclet { .. } => {
425434
if funclet != succ {
426435
set_successor(funclet, succ);
427436
}
@@ -435,7 +444,7 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKi
435444
"promoting {:?} to a funclet and updating {:?}",
436445
succ, succ_funclet
437446
);
438-
result[succ] = CleanupKind::Funclet;
447+
result[succ] = CleanupKind::Funclet { unwind_may_land_here: false };
439448
set_successor(succ_funclet, succ);
440449
set_successor(funclet, succ);
441450
}

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 17 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use super::analyze::CleanupKind;
12
use super::operand::OperandRef;
23
use super::operand::OperandValue::{Immediate, Pair, Ref};
34
use super::place::PlaceRef;
@@ -20,8 +21,8 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
2021
use rustc_middle::ty::{self, Instance, Ty, TypeFoldable};
2122
use rustc_span::source_map::Span;
2223
use rustc_span::{sym, Symbol};
24+
use rustc_target::abi;
2325
use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode};
24-
use rustc_target::abi::{self, LayoutOf};
2526
use rustc_target::spec::abi::Abi;
2627

2728
/// Used by `FunctionCx::codegen_terminator` for emitting common patterns
@@ -49,16 +50,22 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
4950
) -> (Bx::BasicBlock, bool) {
5051
let span = self.terminator.source_info.span;
5152
let lltarget = fx.blocks[target];
52-
let target_funclet = fx.cleanup_kinds[target].funclet_bb(target);
53-
match (self.funclet_bb, target_funclet) {
53+
let target_cleanup_kind = fx.cleanup_kinds[target];
54+
match (self.funclet_bb, target_cleanup_kind.funclet_bb(target)) {
5455
(None, None) => (lltarget, false),
5556
(Some(f), Some(t_f)) if f == t_f || !base::wants_msvc_seh(fx.cx.tcx().sess) => {
5657
(lltarget, false)
5758
}
58-
// jump *into* cleanup - need a landing pad if GNU
59-
(None, Some(_)) => (fx.landing_pad_to(target), false),
59+
// Unwind *into* cleanup - need a landing/cleanup pad.
60+
(None, Some(_)) => {
61+
assert_eq!(
62+
target_cleanup_kind,
63+
CleanupKind::Funclet { unwind_may_land_here: true }
64+
);
65+
(fx.landing_pads[target].unwrap(), false)
66+
}
6067
(Some(_), None) => span_bug!(span, "{:?} - jump out of cleanup?", self.terminator),
61-
(Some(_), Some(_)) => (fx.landing_pad_to(target), true),
68+
(Some(_), Some(_)) => (fx.landing_pads[target].unwrap(), true),
6269
}
6370
}
6471

@@ -155,14 +162,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
155162
if let Some(funclet) = helper.funclet(self) {
156163
bx.cleanup_ret(funclet, None);
157164
} else {
158-
let slot = self.get_personality_slot(&mut bx);
165+
// FIXME(eddyb) consider not using `PlaceRef` for `slot`, to avoid
166+
// having to take apart (and later put back together) the pair.
167+
let slot = self.personality_slot.unwrap();
159168
let lp0 = slot.project_field(&mut bx, 0);
160169
let lp0 = bx.load_operand(lp0).immediate();
161170
let lp1 = slot.project_field(&mut bx, 1);
162171
let lp1 = bx.load_operand(lp1).immediate();
163172
slot.storage_dead(&mut bx);
164173

165-
let mut lp = bx.const_undef(self.landing_pad_type());
174+
let mut lp = bx.cx().const_undef(bx.cx().type_landing_pad());
166175
lp = bx.insert_value(lp, lp0, 0);
167176
lp = bx.insert_value(lp, lp1, 1);
168177
bx.resume(lp);
@@ -1178,59 +1187,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
11781187
self.caller_location.unwrap_or_else(|| span_to_caller_location(source_info.span))
11791188
}
11801189

1181-
fn get_personality_slot(&mut self, bx: &mut Bx) -> PlaceRef<'tcx, Bx::Value> {
1182-
let cx = bx.cx();
1183-
if let Some(slot) = self.personality_slot {
1184-
slot
1185-
} else {
1186-
let layout = cx.layout_of(
1187-
cx.tcx().intern_tup(&[cx.tcx().mk_mut_ptr(cx.tcx().types.u8), cx.tcx().types.i32]),
1188-
);
1189-
let slot = PlaceRef::alloca(bx, layout);
1190-
self.personality_slot = Some(slot);
1191-
slot
1192-
}
1193-
}
1194-
1195-
/// Returns the landing-pad wrapper around the given basic block.
1196-
///
1197-
/// No-op in MSVC SEH scheme.
1198-
fn landing_pad_to(&mut self, target_bb: mir::BasicBlock) -> Bx::BasicBlock {
1199-
if let Some(block) = self.landing_pads[target_bb] {
1200-
return block;
1201-
}
1202-
1203-
let block = self.blocks[target_bb];
1204-
let landing_pad = self.landing_pad_uncached(block);
1205-
self.landing_pads[target_bb] = Some(landing_pad);
1206-
landing_pad
1207-
}
1208-
1209-
fn landing_pad_uncached(&mut self, target_bb: Bx::BasicBlock) -> Bx::BasicBlock {
1210-
if base::wants_msvc_seh(self.cx.sess()) {
1211-
span_bug!(self.mir.span, "landing pad was not inserted?")
1212-
}
1213-
1214-
let mut bx = self.new_block("cleanup");
1215-
1216-
let llpersonality = self.cx.eh_personality();
1217-
let llretty = self.landing_pad_type();
1218-
let lp = bx.landing_pad(llretty, llpersonality, 1);
1219-
bx.set_cleanup(lp);
1220-
1221-
let slot = self.get_personality_slot(&mut bx);
1222-
slot.storage_live(&mut bx);
1223-
Pair(bx.extract_value(lp, 0), bx.extract_value(lp, 1)).store(&mut bx, slot);
1224-
1225-
bx.br(target_bb);
1226-
bx.llbb()
1227-
}
1228-
1229-
fn landing_pad_type(&self) -> Bx::Type {
1230-
let cx = self.cx;
1231-
cx.type_struct(&[cx.type_i8p(), cx.type_i32()], false)
1232-
}
1233-
12341190
fn unreachable_block(&mut self) -> Bx::BasicBlock {
12351191
self.unreachable_block.unwrap_or_else(|| {
12361192
let mut bx = self.new_block("unreachable");

compiler/rustc_codegen_ssa/src/mir/mod.rs

Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use rustc_middle::mir::interpret::ErrorHandled;
66
use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt, TyAndLayout};
77
use rustc_middle::ty::{self, Instance, Ty, TypeFoldable};
88
use rustc_target::abi::call::{FnAbi, PassMode};
9-
use rustc_target::abi::HasDataLayout;
9+
use rustc_target::abi::{HasDataLayout, LayoutOf};
1010

1111
use std::iter;
1212

@@ -40,7 +40,7 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
4040
/// value used for C++ unwinding, which must filter by type: we
4141
/// don't really care about it very much. Anyway, this value
4242
/// contains an alloca into which the personality is stored and
43-
/// then later loaded when generating the DIVERGE_BLOCK.
43+
/// then later loaded when generating `resume` terminators.
4444
personality_slot: Option<PlaceRef<'tcx, Bx::Value>>,
4545

4646
/// A `Block` for each MIR `BasicBlock`
@@ -145,15 +145,29 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
145145

146146
let mut bx = Bx::new_block(cx, llfn, "start");
147147

148-
if mir.basic_blocks().iter().any(|bb| bb.is_cleanup) {
148+
let personality_slot = if mir.basic_blocks().iter().any(|bb| bb.is_cleanup) {
149149
bx.set_personality_fn(cx.eh_personality());
150-
}
150+
151+
if base::wants_msvc_seh(bx.sess()) {
152+
// MSVC doesn't use landing pads.
153+
None
154+
} else {
155+
// FIXME(eddyb) the use of `PlaceRef` requires a Rust type, and taking apart
156+
// (and later putting back together) the pair, instead of treating it opaquely.
157+
let layout = cx.layout_of(
158+
cx.tcx().intern_tup(&[cx.tcx().mk_mut_ptr(cx.tcx().types.u8), cx.tcx().types.i32]),
159+
);
160+
Some(PlaceRef::alloca(&mut bx, layout))
161+
}
162+
} else {
163+
None
164+
};
151165

152166
let cleanup_kinds = analyze::cleanup_kinds(&mir);
153167
// Allocate a `Block` for every basic block, except
154168
// the start block, if nothing loops back to it.
155169
let reentrant_start_block = !mir.predecessors()[mir::START_BLOCK].is_empty();
156-
let block_bxs: IndexVec<mir::BasicBlock, Bx::BasicBlock> = mir
170+
let block_llbbs: IndexVec<mir::BasicBlock, Bx::BasicBlock> = mir
157171
.basic_blocks()
158172
.indices()
159173
.map(|bb| {
@@ -165,15 +179,16 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
165179
})
166180
.collect();
167181

168-
let (landing_pads, funclets) = create_funclets(&mir, &mut bx, &cleanup_kinds, &block_bxs);
182+
let (landing_pads, funclets) =
183+
landing_pads_and_funclets(&mir, &mut bx, &cleanup_kinds, &block_llbbs, personality_slot);
169184
let mut fx = FunctionCx {
170185
instance,
171186
mir,
172187
llfn,
173188
fn_abi,
174189
cx,
175-
personality_slot: None,
176-
blocks: block_bxs,
190+
personality_slot,
191+
blocks: block_llbbs,
177192
unreachable_block: None,
178193
cleanup_kinds,
179194
landing_pads,
@@ -273,24 +288,51 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
273288
}
274289
}
275290

276-
fn create_funclets<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
291+
fn landing_pads_and_funclets<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
277292
mir: &'tcx mir::Body<'tcx>,
278293
bx: &mut Bx,
279294
cleanup_kinds: &IndexVec<mir::BasicBlock, CleanupKind>,
280-
block_bxs: &IndexVec<mir::BasicBlock, Bx::BasicBlock>,
295+
block_llbbs: &IndexVec<mir::BasicBlock, Bx::BasicBlock>,
296+
personality_slot: Option<PlaceRef<'tcx, Bx::Value>>,
281297
) -> (
282298
IndexVec<mir::BasicBlock, Option<Bx::BasicBlock>>,
283299
IndexVec<mir::BasicBlock, Option<Bx::Funclet>>,
284300
) {
285-
iter::zip(block_bxs.iter_enumerated(), cleanup_kinds)
301+
iter::zip(block_llbbs.iter_enumerated(), cleanup_kinds)
286302
.map(|((bb, &llbb), cleanup_kind)| {
287303
match *cleanup_kind {
288-
CleanupKind::Funclet if base::wants_msvc_seh(bx.sess()) => {}
304+
CleanupKind::Funclet { .. } if base::wants_msvc_seh(bx.sess()) => {
305+
// (cont'd below, outside the `match`)
306+
}
307+
308+
// GNU requires a landing pad *only* when unwinding into
309+
// cleanup blocks, not when branching between them.
310+
CleanupKind::Funclet { unwind_may_land_here: true } => {
311+
let mut bx = bx.build_sibling_block("cleanup");
312+
let pad_bb = bx.llbb();
313+
314+
let llpersonality = bx.cx().eh_personality();
315+
let llretty = bx.cx().type_landing_pad();
316+
let lp = bx.landing_pad(llretty, llpersonality, 1);
317+
bx.set_cleanup(lp);
318+
319+
// FIXME(eddyb) consider not using `PlaceRef` for `slot`, to avoid
320+
// having to take apart (and later put back together) the pair.
321+
let slot = personality_slot.unwrap();
322+
slot.storage_live(&mut bx);
323+
OperandValue::Pair(bx.extract_value(lp, 0), bx.extract_value(lp, 1))
324+
.store(&mut bx, slot);
325+
326+
bx.br(llbb);
327+
328+
return (Some(pad_bb), None);
329+
}
330+
289331
_ => return (None, None),
290332
}
291333

292334
let funclet;
293-
let ret_llbb;
335+
let pad_llbb;
294336
match mir[bb].terminator.as_ref().map(|t| &t.kind) {
295337
// This is a basic block that we're aborting the program for,
296338
// notably in an `extern` function. These basic blocks are inserted
@@ -315,7 +357,7 @@ fn create_funclets<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
315357
Some(&mir::TerminatorKind::Abort) => {
316358
let mut cs_bx = bx.build_sibling_block(&format!("cs_funclet{:?}", bb));
317359
let mut cp_bx = bx.build_sibling_block(&format!("cp_funclet{:?}", bb));
318-
ret_llbb = cs_bx.llbb();
360+
pad_llbb = cs_bx.llbb();
319361

320362
let cs = cs_bx.catch_switch(None, None, 1);
321363
cs_bx.add_handler(cs, cp_bx.llbb());
@@ -333,13 +375,13 @@ fn create_funclets<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
333375
}
334376
_ => {
335377
let mut cleanup_bx = bx.build_sibling_block(&format!("funclet_{:?}", bb));
336-
ret_llbb = cleanup_bx.llbb();
378+
pad_llbb = cleanup_bx.llbb();
337379
funclet = cleanup_bx.cleanup_pad(None, &[]);
338380
cleanup_bx.br(llbb);
339381
}
340382
};
341383

342-
(Some(ret_llbb), Some(funclet))
384+
(Some(pad_llbb), Some(funclet))
343385
})
344386
.unzip()
345387
}

compiler/rustc_codegen_ssa/src/traits/type_.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ pub trait DerivedTypeMethods<'tcx>: BaseTypeMethods<'tcx> + MiscMethods<'tcx> {
5050
self.type_ptr_to_ext(self.type_i8(), address_space)
5151
}
5252

53+
// FIXME(eddyb) maybe the backend should get to control this?
54+
fn type_landing_pad(&self) -> Self::Type {
55+
self.type_struct(&[self.type_i8p(), self.type_i32()], false)
56+
}
57+
5358
fn type_int(&self) -> Self::Type {
5459
match &self.sess().target.c_int_width[..] {
5560
"16" => self.type_i16(),

0 commit comments

Comments
 (0)