Merge remote-tracking branch 'origin/trunk' into update_zig_09

This commit is contained in:
Folkert 2022-04-06 14:48:04 +02:00
commit a69bf971f0
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
583 changed files with 35930 additions and 13981 deletions

View file

@ -1,7 +1,8 @@
/// Helpers for interacting with the zig that generates bitcode
use crate::debug_info_init;
use crate::llvm::build::{
load_roc_value, struct_from_fields, Env, C_CALL_CONV, FAST_CALL_CONV, TAG_DATA_INDEX,
complex_bitcast_check_size, load_roc_value, struct_from_fields, to_cc_return, CCReturn, Env,
C_CALL_CONV, FAST_CALL_CONV, TAG_DATA_INDEX,
};
use crate::llvm::convert::basic_type_from_layout;
use crate::llvm::refcounting::{
@ -11,9 +12,14 @@ use inkwell::attributes::{Attribute, AttributeLoc};
use inkwell::types::{BasicType, BasicTypeEnum};
use inkwell::values::{BasicValue, BasicValueEnum, CallSiteValue, FunctionValue, InstructionValue};
use inkwell::AddressSpace;
use roc_error_macros::internal_error;
use roc_module::symbol::Symbol;
use roc_mono::layout::{LambdaSet, Layout, LayoutIds, UnionLayout};
use super::build::create_entry_block_alloca;
use std::convert::TryInto;
pub fn call_bitcode_fn<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
args: &[BasicValueEnum<'ctx>],
@ -35,15 +41,24 @@ pub fn call_list_bitcode_fn<'a, 'ctx, 'env>(
args: &[BasicValueEnum<'ctx>],
fn_name: &str,
) -> BasicValueEnum<'ctx> {
call_bitcode_fn_help(env, args, fn_name)
.try_as_basic_value()
.left()
.unwrap_or_else(|| {
panic!(
"LLVM error: Did not get return value from bitcode function {:?}",
fn_name
)
})
use bumpalo::collections::Vec;
let parent = env
.builder
.get_insert_block()
.and_then(|b| b.get_parent())
.unwrap();
let list_type = super::convert::zig_list_type(env);
let result = create_entry_block_alloca(env, parent, list_type.into(), "list_alloca");
let mut arguments: Vec<BasicValueEnum> = Vec::with_capacity_in(args.len() + 1, env.arena);
arguments.push(result.into());
arguments.extend(args);
call_void_bitcode_fn(env, &arguments, fn_name);
env.builder.build_load(result, "load_list")
}
pub fn call_str_bitcode_fn<'a, 'ctx, 'env>(
@ -51,15 +66,35 @@ pub fn call_str_bitcode_fn<'a, 'ctx, 'env>(
args: &[BasicValueEnum<'ctx>],
fn_name: &str,
) -> BasicValueEnum<'ctx> {
call_bitcode_fn_help(env, args, fn_name)
.try_as_basic_value()
.left()
.unwrap_or_else(|| {
panic!(
"LLVM error: Did not get return value from bitcode function {:?}",
fn_name
)
})
use bumpalo::collections::Vec;
let parent = env
.builder
.get_insert_block()
.and_then(|b| b.get_parent())
.unwrap();
let str_type = super::convert::zig_str_type(env);
match env.target_info.ptr_width() {
roc_target::PtrWidth::Bytes4 => {
// 3 machine words actually fit into 2 registers
call_bitcode_fn(env, args, fn_name)
}
roc_target::PtrWidth::Bytes8 => {
let result =
create_entry_block_alloca(env, parent, str_type.into(), "return_str_alloca");
let mut arguments: Vec<BasicValueEnum> =
Vec::with_capacity_in(args.len() + 1, env.arena);
arguments.push(result.into());
arguments.extend(args);
call_void_bitcode_fn(env, &arguments, fn_name);
result.into()
}
}
}
pub fn call_void_bitcode_fn<'a, 'ctx, 'env>(
@ -88,10 +123,92 @@ fn call_bitcode_fn_help<'a, 'ctx, 'env>(
let call = env.builder.build_call(fn_val, &arguments, "call_builtin");
// Attributes that we propagate from the zig builtin parameters, to the arguments we give to the
// call. It is undefined behavior in LLVM to have an attribute on a parameter, and then call
// the function where that parameter is not present. For many (e.g. nonnull) it can be inferred
// but e.g. byval and sret cannot and must be explicitly provided.
let propagate = [
Attribute::get_named_enum_kind_id("nonnull"),
Attribute::get_named_enum_kind_id("nocapture"),
Attribute::get_named_enum_kind_id("readonly"),
Attribute::get_named_enum_kind_id("noalias"),
Attribute::get_named_enum_kind_id("sret"),
Attribute::get_named_enum_kind_id("byval"),
];
for i in 0..fn_val.count_params() {
let attributes = fn_val.attributes(AttributeLoc::Param(i));
for attribute in attributes {
let kind_id = attribute.get_enum_kind_id();
if propagate.contains(&kind_id) {
call.add_attribute(AttributeLoc::Param(i), attribute)
}
}
}
call.set_call_convention(fn_val.get_call_conventions());
call
}
pub fn call_bitcode_fn_fixing_for_convention<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
args: &[BasicValueEnum<'ctx>],
return_layout: &Layout<'_>,
fn_name: &str,
) -> BasicValueEnum<'ctx> {
// Calling zig bitcode, so we must follow C calling conventions.
let cc_return = to_cc_return(env, return_layout);
match cc_return {
CCReturn::Return => {
// We'll get a return value
call_bitcode_fn(env, args, fn_name)
}
CCReturn::ByPointer => {
// We need to pass the return value by pointer.
let roc_return_type = basic_type_from_layout(env, return_layout);
let cc_ptr_return_type = env
.module
.get_function(fn_name)
.unwrap()
.get_type()
.get_param_types()[0]
.into_pointer_type();
let cc_return_type: BasicTypeEnum<'ctx> = cc_ptr_return_type
.get_element_type()
.try_into()
.expect("Zig bitcode return type is not a basic type!");
let cc_return_value_ptr = env.builder.build_alloca(cc_return_type, "return_value");
let fixed_args: Vec<BasicValueEnum<'ctx>> = [cc_return_value_ptr.into()]
.iter()
.chain(args)
.copied()
.collect();
call_void_bitcode_fn(env, &fixed_args, fn_name);
let cc_return_value = env.builder.build_load(cc_return_value_ptr, "read_result");
if roc_return_type.size_of() == cc_return_type.size_of() {
cc_return_value
} else {
// We need to convert the C-callconv return type, which may be larger than the Roc
// return type, into the Roc return type.
complex_bitcast_check_size(
env,
cc_return_value,
roc_return_type,
"c_value_to_roc_value",
)
}
}
CCReturn::Void => {
internal_error!("Tried to call valued bitcode function, but it has no return type")
}
}
}
const ARGUMENT_SYMBOLS: [Symbol; 8] = [
Symbol::ARG_1,
Symbol::ARG_2,
@ -179,8 +296,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(
@ -292,7 +413,7 @@ 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() {
let argument = if layout.is_passed_by_reference(env.target_info) {
env.builder
.build_pointer_cast(
argument_ptr.into_pointer_value(),
@ -313,7 +434,9 @@ fn build_transform_caller_help<'a, 'ctx, 'env>(
}
match closure_data_layout.runtime_representation() {
Layout::Struct(&[]) => {
Layout::Struct {
field_layouts: &[], ..
} => {
// nothing to add
}
other => {
@ -444,7 +567,7 @@ 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() {
let value = if layout.is_passed_by_reference(env.target_info) {
env.builder
.build_pointer_cast(value_ptr, value_type, "cast_ptr_to_tag_build_rc_wrapper")
.into()
@ -633,7 +756,9 @@ pub fn build_compare_wrapper<'a, 'ctx, 'env>(
let default = [value1.into(), value2.into()];
let arguments_cast = match closure_data_layout.runtime_representation() {
Layout::Struct(&[]) => {
Layout::Struct {
field_layouts: &[], ..
} => {
// nothing to add
&default
}

File diff suppressed because it is too large Load diff

View file

@ -7,7 +7,7 @@ use crate::llvm::build::{
Scope,
};
use crate::llvm::build_list::{layout_width, pass_as_opaque};
use crate::llvm::convert::{basic_type_from_layout, zig_dict_type, zig_list_type};
use crate::llvm::convert::{basic_type_from_layout, zig_dict_type};
use crate::llvm::refcounting::Mode;
use inkwell::attributes::{Attribute, AttributeLoc};
use inkwell::context::Context;
@ -19,6 +19,10 @@ use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout, LayoutIds};
use roc_target::TargetInfo;
use super::bitcode::call_list_bitcode_fn;
use super::build::store_roc_value;
use super::build_list::list_to_c_abi;
#[repr(transparent)]
struct Alignment(u8);
@ -65,7 +69,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),
@ -98,11 +107,14 @@ pub fn dict_insert<'a, 'ctx, 'env>(
let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
let key_ptr = builder.build_alloca(key.get_type(), "key_ptr");
let value_ptr = builder.build_alloca(value.get_type(), "value_ptr");
let key_type = basic_type_from_layout(env, key_layout);
let value_type = basic_type_from_layout(env, value_layout);
env.builder.build_store(key_ptr, key);
env.builder.build_store(value_ptr, value);
let key_ptr = builder.build_alloca(key_type, "key_ptr");
let value_ptr = builder.build_alloca(value_type, "value_ptr");
store_roc_value(env, *key_layout, key_ptr, key);
store_roc_value(env, *value_layout, value_ptr, value);
let key_width = env
.ptr_int()
@ -409,8 +421,6 @@ pub fn dict_keys<'a, 'ctx, 'env>(
key_layout: &Layout<'a>,
value_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
let builder = env.builder;
let key_width = env
.ptr_int()
.const_int(key_layout.stack_size(env.target_info) as u64, false);
@ -424,9 +434,7 @@ pub fn dict_keys<'a, 'ctx, 'env>(
let inc_key_fn = build_inc_wrapper(env, layout_ids, key_layout);
let list_ptr = builder.build_alloca(zig_list_type(env), "list_ptr");
call_void_bitcode_fn(
call_list_bitcode_fn(
env,
&[
pass_dict_c_abi(env, dict),
@ -434,21 +442,9 @@ pub fn dict_keys<'a, 'ctx, 'env>(
key_width.into(),
value_width.into(),
inc_key_fn.as_global_value().as_pointer_value().into(),
list_ptr.into(),
],
bitcode::DICT_KEYS,
);
let list_ptr = env
.builder
.build_bitcast(
list_ptr,
super::convert::zig_list_type(env).ptr_type(AddressSpace::Generic),
"to_roc_list",
)
.into_pointer_value();
env.builder.build_load(list_ptr, "load_keys_list")
)
}
fn pass_dict_c_abi<'a, 'ctx, 'env>(
@ -665,10 +661,6 @@ pub fn dict_values<'a, 'ctx, 'env>(
key_layout: &Layout<'a>,
value_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
let builder = env.builder;
let zig_list_type = super::convert::zig_list_type(env);
let key_width = env
.ptr_int()
.const_int(key_layout.stack_size(env.target_info) as u64, false);
@ -682,9 +674,7 @@ pub fn dict_values<'a, 'ctx, 'env>(
let inc_value_fn = build_inc_wrapper(env, layout_ids, value_layout);
let list_ptr = builder.build_alloca(zig_list_type, "list_ptr");
call_void_bitcode_fn(
call_list_bitcode_fn(
env,
&[
pass_dict_c_abi(env, dict),
@ -692,21 +682,9 @@ pub fn dict_values<'a, 'ctx, 'env>(
key_width.into(),
value_width.into(),
inc_value_fn.as_global_value().as_pointer_value().into(),
list_ptr.into(),
],
bitcode::DICT_VALUES,
);
let list_ptr = env
.builder
.build_bitcast(
list_ptr,
super::convert::zig_list_type(env).ptr_type(AddressSpace::Generic),
"to_roc_list",
)
.into_pointer_value();
env.builder.build_load(list_ptr, "load_keys_list")
)
}
#[allow(clippy::too_many_arguments)]
@ -718,15 +696,6 @@ pub fn set_from_list<'a, 'ctx, 'env>(
) -> BasicValueEnum<'ctx> {
let builder = env.builder;
let list_alloca = builder.build_alloca(list.get_type(), "list_alloca");
let list_ptr = env.builder.build_bitcast(
list_alloca,
env.str_list_c_abi().ptr_type(AddressSpace::Generic),
"to_zig_list",
);
env.builder.build_store(list_alloca, list);
let key_width = env
.ptr_int()
.const_int(key_layout.stack_size(env.target_info) as u64, false);
@ -735,8 +704,7 @@ pub fn set_from_list<'a, 'ctx, 'env>(
let result_alloca = builder.build_alloca(zig_dict_type(env), "result_alloca");
let alignment =
Alignment::from_key_value_layout(key_layout, &Layout::Struct(&[]), env.target_info);
let alignment = Alignment::from_key_value_layout(key_layout, &Layout::UNIT, env.target_info);
let alignment_iv = alignment.as_int_value(env.context);
let hash_fn = build_hash_wrapper(env, layout_ids, key_layout);
@ -747,8 +715,7 @@ pub fn set_from_list<'a, 'ctx, 'env>(
call_void_bitcode_fn(
env,
&[
env.builder
.build_load(list_ptr.into_pointer_value(), "as_i128"),
list_to_c_abi(env, list).into(),
alignment_iv.into(),
key_width.into(),
value_width.into(),
@ -810,7 +777,7 @@ fn build_hash_wrapper<'a, 'ctx, 'env>(
let value_cast = env
.builder
.build_bitcast(value_ptr, value_type, "load_opaque")
.build_bitcast(value_ptr, value_type, "cast_to_known_type")
.into_pointer_value();
let val_arg = load_roc_value(env, *layout, value_cast, "load_opaque");

View file

@ -3,8 +3,7 @@ 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::{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 +12,9 @@ use roc_builtins::bitcode;
use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout};
use super::build::use_roc_value;
use super::convert::argument_type_from_union_layout;
#[derive(Clone, Debug)]
enum WhenRecursive<'a> {
Unreachable,
@ -50,10 +52,10 @@ fn build_hash_layout<'a, 'ctx, 'env>(
hash_builtin(env, layout_ids, seed, val, layout, builtin, when_recursive)
}
Layout::Struct(fields) => build_hash_struct(
Layout::Struct { field_layouts, .. } => build_hash_struct(
env,
layout_ids,
fields,
field_layouts,
when_recursive,
seed,
val.into_struct_value(),
@ -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())
}
},
}
@ -129,12 +127,7 @@ fn hash_builtin<'a, 'ctx, 'env>(
}
Builtin::Str => {
// let zig deal with big vs small string
call_bitcode_fn(
env,
&[seed.into(), build_str::str_to_c_abi(env, val).into()],
bitcode::DICT_HASH_STR,
)
.into_int_value()
call_bitcode_fn(env, &[seed.into(), val], bitcode::DICT_HASH_STR).into_int_value()
}
Builtin::Dict(_, _) => {
@ -166,7 +159,7 @@ fn build_hash_struct<'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 struct_layout = Layout::Struct(field_layouts);
let struct_layout = Layout::struct_no_name_order(field_layouts);
let symbol = Symbol::GENERIC_HASH;
let fn_name = layout_ids
@ -248,7 +241,7 @@ fn hash_struct<'a, 'ctx, 'env>(
) -> IntValue<'ctx> {
let ptr_bytes = env.target_info;
let layout = Layout::Struct(field_layouts);
let layout = Layout::struct_no_name_order(field_layouts);
// Optimization: if the bit representation of equal values is the same
// just hash the bits. Caveat here is tags: e.g. `Nothing` in `Just a`
@ -261,9 +254,11 @@ fn hash_struct<'a, 'ctx, 'env>(
for (index, field_layout) in field_layouts.iter().enumerate() {
let field = env
.builder
.build_extract_value(value, index as u32, "eq_field")
.build_extract_value(value, index as u32, "hash_field")
.unwrap();
let field = use_roc_value(env, *field_layout, field, "store_field_for_hashing");
if let Layout::RecursivePointer = field_layout {
match &when_recursive {
WhenRecursive::Unreachable => {
@ -308,7 +303,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 +312,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 +320,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 +799,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
@ -818,7 +812,7 @@ fn hash_ptr_to_struct<'a, 'ctx, 'env>(
.build_struct_gep(wrapper_ptr, TAG_DATA_INDEX, "get_tag_data")
.unwrap();
let struct_layout = Layout::Struct(field_layouts);
let struct_layout = Layout::struct_no_name_order(field_layouts);
let struct_type = basic_type_from_layout(env, &struct_layout);
let struct_ptr = env
.builder

View file

@ -4,7 +4,7 @@ use crate::llvm::bitcode::{
call_bitcode_fn, call_list_bitcode_fn, call_void_bitcode_fn,
};
use crate::llvm::build::{
allocate_with_refcount_help, cast_basic_basic, complex_bitcast, Env, RocFunctionCall,
allocate_with_refcount_help, cast_basic_basic, Env, RocFunctionCall, Scope,
};
use crate::llvm::convert::basic_type_from_layout;
use crate::llvm::refcounting::increment_refcount_layout;
@ -15,9 +15,48 @@ use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, Str
use inkwell::{AddressSpace, IntPredicate};
use morphic_lib::UpdateMode;
use roc_builtins::bitcode::{self, IntWidth};
use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout, LayoutIds};
use super::build::{load_roc_value, store_roc_value};
use super::build::{create_entry_block_alloca, load_roc_value, load_symbol, store_roc_value};
pub fn list_symbol_to_c_abi<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
scope: &Scope<'a, 'ctx>,
symbol: Symbol,
) -> PointerValue<'ctx> {
let parent = env
.builder
.get_insert_block()
.and_then(|b| b.get_parent())
.unwrap();
let list_type = super::convert::zig_list_type(env);
let list_alloca = create_entry_block_alloca(env, parent, list_type.into(), "list_alloca");
let list = load_symbol(scope, &symbol);
env.builder.build_store(list_alloca, list);
list_alloca
}
pub fn list_to_c_abi<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
list: BasicValueEnum<'ctx>,
) -> PointerValue<'ctx> {
let parent = env
.builder
.get_insert_block()
.and_then(|b| b.get_parent())
.unwrap();
let list_type = super::convert::zig_list_type(env);
let list_alloca = create_entry_block_alloca(env, parent, list_type.into(), "list_alloca");
env.builder.build_store(list_alloca, list);
list_alloca
}
pub fn pass_update_mode<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
@ -43,19 +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",
)
}
fn pass_list_cc<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
list: BasicValueEnum<'ctx>,
) -> BasicValueEnum<'ctx> {
complex_bitcast(
env.builder,
list,
env.str_list_c_abi().into(),
"to_str_list_int",
"pass_element_as_opaque",
)
}
@ -75,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",
)
}
@ -128,7 +155,7 @@ pub fn list_join<'a, 'ctx, 'env>(
call_list_bitcode_fn(
env,
&[
pass_list_cc(env, outer_list),
list_to_c_abi(env, outer_list).into(),
env.alignment_intvalue(element_layout),
layout_width(env, element_layout),
],
@ -146,7 +173,7 @@ pub fn list_reverse<'a, 'ctx, 'env>(
call_list_bitcode_fn(
env,
&[
pass_list_cc(env, list),
list_to_c_abi(env, list).into(),
env.alignment_intvalue(element_layout),
layout_width(env, element_layout),
pass_update_mode(env, update_mode),
@ -193,7 +220,7 @@ pub fn list_append<'a, 'ctx, 'env>(
call_list_bitcode_fn(
env,
&[
pass_list_cc(env, original_wrapper.into()),
list_to_c_abi(env, original_wrapper.into()).into(),
env.alignment_intvalue(element_layout),
pass_element_as_opaque(env, element, *element_layout),
layout_width(env, element_layout),
@ -213,7 +240,7 @@ pub fn list_prepend<'a, 'ctx, 'env>(
call_list_bitcode_fn(
env,
&[
pass_list_cc(env, original_wrapper.into()),
list_to_c_abi(env, original_wrapper.into()).into(),
env.alignment_intvalue(element_layout),
pass_element_as_opaque(env, element, *element_layout),
layout_width(env, element_layout),
@ -234,7 +261,7 @@ pub fn list_swap<'a, 'ctx, 'env>(
call_list_bitcode_fn(
env,
&[
pass_list_cc(env, original_wrapper.into()),
list_to_c_abi(env, original_wrapper.into()).into(),
env.alignment_intvalue(element_layout),
layout_width(env, element_layout),
index_1.into(),
@ -258,7 +285,7 @@ pub fn list_sublist<'a, 'ctx, 'env>(
call_list_bitcode_fn(
env,
&[
pass_list_cc(env, original_wrapper.into()),
list_to_c_abi(env, original_wrapper.into()).into(),
env.alignment_intvalue(element_layout),
layout_width(env, element_layout),
start.into(),
@ -281,7 +308,7 @@ pub fn list_drop_at<'a, 'ctx, 'env>(
call_list_bitcode_fn(
env,
&[
pass_list_cc(env, original_wrapper.into()),
list_to_c_abi(env, original_wrapper.into()).into(),
env.alignment_intvalue(element_layout),
layout_width(env, element_layout),
count.into(),
@ -291,52 +318,70 @@ pub fn list_drop_at<'a, 'ctx, 'env>(
)
}
/// List.set : List elem, Nat, elem -> List elem
pub fn list_set<'a, 'ctx, 'env>(
/// List.replace_unsafe : List elem, Nat, elem -> { list: List elem, value: elem }
pub fn list_replace_unsafe<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
_layout_ids: &mut LayoutIds<'a>,
list: BasicValueEnum<'ctx>,
index: IntValue<'ctx>,
element: BasicValueEnum<'ctx>,
element_layout: &Layout<'a>,
update_mode: UpdateMode,
) -> BasicValueEnum<'ctx> {
let dec_element_fn = build_dec_wrapper(env, layout_ids, element_layout);
let element_type = basic_type_from_layout(env, element_layout);
let element_ptr = env
.builder
.build_alloca(element_type, "output_element_as_opaque");
let (length, bytes) = load_list(
env.builder,
list.into_struct_value(),
env.context.i8_type().ptr_type(AddressSpace::Generic),
);
let new_bytes = match update_mode {
UpdateMode::InPlace => call_bitcode_fn(
// Assume the bounds have already been checked earlier
// (e.g. by List.replace or List.set, which wrap List.#replaceUnsafe)
let new_list = match update_mode {
UpdateMode::InPlace => call_list_bitcode_fn(
env,
&[
bytes.into(),
list_to_c_abi(env, list).into(),
index.into(),
pass_element_as_opaque(env, element, *element_layout),
layout_width(env, element_layout),
dec_element_fn.as_global_value().as_pointer_value().into(),
pass_as_opaque(env, element_ptr),
],
bitcode::LIST_SET_IN_PLACE,
bitcode::LIST_REPLACE_IN_PLACE,
),
UpdateMode::Immutable => call_bitcode_fn(
UpdateMode::Immutable => call_list_bitcode_fn(
env,
&[
bytes.into(),
length.into(),
list_to_c_abi(env, list).into(),
env.alignment_intvalue(element_layout),
index.into(),
pass_element_as_opaque(env, element, *element_layout),
layout_width(env, element_layout),
dec_element_fn.as_global_value().as_pointer_value().into(),
pass_as_opaque(env, element_ptr),
],
bitcode::LIST_SET,
bitcode::LIST_REPLACE,
),
};
store_list(env, new_bytes.into_pointer_value(), length)
// Load the element and returned list into a struct.
let old_element = env.builder.build_load(element_ptr, "load_element");
let result = env
.context
.struct_type(
&[super::convert::zig_list_type(env).into(), element_type],
false,
)
.const_zero();
let result = env
.builder
.build_insert_value(result, new_list, 0, "insert_list")
.unwrap();
env.builder
.build_insert_value(result, old_element, 1, "insert_value")
.unwrap()
.into_struct_value()
.into()
}
fn bounds_check_comparison<'ctx>(
@ -389,17 +434,26 @@ 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(env.target_info) {
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 => {
call_void_bitcode_fn(
env,
&[
pass_list_cc(env, list),
list_to_c_abi(env, list).into(),
roc_function_call.caller.into(),
pass_as_opaque(env, roc_function_call.data),
roc_function_call.inc_n_data.into(),
@ -430,7 +484,7 @@ pub fn list_walk_generic<'a, 'ctx, 'env>(
call_void_bitcode_fn(
env,
&[
pass_list_cc(env, list),
list_to_c_abi(env, list).into(),
roc_function_call.caller.into(),
pass_as_opaque(env, roc_function_call.data),
roc_function_call.inc_n_data.into(),
@ -449,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(env.target_info) {
result_ptr.into()
} else {
env.builder.build_load(result_ptr, "load_result")
}
}
/// List.range : Int a, Int a -> List (Int a)
@ -473,7 +531,7 @@ pub fn list_range<'a, 'ctx, 'env>(
.const_int(int_width as u64, false)
.into();
call_bitcode_fn(
call_list_bitcode_fn(
env,
&[
int_width,
@ -500,7 +558,7 @@ pub fn list_contains<'a, 'ctx, 'env>(
call_bitcode_fn(
env,
&[
pass_list_cc(env, list),
list_to_c_abi(env, list).into(),
pass_element_as_opaque(env, element, *element_layout),
layout_width(env, element_layout),
eq_fn,
@ -523,7 +581,7 @@ pub fn list_keep_if<'a, 'ctx, 'env>(
call_list_bitcode_fn(
env,
&[
pass_list_cc(env, list),
list_to_c_abi(env, list).into(),
roc_function_call.caller.into(),
pass_as_opaque(env, roc_function_call.data),
roc_function_call.inc_n_data.into(),
@ -574,10 +632,10 @@ pub fn list_keep_oks<'a, 'ctx, 'env>(
_ => unreachable!(),
};
call_bitcode_fn(
call_list_bitcode_fn(
env,
&[
pass_list_cc(env, list),
list_to_c_abi(env, list).into(),
roc_function_call.caller.into(),
pass_as_opaque(env, roc_function_call.data),
roc_function_call.inc_n_data.into(),
@ -622,10 +680,10 @@ pub fn list_keep_errs<'a, 'ctx, 'env>(
_ => unreachable!(),
};
call_bitcode_fn(
call_list_bitcode_fn(
env,
&[
pass_list_cc(env, list),
list_to_c_abi(env, list).into(),
roc_function_call.caller.into(),
pass_as_opaque(env, roc_function_call.data),
roc_function_call.inc_n_data.into(),
@ -652,7 +710,7 @@ pub fn list_sort_with<'a, 'ctx, 'env>(
call_list_bitcode_fn(
env,
&[
pass_list_cc(env, list),
list_to_c_abi(env, list).into(),
compare_wrapper.into(),
pass_as_opaque(env, roc_function_call.data),
roc_function_call.inc_n_data.into(),
@ -675,7 +733,7 @@ pub fn list_map_with_index<'a, 'ctx, 'env>(
call_list_bitcode_fn(
env,
&[
pass_list_cc(env, list),
list_to_c_abi(env, list).into(),
roc_function_call.caller.into(),
pass_as_opaque(env, roc_function_call.data),
roc_function_call.inc_n_data.into(),
@ -699,7 +757,7 @@ pub fn list_map<'a, 'ctx, 'env>(
call_list_bitcode_fn(
env,
&[
pass_list_cc(env, list),
list_to_c_abi(env, list).into(),
roc_function_call.caller.into(),
pass_as_opaque(env, roc_function_call.data),
roc_function_call.inc_n_data.into(),
@ -725,11 +783,11 @@ pub fn list_map2<'a, 'ctx, 'env>(
let dec_a = build_dec_wrapper(env, layout_ids, element1_layout);
let dec_b = build_dec_wrapper(env, layout_ids, element2_layout);
call_bitcode_fn(
call_list_bitcode_fn(
env,
&[
pass_list_cc(env, list1),
pass_list_cc(env, list2),
list_to_c_abi(env, list1).into(),
list_to_c_abi(env, list2).into(),
roc_function_call.caller.into(),
pass_as_opaque(env, roc_function_call.data),
roc_function_call.inc_n_data.into(),
@ -764,9 +822,9 @@ pub fn list_map3<'a, 'ctx, 'env>(
call_list_bitcode_fn(
env,
&[
pass_list_cc(env, list1),
pass_list_cc(env, list2),
pass_list_cc(env, list3),
list_to_c_abi(env, list1).into(),
list_to_c_abi(env, list2).into(),
list_to_c_abi(env, list3).into(),
roc_function_call.caller.into(),
pass_as_opaque(env, roc_function_call.data),
roc_function_call.inc_n_data.into(),
@ -806,10 +864,10 @@ pub fn list_map4<'a, 'ctx, 'env>(
call_list_bitcode_fn(
env,
&[
pass_list_cc(env, list1),
pass_list_cc(env, list2),
pass_list_cc(env, list3),
pass_list_cc(env, list4),
list_to_c_abi(env, list1).into(),
list_to_c_abi(env, list2).into(),
list_to_c_abi(env, list3).into(),
list_to_c_abi(env, list4).into(),
roc_function_call.caller.into(),
pass_as_opaque(env, roc_function_call.data),
roc_function_call.inc_n_data.into(),
@ -839,8 +897,8 @@ pub fn list_concat<'a, 'ctx, 'env>(
call_list_bitcode_fn(
env,
&[
pass_list_cc(env, first_list),
pass_list_cc(env, second_list),
list_to_c_abi(env, first_list).into(),
list_to_c_abi(env, second_list).into(),
env.alignment_intvalue(element_layout),
layout_width(env, element_layout),
],
@ -858,7 +916,7 @@ pub fn list_any<'a, 'ctx, 'env>(
call_bitcode_fn(
env,
&[
pass_list_cc(env, list),
list_to_c_abi(env, list).into(),
roc_function_call.caller.into(),
pass_as_opaque(env, roc_function_call.data),
roc_function_call.inc_n_data.into(),
@ -879,7 +937,7 @@ pub fn list_all<'a, 'ctx, 'env>(
call_bitcode_fn(
env,
&[
pass_list_cc(env, list),
list_to_c_abi(env, list).into(),
roc_function_call.caller.into(),
pass_as_opaque(env, roc_function_call.data),
roc_function_call.inc_n_data.into(),
@ -905,7 +963,7 @@ pub fn list_find_unsafe<'a, 'ctx, 'env>(
let result = call_bitcode_fn(
env,
&[
pass_list_cc(env, list),
list_to_c_abi(env, list).into(),
roc_function_call.caller.into(),
pass_as_opaque(env, roc_function_call.data),
roc_function_call.inc_n_data.into(),
@ -1227,7 +1285,6 @@ pub fn allocate_list<'a, 'ctx, 'env>(
number_of_elements: IntValue<'ctx>,
) -> PointerValue<'ctx> {
let builder = env.builder;
let ctx = env.context;
let len_type = env.ptr_int();
let elem_bytes = elem_layout.stack_size(env.target_info) as u64;
@ -1235,13 +1292,9 @@ pub fn allocate_list<'a, 'ctx, 'env>(
let number_of_data_bytes =
builder.build_int_mul(bytes_per_element, number_of_elements, "data_length");
// the refcount of a new list is initially 1
// we assume that the list is indeed used (dead variables are eliminated)
let rc1 = crate::llvm::refcounting::refcount_1(ctx, env.target_info);
let basic_type = basic_type_from_layout(env, elem_layout);
let alignment_bytes = elem_layout.alignment_bytes(env.target_info);
allocate_with_refcount_help(env, basic_type, alignment_bytes, number_of_data_bytes, rc1)
allocate_with_refcount_help(env, basic_type, alignment_bytes, number_of_data_bytes)
}
pub fn store_list<'a, 'ctx, 'env>(
@ -1253,10 +1306,8 @@ pub fn store_list<'a, 'ctx, 'env>(
let struct_type = super::convert::zig_list_type(env);
let mut struct_val;
// Store the pointer
struct_val = builder
let mut struct_val = builder
.build_insert_value(
struct_type.get_undef(),
pass_as_opaque(env, pointer_to_first_element),

View file

@ -1,10 +1,8 @@
use crate::llvm::bitcode::{
call_bitcode_fn, call_list_bitcode_fn, call_str_bitcode_fn, call_void_bitcode_fn,
};
use crate::llvm::bitcode::{call_bitcode_fn, call_str_bitcode_fn, call_void_bitcode_fn};
use crate::llvm::build::{complex_bitcast, Env, Scope};
use crate::llvm::build_list::{allocate_list, pass_update_mode, store_list};
use inkwell::builder::Builder;
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
use inkwell::values::{BasicValueEnum, IntValue, PointerValue, StructValue};
use inkwell::AddressSpace;
use morphic_lib::UpdateMode;
use roc_builtins::bitcode::{self, IntWidth};
@ -12,22 +10,11 @@ use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout};
use roc_target::PtrWidth;
use super::build::load_symbol;
use super::build::{create_entry_block_alloca, load_symbol};
use super::build_list::list_symbol_to_c_abi;
pub static CHAR_LAYOUT: Layout = Layout::u8();
/// Str.repeat : Str, Nat -> Str
pub fn str_repeat<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
scope: &Scope<'a, 'ctx>,
str_symbol: Symbol,
count_symbol: Symbol,
) -> BasicValueEnum<'ctx> {
let str_c_abi = str_symbol_to_c_abi(env, scope, str_symbol);
let count = load_symbol(scope, &count_symbol);
call_str_bitcode_fn(env, &[str_c_abi.into(), count], bitcode::STR_REPEAT)
}
/// Str.split : Str, Str -> List Str
pub fn str_split<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
@ -37,15 +24,11 @@ pub fn str_split<'a, 'ctx, 'env>(
) -> BasicValueEnum<'ctx> {
let builder = env.builder;
let str_c_abi = str_symbol_to_c_abi(env, scope, str_symbol);
let delim_c_abi = str_symbol_to_c_abi(env, scope, delimiter_symbol);
let string = load_symbol(scope, &str_symbol);
let delimiter = load_symbol(scope, &delimiter_symbol);
let segment_count = call_list_bitcode_fn(
env,
&[str_c_abi.into(), delim_c_abi.into()],
bitcode::STR_COUNT_SEGMENTS,
)
.into_int_value();
let segment_count =
call_bitcode_fn(env, &[string, delimiter], bitcode::STR_COUNT_SEGMENTS).into_int_value();
// a pointer to the elements
let ret_list_ptr = allocate_list(env, &Layout::Builtin(Builtin::Str), segment_count);
@ -62,53 +45,39 @@ pub fn str_split<'a, 'ctx, 'env>(
call_void_bitcode_fn(
env,
&[
ret_list_ptr_zig_rocstr,
str_c_abi.into(),
delim_c_abi.into(),
],
&[ret_list_ptr_zig_rocstr, string, delimiter],
bitcode::STR_STR_SPLIT_IN_PLACE,
);
store_list(env, ret_list_ptr, segment_count)
}
fn str_symbol_to_c_abi<'a, 'ctx, 'env>(
pub fn str_symbol_to_c_abi<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
scope: &Scope<'a, 'ctx>,
symbol: Symbol,
) -> IntValue<'ctx> {
) -> PointerValue<'ctx> {
let string = load_symbol(scope, &symbol);
let target_type = match env.target_info.ptr_width() {
PtrWidth::Bytes8 => env.context.i128_type().into(),
PtrWidth::Bytes4 => env.context.i64_type().into(),
};
complex_bitcast(env.builder, string, target_type, "str_to_c_abi").into_int_value()
str_to_c_abi(env, string)
}
pub fn str_to_c_abi<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
value: BasicValueEnum<'ctx>,
) -> IntValue<'ctx> {
let cell = env.builder.build_alloca(value.get_type(), "cell");
env.builder.build_store(cell, value);
let target_type = match env.target_info.ptr_width() {
PtrWidth::Bytes8 => env.context.i128_type(),
PtrWidth::Bytes4 => env.context.i64_type(),
};
let target_type_ptr = env
) -> PointerValue<'ctx> {
let parent = env
.builder
.build_bitcast(cell, target_type.ptr_type(AddressSpace::Generic), "cast")
.into_pointer_value();
.get_insert_block()
.and_then(|b| b.get_parent())
.unwrap();
env.builder
.build_load(target_type_ptr, "load_as_c_abi")
.into_int_value()
let str_type = super::convert::zig_str_type(env);
let string_alloca = create_entry_block_alloca(env, parent, str_type.into(), "str_alloca");
env.builder.build_store(string_alloca, value);
string_alloca
}
pub fn destructure<'ctx>(
@ -129,155 +98,6 @@ pub fn destructure<'ctx>(
(generic_ptr, length)
}
/// Str.concat : Str, Str -> Str
pub fn str_concat<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
scope: &Scope<'a, 'ctx>,
str1_symbol: Symbol,
str2_symbol: Symbol,
) -> BasicValueEnum<'ctx> {
// swap the arguments; second argument comes before the second in the output string
let str1_c_abi = str_symbol_to_c_abi(env, scope, str1_symbol);
let str2_c_abi = str_symbol_to_c_abi(env, scope, str2_symbol);
call_str_bitcode_fn(
env,
&[str1_c_abi.into(), str2_c_abi.into()],
bitcode::STR_CONCAT,
)
}
/// Str.join : List Str, Str -> Str
pub fn str_join_with<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
scope: &Scope<'a, 'ctx>,
list_symbol: Symbol,
str_symbol: Symbol,
) -> BasicValueEnum<'ctx> {
// dirty hack; pretend a `list` is a `str` that works because
// they have the same stack layout `{ u8*, usize }`
let list_i128 = str_symbol_to_c_abi(env, scope, list_symbol);
let str_i128 = str_symbol_to_c_abi(env, scope, str_symbol);
call_str_bitcode_fn(
env,
&[list_i128.into(), str_i128.into()],
bitcode::STR_JOIN_WITH,
)
}
pub fn str_number_of_bytes<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
scope: &Scope<'a, 'ctx>,
str_symbol: Symbol,
) -> IntValue<'ctx> {
let str_i128 = str_symbol_to_c_abi(env, scope, str_symbol);
// the builtin will always return an u64
let length =
call_bitcode_fn(env, &[str_i128.into()], bitcode::STR_NUMBER_OF_BYTES).into_int_value();
// cast to the appropriate usize of the current build
env.builder
.build_int_cast(length, env.ptr_int(), "len_as_usize")
}
/// Str.startsWith : Str, Str -> Bool
pub fn str_starts_with<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
scope: &Scope<'a, 'ctx>,
str_symbol: Symbol,
prefix_symbol: Symbol,
) -> BasicValueEnum<'ctx> {
let str_i128 = str_symbol_to_c_abi(env, scope, str_symbol);
let prefix_i128 = str_symbol_to_c_abi(env, scope, prefix_symbol);
call_bitcode_fn(
env,
&[str_i128.into(), prefix_i128.into()],
bitcode::STR_STARTS_WITH,
)
}
/// Str.startsWithCodePt : Str, U32 -> Bool
pub fn str_starts_with_code_point<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
scope: &Scope<'a, 'ctx>,
str_symbol: Symbol,
prefix_symbol: Symbol,
) -> BasicValueEnum<'ctx> {
let str_i128 = str_symbol_to_c_abi(env, scope, str_symbol);
let prefix = load_symbol(scope, &prefix_symbol);
call_bitcode_fn(
env,
&[str_i128.into(), prefix],
bitcode::STR_STARTS_WITH_CODE_PT,
)
}
/// Str.endsWith : Str, Str -> Bool
pub fn str_ends_with<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
scope: &Scope<'a, 'ctx>,
str_symbol: Symbol,
prefix_symbol: Symbol,
) -> BasicValueEnum<'ctx> {
let str_i128 = str_symbol_to_c_abi(env, scope, str_symbol);
let prefix_i128 = str_symbol_to_c_abi(env, scope, prefix_symbol);
call_bitcode_fn(
env,
&[str_i128.into(), prefix_i128.into()],
bitcode::STR_ENDS_WITH,
)
}
/// Str.countGraphemes : Str -> Int
pub fn str_count_graphemes<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
scope: &Scope<'a, 'ctx>,
str_symbol: Symbol,
) -> BasicValueEnum<'ctx> {
let str_i128 = str_symbol_to_c_abi(env, scope, str_symbol);
call_bitcode_fn(
env,
&[str_i128.into()],
bitcode::STR_COUNT_GRAPEHEME_CLUSTERS,
)
}
/// Str.trim : Str -> Str
pub fn str_trim<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
scope: &Scope<'a, 'ctx>,
str_symbol: Symbol,
) -> BasicValueEnum<'ctx> {
let str_i128 = str_symbol_to_c_abi(env, scope, str_symbol);
call_str_bitcode_fn(env, &[str_i128.into()], bitcode::STR_TRIM)
}
/// Str.trimLeft : Str -> Str
pub fn str_trim_left<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
scope: &Scope<'a, 'ctx>,
str_symbol: Symbol,
) -> BasicValueEnum<'ctx> {
let str_i128 = str_symbol_to_c_abi(env, scope, str_symbol);
call_str_bitcode_fn(env, &[str_i128.into()], bitcode::STR_TRIM_LEFT)
}
/// Str.trimRight : Str -> Str
pub fn str_trim_right<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
scope: &Scope<'a, 'ctx>,
str_symbol: Symbol,
) -> BasicValueEnum<'ctx> {
let str_i128 = str_symbol_to_c_abi(env, scope, str_symbol);
call_str_bitcode_fn(env, &[str_i128.into()], bitcode::STR_TRIM_RIGHT)
}
/// Str.fromInt : Int -> Str
pub fn str_from_int<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
@ -287,21 +107,6 @@ pub fn str_from_int<'a, 'ctx, 'env>(
call_str_bitcode_fn(env, &[value.into()], &bitcode::STR_FROM_INT[int_width])
}
/// Str.toUtf8 : Str -> List U8
pub fn str_to_utf8<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
original_wrapper: StructValue<'ctx>,
) -> BasicValueEnum<'ctx> {
let string = complex_bitcast(
env.builder,
original_wrapper.into(),
env.str_list_c_abi().into(),
"to_utf8",
);
call_list_bitcode_fn(env, &[string], bitcode::STR_TO_UTF8)
}
fn decode_from_utf8_result<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
pointer: PointerValue<'ctx>,
@ -341,8 +146,8 @@ fn decode_from_utf8_result<'a, 'ctx, 'env>(
/// Str.fromUtf8 : List U8, { count : Nat, start : Nat } -> { a : Bool, b : Str, c : Nat, d : I8 }
pub fn str_from_utf8_range<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
_parent: FunctionValue<'ctx>,
list_wrapper: StructValue<'ctx>,
scope: &Scope<'a, 'ctx>,
list: Symbol,
count_and_start: StructValue<'ctx>,
) -> BasicValueEnum<'ctx> {
let builder = env.builder;
@ -353,16 +158,11 @@ pub fn str_from_utf8_range<'a, 'ctx, 'env>(
call_void_bitcode_fn(
env,
&[
complex_bitcast(
env.builder,
list_wrapper.into(),
env.str_list_c_abi().into(),
"to_i128",
),
list_symbol_to_c_abi(env, scope, list).into(),
complex_bitcast(
env.builder,
count_and_start.into(),
env.str_list_c_abi().into(),
env.twice_ptr_int().into(),
"to_i128",
),
result_ptr.into(),
@ -376,8 +176,8 @@ pub fn str_from_utf8_range<'a, 'ctx, 'env>(
/// Str.fromUtf8 : List U8 -> { a : Bool, b : Str, c : Nat, d : I8 }
pub fn str_from_utf8<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
_parent: FunctionValue<'ctx>,
original_wrapper: StructValue<'ctx>,
scope: &Scope<'a, 'ctx>,
list: Symbol,
update_mode: UpdateMode,
) -> BasicValueEnum<'ctx> {
let builder = env.builder;
@ -388,12 +188,7 @@ pub fn str_from_utf8<'a, 'ctx, 'env>(
call_void_bitcode_fn(
env,
&[
complex_bitcast(
env.builder,
original_wrapper.into(),
env.str_list_c_abi().into(),
"to_i128",
),
list_symbol_to_c_abi(env, scope, list).into(),
pass_update_mode(env, update_mode),
result_ptr.into(),
],
@ -420,12 +215,5 @@ pub fn str_equal<'a, 'ctx, 'env>(
value1: BasicValueEnum<'ctx>,
value2: BasicValueEnum<'ctx>,
) -> BasicValueEnum<'ctx> {
let str1_i128 = str_to_c_abi(env, value1);
let str2_i128 = str_to_c_abi(env, value2);
call_bitcode_fn(
env,
&[str1_i128.into(), str2_i128.into()],
bitcode::STR_EQUAL,
)
call_bitcode_fn(env, &[value1, value2], bitcode::STR_EQUAL)
}

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::{
@ -14,7 +14,8 @@ use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout};
use super::build::load_roc_value;
use super::build::{load_roc_value, use_roc_value};
use super::convert::argument_type_from_union_layout;
#[derive(Clone, Debug)]
enum WhenRecursive<'a> {
@ -161,10 +162,10 @@ fn build_eq<'a, 'ctx, 'env>(
build_eq_builtin(env, layout_ids, lhs_val, rhs_val, builtin, when_recursive)
}
Layout::Struct(fields) => build_struct_eq(
Layout::Struct { field_layouts, .. } => build_struct_eq(
env,
layout_ids,
fields,
field_layouts,
when_recursive,
lhs_val.into_struct_value(),
rhs_val.into_struct_value(),
@ -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(),
@ -330,11 +339,11 @@ fn build_neq<'a, 'ctx, 'env>(
build_neq_builtin(env, layout_ids, lhs_val, rhs_val, builtin, when_recursive)
}
Layout::Struct(fields) => {
Layout::Struct { field_layouts, .. } => {
let is_equal = build_struct_eq(
env,
layout_ids,
fields,
field_layouts,
when_recursive,
lhs_val.into_struct_value(),
rhs_val.into_struct_value(),
@ -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")
}
@ -587,7 +613,7 @@ fn build_struct_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 struct_layout = Layout::Struct(field_layouts);
let struct_layout = Layout::struct_no_name_order(field_layouts);
let symbol = Symbol::GENERIC_EQ;
let fn_name = layout_ids
@ -729,8 +755,8 @@ fn build_struct_eq_help<'a, 'ctx, 'env>(
build_eq(
env,
layout_ids,
field1,
field2,
use_roc_value(env, *field_layout, field1, "field1"),
use_roc_value(env, *field_layout, field2, "field2"),
field_layout,
field_layout,
when_recursive.clone(),
@ -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",
);
@ -1208,7 +1236,7 @@ fn eq_ptr_to_struct<'a, 'ctx, 'env>(
tag1: PointerValue<'ctx>,
tag2: PointerValue<'ctx>,
) -> IntValue<'ctx> {
let struct_layout = Layout::Struct(field_layouts);
let struct_layout = Layout::struct_no_name_order(field_layouts);
let wrapper_type = basic_type_from_layout(env, &struct_layout);
debug_assert!(wrapper_type.is_struct_type());
@ -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,126 +23,75 @@ 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::*;
match layout {
Struct(sorted_fields) => basic_type_from_record(env, sorted_fields),
Struct {
field_layouts: sorted_fields,
..
} => 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(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::*;
@ -160,8 +110,57 @@ 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),
Builtin(_) => {
let base = basic_type_from_layout(env, layout);
if layout.is_passed_by_reference(env.target_info) {
base.ptr_type(AddressSpace::Generic).into()
} else {
base
}
}
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::*;
@ -176,7 +175,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::*;
@ -261,33 +260,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,10 +5,9 @@ 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;
use inkwell::module::Linkage;
use inkwell::types::{AnyTypeEnum, BasicMetadataTypeEnum, BasicType, BasicTypeEnum};
use inkwell::values::{
@ -18,18 +17,9 @@ use inkwell::{AddressSpace, IntPredicate};
use roc_module::symbol::Interns;
use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout};
use roc_target::TargetInfo;
/// "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;
pub fn refcount_1(ctx: &Context, target_info: TargetInfo) -> IntValue<'_> {
match target_info.ptr_width() {
roc_target::PtrWidth::Bytes4 => ctx.i32_type().const_int(i32::MIN as u64, false),
roc_target::PtrWidth::Bytes8 => ctx.i64_type().const_int(i64::MIN as u64, false),
}
}
use super::build::load_roc_value;
use super::convert::{argument_type_from_layout, argument_type_from_union_layout};
pub struct PointerToRefcount<'ctx> {
value: PointerValue<'ctx>,
@ -93,7 +83,14 @@ impl<'ctx> PointerToRefcount<'ctx> {
pub fn is_1<'a, 'env>(&self, env: &Env<'a, 'ctx, 'env>) -> IntValue<'ctx> {
let current = self.get_refcount(env);
let one = refcount_1(env.context, env.target_info);
let one = match env.target_info.ptr_width() {
roc_target::PtrWidth::Bytes4 => {
env.context.i32_type().const_int(i32::MIN as u64, false)
}
roc_target::PtrWidth::Bytes8 => {
env.context.i64_type().const_int(i64::MIN as u64, false)
}
};
env.builder
.build_int_compare(IntPredicate::EQ, current, one, "is_one")
@ -122,38 +119,7 @@ impl<'ctx> PointerToRefcount<'ctx> {
}
fn increment<'a, 'env>(&self, amount: IntValue<'ctx>, env: &Env<'a, 'ctx, 'env>) {
let refcount = self.get_refcount(env);
let builder = env.builder;
let refcount_type = env.ptr_int();
let is_static_allocation = builder.build_int_compare(
IntPredicate::EQ,
refcount,
refcount_type.const_int(REFCOUNT_MAX as u64, false),
"refcount_max_check",
);
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_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);
{
env.builder.position_at_end(modify_block);
let incremented = builder.build_int_add(refcount, amount, "increment_refcount");
self.set_refcount(env, incremented);
env.builder.build_unconditional_branch(cont_block);
}
env.builder.position_at_end(cont_block);
incref_pointer(env, self.value, amount);
}
pub fn decrement<'a, 'env>(&self, env: &Env<'a, 'ctx, 'env>, layout: &Layout<'a>) {
@ -229,6 +195,25 @@ impl<'ctx> PointerToRefcount<'ctx> {
}
}
fn incref_pointer<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
pointer: PointerValue<'ctx>,
amount: IntValue<'ctx>,
) {
call_void_bitcode_fn(
env,
&[
env.builder.build_bitcast(
pointer,
env.ptr_int().ptr_type(AddressSpace::Generic),
"to_isize_ptr",
),
amount.into(),
],
roc_builtins::bitcode::UTILS_INCREF,
);
}
fn decref_pointer<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
pointer: PointerValue<'ctx>,
@ -280,7 +265,7 @@ fn modify_refcount_struct<'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 layout = Layout::Struct(layouts);
let layout = Layout::struct_no_name_order(layouts);
let (_, fn_name) = function_name_from_mode(
layout_ids,
@ -440,7 +425,7 @@ fn modify_refcount_builtin<'a, 'ctx, 'env>(
}
Set(element_layout) => {
let key_layout = element_layout;
let value_layout = &Layout::Struct(&[]);
let value_layout = &Layout::UNIT;
let function = modify_refcount_dict(
env,
@ -589,6 +574,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::*;
@ -619,8 +610,9 @@ fn modify_refcount_layout_build_function<'a, 'ctx, 'env>(
}
}
Struct(layouts) => {
let function = modify_refcount_struct(env, layout_ids, layouts, mode, when_recursive);
Struct { field_layouts, .. } => {
let function =
modify_refcount_struct(env, layout_ids, field_layouts, mode, when_recursive);
Some(function)
}
@ -678,7 +670,7 @@ fn modify_refcount_list<'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 = argument_type_from_layout(env, layout);
let function_value = build_header(env, basic_type, mode, &fn_name);
modify_refcount_list_help(
@ -815,7 +807,7 @@ fn modify_refcount_str<'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 = argument_type_from_layout(env, layout);
let function_value = build_header(env, basic_type, mode, &fn_name);
modify_refcount_str_help(env, mode, layout, function_value);
@ -855,17 +847,26 @@ fn modify_refcount_str_help<'a, 'ctx, 'env>(
let parent = fn_val;
let arg_val = if Layout::Builtin(Builtin::Str).is_passed_by_reference(env.target_info) {
env.builder
.build_load(arg_val.into_pointer_value(), "load_str_to_stack")
} else {
// it's already a struct, just do nothing
debug_assert!(arg_val.is_struct_value());
arg_val
};
let str_wrapper = arg_val.into_struct_value();
let len = builder
.build_extract_value(str_wrapper, Builtin::WRAPPER_LEN, "read_str_ptr")
let capacity = builder
.build_extract_value(str_wrapper, Builtin::WRAPPER_CAPACITY, "read_str_capacity")
.unwrap()
.into_int_value();
// Small strings have 1 as the first bit of length, making them negative.
// Small strings have 1 as the first bit of capacity, making them negative.
// Thus, to check for big and non empty, just needs a signed len > 0.
let is_big_and_non_empty = builder.build_int_compare(
IntPredicate::SGT,
len,
capacity,
env.ptr_int().const_zero(),
"is_big_str",
);
@ -889,6 +890,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>,
@ -1312,7 +1383,8 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>(
env.builder.position_at_end(block);
let wrapper_type = basic_type_from_layout(env, &Layout::Struct(field_layouts));
let wrapper_type =
basic_type_from_layout(env, &Layout::struct_no_name_order(field_layouts));
// cast the opaque pointer to a pointer of the correct shape
let struct_ptr = env
@ -1354,9 +1426,8 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>(
.build_struct_gep(struct_ptr, i as u32, "gep_recursive_pointer")
.unwrap();
let field = env
.builder
.build_load(elem_pointer, "decrement_struct_field");
let field =
load_roc_value(env, *field_layout, elem_pointer, "decrement_struct_field");
deferred_nonrec.push((field, field_layout));
}
@ -1616,7 +1687,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();
@ -1633,7 +1705,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(
@ -1697,9 +1769,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);
@ -1720,7 +1792,8 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
let block = env.context.append_basic_block(parent, "tag_id_modify");
env.builder.position_at_end(block);
let wrapper_type = basic_type_from_layout(env, &Layout::Struct(field_layouts));
let wrapper_type =
basic_type_from_layout(env, &Layout::struct_no_name_order(field_layouts));
debug_assert!(wrapper_type.is_struct_type());
let opaque_tag_data_ptr = env
@ -1743,7 +1816,7 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
.build_struct_gep(cast_tag_data_pointer, i as u32, "modify_tag_field")
.unwrap();
let field_value = if field_layout.is_passed_by_reference() {
let field_value = if field_layout.is_passed_by_reference(env.target_info) {
field_ptr.into()
} else {
env.builder.build_load(field_ptr, "field_value")