This commit is contained in:
Folkert 2021-11-05 21:30:20 +01:00
parent 51756cbc89
commit 5cd232816b
8 changed files with 337 additions and 156 deletions

View file

@ -1,7 +1,7 @@
/// Helpers for interacting with the zig that generates bitcode
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::convert::basic_type_from_layout;
use crate::llvm::convert::{basic_type_from_layout, basic_type_from_layout_1};
use crate::llvm::refcounting::{
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] => {
let tag_type = basic_type_from_layout(env, &Layout::Union(union_layout));
let argument_cast = env
.builder
.build_bitcast(
*tag_value_ptr,
let tag_value = env.builder.build_pointer_cast(
tag_value_ptr.into_pointer_value(),
tag_type.ptr_type(AddressSpace::Generic),
"load_opaque",
)
.into_pointer_value();
let tag_value = env.builder.build_load(argument_cast, "get_value");
"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);
let tag_id_i64 = crate::llvm::build::get_tag_id(
env,
function_value,
&union_layout,
tag_value.into(),
);
env.builder
.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) {
let basic_type = basic_type_from_layout(env, layout).ptr_type(AddressSpace::Generic);
let argument = if layout.is_passed_by_reference() {
env.builder
.build_pointer_cast(
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")
.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);
}
@ -300,17 +309,10 @@ fn build_transform_caller_help<'a, 'ctx, 'env>(
let result_u8_ptr = function_value
.get_nth_param(argument_layouts.len() as u32 + 1)
.unwrap();
let result_ptr = env
.builder
.build_bitcast(
result_u8_ptr,
result.get_type().ptr_type(AddressSpace::Generic),
"write_result",
)
.unwrap()
.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.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 = if layout.is_passed_by_reference() {
env.builder
.build_pointer_cast(value_ptr, value_type, "cast_ptr_to_tag")
.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 {
Mode::Inc => {

View file

@ -21,7 +21,8 @@ use crate::llvm::build_str::{
};
use crate::llvm::compare::{generic_eq, generic_neq};
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::{
build_reset, decrement_refcount_layout, increment_refcount_layout, PointerToRefcount,
@ -618,8 +619,8 @@ pub fn construct_optimization_passes<'a>(
mpm.add_always_inliner_pass();
// tail-call elimination is always on
fpm.add_instruction_combining_pass();
fpm.add_tail_call_elimination_pass();
// fpm.add_instruction_combining_pass();
// fpm.add_tail_call_elimination_pass();
let pmb = PassManagerBuilder::create();
match opt_level {
@ -1235,32 +1236,21 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
match union_layout {
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 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");
builder.build_store(argument_pointer, argument);
let struct_ptr = builder.build_pointer_cast(
argument_pointer,
struct_type.ptr_type(AddressSpace::Generic),
"foobar",
);
// 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")
lookup_at_index_ptr2(
env,
union_layout,
tag_id_type,
field_layouts,
*index as usize,
argument.into_pointer_value(),
)
}
UnionLayout::Recursive(tag_layouts) => {
debug_assert!(argument.is_pointer_value());
@ -1555,10 +1545,13 @@ pub fn build_tag<'a, 'ctx, 'env>(
.builder
.build_struct_gep(struct_ptr, index, "get_tag_field_ptr")
.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) => {
debug_assert!(union_size > 1);
@ -1866,9 +1859,10 @@ pub fn get_tag_id<'a, 'ctx, 'env>(
match union_layout {
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(_) => {
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")
.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) {
// 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);
unsafe {
builder
.build_bitcast(
env.builder.build_in_bounds_gep(
as_usize_ptr,
&[index_intvalue],
"get_data_ptr",
),
builder.build_pointer_cast(
env.builder
.build_in_bounds_gep(as_usize_ptr, &[index_intvalue], "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>(
env: &Env<'a, 'ctx, 'env>,
scope: &Scope<'a, 'ctx>,
elem_layout: &Layout<'a>,
element_layout: &Layout<'a>,
elems: &[ListLiteralElement],
) -> BasicValueEnum<'ctx> {
let ctx = env.context;
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_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 false {
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 alignment = elem_layout
let alignment = element_layout
.alignment_bytes(env.ptr_bytes)
.max(env.ptr_bytes);
@ -2241,7 +2249,7 @@ fn list_literal<'a, 'ctx, 'env>(
for (index, element) in elems.iter().enumerate() {
match element {
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());
}
ListLiteralElement::Symbol(symbol) => {
@ -2296,7 +2304,7 @@ fn list_literal<'a, 'ctx, 'env>(
super::build_list::store_list(env, ptr, list_length_intval)
} else {
// 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
env.builder
@ -2320,26 +2328,69 @@ fn list_literal<'a, 'ctx, 'env>(
super::build_list::store_list(env, ptr, list_length_intval)
}
} 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
for (index, element) in elems.iter().enumerate() {
let val = match element {
ListLiteralElement::Literal(literal) => {
build_exp_literal(env, elem_layout, literal)
build_exp_literal(env, element_layout, literal)
}
ListLiteralElement::Symbol(symbol) => load_symbol(scope, symbol),
};
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") };
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)
}
}
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>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
@ -2415,8 +2466,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
let out_parameter = parameters.last().unwrap();
debug_assert!(out_parameter.is_pointer_value());
env.builder
.build_store(out_parameter.into_pointer_value(), value);
store_roc_value(env, *layout, out_parameter.into_pointer_value(), value);
if let Some(block) = env.builder.get_insert_block() {
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 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);
@ -3721,7 +3771,7 @@ fn set_jump_and_catch_long_jump<'a, 'ctx, 'env>(
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>(
@ -3741,12 +3791,13 @@ fn make_exception_catcher<'a, 'ctx, 'env>(
fn make_good_roc_result<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
return_layout: Layout<'a>,
return_value: BasicValueEnum<'ctx>,
) -> BasicValueEnum<'ctx> {
let context = env.context;
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 =
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")
.unwrap();
let v3 = builder
let v3 = if return_layout.is_passed_by_reference() {
let loaded = env.builder.build_load(
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();
.unwrap()
};
v3.into_struct_value().into()
}
@ -3971,6 +4032,8 @@ fn build_procedures_help<'a, 'ctx, 'env>(
app_ll_file,
);
} else {
env.module.print_to_stderr();
panic!(
"The preceding code was from {:?}, which failed LLVM verification in {} build.",
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);
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);
}
@ -4132,19 +4195,41 @@ pub fn build_closure_caller<'a, 'ctx, 'env>(
}
}
let call_result = if env.is_gen_test {
set_jump_and_catch_long_jump(
if env.is_gen_test {
let call_result = set_jump_and_catch_long_jump(
env,
function_value,
evaluator,
&evaluator_arguments,
*return_layout,
)
} else {
call_roc_function(env, evaluator, return_layout, &evaluator_arguments)
};
);
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);
@ -4408,8 +4493,8 @@ pub fn call_roc_function<'a, 'ctx, 'env>(
let pass_by_pointer = roc_function.get_type().get_param_types().len() == arguments.len() + 1;
match RocReturn::from_layout(env, result_layout) {
RocReturn::ByPointer => {
if !pass_by_pointer {
RocReturn::ByPointer if !pass_by_pointer => {
// WARNING this is a hack!!
let mut arguments = Vec::from_iter_in(arguments.iter().copied(), env.arena);
arguments.pop();
@ -4428,8 +4513,9 @@ pub fn call_roc_function<'a, 'ctx, 'env>(
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");
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);
@ -4447,7 +4533,12 @@ pub fn call_roc_function<'a, 'ctx, 'env>(
debug_assert_eq!(roc_function.get_call_conventions(), FAST_CALL_CONV);
call.set_call_convention(FAST_CALL_CONV);
env.builder.build_load(result_alloca, "load_result")
if result_layout.is_passed_by_reference() {
result_alloca.into()
} else {
env.builder
.build_load(result_alloca, "return_by_pointer_load_result")
}
}
RocReturn::Return => {
debug_assert_eq!(

View file

@ -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>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
builtin: &Builtin<'_>,

View file

@ -1,11 +1,11 @@
use crate::debug_info_init;
use crate::llvm::bitcode::call_void_bitcode_fn;
use crate::llvm::build::{
add_func, cast_basic_basic, cast_block_of_memory_to_tag, get_tag_id, get_tag_id_non_recursive,
tag_pointer_clear_tag_id, Env, FAST_CALL_CONV, TAG_DATA_INDEX, TAG_ID_INDEX,
add_func, cast_basic_basic, get_tag_id, tag_pointer_clear_tag_id, Env, FAST_CALL_CONV,
TAG_DATA_INDEX, TAG_ID_INDEX,
};
use crate::llvm::build_list::{incrementing_elem_loop, list_len, load_list};
use crate::llvm::convert::{basic_type_from_layout, ptr_int};
use crate::llvm::convert::{basic_type_from_layout, basic_type_from_layout_1, ptr_int};
use bumpalo::collections::Vec;
use inkwell::basic_block::BasicBlock;
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);
}
}
@ -1620,10 +1602,9 @@ fn modify_refcount_union<'a, 'ctx, 'env>(
let function = match env.module.get_function(fn_name.as_str()) {
Some(function_value) => function_value,
None => {
let basic_type = basic_type_from_layout(env, &layout);
// we pass tag unions by reference for efficiency
let ptr_type = basic_type.ptr_type(AddressSpace::Generic).into();
let function_value = build_header(env, ptr_type, mode, &fn_name);
let basic_type = basic_type_from_layout_1(env, &layout);
let function_value = build_header(env, basic_type, mode, &fn_name);
dbg!(function_value);
modify_refcount_union_help(
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");
dbg!(fn_val, arg_ptr);
// read the tag_id
let tag_id_ptr = env
.builder

View file

@ -863,6 +863,16 @@ impl<'a> Layout<'a> {
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 {
let width = self.stack_size_without_alignment(pointer_size);
let alignment = self.alignment_bytes(pointer_size);
@ -941,16 +951,16 @@ impl<'a> Layout<'a> {
})
.max();
match max_alignment {
Some(align) => {
let tag_id_builtin = variant.tag_id_builtin();
round_up_to_alignment(
align,
match max_alignment {
Some(align) => round_up_to_alignment(
align.max(tag_id_builtin.alignment_bytes(pointer_size)),
tag_id_builtin.alignment_bytes(pointer_size),
)
),
None => {
// none of the tags had any payload, but the tag id still contains information
tag_id_builtin.alignment_bytes(pointer_size)
}
None => 0,
}
}
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);
}
}

View file

@ -3,6 +3,7 @@
use crate::assert_evals_to;
// use crate::assert_wasm_evals_to as assert_evals_to;
use indoc::indoc;
use roc_mono::layout::LambdaSet;
use roc_std::{RocList, RocStr};
#[test]
@ -423,7 +424,7 @@ fn result_with_guard_pattern() {
}
#[test]
fn maybe_is_just() {
fn maybe_is_just_not_nested() {
assert_evals_to!(
indoc!(
r#"

View file

@ -198,6 +198,10 @@ fn create_llvm_module<'a>(
if name.starts_with("roc_builtins.dict") {
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