mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-01 07:41:12 +00:00
Tests passing with generic native/wasm interface
This commit is contained in:
parent
8b73b98622
commit
35c5b6bc4e
3 changed files with 51 additions and 34 deletions
|
@ -3,7 +3,7 @@ pub mod from_wasm32_memory;
|
||||||
|
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use parity_wasm::builder;
|
use parity_wasm::builder;
|
||||||
use parity_wasm::elements::{Internal, ValueType};
|
use parity_wasm::elements::{Instruction, Internal, ValueType};
|
||||||
|
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_module::symbol::{Interns, Symbol};
|
use roc_module::symbol::{Interns, Symbol};
|
||||||
|
@ -31,7 +31,18 @@ pub struct Env<'a> {
|
||||||
pub fn build_module<'a>(
|
pub fn build_module<'a>(
|
||||||
env: &'a Env,
|
env: &'a Env,
|
||||||
procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||||
) -> Result<std::vec::Vec<u8>, String> {
|
) -> Result<Vec<u8>, String> {
|
||||||
|
let (builder, _) = build_module_help(env, procedures)?;
|
||||||
|
let module = builder.build();
|
||||||
|
module
|
||||||
|
.to_bytes()
|
||||||
|
.map_err(|e| -> String { format!("Error serialising Wasm module {:?}", e) })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_module_help<'a>(
|
||||||
|
env: &'a Env,
|
||||||
|
procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||||
|
) -> Result<(builder::ModuleBuilder, u32), String> {
|
||||||
let mut backend = WasmBackend::new();
|
let mut backend = WasmBackend::new();
|
||||||
let mut layout_ids = LayoutIds::default();
|
let mut layout_ids = LayoutIds::default();
|
||||||
|
|
||||||
|
@ -48,8 +59,9 @@ pub fn build_module<'a>(
|
||||||
let mut procedures: std::vec::Vec<_> = procedures.into_iter().collect();
|
let mut procedures: std::vec::Vec<_> = procedures.into_iter().collect();
|
||||||
procedures.sort_by(|a, b| b.0 .0.cmp(&a.0 .0));
|
procedures.sort_by(|a, b| b.0 .0.cmp(&a.0 .0));
|
||||||
|
|
||||||
|
let mut function_index: u32 = 0;
|
||||||
for ((sym, layout), proc) in procedures {
|
for ((sym, layout), proc) in procedures {
|
||||||
let function_index = backend.build_proc(proc, sym)?;
|
function_index = backend.build_proc(proc, sym)?;
|
||||||
if env.exposed_to_host.contains(&sym) {
|
if env.exposed_to_host.contains(&sym) {
|
||||||
let fn_name = layout_ids
|
let fn_name = layout_ids
|
||||||
.get_toplevel(sym, &layout)
|
.get_toplevel(sym, &layout)
|
||||||
|
@ -71,15 +83,18 @@ pub fn build_module<'a>(
|
||||||
.with_min(MIN_MEMORY_SIZE_KB / PAGE_SIZE_KB)
|
.with_min(MIN_MEMORY_SIZE_KB / PAGE_SIZE_KB)
|
||||||
.build();
|
.build();
|
||||||
backend.builder.push_memory(memory);
|
backend.builder.push_memory(memory);
|
||||||
|
|
||||||
let memory_export = builder::export()
|
let memory_export = builder::export()
|
||||||
.field("memory")
|
.field("memory")
|
||||||
.with_internal(Internal::Memory(0))
|
.with_internal(Internal::Memory(0))
|
||||||
.build();
|
.build();
|
||||||
backend.builder.push_export(memory_export);
|
backend.builder.push_export(memory_export);
|
||||||
|
|
||||||
let module = backend.builder.build();
|
let stack_pointer_global = builder::global()
|
||||||
module
|
.with_type(PTR_TYPE)
|
||||||
.to_bytes()
|
.mutable()
|
||||||
.map_err(|e| -> String { format!("Error serialising Wasm module {:?}", e) })
|
.init_expr(Instruction::I32Const((MIN_MEMORY_SIZE_KB * 1024) as i32))
|
||||||
|
.build();
|
||||||
|
backend.builder.push_global(stack_pointer_global);
|
||||||
|
|
||||||
|
Ok((backend.builder, function_index))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
use roc_can::builtins::builtin_defs_map;
|
use roc_can::builtins::builtin_defs_map;
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
// use roc_std::{RocDec, RocList, RocOrder, RocStr};
|
// use roc_std::{RocDec, RocList, RocOrder, RocStr};
|
||||||
|
use crate::helpers::wasm32_test_result::Wasm32TestResult;
|
||||||
|
use roc_gen_wasm::from_wasm32_memory::FromWasm32Memory;
|
||||||
|
|
||||||
|
const TEST_WRAPPER_NAME: &str = "test_wrapper";
|
||||||
|
|
||||||
fn promote_expr_to_module(src: &str) -> String {
|
fn promote_expr_to_module(src: &str) -> String {
|
||||||
let mut buffer = String::from("app \"test\" provides [ main ] to \"./platform\"\n\nmain =\n");
|
let mut buffer = String::from("app \"test\" provides [ main ] to \"./platform\"\n\nmain =\n");
|
||||||
|
@ -16,12 +20,11 @@ fn promote_expr_to_module(src: &str) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn helper_wasm<'a>(
|
pub fn helper_wasm<'a, T: Wasm32TestResult>(
|
||||||
arena: &'a bumpalo::Bump,
|
arena: &'a bumpalo::Bump,
|
||||||
src: &str,
|
src: &str,
|
||||||
stdlib: &'a roc_builtins::std::StdLib,
|
stdlib: &'a roc_builtins::std::StdLib,
|
||||||
_is_gen_test: bool,
|
_result_type_dummy: &T,
|
||||||
_ignore_problems: bool,
|
|
||||||
) -> wasmer::Instance {
|
) -> wasmer::Instance {
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
@ -91,7 +94,11 @@ pub fn helper_wasm<'a>(
|
||||||
exposed_to_host,
|
exposed_to_host,
|
||||||
};
|
};
|
||||||
|
|
||||||
let module_bytes = roc_gen_wasm::build_module(&env, procedures).unwrap();
|
let (mut builder, main_function_index) =
|
||||||
|
roc_gen_wasm::build_module_help(&env, procedures).unwrap();
|
||||||
|
T::insert_test_wrapper(&mut builder, TEST_WRAPPER_NAME, main_function_index);
|
||||||
|
|
||||||
|
let module_bytes = builder.build().to_bytes().unwrap();
|
||||||
|
|
||||||
// for debugging (e.g. with wasm2wat)
|
// for debugging (e.g. with wasm2wat)
|
||||||
if false {
|
if false {
|
||||||
|
@ -128,45 +135,40 @@ pub fn helper_wasm<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn assert_wasm_evals_to_help<T>(src: &str, ignore_problems: bool) -> Result<T, String>
|
pub fn assert_wasm_evals_to_help<T>(src: &str, expected: T) -> Result<T, String>
|
||||||
where
|
where
|
||||||
T: Copy,
|
T: FromWasm32Memory + Wasm32TestResult,
|
||||||
{
|
{
|
||||||
let arena = bumpalo::Bump::new();
|
let arena = bumpalo::Bump::new();
|
||||||
|
|
||||||
// NOTE the stdlib must be in the arena; just taking a reference will segfault
|
// NOTE the stdlib must be in the arena; just taking a reference will segfault
|
||||||
let stdlib = arena.alloc(roc_builtins::std::standard_stdlib());
|
let stdlib = arena.alloc(roc_builtins::std::standard_stdlib());
|
||||||
|
|
||||||
let is_gen_test = true;
|
let instance = crate::helpers::eval::helper_wasm(&arena, src, stdlib, &expected);
|
||||||
let instance =
|
|
||||||
crate::helpers::eval::helper_wasm(&arena, src, stdlib, is_gen_test, ignore_problems);
|
|
||||||
|
|
||||||
let main_function = instance.exports.get_function("#UserApp_main_1").unwrap();
|
let memory = instance.exports.get_memory("memory").unwrap();
|
||||||
|
|
||||||
match main_function.call(&[]) {
|
let test_wrapper = instance.exports.get_function(TEST_WRAPPER_NAME).unwrap();
|
||||||
|
|
||||||
|
match test_wrapper.call(&[]) {
|
||||||
Err(e) => Err(format!("{:?}", e)),
|
Err(e) => Err(format!("{:?}", e)),
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
let integer = match result[0] {
|
let address = match result[0] {
|
||||||
wasmer::Value::I32(a) => a as i64,
|
wasmer::Value::I32(a) => a,
|
||||||
wasmer::Value::I64(a) => a,
|
|
||||||
wasmer::Value::F64(a) => a.to_bits() as i64,
|
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let output_ptr: &T;
|
let output = <T as FromWasm32Memory>::decode(memory, address as u32);
|
||||||
unsafe {
|
|
||||||
output_ptr = std::mem::transmute::<&i64, &T>(&integer);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(*output_ptr)
|
Ok(output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! assert_wasm_evals_to {
|
macro_rules! assert_wasm_evals_to {
|
||||||
($src:expr, $expected:expr, $ty:ty, $transform:expr, $ignore_problems:expr) => {
|
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||||
match $crate::helpers::eval::assert_wasm_evals_to_help::<$ty>($src, $ignore_problems) {
|
match $crate::helpers::eval::assert_wasm_evals_to_help::<$ty>($src, $expected) {
|
||||||
Err(msg) => println!("{:?}", msg),
|
Err(msg) => println!("{:?}", msg),
|
||||||
Ok(actual) => {
|
Ok(actual) => {
|
||||||
#[allow(clippy::bool_assert_comparison)]
|
#[allow(clippy::bool_assert_comparison)]
|
||||||
|
@ -176,11 +178,11 @@ macro_rules! assert_wasm_evals_to {
|
||||||
};
|
};
|
||||||
|
|
||||||
($src:expr, $expected:expr, $ty:ty) => {
|
($src:expr, $expected:expr, $ty:ty) => {
|
||||||
$crate::assert_wasm_evals_to!($src, $expected, $ty, $crate::helpers::eval::identity, false);
|
$crate::assert_wasm_evals_to!($src, $expected, $ty, $crate::helpers::eval::identity);
|
||||||
};
|
};
|
||||||
|
|
||||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||||
$crate::assert_wasm_evals_to!($src, $expected, $ty, $transform, false);
|
$crate::assert_wasm_evals_to!($src, $expected, $ty, $transform);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,7 +194,7 @@ macro_rules! assert_evals_to {
|
||||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||||
// Same as above, except with an additional transformation argument.
|
// Same as above, except with an additional transformation argument.
|
||||||
{
|
{
|
||||||
$crate::assert_wasm_evals_to!($src, $expected, $ty, $transform, false);
|
$crate::assert_wasm_evals_to!($src, $expected, $ty, $transform);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,12 +50,12 @@ macro_rules! build_wrapper_body_primitive {
|
||||||
const MAX_ALIGNED_SIZE: usize = 16;
|
const MAX_ALIGNED_SIZE: usize = 16;
|
||||||
let mut instructions = build_wrapper_body_prelude(MAX_ALIGNED_SIZE);
|
let mut instructions = build_wrapper_body_prelude(MAX_ALIGNED_SIZE);
|
||||||
instructions.extend([
|
instructions.extend([
|
||||||
|
GetGlobal(STACK_POINTER_GLOBAL_ID),
|
||||||
//
|
//
|
||||||
// Call the main function with no arguments. Get primitive back.
|
// Call the main function with no arguments. Get primitive back.
|
||||||
Call(main_function_index),
|
Call(main_function_index),
|
||||||
//
|
//
|
||||||
// Store the primitive at the allocated address
|
// Store the primitive at the allocated address
|
||||||
GetGlobal(STACK_POINTER_GLOBAL_ID),
|
|
||||||
$store_instruction($align, 0),
|
$store_instruction($align, 0),
|
||||||
//
|
//
|
||||||
// Return the result pointer
|
// Return the result pointer
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue