Initial reorg.

This is largely the same as #305, but updated for the current tree.
This commit is contained in:
Dan Gohman
2019-11-07 17:11:06 -08:00
parent 2c69546a24
commit 22641de629
351 changed files with 52 additions and 52 deletions

155
crates/api/src/callable.rs Normal file
View File

@@ -0,0 +1,155 @@
use crate::r#ref::HostRef;
use crate::runtime::Store;
use crate::trap::Trap;
use crate::types::FuncType;
use crate::values::Val;
use alloc::rc::Rc;
use alloc::vec::Vec;
use crate::trampoline::generate_func_export;
use cranelift_codegen::ir;
use wasmtime_jit::InstanceHandle;
use wasmtime_runtime::Export;
pub trait Callable {
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), HostRef<Trap>>;
}
pub(crate) trait WrappedCallable {
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), HostRef<Trap>>;
fn signature(&self) -> &ir::Signature {
match self.wasmtime_export() {
Export::Function { signature, .. } => signature,
_ => panic!("unexpected export type in Callable"),
}
}
fn wasmtime_handle(&self) -> &InstanceHandle;
fn wasmtime_export(&self) -> &Export;
}
pub(crate) struct WasmtimeFn {
store: HostRef<Store>,
instance: InstanceHandle,
export: Export,
}
impl WasmtimeFn {
pub fn new(store: &HostRef<Store>, instance: InstanceHandle, export: Export) -> WasmtimeFn {
WasmtimeFn {
store: store.clone(),
instance,
export,
}
}
}
impl WrappedCallable for WasmtimeFn {
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), HostRef<Trap>> {
use core::cmp::max;
use core::{mem, ptr};
let (vmctx, body, signature) = match self.wasmtime_export() {
Export::Function {
vmctx,
address,
signature,
} => (*vmctx, *address, signature.clone()),
_ => panic!("unexpected export type in Callable"),
};
let value_size = mem::size_of::<u64>();
let mut values_vec: Vec<u64> = vec![0; max(params.len(), results.len())];
// Store the argument values into `values_vec`.
for (index, arg) in params.iter().enumerate() {
unsafe {
let ptr = values_vec.as_mut_ptr().add(index);
match arg {
Val::I32(x) => ptr::write(ptr as *mut i32, *x),
Val::I64(x) => ptr::write(ptr as *mut i64, *x),
Val::F32(x) => ptr::write(ptr as *mut u32, *x),
Val::F64(x) => ptr::write(ptr as *mut u64, *x),
_ => unimplemented!("WasmtimeFn arg"),
}
}
}
// Get the trampoline to call for this function.
let exec_code_buf = self
.store
.borrow_mut()
.context()
.compiler()
.get_published_trampoline(body, &signature, value_size)
.map_err(|_| HostRef::new(Trap::fake()))?; //was ActionError::Setup)?;
// Call the trampoline.
if let Err(message) = unsafe {
wasmtime_runtime::wasmtime_call_trampoline(
vmctx,
exec_code_buf,
values_vec.as_mut_ptr() as *mut u8,
)
} {
return Err(HostRef::new(Trap::new(message)));
}
// Load the return values out of `values_vec`.
for (index, abi_param) in signature.returns.iter().enumerate() {
unsafe {
let ptr = values_vec.as_ptr().add(index);
results[index] = match abi_param.value_type {
ir::types::I32 => Val::I32(ptr::read(ptr as *const i32)),
ir::types::I64 => Val::I64(ptr::read(ptr as *const i64)),
ir::types::F32 => Val::F32(ptr::read(ptr as *const u32)),
ir::types::F64 => Val::F64(ptr::read(ptr as *const u64)),
other => panic!("unsupported value type {:?}", other),
}
}
}
Ok(())
}
fn wasmtime_handle(&self) -> &InstanceHandle {
&self.instance
}
fn wasmtime_export(&self) -> &Export {
&self.export
}
}
pub struct NativeCallable {
callable: Rc<dyn Callable + 'static>,
instance: InstanceHandle,
export: Export,
}
impl NativeCallable {
pub(crate) fn new(
callable: Rc<dyn Callable + 'static>,
ft: &FuncType,
store: &HostRef<Store>,
) -> Self {
let (instance, export) =
generate_func_export(ft, &callable, store).expect("generated func");
NativeCallable {
callable,
instance,
export,
}
}
}
impl WrappedCallable for NativeCallable {
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), HostRef<Trap>> {
self.callable.call(params, results)
}
fn wasmtime_handle(&self) -> &InstanceHandle {
&self.instance
}
fn wasmtime_export(&self) -> &Export {
&self.export
}
}

68
crates/api/src/context.rs Normal file
View File

@@ -0,0 +1,68 @@
use alloc::rc::Rc;
use core::cell::{RefCell, RefMut};
use core::hash::{Hash, Hasher};
use wasmtime_jit::{CompilationStrategy, Compiler, Features};
use cranelift_codegen::settings;
#[derive(Clone)]
pub struct Context {
compiler: Rc<RefCell<Compiler>>,
features: Features,
debug_info: bool,
}
impl Context {
pub fn new(compiler: Compiler, features: Features, debug_info: bool) -> Context {
Context {
compiler: Rc::new(RefCell::new(compiler)),
features,
debug_info,
}
}
pub fn create(
flags: settings::Flags,
features: Features,
debug_info: bool,
strategy: CompilationStrategy,
) -> Context {
Context::new(create_compiler(flags, strategy), features, debug_info)
}
pub(crate) fn debug_info(&self) -> bool {
self.debug_info
}
pub(crate) fn compiler(&mut self) -> RefMut<Compiler> {
self.compiler.borrow_mut()
}
}
impl Hash for Context {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
self.compiler.as_ptr().hash(state)
}
}
impl Eq for Context {}
impl PartialEq for Context {
fn eq(&self, other: &Context) -> bool {
Rc::ptr_eq(&self.compiler, &other.compiler)
}
}
pub(crate) fn create_compiler(flags: settings::Flags, strategy: CompilationStrategy) -> Compiler {
let isa = {
let isa_builder =
cranelift_native::builder().expect("host machine is not a supported target");
isa_builder.finish(flags)
};
Compiler::new(isa, strategy)
}

490
crates/api/src/externals.rs Normal file
View File

@@ -0,0 +1,490 @@
use crate::callable::{Callable, NativeCallable, WasmtimeFn, WrappedCallable};
use crate::r#ref::{AnyRef, HostRef};
use crate::runtime::Store;
use crate::trampoline::{generate_global_export, generate_memory_export, generate_table_export};
use crate::trap::Trap;
use crate::types::{ExternType, FuncType, GlobalType, MemoryType, TableType, ValType};
use crate::values::{from_checked_anyfunc, into_checked_anyfunc, Val};
use alloc::boxed::Box;
use alloc::rc::Rc;
use core::result::Result;
use core::slice;
use wasmtime_runtime::InstanceHandle;
// Externals
#[derive(Clone)]
pub enum Extern {
Func(HostRef<Func>),
Global(HostRef<Global>),
Table(HostRef<Table>),
Memory(HostRef<Memory>),
}
impl Extern {
pub fn func(&self) -> Option<&HostRef<Func>> {
match self {
Extern::Func(func) => Some(func),
_ => None,
}
}
pub fn global(&self) -> Option<&HostRef<Global>> {
match self {
Extern::Global(global) => Some(global),
_ => None,
}
}
pub fn table(&self) -> Option<&HostRef<Table>> {
match self {
Extern::Table(table) => Some(table),
_ => None,
}
}
pub fn memory(&self) -> Option<&HostRef<Memory>> {
match self {
Extern::Memory(memory) => Some(memory),
_ => None,
}
}
pub fn r#type(&self) -> ExternType {
match self {
Extern::Func(ft) => ExternType::ExternFunc(ft.borrow().r#type().clone()),
Extern::Memory(ft) => ExternType::ExternMemory(ft.borrow().r#type().clone()),
Extern::Table(tt) => ExternType::ExternTable(tt.borrow().r#type().clone()),
Extern::Global(gt) => ExternType::ExternGlobal(gt.borrow().r#type().clone()),
}
}
pub(crate) fn get_wasmtime_export(&mut self) -> wasmtime_runtime::Export {
match self {
Extern::Func(f) => f.borrow().wasmtime_export().clone(),
Extern::Global(g) => g.borrow().wasmtime_export().clone(),
Extern::Memory(m) => m.borrow().wasmtime_export().clone(),
Extern::Table(t) => t.borrow().wasmtime_export().clone(),
}
}
pub(crate) fn from_wasmtime_export(
store: &HostRef<Store>,
instance_handle: InstanceHandle,
export: wasmtime_runtime::Export,
) -> Extern {
match export {
wasmtime_runtime::Export::Function { .. } => Extern::Func(HostRef::new(
Func::from_wasmtime_function(export, store, instance_handle),
)),
wasmtime_runtime::Export::Memory { .. } => Extern::Memory(HostRef::new(
Memory::from_wasmtime_memory(export, store, instance_handle),
)),
wasmtime_runtime::Export::Global { .. } => {
Extern::Global(HostRef::new(Global::from_wasmtime_global(export, store)))
}
wasmtime_runtime::Export::Table { .. } => Extern::Table(HostRef::new(
Table::from_wasmtime_table(export, store, instance_handle),
)),
}
}
}
impl From<HostRef<Func>> for Extern {
fn from(r: HostRef<Func>) -> Self {
Extern::Func(r)
}
}
impl From<HostRef<Global>> for Extern {
fn from(r: HostRef<Global>) -> Self {
Extern::Global(r)
}
}
impl From<HostRef<Memory>> for Extern {
fn from(r: HostRef<Memory>) -> Self {
Extern::Memory(r)
}
}
impl From<HostRef<Table>> for Extern {
fn from(r: HostRef<Table>) -> Self {
Extern::Table(r)
}
}
pub struct Func {
_store: HostRef<Store>,
callable: Rc<dyn WrappedCallable + 'static>,
r#type: FuncType,
}
impl Func {
pub fn new(store: &HostRef<Store>, ty: FuncType, callable: Rc<dyn Callable + 'static>) -> Self {
let callable = Rc::new(NativeCallable::new(callable, &ty, &store));
Func::from_wrapped(store, ty, callable)
}
fn from_wrapped(
store: &HostRef<Store>,
r#type: FuncType,
callable: Rc<dyn WrappedCallable + 'static>,
) -> Func {
Func {
_store: store.clone(),
callable,
r#type,
}
}
pub fn r#type(&self) -> &FuncType {
&self.r#type
}
pub fn param_arity(&self) -> usize {
self.r#type.params().len()
}
pub fn result_arity(&self) -> usize {
self.r#type.results().len()
}
pub fn call(&self, params: &[Val]) -> Result<Box<[Val]>, HostRef<Trap>> {
let mut results = vec![Val::default(); self.result_arity()];
self.callable.call(params, &mut results)?;
Ok(results.into_boxed_slice())
}
pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::Export {
self.callable.wasmtime_export()
}
pub(crate) fn from_wasmtime_function(
export: wasmtime_runtime::Export,
store: &HostRef<Store>,
instance_handle: InstanceHandle,
) -> Self {
let ty = if let wasmtime_runtime::Export::Function { signature, .. } = &export {
FuncType::from_cranelift_signature(signature.clone())
} else {
panic!("expected function export")
};
let callable = WasmtimeFn::new(store, instance_handle, export.clone());
Func::from_wrapped(store, ty, Rc::new(callable))
}
}
impl core::fmt::Debug for Func {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Func")
}
}
pub struct Global {
_store: HostRef<Store>,
r#type: GlobalType,
wasmtime_export: wasmtime_runtime::Export,
#[allow(dead_code)]
wasmtime_state: Option<crate::trampoline::GlobalState>,
}
impl Global {
pub fn new(store: &HostRef<Store>, r#type: GlobalType, val: Val) -> Global {
let (wasmtime_export, wasmtime_state) =
generate_global_export(&r#type, val).expect("generated global");
Global {
_store: store.clone(),
r#type,
wasmtime_export,
wasmtime_state: Some(wasmtime_state),
}
}
pub fn r#type(&self) -> &GlobalType {
&self.r#type
}
fn wasmtime_global_definition(&self) -> *mut wasmtime_runtime::VMGlobalDefinition {
match self.wasmtime_export {
wasmtime_runtime::Export::Global { definition, .. } => definition,
_ => panic!("global definition not found"),
}
}
pub fn get(&self) -> Val {
let definition = unsafe { &mut *self.wasmtime_global_definition() };
unsafe {
match self.r#type().content() {
ValType::I32 => Val::from(*definition.as_i32()),
ValType::I64 => Val::from(*definition.as_i64()),
ValType::F32 => Val::from_f32_bits(*definition.as_u32()),
ValType::F64 => Val::from_f64_bits(*definition.as_u64()),
_ => unimplemented!("Global::get for {:?}", self.r#type().content()),
}
}
}
pub fn set(&mut self, val: Val) {
if val.r#type() != *self.r#type().content() {
panic!(
"global of type {:?} cannot be set to {:?}",
self.r#type().content(),
val.r#type()
);
}
let definition = unsafe { &mut *self.wasmtime_global_definition() };
unsafe {
match val {
Val::I32(i) => *definition.as_i32_mut() = i,
Val::I64(i) => *definition.as_i64_mut() = i,
Val::F32(f) => *definition.as_u32_mut() = f,
Val::F64(f) => *definition.as_u64_mut() = f,
_ => unimplemented!("Global::set for {:?}", val.r#type()),
}
}
}
pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::Export {
&self.wasmtime_export
}
pub(crate) fn from_wasmtime_global(
export: wasmtime_runtime::Export,
store: &HostRef<Store>,
) -> Global {
let global = if let wasmtime_runtime::Export::Global { ref global, .. } = export {
global
} else {
panic!("wasmtime export is not memory")
};
let ty = GlobalType::from_cranelift_global(global.clone());
Global {
_store: store.clone(),
r#type: ty,
wasmtime_export: export,
wasmtime_state: None,
}
}
}
pub struct Table {
store: HostRef<Store>,
r#type: TableType,
wasmtime_handle: InstanceHandle,
wasmtime_export: wasmtime_runtime::Export,
}
fn get_table_item(
handle: &InstanceHandle,
store: &HostRef<Store>,
table_index: cranelift_wasm::DefinedTableIndex,
item_index: u32,
) -> Val {
if let Some(item) = handle.table_get(table_index, item_index) {
from_checked_anyfunc(item, store)
} else {
AnyRef::null().into()
}
}
fn set_table_item(
handle: &mut InstanceHandle,
store: &HostRef<Store>,
table_index: cranelift_wasm::DefinedTableIndex,
item_index: u32,
val: Val,
) -> bool {
let item = into_checked_anyfunc(val, store);
if let Some(item_ref) = handle.table_get_mut(table_index, item_index) {
*item_ref = item;
true
} else {
false
}
}
impl Table {
pub fn new(store: &HostRef<Store>, r#type: TableType, init: Val) -> Table {
match r#type.element() {
ValType::FuncRef => (),
_ => panic!("table is not for funcref"),
}
let (mut wasmtime_handle, wasmtime_export) =
generate_table_export(&r#type).expect("generated table");
// Initialize entries with the init value.
match wasmtime_export {
wasmtime_runtime::Export::Table { definition, .. } => {
let index = wasmtime_handle.table_index(unsafe { &*definition });
let len = unsafe { (*definition).current_elements };
for i in 0..len {
let _success =
set_table_item(&mut wasmtime_handle, store, index, i, init.clone());
assert!(_success);
}
}
_ => panic!("global definition not found"),
}
Table {
store: store.clone(),
r#type,
wasmtime_handle,
wasmtime_export,
}
}
pub fn r#type(&self) -> &TableType {
&self.r#type
}
fn wasmtime_table_index(&self) -> cranelift_wasm::DefinedTableIndex {
match self.wasmtime_export {
wasmtime_runtime::Export::Table { definition, .. } => {
self.wasmtime_handle.table_index(unsafe { &*definition })
}
_ => panic!("global definition not found"),
}
}
pub fn get(&self, index: u32) -> Val {
let table_index = self.wasmtime_table_index();
get_table_item(&self.wasmtime_handle, &self.store, table_index, index)
}
pub fn set(&self, index: u32, val: Val) -> bool {
let table_index = self.wasmtime_table_index();
let mut wasmtime_handle = self.wasmtime_handle.clone();
set_table_item(&mut wasmtime_handle, &self.store, table_index, index, val)
}
pub fn size(&self) -> u32 {
match self.wasmtime_export {
wasmtime_runtime::Export::Table { definition, .. } => unsafe {
(*definition).current_elements
},
_ => panic!("global definition not found"),
}
}
pub fn grow(&mut self, delta: u32, init: Val) -> bool {
let index = self.wasmtime_table_index();
if let Some(len) = self.wasmtime_handle.table_grow(index, delta) {
let mut wasmtime_handle = self.wasmtime_handle.clone();
for i in 0..delta {
let i = len - (delta - i);
let _success =
set_table_item(&mut wasmtime_handle, &self.store, index, i, init.clone());
assert!(_success);
}
true
} else {
false
}
}
pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::Export {
&self.wasmtime_export
}
pub(crate) fn from_wasmtime_table(
export: wasmtime_runtime::Export,
store: &HostRef<Store>,
instance_handle: wasmtime_runtime::InstanceHandle,
) -> Table {
let table = if let wasmtime_runtime::Export::Table { ref table, .. } = export {
table
} else {
panic!("wasmtime export is not table")
};
let ty = TableType::from_cranelift_table(table.table.clone());
Table {
store: store.clone(),
r#type: ty,
wasmtime_handle: instance_handle,
wasmtime_export: export,
}
}
}
pub struct Memory {
_store: HostRef<Store>,
r#type: MemoryType,
wasmtime_handle: InstanceHandle,
wasmtime_export: wasmtime_runtime::Export,
}
impl Memory {
pub fn new(store: &HostRef<Store>, r#type: MemoryType) -> Memory {
let (wasmtime_handle, wasmtime_export) =
generate_memory_export(&r#type).expect("generated memory");
Memory {
_store: store.clone(),
r#type,
wasmtime_handle,
wasmtime_export,
}
}
pub fn r#type(&self) -> &MemoryType {
&self.r#type
}
fn wasmtime_memory_definition(&self) -> *mut wasmtime_runtime::VMMemoryDefinition {
match self.wasmtime_export {
wasmtime_runtime::Export::Memory { definition, .. } => definition,
_ => panic!("memory definition not found"),
}
}
// Marked unsafe due to posibility that wasmtime can resize internal memory
// from other threads.
pub unsafe fn data(&self) -> &mut [u8] {
let definition = &*self.wasmtime_memory_definition();
slice::from_raw_parts_mut(definition.base, definition.current_length)
}
pub fn data_ptr(&self) -> *mut u8 {
unsafe { (*self.wasmtime_memory_definition()).base }
}
pub fn data_size(&self) -> usize {
unsafe { (*self.wasmtime_memory_definition()).current_length }
}
pub fn size(&self) -> u32 {
(self.data_size() / wasmtime_environ::WASM_PAGE_SIZE as usize) as u32
}
pub fn grow(&mut self, delta: u32) -> bool {
match self.wasmtime_export {
wasmtime_runtime::Export::Memory { definition, .. } => {
let definition = unsafe { &(*definition) };
let index = self.wasmtime_handle.memory_index(definition);
self.wasmtime_handle.memory_grow(index, delta).is_some()
}
_ => panic!("memory definition not found"),
}
}
pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::Export {
&self.wasmtime_export
}
pub(crate) fn from_wasmtime_memory(
export: wasmtime_runtime::Export,
store: &HostRef<Store>,
instance_handle: wasmtime_runtime::InstanceHandle,
) -> Memory {
let memory = if let wasmtime_runtime::Export::Memory { ref memory, .. } = export {
memory
} else {
panic!("wasmtime export is not memory")
};
let ty = MemoryType::from_cranelift_memory(memory.memory.clone());
Memory {
_store: store.clone(),
r#type: ty,
wasmtime_handle: instance_handle,
wasmtime_export: export,
}
}
}

148
crates/api/src/instance.rs Normal file
View File

@@ -0,0 +1,148 @@
use crate::context::Context;
use crate::externals::Extern;
use crate::module::Module;
use crate::r#ref::HostRef;
use crate::runtime::Store;
use crate::{HashMap, HashSet};
use alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::rc::Rc;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use anyhow::Result;
use core::cell::RefCell;
use wasmtime_jit::{instantiate, Resolver};
use wasmtime_runtime::{Export, InstanceHandle};
struct SimpleResolver {
imports: Vec<(String, String, Extern)>,
}
impl Resolver for SimpleResolver {
fn resolve(&mut self, name: &str, field: &str) -> Option<Export> {
// TODO speedup lookup
self.imports
.iter_mut()
.find(|(n, f, _)| name == n && field == f)
.map(|(_, _, e)| e.get_wasmtime_export())
}
}
pub fn instantiate_in_context(
data: &[u8],
imports: Vec<(String, String, Extern)>,
mut context: Context,
exports: Rc<RefCell<HashMap<String, Option<wasmtime_runtime::Export>>>>,
) -> Result<(InstanceHandle, HashSet<Context>)> {
let mut contexts = HashSet::new();
let debug_info = context.debug_info();
let mut resolver = SimpleResolver { imports };
let instance = instantiate(
&mut context.compiler(),
data,
&mut resolver,
exports,
debug_info,
)?;
contexts.insert(context);
Ok((instance, contexts))
}
#[derive(Clone)]
pub struct Instance {
instance_handle: InstanceHandle,
// We need to keep CodeMemory alive.
contexts: HashSet<Context>,
exports: Box<[Extern]>,
}
impl Instance {
pub fn new(
store: &HostRef<Store>,
module: &HostRef<Module>,
externs: &[Extern],
) -> Result<Instance> {
let context = store.borrow_mut().context().clone();
let exports = store.borrow_mut().global_exports().clone();
let imports = module
.borrow()
.imports()
.iter()
.zip(externs.iter())
.map(|(i, e)| (i.module().to_string(), i.name().to_string(), e.clone()))
.collect::<Vec<_>>();
let (mut instance_handle, contexts) =
instantiate_in_context(module.borrow().binary(), imports, context, exports)?;
let exports = {
let module = module.borrow();
let mut exports = Vec::with_capacity(module.exports().len());
for export in module.exports() {
let name = export.name().to_string();
let export = instance_handle.lookup(&name).expect("export");
exports.push(Extern::from_wasmtime_export(
store,
instance_handle.clone(),
export,
));
}
exports.into_boxed_slice()
};
Ok(Instance {
instance_handle,
contexts,
exports,
})
}
pub fn exports(&self) -> &[Extern] {
&self.exports
}
pub fn from_handle(
store: &HostRef<Store>,
instance_handle: InstanceHandle,
) -> Result<(Instance, HashMap<String, usize>)> {
let contexts = HashSet::new();
let mut exports = Vec::new();
let mut export_names_map = HashMap::new();
let mut mutable = instance_handle.clone();
for (name, _) in instance_handle.clone().exports() {
let export = mutable.lookup(name).expect("export");
if let wasmtime_runtime::Export::Function { signature, .. } = &export {
// HACK ensure all handles, instantiated outside Store, present in
// the store's SignatureRegistry, e.g. WASI instances that are
// imported into this store using the from_handle() method.
let _ = store.borrow_mut().register_cranelift_signature(signature);
}
export_names_map.insert(name.to_owned(), exports.len());
exports.push(Extern::from_wasmtime_export(
store,
instance_handle.clone(),
export.clone(),
));
}
Ok((
Instance {
instance_handle,
contexts,
exports: exports.into_boxed_slice(),
},
export_names_map,
))
}
pub fn handle(&self) -> &InstanceHandle {
&self.instance_handle
}
pub fn get_wasmtime_memory(&self) -> Option<wasmtime_runtime::Export> {
let mut instance_handle = self.instance_handle.clone();
instance_handle.lookup("memory")
}
}

35
crates/api/src/lib.rs Normal file
View File

@@ -0,0 +1,35 @@
//! Wasmtime embed API. Based on wasm-c-api.
#![cfg_attr(not(feature = "std"), no_std)]
mod callable;
mod context;
mod externals;
mod instance;
mod module;
mod r#ref;
mod runtime;
mod trampoline;
mod trap;
mod types;
mod values;
pub mod wasm;
#[macro_use]
extern crate alloc;
pub use crate::callable::Callable;
pub use crate::externals::*;
pub use crate::instance::Instance;
pub use crate::module::Module;
pub use crate::r#ref::{AnyRef, HostInfo, HostRef};
pub use crate::runtime::{Config, Engine, Store};
pub use crate::trap::Trap;
pub use crate::types::*;
pub use crate::values::*;
#[cfg(not(feature = "std"))]
use hashbrown::{hash_map, HashMap, HashSet};
#[cfg(feature = "std")]
use std::collections::{hash_map, HashMap, HashSet};

206
crates/api/src/module.rs Normal file
View File

