mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 23:04:49 +00:00
Merge test_dev, test_wasm, and test_wasm_util into test_gen
This commit is contained in:
parent
cba0f07992
commit
360974398a
32 changed files with 2417 additions and 6571 deletions
252
compiler/test_gen/src/helpers/dev.rs
Normal file
252
compiler/test_gen/src/helpers/dev.rs
Normal file
|
@ -0,0 +1,252 @@
|
|||
use libloading::Library;
|
||||
use roc_build::link::{link, LinkType};
|
||||
use roc_builtins::bitcode;
|
||||
use roc_can::builtins::builtin_defs_map;
|
||||
use roc_collections::all::MutMap;
|
||||
use tempfile::tempdir;
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn promote_expr_to_module(src: &str) -> String {
|
||||
let mut buffer = String::from("app \"test\" provides [ main ] to \"./platform\"\n\nmain =\n");
|
||||
|
||||
for line in src.lines() {
|
||||
// indent the body!
|
||||
buffer.push_str(" ");
|
||||
buffer.push_str(line);
|
||||
buffer.push('\n');
|
||||
}
|
||||
|
||||
buffer
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn helper(
|
||||
arena: &bumpalo::Bump,
|
||||
src: &str,
|
||||
stdlib: roc_builtins::std::StdLib,
|
||||
_leak: bool,
|
||||
lazy_literals: bool,
|
||||
) -> (String, Vec<roc_problem::can::Problem>, Library) {
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
let dir = tempdir().unwrap();
|
||||
let filename = PathBuf::from("Test.roc");
|
||||
let src_dir = Path::new("fake/test/path");
|
||||
let app_o_file = dir.path().join("app.o");
|
||||
|
||||
let module_src;
|
||||
let temp;
|
||||
if src.starts_with("app") {
|
||||
// this is already a module
|
||||
module_src = src;
|
||||
} else {
|
||||
// this is an expression, promote it to a module
|
||||
temp = promote_expr_to_module(src);
|
||||
module_src = &temp;
|
||||
}
|
||||
|
||||
let exposed_types = MutMap::default();
|
||||
let loaded = roc_load::file::load_and_monomorphize_from_str(
|
||||
arena,
|
||||
filename,
|
||||
module_src,
|
||||
&stdlib,
|
||||
src_dir,
|
||||
exposed_types,
|
||||
8,
|
||||
builtin_defs_map,
|
||||
);
|
||||
|
||||
let mut loaded = loaded.expect("failed to load module");
|
||||
|
||||
use roc_load::file::MonomorphizedModule;
|
||||
let MonomorphizedModule {
|
||||
procedures: top_procedures,
|
||||
interns,
|
||||
exposed_to_host,
|
||||
..
|
||||
} = loaded;
|
||||
|
||||
let mut procedures = MutMap::default();
|
||||
|
||||
for (key, proc) in top_procedures {
|
||||
procedures.insert(key, proc);
|
||||
}
|
||||
|
||||
// You can comment and uncomment this block out to get more useful information
|
||||
// while you're working on the dev backend!
|
||||
{
|
||||
// println!("=========== Procedures ==========");
|
||||
// println!("{:?}", procedures);
|
||||
// println!("=================================\n");
|
||||
|
||||
// println!("=========== Interns ==========");
|
||||
// println!("{:?}", interns);
|
||||
// println!("=================================\n");
|
||||
|
||||
// println!("=========== Exposed ==========");
|
||||
// println!("{:?}", exposed_to_host);
|
||||
// println!("=================================\n");
|
||||
}
|
||||
|
||||
debug_assert_eq!(exposed_to_host.len(), 1);
|
||||
let main_fn_symbol = loaded.entry_point.symbol;
|
||||
let main_fn_layout = loaded.entry_point.layout;
|
||||
|
||||
let mut layout_ids = roc_mono::layout::LayoutIds::default();
|
||||
let main_fn_name_base = layout_ids
|
||||
.get_toplevel(main_fn_symbol, &main_fn_layout)
|
||||
.to_symbol_string(main_fn_symbol, &interns);
|
||||
|
||||
let main_fn_name = format!("roc_{}_exposed", main_fn_name_base);
|
||||
|
||||
let mut lines = Vec::new();
|
||||
// errors whose reporting we delay (so we can see that code gen generates runtime errors)
|
||||
let mut delayed_errors = Vec::new();
|
||||
|
||||
for (home, (module_path, src)) in loaded.sources {
|
||||
use roc_reporting::report::{
|
||||
can_problem, mono_problem, type_problem, RocDocAllocator, DEFAULT_PALETTE,
|
||||
};
|
||||
|
||||
let can_problems = loaded.can_problems.remove(&home).unwrap_or_default();
|
||||
let type_problems = loaded.type_problems.remove(&home).unwrap_or_default();
|
||||
let mono_problems = loaded.mono_problems.remove(&home).unwrap_or_default();
|
||||
|
||||
let error_count = can_problems.len() + type_problems.len() + mono_problems.len();
|
||||
|
||||
if error_count == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let src_lines: Vec<&str> = src.split('\n').collect();
|
||||
let palette = DEFAULT_PALETTE;
|
||||
|
||||
// Report parsing and canonicalization problems
|
||||
let alloc = RocDocAllocator::new(&src_lines, home, &interns);
|
||||
|
||||
use roc_problem::can::Problem::*;
|
||||
for problem in can_problems.into_iter() {
|
||||
// Ignore "unused" problems
|
||||
match problem {
|
||||
UnusedDef(_, _) | UnusedArgument(_, _, _) | UnusedImport(_, _) => {
|
||||
delayed_errors.push(problem);
|
||||
continue;
|
||||
}
|
||||
_ => {
|
||||
let report = can_problem(&alloc, module_path.clone(), problem);
|
||||
let mut buf = String::new();
|
||||
|
||||
report.render_color_terminal(&mut buf, &alloc, &palette);
|
||||
|
||||
lines.push(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for problem in type_problems {
|
||||
if let Some(report) = type_problem(&alloc, module_path.clone(), problem) {
|
||||
let mut buf = String::new();
|
||||
|
||||
report.render_color_terminal(&mut buf, &alloc, &palette);
|
||||
|
||||
lines.push(buf);
|
||||
}
|
||||
}
|
||||
|
||||
for problem in mono_problems {
|
||||
let report = mono_problem(&alloc, module_path.clone(), problem);
|
||||
let mut buf = String::new();
|
||||
|
||||
report.render_color_terminal(&mut buf, &alloc, &palette);
|
||||
|
||||
lines.push(buf);
|
||||
}
|
||||
}
|
||||
|
||||
if !lines.is_empty() {
|
||||
println!("{}", lines.join("\n"));
|
||||
assert_eq!(0, 1, "Mistakes were made");
|
||||
}
|
||||
|
||||
let env = roc_gen_dev::Env {
|
||||
arena,
|
||||
interns,
|
||||
exposed_to_host: exposed_to_host.keys().copied().collect(),
|
||||
lazy_literals,
|
||||
generate_allocators: true, // Needed for testing, since we don't have a platform
|
||||
};
|
||||
|
||||
let target = target_lexicon::Triple::host();
|
||||
let module_object =
|
||||
roc_gen_dev::build_module(&env, &target, procedures).expect("failed to compile module");
|
||||
|
||||
let module_out = module_object
|
||||
.write()
|
||||
.expect("failed to build output object");
|
||||
std::fs::write(&app_o_file, module_out).expect("failed to write object to file");
|
||||
|
||||
// std::fs::copy(&app_o_file, "/tmp/app.o").unwrap();
|
||||
|
||||
let (mut child, dylib_path) = link(
|
||||
&target,
|
||||
app_o_file.clone(),
|
||||
// Long term we probably want a smarter way to link in zig builtins.
|
||||
// With the current method all methods are kept and it adds about 100k to all outputs.
|
||||
&[app_o_file.to_str().unwrap(), bitcode::OBJ_PATH],
|
||||
LinkType::Dylib,
|
||||
)
|
||||
.expect("failed to link dynamic library");
|
||||
|
||||
child.wait().unwrap();
|
||||
|
||||
// Load the dylib
|
||||
let path = dylib_path.as_path().to_str().unwrap();
|
||||
|
||||
// std::fs::copy(&path, "/tmp/libapp.so").unwrap();
|
||||
|
||||
let lib = unsafe { Library::new(path) }.expect("failed to load shared library");
|
||||
|
||||
(main_fn_name, delayed_errors, lib)
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! assert_evals_to {
|
||||
($src:expr, $expected:expr, $ty:ty) => {{
|
||||
assert_evals_to!($src, $expected, $ty, (|val| val));
|
||||
}};
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||
// Same as above, except with an additional transformation argument.
|
||||
{
|
||||
assert_evals_to!($src, $expected, $ty, $transform, true);
|
||||
}
|
||||
};
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr) => {
|
||||
// Run both with and without lazy literal optimization.
|
||||
{
|
||||
assert_evals_to!($src, $expected, $ty, $transform, $leak, false);
|
||||
}
|
||||
{
|
||||
assert_evals_to!($src, $expected, $ty, $transform, $leak, true);
|
||||
}
|
||||
};
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr, $lazy_literals:expr) => {
|
||||
use bumpalo::Bump;
|
||||
use roc_gen_dev::run_jit_function_raw;
|
||||
let stdlib = roc_builtins::std::standard_stdlib();
|
||||
|
||||
let arena = Bump::new();
|
||||
let (main_fn_name, errors, lib) =
|
||||
$crate::helpers::dev::helper(&arena, $src, stdlib, $leak, $lazy_literals);
|
||||
|
||||
let transform = |success| {
|
||||
let expected = $expected;
|
||||
let given = $transform(success);
|
||||
assert_eq!(&given, &expected);
|
||||
};
|
||||
run_jit_function_raw!(lib, main_fn_name, $ty, transform, errors)
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use assert_evals_to;
|
210
compiler/test_gen/src/helpers/from_wasm32_memory.rs
Normal file
210
compiler/test_gen/src/helpers/from_wasm32_memory.rs
Normal file
|
@ -0,0 +1,210 @@
|
|||
use roc_std::{RocDec, RocList, RocOrder, RocStr};
|
||||
|
||||
pub trait FromWasm32Memory: Sized {
|
||||
const SIZE_OF_WASM: usize;
|
||||
const ALIGN_OF_WASM: usize;
|
||||
const ACTUAL_WIDTH: usize = if (Self::SIZE_OF_WASM % Self::ALIGN_OF_WASM) == 0 {
|
||||
Self::SIZE_OF_WASM
|
||||
} else {
|
||||
Self::SIZE_OF_WASM + (Self::ALIGN_OF_WASM - (Self::SIZE_OF_WASM % Self::ALIGN_OF_WASM))
|
||||
};
|
||||
|
||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self;
|
||||
}
|
||||
|
||||
macro_rules! from_wasm_memory_primitive_decode {
|
||||
($type_name:ident) => {
|
||||
const SIZE_OF_WASM: usize = core::mem::size_of::<$type_name>();
|
||||
const ALIGN_OF_WASM: usize = core::mem::align_of::<$type_name>();
|
||||
|
||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
let mut output: MaybeUninit<Self> = MaybeUninit::uninit();
|
||||
let width = std::mem::size_of::<Self>();
|
||||
|
||||
let ptr = output.as_mut_ptr();
|
||||
let raw_ptr = ptr as *mut u8;
|
||||
let slice = unsafe { std::slice::from_raw_parts_mut(raw_ptr, width) };
|
||||
|
||||
let ptr: wasmer::WasmPtr<u8, wasmer::Array> = wasmer::WasmPtr::new(offset as u32);
|
||||
let foobar = (ptr.deref(memory, 0, width as u32)).unwrap();
|
||||
let wasm_slice = unsafe { std::mem::transmute(foobar) };
|
||||
|
||||
slice.copy_from_slice(wasm_slice);
|
||||
|
||||
unsafe { output.assume_init() }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! from_wasm_memory_primitive {
|
||||
($($type_name:ident ,)+) => {
|
||||
$(
|
||||
impl FromWasm32Memory for $type_name {
|
||||
from_wasm_memory_primitive_decode!($type_name);
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
from_wasm_memory_primitive!(
|
||||
u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, f32, f64, bool, RocDec, RocOrder,
|
||||
);
|
||||
|
||||
impl FromWasm32Memory for () {
|
||||
const SIZE_OF_WASM: usize = 0;
|
||||
const ALIGN_OF_WASM: usize = 0;
|
||||
|
||||
fn decode(_: &wasmer::Memory, _: u32) -> Self {}
|
||||
}
|
||||
|
||||
impl FromWasm32Memory for RocStr {
|
||||
const SIZE_OF_WASM: usize = 8;
|
||||
const ALIGN_OF_WASM: usize = 4;
|
||||
|
||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
||||
let bytes = <u64 as FromWasm32Memory>::decode(memory, offset);
|
||||
|
||||
let length = (bytes >> 32) as u32;
|
||||
let elements = bytes as u32;
|
||||
|
||||
if length == 0 {
|
||||
RocStr::default()
|
||||
} else if (length as i32) < 0 {
|
||||
// this is a small string
|
||||
let last_byte = bytes.to_ne_bytes()[7];
|
||||
let actual_length = (last_byte ^ 0b1000_0000) as usize;
|
||||
|
||||
let slice = &bytes.to_ne_bytes()[..actual_length as usize];
|
||||
RocStr::from_slice(slice)
|
||||
} else {
|
||||
// this is a big string
|
||||
let ptr: wasmer::WasmPtr<u8, wasmer::Array> = wasmer::WasmPtr::new(elements);
|
||||
let foobar = (ptr.deref(memory, 0, length)).unwrap();
|
||||
let wasm_slice = unsafe { std::mem::transmute(foobar) };
|
||||
|
||||
RocStr::from_slice(wasm_slice)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromWasm32Memory + Clone> FromWasm32Memory for RocList<T> {
|
||||
const SIZE_OF_WASM: usize = 8;
|
||||
const ALIGN_OF_WASM: usize = 4;
|
||||
|
||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
||||
let bytes = <u64 as FromWasm32Memory>::decode(memory, offset);
|
||||
|
||||
let length = (bytes >> 32) as u32;
|
||||
let elements = bytes as u32;
|
||||
|
||||
let mut items = Vec::with_capacity(length as usize);
|
||||
|
||||
for i in 0..length {
|
||||
let item = <T as FromWasm32Memory>::decode(
|
||||
memory,
|
||||
elements + i * <T as FromWasm32Memory>::SIZE_OF_WASM as u32,
|
||||
);
|
||||
items.push(item);
|
||||
}
|
||||
|
||||
RocList::from_slice(&items)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromWasm32Memory> FromWasm32Memory for &'_ T {
|
||||
const SIZE_OF_WASM: usize = 4;
|
||||
const ALIGN_OF_WASM: usize = 4;
|
||||
|
||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
||||
let elements = <u32 as FromWasm32Memory>::decode(memory, offset);
|
||||
|
||||
let actual = <T as FromWasm32Memory>::decode(memory, elements);
|
||||
|
||||
let b = Box::new(actual);
|
||||
|
||||
std::boxed::Box::<T>::leak(b)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromWasm32Memory + Clone, const N: usize> FromWasm32Memory for [T; N] {
|
||||
const SIZE_OF_WASM: usize = N * T::SIZE_OF_WASM;
|
||||
const ALIGN_OF_WASM: usize = T::ALIGN_OF_WASM;
|
||||
|
||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
||||
let ptr: wasmer::WasmPtr<u8, wasmer::Array> = wasmer::WasmPtr::new(offset);
|
||||
let width = <T as FromWasm32Memory>::SIZE_OF_WASM as u32 * N as u32;
|
||||
let foobar = (ptr.deref(memory, 0, width)).unwrap();
|
||||
let wasm_slice: &[T; N] = unsafe { &*(foobar as *const _ as *const [T; N]) };
|
||||
|
||||
wasm_slice.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromWasm32Memory for usize {
|
||||
const SIZE_OF_WASM: usize = 4;
|
||||
const ALIGN_OF_WASM: usize = 4;
|
||||
|
||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
||||
<u32 as FromWasm32Memory>::decode(memory, offset) as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromWasm32Memory, U: FromWasm32Memory> FromWasm32Memory for (T, U) {
|
||||
const SIZE_OF_WASM: usize = T::SIZE_OF_WASM + U::SIZE_OF_WASM;
|
||||
const ALIGN_OF_WASM: usize = max2(T::SIZE_OF_WASM, U::SIZE_OF_WASM);
|
||||
|
||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
||||
debug_assert!(
|
||||
T::ALIGN_OF_WASM >= U::ALIGN_OF_WASM,
|
||||
"this function does not handle alignment"
|
||||
);
|
||||
|
||||
let t = <T as FromWasm32Memory>::decode(memory, offset);
|
||||
|
||||
let u = <U as FromWasm32Memory>::decode(memory, offset + T::ACTUAL_WIDTH as u32);
|
||||
|
||||
(t, u)
|
||||
}
|
||||
}
|
||||
|
||||
const fn max2(a: usize, b: usize) -> usize {
|
||||
if a > b {
|
||||
a
|
||||
} else {
|
||||
b
|
||||
}
|
||||
}
|
||||
|
||||
const fn max3(a: usize, b: usize, c: usize) -> usize {
|
||||
max2(max2(a, b), c)
|
||||
}
|
||||
|
||||
impl<T: FromWasm32Memory, U: FromWasm32Memory, V: FromWasm32Memory> FromWasm32Memory for (T, U, V) {
|
||||
const SIZE_OF_WASM: usize = T::SIZE_OF_WASM + U::SIZE_OF_WASM + V::SIZE_OF_WASM;
|
||||
const ALIGN_OF_WASM: usize = max3(T::SIZE_OF_WASM, U::SIZE_OF_WASM, V::SIZE_OF_WASM);
|
||||
|
||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
||||
debug_assert!(
|
||||
T::ALIGN_OF_WASM >= U::ALIGN_OF_WASM,
|
||||
"this function does not handle alignment"
|
||||
);
|
||||
|
||||
debug_assert!(
|
||||
U::ALIGN_OF_WASM >= V::ALIGN_OF_WASM,
|
||||
"this function does not handle alignment"
|
||||
);
|
||||
|
||||
let t = <T as FromWasm32Memory>::decode(memory, offset);
|
||||
|
||||
let u = <U as FromWasm32Memory>::decode(memory, offset + T::ACTUAL_WIDTH as u32);
|
||||
|
||||
let v = <V as FromWasm32Memory>::decode(
|
||||
memory,
|
||||
offset + T::ACTUAL_WIDTH as u32 + U::ACTUAL_WIDTH as u32,
|
||||
);
|
||||
|
||||
(t, u, v)
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
use crate::helpers::from_wasm32_memory::FromWasm32Memory;
|
||||
use inkwell::module::Module;
|
||||
use libloading::Library;
|
||||
use roc_build::link::module_to_dylib;
|
||||
|
@ -10,7 +11,6 @@ use roc_module::symbol::Symbol;
|
|||
use roc_mono::ir::OptLevel;
|
||||
use roc_types::subs::VarStore;
|
||||
use target_lexicon::Triple;
|
||||
use test_wasm_util::from_wasm32_memory::FromWasm32Memory;
|
||||
|
||||
fn promote_expr_to_module(src: &str) -> String {
|
||||
let mut buffer = String::from("app \"test\" provides [ main ] to \"./platform\"\n\nmain =\n");
|
||||
|
@ -469,7 +469,7 @@ where
|
|||
let stdlib = arena.alloc(roc_builtins::std::standard_stdlib());
|
||||
|
||||
let is_gen_test = true;
|
||||
let instance = crate::helpers::eval::helper_wasm(
|
||||
let instance = crate::helpers::llvm::helper_wasm(
|
||||
&arena,
|
||||
src,
|
||||
stdlib,
|
||||
|
@ -480,7 +480,7 @@ where
|
|||
|
||||
let memory = instance.exports.get_memory("memory").unwrap();
|
||||
|
||||
crate::helpers::eval::MEMORY.with(|f| {
|
||||
crate::helpers::llvm::MEMORY.with(|f| {
|
||||
*f.borrow_mut() = Some(unsafe { std::mem::transmute(memory) });
|
||||
});
|
||||
|
||||
|
@ -494,7 +494,7 @@ where
|
|||
_ => panic!(),
|
||||
};
|
||||
|
||||
let output = <T as crate::helpers::eval::FromWasm32Memory>::decode(
|
||||
let output = <T as crate::helpers::llvm::FromWasm32Memory>::decode(
|
||||
memory,
|
||||
// skip the RocCallResult tag id
|
||||
address as u32 + 8,
|
||||
|
@ -505,10 +505,10 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! assert_wasm_evals_to {
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr, $ignore_problems:expr) => {
|
||||
match $crate::helpers::eval::assert_wasm_evals_to_help::<$ty>($src, $ignore_problems) {
|
||||
match $crate::helpers::llvm::assert_wasm_evals_to_help::<$ty>($src, $ignore_problems) {
|
||||
Err(msg) => panic!("Wasm test failed: {:?}", msg),
|
||||
Ok(actual) => {
|
||||
#[allow(clippy::bool_assert_comparison)]
|
||||
|
@ -518,15 +518,21 @@ macro_rules! assert_wasm_evals_to {
|
|||
};
|
||||
|
||||
($src:expr, $expected:expr, $ty:ty) => {
|
||||
$crate::assert_wasm_evals_to!($src, $expected, $ty, $crate::helpers::eval::identity, false);
|
||||
$crate::helpers::llvm::assert_wasm_evals_to!(
|
||||
$src,
|
||||
$expected,
|
||||
$ty,
|
||||
$crate::helpers::llvm::identity,
|
||||
false
|
||||
);
|
||||
};
|
||||
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||
$crate::assert_wasm_evals_to!($src, $expected, $ty, $transform, false);
|
||||
$crate::helpers::llvm::assert_wasm_evals_to!($src, $expected, $ty, $transform, false);
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! assert_llvm_evals_to {
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr, $ignore_problems:expr) => {
|
||||
use bumpalo::Bump;
|
||||
|
@ -540,7 +546,7 @@ macro_rules! assert_llvm_evals_to {
|
|||
let stdlib = arena.alloc(roc_builtins::std::standard_stdlib());
|
||||
|
||||
let is_gen_test = true;
|
||||
let (main_fn_name, errors, lib) = $crate::helpers::eval::helper(
|
||||
let (main_fn_name, errors, lib) = $crate::helpers::llvm::helper(
|
||||
&arena,
|
||||
$src,
|
||||
stdlib,
|
||||
|
@ -559,26 +565,32 @@ macro_rules! assert_llvm_evals_to {
|
|||
};
|
||||
|
||||
($src:expr, $expected:expr, $ty:ty) => {
|
||||
$crate::assert_llvm_evals_to!($src, $expected, $ty, $crate::helpers::eval::identity, false);
|
||||
$crate::helpers::llvm::assert_llvm_evals_to!(
|
||||
$src,
|
||||
$expected,
|
||||
$ty,
|
||||
$crate::helpers::llvm::identity,
|
||||
false
|
||||
);
|
||||
};
|
||||
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||
$crate::assert_llvm_evals_to!($src, $expected, $ty, $transform, false);
|
||||
$crate::helpers::llvm::assert_llvm_evals_to!($src, $expected, $ty, $transform, false);
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! assert_evals_to {
|
||||
($src:expr, $expected:expr, $ty:ty) => {{
|
||||
assert_evals_to!($src, $expected, $ty, $crate::helpers::eval::identity);
|
||||
assert_evals_to!($src, $expected, $ty, $crate::helpers::llvm::identity);
|
||||
}};
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||
// Same as above, except with an additional transformation argument.
|
||||
{
|
||||
#[cfg(feature = "wasm-cli-run")]
|
||||
$crate::assert_wasm_evals_to!($src, $expected, $ty, $transform, false);
|
||||
$crate::helpers::llvm::assert_wasm_evals_to!($src, $expected, $ty, $transform, false);
|
||||
|
||||
$crate::assert_llvm_evals_to!($src, $expected, $ty, $transform, false);
|
||||
$crate::helpers::llvm::assert_llvm_evals_to!($src, $expected, $ty, $transform, false);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -588,18 +600,32 @@ pub fn identity<T>(value: T) -> T {
|
|||
value
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! assert_non_opt_evals_to {
|
||||
($src:expr, $expected:expr, $ty:ty) => {{
|
||||
$crate::assert_llvm_evals_to!($src, $expected, $ty, $crate::helpers::eval::identity);
|
||||
$crate::helpers::llvm::assert_llvm_evals_to!(
|
||||
$src,
|
||||
$expected,
|
||||
$ty,
|
||||
$crate::helpers::llvm::identity
|
||||
);
|
||||
}};
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||
// Same as above, except with an additional transformation argument.
|
||||
{
|
||||
$crate::assert_llvm_evals_to!($src, $expected, $ty, $transform, false);
|
||||
$crate::helpers::llvm::assert_llvm_evals_to!($src, $expected, $ty, $transform, false);
|
||||
}
|
||||
};
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {{
|
||||
$crate::assert_llvm_evals_to!($src, $expected, $ty, $transform);
|
||||
$crate::helpers::llvm::assert_llvm_evals_to!($src, $expected, $ty, $transform);
|
||||
}};
|
||||
}
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use assert_evals_to;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use assert_llvm_evals_to;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use assert_non_opt_evals_to;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use assert_wasm_evals_to;
|
|
@ -1,7 +1,14 @@
|
|||
extern crate bumpalo;
|
||||
|
||||
#[macro_use]
|
||||
pub mod eval;
|
||||
#[cfg(feature = "gen-dev")]
|
||||
pub mod dev;
|
||||
pub mod from_wasm32_memory;
|
||||
#[cfg(feature = "gen-llvm")]
|
||||
pub mod llvm;
|
||||
#[cfg(feature = "gen-wasm")]
|
||||
pub mod wasm;
|
||||
#[cfg(feature = "gen-wasm")]
|
||||
pub mod wasm32_test_result;
|
||||
|
||||
/// Used in the with_larger_debug_stack() function, for tests that otherwise
|
||||
/// run out of stack space in debug builds (but don't in --release builds)
|
||||
|
|
239
compiler/test_gen/src/helpers/wasm.rs
Normal file
239
compiler/test_gen/src/helpers/wasm.rs
Normal file
|
@ -0,0 +1,239 @@
|
|||
use std::cell::Cell;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use crate::helpers::from_wasm32_memory::FromWasm32Memory;
|
||||
use crate::helpers::wasm32_test_result::Wasm32TestResult;
|
||||
use roc_can::builtins::builtin_defs_map;
|
||||
use roc_collections::all::{MutMap, MutSet};
|
||||
|
||||
const TEST_WRAPPER_NAME: &str = "test_wrapper";
|
||||
|
||||
std::thread_local! {
|
||||
static TEST_COUNTER: Cell<u32> = Cell::new(0);
|
||||
}
|
||||
|
||||
fn promote_expr_to_module(src: &str) -> String {
|
||||
let mut buffer = String::from("app \"test\" provides [ main ] to \"./platform\"\n\nmain =\n");
|
||||
|
||||
for line in src.lines() {
|
||||
// indent the body!
|
||||
buffer.push_str(" ");
|
||||
buffer.push_str(line);
|
||||
buffer.push('\n');
|
||||
}
|
||||
|
||||
buffer
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn helper_wasm<'a, T: Wasm32TestResult>(
|
||||
arena: &'a bumpalo::Bump,
|
||||
src: &str,
|
||||
stdlib: &'a roc_builtins::std::StdLib,
|
||||
_result_type_dummy: &T,
|
||||
) -> wasmer::Instance {
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
let filename = PathBuf::from("Test.roc");
|
||||
let src_dir = Path::new("fake/test/path");
|
||||
|
||||
let module_src;
|
||||
let temp;
|
||||
if src.starts_with("app") {
|
||||
// this is already a module
|
||||
module_src = src;
|
||||
} else {
|
||||
// this is an expression, promote it to a module
|
||||
temp = promote_expr_to_module(src);
|
||||
module_src = &temp;
|
||||
}
|
||||
|
||||
let exposed_types = MutMap::default();
|
||||
let loaded = roc_load::file::load_and_monomorphize_from_str(
|
||||
arena,
|
||||
filename,
|
||||
module_src,
|
||||
stdlib,
|
||||
src_dir,
|
||||
exposed_types,
|
||||
8,
|
||||
builtin_defs_map,
|
||||
);
|
||||
|
||||
let loaded = loaded.expect("failed to load module");
|
||||
|
||||
use roc_load::file::MonomorphizedModule;
|
||||
let MonomorphizedModule {
|
||||
procedures,
|
||||
interns,
|
||||
exposed_to_host,
|
||||
..
|
||||
} = loaded;
|
||||
|
||||
// You can comment and uncomment this block out to get more useful information
|
||||
// while you're working on the wasm backend!
|
||||
// {
|
||||
// println!("=========== Procedures ==========");
|
||||
// println!("{:?}", procedures);
|
||||
// println!("=================================\n");
|
||||
|
||||
// println!("=========== Interns ==========");
|
||||
// println!("{:?}", interns);
|
||||
// println!("=================================\n");
|
||||
|
||||
// println!("=========== Exposed ==========");
|
||||
// println!("{:?}", exposed_to_host);
|
||||
// println!("=================================\n");
|
||||
// }
|
||||
|
||||
debug_assert_eq!(exposed_to_host.len(), 1);
|
||||
let main_fn_symbol = loaded.entry_point.symbol;
|
||||
let main_fn_index = procedures
|
||||
.keys()
|
||||
.position(|(s, _)| *s == main_fn_symbol)
|
||||
.unwrap();
|
||||
|
||||
let exposed_to_host = exposed_to_host.keys().copied().collect::<MutSet<_>>();
|
||||
|
||||
let env = roc_gen_wasm::Env {
|
||||
arena,
|
||||
interns,
|
||||
exposed_to_host,
|
||||
};
|
||||
|
||||
let mut wasm_module = roc_gen_wasm::build_module_help(&env, procedures).unwrap();
|
||||
|
||||
T::insert_test_wrapper(
|
||||
arena,
|
||||
&mut wasm_module,
|
||||
TEST_WRAPPER_NAME,
|
||||
main_fn_index as u32,
|
||||
);
|
||||
|
||||
let mut module_bytes = std::vec::Vec::with_capacity(4096);
|
||||
wasm_module.serialize(&mut module_bytes);
|
||||
|
||||
// for debugging (e.g. with wasm2wat or wasm-objdump)
|
||||
if false {
|
||||
use std::io::Write;
|
||||
|
||||
let mut hash_state = DefaultHasher::new();
|
||||
src.hash(&mut hash_state);
|
||||
let src_hash = hash_state.finish();
|
||||
|
||||
// Filename contains a hash of the Roc test source code. Helpful when comparing across commits.
|
||||
let dir = "/tmp/roc/compiler/gen_wasm/output";
|
||||
std::fs::create_dir_all(dir).unwrap();
|
||||
let path = format!("{}/test-{:016x}.wasm", dir, src_hash);
|
||||
|
||||
// Print out filename (appears just after test name)
|
||||
println!("dumping file {:?}", path);
|
||||
|
||||
match std::fs::File::create(path) {
|
||||
Err(e) => eprintln!("Problem creating wasm debug file: {:?}", e),
|
||||
Ok(mut file) => {
|
||||
file.write_all(&module_bytes).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now, do wasmer stuff
|
||||
|
||||
use wasmer::{Instance, Module, Store};
|
||||
|
||||
let store = Store::default();
|
||||
// let module = Module::from_file(&store, &test_wasm_path).unwrap();
|
||||
let wasmer_module = Module::from_binary(&store, &module_bytes).unwrap();
|
||||
|
||||
// First, we create the `WasiEnv`
|
||||
use wasmer_wasi::WasiState;
|
||||
let mut wasi_env = WasiState::new("hello").finalize().unwrap();
|
||||
|
||||
// Then, we get the import object related to our WASI
|
||||
// and attach it to the Wasm instance.
|
||||
let import_object = wasi_env
|
||||
.import_object(&wasmer_module)
|
||||
.unwrap_or_else(|_| wasmer::imports!());
|
||||
|
||||
Instance::new(&wasmer_module, &import_object).unwrap()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn assert_wasm_evals_to_help<T>(src: &str, expected: T) -> Result<T, String>
|
||||
where
|
||||
T: FromWasm32Memory + Wasm32TestResult,
|
||||
{
|
||||
let arena = bumpalo::Bump::new();
|
||||
|
||||
// NOTE the stdlib must be in the arena; just taking a reference will segfault
|
||||
let stdlib = arena.alloc(roc_builtins::std::standard_stdlib());
|
||||
|
||||
let instance = crate::helpers::wasm::helper_wasm(&arena, src, stdlib, &expected);
|
||||
|
||||
let memory = instance.exports.get_memory("memory").unwrap();
|
||||
|
||||
let test_wrapper = instance.exports.get_function(TEST_WRAPPER_NAME).unwrap();
|
||||
|
||||
match test_wrapper.call(&[]) {
|
||||
Err(e) => Err(format!("{:?}", e)),
|
||||
Ok(result) => {
|
||||
let address = match result[0] {
|
||||
wasmer::Value::I32(a) => a,
|
||||
_ => panic!(),
|
||||
};
|
||||
|
||||
let output = <T as FromWasm32Memory>::decode(memory, address as u32);
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! assert_wasm_evals_to {
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||
match $crate::helpers::wasm::assert_wasm_evals_to_help::<$ty>($src, $expected) {
|
||||
Err(msg) => panic!("{:?}", msg),
|
||||
Ok(actual) => {
|
||||
assert_eq!($transform(actual), $expected)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
($src:expr, $expected:expr, $ty:ty) => {
|
||||
$crate::helpers::wasm::assert_wasm_evals_to!(
|
||||
$src,
|
||||
$expected,
|
||||
$ty,
|
||||
$crate::helpers::wasm::identity
|
||||
);
|
||||
};
|
||||
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||
$crate::helpers::wasm::assert_wasm_evals_to!($src, $expected, $ty, $transform);
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! assert_evals_to {
|
||||
($src:expr, $expected:expr, $ty:ty) => {{
|
||||
assert_evals_to!($src, $expected, $ty, $crate::helpers::wasm::identity);
|
||||
}};
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||
// Same as above, except with an additional transformation argument.
|
||||
{
|
||||
$crate::helpers::wasm::assert_wasm_evals_to!($src, $expected, $ty, $transform);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn identity<T>(value: T) -> T {
|
||||
value
|
||||
}
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use assert_evals_to;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use assert_wasm_evals_to;
|
268
compiler/test_gen/src/helpers/wasm32_test_result.rs
Normal file
268
compiler/test_gen/src/helpers/wasm32_test_result.rs
Normal file
|
@ -0,0 +1,268 @@
|
|||
use bumpalo::collections::Vec;
|
||||
|
||||
use crate::helpers::from_wasm32_memory::FromWasm32Memory;
|
||||
use roc_gen_wasm::wasm_module::opcodes;
|
||||
use roc_gen_wasm::wasm_module::{
|
||||
Align, CodeBuilder, Export, ExportType, LocalId, Signature, ValueType, WasmModule,
|
||||
};
|
||||
use roc_std::{RocDec, RocList, RocOrder, RocStr};
|
||||
|
||||
pub trait Wasm32TestResult {
|
||||
fn insert_test_wrapper<'a>(
|
||||
arena: &'a bumpalo::Bump,
|
||||
wasm_module: &mut WasmModule<'a>,
|
||||
wrapper_name: &str,
|
||||
main_function_index: u32,
|
||||
) {
|
||||
wasm_module.add_function_signature(Signature {
|
||||
param_types: Vec::with_capacity_in(0, arena),
|
||||
ret_type: Some(ValueType::I32),
|
||||
});
|
||||
|
||||
wasm_module.export.entries.push(Export {
|
||||
name: wrapper_name.to_string(),
|
||||
ty: ExportType::Func,
|
||||
index: wasm_module.code.code_builders.len() as u32,
|
||||
});
|
||||
|
||||
let mut code_builder = CodeBuilder::new(arena);
|
||||
Self::build_wrapper_body(&mut code_builder, main_function_index);
|
||||
wasm_module.code.code_builders.push(code_builder);
|
||||
}
|
||||
|
||||
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32);
|
||||
}
|
||||
|
||||
macro_rules! build_wrapper_body_primitive {
|
||||
($store_instruction: ident, $align: expr) => {
|
||||
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32) {
|
||||
let frame_pointer_id = LocalId(0);
|
||||
let frame_pointer = Some(frame_pointer_id);
|
||||
let local_types = &[ValueType::I32];
|
||||
let frame_size = 8;
|
||||
|
||||
code_builder.get_local(frame_pointer_id);
|
||||
// Raw "call" instruction. Don't bother with symbol & relocation since we're not going to link.
|
||||
code_builder.inst_imm32(opcodes::CALL, 0, true, main_function_index);
|
||||
code_builder.$store_instruction($align, 0);
|
||||
code_builder.get_local(frame_pointer_id);
|
||||
|
||||
code_builder.finalize(local_types, frame_size, frame_pointer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! wasm_test_result_primitive {
|
||||
($type_name: ident, $store_instruction: ident, $align: expr) => {
|
||||
impl Wasm32TestResult for $type_name {
|
||||
build_wrapper_body_primitive!($store_instruction, $align);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn build_wrapper_body_stack_memory(
|
||||
code_builder: &mut CodeBuilder,
|
||||
main_function_index: u32,
|
||||
size: usize,
|
||||
) {
|
||||
let local_id = LocalId(0);
|
||||
let local_types = &[ValueType::I32];
|
||||
let frame_pointer = Some(local_id);
|
||||
|
||||
code_builder.get_local(local_id);
|
||||
// Raw "call" instruction. Don't bother with symbol & relocation since we're not going to link.
|
||||
code_builder.inst_imm32(opcodes::CALL, 0, true, main_function_index);
|
||||
code_builder.get_local(local_id);
|
||||
code_builder.finalize(local_types, size as i32, frame_pointer);
|
||||
}
|
||||
|
||||
macro_rules! wasm_test_result_stack_memory {
|
||||
($type_name: ident) => {
|
||||
impl Wasm32TestResult for $type_name {
|
||||
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32) {
|
||||
build_wrapper_body_stack_memory(
|
||||
code_builder,
|
||||
main_function_index,
|
||||
$type_name::ACTUAL_WIDTH,
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
wasm_test_result_primitive!(bool, i32_store8, Align::Bytes1);
|
||||
wasm_test_result_primitive!(RocOrder, i32_store8, Align::Bytes1);
|
||||
|
||||
wasm_test_result_primitive!(u8, i32_store8, Align::Bytes1);
|
||||
wasm_test_result_primitive!(i8, i32_store8, Align::Bytes1);
|
||||
wasm_test_result_primitive!(u16, i32_store16, Align::Bytes2);
|
||||
wasm_test_result_primitive!(i16, i32_store16, Align::Bytes2);
|
||||
wasm_test_result_primitive!(u32, i32_store, Align::Bytes4);
|
||||
wasm_test_result_primitive!(i32, i32_store, Align::Bytes4);
|
||||
wasm_test_result_primitive!(u64, i64_store, Align::Bytes8);
|
||||
wasm_test_result_primitive!(i64, i64_store, Align::Bytes8);
|
||||
wasm_test_result_primitive!(usize, i32_store, Align::Bytes4);
|
||||
|
||||
wasm_test_result_primitive!(f32, f32_store, Align::Bytes8);
|
||||
wasm_test_result_primitive!(f64, f64_store, Align::Bytes8);
|
||||
|
||||
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(code_builder: &mut CodeBuilder, main_function_index: u32) {
|
||||
build_wrapper_body_stack_memory(code_builder, main_function_index, 12)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Wasm32TestResult> Wasm32TestResult for &'_ T {
|
||||
build_wrapper_body_primitive!(i32_store, Align::Bytes4);
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Wasm32TestResult for [T; N]
|
||||
where
|
||||
T: Wasm32TestResult + FromWasm32Memory,
|
||||
{
|
||||
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32) {
|
||||
build_wrapper_body_stack_memory(code_builder, 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(code_builder: &mut CodeBuilder, main_function_index: u32) {
|
||||
build_wrapper_body_stack_memory(
|
||||
code_builder,
|
||||
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(code_builder: &mut CodeBuilder, main_function_index: u32) {
|
||||
build_wrapper_body_stack_memory(
|
||||
code_builder,
|
||||
main_function_index,
|
||||
T::ACTUAL_WIDTH + U::ACTUAL_WIDTH + V::ACTUAL_WIDTH,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U, V, W> Wasm32TestResult for (T, U, V, W)
|
||||
where
|
||||
T: Wasm32TestResult + FromWasm32Memory,
|
||||
U: Wasm32TestResult + FromWasm32Memory,
|
||||
V: Wasm32TestResult + FromWasm32Memory,
|
||||
W: Wasm32TestResult + FromWasm32Memory,
|
||||
{
|
||||
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32) {
|
||||
build_wrapper_body_stack_memory(
|
||||
code_builder,
|
||||
main_function_index,
|
||||
T::ACTUAL_WIDTH + U::ACTUAL_WIDTH + V::ACTUAL_WIDTH + W::ACTUAL_WIDTH,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U, V, W, X> Wasm32TestResult for (T, U, V, W, X)
|
||||
where
|
||||
T: Wasm32TestResult + FromWasm32Memory,
|
||||
U: Wasm32TestResult + FromWasm32Memory,
|
||||
V: Wasm32TestResult + FromWasm32Memory,
|
||||
W: Wasm32TestResult + FromWasm32Memory,
|
||||
X: Wasm32TestResult + FromWasm32Memory,
|
||||
{
|
||||
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32) {
|
||||
build_wrapper_body_stack_memory(
|
||||
code_builder,
|
||||
main_function_index,
|
||||
T::ACTUAL_WIDTH + U::ACTUAL_WIDTH + V::ACTUAL_WIDTH + W::ACTUAL_WIDTH + X::ACTUAL_WIDTH,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U, V, W, X, Y> Wasm32TestResult for (T, U, V, W, X, Y)
|
||||
where
|
||||
T: Wasm32TestResult + FromWasm32Memory,
|
||||
U: Wasm32TestResult + FromWasm32Memory,
|
||||
V: Wasm32TestResult + FromWasm32Memory,
|
||||
W: Wasm32TestResult + FromWasm32Memory,
|
||||
X: Wasm32TestResult + FromWasm32Memory,
|
||||
Y: Wasm32TestResult + FromWasm32Memory,
|
||||
{
|
||||
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32) {
|
||||
build_wrapper_body_stack_memory(
|
||||
code_builder,
|
||||
main_function_index,
|
||||
T::ACTUAL_WIDTH
|
||||
+ U::ACTUAL_WIDTH
|
||||
+ V::ACTUAL_WIDTH
|
||||
+ W::ACTUAL_WIDTH
|
||||
+ X::ACTUAL_WIDTH
|
||||
+ Y::ACTUAL_WIDTH,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U, V, W, X, Y, Z> Wasm32TestResult for (T, U, V, W, X, Y, Z)
|
||||
where
|
||||
T: Wasm32TestResult + FromWasm32Memory,
|
||||
U: Wasm32TestResult + FromWasm32Memory,
|
||||
V: Wasm32TestResult + FromWasm32Memory,
|
||||
W: Wasm32TestResult + FromWasm32Memory,
|
||||
X: Wasm32TestResult + FromWasm32Memory,
|
||||
Y: Wasm32TestResult + FromWasm32Memory,
|
||||
Z: Wasm32TestResult + FromWasm32Memory,
|
||||
{
|
||||
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32) {
|
||||
build_wrapper_body_stack_memory(
|
||||
code_builder,
|
||||
main_function_index,
|
||||
T::ACTUAL_WIDTH
|
||||
+ U::ACTUAL_WIDTH
|
||||
+ V::ACTUAL_WIDTH
|
||||
+ W::ACTUAL_WIDTH
|
||||
+ X::ACTUAL_WIDTH
|
||||
+ Y::ACTUAL_WIDTH
|
||||
+ Z::ACTUAL_WIDTH,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U, V, W, X, Y, Z, A> Wasm32TestResult for (T, U, V, W, X, Y, Z, A)
|
||||
where
|
||||
T: Wasm32TestResult + FromWasm32Memory,
|
||||
U: Wasm32TestResult + FromWasm32Memory,
|
||||
V: Wasm32TestResult + FromWasm32Memory,
|
||||
W: Wasm32TestResult + FromWasm32Memory,
|
||||
X: Wasm32TestResult + FromWasm32Memory,
|
||||
Y: Wasm32TestResult + FromWasm32Memory,
|
||||
Z: Wasm32TestResult + FromWasm32Memory,
|
||||
A: Wasm32TestResult + FromWasm32Memory,
|
||||
{
|
||||
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32) {
|
||||
build_wrapper_body_stack_memory(
|
||||
code_builder,
|
||||
main_function_index,
|
||||
T::ACTUAL_WIDTH
|
||||
+ U::ACTUAL_WIDTH
|
||||
+ V::ACTUAL_WIDTH
|
||||
+ W::ACTUAL_WIDTH
|
||||
+ X::ACTUAL_WIDTH
|
||||
+ Y::ACTUAL_WIDTH
|
||||
+ Z::ACTUAL_WIDTH
|
||||
+ A::ACTUAL_WIDTH,
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue