Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Polymorphic host functions based on dynamic trampoline generation. #1217

Merged
merged 29 commits into from
Mar 3, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
2fe6e6f
Global trampoline buffer.
losfair Feb 14, 2020
12373bb
Func::new_polymorphic
losfair Feb 14, 2020
5f4561e
Fix compilation error on Aarch64.
losfair Feb 15, 2020
7b0f7ee
Merge remote-tracking branch 'origin/master' into feature/polymorphic-v2
losfair Feb 15, 2020
ad20a00
fix(runtime-core) Use explicit `dyn` for trait objects.
Hywan Feb 17, 2020
2ee1e80
feat(runtime-core) Allow dynamic signature for polymorphic host funct…
Hywan Feb 17, 2020
644755f
Merge pull request #1225 from Hywan/feature/polymorphic-v2.1
losfair Feb 24, 2020
2020901
Merge remote-tracking branch 'origin/master' into feature/polymorphic-v2
losfair Feb 24, 2020
b67acbc
Add `ErasedFunc` for type-erased functions.
losfair Feb 24, 2020
b7c9c18
Add dynamic executable memory allocation & tests to trampolines.
losfair Feb 25, 2020
80f824e
Auto-release trampolines.
losfair Feb 25, 2020
40d823e
Merge remote-tracking branch 'origin/master' into feature/polymorphic-v2
losfair Feb 25, 2020
96d9e39
Specify imports instead of using a `*`.
losfair Feb 26, 2020
a0ea1af
Remove pub(self).
losfair Feb 26, 2020
262d431
Remove unneeded allow(dead_code).
losfair Feb 26, 2020
292e42a
Update lib/runtime-core/src/typed_func.rs
losfair Feb 26, 2020
a438a64
fold() -> sum()
losfair Feb 26, 2020
b0877b2
Add safety notice for `TrampolineBufferBuilder::remove_global`.
losfair Feb 26, 2020
eb89720
Merge remote-tracking branch 'origin/feature/polymorphic-v2' into fea…
losfair Feb 26, 2020
72e6a85
Update changelog.
losfair Feb 26, 2020
32915f0
Merge remote-tracking branch 'origin/master' into feature/polymorphic-v2
losfair Feb 27, 2020
31a72e5
Rename ErasedFunc to DynamicFunc and fix leaky `PolymorphicContext`.
losfair Feb 28, 2020
6516243
Merge remote-tracking branch 'origin/master' into feature/polymorphic-v2
losfair Feb 28, 2020
2ddf9ad
Disallow "fat" closures.
losfair Feb 28, 2020
84179db
Fix changelog.
losfair Feb 29, 2020
4012645
Fix `CodeMemory` doc comments.
losfair Feb 29, 2020
d443ad8
Remove outdated comment.
losfair Feb 29, 2020
d9e744d
Resolve review comments.
losfair Mar 3, 2020
f499dea
Merge remote-tracking branch 'origin/master' into feature/polymorphic-v2
losfair Mar 3, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions lib/runtime-core-tests/tests/imports.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down Expand Up @@ -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)))
Expand All @@ -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)
Expand Down Expand Up @@ -142,6 +154,16 @@ fn imported_functions_forms(test: &dyn Fn(&Instance)) {
Ok(n + 1)
}),

"callback_closure_polymorphic" => Func::<i32, i32, _>::new_polymorphic(
losfair marked this conversation as resolved.
Show resolved Hide resolved
Arc::new(FuncSig::new(vec![Type::I32], vec![Type::I32])),
|_, params| -> Vec<Value> {
Hywan marked this conversation as resolved.
Show resolved Hide resolved
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();
Expand Down Expand Up @@ -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,
Expand Down
20 changes: 18 additions & 2 deletions lib/runtime-core/src/loader.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand Down Expand Up @@ -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,
nlewycky marked this conversation as resolved.
Show resolved Hide resolved
-1,
0,
)
Expand All @@ -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.
losfair marked this conversation as resolved.
Show resolved Hide resolved
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)]
Expand Down
51 changes: 51 additions & 0 deletions lib/runtime-core/src/trampoline_x64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
use crate::loader::CodeMemory;
use crate::vm::Ctx;
use std::fmt;
use std::ptr::NonNull;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::{mem, slice};

lazy_static! {
Expand All @@ -29,6 +31,50 @@ lazy_static! {
mem::transmute(ptr)
}
};

static ref TRAMPOLINES: TrampBuffer = TrampBuffer::new();
}

struct TrampBuffer {
buffer: CodeMemory,
len: AtomicUsize,
}

impl TrampBuffer {
/// Creates a trampoline buffer.
fn new() -> TrampBuffer {
// Pre-allocate 64 MiB of virtual memory for code.
losfair marked this conversation as resolved.
Show resolved Hide resolved
let mem = CodeMemory::new(64 * 1048576);
mem.make_writable_executable();
TrampBuffer {
buffer: mem,
len: AtomicUsize::new(0),
}
}

/// Bump allocation. Copies `buf` to the end of this code memory.
///
/// FIXME: Proper storage recycling.
fn append(&self, buf: &[u8]) -> Option<NonNull<u8>> {
let begin = self.len.fetch_add(buf.len(), Ordering::SeqCst);
let end = begin + buf.len();

// Length overflowed. Revert and return None.
if end > self.buffer.len() {
self.len.fetch_sub(buf.len(), Ordering::SeqCst);
return None;
}

// Now we have unique ownership to `self.buffer[begin..end]`.
let slice = unsafe {
std::slice::from_raw_parts_mut(
self.buffer.get_backing_ptr().offset(begin as _),
buf.len(),
)
};
slice.copy_from_slice(buf);
Some(NonNull::new(slice.as_mut_ptr()).unwrap())
}
}

/// An opaque type for pointers to a callable memory location.
Expand Down Expand Up @@ -219,6 +265,11 @@ impl TrampolineBufferBuilder {
idx
}

/// Appends to the global trampoline buffer.
pub fn append_global(self) -> Option<NonNull<u8>> {
TRAMPOLINES.append(&self.code)
}

/// Consumes the builder and builds the trampoline buffer.
pub fn build(self) -> TrampolineBuffer {
get_context(); // ensure lazy initialization is completed
Expand Down
73 changes: 73 additions & 0 deletions lib/runtime-core/src/typed_func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,79 @@ where
_phantom: PhantomData,
}
}

/// Creates a polymorphic function.
#[allow(unused_variables)]
#[cfg(all(unix, target_arch = "x86_64"))]
syrusakbary marked this conversation as resolved.
Show resolved Hide resolved
pub fn new_polymorphic<F>(signature: Arc<FuncSig>, func: F) -> Func<'a, Args, Rets, Host>
where
F: Fn(&mut vm::Ctx, &[crate::types::Value]) -> Vec<crate::types::Value> + 'static,
{
use crate::trampoline_x64::*;
losfair marked this conversation as resolved.
Show resolved Hide resolved
use crate::types::Value;
use std::convert::TryFrom;

losfair marked this conversation as resolved.
Show resolved Hide resolved
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")
losfair marked this conversation as resolved.
Show resolved Hide resolved
}
}
})
.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>
Expand Down
4 changes: 2 additions & 2 deletions lib/runtime-core/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down