Intern captures layouts

This commit is contained in:
Ayaz Hafiz 2022-12-28 17:05:04 -06:00
parent 2f7c4b4083
commit 7045001f64
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
6 changed files with 90 additions and 48 deletions

View file

@ -3028,14 +3028,14 @@ fn update<'a>(
std::mem::swap(&mut state.layout_interner, &mut taken);
taken
};
let layout_interner = layout_interner
let mut layout_interner = layout_interner
.unwrap()
.expect("outstanding references to global layout interener, but we just drained all layout caches");
log!("specializations complete from {:?}", module_id);
debug_print_ir!(state, &layout_interner, ROC_PRINT_IR_AFTER_SPECIALIZATION);
debug_check_ir!(state, arena, &layout_interner, ROC_CHECK_MONO_IR);
debug_check_ir!(state, arena, &mut layout_interner, ROC_CHECK_MONO_IR);
let ident_ids = state.constrained_ident_ids.get_mut(&module_id).unwrap();

View file

@ -2,6 +2,7 @@
use bumpalo::Bump;
use roc_collections::{MutMap, VecMap, VecSet};
use roc_intern::Interner;
use roc_module::symbol::Symbol;
use crate::{
@ -131,8 +132,8 @@ impl<'a> Problems<'a> {
pub fn check_procs<'a>(
arena: &'a Bump,
interner: &'a STLayoutInterner<'a>,
procs: &'a Procs<'a>,
interner: &mut STLayoutInterner<'a>,
procs: &Procs<'a>,
) -> Problems<'a> {
let mut problems = Default::default();
@ -161,9 +162,9 @@ type JoinPoints<'a> = VecMap<JoinPointId, (usize, &'a [Param<'a>])>;
type CallSpecIds = VecMap<CallSpecId, usize>;
struct Ctx<'a, 'r> {
arena: &'a Bump,
interner: &'a STLayoutInterner<'a>,
interner: &'r mut STLayoutInterner<'a>,
problems: &'r mut Vec<Problem<'a>>,
proc: &'a Proc<'a>,
proc: &'r Proc<'a>,
proc_layout: ProcLayout<'a>,
procs: &'r Procs<'a>,
call_spec_ids: CallSpecIds,
@ -180,7 +181,7 @@ impl<'a, 'r> Ctx<'a, 'r> {
fn problem(&mut self, problem_kind: ProblemKind<'a>) {
self.problems.push(Problem {
proc: self.proc,
proc: self.arena.alloc(self.proc.clone()),
proc_layout: self.proc_layout,
line: self.line,
kind: problem_kind,
@ -526,7 +527,8 @@ impl<'a, 'r> Ctx<'a, 'r> {
return None;
}
let layout = resolve_recursive_layout(
self.arena,
ctx.arena,
&mut ctx.interner,
payloads[index as usize],
union_layout,
);
@ -613,8 +615,12 @@ impl<'a, 'r> Ctx<'a, 'r> {
});
}
for (arg, wanted_layout) in arguments.iter().zip(payloads.iter()) {
let wanted_layout =
resolve_recursive_layout(self.arena, *wanted_layout, union_layout);
let wanted_layout = resolve_recursive_layout(
self.arena,
self.interner,
*wanted_layout,
union_layout,
);
self.check_sym_layout(*arg, wanted_layout, UseKind::TagPayloadArg);
}
}
@ -633,12 +639,13 @@ impl<'a, 'r> Ctx<'a, 'r> {
fn resolve_recursive_layout<'a>(
arena: &'a Bump,
interner: &mut STLayoutInterner<'a>,
layout: Layout<'a>,
when_recursive: UnionLayout<'a>,
) -> Layout<'a> {
macro_rules! go {
($lay:expr) => {
resolve_recursive_layout(arena, $lay, when_recursive)
resolve_recursive_layout(arena, interner, $lay, when_recursive)
};
}
@ -671,7 +678,7 @@ fn resolve_recursive_layout<'a>(
} => {
let field_layouts = field_layouts
.iter()
.map(|lay| resolve_recursive_layout(arena, *lay, when_recursive));
.map(|lay| resolve_recursive_layout(arena, interner, *lay, when_recursive));
let field_layouts = arena.alloc_slice_fill_iter(field_layouts);
Layout::Struct {
field_order_hash,
@ -680,7 +687,12 @@ fn resolve_recursive_layout<'a>(
}
Layout::Builtin(builtin) => match builtin {
Builtin::List(inner) => {
let inner = arena.alloc(resolve_recursive_layout(arena, *inner, when_recursive));
let inner = arena.alloc(resolve_recursive_layout(
arena,
interner,
*inner,
when_recursive,
));
Layout::Builtin(Builtin::List(inner))
}
Builtin::Int(_)
@ -694,7 +706,10 @@ fn resolve_recursive_layout<'a>(
representation,
}) => {
let set = set.iter().map(|(symbol, captures)| {
let captures = captures.iter().map(|lay| go!(*lay));
let captures = captures.iter().map(|lay_in| {
let new_lay = go!(*interner.get(*lay_in));
interner.insert(arena.alloc(new_lay))
});
let captures = &*arena.alloc_slice_fill_iter(captures);
(*symbol, captures)
});

View file

@ -487,7 +487,7 @@ where
captures_niche
.0
.iter()
.map(|c| c.to_doc(f, interner, Parens::NotNeeded)),
.map(|&c| interner.get(c).to_doc(f, interner, Parens::NotNeeded)),
f.reflow(", "),
),
f.reflow("})"),

View file

@ -3533,10 +3533,12 @@ fn specialize_proc_help<'a>(
let ptr_bytes = env.target_info;
combined.sort_by(|(_, layout1), (_, layout2)| {
let size1 =
layout1.alignment_bytes(&layout_cache.interner, ptr_bytes);
let size2 =
layout2.alignment_bytes(&layout_cache.interner, ptr_bytes);
let size1 = layout_cache
.get_in(**layout1)
.alignment_bytes(&layout_cache.interner, ptr_bytes);
let size2 = layout_cache
.get_in(**layout2)
.alignment_bytes(&layout_cache.interner, ptr_bytes);
size2.cmp(&size1)
});
@ -3575,16 +3577,20 @@ fn specialize_proc_help<'a>(
let ptr_bytes = env.target_info;
combined.sort_by(|(_, layout1), (_, layout2)| {
let size1 =
layout1.alignment_bytes(&layout_cache.interner, ptr_bytes);
let size2 =
layout2.alignment_bytes(&layout_cache.interner, ptr_bytes);
let size1 = layout_cache
.get_in(**layout1)
.alignment_bytes(&layout_cache.interner, ptr_bytes);
let size2 = layout_cache
.get_in(**layout2)
.alignment_bytes(&layout_cache.interner, ptr_bytes);
size2.cmp(&size1)
});
let ordered_field_layouts = Vec::from_iter_in(
combined.iter().map(|(_, layout)| **layout),
combined
.iter()
.map(|(_, layout)| *layout_cache.get_in(**layout)),
env.arena,
);
let ordered_field_layouts = ordered_field_layouts.into_bump_slice();
@ -3610,7 +3616,7 @@ fn specialize_proc_help<'a>(
specialized_body = Stmt::Let(
symbol,
expr,
**layout,
*layout_cache.get_in(**layout),
env.arena.alloc(specialized_body),
);
}
@ -5730,8 +5736,12 @@ where
let ptr_bytes = env.target_info;
combined.sort_by(|(_, layout1), (_, layout2)| {
let size1 = layout1.alignment_bytes(&layout_cache.interner, ptr_bytes);
let size2 = layout2.alignment_bytes(&layout_cache.interner, ptr_bytes);
let size1 = layout_cache
.get_in(**layout1)
.alignment_bytes(&layout_cache.interner, ptr_bytes);
let size2 = layout_cache
.get_in(**layout2)
.alignment_bytes(&layout_cache.interner, ptr_bytes);
size2.cmp(&size1)
});
@ -5760,16 +5770,23 @@ where
let ptr_bytes = env.target_info;
combined.sort_by(|(_, layout1), (_, layout2)| {
let size1 = layout1.alignment_bytes(&layout_cache.interner, ptr_bytes);
let size2 = layout2.alignment_bytes(&layout_cache.interner, ptr_bytes);
let size1 = layout_cache
.get_in(**layout1)
.alignment_bytes(&layout_cache.interner, ptr_bytes);
let size2 = layout_cache
.get_in(**layout2)
.alignment_bytes(&layout_cache.interner, ptr_bytes);
size2.cmp(&size1)
});
let symbols =
Vec::from_iter_in(combined.iter().map(|(a, _)| *a), env.arena).into_bump_slice();
let field_layouts =
Vec::from_iter_in(combined.iter().map(|(_, b)| **b), env.arena).into_bump_slice();
let field_layouts = Vec::from_iter_in(
combined.iter().map(|(_, b)| *layout_cache.get_in(**b)),
env.arena,
)
.into_bump_slice();
debug_assert_eq!(
Layout::struct_no_name_order(field_layouts),

View file

@ -5,7 +5,7 @@ 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::{Interned, Interner, SingleThreadedInterner, ThreadLocalInterner};
use roc_intern::{Interner, SingleThreadedInterner, ThreadLocalInterner};
use roc_module::ident::{Lowercase, TagName};
use roc_module::symbol::{Interns, Symbol};
use roc_problem::can::RuntimeError;
@ -311,6 +311,10 @@ impl<'a> LayoutCache<'a> {
}
}
pub fn get_in(&self, interned: InLayout<'a>) -> &Layout<'a> {
self.interner.get(interned)
}
#[cfg(debug_assertions)]
pub fn statistics(&self) -> (CacheStatistics, CacheStatistics) {
(self.stats, self.raw_function_stats)
@ -653,6 +657,10 @@ 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> {
@ -1149,7 +1157,7 @@ impl Discriminant {
/// concurrently. The number does not change and will give a reliable output.
struct SetElement<'a> {
symbol: Symbol,
layout: &'a [Layout<'a>],
layout: &'a [InLayout<'a>],
}
impl std::fmt::Debug for SetElement<'_> {
@ -1163,7 +1171,7 @@ impl std::fmt::Debug for SetElement<'_> {
impl std::fmt::Debug for LambdaSet<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
struct Helper<'a> {
set: &'a [(Symbol, &'a [Layout<'a>])],
set: &'a [(Symbol, &'a [InLayout<'a>])],
}
impl std::fmt::Debug for Helper<'_> {
@ -1204,7 +1212,7 @@ impl std::fmt::Debug for LambdaSet<'_> {
///
/// See also https://github.com/roc-lang/roc/issues/3336.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct CapturesNiche<'a>(pub(crate) &'a [Layout<'a>]);
pub struct CapturesNiche<'a>(pub(crate) &'a [InLayout<'a>]);
impl CapturesNiche<'_> {
pub fn no_niche() -> Self {
@ -1254,9 +1262,9 @@ impl<'a> LambdaName<'a> {
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct LambdaSet<'a> {
/// collection of function names and their closure arguments
pub(crate) set: &'a [(Symbol, &'a [Layout<'a>])],
pub(crate) set: &'a [(Symbol, &'a [InLayout<'a>])],
/// how the closure will be represented at runtime
pub(crate) representation: Interned<Layout<'a>>,
pub(crate) representation: InLayout<'a>,
}
#[derive(Debug)]
@ -1271,7 +1279,7 @@ pub enum ClosureRepresentation<'a> {
/// The closure is represented as a union. Includes the tag ID!
/// Each variant is a different function, and its payloads are the captures.
Union {
alphabetic_order_fields: &'a [Layout<'a>],
alphabetic_order_fields: &'a [InLayout<'a>],
closure_name: Symbol,
tag_id: TagIdIntType,
union_layout: UnionLayout<'a>,
@ -1280,7 +1288,7 @@ pub enum ClosureRepresentation<'a> {
/// The layouts are sorted alphabetically by the identifier that is captured.
///
/// We MUST sort these according to their stack size before code gen!
AlphabeticOrderStruct(&'a [Layout<'a>]),
AlphabeticOrderStruct(&'a [InLayout<'a>]),
/// The closure is one function that captures a single identifier, whose value is unwrapped.
UnwrappedCapture(Layout<'a>),
/// The closure dispatches to multiple functions, but none of them capture anything, so this is
@ -1366,7 +1374,7 @@ impl<'a> LambdaSet<'a> {
{
debug_assert!(self.contains(lambda_name.name));
let comparator = |other_name: Symbol, other_captures_layouts: &[Layout]| {
let comparator = |other_name: Symbol, other_captures_layouts: &[InLayout]| {
other_name == lambda_name.name
// Make sure all captures are equal
&& other_captures_layouts
@ -1394,7 +1402,7 @@ impl<'a> LambdaSet<'a> {
self
);
let comparator = |other_name: Symbol, other_captures_layouts: &[Layout]| {
let comparator = |other_name: Symbol, other_captures_layouts: &[InLayout<'a>]| {
other_name == function_symbol
&& other_captures_layouts.iter().zip(captures_layouts).all(
|(other_layout, layout)| {
@ -1424,10 +1432,11 @@ impl<'a> LambdaSet<'a> {
/// Checks if two captured layouts are equivalent under the current lambda set.
/// Resolves recursive pointers to the layout of the lambda set.
fn capture_layouts_eq<I>(&self, interner: &I, left: &Layout, right: &Layout) -> bool
fn capture_layouts_eq<I>(&self, interner: &I, left: &InLayout<'a>, right: &Layout) -> bool
where
I: Interner<'a, Layout<'a>>,
{
let left = interner.get(*left);
if left == right {
return true;
}
@ -1460,7 +1469,7 @@ impl<'a> LambdaSet<'a> {
fn layout_for_member<I, F>(&self, interner: &I, comparator: F) -> ClosureRepresentation<'a>
where
I: Interner<'a, Layout<'a>>,
F: Fn(Symbol, &[Layout]) -> bool,
F: Fn(Symbol, &[InLayout]) -> bool,
{
let repr = interner.get(self.representation);
@ -1687,7 +1696,7 @@ impl<'a> LambdaSet<'a> {
// sort the tags; make sure ordering stays intact!
lambdas.sort_by_key(|(sym, _)| *sym);
let mut set: Vec<(Symbol, &[Layout])> =
let mut set: Vec<(Symbol, &[InLayout])> =
Vec::with_capacity_in(lambdas.len(), env.arena);
let mut set_with_variables: std::vec::Vec<(&Symbol, &[Variable])> =
std::vec::Vec::with_capacity(lambdas.len());
@ -1708,7 +1717,8 @@ impl<'a> LambdaSet<'a> {
// representation, so here the criteria doesn't matter.
let mut criteria = CACHEABLE;
let arg = cached!(Layout::from_var(env, *var), criteria);
arguments.push(arg);
let arg_in = env.cache.interner.insert(env.arena.alloc(arg));
arguments.push(arg_in);
}
let arguments = arguments.into_bump_slice();

View file

@ -135,7 +135,7 @@ fn compiles_to_ir(test_name: &str, src: &str, mode: &str, no_check: bool) {
module_id: home,
procedures,
exposed_to_host,
layout_interner,
mut layout_interner,
interns,
..
} = loaded;
@ -152,7 +152,7 @@ fn compiles_to_ir(test_name: &str, src: &str, mode: &str, no_check: bool) {
let main_fn_symbol = exposed_to_host.values.keys().copied().next();
if !no_check {
check_procedures(arena, &interns, &layout_interner, &procedures);
check_procedures(arena, &interns, &mut layout_interner, &procedures);
}
verify_procedures(test_name, layout_interner, procedures, main_fn_symbol);
@ -161,7 +161,7 @@ fn compiles_to_ir(test_name: &str, src: &str, mode: &str, no_check: bool) {
fn check_procedures<'a>(
arena: &'a Bump,
interns: &Interns,
interner: &STLayoutInterner<'a>,
interner: &mut STLayoutInterner<'a>,
procedures: &MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
) {
use roc_mono::debug::{check_procs, format_problems};