Skip to content

Commit 55a1404

Browse files
committed
Auto merge of rust-lang#122568 - RalfJung:mentioned-items, r=<try>
Draft: recursively evaluate the constants in everything that is 'mentioned' This is another attempt at fixing rust-lang#107503. The previous attempt at rust-lang#112879 seems stuck in figuring out where the [perf regression](https://perf.rust-lang.org/compare.html?start=c55d1ee8d4e3162187214692229a63c2cc5e0f31&end=ec8de1ebe0d698b109beeaaac83e60f4ef8bb7d1&stat=instructions:u) comes from. In rust-lang#122258 I learned some things, which informed the approach this PR is taking. r? `@ghost`
2 parents 72d7897 + 80f6186 commit 55a1404

17 files changed

+340
-54
lines changed

compiler/rustc_data_structures/src/sync.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
//! | ----------------------- | ------------------- | ------------------------------- |
2121
//! | `Lrc<T>` | `rc::Rc<T>` | `sync::Arc<T>` |
2222
//! |` Weak<T>` | `rc::Weak<T>` | `sync::Weak<T>` |
23+
//! | `LRef<'a, T>` [^2] | `&'a mut T` | `&'a T` |
2324
//! | | | |
2425
//! | `AtomicBool` | `Cell<bool>` | `atomic::AtomicBool` |
2526
//! | `AtomicU32` | `Cell<u32>` | `atomic::AtomicU32` |
@@ -38,7 +39,7 @@
3839
//! of a `RefCell`. This is appropriate when interior mutability is not
3940
//! required.
4041
//!
41-
//! [^2] `MTLockRef` is a typedef.
42+
//! [^2] `MTRef`, `MTLockRef` are type aliases.
4243
4344
pub use crate::marker::*;
4445
use std::collections::HashMap;
@@ -208,7 +209,7 @@ cfg_match! {
208209

209210
use std::cell::RefCell as InnerRwLock;
210211

211-
pub type MTLockRef<'a, T> = &'a mut MTLock<T>;
212+
pub type LRef<'a, T> = &'a mut T;
212213

213214
#[derive(Debug, Default)]
214215
pub struct MTLock<T>(T);
@@ -274,7 +275,7 @@ cfg_match! {
274275
pub use std::sync::Arc as Lrc;
275276
pub use std::sync::Weak as Weak;
276277

277-
pub type MTLockRef<'a, T> = &'a MTLock<T>;
278+
pub type LRef<'a, T> = &'a T;
278279

279280
#[derive(Debug, Default)]
280281
pub struct MTLock<T>(Lock<T>);
@@ -314,6 +315,8 @@ cfg_match! {
314315
}
315316
}
316317

318+
pub type MTLockRef<'a, T> = LRef<'a, MTLock<T>>;
319+
317320
#[derive(Default)]
318321
#[cfg_attr(parallel_compiler, repr(align(64)))]
319322
pub struct CacheAligned<T>(pub T);

compiler/rustc_middle/src/mir/mod.rs

+23
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
2020
use rustc_hir::{self, CoroutineDesugaring, CoroutineKind, ImplicitSelfKind};
2121
use rustc_hir::{self as hir, HirId};
2222
use rustc_session::Session;
23+
use rustc_span::source_map::Spanned;
2324
use rustc_target::abi::{FieldIdx, VariantIdx};
2425

2526
use polonius_engine::Atom;
@@ -312,6 +313,16 @@ impl<'tcx> CoroutineInfo<'tcx> {
312313
}
313314
}
314315

316+
/// Some item that needs to monomorphize successfully for a MIR body to be considered well-formed.
317+
#[derive(Copy, Clone, PartialEq, Debug, HashStable, TyEncodable, TyDecodable)]
318+
#[derive(TypeFoldable, TypeVisitable)]
319+
pub enum MentionedItem<'tcx> {
320+
Fn(DefId, GenericArgsRef<'tcx>),
321+
Drop(Ty<'tcx>),
322+
// FIXME: add Vtable { source_ty: Ty<'tcx>, target_ty: Ty<'tcx> },
323+
// FIXME: do we have to add closures?
324+
}
325+
315326
/// The lowered representation of a single function.
316327
#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable, TypeVisitable)]
317328
pub struct Body<'tcx> {
@@ -375,8 +386,18 @@ pub struct Body<'tcx> {
375386

376387
/// Constants that are required to evaluate successfully for this MIR to be well-formed.
377388
/// We hold in this field all the constants we are not able to evaluate yet.
389+
///
390+
/// This is soundness-critical, we make a guarantee that all consts syntactically mentioned in a
391+
/// function have successfully evaluated if the function ever gets xecuted at runtime.
378392
pub required_consts: Vec<ConstOperand<'tcx>>,
379393

394+
/// Further items that were mentioned in this function and hence *may* become monomorphized,
395+
/// depending on optimizations. We use this to ensure their consts evaluate successfully even if
396+
/// they do not get monomorphized, to avoid optimization-dependent compile errors.
397+
///
398+
/// This is *not* soundness-critical and the contents of this list are *not* a stable guarantee.
399+
pub mentioned_items: Vec<Spanned<MentionedItem<'tcx>>>,
400+
380401
/// Does this body use generic parameters. This is used for the `ConstEvaluatable` check.
381402
///
382403
/// Note that this does not actually mean that this body is not computable right now.
@@ -453,6 +474,7 @@ impl<'tcx> Body<'tcx> {
453474
var_debug_info,
454475
span,
455476
required_consts: Vec::new(),
477+
mentioned_items: Vec::new(),
456478
is_polymorphic: false,
457479
injection_phase: None,
458480
tainted_by_errors,
@@ -482,6 +504,7 @@ impl<'tcx> Body<'tcx> {
482504
spread_arg: None,
483505
span: DUMMY_SP,
484506
required_consts: Vec::new(),
507+
mentioned_items: Vec::new(),
485508
var_debug_info: Vec::new(),
486509
is_polymorphic: false,
487510
injection_phase: None,

compiler/rustc_mir_build/src/build/custom/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ pub(super) fn build_custom_mir<'tcx>(
5656
var_debug_info: Vec::new(),
5757
span,
5858
required_consts: Vec::new(),
59+
mentioned_items: Vec::new(),
5960
is_polymorphic: false,
6061
tainted_by_errors: None,
6162
injection_phase: None,

compiler/rustc_mir_transform/src/inline.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,8 @@ impl<'tcx> Inliner<'tcx> {
565565
mut callee_body: Body<'tcx>,
566566
) {
567567
let terminator = caller_body[callsite.block].terminator.take().unwrap();
568-
let TerminatorKind::Call { args, destination, unwind, target, .. } = terminator.kind else {
568+
let TerminatorKind::Call { func, args, destination, unwind, target, .. } = terminator.kind
569+
else {
569570
bug!("unexpected terminator kind {:?}", terminator.kind);
570571
};
571572

@@ -717,6 +718,26 @@ impl<'tcx> Inliner<'tcx> {
717718
Const::Val(..) | Const::Unevaluated(..) => true,
718719
},
719720
));
721+
// Now that we incorporated the callee's `required_consts`, we can remove the callee from
722+
// `mentioned_items` -- but we have to take their `mentioned_items` in return.
723+
// This does some extra work here to save the monomorphization collector work later.
724+
// FIXME: benchmark which option is better.
725+
let callee_item = {
726+
// We need to reconstruct the `required_item` for the callee so that we can find and
727+
// remove it.
728+
let func_ty = func.ty(caller_body, self.tcx);
729+
match func_ty.kind() {
730+
ty::FnDef(def_id, args) => MentionedItem::Fn(*def_id, args),
731+
_ => bug!(),
732+
}
733+
};
734+
if let Some(idx) =
735+
caller_body.mentioned_items.iter().position(|item| item.node == callee_item)
736+
{
737+
// We found the callee, so remove it and add its items instead.
738+
caller_body.mentioned_items.remove(idx);
739+
caller_body.mentioned_items.extend(callee_body.mentioned_items);
740+
}
720741
}
721742

722743
fn make_call_args(

compiler/rustc_mir_transform/src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ mod lint;
8989
mod lower_intrinsics;
9090
mod lower_slice_len;
9191
mod match_branches;
92+
mod mentioned_items;
9293
mod multiple_return_terminators;
9394
mod normalize_array_len;
9495
mod nrvo;
@@ -566,6 +567,10 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
566567
tcx,
567568
body,
568569
&[
570+
// Before doing anything, remember which items are being mentioned so that the set of items
571+
// visited does not depend on the optimization level.
572+
&mentioned_items::MentionedItems,
573+
// Add some UB checks before any UB gets optimized away.
569574
&check_alignment::CheckAlignment,
570575
// Before inlining: trim down MIR with passes to reduce inlining work.
571576

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use rustc_middle::mir::visit::Visitor;
2+
use rustc_middle::mir::{self, ConstOperand, Location, MentionedItem, MirPass};
3+
use rustc_middle::ty::{self, TyCtxt};
4+
use rustc_session::Session;
5+
use rustc_span::source_map::Spanned;
6+
7+
pub struct MentionedItems;
8+
9+
struct MentionedItemsVisitor<'a, 'tcx> {
10+
tcx: TyCtxt<'tcx>,
11+
body: &'a mir::Body<'tcx>,
12+
mentioned_items: &'a mut Vec<Spanned<MentionedItem<'tcx>>>,
13+
}
14+
15+
impl<'tcx> MirPass<'tcx> for MentionedItems {
16+
fn is_enabled(&self, _sess: &Session) -> bool {
17+
// We need to do always do that as the collector will only check the 'mentioned' items of
18+
// 'mentioned' items, so if some item X gets compiled without optimizations but later called
19+
// from somewhere that has optimizations enabled, we need the `mentioned_items` of X to be
20+
// filled.
21+
true
22+
}
23+
24+
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) {
25+
debug_assert!(body.mentioned_items.is_empty());
26+
let mut mentioned_items = Vec::new();
27+
MentionedItemsVisitor { tcx, body, mentioned_items: &mut mentioned_items }.visit_body(body);
28+
body.mentioned_items = mentioned_items;
29+
}
30+
}
31+
32+
impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> {
33+
fn visit_constant(&mut self, constant: &ConstOperand<'tcx>, _: Location) {
34+
let const_ = constant.const_;
35+
// This is how function items get referenced: via constants of `FnDef` type.
36+
if let ty::FnDef(def_id, args) = const_.ty().kind() {
37+
debug!("adding to required_items: {def_id:?}");
38+
self.mentioned_items
39+
.push(Spanned { node: MentionedItem::Fn(*def_id, args), span: constant.span });
40+
}
41+
}
42+
43+
fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
44+
self.super_terminator(terminator, location);
45+
match terminator.kind {
46+
// We don't need to handle `Call` as we already handled all function type operands in
47+
// `visit_constant`. But we do need to handle `Drop`.
48+
mir::TerminatorKind::Drop { place, .. } => {
49+
let ty = place.ty(self.body, self.tcx).ty;
50+
let span = self.body.source_info(location).span;
51+
self.mentioned_items.push(Spanned { node: MentionedItem::Drop(ty), span });
52+
}
53+
_ => {}
54+
}
55+
}
56+
}

compiler/rustc_mir_transform/src/shim.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use std::iter;
1717

1818
use crate::{
1919
abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator,
20-
pass_manager as pm, remove_noop_landing_pads, simplify,
20+
mentioned_items, pass_manager as pm, remove_noop_landing_pads, simplify,
2121
};
2222
use rustc_middle::mir::patch::MirPatch;
2323
use rustc_mir_dataflow::elaborate_drops::{self, DropElaborator, DropFlagMode, DropStyle};
@@ -147,6 +147,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
147147
tcx,
148148
&mut body,
149149
&[
150+
&mentioned_items::MentionedItems,
150151
&abort_unwinding_calls::AbortUnwindingCalls,
151152
&add_call_guards::CriticalCallEdges,
152153
],
@@ -178,6 +179,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
178179
tcx,
179180
&mut result,
180181
&[
182+
&mentioned_items::MentionedItems,
181183
&add_moves_for_packed_drops::AddMovesForPackedDrops,
182184
&deref_separator::Derefer,
183185
&remove_noop_landing_pads::RemoveNoopLandingPads,

0 commit comments

Comments
 (0)