Skip to content

Commit 581eba0

Browse files
committed
Extend GVN to perform local value numbering.
1 parent 62e1859 commit 581eba0

27 files changed

+376
-245
lines changed

compiler/rustc_mir_transform/src/gvn.rs

Lines changed: 90 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ use rustc_const_eval::interpret::{
9494
ImmTy, Immediate, InterpCx, MemPlaceMeta, MemoryKind, OpTy, Projectable, Scalar,
9595
intern_const_alloc_for_constprop,
9696
};
97-
use rustc_data_structures::fx::{FxIndexSet, MutableValues};
97+
use rustc_data_structures::fx::{FxHashMap, FxIndexSet, MutableValues};
9898
use rustc_data_structures::graph::dominators::Dominators;
9999
use rustc_hir::def::DefKind;
100100
use rustc_index::bit_set::DenseBitSet;
@@ -234,8 +234,11 @@ struct VnState<'body, 'a, 'tcx> {
234234
/// Value stored in each local.
235235
locals: IndexVec<Local, Option<VnIndex>>,
236236
/// Locals that are assigned that value.
237-
// This vector does not hold all the values of `VnIndex` that we create.
238-
rev_locals: IndexVec<VnIndex, SmallVec<[Local; 1]>>,
237+
// This vector holds the locals that are SSA.
238+
rev_locals_ssa: IndexVec<VnIndex, SmallVec<[Local; 1]>>,
239+
// This map holds the locals that are not SSA. This map is cleared at the end of each block.
240+
// Therefore, we do not need a location, the local always appears before the current location.
241+
rev_locals_non_ssa: FxHashMap<VnIndex, SmallVec<[Local; 1]>>,
239242
values: FxIndexSet<(Value<'a, 'tcx>, Ty<'tcx>)>,
240243
/// Values evaluated as constants if possible.
241244
evaluated: IndexVec<VnIndex, Option<OpTy<'tcx>>>,
@@ -271,8 +274,9 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
271274
ecx: InterpCx::new(tcx, DUMMY_SP, typing_env, DummyMachine),
272275
local_decls,
273276
is_coroutine: body.coroutine.is_some(),
274-
locals: IndexVec::from_elem(None, local_decls),
275-
rev_locals: IndexVec::with_capacity(num_values),
277+
locals: IndexVec::from_elem(None, &body.local_decls),
278+
rev_locals_ssa: IndexVec::with_capacity(num_values),
279+
rev_locals_non_ssa: FxHashMap::default(),
276280
values: FxIndexSet::with_capacity_and_hasher(num_values, Default::default()),
277281
evaluated: IndexVec::with_capacity(num_values),
278282
next_opaque: 1,
@@ -297,7 +301,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
297301
let evaluated = self.eval_to_const(index);
298302
let _index = self.evaluated.push(evaluated);
299303
debug_assert_eq!(index, _index);
300-
let _index = self.rev_locals.push(SmallVec::new());
304+
let _index = self.rev_locals_ssa.push(Default::default());
301305
debug_assert_eq!(index, _index);
302306
}
303307
index
@@ -335,7 +339,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
335339

336340
let mut projection = place.projection.iter();
337341
let base = if place.is_indirect_first_projection() {
338-
let base = self.locals[place.local]?;
342+
let base = self.local(place.local);
339343
// Skip the initial `Deref`.
340344
projection.next();
341345
AddressBase::Deref(base)
@@ -346,7 +350,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
346350
let projection = self
347351
.arena
348352
.try_alloc_from_iter(
349-
projection.map(|proj| proj.try_map(|value| self.locals[value], |ty| ty).ok_or(())),
353+
projection
354+
.map(|proj| proj.try_map(|value| Some(self.local(value)), |ty| ty).ok_or(())),
350355
)
351356
.ok()?;
352357
let value = Value::Address { base, projection, kind, provenance: self.next_opaque() };
@@ -363,12 +368,49 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
363368
self.values.get_index(index.as_usize()).unwrap().1
364369
}
365370

