mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +00:00
move List.map* into roc
This commit is contained in:
parent
7d8fbfbe85
commit
c734a27b59
16 changed files with 60 additions and 1445 deletions
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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];
|
||||||
|
|
||||||
|
|
|
@ -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, '_>,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
///
|
///
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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],
|
||||||
|
|
|
@ -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];
|
||||||
|
|
|
@ -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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue