Skip to content

Commit

Permalink
bootstrap: use internment instead of hand-rolled interning
Browse files Browse the repository at this point in the history
  • Loading branch information
GrigorenkoPV committed Jul 29, 2024
1 parent 4db3d12 commit 3f2af36
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 202 deletions.
57 changes: 57 additions & 0 deletions src/bootstrap/Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@
# It is not intended for manual editing.
version = 3

[[package]]
name = "ahash"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]

[[package]]
name = "aho-corasick"
version = "1.1.2"
Expand All @@ -11,6 +23,12 @@ dependencies = [
"memchr",
]

[[package]]
name = "allocator-api2"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"

[[package]]
name = "anstyle"
version = "1.0.4"
Expand Down Expand Up @@ -50,6 +68,7 @@ dependencies = [
"fd-lock",
"home",
"ignore",
"internment",
"junction",
"libc",
"object",
Expand Down Expand Up @@ -278,6 +297,16 @@ dependencies = [
"regex-syntax",
]

[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [
"ahash",
"allocator-api2",
]

[[package]]
name = "heck"
version = "0.4.1"
Expand Down Expand Up @@ -309,6 +338,14 @@ dependencies = [
"winapi-util",
]

[[package]]
name = "internment"
version = "0.8.4"
source = "git+https://github.com/droundy/internment.git#8a5feb354d023a7c69f3889e9e737099ec0e4ea7"
dependencies = [
"hashbrown",
]

[[package]]
name = "itoa"
version = "1.0.10"
Expand Down Expand Up @@ -759,3 +796,23 @@ name = "yansi"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"

[[package]]
name = "zerocopy"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"zerocopy-derive",
]

[[package]]
name = "zerocopy-derive"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
1 change: 1 addition & 0 deletions src/bootstrap/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ clap = { version = "4.4", default-features = false, features = ["std", "usage",
clap_complete = "4.4"
fd-lock = "4.0"
home = "0.5"
internment = { git = "https://github.com/droundy/internment.git" } # internment = "0.8.5"
ignore = "0.4"
libc = "0.2"
object = { version = "0.32", default-features = false, features = ["archive", "coff", "read_core", "unaligned"] }
Expand Down
28 changes: 15 additions & 13 deletions src/bootstrap/src/core/config/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use crate::core::build_steps::compile::CODEGEN_BACKEND_PREFIX;
use crate::core::build_steps::llvm;
pub use crate::core::config::flags::Subcommand;
use crate::core::config::flags::{Color, Flags, Warnings};
use crate::utils::cache::{Interned, INTERNER};
use crate::utils::cache::Interned;
use crate::utils::channel::{self, GitInfo};
use crate::utils::helpers::{self, exe, get_closest_merge_base_commit, output, t};

Expand Down Expand Up @@ -435,15 +435,21 @@ impl std::str::FromStr for RustcLto {
}
}

#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
// N.B.: This type is used everywhere, and the entire codebase relies on it being Copy.
// Making !Copy is highly nontrivial!
pub struct TargetSelection {
pub triple: Interned<String>,
file: Option<Interned<String>>,
pub triple: Interned<str>,
file: Option<Interned<str>>,
synthetic: bool,
}

impl Default for TargetSelection {
fn default() -> Self {
Self { triple: "".into(), file: Default::default(), synthetic: Default::default() }
}
}

/// Newtype over `Vec<TargetSelection>` so we can implement custom parsing logic
#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct TargetSelectionList(Vec<TargetSelection>);
Expand All @@ -470,18 +476,14 @@ impl TargetSelection {
(selection, None)
};

let triple = INTERNER.intern_str(triple);
let file = file.map(|f| INTERNER.intern_str(f));
let triple: Interned<str> = triple.into();
let file: Option<Interned<str>> = file.map(|f| f.into());

Self { triple, file, synthetic: false }
}

pub fn create_synthetic(triple: &str, file: &str) -> Self {
Self {
triple: INTERNER.intern_str(triple),
file: Some(INTERNER.intern_str(file)),
synthetic: true,
}
Self { triple: triple.into(), file: Some(file.into()), synthetic: true }
}

pub fn rustc_target_arg(&self) -> &str {
Expand Down Expand Up @@ -532,7 +534,7 @@ impl fmt::Debug for TargetSelection {

impl PartialEq<&str> for TargetSelection {
fn eq(&self, other: &&str) -> bool {
self.triple == *other
&*self.triple == *other
}
}

Expand Down Expand Up @@ -2012,7 +2014,7 @@ impl Config {
// thus, disabled
// - similarly, lld will not be built nor used by default when explicitly asked not to, e.g.
// when the config sets `rust.lld = false`
if config.build.triple == "x86_64-unknown-linux-gnu"
if &*config.build.triple == "x86_64-unknown-linux-gnu"
&& config.hosts == [config.build]
&& (config.channel == "dev" || config.channel == "nightly")
{
Expand Down
190 changes: 1 addition & 189 deletions src/bootstrap/src/utils/cache.rs
Original file line number Diff line number Diff line change
@@ -1,198 +1,10 @@
use std::any::{Any, TypeId};
use std::borrow::Borrow;
use std::cell::RefCell;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::ops::Deref;
use std::path::PathBuf;
use std::sync::{LazyLock, Mutex};
use std::{fmt, mem};

use crate::core::builder::Step;

pub struct Interned<T>(usize, PhantomData<*const T>);

impl<T: Internable + Default> Default for Interned<T> {
fn default() -> Self {
T::default().intern()
}
}

impl<T> Copy for Interned<T> {}
impl<T> Clone for Interned<T> {
fn clone(&self) -> Interned<T> {
*self
}
}

impl<T> PartialEq for Interned<T> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T> Eq for Interned<T> {}

impl PartialEq<str> for Interned<String> {
fn eq(&self, other: &str) -> bool {
*self == other
}
}
impl<'a> PartialEq<&'a str> for Interned<String> {
fn eq(&self, other: &&str) -> bool {
**self == **other
}
}
impl<'a, T> PartialEq<&'a Interned<T>> for Interned<T> {
fn eq(&self, other: &&Self) -> bool {
self.0 == other.0
}
}
impl<'a, T> PartialEq<Interned<T>> for &'a Interned<T> {
fn eq(&self, other: &Interned<T>) -> bool {
self.0 == other.0
}
}

unsafe impl<T> Send for Interned<T> {}
unsafe impl<T> Sync for Interned<T> {}

impl fmt::Display for Interned<String> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s: &str = self;
f.write_str(s)
}
}

impl<T, U: ?Sized + fmt::Debug> fmt::Debug for Interned<T>
where
Self: Deref<Target = U>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s: &U = self;
f.write_fmt(format_args!("{s:?}"))
}
}

impl<T: Internable + Hash> Hash for Interned<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
let l = T::intern_cache().lock().unwrap();
l.get(*self).hash(state)
}
}

impl<T: Internable + Deref> Deref for Interned<T> {
type Target = T::Target;
fn deref(&self) -> &Self::Target {
let l = T::intern_cache().lock().unwrap();
unsafe { mem::transmute::<&Self::Target, &Self::Target>(l.get(*self)) }
}
}

impl<T: Internable + AsRef<U>, U: ?Sized> AsRef<U> for Interned<T> {
fn as_ref(&self) -> &U {
let l = T::intern_cache().lock().unwrap();
unsafe { mem::transmute::<&U, &U>(l.get(*self).as_ref()) }
}
}

impl<T: Internable + PartialOrd> PartialOrd for Interned<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
let l = T::intern_cache().lock().unwrap();
l.get(*self).partial_cmp(l.get(*other))
}
}

impl<T: Internable + Ord> Ord for Interned<T> {
fn cmp(&self, other: &Self) -> Ordering {
let l = T::intern_cache().lock().unwrap();
l.get(*self).cmp(l.get(*other))
}
}

struct TyIntern<T: Clone + Eq> {
items: Vec<T>,
set: HashMap<T, Interned<T>>,
}

impl<T: Hash + Clone + Eq> Default for TyIntern<T> {
fn default() -> Self {
TyIntern { items: Vec::new(), set: Default::default() }
}
}

impl<T: Hash + Clone + Eq> TyIntern<T> {
fn intern_borrow<B>(&mut self, item: &B) -> Interned<T>
where
B: Eq + Hash + ToOwned<Owned = T> + ?Sized,
T: Borrow<B>,
{
if let Some(i) = self.set.get(item) {
return *i;
}
let item = item.to_owned();
let interned = Interned(self.items.len(), PhantomData::<*const T>);
self.set.insert(item.clone(), interned);
self.items.push(item);
interned
}

fn intern(&mut self, item: T) -> Interned<T> {
if let Some(i) = self.set.get(&item) {
return *i;
}
let interned = Interned(self.items.len(), PhantomData::<*const T>);
self.set.insert(item.clone(), interned);
self.items.push(item);
interned
}

fn get(&self, i: Interned<T>) -> &T {
&self.items[i.0]
}
}

#[derive(Default)]
pub struct Interner {
strs: Mutex<TyIntern<String>>,
paths: Mutex<TyIntern<PathBuf>>,
lists: Mutex<TyIntern<Vec<String>>>,
}

trait Internable: Clone + Eq + Hash + 'static {
fn intern_cache() -> &'static Mutex<TyIntern<Self>>;

fn intern(self) -> Interned<Self> {
Self::intern_cache().lock().unwrap().intern(self)
}
}

impl Internable for String {
fn intern_cache() -> &'static Mutex<TyIntern<Self>> {
&INTERNER.strs
}
}

impl Internable for PathBuf {
fn intern_cache() -> &'static Mutex<TyIntern<Self>> {
&INTERNER.paths
}
}

impl Internable for Vec<String> {
fn intern_cache() -> &'static Mutex<TyIntern<Self>> {
&INTERNER.lists
}
}

impl Interner {
pub fn intern_str(&self, s: &str) -> Interned<String> {
self.strs.lock().unwrap().intern_borrow(s)
}
}

pub static INTERNER: LazyLock<Interner> = LazyLock::new(Interner::default);
pub type Interned<T> = internment::Intern<T>;

/// This is essentially a `HashMap` which allows storing any type in its input and
/// any type in its output. It is a write-once cache; values are never evicted,
Expand Down

0 comments on commit 3f2af36

Please sign in to comment.