Skip to content

Commit a237415

Browse files
committed
Auto merge of rust-lang#123272 - saethlin:reachable-mono-cleanup, r=<try>
Only collect mono items from reachable blocks Fixes the wrong commented pointed out in: rust-lang#121421 (comment) Moves the analysis to use the worklist strategy: rust-lang#121421 (comment) Also fixes rust-lang#85836, using the same reachability analysis
2 parents b38b6ca + 97df1ef commit a237415

File tree

4 files changed

+99
-66
lines changed

4 files changed

+99
-66
lines changed

compiler/rustc_codegen_ssa/src/mir/mod.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -257,20 +257,20 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
257257
// Apply debuginfo to the newly allocated locals.
258258
fx.debug_introduce_locals(&mut start_bx);
259259

260-
let reachable_blocks = mir.reachable_blocks_in_mono(cx.tcx(), instance);
261-
262260
// The builders will be created separately for each basic block at `codegen_block`.
263261
// So drop the builder of `start_llbb` to avoid having two at the same time.
264262
drop(start_bx);
265263

264+
let reachable_blocks =
265+
rustc_middle::mir::traversal::mono_reachable_bitset(mir, cx.tcx(), instance);
266+
266267
// Codegen the body of each block using reverse postorder
267268
for (bb, _) in traversal::reverse_postorder(mir) {
268269
if reachable_blocks.contains(bb) {
269270
fx.codegen_block(bb);
270271
} else {
271-
// This may have references to things we didn't monomorphize, so we
272-
// don't actually codegen the body. We still create the block so
273-
// terminators in other blocks can reference it without worry.
272+
// We want to skip this block, because it's not reachable. But we still create
273+
// the block so terminators in other blocks can reference it.
274274
fx.codegen_block_as_unreachable(bb);
275275
}
276276
}

compiler/rustc_middle/src/mir/mod.rs

-52
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ pub use rustc_ast::Mutability;
3030
use rustc_data_structures::fx::FxHashMap;
3131
use rustc_data_structures::fx::FxHashSet;
3232
use rustc_data_structures::graph::dominators::Dominators;
33-
use rustc_data_structures::stack::ensure_sufficient_stack;
3433
use rustc_index::bit_set::BitSet;
3534
use rustc_index::{Idx, IndexSlice, IndexVec};
3635
use rustc_serialize::{Decodable, Encodable};
@@ -687,57 +686,6 @@ impl<'tcx> Body<'tcx> {
687686
self.injection_phase.is_some()
688687
}
689688

690-
/// Finds which basic blocks are actually reachable for a specific
691-
/// monomorphization of this body.
692-
///
693-
/// This is allowed to have false positives; just because this says a block
694-
/// is reachable doesn't mean that's necessarily true. It's thus always
695-
/// legal for this to return a filled set.
696-
///
697-
/// Regardless, the [`BitSet::domain_size`] of the returned set will always
698-
/// exactly match the number of blocks in the body so that `contains`
699-
/// checks can be done without worrying about panicking.
700-
///
701-
/// This is mostly useful because it lets us skip lowering the `false` side
702-
/// of `if <T as Trait>::CONST`, as well as `intrinsics::debug_assertions`.
703-
pub fn reachable_blocks_in_mono(
704-
&self,
705-
tcx: TyCtxt<'tcx>,
706-
instance: Instance<'tcx>,
707-
) -> BitSet<BasicBlock> {
708-
let mut set = BitSet::new_empty(self.basic_blocks.len());
709-
self.reachable_blocks_in_mono_from(tcx, instance, &mut set, START_BLOCK);
710-
set
711-
}
712-
713-
fn reachable_blocks_in_mono_from(
714-
&self,
715-
tcx: TyCtxt<'tcx>,
716-
instance: Instance<'tcx>,
717-
set: &mut BitSet<BasicBlock>,
718-
bb: BasicBlock,
719-
) {
720-
if !set.insert(bb) {
721-
return;
722-
}
723-
724-
let data = &self.basic_blocks[bb];
725-
726-
if let Some((bits, targets)) = Self::try_const_mono_switchint(tcx, instance, data) {
727-
let target = targets.target_for_value(bits);
728-
ensure_sufficient_stack(|| {
729-
self.reachable_blocks_in_mono_from(tcx, instance, set, target)
730-
});
731-
return;
732-
}
733-
734-
for target in data.terminator().successors() {
735-
ensure_sufficient_stack(|| {
736-
self.reachable_blocks_in_mono_from(tcx, instance, set, target)
737-
});
738-
}
739-
}
740-
741689
/// If this basic block ends with a [`TerminatorKind::SwitchInt`] for which we can evaluate the
742690
/// dimscriminant in monomorphization, we return the discriminant bits and the
743691
/// [`SwitchTargets`], just so the caller doesn't also have to match on the terminator.

compiler/rustc_middle/src/mir/traversal.rs

+84
Original file line numberDiff line numberDiff line change
@@ -279,3 +279,87 @@ pub fn reverse_postorder<'a, 'tcx>(
279279
{
280280
body.basic_blocks.reverse_postorder().iter().map(|&bb| (bb, &body.basic_blocks[bb]))
281281
}
282+
283+
#[derive(Clone)]
284+
pub struct MonoReachable<'a, 'tcx> {
285+
body: &'a Body<'tcx>,
286+
tcx: TyCtxt<'tcx>,
287+
instance: Instance<'tcx>,
288+
visited: BitSet<BasicBlock>,
289+
worklist: Vec<BasicBlock>,
290+
}
291+
292+
impl<'a, 'tcx> MonoReachable<'a, 'tcx> {
293+
pub fn new(
294+
body: &'a Body<'tcx>,
295+
tcx: TyCtxt<'tcx>,
296+
instance: Instance<'tcx>,
297+
) -> MonoReachable<'a, 'tcx> {
298+
MonoReachable {
299+
body,
300+
tcx,
301+
instance,
302+
visited: BitSet::new_empty(body.basic_blocks.len()),
303+
worklist: vec![START_BLOCK],
304+
}
305+
}
306+
}
307+
308+
/// Finds which basic blocks are actually reachable for a monomorphized [`Instance`].
309+
///
310+
/// This is allowed to have false positives; just because this says a block
311+
/// is reachable doesn't mean that's necessarily true. It's thus always
312+
/// legal for this to return a filled set.
313+
///
314+
/// Regardless, the [`BitSet::domain_size`] of the returned set will always
315+
/// exactly match the number of blocks in the body so that `contains`
316+
/// checks can be done without worrying about panicking.
317+
///
318+
/// This is mostly useful because it lets us skip lowering the `false` side
319+
/// of `if <T as Trait>::CONST`, as well as [`NullOp::UbChecks`].
320+
///
321+
/// [`NullOp::UbChecks`]: rustc_middle::mir::NullOp::UbChecks
322+
pub fn mono_reachable<'a, 'tcx>(
323+
body: &'a Body<'tcx>,
324+
tcx: TyCtxt<'tcx>,
325+
instance: Instance<'tcx>,
326+
) -> MonoReachable<'a, 'tcx> {
327+
MonoReachable::new(body, tcx, instance)
328+
}
329+
330+
pub fn mono_reachable_bitset<'a, 'tcx>(
331+
body: &'a Body<'tcx>,
332+
tcx: TyCtxt<'tcx>,
333+
instance: Instance<'tcx>,
334+
) -> BitSet<BasicBlock> {
335+
let mut it = MonoReachable::new(body, tcx, instance);
336+
while let Some(_) = it.next() {}
337+
it.visited
338+
}
339+
340+
impl<'a, 'tcx> Iterator for MonoReachable<'a, 'tcx> {
341+
type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
342+
343+
fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
344+
while let Some(idx) = self.worklist.pop() {
345+
if !self.visited.insert(idx) {
346+
continue;
347+
}
348+
349+
let data = &self.body[idx];
350+
351+
if let Some((bits, targets)) =
352+
Body::try_const_mono_switchint(self.tcx, self.instance, data)
353+
{
354+
let target = targets.target_for_value(bits);
355+
self.worklist.push(target);
356+
} else {
357+
self.worklist.extend(data.terminator().successors());
358+
}
359+
360+
return Some((idx, data));
361+
}
362+
363+
None
364+
}
365+
}

compiler/rustc_monomorphize/src/collector.rs

+10-9
Original file line numberDiff line numberDiff line change
@@ -1414,15 +1414,16 @@ fn collect_items_of_instance<'tcx>(
14141414
};
14151415

14161416
if mode == CollectionMode::UsedItems {
1417-
// Visit everything. Here we rely on the visitor also visiting `required_consts`, so that we
1418-
// evaluate them and abort compilation if any of them errors.
1419-
collector.visit_body(body);
1420-
} else {
1421-
// We only need to evaluate all constants, but can ignore the rest of the MIR.
1422-
for const_op in &body.required_consts {
1423-
if let Some(val) = collector.eval_constant(const_op) {
1424-
collect_const_value(tcx, val, mentioned_items);
1425-
}
1417+
for (bb, data) in body.basic_blocks.iter_enumerated() {
1418+
collector.visit_basic_block_data(bb, data)
1419+
}
1420+
}
1421+
1422+
// Always visit all `required_consts`, so that we evaluate them and abort compilation if any of
1423+
// them errors.
1424+
for const_op in &body.required_consts {
1425+
if let Some(val) = collector.eval_constant(const_op) {
1426+
collect_const_value(tcx, val, mentioned_items);
14261427
}
14271428
}
14281429

0 commit comments

Comments
 (0)