@@ -0,0 +1,206 @@
use crate::r#ref::HostRef;
use crate::runtime::Store;
use crate::types::{
ExportType, ExternType, FuncType, GlobalType, ImportType, Limits, MemoryType, Mutability,
TableType, ValType,
};
use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
use anyhow::Result;
use wasmparser::{validate, ExternalKind, ImportSectionEntryType, ModuleReader, SectionCode};
fn into_memory_type(mt: wasmparser::MemoryType) -> MemoryType {
assert!(!mt.shared);
MemoryType::new(Limits::new(
mt.limits.initial,
mt.limits.maximum.unwrap_or(::core::u32::MAX),
))
}
fn into_global_type(gt: &wasmparser::GlobalType) -> GlobalType {
let mutability = if gt.mutable {
Mutability::Var
} else {
Mutability::Const
};
GlobalType::new(into_valtype(&gt.content_type), mutability)
}
fn into_valtype(ty: &wasmparser::Type) -> ValType {
use wasmparser::Type::*;
match ty {
I32 => ValType::I32,
I64 => ValType::I64,
F32 => ValType::F32,
F64 => ValType::F64,
V128 => ValType::V128,
AnyFunc => ValType::FuncRef,
AnyRef => ValType::AnyRef,
_ => unimplemented!("types in into_valtype"),
}
}
fn into_func_type(mt: wasmparser::FuncType) -> FuncType {
assert!(mt.form == wasmparser::Type::Func);
let params = mt.params.iter().map(into_valtype).collect::<Vec<_>>();
let returns = mt.returns.iter().map(into_valtype).collect::<Vec<_>>();
FuncType::new(params.into_boxed_slice(), returns.into_boxed_slice())
}
fn into_table_type(tt: wasmparser::TableType) -> TableType {
assert!(
tt.element_type == wasmparser::Type::AnyFunc || tt.element_type == wasmparser::Type::AnyRef
);
let ty = into_valtype(&tt.element_type);
let limits = Limits::new(
tt.limits.initial,
tt.limits.maximum.unwrap_or(::core::u32::MAX),
);
TableType::new(ty, limits)
}
fn read_imports_and_exports(binary: &[u8]) -> Result<(Box<[ImportType]>, Box<[ExportType]>)> {
let mut reader = ModuleReader::new(binary)?;
let mut imports = Vec::new();
let mut exports = Vec::new();
let mut memories = Vec::new();
let mut tables = Vec::new();
let mut func_sig = Vec::new();
let mut sigs = Vec::new();
let mut globals = Vec::new();
while !reader.eof() {
let section = reader.read()?;
match section.code {
SectionCode::Memory => {
let section = section.get_memory_section_reader()?;
memories.reserve_exact(section.get_count() as usize);
for entry in section {
memories.push(into_memory_type(entry?));
}
}
SectionCode::Type => {
let section = section.get_type_section_reader()?;
sigs.reserve_exact(section.get_count() as usize);
for entry in section {
sigs.push(into_func_type(entry?));
}
}
SectionCode::Function => {
let section = section.get_function_section_reader()?;
func_sig.reserve_exact(section.get_count() as usize);
for entry in section {
func_sig.push(entry?);
}
}
SectionCode::Global => {
let section = section.get_global_section_reader()?;
globals.reserve_exact(section.get_count() as usize);
for entry in section {
globals.push(into_global_type(&entry?.ty));
}
}
SectionCode::Table => {
let section = section.get_table_section_reader()?;
tables.reserve_exact(section.get_count() as usize);
for entry in section {
tables.push(into_table_type(entry?))
}
}
SectionCode::Import => {
let section = section.get_import_section_reader()?;
imports.reserve_exact(section.get_count() as usize);
for entry in section {
let entry = entry?;
let module = String::from(entry.module).into();
let name = String::from(entry.field).into();
let r#type = match entry.ty {
ImportSectionEntryType::Function(index) => {
func_sig.push(index);
let sig = &sigs[index as usize];
ExternType::ExternFunc(sig.clone())
}
ImportSectionEntryType::Table(tt) => {
let table = into_table_type(tt);
tables.push(table.clone());
ExternType::ExternTable(table)
}
ImportSectionEntryType::Memory(mt) => {
let memory = into_memory_type(mt);
memories.push(memory.clone());
ExternType::ExternMemory(memory)
}
ImportSectionEntryType::Global(gt) => {
let global = into_global_type(&gt);
globals.push(global.clone());
ExternType::ExternGlobal(global)
}
};
imports.push(ImportType::new(module, name, r#type));
}
}
SectionCode::Export => {
let section = section.get_export_section_reader()?;
exports.reserve_exact(section.get_count() as usize);
for entry in section {
let entry = entry?;
let name = String::from(entry.field).into();
let r#type = match entry.kind {
ExternalKind::Function => {
let sig_index = func_sig[entry.index as usize] as usize;
let sig = &sigs[sig_index];
ExternType::ExternFunc(sig.clone())
}
ExternalKind::Table => {
ExternType::ExternTable(tables[entry.index as usize].clone())
}
ExternalKind::Memory => {
ExternType::ExternMemory(memories[entry.index as usize].clone())
}
ExternalKind::Global => {
ExternType::ExternGlobal(globals[entry.index as usize].clone())
}
};
exports.push(ExportType::new(name, r#type));
}
}
_ => {
// skip other sections
}
}
}
Ok((imports.into_boxed_slice(), exports.into_boxed_slice()))
}
#[derive(Clone)]
pub struct Module {
store: HostRef<Store>,
binary: Box<[u8]>,
imports: Box<[ImportType]>,
exports: Box<[ExportType]>,
}
impl Module {
pub fn new(store: &HostRef<Store>, binary: &[u8]) -> Result<Module> {
let (imports, exports) = read_imports_and_exports(binary)?;
Ok(Module {
store: store.clone(),
binary: binary.into(),
imports,
exports,
})
}
pub(crate) fn binary(&self) -> &[u8] {
&self.binary
}
pub fn validate(_store: &Store, binary: &[u8]) -> bool {
validate(binary, None).is_ok()
}
pub fn imports(&self) -> &[ImportType] {
&self.imports
}
pub fn exports(&self) -> &[ExportType] {
&self.exports
}
}

211
crates/api/src/ref.rs Normal file
View File

@@ -0,0 +1,211 @@
use alloc::boxed::Box;
use alloc::rc::{Rc, Weak};
use core::any::Any;
use core::cell::{self, RefCell};
use core::fmt;
pub trait HostInfo {
fn finalize(&mut self) {}
}
trait InternalRefBase: Any {
fn as_any(&self) -> &dyn Any;
fn host_info(&self) -> Option<cell::RefMut<Box<dyn HostInfo>>>;
fn set_host_info(&self, info: Option<Box<dyn HostInfo>>);
fn ptr_eq(&self, other: &dyn InternalRefBase) -> bool;
}
#[derive(Clone)]
pub struct InternalRef(Rc<dyn InternalRefBase>);
impl InternalRef {
pub fn is_ref<T: 'static>(&self) -> bool {
let r = self.0.as_any();
Any::is::<HostRef<T>>(r)
}
pub fn get_ref<T: 'static>(&self) -> HostRef<T> {
let r = self.0.as_any();
r.downcast_ref::<HostRef<T>>()
.expect("reference is not T type")
.clone()
}
}
struct AnyAndHostInfo {
any: Box<dyn Any>,
host_info: Option<Box<dyn HostInfo>>,
}
impl Drop for AnyAndHostInfo {
fn drop(&mut self) {
if let Some(info) = &mut self.host_info {
info.finalize();
}
}
}
#[derive(Clone)]
pub struct OtherRef(Rc<RefCell<AnyAndHostInfo>>);
#[derive(Clone)]
pub enum AnyRef {
Null,
Ref(InternalRef),
Other(OtherRef),
}
impl AnyRef {
pub fn new(data: Box<dyn Any>) -> Self {
let info = AnyAndHostInfo {
any: data,
host_info: None,
};
AnyRef::Other(OtherRef(Rc::new(RefCell::new(info))))
}
pub fn null() -> Self {
AnyRef::Null
}
pub fn data(&self) -> cell::Ref<Box<dyn Any>> {
match self {
AnyRef::Other(OtherRef(r)) => cell::Ref::map(r.borrow(), |r| &r.any),
_ => panic!("expected AnyRef::Other"),
}
}
pub fn ptr_eq(&self, other: &AnyRef) -> bool {
match (self, other) {
(AnyRef::Null, AnyRef::Null) => true,
(AnyRef::Ref(InternalRef(ref a)), AnyRef::Ref(InternalRef(ref b))) => {
a.ptr_eq(b.as_ref())
}
(AnyRef::Other(OtherRef(ref a)), AnyRef::Other(OtherRef(ref b))) => Rc::ptr_eq(a, b),
_ => false,
}
}
pub fn host_info(&self) -> Option<cell::RefMut<Box<dyn HostInfo>>> {
match self {
AnyRef::Null => panic!("null"),
AnyRef::Ref(r) => r.0.host_info(),
AnyRef::Other(r) => {
let info = cell::RefMut::map(r.0.borrow_mut(), |b| &mut b.host_info);
if info.is_none() {
return None;
}
Some(cell::RefMut::map(info, |info| info.as_mut().unwrap()))
}
}
}
pub fn set_host_info(&self, info: Option<Box<dyn HostInfo>>) {
match self {
AnyRef::Null => panic!("null"),
AnyRef::Ref(r) => r.0.set_host_info(info),
AnyRef::Other(r) => {
r.0.borrow_mut().host_info = info;
}
}
}
}
impl fmt::Debug for AnyRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AnyRef::Null => write!(f, "null"),
AnyRef::Ref(_) => write!(f, "anyref"),
AnyRef::Other(_) => write!(f, "other ref"),
}
}
}
struct ContentBox<T> {
content: T,
host_info: Option<Box<dyn HostInfo>>,
anyref_data: Weak<dyn InternalRefBase>,
}
impl<T> Drop for ContentBox<T> {
fn drop(&mut self) {
if let Some(info) = &mut self.host_info {
info.finalize();
}
}
}
pub struct HostRef<T>(Rc<RefCell<ContentBox<T>>>);
impl<T: 'static> HostRef<T> {
pub fn new(item: T) -> HostRef<T> {
let anyref_data: Weak<HostRef<T>> = Weak::new();
let content = ContentBox {
content: item,
host_info: None,
anyref_data,
};
HostRef(Rc::new(RefCell::new(content)))
}
pub fn borrow(&self) -> cell::Ref<T> {
cell::Ref::map(self.0.borrow(), |b| &b.content)
}
pub fn borrow_mut(&self) -> cell::RefMut<T> {
cell::RefMut::map(self.0.borrow_mut(), |b| &mut b.content)
}
pub fn ptr_eq(&self, other: &HostRef<T>) -> bool {
Rc::ptr_eq(&self.0, &other.0)
}
pub fn anyref(&self) -> AnyRef {
let r = self.0.borrow_mut().anyref_data.upgrade();
if let Some(r) = r {
return AnyRef::Ref(InternalRef(r));
}
let anyref_data: Rc<dyn InternalRefBase> = Rc::new(self.clone());
self.0.borrow_mut().anyref_data = Rc::downgrade(&anyref_data);
AnyRef::Ref(InternalRef(anyref_data))
}
}
impl<T: 'static> InternalRefBase for HostRef<T> {
fn ptr_eq(&self, other: &dyn InternalRefBase) -> bool {
if let Some(other) = other.as_any().downcast_ref() {
self.ptr_eq(other)
} else {
false
}
}
fn as_any(&self) -> &dyn Any {
self
}
fn host_info(&self) -> Option<cell::RefMut<Box<dyn HostInfo>>> {
let info = cell::RefMut::map(self.0.borrow_mut(), |b| &mut b.host_info);
if info.is_none() {
return None;
}
Some(cell::RefMut::map(info, |info| info.as_mut().unwrap()))
}
fn set_host_info(&self, info: Option<Box<dyn HostInfo>>) {
self.0.borrow_mut().host_info = info;
}
}
impl<T> Clone for HostRef<T> {
fn clone(&self) -> HostRef<T> {
HostRef(self.0.clone())
}
}
impl<T: fmt::Debug> fmt::Debug for HostRef<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Ref(")?;
self.0.borrow().content.fmt(f)?;
write!(f, ")")
}
}

154
crates/api/src/runtime.rs Normal file
View File

@@ -0,0 +1,154 @@
use crate::HashMap;
use alloc::boxed::Box;
use alloc::rc::Rc;
use alloc::string::String;
use core::cell::RefCell;
use crate::context::{create_compiler, Context};
use crate::r#ref::HostRef;
use cranelift_codegen::{ir, settings};
use wasmtime_jit::{CompilationStrategy, Features};
// Runtime Environment
// Configuration
fn default_flags() -> settings::Flags {
let flag_builder = settings::builder();
settings::Flags::new(flag_builder)
}
pub struct Config {
flags: settings::Flags,
features: Features,
debug_info: bool,
strategy: CompilationStrategy,
}
impl Config {
pub fn default() -> Config {
Config {
debug_info: false,
features: Default::default(),
flags: default_flags(),
strategy: CompilationStrategy::Auto,
}
}
pub fn new(
flags: settings::Flags,
features: Features,
debug_info: bool,
strategy: CompilationStrategy,
) -> Config {
Config {
flags,
features,
debug_info,
strategy,
}
}
pub(crate) fn debug_info(&self) -> bool {
self.debug_info
}
pub(crate) fn flags(&self) -> &settings::Flags {
&self.flags
}
pub(crate) fn features(&self) -> &Features {
&self.features
}
pub(crate) fn strategy(&self) -> CompilationStrategy {
self.strategy
}
}
// Engine
pub struct Engine {
config: Config,
}
impl Engine {
pub fn new(config: Config) -> Engine {
Engine { config }
}
pub fn default() -> Engine {
Engine::new(Config::default())
}
pub(crate) fn config(&self) -> &Config {
&self.config
}
pub fn create_wasmtime_context(&self) -> wasmtime_jit::Context {
let flags = self.config.flags().clone();
wasmtime_jit::Context::new(Box::new(create_compiler(flags, self.config.strategy())))
}
}
// Store
pub struct Store {
engine: HostRef<Engine>,
context: Context,
global_exports: Rc<RefCell<HashMap<String, Option<wasmtime_runtime::Export>>>>,
signature_cache: HashMap<wasmtime_runtime::VMSharedSignatureIndex, ir::Signature>,
}
impl Store {
pub fn new(engine: &HostRef<Engine>) -> Store {
let flags = engine.borrow().config().flags().clone();
let features = engine.borrow().config().features().clone();
let debug_info = engine.borrow().config().debug_info();
let strategy = engine.borrow().config().strategy();
Store {
engine: engine.clone(),
context: Context::create(flags, features, debug_info, strategy),
global_exports: Rc::new(RefCell::new(HashMap::new())),
signature_cache: HashMap::new(),
}
}
pub fn engine(&self) -> &HostRef<Engine> {
&self.engine
}
pub(crate) fn context(&mut self) -> &mut Context {
&mut self.context
}
// Specific to wasmtime: hack to pass memory around to wasi
pub fn global_exports(
&self,
) -> &Rc<RefCell<HashMap<String, Option<wasmtime_runtime::Export>>>> {
&self.global_exports
}
pub(crate) fn register_cranelift_signature(
&mut self,
signature: &ir::Signature,
) -> wasmtime_runtime::VMSharedSignatureIndex {
use crate::hash_map::Entry;
let index = self.context().compiler().signatures().register(signature);
match self.signature_cache.entry(index) {
Entry::Vacant(v) => {
v.insert(signature.clone());
}
Entry::Occupied(_) => (),
}
index
}
pub(crate) fn lookup_cranelift_signature(
&self,
type_index: wasmtime_runtime::VMSharedSignatureIndex,
) -> Option<&ir::Signature> {
self.signature_cache.get(&type_index)
}
}

View File

@@ -0,0 +1,62 @@
//! Support for a calling of an imported function.
use anyhow::Result;
use cranelift_entity::PrimaryMap;
use cranelift_wasm::DefinedFuncIndex;
//use target_lexicon::HOST;
use wasmtime_environ::Module;
use wasmtime_runtime::{Imports, InstanceHandle, VMFunctionBody};
use crate::{HashMap, HashSet};
use alloc::boxed::Box;
use alloc::rc::Rc;
use alloc::string::String;
use alloc::vec::Vec;
use core::any::Any;
use core::cell::{RefCell, RefMut};
use crate::runtime::Store;
pub(crate) fn create_handle(
module: Module,
signature_registry: Option<RefMut<Store>>,
finished_functions: PrimaryMap<DefinedFuncIndex, *const VMFunctionBody>,
state: Box<dyn Any>,
) -> Result<InstanceHandle> {
let global_exports: Rc<RefCell<HashMap<String, Option<wasmtime_runtime::Export>>>> =
Rc::new(RefCell::new(HashMap::new()));
let imports = Imports::new(
HashSet::new(),
PrimaryMap::new(),
PrimaryMap::new(),
PrimaryMap::new(),
PrimaryMap::new(),
);
let data_initializers = Vec::new();
// Compute indices into the shared signature table.
let signatures = signature_registry
.and_then(|mut signature_registry| {
Some(
module
.signatures
.values()
.map(|sig| signature_registry.register_cranelift_signature(sig))
.collect::<PrimaryMap<_, _>>(),
)
})
.unwrap_or_else(|| PrimaryMap::new());
Ok(InstanceHandle::new(
Rc::new(module),
global_exports,
finished_functions.into_boxed_slice(),
imports,
&data_initializers,
signatures.into_boxed_slice(),
None,
state,
)
.expect("instance"))
}

View File

@@ -0,0 +1,291 @@
//! Support for a calling of an imported function.
use crate::r#ref::HostRef;
use anyhow::Result;
use cranelift_codegen::ir::types;
use cranelift_codegen::ir::{InstBuilder, StackSlotData, StackSlotKind, TrapCode};
use cranelift_codegen::Context;
use cranelift_codegen::{binemit, ir, isa};
use cranelift_entity::{EntityRef, PrimaryMap};
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
use cranelift_wasm::{DefinedFuncIndex, FuncIndex};
use wasmtime_environ::{CompiledFunction, Export, Module};
use wasmtime_jit::CodeMemory;
use wasmtime_runtime::{InstanceHandle, VMContext, VMFunctionBody};
use alloc::boxed::Box;
use alloc::rc::Rc;
use alloc::string::ToString;
use alloc::vec::Vec;
use core::cmp;
use crate::{Callable, FuncType, Store, Trap, Val};
use super::create_handle::create_handle;
struct TrampolineState {
func: Rc<dyn Callable + 'static>,
trap: Option<HostRef<Trap>>,
#[allow(dead_code)]
code_memory: CodeMemory,
}
unsafe extern "C" fn stub_fn(vmctx: *mut VMContext, call_id: u32, values_vec: *mut i64) -> u32 {
let mut instance = InstanceHandle::from_vmctx(vmctx);
let (args, returns_len) = {
let module = instance.module_ref();
let signature = &module.signatures[module.functions[FuncIndex::new(call_id as usize)]];
let mut args = Vec::new();
for i in 1..signature.params.len() {
args.push(Val::read_value_from(
values_vec.offset(i as isize - 1),
signature.params[i].value_type,
))
}
(args, signature.returns.len())
};
let mut returns = vec![Val::default(); returns_len];
let func = &instance
.host_state()
.downcast_mut::<TrampolineState>()
.expect("state")
.func;
match func.call(&args, &mut returns) {
Ok(()) => {
for i in 0..returns_len {
// TODO check signature.returns[i].value_type ?
returns[i].write_value_to(values_vec.offset(i as isize));
}
0
}
Err(trap) => {
// TODO read custom exception
InstanceHandle::from_vmctx(vmctx)
.host_state()
.downcast_mut::<TrampolineState>()
.expect("state")
.trap = Some(trap);
1
}
}
}
/// Create a trampoline for invoking a Callable.
fn make_trampoline(
isa: &dyn isa::TargetIsa,
code_memory: &mut CodeMemory,
fn_builder_ctx: &mut FunctionBuilderContext,
call_id: u32,
signature: &ir::Signature,
) -> *const VMFunctionBody {
// Mostly reverse copy of the similar method from wasmtime's
// wasmtime-jit/src/compiler.rs.
let pointer_type = isa.pointer_type();
let mut stub_sig = ir::Signature::new(isa.frontend_config().default_call_conv);
// Add the `vmctx` parameter.
stub_sig.params.push(ir::AbiParam::special(
pointer_type,
ir::ArgumentPurpose::VMContext,
));
// Add the `call_id` parameter.
stub_sig.params.push(ir::AbiParam::new(types::I32));
// Add the `values_vec` parameter.
stub_sig.params.push(ir::AbiParam::new(pointer_type));
// Add error/trap return.
stub_sig.returns.push(ir::AbiParam::new(types::I32));
let values_vec_len = 8 * cmp::max(signature.params.len() - 1, signature.returns.len()) as u32;
let mut context = Context::new();
context.func =
ir::Function::with_name_signature(ir::ExternalName::user(0, 0), signature.clone());
let ss = context.func.create_stack_slot(StackSlotData::new(
StackSlotKind::ExplicitSlot,
values_vec_len,
));
let value_size = 8;
{
let mut builder = FunctionBuilder::new(&mut context.func, fn_builder_ctx);
let block0 = builder.create_ebb();
builder.append_ebb_params_for_function_params(block0);
builder.switch_to_block(block0);
builder.seal_block(block0);
let values_vec_ptr_val = builder.ins().stack_addr(pointer_type, ss, 0);
let mflags = ir::MemFlags::trusted();
for i in 1..signature.params.len() {
if i == 0 {
continue;
}
let val = builder.func.dfg.ebb_params(block0)[i];
builder.ins().store(
mflags,
val,
values_vec_ptr_val,
((i - 1) * value_size) as i32,
);
}
let vmctx_ptr_val = builder.func.dfg.ebb_params(block0)[0];
let call_id_val = builder.ins().iconst(types::I32, call_id as i64);
let callee_args = vec![vmctx_ptr_val, call_id_val, values_vec_ptr_val];
let new_sig = builder.import_signature(stub_sig.clone());
let callee_value = builder
.ins()
.iconst(pointer_type, stub_fn as *const VMFunctionBody as i64);
let call = builder
.ins()
.call_indirect(new_sig, callee_value, &callee_args);
let call_result = builder.func.dfg.inst_results(call)[0];
builder.ins().trapnz(call_result, TrapCode::User(0));
let mflags = ir::MemFlags::trusted();
let mut results = Vec::new();
for (i, r) in signature.returns.iter().enumerate() {
let load = builder.ins().load(
r.value_type,
mflags,
values_vec_ptr_val,
(i * value_size) as i32,
);
results.push(load);
}
builder.ins().return_(&results);
builder.finalize()
}
let mut code_buf: Vec<u8> = Vec::new();
let mut reloc_sink = RelocSink {};
let mut trap_sink = binemit::NullTrapSink {};
let mut stackmap_sink = binemit::NullStackmapSink {};
context
.compile_and_emit(
isa,
&mut code_buf,
&mut reloc_sink,
&mut trap_sink,
&mut stackmap_sink,
)
.expect("compile_and_emit");
let mut unwind_info = Vec::new();
context.emit_unwind_info(isa, &mut unwind_info);
code_memory
.allocate_for_function(&CompiledFunction {
body: code_buf,
jt_offsets: context.func.jt_offsets,
unwind_info,
})
.expect("allocate_for_function")
.as_ptr()
}
pub fn create_handle_with_function(
ft: &FuncType,
func: &Rc<dyn Callable + 'static>,
store: &HostRef<Store>,
) -> Result<InstanceHandle> {
let sig = ft.get_cranelift_signature().clone();
let isa = {
let isa_builder =
cranelift_native::builder().expect("host machine is not a supported target");
let flag_builder = cranelift_codegen::settings::builder();
isa_builder.finish(cranelift_codegen::settings::Flags::new(flag_builder))
};
let mut fn_builder_ctx = FunctionBuilderContext::new();
let mut module = Module::new();
let mut finished_functions: PrimaryMap<DefinedFuncIndex, *const VMFunctionBody> =
PrimaryMap::new();
let mut code_memory = CodeMemory::new();
//let pointer_type = types::Type::triple_pointer_type(&HOST);
//let call_conv = isa::CallConv::triple_default(&HOST);
let sig_id = module.signatures.push(sig.clone());
let func_id = module.functions.push(sig_id);
module
.exports
.insert("trampoline".to_string(), Export::Function(func_id));
let trampoline = make_trampoline(
isa.as_ref(),
&mut code_memory,
&mut fn_builder_ctx,
func_id.index() as u32,
&sig,
);
code_memory.publish();
finished_functions.push(trampoline);
let trampoline_state = TrampolineState {
func: func.clone(),
trap: None,
code_memory,
};
create_handle(
module,
Some(store.borrow_mut()),
finished_functions,
Box::new(trampoline_state),
)
}
/// We don't expect trampoline compilation to produce any relocations, so
/// this `RelocSink` just asserts that it doesn't recieve any.
struct RelocSink {}
impl binemit::RelocSink for RelocSink {
fn reloc_ebb(
&mut self,
_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_ebb_offset: binemit::CodeOffset,
) {
panic!("trampoline compilation should not produce ebb relocs");
}
fn reloc_external(
&mut self,
_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_name: &ir::ExternalName,
_addend: binemit::Addend,
) {
panic!("trampoline compilation should not produce external symbol relocs");
}
fn reloc_constant(
&mut self,
_code_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_constant_offset: ir::ConstantOffset,
) {
panic!("trampoline compilation should not produce constant relocs");
}
fn reloc_jt(
&mut self,
_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_jt: ir::JumpTable,
) {
panic!("trampoline compilation should not produce jump table relocs");
}
}

View File

@@ -0,0 +1,46 @@
use alloc::boxed::Box;
use anyhow::Result;
use cranelift_entity::PrimaryMap;
use wasmtime_environ::Module;
use wasmtime_runtime::{InstanceHandle, VMGlobalDefinition};
use super::create_handle::create_handle;
use crate::{GlobalType, Mutability, Val};
#[allow(dead_code)]
pub struct GlobalState {
definition: Box<VMGlobalDefinition>,
handle: InstanceHandle,
}
pub fn create_global(gt: &GlobalType, val: Val) -> Result<(wasmtime_runtime::Export, GlobalState)> {
let mut definition = Box::new(VMGlobalDefinition::new());
unsafe {
match val {
Val::I32(i) => *definition.as_i32_mut() = i,
Val::I64(i) => *definition.as_i64_mut() = i,
Val::F32(f) => *definition.as_u32_mut() = f,
Val::F64(f) => *definition.as_u64_mut() = f,
_ => unimplemented!("create_global for {:?}", gt),
}
}
let global = cranelift_wasm::Global {
ty: gt.content().get_cranelift_type(),
mutability: match gt.mutability() {
Mutability::Const => false,
Mutability::Var => true,
},
initializer: cranelift_wasm::GlobalInit::Import, // TODO is it right?
};
let mut handle =
create_handle(Module::new(), None, PrimaryMap::new(), Box::new(())).expect("handle");
Ok((
wasmtime_runtime::Export::Global {
definition: definition.as_mut(),
vmctx: handle.vmctx_mut_ptr(),
global,
},
GlobalState { definition, handle },
))
}

View File

