Skip to content

Commit 08b7b9c

Browse files
Auto merge of #145737 - cjgillot:gvn-valueset, r=<try>
GVN: stop hashing opaque values
2 parents c5a6a7b + 17e3e9c commit 08b7b9c

File tree

3 files changed

+144
-44
lines changed

3 files changed

+144
-44
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4292,6 +4292,7 @@ name = "rustc_mir_transform"
42924292
version = "0.0.0"
42934293
dependencies = [
42944294
"either",
4295+
"hashbrown",
42954296
"itertools",
42964297
"rustc_abi",
42974298
"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: 142 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -85,15 +85,17 @@
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 rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, VariantIdx};
9193
use rustc_const_eval::const_eval::DummyMachine;
9294
use rustc_const_eval::interpret::{
9395
ImmTy, Immediate, InterpCx, MemPlaceMeta, MemoryKind, OpTy, Projectable, Scalar,
9496
intern_const_alloc_for_constprop,
9597
};
96-
use rustc_data_structures::fx::{FxIndexSet, MutableValues};
98+
use rustc_data_structures::fx::FxHasher;
9799
use rustc_data_structures::graph::dominators::Dominators;
98100
use rustc_hir::def::DefKind;
99101
use rustc_index::bit_set::DenseBitSet;
@@ -151,9 +153,17 @@ impl<'tcx> crate::MirPass<'tcx> for GVN {
151153
}
152154

153155
newtype_index! {
156+
#[debug_format = "_v{}"]
154157
struct VnIndex {}
155158
}
156159

160+
newtype_index! {
161+
#[debug_format = "_o{}"]
162+
struct VnOpaque {}
163+
}
164+
165+
const DETERMINISTIC: VnOpaque = VnOpaque::MAX;
166+
157167
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
158168
enum AddressKind {
159169
Ref(BorrowKind),
@@ -165,14 +175,14 @@ enum Value<'tcx> {
165175
// Root values.
166176
/// Used to represent values we know nothing about.
167177
/// The `usize` is a counter incremented by `new_opaque`.
168-
Opaque(usize),
178+
Opaque(VnOpaque),
169179
/// Evaluated or unevaluated constant value.
170180
Constant {
171181
value: Const<'tcx>,
172182
/// Some constants do not have a deterministic value. To avoid merging two instances of the
173183
/// same `Const`, we assign them an additional integer index.
174-
// `disambiguator` is 0 iff the constant is deterministic.
175-
disambiguator: usize,
184+
// `disambiguator` is `DETERMINISTIC` iff the constant is deterministic.
185+
disambiguator: VnOpaque,
176186
},
177187
/// An aggregate value, either tuple/closure/struct/enum.
178188
/// This does not contain unions, as we cannot reason with the value.
@@ -191,7 +201,7 @@ enum Value<'tcx> {
191201
place: Place<'tcx>,
192202
kind: AddressKind,
193203
/// Give each borrow and pointer a different provenance, so we don't merge them.
194-
provenance: usize,
204+
provenance: VnOpaque,
195205
},
196206

197207
// Extractions.
@@ -212,6 +222,85 @@ enum Value<'tcx> {
212222
},
213223
}
214224

