mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 16:21:11 +00:00
recursive tag unions
This commit is contained in:
parent
ba186bfe09
commit
f9cf4ea371
8 changed files with 286 additions and 75 deletions
|
@ -7,7 +7,7 @@ use crate::llvm::build_list::{
|
|||
};
|
||||
use crate::llvm::compare::{build_eq, build_neq};
|
||||
use crate::llvm::convert::{
|
||||
basic_type_from_layout, collection, get_fn_type, get_ptr_type, ptr_int,
|
||||
basic_type_from_layout, block_of_memory, collection, get_fn_type, get_ptr_type, ptr_int,
|
||||
};
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
|
@ -478,7 +478,6 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
debug_assert!(*union_size > 1);
|
||||
let ptr_size = env.ptr_bytes;
|
||||
|
||||
dbg!(&tag_layout);
|
||||
let mut filler = tag_layout.stack_size(ptr_size);
|
||||
|
||||
let ctx = env.context;
|
||||
|
@ -513,7 +512,6 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
ptr,
|
||||
ctx.i64_type().ptr_type(AddressSpace::Generic).into(),
|
||||
);
|
||||
dbg!(&ptr);
|
||||
field_vals.push(ptr);
|
||||
} else {
|
||||
field_vals.push(val);
|
||||
|
@ -638,14 +636,15 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
.expect("desired field did not decode");
|
||||
|
||||
if let Some(Layout::RecursivePointer) = field_layouts.get(*index as usize) {
|
||||
let struct_layout = Layout::Struct(field_layouts);
|
||||
let desired_type = block_of_memory(env.context, &struct_layout, env.ptr_bytes);
|
||||
|
||||
// the value is a pointer to the actual value; load that value!
|
||||
use inkwell::types::BasicType;
|
||||
let ptr = cast_basic_basic(
|
||||
builder,
|
||||
result,
|
||||
struct_value
|
||||
.get_type()
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.into(),
|
||||
desired_type.ptr_type(AddressSpace::Generic).into(),
|
||||
);
|
||||
builder.build_load(ptr.into_pointer_value(), "load_recursive_field")
|
||||
} else {
|
||||
|
@ -819,7 +818,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
|||
Expr::AccessAtIndex { field_layouts, .. } => {
|
||||
let layout = Layout::Struct(field_layouts);
|
||||
|
||||
basic_type_from_layout(env.arena, context, &layout, env.ptr_bytes)
|
||||
block_of_memory(env.context, &layout, env.ptr_bytes)
|
||||
}
|
||||
_ => unreachable!(
|
||||
"a recursive pointer can only be loaded from a recursive tag union"
|
||||
|
@ -1115,7 +1114,9 @@ fn decrement_refcount_layout<'a, 'ctx, 'env>(
|
|||
}
|
||||
}
|
||||
}
|
||||
RecursiveUnion(_) => todo!("TODO implement decrement layout of recursive tag union"),
|
||||
RecursiveUnion(_) => {
|
||||
println!("TODO implement decrement layout of recursive tag union");
|
||||
}
|
||||
RecursivePointer => todo!("TODO implement decrement layout of recursive tag union"),
|
||||
Union(tags) => {
|
||||
debug_assert!(!tags.is_empty());
|
||||
|
|
|
@ -107,37 +107,7 @@ pub fn basic_type_from_layout<'ctx>(
|
|||
.struct_type(field_types.into_bump_slice(), false)
|
||||
.as_basic_type_enum()
|
||||
}
|
||||
RecursiveUnion(_) | Union(_) => {
|
||||
// TODO make this dynamic
|
||||
let ptr_size = std::mem::size_of::<i64>();
|
||||
let union_size = layout.stack_size(ptr_size as u32);
|
||||
|
||||
// The memory layout of Union is a bit tricky.
|
||||
// We have tags with different memory layouts, that are part of the same type.
|
||||
// For llvm, all tags must have the same memory layout.
|
||||
//
|
||||
// So, we convert all tags to a layout of bytes of some size.
|
||||
// It turns out that encoding to i64 for as many elements as possible is
|
||||
// a nice optimization, the remainder is encoded as bytes.
|
||||
|
||||
let num_i64 = union_size / 8;
|
||||
let num_i8 = union_size % 8;
|
||||
|
||||
let i64_array_type = context.i64_type().array_type(num_i64).as_basic_type_enum();
|
||||
|
||||
if num_i8 == 0 {
|
||||
// the object fits perfectly in some number of i64's
|
||||
// (i.e. the size is a multiple of 8 bytes)
|
||||
context.struct_type(&[i64_array_type], false).into()
|
||||
} else {
|
||||
// there are some trailing bytes at the end
|
||||
let i8_array_type = context.i8_type().array_type(num_i8).as_basic_type_enum();
|
||||
|
||||
context
|
||||
.struct_type(&[i64_array_type, i8_array_type], false)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
RecursiveUnion(_) | Union(_) => block_of_memory(context, layout, ptr_bytes),
|
||||
RecursivePointer => {
|
||||
// TODO make this dynamic
|
||||
context
|
||||
|
@ -165,6 +135,41 @@ pub fn basic_type_from_layout<'ctx>(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn block_of_memory<'ctx>(
|
||||
context: &'ctx Context,
|
||||
layout: &Layout<'_>,
|
||||
ptr_bytes: u32,
|
||||
) -> BasicTypeEnum<'ctx> {
|
||||
// TODO make this dynamic
|
||||
let union_size = layout.stack_size(ptr_bytes as u32);
|
||||
|
||||
// The memory layout of Union is a bit tricky.
|
||||
// We have tags with different memory layouts, that are part of the same type.
|
||||
// For llvm, all tags must have the same memory layout.
|
||||
//
|
||||
// So, we convert all tags to a layout of bytes of some size.
|
||||
// It turns out that encoding to i64 for as many elements as possible is
|
||||
// a nice optimization, the remainder is encoded as bytes.
|
||||
|
||||
let num_i64 = union_size / 8;
|
||||
let num_i8 = union_size % 8;
|
||||
|
||||
let i64_array_type = context.i64_type().array_type(num_i64).as_basic_type_enum();
|
||||
|
||||
if num_i8 == 0 {
|
||||
// the object fits perfectly in some number of i64's
|
||||
// (i.e. the size is a multiple of 8 bytes)
|
||||
context.struct_type(&[i64_array_type], false).into()
|
||||
} else {
|
||||
// there are some trailing bytes at the end
|
||||
let i8_array_type = context.i8_type().array_type(num_i8).as_basic_type_enum();
|
||||
|
||||
context
|
||||
.struct_type(&[i64_array_type, i8_array_type], false)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Two usize values. Could be a wrapper for a List or a Str.
|
||||
///
|
||||
/// It would be nicer if we could store this as a tuple containing one usize
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue