Merge pull request #1916 from rtfeldman/tag-union-imitate-rust

Tag union imitate rust
This commit is contained in:
Richard Feldman 2021-11-13 08:13:32 -08:00 committed by GitHub
commit 1c6fab7043
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 799 additions and 325 deletions

View file

@ -247,7 +247,7 @@ pub fn build_file<'a>(
link( link(
target, target,
binary_path.clone(), binary_path.clone(),
&inputs, &inputs,
link_type link_type
) )
.map_err(|_| { .map_err(|_| {

View file

@ -218,8 +218,8 @@ pub fn gen_and_eval<'a>(
// Verify the module // Verify the module
if let Err(errors) = env.module.verify() { if let Err(errors) = env.module.verify() {
panic!( panic!(
"Errors defining module: {}\n\nUncomment things nearby to see more details.", "Errors defining module:\n{}\n\nUncomment things nearby to see more details.",
errors errors.to_string()
); );
} }

View file

@ -128,20 +128,19 @@ fn build_has_tag_id_help<'a, 'ctx, 'env>(
[tag_id, tag_value_ptr] => { [tag_id, tag_value_ptr] => {
let tag_type = basic_type_from_layout(env, &Layout::Union(union_layout)); let tag_type = basic_type_from_layout(env, &Layout::Union(union_layout));
let argument_cast = env let tag_value = env.builder.build_pointer_cast(
.builder tag_value_ptr.into_pointer_value(),
.build_bitcast( tag_type.ptr_type(AddressSpace::Generic),
*tag_value_ptr, "load_opaque_get_tag_id",
tag_type.ptr_type(AddressSpace::Generic), );
"load_opaque",
)
.into_pointer_value();
let tag_value = env.builder.build_load(argument_cast, "get_value");
let actual_tag_id = { let actual_tag_id = {
let tag_id_i64 = let tag_id_i64 = crate::llvm::build::get_tag_id(
crate::llvm::build::get_tag_id(env, function_value, &union_layout, tag_value); env,
function_value,
&union_layout,
tag_value.into(),
);
env.builder env.builder
.build_int_cast(tag_id_i64, env.context.i16_type(), "to_i16") .build_int_cast(tag_id_i64, env.context.i16_type(), "to_i16")
@ -155,18 +154,11 @@ fn build_has_tag_id_help<'a, 'ctx, 'env>(
); );
let tag_data_ptr = { let tag_data_ptr = {
let data_index = env let ptr = env
.context .builder
.i64_type() .build_struct_gep(tag_value, TAG_DATA_INDEX, "get_data_ptr")
.const_int(TAG_DATA_INDEX as u64, false); .unwrap();
let ptr = unsafe {
env.builder.build_gep(
tag_value_ptr.into_pointer_value(),
&[data_index],
"get_data_ptr",
)
};
env.builder.build_bitcast(ptr, i8_ptr_type, "to_opaque") env.builder.build_bitcast(ptr, i8_ptr_type, "to_opaque")
}; };
@ -191,6 +183,7 @@ pub fn build_transform_caller<'a, 'ctx, 'env>(
function: FunctionValue<'ctx>, function: FunctionValue<'ctx>,
closure_data_layout: LambdaSet<'a>, closure_data_layout: LambdaSet<'a>,
argument_layouts: &[Layout<'a>], argument_layouts: &[Layout<'a>],
result_layout: Layout<'a>,
) -> FunctionValue<'ctx> { ) -> FunctionValue<'ctx> {
let fn_name: &str = &format!( let fn_name: &str = &format!(
"{}_zig_function_caller", "{}_zig_function_caller",
@ -204,6 +197,7 @@ pub fn build_transform_caller<'a, 'ctx, 'env>(
function, function,
closure_data_layout, closure_data_layout,
argument_layouts, argument_layouts,
result_layout,
fn_name, fn_name,
), ),
} }
@ -214,6 +208,7 @@ fn build_transform_caller_help<'a, 'ctx, 'env>(
roc_function: FunctionValue<'ctx>, roc_function: FunctionValue<'ctx>,
closure_data_layout: LambdaSet<'a>, closure_data_layout: LambdaSet<'a>,
argument_layouts: &[Layout<'a>], argument_layouts: &[Layout<'a>],
result_layout: Layout<'a>,
fn_name: &str, fn_name: &str,
) -> FunctionValue<'ctx> { ) -> FunctionValue<'ctx> {
debug_assert!(argument_layouts.len() <= 7); debug_assert!(argument_layouts.len() <= 7);
@ -260,12 +255,22 @@ fn build_transform_caller_help<'a, 'ctx, 'env>(
for (argument_ptr, layout) in arguments.iter().zip(argument_layouts) { for (argument_ptr, layout) in arguments.iter().zip(argument_layouts) {
let basic_type = basic_type_from_layout(env, layout).ptr_type(AddressSpace::Generic); let basic_type = basic_type_from_layout(env, layout).ptr_type(AddressSpace::Generic);
let argument_cast = env let argument = if layout.is_passed_by_reference() {
.builder env.builder
.build_bitcast(*argument_ptr, basic_type, "load_opaque") .build_pointer_cast(
.into_pointer_value(); argument_ptr.into_pointer_value(),
basic_type,
"cast_ptr_to_tag_build_transform_caller_help",
)
.into()
} else {
let argument_cast = env
.builder
.build_bitcast(*argument_ptr, basic_type, "load_opaque_1")
.into_pointer_value();
let argument = env.builder.build_load(argument_cast, "load_opaque"); env.builder.build_load(argument_cast, "load_opaque_2")
};
arguments_cast.push(argument); arguments_cast.push(argument);
} }
@ -288,31 +293,19 @@ fn build_transform_caller_help<'a, 'ctx, 'env>(
} }
} }
let call = { let result = crate::llvm::build::call_roc_function(
env.builder env,
.build_call(roc_function, arguments_cast.as_slice(), "tmp") roc_function,
}; &result_layout,
arguments_cast.as_slice(),
call.set_call_convention(FAST_CALL_CONV); );
let result = call
.try_as_basic_value()
.left()
.unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."));
let result_u8_ptr = function_value let result_u8_ptr = function_value
.get_nth_param(argument_layouts.len() as u32 + 1) .get_nth_param(argument_layouts.len() as u32 + 1)
.unwrap(); .unwrap()
let result_ptr = env
.builder
.build_bitcast(
result_u8_ptr,
result.get_type().ptr_type(AddressSpace::Generic),
"write_result",
)
.into_pointer_value(); .into_pointer_value();
env.builder.build_store(result_ptr, result); crate::llvm::build::store_roc_value_opaque(env, result_layout, result_u8_ptr, result);
env.builder.build_return(None); env.builder.build_return(None);
env.builder.position_at_end(block); env.builder.position_at_end(block);
@ -414,12 +407,18 @@ fn build_rc_wrapper<'a, 'ctx, 'env>(
let value_type = basic_type_from_layout(env, layout).ptr_type(AddressSpace::Generic); let value_type = basic_type_from_layout(env, layout).ptr_type(AddressSpace::Generic);
let value_cast = env let value = if layout.is_passed_by_reference() {
.builder env.builder
.build_bitcast(value_ptr, value_type, "load_opaque") .build_pointer_cast(value_ptr, value_type, "cast_ptr_to_tag_build_rc_wrapper")
.into_pointer_value(); .into()
} else {
let value_cast = env
.builder
.build_bitcast(value_ptr, value_type, "load_opaque")
.into_pointer_value();
let value = env.builder.build_load(value_cast, "load_opaque"); env.builder.build_load(value_cast, "load_opaque")
};
match rc_operation { match rc_operation {
Mode::Inc => { Mode::Inc => {

File diff suppressed because it is too large Load diff

View file

@ -2,9 +2,9 @@ use crate::debug_info_init;
use crate::llvm::bitcode::call_bitcode_fn; use crate::llvm::bitcode::call_bitcode_fn;
use crate::llvm::build::tag_pointer_clear_tag_id; use crate::llvm::build::tag_pointer_clear_tag_id;
use crate::llvm::build::Env; 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::{get_tag_id, FAST_CALL_CONV, TAG_DATA_INDEX};
use crate::llvm::build_str; use crate::llvm::build_str;
use crate::llvm::convert::basic_type_from_layout; use crate::llvm::convert::{basic_type_from_layout, basic_type_from_layout_1};
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use inkwell::values::{ use inkwell::values::{
BasicValue, BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue, BasicValue, BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue,
@ -339,7 +339,8 @@ fn build_hash_tag<'a, 'ctx, 'env>(
None => { None => {
let seed_type = env.context.i64_type(); let seed_type = env.context.i64_type();
let arg_type = basic_type_from_layout(env, layout); let arg_type = basic_type_from_layout_1(env, layout);
dbg!(layout, arg_type);
let function_value = crate::llvm::refcounting::build_header_help( let function_value = crate::llvm::refcounting::build_header_help(
env, env,
@ -423,14 +424,6 @@ fn hash_tag<'a, 'ctx, 'env>(
let block = env.context.append_basic_block(parent, "tag_id_modify"); let block = env.context.append_basic_block(parent, "tag_id_modify");
env.builder.position_at_end(block); env.builder.position_at_end(block);
let struct_layout = Layout::Struct(field_layouts);
let wrapper_type = basic_type_from_layout(env, &struct_layout);
debug_assert!(wrapper_type.is_struct_type());
let as_struct =
cast_block_of_memory_to_tag(env.builder, tag.into_struct_value(), wrapper_type);
// hash the tag id // hash the tag id
let hash_bytes = store_and_use_as_u8_ptr( let hash_bytes = store_and_use_as_u8_ptr(
env, env,
@ -440,7 +433,6 @@ fn hash_tag<'a, 'ctx, 'env>(
.into(), .into(),
&tag_id_layout, &tag_id_layout,
); );
let seed = hash_bitcode_fn( let seed = hash_bitcode_fn(
env, env,
seed, seed,
@ -449,14 +441,9 @@ fn hash_tag<'a, 'ctx, 'env>(
); );
// hash the tag data // hash the tag data
let answer = build_hash_struct( let tag = tag.into_pointer_value();
env, let answer =
layout_ids, hash_ptr_to_struct(env, layout_ids, union_layout, field_layouts, seed, tag);
field_layouts,
WhenRecursive::Unreachable,
seed,
as_struct,
);
merge_phi.add_incoming(&[(&answer, block)]); merge_phi.add_incoming(&[(&answer, block)]);
env.builder.build_unconditional_branch(merge_block); env.builder.build_unconditional_branch(merge_block);
@ -793,7 +780,15 @@ fn hash_list<'a, 'ctx, 'env>(
env.builder.build_store(result, answer); env.builder.build_store(result, answer);
}; };
incrementing_elem_loop(env, parent, ptr, length, "current_index", loop_fn); incrementing_elem_loop(
env,
parent,
*element_layout,
ptr,
length,
"current_index",
loop_fn,
);
env.builder.build_unconditional_branch(done_block); env.builder.build_unconditional_branch(done_block);
@ -822,12 +817,12 @@ fn hash_ptr_to_struct<'a, 'ctx, 'env>(
) -> IntValue<'ctx> { ) -> IntValue<'ctx> {
use inkwell::types::BasicType; use inkwell::types::BasicType;
let wrapper_type = basic_type_from_layout(env, &Layout::Union(*union_layout)); let wrapper_type = basic_type_from_layout_1(env, &Layout::Union(*union_layout));
// cast the opaque pointer to a pointer of the correct shape // cast the opaque pointer to a pointer of the correct shape
let wrapper_ptr = env let wrapper_ptr = env
.builder .builder
.build_bitcast(tag, wrapper_type, "opaque_to_correct") .build_bitcast(tag, wrapper_type, "hash_ptr_to_struct_opaque_to_correct")
.into_pointer_value(); .into_pointer_value();
let struct_ptr = env let struct_ptr = env

View file

@ -17,6 +17,8 @@ use morphic_lib::UpdateMode;
use roc_builtins::bitcode; use roc_builtins::bitcode;
use roc_mono::layout::{Builtin, Layout, LayoutIds}; use roc_mono::layout::{Builtin, Layout, LayoutIds};
use super::build::{load_roc_value, store_roc_value};
pub fn pass_update_mode<'a, 'ctx, 'env>( pub fn pass_update_mode<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
update_mode: UpdateMode, update_mode: UpdateMode,
@ -53,9 +55,13 @@ pub fn call_bitcode_fn_returns_list<'a, 'ctx, 'env>(
fn pass_element_as_opaque<'a, 'ctx, 'env>( fn pass_element_as_opaque<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
element: BasicValueEnum<'ctx>, element: BasicValueEnum<'ctx>,
layout: Layout<'a>,
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
let element_ptr = env.builder.build_alloca(element.get_type(), "element"); let element_type = basic_type_from_layout(env, &layout);
env.builder.build_store(element_ptr, element); let element_ptr = env
.builder
.build_alloca(element_type, "element_to_pass_as_opaque");
store_roc_value(env, layout, element_ptr, element);
env.builder.build_bitcast( env.builder.build_bitcast(
element_ptr, element_ptr,
@ -106,7 +112,7 @@ pub fn list_single<'a, 'ctx, 'env>(
env, env,
&[ &[
env.alignment_intvalue(element_layout), env.alignment_intvalue(element_layout),
pass_element_as_opaque(env, element), pass_element_as_opaque(env, element, *element_layout),
layout_width(env, element_layout), layout_width(env, element_layout),
], ],
bitcode::LIST_SINGLE, bitcode::LIST_SINGLE,
@ -128,7 +134,7 @@ pub fn list_repeat<'a, 'ctx, 'env>(
&[ &[
list_len.into(), list_len.into(),
env.alignment_intvalue(element_layout), env.alignment_intvalue(element_layout),
pass_element_as_opaque(env, element), pass_element_as_opaque(env, element, *element_layout),
layout_width(env, element_layout), layout_width(env, element_layout),
inc_element_fn.as_global_value().as_pointer_value().into(), inc_element_fn.as_global_value().as_pointer_value().into(),
], ],
@ -216,10 +222,11 @@ pub fn list_get_unsafe<'a, 'ctx, 'env>(
// Assume the bounds have already been checked earlier // Assume the bounds have already been checked earlier
// (e.g. by List.get or List.first, which wrap List.#getUnsafe) // (e.g. by List.get or List.first, which wrap List.#getUnsafe)
let elem_ptr = let elem_ptr = unsafe {
unsafe { builder.build_in_bounds_gep(array_data_ptr, &[elem_index], "elem") }; builder.build_in_bounds_gep(array_data_ptr, &[elem_index], "list_get_element")
};
let result = builder.build_load(elem_ptr, "List.get"); let result = load_roc_value(env, **elem_layout, elem_ptr, "list_get_load_element");
increment_refcount_layout(env, parent, layout_ids, 1, result, elem_layout); increment_refcount_layout(env, parent, layout_ids, 1, result, elem_layout);
@ -247,7 +254,7 @@ pub fn list_append<'a, 'ctx, 'env>(
&[ &[
pass_list_cc(env, original_wrapper.into()), pass_list_cc(env, original_wrapper.into()),
env.alignment_intvalue(element_layout), env.alignment_intvalue(element_layout),
pass_element_as_opaque(env, element), pass_element_as_opaque(env, element, *element_layout),
layout_width(env, element_layout), layout_width(env, element_layout),
pass_update_mode(env, update_mode), pass_update_mode(env, update_mode),
], ],
@ -267,7 +274,7 @@ pub fn list_prepend<'a, 'ctx, 'env>(
&[ &[
pass_list_cc(env, original_wrapper.into()), pass_list_cc(env, original_wrapper.into()),
env.alignment_intvalue(element_layout), env.alignment_intvalue(element_layout),
pass_element_as_opaque(env, element), pass_element_as_opaque(env, element, *element_layout),
layout_width(env, element_layout), layout_width(env, element_layout),
], ],
bitcode::LIST_PREPEND, bitcode::LIST_PREPEND,
@ -389,7 +396,7 @@ pub fn list_set<'a, 'ctx, 'env>(
&[ &[
bytes.into(), bytes.into(),
index.into(), index.into(),
pass_element_as_opaque(env, element), pass_element_as_opaque(env, element, *element_layout),
layout_width(env, element_layout), layout_width(env, element_layout),
dec_element_fn.as_global_value().as_pointer_value().into(), dec_element_fn.as_global_value().as_pointer_value().into(),
], ],
@ -402,7 +409,7 @@ pub fn list_set<'a, 'ctx, 'env>(
length.into(), length.into(),
env.alignment_intvalue(element_layout), env.alignment_intvalue(element_layout),
index.into(), index.into(),
pass_element_as_opaque(env, element), pass_element_as_opaque(env, element, *element_layout),
layout_width(env, element_layout), layout_width(env, element_layout),
dec_element_fn.as_global_value().as_pointer_value().into(), dec_element_fn.as_global_value().as_pointer_value().into(),
], ],
@ -578,7 +585,7 @@ pub fn list_contains<'a, 'ctx, 'env>(
env, env,
&[ &[
pass_list_cc(env, list), pass_list_cc(env, list),
pass_element_as_opaque(env, element), pass_element_as_opaque(env, element, *element_layout),
layout_width(env, element_layout), layout_width(env, element_layout),
eq_fn, eq_fn,
], ],
@ -1137,6 +1144,7 @@ where
pub fn incrementing_elem_loop<'a, 'ctx, 'env, LoopFn>( pub fn incrementing_elem_loop<'a, 'ctx, 'env, LoopFn>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
parent: FunctionValue<'ctx>, parent: FunctionValue<'ctx>,
element_layout: Layout<'a>,
ptr: PointerValue<'ctx>, ptr: PointerValue<'ctx>,
len: IntValue<'ctx>, len: IntValue<'ctx>,
index_name: &str, index_name: &str,
@ -1149,9 +1157,14 @@ where
incrementing_index_loop(env, parent, len, index_name, |index| { incrementing_index_loop(env, parent, len, index_name, |index| {
// The pointer to the element in the list // The pointer to the element in the list
let elem_ptr = unsafe { builder.build_in_bounds_gep(ptr, &[index], "load_index") }; let element_ptr = unsafe { builder.build_in_bounds_gep(ptr, &[index], "load_index") };
let elem = builder.build_load(elem_ptr, "get_elem"); let elem = load_roc_value(
env,
element_layout,
element_ptr,
"incrementing_element_loop_load",
);
loop_fn(index, elem); loop_fn(index, elem);
}) })

View file

@ -1,10 +1,8 @@
use crate::llvm::bitcode::call_bitcode_fn; use crate::llvm::bitcode::call_bitcode_fn;
use crate::llvm::build::{ use crate::llvm::build::{get_tag_id, tag_pointer_clear_tag_id, Env, FAST_CALL_CONV};
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_list::{list_len, load_list_ptr};
use crate::llvm::build_str::str_equal; use crate::llvm::build_str::str_equal;
use crate::llvm::convert::basic_type_from_layout; use crate::llvm::convert::{basic_type_from_layout, basic_type_from_layout_1};
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use inkwell::types::BasicType; use inkwell::types::BasicType;
use inkwell::values::{ use inkwell::values::{
@ -751,7 +749,7 @@ fn build_tag_eq<'a, 'ctx, 'env>(
let function = match env.module.get_function(fn_name.as_str()) { let function = match env.module.get_function(fn_name.as_str()) {
Some(function_value) => function_value, Some(function_value) => function_value,
None => { None => {
let arg_type = basic_type_from_layout(env, tag_layout); let arg_type = basic_type_from_layout_1(env, tag_layout);
let function_value = crate::llvm::refcounting::build_header_help( let function_value = crate::llvm::refcounting::build_header_help(
env, env,
@ -844,9 +842,29 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
match union_layout { match union_layout {
NonRecursive(tags) => { NonRecursive(tags) => {
let ptr_equal = env.builder.build_int_compare(
IntPredicate::EQ,
env.builder
.build_ptr_to_int(tag1.into_pointer_value(), env.ptr_int(), "pti"),
env.builder
.build_ptr_to_int(tag2.into_pointer_value(), env.ptr_int(), "pti"),
"compare_pointers",
);
let compare_tag_ids = ctx.append_basic_block(parent, "compare_tag_ids");
env.builder
.build_conditional_branch(ptr_equal, return_true, compare_tag_ids);
env.builder.position_at_end(compare_tag_ids);
let id1 = get_tag_id(env, parent, union_layout, tag1); let id1 = get_tag_id(env, parent, union_layout, tag1);
let id2 = get_tag_id(env, parent, union_layout, tag2); 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 = tag1.into_pointer_value();
let tag2 = tag2.into_pointer_value();
let compare_tag_fields = ctx.append_basic_block(parent, "compare_tag_fields"); let compare_tag_fields = ctx.append_basic_block(parent, "compare_tag_fields");
let same_tag = let same_tag =
@ -866,30 +884,14 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
let block = env.context.append_basic_block(parent, "tag_id_modify"); let block = env.context.append_basic_block(parent, "tag_id_modify");
env.builder.position_at_end(block); env.builder.position_at_end(block);
// TODO drop tag id? let answer = eq_ptr_to_struct(
let struct_layout = Layout::Struct(field_layouts);
let wrapper_type = basic_type_from_layout(env, &struct_layout);
debug_assert!(wrapper_type.is_struct_type());
let struct1 = cast_block_of_memory_to_tag(
env.builder,
tag1.into_struct_value(),
wrapper_type,
);
let struct2 = cast_block_of_memory_to_tag(
env.builder,
tag2.into_struct_value(),
wrapper_type,
);
let answer = build_struct_eq(
env, env,
layout_ids, layout_ids,
union_layout,
Some(when_recursive.clone()),
field_layouts, field_layouts,
when_recursive.clone(), tag1,
struct1, tag2,
struct2,
); );
env.builder.build_return(Some(&answer)); env.builder.build_return(Some(&answer));
@ -946,8 +948,15 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
let block = env.context.append_basic_block(parent, "tag_id_modify"); let block = env.context.append_basic_block(parent, "tag_id_modify");
env.builder.position_at_end(block); env.builder.position_at_end(block);
let answer = let answer = eq_ptr_to_struct(
eq_ptr_to_struct(env, layout_ids, union_layout, field_layouts, tag1, tag2); env,
layout_ids,
union_layout,
None,
field_layouts,
tag1,
tag2,
);
env.builder.build_return(Some(&answer)); env.builder.build_return(Some(&answer));
@ -1003,6 +1012,7 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
env, env,
layout_ids, layout_ids,
union_layout, union_layout,
None,
other_fields, other_fields,
tag1.into_pointer_value(), tag1.into_pointer_value(),
tag2.into_pointer_value(), tag2.into_pointer_value(),
@ -1093,8 +1103,15 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
let block = env.context.append_basic_block(parent, "tag_id_modify"); let block = env.context.append_basic_block(parent, "tag_id_modify");
env.builder.position_at_end(block); env.builder.position_at_end(block);
let answer = let answer = eq_ptr_to_struct(
eq_ptr_to_struct(env, layout_ids, union_layout, field_layouts, tag1, tag2); env,
layout_ids,
union_layout,
None,
field_layouts,
tag1,
tag2,
);
env.builder.build_return(Some(&answer)); env.builder.build_return(Some(&answer));
@ -1128,6 +1145,7 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
env, env,
layout_ids, layout_ids,
union_layout, union_layout,
None,
field_layouts, field_layouts,
tag1.into_pointer_value(), tag1.into_pointer_value(),
tag2.into_pointer_value(), tag2.into_pointer_value(),
@ -1142,6 +1160,7 @@ fn eq_ptr_to_struct<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>, layout_ids: &mut LayoutIds<'a>,
union_layout: &UnionLayout<'a>, union_layout: &UnionLayout<'a>,
opt_when_recursive: Option<WhenRecursive<'a>>,
field_layouts: &'a [Layout<'a>], field_layouts: &'a [Layout<'a>],
tag1: PointerValue<'ctx>, tag1: PointerValue<'ctx>,
tag2: PointerValue<'ctx>, tag2: PointerValue<'ctx>,
@ -1184,7 +1203,7 @@ fn eq_ptr_to_struct<'a, 'ctx, 'env>(
env, env,
layout_ids, layout_ids,
field_layouts, field_layouts,
WhenRecursive::Loop(*union_layout), opt_when_recursive.unwrap_or(WhenRecursive::Loop(*union_layout)),
struct1, struct1,
struct2, struct2,
) )

View file

@ -76,6 +76,66 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>(
} }
} }
pub fn basic_type_from_layout_1<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
layout: &Layout<'_>,
) -> BasicTypeEnum<'ctx> {
use Layout::*;
match layout {
Struct(sorted_fields) => basic_type_from_record(env, sorted_fields),
LambdaSet(lambda_set) => {
basic_type_from_layout_1(env, &lambda_set.runtime_representation())
}
Union(union_layout) => {
use UnionLayout::*;
let tag_id_type = basic_type_from_layout(env, &union_layout.tag_id_layout());
match union_layout {
NonRecursive(tags) => {
let data = block_of_memory_slices(env.context, tags, env.ptr_bytes);
let struct_type = env.context.struct_type(&[data, tag_id_type], false);
struct_type.ptr_type(AddressSpace::Generic).into()
}
Recursive(tags)
| NullableWrapped {
other_tags: tags, ..
} => {
let data = block_of_memory_slices(env.context, tags, env.ptr_bytes);
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 = block_of_memory_slices(env.context, &[other_fields], env.ptr_bytes);
block.ptr_type(AddressSpace::Generic).into()
}
NonNullableUnwrapped(fields) => {
let block = block_of_memory_slices(env.context, &[fields], env.ptr_bytes);
block.ptr_type(AddressSpace::Generic).into()
}
}
}
RecursivePointer => {
// TODO make this dynamic
env.context
.i64_type()
.ptr_type(AddressSpace::Generic)
.as_basic_type_enum()
}
Builtin(builtin) => basic_type_from_builtin(env, builtin),
}
}
pub fn basic_type_from_builtin<'a, 'ctx, 'env>( pub fn basic_type_from_builtin<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>, env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
builtin: &Builtin<'_>, builtin: &Builtin<'_>,

View file

@ -1,11 +1,11 @@
use crate::debug_info_init; use crate::debug_info_init;
use crate::llvm::bitcode::call_void_bitcode_fn; use crate::llvm::bitcode::call_void_bitcode_fn;
use crate::llvm::build::{ use crate::llvm::build::{
add_func, cast_basic_basic, cast_block_of_memory_to_tag, get_tag_id, get_tag_id_non_recursive, add_func, cast_basic_basic, get_tag_id, tag_pointer_clear_tag_id, use_roc_value, Env,
tag_pointer_clear_tag_id, Env, FAST_CALL_CONV, TAG_DATA_INDEX, FAST_CALL_CONV, TAG_DATA_INDEX, TAG_ID_INDEX,
}; };
use crate::llvm::build_list::{incrementing_elem_loop, list_len, load_list}; use crate::llvm::build_list::{incrementing_elem_loop, list_len, load_list};
use crate::llvm::convert::{basic_type_from_layout, ptr_int}; use crate::llvm::convert::{basic_type_from_layout, basic_type_from_layout_1, ptr_int};
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use inkwell::basic_block::BasicBlock; use inkwell::basic_block::BasicBlock;
use inkwell::context::Context; use inkwell::context::Context;
@ -139,8 +139,10 @@ impl<'ctx> PointerToRefcount<'ctx> {
let block = env.builder.get_insert_block().expect("to be in a function"); let block = env.builder.get_insert_block().expect("to be in a function");
let parent = block.get_parent().unwrap(); let parent = block.get_parent().unwrap();
let modify_block = env.context.append_basic_block(parent, "inc_str_modify"); let modify_block = env
let cont_block = env.context.append_basic_block(parent, "inc_str_cont"); .context
.append_basic_block(parent, "inc_refcount_modify");
let cont_block = env.context.append_basic_block(parent, "inc_refcount_cont");
env.builder env.builder
.build_conditional_branch(is_static_allocation, cont_block, modify_block); .build_conditional_branch(is_static_allocation, cont_block, modify_block);
@ -349,18 +351,25 @@ fn modify_refcount_struct_help<'a, 'ctx, 'env>(
for (i, field_layout) in layouts.iter().enumerate() { for (i, field_layout) in layouts.iter().enumerate() {
if field_layout.contains_refcounted() { if field_layout.contains_refcounted() {
let field_ptr = env let raw_value = env
.builder .builder
.build_extract_value(wrapper_struct, i as u32, "decrement_struct_field") .build_extract_value(wrapper_struct, i as u32, "decrement_struct_field")
.unwrap(); .unwrap();
let field_value = use_roc_value(
env,
*field_layout,
raw_value,
"load_struct_tag_field_for_decrement",
);
modify_refcount_layout_help( modify_refcount_layout_help(
env, env,
parent, parent,
layout_ids, layout_ids,
mode.to_call_mode(fn_val), mode.to_call_mode(fn_val),
when_recursive, when_recursive,
field_ptr, field_value,
field_layout, field_layout,
); );
} }
@ -753,7 +762,15 @@ fn modify_refcount_list_help<'a, 'ctx, 'env>(
); );
}; };
incrementing_elem_loop(env, parent, ptr, len, "modify_rc_index", loop_fn); incrementing_elem_loop(
env,
parent,
*element_layout,
ptr,
len,
"modify_rc_index",
loop_fn,
);
} }
let refcount_ptr = PointerToRefcount::from_list_wrapper(env, original_wrapper); let refcount_ptr = PointerToRefcount::from_list_wrapper(env, original_wrapper);
@ -1289,7 +1306,7 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>(
.build_bitcast( .build_bitcast(
value_ptr, value_ptr,
wrapper_type.ptr_type(AddressSpace::Generic), wrapper_type.ptr_type(AddressSpace::Generic),
"opaque_to_correct", "opaque_to_correct_recursive_decrement",
) )
.into_pointer_value(); .into_pointer_value();
@ -1602,7 +1619,7 @@ fn modify_refcount_union<'a, 'ctx, 'env>(
let function = match env.module.get_function(fn_name.as_str()) { let function = match env.module.get_function(fn_name.as_str()) {
Some(function_value) => function_value, Some(function_value) => function_value,
None => { None => {
let basic_type = basic_type_from_layout(env, &layout); let basic_type = basic_type_from_layout_1(env, &layout);
let function_value = build_header(env, basic_type, mode, &fn_name); let function_value = build_header(env, basic_type, mode, &fn_name);
modify_refcount_union_help( modify_refcount_union_help(
@ -1647,18 +1664,24 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
// Add args to scope // Add args to scope
let arg_symbol = Symbol::ARG_1; let arg_symbol = Symbol::ARG_1;
let arg_val = fn_val.get_param_iter().next().unwrap(); let arg_ptr = fn_val.get_param_iter().next().unwrap().into_pointer_value();
arg_val.set_name(arg_symbol.as_str(&env.interns)); arg_ptr.set_name(arg_symbol.as_str(&env.interns));
let parent = fn_val; let parent = fn_val;
let before_block = env.builder.get_insert_block().expect("to be in a function"); let before_block = env.builder.get_insert_block().expect("to be in a function");
let wrapper_struct = arg_val.into_struct_value();
// read the tag_id // read the tag_id
let tag_id = get_tag_id_non_recursive(env, wrapper_struct); let tag_id_ptr = env
.builder
.build_struct_gep(arg_ptr, TAG_ID_INDEX, "tag_id_ptr")
.unwrap();
let tag_id = env
.builder
.build_load(tag_id_ptr, "load_tag_id")
.into_int_value();
let tag_id_u8 = env let tag_id_u8 = env
.builder .builder
@ -1686,12 +1709,16 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
let wrapper_type = basic_type_from_layout(env, &Layout::Struct(field_layouts)); let wrapper_type = basic_type_from_layout(env, &Layout::Struct(field_layouts));
debug_assert!(wrapper_type.is_struct_type()); debug_assert!(wrapper_type.is_struct_type());
let data_bytes = env let opaque_tag_data_ptr = env
.builder .builder
.build_extract_value(wrapper_struct, TAG_DATA_INDEX, "read_tag_id") .build_struct_gep(arg_ptr, TAG_DATA_INDEX, "field_ptr")
.unwrap() .unwrap();
.into_struct_value();
let wrapper_struct = cast_block_of_memory_to_tag(env.builder, data_bytes, wrapper_type); let cast_tag_data_pointer = env.builder.build_pointer_cast(
opaque_tag_data_ptr,
wrapper_type.ptr_type(AddressSpace::Generic),
"cast_to_concrete_tag",
);
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 {
@ -1699,16 +1726,22 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
} else if field_layout.contains_refcounted() { } else if field_layout.contains_refcounted() {
let field_ptr = env let field_ptr = env
.builder .builder
.build_extract_value(wrapper_struct, i as u32, "modify_tag_field") .build_struct_gep(cast_tag_data_pointer, i as u32, "modify_tag_field")
.unwrap(); .unwrap();
let field_value = if field_layout.is_passed_by_reference() {
field_ptr.into()
} else {
env.builder.build_load(field_ptr, "field_value")
};
modify_refcount_layout_help( modify_refcount_layout_help(
env, env,
parent, parent,
layout_ids, layout_ids,
mode.to_call_mode(fn_val), mode.to_call_mode(fn_val),
when_recursive, when_recursive,
field_ptr, field_value,
field_layout, field_layout,
); );
} }

View file

@ -863,6 +863,16 @@ impl<'a> Layout<'a> {
false false
} }
pub fn is_passed_by_reference(&self) -> bool {
match self {
Layout::Union(UnionLayout::NonRecursive(_)) => true,
Layout::LambdaSet(lambda_set) => {
lambda_set.runtime_representation().is_passed_by_reference()
}
_ => false,
}
}
pub fn stack_size(&self, pointer_size: u32) -> u32 { pub fn stack_size(&self, pointer_size: u32) -> u32 {
let width = self.stack_size_without_alignment(pointer_size); let width = self.stack_size_without_alignment(pointer_size);
let alignment = self.alignment_bytes(pointer_size); let alignment = self.alignment_bytes(pointer_size);
@ -941,16 +951,16 @@ impl<'a> Layout<'a> {
}) })
.max(); .max();
let tag_id_builtin = variant.tag_id_builtin();
match max_alignment { match max_alignment {
Some(align) => { Some(align) => round_up_to_alignment(
let tag_id_builtin = variant.tag_id_builtin(); align.max(tag_id_builtin.alignment_bytes(pointer_size)),
tag_id_builtin.alignment_bytes(pointer_size),
round_up_to_alignment( ),
align, None => {
tag_id_builtin.alignment_bytes(pointer_size), // none of the tags had any payload, but the tag id still contains information
) tag_id_builtin.alignment_bytes(pointer_size)
} }
None => 0,
} }
} }
Recursive(_) Recursive(_)
@ -2674,3 +2684,27 @@ impl<'a> std::convert::TryFrom<&Layout<'a>> for ListLayout<'a> {
} }
} }
} }
#[cfg(test)]
mod test {
use super::*;
#[test]
fn width_and_alignment_union_empty_struct() {
let lambda_set = LambdaSet {
set: &[(Symbol::LIST_MAP, &[])],
representation: &Layout::Struct(&[]),
};
let a = &[Layout::Struct(&[])] as &[_];
let b = &[Layout::LambdaSet(lambda_set)] as &[_];
let tt = [a, b];
let layout = Layout::Union(UnionLayout::NonRecursive(&tt));
// at the moment, the tag id uses an I64, so
let ptr_width = 8;
assert_eq!(layout.stack_size(ptr_width), 8);
assert_eq!(layout.alignment_bytes(ptr_width), 8);
}
}

View file

@ -453,7 +453,7 @@ fn result_with_guard_pattern() {
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm"))]
fn maybe_is_just() { fn maybe_is_just_not_nested() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"

View file

@ -198,6 +198,10 @@ fn create_llvm_module<'a>(
if name.starts_with("roc_builtins.dict") { if name.starts_with("roc_builtins.dict") {
function.add_attribute(AttributeLoc::Function, attr); function.add_attribute(AttributeLoc::Function, attr);
} }
if name.starts_with("roc_builtins.list") {
function.add_attribute(AttributeLoc::Function, attr);
}
} }
// Compile and add all the Procs before adding main // Compile and add all the Procs before adding main