From d718e21fd3ba4867d21ba2a28a865a9bcecd17cb Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 29 Mar 2021 23:00:49 +0200 Subject: [PATCH] add List.range --- compiler/builtins/bitcode/src/list.zig | 86 +++++++++++++++++++++++++ compiler/builtins/bitcode/src/main.zig | 1 + compiler/builtins/bitcode/src/utils.zig | 52 +++++++++++++++ compiler/builtins/src/bitcode.rs | 1 + compiler/builtins/src/std.rs | 10 ++- compiler/can/src/builtins.rs | 7 ++ compiler/gen/src/llvm/build.rs | 22 ++++++- compiler/gen/src/llvm/build_list.rs | 73 +++++++++++++++++++++ compiler/module/src/low_level.rs | 1 + compiler/module/src/symbol.rs | 1 + compiler/mono/src/borrow.rs | 1 + 11 files changed, 253 insertions(+), 2 deletions(-) diff --git a/compiler/builtins/bitcode/src/list.zig b/compiler/builtins/bitcode/src/list.zig index 139163af1d..08364a4198 100644 --- a/compiler/builtins/bitcode/src/list.zig +++ b/compiler/builtins/bitcode/src/list.zig @@ -557,3 +557,89 @@ pub fn listAppend(list: RocList, alignment: usize, element: Opaque, element_widt return output; } + +pub fn listRange(width: utils.IntWidth, low: Opaque, high: Opaque) callconv(.C) RocList { + const allocator = std.heap.c_allocator; + const IntWidth = utils.IntWidth; + + switch (width) { + IntWidth.U8 => { + return helper1(allocator, u8, low, high); + }, + IntWidth.U16 => { + return helper1(allocator, u16, low, high); + }, + IntWidth.U32 => { + return helper1(allocator, u32, low, high); + }, + IntWidth.U64 => { + return helper1(allocator, u64, low, high); + }, + IntWidth.U128 => { + return helper1(allocator, u128, low, high); + }, + IntWidth.I8 => { + return helper1(allocator, i8, low, high); + }, + IntWidth.I16 => { + return helper1(allocator, i16, low, high); + }, + IntWidth.I32 => { + return helper1(allocator, i32, low, high); + }, + IntWidth.I64 => { + return helper1(allocator, i64, low, high); + }, + IntWidth.I128 => { + return helper1(allocator, i128, low, high); + }, + IntWidth.Usize => { + return helper1(allocator, usize, low, high); + }, + } +} + +fn helper1(allocator: *Allocator, comptime T: type, low: Opaque, high: Opaque) RocList { + const ptr1 = @ptrCast(*T, @alignCast(@alignOf(T), low)); + const ptr2 = @ptrCast(*T, @alignCast(@alignOf(T), high)); + + return listRangeHelp(allocator, T, ptr1.*, ptr2.*); +} + +fn listRangeHelp(allocator: *Allocator, comptime T: type, low: T, high: T) RocList { + const Order = std.math.Order; + + switch (std.math.order(low, high)) { + Order.gt => { + return RocList.empty(); + }, + + Order.eq => { + const list = RocList.allocate(allocator, @alignOf(usize), 1, @sizeOf(T)); + const buffer = @ptrCast([*]T, @alignCast(@alignOf(T), list.bytes orelse unreachable)); + + buffer[0] = low; + + return list; + }, + + Order.lt => { + const length: usize = @intCast(usize, high - low); + const list = RocList.allocate(allocator, @alignOf(usize), length, @sizeOf(T)); + + const buffer = @ptrCast([*]T, @alignCast(@alignOf(T), list.bytes orelse unreachable)); + + var i: usize = 0; + var current = low; + + while (i < length) { + buffer[i] = current; + + i += 1; + current += 1; + } + + return list; + }, + } +} diff --git a/compiler/builtins/bitcode/src/main.zig b/compiler/builtins/bitcode/src/main.zig index 4cd57a84a5..45dff14377 100644 --- a/compiler/builtins/bitcode/src/main.zig +++ b/compiler/builtins/bitcode/src/main.zig @@ -18,6 +18,7 @@ comptime { exportListFn(list.listContains, "contains"); exportListFn(list.listRepeat, "repeat"); exportListFn(list.listAppend, "append"); + exportListFn(list.listRange, "range"); } // Dict Module diff --git a/compiler/builtins/bitcode/src/utils.zig b/compiler/builtins/bitcode/src/utils.zig index 76d8908206..6628c46ba6 100644 --- a/compiler/builtins/bitcode/src/utils.zig +++ b/compiler/builtins/bitcode/src/utils.zig @@ -4,6 +4,58 @@ const Allocator = std.mem.Allocator; const REFCOUNT_ONE_ISIZE: comptime isize = std.math.minInt(isize); pub const REFCOUNT_ONE: usize = @bitCast(usize, REFCOUNT_ONE_ISIZE); +pub const IntWidth = enum(u8) { + U8, + U16, + U32, + U64, + U128, + I8, + I16, + I32, + I64, + I128, + Usize, +}; + +pub fn intWidth(width: IntWidth) anytype { + switch (width) { + IntWidth.U8 => { + return u8; + }, + IntWidth.U16 => { + return u16; + }, + IntWidth.U32 => { + return u32; + }, + IntWidth.U64 => { + return u64; + }, + IntWidth.U128 => { + return u128; + }, + IntWidth.I8 => { + return i8; + }, + IntWidth.I16 => { + return i16; + }, + IntWidth.I32 => { + return i32; + }, + IntWidth.I64 => { + return i64; + }, + IntWidth.I128 => { + return i128; + }, + IntWidth.Usize => { + return usize; + }, + } +} + pub fn decref( allocator: *Allocator, alignment: usize, diff --git a/compiler/builtins/src/bitcode.rs b/compiler/builtins/src/bitcode.rs index 0fd6cb1f2f..f60d060151 100644 --- a/compiler/builtins/src/bitcode.rs +++ b/compiler/builtins/src/bitcode.rs @@ -74,3 +74,4 @@ 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"; pub const LIST_APPEND: &str = "roc_builtins.list.append"; +pub const LIST_RANGE: &str = "roc_builtins.list.range"; diff --git a/compiler/builtins/src/std.rs b/compiler/builtins/src/std.rs index 4c4b13d99a..9ed7eae58a 100644 --- a/compiler/builtins/src/std.rs +++ b/compiler/builtins/src/std.rs @@ -799,7 +799,7 @@ pub fn types() -> MutMap { ) }); - // keepOks : List before, (before -> Result * after) -> List after + // keepErrs: List before, (before -> Result * after) -> List after add_type(Symbol::LIST_KEEP_ERRS, { let_tvars! { star, cvar, before, after}; top_level_function( @@ -815,6 +815,14 @@ pub fn types() -> MutMap { ) }); + // range : Int a, Int a -> List (Int a) + add_type(Symbol::LIST_RANGE, { + top_level_function( + vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], + Box::new(list_type(int_type(flex(TVAR1)))), + ) + }); + // map : List before, (before -> after) -> List after add_type( Symbol::LIST_MAP, diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index 334fee9cda..bd1e02ebf6 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -87,6 +87,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option LIST_KEEP_IF => list_keep_if, LIST_KEEP_OKS => list_keep_oks, LIST_KEEP_ERRS=> list_keep_errs, + LIST_RANGE => list_range, LIST_WALK => list_walk, LIST_WALK_BACKWARDS => list_walk_backwards, DICT_TEST_HASH => dict_hash_test_only, @@ -229,6 +230,7 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap { Symbol::LIST_KEEP_IF => list_keep_if, Symbol::LIST_KEEP_OKS => list_keep_oks, Symbol::LIST_KEEP_ERRS=> list_keep_errs, + Symbol::LIST_RANGE => list_range, Symbol::LIST_WALK => list_walk, Symbol::LIST_WALK_BACKWARDS => list_walk_backwards, Symbol::DICT_TEST_HASH => dict_hash_test_only, @@ -2198,6 +2200,11 @@ fn list_keep_errs(symbol: Symbol, var_store: &mut VarStore) -> Def { lowlevel_2(symbol, LowLevel::ListKeepErrs, var_store) } +/// List.keepErrs: List before, (before -> Result * after) -> List after +fn list_range(symbol: Symbol, var_store: &mut VarStore) -> Def { + lowlevel_2(symbol, LowLevel::ListRange, var_store) +} + /// List.map : List before, (before -> after) -> List after fn list_map(symbol: Symbol, var_store: &mut VarStore) -> Def { lowlevel_2(symbol, LowLevel::ListMap, var_store) diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index 4dfda5e9fa..68e336b0d5 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -7,7 +7,7 @@ use crate::llvm::build_hash::generic_hash; use crate::llvm::build_list::{ allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat, list_contains, list_get_unsafe, list_join, list_keep_errs, list_keep_if, list_keep_oks, list_len, list_map, - list_map2, list_map3, list_map_with_index, list_prepend, list_product, list_repeat, + list_map2, list_map3, list_map_with_index, list_prepend, list_product, list_range, list_repeat, list_reverse, list_set, list_single, list_sum, list_walk, list_walk_backwards, }; use crate::llvm::build_str::{ @@ -3879,6 +3879,26 @@ fn run_low_level<'a, 'ctx, 'env>( list_contains(env, layout_ids, elem, elem_layout, list) } + ListRange => { + // List.contains : List elem, elem -> Bool + debug_assert_eq!(args.len(), 2); + + let (low, low_layout) = load_symbol_and_layout(scope, &args[0]); + let high = load_symbol(scope, &args[1]); + + let builtin = match low_layout { + Layout::Builtin(builtin) => builtin, + _ => unreachable!(), + }; + + list_range( + env, + layout_ids, + *builtin, + low.into_int_value(), + high.into_int_value(), + ) + } ListWalk => { debug_assert_eq!(args.len(), 3); diff --git a/compiler/gen/src/llvm/build_list.rs b/compiler/gen/src/llvm/build_list.rs index 40321129db..fc42a5c75c 100644 --- a/compiler/gen/src/llvm/build_list.rs +++ b/compiler/gen/src/llvm/build_list.rs @@ -980,6 +980,79 @@ fn list_walk_generic<'a, 'ctx, 'env>( env.builder.build_load(result_ptr, "load_result") } +#[repr(u8)] +enum IntWidth { + U8, + U16, + U32, + U64, + U128, + I8, + I16, + I32, + I64, + I128, + Usize, +} + +impl From> for IntWidth { + fn from(builtin: Builtin) -> Self { + use IntWidth::*; + + match builtin { + Builtin::Int128 => I128, + Builtin::Int64 => I64, + Builtin::Int32 => I32, + Builtin::Int16 => I16, + Builtin::Int8 => I8, + Builtin::Usize => Usize, + _ => unreachable!(), + } + } +} + +/// List.range : Int a, Int a -> List (Int a) +pub fn list_range<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + layout_ids: &mut LayoutIds<'a>, + builtin: Builtin<'a>, + low: IntValue<'ctx>, + high: IntValue<'ctx>, +) -> BasicValueEnum<'ctx> { + let builder = env.builder; + + let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic); + + let low_ptr = builder.build_alloca(low.get_type(), "low_ptr"); + env.builder.build_store(low_ptr, low); + + let high_ptr = builder.build_alloca(high.get_type(), "high_ptr"); + env.builder.build_store(high_ptr, high); + + let int_width = env + .context + .i8_type() + .const_int(IntWidth::from(builtin) as u64, false) + .into(); + + let output = call_bitcode_fn( + env, + &[ + int_width, + env.builder.build_bitcast(low_ptr, u8_ptr, "to_u8_ptr"), + env.builder.build_bitcast(high_ptr, u8_ptr, "to_u8_ptr"), + ], + &bitcode::LIST_RANGE, + ); + + complex_bitcast( + env.builder, + output, + collection(env.context, env.ptr_bytes).into(), + "from_i128", + ) +} + /// List.contains : List elem, elem -> Bool pub fn list_contains<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, diff --git a/compiler/module/src/low_level.rs b/compiler/module/src/low_level.rs index 3922bb8e6d..3d5644b317 100644 --- a/compiler/module/src/low_level.rs +++ b/compiler/module/src/low_level.rs @@ -26,6 +26,7 @@ pub enum LowLevel { ListAppend, ListPrepend, ListJoin, + ListRange, ListMap, ListMap2, ListMap3, diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index 85806a99c5..d876482091 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -915,6 +915,7 @@ define_builtins! { 24 LIST_MAP2: "map2" 25 LIST_MAP3: "map3" 26 LIST_PRODUCT: "product" + 27 LIST_RANGE: "range" } 5 RESULT: "Result" => { 0 RESULT_RESULT: "Result" imported // the Result.Result type alias diff --git a/compiler/mono/src/borrow.rs b/compiler/mono/src/borrow.rs index f48c787194..739ba984fd 100644 --- a/compiler/mono/src/borrow.rs +++ b/compiler/mono/src/borrow.rs @@ -657,6 +657,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] { ListContains => arena.alloc_slice_copy(&[borrowed, irrelevant]), ListWalk => arena.alloc_slice_copy(&[owned, irrelevant, owned]), ListWalkBackwards => arena.alloc_slice_copy(&[owned, irrelevant, owned]), + ListRange => arena.alloc_slice_copy(&[irrelevant, irrelevant]), ListSum | ListProduct => arena.alloc_slice_copy(&[borrowed]), // TODO when we have lists with capacity (if ever)