forked from rust-lang/rust
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of rust-lang#122568 - RalfJung:mentioned-items, r=<try>
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. 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. 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, } ``` Fixes rust-lang#107503
- Loading branch information
Showing
40 changed files
with
1,120 additions
and
191 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
use rustc_middle::mir::visit::Visitor; | ||
use rustc_middle::mir::{self, Location, MentionedItem, MirPass}; | ||
use rustc_middle::ty::{self, adjustment::PointerCoercion, TyCtxt}; | ||
use rustc_session::Session; | ||
use rustc_span::source_map::Spanned; | ||
|
||
pub struct MentionedItems; | ||
|
||
struct MentionedItemsVisitor<'a, 'tcx> { | ||
tcx: TyCtxt<'tcx>, | ||
body: &'a mir::Body<'tcx>, | ||
mentioned_items: &'a mut Vec<Spanned<MentionedItem<'tcx>>>, | ||
} | ||
|
||
impl<'tcx> MirPass<'tcx> for MentionedItems { | ||
fn is_enabled(&self, _sess: &Session) -> bool { | ||
// If this pass is skipped the collector assume that nothing got mentioned! We could | ||
// potentially skip it in opt-level 0 if we are sure that opt-level will never *remove* uses | ||
// of anything, but that still seems fragile. Furthermore, even debug builds use level 1, so | ||
// special-casing level 0 is just not worth it. | ||
true | ||
} | ||
|
||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) { | ||
debug_assert!(body.mentioned_items.is_empty()); | ||
let mut mentioned_items = Vec::new(); | ||
MentionedItemsVisitor { tcx, body, mentioned_items: &mut mentioned_items }.visit_body(body); | ||
body.mentioned_items = mentioned_items; | ||
} | ||
} | ||
|
||
// This visitor is carefully in sync with the one in `rustc_monomorphize::collector`. We are | ||
// visiting the exact same places but then instead of monomorphizing and creating `MonoItems`, we | ||
// have to remain generic and just recording the relevant information in `mentioned_items`, where it | ||
// will then be monomorphized later during "mentioned items" collection. | ||
impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> { | ||
fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) { | ||
self.super_terminator(terminator, location); | ||
let span = || self.body.source_info(location).span; | ||
match &terminator.kind { | ||
mir::TerminatorKind::Call { func, .. } => { | ||
let callee_ty = func.ty(self.body, self.tcx); | ||
self.mentioned_items | ||
.push(Spanned { node: MentionedItem::Fn(callee_ty), span: span() }); | ||
} | ||
mir::TerminatorKind::Drop { place, .. } => { | ||
let ty = place.ty(self.body, self.tcx).ty; | ||
self.mentioned_items.push(Spanned { node: MentionedItem::Drop(ty), span: span() }); | ||
} | ||
mir::TerminatorKind::InlineAsm { ref operands, .. } => { | ||
for op in operands { | ||
match *op { | ||
mir::InlineAsmOperand::SymFn { ref value } => { | ||
self.mentioned_items.push(Spanned { | ||
node: MentionedItem::Fn(value.const_.ty()), | ||
span: span(), | ||
}); | ||
} | ||
_ => {} | ||
} | ||
} | ||
} | ||
_ => {} | ||
} | ||
} | ||
|
||
fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { | ||
self.super_rvalue(rvalue, location); | ||
let span = || self.body.source_info(location).span; | ||
match *rvalue { | ||
// We need to detect unsizing casts that required vtables. | ||
mir::Rvalue::Cast( | ||
mir::CastKind::PointerCoercion(PointerCoercion::Unsize), | ||
ref operand, | ||
target_ty, | ||
) | ||
| mir::Rvalue::Cast(mir::CastKind::DynStar, ref operand, target_ty) => { | ||
self.mentioned_items.push(Spanned { | ||
node: MentionedItem::UnsizeCast { | ||
source_ty: operand.ty(self.body, self.tcx), | ||
target_ty, | ||
}, | ||
span: span(), | ||
}); | ||
} | ||
// Similarly, record closures that are turned into function pointers. | ||
mir::Rvalue::Cast( | ||
mir::CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_)), | ||
ref operand, | ||
_, | ||
) => { | ||
let source_ty = operand.ty(self.body, self.tcx); | ||
match *source_ty.kind() { | ||
ty::Closure(def_id, args) => { | ||
self.mentioned_items.push(Spanned { | ||
node: MentionedItem::Closure(def_id, args), | ||
span: span(), | ||
}); | ||
} | ||
_ => bug!(), | ||
} | ||
} | ||
// And finally, function pointer reification casts. | ||
mir::Rvalue::Cast( | ||
mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer), | ||
ref operand, | ||
_, | ||
) => { | ||
let fn_ty = operand.ty(self.body, self.tcx); | ||
self.mentioned_items.push(Spanned { node: MentionedItem::Fn(fn_ty), span: span() }); | ||
} | ||
_ => {} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.