refactor union layout

This commit is contained in:
Folkert 2021-01-16 16:31:43 +01:00
parent c85fa58648
commit de666c575f
6 changed files with 266 additions and 193 deletions

View file

@ -9,8 +9,8 @@ use crate::llvm::build_str::{
}; };
use crate::llvm::compare::{build_eq, build_neq}; use crate::llvm::compare::{build_eq, build_neq};
use crate::llvm::convert::{ use crate::llvm::convert::{
basic_type_from_builtin, basic_type_from_layout, block_of_memory, block_of_memory_slice, basic_type_from_builtin, basic_type_from_layout, block_of_memory, collection, get_fn_type,
collection, get_fn_type, get_ptr_type, ptr_int, get_ptr_type, ptr_int,
}; };
use crate::llvm::refcounting::{ use crate::llvm::refcounting::{
decrement_refcount_layout, increment_refcount_layout, refcount_is_one_comparison, decrement_refcount_layout, increment_refcount_layout, refcount_is_one_comparison,
@ -42,7 +42,7 @@ use roc_module::ident::TagName;
use roc_module::low_level::LowLevel; use roc_module::low_level::LowLevel;
use roc_module::symbol::{Interns, ModuleId, Symbol}; use roc_module::symbol::{Interns, ModuleId, Symbol};
use roc_mono::ir::{CallType, JoinPointId, Wrapped}; use roc_mono::ir::{CallType, JoinPointId, Wrapped};
use roc_mono::layout::{Builtin, ClosureLayout, Layout, LayoutIds, MemoryMode}; use roc_mono::layout::{Builtin, ClosureLayout, Layout, LayoutIds, MemoryMode, UnionLayout};
use target_lexicon::CallingConvention; use target_lexicon::CallingConvention;
/// This is for Inkwell's FunctionValue::verify - we want to know the verification /// This is for Inkwell's FunctionValue::verify - we want to know the verification
@ -859,13 +859,13 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
Tag { Tag {
arguments, arguments,
tag_layout: Layout::Union(fields), tag_layout: Layout::Union(UnionLayout::NonRecursive(fields)),
union_size, union_size,
tag_id, tag_id,
tag_name, tag_name,
.. ..
} => { } => {
let tag_layout = Layout::Union(fields); let tag_layout = Layout::Union(UnionLayout::NonRecursive(fields));
debug_assert!(*union_size > 1); debug_assert!(*union_size > 1);
let ptr_size = env.ptr_bytes; let ptr_size = env.ptr_bytes;
@ -960,13 +960,13 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
} }
Tag { Tag {
arguments, arguments,
tag_layout: Layout::RecursiveUnion(fields), tag_layout: Layout::Union(UnionLayout::Recursive(fields)),
union_size, union_size,
tag_id, tag_id,
tag_name, tag_name,
.. ..
} => { } => {
let tag_layout = Layout::Union(fields); let tag_layout = Layout::Union(UnionLayout::NonRecursive(fields));
debug_assert!(*union_size > 1); debug_assert!(*union_size > 1);
let ptr_size = env.ptr_bytes; let ptr_size = env.ptr_bytes;
@ -1042,17 +1042,17 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
Tag { Tag {
arguments, arguments,
tag_layout: tag_layout:
Layout::NullableUnion { Layout::Union(UnionLayout::NullableWrapped {
nullable_id, nullable_id,
nullable_layout: _, nullable_layout: _,
foo: fields, other_tags: fields,
}, }),
union_size, union_size,
tag_id, tag_id,
tag_name, tag_name,
.. ..
} => { } => {
let tag_layout = Layout::Union(fields); let tag_layout = Layout::Union(UnionLayout::NonRecursive(fields));
let tag_struct_type = let tag_struct_type =
basic_type_from_layout(env.arena, env.context, &tag_layout, env.ptr_bytes); basic_type_from_layout(env.arena, env.context, &tag_layout, env.ptr_bytes);
if *tag_id == *nullable_id as u8 { if *tag_id == *nullable_id as u8 {
@ -1251,11 +1251,11 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
} }
PointerValue(value) => { PointerValue(value) => {
match structure_layout { match structure_layout {
Layout::NullableUnion { Layout::Union(UnionLayout::NullableWrapped {
nullable_id, nullable_id,
foo: fields, other_tags: fields,
.. ..
} if *index == 0 => { }) if *index == 0 => {
let ptr = value; let ptr = value;
let is_null = env.builder.build_is_null(ptr, "is_null"); let is_null = env.builder.build_is_null(ptr, "is_null");

View file

@ -4,7 +4,7 @@ use inkwell::context::Context;
use inkwell::types::BasicTypeEnum::{self, *}; use inkwell::types::BasicTypeEnum::{self, *};
use inkwell::types::{ArrayType, BasicType, FunctionType, IntType, PointerType, StructType}; use inkwell::types::{ArrayType, BasicType, FunctionType, IntType, PointerType, StructType};
use inkwell::AddressSpace; use inkwell::AddressSpace;
use roc_mono::layout::{Builtin, Layout}; use roc_mono::layout::{Builtin, Layout, UnionLayout};
/// TODO could this be added to Inkwell itself as a method on BasicValueEnum? /// TODO could this be added to Inkwell itself as a method on BasicValueEnum?
pub fn get_ptr_type<'ctx>( pub fn get_ptr_type<'ctx>(
@ -132,18 +132,17 @@ pub fn basic_type_from_layout<'ctx>(
.into(), .into(),
PhantomEmptyStruct => context.struct_type(&[], false).into(), PhantomEmptyStruct => context.struct_type(&[], false).into(),
Struct(sorted_fields) => basic_type_from_record(arena, context, sorted_fields, ptr_bytes), Struct(sorted_fields) => basic_type_from_record(arena, context, sorted_fields, ptr_bytes),
// Union(tags) if tags.len() == 1 => { Union(variant) => {
// let sorted_fields = tags.iter().next().unwrap(); let block = block_of_memory(context, layout, ptr_bytes);
//
// basic_type_from_record(arena, context, sorted_fields, ptr_bytes) use UnionLayout::*;
// } match variant {
RecursiveUnion(_) => block_of_memory(context, layout, ptr_bytes) Recursive(_) | NullableWrapped { .. } => {
.ptr_type(AddressSpace::Generic) block.ptr_type(AddressSpace::Generic).into()
.into(), }
NullableUnion { .. } => block_of_memory(context, layout, ptr_bytes) NonRecursive(_) => block,
.ptr_type(AddressSpace::Generic) }
.into(), }
Union(_) => block_of_memory(context, layout, ptr_bytes),
RecursivePointer => { RecursivePointer => {
// TODO make this dynamic // TODO make this dynamic
context context
@ -183,20 +182,6 @@ pub fn basic_type_from_builtin<'ctx>(
} }
} }
pub fn block_of_memory_slice<'ctx>(
context: &'ctx Context,
layouts: &[Layout<'_>],
ptr_bytes: u32,
) -> BasicTypeEnum<'ctx> {
let mut union_size = 0;
for layout in layouts {
union_size += layout.stack_size(ptr_bytes as u32);
}
block_of_memory_help(context, union_size)
}
pub fn block_of_memory<'ctx>( pub fn block_of_memory<'ctx>(
context: &'ctx Context, context: &'ctx Context,
layout: &Layout<'_>, layout: &Layout<'_>,

View file

@ -12,7 +12,7 @@ use inkwell::types::{AnyTypeEnum, BasicTypeEnum};
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue}; use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
use inkwell::{AddressSpace, IntPredicate}; use inkwell::{AddressSpace, IntPredicate};
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout, LayoutIds, MemoryMode}; use roc_mono::layout::{Builtin, Layout, LayoutIds, MemoryMode, UnionLayout};
pub const REFCOUNT_MAX: usize = 0_usize; pub const REFCOUNT_MAX: usize = 0_usize;
@ -338,20 +338,28 @@ pub fn decrement_refcount_layout<'a, 'ctx, 'env>(
} }
RecursivePointer => todo!("TODO implement decrement layout of recursive tag union"), RecursivePointer => todo!("TODO implement decrement layout of recursive tag union"),
Union(tags) => { Union(variant) => {
use UnionLayout::*;
match variant {
NonRecursive(tags) => {
build_dec_union(env, layout_ids, tags, value); build_dec_union(env, layout_ids, tags, value);
} }
NullableUnion { foo: tags, .. } => { NullableWrapped {
other_tags: tags, ..
} => {
debug_assert!(value.is_pointer_value()); debug_assert!(value.is_pointer_value());
build_dec_rec_union(env, layout_ids, tags, value.into_pointer_value(), true); build_dec_rec_union(env, layout_ids, tags, value.into_pointer_value(), true);
} }
RecursiveUnion(tags) => { Recursive(tags) => {
debug_assert!(value.is_pointer_value()); debug_assert!(value.is_pointer_value());
build_dec_rec_union(env, layout_ids, tags, value.into_pointer_value(), false); build_dec_rec_union(env, layout_ids, tags, value.into_pointer_value(), false);
} }
}
}
FunctionPointer(_, _) | Pointer(_) => {} FunctionPointer(_, _) | Pointer(_) => {}
} }
@ -433,16 +441,28 @@ pub fn increment_refcount_layout<'a, 'ctx, 'env>(
increment_refcount_builtin(env, parent, layout_ids, value, layout, builtin) increment_refcount_builtin(env, parent, layout_ids, value, layout, builtin)
} }
NullableUnion { foo: tags, .. } => { Union(variant) => {
use UnionLayout::*;
match variant {
NullableWrapped {
other_tags: tags, ..
} => {
debug_assert!(value.is_pointer_value()); debug_assert!(value.is_pointer_value());
build_inc_rec_union(env, layout_ids, tags, value.into_pointer_value(), true); build_inc_rec_union(env, layout_ids, tags, value.into_pointer_value(), true);
} }
RecursiveUnion(tags) => { Recursive(tags) => {
debug_assert!(value.is_pointer_value()); debug_assert!(value.is_pointer_value());
build_inc_rec_union(env, layout_ids, tags, value.into_pointer_value(), false); build_inc_rec_union(env, layout_ids, tags, value.into_pointer_value(), false);
} }
NonRecursive(tags) => {
build_inc_union(env, layout_ids, tags, value);
}
}
}
Closure(_, closure_layout, _) => { Closure(_, closure_layout, _) => {
if closure_layout.contains_refcounted() { if closure_layout.contains_refcounted() {
let wrapper_struct = value.into_struct_value(); let wrapper_struct = value.into_struct_value();
@ -1053,7 +1073,7 @@ pub fn build_dec_rec_union<'a, 'ctx, 'env>(
value: PointerValue<'ctx>, value: PointerValue<'ctx>,
is_nullable: bool, is_nullable: bool,
) { ) {
let layout = Layout::RecursiveUnion(fields); let layout = Layout::Union(UnionLayout::Recursive(fields));
let block = env.builder.get_insert_block().expect("to be in a function"); let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap(); let di_location = env.builder.get_current_debug_location().unwrap();
@ -1130,7 +1150,7 @@ pub fn build_dec_rec_union_help<'a, 'ctx, 'env>(
let parent = fn_val; let parent = fn_val;
let layout = Layout::RecursiveUnion(tags); let layout = Layout::Union(UnionLayout::Recursive(tags));
debug_assert!(arg_val.is_pointer_value()); debug_assert!(arg_val.is_pointer_value());
let value_ptr = arg_val.into_pointer_value(); let value_ptr = arg_val.into_pointer_value();
@ -1270,7 +1290,7 @@ pub fn build_dec_union<'a, 'ctx, 'env>(
fields: &'a [&'a [Layout<'a>]], fields: &'a [&'a [Layout<'a>]],
value: BasicValueEnum<'ctx>, value: BasicValueEnum<'ctx>,
) { ) {
let layout = Layout::Union(fields); let layout = Layout::Union(UnionLayout::NonRecursive(fields));
let block = env.builder.get_insert_block().expect("to be in a function"); let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap(); let di_location = env.builder.get_current_debug_location().unwrap();
@ -1435,7 +1455,7 @@ pub fn build_inc_rec_union<'a, 'ctx, 'env>(
value: PointerValue<'ctx>, value: PointerValue<'ctx>,
is_nullable: bool, is_nullable: bool,
) { ) {
let layout = Layout::RecursiveUnion(fields); let layout = Layout::Union(UnionLayout::Recursive(fields));
let block = env.builder.get_insert_block().expect("to be in a function"); let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap(); let di_location = env.builder.get_current_debug_location().unwrap();
@ -1531,7 +1551,7 @@ pub fn build_inc_rec_union_help<'a, 'ctx, 'env>(
let parent = fn_val; let parent = fn_val;
let layout = Layout::RecursiveUnion(tags); let layout = Layout::Union(UnionLayout::Recursive(tags));
debug_assert!(arg_val.is_pointer_value()); debug_assert!(arg_val.is_pointer_value());
let value_ptr = arg_val.into_pointer_value(); let value_ptr = arg_val.into_pointer_value();
@ -1663,7 +1683,7 @@ pub fn build_inc_union<'a, 'ctx, 'env>(
fields: &'a [&'a [Layout<'a>]], fields: &'a [&'a [Layout<'a>]],
value: BasicValueEnum<'ctx>, value: BasicValueEnum<'ctx>,
) { ) {
let layout = Layout::Union(fields); let layout = Layout::Union(UnionLayout::NonRecursive(fields));
let block = env.builder.get_insert_block().expect("to be in a function"); let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap(); let di_location = env.builder.get_current_debug_location().unwrap();
@ -1737,7 +1757,7 @@ pub fn build_inc_union_help<'a, 'ctx, 'env>(
let parent = fn_val; let parent = fn_val;
let layout = Layout::RecursiveUnion(tags); let layout = Layout::Union(UnionLayout::Recursive(tags));
let before_block = env.builder.get_insert_block().expect("to be in a function"); let before_block = env.builder.get_insert_block().expect("to be in a function");
let wrapper_struct = arg_val.into_struct_value(); let wrapper_struct = arg_val.into_struct_value();
@ -1880,9 +1900,7 @@ pub fn refcount_offset<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, layout: &Layou
match layout { match layout {
Layout::Builtin(Builtin::List(_, _)) => env.ptr_bytes as u64, Layout::Builtin(Builtin::List(_, _)) => env.ptr_bytes as u64,
Layout::Builtin(Builtin::Str) => env.ptr_bytes as u64, Layout::Builtin(Builtin::Str) => env.ptr_bytes as u64,
Layout::RecursivePointer | Layout::Union(_) | Layout::RecursiveUnion(_) => { Layout::RecursivePointer | Layout::Union(_) => env.ptr_bytes as u64,
env.ptr_bytes as u64
}
_ => (env.ptr_bytes as u64).max(value_bytes), _ => (env.ptr_bytes as u64).max(value_bytes),
} }
} }

View file

@ -2,7 +2,7 @@ use crate::exhaustive::{Ctor, RenderAs, TagId, Union};
use crate::ir::{ use crate::ir::{
DestructType, Env, Expr, JoinPointId, Literal, Param, Pattern, Procs, Stmt, Wrapped, DestructType, Env, Expr, JoinPointId, Literal, Param, Pattern, Procs, Stmt, Wrapped,
}; };
use crate::layout::{Builtin, Layout, LayoutCache}; use crate::layout::{Builtin, Layout, LayoutCache, UnionLayout};
use roc_collections::all::{MutMap, MutSet}; use roc_collections::all::{MutMap, MutSet};
use roc_module::ident::TagName; use roc_module::ident::TagName;
use roc_module::low_level::LowLevel; use roc_module::low_level::LowLevel;
@ -228,7 +228,9 @@ fn flatten<'a>(
tag_id, tag_id,
tag_name, tag_name,
layout, layout,
} if union.alternatives.len() == 1 && !matches!(layout, Layout::NullableUnion { .. } ) => { } if union.alternatives.len() == 1
&& !matches!(layout, Layout::Union(UnionLayout::NullableWrapped { .. })) =>
{
// TODO ^ do we need to check that guard.is_none() here? // TODO ^ do we need to check that guard.is_none() here?
let path = path_pattern.0; let path = path_pattern.0;
@ -1005,25 +1007,29 @@ fn path_to_expr_help<'a>(
} }
Some(wrapped) => { Some(wrapped) => {
let field_layouts = match &layout { let field_layouts = match &layout {
Layout::Union(layouts) | Layout::RecursiveUnion(layouts) => { Layout::Union(variant) => {
layouts[*tag_id as usize] use UnionLayout::*;
}
Layout::NullableUnion { match variant {
NonRecursive(layouts) | Recursive(layouts) => layouts[*tag_id as usize],
NullableWrapped {
nullable_id, nullable_id,
nullable_layout, nullable_layout,
foo: layouts, other_tags: layouts,
.. ..
} => { } => {
use std::cmp::Ordering; use std::cmp::Ordering;
dbg!(nullable_id, tag_id); dbg!(nullable_id, tag_id);
match (*tag_id as usize).cmp(&(*nullable_id as usize)) { match (*tag_id as usize).cmp(&(*nullable_id as usize)) {
Ordering::Equal => { Ordering::Equal => &*env
&*env.arena.alloc([Layout::Builtin(nullable_layout.clone())]) .arena
} .alloc([Layout::Builtin(nullable_layout.clone())]),
Ordering::Less => layouts[*tag_id as usize], Ordering::Less => layouts[*tag_id as usize],
Ordering::Greater => layouts[*tag_id as usize - 1], Ordering::Greater => layouts[*tag_id as usize - 1],
} }
} }
}
}
Layout::Struct(layouts) => layouts, Layout::Struct(layouts) => layouts,
other => env.arena.alloc([other.clone()]), other => env.arena.alloc([other.clone()]),

View file

@ -1,7 +1,8 @@
use self::InProgressProc::*; use self::InProgressProc::*;
use crate::exhaustive::{Ctor, Guard, RenderAs, TagId}; use crate::exhaustive::{Ctor, Guard, RenderAs, TagId};
use crate::layout::{ use crate::layout::{
Builtin, ClosureLayout, Layout, LayoutCache, LayoutProblem, WrappedVariant, TAG_SIZE, Builtin, ClosureLayout, Layout, LayoutCache, LayoutProblem, UnionLayout, WrappedVariant,
TAG_SIZE,
}; };
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use bumpalo::Bump; use bumpalo::Bump;
@ -820,7 +821,11 @@ impl Wrapped {
_ => Some(Wrapped::RecordOrSingleTagUnion), _ => Some(Wrapped::RecordOrSingleTagUnion),
}, },
Layout::Union(tags) | Layout::RecursiveUnion(tags) => match tags { Layout::Union(variant) => {
use UnionLayout::*;
match variant {
Recursive(tags) | NonRecursive(tags) => match tags {
[] => todo!("how to handle empty tag unions?"), [] => todo!("how to handle empty tag unions?"),
[single] => match single.len() { [single] => match single.len() {
0 => Some(Wrapped::EmptyRecord), 0 => Some(Wrapped::EmptyRecord),
@ -829,7 +834,9 @@ impl Wrapped {
}, },
_ => Some(Wrapped::MultiTagUnion), _ => Some(Wrapped::MultiTagUnion),
}, },
Layout::NullableUnion { .. } => Some(Wrapped::MultiTagUnion), NullableWrapped { .. } => Some(Wrapped::MultiTagUnion),
}
}
_ => None, _ => None,
} }
} }
@ -2742,7 +2749,8 @@ pub fn with_hole<'a>(
layouts.push(arg_layouts); layouts.push(arg_layouts);
} }
let layout = Layout::RecursiveUnion(layouts.into_bump_slice()); let layout =
Layout::Union(UnionLayout::Recursive(layouts.into_bump_slice()));
let tag = Expr::Tag { let tag = Expr::Tag {
tag_layout: layout.clone(), tag_layout: layout.clone(),
@ -2775,7 +2783,8 @@ pub fn with_hole<'a>(
layouts.push(arg_layouts); layouts.push(arg_layouts);
} }
let layout = Layout::Union(layouts.into_bump_slice()); let layout =
Layout::Union(UnionLayout::NonRecursive(layouts.into_bump_slice()));
let tag = Expr::Tag { let tag = Expr::Tag {
tag_layout: layout.clone(), tag_layout: layout.clone(),
@ -2812,11 +2821,11 @@ pub fn with_hole<'a>(
layouts.push(arg_layouts); layouts.push(arg_layouts);
} }
let layout = Layout::NullableUnion { let layout = Layout::Union(UnionLayout::NullableWrapped {
nullable_id, nullable_id,
nullable_layout: TAG_SIZE, nullable_layout: TAG_SIZE,
foo: layouts.into_bump_slice(), other_tags: layouts.into_bump_slice(),
}; });
let tag = Expr::Tag { let tag = Expr::Tag {
tag_layout: layout.clone(), tag_layout: layout.clone(),
@ -5945,6 +5954,8 @@ fn from_can_pattern_help<'a>(
} }
Wrapped(variant) => { Wrapped(variant) => {
let (tag_id, argument_layouts) = variant.tag_name_to_id(tag_name); let (tag_id, argument_layouts) = variant.tag_name_to_id(tag_name);
let number_of_tags = variant.number_of_tags();
let mut ctors = std::vec::Vec::with_capacity(number_of_tags);
let arguments = { let arguments = {
let mut temp = arguments.clone(); let mut temp = arguments.clone();
@ -5971,7 +5982,6 @@ fn from_can_pattern_help<'a>(
} => { } => {
debug_assert!(tags.len() > 1); debug_assert!(tags.len() > 1);
let mut ctors = std::vec::Vec::with_capacity(tags.len());
for (i, (tag_name, args)) in tags.iter().enumerate() { for (i, (tag_name, args)) in tags.iter().enumerate() {
ctors.push(Ctor { ctors.push(Ctor {
tag_id: TagId(i as u8), tag_id: TagId(i as u8),
@ -6013,7 +6023,8 @@ fn from_can_pattern_help<'a>(
temp temp
}; };
let layout = Layout::Union(layouts.into_bump_slice()); let layout =
Layout::Union(UnionLayout::NonRecursive(layouts.into_bump_slice()));
Pattern::AppliedTag { Pattern::AppliedTag {
tag_name: tag_name.clone(), tag_name: tag_name.clone(),
@ -6029,7 +6040,6 @@ fn from_can_pattern_help<'a>(
} => { } => {
debug_assert!(tags.len() > 1); debug_assert!(tags.len() > 1);
let mut ctors = std::vec::Vec::with_capacity(tags.len());
for (i, (tag_name, args)) in tags.iter().enumerate() { for (i, (tag_name, args)) in tags.iter().enumerate() {
ctors.push(Ctor { ctors.push(Ctor {
tag_id: TagId(i as u8), tag_id: TagId(i as u8),
@ -6071,7 +6081,8 @@ fn from_can_pattern_help<'a>(
temp temp
}; };
let layout = Layout::RecursiveUnion(layouts.into_bump_slice()); let layout =
Layout::Union(UnionLayout::Recursive(layouts.into_bump_slice()));
Pattern::AppliedTag { Pattern::AppliedTag {
tag_name: tag_name.clone(), tag_name: tag_name.clone(),
@ -6089,8 +6100,6 @@ fn from_can_pattern_help<'a>(
} => { } => {
debug_assert!(!tags.is_empty()); debug_assert!(!tags.is_empty());
let mut ctors = std::vec::Vec::with_capacity(tags.len());
let mut i = 0; let mut i = 0;
for (tag_name, args) in tags.iter() { for (tag_name, args) in tags.iter() {
if i == nullable_id as usize { if i == nullable_id as usize {
@ -6158,11 +6167,11 @@ fn from_can_pattern_help<'a>(
temp temp
}; };
let layout = Layout::NullableUnion { let layout = Layout::Union(UnionLayout::NullableWrapped {
nullable_id, nullable_id,
nullable_layout: TAG_SIZE, nullable_layout: TAG_SIZE,
foo: layouts.into_bump_slice(), other_tags: layouts.into_bump_slice(),
}; });
Pattern::AppliedTag { Pattern::AppliedTag {
tag_name: tag_name.clone(), tag_name: tag_name.clone(),

View file

@ -29,13 +29,7 @@ pub enum Layout<'a> {
/// this is important for closures that capture zero-sized values /// this is important for closures that capture zero-sized values
PhantomEmptyStruct, PhantomEmptyStruct,
Struct(&'a [Layout<'a>]), Struct(&'a [Layout<'a>]),
Union(&'a [&'a [Layout<'a>]]), Union(UnionLayout<'a>),
RecursiveUnion(&'a [&'a [Layout<'a>]]),
NullableUnion {
nullable_id: i64,
nullable_layout: Builtin<'a>,
foo: &'a [&'a [Layout<'a>]],
},
RecursivePointer, RecursivePointer,
/// A function. The types of its arguments, then the type of its return value. /// A function. The types of its arguments, then the type of its return value.
FunctionPointer(&'a [Layout<'a>], &'a Layout<'a>), FunctionPointer(&'a [Layout<'a>], &'a Layout<'a>),
@ -43,6 +37,18 @@ pub enum Layout<'a> {
Pointer(&'a Layout<'a>), Pointer(&'a Layout<'a>),
} }
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum UnionLayout<'a> {
NonRecursive(&'a [&'a [Layout<'a>]]),
Recursive(&'a [&'a [Layout<'a>]]),
NullableWrapped {
nullable_id: i64,
nullable_layout: Builtin<'a>,
other_tags: &'a [&'a [Layout<'a>]],
},
// NullableUnwrapped,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ClosureLayout<'a> { pub struct ClosureLayout<'a> {
/// the layout that this specific closure captures /// the layout that this specific closure captures
@ -104,7 +110,9 @@ impl<'a> ClosureLayout<'a> {
ClosureLayout { ClosureLayout {
captured: tags, captured: tags,
layout: arena.alloc(Layout::Union(tag_arguments.into_bump_slice())), layout: arena.alloc(Layout::Union(UnionLayout::NonRecursive(
tag_arguments.into_bump_slice(),
))),
} }
} }
@ -273,7 +281,7 @@ impl<'a> ClosureLayout<'a> {
Ok(Expr::Struct(symbols)) Ok(Expr::Struct(symbols))
} }
Layout::Union(tags) => { Layout::Union(UnionLayout::NonRecursive(tags)) => {
// NOTE it's very important that this Union consists of Closure tags // NOTE it's very important that this Union consists of Closure tags
// and is not an unpacked 1-element record // and is not an unpacked 1-element record
@ -284,7 +292,7 @@ impl<'a> ClosureLayout<'a> {
.unwrap() as _; .unwrap() as _;
let expr = Expr::Tag { let expr = Expr::Tag {
tag_layout: Layout::Union(tags), tag_layout: Layout::Union(UnionLayout::NonRecursive(tags)),
tag_name: TagName::Closure(original), tag_name: TagName::Closure(original),
tag_id, tag_id,
union_size: tags.len() as u8, union_size: tags.len() as u8,
@ -459,17 +467,23 @@ impl<'a> Layout<'a> {
Struct(fields) => fields Struct(fields) => fields
.iter() .iter()
.all(|field_layout| field_layout.safe_to_memcpy()), .all(|field_layout| field_layout.safe_to_memcpy()),
Union(tags) => tags Union(variant) => {
use UnionLayout::*;
match variant {
NonRecursive(tags) => tags
.iter() .iter()
.all(|tag_layout| tag_layout.iter().all(|field| field.safe_to_memcpy())), .all(|tag_layout| tag_layout.iter().all(|field| field.safe_to_memcpy())),
RecursiveUnion(_) => { Recursive(_) => {
// a recursive union will always contain a pointer, and is thus not safe to memcpy // a recursive union will always contain a pointer, and is thus not safe to memcpy
false false
} }
NullableUnion { .. } => { NullableWrapped { .. } => {
// a nullable union will always contain a pointer, and is thus not safe to memcpy // a nullable union will always contain a pointer, and is thus not safe to memcpy
false false
} }
}
}
FunctionPointer(_, _) => { FunctionPointer(_, _) => {
// Function pointers are immutable and can always be safely copied // Function pointers are immutable and can always be safely copied
true true
@ -513,7 +527,11 @@ impl<'a> Layout<'a> {
sum sum
} }
Union(fields) => fields Union(variant) => {
use UnionLayout::*;
match variant {
NonRecursive(fields) => fields
.iter() .iter()
.map(|tag_layout| { .map(|tag_layout| {
tag_layout tag_layout
@ -523,7 +541,7 @@ impl<'a> Layout<'a> {
}) })
.max() .max()
.unwrap_or_default(), .unwrap_or_default(),
RecursiveUnion(fields) => fields Recursive(fields) => fields
.iter() .iter()
.map(|tag_layout| { .map(|tag_layout| {
tag_layout tag_layout
@ -533,7 +551,9 @@ impl<'a> Layout<'a> {
}) })
.max() .max()
.unwrap_or_default(), .unwrap_or_default(),
NullableUnion { foo: fields, .. } => fields NullableWrapped {
other_tags: fields, ..
} => fields
.iter() .iter()
.map(|tag_layout| { .map(|tag_layout| {
tag_layout tag_layout
@ -543,6 +563,8 @@ impl<'a> Layout<'a> {
}) })
.max() .max()
.unwrap_or_default(), .unwrap_or_default(),
}
}
Closure(_, closure_layout, _) => pointer_size + closure_layout.stack_size(pointer_size), Closure(_, closure_layout, _) => pointer_size + closure_layout.stack_size(pointer_size),
FunctionPointer(_, _) => pointer_size, FunctionPointer(_, _) => pointer_size,
RecursivePointer => pointer_size, RecursivePointer => pointer_size,
@ -557,20 +579,30 @@ impl<'a> Layout<'a> {
.map(|x| x.alignment_bytes(pointer_size)) .map(|x| x.alignment_bytes(pointer_size))
.max() .max()
.unwrap_or(0), .unwrap_or(0),
Layout::Union(tags) => tags
Layout::Union(variant) => {
use UnionLayout::*;
match variant {
NonRecursive(tags) => tags
.iter() .iter()
.map(|x| x.iter()) .map(|x| x.iter())
.flatten() .flatten()
.map(|x| x.alignment_bytes(pointer_size)) .map(|x| x.alignment_bytes(pointer_size))
.max() .max()
.unwrap_or(0), .unwrap_or(0),
Layout::RecursiveUnion(tags) | Layout::NullableUnion { foo: tags, .. } => tags Recursive(tags)
| NullableWrapped {
other_tags: tags, ..
} => tags
.iter() .iter()
.map(|x| x.iter()) .map(|x| x.iter())
.flatten() .flatten()
.map(|x| x.alignment_bytes(pointer_size)) .map(|x| x.alignment_bytes(pointer_size))
.max() .max()
.unwrap_or(pointer_size), .unwrap_or(pointer_size),
}
}
Layout::Builtin(builtin) => builtin.alignment_bytes(pointer_size), Layout::Builtin(builtin) => builtin.alignment_bytes(pointer_size),
Layout::PhantomEmptyStruct => 0, Layout::PhantomEmptyStruct => 0,
Layout::RecursivePointer => pointer_size, Layout::RecursivePointer => pointer_size,
@ -583,7 +615,22 @@ impl<'a> Layout<'a> {
} }
pub fn is_refcounted(&self) -> bool { pub fn is_refcounted(&self) -> bool {
matches!(self, Layout::Builtin(Builtin::List(MemoryMode::Refcounted, _)) | Layout::Builtin(Builtin::Str) | Layout::RecursiveUnion(_) | Layout::RecursivePointer | Layout::NullableUnion { .. }) use self::Builtin::*;
use Layout::*;
match self {
Union(variant) => {
use UnionLayout::*;
matches!(variant, Recursive(_)| NullableWrapped { .. } )
}
RecursivePointer => true,
Builtin(List(MemoryMode::Refcounted, _)) | Builtin(Str) => true,
_ => false,
}
} }
/// Even if a value (say, a record) is not itself reference counted, /// Even if a value (say, a record) is not itself reference counted,
@ -596,13 +643,19 @@ impl<'a> Layout<'a> {
Builtin(builtin) => builtin.is_refcounted(), Builtin(builtin) => builtin.is_refcounted(),
PhantomEmptyStruct => false, PhantomEmptyStruct => false,
Struct(fields) => fields.iter().any(|f| f.contains_refcounted()), Struct(fields) => fields.iter().any(|f| f.contains_refcounted()),
Union(fields) => fields Union(variant) => {
use UnionLayout::*;
match variant {
NonRecursive(fields) => fields
.iter() .iter()
.map(|ls| ls.iter()) .map(|ls| ls.iter())
.flatten() .flatten()
.any(|f| f.contains_refcounted()), .any(|f| f.contains_refcounted()),
RecursiveUnion(_) => true, Recursive(_) => true,
NullableUnion { .. } => true, NullableWrapped { .. } => true,
}
}
Closure(_, closure_layout, _) => closure_layout.contains_refcounted(), Closure(_, closure_layout, _) => closure_layout.contains_refcounted(),
FunctionPointer(_, _) | RecursivePointer | Pointer(_) => false, FunctionPointer(_, _) | RecursivePointer | Pointer(_) => false,
} }
@ -1067,15 +1120,17 @@ fn layout_from_flat_type<'a>(
tag_layouts.push(tag_layout.into_bump_slice()); tag_layouts.push(tag_layout.into_bump_slice());
} }
if let Some((tag_id, tag_id_layout)) = nullable { let union_layout = if let Some((tag_id, tag_id_layout)) = nullable {
Ok(Layout::NullableUnion { UnionLayout::NullableWrapped {
nullable_id: tag_id, nullable_id: tag_id,
nullable_layout: tag_id_layout, nullable_layout: tag_id_layout,
foo: tag_layouts.into_bump_slice(), other_tags: tag_layouts.into_bump_slice(),
})
} else {
Ok(Layout::RecursiveUnion(tag_layouts.into_bump_slice()))
} }
} else {
UnionLayout::Recursive(tag_layouts.into_bump_slice())
};
Ok(Layout::Union(union_layout))
} }
EmptyTagUnion => { EmptyTagUnion => {
panic!("TODO make Layout for empty Tag Union"); panic!("TODO make Layout for empty Tag Union");
@ -1500,7 +1555,7 @@ pub fn layout_from_tag_union<'a>(
let variant = union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs); let variant = union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs);
match variant { match variant {
Never => Layout::Union(&[]), Never => Layout::Union(UnionLayout::NonRecursive(&[])),
Unit | UnitWithArguments => Layout::Struct(&[]), Unit | UnitWithArguments => Layout::Struct(&[]),
BoolUnion { .. } => Layout::Builtin(Builtin::Int1), BoolUnion { .. } => Layout::Builtin(Builtin::Int1),
ByteUnion(_) => Layout::Builtin(Builtin::Int8), ByteUnion(_) => Layout::Builtin(Builtin::Int8),
@ -1521,7 +1576,7 @@ pub fn layout_from_tag_union<'a>(
let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena); let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena);
tag_layouts.extend(tags.iter().map(|r| r.1)); tag_layouts.extend(tags.iter().map(|r| r.1));
Layout::Union(tag_layouts.into_bump_slice()) Layout::Union(UnionLayout::NonRecursive(tag_layouts.into_bump_slice()))
} }
Recursive { Recursive {
@ -1530,7 +1585,7 @@ pub fn layout_from_tag_union<'a>(
let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena); let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena);
tag_layouts.extend(tags.iter().map(|r| r.1)); tag_layouts.extend(tags.iter().map(|r| r.1));
Layout::RecursiveUnion(tag_layouts.into_bump_slice()) Layout::Union(UnionLayout::Recursive(tag_layouts.into_bump_slice()))
} }
NullableWrapped { NullableWrapped {
@ -1541,11 +1596,11 @@ pub fn layout_from_tag_union<'a>(
let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena); let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena);
tag_layouts.extend(tags.iter().map(|r| r.1)); tag_layouts.extend(tags.iter().map(|r| r.1));
Layout::NullableUnion { Layout::Union(UnionLayout::NullableWrapped {
nullable_id, nullable_id,
nullable_layout: TAG_SIZE, nullable_layout: TAG_SIZE,
foo: tag_layouts.into_bump_slice(), other_tags: tag_layouts.into_bump_slice(),
} })
} }
NullableUnwrapped { .. } => todo!(), NullableUnwrapped { .. } => todo!(),