225+
struct ValueSet<'tcx> {
226+
indices: HashTable<VnIndex>,
227+
hashes: IndexVec<VnIndex, u64>,
228+
values: IndexVec<VnIndex, Value<'tcx>>,
229+
types: IndexVec<VnIndex, Ty<'tcx>>,
230+
opaques: IndexVec<VnOpaque, VnIndex>,
231+
}
232+
233+
impl<'tcx> ValueSet<'tcx> {
234+
fn new(num_values: usize) -> ValueSet<'tcx> {
235+
ValueSet {
236+
indices: HashTable::with_capacity(num_values),
237+
hashes: IndexVec::with_capacity(num_values),
238+
values: IndexVec::with_capacity(num_values),
239+
types: IndexVec::with_capacity(num_values),
240+
opaques: IndexVec::with_capacity(num_values),
241+
}
242+
}
243+
244+
#[inline]
245+
fn insert_unique(
246+
&mut self,
247+
ty: Ty<'tcx>,
248+
value: impl FnOnce(VnOpaque) -> Value<'tcx>,
249+
) -> VnIndex {
250+
let index = self.hashes.push(0);
251+
let _index = self.types.push(ty);
252+
debug_assert_eq!(index, _index);
253+
let opaque = self.opaques.push(index);
254+
let _index = self.values.push(value(opaque));
255+
debug_assert_eq!(index, _index);
256+
index
257+
}
258+
259+
#[allow(rustc::pass_by_value)]
260+
fn insert(&mut self, value: Value<'tcx>, ty: Ty<'tcx>) -> (VnIndex, bool) {
261+
let hash: u64 = {
262+
let mut h = FxHasher::default();
263+
value.hash(&mut h);
264+
ty.hash(&mut h);
265+
h.finish()
266+
};
267+
268+
let eq = |index: &VnIndex| self.values[*index] == value && self.types[*index] == ty;
269+
let hasher = |index: &VnIndex| self.hashes[*index];
270+
match self.indices.entry(hash, eq, hasher) {
271+
Entry::Occupied(entry) => {
272+
let index = *entry.get();
273+
(index, false)
274+
}
275+
Entry::Vacant(entry) => {
276+
let index = self.hashes.push(hash);
277+
entry.insert(index);
278+
let _index = self.values.push(value);
279+
debug_assert_eq!(index, _index);
280+
let _index = self.types.push(ty);
281+
debug_assert_eq!(index, _index);
282+
(index, true)
283+
}
284+
}
285+
}
286+
287+
#[inline]
288+
fn value(&self, index: VnIndex) -> &Value<'tcx> {
289+
&self.values[index]
290+
}
291+
292+
#[inline]
293+
fn ty(&self, index: VnIndex) -> Ty<'tcx> {
294+
self.types[index]
295+
}
296+
297+
#[inline]
298+
fn forget(&mut self, index: VnIndex) {
299+
let opaque = self.opaques.push(index);
300+
self.values[index] = Value::Opaque(opaque);
301+
}
302+
}
303+
215304
struct VnState<'body, 'tcx> {
216305
tcx: TyCtxt<'tcx>,
217306
ecx: InterpCx<'tcx, DummyMachine>,
@@ -222,11 +311,9 @@ struct VnState<'body, 'tcx> {
222311
/// Locals that are assigned that value.
223312
// This vector does not hold all the values of `VnIndex` that we create.
224313
rev_locals: IndexVec<VnIndex, SmallVec<[Local; 1]>>,
225-
values: FxIndexSet<(Value<'tcx>, Ty<'tcx>)>,
314+
values: ValueSet<'tcx>,
226315
/// Values evaluated as constants if possible.
227316
evaluated: IndexVec<VnIndex, Option<OpTy<'tcx>>>,
228-
/// Counter to generate different values.
229-
next_opaque: usize,
230317
/// Cache the deref values.
231318
derefs: Vec<VnIndex>,
232319
ssa: &'body SsaLocals,
@@ -257,9 +344,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
257344
is_coroutine: body.coroutine.is_some(),
258345
locals: IndexVec::from_elem(None, local_decls),
259346
rev_locals: IndexVec::with_capacity(num_values),
260-
values: FxIndexSet::with_capacity_and_hasher(num_values, Default::default()),
347+
values: ValueSet::new(num_values),
261348
evaluated: IndexVec::with_capacity(num_values),
262-
next_opaque: 1,
263349
derefs: Vec::new(),
264350
ssa,
265351
dominators,
@@ -273,8 +359,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
273359

274360
#[instrument(level = "trace", skip(self), ret)]
275361
fn insert(&mut self, ty: Ty<'tcx>, value: Value<'tcx>) -> VnIndex {
276-
let (index, new) = self.values.insert_full((value, ty));
277-
let index = VnIndex::from_usize(index);
362+
let (index, new) = self.values.insert(value, ty);
278363
if new {
279364
// Grow `evaluated` and `rev_locals` here to amortize the allocations.
280365
let evaluated = self.eval_to_const(index);
@@ -286,18 +371,16 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
286371
index
287372
}
288373

289-
fn next_opaque(&mut self) -> usize {
290-
let next_opaque = self.next_opaque;
291-
self.next_opaque += 1;
292-
next_opaque
293-
}
294-
295374
/// Create a new `Value` for which we have no information at all, except that it is distinct
296375
/// from all the others.
297376
#[instrument(level = "trace", skip(self), ret)]
298377
fn new_opaque(&mut self, ty: Ty<'tcx>) -> VnIndex {
299-
let value = Value::Opaque(self.next_opaque());
300-
self.insert(ty, value)
378+
let index = self.values.insert_unique(ty, Value::Opaque);
379+
let _index = self.evaluated.push(None);
380+
debug_assert_eq!(index, _index);
381+
let _index = self.rev_locals.push(SmallVec::new());
382+
debug_assert_eq!(index, _index);
383+
index
301384
}
302385

303386
/// Create a new `Value::Address` distinct from all the others.
@@ -310,18 +393,49 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
310393
}
311394
AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, pty, mutbl.to_mutbl_lossy()),
312395
};
313-
let value = Value::Address { place, kind, provenance: self.next_opaque() };
314-
self.insert(ty, value)
396+
let index =
397+
self.values.insert_unique(ty, |provenance| Value::Address { place, kind, provenance });
398+
let evaluated = self.eval_to_const(index);
399+
let _index = self.evaluated.push(evaluated);
400+
debug_assert_eq!(index, _index);
401+
let _index = self.rev_locals.push(SmallVec::new());
402+
debug_assert_eq!(index, _index);
403+
index
404+
}
405+
406+
#[instrument(level = "trace", skip(self), ret)]
407+
fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex {
408+
let (index, new) = if value.is_deterministic() {
409+
// The constant is deterministic, no need to disambiguate.
410+
let constant = Value::Constant { value, disambiguator: DETERMINISTIC };
411+
self.values.insert(constant, value.ty())
412+
} else {
413+
// Multiple mentions of this constant will yield different values,
414+
// so assign a different `disambiguator` to ensure they do not get the same `VnIndex`.
415+
let index = self.values.insert_unique(value.ty(), |disambiguator| {
416+
debug_assert_ne!(disambiguator, DETERMINISTIC);
417+
Value::Constant { value, disambiguator }
418+
});
419+
(index, true)
420+
};
421+
if new {
422+
let evaluated = self.eval_to_const(index);
423+
let _index = self.evaluated.push(evaluated);
424+
debug_assert_eq!(index, _index);
425+
let _index = self.rev_locals.push(SmallVec::new());
426+
debug_assert_eq!(index, _index);
427+
}
428+
index
315429
}
316430

317431
#[inline]
318432
fn get(&self, index: VnIndex) -> &Value<'tcx> {
319-
&self.values.get_index(index.as_usize()).unwrap().0
433+
self.values.value(index)
320434
}
321435

322436
#[inline]
323437
fn ty(&self, index: VnIndex) -> Ty<'tcx> {
324-
self.values.get_index(index.as_usize()).unwrap().1
438+
self.values.ty(index)
325439
}
326440

327441
/// Record that `local` is assigned `value`. `local` must be SSA.
@@ -332,33 +446,18 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
332446
self.rev_locals[value].push(local);
333447
}
334448

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

357456
fn insert_scalar(&mut self, ty: Ty<'tcx>, scalar: Scalar) -> VnIndex {
358457
// Scalars are deterministic.
359458
let value = Const::from_scalar(self.tcx, scalar, ty);
360459
debug_assert!(value.is_deterministic());
361-
self.insert(ty, Value::Constant { value, disambiguator: 0 })
460+
self.insert(ty, Value::Constant { value, disambiguator: DETERMINISTIC })
362461
}
363462

364463
fn insert_tuple(&mut self, ty: Ty<'tcx>, values: Vec<VnIndex>) -> VnIndex {
@@ -373,8 +472,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
373472

374473
fn invalidate_derefs(&mut self) {
375474
for deref in std::mem::take(&mut self.derefs) {
376-
let opaque = self.next_opaque();
377-
self.values.get_index_mut2(deref.index()).unwrap().0 = Value::Opaque(opaque);
475+
self.values.forget(deref);
378476
}
379477
}
380478

@@ -1615,7 +1713,7 @@ impl<'tcx> VnState<'_, 'tcx> {
16151713
// This was already constant in MIR, do not change it. If the constant is not
16161714
// deterministic, adding an additional mention of it in MIR will not give the same value as
16171715
// the former mention.
1618-
if let Value::Constant { value, disambiguator: 0 } = *self.get(index) {
1716+
if let Value::Constant { value, disambiguator: DETERMINISTIC } = *self.get(index) {
16191717
debug_assert!(value.is_deterministic());
16201718
return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value });
16211719
}

0 commit comments

Comments
 (0)