Skip to content

Commit 290a3bc

Browse files
committed
Merge branch 'gvn-valueset' into local-value-numbering
2 parents e8d23b5 + 17e3e9c commit 290a3bc

File tree

3 files changed

+153
-52
lines changed

3 files changed

+153
-52
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: 151 additions & 52 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 rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, VariantIdx};
9193
use rustc_arena::DroplessArena;
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::{FxHashMap, FxIndexSet, MutableValues};
99+
use rustc_data_structures::fx::{FxHashMap, FxHasher};
98100
use rustc_data_structures::graph::dominators::Dominators;
99101
use rustc_hir::def::DefKind;
100102
use rustc_index::bit_set::DenseBitSet;
@@ -149,9 +151,17 @@ impl<'tcx> crate::MirPass<'tcx> for GVN {
149151
}
150152

151153
newtype_index! {
154+
#[debug_format = "_v{}"]
152155
struct VnIndex {}
153156
}
154157

158+
newtype_index! {
159+
#[debug_format = "_o{}"]
160+
struct VnOpaque {}
161+
}
162+
163+
const DETERMINISTIC: VnOpaque = VnOpaque::MAX;
164+
155165
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
156166
enum AddressKind {
157167
Ref(BorrowKind),
@@ -171,14 +181,14 @@ enum Value<'a, 'tcx> {
171181
// Root values.
172182
/// Used to represent values we know nothing about.
173183
/// The `usize` is a counter incremented by `new_opaque`.
174-
Opaque(usize),
184+
Opaque(VnOpaque),
175185
/// Evaluated or unevaluated constant value.
176186
Constant {
177187
value: Const<'tcx>,
178188
/// Some constants do not have a deterministic value. To avoid merging two instances of the
179189
/// same `Const`, we assign them an additional integer index.
180-
// `disambiguator` is 0 iff the constant is deterministic.
181-
disambiguator: usize,
190+
// `disambiguator` is `DETERMINISTIC` iff the constant is deterministic.
191+
disambiguator: VnOpaque,
182192
},
183193
/// An aggregate value, either tuple/closure/struct/enum.
184194
/// This does not contain unions, as we cannot reason with the value.
@@ -200,7 +210,7 @@ enum Value<'a, 'tcx> {
200210
projection: &'a [ProjectionElem<VnIndex, Ty<'tcx>>],
201211
kind: AddressKind,
202212
/// Give each borrow and pointer a different provenance, so we don't merge them.
203-
provenance: usize,
213+
provenance: VnOpaque,
204214
},
205215

206216
// Extractions.
@@ -221,6 +231,85 @@ enum Value<'a, 'tcx> {
221231
},
222232
}
223233

