summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_mir_transform/src/const_prop.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_mir_transform/src/const_prop.rs')
-rw-r--r--compiler/rustc_mir_transform/src/const_prop.rs311
1 files changed, 132 insertions, 179 deletions
diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs
index 6b2eefce2..1bb45341e 100644
--- a/compiler/rustc_mir_transform/src/const_prop.rs
+++ b/compiler/rustc_mir_transform/src/const_prop.rs
@@ -1,15 +1,13 @@
//! Propagates constants for early reporting of statically known
//! assertion failures
-use std::cell::Cell;
-
use either::Right;
use rustc_const_eval::const_eval::CheckAlignment;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::DefKind;
use rustc_index::bit_set::BitSet;
-use rustc_index::vec::IndexVec;
+use rustc_index::vec::{IndexSlice, IndexVec};
use rustc_middle::mir::visit::{
MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor,
};
@@ -17,7 +15,7 @@ use rustc_middle::mir::*;
use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout};
use rustc_middle::ty::InternalSubsts;
use rustc_middle::ty::{self, ConstKind, Instance, ParamEnv, Ty, TyCtxt, TypeVisitableExt};
-use rustc_span::{def_id::DefId, Span};
+use rustc_span::{def_id::DefId, Span, DUMMY_SP};
use rustc_target::abi::{self, Align, HasDataLayout, Size, TargetDataLayout};
use rustc_target::spec::abi::Abi as CallAbi;
use rustc_trait_selection::traits;
@@ -25,8 +23,8 @@ use rustc_trait_selection::traits;
use crate::MirPass;
use rustc_const_eval::interpret::{
self, compile_time_machine, AllocId, ConstAllocation, ConstValue, CtfeValidationMode, Frame,
- ImmTy, Immediate, InterpCx, InterpResult, LocalState, LocalValue, MemoryKind, OpTy, PlaceTy,
- Pointer, Scalar, StackPopCleanup, StackPopUnwind,
+ ImmTy, Immediate, InterpCx, InterpResult, LocalValue, MemoryKind, OpTy, PlaceTy, Pointer,
+ Scalar, StackPopCleanup,
};
/// The maximum number of bytes that we'll allocate space for a local or the return value.
@@ -56,7 +54,7 @@ pub struct ConstProp;
impl<'tcx> MirPass<'tcx> for ConstProp {
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
- sess.mir_opt_level() >= 1
+ sess.mir_opt_level() >= 2
}
#[instrument(skip(self, tcx), level = "debug")]
@@ -117,10 +115,7 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
.predicates
.iter()
.filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
- if traits::impossible_predicates(
- tcx,
- traits::elaborate_predicates(tcx, predicates).map(|o| o.predicate).collect(),
- ) {
+ if traits::impossible_predicates(tcx, traits::elaborate(tcx, predicates).collect()) {
trace!("ConstProp skipped for {:?}: found unsatisfiable predicates", def_id);
return;
}
@@ -129,7 +124,7 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
let dummy_body = &Body::new(
body.source,
- (*body.basic_blocks).clone(),
+ (*body.basic_blocks).to_owned(),
body.source_scopes.clone(),
body.local_decls.clone(),
Default::default(),
@@ -154,22 +149,15 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
pub struct ConstPropMachine<'mir, 'tcx> {
/// The virtual call stack.
stack: Vec<Frame<'mir, 'tcx>>,
- /// `OnlyInsideOwnBlock` locals that were written in the current block get erased at the end.
pub written_only_inside_own_block_locals: FxHashSet<Local>,
- /// Locals that need to be cleared after every block terminates.
- pub only_propagate_inside_block_locals: BitSet<Local>,
pub can_const_prop: IndexVec<Local, ConstPropMode>,
}
impl ConstPropMachine<'_, '_> {
- pub fn new(
- only_propagate_inside_block_locals: BitSet<Local>,
- can_const_prop: IndexVec<Local, ConstPropMode>,
- ) -> Self {
+ pub fn new(can_const_prop: IndexVec<Local, ConstPropMode>) -> Self {
Self {
stack: Vec::new(),
written_only_inside_own_block_locals: Default::default(),
- only_propagate_inside_block_locals,
can_const_prop,
}
}
@@ -189,7 +177,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
}
#[inline(always)]
- fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
+ fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>, _layout: TyAndLayout<'tcx>) -> bool {
false // for now, we don't enforce validity
}
fn alignment_check_failed(
@@ -218,7 +206,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
_args: &[OpTy<'tcx>],
_destination: &PlaceTy<'tcx>,
_target: Option<BasicBlock>,
- _unwind: StackPopUnwind,
+ _unwind: UnwindAction,
) -> InterpResult<'tcx, Option<(&'mir Body<'tcx>, ty::Instance<'tcx>)>> {
Ok(None)
}
@@ -229,7 +217,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
_args: &[OpTy<'tcx>],
_destination: &PlaceTy<'tcx>,
_target: Option<BasicBlock>,
- _unwind: StackPopUnwind,
+ _unwind: UnwindAction,
) -> InterpResult<'tcx> {
throw_machine_stop_str!("calling intrinsics isn't supported in ConstProp")
}
@@ -237,7 +225,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
fn assert_panic(
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
_msg: &rustc_middle::mir::AssertMessage<'tcx>,
- _unwind: Option<rustc_middle::mir::BasicBlock>,
+ _unwind: rustc_middle::mir::UnwindAction,
) -> InterpResult<'tcx> {
bug!("panics terminators are not evaluated in ConstProp")
}
@@ -257,16 +245,17 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
frame: usize,
local: Local,
) -> InterpResult<'tcx, &'a mut interpret::Operand<Self::Provenance>> {
- 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);
+ assert_eq!(frame, 0);
+ match ecx.machine.can_const_prop[local] {
+ ConstPropMode::NoPropagation => {
+ throw_machine_stop_str!(
+ "tried to write to a local that is marked as not propagatable"
+ )
+ }
+ ConstPropMode::OnlyInsideOwnBlock => {
+ ecx.machine.written_only_inside_own_block_locals.insert(local);
+ }
+ ConstPropMode::FullConstProp => {}
}
ecx.machine.stack[frame].locals[local].access_mut()
}
@@ -327,10 +316,7 @@ struct ConstPropagator<'mir, 'tcx> {
ecx: InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>,
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
- local_decls: &'mir IndexVec<Local, LocalDecl<'tcx>>,
- // Because we have `MutVisitor` we can't obtain the `SourceInfo` from a `Location`. So we store
- // the last known `SourceInfo` here and just keep revisiting it.
- source_info: Option<SourceInfo>,
+ local_decls: &'mir IndexSlice<Local, LocalDecl<'tcx>>,
}
impl<'tcx> LayoutOfHelpers<'tcx> for ConstPropagator<'_, 'tcx> {
@@ -374,17 +360,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
let param_env = tcx.param_env_reveal_all_normalized(def_id);
let can_const_prop = CanConstProp::check(tcx, param_env, body);
- let mut only_propagate_inside_block_locals = BitSet::new_empty(can_const_prop.len());
- for (l, mode) in can_const_prop.iter_enumerated() {
- if *mode == ConstPropMode::OnlyInsideOwnBlock {
- only_propagate_inside_block_locals.insert(l);
- }
- }
let mut ecx = InterpCx::new(
tcx,
tcx.def_span(def_id),
param_env,
- ConstPropMachine::new(only_propagate_inside_block_locals, can_const_prop),
+ ConstPropMachine::new(can_const_prop),
);
let ret_layout = ecx
@@ -411,13 +391,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
)
.expect("failed to push initial stack frame");
- ConstPropagator {
- ecx,
- tcx,
- param_env,
- local_decls: &dummy_body.local_decls,
- source_info: None,
- }
+ ConstPropagator { ecx, tcx, param_env, local_decls: &dummy_body.local_decls }
}
fn get_const(&self, place: Place<'tcx>) -> Option<OpTy<'tcx>> {
@@ -446,10 +420,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
/// Remove `local` from the pool of `Locals`. Allows writing to them,
/// but not reading from them anymore.
fn remove_const(ecx: &mut InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>, local: Local) {
- ecx.frame_mut().locals[local] = LocalState {
- value: LocalValue::Live(interpret::Operand::Immediate(interpret::Immediate::Uninit)),
- layout: Cell::new(None),
- };
+ ecx.frame_mut().locals[local].value =
+ LocalValue::Live(interpret::Operand::Immediate(interpret::Immediate::Uninit));
+ ecx.machine.written_only_inside_own_block_locals.remove(&local);
}
/// Returns the value, if any, of evaluating `c`.
@@ -492,11 +465,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
scalar,
)) = *value
{
- *operand = self.operand_from_scalar(
- scalar,
- value.layout.ty,
- self.source_info.unwrap().span,
- );
+ *operand = self.operand_from_scalar(scalar, value.layout.ty);
}
}
}
@@ -504,7 +473,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
}
}
- fn const_prop(&mut self, rvalue: &Rvalue<'tcx>, place: Place<'tcx>) -> Option<()> {
+ fn check_rvalue(&mut self, rvalue: &Rvalue<'tcx>) -> Option<()> {
// Perform any special handling for specific Rvalue types.
// Generally, checks here fall into one of two categories:
// 1. Additional checking to provide useful lints to the user
@@ -532,6 +501,15 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
return None;
}
+ // Do not try creating references, nor any types with potentially-complex
+ // invariants. This avoids an issue where checking validity would do a
+ // bunch of work generating a nice message about the invariant violation,
+ // only to not show it to anyone (since this isn't the lint).
+ Rvalue::Cast(CastKind::Transmute, op, dst_ty) if !dst_ty.is_primitive() => {
+ trace!("skipping Transmute of {:?} to {:?}", op, dst_ty);
+
+ return None;
+ }
// There's no other checking to do at this time.
Rvalue::Aggregate(..)
@@ -561,7 +539,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
return None;
}
- self.eval_rvalue_with_identities(rvalue, place)
+ Some(())
}
// Attempt to use algebraic identities to eliminate constant expressions
@@ -621,20 +599,24 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
}
/// Creates a new `Operand::Constant` from a `Scalar` value
- fn operand_from_scalar(&self, scalar: Scalar, ty: Ty<'tcx>, span: Span) -> Operand<'tcx> {
+ fn operand_from_scalar(&self, scalar: Scalar, ty: Ty<'tcx>) -> Operand<'tcx> {
Operand::Constant(Box::new(Constant {
- span,
+ span: DUMMY_SP,
user_ty: None,
literal: ConstantKind::from_scalar(self.tcx, scalar, ty),
}))
}
- fn replace_with_const(
- &mut self,
- rval: &mut Rvalue<'tcx>,
- value: &OpTy<'tcx>,
- source_info: SourceInfo,
- ) {
+ fn replace_with_const(&mut self, place: Place<'tcx>, rval: &mut Rvalue<'tcx>) {
+ // This will return None if the above `const_prop` invocation only "wrote" a
+ // type whose creation requires no write. E.g. a generator whose initial state
+ // consists solely of uninitialized memory (so it doesn't capture any locals).
+ let Some(ref value) = self.get_const(place) else { return };
+ if !self.should_const_prop(value) {
+ return;
+ }
+ trace!("replacing {:?}={:?} with {:?}", place, rval, value);
+
if let Rvalue::Use(Operand::Constant(c)) = rval {
match c.literal {
ConstantKind::Ty(c) if matches!(c.kind(), ConstKind::Unevaluated(..)) => {}
@@ -664,11 +646,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
if let Some(Right(imm)) = imm {
match *imm {
interpret::Immediate::Scalar(scalar) => {
- *rval = Rvalue::Use(self.operand_from_scalar(
- scalar,
- value.layout.ty,
- source_info.span,
- ));
+ *rval = Rvalue::Use(self.operand_from_scalar(scalar, value.layout.ty));
}
Immediate::ScalarPair(..) => {
// Found a value represented as a pair. For now only do const-prop if the type
@@ -701,7 +679,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
let const_val = ConstValue::ByRef { alloc, offset: Size::ZERO };
let literal = ConstantKind::Val(const_val, ty);
*rval = Rvalue::Use(Operand::Constant(Box::new(Constant {
- span: source_info.span,
+ span: DUMMY_SP,
user_ty: None,
literal,
})));
@@ -730,6 +708,19 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
_ => false,
}
}
+
+ fn ensure_not_propagated(&self, local: Local) {
+ if cfg!(debug_assertions) {
+ assert!(
+ self.get_const(local.into()).is_none()
+ || self
+ .layout_of(self.local_decls[local].ty)
+ .map_or(true, |layout| layout.is_zst()),
+ "failed to remove values for `{local:?}`, value={:?}",
+ self.get_const(local.into()),
+ )
+ }
+ }
}
/// The mode that `ConstProp` is allowed to run in for a given `Local`.
@@ -739,8 +730,6 @@ pub enum ConstPropMode {
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,
@@ -750,8 +739,6 @@ pub struct CanConstProp {
can_const_prop: IndexVec<Local, ConstPropMode>,
// False at the beginning. Once set, no more assignments are allowed to that local.
found_assignment: BitSet<Local>,
- // Cache of locals' information
- local_kinds: IndexVec<Local, LocalKind>,
}
impl CanConstProp {
@@ -764,10 +751,6 @@ impl CanConstProp {
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;
@@ -780,24 +763,10 @@ impl CanConstProp {
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
- );
- }
+ }
+ // Consider that arguments are assigned on entry.
+ for arg in body.args_iter() {
+ cpv.found_assignment.insert(arg);
}
cpv.visit_body(&body);
cpv.can_const_prop
@@ -827,7 +796,6 @@ impl Visitor<'_> for CanConstProp {
// states as applicable.
ConstPropMode::OnlyInsideOwnBlock => {}
ConstPropMode::NoPropagation => {}
- ConstPropMode::OnlyPropagateInto => {}
other @ ConstPropMode::FullConstProp => {
trace!(
"local {:?} can't be propagated because of multiple assignments. Previous state: {:?}",
@@ -854,7 +822,6 @@ impl Visitor<'_> for CanConstProp {
// mutation.
| NonMutatingUse(NonMutatingUseContext::SharedBorrow)
| NonMutatingUse(NonMutatingUseContext::ShallowBorrow)
- | NonMutatingUse(NonMutatingUseContext::UniqueBorrow)
| NonMutatingUse(NonMutatingUseContext::AddressOf)
| MutatingUse(MutatingUseContext::Borrow)
| MutatingUse(MutatingUseContext::AddressOf) => {
@@ -886,48 +853,23 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
}
}
- fn visit_constant(&mut self, constant: &mut Constant<'tcx>, location: Location) {
- trace!("visit_constant: {:?}", constant);
- self.super_constant(constant, location);
- self.eval_constant(constant);
- }
+ fn visit_assign(
+ &mut self,
+ place: &mut Place<'tcx>,
+ rvalue: &mut Rvalue<'tcx>,
+ location: Location,
+ ) {
+ self.super_assign(place, rvalue, location);
- fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) {
- trace!("visit_statement: {:?}", statement);
- let source_info = statement.source_info;
- self.source_info = Some(source_info);
- match statement.kind {
- StatementKind::Assign(box (place, ref mut rval)) => {
- let can_const_prop = self.ecx.machine.can_const_prop[place.local];
- if let Some(()) = self.const_prop(rval, place) {
- // This will return None if the above `const_prop` invocation only "wrote" a
- // type whose creation requires no write. E.g. a generator whose initial state
- // consists solely of uninitialized memory (so it doesn't capture any locals).
- if let Some(ref value) = self.get_const(place) && self.should_const_prop(value) {
- trace!("replacing {:?} with {:?}", rval, value);
- self.replace_with_const(rval, value, source_info);
- if can_const_prop == ConstPropMode::FullConstProp
- || can_const_prop == ConstPropMode::OnlyInsideOwnBlock
- {
- trace!("propagated into {:?}", place);
- }
- }
- match can_const_prop {
- ConstPropMode::OnlyInsideOwnBlock => {
- trace!(
- "found local restricted to its block. \
- Will remove it from const-prop after block is finished. Local: {:?}",
- place.local
- );
- }
- ConstPropMode::OnlyPropagateInto | ConstPropMode::NoPropagation => {
- trace!("can't propagate into {:?}", place);
- if place.local != RETURN_PLACE {
- Self::remove_const(&mut self.ecx, place.local);
- }
- }
- ConstPropMode::FullConstProp => {}
- }
+ let Some(()) = self.check_rvalue(rvalue) else { return };
+
+ match self.ecx.machine.can_const_prop[place.local] {
+ // Do nothing if the place is indirect.
+ _ if place.is_indirect() => {}
+ ConstPropMode::NoPropagation => self.ensure_not_propagated(place.local),
+ ConstPropMode::OnlyInsideOwnBlock | ConstPropMode::FullConstProp => {
+ if let Some(()) = self.eval_rvalue_with_identities(rvalue, *place) {
+ self.replace_with_const(*place, rvalue);
} else {
// Const prop failed, so erase the destination, ensuring that whatever happens
// from here on, does not know about the previous value.
@@ -947,8 +889,22 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
Self::remove_const(&mut self.ecx, place.local);
}
}
+ }
+ }
+
+ fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) {
+ trace!("visit_statement: {:?}", statement);
+
+ // We want to evaluate operands before any change to the assigned-to value,
+ // so we recurse first.
+ self.super_statement(statement, location);
+
+ match statement.kind {
StatementKind::SetDiscriminant { ref place, .. } => {
match self.ecx.machine.can_const_prop[place.local] {
+ // Do nothing if the place is indirect.
+ _ if place.is_indirect() => {}
+ ConstPropMode::NoPropagation => self.ensure_not_propagated(place.local),
ConstPropMode::FullConstProp | ConstPropMode::OnlyInsideOwnBlock => {
if self.ecx.statement(statement).is_ok() {
trace!("propped discriminant into {:?}", place);
@@ -956,28 +912,22 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
Self::remove_const(&mut self.ecx, place.local);
}
}
- ConstPropMode::OnlyPropagateInto | ConstPropMode::NoPropagation => {
- Self::remove_const(&mut self.ecx, place.local);
- }
}
}
- StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => {
+ StatementKind::StorageLive(local) => {
let frame = self.ecx.frame_mut();
- frame.locals[local].value = if let StatementKind::StorageLive(_) = statement.kind {
- LocalValue::Live(interpret::Operand::Immediate(interpret::Immediate::Uninit))
- } else {
- LocalValue::Dead
- };
+ frame.locals[local].value =
+ LocalValue::Live(interpret::Operand::Immediate(interpret::Immediate::Uninit));
+ }
+ StatementKind::StorageDead(local) => {
+ let frame = self.ecx.frame_mut();
+ frame.locals[local].value = LocalValue::Dead;
}
_ => {}
}
-
- self.super_statement(statement, location);
}
fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) {
- let source_info = terminator.source_info;
- self.source_info = Some(source_info);
self.super_terminator(terminator, location);
match &mut terminator.kind {
@@ -987,11 +937,7 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
&& self.should_const_prop(value)
{
trace!("assertion on {:?} should be {:?}", value, expected);
- *cond = self.operand_from_scalar(
- value_const,
- self.tcx.types.bool,
- source_info.span,
- );
+ *cond = self.operand_from_scalar(value_const, self.tcx.types.bool);
}
}
TerminatorKind::SwitchInt { ref mut discr, .. } => {
@@ -1003,11 +949,10 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
// None of these have Operands to const-propagate.
TerminatorKind::Goto { .. }
| TerminatorKind::Resume
- | TerminatorKind::Abort
+ | TerminatorKind::Terminate
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::Drop { .. }
- | TerminatorKind::DropAndReplace { .. }
| TerminatorKind::Yield { .. }
| TerminatorKind::GeneratorDrop
| TerminatorKind::FalseEdge { .. }
@@ -1027,22 +972,30 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
// We remove all Locals which are restricted in propagation to their containing blocks and
// which were modified in the current block.
// Take it out of the ecx so we can get a mutable reference to the ecx for `remove_const`.
- let mut locals = std::mem::take(&mut self.ecx.machine.written_only_inside_own_block_locals);
- for &local in locals.iter() {
+ let mut written_only_inside_own_block_locals =
+ std::mem::take(&mut self.ecx.machine.written_only_inside_own_block_locals);
+
+ // This loop can get very hot for some bodies: it check each local in each bb.
+ // To avoid this quadratic behaviour, we only clear the locals that were modified inside
+ // the current block.
+ for local in written_only_inside_own_block_locals.drain() {
+ debug_assert_eq!(
+ self.ecx.machine.can_const_prop[local],
+ ConstPropMode::OnlyInsideOwnBlock
+ );
Self::remove_const(&mut self.ecx, local);
}
- locals.clear();
- // Put it back so we reuse the heap of the storage
- self.ecx.machine.written_only_inside_own_block_locals = locals;
+ self.ecx.machine.written_only_inside_own_block_locals =
+ written_only_inside_own_block_locals;
+
if cfg!(debug_assertions) {
- // Ensure we are correctly erasing locals with the non-debug-assert logic.
- for local in self.ecx.machine.only_propagate_inside_block_locals.iter() {
- assert!(
- self.get_const(local.into()).is_none()
- || self
- .layout_of(self.local_decls[local].ty)
- .map_or(true, |layout| layout.is_zst())
- )
+ for (local, &mode) in self.ecx.machine.can_const_prop.iter_enumerated() {
+ match mode {
+ ConstPropMode::FullConstProp => {}
+ ConstPropMode::NoPropagation | ConstPropMode::OnlyInsideOwnBlock => {
+ self.ensure_not_propagated(local);
+ }
+ }
}
}
}