Skip to content

Commit ec26dde

Browse files
Remove no-op cleanups on MIR post-monomorphization
This speeds up LLVM and improves codegen overall. As an example, for cargo this cuts ~5% of the LLVM IR lines we generate (measured with -Cno-prepopulate-passes).
1 parent d41e12f commit ec26dde

File tree

8 files changed

+180
-77
lines changed

8 files changed

+180
-77
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3505,6 +3505,7 @@ dependencies = [
35053505
"rustc_macros",
35063506
"rustc_metadata",
35073507
"rustc_middle",
3508+
"rustc_mir_transform",
35083509
"rustc_query_system",
35093510
"rustc_serialize",
35103511
"rustc_session",

compiler/rustc_codegen_ssa/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ rustc_index = { path = "../rustc_index" }
3030
rustc_macros = { path = "../rustc_macros" }
3131
rustc_metadata = { path = "../rustc_metadata" }
3232
rustc_middle = { path = "../rustc_middle" }
33+
rustc_mir_transform = { path = "../rustc_mir_transform" }
3334
rustc_query_system = { path = "../rustc_query_system" }
3435
rustc_serialize = { path = "../rustc_serialize" }
3536
rustc_session = { path = "../rustc_session" }

compiler/rustc_codegen_ssa/src/mir/analyze.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -279,10 +279,14 @@ impl CleanupKind {
279279
/// MSVC requires unwinding code to be split to a tree of *funclets*, where each funclet can only
280280
/// branch to itself or to its parent. Luckily, the code we generates matches this pattern.
281281
/// Recover that structure in an analyze pass.
282-
pub(crate) fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKind> {
282+
pub(crate) fn cleanup_kinds(
283+
mir: &mir::Body<'_>,
284+
nop_landing_pads: &DenseBitSet<mir::BasicBlock>,
285+
) -> IndexVec<mir::BasicBlock, CleanupKind> {
283286
fn discover_masters<'tcx>(
284287
result: &mut IndexSlice<mir::BasicBlock, CleanupKind>,
285288
mir: &mir::Body<'tcx>,
289+
nop_landing_pads: &DenseBitSet<mir::BasicBlock>,
286290
) {
287291
for (bb, data) in mir.basic_blocks.iter_enumerated() {
288292
match data.terminator().kind {
@@ -301,7 +305,9 @@ pub(crate) fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, Cl
301305
| TerminatorKind::InlineAsm { unwind, .. }
302306
| TerminatorKind::Assert { unwind, .. }
303307
| TerminatorKind::Drop { unwind, .. } => {
304-
if let mir::UnwindAction::Cleanup(unwind) = unwind {
308+
if let mir::UnwindAction::Cleanup(unwind) = unwind
309+
&& !nop_landing_pads.contains(unwind)
310+
{
305311
debug!(
306312
"cleanup_kinds: {:?}/{:?} registering {:?} as funclet",
307313
bb, data, unwind
@@ -382,7 +388,7 @@ pub(crate) fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, Cl
382388

383389
let mut result = IndexVec::from_elem(CleanupKind::NotCleanup, &mir.basic_blocks);
384390

385-
discover_masters(&mut result, mir);
391+
discover_masters(&mut result, mir, &nop_landing_pads);
386392
propagate(&mut result, mir);
387393
debug!("cleanup_kinds: result={:?}", result);
388394
result

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,13 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
200200
}
201201

202202
let unwind_block = match unwind {
203-
mir::UnwindAction::Cleanup(cleanup) => Some(self.llbb_with_cleanup(fx, cleanup)),
203+
mir::UnwindAction::Cleanup(cleanup) => {
204+
if !fx.nop_landing_pads.contains(cleanup) {
205+
Some(self.llbb_with_cleanup(fx, cleanup))
206+
} else {
207+
None
208+
}
209+
}
204210
mir::UnwindAction::Continue => None,
205211
mir::UnwindAction::Unreachable => None,
206212
mir::UnwindAction::Terminate(reason) => {
@@ -286,7 +292,13 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
286292
mergeable_succ: bool,
287293
) -> MergingSucc {
288294
let unwind_target = match unwind {
289-
mir::UnwindAction::Cleanup(cleanup) => Some(self.llbb_with_cleanup(fx, cleanup)),
295+
mir::UnwindAction::Cleanup(cleanup) => {
296+
if !fx.nop_landing_pads.contains(cleanup) {
297+
Some(self.llbb_with_cleanup(fx, cleanup))
298+
} else {
299+
None
300+
}
301+
}
290302
mir::UnwindAction::Terminate(reason) => Some(fx.terminate_block(reason)),
291303
mir::UnwindAction::Continue => None,
292304
mir::UnwindAction::Unreachable => None,

compiler/rustc_codegen_ssa/src/mir/mod.rs

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
9797
/// A cold block is a block that is unlikely to be executed at runtime.
9898
cold_blocks: IndexVec<mir::BasicBlock, bool>,
9999

100+
nop_landing_pads: DenseBitSet<mir::BasicBlock>,
101+
100102
/// The location where each MIR arg/var/tmp/ret is stored. This is
101103
/// usually an `PlaceRef` representing an alloca, but not always:
102104
/// sometimes we can skip the alloca and just store the value
@@ -176,8 +178,14 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
176178

177179
let mut mir = tcx.instance_mir(instance.def);
178180

179-
let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty());
180-
debug!("fn_abi: {:?}", fn_abi);
181+
let nop_landing_pads = rustc_mir_transform::remove_noop_landing_pads::find_noop_landing_pads(
182+
mir,
183+
Some(rustc_mir_transform::remove_noop_landing_pads::ExtraInfo {
184+
tcx,
185+
instance,
186+
typing_env: cx.typing_env(),
187+
}),
188+
);
181189

182190
if tcx.features().ergonomic_clones() {
183191
let monomorphized_mir = instance.instantiate_mir_and_normalize_erasing_regions(
@@ -188,19 +196,23 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
188196
mir = tcx.arena.alloc(optimize_use_clone::<Bx>(cx, monomorphized_mir));
189197
}
190198

199+
let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty());
200+
debug!("fn_abi: {:?}", fn_abi);
201+
191202
let debug_context = cx.create_function_debug_context(instance, fn_abi, llfn, &mir);
192203

193204
let start_llbb = Bx::append_block(cx, llfn, "start");
194205
let mut start_bx = Bx::build(cx, start_llbb);
195206

196-
if mir.basic_blocks.iter().any(|bb| {
197-
bb.is_cleanup || matches!(bb.terminator().unwind(), Some(mir::UnwindAction::Terminate(_)))
207+
if mir::traversal::mono_reachable(&mir, tcx, instance).any(|(bb, block)| {
208+
(block.is_cleanup && !nop_landing_pads.contains(bb))
209+
|| matches!(block.terminator().unwind(), Some(mir::UnwindAction::Terminate(_)))
198210
}) {
199211
start_bx.set_personality_fn(cx.eh_personality());
200212
}
201213

202-
let cleanup_kinds =
203-
base::wants_new_eh_instructions(tcx.sess).then(|| analyze::cleanup_kinds(&mir));
214+
let cleanup_kinds = base::wants_new_eh_instructions(tcx.sess)
215+
.then(|| analyze::cleanup_kinds(&mir, &nop_landing_pads));
204216

205217
let cached_llbbs: IndexVec<mir::BasicBlock, CachedLlbb<Bx::BasicBlock>> =
206218
mir.basic_blocks
@@ -228,6 +240,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
228240
debug_context,
229241
per_local_var_debug_info: None,
230242
caller_location: None,
243+
nop_landing_pads,
231244
};
232245

233246
// It may seem like we should iterate over `required_consts` to ensure they all successfully
@@ -239,7 +252,36 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
239252
fx.compute_per_local_var_debug_info(&mut start_bx).unzip();
240253
fx.per_local_var_debug_info = per_local_var_debug_info;
241254

242-
let traversal_order = traversal::mono_reachable_reverse_postorder(mir, tcx, instance);
255+
let mut traversal_order = traversal::mono_reachable_reverse_postorder(mir, tcx, instance);
256+
257+
// Filter out blocks that won't be codegen'd because of nop_landing_pads optimization.
258+
// FIXME: We might want to integrate the nop_landing_pads analysis into mono reachability.
259+
{
260+
let mut reachable = DenseBitSet::new_empty(mir.basic_blocks.len());
261+
let mut to_visit = vec![mir::START_BLOCK];
262+
while let Some(next) = to_visit.pop() {
263+
if !reachable.insert(next) {
264+
continue;
265+
}
266+
267+
let block = &mir.basic_blocks[next];
268+
if let Some(mir::UnwindAction::Cleanup(target)) = block.terminator().unwind()
269+
&& fx.nop_landing_pads.contains(*target)
270+
{
271+
// This edge will not be followed when we actually codegen, so skip generating it here.
272+
//
273+
// It's guaranteed that the cleanup block (`target`) occurs only in
274+
// UnwindAction::Cleanup(...) -- i.e., we can't incorrectly filter too much here --
275+
// because cleanup transitions must happen via UnwindAction::Cleanup.
276+
to_visit.extend(block.terminator().successors().filter(|s| s != target));
277+
} else {
278+
to_visit.extend(block.terminator().successors());
279+
}
280+
}
281+
282+
traversal_order.retain(|bb| reachable.contains(*bb));
283+
}
284+
243285
let memory_locals = analyze::non_ssa_locals(&fx, &traversal_order);
244286

245287
// Allocate variable and temp allocas

compiler/rustc_mir_transform/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ declare_passes! {
158158
mod prettify : ReorderBasicBlocks, ReorderLocals;
159159
mod promote_consts : PromoteTemps;
160160
mod ref_prop : ReferencePropagation;
161-
mod remove_noop_landing_pads : RemoveNoopLandingPads;
161+
pub mod remove_noop_landing_pads : RemoveNoopLandingPads;
162162
mod remove_place_mention : RemovePlaceMention;
163163
mod remove_storage_markers : RemoveStorageMarkers;
164164
mod remove_uninit_drops : RemoveUninitDrops;

0 commit comments

Comments
 (0)