Skip to content

Commit df8ac8f

Browse files
committedMar 21, 2024
Auto merge of #122568 - RalfJung:mentioned-items, r=oli-obk
recursively evaluate the constants in everything that is 'mentioned' This is another attempt at fixing #107503. The previous attempt at #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 #122258 I learned some things, which informed the approach this PR is taking. Quoting from the new collector docs, which explain the high-level idea: ```rust //! One important role of collection is to evaluate all constants that are used by all the items //! which are being collected. Codegen can then rely on only encountering constants that evaluate //! successfully, and if a constant fails to evaluate, the collector has much better context to be //! able to show where this constant comes up. //! //! However, the exact set of "used" items (collected as described above), and therefore the exact //! set of used constants, can depend on optimizations. Optimizing away dead code may optimize away //! a function call that uses a failing constant, so an unoptimized build may fail where an //! optimized build succeeds. This is undesirable. //! //! To fix this, the collector has the concept of "mentioned" items. Some time during the MIR //! pipeline, before any optimization-level-dependent optimizations, we compute a list of all items //! that syntactically appear in the code. These are considered "mentioned", and even if they are in //! dead code and get optimized away (which makes them no longer "used"), they are still //! "mentioned". For every used item, the collector ensures that all mentioned items, recursively, //! do not use a failing constant. This is reflected via the [`CollectionMode`], which determines //! whether we are visiting a used item or merely a mentioned item. //! //! The collector and "mentioned items" gathering (which lives in `rustc_mir_transform::mentioned_items`) //! need to stay in sync in the following sense: //! //! - For every item that the collector gather that could eventually lead to build failure (most //! likely due to containing a constant that fails to evaluate), a corresponding mentioned item //! must be added. This should use the exact same strategy as the ecollector to make sure they are //! in sync. However, while the collector works on monomorphized types, mentioned items are //! collected on generic MIR -- so any time the collector checks for a particular type (such as //! `ty::FnDef`), we have to just onconditionally add this as a mentioned item. //! - In `visit_mentioned_item`, we then do with that mentioned item exactly what the collector //! would have done during regular MIR visiting. Basically you can think of the collector having //! two stages, a pre-monomorphization stage and a post-monomorphization stage (usually quite //! literally separated by a call to `self.monomorphize`); the pre-monomorphizationn stage is //! duplicated in mentioned items gathering and the post-monomorphization stage is duplicated in //! `visit_mentioned_item`. //! - Finally, as a performance optimization, the collector should fill `used_mentioned_item` during //! its MIR traversal with exactly what mentioned item gathering would have added in the same //! situation. This detects mentioned items that have *not* been optimized away and hence don't //! need a dedicated traversal. enum CollectionMode { /// Collect items that are used, i.e., actually needed for codegen. /// /// Which items are used can depend on optimization levels, as MIR optimizations can remove /// uses. UsedItems, /// Collect items that are mentioned. The goal of this mode is that it is independent of /// optimizations: the set of "mentioned" items is computed before optimizations are run. /// /// The exact contents of this set are *not* a stable guarantee. (For instance, it is currently /// computed after drop-elaboration. If we ever do some optimizations even in debug builds, we /// might decide to run them before computing mentioned items.) The key property of this set is /// that it is optimization-independent. MentionedItems, } ``` And the `mentioned_items` MIR body field docs: ```rust /// Further items that were mentioned in this function and hence *may* become monomorphized, /// depending on optimizations. We use this to avoid optimization-dependent compile errors: the /// collector recursively traverses all "mentioned" items and evaluates all their /// `required_consts`. /// /// This is *not* soundness-critical and the contents of this list are *not* a stable guarantee. /// All that's relevant is that this set is optimization-level-independent, and that it includes /// everything that the collector would consider "used". (For example, we currently compute this /// set after drop elaboration, so some drop calls that can never be reached are not considered /// "mentioned".) See the documentation of `CollectionMode` in /// `compiler/rustc_monomorphize/src/collector.rs` for more context. pub mentioned_items: Vec<Spanned<MentionedItem<'tcx>>>, ``` Fixes #107503
2 parents 47dd709 + 8c01b85 commit df8ac8f

File tree

52 files changed

+1370
-380
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+1370
-380
lines changed
 

‎compiler/rustc_data_structures/src/sync.rs

Lines changed: 6 additions & 3 deletions
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

Lines changed: 46 additions & 0 deletions
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;
@@ -44,6 +45,7 @@ use std::ops::{Index, IndexMut};
4445
use std::{iter, mem};
4546

4647
pub use self::query::*;
48+
use self::visit::TyContext;
4749
pub use basic_blocks::BasicBlocks;
4850

4951
mod basic_blocks;
@@ -304,6 +306,21 @@ impl<'tcx> CoroutineInfo<'tcx> {
304306
}
305307
}
306308

309+
/// Some item that needs to monomorphize successfully for a MIR body to be considered well-formed.
310+
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, HashStable, TyEncodable, TyDecodable)]
311+
#[derive(TypeFoldable, TypeVisitable)]
312+
pub enum MentionedItem<'tcx> {
313+
/// A function that gets called. We don't necessarily know its precise type yet, since it can be
314+
/// hidden behind a generic.
315+
Fn(Ty<'tcx>),
316+
/// A type that has its drop shim called.
317+
Drop(Ty<'tcx>),
318+
/// Unsizing casts might require vtables, so we have to record them.
319+
UnsizeCast { source_ty: Ty<'tcx>, target_ty: Ty<'tcx> },
320+
/// A closure that is coerced to a function pointer.
321+
Closure(Ty<'tcx>),
322+
}
323+
307324
/// The lowered representation of a single function.
308325
#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable, TypeVisitable)]
309326
pub struct Body<'tcx> {
@@ -367,8 +384,24 @@ pub struct Body<'tcx> {
367384

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

392+
/// Further items that were mentioned in this function and hence *may* become monomorphized,
393+
/// depending on optimizations. We use this to avoid optimization-dependent compile errors: the
394+
/// collector recursively traverses all "mentioned" items and evaluates all their
395+
/// `required_consts`.
396+
///
397+
/// This is *not* soundness-critical and the contents of this list are *not* a stable guarantee.
398+
/// All that's relevant is that this set is optimization-level-independent, and that it includes
399+
/// everything that the collector would consider "used". (For example, we currently compute this
400+
/// set after drop elaboration, so some drop calls that can never be reached are not considered
401+
/// "mentioned".) See the documentation of `CollectionMode` in
402+
/// `compiler/rustc_monomorphize/src/collector.rs` for more context.
403+
pub mentioned_items: Vec<Spanned<MentionedItem<'tcx>>>,
404+
372405
/// Does this body use generic parameters. This is used for the `ConstEvaluatable` check.
373406
///
374407
/// Note that this does not actually mean that this body is not computable right now.
@@ -445,6 +478,7 @@ impl<'tcx> Body<'tcx> {
445478
var_debug_info,
446479
span,
447480
required_consts: Vec::new(),
481+
mentioned_items: Vec::new(),
448482
is_polymorphic: false,
449483
injection_phase: None,
450484
tainted_by_errors,
@@ -474,6 +508,7 @@ impl<'tcx> Body<'tcx> {
474508
spread_arg: None,
475509
span: DUMMY_SP,
476510
required_consts: Vec::new(),
511+
mentioned_items: Vec::new(),
477512
var_debug_info: Vec::new(),
478513
is_polymorphic: false,
479514
injection_phase: None,
@@ -568,6 +603,17 @@ impl<'tcx> Body<'tcx> {
568603
}
569604
}
570605

606+
pub fn span_for_ty_context(&self, ty_context: TyContext) -> Span {
607+
match ty_context {
608+
TyContext::UserTy(span) => span,
609+
TyContext::ReturnTy(source_info)
610+
| TyContext::LocalDecl { source_info, .. }
611+
| TyContext::YieldTy(source_info)
612+
| TyContext::ResumeTy(source_info) => source_info.span,
613+
TyContext::Location(loc) => self.source_info(loc).span,
614+
}
615+
}
616+
571617
/// Returns the return type; it always return first element from `local_decls` array.
572618
#[inline]
573619
pub fn return_ty(&self) -> Ty<'tcx> {

0 commit comments

Comments
 (0)
Please sign in to comment.