Refactor tracking of writes.

This commit is contained in:
Camille GILLOT 2023-03-07 15:20:57 +00:00
parent 9928d0e566
commit d97a7ce69b
2 changed files with 58 additions and 72 deletions

View File

@ -4,7 +4,6 @@
use either::Right; use either::Right;
use rustc_const_eval::const_eval::CheckAlignment; use rustc_const_eval::const_eval::CheckAlignment;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::DefKind; use rustc_hir::def::DefKind;
use rustc_index::bit_set::BitSet; use rustc_index::bit_set::BitSet;
use rustc_index::vec::IndexVec; use rustc_index::vec::IndexVec;
@ -152,24 +151,12 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
pub struct ConstPropMachine<'mir, 'tcx> { pub struct ConstPropMachine<'mir, 'tcx> {
/// The virtual call stack. /// The virtual call stack.
stack: Vec<Frame<'mir, 'tcx>>, 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>, pub can_const_prop: IndexVec<Local, ConstPropMode>,
} }
impl ConstPropMachine<'_, '_> { impl ConstPropMachine<'_, '_> {
pub fn new( pub fn new(can_const_prop: IndexVec<Local, ConstPropMode>) -> Self {
only_propagate_inside_block_locals: BitSet<Local>, Self { stack: Vec::new(), can_const_prop }
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,
}
} }
} }
@ -255,16 +242,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
frame: usize, frame: usize,
local: Local, local: Local,
) -> InterpResult<'tcx, &'a mut interpret::Operand<Self::Provenance>> { ) -> InterpResult<'tcx, &'a mut interpret::Operand<Self::Provenance>> {
if ecx.machine.can_const_prop[local] == ConstPropMode::NoPropagation { assert_eq!(frame, 0);
throw_machine_stop_str!("tried to write to a local that is marked as not propagatable") match ecx.machine.can_const_prop[local] {
} ConstPropMode::NoPropagation => {
if frame == 0 && ecx.machine.only_propagate_inside_block_locals.contains(local) { throw_machine_stop_str!(
trace!( "tried to write to a local that is marked as not propagatable"
"mutating local {:?} which is restricted to its block. \ )
Will remove it from const-prop after block is finished.", }
local ConstPropMode::OnlyInsideOwnBlock | ConstPropMode::FullConstProp => {}
);
ecx.machine.written_only_inside_own_block_locals.insert(local);
} }
ecx.machine.stack[frame].locals[local].access_mut() ecx.machine.stack[frame].locals[local].access_mut()
} }
@ -369,17 +354,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
let param_env = tcx.param_env_reveal_all_normalized(def_id); let param_env = tcx.param_env_reveal_all_normalized(def_id);
let can_const_prop = CanConstProp::check(tcx, param_env, body); 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( let mut ecx = InterpCx::new(
tcx, tcx,
tcx.def_span(def_id), tcx.def_span(def_id),
param_env, param_env,
ConstPropMachine::new(only_propagate_inside_block_locals, can_const_prop), ConstPropMachine::new(can_const_prop),
); );
let ret_layout = ecx let ret_layout = ecx
@ -977,26 +956,33 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) { fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) {
self.super_basic_block_data(block, data); self.super_basic_block_data(block, data);
let ensure_not_propagated = |this: &mut Self, local: Local| {
if cfg!(debug_assertions) {
assert!(
this.get_const(local.into()).is_none()
|| this
.layout_of(this.local_decls[local].ty)
.map_or(true, |layout| layout.is_zst()),
"failed to remove values for `{local:?}`, value={:?}",
this.get_const(local.into()),
)
}
};
// We remove all Locals which are restricted in propagation to their containing blocks and // We remove all Locals which are restricted in propagation to their containing blocks and
// which were modified in the current block. // 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`. // 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); let can_const_prop = std::mem::take(&mut self.ecx.machine.can_const_prop);
for &local in locals.iter() { for (local, &mode) in can_const_prop.iter_enumerated() {
Self::remove_const(&mut self.ecx, local); match mode {
} ConstPropMode::FullConstProp => {}
locals.clear(); ConstPropMode::NoPropagation => ensure_not_propagated(self, local),
// Put it back so we reuse the heap of the storage ConstPropMode::OnlyInsideOwnBlock => {
self.ecx.machine.written_only_inside_own_block_locals = locals; Self::remove_const(&mut self.ecx, local);
if cfg!(debug_assertions) { ensure_not_propagated(self, local);
// 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())
)
} }
} }
self.ecx.machine.can_const_prop = can_const_prop;
} }
} }

View File

@ -9,7 +9,6 @@ use rustc_const_eval::interpret::{
}; };
use rustc_hir::def::DefKind; use rustc_hir::def::DefKind;
use rustc_hir::HirId; use rustc_hir::HirId;
use rustc_index::bit_set::BitSet;
use rustc_index::vec::IndexVec; use rustc_index::vec::IndexVec;
use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::*; use rustc_middle::mir::*;
@ -179,17 +178,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
let param_env = tcx.param_env_reveal_all_normalized(def_id); let param_env = tcx.param_env_reveal_all_normalized(def_id);
let can_const_prop = CanConstProp::check(tcx, param_env, body); 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( let mut ecx = InterpCx::new(
tcx, tcx,
tcx.def_span(def_id), tcx.def_span(def_id),
param_env, param_env,
ConstPropMachine::new(only_propagate_inside_block_locals, can_const_prop), ConstPropMachine::new(can_const_prop),
); );
let ret_layout = ecx let ret_layout = ecx
@ -687,26 +680,33 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
fn visit_basic_block_data(&mut self, block: BasicBlock, data: &BasicBlockData<'tcx>) { fn visit_basic_block_data(&mut self, block: BasicBlock, data: &BasicBlockData<'tcx>) {
self.super_basic_block_data(block, data); self.super_basic_block_data(block, data);
let ensure_not_propagated = |this: &mut Self, local: Local| {
if cfg!(debug_assertions) {
assert!(
this.get_const(local.into()).is_none()
|| this
.layout_of(this.local_decls[local].ty)
.map_or(true, |layout| layout.is_zst()),
"failed to remove values for `{local:?}`, value={:?}",
this.get_const(local.into()),
)
}
};
// We remove all Locals which are restricted in propagation to their containing blocks and // We remove all Locals which are restricted in propagation to their containing blocks and
// which were modified in the current block. // 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`. // 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); let can_const_prop = std::mem::take(&mut self.ecx.machine.can_const_prop);
for &local in locals.iter() { for (local, &mode) in can_const_prop.iter_enumerated() {
Self::remove_const(&mut self.ecx, local); match mode {
} ConstPropMode::FullConstProp => {}
locals.clear(); ConstPropMode::NoPropagation => ensure_not_propagated(self, local),
// Put it back so we reuse the heap of the storage ConstPropMode::OnlyInsideOwnBlock => {
self.ecx.machine.written_only_inside_own_block_locals = locals; Self::remove_const(&mut self.ecx, local);
if cfg!(debug_assertions) { ensure_not_propagated(self, local);
// 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())
)
} }
} }
self.ecx.machine.can_const_prop = can_const_prop;
} }
} }