Cache many layouts into interner by default

This commit is contained in:
Ayaz Hafiz 2023-01-03 11:45:48 -06:00
parent b60d5c0251
commit b1424afefd
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
2 changed files with 81 additions and 4 deletions

View file

@ -3,6 +3,7 @@
use std::{ use std::{
cell::RefCell, cell::RefCell,
hash::{BuildHasher, Hash, Hasher}, hash::{BuildHasher, Hash, Hasher},
marker::PhantomData,
sync::Arc, sync::Arc,
}; };
@ -23,6 +24,25 @@ impl<T> Clone for Interned<T> {
impl<T> Copy for Interned<T> {} impl<T> Copy for Interned<T> {}
impl<T> Interned<T> {
/// # Safety
///
/// The index is not guaranteed to exist. Use this only when creating an interner with constant
/// indices, with the variant that `insert` returns a monotonically increasing index.
///
/// For example:
///
/// ```ignore(illustrative)
/// let reserved_interned = Interned::from_reserved_index(0);
/// let interner = GlobalInterner::with_capacity(1);
/// let inserted = interner.insert("something");
/// assert_eq!(reserved_interned, inserted);
/// ```
pub const unsafe fn from_reserved_index(index: usize) -> Self {
Self(index, PhantomData)
}
}
/// A concurrent interner, suitable for usage between threads. /// A concurrent interner, suitable for usage between threads.
/// ///
/// The interner does not currently maintain its own arena; you will have to supply /// The interner does not currently maintain its own arena; you will have to supply
@ -143,6 +163,10 @@ impl<'a, K: Hash + Eq> GlobalInterner<'a, K> {
let Interned(index, _) = interned; let Interned(index, _) = interned;
self.vec.read()[index] self.vec.read()[index]
} }
pub fn is_empty(&self) -> bool {
self.vec.read().is_empty()
}
} }
impl<'a, K: Hash + Eq> ThreadLocalInterner<'a, K> { impl<'a, K: Hash + Eq> ThreadLocalInterner<'a, K> {
@ -201,6 +225,10 @@ impl<'a, K> SingleThreadedInterner<'a, K> {
vec: RwLock::new(vec), vec: RwLock::new(vec),
}) })
} }
pub fn is_empty(&self) -> bool {
self.vec.is_empty()
}
} }
impl<'a, K: Hash + Eq> Interner<'a, K> for SingleThreadedInterner<'a, K> { impl<'a, K: Hash + Eq> Interner<'a, K> for SingleThreadedInterner<'a, K> {

View file

@ -1,10 +1,57 @@
use std::sync::Arc; use std::sync::Arc;
use roc_builtins::bitcode::IntWidth;
use roc_intern::{GlobalInterner, Interned, Interner, SingleThreadedInterner, ThreadLocalInterner}; use roc_intern::{GlobalInterner, Interned, Interner, SingleThreadedInterner, ThreadLocalInterner};
use super::Layout; use super::{Builtin, Layout};
pub trait LayoutInterner<'a>: Interner<'a, Layout<'a>> {} macro_rules! cache_interned_layouts {
($($i:literal, $name:ident, $layout:expr)*; $total_constants:literal) => {
pub trait LayoutInterner<'a>: Interner<'a, Layout<'a>> {
$(
const $name: Interned<Layout<'a>> = unsafe { Interned::from_reserved_index($i) };
)*
}
fn fill_reserved_layouts<'a>(interner: &mut STLayoutInterner<'a>) {
assert!(interner.0.is_empty());
$(
interner.0.insert(&$layout);
)*
}
const fn _are_constants_in_order_non_redundant() -> usize {
let mut total_seen = 0;
$(total_seen += ($i * 0) + 1;)*
match 0usize {
$($i => {})*
_ => {}
}
total_seen
}
const _ASSERT_NON_REDUNDANT_CONSTANTS: () =
assert!(_are_constants_in_order_non_redundant() == $total_constants);
}
}
cache_interned_layouts! {
0, VOID, Layout::VOID
1, UNIT, Layout::UNIT
2, BOOL, Layout::Builtin(Builtin::Bool)
3, U8, Layout::Builtin(Builtin::Int(IntWidth::U8))
4, U16, Layout::Builtin(Builtin::Int(IntWidth::U16))
5, U32, Layout::Builtin(Builtin::Int(IntWidth::U32))
6, U64, Layout::Builtin(Builtin::Int(IntWidth::U64))
7, U128, Layout::Builtin(Builtin::Int(IntWidth::U128))
8, I8, Layout::Builtin(Builtin::Int(IntWidth::I8))
9, I16, Layout::Builtin(Builtin::Int(IntWidth::I16))
10, I32, Layout::Builtin(Builtin::Int(IntWidth::I32))
11, I64, Layout::Builtin(Builtin::Int(IntWidth::I64))
12, I128, Layout::Builtin(Builtin::Int(IntWidth::I128))
; 13
}
#[derive(Debug)] #[derive(Debug)]
pub struct GlobalLayoutInterner<'a>(Arc<GlobalInterner<'a, Layout<'a>>>); pub struct GlobalLayoutInterner<'a>(Arc<GlobalInterner<'a, Layout<'a>>>);
@ -15,7 +62,7 @@ pub struct STLayoutInterner<'a>(SingleThreadedInterner<'a, Layout<'a>>);
impl<'a> GlobalLayoutInterner<'a> { impl<'a> GlobalLayoutInterner<'a> {
pub fn with_capacity(capacity: usize) -> Self { pub fn with_capacity(capacity: usize) -> Self {
Self(GlobalInterner::with_capacity(capacity)) STLayoutInterner::with_capacity(capacity).into_global()
} }
pub fn fork(&self) -> TLLayoutInterner<'a> { pub fn fork(&self) -> TLLayoutInterner<'a> {
TLLayoutInterner(self.0.fork()) TLLayoutInterner(self.0.fork())
@ -30,7 +77,9 @@ impl<'a> GlobalLayoutInterner<'a> {
impl<'a> STLayoutInterner<'a> { impl<'a> STLayoutInterner<'a> {
pub fn with_capacity(capacity: usize) -> Self { pub fn with_capacity(capacity: usize) -> Self {
Self(SingleThreadedInterner::with_capacity(capacity)) let mut interner = Self(SingleThreadedInterner::with_capacity(capacity));
fill_reserved_layouts(&mut interner);
interner
} }
pub fn into_global(self) -> GlobalLayoutInterner<'a> { pub fn into_global(self) -> GlobalLayoutInterner<'a> {
GlobalLayoutInterner(self.0.into_global()) GlobalLayoutInterner(self.0.into_global())