Skip to content

Commit d0df954

Browse files
committed
Auto merge of #122862 - RalfJung:collect-all-mono, r=<try>
collector: always consider all monomorphic functions to be 'mentioned' This would fix #122814. But it's probably not going to be cheap... Ideally we'd avoid building the optimized MIR for these new roots, and only request `mir_drops_elaborated_and_const_checked` -- but that MIR is often getting stolen so I don't see a way to do that. TODO before landing: - [ ] Figure out if there is a testcase [here](#122814 (comment)). r? `@oli-obk` `@tmiasko`
2 parents cdb683f + 59803ef commit d0df954

19 files changed

+267
-60
lines changed

compiler/rustc_monomorphize/src/collector.rs

+73-31
Original file line numberDiff line numberDiff line change
@@ -1513,16 +1513,26 @@ fn collect_const_value<'tcx>(
15131513
// Find all non-generic items by walking the HIR. These items serve as roots to
15141514
// start monomorphizing from.
15151515
#[instrument(skip(tcx, mode), level = "debug")]
1516-
fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionStrategy) -> Vec<MonoItem<'_>> {
1516+
fn collect_roots(
1517+
tcx: TyCtxt<'_>,
1518+
mode: MonoItemCollectionStrategy,
1519+
) -> Vec<(MonoItem<'_>, CollectionMode)> {
15171520
debug!("collecting roots");
1518-
let mut roots = Vec::new();
1521+
let mut used_roots = MonoItems::new();
1522+
let mut mentioned_roots = MonoItems::new();
15191523

15201524
{
15211525
let entry_fn = tcx.entry_fn(());
15221526

15231527
debug!("collect_roots: entry_fn = {:?}", entry_fn);
15241528

1525-
let mut collector = RootCollector { tcx, strategy: mode, entry_fn, output: &mut roots };
1529+
let mut collector = RootCollector {
1530+
tcx,
1531+
strategy: mode,
1532+
entry_fn,
1533+
used_roots: &mut used_roots,
1534+
mentioned_roots: &mut mentioned_roots,
1535+
};
15261536

15271537
let crate_items = tcx.hir_crate_items(());
15281538

@@ -1537,21 +1547,30 @@ fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionStrategy) -> Vec<MonoI
15371547
collector.push_extra_entry_roots();
15381548
}
15391549

1550+
// Chain the two root lists together. Used items go first, to make it
1551+
// more likely that when we visit a mentioned item, we can stop immediately as it was already used.
15401552
// We can only codegen items that are instantiable - items all of
15411553
// whose predicates hold. Luckily, items that aren't instantiable
15421554
// can't actually be used, so we can just skip codegenning them.
1543-
roots
1555+
used_roots
15441556
.into_iter()
1545-
.filter_map(|Spanned { node: mono_item, .. }| {
1546-
mono_item.is_instantiable(tcx).then_some(mono_item)
1547-
})
1557+
.map(|mono_item| (mono_item.node, CollectionMode::UsedItems))
1558+
.chain(
1559+
mentioned_roots
1560+
.into_iter()
1561+
.map(|mono_item| (mono_item.node, CollectionMode::MentionedItems)),
1562+
)
1563+
.filter(|(mono_item, _mode)| mono_item.is_instantiable(tcx))
15481564
.collect()
15491565
}
15501566

15511567
struct RootCollector<'a, 'tcx> {
15521568
tcx: TyCtxt<'tcx>,
15531569
strategy: MonoItemCollectionStrategy,
1554-
output: &'a mut MonoItems<'tcx>,
1570+
// `MonoItems` includes spans we don't actually want... but this lets us reuse some of the
1571+
// collector's functions.
1572+
used_roots: &'a mut MonoItems<'tcx>,
1573+
mentioned_roots: &'a mut MonoItems<'tcx>,
15551574
entry_fn: Option<(DefId, EntryFnType)>,
15561575
}
15571576

@@ -1565,33 +1584,33 @@ impl<'v> RootCollector<'_, 'v> {
15651584
debug!("RootCollector: ADT drop-glue for `{id:?}`",);
15661585

15671586
let ty = self.tcx.type_of(id.owner_id.to_def_id()).no_bound_vars().unwrap();
1568-
visit_drop_use(self.tcx, ty, true, DUMMY_SP, self.output);
1587+
visit_drop_use(self.tcx, ty, true, DUMMY_SP, self.used_roots);
15691588
}
15701589
}
15711590
DefKind::GlobalAsm => {
15721591
debug!(
15731592
"RootCollector: ItemKind::GlobalAsm({})",
15741593
self.tcx.def_path_str(id.owner_id)
15751594
);
1576-
self.output.push(dummy_spanned(MonoItem::GlobalAsm(id)));
1595+
self.used_roots.push(dummy_spanned(MonoItem::GlobalAsm(id)));
15771596
}
15781597
DefKind::Static { .. } => {
15791598
let def_id = id.owner_id.to_def_id();
15801599
debug!("RootCollector: ItemKind::Static({})", self.tcx.def_path_str(def_id));
1581-
self.output.push(dummy_spanned(MonoItem::Static(def_id)));
1600+
self.used_roots.push(dummy_spanned(MonoItem::Static(def_id)));
15821601
}
15831602
DefKind::Const => {
15841603
// const items only generate mono items if they are
15851604
// actually used somewhere. Just declaring them is insufficient.
15861605

15871606
// but even just declaring them must collect the items they refer to
15881607
if let Ok(val) = self.tcx.const_eval_poly(id.owner_id.to_def_id()) {
1589-
collect_const_value(self.tcx, val, self.output);
1608+
collect_const_value(self.tcx, val, self.used_roots);
15901609
}
15911610
}
15921611
DefKind::Impl { .. } => {
15931612
if self.strategy == MonoItemCollectionStrategy::Eager {
1594-
create_mono_items_for_default_impls(self.tcx, id, self.output);
1613+
create_mono_items_for_default_impls(self.tcx, id, self.used_roots);
15951614
}
15961615
}
15971616
DefKind::Fn => {
@@ -1607,31 +1626,54 @@ impl<'v> RootCollector<'_, 'v> {
16071626
}
16081627
}
16091628

1610-
fn is_root(&self, def_id: LocalDefId) -> bool {
1611-
!self.tcx.generics_of(def_id).requires_monomorphization(self.tcx)
1612-
&& match self.strategy {
1613-
MonoItemCollectionStrategy::Eager => true,
1614-
MonoItemCollectionStrategy::Lazy => {
1615-
self.entry_fn.and_then(|(id, _)| id.as_local()) == Some(def_id)
1616-
|| self.tcx.is_reachable_non_generic(def_id)
1617-
|| self
1618-
.tcx
1619-
.codegen_fn_attrs(def_id)
1620-
.flags
1621-
.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
1622-
}
1629+
/// Determines whether this is an item we start walking, and in which mode. The "real" roots are
1630+
/// walked as "used" items, but that set is optimization-dependent. We add all other non-generic
1631+
/// items as "mentioned" roots. This makes the set of items where `is_root` return `Some`
1632+
/// optimization-independent, which is crucial to ensure that optimized and unoptimized builds
1633+
/// evaluate the same constants.
1634+
fn is_root(&self, def_id: LocalDefId) -> Option<CollectionMode> {
1635+
// Generic things are never roots.
1636+
if self.tcx.generics_of(def_id).requires_monomorphization(self.tcx) {
1637+
return None;
1638+
}
1639+
// We have to skip `must_be_overridden` bodies; asking for their MIR ICEs.
1640+
if self.tcx.intrinsic(def_id).is_some_and(|i| i.must_be_overridden) {
1641+
return None;
1642+
}
1643+
// The rest is definitely a root, but is it used or merely mentioned?
1644+
// Determine whether this item is reachable, which makes it "used".
1645+
let is_used_root = match self.strategy {
1646+
MonoItemCollectionStrategy::Eager => true,
1647+
MonoItemCollectionStrategy::Lazy => {
1648+
self.entry_fn.and_then(|(id, _)| id.as_local()) == Some(def_id)
1649+
|| self.tcx.is_reachable_non_generic(def_id)
1650+
|| self
1651+
.tcx
1652+
.codegen_fn_attrs(def_id)
1653+
.flags
1654+
.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
16231655
}
1656+
};
1657+
if is_used_root {
1658+
Some(CollectionMode::UsedItems)
1659+
} else {
1660+
Some(CollectionMode::MentionedItems)
1661+
}
16241662
}
16251663

16261664
/// If `def_id` represents a root, pushes it onto the list of
16271665
/// outputs. (Note that all roots must be monomorphic.)
16281666
#[instrument(skip(self), level = "debug")]
16291667
fn push_if_root(&mut self, def_id: LocalDefId) {
1630-
if self.is_root(def_id) {
1668+
if let Some(mode) = self.is_root(def_id) {
16311669
debug!("found root");
16321670

16331671
let instance = Instance::mono(self.tcx, def_id.to_def_id());
1634-
self.output.push(create_fn_mono_item(self.tcx, instance, DUMMY_SP));
1672+
let mono_item = create_fn_mono_item(self.tcx, instance, DUMMY_SP);
1673+
match mode {
1674+
CollectionMode::UsedItems => self.used_roots.push(mono_item),
1675+
CollectionMode::MentionedItems => self.mentioned_roots.push(mono_item),
1676+
}
16351677
}
16361678
}
16371679

@@ -1667,7 +1709,7 @@ impl<'v> RootCollector<'_, 'v> {
16671709
self.tcx.mk_args(&[main_ret_ty.into()]),
16681710
);
16691711

1670-
self.output.push(create_fn_mono_item(self.tcx, start_instance, DUMMY_SP));
1712+
self.used_roots.push(create_fn_mono_item(self.tcx, start_instance, DUMMY_SP));
16711713
}
16721714
}
16731715

@@ -1772,15 +1814,15 @@ pub fn collect_crate_mono_items(
17721814
let state: LRef<'_, _> = &mut state;
17731815

17741816
tcx.sess.time("monomorphization_collector_graph_walk", || {
1775-
par_for_each_in(roots, |root| {
1817+
par_for_each_in(roots, |(root, mode)| {
17761818
let mut recursion_depths = DefIdMap::default();
17771819
collect_items_rec(
17781820
tcx,
17791821
dummy_spanned(root),
17801822
state,
17811823
&mut recursion_depths,
17821824
recursion_limit,
1783-
CollectionMode::UsedItems,
1825+
mode,
17841826
);
17851827
});
17861828
});

tests/incremental/callee_caller_cross_crate/b.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
extern crate a;
88

9-
#[rustc_clean(except="typeck", cfg="rpass2")]
9+
#[rustc_clean(except="typeck,optimized_mir", cfg="rpass2")]
1010
pub fn call_function0() {
1111
a::function0(77);
1212
}

tests/incremental/dirty_clean.rs

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ mod y {
3636
//[cfail2]~| ERROR `type_of(y)` should be dirty but is not
3737
//[cfail2]~| ERROR `fn_sig(y)` should be dirty but is not
3838
//[cfail2]~| ERROR `typeck(y)` should be clean but is not
39+
//[cfail2]~| ERROR `optimized_mir(y)` should be clean but is not
3940
x::x();
4041
}
4142
}

tests/incremental/hashes/call_expressions.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ mod change_callee_indirectly_function {
6262
#[cfg(not(any(cfail1,cfail4)))]
6363
use super::callee2 as callee;
6464

65-
#[rustc_clean(except="opt_hir_owner_nodes,typeck", cfg="cfail2")]
65+
#[rustc_clean(except="opt_hir_owner_nodes,typeck,optimized_mir", cfg="cfail2")]
6666
#[rustc_clean(cfg="cfail3")]
67-
#[rustc_clean(except="opt_hir_owner_nodes,typeck", cfg="cfail5")]
67+
#[rustc_clean(except="opt_hir_owner_nodes,typeck,optimized_mir", cfg="cfail5")]
6868
#[rustc_clean(cfg="cfail6")]
6969
pub fn change_callee_indirectly_function() {
7070
callee(1, 2)

tests/incremental/hashes/indexing_expressions.rs

+14-14
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ fn change_simple_index(slice: &[u32]) -> u32 {
2323
}
2424

2525
#[cfg(not(any(cfail1,cfail4)))]
26-
#[rustc_clean(except="opt_hir_owner_nodes", cfg="cfail2")]
26+
#[rustc_clean(except="opt_hir_owner_nodes,optimized_mir", cfg="cfail2")]
2727
#[rustc_clean(cfg="cfail3")]
28-
#[rustc_clean(except="opt_hir_owner_nodes", cfg="cfail5")]
28+
#[rustc_clean(except="opt_hir_owner_nodes,optimized_mir", cfg="cfail5")]
2929
#[rustc_clean(cfg="cfail6")]
3030
fn change_simple_index(slice: &[u32]) -> u32 {
3131
slice[4]
@@ -40,9 +40,9 @@ fn change_lower_bound(slice: &[u32]) -> &[u32] {
4040
}
4141

4242
#[cfg(not(any(cfail1,cfail4)))]
43-
#[rustc_clean(except="opt_hir_owner_nodes", cfg="cfail2")]
43+
#[rustc_clean(except="opt_hir_owner_nodes,optimized_mir", cfg="cfail2")]
4444
#[rustc_clean(cfg="cfail3")]
45-
#[rustc_clean(except="opt_hir_owner_nodes", cfg="cfail5")]
45+
#[rustc_clean(except="opt_hir_owner_nodes,optimized_mir", cfg="cfail5")]
4646
#[rustc_clean(cfg="cfail6")]
4747
fn change_lower_bound(slice: &[u32]) -> &[u32] {
4848
&slice[2..5]
@@ -57,9 +57,9 @@ fn change_upper_bound(slice: &[u32]) -> &[u32] {
5757
}
5858

5959
#[cfg(not(any(cfail1,cfail4)))]
60-
#[rustc_clean(except="opt_hir_owner_nodes", cfg="cfail2")]
60+
#[rustc_clean(except="opt_hir_owner_nodes,optimized_mir", cfg="cfail2")]
6161
#[rustc_clean(cfg="cfail3")]
62-
#[rustc_clean(except="opt_hir_owner_nodes", cfg="cfail5")]
62+
#[rustc_clean(except="opt_hir_owner_nodes,optimized_mir", cfg="cfail5")]
6363
#[rustc_clean(cfg="cfail6")]
6464
fn change_upper_bound(slice: &[u32]) -> &[u32] {
6565
&slice[3..7]
@@ -74,9 +74,9 @@ fn add_lower_bound(slice: &[u32]) -> &[u32] {
7474
}
7575

7676
#[cfg(not(any(cfail1,cfail4)))]
77-
#[rustc_clean(except="opt_hir_owner_nodes,typeck", cfg="cfail2")]
77+
#[rustc_clean(except="opt_hir_owner_nodes,typeck,optimized_mir", cfg="cfail2")]
7878
#[rustc_clean(cfg="cfail3")]
79-
#[rustc_clean(except="opt_hir_owner_nodes,typeck", cfg="cfail5")]
79+
#[rustc_clean(except="opt_hir_owner_nodes,typeck,optimized_mir", cfg="cfail5")]
8080
#[rustc_clean(cfg="cfail6")]
8181
fn add_lower_bound(slice: &[u32]) -> &[u32] {
8282
&slice[3..4]
@@ -91,9 +91,9 @@ fn add_upper_bound(slice: &[u32]) -> &[u32] {
9191
}
9292

9393
#[cfg(not(any(cfail1,cfail4)))]
94-
#[rustc_clean(except="opt_hir_owner_nodes,typeck", cfg="cfail2")]
94+
#[rustc_clean(except="opt_hir_owner_nodes,typeck,optimized_mir", cfg="cfail2")]
9595
#[rustc_clean(cfg="cfail3")]
96-
#[rustc_clean(except="opt_hir_owner_nodes,typeck", cfg="cfail5")]
96+
#[rustc_clean(except="opt_hir_owner_nodes,typeck,optimized_mir", cfg="cfail5")]
9797
#[rustc_clean(cfg="cfail6")]
9898
fn add_upper_bound(slice: &[u32]) -> &[u32] {
9999
&slice[3..7]
@@ -108,9 +108,9 @@ fn change_mutability(slice: &mut [u32]) -> u32 {
108108
}
109109

110110
#[cfg(not(any(cfail1,cfail4)))]
111-
#[rustc_clean(except="opt_hir_owner_nodes,typeck", cfg="cfail2")]
111+
#[rustc_clean(except="opt_hir_owner_nodes,typeck,optimized_mir", cfg="cfail2")]
112112
#[rustc_clean(cfg="cfail3")]
113-
#[rustc_clean(except="opt_hir_owner_nodes,typeck", cfg="cfail5")]
113+
#[rustc_clean(except="opt_hir_owner_nodes,typeck,optimized_mir", cfg="cfail5")]
114114
#[rustc_clean(cfg="cfail6")]
115115
fn change_mutability(slice: &mut [u32]) -> u32 {
116116
(& slice[3..5])[0]
@@ -125,9 +125,9 @@ fn exclusive_to_inclusive_range(slice: &[u32]) -> &[u32] {
125125
}
126126

127127
#[cfg(not(any(cfail1,cfail4)))]
128-
#[rustc_clean(except="opt_hir_owner_nodes,typeck", cfg="cfail2")]
128+
#[rustc_clean(except="opt_hir_owner_nodes,typeck,optimized_mir", cfg="cfail2")]
129129
#[rustc_clean(cfg="cfail3")]
130-
#[rustc_clean(except="opt_hir_owner_nodes,typeck", cfg="cfail5")]
130+
#[rustc_clean(except="opt_hir_owner_nodes,typeck,optimized_mir", cfg="cfail5")]
131131
#[rustc_clean(cfg="cfail6")]
132132
fn exclusive_to_inclusive_range(slice: &[u32]) -> &[u32] {
133133
&slice[3..=7]

tests/incremental/ich_method_call_trait_scope.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ mod mod3 {
2626
#[cfg(rpass2)]
2727
use Trait2;
2828

29-
#[rustc_clean(except="typeck", cfg="rpass2")]
29+
#[rustc_clean(except="typeck,optimized_mir", cfg="rpass2")]
3030
fn bar() {
3131
().method();
3232
}

tests/incremental/ich_resolve_results.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ mod mod3 {
3636
}
3737

3838
#[rustc_clean(cfg="rpass2")]
39-
#[rustc_clean(except="opt_hir_owner_nodes,typeck", cfg="rpass3")]
39+
#[rustc_clean(except="opt_hir_owner_nodes,typeck,optimized_mir", cfg="rpass3")]
4040
fn in_type() {
4141
test::<Foo>();
4242
}

tests/incremental/struct_add_field.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ pub struct Y {
2121
pub y: char
2222
}
2323

24-
#[rustc_clean(except="fn_sig,typeck", cfg="rpass2")]
24+
#[rustc_clean(except="fn_sig,typeck,optimized_mir", cfg="rpass2")]
2525
pub fn use_X(x: X) -> u32 {
2626
x.x as u32
2727
}
2828

29-
#[rustc_clean(except="typeck", cfg="rpass2")]
29+
#[rustc_clean(except="typeck,optimized_mir", cfg="rpass2")]
3030
pub fn use_EmbedX(embed: EmbedX) -> u32 {
3131
embed.x.x as u32
3232
}

tests/incremental/struct_change_field_type.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ pub struct Y {
2424
pub y: char
2525
}
2626

27-
#[rustc_clean(except="typeck", cfg="rpass2")]
27+
#[rustc_clean(except="typeck,optimized_mir", cfg="rpass2")]
2828
pub fn use_X() -> u32 {
2929
let x: X = X { x: 22 };
3030
x.x as u32
3131
}
3232

33-
#[rustc_clean(except="typeck", cfg="rpass2")]
33+
#[rustc_clean(except="typeck,optimized_mir", cfg="rpass2")]
3434
pub fn use_EmbedX(x: EmbedX) -> u32 {
3535
let x: X = X { x: 22 };
3636
x.x as u32

tests/incremental/struct_change_field_type_cross_crate/b.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ extern crate a;
88

99
use a::*;
1010

11-
#[rustc_clean(except="typeck", cfg="rpass2")]
11+
#[rustc_clean(except="typeck,optimized_mir", cfg="rpass2")]
1212
pub fn use_X() -> u32 {
1313
let x: X = X { x: 22 };
1414
x.x as u32
1515
}
1616

17-
#[rustc_clean(except="typeck", cfg="rpass2")]
17+
#[rustc_clean(except="typeck,optimized_mir", cfg="rpass2")]
1818
pub fn use_EmbedX(embed: EmbedX) -> u32 {
1919
embed.x.x as u32
2020
}

0 commit comments

Comments
 (0)