roc/compiler/gen_wasm/tests/helpers/wasm32_test_result.rs
2021-09-11 16:55:20 +01:00

138 lines
4.6 KiB
Rust

use parity_wasm::elements::{Instruction, Instruction::*};
use roc_gen_wasm::from_wasm32_memory::FromWasm32Memory;
use roc_gen_wasm::*;
use roc_std::{RocDec, RocList, RocOrder, RocStr};
pub trait Wasm32TestResult {
fn gen_wrapper(main_function_index: u32) -> Vec<Instruction>;
}
fn build_wrapper_body_prelude(stack_memory_size: usize) -> Vec<Instruction> {
vec![
GetGlobal(STACK_POINTER_GLOBAL_ID),
I32Const(stack_memory_size as i32),
I32Sub,
SetGlobal(STACK_POINTER_GLOBAL_ID),
]
}
macro_rules! build_wrapper_body_primitive {
($store_instruction: expr, $align: expr) => {
fn build_wrapper_body(main_function_index: u32) -> Vec<Instruction> {
const MAX_ALIGNED_SIZE: usize = 16;
let mut instructions = build_wrapper_body_prelude(MAX_ALIGNED_SIZE);
instructions.extend([
//
// Call the main function with no arguments. Get primitive back.
Call(main_function_index),
//
// Store the primitive at the allocated address
GetGlobal(STACK_POINTER_GLOBAL_ID),
$store_instruction($align, 0),
//
// Return the result pointer
GetGlobal(STACK_POINTER_GLOBAL_ID),
End,
]);
instructions
}
};
}
macro_rules! wasm_test_result_primitive {
($type_name: ident, $store_instruction: expr, $align: expr) => {
impl Wasm32TestResult for $type_name {
build_wrapper_body_primitive!($store_instruction, $align);
}
};
}
fn build_wrapper_body_stack_memory(main_function_index: u32, size: usize) -> Vec<Instruction> {
let mut instructions = build_wrapper_body_prelude(size);
instructions.extend([
//
// Call the main function with the allocated address to write the result.
// No value is returned to the VM stack. This is the same as in compiled C.
GetGlobal(STACK_POINTER_GLOBAL_ID),
Call(main_function_index),
//
// Return the result address
GetGlobal(STACK_POINTER_GLOBAL_ID),
End,
]);
instructions
}
macro_rules! wasm_test_result_stack_memory {
($type_name: ident) => {
impl Wasm32TestResult for $type_name {
fn build_wrapper_body(main_function_index: u32) -> Vec<Instruction> {
build_wrapper_body_stack_memory(main_function_index, $type_name::ACTUAL_WIDTH)
}
}
};
}
wasm_test_result_primitive!(bool, I32Store8, ALIGN_1);
wasm_test_result_primitive!(RocOrder, I32Store8, ALIGN_1);
wasm_test_result_primitive!(u8, I32Store8, ALIGN_1);
wasm_test_result_primitive!(i8, I32Store8, ALIGN_1);
wasm_test_result_primitive!(u16, I32Store16, ALIGN_2);
wasm_test_result_primitive!(i16, I32Store16, ALIGN_2);
wasm_test_result_primitive!(u32, I32Store, ALIGN_4);
wasm_test_result_primitive!(i32, I32Store, ALIGN_4);
wasm_test_result_primitive!(u64, I64Store, ALIGN_8);
wasm_test_result_primitive!(i64, I64Store, ALIGN_8);
wasm_test_result_primitive!(f32, F32Store, ALIGN_8);
wasm_test_result_primitive!(f64, F64Store, ALIGN_8);
wasm_test_result_stack_memory!(u128);
wasm_test_result_stack_memory!(i128);
wasm_test_result_stack_memory!(RocDec);
wasm_test_result_stack_memory!(RocStr);
impl<T: Wasm32TestResult> Wasm32TestResult for RocList<T> {
fn build_wrapper_body(main_function_index: u32) -> Vec<Instruction> {
build_wrapper_body_stack_memory(main_function_index, 12)
}
}
impl<T: Wasm32TestResult> Wasm32TestResult for &'_ T {
build_wrapper_body_primitive!(I32Store, ALIGN_4);
}
impl<T, const N: usize> Wasm32TestResult for [T; N]
where
T: Wasm32TestResult + FromWasm32Memory,
{
fn build_wrapper_body(main_function_index: u32) -> Vec<Instruction> {
build_wrapper_body_stack_memory(main_function_index, N * T::ACTUAL_WIDTH)
}
}
impl<T, U> Wasm32TestResult for (T, U)
where
T: Wasm32TestResult + FromWasm32Memory,
U: Wasm32TestResult + FromWasm32Memory,
{
fn build_wrapper_body(main_function_index: u32) -> Vec<Instruction> {
build_wrapper_body_stack_memory(main_function_index, T::ACTUAL_WIDTH + U::ACTUAL_WIDTH)
}
}
impl<T, U, V> Wasm32TestResult for (T, U, V)
where
T: Wasm32TestResult + FromWasm32Memory,
U: Wasm32TestResult + FromWasm32Memory,
V: Wasm32TestResult + FromWasm32Memory,
{
fn build_wrapper_body(main_function_index: u32) -> Vec<Instruction> {
build_wrapper_body_stack_memory(
main_function_index,
T::ACTUAL_WIDTH + U::ACTUAL_WIDTH + V::ACTUAL_WIDTH,
)
}
}