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 4cee6b3

Browse files
committedMar 20, 2024
collector: always consider all monomorphic functions to be 'mentioned'
1 parent 0cb1065 commit 4cee6b3

8 files changed

+229
-31
lines changed
 

Diff for: ‎compiler/rustc_monomorphize/src/collector.rs

+77-31
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,9 @@ fn collect_items_rec<'tcx>(
429429
}
430430
MonoItem::Fn(instance) => {
431431
// Sanity check whether this ended up being collected accidentally
432-
debug_assert!(should_codegen_locally(tcx, &instance));
432+
if mode == CollectionMode::UsedItems {
433+
debug_assert!(should_codegen_locally(tcx, &instance));
434+
}
433435

434436
// Keep track of the monomorphization recursion depth
435437
recursion_depth_reset = Some(check_recursion_limit(
@@ -1518,16 +1520,26 @@ fn collect_const_value<'tcx>(
15181520
// Find all non-generic items by walking the HIR. These items serve as roots to
15191521
// start monomorphizing from.
15201522
#[instrument(skip(tcx, mode), level = "debug")]
1521-
fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionStrategy) -> Vec<MonoItem<'_>> {
1523+
fn collect_roots(
1524+
tcx: TyCtxt<'_>,
1525+
mode: MonoItemCollectionStrategy,
1526+
) -> Vec<(MonoItem<'_>, CollectionMode)> {
15221527
debug!("collecting roots");
1523-
let mut roots = Vec::new();
1528+
let mut used_roots = MonoItems::new();
1529+
let mut mentioned_roots = MonoItems::new();
15241530

15251531
{
15261532
let entry_fn = tcx.entry_fn(());
15271533

15281534
debug!("collect_roots: entry_fn = {:?}", entry_fn);
15291535

1530-
let mut collector = RootCollector { tcx, strategy: mode, entry_fn, output: &mut roots };
1536+
let mut collector = RootCollector {
1537+
tcx,
1538+
strategy: mode,
1539+
entry_fn,
1540+
used_roots: &mut used_roots,
1541+
mentioned_roots: &mut mentioned_roots,
1542+
};
15311543

15321544
let crate_items = tcx.hir_crate_items(());
15331545

@@ -1542,21 +1554,32 @@ fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionStrategy) -> Vec<MonoI
15421554
collector.push_extra_entry_roots();
15431555
}
15441556

1557+
// Chain the two root lists together. Used items go first, to make it
1558+
// more likely that when we visit a mentioned item, we can stop immediately as it was already used.
15451559
// We can only codegen items that are instantiable - items all of
15461560
// whose predicates hold. Luckily, items that aren't instantiable
15471561
// can't actually be used, so we can just skip codegenning them.
1548-
roots
1562+
used_roots
15491563
.into_iter()
1550-
.filter_map(|Spanned { node: mono_item, .. }| {
1551-
mono_item.is_instantiable(tcx).then_some(mono_item)
1564+
.map(|mono_item| (mono_item, CollectionMode::UsedItems))
1565+
.chain(
1566+
mentioned_roots
1567+
.into_iter()
1568+
.map(|mono_item| (mono_item, CollectionMode::MentionedItems)),
1569+
)
1570+
.filter_map(|(Spanned { node: mono_item, .. }, mode)| {
1571+
mono_item.is_instantiable(tcx).then_some((mono_item, mode))
15521572
})
15531573
.collect()
15541574
}
15551575

15561576
struct RootCollector<'a, 'tcx> {
15571577
tcx: TyCtxt<'tcx>,
15581578
strategy: MonoItemCollectionStrategy,
1559-
output: &'a mut MonoItems<'tcx>,
1579+
// `MonoItems` includes spans we don't actually want... but this lets us reuse some of the
1580+
// collector's functions.
1581+
used_roots: &'a mut MonoItems<'tcx>,
1582+
mentioned_roots: &'a mut MonoItems<'tcx>,
15601583
entry_fn: Option<(DefId, EntryFnType)>,
15611584
}
15621585

@@ -1570,33 +1593,33 @@ impl<'v> RootCollector<'_, 'v> {
15701593
debug!("RootCollector: ADT drop-glue for `{id:?}`",);
15711594

15721595
let ty = self.tcx.type_of(id.owner_id.to_def_id()).no_bound_vars().unwrap();
1573-
visit_drop_use(self.tcx, ty, true, DUMMY_SP, self.output);
1596+
visit_drop_use(self.tcx, ty, true, DUMMY_SP, self.used_roots);
15741597
}
15751598
}
15761599
DefKind::GlobalAsm => {
15771600
debug!(
15781601
"RootCollector: ItemKind::GlobalAsm({})",
15791602
self.tcx.def_path_str(id.owner_id)
15801603
);
1581-
self.output.push(dummy_spanned(MonoItem::GlobalAsm(id)));
1604+
self.used_roots.push(dummy_spanned(MonoItem::GlobalAsm(id)));
15821605
}
15831606
DefKind::Static { .. } => {
15841607
let def_id = id.owner_id.to_def_id();
15851608
debug!("RootCollector: ItemKind::Static({})", self.tcx.def_path_str(def_id));
1586-
self.output.push(dummy_spanned(MonoItem::Static(def_id)));
1609+
self.used_roots.push(dummy_spanned(MonoItem::Static(def_id)));
15871610
}
15881611
DefKind::Const => {
15891612
// const items only generate mono items if they are
15901613
// actually used somewhere. Just declaring them is insufficient.
15911614

15921615
// but even just declaring them must collect the items they refer to
15931616
if let Ok(val) = self.tcx.const_eval_poly(id.owner_id.to_def_id()) {
1594-
collect_const_value(self.tcx, val, self.output);
1617+
collect_const_value(self.tcx, val, self.used_roots);
15951618
}
15961619
}
15971620
DefKind::Impl { .. } => {
15981621
if self.strategy == MonoItemCollectionStrategy::Eager {
1599-
create_mono_items_for_default_impls(self.tcx, id, self.output);
1622+
create_mono_items_for_default_impls(self.tcx, id, self.used_roots);
16001623
}
16011624
}
16021625
DefKind::Fn => {
@@ -1612,31 +1635,54 @@ impl<'v> RootCollector<'_, 'v> {
16121635
}
16131636
}
16141637

1615-
fn is_root(&self, def_id: LocalDefId) -> bool {
1616-
!self.tcx.generics_of(def_id).requires_monomorphization(self.tcx)
1617-
&& match self.strategy {
1618-
MonoItemCollectionStrategy::Eager => true,
1619-
MonoItemCollectionStrategy::Lazy => {
1620-
self.entry_fn.and_then(|(id, _)| id.as_local()) == Some(def_id)
1621-
|| self.tcx.is_reachable_non_generic(def_id)
1622-
|| self
1623-
.tcx
1624-
.codegen_fn_attrs(def_id)
1625-
.flags
1626-
.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
1627-
}
1638+
/// Determines whether this is an item we start walking, and in which mode. The "real" roots are
1639+
/// walked as "used" items, but that set is optimization-dependent. We add all other non-generic
1640+
/// items as "mentioned" roots. This makes the set of items where `is_root` return `Some`
1641+
/// optimization-independent, which is crucial to ensure that optimized and unoptimized builds
1642+
/// evaluate the same constants.
1643+
fn is_root(&self, def_id: LocalDefId) -> Option<CollectionMode> {
1644+
// Generic things are never roots.
1645+
if self.tcx.generics_of(def_id).requires_monomorphization(self.tcx) {
1646+
return None;
1647+
}
1648+
// Determine whether this item is reachable, which makes it "used".
1649+
let is_used_root = match self.strategy {
1650+
MonoItemCollectionStrategy::Eager => true,
1651+
MonoItemCollectionStrategy::Lazy => {
1652+
self.entry_fn.and_then(|(id, _)| id.as_local()) == Some(def_id)
1653+
|| self.tcx.is_reachable_non_generic(def_id)
1654+
|| self
1655+
.tcx
1656+
.codegen_fn_attrs(def_id)
1657+
.flags
1658+
.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
16281659
}
1660+
};
1661+
if is_used_root {
1662+
return Some(CollectionMode::UsedItems);
1663+
}
1664+
// We have to skip `must_be_overridden` bodies; asking for their "mentioned" items
1665+
// leads to ICEs.
1666+
if self.tcx.intrinsic(def_id).is_some_and(|i| i.must_be_overridden) {
1667+
return None;
1668+
}
1669+
// Everything else is considered "mentioned"
1670+
Some(CollectionMode::MentionedItems)
16291671
}
16301672

16311673
/// If `def_id` represents a root, pushes it onto the list of
16321674
/// outputs. (Note that all roots must be monomorphic.)
16331675
#[instrument(skip(self), level = "debug")]
16341676
fn push_if_root(&mut self, def_id: LocalDefId) {
1635-
if self.is_root(def_id) {
1677+
if let Some(mode) = self.is_root(def_id) {
16361678
debug!("found root");
16371679

16381680
let instance = Instance::mono(self.tcx, def_id.to_def_id());
1639-
self.output.push(create_fn_mono_item(self.tcx, instance, DUMMY_SP));
1681+
let mono_item = create_fn_mono_item(self.tcx, instance, DUMMY_SP);
1682+
match mode {
1683+
CollectionMode::UsedItems => self.used_roots.push(mono_item),
1684+
CollectionMode::MentionedItems => self.mentioned_roots.push(mono_item),
1685+
}
16401686
}
16411687
}
16421688

@@ -1672,7 +1718,7 @@ impl<'v> RootCollector<'_, 'v> {
16721718
self.tcx.mk_args(&[main_ret_ty.into()]),
16731719
);
16741720

1675-
self.output.push(create_fn_mono_item(self.tcx, start_instance, DUMMY_SP));
1721+
self.used_roots.push(create_fn_mono_item(self.tcx, start_instance, DUMMY_SP));
16761722
}
16771723
}
16781724

@@ -1777,15 +1823,15 @@ pub fn collect_crate_mono_items(
17771823
let state: LRef<'_, _> = &mut state;
17781824

17791825
tcx.sess.time("monomorphization_collector_graph_walk", || {
1780-
par_for_each_in(roots, |root| {
1826+
par_for_each_in(roots, |(root, mode)| {
17811827
let mut recursion_depths = DefIdMap::default();
17821828
collect_items_rec(
17831829
tcx,
17841830
dummy_spanned(root),
17851831
state,
17861832
&mut recursion_depths,
17871833
recursion_limit,
1788-
CollectionMode::UsedItems,
1834+
mode,
17891835
);
17901836
});
17911837
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error[E0080]: evaluation of `Fail::<i32>::C` failed
2+
--> $DIR/collect-roots-dead-fn.rs:12:19
3+
|
4+
LL | const C: () = panic!();
5+
| ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-roots-dead-fn.rs:12:19
6+
|
7+
= note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
8+
9+
note: erroneous constant encountered
10+
--> $DIR/collect-roots-dead-fn.rs:25:5
11+
|
12+
LL | Fail::<T>::C;
13+
| ^^^^^^^^^^^^
14+
15+
note: the above error was encountered while instantiating `fn h::<i32>`
16+
--> $DIR/collect-roots-dead-fn.rs:21:5
17+
|
18+
LL | h::<i32>()
19+
| ^^^^^^^^^^
20+
21+
error: aborting due to 1 previous error
22+
23+
For more information about this error, try `rustc --explain E0080`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0080]: evaluation of `Fail::<i32>::C` failed
2+
--> $DIR/collect-roots-dead-fn.rs:12:19
3+
|
4+
LL | const C: () = panic!();
5+
| ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-roots-dead-fn.rs:12:19
6+
|
7+
= note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
8+
9+
note: erroneous constant encountered
10+
--> $DIR/collect-roots-dead-fn.rs:25:5
11+
|
12+
LL | Fail::<T>::C;
13+
| ^^^^^^^^^^^^
14+
15+
error: aborting due to 1 previous error
16+
17+
For more information about this error, try `rustc --explain E0080`.
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//@revisions: noopt opt
2+
//@ build-fail
3+
//@[noopt] compile-flags: -Copt-level=0
4+
//@[opt] compile-flags: -O
5+
6+
//! This used to fail in optimized builds but somehow unoptimized builds
7+
//! never collected `g` so the const-eval error never happened.
8+
#![crate_type = "lib"]
9+
10+
struct Fail<T>(T);
11+
impl<T> Fail<T> {
12+
const C: () = panic!(); //~ERROR: evaluation of `Fail::<i32>::C` failed
13+
}
14+
15+
pub fn f() {
16+
loop {}; g()
17+
}
18+
19+
#[inline(never)]
20+
fn g() {
21+
h::<i32>()
22+
}
23+
24+
fn h<T>() {
25+
Fail::<T>::C;
26+
}
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//@revisions: noopt opt
2+
//@ build-pass
3+
//@[noopt] compile-flags: -Copt-level=0
4+
//@[opt] compile-flags: -O
5+
6+
//! A slight variant of `collect-roots-in-dead-fn` where the dead call is itself generic.
7+
//! Now this *passes* in both optimized and unoptimized builds.
8+
//! (Curcially, behavior is still the same in both cases.)
9+
#![crate_type = "lib"]
10+
11+
struct Fail<T>(T);
12+
impl<T> Fail<T> {
13+
const C: () = panic!();
14+
}
15+
16+
pub fn f() {
17+
loop {}; h::<i32>()
18+
}
19+
20+
#[inline(never)]
21+
fn h<T>() {
22+
Fail::<T>::C;
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error[E0080]: evaluation of `Zst::<u32>::ASSERT` failed
2+
--> $DIR/collect-roots-inline-fn.rs:13:9
3+
|
4+
LL | panic!();
5+
| ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-roots-inline-fn.rs:13:9
6+
|
7+
= note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
8+
9+
note: erroneous constant encountered
10+
--> $DIR/collect-roots-inline-fn.rs:18:5
11+
|
12+
LL | Zst::<T>::ASSERT;
13+
| ^^^^^^^^^^^^^^^^
14+
15+
note: the above error was encountered while instantiating `fn f::<u32>`
16+
--> $DIR/collect-roots-inline-fn.rs:22:5
17+
|
18+
LL | f::<u32>()
19+
| ^^^^^^^^^^
20+
21+
error: aborting due to 1 previous error
22+
23+
For more information about this error, try `rustc --explain E0080`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0080]: evaluation of `Zst::<u32>::ASSERT` failed
2+
--> $DIR/collect-roots-inline-fn.rs:13:9
3+
|
4+
LL | panic!();
5+
| ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-roots-inline-fn.rs:13:9
6+
|
7+
= note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
8+
9+
note: erroneous constant encountered
10+
--> $DIR/collect-roots-inline-fn.rs:18:5
11+
|
12+
LL | Zst::<T>::ASSERT;
13+
| ^^^^^^^^^^^^^^^^
14+
15+
error: aborting due to 1 previous error
16+
17+
For more information about this error, try `rustc --explain E0080`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//@revisions: noopt opt
2+
//@ build-fail
3+
//@[noopt] compile-flags: -Copt-level=0
4+
//@[opt] compile-flags: -O
5+
6+
//! In optimized builds, the functions in this crate are all marked "inline" so none of them become
7+
//! collector roots. Ensure that we still evaluate the constants.
8+
#![crate_type = "lib"]
9+
10+
struct Zst<T>(T);
11+
impl<T> Zst<T> {
12+
const ASSERT: () = if std::mem::size_of::<T>() != 0 {
13+
panic!(); //~ERROR: evaluation of `Zst::<u32>::ASSERT` failed
14+
};
15+
}
16+
17+
fn f<T>() {
18+
Zst::<T>::ASSERT;
19+
}
20+
21+
pub fn g() {
22+
f::<u32>()
23+
}

0 commit comments

Comments
 (0)
Please sign in to comment.