@@ -0,0 +1,35 @@
use alloc::boxed::Box;
use alloc::string::ToString;
use anyhow::Result;
use cranelift_entity::PrimaryMap;
use wasmtime_environ::Module;
use wasmtime_runtime::InstanceHandle;
use super::create_handle::create_handle;
use crate::MemoryType;
#[allow(dead_code)]
pub fn create_handle_with_memory(memory: &MemoryType) -> Result<InstanceHandle> {
let mut module = Module::new();
let memory = cranelift_wasm::Memory {
minimum: memory.limits().min(),
maximum: if memory.limits().max() == core::u32::MAX {
None
} else {
Some(memory.limits().max())
},
shared: false, // TODO
};
let tunable = Default::default();
let memory_plan = wasmtime_environ::MemoryPlan::for_memory(memory, &tunable);
let memory_id = module.memory_plans.push(memory_plan);
module.exports.insert(
"memory".to_string(),
wasmtime_environ::Export::Memory(memory_id),
);
create_handle(module, None, PrimaryMap::new(), Box::new(()))
}

View File

@@ -0,0 +1,52 @@
//! Utility module to create trampolines in/out WebAssembly module.
mod create_handle;
mod func;
mod global;
mod memory;
mod table;
use crate::r#ref::HostRef;
use alloc::rc::Rc;
use anyhow::Result;
use self::func::create_handle_with_function;
use self::global::create_global;
use self::memory::create_handle_with_memory;
use self::table::create_handle_with_table;
use super::{Callable, FuncType, GlobalType, MemoryType, Store, TableType, Val};
pub use self::global::GlobalState;
pub fn generate_func_export(
ft: &FuncType,
func: &Rc<dyn Callable + 'static>,
store: &HostRef<Store>,
) -> Result<(wasmtime_runtime::InstanceHandle, wasmtime_runtime::Export)> {
let mut instance = create_handle_with_function(ft, func, store)?;
let export = instance.lookup("trampoline").expect("trampoline export");
Ok((instance, export))
}
pub fn generate_global_export(
gt: &GlobalType,
val: Val,
) -> Result<(wasmtime_runtime::Export, GlobalState)> {
create_global(gt, val)
}
pub fn generate_memory_export(
m: &MemoryType,
) -> Result<(wasmtime_runtime::InstanceHandle, wasmtime_runtime::Export)> {
let mut instance = create_handle_with_memory(m)?;
let export = instance.lookup("memory").expect("memory export");
Ok((instance, export))
}
pub fn generate_table_export(
t: &TableType,
) -> Result<(wasmtime_runtime::InstanceHandle, wasmtime_runtime::Export)> {
let mut instance = create_handle_with_table(t)?;
let export = instance.lookup("table").expect("table export");
Ok((instance, export))
}

View File

@@ -0,0 +1,37 @@
use alloc::boxed::Box;
use alloc::string::ToString;
use anyhow::Result;
use cranelift_entity::PrimaryMap;
use cranelift_wasm::TableElementType;
use wasmtime_environ::Module;
use wasmtime_runtime::InstanceHandle;
use super::create_handle::create_handle;
use crate::{TableType, ValType};
pub fn create_handle_with_table(table: &TableType) -> Result<InstanceHandle> {
let mut module = Module::new();
let table = cranelift_wasm::Table {
minimum: table.limits().min(),
maximum: if table.limits().max() == core::u32::MAX {
None
} else {
Some(table.limits().max())
},
ty: match table.element() {
ValType::FuncRef => TableElementType::Func,
_ => TableElementType::Val(table.element().get_cranelift_type()),
},
};
let tunable = Default::default();
let table_plan = wasmtime_environ::TablePlan::for_table(table, &tunable);
let table_id = module.table_plans.push(table_plan);
module.exports.insert(
"table".to_string(),
wasmtime_environ::Export::Table(table_id),
);
create_handle(module, None, PrimaryMap::new(), Box::new(()))
}

22
crates/api/src/trap.rs Normal file
View File

@@ -0,0 +1,22 @@
use alloc::string::{String, ToString};
use thiserror::Error;
#[derive(Error, Debug)]
#[error("Wasm trap: {message}")]
pub struct Trap {
message: String,
}
impl Trap {
pub fn new(message: String) -> Trap {
Trap { message }
}
pub fn fake() -> Trap {
Trap::new("TODO trap".to_string())
}
pub fn message(&self) -> &str {
&self.message
}
}

353
crates/api/src/types.rs Normal file
View File

@@ -0,0 +1,353 @@
use alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
use cranelift_codegen::ir;
// Type Representations
// Type attributes
#[derive(Debug, Clone, Copy)]
pub enum Mutability {
Const,
Var,
}
#[derive(Debug, Clone)]
pub struct Limits {
min: u32,
max: u32,
}
impl Limits {
pub fn new(min: u32, max: u32) -> Limits {
Limits { min, max }
}
pub fn at_least(min: u32) -> Limits {
Limits {
min,
max: ::core::u32::MAX,
}
}
pub fn min(&self) -> u32 {
self.min
}
pub fn max(&self) -> u32 {
self.max
}
}
// Value Types
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum ValType {
I32,
I64,
F32,
F64,
V128,
AnyRef, /* = 128 */
FuncRef,
}
impl ValType {
pub fn is_num(&self) -> bool {
match self {
ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 => true,
_ => false,
}
}
pub fn is_ref(&self) -> bool {
match self {
ValType::AnyRef | ValType::FuncRef => true,
_ => false,
}
}
pub(crate) fn get_cranelift_type(&self) -> ir::Type {
match self {
ValType::I32 => ir::types::I32,
ValType::I64 => ir::types::I64,
ValType::F32 => ir::types::F32,
ValType::F64 => ir::types::F64,
ValType::V128 => ir::types::I8X16,
_ => unimplemented!("get_cranelift_type other"),
}
}
pub(crate) fn from_cranelift_type(ty: ir::Type) -> ValType {
match ty {
ir::types::I32 => ValType::I32,
ir::types::I64 => ValType::I64,
ir::types::F32 => ValType::F32,
ir::types::F64 => ValType::F64,
ir::types::I8X16 => ValType::V128,
_ => unimplemented!("from_cranelift_type other"),
}
}
}
// External Types
#[derive(Debug, Clone)]
pub enum ExternType {
ExternFunc(FuncType),
ExternGlobal(GlobalType),
ExternTable(TableType),
ExternMemory(MemoryType),
}
impl ExternType {
pub fn func(&self) -> &FuncType {
match self {
ExternType::ExternFunc(func) => func,
_ => panic!("ExternType::ExternFunc expected"),
}
}
pub fn global(&self) -> &GlobalType {
match self {
ExternType::ExternGlobal(func) => func,
_ => panic!("ExternType::ExternGlobal expected"),
}
}
pub fn table(&self) -> &TableType {
match self {
ExternType::ExternTable(table) => table,
_ => panic!("ExternType::ExternTable expected"),
}
}
pub fn memory(&self) -> &MemoryType {
match self {
ExternType::ExternMemory(memory) => memory,
_ => panic!("ExternType::ExternMemory expected"),
}
}
}
// Function Types
fn from_cranelift_abiparam(param: &ir::AbiParam) -> ValType {
assert!(param.purpose == ir::ArgumentPurpose::Normal);
ValType::from_cranelift_type(param.value_type)
}
#[derive(Debug, Clone)]
pub struct FuncType {
params: Box<[ValType]>,
results: Box<[ValType]>,
signature: ir::Signature,
}
impl FuncType {
pub fn new(params: Box<[ValType]>, results: Box<[ValType]>) -> FuncType {
use cranelift_codegen::ir::*;
use cranelift_codegen::isa::CallConv;
use target_lexicon::HOST;
let call_conv = CallConv::triple_default(&HOST);
let signature: Signature = {
let mut params = params
.iter()
.map(|p| AbiParam::new(p.get_cranelift_type()))
.collect::<Vec<_>>();
let returns = results
.iter()
.map(|p| AbiParam::new(p.get_cranelift_type()))
.collect::<Vec<_>>();
params.insert(0, AbiParam::special(types::I64, ArgumentPurpose::VMContext));
Signature {
params,
returns,
call_conv,
}
};
FuncType {
params,
results,
signature,
}
}
pub fn params(&self) -> &[ValType] {
&self.params
}
pub fn results(&self) -> &[ValType] {
&self.results
}
pub(crate) fn get_cranelift_signature(&self) -> &ir::Signature {
&self.signature
}
pub(crate) fn from_cranelift_signature(signature: ir::Signature) -> FuncType {
let params = signature
.params
.iter()
.filter(|p| p.purpose == ir::ArgumentPurpose::Normal)
.map(|p| from_cranelift_abiparam(p))
.collect::<Vec<_>>();
let results = signature
.returns
.iter()
.map(|p| from_cranelift_abiparam(p))
.collect::<Vec<_>>();
FuncType {
params: params.into_boxed_slice(),
results: results.into_boxed_slice(),
signature,
}
}
}
// Global Types
#[derive(Debug, Clone)]
pub struct GlobalType {
content: ValType,
mutability: Mutability,
}
impl GlobalType {
pub fn new(content: ValType, mutability: Mutability) -> GlobalType {
GlobalType {
content,
mutability,
}
}
pub fn content(&self) -> &ValType {
&self.content
}
pub fn mutability(&self) -> Mutability {
self.mutability
}
pub(crate) fn from_cranelift_global(global: cranelift_wasm::Global) -> GlobalType {
let ty = ValType::from_cranelift_type(global.ty);
let mutability = if global.mutability {
Mutability::Var
} else {
Mutability::Const
};
GlobalType::new(ty, mutability)
}
}
// Table Types
#[derive(Debug, Clone)]
pub struct TableType {
element: ValType,
limits: Limits,
}
impl TableType {
pub fn new(element: ValType, limits: Limits) -> TableType {
TableType { element, limits }
}
pub fn element(&self) -> &ValType {
&self.element
}
pub fn limits(&self) -> &Limits {
&self.limits
}
pub(crate) fn from_cranelift_table(table: cranelift_wasm::Table) -> TableType {
assert!(if let cranelift_wasm::TableElementType::Func = table.ty {
true
} else {
false
});
let ty = ValType::FuncRef;
let limits = Limits::new(table.minimum, table.maximum.unwrap_or(::core::u32::MAX));
TableType::new(ty, limits)
}
}
// Memory Types
#[derive(Debug, Clone)]
pub struct MemoryType {
limits: Limits,
}
impl MemoryType {
pub fn new(limits: Limits) -> MemoryType {
MemoryType { limits }
}
pub fn limits(&self) -> &Limits {
&self.limits
}
pub(crate) fn from_cranelift_memory(memory: cranelift_wasm::Memory) -> MemoryType {
MemoryType::new(Limits::new(
memory.minimum,
memory.maximum.unwrap_or(::core::u32::MAX),
))
}
}
// Import Types
#[derive(Debug, Clone)]
pub struct Name(String);
impl From<String> for Name {
fn from(s: String) -> Name {
Name(s)
}
}
impl ::alloc::string::ToString for Name {
fn to_string(&self) -> String {
self.0.to_owned()
}
}
#[derive(Debug, Clone)]
pub struct ImportType {
module: Name,
name: Name,
r#type: ExternType,
}
impl ImportType {
pub fn new(module: Name, name: Name, r#type: ExternType) -> ImportType {
ImportType {
module,
name,
r#type,
}
}
pub fn module(&self) -> &Name {
&self.module
}
pub fn name(&self) -> &Name {
&self.name
}
pub fn r#type(&self) -> &ExternType {
&self.r#type
}
}
// Export Types
#[derive(Debug, Clone)]
pub struct ExportType {
name: Name,
r#type: ExternType,
}
impl ExportType {
pub fn new(name: Name, r#type: ExternType) -> ExportType {
ExportType { name, r#type }
}
pub fn name(&self) -> &Name {
&self.name
}
pub fn r#type(&self) -> &ExternType {
&self.r#type
}
}

