From 3112025b0c4e218d7e7691bd3de73865934b6d24 Mon Sep 17 00:00:00 2001 From: Chad Stearns Date: Sun, 13 Sep 2020 14:46:21 -0400 Subject: [PATCH] Passed down inplace to everything that uses allocate_list --- compiler/gen/src/llvm/build.rs | 68 +++++++++++++++++++------ compiler/gen/src/llvm/build_list.rs | 77 +++++++++++++++++++---------- 2 files changed, 104 insertions(+), 41 deletions(-) diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index 956e60ef9b..a51da77000 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -212,6 +212,17 @@ pub fn construct_optimization_passes<'a>( (mpm, fpm) } +fn get_inplace_from_layout<'a, 'b>(layout: &'b Layout<'a>) -> InPlace { + match layout { + Layout::Builtin(Builtin::EmptyList) => InPlace::InPlace, + Layout::Builtin(Builtin::List(memory_mode, _)) => match memory_mode { + MemoryMode::Unique => InPlace::InPlace, + MemoryMode::Refcounted => InPlace::Clone, + }, + _ => unreachable!("Layout {:?} does not have an inplace", layout), + } +} + pub fn build_exp_literal<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, literal: &roc_mono::ir::Literal<'a>, @@ -239,7 +250,7 @@ pub fn build_exp_literal<'a, 'ctx, 'env>( let len_type = env.ptr_int(); let len = len_type.const_int(bytes_len, false); - allocate_list(env, &CHAR_LAYOUT, len) + allocate_list(env, InPlace::Clone, &CHAR_LAYOUT, len) // TODO check if malloc returned null; if so, runtime error for OOM! }; @@ -671,7 +682,11 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( } } EmptyArray => empty_polymorphic_list(env), - Array { elem_layout, elems } => list_literal(env, scope, elem_layout, elems), + Array { elem_layout, elems } => { + let inplace = get_inplace_from_layout(layout); + + list_literal(env, inplace, scope, elem_layout, elems) + } FunctionPointer(symbol, layout) => { let fn_name = layout_ids .get(*symbol, layout) @@ -760,6 +775,7 @@ pub fn allocate_with_refcount<'a, 'ctx, 'env>( fn list_literal<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, + inplace: InPlace, scope: &Scope<'a, 'ctx>, elem_layout: &Layout<'a>, elems: &&[Symbol], @@ -775,7 +791,7 @@ fn list_literal<'a, 'ctx, 'env>( let len_type = env.ptr_int(); let len = len_type.const_int(bytes_len, false); - allocate_list(env, elem_layout, len) + allocate_list(env, inplace, elem_layout, len) // TODO check if malloc returned null; if so, runtime error for OOM! }; @@ -1469,6 +1485,7 @@ fn call_intrinsic<'a, 'ctx, 'env>( }) } +#[derive(Copy, Clone)] pub enum InPlace { InPlace, Clone, @@ -1512,7 +1529,9 @@ fn run_low_level<'a, 'ctx, 'env>( let second_str = load_symbol(env, scope, &args[1]); - str_concat(env, parent, first_str, second_str) + let inplace = get_inplace_from_layout(layout); + + str_concat(env, inplace, parent, first_str, second_str) } ListLen => { // List.len : List * -> Int @@ -1528,7 +1547,9 @@ fn run_low_level<'a, 'ctx, 'env>( let (arg, arg_layout) = load_symbol_and_layout(env, scope, &args[0]); - list_single(env, layout, arg, arg_layout) + let inplace = get_inplace_from_layout(layout); + + list_single(env, inplace, arg, arg_layout) } ListRepeat => { // List.repeat : Int, elem -> List elem @@ -1537,7 +1558,9 @@ fn run_low_level<'a, 'ctx, 'env>( let list_len = load_symbol(env, scope, &args[0]).into_int_value(); let (elem, elem_layout) = load_symbol_and_layout(env, scope, &args[1]); - list_repeat(env, parent, list_len, elem, elem_layout) + let inplace = get_inplace_from_layout(layout); + + list_repeat(env, inplace, parent, list_len, elem, elem_layout) } ListReverse => { // List.reverse : List elem -> List elem @@ -1545,7 +1568,9 @@ fn run_low_level<'a, 'ctx, 'env>( let (list, list_layout) = load_symbol_and_layout(env, scope, &args[0]); - list_reverse(env, parent, InPlace::Clone, list, list_layout) + let inplace = get_inplace_from_layout(layout); + + list_reverse(env, parent, inplace, list, list_layout) } ListConcat => { debug_assert_eq!(args.len(), 2); @@ -1554,7 +1579,9 @@ fn run_low_level<'a, 'ctx, 'env>( let second_list = load_symbol(env, scope, &args[1]); - list_concat(env, parent, first_list, second_list, list_layout) + let inplace = get_inplace_from_layout(layout); + + list_concat(env, inplace, parent, first_list, second_list, list_layout) } ListMap => { // List.map : List before, (before -> after) -> List after @@ -1564,7 +1591,9 @@ fn run_low_level<'a, 'ctx, 'env>( let (func, func_layout) = load_symbol_and_layout(env, scope, &args[1]); - list_map(env, parent, func, func_layout, list, list_layout) + let inplace = get_inplace_from_layout(layout); + + list_map(env, inplace, parent, func, func_layout, list, list_layout) } ListKeepIf => { // List.keepIf : List elem, (elem -> Bool) -> List elem @@ -1574,7 +1603,9 @@ fn run_low_level<'a, 'ctx, 'env>( let (func, func_layout) = load_symbol_and_layout(env, scope, &args[1]); - list_keep_if(env, parent, func, func_layout, list, list_layout) + let inplace = get_inplace_from_layout(layout); + + list_keep_if(env, inplace, parent, func, func_layout, list, list_layout) } ListWalkRight => { // List.walkRight : List elem, (elem -> accum -> accum), accum -> accum @@ -1604,7 +1635,9 @@ fn run_low_level<'a, 'ctx, 'env>( let original_wrapper = load_symbol(env, scope, &args[0]).into_struct_value(); let (elem, elem_layout) = load_symbol_and_layout(env, scope, &args[1]); - list_append(env, original_wrapper, elem, elem_layout) + let inplace = get_inplace_from_layout(layout); + + list_append(env, inplace, original_wrapper, elem, elem_layout) } ListPrepend => { // List.prepend : List elem, elem -> List elem @@ -1613,7 +1646,9 @@ fn run_low_level<'a, 'ctx, 'env>( let original_wrapper = load_symbol(env, scope, &args[0]).into_struct_value(); let (elem, elem_layout) = load_symbol_and_layout(env, scope, &args[1]); - list_prepend(env, original_wrapper, elem, elem_layout) + let inplace = get_inplace_from_layout(layout); + + list_prepend(env, inplace, original_wrapper, elem, elem_layout) } ListJoin => { // List.join : List (List elem) -> List elem @@ -1621,7 +1656,9 @@ fn run_low_level<'a, 'ctx, 'env>( let (list, outer_list_layout) = load_symbol_and_layout(env, scope, &args[0]); - list_join(env, parent, list, outer_list_layout) + let inplace = get_inplace_from_layout(layout); + + list_join(env, inplace, parent, list, outer_list_layout) } NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumSin | NumCos | NumToFloat => { debug_assert_eq!(args.len(), 1); @@ -1925,6 +1962,7 @@ where /// Str.concat : Str, Str -> Str fn str_concat<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, + inplace: InPlace, parent: FunctionValue<'ctx>, first_str: BasicValueEnum<'ctx>, second_str: BasicValueEnum<'ctx>, @@ -1955,6 +1993,7 @@ fn str_concat<'a, 'ctx, 'env>( let (new_wrapper, _) = clone_nonempty_list( env, + inplace, second_str_len, load_list_ptr(builder, second_str_wrapper, ptr_type), &CHAR_LAYOUT, @@ -1982,6 +2021,7 @@ fn str_concat<'a, 'ctx, 'env>( let if_second_str_is_empty = || { let (new_wrapper, _) = clone_nonempty_list( env, + inplace, first_str_len, load_list_ptr(builder, first_str_wrapper, ptr_type), &CHAR_LAYOUT, @@ -1999,7 +2039,7 @@ fn str_concat<'a, 'ctx, 'env>( let combined_str_len = builder.build_int_add(first_str_len, second_str_len, "add_list_lengths"); - let combined_str_ptr = allocate_list(env, &CHAR_LAYOUT, combined_str_len); + let combined_str_ptr = allocate_list(env, inplace, &CHAR_LAYOUT, combined_str_len); // FIRST LOOP let first_str_ptr = load_list_ptr(builder, first_str_wrapper, ptr_type); diff --git a/compiler/gen/src/llvm/build_list.rs b/compiler/gen/src/llvm/build_list.rs index 97ead29f59..e5fec985d1 100644 --- a/compiler/gen/src/llvm/build_list.rs +++ b/compiler/gen/src/llvm/build_list.rs @@ -17,7 +17,7 @@ fn get_list_element_type<'a, 'b>(layout: &'b Layout<'a>) -> Option<&'b Layout<'a /// List.single : a -> List a pub fn list_single<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - layout: &Layout<'a>, + inplace: InPlace, elem: BasicValueEnum<'ctx>, elem_layout: &Layout<'a>, ) -> BasicValueEnum<'ctx> { @@ -26,7 +26,8 @@ pub fn list_single<'a, 'ctx, 'env>( // allocate a list of size 1 on the heap let size = ctx.i64_type().const_int(1, false); - let ptr = allocate_list(env, elem_layout, size); + + let ptr = allocate_list(env, inplace, elem_layout, size); // Put the element into the list let elem_ptr = unsafe { @@ -48,6 +49,7 @@ pub fn list_single<'a, 'ctx, 'env>( /// List.repeat : Int, elem -> List elem pub fn list_repeat<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, + inplace: InPlace, parent: FunctionValue<'ctx>, list_len: IntValue<'ctx>, elem: BasicValueEnum<'ctx>, @@ -72,7 +74,7 @@ pub fn list_repeat<'a, 'ctx, 'env>( let build_then = || { // Allocate space for the new array that we'll copy into. - let list_ptr = allocate_list(env, elem_layout, list_len); + let list_ptr = allocate_list(env, inplace, elem_layout, list_len); // TODO check if malloc returned null; if so, runtime error for OOM! let index_name = "#index"; @@ -137,6 +139,7 @@ pub fn list_repeat<'a, 'ctx, 'env>( /// List.prepend List elem, elem -> List elem pub fn list_prepend<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, + inplace: InPlace, original_wrapper: StructValue<'ctx>, elem: BasicValueEnum<'ctx>, elem_layout: &Layout<'a>, @@ -158,7 +161,7 @@ pub fn list_prepend<'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 clone_ptr = allocate_list(env, inplace, elem_layout, new_list_len); builder.build_store(clone_ptr, elem); @@ -199,6 +202,7 @@ pub fn list_prepend<'a, 'ctx, 'env>( /// List.join : List (List elem) -> List elem pub fn list_join<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, + inplace: InPlace, parent: FunctionValue<'ctx>, outer_list: BasicValueEnum<'ctx>, outer_list_layout: &Layout<'a>, @@ -276,7 +280,7 @@ pub fn list_join<'a, 'ctx, 'env>( .build_load(list_len_sum_alloca, list_len_sum_name) .into_int_value(); - let final_list_ptr = allocate_list(env, elem_layout, final_list_sum); + let final_list_ptr = allocate_list(env, inplace, elem_layout, final_list_sum); let dest_elem_ptr_alloca = builder.build_alloca(elem_ptr_type, "dest_elem"); @@ -377,7 +381,7 @@ pub fn list_join<'a, 'ctx, 'env>( pub fn list_reverse_help<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, parent: FunctionValue<'ctx>, - in_place: InPlace, + inplace: InPlace, length: IntValue<'ctx>, source_ptr: PointerValue<'ctx>, dest_ptr: PointerValue<'ctx>, @@ -406,7 +410,7 @@ pub fn list_reverse_help<'a, 'ctx, 'env>( // if updating in-place, then the "middle element" can be left untouched // otherwise, the middle element needs to be copied over from the source to the target - let predicate = match in_place { + let predicate = match inplace { InPlace::InPlace => IntPredicate::SGT, InPlace::Clone => IntPredicate::SGE, }; @@ -430,7 +434,7 @@ pub fn list_reverse_help<'a, 'ctx, 'env>( let high_value = builder.build_load(high_ptr, "load_high"); // swap the two values - if let InPlace::Clone = in_place { + if let InPlace::Clone = inplace { low_ptr = unsafe { builder.build_in_bounds_gep(dest_ptr, &[low], "low_ptr") }; high_ptr = unsafe { builder.build_in_bounds_gep(dest_ptr, &[high], "high_ptr") }; } @@ -451,7 +455,7 @@ pub fn list_reverse_help<'a, 'ctx, 'env>( pub fn list_reverse<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, parent: FunctionValue<'ctx>, - in_place: InPlace, + output_inplace: InPlace, list: BasicValueEnum<'ctx>, list_layout: &Layout<'a>, ) -> BasicValueEnum<'ctx> { @@ -461,12 +465,21 @@ pub fn list_reverse<'a, 'ctx, 'env>( let ctx = env.context; let wrapper_struct = list.into_struct_value(); - let element_layout = match get_list_element_type(list_layout) { - Some(element_layout) => element_layout.clone(), - None => { + let (input_inplace, element_layout) = match list_layout.clone() { + Layout::Builtin(Builtin::EmptyList) => ( + InPlace::InPlace, // this pointer will never actually be dereferenced - Layout::Builtin(Builtin::Int64) - } + Layout::Builtin(Builtin::Int64), + ), + Layout::Builtin(Builtin::List(memory_mode, elem_layout)) => ( + match memory_mode { + MemoryMode::Unique => InPlace::InPlace, + MemoryMode::Refcounted => InPlace::Clone, + }, + elem_layout.clone(), + ), + + _ => unreachable!("Invalid layout {:?} in List.reverse", list_layout), }; let list_type = basic_type_from_layout(env.arena, env.context, &element_layout, env.ptr_bytes); @@ -475,9 +488,9 @@ pub fn list_reverse<'a, 'ctx, 'env>( let list_ptr = load_list_ptr(builder, wrapper_struct, ptr_type); let length = list_len(builder, list.into_struct_value()); - match in_place { + match input_inplace { InPlace::InPlace => { - list_reverse_help(env, parent, in_place, length, list_ptr, list_ptr); + list_reverse_help(env, parent, input_inplace, length, list_ptr, list_ptr); list } @@ -512,7 +525,7 @@ pub fn list_reverse<'a, 'ctx, 'env>( { builder.position_at_end(len_1_block); - let new_list_ptr = clone_list(env, &element_layout, one, list_ptr); + let new_list_ptr = clone_list(env, output_inplace, &element_layout, one, list_ptr); builder.build_store(result, new_list_ptr); builder.build_unconditional_branch(cont_block); @@ -522,7 +535,7 @@ pub fn list_reverse<'a, 'ctx, 'env>( { builder.position_at_end(len_n_block); - let new_list_ptr = allocate_list(env, &element_layout, length); + let new_list_ptr = allocate_list(env, output_inplace, &element_layout, length); list_reverse_help(env, parent, InPlace::Clone, length, list_ptr, new_list_ptr); @@ -574,6 +587,7 @@ pub fn list_get_unsafe<'a, 'ctx, 'env>( /// List.append : List elem, elem -> List elem pub fn list_append<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, + inplace: InPlace, original_wrapper: StructValue<'ctx>, elem: BasicValueEnum<'ctx>, elem_layout: &Layout<'a>, @@ -609,7 +623,7 @@ pub fn list_append<'a, 'ctx, 'env>( .build_int_mul(elem_bytes, list_len, "mul_old_len_by_elem_bytes"); // Allocate space for the new array that we'll copy into. - let clone_ptr = allocate_list(env, elem_layout, new_list_len); + let clone_ptr = allocate_list(env, inplace, elem_layout, new_list_len); // TODO check if malloc returned null; if so, runtime error for OOM! @@ -635,7 +649,7 @@ pub fn list_set<'a, 'ctx, 'env>( parent: FunctionValue<'ctx>, args: &[(BasicValueEnum<'ctx>, &'a Layout<'a>)], env: &Env<'a, 'ctx, 'env>, - in_place: InPlace, + inplace: InPlace, ) -> BasicValueEnum<'ctx> { let builder = env.builder; @@ -657,13 +671,14 @@ pub fn list_set<'a, 'ctx, 'env>( let ctx = env.context; 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 (new_wrapper, array_data_ptr) = match in_place { + let (new_wrapper, array_data_ptr) = match inplace { InPlace::InPlace => ( original_wrapper, load_list_ptr(builder, original_wrapper, ptr_type), ), InPlace::Clone => clone_nonempty_list( env, + inplace, list_len, load_list_ptr(builder, original_wrapper, ptr_type), elem_layout, @@ -813,6 +828,7 @@ pub fn list_walk_right<'a, 'ctx, 'env>( /// List.keepIf : List elem, (elem -> Bool) -> List elem pub fn list_keep_if<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, + inplace: InPlace, parent: FunctionValue<'ctx>, func: BasicValueEnum<'ctx>, func_layout: &Layout<'a>, @@ -903,7 +919,7 @@ pub fn list_keep_if<'a, 'ctx, 'env>( // Make a new list, with a length equal to the number // of `elem` that passed the `elem -> Bool` function. - let ret_list_ptr = allocate_list(env, elem_layout, final_ret_list_len); + let ret_list_ptr = allocate_list(env, inplace, elem_layout, final_ret_list_len); // Make a pointer into the return list. This pointer is used // below to store elements into return list. @@ -993,6 +1009,7 @@ pub fn list_keep_if<'a, 'ctx, 'env>( /// List.map : List before, (before -> after) -> List after pub fn list_map<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, + inplace: InPlace, parent: FunctionValue<'ctx>, func: BasicValueEnum<'ctx>, func_layout: &Layout<'a>, @@ -1007,7 +1024,7 @@ pub fn list_map<'a, 'ctx, 'env>( let ctx = env.context; let builder = env.builder; - let ret_list_ptr = allocate_list(env, ret_elem_layout, len); + let ret_list_ptr = allocate_list(env, inplace, ret_elem_layout, len); 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); @@ -1054,6 +1071,7 @@ pub fn list_map<'a, 'ctx, 'env>( /// List.concat : List elem, List elem -> List elem pub fn list_concat<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, + inplace: InPlace, parent: FunctionValue<'ctx>, first_list: BasicValueEnum<'ctx>, second_list: BasicValueEnum<'ctx>, @@ -1095,6 +1113,7 @@ pub fn list_concat<'a, 'ctx, 'env>( let (new_wrapper, _) = clone_nonempty_list( env, + inplace, second_list_len, load_list_ptr(builder, second_list_wrapper, ptr_type), elem_layout, @@ -1122,6 +1141,7 @@ pub fn list_concat<'a, 'ctx, 'env>( let if_second_list_is_empty = || { let (new_wrapper, _) = clone_nonempty_list( env, + inplace, first_list_len, load_list_ptr(builder, first_list_wrapper, ptr_type), elem_layout, @@ -1140,7 +1160,8 @@ pub fn list_concat<'a, 'ctx, 'env>( let combined_list_len = builder.build_int_add(first_list_len, second_list_len, "add_list_lengths"); - let combined_list_ptr = allocate_list(env, elem_layout, combined_list_len); + let combined_list_ptr = + allocate_list(env, inplace, elem_layout, combined_list_len); let first_list_ptr = load_list_ptr(builder, first_list_wrapper, ptr_type); @@ -1540,6 +1561,7 @@ pub fn load_list_ptr<'ctx>( pub fn clone_nonempty_list<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, + inplace: InPlace, list_len: IntValue<'ctx>, elems_ptr: PointerValue<'ctx>, elem_layout: &Layout<'_>, @@ -1557,7 +1579,7 @@ pub fn clone_nonempty_list<'a, 'ctx, 'env>( .build_int_mul(elem_bytes, list_len, "clone_mul_len_by_elem_bytes"); // Allocate space for the new array that we'll copy into. - let clone_ptr = allocate_list(env, elem_layout, list_len); + let clone_ptr = allocate_list(env, inplace, elem_layout, 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"); @@ -1607,6 +1629,7 @@ pub fn clone_nonempty_list<'a, 'ctx, 'env>( pub fn clone_list<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, + output_inplace: InPlace, elem_layout: &Layout<'a>, length: IntValue<'ctx>, old_ptr: PointerValue<'ctx>, @@ -1615,7 +1638,7 @@ pub fn clone_list<'a, 'ctx, 'env>( let ptr_bytes = env.ptr_bytes; // allocate new empty list (with refcount 1) - let new_ptr = allocate_list(env, elem_layout, length); + let new_ptr = allocate_list(env, output_inplace, elem_layout, length); let stack_size = elem_layout.stack_size(env.ptr_bytes); let bytes = builder.build_int_mul( @@ -1632,7 +1655,7 @@ pub fn clone_list<'a, 'ctx, 'env>( pub fn allocate_list<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - // in_place: InPlace, + inplace: InPlace, elem_layout: &Layout<'a>, length: IntValue<'ctx>, ) -> PointerValue<'ctx> {