Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use raw_entry for more efficient interning #56324

Merged
merged 2 commits into from
Nov 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/libarena/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ pub struct DroplessArena {
unsafe impl Send for DroplessArena {}

impl Default for DroplessArena {
#[inline]
fn default() -> DroplessArena {
DroplessArena {
ptr: Cell::new(0 as *mut u8),
Expand All @@ -319,6 +320,7 @@ impl DroplessArena {
false
}

#[inline]
fn align(&self, align: usize) {
let final_address = ((self.ptr.get() as usize) + align - 1) & !(align - 1);
self.ptr.set(final_address as *mut u8);
Expand Down
1 change: 1 addition & 0 deletions src/libcore/num/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3614,6 +3614,7 @@ assert_eq!(3", stringify!($SelfT), ".checked_next_power_of_two(), Some(4));
assert_eq!(", stringify!($SelfT), "::max_value().checked_next_power_of_two(), None);",
$EndFeature, "
```"),
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn checked_next_power_of_two(self) -> Option<Self> {
self.one_less_than_next_power_of_two().checked_add(1)
Expand Down
185 changes: 74 additions & 111 deletions src/librustc/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ use ty::CanonicalTy;
use ty::CanonicalPolyFnSig;
use util::nodemap::{DefIdMap, DefIdSet, ItemLocalMap};
use util::nodemap::{FxHashMap, FxHashSet};
use rustc_data_structures::interner::HashInterner;
use smallvec::SmallVec;
use rustc_data_structures::stable_hasher::{HashStable, hash_stable_hashmap,
StableHasher, StableHasherResult,
Expand Down Expand Up @@ -113,7 +114,7 @@ pub struct GlobalArenas<'tcx> {
const_allocs: TypedArena<interpret::Allocation>,
}

type InternedSet<'tcx, T> = Lock<FxHashSet<Interned<'tcx, T>>>;
type InternedSet<'tcx, T> = Lock<FxHashMap<Interned<'tcx, T>, ()>>;

pub struct CtxtInterners<'tcx> {
/// The arena that types, regions, etc are allocated from
Expand Down Expand Up @@ -166,51 +167,39 @@ impl<'gcx: 'tcx, 'tcx> CtxtInterners<'tcx> {
// determine that all contents are in the global tcx.
// See comments on Lift for why we can't use that.
if flags.flags.intersects(ty::TypeFlags::KEEP_IN_LOCAL_TCX) {
let mut interner = local.type_.borrow_mut();
if let Some(&Interned(ty)) = interner.get(&st) {
return ty;
}

let ty_struct = TyS {
sty: st,
flags: flags.flags,
outer_exclusive_binder: flags.outer_exclusive_binder,
};
local.type_.borrow_mut().intern(st, |st| {
let ty_struct = TyS {
sty: st,
flags: flags.flags,
outer_exclusive_binder: flags.outer_exclusive_binder,
};

// Make sure we don't end up with inference
// types/regions in the global interner
if local as *const _ as usize == global as *const _ as usize {
bug!("Attempted to intern `{:?}` which contains \
inference types/regions in the global type context",
&ty_struct);
}
// Make sure we don't end up with inference
// types/regions in the global interner
if local as *const _ as usize == global as *const _ as usize {
bug!("Attempted to intern `{:?}` which contains \
inference types/regions in the global type context",
&ty_struct);
}

// Don't be &mut TyS.
let ty: Ty<'tcx> = local.arena.alloc(ty_struct);
interner.insert(Interned(ty));
ty
Interned(local.arena.alloc(ty_struct))
}).0
} else {
let mut interner = global.type_.borrow_mut();
if let Some(&Interned(ty)) = interner.get(&st) {
return ty;
}

let ty_struct = TyS {
sty: st,
flags: flags.flags,
outer_exclusive_binder: flags.outer_exclusive_binder,
};
global.type_.borrow_mut().intern(st, |st| {
let ty_struct = TyS {
sty: st,
flags: flags.flags,
outer_exclusive_binder: flags.outer_exclusive_binder,
};

// This is safe because all the types the ty_struct can point to
// already is in the global arena
let ty_struct: TyS<'gcx> = unsafe {
mem::transmute(ty_struct)
};
// This is safe because all the types the ty_struct can point to
// already is in the global arena
let ty_struct: TyS<'gcx> = unsafe {
mem::transmute(ty_struct)
};

// Don't be &mut TyS.
let ty: Ty<'gcx> = global.arena.alloc(ty_struct);
interner.insert(Interned(ty));
ty
Interned(global.arena.alloc(ty_struct))
}).0
}
}
}
Expand Down Expand Up @@ -825,12 +814,9 @@ impl<'tcx> CommonTypes<'tcx> {
fn new(interners: &CtxtInterners<'tcx>) -> CommonTypes<'tcx> {
let mk = |sty| CtxtInterners::intern_ty(interners, interners, sty);
let mk_region = |r| {
if let Some(r) = interners.region.borrow().get(&r) {
return r.0;
}
let r = interners.arena.alloc(r);
interners.region.borrow_mut().insert(Interned(r));
&*r
interners.region.borrow_mut().intern(r, |r| {
Interned(interners.arena.alloc(r))
}).0
};
CommonTypes {
bool: mk(Bool),
Expand Down Expand Up @@ -950,14 +936,14 @@ pub struct GlobalCtxt<'tcx> {
/// Data layout specification for the current target.
pub data_layout: TargetDataLayout,

stability_interner: Lock<FxHashSet<&'tcx attr::Stability>>,
stability_interner: Lock<FxHashMap<&'tcx attr::Stability, ()>>,

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

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

layout_interner: Lock<FxHashSet<&'tcx LayoutDetails>>,
layout_interner: Lock<FxHashMap<&'tcx LayoutDetails, ()>>,

/// A general purpose channel to throw data out the back towards LLVM worker
/// threads.
Expand Down Expand Up @@ -1040,16 +1026,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
self,
alloc: Allocation,
) -> &'gcx Allocation {
let allocs = &mut self.allocation_interner.borrow_mut();
if let Some(alloc) = allocs.get(&alloc) {
return alloc;
}

let interned = self.global_arenas.const_allocs.alloc(alloc);
if let Some(prev) = allocs.replace(interned) { // insert into interner
bug!("Tried to overwrite interned Allocation: {:#?}", prev)
}
interned
self.allocation_interner.borrow_mut().intern(alloc, |alloc| {
self.global_arenas.const_allocs.alloc(alloc)
})
}

/// Allocates a byte or string literal for `mir::interpret`, read-only
Expand All @@ -1061,29 +1040,15 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
}

pub fn intern_stability(self, stab: attr::Stability) -> &'gcx attr::Stability {
let mut stability_interner = self.stability_interner.borrow_mut();
if let Some(st) = stability_interner.get(&stab) {
return st;
}

let interned = self.global_interners.arena.alloc(stab);
if let Some(prev) = stability_interner.replace(interned) {
bug!("Tried to overwrite interned Stability: {:?}", prev)
}
interned
self.stability_interner.borrow_mut().intern(stab, |stab| {
self.global_interners.arena.alloc(stab)
})
}

pub fn intern_layout(self, layout: LayoutDetails) -> &'gcx LayoutDetails {
let mut layout_interner = self.layout_interner.borrow_mut();
if let Some(layout) = layout_interner.get(&layout) {
return layout;
}

let interned = self.global_arenas.layout.alloc(layout);
if let Some(prev) = layout_interner.replace(interned) {
bug!("Tried to overwrite interned Layout: {:?}", prev)
}
interned
self.layout_interner.borrow_mut().intern(layout, |layout| {
self.global_arenas.layout.alloc(layout)
})
}

/// Returns a range of the start/end indices specified with the
Expand Down Expand Up @@ -2193,7 +2158,7 @@ macro_rules! sty_debug_print {
};
$(let mut $variant = total;)*

for &Interned(t) in tcx.interners.type_.borrow().iter() {
for &Interned(t) in tcx.interners.type_.borrow().keys() {
let variant = match t.sty {
ty::Bool | ty::Char | ty::Int(..) | ty::Uint(..) |
ty::Float(..) | ty::Str | ty::Never => continue,
Expand Down Expand Up @@ -2252,6 +2217,13 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
/// An entry in an interner.
struct Interned<'tcx, T: 'tcx+?Sized>(&'tcx T);

impl<'tcx, T: 'tcx+?Sized> Clone for Interned<'tcx, T> {
fn clone(&self) -> Self {
Interned(self.0)
}
}
impl<'tcx, T: 'tcx+?Sized> Copy for Interned<'tcx, T> {}

// NB: An Interned<Ty> compares and hashes as a sty.
impl<'tcx> PartialEq for Interned<'tcx, TyS<'tcx>> {
fn eq(&self, other: &Interned<'tcx, TyS<'tcx>>) -> bool {
Expand Down Expand Up @@ -2372,37 +2344,28 @@ macro_rules! intern_method {
// determine that all contents are in the global tcx.
// See comments on Lift for why we can't use that.
if ($keep_in_local_tcx)(&v) {
let mut interner = self.interners.$name.borrow_mut();
if let Some(&Interned(v)) = interner.get(key) {
return v;
}

// Make sure we don't end up with inference
// types/regions in the global tcx.
if self.is_global() {
bug!("Attempted to intern `{:?}` which contains \
inference types/regions in the global type context",
v);
}

let i = $alloc_method(&self.interners.arena, v);
interner.insert(Interned(i));
i
self.interners.$name.borrow_mut().intern_ref(key, || {
// Make sure we don't end up with inference
// types/regions in the global tcx.
if self.is_global() {
bug!("Attempted to intern `{:?}` which contains \
inference types/regions in the global type context",
v);
}

Interned($alloc_method(&self.interners.arena, v))
}).0
} else {
let mut interner = self.global_interners.$name.borrow_mut();
if let Some(&Interned(v)) = interner.get(key) {
return v;
}

// This transmutes $alloc<'tcx> to $alloc<'gcx>
let v = unsafe {
mem::transmute(v)
};
let i: &$lt_tcx $ty = $alloc_method(&self.global_interners.arena, v);
// Cast to 'gcx
let i = unsafe { mem::transmute(i) };
interner.insert(Interned(i));
i
self.global_interners.$name.borrow_mut().intern_ref(key, || {
// This transmutes $alloc<'tcx> to $alloc<'gcx>
let v = unsafe {
mem::transmute(v)
};
let i: &$lt_tcx $ty = $alloc_method(&self.global_interners.arena, v);
// Cast to 'gcx
let i = unsafe { mem::transmute(i) };
Interned(i)
}).0
}
}
}
Expand Down
68 changes: 68 additions & 0 deletions src/librustc_data_structures/interner.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::hash::Hash;
use std::hash::BuildHasher;
use std::hash::Hasher;
use std::collections::HashMap;
use std::collections::hash_map::RawEntryMut;
use std::borrow::Borrow;

pub trait HashInterner<K: Eq + Hash> {
fn intern_ref<Q: ?Sized, F: FnOnce() -> K>(&mut self, value: &Q, make: F) -> K
where K: Borrow<Q>,
Q: Hash + Eq;

fn intern<Q, F: FnOnce(Q) -> K>(&mut self, value: Q, make: F) -> K
where K: Borrow<Q>,
Q: Hash + Eq;
}

impl<K: Eq + Hash + Copy, S: BuildHasher> HashInterner<K> for HashMap<K, (), S> {
#[inline]
fn intern_ref<Q: ?Sized, F: FnOnce() -> K>(&mut self, value: &Q, make: F) -> K
where K: Borrow<Q>,
Q: Hash + Eq
{
let mut hasher = self.hasher().build_hasher();
value.hash(&mut hasher);
let hash = hasher.finish();
let entry = self.raw_entry_mut().from_key_hashed_nocheck(hash, value);

match entry {
RawEntryMut::Occupied(e) => *e.key(),
RawEntryMut::Vacant(e) => {
let v = make();
e.insert_hashed_nocheck(hash, v, ());
v
}
}
}

#[inline]
fn intern<Q, F: FnOnce(Q) -> K>(&mut self, value: Q, make: F) -> K
where K: Borrow<Q>,
Q: Hash + Eq
{
let mut hasher = self.hasher().build_hasher();
value.hash(&mut hasher);
let hash = hasher.finish();
let entry = self.raw_entry_mut().from_key_hashed_nocheck(hash, &value);

match entry {
RawEntryMut::Occupied(e) => *e.key(),
RawEntryMut::Vacant(e) => {
let v = make(value);
e.insert_hashed_nocheck(hash, v, ());
v
}
}
}
}
2 changes: 2 additions & 0 deletions src/librustc_data_structures/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#![feature(nll)]
#![feature(allow_internal_unstable)]
#![feature(vec_resize_with)]
#![feature(hash_raw_entry)]

#![cfg_attr(unix, feature(libc))]
#![cfg_attr(test, feature(test))]
Expand Down Expand Up @@ -66,6 +67,7 @@ pub mod flock;
pub mod fx;
pub mod graph;
pub mod indexed_vec;
pub mod interner;
pub mod obligation_forest;
pub mod owning_ref;
pub mod ptr_key;
Expand Down
Loading