Merge branch 'trunk' of github.com:rtfeldman/roc into list-str-capacity

This commit is contained in:
Brian Carroll 2022-03-11 19:28:13 +00:00
commit 456d8ff9cb
41 changed files with 790 additions and 210 deletions

View file

@ -260,8 +260,12 @@ fn build_has_tag_id_help<'a, 'ctx, 'env>(
tag_value.into(),
);
env.builder
.build_int_cast(tag_id_i64, env.context.i16_type(), "to_i16")
env.builder.build_int_cast_sign_flag(
tag_id_i64,
env.context.i16_type(),
true,
"to_i16",
)
};
let answer = env.builder.build_int_compare(

View file

@ -24,7 +24,7 @@ use crate::llvm::build_str::{
};
use crate::llvm::compare::{generic_eq, generic_neq};
use crate::llvm::convert::{
self, basic_type_from_builtin, basic_type_from_layout, basic_type_from_layout_1,
self, argument_type_from_layout, basic_type_from_builtin, basic_type_from_layout,
block_of_memory_slices, zig_str_type,
};
use crate::llvm::refcounting::{
@ -1100,6 +1100,30 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
..
} => build_tag(env, scope, union_layout, *tag_id, arguments, None, parent),
ExprBox { symbol } => {
let (value, layout) = load_symbol_and_layout(scope, symbol);
let basic_type = basic_type_from_layout(env, layout);
let allocation = reserve_with_refcount_help(
env,
basic_type,
layout.stack_size(env.target_info),
layout.alignment_bytes(env.target_info),
);
env.builder.build_store(allocation, value);
allocation.into()
}
ExprUnbox { symbol } => {
let value = load_symbol(scope, symbol);
debug_assert!(value.is_pointer_value());
env.builder
.build_load(value.into_pointer_value(), "load_boxed_value")
}
Reset { symbol, .. } => {
let (tag_ptr, layout) = load_symbol_and_layout(scope, symbol);
let tag_ptr = tag_ptr.into_pointer_value();
@ -1798,7 +1822,7 @@ pub fn tag_pointer_read_tag_id<'a, 'ctx, 'env>(
let masked = env.builder.build_and(as_int, mask_intval, "mask");
env.builder
.build_int_cast(masked, env.context.i8_type(), "to_u8")
.build_int_cast_sign_flag(masked, env.context.i8_type(), false, "to_u8")
}
pub fn tag_pointer_clear_tag_id<'a, 'ctx, 'env>(
@ -2553,6 +2577,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
let align_bytes = layout.alignment_bytes(env.target_info);
if align_bytes > 0 {
debug_assert!(value.is_pointer_value(), "{:?}\n{:?}", value, layout);
let value_ptr = value.into_pointer_value();
// We can only do this if the function itself writes data into this
@ -4197,7 +4222,7 @@ fn build_proc_header<'a, 'ctx, 'env>(
let mut arg_basic_types = Vec::with_capacity_in(args.len(), arena);
for (layout, _) in args.iter() {
let arg_type = basic_type_from_layout_1(env, layout);
let arg_type = argument_type_from_layout(env, layout);
arg_basic_types.push(arg_type);
}
@ -5864,8 +5889,11 @@ fn run_low_level<'a, 'ctx, 'env>(
let arg = load_symbol(scope, &args[0]).into_int_value();
let to = basic_type_from_layout(env, layout).into_int_type();
let to_signed = intwidth_from_layout(*layout).is_signed();
env.builder.build_int_cast(arg, to, "inc_cast").into()
env.builder
.build_int_cast_sign_flag(arg, to, to_signed, "inc_cast")
.into()
}
Eq => {
debug_assert_eq!(args.len(), 2);
@ -6091,6 +6119,10 @@ fn run_low_level<'a, 'ctx, 'env>(
unreachable!("these are higher order, and are handled elsewhere")
}
BoxExpr | UnboxExpr => {
unreachable!("The {:?} operation is turned into mono Expr", op)
}
PtrCast | RefCountInc | RefCountDec => {
unreachable!("Not used in LLVM backend: {:?}", op);
}
@ -6969,7 +7001,12 @@ fn build_int_unary_op<'a, 'ctx, 'env>(
let target_int_type = convert::int_type_from_int_width(env, target_int_width);
let target_int_val: BasicValueEnum<'ctx> = env
.builder
.build_int_cast(arg, target_int_type, "int_cast")
.build_int_cast_sign_flag(
arg,
target_int_type,
target_int_width.is_signed(),
"int_cast",
)
.into();
let return_type =

View file

@ -67,7 +67,12 @@ pub fn dict_len<'a, 'ctx, 'env>(
);
env.builder
.build_int_cast(length_i64.into_int_value(), env.ptr_int(), "to_usize")
.build_int_cast_sign_flag(
length_i64.into_int_value(),
env.ptr_int(),
false,
"to_usize",
)
.into()
}
_ => unreachable!("Invalid layout given to Dict.len : {:?}", dict_layout),

View file

@ -4,7 +4,7 @@ use crate::llvm::build::tag_pointer_clear_tag_id;
use crate::llvm::build::Env;
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, basic_type_from_layout_1};
use crate::llvm::convert::basic_type_from_layout;
use bumpalo::collections::Vec;
use inkwell::values::{
BasicValue, BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue,
@ -13,6 +13,8 @@ use roc_builtins::bitcode;
use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout};
use super::convert::argument_type_from_union_layout;
#[derive(Clone, Debug)]
enum WhenRecursive<'a> {
Unreachable,
@ -68,8 +70,11 @@ fn build_hash_layout<'a, 'ctx, 'env>(
when_recursive,
),
Layout::Union(union_layout) => {
build_hash_tag(env, layout_ids, layout, union_layout, seed, val)
Layout::Union(union_layout) => build_hash_tag(env, layout_ids, union_layout, seed, val),
Layout::Boxed(_inner_layout) => {
// build_hash_box(env, layout_ids, layout, inner_layout, seed, val)
todo!()
}
Layout::RecursivePointer => match when_recursive {
@ -87,14 +92,7 @@ fn build_hash_layout<'a, 'ctx, 'env>(
.build_bitcast(val, bt, "i64_to_opaque")
.into_pointer_value();
build_hash_tag(
env,
layout_ids,
&layout,
&union_layout,
seed,
field_cast.into(),
)
build_hash_tag(env, layout_ids, &union_layout, seed, field_cast.into())
}
},
}
@ -308,7 +306,6 @@ fn hash_struct<'a, 'ctx, 'env>(
fn build_hash_tag<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
layout: &Layout<'a>,
union_layout: &UnionLayout<'a>,
seed: IntValue<'ctx>,
value: BasicValueEnum<'ctx>,
@ -318,7 +315,7 @@ fn build_hash_tag<'a, 'ctx, 'env>(
let symbol = Symbol::GENERIC_HASH;
let fn_name = layout_ids
.get(symbol, layout)
.get(symbol, &Layout::Union(*union_layout))
.to_symbol_string(symbol, &env.interns);
let function = match env.module.get_function(fn_name.as_str()) {
@ -326,7 +323,7 @@ fn build_hash_tag<'a, 'ctx, 'env>(
None => {
let seed_type = env.context.i64_type();
let arg_type = basic_type_from_layout_1(env, layout);
let arg_type = argument_type_from_union_layout(env, union_layout);
let function_value = crate::llvm::refcounting::build_header_help(
env,
@ -805,7 +802,7 @@ fn hash_ptr_to_struct<'a, 'ctx, 'env>(
) -> IntValue<'ctx> {
use inkwell::types::BasicType;
let wrapper_type = basic_type_from_layout_1(env, &Layout::Union(*union_layout));
let wrapper_type = argument_type_from_union_layout(env, union_layout);
// cast the opaque pointer to a pointer of the correct shape
let wrapper_ptr = env

View file

@ -82,7 +82,7 @@ fn pass_element_as_opaque<'a, 'ctx, 'env>(
env.builder.build_bitcast(
element_ptr,
env.context.i8_type().ptr_type(AddressSpace::Generic),
"to_opaque",
"pass_element_as_opaque",
)
}
@ -102,7 +102,7 @@ pub fn pass_as_opaque<'a, 'ctx, 'env>(
env.builder.build_bitcast(
ptr,
env.context.i8_type().ptr_type(AddressSpace::Generic),
"to_opaque",
"pass_as_opaque",
)
}
@ -434,10 +434,19 @@ pub fn list_walk_generic<'a, 'ctx, 'env>(
ListWalk::WalkBackwardsUntil => todo!(),
};
let default_ptr = builder.build_alloca(default.get_type(), "default_ptr");
env.builder.build_store(default_ptr, default);
let default_ptr = if default_layout.is_passed_by_reference() {
debug_assert!(default.is_pointer_value());
default.into_pointer_value()
} else {
let default_ptr = builder.build_alloca(default.get_type(), "default_ptr");
env.builder.build_store(default_ptr, default);
default_ptr
};
let result_ptr = env.builder.build_alloca(default.get_type(), "result");
let result_ptr = {
let basic_type = basic_type_from_layout(env, default_layout);
env.builder.build_alloca(basic_type, "result")
};
match variant {
ListWalk::Walk | ListWalk::WalkBackwards => {
@ -494,7 +503,11 @@ pub fn list_walk_generic<'a, 'ctx, 'env>(
}
}
env.builder.build_load(result_ptr, "load_result")
if default_layout.is_passed_by_reference() {
result_ptr.into()
} else {
env.builder.build_load(result_ptr, "load_result")
}
}
/// List.range : Int a, Int a -> List (Int a)

View file

@ -170,7 +170,7 @@ pub fn str_number_of_bytes<'a, 'ctx, 'env>(
// cast to the appropriate usize of the current build
env.builder
.build_int_cast(length, env.ptr_int(), "len_as_usize")
.build_int_cast_sign_flag(length, env.ptr_int(), false, "len_as_usize")
}
/// Str.startsWith : Str, Str -> Bool

View file

@ -2,7 +2,7 @@ use crate::llvm::bitcode::call_bitcode_fn;
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, basic_type_from_layout_1};
use crate::llvm::convert::basic_type_from_layout;
use bumpalo::collections::Vec;
use inkwell::types::BasicType;
use inkwell::values::{
@ -15,6 +15,7 @@ use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout};
use super::build::load_roc_value;
use super::convert::argument_type_from_union_layout;
#[derive(Clone, Debug)]
enum WhenRecursive<'a> {
@ -176,12 +177,21 @@ fn build_eq<'a, 'ctx, 'env>(
env,
layout_ids,
when_recursive,
lhs_layout,
union_layout,
lhs_val,
rhs_val,
),
Layout::Boxed(inner_layout) => build_box_eq(
env,
layout_ids,
when_recursive,
lhs_layout,
inner_layout,
lhs_val,
rhs_val,
),
Layout::RecursivePointer => match when_recursive {
WhenRecursive::Unreachable => {
unreachable!("recursion pointers should never be compared directly")
@ -207,7 +217,6 @@ fn build_eq<'a, 'ctx, 'env>(
env,
layout_ids,
WhenRecursive::Loop(union_layout),
&layout,
&union_layout,
field1_cast.into(),
field2_cast.into(),
@ -345,12 +354,12 @@ fn build_neq<'a, 'ctx, 'env>(
result.into()
}
Layout::Union(union_layout) => {
let is_equal = build_tag_eq(
env,
layout_ids,
when_recursive,
lhs_layout,
union_layout,
lhs_val,
rhs_val,
@ -362,6 +371,23 @@ fn build_neq<'a, 'ctx, 'env>(
result.into()
}
Layout::Boxed(inner_layout) => {
let is_equal = build_box_eq(
env,
layout_ids,
when_recursive,
lhs_layout,
inner_layout,
lhs_val,
rhs_val,
)
.into_int_value();
let result: IntValue = env.builder.build_not(is_equal, "negate");
result.into()
}
Layout::RecursivePointer => {
unreachable!("recursion pointers should never be compared directly")
}
@ -764,7 +790,6 @@ fn build_tag_eq<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
when_recursive: WhenRecursive<'a>,
tag_layout: &Layout<'a>,
union_layout: &UnionLayout<'a>,
tag1: BasicValueEnum<'ctx>,
tag2: BasicValueEnum<'ctx>,
@ -772,15 +797,16 @@ fn build_tag_eq<'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 tag_layout = Layout::Union(*union_layout);
let symbol = Symbol::GENERIC_EQ;
let fn_name = layout_ids
.get(symbol, tag_layout)
.get(symbol, &tag_layout)
.to_symbol_string(symbol, &env.interns);
let function = match env.module.get_function(fn_name.as_str()) {
Some(function_value) => function_value,
None => {
let arg_type = basic_type_from_layout_1(env, tag_layout);
let arg_type = argument_type_from_union_layout(env, union_layout);
let function_value = crate::llvm::refcounting::build_header_help(
env,
@ -1101,8 +1127,10 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
let i8_type = env.context.i8_type();
let sum = env.builder.build_int_add(
env.builder.build_int_cast(is_null_1, i8_type, "to_u8"),
env.builder.build_int_cast(is_null_2, i8_type, "to_u8"),
env.builder
.build_int_cast_sign_flag(is_null_1, i8_type, false, "to_u8"),
env.builder
.build_int_cast_sign_flag(is_null_2, i8_type, false, "to_u8"),
"sum_is_null",
);
@ -1252,3 +1280,141 @@ fn eq_ptr_to_struct<'a, 'ctx, 'env>(
)
.into_int_value()
}
/// ----
fn build_box_eq<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
when_recursive: WhenRecursive<'a>,
box_layout: &Layout<'a>,
inner_layout: &Layout<'a>,
tag1: BasicValueEnum<'ctx>,
tag2: BasicValueEnum<'ctx>,
) -> BasicValueEnum<'ctx> {
let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap();
let symbol = Symbol::GENERIC_EQ;
let fn_name = layout_ids
.get(symbol, box_layout)
.to_symbol_string(symbol, &env.interns);
let function = match env.module.get_function(fn_name.as_str()) {
Some(function_value) => function_value,
None => {
let arg_type = basic_type_from_layout(env, box_layout);
let function_value = crate::llvm::refcounting::build_header_help(
env,
&fn_name,
env.context.bool_type().into(),
&[arg_type, arg_type],
);
build_box_eq_help(
env,
layout_ids,
when_recursive,
function_value,
inner_layout,
);
function_value
}
};
env.builder.position_at_end(block);
env.builder
.set_current_debug_location(env.context, di_location);
let call = env
.builder
.build_call(function, &[tag1.into(), tag2.into()], "tag_eq");
call.set_call_convention(FAST_CALL_CONV);
call.try_as_basic_value().left().unwrap()
}
fn build_box_eq_help<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
when_recursive: WhenRecursive<'a>,
parent: FunctionValue<'ctx>,
inner_layout: &Layout<'a>,
) {
let ctx = env.context;
let builder = env.builder;
{
use inkwell::debug_info::AsDIScope;
let func_scope = parent.get_subprogram().unwrap();
let lexical_block = env.dibuilder.create_lexical_block(
/* scope */ func_scope.as_debug_info_scope(),
/* file */ env.compile_unit.get_file(),
/* line_no */ 0,
/* column_no */ 0,
);
let loc = env.dibuilder.create_debug_location(
ctx,
/* line */ 0,
/* column */ 0,
/* current_scope */ lexical_block.as_debug_info_scope(),
/* inlined_at */ None,
);
builder.set_current_debug_location(ctx, loc);
}
// Add args to scope
let mut it = parent.get_param_iter();
let box1 = it.next().unwrap();
let box2 = it.next().unwrap();
box1.set_name(Symbol::ARG_1.as_str(&env.interns));
box2.set_name(Symbol::ARG_2.as_str(&env.interns));
let return_true = ctx.append_basic_block(parent, "return_true");
env.builder.position_at_end(return_true);
env.builder
.build_return(Some(&env.context.bool_type().const_all_ones()));
let entry = ctx.append_basic_block(parent, "entry");
env.builder.position_at_end(entry);
let ptr_equal = env.builder.build_int_compare(
IntPredicate::EQ,
env.builder
.build_ptr_to_int(box1.into_pointer_value(), env.ptr_int(), "pti"),
env.builder
.build_ptr_to_int(box2.into_pointer_value(), env.ptr_int(), "pti"),
"compare_pointers",
);
let compare_inner_value = ctx.append_basic_block(parent, "compare_inner_value");
env.builder
.build_conditional_branch(ptr_equal, return_true, compare_inner_value);
env.builder.position_at_end(compare_inner_value);
// clear the tag_id so we get a pointer to the actual data
let box1 = box1.into_pointer_value();
let box2 = box2.into_pointer_value();
let value1 = env.builder.build_load(box1, "load_box1");
let value2 = env.builder.build_load(box2, "load_box2");
let is_equal = build_eq(
env,
layout_ids,
value1,
value2,
inner_layout,
inner_layout,
when_recursive,
);
env.builder.build_return(Some(&is_equal));
}

View file

@ -1,3 +1,4 @@
use crate::llvm::build::Env;
use bumpalo::collections::Vec;
use inkwell::context::Context;
use inkwell::types::{BasicType, BasicTypeEnum, FloatType, IntType, StructType};
@ -7,7 +8,7 @@ use roc_mono::layout::{Builtin, Layout, UnionLayout};
use roc_target::TargetInfo;
fn basic_type_from_record<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
env: &Env<'a, 'ctx, 'env>,
fields: &[Layout<'_>],
) -> BasicTypeEnum<'ctx> {
let mut field_types = Vec::with_capacity_in(fields.len(), env.arena);
@ -22,7 +23,7 @@ fn basic_type_from_record<'a, 'ctx, 'env>(
}
pub fn basic_type_from_layout<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
env: &Env<'a, 'ctx, 'env>,
layout: &Layout<'_>,
) -> BasicTypeEnum<'ctx> {
use Layout::*;
@ -33,121 +34,64 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>(
..
} => basic_type_from_record(env, sorted_fields),
LambdaSet(lambda_set) => basic_type_from_layout(env, &lambda_set.runtime_representation()),
Union(union_layout) => {
use UnionLayout::*;
Boxed(inner_layout) => {
let inner_type = basic_type_from_layout(env, inner_layout);
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.target_info);
env.context.struct_type(&[data, tag_id_type], false).into()
}
Recursive(tags)
| NullableWrapped {
other_tags: tags, ..
} => {
let data = block_of_memory_slices(env.context, tags, env.target_info);
if union_layout.stores_tag_id_as_data(env.target_info) {
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.target_info);
block.ptr_type(AddressSpace::Generic).into()
}
NonNullableUnwrapped(fields) => {
let block = block_of_memory_slices(env.context, &[fields], env.target_info);
block.ptr_type(AddressSpace::Generic).into()
}
}
}
RecursivePointer => {
// TODO make this dynamic
env.context
.i64_type()
.ptr_type(AddressSpace::Generic)
.as_basic_type_enum()
inner_type.ptr_type(AddressSpace::Generic).into()
}
Union(union_layout) => basic_type_from_union_layout(env, union_layout),
RecursivePointer => 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_layout_1<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
layout: &Layout<'_>,
pub fn basic_type_from_union_layout<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
union_layout: &UnionLayout<'_>,
) -> BasicTypeEnum<'ctx> {
use Layout::*;
use UnionLayout::*;
match layout {
Struct {
field_layouts: sorted_fields,
..
} => basic_type_from_record(env, sorted_fields),
LambdaSet(lambda_set) => {
basic_type_from_layout_1(env, &lambda_set.runtime_representation())
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.target_info);
env.context.struct_type(&[data, tag_id_type], false).into()
}
Union(union_layout) => {
use UnionLayout::*;
Recursive(tags)
| NullableWrapped {
other_tags: tags, ..
} => {
let data = block_of_memory_slices(env.context, tags, env.target_info);
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.target_info);
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.target_info);
if union_layout.stores_tag_id_as_data(env.target_info) {
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.target_info);
block.ptr_type(AddressSpace::Generic).into()
}
NonNullableUnwrapped(fields) => {
let block = block_of_memory_slices(env.context, &[fields], env.target_info);
block.ptr_type(AddressSpace::Generic).into()
}
if union_layout.stores_tag_id_as_data(env.target_info) {
env.context
.struct_type(&[data, tag_id_type], false)
.ptr_type(AddressSpace::Generic)
.into()
} else {
data.ptr_type(AddressSpace::Generic).into()
}
}
RecursivePointer => {
// TODO make this dynamic
env.context
.i64_type()
.ptr_type(AddressSpace::Generic)
.as_basic_type_enum()
NullableUnwrapped { other_fields, .. } => {
let block = block_of_memory_slices(env.context, &[other_fields], env.target_info);
block.ptr_type(AddressSpace::Generic).into()
}
NonNullableUnwrapped(fields) => {
let block = block_of_memory_slices(env.context, &[fields], env.target_info);
block.ptr_type(AddressSpace::Generic).into()
}
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>,
env: &Env<'a, 'ctx, 'env>,
builtin: &Builtin<'_>,
) -> BasicTypeEnum<'ctx> {
use Builtin::*;
@ -166,8 +110,48 @@ pub fn basic_type_from_builtin<'a, 'ctx, 'env>(
}
}
/// Turn a layout into a BasicType that we use in LLVM function arguments.
///
/// This makes it possible to pass values as something different from how they are typically stored.
/// Current differences
///
/// - tag unions are passed by-reference. That means that
/// * `f : [ Some I64, None ] -> I64` is typed `{ { i64, i8 }, i64 }* -> i64`
/// * `f : { x : [ Some I64, None ] } -> I64 is typed `{ { { i64, i8 }, i64 } } -> i64`
///
/// Ideas exist to have (bigger than 2 register) records also be passed by-reference, but this
/// is not currently implemented
pub fn argument_type_from_layout<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout: &Layout<'_>,
) -> BasicTypeEnum<'ctx> {
use Layout::*;
match layout {
LambdaSet(lambda_set) => {
argument_type_from_layout(env, &lambda_set.runtime_representation())
}
Union(union_layout) => argument_type_from_union_layout(env, union_layout),
other => basic_type_from_layout(env, other),
}
}
/// Non-recursive tag unions are stored on the stack, but passed by-reference
pub fn argument_type_from_union_layout<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
union_layout: &UnionLayout<'_>,
) -> BasicTypeEnum<'ctx> {
let heap_type = basic_type_from_union_layout(env, union_layout);
if let UnionLayout::NonRecursive(_) = union_layout {
heap_type.ptr_type(AddressSpace::Generic).into()
} else {
heap_type
}
}
pub fn int_type_from_int_width<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
env: &Env<'a, 'ctx, 'env>,
int_width: IntWidth,
) -> IntType<'ctx> {
use IntWidth::*;
@ -182,7 +166,7 @@ pub fn int_type_from_int_width<'a, 'ctx, 'env>(
}
pub fn float_type_from_float_width<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
env: &Env<'a, 'ctx, 'env>,
float_width: FloatWidth,
) -> FloatType<'ctx> {
use FloatWidth::*;
@ -267,33 +251,23 @@ pub fn str_list_int(ctx: &Context, target_info: TargetInfo) -> IntType<'_> {
}
}
pub fn zig_dict_type<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
) -> StructType<'ctx> {
pub fn zig_dict_type<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> StructType<'ctx> {
env.module.get_struct_type("dict.RocDict").unwrap()
}
pub fn zig_list_type<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
) -> StructType<'ctx> {
pub fn zig_list_type<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> StructType<'ctx> {
env.module.get_struct_type("list.RocList").unwrap()
}
pub fn zig_str_type<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
) -> StructType<'ctx> {
pub fn zig_str_type<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> StructType<'ctx> {
env.module.get_struct_type("str.RocStr").unwrap()
}
pub fn zig_has_tag_id_type<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
) -> StructType<'ctx> {
pub fn zig_has_tag_id_type<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> StructType<'ctx> {
env.module.get_struct_type("list.HasTagId").unwrap()
}
pub fn zig_with_overflow_roc_dec<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
) -> StructType<'ctx> {
pub fn zig_with_overflow_roc_dec<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> StructType<'ctx> {
env.module
.get_struct_type("utils.WithOverflow(dec.RocDec)")
.unwrap()

View file

@ -5,7 +5,7 @@ use crate::llvm::build::{
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, basic_type_from_layout_1};
use crate::llvm::convert::basic_type_from_layout;
use bumpalo::collections::Vec;
use inkwell::basic_block::BasicBlock;
use inkwell::context::Context;
@ -20,6 +20,8 @@ use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout};
use roc_target::TargetInfo;
use super::convert::argument_type_from_union_layout;
/// "Infinite" reference count, for static values
/// Ref counts are encoded as negative numbers where isize::MIN represents 1
pub const REFCOUNT_MAX: usize = 0_usize;
@ -589,6 +591,12 @@ fn modify_refcount_layout_build_function<'a, 'ctx, 'env>(
modify_refcount_builtin(env, layout_ids, mode, when_recursive, layout, builtin)
}
Boxed(inner) => {
let function = modify_refcount_boxed(env, layout_ids, mode, inner);
Some(function)
}
Union(variant) => {
use UnionLayout::*;
@ -891,6 +899,76 @@ fn modify_refcount_str_help<'a, 'ctx, 'env>(
builder.build_return(None);
}
fn modify_refcount_boxed<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
mode: Mode,
inner_layout: &'a Layout<'a>,
) -> FunctionValue<'ctx> {
let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap();
let boxed_layout = env.arena.alloc(Layout::Boxed(inner_layout));
let (_, fn_name) = function_name_from_mode(
layout_ids,
&env.interns,
"increment_boxed",
"decrement_boxed",
boxed_layout,
mode,
);
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, boxed_layout);
let function_value = build_header(env, basic_type, mode, &fn_name);
modify_refcount_box_help(env, mode, inner_layout, function_value);
function_value
}
};
env.builder.position_at_end(block);
env.builder
.set_current_debug_location(env.context, di_location);
function
}
fn modify_refcount_box_help<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
mode: Mode,
inner_layout: &Layout<'a>,
fn_val: FunctionValue<'ctx>,
) {
let builder = env.builder;
let ctx = env.context;
// Add a basic block for the entry point
let entry = ctx.append_basic_block(fn_val, "entry");
builder.position_at_end(entry);
debug_info_init!(env, fn_val);
// Add args to scope
let arg_symbol = Symbol::ARG_1;
let arg_val = fn_val.get_param_iter().next().unwrap();
arg_val.set_name(arg_symbol.as_str(&env.interns));
let boxed = arg_val.into_pointer_value();
let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, boxed);
let call_mode = mode_to_call_mode(fn_val, mode);
let boxed_layout = Layout::Boxed(inner_layout);
refcount_ptr.modify(call_mode, &boxed_layout, env);
// this function returns void
builder.build_return(None);
}
#[allow(clippy::too_many_arguments)]
fn modify_refcount_dict<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
@ -1619,7 +1697,8 @@ fn modify_refcount_union<'a, 'ctx, 'env>(
when_recursive: &WhenRecursive<'a>,
fields: &'a [&'a [Layout<'a>]],
) -> FunctionValue<'ctx> {
let layout = Layout::Union(UnionLayout::NonRecursive(fields));
let union_layout = UnionLayout::NonRecursive(fields);
let layout = Layout::Union(union_layout);
let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap();
@ -1636,7 +1715,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_1(env, &layout);
let basic_type = argument_type_from_union_layout(env, &union_layout);
let function_value = build_header(env, basic_type, mode, &fn_name);
modify_refcount_union_help(
@ -1700,9 +1779,9 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
.build_load(tag_id_ptr, "load_tag_id")
.into_int_value();
let tag_id_u8 = env
.builder
.build_int_cast(tag_id, env.context.i8_type(), "tag_id_u8");
let tag_id_u8 =
env.builder
.build_int_cast_sign_flag(tag_id, env.context.i8_type(), false, "tag_id_u8");
// next, make a jump table for all possible values of the tag_id
let mut cases = Vec::with_capacity_in(tags.len(), env.arena);