mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 06:44:46 +00:00
Merge remote-tracking branch 'origin/trunk' into add-dec-types
This commit is contained in:
commit
cb42f0c039
16 changed files with 1471 additions and 443 deletions
|
@ -22,7 +22,7 @@ use crate::llvm::convert::{
|
|||
basic_type_from_builtin, basic_type_from_layout, block_of_memory_slices, ptr_int,
|
||||
};
|
||||
use crate::llvm::refcounting::{
|
||||
decrement_refcount_layout, increment_refcount_layout, PointerToRefcount,
|
||||
build_reset, decrement_refcount_layout, increment_refcount_layout, PointerToRefcount,
|
||||
};
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
|
@ -887,7 +887,7 @@ pub fn build_exp_call<'a, 'ctx, 'env>(
|
|||
}
|
||||
}
|
||||
|
||||
pub const TAG_ID_INDEX: u32 = 1;
|
||||
const TAG_ID_INDEX: u32 = 1;
|
||||
pub const TAG_DATA_INDEX: u32 = 0;
|
||||
|
||||
pub fn struct_from_fields<'a, 'ctx, 'env, I>(
|
||||
|
@ -913,6 +913,34 @@ where
|
|||
struct_value.into_struct_value()
|
||||
}
|
||||
|
||||
fn struct_pointer_from_fields<'a, 'ctx, 'env, I>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
struct_type: StructType<'ctx>,
|
||||
input_pointer: PointerValue<'ctx>,
|
||||
values: I,
|
||||
) where
|
||||
I: Iterator<Item = (usize, BasicValueEnum<'ctx>)>,
|
||||
{
|
||||
let struct_ptr = env
|
||||
.builder
|
||||
.build_bitcast(
|
||||
input_pointer,
|
||||
struct_type.ptr_type(AddressSpace::Generic),
|
||||
"struct_ptr",
|
||||
)
|
||||
.into_pointer_value();
|
||||
|
||||
// Insert field exprs into struct_val
|
||||
for (index, field_val) 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);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
|
@ -963,16 +991,87 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
struct_from_fields(env, struct_type, field_vals.into_iter().enumerate()).into()
|
||||
}
|
||||
|
||||
Reuse {
|
||||
arguments,
|
||||
tag_layout: union_layout,
|
||||
tag_id,
|
||||
symbol,
|
||||
..
|
||||
} => {
|
||||
let reset = load_symbol(scope, symbol).into_pointer_value();
|
||||
build_tag(
|
||||
env,
|
||||
scope,
|
||||
union_layout,
|
||||
*tag_id,
|
||||
arguments,
|
||||
Some(reset),
|
||||
parent,
|
||||
)
|
||||
}
|
||||
|
||||
Tag {
|
||||
arguments,
|
||||
tag_layout: union_layout,
|
||||
union_size,
|
||||
tag_id,
|
||||
..
|
||||
} => build_tag(env, scope, union_layout, *union_size, *tag_id, arguments),
|
||||
} => build_tag(env, scope, union_layout, *tag_id, arguments, None, parent),
|
||||
|
||||
Reset(_) => todo!(),
|
||||
Reuse { .. } => todo!(),
|
||||
Reset(symbol) => {
|
||||
let (tag_ptr, layout) = load_symbol_and_layout(scope, symbol);
|
||||
let tag_ptr = tag_ptr.into_pointer_value();
|
||||
|
||||
// reset is only generated for union values
|
||||
let union_layout = match layout {
|
||||
Layout::Union(ul) => ul,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let ctx = env.context;
|
||||
let then_block = ctx.append_basic_block(parent, "then_reset");
|
||||
let else_block = ctx.append_basic_block(parent, "else_decref");
|
||||
let cont_block = ctx.append_basic_block(parent, "cont");
|
||||
|
||||
let refcount_ptr =
|
||||
PointerToRefcount::from_ptr_to_data(env, tag_pointer_clear_tag_id(env, tag_ptr));
|
||||
let is_unique = refcount_ptr.is_1(env);
|
||||
|
||||
env.builder
|
||||
.build_conditional_branch(is_unique, then_block, else_block);
|
||||
|
||||
{
|
||||
// reset, when used on a unique reference, eagerly decrements the components of the
|
||||
// referenced value, and returns the location of the now-invalid cell
|
||||
env.builder.position_at_end(then_block);
|
||||
|
||||
let reset_function = build_reset(env, layout_ids, *union_layout);
|
||||
let call = env
|
||||
.builder
|
||||
.build_call(reset_function, &[tag_ptr.into()], "call_reset");
|
||||
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
|
||||
let _ = call.try_as_basic_value();
|
||||
|
||||
env.builder.build_unconditional_branch(cont_block);
|
||||
}
|
||||
{
|
||||
// If reset is used on a shared, non-reusable reference, it behaves
|
||||
// like dec and returns NULL, which instructs reuse to behave like ctor
|
||||
env.builder.position_at_end(else_block);
|
||||
refcount_ptr.decrement(env, layout);
|
||||
env.builder.build_unconditional_branch(cont_block);
|
||||
}
|
||||
{
|
||||
env.builder.position_at_end(cont_block);
|
||||
let phi = env.builder.build_phi(tag_ptr.get_type(), "branch");
|
||||
|
||||
let null_ptr = tag_ptr.get_type().const_null();
|
||||
phi.add_incoming(&[(&tag_ptr, then_block), (&null_ptr, else_block)]);
|
||||
|
||||
phi.as_basic_value()
|
||||
}
|
||||
}
|
||||
|
||||
StructAtIndex {
|
||||
index, structure, ..
|
||||
|
@ -1078,13 +1177,15 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
let tag_id_type =
|
||||
basic_type_from_layout(env, &union_layout.tag_id_layout()).into_int_type();
|
||||
|
||||
let ptr = tag_pointer_clear_tag_id(env, argument.into_pointer_value());
|
||||
|
||||
lookup_at_index_ptr2(
|
||||
env,
|
||||
union_layout,
|
||||
tag_id_type,
|
||||
field_layouts,
|
||||
*index as usize,
|
||||
argument.into_pointer_value(),
|
||||
ptr,
|
||||
)
|
||||
}
|
||||
UnionLayout::NonNullableUnwrapped(field_layouts) => {
|
||||
|
@ -1119,13 +1220,14 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
let tag_id_type =
|
||||
basic_type_from_layout(env, &union_layout.tag_id_layout()).into_int_type();
|
||||
|
||||
let ptr = tag_pointer_clear_tag_id(env, argument.into_pointer_value());
|
||||
lookup_at_index_ptr2(
|
||||
env,
|
||||
union_layout,
|
||||
tag_id_type,
|
||||
field_layouts,
|
||||
*index as usize,
|
||||
argument.into_pointer_value(),
|
||||
ptr,
|
||||
)
|
||||
}
|
||||
UnionLayout::NullableUnwrapped {
|
||||
|
@ -1165,15 +1267,103 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn build_wrapped_tag<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
union_layout: &UnionLayout<'a>,
|
||||
tag_id: u8,
|
||||
arguments: &[Symbol],
|
||||
tag_field_layouts: &[Layout<'a>],
|
||||
tags: &[&[Layout<'a>]],
|
||||
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 {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the struct_type
|
||||
let raw_data_ptr = allocate_tag(env, parent, reuse_allocation, union_layout, tags);
|
||||
let struct_type = env.context.struct_type(&field_types, false);
|
||||
|
||||
if union_layout.stores_tag_id_as_data(env.ptr_bytes) {
|
||||
let tag_id_ptr = builder
|
||||
.build_struct_gep(raw_data_ptr, TAG_ID_INDEX, "tag_id_index")
|
||||
.unwrap();
|
||||
|
||||
let tag_id_type = basic_type_from_layout(env, &tag_id_layout).into_int_type();
|
||||
|
||||
env.builder
|
||||
.build_store(tag_id_ptr, tag_id_type.const_int(tag_id as u64, false));
|
||||
|
||||
let opaque_struct_ptr = builder
|
||||
.build_struct_gep(raw_data_ptr, TAG_DATA_INDEX, "tag_data_index")
|
||||
.unwrap();
|
||||
|
||||
struct_pointer_from_fields(
|
||||
env,
|
||||
struct_type,
|
||||
opaque_struct_ptr,
|
||||
field_vals.into_iter().enumerate(),
|
||||
);
|
||||
|
||||
raw_data_ptr.into()
|
||||
} else {
|
||||
struct_pointer_from_fields(
|
||||
env,
|
||||
struct_type,
|
||||
raw_data_ptr,
|
||||
field_vals.into_iter().enumerate(),
|
||||
);
|
||||
|
||||
tag_pointer_set_tag_id(env, tag_id, raw_data_ptr).into()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_tag<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
union_layout: &UnionLayout<'a>,
|
||||
union_size: u8,
|
||||
tag_id: u8,
|
||||
arguments: &[Symbol],
|
||||
reuse_allocation: Option<PointerValue<'ctx>>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let tag_id_layout = union_layout.tag_id_layout();
|
||||
let union_size = union_layout.number_of_tags();
|
||||
|
||||
match union_layout {
|
||||
UnionLayout::NonRecursive(tags) => {
|
||||
|
@ -1263,79 +1453,51 @@ pub fn build_tag<'a, 'ctx, 'env>(
|
|||
UnionLayout::Recursive(tags) => {
|
||||
debug_assert!(union_size > 1);
|
||||
|
||||
let ctx = env.context;
|
||||
let builder = env.builder;
|
||||
|
||||
// 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);
|
||||
|
||||
let tag_field_layouts = &tags[tag_id as usize];
|
||||
|
||||
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);
|
||||
build_wrapped_tag(
|
||||
env,
|
||||
scope,
|
||||
union_layout,
|
||||
tag_id,
|
||||
arguments,
|
||||
&tag_field_layouts,
|
||||
tags,
|
||||
reuse_allocation,
|
||||
parent,
|
||||
)
|
||||
}
|
||||
UnionLayout::NullableWrapped {
|
||||
nullable_id,
|
||||
other_tags: tags,
|
||||
} => {
|
||||
let tag_field_layouts = {
|
||||
use std::cmp::Ordering::*;
|
||||
match tag_id.cmp(&(*nullable_id as u8)) {
|
||||
Equal => {
|
||||
let layout = Layout::Union(*union_layout);
|
||||
|
||||
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);
|
||||
return basic_type_from_layout(env, &layout)
|
||||
.into_pointer_type()
|
||||
.const_null()
|
||||
.into();
|
||||
}
|
||||
Less => &tags[tag_id as usize],
|
||||
Greater => &tags[tag_id as usize - 1],
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Create the struct_type
|
||||
let raw_data_ptr =
|
||||
reserve_with_refcount_union_as_block_of_memory(env, *union_layout, tags);
|
||||
|
||||
let tag_id_ptr = builder
|
||||
.build_struct_gep(raw_data_ptr, TAG_ID_INDEX, "tag_id_index")
|
||||
.unwrap();
|
||||
|
||||
let tag_id_type = basic_type_from_layout(env, &tag_id_layout).into_int_type();
|
||||
|
||||
env.builder
|
||||
.build_store(tag_id_ptr, tag_id_type.const_int(tag_id as u64, false));
|
||||
|
||||
let opaque_struct_ptr = builder
|
||||
.build_struct_gep(raw_data_ptr, TAG_DATA_INDEX, "tag_data_index")
|
||||
.unwrap();
|
||||
|
||||
let struct_type = env.context.struct_type(&field_types, false);
|
||||
let struct_ptr = env
|
||||
.builder
|
||||
.build_bitcast(
|
||||
opaque_struct_ptr,
|
||||
struct_type.ptr_type(AddressSpace::Generic),
|
||||
"struct_ptr",
|
||||
)
|
||||
.into_pointer_value();
|
||||
|
||||
// Insert field exprs into struct_val
|
||||
for (index, field_val) in field_vals.into_iter().enumerate() {
|
||||
let field_ptr = builder
|
||||
.build_struct_gep(struct_ptr, index as u32, "field_struct_gep")
|
||||
.unwrap();
|
||||
|
||||
builder.build_store(field_ptr, field_val);
|
||||
}
|
||||
|
||||
raw_data_ptr.into()
|
||||
build_wrapped_tag(
|
||||
env,
|
||||
scope,
|
||||
union_layout,
|
||||
tag_id,
|
||||
arguments,
|
||||
&tag_field_layouts,
|
||||
tags,
|
||||
reuse_allocation,
|
||||
parent,
|
||||
)
|
||||
}
|
||||
UnionLayout::NonNullableUnwrapped(fields) => {
|
||||
debug_assert_eq!(union_size, 1);
|
||||
|
@ -1343,7 +1505,6 @@ pub fn build_tag<'a, 'ctx, 'env>(
|
|||
debug_assert_eq!(arguments.len(), fields.len());
|
||||
|
||||
let ctx = env.context;
|
||||
let builder = env.builder;
|
||||
|
||||
// Determine types
|
||||
let num_fields = arguments.len() + 1;
|
||||
|
@ -1380,126 +1541,16 @@ pub fn build_tag<'a, 'ctx, 'env>(
|
|||
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_ptr = env
|
||||
.builder
|
||||
.build_bitcast(
|
||||
data_ptr,
|
||||
struct_type.ptr_type(AddressSpace::Generic),
|
||||
"block_of_memory_to_tag",
|
||||
)
|
||||
.into_pointer_value();
|
||||
|
||||
// Insert field exprs into struct_val
|
||||
for (index, field_val) in field_vals.into_iter().enumerate() {
|
||||
let field_ptr = builder
|
||||
.build_struct_gep(struct_ptr, index as u32, "struct_gep")
|
||||
.unwrap();
|
||||
|
||||
builder.build_store(field_ptr, field_val);
|
||||
}
|
||||
struct_pointer_from_fields(
|
||||
env,
|
||||
struct_type,
|
||||
data_ptr,
|
||||
field_vals.into_iter().enumerate(),
|
||||
);
|
||||
|
||||
data_ptr.into()
|
||||
}
|
||||
UnionLayout::NullableWrapped {
|
||||
nullable_id,
|
||||
other_tags: tags,
|
||||
} => {
|
||||
if tag_id == *nullable_id as u8 {
|
||||
let layout = Layout::Union(*union_layout);
|
||||
|
||||
return basic_type_from_layout(env, &layout)
|
||||
.into_pointer_type()
|
||||
.const_null()
|
||||
.into();
|
||||
}
|
||||
|
||||
debug_assert!(union_size > 1);
|
||||
|
||||
let ctx = env.context;
|
||||
let builder = env.builder;
|
||||
|
||||
// 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);
|
||||
|
||||
let tag_field_layouts = {
|
||||
use std::cmp::Ordering::*;
|
||||
match tag_id.cmp(&(*nullable_id as u8)) {
|
||||
Equal => unreachable!("early return above"),
|
||||
Less => &tags[tag_id as usize],
|
||||
Greater => &tags[tag_id as usize - 1],
|
||||
}
|
||||
};
|
||||
|
||||
for (field_symbol, tag_field_layout) in arguments.iter().zip(tag_field_layouts.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the struct_type
|
||||
let raw_data_ptr =
|
||||
reserve_with_refcount_union_as_block_of_memory(env, *union_layout, tags);
|
||||
|
||||
let tag_id_ptr = builder
|
||||
.build_struct_gep(raw_data_ptr, TAG_ID_INDEX, "tag_id_index")
|
||||
.unwrap();
|
||||
|
||||
let tag_id_type = basic_type_from_layout(env, &tag_id_layout).into_int_type();
|
||||
|
||||
env.builder
|
||||
.build_store(tag_id_ptr, tag_id_type.const_int(tag_id as u64, false));
|
||||
|
||||
let opaque_struct_ptr = builder
|
||||
.build_struct_gep(raw_data_ptr, TAG_DATA_INDEX, "tag_data_index")
|
||||
.unwrap();
|
||||
|
||||
let struct_type = env.context.struct_type(&field_types, false);
|
||||
let struct_ptr = env
|
||||
.builder
|
||||
.build_bitcast(
|
||||
opaque_struct_ptr,
|
||||
struct_type.ptr_type(AddressSpace::Generic),
|
||||
"struct_ptr",
|
||||
)
|
||||
.into_pointer_value();
|
||||
|
||||
// Insert field exprs into struct_val
|
||||
for (index, field_val) in field_vals.into_iter().enumerate() {
|
||||
let field_ptr = builder
|
||||
.build_struct_gep(struct_ptr, index as u32, "field_struct_gep")
|
||||
.unwrap();
|
||||
|
||||
builder.build_store(field_ptr, field_val);
|
||||
}
|
||||
|
||||
raw_data_ptr.into()
|
||||
}
|
||||
UnionLayout::NullableUnwrapped {
|
||||
nullable_id,
|
||||
other_fields,
|
||||
|
@ -1520,7 +1571,6 @@ pub fn build_tag<'a, 'ctx, 'env>(
|
|||
debug_assert!(union_size == 2);
|
||||
|
||||
let ctx = env.context;
|
||||
let builder = env.builder;
|
||||
|
||||
// Determine types
|
||||
let num_fields = arguments.len() + 1;
|
||||
|
@ -1561,32 +1611,128 @@ pub fn build_tag<'a, 'ctx, 'env>(
|
|||
|
||||
// Create the struct_type
|
||||
let data_ptr =
|
||||
reserve_with_refcount_union_as_block_of_memory(env, *union_layout, &[other_fields]);
|
||||
allocate_tag(env, parent, reuse_allocation, union_layout, &[other_fields]);
|
||||
|
||||
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
||||
let struct_ptr = env
|
||||
.builder
|
||||
.build_bitcast(
|
||||
data_ptr,
|
||||
struct_type.ptr_type(AddressSpace::Generic),
|
||||
"block_of_memory_to_tag",
|
||||
)
|
||||
.into_pointer_value();
|
||||
|
||||
// Insert field exprs into struct_val
|
||||
for (index, field_val) in field_vals.into_iter().enumerate() {
|
||||
let field_ptr = builder
|
||||
.build_struct_gep(struct_ptr, index as u32, "struct_gep")
|
||||
.unwrap();
|
||||
|
||||
builder.build_store(field_ptr, field_val);
|
||||
}
|
||||
struct_pointer_from_fields(
|
||||
env,
|
||||
struct_type,
|
||||
data_ptr,
|
||||
field_vals.into_iter().enumerate(),
|
||||
);
|
||||
|
||||
data_ptr.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn tag_pointer_set_tag_id<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
tag_id: u8,
|
||||
pointer: PointerValue<'ctx>,
|
||||
) -> PointerValue<'ctx> {
|
||||
// we only have 3 bits, so can encode only 0..7
|
||||
debug_assert!(tag_id < 8);
|
||||
|
||||
let ptr_int = env.ptr_int();
|
||||
|
||||
let as_int = env.builder.build_ptr_to_int(pointer, ptr_int, "to_int");
|
||||
|
||||
let tag_id_intval = ptr_int.const_int(tag_id as u64, false);
|
||||
let combined = env.builder.build_or(as_int, tag_id_intval, "store_tag_id");
|
||||
|
||||
env.builder
|
||||
.build_int_to_ptr(combined, pointer.get_type(), "to_ptr")
|
||||
}
|
||||
|
||||
pub fn tag_pointer_read_tag_id<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
pointer: PointerValue<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
let mask: u64 = 0b0000_0111;
|
||||
|
||||
let ptr_int = env.ptr_int();
|
||||
|
||||
let as_int = env.builder.build_ptr_to_int(pointer, ptr_int, "to_int");
|
||||
let mask_intval = env.ptr_int().const_int(mask, false);
|
||||
|
||||
let masked = env.builder.build_and(as_int, mask_intval, "mask");
|
||||
|
||||
env.builder
|
||||
.build_int_cast(masked, env.context.i8_type(), "to_u8")
|
||||
}
|
||||
|
||||
pub fn tag_pointer_clear_tag_id<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
pointer: PointerValue<'ctx>,
|
||||
) -> PointerValue<'ctx> {
|
||||
let ptr_int = env.ptr_int();
|
||||
|
||||
let as_int = env.builder.build_ptr_to_int(pointer, ptr_int, "to_int");
|
||||
|
||||
let mask = {
|
||||
let a = env.ptr_int().const_all_ones();
|
||||
let tag_id_bits = env.ptr_int().const_int(3, false);
|
||||
env.builder.build_left_shift(a, tag_id_bits, "make_mask")
|
||||
};
|
||||
|
||||
let masked = env.builder.build_and(as_int, mask, "masked");
|
||||
|
||||
env.builder
|
||||
.build_int_to_ptr(masked, pointer.get_type(), "to_ptr")
|
||||
}
|
||||
|
||||
fn allocate_tag<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
reuse_allocation: Option<PointerValue<'ctx>>,
|
||||
union_layout: &UnionLayout<'a>,
|
||||
tags: &[&[Layout<'a>]],
|
||||
) -> PointerValue<'ctx> {
|
||||
match reuse_allocation {
|
||||
Some(ptr) => {
|
||||
// check if its a null pointer
|
||||
let is_null_ptr = env.builder.build_is_null(ptr, "is_null_ptr");
|
||||
let ctx = env.context;
|
||||
let then_block = ctx.append_basic_block(parent, "then_allocate_fresh");
|
||||
let else_block = ctx.append_basic_block(parent, "else_reuse");
|
||||
let cont_block = ctx.append_basic_block(parent, "cont");
|
||||
|
||||
env.builder
|
||||
.build_conditional_branch(is_null_ptr, then_block, else_block);
|
||||
|
||||
let raw_ptr = {
|
||||
env.builder.position_at_end(then_block);
|
||||
let raw_ptr =
|
||||
reserve_with_refcount_union_as_block_of_memory(env, *union_layout, tags);
|
||||
env.builder.build_unconditional_branch(cont_block);
|
||||
raw_ptr
|
||||
};
|
||||
|
||||
let reuse_ptr = {
|
||||
env.builder.position_at_end(else_block);
|
||||
|
||||
let cleared = tag_pointer_clear_tag_id(env, ptr);
|
||||
|
||||
env.builder.build_unconditional_branch(cont_block);
|
||||
|
||||
cleared
|
||||
};
|
||||
|
||||
{
|
||||
env.builder.position_at_end(cont_block);
|
||||
let phi = env.builder.build_phi(raw_ptr.get_type(), "branch");
|
||||
|
||||
phi.add_incoming(&[(&raw_ptr, then_block), (&reuse_ptr, else_block)]);
|
||||
|
||||
phi.as_basic_value().into_pointer_value()
|
||||
}
|
||||
}
|
||||
None => reserve_with_refcount_union_as_block_of_memory(env, *union_layout, tags),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_tag_id<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
|
@ -1604,7 +1750,15 @@ pub fn get_tag_id<'a, 'ctx, 'env>(
|
|||
|
||||
get_tag_id_non_recursive(env, tag)
|
||||
}
|
||||
UnionLayout::Recursive(_) => get_tag_id_wrapped(env, argument.into_pointer_value()),
|
||||
UnionLayout::Recursive(_) => {
|
||||
let argument_ptr = argument.into_pointer_value();
|
||||
|
||||
if union_layout.stores_tag_id_as_data(env.ptr_bytes) {
|
||||
get_tag_id_wrapped(env, argument_ptr)
|
||||
} else {
|
||||
tag_pointer_read_tag_id(env, argument_ptr)
|
||||
}
|
||||
}
|
||||
UnionLayout::NonNullableUnwrapped(_) => tag_id_int_type.const_zero(),
|
||||
UnionLayout::NullableWrapped { nullable_id, .. } => {
|
||||
let argument_ptr = argument.into_pointer_value();
|
||||
|
@ -1629,7 +1783,12 @@ pub fn get_tag_id<'a, 'ctx, 'env>(
|
|||
|
||||
{
|
||||
env.builder.position_at_end(else_block);
|
||||
let tag_id = get_tag_id_wrapped(env, argument_ptr);
|
||||
|
||||
let tag_id = if union_layout.stores_tag_id_as_data(env.ptr_bytes) {
|
||||
get_tag_id_wrapped(env, argument_ptr)
|
||||
} else {
|
||||
tag_pointer_read_tag_id(env, argument_ptr)
|
||||
};
|
||||
env.builder.build_store(result, tag_id);
|
||||
env.builder.build_unconditional_branch(cont_block);
|
||||
}
|
||||
|
@ -1765,9 +1924,11 @@ fn reserve_with_refcount_union_as_block_of_memory<'a, 'ctx, 'env>(
|
|||
union_layout: UnionLayout<'a>,
|
||||
fields: &[&[Layout<'a>]],
|
||||
) -> PointerValue<'ctx> {
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
|
||||
let block_type = block_of_memory_slices(env.context, fields, env.ptr_bytes);
|
||||
|
||||
let basic_type = if union_layout.stores_tag_id() {
|
||||
let basic_type = if union_layout.stores_tag_id_as_data(ptr_bytes) {
|
||||
let tag_id_type = basic_type_from_layout(env, &union_layout.tag_id_layout());
|
||||
|
||||
env.context
|
||||
|
@ -1783,7 +1944,7 @@ fn reserve_with_refcount_union_as_block_of_memory<'a, 'ctx, 'env>(
|
|||
.max()
|
||||
.unwrap_or_default();
|
||||
|
||||
if union_layout.stores_tag_id() {
|
||||
if union_layout.stores_tag_id_as_data(ptr_bytes) {
|
||||
stack_size += union_layout.tag_id_layout().stack_size(env.ptr_bytes);
|
||||
}
|
||||
|
||||
|
@ -2406,11 +2567,26 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
|||
|
||||
_ if layout.is_refcounted() => {
|
||||
if value.is_pointer_value() {
|
||||
// BasicValueEnum::PointerValue(value_ptr) => {
|
||||
let value_ptr = value.into_pointer_value();
|
||||
let refcount_ptr =
|
||||
PointerToRefcount::from_ptr_to_data(env, value_ptr);
|
||||
refcount_ptr.decrement(env, layout);
|
||||
|
||||
let then_block = env.context.append_basic_block(parent, "then");
|
||||
let done_block = env.context.append_basic_block(parent, "done");
|
||||
|
||||
let condition =
|
||||
env.builder.build_is_not_null(value_ptr, "box_is_not_null");
|
||||
env.builder
|
||||
.build_conditional_branch(condition, then_block, done_block);
|
||||
|
||||
{
|
||||
env.builder.position_at_end(then_block);
|
||||
let refcount_ptr =
|
||||
PointerToRefcount::from_ptr_to_data(env, value_ptr);
|
||||
refcount_ptr.decrement(env, layout);
|
||||
|
||||
env.builder.build_unconditional_branch(done_block);
|
||||
}
|
||||
|
||||
env.builder.position_at_end(done_block);
|
||||
} else {
|
||||
eprint!("we're likely leaking memory; see issue #985 for details");
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::debug_info_init;
|
||||
use crate::llvm::bitcode::call_bitcode_fn;
|
||||
use crate::llvm::build::tag_pointer_clear_tag_id;
|
||||
use crate::llvm::build::Env;
|
||||
use crate::llvm::build::{cast_block_of_memory_to_tag, get_tag_id, FAST_CALL_CONV, TAG_DATA_INDEX};
|
||||
use crate::llvm::build_str;
|
||||
|
@ -494,14 +495,9 @@ fn hash_tag<'a, 'ctx, 'env>(
|
|||
);
|
||||
|
||||
// hash the tag data
|
||||
let answer = hash_ptr_to_struct(
|
||||
env,
|
||||
layout_ids,
|
||||
union_layout,
|
||||
field_layouts,
|
||||
seed,
|
||||
tag.into_pointer_value(),
|
||||
);
|
||||
let tag = tag_pointer_clear_tag_id(env, tag.into_pointer_value());
|
||||
let answer =
|
||||
hash_ptr_to_struct(env, layout_ids, union_layout, field_layouts, seed, tag);
|
||||
|
||||
merge_phi.add_incoming(&[(&answer, block)]);
|
||||
env.builder.build_unconditional_branch(merge_block);
|
||||
|
@ -599,6 +595,7 @@ fn hash_tag<'a, 'ctx, 'env>(
|
|||
);
|
||||
|
||||
// hash tag data
|
||||
let tag = tag_pointer_clear_tag_id(env, tag);
|
||||
let answer = hash_ptr_to_struct(
|
||||
env,
|
||||
layout_ids,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::llvm::bitcode::call_bitcode_fn;
|
||||
use crate::llvm::build::Env;
|
||||
use crate::llvm::build::{cast_block_of_memory_to_tag, get_tag_id, FAST_CALL_CONV};
|
||||
use crate::llvm::build::{
|
||||
cast_block_of_memory_to_tag, get_tag_id, tag_pointer_clear_tag_id, Env, FAST_CALL_CONV,
|
||||
};
|
||||
use crate::llvm::build_list::{list_len, load_list_ptr};
|
||||
use crate::llvm::build_str::str_equal;
|
||||
use crate::llvm::convert::basic_type_from_layout;
|
||||
|
@ -929,6 +930,10 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
|
|||
let id1 = get_tag_id(env, parent, union_layout, tag1);
|
||||
let id2 = get_tag_id(env, parent, union_layout, tag2);
|
||||
|
||||
// clear the tag_id so we get a pointer to the actual data
|
||||
let tag1 = tag_pointer_clear_tag_id(env, tag1.into_pointer_value());
|
||||
let tag2 = tag_pointer_clear_tag_id(env, tag2.into_pointer_value());
|
||||
|
||||
let compare_tag_fields = ctx.append_basic_block(parent, "compare_tag_fields");
|
||||
|
||||
let same_tag =
|
||||
|
@ -948,14 +953,8 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
|
|||
let block = env.context.append_basic_block(parent, "tag_id_modify");
|
||||
env.builder.position_at_end(block);
|
||||
|
||||
let answer = eq_ptr_to_struct(
|
||||
env,
|
||||
layout_ids,
|
||||
union_layout,
|
||||
field_layouts,
|
||||
tag1.into_pointer_value(),
|
||||
tag2.into_pointer_value(),
|
||||
);
|
||||
let answer =
|
||||
eq_ptr_to_struct(env, layout_ids, union_layout, field_layouts, tag1, tag2);
|
||||
|
||||
env.builder.build_return(Some(&answer));
|
||||
|
||||
|
@ -1077,6 +1076,10 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
|
|||
let id1 = get_tag_id(env, parent, union_layout, tag1);
|
||||
let id2 = get_tag_id(env, parent, union_layout, tag2);
|
||||
|
||||
// clear the tag_id so we get a pointer to the actual data
|
||||
let tag1 = tag_pointer_clear_tag_id(env, tag1.into_pointer_value());
|
||||
let tag2 = tag_pointer_clear_tag_id(env, tag2.into_pointer_value());
|
||||
|
||||
let compare_tag_fields = ctx.append_basic_block(parent, "compare_tag_fields");
|
||||
|
||||
let same_tag =
|
||||
|
@ -1097,14 +1100,8 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
|
|||
let block = env.context.append_basic_block(parent, "tag_id_modify");
|
||||
env.builder.position_at_end(block);
|
||||
|
||||
let answer = eq_ptr_to_struct(
|
||||
env,
|
||||
layout_ids,
|
||||
union_layout,
|
||||
field_layouts,
|
||||
tag1.into_pointer_value(),
|
||||
tag2.into_pointer_value(),
|
||||
);
|
||||
let answer =
|
||||
eq_ptr_to_struct(env, layout_ids, union_layout, field_layouts, tag1, tag2);
|
||||
|
||||
env.builder.build_return(Some(&answer));
|
||||
|
||||
|
|
|
@ -31,21 +31,31 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>(
|
|||
basic_type_from_layout(env, &closure_data_layout)
|
||||
}
|
||||
Struct(sorted_fields) => basic_type_from_record(env, sorted_fields),
|
||||
Union(variant) => {
|
||||
Union(union_layout) => {
|
||||
use UnionLayout::*;
|
||||
|
||||
let tag_id_type = basic_type_from_layout(env, &variant.tag_id_layout());
|
||||
let tag_id_type = basic_type_from_layout(env, &union_layout.tag_id_layout());
|
||||
|
||||
match variant {
|
||||
NullableWrapped {
|
||||
match union_layout {
|
||||
NonRecursive(tags) => {
|
||||
let data = block_of_memory_slices(env.context, tags, env.ptr_bytes);
|
||||
|
||||
env.context.struct_type(&[data, tag_id_type], false).into()
|
||||
}
|
||||
Recursive(tags)
|
||||
| NullableWrapped {
|
||||
other_tags: tags, ..
|
||||
} => {
|
||||
let data = block_of_memory_slices(env.context, tags, env.ptr_bytes);
|
||||
|
||||
env.context
|
||||
.struct_type(&[data, tag_id_type], false)
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.into()
|
||||
if union_layout.stores_tag_id_as_data(env.ptr_bytes) {
|
||||
env.context
|
||||
.struct_type(&[data, tag_id_type], false)
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.into()
|
||||
} else {
|
||||
data.ptr_type(AddressSpace::Generic).into()
|
||||
}
|
||||
}
|
||||
NullableUnwrapped { other_fields, .. } => {
|
||||
let block =
|
||||
|
@ -56,19 +66,6 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>(
|
|||
let block = block_of_memory_slices(env.context, &[fields], env.ptr_bytes);
|
||||
block.ptr_type(AddressSpace::Generic).into()
|
||||
}
|
||||
Recursive(tags) => {
|
||||
let data = block_of_memory_slices(env.context, tags, env.ptr_bytes);
|
||||
|
||||
env.context
|
||||
.struct_type(&[data, tag_id_type], false)
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.into()
|
||||
}
|
||||
NonRecursive(tags) => {
|
||||
let data = block_of_memory_slices(env.context, tags, env.ptr_bytes);
|
||||
|
||||
env.context.struct_type(&[data, tag_id_type], false).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
RecursivePointer => {
|
||||
|
@ -146,16 +143,6 @@ pub fn union_data_is_struct_type<'ctx>(
|
|||
context.struct_type(&[struct_type.into(), tag_id_type.into()], false)
|
||||
}
|
||||
|
||||
pub fn union_data_block_of_memory<'ctx>(
|
||||
context: &'ctx Context,
|
||||
tag_id_int_type: IntType<'ctx>,
|
||||
layouts: &[&[Layout<'_>]],
|
||||
ptr_bytes: u32,
|
||||
) -> StructType<'ctx> {
|
||||
let data_type = block_of_memory_slices(context, layouts, ptr_bytes);
|
||||
context.struct_type(&[data_type, tag_id_int_type.into()], false)
|
||||
}
|
||||
|
||||
pub fn block_of_memory<'ctx>(
|
||||
context: &'ctx Context,
|
||||
layout: &Layout<'_>,
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
use crate::debug_info_init;
|
||||
use crate::llvm::build::{
|
||||
add_func, cast_basic_basic, cast_block_of_memory_to_tag, get_tag_id, get_tag_id_non_recursive,
|
||||
Env, FAST_CALL_CONV, LLVM_SADD_WITH_OVERFLOW_I64, TAG_DATA_INDEX,
|
||||
tag_pointer_clear_tag_id, Env, FAST_CALL_CONV, LLVM_SADD_WITH_OVERFLOW_I64, TAG_DATA_INDEX,
|
||||
};
|
||||
use crate::llvm::build_list::{incrementing_elem_loop, list_len, load_list};
|
||||
use crate::llvm::convert::{
|
||||
basic_type_from_layout, block_of_memory_slices, ptr_int, union_data_block_of_memory,
|
||||
};
|
||||
use crate::llvm::convert::{basic_type_from_layout, ptr_int};
|
||||
use bumpalo::collections::Vec;
|
||||
use inkwell::basic_block::BasicBlock;
|
||||
use inkwell::context::Context;
|
||||
|
@ -644,70 +642,21 @@ fn modify_refcount_layout_build_function<'a, 'ctx, 'env>(
|
|||
Union(variant) => {
|
||||
use UnionLayout::*;
|
||||
|
||||
match variant {
|
||||
NullableWrapped {
|
||||
other_tags: tags, ..
|
||||
} => {
|
||||
let function = build_rec_union(
|
||||
env,
|
||||
layout_ids,
|
||||
mode,
|
||||
&WhenRecursive::Loop(*variant),
|
||||
*variant,
|
||||
tags,
|
||||
true,
|
||||
);
|
||||
if let NonRecursive(tags) = variant {
|
||||
let function = modify_refcount_union(env, layout_ids, mode, when_recursive, tags);
|
||||
|
||||
Some(function)
|
||||
}
|
||||
|
||||
NullableUnwrapped { other_fields, .. } => {
|
||||
let function = build_rec_union(
|
||||
env,
|
||||
layout_ids,
|
||||
mode,
|
||||
&WhenRecursive::Loop(*variant),
|
||||
*variant,
|
||||
env.arena.alloc([*other_fields]),
|
||||
true,
|
||||
);
|
||||
|
||||
Some(function)
|
||||
}
|
||||
|
||||
NonNullableUnwrapped(fields) => {
|
||||
let function = build_rec_union(
|
||||
env,
|
||||
layout_ids,
|
||||
mode,
|
||||
&WhenRecursive::Loop(*variant),
|
||||
*variant,
|
||||
&*env.arena.alloc([*fields]),
|
||||
true,
|
||||
);
|
||||
Some(function)
|
||||
}
|
||||
|
||||
Recursive(tags) => {
|
||||
let function = build_rec_union(
|
||||
env,
|
||||
layout_ids,
|
||||
mode,
|
||||
&WhenRecursive::Loop(*variant),
|
||||
*variant,
|
||||
tags,
|
||||
false,
|
||||
);
|
||||
Some(function)
|
||||
}
|
||||
|
||||
NonRecursive(tags) => {
|
||||
let function =
|
||||
modify_refcount_union(env, layout_ids, mode, when_recursive, tags);
|
||||
|
||||
Some(function)
|
||||
}
|
||||
return Some(function);
|
||||
}
|
||||
|
||||
let function = build_rec_union(
|
||||
env,
|
||||
layout_ids,
|
||||
mode,
|
||||
&WhenRecursive::Loop(*variant),
|
||||
*variant,
|
||||
);
|
||||
|
||||
Some(function)
|
||||
}
|
||||
|
||||
Closure(_, lambda_set, _) => {
|
||||
|
@ -1208,10 +1157,8 @@ fn build_rec_union<'a, 'ctx, 'env>(
|
|||
mode: Mode,
|
||||
when_recursive: &WhenRecursive<'a>,
|
||||
union_layout: UnionLayout<'a>,
|
||||
tags: &'a [&'a [Layout<'a>]],
|
||||
is_nullable: bool,
|
||||
) -> FunctionValue<'ctx> {
|
||||
let layout = Layout::Union(UnionLayout::Recursive(tags));
|
||||
let layout = Layout::Union(union_layout);
|
||||
|
||||
let (_, fn_name) = function_name_from_mode(
|
||||
layout_ids,
|
||||
|
@ -1228,7 +1175,7 @@ fn build_rec_union<'a, 'ctx, 'env>(
|
|||
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||
let di_location = env.builder.get_current_debug_location().unwrap();
|
||||
|
||||
let basic_type = basic_type_from_layout(env, &Layout::Union(union_layout));
|
||||
let basic_type = basic_type_from_layout(env, &layout);
|
||||
let function_value = build_header(env, basic_type, mode, &fn_name);
|
||||
|
||||
build_rec_union_help(
|
||||
|
@ -1237,9 +1184,7 @@ fn build_rec_union<'a, 'ctx, 'env>(
|
|||
mode,
|
||||
when_recursive,
|
||||
union_layout,
|
||||
tags,
|
||||
function_value,
|
||||
is_nullable,
|
||||
);
|
||||
|
||||
env.builder.position_at_end(block);
|
||||
|
@ -1260,10 +1205,10 @@ fn build_rec_union_help<'a, 'ctx, 'env>(
|
|||
mode: Mode,
|
||||
when_recursive: &WhenRecursive<'a>,
|
||||
union_layout: UnionLayout<'a>,
|
||||
tags: &'a [&'a [roc_mono::layout::Layout<'a>]],
|
||||
fn_val: FunctionValue<'ctx>,
|
||||
is_nullable: bool,
|
||||
) {
|
||||
let tags = union_layout_tags(env.arena, &union_layout);
|
||||
let is_nullable = union_layout.is_nullable();
|
||||
debug_assert!(!tags.is_empty());
|
||||
|
||||
let context = &env.context;
|
||||
|
@ -1286,7 +1231,8 @@ fn build_rec_union_help<'a, 'ctx, 'env>(
|
|||
let parent = fn_val;
|
||||
|
||||
debug_assert!(arg_val.is_pointer_value());
|
||||
let value_ptr = arg_val.into_pointer_value();
|
||||
let current_tag_id = get_tag_id(env, fn_val, &union_layout, arg_val);
|
||||
let value_ptr = tag_pointer_clear_tag_id(env, arg_val.into_pointer_value());
|
||||
|
||||
// to increment/decrement the cons-cell itself
|
||||
let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, value_ptr);
|
||||
|
@ -1351,14 +1297,21 @@ fn build_rec_union_help<'a, 'ctx, 'env>(
|
|||
union_layout,
|
||||
tags,
|
||||
value_ptr,
|
||||
current_tag_id,
|
||||
refcount_ptr,
|
||||
do_recurse_block,
|
||||
DecOrReuse::Dec,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum DecOrReuse {
|
||||
Dec,
|
||||
Reuse,
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
|
@ -1369,8 +1322,10 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>(
|
|||
union_layout: UnionLayout<'a>,
|
||||
tags: &[&[Layout<'a>]],
|
||||
value_ptr: PointerValue<'ctx>,
|
||||
current_tag_id: IntValue<'ctx>,
|
||||
refcount_ptr: PointerToRefcount<'ctx>,
|
||||
match_block: BasicBlock<'ctx>,
|
||||
decrement_or_reuse: DecOrReuse,
|
||||
) {
|
||||
let mode = Mode::Dec;
|
||||
let call_mode = mode_to_call_mode(decrement_fn, mode);
|
||||
|
@ -1442,28 +1397,8 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>(
|
|||
debug_assert!(ptr_as_i64_ptr.is_pointer_value());
|
||||
|
||||
// therefore we must cast it to our desired type
|
||||
let union_type = match union_layout {
|
||||
UnionLayout::NonRecursive(_) => unreachable!(),
|
||||
UnionLayout::Recursive(_) | UnionLayout::NullableWrapped { .. } => {
|
||||
union_data_block_of_memory(
|
||||
env.context,
|
||||
tag_id_int_type,
|
||||
tags,
|
||||
env.ptr_bytes,
|
||||
)
|
||||
.into()
|
||||
}
|
||||
UnionLayout::NonNullableUnwrapped { .. }
|
||||
| UnionLayout::NullableUnwrapped { .. } => {
|
||||
block_of_memory_slices(env.context, tags, env.ptr_bytes)
|
||||
}
|
||||
};
|
||||
|
||||
let recursive_field_ptr = cast_basic_basic(
|
||||
env.builder,
|
||||
ptr_as_i64_ptr,
|
||||
union_type.ptr_type(AddressSpace::Generic).into(),
|
||||
);
|
||||
let union_type = basic_type_from_layout(env, &Layout::Union(union_layout));
|
||||
let recursive_field_ptr = cast_basic_basic(env.builder, ptr_as_i64_ptr, union_type);
|
||||
|
||||
deferred_rec.push(recursive_field_ptr);
|
||||
} else if field_layout.contains_refcounted() {
|
||||
|
@ -1486,7 +1421,13 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>(
|
|||
// lists. To achieve it, we must first load all fields that we want to inc/dec (done above)
|
||||
// and store them on the stack, then modify (and potentially free) the current cell, then
|
||||
// actually inc/dec the fields.
|
||||
refcount_ptr.modify(call_mode, &Layout::Union(union_layout), env);
|
||||
|
||||
match decrement_or_reuse {
|
||||
DecOrReuse::Reuse => {}
|
||||
DecOrReuse::Dec => {
|
||||
refcount_ptr.modify(call_mode, &Layout::Union(union_layout), env);
|
||||
}
|
||||
}
|
||||
|
||||
for (field, field_layout) in deferred_nonrec {
|
||||
modify_refcount_layout_help(
|
||||
|
@ -1524,25 +1465,182 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>(
|
|||
let (_, only_branch) = cases.pop().unwrap();
|
||||
env.builder.build_unconditional_branch(only_branch);
|
||||
} else {
|
||||
// read the tag_id
|
||||
let current_tag_id = get_tag_id(env, parent, &union_layout, value_ptr.into());
|
||||
|
||||
let merge_block = env.context.append_basic_block(parent, "decrement_merge");
|
||||
let default_block = env.context.append_basic_block(parent, "switch_default");
|
||||
|
||||
// switch on it
|
||||
env.builder
|
||||
.build_switch(current_tag_id, merge_block, &cases);
|
||||
.build_switch(current_tag_id, default_block, &cases);
|
||||
|
||||
env.builder.position_at_end(merge_block);
|
||||
{
|
||||
env.builder.position_at_end(default_block);
|
||||
|
||||
// increment/decrement the cons-cell itself
|
||||
refcount_ptr.modify(call_mode, &Layout::Union(union_layout), env);
|
||||
// increment/decrement the cons-cell itself
|
||||
if let DecOrReuse::Dec = decrement_or_reuse {
|
||||
refcount_ptr.modify(call_mode, &Layout::Union(union_layout), env);
|
||||
}
|
||||
}
|
||||
|
||||
// this function returns void
|
||||
builder.build_return(None);
|
||||
}
|
||||
}
|
||||
|
||||
fn union_layout_tags<'a>(
|
||||
arena: &'a bumpalo::Bump,
|
||||
union_layout: &UnionLayout<'a>,
|
||||
) -> &'a [&'a [Layout<'a>]] {
|
||||
use UnionLayout::*;
|
||||
|
||||
match union_layout {
|
||||
NullableWrapped {
|
||||
other_tags: tags, ..
|
||||
} => *tags,
|
||||
NullableUnwrapped { other_fields, .. } => arena.alloc([*other_fields]),
|
||||
NonNullableUnwrapped(fields) => arena.alloc([*fields]),
|
||||
Recursive(tags) => tags,
|
||||
NonRecursive(tags) => tags,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_reset<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
union_layout: UnionLayout<'a>,
|
||||
) -> FunctionValue<'ctx> {
|
||||
let mode = Mode::Dec;
|
||||
|
||||
let layout_id = layout_ids.get(Symbol::DEC, &Layout::Union(union_layout));
|
||||
let fn_name = layout_id.to_symbol_string(Symbol::DEC, &env.interns);
|
||||
let fn_name = format!("{}_reset", fn_name);
|
||||
|
||||
let when_recursive = WhenRecursive::Loop(union_layout);
|
||||
let dec_function = build_rec_union(env, layout_ids, Mode::Dec, &when_recursive, union_layout);
|
||||
|
||||
let function = match env.module.get_function(fn_name.as_str()) {
|
||||
Some(function_value) => function_value,
|
||||
None => {
|
||||
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||
let di_location = env.builder.get_current_debug_location().unwrap();
|
||||
|
||||
let basic_type = basic_type_from_layout(env, &Layout::Union(union_layout));
|
||||
let function_value = build_header(env, basic_type, mode, &fn_name);
|
||||
|
||||
build_reuse_rec_union_help(
|
||||
env,
|
||||
layout_ids,
|
||||
&when_recursive,
|
||||
union_layout,
|
||||
function_value,
|
||||
dec_function,
|
||||
);
|
||||
|
||||
env.builder.position_at_end(block);
|
||||
env.builder
|
||||
.set_current_debug_location(env.context, di_location);
|
||||
|
||||
function_value
|
||||
}
|
||||
};
|
||||
|
||||
function
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn build_reuse_rec_union_help<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
when_recursive: &WhenRecursive<'a>,
|
||||
union_layout: UnionLayout<'a>,
|
||||
reset_function: FunctionValue<'ctx>,
|
||||
dec_function: FunctionValue<'ctx>,
|
||||
) {
|
||||
let tags = union_layout_tags(env.arena, &union_layout);
|
||||
let is_nullable = union_layout.is_nullable();
|
||||
|
||||
debug_assert!(!tags.is_empty());
|
||||
|
||||
let context = &env.context;
|
||||
let builder = env.builder;
|
||||
|
||||
// Add a basic block for the entry point
|
||||
let entry = context.append_basic_block(reset_function, "entry");
|
||||
|
||||
builder.position_at_end(entry);
|
||||
|
||||
debug_info_init!(env, reset_function);
|
||||
|
||||
// Add args to scope
|
||||
let arg_symbol = Symbol::ARG_1;
|
||||
|
||||
let arg_val = reset_function.get_param_iter().next().unwrap();
|
||||
|
||||
arg_val.set_name(arg_symbol.ident_string(&env.interns));
|
||||
|
||||
let parent = reset_function;
|
||||
|
||||
debug_assert!(arg_val.is_pointer_value());
|
||||
let current_tag_id = get_tag_id(env, reset_function, &union_layout, arg_val);
|
||||
let value_ptr = tag_pointer_clear_tag_id(env, arg_val.into_pointer_value());
|
||||
|
||||
// to increment/decrement the cons-cell itself
|
||||
let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, value_ptr);
|
||||
let call_mode = CallMode::Dec;
|
||||
|
||||
let should_recurse_block = env.context.append_basic_block(parent, "should_recurse");
|
||||
|
||||
let ctx = env.context;
|
||||
if is_nullable {
|
||||
let is_null = env.builder.build_is_null(value_ptr, "is_null");
|
||||
|
||||
let then_block = ctx.append_basic_block(parent, "then");
|
||||
|
||||
env.builder
|
||||
.build_conditional_branch(is_null, then_block, should_recurse_block);
|
||||
|
||||
{
|
||||
env.builder.position_at_end(then_block);
|
||||
env.builder.build_return(None);
|
||||
}
|
||||
} else {
|
||||
env.builder.build_unconditional_branch(should_recurse_block);
|
||||
}
|
||||
|
||||
env.builder.position_at_end(should_recurse_block);
|
||||
|
||||
let layout = Layout::Union(union_layout);
|
||||
|
||||
let do_recurse_block = env.context.append_basic_block(parent, "do_recurse");
|
||||
let no_recurse_block = env.context.append_basic_block(parent, "no_recurse");
|
||||
|
||||
builder.build_conditional_branch(refcount_ptr.is_1(env), do_recurse_block, no_recurse_block);
|
||||
|
||||
{
|
||||
env.builder.position_at_end(no_recurse_block);
|
||||
|
||||
refcount_ptr.modify(call_mode, &layout, env);
|
||||
env.builder.build_return(None);
|
||||
}
|
||||
|
||||
{
|
||||
env.builder.position_at_end(do_recurse_block);
|
||||
|
||||
build_rec_union_recursive_decrement(
|
||||
env,
|
||||
layout_ids,
|
||||
when_recursive,
|
||||
parent,
|
||||
dec_function,
|
||||
union_layout,
|
||||
tags,
|
||||
value_ptr,
|
||||
current_tag_id,
|
||||
refcount_ptr,
|
||||
do_recurse_block,
|
||||
DecOrReuse::Reuse,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn function_name_from_mode<'a>(
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
interns: &Interns,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue