First pass at some List implementation stuff

This commit is contained in:
Richard Feldman 2020-03-10 03:01:01 -04:00
parent 80722b872a
commit 4c19dd86ff
4 changed files with 75 additions and 22 deletions

View file

@ -233,9 +233,34 @@ pub fn build_expr<'a, B: Backend>(
}
}
Array { elem_layout, elems } => {
let cfg = env.cfg;
let ptr_bytes = cfg.pointer_bytes() as u32;
if elems.is_empty() {
panic!("TODO build an empty Array in Crane");
let slot = builder.create_stack_slot(StackSlotData::new(
StackSlotKind::ExplicitSlot,
ptr_bytes + 64,
));
let ptr_type = cfg.pointer_type();
let null_ptr = builder.ins().null(ptr_type);
let zero = builder.ins().iconst(Type::int(32).unwrap(), 0);
// Initialize a null pointer for the pointer
builder.ins().stack_store(null_ptr, slot, Offset32::new(0));
// Set both length and capacity to 0
let ptr_bytes = ptr_bytes as i32;
builder
.ins()
.stack_store(zero, slot, Offset32::new(ptr_bytes));
builder
.ins()
.stack_store(zero, slot, Offset32::new(ptr_bytes + 32));
builder.ins().stack_addr(ptr_type, slot, Offset32::new(0))
} else {
panic!("TODO make this work like the empty List, then verify that they both actually work");
let elem_bytes = elem_layout.stack_size(env.cfg.pointer_bytes() as u32) as usize;
let bytes_len = (elem_bytes * elems.len()) + 1/* TODO drop the +1 when we have structs and this is no longer NUL-terminated. */;
let ptr = call_malloc(env, module, builder, bytes_len);
@ -574,6 +599,22 @@ fn call_by_name<'a, B: Backend>(
builder.ins().ineg(num)
}
Symbol::LIST_LEN => {
debug_assert!(args.len() == 1);
let list = build_arg(&args[0], env, scope, module, builder, procs);
// Get the 32-bit int length
let i32_val = builder.ins().load_complex(
Type::int(32).unwrap(),
MemFlags::new(),
&[list],
Offset32::new(0),
);
// Cast the 32-bit int length to a 64-bit integer
builder.ins().bitcast(Type::int(64).unwrap(), i32_val)
}
Symbol::LIST_GET_UNSAFE => {
debug_assert!(args.len() == 2);
@ -667,7 +708,7 @@ fn call_by_name<'a, B: Backend>(
let fn_id = match scope.get(&symbol) {
Some(ScopeEntry::Func{ func_id, .. }) => *func_id,
other => panic!(
"CallByName could not find function named {:?} in scope; instead, found {:?} in scope {:?}",
"CallByName could not find function named {:?} declared in scope (and it was not special-cased in crane::build as a builtin); instead, found {:?} in scope {:?}",
symbol, other, scope
),
};

View file

@ -14,7 +14,7 @@ use crate::llvm::convert::{
use roc_collections::all::ImMap;
use roc_module::symbol::{Interns, Symbol};
use roc_mono::expr::{Expr, Proc, Procs};
use roc_mono::layout::Layout;
use roc_mono::layout::{Builtin, Layout};
/// This is for Inkwell's FunctionValue::verify - we want to know the verification
/// output in debug builds, but we don't want it to print to stdout in release builds!
@ -222,7 +222,7 @@ pub fn build_expr<'a, 'ctx, 'env>(
.build_insert_value(
struct_type.const_zero(),
BasicValueEnum::PointerValue(ptr_type.const_null()),
0,
Builtin::WRAPPER_PTR,
"insert_ptr",
)
.unwrap();
@ -258,17 +258,27 @@ pub fn build_expr<'a, 'ctx, 'env>(
// Field 0: pointer
struct_val = builder
.build_insert_value(struct_type.const_zero(), ptr_val, 0, "insert_ptr")
.build_insert_value(
struct_type.const_zero(),
ptr_val,
Builtin::WRAPPER_PTR,
"insert_ptr",
)
.unwrap();
// Field 1: length
struct_val = builder
.build_insert_value(struct_val, len, 1, "insert_len")
.build_insert_value(struct_val, len, Builtin::WRAPPER_LEN, "insert_len")
.unwrap();
// Field 2: capacity (initially set to length)
struct_val = builder
.build_insert_value(struct_val, len, 2, "insert_capacity")
.build_insert_value(
struct_val,
len,
Builtin::WRAPPER_CAPACITY,
"insert_capacity",
)
.unwrap();
BasicValueEnum::StructValue(struct_val.into_struct_value())
@ -615,7 +625,7 @@ fn call_with_args<'a, 'ctx, 'env>(
let builder = env.builder;
// Get the 32-bit int length
let i32_val = builder.build_extract_value(wrapper_struct, 1, "unwrapped_list_len").unwrap().into_int_value();
let i32_val = builder.build_extract_value(wrapper_struct, Builtin::WRAPPER_LEN, "unwrapped_list_len").unwrap().into_int_value();
// cast the 32-bit length to a 64-bit int
BasicValueEnum::IntValue(builder.build_int_cast(i32_val, env.context.i64_type(), "i32_to_i64"))
@ -641,12 +651,12 @@ fn call_with_args<'a, 'ctx, 'env>(
let elem_index = args[1].into_int_value();
// Slot 1 in the wrapper struct is the length
let _list_len = builder.build_extract_value(wrapper_struct, 1, "unwrapped_list_len").unwrap().into_int_value();
let _list_len = builder.build_extract_value(wrapper_struct, Builtin::WRAPPER_LEN, "unwrapped_list_len").unwrap().into_int_value();
// TODO here, check to see if the requested index exceeds the length of the array.
// Slot 0 in the wrapper struct is the pointer to the array data
let array_data_ptr = builder.build_extract_value(wrapper_struct, 0, "unwrapped_list_ptr").unwrap().into_pointer_value();
let array_data_ptr = builder.build_extract_value(wrapper_struct, Builtin::WRAPPER_PTR, "unwrapped_list_ptr").unwrap().into_pointer_value();
let elem_bytes = 8; // TODO Look this size up instead of hardcoding it!
let elem_size = env.context.i64_type().const_int(elem_bytes, false);
@ -669,13 +679,13 @@ fn call_with_args<'a, 'ctx, 'env>(
let elem = args[2];
// Slot 1 in the wrapper struct is the length
let _list_len = builder.build_extract_value(wrapper_struct, 1, "unwrapped_list_len").unwrap().into_int_value();
let _list_len = builder.build_extract_value(wrapper_struct, Builtin::WRAPPER_LEN, "unwrapped_list_len").unwrap().into_int_value();
// TODO here, check to see if the requested index exceeds the length of the array.
// If so, bail out and return the list unaltered.
// Slot 0 in the wrapper struct is the pointer to the array data
let array_data_ptr = builder.build_extract_value(wrapper_struct, 0, "unwrapped_list_ptr").unwrap().into_pointer_value();
let array_data_ptr = builder.build_extract_value(wrapper_struct, Builtin::WRAPPER_PTR, "unwrapped_list_ptr").unwrap().into_pointer_value();
let elem_bytes = 8; // TODO Look this size up instead of hardcoding it!
let elem_size = env.context.i64_type().const_int(elem_bytes, false);

View file

@ -488,14 +488,13 @@ mod test_gen {
// }
#[test]
fn int_list_len() {
// assert_evals_to!("List.len [ 12, 9, 6, 3 ]", 4, i64);
assert_llvm_evals_to!("List.len [ 12, 9, 6, 3 ]", 4, i64, |x| x);
fn basic_int_list_len() {
assert_evals_to!("List.len [ 12, 9, 6, 3 ]", 4, i64);
}
#[test]
fn loaded_int_list_len() {
assert_llvm_evals_to!(
assert_evals_to!(
indoc!(
r#"
nums = [ 2, 4, 6 ]
@ -504,14 +503,13 @@ mod test_gen {
"#
),
3,
i64,
|x| x
i64
);
}
#[test]
fn fn_int_list_len() {
assert_llvm_evals_to!(
assert_evals_to!(
indoc!(
r#"
# TODO remove this annotation once monomorphization works!
@ -524,14 +522,13 @@ mod test_gen {
"#
),
3,
i64,
|x| x
i64
);
}
// #[test]
// fn int_list_is_empty() {
// assert_evals_to!("List.is_empty [ 12, 9, 6, 3 ]", 0, i32, |x| x);
// assert_evals_to!("List.is_empty [ 12, 9, 6, 3 ]", 0, u8, |x| x);
// }
#[test]

View file

@ -92,6 +92,11 @@ impl<'a> Builtin<'a> {
const SET_WORDS: u32 = Builtin::MAP_WORDS; // Set is an alias for Map with {} for value
const LIST_WORDS: u32 = 3;
/// Layout of collection wrapper - a struct of (pointer, length, capacity)
pub const WRAPPER_PTR: u32 = 0;
pub const WRAPPER_LEN: u32 = 1;
pub const WRAPPER_CAPACITY: u32 = 2;
pub fn stack_size(&self, pointer_size: u32) -> u32 {
use Builtin::*;