refactor build_hash

This commit is contained in:
Folkert 2021-02-10 15:24:06 +01:00
parent 0ed87828e5
commit 9ab9675a43

View file

@ -2,7 +2,7 @@ use crate::llvm::build::call_bitcode_fn;
use crate::llvm::build::Env;
use crate::llvm::build_str;
use crate::llvm::convert::basic_type_from_layout;
use inkwell::values::{BasicValueEnum, IntValue};
use inkwell::values::{BasicValueEnum, IntValue, PointerValue, StructValue};
use roc_builtins::bitcode;
use roc_mono::layout::{Builtin, Layout};
@ -12,12 +12,29 @@ pub fn hash<'a, 'ctx, 'env>(
val: BasicValueEnum<'ctx>,
layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
let ptr_bytes = env.ptr_bytes;
// NOTE: C and Zig use this value for their initial HashMap seed: 0xc70f6907
match layout {
Layout::Builtin(builtin) => match builtin {
Layout::Builtin(builtin) => hash_builtin(env, seed, val, layout, builtin).into(),
Layout::Struct(fields) => hash_struct(env, seed, val.into_struct_value(), fields).into(),
_ => {
todo!("Implement hash for other layouts")
}
}
}
fn hash_builtin<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
seed: IntValue<'ctx>,
val: BasicValueEnum<'ctx>,
layout: &Layout<'a>,
builtin: &Builtin<'a>,
) -> IntValue<'ctx> {
let ptr_bytes = env.ptr_bytes;
match builtin {
Builtin::Int128
| Builtin::Int64
| Builtin::Int32
@ -29,30 +46,8 @@ pub fn hash<'a, 'ctx, 'env>(
| Builtin::Float128
| Builtin::Float16
| Builtin::Usize => {
let num_bytes = env
.context
.i64_type()
.const_int(layout.stack_size(ptr_bytes) as u64, false);
let basic_type =
basic_type_from_layout(env.arena, env.context, layout, env.ptr_bytes);
let alloc = env.builder.build_alloca(basic_type, "store");
env.builder.build_store(alloc, val);
let hash_bytes = env.builder.build_bitcast(
alloc,
env.context
.i8_type()
.ptr_type(inkwell::AddressSpace::Generic),
"as_u8_ptr",
);
call_bitcode_fn(
env,
&[seed.into(), hash_bytes, num_bytes.into()],
&bitcode::DICT_HASH,
)
let hash_bytes = store_and_use_as_u8_ptr(env, val.into(), &layout);
hash_bitcode_fn(env, seed, hash_bytes, layout.stack_size(ptr_bytes))
}
Builtin::Str => {
// let zig deal with big vs small string
@ -61,6 +56,7 @@ pub fn hash<'a, 'ctx, 'env>(
&[seed.into(), build_str::str_to_i128(env, val).into()],
&bitcode::DICT_HASH_STR,
)
.into_int_value()
}
Builtin::EmptyStr | Builtin::EmptyDict | Builtin::EmptyList | Builtin::EmptySet => {
todo!("These are all the same, so we should just hardcode some hash value")
@ -75,10 +71,60 @@ pub fn hash<'a, 'ctx, 'env>(
Builtin::List(_, _) => {
todo!("Implement Hash for List")
}
},
}
}
_ => {
todo!("Implement hash for other layouts")
fn hash_struct<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
mut seed: IntValue<'ctx>,
val: StructValue<'ctx>,
field_layouts: &[Layout<'a>],
) -> IntValue<'ctx> {
let ptr_bytes = env.ptr_bytes;
let layout = Layout::Struct(field_layouts);
if !layout.contains_refcounted() {
// this is a struct of only basic types, so we can just hash its bits
let hash_bytes = store_and_use_as_u8_ptr(env, val.into(), &layout);
hash_bitcode_fn(env, seed, hash_bytes, layout.stack_size(ptr_bytes))
} else {
todo!()
}
}
fn store_and_use_as_u8_ptr<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
value: BasicValueEnum<'ctx>,
layout: &Layout<'a>,
) -> PointerValue<'ctx> {
let basic_type = basic_type_from_layout(env.arena, env.context, &layout, env.ptr_bytes);
let alloc = env.builder.build_alloca(basic_type, "store");
env.builder.build_store(alloc, value);
env.builder
.build_bitcast(
alloc,
env.context
.i8_type()
.ptr_type(inkwell::AddressSpace::Generic),
"as_u8_ptr",
)
.into_pointer_value()
}
fn hash_bitcode_fn<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
seed: IntValue<'ctx>,
buffer: PointerValue<'ctx>,
width: u32,
) -> IntValue<'ctx> {
let num_bytes = env.context.i64_type().const_int(width as u64, false);
call_bitcode_fn(
env,
&[seed.into(), buffer.into(), num_bytes.into()],
&bitcode::DICT_HASH,
)
.into_int_value()
}