Merge branch 'trunk' of https://github.com/rtfeldman/roc into add-dec-types

This commit is contained in:
Jared Ramirez 2021-07-08 16:47:42 -07:00
commit 67eef2c97f
82 changed files with 3791 additions and 2228 deletions

View file

@ -1,6 +1,6 @@
/// Helpers for interacting with the zig that generates bitcode
use crate::debug_info_init;
use crate::llvm::build::{Env, C_CALL_CONV, FAST_CALL_CONV};
use crate::llvm::build::{struct_from_fields, Env, C_CALL_CONV, FAST_CALL_CONV, TAG_DATA_INDEX};
use crate::llvm::convert::basic_type_from_layout;
use crate::llvm::refcounting::{
decrement_refcount_layout, increment_n_refcount_layout, increment_refcount_layout,
@ -10,7 +10,7 @@ use inkwell::types::{BasicType, BasicTypeEnum};
use inkwell::values::{BasicValue, BasicValueEnum, CallSiteValue, FunctionValue, InstructionValue};
use inkwell::AddressSpace;
use roc_module::symbol::Symbol;
use roc_mono::layout::{Layout, LayoutIds};
use roc_mono::layout::{Layout, LayoutIds, UnionLayout};
pub fn call_bitcode_fn<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
@ -66,6 +66,126 @@ const ARGUMENT_SYMBOLS: [Symbol; 8] = [
Symbol::ARG_8,
];
pub fn build_has_tag_id<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
function: FunctionValue<'ctx>,
union_layout: UnionLayout<'a>,
) -> FunctionValue<'ctx> {
let fn_name: &str = &format!("{}_has_tag_id", function.get_name().to_string_lossy());
// currently the code assumes we're dealing with a non-recursive layout
debug_assert!(matches!(union_layout, UnionLayout::NonRecursive(_)));
match env.module.get_function(fn_name) {
Some(function_value) => function_value,
None => build_has_tag_id_help(env, union_layout, &fn_name),
}
}
fn build_has_tag_id_help<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
union_layout: UnionLayout<'a>,
fn_name: &str,
) -> FunctionValue<'ctx> {
let i8_ptr_type = env.context.i8_type().ptr_type(AddressSpace::Generic);
let argument_types: &[BasicTypeEnum] = &[env.context.i16_type().into(), i8_ptr_type.into()];
let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap();
let output_type = crate::llvm::convert::zig_has_tag_id_type(env);
let function_value = crate::llvm::refcounting::build_header_help(
env,
&fn_name,
output_type.into(),
&argument_types,
);
// called from zig, must use C calling convention
function_value.set_call_conventions(C_CALL_CONV);
let kind_id = Attribute::get_named_enum_kind_id("alwaysinline");
debug_assert!(kind_id > 0);
let attr = env.context.create_enum_attribute(kind_id, 1);
function_value.add_attribute(AttributeLoc::Function, attr);
let entry = env.context.append_basic_block(function_value, "entry");
env.builder.position_at_end(entry);
debug_info_init!(env, function_value);
let it = function_value.get_param_iter();
let arguments =
bumpalo::collections::Vec::from_iter_in(it.take(argument_types.len()), env.arena);
for (argument, name) in arguments.iter().zip(ARGUMENT_SYMBOLS.iter()) {
argument.set_name(name.ident_string(&env.interns));
}
match arguments.as_slice() {
[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,
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 tag_id_i64 =
crate::llvm::build::get_tag_id(env, function_value, &union_layout, tag_value);
env.builder
.build_int_cast(tag_id_i64, env.context.i16_type(), "to_i16")
};
let answer = env.builder.build_int_compare(
inkwell::IntPredicate::EQ,
tag_id.into_int_value(),
actual_tag_id,
"compare",
);
let tag_data_ptr = {
let data_index = env
.context
.i64_type()
.const_int(TAG_DATA_INDEX as u64, false);
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")
};
let field_vals = [(0, answer.into()), (1, tag_data_ptr)];
let output = struct_from_fields(env, output_type, field_vals.iter().copied());
env.builder.build_return(Some(&output));
env.builder.position_at_end(block);
env.builder
.set_current_debug_location(env.context, di_location);
function_value
}
_ => unreachable!(),
}
}
pub fn build_transform_caller<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
function: FunctionValue<'ctx>,

File diff suppressed because it is too large Load diff

View file

