Skip to content

Commit 75ee137

Browse files
committed
Do not use for_each_assignment_mut to iterate over assignment statements
`for_each_assignment_mut` can skip assignment statements with side effects, which can result in some assignment statements retrieving outdated value. For example, it may skip a dereference assignment statement.
1 parent 2633e01 commit 75ee137

File tree

4 files changed

+41
-79
lines changed

4 files changed

+41
-79
lines changed

compiler/rustc_mir_transform/src/gvn.rs

+37-40
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@
33
//! MIR may contain repeated and/or redundant computations. The objective of this pass is to detect
44
//! such redundancies and re-use the already-computed result when possible.
55
//!
6-
//! In a first pass, we compute a symbolic representation of values that are assigned to SSA
7-
//! locals. This symbolic representation is defined by the `Value` enum. Each produced instance of
8-
//! `Value` is interned as a `VnIndex`, which allows us to cheaply compute identical values.
9-
//!
106
//! From those assignments, we construct a mapping `VnIndex -> Vec<(Local, Location)>` of available
117
//! values, the locals in which they are stored, and the assignment location.
128
//!
13-
//! In a second pass, we traverse all (non SSA) assignments `x = rvalue` and operands. For each
9+
//! We traverse all assignments `x = rvalue` and operands.
10+
//!
11+
//! For each SSA one, we compute a symbolic representation of values that are assigned to SSA
12+
//! locals. This symbolic representation is defined by the `Value` enum. Each produced instance of
13+
//! `Value` is interned as a `VnIndex`, which allows us to cheaply compute identical values.
14+
//!
15+
//! For each non-SSA
1416
//! one, we compute the `VnIndex` of the rvalue. If this `VnIndex` is associated to a constant, we
1517
//! replace the rvalue/operand by that constant. Otherwise, if there is an SSA local `y`
1618
//! associated to this `VnIndex`, and if its definition location strictly dominates the assignment
@@ -107,7 +109,7 @@ use rustc_span::def_id::DefId;
107109
use smallvec::SmallVec;
108110
use tracing::{debug, instrument, trace};
109111

110-
use crate::ssa::{AssignedValue, SsaLocals};
112+
use crate::ssa::SsaLocals;
111113

112114
pub(super) struct GVN;
113115

@@ -126,31 +128,11 @@ impl<'tcx> crate::MirPass<'tcx> for GVN {
126128
let dominators = body.basic_blocks.dominators().clone();
127129

128130
let mut state = VnState::new(tcx, body, typing_env, &ssa, dominators, &body.local_decls);
129-
ssa.for_each_assignment_mut(
130-
body.basic_blocks.as_mut_preserves_cfg(),
131-
|local, value, location| {
132-
let value = match value {
133-
// We do not know anything of this assigned value.
134-
AssignedValue::Arg | AssignedValue::Terminator => None,
135-
// Try to get some insight.
136-
AssignedValue::Rvalue(rvalue) => {
137-
let value = state.simplify_rvalue(rvalue, location);
138-
// FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark
139-
// `local` as reusable if we have an exact type match.
140-
if state.local_decls[local].ty != rvalue.ty(state.local_decls, tcx) {
141-
return;
142-
}
143-
value
144-
}
145-
};
146-
// `next_opaque` is `Some`, so `new_opaque` must return `Some`.
147-
let value = value.or_else(|| state.new_opaque()).unwrap();
148-
state.assign(local, value);
149-
},
150-
);
151131

152-
// Stop creating opaques during replacement as it is useless.
153-
state.next_opaque = None;
132+
for local in body.args_iter().filter(|&local| ssa.is_ssa(local)) {
133+
let opaque = state.new_opaque().unwrap();
134+
state.assign(local, opaque);
135+
}
154136

155137
let reverse_postorder = body.basic_blocks.reverse_postorder().to_vec();
156138
for bb in reverse_postorder {
@@ -246,7 +228,6 @@ struct VnState<'body, 'tcx> {
246228
locals: IndexVec<Local, Option<VnIndex>>,
247229
/// Locals that are assigned that value.
248230
// This vector does not hold all the values of `VnIndex` that we create.
249-
// It stops at the largest value created in the first phase of collecting assignments.
250231
rev_locals: IndexVec<VnIndex, SmallVec<[Local; 1]>>,
251232
values: FxIndexSet<Value<'tcx>>,
252233
/// Values evaluated as constants if possible.
@@ -341,6 +322,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
341322
/// Record that `local` is assigned `value`. `local` must be SSA.
342323
#[instrument(level = "trace", skip(self))]
343324
fn assign(&mut self, local: Local, value: VnIndex) {
325+
debug_assert!(self.ssa.is_ssa(local));
344326
self.locals[local] = Some(value);
345327

346328
// Only register the value if its type is `Sized`, as we will emit copies of it.
@@ -1638,15 +1620,19 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> {
16381620
if let StatementKind::Assign(box (ref mut lhs, ref mut rvalue)) = stmt.kind {
16391621
self.simplify_place_projection(lhs, location);
16401622

1641-
// Do not try to simplify a constant, it's already in canonical shape.
1642-
if matches!(rvalue, Rvalue::Use(Operand::Constant(_))) {
1643-
return;
1644-
}
1645-
1646-
let value = lhs
1647-
.as_local()
1648-
.and_then(|local| self.locals[local])
1649-
.or_else(|| self.simplify_rvalue(rvalue, location));
1623+
let value = self.simplify_rvalue(rvalue, location);
1624+
let value = if let Some(local) = lhs.as_local()
1625+
&& self.ssa.is_ssa(local)
1626+
// FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark
1627+
// `local` as reusable if we have an exact type match.
1628+
&& self.local_decls[local].ty == rvalue.ty(self.local_decls, self.tcx)
1629+
{
1630+
let value = value.or_else(|| self.new_opaque()).unwrap();
1631+
self.assign(local, value);
1632+
Some(value)
1633+
} else {
1634+
value
1635+
};
16501636
let Some(value) = value else { return };
16511637

16521638
if let Some(const_) = self.try_as_constant(value) {
@@ -1662,6 +1648,17 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> {
16621648
}
16631649
self.super_statement(stmt, location);
16641650
}
1651+
1652+
fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) {
1653+
if let Terminator { kind: TerminatorKind::Call { destination, .. }, .. } = terminator
1654+
&& let Some(local) = destination.as_local()
1655+
&& self.ssa.is_ssa(local)
1656+
{
1657+
let opaque = self.new_opaque().unwrap();
1658+
self.assign(local, opaque);
1659+
}
1660+
self.super_terminator(terminator, location);
1661+
}
16651662
}
16661663

16671664
struct StorageRemover<'tcx> {

compiler/rustc_mir_transform/src/ssa.rs

-37
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,6 @@ pub(super) struct SsaLocals {
3232
borrowed_locals: BitSet<Local>,
3333
}
3434

35-
pub(super) enum AssignedValue<'a, 'tcx> {
36-
Arg,
37-
Rvalue(&'a mut Rvalue<'tcx>),
38-
Terminator,
39-
}
40-
4135
impl SsaLocals {
4236
pub(super) fn new<'tcx>(
4337
tcx: TyCtxt<'tcx>,
@@ -152,37 +146,6 @@ impl SsaLocals {
152146
})
153147
}
154148

155-
pub(super) fn for_each_assignment_mut<'tcx>(
156-
&self,
157-
basic_blocks: &mut IndexSlice<BasicBlock, BasicBlockData<'tcx>>,
158-
mut f: impl FnMut(Local, AssignedValue<'_, 'tcx>, Location),
159-
) {
160-
for &local in &self.assignment_order {
161-
match self.assignments[local] {
162-
Set1::One(DefLocation::Argument) => f(local, AssignedValue::Arg, Location {
163-
block: START_BLOCK,
164-
statement_index: 0,
165-
}),
166-
Set1::One(DefLocation::Assignment(loc)) => {
167-
let bb = &mut basic_blocks[loc.block];
168-
// `loc` must point to a direct assignment to `local`.
169-
let stmt = &mut bb.statements[loc.statement_index];
170-
let StatementKind::Assign(box (target, ref mut rvalue)) = stmt.kind else {
171-
bug!()
172-
};
173-
assert_eq!(target.as_local(), Some(local));
174-
f(local, AssignedValue::Rvalue(rvalue), loc)
175-
}
176-
Set1::One(DefLocation::CallReturn { call, .. }) => {
177-
let bb = &mut basic_blocks[call];
178-
let loc = Location { block: call, statement_index: bb.statements.len() };
179-
f(local, AssignedValue::Terminator, loc)
180-
}
181-
_ => {}
182-
}
183-
}
184-
}
185-
186149
/// Compute the equivalence classes for locals, based on copy statements.
187150
///
188151
/// The returned vector maps each local to the one it copies. In the following case:

tests/mir-opt/const_prop/control_flow_simplification.hello.GVN.panic-abort.diff

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88

99
bb0: {
1010
StorageLive(_1);
11-
_1 = const <bool as NeedsDrop>::NEEDS;
11+
- _1 = const <bool as NeedsDrop>::NEEDS;
1212
- switchInt(move _1) -> [0: bb2, otherwise: bb1];
13+
+ _1 = const false;
1314
+ switchInt(const false) -> [0: bb2, otherwise: bb1];
1415
}
1516

tests/mir-opt/const_prop/control_flow_simplification.hello.GVN.panic-unwind.diff

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88

99
bb0: {
1010
StorageLive(_1);
11-
_1 = const <bool as NeedsDrop>::NEEDS;
11+
- _1 = const <bool as NeedsDrop>::NEEDS;
1212
- switchInt(move _1) -> [0: bb2, otherwise: bb1];
13+
+ _1 = const false;
1314
+ switchInt(const false) -> [0: bb2, otherwise: bb1];
1415
}
1516

0 commit comments

Comments
 (0)