Func::new_polymorphic

This commit is contained in:
losfair
2020-02-15 01:31:49 +08:00
parent 2fe6e6f039
commit 12373bb872
4 changed files with 122 additions and 7 deletions

View File

@ -1,6 +1,13 @@
use std::sync::Arc;
use wasmer_runtime_core::{
compile_with, error::RuntimeError, imports, memory::Memory, typed_func::Func,
types::MemoryDescriptor, units::Pages, vm, Instance,
compile_with,
error::RuntimeError,
imports,
memory::Memory,
typed_func::Func,
types::{FuncSig, MemoryDescriptor, Type, Value},
units::Pages,
vm, Instance,
};
use wasmer_runtime_core_tests::{get_compiler, wat2wasm};
@ -68,6 +75,7 @@ fn imported_functions_forms(test: &dyn Fn(&Instance)) {
(import "env" "memory" (memory 1 1))
(import "env" "callback_fn" (func $callback_fn (type $type)))
(import "env" "callback_closure" (func $callback_closure (type $type)))
(import "env" "callback_closure_polymorphic" (func $callback_closure_polymorphic (type $type)))
(import "env" "callback_closure_with_env" (func $callback_closure_with_env (type $type)))
(import "env" "callback_fn_with_vmctx" (func $callback_fn_with_vmctx (type $type)))
(import "env" "callback_closure_with_vmctx" (func $callback_closure_with_vmctx (type $type)))
@ -86,6 +94,10 @@ fn imported_functions_forms(test: &dyn Fn(&Instance)) {
get_local 0
call $callback_closure)
(func (export "function_closure_polymorphic") (type $type)
get_local 0
call $callback_closure_polymorphic)
(func (export "function_closure_with_env") (type $type)
get_local 0
call $callback_closure_with_env)
@ -142,6 +154,16 @@ fn imported_functions_forms(test: &dyn Fn(&Instance)) {
Ok(n + 1)
}),
"callback_closure_polymorphic" => Func::<i32, i32, _>::new_polymorphic(
Arc::new(FuncSig::new(vec![Type::I32], vec![Type::I32])),
|_, params| -> Vec<Value> {
match params[0] {
Value::I32(x) => vec![Value::I32(x + 1)],
_ => unreachable!()
}
}
),
// Closure with a captured environment (a single variable + an instance of `Memory`).
"callback_closure_with_env" => Func::new(move |n: i32| -> Result<i32, ()> {
let shift_ = shift + memory.view::<i32>()[0].get();
@ -236,6 +258,11 @@ macro_rules! test {
test!(test_fn, function_fn, Ok(2));
test!(test_closure, function_closure, Ok(2));
test!(
test_closure_polymorphic,
function_closure_polymorphic,
Ok(2)
);
test!(
test_closure_with_env,
function_closure_with_env,

View File

@ -1,7 +1,9 @@
//! The loader module functions are used to load an instance.
use crate::{backend::RunnableModule, module::ModuleInfo, types::Type, types::Value, vm::Ctx};
#[cfg(unix)]
use libc::{mmap, mprotect, munmap, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_READ, PROT_WRITE};
use libc::{
mmap, mprotect, munmap, MAP_ANON, MAP_NORESERVE, MAP_PRIVATE, PROT_EXEC, PROT_READ, PROT_WRITE,
};
use std::{
fmt::Debug,
ops::{Deref, DerefMut},
@ -169,7 +171,7 @@ impl CodeMemory {
std::ptr::null_mut(),
size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON,
MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
-1,
0,
)
@ -196,6 +198,20 @@ impl CodeMemory {
panic!("cannot set code memory to writable");
}
}
/// Makes this code memory both writable and executable.
///
/// Avoid using this if a combination `make_executable` and `make_writable` can be used.
pub fn make_writable_executable(&self) {
if unsafe { mprotect(self.ptr as _, self.size, PROT_READ | PROT_WRITE | PROT_EXEC) } != 0 {
panic!("cannot set code memory to writable and executable");
}
}
/// Returns the backing pointer of this code memory.
pub fn get_backing_ptr(&self) -> *mut u8 {
self.ptr
}
}
#[cfg(unix)]

View File

@ -4,7 +4,7 @@ use crate::{
error::RuntimeError,
export::{Context, Export, FuncPointer},
import::IsExport,
types::{FuncSig, NativeWasmType, Type, WasmExternType},
types::{FuncSig, NativeWasmType, Type, Value, WasmExternType},
vm,
};
use std::{
@ -240,6 +240,78 @@ where
_phantom: PhantomData,
}
}
/// Creates a polymorphic function.
#[allow(unused_variables)]
#[cfg(all(unix, target_arch = "x86_64"))]
pub fn new_polymorphic<F>(signature: Arc<FuncSig>, func: F) -> Func<'a, Args, Rets, Host>
where
F: Fn(&mut vm::Ctx, &[Value]) -> Vec<Value> + 'static,
{
use crate::trampoline_x64::*;
use std::convert::TryFrom;
struct PolymorphicContext {
arg_types: Vec<Type>,
func: Box<Fn(&mut vm::Ctx, &[Value]) -> Vec<Value>>,
}
unsafe extern "C" fn enter_host_polymorphic(
ctx: *const CallContext,
args: *const u64,
) -> u64 {
let ctx = &*(ctx as *const PolymorphicContext);
let vmctx = &mut *(*args.offset(0) as *mut vm::Ctx);
let args: Vec<Value> = ctx
.arg_types
.iter()
.enumerate()
.map(|(i, t)| {
let i = i + 1; // skip vmctx
match *t {
Type::I32 => Value::I32(*args.offset(i as _) as i32),
Type::I64 => Value::I64(*args.offset(i as _) as i64),
Type::F32 => Value::F32(f32::from_bits(*args.offset(i as _) as u32)),
Type::F64 => Value::F64(f64::from_bits(*args.offset(i as _) as u64)),
Type::V128 => {
panic!("enter_host_polymorphic: 128-bit types are not supported")
}
}
})
.collect();
let rets = (ctx.func)(vmctx, &args);
if rets.len() == 0 {
0
} else if rets.len() == 1 {
u64::try_from(rets[0].to_u128()).expect(
"128-bit return value from polymorphic host functions is not yet supported",
)
} else {
panic!(
"multiple return values from polymorphic host functions is not yet supported"
);
}
}
let mut builder = TrampolineBufferBuilder::new();
let ctx = Box::new(PolymorphicContext {
arg_types: signature.params().to_vec(),
func: Box::new(func),
});
builder.add_callinfo_trampoline(
enter_host_polymorphic,
Box::into_raw(ctx) as *const _,
(signature.params().len() + 1) as u32, // +vmctx
);
let ptr = builder
.append_global()
.expect("cannot bump-allocate global trampoline memory");
Func {
inner: Host(()),
func: ptr.cast::<vm::Func>(),
func_env: None,
vmctx: ptr::null_mut(),
_phantom: PhantomData,
}
}
}
impl<'a, Args, Rets, Inner> Func<'a, Args, Rets, Inner>

View File

@ -545,13 +545,13 @@ impl Ctx {
/// `typed_func` module within the `wrap` functions, to wrap imported
/// functions.
#[repr(transparent)]
pub struct Func(pub(self) *mut c_void);
pub struct Func(*mut c_void);
/// Represents a function environment pointer, like a captured
/// environment of a closure. It is mostly used in the `typed_func`
/// module within the `wrap` functions, to wrap imported functions.
#[repr(transparent)]
pub struct FuncEnv(pub(self) *mut c_void);
pub struct FuncEnv(*mut c_void);
/// Represents a function context. It is used by imported functions
/// only.