366-
/// Record that `local` is assigned `value`. `local` must be SSA.
371+
/// Record that `local` is assigned `value`.
367372
#[instrument(level = "trace", skip(self))]
368373
fn assign(&mut self, local: Local, value: VnIndex) {
369-
debug_assert!(self.ssa.is_ssa(local));
370374
self.locals[local] = Some(value);
371-
self.rev_locals[value].push(local);
375+
if self.ssa.is_ssa(local) {
376+
self.rev_locals_ssa[value].push(local);
377+
} else {
378+
self.rev_locals_non_ssa.entry(value).or_default().push(local);
379+
}
380+
}
381+
382+
/// Return the value assigned to a local, or assign an opaque value and return it.
383+
#[instrument(level = "trace", skip(self), ret)]
384+
fn local(&mut self, local: Local) -> VnIndex {
385+
if let Some(value) = self.locals[local] {
386+
return value;
387+
}
388+
let value = self.new_opaque(self.local_decls[local].ty);
389+
self.locals[local] = Some(value);
390+
self.rev_locals_non_ssa.entry(value).or_default().push(local);
391+
value
392+
}
393+
394+
#[instrument(level = "trace", skip(self))]
395+
fn discard_place(&mut self, place: Place<'tcx>) {
396+
let discard_local = |this: &mut Self, local| {
397+
if this.ssa.is_ssa(local) {
398+
return;
399+
}
400+
if let Some(value) = this.locals[local].take() {
401+
this.rev_locals_non_ssa.entry(value).or_default().retain(|l| *l != local);
402+
}
403+
};
404+
if place.is_indirect_first_projection() {
405+
// Non-local mutation maybe invalidate deref.
406+
self.invalidate_derefs();
407+
// Remove stored value from borrowed locals.
408+
for local in self.ssa.borrowed_locals().iter() {
409+
discard_local(self, local);
410+
}
411+
} else {
412+
discard_local(self, place.local);
413+
}
372414
}
373415

374416
fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex {
@@ -633,7 +675,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
633675
let (mut place_ty, mut value) = match base {
634676
// The base is a local, so we take the local's value and project from it.
635677
AddressBase::Local(local) => {
636-
let local = self.locals[local]?;
678+
let local = self.local(local);
637679
let place_ty = PlaceTy::from_ty(self.ty(local));
638680
(place_ty, local)
639681
}
@@ -739,7 +781,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
739781
// If the projection is indirect, we treat the local as a value, so can replace it with
740782
// another local.
741783
if place.is_indirect_first_projection()
742-
&& let Some(base) = self.locals[place.local]
784+
&& let base = self.local(place.local)
743785
&& let Some(new_local) = self.try_as_local(base, location)
744786
&& place.local != new_local
745787
{
@@ -751,9 +793,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
751793

752794
for i in 0..projection.len() {
753795
let elem = projection[i];
754-
if let ProjectionElem::Index(idx_local) = elem
755-
&& let Some(idx) = self.locals[idx_local]
756-
{
796+
if let ProjectionElem::Index(idx_local) = elem {
797+
let idx = self.local(idx_local);
757798
if let Some(offset) = self.evaluated[idx].as_ref()
758799
&& let Some(offset) = self.ecx.read_target_usize(offset).discard_err()
759800
&& let Some(min_length) = offset.checked_add(1)
@@ -789,7 +830,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
789830
let mut place_ref = place.as_ref();
790831

791832
// Invariant: `value` holds the value up-to the `index`th projection excluded.
792-
let Some(mut value) = self.locals[place.local] else { return Err(place_ref) };
833+
let mut value = self.local(place.local);
793834
// Invariant: `value` has type `place_ty`, with optional downcast variant if needed.
794835
let mut place_ty = PlaceTy::from_ty(self.local_decls[place.local].ty);
795836
for (index, proj) in place.projection.iter().enumerate() {
@@ -800,7 +841,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
800841
place_ref = PlaceRef { local, projection: &place.projection[index..] };
801842
}
802843

803-
let Some(proj) = proj.try_map(|value| self.locals[value], |ty| ty) else {
844+
let Some(proj) = proj.try_map(|value| Some(self.local(value)), |ty| ty) else {
804845
return Err(place_ref);
805846
};
806847
let Some(ty_and_value) = self.project(place_ty, value, proj) else {
@@ -1709,11 +1750,17 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
17091750
/// If there is a local which is assigned `index`, and its assignment strictly dominates `loc`,
17101751
/// return it. If you used this local, add it to `reused_locals` to remove storage statements.
17111752
fn try_as_local(&mut self, index: VnIndex, loc: Location) -> Option<Local> {
1712-
let other = self.rev_locals.get(index)?;
1713-
other
1714-
.iter()
1715-
.find(|&&other| self.ssa.assignment_dominates(&self.dominators, other, loc))
1716-
.copied()
1753+
if let Some(ssa) = self.rev_locals_ssa.get(index)
1754+
&& let Some(other) = ssa
1755+
.iter()
1756+
.find(|&&other| self.ssa.assignment_dominates(&self.dominators, other, loc))
1757+
{
1758+
Some(*other)
1759+
} else if let Some(non_ssa) = self.rev_locals_non_ssa.get(&index) {
1760+
non_ssa.first().copied()
1761+
} else {
1762+
None
1763+
}
17171764
}
17181765
}
17191766

@@ -1722,11 +1769,20 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
17221769
self.tcx
17231770
}
17241771

1772+
fn visit_basic_block_data(&mut self, block: BasicBlock, bbdata: &mut BasicBlockData<'tcx>) {
1773+
self.rev_locals_non_ssa.clear();
1774+
for local in self.locals.indices() {
1775+
if !self.ssa.is_ssa(local) {
1776+
self.locals[local] = None;
1777+
}
1778+
}
1779+
self.super_basic_block_data(block, bbdata);
1780+
}
1781+
17251782
fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
17261783
self.simplify_place_projection(place, location);
1727-
if context.is_mutating_use() && place.is_indirect() {
1728-
// Non-local mutation maybe invalidate deref.
1729-
self.invalidate_derefs();
1784+
if context.is_mutating_use() {
1785+
self.discard_place(*place);
17301786
}
17311787
self.super_place(place, context, location);
17321788
}
@@ -1765,13 +1821,9 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
17651821
}
17661822
}
17671823

1768-
if lhs.is_indirect() {
1769-
// Non-local mutation maybe invalidate deref.
1770-
self.invalidate_derefs();
1771-
}
1824+
self.discard_place(*lhs);
17721825

17731826
if let Some(local) = lhs.as_local()
1774-
&& self.ssa.is_ssa(local)
17751827
&& let rvalue_ty = rvalue.ty(self.local_decls, self.tcx)
17761828
// FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark
17771829
// `local` as reusable if we have an exact type match.
@@ -1783,14 +1835,13 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
17831835
}
17841836

17851837
fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) {
1786-
if let Terminator { kind: TerminatorKind::Call { destination, .. }, .. } = terminator {
1787-
if let Some(local) = destination.as_local()
1788-
&& self.ssa.is_ssa(local)
1789-
{
1790-
let ty = self.local_decls[local].ty;
1791-
let opaque = self.new_opaque(ty);
1792-
self.assign(local, opaque);
1793-
}
1838+
self.super_terminator(terminator, location);
1839+
if let Terminator { kind: TerminatorKind::Call { destination, .. }, .. } = terminator
1840+
&& let Some(local) = destination.as_local()
1841+
{
1842+
let ty = self.local_decls[local].ty;
1843+
let opaque = self.new_opaque(ty);
1844+
self.assign(local, opaque);
17941845
}
17951846
// Function calls and ASM may invalidate (nested) derefs. We must handle them carefully.
17961847
// Currently, only preserving derefs for trivial terminators like SwitchInt and Goto.
@@ -1801,7 +1852,6 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
18011852
if !safe_to_preserve_derefs {
18021853
self.invalidate_derefs();
18031854
}
1804-
self.super_terminator(terminator, location);
18051855
}
18061856
}
18071857

tests/coverage/closure.cov-map

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -74,24 +74,22 @@ Number of file 0 mappings: 66
7474
- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2)
7575
Highest counter ID seen: c1
7676

77-
Function name: closure::main::{closure#0}
78-
Raw bytes (51): 0x[01, 01, 01, 01, 05, 09, 01, 28, 05, 00, 06, 01, 01, 0d, 00, 1a, 01, 00, 1d, 00, 1e, 01, 01, 0c, 00, 14, 05, 00, 15, 02, 0a, 02, 02, 09, 00, 0a, 01, 01, 09, 00, 17, 01, 00, 18, 00, 20, 01, 01, 05, 00, 06]
77+
Function name: closure::main::{closure#0} (unused)
78+
Raw bytes (49): 0x[01, 01, 00, 09, 00, 28, 05, 00, 06, 00, 01, 0d, 00, 1a, 00, 00, 1d, 00, 1e, 00, 01, 0c, 00, 14, 00, 00, 15, 02, 0a, 00, 02, 09, 00, 0a, 00, 01, 09, 00, 17, 00, 00, 18, 00, 20, 00, 01, 05, 00, 06]
7979
Number of files: 1
8080
- file 0 => $DIR/closure.rs
81-
Number of expressions: 1
82-
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
81+
Number of expressions: 0
8382
Number of file 0 mappings: 9
84-
- Code(Counter(0)) at (prev + 40, 5) to (start + 0, 6)
85-
- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 26)
86-
- Code(Counter(0)) at (prev + 0, 29) to (start + 0, 30)
87-
- Code(Counter(0)) at (prev + 1, 12) to (start + 0, 20)
88-
- Code(Counter(1)) at (prev + 0, 21) to (start + 2, 10)
89-
- Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 0, 10)
90-
= (c0 - c1)
91-
- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 23)
92-
- Code(Counter(0)) at (prev + 0, 24) to (start + 0, 32)
93-
- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 6)
94-
Highest counter ID seen: c1
83+
- Code(Zero) at (prev + 40, 5) to (start + 0, 6)
84+
- Code(Zero) at (prev + 1, 13) to (start + 0, 26)
85+
- Code(Zero) at (prev + 0, 29) to (start + 0, 30)
86+
- Code(Zero) at (prev + 1, 12) to (start + 0, 20)
87+
- Code(Zero) at (prev + 0, 21) to (start + 2, 10)
88+
- Code(Zero) at (prev + 2, 9) to (start + 0, 10)
89+
- Code(Zero) at (prev + 1, 9) to (start + 0, 23)
90+
- Code(Zero) at (prev + 0, 24) to (start + 0, 32)
91+
- Code(Zero) at (prev + 1, 5) to (start + 0, 6)
92+
Highest counter ID seen: (none)
9593

