Skip to content

Commit ecfe721

Browse files
committed
Rollup merge of rust-lang#56324 - Zoxc:int-ext, r=nikomatsakis
Use raw_entry for more efficient interning Fixes rust-lang#56308 (comment)
2 parents 2584d92 + 946ea14 commit ecfe721

File tree

6 files changed

+157
-111
lines changed

6 files changed

+157
-111
lines changed

src/libarena/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ pub struct DroplessArena {
298298
unsafe impl Send for DroplessArena {}
299299

300300
impl Default for DroplessArena {
301+
#[inline]
301302
fn default() -> DroplessArena {
302303
DroplessArena {
303304
ptr: Cell::new(0 as *mut u8),
@@ -319,6 +320,7 @@ impl DroplessArena {
319320
false
320321
}
321322

323+
#[inline]
322324
fn align(&self, align: usize) {
323325
let final_address = ((self.ptr.get() as usize) + align - 1) & !(align - 1);
324326
self.ptr.set(final_address as *mut u8);

src/librustc/ty/context.rs

+74-111
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ use ty::CanonicalTy;
5353
use ty::CanonicalPolyFnSig;
5454
use util::nodemap::{DefIdMap, DefIdSet, ItemLocalMap};
5555
use util::nodemap::{FxHashMap, FxHashSet};
56+
use rustc_data_structures::interner::HashInterner;
5657
use smallvec::SmallVec;
5758
use rustc_data_structures::stable_hasher::{HashStable, hash_stable_hashmap,
5859
StableHasher, StableHasherResult,
@@ -113,7 +114,7 @@ pub struct GlobalArenas<'tcx> {
113114
const_allocs: TypedArena<interpret::Allocation>,
114115
}
115116

116-
type InternedSet<'tcx, T> = Lock<FxHashSet<Interned<'tcx, T>>>;
117+
type InternedSet<'tcx, T> = Lock<FxHashMap<Interned<'tcx, T>, ()>>;
117118

118119
pub struct CtxtInterners<'tcx> {
119120
/// The arena that types, regions, etc are allocated from
@@ -167,51 +168,39 @@ impl<'gcx: 'tcx, 'tcx> CtxtInterners<'tcx> {
167168
// determine that all contents are in the global tcx.
168169
// See comments on Lift for why we can't use that.
169170
if flags.flags.intersects(ty::TypeFlags::KEEP_IN_LOCAL_TCX) {
170-
let mut interner = local.type_.borrow_mut();
171-
if let Some(&Interned(ty)) = interner.get(&st) {
172-
return ty;
173-
}
174-
175-
let ty_struct = TyS {
176-
sty: st,
177-
flags: flags.flags,
178-
outer_exclusive_binder: flags.outer_exclusive_binder,
179-
};
171+
local.type_.borrow_mut().intern(st, |st| {
172+
let ty_struct = TyS {
173+
sty: st,
174+
flags: flags.flags,
175+
outer_exclusive_binder: flags.outer_exclusive_binder,
176+
};
180177

181-
// Make sure we don't end up with inference
182-
// types/regions in the global interner
183-
if local as *const _ as usize == global as *const _ as usize {
184-
bug!("Attempted to intern `{:?}` which contains \
185-
inference types/regions in the global type context",
186-
&ty_struct);
187-
}
178+
// Make sure we don't end up with inference
179+
// types/regions in the global interner
180+
if local as *const _ as usize == global as *const _ as usize {
181+
bug!("Attempted to intern `{:?}` which contains \
182+
inference types/regions in the global type context",
183+
&ty_struct);
184+
}
188185

189-
// Don't be &mut TyS.
190-
let ty: Ty<'tcx> = local.arena.alloc(ty_struct);
191-
interner.insert(Interned(ty));
192-
ty
186+
Interned(local.arena.alloc(ty_struct))
187+
}).0
193188
} else {
194-
let mut interner = global.type_.borrow_mut();
195-
if let Some(&Interned(ty)) = interner.get(&st) {
196-
return ty;
197-
}
198-
199-
let ty_struct = TyS {
200-
sty: st,
201-
flags: flags.flags,
202-
outer_exclusive_binder: flags.outer_exclusive_binder,
203-
};
189+
global.type_.borrow_mut().intern(st, |st| {
190+
let ty_struct = TyS {
191+
sty: st,
192+
flags: flags.flags,
193+
outer_exclusive_binder: flags.outer_exclusive_binder,
194+
};
204195

205-
// This is safe because all the types the ty_struct can point to
206-
// already is in the global arena
207-
let ty_struct: TyS<'gcx> = unsafe {
208-
mem::transmute(ty_struct)
209-
};
196+
// This is safe because all the types the ty_struct can point to
197+
// already is in the global arena
198+
let ty_struct: TyS<'gcx> = unsafe {
199+
mem::transmute(ty_struct)
200+
};
210201

211-
// Don't be &mut TyS.
212-
let ty: Ty<'gcx> = global.arena.alloc(ty_struct);
213-
interner.insert(Interned(ty));
214-
ty
202+
Interned(global.arena.alloc(ty_struct))
203+
}).0
215204
}
216205
}
217206
}
@@ -827,12 +816,9 @@ impl<'tcx> CommonTypes<'tcx> {
827816
fn new(interners: &CtxtInterners<'tcx>) -> CommonTypes<'tcx> {
828817
let mk = |sty| CtxtInterners::intern_ty(interners, interners, sty);
829818
let mk_region = |r| {
830-
if let Some(r) = interners.region.borrow().get(&r) {
831-
return r.0;
832-
}
833-
let r = interners.arena.alloc(r);
834-
interners.region.borrow_mut().insert(Interned(r));
835-
&*r
819+
interners.region.borrow_mut().intern(r, |r| {
820+
Interned(interners.arena.alloc(r))
821+
}).0
836822
};
837823

838824
CommonTypes {
@@ -955,14 +941,14 @@ pub struct GlobalCtxt<'tcx> {
955941
/// Data layout specification for the current target.
956942
pub data_layout: TargetDataLayout,
957943

958-
stability_interner: Lock<FxHashSet<&'tcx attr::Stability>>,
944+
stability_interner: Lock<FxHashMap<&'tcx attr::Stability, ()>>,
959945

960946
/// Stores the value of constants (and deduplicates the actual memory)
961-
allocation_interner: Lock<FxHashSet<&'tcx Allocation>>,
947+
allocation_interner: Lock<FxHashMap<&'tcx Allocation, ()>>,
962948

963949
pub alloc_map: Lock<interpret::AllocMap<'tcx, &'tcx Allocation>>,
964950

965-
layout_interner: Lock<FxHashSet<&'tcx LayoutDetails>>,
951+
layout_interner: Lock<FxHashMap<&'tcx LayoutDetails, ()>>,
966952

967953
/// A general purpose channel to throw data out the back towards LLVM worker
968954
/// threads.
@@ -1045,16 +1031,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
10451031
self,
10461032
alloc: Allocation,
10471033
) -> &'gcx Allocation {
1048-
let allocs = &mut self.allocation_interner.borrow_mut();
1049-
if let Some(alloc) = allocs.get(&alloc) {
1050-
return alloc;
1051-
}
1052-
1053-
let interned = self.global_arenas.const_allocs.alloc(alloc);
1054-
if let Some(prev) = allocs.replace(interned) { // insert into interner
1055-
bug!("Tried to overwrite interned Allocation: {:#?}", prev)
1056-
}
1057-
interned
1034+
self.allocation_interner.borrow_mut().intern(alloc, |alloc| {
1035+
self.global_arenas.const_allocs.alloc(alloc)
1036+
})
10581037
}
10591038

10601039
/// Allocates a byte or string literal for `mir::interpret`, read-only
@@ -1066,29 +1045,15 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
10661045
}
10671046

10681047
pub fn intern_stability(self, stab: attr::Stability) -> &'gcx attr::Stability {
1069-
let mut stability_interner = self.stability_interner.borrow_mut();
1070-
if let Some(st) = stability_interner.get(&stab) {
1071-
return st;
1072-
}
1073-
1074-
let interned = self.global_interners.arena.alloc(stab);
1075-
if let Some(prev) = stability_interner.replace(interned) {
1076-
bug!("Tried to overwrite interned Stability: {:?}", prev)
1077-
}
1078-
interned
1048+
self.stability_interner.borrow_mut().intern(stab, |stab| {
1049+
self.global_interners.arena.alloc(stab)
1050+
})
10791051
}
10801052

10811053
pub fn intern_layout(self, layout: LayoutDetails) -> &'gcx LayoutDetails {
1082-
let mut layout_interner = self.layout_interner.borrow_mut();
1083-
if let Some(layout) = layout_interner.get(&layout) {
1084-
return layout;
1085-
}
1086-
1087-
let interned = self.global_arenas.layout.alloc(layout);
1088-
if let Some(prev) = layout_interner.replace(interned) {
1089-
bug!("Tried to overwrite interned Layout: {:?}", prev)
1090-
}
1091-
interned
1054+
self.layout_interner.borrow_mut().intern(layout, |layout| {
1055+
self.global_arenas.layout.alloc(layout)
1056+
})
10921057
}
10931058

10941059
/// Returns a range of the start/end indices specified with the
@@ -2198,7 +2163,7 @@ macro_rules! sty_debug_print {
21982163
};
21992164
$(let mut $variant = total;)*
22002165

2201-
for &Interned(t) in tcx.interners.type_.borrow().iter() {
2166+
for &Interned(t) in tcx.interners.type_.borrow().keys() {
22022167
let variant = match t.sty {
22032168
ty::Bool | ty::Char | ty::Int(..) | ty::Uint(..) |
22042169
ty::Float(..) | ty::Str | ty::Never => continue,
@@ -2257,6 +2222,13 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
22572222
/// An entry in an interner.
22582223
struct Interned<'tcx, T: 'tcx+?Sized>(&'tcx T);
22592224

2225+
impl<'tcx, T: 'tcx+?Sized> Clone for Interned<'tcx, T> {
2226+
fn clone(&self) -> Self {
2227+
Interned(self.0)
2228+
}
2229+
}
2230+
impl<'tcx, T: 'tcx+?Sized> Copy for Interned<'tcx, T> {}
2231+
22602232
// NB: An Interned<Ty> compares and hashes as a sty.
22612233
impl<'tcx> PartialEq for Interned<'tcx, TyS<'tcx>> {
22622234
fn eq(&self, other: &Interned<'tcx, TyS<'tcx>>) -> bool {
@@ -2377,37 +2349,28 @@ macro_rules! intern_method {
23772349
// determine that all contents are in the global tcx.
23782350
// See comments on Lift for why we can't use that.
23792351
if ($keep_in_local_tcx)(&v) {
2380-
let mut interner = self.interners.$name.borrow_mut();
2381-
if let Some(&Interned(v)) = interner.get(key) {
2382-
return v;
2383-
}
2384-
2385-
// Make sure we don't end up with inference
2386-
// types/regions in the global tcx.
2387-
if self.is_global() {
2388-
bug!("Attempted to intern `{:?}` which contains \
2389-
inference types/regions in the global type context",
2390-
v);
2391-
}
2392-
2393-
let i = $alloc_method(&self.interners.arena, v);
2394-
interner.insert(Interned(i));
2395-
i
2352+
self.interners.$name.borrow_mut().intern_ref(key, || {
2353+
// Make sure we don't end up with inference
2354+
// types/regions in the global tcx.
2355+
if self.is_global() {
2356+
bug!("Attempted to intern `{:?}` which contains \
2357+
inference types/regions in the global type context",
2358+
v);
2359+
}
2360+
2361+
Interned($alloc_method(&self.interners.arena, v))
2362+
}).0
23962363
} else {
2397-
let mut interner = self.global_interners.$name.borrow_mut();
2398-
if let Some(&Interned(v)) = interner.get(key) {
2399-
return v;
2400-
}
2401-
2402-
// This transmutes $alloc<'tcx> to $alloc<'gcx>
2403-
let v = unsafe {
2404-
mem::transmute(v)
2405-
};
2406-
let i: &$lt_tcx $ty = $alloc_method(&self.global_interners.arena, v);
2407-
// Cast to 'gcx
2408-
let i = unsafe { mem::transmute(i) };
2409-
interner.insert(Interned(i));
2410-
i
2364+
self.global_interners.$name.borrow_mut().intern_ref(key, || {
2365+
// This transmutes $alloc<'tcx> to $alloc<'gcx>
2366+
let v = unsafe {
2367+
mem::transmute(v)
2368+
};
2369+
let i: &$lt_tcx $ty = $alloc_method(&self.global_interners.arena, v);
2370+
// Cast to 'gcx
2371+
let i = unsafe { mem::transmute(i) };
2372+
Interned(i)
2373+
}).0
24112374
}
24122375
}
24132376
}
+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use std::hash::Hash;
12+
use std::hash::BuildHasher;
13+
use std::hash::Hasher;
14+
use std::collections::HashMap;
15+
use std::collections::hash_map::RawEntryMut;
16+
use std::borrow::Borrow;
17+
18+
pub trait HashInterner<K: Eq + Hash> {
19+
fn intern_ref<Q: ?Sized, F: FnOnce() -> K>(&mut self, value: &Q, make: F) -> K
20+
where K: Borrow<Q>,
21+
Q: Hash + Eq;
22+
23+
fn intern<Q, F: FnOnce(Q) -> K>(&mut self, value: Q, make: F) -> K
24+
where K: Borrow<Q>,
25+
Q: Hash + Eq;
26+
}
27+
28+
impl<K: Eq + Hash + Copy, S: BuildHasher> HashInterner<K> for HashMap<K, (), S> {
29+
#[inline]
30+
fn intern_ref<Q: ?Sized, F: FnOnce() -> K>(&mut self, value: &Q, make: F) -> K
31+
where K: Borrow<Q>,
32+
Q: Hash + Eq
33+
{
34+
let mut hasher = self.hasher().build_hasher();
35+
value.hash(&mut hasher);
36+
let hash = hasher.finish();
37+
let entry = self.raw_entry_mut().from_key_hashed_nocheck(hash, value);
38+
39+
match entry {
40+
RawEntryMut::Occupied(e) => *e.key(),
41+
RawEntryMut::Vacant(e) => {
42+
let v = make();
43+
e.insert_hashed_nocheck(hash, v, ());
44+
v
45+
}
46+
}
47+
}
48+
49+
#[inline]
50+
fn intern<Q, F: FnOnce(Q) -> K>(&mut self, value: Q, make: F) -> K
51+
where K: Borrow<Q>,
52+
Q: Hash + Eq
53+
{
54+
let mut hasher = self.hasher().build_hasher();
55+
value.hash(&mut hasher);
56+
let hash = hasher.finish();
57+
let entry = self.raw_entry_mut().from_key_hashed_nocheck(hash, &value);
58+
59+
match entry {
60+
RawEntryMut::Occupied(e) => *e.key(),
61+
RawEntryMut::Vacant(e) => {
62+
let v = make(value);
63+
e.insert_hashed_nocheck(hash, v, ());
64+
v
65+
}
66+
}
67+
}
68+
}

src/librustc_data_structures/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#![feature(nll)]
3030
#![feature(allow_internal_unstable)]
3131
#![feature(vec_resize_with)]
32+
#![feature(hash_raw_entry)]
3233

3334
#![cfg_attr(unix, feature(libc))]
3435
#![cfg_attr(test, feature(test))]
@@ -66,6 +67,7 @@ pub mod flock;
6667
pub mod fx;
6768
pub mod graph;
6869
pub mod indexed_vec;
70+
pub mod interner;
6971
pub mod obligation_forest;
7072
pub mod owning_ref;
7173
pub mod ptr_key;

0 commit comments

Comments
 (0)