add List.clone

This commit is contained in:
Folkert 2024-01-20 20:27:59 +01:00
parent ebfcd71e8d
commit f1ffc36efe
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
14 changed files with 147 additions and 4 deletions

View file

@ -1173,6 +1173,16 @@ fn lowlevel_spec<'a>(
_ => unreachable!(),
}
}
ListClone => {
let list = env.symbols[&arguments[0]];
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
let cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
let _unit = builder.add_update(block, update_mode_var, cell)?;
with_new_heap_cell(builder, block, bag)
}
ListSwap => {
let list = env.symbols[&arguments[0]];

View file

@ -962,6 +962,14 @@ pub fn listIsUnique(
return list.isEmpty() or list.isUnique();
}
pub fn listClone(
list: RocList,
alignment: u32,
element_width: usize,
) callconv(.C) RocList {
return list.makeUnique(alignment, element_width);
}
pub fn listCapacity(
list: RocList,
) callconv(.C) usize {

View file

@ -75,6 +75,7 @@ comptime {
exportListFn(list.listReplaceInPlace, "replace_in_place");
exportListFn(list.listSwap, "swap");
exportListFn(list.listIsUnique, "is_unique");
exportListFn(list.listClone, "clone");
exportListFn(list.listCapacity, "capacity");
exportListFn(list.listAllocationPtr, "allocation_ptr");
exportListFn(list.listReleaseExcessCapacity, "release_excess_capacity");

View file

@ -435,7 +435,8 @@ repeatHelp = \value, count, accum ->
## ```
reverse : List a -> List a
reverse = \list ->
reverseHelp list 0 (Num.subSaturated (List.len list) 1)
end = List.len list |> Num.subSaturated 1
reverseHelp (List.clone list) 0 end
reverseHelp = \list, left, right ->
if left < right then
@ -443,6 +444,9 @@ reverseHelp = \list, left, right ->
else
list
# Ensures that the list in unique (will re-use if already unique)
clone : List a -> List a
## Join the given lists together into one list.
## ```
## expect List.join [[1], [2, 3], [], [4, 5]] == [1, 2, 3, 4, 5]

View file

@ -386,6 +386,7 @@ pub const LIST_CONCAT: &str = "roc_builtins.list.concat";
pub const LIST_REPLACE: &str = "roc_builtins.list.replace";
pub const LIST_REPLACE_IN_PLACE: &str = "roc_builtins.list.replace_in_place";
pub const LIST_IS_UNIQUE: &str = "roc_builtins.list.is_unique";
pub const LIST_CLONE: &str = "roc_builtins.list.clone";
pub const LIST_PREPEND: &str = "roc_builtins.list.prepend";
pub const LIST_APPEND_UNSAFE: &str = "roc_builtins.list.append_unsafe";
pub const LIST_RESERVE: &str = "roc_builtins.list.reserve";

View file

@ -142,6 +142,7 @@ map_symbol_to_lowlevel_and_arity! {
ListWithCapacity; LIST_WITH_CAPACITY; 1,
ListReserve; LIST_RESERVE; 2,
ListIsUnique; LIST_IS_UNIQUE; 1,
ListClone; LIST_CLONE; 1,
ListAppendUnsafe; LIST_APPEND_UNSAFE; 2,
ListPrepend; LIST_PREPEND; 2,
ListGetUnsafe; LIST_GET_UNSAFE; 2,

View file

@ -2809,6 +2809,55 @@ impl<
self.storage_manager.list_len(&mut self.buf, dst, list);
}
fn build_list_clone(
&mut self,
dst: Symbol,
input_list: Symbol,
elem_layout: InLayout<'a>,
ret_layout: InLayout<'a>,
) {
// List alignment argument (u32).
self.load_layout_alignment(ret_layout, Symbol::DEV_TMP);
// Load element_width argument (usize).
self.load_layout_stack_size(elem_layout, Symbol::DEV_TMP2);
// Setup the return location.
let base_offset =
self.storage_manager
.claim_stack_area_layout(self.layout_interner, dst, ret_layout);
let lowlevel_args = [
input_list,
// alignment
Symbol::DEV_TMP,
// element_width
Symbol::DEV_TMP2,
];
let lowlevel_arg_layouts = [ret_layout, Layout::U32, Layout::U64];
self.build_fn_call(
&Symbol::DEV_TMP3,
bitcode::LIST_CLONE.to_string(),
&lowlevel_args,
&lowlevel_arg_layouts,
&ret_layout,
);
self.free_symbol(&Symbol::DEV_TMP);
self.free_symbol(&Symbol::DEV_TMP2);
// Copy from list to the output record.
self.storage_manager.copy_symbol_to_stack_offset(
self.layout_interner,
&mut self.buf,
base_offset,
&Symbol::DEV_TMP3,
&ret_layout,
);
self.free_symbol(&Symbol::DEV_TMP3);
}
fn build_list_with_capacity(
&mut self,
dst: &Symbol,

View file

@ -1518,6 +1518,15 @@ trait Backend<'a> {
let elem_layout = list_element_layout!(self.interner(), *ret_layout);
self.build_list_with_capacity(sym, args[0], arg_layouts[0], elem_layout, ret_layout)
}
LowLevel::ListClone => {
debug_assert_eq!(
1,
args.len(),
"ListClone: expected to have exactly one argument"
);
let elem_layout = list_element_layout!(self.interner(), *ret_layout);
self.build_list_clone(*sym, args[0], elem_layout, *ret_layout)
}
LowLevel::ListReserve => {
debug_assert_eq!(
2,
@ -2416,6 +2425,14 @@ trait Backend<'a> {
fn build_indirect_inc(&mut self, layout: InLayout<'a>) -> Symbol;
fn build_indirect_dec(&mut self, layout: InLayout<'a>) -> Symbol;
fn build_list_clone(
&mut self,
dst: Symbol,
input_list: Symbol,
elem_layout: InLayout<'a>,
ret_layout: InLayout<'a>,
);
/// build_list_with_capacity creates and returns a list with the given capacity.
fn build_list_with_capacity(
&mut self,

View file

@ -34,8 +34,8 @@ use crate::llvm::{
BuilderExt, FuncBorrowSpec, RocReturn,
},
build_list::{
list_append_unsafe, list_concat, list_drop_at, list_get_unsafe, list_len, list_map,
list_map2, list_map3, list_map4, list_prepend, list_release_excess_capacity,
layout_width, list_append_unsafe, list_concat, list_drop_at, list_get_unsafe, list_len,
list_map, list_map2, list_map3, list_map4, list_prepend, list_release_excess_capacity,
list_replace_unsafe, list_reserve, list_sort_with, list_sublist, list_swap,
list_symbol_to_c_abi, list_with_capacity, pass_update_mode,
},
@ -962,6 +962,31 @@ pub(crate) fn run_low_level<'a, 'ctx>(
bitcode::LIST_IS_UNIQUE,
)
}
ListClone => {
// List.clone : List a -> List a
arguments_with_layouts!((list, list_layout));
let element_layout = list_element_layout!(layout_interner, list_layout);
match update_mode {
UpdateMode::Immutable => {
//
call_list_bitcode_fn(
env,
&[list.into_struct_value()],
&[
env.alignment_intvalue(layout_interner, element_layout),
layout_width(env, layout_interner, element_layout),
],
BitcodeReturns::List,
bitcode::LIST_CLONE,
)
}
UpdateMode::InPlace => {
// we statically know the list is unique
list
}
}
}
NumToStr => {
// Num.toStr : Num a -> Str
arguments_with_layouts!((num, num_layout));

View file

@ -299,6 +299,28 @@ impl<'a> LowLevelCall<'a> {
ListIsUnique => self.load_args_and_call_zig(backend, bitcode::LIST_IS_UNIQUE),
ListClone => {
let input_list: Symbol = self.arguments[0];
let elem_layout = unwrap_list_elem_layout(self.ret_layout_raw);
let elem_layout = backend.layout_interner.get_repr(elem_layout);
let (elem_width, elem_align) =
elem_layout.stack_size_and_alignment(backend.layout_interner);
// Zig arguments Wasm types
// (return pointer) i32
// input_list: &RocList i32
// alignment: u32 i32
// element_width: usize i32
backend
.storage
.load_symbols(&mut backend.code_builder, &[self.ret_symbol, input_list]);
backend.code_builder.i32_const(elem_align as i32);
backend.code_builder.i32_const(elem_width as i32);
backend.call_host_fn_after_loading_args(bitcode::LIST_CLONE);
}
ListMap | ListMap2 | ListMap3 | ListMap4 | ListSortWith => {
internal_error!("HigherOrder lowlevels should not be handled here")
}

View file

@ -50,8 +50,9 @@ pub enum LowLevel {
ListSublist,
ListDropAt,
ListSwap,
ListIsUnique,
ListGetCapacity,
ListIsUnique,
ListClone,
NumAdd,
NumAddWrap,
NumAddChecked,
@ -291,6 +292,7 @@ map_symbol_to_lowlevel! {
ListReserve <= LIST_RESERVE;
ListReleaseExcessCapacity <= LIST_RELEASE_EXCESS_CAPACITY;
ListIsUnique <= LIST_IS_UNIQUE;
ListClone <= LIST_CLONE;
ListAppendUnsafe <= LIST_APPEND_UNSAFE;
ListPrepend <= LIST_PREPEND;
ListGetUnsafe <= LIST_GET_UNSAFE, DICT_LIST_GET_UNSAFE;

View file

@ -1440,6 +1440,7 @@ define_builtins! {
84 LIST_APPEND_IF_OK: "appendIfOk"
85 LIST_PREPEND_IF_OK: "prependIfOk"
86 LIST_WALK_WITH_INDEX_UNTIL: "walkWithIndexUntil"
87 LIST_CLONE: "clone"
}
7 RESULT: "Result" => {
0 RESULT_RESULT: "Result" exposed_type=true // the Result.Result type alias

View file

@ -1611,6 +1611,7 @@ fn low_level_no_rc(lowlevel: &LowLevel) -> RC {
Hash => RC::NoRc,
ListIsUnique => RC::Rc,
ListClone => RC::Rc,
BoxExpr | UnboxExpr => {
unreachable!("These lowlevel operations are turned into mono Expr's")

View file

@ -1366,6 +1366,7 @@ fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[Ownership] {
Hash => arena.alloc_slice_copy(&[borrowed, irrelevant]),
ListIsUnique => arena.alloc_slice_copy(&[borrowed]),
ListClone => arena.alloc_slice_copy(&[owned]),
BoxExpr | UnboxExpr => {
unreachable!("These lowlevel operations are turned into mono Expr's")