From d29b8764f0ac681de7d907b491bb2be05c38e2df Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 18 Feb 2021 23:39:50 +0100 Subject: [PATCH 01/25] List.repeat in zig --- compiler/builtins/bitcode/src/list.zig | 30 +++++++ compiler/builtins/bitcode/src/main.zig | 1 + compiler/builtins/src/bitcode.rs | 1 + compiler/gen/src/llvm/bitcode.rs | 9 ++ compiler/gen/src/llvm/build.rs | 10 ++- compiler/gen/src/llvm/build_list.rs | 114 ++++++++----------------- compiler/mono/src/borrow.rs | 2 +- compiler/mono/src/ir.rs | 2 +- examples/benchmarks/AStarTests.roc | 17 ++-- 9 files changed, 96 insertions(+), 90 deletions(-) diff --git a/compiler/builtins/bitcode/src/list.zig b/compiler/builtins/bitcode/src/list.zig index 8ea824fa31..9ea6c399d9 100644 --- a/compiler/builtins/bitcode/src/list.zig +++ b/compiler/builtins/bitcode/src/list.zig @@ -7,6 +7,9 @@ const Allocator = mem.Allocator; const EqFn = fn (?[*]u8, ?[*]u8) callconv(.C) bool; const Opaque = ?[*]u8; +const Inc = fn (?[*]u8) callconv(.C) void; +const Dec = fn (?[*]u8) callconv(.C) void; + pub const RocList = extern struct { bytes: ?[*]u8, length: usize, @@ -278,3 +281,30 @@ pub fn listContains(list: RocList, key: Opaque, key_width: usize, is_eq: EqFn) c return false; } + +pub fn listRepeat(count: usize, alignment: usize, element: Opaque, element_width: usize, inc_n_element: Inc) callconv(.C) RocList { + if (count == 0) { + return RocList.empty(); + } + + const allocator = std.heap.c_allocator; + var output = RocList.allocate(allocator, alignment, count, element_width); + + if (output.bytes) |target_ptr| { + var i: usize = 0; + const source = element orelse unreachable; + while (i < count) : (i += 1) { + @memcpy(target_ptr + i * element_width, source, element_width); + } + + // TODO do all increments at once! + i = 0; + while (i < count) : (i += 1) { + inc_n_element(element); + } + + return output; + } else { + unreachable; + } +} diff --git a/compiler/builtins/bitcode/src/main.zig b/compiler/builtins/bitcode/src/main.zig index 1ecb482406..e05445611e 100644 --- a/compiler/builtins/bitcode/src/main.zig +++ b/compiler/builtins/bitcode/src/main.zig @@ -14,6 +14,7 @@ comptime { exportListFn(list.listKeepOks, "keep_oks"); exportListFn(list.listKeepErrs, "keep_errs"); exportListFn(list.listContains, "contains"); + exportListFn(list.listRepeat, "repeat"); } // Dict Module diff --git a/compiler/builtins/src/bitcode.rs b/compiler/builtins/src/bitcode.rs index a220151bc9..0052846424 100644 --- a/compiler/builtins/src/bitcode.rs +++ b/compiler/builtins/src/bitcode.rs @@ -67,3 +67,4 @@ pub const LIST_KEEP_ERRS: &str = "roc_builtins.list.keep_errs"; pub const LIST_WALK: &str = "roc_builtins.list.walk"; pub const LIST_WALK_BACKWARDS: &str = "roc_builtins.list.walk_backwards"; pub const LIST_CONTAINS: &str = "roc_builtins.list.contains"; +pub const LIST_REPEAT: &str = "roc_builtins.list.repeat"; diff --git a/compiler/gen/src/llvm/bitcode.rs b/compiler/gen/src/llvm/bitcode.rs index bd494641d0..fe7b037500 100644 --- a/compiler/gen/src/llvm/bitcode.rs +++ b/compiler/gen/src/llvm/bitcode.rs @@ -207,6 +207,15 @@ fn build_transform_caller_help<'a, 'ctx, 'env>( function_value } +pub fn build_inc_n_wrapper<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + layout_ids: &mut LayoutIds<'a>, + layout: &Layout<'a>, + n: u64, +) -> FunctionValue<'ctx> { + build_rc_wrapper(env, layout_ids, layout, Mode::Inc(n)) +} + pub fn build_inc_wrapper<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index df7e65c4a0..f154a30dfb 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -3597,7 +3597,15 @@ fn run_low_level<'a, 'ctx, 'env>( let inplace = get_inplace_from_layout(layout); - list_repeat(env, inplace, parent, list_len, elem, elem_layout) + list_repeat( + env, + layout_ids, + inplace, + parent, + list_len, + elem, + elem_layout, + ) } ListReverse => { // List.reverse : List elem -> List elem diff --git a/compiler/gen/src/llvm/build_list.rs b/compiler/gen/src/llvm/build_list.rs index c2f3639f0d..8c80e3886d 100644 --- a/compiler/gen/src/llvm/build_list.rs +++ b/compiler/gen/src/llvm/build_list.rs @@ -1,6 +1,7 @@ #![allow(clippy::too_many_arguments)] use crate::llvm::bitcode::{ - build_eq_wrapper, build_transform_caller, call_bitcode_fn, call_void_bitcode_fn, + build_eq_wrapper, build_inc_wrapper, build_transform_caller, call_bitcode_fn, + call_void_bitcode_fn, }; use crate::llvm::build::{ allocate_with_refcount_help, build_num_binop, cast_basic_basic, complex_bitcast, Env, InPlace, @@ -53,90 +54,45 @@ 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>, + layout_ids: &mut LayoutIds<'a>, inplace: InPlace, parent: FunctionValue<'ctx>, list_len: IntValue<'ctx>, - elem: BasicValueEnum<'ctx>, - elem_layout: &Layout<'a>, + element: BasicValueEnum<'ctx>, + element_layout: &Layout<'a>, ) -> BasicValueEnum<'ctx> { let builder = env.builder; - let ctx = env.context; - // list_len > 0 - // We have to do a loop below, continuously adding the `elem` - // to the output list `List elem` until we have reached the - // number of repeats. This `comparison` is used to check - // if we need to do any looping; because if we dont, then we - // dont need to allocate memory for the index or the check - // if index != 0 - let comparison = builder.build_int_compare( - IntPredicate::UGT, - list_len, - ctx.i64_type().const_int(0, false), - "atleastzero", + let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic); + + let element_ptr = builder.build_alloca(element.get_type(), "element_ptr"); + env.builder.build_store(element_ptr, element); + + let element_width = env + .ptr_int() + .const_int(element_layout.stack_size(env.ptr_bytes) as u64, false); + + let alignment = element_layout.alignment_bytes(env.ptr_bytes); + let alignment_iv = env.ptr_int().const_int(alignment as u64, false); + let inc_element_fn = build_inc_wrapper(env, layout_ids, element_layout); + + let output = call_bitcode_fn( + env, + &[ + list_len.into(), + alignment_iv.into(), + env.builder.build_bitcast(element_ptr, u8_ptr, "to_u8_ptr"), + element_width.into(), + inc_element_fn.as_global_value().as_pointer_value().into(), + ], + bitcode::LIST_REPEAT, ); - let build_then = || { - // Allocate space for the new array that we'll copy into. - 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"; - let start_alloca = builder.build_alloca(ctx.i64_type(), index_name); - - // Start at the last element in the list. - let last_elem_index = builder.build_int_sub( - list_len, - ctx.i64_type().const_int(1, false), - "lastelemindex", - ); - builder.build_store(start_alloca, last_elem_index); - - let loop_bb = ctx.append_basic_block(parent, "loop"); - builder.build_unconditional_branch(loop_bb); - builder.position_at_end(loop_bb); - - // #index = #index - 1 - 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"); - - builder.build_store(start_alloca, next_index); - let elem_ptr = - unsafe { builder.build_in_bounds_gep(list_ptr, &[curr_index], "load_index") }; - - // Mutate the new array in-place to change the element. - builder.build_store(elem_ptr, elem); - - // #index != 0 - let end_cond = builder.build_int_compare( - IntPredicate::NE, - ctx.i64_type().const_int(0, false), - curr_index, - "loopcond", - ); - - let after_bb = ctx.append_basic_block(parent, "afterloop"); - - builder.build_conditional_branch(end_cond, loop_bb, after_bb); - builder.position_at_end(after_bb); - - store_list(env, list_ptr, list_len) - }; - - let build_else = || empty_polymorphic_list(env); - - let struct_type = collection(ctx, env.ptr_bytes); - - build_basic_phi2( - env, - parent, - comparison, - build_then, - build_else, - BasicTypeEnum::StructType(struct_type), + complex_bitcast( + env.builder, + output, + collection(env.context, env.ptr_bytes).into(), + "from_i128", ) } @@ -1532,7 +1488,7 @@ where "bounds_check", ); - let after_loop_bb = ctx.append_basic_block(parent, "after_outer_loop"); + let after_loop_bb = ctx.append_basic_block(parent, "after_outer_loop_1"); builder.build_conditional_branch(condition, loop_bb, after_loop_bb); builder.position_at_end(after_loop_bb); @@ -1599,7 +1555,7 @@ where // #index < end let loop_end_cond = bounds_check_comparison(builder, next_index, end); - let after_loop_bb = ctx.append_basic_block(parent, "after_outer_loop"); + let after_loop_bb = ctx.append_basic_block(parent, "after_outer_loop_2"); builder.build_conditional_branch(loop_end_cond, loop_bb, after_loop_bb); builder.position_at_end(after_loop_bb); diff --git a/compiler/mono/src/borrow.rs b/compiler/mono/src/borrow.rs index f142d01368..e4fef005b4 100644 --- a/compiler/mono/src/borrow.rs +++ b/compiler/mono/src/borrow.rs @@ -635,7 +635,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] { ListConcat | StrConcat => arena.alloc_slice_copy(&[owned, borrowed]), StrSplit => arena.alloc_slice_copy(&[borrowed, borrowed]), ListSingle => arena.alloc_slice_copy(&[irrelevant]), - ListRepeat => arena.alloc_slice_copy(&[irrelevant, irrelevant]), + ListRepeat => arena.alloc_slice_copy(&[irrelevant, borrowed]), ListReverse => arena.alloc_slice_copy(&[owned]), ListPrepend => arena.alloc_slice_copy(&[owned, owned]), StrJoinWith => arena.alloc_slice_copy(&[irrelevant, irrelevant]), diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 0b94f7a489..4ed2d56c5d 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -17,7 +17,7 @@ use roc_types::subs::{Content, FlatType, Subs, Variable}; use std::collections::HashMap; use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder}; -pub const PRETTY_PRINT_IR_SYMBOLS: bool = false; +pub const PRETTY_PRINT_IR_SYMBOLS: bool = true; macro_rules! return_on_layout_error { ($env:expr, $layout_result:expr) => { diff --git a/examples/benchmarks/AStarTests.roc b/examples/benchmarks/AStarTests.roc index 84ee16b823..80401e51db 100644 --- a/examples/benchmarks/AStarTests.roc +++ b/examples/benchmarks/AStarTests.roc @@ -9,14 +9,15 @@ fromList = \list -> List.walk list (\x, a -> Set.insert a x) Set.empty main : Task.Task {} [] main = - Task.after Task.getInt \n -> - when n is - 1 -> - Task.putLine (showBool test1) - - _ -> - ns = Str.fromInt n - Task.putLine "No test \(ns)" + Task.putLine (showBool test1) +# Task.after Task.getInt \n -> +# when n is +# 1 -> +# Task.putLine (showBool test1) +# +# _ -> +# ns = Str.fromInt n +# Task.putLine "No test \(ns)" showBool : Bool -> Str showBool = \b -> From 9faf84be160d764f9dd5b1f6f755b9efc79f26dc Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 19 Feb 2021 01:08:33 +0100 Subject: [PATCH 02/25] correct RC for List.repeat, List.keepIf, List.keepOks --- compiler/builtins/bitcode/src/list.zig | 51 ++++++++++++-------- compiler/gen/src/llvm/build_list.rs | 12 ++++- compiler/gen/src/llvm/refcounting.rs | 64 +++++++++++++++++--------- 3 files changed, 84 insertions(+), 43 deletions(-) diff --git a/compiler/builtins/bitcode/src/list.zig b/compiler/builtins/bitcode/src/list.zig index 9ea6c399d9..e68c6e3603 100644 --- a/compiler/builtins/bitcode/src/list.zig +++ b/compiler/builtins/bitcode/src/list.zig @@ -61,7 +61,7 @@ pub const RocList = extern struct { } // unfortunately, we have to clone - var new_list = RocList.allocate(allocator, self.length, alignment, element_width); + var new_list = RocList.allocate(allocator, alignment, self.length, element_width); var old_bytes: [*]u8 = @ptrCast([*]u8, self.bytes); var new_bytes: [*]u8 = @ptrCast([*]u8, new_list.bytes); @@ -152,7 +152,7 @@ pub fn listMapWithIndex(list: RocList, transform: Opaque, caller: Caller2, align } } -pub fn listKeepIf(list: RocList, transform: Opaque, caller: Caller1, alignment: usize, element_width: usize) callconv(.C) RocList { +pub fn listKeepIf(list: RocList, transform: Opaque, caller: Caller1, alignment: usize, element_width: usize, inc: Inc, dec: Dec) callconv(.C) RocList { if (list.bytes) |source_ptr| { const size = list.len(); var i: usize = 0; @@ -163,6 +163,7 @@ pub fn listKeepIf(list: RocList, transform: Opaque, caller: Caller1, alignment: while (i < size) : (i += 1) { var keep = false; const element = source_ptr + (i * element_width); + inc(element); caller(transform, element, @ptrCast(?[*]u8, &keep)); if (keep) { @@ -170,29 +171,36 @@ pub fn listKeepIf(list: RocList, transform: Opaque, caller: Caller1, alignment: kept += 1; } else { - // TODO decrement the value? + dec(element); } } - output.length = kept; - + // consume the input list utils.decref(std.heap.c_allocator, alignment, list.bytes, size * element_width); - return output; + if (kept == 0) { + // if the output is empty, deallocate the space we made for the result + utils.decref(std.heap.c_allocator, alignment, output.bytes, size * element_width); + return RocList.empty(); + } else { + output.length = kept; + + return output; + } } else { return RocList.empty(); } } -pub fn listKeepOks(list: RocList, transform: Opaque, caller: Caller1, alignment: usize, before_width: usize, result_width: usize, after_width: usize) callconv(.C) RocList { - return listKeepResult(list, RocResult.isOk, transform, caller, alignment, before_width, result_width, after_width); +pub fn listKeepOks(list: RocList, transform: Opaque, caller: Caller1, alignment: usize, before_width: usize, result_width: usize, after_width: usize, dec_result: Dec) callconv(.C) RocList { + return listKeepResult(list, RocResult.isOk, transform, caller, alignment, before_width, result_width, after_width, dec_result); } -pub fn listKeepErrs(list: RocList, transform: Opaque, caller: Caller1, alignment: usize, before_width: usize, result_width: usize, after_width: usize) callconv(.C) RocList { - return listKeepResult(list, RocResult.isErr, transform, caller, alignment, before_width, result_width, after_width); +pub fn listKeepErrs(list: RocList, transform: Opaque, caller: Caller1, alignment: usize, before_width: usize, result_width: usize, after_width: usize, dec_result: Dec) callconv(.C) RocList { + return listKeepResult(list, RocResult.isErr, transform, caller, alignment, before_width, result_width, after_width, dec_result); } -pub fn listKeepResult(list: RocList, is_good_constructor: fn (RocResult) bool, transform: Opaque, caller: Caller1, alignment: usize, before_width: usize, result_width: usize, after_width: usize) RocList { +pub fn listKeepResult(list: RocList, is_good_constructor: fn (RocResult) bool, transform: Opaque, caller: Caller1, alignment: usize, before_width: usize, result_width: usize, after_width: usize, dec_result: Dec) RocList { if (list.bytes) |source_ptr| { const size = list.len(); var i: usize = 0; @@ -203,23 +211,30 @@ pub fn listKeepResult(list: RocList, is_good_constructor: fn (RocResult) bool, t var kept: usize = 0; while (i < size) : (i += 1) { - const element = source_ptr + (i * before_width); - caller(transform, element, temporary); + const before_element = source_ptr + (i * before_width); + caller(transform, before_element, temporary); const result = utils.RocResult{ .bytes = temporary }; + const after_element = temporary + @sizeOf(i64); if (is_good_constructor(result)) { - @memcpy(target_ptr + (kept * after_width), temporary + @sizeOf(i64), after_width); - + @memcpy(target_ptr + (kept * after_width), after_element, after_width); kept += 1; + } else { + dec_result(temporary); } } - output.length = kept; - utils.decref(std.heap.c_allocator, alignment, list.bytes, size * before_width); + std.heap.c_allocator.free(temporary[0..result_width]); - return output; + if (kept == 0) { + utils.decref(std.heap.c_allocator, alignment, output.bytes, size * after_width); + return RocList.empty(); + } else { + output.length = kept; + return output; + } } else { return RocList.empty(); } diff --git a/compiler/gen/src/llvm/build_list.rs b/compiler/gen/src/llvm/build_list.rs index 8c80e3886d..2525dbad66 100644 --- a/compiler/gen/src/llvm/build_list.rs +++ b/compiler/gen/src/llvm/build_list.rs @@ -1,7 +1,7 @@ #![allow(clippy::too_many_arguments)] use crate::llvm::bitcode::{ - build_eq_wrapper, build_inc_wrapper, build_transform_caller, call_bitcode_fn, - call_void_bitcode_fn, + build_dec_wrapper, build_eq_wrapper, build_inc_wrapper, build_transform_caller, + call_bitcode_fn, call_void_bitcode_fn, }; use crate::llvm::build::{ allocate_with_refcount_help, build_num_binop, cast_basic_basic, complex_bitcast, Env, InPlace, @@ -984,6 +984,9 @@ pub fn list_keep_if<'a, 'ctx, 'env>( let alignment = element_layout.alignment_bytes(env.ptr_bytes); let alignment_iv = env.ptr_int().const_int(alignment as u64, false); + let inc_element_fn = build_inc_wrapper(env, layout_ids, element_layout); + let dec_element_fn = build_dec_wrapper(env, layout_ids, element_layout); + let output = call_bitcode_fn( env, &[ @@ -993,6 +996,8 @@ pub fn list_keep_if<'a, 'ctx, 'env>( stepper_caller.into(), alignment_iv.into(), element_width.into(), + inc_element_fn.as_global_value().as_pointer_value().into(), + dec_element_fn.as_global_value().as_pointer_value().into(), ], &bitcode::LIST_KEEP_IF, ); @@ -1094,6 +1099,8 @@ pub fn list_keep_result<'a, 'ctx, 'env>( let alignment = before_layout.alignment_bytes(env.ptr_bytes); let alignment_iv = env.ptr_int().const_int(alignment as u64, false); + let dec_result_fn = build_dec_wrapper(env, layout_ids, result_layout); + let output = call_bitcode_fn( env, &[ @@ -1105,6 +1112,7 @@ pub fn list_keep_result<'a, 'ctx, 'env>( before_width.into(), result_width.into(), after_width.into(), + dec_result_fn.as_global_value().as_pointer_value().into(), ], op, ); diff --git a/compiler/gen/src/llvm/refcounting.rs b/compiler/gen/src/llvm/refcounting.rs index 473b578458..7ae98ecd71 100644 --- a/compiler/gen/src/llvm/refcounting.rs +++ b/compiler/gen/src/llvm/refcounting.rs @@ -342,30 +342,16 @@ fn modify_refcount_builtin<'a, 'ctx, 'env>( match builtin { List(memory_mode, element_layout) => { let wrapper_struct = value.into_struct_value(); - if element_layout.contains_refcounted() { - let ptr_type = - basic_type_from_layout(env.arena, env.context, element_layout, env.ptr_bytes) - .ptr_type(AddressSpace::Generic); - - let (len, ptr) = load_list(env.builder, wrapper_struct, ptr_type); - - let loop_fn = |_index, element| { - modify_refcount_layout(env, parent, layout_ids, mode, element, element_layout); - }; - - incrementing_elem_loop( - env.builder, - env.context, - parent, - ptr, - len, - "modify_rc_index", - loop_fn, - ); - } if let MemoryMode::Refcounted = memory_mode { - modify_refcount_list(env, layout_ids, mode, layout, wrapper_struct); + modify_refcount_list( + env, + layout_ids, + mode, + layout, + element_layout, + wrapper_struct, + ); } } Set(element_layout) => { @@ -511,6 +497,7 @@ fn modify_refcount_list<'a, 'ctx, 'env>( layout_ids: &mut LayoutIds<'a>, mode: Mode, layout: &Layout<'a>, + element_layout: &Layout<'a>, original_wrapper: StructValue<'ctx>, ) { let block = env.builder.get_insert_block().expect("to be in a function"); @@ -531,7 +518,14 @@ fn modify_refcount_list<'a, 'ctx, 'env>( let basic_type = basic_type_from_layout(env.arena, env.context, &layout, env.ptr_bytes); let function_value = build_header(env, basic_type, mode, &fn_name); - modify_refcount_list_help(env, mode, layout, function_value); + modify_refcount_list_help( + env, + layout_ids, + mode, + layout, + element_layout, + function_value, + ); function_value } @@ -553,8 +547,10 @@ fn mode_to_call_mode(function: FunctionValue<'_>, mode: Mode) -> CallMode<'_> { fn modify_refcount_list_help<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, + layout_ids: &mut LayoutIds<'a>, mode: Mode, layout: &Layout<'a>, + element_layout: &Layout<'a>, fn_val: FunctionValue<'ctx>, ) { let builder = env.builder; @@ -593,6 +589,28 @@ fn modify_refcount_list_help<'a, 'ctx, 'env>( builder.position_at_end(modification_block); + if element_layout.contains_refcounted() { + let ptr_type = + basic_type_from_layout(env.arena, env.context, element_layout, env.ptr_bytes) + .ptr_type(AddressSpace::Generic); + + let (len, ptr) = load_list(env.builder, original_wrapper, ptr_type); + + let loop_fn = |_index, element| { + modify_refcount_layout(env, parent, layout_ids, mode, element, element_layout); + }; + + incrementing_elem_loop( + env.builder, + env.context, + parent, + ptr, + len, + "modify_rc_index", + loop_fn, + ); + } + let refcount_ptr = PointerToRefcount::from_list_wrapper(env, original_wrapper); let call_mode = mode_to_call_mode(fn_val, mode); refcount_ptr.modify(call_mode, layout, env); From 9cfadb1b5a70c1032cc1d194547f716eceb7b3e7 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 19 Feb 2021 17:07:27 +0100 Subject: [PATCH 03/25] modify example code --- examples/benchmarks/AStar.roc | 84 ++++++++++++++++-------------- examples/benchmarks/AStarTests.roc | 15 +++--- 2 files changed, 52 insertions(+), 47 deletions(-) diff --git a/examples/benchmarks/AStar.roc b/examples/benchmarks/AStar.roc index 78f0022bf5..9c73b7bef2 100644 --- a/examples/benchmarks/AStar.roc +++ b/examples/benchmarks/AStar.roc @@ -1,6 +1,6 @@ -interface AStar exposes [ findPath, Model, initialModel ] imports [Quicksort] +interface AStar exposes [ findPath, Model, initialModel, cheapestOpen, takeStep, reconstructPath ] imports [Quicksort] -findPath = \costFn, moveFn, start, end -> +findPath = \costFn, moveFn, start, end -> astar costFn moveFn end (initialModel start) Model position : @@ -14,9 +14,9 @@ Model position : initialModel : position -> Model position initialModel = \start -> { - evaluated : Set.empty, + evaluated : Set.empty, openSet : Set.singleton start, - costs : Dict.singleton start 0, + costs : Dict.singleton start 0, cameFrom : Dict.empty } @@ -50,44 +50,28 @@ reconstructPath = \cameFrom, goal -> updateCost : position, position, Model position -> Model position updateCost = \current, neighbor, model -> + newCameFrom = + Dict.insert model.cameFrom neighbor current + + newCosts = + Dict.insert model.costs neighbor distanceTo + + distanceTo = + reconstructPath newCameFrom neighbor + |> List.len + |> Num.toFloat + + newModel = + { model & + costs: newCosts, + cameFrom: newCameFrom + } + when Dict.get model.costs neighbor is Err _ -> - newCameFrom = - Dict.insert model.cameFrom neighbor current - - newCosts = - Dict.insert model.costs neighbor distanceTo - - distanceTo = - reconstructPath newCameFrom neighbor - |> List.len - |> Num.toFloat - - { model & - costs: newCosts, - cameFrom: newCameFrom - } + newModel Ok previousDistance -> - - newCameFrom = - Dict.insert model.cameFrom neighbor current - - newCosts = - Dict.insert model.costs neighbor distanceTo - - distanceTo = - reconstructPath newCameFrom neighbor - |> List.len - |> Num.toFloat - - newModel = - { model & - costs: newCosts, - cameFrom: newCameFrom - } - - if distanceTo < previousDistance then newModel @@ -126,3 +110,27 @@ astar = \costFn, moveFn, goal, model -> Set.walk newNeighbors (\n, m -> updateCost current n m) modelWithNeighbors astar costFn moveFn goal modelWithCosts + +takeStep = \moveFn, _goal, model, current -> + modelPopped = + { model & + openSet: Set.remove model.openSet current, + evaluated: Set.insert model.evaluated current, + } + + neighbors = moveFn current + + newNeighbors = Set.difference neighbors modelPopped.evaluated + + modelWithNeighbors = { modelPopped & openSet: Set.union modelPopped.openSet newNeighbors } + + # a lot goes wrong here + modelWithCosts = + Set.walk newNeighbors (\n, m -> updateCost current n m) modelWithNeighbors + + modelWithCosts + + + + + diff --git a/examples/benchmarks/AStarTests.roc b/examples/benchmarks/AStarTests.roc index 80401e51db..1cb5909c8f 100644 --- a/examples/benchmarks/AStarTests.roc +++ b/examples/benchmarks/AStarTests.roc @@ -3,13 +3,10 @@ app "astar-tests" imports [base.Task, AStar] provides [ main ] to base -fromList : List a -> Set a -fromList = \list -> List.walk list (\x, a -> Set.insert a x) Set.empty - - main : Task.Task {} [] main = Task.putLine (showBool test1) + # Task.after Task.getInt \n -> # when n is # 1 -> @@ -27,17 +24,17 @@ showBool = \b -> test1 : Bool test1 = - example1 == [3, 4] + example1 == [2, 4] example1 : List I64 example1 = step : I64 -> Set I64 step = \n -> when n is - 1 -> fromList [ 2,3 ] - 2 -> fromList [4] - 3 -> fromList [4] - _ -> fromList [] + 1 -> Set.fromList [ 2,3 ] + 2 -> Set.fromList [4] + 3 -> Set.fromList [4] + _ -> Set.fromList [] cost : I64, I64 -> F64 cost = \_, _ -> 1 From 20921f94fba9f61825393d7b11a4e5bcaf7c66f6 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 19 Feb 2021 17:08:22 +0100 Subject: [PATCH 04/25] don't abuse var symbols --- compiler/can/src/builtins.rs | 2 +- compiler/module/src/symbol.rs | 23 ++++++++++++----------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index 2865f31dff..fdff0a4e2c 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -2042,7 +2042,7 @@ fn dict_get(symbol: Symbol, var_store: &mut VarStore) -> Def { let arg_dict = Symbol::ARG_1; let arg_key = Symbol::ARG_2; - let temp_record = Symbol::ARG_3; + let temp_record = Symbol::DICT_GET_RESULT; let bool_var = var_store.fresh(); let flag_var = var_store.fresh(); diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index 25fb2cb0fb..54700dd492 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -912,23 +912,24 @@ define_builtins! { 2 DICT_EMPTY: "empty" 3 DICT_SINGLETON: "singleton" 4 DICT_GET: "get" - 5 DICT_INSERT: "insert" - 6 DICT_LEN: "len" + 5 DICT_GET_RESULT: "#get_result" // symbol used in the definition of Dict.get + 6 DICT_WALK: "walk" + 7 DICT_INSERT: "insert" + 8 DICT_LEN: "len" // This should not be exposed to users, its for testing the // hash function ONLY - 7 DICT_TEST_HASH: "hashTestOnly" + 9 DICT_TEST_HASH: "hashTestOnly" - 8 DICT_REMOVE: "remove" - 9 DICT_CONTAINS: "contains" - 10 DICT_KEYS: "keys" - 11 DICT_VALUES: "values" + 10 DICT_REMOVE: "remove" + 11 DICT_CONTAINS: "contains" + 12 DICT_KEYS: "keys" + 13 DICT_VALUES: "values" - 12 DICT_UNION: "union" - 13 DICT_INTERSECTION: "intersection" - 14 DICT_DIFFERENCE: "difference" + 14 DICT_UNION: "union" + 15 DICT_INTERSECTION: "intersection" + 16 DICT_DIFFERENCE: "difference" - 15 DICT_WALK: "walk" } 7 SET: "Set" => { From b24882eb9550b3fbcc19ffc5459cf674758a7881 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 19 Feb 2021 17:10:30 +0100 Subject: [PATCH 05/25] adjust borrow signatures of builtins --- compiler/mono/src/borrow.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/mono/src/borrow.rs b/compiler/mono/src/borrow.rs index e4fef005b4..0be5621e2d 100644 --- a/compiler/mono/src/borrow.rs +++ b/compiler/mono/src/borrow.rs @@ -632,16 +632,16 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] { ListSet => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]), ListSetInPlace => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]), ListGetUnsafe => arena.alloc_slice_copy(&[borrowed, irrelevant]), - ListConcat | StrConcat => arena.alloc_slice_copy(&[owned, borrowed]), + ListConcat | StrConcat => arena.alloc_slice_copy(&[borrowed, borrowed]), StrSplit => arena.alloc_slice_copy(&[borrowed, borrowed]), ListSingle => arena.alloc_slice_copy(&[irrelevant]), ListRepeat => arena.alloc_slice_copy(&[irrelevant, borrowed]), ListReverse => arena.alloc_slice_copy(&[owned]), ListPrepend => arena.alloc_slice_copy(&[owned, owned]), - StrJoinWith => arena.alloc_slice_copy(&[irrelevant, irrelevant]), + StrJoinWith => arena.alloc_slice_copy(&[borrowed, borrowed]), ListJoin => arena.alloc_slice_copy(&[irrelevant]), ListMap | ListMapWithIndex => arena.alloc_slice_copy(&[owned, irrelevant]), - ListKeepIf | ListKeepOks | ListKeepErrs => arena.alloc_slice_copy(&[owned, irrelevant]), + ListKeepIf | ListKeepOks | ListKeepErrs => arena.alloc_slice_copy(&[owned, borrowed]), ListContains => arena.alloc_slice_copy(&[borrowed, irrelevant]), ListWalk => arena.alloc_slice_copy(&[owned, irrelevant, owned]), ListWalkBackwards => arena.alloc_slice_copy(&[owned, irrelevant, owned]), @@ -651,9 +651,11 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] { // List.append should own its first argument ListAppend => arena.alloc_slice_copy(&[borrowed, owned]), - Eq | NotEq | And | Or | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumSubWrap - | NumSubChecked | NumMul | NumMulWrap | NumMulChecked | NumGt | NumGte | NumLt | NumLte - | NumCompare | NumDivUnchecked | NumRemUnchecked | NumPow | NumPowInt | NumBitwiseAnd + Eq | NotEq => arena.alloc_slice_copy(&[borrowed, borrowed]), + + And | Or | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumSubWrap | NumSubChecked + | NumMul | NumMulWrap | NumMulChecked | NumGt | NumGte | NumLt | NumLte | NumCompare + | NumDivUnchecked | NumRemUnchecked | NumPow | NumPowInt | NumBitwiseAnd | NumBitwiseXor => arena.alloc_slice_copy(&[irrelevant, irrelevant]), NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumRound | NumCeiling | NumFloor From 207a5eb537385ac19758e4b7735b689b61a4fbd5 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 19 Feb 2021 17:11:29 +0100 Subject: [PATCH 06/25] change how invoke refcounting works (the live variable calculation was incorrect) --- compiler/mono/src/inc_dec.rs | 51 +++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/compiler/mono/src/inc_dec.rs b/compiler/mono/src/inc_dec.rs index 77c2dc6bd2..5465f0f7ae 100644 --- a/compiler/mono/src/inc_dec.rs +++ b/compiler/mono/src/inc_dec.rs @@ -729,8 +729,12 @@ impl<'a> Context<'a> { layout, } => { // TODO this combines parts of Let and Switch. Did this happen correctly? - let mut case_live_vars = collect_stmt(stmt, &self.jp_live_vars, MutSet::default()); + let mut case_live_vars = collect_stmt(pass, &self.jp_live_vars, MutSet::default()); + case_live_vars.extend(collect_stmt(fail, &self.jp_live_vars, MutSet::default())); + // the result of an invoke should not be touched in the fail branch + // but it should be present in the pass branch (otherwise it would be dead) + debug_assert!(case_live_vars.contains(symbol)); case_live_vars.remove(symbol); let fail = { @@ -758,9 +762,50 @@ impl<'a> Context<'a> { layout: layout.clone(), }; - let stmt = self.arena.alloc(invoke); + let cont = self.arena.alloc(invoke); - (stmt, case_live_vars) + use crate::ir::CallType; + let stmt = match &call.call_type { + CallType::LowLevel { op } => { + let ps = crate::borrow::lowlevel_borrow_signature(self.arena, *op); + self.add_dec_after_lowlevel(call.arguments, ps, cont, &case_live_vars) + } + + CallType::Foreign { .. } => { + let ps = crate::borrow::foreign_borrow_signature( + self.arena, + call.arguments.len(), + ); + self.add_dec_after_lowlevel(call.arguments, ps, cont, &case_live_vars) + } + + CallType::ByName { + name, full_layout, .. + } => { + // get the borrow signature + match self.param_map.get_symbol(*name, full_layout.clone()) { + Some(ps) => self.add_dec_after_application( + call.arguments, + ps, + cont, + &case_live_vars, + ), + None => self.add_inc_before_consume_all( + call.arguments, + cont, + &case_live_vars, + ), + } + } + CallType::ByPointer { .. } => { + self.add_inc_before_consume_all(call.arguments, cont, &case_live_vars) + } + }; + + let mut invoke_live_vars = case_live_vars; + occuring_variables_call(call, &mut invoke_live_vars); + + (stmt, invoke_live_vars) } Join { id: j, From c10c4424736de0dfd42fee1158adb51da3e22447 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 19 Feb 2021 17:12:11 +0100 Subject: [PATCH 07/25] add better debug info --- compiler/mono/src/ir.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 4ed2d56c5d..0b4c013518 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -17,7 +17,7 @@ use roc_types::subs::{Content, FlatType, Subs, Variable}; use std::collections::HashMap; use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder}; -pub const PRETTY_PRINT_IR_SYMBOLS: bool = true; +pub const PRETTY_PRINT_IR_SYMBOLS: bool = false; macro_rules! return_on_layout_error { ($env:expr, $layout_result:expr) => { @@ -5241,12 +5241,6 @@ fn store_pattern_help<'a>( match store_pattern_help(env, procs, layout_cache, argument, symbol, stmt) { StorePattern::Productive(new) => { is_productive = true; - println!( - "Access {:?}.{:?} {:?}", - tag_name.clone(), - outer_symbol, - index - ); stmt = new; // only if we bind one of its (sub)fields to a used name should we // extract the field @@ -5948,7 +5942,8 @@ fn call_by_name<'a>( debug_assert_eq!( arg_layouts.len(), field_symbols.len(), - "see call_by_name for background (scroll down a bit)" + "see call_by_name for background (scroll down a bit), function is {:?}", + proc_name, ); let call = self::Call { @@ -5999,7 +5994,8 @@ fn call_by_name<'a>( debug_assert_eq!( arg_layouts.len(), field_symbols.len(), - "see call_by_name for background (scroll down a bit)" + "see call_by_name for background (scroll down a bit), function is {:?}", + proc_name, ); let call = self::Call { From 9e2c1b750f59298f24e80f4702190bb2373994ba Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 19 Feb 2021 17:12:36 +0100 Subject: [PATCH 08/25] enable astar test --- cli/tests/cli_run.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/cli/tests/cli_run.rs b/cli/tests/cli_run.rs index 18314300c6..cbe71a5ad7 100644 --- a/cli/tests/cli_run.rs +++ b/cli/tests/cli_run.rs @@ -228,7 +228,6 @@ mod cli_run { } #[test] - #[ignore] #[serial(astar)] fn run_astar_optimized_1() { check_output_with_stdin( From 17fddaf0dd431802ba4e019bcfffc515de61cfe5 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 19 Feb 2021 17:13:09 +0100 Subject: [PATCH 09/25] refcount details of builtins --- compiler/builtins/bitcode/src/dict.zig | 27 ++++++++++++++++++++----- compiler/builtins/bitcode/src/list.zig | 11 +++++----- compiler/builtins/bitcode/src/str.zig | 5 +++++ compiler/builtins/bitcode/src/utils.zig | 18 ++++++++--------- compiler/gen/src/llvm/build_dict.rs | 5 +++++ compiler/gen/src/llvm/build_list.rs | 2 ++ compiler/gen/src/llvm/refcounting.rs | 2 +- 7 files changed, 50 insertions(+), 20 deletions(-) diff --git a/compiler/builtins/bitcode/src/dict.zig b/compiler/builtins/bitcode/src/dict.zig index f6ae1913db..4d2651682d 100644 --- a/compiler/builtins/bitcode/src/dict.zig +++ b/compiler/builtins/bitcode/src/dict.zig @@ -296,6 +296,10 @@ pub const RocDict = extern struct { } fn getKey(self: *const RocDict, index: usize, alignment: Alignment, key_width: usize, value_width: usize) Opaque { + if (key_width == 0) { + return null; + } + const offset = blk: { if (alignment.keyFirst()) { break :blk (index * key_width); @@ -329,6 +333,10 @@ pub const RocDict = extern struct { } fn getValue(self: *const RocDict, index: usize, alignment: Alignment, key_width: usize, value_width: usize) Opaque { + if (value_width == 0) { + return null; + } + const offset = blk: { if (alignment.keyFirst()) { break :blk (self.capacity() * key_width) + (index * value_width); @@ -492,6 +500,8 @@ pub fn dictRemove(input: RocDict, alignment: Alignment, key: Opaque, key_width: if (dict.dict_entries_len == 0) { const data_bytes = dict.capacity() * slotSize(key_width, value_width); decref(std.heap.c_allocator, alignment, dict.dict_bytes, data_bytes); + output.* = RocDict.empty(); + return; } output.* = dict; @@ -660,6 +670,7 @@ pub fn dictUnion(dict1: RocDict, dict2: RocDict, alignment: Alignment, key_width // we need an extra RC token for the key inc_key(key); + inc_value(value); const dec_key = doNothing; const dec_value = doNothing; @@ -702,7 +713,7 @@ pub fn dictIntersection(dict1: RocDict, dict2: RocDict, alignment: Alignment, ke } } -pub fn dictDifference(dict1: RocDict, dict2: RocDict, alignment: Alignment, key_width: usize, value_width: usize, hash_fn: HashFn, is_eq: EqFn, dec_key: Inc, dec_value: Inc, output: *RocDict) callconv(.C) void { +pub fn dictDifference(dict1: RocDict, dict2: RocDict, alignment: Alignment, key_width: usize, value_width: usize, hash_fn: HashFn, is_eq: EqFn, dec_key: Dec, dec_value: Dec, output: *RocDict) callconv(.C) void { output.* = dict1.makeUnique(std.heap.c_allocator, alignment, key_width, value_width); var i: usize = 0; @@ -748,7 +759,7 @@ pub fn setFromList(list: RocList, alignment: Alignment, key_width: usize, value_ } const StepperCaller = fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void; -pub fn dictWalk(dict: RocDict, stepper: Opaque, stepper_caller: StepperCaller, accum: Opaque, alignment: Alignment, key_width: usize, value_width: usize, accum_width: usize, output: Opaque) callconv(.C) void { +pub fn dictWalk(dict: RocDict, stepper: Opaque, stepper_caller: StepperCaller, accum: Opaque, alignment: Alignment, key_width: usize, value_width: usize, accum_width: usize, inc_key: Inc, inc_value: Inc, output: Opaque) callconv(.C) void { @memcpy(output orelse unreachable, accum orelse unreachable, accum_width); var i: usize = 0; @@ -756,10 +767,16 @@ pub fn dictWalk(dict: RocDict, stepper: Opaque, stepper_caller: StepperCaller, a while (i < size) : (i += 1) { switch (dict.getSlot(i, key_width, value_width)) { Slot.Filled => { - const key = dict.getKey(i, alignment, key_width, value_width); - const value = dict.getValue(i, alignment, key_width, value_width); + if (value_width == 0) { + const key = dict.getKey(i, alignment, key_width, value_width); - stepper_caller(stepper, key, value, output, output); + stepper_caller(stepper, key, null, output, output); + } else { + const key = dict.getKey(i, alignment, key_width, value_width); + const value = dict.getValue(i, alignment, key_width, value_width); + + stepper_caller(stepper, key, value, output, output); + } }, else => {}, } diff --git a/compiler/builtins/bitcode/src/list.zig b/compiler/builtins/bitcode/src/list.zig index e68c6e3603..0183945e30 100644 --- a/compiler/builtins/bitcode/src/list.zig +++ b/compiler/builtins/bitcode/src/list.zig @@ -192,15 +192,15 @@ pub fn listKeepIf(list: RocList, transform: Opaque, caller: Caller1, alignment: } } -pub fn listKeepOks(list: RocList, transform: Opaque, caller: Caller1, alignment: usize, before_width: usize, result_width: usize, after_width: usize, dec_result: Dec) callconv(.C) RocList { - return listKeepResult(list, RocResult.isOk, transform, caller, alignment, before_width, result_width, after_width, dec_result); +pub fn listKeepOks(list: RocList, transform: Opaque, caller: Caller1, alignment: usize, before_width: usize, result_width: usize, after_width: usize, inc_closure: Inc, dec_result: Dec) callconv(.C) RocList { + return listKeepResult(list, RocResult.isOk, transform, caller, alignment, before_width, result_width, after_width, inc_closure, dec_result); } -pub fn listKeepErrs(list: RocList, transform: Opaque, caller: Caller1, alignment: usize, before_width: usize, result_width: usize, after_width: usize, dec_result: Dec) callconv(.C) RocList { - return listKeepResult(list, RocResult.isErr, transform, caller, alignment, before_width, result_width, after_width, dec_result); +pub fn listKeepErrs(list: RocList, transform: Opaque, caller: Caller1, alignment: usize, before_width: usize, result_width: usize, after_width: usize, inc_closure: Inc, dec_result: Dec) callconv(.C) RocList { + return listKeepResult(list, RocResult.isErr, transform, caller, alignment, before_width, result_width, after_width, inc_closure, dec_result); } -pub fn listKeepResult(list: RocList, is_good_constructor: fn (RocResult) bool, transform: Opaque, caller: Caller1, alignment: usize, before_width: usize, result_width: usize, after_width: usize, dec_result: Dec) RocList { +pub fn listKeepResult(list: RocList, is_good_constructor: fn (RocResult) bool, transform: Opaque, caller: Caller1, alignment: usize, before_width: usize, result_width: usize, after_width: usize, inc_closure: Inc, dec_result: Dec) RocList { if (list.bytes) |source_ptr| { const size = list.len(); var i: usize = 0; @@ -212,6 +212,7 @@ pub fn listKeepResult(list: RocList, is_good_constructor: fn (RocResult) bool, t var kept: usize = 0; while (i < size) : (i += 1) { const before_element = source_ptr + (i * before_width); + inc_closure(transform); caller(transform, before_element, temporary); const result = utils.RocResult{ .bytes = temporary }; diff --git a/compiler/builtins/bitcode/src/str.zig b/compiler/builtins/bitcode/src/str.zig index 97a4928ee3..9187d35d85 100644 --- a/compiler/builtins/bitcode/src/str.zig +++ b/compiler/builtins/bitcode/src/str.zig @@ -50,6 +50,9 @@ pub const RocStr = extern struct { const length = @sizeOf(usize) + number_of_chars; var new_bytes: []usize = allocator.alloc(usize, length) catch unreachable; + const stdout = std.io.getStdOut().writer(); + stdout.print("Hello, {d}!\n", .{length}) catch unreachable; + if (in_place == InPlace.InPlace) { new_bytes[0] = @intCast(usize, number_of_chars); } else { @@ -833,8 +836,10 @@ pub fn strConcatC(result_in_place: InPlace, arg1: RocStr, arg2: RocStr) callconv fn strConcat(allocator: *Allocator, result_in_place: InPlace, arg1: RocStr, arg2: RocStr) RocStr { if (arg1.isEmpty()) { + // the second argument is borrowed, so we must increment its refcount before returning return RocStr.clone(allocator, result_in_place, arg2); } else if (arg2.isEmpty()) { + // the first argument is owned, so we can return it without cloning return RocStr.clone(allocator, result_in_place, arg1); } else { const combined_length = arg1.len() + arg2.len(); diff --git a/compiler/builtins/bitcode/src/utils.zig b/compiler/builtins/bitcode/src/utils.zig index e95f3ab711..76d8908206 100644 --- a/compiler/builtins/bitcode/src/utils.zig +++ b/compiler/builtins/bitcode/src/utils.zig @@ -16,25 +16,25 @@ pub fn decref( var bytes = bytes_or_null orelse return; - const usizes: [*]usize = @ptrCast([*]usize, @alignCast(8, bytes)); + const isizes: [*]isize = @ptrCast([*]isize, @alignCast(8, bytes)); - const refcount = (usizes - 1)[0]; + const refcount = (isizes - 1)[0]; const refcount_isize = @bitCast(isize, refcount); switch (alignment) { 16 => { - if (refcount == REFCOUNT_ONE) { + if (refcount == REFCOUNT_ONE_ISIZE) { allocator.free((bytes - 16)[0 .. 16 + data_bytes]); } else if (refcount_isize < 0) { - (usizes - 1)[0] = refcount + 1; + (isizes - 1)[0] = refcount - 1; } }, else => { // NOTE enums can currently have an alignment of < 8 - if (refcount == REFCOUNT_ONE) { + if (refcount == REFCOUNT_ONE_ISIZE) { allocator.free((bytes - 8)[0 .. 8 + data_bytes]); } else if (refcount_isize < 0) { - (usizes - 1)[0] = refcount + 1; + (isizes - 1)[0] = refcount - 1; } }, } @@ -72,11 +72,11 @@ pub fn allocateWithRefcount( var new_bytes: []align(8) u8 = allocator.alignedAlloc(u8, 8, length) catch unreachable; - var as_usize_array = @ptrCast([*]usize, new_bytes); + var as_usize_array = @ptrCast([*]isize, new_bytes); if (result_in_place) { - as_usize_array[0] = @intCast(usize, number_of_slots); + as_usize_array[0] = @intCast(isize, number_of_slots); } else { - as_usize_array[0] = REFCOUNT_ONE; + as_usize_array[0] = REFCOUNT_ONE_ISIZE; } var as_u8_array = @ptrCast([*]u8, new_bytes); diff --git a/compiler/gen/src/llvm/build_dict.rs b/compiler/gen/src/llvm/build_dict.rs index 8aa6f17887..61b69c7009 100644 --- a/compiler/gen/src/llvm/build_dict.rs +++ b/compiler/gen/src/llvm/build_dict.rs @@ -747,6 +747,9 @@ pub fn dict_walk<'a, 'ctx, 'env>( let output_ptr = builder.build_alloca(accum_bt, "output_ptr"); + let inc_key_fn = build_inc_wrapper(env, layout_ids, key_layout); + let inc_value_fn = build_inc_wrapper(env, layout_ids, value_layout); + call_void_bitcode_fn( env, &[ @@ -758,6 +761,8 @@ pub fn dict_walk<'a, 'ctx, 'env>( key_width.into(), value_width.into(), accum_width.into(), + inc_key_fn.as_global_value().as_pointer_value().into(), + inc_value_fn.as_global_value().as_pointer_value().into(), env.builder.build_bitcast(output_ptr, u8_ptr, "to_opaque"), ], &bitcode::DICT_WALK, diff --git a/compiler/gen/src/llvm/build_list.rs b/compiler/gen/src/llvm/build_list.rs index 2525dbad66..1ffd6903c2 100644 --- a/compiler/gen/src/llvm/build_list.rs +++ b/compiler/gen/src/llvm/build_list.rs @@ -1099,6 +1099,7 @@ pub fn list_keep_result<'a, 'ctx, 'env>( let alignment = before_layout.alignment_bytes(env.ptr_bytes); let alignment_iv = env.ptr_int().const_int(alignment as u64, false); + let inc_closure = build_inc_wrapper(env, layout_ids, transform_layout); let dec_result_fn = build_dec_wrapper(env, layout_ids, result_layout); let output = call_bitcode_fn( @@ -1112,6 +1113,7 @@ pub fn list_keep_result<'a, 'ctx, 'env>( before_width.into(), result_width.into(), after_width.into(), + inc_closure.as_global_value().as_pointer_value().into(), dec_result_fn.as_global_value().as_pointer_value().into(), ], op, diff --git a/compiler/gen/src/llvm/refcounting.rs b/compiler/gen/src/llvm/refcounting.rs index 7ae98ecd71..9cb25c6037 100644 --- a/compiler/gen/src/llvm/refcounting.rs +++ b/compiler/gen/src/llvm/refcounting.rs @@ -802,7 +802,7 @@ fn modify_refcount_dict_help<'a, 'ctx, 'env>( .into_int_value(); // the block we'll always jump to when we're done - let cont_block = ctx.append_basic_block(parent, "modify_rc_str_cont"); + let cont_block = ctx.append_basic_block(parent, "modify_rc_dict_cont"); let modification_block = ctx.append_basic_block(parent, "modify_rc"); let is_non_empty = builder.build_int_compare( From 9473a332dbfafcabb95f762c879b4e5c08095a82 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 19 Feb 2021 19:22:05 +0100 Subject: [PATCH 10/25] clippy --- compiler/builtins/bitcode/src/str.zig | 3 --- compiler/gen/src/llvm/build.rs | 12 +----------- compiler/gen/src/llvm/build_list.rs | 2 -- compiler/gen/src/llvm/refcounting.rs | 5 +---- compiler/mono/src/borrow.rs | 4 +++- compiler/mono/src/ir.rs | 5 +---- 6 files changed, 6 insertions(+), 25 deletions(-) diff --git a/compiler/builtins/bitcode/src/str.zig b/compiler/builtins/bitcode/src/str.zig index 9187d35d85..7c43dc9de0 100644 --- a/compiler/builtins/bitcode/src/str.zig +++ b/compiler/builtins/bitcode/src/str.zig @@ -50,9 +50,6 @@ pub const RocStr = extern struct { const length = @sizeOf(usize) + number_of_chars; var new_bytes: []usize = allocator.alloc(usize, length) catch unreachable; - const stdout = std.io.getStdOut().writer(); - stdout.print("Hello, {d}!\n", .{length}) catch unreachable; - if (in_place == InPlace.InPlace) { new_bytes[0] = @intCast(usize, number_of_chars); } else { diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index f154a30dfb..5e65bc2cee 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -3595,17 +3595,7 @@ fn run_low_level<'a, 'ctx, 'env>( let list_len = load_symbol(scope, &args[0]).into_int_value(); let (elem, elem_layout) = load_symbol_and_layout(scope, &args[1]); - let inplace = get_inplace_from_layout(layout); - - list_repeat( - env, - layout_ids, - inplace, - parent, - list_len, - elem, - elem_layout, - ) + list_repeat(env, layout_ids, list_len, elem, elem_layout) } ListReverse => { // List.reverse : List elem -> List elem diff --git a/compiler/gen/src/llvm/build_list.rs b/compiler/gen/src/llvm/build_list.rs index 1ffd6903c2..b9ebfa7b3a 100644 --- a/compiler/gen/src/llvm/build_list.rs +++ b/compiler/gen/src/llvm/build_list.rs @@ -55,8 +55,6 @@ pub fn list_single<'a, 'ctx, 'env>( pub fn list_repeat<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, - inplace: InPlace, - parent: FunctionValue<'ctx>, list_len: IntValue<'ctx>, element: BasicValueEnum<'ctx>, element_layout: &Layout<'a>, diff --git a/compiler/gen/src/llvm/refcounting.rs b/compiler/gen/src/llvm/refcounting.rs index 9cb25c6037..57984d7daf 100644 --- a/compiler/gen/src/llvm/refcounting.rs +++ b/compiler/gen/src/llvm/refcounting.rs @@ -330,7 +330,6 @@ pub fn decrement_refcount_layout<'a, 'ctx, 'env>( fn modify_refcount_builtin<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - parent: FunctionValue<'ctx>, layout_ids: &mut LayoutIds<'a>, mode: Mode, value: BasicValueEnum<'ctx>, @@ -394,9 +393,7 @@ fn modify_refcount_layout<'a, 'ctx, 'env>( use Layout::*; match layout { - Builtin(builtin) => { - modify_refcount_builtin(env, parent, layout_ids, mode, value, layout, builtin) - } + Builtin(builtin) => modify_refcount_builtin(env, layout_ids, mode, value, layout, builtin), Union(variant) => { use UnionLayout::*; diff --git a/compiler/mono/src/borrow.rs b/compiler/mono/src/borrow.rs index 0be5621e2d..ba652c0c7b 100644 --- a/compiler/mono/src/borrow.rs +++ b/compiler/mono/src/borrow.rs @@ -610,7 +610,9 @@ impl<'a> BorrowInfState<'a> { } pub fn foreign_borrow_signature(arena: &Bump, arity: usize) -> &[bool] { - let all = bumpalo::vec![in arena; false; arity]; + // NOTE this means that Roc is responsible for cleaning up resources; + // the host cannot (currently) take ownership + let all = bumpalo::vec![in arena; true; arity]; all.into_bump_slice() } diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 0b4c013518..8012934bcd 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -5183,10 +5183,7 @@ fn store_pattern_help<'a>( return StorePattern::NotProductive(stmt); } AppliedTag { - arguments, - layout, - tag_name, - .. + arguments, layout, .. } => { let wrapped = Wrapped::from_layout(layout); let write_tag = wrapped == Wrapped::MultiTagUnion; From a2a31cb962141e4e9139ae3f4e2780632878ae06 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 19 Feb 2021 19:26:29 +0100 Subject: [PATCH 11/25] reuse allocateWithRefcount in str --- compiler/builtins/bitcode/src/str.zig | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/compiler/builtins/bitcode/src/str.zig b/compiler/builtins/bitcode/src/str.zig index 7c43dc9de0..07f3b2d6d7 100644 --- a/compiler/builtins/bitcode/src/str.zig +++ b/compiler/builtins/bitcode/src/str.zig @@ -1,3 +1,4 @@ +const utils = @import("utils.zig"); const std = @import("std"); const mem = std.mem; const always_inline = std.builtin.CallOptions.Modifier.always_inline; @@ -47,18 +48,7 @@ pub const RocStr = extern struct { } pub fn initBig(allocator: *Allocator, in_place: InPlace, number_of_chars: u64) RocStr { - const length = @sizeOf(usize) + number_of_chars; - var new_bytes: []usize = allocator.alloc(usize, length) catch unreachable; - - if (in_place == InPlace.InPlace) { - new_bytes[0] = @intCast(usize, number_of_chars); - } else { - const v: isize = std.math.minInt(isize); - new_bytes[0] = @bitCast(usize, v); - } - - var first_element = @ptrCast([*]align(@alignOf(usize)) u8, new_bytes); - first_element += @sizeOf(usize); + const first_element = utils.allocateWithRefcount(allocator, @sizeOf(usize), number_of_chars); return RocStr{ .str_bytes = first_element, From 903c0d8dec24fa58523bbd4965e2d8c2add877e9 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 19 Feb 2021 19:42:11 +0100 Subject: [PATCH 12/25] enable valgrind for AStar --- cli/tests/cli_run.rs | 2 +- compiler/builtins/bitcode/src/dict.zig | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/cli/tests/cli_run.rs b/cli/tests/cli_run.rs index cbe71a5ad7..24f6b7e7b9 100644 --- a/cli/tests/cli_run.rs +++ b/cli/tests/cli_run.rs @@ -236,7 +236,7 @@ mod cli_run { "astar-tests", &[], "True\n", - false, + true, ); } diff --git a/compiler/builtins/bitcode/src/dict.zig b/compiler/builtins/bitcode/src/dict.zig index 4d2651682d..98f8d2ac9f 100644 --- a/compiler/builtins/bitcode/src/dict.zig +++ b/compiler/builtins/bitcode/src/dict.zig @@ -672,6 +672,7 @@ pub fn dictUnion(dict1: RocDict, dict2: RocDict, alignment: Alignment, key_width inc_key(key); inc_value(value); + // we know the newly added key is not a duplicate, so the `dec`s are unreachable const dec_key = doNothing; const dec_value = doNothing; From ff20ab76ce7bf1ebe11fe844abc2def3e0e5ff60 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 19 Feb 2021 21:29:33 +0100 Subject: [PATCH 13/25] put closure tests into one file --- cli/tests/cli_run.rs | 44 +++----------------------------- compiler/mono/src/ir.rs | 6 +++-- examples/benchmarks/Closure1.roc | 15 ----------- examples/benchmarks/Closure2.roc | 17 ------------ examples/benchmarks/Closure3.roc | 14 ---------- examples/benchmarks/Closure4.roc | 16 ------------ 6 files changed, 8 insertions(+), 104 deletions(-) delete mode 100644 examples/benchmarks/Closure1.roc delete mode 100644 examples/benchmarks/Closure2.roc delete mode 100644 examples/benchmarks/Closure3.roc delete mode 100644 examples/benchmarks/Closure4.roc diff --git a/cli/tests/cli_run.rs b/cli/tests/cli_run.rs index 24f6b7e7b9..7dc993609a 100644 --- a/cli/tests/cli_run.rs +++ b/cli/tests/cli_run.rs @@ -241,47 +241,11 @@ mod cli_run { } #[test] - #[serial(closure1)] - fn closure1() { + #[serial(closure)] + fn closure() { check_output( - &example_file("benchmarks", "Closure1.roc"), - "closure1", - &[], - "", - true, - ); - } - - #[test] - #[serial(closure2)] - fn closure2() { - check_output( - &example_file("benchmarks", "Closure2.roc"), - "closure2", - &[], - "", - true, - ); - } - - #[test] - #[serial(closure3)] - fn closure3() { - check_output( - &example_file("benchmarks", "Closure3.roc"), - "closure3", - &[], - "", - true, - ); - } - - #[test] - #[serial(closure4)] - fn closure4() { - check_output( - &example_file("benchmarks", "Closure4.roc"), - "closure4", + &example_file("benchmarks", "Closure.roc"), + "closure", &[], "", true, diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 8012934bcd..9e6f2ccd47 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -6514,8 +6514,10 @@ fn from_can_pattern_help<'a>( debug_assert_eq!( arguments.len(), argument_layouts[1..].len(), - "{:?}", - tag_name + "The {:?} tag got {} arguments, but its layout expects {}!", + tag_name, + arguments.len(), + argument_layouts[1..].len(), ); let it = argument_layouts[1..].iter(); diff --git a/examples/benchmarks/Closure1.roc b/examples/benchmarks/Closure1.roc deleted file mode 100644 index 6065effa90..0000000000 --- a/examples/benchmarks/Closure1.roc +++ /dev/null @@ -1,15 +0,0 @@ -app "closure1" - packages { base: "platform" } - imports [base.Task] - provides [ main ] to base - -# see https://github.com/rtfeldman/roc/issues/985 - -main : Task.Task {} [] -main = - Task.succeed (foo toUnitBorrowed "a long string such that it's malloced") - |> Task.map (\_ -> {}) - -toUnitBorrowed = \x -> Str.countGraphemes x - -foo = \f, x -> f x diff --git a/examples/benchmarks/Closure2.roc b/examples/benchmarks/Closure2.roc deleted file mode 100644 index d2fc1803d0..0000000000 --- a/examples/benchmarks/Closure2.roc +++ /dev/null @@ -1,17 +0,0 @@ -app "closure2" - packages { base: "platform" } - imports [base.Task] - provides [ main ] to base - -# see https://github.com/rtfeldman/roc/issues/985 - -main : Task.Task {} [] -main = - x : Str - x = "a long string such that it's malloced" - - Task.succeed {} - |> Task.map (\_ -> x) - |> Task.map toUnit - -toUnit = (\_ -> {}) diff --git a/examples/benchmarks/Closure3.roc b/examples/benchmarks/Closure3.roc deleted file mode 100644 index 0f0334d332..0000000000 --- a/examples/benchmarks/Closure3.roc +++ /dev/null @@ -1,14 +0,0 @@ -app "closure3" - packages { base: "platform" } - imports [base.Task] - provides [ main ] to base - -# see https://github.com/rtfeldman/roc/issues/985 - -main : Task.Task {} [] -main = - x : Str - x = "a long string such that it's malloced" - - Task.succeed {} - |> Task.after (\_ -> Task.succeed x |> Task.map (\_ -> {})) diff --git a/examples/benchmarks/Closure4.roc b/examples/benchmarks/Closure4.roc deleted file mode 100644 index 5945e8897c..0000000000 --- a/examples/benchmarks/Closure4.roc +++ /dev/null @@ -1,16 +0,0 @@ -app "closure4" - packages { base: "platform" } - imports [base.Task] - provides [ main ] to base - -# see https://github.com/rtfeldman/roc/issues/985 - -main : Task.Task {} [] -main = - x : Str - x = "a long string such that it's malloced" - - Task.succeed {} - |> Task.after (\_ -> Task.succeed x) - |> Task.map (\_ -> {}) - From a850ccb94ae2d56b581e9ed7e3d26ca691364998 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 19 Feb 2021 22:13:50 +0100 Subject: [PATCH 14/25] use the WhenRecursive trick in refcounting --- compiler/gen/src/llvm/refcounting.rs | 163 +++++++++++++++++++++++++-- 1 file changed, 152 insertions(+), 11 deletions(-) diff --git a/compiler/gen/src/llvm/refcounting.rs b/compiler/gen/src/llvm/refcounting.rs index 57984d7daf..31e4e999e6 100644 --- a/compiler/gen/src/llvm/refcounting.rs +++ b/compiler/gen/src/llvm/refcounting.rs @@ -285,6 +285,7 @@ fn modify_refcount_struct<'a, 'ctx, 'env>( value: BasicValueEnum<'ctx>, layouts: &[Layout<'a>], mode: Mode, + when_recursive: &WhenRecursive<'a>, ) { let wrapper_struct = value.into_struct_value(); @@ -295,7 +296,15 @@ fn modify_refcount_struct<'a, 'ctx, 'env>( .build_extract_value(wrapper_struct, i as u32, "decrement_struct_field") .unwrap(); - modify_refcount_layout(env, parent, layout_ids, mode, field_ptr, field_layout); + modify_refcount_layout_help( + env, + parent, + layout_ids, + mode, + when_recursive, + field_ptr, + field_layout, + ); } } } @@ -332,6 +341,7 @@ fn modify_refcount_builtin<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, mode: Mode, + when_recursive: &WhenRecursive<'a>, value: BasicValueEnum<'ctx>, layout: &Layout<'a>, builtin: &Builtin<'a>, @@ -347,6 +357,7 @@ fn modify_refcount_builtin<'a, 'ctx, 'env>( env, layout_ids, mode, + when_recursive, layout, element_layout, wrapper_struct, @@ -365,6 +376,7 @@ fn modify_refcount_builtin<'a, 'ctx, 'env>( env, layout_ids, mode, + when_recursive, layout, key_layout, value_layout, @@ -389,11 +401,45 @@ fn modify_refcount_layout<'a, 'ctx, 'env>( mode: Mode, value: BasicValueEnum<'ctx>, layout: &Layout<'a>, +) { + modify_refcount_layout_help( + env, + parent, + layout_ids, + mode, + &WhenRecursive::Unreachable, + value, + layout, + ); +} + +#[derive(Clone, Debug, PartialEq)] +enum WhenRecursive<'a> { + Unreachable, + Loop(UnionLayout<'a>), +} + +fn modify_refcount_layout_help<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + parent: FunctionValue<'ctx>, + layout_ids: &mut LayoutIds<'a>, + mode: Mode, + when_recursive: &WhenRecursive<'a>, + value: BasicValueEnum<'ctx>, + layout: &Layout<'a>, ) { use Layout::*; match layout { - Builtin(builtin) => modify_refcount_builtin(env, layout_ids, mode, value, layout, builtin), + Builtin(builtin) => modify_refcount_builtin( + env, + layout_ids, + mode, + when_recursive, + value, + layout, + builtin, + ), Union(variant) => { use UnionLayout::*; @@ -408,6 +454,7 @@ fn modify_refcount_layout<'a, 'ctx, 'env>( env, layout_ids, mode, + &WhenRecursive::Loop(variant.clone()), tags, value.into_pointer_value(), true, @@ -423,6 +470,7 @@ fn modify_refcount_layout<'a, 'ctx, 'env>( env, layout_ids, mode, + &WhenRecursive::Loop(variant.clone()), &*env.arena.alloc([other_fields]), value.into_pointer_value(), true, @@ -436,6 +484,7 @@ fn modify_refcount_layout<'a, 'ctx, 'env>( env, layout_ids, mode, + &WhenRecursive::Loop(variant.clone()), &*env.arena.alloc([*fields]), value.into_pointer_value(), true, @@ -448,13 +497,16 @@ fn modify_refcount_layout<'a, 'ctx, 'env>( env, layout_ids, mode, + &WhenRecursive::Loop(variant.clone()), tags, value.into_pointer_value(), false, ); } - NonRecursive(tags) => modify_refcount_union(env, layout_ids, mode, tags, value), + NonRecursive(tags) => { + modify_refcount_union(env, layout_ids, mode, when_recursive, tags, value) + } } } Closure(_, closure_layout, _) => { @@ -466,11 +518,12 @@ fn modify_refcount_layout<'a, 'ctx, 'env>( .build_extract_value(wrapper_struct, 1, "modify_rc_closure_data") .unwrap(); - modify_refcount_layout( + modify_refcount_layout_help( env, parent, layout_ids, mode, + when_recursive, field_ptr, &closure_layout.as_block_of_memory_layout(), ) @@ -478,12 +531,45 @@ fn modify_refcount_layout<'a, 'ctx, 'env>( } Struct(layouts) => { - modify_refcount_struct(env, parent, layout_ids, value, layouts, mode); + modify_refcount_struct( + env, + parent, + layout_ids, + value, + layouts, + mode, + when_recursive, + ); } PhantomEmptyStruct => {} - RecursivePointer => todo!("TODO implement decrement layout of recursive tag union"), + Layout::RecursivePointer => match when_recursive { + WhenRecursive::Unreachable => { + unreachable!("recursion pointers should never be hashed directly") + } + WhenRecursive::Loop(union_layout) => { + let layout = Layout::Union(union_layout.clone()); + + let bt = basic_type_from_layout(env.arena, env.context, &layout, env.ptr_bytes); + + // cast the i64 pointer to a pointer to block of memory + let field_cast = env + .builder + .build_bitcast(value, bt, "i64_to_opaque") + .into_pointer_value(); + + modify_refcount_layout_help( + env, + parent, + layout_ids, + mode, + when_recursive, + field_cast.into(), + &layout, + ) + } + }, FunctionPointer(_, _) | Pointer(_) => {} } @@ -493,6 +579,7 @@ fn modify_refcount_list<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, mode: Mode, + when_recursive: &WhenRecursive<'a>, layout: &Layout<'a>, element_layout: &Layout<'a>, original_wrapper: StructValue<'ctx>, @@ -519,6 +606,7 @@ fn modify_refcount_list<'a, 'ctx, 'env>( env, layout_ids, mode, + when_recursive, layout, element_layout, function_value, @@ -546,6 +634,7 @@ fn modify_refcount_list_help<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, mode: Mode, + when_recursive: &WhenRecursive<'a>, layout: &Layout<'a>, element_layout: &Layout<'a>, fn_val: FunctionValue<'ctx>, @@ -594,7 +683,15 @@ fn modify_refcount_list_help<'a, 'ctx, 'env>( let (len, ptr) = load_list(env.builder, original_wrapper, ptr_type); let loop_fn = |_index, element| { - modify_refcount_layout(env, parent, layout_ids, mode, element, element_layout); + modify_refcount_layout_help( + env, + parent, + layout_ids, + mode, + when_recursive, + element, + element_layout, + ); }; incrementing_elem_loop( @@ -720,6 +817,7 @@ fn modify_refcount_dict<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, mode: Mode, + when_recursive: &WhenRecursive<'a>, layout: &Layout<'a>, key_layout: &Layout<'a>, value_layout: &Layout<'a>, @@ -747,6 +845,7 @@ fn modify_refcount_dict<'a, 'ctx, 'env>( env, layout_ids, mode, + when_recursive, layout, key_layout, value_layout, @@ -768,11 +867,18 @@ fn modify_refcount_dict_help<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, mode: Mode, + when_recursive: &WhenRecursive<'a>, layout: &Layout<'a>, key_layout: &Layout<'a>, value_layout: &Layout<'a>, fn_val: FunctionValue<'ctx>, ) { + debug_assert_eq!( + when_recursive, + &WhenRecursive::Unreachable, + "TODO pipe when_recursive through the dict key/value inc/dec" + ); + let builder = env.builder; let ctx = env.context; @@ -909,6 +1015,7 @@ fn build_rec_union<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, mode: Mode, + when_recursive: &WhenRecursive<'a>, fields: &'a [&'a [Layout<'a>]], value: PointerValue<'ctx>, is_nullable: bool, @@ -935,7 +1042,15 @@ fn build_rec_union<'a, 'ctx, 'env>( .into(); let function_value = build_header(env, basic_type, mode, &fn_name); - build_rec_union_help(env, layout_ids, mode, fields, function_value, is_nullable); + build_rec_union_help( + env, + layout_ids, + mode, + when_recursive, + fields, + function_value, + is_nullable, + ); env.builder.position_at_end(block); env.builder @@ -952,6 +1067,7 @@ fn build_rec_union_help<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, mode: Mode, + when_recursive: &WhenRecursive<'a>, tags: &[&[Layout<'a>]], fn_val: FunctionValue<'ctx>, is_nullable: bool, @@ -1108,7 +1224,15 @@ fn build_rec_union_help<'a, 'ctx, 'env>( refcount_ptr.modify(call_mode, &layout, env); for (field, field_layout) in deferred_nonrec { - modify_refcount_layout(env, parent, layout_ids, mode, field, field_layout); + modify_refcount_layout_help( + env, + parent, + layout_ids, + mode, + when_recursive, + field, + field_layout, + ); } let call_name = pick("recursive_tag_increment", "recursive_tag_decrement"); @@ -1223,6 +1347,7 @@ fn modify_refcount_union<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, mode: Mode, + when_recursive: &WhenRecursive<'a>, fields: &'a [&'a [Layout<'a>]], value: BasicValueEnum<'ctx>, ) { @@ -1246,7 +1371,14 @@ fn modify_refcount_union<'a, 'ctx, 'env>( let basic_type = block_of_memory(env.context, &layout, env.ptr_bytes); let function_value = build_header(env, basic_type, mode, &fn_name); - modify_refcount_union_help(env, layout_ids, mode, fields, function_value); + modify_refcount_union_help( + env, + layout_ids, + mode, + when_recursive, + fields, + function_value, + ); function_value } @@ -1263,6 +1395,7 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, mode: Mode, + when_recursive: &WhenRecursive<'a>, tags: &[&[Layout<'a>]], fn_val: FunctionValue<'ctx>, ) { @@ -1347,7 +1480,15 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>( .build_extract_value(wrapper_struct, i as u32, "modify_tag_field") .unwrap(); - modify_refcount_layout(env, parent, layout_ids, mode, field_ptr, field_layout); + modify_refcount_layout_help( + env, + parent, + layout_ids, + mode, + when_recursive, + field_ptr, + field_layout, + ); } } From 9a8285386ff9bdf40917b2dd1c233a3cce7d489b Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 19 Feb 2021 22:25:11 +0100 Subject: [PATCH 15/25] fix mono test --- compiler/mono/tests/test_mono.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/mono/tests/test_mono.rs b/compiler/mono/tests/test_mono.rs index 0d14ab39bc..9709e97625 100644 --- a/compiler/mono/tests/test_mono.rs +++ b/compiler/mono/tests/test_mono.rs @@ -646,13 +646,13 @@ mod test_mono { let Test.4 = lowlevel DictEmpty ; ret Test.4; - procedure Dict.6 (#Attr.2): + procedure Dict.8 (#Attr.2): let Test.3 = lowlevel DictSize #Attr.2; ret Test.3; procedure Test.0 (): let Test.2 = FunctionPointer Dict.2; - let Test.1 = CallByName Dict.6 Test.2; + let Test.1 = CallByName Dict.8 Test.2; ret Test.1; "# ), From b43b36dd0019c2bd8a5d9eb63691e10f108e105d Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 19 Feb 2021 22:26:35 +0100 Subject: [PATCH 16/25] clippy --- compiler/gen/src/llvm/refcounting.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/gen/src/llvm/refcounting.rs b/compiler/gen/src/llvm/refcounting.rs index 31e4e999e6..4cd134818e 100644 --- a/compiler/gen/src/llvm/refcounting.rs +++ b/compiler/gen/src/llvm/refcounting.rs @@ -813,6 +813,7 @@ fn modify_refcount_str_help<'a, 'ctx, 'env>( builder.build_return(None); } +#[allow(clippy::too_many_arguments)] fn modify_refcount_dict<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, @@ -863,6 +864,7 @@ fn modify_refcount_dict<'a, 'ctx, 'env>( call_help(env, function, mode, original_wrapper.into(), call_name); } +#[allow(clippy::too_many_arguments)] fn modify_refcount_dict_help<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, From b60421c13f5bd24ac7415efa0d3972e77123b92f Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 19 Feb 2021 23:01:27 +0100 Subject: [PATCH 17/25] add Closure.roc --- examples/benchmarks/Closure.roc | 58 +++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 examples/benchmarks/Closure.roc diff --git a/examples/benchmarks/Closure.roc b/examples/benchmarks/Closure.roc new file mode 100644 index 0000000000..80c570cb72 --- /dev/null +++ b/examples/benchmarks/Closure.roc @@ -0,0 +1,58 @@ +app "closure" + packages { base: "platform" } + imports [base.Task] + provides [ main ] to base + +# see https://github.com/rtfeldman/roc/issues/985 + +main : Task.Task {} [] +main = closure1 {} + |> Task.after (\_ -> closure2 {}) + |> Task.after (\_ -> closure2 {}) + |> Task.after (\_ -> closure2 {}) + +# --- + +closure1 : {} -> Task.Task {} [] +closure1 = \_ -> + Task.succeed (foo toUnitBorrowed "a long string such that it's malloced") + |> Task.map (\_ -> {}) + +toUnitBorrowed = \x -> Str.countGraphemes x + +foo = \f, x -> f x + +# --- + +closure2 : {} -> Task.Task {} [] +closure2 = \_ -> + x : Str + x = "a long string such that it's malloced" + + Task.succeed {} + |> Task.map (\_ -> x) + |> Task.map toUnit + +toUnit = (\_ -> {}) + +# --- + +closure3 : {} -> Task.Task {} [] +closure3 = \_ -> + x : Str + x = "a long string such that it's malloced" + + Task.succeed {} + |> Task.after (\_ -> Task.succeed x |> Task.map (\_ -> {})) + +# --- + +closure4 : {} -> Task.Task {} [] +closure4 = \_ -> + x : Str + x = "a long string such that it's malloced" + + Task.succeed {} + |> Task.after (\_ -> Task.succeed x) + |> Task.map (\_ -> {}) + From 951914c315d9a04789e196cd89d246c81ce19320 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 20 Feb 2021 00:05:18 +0100 Subject: [PATCH 18/25] ignore astar test... --- cli/tests/cli_run.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/cli/tests/cli_run.rs b/cli/tests/cli_run.rs index 7dc993609a..ea61f81a17 100644 --- a/cli/tests/cli_run.rs +++ b/cli/tests/cli_run.rs @@ -228,6 +228,7 @@ mod cli_run { } #[test] + #[ignore] #[serial(astar)] fn run_astar_optimized_1() { check_output_with_stdin( From 1a39fa201cb51220b61bee06496d83b4abaa9272 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 20 Feb 2021 04:16:09 +0100 Subject: [PATCH 19/25] dicts are only 3 words on the stack --- compiler/mono/src/layout.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index 668ba8f72f..b6c1e22bfe 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -877,7 +877,7 @@ impl<'a> Builtin<'a> { /// Number of machine words in an empty one of these pub const STR_WORDS: u32 = 2; - pub const DICT_WORDS: u32 = 6; + pub const DICT_WORDS: u32 = 3; pub const SET_WORDS: u32 = Builtin::DICT_WORDS; // Set is an alias for Dict with {} for value pub const LIST_WORDS: u32 = 2; From 48dee993d05ff35ae159a168d19b39c10c5ae2dd Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 20 Feb 2021 04:25:21 +0100 Subject: [PATCH 20/25] stop aliasing in dictWalk --- compiler/builtins/bitcode/src/dict.zig | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/compiler/builtins/bitcode/src/dict.zig b/compiler/builtins/bitcode/src/dict.zig index 98f8d2ac9f..da03b49e18 100644 --- a/compiler/builtins/bitcode/src/dict.zig +++ b/compiler/builtins/bitcode/src/dict.zig @@ -761,28 +761,35 @@ pub fn setFromList(list: RocList, alignment: Alignment, key_width: usize, value_ const StepperCaller = fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void; pub fn dictWalk(dict: RocDict, stepper: Opaque, stepper_caller: StepperCaller, accum: Opaque, alignment: Alignment, key_width: usize, value_width: usize, accum_width: usize, inc_key: Inc, inc_value: Inc, output: Opaque) callconv(.C) void { - @memcpy(output orelse unreachable, accum orelse unreachable, accum_width); + // allocate space to write the result of the stepper into + // experimentally aliasing the accum and output pointers is not a good idea + const alloc: [*]u8 = @ptrCast([*]u8, std.heap.c_allocator.alloc(u8, accum_width) catch unreachable); + var b1 = output orelse unreachable; + var b2 = alloc; + + @memcpy(b2, accum orelse unreachable, accum_width); var i: usize = 0; const size = dict.capacity(); while (i < size) : (i += 1) { switch (dict.getSlot(i, key_width, value_width)) { Slot.Filled => { - if (value_width == 0) { - const key = dict.getKey(i, alignment, key_width, value_width); + const key = dict.getKey(i, alignment, key_width, value_width); + const value = dict.getValue(i, alignment, key_width, value_width); - stepper_caller(stepper, key, null, output, output); - } else { - const key = dict.getKey(i, alignment, key_width, value_width); - const value = dict.getValue(i, alignment, key_width, value_width); + stepper_caller(stepper, key, value, b2, b1); - stepper_caller(stepper, key, value, output, output); - } + const temp = b1; + b2 = b1; + b1 = temp; }, else => {}, } } + @memcpy(output orelse unreachable, b2, accum_width); + std.heap.c_allocator.free(alloc[0..accum_width]); + const data_bytes = dict.capacity() * slotSize(key_width, value_width); decref(std.heap.c_allocator, alignment, dict.dict_bytes, data_bytes); } From 90ac2aa9cae078213d9b154c4096496f546249fd Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 20 Feb 2021 04:27:30 +0100 Subject: [PATCH 21/25] enable astar test --- cli/tests/cli_run.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/cli/tests/cli_run.rs b/cli/tests/cli_run.rs index ea61f81a17..7dc993609a 100644 --- a/cli/tests/cli_run.rs +++ b/cli/tests/cli_run.rs @@ -228,7 +228,6 @@ mod cli_run { } #[test] - #[ignore] #[serial(astar)] fn run_astar_optimized_1() { check_output_with_stdin( From 6d514997ab78f19262883464b630d4f5a101943b Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Sat, 20 Feb 2021 16:03:01 +0100 Subject: [PATCH 22/25] set RUST_BACKTRACE=1 in earthly --- Earthfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Earthfile b/Earthfile index af5c0340c4..cbf9b86844 100644 --- a/Earthfile +++ b/Earthfile @@ -86,6 +86,7 @@ test-rust: FROM +build-rust ARG RUSTC_WRAPPER=/usr/local/cargo/bin/sccache ARG SCCACHE_DIR=/earthbuild/sccache_dir + ARG RUST_BACKTRACE=1 RUN cargo test --release test-all: @@ -94,4 +95,4 @@ test-all: BUILD +save-cache BUILD +test-zig BUILD +test-rust - \ No newline at end of file + From 06ec1ca95235b9a7eb565f99e7d990f8cad59405 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Sat, 20 Feb 2021 16:38:36 +0100 Subject: [PATCH 23/25] run only AStar --- Earthfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Earthfile b/Earthfile index cbf9b86844..932b3dec8d 100644 --- a/Earthfile +++ b/Earthfile @@ -87,7 +87,8 @@ test-rust: ARG RUSTC_WRAPPER=/usr/local/cargo/bin/sccache ARG SCCACHE_DIR=/earthbuild/sccache_dir ARG RUST_BACKTRACE=1 - RUN cargo test --release + # TODO reenable: RUN cargo test --release + RUN cargo run run examples/benchmarks/AStar.roc test-all: BUILD +check-clippy From 0f5500b564290a97b77a1553aa0a09020ea48c46 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 20 Feb 2021 16:45:39 +0100 Subject: [PATCH 24/25] add Quicksort.roc for use in AStar --- examples/benchmarks/Quicksort.roc | 77 +++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 examples/benchmarks/Quicksort.roc diff --git a/examples/benchmarks/Quicksort.roc b/examples/benchmarks/Quicksort.roc new file mode 100644 index 0000000000..bbd2a90c12 --- /dev/null +++ b/examples/benchmarks/Quicksort.roc @@ -0,0 +1,77 @@ +interface Quicksort exposes [ sortBy, show ] imports [] + +show : List I64 -> Str +show = \list -> + if List.isEmpty list then + "[]" + else + content = + list + |> List.map Str.fromInt + |> Str.joinWith ", " + + "[ \(content) ]" + +sortBy : List a, (a -> Num *) -> List a +sortBy = \list, toComparable -> + sortWith list (\x, y -> Num.compare (toComparable x) (toComparable y)) + +Order a : a, a -> [ LT, GT, EQ ] + +sortWith : List a, (a, a -> [ LT, GT, EQ ]) -> List a +sortWith = \list, order -> + n = List.len list + quicksortHelp list order 0 (n - 1) + +quicksortHelp : List a, Order a, Nat, Nat -> List a +quicksortHelp = \list, order, low, high -> + if low < high then + when partition low high list order is + Pair partitionIndex partitioned -> + partitioned + |> quicksortHelp order low (partitionIndex - 1) + |> quicksortHelp order (partitionIndex + 1) high + else + list + + +partition : Nat, Nat, List a, Order a -> [ Pair Nat (List a) ] +partition = \low, high, initialList, order -> + when List.get initialList high is + Ok pivot -> + when partitionHelp (low - 1) low initialList order high pivot is + Pair newI newList -> + Pair (newI + 1) (swap (newI + 1) high newList) + + Err _ -> + Pair (low - 1) initialList + +partitionHelp : Nat, Nat, List c, Order c, Nat, c -> [ Pair Nat (List c) ] +partitionHelp = \i, j, list, order, high, pivot -> + if j < high then + when List.get list j is + Ok value -> + when order value pivot is + LT | EQ -> + partitionHelp (i + 1) (j + 1) (swap (i + 1) j list) order high pivot + + GT -> + partitionHelp i (j + 1) list order high pivot + + Err _ -> + Pair i list + else + Pair i list + + +swap : Nat, Nat, 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 + + _ -> + [] + From 1a52023286d96c645a1916c397dfdd2806be494f Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 20 Feb 2021 17:22:11 +0100 Subject: [PATCH 25/25] re-enable test suit --- Earthfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Earthfile b/Earthfile index 932b3dec8d..cbf9b86844 100644 --- a/Earthfile +++ b/Earthfile @@ -87,8 +87,7 @@ test-rust: ARG RUSTC_WRAPPER=/usr/local/cargo/bin/sccache ARG SCCACHE_DIR=/earthbuild/sccache_dir ARG RUST_BACKTRACE=1 - # TODO reenable: RUN cargo test --release - RUN cargo run run examples/benchmarks/AStar.roc + RUN cargo test --release test-all: BUILD +check-clippy