Skip to content

Commit

Permalink
Auto merge of #71518 - felix91gr:const_prop_bugfix_just_block_prop, r…
Browse files Browse the repository at this point in the history
…=wesleywiser

Const-prop bugfix: only add propagation inside own block for user variables

A testing spinoff of #71298. This one only adds the const-prop for locals that are user variables.
  • Loading branch information
bors committed Apr 29, 2020
2 parents 825cf51 + 16ebaf9 commit 9201998
Show file tree
Hide file tree
Showing 22 changed files with 818 additions and 41 deletions.
91 changes: 70 additions & 21 deletions src/librustc_mir/transform/const_prop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@ struct ConstPropagator<'mir, '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>,
// Locals we need to forget at the end of the current block
locals_of_current_block: BitSet<Local>,
}

impl<'mir, 'tcx> LayoutOf for ConstPropagator<'mir, 'tcx> {
Expand Down Expand Up @@ -343,6 +345,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
//FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_body()` needs it
local_decls: body.local_decls.clone(),
source_info: None,
locals_of_current_block: BitSet::new_empty(body.local_decls.len()),
}
}

Expand All @@ -357,8 +360,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
}
}

fn remove_const(&mut self, local: Local) {
self.ecx.frame_mut().locals[local] =
/// 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::Uninitialized, layout: Cell::new(None) };
}

Expand Down Expand Up @@ -389,6 +394,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
}
}

/// Returns the value, if any, of evaluating `c`.
fn eval_constant(&mut self, c: &Constant<'tcx>, source_info: SourceInfo) -> Option<OpTy<'tcx>> {
// FIXME we need to revisit this for #67176
if c.needs_subst() {
Expand Down Expand Up @@ -429,11 +435,14 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
}
}

/// Returns the value, if any, of evaluating `place`.
fn eval_place(&mut self, place: Place<'tcx>) -> Option<OpTy<'tcx>> {
trace!("eval_place(place={:?})", place);
self.use_ecx(|this| this.ecx.eval_place_to_op(place, None))
}

/// Returns the value, if any, of evaluating `op`. Calls upon `eval_constant`
/// or `eval_place`, depending on the variant of `Operand` used.
fn eval_operand(&mut self, op: &Operand<'tcx>, source_info: SourceInfo) -> Option<OpTy<'tcx>> {
match *op {
Operand::Constant(ref c) => self.eval_constant(c, source_info),
Expand Down Expand Up @@ -592,6 +601,7 @@ 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> {
Operand::Constant(Box::new(Constant {
span,
Expand Down Expand Up @@ -637,6 +647,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
// Found a value represented as a pair. For now only do cont-prop if type of
// Rvalue is also a pair with two scalars. The more general case is more
// complicated to implement so we'll do it later.
// FIXME: implement the general case stated above ^.
let ty = &value.layout.ty.kind;
// Only do it for tuples
if let ty::Tuple(substs) = ty {
Expand Down Expand Up @@ -673,6 +684,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
}
}

/// Returns `true` if and only if this `op` should be const-propagated into.
fn should_const_prop(&mut self, op: OpTy<'tcx>) -> bool {
let mir_opt_level = self.tcx.sess.opts.debugging_opts.mir_opt_level;

Expand Down Expand Up @@ -704,6 +716,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
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,
/// No propagation is allowed at all.
Expand All @@ -712,28 +726,41 @@ enum ConstPropMode {

struct CanConstProp {
can_const_prop: IndexVec<Local, ConstPropMode>,
// false at the beginning, once set, there are not allowed to be any more assignments
// 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 {
/// returns true if `local` can be propagated
/// Returns true if `local` can be propagated
fn check(body: &Body<'_>) -> IndexVec<Local, ConstPropMode> {
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() {
// cannot use args at all
// cannot use locals because if x < y { y - x } else { x - y } would
// 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
let local_kind = body.local_kind(local);

if local_kind == LocalKind::Arg || local_kind == LocalKind::Var {
if cpv.local_kinds[local] == LocalKind::Arg {
*val = ConstPropMode::OnlyPropagateInto;
trace!("local {:?} can't be const propagated because it's not a temporary", local);
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);
Expand All @@ -759,8 +786,12 @@ impl<'tcx> Visitor<'tcx> for CanConstProp {
| NonMutatingUse(NonMutatingUseContext::Move)
| NonMutatingUse(NonMutatingUseContext::Inspect)
| NonMutatingUse(NonMutatingUseContext::Projection)
| MutatingUse(MutatingUseContext::Projection)
| NonUse(_) => {}
MutatingUse(MutatingUseContext::Projection) => {
if self.local_kinds[local] != LocalKind::Temp {
self.can_const_prop[local] = ConstPropMode::NoPropagation;
}
}
_ => {
trace!("local {:?} can't be propagaged because it's used: {:?}", local, context);
self.can_const_prop[local] = ConstPropMode::NoPropagation;
Expand Down Expand Up @@ -797,25 +828,35 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
if let Some(local) = place.as_local() {
let can_const_prop = self.can_const_prop[local];
if let Some(()) = self.const_prop(rval, place_layout, source_info, place) {
if can_const_prop == ConstPropMode::FullConstProp
|| can_const_prop == ConstPropMode::OnlyPropagateInto
{
if can_const_prop != ConstPropMode::NoPropagation {
// This will return None for Locals that are from other blocks,
// so it should be okay to propagate from here on down.
if let Some(value) = self.get_const(local) {
if self.should_const_prop(value) {
trace!("replacing {:?} with {:?}", rval, value);
self.replace_with_const(rval, value, statement.source_info);

if can_const_prop == ConstPropMode::FullConstProp {
if can_const_prop == ConstPropMode::FullConstProp
|| can_const_prop == ConstPropMode::OnlyInsideOwnBlock
{
trace!("propagated into {:?}", local);
}
}
if can_const_prop == ConstPropMode::OnlyInsideOwnBlock {
trace!(
"found local restricted to its block. Will remove it from const-prop after block is finished. Local: {:?}",
local
);
self.locals_of_current_block.insert(local);
}
}
}
}
if self.can_const_prop[local] != ConstPropMode::FullConstProp {
if self.can_const_prop[local] == ConstPropMode::OnlyPropagateInto
|| self.can_const_prop[local] == ConstPropMode::NoPropagation
{
trace!("can't propagate into {:?}", local);
if local != RETURN_PLACE {
self.remove_const(local);
Self::remove_const(&mut self.ecx, local);
}
}
}
Expand Down Expand Up @@ -850,11 +891,11 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
let expected = ScalarMaybeUndef::from(Scalar::from_bool(*expected));
let value_const = self.ecx.read_scalar(value).unwrap();
if expected != value_const {
// poison all places this operand references so that further code
// Poison all places this operand references so that further code
// doesn't use the invalid value
match cond {
Operand::Move(ref place) | Operand::Copy(ref place) => {
self.remove_const(place.local);
Self::remove_const(&mut self.ecx, place.local);
}
Operand::Constant(_) => {}
}
Expand Down Expand Up @@ -916,7 +957,7 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
}
}
}
//none of these have Operands to const-propagate
// None of these have Operands to const-propagate
TerminatorKind::Goto { .. }
| TerminatorKind::Resume
| TerminatorKind::Abort
Expand All @@ -931,5 +972,13 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
//FIXME(wesleywiser) Call does have Operands that could be const-propagated
TerminatorKind::Call { .. } => {}
}
// We remove all Locals which are restricted in propagation to their containing blocks.
// We wouldn't need to clone, but the borrow checker can't see that we're not aliasing
// the locals_of_current_block field, so we need to clone it first.
// let ecx = &mut self.ecx;
for local in self.locals_of_current_block.iter() {
Self::remove_const(&mut self.ecx, local);
}
self.locals_of_current_block.clear();
}
}
6 changes: 6 additions & 0 deletions src/test/mir-opt/const_prop/bad_op_div_by_zero.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// EMIT_MIR rustc.main.ConstProp.diff
#[allow(unconditional_panic)]
fn main() {
let y = 0;
let _z = 1 / y;
}
Loading

0 comments on commit 9201998

Please sign in to comment.