@ -635,7 +635,6 @@ fn dict_intersect_or_difference<'a, 'ctx, 'env>(
#[allow(clippy::too_many_arguments)]
pub fn dict_walk<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
roc_function_call: RocFunctionCall<'ctx>,
dict: BasicValueEnum<'ctx>,
accum: BasicValueEnum<'ctx>,
@ -660,9 +659,6 @@ pub fn dict_walk<'a, 'ctx, 'env>(
let output_ptr = builder.build_alloca(accum_bt, "output_ptr");
let inc_key_fn = build_inc_wrapper(env, layout_ids, key_layout);
let inc_value_fn = build_inc_wrapper(env, layout_ids, value_layout);
call_void_bitcode_fn(
env,
&[
@ -676,8 +672,6 @@ pub fn dict_walk<'a, 'ctx, 'env>(
layout_width(env, key_layout),
layout_width(env, value_layout),
layout_width(env, accum_layout),
inc_key_fn.as_global_value().as_pointer_value().into(),
inc_value_fn.as_global_value().as_pointer_value().into(),
env.builder.build_bitcast(output_ptr, u8_ptr, "to_opaque"),
],
&bitcode::DICT_WALK,

View file

@ -1,7 +1,7 @@
use crate::debug_info_init;
use crate::llvm::bitcode::call_bitcode_fn;
use crate::llvm::build::Env;
use crate::llvm::build::{cast_block_of_memory_to_tag, complex_bitcast, FAST_CALL_CONV};
use crate::llvm::build::{cast_block_of_memory_to_tag, get_tag_id, FAST_CALL_CONV, TAG_DATA_INDEX};
use crate::llvm::build_str;
use crate::llvm::convert::basic_type_from_layout;
use bumpalo::collections::Vec;
@ -404,13 +404,15 @@ fn hash_tag<'a, 'ctx, 'env>(
let merge_block = env.context.append_basic_block(parent, "merge_block");
env.builder.position_at_end(merge_block);
let merge_phi = env.builder.build_phi(env.context.i64_type(), "merge_hash");
let tag_id_layout = union_layout.tag_id_layout();
let tag_id_basic_type = basic_type_from_layout(env, &tag_id_layout);
let merge_phi = env.builder.build_phi(seed.get_type(), "merge_hash");
env.builder.position_at_end(entry_block);
match union_layout {
NonRecursive(tags) => {
// SAFETY we know that non-recursive tags cannot be NULL
let tag_id = nonrec_tag_id(env, tag.into_struct_value());
let current_tag_id = get_tag_id(env, parent, union_layout, tag);
let mut cases = Vec::with_capacity_in(tags.len(), env.arena);
@ -418,7 +420,6 @@ fn hash_tag<'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);
@ -427,6 +428,24 @@ fn hash_tag<'a, 'ctx, 'env>(
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,
tag_id_basic_type
.into_int_type()
.const_int(tag_id as u64, false)
.into(),
&tag_id_layout,
);
let seed = hash_bitcode_fn(
env,
seed,
hash_bytes,
tag_id_layout.stack_size(env.ptr_bytes),
);
// hash the tag data
let answer = build_hash_struct(
env,
layout_ids,
@ -440,7 +459,7 @@ fn hash_tag<'a, 'ctx, 'env>(
env.builder.build_unconditional_branch(merge_block);
cases.push((
env.context.i64_type().const_int(tag_id as u64, false),
current_tag_id.get_type().const_int(tag_id as u64, false),
block,
));
}
@ -449,11 +468,10 @@ fn hash_tag<'a, 'ctx, 'env>(
let default = cases.pop().unwrap().1;
env.builder.build_switch(tag_id, default, &cases);
env.builder.build_switch(current_tag_id, default, &cases);
}
Recursive(tags) => {
// SAFETY recursive tag unions are not NULL
let tag_id = unsafe { rec_tag_id_unsafe(env, tag.into_pointer_value()) };
let current_tag_id = get_tag_id(env, parent, union_layout, tag);
let mut cases = Vec::with_capacity_in(tags.len(), env.arena);
@ -461,6 +479,23 @@ fn hash_tag<'a, 'ctx, 'env>(
let block = env.context.append_basic_block(parent, "tag_id_modify");
env.builder.position_at_end(block);
// hash the tag id
let hash_bytes = store_and_use_as_u8_ptr(
env,
tag_id_basic_type
.into_int_type()
.const_int(tag_id as u64, false)
.into(),
&tag_id_layout,
);
let seed = hash_bitcode_fn(
env,
seed,
hash_bytes,
tag_id_layout.stack_size(env.ptr_bytes),
);
// hash the tag data
let answer = hash_ptr_to_struct(
env,
layout_ids,
@ -474,7 +509,7 @@ fn hash_tag<'a, 'ctx, 'env>(
env.builder.build_unconditional_branch(merge_block);
cases.push((
env.context.i64_type().const_int(tag_id as u64, false),
current_tag_id.get_type().const_int(tag_id as u64, false),
block,
));
}
@ -483,11 +518,10 @@ fn hash_tag<'a, 'ctx, 'env>(
let default = cases.pop().unwrap().1;
env.builder.build_switch(tag_id, default, &cases);
env.builder.build_switch(current_tag_id, default, &cases);
}
NullableUnwrapped { other_fields, .. } => {
let tag = tag.into_pointer_value();
let other_fields = &other_fields[1..];
let is_null = env.builder.build_is_null(tag, "is_null");
@ -516,7 +550,10 @@ fn hash_tag<'a, 'ctx, 'env>(
env.builder.build_unconditional_branch(merge_block);
}
}
NullableWrapped { other_tags, .. } => {
NullableWrapped {
other_tags,
nullable_id,
} => {
let tag = tag.into_pointer_value();
let is_null = env.builder.build_is_null(tag, "is_null");
@ -537,31 +574,57 @@ fn hash_tag<'a, 'ctx, 'env>(
}
{
env.builder.position_at_end(hash_other_block);
// SAFETY recursive tag unions are not NULL
let tag_id = unsafe { rec_tag_id_unsafe(env, tag) };
let mut cases = Vec::with_capacity_in(other_tags.len(), env.arena);
for (tag_id, field_layouts) in other_tags.iter().enumerate() {
for (mut tag_id, field_layouts) in other_tags.iter().enumerate() {
if tag_id >= *nullable_id as usize {
tag_id += 1;
}
let block = env.context.append_basic_block(parent, "tag_id_modify");
env.builder.position_at_end(block);
let answer =
hash_ptr_to_struct(env, layout_ids, union_layout, field_layouts, seed, tag);
// hash the tag id
let hash_bytes = store_and_use_as_u8_ptr(
env,
tag_id_basic_type
.into_int_type()
.const_int(tag_id as u64, false)
.into(),
&tag_id_layout,
);
let seed1 = hash_bitcode_fn(
env,
seed,
hash_bytes,
tag_id_layout.stack_size(env.ptr_bytes),
);
// hash tag data
let answer = hash_ptr_to_struct(
env,
layout_ids,
union_layout,
field_layouts,
seed1,
tag,
);
merge_phi.add_incoming(&[(&answer, block)]);
env.builder.build_unconditional_branch(merge_block);
cases.push((
env.context.i64_type().const_int(tag_id as u64, false),
tag_id_basic_type
.into_int_type()
.const_int(tag_id as u64, false),
block,
));
}
env.builder.position_at_end(hash_other_block);
let tag_id = get_tag_id(env, parent, union_layout, tag.into());
let default = cases.pop().unwrap().1;
env.builder.build_switch(tag_id, default, &cases);
@ -768,18 +831,27 @@ fn hash_ptr_to_struct<'a, 'ctx, 'env>(
) -> IntValue<'ctx> {
use inkwell::types::BasicType;
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 wrapper_type = basic_type_from_layout(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")
.into_pointer_value();
let struct_ptr = env
.builder
.build_struct_gep(wrapper_ptr, TAG_DATA_INDEX, "get_tag_data")
.unwrap();
let struct_layout = Layout::Struct(field_layouts);
let struct_type = basic_type_from_layout(env, &struct_layout);
let struct_ptr = env
.builder
.build_bitcast(
tag,
wrapper_type.ptr_type(inkwell::AddressSpace::Generic),
"opaque_to_correct",
struct_ptr,
struct_type.ptr_type(inkwell::AddressSpace::Generic),
"cast_tag_data",
)
.into_pointer_value();
@ -807,14 +879,13 @@ fn store_and_use_as_u8_ptr<'a, 'ctx, 'env>(
let alloc = env.builder.build_alloca(basic_type, "store");
env.builder.build_store(alloc, value);
let u8_ptr = env
.context
.i8_type()
.ptr_type(inkwell::AddressSpace::Generic);
env.builder
.build_bitcast(
alloc,
env.context
.i8_type()
.ptr_type(inkwell::AddressSpace::Generic),
"as_u8_ptr",
)
.build_bitcast(alloc, u8_ptr, "as_u8_ptr")
.into_pointer_value()
}
@ -833,34 +904,3 @@ fn hash_bitcode_fn<'a, 'ctx, 'env>(
)
.into_int_value()
}
fn nonrec_tag_id<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
tag: StructValue<'ctx>,
) -> IntValue<'ctx> {
complex_bitcast(
env.builder,
tag.into(),
env.context.i64_type().into(),
"load_tag_id",
)
.into_int_value()
}
unsafe fn rec_tag_id_unsafe<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
tag: PointerValue<'ctx>,
) -> IntValue<'ctx> {
let ptr = env
.builder
.build_bitcast(
tag,
env.context
.i64_type()
.ptr_type(inkwell::AddressSpace::Generic),
"cast_for_tag_id",
)
.into_pointer_value();
env.builder.build_load(ptr, "load_tag_id").into_int_value()
}

View file

@ -1,7 +1,7 @@
#![allow(clippy::too_many_arguments)]
use crate::llvm::bitcode::{
build_dec_wrapper, build_eq_wrapper, build_inc_n_wrapper, build_inc_wrapper, call_bitcode_fn,
call_void_bitcode_fn,
build_dec_wrapper, build_eq_wrapper, build_has_tag_id, build_inc_n_wrapper, build_inc_wrapper,
call_bitcode_fn, call_void_bitcode_fn,
};
use crate::llvm::build::{
allocate_with_refcount_help, cast_basic_basic, complex_bitcast, Env, RocFunctionCall,
@ -13,6 +13,7 @@ use inkwell::context::Context;
use inkwell::types::{BasicType, BasicTypeEnum, PointerType};
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
use inkwell::{AddressSpace, IntPredicate};
use morphic_lib::UpdateMode;
use roc_builtins::bitcode;
use roc_mono::layout::{Builtin, Layout, LayoutIds};
@ -350,6 +351,7 @@ pub fn list_set<'a, 'ctx, 'env>(
index: IntValue<'ctx>,
element: BasicValueEnum<'ctx>,
element_layout: &'a Layout<'a>,
update_mode: UpdateMode,
) -> BasicValueEnum<'ctx> {
let dec_element_fn = build_dec_wrapper(env, layout_ids, element_layout);
@ -359,19 +361,32 @@ pub fn list_set<'a, 'ctx, 'env>(
env.context.i8_type().ptr_type(AddressSpace::Generic),
);
let new_bytes = call_bitcode_fn(
env,
&[
bytes.into(),
length.into(),
env.alignment_intvalue(&element_layout),
index.into(),
pass_element_as_opaque(env, element),
layout_width(env, element_layout),
dec_element_fn.as_global_value().as_pointer_value().into(),
],
&bitcode::LIST_SET,
);
let new_bytes = match update_mode {
UpdateMode::InPlace => call_bitcode_fn(
env,
&[
bytes.into(),
index.into(),
pass_element_as_opaque(env, element),
layout_width(env, element_layout),
dec_element_fn.as_global_value().as_pointer_value().into(),
],
bitcode::LIST_SET_IN_PLACE,
),
UpdateMode::Immutable => call_bitcode_fn(
env,
&[
bytes.into(),
length.into(),
env.alignment_intvalue(&element_layout),
index.into(),
pass_element_as_opaque(env, element),
layout_width(env, element_layout),
dec_element_fn.as_global_value().as_pointer_value().into(),
],
bitcode::LIST_SET,
),
};
store_list(env, new_bytes.into_pointer_value(), length)
}
@ -410,6 +425,7 @@ pub fn list_walk_generic<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
roc_function_call: RocFunctionCall<'ctx>,
function_call_return_layout: &Layout<'a>,
list: BasicValueEnum<'ctx>,
element_layout: &Layout<'a>,
default: BasicValueEnum<'ctx>,
@ -450,6 +466,18 @@ pub fn list_walk_generic<'a, 'ctx, 'env>(
);
}
ListWalk::WalkUntil | ListWalk::WalkBackwardsUntil => {
let function = env
.builder
.get_insert_block()
.unwrap()
.get_parent()
.unwrap();
let has_tag_id = match function_call_return_layout {
Layout::Union(union_layout) => build_has_tag_id(env, function, *union_layout),
_ => unreachable!(),
};
let dec_element_fn = build_dec_wrapper(env, layout_ids, element_layout);
call_void_bitcode_fn(
env,
@ -462,7 +490,9 @@ pub fn list_walk_generic<'a, 'ctx, 'env>(
pass_as_opaque(env, default_ptr),
env.alignment_intvalue(&element_layout),
layout_width(env, element_layout),
layout_width(env, function_call_return_layout),
layout_width(env, default_layout),
has_tag_id.as_global_value().as_pointer_value().into(),
dec_element_fn.as_global_value().as_pointer_value().into(),
pass_as_opaque(env, result_ptr),
],
@ -604,6 +634,18 @@ pub fn list_keep_oks<'a, 'ctx, 'env>(
) -> BasicValueEnum<'ctx> {
let dec_result_fn = build_dec_wrapper(env, layout_ids, result_layout);
let function = env
.builder
.get_insert_block()
.unwrap()
.get_parent()
.unwrap();
let has_tag_id = match result_layout {
Layout::Union(union_layout) => build_has_tag_id(env, function, *union_layout),
_ => unreachable!(),
};
call_bitcode_fn(
env,
&[
@ -616,6 +658,7 @@ pub fn list_keep_oks<'a, 'ctx, 'env>(
layout_width(env, before_layout),
layout_width(env, result_layout),
layout_width(env, after_layout),
has_tag_id.as_global_value().as_pointer_value().into(),
dec_result_fn.as_global_value().as_pointer_value().into(),
],
bitcode::LIST_KEEP_OKS,
@ -635,6 +678,18 @@ pub fn list_keep_errs<'a, 'ctx, 'env>(
) -> BasicValueEnum<'ctx> {
let dec_result_fn = build_dec_wrapper(env, layout_ids, result_layout);
let function = env
.builder
.get_insert_block()
.unwrap()
.get_parent()
.unwrap();
let has_tag_id = match result_layout {
Layout::Union(union_layout) => build_has_tag_id(env, function, *union_layout),
_ => unreachable!(),
};
call_bitcode_fn(
env,
&[
@ -647,6 +702,7 @@ pub fn list_keep_errs<'a, 'ctx, 'env>(
layout_width(env, before_layout),
layout_width(env, result_layout),
layout_width(env, after_layout),
has_tag_id.as_global_value().as_pointer_value().into(),
dec_result_fn.as_global_value().as_pointer_value().into(),
],
bitcode::LIST_KEEP_ERRS,
@ -1080,7 +1136,9 @@ pub fn allocate_list<'a, 'ctx, 'env>(
// we assume that the list is indeed used (dead variables are eliminated)
let rc1 = crate::llvm::refcounting::refcount_1(ctx, env.ptr_bytes);
allocate_with_refcount_help(env, elem_layout, number_of_data_bytes, rc1)
let basic_type = basic_type_from_layout(env, elem_layout);
let alignment_bytes = elem_layout.alignment_bytes(env.ptr_bytes);
allocate_with_refcount_help(env, basic_type, alignment_bytes, number_of_data_bytes, rc1)
}
pub fn store_list<'a, 'ctx, 'env>(

