Skip to content

Commit 0f0fb39

Browse files
Auto merge of #145737 - cjgillot:gvn-valueset, r=<try>
GVN: stop hashing opaque values
2 parents 9c27f27 + 63f7076 commit 0f0fb39

File tree

3 files changed

+171
-44
lines changed

3 files changed

+171
-44
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4206,6 +4206,7 @@ name = "rustc_mir_transform"
42064206
version = "0.0.0"
42074207
dependencies = [
42084208
"either",
4209+
"hashbrown",
42094210
"itertools",
42104211
"rustc_abi",
42114212
"rustc_arena",

compiler/rustc_mir_transform/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ edition = "2024"
66
[dependencies]
77
# tidy-alphabetical-start
88
either = "1"
9+
hashbrown = "0.15"
910
itertools = "0.12"
1011
rustc_abi = { path = "../rustc_abi" }
1112
rustc_arena = { path = "../rustc_arena" }

compiler/rustc_mir_transform/src/gvn.rs

Lines changed: 169 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -85,16 +85,18 @@
8585
//! that contain `AllocId`s.
8686
8787
use std::borrow::Cow;
88+
use std::hash::{Hash, Hasher};
8889

8990
use either::Either;
91+
use hashbrown::hash_table::{Entry, HashTable};
9092
use itertools::Itertools as _;
9193
use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, VariantIdx};
9294
use rustc_const_eval::const_eval::DummyMachine;
9395
use rustc_const_eval::interpret::{
9496
ImmTy, Immediate, InterpCx, MemPlaceMeta, MemoryKind, OpTy, Projectable, Scalar,
9597
intern_const_alloc_for_constprop,
9698
};
97-
use rustc_data_structures::fx::{FxIndexSet, MutableValues};
99+
use rustc_data_structures::fx::FxHasher;
98100
use rustc_data_structures::graph::dominators::Dominators;
99101
use rustc_hir::def::DefKind;
100102
use rustc_index::bit_set::DenseBitSet;
@@ -152,9 +154,23 @@ impl<'tcx> crate::MirPass<'tcx> for GVN {
152154
}
153155

154156
newtype_index! {
157+
#[debug_format = "_v{}"]
155158
struct VnIndex {}
156159
}
157160

161+
#[derive(Copy, Clone, Debug, Eq)]
162+
struct VnOpaque;
163+
impl PartialEq for VnOpaque {
164+
fn eq(&self, _: &VnOpaque) -> bool {
165+
unreachable!()
166+
}
167+
}
168+
impl Hash for VnOpaque {
169+
fn hash<T: Hasher>(&self, _: &mut T) {
170+
unreachable!()
171+
}
172+
}
173+
158174
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
159175
enum AddressKind {
160176
Ref(BorrowKind),
@@ -166,14 +182,14 @@ enum Value<'tcx> {
166182
// Root values.
167183
/// Used to represent values we know nothing about.
168184
/// The `usize` is a counter incremented by `new_opaque`.
169-
Opaque(usize),
185+
Opaque(VnOpaque),
170186
/// Evaluated or unevaluated constant value.
171187
Constant {
172188
value: Const<'tcx>,
173189
/// Some constants do not have a deterministic value. To avoid merging two instances of the
174190
/// same `Const`, we assign them an additional integer index.
175-
// `disambiguator` is 0 iff the constant is deterministic.
176-
disambiguator: usize,
191+
// `disambiguator` is `None` iff the constant is deterministic.
192+
disambiguator: Option<VnOpaque>,
177193
},
178194
/// An aggregate value, either tuple/closure/struct/enum.
179195
/// This does not contain unions, as we cannot reason with the value.
@@ -192,7 +208,7 @@ enum Value<'tcx> {
192208
place: Place<'tcx>,
193209
kind: AddressKind,
194210
/// Give each borrow and pointer a different provenance, so we don't merge them.
195-
provenance: usize,
211+
provenance: VnOpaque,
196212
},
197213

198214
// Extractions.
@@ -213,6 +229,106 @@ enum Value<'tcx> {
213229
},
214230
}
215231

232+
/// Stores and deduplicates pairs of `(Value, Ty)` into in `VnIndex` numbered values.
233+
///
234+
/// This data structure is mostly a partial reimplementation of `FxIndexMap<VnIndex, (Value, Ty)>`.
235+
/// We do not use a regular `FxIndexMap` to skip hashing values that are unique by construction,
236+
/// like opaque values, address with provenance and non-deterministic constants.
237+
struct ValueSet<'tcx> {
238+
indices: HashTable<VnIndex>,
239+
hashes: IndexVec<VnIndex, u64>,
240+
values: IndexVec<VnIndex, Value<'tcx>>,
241+
types: IndexVec<VnIndex, Ty<'tcx>>,
242+
}
243+
244+
impl<'tcx> ValueSet<'tcx> {
245+
fn new(num_values: usize) -> ValueSet<'tcx> {
246+
ValueSet {
247+
indices: HashTable::with_capacity(num_values),
248+
hashes: IndexVec::with_capacity(num_values),
249+
values: IndexVec::with_capacity(num_values),
250+
types: IndexVec::with_capacity(num_values),
251+
}
252+
}
253+
254+
/// Insert a `(Value, Ty)` pair without hashing or deduplication.
255+
#[inline]
256+
fn insert_unique(
257+
&mut self,
258+
ty: Ty<'tcx>,
259+
value: impl FnOnce(VnOpaque) -> Value<'tcx>,
260+
) -> VnIndex {
261+
let value = value(VnOpaque);
262+
263+
debug_assert!(match value {
264+
Value::Opaque(_) | Value::Address { .. } => true,
265+
Value::Constant { disambiguator, .. } => disambiguator.is_some(),
266+
_ => false,
267+
});
268+
269+
let index = self.hashes.push(0);
270+
let _index = self.types.push(ty);
271+
debug_assert_eq!(index, _index);
272+
let _index = self.values.push(value);
273+
debug_assert_eq!(index, _index);
274+
index
275+
}
276+
277+
/// Insert a `(Value, Ty)` pair to be deduplicated.
278+
/// Returns `true` as second tuple field if this value did not exist previously.
279+
#[allow(rustc::pass_by_value)] // closures take `&VnIndex`
280+
fn insert(&mut self, ty: Ty<'tcx>, value: Value<'tcx>) -> (VnIndex, bool) {
281+
debug_assert!(match value {
282+
Value::Opaque(_) | Value::Address { .. } => false,
283+
Value::Constant { disambiguator, .. } => disambiguator.is_none(),
284+
_ => true,
285+
});
286+
287+
let hash: u64 = {
288+
let mut h = FxHasher::default();
289+
value.hash(&mut h);
290+
ty.hash(&mut h);
291+
h.finish()
292+
};
293+
294+
let eq = |index: &VnIndex| self.values[*index] == value && self.types[*index] == ty;
295+
let hasher = |index: &VnIndex| self.hashes[*index];
296+
match self.indices.entry(hash, eq, hasher) {
297+
Entry::Occupied(entry) => {
298+
let index = *entry.get();
299+
(index, false)
300+
}
301+
Entry::Vacant(entry) => {
302+
let index = self.hashes.push(hash);
303+
entry.insert(index);
304+
let _index = self.values.push(value);
305+
debug_assert_eq!(index, _index);
306+
let _index = self.types.push(ty);
307+
debug_assert_eq!(index, _index);
308+
(index, true)
309+
}
310+
}
311+
}
312+
313+
/// Return the `Value` associated with the given `VnIndex`.
314+
#[inline]
315+
fn value(&self, index: VnIndex) -> &Value<'tcx> {
316+
&self.values[index]
317+
}
318+
319+
/// Return the type associated with the given `VnIndex`.
320+
#[inline]
321+
fn ty(&self, index: VnIndex) -> Ty<'tcx> {
322+
self.types[index]
323+
}
324+
325+
/// Replace the value associated with `index` with an opaque value.
326+
#[inline]
327+
fn forget(&mut self, index: VnIndex) {
328+
self.values[index] = Value::Opaque(VnOpaque);
329+
}
330+
}
331+
216332
struct VnState<'body, 'tcx> {
217333
tcx: TyCtxt<'tcx>,
218334
ecx: InterpCx<'tcx, DummyMachine>,
@@ -223,11 +339,9 @@ struct VnState<'body, 'tcx> {
223339
/// Locals that are assigned that value.
224340
// This vector does not hold all the values of `VnIndex` that we create.
225341
rev_locals: IndexVec<VnIndex, SmallVec<[Local; 1]>>,
226-
values: FxIndexSet<(Value<'tcx>, Ty<'tcx>)>,
342+
values: ValueSet<'tcx>,
227343
/// Values evaluated as constants if possible.
228344
evaluated: IndexVec<VnIndex, Option<OpTy<'tcx>>>,
229-
/// Counter to generate different values.
230-
next_opaque: usize,
231345
/// Cache the deref values.
232346
derefs: Vec<VnIndex>,
233347
ssa: &'body SsaLocals,
@@ -258,9 +372,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
258372
is_coroutine: body.coroutine.is_some(),
259373
locals: IndexVec::from_elem(None, local_decls),
260374
rev_locals: IndexVec::with_capacity(num_values),
261-
values: FxIndexSet::with_capacity_and_hasher(num_values, Default::default()),
375+
values: ValueSet::new(num_values),
262376
evaluated: IndexVec::with_capacity(num_values),
263-
next_opaque: 1,
264377
derefs: Vec::new(),
265378
ssa,
266379
dominators,
@@ -274,8 +387,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
274387

275388
#[instrument(level = "trace", skip(self), ret)]
276389
fn insert(&mut self, ty: Ty<'tcx>, value: Value<'tcx>) -> VnIndex {
277-
let (index, new) = self.values.insert_full((value, ty));
278-
let index = VnIndex::from_usize(index);
390+
let (index, new) = self.values.insert(ty, value);
279391
if new {
280392
// Grow `evaluated` and `rev_locals` here to amortize the allocations.
281393
let evaluated = self.eval_to_const(index);
@@ -287,18 +399,16 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
287399
index
288400
}
289401

290-
fn next_opaque(&mut self) -> usize {
291-
let next_opaque = self.next_opaque;
292-
self.next_opaque += 1;
293-
next_opaque
294-
}
295-
296402
/// Create a new `Value` for which we have no information at all, except that it is distinct
297403
/// from all the others.
298404
#[instrument(level = "trace", skip(self), ret)]
299405
fn new_opaque(&mut self, ty: Ty<'tcx>) -> VnIndex {
300-
let value = Value::Opaque(self.next_opaque());
301-
self.insert(ty, value)
406+
let index = self.values.insert_unique(ty, Value::Opaque);
407+
let _index = self.evaluated.push(None);
408+
debug_assert_eq!(index, _index);
409+
let _index = self.rev_locals.push(SmallVec::new());
410+
debug_assert_eq!(index, _index);
411+
index
302412
}
303413

304414
/// Create a new `Value::Address` distinct from all the others.
@@ -311,18 +421,49 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
311421
}
312422
AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, pty, mutbl.to_mutbl_lossy()),
313423
};
314-
let value = Value::Address { place, kind, provenance: self.next_opaque() };
315-
self.insert(ty, value)
424+
let index =
425+
self.values.insert_unique(ty, |provenance| Value::Address { place, kind, provenance });
426+
let evaluated = self.eval_to_const(index);
427+
let _index = self.evaluated.push(evaluated);
428+
debug_assert_eq!(index, _index);
429+
let _index = self.rev_locals.push(SmallVec::new());
430+
debug_assert_eq!(index, _index);
431+
index
432+
}
433+
434+
#[instrument(level = "trace", skip(self), ret)]
435+
fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex {
436+
let (index, new) = if value.is_deterministic() {
437+
// The constant is deterministic, no need to disambiguate.
438+
let constant = Value::Constant { value, disambiguator: None };
439+
self.values.insert(value.ty(), constant)
440+
} else {
441+
// Multiple mentions of this constant will yield different values,
442+
// so assign a different `disambiguator` to ensure they do not get the same `VnIndex`.
443+
let index = self.values.insert_unique(value.ty(), |disambiguator| Value::Constant {
444+
value,
445+
disambiguator: Some(disambiguator),
446+
});
447+
(index, true)
448+
};
449+
if new {
450+
let evaluated = self.eval_to_const(index);
451+
let _index = self.evaluated.push(evaluated);
452+
debug_assert_eq!(index, _index);
453+
let _index = self.rev_locals.push(SmallVec::new());
454+
debug_assert_eq!(index, _index);
455+
}
456+
index
316457
}
317458

318459
#[inline]
319460
fn get(&self, index: VnIndex) -> &Value<'tcx> {
320-
&self.values.get_index(index.as_usize()).unwrap().0
461+
self.values.value(index)
321462
}
322463

323464
#[inline]
324465
fn ty(&self, index: VnIndex) -> Ty<'tcx> {
325-
self.values.get_index(index.as_usize()).unwrap().1
466+
self.values.ty(index)
326467
}
327468

328469
/// Record that `local` is assigned `value`. `local` must be SSA.
@@ -333,33 +474,18 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
333474
self.rev_locals[value].push(local);
334475
}
335476

336-
fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex {
337-
let disambiguator = if value.is_deterministic() {
338-
// The constant is deterministic, no need to disambiguate.
339-
0
340-
} else {
341-
// Multiple mentions of this constant will yield different values,
342-
// so assign a different `disambiguator` to ensure they do not get the same `VnIndex`.
343-
let disambiguator = self.next_opaque();
344-
// `disambiguator: 0` means deterministic.
345-
debug_assert_ne!(disambiguator, 0);
346-
disambiguator
347-
};
348-
self.insert(value.ty(), Value::Constant { value, disambiguator })
349-
}
350-
351477
fn insert_bool(&mut self, flag: bool) -> VnIndex {
352478
// Booleans are deterministic.
353479
let value = Const::from_bool(self.tcx, flag);
354480
debug_assert!(value.is_deterministic());
355-
self.insert(self.tcx.types.bool, Value::Constant { value, disambiguator: 0 })
481+
self.insert(self.tcx.types.bool, Value::Constant { value, disambiguator: None })
356482
}
357483

358484
fn insert_scalar(&mut self, ty: Ty<'tcx>, scalar: Scalar) -> VnIndex {
359485
// Scalars are deterministic.
360486
let value = Const::from_scalar(self.tcx, scalar, ty);
361487
debug_assert!(value.is_deterministic());
362-
self.insert(ty, Value::Constant { value, disambiguator: 0 })
488+
self.insert(ty, Value::Constant { value, disambiguator: None })
363489
}
364490

365491
fn insert_tuple(&mut self, ty: Ty<'tcx>, values: Vec<VnIndex>) -> VnIndex {
@@ -374,8 +500,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
374500

375501
fn invalidate_derefs(&mut self) {
376502
for deref in std::mem::take(&mut self.derefs) {
377-
let opaque = self.next_opaque();
378-
self.values.get_index_mut2(deref.index()).unwrap().0 = Value::Opaque(opaque);
503+
self.values.forget(deref);
379504
}
380505
}
381506

@@ -1605,7 +1730,7 @@ impl<'tcx> VnState<'_, 'tcx> {
16051730
// This was already constant in MIR, do not change it. If the constant is not
16061731
// deterministic, adding an additional mention of it in MIR will not give the same value as
16071732
// the former mention.
1608-
if let Value::Constant { value, disambiguator: 0 } = *self.get(index) {
1733+
if let Value::Constant { value, disambiguator: None } = *self.get(index) {
16091734
debug_assert!(value.is_deterministic());
16101735
return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value });
16111736
}

0 commit comments

Comments
 (0)