Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit b1b76aa

Browse files
committedApr 1, 2024
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 871df0d + 5a6e55a commit b1b76aa

File tree

9 files changed

+124
-59
lines changed

9 files changed

+124
-59
lines changed
 

‎Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -4375,6 +4375,7 @@ dependencies = [
43754375
"rustc_errors",
43764376
"rustc_fluent_macro",
43774377
"rustc_hir",
4378+
"rustc_index",
43784379
"rustc_macros",
43794380
"rustc_middle",
43804381
"rustc_session",

‎compiler/rustc_codegen_ssa/src/mir/mod.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -257,20 +257,19 @@ 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 = cx.tcx().reachable_blocks(instance);
265+
266266
// Codegen the body of each block using reverse postorder
267267
for (bb, _) in traversal::reverse_postorder(mir) {
268268
if reachable_blocks.contains(bb) {
269269
fx.codegen_block(bb);
270270
} 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.
271+
// We want to skip this block, because it's not reachable. But we still create
272+
// the block so terminators in other blocks can reference it.
274273
fx.codegen_block_as_unreachable(bb);
275274
}
276275
}

‎compiler/rustc_interface/src/passes.rs

+1
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,7 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock<Providers> = LazyLock::new(|| {
631631
rustc_lint::provide(providers);
632632
rustc_symbol_mangling::provide(providers);
633633
rustc_codegen_ssa::provide(providers);
634+
rustc_middle::mir::traversal::provide(providers);
634635
*providers
635636
});
636637

‎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

+73
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use crate::query::Providers;
2+
13
use super::*;
24

35
/// Preorder traversal of a graph.
@@ -279,3 +281,74 @@ pub fn reverse_postorder<'a, 'tcx>(
279281
{
280282
body.basic_blocks.reverse_postorder().iter().map(|&bb| (bb, &body.basic_blocks[bb]))
281283
}
284+
285+
/// Finds which basic blocks are actually reachable for a monomorphized [`Instance`].
286+
///
287+
/// This is allowed to have false positives; just because this says a block
288+
/// is reachable doesn't mean that's necessarily true. It's thus always
289+
/// legal for this to return a filled set.
290+
///
291+
/// Regardless, the [`BitSet::domain_size`] of the returned set will always
292+
/// exactly match the number of blocks in the body so that `contains`
293+
/// checks can be done without worrying about panicking.
294+
///
295+
/// This is mostly useful because it lets us skip lowering the `false` side
296+
/// of `if <T as Trait>::CONST`, as well as [`NullOp::UbChecks`].
297+
///
298+
/// [`NullOp::UbChecks`]: rustc_middle::mir::NullOp::UbChecks
299+
pub fn reachable_blocks<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> BitSet<BasicBlock> {
300+
let body = tcx.instance_mir(instance.def);
301+
let mut visitor = MonoReachable {
302+
body,
303+
tcx,
304+
instance,
305+
worklist: Vec::new(),
306+
visited: BitSet::new_empty(body.basic_blocks.len()),
307+
};
308+
309+
visitor.visited.insert(START_BLOCK);
310+
visitor.visit(START_BLOCK);
311+
312+
while let Some(bb) = visitor.worklist.pop() {
313+
if visitor.visited.insert(bb) {
314+
visitor.visit(bb);
315+
}
316+
}
317+
318+
visitor.visited
319+
}
320+
321+
struct MonoReachable<'a, 'tcx> {
322+
body: &'a Body<'tcx>,
323+
tcx: TyCtxt<'tcx>,
324+
instance: Instance<'tcx>,
325+
worklist: Vec<BasicBlock>,
326+
visited: BitSet<BasicBlock>,
327+
}
328+
329+
impl<'a, 'tcx> MonoReachable<'a, 'tcx> {
330+
fn visit(&mut self, bb: BasicBlock) {
331+
let block = &self.body.basic_blocks[bb];
332+
333+
if let Some((bits, targets)) =
334+
Body::try_const_mono_switchint(self.tcx, self.instance, block)
335+
{
336+
let target = targets.target_for_value(bits);
337+
self.push(target);
338+
} else {
339+
for target in block.terminator().successors() {
340+
self.push(target);
341+
}
342+
}
343+
}
344+
345+
fn push(&mut self, bb: BasicBlock) {
346+
if !self.visited.contains(bb) {
347+
self.worklist.push(bb);
348+
}
349+
}
350+
}
351+
352+
pub fn provide(providers: &mut Providers) {
353+
providers.reachable_blocks = reachable_blocks;
354+
}

‎compiler/rustc_middle/src/query/keys.rs

+17
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,15 @@ impl<'tcx> Key for ty::Instance<'tcx> {
8787
}
8888
}
8989

90+
impl<'tcx> AsLocalKey for ty::Instance<'tcx> {
91+
type LocalKey = Self;
92+
93+
#[inline(always)]
94+
fn as_local_key(&self) -> Option<Self::LocalKey> {
95+
self.def_id().is_local().then(|| *self)
96+
}
97+
}
98+
9099
impl<'tcx> Key for mir::interpret::GlobalId<'tcx> {
91100
type Cache<V> = DefaultCache<Self, V>;
92101

@@ -534,6 +543,14 @@ impl<'tcx> Key for (ty::Instance<'tcx>, &'tcx ty::List<Ty<'tcx>>) {
534543
}
535544
}
536545

546+
impl<'tcx> Key for (ty::Instance<'tcx>, &'tcx mir::Body<'tcx>) {
547+
type Cache<V> = DefaultCache<Self, V>;
548+
549+
fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
550+
self.0.default_span(tcx)
551+
}
552+
}
553+
537554
impl<'tcx> Key for (Ty<'tcx>, ty::ValTree<'tcx>) {
538555
type Cache<V> = DefaultCache<Self, V>;
539556

‎compiler/rustc_middle/src/query/mod.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ use rustc_hir::def_id::{
7171
};
7272
use rustc_hir::lang_items::{LangItem, LanguageItems};
7373
use rustc_hir::{Crate, ItemLocalId, ItemLocalMap, TraitCandidate};
74+
use rustc_index::bit_set::BitSet;
7475
use rustc_index::IndexVec;
7576
use rustc_query_system::ich::StableHashingContext;
7677
use rustc_query_system::query::{try_get_cached, QueryCache, QueryMode, QueryState};
@@ -270,7 +271,7 @@ rustc_queries! {
270271
feedable
271272
}
272273

273-
query unsizing_params_for_adt(key: DefId) -> &'tcx rustc_index::bit_set::BitSet<u32>
274+
query unsizing_params_for_adt(key: DefId) -> &'tcx BitSet<u32>
274275
{
275276
arena_cache
276277
desc { |tcx|
@@ -460,7 +461,7 @@ rustc_queries! {
460461
}
461462

462463
/// Set of param indexes for type params that are in the type's representation
463-
query params_in_repr(key: DefId) -> &'tcx rustc_index::bit_set::BitSet<u32> {
464+
query params_in_repr(key: DefId) -> &'tcx BitSet<u32> {
464465
desc { "finding type parameters in the representation" }
465466
arena_cache
466467
no_hash
@@ -2251,6 +2252,11 @@ rustc_queries! {
22512252
query find_field((def_id, ident): (DefId, rustc_span::symbol::Ident)) -> Option<rustc_target::abi::FieldIdx> {
22522253
desc { |tcx| "find the index of maybe nested field `{ident}` in `{}`", tcx.def_path_str(def_id) }
22532254
}
2255+
2256+
query reachable_blocks(instance: ty::Instance<'tcx>) -> &'tcx BitSet<mir::BasicBlock> {
2257+
arena_cache
2258+
desc { |tcx| "determining reachable blocks in `{}`", tcx.def_path_str(instance.def_id()) }
2259+
}
22542260
}
22552261

22562262
rustc_query_append! { define_callbacks! }

‎compiler/rustc_monomorphize/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ rustc_data_structures = { path = "../rustc_data_structures" }
99
rustc_errors = { path = "../rustc_errors" }
1010
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
1111
rustc_hir = { path = "../rustc_hir" }
12+
rustc_index = { path = "../rustc_index" }
1213
rustc_macros = { path = "../rustc_macros" }
1314
rustc_middle = { path = "../rustc_middle" }
1415
rustc_session = { path = "../rustc_session" }

‎compiler/rustc_monomorphize/src/collector.rs

+19
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ use rustc_hir as hir;
211211
use rustc_hir::def::DefKind;
212212
use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
213213
use rustc_hir::lang_items::LangItem;
214+
use rustc_index::bit_set::BitSet;
214215
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
215216
use rustc_middle::mir::interpret::{AllocId, ErrorHandled, GlobalAlloc, Scalar};
216217
use rustc_middle::mir::mono::{InstantiationMode, MonoItem};
@@ -671,6 +672,7 @@ struct MirUsedCollector<'a, 'tcx> {
671672
visiting_call_terminator: bool,
672673
/// Set of functions for which it is OK to move large data into.
673674
skip_move_check_fns: Option<Vec<DefId>>,
675+
reachable_blocks: Option<BitSet<mir::BasicBlock>>,
674676
}
675677

676678
impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> {
@@ -831,6 +833,17 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> {
831833
}
832834

833835
impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
836+
fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) {
837+
if self
838+
.reachable_blocks
839+
.as_ref()
840+
.expect("we should only walk blocks with CollectionMode::UsedItems")
841+
.contains(block)
842+
{
843+
self.super_basic_block_data(block, data)
844+
}
845+
}
846+
834847
fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
835848
debug!("visiting rvalue {:?}", *rvalue);
836849

@@ -1402,6 +1415,7 @@ fn collect_items_of_instance<'tcx>(
14021415
// added to `used_items` in a hash set, which can efficiently query in the
14031416
// `body.mentioned_items` loop below without even having to monomorphize the item.
14041417
let mut used_mentioned_items = FxHashSet::<MentionedItem<'tcx>>::default();
1418+
14051419
let mut collector = MirUsedCollector {
14061420
tcx,
14071421
body,
@@ -1411,6 +1425,11 @@ fn collect_items_of_instance<'tcx>(
14111425
move_size_spans: vec![],
14121426
visiting_call_terminator: false,
14131427
skip_move_check_fns: None,
1428+
reachable_blocks: if mode == CollectionMode::UsedItems {
1429+
Some(rustc_middle::mir::traversal::reachable_blocks(tcx, instance))
1430+
} else {
1431+
None
1432+
},
14141433
};
14151434

14161435
if mode == CollectionMode::UsedItems {

0 commit comments

Comments
 (0)
Failed to load comments.