234+
struct ValueSet<'a, 'tcx> {
235+
indices: HashTable<VnIndex>,
236+
hashes: IndexVec<VnIndex, u64>,
237+
values: IndexVec<VnIndex, Value<'a, 'tcx>>,
238+
types: IndexVec<VnIndex, Ty<'tcx>>,
239+
opaques: IndexVec<VnOpaque, VnIndex>,
240+
}
241+
242+
impl<'a, 'tcx> ValueSet<'a, 'tcx> {
243+
fn new(num_values: usize) -> ValueSet<'a, 'tcx> {
244+
ValueSet {
245+
indices: HashTable::with_capacity(num_values),
246+
hashes: IndexVec::with_capacity(num_values),
247+
values: IndexVec::with_capacity(num_values),
248+
types: IndexVec::with_capacity(num_values),
249+
opaques: IndexVec::with_capacity(num_values),
250+
}
251+
}
252+
253+
#[inline]
254+
fn insert_unique(
255+
&mut self,
256+
ty: Ty<'tcx>,
257+
value: impl FnOnce(VnOpaque) -> Value<'a, 'tcx>,
258+
) -> VnIndex {
259+
let index = self.hashes.push(0);
260+
let _index = self.types.push(ty);
261+
debug_assert_eq!(index, _index);
262+
let opaque = self.opaques.push(index);
263+
let _index = self.values.push(value(opaque));
264+
debug_assert_eq!(index, _index);
265+
index
266+
}
267+
268+
#[allow(rustc::pass_by_value)]
269+
fn insert(&mut self, value: Value<'a, 'tcx>, ty: Ty<'tcx>) -> (VnIndex, bool) {
270+
let hash: u64 = {
271+
let mut h = FxHasher::default();
272+
value.hash(&mut h);
273+
ty.hash(&mut h);
274+
h.finish()
275+
};
276+
277+
let eq = |index: &VnIndex| self.values[*index] == value && self.types[*index] == ty;
278+
let hasher = |index: &VnIndex| self.hashes[*index];
279+
match self.indices.entry(hash, eq, hasher) {
280+
Entry::Occupied(entry) => {
281+
let index = *entry.get();
282+
(index, false)
283+
}
284+
Entry::Vacant(entry) => {
285+
let index = self.hashes.push(hash);
286+
entry.insert(index);
287+
let _index = self.values.push(value);
288+
debug_assert_eq!(index, _index);
289+
let _index = self.types.push(ty);
290+
debug_assert_eq!(index, _index);
291+
(index, true)
292+
}
293+
}
294+
}
295+
296+
#[inline]
297+
fn value(&self, index: VnIndex) -> Value<'a, 'tcx> {
298+
self.values[index]
299+
}
300+
301+
#[inline]
302+
fn ty(&self, index: VnIndex) -> Ty<'tcx> {
303+
self.types[index]
304+
}
305+
306+
#[inline]
307+
fn forget(&mut self, index: VnIndex) {
308+
let opaque = self.opaques.push(index);
309+
self.values[index] = Value::Opaque(opaque);
310+
}
311+
}
312+
224313
struct VnState<'body, 'a, 'tcx> {
225314
tcx: TyCtxt<'tcx>,
226315
ecx: InterpCx<'tcx, DummyMachine>,
@@ -233,11 +322,9 @@ struct VnState<'body, 'a, 'tcx> {
233322
rev_locals_ssa: IndexVec<VnIndex, SmallVec<[(Local, Location); 1]>>,
234323
// This vector holds the locals that are not SSA.
235324
rev_locals_non_ssa: FxHashMap<VnIndex, SmallVec<[(Local, Location); 1]>>,
236-
values: FxIndexSet<(Value<'a, 'tcx>, Ty<'tcx>)>,
325+
values: ValueSet<'a, 'tcx>,
237326
/// Values evaluated as constants if possible.
238327
evaluated: IndexVec<VnIndex, Option<OpTy<'tcx>>>,
239-
/// Counter to generate different values.
240-
next_opaque: usize,
241328
/// Cache the deref values.
242329
derefs: Vec<VnIndex>,
243330
ssa: &'body SsaLocals,
@@ -271,9 +358,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
271358
locals: IndexVec::with_capacity(body.local_decls.len()),
272359
rev_locals_ssa: IndexVec::with_capacity(num_values),
273360
rev_locals_non_ssa: FxHashMap::default(),
274-
values: FxIndexSet::with_capacity_and_hasher(num_values, Default::default()),
361+
values: ValueSet::new(num_values),
275362
evaluated: IndexVec::with_capacity(num_values),
276-
next_opaque: 1,
277363
derefs: Vec::new(),
278364
ssa,
279365
dominators,
@@ -299,10 +385,9 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
299385

300386
#[instrument(level = "trace", skip(self), ret)]
301387
fn insert(&mut self, ty: Ty<'tcx>, value: Value<'a, 'tcx>) -> VnIndex {
302-
let (index, new) = self.values.insert_full((value, ty));
303-
let index = VnIndex::from_usize(index);
388+
let (index, new) = self.values.insert(value, ty);
304389
if new {
305-
// Grow `evaluated` and `rev_locals` here to amortize the allocations.
390+
// Grow `evaluated` and `rev_locals_ssa` here to amortize the allocations.
306391
let evaluated = self.eval_to_const(index);
307392
let _index = self.evaluated.push(evaluated);
308393
debug_assert_eq!(index, _index);
@@ -312,18 +397,16 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
312397
index
313398
}
314399

315-
fn next_opaque(&mut self) -> usize {
316-
let next_opaque = self.next_opaque;
317-
self.next_opaque += 1;
318-
next_opaque
319-
}
320-
321400
/// Create a new `Value` for which we have no information at all, except that it is distinct
322401
/// from all the others.
323402
#[instrument(level = "trace", skip(self), ret)]
324403
fn new_opaque(&mut self, ty: Ty<'tcx>) -> VnIndex {
325-
let value = Value::Opaque(self.next_opaque());
326-
self.insert(ty, value)
404+
let index = self.values.insert_unique(ty, Value::Opaque);
405+
let _index = self.evaluated.push(None);
406+
debug_assert_eq!(index, _index);
407+
let _index = self.rev_locals_ssa.push(SmallVec::new());
408+
debug_assert_eq!(index, _index);
409+
index
327410
}
328411

329412
/// Create a new `Value::Address` distinct from all the others.
@@ -352,25 +435,57 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
352435
AddressBase::Local(place.local)
353436
};
354437
// Do not try evaluating inside `Index`, this has been done by `simplify_place_value`.
355-
let projection = self
356-
.arena
357-
.try_alloc_from_iter(
358-
projection
359-
.map(|proj| proj.try_map(|value| Some(self.locals[value]), |ty| ty).ok_or(())),
360-
)
361-
.ok()?;
362-
let value = Value::Address { base, projection, kind, provenance: self.next_opaque() };
363-
Some(self.insert(ty, value))
438+
let projection = projection
439+
.map(|proj| proj.try_map(|value| Some(self.locals[value]), |ty| ty).ok_or(()));
440+
let projection = self.arena.try_alloc_from_iter(projection).ok()?;
441+
442+
let index = self.values.insert_unique(ty, |provenance| Value::Address {
443+
base,
444+
projection,
445+
kind,
446+
provenance,
447+
});
448+
let evaluated = self.eval_to_const(index);
449+
let _index = self.evaluated.push(evaluated);
450+
debug_assert_eq!(index, _index);
451+
let _index = self.rev_locals_ssa.push(SmallVec::new());
452+
debug_assert_eq!(index, _index);
453+
Some(index)
454+
}
455+
456+
#[instrument(level = "trace", skip(self), ret)]
457+
fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex {
458+
let (index, new) = if value.is_deterministic() {
459+
// The constant is deterministic, no need to disambiguate.
460+
let constant = Value::Constant { value, disambiguator: DETERMINISTIC };
461+
self.values.insert(constant, value.ty())
462+
} else {
463+
// Multiple mentions of this constant will yield different values,
464+
// so assign a different `disambiguator` to ensure they do not get the same `VnIndex`.
465+
let index = self.values.insert_unique(value.ty(), |disambiguator| {
466+
debug_assert_ne!(disambiguator, DETERMINISTIC);
467+
Value::Constant { value, disambiguator }
468+
});
469+
(index, true)
470+
};
471+
if new {
472+
let evaluated = self.eval_to_const(index);
473+
let _index = self.evaluated.push(evaluated);
474+
debug_assert_eq!(index, _index);
475+
let _index = self.rev_locals_ssa.push(SmallVec::new());
476+
debug_assert_eq!(index, _index);
477+
}
478+
index
364479
}
365480

366481
#[inline]
367482
fn get(&self, index: VnIndex) -> Value<'a, 'tcx> {
368-
self.values.get_index(index.as_usize()).unwrap().0
483+
self.values.value(index)
369484
}
370485

371486
#[inline]
372487
fn ty(&self, index: VnIndex) -> Ty<'tcx> {
373-
self.values.get_index(index.as_usize()).unwrap().1
488+
self.values.ty(index)
374489
}
375490

376491
/// Record that `local` is assigned `value`.
@@ -406,33 +521,18 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
406521
}
407522
}
408523

409-
fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex {
410-
let disambiguator = if value.is_deterministic() {
411-
// The constant is deterministic, no need to disambiguate.
412-
0
413-
} else {
414-
// Multiple mentions of this constant will yield different values,
415-
// so assign a different `disambiguator` to ensure they do not get the same `VnIndex`.
416-
let disambiguator = self.next_opaque();
417-
// `disambiguator: 0` means deterministic.
418-
debug_assert_ne!(disambiguator, 0);
419-
disambiguator
420-
};
421-
self.insert(value.ty(), Value::Constant { value, disambiguator })
422-
}
423-
424524
fn insert_bool(&mut self, flag: bool) -> VnIndex {
425525
// Booleans are deterministic.
426526
let value = Const::from_bool(self.tcx, flag);
427527
debug_assert!(value.is_deterministic());
428-
self.insert(self.tcx.types.bool, Value::Constant { value, disambiguator: 0 })
528+
self.insert(self.tcx.types.bool, Value::Constant { value, disambiguator: DETERMINISTIC })
429529
}
430530

431531
fn insert_scalar(&mut self, ty: Ty<'tcx>, scalar: Scalar) -> VnIndex {
432532
// Scalars are deterministic.
433533
let value = Const::from_scalar(self.tcx, scalar, ty);
434534
debug_assert!(value.is_deterministic());
435-
self.insert(ty, Value::Constant { value, disambiguator: 0 })
535+
self.insert(ty, Value::Constant { value, disambiguator: DETERMINISTIC })
436536
}
437537

438538
fn insert_tuple(&mut self, ty: Ty<'tcx>, values: &[VnIndex]) -> VnIndex {
@@ -447,8 +547,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
447547

448548
fn invalidate_derefs(&mut self) {
449549
for deref in std::mem::take(&mut self.derefs) {
450-
let opaque = self.next_opaque();
451-
self.values.get_index_mut2(deref.index()).unwrap().0 = Value::Opaque(opaque);
550+
self.values.forget(deref);
452551
}
453552
}
454553

@@ -1714,7 +1813,7 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
17141813
// This was already constant in MIR, do not change it. If the constant is not
17151814
// deterministic, adding an additional mention of it in MIR will not give the same value as
17161815
// the former mention.
1717-
if let Value::Constant { value, disambiguator: 0 } = self.get(index) {
1816+
if let Value::Constant { value, disambiguator: DETERMINISTIC } = self.get(index) {
17181817
debug_assert!(value.is_deterministic());
17191818
return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value });
17201819
}

0 commit comments

Comments
 (0)