9694
Function name: closure::main::{closure#10} (unused)
9795
Raw bytes (25): 0x[01, 01, 00, 04, 00, 9b, 01, 07, 00, 08, 00, 00, 09, 00, 11, 00, 00, 12, 00, 1e, 00, 00, 20, 00, 21]
@@ -199,24 +197,22 @@ Number of file 0 mappings: 7
199197
- Code(Counter(0)) at (prev + 2, 9) to (start + 0, 10)
200198
Highest counter ID seen: c1
201199

202-
Function name: closure::main::{closure#18}
203-
Raw bytes (51): 0x[01, 01, 01, 01, 05, 09, 01, 19, 0d, 00, 0e, 01, 01, 15, 00, 22, 01, 00, 25, 00, 26, 01, 01, 14, 00, 1c, 05, 00, 1d, 02, 12, 02, 02, 11, 00, 12, 01, 01, 11, 00, 1f, 01, 00, 20, 00, 28, 01, 01, 0d, 00, 0e]
200+
Function name: closure::main::{closure#18} (unused)
201+
Raw bytes (49): 0x[01, 01, 00, 09, 00, 19, 0d, 00, 0e, 00, 01, 15, 00, 22, 00, 00, 25, 00, 26, 00, 01, 14, 00, 1c, 00, 00, 1d, 02, 12, 00, 02, 11, 00, 12, 00, 01, 11, 00, 1f, 00, 00, 20, 00, 28, 00, 01, 0d, 00, 0e]
204202
Number of files: 1
205203
- file 0 => $DIR/closure.rs
206-
Number of expressions: 1
207-
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
204+
Number of expressions: 0
208205
Number of file 0 mappings: 9
209-
- Code(Counter(0)) at (prev + 25, 13) to (start + 0, 14)
210-
- Code(Counter(0)) at (prev + 1, 21) to (start + 0, 34)
211-
- Code(Counter(0)) at (prev + 0, 37) to (start + 0, 38)
212-
- Code(Counter(0)) at (prev + 1, 20) to (start + 0, 28)
213-
- Code(Counter(1)) at (prev + 0, 29) to (start + 2, 18)
214-
- Code(Expression(0, Sub)) at (prev + 2, 17) to (start + 0, 18)
215-
= (c0 - c1)
216-
- Code(Counter(0)) at (prev + 1, 17) to (start + 0, 31)
217-
- Code(Counter(0)) at (prev + 0, 32) to (start + 0, 40)
218-
- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 14)
219-
Highest counter ID seen: c1
206+
- Code(Zero) at (prev + 25, 13) to (start + 0, 14)
207+
- Code(Zero) at (prev + 1, 21) to (start + 0, 34)
208+
- Code(Zero) at (prev + 0, 37) to (start + 0, 38)
209+
- Code(Zero) at (prev + 1, 20) to (start + 0, 28)
210+
- Code(Zero) at (prev + 0, 29) to (start + 2, 18)
211+
- Code(Zero) at (prev + 2, 17) to (start + 0, 18)
212+
- Code(Zero) at (prev + 1, 17) to (start + 0, 31)
213+
- Code(Zero) at (prev + 0, 32) to (start + 0, 40)
214+
- Code(Zero) at (prev + 1, 13) to (start + 0, 14)
215+
Highest counter ID seen: (none)
220216

221217
Function name: closure::main::{closure#19}
222218
Raw bytes (51): 0x[01, 01, 01, 01, 05, 09, 01, 43, 0d, 00, 0e, 01, 01, 15, 00, 22, 01, 00, 25, 00, 26, 01, 01, 14, 00, 1c, 05, 00, 1d, 02, 12, 02, 02, 11, 00, 12, 01, 01, 11, 00, 1f, 01, 00, 20, 00, 28, 01, 01, 0d, 00, 0e]

tests/coverage/issue-83601.cov-map

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,22 @@
11
Function name: issue_83601::main
2-
Raw bytes (76): 0x[01, 01, 01, 05, 09, 0e, 01, 06, 01, 00, 0a, 01, 01, 09, 00, 0c, 01, 00, 0f, 00, 15, 01, 01, 05, 00, 0f, 05, 01, 09, 00, 0c, 05, 00, 0f, 00, 15, 05, 01, 05, 00, 0f, 02, 01, 05, 00, 0d, 02, 00, 0e, 00, 14, 02, 01, 05, 00, 0d, 02, 00, 0e, 00, 14, 02, 01, 05, 00, 0d, 02, 00, 0e, 00, 14, 02, 01, 01, 00, 02]
2+
Raw bytes (74): 0x[01, 01, 00, 0e, 01, 06, 01, 00, 0a, 01, 01, 09, 00, 0c, 01, 00, 0f, 00, 15, 01, 01, 05, 00, 0f, 01, 01, 09, 00, 0c, 01, 00, 0f, 00, 15, 01, 01, 05, 00, 0f, 01, 01, 05, 00, 0d, 01, 00, 0e, 00, 14, 01, 01, 05, 00, 0d, 01, 00, 0e, 00, 14, 01, 01, 05, 00, 0d, 01, 00, 0e, 00, 14, 01, 01, 01, 00, 02]
33
Number of files: 1
44
- file 0 => $DIR/issue-83601.rs
5-
Number of expressions: 1
6-
- expression 0 operands: lhs = Counter(1), rhs = Counter(2)
5+
Number of expressions: 0
76
Number of file 0 mappings: 14
87
- Code(Counter(0)) at (prev + 6, 1) to (start + 0, 10)
98
- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 12)
109
- Code(Counter(0)) at (prev + 0, 15) to (start + 0, 21)
1110
- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 15)
12-
- Code(Counter(1)) at (prev + 1, 9) to (start + 0, 12)
13-
- Code(Counter(1)) at (prev + 0, 15) to (start + 0, 21)
14-
- Code(Counter(1)) at (prev + 1, 5) to (start + 0, 15)
15-
- Code(Expression(0, Sub)) at (prev + 1, 5) to (start + 0, 13)
16-
= (c1 - c2)
17-
- Code(Expression(0, Sub)) at (prev + 0, 14) to (start + 0, 20)
18-
= (c1 - c2)
19-
- Code(Expression(0, Sub)) at (prev + 1, 5) to (start + 0, 13)
20-
= (c1 - c2)
21-
- Code(Expression(0, Sub)) at (prev + 0, 14) to (start + 0, 20)
22-
= (c1 - c2)
23-
- Code(Expression(0, Sub)) at (prev + 1, 5) to (start + 0, 13)
24-
= (c1 - c2)
25-
- Code(Expression(0, Sub)) at (prev + 0, 14) to (start + 0, 20)
26-
= (c1 - c2)
27-
- Code(Expression(0, Sub)) at (prev + 1, 1) to (start + 0, 2)
28-
= (c1 - c2)
29-
Highest counter ID seen: c1
11+
- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 12)
12+
- Code(Counter(0)) at (prev + 0, 15) to (start + 0, 21)
13+
- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 15)
14+
- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 13)
15+
- Code(Counter(0)) at (prev + 0, 14) to (start + 0, 20)
16+
- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 13)
17+
- Code(Counter(0)) at (prev + 0, 14) to (start + 0, 20)
18+
- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 13)
19+
- Code(Counter(0)) at (prev + 0, 14) to (start + 0, 20)
20+
- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2)
21+
Highest counter ID seen: c0
3022

0 commit comments

Comments
 (0)