View file

@ -1,6 +1,6 @@
use crate::llvm::bitcode::call_bitcode_fn;
use crate::llvm::build::Env;
use crate::llvm::build::{cast_block_of_memory_to_tag, complex_bitcast, FAST_CALL_CONV};
use crate::llvm::build::{cast_block_of_memory_to_tag, get_tag_id, 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;
@ -850,9 +850,8 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
match union_layout {
NonRecursive(tags) => {
// SAFETY we know that non-recursive tags cannot be NULL
let id1 = nonrec_tag_id(env, tag1.into_struct_value());
let id2 = nonrec_tag_id(env, tag2.into_struct_value());
let id1 = get_tag_id(env, parent, union_layout, tag1);
let id2 = get_tag_id(env, parent, union_layout, tag2);
let compare_tag_fields = ctx.append_basic_block(parent, "compare_tag_fields");
@ -901,10 +900,7 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
env.builder.build_return(Some(&answer));
cases.push((
env.context.i64_type().const_int(tag_id as u64, false),
block,
));
cases.push((id1.get_type().const_int(tag_id as u64, false), block));
}
env.builder.position_at_end(compare_tag_fields);
@ -930,9 +926,8 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
env.builder.position_at_end(compare_tag_ids);
// SAFETY we know that non-recursive tags cannot be NULL
let id1 = unsafe { rec_tag_id_unsafe(env, tag1.into_pointer_value()) };
let id2 = unsafe { rec_tag_id_unsafe(env, tag2.into_pointer_value()) };
let id1 = get_tag_id(env, parent, union_layout, tag1);
let id2 = get_tag_id(env, parent, union_layout, tag2);
let compare_tag_fields = ctx.append_basic_block(parent, "compare_tag_fields");
@ -964,10 +959,7 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
env.builder.build_return(Some(&answer));
cases.push((
env.context.i64_type().const_int(tag_id as u64, false),
block,
));
cases.push((id1.get_type().const_int(tag_id as u64, false), block));
}
env.builder.position_at_end(compare_tag_fields);
@ -977,9 +969,6 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
env.builder.build_switch(id1, default, &cases);
}
NullableUnwrapped { other_fields, .. } => {
// drop the tag id; it is not stored
let other_fields = &other_fields[1..];
let ptr_equal = env.builder.build_int_compare(
IntPredicate::EQ,
env.builder
@ -1085,9 +1074,8 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
env.builder.position_at_end(compare_other);
// SAFETY we know at this point that tag1/tag2 are not NULL
let id1 = unsafe { rec_tag_id_unsafe(env, tag1.into_pointer_value()) };
let id2 = unsafe { rec_tag_id_unsafe(env, tag2.into_pointer_value()) };
let id1 = get_tag_id(env, parent, union_layout, tag1);
let id2 = get_tag_id(env, parent, union_layout, tag2);
let compare_tag_fields = ctx.append_basic_block(parent, "compare_tag_fields");
@ -1120,10 +1108,7 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
env.builder.build_return(Some(&answer));
cases.push((
env.context.i64_type().const_int(tag_id as u64, false),
block,
));
cases.push((id1.get_type().const_int(tag_id as u64, false), block));
}
env.builder.position_at_end(compare_tag_fields);
@ -1215,32 +1200,3 @@ fn eq_ptr_to_struct<'a, 'ctx, 'env>(
)
.into_int_value()
}
fn nonrec_tag_id<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
tag: StructValue<'ctx>,
) -> IntValue<'ctx> {
complex_bitcast(
env.builder,
tag.into(),
env.context.i64_type().into(),
"load_tag_id",
)
.into_int_value()
}
unsafe fn rec_tag_id_unsafe<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
tag: PointerValue<'ctx>,
) -> IntValue<'ctx> {
let ptr = env
.builder
.build_bitcast(
tag,
env.context.i64_type().ptr_type(AddressSpace::Generic),
"cast_for_tag_id",
)
.into_pointer_value();
env.builder.build_load(ptr, "load_tag_id").into_int_value()
}

View file

@ -33,24 +33,42 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>(
Struct(sorted_fields) => basic_type_from_record(env, sorted_fields),
Union(variant) => {
use UnionLayout::*;
let tag_id_type = basic_type_from_layout(env, &variant.tag_id_layout());
match variant {
Recursive(tags)
| NullableWrapped {
NullableWrapped {
other_tags: tags, ..
} => {
let block = block_of_memory_slices(env.context, tags, env.ptr_bytes);
block.ptr_type(AddressSpace::Generic).into()
let data = block_of_memory_slices(env.context, tags, env.ptr_bytes);
env.context
.struct_type(&[data, tag_id_type], false)
.ptr_type(AddressSpace::Generic)
.into()
}
NullableUnwrapped { other_fields, .. } => {
let block =
block_of_memory_slices(env.context, &[&other_fields[1..]], env.ptr_bytes);
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()
}
NonRecursive(_) => block_of_memory(env.context, layout, env.ptr_bytes),
Recursive(tags) => {
let data = block_of_memory_slices(env.context, tags, env.ptr_bytes);
env.context
.struct_type(&[data, tag_id_type], false)
.ptr_type(AddressSpace::Generic)
.into()
}
NonRecursive(tags) => {
let data = block_of_memory_slices(env.context, tags, env.ptr_bytes);
env.context.struct_type(&[data, tag_id_type], false).into()
}
}
}
RecursivePointer => {
@ -112,13 +130,43 @@ pub fn block_of_memory_slices<'ctx>(
block_of_memory_help(context, union_size)
}
pub fn union_data_is_struct<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
layouts: &[Layout<'_>],
) -> StructType<'ctx> {
let data_type = basic_type_from_record(env, layouts);
union_data_is_struct_type(env.context, data_type.into_struct_type())
}
pub fn union_data_is_struct_type<'ctx>(
context: &'ctx Context,
struct_type: StructType<'ctx>,
) -> StructType<'ctx> {
let tag_id_type = context.i64_type();
context.struct_type(&[struct_type.into(), tag_id_type.into()], false)
}
pub fn union_data_block_of_memory<'ctx>(
context: &'ctx Context,
tag_id_int_type: IntType<'ctx>,
layouts: &[&[Layout<'_>]],
ptr_bytes: u32,
) -> StructType<'ctx> {
let data_type = block_of_memory_slices(context, layouts, ptr_bytes);
context.struct_type(&[data_type, tag_id_int_type.into()], false)
}
pub fn block_of_memory<'ctx>(
context: &'ctx Context,
layout: &Layout<'_>,
ptr_bytes: u32,
) -> BasicTypeEnum<'ctx> {
// TODO make this dynamic
let union_size = layout.stack_size(ptr_bytes as u32);
let mut union_size = layout.stack_size(ptr_bytes as u32);
if let Layout::Union(UnionLayout::NonRecursive { .. }) = layout {
union_size -= ptr_bytes;
}
block_of_memory_help(context, union_size)
}
@ -182,8 +230,8 @@ pub fn zig_str_type<'a, 'ctx, 'env>(
env.module.get_struct_type("str.RocStr").unwrap()
}
// pub fn zig_dec_type<'a, 'ctx, 'env>(
// env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
// ) -> StructType<'ctx> {
// env.module.get_struct_type("dec.RocDec").unwrap()
// }
pub fn zig_has_tag_id_type<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
) -> StructType<'ctx> {
env.module.get_struct_type("list.HasTagId").unwrap()
}