235
crates/api/src/values.rs Normal file
View File

@@ -0,0 +1,235 @@
use crate::externals::Func;
use crate::r#ref::{AnyRef, HostRef};
use crate::runtime::Store;
use crate::types::ValType;
use core::ptr;
use cranelift_codegen::ir;
use wasmtime_jit::RuntimeValue;
#[derive(Debug, Clone)]
pub enum Val {
I32(i32),
I64(i64),
F32(u32),
F64(u64),
AnyRef(AnyRef),
FuncRef(HostRef<Func>),
}
impl Val {
pub fn default() -> Val {
Val::AnyRef(AnyRef::null())
}
pub fn r#type(&self) -> ValType {
match self {
Val::I32(_) => ValType::I32,
Val::I64(_) => ValType::I64,
Val::F32(_) => ValType::F32,
Val::F64(_) => ValType::F64,
Val::AnyRef(_) => ValType::AnyRef,
Val::FuncRef(_) => ValType::FuncRef,
}
}
pub(crate) unsafe fn write_value_to(&self, p: *mut i64) {
match self {
Val::I32(i) => ptr::write(p as *mut i32, *i),
Val::I64(i) => ptr::write(p as *mut i64, *i),
Val::F32(u) => ptr::write(p as *mut u32, *u),
Val::F64(u) => ptr::write(p as *mut u64, *u),
_ => unimplemented!("Val::write_value_to"),
}
}
pub(crate) unsafe fn read_value_from(p: *const i64, ty: ir::Type) -> Val {
match ty {
ir::types::I32 => Val::I32(ptr::read(p as *const i32)),
ir::types::I64 => Val::I64(ptr::read(p as *const i64)),
ir::types::F32 => Val::F32(ptr::read(p as *const u32)),
ir::types::F64 => Val::F64(ptr::read(p as *const u64)),
_ => unimplemented!("Val::read_value_from"),
}
}
pub fn from_f32_bits(v: u32) -> Val {
Val::F32(v)
}
pub fn from_f64_bits(v: u64) -> Val {
Val::F64(v)
}
pub fn i32(&self) -> i32 {
if let Val::I32(i) = self {
*i
} else {
panic!("Invalid conversion of {:?} to i32.", self);
}
}
pub fn i64(&self) -> i64 {
if let Val::I64(i) = self {
*i
} else {
panic!("Invalid conversion of {:?} to i64.", self);
}
}
pub fn f32(&self) -> f32 {
RuntimeValue::F32(self.f32_bits()).unwrap_f32()
}
pub fn f64(&self) -> f64 {
RuntimeValue::F64(self.f64_bits()).unwrap_f64()
}
pub fn f32_bits(&self) -> u32 {
if let Val::F32(i) = self {
*i
} else {
panic!("Invalid conversion of {:?} to f32.", self);
}
}
pub fn f64_bits(&self) -> u64 {
if let Val::F64(i) = self {
*i
} else {
panic!("Invalid conversion of {:?} to f64.", self);
}
}
}
impl From<i32> for Val {
fn from(val: i32) -> Val {
Val::I32(val)
}
}
impl From<i64> for Val {
fn from(val: i64) -> Val {
Val::I64(val)
}
}
impl From<f32> for Val {
fn from(val: f32) -> Val {
Val::F32(val.to_bits())
}
}
impl From<f64> for Val {
fn from(val: f64) -> Val {
Val::F64(val.to_bits())
}
}
impl Into<i32> for Val {
fn into(self) -> i32 {
self.i32()
}
}
impl Into<i64> for Val {
fn into(self) -> i64 {
self.i64()
}
}
impl Into<f32> for Val {
fn into(self) -> f32 {
self.f32()
}
}
impl Into<f64> for Val {
fn into(self) -> f64 {
self.f64()
}
}
impl From<AnyRef> for Val {
fn from(val: AnyRef) -> Val {
match &val {
AnyRef::Ref(r) => {
if r.is_ref::<Func>() {
Val::FuncRef(r.get_ref())
} else {
Val::AnyRef(val)
}
}
_ => unimplemented!("AnyRef::Other"),
}
}
}
impl From<HostRef<Func>> for Val {
fn from(val: HostRef<Func>) -> Val {
Val::FuncRef(val)
}
}
impl Into<AnyRef> for Val {
fn into(self) -> AnyRef {
match self {
Val::AnyRef(r) => r,
Val::FuncRef(f) => f.anyref(),
_ => panic!("Invalid conversion of {:?} to anyref.", self),
}
}
}
pub(crate) fn into_checked_anyfunc(
val: Val,
store: &HostRef<Store>,
) -> wasmtime_runtime::VMCallerCheckedAnyfunc {
match val {
Val::AnyRef(AnyRef::Null) => wasmtime_runtime::VMCallerCheckedAnyfunc {
func_ptr: ptr::null(),
type_index: wasmtime_runtime::VMSharedSignatureIndex::default(),
vmctx: ptr::null_mut(),
},
Val::FuncRef(f) => {
let f = f.borrow();
let (vmctx, func_ptr, signature) = match f.wasmtime_export() {
wasmtime_runtime::Export::Function {
vmctx,
address,
signature,
} => (*vmctx, *address, signature),
_ => panic!("expected function export"),
};
let type_index = store.borrow_mut().register_cranelift_signature(signature);
wasmtime_runtime::VMCallerCheckedAnyfunc {
func_ptr,
type_index,
vmctx,
}
}
_ => panic!("val is not funcref"),
}
}
pub(crate) fn from_checked_anyfunc(
item: &wasmtime_runtime::VMCallerCheckedAnyfunc,
store: &HostRef<Store>,
) -> Val {
if item.type_index == wasmtime_runtime::VMSharedSignatureIndex::default() {
return Val::AnyRef(AnyRef::Null);
}
let signature = store
.borrow()
.lookup_cranelift_signature(item.type_index)
.expect("signature")
.clone();
let instance_handle = unsafe { wasmtime_runtime::InstanceHandle::from_vmctx(item.vmctx) };
let export = wasmtime_runtime::Export::Function {
address: item.func_ptr,
signature,
vmctx: item.vmctx,
};
let f = Func::from_wasmtime_function(export, store, instance_handle);
Val::FuncRef(HostRef::new(f))
}

1655
crates/api/src/wasm.rs Normal file

File diff suppressed because it is too large Load Diff