Skip to content

Commit 477da90

Browse files
committed
Extend GVN to perform local value numbering.
1 parent e86eeb3 commit 477da90

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
@@ -95,7 +95,7 @@ use rustc_const_eval::interpret::{
9595
ImmTy, Immediate, InterpCx, MemPlaceMeta, MemoryKind, OpTy, Projectable, Scalar,
9696
intern_const_alloc_for_constprop,
9797
};
98-
use rustc_data_structures::fx::{FxIndexSet, MutableValues};
98+
use rustc_data_structures::fx::{FxHashMap, FxIndexSet, MutableValues};
9999
use rustc_data_structures::graph::dominators::Dominators;
100100
use rustc_hir::def::DefKind;
101101
use rustc_index::bit_set::DenseBitSet;
@@ -233,8 +233,11 @@ struct VnState<'body, 'a, 'tcx> {
233233
/// Value stored in each local.
234234
locals: IndexVec<Local, Option<VnIndex>>,
235235
/// Locals that are assigned that value.
236-
// This vector does not hold all the values of `VnIndex` that we create.
237-
rev_locals: IndexVec<VnIndex, SmallVec<[Local; 1]>>,
236+
// This vector holds the locals that are SSA.
237+
rev_locals_ssa: IndexVec<VnIndex, SmallVec<[Local; 1]>>,
238+
// This map holds the locals that are not SSA. This map is cleared at the end of each block.
239+
// Therefore, we do not need a location, the local always appears before the current location.
240+
rev_locals_non_ssa: FxHashMap<VnIndex, SmallVec<[Local; 1]>>,
238241
values: FxIndexSet<(Value<'a, 'tcx>, Ty<'tcx>)>,
239242
/// Values evaluated as constants if possible.
240243
evaluated: IndexVec<VnIndex, Option<OpTy<'tcx>>>,
@@ -270,8 +273,9 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
270273
ecx: InterpCx::new(tcx, DUMMY_SP, typing_env, DummyMachine),
271274
local_decls,
272275
is_coroutine: body.coroutine.is_some(),
273-
locals: IndexVec::from_elem(None, local_decls),
274-
rev_locals: IndexVec::with_capacity(num_values),
276+
locals: IndexVec::from_elem(None, &body.local_decls),
277+
rev_locals_ssa: IndexVec::with_capacity(num_values),
278+
rev_locals_non_ssa: FxHashMap::default(),
275279
values: FxIndexSet::with_capacity_and_hasher(num_values, Default::default()),
276280
evaluated: IndexVec::with_capacity(num_values),
277281
next_opaque: 1,
@@ -296,7 +300,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
296300
let evaluated = self.eval_to_const(index);
297301
let _index = self.evaluated.push(evaluated);
298302
debug_assert_eq!(index, _index);
299-
let _index = self.rev_locals.push(SmallVec::new());
303+
let _index = self.rev_locals_ssa.push(Default::default());
300304
debug_assert_eq!(index, _index);
301305
}
302306
index
@@ -334,7 +338,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
334338

335339
let mut projection = place.projection.iter();
336340
let base = if place.is_indirect_first_projection() {
337-
let base = self.locals[place.local]?;
341+
let base = self.local(place.local);
338342
// Skip the initial `Deref`.
339343
projection.next();
340344
AddressBase::Deref(base)
@@ -345,7 +349,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
345349
let projection = self
346350
.arena
347351
.try_alloc_from_iter(
348-
projection.map(|proj| proj.try_map(|value| self.locals[value], |ty| ty).ok_or(())),
352+
projection
353+
.map(|proj| proj.try_map(|value| Some(self.local(value)), |ty| ty).ok_or(())),
349354
)
350355
.ok()?;
351356
let value = Value::Address { base, projection, kind, provenance: self.next_opaque() };
@@ -362,12 +367,49 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
362367
self.values.get_index(index.as_usize()).unwrap().1
363368
}
364369

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

373415
fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex {
@@ -627,7 +669,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
627669
let (mut place_ty, mut value) = match base {
628670
// The base is a local, so we take the local's value and project from it.
629671
AddressBase::Local(local) => {
630-
let local = self.locals[local]?;
672+
let local = self.local(local);
631673
let place_ty = PlaceTy::from_ty(self.ty(local));
632674
(place_ty, local)
633675
}
@@ -733,7 +775,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
733775
// If the projection is indirect, we treat the local as a value, so can replace it with
734776
// another local.
735777
if place.is_indirect_first_projection()
736-
&& let Some(base) = self.locals[place.local]
778+
&& let base = self.local(place.local)
737779
&& let Some(new_local) = self.try_as_local(base, location)
738780
&& place.local != new_local
739781
{
@@ -745,9 +787,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
745787

746788
for i in 0..projection.len() {
747789
let elem = projection[i];
748-
if let ProjectionElem::Index(idx_local) = elem
749-
&& let Some(idx) = self.locals[idx_local]
750-
{
790+
if let ProjectionElem::Index(idx_local) = elem {
791+
let idx = self.local(idx_local);
751792
if let Some(offset) = self.evaluated[idx].as_ref()
752793
&& let Some(offset) = self.ecx.read_target_usize(offset).discard_err()
753794
&& let Some(min_length) = offset.checked_add(1)
@@ -783,7 +824,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
783824
let mut place_ref = place.as_ref();
784825

785826
// Invariant: `value` holds the value up-to the `index`th projection excluded.
786-
let Some(mut value) = self.locals[place.local] else { return Err(place_ref) };
827+
let mut value = self.local(place.local);
787828
// Invariant: `value` has type `place_ty`, with optional downcast variant if needed.
788829
let mut place_ty = PlaceTy::from_ty(self.local_decls[place.local].ty);
789830
for (index, proj) in place.projection.iter().enumerate() {
@@ -794,7 +835,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
794835
place_ref = PlaceRef { local, projection: &place.projection[index..] };
795836
}
796837

797-
let Some(proj) = proj.try_map(|value| self.locals[value], |ty| ty) else {
838+
let Some(proj) = proj.try_map(|value| Some(self.local(value)), |ty| ty) else {
798839
return Err(place_ref);
799840
};
800841
let Some(ty_and_value) = self.project(place_ty, value, proj) else {
@@ -1677,11 +1718,17 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
16771718
/// If there is a local which is assigned `index`, and its assignment strictly dominates `loc`,
16781719
/// return it. If you used this local, add it to `reused_locals` to remove storage statements.
16791720
fn try_as_local(&mut self, index: VnIndex, loc: Location) -> Option<Local> {
1680-
let other = self.rev_locals.get(index)?;
1681-
other
1682-
.iter()
1683-
.find(|&&other| self.ssa.assignment_dominates(&self.dominators, other, loc))
1684-
.copied()
1721+
if let Some(ssa) = self.rev_locals_ssa.get(index)
1722+
&& let Some(other) = ssa
1723+
.iter()
1724+
.find(|&&other| self.ssa.assignment_dominates(&self.dominators, other, loc))
1725+
{
1726+
Some(*other)
1727+
} else if let Some(non_ssa) = self.rev_locals_non_ssa.get(&index) {
1728+
non_ssa.first().copied()
1729+
} else {
1730+
None
1731+
}
16851732
}
16861733
}
16871734

@@ -1690,11 +1737,20 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
16901737
self.tcx
16911738
}
16921739

1740+
fn visit_basic_block_data(&mut self, block: BasicBlock, bbdata: &mut BasicBlockData<'tcx>) {
1741+
self.rev_locals_non_ssa.clear();
1742+
for local in self.locals.indices() {
1743+
if !self.ssa.is_ssa(local) {
1744+
self.locals[local] = None;
1745+
}
1746+
}
1747+
self.super_basic_block_data(block, bbdata);
1748+
}
1749+
16931750
fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
16941751
self.simplify_place_projection(place, location);
1695-
if context.is_mutating_use() && place.is_indirect() {
1696-
// Non-local mutation maybe invalidate deref.
1697-
self.invalidate_derefs();
1752+
if context.is_mutating_use() {
1753+
self.discard_place(*place);
16981754
}
16991755
self.super_place(place, context, location);
17001756
}
@@ -1733,13 +1789,9 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
17331789
}
17341790
}
17351791

1736-
if lhs.is_indirect() {
1737-
// Non-local mutation maybe invalidate deref.
1738-
self.invalidate_derefs();
1739-
}
1792+
self.discard_place(*lhs);
17401793

17411794
if let Some(local) = lhs.as_local()
1742-
&& self.ssa.is_ssa(local)
17431795
&& let rvalue_ty = rvalue.ty(self.local_decls, self.tcx)
17441796
// FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark
17451797
// `local` as reusable if we have an exact type match.
@@ -1751,14 +1803,13 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
17511803
}
17521804

17531805
fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) {
1754-
if let Terminator { kind: TerminatorKind::Call { destination, .. }, .. } = terminator {
1755-
if let Some(local) = destination.as_local()
1756-
&& self.ssa.is_ssa(local)
1757-
{
1758-
let ty = self.local_decls[local].ty;
1759-
let opaque = self.new_opaque(ty);
1760-
self.assign(local, opaque);
1761-
}
1806+
self.super_terminator(terminator, location);
1807+
if let Terminator { kind: TerminatorKind::Call { destination, .. }, .. } = terminator
1808+
&& let Some(local) = destination.as_local()
1809+
{
1810+
let ty = self.local_decls[local].ty;
1811+
let opaque = self.new_opaque(ty);
1812+
self.assign(local, opaque);
17621813
}
17631814
// Function calls and ASM may invalidate (nested) derefs. We must handle them carefully.
17641815
// Currently, only preserving derefs for trivial terminators like SwitchInt and Goto.
@@ -1769,7 +1820,6 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
17691820
if !safe_to_preserve_derefs {
17701821
self.invalidate_derefs();
17711822
}
1772-
self.super_terminator(terminator, location);
17731823
}
17741824
}
17751825

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)