diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs
index f6484a9b54d3c..85ad6b8f2feff 100644
--- a/compiler/rustc_mir_transform/src/const_prop.rs
+++ b/compiler/rustc_mir_transform/src/const_prop.rs
@@ -155,18 +155,18 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
}
}
-struct ConstPropMachine<'mir, 'tcx> {
+pub struct ConstPropMachine<'mir, 'tcx> {
/// The virtual call stack.
stack: Vec>,
/// `OnlyInsideOwnBlock` locals that were written in the current block get erased at the end.
- written_only_inside_own_block_locals: FxHashSet,
+ pub written_only_inside_own_block_locals: FxHashSet,
/// Locals that need to be cleared after every block terminates.
- only_propagate_inside_block_locals: BitSet,
- can_const_prop: IndexVec,
+ pub only_propagate_inside_block_locals: BitSet,
+ pub can_const_prop: IndexVec,
}
impl ConstPropMachine<'_, '_> {
- fn new(
+ pub fn new(
only_propagate_inside_block_locals: BitSet,
can_const_prop: IndexVec,
) -> Self {
@@ -816,7 +816,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
/// The mode that `ConstProp` is allowed to run in for a given `Local`.
#[derive(Clone, Copy, Debug, PartialEq)]
-enum ConstPropMode {
+pub enum ConstPropMode {
/// The `Local` can be propagated into and reads of this `Local` can also be propagated.
FullConstProp,
/// The `Local` can only be propagated into and from its own block.
@@ -828,7 +828,7 @@ enum ConstPropMode {
NoPropagation,
}
-struct CanConstProp {
+pub struct CanConstProp {
can_const_prop: IndexVec,
// False at the beginning. Once set, no more assignments are allowed to that local.
found_assignment: BitSet,
@@ -838,7 +838,7 @@ struct CanConstProp {
impl CanConstProp {
/// Returns true if `local` can be propagated
- fn check<'tcx>(
+ pub fn check<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
body: &Body<'tcx>,
diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs
index 97b1433f5d27a..3ae6a88a140ea 100644
--- a/compiler/rustc_mir_transform/src/const_prop_lint.rs
+++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs
@@ -1,19 +1,24 @@
//! Propagates constants for early reporting of statically known
//! assertion failures
-use std::cell::Cell;
-
-use rustc_ast::Mutability;
-use rustc_data_structures::fx::FxHashSet;
+use crate::const_prop::CanConstProp;
+use crate::const_prop::ConstPropMachine;
+use crate::const_prop::ConstPropMode;
+use crate::MirLint;
+use rustc_const_eval::const_eval::ConstEvalErr;
+use rustc_const_eval::interpret::{
+ self, InterpCx, InterpResult, LocalState, LocalValue, MemoryKind, OpTy, Scalar,
+ ScalarMaybeUninit, StackPopCleanup,
+};
use rustc_hir::def::DefKind;
use rustc_hir::HirId;
use rustc_index::bit_set::BitSet;
use rustc_index::vec::IndexVec;
-use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
+use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::{
- AssertKind, BasicBlock, BinOp, Body, Constant, ConstantKind, Local, LocalDecl, LocalKind,
- Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData, Statement,
- StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE,
+ AssertKind, BinOp, Body, Constant, ConstantKind, Local, LocalDecl, Location, Operand, Place,
+ Rvalue, SourceInfo, SourceScope, SourceScopeData, Statement, StatementKind, Terminator,
+ TerminatorKind, UnOp, RETURN_PLACE,
};
use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout};
use rustc_middle::ty::subst::{InternalSubsts, Subst};
@@ -22,42 +27,15 @@ use rustc_middle::ty::{
TypeVisitable,
};
use rustc_session::lint;
-use rustc_span::{def_id::DefId, Span};
+use rustc_span::Span;
use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout};
-use rustc_target::spec::abi::Abi as CallAbi;
use rustc_trait_selection::traits;
-
-use crate::MirLint;
-use rustc_const_eval::const_eval::ConstEvalErr;
-use rustc_const_eval::interpret::{
- self, compile_time_machine, AllocId, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult,
- LocalState, LocalValue, MemoryKind, OpTy, PlaceTy, Pointer, Scalar, ScalarMaybeUninit,
- StackPopCleanup, StackPopUnwind,
-};
+use std::cell::Cell;
/// The maximum number of bytes that we'll allocate space for a local or the return value.
/// Needed for #66397, because otherwise we eval into large places and that can cause OOM or just
/// Severely regress performance.
const MAX_ALLOC_LIMIT: u64 = 1024;
-
-/// Macro for machine-specific `InterpError` without allocation.
-/// (These will never be shown to the user, but they help diagnose ICEs.)
-macro_rules! throw_machine_stop_str {
- ($($tt:tt)*) => {{
- // We make a new local type for it. The type itself does not carry any information,
- // but its vtable (for the `MachineStopType` trait) does.
- struct Zst;
- // Printing this type shows the desired string.
- impl std::fmt::Display for Zst {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, $($tt)*)
- }
- }
- impl rustc_middle::mir::interpret::MachineStopType for Zst {}
- throw_machine_stop!(Zst)
- }};
-}
-
pub struct ConstProp;
impl<'tcx> MirLint<'tcx> for ConstProp {
@@ -151,172 +129,6 @@ impl<'tcx> MirLint<'tcx> for ConstProp {
}
}
-struct ConstPropMachine<'mir, 'tcx> {
- /// The virtual call stack.
- stack: Vec>,
- /// `OnlyInsideOwnBlock` locals that were written in the current block get erased at the end.
- written_only_inside_own_block_locals: FxHashSet,
- /// Locals that need to be cleared after every block terminates.
- only_propagate_inside_block_locals: BitSet,
- can_const_prop: IndexVec,
-}
-
-impl ConstPropMachine<'_, '_> {
- fn new(
- only_propagate_inside_block_locals: BitSet,
- can_const_prop: IndexVec,
- ) -> Self {
- Self {
- stack: Vec::new(),
- written_only_inside_own_block_locals: Default::default(),
- only_propagate_inside_block_locals,
- can_const_prop,
- }
- }
-}
-
-impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> {
- compile_time_machine!(<'mir, 'tcx>);
- const PANIC_ON_ALLOC_FAIL: bool = true; // all allocations are small (see `MAX_ALLOC_LIMIT`)
-
- type MemoryKind = !;
-
- fn load_mir(
- _ecx: &InterpCx<'mir, 'tcx, Self>,
- _instance: ty::InstanceDef<'tcx>,
- ) -> InterpResult<'tcx, &'tcx Body<'tcx>> {
- throw_machine_stop_str!("calling functions isn't supported in ConstProp")
- }
-
- fn find_mir_or_eval_fn(
- _ecx: &mut InterpCx<'mir, 'tcx, Self>,
- _instance: ty::Instance<'tcx>,
- _abi: CallAbi,
- _args: &[OpTy<'tcx>],
- _destination: &PlaceTy<'tcx>,
- _target: Option,
- _unwind: StackPopUnwind,
- ) -> InterpResult<'tcx, Option<(&'mir Body<'tcx>, ty::Instance<'tcx>)>> {
- Ok(None)
- }
-
- fn call_intrinsic(
- _ecx: &mut InterpCx<'mir, 'tcx, Self>,
- _instance: ty::Instance<'tcx>,
- _args: &[OpTy<'tcx>],
- _destination: &PlaceTy<'tcx>,
- _target: Option,
- _unwind: StackPopUnwind,
- ) -> InterpResult<'tcx> {
- throw_machine_stop_str!("calling intrinsics isn't supported in ConstProp")
- }
-
- fn assert_panic(
- _ecx: &mut InterpCx<'mir, 'tcx, Self>,
- _msg: &rustc_middle::mir::AssertMessage<'tcx>,
- _unwind: Option,
- ) -> InterpResult<'tcx> {
- bug!("panics terminators are not evaluated in ConstProp")
- }
-
- fn binary_ptr_op(
- _ecx: &InterpCx<'mir, 'tcx, Self>,
- _bin_op: BinOp,
- _left: &ImmTy<'tcx>,
- _right: &ImmTy<'tcx>,
- ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> {
- // We can't do this because aliasing of memory can differ between const eval and llvm
- throw_machine_stop_str!("pointer arithmetic or comparisons aren't supported in ConstProp")
- }
-
- fn access_local<'a>(
- frame: &'a Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>,
- local: Local,
- ) -> InterpResult<'tcx, &'a interpret::Operand> {
- let l = &frame.locals[local];
-
- if matches!(
- l.value,
- LocalValue::Live(interpret::Operand::Immediate(interpret::Immediate::Uninit))
- ) {
- // For us "uninit" means "we don't know its value, might be initiailized or not".
- // So stop here.
- throw_machine_stop_str!("tried to access a local with unknown value")
- }
-
- l.access()
- }
-
- fn access_local_mut<'a>(
- ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
- frame: usize,
- local: Local,
- ) -> InterpResult<'tcx, &'a mut interpret::Operand> {
- if ecx.machine.can_const_prop[local] == ConstPropMode::NoPropagation {
- throw_machine_stop_str!("tried to write to a local that is marked as not propagatable")
- }
- if frame == 0 && ecx.machine.only_propagate_inside_block_locals.contains(local) {
- trace!(
- "mutating local {:?} which is restricted to its block. \
- Will remove it from const-prop after block is finished.",
- local
- );
- ecx.machine.written_only_inside_own_block_locals.insert(local);
- }
- ecx.machine.stack[frame].locals[local].access_mut()
- }
-
- fn before_access_global(
- _tcx: TyCtxt<'tcx>,
- _machine: &Self,
- _alloc_id: AllocId,
- alloc: ConstAllocation<'tcx, Self::Provenance, Self::AllocExtra>,
- _static_def_id: Option,
- is_write: bool,
- ) -> InterpResult<'tcx> {
- if is_write {
- throw_machine_stop_str!("can't write to global");
- }
- // If the static allocation is mutable, then we can't const prop it as its content
- // might be different at runtime.
- if alloc.inner().mutability == Mutability::Mut {
- throw_machine_stop_str!("can't access mutable globals in ConstProp");
- }
-
- Ok(())
- }
-
- #[inline(always)]
- fn expose_ptr(
- _ecx: &mut InterpCx<'mir, 'tcx, Self>,
- _ptr: Pointer,
- ) -> InterpResult<'tcx> {
- throw_machine_stop_str!("exposing pointers isn't supported in ConstProp")
- }
-
- #[inline(always)]
- fn init_frame_extra(
- _ecx: &mut InterpCx<'mir, 'tcx, Self>,
- frame: Frame<'mir, 'tcx>,
- ) -> InterpResult<'tcx, Frame<'mir, 'tcx>> {
- Ok(frame)
- }
-
- #[inline(always)]
- fn stack<'a>(
- ecx: &'a InterpCx<'mir, 'tcx, Self>,
- ) -> &'a [Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] {
- &ecx.machine.stack
- }
-
- #[inline(always)]
- fn stack_mut<'a>(
- ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
- ) -> &'a mut Vec> {
- &mut ecx.machine.stack
- }
-}
-
/// Finds optimization opportunities on the MIR.
struct ConstPropagator<'mir, 'tcx> {
ecx: InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>,
@@ -711,139 +523,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
}
}
-/// The mode that `ConstProp` is allowed to run in for a given `Local`.
-#[derive(Clone, Copy, Debug, PartialEq)]
-enum ConstPropMode {
- /// The `Local` can be propagated into and reads of this `Local` can also be propagated.
- FullConstProp,
- /// The `Local` can only be propagated into and from its own block.
- OnlyInsideOwnBlock,
- /// The `Local` can be propagated into but reads cannot be propagated.
- OnlyPropagateInto,
- /// The `Local` cannot be part of propagation at all. Any statement
- /// referencing it either for reading or writing will not get propagated.
- NoPropagation,
-}
-
-struct CanConstProp {
- can_const_prop: IndexVec,
- // False at the beginning. Once set, no more assignments are allowed to that local.
- found_assignment: BitSet,
- // Cache of locals' information
- local_kinds: IndexVec,
-}
-
-impl CanConstProp {
- /// Returns true if `local` can be propagated
- fn check<'tcx>(
- tcx: TyCtxt<'tcx>,
- param_env: ParamEnv<'tcx>,
- body: &Body<'tcx>,
- ) -> IndexVec {
- let mut cpv = CanConstProp {
- can_const_prop: IndexVec::from_elem(ConstPropMode::FullConstProp, &body.local_decls),
- found_assignment: BitSet::new_empty(body.local_decls.len()),
- local_kinds: IndexVec::from_fn_n(
- |local| body.local_kind(local),
- body.local_decls.len(),
- ),
- };
- for (local, val) in cpv.can_const_prop.iter_enumerated_mut() {
- let ty = body.local_decls[local].ty;
- match tcx.layout_of(param_env.and(ty)) {
- Ok(layout) if layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) => {}
- // Either the layout fails to compute, then we can't use this local anyway
- // or the local is too large, then we don't want to.
- _ => {
- *val = ConstPropMode::NoPropagation;
- continue;
- }
- }
- // Cannot use args at all
- // Cannot use locals because if x < y { y - x } else { x - y } would
- // lint for x != y
- // FIXME(oli-obk): lint variables until they are used in a condition
- // FIXME(oli-obk): lint if return value is constant
- if cpv.local_kinds[local] == LocalKind::Arg {
- *val = ConstPropMode::OnlyPropagateInto;
- trace!(
- "local {:?} can't be const propagated because it's a function argument",
- local
- );
- } else if cpv.local_kinds[local] == LocalKind::Var {
- *val = ConstPropMode::OnlyInsideOwnBlock;
- trace!(
- "local {:?} will only be propagated inside its block, because it's a user variable",
- local
- );
- }
- }
- cpv.visit_body(&body);
- cpv.can_const_prop
- }
-}
-
-impl Visitor<'_> for CanConstProp {
- fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) {
- use rustc_middle::mir::visit::PlaceContext::*;
- match context {
- // Projections are fine, because `&mut foo.x` will be caught by
- // `MutatingUseContext::Borrow` elsewhere.
- MutatingUse(MutatingUseContext::Projection)
- // These are just stores, where the storing is not propagatable, but there may be later
- // mutations of the same local via `Store`
- | MutatingUse(MutatingUseContext::Call)
- | MutatingUse(MutatingUseContext::AsmOutput)
- | MutatingUse(MutatingUseContext::Deinit)
- // Actual store that can possibly even propagate a value
- | MutatingUse(MutatingUseContext::SetDiscriminant)
- | MutatingUse(MutatingUseContext::Store) => {
- if !self.found_assignment.insert(local) {
- match &mut self.can_const_prop[local] {
- // If the local can only get propagated in its own block, then we don't have
- // to worry about multiple assignments, as we'll nuke the const state at the
- // end of the block anyway, and inside the block we overwrite previous
- // states as applicable.
- ConstPropMode::OnlyInsideOwnBlock => {}
- ConstPropMode::NoPropagation => {}
- ConstPropMode::OnlyPropagateInto => {}
- other @ ConstPropMode::FullConstProp => {
- trace!(
- "local {:?} can't be propagated because of multiple assignments. Previous state: {:?}",
- local, other,
- );
- *other = ConstPropMode::OnlyInsideOwnBlock;
- }
- }
- }
- }
- // Reading constants is allowed an arbitrary number of times
- NonMutatingUse(NonMutatingUseContext::Copy)
- | NonMutatingUse(NonMutatingUseContext::Move)
- | NonMutatingUse(NonMutatingUseContext::Inspect)
- | NonMutatingUse(NonMutatingUseContext::Projection)
- | NonUse(_) => {}
-
- // These could be propagated with a smarter analysis or just some careful thinking about
- // whether they'd be fine right now.
- MutatingUse(MutatingUseContext::Yield)
- | MutatingUse(MutatingUseContext::Drop)
- | MutatingUse(MutatingUseContext::Retag)
- // These can't ever be propagated under any scheme, as we can't reason about indirect
- // mutation.
- | NonMutatingUse(NonMutatingUseContext::SharedBorrow)
- | NonMutatingUse(NonMutatingUseContext::ShallowBorrow)
- | NonMutatingUse(NonMutatingUseContext::UniqueBorrow)
- | NonMutatingUse(NonMutatingUseContext::AddressOf)
- | MutatingUse(MutatingUseContext::Borrow)
- | MutatingUse(MutatingUseContext::AddressOf) => {
- trace!("local {:?} can't be propagaged because it's used: {:?}", local, context);
- self.can_const_prop[local] = ConstPropMode::NoPropagation;
- }
- }
- }
-}
-
impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
fn visit_body(&mut self, body: &Body<'tcx>) {
for (bb, data) in body.basic_blocks().iter_enumerated() {