Make all layouts interned in mono

This commit is contained in:
Ayaz Hafiz 2023-01-03 19:49:07 -06:00
parent dc6b7003a8
commit fa8effd3e8
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
14 changed files with 932 additions and 866 deletions

View file

@ -3092,6 +3092,7 @@ fn update<'a>(
Proc::insert_reset_reuse_operations(
arena,
&mut layout_interner,
module_id,
ident_ids,
&mut update_mode_ids,
@ -3430,7 +3431,7 @@ fn proc_layout_for<'a>(
// is a function value
roc_mono::ir::ProcLayout {
arguments: &[],
result: Layout::struct_no_name_order(&[]),
result: Layout::UNIT,
niche: Niche::NONE,
}
}

View file

@ -4,7 +4,7 @@ use std::hash::Hash;
use crate::ir::{
Expr, HigherOrderLowLevel, JoinPointId, Param, PassedFunction, Proc, ProcLayout, Stmt,
};
use crate::layout::Layout;
use crate::layout::{InLayout, Layout, LayoutInterner, STLayoutInterner};
use bumpalo::collections::Vec;
use bumpalo::Bump;
use roc_collections::all::{MutMap, MutSet};
@ -33,6 +33,7 @@ impl Ownership {
}
pub fn infer_borrow<'a>(
arena: &'a Bump,
interner: &STLayoutInterner<'a>,
procs: &MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
host_exposed_procs: &[Symbol],
) -> ParamMap<'a> {
@ -49,7 +50,7 @@ pub fn infer_borrow<'a>(
};
for (key, proc) in procs {
param_map.visit_proc(arena, proc, *key);
param_map.visit_proc(arena, interner, proc, *key);
}
let mut env = BorrowInfState {
@ -232,10 +233,14 @@ impl<'a> ParamMap<'a> {
}
impl<'a> ParamMap<'a> {
fn init_borrow_params(arena: &'a Bump, ps: &'a [Param<'a>]) -> &'a [Param<'a>] {
fn init_borrow_params(
arena: &'a Bump,
interner: &STLayoutInterner<'a>,
ps: &'a [Param<'a>],
) -> &'a [Param<'a>] {
Vec::from_iter_in(
ps.iter().map(|p| Param {
ownership: Ownership::from_layout(&p.layout),
ownership: Ownership::from_layout(&interner.get(p.layout)),
layout: p.layout,
symbol: p.symbol,
}),
@ -244,10 +249,14 @@ impl<'a> ParamMap<'a> {
.into_bump_slice()
}
fn init_borrow_args(arena: &'a Bump, ps: &'a [(Layout<'a>, Symbol)]) -> &'a [Param<'a>] {
fn init_borrow_args(
arena: &'a Bump,
interner: &STLayoutInterner<'a>,
ps: &'a [(InLayout<'a>, Symbol)],
) -> &'a [Param<'a>] {
Vec::from_iter_in(
ps.iter().map(|(layout, symbol)| Param {
ownership: Ownership::from_layout(layout),
ownership: Ownership::from_layout(&interner.get(*layout)),
layout: *layout,
symbol: *symbol,
}),
@ -258,7 +267,7 @@ impl<'a> ParamMap<'a> {
fn init_borrow_args_always_owned(
arena: &'a Bump,
ps: &'a [(Layout<'a>, Symbol)],
ps: &'a [(InLayout<'a>, Symbol)],
) -> &'a [Param<'a>] {
Vec::from_iter_in(
ps.iter().map(|(layout, symbol)| Param {
@ -271,15 +280,21 @@ impl<'a> ParamMap<'a> {
.into_bump_slice()
}
fn visit_proc(&mut self, arena: &'a Bump, proc: &Proc<'a>, key: (Symbol, ProcLayout<'a>)) {
fn visit_proc(
&mut self,
arena: &'a Bump,
interner: &STLayoutInterner<'a>,
proc: &Proc<'a>,
key: (Symbol, ProcLayout<'a>),
) {
if proc.must_own_arguments {
self.visit_proc_always_owned(arena, proc, key);
self.visit_proc_always_owned(arena, interner, proc, key);
return;
}
let index: usize = self.get_param_offset(key.0, key.1).into();
for (i, param) in Self::init_borrow_args(arena, proc.args)
for (i, param) in Self::init_borrow_args(arena, interner, proc.args)
.iter()
.copied()
.enumerate()
@ -287,12 +302,13 @@ impl<'a> ParamMap<'a> {
self.declarations[index + i] = param;
}
self.visit_stmt(arena, proc.name.name(), &proc.body);
self.visit_stmt(arena, interner, proc.name.name(), &proc.body);
}
fn visit_proc_always_owned(
&mut self,
arena: &'a Bump,
interner: &STLayoutInterner<'a>,
proc: &Proc<'a>,
key: (Symbol, ProcLayout<'a>),
) {
@ -306,10 +322,16 @@ impl<'a> ParamMap<'a> {
self.declarations[index + i] = param;
}
self.visit_stmt(arena, proc.name.name(), &proc.body);
self.visit_stmt(arena, interner, proc.name.name(), &proc.body);
}
fn visit_stmt(&mut self, arena: &'a Bump, _fnid: Symbol, stmt: &Stmt<'a>) {
fn visit_stmt(
&mut self,
arena: &'a Bump,
interner: &STLayoutInterner<'a>,
_fnid: Symbol,
stmt: &Stmt<'a>,
) {
use Stmt::*;
let mut stack = bumpalo::vec![in arena; stmt];
@ -323,7 +345,7 @@ impl<'a> ParamMap<'a> {
body: b,
} => {
self.join_points
.insert(*j, Self::init_borrow_params(arena, xs));
.insert(*j, Self::init_borrow_params(arena, interner, xs));
stack.push(v);
stack.push(b);
@ -527,8 +549,7 @@ impl<'a> BorrowInfState<'a> {
arg_layouts,
..
} => {
let top_level =
ProcLayout::new(self.arena, arg_layouts, name.niche(), **ret_layout);
let top_level = ProcLayout::new(self.arena, arg_layouts, name.niche(), *ret_layout);
// get the borrow signature of the applied function
let ps = param_map
@ -756,7 +777,7 @@ impl<'a> BorrowInfState<'a> {
Stmt::Ret(z),
) = (v, b)
{
let top_level = ProcLayout::new(self.arena, arg_layouts, g.niche(), **ret_layout);
let top_level = ProcLayout::new(self.arena, arg_layouts, g.niche(), *ret_layout);
if self.current_proc == g.name() && x == *z {
// anonymous functions (for which the ps may not be known)

View file

@ -20,9 +20,9 @@ pub fn eq_generic<'a>(
ident_ids: &mut IdentIds,
ctx: &mut Context<'a>,
layout_interner: &mut STLayoutInterner<'a>,
layout: Layout<'a>,
layout: InLayout<'a>,
) -> Stmt<'a> {
let main_body = match layout {
let main_body = match layout_interner.get(layout) {
Layout::Builtin(Builtin::Int(_) | Builtin::Float(_) | Builtin::Bool | Builtin::Decimal) => {
unreachable!(
"No generated proc for `==`. Use direct code gen for {:?}",
@ -142,7 +142,7 @@ fn eq_struct<'a>(
ident_ids: &mut IdentIds,
ctx: &mut Context<'a>,
layout_interner: &mut STLayoutInterner<'a>,
field_layouts: &'a [Layout<'a>],
field_layouts: &'a [InLayout<'a>],
) -> Stmt<'a> {
let mut else_stmt = Stmt::Ret(Symbol::BOOL_TRUE);
for (i, layout) in field_layouts.iter().enumerate().rev() {
@ -277,7 +277,7 @@ fn eq_tag_union_help<'a>(
ctx: &mut Context<'a>,
layout_interner: &mut STLayoutInterner<'a>,
union_layout: UnionLayout<'a>,
tag_layouts: &'a [&'a [Layout<'a>]],
tag_layouts: &'a [&'a [InLayout<'a>]],
nullable_id: Option<TagIdIntType>,
) -> Stmt<'a> {
let tailrec_loop = JoinPointId(root.create_symbol(ident_ids, "tailrec_loop"));
@ -417,10 +417,11 @@ fn eq_tag_union_help<'a>(
if is_non_recursive {
compare_ptr_or_value
} else {
let union_layout = layout_interner.insert(Layout::Union(union_layout));
let loop_params_iter = operands.iter().map(|arg| Param {
symbol: *arg,
ownership: Ownership::Borrowed,
layout: Layout::Union(union_layout),
layout: union_layout,
});
let loop_start = Stmt::Jump(tailrec_loop, root.arena.alloc([ARG_1, ARG_2]));
@ -442,7 +443,7 @@ fn eq_tag_fields<'a>(
layout_interner: &mut STLayoutInterner<'a>,
tailrec_loop: JoinPointId,
union_layout: UnionLayout<'a>,
field_layouts: &'a [Layout<'a>],
field_layouts: &'a [InLayout<'a>],
operands: [Symbol; 2],
tag_id: TagIdIntType,
) -> Stmt<'a> {
@ -450,7 +451,7 @@ fn eq_tag_fields<'a>(
// (If there are more than one, the others will use non-tail recursion)
let rec_ptr_index = field_layouts
.iter()
.position(|field| matches!(field, Layout::RecursivePointer));
.position(|field| matches!(layout_interner.get(*field), Layout::RecursivePointer));
let (tailrec_index, innermost_stmt) = match rec_ptr_index {
None => {
@ -579,8 +580,6 @@ fn eq_boxed<'a>(
layout_interner: &mut STLayoutInterner<'a>,
inner_layout: InLayout<'a>,
) -> Stmt<'a> {
let inner_layout = layout_interner.get(inner_layout);
let a = root.create_symbol(ident_ids, "a");
let b = root.create_symbol(ident_ids, "b");
let result = root.create_symbol(ident_ids, "result");
@ -638,11 +637,9 @@ fn eq_list<'a>(
let layout_isize = root.layout_isize;
let arena = root.arena;
let elem_layout = layout_interner.get(elem_layout);
// A "Box" layout (heap pointer to a single list element)
let box_union_layout = UnionLayout::NonNullableUnwrapped(root.arena.alloc([elem_layout]));
let box_layout = Layout::Union(box_union_layout);
let box_layout = layout_interner.insert(Layout::Union(box_union_layout));
// Compare lengths
@ -687,7 +684,10 @@ fn eq_list<'a>(
// let size = literal int
let size = root.create_symbol(ident_ids, "size");
let size_expr = Expr::Literal(Literal::Int(
(elem_layout.stack_size(layout_interner, root.target_info) as i128).to_ne_bytes(),
(layout_interner
.get(elem_layout)
.stack_size(layout_interner, root.target_info) as i128)
.to_ne_bytes(),
));
let size_stmt = |next| Stmt::Let(size, size_expr, layout_isize, next);

View file

@ -9,14 +9,14 @@ use crate::ir::{
SelfRecursive, Stmt, UpdateModeId,
};
use crate::layout::{
Builtin, LambdaName, Layout, LayoutInterner, Niche, STLayoutInterner, UnionLayout,
Builtin, InLayout, LambdaName, Layout, LayoutInterner, Niche, STLayoutInterner, UnionLayout,
};
mod equality;
mod refcount;
const LAYOUT_BOOL: Layout = Layout::Builtin(Builtin::Bool);
const LAYOUT_UNIT: Layout = Layout::UNIT;
const LAYOUT_BOOL: InLayout = Layout::BOOL;
const LAYOUT_UNIT: InLayout = Layout::UNIT;
const ARG_1: Symbol = Symbol::ARG_1;
const ARG_2: Symbol = Symbol::ARG_2;
@ -43,7 +43,7 @@ impl HelperOp {
#[derive(Debug)]
struct Specialization<'a> {
op: HelperOp,
layout: Layout<'a>,
layout: InLayout<'a>,
symbol: Symbol,
proc: Option<Proc<'a>>,
}
@ -77,7 +77,7 @@ pub struct CodeGenHelp<'a> {
arena: &'a Bump,
home: ModuleId,
target_info: TargetInfo,
layout_isize: Layout<'a>,
layout_isize: InLayout<'a>,
union_refcount: UnionLayout<'a>,
specializations: Vec<'a, Specialization<'a>>,
debug_recursion_depth: usize,
@ -121,11 +121,11 @@ impl<'a> CodeGenHelp<'a> {
&mut self,
ident_ids: &mut IdentIds,
layout_interner: &mut STLayoutInterner<'a>,
layout: Layout<'a>,
layout: InLayout<'a>,
modify: &ModifyRc,
following: &'a Stmt<'a>,
) -> (&'a Stmt<'a>, Vec<'a, (Symbol, ProcLayout<'a>)>) {
if !refcount::is_rc_implemented_yet(layout_interner, &layout) {
if !refcount::is_rc_implemented_yet(layout_interner, layout) {
// Just a warning, so we can decouple backend development from refcounting development.
// When we are closer to completion, we can change it to a panic.
println!(
@ -166,7 +166,7 @@ impl<'a> CodeGenHelp<'a> {
&mut self,
ident_ids: &mut IdentIds,
layout_interner: &mut STLayoutInterner<'a>,
layout: Layout<'a>,
layout: InLayout<'a>,
argument: Symbol,
) -> (Expr<'a>, Vec<'a, (Symbol, ProcLayout<'a>)>) {
let mut ctx = Context {
@ -178,7 +178,7 @@ impl<'a> CodeGenHelp<'a> {
let proc_name = self.find_or_create_proc(ident_ids, &mut ctx, layout_interner, layout);
let arguments = self.arena.alloc([argument]);
let ret_layout = self.arena.alloc(layout);
let ret_layout = layout;
let arg_layouts = self.arena.alloc([layout]);
let expr = Expr::Call(Call {
call_type: CallType::ByName {
@ -200,7 +200,7 @@ impl<'a> CodeGenHelp<'a> {
&mut self,
ident_ids: &mut IdentIds,
layout_interner: &mut STLayoutInterner<'a>,
layout: Layout<'a>,
layout: InLayout<'a>,
op: HelperOp,
) -> (Symbol, Vec<'a, (Symbol, ProcLayout<'a>)>) {
let mut ctx = Context {
@ -220,7 +220,7 @@ impl<'a> CodeGenHelp<'a> {
&mut self,
ident_ids: &mut IdentIds,
layout_interner: &mut STLayoutInterner<'a>,
layout: &Layout<'a>,
layout: InLayout<'a>,
arguments: &'a [Symbol],
) -> (Expr<'a>, Vec<'a, (Symbol, ProcLayout<'a>)>) {
let mut ctx = Context {
@ -230,7 +230,7 @@ impl<'a> CodeGenHelp<'a> {
};
let expr = self
.call_specialized_op(ident_ids, &mut ctx, layout_interner, *layout, arguments)
.call_specialized_op(ident_ids, &mut ctx, layout_interner, layout, arguments)
.unwrap();
(expr, ctx.new_linker_data)
@ -247,7 +247,7 @@ impl<'a> CodeGenHelp<'a> {
ident_ids: &mut IdentIds,
ctx: &mut Context<'a>,
layout_interner: &mut STLayoutInterner<'a>,
called_layout: Layout<'a>,
called_layout: InLayout<'a>,
arguments: &'a [Symbol],
) -> Option<Expr<'a>> {
use HelperOp::*;
@ -255,23 +255,23 @@ impl<'a> CodeGenHelp<'a> {
// debug_assert!(self.debug_recursion_depth < 100);
self.debug_recursion_depth += 1;
let layout = if matches!(called_layout, Layout::RecursivePointer) {
let layout = if matches!(layout_interner.get(called_layout), Layout::RecursivePointer) {
let union_layout = ctx.recursive_union.unwrap();
Layout::Union(union_layout)
layout_interner.insert(Layout::Union(union_layout))
} else {
called_layout
};
if layout_needs_helper_proc(&layout, ctx.op) {
if layout_needs_helper_proc(layout_interner, layout, ctx.op) {
let proc_name = self.find_or_create_proc(ident_ids, ctx, layout_interner, layout);
let (ret_layout, arg_layouts): (&'a Layout<'a>, &'a [Layout<'a>]) = {
let (ret_layout, arg_layouts): (InLayout<'a>, &'a [InLayout<'a>]) = {
let arg = self.replace_rec_ptr(ctx, layout_interner, layout);
match ctx.op {
Dec | DecRef(_) => (&LAYOUT_UNIT, self.arena.alloc([arg])),
Reset => (self.arena.alloc(layout), self.arena.alloc([layout])),
Inc => (&LAYOUT_UNIT, self.arena.alloc([arg, self.layout_isize])),
Eq => (&LAYOUT_BOOL, self.arena.alloc([arg, arg])),
Dec | DecRef(_) => (LAYOUT_UNIT, self.arena.alloc([arg])),
Reset => (layout, self.arena.alloc([layout])),
Inc => (LAYOUT_UNIT, self.arena.alloc([arg, self.layout_isize])),
Eq => (LAYOUT_BOOL, self.arena.alloc([arg, arg])),
}
};
@ -302,7 +302,7 @@ impl<'a> CodeGenHelp<'a> {
ident_ids: &mut IdentIds,
ctx: &mut Context<'a>,
layout_interner: &mut STLayoutInterner<'a>,
orig_layout: Layout<'a>,
orig_layout: InLayout<'a>,
) -> Symbol {
use HelperOp::*;
@ -320,7 +320,7 @@ impl<'a> CodeGenHelp<'a> {
// Procs can be recursive, so we need to create the symbol before the body is complete
// But with nested recursion, that means Symbols and Procs can end up in different orders.
// We want the same order, especially for function indices in Wasm. So create an empty slot and fill it in later.
let (proc_symbol, proc_layout) = self.create_proc_symbol(ident_ids, ctx, &layout);
let (proc_symbol, proc_layout) = self.create_proc_symbol(ident_ids, ctx, layout);
ctx.new_linker_data.push((proc_symbol, proc_layout));
let spec_index = self.specializations.len();
self.specializations.push(Specialization {
@ -360,7 +360,7 @@ impl<'a> CodeGenHelp<'a> {
),
};
let args: &'a [(Layout<'a>, Symbol)] = {
let args: &'a [(InLayout<'a>, Symbol)] = {
let roc_value = (layout, ARG_1);
match ctx.op {
Inc => {
@ -390,7 +390,7 @@ impl<'a> CodeGenHelp<'a> {
&self,
ident_ids: &mut IdentIds,
ctx: &mut Context<'a>,
layout: &Layout<'a>,
layout: InLayout<'a>,
) -> (Symbol, ProcLayout<'a>) {
let debug_name = format!(
"#help{}_{:?}_{:?}",
@ -403,23 +403,23 @@ impl<'a> CodeGenHelp<'a> {
let proc_layout = match ctx.op {
HelperOp::Inc => ProcLayout {
arguments: self.arena.alloc([*layout, self.layout_isize]),
arguments: self.arena.alloc([layout, self.layout_isize]),
result: LAYOUT_UNIT,
niche: Niche::NONE,
},
HelperOp::Dec => ProcLayout {
arguments: self.arena.alloc([*layout]),
arguments: self.arena.alloc([layout]),
result: LAYOUT_UNIT,
niche: Niche::NONE,
},
HelperOp::Reset => ProcLayout {
arguments: self.arena.alloc([*layout]),
result: *layout,
arguments: self.arena.alloc([layout]),
result: layout,
niche: Niche::NONE,
},
HelperOp::DecRef(_) => unreachable!("No generated Proc for DecRef"),
HelperOp::Eq => ProcLayout {
arguments: self.arena.alloc([*layout, *layout]),
arguments: self.arena.alloc([layout, layout]),
result: LAYOUT_BOOL,
niche: Niche::NONE,
},
@ -442,16 +442,15 @@ impl<'a> CodeGenHelp<'a> {
&mut self,
ctx: &Context<'a>,
layout_interner: &mut STLayoutInterner<'a>,
layout: Layout<'a>,
) -> Layout<'a> {
match layout {
layout: InLayout<'a>,
) -> InLayout<'a> {
let layout = match layout_interner.get(layout) {
Layout::Builtin(Builtin::List(v)) => {
let v = self.replace_rec_ptr(ctx, layout_interner, layout_interner.get(v));
let v = layout_interner.insert(v);
let v = self.replace_rec_ptr(ctx, layout_interner, v);
Layout::Builtin(Builtin::List(v))
}
Layout::Builtin(_) => layout,
Layout::Builtin(_) => return layout,
Layout::Struct {
field_layouts,
@ -482,54 +481,53 @@ impl<'a> CodeGenHelp<'a> {
Layout::Union(_) => {
// we always fully unroll recursive types. That means tha when we find a
// recursive tag union we can replace it with the layout
layout
return layout;
}
Layout::Boxed(inner) => {
let inner = layout_interner.get(inner);
let inner = self.replace_rec_ptr(ctx, layout_interner, inner);
let inner = layout_interner.insert(inner);
Layout::Boxed(inner)
}
Layout::LambdaSet(lambda_set) => self.replace_rec_ptr(
ctx,
layout_interner,
lambda_set.runtime_representation(layout_interner),
),
Layout::LambdaSet(lambda_set) => {
return self.replace_rec_ptr(ctx, layout_interner, lambda_set.representation)
}
// This line is the whole point of the function
Layout::RecursivePointer => Layout::Union(ctx.recursive_union.unwrap()),
}
};
layout_interner.insert(layout)
}
fn union_tail_recursion_fields(
&self,
layout_interner: &STLayoutInterner<'a>,
union: UnionLayout<'a>,
) -> (bool, Vec<'a, Option<usize>>) {
use UnionLayout::*;
match union {
NonRecursive(_) => (false, bumpalo::vec![in self.arena]),
Recursive(tags) => self.union_tail_recursion_fields_help(tags),
Recursive(tags) => self.union_tail_recursion_fields_help(layout_interner, tags),
NonNullableUnwrapped(field_layouts) => {
self.union_tail_recursion_fields_help(&[field_layouts])
self.union_tail_recursion_fields_help(layout_interner, &[field_layouts])
}
NullableWrapped {
other_tags: tags, ..
} => self.union_tail_recursion_fields_help(tags),
} => self.union_tail_recursion_fields_help(layout_interner, tags),
NullableUnwrapped { other_fields, .. } => {
self.union_tail_recursion_fields_help(&[other_fields])
self.union_tail_recursion_fields_help(layout_interner, &[other_fields])
}
}
}
fn union_tail_recursion_fields_help(
&self,
tags: &[&'a [Layout<'a>]],
layout_interner: &STLayoutInterner<'a>,
tags: &[&'a [InLayout<'a>]],
) -> (bool, Vec<'a, Option<usize>>) {
let mut can_use_tailrec = false;
let mut tailrec_indices = Vec::with_capacity_in(tags.len(), self.arena);
@ -537,7 +535,7 @@ impl<'a> CodeGenHelp<'a> {
for fields in tags.iter() {
let found_index = fields
.iter()
.position(|f| matches!(f, Layout::RecursivePointer));
.position(|f| matches!(layout_interner.get(*f), Layout::RecursivePointer));
tailrec_indices.push(found_index);
can_use_tailrec |= found_index.is_some();
}
@ -548,7 +546,7 @@ impl<'a> CodeGenHelp<'a> {
fn let_lowlevel<'a>(
arena: &'a Bump,
result_layout: Layout<'a>,
result_layout: InLayout<'a>,
result: Symbol,
op: LowLevel,
arguments: &[Symbol],
@ -568,8 +566,12 @@ fn let_lowlevel<'a>(
)
}
fn layout_needs_helper_proc(layout: &Layout, op: HelperOp) -> bool {
match layout {
fn layout_needs_helper_proc<'a>(
layout_interner: &STLayoutInterner<'a>,
layout: InLayout<'a>,
op: HelperOp,
) -> bool {
match layout_interner.get(layout) {
Layout::Builtin(Builtin::Int(_) | Builtin::Float(_) | Builtin::Bool | Builtin::Decimal) => {
false
}

View file

@ -1,7 +1,6 @@
#![allow(clippy::too_many_arguments)]
use bumpalo::collections::vec::Vec;
use roc_builtins::bitcode::IntWidth;
use roc_module::low_level::{LowLevel, LowLevel::*};
use roc_module::symbol::{IdentIds, Symbol};
use roc_target::PtrWidth;
@ -17,19 +16,20 @@ use crate::layout::{
use super::{CodeGenHelp, Context, HelperOp};
const LAYOUT_BOOL: Layout = Layout::Builtin(Builtin::Bool);
const LAYOUT_UNIT: Layout = Layout::UNIT;
const LAYOUT_U32: Layout = Layout::Builtin(Builtin::Int(IntWidth::U32));
const LAYOUT_BOOL: InLayout = Layout::BOOL;
const LAYOUT_UNIT: InLayout = Layout::UNIT;
const LAYOUT_U32: InLayout = Layout::U32;
// TODO: Replace usages with root.union_refcount
const LAYOUT_PTR: Layout = Layout::RecursivePointer;
// TODO(recursive-layouts): update once we have disjoint recursive pointers
const LAYOUT_PTR: InLayout = Layout::RECURSIVE_PTR;
pub fn refcount_stmt<'a>(
root: &mut CodeGenHelp<'a>,
ident_ids: &mut IdentIds,
ctx: &mut Context<'a>,
layout_interner: &mut STLayoutInterner<'a>,
layout: Layout<'a>,
layout: InLayout<'a>,
modify: &ModifyRc,
following: &'a Stmt<'a>,
) -> &'a Stmt<'a> {
@ -77,7 +77,7 @@ pub fn refcount_stmt<'a>(
}
ModifyRc::DecRef(structure) => {
match layout {
match layout_interner.get(layout) {
// Str has no children, so we might as well do what we normally do and call the helper.
Layout::Builtin(Builtin::Str) => {
ctx.op = HelperOp::Dec;
@ -128,12 +128,12 @@ pub fn refcount_generic<'a>(
ident_ids: &mut IdentIds,
ctx: &mut Context<'a>,
layout_interner: &mut STLayoutInterner<'a>,
layout: Layout<'a>,
layout: InLayout<'a>,
structure: Symbol,
) -> Stmt<'a> {
debug_assert!(is_rc_implemented_yet(layout_interner, &layout));
debug_assert!(is_rc_implemented_yet(layout_interner, layout));
match layout {
match layout_interner.get(layout) {
Layout::Builtin(Builtin::Int(_) | Builtin::Float(_) | Builtin::Bool | Builtin::Decimal) => {
// Generate a dummy function that immediately returns Unit
// Some higher-order Zig builtins *always* call an RC function on List elements.
@ -145,7 +145,7 @@ pub fn refcount_generic<'a>(
ident_ids,
ctx,
layout_interner,
&layout,
layout,
elem_layout,
structure,
),
@ -166,7 +166,7 @@ pub fn refcount_generic<'a>(
structure,
),
Layout::LambdaSet(lambda_set) => {
let runtime_layout = lambda_set.runtime_representation(layout_interner);
let runtime_layout = lambda_set.representation;
refcount_generic(
root,
ident_ids,
@ -179,18 +179,15 @@ pub fn refcount_generic<'a>(
Layout::RecursivePointer => unreachable!(
"We should never call a refcounting helper on a RecursivePointer layout directly"
),
Layout::Boxed(inner_layout) => {
let inner_layout = layout_interner.get(inner_layout);
refcount_boxed(
root,
ident_ids,
ctx,
layout_interner,
&layout,
&inner_layout,
structure,
)
}
Layout::Boxed(inner_layout) => refcount_boxed(
root,
ident_ids,
ctx,
layout_interner,
layout,
inner_layout,
structure,
),
}
}
@ -199,7 +196,7 @@ pub fn refcount_reset_proc_body<'a>(
ident_ids: &mut IdentIds,
ctx: &mut Context<'a>,
layout_interner: &mut STLayoutInterner<'a>,
layout: Layout<'a>,
layout: InLayout<'a>,
structure: Symbol,
) -> Stmt<'a> {
let rc_ptr = root.create_symbol(ident_ids, "rc_ptr");
@ -208,7 +205,7 @@ pub fn refcount_reset_proc_body<'a>(
let is_unique = root.create_symbol(ident_ids, "is_unique");
let addr = root.create_symbol(ident_ids, "addr");
let union_layout = match layout {
let union_layout = match layout_interner.get(layout) {
Layout::Union(u) => u,
_ => unimplemented!("Reset is only implemented for UnionLayout"),
};
@ -267,7 +264,10 @@ pub fn refcount_reset_proc_body<'a>(
let alloc_addr_stmt = {
let alignment = root.create_symbol(ident_ids, "alignment");
let alignment_expr = Expr::Literal(Literal::Int(
(layout.alignment_bytes(layout_interner, root.target_info) as i128).to_ne_bytes(),
(layout_interner
.get(layout)
.alignment_bytes(layout_interner, root.target_info) as i128)
.to_ne_bytes(),
));
let alloc_addr = root.create_symbol(ident_ids, "alloc_addr");
let alloc_addr_expr = Expr::Call(Call {
@ -420,41 +420,36 @@ pub fn refcount_reset_proc_body<'a>(
// Check if refcounting is implemented yet. In the long term, this will be deleted.
// In the short term, it helps us to skip refcounting and let it leak, so we can make
// progress incrementally. Kept in sync with generate_procs using assertions.
pub fn is_rc_implemented_yet<'a, I>(interner: &I, layout: &Layout<'a>) -> bool
pub fn is_rc_implemented_yet<'a, I>(interner: &I, layout: InLayout<'a>) -> bool
where
I: LayoutInterner<'a>,
{
use UnionLayout::*;
match layout {
Layout::Builtin(Builtin::List(elem_layout)) => {
let elem_layout = interner.get(*elem_layout);
is_rc_implemented_yet(interner, &elem_layout)
}
match interner.get(layout) {
Layout::Builtin(Builtin::List(elem_layout)) => is_rc_implemented_yet(interner, elem_layout),
Layout::Builtin(_) => true,
Layout::Struct { field_layouts, .. } => field_layouts
.iter()
.all(|l| is_rc_implemented_yet(interner, l)),
.all(|l| is_rc_implemented_yet(interner, *l)),
Layout::Union(union_layout) => match union_layout {
NonRecursive(tags) => tags
.iter()
.all(|fields| fields.iter().all(|l| is_rc_implemented_yet(interner, l))),
.all(|fields| fields.iter().all(|l| is_rc_implemented_yet(interner, *l))),
Recursive(tags) => tags
.iter()
.all(|fields| fields.iter().all(|l| is_rc_implemented_yet(interner, l))),
.all(|fields| fields.iter().all(|l| is_rc_implemented_yet(interner, *l))),
NonNullableUnwrapped(fields) => {
fields.iter().all(|l| is_rc_implemented_yet(interner, l))
fields.iter().all(|l| is_rc_implemented_yet(interner, *l))
}
NullableWrapped { other_tags, .. } => other_tags
.iter()
.all(|fields| fields.iter().all(|l| is_rc_implemented_yet(interner, l))),
.all(|fields| fields.iter().all(|l| is_rc_implemented_yet(interner, *l))),
NullableUnwrapped { other_fields, .. } => other_fields
.iter()
.all(|l| is_rc_implemented_yet(interner, l)),
.all(|l| is_rc_implemented_yet(interner, *l)),
},
Layout::LambdaSet(lambda_set) => {
is_rc_implemented_yet(interner, &lambda_set.runtime_representation(interner))
}
Layout::LambdaSet(lambda_set) => is_rc_implemented_yet(interner, lambda_set.representation),
Layout::RecursivePointer => true,
Layout::Boxed(_) => true,
}
@ -765,18 +760,16 @@ fn refcount_list<'a>(
ident_ids: &mut IdentIds,
ctx: &mut Context<'a>,
layout_interner: &mut STLayoutInterner<'a>,
layout: &Layout,
layout: InLayout,
elem_layout: InLayout<'a>,
structure: Symbol,
) -> Stmt<'a> {
let layout_isize = root.layout_isize;
let arena = root.arena;
let elem_layout = layout_interner.get(elem_layout);
// A "Box" layout (heap pointer to a single list element)
let box_union_layout = UnionLayout::NonNullableUnwrapped(arena.alloc([elem_layout]));
let box_layout = Layout::Union(box_union_layout);
let box_layout = layout_interner.insert(Layout::Union(box_union_layout));
//
// Check if the list is empty
@ -816,7 +809,7 @@ fn refcount_list<'a>(
//
let rc_ptr = root.create_symbol(ident_ids, "rc_ptr");
let alignment = layout.alignment_bytes(layout_interner, root.target_info);
let alignment = layout_interner.alignment_bytes(layout);
let ret_stmt = rc_return_stmt(root, ident_ids, ctx);
let modify_list = modify_refcount(
@ -837,22 +830,23 @@ fn refcount_list<'a>(
arena.alloc(modify_list),
);
let modify_elems_and_list = if elem_layout.is_refcounted() && !ctx.op.is_decref() {
refcount_list_elems(
root,
ident_ids,
ctx,
layout_interner,
&elem_layout,
LAYOUT_UNIT,
box_union_layout,
len,
elements,
get_rc_and_modify_list,
)
} else {
get_rc_and_modify_list
};
let modify_elems_and_list =
if layout_interner.get(elem_layout).is_refcounted() && !ctx.op.is_decref() {
refcount_list_elems(
root,
ident_ids,
ctx,
layout_interner,
elem_layout,
LAYOUT_UNIT,
box_union_layout,
len,
elements,
get_rc_and_modify_list,
)
} else {
get_rc_and_modify_list
};
//
// Do nothing if the list is empty
@ -893,8 +887,8 @@ fn refcount_list_elems<'a>(
ident_ids: &mut IdentIds,
ctx: &mut Context<'a>,
layout_interner: &mut STLayoutInterner<'a>,
elem_layout: &Layout<'a>,
ret_layout: Layout<'a>,
elem_layout: InLayout<'a>,
ret_layout: InLayout<'a>,
box_union_layout: UnionLayout<'a>,
length: Symbol,
elements: Symbol,
@ -915,7 +909,7 @@ fn refcount_list_elems<'a>(
// let size = literal int
let elem_size = root.create_symbol(ident_ids, "elem_size");
let elem_size_expr = Expr::Literal(Literal::Int(
(elem_layout.stack_size(layout_interner, root.target_info) as i128).to_ne_bytes(),
(layout_interner.stack_size(elem_layout) as i128).to_ne_bytes(),
));
let elem_size_stmt = |next| Stmt::Let(elem_size, elem_size_expr, layout_isize, next);
@ -955,7 +949,7 @@ fn refcount_list_elems<'a>(
// Cast integer to box pointer
let box_ptr = root.create_symbol(ident_ids, "box");
let box_layout = Layout::Union(box_union_layout);
let box_layout = layout_interner.insert(Layout::Union(box_union_layout));
let box_stmt = |next| let_lowlevel(arena, box_layout, box_ptr, PtrCast, &[addr], next);
// Dereference the box pointer to get the current element
@ -966,7 +960,7 @@ fn refcount_list_elems<'a>(
tag_id: 0,
index: 0,
};
let elem_stmt = |next| Stmt::Let(elem, elem_expr, *elem_layout, next);
let elem_stmt = |next| Stmt::Let(elem, elem_expr, elem_layout, next);
//
// Modify element refcount
@ -975,7 +969,7 @@ fn refcount_list_elems<'a>(
let mod_elem_unit = root.create_symbol(ident_ids, "mod_elem_unit");
let mod_elem_args = refcount_args(root, ctx, elem);
let mod_elem_expr = root
.call_specialized_op(ident_ids, ctx, layout_interner, *elem_layout, mod_elem_args)
.call_specialized_op(ident_ids, ctx, layout_interner, elem_layout, mod_elem_args)
.unwrap();
let mod_elem_stmt = |next| Stmt::Let(mod_elem_unit, mod_elem_expr, LAYOUT_UNIT, next);
@ -1059,13 +1053,13 @@ fn refcount_struct<'a>(
ident_ids: &mut IdentIds,
ctx: &mut Context<'a>,
layout_interner: &mut STLayoutInterner<'a>,
field_layouts: &'a [Layout<'a>],
field_layouts: &'a [InLayout<'a>],
structure: Symbol,
) -> Stmt<'a> {
let mut stmt = rc_return_stmt(root, ident_ids, ctx);
for (i, field_layout) in field_layouts.iter().enumerate().rev() {
if field_layout.contains_refcounted(layout_interner) {
if layout_interner.contains_refcounted(*field_layout) {
let field_val = root.create_symbol(ident_ids, &format!("field_val_{}", i));
let field_val_expr = Expr::StructAtIndex {
index: i as u64,
@ -1121,7 +1115,7 @@ fn refcount_union<'a>(
),
Recursive(tags) => {
let (is_tailrec, tail_idx) = root.union_tail_recursion_fields(union);
let (is_tailrec, tail_idx) = root.union_tail_recursion_fields(layout_interner, union);
if is_tailrec && !ctx.op.is_decref() {
refcount_union_tailrec(
root,
@ -1171,7 +1165,7 @@ fn refcount_union<'a>(
nullable_id,
} => {
let null_id = Some(nullable_id);
let (is_tailrec, tail_idx) = root.union_tail_recursion_fields(union);
let (is_tailrec, tail_idx) = root.union_tail_recursion_fields(layout_interner, union);
if is_tailrec && !ctx.op.is_decref() {
refcount_union_tailrec(
root,
@ -1204,7 +1198,7 @@ fn refcount_union<'a>(
} => {
let null_id = Some(nullable_id as TagIdIntType);
let tags = root.arena.alloc([other_fields]);
let (is_tailrec, tail_idx) = root.union_tail_recursion_fields(union);
let (is_tailrec, tail_idx) = root.union_tail_recursion_fields(layout_interner, union);
if is_tailrec && !ctx.op.is_decref() {
refcount_union_tailrec(
root,
@ -1243,7 +1237,7 @@ fn refcount_union_nonrec<'a>(
ctx: &mut Context<'a>,
layout_interner: &mut STLayoutInterner<'a>,
union_layout: UnionLayout<'a>,
tag_layouts: &'a [&'a [Layout<'a>]],
tag_layouts: &'a [&'a [InLayout<'a>]],
structure: Symbol,
) -> Stmt<'a> {
let tag_id_layout = union_layout.tag_id_layout();
@ -1289,11 +1283,11 @@ fn refcount_union_contents<'a>(
ctx: &mut Context<'a>,
layout_interner: &mut STLayoutInterner<'a>,
union_layout: UnionLayout<'a>,
tag_layouts: &'a [&'a [Layout<'a>]],
tag_layouts: &'a [&'a [InLayout<'a>]],
null_id: Option<TagIdIntType>,
structure: Symbol,
tag_id_sym: Symbol,
tag_id_layout: Layout<'a>,
tag_id_layout: InLayout<'a>,
next_stmt: Stmt<'a>,
) -> Stmt<'a> {
let jp_contents_modified = JoinPointId(root.create_symbol(ident_ids, "jp_contents_modified"));
@ -1358,7 +1352,7 @@ fn refcount_union_rec<'a>(
ctx: &mut Context<'a>,
layout_interner: &mut STLayoutInterner<'a>,
union_layout: UnionLayout<'a>,
tag_layouts: &'a [&'a [Layout<'a>]],
tag_layouts: &'a [&'a [InLayout<'a>]],
null_id: Option<TagIdIntType>,
structure: Symbol,
) -> Stmt<'a> {
@ -1437,7 +1431,7 @@ fn refcount_union_tailrec<'a>(
ctx: &mut Context<'a>,
layout_interner: &mut STLayoutInterner<'a>,
union_layout: UnionLayout<'a>,
tag_layouts: &'a [&'a [Layout<'a>]],
tag_layouts: &'a [&'a [InLayout<'a>]],
null_id: Option<TagIdIntType>,
tailrec_indices: Vec<'a, Option<usize>>,
initial_structure: Symbol,
@ -1445,7 +1439,7 @@ fn refcount_union_tailrec<'a>(
let tailrec_loop = JoinPointId(root.create_symbol(ident_ids, "tailrec_loop"));
let current = root.create_symbol(ident_ids, "current");
let next_ptr = root.create_symbol(ident_ids, "next_ptr");
let layout = Layout::Union(union_layout);
let layout = layout_interner.insert(Layout::Union(union_layout));
let tag_id_layout = union_layout.tag_id_layout();
@ -1490,7 +1484,7 @@ fn refcount_union_tailrec<'a>(
)
};
let alignment = layout.alignment_bytes(layout_interner, root.target_info);
let alignment = layout_interner.alignment_bytes(layout);
let modify_structure_stmt = modify_refcount(
root,
ident_ids,
@ -1621,10 +1615,11 @@ fn refcount_union_tailrec<'a>(
));
let loop_init = Stmt::Jump(tailrec_loop, root.arena.alloc([initial_structure]));
let union_layout = layout_interner.insert(Layout::Union(union_layout));
let loop_param = Param {
symbol: current,
ownership: Ownership::Borrowed,
layout: Layout::Union(union_layout),
layout: union_layout,
};
Stmt::Join {
@ -1641,7 +1636,7 @@ fn refcount_tag_fields<'a>(
ctx: &mut Context<'a>,
layout_interner: &mut STLayoutInterner<'a>,
union_layout: UnionLayout<'a>,
field_layouts: &'a [Layout<'a>],
field_layouts: &'a [InLayout<'a>],
structure: Symbol,
tag_id: TagIdIntType,
following: Stmt<'a>,
@ -1649,7 +1644,7 @@ fn refcount_tag_fields<'a>(
let mut stmt = following;
for (i, field_layout) in field_layouts.iter().enumerate().rev() {
if field_layout.contains_refcounted(layout_interner) {
if layout_interner.contains_refcounted(*field_layout) {
let field_val = root.create_symbol(ident_ids, &format!("field_{}_{}", tag_id, i));
let field_val_expr = Expr::UnionAtIndex {
union_layout,
@ -1684,8 +1679,8 @@ fn refcount_boxed<'a>(
ident_ids: &mut IdentIds,
ctx: &mut Context<'a>,
layout_interner: &mut STLayoutInterner<'a>,
layout: &Layout<'a>,
inner_layout: &Layout<'a>,
layout: InLayout<'a>,
inner_layout: InLayout<'a>,
outer: Symbol,
) -> Stmt<'a> {
let arena = root.arena;
@ -1697,7 +1692,7 @@ fn refcount_boxed<'a>(
//
let rc_ptr = root.create_symbol(ident_ids, "rc_ptr");
let alignment = layout.alignment_bytes(layout_interner, root.target_info);
let alignment = layout_interner.alignment_bytes(layout);
let ret_stmt = rc_return_stmt(root, ident_ids, ctx);
let modify_outer = modify_refcount(
root,
@ -1717,7 +1712,7 @@ fn refcount_boxed<'a>(
arena.alloc(modify_outer),
);
if inner_layout.is_refcounted() && !ctx.op.is_decref() {
if layout_interner.is_refcounted(inner_layout) && !ctx.op.is_decref() {
let inner = root.create_symbol(ident_ids, "inner");
let inner_expr = Expr::ExprUnbox { symbol: outer };
@ -1728,7 +1723,7 @@ fn refcount_boxed<'a>(
ident_ids,
ctx,
layout_interner,
*inner_layout,
inner_layout,
mod_inner_args,
)
.unwrap();
@ -1736,7 +1731,7 @@ fn refcount_boxed<'a>(
Stmt::Let(
inner,
inner_expr,
*inner_layout,
inner_layout,
arena.alloc(Stmt::Let(
mod_inner_unit,
mod_inner_expr,

View file

@ -10,7 +10,8 @@ use crate::{
ModifyRc, Param, Proc, ProcLayout, Stmt,
},
layout::{
Builtin, LambdaSet, Layout, LayoutInterner, STLayoutInterner, TagIdIntType, UnionLayout,
Builtin, InLayout, LambdaSet, Layout, LayoutInterner, STLayoutInterner, TagIdIntType,
UnionLayout,
},
};
@ -38,18 +39,18 @@ pub enum ProblemKind<'a> {
},
SymbolUseMismatch {
symbol: Symbol,
def_layout: Layout<'a>,
def_layout: InLayout<'a>,
def_line: usize,
use_layout: Layout<'a>,
use_layout: InLayout<'a>,
use_kind: UseKind,
},
SymbolDefMismatch {
symbol: Symbol,
def_layout: Layout<'a>,
expr_layout: Layout<'a>,
def_layout: InLayout<'a>,
expr_layout: InLayout<'a>,
},
BadSwitchConditionLayout {
found_layout: Layout<'a>,
found_layout: InLayout<'a>,
},
DuplicateSwitchBranch {},
RedefinedJoinPoint {
@ -158,7 +159,7 @@ pub fn check_procs<'a>(
Problems(problems)
}
type VEnv<'a> = VecMap<Symbol, (usize, Layout<'a>)>;
type VEnv<'a> = VecMap<Symbol, (usize, InLayout<'a>)>;
type JoinPoints<'a> = VecMap<JoinPointId, (usize, &'a [Param<'a>])>;
type CallSpecIds = VecMap<CallSpecId, usize>;
struct Ctx<'a, 'r> {
@ -169,7 +170,7 @@ struct Ctx<'a, 'r> {
proc_layout: ProcLayout<'a>,
procs: &'r Procs<'a>,
call_spec_ids: CallSpecIds,
ret_layout: Layout<'a>,
ret_layout: InLayout<'a>,
venv: VEnv<'a>,
joinpoints: JoinPoints<'a>,
line: usize,
@ -192,19 +193,19 @@ impl<'a, 'r> Ctx<'a, 'r> {
r
}
fn resolve(&mut self, mut layout: Layout<'a>) -> Layout<'a> {
fn resolve(&mut self, mut layout: InLayout<'a>) -> InLayout<'a> {
// Note that we are more aggressive than the usual `runtime_representation`
// here because we need strict equality, and so cannot unwrap lambda sets
// lazily.
loop {
match layout {
Layout::LambdaSet(ls) => layout = ls.runtime_representation(self.interner),
layout => return layout,
match self.interner.get(layout) {
Layout::LambdaSet(ls) => layout = ls.representation,
_ => return layout,
}
}
}
fn insert(&mut self, symbol: Symbol, layout: Layout<'a>) {
fn insert(&mut self, symbol: Symbol, layout: InLayout<'a>) {
if let Some((old_line, _)) = self.venv.insert(symbol, (self.line, layout)) {
self.problem(ProblemKind::RedefinedSymbol { symbol, old_line })
}
@ -219,7 +220,7 @@ impl<'a, 'r> Ctx<'a, 'r> {
fn with_sym_layout<T>(
&mut self,
symbol: Symbol,
f: impl FnOnce(&mut Self, usize, Layout<'a>) -> Option<T>,
f: impl FnOnce(&mut Self, usize, InLayout<'a>) -> Option<T>,
) -> Option<T> {
if let Some(&(def_line, layout)) = self.venv.get(&symbol) {
f(self, def_line, layout)
@ -229,7 +230,12 @@ impl<'a, 'r> Ctx<'a, 'r> {
}
}
fn check_sym_layout(&mut self, symbol: Symbol, expected_layout: Layout<'a>, use_kind: UseKind) {
fn check_sym_layout(
&mut self,
symbol: Symbol,
expected_layout: InLayout<'a>,
use_kind: UseKind,
) {
if let Some(&(def_line, layout)) = self.venv.get(&symbol) {
if self.resolve(layout) != self.resolve(expected_layout) {
self.problem(ProblemKind::SymbolUseMismatch {
@ -278,7 +284,8 @@ impl<'a, 'r> Ctx<'a, 'r> {
ret_layout: _,
} => {
self.check_sym_layout(*cond_symbol, *cond_layout, UseKind::SwitchCond);
match self.resolve(*cond_layout) {
let layout = self.resolve(*cond_layout);
match self.interner.get(layout) {
Layout::Builtin(Builtin::Int(_)) => {}
Layout::Builtin(Builtin::Bool) => {}
_ => self.problem(ProblemKind::BadSwitchConditionLayout {
@ -320,11 +327,7 @@ impl<'a, 'r> Ctx<'a, 'r> {
variables: _,
remainder,
} => {
self.check_sym_layout(
condition,
Layout::Builtin(Builtin::Bool),
UseKind::ExpectCond,
);
self.check_sym_layout(condition, Layout::BOOL, UseKind::ExpectCond);
for sym in lookups.iter() {
self.check_sym_exists(*sym);
}
@ -374,13 +377,11 @@ impl<'a, 'r> Ctx<'a, 'r> {
self.problem(ProblemKind::NoJoinPoint { id });
}
}
&Stmt::Crash(sym, _) => {
self.check_sym_layout(sym, Layout::Builtin(Builtin::Str), UseKind::CrashArg)
}
&Stmt::Crash(sym, _) => self.check_sym_layout(sym, Layout::STR, UseKind::CrashArg),
}
}
fn check_expr(&mut self, e: &Expr<'a>) -> Option<Layout<'a>> {
fn check_expr(&mut self, e: &Expr<'a>) -> Option<InLayout<'a>> {
match e {
Expr::Literal(_) => None,
Expr::Call(call) => self.check_call(call),
@ -390,7 +391,7 @@ impl<'a, 'r> Ctx<'a, 'r> {
arguments,
} => {
self.check_tag_expr(tag_layout, tag_id, arguments);
Some(Layout::Union(tag_layout))
Some(self.interner.insert(Layout::Union(tag_layout)))
}
Expr::Struct(syms) => {
for sym in syms.iter() {
@ -424,26 +425,29 @@ impl<'a, 'r> Ctx<'a, 'r> {
}
}
}
let elem_layout = self.interner.insert(*elem_layout);
Some(Layout::Builtin(Builtin::List(elem_layout)))
Some(
self.interner
.insert(Layout::Builtin(Builtin::List(*elem_layout))),
)
}
Expr::EmptyArray => {
// TODO don't know what the element layout is
None
}
&Expr::ExprBox { symbol } => self.with_sym_layout(symbol, |ctx, _def_line, layout| {
let inner = ctx.interner.insert(layout);
Some(Layout::Boxed(inner))
let inner = layout;
Some(ctx.interner.insert(Layout::Boxed(inner)))
}),
&Expr::ExprUnbox { symbol } => {
self.with_sym_layout(symbol, |ctx, def_line, layout| match ctx.resolve(layout) {
Layout::Boxed(inner) => Some(ctx.interner.get(inner)),
&Expr::ExprUnbox { symbol } => self.with_sym_layout(symbol, |ctx, def_line, layout| {
let layout = ctx.resolve(layout);
match ctx.interner.get(layout) {
Layout::Boxed(inner) => Some(inner),
_ => {
ctx.problem(ProblemKind::UnboxNotABox { symbol, def_line });
None
}
})
}
}
}),
&Expr::Reuse {
symbol,
update_tag_id: _,
@ -452,9 +456,10 @@ impl<'a, 'r> Ctx<'a, 'r> {
tag_id: _,
arguments: _,
} => {
self.check_sym_layout(symbol, Layout::Union(tag_layout), UseKind::TagReuse);
let union = self.interner.insert(Layout::Union(tag_layout));
self.check_sym_layout(symbol, union, UseKind::TagReuse);
// TODO also check update arguments
Some(Layout::Union(tag_layout))
Some(union)
}
&Expr::Reset {
symbol,
@ -467,9 +472,10 @@ impl<'a, 'r> Ctx<'a, 'r> {
}
}
fn check_struct_at_index(&mut self, structure: Symbol, index: u64) -> Option<Layout<'a>> {
fn check_struct_at_index(&mut self, structure: Symbol, index: u64) -> Option<InLayout<'a>> {
self.with_sym_layout(structure, |ctx, def_line, layout| {
match ctx.resolve(layout) {
let layout = ctx.resolve(layout);
match ctx.interner.get(layout) {
Layout::Struct { field_layouts, .. } => {
if index as usize >= field_layouts.len() {
ctx.problem(ProblemKind::StructIndexOOB {
@ -500,9 +506,10 @@ impl<'a, 'r> Ctx<'a, 'r> {
union_layout: UnionLayout<'a>,
tag_id: u16,
index: u64,
) -> Option<Layout<'a>> {
) -> Option<InLayout<'a>> {
let union = self.interner.insert(Layout::Union(union_layout));
self.with_sym_layout(structure, |ctx, def_line, _layout| {
ctx.check_sym_layout(structure, Layout::Union(union_layout), UseKind::TagExpr);
ctx.check_sym_layout(structure, union, UseKind::TagExpr);
match get_tag_id_payloads(union_layout, tag_id) {
TagPayloads::IdNotInUnion => {
@ -537,7 +544,7 @@ impl<'a, 'r> Ctx<'a, 'r> {
})
}
fn check_call(&mut self, call: &Call<'a>) -> Option<Layout<'a>> {
fn check_call(&mut self, call: &Call<'a>) -> Option<InLayout<'a>> {
let Call {
call_type,
arguments,
@ -552,7 +559,7 @@ impl<'a, 'r> Ctx<'a, 'r> {
} => {
let proc_layout = ProcLayout {
arguments: arg_layouts,
result: **ret_layout,
result: *ret_layout,
niche: name.niche(),
};
if !self.procs.contains_key(&(name.name(), proc_layout)) {
@ -576,7 +583,7 @@ impl<'a, 'r> Ctx<'a, 'r> {
{
self.problem(ProblemKind::DuplicateCallSpecId { old_call_line });
}
Some(**ret_layout)
Some(*ret_layout)
}
CallType::HigherOrder(HigherOrderLowLevel {
op: _,
@ -590,7 +597,7 @@ impl<'a, 'r> Ctx<'a, 'r> {
CallType::Foreign {
foreign_symbol: _,
ret_layout,
} => Some(**ret_layout),
} => Some(*ret_layout),
CallType::LowLevel {
op: _,
update_mode: _,
@ -639,9 +646,9 @@ impl<'a, 'r> Ctx<'a, 'r> {
fn resolve_recursive_layout<'a>(
arena: &'a Bump,
interner: &mut STLayoutInterner<'a>,
layout: Layout<'a>,
layout: InLayout<'a>,
when_recursive: UnionLayout<'a>,
) -> Layout<'a> {
) -> InLayout<'a> {
macro_rules! go {
($lay:expr) => {
resolve_recursive_layout(arena, interner, $lay, when_recursive)
@ -649,7 +656,7 @@ fn resolve_recursive_layout<'a>(
}
// TODO check if recursive pointer not in recursive union
match layout {
let layout = match interner.get(layout) {
Layout::RecursivePointer => Layout::Union(when_recursive),
Layout::Union(union_layout) => match union_layout {
UnionLayout::NonRecursive(payloads) => {
@ -667,12 +674,12 @@ fn resolve_recursive_layout<'a>(
// This is the recursive layout.
// TODO will need fixing to be modified once we support multiple
// recursive pointers in one structure.
layout
return layout;
}
},
Layout::Boxed(inner) => {
let inner = go!(interner.get(inner));
Layout::Boxed(interner.insert(inner))
let inner = go!(inner);
Layout::Boxed(inner)
}
Layout::Struct {
field_order_hash,
@ -689,16 +696,14 @@ fn resolve_recursive_layout<'a>(
}
Layout::Builtin(builtin) => match builtin {
Builtin::List(inner) => {
let inner =
resolve_recursive_layout(arena, interner, interner.get(inner), when_recursive);
let inner = interner.insert(inner);
let inner = resolve_recursive_layout(arena, interner, inner, when_recursive);
Layout::Builtin(Builtin::List(inner))
}
Builtin::Int(_)
| Builtin::Float(_)
| Builtin::Bool
| Builtin::Decimal
| Builtin::Str => layout,
| Builtin::Str => return layout,
},
Layout::LambdaSet(LambdaSet {
set,
@ -706,10 +711,7 @@ fn resolve_recursive_layout<'a>(
full_layout,
}) => {
let set = set.iter().map(|(symbol, captures)| {
let captures = captures.iter().map(|lay_in| {
let new_lay = go!(interner.get(*lay_in));
interner.insert(new_lay)
});
let captures = captures.iter().map(|lay_in| go!(*lay_in));
let captures = &*arena.alloc_slice_fill_iter(captures);
(*symbol, captures)
});
@ -720,12 +722,14 @@ fn resolve_recursive_layout<'a>(
full_layout,
})
}
}
};
interner.insert(layout)
}
enum TagPayloads<'a> {
IdNotInUnion,
Payloads(&'a [Layout<'a>]),
Payloads(&'a [InLayout<'a>]),
}
fn get_tag_id_payloads(union_layout: UnionLayout, tag_id: TagIdIntType) -> TagPayloads {

View file

@ -157,7 +157,7 @@ where
f.concat([
format_symbol(f, interns, symbol),
f.reflow(" defined here with layout "),
def_layout.to_doc(f, interner, Parens::NotNeeded),
interner.to_doc(def_layout, f, Parens::NotNeeded),
]),
)];
f.concat([
@ -165,7 +165,7 @@ where
f.reflow(" used as a "),
f.reflow(format_use_kind(use_kind)),
f.reflow(" here with layout "),
use_layout.to_doc(f, interner, Parens::NotNeeded),
interner.to_doc(use_layout, f, Parens::NotNeeded),
])
}
ProblemKind::SymbolDefMismatch {
@ -178,9 +178,9 @@ where
f.concat([
format_symbol(f, interns, symbol),
f.reflow(" is defined as "),
def_layout.to_doc(f, interner, Parens::NotNeeded),
interner.to_doc(def_layout, f, Parens::NotNeeded),
f.reflow(" but its initializer is "),
expr_layout.to_doc(f, interner, Parens::NotNeeded),
interner.to_doc(expr_layout, f, Parens::NotNeeded),
])
}
ProblemKind::BadSwitchConditionLayout { found_layout } => {
@ -188,7 +188,7 @@ where
docs_before = vec![];
f.concat([
f.reflow("This switch condition is a "),
found_layout.to_doc(f, interner, Parens::NotNeeded),
interner.to_doc(found_layout, f, Parens::NotNeeded),
])
}
ProblemKind::DuplicateSwitchBranch {} => {
@ -469,13 +469,13 @@ where
let args = f.intersperse(
arguments
.iter()
.map(|a| a.to_doc(f, interner, Parens::InFunction)),
.map(|a| interner.to_doc(*a, f, Parens::InFunction)),
f.reflow(", "),
);
let fun = f.concat([
f.concat([f.reflow("("), args, f.reflow(")")]),
f.reflow(" -> "),
result.to_doc(f, interner, Parens::NotNeeded),
interner.to_doc(result, f, Parens::NotNeeded),
]);
let niche = (f.text("("))
.append(captures_niche.to_doc(f, interner))

View file

@ -4,7 +4,8 @@ use crate::ir::{
ListIndex, Literal, Param, Pattern, Procs, Stmt,
};
use crate::layout::{
Builtin, Layout, LayoutCache, LayoutInterner, TLLayoutInterner, TagIdIntType, UnionLayout,
Builtin, InLayout, Layout, LayoutCache, LayoutInterner, TLLayoutInterner, TagIdIntType,
UnionLayout,
};
use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_collections::all::{MutMap, MutSet};
@ -23,7 +24,10 @@ const RECORD_TAG_NAME: &str = "#Record";
/// some normal branches and gives out a decision tree that has "labels" at all
/// the leafs and a dictionary that maps these "labels" to the code that should
/// run.
fn compile<'a>(raw_branches: Vec<(Guard<'a>, Pattern<'a>, u64)>) -> DecisionTree<'a> {
fn compile<'a>(
interner: &TLLayoutInterner<'a>,
raw_branches: Vec<(Guard<'a>, Pattern<'a>, u64)>,
) -> DecisionTree<'a> {
let formatted = raw_branches
.into_iter()
.map(|(guard, pattern, index)| Branch {
@ -33,7 +37,7 @@ fn compile<'a>(raw_branches: Vec<(Guard<'a>, Pattern<'a>, u64)>) -> DecisionTree
})
.collect();
to_decision_tree(formatted)
to_decision_tree(interner, formatted)
}
#[derive(Clone, Debug, PartialEq)]
@ -96,7 +100,7 @@ enum Test<'a> {
tag_id: TagIdIntType,
ctor_name: CtorName,
union: roc_exhaustive::Union,
arguments: Vec<(Pattern<'a>, Layout<'a>)>,
arguments: Vec<(Pattern<'a>, InLayout<'a>)>,
},
IsInt([u8; 16], IntWidth),
IsFloat(u64, FloatWidth),
@ -207,7 +211,10 @@ struct Branch<'a> {
patterns: Vec<(Vec<PathInstruction>, Pattern<'a>)>,
}
fn to_decision_tree(raw_branches: Vec<Branch>) -> DecisionTree {
fn to_decision_tree<'a>(
interner: &TLLayoutInterner<'a>,
raw_branches: Vec<Branch<'a>>,
) -> DecisionTree<'a> {
let branches: Vec<_> = raw_branches.into_iter().map(flatten_patterns).collect();
debug_assert!(!branches.is_empty());
@ -239,7 +246,7 @@ fn to_decision_tree(raw_branches: Vec<Branch>) -> DecisionTree {
let default = if branches.is_empty() {
None
} else {
Some(Box::new(to_decision_tree(branches)))
Some(Box::new(to_decision_tree(interner, branches)))
};
DecisionTree::Decision {
@ -256,7 +263,7 @@ fn to_decision_tree(raw_branches: Vec<Branch>) -> DecisionTree {
let path = pick_path(&branches).clone();
let bs = branches.clone();
let (edges, fallback) = gather_edges(branches, &path);
let (edges, fallback) = gather_edges(interner, branches, &path);
let mut decision_edges: Vec<_> = edges
.into_iter()
@ -264,7 +271,7 @@ fn to_decision_tree(raw_branches: Vec<Branch>) -> DecisionTree {
if bs == branches {
panic!();
} else {
(test, to_decision_tree(branches))
(test, to_decision_tree(interner, branches))
}
})
.collect();
@ -280,12 +287,12 @@ fn to_decision_tree(raw_branches: Vec<Branch>) -> DecisionTree {
([], _) => {
// should be guaranteed by the patterns
debug_assert!(!fallback.is_empty());
to_decision_tree(fallback)
to_decision_tree(interner, fallback)
}
(_, _) => break_out_guard(
path,
decision_edges,
Some(Box::new(to_decision_tree(fallback))),
Some(Box::new(to_decision_tree(interner, fallback))),
),
}
}
@ -458,6 +465,7 @@ fn check_for_match(branches: &[Branch]) -> Match {
// my understanding: branches that we could jump to based on the pattern at the current path
fn gather_edges<'a>(
interner: &TLLayoutInterner<'a>,
branches: Vec<Branch<'a>>,
path: &[PathInstruction],
) -> (Vec<(GuardedTest<'a>, Vec<Branch<'a>>)>, Vec<Branch<'a>>) {
@ -467,7 +475,7 @@ fn gather_edges<'a>(
let all_edges = relevant_tests
.into_iter()
.map(|t| edges_for(path, &branches, t))
.map(|t| edges_for(interner, path, &branches, t))
.collect();
let fallbacks = if check {
@ -661,6 +669,7 @@ fn test_at_path<'a>(
// understanding: if the test is successful, where could we go?
fn edges_for<'a>(
interner: &TLLayoutInterner<'a>,
path: &[PathInstruction],
branches: &[Branch<'a>],
test: GuardedTest<'a>,
@ -682,13 +691,14 @@ fn edges_for<'a>(
};
for branch in it {
new_branches.extend(to_relevant_branch(&test, path, branch));
new_branches.extend(to_relevant_branch(interner, &test, path, branch));
}
(test, new_branches)
}
fn to_relevant_branch<'a>(
interner: &TLLayoutInterner<'a>,
guarded_test: &GuardedTest<'a>,
path: &[PathInstruction],
branch: &Branch<'a>,
@ -712,13 +722,14 @@ fn to_relevant_branch<'a>(
Some(branch.clone())
}
GuardedTest::TestNotGuarded { test } => {
to_relevant_branch_help(test, path, start, end, branch, pattern)
to_relevant_branch_help(interner, test, path, start, end, branch, pattern)
}
},
}
}
fn to_relevant_branch_help<'a>(
interner: &TLLayoutInterner<'a>,
test: &Test<'a>,
path: &[PathInstruction],
mut start: Vec<(Vec<PathInstruction>, Pattern<'a>)>,
@ -903,11 +914,15 @@ fn to_relevant_branch_help<'a>(
// the test matches the constructor of this pattern
match layout {
UnionLayout::NonRecursive(
[[Layout::Struct {
field_layouts: [_], ..
}]],
) => {
UnionLayout::NonRecursive([[arg]])
if matches!(
interner.get(*arg),
Layout::Struct {
field_layouts: [_],
..
}
) =>
{
// a one-element record equivalent
// Theory: Unbox doesn't have any value for us
debug_assert_eq!(arguments.len(), 1);
@ -1274,15 +1289,15 @@ enum Choice<'a> {
Jump(Label),
}
type StoresVec<'a> = bumpalo::collections::Vec<'a, (Symbol, Layout<'a>, Expr<'a>)>;
type StoresVec<'a> = bumpalo::collections::Vec<'a, (Symbol, InLayout<'a>, Expr<'a>)>;
pub fn optimize_when<'a>(
env: &mut Env<'a, '_>,
procs: &mut Procs<'a>,
layout_cache: &mut LayoutCache<'a>,
cond_symbol: Symbol,
cond_layout: Layout<'a>,
ret_layout: Layout<'a>,
cond_layout: InLayout<'a>,
ret_layout: InLayout<'a>,
opt_branches: bumpalo::collections::Vec<'a, (Pattern<'a>, Guard<'a>, Stmt<'a>)>,
) -> Stmt<'a> {
let (patterns, _indexed_branches) = opt_branches
@ -1299,7 +1314,7 @@ pub fn optimize_when<'a>(
let indexed_branches: Vec<_> = _indexed_branches;
let decision_tree = compile(patterns);
let decision_tree = compile(&layout_cache.interner, patterns);
let decider = tree_to_decider(decision_tree);
// for each target (branch body), count in how many ways it can be reached
@ -1361,11 +1376,11 @@ enum PathInstruction {
fn path_to_expr_help<'a>(
env: &mut Env<'a, '_>,
layout_interner: &TLLayoutInterner<'a>,
layout_interner: &mut TLLayoutInterner<'a>,
mut symbol: Symbol,
path: &[PathInstruction],
mut layout: Layout<'a>,
) -> (Symbol, StoresVec<'a>, Layout<'a>) {
mut layout: InLayout<'a>,
) -> (Symbol, StoresVec<'a>, InLayout<'a>) {
let mut stores = bumpalo::collections::Vec::new_in(env.arena);
// let instructions = reverse_path(path);
@ -1381,17 +1396,20 @@ fn path_to_expr_help<'a>(
PathInstruction::TagIndex { index, tag_id } => {
let index = *index;
match &layout {
match layout_interner.get(layout) {
Layout::Union(union_layout) => {
let inner_expr = Expr::UnionAtIndex {
tag_id: *tag_id,
structure: symbol,
index,
union_layout: *union_layout,
union_layout: union_layout,
};
let inner_layout =
union_layout.layout_at(*tag_id as TagIdIntType, index as usize);
let inner_layout = union_layout.layout_at(
layout_interner,
*tag_id as TagIdIntType,
index as usize,
);
symbol = env.unique_symbol();
stores.push((symbol, inner_layout, inner_expr));
@ -1431,7 +1449,7 @@ fn path_to_expr_help<'a>(
PathInstruction::ListIndex { index } => {
let list_sym = symbol;
match layout {
match layout_interner.get(layout) {
Layout::Builtin(Builtin::List(elem_layout)) => {
let (index_sym, new_stores) = build_list_index_probe(env, list_sym, index);
@ -1446,8 +1464,6 @@ fn path_to_expr_help<'a>(
arguments: env.arena.alloc([list_sym, index_sym]),
});
let elem_layout = layout_interner.get(elem_layout);
stores.push((load_sym, elem_layout, load_expr));
layout = elem_layout;
@ -1464,9 +1480,9 @@ fn path_to_expr_help<'a>(
fn test_to_comparison<'a>(
env: &mut Env<'a, '_>,
layout_interner: &TLLayoutInterner<'a>,
layout_interner: &mut TLLayoutInterner<'a>,
cond_symbol: Symbol,
cond_layout: &Layout<'a>,
cond_layout: &InLayout<'a>,
path: &[PathInstruction],
test: Test<'a>,
) -> (StoresVec<'a>, Comparison, Option<ConstructorKnown<'a>>) {
@ -1480,7 +1496,7 @@ fn test_to_comparison<'a>(
// (e.g. record pattern guard matches)
debug_assert!(union.alternatives.len() > 1);
match test_layout {
match layout_interner.get(test_layout) {
Layout::Union(union_layout) => {
let lhs = Expr::Literal(Literal::Int((tag_id as i128).to_ne_bytes()));
@ -1544,7 +1560,7 @@ fn test_to_comparison<'a>(
let lhs = Expr::Literal(Literal::Byte(test_byte as u8));
let lhs_symbol = env.unique_symbol();
stores.push((lhs_symbol, Layout::u8(), lhs));
stores.push((lhs_symbol, Layout::U8, lhs));
(stores, (lhs_symbol, Comparator::Eq, rhs_symbol), None)
}
@ -1552,7 +1568,7 @@ fn test_to_comparison<'a>(
Test::IsBit(test_bit) => {
let lhs = Expr::Literal(Literal::Bool(test_bit));
let lhs_symbol = env.unique_symbol();
stores.push((lhs_symbol, Layout::Builtin(Builtin::Bool), lhs));
stores.push((lhs_symbol, Layout::BOOL, lhs));
(stores, (lhs_symbol, Comparator::Eq, rhs_symbol), None)
}
@ -1561,7 +1577,7 @@ fn test_to_comparison<'a>(
let lhs = Expr::Literal(Literal::Str(env.arena.alloc(test_str)));
let lhs_symbol = env.unique_symbol();
stores.push((lhs_symbol, Layout::Builtin(Builtin::Str), lhs));
stores.push((lhs_symbol, Layout::STR, lhs));
(stores, (lhs_symbol, Comparator::Eq, rhs_symbol), None)
}
@ -1570,7 +1586,7 @@ fn test_to_comparison<'a>(
let list_layout = test_layout;
let list_sym = rhs_symbol;
match list_layout {
match layout_interner.get(list_layout) {
Layout::Builtin(Builtin::List(_elem_layout)) => {
let real_len_expr = Expr::Call(Call {
call_type: CallType::LowLevel {
@ -1614,16 +1630,16 @@ enum Comparator {
type Comparison = (Symbol, Comparator, Symbol);
type Tests<'a> = std::vec::Vec<(
bumpalo::collections::Vec<'a, (Symbol, Layout<'a>, Expr<'a>)>,
bumpalo::collections::Vec<'a, (Symbol, InLayout<'a>, Expr<'a>)>,
Comparison,
Option<ConstructorKnown<'a>>,
)>;
fn stores_and_condition<'a>(
env: &mut Env<'a, '_>,
layout_interner: &TLLayoutInterner<'a>,
layout_interner: &mut TLLayoutInterner<'a>,
cond_symbol: Symbol,
cond_layout: &Layout<'a>,
cond_layout: &InLayout<'a>,
test_chain: Vec<(Vec<PathInstruction>, Test<'a>)>,
) -> Tests<'a> {
let mut tests: Tests = Vec::with_capacity(test_chain.len());
@ -1646,8 +1662,8 @@ fn stores_and_condition<'a>(
#[allow(clippy::too_many_arguments)]
fn compile_test<'a>(
env: &mut Env<'a, '_>,
ret_layout: Layout<'a>,
stores: bumpalo::collections::Vec<'a, (Symbol, Layout<'a>, Expr<'a>)>,
ret_layout: InLayout<'a>,
stores: bumpalo::collections::Vec<'a, (Symbol, InLayout<'a>, Expr<'a>)>,
lhs: Symbol,
cmp: Comparator,
rhs: Symbol,
@ -1671,8 +1687,8 @@ fn compile_test<'a>(
fn compile_test_help<'a>(
env: &mut Env<'a, '_>,
branch_info: ConstructorKnown<'a>,
ret_layout: Layout<'a>,
stores: bumpalo::collections::Vec<'a, (Symbol, Layout<'a>, Expr<'a>)>,
ret_layout: InLayout<'a>,
stores: bumpalo::collections::Vec<'a, (Symbol, InLayout<'a>, Expr<'a>)>,
lhs: Symbol,
cmp: Comparator,
rhs: Symbol,
@ -1729,7 +1745,7 @@ fn compile_test_help<'a>(
cond = Stmt::Switch {
cond_symbol: test_symbol,
cond_layout: Layout::Builtin(Builtin::Bool),
cond_layout: Layout::BOOL,
ret_layout,
branches,
default_branch,
@ -1748,12 +1764,7 @@ fn compile_test_help<'a>(
});
// write to the test symbol
cond = Stmt::Let(
test_symbol,
test,
Layout::Builtin(Builtin::Bool),
arena.alloc(cond),
);
cond = Stmt::Let(test_symbol, test, Layout::BOOL, arena.alloc(cond));
// stores are in top-to-bottom order, so we have to add them in reverse
for (symbol, layout, expr) in stores.into_iter().rev() {
@ -1765,7 +1776,7 @@ fn compile_test_help<'a>(
fn compile_tests<'a>(
env: &mut Env<'a, '_>,
ret_layout: Layout<'a>,
ret_layout: InLayout<'a>,
tests: Tests<'a>,
fail: &'a Stmt<'a>,
mut cond: Stmt<'a>,
@ -1789,13 +1800,13 @@ fn compile_tests<'a>(
enum ConstructorKnown<'a> {
Both {
scrutinee: Symbol,
layout: Layout<'a>,
layout: InLayout<'a>,
pass: TagIdIntType,
fail: TagIdIntType,
},
OnlyPass {
scrutinee: Symbol,
layout: Layout<'a>,
layout: InLayout<'a>,
tag_id: TagIdIntType,
},
Neither,
@ -1804,7 +1815,7 @@ enum ConstructorKnown<'a> {
impl<'a> ConstructorKnown<'a> {
fn from_test_chain(
cond_symbol: Symbol,
cond_layout: &Layout<'a>,
cond_layout: InLayout<'a>,
test_chain: &[(Vec<PathInstruction>, Test)],
) -> Self {
match test_chain {
@ -1813,14 +1824,14 @@ impl<'a> ConstructorKnown<'a> {
if union.alternatives.len() == 2 {
// excluded middle: we also know the tag_id in the fail branch
ConstructorKnown::Both {
layout: *cond_layout,
layout: cond_layout,
scrutinee: cond_symbol,
pass: *tag_id,
fail: (*tag_id == 0) as _,
}
} else {
ConstructorKnown::OnlyPass {
layout: *cond_layout,
layout: cond_layout,
scrutinee: cond_symbol,
tag_id: *tag_id,
}
@ -1842,8 +1853,8 @@ fn decide_to_branching<'a>(
procs: &mut Procs<'a>,
layout_cache: &mut LayoutCache<'a>,
cond_symbol: Symbol,
cond_layout: Layout<'a>,
ret_layout: Layout<'a>,
cond_layout: InLayout<'a>,
ret_layout: InLayout<'a>,
decider: Decider<'a, Choice<'a>>,
jumps: &[(u64, JoinPointId, Stmt<'a>)],
) -> Stmt<'a> {
@ -1897,7 +1908,7 @@ fn decide_to_branching<'a>(
let decide = crate::ir::cond(
env,
test_symbol,
Layout::Builtin(Builtin::Bool),
Layout::BOOL,
pass_expr,
fail_expr,
ret_layout,
@ -1906,7 +1917,7 @@ fn decide_to_branching<'a>(
// calculate the guard value
let param = Param {
symbol: test_symbol,
layout: Layout::Builtin(Builtin::Bool),
layout: Layout::BOOL,
ownership: Ownership::Owned,
};
@ -1949,11 +1960,11 @@ fn decide_to_branching<'a>(
);
let chain_branch_info =
ConstructorKnown::from_test_chain(cond_symbol, &cond_layout, &test_chain);
ConstructorKnown::from_test_chain(cond_symbol, cond_layout, &test_chain);
let tests = stores_and_condition(
env,
&layout_cache.interner,
&mut layout_cache.interner,
cond_symbol,
&cond_layout,
test_chain,
@ -2004,8 +2015,13 @@ fn decide_to_branching<'a>(
// the cond_layout can change in the process. E.g. if the cond is a Tag, we actually
// switch on the tag discriminant (currently an i64 value)
// NOTE the tag discriminant is not actually loaded, `cond` can point to a tag
let (inner_cond_symbol, cond_stores_vec, inner_cond_layout) =
path_to_expr_help(env, &layout_cache.interner, cond_symbol, &path, cond_layout);
let (inner_cond_symbol, cond_stores_vec, inner_cond_layout) = path_to_expr_help(
env,
&mut layout_cache.interner,
cond_symbol,
&path,
cond_layout,
);
let default_branch = decide_to_branching(
env,
@ -2082,7 +2098,8 @@ fn decide_to_branching<'a>(
// We have learned more about the exact layout of the cond (based on the path)
// but tests are still relative to the original cond symbol
let mut switch = if let Layout::Union(union_layout) = inner_cond_layout {
let inner_cond_layout_raw = layout_cache.get_in(inner_cond_layout);
let mut switch = if let Layout::Union(union_layout) = inner_cond_layout_raw {
let tag_id_symbol = env.unique_symbol();
let temp = Stmt::Switch {
@ -2104,7 +2121,7 @@ fn decide_to_branching<'a>(
union_layout.tag_id_layout(),
env.arena.alloc(temp),
)
} else if let Layout::Builtin(Builtin::List(_)) = inner_cond_layout {
} else if let Layout::Builtin(Builtin::List(_)) = inner_cond_layout_raw {
let len_symbol = env.unique_symbol();
let switch = Stmt::Switch {
@ -2150,7 +2167,7 @@ fn decide_to_branching<'a>(
}
/*
fn boolean_all<'a>(arena: &'a Bump, tests: Vec<(Expr<'a>, Expr<'a>, Layout<'a>)>) -> Expr<'a> {
fn boolean_all<'a>(arena: &'a Bump, tests: Vec<(Expr<'a>, Expr<'a>, InLayout<'a>)>) -> Expr<'a> {
let mut expr = Expr::Bool(true);
for (lhs, rhs, layout) in tests.into_iter().rev() {

View file

@ -3,7 +3,7 @@ use crate::ir::{
CallType, Expr, HigherOrderLowLevel, JoinPointId, ModifyRc, Param, Proc, ProcLayout, Stmt,
UpdateModeIds,
};
use crate::layout::{Layout, STLayoutInterner};
use crate::layout::{InLayout, Layout, LayoutInterner, STLayoutInterner};
use bumpalo::collections::Vec;
use bumpalo::Bump;
use roc_collections::all::{MutMap, MutSet};
@ -559,7 +559,7 @@ impl<'a, 'i> Context<'a, 'i> {
z: Symbol,
call_type: crate::ir::CallType<'a>,
arguments: &'a [Symbol],
l: Layout<'a>,
l: InLayout<'a>,
b: &'a Stmt<'a>,
b_live_vars: &LiveVarSet,
) -> &'a Stmt<'a> {
@ -600,8 +600,7 @@ impl<'a, 'i> Context<'a, 'i> {
arg_layouts,
..
} => {
let top_level =
ProcLayout::new(self.arena, arg_layouts, name.niche(), **ret_layout);
let top_level = ProcLayout::new(self.arena, arg_layouts, name.niche(), *ret_layout);
// get the borrow signature
let ps = self
@ -629,7 +628,7 @@ impl<'a, 'i> Context<'a, 'i> {
z: Symbol,
lowlevel: &'a crate::ir::HigherOrderLowLevel,
arguments: &'a [Symbol],
l: Layout<'a>,
l: InLayout<'a>,
b: &'a Stmt<'a>,
b_live_vars: &LiveVarSet,
) -> &'a Stmt<'a> {
@ -857,7 +856,7 @@ impl<'a, 'i> Context<'a, 'i> {
codegen: &mut CodegenTools<'i>,
z: Symbol,
v: Expr<'a>,
l: Layout<'a>,
l: InLayout<'a>,
b: &'a Stmt<'a>,
b_live_vars: &LiveVarSet,
) -> (&'a Stmt<'a>, LiveVarSet) {
@ -955,7 +954,7 @@ impl<'a, 'i> Context<'a, 'i> {
(new_b, live_vars)
}
fn update_var_info(&self, symbol: Symbol, layout: &Layout<'a>, expr: &Expr<'a>) -> Self {
fn update_var_info(&self, symbol: Symbol, layout: &InLayout<'a>, expr: &Expr<'a>) -> Self {
// is this value a constant? TODO do function pointers also fall into this category?
let persistent = false;
@ -970,13 +969,13 @@ impl<'a, 'i> Context<'a, 'i> {
fn update_var_info_help(
&self,
symbol: Symbol,
layout: &Layout<'a>,
layout: &InLayout<'a>,
persistent: bool,
consume: bool,
reset: bool,
) -> Self {
// should we perform incs and decs on this value?
let reference = layout.contains_refcounted(self.layout_interner);
let reference = self.layout_interner.contains_refcounted(*layout);
let info = VarInfo {
reference,
@ -997,7 +996,7 @@ impl<'a, 'i> Context<'a, 'i> {
for p in ps.iter() {
let info = VarInfo {
reference: p.layout.contains_refcounted(self.layout_interner),
reference: self.layout_interner.contains_refcounted(p.layout),
consume: match p.ownership {
Ownership::Owned => true,
Ownership::Borrowed => false,
@ -1024,7 +1023,7 @@ impl<'a, 'i> Context<'a, 'i> {
) -> &'a Stmt<'a> {
for p in ps.iter() {
if p.ownership == Ownership::Owned
&& p.layout.contains_refcounted(self.layout_interner)
&& self.layout_interner.contains_refcounted(p.layout)
&& !b_live_vars.contains(&p.symbol)
{
b = self.add_dec(p.symbol, b)
@ -1300,7 +1299,7 @@ fn branch_on_list_uniqueness<'a, 'i>(
arena: &'a Bump,
codegen: &mut CodegenTools<'i>,
list_symbol: Symbol,
return_layout: Layout<'a>,
return_layout: InLayout<'a>,
then_branch_stmt: Stmt<'a>,
else_branch_stmt: &'a Stmt<'a>,
) -> Stmt<'a> {
@ -1331,7 +1330,7 @@ fn branch_on_list_uniqueness<'a, 'i>(
Stmt::Let(
condition_symbol,
Expr::Call(condition_call),
Layout::bool(),
Layout::BOOL,
stmt,
)
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -11,14 +11,11 @@ use roc_collections::{default_hasher, BumpMap};
use roc_module::symbol::Symbol;
use roc_target::TargetInfo;
use super::{Builtin, LambdaSet, Layout};
#[allow(unused)] // for now
pub struct InLayouts(PhantomData<()>);
use super::{Builtin, FieldOrderHash, LambdaSet, Layout, UnionLayout};
macro_rules! cache_interned_layouts {
($($i:literal, $name:ident, $layout:expr)*; $total_constants:literal) => {
impl InLayouts {
impl<'a> Layout<'a> {
$(
#[allow(unused)] // for now
pub const $name: InLayout<'static> = unsafe { InLayout::from_reserved_index($i) };
@ -48,8 +45,8 @@ macro_rules! cache_interned_layouts {
}
cache_interned_layouts! {
0, VOID, Layout::VOID
1, UNIT, Layout::UNIT
0, VOID, Layout::VOID_NAKED
1, UNIT, Layout::UNIT_NAKED
2, BOOL, Layout::Builtin(Builtin::Bool)
3, U8, Layout::Builtin(Builtin::Int(IntWidth::U8))
4, U16, Layout::Builtin(Builtin::Int(IntWidth::U16))
@ -64,13 +61,20 @@ cache_interned_layouts! {
13, F32, Layout::Builtin(Builtin::Float(FloatWidth::F32))
14, F64, Layout::Builtin(Builtin::Float(FloatWidth::F64))
15, DEC, Layout::Builtin(Builtin::Decimal)
16, STR, Layout::Builtin(Builtin::Str)
17, RECURSIVE_PTR, Layout::RecursivePointer
; 16
; 18
}
impl InLayouts {
#[allow(unused)] // for now
pub const fn from_int_width(w: IntWidth) -> InLayout<'static> {
impl<'a> Layout<'a> {
pub(super) const VOID_NAKED: Self = Layout::Union(UnionLayout::NonRecursive(&[]));
pub(super) const UNIT_NAKED: Self = Layout::Struct {
field_layouts: &[],
field_order_hash: FieldOrderHash::ZERO_FIELD_HASH,
};
pub const fn int_width(w: IntWidth) -> InLayout<'static> {
match w {
IntWidth::U8 => Self::U8,
IntWidth::U16 => Self::U16,
@ -84,8 +88,7 @@ impl InLayouts {
IntWidth::I128 => Self::I128,
}
}
#[allow(unused)] // for now
pub const fn from_float_width(w: FloatWidth) -> InLayout<'static> {
pub const fn float_width(w: FloatWidth) -> InLayout<'static> {
match w {
FloatWidth::F32 => Self::F32,
FloatWidth::F64 => Self::F64,
@ -114,6 +117,9 @@ pub trait LayoutInterner<'a>: Sized {
/// Retrieves a value from the interner.
fn get(&self, key: InLayout<'a>) -> Layout<'a>;
//
// Convenience methods
fn target_info(&self) -> TargetInfo;
fn alignment_bytes(&self, layout: InLayout<'a>) -> u32 {
@ -123,6 +129,28 @@ pub trait LayoutInterner<'a>: Sized {
fn stack_size(&self, layout: InLayout<'a>) -> u32 {
self.get(layout).stack_size(self, self.target_info())
}
fn contains_refcounted(&self, layout: InLayout<'a>) -> bool {
self.get(layout).contains_refcounted(self)
}
fn is_refcounted(&self, layout: InLayout<'a>) -> bool {
self.get(layout).is_refcounted()
}
fn to_doc<'b, D, A>(
&self,
layout: InLayout<'a>,
alloc: &'b D,
parens: crate::ir::Parens,
) -> ven_pretty::DocBuilder<'b, D, A>
where
D: ven_pretty::DocAllocator<'b, A>,
D::Doc: Clone,
A: Clone,
{
self.get(layout).to_doc(alloc, self, parens)
}
}
/// An interned layout.
@ -227,7 +255,7 @@ fn make_normalized_lamdba_set<'a>(
LambdaSet {
set,
representation,
full_layout: InLayouts::VOID,
full_layout: Layout::VOID,
}
}

View file

@ -17,7 +17,7 @@ use crate::inc_dec::{collect_stmt, occurring_variables_expr, JPLiveVarMap, LiveV
use crate::ir::{
BranchInfo, Call, Expr, ListLiteralElement, Proc, Stmt, UpdateModeId, UpdateModeIds,
};
use crate::layout::{Layout, TagIdIntType, UnionLayout};
use crate::layout::{Layout, LayoutInterner, STLayoutInterner, TagIdIntType, UnionLayout};
use bumpalo::collections::Vec;
use bumpalo::Bump;
use roc_collections::all::MutSet;
@ -25,6 +25,7 @@ use roc_module::symbol::{IdentIds, ModuleId, Symbol};
pub fn insert_reset_reuse<'a, 'i>(
arena: &'a Bump,
interner: &'i mut STLayoutInterner<'a>,
home: ModuleId,
ident_ids: &'i mut IdentIds,
update_mode_ids: &'i mut UpdateModeIds,
@ -32,6 +33,7 @@ pub fn insert_reset_reuse<'a, 'i>(
) -> Proc<'a> {
let mut env = Env {
arena,
interner,
home,
ident_ids,
update_mode_ids,
@ -65,6 +67,7 @@ fn may_reuse(tag_layout: UnionLayout, tag_id: TagIdIntType, other: &CtorInfo) ->
#[derive(Debug)]
struct Env<'a, 'i> {
arena: &'a Bump,
interner: &'i mut STLayoutInterner<'a>,
/// required for creating new `Symbol`s
home: ModuleId,
@ -332,7 +335,7 @@ fn insert_reset<'a>(
update_mode: w.update_mode,
};
let layout = Layout::Union(union_layout);
let layout = env.interner.insert(Layout::Union(union_layout));
stmt = env
.arena
@ -612,11 +615,11 @@ fn function_r_branch_body<'a, 'i>(
scrutinee,
layout,
tag_id,
} => match layout {
} => match env.interner.get(*layout) {
Layout::Union(UnionLayout::NonRecursive(_)) => temp,
Layout::Union(union_layout) if !union_layout.tag_is_null(*tag_id) => {
let ctor_info = CtorInfo {
layout: *union_layout,
layout: union_layout,
id: *tag_id,
};
function_d(env, *scrutinee, &ctor_info, temp)

View file

@ -2,7 +2,7 @@
use crate::borrow::Ownership;
use crate::ir::{CallType, Expr, JoinPointId, Param, Stmt};
use crate::layout::{LambdaName, Layout};
use crate::layout::{InLayout, LambdaName};
use bumpalo::collections::Vec;
use bumpalo::Bump;
use roc_module::symbol::Symbol;
@ -34,8 +34,8 @@ pub fn make_tail_recursive<'a>(
id: JoinPointId,
needle: LambdaName,
stmt: Stmt<'a>,
args: &'a [(Layout<'a>, Symbol, Symbol)],
ret_layout: Layout,
args: &'a [(InLayout<'a>, Symbol, Symbol)],
ret_layout: InLayout<'a>,
) -> Option<Stmt<'a>> {
let allocated = arena.alloc(stmt);
@ -73,8 +73,8 @@ fn insert_jumps<'a>(
stmt: &'a Stmt<'a>,
goal_id: JoinPointId,
needle: LambdaName,
needle_arguments: &'a [(Layout<'a>, Symbol, Symbol)],
needle_result: Layout,
needle_arguments: &'a [(InLayout<'a>, Symbol, Symbol)],
needle_result: InLayout<'a>,
) -> Option<&'a Stmt<'a>> {
use Stmt::*;
@ -101,7 +101,7 @@ fn insert_jumps<'a>(
}),
_,
Stmt::Ret(rsym),
) if symbol == rsym && is_equal_function(*fsym, arg_layouts, **ret_layout) => {
) if symbol == rsym && is_equal_function(*fsym, arg_layouts, *ret_layout) => {
// replace the call and return with a jump
let jump = Stmt::Jump(goal_id, arguments);