mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 08:34:33 +00:00
Merge branch 'trunk' of /rtfeldman/roc into text_rope
This commit is contained in:
commit
729e3e358b
8 changed files with 686 additions and 114 deletions
|
@ -164,7 +164,10 @@ fn jit_to_ast_help<'a>(
|
||||||
|
|
||||||
let size = layout.stack_size(env.ptr_bytes);
|
let size = layout.stack_size(env.ptr_bytes);
|
||||||
match union_variant {
|
match union_variant {
|
||||||
UnionVariant::Wrapped(tags_and_layouts) => {
|
UnionVariant::Wrapped {
|
||||||
|
sorted_tag_layouts: tags_and_layouts,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
Ok(run_jit_function_dynamic_type!(
|
Ok(run_jit_function_dynamic_type!(
|
||||||
lib,
|
lib,
|
||||||
main_fn_name,
|
main_fn_name,
|
||||||
|
|
|
@ -808,6 +808,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
layout: &Layout<'a>,
|
layout: &Layout<'a>,
|
||||||
expr: &roc_mono::ir::Expr<'a>,
|
expr: &roc_mono::ir::Expr<'a>,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
use inkwell::types::BasicType;
|
||||||
use roc_mono::ir::Expr::*;
|
use roc_mono::ir::Expr::*;
|
||||||
|
|
||||||
match expr {
|
match expr {
|
||||||
|
@ -960,15 +961,9 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
field_types.push(field_type);
|
field_types.push(field_type);
|
||||||
|
|
||||||
if let Layout::RecursivePointer = tag_field_layout {
|
if let Layout::RecursivePointer = tag_field_layout {
|
||||||
let ptr = allocate_with_refcount(env, &tag_layout, val);
|
panic!(
|
||||||
|
r"non-recursive tag unions cannot directly contain a recursive pointer"
|
||||||
let ptr = cast_basic_basic(
|
|
||||||
builder,
|
|
||||||
ptr.into(),
|
|
||||||
ctx.i64_type().ptr_type(AddressSpace::Generic).into(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
field_vals.push(ptr);
|
|
||||||
} else {
|
} else {
|
||||||
// this check fails for recursive tag unions, but can be helpful while debugging
|
// this check fails for recursive tag unions, but can be helpful while debugging
|
||||||
debug_assert_eq!(tag_field_layout, val_layout);
|
debug_assert_eq!(tag_field_layout, val_layout);
|
||||||
|
@ -1027,7 +1022,88 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
internal_type,
|
internal_type,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Tag { .. } => unreachable!("tags should have a union layout"),
|
Tag {
|
||||||
|
arguments,
|
||||||
|
tag_layout: Layout::RecursiveUnion(fields),
|
||||||
|
union_size,
|
||||||
|
tag_id,
|
||||||
|
tag_name,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let tag_layout = Layout::Union(fields);
|
||||||
|
|
||||||
|
debug_assert!(*union_size > 1);
|
||||||
|
let ptr_size = env.ptr_bytes;
|
||||||
|
|
||||||
|
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 = if let TagName::Closure(_) = tag_name {
|
||||||
|
// closures ignore (and do not store) the discriminant
|
||||||
|
&fields[*tag_id as usize][1..]
|
||||||
|
} else {
|
||||||
|
&fields[*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(env, 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.arena, env.context, tag_field_layout, ptr_size);
|
||||||
|
|
||||||
|
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 = cast_basic_basic(
|
||||||
|
builder,
|
||||||
|
val,
|
||||||
|
ctx.i64_type().ptr_type(AddressSpace::Generic).into(),
|
||||||
|
);
|
||||||
|
|
||||||
|
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 data_ptr = reserve_with_refcount(env, &tag_layout);
|
||||||
|
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
||||||
|
let struct_ptr = cast_basic_basic(
|
||||||
|
builder,
|
||||||
|
data_ptr.into(),
|
||||||
|
struct_type.ptr_type(AddressSpace::Generic).into(),
|
||||||
|
)
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
data_ptr.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag { .. } => unreachable!("tags should have a Union or RecursiveUnion layout"),
|
||||||
|
|
||||||
Reset(_) => todo!(),
|
Reset(_) => todo!(),
|
||||||
Reuse { .. } => todo!(),
|
Reuse { .. } => todo!(),
|
||||||
|
@ -1092,6 +1168,8 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
field_layouts,
|
field_layouts,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
|
use BasicValueEnum::*;
|
||||||
|
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
|
|
||||||
// Determine types, assumes the descriminant is in the field layouts
|
// Determine types, assumes the descriminant is in the field layouts
|
||||||
|
@ -1111,20 +1189,22 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
.struct_type(field_types.into_bump_slice(), false);
|
.struct_type(field_types.into_bump_slice(), false);
|
||||||
|
|
||||||
// cast the argument bytes into the desired shape for this tag
|
// cast the argument bytes into the desired shape for this tag
|
||||||
let argument = load_symbol(env, scope, structure).into_struct_value();
|
let argument = load_symbol(env, scope, structure);
|
||||||
|
|
||||||
let struct_value = cast_struct_struct(builder, argument, struct_type);
|
let struct_layout = Layout::Struct(field_layouts);
|
||||||
|
match argument {
|
||||||
|
StructValue(value) => {
|
||||||
|
let struct_value = cast_struct_struct(builder, value, struct_type);
|
||||||
|
|
||||||
let result = builder
|
let result = builder
|
||||||
.build_extract_value(struct_value, *index as u32, "")
|
.build_extract_value(struct_value, *index as u32, "")
|
||||||
.expect("desired field did not decode");
|
.expect("desired field did not decode");
|
||||||
|
|
||||||
if let Some(Layout::RecursivePointer) = field_layouts.get(*index as usize) {
|
if let Some(Layout::RecursivePointer) = field_layouts.get(*index as usize) {
|
||||||
let struct_layout = Layout::Struct(field_layouts);
|
let desired_type =
|
||||||
let desired_type = block_of_memory(env.context, &struct_layout, env.ptr_bytes);
|
block_of_memory(env.context, &struct_layout, env.ptr_bytes);
|
||||||
|
|
||||||
// the value is a pointer to the actual value; load that value!
|
// the value is a pointer to the actual value; load that value!
|
||||||
use inkwell::types::BasicType;
|
|
||||||
let ptr = cast_basic_basic(
|
let ptr = cast_basic_basic(
|
||||||
builder,
|
builder,
|
||||||
result,
|
result,
|
||||||
|
@ -1135,6 +1215,37 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
PointerValue(value) => {
|
||||||
|
let ptr = cast_basic_basic(
|
||||||
|
builder,
|
||||||
|
value.into(),
|
||||||
|
struct_type.ptr_type(AddressSpace::Generic).into(),
|
||||||
|
)
|
||||||
|
.into_pointer_value();
|
||||||
|
|
||||||
|
let elem_ptr = builder
|
||||||
|
.build_struct_gep(ptr, *index as u32, "at_index_struct_gep")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let result = builder.build_load(elem_ptr, "load_at_index_ptr");
|
||||||
|
|
||||||
|
if let Some(Layout::RecursivePointer) = field_layouts.get(*index as usize) {
|
||||||
|
// a recursive field is stored as a `i64*`, to use it we must cast it to
|
||||||
|
// a pointer to the block of memory representation
|
||||||
|
cast_basic_basic(
|
||||||
|
builder,
|
||||||
|
result,
|
||||||
|
block_of_memory(env.context, &struct_layout, env.ptr_bytes)
|
||||||
|
.ptr_type(AddressSpace::Generic)
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => panic!("cannot look up index in {:?}", argument),
|
||||||
|
}
|
||||||
|
}
|
||||||
EmptyArray => empty_polymorphic_list(env),
|
EmptyArray => empty_polymorphic_list(env),
|
||||||
Array { elem_layout, elems } => {
|
Array { elem_layout, elems } => {
|
||||||
let inplace = get_inplace_from_layout(layout);
|
let inplace = get_inplace_from_layout(layout);
|
||||||
|
@ -1178,12 +1289,10 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn allocate_with_refcount<'a, 'ctx, 'env>(
|
pub fn reserve_with_refcount<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout: &Layout<'a>,
|
layout: &Layout<'a>,
|
||||||
value: BasicValueEnum<'ctx>,
|
|
||||||
) -> PointerValue<'ctx> {
|
) -> PointerValue<'ctx> {
|
||||||
let builder = env.builder;
|
|
||||||
let ctx = env.context;
|
let ctx = env.context;
|
||||||
|
|
||||||
let len_type = env.ptr_int();
|
let len_type = env.ptr_int();
|
||||||
|
@ -1193,10 +1302,18 @@ pub fn allocate_with_refcount<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
let rc1 = crate::llvm::refcounting::refcount_1(ctx, env.ptr_bytes);
|
let rc1 = crate::llvm::refcounting::refcount_1(ctx, env.ptr_bytes);
|
||||||
|
|
||||||
let data_ptr = allocate_with_refcount_help(env, layout, value_bytes_intvalue, rc1);
|
allocate_with_refcount_help(env, layout, value_bytes_intvalue, rc1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn allocate_with_refcount<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
layout: &Layout<'a>,
|
||||||
|
value: BasicValueEnum<'ctx>,
|
||||||
|
) -> PointerValue<'ctx> {
|
||||||
|
let data_ptr = reserve_with_refcount(env, layout);
|
||||||
|
|
||||||
// store the value in the pointer
|
// store the value in the pointer
|
||||||
builder.build_store(data_ptr, value);
|
env.builder.build_store(data_ptr, value);
|
||||||
|
|
||||||
data_ptr
|
data_ptr
|
||||||
}
|
}
|
||||||
|
@ -1454,6 +1571,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
let mut stack = Vec::with_capacity_in(queue.len(), env.arena);
|
let mut stack = Vec::with_capacity_in(queue.len(), env.arena);
|
||||||
|
|
||||||
for (symbol, expr, layout) in queue {
|
for (symbol, expr, layout) in queue {
|
||||||
|
debug_assert!(layout != &Layout::RecursivePointer);
|
||||||
let context = &env.context;
|
let context = &env.context;
|
||||||
|
|
||||||
let val = build_exp_expr(env, layout_ids, &scope, parent, layout, &expr);
|
let val = build_exp_expr(env, layout_ids, &scope, parent, layout, &expr);
|
||||||
|
|
|
@ -137,7 +137,10 @@ pub fn basic_type_from_layout<'ctx>(
|
||||||
|
|
||||||
basic_type_from_record(arena, context, sorted_fields, ptr_bytes)
|
basic_type_from_record(arena, context, sorted_fields, ptr_bytes)
|
||||||
}
|
}
|
||||||
RecursiveUnion(_) | Union(_) => block_of_memory(context, layout, ptr_bytes),
|
RecursiveUnion(_) => block_of_memory(context, layout, ptr_bytes)
|
||||||
|
.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
|
||||||
|
|
|
@ -343,7 +343,8 @@ pub fn decrement_refcount_layout<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
|
|
||||||
RecursiveUnion(tags) => {
|
RecursiveUnion(tags) => {
|
||||||
build_dec_union(env, layout_ids, tags, value);
|
debug_assert!(value.is_pointer_value());
|
||||||
|
build_dec_rec_union(env, layout_ids, tags, value.into_pointer_value());
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionPointer(_, _) | Pointer(_) => {}
|
FunctionPointer(_, _) | Pointer(_) => {}
|
||||||
|
@ -427,7 +428,8 @@ pub fn increment_refcount_layout<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
|
|
||||||
RecursiveUnion(tags) => {
|
RecursiveUnion(tags) => {
|
||||||
build_inc_union(env, layout_ids, tags, value);
|
debug_assert!(value.is_pointer_value());
|
||||||
|
build_inc_rec_union(env, layout_ids, tags, value.into_pointer_value());
|
||||||
}
|
}
|
||||||
Closure(_, closure_layout, _) => {
|
Closure(_, closure_layout, _) => {
|
||||||
if closure_layout.contains_refcounted() {
|
if closure_layout.contains_refcounted() {
|
||||||
|
@ -1032,6 +1034,202 @@ pub fn build_header_help<'a, 'ctx, 'env>(
|
||||||
fn_val
|
fn_val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn build_dec_rec_union<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
fields: &'a [&'a [Layout<'a>]],
|
||||||
|
value: PointerValue<'ctx>,
|
||||||
|
) {
|
||||||
|
let layout = Layout::RecursiveUnion(fields);
|
||||||
|
|
||||||
|
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||||
|
let di_location = env.builder.get_current_debug_location().unwrap();
|
||||||
|
|
||||||
|
let symbol = Symbol::DEC;
|
||||||
|
let fn_name = layout_ids
|
||||||
|
.get(symbol, &layout)
|
||||||
|
.to_symbol_string(symbol, &env.interns);
|
||||||
|
|
||||||
|
let function = match env.module.get_function(fn_name.as_str()) {
|
||||||
|
Some(function_value) => function_value,
|
||||||
|
None => {
|
||||||
|
let function_value = build_header(env, &layout, &fn_name);
|
||||||
|
|
||||||
|
build_dec_rec_union_help(env, layout_ids, fields, function_value);
|
||||||
|
|
||||||
|
function_value
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
env.builder.position_at_end(block);
|
||||||
|
env.builder
|
||||||
|
.set_current_debug_location(env.context, di_location);
|
||||||
|
|
||||||
|
let call = env
|
||||||
|
.builder
|
||||||
|
.build_call(function, &[value.into()], "decrement_union");
|
||||||
|
|
||||||
|
call.set_call_convention(FAST_CALL_CONV);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_dec_rec_union_help<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
tags: &[&[Layout<'a>]],
|
||||||
|
fn_val: FunctionValue<'ctx>,
|
||||||
|
) {
|
||||||
|
debug_assert!(!tags.is_empty());
|
||||||
|
|
||||||
|
use inkwell::types::BasicType;
|
||||||
|
|
||||||
|
let context = &env.context;
|
||||||
|
let builder = env.builder;
|
||||||
|
|
||||||
|
// Add a basic block for the entry point
|
||||||
|
let entry = context.append_basic_block(fn_val, "entry");
|
||||||
|
|
||||||
|
builder.position_at_end(entry);
|
||||||
|
|
||||||
|
let func_scope = fn_val.get_subprogram().unwrap();
|
||||||
|
let lexical_block = env.dibuilder.create_lexical_block(
|
||||||
|
/* scope */ func_scope.as_debug_info_scope(),
|
||||||
|
/* file */ env.compile_unit.get_file(),
|
||||||
|
/* line_no */ 0,
|
||||||
|
/* column_no */ 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
let loc = env.dibuilder.create_debug_location(
|
||||||
|
context,
|
||||||
|
/* line */ 0,
|
||||||
|
/* column */ 0,
|
||||||
|
/* current_scope */ lexical_block.as_debug_info_scope(),
|
||||||
|
/* inlined_at */ None,
|
||||||
|
);
|
||||||
|
builder.set_current_debug_location(&context, loc);
|
||||||
|
|
||||||
|
// Add args to scope
|
||||||
|
let arg_symbol = Symbol::ARG_1;
|
||||||
|
|
||||||
|
let arg_val = fn_val.get_param_iter().next().unwrap();
|
||||||
|
|
||||||
|
set_name(arg_val, arg_symbol.ident_string(&env.interns));
|
||||||
|
|
||||||
|
let parent = fn_val;
|
||||||
|
|
||||||
|
let layout = Layout::RecursiveUnion(tags);
|
||||||
|
let before_block = env.builder.get_insert_block().expect("to be in a function");
|
||||||
|
|
||||||
|
debug_assert!(arg_val.is_pointer_value());
|
||||||
|
let value_ptr = arg_val.into_pointer_value();
|
||||||
|
|
||||||
|
// next, make a jump table for all possible values of the tag_id
|
||||||
|
let mut cases = Vec::with_capacity_in(tags.len(), env.arena);
|
||||||
|
|
||||||
|
let merge_block = env.context.append_basic_block(parent, "decrement_merge");
|
||||||
|
|
||||||
|
builder.set_current_debug_location(&context, loc);
|
||||||
|
|
||||||
|
for (tag_id, field_layouts) in tags.iter().enumerate() {
|
||||||
|
// if none of the fields are or contain anything refcounted, just move on
|
||||||
|
if !field_layouts
|
||||||
|
.iter()
|
||||||
|
.any(|x| x.is_refcounted() || x.contains_refcounted())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let block = env.context.append_basic_block(parent, "tag_id_decrement");
|
||||||
|
env.builder.position_at_end(block);
|
||||||
|
|
||||||
|
let wrapper_type = basic_type_from_layout(
|
||||||
|
env.arena,
|
||||||
|
env.context,
|
||||||
|
&Layout::Struct(field_layouts),
|
||||||
|
env.ptr_bytes,
|
||||||
|
);
|
||||||
|
|
||||||
|
// cast the opaque pointer to a pointer of the correct shape
|
||||||
|
let struct_ptr = cast_basic_basic(
|
||||||
|
env.builder,
|
||||||
|
value_ptr.into(),
|
||||||
|
wrapper_type.ptr_type(AddressSpace::Generic).into(),
|
||||||
|
)
|
||||||
|
.into_pointer_value();
|
||||||
|
|
||||||
|
for (i, field_layout) in field_layouts.iter().enumerate() {
|
||||||
|
if let Layout::RecursivePointer = field_layout {
|
||||||
|
// this field has type `*i64`, but is really a pointer to the data we want
|
||||||
|
let elem_pointer = env
|
||||||
|
.builder
|
||||||
|
.build_struct_gep(struct_ptr, i as u32, "gep_recursive_pointer")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let ptr_as_i64_ptr = env
|
||||||
|
.builder
|
||||||
|
.build_load(elem_pointer, "load_recursive_pointer");
|
||||||
|
|
||||||
|
debug_assert!(ptr_as_i64_ptr.is_pointer_value());
|
||||||
|
|
||||||
|
// therefore we must cast it to our desired type
|
||||||
|
let union_type =
|
||||||
|
basic_type_from_layout(env.arena, env.context, &layout, env.ptr_bytes);
|
||||||
|
let recursive_field_ptr = cast_basic_basic(env.builder, ptr_as_i64_ptr, union_type);
|
||||||
|
|
||||||
|
// recursively decrement the field
|
||||||
|
let call = env.builder.build_call(
|
||||||
|
fn_val,
|
||||||
|
&[recursive_field_ptr],
|
||||||
|
"recursive_tag_decrement",
|
||||||
|
);
|
||||||
|
|
||||||
|
// Because it's an internal-only function, use the fast calling convention.
|
||||||
|
call.set_call_convention(FAST_CALL_CONV);
|
||||||
|
} else if field_layout.contains_refcounted() {
|
||||||
|
// TODO this loads the whole field onto the stack;
|
||||||
|
// that's wasteful if e.g. the field is a big record, where only
|
||||||
|
// some fields are actually refcounted.
|
||||||
|
let elem_pointer = env
|
||||||
|
.builder
|
||||||
|
.build_struct_gep(struct_ptr, i as u32, "gep_recursive_pointer")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let field = env
|
||||||
|
.builder
|
||||||
|
.build_load(elem_pointer, "decrement_struct_field");
|
||||||
|
|
||||||
|
decrement_refcount_layout(env, parent, layout_ids, field, field_layout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
env.builder.build_unconditional_branch(merge_block);
|
||||||
|
|
||||||
|
cases.push((
|
||||||
|
env.context.i64_type().const_int(tag_id as u64, false),
|
||||||
|
block,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
cases.reverse();
|
||||||
|
|
||||||
|
env.builder.position_at_end(before_block);
|
||||||
|
|
||||||
|
// read the tag_id
|
||||||
|
let current_tag_id = rec_union_read_tag(env, value_ptr);
|
||||||
|
|
||||||
|
// switch on it
|
||||||
|
env.builder
|
||||||
|
.build_switch(current_tag_id, merge_block, &cases);
|
||||||
|
|
||||||
|
env.builder.position_at_end(merge_block);
|
||||||
|
|
||||||
|
// decrement this cons-cell itself
|
||||||
|
let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, value_ptr);
|
||||||
|
refcount_ptr.decrement(env, &layout);
|
||||||
|
|
||||||
|
// this function returns void
|
||||||
|
builder.build_return(None);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build_dec_union<'a, 'ctx, 'env>(
|
pub fn build_dec_union<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
@ -1078,8 +1276,6 @@ pub fn build_dec_union_help<'a, 'ctx, 'env>(
|
||||||
) {
|
) {
|
||||||
debug_assert!(!tags.is_empty());
|
debug_assert!(!tags.is_empty());
|
||||||
|
|
||||||
use inkwell::types::BasicType;
|
|
||||||
|
|
||||||
let context = &env.context;
|
let context = &env.context;
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
|
|
||||||
|
@ -1105,32 +1301,18 @@ pub fn build_dec_union_help<'a, 'ctx, 'env>(
|
||||||
);
|
);
|
||||||
builder.set_current_debug_location(&context, loc);
|
builder.set_current_debug_location(&context, loc);
|
||||||
|
|
||||||
let mut scope = Scope::default();
|
|
||||||
|
|
||||||
// Add args to scope
|
// Add args to scope
|
||||||
let arg_symbol = Symbol::ARG_1;
|
let arg_symbol = Symbol::ARG_1;
|
||||||
let layout = Layout::Union(tags);
|
|
||||||
|
|
||||||
let arg_val = fn_val.get_param_iter().next().unwrap();
|
let arg_val = fn_val.get_param_iter().next().unwrap();
|
||||||
|
|
||||||
set_name(arg_val, arg_symbol.ident_string(&env.interns));
|
set_name(arg_val, arg_symbol.ident_string(&env.interns));
|
||||||
|
|
||||||
let alloca = create_entry_block_alloca(
|
|
||||||
env,
|
|
||||||
fn_val,
|
|
||||||
arg_val.get_type(),
|
|
||||||
arg_symbol.ident_string(&env.interns),
|
|
||||||
);
|
|
||||||
|
|
||||||
builder.build_store(alloca, arg_val);
|
|
||||||
|
|
||||||
scope.insert(arg_symbol, (layout.clone(), alloca));
|
|
||||||
|
|
||||||
let parent = fn_val;
|
let parent = fn_val;
|
||||||
|
|
||||||
let layout = Layout::RecursiveUnion(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");
|
||||||
|
|
||||||
|
debug_assert!(arg_val.is_struct_value());
|
||||||
let wrapper_struct = arg_val.into_struct_value();
|
let wrapper_struct = arg_val.into_struct_value();
|
||||||
|
|
||||||
// next, make a jump table for all possible values of the tag_id
|
// next, make a jump table for all possible values of the tag_id
|
||||||
|
@ -1164,39 +1346,7 @@ pub fn build_dec_union_help<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
for (i, field_layout) in field_layouts.iter().enumerate() {
|
for (i, field_layout) in field_layouts.iter().enumerate() {
|
||||||
if let Layout::RecursivePointer = field_layout {
|
if let Layout::RecursivePointer = field_layout {
|
||||||
// this field has type `*i64`, but is really a pointer to the data we want
|
panic!("a non-recursive tag union cannot contain RecursivePointer");
|
||||||
let ptr_as_i64_ptr = env
|
|
||||||
.builder
|
|
||||||
.build_extract_value(wrapper_struct, i as u32, "decrement_struct_field")
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
debug_assert!(ptr_as_i64_ptr.is_pointer_value());
|
|
||||||
|
|
||||||
// therefore we must cast it to our desired type
|
|
||||||
let union_type = block_of_memory(env.context, &layout, env.ptr_bytes);
|
|
||||||
let recursive_field_ptr = cast_basic_basic(
|
|
||||||
env.builder,
|
|
||||||
ptr_as_i64_ptr,
|
|
||||||
union_type.ptr_type(AddressSpace::Generic).into(),
|
|
||||||
)
|
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
let recursive_field = env
|
|
||||||
.builder
|
|
||||||
.build_load(recursive_field_ptr, "load_recursive_field");
|
|
||||||
|
|
||||||
// recursively decrement the field
|
|
||||||
let call =
|
|
||||||
env.builder
|
|
||||||
.build_call(fn_val, &[recursive_field], "recursive_tag_decrement");
|
|
||||||
|
|
||||||
// Because it's an internal-only function, use the fast calling convention.
|
|
||||||
call.set_call_convention(FAST_CALL_CONV);
|
|
||||||
|
|
||||||
// TODO do this decrement before the recursive call?
|
|
||||||
// Then the recursive call is potentially TCE'd
|
|
||||||
let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, recursive_field_ptr);
|
|
||||||
refcount_ptr.decrement(env, &layout);
|
|
||||||
} else if field_layout.contains_refcounted() {
|
} else if field_layout.contains_refcounted() {
|
||||||
let field_ptr = env
|
let field_ptr = env
|
||||||
.builder
|
.builder
|
||||||
|
@ -1244,6 +1394,213 @@ pub fn build_dec_union_help<'a, 'ctx, 'env>(
|
||||||
builder.build_return(None);
|
builder.build_return(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn build_inc_rec_union<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
fields: &'a [&'a [Layout<'a>]],
|
||||||
|
value: PointerValue<'ctx>,
|
||||||
|
) {
|
||||||
|
let layout = Layout::RecursiveUnion(fields);
|
||||||
|
|
||||||
|
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||||
|
let di_location = env.builder.get_current_debug_location().unwrap();
|
||||||
|
|
||||||
|
let symbol = Symbol::INC;
|
||||||
|
let fn_name = layout_ids
|
||||||
|
.get(symbol, &layout)
|
||||||
|
.to_symbol_string(symbol, &env.interns);
|
||||||
|
|
||||||
|
let function = match env.module.get_function(fn_name.as_str()) {
|
||||||
|
Some(function_value) => function_value,
|
||||||
|
None => {
|
||||||
|
let function_value = build_header(env, &layout, &fn_name);
|
||||||
|
|
||||||
|
build_inc_rec_union_help(env, layout_ids, fields, function_value);
|
||||||
|
|
||||||
|
function_value
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
env.builder.position_at_end(block);
|
||||||
|
env.builder
|
||||||
|
.set_current_debug_location(env.context, di_location);
|
||||||
|
let call = env
|
||||||
|
.builder
|
||||||
|
.build_call(function, &[value.into()], "increment_union");
|
||||||
|
|
||||||
|
call.set_call_convention(FAST_CALL_CONV);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rec_union_read_tag<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
value_ptr: PointerValue<'ctx>,
|
||||||
|
) -> IntValue<'ctx> {
|
||||||
|
// Assumption: the tag is the first thing stored
|
||||||
|
// so cast the pointer to the data to a `i64*`
|
||||||
|
let tag_ptr = cast_basic_basic(
|
||||||
|
env.builder,
|
||||||
|
value_ptr.into(),
|
||||||
|
env.context
|
||||||
|
.i64_type()
|
||||||
|
.ptr_type(AddressSpace::Generic)
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
.into_pointer_value();
|
||||||
|
|
||||||
|
env.builder
|
||||||
|
.build_load(tag_ptr, "load_tag_id")
|
||||||
|
.into_int_value()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_inc_rec_union_help<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
tags: &[&[Layout<'a>]],
|
||||||
|
fn_val: FunctionValue<'ctx>,
|
||||||
|
) {
|
||||||
|
debug_assert!(!tags.is_empty());
|
||||||
|
|
||||||
|
use inkwell::types::BasicType;
|
||||||
|
|
||||||
|
let context = &env.context;
|
||||||
|
let builder = env.builder;
|
||||||
|
|
||||||
|
// Add a basic block for the entry point
|
||||||
|
let entry = context.append_basic_block(fn_val, "entry");
|
||||||
|
|
||||||
|
builder.position_at_end(entry);
|
||||||
|
|
||||||
|
let func_scope = fn_val.get_subprogram().unwrap();
|
||||||
|
let lexical_block = env.dibuilder.create_lexical_block(
|
||||||
|
/* scope */ func_scope.as_debug_info_scope(),
|
||||||
|
/* file */ env.compile_unit.get_file(),
|
||||||
|
/* line_no */ 0,
|
||||||
|
/* column_no */ 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
let loc = env.dibuilder.create_debug_location(
|
||||||
|
context,
|
||||||
|
/* line */ 0,
|
||||||
|
/* column */ 0,
|
||||||
|
/* current_scope */ lexical_block.as_debug_info_scope(),
|
||||||
|
/* inlined_at */ None,
|
||||||
|
);
|
||||||
|
builder.set_current_debug_location(&context, loc);
|
||||||
|
|
||||||
|
// Add args to scope
|
||||||
|
let arg_symbol = Symbol::ARG_1;
|
||||||
|
let arg_val = fn_val.get_param_iter().next().unwrap();
|
||||||
|
|
||||||
|
set_name(arg_val, arg_symbol.ident_string(&env.interns));
|
||||||
|
|
||||||
|
let parent = fn_val;
|
||||||
|
|
||||||
|
let layout = Layout::RecursiveUnion(tags);
|
||||||
|
let before_block = env.builder.get_insert_block().expect("to be in a function");
|
||||||
|
|
||||||
|
debug_assert!(arg_val.is_pointer_value());
|
||||||
|
let value_ptr = arg_val.into_pointer_value();
|
||||||
|
|
||||||
|
// read the tag_id
|
||||||
|
let tag_id = rec_union_read_tag(env, value_ptr);
|
||||||
|
|
||||||
|
let tag_id_u8 = cast_basic_basic(env.builder, tag_id.into(), env.context.i8_type().into());
|
||||||
|
|
||||||
|
// next, make a jump table for all possible values of the tag_id
|
||||||
|
let mut cases = Vec::with_capacity_in(tags.len(), env.arena);
|
||||||
|
|
||||||
|
let merge_block = env.context.append_basic_block(parent, "increment_merge");
|
||||||
|
|
||||||
|
for (tag_id, field_layouts) in tags.iter().enumerate() {
|
||||||
|
// if none of the fields are or contain anything refcounted, just move on
|
||||||
|
if !field_layouts
|
||||||
|
.iter()
|
||||||
|
.any(|x| x.is_refcounted() || x.contains_refcounted())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let block = env.context.append_basic_block(parent, "tag_id_increment");
|
||||||
|
env.builder.position_at_end(block);
|
||||||
|
|
||||||
|
let wrapper_type = basic_type_from_layout(
|
||||||
|
env.arena,
|
||||||
|
env.context,
|
||||||
|
&Layout::Struct(field_layouts),
|
||||||
|
env.ptr_bytes,
|
||||||
|
);
|
||||||
|
|
||||||
|
// cast the opaque pointer to a pointer of the correct shape
|
||||||
|
let struct_ptr = cast_basic_basic(
|
||||||
|
env.builder,
|
||||||
|
value_ptr.into(),
|
||||||
|
wrapper_type.ptr_type(AddressSpace::Generic).into(),
|
||||||
|
)
|
||||||
|
.into_pointer_value();
|
||||||
|
|
||||||
|
for (i, field_layout) in field_layouts.iter().enumerate() {
|
||||||
|
if let Layout::RecursivePointer = field_layout {
|
||||||
|
// this field has type `*i64`, but is really a pointer to the data we want
|
||||||
|
let elem_pointer = env
|
||||||
|
.builder
|
||||||
|
.build_struct_gep(struct_ptr, i as u32, "gep_recursive_pointer")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let ptr_as_i64_ptr = env
|
||||||
|
.builder
|
||||||
|
.build_load(elem_pointer, "load_recursive_pointer");
|
||||||
|
|
||||||
|
debug_assert!(ptr_as_i64_ptr.is_pointer_value());
|
||||||
|
|
||||||
|
// therefore we must cast it to our desired type
|
||||||
|
let union_type = block_of_memory(env.context, &layout, env.ptr_bytes);
|
||||||
|
let recursive_field_ptr = cast_basic_basic(
|
||||||
|
env.builder,
|
||||||
|
ptr_as_i64_ptr,
|
||||||
|
union_type.ptr_type(AddressSpace::Generic).into(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// recursively increment the field
|
||||||
|
let call = env.builder.build_call(
|
||||||
|
fn_val,
|
||||||
|
&[recursive_field_ptr],
|
||||||
|
"recursive_tag_increment",
|
||||||
|
);
|
||||||
|
|
||||||
|
// Because it's an internal-only function, use the fast calling convention.
|
||||||
|
call.set_call_convention(FAST_CALL_CONV);
|
||||||
|
} else if field_layout.contains_refcounted() {
|
||||||
|
let elem_pointer = env
|
||||||
|
.builder
|
||||||
|
.build_struct_gep(struct_ptr, i as u32, "gep_field")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let field = env.builder.build_load(elem_pointer, "load_field");
|
||||||
|
|
||||||
|
increment_refcount_layout(env, parent, layout_ids, field, field_layout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
env.builder.build_unconditional_branch(merge_block);
|
||||||
|
|
||||||
|
cases.push((env.context.i8_type().const_int(tag_id as u64, false), block));
|
||||||
|
}
|
||||||
|
|
||||||
|
env.builder.position_at_end(before_block);
|
||||||
|
|
||||||
|
env.builder
|
||||||
|
.build_switch(tag_id_u8.into_int_value(), merge_block, &cases);
|
||||||
|
|
||||||
|
env.builder.position_at_end(merge_block);
|
||||||
|
|
||||||
|
// increment this cons cell
|
||||||
|
let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, value_ptr);
|
||||||
|
refcount_ptr.increment(env);
|
||||||
|
|
||||||
|
// this function returns void
|
||||||
|
builder.build_return(None);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build_inc_union<'a, 'ctx, 'env>(
|
pub fn build_inc_union<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
@ -1316,26 +1673,12 @@ pub fn build_inc_union_help<'a, 'ctx, 'env>(
|
||||||
);
|
);
|
||||||
builder.set_current_debug_location(&context, loc);
|
builder.set_current_debug_location(&context, loc);
|
||||||
|
|
||||||
let mut scope = Scope::default();
|
|
||||||
|
|
||||||
// Add args to scope
|
// Add args to scope
|
||||||
let arg_symbol = Symbol::ARG_1;
|
let arg_symbol = Symbol::ARG_1;
|
||||||
let layout = Layout::Union(tags);
|
|
||||||
let arg_val = fn_val.get_param_iter().next().unwrap();
|
let arg_val = fn_val.get_param_iter().next().unwrap();
|
||||||
|
|
||||||
set_name(arg_val, arg_symbol.ident_string(&env.interns));
|
set_name(arg_val, arg_symbol.ident_string(&env.interns));
|
||||||
|
|
||||||
let alloca = create_entry_block_alloca(
|
|
||||||
env,
|
|
||||||
fn_val,
|
|
||||||
arg_val.get_type(),
|
|
||||||
arg_symbol.ident_string(&env.interns),
|
|
||||||
);
|
|
||||||
|
|
||||||
builder.build_store(alloca, arg_val);
|
|
||||||
|
|
||||||
scope.insert(arg_symbol, (layout.clone(), alloca));
|
|
||||||
|
|
||||||
let parent = fn_val;
|
let parent = fn_val;
|
||||||
|
|
||||||
let layout = Layout::RecursiveUnion(tags);
|
let layout = Layout::RecursiveUnion(tags);
|
||||||
|
|
|
@ -564,7 +564,7 @@ mod gen_primitives {
|
||||||
Cons _ rest -> 1 + len rest
|
Cons _ rest -> 1 + len rest
|
||||||
|
|
||||||
main =
|
main =
|
||||||
nil : LinkedList (Int *)
|
nil : LinkedList {}
|
||||||
nil = Nil
|
nil = Nil
|
||||||
|
|
||||||
len nil
|
len nil
|
||||||
|
@ -1122,6 +1122,40 @@ mod gen_primitives {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn linked_list_is_singleton() {
|
||||||
|
assert_non_opt_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
|
ConsList a : [ Cons a (ConsList a), Nil ]
|
||||||
|
|
||||||
|
empty : ConsList a
|
||||||
|
empty = Nil
|
||||||
|
|
||||||
|
isSingleton : ConsList a -> Bool
|
||||||
|
isSingleton = \list ->
|
||||||
|
when list is
|
||||||
|
Cons _ Nil ->
|
||||||
|
True
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
False
|
||||||
|
|
||||||
|
main : Bool
|
||||||
|
main =
|
||||||
|
myList : ConsList I64
|
||||||
|
myList = empty
|
||||||
|
|
||||||
|
isSingleton myList
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
false,
|
||||||
|
bool
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn linked_list_is_empty_1() {
|
fn linked_list_is_empty_1() {
|
||||||
assert_non_opt_evals_to!(
|
assert_non_opt_evals_to!(
|
||||||
|
@ -1176,7 +1210,7 @@ mod gen_primitives {
|
||||||
|
|
||||||
main : Bool
|
main : Bool
|
||||||
main =
|
main =
|
||||||
myList : ConsList (Int *)
|
myList : ConsList I64
|
||||||
myList = Cons 0x1 Nil
|
myList = Cons 0x1 Nil
|
||||||
|
|
||||||
isEmpty myList
|
isEmpty myList
|
||||||
|
@ -1187,6 +1221,26 @@ mod gen_primitives {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn linked_list_singleton() {
|
||||||
|
// verifies only that valid llvm is produced
|
||||||
|
assert_non_opt_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
|
ConsList a : [ Cons a (ConsList a), Nil ]
|
||||||
|
|
||||||
|
main : ConsList I64
|
||||||
|
main = Cons 0x1 Nil
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
0,
|
||||||
|
i64,
|
||||||
|
|_| 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn recursive_functon_with_rigid() {
|
fn recursive_functon_with_rigid() {
|
||||||
assert_non_opt_evals_to!(
|
assert_non_opt_evals_to!(
|
||||||
|
@ -1354,7 +1408,8 @@ mod gen_primitives {
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
1,
|
1,
|
||||||
i64
|
&i64,
|
||||||
|
|x: &i64| *x
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1451,7 +1506,8 @@ mod gen_primitives {
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
1,
|
1,
|
||||||
i64
|
&i64,
|
||||||
|
|x: &i64| *x
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2731,7 +2731,10 @@ pub fn with_hole<'a>(
|
||||||
let iter = field_symbols_temp.into_iter().map(|(_, _, data)| data);
|
let iter = field_symbols_temp.into_iter().map(|(_, _, data)| data);
|
||||||
assign_to_symbols(env, procs, layout_cache, iter, stmt)
|
assign_to_symbols(env, procs, layout_cache, iter, stmt)
|
||||||
}
|
}
|
||||||
Wrapped(sorted_tag_layouts) => {
|
Wrapped {
|
||||||
|
sorted_tag_layouts,
|
||||||
|
is_recursive,
|
||||||
|
} => {
|
||||||
let union_size = sorted_tag_layouts.len() as u8;
|
let union_size = sorted_tag_layouts.len() as u8;
|
||||||
let (tag_id, (_, _)) = sorted_tag_layouts
|
let (tag_id, (_, _)) = sorted_tag_layouts
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -2786,7 +2789,12 @@ pub fn with_hole<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
let field_symbols = field_symbols.into_bump_slice();
|
let field_symbols = field_symbols.into_bump_slice();
|
||||||
let layout = Layout::Union(layouts.into_bump_slice());
|
let layout = if is_recursive {
|
||||||
|
Layout::RecursiveUnion(layouts.into_bump_slice())
|
||||||
|
} else {
|
||||||
|
Layout::Union(layouts.into_bump_slice())
|
||||||
|
};
|
||||||
|
|
||||||
let tag = Expr::Tag {
|
let tag = Expr::Tag {
|
||||||
tag_layout: layout.clone(),
|
tag_layout: layout.clone(),
|
||||||
tag_name,
|
tag_name,
|
||||||
|
@ -4794,6 +4802,12 @@ fn store_pattern<'a>(
|
||||||
for (index, (argument, arg_layout)) in arguments.iter().enumerate().rev() {
|
for (index, (argument, arg_layout)) in arguments.iter().enumerate().rev() {
|
||||||
let index = if write_tag { index + 1 } else { index };
|
let index = if write_tag { index + 1 } else { index };
|
||||||
|
|
||||||
|
let mut arg_layout = arg_layout;
|
||||||
|
|
||||||
|
if let Layout::RecursivePointer = arg_layout {
|
||||||
|
arg_layout = layout;
|
||||||
|
}
|
||||||
|
|
||||||
let load = Expr::AccessAtIndex {
|
let load = Expr::AccessAtIndex {
|
||||||
wrapped,
|
wrapped,
|
||||||
index: index as u64,
|
index: index as u64,
|
||||||
|
@ -5857,7 +5871,10 @@ fn from_can_pattern_help<'a>(
|
||||||
layout,
|
layout,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Wrapped(tags) => {
|
Wrapped {
|
||||||
|
sorted_tag_layouts: tags,
|
||||||
|
is_recursive,
|
||||||
|
} => {
|
||||||
let mut ctors = std::vec::Vec::with_capacity(tags.len());
|
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 {
|
||||||
|
@ -5912,7 +5929,11 @@ fn from_can_pattern_help<'a>(
|
||||||
layouts.push(arg_layouts);
|
layouts.push(arg_layouts);
|
||||||
}
|
}
|
||||||
|
|
||||||
let layout = Layout::Union(layouts.into_bump_slice());
|
let layout = if is_recursive {
|
||||||
|
Layout::RecursiveUnion(layouts.into_bump_slice())
|
||||||
|
} else {
|
||||||
|
Layout::Union(layouts.into_bump_slice())
|
||||||
|
};
|
||||||
|
|
||||||
Pattern::AppliedTag {
|
Pattern::AppliedTag {
|
||||||
tag_name: tag_name.clone(),
|
tag_name: tag_name.clone(),
|
||||||
|
|
|
@ -158,7 +158,13 @@ impl<'a> ClosureLayout<'a> {
|
||||||
|
|
||||||
Ok(Some(closure_layout))
|
Ok(Some(closure_layout))
|
||||||
}
|
}
|
||||||
Wrapped(tags) => {
|
Wrapped {
|
||||||
|
sorted_tag_layouts: tags,
|
||||||
|
is_recursive,
|
||||||
|
} => {
|
||||||
|
// TODO handle recursive closures
|
||||||
|
debug_assert!(!is_recursive);
|
||||||
|
|
||||||
let closure_layout =
|
let closure_layout =
|
||||||
ClosureLayout::from_tag_union(arena, tags.into_bump_slice());
|
ClosureLayout::from_tag_union(arena, tags.into_bump_slice());
|
||||||
|
|
||||||
|
@ -1095,10 +1101,16 @@ pub enum UnionVariant<'a> {
|
||||||
Never,
|
Never,
|
||||||
Unit,
|
Unit,
|
||||||
UnitWithArguments,
|
UnitWithArguments,
|
||||||
BoolUnion { ttrue: TagName, ffalse: TagName },
|
BoolUnion {
|
||||||
|
ttrue: TagName,
|
||||||
|
ffalse: TagName,
|
||||||
|
},
|
||||||
ByteUnion(Vec<'a, TagName>),
|
ByteUnion(Vec<'a, TagName>),
|
||||||
Unwrapped(Vec<'a, Layout<'a>>),
|
Unwrapped(Vec<'a, Layout<'a>>),
|
||||||
Wrapped(Vec<'a, (TagName, &'a [Layout<'a>])>),
|
Wrapped {
|
||||||
|
sorted_tag_layouts: Vec<'a, (TagName, &'a [Layout<'a>])>,
|
||||||
|
is_recursive: bool,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn union_sorted_tags<'a>(arena: &'a Bump, var: Variable, subs: &Subs) -> UnionVariant<'a> {
|
pub fn union_sorted_tags<'a>(arena: &'a Bump, var: Variable, subs: &Subs) -> UnionVariant<'a> {
|
||||||
|
@ -1277,7 +1289,10 @@ pub fn union_sorted_tags_help<'a>(
|
||||||
|
|
||||||
UnionVariant::ByteUnion(tag_names)
|
UnionVariant::ByteUnion(tag_names)
|
||||||
}
|
}
|
||||||
_ => UnionVariant::Wrapped(answer),
|
_ => UnionVariant::Wrapped {
|
||||||
|
sorted_tag_layouts: answer,
|
||||||
|
is_recursive: opt_rec_var.is_some(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1316,17 +1331,25 @@ pub fn layout_from_tag_union<'a>(
|
||||||
Layout::Struct(field_layouts.into_bump_slice())
|
Layout::Struct(field_layouts.into_bump_slice())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Wrapped(tags) => {
|
Wrapped {
|
||||||
|
sorted_tag_layouts: tags,
|
||||||
|
is_recursive,
|
||||||
|
} => {
|
||||||
let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena);
|
let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena);
|
||||||
|
|
||||||
for (_, tag_layout) in tags {
|
for (_, tag_layout) in tags {
|
||||||
tag_layouts.push(tag_layout);
|
tag_layouts.push(tag_layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if is_recursive {
|
||||||
|
Layout::RecursiveUnion(tag_layouts.into_bump_slice())
|
||||||
|
} else {
|
||||||
Layout::Union(tag_layouts.into_bump_slice())
|
Layout::Union(tag_layouts.into_bump_slice())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
|
|
|
@ -1893,6 +1893,7 @@ mod test_mono {
|
||||||
let Test.2 = S Test.9 Test.8;
|
let Test.2 = S Test.9 Test.8;
|
||||||
let Test.5 = 1i64;
|
let Test.5 = 1i64;
|
||||||
let Test.6 = Index 0 Test.2;
|
let Test.6 = Index 0 Test.2;
|
||||||
|
dec Test.2;
|
||||||
let Test.7 = lowlevel Eq Test.5 Test.6;
|
let Test.7 = lowlevel Eq Test.5 Test.6;
|
||||||
if Test.7 then
|
if Test.7 then
|
||||||
let Test.3 = 0i64;
|
let Test.3 = 0i64;
|
||||||
|
@ -1944,12 +1945,16 @@ mod test_mono {
|
||||||
let Test.10 = lowlevel Eq Test.8 Test.9;
|
let Test.10 = lowlevel Eq Test.8 Test.9;
|
||||||
if Test.10 then
|
if Test.10 then
|
||||||
let Test.4 = Index 1 Test.2;
|
let Test.4 = Index 1 Test.2;
|
||||||
|
inc Test.4;
|
||||||
|
dec Test.2;
|
||||||
let Test.3 = 1i64;
|
let Test.3 = 1i64;
|
||||||
ret Test.3;
|
ret Test.3;
|
||||||
else
|
else
|
||||||
|
dec Test.2;
|
||||||
let Test.5 = 0i64;
|
let Test.5 = 0i64;
|
||||||
ret Test.5;
|
ret Test.5;
|
||||||
else
|
else
|
||||||
|
dec Test.2;
|
||||||
let Test.6 = 0i64;
|
let Test.6 = 0i64;
|
||||||
ret Test.6;
|
ret Test.6;
|
||||||
"#
|
"#
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue