mirror of
https://github.com/roc-lang/roc.git
synced 2025-11-25 21:37:48 +00:00
add List.releaseExcessCapacity builtin
This commit is contained in:
parent
40b50b0091
commit
1319ba4844
13 changed files with 177 additions and 3 deletions
|
|
@ -1157,6 +1157,11 @@ fn lowlevel_spec<'a>(
|
||||||
|
|
||||||
list_clone(builder, block, update_mode_var, list)
|
list_clone(builder, block, update_mode_var, list)
|
||||||
}
|
}
|
||||||
|
ListReleaseExcessCapacity => {
|
||||||
|
let list = env.symbols[&arguments[0]];
|
||||||
|
|
||||||
|
list_clone(builder, block, update_mode_var, list)
|
||||||
|
}
|
||||||
ListAppendUnsafe => {
|
ListAppendUnsafe => {
|
||||||
let list = env.symbols[&arguments[0]];
|
let list = env.symbols[&arguments[0]];
|
||||||
let to_insert = env.symbols[&arguments[1]];
|
let to_insert = env.symbols[&arguments[1]];
|
||||||
|
|
|
||||||
|
|
@ -188,6 +188,23 @@ pub const RocList = extern struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn allocateExact(
|
||||||
|
alignment: u32,
|
||||||
|
length: usize,
|
||||||
|
element_width: usize,
|
||||||
|
) RocList {
|
||||||
|
if (length == 0) {
|
||||||
|
return empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
const data_bytes = length * element_width;
|
||||||
|
return RocList{
|
||||||
|
.bytes = utils.allocateWithRefcount(data_bytes, alignment),
|
||||||
|
.length = length,
|
||||||
|
.capacity_or_ref_ptr = length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn reallocate(
|
pub fn reallocate(
|
||||||
self: RocList,
|
self: RocList,
|
||||||
alignment: u32,
|
alignment: u32,
|
||||||
|
|
@ -474,6 +491,30 @@ pub fn listReserve(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn listReleaseExcessCapacity(
|
||||||
|
list: RocList,
|
||||||
|
alignment: u32,
|
||||||
|
element_width: usize,
|
||||||
|
update_mode: UpdateMode,
|
||||||
|
) callconv(.C) RocList {
|
||||||
|
const old_length = list.len();
|
||||||
|
// We use the direct list.capacity_or_ref_ptr to make sure both that there is no extra capacity and that it isn't a seamless slice.
|
||||||
|
if ((update_mode == .InPlace or list.isUnique()) and list.capacity_or_ref_ptr == old_length) {
|
||||||
|
return list;
|
||||||
|
} else if (old_length == 0) {
|
||||||
|
list.decref(alignment);
|
||||||
|
return RocList.empty();
|
||||||
|
} else {
|
||||||
|
var output = RocList.allocateExact(alignment, old_length, element_width);
|
||||||
|
if (list.bytes) |source_ptr| {
|
||||||
|
const dest_ptr = output.bytes orelse unreachable;
|
||||||
|
|
||||||
|
@memcpy(dest_ptr, source_ptr, old_length * element_width);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn listAppendUnsafe(
|
pub fn listAppendUnsafe(
|
||||||
list: RocList,
|
list: RocList,
|
||||||
element: Opaque,
|
element: Opaque,
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ comptime {
|
||||||
exportListFn(list.listIsUnique, "is_unique");
|
exportListFn(list.listIsUnique, "is_unique");
|
||||||
exportListFn(list.listCapacity, "capacity");
|
exportListFn(list.listCapacity, "capacity");
|
||||||
exportListFn(list.listRefcountPtr, "refcount_ptr");
|
exportListFn(list.listRefcountPtr, "refcount_ptr");
|
||||||
|
exportListFn(list.listReleaseExcessCapacity, "release_excess_capacity");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Num Module
|
// Num Module
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@ interface List
|
||||||
sortAsc,
|
sortAsc,
|
||||||
sortDesc,
|
sortDesc,
|
||||||
reserve,
|
reserve,
|
||||||
|
releaseExcessCapacity,
|
||||||
walkBackwardsUntil,
|
walkBackwardsUntil,
|
||||||
countIf,
|
countIf,
|
||||||
]
|
]
|
||||||
|
|
@ -291,6 +292,10 @@ withCapacity : Nat -> List a
|
||||||
## Enlarge the list for at least capacity additional elements
|
## Enlarge the list for at least capacity additional elements
|
||||||
reserve : List a, Nat -> List a
|
reserve : List a, Nat -> List a
|
||||||
|
|
||||||
|
## Shrink the memory footprint of a list such that it's capacity and length are equal.
|
||||||
|
## Note: This will also convert seamless slices to regular lists.
|
||||||
|
releaseExcessCapacity : List a -> List a
|
||||||
|
|
||||||
## Put two lists together.
|
## Put two lists together.
|
||||||
## ```
|
## ```
|
||||||
## List.concat [1, 2, 3] [4, 5]
|
## List.concat [1, 2, 3] [4, 5]
|
||||||
|
|
|
||||||
|
|
@ -352,6 +352,7 @@ pub const LIST_APPEND_UNSAFE: &str = "roc_builtins.list.append_unsafe";
|
||||||
pub const LIST_RESERVE: &str = "roc_builtins.list.reserve";
|
pub const LIST_RESERVE: &str = "roc_builtins.list.reserve";
|
||||||
pub const LIST_CAPACITY: &str = "roc_builtins.list.capacity";
|
pub const LIST_CAPACITY: &str = "roc_builtins.list.capacity";
|
||||||
pub const LIST_REFCOUNT_PTR: &str = "roc_builtins.list.refcount_ptr";
|
pub const LIST_REFCOUNT_PTR: &str = "roc_builtins.list.refcount_ptr";
|
||||||
|
pub const LIST_RELEASE_EXCESS_CAPACITY: &str = "roc_builtins.list.release_excess_capacity";
|
||||||
|
|
||||||
pub const DEC_FROM_STR: &str = "roc_builtins.dec.from_str";
|
pub const DEC_FROM_STR: &str = "roc_builtins.dec.from_str";
|
||||||
pub const DEC_TO_STR: &str = "roc_builtins.dec.to_str";
|
pub const DEC_TO_STR: &str = "roc_builtins.dec.to_str";
|
||||||
|
|
|
||||||
|
|
@ -145,6 +145,7 @@ map_symbol_to_lowlevel_and_arity! {
|
||||||
ListDropAt; LIST_DROP_AT; 2,
|
ListDropAt; LIST_DROP_AT; 2,
|
||||||
ListSwap; LIST_SWAP; 3,
|
ListSwap; LIST_SWAP; 3,
|
||||||
ListGetCapacity; LIST_CAPACITY; 1,
|
ListGetCapacity; LIST_CAPACITY; 1,
|
||||||
|
ListReleaseExcessCapacity; LIST_RELEASE_EXCESS_CAPACITY; 1,
|
||||||
|
|
||||||
ListGetUnsafe; DICT_LIST_GET_UNSAFE; 2,
|
ListGetUnsafe; DICT_LIST_GET_UNSAFE; 2,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -183,6 +183,26 @@ pub(crate) fn list_reserve<'a, 'ctx, 'env>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// List.releaseExcessCapacity : List elem -> List elem
|
||||||
|
pub(crate) fn list_release_excess_capacity<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
layout_interner: &mut STLayoutInterner<'a>,
|
||||||
|
list: BasicValueEnum<'ctx>,
|
||||||
|
element_layout: InLayout<'a>,
|
||||||
|
update_mode: UpdateMode,
|
||||||
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
call_list_bitcode_fn_1(
|
||||||
|
env,
|
||||||
|
list.into_struct_value(),
|
||||||
|
&[
|
||||||
|
env.alignment_intvalue(layout_interner, element_layout),
|
||||||
|
layout_width(env, layout_interner, element_layout),
|
||||||
|
pass_update_mode(env, update_mode),
|
||||||
|
],
|
||||||
|
bitcode::LIST_RELEASE_EXCESS_CAPACITY,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// List.appendUnsafe : List elem, elem -> List elem
|
/// List.appendUnsafe : List elem, elem -> List elem
|
||||||
pub(crate) fn list_append_unsafe<'a, 'ctx, 'env>(
|
pub(crate) fn list_append_unsafe<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,9 @@ use crate::llvm::{
|
||||||
},
|
},
|
||||||
build_list::{
|
build_list::{
|
||||||
list_append_unsafe, list_concat, list_drop_at, list_get_unsafe, list_len, list_map,
|
list_append_unsafe, list_concat, list_drop_at, list_get_unsafe, list_len, list_map,
|
||||||
list_map2, list_map3, list_map4, list_prepend, list_replace_unsafe, list_reserve,
|
list_map2, list_map3, list_map4, list_prepend, list_release_excess_capacity,
|
||||||
list_sort_with, list_sublist, list_swap, list_symbol_to_c_abi, list_with_capacity,
|
list_replace_unsafe, list_reserve, list_sort_with, list_sublist, list_swap,
|
||||||
pass_update_mode,
|
list_symbol_to_c_abi, list_with_capacity, pass_update_mode,
|
||||||
},
|
},
|
||||||
compare::{generic_eq, generic_neq},
|
compare::{generic_eq, generic_neq},
|
||||||
convert::{
|
convert::{
|
||||||
|
|
@ -707,6 +707,15 @@ pub(crate) fn run_low_level<'a, 'ctx, 'env>(
|
||||||
update_mode,
|
update_mode,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
ListReleaseExcessCapacity => {
|
||||||
|
// List.releaseExcessCapacity: List elem -> List elem
|
||||||
|
debug_assert_eq!(args.len(), 1);
|
||||||
|
|
||||||
|
let (list, list_layout) = load_symbol_and_layout(scope, &args[0]);
|
||||||
|
let element_layout = list_element_layout!(layout_interner, list_layout);
|
||||||
|
|
||||||
|
list_release_excess_capacity(env, layout_interner, list, element_layout, update_mode)
|
||||||
|
}
|
||||||
ListSwap => {
|
ListSwap => {
|
||||||
// List.swap : List elem, Nat, Nat -> List elem
|
// List.swap : List elem, Nat, Nat -> List elem
|
||||||
debug_assert_eq!(args.len(), 3);
|
debug_assert_eq!(args.len(), 3);
|
||||||
|
|
|
||||||
|
|
@ -555,6 +555,46 @@ impl<'a> LowLevelCall<'a> {
|
||||||
backend.call_host_fn_after_loading_args(bitcode::LIST_RESERVE, 7, false);
|
backend.call_host_fn_after_loading_args(bitcode::LIST_RESERVE, 7, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ListReleaseExcessCapacity => {
|
||||||
|
// List.releaseExcessCapacity : List elem -> List elem
|
||||||
|
|
||||||
|
let list: Symbol = self.arguments[0];
|
||||||
|
|
||||||
|
let elem_layout = unwrap_list_elem_layout(self.ret_layout_raw);
|
||||||
|
let elem_layout = backend.layout_interner.get(elem_layout);
|
||||||
|
let (elem_width, elem_align) =
|
||||||
|
elem_layout.stack_size_and_alignment(backend.layout_interner, TARGET_INFO);
|
||||||
|
|
||||||
|
// Zig arguments Wasm types
|
||||||
|
// (return pointer) i32
|
||||||
|
// list: RocList i64, i32
|
||||||
|
// alignment: u32 i32
|
||||||
|
// element_width: usize i32
|
||||||
|
// update_mode: UpdateMode i32
|
||||||
|
|
||||||
|
// return pointer and list
|
||||||
|
backend.storage.load_symbols_for_call(
|
||||||
|
backend.env.arena,
|
||||||
|
&mut backend.code_builder,
|
||||||
|
&[list],
|
||||||
|
self.ret_symbol,
|
||||||
|
&WasmLayout::new(backend.layout_interner, self.ret_layout),
|
||||||
|
CallConv::Zig,
|
||||||
|
);
|
||||||
|
|
||||||
|
backend.code_builder.i32_const(elem_align as i32);
|
||||||
|
|
||||||
|
backend.code_builder.i32_const(elem_width as i32);
|
||||||
|
|
||||||
|
backend.code_builder.i32_const(UPDATE_MODE_IMMUTABLE);
|
||||||
|
|
||||||
|
backend.call_host_fn_after_loading_args(
|
||||||
|
bitcode::LIST_RELEASE_EXCESS_CAPACITY,
|
||||||
|
6,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
ListAppendUnsafe => {
|
ListAppendUnsafe => {
|
||||||
// List.append : List elem, elem -> List elem
|
// List.append : List elem, elem -> List elem
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ pub enum LowLevel {
|
||||||
ListLen,
|
ListLen,
|
||||||
ListWithCapacity,
|
ListWithCapacity,
|
||||||
ListReserve,
|
ListReserve,
|
||||||
|
ListReleaseExcessCapacity,
|
||||||
ListAppendUnsafe,
|
ListAppendUnsafe,
|
||||||
ListGetUnsafe,
|
ListGetUnsafe,
|
||||||
ListReplaceUnsafe,
|
ListReplaceUnsafe,
|
||||||
|
|
@ -262,6 +263,7 @@ map_symbol_to_lowlevel! {
|
||||||
ListGetCapacity <= LIST_CAPACITY,
|
ListGetCapacity <= LIST_CAPACITY,
|
||||||
ListWithCapacity <= LIST_WITH_CAPACITY,
|
ListWithCapacity <= LIST_WITH_CAPACITY,
|
||||||
ListReserve <= LIST_RESERVE,
|
ListReserve <= LIST_RESERVE,
|
||||||
|
ListReleaseExcessCapacity <= LIST_RELEASE_EXCESS_CAPACITY,
|
||||||
ListIsUnique <= LIST_IS_UNIQUE,
|
ListIsUnique <= LIST_IS_UNIQUE,
|
||||||
ListAppendUnsafe <= LIST_APPEND_UNSAFE,
|
ListAppendUnsafe <= LIST_APPEND_UNSAFE,
|
||||||
ListPrepend <= LIST_PREPEND,
|
ListPrepend <= LIST_PREPEND,
|
||||||
|
|
|
||||||
|
|
@ -1410,6 +1410,7 @@ define_builtins! {
|
||||||
78 LIST_WALK_FROM: "walkFrom"
|
78 LIST_WALK_FROM: "walkFrom"
|
||||||
79 LIST_WALK_FROM_UNTIL: "walkFromUntil"
|
79 LIST_WALK_FROM_UNTIL: "walkFromUntil"
|
||||||
80 LIST_ITER_HELP: "iterHelp"
|
80 LIST_ITER_HELP: "iterHelp"
|
||||||
|
81 LIST_RELEASE_EXCESS_CAPACITY: "releaseExcessCapacity"
|
||||||
}
|
}
|
||||||
7 RESULT: "Result" => {
|
7 RESULT: "Result" => {
|
||||||
0 RESULT_RESULT: "Result" exposed_type=true // the Result.Result type alias
|
0 RESULT_RESULT: "Result" exposed_type=true // the Result.Result type alias
|
||||||
|
|
|
||||||
|
|
@ -1016,6 +1016,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||||
ListSublist => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
|
ListSublist => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
|
||||||
ListDropAt => arena.alloc_slice_copy(&[owned, irrelevant]),
|
ListDropAt => arena.alloc_slice_copy(&[owned, irrelevant]),
|
||||||
ListSwap => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
|
ListSwap => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
|
||||||
|
ListReleaseExcessCapacity => arena.alloc_slice_copy(&[owned]),
|
||||||
|
|
||||||
Eq | NotEq => arena.alloc_slice_copy(&[borrowed, borrowed]),
|
Eq | NotEq => arena.alloc_slice_copy(&[borrowed, borrowed]),
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3493,6 +3493,53 @@ fn reserve_unchanged() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
fn release_excess_capacity() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
List.reserve [] 15
|
||||||
|
|> List.releaseExcessCapacity
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
(0, RocList::empty()),
|
||||||
|
RocList<u64>,
|
||||||
|
|value: RocList<u64>| (value.capacity(), value)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
fn release_excess_capacity_with_len() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
List.reserve [1] 50
|
||||||
|
|> List.releaseExcessCapacity
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
(1, RocList::from_slice(&[1])),
|
||||||
|
RocList<u64>,
|
||||||
|
|value: RocList<u64>| (value.capacity(), value)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
fn release_excess_capacity_empty() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
List.releaseExcessCapacity []
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
(0, RocList::empty()),
|
||||||
|
RocList<u64>,
|
||||||
|
|value: RocList<u64>| (value.capacity(), value)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
fn call_function_in_empty_list() {
|
fn call_function_in_empty_list() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue