roc/compiler/gen_llvm/src/llvm/build_dict.rs
2021-07-30 17:13:50 +02:00

875 lines
29 KiB
Rust

use crate::debug_info_init;
use crate::llvm::bitcode::{
build_dec_wrapper, build_eq_wrapper, build_inc_wrapper, call_bitcode_fn, call_void_bitcode_fn,
};
use crate::llvm::build::{
complex_bitcast, load_symbol, load_symbol_and_layout, Env, RocFunctionCall, Scope,
};
use crate::llvm::build_list::{layout_width, pass_as_opaque};
use crate::llvm::convert::basic_type_from_layout;
use crate::llvm::refcounting::Mode;
use inkwell::attributes::{Attribute, AttributeLoc};
use inkwell::types::BasicType;
use inkwell::values::{BasicValue, BasicValueEnum, FunctionValue, StructValue};
use inkwell::AddressSpace;
use roc_builtins::bitcode;
use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout, LayoutIds};
#[repr(u8)]
enum Alignment {
Align16KeyFirst = 0,
Align16ValueFirst = 1,
Align8KeyFirst = 2,
Align8ValueFirst = 3,
}
impl Alignment {
fn from_key_value_layout(key: &Layout, value: &Layout, ptr_bytes: u32) -> Alignment {
let key_align = key.alignment_bytes(ptr_bytes);
let value_align = value.alignment_bytes(ptr_bytes);
if key_align >= value_align {
match key_align.max(value_align) {
8 => Alignment::Align8KeyFirst,
16 => Alignment::Align16KeyFirst,
_ => unreachable!(),
}
} else {
match key_align.max(value_align) {
8 => Alignment::Align8ValueFirst,
16 => Alignment::Align16ValueFirst,
_ => unreachable!(),
}
}
}
}
pub fn dict_len<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
scope: &Scope<'a, 'ctx>,
dict_symbol: Symbol,
) -> BasicValueEnum<'ctx> {
let ctx = env.context;
let (_, dict_layout) = load_symbol_and_layout(scope, &dict_symbol);
match dict_layout {
Layout::Builtin(Builtin::Dict(_, _)) => {
// let dict_as_int = dict_symbol_to_i128(env, scope, dict_symbol);
let dict_as_zig_dict = dict_symbol_to_zig_dict(env, scope, dict_symbol);
let dict_ptr = env
.builder
.build_alloca(dict_as_zig_dict.get_type(), "dict_ptr");
env.builder.build_store(dict_ptr, dict_as_zig_dict);
call_bitcode_fn(env, &[dict_ptr.into()], bitcode::DICT_LEN)
}
Layout::Builtin(Builtin::EmptyDict) => ctx.i64_type().const_zero().into(),
_ => unreachable!("Invalid layout given to Dict.len : {:?}", dict_layout),
}
}
pub fn dict_empty<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> BasicValueEnum<'ctx> {
// get the RocDict type defined by zig
let roc_dict_type = env.module.get_struct_type("dict.RocDict").unwrap();
// we must give a pointer for the bitcode function to write the result into
let result_alloc = env.builder.build_alloca(roc_dict_type, "dict_empty");
call_void_bitcode_fn(env, &[result_alloc.into()], bitcode::DICT_EMPTY);
env.builder.build_load(result_alloc, "load_result")
}
#[allow(clippy::too_many_arguments)]
pub fn dict_insert<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
dict: BasicValueEnum<'ctx>,
key: BasicValueEnum<'ctx>,
key_layout: &Layout<'a>,
value: BasicValueEnum<'ctx>,
value_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
let builder = env.builder;
let zig_dict_type = env.module.get_struct_type("dict.RocDict").unwrap();
let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
let dict_ptr = builder.build_alloca(zig_dict_type, "dict_ptr");
let key_ptr = builder.build_alloca(key.get_type(), "key_ptr");
let value_ptr = builder.build_alloca(value.get_type(), "value_ptr");
env.builder.build_store(dict_ptr, dict);
env.builder.build_store(key_ptr, key);
env.builder.build_store(value_ptr, value);
let key_width = env
.ptr_int()
.const_int(key_layout.stack_size(env.ptr_bytes) as u64, false);
let value_width = env
.ptr_int()
.const_int(value_layout.stack_size(env.ptr_bytes) as u64, false);
let result_ptr = builder.build_alloca(zig_dict_type, "result_ptr");
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes);
let alignment_iv = env.context.i8_type().const_int(alignment as u64, false);
let hash_fn = build_hash_wrapper(env, layout_ids, key_layout);
let eq_fn = build_eq_wrapper(env, layout_ids, key_layout);
let dec_key_fn = build_dec_wrapper(env, layout_ids, key_layout);
let dec_value_fn = build_dec_wrapper(env, layout_ids, value_layout);
call_void_bitcode_fn(
env,
&[
dict_ptr.into(),
alignment_iv.into(),
env.builder.build_bitcast(key_ptr, u8_ptr, "to_u8_ptr"),
key_width.into(),
env.builder.build_bitcast(value_ptr, u8_ptr, "to_u8_ptr"),
value_width.into(),
hash_fn.as_global_value().as_pointer_value().into(),
eq_fn.as_global_value().as_pointer_value().into(),
dec_key_fn.as_global_value().as_pointer_value().into(),
dec_value_fn.as_global_value().as_pointer_value().into(),
result_ptr.into(),
],
bitcode::DICT_INSERT,
);
env.builder.build_load(result_ptr, "load_result")
}
#[allow(clippy::too_many_arguments)]
pub fn dict_remove<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
dict: BasicValueEnum<'ctx>,
key: BasicValueEnum<'ctx>,
key_layout: &Layout<'a>,
value_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
let builder = env.builder;
let zig_dict_type = env.module.get_struct_type("dict.RocDict").unwrap();
let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
let dict_ptr = builder.build_alloca(zig_dict_type, "dict_ptr");
let key_ptr = builder.build_alloca(key.get_type(), "key_ptr");
env.builder.build_store(dict_ptr, dict);
env.builder.build_store(key_ptr, key);
let key_width = env
.ptr_int()
.const_int(key_layout.stack_size(env.ptr_bytes) as u64, false);
let value_width = env
.ptr_int()
.const_int(value_layout.stack_size(env.ptr_bytes) as u64, false);
let result_ptr = builder.build_alloca(zig_dict_type, "result_ptr");
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes);
let alignment_iv = env.context.i8_type().const_int(alignment as u64, false);
let hash_fn = build_hash_wrapper(env, layout_ids, key_layout);
let eq_fn = build_eq_wrapper(env, layout_ids, key_layout);
let dec_key_fn = build_dec_wrapper(env, layout_ids, key_layout);
let dec_value_fn = build_dec_wrapper(env, layout_ids, value_layout);
call_void_bitcode_fn(
env,
&[
dict_ptr.into(),
alignment_iv.into(),
env.builder.build_bitcast(key_ptr, u8_ptr, "to_u8_ptr"),
key_width.into(),
value_width.into(),
hash_fn.as_global_value().as_pointer_value().into(),
eq_fn.as_global_value().as_pointer_value().into(),
dec_key_fn.as_global_value().as_pointer_value().into(),
dec_value_fn.as_global_value().as_pointer_value().into(),
result_ptr.into(),
],
bitcode::DICT_REMOVE,
);
env.builder.build_load(result_ptr, "load_result")
}
#[allow(clippy::too_many_arguments)]
pub fn dict_contains<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
dict: BasicValueEnum<'ctx>,
key: BasicValueEnum<'ctx>,
key_layout: &Layout<'a>,
value_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
let builder = env.builder;
let zig_dict_type = env.module.get_struct_type("dict.RocDict").unwrap();
let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
let dict_ptr = builder.build_alloca(zig_dict_type, "dict_ptr");
let key_ptr = builder.build_alloca(key.get_type(), "key_ptr");
env.builder.build_store(dict_ptr, dict);
env.builder.build_store(key_ptr, key);
let key_width = env
.ptr_int()
.const_int(key_layout.stack_size(env.ptr_bytes) as u64, false);
let value_width = env
.ptr_int()
.const_int(value_layout.stack_size(env.ptr_bytes) as u64, false);
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes);
let alignment_iv = env.context.i8_type().const_int(alignment as u64, false);
let hash_fn = build_hash_wrapper(env, layout_ids, key_layout);
let eq_fn = build_eq_wrapper(env, layout_ids, key_layout);
call_bitcode_fn(
env,
&[
dict_ptr.into(),
alignment_iv.into(),
env.builder.build_bitcast(key_ptr, u8_ptr, "to_u8_ptr"),
key_width.into(),
value_width.into(),
hash_fn.as_global_value().as_pointer_value().into(),
eq_fn.as_global_value().as_pointer_value().into(),
],
bitcode::DICT_CONTAINS,
)
}
#[allow(clippy::too_many_arguments)]
pub fn dict_get<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
dict: BasicValueEnum<'ctx>,
key: BasicValueEnum<'ctx>,
key_layout: &Layout<'a>,
value_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
let builder = env.builder;
let zig_dict_type = env.module.get_struct_type("dict.RocDict").unwrap();
let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
let dict_ptr = builder.build_alloca(zig_dict_type, "dict_ptr");
let key_ptr = builder.build_alloca(key.get_type(), "key_ptr");
env.builder.build_store(dict_ptr, dict);
env.builder.build_store(key_ptr, key);
let key_width = env
.ptr_int()
.const_int(key_layout.stack_size(env.ptr_bytes) as u64, false);
let value_width = env
.ptr_int()
.const_int(value_layout.stack_size(env.ptr_bytes) as u64, false);
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes);
let alignment_iv = env.context.i8_type().const_int(alignment as u64, false);
let hash_fn = build_hash_wrapper(env, layout_ids, key_layout);
let eq_fn = build_eq_wrapper(env, layout_ids, key_layout);
let inc_value_fn = build_inc_wrapper(env, layout_ids, value_layout);
// { flag: bool, value: *const u8 }
let result = call_bitcode_fn(
env,
&[
dict_ptr.into(),
alignment_iv.into(),
env.builder.build_bitcast(key_ptr, u8_ptr, "to_u8_ptr"),
key_width.into(),
value_width.into(),
hash_fn.as_global_value().as_pointer_value().into(),
eq_fn.as_global_value().as_pointer_value().into(),
inc_value_fn.as_global_value().as_pointer_value().into(),
],
bitcode::DICT_GET,
)
.into_struct_value();
let flag = env
.builder
.build_extract_value(result, 1, "get_flag")
.unwrap()
.into_int_value();
let value_u8_ptr = env
.builder
.build_extract_value(result, 0, "get_value_ptr")
.unwrap()
.into_pointer_value();
let start_block = env.builder.get_insert_block().unwrap();
let parent = start_block.get_parent().unwrap();
let if_not_null = env.context.append_basic_block(parent, "if_not_null");
let done_block = env.context.append_basic_block(parent, "done");
let value_bt = basic_type_from_layout(env, value_layout);
let default = value_bt.const_zero();
env.builder
.build_conditional_branch(flag, if_not_null, done_block);
env.builder.position_at_end(if_not_null);
let value_ptr = env
.builder
.build_bitcast(
value_u8_ptr,
value_bt.ptr_type(AddressSpace::Generic),
"from_opaque",
)
.into_pointer_value();
let loaded = env.builder.build_load(value_ptr, "load_value");
env.builder.build_unconditional_branch(done_block);
env.builder.position_at_end(done_block);
let result_phi = env.builder.build_phi(value_bt, "result");
result_phi.add_incoming(&[(&default, start_block), (&loaded, if_not_null)]);
let value = result_phi.as_basic_value();
let result = env
.context
.struct_type(&[value_bt, env.context.bool_type().into()], false)
.const_zero();
let result = env
.builder
.build_insert_value(result, flag, 1, "insert_flag")
.unwrap();
env.builder
.build_insert_value(result, value, 0, "insert_value")
.unwrap()
.into_struct_value()
.into()
}
#[allow(clippy::too_many_arguments)]
pub fn dict_elements_rc<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
dict: BasicValueEnum<'ctx>,
key_layout: &Layout<'a>,
value_layout: &Layout<'a>,
rc_operation: Mode,
) {
let builder = env.builder;
let zig_dict_type = env.module.get_struct_type("dict.RocDict").unwrap();
let dict_ptr = builder.build_alloca(zig_dict_type, "dict_ptr");
env.builder.build_store(dict_ptr, dict);
let key_width = env
.ptr_int()
.const_int(key_layout.stack_size(env.ptr_bytes) as u64, false);
let value_width = env
.ptr_int()
.const_int(value_layout.stack_size(env.ptr_bytes) as u64, false);
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes);
let alignment_iv = env.context.i8_type().const_int(alignment as u64, false);
let (key_fn, value_fn) = match rc_operation {
Mode::Inc => (
build_inc_wrapper(env, layout_ids, key_layout),
build_inc_wrapper(env, layout_ids, value_layout),
),
Mode::Dec => (
build_dec_wrapper(env, layout_ids, key_layout),
build_dec_wrapper(env, layout_ids, value_layout),
),
};
call_void_bitcode_fn(
env,
&[
dict_ptr.into(),
alignment_iv.into(),
key_width.into(),
value_width.into(),
key_fn.as_global_value().as_pointer_value().into(),
value_fn.as_global_value().as_pointer_value().into(),
],
bitcode::DICT_ELEMENTS_RC,
);
}
#[allow(clippy::too_many_arguments)]
pub fn dict_keys<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
dict: BasicValueEnum<'ctx>,
key_layout: &Layout<'a>,
value_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
let builder = env.builder;
let zig_dict_type = env.module.get_struct_type("dict.RocDict").unwrap();
let zig_list_type = env.module.get_struct_type("list.RocList").unwrap();
let dict_ptr = builder.build_alloca(zig_dict_type, "dict_ptr");
env.builder.build_store(dict_ptr, dict);
let key_width = env
.ptr_int()
.const_int(key_layout.stack_size(env.ptr_bytes) as u64, false);
let value_width = env
.ptr_int()
.const_int(value_layout.stack_size(env.ptr_bytes) as u64, false);
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes);
let alignment_iv = env.context.i8_type().const_int(alignment as u64, false);
let inc_key_fn = build_inc_wrapper(env, layout_ids, key_layout);
let list_ptr = builder.build_alloca(zig_list_type, "list_ptr");
call_void_bitcode_fn(
env,
&[
dict_ptr.into(),
alignment_iv.into(),
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")
}
#[allow(clippy::too_many_arguments)]
pub fn dict_union<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
dict1: BasicValueEnum<'ctx>,
dict2: BasicValueEnum<'ctx>,
key_layout: &Layout<'a>,
value_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
let builder = env.builder;
let zig_dict_type = env.module.get_struct_type("dict.RocDict").unwrap();
let dict1_ptr = builder.build_alloca(zig_dict_type, "dict_ptr");
let dict2_ptr = builder.build_alloca(zig_dict_type, "dict_ptr");
env.builder.build_store(dict1_ptr, dict1);
env.builder.build_store(dict2_ptr, dict2);
let key_width = env
.ptr_int()
.const_int(key_layout.stack_size(env.ptr_bytes) as u64, false);
let value_width = env
.ptr_int()
.const_int(value_layout.stack_size(env.ptr_bytes) as u64, false);
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes);
let alignment_iv = env.context.i8_type().const_int(alignment as u64, false);
let hash_fn = build_hash_wrapper(env, layout_ids, key_layout);
let eq_fn = build_eq_wrapper(env, layout_ids, key_layout);
let inc_key_fn = build_inc_wrapper(env, layout_ids, key_layout);
let inc_value_fn = build_inc_wrapper(env, layout_ids, value_layout);
let output_ptr = builder.build_alloca(zig_dict_type, "output_ptr");
call_void_bitcode_fn(
env,
&[
dict1_ptr.into(),
dict2_ptr.into(),
alignment_iv.into(),
key_width.into(),
value_width.into(),
hash_fn.as_global_value().as_pointer_value().into(),
eq_fn.as_global_value().as_pointer_value().into(),
inc_key_fn.as_global_value().as_pointer_value().into(),
inc_value_fn.as_global_value().as_pointer_value().into(),
output_ptr.into(),
],
bitcode::DICT_UNION,
);
env.builder.build_load(output_ptr, "load_output_ptr")
}
#[allow(clippy::too_many_arguments)]
pub fn dict_difference<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
dict1: BasicValueEnum<'ctx>,
dict2: BasicValueEnum<'ctx>,
key_layout: &Layout<'a>,
value_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
dict_intersect_or_difference(
env,
layout_ids,
dict1,
dict2,
key_layout,
value_layout,
bitcode::DICT_DIFFERENCE,
)
}
#[allow(clippy::too_many_arguments)]
pub fn dict_intersection<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
dict1: BasicValueEnum<'ctx>,
dict2: BasicValueEnum<'ctx>,
key_layout: &Layout<'a>,
value_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
dict_intersect_or_difference(
env,
layout_ids,
dict1,
dict2,
key_layout,
value_layout,
bitcode::DICT_INTERSECTION,
)
}
#[allow(clippy::too_many_arguments)]
fn dict_intersect_or_difference<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
dict1: BasicValueEnum<'ctx>,
dict2: BasicValueEnum<'ctx>,
key_layout: &Layout<'a>,
value_layout: &Layout<'a>,
op: &str,
) -> BasicValueEnum<'ctx> {
let builder = env.builder;
let zig_dict_type = env.module.get_struct_type("dict.RocDict").unwrap();
let dict1_ptr = builder.build_alloca(zig_dict_type, "dict_ptr");
let dict2_ptr = builder.build_alloca(zig_dict_type, "dict_ptr");
env.builder.build_store(dict1_ptr, dict1);
env.builder.build_store(dict2_ptr, dict2);
let key_width = env
.ptr_int()
.const_int(key_layout.stack_size(env.ptr_bytes) as u64, false);
let value_width = env
.ptr_int()
.const_int(value_layout.stack_size(env.ptr_bytes) as u64, false);
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes);
let alignment_iv = env.context.i8_type().const_int(alignment as u64, false);
let hash_fn = build_hash_wrapper(env, layout_ids, key_layout);
let eq_fn = build_eq_wrapper(env, layout_ids, key_layout);
let dec_key_fn = build_dec_wrapper(env, layout_ids, key_layout);
let dec_value_fn = build_dec_wrapper(env, layout_ids, value_layout);
let output_ptr = builder.build_alloca(zig_dict_type, "output_ptr");
call_void_bitcode_fn(
env,
&[
dict1_ptr.into(),
dict2_ptr.into(),
alignment_iv.into(),
key_width.into(),
value_width.into(),
hash_fn.as_global_value().as_pointer_value().into(),
eq_fn.as_global_value().as_pointer_value().into(),
dec_key_fn.as_global_value().as_pointer_value().into(),
dec_value_fn.as_global_value().as_pointer_value().into(),
output_ptr.into(),
],
op,
);
env.builder.build_load(output_ptr, "load_output_ptr")
}
#[allow(clippy::too_many_arguments)]
pub fn dict_walk<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
roc_function_call: RocFunctionCall<'ctx>,
dict: BasicValueEnum<'ctx>,
accum: BasicValueEnum<'ctx>,
key_layout: &Layout<'a>,
value_layout: &Layout<'a>,
accum_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
let builder = env.builder;
let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
let zig_dict_type = env.module.get_struct_type("dict.RocDict").unwrap();
let dict_ptr = builder.build_alloca(zig_dict_type, "dict_ptr");
env.builder.build_store(dict_ptr, dict);
let accum_bt = basic_type_from_layout(env, accum_layout);
let accum_ptr = builder.build_alloca(accum_bt, "accum_ptr");
env.builder.build_store(accum_ptr, accum);
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes);
let alignment_iv = env.context.i8_type().const_int(alignment as u64, false);
let output_ptr = builder.build_alloca(accum_bt, "output_ptr");
call_void_bitcode_fn(
env,
&[
dict_ptr.into(),
roc_function_call.caller.into(),
pass_as_opaque(env, roc_function_call.data),
roc_function_call.inc_n_data.into(),
roc_function_call.data_is_owned.into(),
env.builder.build_bitcast(accum_ptr, u8_ptr, "to_opaque"),
alignment_iv.into(),
layout_width(env, key_layout),
layout_width(env, value_layout),
layout_width(env, accum_layout),
env.builder.build_bitcast(output_ptr, u8_ptr, "to_opaque"),
],
bitcode::DICT_WALK,
);
env.builder.build_load(output_ptr, "load_output_ptr")
}
#[allow(clippy::too_many_arguments)]
pub fn dict_values<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
dict: BasicValueEnum<'ctx>,
key_layout: &Layout<'a>,
value_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
let builder = env.builder;
let zig_dict_type = super::convert::zig_dict_type(env);
let zig_list_type = super::convert::zig_list_type(env);
let dict_ptr = builder.build_alloca(zig_dict_type, "dict_ptr");
env.builder.build_store(dict_ptr, dict);
let key_width = env
.ptr_int()
.const_int(key_layout.stack_size(env.ptr_bytes) as u64, false);
let value_width = env
.ptr_int()
.const_int(value_layout.stack_size(env.ptr_bytes) as u64, false);
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes);
let alignment_iv = env.context.i8_type().const_int(alignment as u64, false);
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(
env,
&[
dict_ptr.into(),
alignment_iv.into(),
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)]
pub fn set_from_list<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
list: BasicValueEnum<'ctx>,
key_layout: &Layout<'a>,
) -> 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.context.i128_type().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.ptr_bytes) as u64, false);
let value_width = env.ptr_int().const_zero();
let result_alloca = builder.build_alloca(zig_dict_type(env), "result_alloca");
let alignment =
Alignment::from_key_value_layout(key_layout, &Layout::Struct(&[]), env.ptr_bytes);
let alignment_iv = env.context.i8_type().const_int(alignment as u64, false);
let hash_fn = build_hash_wrapper(env, layout_ids, key_layout);
let eq_fn = build_eq_wrapper(env, layout_ids, key_layout);
let dec_key_fn = build_dec_wrapper(env, layout_ids, key_layout);
call_void_bitcode_fn(
env,
&[
env.builder
.build_load(list_ptr.into_pointer_value(), "as_i128"),
alignment_iv.into(),
key_width.into(),
value_width.into(),
hash_fn.as_global_value().as_pointer_value().into(),
eq_fn.as_global_value().as_pointer_value().into(),
dec_key_fn.as_global_value().as_pointer_value().into(),
result_alloca.into(),
],
bitcode::SET_FROM_LIST,
);
env.builder.build_load(result_alloca, "load_result")
}
fn build_hash_wrapper<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
layout: &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 symbol = Symbol::GENERIC_HASH_REF;
let fn_name = layout_ids
.get(symbol, layout)
.to_symbol_string(symbol, &env.interns);
let function_value = match env.module.get_function(fn_name.as_str()) {
Some(function_value) => function_value,
None => {
let seed_type = env.context.i64_type();
let arg_type = env.context.i8_type().ptr_type(AddressSpace::Generic);
let function_value = crate::llvm::refcounting::build_header_help(
env,
&fn_name,
seed_type.into(),
&[seed_type.into(), arg_type.into()],
);
let kind_id = Attribute::get_named_enum_kind_id("alwaysinline");
debug_assert!(kind_id > 0);
let attr = env.context.create_enum_attribute(kind_id, 1);
function_value.add_attribute(AttributeLoc::Function, attr);
let entry = env.context.append_basic_block(function_value, "entry");
env.builder.position_at_end(entry);
debug_info_init!(env, function_value);
let mut it = function_value.get_param_iter();
let seed_arg = it.next().unwrap().into_int_value();
let value_ptr = it.next().unwrap().into_pointer_value();
seed_arg.set_name(Symbol::ARG_1.ident_string(&env.interns));
value_ptr.set_name(Symbol::ARG_2.ident_string(&env.interns));
let value_type = basic_type_from_layout(env, layout).ptr_type(AddressSpace::Generic);
let value_cast = env
.builder
.build_bitcast(value_ptr, value_type, "load_opaque")
.into_pointer_value();
let val_arg = env.builder.build_load(value_cast, "load_opaque");
let result =
crate::llvm::build_hash::generic_hash(env, layout_ids, seed_arg, val_arg, layout);
env.builder.build_return(Some(&result));
function_value
}
};
env.builder.position_at_end(block);
env.builder
.set_current_debug_location(env.context, di_location);
function_value
}
fn dict_symbol_to_zig_dict<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
scope: &Scope<'a, 'ctx>,
symbol: Symbol,
) -> StructValue<'ctx> {
let dict = load_symbol(scope, &symbol);
let zig_dict_type = env.module.get_struct_type("dict.RocDict").unwrap();
complex_bitcast(env.builder, dict, zig_dict_type.into(), "dict_to_zig_dict").into_struct_value()
}
fn zig_dict_type<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> inkwell::types::StructType<'ctx> {
env.module.get_struct_type("dict.RocDict").unwrap()
}