wasmtime: Add support for func.ref and table.grow with funcrefs
`funcref`s are implemented as `NonNull<VMCallerCheckedAnyfunc>`. This should be more efficient than using a `VMExternRef` that points at a `VMCallerCheckedAnyfunc` because it gets rid of an indirection, dynamic allocation, and some reference counting. Note that the null function reference is *NOT* a null pointer; it is a `VMCallerCheckedAnyfunc` that has a null `func_ptr` member. Part of #929
This commit is contained in:
4
build.rs
4
build.rs
@@ -215,7 +215,9 @@ fn ignore(testsuite: &str, testname: &str, strategy: &str) -> bool {
|
|||||||
|
|
||||||
("reference_types", "table_copy_on_imported_tables")
|
("reference_types", "table_copy_on_imported_tables")
|
||||||
| ("reference_types", "externref_id_function")
|
| ("reference_types", "externref_id_function")
|
||||||
| ("reference_types", "table_size") => {
|
| ("reference_types", "table_size")
|
||||||
|
| ("reference_types", "simple_ref_is_null")
|
||||||
|
| ("reference_types", "table_grow_with_funcref") => {
|
||||||
// TODO(#1886): Ignore if this isn't x64, because Cranelift only
|
// TODO(#1886): Ignore if this isn't x64, because Cranelift only
|
||||||
// supports reference types on x64.
|
// supports reference types on x64.
|
||||||
return env::var("CARGO_CFG_TARGET_ARCH").unwrap() != "x86_64";
|
return env::var("CARGO_CFG_TARGET_ARCH").unwrap() != "x86_64";
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ pub fn parse_import_section<'data>(
|
|||||||
ImportSectionEntryType::Global(ref ty) => {
|
ImportSectionEntryType::Global(ref ty) => {
|
||||||
environ.declare_global_import(
|
environ.declare_global_import(
|
||||||
Global {
|
Global {
|
||||||
|
wasm_ty: ty.content_type,
|
||||||
ty: type_to_type(ty.content_type, environ).unwrap(),
|
ty: type_to_type(ty.content_type, environ).unwrap(),
|
||||||
mutability: ty.mutable,
|
mutability: ty.mutable,
|
||||||
initializer: GlobalInit::Import,
|
initializer: GlobalInit::Import,
|
||||||
@@ -229,6 +230,7 @@ pub fn parse_global_section(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
let global = Global {
|
let global = Global {
|
||||||
|
wasm_ty: content_type,
|
||||||
ty: type_to_type(content_type, environ).unwrap(),
|
ty: type_to_type(content_type, environ).unwrap(),
|
||||||
mutability: mutable,
|
mutability: mutable,
|
||||||
initializer,
|
initializer,
|
||||||
|
|||||||
@@ -67,10 +67,18 @@ entity_impl!(DataIndex);
|
|||||||
pub struct ElemIndex(u32);
|
pub struct ElemIndex(u32);
|
||||||
entity_impl!(ElemIndex);
|
entity_impl!(ElemIndex);
|
||||||
|
|
||||||
/// WebAssembly global.
|
/// A WebAssembly global.
|
||||||
|
///
|
||||||
|
/// Note that we record both the original Wasm type and the Cranelift IR type
|
||||||
|
/// used to represent it. This is because multiple different kinds of Wasm types
|
||||||
|
/// might be represented with the same Cranelift IR type. For example, both a
|
||||||
|
/// Wasm `i64` and a `funcref` might be represented with a Cranelift `i64` on
|
||||||
|
/// 64-bit architectures, and when GC is not required for func refs.
|
||||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||||
pub struct Global {
|
pub struct Global {
|
||||||
/// The type of the value stored in the global.
|
/// The Wasm type of the value stored in the global.
|
||||||
|
pub wasm_ty: crate::WasmType,
|
||||||
|
/// The Cranelift IR type of the value stored in the global.
|
||||||
pub ty: ir::Type,
|
pub ty: ir::Type,
|
||||||
/// A flag indicating whether the value may change at runtime.
|
/// A flag indicating whether the value may change at runtime.
|
||||||
pub mutability: bool,
|
pub mutability: bool,
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ pub extern "C" fn wasm_table_new(
|
|||||||
) -> Option<Box<wasm_table_t>> {
|
) -> Option<Box<wasm_table_t>> {
|
||||||
let init: Val = match init {
|
let init: Val = match init {
|
||||||
Some(init) => init.r.into(),
|
Some(init) => init.r.into(),
|
||||||
None => Val::ExternRef(None),
|
None => Val::FuncRef(None),
|
||||||
};
|
};
|
||||||
let table = Table::new(&store.store, tt.ty().ty.clone(), init).ok()?;
|
let table = Table::new(&store.store, tt.ty().ty.clone(), init).ok()?;
|
||||||
Some(Box::new(wasm_table_t {
|
Some(Box::new(wasm_table_t {
|
||||||
@@ -60,8 +60,8 @@ pub extern "C" fn wasmtime_funcref_table_new(
|
|||||||
out: &mut *mut wasm_table_t,
|
out: &mut *mut wasm_table_t,
|
||||||
) -> Option<Box<wasmtime_error_t>> {
|
) -> Option<Box<wasmtime_error_t>> {
|
||||||
let init: Val = match init {
|
let init: Val = match init {
|
||||||
Some(val) => Val::FuncRef(val.func().borrow().clone()),
|
Some(val) => Val::FuncRef(Some(val.func().borrow().clone())),
|
||||||
None => Val::ExternRef(None),
|
None => Val::FuncRef(None),
|
||||||
};
|
};
|
||||||
handle_result(
|
handle_result(
|
||||||
Table::new(&store.store, tt.ty().ty.clone(), init),
|
Table::new(&store.store, tt.ty().ty.clone(), init),
|
||||||
@@ -85,7 +85,7 @@ pub extern "C" fn wasm_table_type(t: &wasm_table_t) -> Box<wasm_tabletype_t> {
|
|||||||
pub extern "C" fn wasm_table_get(t: &wasm_table_t, index: wasm_table_size_t) -> *mut wasm_ref_t {
|
pub extern "C" fn wasm_table_get(t: &wasm_table_t, index: wasm_table_size_t) -> *mut wasm_ref_t {
|
||||||
match t.table().borrow().get(index) {
|
match t.table().borrow().get(index) {
|
||||||
Some(val) => into_funcref(val),
|
Some(val) => into_funcref(val),
|
||||||
None => into_funcref(Val::ExternRef(None)),
|
None => into_funcref(Val::FuncRef(None)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,14 +99,14 @@ pub extern "C" fn wasmtime_funcref_table_get(
|
|||||||
Some(val) => {
|
Some(val) => {
|
||||||
*ptr = match val {
|
*ptr = match val {
|
||||||
// TODO: what do do about creating new `HostRef` handles here?
|
// TODO: what do do about creating new `HostRef` handles here?
|
||||||
Val::FuncRef(f) => {
|
Val::FuncRef(None) => ptr::null_mut(),
|
||||||
|
Val::FuncRef(Some(f)) => {
|
||||||
let store = match t.table().as_ref().store() {
|
let store = match t.table().as_ref().store() {
|
||||||
None => return false,
|
None => return false,
|
||||||
Some(store) => store,
|
Some(store) => store,
|
||||||
};
|
};
|
||||||
Box::into_raw(Box::new(HostRef::new(&store, f).into()))
|
Box::into_raw(Box::new(HostRef::new(&store, f).into()))
|
||||||
}
|
}
|
||||||
Val::ExternRef(None) => ptr::null_mut(),
|
|
||||||
_ => return false,
|
_ => return false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -133,14 +133,14 @@ pub extern "C" fn wasmtime_funcref_table_set(
|
|||||||
val: Option<&wasm_func_t>,
|
val: Option<&wasm_func_t>,
|
||||||
) -> Option<Box<wasmtime_error_t>> {
|
) -> Option<Box<wasmtime_error_t>> {
|
||||||
let val = match val {
|
let val = match val {
|
||||||
Some(val) => Val::FuncRef(val.func().borrow().clone()),
|
Some(val) => Val::FuncRef(Some(val.func().borrow().clone())),
|
||||||
None => Val::ExternRef(None),
|
None => Val::FuncRef(None),
|
||||||
};
|
};
|
||||||
handle_result(t.table().borrow().set(index, val), |()| {})
|
handle_result(t.table().borrow().set(index, val), |()| {})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_funcref(val: Val) -> *mut wasm_ref_t {
|
fn into_funcref(val: Val) -> *mut wasm_ref_t {
|
||||||
if let Val::ExternRef(None) = val {
|
if let Val::FuncRef(None) = val {
|
||||||
return ptr::null_mut();
|
return ptr::null_mut();
|
||||||
}
|
}
|
||||||
let externref = match val.externref() {
|
let externref = match val.externref() {
|
||||||
@@ -155,7 +155,7 @@ unsafe fn from_funcref(r: *mut wasm_ref_t) -> Val {
|
|||||||
if !r.is_null() {
|
if !r.is_null() {
|
||||||
Box::from_raw(r).r.into()
|
Box::from_raw(r).r.into()
|
||||||
} else {
|
} else {
|
||||||
Val::ExternRef(None)
|
Val::FuncRef(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,8 +182,8 @@ pub extern "C" fn wasmtime_funcref_table_grow(
|
|||||||
prev_size: Option<&mut wasm_table_size_t>,
|
prev_size: Option<&mut wasm_table_size_t>,
|
||||||
) -> Option<Box<wasmtime_error_t>> {
|
) -> Option<Box<wasmtime_error_t>> {
|
||||||
let val = match init {
|
let val = match init {
|
||||||
Some(val) => Val::FuncRef(val.func().borrow().clone()),
|
Some(val) => Val::FuncRef(Some(val.func().borrow().clone())),
|
||||||
None => Val::ExternRef(None),
|
None => Val::FuncRef(None),
|
||||||
};
|
};
|
||||||
handle_result(t.table().borrow().grow(delta, val), |prev| {
|
handle_result(t.table().borrow().grow(delta, val), |prev| {
|
||||||
if let Some(ptr) = prev_size {
|
if let Some(ptr) = prev_size {
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ use cranelift_codegen::ir::{AbiParam, ArgumentPurpose, Function, InstBuilder, Si
|
|||||||
use cranelift_codegen::isa::{self, TargetFrontendConfig};
|
use cranelift_codegen::isa::{self, TargetFrontendConfig};
|
||||||
use cranelift_entity::EntityRef;
|
use cranelift_entity::EntityRef;
|
||||||
use cranelift_wasm::{
|
use cranelift_wasm::{
|
||||||
self, FuncIndex, GlobalIndex, GlobalVariable, MemoryIndex, SignatureIndex, TableElementType,
|
self, FuncIndex, GlobalIndex, GlobalVariable, MemoryIndex, SignatureIndex, TableIndex,
|
||||||
TableIndex, TargetEnvironment, WasmError, WasmResult,
|
TargetEnvironment, WasmError, WasmResult, WasmType,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "lightbeam")]
|
#[cfg(feature = "lightbeam")]
|
||||||
use cranelift_wasm::{DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex};
|
use cranelift_wasm::{DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex};
|
||||||
@@ -68,6 +68,10 @@ macro_rules! declare_builtin_functions {
|
|||||||
AbiParam::new(self.reference_type)
|
AbiParam::new(self.reference_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pointer(&self) -> AbiParam {
|
||||||
|
AbiParam::new(self.pointer_type)
|
||||||
|
}
|
||||||
|
|
||||||
fn i32(&self) -> AbiParam {
|
fn i32(&self) -> AbiParam {
|
||||||
AbiParam::new(I32)
|
AbiParam::new(I32)
|
||||||
}
|
}
|
||||||
@@ -161,8 +165,10 @@ declare_builtin_functions! {
|
|||||||
memory_init(vmctx, i32, i32, i32, i32, i32) -> ();
|
memory_init(vmctx, i32, i32, i32, i32, i32) -> ();
|
||||||
/// Returns an index for wasm's `data.drop` instruction.
|
/// Returns an index for wasm's `data.drop` instruction.
|
||||||
data_drop(vmctx, i32) -> ();
|
data_drop(vmctx, i32) -> ();
|
||||||
|
/// Returns an index for Wasm's `table.grow` instruction for `funcref`s.
|
||||||
|
table_grow_funcref(vmctx, i32, i32, pointer) -> (i32);
|
||||||
/// Returns an index for Wasm's `table.grow` instruction for `externref`s.
|
/// Returns an index for Wasm's `table.grow` instruction for `externref`s.
|
||||||
table_grow_extern_ref(vmctx, i32, i32, reference) -> (i32);
|
table_grow_externref(vmctx, i32, i32, reference) -> (i32);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BuiltinFunctionIndex {
|
impl BuiltinFunctionIndex {
|
||||||
@@ -280,26 +286,6 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_table_grow_func(
|
|
||||||
&mut self,
|
|
||||||
func: &mut Function,
|
|
||||||
table_index: TableIndex,
|
|
||||||
) -> WasmResult<(ir::SigRef, BuiltinFunctionIndex, usize)> {
|
|
||||||
match self.module.table_plans[table_index].table.ty {
|
|
||||||
TableElementType::Func => Err(WasmError::Unsupported(
|
|
||||||
"the `table.grow` instruction is not supported with `funcref` yet".into(),
|
|
||||||
)),
|
|
||||||
TableElementType::Val(ty) => {
|
|
||||||
assert_eq!(ty, self.reference_type());
|
|
||||||
Ok((
|
|
||||||
self.builtin_function_signatures.table_grow_extern_ref(func),
|
|
||||||
BuiltinFunctionIndex::table_grow_extern_ref(),
|
|
||||||
table_index.as_u32() as usize,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_table_copy_func(
|
fn get_table_copy_func(
|
||||||
&mut self,
|
&mut self,
|
||||||
func: &mut Function,
|
func: &mut Function,
|
||||||
@@ -552,6 +538,10 @@ impl<'module_environment> TargetEnvironment for FuncEnvironment<'module_environm
|
|||||||
fn target_config(&self) -> TargetFrontendConfig {
|
fn target_config(&self) -> TargetFrontendConfig {
|
||||||
self.target_config
|
self.target_config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reference_type(&self, ty: WasmType) -> ir::Type {
|
||||||
|
crate::reference_type(ty, self.pointer_type())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'module_environment> {
|
impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'module_environment> {
|
||||||
@@ -604,9 +594,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
});
|
});
|
||||||
|
|
||||||
let element_size = match self.module.table_plans[index].style {
|
let element_size = match self.module.table_plans[index].style {
|
||||||
TableStyle::CallerChecksSignature => {
|
TableStyle::CallerChecksSignature => u64::from(self.pointer_type().bytes()),
|
||||||
u64::from(self.offsets.size_of_vmcaller_checked_anyfunc())
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(func.create_table(ir::TableData {
|
Ok(func.create_table(ir::TableData {
|
||||||
@@ -626,24 +614,34 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
delta: ir::Value,
|
delta: ir::Value,
|
||||||
init_value: ir::Value,
|
init_value: ir::Value,
|
||||||
) -> WasmResult<ir::Value> {
|
) -> WasmResult<ir::Value> {
|
||||||
let (func_sig, func_idx, table_index_arg) =
|
let (func_idx, func_sig) =
|
||||||
self.get_table_grow_func(&mut pos.func, table_index)?;
|
match self.module.table_plans[table_index].table.wasm_ty {
|
||||||
|
WasmType::FuncRef => (
|
||||||
let table_index_arg = pos.ins().iconst(I32, table_index_arg as i64);
|
BuiltinFunctionIndex::table_grow_funcref(),
|
||||||
|
self.builtin_function_signatures
|
||||||
|
.table_grow_funcref(&mut pos.func),
|
||||||
|
),
|
||||||
|
WasmType::ExternRef => (
|
||||||
|
BuiltinFunctionIndex::table_grow_externref(),
|
||||||
|
self.builtin_function_signatures
|
||||||
|
.table_grow_externref(&mut pos.func),
|
||||||
|
),
|
||||||
|
_ => return Err(WasmError::Unsupported(
|
||||||
|
"`table.grow` with a table element type that is not `funcref` or `externref`"
|
||||||
|
.into(),
|
||||||
|
)),
|
||||||
|
};
|
||||||
|
|
||||||
let (vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx);
|
let (vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx);
|
||||||
|
|
||||||
|
let table_index_arg = pos.ins().iconst(I32, table_index.as_u32() as i64);
|
||||||
let call_inst = pos.ins().call_indirect(
|
let call_inst = pos.ins().call_indirect(
|
||||||
func_sig,
|
func_sig,
|
||||||
func_addr,
|
func_addr,
|
||||||
&[vmctx, table_index_arg, delta, init_value],
|
&[vmctx, table_index_arg, delta, init_value],
|
||||||
);
|
);
|
||||||
Ok(pos
|
|
||||||
.func
|
Ok(pos.func.dfg.first_result(call_inst))
|
||||||
.dfg
|
|
||||||
.inst_results(call_inst)
|
|
||||||
.first()
|
|
||||||
.copied()
|
|
||||||
.unwrap())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn translate_table_get(
|
fn translate_table_get(
|
||||||
@@ -684,14 +682,50 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn translate_ref_null(
|
||||||
|
&mut self,
|
||||||
|
mut pos: cranelift_codegen::cursor::FuncCursor,
|
||||||
|
ty: WasmType,
|
||||||
|
) -> WasmResult<ir::Value> {
|
||||||
|
Ok(match ty {
|
||||||
|
WasmType::FuncRef => pos.ins().iconst(self.pointer_type(), 0),
|
||||||
|
WasmType::ExternRef => pos.ins().null(self.reference_type(ty)),
|
||||||
|
_ => {
|
||||||
|
return Err(WasmError::Unsupported(
|
||||||
|
"`ref.null T` that is not a `funcref` or an `externref`".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn translate_ref_is_null(
|
||||||
|
&mut self,
|
||||||
|
mut pos: cranelift_codegen::cursor::FuncCursor,
|
||||||
|
value: ir::Value,
|
||||||
|
) -> WasmResult<ir::Value> {
|
||||||
|
let bool_is_null = match pos.func.dfg.value_type(value) {
|
||||||
|
// `externref`
|
||||||
|
ty if ty.is_ref() => pos.ins().is_null(value),
|
||||||
|
// `funcref`
|
||||||
|
ty if ty == self.pointer_type() => {
|
||||||
|
pos.ins()
|
||||||
|
.icmp_imm(cranelift_codegen::ir::condcodes::IntCC::Equal, value, 0)
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(pos.ins().bint(ir::types::I32, bool_is_null))
|
||||||
|
}
|
||||||
|
|
||||||
fn translate_ref_func(
|
fn translate_ref_func(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: cranelift_codegen::cursor::FuncCursor<'_>,
|
mut pos: cranelift_codegen::cursor::FuncCursor<'_>,
|
||||||
_: FuncIndex,
|
func_index: FuncIndex,
|
||||||
) -> WasmResult<ir::Value> {
|
) -> WasmResult<ir::Value> {
|
||||||
Err(WasmError::Unsupported(
|
let vmctx = self.vmctx(&mut pos.func);
|
||||||
"the `ref.func` instruction is not supported yet".into(),
|
let vmctx = pos.ins().global_value(self.pointer_type(), vmctx);
|
||||||
))
|
let offset = self.offsets.vmctx_anyfunc(func_index);
|
||||||
|
Ok(pos.ins().iadd_imm(vmctx, i64::from(offset)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn translate_custom_global_get(
|
fn translate_custom_global_get(
|
||||||
@@ -861,18 +895,25 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
|
|
||||||
let table_entry_addr = pos.ins().table_addr(pointer_type, table, callee, 0);
|
let table_entry_addr = pos.ins().table_addr(pointer_type, table, callee, 0);
|
||||||
|
|
||||||
// Dereference table_entry_addr to get the function address.
|
// Dereference the table entry to get the pointer to the
|
||||||
|
// `VMCallerCheckedAnyfunc`.
|
||||||
|
let anyfunc_ptr =
|
||||||
|
pos.ins()
|
||||||
|
.load(pointer_type, ir::MemFlags::trusted(), table_entry_addr, 0);
|
||||||
|
|
||||||
|
// Check for whether the table element is null, and trap if so.
|
||||||
|
pos.ins()
|
||||||
|
.trapz(anyfunc_ptr, ir::TrapCode::IndirectCallToNull);
|
||||||
|
|
||||||
|
// Dereference anyfunc pointer to get the function address.
|
||||||
let mem_flags = ir::MemFlags::trusted();
|
let mem_flags = ir::MemFlags::trusted();
|
||||||
let func_addr = pos.ins().load(
|
let func_addr = pos.ins().load(
|
||||||
pointer_type,
|
pointer_type,
|
||||||
mem_flags,
|
mem_flags,
|
||||||
table_entry_addr,
|
anyfunc_ptr,
|
||||||
i32::from(self.offsets.vmcaller_checked_anyfunc_func_ptr()),
|
i32::from(self.offsets.vmcaller_checked_anyfunc_func_ptr()),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check whether `func_addr` is null.
|
|
||||||
pos.ins().trapz(func_addr, ir::TrapCode::IndirectCallToNull);
|
|
||||||
|
|
||||||
// If necessary, check the signature.
|
// If necessary, check the signature.
|
||||||
match self.module.table_plans[table_index].style {
|
match self.module.table_plans[table_index].style {
|
||||||
TableStyle::CallerChecksSignature => {
|
TableStyle::CallerChecksSignature => {
|
||||||
@@ -893,7 +934,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
let callee_sig_id = pos.ins().load(
|
let callee_sig_id = pos.ins().load(
|
||||||
sig_id_type,
|
sig_id_type,
|
||||||
mem_flags,
|
mem_flags,
|
||||||
table_entry_addr,
|
anyfunc_ptr,
|
||||||
i32::from(self.offsets.vmcaller_checked_anyfunc_type_index()),
|
i32::from(self.offsets.vmcaller_checked_anyfunc_type_index()),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -910,7 +951,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
let vmctx = pos.ins().load(
|
let vmctx = pos.ins().load(
|
||||||
pointer_type,
|
pointer_type,
|
||||||
mem_flags,
|
mem_flags,
|
||||||
table_entry_addr,
|
anyfunc_ptr,
|
||||||
i32::from(self.offsets.vmcaller_checked_anyfunc_vmctx()),
|
i32::from(self.offsets.vmcaller_checked_anyfunc_vmctx()),
|
||||||
);
|
);
|
||||||
real_call_args.push(vmctx);
|
real_call_args.push(vmctx);
|
||||||
|
|||||||
@@ -72,3 +72,18 @@ pub const WASM_MAX_PAGES: u32 = 0x10000;
|
|||||||
|
|
||||||
/// Version number of this crate.
|
/// Version number of this crate.
|
||||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
|
pub(crate) fn reference_type(
|
||||||
|
wasm_ty: cranelift_wasm::WasmType,
|
||||||
|
pointer_type: ir::Type,
|
||||||
|
) -> ir::Type {
|
||||||
|
match wasm_ty {
|
||||||
|
cranelift_wasm::WasmType::FuncRef => pointer_type,
|
||||||
|
cranelift_wasm::WasmType::ExternRef => match pointer_type {
|
||||||
|
ir::types::I32 => ir::types::R32,
|
||||||
|
ir::types::I64 => ir::types::R64,
|
||||||
|
_ => panic!("unsupported pointer type"),
|
||||||
|
},
|
||||||
|
_ => panic!("unsupported Wasm reference type"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -92,6 +92,10 @@ impl<'data> TargetEnvironment for ModuleEnvironment<'data> {
|
|||||||
fn target_config(&self) -> TargetFrontendConfig {
|
fn target_config(&self) -> TargetFrontendConfig {
|
||||||
self.result.target_config
|
self.result.target_config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reference_type(&self, ty: cranelift_wasm::WasmType) -> ir::Type {
|
||||||
|
crate::reference_type(ty, self.pointer_type())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This trait is useful for `translate_module` because it tells how to translate
|
/// This trait is useful for `translate_module` because it tells how to translate
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
// tables: [VMTableDefinition; module.num_defined_tables],
|
// tables: [VMTableDefinition; module.num_defined_tables],
|
||||||
// memories: [VMMemoryDefinition; module.num_defined_memories],
|
// memories: [VMMemoryDefinition; module.num_defined_memories],
|
||||||
// globals: [VMGlobalDefinition; module.num_defined_globals],
|
// globals: [VMGlobalDefinition; module.num_defined_globals],
|
||||||
|
// anyfuncs: [VMCallerCheckedAnyfunc; module.num_imported_functions + module.num_defined_functions],
|
||||||
// builtins: VMBuiltinFunctionsArray,
|
// builtins: VMBuiltinFunctionsArray,
|
||||||
// }
|
// }
|
||||||
|
|
||||||
@@ -62,6 +63,8 @@ pub struct VMOffsets {
|
|||||||
pub num_imported_memories: u32,
|
pub num_imported_memories: u32,
|
||||||
/// The number of imported globals in the module.
|
/// The number of imported globals in the module.
|
||||||
pub num_imported_globals: u32,
|
pub num_imported_globals: u32,
|
||||||
|
/// The number of defined functions in the module.
|
||||||
|
pub num_defined_functions: u32,
|
||||||
/// The number of defined tables in the module.
|
/// The number of defined tables in the module.
|
||||||
pub num_defined_tables: u32,
|
pub num_defined_tables: u32,
|
||||||
/// The number of defined memories in the module.
|
/// The number of defined memories in the module.
|
||||||
@@ -80,6 +83,7 @@ impl VMOffsets {
|
|||||||
num_imported_tables: cast_to_u32(module.num_imported_tables),
|
num_imported_tables: cast_to_u32(module.num_imported_tables),
|
||||||
num_imported_memories: cast_to_u32(module.num_imported_memories),
|
num_imported_memories: cast_to_u32(module.num_imported_memories),
|
||||||
num_imported_globals: cast_to_u32(module.num_imported_globals),
|
num_imported_globals: cast_to_u32(module.num_imported_globals),
|
||||||
|
num_defined_functions: cast_to_u32(module.functions.len()),
|
||||||
num_defined_tables: cast_to_u32(module.table_plans.len()),
|
num_defined_tables: cast_to_u32(module.table_plans.len()),
|
||||||
num_defined_memories: cast_to_u32(module.memory_plans.len()),
|
num_defined_memories: cast_to_u32(module.memory_plans.len()),
|
||||||
num_defined_globals: cast_to_u32(module.globals.len()),
|
num_defined_globals: cast_to_u32(module.globals.len()),
|
||||||
@@ -390,8 +394,8 @@ impl VMOffsets {
|
|||||||
align(offset, 16)
|
align(offset, 16)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The offset of the builtin functions array.
|
/// The offset of the `anyfuncs` array.
|
||||||
pub fn vmctx_builtin_functions_begin(&self) -> u32 {
|
pub fn vmctx_anyfuncs_begin(&self) -> u32 {
|
||||||
self.vmctx_globals_begin()
|
self.vmctx_globals_begin()
|
||||||
.checked_add(
|
.checked_add(
|
||||||
self.num_defined_globals
|
self.num_defined_globals
|
||||||
@@ -401,6 +405,19 @@ impl VMOffsets {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The offset of the builtin functions array.
|
||||||
|
pub fn vmctx_builtin_functions_begin(&self) -> u32 {
|
||||||
|
self.vmctx_anyfuncs_begin()
|
||||||
|
.checked_add(
|
||||||
|
self.num_imported_functions
|
||||||
|
.checked_add(self.num_defined_functions)
|
||||||
|
.unwrap()
|
||||||
|
.checked_mul(u32::from(self.size_of_vmcaller_checked_anyfunc()))
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the size of the `VMContext` allocation.
|
/// Return the size of the `VMContext` allocation.
|
||||||
pub fn size_of_vmctx(&self) -> u32 {
|
pub fn size_of_vmctx(&self) -> u32 {
|
||||||
self.vmctx_builtin_functions_begin()
|
self.vmctx_builtin_functions_begin()
|
||||||
@@ -516,6 +533,19 @@ impl VMOffsets {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the offset to the `VMCallerCheckedAnyfunc` for the given function
|
||||||
|
/// index (either imported or defined).
|
||||||
|
pub fn vmctx_anyfunc(&self, index: FuncIndex) -> u32 {
|
||||||
|
self.vmctx_anyfuncs_begin()
|
||||||
|
.checked_add(
|
||||||
|
index
|
||||||
|
.as_u32()
|
||||||
|
.checked_mul(u32::from(self.size_of_vmcaller_checked_anyfunc()))
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the offset to the `body` field in `*const VMFunctionBody` index `index`.
|
/// Return the offset to the `body` field in `*const VMFunctionBody` index `index`.
|
||||||
pub fn vmctx_vmfunction_import_body(&self, index: FuncIndex) -> u32 {
|
pub fn vmctx_vmfunction_import_body(&self, index: FuncIndex) -> u32 {
|
||||||
self.vmctx_vmfunction_import(index)
|
self.vmctx_vmfunction_import(index)
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ pub fn resolve_imports(
|
|||||||
match (import, &export) {
|
match (import, &export) {
|
||||||
(EntityIndex::Function(func_index), Some(Export::Function(f))) => {
|
(EntityIndex::Function(func_index), Some(Export::Function(f))) => {
|
||||||
let import_signature = module.local.native_func_signature(*func_index);
|
let import_signature = module.local.native_func_signature(*func_index);
|
||||||
let signature = signatures.lookup_native(f.signature).unwrap();
|
let signature = signatures
|
||||||
|
.lookup_native(unsafe { f.anyfunc.as_ref().type_index })
|
||||||
|
.unwrap();
|
||||||
if signature != *import_signature {
|
if signature != *import_signature {
|
||||||
// TODO: If the difference is in the calling convention,
|
// TODO: If the difference is in the calling convention,
|
||||||
// we could emit a wrapper function to fix it up.
|
// we could emit a wrapper function to fix it up.
|
||||||
@@ -43,8 +45,8 @@ pub fn resolve_imports(
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
function_imports.push(VMFunctionImport {
|
function_imports.push(VMFunctionImport {
|
||||||
body: f.address,
|
body: unsafe { f.anyfunc.as_ref().func_ptr },
|
||||||
vmctx: f.vmctx,
|
vmctx: unsafe { f.anyfunc.as_ref().vmctx },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
(EntityIndex::Function(_), Some(_)) => {
|
(EntityIndex::Function(_), Some(_)) => {
|
||||||
@@ -169,16 +171,20 @@ fn is_global_compatible(exported: &Global, imported: &Global) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let Global {
|
let Global {
|
||||||
|
wasm_ty: exported_wasm_ty,
|
||||||
ty: exported_ty,
|
ty: exported_ty,
|
||||||
mutability: exported_mutability,
|
mutability: exported_mutability,
|
||||||
initializer: _exported_initializer,
|
initializer: _exported_initializer,
|
||||||
} = exported;
|
} = exported;
|
||||||
let Global {
|
let Global {
|
||||||
|
wasm_ty: imported_wasm_ty,
|
||||||
ty: imported_ty,
|
ty: imported_ty,
|
||||||
mutability: imported_mutability,
|
mutability: imported_mutability,
|
||||||
initializer: _imported_initializer,
|
initializer: _imported_initializer,
|
||||||
} = imported;
|
} = imported;
|
||||||
exported_ty == imported_ty && imported_mutability == exported_mutability
|
exported_wasm_ty == imported_wasm_ty
|
||||||
|
&& exported_ty == imported_ty
|
||||||
|
&& imported_mutability == exported_mutability
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_table_element_type_compatible(
|
fn is_table_element_type_compatible(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::vmcontext::{
|
use crate::vmcontext::{
|
||||||
VMContext, VMFunctionBody, VMGlobalDefinition, VMMemoryDefinition, VMSharedSignatureIndex,
|
VMCallerCheckedAnyfunc, VMContext, VMGlobalDefinition, VMMemoryDefinition, VMTableDefinition,
|
||||||
VMTableDefinition,
|
|
||||||
};
|
};
|
||||||
|
use std::ptr::NonNull;
|
||||||
use wasmtime_environ::wasm::Global;
|
use wasmtime_environ::wasm::Global;
|
||||||
use wasmtime_environ::{MemoryPlan, TablePlan};
|
use wasmtime_environ::{MemoryPlan, TablePlan};
|
||||||
|
|
||||||
@@ -24,14 +24,11 @@ pub enum Export {
|
|||||||
/// A function export value.
|
/// A function export value.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ExportFunction {
|
pub struct ExportFunction {
|
||||||
/// The address of the native-code function.
|
/// The `VMCallerCheckedAnyfunc` for this exported function.
|
||||||
pub address: *const VMFunctionBody,
|
|
||||||
/// Pointer to the containing `VMContext`.
|
|
||||||
pub vmctx: *mut VMContext,
|
|
||||||
/// The function signature declaration, used for compatibilty checking.
|
|
||||||
///
|
///
|
||||||
/// Note that this indexes within the module associated with `vmctx`.
|
/// Note that exported functions cannot be a null funcref, so this is a
|
||||||
pub signature: VMSharedSignatureIndex,
|
/// non-null pointer.
|
||||||
|
pub anyfunc: NonNull<VMCallerCheckedAnyfunc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ExportFunction> for Export {
|
impl From<ExportFunction> for Export {
|
||||||
|
|||||||
@@ -545,7 +545,10 @@ impl VMExternRefActivationsTable {
|
|||||||
return Err(externref);
|
return Err(externref);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_assert!((*next.as_ref().get()).is_none());
|
debug_assert!(
|
||||||
|
(*next.as_ref().get()).is_none(),
|
||||||
|
"slots >= the `next` bump finger are always `None`"
|
||||||
|
);
|
||||||
ptr::write(next.as_ptr(), UnsafeCell::new(Some(externref)));
|
ptr::write(next.as_ptr(), UnsafeCell::new(Some(externref)));
|
||||||
|
|
||||||
let next = NonNull::new_unchecked(next.as_ptr().add(1));
|
let next = NonNull::new_unchecked(next.as_ptr().add(1));
|
||||||
@@ -1121,6 +1124,7 @@ mod tests {
|
|||||||
num_imported_tables: 0,
|
num_imported_tables: 0,
|
||||||
num_imported_memories: 0,
|
num_imported_memories: 0,
|
||||||
num_imported_globals: 0,
|
num_imported_globals: 0,
|
||||||
|
num_defined_functions: 0,
|
||||||
num_defined_tables: 0,
|
num_defined_tables: 0,
|
||||||
num_defined_memories: 0,
|
num_defined_memories: 0,
|
||||||
num_defined_globals: 0,
|
num_defined_globals: 0,
|
||||||
@@ -1147,6 +1151,7 @@ mod tests {
|
|||||||
num_imported_tables: 0,
|
num_imported_tables: 0,
|
||||||
num_imported_memories: 0,
|
num_imported_memories: 0,
|
||||||
num_imported_globals: 0,
|
num_imported_globals: 0,
|
||||||
|
num_defined_functions: 0,
|
||||||
num_defined_tables: 0,
|
num_defined_tables: 0,
|
||||||
num_defined_memories: 0,
|
num_defined_memories: 0,
|
||||||
num_defined_globals: 0,
|
num_defined_globals: 0,
|
||||||
|
|||||||
@@ -21,13 +21,15 @@ use std::any::Any;
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
use std::ptr::NonNull;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{mem, ptr, slice};
|
use std::{mem, ptr, slice};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use wasmtime_environ::entity::{packed_option::ReservedValue, BoxedSlice, EntityRef, PrimaryMap};
|
use wasmtime_environ::entity::{packed_option::ReservedValue, BoxedSlice, EntityRef, PrimaryMap};
|
||||||
use wasmtime_environ::wasm::{
|
use wasmtime_environ::wasm::{
|
||||||
DataIndex, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex,
|
DataIndex, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex,
|
||||||
ElemIndex, FuncIndex, GlobalIndex, GlobalInit, MemoryIndex, SignatureIndex, TableIndex,
|
ElemIndex, FuncIndex, GlobalIndex, GlobalInit, MemoryIndex, SignatureIndex, TableElementType,
|
||||||
|
TableIndex,
|
||||||
};
|
};
|
||||||
use wasmtime_environ::{ir, DataInitializer, EntityIndex, Module, TableElements, VMOffsets};
|
use wasmtime_environ::{ir, DataInitializer, EntityIndex, Module, TableElements, VMOffsets};
|
||||||
|
|
||||||
@@ -54,7 +56,7 @@ pub(crate) struct Instance {
|
|||||||
/// Passive elements in this instantiation. As `elem.drop`s happen, these
|
/// Passive elements in this instantiation. As `elem.drop`s happen, these
|
||||||
/// entries get removed. A missing entry is considered equivalent to an
|
/// entries get removed. A missing entry is considered equivalent to an
|
||||||
/// empty slice.
|
/// empty slice.
|
||||||
passive_elements: RefCell<HashMap<ElemIndex, Box<[VMCallerCheckedAnyfunc]>>>,
|
passive_elements: RefCell<HashMap<ElemIndex, Box<[*mut VMCallerCheckedAnyfunc]>>>,
|
||||||
|
|
||||||
/// Passive data segments from our module. As `data.drop`s happen, entries
|
/// Passive data segments from our module. As `data.drop`s happen, entries
|
||||||
/// get removed. A missing entry is considered equivalent to an empty slice.
|
/// get removed. A missing entry is considered equivalent to an empty slice.
|
||||||
@@ -273,23 +275,10 @@ impl Instance {
|
|||||||
pub fn lookup_by_declaration(&self, export: &EntityIndex) -> Export {
|
pub fn lookup_by_declaration(&self, export: &EntityIndex) -> Export {
|
||||||
match export {
|
match export {
|
||||||
EntityIndex::Function(index) => {
|
EntityIndex::Function(index) => {
|
||||||
let signature = self.signature_id(self.module.local.functions[*index]);
|
let anyfunc = self.get_caller_checked_anyfunc(*index).unwrap();
|
||||||
let (address, vmctx) =
|
let anyfunc =
|
||||||
if let Some(def_index) = self.module.local.defined_func_index(*index) {
|
NonNull::new(anyfunc as *const VMCallerCheckedAnyfunc as *mut _).unwrap();
|
||||||
(
|
ExportFunction { anyfunc }.into()
|
||||||
self.finished_functions[def_index] as *const _,
|
|
||||||
self.vmctx_ptr(),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
let import = self.imported_function(*index);
|
|
||||||
(import.body, import.vmctx)
|
|
||||||
};
|
|
||||||
ExportFunction {
|
|
||||||
address,
|
|
||||||
signature,
|
|
||||||
vmctx,
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
EntityIndex::Table(index) => {
|
EntityIndex::Table(index) => {
|
||||||
let (definition, vmctx) =
|
let (definition, vmctx) =
|
||||||
@@ -448,6 +437,11 @@ impl Instance {
|
|||||||
foreign_instance.memory_size(foreign_index)
|
foreign_instance.memory_size(foreign_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn table_element_type(&self, table_index: TableIndex) -> TableElementType {
|
||||||
|
let table = self.get_table(table_index);
|
||||||
|
table.element_type()
|
||||||
|
}
|
||||||
|
|
||||||
/// Grow table by the specified amount of elements, filling them with
|
/// Grow table by the specified amount of elements, filling them with
|
||||||
/// `init_value`.
|
/// `init_value`.
|
||||||
///
|
///
|
||||||
@@ -513,30 +507,25 @@ impl Instance {
|
|||||||
Layout::from_size_align(size, align).unwrap()
|
Layout::from_size_align(size, align).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a `VMCallerCheckedAnyfunc` for the given `FuncIndex`.
|
/// Get a `&VMCallerCheckedAnyfunc` for the given `FuncIndex`.
|
||||||
fn get_caller_checked_anyfunc(&self, index: FuncIndex) -> VMCallerCheckedAnyfunc {
|
///
|
||||||
|
/// Returns `None` if the index is the reserved index value.
|
||||||
|
///
|
||||||
|
/// The returned reference is a stable reference that won't be moved and can
|
||||||
|
/// be passed into JIT code.
|
||||||
|
pub(crate) fn get_caller_checked_anyfunc(
|
||||||
|
&self,
|
||||||
|
index: FuncIndex,
|
||||||
|
) -> Option<&VMCallerCheckedAnyfunc> {
|
||||||
if index == FuncIndex::reserved_value() {
|
if index == FuncIndex::reserved_value() {
|
||||||
return VMCallerCheckedAnyfunc::default();
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let sig = self.module.local.functions[index];
|
Some(unsafe { &*self.anyfunc_ptr(index) })
|
||||||
let type_index = self.signature_id(sig);
|
|
||||||
|
|
||||||
let (func_ptr, vmctx) = if let Some(def_index) = self.module.local.defined_func_index(index)
|
|
||||||
{
|
|
||||||
(
|
|
||||||
self.finished_functions[def_index] as *const _,
|
|
||||||
self.vmctx_ptr(),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
let import = self.imported_function(index);
|
|
||||||
(import.body, import.vmctx)
|
|
||||||
};
|
|
||||||
VMCallerCheckedAnyfunc {
|
|
||||||
func_ptr,
|
|
||||||
type_index,
|
|
||||||
vmctx,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn anyfunc_ptr(&self, index: FuncIndex) -> *mut VMCallerCheckedAnyfunc {
|
||||||
|
self.vmctx_plus_offset(self.offsets.vmctx_anyfunc(index))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The `table.init` operation: initializes a portion of a table with a
|
/// The `table.init` operation: initializes a portion of a table with a
|
||||||
@@ -574,7 +563,7 @@ impl Instance {
|
|||||||
// TODO(#983): investigate replacing this get/set loop with a `memcpy`.
|
// TODO(#983): investigate replacing this get/set loop with a `memcpy`.
|
||||||
for (dst, src) in (dst..dst + len).zip(src..src + len) {
|
for (dst, src) in (dst..dst + len).zip(src..src + len) {
|
||||||
table
|
table
|
||||||
.set(dst, TableElement::FuncRef(elem[src as usize].clone()))
|
.set(dst, TableElement::FuncRef(elem[src as usize]))
|
||||||
.expect("should never panic because we already did the bounds check above");
|
.expect("should never panic because we already did the bounds check above");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -932,6 +921,30 @@ impl InstanceHandle {
|
|||||||
*instance.externref_activations_table() = externref_activations_table;
|
*instance.externref_activations_table() = externref_activations_table;
|
||||||
*instance.stack_map_registry() = stack_map_registry;
|
*instance.stack_map_registry() = stack_map_registry;
|
||||||
|
|
||||||
|
for (index, sig) in instance.module.local.functions.iter() {
|
||||||
|
let type_index = instance.signature_id(*sig);
|
||||||
|
|
||||||
|
let (func_ptr, vmctx) =
|
||||||
|
if let Some(def_index) = instance.module.local.defined_func_index(index) {
|
||||||
|
(
|
||||||
|
NonNull::new(instance.finished_functions[def_index] as *mut _).unwrap(),
|
||||||
|
instance.vmctx_ptr(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let import = instance.imported_function(index);
|
||||||
|
(import.body, import.vmctx)
|
||||||
|
};
|
||||||
|
|
||||||
|
ptr::write(
|
||||||
|
instance.anyfunc_ptr(index),
|
||||||
|
VMCallerCheckedAnyfunc {
|
||||||
|
func_ptr,
|
||||||
|
type_index,
|
||||||
|
vmctx,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Perform infallible initialization in this constructor, while fallible
|
// Perform infallible initialization in this constructor, while fallible
|
||||||
// initialization is deferred to the `initialize` method.
|
// initialization is deferred to the `initialize` method.
|
||||||
initialize_passive_elements(instance);
|
initialize_passive_elements(instance);
|
||||||
@@ -1246,12 +1259,14 @@ fn initialize_tables(instance: &Instance) -> Result<(), InstantiationError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i, func_idx) in init.elements.iter().enumerate() {
|
for (i, func_idx) in init.elements.iter().enumerate() {
|
||||||
let anyfunc = instance.get_caller_checked_anyfunc(*func_idx);
|
let anyfunc = instance.get_caller_checked_anyfunc(*func_idx).map_or(
|
||||||
|
ptr::null_mut(),
|
||||||
|
|f: &VMCallerCheckedAnyfunc| {
|
||||||
|
f as *const VMCallerCheckedAnyfunc as *mut VMCallerCheckedAnyfunc
|
||||||
|
},
|
||||||
|
);
|
||||||
table
|
table
|
||||||
.set(
|
.set(u32::try_from(start + i).unwrap(), anyfunc.into())
|
||||||
u32::try_from(start + i).unwrap(),
|
|
||||||
TableElement::FuncRef(anyfunc),
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1280,7 +1295,14 @@ fn initialize_passive_elements(instance: &Instance) {
|
|||||||
*idx,
|
*idx,
|
||||||
segments
|
segments
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| instance.get_caller_checked_anyfunc(*s))
|
.map(|s| {
|
||||||
|
instance.get_caller_checked_anyfunc(*s).map_or(
|
||||||
|
ptr::null_mut(),
|
||||||
|
|f: &VMCallerCheckedAnyfunc| {
|
||||||
|
f as *const VMCallerCheckedAnyfunc as *mut _
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -56,3 +56,14 @@ pub use crate::vmcontext::{
|
|||||||
|
|
||||||
/// Version number of this crate.
|
/// Version number of this crate.
|
||||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
|
/// The Cranelift IR type used for reference types for this target architecture.
|
||||||
|
pub fn ref_type() -> wasmtime_environ::ir::Type {
|
||||||
|
if cfg!(target_pointer_width = "32") {
|
||||||
|
wasmtime_environ::ir::types::R32
|
||||||
|
} else if cfg!(target_pointer_width = "64") {
|
||||||
|
wasmtime_environ::ir::types::R64
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -57,10 +57,12 @@
|
|||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use crate::externref::VMExternRef;
|
use crate::externref::VMExternRef;
|
||||||
use crate::table::{Table, TableElement};
|
use crate::table::Table;
|
||||||
use crate::traphandlers::raise_lib_trap;
|
use crate::traphandlers::raise_lib_trap;
|
||||||
use crate::vmcontext::VMContext;
|
use crate::vmcontext::{VMCallerCheckedAnyfunc, VMContext};
|
||||||
use wasmtime_environ::wasm::{DataIndex, DefinedMemoryIndex, ElemIndex, MemoryIndex, TableIndex};
|
use wasmtime_environ::wasm::{
|
||||||
|
DataIndex, DefinedMemoryIndex, ElemIndex, MemoryIndex, TableElementType, TableIndex,
|
||||||
|
};
|
||||||
|
|
||||||
/// Implementation of f32.ceil
|
/// Implementation of f32.ceil
|
||||||
pub extern "C" fn wasmtime_f32_ceil(x: f32) -> f32 {
|
pub extern "C" fn wasmtime_f32_ceil(x: f32) -> f32 {
|
||||||
@@ -226,24 +228,38 @@ pub unsafe extern "C" fn wasmtime_imported_memory32_size(
|
|||||||
instance.imported_memory_size(memory_index)
|
instance.imported_memory_size(memory_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implementation of `table.grow` for `externref`s.
|
/// Implementation of `table.grow`.
|
||||||
pub unsafe extern "C" fn wasmtime_table_grow_extern_ref(
|
pub unsafe extern "C" fn wasmtime_table_grow(
|
||||||
vmctx: *mut VMContext,
|
vmctx: *mut VMContext,
|
||||||
table_index: u32,
|
table_index: u32,
|
||||||
delta: u32,
|
delta: u32,
|
||||||
|
// NB: we don't know whether this is a pointer to a `VMCallerCheckedAnyfunc`
|
||||||
|
// or is a `VMExternRef` until we look at the table type.
|
||||||
init_value: *mut u8,
|
init_value: *mut u8,
|
||||||
) -> u32 {
|
) -> u32 {
|
||||||
|
let instance = (&mut *vmctx).instance();
|
||||||
|
let table_index = TableIndex::from_u32(table_index);
|
||||||
|
match instance.table_element_type(table_index) {
|
||||||
|
TableElementType::Func => {
|
||||||
|
let func = init_value as *mut VMCallerCheckedAnyfunc;
|
||||||
|
instance
|
||||||
|
.table_grow(table_index, delta, func.into())
|
||||||
|
.unwrap_or(-1_i32 as u32)
|
||||||
|
}
|
||||||
|
TableElementType::Val(ty) => {
|
||||||
|
debug_assert_eq!(ty, crate::ref_type());
|
||||||
|
|
||||||
let init_value = if init_value.is_null() {
|
let init_value = if init_value.is_null() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(VMExternRef::clone_from_raw(init_value))
|
Some(VMExternRef::clone_from_raw(init_value))
|
||||||
};
|
};
|
||||||
|
|
||||||
let instance = (&mut *vmctx).instance();
|
|
||||||
let table_index = TableIndex::from_u32(table_index);
|
|
||||||
instance
|
instance
|
||||||
.table_grow(table_index, delta, TableElement::ExternRef(init_value))
|
.table_grow(table_index, delta, init_value.into())
|
||||||
.unwrap_or(-1_i32 as u32)
|
.unwrap_or(-1_i32 as u32)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implementation of `table.copy`.
|
/// Implementation of `table.copy`.
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use crate::vmcontext::{VMCallerCheckedAnyfunc, VMTableDefinition};
|
|||||||
use crate::{Trap, VMExternRef};
|
use crate::{Trap, VMExternRef};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::convert::{TryFrom, TryInto};
|
use std::convert::{TryFrom, TryInto};
|
||||||
|
use std::ptr;
|
||||||
use wasmtime_environ::wasm::TableElementType;
|
use wasmtime_environ::wasm::TableElementType;
|
||||||
use wasmtime_environ::{ir, TablePlan, TableStyle};
|
use wasmtime_environ::{ir, TablePlan, TableStyle};
|
||||||
|
|
||||||
@@ -20,34 +21,27 @@ pub struct Table {
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum TableElement {
|
pub enum TableElement {
|
||||||
/// A `funcref`.
|
/// A `funcref`.
|
||||||
FuncRef(VMCallerCheckedAnyfunc),
|
FuncRef(*mut VMCallerCheckedAnyfunc),
|
||||||
/// An `exrernref`.
|
/// An `exrernref`.
|
||||||
ExternRef(Option<VMExternRef>),
|
ExternRef(Option<VMExternRef>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum TableElements {
|
enum TableElements {
|
||||||
FuncRefs(Vec<VMCallerCheckedAnyfunc>),
|
FuncRefs(Vec<*mut VMCallerCheckedAnyfunc>),
|
||||||
ExternRefs(Vec<Option<VMExternRef>>),
|
ExternRefs(Vec<Option<VMExternRef>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Table {
|
impl Table {
|
||||||
/// Create a new table instance with specified minimum and maximum number of elements.
|
/// Create a new table instance with specified minimum and maximum number of elements.
|
||||||
pub fn new(plan: &TablePlan) -> Self {
|
pub fn new(plan: &TablePlan) -> Self {
|
||||||
let elements =
|
|
||||||
RefCell::new(match plan.table.ty {
|
|
||||||
TableElementType::Func => TableElements::FuncRefs(vec![
|
|
||||||
VMCallerCheckedAnyfunc::default();
|
|
||||||
usize::try_from(plan.table.minimum).unwrap()
|
|
||||||
]),
|
|
||||||
TableElementType::Val(ty)
|
|
||||||
if (cfg!(target_pointer_width = "64") && ty == ir::types::R64)
|
|
||||||
|| (cfg!(target_pointer_width = "32") && ty == ir::types::R32) =>
|
|
||||||
{
|
|
||||||
let min = usize::try_from(plan.table.minimum).unwrap();
|
let min = usize::try_from(plan.table.minimum).unwrap();
|
||||||
|
let elements = RefCell::new(match plan.table.ty {
|
||||||
|
TableElementType::Func => TableElements::FuncRefs(vec![ptr::null_mut(); min]),
|
||||||
|
TableElementType::Val(ty) => {
|
||||||
|
debug_assert_eq!(ty, crate::ref_type());
|
||||||
TableElements::ExternRefs(vec![None; min])
|
TableElements::ExternRefs(vec![None; min])
|
||||||
}
|
}
|
||||||
TableElementType::Val(ty) => unimplemented!("unsupported table type ({})", ty),
|
|
||||||
});
|
});
|
||||||
match plan.style {
|
match plan.style {
|
||||||
TableStyle::CallerChecksSignature => Self {
|
TableStyle::CallerChecksSignature => Self {
|
||||||
@@ -57,6 +51,14 @@ impl Table {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the type of the elements in this table.
|
||||||
|
pub fn element_type(&self) -> TableElementType {
|
||||||
|
match &*self.elements.borrow() {
|
||||||
|
TableElements::FuncRefs(_) => TableElementType::Func,
|
||||||
|
TableElements::ExternRefs(_) => TableElementType::Val(crate::ref_type()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the number of allocated elements.
|
/// Returns the number of allocated elements.
|
||||||
pub fn size(&self) -> u32 {
|
pub fn size(&self) -> u32 {
|
||||||
match &*self.elements.borrow() {
|
match &*self.elements.borrow() {
|
||||||
@@ -199,7 +201,7 @@ impl Table {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<TableElement> for VMCallerCheckedAnyfunc {
|
impl TryFrom<TableElement> for *mut VMCallerCheckedAnyfunc {
|
||||||
type Error = TableElement;
|
type Error = TableElement;
|
||||||
|
|
||||||
fn try_from(e: TableElement) -> Result<Self, Self::Error> {
|
fn try_from(e: TableElement) -> Result<Self, Self::Error> {
|
||||||
@@ -221,8 +223,8 @@ impl TryFrom<TableElement> for Option<VMExternRef> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<VMCallerCheckedAnyfunc> for TableElement {
|
impl From<*mut VMCallerCheckedAnyfunc> for TableElement {
|
||||||
fn from(f: VMCallerCheckedAnyfunc) -> TableElement {
|
fn from(f: *mut VMCallerCheckedAnyfunc) -> TableElement {
|
||||||
TableElement::FuncRef(f)
|
TableElement::FuncRef(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,9 @@
|
|||||||
|
|
||||||
use crate::instance::Instance;
|
use crate::instance::Instance;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
use std::ptr::NonNull;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
|
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
|
||||||
use std::{ptr, u32};
|
use std::u32;
|
||||||
use wasmtime_environ::BuiltinFunctionIndex;
|
use wasmtime_environ::BuiltinFunctionIndex;
|
||||||
|
|
||||||
/// An imported function.
|
/// An imported function.
|
||||||
@@ -12,7 +13,7 @@ use wasmtime_environ::BuiltinFunctionIndex;
|
|||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct VMFunctionImport {
|
pub struct VMFunctionImport {
|
||||||
/// A pointer to the imported function body.
|
/// A pointer to the imported function body.
|
||||||
pub body: *const VMFunctionBody,
|
pub body: NonNull<VMFunctionBody>,
|
||||||
|
|
||||||
/// A pointer to the `VMContext` that owns the function.
|
/// A pointer to the `VMContext` that owns the function.
|
||||||
pub vmctx: *mut VMContext,
|
pub vmctx: *mut VMContext,
|
||||||
@@ -475,7 +476,7 @@ impl Default for VMSharedSignatureIndex {
|
|||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct VMCallerCheckedAnyfunc {
|
pub struct VMCallerCheckedAnyfunc {
|
||||||
/// Function body.
|
/// Function body.
|
||||||
pub func_ptr: *const VMFunctionBody,
|
pub func_ptr: NonNull<VMFunctionBody>,
|
||||||
/// Function signature id.
|
/// Function signature id.
|
||||||
pub type_index: VMSharedSignatureIndex,
|
pub type_index: VMSharedSignatureIndex,
|
||||||
/// Function `VMContext`.
|
/// Function `VMContext`.
|
||||||
@@ -513,16 +514,6 @@ mod test_vmcaller_checked_anyfunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for VMCallerCheckedAnyfunc {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
func_ptr: ptr::null_mut(),
|
|
||||||
type_index: Default::default(),
|
|
||||||
vmctx: ptr::null_mut(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An array that stores addresses of builtin functions. We translate code
|
/// An array that stores addresses of builtin functions. We translate code
|
||||||
/// to use indirect calls. This way, we don't have to patch the code.
|
/// to use indirect calls. This way, we don't have to patch the code.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@@ -549,8 +540,10 @@ impl VMBuiltinFunctionsArray {
|
|||||||
ptrs[BuiltinFunctionIndex::imported_memory32_size().index() as usize] =
|
ptrs[BuiltinFunctionIndex::imported_memory32_size().index() as usize] =
|
||||||
wasmtime_imported_memory32_size as usize;
|
wasmtime_imported_memory32_size as usize;
|
||||||
ptrs[BuiltinFunctionIndex::table_copy().index() as usize] = wasmtime_table_copy as usize;
|
ptrs[BuiltinFunctionIndex::table_copy().index() as usize] = wasmtime_table_copy as usize;
|
||||||
ptrs[BuiltinFunctionIndex::table_grow_extern_ref().index() as usize] =
|
ptrs[BuiltinFunctionIndex::table_grow_funcref().index() as usize] =
|
||||||
wasmtime_table_grow_extern_ref as usize;
|
wasmtime_table_grow as usize;
|
||||||
|
ptrs[BuiltinFunctionIndex::table_grow_externref().index() as usize] =
|
||||||
|
wasmtime_table_grow as usize;
|
||||||
ptrs[BuiltinFunctionIndex::table_init().index() as usize] = wasmtime_table_init as usize;
|
ptrs[BuiltinFunctionIndex::table_init().index() as usize] = wasmtime_table_init as usize;
|
||||||
ptrs[BuiltinFunctionIndex::elem_drop().index() as usize] = wasmtime_elem_drop as usize;
|
ptrs[BuiltinFunctionIndex::elem_drop().index() as usize] = wasmtime_elem_drop as usize;
|
||||||
ptrs[BuiltinFunctionIndex::defined_memory_copy().index() as usize] =
|
ptrs[BuiltinFunctionIndex::defined_memory_copy().index() as usize] =
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ impl Global {
|
|||||||
|
|
||||||
/// Returns the value type of this `global`.
|
/// Returns the value type of this `global`.
|
||||||
pub fn val_type(&self) -> ValType {
|
pub fn val_type(&self) -> ValType {
|
||||||
ValType::from_wasmtime_type(self.wasmtime_export.global.ty)
|
ValType::from_wasm_type(&self.wasmtime_export.global.wasm_ty)
|
||||||
.expect("core wasm type should be supported")
|
.expect("core wasm type should be supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,14 +298,10 @@ fn set_table_item(
|
|||||||
instance: &InstanceHandle,
|
instance: &InstanceHandle,
|
||||||
table_index: wasm::DefinedTableIndex,
|
table_index: wasm::DefinedTableIndex,
|
||||||
item_index: u32,
|
item_index: u32,
|
||||||
item: wasmtime_runtime::VMCallerCheckedAnyfunc,
|
item: *mut wasmtime_runtime::VMCallerCheckedAnyfunc,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
instance
|
instance
|
||||||
.table_set(
|
.table_set(table_index, item_index, item.into())
|
||||||
table_index,
|
|
||||||
item_index,
|
|
||||||
runtime::TableElement::FuncRef(item),
|
|
||||||
)
|
|
||||||
.map_err(|()| anyhow!("table element index out of bounds"))
|
.map_err(|()| anyhow!("table element index out of bounds"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,7 +325,7 @@ impl Table {
|
|||||||
let definition = unsafe { &*wasmtime_export.definition };
|
let definition = unsafe { &*wasmtime_export.definition };
|
||||||
let index = instance.table_index(definition);
|
let index = instance.table_index(definition);
|
||||||
for i in 0..definition.current_elements {
|
for i in 0..definition.current_elements {
|
||||||
set_table_item(&instance, index, i, item.clone())?;
|
set_table_item(&instance, index, i, item)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Table {
|
Ok(Table {
|
||||||
@@ -356,7 +352,7 @@ impl Table {
|
|||||||
let item = self.instance.table_get(table_index, index)?;
|
let item = self.instance.table_get(table_index, index)?;
|
||||||
match item {
|
match item {
|
||||||
runtime::TableElement::FuncRef(f) => {
|
runtime::TableElement::FuncRef(f) => {
|
||||||
Some(from_checked_anyfunc(f, &self.instance.store))
|
Some(unsafe { from_checked_anyfunc(f, &self.instance.store) })
|
||||||
}
|
}
|
||||||
runtime::TableElement::ExternRef(None) => Some(Val::ExternRef(None)),
|
runtime::TableElement::ExternRef(None) => Some(Val::ExternRef(None)),
|
||||||
runtime::TableElement::ExternRef(Some(x)) => Some(Val::ExternRef(Some(ExternRef {
|
runtime::TableElement::ExternRef(Some(x)) => Some(Val::ExternRef(Some(ExternRef {
|
||||||
@@ -398,8 +394,7 @@ impl Table {
|
|||||||
let orig_size = match self.ty().element() {
|
let orig_size = match self.ty().element() {
|
||||||
ValType::FuncRef => {
|
ValType::FuncRef => {
|
||||||
let init = into_checked_anyfunc(init, &self.instance.store)?;
|
let init = into_checked_anyfunc(init, &self.instance.store)?;
|
||||||
self.instance
|
self.instance.defined_table_grow(index, delta, init.into())
|
||||||
.defined_table_grow(index, delta, runtime::TableElement::FuncRef(init))
|
|
||||||
}
|
}
|
||||||
ValType::ExternRef => {
|
ValType::ExternRef => {
|
||||||
let init = match init {
|
let init = match init {
|
||||||
|
|||||||
@@ -6,10 +6,11 @@ use std::cmp::max;
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::panic::{self, AssertUnwindSafe};
|
use std::panic::{self, AssertUnwindSafe};
|
||||||
use std::ptr;
|
use std::ptr::{self, NonNull};
|
||||||
use std::rc::Weak;
|
use std::rc::Weak;
|
||||||
use wasmtime_runtime::{raise_user_trap, ExportFunction, VMTrampoline};
|
use wasmtime_runtime::{
|
||||||
use wasmtime_runtime::{Export, InstanceHandle, VMContext, VMFunctionBody};
|
raise_user_trap, Export, InstanceHandle, VMContext, VMFunctionBody, VMTrampoline,
|
||||||
|
};
|
||||||
|
|
||||||
/// A WebAssembly function which can be called.
|
/// A WebAssembly function which can be called.
|
||||||
///
|
///
|
||||||
@@ -140,8 +141,8 @@ use wasmtime_runtime::{Export, InstanceHandle, VMContext, VMFunctionBody};
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Func {
|
pub struct Func {
|
||||||
instance: StoreInstanceHandle,
|
instance: StoreInstanceHandle,
|
||||||
export: ExportFunction,
|
|
||||||
trampoline: VMTrampoline,
|
trampoline: VMTrampoline,
|
||||||
|
export: wasmtime_runtime::ExportFunction,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! getters {
|
macro_rules! getters {
|
||||||
@@ -175,10 +176,11 @@ macro_rules! getters {
|
|||||||
.context("Type mismatch in return type")?;
|
.context("Type mismatch in return type")?;
|
||||||
ensure!(results.next().is_none(), "Type mismatch: too many return values (expected 1)");
|
ensure!(results.next().is_none(), "Type mismatch: too many return values (expected 1)");
|
||||||
|
|
||||||
// Pass the instance into the closure so that we keep it live for the lifetime
|
// Pass the instance into the closure so that we keep it live for
|
||||||
// of the closure. Pass the export in so that we can call it.
|
// the lifetime of the closure. Pass the `anyfunc` in so that we can
|
||||||
|
// call it.
|
||||||
let instance = self.instance.clone();
|
let instance = self.instance.clone();
|
||||||
let export = self.export.clone();
|
let anyfunc = self.export.anyfunc;
|
||||||
|
|
||||||
// ... and then once we've passed the typechecks we can hand out our
|
// ... and then once we've passed the typechecks we can hand out our
|
||||||
// object since our `transmute` below should be safe!
|
// object since our `transmute` below should be safe!
|
||||||
@@ -191,12 +193,12 @@ macro_rules! getters {
|
|||||||
*mut VMContext,
|
*mut VMContext,
|
||||||
$($args,)*
|
$($args,)*
|
||||||
) -> R,
|
) -> R,
|
||||||
>(export.address);
|
>(anyfunc.as_ref().func_ptr.as_ptr());
|
||||||
let mut ret = None;
|
let mut ret = None;
|
||||||
$(let $args = $args.into_abi();)*
|
$(let $args = $args.into_abi();)*
|
||||||
|
|
||||||
invoke_wasm_and_catch_traps(export.vmctx, &instance.store, || {
|
invoke_wasm_and_catch_traps(anyfunc.as_ref().vmctx, &instance.store, || {
|
||||||
ret = Some(fnptr(export.vmctx, ptr::null_mut(), $($args,)*));
|
ret = Some(fnptr(anyfunc.as_ref().vmctx, ptr::null_mut(), $($args,)*));
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(ret.unwrap())
|
Ok(ret.unwrap())
|
||||||
@@ -282,8 +284,8 @@ impl Func {
|
|||||||
crate::trampoline::generate_func_export(&ty, func, store).expect("generated func");
|
crate::trampoline::generate_func_export(&ty, func, store).expect("generated func");
|
||||||
Func {
|
Func {
|
||||||
instance,
|
instance,
|
||||||
export,
|
|
||||||
trampoline,
|
trampoline,
|
||||||
|
export,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -488,7 +490,10 @@ impl Func {
|
|||||||
pub fn ty(&self) -> FuncType {
|
pub fn ty(&self) -> FuncType {
|
||||||
// Signatures should always be registered in the store's registry of
|
// Signatures should always be registered in the store's registry of
|
||||||
// shared signatures, so we should be able to unwrap safely here.
|
// shared signatures, so we should be able to unwrap safely here.
|
||||||
let sig = self.instance.store.lookup_signature(self.export.signature);
|
let sig = self
|
||||||
|
.instance
|
||||||
|
.store
|
||||||
|
.lookup_signature(unsafe { self.export.anyfunc.as_ref().type_index });
|
||||||
|
|
||||||
// This is only called with `Export::Function`, and since it's coming
|
// This is only called with `Export::Function`, and since it's coming
|
||||||
// from wasmtime_runtime itself we should support all the types coming
|
// from wasmtime_runtime itself we should support all the types coming
|
||||||
@@ -498,13 +503,19 @@ impl Func {
|
|||||||
|
|
||||||
/// Returns the number of parameters that this function takes.
|
/// Returns the number of parameters that this function takes.
|
||||||
pub fn param_arity(&self) -> usize {
|
pub fn param_arity(&self) -> usize {
|
||||||
let sig = self.instance.store.lookup_signature(self.export.signature);
|
let sig = self
|
||||||
|
.instance
|
||||||
|
.store
|
||||||
|
.lookup_signature(unsafe { self.export.anyfunc.as_ref().type_index });
|
||||||
sig.params.len()
|
sig.params.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of results this function produces.
|
/// Returns the number of results this function produces.
|
||||||
pub fn result_arity(&self) -> usize {
|
pub fn result_arity(&self) -> usize {
|
||||||
let sig = self.instance.store.lookup_signature(self.export.signature);
|
let sig = self
|
||||||
|
.instance
|
||||||
|
.store
|
||||||
|
.lookup_signature(unsafe { self.export.anyfunc.as_ref().type_index });
|
||||||
sig.returns.len()
|
sig.returns.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -553,14 +564,17 @@ impl Func {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Call the trampoline.
|
// Call the trampoline.
|
||||||
invoke_wasm_and_catch_traps(self.export.vmctx, &self.instance.store, || unsafe {
|
unsafe {
|
||||||
|
let anyfunc = self.export.anyfunc.as_ref();
|
||||||
|
invoke_wasm_and_catch_traps(anyfunc.vmctx, &self.instance.store, || {
|
||||||
(self.trampoline)(
|
(self.trampoline)(
|
||||||
self.export.vmctx,
|
anyfunc.vmctx,
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
self.export.address,
|
anyfunc.func_ptr.as_ptr(),
|
||||||
values_vec.as_mut_ptr(),
|
values_vec.as_mut_ptr(),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
// Load the return values out of `values_vec`.
|
// Load the return values out of `values_vec`.
|
||||||
let mut results = Vec::with_capacity(my_ty.results().len());
|
let mut results = Vec::with_capacity(my_ty.results().len());
|
||||||
@@ -578,6 +592,12 @@ impl Func {
|
|||||||
&self.export
|
&self.export
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn caller_checked_anyfunc(
|
||||||
|
&self,
|
||||||
|
) -> NonNull<wasmtime_runtime::VMCallerCheckedAnyfunc> {
|
||||||
|
self.export.anyfunc
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn from_wasmtime_function(
|
pub(crate) fn from_wasmtime_function(
|
||||||
export: wasmtime_runtime::ExportFunction,
|
export: wasmtime_runtime::ExportFunction,
|
||||||
instance: StoreInstanceHandle,
|
instance: StoreInstanceHandle,
|
||||||
@@ -586,7 +606,7 @@ impl Func {
|
|||||||
// on that module as well, so unwrap the result here since otherwise
|
// on that module as well, so unwrap the result here since otherwise
|
||||||
// it's a bug in wasmtime.
|
// it's a bug in wasmtime.
|
||||||
let trampoline = instance
|
let trampoline = instance
|
||||||
.trampoline(export.signature)
|
.trampoline(unsafe { export.anyfunc.as_ref().type_index })
|
||||||
.expect("failed to retrieve trampoline from module");
|
.expect("failed to retrieve trampoline from module");
|
||||||
|
|
||||||
Func {
|
Func {
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ use std::any::Any;
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
use wasmtime_environ::EntityIndex;
|
use wasmtime_environ::EntityIndex;
|
||||||
use wasmtime_jit::{CompiledModule, Resolver};
|
use wasmtime_jit::{CompiledModule, Resolver};
|
||||||
use wasmtime_runtime::{InstantiationError, VMContext, VMFunctionBody};
|
use wasmtime_runtime::{
|
||||||
|
InstantiationError, StackMapRegistry, VMContext, VMExternRefActivationsTable, VMFunctionBody,
|
||||||
|
};
|
||||||
|
|
||||||
struct SimpleResolver<'a> {
|
struct SimpleResolver<'a> {
|
||||||
imports: &'a [Extern],
|
imports: &'a [Extern],
|
||||||
@@ -50,8 +52,8 @@ fn instantiate(
|
|||||||
config.memory_creator.as_ref().map(|a| a as _),
|
config.memory_creator.as_ref().map(|a| a as _),
|
||||||
store.interrupts().clone(),
|
store.interrupts().clone(),
|
||||||
host,
|
host,
|
||||||
&*store.externref_activations_table() as *const _ as *mut _,
|
&**store.externref_activations_table() as *const VMExternRefActivationsTable as *mut _,
|
||||||
&*store.stack_map_registry() as *const _ as *mut _,
|
&**store.stack_map_registry() as *const StackMapRegistry as *mut _,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// After we've created the `InstanceHandle` we still need to run
|
// After we've created the `InstanceHandle` we still need to run
|
||||||
@@ -95,7 +97,9 @@ fn instantiate(
|
|||||||
mem::transmute::<
|
mem::transmute::<
|
||||||
*const VMFunctionBody,
|
*const VMFunctionBody,
|
||||||
unsafe extern "C" fn(*mut VMContext, *mut VMContext),
|
unsafe extern "C" fn(*mut VMContext, *mut VMContext),
|
||||||
>(f.address)(f.vmctx, vmctx_ptr)
|
>(f.anyfunc.as_ref().func_ptr.as_ptr())(
|
||||||
|
f.anyfunc.as_ref().vmctx, vmctx_ptr
|
||||||
|
)
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -427,7 +427,7 @@ impl Linker {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
self.insert(module_name, export.name(), Extern::Func(func))?;
|
self.insert(module_name, export.name(), func.into())?;
|
||||||
} else if export.name() == "memory" && export.ty().memory().is_some() {
|
} else if export.name() == "memory" && export.ty().memory().is_some() {
|
||||||
// Allow an exported "memory" memory for now.
|
// Allow an exported "memory" memory for now.
|
||||||
} else if export.name() == "__indirect_function_table" && export.ty().table().is_some()
|
} else if export.name() == "__indirect_function_table" && export.ty().table().is_some()
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ use wasmtime_environ::entity::PrimaryMap;
|
|||||||
use wasmtime_environ::wasm::DefinedFuncIndex;
|
use wasmtime_environ::wasm::DefinedFuncIndex;
|
||||||
use wasmtime_environ::Module;
|
use wasmtime_environ::Module;
|
||||||
use wasmtime_runtime::{
|
use wasmtime_runtime::{
|
||||||
Imports, InstanceHandle, VMFunctionBody, VMSharedSignatureIndex, VMTrampoline,
|
Imports, InstanceHandle, StackMapRegistry, VMExternRefActivationsTable, VMFunctionBody,
|
||||||
|
VMSharedSignatureIndex, VMTrampoline,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn create_handle(
|
pub(crate) fn create_handle(
|
||||||
@@ -46,8 +47,8 @@ pub(crate) fn create_handle(
|
|||||||
signatures.into_boxed_slice(),
|
signatures.into_boxed_slice(),
|
||||||
state,
|
state,
|
||||||
store.interrupts().clone(),
|
store.interrupts().clone(),
|
||||||
&*store.externref_activations_table() as *const _ as *mut _,
|
&**store.externref_activations_table() as *const VMExternRefActivationsTable as *mut _,
|
||||||
&*store.stack_map_registry() as *const _ as *mut _,
|
&**store.stack_map_registry() as *const StackMapRegistry as *mut _,
|
||||||
)?;
|
)?;
|
||||||
Ok(store.add_instance(handle))
|
Ok(store.add_instance(handle))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
use super::create_handle::create_handle;
|
use super::create_handle::create_handle;
|
||||||
use crate::trampoline::StoreInstanceHandle;
|
use crate::trampoline::StoreInstanceHandle;
|
||||||
use crate::Store;
|
use crate::{GlobalType, Mutability, Store, Val};
|
||||||
use crate::{GlobalType, Mutability, Val};
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use wasmtime_environ::entity::PrimaryMap;
|
use wasmtime_environ::entity::PrimaryMap;
|
||||||
use wasmtime_environ::{wasm, EntityIndex, Module};
|
use wasmtime_environ::{wasm, EntityIndex, Module};
|
||||||
|
|
||||||
pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreInstanceHandle> {
|
pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreInstanceHandle> {
|
||||||
let global = wasm::Global {
|
let global = wasm::Global {
|
||||||
|
wasm_ty: gt.content().to_wasm_type(),
|
||||||
ty: match gt.content().get_wasmtime_type() {
|
ty: match gt.content().get_wasmtime_type() {
|
||||||
Some(t) => t,
|
Some(t) => t,
|
||||||
None => bail!("cannot support {:?} as a wasm global type", gt.content()),
|
None => bail!("cannot support {:?} as a wasm global type", gt.content()),
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ pub fn create_handle_with_table(store: &Store, table: &TableType) -> Result<Stor
|
|||||||
let mut module = Module::new();
|
let mut module = Module::new();
|
||||||
|
|
||||||
let table = wasm::Table {
|
let table = wasm::Table {
|
||||||
|
wasm_ty: table.element().to_wasm_type(),
|
||||||
minimum: table.limits().min(),
|
minimum: table.limits().min(),
|
||||||
maximum: table.limits().max(),
|
maximum: table.limits().max(),
|
||||||
ty: match table.element() {
|
ty: match table.element() {
|
||||||
|
|||||||
@@ -106,25 +106,7 @@ impl ValType {
|
|||||||
ValType::F32 => Some(ir::types::F32),
|
ValType::F32 => Some(ir::types::F32),
|
||||||
ValType::F64 => Some(ir::types::F64),
|
ValType::F64 => Some(ir::types::F64),
|
||||||
ValType::V128 => Some(ir::types::I8X16),
|
ValType::V128 => Some(ir::types::I8X16),
|
||||||
#[cfg(target_pointer_width = "64")]
|
ValType::ExternRef => Some(wasmtime_runtime::ref_type()),
|
||||||
ValType::ExternRef => Some(ir::types::R64),
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
|
||||||
ValType::ExternRef => Some(ir::types::R32),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn from_wasmtime_type(ty: ir::Type) -> Option<ValType> {
|
|
||||||
match ty {
|
|
||||||
ir::types::I32 => Some(ValType::I32),
|
|
||||||
ir::types::I64 => Some(ValType::I64),
|
|
||||||
ir::types::F32 => Some(ValType::F32),
|
|
||||||
ir::types::F64 => Some(ValType::F64),
|
|
||||||
ir::types::I8X16 => Some(ValType::V128),
|
|
||||||
#[cfg(target_pointer_width = "64")]
|
|
||||||
ir::types::R64 => Some(ValType::ExternRef),
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
|
||||||
ir::types::R32 => Some(ValType::ExternRef),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -353,7 +335,7 @@ impl GlobalType {
|
|||||||
/// Returns `None` if the wasmtime global has a type that we can't
|
/// Returns `None` if the wasmtime global has a type that we can't
|
||||||
/// represent, but that should only very rarely happen and indicate a bug.
|
/// represent, but that should only very rarely happen and indicate a bug.
|
||||||
pub(crate) fn from_wasmtime_global(global: &wasm::Global) -> Option<GlobalType> {
|
pub(crate) fn from_wasmtime_global(global: &wasm::Global) -> Option<GlobalType> {
|
||||||
let ty = ValType::from_wasmtime_type(global.ty)?;
|
let ty = ValType::from_wasm_type(&global.wasm_ty)?;
|
||||||
let mutability = if global.mutability {
|
let mutability = if global.mutability {
|
||||||
Mutability::Var
|
Mutability::Var
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use crate::r#ref::ExternRef;
|
use crate::r#ref::ExternRef;
|
||||||
use crate::{Func, Store, ValType};
|
use crate::{Func, Store, ValType};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use std::ptr;
|
use std::ptr::{self, NonNull};
|
||||||
use wasmtime_runtime::VMExternRef;
|
use wasmtime_runtime::{self as runtime, VMExternRef};
|
||||||
|
|
||||||
/// Possible runtime values that a WebAssembly module can either consume or
|
/// Possible runtime values that a WebAssembly module can either consume or
|
||||||
/// produce.
|
/// produce.
|
||||||
@@ -26,11 +26,18 @@ pub enum Val {
|
|||||||
/// `f64::from_bits` to create an `f64` value.
|
/// `f64::from_bits` to create an `f64` value.
|
||||||
F64(u64),
|
F64(u64),
|
||||||
|
|
||||||
/// An `externref` value which can hold opaque data to the wasm instance itself.
|
/// An `externref` value which can hold opaque data to the Wasm instance
|
||||||
|
/// itself.
|
||||||
|
///
|
||||||
|
/// `ExternRef(None)` is the null external reference, created by `ref.null
|
||||||
|
/// extern` in Wasm.
|
||||||
ExternRef(Option<ExternRef>),
|
ExternRef(Option<ExternRef>),
|
||||||
|
|
||||||
/// A first-class reference to a WebAssembly function.
|
/// A first-class reference to a WebAssembly function.
|
||||||
FuncRef(Func),
|
///
|
||||||
|
/// `FuncRef(None)` is the null function reference, created by `ref.null
|
||||||
|
/// func` in Wasm.
|
||||||
|
FuncRef(Option<Func>),
|
||||||
|
|
||||||
/// A 128-bit number
|
/// A 128-bit number
|
||||||
V128(u128),
|
V128(u128),
|
||||||
@@ -94,7 +101,14 @@ impl Val {
|
|||||||
.insert_with_gc(x.inner, store.stack_map_registry());
|
.insert_with_gc(x.inner, store.stack_map_registry());
|
||||||
ptr::write(p as *mut *mut u8, externref_ptr)
|
ptr::write(p as *mut *mut u8, externref_ptr)
|
||||||
}
|
}
|
||||||
_ => unimplemented!("Val::write_value_to"),
|
Val::FuncRef(f) => ptr::write(
|
||||||
|
p as *mut *mut runtime::VMCallerCheckedAnyfunc,
|
||||||
|
if let Some(f) = f {
|
||||||
|
f.caller_checked_anyfunc().as_ptr()
|
||||||
|
} else {
|
||||||
|
ptr::null_mut()
|
||||||
|
},
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +130,10 @@ impl Val {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => unimplemented!("Val::read_value_from: {:?}", ty),
|
ValType::FuncRef => {
|
||||||
|
let func = ptr::read(p as *const *mut runtime::VMCallerCheckedAnyfunc);
|
||||||
|
from_checked_anyfunc(func, store)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,7 +143,7 @@ impl Val {
|
|||||||
(I64(i64) i64 unwrap_i64 *e)
|
(I64(i64) i64 unwrap_i64 *e)
|
||||||
(F32(f32) f32 unwrap_f32 f32::from_bits(*e))
|
(F32(f32) f32 unwrap_f32 f32::from_bits(*e))
|
||||||
(F64(f64) f64 unwrap_f64 f64::from_bits(*e))
|
(F64(f64) f64 unwrap_f64 f64::from_bits(*e))
|
||||||
(FuncRef(&Func) funcref unwrap_funcref e)
|
(FuncRef(Option<&Func>) funcref unwrap_funcref e.as_ref())
|
||||||
(V128(u128) v128 unwrap_v128 *e)
|
(V128(u128) v128 unwrap_v128 *e)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,7 +177,8 @@ impl Val {
|
|||||||
|
|
||||||
pub(crate) fn comes_from_same_store(&self, store: &Store) -> bool {
|
pub(crate) fn comes_from_same_store(&self, store: &Store) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Val::FuncRef(f) => Store::same(store, f.store()),
|
Val::FuncRef(Some(f)) => Store::same(store, f.store()),
|
||||||
|
Val::FuncRef(None) => true,
|
||||||
|
|
||||||
// TODO: need to implement this once we actually finalize what
|
// TODO: need to implement this once we actually finalize what
|
||||||
// `externref` will look like and it's actually implemented to pass it
|
// `externref` will look like and it's actually implemented to pass it
|
||||||
@@ -211,51 +229,47 @@ impl From<Option<ExternRef>> for Val {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Option<Func>> for Val {
|
||||||
|
fn from(val: Option<Func>) -> Val {
|
||||||
|
Val::FuncRef(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Func> for Val {
|
impl From<Func> for Val {
|
||||||
fn from(val: Func) -> Val {
|
fn from(val: Func) -> Val {
|
||||||
Val::FuncRef(val)
|
Val::FuncRef(Some(val))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn into_checked_anyfunc(
|
pub(crate) fn into_checked_anyfunc(
|
||||||
val: Val,
|
val: Val,
|
||||||
store: &Store,
|
store: &Store,
|
||||||
) -> Result<wasmtime_runtime::VMCallerCheckedAnyfunc> {
|
) -> Result<*mut wasmtime_runtime::VMCallerCheckedAnyfunc> {
|
||||||
if !val.comes_from_same_store(store) {
|
if !val.comes_from_same_store(store) {
|
||||||
bail!("cross-`Store` values are not supported");
|
bail!("cross-`Store` values are not supported");
|
||||||
}
|
}
|
||||||
Ok(match val {
|
Ok(match val {
|
||||||
Val::ExternRef(None) => wasmtime_runtime::VMCallerCheckedAnyfunc {
|
Val::FuncRef(None) => ptr::null_mut(),
|
||||||
func_ptr: ptr::null(),
|
Val::FuncRef(Some(f)) => f.caller_checked_anyfunc().as_ptr(),
|
||||||
type_index: wasmtime_runtime::VMSharedSignatureIndex::default(),
|
|
||||||
vmctx: ptr::null_mut(),
|
|
||||||
},
|
|
||||||
Val::FuncRef(f) => {
|
|
||||||
let f = f.wasmtime_function();
|
|
||||||
wasmtime_runtime::VMCallerCheckedAnyfunc {
|
|
||||||
func_ptr: f.address,
|
|
||||||
type_index: f.signature,
|
|
||||||
vmctx: f.vmctx,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => bail!("val is not funcref"),
|
_ => bail!("val is not funcref"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_checked_anyfunc(
|
pub(crate) unsafe fn from_checked_anyfunc(
|
||||||
item: wasmtime_runtime::VMCallerCheckedAnyfunc,
|
anyfunc: *mut wasmtime_runtime::VMCallerCheckedAnyfunc,
|
||||||
store: &Store,
|
store: &Store,
|
||||||
) -> Val {
|
) -> Val {
|
||||||
if item.type_index == wasmtime_runtime::VMSharedSignatureIndex::default() {
|
let anyfunc = match NonNull::new(anyfunc) {
|
||||||
return Val::ExternRef(None);
|
None => return Val::FuncRef(None),
|
||||||
}
|
Some(f) => f,
|
||||||
let instance_handle = unsafe { wasmtime_runtime::InstanceHandle::from_vmctx(item.vmctx) };
|
|
||||||
let export = wasmtime_runtime::ExportFunction {
|
|
||||||
address: item.func_ptr,
|
|
||||||
signature: item.type_index,
|
|
||||||
vmctx: item.vmctx,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
debug_assert!(
|
||||||
|
anyfunc.as_ref().type_index != wasmtime_runtime::VMSharedSignatureIndex::default()
|
||||||
|
);
|
||||||
|
let instance_handle = wasmtime_runtime::InstanceHandle::from_vmctx(anyfunc.as_ref().vmctx);
|
||||||
|
let export = wasmtime_runtime::ExportFunction { anyfunc };
|
||||||
let instance = store.existing_instance_handle(instance_handle);
|
let instance = store.existing_instance_handle(instance_handle);
|
||||||
let f = Func::from_wasmtime_function(export, instance);
|
let f = Func::from_wasmtime_function(export, instance);
|
||||||
Val::FuncRef(f)
|
Val::FuncRef(Some(f))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ pub fn link_spectest(linker: &mut Linker) -> Result<()> {
|
|||||||
linker.define("spectest", "global_f64", g)?;
|
linker.define("spectest", "global_f64", g)?;
|
||||||
|
|
||||||
let ty = TableType::new(ValType::FuncRef, Limits::new(10, Some(20)));
|
let ty = TableType::new(ValType::FuncRef, Limits::new(10, Some(20)));
|
||||||
let table = Table::new(linker.store(), ty, Val::ExternRef(None))?;
|
let table = Table::new(linker.store(), ty, Val::FuncRef(None))?;
|
||||||
linker.define("spectest", "table", table)?;
|
linker.define("spectest", "table", table)?;
|
||||||
|
|
||||||
let ty = MemoryType::new(Limits::new(1, Some(2)));
|
let ty = MemoryType::new(Limits::new(1, Some(2)));
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ fn runtime_value(store: &Store, v: &wast::Expression<'_>) -> Result<Val> {
|
|||||||
F64Const(x) => Val::F64(x.bits),
|
F64Const(x) => Val::F64(x.bits),
|
||||||
V128Const(x) => Val::V128(u128::from_le_bytes(x.to_le_bytes())),
|
V128Const(x) => Val::V128(u128::from_le_bytes(x.to_le_bytes())),
|
||||||
RefNull(RefType::Extern) => Val::ExternRef(None),
|
RefNull(RefType::Extern) => Val::ExternRef(None),
|
||||||
|
RefNull(RefType::Func) => Val::FuncRef(None),
|
||||||
RefExtern(x) => Val::ExternRef(Some(ExternRef::new(store, *x))),
|
RefExtern(x) => Val::ExternRef(Some(ExternRef::new(store, *x))),
|
||||||
other => bail!("couldn't convert {:?} to a runtime value", other),
|
other => bail!("couldn't convert {:?} to a runtime value", other),
|
||||||
})
|
})
|
||||||
@@ -420,6 +421,7 @@ fn val_matches(actual: &Val, expected: &wast::AssertExpression) -> Result<bool>
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
(Val::FuncRef(x), wast::AssertExpression::RefNull(wast::RefType::Func)) => x.is_none(),
|
||||||
_ => bail!(
|
_ => bail!(
|
||||||
"don't know how to compare {:?} and {:?} yet",
|
"don't know how to compare {:?} and {:?} yet",
|
||||||
actual,
|
actual,
|
||||||
|
|||||||
@@ -28,27 +28,27 @@ fn bad_tables() {
|
|||||||
|
|
||||||
// get out of bounds
|
// get out of bounds
|
||||||
let ty = TableType::new(ValType::FuncRef, Limits::new(0, Some(1)));
|
let ty = TableType::new(ValType::FuncRef, Limits::new(0, Some(1)));
|
||||||
let t = Table::new(&Store::default(), ty.clone(), Val::ExternRef(None)).unwrap();
|
let t = Table::new(&Store::default(), ty.clone(), Val::FuncRef(None)).unwrap();
|
||||||
assert!(t.get(0).is_none());
|
assert!(t.get(0).is_none());
|
||||||
assert!(t.get(u32::max_value()).is_none());
|
assert!(t.get(u32::max_value()).is_none());
|
||||||
|
|
||||||
// set out of bounds or wrong type
|
// set out of bounds or wrong type
|
||||||
let ty = TableType::new(ValType::FuncRef, Limits::new(1, Some(1)));
|
let ty = TableType::new(ValType::FuncRef, Limits::new(1, Some(1)));
|
||||||
let t = Table::new(&Store::default(), ty.clone(), Val::ExternRef(None)).unwrap();
|
let t = Table::new(&Store::default(), ty.clone(), Val::FuncRef(None)).unwrap();
|
||||||
assert!(t.set(0, Val::I32(0)).is_err());
|
assert!(t.set(0, Val::I32(0)).is_err());
|
||||||
assert!(t.set(0, Val::ExternRef(None)).is_ok());
|
assert!(t.set(0, Val::FuncRef(None)).is_ok());
|
||||||
assert!(t.set(1, Val::ExternRef(None)).is_err());
|
assert!(t.set(1, Val::FuncRef(None)).is_err());
|
||||||
|
|
||||||
// grow beyond max
|
// grow beyond max
|
||||||
let ty = TableType::new(ValType::FuncRef, Limits::new(1, Some(1)));
|
let ty = TableType::new(ValType::FuncRef, Limits::new(1, Some(1)));
|
||||||
let t = Table::new(&Store::default(), ty.clone(), Val::ExternRef(None)).unwrap();
|
let t = Table::new(&Store::default(), ty.clone(), Val::FuncRef(None)).unwrap();
|
||||||
assert!(t.grow(0, Val::ExternRef(None)).is_ok());
|
assert!(t.grow(0, Val::FuncRef(None)).is_ok());
|
||||||
assert!(t.grow(1, Val::ExternRef(None)).is_err());
|
assert!(t.grow(1, Val::FuncRef(None)).is_err());
|
||||||
assert_eq!(t.size(), 1);
|
assert_eq!(t.size(), 1);
|
||||||
|
|
||||||
// grow wrong type
|
// grow wrong type
|
||||||
let ty = TableType::new(ValType::FuncRef, Limits::new(1, Some(2)));
|
let ty = TableType::new(ValType::FuncRef, Limits::new(1, Some(2)));
|
||||||
let t = Table::new(&Store::default(), ty.clone(), Val::ExternRef(None)).unwrap();
|
let t = Table::new(&Store::default(), ty.clone(), Val::FuncRef(None)).unwrap();
|
||||||
assert!(t.grow(1, Val::I32(0)).is_err());
|
assert!(t.grow(1, Val::I32(0)).is_err());
|
||||||
assert_eq!(t.size(), 1);
|
assert_eq!(t.size(), 1);
|
||||||
}
|
}
|
||||||
@@ -69,7 +69,7 @@ fn cross_store() -> anyhow::Result<()> {
|
|||||||
let ty = MemoryType::new(Limits::new(1, None));
|
let ty = MemoryType::new(Limits::new(1, None));
|
||||||
let memory = Memory::new(&store2, ty);
|
let memory = Memory::new(&store2, ty);
|
||||||
let ty = TableType::new(ValType::FuncRef, Limits::new(1, None));
|
let ty = TableType::new(ValType::FuncRef, Limits::new(1, None));
|
||||||
let table = Table::new(&store2, ty, Val::ExternRef(None))?;
|
let table = Table::new(&store2, ty, Val::FuncRef(None))?;
|
||||||
|
|
||||||
let need_func = Module::new(&engine, r#"(module (import "" "" (func)))"#)?;
|
let need_func = Module::new(&engine, r#"(module (import "" "" (func)))"#)?;
|
||||||
assert!(Instance::new(&store1, &need_func, &[func.into()]).is_err());
|
assert!(Instance::new(&store1, &need_func, &[func.into()]).is_err());
|
||||||
@@ -85,8 +85,8 @@ fn cross_store() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
// ============ Cross-store globals ==============
|
// ============ Cross-store globals ==============
|
||||||
|
|
||||||
let store1val = Val::FuncRef(Func::wrap(&store1, || {}));
|
let store1val = Val::FuncRef(Some(Func::wrap(&store1, || {})));
|
||||||
let store2val = Val::FuncRef(Func::wrap(&store2, || {}));
|
let store2val = Val::FuncRef(Some(Func::wrap(&store2, || {})));
|
||||||
|
|
||||||
let ty = GlobalType::new(ValType::FuncRef, Mutability::Var);
|
let ty = GlobalType::new(ValType::FuncRef, Mutability::Var);
|
||||||
assert!(Global::new(&store2, ty.clone(), store1val.clone()).is_err());
|
assert!(Global::new(&store2, ty.clone(), store1val.clone()).is_err());
|
||||||
|
|||||||
85
tests/all/funcref.rs
Normal file
85
tests/all/funcref.rs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
use super::ref_types_module;
|
||||||
|
use wasmtime::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pass_funcref_in_and_out_of_wasm() -> anyhow::Result<()> {
|
||||||
|
let (store, module) = ref_types_module(
|
||||||
|
r#"
|
||||||
|
(module
|
||||||
|
(func (export "func") (param funcref) (result funcref)
|
||||||
|
local.get 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let instance = Instance::new(&store, &module, &[])?;
|
||||||
|
let func = instance.get_func("func").unwrap();
|
||||||
|
|
||||||
|
// Pass in a non-null funcref.
|
||||||
|
{
|
||||||
|
let results = func.call(&[Val::FuncRef(Some(func.clone()))])?;
|
||||||
|
assert_eq!(results.len(), 1);
|
||||||
|
|
||||||
|
// Can't compare `Func` for equality, so this is the best we can do here.
|
||||||
|
let result_func = results[0].unwrap_funcref().unwrap();
|
||||||
|
assert_eq!(func.ty(), result_func.ty());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass in a null funcref.
|
||||||
|
{
|
||||||
|
let results = func.call(&[Val::FuncRef(None)])?;
|
||||||
|
assert_eq!(results.len(), 1);
|
||||||
|
|
||||||
|
let result_func = results[0].unwrap_funcref();
|
||||||
|
assert!(result_func.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass in a `funcref` from another instance.
|
||||||
|
{
|
||||||
|
let other_instance = Instance::new(&store, &module, &[])?;
|
||||||
|
let other_instance_func = other_instance.get_func("func").unwrap();
|
||||||
|
|
||||||
|
let results = func.call(&[Val::FuncRef(Some(other_instance_func.clone()))])?;
|
||||||
|
assert_eq!(results.len(), 1);
|
||||||
|
|
||||||
|
// Can't compare `Func` for equality, so this is the best we can do here.
|
||||||
|
let result_func = results[0].unwrap_funcref().unwrap();
|
||||||
|
assert_eq!(other_instance_func.ty(), result_func.ty());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Passing in a `funcref` from another store fails.
|
||||||
|
{
|
||||||
|
let (other_store, other_module) = ref_types_module(r#"(module (func (export "f")))"#)?;
|
||||||
|
let other_store_instance = Instance::new(&other_store, &other_module, &[])?;
|
||||||
|
let f = other_store_instance.get_func("f").unwrap();
|
||||||
|
|
||||||
|
assert!(func.call(&[Val::FuncRef(Some(f))]).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn receive_null_funcref_from_wasm() -> anyhow::Result<()> {
|
||||||
|
let (store, module) = ref_types_module(
|
||||||
|
r#"
|
||||||
|
(module
|
||||||
|
(func (export "get-null") (result funcref)
|
||||||
|
ref.null func
|
||||||
|
)
|
||||||
|
)
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let instance = Instance::new(&store, &module, &[])?;
|
||||||
|
let get_null = instance.get_func("get-null").unwrap();
|
||||||
|
|
||||||
|
let results = get_null.call(&[])?;
|
||||||
|
assert_eq!(results.len(), 1);
|
||||||
|
|
||||||
|
let result_func = results[0].unwrap_funcref();
|
||||||
|
assert!(result_func.is_none());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -1,21 +1,8 @@
|
|||||||
|
use super::ref_types_module;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use wasmtime::*;
|
use wasmtime::*;
|
||||||
|
|
||||||
fn ref_types_module(source: &str) -> anyhow::Result<(Store, Module)> {
|
|
||||||
let _ = env_logger::try_init();
|
|
||||||
|
|
||||||
let mut config = Config::new();
|
|
||||||
config.wasm_reference_types(true);
|
|
||||||
|
|
||||||
let engine = Engine::new(&config);
|
|
||||||
let store = Store::new(&engine);
|
|
||||||
|
|
||||||
let module = Module::new(&engine, source)?;
|
|
||||||
|
|
||||||
Ok((store, module))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn smoke_test_gc() -> anyhow::Result<()> {
|
fn smoke_test_gc() -> anyhow::Result<()> {
|
||||||
let (store, module) = ref_types_module(
|
let (store, module) = ref_types_module(
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ fn test_invoke_func_via_table() -> Result<()> {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.funcref()
|
.funcref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
.clone();
|
.clone();
|
||||||
let result = f.call(&[]).unwrap();
|
let result = f.call(&[]).unwrap();
|
||||||
assert_eq!(result[0].unwrap_i64(), 42);
|
assert_eq!(result[0].unwrap_i64(), 42);
|
||||||
|
|||||||
@@ -59,11 +59,11 @@ fn link_twice_bad() -> Result<()> {
|
|||||||
|
|
||||||
// tables
|
// tables
|
||||||
let ty = TableType::new(ValType::FuncRef, Limits::new(1, None));
|
let ty = TableType::new(ValType::FuncRef, Limits::new(1, None));
|
||||||
let table = Table::new(&store, ty, Val::ExternRef(None))?;
|
let table = Table::new(&store, ty, Val::FuncRef(None))?;
|
||||||
linker.define("", "", table.clone())?;
|
linker.define("", "", table.clone())?;
|
||||||
assert!(linker.define("", "", table.clone()).is_err());
|
assert!(linker.define("", "", table.clone()).is_err());
|
||||||
let ty = TableType::new(ValType::FuncRef, Limits::new(2, None));
|
let ty = TableType::new(ValType::FuncRef, Limits::new(2, None));
|
||||||
let table = Table::new(&store, ty, Val::ExternRef(None))?;
|
let table = Table::new(&store, ty, Val::FuncRef(None))?;
|
||||||
assert!(linker.define("", "", table.clone()).is_err());
|
assert!(linker.define("", "", table.clone()).is_err());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,4 +21,26 @@ mod wast;
|
|||||||
|
|
||||||
// TODO(#1886): Cranelift only supports reference types on x64.
|
// TODO(#1886): Cranelift only supports reference types on x64.
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
mod funcref;
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
mod gc;
|
mod gc;
|
||||||
|
|
||||||
|
/// A helper to compile a module in a new store with reference types enabled.
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
pub(crate) fn ref_types_module(
|
||||||
|
source: &str,
|
||||||
|
) -> anyhow::Result<(wasmtime::Store, wasmtime::Module)> {
|
||||||
|
use wasmtime::*;
|
||||||
|
|
||||||
|
let _ = env_logger::try_init();
|
||||||
|
|
||||||
|
let mut config = Config::new();
|
||||||
|
config.wasm_reference_types(true);
|
||||||
|
|
||||||
|
let engine = Engine::new(&config);
|
||||||
|
let store = Store::new(&engine);
|
||||||
|
|
||||||
|
let module = Module::new(&engine, source)?;
|
||||||
|
|
||||||
|
Ok((store, module))
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ use wasmtime::*;
|
|||||||
fn get_none() {
|
fn get_none() {
|
||||||
let store = Store::default();
|
let store = Store::default();
|
||||||
let ty = TableType::new(ValType::FuncRef, Limits::new(1, None));
|
let ty = TableType::new(ValType::FuncRef, Limits::new(1, None));
|
||||||
let table = Table::new(&store, ty, Val::ExternRef(None)).unwrap();
|
let table = Table::new(&store, ty, Val::FuncRef(None)).unwrap();
|
||||||
match table.get(0) {
|
match table.get(0) {
|
||||||
Some(Val::ExternRef(None)) => {}
|
Some(Val::FuncRef(None)) => {}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
assert!(table.get(1).is_none());
|
assert!(table.get(1).is_none());
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ fn use_func_after_drop() -> Result<()> {
|
|||||||
assert_eq!(closed_over_data, "abcd");
|
assert_eq!(closed_over_data, "abcd");
|
||||||
});
|
});
|
||||||
let ty = TableType::new(ValType::FuncRef, Limits::new(1, None));
|
let ty = TableType::new(ValType::FuncRef, Limits::new(1, None));
|
||||||
table = Table::new(&store, ty, Val::ExternRef(None))?;
|
table = Table::new(&store, ty, Val::FuncRef(None))?;
|
||||||
table.set(0, func.into())?;
|
table.set(0, func.into())?;
|
||||||
}
|
}
|
||||||
let func = table.get(0).unwrap().funcref().unwrap().clone();
|
let func = table.get(0).unwrap().funcref().unwrap().unwrap().clone();
|
||||||
let func = func.get0::<()>()?;
|
let func = func.get0::<()>()?;
|
||||||
func()?;
|
func()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
17
tests/misc_testsuite/reference-types/simple_ref_is_null.wast
Normal file
17
tests/misc_testsuite/reference-types/simple_ref_is_null.wast
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
(module
|
||||||
|
(func (export "func_is_null") (param funcref) (result i32)
|
||||||
|
(ref.is_null func (local.get 0))
|
||||||
|
)
|
||||||
|
(func (export "func_is_null_with_non_null_funcref") (result i32)
|
||||||
|
(call 0 (ref.func 0))
|
||||||
|
)
|
||||||
|
(func (export "extern_is_null") (param externref) (result i32)
|
||||||
|
(ref.is_null extern (local.get 0))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(assert_return (invoke "func_is_null" (ref.null func)) (i32.const 1))
|
||||||
|
(assert_return (invoke "func_is_null_with_non_null_funcref") (i32.const 0))
|
||||||
|
|
||||||
|
(assert_return (invoke "extern_is_null" (ref.null extern)) (i32.const 1))
|
||||||
|
(assert_return (invoke "extern_is_null" (ref.extern 1)) (i32.const 0))
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
(module
|
||||||
|
(table $t 0 funcref)
|
||||||
|
(func (export "size") (result i32)
|
||||||
|
(table.size $t)
|
||||||
|
)
|
||||||
|
(func $f (export "grow-by-1") (result i32)
|
||||||
|
(table.grow $t (ref.func $f) (i32.const 1))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(assert_return (invoke "size") (i32.const 0))
|
||||||
|
(assert_return (invoke "grow-by-1") (i32.const 0))
|
||||||
|
(assert_return (invoke "size") (i32.const 1))
|
||||||
Reference in New Issue
Block a user