View file

@ -1,11 +1,11 @@
use crate::debug_info_init;
use crate::llvm::build::{
add_func, cast_basic_basic, cast_block_of_memory_to_tag, Env, FAST_CALL_CONV,
LLVM_SADD_WITH_OVERFLOW_I64,
add_func, cast_basic_basic, cast_block_of_memory_to_tag, get_tag_id, get_tag_id_non_recursive,
Env, FAST_CALL_CONV, LLVM_SADD_WITH_OVERFLOW_I64, TAG_DATA_INDEX,
};
use crate::llvm::build_list::{incrementing_elem_loop, list_len, load_list};
use crate::llvm::convert::{
basic_type_from_layout, block_of_memory, block_of_memory_slices, ptr_int,
basic_type_from_layout, block_of_memory_slices, ptr_int, union_data_block_of_memory,
};
use bumpalo::collections::Vec;
use inkwell::basic_block::BasicBlock;
@ -653,6 +653,7 @@ fn modify_refcount_layout_build_function<'a, 'ctx, 'env>(
layout_ids,
mode,
&WhenRecursive::Loop(*variant),
*variant,
tags,
true,
);
@ -661,14 +662,13 @@ fn modify_refcount_layout_build_function<'a, 'ctx, 'env>(
}
NullableUnwrapped { other_fields, .. } => {
let other_fields = &other_fields[1..];
let function = build_rec_union(
env,
layout_ids,
mode,
&WhenRecursive::Loop(*variant),
&*env.arena.alloc([other_fields]),
*variant,
env.arena.alloc([*other_fields]),
true,
);
@ -681,6 +681,7 @@ fn modify_refcount_layout_build_function<'a, 'ctx, 'env>(
layout_ids,
mode,
&WhenRecursive::Loop(*variant),
*variant,
&*env.arena.alloc([*fields]),
true,
);
@ -693,6 +694,7 @@ fn modify_refcount_layout_build_function<'a, 'ctx, 'env>(
layout_ids,
mode,
&WhenRecursive::Loop(*variant),
*variant,
tags,
false,
);
@ -1205,10 +1207,11 @@ fn build_rec_union<'a, 'ctx, 'env>(
layout_ids: &mut LayoutIds<'a>,
mode: Mode,
when_recursive: &WhenRecursive<'a>,
fields: &'a [&'a [Layout<'a>]],
union_layout: UnionLayout<'a>,
tags: &'a [&'a [Layout<'a>]],
is_nullable: bool,
) -> FunctionValue<'ctx> {
let layout = Layout::Union(UnionLayout::Recursive(fields));
let layout = Layout::Union(UnionLayout::Recursive(tags));
let (_, fn_name) = function_name_from_mode(
layout_ids,
@ -1225,9 +1228,7 @@ fn build_rec_union<'a, 'ctx, 'env>(
let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap();
let basic_type = block_of_memory_slices(env.context, fields, env.ptr_bytes)
.ptr_type(AddressSpace::Generic)
.into();
let basic_type = basic_type_from_layout(env, &Layout::Union(union_layout));
let function_value = build_header(env, basic_type, mode, &fn_name);
build_rec_union_help(
@ -1235,7 +1236,8 @@ fn build_rec_union<'a, 'ctx, 'env>(
layout_ids,
mode,
when_recursive,
fields,
union_layout,
tags,
function_value,
is_nullable,
);
@ -1251,11 +1253,13 @@ fn build_rec_union<'a, 'ctx, 'env>(
function
}
#[allow(clippy::too_many_arguments)]
fn build_rec_union_help<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
mode: Mode,
when_recursive: &WhenRecursive<'a>,
union_layout: UnionLayout<'a>,
tags: &'a [&'a [roc_mono::layout::Layout<'a>]],
fn_val: FunctionValue<'ctx>,
is_nullable: bool,
@ -1281,8 +1285,6 @@ fn build_rec_union_help<'a, 'ctx, 'env>(
let parent = fn_val;
let layout = Layout::Union(UnionLayout::Recursive(tags));
debug_assert!(arg_val.is_pointer_value());
let value_ptr = arg_val.into_pointer_value();
@ -1311,6 +1313,8 @@ fn build_rec_union_help<'a, 'ctx, 'env>(
env.builder.position_at_end(should_recurse_block);
let layout = Layout::Union(union_layout);
match mode {
Mode::Inc => {
// inc is cheap; we never recurse
@ -1344,7 +1348,7 @@ fn build_rec_union_help<'a, 'ctx, 'env>(
when_recursive,
parent,
fn_val,
layout,
union_layout,
tags,
value_ptr,
refcount_ptr,
@ -1362,7 +1366,7 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>(
when_recursive: &WhenRecursive<'a>,
parent: FunctionValue<'ctx>,
decrement_fn: FunctionValue<'ctx>,
layout: Layout<'a>,
union_layout: UnionLayout<'a>,
tags: &[&[Layout<'a>]],
value_ptr: PointerValue<'ctx>,
refcount_ptr: PointerToRefcount<'ctx>,
@ -1390,6 +1394,9 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>(
// 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 tag_id_int_type =
basic_type_from_layout(env, &union_layout.tag_id_layout()).into_int_type();
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
@ -1435,7 +1442,23 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>(
debug_assert!(ptr_as_i64_ptr.is_pointer_value());
// therefore we must cast it to our desired type
let union_type = block_of_memory_slices(env.context, tags, env.ptr_bytes);
let union_type = match union_layout {
UnionLayout::NonRecursive(_) => unreachable!(),
UnionLayout::Recursive(_) | UnionLayout::NullableWrapped { .. } => {
union_data_block_of_memory(
env.context,
tag_id_int_type,
tags,
env.ptr_bytes,
)
.into()
}
UnionLayout::NonNullableUnwrapped { .. }
| UnionLayout::NullableUnwrapped { .. } => {
block_of_memory_slices(env.context, tags, env.ptr_bytes)
}
};
let recursive_field_ptr = cast_basic_basic(
env.builder,
ptr_as_i64_ptr,
@ -1463,7 +1486,7 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>(
// lists. To achieve it, we must first load all fields that we want to inc/dec (done above)
// and store them on the stack, then modify (and potentially free) the current cell, then
// actually inc/dec the fields.
refcount_ptr.modify(call_mode, &layout, env);
refcount_ptr.modify(call_mode, &Layout::Union(union_layout), env);
for (field, field_layout) in deferred_nonrec {
modify_refcount_layout_help(
@ -1486,10 +1509,7 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>(
// this function returns void
builder.build_return(None);
cases.push((
env.context.i64_type().const_int(tag_id as u64, false),
block,
));
cases.push((tag_id_int_type.const_int(tag_id as u64, false), block));
}
env.builder.position_at_end(match_block);
@ -1505,7 +1525,7 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>(
env.builder.build_unconditional_branch(only_branch);
} else {
// read the tag_id
let current_tag_id = rec_union_read_tag(env, value_ptr);
let current_tag_id = get_tag_id(env, parent, &union_layout, value_ptr.into());
let merge_block = env.context.append_basic_block(parent, "decrement_merge");
@ -1516,30 +1536,13 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>(
env.builder.position_at_end(merge_block);
// increment/decrement the cons-cell itself
refcount_ptr.modify(call_mode, &layout, env);
refcount_ptr.modify(call_mode, &Layout::Union(union_layout), env);
// this function returns void
builder.build_return(None);
}
}
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_type = env.context.i64_type().ptr_type(AddressSpace::Generic);
let tag_ptr = env
.builder
.build_bitcast(value_ptr, tag_ptr_type, "cast_tag_ptr")
.into_pointer_value();
env.builder
.build_load(tag_ptr, "load_tag_id")
.into_int_value()
}
fn function_name_from_mode<'a>(
layout_ids: &mut LayoutIds<'a>,
interns: &Interns,
@ -1584,7 +1587,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 = block_of_memory(env.context, &layout, env.ptr_bytes);
let basic_type = basic_type_from_layout(env, &layout);
let function_value = build_header(env, basic_type, mode, &fn_name);
modify_refcount_union_help(
@ -1640,19 +1643,7 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
let wrapper_struct = arg_val.into_struct_value();
// read the tag_id
let tag_id = {
// the first element of the wrapping struct is an array of i64
let first_array = env
.builder
.build_extract_value(wrapper_struct, 0, "read_tag_id")
.unwrap()
.into_array_value();
env.builder
.build_extract_value(first_array, 0, "read_tag_id_2")
.unwrap()
.into_int_value()
};
let tag_id = get_tag_id_non_recursive(env, wrapper_struct);
let tag_id_u8 = env
.builder
@ -1680,7 +1671,12 @@ 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 wrapper_struct = cast_block_of_memory_to_tag(env.builder, wrapper_struct, wrapper_type);
let data_bytes = 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);
for (i, field_layout) in field_layouts.iter().enumerate() {
if let Layout::RecursivePointer = field_layout {