Inline interners into the layout interner module

I realized that we'll need to make the layout interner more complicated
to support things like recursive pointers pointing to their parents and
to support lambda set layout caching. Since the layout interner is the
only user of intern crate right now anyway, just inline the whole thing.
This commit is contained in:
Ayaz Hafiz 2023-01-03 14:19:39 -06:00
parent 82e6982196
commit 50826d1a83
No known key found for this signature in database
GPG Key ID: 0E2A37416A25EF58
32 changed files with 277 additions and 376 deletions

20
Cargo.lock generated
View File

@ -3273,7 +3273,6 @@ dependencies = [
"morphic_lib",
"roc_collections",
"roc_debug_flags",
"roc_intern",
"roc_module",
"roc_mono",
]
@ -3404,7 +3403,6 @@ dependencies = [
"roc_fmt",
"roc_gen_llvm",
"roc_glue",
"roc_intern",
"roc_linker",
"roc_load",
"roc_module",
@ -3621,7 +3619,6 @@ dependencies = [
"roc_can",
"roc_collections",
"roc_error_macros",
"roc_intern",
"roc_module",
"roc_mono",
"roc_parse",
@ -3647,7 +3644,6 @@ dependencies = [
"roc_collections",
"roc_debug_flags",
"roc_error_macros",
"roc_intern",
"roc_module",
"roc_mono",
"roc_region",
@ -3665,7 +3661,6 @@ dependencies = [
"roc_builtins",
"roc_collections",
"roc_error_macros",
"roc_intern",
"roc_module",
"roc_mono",
"roc_std",
@ -3689,7 +3684,6 @@ dependencies = [
"roc_can",
"roc_collections",
"roc_error_macros",
"roc_intern",
"roc_load",
"roc_module",
"roc_mono",
@ -3718,14 +3712,6 @@ dependencies = [
name = "roc_ident"
version = "0.0.1"
[[package]]
name = "roc_intern"
version = "0.0.1"
dependencies = [
"parking_lot 0.12.1",
"roc_collections",
]
[[package]]
name = "roc_late_solve"
version = "0.0.1"
@ -3799,7 +3785,6 @@ dependencies = [
"roc_derive",
"roc_derive_key",
"roc_error_macros",
"roc_intern",
"roc_late_solve",
"roc_module",
"roc_mono",
@ -3839,6 +3824,7 @@ dependencies = [
"bitvec 1.0.1",
"bumpalo",
"hashbrown 0.12.3",
"parking_lot 0.12.1",
"roc_builtins",
"roc_can",
"roc_collections",
@ -3847,7 +3833,6 @@ dependencies = [
"roc_derive_key",
"roc_error_macros",
"roc_exhaustive",
"roc_intern",
"roc_late_solve",
"roc_module",
"roc_problem",
@ -3928,7 +3913,6 @@ dependencies = [
"roc_builtins",
"roc_collections",
"roc_gen_llvm",
"roc_intern",
"roc_load",
"roc_module",
"roc_mono",
@ -3954,7 +3938,6 @@ dependencies = [
"roc_can",
"roc_collections",
"roc_fmt",
"roc_intern",
"roc_load",
"roc_module",
"roc_mono",
@ -3984,7 +3967,6 @@ dependencies = [
"roc_collections",
"roc_error_macros",
"roc_gen_llvm",
"roc_intern",
"roc_load",
"roc_module",
"roc_mono",

View File

@ -63,7 +63,6 @@ roc_editor = { path = "../editor", optional = true }
roc_linker = { path = "../linker" }
roc_repl_cli = { path = "../repl_cli", optional = true }
roc_tracing = { path = "../tracing" }
roc_intern = { path = "../compiler/intern" }
roc_gen_llvm = {path = "../compiler/gen_llvm"}
roc_wasm_interp = { path = "../wasm_interp", optional = true }

View File

@ -8,7 +8,6 @@ version = "0.0.1"
[dependencies]
morphic_lib = {path = "../../vendor/morphic_lib"}
roc_collections = {path = "../collections"}
roc_intern = {path = "../intern"}
roc_module = {path = "../module"}
roc_mono = {path = "../mono"}
roc_debug_flags = {path = "../debug_flags"}

View File

@ -6,7 +6,6 @@ use morphic_lib::{
TypeDefBuilder, TypeId, TypeName, UpdateModeVar, ValueId,
};
use roc_collections::all::{MutMap, MutSet};
use roc_intern::Interner;
use roc_module::low_level::LowLevel;
use roc_module::symbol::Symbol;
@ -15,7 +14,8 @@ use roc_mono::ir::{
Literal, ModifyRc, OptLevel, Proc, ProcLayout, SingleEntryPoint, Stmt,
};
use roc_mono::layout::{
Builtin, FieldOrderHash, Layout, Niche, RawFunctionLayout, STLayoutInterner, UnionLayout,
Builtin, FieldOrderHash, Layout, LayoutInterner, Niche, RawFunctionLayout, STLayoutInterner,
UnionLayout,
};
// just using one module for now

View File

@ -8,7 +8,6 @@ edition = "2021"
[dependencies]
roc_collections = { path = "../collections" }
roc_intern = { path = "../intern" }
roc_region = { path = "../region" }
roc_module = { path = "../module" }
roc_problem = { path = "../problem" }

View File

@ -9,7 +9,6 @@ edition = "2021"
[dependencies]
roc_alias_analysis = { path = "../alias_analysis" }
roc_collections = { path = "../collections" }
roc_intern = { path = "../intern" }
roc_module = { path = "../module" }
roc_builtins = { path = "../builtins" }
roc_error_macros = { path = "../../error_macros" }

View File

@ -38,15 +38,14 @@ use roc_debug_flags::dbg_do;
#[cfg(debug_assertions)]
use roc_debug_flags::ROC_PRINT_LLVM_FN_VERIFICATION;
use roc_error_macros::internal_error;
use roc_intern::Interner;
use roc_module::symbol::{Interns, ModuleId, Symbol};
use roc_mono::ir::{
BranchInfo, CallType, CrashTag, EntryPoint, JoinPointId, ListLiteralElement, ModifyRc,
OptLevel, ProcLayout, SingleEntryPoint,
};
use roc_mono::layout::{
Builtin, LambdaName, LambdaSet, Layout, LayoutIds, Niche, RawFunctionLayout, STLayoutInterner,
TagIdIntType, UnionLayout,
Builtin, LambdaName, LambdaSet, Layout, LayoutIds, LayoutInterner, Niche, RawFunctionLayout,
STLayoutInterner, TagIdIntType, UnionLayout,
};
use roc_std::RocDec;
use roc_target::{PtrWidth, TargetInfo};

View File

@ -10,9 +10,8 @@ use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, Str
use inkwell::{AddressSpace, IntPredicate};
use morphic_lib::UpdateMode;
use roc_builtins::bitcode;
use roc_intern::Interner;
use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, InLayout, Layout, LayoutIds, STLayoutInterner};
use roc_mono::layout::{Builtin, InLayout, Layout, LayoutIds, LayoutInterner, STLayoutInterner};
use super::bitcode::{call_list_bitcode_fn, BitcodeReturns};
use super::build::{

View File

@ -12,9 +12,10 @@ use inkwell::values::{
use inkwell::{AddressSpace, FloatPredicate, IntPredicate};
use roc_builtins::bitcode;
use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_intern::Interner;
use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, InLayout, Layout, LayoutIds, STLayoutInterner, UnionLayout};
use roc_mono::layout::{
Builtin, InLayout, Layout, LayoutIds, LayoutInterner, STLayoutInterner, UnionLayout,
};
use super::build::{load_roc_value, use_roc_value, BuilderExt};
use super::convert::argument_type_from_union_layout;

View File

@ -5,8 +5,9 @@ use inkwell::types::{BasicType, BasicTypeEnum, FloatType, IntType, StructType};
use inkwell::values::StructValue;
use inkwell::AddressSpace;
use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_intern::Interner;
use roc_mono::layout::{round_up_to_alignment, Builtin, Layout, STLayoutInterner, UnionLayout};
use roc_mono::layout::{
round_up_to_alignment, Builtin, Layout, LayoutInterner, STLayoutInterner, UnionLayout,
};
use roc_target::TargetInfo;
fn basic_type_from_record<'a, 'ctx, 'env>(

View File

@ -9,10 +9,9 @@ use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum};
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue};
use inkwell::AddressSpace;
use roc_builtins::bitcode;
use roc_intern::Interner;
use roc_module::symbol::Symbol;
use roc_mono::ir::LookupType;
use roc_mono::layout::{Builtin, Layout, LayoutIds, STLayoutInterner, UnionLayout};
use roc_mono::layout::{Builtin, Layout, LayoutIds, LayoutInterner, STLayoutInterner, UnionLayout};
use roc_region::all::Region;
use super::build::BuilderExt;

View File

@ -9,11 +9,10 @@ use inkwell::{
use morphic_lib::{FuncSpec, UpdateMode};
use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
use roc_error_macros::internal_error;
use roc_intern::Interner;
use roc_module::{low_level::LowLevel, symbol::Symbol};
use roc_mono::{
ir::HigherOrderLowLevel,
layout::{Builtin, LambdaSet, Layout, LayoutIds, STLayoutInterner},
layout::{Builtin, LambdaSet, Layout, LayoutIds, LayoutInterner, STLayoutInterner},
};
use roc_target::PtrWidth;

View File

@ -15,10 +15,11 @@ use inkwell::values::{
BasicValue, BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue,
};
use inkwell::{AddressSpace, IntPredicate};
use roc_intern::Interner;
use roc_module::symbol::Interns;
use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, InLayout, Layout, LayoutIds, STLayoutInterner, UnionLayout};
use roc_mono::layout::{
Builtin, InLayout, Layout, LayoutIds, LayoutInterner, STLayoutInterner, UnionLayout,
};
use super::build::{cast_if_necessary_for_opaque_recursive_pointers, load_roc_value, FunctionSpec};
use super::convert::{argument_type_from_layout, argument_type_from_union_layout};

View File

@ -9,7 +9,6 @@ description = "Provides the WASM backend to generate Roc binaries."
[dependencies]
roc_builtins = { path = "../builtins" }
roc_collections = { path = "../collections" }
roc_intern = { path = "../intern" }
roc_module = { path = "../module" }
roc_mono = { path = "../mono" }
roc_target = { path = "../roc_target" }

View File

@ -4,7 +4,6 @@ use bumpalo::collections::{String, Vec};
use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_collections::all::MutMap;
use roc_error_macros::internal_error;
use roc_intern::Interner;
use roc_module::low_level::{LowLevel, LowLevelWrapperType};
use roc_module::symbol::{Interns, Symbol};
use roc_mono::code_gen_help::{CodeGenHelp, HelperOp, REFCOUNT_MAX};
@ -12,7 +11,9 @@ use roc_mono::ir::{
BranchInfo, CallType, CrashTag, Expr, JoinPointId, ListLiteralElement, Literal, ModifyRc,
Param, Proc, ProcLayout, Stmt,
};
use roc_mono::layout::{Builtin, Layout, LayoutIds, STLayoutInterner, TagIdIntType, UnionLayout};
use roc_mono::layout::{
Builtin, Layout, LayoutIds, LayoutInterner, STLayoutInterner, TagIdIntType, UnionLayout,
};
use roc_std::RocDec;
use roc_wasm_module::linking::{DataSymbol, WasmObjectSymbol};

View File

@ -2,12 +2,11 @@ use bumpalo::collections::Vec;
use bumpalo::Bump;
use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
use roc_error_macros::internal_error;
use roc_intern::Interner;
use roc_module::low_level::LowLevel;
use roc_module::symbol::Symbol;
use roc_mono::code_gen_help::HelperOp;
use roc_mono::ir::{HigherOrderLowLevel, PassedFunction, ProcLayout};
use roc_mono::layout::{Builtin, FieldOrderHash, InLayout, Layout, UnionLayout};
use roc_mono::layout::{Builtin, FieldOrderHash, InLayout, Layout, LayoutInterner, UnionLayout};
use roc_mono::low_level::HigherOrder;
use crate::backend::{ProcLookupData, ProcSource, WasmBackend};

View File

@ -1,11 +0,0 @@
[package]
name = "roc_intern"
version = "0.0.1"
authors = ["The Roc Contributors"]
license = "UPL-1.0"
edition = "2021"
description = "Provides generic interners for concurrent and single-thread use cases."
[dependencies]
roc_collections = { path = "../collections" }
parking_lot = "0.12"

View File

@ -1,253 +0,0 @@
//! Generic interners for concurrent and single-thread use cases.
use std::{
cell::RefCell,
hash::{BuildHasher, Hash, Hasher},
marker::PhantomData,
sync::Arc,
};
use parking_lot::{Mutex, RwLock};
use roc_collections::{default_hasher, BumpMap};
/// An interned value.
///
/// When possible, prefer comparing/hashing on the [Interned] representation of a value, rather
/// than the value itself.
#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Interned<T>(usize, std::marker::PhantomData<T>);
impl<T> Clone for Interned<T> {
fn clone(&self) -> Self {
Self(self.0, Default::default())
}
}
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.
///
/// The interner does not currently maintain its own arena; you will have to supply
/// values-to-be-interned as allocated in an independent arena.
///
/// If you need a concurrent global interner, you'll likely want each thread to take a
/// [ThreadLocalInterner] via [GlobalInterner::fork], for caching purposes.
///
/// Originally derived from https://gist.github.com/matklad/44ba1a5a6168bc0c26c995131c007907;
/// thank you, Aleksey!
#[derive(Debug)]
pub struct GlobalInterner<'a, K> {
map: Mutex<BumpMap<&'a K, Interned<K>>>,
vec: RwLock<Vec<&'a K>>,
}
/// A derivative of a [GlobalInterner] interner that provides caching desirable for
/// thread-local workloads. The only way to get a [ThreadLocalInterner] is via
/// [GlobalInterner::fork].
///
/// All values interned into a [ThreadLocalInterner] are made available in its parent
/// [GlobalInterner], making this suitable for global sharing of interned values.
///
/// Originally derived from https://gist.github.com/matklad/44ba1a5a6168bc0c26c995131c007907;
/// thank you, Aleksey!
#[derive(Debug)]
pub struct ThreadLocalInterner<'a, K> {
parent: Arc<GlobalInterner<'a, K>>,
map: BumpMap<&'a K, Interned<K>>,
/// Cache of interned values from the parent for local access.
vec: RefCell<Vec<Option<&'a K>>>,
}
/// A single-threaded interner, with no concurrency properties.
///
/// The only way to construct such an interner is to collapse a shared [GlobalInterner] into
/// a [SingleThreadedInterner], via [GlobalInterner::unwrap].
#[derive(Debug)]
pub struct SingleThreadedInterner<'a, K> {
map: BumpMap<&'a K, Interned<K>>,
vec: Vec<&'a K>,
}
/// Generic hasher for a value, to be used by all interners.
///
/// This uses the [default_hasher], so interner maps should also rely on [default_hasher].
fn hash<V: Hash>(val: V) -> u64 {
let mut state = roc_collections::all::BuildHasher::default().build_hasher();
val.hash(&mut state);
state.finish()
}
pub trait Interner<'a, K: Hash + Eq> {
/// Interns a value, returning its interned representation.
/// If the value has been interned before, the old interned representation will be re-used.
///
/// Note that the provided value must be allocated into an arena of your choosing, but which
/// must live at least as long as the interner lives.
// TODO: we should consider maintaining our own arena in the interner, to avoid redundant
// allocations when values already have interned representations.
fn insert(&mut self, value: &'a K) -> Interned<K>;
/// Retrieves a value from the interner.
fn get(&self, key: Interned<K>) -> &'a K;
}
impl<'a, K: Hash + Eq> GlobalInterner<'a, K> {
/// Creates a new global interner with the given capacity.
pub fn with_capacity(cap: usize) -> Arc<GlobalInterner<'a, K>> {
let map: BumpMap<&'a K, Interned<K>> =
BumpMap::with_capacity_and_hasher(cap, default_hasher());
Arc::new(GlobalInterner {
map: Mutex::new(map),
vec: RwLock::new(Vec::with_capacity(cap)),
})
}
/// Creates a derivative [ThreadLocalInterner] pointing back to this global interner.
pub fn fork(self: &Arc<GlobalInterner<'a, K>>) -> ThreadLocalInterner<'a, K> {
ThreadLocalInterner {
parent: Arc::clone(self),
map: Default::default(),
vec: Default::default(),
}
}
/// Collapses a shared [GlobalInterner] into a [SingleThreadedInterner].
///
/// Returns an [Err] with `self` if there are outstanding references to the [GlobalInterner].
pub fn unwrap(
self: Arc<GlobalInterner<'a, K>>,
) -> Result<SingleThreadedInterner<'a, K>, Arc<Self>> {
let GlobalInterner { map, vec } = Arc::try_unwrap(self)?;
let map = Mutex::into_inner(map);
let vec = RwLock::into_inner(vec);
Ok(SingleThreadedInterner { map, vec })
}
/// Interns a value with a pre-computed hash.
/// Prefer calling this when possible, especially from [ThreadLocalInterner], to avoid
/// re-computing hashes.
fn insert_hashed(&self, value: &'a K, hash: u64) -> Interned<K> {
let mut map = self.map.lock();
let (_, interned) = map
.raw_entry_mut()
.from_key_hashed_nocheck(hash, &value)
.or_insert_with(|| {
let mut vec = self.vec.write();
let interned = Interned(vec.len(), Default::default());
vec.push(value);
(value, interned)
});
*interned
}
fn get(&self, interned: Interned<K>) -> &'a K {
let Interned(index, _) = interned;
self.vec.read()[index]
}
pub fn is_empty(&self) -> bool {
self.vec.read().is_empty()
}
}
impl<'a, K: Hash + Eq> ThreadLocalInterner<'a, K> {
/// Records an interned value in thread-specific storage, for faster access on lookups.
fn record(&self, key: &'a K, interned: Interned<K>) {
let mut vec = self.vec.borrow_mut();
let len = vec.len().max(interned.0 + 1);
vec.resize(len, None);
vec[interned.0] = Some(key);
}
}
impl<'a, K: Hash + Eq> Interner<'a, K> for ThreadLocalInterner<'a, K> {
fn insert(&mut self, value: &'a K) -> Interned<K> {
let global = &*self.parent;
let hash = hash(value);
let (&mut value, &mut interned) = self
.map
.raw_entry_mut()
.from_key_hashed_nocheck(hash, &value)
.or_insert_with(|| {
let interned = global.insert_hashed(value, hash);
(value, interned)
});
self.record(value, interned);
interned
}
fn get(&self, key: Interned<K>) -> &'a K {
if let Some(Some(value)) = self.vec.borrow().get(key.0) {
return value;
}
let value = self.parent.get(key);
self.record(value, key);
value
}
}
impl<'a, K> SingleThreadedInterner<'a, K> {
/// Creates a new single threaded interner with the given capacity.
pub fn with_capacity(cap: usize) -> Self {
Self {
map: BumpMap::with_capacity_and_hasher(cap, default_hasher()),
vec: Vec::with_capacity(cap),
}
}
/// Promotes the [SingleThreadedInterner] back to a [GlobalInterner].
///
/// You should *only* use this if you need to go from a single-threaded to a concurrent context,
/// or in a case where you explicitly need access to [ThreadLocalInterner]s.
pub fn into_global(self) -> Arc<GlobalInterner<'a, K>> {
let SingleThreadedInterner { map, vec } = self;
Arc::new(GlobalInterner {
map: Mutex::new(map),
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> {
fn insert(&mut self, value: &'a K) -> Interned<K> {
let hash = hash(value);
let (_, interned) = self
.map
.raw_entry_mut()
.from_key_hashed_nocheck(hash, value)
.or_insert_with(|| {
let interned = Interned(self.vec.len(), Default::default());
self.vec.push(value);
(value, interned)
});
*interned
}
fn get(&self, key: Interned<K>) -> &'a K {
let Interned(index, _) = key;
self.vec[index]
}
}

View File

@ -24,7 +24,6 @@ roc_solve = { path = "../solve" }
roc_solve_problem = { path = "../solve_problem" }
roc_late_solve = { path = "../late_solve" }
roc_mono = { path = "../mono" }
roc_intern = { path = "../intern" }
roc_target = { path = "../roc_target" }
roc_tracing = { path = "../../tracing" }
roc_packaging = { path = "../../packaging" }

View File

@ -15,7 +15,6 @@ roc_types = { path = "../types" }
roc_can = { path = "../can" }
roc_derive_key = { path = "../derive_key" }
roc_derive = { path = "../derive" }
roc_intern = { path = "../intern" }
roc_late_solve = { path = "../late_solve" }
roc_std = { path = "../../roc_std" }
roc_problem = { path = "../problem" }
@ -30,3 +29,4 @@ bumpalo.workspace = true
hashbrown.workspace = true
static_assertions.workspace = true
bitvec.workspace = true
parking_lot.workspace = true

View File

@ -1,12 +1,13 @@
use bumpalo::collections::vec::Vec;
use roc_intern::Interner;
use roc_module::low_level::LowLevel;
use roc_module::symbol::{IdentIds, Symbol};
use crate::ir::{
BranchInfo, Call, CallType, Expr, JoinPointId, Literal, Param, Stmt, UpdateModeId,
};
use crate::layout::{Builtin, InLayout, Layout, STLayoutInterner, TagIdIntType, UnionLayout};
use crate::layout::{
Builtin, InLayout, Layout, LayoutInterner, STLayoutInterner, TagIdIntType, UnionLayout,
};
use super::{let_lowlevel, CodeGenHelp, Context, LAYOUT_BOOL};

View File

@ -1,6 +1,5 @@
use bumpalo::collections::vec::Vec;
use bumpalo::Bump;
use roc_intern::Interner;
use roc_module::low_level::LowLevel;
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
use roc_target::TargetInfo;
@ -9,7 +8,9 @@ use crate::ir::{
Call, CallSpecId, CallType, Expr, HostExposedLayouts, JoinPointId, ModifyRc, Proc, ProcLayout,
SelfRecursive, Stmt, UpdateModeId,
};
use crate::layout::{Builtin, LambdaName, Layout, Niche, STLayoutInterner, UnionLayout};
use crate::layout::{
Builtin, LambdaName, Layout, LayoutInterner, Niche, STLayoutInterner, UnionLayout,
};
mod equality;
mod refcount;

View File

@ -2,7 +2,6 @@
use bumpalo::collections::vec::Vec;
use roc_builtins::bitcode::IntWidth;
use roc_intern::Interner;
use roc_module::low_level::{LowLevel, LowLevel::*};
use roc_module::symbol::{IdentIds, Symbol};
use roc_target::PtrWidth;

View File

@ -2,7 +2,6 @@
use bumpalo::Bump;
use roc_collections::{MutMap, VecMap, VecSet};
use roc_intern::Interner;
use roc_module::symbol::Symbol;
use crate::{
@ -10,7 +9,9 @@ use crate::{
Call, CallSpecId, CallType, Expr, HigherOrderLowLevel, JoinPointId, ListLiteralElement,
ModifyRc, Param, Proc, ProcLayout, Stmt,
},
layout::{Builtin, LambdaSet, Layout, STLayoutInterner, TagIdIntType, UnionLayout},
layout::{
Builtin, LambdaSet, Layout, LayoutInterner, STLayoutInterner, TagIdIntType, UnionLayout,
},
};
pub enum UseKind {

View File

@ -2,12 +2,13 @@ use crate::ir::{
build_list_index_probe, BranchInfo, Call, CallType, DestructType, Env, Expr, JoinPointId,
ListIndex, Literal, Param, Pattern, Procs, Stmt,
};
use crate::layout::{Builtin, Layout, LayoutCache, TLLayoutInterner, TagIdIntType, UnionLayout};
use crate::layout::{
Builtin, Layout, LayoutCache, LayoutInterner, TLLayoutInterner, TagIdIntType, UnionLayout,
};
use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_collections::all::{MutMap, MutSet};
use roc_error_macros::internal_error;
use roc_exhaustive::{Ctor, CtorName, ListArity, RenderAs, TagId, Union};
use roc_intern::Interner;
use roc_module::ident::TagName;
use roc_module::low_level::LowLevel;
use roc_module::symbol::Symbol;

View File

@ -5,7 +5,6 @@ use bumpalo::Bump;
use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_collections::all::{default_hasher, FnvMap, MutMap};
use roc_error_macros::{internal_error, todo_abilities};
use roc_intern::Interner;
use roc_module::ident::{Lowercase, TagName};
use roc_module::symbol::{Interns, Symbol};
use roc_problem::can::RuntimeError;
@ -23,7 +22,9 @@ use std::hash::{Hash, Hasher};
use ven_pretty::{DocAllocator, DocBuilder};
mod intern;
pub use intern::{GlobalLayoutInterner, LayoutInterner, STLayoutInterner, TLLayoutInterner};
pub use intern::{
GlobalLayoutInterner, InLayout, LayoutInterner, STLayoutInterner, TLLayoutInterner,
};
// if your changes cause this number to go down, great!
// please change it to the lower number.
@ -661,10 +662,6 @@ impl FieldOrderHash {
}
}
/// An interned layout.
// One day I would like to take over `LayoutId` as the name here, but that is currently used elsewhere.
pub type InLayout<'a> = roc_intern::Interned<Layout<'a>>;
/// Types for code gen must be monomorphic. No type variables allowed!
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Layout<'a> {

View File

@ -1,25 +1,33 @@
use std::{marker::PhantomData, sync::Arc};
use std::{
cell::RefCell,
hash::{BuildHasher, Hasher},
marker::PhantomData,
sync::Arc,
};
use parking_lot::{Mutex, RwLock};
use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_intern::{GlobalInterner, Interned, Interner, SingleThreadedInterner, ThreadLocalInterner};
use roc_collections::{default_hasher, BumpMap};
use roc_target::TargetInfo;
use super::{Builtin, Layout};
pub struct InternedLayouts(PhantomData<()>);
#[allow(unused)] // for now
pub struct InLayouts(PhantomData<()>);
macro_rules! cache_interned_layouts {
($($i:literal, $name:ident, $layout:expr)*; $total_constants:literal) => {
impl InternedLayouts {
impl InLayouts {
$(
pub const $name: Interned<Layout<'static>> = unsafe { Interned::from_reserved_index($i) };
#[allow(unused)] // for now
pub const $name: InLayout<'static> = unsafe { InLayout::from_reserved_index($i) };
)*
}
fn fill_reserved_layouts<'a>(interner: &mut STLayoutInterner<'a>) {
assert!(interner.0.is_empty());
assert!(interner.is_empty());
$(
interner.0.insert(&$layout);
interner.insert(&$layout);
)*
}
@ -59,8 +67,9 @@ cache_interned_layouts! {
; 16
}
impl InternedLayouts {
pub const fn from_int_width(w: IntWidth) -> Interned<Layout<'static>> {
impl InLayouts {
#[allow(unused)] // for now
pub const fn from_int_width(w: IntWidth) -> InLayout<'static> {
match w {
IntWidth::U8 => Self::U8,
IntWidth::U16 => Self::U16,
@ -74,7 +83,8 @@ impl InternedLayouts {
IntWidth::I128 => Self::I128,
}
}
pub const fn from_float_width(w: FloatWidth) -> Interned<Layout<'static>> {
#[allow(unused)] // for now
pub const fn from_float_width(w: FloatWidth) -> InLayout<'static> {
match w {
FloatWidth::F32 => Self::F32,
FloatWidth::F64 => Self::F64,
@ -82,63 +92,248 @@ impl InternedLayouts {
}
}
pub trait LayoutInterner<'a>: Interner<'a, Layout<'a>> + Sized {
fn alignment_bytes(&self, target_info: TargetInfo, layout: Interned<Layout<'a>>) -> u32 {
pub trait LayoutInterner<'a>: Sized {
/// Interns a value, returning its interned representation.
/// If the value has been interned before, the old interned representation will be re-used.
///
/// Note that the provided value must be allocated into an arena of your choosing, but which
/// must live at least as long as the interner lives.
// TODO: we should consider maintaining our own arena in the interner, to avoid redundant
// allocations when values already have interned representations.
fn insert(&mut self, value: &'a Layout<'a>) -> InLayout<'a>;
/// Retrieves a value from the interner.
fn get(&self, key: InLayout<'a>) -> &'a Layout<'a>;
fn alignment_bytes(&self, target_info: TargetInfo, layout: InLayout<'a>) -> u32 {
self.get(layout).alignment_bytes(self, target_info)
}
}
/// An interned layout.
///
/// When possible, prefer comparing/hashing on the [InLayout] representation of a value, rather
/// than the value itself.
#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct InLayout<'a>(usize, std::marker::PhantomData<&'a ()>);
impl<'a> Clone for InLayout<'a> {
fn clone(&self) -> Self {
Self(self.0, Default::default())
}
}
impl<'a> Copy for InLayout<'a> {}
impl<'a> InLayout<'a> {
/// # 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 = InLayout::from_reserved_index(0);
/// let interner = GlobalLayoutInterner::with_capacity(1);
/// let inserted = interner.insert("something");
/// assert_eq!(reserved_interned, inserted);
/// ```
const unsafe fn from_reserved_index(index: usize) -> Self {
Self(index, PhantomData)
}
}
/// A concurrent interner, suitable for usage between threads.
///
/// The interner does not currently maintain its own arena; you will have to supply
/// values-to-be-interned as allocated in an independent arena.
///
/// If you need a concurrent global interner, you'll likely want each thread to take a
/// [TLLayoutInterner] via [GlobalLayoutInterner::fork], for caching purposes.
///
/// Originally derived from https://gist.github.com/matklad/44ba1a5a6168bc0c26c995131c007907;
/// thank you, Aleksey!
#[derive(Debug)]
pub struct GlobalLayoutInterner<'a>(Arc<GlobalInterner<'a, Layout<'a>>>);
pub struct GlobalLayoutInterner<'a>(Arc<GlobalLayoutInternerInner<'a>>);
#[derive(Debug)]
pub struct TLLayoutInterner<'a>(ThreadLocalInterner<'a, Layout<'a>>);
struct GlobalLayoutInternerInner<'a> {
map: Mutex<BumpMap<&'a Layout<'a>, InLayout<'a>>>,
vec: RwLock<Vec<&'a Layout<'a>>>,
}
/// A derivative of a [GlobalLayoutInterner] interner that provides caching desirable for
/// thread-local workloads. The only way to get a [TLLayoutInterner] is via
/// [GlobalLayoutInterner::fork].
///
/// All values interned into a [TLLayoutInterner] are made available in its parent
/// [GlobalLayoutInterner], making this suitable for global sharing of interned values.
///
/// Originally derived from https://gist.github.com/matklad/44ba1a5a6168bc0c26c995131c007907;
/// thank you, Aleksey!
#[derive(Debug)]
pub struct STLayoutInterner<'a>(SingleThreadedInterner<'a, Layout<'a>>);
pub struct TLLayoutInterner<'a> {
parent: GlobalLayoutInterner<'a>,
map: BumpMap<&'a Layout<'a>, InLayout<'a>>,
/// Cache of interned values from the parent for local access.
vec: RefCell<Vec<Option<&'a Layout<'a>>>>,
}
/// A single-threaded interner, with no concurrency properties.
///
/// The only way to construct such an interner is to collapse a shared [GlobalLayoutInterner] into
/// a [STLayoutInterner], via [GlobalLayoutInterner::unwrap].
#[derive(Debug)]
pub struct STLayoutInterner<'a> {
map: BumpMap<&'a Layout<'a>, InLayout<'a>>,
vec: Vec<&'a Layout<'a>>,
}
/// Generic hasher for a value, to be used by all interners.
///
/// This uses the [default_hasher], so interner maps should also rely on [default_hasher].
fn hash<V: std::hash::Hash>(val: V) -> u64 {
let mut state = roc_collections::all::BuildHasher::default().build_hasher();
val.hash(&mut state);
state.finish()
}
impl<'a> GlobalLayoutInterner<'a> {
pub fn with_capacity(capacity: usize) -> Self {
STLayoutInterner::with_capacity(capacity).into_global()
/// Creates a new global interner with the given capacity.
pub fn with_capacity(cap: usize) -> Self {
STLayoutInterner::with_capacity(cap).into_global()
}
/// Creates a derivative [TLLayoutInterner] pointing back to this global interner.
pub fn fork(&self) -> TLLayoutInterner<'a> {
TLLayoutInterner(self.0.fork())
}
pub fn unwrap(self) -> Result<STLayoutInterner<'a>, Self> {
match self.0.unwrap() {
Ok(st) => Ok(STLayoutInterner(st)),
Err(global) => Err(Self(global)),
TLLayoutInterner {
parent: Self(Arc::clone(&self.0)),
map: Default::default(),
vec: Default::default(),
}
}
/// Collapses a shared [GlobalLayoutInterner] into a [STLayoutInterner].
///
/// Returns an [Err] with `self` if there are outstanding references to the [GlobalLayoutInterner].
pub fn unwrap(self) -> Result<STLayoutInterner<'a>, Self> {
let GlobalLayoutInternerInner { map, vec } = match Arc::try_unwrap(self.0) {
Ok(inner) => inner,
Err(li) => return Err(Self(li)),
};
let map = Mutex::into_inner(map);
let vec = RwLock::into_inner(vec);
Ok(STLayoutInterner { map, vec })
}
/// Interns a value with a pre-computed hash.
/// Prefer calling this when possible, especially from [TLLayoutInterner], to avoid
/// re-computing hashes.
fn insert_hashed(&self, value: &'a Layout<'a>, hash: u64) -> InLayout<'a> {
let mut map = self.0.map.lock();
let (_, interned) = map
.raw_entry_mut()
.from_key_hashed_nocheck(hash, &value)
.or_insert_with(|| {
let mut vec = self.0.vec.write();
let interned = InLayout(vec.len(), Default::default());
vec.push(value);
(value, interned)
});
*interned
}
fn get(&self, interned: InLayout<'a>) -> &'a Layout<'a> {
let InLayout(index, _) = interned;
self.0.vec.read()[index]
}
pub fn is_empty(&self) -> bool {
self.0.vec.read().is_empty()
}
}
impl<'a> TLLayoutInterner<'a> {
/// Records an interned value in thread-specific storage, for faster access on lookups.
fn record(&self, key: &'a Layout<'a>, interned: InLayout<'a>) {
let mut vec = self.vec.borrow_mut();
let len = vec.len().max(interned.0 + 1);
vec.resize(len, None);
vec[interned.0] = Some(key);
}
}
impl<'a> LayoutInterner<'a> for TLLayoutInterner<'a> {
fn insert(&mut self, value: &'a Layout<'a>) -> InLayout<'a> {
let global = &self.parent;
let hash = hash(value);
let (&mut value, &mut interned) = self
.map
.raw_entry_mut()
.from_key_hashed_nocheck(hash, &value)
.or_insert_with(|| {
let interned = global.insert_hashed(value, hash);
(value, interned)
});
self.record(value, interned);
interned
}
fn get(&self, key: InLayout<'a>) -> &'a Layout<'a> {
if let Some(Some(value)) = self.vec.borrow().get(key.0) {
return value;
}
let value = self.parent.get(key);
self.record(value, key);
value
}
}
impl<'a> STLayoutInterner<'a> {
pub fn with_capacity(capacity: usize) -> Self {
let mut interner = Self(SingleThreadedInterner::with_capacity(capacity));
/// Creates a new single threaded interner with the given capacity.
pub fn with_capacity(cap: usize) -> Self {
let mut interner = Self {
map: BumpMap::with_capacity_and_hasher(cap, default_hasher()),
vec: Vec::with_capacity(cap),
};
fill_reserved_layouts(&mut interner);
interner
}
/// Promotes the [STLayoutInterner] back to a [GlobalLayoutInterner].
///
/// You should *only* use this if you need to go from a single-threaded to a concurrent context,
/// or in a case where you explicitly need access to [TLLayoutInterner]s.
pub fn into_global(self) -> GlobalLayoutInterner<'a> {
GlobalLayoutInterner(self.0.into_global())
let STLayoutInterner { map, vec } = self;
GlobalLayoutInterner(Arc::new(GlobalLayoutInternerInner {
map: Mutex::new(map),
vec: RwLock::new(vec),
}))
}
pub fn is_empty(&self) -> bool {
self.vec.is_empty()
}
}
impl<'a> Interner<'a, Layout<'a>> for TLLayoutInterner<'a> {
fn insert(&mut self, value: &'a Layout<'a>) -> Interned<Layout<'a>> {
self.0.insert(value)
impl<'a> LayoutInterner<'a> for STLayoutInterner<'a> {
fn insert(&mut self, value: &'a Layout<'a>) -> InLayout<'a> {
let hash = hash(value);
let (_, interned) = self
.map
.raw_entry_mut()
.from_key_hashed_nocheck(hash, value)
.or_insert_with(|| {
let interned = InLayout(self.vec.len(), Default::default());
self.vec.push(value);
(value, interned)
});
*interned
}
fn get(&self, key: Interned<Layout<'a>>) -> &'a Layout<'a> {
self.0.get(key)
fn get(&self, key: InLayout<'a>) -> &'a Layout<'a> {
let InLayout(index, _) = key;
self.vec[index]
}
}
impl<'a> LayoutInterner<'a> for TLLayoutInterner<'a> {}
impl<'a> Interner<'a, Layout<'a>> for STLayoutInterner<'a> {
fn insert(&mut self, value: &'a Layout<'a>) -> Interned<Layout<'a>> {
self.0.insert(value)
}
fn get(&self, key: Interned<Layout<'a>>) -> &'a Layout<'a> {
self.0.get(key)
}
}
impl<'a> LayoutInterner<'a> for STLayoutInterner<'a> {}

View File

@ -9,7 +9,6 @@ description = "Generates code needed for platform hosts to communicate with Roc
[dependencies]
roc_std = { path = "../roc_std"}
roc_can = { path = "../compiler/can" }
roc_intern = { path = "../compiler/intern" }
roc_mono = { path = "../compiler/mono" }
roc_load = { path = "../compiler/load" }
roc_reporting = { path = "../reporting" }

View File

@ -29,7 +29,6 @@ roc_build = {path = "../compiler/build"}
roc_builtins = {path = "../compiler/builtins"}
roc_collections = {path = "../compiler/collections"}
roc_gen_llvm = {path = "../compiler/gen_llvm"}
roc_intern = {path = "../compiler/intern"}
roc_load = {path = "../compiler/load"}
roc_mono = {path = "../compiler/mono"}
roc_parse = {path = "../compiler/parse"}

View File

@ -15,7 +15,6 @@ roc_builtins = {path = "../compiler/builtins"}
roc_can = {path = "../compiler/can"}
roc_collections = {path = "../compiler/collections"}
roc_fmt = {path = "../compiler/fmt"}
roc_intern = {path = "../compiler/intern"}
roc_load = {path = "../compiler/load"}
roc_module = {path = "../compiler/module"}
roc_mono = {path = "../compiler/mono"}

View File

@ -1,6 +1,5 @@
use bumpalo::collections::Vec;
use bumpalo::Bump;
use roc_intern::Interner;
use roc_types::types::AliasKind;
use std::cmp::{max_by_key, min_by_key};
@ -11,8 +10,8 @@ use roc_module::ident::TagName;
use roc_module::symbol::{Interns, ModuleId, Symbol};
use roc_mono::ir::ProcLayout;
use roc_mono::layout::{
self, union_sorted_tags_pub, Builtin, InLayout, Layout, LayoutCache, TLLayoutInterner,
UnionLayout, UnionVariant, WrappedVariant,
self, union_sorted_tags_pub, Builtin, InLayout, Layout, LayoutCache, LayoutInterner,
TLLayoutInterner, UnionLayout, UnionVariant, WrappedVariant,
};
use roc_parse::ast::{AssignedField, Collection, Expr, Pattern, StrLiteral};
use roc_region::all::{Loc, Region};

View File

@ -17,7 +17,6 @@ inkwell.workspace = true
roc_builtins = {path = "../compiler/builtins"}
roc_can = {path = "../compiler/can"}
roc_collections = {path = "../compiler/collections"}
roc_intern = {path = "../compiler/intern"}
roc_load = {path = "../compiler/load"}
roc_mono = {path = "../compiler/mono"}
roc_parse = {path = "../compiler/parse"}