mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-09 06:08:29 +00:00
Add multi-value-import tests.
Also fix implementation of trampolines in LLVM.
This commit is contained in:
@@ -1,6 +1,9 @@
|
|||||||
use crate::config::{CompiledFunctionKind, LLVMConfig};
|
use crate::config::{CompiledFunctionKind, LLVMConfig};
|
||||||
use crate::object_file::load_object_file;
|
use crate::object_file::load_object_file;
|
||||||
use crate::translator::abi::{func_type_to_llvm, is_sret, rets_from_call};
|
use crate::translator::abi::{
|
||||||
|
func_type_to_llvm, get_vmctx_ptr_param, is_sret, pack_values_for_register_return,
|
||||||
|
rets_from_call,
|
||||||
|
};
|
||||||
use crate::translator::intrinsics::{type_to_llvm, type_to_llvm_ptr, Intrinsics};
|
use crate::translator::intrinsics::{type_to_llvm, type_to_llvm_ptr, Intrinsics};
|
||||||
use inkwell::{
|
use inkwell::{
|
||||||
attributes::{Attribute, AttributeLoc},
|
attributes::{Attribute, AttributeLoc},
|
||||||
@@ -14,7 +17,6 @@ use inkwell::{
|
|||||||
};
|
};
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::iter;
|
|
||||||
use wasm_common::{FunctionType, Type};
|
use wasm_common::{FunctionType, Type};
|
||||||
use wasmer_compiler::{CompileError, FunctionBody};
|
use wasmer_compiler::{CompileError, FunctionBody};
|
||||||
|
|
||||||
@@ -134,16 +136,11 @@ impl FuncTrampoline {
|
|||||||
module.set_data_layout(&target_machine.get_target_data().get_data_layout());
|
module.set_data_layout(&target_machine.get_target_data().get_data_layout());
|
||||||
let intrinsics = Intrinsics::declare(&module, &self.ctx);
|
let intrinsics = Intrinsics::declare(&module, &self.ctx);
|
||||||
|
|
||||||
let params = iter::once(Ok(intrinsics.ctx_ptr_ty.as_basic_type_enum()))
|
let (trampoline_ty, trampoline_attrs) = func_type_to_llvm(&self.ctx, &intrinsics, ty)?;
|
||||||
.chain(
|
|
||||||
ty.params()
|
|
||||||
.iter()
|
|
||||||
.map(|param_ty| type_to_llvm(&intrinsics, *param_ty)),
|
|
||||||
)
|
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
|
||||||
let trampoline_ty = intrinsics.void_ty.fn_type(params.as_slice(), false);
|
|
||||||
|
|
||||||
let trampoline_func = module.add_function("", trampoline_ty, Some(Linkage::External));
|
let trampoline_func = module.add_function("", trampoline_ty, Some(Linkage::External));
|
||||||
|
for (attr, attr_loc) in trampoline_attrs {
|
||||||
|
trampoline_func.add_attribute(attr_loc, attr);
|
||||||
|
}
|
||||||
trampoline_func
|
trampoline_func
|
||||||
.as_global_value()
|
.as_global_value()
|
||||||
.set_section(FUNCTION_SECTION);
|
.set_section(FUNCTION_SECTION);
|
||||||
@@ -343,7 +340,12 @@ fn generate_dynamic_trampoline<'ctx>(
|
|||||||
let ptr = builder
|
let ptr = builder
|
||||||
.build_bitcast(ptr, type_to_llvm_ptr(intrinsics, func_sig.params()[i])?, "")
|
.build_bitcast(ptr, type_to_llvm_ptr(intrinsics, func_sig.params()[i])?, "")
|
||||||
.into_pointer_value();
|
.into_pointer_value();
|
||||||
builder.build_store(ptr, trampoline_func.get_nth_param(i as u32 + 1).unwrap());
|
builder.build_store(
|
||||||
|
ptr,
|
||||||
|
trampoline_func
|
||||||
|
.get_nth_param(i as u32 + if is_sret(func_sig)? { 2 } else { 1 })
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let callee_ty = intrinsics
|
let callee_ty = intrinsics
|
||||||
@@ -358,7 +360,7 @@ fn generate_dynamic_trampoline<'ctx>(
|
|||||||
.ptr_type(AddressSpace::Generic)
|
.ptr_type(AddressSpace::Generic)
|
||||||
.ptr_type(AddressSpace::Generic);
|
.ptr_type(AddressSpace::Generic);
|
||||||
|
|
||||||
let vmctx = trampoline_func.get_nth_param(0).unwrap();
|
let vmctx = get_vmctx_ptr_param(&trampoline_func);
|
||||||
let callee = builder
|
let callee = builder
|
||||||
.build_load(
|
.build_load(
|
||||||
builder
|
builder
|
||||||
@@ -368,32 +370,68 @@ fn generate_dynamic_trampoline<'ctx>(
|
|||||||
)
|
)
|
||||||
.into_pointer_value();
|
.into_pointer_value();
|
||||||
|
|
||||||
|
let values_ptr = builder.build_pointer_cast(values, intrinsics.i128_ptr_ty, "");
|
||||||
builder.build_call(
|
builder.build_call(
|
||||||
callee,
|
callee,
|
||||||
&[vmctx.as_basic_value_enum(), values.as_basic_value_enum()],
|
&[
|
||||||
|
vmctx.as_basic_value_enum(),
|
||||||
|
values_ptr.as_basic_value_enum(),
|
||||||
|
],
|
||||||
"",
|
"",
|
||||||
);
|
);
|
||||||
|
|
||||||
match func_sig.results() {
|
if func_sig.results().is_empty() {
|
||||||
[] => {
|
|
||||||
builder.build_return(None);
|
builder.build_return(None);
|
||||||
}
|
} else {
|
||||||
[ty] => {
|
let results = func_sig
|
||||||
|
.results()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(idx, ty)| {
|
||||||
let ptr = unsafe {
|
let ptr = unsafe {
|
||||||
builder.build_in_bounds_gep(
|
builder.build_gep(
|
||||||
values,
|
values,
|
||||||
&[intrinsics.i32_zero, intrinsics.i32_ty.const_int(0, false)],
|
&[intrinsics.i32_ty.const_int(idx.try_into().unwrap(), false)],
|
||||||
"",
|
"",
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let ptr = builder
|
let ptr = builder.build_pointer_cast(ptr, type_to_llvm_ptr(intrinsics, *ty)?, "");
|
||||||
.build_bitcast(ptr, type_to_llvm_ptr(intrinsics, *ty)?, "")
|
Ok(builder.build_load(ptr, ""))
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, CompileError>>()?;
|
||||||
|
|
||||||
|
if is_sret(func_sig)? {
|
||||||
|
let sret = trampoline_func
|
||||||
|
.get_first_param()
|
||||||
|
.unwrap()
|
||||||
.into_pointer_value();
|
.into_pointer_value();
|
||||||
let ret = builder.build_load(ptr, "");
|
let mut struct_value = sret
|
||||||
builder.build_return(Some(&ret));
|
.get_type()
|
||||||
|
.get_element_type()
|
||||||
|
.into_struct_type()
|
||||||
|
.get_undef();
|
||||||
|
for (idx, value) in results.iter().enumerate() {
|
||||||
|
let value = builder.build_bitcast(
|
||||||
|
*value,
|
||||||
|
type_to_llvm(&intrinsics, func_sig.results()[idx])?,
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
struct_value = builder
|
||||||
|
.build_insert_value(struct_value, value, idx as u32, "")
|
||||||
|
.unwrap()
|
||||||
|
.into_struct_value();
|
||||||
|
}
|
||||||
|
builder.build_store(sret, struct_value);
|
||||||
|
builder.build_return(None);
|
||||||
|
} else {
|
||||||
|
builder.build_return(Some(&pack_values_for_register_return(
|
||||||
|
&intrinsics,
|
||||||
|
&builder,
|
||||||
|
&results.as_slice(),
|
||||||
|
&trampoline_func.get_type(),
|
||||||
|
)?));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => unimplemented!("multi-value return is not yet implemented"),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,4 +5,5 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
mod imports;
|
mod imports;
|
||||||
|
mod multi_value_imports;
|
||||||
mod wast;
|
mod wast;
|
||||||
|
|||||||
204
tests/compilers/multi_value_imports.rs
Normal file
204
tests/compilers/multi_value_imports.rs
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
//! Testing the imports with different provided functions.
|
||||||
|
//! This tests checks that the provided functions (both native and
|
||||||
|
//! dynamic ones) work properly.
|
||||||
|
|
||||||
|
macro_rules! mvr_test {
|
||||||
|
($test_name:ident, $( $result_type:ty ),* ) => {
|
||||||
|
mod $test_name {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn get_module(store: &Store) -> anyhow::Result<wasmer::Module> {
|
||||||
|
let wat: String = r#"
|
||||||
|
(type $type (func (param i32) (result
|
||||||
|
"#.to_string() +
|
||||||
|
&stringify!( $( $result_type ),* ).replace(",", "").replace("(", "").replace(")", "") + &r#")))
|
||||||
|
(import "host" "callback_fn" (func $callback_fn (type $type)))
|
||||||
|
(func (export "test_call") (type $type)
|
||||||
|
get_local 0
|
||||||
|
call $callback_fn)
|
||||||
|
(func (export "test_call_indirect") (type $type)
|
||||||
|
(i32.const 1)
|
||||||
|
(call_indirect (type $type) (i32.const 0))
|
||||||
|
)
|
||||||
|
(table funcref
|
||||||
|
(elem
|
||||||
|
$callback_fn
|
||||||
|
)
|
||||||
|
)
|
||||||
|
"#.to_string();
|
||||||
|
Ok(wasmer::Module::new(&store, &wat)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn callback_fn(n: i32) -> ( $( $result_type ),* ) {
|
||||||
|
( $( <$result_type>::expected_value(n) ),* )
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn native() -> anyhow::Result<()> {
|
||||||
|
let store = get_store();
|
||||||
|
let module = get_module(&store)?;
|
||||||
|
let instance = wasmer::Instance::new(
|
||||||
|
&module,
|
||||||
|
&wasmer::imports! {
|
||||||
|
"host" => {
|
||||||
|
"callback_fn" => wasmer::Function::new(&store, callback_fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)?;
|
||||||
|
let expected_value = vec![ $( <$result_type>::expected_val(1) ),* ].into_boxed_slice();
|
||||||
|
assert_eq!(instance.exports.get_function("test_call")?.call(&[wasmer::Val::I32(1)])?,
|
||||||
|
expected_value);
|
||||||
|
assert_eq!(instance.exports.get_function("test_call_indirect")?.call(&[wasmer::Val::I32(1)])?,
|
||||||
|
expected_value);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dynamic_callback_fn(values: &[wasmer::Value]) -> Result<Vec<wasmer::Val>, wasmer::RuntimeError> {
|
||||||
|
assert_eq!(values[0], wasmer::Value::I32(1));
|
||||||
|
Ok(vec![ $( <$result_type>::expected_val(1) ),* ])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dynamic() -> anyhow::Result<()> {
|
||||||
|
let store = get_store();
|
||||||
|
let module = get_module(&store)?;
|
||||||
|
let instance = wasmer::Instance::new(
|
||||||
|
&module,
|
||||||
|
&wasmer::imports! {
|
||||||
|
"host" => {
|
||||||
|
"callback_fn" => wasmer::Function::new_dynamic(&store, &wasmer::FunctionType::new(vec![wasmer::ValType::I32], vec![ $( <$result_type>::expected_valtype() ),* ]), dynamic_callback_fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)?;
|
||||||
|
let expected_value = vec![ $( <$result_type>::expected_val(1) ),* ].into_boxed_slice();
|
||||||
|
assert_eq!(instance.exports.get_function("test_call")?.call(&[wasmer::Val::I32(1)])?,
|
||||||
|
expected_value);
|
||||||
|
assert_eq!(instance.exports.get_function("test_call_indirect")?.call(&[wasmer::Val::I32(1)])?,
|
||||||
|
expected_value);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wasmer_compilers! {
|
||||||
|
trait ExpectedExpr {
|
||||||
|
fn expected_value(n: i32) -> Self;
|
||||||
|
fn expected_val(n: i32) -> wasmer::Val;
|
||||||
|
fn expected_valtype() -> wasmer::ValType;
|
||||||
|
}
|
||||||
|
impl ExpectedExpr for i32 {
|
||||||
|
fn expected_value(n: i32) -> i32 {
|
||||||
|
n + 1
|
||||||
|
}
|
||||||
|
fn expected_val(n: i32) -> wasmer::Val {
|
||||||
|
wasmer::Val::I32(Self::expected_value(n))
|
||||||
|
}
|
||||||
|
fn expected_valtype() -> wasmer::ValType {
|
||||||
|
wasmer::ValType::I32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ExpectedExpr for i64 {
|
||||||
|
fn expected_value(n: i32) -> i64 {
|
||||||
|
n as i64 + 2i64
|
||||||
|
}
|
||||||
|
fn expected_val(n: i32) -> wasmer::Val {
|
||||||
|
wasmer::Val::I64(Self::expected_value(n))
|
||||||
|
}
|
||||||
|
fn expected_valtype() -> wasmer::ValType {
|
||||||
|
wasmer::ValType::I64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ExpectedExpr for f32 {
|
||||||
|
fn expected_value(n: i32) -> f32 {
|
||||||
|
n as f32 * 0.1
|
||||||
|
}
|
||||||
|
fn expected_val(n: i32) -> wasmer::Val {
|
||||||
|
wasmer::Val::F32(Self::expected_value(n))
|
||||||
|
}
|
||||||
|
fn expected_valtype() -> wasmer::ValType {
|
||||||
|
wasmer::ValType::F32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ExpectedExpr for f64 {
|
||||||
|
fn expected_value(n: i32) -> f64 {
|
||||||
|
n as f64 * 0.12
|
||||||
|
}
|
||||||
|
fn expected_val(n: i32) -> wasmer::Val {
|
||||||
|
wasmer::Val::F64(Self::expected_value(n))
|
||||||
|
}
|
||||||
|
fn expected_valtype() -> wasmer::ValType {
|
||||||
|
wasmer::ValType::F64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mvr_test!(test_mvr_i32_i32, i32, i32);
|
||||||
|
mvr_test!(test_mvr_i32_f32, i32, f32);
|
||||||
|
mvr_test!(test_mvr_f32_i32, f32, i32);
|
||||||
|
mvr_test!(test_mvr_f32_f32, f32, f32);
|
||||||
|
|
||||||
|
mvr_test!(test_mvr_i64_i32, i64, i32);
|
||||||
|
mvr_test!(test_mvr_i64_f32, i64, f32);
|
||||||
|
mvr_test!(test_mvr_f64_i32, f64, i32);
|
||||||
|
mvr_test!(test_mvr_f64_f32, f64, f32);
|
||||||
|
|
||||||
|
mvr_test!(test_mvr_i32_i64, i32, i64);
|
||||||
|
mvr_test!(test_mvr_f32_i64, f32, i64);
|
||||||
|
mvr_test!(test_mvr_i32_f64, i32, f64);
|
||||||
|
mvr_test!(test_mvr_f32_f64, f32, f64);
|
||||||
|
|
||||||
|
mvr_test!(test_mvr_i32_i32_i32, i32, i32, i32);
|
||||||
|
mvr_test!(test_mvr_i32_i32_f32, i32, i32, f32);
|
||||||
|
mvr_test!(test_mvr_i32_f32_i32, i32, f32, i32);
|
||||||
|
mvr_test!(test_mvr_i32_f32_f32, i32, f32, f32);
|
||||||
|
mvr_test!(test_mvr_f32_i32_i32, f32, i32, i32);
|
||||||
|
mvr_test!(test_mvr_f32_i32_f32, f32, i32, f32);
|
||||||
|
mvr_test!(test_mvr_f32_f32_i32, f32, f32, i32);
|
||||||
|
mvr_test!(test_mvr_f32_f32_f32, f32, f32, f32);
|
||||||
|
|
||||||
|
mvr_test!(test_mvr_i32_i32_i64, i32, i32, i64);
|
||||||
|
mvr_test!(test_mvr_i32_f32_i64, i32, f32, i64);
|
||||||
|
mvr_test!(test_mvr_f32_i32_i64, f32, i32, i64);
|
||||||
|
mvr_test!(test_mvr_f32_f32_i64, f32, f32, i64);
|
||||||
|
mvr_test!(test_mvr_i32_i32_f64, i32, i32, f64);
|
||||||
|
mvr_test!(test_mvr_i32_f32_f64, i32, f32, f64);
|
||||||
|
mvr_test!(test_mvr_f32_i32_f64, f32, i32, f64);
|
||||||
|
mvr_test!(test_mvr_f32_f32_f64, f32, f32, f64);
|
||||||
|
|
||||||
|
mvr_test!(test_mvr_i32_i64_i32, i32, i64, i32);
|
||||||
|
mvr_test!(test_mvr_i32_i64_f32, i32, i64, f32);
|
||||||
|
mvr_test!(test_mvr_f32_i64_i32, f32, i64, i32);
|
||||||
|
mvr_test!(test_mvr_f32_i64_f32, f32, i64, f32);
|
||||||
|
mvr_test!(test_mvr_i32_f64_i32, i32, f64, i32);
|
||||||
|
mvr_test!(test_mvr_i32_f64_f32, i32, f64, f32);
|
||||||
|
mvr_test!(test_mvr_f32_f64_i32, f32, f64, i32);
|
||||||
|
mvr_test!(test_mvr_f32_f64_f32, f32, f64, f32);
|
||||||
|
|
||||||
|
mvr_test!(test_mvr_i64_i32_i32, i64, i32, i32);
|
||||||
|
mvr_test!(test_mvr_i64_i32_f32, i64, i32, f32);
|
||||||
|
mvr_test!(test_mvr_i64_f32_i32, i64, f32, i32);
|
||||||
|
mvr_test!(test_mvr_i64_f32_f32, i64, f32, f32);
|
||||||
|
mvr_test!(test_mvr_f64_i32_i32, f64, i32, i32);
|
||||||
|
mvr_test!(test_mvr_f64_i32_f32, f64, i32, f32);
|
||||||
|
mvr_test!(test_mvr_f64_f32_i32, f64, f32, i32);
|
||||||
|
mvr_test!(test_mvr_f64_f32_f32, f64, f32, f32);
|
||||||
|
|
||||||
|
mvr_test!(test_mvr_i32_i32_i32_i32, i32, i32, i32, i32);
|
||||||
|
mvr_test!(test_mvr_i32_i32_i32_f32, i32, i32, i32, f32);
|
||||||
|
mvr_test!(test_mvr_i32_i32_f32_i32, i32, i32, f32, i32);
|
||||||
|
mvr_test!(test_mvr_i32_i32_f32_f32, i32, i32, f32, f32);
|
||||||
|
mvr_test!(test_mvr_i32_f32_i32_i32, i32, f32, i32, i32);
|
||||||
|
mvr_test!(test_mvr_i32_f32_i32_f32, i32, f32, i32, f32);
|
||||||
|
mvr_test!(test_mvr_i32_f32_f32_i32, i32, f32, f32, i32);
|
||||||
|
mvr_test!(test_mvr_i32_f32_f32_f32, i32, f32, f32, f32);
|
||||||
|
mvr_test!(test_mvr_f32_i32_i32_i32, f32, i32, i32, i32);
|
||||||
|
mvr_test!(test_mvr_f32_i32_i32_f32, f32, i32, i32, f32);
|
||||||
|
mvr_test!(test_mvr_f32_i32_f32_i32, f32, i32, f32, i32);
|
||||||
|
mvr_test!(test_mvr_f32_i32_f32_f32, f32, i32, f32, f32);
|
||||||
|
mvr_test!(test_mvr_f32_f32_i32_i32, f32, f32, i32, i32);
|
||||||
|
mvr_test!(test_mvr_f32_f32_i32_f32, f32, f32, i32, f32);
|
||||||
|
mvr_test!(test_mvr_f32_f32_f32_i32, f32, f32, f32, i32);
|
||||||
|
mvr_test!(test_mvr_f32_f32_f32_f32, f32, f32, f32, f32);
|
||||||
|
|
||||||
|
mvr_test!(test_mvr_i32_i32_i32_i32_i32, i32, i32, i32, i32, i32);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user