Moved over to incrementing_elem_loop from incrementing_index_loop

This commit is contained in:
Chad Stearns 2020-08-29 21:56:52 -04:00
parent dedbd5b3bd
commit f2a8065adc
2 changed files with 95 additions and 145 deletions

View file

@ -1,9 +1,9 @@
use crate::layout_id::LayoutIds; use crate::layout_id::LayoutIds;
use crate::llvm::build_list::{ use crate::llvm::build_list::{
allocate_list, build_basic_phi2, clone_nonempty_list, empty_list, empty_polymorphic_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_len, list_map, list_prepend, list_repeat, list_reverse, list_set, list_single, list_join, list_len, list_map, list_prepend, list_repeat, list_reverse, list_set, list_single,
load_list_ptr, load_list_ptr, store_list,
}; };
use crate::llvm::compare::{build_eq, build_neq}; use crate::llvm::compare::{build_eq, build_neq};
use crate::llvm::convert::{ use crate::llvm::convert::{
@ -1674,9 +1674,9 @@ fn run_low_level<'a, 'ctx, 'env>(
// List.reverse : List elem -> List elem // List.reverse : List elem -> List elem
debug_assert_eq!(args.len(), 1); 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 => { ListConcat => {
debug_assert_eq!(args.len(), 2); debug_assert_eq!(args.len(), 2);
@ -1719,9 +1719,8 @@ fn run_low_level<'a, 'ctx, 'env>(
debug_assert_eq!(args.len(), 1); debug_assert_eq!(args.len(), 1);
let (list, outer_list_layout) = load_symbol_and_layout(env, scope, &args[0]); 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 => { NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumSin | NumCos | NumToFloat => {
debug_assert_eq!(args.len(), 1); debug_assert_eq!(args.len(), 1);
@ -2000,14 +1999,9 @@ fn str_concat<'a, 'ctx, 'env>(
let combined_str_ptr = allocate_list(env, &CHAR_LAYOUT, combined_str_len); let combined_str_ptr = allocate_list(env, &CHAR_LAYOUT, combined_str_len);
// FIRST LOOP // FIRST LOOP
let first_loop = |first_index| { let first_str_ptr = load_list_ptr(builder, first_str_wrapper, ptr_type);
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 // The pointer to the element in the combined list
let combined_str_elem_ptr = unsafe { let combined_str_elem_ptr = unsafe {
builder.build_in_bounds_gep( builder.build_in_bounds_gep(
@ -2017,18 +2011,17 @@ 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. // Mutate the new array in-place to change the element.
builder.build_store(combined_str_elem_ptr, first_str_elem); builder.build_store(combined_str_elem_ptr, first_str_elem);
}; };
let index_name = "#index"; let index_name = "#index";
let index_alloca = incrementing_index_loop( let index_alloca = incrementing_elem_loop(
builder, builder,
parent, parent,
ctx, ctx,
first_str_ptr,
first_str_len, first_str_len,
index_name, index_name,
None, None,
@ -2039,14 +2032,9 @@ fn str_concat<'a, 'ctx, 'env>(
builder.build_store(index_alloca, ctx.i64_type().const_int(0, false)); builder.build_store(index_alloca, ctx.i64_type().const_int(0, false));
// SECOND LOOP // SECOND LOOP
let second_loop = |second_index| { let second_str_ptr = load_list_ptr(builder, second_str_wrapper, ptr_type);
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. // The pointer to the element in the combined str.
// Note that the pointer does not start at the index // Note that the pointer does not start at the index
// 0, it starts at the index of first_str_len. In that // 0, it starts at the index of first_str_len. In that
@ -2065,55 +2053,22 @@ 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. // Mutate the new array in-place to change the element.
builder.build_store(combined_str_char_ptr, second_str_elem); builder.build_store(combined_str_char_ptr, second_str_elem);
}; };
incrementing_index_loop( incrementing_elem_loop(
builder, builder,
parent, parent,
ctx, ctx,
second_str_ptr,
second_str_len, second_str_len,
index_name, index_name,
Some(index_alloca), Some(index_alloca),
second_loop, second_loop,
); );
let ptr_bytes = env.ptr_bytes; store_list(env, combined_str_ptr, combined_str_len)
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",
)
}; };
build_basic_phi2( 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 crate::llvm::convert::{basic_type_from_layout, collection, get_ptr_type, ptr_int};
use inkwell::builder::Builder; use inkwell::builder::Builder;
use inkwell::context::Context; use inkwell::context::Context;
use inkwell::types::{BasicTypeEnum, PointerType}; use inkwell::types::{BasicTypeEnum, PointerType};
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue}; use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
use inkwell::{AddressSpace, IntPredicate}; use inkwell::{AddressSpace, IntPredicate};
use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout, MemoryMode}; use roc_mono::layout::{Builtin, Layout, MemoryMode};
/// List.single : a -> List a /// List.single : a -> List a
@ -193,7 +192,7 @@ pub fn list_prepend<'a, 'ctx, 'env>(
pub fn list_join<'a, 'ctx, 'env>( pub fn list_join<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
parent: FunctionValue<'ctx>, parent: FunctionValue<'ctx>,
outer_list_wrapper: StructValue<'ctx>, outer_list: BasicValueEnum<'ctx>,
outer_list_layout: &Layout<'a>, outer_list_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
// List.join is implemented as follows: // List.join is implemented as follows:
@ -221,6 +220,7 @@ pub fn list_join<'a, 'ctx, 'env>(
let inner_list_type = let inner_list_type =
basic_type_from_layout(env.arena, ctx, &inner_list_layout, env.ptr_bytes); 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_len = list_len(builder, outer_list_wrapper);
let outer_list_ptr = { let outer_list_ptr = {
let elem_ptr_type = get_ptr_type(&inner_list_type, AddressSpace::Generic); let elem_ptr_type = get_ptr_type(&inner_list_type, AddressSpace::Generic);
@ -240,12 +240,7 @@ pub fn list_join<'a, 'ctx, 'env>(
builder.build_store(list_len_sum_alloca, ctx.i64_type().const_int(0, false)); builder.build_store(list_len_sum_alloca, ctx.i64_type().const_int(0, false));
// List Sum Loop // List Sum Loop
let sum_loop = |sum_index| { let sum_loop = |_, inner_list: BasicValueEnum<'ctx>| {
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 inner_list_len = list_len(builder, inner_list.into_struct_value()); let inner_list_len = list_len(builder, inner_list.into_struct_value());
let next_list_sum = builder.build_int_add( let next_list_sum = builder.build_int_add(
@ -259,10 +254,11 @@ pub fn list_join<'a, 'ctx, 'env>(
builder.build_store(list_len_sum_alloca, next_list_sum); builder.build_store(list_len_sum_alloca, next_list_sum);
}; };
incrementing_index_loop( incrementing_elem_loop(
builder, builder,
parent, parent,
ctx, ctx,
outer_list_ptr,
outer_list_len, outer_list_len,
"#sum_index", "#sum_index",
None, None,
@ -280,16 +276,8 @@ pub fn list_join<'a, 'ctx, 'env>(
builder.build_store(dest_elem_ptr_alloca, final_list_ptr); builder.build_store(dest_elem_ptr_alloca, final_list_ptr);
// Inner List Loop // Inner List Loop
let inner_list_loop = |index| { let inner_list_loop = |_, inner_list: BasicValueEnum<'ctx>| {
let inner_list_wrapper = { let inner_list_wrapper = inner_list.into_struct_value();
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_len = list_len(builder, inner_list_wrapper); let inner_list_len = list_len(builder, inner_list_wrapper);
@ -307,20 +295,10 @@ pub fn list_join<'a, 'ctx, 'env>(
); );
builder.position_at_end(inner_list_non_empty_block); 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 // Element Inserting Loop
let inner_elem_loop = |inner_index| { let inner_elem_loop = |_, src_elem| {
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");
// TODO clone src_elem // TODO clone src_elem
let curr_dest_elem_ptr = builder let curr_dest_elem_ptr = builder
@ -340,10 +318,11 @@ pub fn list_join<'a, 'ctx, 'env>(
builder.build_store(dest_elem_ptr_alloca, inc_dest_elem_ptr); builder.build_store(dest_elem_ptr_alloca, inc_dest_elem_ptr);
}; };
incrementing_index_loop( incrementing_elem_loop(
builder, builder,
parent, parent,
ctx, ctx,
inner_list_ptr,
inner_list_len, inner_list_len,
"#inner_index", "#inner_index",
None, None,
@ -354,10 +333,11 @@ pub fn list_join<'a, 'ctx, 'env>(
builder.position_at_end(after_inner_list_non_empty_block); builder.position_at_end(after_inner_list_non_empty_block);
}; };
incrementing_index_loop( incrementing_elem_loop(
builder, builder,
parent, parent,
ctx, ctx,
outer_list_ptr,
outer_list_len, outer_list_len,
"#inner_list_index", "#inner_list_index",
None, None,
@ -391,11 +371,9 @@ pub fn list_join<'a, 'ctx, 'env>(
pub fn list_reverse<'a, 'ctx, 'env>( pub fn list_reverse<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
parent: FunctionValue<'ctx>, parent: FunctionValue<'ctx>,
scope: &Scope<'a, 'ctx>, list: BasicValueEnum<'ctx>,
list: &Symbol, list_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
let (_, list_layout) = load_symbol_and_layout(env, scope, list);
let non_empty_fn = let non_empty_fn =
|elem_layout: &Layout<'a>, len: IntValue<'ctx>, wrapper_struct: StructValue<'ctx>| { |elem_layout: &Layout<'a>, len: IntValue<'ctx>, wrapper_struct: StructValue<'ctx>| {
let builder = env.builder; let builder = env.builder;
@ -475,7 +453,7 @@ pub fn list_reverse<'a, 'ctx, 'env>(
store_list(env, reversed_list_ptr, len) store_list(env, reversed_list_ptr, len)
}; };
if_non_empty(env, parent, scope, non_empty_fn, list, list_layout) if_list_is_not_empty(env, parent, non_empty_fn, list, list_layout)
} }
pub fn list_get_unsafe<'a, 'ctx, 'env>( pub fn list_get_unsafe<'a, 'ctx, 'env>(
@ -667,7 +645,9 @@ pub fn list_map<'a, 'ctx, 'env>(
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
match (func, func_layout) { match (func, func_layout) {
(BasicValueEnum::PointerValue(func_ptr), Layout::FunctionPointer(_, ret_elem_layout)) => { (BasicValueEnum::PointerValue(func_ptr), Layout::FunctionPointer(_, ret_elem_layout)) => {
let non_empty_fn = || { let non_empty_fn = |elem_layout: &Layout<'a>,
len: IntValue<'ctx>,
list_wrapper: StructValue<'ctx>| {
let ctx = env.context; let ctx = env.context;
let builder = env.builder; let builder = env.builder;
@ -678,12 +658,8 @@ pub fn list_map<'a, 'ctx, 'env>(
let list_ptr = load_list_ptr(builder, list_wrapper, ptr_type); 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 // 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 = let call_site_value =
builder.build_call(func_ptr, env.arena.alloc([before_elem]), "map_func"); builder.build_call(func_ptr, env.arena.alloc([before_elem]), "map_func");
@ -705,12 +681,14 @@ pub fn list_map<'a, 'ctx, 'env>(
builder.build_store(after_elem_ptr, after_elem); 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, list_ptr, len, "#index", None, list_loop,
);
store_list(env, ret_list_ptr, len) store_list(env, ret_list_ptr, len)
}; };
if_non_empty(env, parent, scope, non_empty_fn, list, list_layout) if_list_is_not_empty(env, parent, non_empty_fn, list, list_layout)
} }
_ => { _ => {
unreachable!( unreachable!(
@ -815,16 +793,7 @@ pub fn list_concat<'a, 'ctx, 'env>(
let first_list_ptr = load_list_ptr(builder, first_list_wrapper, ptr_type); let first_list_ptr = load_list_ptr(builder, first_list_wrapper, ptr_type);
// FIRST LOOP // FIRST LOOP
let first_loop = |first_index| { let first_loop = |first_index, first_list_elem| {
// 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",
)
};
// The pointer to the element in the combined list // The pointer to the element in the combined list
let combined_list_elem_ptr = unsafe { let combined_list_elem_ptr = unsafe {
builder.build_in_bounds_gep( builder.build_in_bounds_gep(
@ -834,18 +803,17 @@ 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. // Mutate the new array in-place to change the element.
builder.build_store(combined_list_elem_ptr, first_list_elem); builder.build_store(combined_list_elem_ptr, first_list_elem);
}; };
let index_name = "#index"; let index_name = "#index";
let index_alloca = incrementing_index_loop( let index_alloca = incrementing_elem_loop(
builder, builder,
parent, parent,
ctx, ctx,
first_list_ptr,
first_list_len, first_list_len,
index_name, index_name,
None, None,
@ -855,19 +823,10 @@ pub fn list_concat<'a, 'ctx, 'env>(
// Reset the index variable to 0 // Reset the index variable to 0
builder.build_store(index_alloca, ctx.i64_type().const_int(0, false)); builder.build_store(index_alloca, ctx.i64_type().const_int(0, false));
let second_list_ptr = load_list_ptr(builder, second_list_wrapper, ptr_type);
// SECOND LOOP // SECOND LOOP
let second_loop = |second_index| { let second_loop = |second_index, second_list_elem| {
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",
)
};
// The pointer to the element in the combined list. // The pointer to the element in the combined list.
// Note that the pointer does not start at the index // Note that the pointer does not start at the index
// 0, it starts at the index of first_list_len. In that // 0, it starts at the index of first_list_len. In that
@ -890,16 +849,15 @@ 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. // Mutate the new array in-place to change the element.
builder.build_store(combined_list_elem_ptr, second_list_elem); builder.build_store(combined_list_elem_ptr, second_list_elem);
}; };
incrementing_index_loop( incrementing_elem_loop(
builder, builder,
parent, parent,
ctx, ctx,
second_list_ptr,
second_list_len, second_list_len,
index_name, index_name,
Some(index_alloca), Some(index_alloca),
@ -939,7 +897,40 @@ pub fn list_concat<'a, 'ctx, 'env>(
// This helper simulates a basic for loop, where // This helper simulates a basic for loop, where
// and index increments up from 0 to some end value // and index increments up from 0 to some end value
pub fn incrementing_index_loop<'ctx, LoopFn>( pub fn incrementing_elem_loop<'ctx, LoopFn>(
builder: &Builder<'ctx>,
parent: FunctionValue<'ctx>,
ctx: &'ctx Context,
list_ptr: PointerValue<'ctx>,
end: IntValue<'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,
end,
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
fn incrementing_index_loop<'ctx, LoopFn>(
builder: &Builder<'ctx>, builder: &Builder<'ctx>,
parent: FunctionValue<'ctx>, parent: FunctionValue<'ctx>,
ctx: &'ctx Context, ctx: &'ctx Context,
@ -989,13 +980,19 @@ where
index_alloca index_alloca
} }
fn if_non_empty<'a, 'ctx, 'env, 'b, NonEmptyFn>( // In many cases we dont want to do anything if the
// builtin was given an empty list. This is because
// allocating memory for a list is costly, so its
// better to skip if it we can. Furthermore, checking
// if a list is empty requires both seeing if the list
// is a NonEmpty layout and if its a List(elem_layout)
// but with a length of 0.
fn if_list_is_not_empty<'a, 'ctx, 'env, 'b, NonEmptyFn>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
parent: FunctionValue<'ctx>, parent: FunctionValue<'ctx>,
scope: &Scope<'a, 'ctx>,
mut build_non_empty: NonEmptyFn, mut build_non_empty: NonEmptyFn,
list: &Symbol, list: BasicValueEnum<'ctx>,
list_layout: &'b Layout<'a>, list_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> ) -> BasicValueEnum<'ctx>
where where
NonEmptyFn: FnMut(&Layout<'a>, IntValue<'ctx>, StructValue<'ctx>) -> BasicValueEnum<'ctx>, NonEmptyFn: FnMut(&Layout<'a>, IntValue<'ctx>, StructValue<'ctx>) -> BasicValueEnum<'ctx>,
@ -1004,16 +1001,14 @@ where
Layout::Builtin(Builtin::EmptyList) => empty_list(env), Layout::Builtin(Builtin::EmptyList) => empty_list(env),
Layout::Builtin(Builtin::List(_, elem_layout)) => { Layout::Builtin(Builtin::List(_, elem_layout)) => {
let wrapper_struct = load_symbol(env, scope, list).into_struct_value();
let builder = env.builder; let builder = env.builder;
let ctx = env.context; let ctx = env.context;
let wrapper_struct = list.into_struct_value();
let len = list_len(builder, wrapper_struct); let len = list_len(builder, wrapper_struct);
// list_len > 0 // 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( let comparison = builder.build_int_compare(
IntPredicate::UGT, IntPredicate::UGT,
len, len,
@ -1264,7 +1259,7 @@ pub fn allocate_list<'a, 'ctx, 'env>(
list_element_ptr list_element_ptr
} }
fn store_list<'a, 'ctx, 'env>( pub fn store_list<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
list_ptr: PointerValue<'ctx>, list_ptr: PointerValue<'ctx>,
len: IntValue<'ctx>, len: IntValue<'ctx>,