Skip to content

Commit 106f4cc

Browse files
committed
Extend GVN to perform local value numbering.
1 parent 100ab5f commit 106f4cc

File tree

38 files changed

+734
-688
lines changed

38 files changed

+734
-688
lines changed

compiler/rustc_mir_transform/src/gvn.rs

Lines changed: 88 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,6 @@ impl<'tcx> crate::MirPass<'tcx> for GVN {
131131
let mut state =
132132
VnState::new(tcx, body, typing_env, &ssa, dominators, &body.local_decls, &arena);
133133

134-
for local in body.args_iter().filter(|&local| ssa.is_ssa(local)) {
135-
let opaque = state.new_opaque(body.local_decls[local].ty);
136-
state.assign(local, opaque);
137-
}
138-
139134
let reverse_postorder = body.basic_blocks.reverse_postorder().to_vec();
140135
for bb in reverse_postorder {
141136
let data = &mut body.basic_blocks.as_mut_preserves_cfg()[bb];
@@ -232,10 +227,10 @@ struct VnState<'body, 'a, 'tcx> {
232227
local_decls: &'body LocalDecls<'tcx>,
233228
is_coroutine: bool,
234229
/// Value stored in each local.
235-
locals: IndexVec<Local, Option<VnIndex>>,
230+
locals: IndexVec<Local, VnIndex>,
236231
/// Locals that are assigned that value.
237232
// This vector does not hold all the values of `VnIndex` that we create.
238-
rev_locals: IndexVec<VnIndex, SmallVec<[Local; 1]>>,
233+
rev_locals: IndexVec<VnIndex, SmallVec<[(Local, Location); 1]>>,
239234
values: FxIndexSet<(Value<'a, 'tcx>, Ty<'tcx>)>,
240235
/// Values evaluated as constants if possible.
241236
evaluated: IndexVec<VnIndex, Option<OpTy<'tcx>>>,
@@ -266,12 +261,12 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
266261
let num_values =
267262
2 * body.basic_blocks.iter().map(|bbdata| bbdata.statements.len()).sum::<usize>()
268263
+ 4 * body.basic_blocks.len();
269-
VnState {
264+
let mut this = VnState {
270265
tcx,
271266
ecx: InterpCx::new(tcx, DUMMY_SP, typing_env, DummyMachine),
272267
local_decls,
273268
is_coroutine: body.coroutine.is_some(),
274-
locals: IndexVec::from_elem(None, local_decls),
269+
locals: IndexVec::with_capacity(body.local_decls.len()),
275270
rev_locals: IndexVec::with_capacity(num_values),
276271
values: FxIndexSet::with_capacity_and_hasher(num_values, Default::default()),
277272
evaluated: IndexVec::with_capacity(num_values),
@@ -281,7 +276,14 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
281276
dominators,
282277
reused_locals: DenseBitSet::new_empty(local_decls.len()),
283278
arena,
279+
};
280+
let init_loc = Location { block: START_BLOCK, statement_index: 0 };
281+
for decl in body.local_decls.iter() {
282+
let value = this.new_opaque(decl.ty);
283+
let local = this.locals.push(value);
284+
this.rev_locals[value].push((local, init_loc));
284285
}
286+
this
285287
}
286288

287289
fn typing_env(&self) -> ty::TypingEnv<'tcx> {
@@ -335,7 +337,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
335337

336338
let mut projection = place.projection.iter();
337339
let base = if place.is_indirect_first_projection() {
338-
let base = self.locals[place.local]?;
340+
let base = self.locals[place.local];
339341
// Skip the initial `Deref`.
340342
projection.next();
341343
AddressBase::Deref(base)
@@ -346,7 +348,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
346348
let projection = self
347349
.arena
348350
.try_alloc_from_iter(
349-
projection.map(|proj| proj.try_map(|value| self.locals[value], |ty| ty).ok_or(())),
351+
projection
352+
.map(|proj| proj.try_map(|value| Some(self.locals[value]), |ty| ty).ok_or(())),
350353
)
351354
.ok()?;
352355
let value = Value::Address { base, projection, kind, provenance: self.next_opaque() };
@@ -363,12 +366,39 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
363366
self.values.get_index(index.as_usize()).unwrap().1
364367
}
365368

366-
/// Record that `local` is assigned `value`. `local` must be SSA.
369+
/// Record that `local` is assigned `value`.
367370
#[instrument(level = "trace", skip(self))]
368-
fn assign(&mut self, local: Local, value: VnIndex) {
369-
debug_assert!(self.ssa.is_ssa(local));
370-
self.locals[local] = Some(value);
371-
self.rev_locals[value].push(local);
371+
fn assign(&mut self, local: Local, value: VnIndex, loc: Location) {
372+
self.locals[local] = value;
373+
self.rev_locals[value].push((local, loc));
374+
}
375+
376+
#[instrument(level = "trace", skip(self))]
377+
fn discard_place(&mut self, place: Place<'tcx>) {
378+
let discard_local = |this: &mut Self, local| {
379+
if this.ssa.is_ssa(local) {
380+
return;
381+
}
382+
let value = this.locals[local];
383+
this.rev_locals[value].retain(|(l, _)| *l != local);
384+
this.locals[local] = this.new_opaque(this.ty(value));
385+
#[cfg(debug_assertions)]
386+
for local_vec in this.rev_locals.iter() {
387+
for (other, _) in local_vec {
388+
debug_assert_ne!(*other, local);
389+
}
390+
}
391+
};
392+
if place.is_indirect_first_projection() {
393+
// Non-local mutation maybe invalidate deref.
394+
self.invalidate_derefs();
395+
// Remove stored value from borrowed locals.
396+
for local in self.ssa.borrowed_locals().iter() {
397+
discard_local(self, local);
398+
}
399+
} else {
400+
discard_local(self, place.local);
401+
}
372402
}
373403

374404
fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex {
@@ -633,7 +663,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
633663
let (mut place_ty, mut value) = match base {
634664
// The base is a local, so we take the local's value and project from it.
635665
AddressBase::Local(local) => {
636-
let local = self.locals[local]?;
666+
let local = self.locals[local];
637667
let place_ty = PlaceTy::from_ty(self.ty(local));
638668
(place_ty, local)
639669
}
@@ -744,7 +774,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
744774
// If the projection is indirect, we treat the local as a value, so can replace it with
745775
// another local.
746776
if place.is_indirect_first_projection()
747-
&& let Some(base) = self.locals[place.local]
777+
&& let base = self.locals[place.local]
748778
&& let Some(new_local) = self.try_as_local(base, location)
749779
&& place.local != new_local
750780
{
@@ -756,9 +786,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
756786

757787
for i in 0..projection.len() {
758788
let elem = projection[i];
759-
if let ProjectionElem::Index(idx_local) = elem
760-
&& let Some(idx) = self.locals[idx_local]
761-
{
789+
if let ProjectionElem::Index(idx_local) = elem {
790+
let idx = self.locals[idx_local];
762791
if let Some(offset) = self.evaluated[idx].as_ref()
763792
&& let Some(offset) = self.ecx.read_target_usize(offset).discard_err()
764793
&& let Some(min_length) = offset.checked_add(1)
@@ -794,7 +823,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
794823
let mut place_ref = place.as_ref();
795824

796825
// Invariant: `value` holds the value up-to the `index`th projection excluded.
797-
let Some(mut value) = self.locals[place.local] else { return Err(place_ref) };
826+
let mut value = self.locals[place.local];
798827
// Invariant: `value` has type `place_ty`, with optional downcast variant if needed.
799828
let mut place_ty = PlaceTy::from_ty(self.local_decls[place.local].ty);
800829
for (index, proj) in place.projection.iter().enumerate() {
@@ -805,7 +834,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
805834
place_ref = PlaceRef { local, projection: &place.projection[index..] };
806835
}
807836

808-
let Some(proj) = proj.try_map(|value| self.locals[value], |ty| ty) else {
837+
let Some(proj) = proj.try_map(|value| Some(self.locals[value]), |ty| ty) else {
809838
return Err(place_ref);
810839
};
811840
let Some(ty_and_value) = self.project(place_ty, value, proj) else {
@@ -1017,6 +1046,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
10171046
None
10181047
}
10191048

1049+
#[instrument(level = "trace", skip(self), ret)]
10201050
fn simplify_aggregate(
10211051
&mut self,
10221052
lhs: &Place<'tcx>,
@@ -1661,6 +1691,7 @@ fn op_to_prop_const<'tcx>(
16611691
impl<'tcx> VnState<'_, '_, 'tcx> {
16621692
/// If either [`Self::try_as_constant`] as [`Self::try_as_place`] succeeds,
16631693
/// returns that result as an [`Operand`].
1694+
#[instrument(level = "trace", skip(self), ret)]
16641695
fn try_as_operand(&mut self, index: VnIndex, location: Location) -> Option<Operand<'tcx>> {
16651696
if let Some(const_) = self.try_as_constant(index) {
16661697
Some(Operand::Constant(Box::new(const_)))
@@ -1673,6 +1704,7 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
16731704
}
16741705

16751706
/// If `index` is a `Value::Constant`, return the `Constant` to be put in the MIR.
1707+
#[instrument(level = "trace", skip(self), ret)]
16761708
fn try_as_constant(&mut self, index: VnIndex) -> Option<ConstOperand<'tcx>> {
16771709
// This was already constant in MIR, do not change it. If the constant is not
16781710
// deterministic, adding an additional mention of it in MIR will not give the same value as
@@ -1730,12 +1762,17 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
17301762

17311763
/// If there is a local which is assigned `index`, and its assignment strictly dominates `loc`,
17321764
/// return it. If you used this local, add it to `reused_locals` to remove storage statements.
1765+
#[instrument(level = "trace", skip(self), ret)]
17331766
fn try_as_local(&mut self, index: VnIndex, loc: Location) -> Option<Local> {
17341767
let other = self.rev_locals.get(index)?;
17351768
other
17361769
.iter()
1737-
.find(|&&other| self.ssa.assignment_dominates(&self.dominators, other, loc))
1738-
.copied()
1770+
.find(|&&(other, assign_loc)| {
1771+
self.ssa.assignment_dominates(&self.dominators, other, loc)
1772+
|| (assign_loc.block == loc.block
1773+
&& assign_loc.statement_index < loc.statement_index)
1774+
})
1775+
.map(|&(other, _)| other)
17391776
}
17401777
}
17411778

@@ -1744,11 +1781,25 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
17441781
self.tcx
17451782
}
17461783

1784+
fn visit_basic_block_data(&mut self, block: BasicBlock, bbdata: &mut BasicBlockData<'tcx>) {
1785+
for local_set in self.rev_locals.iter_mut() {
1786+
local_set.retain(|(local, _)| self.ssa.is_ssa(*local));
1787+
}
1788+
for local in self.locals.indices() {
1789+
if !self.ssa.is_ssa(local) {
1790+
let current = self.locals[local];
1791+
let new = self.new_opaque(self.ty(current));
1792+
self.locals[local] = new;
1793+
self.rev_locals[new].push((local, Location { block, statement_index: 0 }));
1794+
}
1795+
}
1796+
self.super_basic_block_data(block, bbdata);
1797+
}
1798+
17471799
fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
17481800
self.simplify_place_projection(place, location);
1749-
if context.is_mutating_use() && place.is_indirect() {
1750-
// Non-local mutation maybe invalidate deref.
1751-
self.invalidate_derefs();
1801+
if context.is_mutating_use() {
1802+
self.discard_place(*place);
17521803
}
17531804
self.super_place(place, context, location);
17541805
}
@@ -1779,32 +1830,27 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
17791830
}
17801831
}
17811832

1782-
if lhs.is_indirect() {
1783-
// Non-local mutation maybe invalidate deref.
1784-
self.invalidate_derefs();
1785-
}
1833+
self.discard_place(*lhs);
17861834

17871835
if let Some(local) = lhs.as_local()
1788-
&& self.ssa.is_ssa(local)
17891836
&& let rvalue_ty = rvalue.ty(self.local_decls, self.tcx)
17901837
// FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark
17911838
// `local` as reusable if we have an exact type match.
17921839
&& self.local_decls[local].ty == rvalue_ty
17931840
{
17941841
let value = value.unwrap_or_else(|| self.new_opaque(rvalue_ty));
1795-
self.assign(local, value);
1842+
self.assign(local, value, location);
17961843
}
17971844
}
17981845

17991846
fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) {
1800-
if let Terminator { kind: TerminatorKind::Call { destination, .. }, .. } = terminator {
1801-
if let Some(local) = destination.as_local()
1802-
&& self.ssa.is_ssa(local)
1803-
{
1804-
let ty = self.local_decls[local].ty;
1805-
let opaque = self.new_opaque(ty);
1806-
self.assign(local, opaque);
1807-
}
1847+
self.super_terminator(terminator, location);
1848+
if let Terminator { kind: TerminatorKind::Call { destination, .. }, .. } = terminator
1849+
&& let Some(local) = destination.as_local()
1850+
{
1851+
let ty = self.local_decls[local].ty;
1852+
let opaque = self.new_opaque(ty);
1853+
self.assign(local, opaque, location);
18081854
}
18091855
// Function calls and ASM may invalidate (nested) derefs. We must handle them carefully.
18101856
// Currently, only preserving derefs for trivial terminators like SwitchInt and Goto.
@@ -1815,7 +1861,6 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
18151861
if !safe_to_preserve_derefs {
18161862
self.invalidate_derefs();
18171863
}
1818-
self.super_terminator(terminator, location);
18191864
}
18201865
}
18211866

tests/mir-opt/const_prop/boxes.main.GVN.panic-abort.diff

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,21 @@
3030
}
3131

3232
bb1: {
33-
StorageLive(_7);
33+
- StorageLive(_7);
34+
+ nop;
3435
_7 = ShallowInitBox(move _6, i32);
3536
_8 = copy ((_7.0: std::ptr::Unique<i32>).0: std::ptr::NonNull<i32>) as *const i32 (Transmute);
3637
(*_8) = const 42_i32;
37-
_3 = move _7;
38-
StorageDead(_7);
39-
_9 = copy ((_3.0: std::ptr::Unique<i32>).0: std::ptr::NonNull<i32>) as *const i32 (Transmute);
40-
_2 = copy (*_9);
38+
- _3 = move _7;
39+
- StorageDead(_7);
40+
- _9 = copy ((_3.0: std::ptr::Unique<i32>).0: std::ptr::NonNull<i32>) as *const i32 (Transmute);
41+
- _2 = copy (*_9);
4142
- _1 = Add(move _2, const 0_i32);
4243
- StorageDead(_2);
44+
+ _3 = copy _7;
45+
+ nop;
46+
+ _9 = copy _8;
47+
+ _2 = copy (*_8);
4348
+ _1 = copy _2;
4449
+ nop;
4550
drop(_3) -> [return: bb2, unwind unreachable];

tests/mir-opt/const_prop/boxes.main.GVN.panic-unwind.diff

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,21 @@
3030
}
3131

3232
bb1: {
33-
StorageLive(_7);
33+
- StorageLive(_7);
34+
+ nop;
3435
_7 = ShallowInitBox(move _6, i32);
3536
_8 = copy ((_7.0: std::ptr::Unique<i32>).0: std::ptr::NonNull<i32>) as *const i32 (Transmute);
3637
(*_8) = const 42_i32;
37-
_3 = move _7;
38-
StorageDead(_7);
39-
_9 = copy ((_3.0: std::ptr::Unique<i32>).0: std::ptr::NonNull<i32>) as *const i32 (Transmute);
40-
_2 = copy (*_9);
38+
- _3 = move _7;
39+
- StorageDead(_7);
40+
- _9 = copy ((_3.0: std::ptr::Unique<i32>).0: std::ptr::NonNull<i32>) as *const i32 (Transmute);
41+
- _2 = copy (*_9);
4142
- _1 = Add(move _2, const 0_i32);
4243
- StorageDead(_2);
44+
+ _3 = copy _7;
45+
+ nop;
46+
+ _9 = copy _8;
47+
+ _2 = copy (*_8);
4348
+ _1 = copy _2;
4449
+ nop;
4550
drop(_3) -> [return: bb2, unwind: bb3];

tests/mir-opt/const_prop/mutable_variable.main.GVN.diff

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
_1 = const 42_i32;
1818
_1 = const 99_i32;
1919
StorageLive(_2);
20-
_2 = copy _1;
20+
- _2 = copy _1;
21+
+ _2 = const 99_i32;
2122
_0 = const ();
2223
StorageDead(_2);
2324
StorageDead(_1);

tests/mir-opt/const_prop/mutable_variable.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ fn main() {
77
// CHECK: debug y => [[y:_.*]];
88
// CHECK: [[x]] = const 42_i32;
99
// CHECK: [[x]] = const 99_i32;
10-
// CHECK: [[y]] = copy [[x]];
10+
// CHECK: [[y]] = const 99_i32;
1111
let mut x = 42;
1212
x = 99;
1313
let y = x;

tests/mir-opt/const_prop/mutable_variable_no_prop.main.GVN.diff

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,21 @@
1919
StorageLive(_1);
2020
_1 = const 42_u32;
2121
StorageLive(_2);
22-
StorageLive(_3);
22+
- StorageLive(_3);
23+
+ nop;
2324
StorageLive(_4);
2425
_4 = const {ALLOC0: *mut u32};
2526
_3 = copy (*_4);
26-
_1 = move _3;
27-
StorageDead(_3);
27+
- _1 = move _3;
28+
- StorageDead(_3);
29+
+ _1 = copy _3;
30+
+ nop;
2831
StorageDead(_4);
2932
_2 = const ();
3033
StorageDead(_2);
3134
StorageLive(_5);
32-
_5 = copy _1;
35+
- _5 = copy _1;
36+
+ _5 = copy _3;
3337
_0 = const ();
3438
StorageDead(_5);
3539
StorageDead(_1);

tests/mir-opt/const_prop/mutable_variable_no_prop.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ fn main() {
1010
// CHECK: debug y => [[y:_.*]];
1111
// CHECK: [[x]] = const 42_u32;
1212
// CHECK: [[tmp:_.*]] = copy (*{{_.*}});
13-
// CHECK: [[x]] = move [[tmp]];
14-
// CHECK: [[y]] = copy [[x]];
13+
// CHECK: [[x]] = copy [[tmp]];
14+
// CHECK: [[y]] = copy [[tmp]];
1515
let mut x = 42;
1616
unsafe {
1717
x = STATIC;

0 commit comments

Comments
 (0)