rework how tags are created

This commit is contained in:
Folkert 2022-03-26 13:43:41 +01:00
parent 269d31b9ae
commit 84c6e741fe
No known key found for this signature in database
GPG Key ID: 1F17F6FFD112B97C

View File

@ -1012,7 +1012,7 @@ fn struct_pointer_from_fields<'a, 'ctx, 'env, I>(
input_pointer: PointerValue<'ctx>,
values: I,
) where
I: Iterator<Item = (usize, BasicValueEnum<'ctx>)>,
I: Iterator<Item = (usize, (Layout<'a>, BasicValueEnum<'ctx>))>,
{
let struct_ptr = env
.builder
@ -1024,13 +1024,13 @@ fn struct_pointer_from_fields<'a, 'ctx, 'env, I>(
.into_pointer_value();
// Insert field exprs into struct_val
for (index, field_val) in values {
for (index, (field_layout, field_value)) in values {
let field_ptr = env
.builder
.build_struct_gep(struct_ptr, index as u32, "field_struct_gep")
.unwrap();
env.builder.build_store(field_ptr, field_val);
store_roc_value(env, field_layout, field_ptr, field_value);
}
}
@ -1405,53 +1405,11 @@ fn build_wrapped_tag<'a, 'ctx, 'env>(
reuse_allocation: Option<PointerValue<'ctx>>,
parent: FunctionValue<'ctx>,
) -> BasicValueEnum<'ctx> {
let ctx = env.context;
let builder = env.builder;
let tag_id_layout = union_layout.tag_id_layout();
// Determine types
let num_fields = arguments.len() + 1;
let mut field_types = Vec::with_capacity_in(num_fields, env.arena);
let mut field_vals = Vec::with_capacity_in(num_fields, env.arena);
for (field_symbol, tag_field_layout) in arguments.iter().zip(tag_field_layouts.iter()) {
let (val, _val_layout) = load_symbol_and_layout(scope, field_symbol);
let field_type = basic_type_from_layout(env, tag_field_layout);
field_types.push(field_type);
if let Layout::RecursivePointer = tag_field_layout {
debug_assert!(val.is_pointer_value());
// we store recursive pointers as `i64*`
let ptr = env.builder.build_bitcast(
val,
ctx.i64_type().ptr_type(AddressSpace::Generic),
"cast_recursive_pointer",
);
field_vals.push(ptr);
} else if matches!(
tag_field_layout,
Layout::Union(UnionLayout::NonRecursive(_))
) {
debug_assert!(val.is_pointer_value());
// We store non-recursive unions without any indirection.
let reified = env
.builder
.build_load(val.into_pointer_value(), "load_non_recursive");
field_vals.push(reified);
} else {
// this check fails for recursive tag unions, but can be helpful while debugging
// debug_assert_eq!(tag_field_layout, val_layout);
field_vals.push(val);
}
}
let (field_types, field_values) = build_tag_fields(env, scope, tag_field_layouts, arguments);
// Create the struct_type
let raw_data_ptr = allocate_tag(env, parent, reuse_allocation, union_layout, tags);
@ -1475,7 +1433,7 @@ fn build_wrapped_tag<'a, 'ctx, 'env>(
env,
struct_type,
opaque_struct_ptr,
field_vals.into_iter().enumerate(),
field_values.into_iter().enumerate(),
);
raw_data_ptr.into()
@ -1484,7 +1442,7 @@ fn build_wrapped_tag<'a, 'ctx, 'env>(
env,
struct_type,
raw_data_ptr,
field_vals.into_iter().enumerate(),
field_values.into_iter().enumerate(),
);
tag_pointer_set_tag_id(env, tag_id, raw_data_ptr).into()
@ -1527,7 +1485,63 @@ pub fn entry_block_alloca_zerofill<'a, 'ctx, 'env>(
result_alloca
}
pub fn build_tag<'a, 'ctx, 'env>(
fn build_tag_field_value<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
value: BasicValueEnum<'ctx>,
tag_field_layout: Layout<'a>,
) -> BasicValueEnum<'ctx> {
if let Layout::RecursivePointer = tag_field_layout {
debug_assert!(value.is_pointer_value());
// we store recursive pointers as `i64*`
env.builder.build_bitcast(
value,
env.context.i64_type().ptr_type(AddressSpace::Generic),
"cast_recursive_pointer",
)
} else if tag_field_layout.is_passed_by_reference() {
debug_assert!(value.is_pointer_value());
// NOTE: we rely on this being passed to `store_roc_value` so that
// the value is memcpy'd
value
} else {
// this check fails for recursive tag unions, but can be helpful while debugging
// debug_assert_eq!(tag_field_layout, val_layout);
value
}
}
fn build_tag_fields<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
scope: &Scope<'a, 'ctx>,
fields: &[Layout<'a>],
arguments: &[Symbol],
) -> (
Vec<'a, BasicTypeEnum<'ctx>>,
Vec<'a, (Layout<'a>, BasicValueEnum<'ctx>)>,
) {
debug_assert_eq!(fields.len(), arguments.len());
let capacity = fields.len();
let mut field_types = Vec::with_capacity_in(capacity, env.arena);
let mut field_values = Vec::with_capacity_in(capacity, env.arena);
for (field_symbol, tag_field_layout) in arguments.iter().zip(fields.iter()) {
let field_type = basic_type_from_layout(env, tag_field_layout);
field_types.push(field_type);
let raw_value = load_symbol(scope, field_symbol);
let field_value = build_tag_field_value(env, raw_value, *tag_field_layout);
field_values.push((*tag_field_layout, field_value));
}
(field_types, field_values)
}
fn build_tag<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
scope: &Scope<'a, 'ctx>,
union_layout: &UnionLayout<'a>,
@ -1678,49 +1692,21 @@ pub fn build_tag<'a, 'ctx, 'env>(
debug_assert_eq!(tag_id, 0);
debug_assert_eq!(arguments.len(), fields.len());
let ctx = env.context;
// Determine types
let num_fields = arguments.len() + 1;
let mut field_types = Vec::with_capacity_in(num_fields, env.arena);
let mut field_vals = Vec::with_capacity_in(num_fields, env.arena);
for (field_symbol, tag_field_layout) in arguments.iter().zip(fields.iter()) {
let val = load_symbol(scope, field_symbol);
let field_type = basic_type_from_layout(env, tag_field_layout);
field_types.push(field_type);
if let Layout::RecursivePointer = tag_field_layout {
debug_assert!(val.is_pointer_value());
// we store recursive pointers as `i64*`
let ptr = env.builder.build_bitcast(
val,
ctx.i64_type().ptr_type(AddressSpace::Generic),
"cast_recursive_pointer",
);
field_vals.push(ptr);
} else {
// this check fails for recursive tag unions, but can be helpful while debugging
field_vals.push(val);
}
}
let (field_types, field_values) = build_tag_fields(env, scope, fields, arguments);
// Create the struct_type
let data_ptr =
reserve_with_refcount_union_as_block_of_memory(env, *union_layout, &[fields]);
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
let struct_type = env
.context
.struct_type(field_types.into_bump_slice(), false);
struct_pointer_from_fields(
env,
struct_type,
data_ptr,
field_vals.into_iter().enumerate(),
field_values.into_iter().enumerate(),
);
data_ptr.into()
@ -1744,56 +1730,22 @@ pub fn build_tag<'a, 'ctx, 'env>(
debug_assert!(union_size == 2);
let ctx = env.context;
// Determine types
let num_fields = arguments.len() + 1;
let mut field_types = Vec::with_capacity_in(num_fields, env.arena);
let mut field_vals = Vec::with_capacity_in(num_fields, env.arena);
debug_assert_eq!(arguments.len(), other_fields.len());
for (field_symbol, tag_field_layout) in arguments.iter().zip(other_fields.iter()) {
let val = load_symbol(scope, field_symbol);
// Zero-sized fields have no runtime representation.
// The layout of the struct expects them to be dropped!
if !tag_field_layout.is_dropped_because_empty() {
let field_type = basic_type_from_layout(env, tag_field_layout);
field_types.push(field_type);
if let Layout::RecursivePointer = tag_field_layout {
debug_assert!(val.is_pointer_value());
// we store recursive pointers as `i64*`
let ptr = env.builder.build_bitcast(
val,
ctx.i64_type().ptr_type(AddressSpace::Generic),
"cast_recursive_pointer",
);
field_vals.push(ptr);
} else {
// this check fails for recursive tag unions, but can be helpful while debugging
// debug_assert_eq!(tag_field_layout, val_layout);
field_vals.push(val);
}
}
}
let (field_types, field_values) = build_tag_fields(env, scope, other_fields, arguments);
// Create the struct_type
let data_ptr =
allocate_tag(env, parent, reuse_allocation, union_layout, &[other_fields]);
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
let struct_type = env
.context
.struct_type(field_types.into_bump_slice(), false);
struct_pointer_from_fields(
env,
struct_type,
data_ptr,
field_vals.into_iter().enumerate(),
field_values.into_iter().enumerate(),
);
data_ptr.into()
@ -2491,6 +2443,8 @@ pub fn store_roc_value<'a, 'ctx, 'env>(
value: BasicValueEnum<'ctx>,
) {
if layout.is_passed_by_reference() {
debug_assert!(value.is_pointer_value());
let align_bytes = layout.alignment_bytes(env.target_info);
if align_bytes > 0 {