wasm: Extract List.mapN logic into a helper function

This commit is contained in:
Brian Carroll 2022-04-08 14:45:47 +01:00
parent a7a84019cd
commit bdad1a5161
3 changed files with 123 additions and 167 deletions

View file

@ -264,5 +264,5 @@ pub const DEBUG_LOG_SETTINGS: WasmDebugLogSettings = WasmDebugLogSettings {
let_stmt_ir: false && cfg!(debug_assertions),
instructions: false && cfg!(debug_assertions),
storage_map: false && cfg!(debug_assertions),
keep_test_binary: true && cfg!(debug_assertions),
keep_test_binary: false && cfg!(debug_assertions),
};

View file

@ -1033,172 +1033,60 @@ pub fn call_higher_order_lowlevel<'a>(
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 cb = &mut backend.code_builder;
// 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(
match op {
ListMap { xs } => list_map_n(
bitcode::LIST_MAP,
num_wasm_args,
has_return_val,
);
}
backend,
&[*xs],
return_sym,
*return_layout,
wrapper_fn_ptr,
inc_fn_ptr,
closure_data_exists,
*captured_environment,
*owns_captured_environment,
),
ListMap2 { xs, ys } => {
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(
ListMap2 { xs, ys } => list_map_n(
bitcode::LIST_MAP2,
num_wasm_args,
has_return_val,
);
}
backend,
&[*xs, *ys],
return_sym,
*return_layout,
wrapper_fn_ptr,
inc_fn_ptr,
closure_data_exists,
*captured_environment,
*owns_captured_environment,
),
ListMap3 { xs, ys, zs } => {
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(
ListMap3 { xs, ys, zs } => list_map_n(
bitcode::LIST_MAP3,
num_wasm_args,
has_return_val,
);
}
backend,
&[*xs, *ys, *zs],
return_sym,
*return_layout,
wrapper_fn_ptr,
inc_fn_ptr,
closure_data_exists,
*captured_environment,
*owns_captured_environment,
),
ListMap4 { .. }
| ListMapWithIndex { .. }
ListMap4 { xs, ys, zs, ws } => list_map_n(
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 { .. }
| ListWalk { .. }
| ListWalkUntil { .. }
@ -1212,3 +1100,71 @@ pub fn call_higher_order_lowlevel<'a>(
| 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);
}

View file

@ -1104,7 +1104,7 @@ fn list_map_closure() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn list_map4_group() {
assert_evals_to!(
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)
"#
),
RocList::from_slice(&[(1, 3, 2, 3), (2, 2, 1, 1), (3, 1, 3, 2)]),
RocList<(i64, i64, i64, i64)>
RocList::from_slice(&[[1, 3, 2, 3], [2, 2, 1, 1], [3, 1, 3, 2]]),
RocList<[i64; 4]>
);
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn list_map4_different_length() {
assert_evals_to!(
indoc!(