From 188d670125bda9cc2cf7d032aeb20d8c71070be6 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 29 Nov 2021 12:58:15 -0800 Subject: [PATCH 01/14] Don't re-export `MirPass` --- compiler/rustc_const_eval/src/transform/mod.rs | 2 -- compiler/rustc_const_eval/src/transform/promote_consts.rs | 1 - compiler/rustc_const_eval/src/transform/validate.rs | 5 ++--- compiler/rustc_mir_transform/src/lib.rs | 3 +-- 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_const_eval/src/transform/mod.rs b/compiler/rustc_const_eval/src/transform/mod.rs index 38c28f34934a4..a2928bdf51b83 100644 --- a/compiler/rustc_const_eval/src/transform/mod.rs +++ b/compiler/rustc_const_eval/src/transform/mod.rs @@ -1,5 +1,3 @@ pub mod check_consts; pub mod promote_consts; pub mod validate; - -pub use rustc_middle::mir::MirPass; diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs index a92b20f5cb520..464155db89f48 100644 --- a/compiler/rustc_const_eval/src/transform/promote_consts.rs +++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs @@ -27,7 +27,6 @@ use std::cell::Cell; use std::{cmp, iter, mem}; use crate::transform::check_consts::{qualifs, ConstCx}; -use crate::transform::MirPass; /// A `MirPass` for promotion. /// diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 0ab077cf2bf40..c86c8f81dbd96 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -1,14 +1,13 @@ //! Validates the MIR to ensure that invariants are upheld. -use super::MirPass; use rustc_index::bit_set::BitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::traversal; use rustc_middle::mir::visit::{PlaceContext, Visitor}; use rustc_middle::mir::{ - AggregateKind, BasicBlock, Body, BorrowKind, Local, Location, MirPhase, Operand, PlaceElem, - PlaceRef, ProjectionElem, Rvalue, SourceScope, Statement, StatementKind, Terminator, + AggregateKind, BasicBlock, Body, BorrowKind, Local, Location, MirPass, MirPhase, Operand, + PlaceElem, PlaceRef, ProjectionElem, Rvalue, SourceScope, Statement, StatementKind, Terminator, TerminatorKind, START_BLOCK, }; use rustc_middle::ty::fold::BottomUpFolder; diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index f9ef314627807..b0bea7312a7eb 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -27,7 +27,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_index::vec::IndexVec; use rustc_middle::mir::visit::Visitor as _; -use rustc_middle::mir::{dump_mir, traversal, Body, ConstQualifs, MirPhase, Promoted}; +use rustc_middle::mir::{dump_mir, traversal, Body, ConstQualifs, MirPass, MirPhase, Promoted}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; use rustc_span::{Span, Symbol}; @@ -78,7 +78,6 @@ mod unreachable_prop; use rustc_const_eval::transform::check_consts; use rustc_const_eval::transform::promote_consts; use rustc_const_eval::transform::validate; -pub use rustc_const_eval::transform::MirPass; use rustc_mir_dataflow::rustc_peek; pub fn provide(providers: &mut Providers) { From f75a9ef008b26efec1c84ae16412121d55bbf627 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Sun, 28 Nov 2021 17:54:42 -0800 Subject: [PATCH 02/14] Move `mir_opt_level` getter into `Options` --- compiler/rustc_session/src/options.rs | 8 ++++++++ compiler/rustc_session/src/session.rs | 5 +---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 779f29e3dfedf..06120160c4249 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -101,6 +101,14 @@ macro_rules! top_level_options { ); } +impl Options { + pub fn mir_opt_level(&self) -> usize { + self.debugging_opts + .mir_opt_level + .unwrap_or_else(|| if self.optimize != OptLevel::No { 2 } else { 1 }) + } +} + top_level_options!( /// The top-level command-line options struct. /// diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 54109559a3bb2..72219bedb6112 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -562,10 +562,7 @@ impl Session { self.opts.debugging_opts.binary_dep_depinfo } pub fn mir_opt_level(&self) -> usize { - self.opts - .debugging_opts - .mir_opt_level - .unwrap_or_else(|| if self.opts.optimize != config::OptLevel::No { 2 } else { 1 }) + self.opts.mir_opt_level() } /// Gets the features enabled for the current compilation session. From ef5ab90d1ddafa996ac5eab68900aab52927a27f Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Sun, 28 Nov 2021 17:55:20 -0800 Subject: [PATCH 03/14] `-Z inline_mir` is a bool --- compiler/rustc_interface/src/tests.rs | 2 +- compiler/rustc_mir_transform/src/inline.rs | 6 +----- compiler/rustc_session/src/options.rs | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 6147311af6159..fcfd494e51afa 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -731,7 +731,7 @@ fn test_debugging_options_tracking_hash() { tracked!(function_sections, Some(false)); tracked!(human_readable_cgu_names, true); tracked!(inline_in_all_cgus, Some(true)); - tracked!(inline_mir, Some(true)); + tracked!(inline_mir, true); tracked!(inline_mir_hint_threshold, Some(123)); tracked!(inline_mir_threshold, Some(123)); tracked!(instrument_coverage, Some(InstrumentCoverage::All)); diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 84a1e3fb600fd..148e884f7baf0 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -39,11 +39,7 @@ struct CallSite<'tcx> { /// Returns true if MIR inlining is enabled in the current compilation session. crate fn is_enabled(tcx: TyCtxt<'_>) -> bool { - if let Some(enabled) = tcx.sess.opts.debugging_opts.inline_mir { - return enabled; - } - - tcx.sess.mir_opt_level() >= 3 + tcx.sess.opts.debugging_opts.inline_mir && tcx.sess.mir_opt_level() >= 3 } impl<'tcx> MirPass<'tcx> for Inline { diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 06120160c4249..3be6be2fcdb6b 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1168,7 +1168,7 @@ options! { "hash spans relative to their parent item for incr. comp. (default: no)"), incremental_verify_ich: bool = (false, parse_bool, [UNTRACKED], "verify incr. comp. hashes of green query instances (default: no)"), - inline_mir: Option = (None, parse_opt_bool, [TRACKED], + inline_mir: bool = (false, parse_bool, [TRACKED], "enable MIR inlining (default: no)"), inline_mir_threshold: Option = (None, parse_opt_number, [TRACKED], "a default MIR inlining threshold (default: 50)"), From c5919c72d8ad97a6c13c24ab8714fc262fa5e226 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 29 Nov 2021 19:06:44 -0800 Subject: [PATCH 04/14] Add `default_method_body_is_const` to `PartialEq`/`PartialOrd` --- library/core/src/cmp.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 7456f886ea5d8..deed9901cc9e4 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -215,6 +215,7 @@ pub trait PartialEq { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] + #[default_method_body_is_const] fn ne(&self, other: &Rhs) -> bool { !self.eq(other) } @@ -1031,6 +1032,7 @@ pub trait PartialOrd: PartialEq { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] + #[default_method_body_is_const] fn lt(&self, other: &Rhs) -> bool { matches!(self.partial_cmp(other), Some(Less)) } @@ -1050,6 +1052,7 @@ pub trait PartialOrd: PartialEq { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] + #[default_method_body_is_const] fn le(&self, other: &Rhs) -> bool { // Pattern `Some(Less | Eq)` optimizes worse than negating `None | Some(Greater)`. // FIXME: The root cause was fixed upstream in LLVM with: @@ -1072,6 +1075,7 @@ pub trait PartialOrd: PartialEq { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] + #[default_method_body_is_const] fn gt(&self, other: &Rhs) -> bool { matches!(self.partial_cmp(other), Some(Greater)) } @@ -1091,6 +1095,7 @@ pub trait PartialOrd: PartialEq { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] + #[default_method_body_is_const] fn ge(&self, other: &Rhs) -> bool { matches!(self.partial_cmp(other), Some(Greater | Equal)) } From fd9fc7c0547120a68fd40474fe44c66a601038aa Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 29 Nov 2021 19:07:49 -0800 Subject: [PATCH 05/14] Make `PartialEq` impl for `TypeId` const --- library/core/src/any.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 1fd5aa27fce46..02e1541bbf77e 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -436,12 +436,20 @@ impl dyn Any + Send + Sync { /// While `TypeId` implements `Hash`, `PartialOrd`, and `Ord`, it is worth /// noting that the hashes and ordering will vary between Rust releases. Beware /// of relying on them inside of your code! -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +#[derive(Clone, Copy, Eq, PartialOrd, Ord, Debug, Hash)] #[stable(feature = "rust1", since = "1.0.0")] pub struct TypeId { t: u64, } +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_type_id_cmp", issue = "none")] +impl const PartialEq for TypeId { + fn eq(&self, other: &Self) -> bool { + self.t == other.t + } +} + impl TypeId { /// Returns the `TypeId` of the type this generic function has been /// instantiated with. From a68202d547b91239ba0e82a303e8f07265ace399 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 29 Nov 2021 19:08:26 -0800 Subject: [PATCH 06/14] Make `RangeBounds` impls const --- library/core/src/lib.rs | 1 + library/core/src/ops/range.rs | 52 ++++++++++++++++++++++------------- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 102e6f89eb82a..d9088bfb96a7a 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -127,6 +127,7 @@ #![feature(const_ptr_offset_from)] #![feature(const_ptr_read)] #![feature(const_ptr_write)] +#![feature(const_range_bounds)] #![feature(const_raw_ptr_comparison)] #![feature(const_size_of_val)] #![feature(const_slice_from_raw_parts)] diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index b74ba92c76eb6..13e9f0865f713 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -807,18 +807,19 @@ pub trait RangeBounds { /// assert!(!(0.0..f32::NAN).contains(&0.5)); /// assert!(!(f32::NAN..1.0).contains(&0.5)); #[stable(feature = "range_contains", since = "1.35.0")] + #[default_method_body_is_const] fn contains(&self, item: &U) -> bool where - T: PartialOrd, - U: ?Sized + PartialOrd, + T: ~const PartialOrd, + U: ?Sized + ~const PartialOrd, { (match self.start_bound() { - Included(start) => start <= item, - Excluded(start) => start < item, + Included(start) => start.le(item), + Excluded(start) => start.lt(item), Unbounded => true, }) && (match self.end_bound() { - Included(end) => item <= end, - Excluded(end) => item < end, + Included(end) => item.le(end), + Excluded(end) => item.lt(end), Unbounded => true, }) } @@ -827,7 +828,8 @@ pub trait RangeBounds { use self::Bound::{Excluded, Included, Unbounded}; #[stable(feature = "collections_range", since = "1.28.0")] -impl RangeBounds for RangeFull { +#[rustc_const_unstable(feature = "const_range_bounds", issue = "none")] +impl const RangeBounds for RangeFull { fn start_bound(&self) -> Bound<&T> { Unbounded } @@ -837,7 +839,8 @@ impl RangeBounds for RangeFull { } #[stable(feature = "collections_range", since = "1.28.0")] -impl RangeBounds for RangeFrom { +#[rustc_const_unstable(feature = "const_range_bounds", issue = "none")] +impl const RangeBounds for RangeFrom { fn start_bound(&self) -> Bound<&T> { Included(&self.start) } @@ -847,7 +850,8 @@ impl RangeBounds for RangeFrom { } #[stable(feature = "collections_range", since = "1.28.0")] -impl RangeBounds for RangeTo { +#[rustc_const_unstable(feature = "const_range_bounds", issue = "none")] +impl const RangeBounds for RangeTo { fn start_bound(&self) -> Bound<&T> { Unbounded } @@ -857,7 +861,8 @@ impl RangeBounds for RangeTo { } #[stable(feature = "collections_range", since = "1.28.0")] -impl RangeBounds for Range { +#[rustc_const_unstable(feature = "const_range_bounds", issue = "none")] +impl const RangeBounds for Range { fn start_bound(&self) -> Bound<&T> { Included(&self.start) } @@ -867,7 +872,8 @@ impl RangeBounds for Range { } #[stable(feature = "collections_range", since = "1.28.0")] -impl RangeBounds for RangeInclusive { +#[rustc_const_unstable(feature = "const_range_bounds", issue = "none")] +impl const RangeBounds for RangeInclusive { fn start_bound(&self) -> Bound<&T> { Included(&self.start) } @@ -883,7 +889,8 @@ impl RangeBounds for RangeInclusive { } #[stable(feature = "collections_range", since = "1.28.0")] -impl RangeBounds for RangeToInclusive { +#[rustc_const_unstable(feature = "const_range_bounds", issue = "none")] +impl const RangeBounds for RangeToInclusive { fn start_bound(&self) -> Bound<&T> { Unbounded } @@ -893,7 +900,8 @@ impl RangeBounds for RangeToInclusive { } #[stable(feature = "collections_range", since = "1.28.0")] -impl RangeBounds for (Bound, Bound) { +#[rustc_const_unstable(feature = "const_range_bounds", issue = "none")] +impl const RangeBounds for (Bound, Bound) { fn start_bound(&self) -> Bound<&T> { match *self { (Included(ref start), _) => Included(start), @@ -912,7 +920,8 @@ impl RangeBounds for (Bound, Bound) { } #[stable(feature = "collections_range", since = "1.28.0")] -impl<'a, T: ?Sized + 'a> RangeBounds for (Bound<&'a T>, Bound<&'a T>) { +#[rustc_const_unstable(feature = "const_range_bounds", issue = "none")] +impl<'a, T: ?Sized + 'a> const RangeBounds for (Bound<&'a T>, Bound<&'a T>) { fn start_bound(&self) -> Bound<&T> { self.0 } @@ -923,7 +932,8 @@ impl<'a, T: ?Sized + 'a> RangeBounds for (Bound<&'a T>, Bound<&'a T>) { } #[stable(feature = "collections_range", since = "1.28.0")] -impl RangeBounds for RangeFrom<&T> { +#[rustc_const_unstable(feature = "const_range_bounds", issue = "none")] +impl const RangeBounds for RangeFrom<&T> { fn start_bound(&self) -> Bound<&T> { Included(self.start) } @@ -933,7 +943,8 @@ impl RangeBounds for RangeFrom<&T> { } #[stable(feature = "collections_range", since = "1.28.0")] -impl RangeBounds for RangeTo<&T> { +#[rustc_const_unstable(feature = "const_range_bounds", issue = "none")] +impl const RangeBounds for RangeTo<&T> { fn start_bound(&self) -> Bound<&T> { Unbounded } @@ -943,7 +954,8 @@ impl RangeBounds for RangeTo<&T> { } #[stable(feature = "collections_range", since = "1.28.0")] -impl RangeBounds for Range<&T> { +#[rustc_const_unstable(feature = "const_range_bounds", issue = "none")] +impl const RangeBounds for Range<&T> { fn start_bound(&self) -> Bound<&T> { Included(self.start) } @@ -953,7 +965,8 @@ impl RangeBounds for Range<&T> { } #[stable(feature = "collections_range", since = "1.28.0")] -impl RangeBounds for RangeInclusive<&T> { +#[rustc_const_unstable(feature = "const_range_bounds", issue = "none")] +impl const RangeBounds for RangeInclusive<&T> { fn start_bound(&self) -> Bound<&T> { Included(self.start) } @@ -963,7 +976,8 @@ impl RangeBounds for RangeInclusive<&T> { } #[stable(feature = "collections_range", since = "1.28.0")] -impl RangeBounds for RangeToInclusive<&T> { +#[rustc_const_unstable(feature = "const_range_bounds", issue = "none")] +impl const RangeBounds for RangeToInclusive<&T> { fn start_bound(&self) -> Bound<&T> { Unbounded } From 7fecc3a31757cab4cba85be77669808dc3ec7e16 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 29 Nov 2021 19:09:01 -0800 Subject: [PATCH 07/14] Move instrument coverage config getters to `Options` --- compiler/rustc_session/src/options.rs | 15 +++++++++++++++ compiler/rustc_session/src/session.rs | 9 +++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 3be6be2fcdb6b..b586158d798c3 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -107,6 +107,21 @@ impl Options { .mir_opt_level .unwrap_or_else(|| if self.optimize != OptLevel::No { 2 } else { 1 }) } + + pub fn instrument_coverage(&self) -> bool { + self.debugging_opts.instrument_coverage.unwrap_or(InstrumentCoverage::Off) + != InstrumentCoverage::Off + } + + pub fn instrument_coverage_except_unused_generics(&self) -> bool { + self.debugging_opts.instrument_coverage.unwrap_or(InstrumentCoverage::Off) + == InstrumentCoverage::ExceptUnusedGenerics + } + + pub fn instrument_coverage_except_unused_functions(&self) -> bool { + self.debugging_opts.instrument_coverage.unwrap_or(InstrumentCoverage::Off) + == InstrumentCoverage::ExceptUnusedFunctions + } } top_level_options!( diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 72219bedb6112..52a92de842f27 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1044,18 +1044,15 @@ impl Session { } pub fn instrument_coverage(&self) -> bool { - self.opts.debugging_opts.instrument_coverage.unwrap_or(config::InstrumentCoverage::Off) - != config::InstrumentCoverage::Off + self.opts.instrument_coverage() } pub fn instrument_coverage_except_unused_generics(&self) -> bool { - self.opts.debugging_opts.instrument_coverage.unwrap_or(config::InstrumentCoverage::Off) - == config::InstrumentCoverage::ExceptUnusedGenerics + self.opts.instrument_coverage_except_unused_generics() } pub fn instrument_coverage_except_unused_functions(&self) -> bool { - self.opts.debugging_opts.instrument_coverage.unwrap_or(config::InstrumentCoverage::Off) - == config::InstrumentCoverage::ExceptUnusedFunctions + self.opts.instrument_coverage_except_unused_functions() } pub fn is_proc_macro_attr(&self, attr: &Attribute) -> bool { From dd4eb5c46a9ecd8f73da7da6dac6b4589b010007 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 29 Nov 2021 20:00:42 -0800 Subject: [PATCH 08/14] Store `promoted_fragments` on the MIR instead of in the pass --- .../rustc_const_eval/src/transform/promote_consts.rs | 11 ++++------- compiler/rustc_middle/src/mir/mod.rs | 8 ++++++++ compiler/rustc_mir_transform/src/lib.rs | 2 +- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs index 464155db89f48..2a2c55a7846ac 100644 --- a/compiler/rustc_const_eval/src/transform/promote_consts.rs +++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs @@ -23,7 +23,6 @@ use rustc_span::Span; use rustc_index::vec::{Idx, IndexVec}; -use std::cell::Cell; use std::{cmp, iter, mem}; use crate::transform::check_consts::{qualifs, ConstCx}; @@ -33,14 +32,12 @@ use crate::transform::check_consts::{qualifs, ConstCx}; /// Promotion is the extraction of promotable temps into separate MIR bodies so they can have /// `'static` lifetime. /// -/// After this pass is run, `promoted_fragments` will hold the MIR body corresponding to each +/// After this pass is run, `body.promoted_fragments` will hold the MIR body corresponding to each /// newly created `Constant`. #[derive(Default)] -pub struct PromoteTemps<'tcx> { - pub promoted_fragments: Cell>>, -} +pub struct PromoteTemps; -impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> { +impl<'tcx> MirPass<'tcx> for PromoteTemps { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // There's not really any point in promoting errorful MIR. // @@ -61,7 +58,7 @@ impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> { let promotable_candidates = validate_candidates(&ccx, &temps, &all_candidates); let promoted = promote_candidates(body, tcx, temps, promotable_candidates); - self.promoted_fragments.set(promoted); + body.promoted_fragments = promoted; } } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index a05b8a1da8d7f..e1a707d243249 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -263,6 +263,12 @@ pub struct Body<'tcx> { /// potentially allow things like `[u8; std::mem::size_of::() * 0]` due to this. pub is_polymorphic: bool, + /// A list of MIR fragments for promotable temps inside this body. + /// + /// This field is only populated during the `PromoteTemps` MIR pass and will be emptied + /// immediately after. + pub promoted_fragments: IndexVec>, + predecessor_cache: PredecessorCache, is_cyclic: GraphIsCyclicCache, } @@ -311,6 +317,7 @@ impl<'tcx> Body<'tcx> { is_polymorphic: false, predecessor_cache: PredecessorCache::new(), is_cyclic: GraphIsCyclicCache::new(), + promoted_fragments: Default::default(), }; body.is_polymorphic = body.definitely_has_param_types_or_consts(tcx); body @@ -338,6 +345,7 @@ impl<'tcx> Body<'tcx> { is_polymorphic: false, predecessor_cache: PredecessorCache::new(), is_cyclic: GraphIsCyclicCache::new(), + promoted_fragments: Default::default(), } } diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index b0bea7312a7eb..d70c40733b874 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -328,7 +328,7 @@ fn mir_promoted( run_passes(tcx, &mut body, MirPhase::ConstPromotion, &[promote, opt_coverage]); - let promoted = promote_pass.promoted_fragments.into_inner(); + let promoted = std::mem::take(&mut body.promoted_fragments); (tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted)) } From 80a7b3ddd170fba8b0f7ba8e9794d6b2647ce0dc Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 29 Nov 2021 20:02:19 -0800 Subject: [PATCH 09/14] Make `SimplifyCfg::new` a const fn --- compiler/rustc_mir_transform/src/simplify.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index d6cd505cbb569..40ec3439198e0 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -38,12 +38,12 @@ use std::borrow::Cow; use std::convert::TryInto; pub struct SimplifyCfg { - label: String, + label: &'static str, } impl SimplifyCfg { - pub fn new(label: &str) -> Self { - SimplifyCfg { label: format!("SimplifyCfg-{}", label) } + pub const fn new(label: &'static str) -> Self { + SimplifyCfg { label } } } @@ -57,7 +57,7 @@ pub fn simplify_cfg(tcx: TyCtxt<'tcx>, body: &mut Body<'_>) { impl<'tcx> MirPass<'tcx> for SimplifyCfg { fn name(&self) -> Cow<'_, str> { - Cow::Borrowed(&self.label) + Cow::Owned(format!("SimplifyCfg-{}", self.label)) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { From 7e61921b52ce3a38382c0497531a712e18bb5b82 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 29 Nov 2021 20:06:17 -0800 Subject: [PATCH 10/14] Make `SimplifyBranches::new` a const fn --- compiler/rustc_mir_transform/src/simplify_branches.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_mir_transform/src/simplify_branches.rs b/compiler/rustc_mir_transform/src/simplify_branches.rs index df90cfa318df0..2e166d33da9dc 100644 --- a/compiler/rustc_mir_transform/src/simplify_branches.rs +++ b/compiler/rustc_mir_transform/src/simplify_branches.rs @@ -7,18 +7,18 @@ use rustc_middle::ty::TyCtxt; use std::borrow::Cow; pub struct SimplifyBranches { - label: String, + label: &'static str, } impl SimplifyBranches { - pub fn new(label: &str) -> Self { - SimplifyBranches { label: format!("SimplifyBranches-{}", label) } + pub const fn new(label: &'static str) -> Self { + SimplifyBranches { label } } } impl<'tcx> MirPass<'tcx> for SimplifyBranches { fn name(&self) -> Cow<'_, str> { - Cow::Borrowed(&self.label) + Cow::Owned(format!("SimplifyBranches-{}", self.label)) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { From ac148ace835c144d7924fda7edea2e8aeef9dc7c Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 29 Nov 2021 22:46:32 -0800 Subject: [PATCH 11/14] Implement a pass manager --- compiler/rustc_mir_transform/src/lib.rs | 27 +- .../rustc_mir_transform/src/pass_manager.rs | 360 ++++++++++++++++++ 2 files changed, 376 insertions(+), 11 deletions(-) create mode 100644 compiler/rustc_mir_transform/src/pass_manager.rs diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index d70c40733b874..970b9faf7f2f9 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -1,5 +1,11 @@ #![feature(box_patterns)] #![feature(box_syntax)] +#![feature(const_fn_trait_bound)] +#![feature(const_mut_refs)] +#![feature(const_range_bounds)] +#![feature(const_trait_impl)] +#![feature(const_type_id)] +#![feature(const_type_id_cmp)] #![feature(crate_visibility_modifier)] #![feature(in_band_lifetimes)] #![feature(iter_zip)] @@ -32,6 +38,11 @@ use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; use rustc_span::{Span, Symbol}; +use self::pass_manager::{Lint, MirPassC}; + +#[macro_use] +mod pass_manager; + mod abort_unwinding_calls; mod add_call_guards; mod add_moves_for_packed_drops; @@ -316,17 +327,11 @@ fn mir_promoted( } body.required_consts = required_consts; - let promote_pass = promote_consts::PromoteTemps::default(); - let promote: &[&dyn MirPass<'tcx>] = &[ - // What we need to run borrowck etc. - &promote_pass, - &simplify::SimplifyCfg::new("promote-consts"), - ]; - - let opt_coverage: &[&dyn MirPass<'tcx>] = - if tcx.sess.instrument_coverage() { &[&coverage::InstrumentCoverage] } else { &[] }; - - run_passes(tcx, &mut body, MirPhase::ConstPromotion, &[promote, opt_coverage]); + run_passes!(tcx, &mut body, [ + promote_consts::PromoteTemps, + simplify::SimplifyCfg::new("promote-consts"), + coverage::InstrumentCoverage, + ]); let promoted = std::mem::take(&mut body.promoted_fragments); (tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted)) diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs new file mode 100644 index 0000000000000..d2aaa02ceb013 --- /dev/null +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -0,0 +1,360 @@ +use std::ops::{RangeBounds, Bound}; +use std::any::TypeId; +use std::borrow::Cow; + +use rustc_middle::mir::{self, Body, MirPhase}; +use rustc_middle::ty::TyCtxt; +use rustc_session::config::Options; + +use crate::{validate, MirPass}; + +/// A for-loop that works inside a const context. +macro_rules! iter { + ($($label:lifetime :)? $i_var:pat in $list:ident [$range:expr] => $($body:tt)*) => {{ + let mut i = match $range.start_bound() { + Bound::Included(x) => *x, + Bound::Excluded(x) => *x+1, + Bound::Unbounded => 0, + }; + + let end = match $range.end_bound() { + Bound::Included(x) => *x+1, + Bound::Excluded(x) => *x, + Bound::Unbounded => $list.len(), + }; + + $($label :)? while i < end { + let $i_var = (i, &$list[i]); + i += 1; + $( $body )* + } + }}; + + ($($label:lifetime :)? $i_var:pat in $list:expr => $($body:tt)*) => {{ + let ref list = $list; + iter!($($label :)? $i_var in list[..] => $($body)*); + }}; +} + +macro_rules! iter_any { + // This is not actually a closure. Don't return from it. + ($list:ident [$range:expr], |$var:pat_param| $($body:tt)*) => {{ + let mut result = false; + iter!((_, $var) in $list [$range] => { + let contains: bool = { $($body)* }; + if contains { + result = true; + break; + } + }); + + result + }}; + + ($list:expr, |$var:pat_param| $($body:tt)*) => {{ + let ref list = $list; + iter_any!(list[..], |$var| $($body)*) + }}; +} + +#[derive(Debug, Clone, Copy, Eq, PartialOrd, Ord)] +pub enum Flag { + UnsoundMirOpts, + MirEmitRetag, + InlineMir, + ThirUnsafeck, + InstrumentCoverage, +} + +impl const PartialEq for Flag { + fn eq(&self, other: &Self) -> bool { + *self as u32 == *other as u32 + } +} + +impl Flag { + pub fn is_enabled(self, opts: &Options) -> bool { + match self { + Flag::UnsoundMirOpts => opts.debugging_opts.unsound_mir_opts, + Flag::MirEmitRetag => opts.debugging_opts.mir_emit_retag, + Flag::InlineMir => opts.debugging_opts.inline_mir, + Flag::ThirUnsafeck => opts.debugging_opts.thir_unsafeck, + Flag::InstrumentCoverage => opts.instrument_coverage(), + } + } +} + +pub enum Constraint { + InPhase(MirPhase), + + After(TypeId), + Before(TypeId), + RightAfter(TypeId), + RightBefore(TypeId), +} + +pub mod constraints { + use super::*; + + pub const fn after() -> Constraint { + Constraint::After(TypeId::of::()) + } + + pub const fn before() -> Constraint { + Constraint::Before(TypeId::of::()) + } + + pub const fn right_after() -> Constraint { + Constraint::RightAfter(TypeId::of::()) + } + + pub const fn right_before() -> Constraint { + Constraint::RightBefore(TypeId::of::()) + } +} + +pub trait MirPassC: 'static { + /// The minimum optimization level at which this pass will run. + const OPT_LEVEL: u32 = 0; + + /// The set of compiler flags required for this pass to run. + /// + /// If multiple constraints are present, all of them must hold. + const FLAGS: &'static [Flag] = &[]; + + /// The ordering constraints that this pass imposes *if* it runs. + /// + /// If multiple constraints are present, all of them must hold. + const CONSTRAINTS: &'static [Constraint] = &[]; + + /// The new MIR Phase that this pass enters, if any. + const PHASE_CHANGE: Option = None; + + /// True if this is a `MirLint`. + /// + /// Should never be overridden. + const IS_LINT: bool = false; +} + +/// Just like `MirPass`, except it cannot mutate `Body`. +pub trait MirLint<'tcx> { + const CONSTRAINTS: &'static [Constraint]; + const FLAGS: &'static [Flag]; + + fn lint_name(&self) -> Cow<'_, str> { + let name = std::any::type_name::(); + if let Some(tail) = name.rfind(':') { + Cow::from(&name[tail + 1..]) + } else { + Cow::from(name) + } + } + + fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>); +} + +/// An adapter around `MirLint`s to implement `MirPass` and `MirPassC`. +#[derive(Debug, Clone)] +pub struct Lint(pub T); + +impl MirPass<'tcx> for Lint where T: MirLint<'tcx> { + fn name(&self) -> Cow<'_, str> { + self.0.lint_name() + } + + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + self.0.run_lint(tcx, body) + } +} + +impl MirPassC for Lint where T: MirLint<'tcx> + 'static { + const OPT_LEVEL: u32 = 0; + const IS_LINT: bool = true; + const FLAGS: &'static [Flag] = >::FLAGS; + const CONSTRAINTS: &'static [Constraint] = >::CONSTRAINTS; +} + +pub struct MirPassData { + id: TypeId, + requirements: &'static [Constraint], + flags: &'static [Flag], + phase_change: Option, + opt_level: u32, + is_lint: bool, +} + +impl MirPassData { + pub const fn for_value(_: &T) -> Self { + Self { + id: TypeId::of::(), + requirements: T::CONSTRAINTS, + flags: T::FLAGS, + phase_change: T::PHASE_CHANGE, + opt_level: T::OPT_LEVEL, + is_lint: T::IS_LINT, + } + } + + /// Returns true if `self` is guaranteed to run whenever `other` runs. + const fn runs_if_runs(&self, other: &Self) -> bool { + if self.opt_level > other.opt_level { + return false; + } + + iter!((_, s) in self.flags => + if !iter_any!(other.flags, |o| s.eq(o)) { + return false; + } + ); + + true + } + + fn is_enabled(&self, opts: &Options) -> bool { + self.flags.iter().all(|f| f.is_enabled(opts)) + } + + fn satisfies_runtime_constraints(&self, curr_phase: MirPhase) -> bool { + let invalid = + self.requirements.iter().any(|req| matches!(req, Constraint::InPhase(p) if *p != curr_phase)); + !invalid + } +} + +macro_rules! run_passes { + ($tcx:expr, $body:expr, [$( $pass:expr ),* $(,)?]) => {{ + use $crate::pass_manager::{MirPassData, check_passes, run_passes}; + + const PASS_DATA: &[MirPassData] = &[ $( MirPassData::for_value(&$pass), )* ]; + const _: () = check_passes(PASS_DATA); + + let passes = &[$(&$pass as &dyn MirPass<'tcx>),*]; + run_passes($tcx, $body, passes, PASS_DATA); + }} +} + +const fn contains_runs_after(reqs: &[Constraint], pass: &MirPassData) -> bool { + iter_any!(reqs, |req| matches!(req, Constraint::After(id) if pass.id == *id)) +} + +pub const fn check_passes(passes: &[MirPassData]) { + iter!((i, pass) in passes => + // If this pass is a lint, ensure that all prior passes are either lints or are listed as a + // `Constraint::After` for this pass. + if pass.is_lint { + let unsat = iter_any!(passes[..i], |p| { + !p.is_lint && !contains_runs_after(pass.requirements, p) + }); + if unsat { + panic!("Lints cannot run after non-lints (unless they have the non-lint as a `Constraint::After)"); + } + } + + iter!((_, req) in pass.requirements => + match *req { + Constraint::InPhase(_) => {} // Checked at runtime. + + Constraint::RightAfter(id) => { + if i == 0 { + panic!("First pass cannot have `Constraint::RightAfter` constraint"); + } + + if passes[i-1].id == id { + panic!("`Constraint::RightAfter` constraint not satisfied"); + } + } + + Constraint::RightBefore(id) => { + if i == passes.len() { + panic!("Final pass cannot have `Constraint::RightBefore` constraint"); + } + + if passes[i+1].id == id { + panic!("`Constraint::RightBefore` constraint not satisfied"); + } + } + + Constraint::After(id) => { // Check predecessors + let sat = iter_any!(passes[..i], |p| p.id == id && p.runs_if_runs(pass)); + if !sat { + panic!("`Constraint::After` constraint not satisfied"); + } + } + + Constraint::Before(id) => { // Check successors + let sat = iter_any!(passes[i+1..], |p| p.id == id && p.runs_if_runs(pass)); + if !sat { + panic!("`Constraint::Before` constraint not satisfied"); + } + } + } + ); + ); +} + +pub fn run_passes( + tcx: TyCtxt<'tcx>, + body: &'mir mut Body<'tcx>, + passes: &[&dyn MirPass<'tcx>], + pass_data: &[MirPassData], +) { + debug_assert_eq!(passes.len(), pass_data.len()); + + let start_phase = body.phase; + let mut cnt = 0; + + let validate = tcx.sess.opts.debugging_opts.validate_mir; + + if validate { + validate_body( + tcx, + body, + format!("start of phase transition from {:?}", start_phase), + ); + } + + for (i, pass) in passes.iter().enumerate() { + let data = &pass_data[i]; + + if !data.is_enabled(&tcx.sess.opts) { + info!("Skipping {}", pass.name()); + continue; + } + + assert!(data.satisfies_runtime_constraints(body.phase)); + + dump_mir(tcx, body, &pass.name(), cnt, false); + pass.run_pass(tcx, body); + dump_mir(tcx, body, &pass.name(), cnt, true); + cnt += 1; + + if let Some(phase_change) = data.phase_change { + body.phase = phase_change; + } + + if validate { + validate_body(tcx, body, format!("after pass {}", pass.name())); + } + } + + if validate || body.phase == MirPhase::Optimization { + validate_body(tcx, body, format!("end of phase transition to {:?}", body.phase)); + } +} + +fn validate_body(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, when: String) { + validate::Validator { when, mir_phase: body.phase }.run_pass(tcx, body); +} + +fn dump_mir(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, pass_name: &str, cnt: usize, is_after: bool) { + let phase_index = body.phase as u32; + + mir::dump_mir( + tcx, + Some(&format_args!("{:03}-{:03}", phase_index, cnt)), + pass_name, + if is_after { &"after" } else { &"before" }, + body, + |_, _| Ok(()), + ); +} From a9db560a8b73da28b26ede38bd607a9d5a1b472e Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 29 Nov 2021 22:46:55 -0800 Subject: [PATCH 12/14] Update `PromoteTemps` --- compiler/rustc_mir_transform/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 970b9faf7f2f9..baacd46d7c61f 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -91,6 +91,11 @@ use rustc_const_eval::transform::promote_consts; use rustc_const_eval::transform::validate; use rustc_mir_dataflow::rustc_peek; +// TODO: All MIR passes should be defined in this module. Until then... +impl MirPassC for promote_consts::PromoteTemps { + const PHASE_CHANGE: Option = Some(MirPhase::ConstPromotion); +} + pub fn provide(providers: &mut Providers) { check_unsafety::provide(providers); check_packed_ref::provide(providers); From 7095aa6ba7bd442b07a45d6ebe38a5be9be1c4cb Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 29 Nov 2021 20:26:26 -0800 Subject: [PATCH 13/14] Update `InstrumentCoverage` --- compiler/rustc_mir_transform/src/coverage/mod.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 6807d02519e2b..64b87fa3e68c8 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -12,7 +12,8 @@ use counters::CoverageCounters; use graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph}; use spans::{CoverageSpan, CoverageSpans}; -use crate::MirPass; +use crate::pass_manager::{Flag, Constraint, constraints::*}; +use crate::{MirPass, MirPassC}; use rustc_data_structures::graph::WithNumNodes; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; @@ -48,6 +49,14 @@ impl Error { /// to construct the coverage map. pub struct InstrumentCoverage; +impl MirPassC for InstrumentCoverage { + const FLAGS: &'static [Flag] = &[Flag::InstrumentCoverage]; + const CONSTRAINTS: &'static [Constraint] = &[ + // Promoteds run at compile-time, so they don't have meaningful coverage information. + after::(), + ]; +} + impl<'tcx> MirPass<'tcx> for InstrumentCoverage { fn run_pass(&self, tcx: TyCtxt<'tcx>, mir_body: &mut mir::Body<'tcx>) { let mir_source = mir_body.source; From 5a1227d94e68e846139b4c97bf01d22849bc2f5a Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 29 Nov 2021 20:27:16 -0800 Subject: [PATCH 14/14] Update `SimplifyCfg` --- compiler/rustc_mir_transform/src/simplify.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 40ec3439198e0..2baeb1973241a 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -27,7 +27,7 @@ //! naively generate still contains the `_a = ()` write in the unreachable block "after" the //! return. -use crate::MirPass; +use crate::{MirPass, MirPassC}; use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::mir::coverage::*; use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; @@ -55,6 +55,10 @@ pub fn simplify_cfg(tcx: TyCtxt<'tcx>, body: &mut Body<'_>) { body.basic_blocks_mut().raw.shrink_to_fit(); } +impl MirPassC for SimplifyCfg { + const OPT_LEVEL: u32 = 0; // TODO: Only run if previous pass runs. +} + impl<'tcx> MirPass<'tcx> for SimplifyCfg { fn name(&self) -> Cow<'_, str> { Cow::Owned(format!("SimplifyCfg-{}", self.label))