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 ->