mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 11:52:19 +00:00
Merge branch 'trunk' of github.com:rtfeldman/roc into pure-roc-list-walk
This commit is contained in:
commit
219e6d11cf
1067 changed files with 105 additions and 104 deletions
22
crates/compiler/gen_llvm/Cargo.toml
Normal file
22
crates/compiler/gen_llvm/Cargo.toml
Normal file
|
@ -0,0 +1,22 @@
|
|||
[package]
|
||||
name = "roc_gen_llvm"
|
||||
description = "The LLVM backend for the Roc compiler"
|
||||
version = "0.1.0"
|
||||
authors = ["The Roc Contributors"]
|
||||
license = "UPL-1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
roc_alias_analysis = { path = "../alias_analysis" }
|
||||
roc_collections = { path = "../collections" }
|
||||
roc_module = { path = "../module" }
|
||||
roc_builtins = { path = "../builtins" }
|
||||
roc_error_macros = { path = "../../error_macros" }
|
||||
roc_mono = { path = "../mono" }
|
||||
roc_target = { path = "../roc_target" }
|
||||
roc_std = { path = "../../roc_std", default-features = false }
|
||||
roc_debug_flags = { path = "../debug_flags" }
|
||||
morphic_lib = { path = "../../vendor/morphic_lib" }
|
||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||
inkwell = { path = "../../vendor/inkwell" }
|
||||
target-lexicon = "0.12.3"
|
9
crates/compiler/gen_llvm/src/lib.rs
Normal file
9
crates/compiler/gen_llvm/src/lib.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
#![warn(clippy::dbg_macro)]
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
// we actually want to compare against the literal float bits
|
||||
#![allow(clippy::float_cmp)]
|
||||
|
||||
pub mod llvm;
|
||||
|
||||
pub mod run_roc;
|
804
crates/compiler/gen_llvm/src/llvm/bitcode.rs
Normal file
804
crates/compiler/gen_llvm/src/llvm/bitcode.rs
Normal file
|
@ -0,0 +1,804 @@
|
|||
/// Helpers for interacting with the zig that generates bitcode
|
||||
use crate::debug_info_init;
|
||||
use crate::llvm::build::{
|
||||
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::{
|
||||
decrement_refcount_layout, increment_n_refcount_layout, increment_refcount_layout,
|
||||
};
|
||||
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>],
|
||||
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
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn call_list_bitcode_fn<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
args: &[BasicValueEnum<'ctx>],
|
||||
fn_name: &str,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
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>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
args: &[BasicValueEnum<'ctx>],
|
||||
fn_name: &str,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
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>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
args: &[BasicValueEnum<'ctx>],
|
||||
fn_name: &str,
|
||||
) -> InstructionValue<'ctx> {
|
||||
call_bitcode_fn_help(env, args, fn_name)
|
||||
.try_as_basic_value()
|
||||
.right()
|
||||
.unwrap_or_else(|| panic!("LLVM error: Tried to call void bitcode function, but got return value from bitcode function, {:?}", fn_name))
|
||||
}
|
||||
|
||||
fn call_bitcode_fn_help<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
args: &[BasicValueEnum<'ctx>],
|
||||
fn_name: &str,
|
||||
) -> CallSiteValue<'ctx> {
|
||||
let it = args.iter().map(|x| (*x).into());
|
||||
let arguments = bumpalo::collections::Vec::from_iter_in(it, env.arena);
|
||||
|
||||
let fn_val = env
|
||||
.module
|
||||
.get_function(fn_name)
|
||||
.unwrap_or_else(|| panic!("Unrecognized builtin function: {:?} - if you're working on the Roc compiler, do you need to rebuild the bitcode? See compiler/builtins/bitcode/README.md", fn_name));
|
||||
|
||||
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,
|
||||
Symbol::ARG_3,
|
||||
Symbol::ARG_4,
|
||||
Symbol::ARG_5,
|
||||
Symbol::ARG_6,
|
||||
Symbol::ARG_7,
|
||||
Symbol::ARG_8,
|
||||
];
|
||||
|
||||
pub fn build_has_tag_id<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
function: FunctionValue<'ctx>,
|
||||
union_layout: UnionLayout<'a>,
|
||||
) -> FunctionValue<'ctx> {
|
||||
let fn_name: &str = &format!("{}_has_tag_id", function.get_name().to_string_lossy());
|
||||
|
||||
// currently the code assumes we're dealing with a non-recursive layout
|
||||
debug_assert!(matches!(union_layout, UnionLayout::NonRecursive(_)));
|
||||
|
||||
match env.module.get_function(fn_name) {
|
||||
Some(function_value) => function_value,
|
||||
None => build_has_tag_id_help(env, union_layout, fn_name),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_has_tag_id_help<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
union_layout: UnionLayout<'a>,
|
||||
fn_name: &str,
|
||||
) -> FunctionValue<'ctx> {
|
||||
let i8_ptr_type = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
let argument_types: &[BasicTypeEnum] = &[env.context.i16_type().into(), i8_ptr_type.into()];
|
||||
|
||||
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||
let di_location = env.builder.get_current_debug_location().unwrap();
|
||||
|
||||
let output_type = crate::llvm::convert::zig_has_tag_id_type(env);
|
||||
|
||||
let function_value = crate::llvm::refcounting::build_header_help(
|
||||
env,
|
||||
fn_name,
|
||||
output_type.into(),
|
||||
argument_types,
|
||||
);
|
||||
|
||||
// called from zig, must use C calling convention
|
||||
function_value.set_call_conventions(C_CALL_CONV);
|
||||
|
||||
let kind_id = Attribute::get_named_enum_kind_id("alwaysinline");
|
||||
debug_assert!(kind_id > 0);
|
||||
let attr = env.context.create_enum_attribute(kind_id, 1);
|
||||
function_value.add_attribute(AttributeLoc::Function, attr);
|
||||
|
||||
let entry = env.context.append_basic_block(function_value, "entry");
|
||||
env.builder.position_at_end(entry);
|
||||
|
||||
debug_info_init!(env, function_value);
|
||||
|
||||
let it = function_value.get_param_iter();
|
||||
|
||||
let arguments =
|
||||
bumpalo::collections::Vec::from_iter_in(it.take(argument_types.len()), env.arena);
|
||||
|
||||
for (argument, name) in arguments.iter().zip(ARGUMENT_SYMBOLS.iter()) {
|
||||
argument.set_name(name.as_str(&env.interns));
|
||||
}
|
||||
|
||||
match arguments.as_slice() {
|
||||
[tag_id, tag_value_ptr] => {
|
||||
let tag_type = basic_type_from_layout(env, &Layout::Union(union_layout));
|
||||
|
||||
let tag_value = env.builder.build_pointer_cast(
|
||||
tag_value_ptr.into_pointer_value(),
|
||||
tag_type.ptr_type(AddressSpace::Generic),
|
||||
"load_opaque_get_tag_id",
|
||||
);
|
||||
|
||||
let actual_tag_id = {
|
||||
let tag_id_i64 = crate::llvm::build::get_tag_id(
|
||||
env,
|
||||
function_value,
|
||||
&union_layout,
|
||||
tag_value.into(),
|
||||
);
|
||||
|
||||
env.builder.build_int_cast_sign_flag(
|
||||
tag_id_i64,
|
||||
env.context.i16_type(),
|
||||
true,
|
||||
"to_i16",
|
||||
)
|
||||
};
|
||||
|
||||
let answer = env.builder.build_int_compare(
|
||||
inkwell::IntPredicate::EQ,
|
||||
tag_id.into_int_value(),
|
||||
actual_tag_id,
|
||||
"compare",
|
||||
);
|
||||
|
||||
let tag_data_ptr = {
|
||||
let ptr = env
|
||||
.builder
|
||||
.build_struct_gep(tag_value, TAG_DATA_INDEX, "get_data_ptr")
|
||||
.unwrap();
|
||||
|
||||
env.builder.build_bitcast(ptr, i8_ptr_type, "to_opaque")
|
||||
};
|
||||
|
||||
let field_vals = [(0, answer.into()), (1, tag_data_ptr)];
|
||||
|
||||
let output = struct_from_fields(env, output_type, field_vals.iter().copied());
|
||||
|
||||
env.builder.build_return(Some(&output));
|
||||
|
||||
env.builder.position_at_end(block);
|
||||
env.builder
|
||||
.set_current_debug_location(env.context, di_location);
|
||||
|
||||
function_value
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_transform_caller<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
function: FunctionValue<'ctx>,
|
||||
closure_data_layout: LambdaSet<'a>,
|
||||
argument_layouts: &[Layout<'a>],
|
||||
result_layout: Layout<'a>,
|
||||
) -> FunctionValue<'ctx> {
|
||||
let fn_name: &str = &format!(
|
||||
"{}_zig_function_caller",
|
||||
function.get_name().to_string_lossy()
|
||||
);
|
||||
|
||||
match env.module.get_function(fn_name) {
|
||||
Some(function_value) => function_value,
|
||||
None => build_transform_caller_help(
|
||||
env,
|
||||
function,
|
||||
closure_data_layout,
|
||||
argument_layouts,
|
||||
result_layout,
|
||||
fn_name,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_transform_caller_help<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
roc_function: FunctionValue<'ctx>,
|
||||
closure_data_layout: LambdaSet<'a>,
|
||||
argument_layouts: &[Layout<'a>],
|
||||
result_layout: Layout<'a>,
|
||||
fn_name: &str,
|
||||
) -> FunctionValue<'ctx> {
|
||||
debug_assert!(argument_layouts.len() <= 7);
|
||||
|
||||
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||
let di_location = env.builder.get_current_debug_location().unwrap();
|
||||
|
||||
let arg_type = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
let function_value = crate::llvm::refcounting::build_header_help(
|
||||
env,
|
||||
fn_name,
|
||||
env.context.void_type().into(),
|
||||
&(bumpalo::vec![in env.arena; BasicTypeEnum::PointerType(arg_type); argument_layouts.len() + 2]),
|
||||
);
|
||||
|
||||
// called from zig, must use C calling convention
|
||||
function_value.set_call_conventions(C_CALL_CONV);
|
||||
|
||||
let kind_id = Attribute::get_named_enum_kind_id("alwaysinline");
|
||||
debug_assert!(kind_id > 0);
|
||||
let attr = env.context.create_enum_attribute(kind_id, 1);
|
||||
function_value.add_attribute(AttributeLoc::Function, attr);
|
||||
|
||||
let entry = env.context.append_basic_block(function_value, "entry");
|
||||
env.builder.position_at_end(entry);
|
||||
|
||||
debug_info_init!(env, function_value);
|
||||
|
||||
let mut it = function_value.get_param_iter();
|
||||
let closure_ptr = it.next().unwrap().into_pointer_value();
|
||||
closure_ptr.set_name(Symbol::ARG_1.as_str(&env.interns));
|
||||
|
||||
let arguments =
|
||||
bumpalo::collections::Vec::from_iter_in(it.take(argument_layouts.len()), env.arena);
|
||||
|
||||
for (argument, name) in arguments.iter().zip(ARGUMENT_SYMBOLS[1..].iter()) {
|
||||
argument.set_name(name.as_str(&env.interns));
|
||||
}
|
||||
|
||||
let mut arguments_cast =
|
||||
bumpalo::collections::Vec::with_capacity_in(arguments.len(), env.arena);
|
||||
|
||||
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(env.target_info) {
|
||||
env.builder
|
||||
.build_pointer_cast(
|
||||
argument_ptr.into_pointer_value(),
|
||||
basic_type,
|
||||
"cast_ptr_to_tag_build_transform_caller_help",
|
||||
)
|
||||
.into()
|
||||
} else {
|
||||
let argument_cast = env
|
||||
.builder
|
||||
.build_bitcast(*argument_ptr, basic_type, "load_opaque_1")
|
||||
.into_pointer_value();
|
||||
|
||||
env.builder.build_load(argument_cast, "load_opaque_2")
|
||||
};
|
||||
|
||||
arguments_cast.push(argument);
|
||||
}
|
||||
|
||||
match closure_data_layout.runtime_representation() {
|
||||
Layout::Struct {
|
||||
field_layouts: &[], ..
|
||||
} => {
|
||||
// nothing to add
|
||||
}
|
||||
other => {
|
||||
let closure_type = basic_type_from_layout(env, &other).ptr_type(AddressSpace::Generic);
|
||||
|
||||
let closure_cast = env
|
||||
.builder
|
||||
.build_bitcast(closure_ptr, closure_type, "load_opaque")
|
||||
.into_pointer_value();
|
||||
|
||||
let closure_data = env.builder.build_load(closure_cast, "load_opaque");
|
||||
|
||||
arguments_cast.push(closure_data);
|
||||
}
|
||||
}
|
||||
|
||||
let result = crate::llvm::build::call_roc_function(
|
||||
env,
|
||||
roc_function,
|
||||
&result_layout,
|
||||
arguments_cast.as_slice(),
|
||||
);
|
||||
|
||||
let result_u8_ptr = function_value
|
||||
.get_nth_param(argument_layouts.len() as u32 + 1)
|
||||
.unwrap()
|
||||
.into_pointer_value();
|
||||
|
||||
crate::llvm::build::store_roc_value_opaque(env, result_layout, result_u8_ptr, result);
|
||||
env.builder.build_return(None);
|
||||
|
||||
env.builder.position_at_end(block);
|
||||
env.builder
|
||||
.set_current_debug_location(env.context, di_location);
|
||||
|
||||
function_value
|
||||
}
|
||||
|
||||
enum Mode {
|
||||
Inc,
|
||||
IncN,
|
||||
Dec,
|
||||
}
|
||||
|
||||
/// a function that accepts two arguments: the value to increment, and an amount to increment by
|
||||
pub fn build_inc_n_wrapper<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
layout: &Layout<'a>,
|
||||
) -> FunctionValue<'ctx> {
|
||||
build_rc_wrapper(env, layout_ids, layout, Mode::IncN)
|
||||
}
|
||||
|
||||
/// a function that accepts two arguments: the value to increment; increments by 1
|
||||
pub fn build_inc_wrapper<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
layout: &Layout<'a>,
|
||||
) -> FunctionValue<'ctx> {
|
||||
build_rc_wrapper(env, layout_ids, layout, Mode::Inc)
|
||||
}
|
||||
|
||||
pub fn build_dec_wrapper<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
layout: &Layout<'a>,
|
||||
) -> FunctionValue<'ctx> {
|
||||
build_rc_wrapper(env, layout_ids, layout, Mode::Dec)
|
||||
}
|
||||
|
||||
fn build_rc_wrapper<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
layout: &Layout<'a>,
|
||||
rc_operation: Mode,
|
||||
) -> 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_RC_REF;
|
||||
let fn_name = layout_ids
|
||||
.get(symbol, layout)
|
||||
.to_symbol_string(symbol, &env.interns);
|
||||
|
||||
let fn_name = match rc_operation {
|
||||
Mode::IncN => format!("{}_inc_n", fn_name),
|
||||
Mode::Inc => format!("{}_inc", fn_name),
|
||||
Mode::Dec => format!("{}_dec", fn_name),
|
||||
};
|
||||
|
||||
let function_value = match env.module.get_function(fn_name.as_str()) {
|
||||
Some(function_value) => function_value,
|
||||
None => {
|
||||
let arg_type = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
let function_value = match rc_operation {
|
||||
Mode::Inc | Mode::Dec => crate::llvm::refcounting::build_header_help(
|
||||
env,
|
||||
&fn_name,
|
||||
env.context.void_type().into(),
|
||||
&[arg_type.into()],
|
||||
),
|
||||
Mode::IncN => crate::llvm::refcounting::build_header_help(
|
||||
env,
|
||||
&fn_name,
|
||||
env.context.void_type().into(),
|
||||
&[arg_type.into(), env.ptr_int().into()],
|
||||
),
|
||||
};
|
||||
|
||||
// called from zig, must use C calling convention
|
||||
function_value.set_call_conventions(C_CALL_CONV);
|
||||
|
||||
let kind_id = Attribute::get_named_enum_kind_id("alwaysinline");
|
||||
debug_assert!(kind_id > 0);
|
||||
let attr = env.context.create_enum_attribute(kind_id, 1);
|
||||
function_value.add_attribute(AttributeLoc::Function, attr);
|
||||
|
||||
let entry = env.context.append_basic_block(function_value, "entry");
|
||||
env.builder.position_at_end(entry);
|
||||
|
||||
debug_info_init!(env, function_value);
|
||||
|
||||
let mut it = function_value.get_param_iter();
|
||||
let value_ptr = it.next().unwrap().into_pointer_value();
|
||||
|
||||
value_ptr.set_name(Symbol::ARG_1.as_str(&env.interns));
|
||||
|
||||
let value_type = basic_type_from_layout(env, layout).ptr_type(AddressSpace::Generic);
|
||||
|
||||
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()
|
||||
} else {
|
||||
let value_cast = env
|
||||
.builder
|
||||
.build_bitcast(value_ptr, value_type, "load_opaque")
|
||||
.into_pointer_value();
|
||||
|
||||
env.builder.build_load(value_cast, "load_opaque")
|
||||
};
|
||||
|
||||
match rc_operation {
|
||||
Mode::Inc => {
|
||||
let n = 1;
|
||||
increment_refcount_layout(env, function_value, layout_ids, n, value, layout);
|
||||
}
|
||||
Mode::IncN => {
|
||||
let n = it.next().unwrap().into_int_value();
|
||||
n.set_name(Symbol::ARG_2.as_str(&env.interns));
|
||||
|
||||
increment_n_refcount_layout(env, function_value, layout_ids, n, value, layout);
|
||||
}
|
||||
Mode::Dec => {
|
||||
decrement_refcount_layout(env, function_value, layout_ids, value, layout);
|
||||
}
|
||||
}
|
||||
|
||||
env.builder.build_return(None);
|
||||
|
||||
function_value
|
||||
}
|
||||
};
|
||||
|
||||
env.builder.position_at_end(block);
|
||||
env.builder
|
||||
.set_current_debug_location(env.context, di_location);
|
||||
|
||||
function_value
|
||||
}
|
||||
|
||||
pub fn build_eq_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_EQ_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 arg_type = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
let function_value = crate::llvm::refcounting::build_header_help(
|
||||
env,
|
||||
&fn_name,
|
||||
env.context.bool_type().into(),
|
||||
&[arg_type.into(), arg_type.into()],
|
||||
);
|
||||
|
||||
// called from zig, must use C calling convention
|
||||
function_value.set_call_conventions(C_CALL_CONV);
|
||||
|
||||
let kind_id = Attribute::get_named_enum_kind_id("alwaysinline");
|
||||
debug_assert!(kind_id > 0);
|
||||
let attr = env.context.create_enum_attribute(kind_id, 1);
|
||||
function_value.add_attribute(AttributeLoc::Function, attr);
|
||||
|
||||
let entry = env.context.append_basic_block(function_value, "entry");
|
||||
env.builder.position_at_end(entry);
|
||||
|
||||
debug_info_init!(env, function_value);
|
||||
|
||||
let mut it = function_value.get_param_iter();
|
||||
let value_ptr1 = it.next().unwrap().into_pointer_value();
|
||||
let value_ptr2 = it.next().unwrap().into_pointer_value();
|
||||
|
||||
value_ptr1.set_name(Symbol::ARG_1.as_str(&env.interns));
|
||||
value_ptr2.set_name(Symbol::ARG_2.as_str(&env.interns));
|
||||
|
||||
let value_type = basic_type_from_layout(env, layout).ptr_type(AddressSpace::Generic);
|
||||
|
||||
let value_cast1 = env
|
||||
.builder
|
||||
.build_bitcast(value_ptr1, value_type, "load_opaque")
|
||||
.into_pointer_value();
|
||||
|
||||
let value_cast2 = env
|
||||
.builder
|
||||
.build_bitcast(value_ptr2, value_type, "load_opaque")
|
||||
.into_pointer_value();
|
||||
|
||||
// load_roc_value(env, *element_layout, elem_ptr, "get_elem")
|
||||
let value1 = load_roc_value(env, *layout, value_cast1, "load_opaque");
|
||||
let value2 = load_roc_value(env, *layout, value_cast2, "load_opaque");
|
||||
|
||||
let result =
|
||||
crate::llvm::compare::generic_eq(env, layout_ids, value1, value2, layout, 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
|
||||
}
|
||||
|
||||
pub fn build_compare_wrapper<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
roc_function: FunctionValue<'ctx>,
|
||||
closure_data_layout: LambdaSet<'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 fn_name: &str = &format!(
|
||||
"{}_compare_wrapper",
|
||||
roc_function.get_name().to_string_lossy()
|
||||
);
|
||||
|
||||
let function_value = match env.module.get_function(fn_name) {
|
||||
Some(function_value) => function_value,
|
||||
None => {
|
||||
let arg_type = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
let function_value = crate::llvm::refcounting::build_header_help(
|
||||
env,
|
||||
fn_name,
|
||||
env.context.i8_type().into(),
|
||||
&[arg_type.into(), arg_type.into(), arg_type.into()],
|
||||
);
|
||||
|
||||
// called from zig, must use C calling convention
|
||||
function_value.set_call_conventions(C_CALL_CONV);
|
||||
|
||||
// we expose this function to zig; must use c calling convention
|
||||
function_value.set_call_conventions(C_CALL_CONV);
|
||||
|
||||
let kind_id = Attribute::get_named_enum_kind_id("alwaysinline");
|
||||
debug_assert!(kind_id > 0);
|
||||
let attr = env.context.create_enum_attribute(kind_id, 1);
|
||||
function_value.add_attribute(AttributeLoc::Function, attr);
|
||||
|
||||
let entry = env.context.append_basic_block(function_value, "entry");
|
||||
env.builder.position_at_end(entry);
|
||||
|
||||
debug_info_init!(env, function_value);
|
||||
|
||||
let mut it = function_value.get_param_iter();
|
||||
let closure_ptr = it.next().unwrap().into_pointer_value();
|
||||
let value_ptr1 = it.next().unwrap().into_pointer_value();
|
||||
let value_ptr2 = it.next().unwrap().into_pointer_value();
|
||||
|
||||
closure_ptr.set_name(Symbol::ARG_1.as_str(&env.interns));
|
||||
value_ptr1.set_name(Symbol::ARG_2.as_str(&env.interns));
|
||||
value_ptr2.set_name(Symbol::ARG_3.as_str(&env.interns));
|
||||
|
||||
let value_type = basic_type_from_layout(env, layout);
|
||||
let value_ptr_type = value_type.ptr_type(AddressSpace::Generic);
|
||||
|
||||
let value_cast1 = env
|
||||
.builder
|
||||
.build_bitcast(value_ptr1, value_ptr_type, "load_opaque")
|
||||
.into_pointer_value();
|
||||
|
||||
let value_cast2 = env
|
||||
.builder
|
||||
.build_bitcast(value_ptr2, value_ptr_type, "load_opaque")
|
||||
.into_pointer_value();
|
||||
|
||||
let value1 = env.builder.build_load(value_cast1, "load_opaque");
|
||||
let value2 = env.builder.build_load(value_cast2, "load_opaque");
|
||||
|
||||
let default = [value1.into(), value2.into()];
|
||||
|
||||
let arguments_cast = match closure_data_layout.runtime_representation() {
|
||||
Layout::Struct {
|
||||
field_layouts: &[], ..
|
||||
} => {
|
||||
// nothing to add
|
||||
&default
|
||||
}
|
||||
other => {
|
||||
let closure_type =
|
||||
basic_type_from_layout(env, &other).ptr_type(AddressSpace::Generic);
|
||||
|
||||
let closure_cast = env
|
||||
.builder
|
||||
.build_bitcast(closure_ptr, closure_type, "load_opaque")
|
||||
.into_pointer_value();
|
||||
|
||||
let closure_data = env.builder.build_load(closure_cast, "load_opaque");
|
||||
|
||||
env.arena
|
||||
.alloc([value1.into(), value2.into(), closure_data.into()])
|
||||
as &[_]
|
||||
}
|
||||
};
|
||||
|
||||
let call = env.builder.build_call(
|
||||
roc_function,
|
||||
arguments_cast,
|
||||
"call_user_defined_compare_function",
|
||||
);
|
||||
|
||||
let result = call.try_as_basic_value().left().unwrap();
|
||||
|
||||
// IMPORTANT! we call a user function, so it has the fast calling convention
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
|
||||
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
|
||||
}
|
7531
crates/compiler/gen_llvm/src/llvm/build.rs
Normal file
7531
crates/compiler/gen_llvm/src/llvm/build.rs
Normal file
File diff suppressed because it is too large
Load diff
839
crates/compiler/gen_llvm/src/llvm/build_dict.rs
Normal file
839
crates/compiler/gen_llvm/src/llvm/build_dict.rs
Normal file
|
@ -0,0 +1,839 @@
|
|||
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_roc_value, 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, zig_dict_type};
|
||||
use crate::llvm::refcounting::Mode;
|
||||
use inkwell::attributes::{Attribute, AttributeLoc};
|
||||
use inkwell::context::Context;
|
||||
use inkwell::types::BasicType;
|
||||
use inkwell::values::{BasicValue, BasicValueEnum, FunctionValue, IntValue, StructValue};
|
||||
use inkwell::AddressSpace;
|
||||
use roc_builtins::bitcode;
|
||||
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);
|
||||
|
||||
impl Alignment {
|
||||
fn from_key_value_layout(key: &Layout, value: &Layout, target_info: TargetInfo) -> Alignment {
|
||||
let key_align = key.alignment_bytes(target_info);
|
||||
let value_align = value.alignment_bytes(target_info);
|
||||
|
||||
let mut bits = key_align.max(value_align) as u8;
|
||||
|
||||
// alignment must be a power of 2
|
||||
debug_assert!(bits.is_power_of_two());
|
||||
|
||||
let value_before_key_flag = 0b1000_0000;
|
||||
|
||||
if key_align < value_align {
|
||||
bits |= value_before_key_flag;
|
||||
}
|
||||
|
||||
Alignment(bits)
|
||||
}
|
||||
|
||||
fn as_int_value<'ctx>(&self, context: &'ctx Context) -> IntValue<'ctx> {
|
||||
context.i8_type().const_int(self.0 as u64, false)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dict_len<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
dict_symbol: Symbol,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
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 length_i64 = call_bitcode_fn(
|
||||
env,
|
||||
&[pass_dict_c_abi(env, dict_as_zig_dict.into())],
|
||||
bitcode::DICT_LEN,
|
||||
);
|
||||
|
||||
env.builder
|
||||
.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),
|
||||
}
|
||||
}
|
||||
|
||||
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 u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
let key_type = basic_type_from_layout(env, key_layout);
|
||||
let value_type = basic_type_from_layout(env, value_layout);
|
||||
|
||||
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()
|
||||
.const_int(key_layout.stack_size(env.target_info) as u64, false);
|
||||
|
||||
let value_width = env
|
||||
.ptr_int()
|
||||
.const_int(value_layout.stack_size(env.target_info) as u64, false);
|
||||
|
||||
let result_ptr = builder.build_alloca(zig_dict_type(env), "result_ptr");
|
||||
|
||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info);
|
||||
let alignment_iv = alignment.as_int_value(env.context);
|
||||
|
||||
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,
|
||||
&[
|
||||
pass_dict_c_abi(env, dict),
|
||||
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 u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
let key_ptr = builder.build_alloca(key.get_type(), "key_ptr");
|
||||
|
||||
env.builder.build_store(key_ptr, key);
|
||||
|
||||
let key_width = env
|
||||
.ptr_int()
|
||||
.const_int(key_layout.stack_size(env.target_info) as u64, false);
|
||||
|
||||
let value_width = env
|
||||
.ptr_int()
|
||||
.const_int(value_layout.stack_size(env.target_info) as u64, false);
|
||||
|
||||
let result_ptr = builder.build_alloca(zig_dict_type(env), "result_ptr");
|
||||
|
||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info);
|
||||
let alignment_iv = alignment.as_int_value(env.context);
|
||||
|
||||
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,
|
||||
&[
|
||||
pass_dict_c_abi(env, dict),
|
||||
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 u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
let key_ptr = builder.build_alloca(key.get_type(), "key_ptr");
|
||||
|
||||
env.builder.build_store(key_ptr, key);
|
||||
|
||||
let key_width = env
|
||||
.ptr_int()
|
||||
.const_int(key_layout.stack_size(env.target_info) as u64, false);
|
||||
|
||||
let value_width = env
|
||||
.ptr_int()
|
||||
.const_int(value_layout.stack_size(env.target_info) as u64, false);
|
||||
|
||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info);
|
||||
let alignment_iv = alignment.as_int_value(env.context);
|
||||
|
||||
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,
|
||||
&[
|
||||
pass_dict_c_abi(env, dict),
|
||||
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 u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
let key_ptr = builder.build_alloca(key.get_type(), "key_ptr");
|
||||
|
||||
env.builder.build_store(key_ptr, key);
|
||||
|
||||
let key_width = env
|
||||
.ptr_int()
|
||||
.const_int(key_layout.stack_size(env.target_info) as u64, false);
|
||||
|
||||
let value_width = env
|
||||
.ptr_int()
|
||||
.const_int(value_layout.stack_size(env.target_info) as u64, false);
|
||||
|
||||
let value_bt = basic_type_from_layout(env, value_layout);
|
||||
|
||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info);
|
||||
let alignment_iv = alignment.as_int_value(env.context);
|
||||
|
||||
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,
|
||||
&[
|
||||
pass_dict_c_abi(env, dict),
|
||||
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_u8 = env
|
||||
.builder
|
||||
.build_extract_value(result, 1, "get_flag")
|
||||
.unwrap()
|
||||
.into_int_value();
|
||||
|
||||
let flag = env
|
||||
.builder
|
||||
.build_int_cast(flag_u8, env.context.bool_type(), "to_bool");
|
||||
|
||||
let value_u8_ptr_int = env
|
||||
.builder
|
||||
.build_extract_value(result, 0, "get_value_ptr_int")
|
||||
.unwrap()
|
||||
.into_int_value();
|
||||
|
||||
let ptr_type = value_bt.ptr_type(AddressSpace::Generic);
|
||||
let value_u8_ptr = env
|
||||
.builder
|
||||
.build_int_to_ptr(value_u8_ptr_int, ptr_type, "opaque_value_ptr");
|
||||
|
||||
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 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 key_width = env
|
||||
.ptr_int()
|
||||
.const_int(key_layout.stack_size(env.target_info) as u64, false);
|
||||
|
||||
let value_width = env
|
||||
.ptr_int()
|
||||
.const_int(value_layout.stack_size(env.target_info) as u64, false);
|
||||
|
||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info);
|
||||
let alignment_iv = alignment.as_int_value(env.context);
|
||||
|
||||
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,
|
||||
&[
|
||||
pass_dict_c_abi(env, dict),
|
||||
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 key_width = env
|
||||
.ptr_int()
|
||||
.const_int(key_layout.stack_size(env.target_info) as u64, false);
|
||||
|
||||
let value_width = env
|
||||
.ptr_int()
|
||||
.const_int(value_layout.stack_size(env.target_info) as u64, false);
|
||||
|
||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info);
|
||||
let alignment_iv = alignment.as_int_value(env.context);
|
||||
|
||||
let inc_key_fn = build_inc_wrapper(env, layout_ids, key_layout);
|
||||
|
||||
call_list_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
pass_dict_c_abi(env, dict),
|
||||
alignment_iv.into(),
|
||||
key_width.into(),
|
||||
value_width.into(),
|
||||
inc_key_fn.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
bitcode::DICT_KEYS,
|
||||
)
|
||||
}
|
||||
|
||||
fn pass_dict_c_abi<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
dict: BasicValueEnum<'ctx>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
match env.target_info.ptr_width() {
|
||||
roc_target::PtrWidth::Bytes4 => {
|
||||
let target_type = env.context.custom_width_int_type(96).into();
|
||||
|
||||
complex_bitcast(env.builder, dict, target_type, "to_i96")
|
||||
}
|
||||
roc_target::PtrWidth::Bytes8 => {
|
||||
let dict_ptr = env.builder.build_alloca(zig_dict_type(env), "dict_ptr");
|
||||
env.builder.build_store(dict_ptr, dict);
|
||||
|
||||
dict_ptr.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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 key_width = env
|
||||
.ptr_int()
|
||||
.const_int(key_layout.stack_size(env.target_info) as u64, false);
|
||||
|
||||
let value_width = env
|
||||
.ptr_int()
|
||||
.const_int(value_layout.stack_size(env.target_info) as u64, false);
|
||||
|
||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info);
|
||||
let alignment_iv = alignment.as_int_value(env.context);
|
||||
|
||||
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(env), "output_ptr");
|
||||
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
pass_dict_c_abi(env, dict1),
|
||||
pass_dict_c_abi(env, dict2),
|
||||
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 key_width = env
|
||||
.ptr_int()
|
||||
.const_int(key_layout.stack_size(env.target_info) as u64, false);
|
||||
|
||||
let value_width = env
|
||||
.ptr_int()
|
||||
.const_int(value_layout.stack_size(env.target_info) as u64, false);
|
||||
|
||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info);
|
||||
let alignment_iv = alignment.as_int_value(env.context);
|
||||
|
||||
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,
|
||||
&[
|
||||
pass_dict_c_abi(env, dict1),
|
||||
pass_dict_c_abi(env, dict2),
|
||||
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 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.target_info);
|
||||
let alignment_iv = alignment.as_int_value(env.context);
|
||||
|
||||
let output_ptr = builder.build_alloca(accum_bt, "output_ptr");
|
||||
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
pass_dict_c_abi(env, dict),
|
||||
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 key_width = env
|
||||
.ptr_int()
|
||||
.const_int(key_layout.stack_size(env.target_info) as u64, false);
|
||||
|
||||
let value_width = env
|
||||
.ptr_int()
|
||||
.const_int(value_layout.stack_size(env.target_info) as u64, false);
|
||||
|
||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info);
|
||||
let alignment_iv = alignment.as_int_value(env.context);
|
||||
|
||||
let inc_value_fn = build_inc_wrapper(env, layout_ids, value_layout);
|
||||
|
||||
call_list_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
pass_dict_c_abi(env, dict),
|
||||
alignment_iv.into(),
|
||||
key_width.into(),
|
||||
value_width.into(),
|
||||
inc_value_fn.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
bitcode::DICT_VALUES,
|
||||
)
|
||||
}
|
||||
|
||||
#[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 key_width = env
|
||||
.ptr_int()
|
||||
.const_int(key_layout.stack_size(env.target_info) 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::UNIT, env.target_info);
|
||||
let alignment_iv = alignment.as_int_value(env.context);
|
||||
|
||||
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,
|
||||
&[
|
||||
list_to_c_abi(env, list).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(),
|
||||
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.as_str(&env.interns));
|
||||
value_ptr.set_name(Symbol::ARG_2.as_str(&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, "cast_to_known_type")
|
||||
.into_pointer_value();
|
||||
|
||||
let val_arg = load_roc_value(env, *layout, 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);
|
||||
|
||||
complex_bitcast(
|
||||
env.builder,
|
||||
dict,
|
||||
crate::llvm::convert::zig_dict_type(env).into(),
|
||||
"dict_to_zig_dict",
|
||||
)
|
||||
.into_struct_value()
|
||||
}
|
||||
|
||||
pub fn decref<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
wrapper_struct: StructValue<'ctx>,
|
||||
alignment: u32,
|
||||
) {
|
||||
let pointer = env
|
||||
.builder
|
||||
.build_extract_value(wrapper_struct, Builtin::WRAPPER_PTR, "read_list_ptr")
|
||||
.unwrap()
|
||||
.into_pointer_value();
|
||||
|
||||
crate::llvm::refcounting::decref_pointer_check_null(env, pointer, alignment);
|
||||
}
|
874
crates/compiler/gen_llvm/src/llvm/build_hash.rs
Normal file
874
crates/compiler/gen_llvm/src/llvm/build_hash.rs
Normal file
|
@ -0,0 +1,874 @@
|
|||
use crate::debug_info_init;
|
||||
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::convert::basic_type_from_layout;
|
||||
use bumpalo::collections::Vec;
|
||||
use inkwell::values::{
|
||||
BasicValue, BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue,
|
||||
};
|
||||
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,
|
||||
Loop(UnionLayout<'a>),
|
||||
}
|
||||
|
||||
pub fn generic_hash<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
seed: IntValue<'ctx>,
|
||||
val: BasicValueEnum<'ctx>,
|
||||
layout: &Layout<'a>,
|
||||
) -> IntValue<'ctx> {
|
||||
// NOTE: C and Zig use this value for their initial HashMap seed: 0xc70f6907
|
||||
build_hash_layout(
|
||||
env,
|
||||
layout_ids,
|
||||
seed,
|
||||
val,
|
||||
layout,
|
||||
WhenRecursive::Unreachable,
|
||||
)
|
||||
}
|
||||
|
||||
fn build_hash_layout<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
seed: IntValue<'ctx>,
|
||||
val: BasicValueEnum<'ctx>,
|
||||
layout: &Layout<'a>,
|
||||
when_recursive: WhenRecursive<'a>,
|
||||
) -> IntValue<'ctx> {
|
||||
match layout {
|
||||
Layout::Builtin(builtin) => {
|
||||
hash_builtin(env, layout_ids, seed, val, layout, builtin, when_recursive)
|
||||
}
|
||||
|
||||
Layout::Struct { field_layouts, .. } => build_hash_struct(
|
||||
env,
|
||||
layout_ids,
|
||||
field_layouts,
|
||||
when_recursive,
|
||||
seed,
|
||||
val.into_struct_value(),
|
||||
),
|
||||
|
||||
Layout::LambdaSet(lambda_set) => build_hash_layout(
|
||||
env,
|
||||
layout_ids,
|
||||
seed,
|
||||
val,
|
||||
&lambda_set.runtime_representation(),
|
||||
when_recursive,
|
||||
),
|
||||
|
||||
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 {
|
||||
WhenRecursive::Unreachable => {
|
||||
unreachable!("recursion pointers should never be hashed directly")
|
||||
}
|
||||
WhenRecursive::Loop(union_layout) => {
|
||||
let layout = Layout::Union(union_layout);
|
||||
|
||||
let bt = basic_type_from_layout(env, &layout);
|
||||
|
||||
// cast the i64 pointer to a pointer to block of memory
|
||||
let field_cast = env
|
||||
.builder
|
||||
.build_bitcast(val, bt, "i64_to_opaque")
|
||||
.into_pointer_value();
|
||||
|
||||
build_hash_tag(env, layout_ids, &union_layout, seed, field_cast.into())
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn append_hash_layout<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
seed: IntValue<'ctx>,
|
||||
val: BasicValueEnum<'ctx>,
|
||||
layout: &Layout<'a>,
|
||||
when_recursive: WhenRecursive<'a>,
|
||||
) -> IntValue<'ctx> {
|
||||
build_hash_layout(env, layout_ids, seed, val, layout, when_recursive)
|
||||
}
|
||||
|
||||
fn hash_builtin<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
seed: IntValue<'ctx>,
|
||||
val: BasicValueEnum<'ctx>,
|
||||
layout: &Layout<'a>,
|
||||
builtin: &Builtin<'a>,
|
||||
when_recursive: WhenRecursive<'a>,
|
||||
) -> IntValue<'ctx> {
|
||||
let ptr_bytes = env.target_info;
|
||||
|
||||
match builtin {
|
||||
Builtin::Int(_) | Builtin::Float(_) | Builtin::Bool | Builtin::Decimal => {
|
||||
let hash_bytes = store_and_use_as_u8_ptr(env, val, layout);
|
||||
hash_bitcode_fn(env, seed, hash_bytes, layout.stack_size(ptr_bytes))
|
||||
}
|
||||
Builtin::Str => {
|
||||
// let zig deal with big vs small string
|
||||
call_bitcode_fn(env, &[seed.into(), val], bitcode::DICT_HASH_STR).into_int_value()
|
||||
}
|
||||
|
||||
Builtin::Dict(_, _) => {
|
||||
todo!("Implement hash for Dict")
|
||||
}
|
||||
Builtin::Set(_) => {
|
||||
todo!("Implement Hash for Set")
|
||||
}
|
||||
Builtin::List(element_layout) => build_hash_list(
|
||||
env,
|
||||
layout_ids,
|
||||
layout,
|
||||
element_layout,
|
||||
when_recursive,
|
||||
seed,
|
||||
val.into_struct_value(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_hash_struct<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
field_layouts: &'a [Layout<'a>],
|
||||
when_recursive: WhenRecursive<'a>,
|
||||
seed: IntValue<'ctx>,
|
||||
value: StructValue<'ctx>,
|
||||
) -> IntValue<'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 struct_layout = Layout::struct_no_name_order(field_layouts);
|
||||
|
||||
let symbol = Symbol::GENERIC_HASH;
|
||||
let fn_name = layout_ids
|
||||
.get(symbol, &struct_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 seed_type = env.context.i64_type();
|
||||
|
||||
let arg_type = basic_type_from_layout(env, &struct_layout);
|
||||
|
||||
let function_value = crate::llvm::refcounting::build_header_help(
|
||||
env,
|
||||
&fn_name,
|
||||
seed_type.into(),
|
||||
&[seed_type.into(), arg_type],
|
||||
);
|
||||
|
||||
build_hash_struct_help(
|
||||
env,
|
||||
layout_ids,
|
||||
function_value,
|
||||
when_recursive,
|
||||
field_layouts,
|
||||
);
|
||||
|
||||
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, &[seed.into(), value.into()], "struct_hash");
|
||||
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
|
||||
call.try_as_basic_value().left().unwrap().into_int_value()
|
||||
}
|
||||
|
||||
fn build_hash_struct_help<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
when_recursive: WhenRecursive<'a>,
|
||||
field_layouts: &[Layout<'a>],
|
||||
) {
|
||||
let ctx = env.context;
|
||||
|
||||
debug_info_init!(env, parent);
|
||||
|
||||
// Add args to scope
|
||||
let mut it = parent.get_param_iter();
|
||||
let seed = it.next().unwrap().into_int_value();
|
||||
let value = it.next().unwrap().into_struct_value();
|
||||
|
||||
seed.set_name(Symbol::ARG_1.as_str(&env.interns));
|
||||
value.set_name(Symbol::ARG_2.as_str(&env.interns));
|
||||
|
||||
let entry = ctx.append_basic_block(parent, "entry");
|
||||
env.builder.position_at_end(entry);
|
||||
|
||||
let result = hash_struct(env, layout_ids, seed, value, when_recursive, field_layouts);
|
||||
|
||||
env.builder.build_return(Some(&result));
|
||||
}
|
||||
|
||||
fn hash_struct<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
mut seed: IntValue<'ctx>,
|
||||
value: StructValue<'ctx>,
|
||||
when_recursive: WhenRecursive<'a>,
|
||||
field_layouts: &[Layout<'a>],
|
||||
) -> IntValue<'ctx> {
|
||||
let ptr_bytes = env.target_info;
|
||||
|
||||
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`
|
||||
// contains garbage bits after the tag (currently)
|
||||
if false {
|
||||
// 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, value.into(), &layout);
|
||||
hash_bitcode_fn(env, seed, hash_bytes, layout.stack_size(ptr_bytes))
|
||||
} else {
|
||||
for (index, field_layout) in field_layouts.iter().enumerate() {
|
||||
let field = env
|
||||
.builder
|
||||
.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 => {
|
||||
unreachable!("The current layout should not be recursive, but is")
|
||||
}
|
||||
WhenRecursive::Loop(union_layout) => {
|
||||
let field_layout = Layout::Union(*union_layout);
|
||||
|
||||
let bt = basic_type_from_layout(env, &field_layout);
|
||||
|
||||
// cast the i64 pointer to a pointer to block of memory
|
||||
let field_cast = env
|
||||
.builder
|
||||
.build_bitcast(field, bt, "i64_to_opaque")
|
||||
.into_pointer_value();
|
||||
|
||||
seed = append_hash_layout(
|
||||
env,
|
||||
layout_ids,
|
||||
seed,
|
||||
field_cast.into(),
|
||||
&field_layout,
|
||||
when_recursive.clone(),
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
seed = append_hash_layout(
|
||||
env,
|
||||
layout_ids,
|
||||
seed,
|
||||
field,
|
||||
field_layout,
|
||||
when_recursive.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
seed
|
||||
}
|
||||
}
|
||||
|
||||
fn build_hash_tag<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
union_layout: &UnionLayout<'a>,
|
||||
seed: IntValue<'ctx>,
|
||||
value: BasicValueEnum<'ctx>,
|
||||
) -> IntValue<'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;
|
||||
let fn_name = layout_ids
|
||||
.get(symbol, &Layout::Union(*union_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 seed_type = env.context.i64_type();
|
||||
|
||||
let arg_type = argument_type_from_union_layout(env, union_layout);
|
||||
|
||||
let function_value = crate::llvm::refcounting::build_header_help(
|
||||
env,
|
||||
&fn_name,
|
||||
seed_type.into(),
|
||||
&[seed_type.into(), arg_type],
|
||||
);
|
||||
|
||||
build_hash_tag_help(env, layout_ids, function_value, union_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, &[seed.into(), value.into()], "struct_hash");
|
||||
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
|
||||
call.try_as_basic_value().left().unwrap().into_int_value()
|
||||
}
|
||||
|
||||
fn build_hash_tag_help<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
union_layout: &UnionLayout<'a>,
|
||||
) {
|
||||
let ctx = env.context;
|
||||
|
||||
debug_info_init!(env, parent);
|
||||
|
||||
// Add args to scope
|
||||
let mut it = parent.get_param_iter();
|
||||
let seed = it.next().unwrap().into_int_value();
|
||||
let value = it.next().unwrap();
|
||||
|
||||
seed.set_name(Symbol::ARG_1.as_str(&env.interns));
|
||||
value.set_name(Symbol::ARG_2.as_str(&env.interns));
|
||||
|
||||
let entry = ctx.append_basic_block(parent, "entry");
|
||||
env.builder.position_at_end(entry);
|
||||
|
||||
let result = hash_tag(env, layout_ids, parent, seed, value, union_layout);
|
||||
|
||||
env.builder.build_return(Some(&result));
|
||||
}
|
||||
|
||||
fn hash_tag<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
seed: IntValue<'ctx>,
|
||||
tag: BasicValueEnum<'ctx>,
|
||||
union_layout: &UnionLayout<'a>,
|
||||
) -> IntValue<'ctx> {
|
||||
use UnionLayout::*;
|
||||
|
||||
let entry_block = env.builder.get_insert_block().unwrap();
|
||||
|
||||
let merge_block = env.context.append_basic_block(parent, "merge_block");
|
||||
env.builder.position_at_end(merge_block);
|
||||
|
||||
let tag_id_layout = union_layout.tag_id_layout();
|
||||
let tag_id_basic_type = basic_type_from_layout(env, &tag_id_layout);
|
||||
|
||||
let merge_phi = env.builder.build_phi(seed.get_type(), "merge_hash");
|
||||
|
||||
env.builder.position_at_end(entry_block);
|
||||
match union_layout {
|
||||
NonRecursive(tags) => {
|
||||
let current_tag_id = get_tag_id(env, parent, union_layout, tag);
|
||||
|
||||
let mut cases = Vec::with_capacity_in(tags.len(), env.arena);
|
||||
|
||||
for (tag_id, field_layouts) in tags.iter().enumerate() {
|
||||
let block = env.context.append_basic_block(parent, "tag_id_modify");
|
||||
env.builder.position_at_end(block);
|
||||
|
||||
// hash the tag id
|
||||
let hash_bytes = store_and_use_as_u8_ptr(
|
||||
env,
|
||||
tag_id_basic_type
|
||||
.into_int_type()
|
||||
.const_int(tag_id as u64, false)
|
||||
.into(),
|
||||
&tag_id_layout,
|
||||
);
|
||||
let seed = hash_bitcode_fn(
|
||||
env,
|
||||
seed,
|
||||
hash_bytes,
|
||||
tag_id_layout.stack_size(env.target_info),
|
||||
);
|
||||
|
||||
// hash the tag data
|
||||
let tag = tag.into_pointer_value();
|
||||
let answer =
|
||||
hash_ptr_to_struct(env, layout_ids, union_layout, field_layouts, seed, tag);
|
||||
|
||||
merge_phi.add_incoming(&[(&answer, block)]);
|
||||
env.builder.build_unconditional_branch(merge_block);
|
||||
|
||||
cases.push((
|
||||
current_tag_id.get_type().const_int(tag_id as u64, false),
|
||||
block,
|
||||
));
|
||||
}
|
||||
|
||||
env.builder.position_at_end(entry_block);
|
||||
|
||||
match cases.pop() {
|
||||
Some((_, default)) => {
|
||||
env.builder.build_switch(current_tag_id, default, &cases);
|
||||
}
|
||||
None => {
|
||||
// we're hashing empty tag unions; this code is effectively unreachable
|
||||
env.builder.build_unreachable();
|
||||
}
|
||||
}
|
||||
}
|
||||
Recursive(tags) => {
|
||||
let current_tag_id = get_tag_id(env, parent, union_layout, tag);
|
||||
|
||||
let mut cases = Vec::with_capacity_in(tags.len(), env.arena);
|
||||
|
||||
for (tag_id, field_layouts) in tags.iter().enumerate() {
|
||||
let block = env.context.append_basic_block(parent, "tag_id_modify");
|
||||
env.builder.position_at_end(block);
|
||||
|
||||
// hash the tag id
|
||||
let hash_bytes = store_and_use_as_u8_ptr(
|
||||
env,
|
||||
tag_id_basic_type
|
||||
.into_int_type()
|
||||
.const_int(tag_id as u64, false)
|
||||
.into(),
|
||||
&tag_id_layout,
|
||||
);
|
||||
let seed = hash_bitcode_fn(
|
||||
env,
|
||||
seed,
|
||||
hash_bytes,
|
||||
tag_id_layout.stack_size(env.target_info),
|
||||
);
|
||||
|
||||
// hash the tag data
|
||||
let tag = tag_pointer_clear_tag_id(env, tag.into_pointer_value());
|
||||
let answer =
|
||||
hash_ptr_to_struct(env, layout_ids, union_layout, field_layouts, seed, tag);
|
||||
|
||||
merge_phi.add_incoming(&[(&answer, block)]);
|
||||
env.builder.build_unconditional_branch(merge_block);
|
||||
|
||||
cases.push((
|
||||
current_tag_id.get_type().const_int(tag_id as u64, false),
|
||||
block,
|
||||
));
|
||||
}
|
||||
|
||||
env.builder.position_at_end(entry_block);
|
||||
|
||||
let default = cases.pop().unwrap().1;
|
||||
|
||||
env.builder.build_switch(current_tag_id, default, &cases);
|
||||
}
|
||||
NullableUnwrapped { other_fields, .. } => {
|
||||
let tag = tag.into_pointer_value();
|
||||
|
||||
let is_null = env.builder.build_is_null(tag, "is_null");
|
||||
|
||||
let hash_null_block = env.context.append_basic_block(parent, "hash_null_block");
|
||||
let hash_other_block = env.context.append_basic_block(parent, "hash_other_block");
|
||||
|
||||
env.builder
|
||||
.build_conditional_branch(is_null, hash_null_block, hash_other_block);
|
||||
|
||||
{
|
||||
env.builder.position_at_end(hash_null_block);
|
||||
|
||||
let answer = hash_null(seed);
|
||||
|
||||
merge_phi.add_incoming(&[(&answer, hash_null_block)]);
|
||||
env.builder.build_unconditional_branch(merge_block);
|
||||
}
|
||||
|
||||
{
|
||||
env.builder.position_at_end(hash_other_block);
|
||||
|
||||
let answer =
|
||||
hash_ptr_to_struct(env, layout_ids, union_layout, other_fields, seed, tag);
|
||||
|
||||
merge_phi.add_incoming(&[(&answer, hash_other_block)]);
|
||||
env.builder.build_unconditional_branch(merge_block);
|
||||
}
|
||||
}
|
||||
NullableWrapped {
|
||||
other_tags,
|
||||
nullable_id,
|
||||
} => {
|
||||
let tag = tag.into_pointer_value();
|
||||
|
||||
let is_null = env.builder.build_is_null(tag, "is_null");
|
||||
|
||||
let hash_null_block = env.context.append_basic_block(parent, "hash_null_block");
|
||||
let hash_other_block = env.context.append_basic_block(parent, "hash_other_block");
|
||||
|
||||
env.builder
|
||||
.build_conditional_branch(is_null, hash_null_block, hash_other_block);
|
||||
|
||||
{
|
||||
env.builder.position_at_end(hash_null_block);
|
||||
|
||||
let answer = hash_null(seed);
|
||||
|
||||
merge_phi.add_incoming(&[(&answer, hash_null_block)]);
|
||||
env.builder.build_unconditional_branch(merge_block);
|
||||
}
|
||||
|
||||
{
|
||||
let mut cases = Vec::with_capacity_in(other_tags.len(), env.arena);
|
||||
|
||||
for (mut tag_id, field_layouts) in other_tags.iter().enumerate() {
|
||||
if tag_id >= *nullable_id as usize {
|
||||
tag_id += 1;
|
||||
}
|
||||
|
||||
let block = env.context.append_basic_block(parent, "tag_id_modify");
|
||||
env.builder.position_at_end(block);
|
||||
|
||||
// hash the tag id
|
||||
let hash_bytes = store_and_use_as_u8_ptr(
|
||||
env,
|
||||
tag_id_basic_type
|
||||
.into_int_type()
|
||||
.const_int(tag_id as u64, false)
|
||||
.into(),
|
||||
&tag_id_layout,
|
||||
);
|
||||
let seed1 = hash_bitcode_fn(
|
||||
env,
|
||||
seed,
|
||||
hash_bytes,
|
||||
tag_id_layout.stack_size(env.target_info),
|
||||
);
|
||||
|
||||
// hash tag data
|
||||
let tag = tag_pointer_clear_tag_id(env, tag);
|
||||
let answer = hash_ptr_to_struct(
|
||||
env,
|
||||
layout_ids,
|
||||
union_layout,
|
||||
field_layouts,
|
||||
seed1,
|
||||
tag,
|
||||
);
|
||||
|
||||
merge_phi.add_incoming(&[(&answer, block)]);
|
||||
env.builder.build_unconditional_branch(merge_block);
|
||||
|
||||
cases.push((
|
||||
tag_id_basic_type
|
||||
.into_int_type()
|
||||
.const_int(tag_id as u64, false),
|
||||
block,
|
||||
));
|
||||
}
|
||||
|
||||
env.builder.position_at_end(hash_other_block);
|
||||
|
||||
let tag_id = get_tag_id(env, parent, union_layout, tag.into());
|
||||
|
||||
let default = cases.pop().unwrap().1;
|
||||
|
||||
env.builder.build_switch(tag_id, default, &cases);
|
||||
}
|
||||
}
|
||||
NonNullableUnwrapped(field_layouts) => {
|
||||
let answer = hash_ptr_to_struct(
|
||||
env,
|
||||
layout_ids,
|
||||
union_layout,
|
||||
field_layouts,
|
||||
seed,
|
||||
tag.into_pointer_value(),
|
||||
);
|
||||
|
||||
merge_phi.add_incoming(&[(&answer, entry_block)]);
|
||||
env.builder.build_unconditional_branch(merge_block);
|
||||
}
|
||||
}
|
||||
|
||||
env.builder.position_at_end(merge_block);
|
||||
|
||||
merge_phi.as_basic_value().into_int_value()
|
||||
}
|
||||
|
||||
fn build_hash_list<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
layout: &Layout<'a>,
|
||||
element_layout: &Layout<'a>,
|
||||
when_recursive: WhenRecursive<'a>,
|
||||
seed: IntValue<'ctx>,
|
||||
value: StructValue<'ctx>,
|
||||
) -> IntValue<'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;
|
||||
let fn_name = layout_ids
|
||||
.get(symbol, 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 seed_type = env.context.i64_type();
|
||||
|
||||
let arg_type = basic_type_from_layout(env, layout);
|
||||
|
||||
let function_value = crate::llvm::refcounting::build_header_help(
|
||||
env,
|
||||
&fn_name,
|
||||
seed_type.into(),
|
||||
&[seed_type.into(), arg_type],
|
||||
);
|
||||
|
||||
build_hash_list_help(
|
||||
env,
|
||||
layout_ids,
|
||||
function_value,
|
||||
when_recursive,
|
||||
element_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, &[seed.into(), value.into()], "struct_hash");
|
||||
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
|
||||
call.try_as_basic_value().left().unwrap().into_int_value()
|
||||
}
|
||||
|
||||
fn build_hash_list_help<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
when_recursive: WhenRecursive<'a>,
|
||||
element_layout: &Layout<'a>,
|
||||
) {
|
||||
let ctx = env.context;
|
||||
|
||||
debug_info_init!(env, parent);
|
||||
|
||||
// Add args to scope
|
||||
let mut it = parent.get_param_iter();
|
||||
let seed = it.next().unwrap().into_int_value();
|
||||
let value = it.next().unwrap().into_struct_value();
|
||||
|
||||
seed.set_name(Symbol::ARG_1.as_str(&env.interns));
|
||||
value.set_name(Symbol::ARG_2.as_str(&env.interns));
|
||||
|
||||
let entry = ctx.append_basic_block(parent, "entry");
|
||||
env.builder.position_at_end(entry);
|
||||
|
||||
let result = hash_list(
|
||||
env,
|
||||
layout_ids,
|
||||
parent,
|
||||
seed,
|
||||
value,
|
||||
when_recursive,
|
||||
element_layout,
|
||||
);
|
||||
|
||||
env.builder.build_return(Some(&result));
|
||||
}
|
||||
|
||||
fn hash_list<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
seed: IntValue<'ctx>,
|
||||
value: StructValue<'ctx>,
|
||||
when_recursive: WhenRecursive<'a>,
|
||||
element_layout: &Layout<'a>,
|
||||
) -> IntValue<'ctx> {
|
||||
use crate::llvm::build_list::{incrementing_elem_loop, load_list};
|
||||
use inkwell::types::BasicType;
|
||||
|
||||
// hash of a list is the hash of its elements
|
||||
let done_block = env.context.append_basic_block(parent, "done");
|
||||
let loop_block = env.context.append_basic_block(parent, "loop");
|
||||
|
||||
let element_type = basic_type_from_layout(env, element_layout);
|
||||
let ptr_type = element_type.ptr_type(inkwell::AddressSpace::Generic);
|
||||
|
||||
let (length, ptr) = load_list(env.builder, value, ptr_type);
|
||||
|
||||
let result = env.builder.build_alloca(env.context.i64_type(), "result");
|
||||
env.builder.build_store(result, seed);
|
||||
|
||||
let is_empty = env.builder.build_int_compare(
|
||||
inkwell::IntPredicate::EQ,
|
||||
length,
|
||||
env.ptr_int().const_zero(),
|
||||
"is_empty",
|
||||
);
|
||||
|
||||
env.builder
|
||||
.build_conditional_branch(is_empty, done_block, loop_block);
|
||||
|
||||
env.builder.position_at_end(loop_block);
|
||||
|
||||
let loop_fn = |_index, element| {
|
||||
let seed = env
|
||||
.builder
|
||||
.build_load(result, "load_current")
|
||||
.into_int_value();
|
||||
|
||||
let answer = append_hash_layout(
|
||||
env,
|
||||
layout_ids,
|
||||
seed,
|
||||
element,
|
||||
element_layout,
|
||||
when_recursive.clone(),
|
||||
);
|
||||
|
||||
env.builder.build_store(result, answer);
|
||||
};
|
||||
|
||||
incrementing_elem_loop(
|
||||
env,
|
||||
parent,
|
||||
*element_layout,
|
||||
ptr,
|
||||
length,
|
||||
"current_index",
|
||||
loop_fn,
|
||||
);
|
||||
|
||||
env.builder.build_unconditional_branch(done_block);
|
||||
|
||||
env.builder.position_at_end(done_block);
|
||||
|
||||
env.builder
|
||||
.build_load(result, "load_current")
|
||||
.into_int_value()
|
||||
}
|
||||
|
||||
fn hash_null(seed: IntValue<'_>) -> IntValue<'_> {
|
||||
seed
|
||||
}
|
||||
|
||||
fn hash_ptr_to_struct<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
union_layout: &UnionLayout<'a>,
|
||||
field_layouts: &'a [Layout<'a>],
|
||||
seed: IntValue<'ctx>,
|
||||
tag: PointerValue<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
use inkwell::types::BasicType;
|
||||
|
||||
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
|
||||
.builder
|
||||
.build_bitcast(tag, wrapper_type, "hash_ptr_to_struct_opaque_to_correct")
|
||||
.into_pointer_value();
|
||||
|
||||
let struct_ptr = env
|
||||
.builder
|
||||
.build_struct_gep(wrapper_ptr, TAG_DATA_INDEX, "get_tag_data")
|
||||
.unwrap();
|
||||
|
||||
let struct_layout = Layout::struct_no_name_order(field_layouts);
|
||||
let struct_type = basic_type_from_layout(env, &struct_layout);
|
||||
let struct_ptr = env
|
||||
.builder
|
||||
.build_bitcast(
|
||||
struct_ptr,
|
||||
struct_type.ptr_type(inkwell::AddressSpace::Generic),
|
||||
"cast_tag_data",
|
||||
)
|
||||
.into_pointer_value();
|
||||
|
||||
let struct_value = env
|
||||
.builder
|
||||
.build_load(struct_ptr, "load_struct1")
|
||||
.into_struct_value();
|
||||
|
||||
build_hash_struct(
|
||||
env,
|
||||
layout_ids,
|
||||
field_layouts,
|
||||
WhenRecursive::Loop(*union_layout),
|
||||
seed,
|
||||
struct_value,
|
||||
)
|
||||
}
|
||||
|
||||
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, layout);
|
||||
let alloc = env.builder.build_alloca(basic_type, "store");
|
||||
env.builder.build_store(alloc, value);
|
||||
|
||||
let u8_ptr = env
|
||||
.context
|
||||
.i8_type()
|
||||
.ptr_type(inkwell::AddressSpace::Generic);
|
||||
|
||||
env.builder
|
||||
.build_bitcast(alloc, u8_ptr, "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.ptr_int().const_int(width as u64, false);
|
||||
|
||||
call_bitcode_fn(
|
||||
env,
|
||||
&[seed.into(), buffer.into(), num_bytes.into()],
|
||||
bitcode::DICT_HASH,
|
||||
)
|
||||
.into_int_value()
|
||||
}
|
1011
crates/compiler/gen_llvm/src/llvm/build_list.rs
Normal file
1011
crates/compiler/gen_llvm/src/llvm/build_list.rs
Normal file
File diff suppressed because it is too large
Load diff
225
crates/compiler/gen_llvm/src/llvm/build_str.rs
Normal file
225
crates/compiler/gen_llvm/src/llvm/build_str.rs
Normal file
|
@ -0,0 +1,225 @@
|
|||
use crate::llvm::bitcode::{call_bitcode_fn, call_str_bitcode_fn, call_void_bitcode_fn};
|
||||
use crate::llvm::build::{Env, Scope};
|
||||
use crate::llvm::build_list::{allocate_list, pass_update_mode, store_list};
|
||||
use inkwell::builder::Builder;
|
||||
use inkwell::values::{BasicValueEnum, IntValue, PointerValue, StructValue};
|
||||
use inkwell::AddressSpace;
|
||||
use morphic_lib::UpdateMode;
|
||||
use roc_builtins::bitcode::{self, IntWidth};
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::layout::{Builtin, Layout};
|
||||
use roc_target::PtrWidth;
|
||||
|
||||
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.split : Str, Str -> List Str
|
||||
pub fn str_split<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
str_symbol: Symbol,
|
||||
delimiter_symbol: Symbol,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let builder = env.builder;
|
||||
|
||||
let string = load_symbol(scope, &str_symbol);
|
||||
let delimiter = load_symbol(scope, &delimiter_symbol);
|
||||
|
||||
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);
|
||||
|
||||
// get the RocStr type defined by zig
|
||||
let roc_str_type = env.module.get_struct_type("str.RocStr").unwrap();
|
||||
|
||||
// convert `*mut { *mut u8, i64 }` to `*mut RocStr`
|
||||
let ret_list_ptr_zig_rocstr = builder.build_bitcast(
|
||||
ret_list_ptr,
|
||||
roc_str_type.ptr_type(AddressSpace::Generic),
|
||||
"convert_to_zig_rocstr",
|
||||
);
|
||||
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
&[ret_list_ptr_zig_rocstr, string, delimiter],
|
||||
bitcode::STR_STR_SPLIT_IN_PLACE,
|
||||
);
|
||||
|
||||
store_list(env, ret_list_ptr, segment_count)
|
||||
}
|
||||
|
||||
pub fn str_symbol_to_c_abi<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
symbol: Symbol,
|
||||
) -> PointerValue<'ctx> {
|
||||
let string = load_symbol(scope, &symbol);
|
||||
|
||||
str_to_c_abi(env, string)
|
||||
}
|
||||
|
||||
pub fn str_to_c_abi<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
value: BasicValueEnum<'ctx>,
|
||||
) -> PointerValue<'ctx> {
|
||||
let parent = env
|
||||
.builder
|
||||
.get_insert_block()
|
||||
.and_then(|b| b.get_parent())
|
||||
.unwrap();
|
||||
|
||||
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>(
|
||||
builder: &Builder<'ctx>,
|
||||
wrapper_struct: StructValue<'ctx>,
|
||||
) -> (PointerValue<'ctx>, IntValue<'ctx>) {
|
||||
let length = builder
|
||||
.build_extract_value(wrapper_struct, Builtin::WRAPPER_LEN, "list_len")
|
||||
.unwrap()
|
||||
.into_int_value();
|
||||
|
||||
// a `*mut u8` pointer
|
||||
let generic_ptr = builder
|
||||
.build_extract_value(wrapper_struct, Builtin::WRAPPER_PTR, "read_list_ptr")
|
||||
.unwrap()
|
||||
.into_pointer_value();
|
||||
|
||||
(generic_ptr, length)
|
||||
}
|
||||
|
||||
/// Str.fromInt : Int -> Str
|
||||
pub fn str_from_int<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
value: IntValue<'ctx>,
|
||||
int_width: IntWidth,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
call_str_bitcode_fn(env, &[value.into()], &bitcode::STR_FROM_INT[int_width])
|
||||
}
|
||||
|
||||
fn decode_from_utf8_result<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
pointer: PointerValue<'ctx>,
|
||||
) -> StructValue<'ctx> {
|
||||
let builder = env.builder;
|
||||
let ctx = env.context;
|
||||
|
||||
let fields = match env.target_info.ptr_width() {
|
||||
PtrWidth::Bytes4 | PtrWidth::Bytes8 => [
|
||||
env.ptr_int().into(),
|
||||
super::convert::zig_str_type(env).into(),
|
||||
env.context.bool_type().into(),
|
||||
ctx.i8_type().into(),
|
||||
],
|
||||
};
|
||||
|
||||
let record_type = env.context.struct_type(&fields, false);
|
||||
|
||||
match env.target_info.ptr_width() {
|
||||
PtrWidth::Bytes4 | PtrWidth::Bytes8 => {
|
||||
let result_ptr_cast = env
|
||||
.builder
|
||||
.build_bitcast(
|
||||
pointer,
|
||||
record_type.ptr_type(AddressSpace::Generic),
|
||||
"to_unnamed",
|
||||
)
|
||||
.into_pointer_value();
|
||||
|
||||
builder
|
||||
.build_load(result_ptr_cast, "load_utf8_validate_bytes_result")
|
||||
.into_struct_value()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
list: Symbol,
|
||||
count_and_start: StructValue<'ctx>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let builder = env.builder;
|
||||
|
||||
let result_type = env.module.get_struct_type("str.FromUtf8Result").unwrap();
|
||||
let result_ptr = builder.build_alloca(result_type, "alloca_utf8_validate_bytes_result");
|
||||
|
||||
let count = env
|
||||
.builder
|
||||
.build_extract_value(count_and_start, 0, "get_count")
|
||||
.unwrap();
|
||||
|
||||
let start = env
|
||||
.builder
|
||||
.build_extract_value(count_and_start, 1, "get_start")
|
||||
.unwrap();
|
||||
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
list_symbol_to_c_abi(env, scope, list).into(),
|
||||
count,
|
||||
start,
|
||||
result_ptr.into(),
|
||||
],
|
||||
bitcode::STR_FROM_UTF8_RANGE,
|
||||
);
|
||||
|
||||
decode_from_utf8_result(env, result_ptr).into()
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
list: Symbol,
|
||||
update_mode: UpdateMode,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let builder = env.builder;
|
||||
|
||||
let result_type = env.module.get_struct_type("str.FromUtf8Result").unwrap();
|
||||
let result_ptr = builder.build_alloca(result_type, "alloca_utf8_validate_bytes_result");
|
||||
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
list_symbol_to_c_abi(env, scope, list).into(),
|
||||
pass_update_mode(env, update_mode),
|
||||
result_ptr.into(),
|
||||
],
|
||||
bitcode::STR_FROM_UTF8,
|
||||
);
|
||||
|
||||
decode_from_utf8_result(env, result_ptr).into()
|
||||
}
|
||||
|
||||
/// Str.fromFloat : Int -> Str
|
||||
pub fn str_from_float<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
int_symbol: Symbol,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let float = load_symbol(scope, &int_symbol);
|
||||
|
||||
call_str_bitcode_fn(env, &[float], bitcode::STR_FROM_FLOAT)
|
||||
}
|
||||
|
||||
/// Str.equal : Str, Str -> Bool
|
||||
pub fn str_equal<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
value1: BasicValueEnum<'ctx>,
|
||||
value2: BasicValueEnum<'ctx>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
call_bitcode_fn(env, &[value1, value2], bitcode::STR_EQUAL)
|
||||
}
|
1419
crates/compiler/gen_llvm/src/llvm/compare.rs
Normal file
1419
crates/compiler/gen_llvm/src/llvm/compare.rs
Normal file
File diff suppressed because it is too large
Load diff
286
crates/compiler/gen_llvm/src/llvm/convert.rs
Normal file
286
crates/compiler/gen_llvm/src/llvm/convert.rs
Normal file
|
@ -0,0 +1,286 @@
|
|||
use crate::llvm::build::Env;
|
||||
use bumpalo::collections::Vec;
|
||||
use inkwell::context::Context;
|
||||
use inkwell::types::{BasicType, BasicTypeEnum, FloatType, IntType, StructType};
|
||||
use inkwell::AddressSpace;
|
||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||
use roc_mono::layout::{Builtin, Layout, UnionLayout};
|
||||
use roc_target::TargetInfo;
|
||||
|
||||
fn basic_type_from_record<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
fields: &[Layout<'_>],
|
||||
) -> BasicTypeEnum<'ctx> {
|
||||
let mut field_types = Vec::with_capacity_in(fields.len(), env.arena);
|
||||
|
||||
for field_layout in fields.iter() {
|
||||
field_types.push(basic_type_from_layout(env, field_layout));
|
||||
}
|
||||
|
||||
env.context
|
||||
.struct_type(field_types.into_bump_slice(), false)
|
||||
.as_basic_type_enum()
|
||||
}
|
||||
|
||||
pub fn basic_type_from_layout<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout: &Layout<'_>,
|
||||
) -> BasicTypeEnum<'ctx> {
|
||||
use Layout::*;
|
||||
|
||||
match layout {
|
||||
Struct {
|
||||
field_layouts: sorted_fields,
|
||||
..
|
||||
} => basic_type_from_record(env, sorted_fields),
|
||||
LambdaSet(lambda_set) => basic_type_from_layout(env, &lambda_set.runtime_representation()),
|
||||
Boxed(inner_layout) => {
|
||||
let inner_type = basic_type_from_layout(env, inner_layout);
|
||||
|
||||
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_union_layout<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
union_layout: &UnionLayout<'_>,
|
||||
) -> BasicTypeEnum<'ctx> {
|
||||
use UnionLayout::*;
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn basic_type_from_builtin<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
builtin: &Builtin<'_>,
|
||||
) -> BasicTypeEnum<'ctx> {
|
||||
use Builtin::*;
|
||||
|
||||
let context = env.context;
|
||||
|
||||
match builtin {
|
||||
Int(int_width) => int_type_from_int_width(env, *int_width).as_basic_type_enum(),
|
||||
Float(float_width) => float_type_from_float_width(env, *float_width).as_basic_type_enum(),
|
||||
Bool => context.bool_type().as_basic_type_enum(),
|
||||
Decimal => context.i128_type().as_basic_type_enum(),
|
||||
Dict(_, _) => zig_dict_type(env).into(),
|
||||
Set(_) => zig_dict_type(env).into(),
|
||||
List(_) => zig_list_type(env).into(),
|
||||
Str => zig_str_type(env).into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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: &Env<'a, 'ctx, 'env>,
|
||||
int_width: IntWidth,
|
||||
) -> IntType<'ctx> {
|
||||
use IntWidth::*;
|
||||
|
||||
match int_width {
|
||||
U128 | I128 => env.context.i128_type(),
|
||||
U64 | I64 => env.context.i64_type(),
|
||||
U32 | I32 => env.context.i32_type(),
|
||||
U16 | I16 => env.context.i16_type(),
|
||||
U8 | I8 => env.context.i8_type(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn float_type_from_float_width<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
float_width: FloatWidth,
|
||||
) -> FloatType<'ctx> {
|
||||
use FloatWidth::*;
|
||||
|
||||
match float_width {
|
||||
F128 => todo!("F128 is not implemented"),
|
||||
F64 => env.context.f64_type(),
|
||||
F32 => env.context.f32_type(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn block_of_memory_slices<'ctx>(
|
||||
context: &'ctx Context,
|
||||
layouts: &[&[Layout<'_>]],
|
||||
target_info: TargetInfo,
|
||||
) -> BasicTypeEnum<'ctx> {
|
||||
let mut union_size = 0;
|
||||
for tag in layouts {
|
||||
let mut total = 0;
|
||||
for layout in tag.iter() {
|
||||
total += layout.stack_size(target_info);
|
||||
}
|
||||
|
||||
union_size = union_size.max(total);
|
||||
}
|
||||
|
||||
block_of_memory_help(context, union_size)
|
||||
}
|
||||
|
||||
pub fn block_of_memory<'ctx>(
|
||||
context: &'ctx Context,
|
||||
layout: &Layout<'_>,
|
||||
target_info: TargetInfo,
|
||||
) -> BasicTypeEnum<'ctx> {
|
||||
// TODO make this dynamic
|
||||
let mut union_size = layout.stack_size(target_info);
|
||||
|
||||
if let Layout::Union(UnionLayout::NonRecursive { .. }) = layout {
|
||||
union_size -= target_info.ptr_width() as u32;
|
||||
}
|
||||
|
||||
block_of_memory_help(context, union_size)
|
||||
}
|
||||
|
||||
fn block_of_memory_help(context: &Context, union_size: u32) -> BasicTypeEnum<'_> {
|
||||
// The memory layout of Union is a bit tricky.
|
||||
// We have tags with different memory layouts, that are part of the same type.
|
||||
// For llvm, all tags must have the same memory layout.
|
||||
//
|
||||
// So, we convert all tags to a layout of bytes of some size.
|
||||
// It turns out that encoding to i64 for as many elements as possible is
|
||||
// a nice optimization, the remainder is encoded as bytes.
|
||||
|
||||
let num_i64 = union_size / 8;
|
||||
let num_i8 = union_size % 8;
|
||||
|
||||
let i8_array_type = context.i8_type().array_type(num_i8).as_basic_type_enum();
|
||||
let i64_array_type = context.i64_type().array_type(num_i64).as_basic_type_enum();
|
||||
|
||||
if num_i64 == 0 {
|
||||
// The object fits perfectly in some number of i8s
|
||||
context.struct_type(&[i8_array_type], false).into()
|
||||
} else if num_i8 == 0 {
|
||||
// The object fits perfectly in some number of i64s
|
||||
// (i.e. the size is a multiple of 8 bytes)
|
||||
context.struct_type(&[i64_array_type], false).into()
|
||||
} else {
|
||||
// There are some trailing bytes at the end
|
||||
let i8_array_type = context.i8_type().array_type(num_i8).as_basic_type_enum();
|
||||
|
||||
context
|
||||
.struct_type(&[i64_array_type, i8_array_type], false)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// The int type that the C ABI turns our RocList/RocStr into
|
||||
pub fn str_list_int(ctx: &Context, target_info: TargetInfo) -> IntType<'_> {
|
||||
match target_info.ptr_width() {
|
||||
roc_target::PtrWidth::Bytes4 => ctx.i64_type(),
|
||||
roc_target::PtrWidth::Bytes8 => ctx.i128_type(),
|
||||
}
|
||||
}
|
||||
|
||||
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: &Env<'a, 'ctx, 'env>) -> StructType<'ctx> {
|
||||
env.module.get_struct_type("list.RocList").unwrap()
|
||||
}
|
||||
|
||||
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: &Env<'a, 'ctx, 'env>) -> StructType<'ctx> {
|
||||
let u8_ptr_t = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
env.context
|
||||
.struct_type(&[env.context.bool_type().into(), u8_ptr_t.into()], false)
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
241
crates/compiler/gen_llvm/src/llvm/externs.rs
Normal file
241
crates/compiler/gen_llvm/src/llvm/externs.rs
Normal file
|
@ -0,0 +1,241 @@
|
|||
use crate::llvm::bitcode::call_void_bitcode_fn;
|
||||
use crate::llvm::build::{add_func, get_panic_msg_ptr, C_CALL_CONV};
|
||||
use crate::llvm::build::{CCReturn, Env, FunctionSpec};
|
||||
use inkwell::module::Linkage;
|
||||
use inkwell::types::BasicType;
|
||||
use inkwell::values::BasicValue;
|
||||
use inkwell::AddressSpace;
|
||||
use roc_builtins::bitcode;
|
||||
|
||||
use super::build::{get_sjlj_buffer, LLVM_LONGJMP};
|
||||
|
||||
/// Define functions for roc_alloc, roc_realloc, and roc_dealloc
|
||||
/// which use libc implementations (malloc, realloc, and free)
|
||||
pub fn add_default_roc_externs(env: &Env<'_, '_, '_>) {
|
||||
let ctx = env.context;
|
||||
let module = env.module;
|
||||
let builder = env.builder;
|
||||
|
||||
let usize_type = env.ptr_int();
|
||||
let i8_ptr_type = ctx.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
// roc_alloc
|
||||
{
|
||||
// The type of this function (but not the implementation) should have
|
||||
// already been defined by the builtins, which rely on it.
|
||||
let fn_val = module.get_function("roc_alloc").unwrap();
|
||||
let mut params = fn_val.get_param_iter();
|
||||
let size_arg = params.next().unwrap();
|
||||
let _alignment_arg = params.next().unwrap();
|
||||
|
||||
debug_assert!(params.next().is_none());
|
||||
|
||||
// Add a basic block for the entry point
|
||||
let entry = ctx.append_basic_block(fn_val, "entry");
|
||||
|
||||
builder.position_at_end(entry);
|
||||
|
||||
// Call libc malloc()
|
||||
let retval = builder
|
||||
.build_array_malloc(ctx.i8_type(), size_arg.into_int_value(), "call_malloc")
|
||||
.unwrap();
|
||||
|
||||
builder.build_return(Some(&retval));
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
crate::llvm::build::verify_fn(fn_val);
|
||||
}
|
||||
}
|
||||
|
||||
// roc_memcpy
|
||||
{
|
||||
// The type of this function (but not the implementation) should have
|
||||
// already been defined by the builtins, which rely on it.
|
||||
let fn_val = module.get_function("roc_memcpy").unwrap();
|
||||
let mut params = fn_val.get_param_iter();
|
||||
let dest_arg = params.next().unwrap();
|
||||
let dest_alignment = 1;
|
||||
let src_arg = params.next().unwrap();
|
||||
let src_alignment = 1;
|
||||
let bytes_arg = params.next().unwrap();
|
||||
|
||||
debug_assert!(params.next().is_none());
|
||||
|
||||
// Add a basic block for the entry point
|
||||
let entry = ctx.append_basic_block(fn_val, "entry");
|
||||
|
||||
builder.position_at_end(entry);
|
||||
|
||||
// Call libc memcpy()
|
||||
let _retval = builder
|
||||
.build_memcpy(
|
||||
dest_arg.into_pointer_value(),
|
||||
dest_alignment,
|
||||
src_arg.into_pointer_value(),
|
||||
src_alignment,
|
||||
bytes_arg.into_int_value(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
builder.build_return(None);
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
crate::llvm::build::verify_fn(fn_val);
|
||||
}
|
||||
}
|
||||
|
||||
// roc_realloc
|
||||
{
|
||||
let libc_realloc_val = {
|
||||
let fn_spec = FunctionSpec::cconv(
|
||||
env,
|
||||
CCReturn::Return,
|
||||
Some(i8_ptr_type.as_basic_type_enum()),
|
||||
&[
|
||||
// ptr: *void
|
||||
i8_ptr_type.into(),
|
||||
// size: usize
|
||||
usize_type.into(),
|
||||
],
|
||||
);
|
||||
let fn_val = add_func(env.context, module, "realloc", fn_spec, Linkage::External);
|
||||
|
||||
let mut params = fn_val.get_param_iter();
|
||||
let ptr_arg = params.next().unwrap();
|
||||
let size_arg = params.next().unwrap();
|
||||
|
||||
debug_assert!(params.next().is_none());
|
||||
|
||||
ptr_arg.set_name("ptr");
|
||||
size_arg.set_name("size");
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
crate::llvm::build::verify_fn(fn_val);
|
||||
}
|
||||
|
||||
fn_val
|
||||
};
|
||||
|
||||
// The type of this function (but not the implementation) should have
|
||||
// already been defined by the builtins, which rely on it.
|
||||
let fn_val = module.get_function("roc_realloc").unwrap();
|
||||
let mut params = fn_val.get_param_iter();
|
||||
let ptr_arg = params.next().unwrap();
|
||||
let new_size_arg = params.next().unwrap();
|
||||
let _old_size_arg = params.next().unwrap();
|
||||
let _alignment_arg = params.next().unwrap();
|
||||
|
||||
debug_assert!(params.next().is_none());
|
||||
|
||||
// Add a basic block for the entry point
|
||||
let entry = ctx.append_basic_block(fn_val, "entry");
|
||||
|
||||
builder.position_at_end(entry);
|
||||
|
||||
// Call libc realloc()
|
||||
let call = builder.build_call(
|
||||
libc_realloc_val,
|
||||
&[ptr_arg.into(), new_size_arg.into()],
|
||||
"call_libc_realloc",
|
||||
);
|
||||
|
||||
call.set_call_convention(C_CALL_CONV);
|
||||
|
||||
let retval = call.try_as_basic_value().left().unwrap();
|
||||
|
||||
builder.build_return(Some(&retval));
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
crate::llvm::build::verify_fn(fn_val);
|
||||
}
|
||||
}
|
||||
|
||||
// roc_dealloc
|
||||
{
|
||||
// The type of this function (but not the implementation) should have
|
||||
// already been defined by the builtins, which rely on it.
|
||||
let fn_val = module.get_function("roc_dealloc").unwrap();
|
||||
let mut params = fn_val.get_param_iter();
|
||||
let ptr_arg = params.next().unwrap();
|
||||
let _alignment_arg = params.next().unwrap();
|
||||
|
||||
debug_assert!(params.next().is_none());
|
||||
|
||||
// Add a basic block for the entry point
|
||||
let entry = ctx.append_basic_block(fn_val, "entry");
|
||||
|
||||
builder.position_at_end(entry);
|
||||
|
||||
// Call libc free()
|
||||
builder.build_free(ptr_arg.into_pointer_value());
|
||||
|
||||
builder.build_return(None);
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
crate::llvm::build::verify_fn(fn_val);
|
||||
}
|
||||
}
|
||||
|
||||
if env.is_gen_test {
|
||||
add_sjlj_roc_panic(env)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_sjlj_roc_panic(env: &Env<'_, '_, '_>) {
|
||||
let ctx = env.context;
|
||||
let module = env.module;
|
||||
let builder = env.builder;
|
||||
|
||||
// roc_panic
|
||||
{
|
||||
// The type of this function (but not the implementation) should have
|
||||
// already been defined by the builtins, which rely on it.
|
||||
let fn_val = module.get_function("roc_panic").unwrap();
|
||||
let mut params = fn_val.get_param_iter();
|
||||
let ptr_arg = params.next().unwrap();
|
||||
|
||||
// in debug mode, this is assumed to be NullTerminatedString
|
||||
let _tag_id_arg = params.next().unwrap();
|
||||
|
||||
debug_assert!(params.next().is_none());
|
||||
|
||||
let subprogram = env.new_subprogram("roc_panic");
|
||||
fn_val.set_subprogram(subprogram);
|
||||
|
||||
env.dibuilder.finalize();
|
||||
|
||||
// Add a basic block for the entry point
|
||||
let entry = ctx.append_basic_block(fn_val, "entry");
|
||||
|
||||
builder.position_at_end(entry);
|
||||
|
||||
// write our error message pointer
|
||||
env.builder.build_store(get_panic_msg_ptr(env), ptr_arg);
|
||||
|
||||
build_longjmp_call(env);
|
||||
|
||||
builder.build_unreachable();
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
crate::llvm::build::verify_fn(fn_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_longjmp_call(env: &Env) {
|
||||
let jmp_buf = get_sjlj_buffer(env);
|
||||
if cfg!(target_arch = "aarch64") {
|
||||
// Call the Zig-linked longjmp: `void longjmp(i32*, i32)`
|
||||
let tag = env.context.i32_type().const_int(1, false);
|
||||
let _call =
|
||||
call_void_bitcode_fn(env, &[jmp_buf.into(), tag.into()], bitcode::UTILS_LONGJMP);
|
||||
} else {
|
||||
// Call the LLVM-intrinsic longjmp: `void @llvm.eh.sjlj.longjmp(i8* %setjmp_buf)`
|
||||
let jmp_buf_i8p = env.builder.build_bitcast(
|
||||
jmp_buf,
|
||||
env.context.i8_type().ptr_type(AddressSpace::Generic),
|
||||
"jmp_buf i8*",
|
||||
);
|
||||
let _call = env.build_intrinsic_call(LLVM_LONGJMP, &[jmp_buf_i8p]);
|
||||
}
|
||||
}
|
10
crates/compiler/gen_llvm/src/llvm/mod.rs
Normal file
10
crates/compiler/gen_llvm/src/llvm/mod.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
pub mod bitcode;
|
||||
pub mod build;
|
||||
pub mod build_dict;
|
||||
pub mod build_hash;
|
||||
pub mod build_list;
|
||||
pub mod build_str;
|
||||
pub mod compare;
|
||||
pub mod convert;
|
||||
pub mod externs;
|
||||
pub mod refcounting;
|
1882
crates/compiler/gen_llvm/src/llvm/refcounting.rs
Normal file
1882
crates/compiler/gen_llvm/src/llvm/refcounting.rs
Normal file
File diff suppressed because it is too large
Load diff
173
crates/compiler/gen_llvm/src/run_roc.rs
Normal file
173
crates/compiler/gen_llvm/src/run_roc.rs
Normal file
|
@ -0,0 +1,173 @@
|
|||
use std::ffi::CString;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
/// This must have the same size as the repr() of RocCallResult!
|
||||
pub const ROC_CALL_RESULT_DISCRIMINANT_SIZE: usize = std::mem::size_of::<u64>();
|
||||
|
||||
#[repr(C)]
|
||||
pub struct RocCallResult<T> {
|
||||
tag: u64,
|
||||
error_msg: *mut c_char,
|
||||
value: MaybeUninit<T>,
|
||||
}
|
||||
|
||||
impl<T: Sized> From<RocCallResult<T>> for Result<T, String> {
|
||||
fn from(call_result: RocCallResult<T>) -> Self {
|
||||
match call_result.tag {
|
||||
0 => Ok(unsafe { call_result.value.assume_init() }),
|
||||
_ => Err({
|
||||
let raw = unsafe { CString::from_raw(call_result.error_msg) };
|
||||
|
||||
let result = format!("{:?}", raw);
|
||||
|
||||
// make sure rust does not try to free the Roc string
|
||||
std::mem::forget(raw);
|
||||
|
||||
result
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! run_jit_function {
|
||||
($lib: expr, $main_fn_name: expr, $ty:ty, $transform:expr) => {{
|
||||
let v: String = String::new();
|
||||
run_jit_function!($lib, $main_fn_name, $ty, $transform, v)
|
||||
}};
|
||||
|
||||
($lib: expr, $main_fn_name: expr, $ty:ty, $transform:expr, $errors:expr) => {{
|
||||
run_jit_function!($lib, $main_fn_name, $ty, $transform, $errors, &[])
|
||||
}};
|
||||
($lib: expr, $main_fn_name: expr, $ty:ty, $transform:expr, $errors:expr, $expect_failures:expr) => {{
|
||||
use inkwell::context::Context;
|
||||
use roc_builtins::bitcode;
|
||||
use roc_gen_llvm::run_roc::RocCallResult;
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(C)]
|
||||
struct Failure {
|
||||
start_line: u32,
|
||||
end_line: u32,
|
||||
start_col: u16,
|
||||
end_col: u16,
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let main: libloading::Symbol<unsafe extern "C" fn(*mut RocCallResult<$ty>)> = $lib
|
||||
.get($main_fn_name.as_bytes())
|
||||
.ok()
|
||||
.ok_or(format!("Unable to JIT compile `{}`", $main_fn_name))
|
||||
.expect("errored");
|
||||
|
||||
#[repr(C)]
|
||||
struct Failures {
|
||||
failures: *const Failure,
|
||||
count: usize,
|
||||
}
|
||||
|
||||
impl Drop for Failures {
|
||||
fn drop(&mut self) {
|
||||
use std::alloc::{dealloc, Layout};
|
||||
use std::mem;
|
||||
|
||||
unsafe {
|
||||
let layout = Layout::from_size_align_unchecked(
|
||||
mem::size_of::<Failure>(),
|
||||
mem::align_of::<Failure>(),
|
||||
);
|
||||
|
||||
dealloc(self.failures as *mut u8, layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let get_expect_failures: libloading::Symbol<unsafe extern "C" fn() -> Failures> = $lib
|
||||
.get(bitcode::UTILS_GET_EXPECT_FAILURES.as_bytes())
|
||||
.ok()
|
||||
.ok_or(format!(
|
||||
"Unable to JIT compile `{}`",
|
||||
bitcode::UTILS_GET_EXPECT_FAILURES
|
||||
))
|
||||
.expect("errored");
|
||||
let mut main_result = MaybeUninit::uninit();
|
||||
|
||||
main(main_result.as_mut_ptr());
|
||||
let failures = get_expect_failures();
|
||||
|
||||
if failures.count > 0 {
|
||||
// TODO tell the user about the failures!
|
||||
let failures =
|
||||
unsafe { core::slice::from_raw_parts(failures.failures, failures.count) };
|
||||
|
||||
panic!("Failed with {} failures. Failures: ", failures.len());
|
||||
}
|
||||
|
||||
match main_result.assume_init().into() {
|
||||
Ok(success) => {
|
||||
// only if there are no exceptions thrown, check for errors
|
||||
assert!($errors.is_empty(), "Encountered errors:\n{}", $errors);
|
||||
|
||||
$transform(success)
|
||||
}
|
||||
Err(error_msg) => panic!("Roc failed with message: {}", error_msg),
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
/// In the repl, we don't know the type that is returned; it it's large enough to not fit in 2
|
||||
/// registers (i.e. size bigger than 16 bytes on 64-bit systems), then we use this macro.
|
||||
/// It explicitly allocates a buffer that the roc main function can write its result into.
|
||||
#[macro_export]
|
||||
macro_rules! run_jit_function_dynamic_type {
|
||||
($lib: expr, $main_fn_name: expr, $bytes:expr, $transform:expr) => {{
|
||||
let v: String = String::new();
|
||||
run_jit_function_dynamic_type!($lib, $main_fn_name, $bytes, $transform, v)
|
||||
}};
|
||||
|
||||
($lib: expr, $main_fn_name: expr, $bytes:expr, $transform:expr, $errors:expr) => {{
|
||||
use inkwell::context::Context;
|
||||
use roc_gen_llvm::run_roc::RocCallResult;
|
||||
|
||||
unsafe {
|
||||
let main: libloading::Symbol<unsafe extern "C" fn(*const u8)> = $lib
|
||||
.get($main_fn_name.as_bytes())
|
||||
.ok()
|
||||
.ok_or(format!("Unable to JIT compile `{}`", $main_fn_name))
|
||||
.expect("errored");
|
||||
|
||||
let size = std::mem::size_of::<RocCallResult<()>>() + $bytes;
|
||||
let layout = std::alloc::Layout::array::<u8>(size).unwrap();
|
||||
let result = std::alloc::alloc(layout);
|
||||
main(result);
|
||||
|
||||
let flag = *result;
|
||||
|
||||
if flag == 0 {
|
||||
$transform(result.add(std::mem::size_of::<RocCallResult<()>>()) as usize)
|
||||
} else {
|
||||
use std::ffi::CString;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
// first field is a char pointer (to the error message)
|
||||
// read value, and transmute to a pointer
|
||||
let ptr_as_int = *(result as *const u64).offset(1);
|
||||
let ptr = std::mem::transmute::<u64, *mut c_char>(ptr_as_int);
|
||||
|
||||
// make CString (null-terminated)
|
||||
let raw = CString::from_raw(ptr);
|
||||
|
||||
let result = format!("{:?}", raw);
|
||||
|
||||
// make sure rust doesn't try to free the Roc constant string
|
||||
std::mem::forget(raw);
|
||||
|
||||
eprintln!("{}", result);
|
||||
panic!("Roc hit an error");
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue