Fix Cranelift gen of nonempty lists

This commit is contained in:
Richard Feldman 2020-03-14 20:44:39 -04:00
parent 5beb65880c
commit 5bf82fa42c
3 changed files with 35 additions and 49 deletions

View file

@ -288,39 +288,18 @@ pub fn build_expr<'a, B: Backend>(
Array { elem_layout, elems } => { Array { elem_layout, elems } => {
let cfg = env.cfg; let cfg = env.cfg;
let ptr_bytes = cfg.pointer_bytes() as u32; let ptr_bytes = cfg.pointer_bytes() as u32;
let slot = builder.create_stack_slot(StackSlotData::new(
StackSlotKind::ExplicitSlot,
ptr_bytes * Builtin::LIST_WORDS,
));
if elems.is_empty() { let elems_ptr = if elems.is_empty() {
let slot = builder.create_stack_slot(StackSlotData::new( // Empty lists get a null pointer so they don't allocate on the heap
StackSlotKind::ExplicitSlot, builder.ins().iconst(cfg.pointer_type(), 0)
// 1 pointer-sized slot for the array pointer, and
// 1 pointer-sized slot for the length
ptr_bytes * 2,
));
// Set list pointer to null
{
// let null_ptr = builder.ins().null(ptr_type);
let zero = builder.ins().iconst(cfg.pointer_type(), 0);
builder.ins().stack_store(zero, slot, Offset32::new(0));
}
// Set length to 0
{
let zero = builder.ins().iconst(cfg.pointer_type(), 0);
builder
.ins()
.stack_store(zero, slot, Offset32::new(ptr_bytes as i32));
}
// Return the pointer
builder.ins().stack_addr(cfg.pointer_type(), slot, Offset32::new(0))
} else { } else {
panic!("TODO make this work like the empty List, then verify that they both actually work"); let elem_bytes = elem_layout.stack_size(ptr_bytes as u32);
let elem_bytes = elem_layout.stack_size(env.cfg.pointer_bytes() as u32) as usize; let bytes_len = elem_bytes as usize * elems.len();
let bytes_len = (elem_bytes * elems.len()) + 1/* TODO drop the +1 when we have structs and this is no longer NUL-terminated. */; let elems_ptr = call_malloc(env, module, builder, bytes_len);
let ptr = call_malloc(env, module, builder, bytes_len);
let mem_flags = MemFlags::new(); let mem_flags = MemFlags::new();
// Copy the elements from the literal into the array // Copy the elements from the literal into the array
@ -328,20 +307,28 @@ pub fn build_expr<'a, B: Backend>(
let offset = Offset32::new(elem_bytes as i32 * index as i32); let offset = Offset32::new(elem_bytes as i32 * index as i32);
let val = build_expr(env, scope, module, builder, elem, procs); let val = build_expr(env, scope, module, builder, elem, procs);
builder.ins().store(mem_flags, val, ptr, offset); builder.ins().store(mem_flags, val, elems_ptr, offset);
} }
// Add a NUL terminator at the end. elems_ptr
// TODO: Instead of NUL-terminating, return a struct };
// with the pointer and also the length and capacity.
let nul_terminator = builder.ins().iconst(types::I8, 0);
let index = bytes_len as i32 - 1;
let offset = Offset32::new(index);
builder.ins().store(mem_flags, nul_terminator, ptr, offset); // Store the pointer in slot 0
builder
.ins()
.stack_store(elems_ptr, slot, Offset32::new(0));
ptr // Store the length in slot 1
{
let length = builder.ins().iconst(env.ptr_sized_int(), elems.len() as i64);
builder
.ins()
.stack_store(length, slot, Offset32::new(ptr_bytes as i32));
} }
// Return the pointer to the wrapper
builder.ins().stack_addr(cfg.pointer_type(), slot, Offset32::new(0))
} }
_ => { _ => {
panic!("I don't yet know how to crane build {:?}", expr); panic!("I don't yet know how to crane build {:?}", expr);
@ -680,7 +667,7 @@ fn call_by_name<'a, B: Backend>(
env.ptr_sized_int(), env.ptr_sized_int(),
MemFlags::new(), MemFlags::new(),
list_ptr, list_ptr,
Offset32::new(0), Offset32::new(env.cfg.pointer_bytes() as i32),
) )
} }
Symbol::LIST_GET_UNSAFE => { Symbol::LIST_GET_UNSAFE => {

View file

@ -507,7 +507,7 @@ mod test_gen {
"# "#
), ),
3, 3,
i64 usize
); );
} }
@ -517,7 +517,6 @@ mod test_gen {
indoc!( indoc!(
r#" r#"
# TODO remove this annotation once monomorphization works! # TODO remove this annotation once monomorphization works!
getLen : List Int -> Int
getLen = \list -> List.len list getLen = \list -> List.len list
nums = [ 2, 4, 6 ] nums = [ 2, 4, 6 ]
@ -526,13 +525,13 @@ mod test_gen {
"# "#
), ),
3, 3,
i64 usize
); );
} }
// #[test] // #[test]
// fn int_list_is_empty() { // fn int_list_is_empty() {
// assert_evals_to!("List.is_empty [ 12, 9, 6, 3 ]", 0, u8, |x| x); // assert_evals_to!("List.isEmpty [ 12, 9, 6, 3 ]", 0, u8, |x| x);
// } // }
#[test] #[test]

View file

@ -104,10 +104,10 @@ impl<'a> Builtin<'a> {
const BYTE_SIZE: u32 = std::mem::size_of::<u8>() as u32; const BYTE_SIZE: u32 = std::mem::size_of::<u8>() as u32;
/// Number of machine words in an empty one of these /// Number of machine words in an empty one of these
const STR_WORDS: u32 = 3; pub const STR_WORDS: u32 = 2;
const MAP_WORDS: u32 = 6; pub const MAP_WORDS: u32 = 6;
const SET_WORDS: u32 = Builtin::MAP_WORDS; // Set is an alias for Map with {} for value pub const SET_WORDS: u32 = Builtin::MAP_WORDS; // Set is an alias for Map with {} for value
const LIST_WORDS: u32 = 3; pub const LIST_WORDS: u32 = 2;
/// Layout of collection wrapper - a struct of (pointer, length, capacity) /// Layout of collection wrapper - a struct of (pointer, length, capacity)
pub const WRAPPER_PTR: u32 = 0; pub const WRAPPER_PTR: u32 = 0;