move List.map* into roc

This commit is contained in:
Brendan Hansknecht 2024-07-12 15:32:49 -07:00
parent 7d8fbfbe85
commit c734a27b59
No known key found for this signature in database
GPG key ID: 0EA784685083E75B
16 changed files with 60 additions and 1445 deletions

View file

@ -863,7 +863,6 @@ fn call_spec<'a>(
let closure_env = env.symbols[&passed_function.captured_environment]; let closure_env = env.symbols[&passed_function.captured_environment];
let return_layout = &passed_function.return_layout;
let argument_layouts = passed_function.argument_layouts; let argument_layouts = passed_function.argument_layouts;
macro_rules! call_function { macro_rules! call_function {
@ -879,30 +878,6 @@ fn call_spec<'a>(
} }
match op { match op {
ListMap { xs } => {
let list = env.symbols[xs];
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
let input_bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
let element = builder.add_bag_get(block, input_bag)?;
let new_element = call_function!(builder, block, [element]);
list_append(builder, block, update_mode_var, state, new_element)
};
let output_element_type =
layout_spec(env, builder, interner, interner.get_repr(*return_layout))?;
let state_layout = LayoutRepr::Builtin(Builtin::List(*return_layout));
let state_type = layout_spec(env, builder, interner, state_layout)?;
let init_state = new_list(builder, block, output_element_type)?;
add_loop(builder, block, state_type, init_state, loop_body)
}
ListSortWith { xs } => { ListSortWith { xs } => {
let list = env.symbols[xs]; let list = env.symbols[xs];
@ -928,109 +903,6 @@ fn call_spec<'a>(
add_loop(builder, block, state_type, init_state, loop_body) add_loop(builder, block, state_type, init_state, loop_body)
} }
ListMap2 { xs, ys } => {
let list1 = env.symbols[xs];
let list2 = env.symbols[ys];
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
let input_bag_1 =
builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?;
let input_bag_2 =
builder.add_get_tuple_field(block, list2, LIST_BAG_INDEX)?;
let element_1 = builder.add_bag_get(block, input_bag_1)?;
let element_2 = builder.add_bag_get(block, input_bag_2)?;
let new_element = call_function!(builder, block, [element_1, element_2]);
list_append(builder, block, update_mode_var, state, new_element)
};
let output_element_type =
layout_spec(env, builder, interner, interner.get_repr(*return_layout))?;
let state_layout = LayoutRepr::Builtin(Builtin::List(*return_layout));
let state_type = layout_spec(env, builder, interner, state_layout)?;
let init_state = new_list(builder, block, output_element_type)?;
add_loop(builder, block, state_type, init_state, loop_body)
}
ListMap3 { xs, ys, zs } => {
let list1 = env.symbols[xs];
let list2 = env.symbols[ys];
let list3 = env.symbols[zs];
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
let input_bag_1 =
builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?;
let input_bag_2 =
builder.add_get_tuple_field(block, list2, LIST_BAG_INDEX)?;
let input_bag_3 =
builder.add_get_tuple_field(block, list3, LIST_BAG_INDEX)?;
let element_1 = builder.add_bag_get(block, input_bag_1)?;
let element_2 = builder.add_bag_get(block, input_bag_2)?;
let element_3 = builder.add_bag_get(block, input_bag_3)?;
let new_element =
call_function!(builder, block, [element_1, element_2, element_3]);
list_append(builder, block, update_mode_var, state, new_element)
};
let output_element_type =
layout_spec(env, builder, interner, interner.get_repr(*return_layout))?;
let state_layout = LayoutRepr::Builtin(Builtin::List(*return_layout));
let state_type = layout_spec(env, builder, interner, state_layout)?;
let init_state = new_list(builder, block, output_element_type)?;
add_loop(builder, block, state_type, init_state, loop_body)
}
ListMap4 { xs, ys, zs, ws } => {
let list1 = env.symbols[xs];
let list2 = env.symbols[ys];
let list3 = env.symbols[zs];
let list4 = env.symbols[ws];
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
let input_bag_1 =
builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?;
let input_bag_2 =
builder.add_get_tuple_field(block, list2, LIST_BAG_INDEX)?;
let input_bag_3 =
builder.add_get_tuple_field(block, list3, LIST_BAG_INDEX)?;
let input_bag_4 =
builder.add_get_tuple_field(block, list4, LIST_BAG_INDEX)?;
let element_1 = builder.add_bag_get(block, input_bag_1)?;
let element_2 = builder.add_bag_get(block, input_bag_2)?;
let element_3 = builder.add_bag_get(block, input_bag_3)?;
let element_4 = builder.add_bag_get(block, input_bag_4)?;
let new_element = call_function!(
builder,
block,
[element_1, element_2, element_3, element_4]
);
list_append(builder, block, update_mode_var, state, new_element)
};
let output_element_type =
layout_spec(env, builder, interner, interner.get_repr(*return_layout))?;
let state_layout = LayoutRepr::Builtin(Builtin::List(*return_layout));
let state_type = layout_spec(env, builder, interner, state_layout)?;
let init_state = new_list(builder, block, output_element_type)?;
add_loop(builder, block, state_type, init_state, loop_body)
}
} }
} }
} }

View file

@ -347,193 +347,6 @@ pub fn listDecref(list: RocList, alignment: u32, element_width: usize, elements_
list.decref(alignment, element_width, elements_refcounted, dec); list.decref(alignment, element_width, elements_refcounted, dec);
} }
const Caller0 = *const fn (?[*]u8, ?[*]u8) callconv(.C) void;
const Caller1 = *const fn (?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
const Caller2 = *const fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
const Caller3 = *const fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
const Caller4 = *const fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
pub fn listMap(
list: RocList,
caller: Caller1,
data: Opaque,
inc_n_data: IncN,
data_is_owned: bool,
alignment: u32,
old_element_width: usize,
new_element_width: usize,
new_elements_refcount: bool,
) callconv(.C) RocList {
if (list.bytes) |source_ptr| {
const size = list.len();
var i: usize = 0;
const output = RocList.allocate(alignment, size, new_element_width, new_elements_refcount);
const target_ptr = output.bytes orelse unreachable;
if (data_is_owned) {
inc_n_data(data, size);
}
while (i < size) : (i += 1) {
const element = source_ptr + (i * old_element_width);
caller(data, element, target_ptr + (i * new_element_width));
}
return output;
} else {
return RocList.empty();
}
}
pub fn listMap2(
list1: RocList,
list2: RocList,
caller: Caller2,
data: Opaque,
inc_n_data: IncN,
data_is_owned: bool,
alignment: u32,
a_width: usize,
b_width: usize,
c_width: usize,
c_elements_refcounted: bool,
) callconv(.C) RocList {
const output_length = @min(list1.len(), list2.len());
if (data_is_owned) {
inc_n_data(data, output_length);
}
if (list1.bytes) |source_a| {
if (list2.bytes) |source_b| {
const output = RocList.allocate(alignment, output_length, c_width, c_elements_refcounted);
const target_ptr = output.bytes orelse unreachable;
var i: usize = 0;
while (i < output_length) : (i += 1) {
const element_a = source_a + i * a_width;
const element_b = source_b + i * b_width;
const target = target_ptr + i * c_width;
caller(data, element_a, element_b, target);
}
return output;
} else {
return RocList.empty();
}
} else {
return RocList.empty();
}
}
pub fn listMap3(
list1: RocList,
list2: RocList,
list3: RocList,
caller: Caller3,
data: Opaque,
inc_n_data: IncN,
data_is_owned: bool,
alignment: u32,
a_width: usize,
b_width: usize,
c_width: usize,
d_width: usize,
d_elements_refcounted: bool,
) callconv(.C) RocList {
const smaller_length = @min(list1.len(), list2.len());
const output_length = @min(smaller_length, list3.len());
if (data_is_owned) {
inc_n_data(data, output_length);
}
if (list1.bytes) |source_a| {
if (list2.bytes) |source_b| {
if (list3.bytes) |source_c| {
const output = RocList.allocate(alignment, output_length, d_width, d_elements_refcounted);
const target_ptr = output.bytes orelse unreachable;
var i: usize = 0;
while (i < output_length) : (i += 1) {
const element_a = source_a + i * a_width;
const element_b = source_b + i * b_width;
const element_c = source_c + i * c_width;
const target = target_ptr + i * d_width;
caller(data, element_a, element_b, element_c, target);
}
return output;
} else {
return RocList.empty();
}
} else {
return RocList.empty();
}
} else {
return RocList.empty();
}
}
pub fn listMap4(
list1: RocList,
list2: RocList,
list3: RocList,
list4: RocList,
caller: Caller4,
data: Opaque,
inc_n_data: IncN,
data_is_owned: bool,
alignment: u32,
a_width: usize,
b_width: usize,
c_width: usize,
d_width: usize,
e_width: usize,
e_elements_refcounted: bool,
) callconv(.C) RocList {
const output_length = @min(@min(list1.len(), list2.len()), @min(list3.len(), list4.len()));
if (data_is_owned) {
inc_n_data(data, output_length);
}
if (list1.bytes) |source_a| {
if (list2.bytes) |source_b| {
if (list3.bytes) |source_c| {
if (list4.bytes) |source_d| {
const output = RocList.allocate(alignment, output_length, e_width, e_elements_refcounted);
const target_ptr = output.bytes orelse unreachable;
var i: usize = 0;
while (i < output_length) : (i += 1) {
const element_a = source_a + i * a_width;
const element_b = source_b + i * b_width;
const element_c = source_c + i * c_width;
const element_d = source_d + i * d_width;
const target = target_ptr + i * e_width;
caller(data, element_a, element_b, element_c, element_d, target);
}
return output;
} else {
return RocList.empty();
}
} else {
return RocList.empty();
}
} else {
return RocList.empty();
}
} else {
return RocList.empty();
}
}
pub fn listWithCapacity( pub fn listWithCapacity(
capacity: u64, capacity: u64,
alignment: u32, alignment: u32,

View file

@ -65,10 +65,6 @@ comptime {
const list = @import("list.zig"); const list = @import("list.zig");
comptime { comptime {
exportListFn(list.listMap, "map");
exportListFn(list.listMap2, "map2");
exportListFn(list.listMap3, "map3");
exportListFn(list.listMap4, "map4");
exportListFn(list.listAppendUnsafe, "append_unsafe"); exportListFn(list.listAppendUnsafe, "append_unsafe");
exportListFn(list.listReserve, "reserve"); exportListFn(list.listReserve, "reserve");
exportListFn(list.listPrepend, "prepend"); exportListFn(list.listPrepend, "prepend");

View file

@ -746,6 +746,15 @@ keepErrs = \list, toResult ->
## expect List.map ["", "a", "bc"] Str.isEmpty == [Bool.true, Bool.false, Bool.false] ## expect List.map ["", "a", "bc"] Str.isEmpty == [Bool.true, Bool.false, Bool.false]
## ``` ## ```
map : List a, (a -> b) -> List b map : List a, (a -> b) -> List b
map = \list, mapper ->
# TODO: allow checking the refcounting and running the map inplace.
# Perferably allow it even if the types are different (must be same size with padding though).
length = List.len list
List.walk
list
(List.withCapacity length)
\state, elem ->
List.appendUnsafe state (mapper elem)
## Run a transformation function on the first element of each list, ## Run a transformation function on the first element of each list,
## and use that as the first element in the returned list. ## and use that as the first element in the returned list.
@ -757,16 +766,56 @@ map : List a, (a -> b) -> List b
## zipped = List.map2 ["a", "b", "c"] [1, 2, 3] Pair ## zipped = List.map2 ["a", "b", "c"] [1, 2, 3] Pair
## ``` ## ```
map2 : List a, List b, (a, b -> c) -> List c map2 : List a, List b, (a, b -> c) -> List c
map2 = \listA, listB, mapper ->
length = Num.min (List.len listA) (List.len listB)
map2Help listA listB (List.withCapacity length) mapper 0 length
map2Help : List a, List b, List c, (a, b -> c), U64, U64 -> List c
map2Help = \listA, listB, out, mapper, index, length ->
if index < length then
mapped = mapper (List.getUnsafe listA index) (List.getUnsafe listB index)
map2Help listA listB (List.appendUnsafe out mapped) mapper (Num.addWrap index 1) length
else
out
## Run a transformation function on the first element of each list, ## Run a transformation function on the first element of each list,
## and use that as the first element in the returned list. ## and use that as the first element in the returned list.
## Repeat until a list runs out of elements. ## Repeat until a list runs out of elements.
map3 : List a, List b, List c, (a, b, c -> d) -> List d map3 : List a, List b, List c, (a, b, c -> d) -> List d
map3 = \listA, listB, listC, mapper ->
length = Num.min
(Num.min (List.len listA) (List.len listB))
(List.len listC)
map3Help listA listB listC (List.withCapacity length) mapper 0 length
map3Help : List a, List b, List c, List d, (a, b, c -> d), U64, U64 -> List d
map3Help = \listA, listB, listC, out, mapper, index, length ->
if index < length then
mapped = mapper (List.getUnsafe listA index) (List.getUnsafe listB index) (List.getUnsafe listC index)
map3Help listA listB listC (List.appendUnsafe out mapped) mapper (Num.addWrap index 1) length
else
out
## Run a transformation function on the first element of each list, ## Run a transformation function on the first element of each list,
## and use that as the first element in the returned list. ## and use that as the first element in the returned list.
## Repeat until a list runs out of elements. ## Repeat until a list runs out of elements.
map4 : List a, List b, List c, List d, (a, b, c, d -> e) -> List e map4 : List a, List b, List c, List d, (a, b, c, d -> e) -> List e
map4 = \listA, listB, listC, listD, mapper ->
length = Num.min
(Num.min (List.len listA) (List.len listB))
(Num.min (List.len listC) (List.len listD))
map4Help listA listB listC listD (List.withCapacity length) mapper 0 length
map4Help : List a, List b, List c, List d, List e, (a, b, c, d -> e), U64, U64 -> List e
map4Help = \listA, listB, listC, listD, out, mapper, index, length ->
if index < length then
mapped = mapper (List.getUnsafe listA index) (List.getUnsafe listB index) (List.getUnsafe listC index) (List.getUnsafe listD index)
map4Help listA listB listC listD (List.append out mapped) mapper (Num.addWrap index 1) length
else
out
## This works like [List.map], except it also passes the index ## This works like [List.map], except it also passes the index
## of the element to the conversion function. ## of the element to the conversion function.

View file

@ -142,10 +142,6 @@ map_symbol_to_lowlevel_and_arity! {
ListGetUnsafe; LIST_GET_UNSAFE; 2, ListGetUnsafe; LIST_GET_UNSAFE; 2,
ListReplaceUnsafe; LIST_REPLACE_UNSAFE; 3, ListReplaceUnsafe; LIST_REPLACE_UNSAFE; 3,
ListConcat; LIST_CONCAT; 2, ListConcat; LIST_CONCAT; 2,
ListMap; LIST_MAP; 2,
ListMap2; LIST_MAP2; 3,
ListMap3; LIST_MAP3; 4,
ListMap4; LIST_MAP4; 5,
ListSortWith; LIST_SORT_WITH; 2, ListSortWith; LIST_SORT_WITH; 2,
ListSublist; LIST_SUBLIST_LOWLEVEL; 3, ListSublist; LIST_SUBLIST_LOWLEVEL; 3,
ListDropAt; LIST_DROP_AT; 2, ListDropAt; LIST_DROP_AT; 2,

View file

@ -2331,17 +2331,6 @@ impl<
.unwrap(); .unwrap();
let caller_proc = match higher_order.op { let caller_proc = match higher_order.op {
HigherOrder::ListMap { .. }
| HigherOrder::ListMap2 { .. }
| HigherOrder::ListMap3 { .. }
| HigherOrder::ListMap4 { .. } => CallerProc::new_list_map(
self.env.arena,
self.env.module_id,
ident_ids,
self.layout_interner,
&higher_order.passed_function,
higher_order.closure_env_layout,
),
HigherOrder::ListSortWith { .. } => CallerProc::new_compare( HigherOrder::ListSortWith { .. } => CallerProc::new_compare(
self.env.arena, self.env.arena,
self.env.module_id, self.env.module_id,
@ -2395,396 +2384,6 @@ impl<
let usize_ = Layout::U64; let usize_ = Layout::U64;
match higher_order.op { match higher_order.op {
HigherOrder::ListMap { xs } => {
let old_element_layout = argument_layouts[0];
let new_element_layout = higher_order.passed_function.return_layout;
let input_list_layout = LayoutRepr::Builtin(Builtin::List(old_element_layout));
let input_list_in_layout = self
.layout_interner
.insert_direct_no_semantic(input_list_layout);
let alignment = self.debug_symbol("alignment");
let old_element_width = self.debug_symbol("old_element_width");
let new_element_width = self.debug_symbol("new_element_width");
self.load_layout_alignment(new_element_layout, alignment);
self.load_layout_stack_size(old_element_layout, old_element_width);
self.load_layout_stack_size(new_element_layout, new_element_width);
self.build_fn_pointer(&caller, caller_string);
// we pass a null pointer when the data is not owned. the zig code must not call this!
let data_is_owned = higher_order.closure_env_layout.is_some()
&& higher_order.passed_function.owns_captured_environment;
self.load_literal(
&Symbol::DEV_TMP2,
&Layout::BOOL,
&Literal::Bool(data_is_owned),
);
// Load element_refcounted argument (bool).
self.load_layout_refcounted(new_element_layout, Symbol::DEV_TMP3);
// list: RocList,
// caller: Caller1,
// data: Opaque,
// inc_n_data: IncN,
// data_is_owned: bool,
// alignment: u32,
// old_element_width: usize,
// new_element_width: usize,
// new_element_refcounted: bool,
let arguments = [
xs,
caller,
data,
inc_n_data,
Symbol::DEV_TMP2,
alignment,
old_element_width,
new_element_width,
Symbol::DEV_TMP3,
];
let layouts = [
input_list_in_layout,
ptr,
ptr,
ptr,
Layout::BOOL,
Layout::U32,
usize_,
usize_,
Layout::BOOL,
];
self.build_fn_call_stack_return(
bitcode::LIST_MAP.to_string(),
&arguments,
&layouts,
ret_layout,
*dst,
);
self.free_symbol(&Symbol::DEV_TMP);
self.free_symbol(&Symbol::DEV_TMP2);
self.free_symbol(&Symbol::DEV_TMP3);
}
HigherOrder::ListMap2 { xs, ys } => {
let old_element_layout1 = argument_layouts[0];
let old_element_layout2 = argument_layouts[1];
let new_element_layout = higher_order.passed_function.return_layout;
let input_list_layout1 = LayoutRepr::Builtin(Builtin::List(old_element_layout1));
let input_list_in_layout1 = self
.layout_interner
.insert_direct_no_semantic(input_list_layout1);
let input_list_layout2 = LayoutRepr::Builtin(Builtin::List(old_element_layout2));
let input_list_in_layout2 = self
.layout_interner
.insert_direct_no_semantic(input_list_layout2);
let alignment = self.debug_symbol("alignment");
let old_element_width1 = self.debug_symbol("old_element_width1");
let old_element_width2 = self.debug_symbol("old_element_width2");
let new_element_width = self.debug_symbol("new_element_width");
self.load_layout_alignment(new_element_layout, alignment);
self.load_layout_stack_size(old_element_layout1, old_element_width1);
self.load_layout_stack_size(old_element_layout2, old_element_width2);
self.load_layout_stack_size(new_element_layout, new_element_width);
self.build_fn_pointer(&caller, caller_string);
// we pass a null pointer when the data is not owned. the zig code must not call this!
let data_is_owned = higher_order.closure_env_layout.is_some()
&& higher_order.passed_function.owns_captured_environment;
self.load_literal(
&Symbol::DEV_TMP2,
&Layout::BOOL,
&Literal::Bool(data_is_owned),
);
// Load element_refcounted argument (bool).
self.load_layout_refcounted(new_element_layout, Symbol::DEV_TMP3);
// list1: RocList,
// list2: RocList,
// caller: Caller1,
// data: Opaque,
// inc_n_data: IncN,
// data_is_owned: bool,
// alignment: u32,
// old_element_width1: usize,
// old_element_width2: usize,
// new_element_width: usize,
// inc1: Inc
// inc2: Inc
// new_element_refcounted: bool,
let arguments = [
xs,
ys,
caller,
data,
inc_n_data,
Symbol::DEV_TMP2,
alignment,
old_element_width1,
old_element_width2,
new_element_width,
Symbol::DEV_TMP3,
];
let layouts = [
input_list_in_layout1,
input_list_in_layout2,
ptr,
ptr,
ptr,
Layout::BOOL,
Layout::U32,
usize_,
usize_,
usize_,
Layout::BOOL, // new_element_refcounted
];
self.build_fn_call_stack_return(
bitcode::LIST_MAP2.to_string(),
&arguments,
&layouts,
ret_layout,
*dst,
);
self.free_symbol(&Symbol::DEV_TMP);
self.free_symbol(&Symbol::DEV_TMP2);
self.free_symbol(&Symbol::DEV_TMP3);
}
HigherOrder::ListMap3 { xs, ys, zs } => {
let old_element_layout1 = argument_layouts[0];
let old_element_layout2 = argument_layouts[1];
let old_element_layout3 = argument_layouts[2];
let new_element_layout = higher_order.passed_function.return_layout;
let input_list_layout1 = LayoutRepr::Builtin(Builtin::List(old_element_layout1));
let input_list_in_layout1 = self
.layout_interner
.insert_direct_no_semantic(input_list_layout1);
let input_list_layout2 = LayoutRepr::Builtin(Builtin::List(old_element_layout2));
let input_list_in_layout2 = self
.layout_interner
.insert_direct_no_semantic(input_list_layout2);
let input_list_layout3 = LayoutRepr::Builtin(Builtin::List(old_element_layout3));
let input_list_in_layout3 = self
.layout_interner
.insert_direct_no_semantic(input_list_layout3);
let alignment = self.debug_symbol("alignment");
let old_element_width1 = self.debug_symbol("old_element_width1");
let old_element_width2 = self.debug_symbol("old_element_width2");
let old_element_width3 = self.debug_symbol("old_element_width3");
let new_element_width = self.debug_symbol("new_element_width");
self.load_layout_alignment(new_element_layout, alignment);
self.load_layout_stack_size(old_element_layout1, old_element_width1);
self.load_layout_stack_size(old_element_layout2, old_element_width2);
self.load_layout_stack_size(old_element_layout3, old_element_width3);
self.load_layout_stack_size(new_element_layout, new_element_width);
self.build_fn_pointer(&caller, caller_string);
// we pass a null pointer when the data is not owned. the zig code must not call this!
let data_is_owned = higher_order.closure_env_layout.is_some()
&& higher_order.passed_function.owns_captured_environment;
self.load_literal(
&Symbol::DEV_TMP2,
&Layout::BOOL,
&Literal::Bool(data_is_owned),
);
// Load element_refcounted argument (bool).
self.load_layout_refcounted(new_element_layout, Symbol::DEV_TMP3);
// list1: RocList,
// list2: RocList,
// caller: Caller1,
// data: Opaque,
// inc_n_data: IncN,
// data_is_owned: bool,
// alignment: u32,
// old_element_width1: usize,
// old_element_width2: usize,
// new_element_width: usize,
// inc1: Inc
// inc2: Inc
// inc3: Inc
// new_element_refcounted: bool,
let arguments = [
xs,
ys,
zs,
caller,
data,
inc_n_data,
Symbol::DEV_TMP2,
alignment,
old_element_width1,
old_element_width2,
old_element_width3,
new_element_width,
Symbol::DEV_TMP3,
];
let layouts = [
input_list_in_layout1,
input_list_in_layout2,
input_list_in_layout3,
ptr,
ptr,
ptr,
Layout::BOOL,
Layout::U32,
usize_, // old_element_width_1
usize_, // old_element_width_2
usize_, // old_element_width_3
usize_, // new_element_width
Layout::BOOL, // new_element_refcounted
];
self.build_fn_call_stack_return(
bitcode::LIST_MAP3.to_string(),
&arguments,
&layouts,
ret_layout,
*dst,
);
self.free_symbol(&Symbol::DEV_TMP);
self.free_symbol(&Symbol::DEV_TMP2);
self.free_symbol(&Symbol::DEV_TMP3);
}
HigherOrder::ListMap4 { xs, ys, zs, ws } => {
let old_element_layout1 = argument_layouts[0];
let old_element_layout2 = argument_layouts[1];
let old_element_layout3 = argument_layouts[2];
let old_element_layout4 = argument_layouts[3];
let new_element_layout = higher_order.passed_function.return_layout;
let input_list_layout1 = LayoutRepr::Builtin(Builtin::List(old_element_layout1));
let input_list_in_layout1 = self
.layout_interner
.insert_direct_no_semantic(input_list_layout1);
let input_list_layout2 = LayoutRepr::Builtin(Builtin::List(old_element_layout2));
let input_list_in_layout2 = self
.layout_interner
.insert_direct_no_semantic(input_list_layout2);
let input_list_layout3 = LayoutRepr::Builtin(Builtin::List(old_element_layout3));
let input_list_in_layout3 = self
.layout_interner
.insert_direct_no_semantic(input_list_layout3);
let input_list_layout4 = LayoutRepr::Builtin(Builtin::List(old_element_layout4));
let input_list_in_layout4 = self
.layout_interner
.insert_direct_no_semantic(input_list_layout4);
let alignment = self.debug_symbol("alignment");
let old_element_width1 = self.debug_symbol("old_element_width1");
let old_element_width2 = self.debug_symbol("old_element_width2");
let old_element_width3 = self.debug_symbol("old_element_width3");
let old_element_width4 = self.debug_symbol("old_element_width4");
let new_element_width = self.debug_symbol("new_element_width");
self.load_layout_alignment(new_element_layout, alignment);
self.load_layout_stack_size(old_element_layout1, old_element_width1);
self.load_layout_stack_size(old_element_layout2, old_element_width2);
self.load_layout_stack_size(old_element_layout3, old_element_width3);
self.load_layout_stack_size(old_element_layout4, old_element_width4);
self.load_layout_stack_size(new_element_layout, new_element_width);
self.build_fn_pointer(&caller, caller_string);
// we pass a null pointer when the data is not owned. the zig code must not call this!
let data_is_owned = higher_order.closure_env_layout.is_some()
&& higher_order.passed_function.owns_captured_environment;
self.load_literal(
&Symbol::DEV_TMP2,
&Layout::BOOL,
&Literal::Bool(data_is_owned),
);
// Load element_refcounted argument (bool).
self.load_layout_refcounted(new_element_layout, Symbol::DEV_TMP3);
let arguments = [
xs,
ys,
zs,
ws,
caller,
data,
inc_n_data,
Symbol::DEV_TMP2,
alignment,
old_element_width1,
old_element_width2,
old_element_width3,
old_element_width4,
new_element_width,
Symbol::DEV_TMP3,
];
let layouts = [
input_list_in_layout1,
input_list_in_layout2,
input_list_in_layout3,
input_list_in_layout4,
ptr,
ptr,
ptr,
Layout::BOOL,
Layout::U32,
usize_, // old_element_width_1
usize_, // old_element_width_2
usize_, // old_element_width_3
usize_, // old_element_width_4
usize_, // new_element_width
Layout::BOOL, // new_element_refcounted
];
self.build_fn_call_stack_return(
bitcode::LIST_MAP4.to_string(),
&arguments,
&layouts,
ret_layout,
*dst,
);
self.free_symbol(&Symbol::DEV_TMP);
self.free_symbol(&Symbol::DEV_TMP2);
self.free_symbol(&Symbol::DEV_TMP3);
}
HigherOrder::ListSortWith { xs } => { HigherOrder::ListSortWith { xs } => {
let element_layout = argument_layouts[0]; let element_layout = argument_layouts[0];

View file

@ -564,137 +564,6 @@ pub(crate) fn list_sort_with<'a, 'ctx>(
) )
} }
/// List.map : List before, (before -> after) -> List after
pub(crate) fn list_map<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>,
layout_interner: &STLayoutInterner<'a>,
roc_function_call: RocFunctionCall<'ctx>,
list: BasicValueEnum<'ctx>,
element_layout: InLayout<'a>,
return_layout: InLayout<'a>,
) -> BasicValueEnum<'ctx> {
call_list_bitcode_fn_1(
env,
list.into_struct_value(),
&[
roc_function_call.caller.into(),
pass_as_opaque(env, roc_function_call.data),
roc_function_call.inc_n_data.into(),
roc_function_call.data_is_owned.into(),
env.alignment_intvalue(layout_interner, return_layout),
layout_width(env, layout_interner, element_layout),
layout_width(env, layout_interner, return_layout),
layout_refcounted(env, layout_interner, return_layout),
],
bitcode::LIST_MAP,
)
}
pub(crate) fn list_map2<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>,
layout_interner: &STLayoutInterner<'a>,
roc_function_call: RocFunctionCall<'ctx>,
list1: BasicValueEnum<'ctx>,
list2: BasicValueEnum<'ctx>,
element1_layout: InLayout<'a>,
element2_layout: InLayout<'a>,
return_layout: InLayout<'a>,
) -> BasicValueEnum<'ctx> {
call_list_bitcode_fn(
env,
&[list1.into_struct_value(), list2.into_struct_value()],
&[
roc_function_call.caller.into(),
pass_as_opaque(env, roc_function_call.data),
roc_function_call.inc_n_data.into(),
roc_function_call.data_is_owned.into(),
env.alignment_intvalue(layout_interner, return_layout),
layout_width(env, layout_interner, element1_layout),
layout_width(env, layout_interner, element2_layout),
layout_width(env, layout_interner, return_layout),
layout_refcounted(env, layout_interner, return_layout),
],
BitcodeReturns::List,
bitcode::LIST_MAP2,
)
}
pub(crate) fn list_map3<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>,
layout_interner: &STLayoutInterner<'a>,
roc_function_call: RocFunctionCall<'ctx>,
list1: BasicValueEnum<'ctx>,
list2: BasicValueEnum<'ctx>,
list3: BasicValueEnum<'ctx>,
element1_layout: InLayout<'a>,
element2_layout: InLayout<'a>,
element3_layout: InLayout<'a>,
result_layout: InLayout<'a>,
) -> BasicValueEnum<'ctx> {
call_list_bitcode_fn(
env,
&[
list1.into_struct_value(),
list2.into_struct_value(),
list3.into_struct_value(),
],
&[
roc_function_call.caller.into(),
pass_as_opaque(env, roc_function_call.data),
roc_function_call.inc_n_data.into(),
roc_function_call.data_is_owned.into(),
env.alignment_intvalue(layout_interner, result_layout),
layout_width(env, layout_interner, element1_layout),
layout_width(env, layout_interner, element2_layout),
layout_width(env, layout_interner, element3_layout),
layout_width(env, layout_interner, result_layout),
layout_refcounted(env, layout_interner, result_layout),
],
BitcodeReturns::List,
bitcode::LIST_MAP3,
)
}
pub(crate) fn list_map4<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>,
layout_interner: &STLayoutInterner<'a>,
roc_function_call: RocFunctionCall<'ctx>,
list1: BasicValueEnum<'ctx>,
list2: BasicValueEnum<'ctx>,
list3: BasicValueEnum<'ctx>,
list4: BasicValueEnum<'ctx>,
element1_layout: InLayout<'a>,
element2_layout: InLayout<'a>,
element3_layout: InLayout<'a>,
element4_layout: InLayout<'a>,
result_layout: InLayout<'a>,
) -> BasicValueEnum<'ctx> {
call_list_bitcode_fn(
env,
&[
list1.into_struct_value(),
list2.into_struct_value(),
list3.into_struct_value(),
list4.into_struct_value(),
],
&[
roc_function_call.caller.into(),
pass_as_opaque(env, roc_function_call.data),
roc_function_call.inc_n_data.into(),
roc_function_call.data_is_owned.into(),
env.alignment_intvalue(layout_interner, result_layout),
layout_width(env, layout_interner, element1_layout),
layout_width(env, layout_interner, element2_layout),
layout_width(env, layout_interner, element3_layout),
layout_width(env, layout_interner, element4_layout),
layout_width(env, layout_interner, result_layout),
layout_refcounted(env, layout_interner, result_layout),
],
BitcodeReturns::List,
bitcode::LIST_MAP4,
)
}
/// List.concat : List elem, List elem -> List elem /// List.concat : List elem, List elem -> List elem
pub(crate) fn list_concat<'a, 'ctx>( pub(crate) fn list_concat<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>, env: &Env<'a, 'ctx, '_>,

View file

@ -36,9 +36,9 @@ use crate::llvm::{
}, },
build_list::{ build_list::{
list_append_unsafe, list_clone, list_concat, list_drop_at, list_get_unsafe, list_len_usize, list_append_unsafe, list_clone, list_concat, list_drop_at, list_get_unsafe, list_len_usize,
list_map, list_map2, list_map3, list_map4, list_prepend, list_release_excess_capacity, list_prepend, list_release_excess_capacity, list_replace_unsafe, list_reserve,
list_replace_unsafe, list_reserve, list_sort_with, list_sublist, list_swap, list_sort_with, list_sublist, list_swap, list_symbol_to_c_abi, list_with_capacity,
list_symbol_to_c_abi, list_with_capacity, pass_update_mode, pass_update_mode,
}, },
compare::{generic_eq, generic_neq}, compare::{generic_eq, generic_neq},
convert::{ convert::{
@ -1306,7 +1306,7 @@ pub(crate) fn run_low_level<'a, 'ctx>(
unimplemented!() unimplemented!()
} }
ListMap | ListMap2 | ListMap3 | ListMap4 | ListSortWith => { ListSortWith => {
unreachable!("these are higher order, and are handled elsewhere") unreachable!("these are higher order, and are handled elsewhere")
} }
@ -2772,7 +2772,7 @@ pub(crate) fn run_higher_order_low_level<'a, 'ctx>(
layout_interner: &STLayoutInterner<'a>, layout_interner: &STLayoutInterner<'a>,
layout_ids: &mut LayoutIds<'a>, layout_ids: &mut LayoutIds<'a>,
scope: &Scope<'a, 'ctx>, scope: &Scope<'a, 'ctx>,
return_layout: InLayout<'a>, _return_layout: InLayout<'a>,
func_spec: FuncSpec, func_spec: FuncSpec,
higher_order: &HigherOrderLowLevel<'a>, higher_order: &HigherOrderLowLevel<'a>,
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
@ -2811,198 +2811,6 @@ pub(crate) fn run_higher_order_low_level<'a, 'ctx>(
} }
match op { match op {
ListMap { xs } => {
// List.map : List before, (before -> after) -> List after
let (list, list_layout) = scope.load_symbol_and_layout(xs);
let (function, closure, closure_layout) = function_details!();
match (
layout_interner.get_repr(list_layout),
layout_interner.get_repr(return_layout),
) {
(
LayoutRepr::Builtin(Builtin::List(element_layout)),
LayoutRepr::Builtin(Builtin::List(result_layout)),
) => {
let argument_layouts = &[element_layout];
let roc_function_call = roc_function_call(
env,
layout_interner,
layout_ids,
function,
closure,
closure_layout,
function_owns_closure_data,
argument_layouts,
result_layout,
);
list_map(
env,
layout_interner,
roc_function_call,
list,
element_layout,
result_layout,
)
}
_ => unreachable!("invalid list layout"),
}
}
ListMap2 { xs, ys } => {
let (list1, list1_layout) = scope.load_symbol_and_layout(xs);
let (list2, list2_layout) = scope.load_symbol_and_layout(ys);
let (function, closure, closure_layout) = function_details!();
match (
layout_interner.get_repr(list1_layout),
layout_interner.get_repr(list2_layout),
layout_interner.get_repr(return_layout),
) {
(
LayoutRepr::Builtin(Builtin::List(element1_layout)),
LayoutRepr::Builtin(Builtin::List(element2_layout)),
LayoutRepr::Builtin(Builtin::List(result_layout)),
) => {
let argument_layouts = &[element1_layout, element2_layout];
let roc_function_call = roc_function_call(
env,
layout_interner,
layout_ids,
function,
closure,
closure_layout,
function_owns_closure_data,
argument_layouts,
result_layout,
);
list_map2(
env,
layout_interner,
roc_function_call,
list1,
list2,
element1_layout,
element2_layout,
result_layout,
)
}
_ => unreachable!("invalid list layout"),
}
}
ListMap3 { xs, ys, zs } => {
let (list1, list1_layout) = scope.load_symbol_and_layout(xs);
let (list2, list2_layout) = scope.load_symbol_and_layout(ys);
let (list3, list3_layout) = scope.load_symbol_and_layout(zs);
let (function, closure, closure_layout) = function_details!();
match (
layout_interner.get_repr(list1_layout),
layout_interner.get_repr(list2_layout),
layout_interner.get_repr(list3_layout),
layout_interner.get_repr(return_layout),
) {
(
LayoutRepr::Builtin(Builtin::List(element1_layout)),
LayoutRepr::Builtin(Builtin::List(element2_layout)),
LayoutRepr::Builtin(Builtin::List(element3_layout)),
LayoutRepr::Builtin(Builtin::List(result_layout)),
) => {
let argument_layouts = &[element1_layout, element2_layout, element3_layout];
let roc_function_call = roc_function_call(
env,
layout_interner,
layout_ids,
function,
closure,
closure_layout,
function_owns_closure_data,
argument_layouts,
result_layout,
);
list_map3(
env,
layout_interner,
roc_function_call,
list1,
list2,
list3,
element1_layout,
element2_layout,
element3_layout,
result_layout,
)
}
_ => unreachable!("invalid list layout"),
}
}
ListMap4 { xs, ys, zs, ws } => {
let (list1, list1_layout) = scope.load_symbol_and_layout(xs);
let (list2, list2_layout) = scope.load_symbol_and_layout(ys);
let (list3, list3_layout) = scope.load_symbol_and_layout(zs);
let (list4, list4_layout) = scope.load_symbol_and_layout(ws);
let (function, closure, closure_layout) = function_details!();
match (
layout_interner.get_repr(list1_layout),
layout_interner.get_repr(list2_layout),
layout_interner.get_repr(list3_layout),
layout_interner.get_repr(list4_layout),
layout_interner.get_repr(return_layout),
) {
(
LayoutRepr::Builtin(Builtin::List(element1_layout)),
LayoutRepr::Builtin(Builtin::List(element2_layout)),
LayoutRepr::Builtin(Builtin::List(element3_layout)),
LayoutRepr::Builtin(Builtin::List(element4_layout)),
LayoutRepr::Builtin(Builtin::List(result_layout)),
) => {
let argument_layouts = &[
element1_layout,
element2_layout,
element3_layout,
element4_layout,
];
let roc_function_call = roc_function_call(
env,
layout_interner,
layout_ids,
function,
closure,
closure_layout,
function_owns_closure_data,
argument_layouts,
result_layout,
);
list_map4(
env,
layout_interner,
roc_function_call,
list1,
list2,
list3,
list4,
element1_layout,
element2_layout,
element3_layout,
element4_layout,
result_layout,
)
}
_ => unreachable!("invalid list layout"),
}
}
ListSortWith { xs } => { ListSortWith { xs } => {
// List.sortWith : List a, (a, a -> Ordering) -> List a // List.sortWith : List a, (a, a -> Ordering) -> List a
let (list, list_layout) = scope.load_symbol_and_layout(xs); let (list, list_layout) = scope.load_symbol_and_layout(xs);

View file

@ -39,7 +39,6 @@ pub enum ProcSource {
Roc, Roc,
Helper, Helper,
/// Wrapper function for higher-order calls from Zig to Roc /// Wrapper function for higher-order calls from Zig to Roc
HigherOrderMapper(usize),
HigherOrderCompare(usize), HigherOrderCompare(usize),
} }
@ -491,126 +490,6 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
self.module.names.append_function(wasm_fn_index, name); self.module.names.append_function(wasm_fn_index, name);
} }
/// Build a wrapper around a Roc procedure so that it can be called from Zig builtins List.map*
///
/// The generic Zig code passes *pointers* to all of the argument values (e.g. on the heap in a List).
/// Numbers up to 64 bits are passed by value, so we need to load them from the provided pointer.
/// Everything else is passed by reference, so we can just pass the pointer through.
///
/// NOTE: If the builtins expected the return pointer first and closure data last, we could eliminate the wrapper
/// when all args are pass-by-reference and non-zero size. But currently we need it to swap those around.
pub fn build_higher_order_mapper(
&mut self,
wrapper_lookup_idx: usize,
inner_lookup_idx: usize,
) {
use Align::*;
use ValueType::*;
let ProcLookupData {
name: wrapper_name,
layout: wrapper_proc_layout,
..
} = self.proc_lookup[wrapper_lookup_idx];
let wrapper_arg_layouts = wrapper_proc_layout.arguments;
// Our convention is that the last arg of the wrapper is the heap return pointer
let heap_return_ptr_id = LocalId(wrapper_arg_layouts.len() as u32 - 1);
let inner_ret_layout = match wrapper_arg_layouts
.last()
.map(|l| self.layout_interner.get_repr(*l))
{
Some(LayoutRepr::Ptr(inner)) => WasmLayout::new(self.layout_interner, inner),
x => internal_error!("Higher-order wrapper: invalid return layout {:?}", x),
};
let ret_type_and_size = match inner_ret_layout.return_method() {
ReturnMethod::NoReturnValue => None,
ReturnMethod::Primitive(ty, size) => {
// If the inner function returns a primitive, load the address to store it at
// After the call, it will be under the call result in the value stack
self.code_builder.get_local(heap_return_ptr_id);
Some((ty, size))
}
ReturnMethod::WriteToPointerArg => {
// If the inner function writes to a return pointer, load its address
self.code_builder.get_local(heap_return_ptr_id);
None
}
};
// Load all the arguments for the inner function
for (i, wrapper_arg) in wrapper_arg_layouts.iter().enumerate() {
let is_closure_data = i == 0; // Skip closure data (first for wrapper, last for inner). We'll handle it below.
let is_return_pointer = i == wrapper_arg_layouts.len() - 1; // Skip return pointer (may not be an arg for inner. And if it is, swaps from end to start)
if is_closure_data || is_return_pointer {
continue;
}
let inner_layout = match self.layout_interner.get_repr(*wrapper_arg) {
LayoutRepr::Ptr(inner) => inner,
x => internal_error!("Expected a Ptr layout, got {:?}", x),
};
if self.layout_interner.stack_size(inner_layout) == 0 {
continue;
}
// Load the argument pointer. If it's a primitive value, dereference it too.
self.code_builder.get_local(LocalId(i as u32));
self.dereference_boxed_value(inner_layout);
}
// If the inner function has closure data, it's the last arg of the inner fn
let closure_data_layout = wrapper_arg_layouts[0];
if self.layout_interner.stack_size(closure_data_layout) > 0 {
// The closure data exists, and will have been passed in to the wrapper as a
// one-element struct.
let inner_closure_data_layout = match self.layout_interner.get_repr(closure_data_layout)
{
LayoutRepr::Struct([inner]) => inner,
other => internal_error!(
"Expected a boxed layout for wrapped closure data, got {:?}",
other
),
};
self.code_builder.get_local(LocalId(0));
// Since the closure data is wrapped in a one-element struct, we've been passed in the
// pointer to that struct in the stack memory. To get the closure data we just need to
// dereference the pointer.
self.dereference_boxed_value(*inner_closure_data_layout);
}
// Call the wrapped inner function
let inner_wasm_fn_index = self.fn_index_offset + inner_lookup_idx as u32;
self.code_builder.call(inner_wasm_fn_index);
// If the inner function returns a primitive, store it to the address we loaded at the very beginning
if let Some((ty, size)) = ret_type_and_size {
match (ty, size) {
(I64, 8) => self.code_builder.i64_store(Bytes8, 0),
(I32, 4) => self.code_builder.i32_store(Bytes4, 0),
(I32, 2) => self.code_builder.i32_store16(Bytes2, 0),
(I32, 1) => self.code_builder.i32_store8(Bytes1, 0),
(F32, 4) => self.code_builder.f32_store(Bytes4, 0),
(F64, 8) => self.code_builder.f64_store(Bytes8, 0),
_ => {
internal_error!("Cannot store {:?} with alignment of {:?}", ty, size);
}
}
}
// Write empty function header (local variables array with zero length)
self.code_builder.build_fn_header_and_footer(&[], 0, None);
self.module.add_function_signature(Signature {
param_types: bumpalo::vec![in self.env.arena; I32; wrapper_arg_layouts.len()],
ret_type: None,
});
self.append_proc_debug_name(wrapper_name);
self.reset();
}
/// Build a wrapper around a Roc comparison proc so that it can be called from higher-order Zig builtins. /// Build a wrapper around a Roc comparison proc so that it can be called from higher-order Zig builtins.
/// Comparison procedure signature is: closure_data, a, b -> Order (u8) /// Comparison procedure signature is: closure_data, a, b -> Order (u8)
/// ///

View file

@ -178,7 +178,6 @@ pub fn build_app_module<'a, 'r>(
match source { match source {
Roc => { /* already generated */ } Roc => { /* already generated */ }
Helper => backend.build_proc(helper_iter.next().unwrap()), Helper => backend.build_proc(helper_iter.next().unwrap()),
HigherOrderMapper(inner_idx) => backend.build_higher_order_mapper(idx, *inner_idx),
HigherOrderCompare(inner_idx) => backend.build_higher_order_compare(idx, *inner_idx), HigherOrderCompare(inner_idx) => backend.build_higher_order_compare(idx, *inner_idx),
} }
} }

View file

@ -377,7 +377,7 @@ impl<'a> LowLevelCall<'a> {
backend.call_host_fn_after_loading_args(bitcode::LIST_DECREF); backend.call_host_fn_after_loading_args(bitcode::LIST_DECREF);
} }
ListMap | ListMap2 | ListMap3 | ListMap4 | ListSortWith => { ListSortWith => {
internal_error!("HigherOrder lowlevels should not be handled here") internal_error!("HigherOrder lowlevels should not be handled here")
} }
@ -2614,7 +2614,7 @@ fn num_is_finite(backend: &mut WasmBackend<'_, '_>, argument: Symbol) {
pub fn call_higher_order_lowlevel<'a>( pub fn call_higher_order_lowlevel<'a>(
backend: &mut WasmBackend<'a, '_>, backend: &mut WasmBackend<'a, '_>,
return_sym: Symbol, return_sym: Symbol,
return_layout: &InLayout<'a>, _return_layout: &InLayout<'a>,
higher_order: &'a HigherOrderLowLevel<'a>, higher_order: &'a HigherOrderLowLevel<'a>,
) { ) {
use HigherOrder::*; use HigherOrder::*;
@ -2726,9 +2726,6 @@ pub fn call_higher_order_lowlevel<'a>(
.unwrap(); .unwrap();
match op { match op {
ListSortWith { .. } => ProcSource::HigherOrderCompare(passed_proc_index), ListSortWith { .. } => ProcSource::HigherOrderCompare(passed_proc_index),
ListMap { .. } | ListMap2 { .. } | ListMap3 { .. } | ListMap4 { .. } => {
ProcSource::HigherOrderMapper(passed_proc_index)
}
} }
}; };
let wrapper_sym = backend.create_symbol(&format!("#wrap#{fn_name:?}")); let wrapper_sym = backend.create_symbol(&format!("#wrap#{fn_name:?}"));
@ -2753,19 +2750,6 @@ pub fn call_higher_order_lowlevel<'a>(
wrapper_arg_layouts.extend(boxed_closure_arg_layouts); wrapper_arg_layouts.extend(boxed_closure_arg_layouts);
match helper_proc_source { match helper_proc_source {
ProcSource::HigherOrderMapper(_) => {
// Our convention for mappers is that they write to the heap via the last argument
wrapper_arg_layouts.push(
backend
.layout_interner
.insert_direct_no_semantic(LayoutRepr::Ptr(*result_layout)),
);
ProcLayout {
arguments: wrapper_arg_layouts.into_bump_slice(),
result: Layout::UNIT,
niche: fn_name.niche(),
}
}
ProcSource::HigherOrderCompare(_) => ProcLayout { ProcSource::HigherOrderCompare(_) => ProcLayout {
arguments: wrapper_arg_layouts.into_bump_slice(), arguments: wrapper_arg_layouts.into_bump_slice(),
result: *result_layout, result: *result_layout,
@ -2792,58 +2776,6 @@ pub fn call_higher_order_lowlevel<'a>(
}; };
match op { match op {
ListMap { xs } => list_map_n(
bitcode::LIST_MAP,
backend,
&[*xs],
return_sym,
*return_layout,
wrapper_fn_ptr,
inc_n_fn_ptr,
closure_data_exists,
wrapped_captured_environment,
*owns_captured_environment,
),
ListMap2 { xs, ys } => list_map_n(
bitcode::LIST_MAP2,
backend,
&[*xs, *ys],
return_sym,
*return_layout,
wrapper_fn_ptr,
inc_n_fn_ptr,
closure_data_exists,
wrapped_captured_environment,
*owns_captured_environment,
),
ListMap3 { xs, ys, zs } => list_map_n(
bitcode::LIST_MAP3,
backend,
&[*xs, *ys, *zs],
return_sym,
*return_layout,
wrapper_fn_ptr,
inc_n_fn_ptr,
closure_data_exists,
wrapped_captured_environment,
*owns_captured_environment,
),
ListMap4 { xs, ys, zs, ws } => list_map_n(
bitcode::LIST_MAP4,
backend,
&[*xs, *ys, *zs, *ws],
return_sym,
*return_layout,
wrapper_fn_ptr,
inc_n_fn_ptr,
closure_data_exists,
wrapped_captured_environment,
*owns_captured_environment,
),
ListSortWith { xs } => { ListSortWith { xs } => {
let elem_in_layout = unwrap_list_elem_layout( let elem_in_layout = unwrap_list_elem_layout(
backend backend
@ -2905,63 +2837,6 @@ fn unwrap_list_elem_layout(list_layout: LayoutRepr) -> InLayout {
} }
} }
#[allow(clippy::too_many_arguments)]
fn list_map_n<'a>(
zig_fn_name: &'static str,
backend: &mut WasmBackend<'a, '_>,
arg_symbols: &[Symbol],
return_sym: Symbol,
return_layout: InLayout<'a>,
wrapper_fn_ptr: i32,
inc_fn_ptr: i32,
closure_data_exists: bool,
captured_environment: Symbol,
owns_captured_environment: bool,
) {
let arg_elem_layouts = Vec::from_iter_in(
arg_symbols.iter().map(|sym| {
unwrap_list_elem_layout(
backend
.layout_interner
.get_repr(backend.storage.symbol_layouts[sym]),
)
}),
backend.env.arena,
);
let elem_in_ret = unwrap_list_elem_layout(backend.layout_interner.get_repr(return_layout));
let elem_ret = backend.layout_interner.get_repr(elem_in_ret);
let (elem_ret_size, elem_ret_align) =
elem_ret.stack_size_and_alignment(backend.layout_interner);
let elem_ret_refcounted = backend.layout_interner.contains_refcounted(elem_in_ret);
let cb = &mut backend.code_builder;
let mut args_vec = Vec::with_capacity_in(arg_symbols.len() + 1, backend.env.arena);
args_vec.push(return_sym);
args_vec.extend_from_slice(arg_symbols);
backend.storage.load_symbols(cb, &args_vec);
cb.i32_const(wrapper_fn_ptr);
if closure_data_exists {
backend.storage.load_symbols(cb, &[captured_environment]);
} else {
// load_symbols assumes that a zero-size arg should be eliminated in code gen,
// but that's a specialization that our Zig code doesn't have! Pass a null pointer.
cb.i32_const(0);
}
cb.i32_const(inc_fn_ptr);
cb.i32_const(owns_captured_environment as i32);
cb.i32_const(elem_ret_align as i32);
for el in arg_elem_layouts.iter() {
cb.i32_const(backend.layout_interner.stack_size(*el) as i32);
}
cb.i32_const(elem_ret_size as i32);
backend.code_builder.i32_const(elem_ret_refcounted as i32);
backend.call_host_fn_after_loading_args(zig_fn_name);
}
fn ensure_symbol_is_in_memory<'a>( fn ensure_symbol_is_in_memory<'a>(
backend: &mut WasmBackend<'a, '_>, backend: &mut WasmBackend<'a, '_>,
symbol: Symbol, symbol: Symbol,

View file

@ -36,10 +36,6 @@ pub enum LowLevel {
ListReplaceUnsafe, ListReplaceUnsafe,
ListConcat, ListConcat,
ListPrepend, ListPrepend,
ListMap,
ListMap2,
ListMap3,
ListMap4,
ListSortWith, ListSortWith,
ListSublist, ListSublist,
ListDropAt, ListDropAt,
@ -137,7 +133,7 @@ pub enum LowLevel {
macro_rules! higher_order { macro_rules! higher_order {
() => { () => {
ListMap | ListMap2 | ListMap3 | ListMap4 | ListSortWith ListSortWith
}; };
} }
@ -154,10 +150,6 @@ impl LowLevel {
use LowLevel::*; use LowLevel::*;
match self { match self {
ListMap => 1,
ListMap2 => 2,
ListMap3 => 3,
ListMap4 => 4,
ListSortWith => 1, ListSortWith => 1,
_ => unreachable!(), _ => unreachable!(),
} }
@ -213,10 +205,6 @@ macro_rules! map_symbol_to_lowlevel {
// these are higher-order lowlevels. these need the surrounding // these are higher-order lowlevels. these need the surrounding
// function to provide enough type information for code generation // function to provide enough type information for code generation
LowLevel::ListMap => unreachable!(),
LowLevel::ListMap2 => unreachable!(),
LowLevel::ListMap3 => unreachable!(),
LowLevel::ListMap4 => unreachable!(),
LowLevel::ListSortWith => unreachable!(), LowLevel::ListSortWith => unreachable!(),
// (un)boxing is handled in a custom way // (un)boxing is handled in a custom way

View file

@ -1549,7 +1549,7 @@ fn low_level_no_rc(lowlevel: &LowLevel) -> RC {
StrToNum => RC::NoRc, StrToNum => RC::NoRc,
ListPrepend => RC::Rc, ListPrepend => RC::Rc,
StrJoinWith => RC::NoRc, StrJoinWith => RC::NoRc,
ListMap | ListMap2 | ListMap3 | ListMap4 | ListSortWith => RC::Rc, ListSortWith => RC::Rc,
ListAppendUnsafe ListAppendUnsafe
| ListReserve | ListReserve

View file

@ -1103,72 +1103,7 @@ fn insert_refcount_operations_binding<'a>(
// This should always be true, not sure where this could be set to false. // This should always be true, not sure where this could be set to false.
debug_assert!(passed_function.owns_captured_environment); debug_assert!(passed_function.owns_captured_environment);
// define macro that inserts a decref statement for a symbol amount of symbols
macro_rules! decref_lists {
($stmt:expr, $symbol:expr) => {
arena.alloc(Stmt::Refcounting(ModifyRc::DecRef($symbol), $stmt))
};
($stmt:expr, $symbol:expr, $($symbols:expr),+) => {{
decref_lists!(decref_lists!($stmt, $symbol), $($symbols),+)
}};
}
match operator { match operator {
HigherOrder::ListMap { xs } => {
if let [_xs_symbol, _function_symbol, closure_symbol] = &arguments {
let new_stmt = dec_borrowed!([*closure_symbol], stmt);
let new_stmt = decref_lists!(new_stmt, *xs);
let new_let = new_let!(new_stmt);
inc_owned!([*xs].into_iter(), new_let)
} else {
panic!("ListMap should have 3 arguments");
}
}
HigherOrder::ListMap2 { xs, ys } => {
if let [_xs_symbol, _ys_symbol, _function_symbol, closure_symbol] =
&arguments
{
let new_stmt = dec_borrowed!([*closure_symbol], stmt);
let new_stmt = decref_lists!(new_stmt, *xs, *ys);
let new_let = new_let!(new_stmt);
inc_owned!([*xs, *ys].into_iter(), new_let)
} else {
panic!("ListMap2 should have 4 arguments");
}
}
HigherOrder::ListMap3 { xs, ys, zs } => {
if let [_xs_symbol, _ys_symbol, _zs_symbol, _function_symbol, closure_symbol] =
&arguments
{
let new_stmt = dec_borrowed!([*closure_symbol], stmt);
let new_stmt = decref_lists!(new_stmt, *xs, *ys, *zs);
let new_let = new_let!(new_stmt);
inc_owned!([*xs, *ys, *zs].into_iter(), new_let)
} else {
panic!("ListMap3 should have 5 arguments");
}
}
HigherOrder::ListMap4 { xs, ys, zs, ws } => {
if let [_xs_symbol, _ys_symbol, _zs_symbol, _ws_symbol, _function_symbol, closure_symbol] =
&arguments
{
let new_stmt = dec_borrowed!([*closure_symbol], stmt);
let new_stmt = decref_lists!(new_stmt, *xs, *ys, *zs, *ws);
let new_let = new_let!(new_stmt);
inc_owned!([*xs, *ys, *zs, *ws].into_iter(), new_let)
} else {
panic!("ListMap4 should have 6 arguments");
}
}
HigherOrder::ListSortWith { xs } => { HigherOrder::ListSortWith { xs } => {
// TODO if non-unique, elements have been consumed, must still consume the list itself // TODO if non-unique, elements have been consumed, must still consume the list itself
if let [_xs_symbol, _function_symbol, closure_symbol] = &arguments { if let [_xs_symbol, _function_symbol, closure_symbol] = &arguments {
@ -1343,10 +1278,6 @@ pub(crate) fn lowlevel_borrow_signature(op: LowLevel) -> &'static [Ownership] {
StrToNum => &[BORROWED], StrToNum => &[BORROWED],
ListPrepend => &[OWNED, OWNED], ListPrepend => &[OWNED, OWNED],
StrJoinWith => &[BORROWED, BORROWED], StrJoinWith => &[BORROWED, BORROWED],
ListMap => &[OWNED, FUNCTION, CLOSURE_DATA],
ListMap2 => &[OWNED, OWNED, FUNCTION, CLOSURE_DATA],
ListMap3 => &[OWNED, OWNED, OWNED, FUNCTION, CLOSURE_DATA],
ListMap4 => &[OWNED, OWNED, OWNED, OWNED, FUNCTION, CLOSURE_DATA],
ListSortWith => &[OWNED, FUNCTION, CLOSURE_DATA], ListSortWith => &[OWNED, FUNCTION, CLOSURE_DATA],
ListAppendUnsafe => &[OWNED, OWNED], ListAppendUnsafe => &[OWNED, OWNED],
ListReserve => &[OWNED, IRRELEVANT], ListReserve => &[OWNED, IRRELEVANT],

View file

@ -5812,43 +5812,11 @@ pub fn with_hole<'a>(
use LowLevel::*; use LowLevel::*;
match op { match op {
ListMap => {
debug_assert_eq!(arg_symbols.len(), 2);
let xs = arg_symbols[0];
match_on_closure_argument!(ListMap, [xs])
}
ListSortWith => { ListSortWith => {
debug_assert_eq!(arg_symbols.len(), 2); debug_assert_eq!(arg_symbols.len(), 2);
let xs = arg_symbols[0]; let xs = arg_symbols[0];
match_on_closure_argument!(ListSortWith, [xs]) match_on_closure_argument!(ListSortWith, [xs])
} }
ListMap2 => {
debug_assert_eq!(arg_symbols.len(), 3);
let xs = arg_symbols[0];
let ys = arg_symbols[1];
match_on_closure_argument!(ListMap2, [xs, ys])
}
ListMap3 => {
debug_assert_eq!(arg_symbols.len(), 4);
let xs = arg_symbols[0];
let ys = arg_symbols[1];
let zs = arg_symbols[2];
match_on_closure_argument!(ListMap3, [xs, ys, zs])
}
ListMap4 => {
debug_assert_eq!(arg_symbols.len(), 5);
let xs = arg_symbols[0];
let ys = arg_symbols[1];
let zs = arg_symbols[2];
let ws = arg_symbols[3];
match_on_closure_argument!(ListMap4, [xs, ys, zs, ws])
}
BoxExpr => { BoxExpr => {
debug_assert_eq!(arg_symbols.len(), 1); debug_assert_eq!(arg_symbols.len(), 1);
let x = arg_symbols[0]; let x = arg_symbols[0];

View file

@ -2,36 +2,12 @@ use roc_module::symbol::Symbol;
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum HigherOrder { pub enum HigherOrder {
ListMap { ListSortWith { xs: Symbol },
xs: Symbol,
},
ListMap2 {
xs: Symbol,
ys: Symbol,
},
ListMap3 {
xs: Symbol,
ys: Symbol,
zs: Symbol,
},
ListMap4 {
xs: Symbol,
ys: Symbol,
zs: Symbol,
ws: Symbol,
},
ListSortWith {
xs: Symbol,
},
} }
impl HigherOrder { impl HigherOrder {
pub fn function_arity(&self) -> usize { pub fn function_arity(&self) -> usize {
match self { match self {
HigherOrder::ListMap { .. } => 1,
HigherOrder::ListMap2 { .. } => 2,
HigherOrder::ListMap3 { .. } => 3,
HigherOrder::ListMap4 { .. } => 4,
HigherOrder::ListSortWith { .. } => 2, HigherOrder::ListSortWith { .. } => 2,
} }
} }
@ -42,10 +18,7 @@ impl HigherOrder {
use HigherOrder::*; use HigherOrder::*;
match self { match self {
ListMap { .. } | ListSortWith { .. } => 2, ListSortWith { .. } => 2,
ListMap2 { .. } => 3,
ListMap3 { .. } => 4,
ListMap4 { .. } => 5,
} }
} }