Separate checking rvalue from evaluation.
This commit is contained in:
parent
f00be8b77b
commit
b55c4f8312
2 changed files with 100 additions and 109 deletions
|
@ -470,7 +470,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.
|
// Perform any special handling for specific Rvalue types.
|
||||||
// Generally, checks here fall into one of two categories:
|
// Generally, checks here fall into one of two categories:
|
||||||
// 1. Additional checking to provide useful lints to the user
|
// 1. Additional checking to provide useful lints to the user
|
||||||
|
@ -527,7 +527,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.eval_rvalue_with_identities(rvalue, place)
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to use algebraic identities to eliminate constant expressions
|
// Attempt to use algebraic identities to eliminate constant expressions
|
||||||
|
@ -595,7 +595,16 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace_with_const(&mut self, rval: &mut Rvalue<'tcx>, value: &OpTy<'tcx>) {
|
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 {
|
if let Rvalue::Use(Operand::Constant(c)) = rval {
|
||||||
match c.literal {
|
match c.literal {
|
||||||
ConstantKind::Ty(c) if matches!(c.kind(), ConstKind::Unevaluated(..)) => {}
|
ConstantKind::Ty(c) if matches!(c.kind(), ConstKind::Unevaluated(..)) => {}
|
||||||
|
@ -688,6 +697,19 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ensure_not_propagated(&mut 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`.
|
/// The mode that `ConstProp` is allowed to run in for a given `Local`.
|
||||||
|
@ -827,44 +849,23 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
|
||||||
self.eval_constant(constant);
|
self.eval_constant(constant);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) {
|
fn visit_assign(
|
||||||
trace!("visit_statement: {:?}", statement);
|
&mut self,
|
||||||
|
place: &mut Place<'tcx>,
|
||||||
|
rvalue: &mut Rvalue<'tcx>,
|
||||||
|
location: Location,
|
||||||
|
) {
|
||||||
|
self.super_assign(place, rvalue, location);
|
||||||
|
|
||||||
// Recurse into statement before applying the assignment.
|
let Some(()) = self.check_rvalue(rvalue) else { return };
|
||||||
self.super_statement(statement, location);
|
|
||||||
|
|
||||||
match statement.kind {
|
match self.ecx.machine.can_const_prop[place.local] {
|
||||||
StatementKind::Assign(box (place, ref mut rval)) => {
|
// Do nothing if the place is indirect.
|
||||||
let can_const_prop = self.ecx.machine.can_const_prop[place.local];
|
_ if place.is_indirect() => {}
|
||||||
if let Some(()) = self.const_prop(rval, place) {
|
ConstPropMode::NoPropagation => self.ensure_not_propagated(place.local),
|
||||||
// This will return None if the above `const_prop` invocation only "wrote" a
|
ConstPropMode::OnlyInsideOwnBlock | ConstPropMode::FullConstProp => {
|
||||||
// type whose creation requires no write. E.g. a generator whose initial state
|
if let Some(()) = self.eval_rvalue_with_identities(rvalue, *place) {
|
||||||
// consists solely of uninitialized memory (so it doesn't capture any locals).
|
self.replace_with_const(*place, rvalue);
|
||||||
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);
|
|
||||||
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::NoPropagation => {
|
|
||||||
trace!("can't propagate into {:?}", place);
|
|
||||||
if place.local != RETURN_PLACE {
|
|
||||||
Self::remove_const(&mut self.ecx, place.local);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConstPropMode::FullConstProp => {}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Const prop failed, so erase the destination, ensuring that whatever happens
|
// Const prop failed, so erase the destination, ensuring that whatever happens
|
||||||
// from here on, does not know about the previous value.
|
// from here on, does not know about the previous value.
|
||||||
|
@ -884,8 +885,21 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
|
||||||
Self::remove_const(&mut self.ecx, place.local);
|
Self::remove_const(&mut self.ecx, place.local);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) {
|
||||||
|
trace!("visit_statement: {:?}", statement);
|
||||||
|
|
||||||
|
// Recurse into statement before applying the assignment.
|
||||||
|
self.super_statement(statement, location);
|
||||||
|
|
||||||
|
match statement.kind {
|
||||||
StatementKind::SetDiscriminant { ref place, .. } => {
|
StatementKind::SetDiscriminant { ref place, .. } => {
|
||||||
match self.ecx.machine.can_const_prop[place.local] {
|
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 => {
|
ConstPropMode::FullConstProp | ConstPropMode::OnlyInsideOwnBlock => {
|
||||||
if self.ecx.statement(statement).is_ok() {
|
if self.ecx.statement(statement).is_ok() {
|
||||||
trace!("propped discriminant into {:?}", place);
|
trace!("propped discriminant into {:?}", place);
|
||||||
|
@ -893,9 +907,6 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
|
||||||
Self::remove_const(&mut self.ecx, place.local);
|
Self::remove_const(&mut self.ecx, place.local);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ConstPropMode::NoPropagation => {
|
|
||||||
Self::remove_const(&mut self.ecx, place.local);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StatementKind::StorageLive(local) => {
|
StatementKind::StorageLive(local) => {
|
||||||
|
@ -958,19 +969,6 @@ 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`.
|
||||||
|
@ -978,10 +976,10 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
|
||||||
for (local, &mode) in can_const_prop.iter_enumerated() {
|
for (local, &mode) in can_const_prop.iter_enumerated() {
|
||||||
match mode {
|
match mode {
|
||||||
ConstPropMode::FullConstProp => {}
|
ConstPropMode::FullConstProp => {}
|
||||||
ConstPropMode::NoPropagation => ensure_not_propagated(self, local),
|
ConstPropMode::NoPropagation => self.ensure_not_propagated(local),
|
||||||
ConstPropMode::OnlyInsideOwnBlock => {
|
ConstPropMode::OnlyInsideOwnBlock => {
|
||||||
Self::remove_const(&mut self.ecx, local);
|
Self::remove_const(&mut self.ecx, local);
|
||||||
ensure_not_propagated(self, local);
|
self.ensure_not_propagated(local);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -405,12 +405,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn const_prop(
|
fn check_rvalue(&mut self, rvalue: &Rvalue<'tcx>, source_info: SourceInfo) -> Option<()> {
|
||||||
&mut self,
|
|
||||||
rvalue: &Rvalue<'tcx>,
|
|
||||||
source_info: SourceInfo,
|
|
||||||
place: Place<'tcx>,
|
|
||||||
) -> Option<()> {
|
|
||||||
// Perform any special handling for specific Rvalue types.
|
// Perform any special handling for specific Rvalue types.
|
||||||
// Generally, checks here fall into one of two categories:
|
// Generally, checks here fall into one of two categories:
|
||||||
// 1. Additional checking to provide useful lints to the user
|
// 1. Additional checking to provide useful lints to the user
|
||||||
|
@ -486,7 +481,20 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.use_ecx(source_info, |this| this.ecx.eval_rvalue_into_place(rvalue, place))
|
Some(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensure_not_propagated(&mut 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()),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -507,35 +515,21 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
|
||||||
self.eval_constant(constant, self.source_info.unwrap());
|
self.eval_constant(constant, self.source_info.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
|
fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) {
|
||||||
trace!("visit_statement: {:?}", statement);
|
self.super_assign(place, rvalue, location);
|
||||||
let source_info = statement.source_info;
|
|
||||||
self.source_info = Some(source_info);
|
|
||||||
|
|
||||||
// Recurse into statement before applying the assignment.
|
let source_info = self.source_info.unwrap();
|
||||||
self.super_statement(statement, location);
|
let Some(()) = self.check_rvalue(rvalue, source_info) else { return };
|
||||||
|
|
||||||
match statement.kind {
|
match self.ecx.machine.can_const_prop[place.local] {
|
||||||
StatementKind::Assign(box (place, ref rval)) => {
|
// Do nothing if the place is indirect.
|
||||||
let can_const_prop = self.ecx.machine.can_const_prop[place.local];
|
_ if place.is_indirect() => {}
|
||||||
if let Some(()) = self.const_prop(rval, source_info, place) {
|
ConstPropMode::NoPropagation => self.ensure_not_propagated(place.local),
|
||||||
match can_const_prop {
|
ConstPropMode::OnlyInsideOwnBlock | ConstPropMode::FullConstProp => {
|
||||||
ConstPropMode::OnlyInsideOwnBlock => {
|
if self
|
||||||
trace!(
|
.use_ecx(source_info, |this| this.ecx.eval_rvalue_into_place(rvalue, *place))
|
||||||
"found local restricted to its block. \
|
.is_none()
|
||||||
Will remove it from const-prop after block is finished. Local: {:?}",
|
{
|
||||||
place.local
|
|
||||||
);
|
|
||||||
}
|
|
||||||
ConstPropMode::NoPropagation => {
|
|
||||||
trace!("can't propagate into {:?}", place);
|
|
||||||
if place.local != RETURN_PLACE {
|
|
||||||
Self::remove_const(&mut self.ecx, place.local);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConstPropMode::FullConstProp => {}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Const prop failed, so erase the destination, ensuring that whatever happens
|
// Const prop failed, so erase the destination, ensuring that whatever happens
|
||||||
// from here on, does not know about the previous value.
|
// from here on, does not know about the previous value.
|
||||||
// This is important in case we have
|
// This is important in case we have
|
||||||
|
@ -554,8 +548,23 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
|
||||||
Self::remove_const(&mut self.ecx, place.local);
|
Self::remove_const(&mut self.ecx, place.local);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
|
||||||
|
trace!("visit_statement: {:?}", statement);
|
||||||
|
let source_info = statement.source_info;
|
||||||
|
self.source_info = Some(source_info);
|
||||||
|
|
||||||
|
// Recurse into statement before applying the assignment.
|
||||||
|
self.super_statement(statement, location);
|
||||||
|
|
||||||
|
match statement.kind {
|
||||||
StatementKind::SetDiscriminant { ref place, .. } => {
|
StatementKind::SetDiscriminant { ref place, .. } => {
|
||||||
match self.ecx.machine.can_const_prop[place.local] {
|
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 => {
|
ConstPropMode::FullConstProp | ConstPropMode::OnlyInsideOwnBlock => {
|
||||||
if self.use_ecx(source_info, |this| this.ecx.statement(statement)).is_some()
|
if self.use_ecx(source_info, |this| this.ecx.statement(statement)).is_some()
|
||||||
{
|
{
|
||||||
|
@ -564,9 +573,6 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
|
||||||
Self::remove_const(&mut self.ecx, place.local);
|
Self::remove_const(&mut self.ecx, place.local);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ConstPropMode::NoPropagation => {
|
|
||||||
Self::remove_const(&mut self.ecx, place.local);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StatementKind::StorageLive(local) => {
|
StatementKind::StorageLive(local) => {
|
||||||
|
@ -682,19 +688,6 @@ 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`.
|
||||||
|
@ -702,10 +695,10 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
|
||||||
for (local, &mode) in can_const_prop.iter_enumerated() {
|
for (local, &mode) in can_const_prop.iter_enumerated() {
|
||||||
match mode {
|
match mode {
|
||||||
ConstPropMode::FullConstProp => {}
|
ConstPropMode::FullConstProp => {}
|
||||||
ConstPropMode::NoPropagation => ensure_not_propagated(self, local),
|
ConstPropMode::NoPropagation => self.ensure_not_propagated(local),
|
||||||
ConstPropMode::OnlyInsideOwnBlock => {
|
ConstPropMode::OnlyInsideOwnBlock => {
|
||||||
Self::remove_const(&mut self.ecx, local);
|
Self::remove_const(&mut self.ecx, local);
|
||||||
ensure_not_propagated(self, local);
|
self.ensure_not_propagated(local);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue