Merge in remote

This commit is contained in:
Chad Stearns 2020-08-30 15:38:43 -04:00
commit 8b31419b2c
4 changed files with 597 additions and 759 deletions

View file

@ -1,9 +1,9 @@
use crate::layout_id::LayoutIds;
use crate::llvm::build_list::{
allocate_list, build_basic_phi2, clone_nonempty_list, empty_list, empty_polymorphic_list,
incrementing_index_loop, list_append, list_concat, list_get_unsafe, list_is_not_empty,
incrementing_elem_loop, list_append, list_concat, list_get_unsafe, list_is_not_empty,
list_join, list_keep_if, list_len, list_map, list_prepend, list_repeat, list_reverse, list_set,
list_single, load_list_ptr,
list_single, load_list_ptr, store_list, LoopListArg,
};
use crate::llvm::compare::{build_eq, build_neq};
use crate::llvm::convert::{
@ -1674,9 +1674,9 @@ fn run_low_level<'a, 'ctx, 'env>(
// List.reverse : List elem -> List elem
debug_assert_eq!(args.len(), 1);
let list = &args[0];
let (list, list_layout) = load_symbol_and_layout(env, scope, &args[0]);
list_reverse(env, parent, scope, list)
list_reverse(env, parent, list, list_layout)
}
ListConcat => {
debug_assert_eq!(args.len(), 2);
@ -1730,9 +1730,8 @@ fn run_low_level<'a, 'ctx, 'env>(
debug_assert_eq!(args.len(), 1);
let (list, outer_list_layout) = load_symbol_and_layout(env, scope, &args[0]);
let outer_wrapper_struct = list.into_struct_value();
list_join(env, parent, outer_wrapper_struct, outer_list_layout)
list_join(env, parent, list, outer_list_layout)
}
NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumSin | NumCos | NumToFloat => {
debug_assert_eq!(args.len(), 1);
@ -2011,14 +2010,9 @@ fn str_concat<'a, 'ctx, 'env>(
let combined_str_ptr = allocate_list(env, &CHAR_LAYOUT, combined_str_len);
// FIRST LOOP
let first_loop = |first_index| {
let first_str_ptr = load_list_ptr(builder, first_str_wrapper, ptr_type);
// The pointer to the element in the first list
let first_str_char_ptr = unsafe {
builder.build_in_bounds_gep(first_str_ptr, &[first_index], "load_index")
};
let first_loop = |first_index, first_str_elem| {
// The pointer to the element in the combined list
let combined_str_elem_ptr = unsafe {
builder.build_in_bounds_gep(
@ -2028,19 +2022,20 @@ fn str_concat<'a, 'ctx, 'env>(
)
};
let first_str_elem = builder.build_load(first_str_char_ptr, "get_elem");
// Mutate the new array in-place to change the element.
builder.build_store(combined_str_elem_ptr, first_str_elem);
};
let index_name = "#index";
let index_alloca = incrementing_index_loop(
let index_alloca = incrementing_elem_loop(
builder,
parent,
ctx,
first_str_len,
LoopListArg {
ptr: first_str_ptr,
len: first_str_len,
},
index_name,
None,
first_loop,
@ -2050,14 +2045,9 @@ fn str_concat<'a, 'ctx, 'env>(
builder.build_store(index_alloca, ctx.i64_type().const_int(0, false));
// SECOND LOOP
let second_loop = |second_index| {
let second_str_ptr = load_list_ptr(builder, second_str_wrapper, ptr_type);
// The pointer to the element in the second list
let second_str_char_ptr = unsafe {
builder.build_in_bounds_gep(second_str_ptr, &[second_index], "load_index")
};
let second_loop = |second_index, second_str_elem| {
// The pointer to the element in the combined str.
// Note that the pointer does not start at the index
// 0, it starts at the index of first_str_len. In that
@ -2076,55 +2066,24 @@ fn str_concat<'a, 'ctx, 'env>(
)
};
let second_str_elem = builder.build_load(second_str_char_ptr, "get_elem");
// Mutate the new array in-place to change the element.
builder.build_store(combined_str_char_ptr, second_str_elem);
};
incrementing_index_loop(
incrementing_elem_loop(
builder,
parent,
ctx,
second_str_len,
LoopListArg {
ptr: second_str_ptr,
len: second_str_len,
},
index_name,
Some(index_alloca),
second_loop,
);
let ptr_bytes = env.ptr_bytes;
let int_type = ptr_int(ctx, ptr_bytes);
let ptr_as_int = builder.build_ptr_to_int(combined_str_ptr, int_type, "list_cast_ptr");
let struct_type = collection(ctx, ptr_bytes);
let mut struct_val;
// Store the pointer
struct_val = builder
.build_insert_value(
struct_type.get_undef(),
ptr_as_int,
Builtin::WRAPPER_PTR,
"insert_ptr",
)
.unwrap();
// Store the length
struct_val = builder
.build_insert_value(
struct_val,
combined_str_len,
Builtin::WRAPPER_LEN,
"insert_len",
)
.unwrap();
builder.build_bitcast(
struct_val.into_struct_value(),
collection(ctx, ptr_bytes),
"cast_collection",
)
store_list(env, combined_str_ptr, combined_str_len)
};
build_basic_phi2(

View file

@ -1,11 +1,10 @@
use crate::llvm::build::{load_symbol, load_symbol_and_layout, Env, InPlace, Scope};
use crate::llvm::build::{Env, InPlace};
use crate::llvm::convert::{basic_type_from_layout, collection, get_ptr_type, ptr_int};
use inkwell::builder::Builder;
use inkwell::context::Context;
use inkwell::types::{BasicTypeEnum, PointerType};
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
use inkwell::{AddressSpace, IntPredicate};
use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout, MemoryMode};
/// List.single : a -> List a
@ -35,35 +34,7 @@ pub fn list_single<'a, 'ctx, 'env>(
builder.build_store(elem_ptr, elem);
let ptr_bytes = env.ptr_bytes;
let int_type = ptr_int(ctx, ptr_bytes);
let ptr_as_int = builder.build_ptr_to_int(ptr, int_type, "list_cast_ptr");
let struct_type = collection(ctx, ptr_bytes);
let len = BasicValueEnum::IntValue(env.ptr_int().const_int(1, false));
let mut struct_val;
// Store the pointer
struct_val = builder
.build_insert_value(
struct_type.get_undef(),
ptr_as_int,
Builtin::WRAPPER_PTR,
"insert_ptr",
)
.unwrap();
// Store the length
struct_val = builder
.build_insert_value(struct_val, len, Builtin::WRAPPER_LEN, "insert_len")
.unwrap();
//
builder.build_bitcast(
struct_val.into_struct_value(),
collection(ctx, ptr_bytes),
"cast_collection",
)
store_list(env, ptr, env.ptr_int().const_int(1, false))
}
/// List.repeat : Int, elem -> List elem
@ -138,33 +109,7 @@ pub fn list_repeat<'a, 'ctx, 'env>(
builder.build_conditional_branch(end_cond, loop_bb, after_bb);
builder.position_at_end(after_bb);
let ptr_bytes = env.ptr_bytes;
let int_type = ptr_int(ctx, ptr_bytes);
let ptr_as_int = builder.build_ptr_to_int(list_ptr, int_type, "list_cast_ptr");
let struct_type = collection(ctx, ptr_bytes);
let mut struct_val;
// Store the pointer
struct_val = builder
.build_insert_value(
struct_type.get_undef(),
ptr_as_int,
Builtin::WRAPPER_PTR,
"insert_ptr",
)
.unwrap();
// Store the length
struct_val = builder
.build_insert_value(struct_val, list_len, Builtin::WRAPPER_LEN, "insert_len")
.unwrap();
builder.build_bitcast(
struct_val.into_struct_value(),
collection(ctx, ptr_bytes),
"cast_collection",
)
store_list(env, list_ptr, list_len)
};
let build_else = || empty_polymorphic_list(env);
@ -204,12 +149,8 @@ pub fn list_prepend<'a, 'ctx, 'env>(
"new_list_length",
);
let ptr_bytes = env.ptr_bytes;
// Allocate space for the new array that we'll copy into.
let clone_ptr = allocate_list(env, elem_layout, new_list_len);
let int_type = ptr_int(ctx, ptr_bytes);
let ptr_as_int = builder.build_ptr_to_int(clone_ptr, int_type, "list_cast_ptr");
builder.build_store(clone_ptr, elem);
@ -232,6 +173,8 @@ pub fn list_prepend<'a, 'ctx, 'env>(
.builder
.build_int_mul(elem_bytes, len, "mul_old_len_by_elem_bytes");
let ptr_bytes = env.ptr_bytes;
if elem_layout.safe_to_memcpy() {
// Copy the bytes from the original array into the new
// one we just malloc'd.
@ -242,37 +185,14 @@ pub fn list_prepend<'a, 'ctx, 'env>(
panic!("TODO Cranelift currently only knows how to clone list elements that are Copy.");
}
// Create a fresh wrapper struct for the newly populated array
let struct_type = collection(ctx, env.ptr_bytes);
let mut struct_val;
// Store the pointer
struct_val = builder
.build_insert_value(
struct_type.get_undef(),
ptr_as_int,
Builtin::WRAPPER_PTR,
"insert_ptr",
)
.unwrap();
// Store the length
struct_val = builder
.build_insert_value(struct_val, new_list_len, Builtin::WRAPPER_LEN, "insert_len")
.unwrap();
builder.build_bitcast(
struct_val.into_struct_value(),
collection(ctx, ptr_bytes),
"cast_collection",
)
store_list(env, clone_ptr, new_list_len)
}
/// List.join : List (List elem) -> List elem
pub fn list_join<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
parent: FunctionValue<'ctx>,
outer_list_wrapper: StructValue<'ctx>,
outer_list: BasicValueEnum<'ctx>,
outer_list_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
// List.join is implemented as follows:
@ -300,6 +220,7 @@ pub fn list_join<'a, 'ctx, 'env>(
let inner_list_type =
basic_type_from_layout(env.arena, ctx, &inner_list_layout, env.ptr_bytes);
let outer_list_wrapper = outer_list.into_struct_value();
let outer_list_len = list_len(builder, outer_list_wrapper);
let outer_list_ptr = {
let elem_ptr_type = get_ptr_type(&inner_list_type, AddressSpace::Generic);
@ -319,12 +240,7 @@ pub fn list_join<'a, 'ctx, 'env>(
builder.build_store(list_len_sum_alloca, ctx.i64_type().const_int(0, false));
// List Sum Loop
let sum_loop = |sum_index| {
let inner_list_wrapper_ptr = unsafe {
builder.build_in_bounds_gep(outer_list_ptr, &[sum_index], "load_index")
};
let inner_list = builder.build_load(inner_list_wrapper_ptr, "inner_list");
let sum_loop = |_, inner_list: BasicValueEnum<'ctx>| {
let inner_list_len = list_len(builder, inner_list.into_struct_value());
let next_list_sum = builder.build_int_add(
@ -338,11 +254,14 @@ pub fn list_join<'a, 'ctx, 'env>(
builder.build_store(list_len_sum_alloca, next_list_sum);
};
incrementing_index_loop(
incrementing_elem_loop(
builder,
parent,
ctx,
outer_list_len,
LoopListArg {
ptr: outer_list_ptr,
len: outer_list_len,
},
"#sum_index",
None,
sum_loop,
@ -359,16 +278,8 @@ pub fn list_join<'a, 'ctx, 'env>(
builder.build_store(dest_elem_ptr_alloca, final_list_ptr);
// Inner List Loop
let inner_list_loop = |index| {
let inner_list_wrapper = {
let wrapper_ptr = unsafe {
builder.build_in_bounds_gep(outer_list_ptr, &[index], "load_index")
};
builder
.build_load(wrapper_ptr, "inner_list_wrapper")
.into_struct_value()
};
let inner_list_loop = |_, inner_list: BasicValueEnum<'ctx>| {
let inner_list_wrapper = inner_list.into_struct_value();
let inner_list_len = list_len(builder, inner_list_wrapper);
@ -386,20 +297,10 @@ pub fn list_join<'a, 'ctx, 'env>(
);
builder.position_at_end(inner_list_non_empty_block);
let inner_list_ptr = load_list_ptr(builder, inner_list_wrapper, elem_ptr_type);
// Element Inserting Loop
let inner_elem_loop = |inner_index| {
let src_elem_ptr = unsafe {
let inner_list_ptr =
load_list_ptr(builder, inner_list_wrapper, elem_ptr_type);
builder.build_in_bounds_gep(
inner_list_ptr,
&[inner_index],
"load_index",
)
};
let src_elem = builder.build_load(src_elem_ptr, "get_elem");
let inner_elem_loop = |_, src_elem| {
// TODO clone src_elem
let curr_dest_elem_ptr = builder
@ -419,11 +320,14 @@ pub fn list_join<'a, 'ctx, 'env>(
builder.build_store(dest_elem_ptr_alloca, inc_dest_elem_ptr);
};
incrementing_index_loop(
incrementing_elem_loop(
builder,
parent,
ctx,
inner_list_len,
LoopListArg {
ptr: inner_list_ptr,
len: inner_list_len,
},
"#inner_index",
None,
inner_elem_loop,
@ -433,49 +337,20 @@ pub fn list_join<'a, 'ctx, 'env>(
builder.position_at_end(after_inner_list_non_empty_block);
};
incrementing_index_loop(
incrementing_elem_loop(
builder,
parent,
ctx,
outer_list_len,
LoopListArg {
ptr: outer_list_ptr,
len: outer_list_len,
},
"#inner_list_index",
None,
inner_list_loop,
);
let ptr_bytes = env.ptr_bytes;
let int_type = ptr_int(ctx, ptr_bytes);
let ptr_as_int =
builder.build_ptr_to_int(final_list_ptr, int_type, "list_cast_ptr");
let struct_type = collection(ctx, ptr_bytes);
let mut struct_val;
// Store the pointer
struct_val = builder
.build_insert_value(
struct_type.get_undef(),
ptr_as_int,
Builtin::WRAPPER_PTR,
"insert_ptr",
)
.unwrap();
// Store the length
struct_val = builder
.build_insert_value(
struct_val,
final_list_sum,
Builtin::WRAPPER_LEN,
"insert_len",
)
.unwrap();
builder.build_bitcast(
struct_val.into_struct_value(),
collection(ctx, ptr_bytes),
"cast_collection",
)
store_list(env, final_list_ptr, final_list_sum)
};
let build_else = || empty_list(env);
@ -502,33 +377,14 @@ pub fn list_join<'a, 'ctx, 'env>(
pub fn list_reverse<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
parent: FunctionValue<'ctx>,
scope: &Scope<'a, 'ctx>,
list: &Symbol,
list: BasicValueEnum<'ctx>,
list_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
let (_, list_layout) = load_symbol_and_layout(env, scope, list);
match list_layout {
Layout::Builtin(Builtin::EmptyList) => empty_list(env),
Layout::Builtin(Builtin::List(_, elem_layout)) => {
let wrapper_struct = load_symbol(env, scope, list).into_struct_value();
let non_empty_fn =
|elem_layout: &Layout<'a>, len: IntValue<'ctx>, wrapper_struct: StructValue<'ctx>| {
let builder = env.builder;
let ctx = env.context;
let len = list_len(builder, wrapper_struct);
// list_len > 0
// We do this check to avoid allocating memory. If the input
// list is empty, then we can just return an empty list.
let comparison = builder.build_int_compare(
IntPredicate::UGT,
len,
ctx.i64_type().const_int(0, false),
"greaterthanzero",
);
let build_then = || {
// Allocate space for the new array that we'll copy into.
let elem_type = basic_type_from_layout(env.arena, ctx, elem_layout, env.ptr_bytes);
@ -554,11 +410,8 @@ pub fn list_reverse<'a, 'ctx, 'env>(
let curr_index = builder
.build_load(start_alloca, index_name)
.into_int_value();
let next_index = builder.build_int_sub(
curr_index,
ctx.i64_type().const_int(1, false),
"nextindex",
);
let next_index =
builder.build_int_sub(curr_index, ctx.i64_type().const_int(1, false), "nextindex");
builder.build_store(start_alloca, next_index);
@ -603,53 +456,10 @@ pub fn list_reverse<'a, 'ctx, 'env>(
builder.build_conditional_branch(end_cond, loop_bb, after_bb);
builder.position_at_end(after_bb);
let ptr_bytes = env.ptr_bytes;
let int_type = ptr_int(ctx, ptr_bytes);
let ptr_as_int =
builder.build_ptr_to_int(reversed_list_ptr, int_type, "list_cast_ptr");
let struct_type = collection(ctx, ptr_bytes);
let mut struct_val;
// Store the pointer
struct_val = builder
.build_insert_value(
struct_type.get_undef(),
ptr_as_int,
Builtin::WRAPPER_PTR,
"insert_ptr",
)
.unwrap();
// Store the length
struct_val = builder
.build_insert_value(struct_val, len, Builtin::WRAPPER_LEN, "insert_len")
.unwrap();
builder.build_bitcast(
struct_val.into_struct_value(),
collection(ctx, ptr_bytes),
"cast_collection",
)
store_list(env, reversed_list_ptr, len)
};
let build_else = || empty_list(env);
let struct_type = collection(ctx, env.ptr_bytes);
build_basic_phi2(
env,
parent,
comparison,
build_then,
build_else,
BasicTypeEnum::StructType(struct_type),
)
}
_ => {
unreachable!("Invalid List layout for List.reverse {:?}", list_layout);
}
}
if_list_is_not_empty(env, parent, non_empty_fn, list, list_layout, "List.reverse")
}
pub fn list_get_unsafe<'a, 'ctx, 'env>(
@ -723,8 +533,6 @@ pub fn list_append<'a, 'ctx, 'env>(
// Allocate space for the new array that we'll copy into.
let clone_ptr = allocate_list(env, elem_layout, new_list_len);
let int_type = ptr_int(ctx, ptr_bytes);
let ptr_as_int = builder.build_ptr_to_int(clone_ptr, int_type, "list_cast_ptr");
// TODO check if malloc returned null; if so, runtime error for OOM!
@ -738,34 +546,11 @@ pub fn list_append<'a, 'ctx, 'env>(
panic!("TODO Cranelift currently only knows how to clone list elements that are Copy.");
}
// Create a fresh wrapper struct for the newly populated array
let struct_type = collection(ctx, env.ptr_bytes);
let mut struct_val;
// Store the pointer
struct_val = builder
.build_insert_value(
struct_type.get_undef(),
ptr_as_int,
Builtin::WRAPPER_PTR,
"insert_ptr",
)
.unwrap();
// Store the length
struct_val = builder
.build_insert_value(struct_val, new_list_len, Builtin::WRAPPER_LEN, "insert_len")
.unwrap();
let elem_ptr = unsafe { builder.build_in_bounds_gep(clone_ptr, &[list_len], "load_index") };
builder.build_store(elem_ptr, elem);
builder.build_bitcast(
struct_val.into_struct_value(),
collection(ctx, ptr_bytes),
"cast_collection",
)
store_list(env, clone_ptr, new_list_len)
}
/// List.set : List elem, Int, elem -> List elem
@ -878,46 +663,24 @@ pub fn list_map<'a, 'ctx, 'env>(
) -> BasicValueEnum<'ctx> {
match (func, func_layout) {
(BasicValueEnum::PointerValue(func_ptr), Layout::FunctionPointer(_, ret_elem_layout)) => {
match list_layout {
Layout::Builtin(Builtin::EmptyList) => empty_list(env),
Layout::Builtin(Builtin::List(_, elem_layout)) => {
let non_empty_fn = |elem_layout: &Layout<'a>,
len: IntValue<'ctx>,
list_wrapper: StructValue<'ctx>| {
let ctx = env.context;
let builder = env.builder;
let list_wrapper = list.into_struct_value();
let len = list_len(builder, list_wrapper);
// len > 0
// We do this check to avoid allocating memory. If the input
// list is empty, then we can just return an empty list.
let list_length_comparison = list_is_not_empty(builder, ctx, len);
let if_list_is_empty = || empty_list(env);
let if_list_is_not_empty = || {
let ret_list_ptr = allocate_list(env, ret_elem_layout, len);
let elem_type =
basic_type_from_layout(env.arena, ctx, elem_layout, env.ptr_bytes);
let elem_type = basic_type_from_layout(env.arena, ctx, elem_layout, env.ptr_bytes);
let ptr_type = get_ptr_type(&elem_type, AddressSpace::Generic);
let list_ptr = load_list_ptr(builder, list_wrapper, ptr_type);
let list_loop = |index| {
let list_loop = |index, before_elem| {
// The pointer to the element in the input list
let before_elem_ptr = unsafe {
builder.build_in_bounds_gep(list_ptr, &[index], "load_index")
};
let before_elem =
builder.build_load(before_elem_ptr, "get_before_elem");
let call_site_value = builder.build_call(
func_ptr,
env.arena.alloc([before_elem]),
"map_func",
);
let call_site_value =
builder.build_call(func_ptr, env.arena.alloc([before_elem]), "map_func");
// set the calling convention explicitly for this call
call_site_value.set_call_convention(crate::llvm::build::FAST_CALL_CONV);
@ -929,65 +692,27 @@ pub fn list_map<'a, 'ctx, 'env>(
// The pointer to the element in the mapped-over list
let after_elem_ptr = unsafe {
builder.build_in_bounds_gep(
ret_list_ptr,
&[index],
"load_index_after_list",
)
builder.build_in_bounds_gep(ret_list_ptr, &[index], "load_index_after_list")
};
// Mutate the new array in-place to change the element.
builder.build_store(after_elem_ptr, after_elem);
};
incrementing_index_loop(
builder, parent, ctx, len, "#index", None, list_loop,
incrementing_elem_loop(
builder,
parent,
ctx,
LoopListArg { ptr: list_ptr, len },
"#index",
None,
list_loop,
);
let ptr_bytes = env.ptr_bytes;
let int_type = ptr_int(ctx, ptr_bytes);
let ptr_as_int =
builder.build_ptr_to_int(ret_list_ptr, int_type, "list_cast_ptr");
let struct_type = collection(ctx, ptr_bytes);
let mut struct_val;
// Store the pointer
struct_val = builder
.build_insert_value(
struct_type.get_undef(),
ptr_as_int,
Builtin::WRAPPER_PTR,
"insert_ptr",
)
.unwrap();
// Store the length
struct_val = builder
.build_insert_value(struct_val, len, Builtin::WRAPPER_LEN, "insert_len")
.unwrap();
builder.build_bitcast(
struct_val.into_struct_value(),
collection(ctx, ptr_bytes),
"cast_collection",
)
store_list(env, ret_list_ptr, len)
};
build_basic_phi2(
env,
parent,
list_length_comparison,
if_list_is_not_empty,
if_list_is_empty,
BasicTypeEnum::StructType(collection(ctx, env.ptr_bytes)),
)
}
_ => {
unreachable!("Invalid List layout for List.map : {:?}", list_layout);
}
}
if_list_is_not_empty(env, parent, non_empty_fn, list, list_layout, "List.map")
}
_ => {
unreachable!(
@ -1092,16 +817,7 @@ pub fn list_concat<'a, 'ctx, 'env>(
let first_list_ptr = load_list_ptr(builder, first_list_wrapper, ptr_type);
// FIRST LOOP
let first_loop = |first_index| {
// The pointer to the element in the first list
let first_list_elem_ptr = unsafe {
builder.build_in_bounds_gep(
first_list_ptr,
&[first_index],
"load_index",
)
};
let first_loop = |first_index, first_list_elem| {
// The pointer to the element in the combined list
let combined_list_elem_ptr = unsafe {
builder.build_in_bounds_gep(
@ -1111,19 +827,20 @@ pub fn list_concat<'a, 'ctx, 'env>(
)
};
let first_list_elem = builder.build_load(first_list_elem_ptr, "get_elem");
// Mutate the new array in-place to change the element.
builder.build_store(combined_list_elem_ptr, first_list_elem);
};
let index_name = "#index";
let index_alloca = incrementing_index_loop(
let index_alloca = incrementing_elem_loop(
builder,
parent,
ctx,
first_list_len,
LoopListArg {
ptr: first_list_ptr,
len: first_list_len,
},
index_name,
None,
first_loop,
@ -1132,19 +849,10 @@ pub fn list_concat<'a, 'ctx, 'env>(
// Reset the index variable to 0
builder.build_store(index_alloca, ctx.i64_type().const_int(0, false));
// SECOND LOOP
let second_loop = |second_index| {
let second_list_ptr = load_list_ptr(builder, second_list_wrapper, ptr_type);
// The pointer to the element in the second list
let second_list_elem_ptr = unsafe {
builder.build_in_bounds_gep(
second_list_ptr,
&[second_index],
"load_index",
)
};
// SECOND LOOP
let second_loop = |second_index, second_list_elem| {
// The pointer to the element in the combined list.
// Note that the pointer does not start at the index
// 0, it starts at the index of first_list_len. In that
@ -1167,56 +875,24 @@ pub fn list_concat<'a, 'ctx, 'env>(
)
};
let second_list_elem = builder.build_load(second_list_elem_ptr, "get_elem");
// Mutate the new array in-place to change the element.
builder.build_store(combined_list_elem_ptr, second_list_elem);
};
incrementing_index_loop(
incrementing_elem_loop(
builder,
parent,
ctx,
second_list_len,
LoopListArg {
ptr: second_list_ptr,
len: second_list_len,
},
index_name,
Some(index_alloca),
second_loop,
);
let ptr_bytes = env.ptr_bytes;
let int_type = ptr_int(ctx, ptr_bytes);
let ptr_as_int =
builder.build_ptr_to_int(combined_list_ptr, int_type, "list_cast_ptr");
let struct_type = collection(ctx, ptr_bytes);
let mut struct_val;
// Store the pointer
struct_val = builder
.build_insert_value(
struct_type.get_undef(),
ptr_as_int,
Builtin::WRAPPER_PTR,
"insert_ptr",
)
.unwrap();
// Store the length
struct_val = builder
.build_insert_value(
struct_val,
combined_list_len,
Builtin::WRAPPER_LEN,
"insert_len",
)
.unwrap();
builder.build_bitcast(
struct_val.into_struct_value(),
collection(ctx, ptr_bytes),
"cast_collection",
)
store_list(env, combined_list_ptr, combined_list_len)
};
build_basic_phi2(
@ -1247,9 +923,44 @@ pub fn list_concat<'a, 'ctx, 'env>(
}
}
pub struct LoopListArg<'ctx> {
pub ptr: PointerValue<'ctx>,
pub len: IntValue<'ctx>,
}
pub fn incrementing_elem_loop<'ctx, LoopFn>(
builder: &Builder<'ctx>,
parent: FunctionValue<'ctx>,
ctx: &'ctx Context,
list: LoopListArg<'ctx>,
index_name: &str,
maybe_alloca: Option<PointerValue<'ctx>>,
mut loop_fn: LoopFn,
) -> PointerValue<'ctx>
where
LoopFn: FnMut(IntValue<'ctx>, BasicValueEnum<'ctx>),
{
incrementing_index_loop(
builder,
parent,
ctx,
list.len,
index_name,
maybe_alloca,
|index| {
// The pointer to the element in the list
let elem_ptr = unsafe { builder.build_in_bounds_gep(list.ptr, &[index], "load_index") };
let elem = builder.build_load(elem_ptr, "get_elem");
loop_fn(index, elem);
},
)
}
// This helper simulates a basic for loop, where
// and index increments up from 0 to some end value
pub fn incrementing_index_loop<'ctx, LoopFn>(
fn incrementing_index_loop<'ctx, LoopFn>(
builder: &Builder<'ctx>,
parent: FunctionValue<'ctx>,
ctx: &'ctx Context,
@ -1299,6 +1010,59 @@ where
index_alloca
}
// This function checks if the list is empty, and
// if it is, it returns an empty list, and if not
// it runs whatever code is passed in under `build_non_empty`
// This is the avoid allocating memory if the list is empty.
fn if_list_is_not_empty<'a, 'ctx, 'env, 'b, NonEmptyFn>(
env: &Env<'a, 'ctx, 'env>,
parent: FunctionValue<'ctx>,
mut build_non_empty: NonEmptyFn,
list: BasicValueEnum<'ctx>,
list_layout: &Layout<'a>,
list_fn_name: &str,
) -> BasicValueEnum<'ctx>
where
NonEmptyFn: FnMut(&Layout<'a>, IntValue<'ctx>, StructValue<'ctx>) -> BasicValueEnum<'ctx>,
{
match list_layout {
Layout::Builtin(Builtin::EmptyList) => empty_list(env),
Layout::Builtin(Builtin::List(_, elem_layout)) => {
let builder = env.builder;
let ctx = env.context;
let wrapper_struct = list.into_struct_value();
let len = list_len(builder, wrapper_struct);
// list_len > 0
let comparison = builder.build_int_compare(
IntPredicate::UGT,
len,
ctx.i64_type().const_int(0, false),
"greaterthanzero",
);
let build_empty = || empty_list(env);
let struct_type = collection(ctx, env.ptr_bytes);
build_basic_phi2(
env,
parent,
comparison,
|| build_non_empty(elem_layout, len, wrapper_struct),
build_empty,
BasicTypeEnum::StructType(struct_type),
)
}
_ => {
unreachable!("Invalid List layout for {} {:?}", list_fn_name, list_layout);
}
}
}
pub fn build_basic_phi2<'a, 'ctx, 'env, PassFn, FailFn>(
env: &Env<'a, 'ctx, 'env>,
parent: FunctionValue<'ctx>,
@ -1522,3 +1286,40 @@ pub fn allocate_list<'a, 'ctx, 'env>(
list_element_ptr
}
pub fn store_list<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
list_ptr: PointerValue<'ctx>,
len: IntValue<'ctx>,
) -> BasicValueEnum<'ctx> {
let ctx = env.context;
let builder = env.builder;
let ptr_bytes = env.ptr_bytes;
let int_type = ptr_int(ctx, ptr_bytes);
let ptr_as_int = builder.build_ptr_to_int(list_ptr, int_type, "list_cast_ptr");
let struct_type = collection(ctx, ptr_bytes);
let mut struct_val;
// Store the pointer
struct_val = builder
.build_insert_value(
struct_type.get_undef(),
ptr_as_int,
Builtin::WRAPPER_PTR,
"insert_ptr",
)
.unwrap();
// Store the length
struct_val = builder
.build_insert_value(struct_val, len, Builtin::WRAPPER_LEN, "insert_len")
.unwrap();
builder.build_bitcast(
struct_val.into_struct_value(),
collection(ctx, ptr_bytes),
"cast_collection",
)
}

View file

@ -34,7 +34,15 @@ mod gen_list {
fn list_append() {
assert_evals_to!("List.append [1] 2", &[1, 2], &'static [i64]);
assert_evals_to!("List.append [1, 1] 2", &[1, 1, 2], &'static [i64]);
}
#[test]
fn list_append_to_empty_list() {
assert_evals_to!("List.append [] 3", &[3], &'static [i64]);
}
#[test]
fn list_append_to_empty_list_of_int() {
assert_evals_to!(
indoc!(
r#"
@ -48,11 +56,19 @@ mod gen_list {
&[3, 3],
&'static [i64]
);
}
#[test]
fn list_append_bools() {
assert_evals_to!(
"List.append [ True, False ] True",
&[true, false, true],
&'static [bool]
);
}
#[test]
fn list_append_longer_list() {
assert_evals_to!(
"List.append [ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 ] 23",
&[11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
@ -78,12 +94,19 @@ mod gen_list {
&[6, 4],
&'static [i64]
);
}
#[test]
fn list_prepend_bools() {
assert_evals_to!(
"List.prepend [ True, False ] True",
&[true, true, false],
&'static [bool]
);
}
#[test]
fn list_prepend_big_list() {
assert_evals_to!(
"List.prepend [ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 100, 100, 100, 100 ] 9",
&[9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 100, 100, 100, 100],
@ -92,17 +115,15 @@ mod gen_list {
}
#[test]
fn list_keep_if() {
fn list_keep_if_empty_list_of_int() {
assert_evals_to!(
indoc!(
r#"
main = \{} ->
empty : List Int
empty =
[]
List.keepIf empty (\x -> True)
main {}
"#
),
&[],
@ -111,114 +132,131 @@ mod gen_list {
}
#[test]
fn list_map() {
fn list_keep_if_empty_list() {
assert_evals_to!(
indoc!(
r#"
main = \{} ->
empty : List Int
empty =
[]
List.map empty (\x -> x)
main {}
List.keepIf [] (\x -> True)
"#
),
&[],
&'static [i64]
);
}
#[test]
fn list_map_on_empty_list_with_int_layout() {
assert_evals_to!(
indoc!(
r#"
empty : List Int
empty =
[]
List.map empty (\x -> x)
"#
),
&[],
&'static [i64]
);
}
#[test]
fn list_map_on_non_empty_list() {
assert_evals_to!(
indoc!(
r#"
main = \{} ->
nonEmpty : List Int
nonEmpty =
[ 1 ]
List.map nonEmpty (\x -> x)
main {}
"#
),
&[1],
&'static [i64]
);
}
#[test]
fn list_map_changes_input() {
assert_evals_to!(
indoc!(
r#"
main = \{} ->
nonEmpty : List Int
nonEmpty =
[ 1 ]
List.map nonEmpty (\x -> x + 1)
main {}
"#
),
&[2],
&'static [i64]
);
}
#[test]
fn list_map_on_big_list() {
assert_evals_to!(
indoc!(
r#"
main = \{} ->
nonEmpty : List Int
nonEmpty =
[ 1, 2, 3, 4, 5 ]
[ 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5 ]
List.map nonEmpty (\x -> x * 2)
main {}
"#
),
&[2, 4, 6, 8, 10],
&[2, 4, 6, 8, 10, 2, 4, 6, 8, 10, 2, 4, 6, 8, 10, 2, 4, 6, 8, 10, 2, 4, 6, 8, 10],
&'static [i64]
);
}
#[test]
fn list_map_with_type_change() {
assert_evals_to!(
indoc!(
r#"
main = \{} ->
nonEmpty : List Int
nonEmpty =
[ 1, 1, -4, 1, 2 ]
List.map nonEmpty (\x -> x > 0)
main {}
"#
),
&[true, true, false, true, true],
&'static [bool]
);
}
#[test]
fn list_map_using_defined_function() {
assert_evals_to!(
indoc!(
r#"
main = \{} ->
nonEmpty : List Int
nonEmpty =
[ 1, 1, -4, 1, 2 ]
[ 2, 2, -4, 2, 3 ]
greaterThanOne : Int -> Bool
greaterThanOne = \i ->
i > 0
i > 1
List.map nonEmpty greaterThanOne
main {}
"#
),
&[true, true, false, true, true],
&'static [bool]
);
}
#[test]
fn list_map_all_inline() {
assert_evals_to!(
indoc!(
r#"
main = \{} ->
List.map [] (\x -> x > 0)
main {}
"#
),
&[],
@ -227,20 +265,35 @@ mod gen_list {
}
#[test]
fn list_join() {
fn list_join_empty_list() {
assert_evals_to!("List.join []", &[], &'static [i64]);
}
#[test]
fn list_join_one_list() {
assert_evals_to!("List.join [ [1, 2, 3 ] ]", &[1, 2, 3], &'static [i64]);
}
#[test]
fn list_join_two_non_empty_lists() {
assert_evals_to!(
"List.join [ [1, 2, 3 ] , [4 ,5, 6] ]",
&[1, 2, 3, 4, 5, 6],
&'static [i64]
);
}
#[test]
fn list_join_two_non_empty_lists_of_float() {
assert_evals_to!(
"List.join [ [ 1.2, 1.1 ], [ 2.1, 2.2 ] ]",
&[1.2, 1.1, 2.1, 2.2],
&'static [f64]
);
}
#[test]
fn list_join_to_big_list() {
assert_evals_to!(
indoc!(
r#"
@ -262,7 +315,10 @@ mod gen_list {
],
&'static [f64]
);
}
#[test]
fn list_join_defined_empty_list() {
assert_evals_to!(
indoc!(
r#"
@ -276,9 +332,15 @@ mod gen_list {
&[0.2, 11.11],
&'static [f64]
);
}
#[test]
fn list_join_all_empty_lists() {
assert_evals_to!("List.join [ [], [], [] ]", &[], &'static [f64]);
}
#[test]
fn list_join_one_empty_list() {
assert_evals_to!(
"List.join [ [ 1.2, 1.1 ], [] ]",
&[1.2, 1.1],
@ -328,6 +390,10 @@ mod gen_list {
);
assert_evals_to!("List.reverse [1, 2, 3]", &[3, 2, 1], &'static [i64]);
assert_evals_to!("List.reverse [4]", &[4], &'static [i64]);
}
#[test]
fn list_reverse_empty_list_of_int() {
assert_evals_to!(
indoc!(
r#"
@ -341,6 +407,10 @@ mod gen_list {
&[],
&'static [i64]
);
}
#[test]
fn list_reverse_empty_list() {
assert_evals_to!("List.reverse []", &[], &'static [i64]);
}
@ -366,9 +436,12 @@ mod gen_list {
}
#[test]
fn list_concat_vanilla() {
fn list_concat_two_empty_lists() {
assert_evals_to!("List.concat [] []", &[], &'static [i64]);
}
#[test]
fn list_concat_two_empty_lists_of_int() {
assert_evals_to!(
indoc!(
r#"
@ -386,16 +459,25 @@ mod gen_list {
&[],
&'static [i64]
);
}
#[test]
fn list_concat_second_list_is_empty() {
assert_evals_to!("List.concat [ 12, 13 ] []", &[12, 13], &'static [i64]);
assert_evals_to!(
"List.concat [ 34, 43 ] [ 64, 55, 66 ]",
&[34, 43, 64, 55, 66],
&'static [i64]
);
}
#[test]
fn list_concat_first_list_is_empty() {
assert_evals_to!("List.concat [] [ 23, 24 ]", &[23, 24], &'static [i64]);
}
#[test]
fn list_concat_two_non_empty_lists() {
assert_evals_to!(
"List.concat [1, 2 ] [ 3, 4 ]",
&[1, 2, 3, 4],
@ -555,24 +637,20 @@ mod gen_list {
);
}
// TODO getting this to work requires generating a runtime error for the Ok
// branch here, which is not yet something we support as of when this
// test was originally written.
//
// #[test]
// fn first_empty_list() {
// assert_evals_to!(
// indoc!(
// r#"
// when List.first [] is
// Ok val -> val
// Err _ -> -1
// "#
// ),
// -1,
// i64
// );
// }
#[test]
fn first_empty_list() {
assert_evals_to!(
indoc!(
r#"
when List.first [] is
Ok val -> val
Err _ -> -1
"#
),
-1,
i64
);
}
#[test]
fn get_empty_list() {
@ -980,151 +1058,151 @@ mod gen_list {
})
}
// #[test]
// fn foobar2() {
// with_larger_debug_stack(|| {
// assert_evals_to!(
// indoc!(
// r#"
// quicksort : List (Num a) -> List (Num a)
// quicksort = \list ->
// quicksortHelp list 0 (List.len list - 1)
//
//
// quicksortHelp : List (Num a), Int, Int -> List (Num a)
// quicksortHelp = \list, low, high ->
// if low < high then
// when partition low high list is
// Pair partitionIndex partitioned ->
// partitioned
// |> quicksortHelp low (partitionIndex - 1)
// |> quicksortHelp (partitionIndex + 1) high
// else
// list
//
//
// swap : Int, Int, List a -> List a
// swap = \i, j, list ->
// when Pair (List.get list i) (List.get list j) is
// Pair (Ok atI) (Ok atJ) ->
// list
// |> List.set i atJ
// |> List.set j atI
//
// _ ->
// []
//
// partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
// partition = \low, high, initialList ->
// when List.get initialList high is
// Ok pivot ->
// when partitionHelp (low - 1) low initialList high pivot is
// Pair newI newList ->
// Pair (newI + 1) (swap (newI + 1) high newList)
//
// Err _ ->
// Pair (low - 1) initialList
//
//
// partitionHelp : Int, Int, List (Num a), Int, Int -> [ Pair Int (List (Num a)) ]
// partitionHelp = \i, j, list, high, pivot ->
// # if j < high then
// if False then
// when List.get list j is
// Ok value ->
// if value <= pivot then
// partitionHelp (i + 1) (j + 1) (swap (i + 1) j list) high pivot
// else
// partitionHelp i (j + 1) list high pivot
//
// Err _ ->
// Pair i list
// else
// Pair i list
//
//
//
// quicksort [ 7, 4, 21, 19 ]
// "#
// ),
// &[19, 7, 4, 21],
// &'static [i64],
// );
// })
// }
#[test]
fn foobar2() {
with_larger_debug_stack(|| {
assert_evals_to!(
indoc!(
r#"
quicksort : List (Num a) -> List (Num a)
quicksort = \list ->
quicksortHelp list 0 (List.len list - 1)
// #[test]
// fn foobar() {
// with_larger_debug_stack(|| {
// assert_evals_to!(
// indoc!(
// r#"
// quicksort : List (Num a) -> List (Num a)
// quicksort = \list ->
// quicksortHelp list 0 (List.len list - 1)
//
//
// quicksortHelp : List (Num a), Int, Int -> List (Num a)
// quicksortHelp = \list, low, high ->
// if low < high then
// when partition low high list is
// Pair partitionIndex partitioned ->
// partitioned
// |> quicksortHelp low (partitionIndex - 1)
// |> quicksortHelp (partitionIndex + 1) high
// else
// list
//
//
// swap : Int, Int, List a -> List a
// swap = \i, j, list ->
// when Pair (List.get list i) (List.get list j) is
// Pair (Ok atI) (Ok atJ) ->
// list
// |> List.set i atJ
// |> List.set j atI
//
// _ ->
// []
//
// partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
// partition = \low, high, initialList ->
// when List.get initialList high is
// Ok pivot ->
// when partitionHelp (low - 1) low initialList high pivot is
// Pair newI newList ->
// Pair (newI + 1) (swap (newI + 1) high newList)
//
// Err _ ->
// Pair (low - 1) initialList
//
//
// partitionHelp : Int, Int, List (Num a), Int, Int -> [ Pair Int (List (Num a)) ]
// partitionHelp = \i, j, list, high, pivot ->
// if j < high then
// when List.get list j is
// Ok value ->
// if value <= pivot then
// partitionHelp (i + 1) (j + 1) (swap (i + 1) j list) high pivot
// else
// partitionHelp i (j + 1) list high pivot
//
// Err _ ->
// Pair i list
// else
// Pair i list
//
//
//
// when List.first (quicksort [0x1]) is
// _ -> 4
// "#
// ),
// 4,
// i64,
// );
// })
// }
quicksortHelp : List (Num a), Int, Int -> List (Num a)
quicksortHelp = \list, low, high ->
if low < high then
when partition low high list is
Pair partitionIndex partitioned ->
partitioned
|> quicksortHelp low (partitionIndex - 1)
|> quicksortHelp (partitionIndex + 1) high
else
list
swap : Int, Int, List a -> List a
swap = \i, j, list ->
when Pair (List.get list i) (List.get list j) is
Pair (Ok atI) (Ok atJ) ->
list
|> List.set i atJ
|> List.set j atI
_ ->
[]
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
partition = \low, high, initialList ->
when List.get initialList high is
Ok pivot ->
when partitionHelp (low - 1) low initialList high pivot is
Pair newI newList ->
Pair (newI + 1) (swap (newI + 1) high newList)
Err _ ->
Pair (low - 1) initialList
partitionHelp : Int, Int, List (Num a), Int, Int -> [ Pair Int (List (Num a)) ]
partitionHelp = \i, j, list, high, pivot ->
# if j < high then
if False then
when List.get list j is
Ok value ->
if value <= pivot then
partitionHelp (i + 1) (j + 1) (swap (i + 1) j list) high pivot
else
partitionHelp i (j + 1) list high pivot
Err _ ->
Pair i list
else
Pair i list
quicksort [ 7, 4, 21, 19 ]
"#
),
&[19, 7, 4, 21],
&'static [i64]
);
})
}
#[test]
fn foobar() {
with_larger_debug_stack(|| {
assert_evals_to!(
indoc!(
r#"
quicksort : List (Num a) -> List (Num a)
quicksort = \list ->
quicksortHelp list 0 (List.len list - 1)
quicksortHelp : List (Num a), Int, Int -> List (Num a)
quicksortHelp = \list, low, high ->
if low < high then
when partition low high list is
Pair partitionIndex partitioned ->
partitioned
|> quicksortHelp low (partitionIndex - 1)
|> quicksortHelp (partitionIndex + 1) high
else
list
swap : Int, Int, List a -> List a
swap = \i, j, list ->
when Pair (List.get list i) (List.get list j) is
Pair (Ok atI) (Ok atJ) ->
list
|> List.set i atJ
|> List.set j atI
_ ->
[]
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
partition = \low, high, initialList ->
when List.get initialList high is
Ok pivot ->
when partitionHelp (low - 1) low initialList high pivot is
Pair newI newList ->
Pair (newI + 1) (swap (newI + 1) high newList)
Err _ ->
Pair (low - 1) initialList
partitionHelp : Int, Int, List (Num a), Int, Int -> [ Pair Int (List (Num a)) ]
partitionHelp = \i, j, list, high, pivot ->
if j < high then
when List.get list j is
Ok value ->
if value <= pivot then
partitionHelp (i + 1) (j + 1) (swap (i + 1) j list) high pivot
else
partitionHelp i (j + 1) list high pivot
Err _ ->
Pair i list
else
Pair i list
when List.first (quicksort [0x1]) is
_ -> 4
"#
),
4,
i64
);
})
}
#[test]
fn empty_list_increment_decrement() {

View file

@ -311,7 +311,7 @@ fn layout_from_flat_type<'a>(
// Num.Num should only ever have 1 argument, e.g. Num.Num Int.Integer
debug_assert_eq!(args.len(), 1);
let var = args.iter().next().unwrap();
let var = args.get(0).unwrap();
let content = subs.get_without_compacting(*var).content;
layout_from_num_content(content)