mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 06:14:46 +00:00
waypoint
This commit is contained in:
parent
51756cbc89
commit
5cd232816b
8 changed files with 337 additions and 156 deletions
|
@ -255,7 +255,7 @@ pub fn build_file<'a>(
|
||||||
link(
|
link(
|
||||||
target,
|
target,
|
||||||
binary_path.clone(),
|
binary_path.clone(),
|
||||||
&inputs,
|
&inputs,
|
||||||
link_type
|
link_type
|
||||||
)
|
)
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/// Helpers for interacting with the zig that generates bitcode
|
/// Helpers for interacting with the zig that generates bitcode
|
||||||
use crate::debug_info_init;
|
use crate::debug_info_init;
|
||||||
use crate::llvm::build::{struct_from_fields, Env, C_CALL_CONV, FAST_CALL_CONV, TAG_DATA_INDEX};
|
use crate::llvm::build::{struct_from_fields, Env, C_CALL_CONV, FAST_CALL_CONV, TAG_DATA_INDEX};
|
||||||
use crate::llvm::convert::basic_type_from_layout;
|
use crate::llvm::convert::{basic_type_from_layout, basic_type_from_layout_1};
|
||||||
use crate::llvm::refcounting::{
|
use crate::llvm::refcounting::{
|
||||||
decrement_refcount_layout, increment_n_refcount_layout, increment_refcount_layout,
|
decrement_refcount_layout, increment_n_refcount_layout, increment_refcount_layout,
|
||||||
};
|
};
|
||||||
|
@ -128,20 +128,19 @@ fn build_has_tag_id_help<'a, 'ctx, 'env>(
|
||||||
[tag_id, tag_value_ptr] => {
|
[tag_id, tag_value_ptr] => {
|
||||||
let tag_type = basic_type_from_layout(env, &Layout::Union(union_layout));
|
let tag_type = basic_type_from_layout(env, &Layout::Union(union_layout));
|
||||||
|
|
||||||
let argument_cast = env
|
let tag_value = env.builder.build_pointer_cast(
|
||||||
.builder
|
tag_value_ptr.into_pointer_value(),
|
||||||
.build_bitcast(
|
tag_type.ptr_type(AddressSpace::Generic),
|
||||||
*tag_value_ptr,
|
"load_opaque_get_tag_id",
|
||||||
tag_type.ptr_type(AddressSpace::Generic),
|
);
|
||||||
"load_opaque",
|
|
||||||
)
|
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
let tag_value = env.builder.build_load(argument_cast, "get_value");
|
|
||||||
|
|
||||||
let actual_tag_id = {
|
let actual_tag_id = {
|
||||||
let tag_id_i64 =
|
let tag_id_i64 = crate::llvm::build::get_tag_id(
|
||||||
crate::llvm::build::get_tag_id(env, function_value, &union_layout, tag_value);
|
env,
|
||||||
|
function_value,
|
||||||
|
&union_layout,
|
||||||
|
tag_value.into(),
|
||||||
|
);
|
||||||
|
|
||||||
env.builder
|
env.builder
|
||||||
.build_int_cast(tag_id_i64, env.context.i16_type(), "to_i16")
|
.build_int_cast(tag_id_i64, env.context.i16_type(), "to_i16")
|
||||||
|
@ -263,12 +262,22 @@ fn build_transform_caller_help<'a, 'ctx, 'env>(
|
||||||
for (argument_ptr, layout) in arguments.iter().zip(argument_layouts) {
|
for (argument_ptr, layout) in arguments.iter().zip(argument_layouts) {
|
||||||
let basic_type = basic_type_from_layout(env, layout).ptr_type(AddressSpace::Generic);
|
let basic_type = basic_type_from_layout(env, layout).ptr_type(AddressSpace::Generic);
|
||||||
|
|
||||||
let argument_cast = env
|
let argument = if layout.is_passed_by_reference() {
|
||||||
.builder
|
env.builder
|
||||||
.build_bitcast(*argument_ptr, basic_type, "load_opaque")
|
.build_pointer_cast(
|
||||||
.into_pointer_value();
|
argument_ptr.into_pointer_value(),
|
||||||
|
basic_type,
|
||||||
|
"cast_ptr_to_tag",
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
} else {
|
||||||
|
let argument_cast = env
|
||||||
|
.builder
|
||||||
|
.build_bitcast(*argument_ptr, basic_type, "load_opaque_1")
|
||||||
|
.into_pointer_value();
|
||||||
|
|
||||||
let argument = env.builder.build_load(argument_cast, "load_opaque");
|
env.builder.build_load(argument_cast, "load_opaque_2")
|
||||||
|
};
|
||||||
|
|
||||||
arguments_cast.push(argument);
|
arguments_cast.push(argument);
|
||||||
}
|
}
|
||||||
|
@ -300,17 +309,10 @@ fn build_transform_caller_help<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
let result_u8_ptr = function_value
|
let result_u8_ptr = function_value
|
||||||
.get_nth_param(argument_layouts.len() as u32 + 1)
|
.get_nth_param(argument_layouts.len() as u32 + 1)
|
||||||
.unwrap();
|
.unwrap()
|
||||||
let result_ptr = env
|
|
||||||
.builder
|
|
||||||
.build_bitcast(
|
|
||||||
result_u8_ptr,
|
|
||||||
result.get_type().ptr_type(AddressSpace::Generic),
|
|
||||||
"write_result",
|
|
||||||
)
|
|
||||||
.into_pointer_value();
|
.into_pointer_value();
|
||||||
|
|
||||||
env.builder.build_store(result_ptr, result);
|
crate::llvm::build::store_roc_value_opaque(env, result_layout, result_u8_ptr, result);
|
||||||
env.builder.build_return(None);
|
env.builder.build_return(None);
|
||||||
|
|
||||||
env.builder.position_at_end(block);
|
env.builder.position_at_end(block);
|
||||||
|
@ -412,12 +414,18 @@ fn build_rc_wrapper<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
let value_type = basic_type_from_layout(env, layout).ptr_type(AddressSpace::Generic);
|
let value_type = basic_type_from_layout(env, layout).ptr_type(AddressSpace::Generic);
|
||||||
|
|
||||||
let value_cast = env
|
let value = if layout.is_passed_by_reference() {
|
||||||
.builder
|
env.builder
|
||||||
.build_bitcast(value_ptr, value_type, "load_opaque")
|
.build_pointer_cast(value_ptr, value_type, "cast_ptr_to_tag")
|
||||||
.into_pointer_value();
|
.into()
|
||||||
|
} else {
|
||||||
|
let value_cast = env
|
||||||
|
.builder
|
||||||
|
.build_bitcast(value_ptr, value_type, "load_opaque")
|
||||||
|
.into_pointer_value();
|
||||||
|
|
||||||
let value = env.builder.build_load(value_cast, "load_opaque");
|
env.builder.build_load(value_cast, "load_opaque")
|
||||||
|
};
|
||||||
|
|
||||||
match rc_operation {
|
match rc_operation {
|
||||||
Mode::Inc => {
|
Mode::Inc => {
|
||||||
|
|
|
@ -21,7 +21,8 @@ use crate::llvm::build_str::{
|
||||||
};
|
};
|
||||||
use crate::llvm::compare::{generic_eq, generic_neq};
|
use crate::llvm::compare::{generic_eq, generic_neq};
|
||||||
use crate::llvm::convert::{
|
use crate::llvm::convert::{
|
||||||
basic_type_from_builtin, basic_type_from_layout, block_of_memory_slices, ptr_int,
|
basic_type_from_builtin, basic_type_from_layout, basic_type_from_layout_1,
|
||||||
|
block_of_memory_slices, ptr_int,
|
||||||
};
|
};
|
||||||
use crate::llvm::refcounting::{
|
use crate::llvm::refcounting::{
|
||||||
build_reset, decrement_refcount_layout, increment_refcount_layout, PointerToRefcount,
|
build_reset, decrement_refcount_layout, increment_refcount_layout, PointerToRefcount,
|
||||||
|
@ -618,8 +619,8 @@ pub fn construct_optimization_passes<'a>(
|
||||||
mpm.add_always_inliner_pass();
|
mpm.add_always_inliner_pass();
|
||||||
|
|
||||||
// tail-call elimination is always on
|
// tail-call elimination is always on
|
||||||
fpm.add_instruction_combining_pass();
|
// fpm.add_instruction_combining_pass();
|
||||||
fpm.add_tail_call_elimination_pass();
|
// fpm.add_tail_call_elimination_pass();
|
||||||
|
|
||||||
let pmb = PassManagerBuilder::create();
|
let pmb = PassManagerBuilder::create();
|
||||||
match opt_level {
|
match opt_level {
|
||||||
|
@ -1235,32 +1236,21 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
match union_layout {
|
match union_layout {
|
||||||
UnionLayout::NonRecursive(tag_layouts) => {
|
UnionLayout::NonRecursive(tag_layouts) => {
|
||||||
debug_assert!(argument.is_struct_value());
|
debug_assert!(argument.is_pointer_value());
|
||||||
|
|
||||||
let field_layouts = tag_layouts[*tag_id as usize];
|
let field_layouts = tag_layouts[*tag_id as usize];
|
||||||
let struct_layout = Layout::Struct(field_layouts);
|
|
||||||
|
|
||||||
let struct_type = basic_type_from_layout(env, &struct_layout);
|
let tag_id_type =
|
||||||
|
basic_type_from_layout(env, &union_layout.tag_id_layout()).into_int_type();
|
||||||
|
|
||||||
let argument_pointer = builder.build_alloca(argument.get_type(), "cast_alloca");
|
lookup_at_index_ptr2(
|
||||||
builder.build_store(argument_pointer, argument);
|
env,
|
||||||
|
union_layout,
|
||||||
let struct_ptr = builder.build_pointer_cast(
|
tag_id_type,
|
||||||
argument_pointer,
|
field_layouts,
|
||||||
struct_type.ptr_type(AddressSpace::Generic),
|
*index as usize,
|
||||||
"foobar",
|
argument.into_pointer_value(),
|
||||||
);
|
)
|
||||||
|
|
||||||
// let struct_value = access_index_struct_value(
|
|
||||||
// builder,
|
|
||||||
// argument.into_struct_value(),
|
|
||||||
// struct_type.into_struct_type(),
|
|
||||||
// );
|
|
||||||
|
|
||||||
let result = builder
|
|
||||||
.build_struct_gep(struct_ptr, *index as u32, "")
|
|
||||||
.expect("desired field did not decode");
|
|
||||||
|
|
||||||
builder.build_load(result, "foobarbaz")
|
|
||||||
}
|
}
|
||||||
UnionLayout::Recursive(tag_layouts) => {
|
UnionLayout::Recursive(tag_layouts) => {
|
||||||
debug_assert!(argument.is_pointer_value());
|
debug_assert!(argument.is_pointer_value());
|
||||||
|
@ -1555,10 +1545,13 @@ pub fn build_tag<'a, 'ctx, 'env>(
|
||||||
.builder
|
.builder
|
||||||
.build_struct_gep(struct_ptr, index, "get_tag_field_ptr")
|
.build_struct_gep(struct_ptr, index, "get_tag_field_ptr")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
env.builder.build_store(ptr, field_val);
|
|
||||||
|
let field_layout = tag_field_layouts[index as usize];
|
||||||
|
store_roc_value(env, field_layout, ptr, field_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
env.builder.build_load(result_alloca, "load_result")
|
// env.builder.build_load(result_alloca, "load_result")
|
||||||
|
result_alloca.into()
|
||||||
}
|
}
|
||||||
UnionLayout::Recursive(tags) => {
|
UnionLayout::Recursive(tags) => {
|
||||||
debug_assert!(union_size > 1);
|
debug_assert!(union_size > 1);
|
||||||
|
@ -1866,9 +1859,10 @@ pub fn get_tag_id<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
match union_layout {
|
match union_layout {
|
||||||
UnionLayout::NonRecursive(_) => {
|
UnionLayout::NonRecursive(_) => {
|
||||||
let tag = argument.into_struct_value();
|
debug_assert!(argument.is_pointer_value(), "{:?}", argument);
|
||||||
|
|
||||||
get_tag_id_non_recursive(env, tag)
|
let argument_ptr = argument.into_pointer_value();
|
||||||
|
get_tag_id_wrapped(env, argument_ptr)
|
||||||
}
|
}
|
||||||
UnionLayout::Recursive(_) => {
|
UnionLayout::Recursive(_) => {
|
||||||
let argument_ptr = argument.into_pointer_value();
|
let argument_ptr = argument.into_pointer_value();
|
||||||
|
@ -2008,7 +2002,26 @@ fn lookup_at_index_ptr2<'a, 'ctx, 'env>(
|
||||||
.build_struct_gep(data_ptr, index as u32, "at_index_struct_gep")
|
.build_struct_gep(data_ptr, index as u32, "at_index_struct_gep")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let result = builder.build_load(elem_ptr, "load_at_index_ptr");
|
let field_layout = field_layouts[index];
|
||||||
|
let result = if field_layout.is_passed_by_reference() {
|
||||||
|
let field_type = basic_type_from_layout(env, &field_layout);
|
||||||
|
|
||||||
|
let align_bytes = field_layout.alignment_bytes(env.ptr_bytes);
|
||||||
|
let alloca = env.builder.build_alloca(field_type, "copied_tag");
|
||||||
|
if align_bytes > 0 {
|
||||||
|
let size = env
|
||||||
|
.ptr_int()
|
||||||
|
.const_int(field_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||||
|
|
||||||
|
env.builder
|
||||||
|
.build_memcpy(alloca, align_bytes, elem_ptr, align_bytes, size)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
alloca.into()
|
||||||
|
} else {
|
||||||
|
builder.build_load(elem_ptr, "load_at_index_ptr")
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(Layout::RecursivePointer) = field_layouts.get(index as usize) {
|
if let Some(Layout::RecursivePointer) = field_layouts.get(index as usize) {
|
||||||
// a recursive field is stored as a `i64*`, to use it we must cast it to
|
// a recursive field is stored as a `i64*`, to use it we must cast it to
|
||||||
|
@ -2155,17 +2168,12 @@ pub fn allocate_with_refcount_help<'a, 'ctx, 'env>(
|
||||||
let ptr_type = value_type.ptr_type(AddressSpace::Generic);
|
let ptr_type = value_type.ptr_type(AddressSpace::Generic);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
builder
|
builder.build_pointer_cast(
|
||||||
.build_bitcast(
|
env.builder
|
||||||
env.builder.build_in_bounds_gep(
|
.build_in_bounds_gep(as_usize_ptr, &[index_intvalue], "get_data_ptr"),
|
||||||
as_usize_ptr,
|
ptr_type,
|
||||||
&[index_intvalue],
|
"alloc_cast_to_desired",
|
||||||
"get_data_ptr",
|
)
|
||||||
),
|
|
||||||
ptr_type,
|
|
||||||
"alloc_cast_to_desired",
|
|
||||||
)
|
|
||||||
.into_pointer_value()
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2191,13 +2199,13 @@ pub fn allocate_with_refcount_help<'a, 'ctx, 'env>(
|
||||||
fn list_literal<'a, 'ctx, 'env>(
|
fn list_literal<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
scope: &Scope<'a, 'ctx>,
|
scope: &Scope<'a, 'ctx>,
|
||||||
elem_layout: &Layout<'a>,
|
element_layout: &Layout<'a>,
|
||||||
elems: &[ListLiteralElement],
|
elems: &[ListLiteralElement],
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
let ctx = env.context;
|
let ctx = env.context;
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
|
|
||||||
let element_type = basic_type_from_layout(env, elem_layout);
|
let element_type = basic_type_from_layout(env, element_layout);
|
||||||
|
|
||||||
let list_length = elems.len();
|
let list_length = elems.len();
|
||||||
let list_length_intval = env.ptr_int().const_int(list_length as _, false);
|
let list_length_intval = env.ptr_int().const_int(list_length as _, false);
|
||||||
|
@ -2207,9 +2215,9 @@ fn list_literal<'a, 'ctx, 'env>(
|
||||||
// if element_type.is_int_type() {
|
// if element_type.is_int_type() {
|
||||||
if false {
|
if false {
|
||||||
let element_type = element_type.into_int_type();
|
let element_type = element_type.into_int_type();
|
||||||
let element_width = elem_layout.stack_size(env.ptr_bytes);
|
let element_width = element_layout.stack_size(env.ptr_bytes);
|
||||||
let size = list_length * element_width as usize;
|
let size = list_length * element_width as usize;
|
||||||
let alignment = elem_layout
|
let alignment = element_layout
|
||||||
.alignment_bytes(env.ptr_bytes)
|
.alignment_bytes(env.ptr_bytes)
|
||||||
.max(env.ptr_bytes);
|
.max(env.ptr_bytes);
|
||||||
|
|
||||||
|
@ -2241,7 +2249,7 @@ fn list_literal<'a, 'ctx, 'env>(
|
||||||
for (index, element) in elems.iter().enumerate() {
|
for (index, element) in elems.iter().enumerate() {
|
||||||
match element {
|
match element {
|
||||||
ListLiteralElement::Literal(literal) => {
|
ListLiteralElement::Literal(literal) => {
|
||||||
let val = build_exp_literal(env, elem_layout, literal);
|
let val = build_exp_literal(env, element_layout, literal);
|
||||||
global_elements.push(val.into_int_value());
|
global_elements.push(val.into_int_value());
|
||||||
}
|
}
|
||||||
ListLiteralElement::Symbol(symbol) => {
|
ListLiteralElement::Symbol(symbol) => {
|
||||||
|
@ -2296,7 +2304,7 @@ fn list_literal<'a, 'ctx, 'env>(
|
||||||
super::build_list::store_list(env, ptr, list_length_intval)
|
super::build_list::store_list(env, ptr, list_length_intval)
|
||||||
} else {
|
} else {
|
||||||
// some of our elements are non-constant, so we must allocate space on the heap
|
// some of our elements are non-constant, so we must allocate space on the heap
|
||||||
let ptr = allocate_list(env, elem_layout, list_length_intval);
|
let ptr = allocate_list(env, element_layout, list_length_intval);
|
||||||
|
|
||||||
// then, copy the relevant segment from the constant section into the heap
|
// then, copy the relevant segment from the constant section into the heap
|
||||||
env.builder
|
env.builder
|
||||||
|
@ -2320,26 +2328,69 @@ fn list_literal<'a, 'ctx, 'env>(
|
||||||
super::build_list::store_list(env, ptr, list_length_intval)
|
super::build_list::store_list(env, ptr, list_length_intval)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let ptr = allocate_list(env, elem_layout, list_length_intval);
|
let ptr = allocate_list(env, element_layout, list_length_intval);
|
||||||
|
|
||||||
// Copy the elements from the list literal into the array
|
// Copy the elements from the list literal into the array
|
||||||
for (index, element) in elems.iter().enumerate() {
|
for (index, element) in elems.iter().enumerate() {
|
||||||
let val = match element {
|
let val = match element {
|
||||||
ListLiteralElement::Literal(literal) => {
|
ListLiteralElement::Literal(literal) => {
|
||||||
build_exp_literal(env, elem_layout, literal)
|
build_exp_literal(env, element_layout, literal)
|
||||||
}
|
}
|
||||||
ListLiteralElement::Symbol(symbol) => load_symbol(scope, symbol),
|
ListLiteralElement::Symbol(symbol) => load_symbol(scope, symbol),
|
||||||
};
|
};
|
||||||
let index_val = ctx.i64_type().const_int(index as u64, false);
|
let index_val = ctx.i64_type().const_int(index as u64, false);
|
||||||
let elem_ptr = unsafe { builder.build_in_bounds_gep(ptr, &[index_val], "index") };
|
let elem_ptr = unsafe { builder.build_in_bounds_gep(ptr, &[index_val], "index") };
|
||||||
|
|
||||||
builder.build_store(elem_ptr, val);
|
store_roc_value(env, *element_layout, elem_ptr, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
super::build_list::store_list(env, ptr, list_length_intval)
|
super::build_list::store_list(env, ptr, list_length_intval)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn store_roc_value_opaque<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
layout: Layout<'a>,
|
||||||
|
opaque_destination: PointerValue<'ctx>,
|
||||||
|
value: BasicValueEnum<'ctx>,
|
||||||
|
) {
|
||||||
|
let target_type = basic_type_from_layout(env, &layout).ptr_type(AddressSpace::Generic);
|
||||||
|
let destination =
|
||||||
|
env.builder
|
||||||
|
.build_pointer_cast(opaque_destination, target_type, "store_roc_value_opaque");
|
||||||
|
|
||||||
|
store_roc_value(env, layout, destination, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn store_roc_value<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
layout: Layout<'a>,
|
||||||
|
destination: PointerValue<'ctx>,
|
||||||
|
value: BasicValueEnum<'ctx>,
|
||||||
|
) {
|
||||||
|
if layout.is_passed_by_reference() {
|
||||||
|
let align_bytes = layout.alignment_bytes(env.ptr_bytes);
|
||||||
|
|
||||||
|
if align_bytes > 0 {
|
||||||
|
let size = env
|
||||||
|
.ptr_int()
|
||||||
|
.const_int(layout.stack_size(env.ptr_bytes) as u64, false);
|
||||||
|
|
||||||
|
env.builder
|
||||||
|
.build_memcpy(
|
||||||
|
destination,
|
||||||
|
align_bytes,
|
||||||
|
value.into_pointer_value(),
|
||||||
|
align_bytes,
|
||||||
|
size,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
env.builder.build_store(destination, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
@ -2415,8 +2466,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
let out_parameter = parameters.last().unwrap();
|
let out_parameter = parameters.last().unwrap();
|
||||||
debug_assert!(out_parameter.is_pointer_value());
|
debug_assert!(out_parameter.is_pointer_value());
|
||||||
|
|
||||||
env.builder
|
store_roc_value(env, *layout, out_parameter.into_pointer_value(), value);
|
||||||
.build_store(out_parameter.into_pointer_value(), value);
|
|
||||||
|
|
||||||
if let Some(block) = env.builder.get_insert_block() {
|
if let Some(block) = env.builder.get_insert_block() {
|
||||||
match block.get_terminator() {
|
match block.get_terminator() {
|
||||||
|
@ -3656,7 +3706,7 @@ fn set_jump_and_catch_long_jump<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
let call_result = call_roc_function(env, roc_function, &return_layout, arguments);
|
let call_result = call_roc_function(env, roc_function, &return_layout, arguments);
|
||||||
|
|
||||||
let return_value = make_good_roc_result(env, call_result);
|
let return_value = make_good_roc_result(env, return_layout, call_result);
|
||||||
|
|
||||||
builder.build_store(result_alloca, return_value);
|
builder.build_store(result_alloca, return_value);
|
||||||
|
|
||||||
|
@ -3721,7 +3771,7 @@ fn set_jump_and_catch_long_jump<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
env.builder.position_at_end(cont_block);
|
env.builder.position_at_end(cont_block);
|
||||||
|
|
||||||
builder.build_load(result_alloca, "load_result")
|
builder.build_load(result_alloca, "set_jump_and_catch_long_jump_load_result")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_exception_catcher<'a, 'ctx, 'env>(
|
fn make_exception_catcher<'a, 'ctx, 'env>(
|
||||||
|
@ -3741,12 +3791,13 @@ fn make_exception_catcher<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
fn make_good_roc_result<'a, 'ctx, 'env>(
|
fn make_good_roc_result<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
return_layout: Layout<'a>,
|
||||||
return_value: BasicValueEnum<'ctx>,
|
return_value: BasicValueEnum<'ctx>,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
let context = env.context;
|
let context = env.context;
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
|
|
||||||
let content_type = return_value.get_type();
|
let content_type = basic_type_from_layout(env, &return_layout);
|
||||||
let wrapper_return_type =
|
let wrapper_return_type =
|
||||||
context.struct_type(&[context.i64_type().into(), content_type], false);
|
context.struct_type(&[context.i64_type().into(), content_type], false);
|
||||||
|
|
||||||
|
@ -3756,9 +3807,19 @@ fn make_good_roc_result<'a, 'ctx, 'env>(
|
||||||
.build_insert_value(v1, context.i64_type().const_zero(), 0, "set_no_error")
|
.build_insert_value(v1, context.i64_type().const_zero(), 0, "set_no_error")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let v3 = builder
|
let v3 = if return_layout.is_passed_by_reference() {
|
||||||
.build_insert_value(v2, return_value, 1, "set_call_result")
|
let loaded = env.builder.build_load(
|
||||||
.unwrap();
|
return_value.into_pointer_value(),
|
||||||
|
"load_call_result_passed_by_ptr",
|
||||||
|
);
|
||||||
|
builder
|
||||||
|
.build_insert_value(v2, loaded, 1, "set_call_result")
|
||||||
|
.unwrap()
|
||||||
|
} else {
|
||||||
|
builder
|
||||||
|
.build_insert_value(v2, return_value, 1, "set_call_result")
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
v3.into_struct_value().into()
|
v3.into_struct_value().into()
|
||||||
}
|
}
|
||||||
|
@ -3971,6 +4032,8 @@ fn build_procedures_help<'a, 'ctx, 'env>(
|
||||||
app_ll_file,
|
app_ll_file,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
env.module.print_to_stderr();
|
||||||
|
|
||||||
panic!(
|
panic!(
|
||||||
"The preceding code was from {:?}, which failed LLVM verification in {} build.",
|
"The preceding code was from {:?}, which failed LLVM verification in {} build.",
|
||||||
fn_val.get_name().to_str().unwrap(),
|
fn_val.get_name().to_str().unwrap(),
|
||||||
|
@ -4020,7 +4083,7 @@ fn build_proc_header<'a, 'ctx, 'env>(
|
||||||
let mut arg_basic_types = Vec::with_capacity_in(args.len(), arena);
|
let mut arg_basic_types = Vec::with_capacity_in(args.len(), arena);
|
||||||
|
|
||||||
for (layout, _) in args.iter() {
|
for (layout, _) in args.iter() {
|
||||||
let arg_type = basic_type_from_layout(env, layout);
|
let arg_type = basic_type_from_layout_1(env, layout);
|
||||||
|
|
||||||
arg_basic_types.push(arg_type);
|
arg_basic_types.push(arg_type);
|
||||||
}
|
}
|
||||||
|
@ -4132,19 +4195,41 @@ pub fn build_closure_caller<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let call_result = if env.is_gen_test {
|
if env.is_gen_test {
|
||||||
set_jump_and_catch_long_jump(
|
let call_result = set_jump_and_catch_long_jump(
|
||||||
env,
|
env,
|
||||||
function_value,
|
function_value,
|
||||||
evaluator,
|
evaluator,
|
||||||
&evaluator_arguments,
|
&evaluator_arguments,
|
||||||
*return_layout,
|
*return_layout,
|
||||||
)
|
);
|
||||||
} else {
|
|
||||||
call_roc_function(env, evaluator, return_layout, &evaluator_arguments)
|
|
||||||
};
|
|
||||||
|
|
||||||
builder.build_store(output, call_result);
|
builder.build_store(output, call_result);
|
||||||
|
} else {
|
||||||
|
let call_result = call_roc_function(env, evaluator, return_layout, &evaluator_arguments);
|
||||||
|
|
||||||
|
if return_layout.is_passed_by_reference() {
|
||||||
|
let align_bytes = return_layout.alignment_bytes(env.ptr_bytes);
|
||||||
|
|
||||||
|
if align_bytes > 0 {
|
||||||
|
let size = env
|
||||||
|
.ptr_int()
|
||||||
|
.const_int(return_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||||
|
|
||||||
|
env.builder
|
||||||
|
.build_memcpy(
|
||||||
|
output,
|
||||||
|
align_bytes,
|
||||||
|
call_result.into_pointer_value(),
|
||||||
|
align_bytes,
|
||||||
|
size,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
builder.build_store(output, call_result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
builder.build_return(None);
|
builder.build_return(None);
|
||||||
|
|
||||||
|
@ -4408,29 +4493,10 @@ pub fn call_roc_function<'a, 'ctx, 'env>(
|
||||||
let pass_by_pointer = roc_function.get_type().get_param_types().len() == arguments.len() + 1;
|
let pass_by_pointer = roc_function.get_type().get_param_types().len() == arguments.len() + 1;
|
||||||
|
|
||||||
match RocReturn::from_layout(env, result_layout) {
|
match RocReturn::from_layout(env, result_layout) {
|
||||||
RocReturn::ByPointer => {
|
RocReturn::ByPointer if !pass_by_pointer => {
|
||||||
if !pass_by_pointer {
|
// WARNING this is a hack!!
|
||||||
let mut arguments = Vec::from_iter_in(arguments.iter().copied(), env.arena);
|
|
||||||
arguments.pop();
|
|
||||||
|
|
||||||
let result_type = basic_type_from_layout(env, result_layout);
|
|
||||||
let result_alloca = env.builder.build_alloca(result_type, "result_value");
|
|
||||||
|
|
||||||
arguments.push(result_alloca.into());
|
|
||||||
|
|
||||||
debug_assert_eq!(
|
|
||||||
roc_function.get_type().get_param_types().len(),
|
|
||||||
arguments.len()
|
|
||||||
);
|
|
||||||
let call = env.builder.build_call(roc_function, &arguments, "call");
|
|
||||||
|
|
||||||
// roc functions should have the fast calling convention
|
|
||||||
debug_assert_eq!(roc_function.get_call_conventions(), FAST_CALL_CONV);
|
|
||||||
call.set_call_convention(FAST_CALL_CONV);
|
|
||||||
|
|
||||||
return env.builder.build_load(result_alloca, "load_result");
|
|
||||||
}
|
|
||||||
let mut arguments = Vec::from_iter_in(arguments.iter().copied(), env.arena);
|
let mut arguments = Vec::from_iter_in(arguments.iter().copied(), env.arena);
|
||||||
|
arguments.pop();
|
||||||
|
|
||||||
let result_type = basic_type_from_layout(env, result_layout);
|
let result_type = basic_type_from_layout(env, result_layout);
|
||||||
let result_alloca = env.builder.build_alloca(result_type, "result_value");
|
let result_alloca = env.builder.build_alloca(result_type, "result_value");
|
||||||
|
@ -4449,6 +4515,31 @@ pub fn call_roc_function<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
env.builder.build_load(result_alloca, "load_result")
|
env.builder.build_load(result_alloca, "load_result")
|
||||||
}
|
}
|
||||||
|
RocReturn::ByPointer => {
|
||||||
|
let mut arguments = Vec::from_iter_in(arguments.iter().copied(), env.arena);
|
||||||
|
|
||||||
|
let result_type = basic_type_from_layout(env, result_layout);
|
||||||
|
let result_alloca = env.builder.build_alloca(result_type, "result_value");
|
||||||
|
|
||||||
|
arguments.push(result_alloca.into());
|
||||||
|
|
||||||
|
debug_assert_eq!(
|
||||||
|
roc_function.get_type().get_param_types().len(),
|
||||||
|
arguments.len()
|
||||||
|
);
|
||||||
|
let call = env.builder.build_call(roc_function, &arguments, "call");
|
||||||
|
|
||||||
|
// roc functions should have the fast calling convention
|
||||||
|
debug_assert_eq!(roc_function.get_call_conventions(), FAST_CALL_CONV);
|
||||||
|
call.set_call_convention(FAST_CALL_CONV);
|
||||||
|
|
||||||
|
if result_layout.is_passed_by_reference() {
|
||||||
|
result_alloca.into()
|
||||||
|
} else {
|
||||||
|
env.builder
|
||||||
|
.build_load(result_alloca, "return_by_pointer_load_result")
|
||||||
|
}
|
||||||
|
}
|
||||||
RocReturn::Return => {
|
RocReturn::Return => {
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
roc_function.get_type().get_param_types().len(),
|
roc_function.get_type().get_param_types().len(),
|
||||||
|
|
|
@ -76,6 +76,66 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn basic_type_from_layout_1<'a, 'ctx, 'env>(
|
||||||
|
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
|
||||||
|
layout: &Layout<'_>,
|
||||||
|
) -> BasicTypeEnum<'ctx> {
|
||||||
|
use Layout::*;
|
||||||
|
|
||||||
|
match layout {
|
||||||
|
Struct(sorted_fields) => basic_type_from_record(env, sorted_fields),
|
||||||
|
LambdaSet(lambda_set) => {
|
||||||
|
basic_type_from_layout_1(env, &lambda_set.runtime_representation())
|
||||||
|
}
|
||||||
|
Union(union_layout) => {
|
||||||
|
use UnionLayout::*;
|
||||||
|
|
||||||
|
let tag_id_type = basic_type_from_layout_1(env, &union_layout.tag_id_layout());
|
||||||
|
|
||||||
|
match union_layout {
|
||||||
|
NonRecursive(tags) => {
|
||||||
|
let data = block_of_memory_slices(env.context, tags, env.ptr_bytes);
|
||||||
|
let struct_type = env.context.struct_type(&[data, tag_id_type], false);
|
||||||
|
|
||||||
|
struct_type.ptr_type(AddressSpace::Generic).into()
|
||||||
|
}
|
||||||
|
Recursive(tags)
|
||||||
|
| NullableWrapped {
|
||||||
|
other_tags: tags, ..
|
||||||
|
} => {
|
||||||
|
let data = block_of_memory_slices(env.context, tags, env.ptr_bytes);
|
||||||
|
|
||||||
|
if union_layout.stores_tag_id_as_data(env.ptr_bytes) {
|
||||||
|
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.ptr_bytes);
|
||||||
|
block.ptr_type(AddressSpace::Generic).into()
|
||||||
|
}
|
||||||
|
NonNullableUnwrapped(fields) => {
|
||||||
|
let block = block_of_memory_slices(env.context, &[fields], env.ptr_bytes);
|
||||||
|
block.ptr_type(AddressSpace::Generic).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RecursivePointer => {
|
||||||
|
// TODO make this dynamic
|
||||||
|
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_builtin<'a, 'ctx, 'env>(
|
pub fn basic_type_from_builtin<'a, 'ctx, 'env>(
|
||||||
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
|
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
|
||||||
builtin: &Builtin<'_>,
|
builtin: &Builtin<'_>,
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use crate::debug_info_init;
|
use crate::debug_info_init;
|
||||||
use crate::llvm::bitcode::call_void_bitcode_fn;
|
use crate::llvm::bitcode::call_void_bitcode_fn;
|
||||||
use crate::llvm::build::{
|
use crate::llvm::build::{
|
||||||
add_func, cast_basic_basic, cast_block_of_memory_to_tag, get_tag_id, get_tag_id_non_recursive,
|
add_func, cast_basic_basic, get_tag_id, tag_pointer_clear_tag_id, Env, FAST_CALL_CONV,
|
||||||
tag_pointer_clear_tag_id, Env, FAST_CALL_CONV, TAG_DATA_INDEX, TAG_ID_INDEX,
|
TAG_DATA_INDEX, TAG_ID_INDEX,
|
||||||
};
|
};
|
||||||
use crate::llvm::build_list::{incrementing_elem_loop, list_len, load_list};
|
use crate::llvm::build_list::{incrementing_elem_loop, list_len, load_list};
|
||||||
use crate::llvm::convert::{basic_type_from_layout, ptr_int};
|
use crate::llvm::convert::{basic_type_from_layout, basic_type_from_layout_1, ptr_int};
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use inkwell::basic_block::BasicBlock;
|
use inkwell::basic_block::BasicBlock;
|
||||||
use inkwell::context::Context;
|
use inkwell::context::Context;
|
||||||
|
@ -542,24 +542,6 @@ fn modify_refcount_layout_help<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
if let Layout::Union(UnionLayout::NonRecursive(_)) = layout {
|
|
||||||
let alloca = env.builder.build_alloca(value.get_type(), "tag_union");
|
|
||||||
env.builder.build_store(alloca, value);
|
|
||||||
call_help(env, function, call_mode, alloca.into());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Layout::LambdaSet(lambda_set) = layout {
|
|
||||||
if let Layout::Union(UnionLayout::NonRecursive(_)) =
|
|
||||||
lambda_set.runtime_representation()
|
|
||||||
{
|
|
||||||
let alloca = env.builder.build_alloca(value.get_type(), "tag_union");
|
|
||||||
env.builder.build_store(alloca, value);
|
|
||||||
call_help(env, function, call_mode, alloca.into());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
call_help(env, function, call_mode, value);
|
call_help(env, function, call_mode, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1620,10 +1602,9 @@ fn modify_refcount_union<'a, 'ctx, 'env>(
|
||||||
let function = match env.module.get_function(fn_name.as_str()) {
|
let function = match env.module.get_function(fn_name.as_str()) {
|
||||||
Some(function_value) => function_value,
|
Some(function_value) => function_value,
|
||||||
None => {
|
None => {
|
||||||
let basic_type = basic_type_from_layout(env, &layout);
|
let basic_type = basic_type_from_layout_1(env, &layout);
|
||||||
// we pass tag unions by reference for efficiency
|
let function_value = build_header(env, basic_type, mode, &fn_name);
|
||||||
let ptr_type = basic_type.ptr_type(AddressSpace::Generic).into();
|
dbg!(function_value);
|
||||||
let function_value = build_header(env, ptr_type, mode, &fn_name);
|
|
||||||
|
|
||||||
modify_refcount_union_help(
|
modify_refcount_union_help(
|
||||||
env,
|
env,
|
||||||
|
@ -1675,6 +1656,8 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
let before_block = env.builder.get_insert_block().expect("to be in a function");
|
let before_block = env.builder.get_insert_block().expect("to be in a function");
|
||||||
|
|
||||||
|
dbg!(fn_val, arg_ptr);
|
||||||
|
|
||||||
// read the tag_id
|
// read the tag_id
|
||||||
let tag_id_ptr = env
|
let tag_id_ptr = env
|
||||||
.builder
|
.builder
|
||||||
|
|
|
@ -863,6 +863,16 @@ impl<'a> Layout<'a> {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_passed_by_reference(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Layout::Union(UnionLayout::NonRecursive(_)) => true,
|
||||||
|
Layout::LambdaSet(lambda_set) => {
|
||||||
|
lambda_set.runtime_representation().is_passed_by_reference()
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn stack_size(&self, pointer_size: u32) -> u32 {
|
pub fn stack_size(&self, pointer_size: u32) -> u32 {
|
||||||
let width = self.stack_size_without_alignment(pointer_size);
|
let width = self.stack_size_without_alignment(pointer_size);
|
||||||
let alignment = self.alignment_bytes(pointer_size);
|
let alignment = self.alignment_bytes(pointer_size);
|
||||||
|
@ -941,16 +951,16 @@ impl<'a> Layout<'a> {
|
||||||
})
|
})
|
||||||
.max();
|
.max();
|
||||||
|
|
||||||
|
let tag_id_builtin = variant.tag_id_builtin();
|
||||||
match max_alignment {
|
match max_alignment {
|
||||||
Some(align) => {
|
Some(align) => round_up_to_alignment(
|
||||||
let tag_id_builtin = variant.tag_id_builtin();
|
align.max(tag_id_builtin.alignment_bytes(pointer_size)),
|
||||||
|
tag_id_builtin.alignment_bytes(pointer_size),
|
||||||
round_up_to_alignment(
|
),
|
||||||
align,
|
None => {
|
||||||
tag_id_builtin.alignment_bytes(pointer_size),
|
// none of the tags had any payload, but the tag id still contains information
|
||||||
)
|
tag_id_builtin.alignment_bytes(pointer_size)
|
||||||
}
|
}
|
||||||
None => 0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Recursive(_)
|
Recursive(_)
|
||||||
|
@ -2675,3 +2685,27 @@ impl<'a> std::convert::TryFrom<&Layout<'a>> for ListLayout<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn width_and_alignment_union_empty_struct() {
|
||||||
|
let lambda_set = LambdaSet {
|
||||||
|
set: &[(Symbol::LIST_MAP, &[])],
|
||||||
|
representation: &Layout::Struct(&[]),
|
||||||
|
};
|
||||||
|
|
||||||
|
let a = &[Layout::Struct(&[])] as &[_];
|
||||||
|
let b = &[Layout::LambdaSet(lambda_set)] as &[_];
|
||||||
|
let tt = [a, b];
|
||||||
|
|
||||||
|
let layout = Layout::Union(UnionLayout::NonRecursive(&tt));
|
||||||
|
|
||||||
|
// at the moment, the tag id uses an I64, so
|
||||||
|
let ptr_width = 8;
|
||||||
|
assert_eq!(layout.stack_size(ptr_width), 8);
|
||||||
|
assert_eq!(layout.alignment_bytes(ptr_width), 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
use crate::assert_evals_to;
|
use crate::assert_evals_to;
|
||||||
// use crate::assert_wasm_evals_to as assert_evals_to;
|
// use crate::assert_wasm_evals_to as assert_evals_to;
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
|
use roc_mono::layout::LambdaSet;
|
||||||
use roc_std::{RocList, RocStr};
|
use roc_std::{RocList, RocStr};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -423,7 +424,7 @@ fn result_with_guard_pattern() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn maybe_is_just() {
|
fn maybe_is_just_not_nested() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
|
|
|
@ -198,6 +198,10 @@ fn create_llvm_module<'a>(
|
||||||
if name.starts_with("roc_builtins.dict") {
|
if name.starts_with("roc_builtins.dict") {
|
||||||
function.add_attribute(AttributeLoc::Function, attr);
|
function.add_attribute(AttributeLoc::Function, attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if name.starts_with("roc_builtins.list") {
|
||||||
|
function.add_attribute(AttributeLoc::Function, attr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile and add all the Procs before adding main
|
// Compile and add all the Procs before adding main
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue