mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 08:11:12 +00:00
wasm: Extract List.mapN logic into a helper function
This commit is contained in:
parent
a7a84019cd
commit
bdad1a5161
3 changed files with 123 additions and 167 deletions
|
@ -264,5 +264,5 @@ pub const DEBUG_LOG_SETTINGS: WasmDebugLogSettings = WasmDebugLogSettings {
|
||||||
let_stmt_ir: false && cfg!(debug_assertions),
|
let_stmt_ir: false && cfg!(debug_assertions),
|
||||||
instructions: false && cfg!(debug_assertions),
|
instructions: false && cfg!(debug_assertions),
|
||||||
storage_map: false && cfg!(debug_assertions),
|
storage_map: false && cfg!(debug_assertions),
|
||||||
keep_test_binary: true && cfg!(debug_assertions),
|
keep_test_binary: false && cfg!(debug_assertions),
|
||||||
};
|
};
|
||||||
|
|
|
@ -1033,172 +1033,60 @@ pub fn call_higher_order_lowlevel<'a>(
|
||||||
let elem_x_size = elem_x.stack_size(TARGET_INFO);
|
let elem_x_size = elem_x.stack_size(TARGET_INFO);
|
||||||
let (elem_ret_size, elem_ret_align) = elem_ret.stack_size_and_alignment(TARGET_INFO);
|
let (elem_ret_size, elem_ret_align) = elem_ret.stack_size_and_alignment(TARGET_INFO);
|
||||||
|
|
||||||
let cb = &mut backend.code_builder;
|
match op {
|
||||||
|
ListMap { xs } => list_map_n(
|
||||||
// Load return pointer & argument values
|
|
||||||
// Wasm signature: (i32, i64, i64, i32, i32, i32, i32, i32, i32, i32) -> nil
|
|
||||||
backend.storage.load_symbols(cb, &[return_sym]);
|
|
||||||
backend.storage.load_symbol_zig(cb, *xs); // 2 x i64
|
|
||||||
cb.i32_const(wrapper_fn_ptr);
|
|
||||||
if closure_data_exists {
|
|
||||||
backend.storage.load_symbols(cb, &[*captured_environment]);
|
|
||||||
} else {
|
|
||||||
// Normally, a zero-size arg would be eliminated in code gen, but Zig expects one!
|
|
||||||
cb.i32_const(0); // null pointer
|
|
||||||
}
|
|
||||||
cb.i32_const(inc_fn_ptr);
|
|
||||||
cb.i32_const(*owns_captured_environment as i32);
|
|
||||||
cb.i32_const(elem_ret_align as i32); // used for allocating the new list
|
|
||||||
cb.i32_const(elem_x_size as i32);
|
|
||||||
cb.i32_const(elem_ret_size as i32);
|
|
||||||
|
|
||||||
let num_wasm_args = 10; // 1 return pointer + 8 Zig args + list 2nd i64
|
|
||||||
let has_return_val = false;
|
|
||||||
backend.call_zig_builtin_after_loading_args(
|
|
||||||
bitcode::LIST_MAP,
|
bitcode::LIST_MAP,
|
||||||
num_wasm_args,
|
backend,
|
||||||
has_return_val,
|
&[*xs],
|
||||||
);
|
return_sym,
|
||||||
}
|
*return_layout,
|
||||||
|
wrapper_fn_ptr,
|
||||||
|
inc_fn_ptr,
|
||||||
|
closure_data_exists,
|
||||||
|
*captured_environment,
|
||||||
|
*owns_captured_environment,
|
||||||
|
),
|
||||||
|
|
||||||
ListMap2 { xs, ys } => {
|
ListMap2 { xs, ys } => list_map_n(
|
||||||
let list_x = backend.storage.symbol_layouts[xs];
|
|
||||||
let list_y = backend.storage.symbol_layouts[ys];
|
|
||||||
|
|
||||||
let (elem_x, elem_y, elem_ret) = match (list_x, list_y, return_layout) {
|
|
||||||
(
|
|
||||||
Layout::Builtin(Builtin::List(x)),
|
|
||||||
Layout::Builtin(Builtin::List(y)),
|
|
||||||
Layout::Builtin(Builtin::List(ret)),
|
|
||||||
) => (x, y, ret),
|
|
||||||
_ => internal_error!("invalid arguments layout for {:?}", op),
|
|
||||||
};
|
|
||||||
let elem_x_size = elem_x.stack_size(TARGET_INFO);
|
|
||||||
let elem_y_size = elem_y.stack_size(TARGET_INFO);
|
|
||||||
let (elem_ret_size, elem_ret_align) = elem_ret.stack_size_and_alignment(TARGET_INFO);
|
|
||||||
|
|
||||||
let dec_x_fn_ptr = backend.get_refcount_fn_ptr(*elem_x, HelperOp::Dec);
|
|
||||||
let dec_y_fn_ptr = backend.get_refcount_fn_ptr(*elem_y, HelperOp::Dec);
|
|
||||||
|
|
||||||
let cb = &mut backend.code_builder;
|
|
||||||
|
|
||||||
/* Load Wasm arguments
|
|
||||||
return ptr: RocList, // i32
|
|
||||||
list1: RocList, // i64, i64
|
|
||||||
list2: RocList, // i64, i64
|
|
||||||
caller: Caller2, // i32
|
|
||||||
data: Opaque, // i32
|
|
||||||
inc_n_data: IncN, // i32
|
|
||||||
data_is_owned: bool, // i32
|
|
||||||
alignment: u32, // i32
|
|
||||||
a_width: usize, // i32
|
|
||||||
b_width: usize, // i32
|
|
||||||
c_width: usize, // i32
|
|
||||||
dec_a: Dec, // i32
|
|
||||||
dec_b: Dec, // i32
|
|
||||||
*/
|
|
||||||
backend.storage.load_symbols(cb, &[return_sym]);
|
|
||||||
backend.storage.load_symbol_zig(cb, *xs);
|
|
||||||
backend.storage.load_symbol_zig(cb, *ys);
|
|
||||||
cb.i32_const(wrapper_fn_ptr);
|
|
||||||
if closure_data_exists {
|
|
||||||
backend.storage.load_symbols(cb, &[*captured_environment]);
|
|
||||||
} else {
|
|
||||||
cb.i32_const(0); // null pointer
|
|
||||||
}
|
|
||||||
cb.i32_const(inc_fn_ptr);
|
|
||||||
cb.i32_const(*owns_captured_environment as i32);
|
|
||||||
cb.i32_const(elem_ret_align as i32);
|
|
||||||
cb.i32_const(elem_x_size as i32);
|
|
||||||
cb.i32_const(elem_y_size as i32);
|
|
||||||
cb.i32_const(elem_ret_size as i32);
|
|
||||||
cb.i32_const(dec_x_fn_ptr);
|
|
||||||
cb.i32_const(dec_y_fn_ptr);
|
|
||||||
|
|
||||||
let num_wasm_args = 15;
|
|
||||||
let has_return_val = false;
|
|
||||||
backend.call_zig_builtin_after_loading_args(
|
|
||||||
bitcode::LIST_MAP2,
|
bitcode::LIST_MAP2,
|
||||||
num_wasm_args,
|
backend,
|
||||||
has_return_val,
|
&[*xs, *ys],
|
||||||
);
|
return_sym,
|
||||||
}
|
*return_layout,
|
||||||
|
wrapper_fn_ptr,
|
||||||
|
inc_fn_ptr,
|
||||||
|
closure_data_exists,
|
||||||
|
*captured_environment,
|
||||||
|
*owns_captured_environment,
|
||||||
|
),
|
||||||
|
|
||||||
ListMap3 { xs, ys, zs } => {
|
ListMap3 { xs, ys, zs } => list_map_n(
|
||||||
let list_x = backend.storage.symbol_layouts[xs];
|
|
||||||
let list_y = backend.storage.symbol_layouts[ys];
|
|
||||||
let list_z = backend.storage.symbol_layouts[zs];
|
|
||||||
|
|
||||||
let (elem_x, elem_y, elem_z, elem_ret) = match (list_x, list_y, list_z, return_layout) {
|
|
||||||
(
|
|
||||||
Layout::Builtin(Builtin::List(x)),
|
|
||||||
Layout::Builtin(Builtin::List(y)),
|
|
||||||
Layout::Builtin(Builtin::List(z)),
|
|
||||||
Layout::Builtin(Builtin::List(ret)),
|
|
||||||
) => (x, y, z, ret),
|
|
||||||
e => internal_error!("invalid arguments layout for {:?}\n{:?}", op, e),
|
|
||||||
};
|
|
||||||
let elem_x_size = elem_x.stack_size(TARGET_INFO);
|
|
||||||
let elem_y_size = elem_y.stack_size(TARGET_INFO);
|
|
||||||
let elem_z_size = elem_y.stack_size(TARGET_INFO);
|
|
||||||
let (elem_ret_size, elem_ret_align) = elem_ret.stack_size_and_alignment(TARGET_INFO);
|
|
||||||
|
|
||||||
let dec_x_fn_ptr = backend.get_refcount_fn_ptr(*elem_x, HelperOp::Dec);
|
|
||||||
let dec_y_fn_ptr = backend.get_refcount_fn_ptr(*elem_y, HelperOp::Dec);
|
|
||||||
let dec_z_fn_ptr = backend.get_refcount_fn_ptr(*elem_z, HelperOp::Dec);
|
|
||||||
|
|
||||||
let cb = &mut backend.code_builder;
|
|
||||||
|
|
||||||
/* Load Wasm arguments
|
|
||||||
return ptr i32
|
|
||||||
list1: RocList, i64, i64
|
|
||||||
list2: RocList, i64, i64
|
|
||||||
list3: RocList, i64, i64
|
|
||||||
caller: Caller3, i32
|
|
||||||
data: Opaque, i32
|
|
||||||
inc_n_data: IncN, i32
|
|
||||||
data_is_owned: bool, i32
|
|
||||||
alignment: u32, i32
|
|
||||||
a_width: usize, i32
|
|
||||||
b_width: usize, i32
|
|
||||||
c_width: usize, i32
|
|
||||||
d_width: usize, i32
|
|
||||||
dec_a: Dec, i32
|
|
||||||
dec_b: Dec, i32
|
|
||||||
dec_c: Dec, i32
|
|
||||||
*/
|
|
||||||
backend.storage.load_symbols(cb, &[return_sym]);
|
|
||||||
backend.storage.load_symbol_zig(cb, *xs);
|
|
||||||
backend.storage.load_symbol_zig(cb, *ys);
|
|
||||||
backend.storage.load_symbol_zig(cb, *zs);
|
|
||||||
cb.i32_const(wrapper_fn_ptr);
|
|
||||||
if closure_data_exists {
|
|
||||||
backend.storage.load_symbols(cb, &[*captured_environment]);
|
|
||||||
} else {
|
|
||||||
cb.i32_const(0); // null pointer
|
|
||||||
}
|
|
||||||
cb.i32_const(inc_fn_ptr);
|
|
||||||
cb.i32_const(*owns_captured_environment as i32);
|
|
||||||
cb.i32_const(elem_ret_align as i32);
|
|
||||||
cb.i32_const(elem_x_size as i32);
|
|
||||||
cb.i32_const(elem_y_size as i32);
|
|
||||||
cb.i32_const(elem_z_size as i32);
|
|
||||||
cb.i32_const(elem_ret_size as i32);
|
|
||||||
cb.i32_const(dec_x_fn_ptr);
|
|
||||||
cb.i32_const(dec_y_fn_ptr);
|
|
||||||
cb.i32_const(dec_z_fn_ptr);
|
|
||||||
|
|
||||||
let num_wasm_args = 19;
|
|
||||||
let has_return_val = false;
|
|
||||||
backend.call_zig_builtin_after_loading_args(
|
|
||||||
bitcode::LIST_MAP3,
|
bitcode::LIST_MAP3,
|
||||||
num_wasm_args,
|
backend,
|
||||||
has_return_val,
|
&[*xs, *ys, *zs],
|
||||||
);
|
return_sym,
|
||||||
}
|
*return_layout,
|
||||||
|
wrapper_fn_ptr,
|
||||||
|
inc_fn_ptr,
|
||||||
|
closure_data_exists,
|
||||||
|
*captured_environment,
|
||||||
|
*owns_captured_environment,
|
||||||
|
),
|
||||||
|
|
||||||
ListMap4 { .. }
|
ListMap4 { xs, ys, zs, ws } => list_map_n(
|
||||||
| ListMapWithIndex { .. }
|
bitcode::LIST_MAP4,
|
||||||
|
backend,
|
||||||
|
&[*xs, *ys, *zs, *ws],
|
||||||
|
return_sym,
|
||||||
|
*return_layout,
|
||||||
|
wrapper_fn_ptr,
|
||||||
|
inc_fn_ptr,
|
||||||
|
closure_data_exists,
|
||||||
|
*captured_environment,
|
||||||
|
*owns_captured_environment,
|
||||||
|
),
|
||||||
|
|
||||||
|
ListMapWithIndex { .. }
|
||||||
| ListKeepIf { .. }
|
| ListKeepIf { .. }
|
||||||
| ListWalk { .. }
|
| ListWalk { .. }
|
||||||
| ListWalkUntil { .. }
|
| ListWalkUntil { .. }
|
||||||
|
@ -1212,3 +1100,71 @@ pub fn call_higher_order_lowlevel<'a>(
|
||||||
| DictWalk { .. } => todo!("{:?}", op),
|
| DictWalk { .. } => todo!("{:?}", op),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn unwrap_list_elem_layout(list_layout: Layout<'_>) -> &Layout<'_> {
|
||||||
|
match list_layout {
|
||||||
|
Layout::Builtin(Builtin::List(x)) => x,
|
||||||
|
e => internal_error!("expected List layout, got {:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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: Layout<'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.storage.symbol_layouts[sym])),
|
||||||
|
backend.env.arena,
|
||||||
|
);
|
||||||
|
|
||||||
|
let elem_ret = unwrap_list_elem_layout(return_layout);
|
||||||
|
let (elem_ret_size, elem_ret_align) = elem_ret.stack_size_and_alignment(TARGET_INFO);
|
||||||
|
|
||||||
|
let cb = &mut backend.code_builder;
|
||||||
|
|
||||||
|
backend.storage.load_symbols(cb, &[return_sym]);
|
||||||
|
|
||||||
|
for s in arg_symbols {
|
||||||
|
backend.storage.load_symbol_zig(cb, *s);
|
||||||
|
}
|
||||||
|
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(el.stack_size(TARGET_INFO) as i32);
|
||||||
|
}
|
||||||
|
cb.i32_const(elem_ret_size as i32);
|
||||||
|
|
||||||
|
// If we have lists of different lengths, we may need to decrement
|
||||||
|
let num_wasm_args = if arg_elem_layouts.len() > 1 {
|
||||||
|
for el in arg_elem_layouts.iter() {
|
||||||
|
let ptr = backend.get_refcount_fn_ptr(*el, HelperOp::Dec);
|
||||||
|
backend.code_builder.i32_const(ptr);
|
||||||
|
}
|
||||||
|
7 + arg_elem_layouts.len() * 4
|
||||||
|
} else {
|
||||||
|
7 + arg_elem_layouts.len() * 3
|
||||||
|
};
|
||||||
|
|
||||||
|
let has_return_val = false;
|
||||||
|
backend.call_zig_builtin_after_loading_args(zig_fn_name, num_wasm_args, has_return_val);
|
||||||
|
}
|
||||||
|
|
|
@ -1104,7 +1104,7 @@ fn list_map_closure() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
fn list_map4_group() {
|
fn list_map4_group() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -1112,13 +1112,13 @@ fn list_map4_group() {
|
||||||
List.map4 [1,2,3] [3,2,1] [2,1,3] [3,1,2] (\a, b, c, d -> Group a b c d)
|
List.map4 [1,2,3] [3,2,1] [2,1,3] [3,1,2] (\a, b, c, d -> Group a b c d)
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
RocList::from_slice(&[(1, 3, 2, 3), (2, 2, 1, 1), (3, 1, 3, 2)]),
|
RocList::from_slice(&[[1, 3, 2, 3], [2, 2, 1, 1], [3, 1, 3, 2]]),
|
||||||
RocList<(i64, i64, i64, i64)>
|
RocList<[i64; 4]>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
fn list_map4_different_length() {
|
fn list_map4_different_length() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue