load/store all tags in llvm

This commit is contained in:
Folkert 2020-03-17 22:04:11 +01:00
parent 6253d2d1af
commit 165c5d8363
2 changed files with 130 additions and 36 deletions

View file

@ -11,7 +11,7 @@ use inkwell::{AddressSpace, FloatPredicate, IntPredicate};
use crate::llvm::convert::{
basic_type_from_layout, collection_wrapper, get_array_type, get_fn_type,
};
use roc_collections::all::ImMap;
use roc_collections::all::{ImMap, MutMap};
use roc_module::symbol::{Interns, Symbol};
use roc_mono::expr::{Expr, Proc, Procs};
use roc_mono::layout::{Builtin, Layout};
@ -359,6 +359,100 @@ pub fn build_expr<'a, 'ctx, 'env>(
BasicValueEnum::StructValue(struct_val.into_struct_value())
}
Tag {
tag_id,
arguments,
tag_layout,
..
} => {
// TODO make dynamic
let ptr_size = 8;
let whole_size = tag_layout.stack_size(ptr_size);
let mut filler = tag_layout.stack_size(ptr_size);
// put the discriminant in the first slot
let discriminant = (
Expr::Byte(*tag_id),
Layout::Builtin(Builtin::Byte(MutMap::default())),
);
let it = std::iter::once(&discriminant).chain(arguments.iter());
// let it = arguments.iter();
let ctx = env.context;
let builder = env.builder;
// Determine types
let num_fields = arguments.len() + 1;
let mut field_types = Vec::with_capacity_in(num_fields, env.arena);
let mut field_vals = Vec::with_capacity_in(num_fields, env.arena);
for (field_expr, field_layout) in it {
let val = build_expr(env, &scope, parent, field_expr, procs);
let field_type = basic_type_from_layout(env.arena, env.context, &field_layout);
field_types.push(field_type);
field_vals.push(val);
let field_size = field_layout.stack_size(ptr_size);
filler -= field_size;
}
if filler > 0 {
field_types.push(env.context.i8_type().array_type(filler).into());
}
// Create the struct_type
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
let mut struct_val = struct_type.const_zero().into();
// Insert field exprs into struct_val
for (index, field_val) in field_vals.into_iter().enumerate() {
struct_val = builder
.build_insert_value(struct_val, field_val, index as u32, "insert_field")
.unwrap();
}
// How we create tag values
//
// The memory layout of tags can be different. e.g. in
//
// [ Ok Int, Err Str ]
//
// the `Ok` tag stores a 64-bit integer, the `Err` tag stores a struct.
// All tags of a union must have the same length, for easy addressing (e.g. array lookups).
// So we need to ask for the maximum of all tag's sizes, but for most tags won't use
// all that memory, and certainly won't use it in the same way (the tags have fields of
// different types/sizes)
//
// In llvm, we must be explicit about the type of value we're creating: we can't just
// make a unspecified block of memory. So what we do is create a byte array of the
// desired size. Then when we know which tag we have (which is here, in this function),
// we need to cast that down to the array of bytes that llvm expects
//
// There is the bitcast instruction, but it doesn't work for arrays. So we need to jump
// through some hoops using store and load to get this to work.
//
// This tricks comes from
// https://github.com/raviqqe/ssf/blob/bc32aae68940d5bddf5984128e85af75ca4f4686/ssf-llvm/src/expression_compiler.rs#L116
let struct_pointer = builder.build_alloca(ctx.i8_type().array_type(whole_size), "");
builder.build_store(
builder
.build_bitcast(
struct_pointer,
struct_val
.into_struct_value()
.get_type()
.ptr_type(inkwell::AddressSpace::Generic),
"",
)
.into_pointer_value(),
struct_val,
);
builder.build_load(struct_pointer, "")
}
Access {
label,
field_layout,

View file

@ -1181,41 +1181,41 @@ mod test_gen {
);
}
// #[test]
// fn applied_tag_nothing() {
// assert_evals_to!(
// indoc!(
// r#"
// Maybe a : [ Just a, Nothing ]
//
// x : Maybe Int
// x = Nothing
//
// 0x1
// "#
// ),
// 1,
// i64
// );
// }
//
// #[test]
// fn applied_tag_just() {
// assert_evals_to!(
// indoc!(
// r#"
// Maybe a : [ Just a, Nothing ]
//
// y : Maybe Int
// y = Just 0x4
//
// 0x1
// "#
// ),
// 1,
// i64
// );
// }
#[test]
fn applied_tag_nothing() {
assert_evals_to!(
indoc!(
r#"
Maybe a : [ Just a, Nothing ]
x : Maybe Int
x = Nothing
0x1
"#
),
1,
i64
);
}
#[test]
fn applied_tag_just() {
assert_evals_to!(
indoc!(
r#"
Maybe a : [ Just a, Nothing ]
y : Maybe Int
y = Just 0x4
0x1
"#
),
1,
i64
);
}
// #[test]
// fn when_on_result() {