mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 06:44:46 +00:00
Merge pull request #1916 from rtfeldman/tag-union-imitate-rust
Tag union imitate rust
This commit is contained in:
commit
1c6fab7043
12 changed files with 799 additions and 325 deletions
|
@ -218,8 +218,8 @@ pub fn gen_and_eval<'a>(
|
|||
// Verify the module
|
||||
if let Err(errors) = env.module.verify() {
|
||||
panic!(
|
||||
"Errors defining module: {}\n\nUncomment things nearby to see more details.",
|
||||
errors
|
||||
"Errors defining module:\n{}\n\nUncomment things nearby to see more details.",
|
||||
errors.to_string()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -128,20 +128,19 @@ fn build_has_tag_id_help<'a, 'ctx, 'env>(
|
|||
[tag_id, tag_value_ptr] => {
|
||||
let tag_type = basic_type_from_layout(env, &Layout::Union(union_layout));
|
||||
|
||||
let argument_cast = env
|
||||
.builder
|
||||
.build_bitcast(
|
||||
*tag_value_ptr,
|
||||
let tag_value = env.builder.build_pointer_cast(
|
||||
tag_value_ptr.into_pointer_value(),
|
||||
tag_type.ptr_type(AddressSpace::Generic),
|
||||
"load_opaque",
|
||||
)
|
||||
.into_pointer_value();
|
||||
|
||||
let tag_value = env.builder.build_load(argument_cast, "get_value");
|
||||
"load_opaque_get_tag_id",
|
||||
);
|
||||
|
||||
let actual_tag_id = {
|
||||
let tag_id_i64 =
|
||||
crate::llvm::build::get_tag_id(env, function_value, &union_layout, tag_value);
|
||||
let tag_id_i64 = crate::llvm::build::get_tag_id(
|
||||
env,
|
||||
function_value,
|
||||
&union_layout,
|
||||
tag_value.into(),
|
||||
);
|
||||
|
||||
env.builder
|
||||
.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 data_index = env
|
||||
.context
|
||||
.i64_type()
|
||||
.const_int(TAG_DATA_INDEX as u64, false);
|
||||
let ptr = env
|
||||
.builder
|
||||
.build_struct_gep(tag_value, TAG_DATA_INDEX, "get_data_ptr")
|
||||
.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")
|
||||
};
|
||||
|
||||
|
@ -191,6 +183,7 @@ pub fn build_transform_caller<'a, 'ctx, 'env>(
|
|||
function: FunctionValue<'ctx>,
|
||||
closure_data_layout: LambdaSet<'a>,
|
||||
argument_layouts: &[Layout<'a>],
|
||||
result_layout: Layout<'a>,
|
||||
) -> FunctionValue<'ctx> {
|
||||
let fn_name: &str = &format!(
|
||||
"{}_zig_function_caller",
|
||||
|
@ -204,6 +197,7 @@ pub fn build_transform_caller<'a, 'ctx, 'env>(
|
|||
function,
|
||||
closure_data_layout,
|
||||
argument_layouts,
|
||||
result_layout,
|
||||
fn_name,
|
||||
),
|
||||
}
|
||||
|
@ -214,6 +208,7 @@ fn build_transform_caller_help<'a, 'ctx, 'env>(
|
|||
roc_function: FunctionValue<'ctx>,
|
||||
closure_data_layout: LambdaSet<'a>,
|
||||
argument_layouts: &[Layout<'a>],
|
||||
result_layout: Layout<'a>,
|
||||
fn_name: &str,
|
||||
) -> FunctionValue<'ctx> {
|
||||
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) {
|
||||
let basic_type = basic_type_from_layout(env, layout).ptr_type(AddressSpace::Generic);
|
||||
|
||||
let argument = if layout.is_passed_by_reference() {
|
||||
env.builder
|
||||
.build_pointer_cast(
|
||||
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")
|
||||
.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);
|
||||
}
|
||||
|
@ -288,31 +293,19 @@ fn build_transform_caller_help<'a, 'ctx, 'env>(
|
|||
}
|
||||
}
|
||||
|
||||
let call = {
|
||||
env.builder
|
||||
.build_call(roc_function, arguments_cast.as_slice(), "tmp")
|
||||
};
|
||||
|
||||
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 = crate::llvm::build::call_roc_function(
|
||||
env,
|
||||
roc_function,
|
||||
&result_layout,
|
||||
arguments_cast.as_slice(),
|
||||
);
|
||||
|
||||
let result_u8_ptr = function_value
|
||||
.get_nth_param(argument_layouts.len() as u32 + 1)
|
||||
.unwrap();
|
||||
let result_ptr = env
|
||||
.builder
|
||||
.build_bitcast(
|
||||
result_u8_ptr,
|
||||
result.get_type().ptr_type(AddressSpace::Generic),
|
||||
"write_result",
|
||||
)
|
||||
.unwrap()
|
||||
.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.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 = if layout.is_passed_by_reference() {
|
||||
env.builder
|
||||
.build_pointer_cast(value_ptr, value_type, "cast_ptr_to_tag_build_rc_wrapper")
|
||||
.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 {
|
||||
Mode::Inc => {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,9 +2,9 @@ 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::{get_tag_id, FAST_CALL_CONV, TAG_DATA_INDEX};
|
||||
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 inkwell::values::{
|
||||
BasicValue, BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue,
|
||||
|
@ -339,7 +339,8 @@ fn build_hash_tag<'a, 'ctx, 'env>(
|
|||
None => {
|
||||
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(
|
||||
env,
|
||||
|
@ -423,14 +424,6 @@ fn hash_tag<'a, 'ctx, 'env>(
|
|||
let block = env.context.append_basic_block(parent, "tag_id_modify");
|
||||
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
|
||||
let hash_bytes = store_and_use_as_u8_ptr(
|
||||
env,
|
||||
|
@ -440,7 +433,6 @@ fn hash_tag<'a, 'ctx, 'env>(
|
|||
.into(),
|
||||
&tag_id_layout,
|
||||
);
|
||||
|
||||
let seed = hash_bitcode_fn(
|
||||
env,
|
||||
seed,
|
||||
|
@ -449,14 +441,9 @@ fn hash_tag<'a, 'ctx, 'env>(
|
|||
);
|
||||
|
||||
// hash the tag data
|
||||
let answer = build_hash_struct(
|
||||
env,
|
||||
layout_ids,
|
||||
field_layouts,
|
||||
WhenRecursive::Unreachable,
|
||||
seed,
|
||||
as_struct,
|
||||
);
|
||||
let tag = 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);
|
||||
|
@ -793,7 +780,15 @@ fn hash_list<'a, 'ctx, 'env>(
|
|||
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);
|
||||
|
||||
|
@ -822,12 +817,12 @@ fn hash_ptr_to_struct<'a, 'ctx, 'env>(
|
|||
) -> IntValue<'ctx> {
|
||||
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
|
||||
let wrapper_ptr = env
|
||||
.builder
|
||||
.build_bitcast(tag, wrapper_type, "opaque_to_correct")
|
||||
.build_bitcast(tag, wrapper_type, "hash_ptr_to_struct_opaque_to_correct")
|
||||
.into_pointer_value();
|
||||
|
||||
let struct_ptr = env
|
||||
|
|
|
@ -17,6 +17,8 @@ use morphic_lib::UpdateMode;
|
|||
use roc_builtins::bitcode;
|
||||
use roc_mono::layout::{Builtin, Layout, LayoutIds};
|
||||
|
||||
use super::build::{load_roc_value, store_roc_value};
|
||||
|
||||
pub fn pass_update_mode<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
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>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
element: BasicValueEnum<'ctx>,
|
||||
layout: Layout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let element_ptr = env.builder.build_alloca(element.get_type(), "element");
|
||||
env.builder.build_store(element_ptr, element);
|
||||
let element_type = basic_type_from_layout(env, &layout);
|
||||
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(
|
||||
element_ptr,
|
||||
|
@ -106,7 +112,7 @@ pub fn list_single<'a, 'ctx, 'env>(
|
|||
env,
|
||||
&[
|
||||
env.alignment_intvalue(element_layout),
|
||||
pass_element_as_opaque(env, element),
|
||||
pass_element_as_opaque(env, element, *element_layout),
|
||||
layout_width(env, element_layout),
|
||||
],
|
||||
bitcode::LIST_SINGLE,
|
||||
|
@ -128,7 +134,7 @@ pub fn list_repeat<'a, 'ctx, 'env>(
|
|||
&[
|
||||
list_len.into(),
|
||||
env.alignment_intvalue(element_layout),
|
||||
pass_element_as_opaque(env, element),
|
||||
pass_element_as_opaque(env, element, *element_layout),
|
||||
layout_width(env, element_layout),
|
||||
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
|
||||
// (e.g. by List.get or List.first, which wrap List.#getUnsafe)
|
||||
let elem_ptr =
|
||||
unsafe { builder.build_in_bounds_gep(array_data_ptr, &[elem_index], "elem") };
|
||||
let elem_ptr = unsafe {
|
||||
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);
|
||||
|
||||
|
@ -247,7 +254,7 @@ pub fn list_append<'a, 'ctx, 'env>(
|
|||
&[
|
||||
pass_list_cc(env, original_wrapper.into()),
|
||||
env.alignment_intvalue(element_layout),
|
||||
pass_element_as_opaque(env, element),
|
||||
pass_element_as_opaque(env, element, *element_layout),
|
||||
layout_width(env, element_layout),
|
||||
pass_update_mode(env, update_mode),
|
||||
],
|
||||
|
@ -267,7 +274,7 @@ pub fn list_prepend<'a, 'ctx, 'env>(
|
|||
&[
|
||||
pass_list_cc(env, original_wrapper.into()),
|
||||
env.alignment_intvalue(element_layout),
|
||||
pass_element_as_opaque(env, element),
|
||||
pass_element_as_opaque(env, element, *element_layout),
|
||||
layout_width(env, element_layout),
|
||||
],
|
||||
bitcode::LIST_PREPEND,
|
||||
|
@ -389,7 +396,7 @@ pub fn list_set<'a, 'ctx, 'env>(
|
|||
&[
|
||||
bytes.into(),
|
||||
index.into(),
|
||||
pass_element_as_opaque(env, element),
|
||||
pass_element_as_opaque(env, element, *element_layout),
|
||||
layout_width(env, element_layout),
|
||||
dec_element_fn.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
|
@ -402,7 +409,7 @@ pub fn list_set<'a, 'ctx, 'env>(
|
|||
length.into(),
|
||||
env.alignment_intvalue(element_layout),
|
||||
index.into(),
|
||||
pass_element_as_opaque(env, element),
|
||||
pass_element_as_opaque(env, element, *element_layout),
|
||||
layout_width(env, element_layout),
|
||||
dec_element_fn.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
|
@ -578,7 +585,7 @@ pub fn list_contains<'a, 'ctx, 'env>(
|
|||
env,
|
||||
&[
|
||||
pass_list_cc(env, list),
|
||||
pass_element_as_opaque(env, element),
|
||||
pass_element_as_opaque(env, element, *element_layout),
|
||||
layout_width(env, element_layout),
|
||||
eq_fn,
|
||||
],
|
||||
|
@ -1137,6 +1144,7 @@ where
|
|||
pub fn incrementing_elem_loop<'a, 'ctx, 'env, LoopFn>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
element_layout: Layout<'a>,
|
||||
ptr: PointerValue<'ctx>,
|
||||
len: IntValue<'ctx>,
|
||||
index_name: &str,
|
||||
|
@ -1149,9 +1157,14 @@ where
|
|||
|
||||
incrementing_index_loop(env, parent, len, index_name, |index| {
|
||||
// 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);
|
||||
})
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
use crate::llvm::bitcode::call_bitcode_fn;
|
||||
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::{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;
|
||||
use crate::llvm::convert::{basic_type_from_layout, basic_type_from_layout_1};
|
||||
use bumpalo::collections::Vec;
|
||||
use inkwell::types::BasicType;
|
||||
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()) {
|
||||
Some(function_value) => function_value,
|
||||
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(
|
||||
env,
|
||||
|
@ -844,9 +842,29 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
|
|||
|
||||
match union_layout {
|
||||
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 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 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");
|
||||
env.builder.position_at_end(block);
|
||||
|
||||
// TODO drop tag id?
|
||||
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(
|
||||
let answer = eq_ptr_to_struct(
|
||||
env,
|
||||
layout_ids,
|
||||
union_layout,
|
||||
Some(when_recursive.clone()),
|
||||
field_layouts,
|
||||
when_recursive.clone(),
|
||||
struct1,
|
||||
struct2,
|
||||
tag1,
|
||||
tag2,
|
||||
);
|
||||
|
||||
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");
|
||||
env.builder.position_at_end(block);
|
||||
|
||||
let answer =
|
||||
eq_ptr_to_struct(env, layout_ids, union_layout, field_layouts, tag1, tag2);
|
||||
let answer = eq_ptr_to_struct(
|
||||
env,
|
||||
layout_ids,
|
||||
union_layout,
|
||||
None,
|
||||
field_layouts,
|
||||
tag1,
|
||||
tag2,
|
||||
);
|
||||
|
||||
env.builder.build_return(Some(&answer));
|
||||
|
||||
|
@ -1003,6 +1012,7 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
|
|||
env,
|
||||
layout_ids,
|
||||
union_layout,
|
||||
None,
|
||||
other_fields,
|
||||
tag1.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");
|
||||
env.builder.position_at_end(block);
|
||||
|
||||
let answer =
|
||||
eq_ptr_to_struct(env, layout_ids, union_layout, field_layouts, tag1, tag2);
|
||||
let answer = eq_ptr_to_struct(
|
||||
env,
|
||||
layout_ids,
|
||||
union_layout,
|
||||
None,
|
||||
field_layouts,
|
||||
tag1,
|
||||
tag2,
|
||||
);
|
||||
|
||||
env.builder.build_return(Some(&answer));
|
||||
|
||||
|
@ -1128,6 +1145,7 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
|
|||
env,
|
||||
layout_ids,
|
||||
union_layout,
|
||||
None,
|
||||
field_layouts,
|
||||
tag1.into_pointer_value(),
|
||||
tag2.into_pointer_value(),
|
||||
|
@ -1142,6 +1160,7 @@ fn eq_ptr_to_struct<'a, 'ctx, 'env>(
|
|||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
union_layout: &UnionLayout<'a>,
|
||||
opt_when_recursive: Option<WhenRecursive<'a>>,
|
||||
field_layouts: &'a [Layout<'a>],
|
||||
tag1: PointerValue<'ctx>,
|
||||
tag2: PointerValue<'ctx>,
|
||||
|
@ -1184,7 +1203,7 @@ fn eq_ptr_to_struct<'a, 'ctx, 'env>(
|
|||
env,
|
||||
layout_ids,
|
||||
field_layouts,
|
||||
WhenRecursive::Loop(*union_layout),
|
||||
opt_when_recursive.unwrap_or(WhenRecursive::Loop(*union_layout)),
|
||||
struct1,
|
||||
struct2,
|
||||
)
|
||||
|
|
|
@ -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>(
|
||||
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
|
||||
builtin: &Builtin<'_>,
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use crate::debug_info_init;
|
||||
use crate::llvm::bitcode::call_void_bitcode_fn;
|
||||
use crate::llvm::build::{
|
||||
add_func, cast_basic_basic, cast_block_of_memory_to_tag, get_tag_id, get_tag_id_non_recursive,
|
||||
tag_pointer_clear_tag_id, Env, FAST_CALL_CONV, TAG_DATA_INDEX,
|
||||
add_func, cast_basic_basic, get_tag_id, tag_pointer_clear_tag_id, use_roc_value, Env,
|
||||
FAST_CALL_CONV, TAG_DATA_INDEX, TAG_ID_INDEX,
|
||||
};
|
||||
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 inkwell::basic_block::BasicBlock;
|
||||
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 parent = block.get_parent().unwrap();
|
||||
|
||||
let modify_block = env.context.append_basic_block(parent, "inc_str_modify");
|
||||
let cont_block = env.context.append_basic_block(parent, "inc_str_cont");
|
||||
let modify_block = env
|
||||
.context
|
||||
.append_basic_block(parent, "inc_refcount_modify");
|
||||
let cont_block = env.context.append_basic_block(parent, "inc_refcount_cont");
|
||||
|
||||
env.builder
|
||||
.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() {
|
||||
if field_layout.contains_refcounted() {
|
||||
let field_ptr = env
|
||||
let raw_value = env
|
||||
.builder
|
||||
.build_extract_value(wrapper_struct, i as u32, "decrement_struct_field")
|
||||
.unwrap();
|
||||
|
||||
let field_value = use_roc_value(
|
||||
env,
|
||||
*field_layout,
|
||||
raw_value,
|
||||
"load_struct_tag_field_for_decrement",
|
||||
);
|
||||
|
||||
modify_refcount_layout_help(
|
||||
env,
|
||||
parent,
|
||||
layout_ids,
|
||||
mode.to_call_mode(fn_val),
|
||||
when_recursive,
|
||||
field_ptr,
|
||||
field_value,
|
||||
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);
|
||||
|
@ -1289,7 +1306,7 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>(
|
|||
.build_bitcast(
|
||||
value_ptr,
|
||||
wrapper_type.ptr_type(AddressSpace::Generic),
|
||||
"opaque_to_correct",
|
||||
"opaque_to_correct_recursive_decrement",
|
||||
)
|
||||
.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()) {
|
||||
Some(function_value) => function_value,
|
||||
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);
|
||||
|
||||
modify_refcount_union_help(
|
||||
|
@ -1647,18 +1664,24 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
|
|||
|
||||
// Add args to scope
|
||||
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 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
|
||||
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
|
||||
.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));
|
||||
|
||||
debug_assert!(wrapper_type.is_struct_type());
|
||||
let data_bytes = env
|
||||
let opaque_tag_data_ptr = env
|
||||
.builder
|
||||
.build_extract_value(wrapper_struct, TAG_DATA_INDEX, "read_tag_id")
|
||||
.unwrap()
|
||||
.into_struct_value();
|
||||
let wrapper_struct = cast_block_of_memory_to_tag(env.builder, data_bytes, wrapper_type);
|
||||
.build_struct_gep(arg_ptr, TAG_DATA_INDEX, "field_ptr")
|
||||
.unwrap();
|
||||
|
||||
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() {
|
||||
if let Layout::RecursivePointer = field_layout {
|
||||
|
@ -1699,16 +1726,22 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
|
|||
} else if field_layout.contains_refcounted() {
|
||||
let field_ptr = env
|
||||
.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();
|
||||
|
||||
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(
|
||||
env,
|
||||
parent,
|
||||
layout_ids,
|
||||
mode.to_call_mode(fn_val),
|
||||
when_recursive,
|
||||
field_ptr,
|
||||
field_value,
|
||||
field_layout,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -863,6 +863,16 @@ impl<'a> Layout<'a> {
|
|||
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 {
|
||||
let width = self.stack_size_without_alignment(pointer_size);
|
||||
let alignment = self.alignment_bytes(pointer_size);
|
||||
|
@ -941,16 +951,16 @@ impl<'a> Layout<'a> {
|
|||
})
|
||||
.max();
|
||||
|
||||
match max_alignment {
|
||||
Some(align) => {
|
||||
let tag_id_builtin = variant.tag_id_builtin();
|
||||
|
||||
round_up_to_alignment(
|
||||
align,
|
||||
match max_alignment {
|
||||
Some(align) => round_up_to_alignment(
|
||||
align.max(tag_id_builtin.alignment_bytes(pointer_size)),
|
||||
tag_id_builtin.alignment_bytes(pointer_size),
|
||||
)
|
||||
),
|
||||
None => {
|
||||
// none of the tags had any payload, but the tag id still contains information
|
||||
tag_id_builtin.alignment_bytes(pointer_size)
|
||||
}
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -453,7 +453,7 @@ fn result_with_guard_pattern() {
|
|||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn maybe_is_just() {
|
||||
fn maybe_is_just_not_nested() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
|
|
|
@ -198,6 +198,10 @@ fn create_llvm_module<'a>(
|
|||
if name.starts_with("roc_builtins.dict") {
|
||||
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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue