Skip to content

Commit 9201998

Browse files
committed
Auto merge of #71518 - felix91gr:const_prop_bugfix_just_block_prop, r=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.
2 parents 825cf51 + 16ebaf9 commit 9201998

22 files changed

+818
-41
lines changed

src/librustc_mir/transform/const_prop.rs

+70-21
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,8 @@ struct ConstPropagator<'mir, 'tcx> {
274274
// Because we have `MutVisitor` we can't obtain the `SourceInfo` from a `Location`. So we store
275275
// the last known `SourceInfo` here and just keep revisiting it.
276276
source_info: Option<SourceInfo>,
277+
// Locals we need to forget at the end of the current block
278+
locals_of_current_block: BitSet<Local>,
277279
}
278280

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

@@ -357,8 +360,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
357360
}
358361
}
359362

360-
fn remove_const(&mut self, local: Local) {
361-
self.ecx.frame_mut().locals[local] =
363+
/// Remove `local` from the pool of `Locals`. Allows writing to them,
364+
/// but not reading from them anymore.
365+
fn remove_const(ecx: &mut InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>, local: Local) {
366+
ecx.frame_mut().locals[local] =
362367
LocalState { value: LocalValue::Uninitialized, layout: Cell::new(None) };
363368
}
364369

@@ -389,6 +394,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
389394
}
390395
}
391396

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

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

444+
/// Returns the value, if any, of evaluating `op`. Calls upon `eval_constant`
445+
/// or `eval_place`, depending on the variant of `Operand` used.
437446
fn eval_operand(&mut self, op: &Operand<'tcx>, source_info: SourceInfo) -> Option<OpTy<'tcx>> {
438447
match *op {
439448
Operand::Constant(ref c) => self.eval_constant(c, source_info),
@@ -592,6 +601,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
592601
})
593602
}
594603

604+
/// Creates a new `Operand::Constant` from a `Scalar` value
595605
fn operand_from_scalar(&self, scalar: Scalar, ty: Ty<'tcx>, span: Span) -> Operand<'tcx> {
596606
Operand::Constant(Box::new(Constant {
597607
span,
@@ -637,6 +647,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
637647
// Found a value represented as a pair. For now only do cont-prop if type of
638648
// Rvalue is also a pair with two scalars. The more general case is more
639649
// complicated to implement so we'll do it later.
650+
// FIXME: implement the general case stated above ^.
640651
let ty = &value.layout.ty.kind;
641652
// Only do it for tuples
642653
if let ty::Tuple(substs) = ty {
@@ -673,6 +684,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
673684
}
674685
}
675686

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

@@ -704,6 +716,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
704716
enum ConstPropMode {
705717
/// The `Local` can be propagated into and reads of this `Local` can also be propagated.
706718
FullConstProp,
719+
/// The `Local` can only be propagated into and from its own block.
720+
OnlyInsideOwnBlock,
707721
/// The `Local` can be propagated into but reads cannot be propagated.
708722
OnlyPropagateInto,
709723
/// No propagation is allowed at all.
@@ -712,28 +726,41 @@ enum ConstPropMode {
712726

713727
struct CanConstProp {
714728
can_const_prop: IndexVec<Local, ConstPropMode>,
715-
// false at the beginning, once set, there are not allowed to be any more assignments
729+
// False at the beginning. Once set, no more assignments are allowed to that local.
716730
found_assignment: BitSet<Local>,
731+
// Cache of locals' information
732+
local_kinds: IndexVec<Local, LocalKind>,
717733
}
718734

719735
impl CanConstProp {
720-
/// returns true if `local` can be propagated
736+
/// Returns true if `local` can be propagated
721737
fn check(body: &Body<'_>) -> IndexVec<Local, ConstPropMode> {
722738
let mut cpv = CanConstProp {
723739
can_const_prop: IndexVec::from_elem(ConstPropMode::FullConstProp, &body.local_decls),
724740
found_assignment: BitSet::new_empty(body.local_decls.len()),
741+
local_kinds: IndexVec::from_fn_n(
742+
|local| body.local_kind(local),
743+
body.local_decls.len(),
744+
),
725745
};
726746
for (local, val) in cpv.can_const_prop.iter_enumerated_mut() {
727-
// cannot use args at all
728-
// cannot use locals because if x < y { y - x } else { x - y } would
747+
// Cannot use args at all
748+
// Cannot use locals because if x < y { y - x } else { x - y } would
729749
// lint for x != y
730750
// FIXME(oli-obk): lint variables until they are used in a condition
731751
// FIXME(oli-obk): lint if return value is constant
732-
let local_kind = body.local_kind(local);
733-
734-
if local_kind == LocalKind::Arg || local_kind == LocalKind::Var {
752+
if cpv.local_kinds[local] == LocalKind::Arg {
735753
*val = ConstPropMode::OnlyPropagateInto;
736-
trace!("local {:?} can't be const propagated because it's not a temporary", local);
754+
trace!(
755+
"local {:?} can't be const propagated because it's a function argument",
756+
local
757+
);
758+
} else if cpv.local_kinds[local] == LocalKind::Var {
759+
*val = ConstPropMode::OnlyInsideOwnBlock;
760+
trace!(
761+
"local {:?} will only be propagated inside its block, because it's a user variable",
762+
local
763+
);
737764
}
738765
}
739766
cpv.visit_body(&body);
@@ -759,8 +786,12 @@ impl<'tcx> Visitor<'tcx> for CanConstProp {
759786
| NonMutatingUse(NonMutatingUseContext::Move)
760787
| NonMutatingUse(NonMutatingUseContext::Inspect)
761788
| NonMutatingUse(NonMutatingUseContext::Projection)
762-
| MutatingUse(MutatingUseContext::Projection)
763789
| NonUse(_) => {}
790+
MutatingUse(MutatingUseContext::Projection) => {
791+
if self.local_kinds[local] != LocalKind::Temp {
792+
self.can_const_prop[local] = ConstPropMode::NoPropagation;
793+
}
794+
}
764795
_ => {
765796
trace!("local {:?} can't be propagaged because it's used: {:?}", local, context);
766797
self.can_const_prop[local] = ConstPropMode::NoPropagation;
@@ -797,25 +828,35 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
797828
if let Some(local) = place.as_local() {
798829
let can_const_prop = self.can_const_prop[local];
799830
if let Some(()) = self.const_prop(rval, place_layout, source_info, place) {
800-
if can_const_prop == ConstPropMode::FullConstProp
801-
|| can_const_prop == ConstPropMode::OnlyPropagateInto
802-
{
831+
if can_const_prop != ConstPropMode::NoPropagation {
832+
// This will return None for Locals that are from other blocks,
833+
// so it should be okay to propagate from here on down.
803834
if let Some(value) = self.get_const(local) {
804835
if self.should_const_prop(value) {
805836
trace!("replacing {:?} with {:?}", rval, value);
806837
self.replace_with_const(rval, value, statement.source_info);
807-
808-
if can_const_prop == ConstPropMode::FullConstProp {
838+
if can_const_prop == ConstPropMode::FullConstProp
839+
|| can_const_prop == ConstPropMode::OnlyInsideOwnBlock
840+
{
809841
trace!("propagated into {:?}", local);
810842
}
811843
}
844+
if can_const_prop == ConstPropMode::OnlyInsideOwnBlock {
845+
trace!(
846+
"found local restricted to its block. Will remove it from const-prop after block is finished. Local: {:?}",
847+
local
848+
);
849+
self.locals_of_current_block.insert(local);
850+
}
812851
}
813852
}
814853
}
815-
if self.can_const_prop[local] != ConstPropMode::FullConstProp {
854+
if self.can_const_prop[local] == ConstPropMode::OnlyPropagateInto
855+
|| self.can_const_prop[local] == ConstPropMode::NoPropagation
856+
{
816857
trace!("can't propagate into {:?}", local);
817858
if local != RETURN_PLACE {
818-
self.remove_const(local);
859+
Self::remove_const(&mut self.ecx, local);
819860
}
820861
}
821862
}
@@ -850,11 +891,11 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
850891
let expected = ScalarMaybeUndef::from(Scalar::from_bool(*expected));
851892
let value_const = self.ecx.read_scalar(value).unwrap();
852893
if expected != value_const {
853-
// poison all places this operand references so that further code
894+
// Poison all places this operand references so that further code
854895
// doesn't use the invalid value
855896
match cond {
856897
Operand::Move(ref place) | Operand::Copy(ref place) => {
857-
self.remove_const(place.local);
898+
Self::remove_const(&mut self.ecx, place.local);
858899
}
859900
Operand::Constant(_) => {}
860901
}
@@ -916,7 +957,7 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
916957
}
917958
}
918959
}
919-
//none of these have Operands to const-propagate
960+
// None of these have Operands to const-propagate
920961
TerminatorKind::Goto { .. }
921962
| TerminatorKind::Resume
922963
| TerminatorKind::Abort
@@ -931,5 +972,13 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
931972
//FIXME(wesleywiser) Call does have Operands that could be const-propagated
932973
TerminatorKind::Call { .. } => {}
933974
}
975+
// We remove all Locals which are restricted in propagation to their containing blocks.
976+
// We wouldn't need to clone, but the borrow checker can't see that we're not aliasing
977+
// the locals_of_current_block field, so we need to clone it first.
978+
// let ecx = &mut self.ecx;
979+
for local in self.locals_of_current_block.iter() {
980+
Self::remove_const(&mut self.ecx, local);
981+
}
982+
self.locals_of_current_block.clear();
934983
}
935984
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// EMIT_MIR rustc.main.ConstProp.diff
2+
#[allow(unconditional_panic)]
3+
fn main() {
4+
let y = 0;
5+
let _z = 1 / y;
6+
}

0 commit comments

Comments
 (0)