diff --git a/crates/environ/src/func_environ.rs b/crates/environ/src/func_environ.rs index 52e4fbb64ae9..c4ee1aa16d97 100644 --- a/crates/environ/src/func_environ.rs +++ b/crates/environ/src/func_environ.rs @@ -1192,7 +1192,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m _table: ir::Table, ) -> WasmResult { Err(WasmError::Unsupported( - "bulk memory: `table.size`".to_string(), + "reference types: `table.size`".to_string(), )) } diff --git a/crates/runtime/src/instance.rs b/crates/runtime/src/instance.rs index 8642da2aef45..88a09b20c820 100644 --- a/crates/runtime/src/instance.rs +++ b/crates/runtime/src/instance.rs @@ -5,7 +5,7 @@ use crate::export::Export; use crate::imports::Imports; use crate::memory::{DefaultMemoryCreator, RuntimeLinearMemory, RuntimeMemoryCreator}; -use crate::table::Table; +use crate::table::{Table, TableElement}; use crate::traphandlers::Trap; use crate::vmcontext::{ VMBuiltinFunctionsArray, VMCallerCheckedAnyfunc, VMContext, VMFunctionBody, VMFunctionImport, @@ -455,11 +455,7 @@ impl Instance { } // Get table element by index. - fn table_get( - &self, - table_index: DefinedTableIndex, - index: u32, - ) -> Option { + fn table_get(&self, table_index: DefinedTableIndex, index: u32) -> Option { self.tables .get(table_index) .unwrap_or_else(|| panic!("no table for index {}", table_index.index())) @@ -470,7 +466,7 @@ impl Instance { &self, table_index: DefinedTableIndex, index: u32, - val: VMCallerCheckedAnyfunc, + val: TableElement, ) -> Result<(), ()> { self.tables .get(table_index) @@ -547,7 +543,7 @@ impl Instance { // TODO(#983): investigate replacing this get/set loop with a `memcpy`. for (dst, src) in (dst..dst + len).zip(src..src + len) { table - .set(dst, elem[src as usize].clone()) + .set(dst, TableElement::FuncRef(elem[src as usize].clone())) .expect("should never panic because we already did the bounds check above"); } @@ -993,11 +989,7 @@ impl InstanceHandle { /// Get table element reference. /// /// Returns `None` if index is out of bounds. - pub fn table_get( - &self, - table_index: DefinedTableIndex, - index: u32, - ) -> Option { + pub fn table_get(&self, table_index: DefinedTableIndex, index: u32) -> Option { self.instance().table_get(table_index, index) } @@ -1008,7 +1000,7 @@ impl InstanceHandle { &self, table_index: DefinedTableIndex, index: u32, - val: VMCallerCheckedAnyfunc, + val: TableElement, ) -> Result<(), ()> { self.instance().table_set(table_index, index, val) } @@ -1174,7 +1166,10 @@ fn initialize_tables(instance: &Instance) -> Result<(), InstantiationError> { for (i, func_idx) in init.elements.iter().enumerate() { let anyfunc = instance.get_caller_checked_anyfunc(*func_idx); table - .set(u32::try_from(start + i).unwrap(), anyfunc) + .set( + u32::try_from(start + i).unwrap(), + TableElement::FuncRef(anyfunc), + ) .unwrap(); } } diff --git a/crates/runtime/src/lib.rs b/crates/runtime/src/lib.rs index 6f2896054a5f..3d295974ea0c 100644 --- a/crates/runtime/src/lib.rs +++ b/crates/runtime/src/lib.rs @@ -44,7 +44,7 @@ pub use crate::jit_int::GdbJitImageRegistration; pub use crate::memory::{RuntimeLinearMemory, RuntimeMemoryCreator}; pub use crate::mmap::Mmap; pub use crate::sig_registry::SignatureRegistry; -pub use crate::table::Table; +pub use crate::table::{Table, TableElement}; pub use crate::traphandlers::{ catch_traps, init_traps, raise_lib_trap, raise_user_trap, resume_panic, SignalHandler, Trap, }; diff --git a/crates/runtime/src/table.rs b/crates/runtime/src/table.rs index 5439eec97e9e..aa2f793f8d6f 100644 --- a/crates/runtime/src/table.rs +++ b/crates/runtime/src/table.rs @@ -3,7 +3,7 @@ //! `Table` is to WebAssembly tables what `LinearMemory` is to WebAssembly linear memories. use crate::vmcontext::{VMCallerCheckedAnyfunc, VMTableDefinition}; -use crate::Trap; +use crate::{Trap, VMExternRef}; use std::cell::RefCell; use std::convert::{TryFrom, TryInto}; use wasmtime_environ::wasm::TableElementType; @@ -12,25 +12,46 @@ use wasmtime_environ::{ir, TablePlan, TableStyle}; /// A table instance. #[derive(Debug)] pub struct Table { - vec: RefCell>, + elements: RefCell, maximum: Option, } +/// An element going into or coming out of a table. +#[derive(Clone, Debug)] +pub enum TableElement { + /// A `funcref`. + FuncRef(VMCallerCheckedAnyfunc), + /// An `exrernref`. + ExternRef(Option), +} + +#[derive(Debug)] +enum TableElements { + FuncRefs(Vec), + ExternRefs(Vec>), +} + impl Table { /// Create a new table instance with specified minimum and maximum number of elements. pub fn new(plan: &TablePlan) -> Self { - match plan.table.ty { - TableElementType::Func => (), - TableElementType::Val(ty) => { - unimplemented!("tables of types other than anyfunc ({})", ty) - } - }; - match plan.style { - TableStyle::CallerChecksSignature => Self { - vec: RefCell::new(vec![ + 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(); + TableElements::ExternRefs(vec![None; min]) + } + TableElementType::Val(ty) => unimplemented!("unsupported table type ({})", ty), + }); + match plan.style { + TableStyle::CallerChecksSignature => Self { + elements, maximum: plan.table.maximum, }, } @@ -38,7 +59,10 @@ impl Table { /// Returns the number of allocated elements. pub fn size(&self) -> u32 { - self.vec.borrow().len().try_into().unwrap() + match &*self.elements.borrow() { + TableElements::FuncRefs(x) => x.len().try_into().unwrap(), + TableElements::ExternRefs(x) => x.len().try_into().unwrap(), + } } /// Grow table by the specified amount of elements. @@ -61,33 +85,45 @@ impl Table { return None; } }; - self.vec.borrow_mut().resize( - usize::try_from(new_len).unwrap(), - VMCallerCheckedAnyfunc::default(), - ); + let new_len = usize::try_from(new_len).unwrap(); + match &mut *self.elements.borrow_mut() { + TableElements::FuncRefs(x) => x.resize(new_len, VMCallerCheckedAnyfunc::default()), + TableElements::ExternRefs(x) => x.resize(new_len, None), + } Some(size) } /// Get reference to the specified element. /// /// Returns `None` if the index is out of bounds. - pub fn get(&self, index: u32) -> Option { - self.vec.borrow().get(index as usize).cloned() + pub fn get(&self, index: u32) -> Option { + match &*self.elements.borrow() { + TableElements::FuncRefs(x) => x.get(index as usize).cloned().map(TableElement::FuncRef), + TableElements::ExternRefs(x) => { + x.get(index as usize).cloned().map(TableElement::ExternRef) + } + } } /// Set reference to the specified element. /// - /// # Panics + /// # Errors /// - /// Panics if `index` is out of bounds. - pub fn set(&self, index: u32, func: VMCallerCheckedAnyfunc) -> Result<(), ()> { - match self.vec.borrow_mut().get_mut(index as usize) { - Some(slot) => { - *slot = func; - Ok(()) + /// Returns an error if `index` is out of bounds or if this table type does + /// not match the element type. + pub fn set(&self, index: u32, elem: TableElement) -> Result<(), ()> { + let mut elems = self.elements.borrow_mut(); + match &mut *elems { + TableElements::FuncRefs(x) => { + let slot = x.get_mut(index as usize).ok_or(())?; + *slot = elem.try_into().or(Err(()))?; + } + TableElements::ExternRefs(x) => { + let slot = x.get_mut(index as usize).ok_or(())?; + *slot = elem.try_into().or(Err(()))?; } - None => Err(()), } + Ok(()) } /// Copy `len` elements from `src_table[src_index..]` into `dst_table[dst_index..]`. @@ -137,10 +173,37 @@ impl Table { /// Return a `VMTableDefinition` for exposing the table to compiled wasm code. pub fn vmtable(&self) -> VMTableDefinition { - let mut vec = self.vec.borrow_mut(); - VMTableDefinition { - base: vec.as_mut_ptr() as *mut u8, - current_elements: vec.len().try_into().unwrap(), + match &*self.elements.borrow() { + TableElements::FuncRefs(x) => VMTableDefinition { + base: x.as_ptr() as *const u8 as *mut u8, + current_elements: x.len().try_into().unwrap(), + }, + TableElements::ExternRefs(x) => VMTableDefinition { + base: x.as_ptr() as *const u8 as *mut u8, + current_elements: x.len().try_into().unwrap(), + }, + } + } +} + +impl TryFrom for VMCallerCheckedAnyfunc { + type Error = TableElement; + + fn try_from(e: TableElement) -> Result { + match e { + TableElement::FuncRef(f) => Ok(f), + _ => Err(e), + } + } +} + +impl TryFrom for Option { + type Error = TableElement; + + fn try_from(e: TableElement) -> Result { + match e { + TableElement::ExternRef(x) => Ok(x), + _ => Err(e), } } } diff --git a/crates/wasmtime/src/externals.rs b/crates/wasmtime/src/externals.rs index 5e6cf3acadb7..77e75f64f465 100644 --- a/crates/wasmtime/src/externals.rs +++ b/crates/wasmtime/src/externals.rs @@ -2,8 +2,10 @@ use crate::trampoline::{ generate_global_export, generate_memory_export, generate_table_export, StoreInstanceHandle, }; use crate::values::{from_checked_anyfunc, into_checked_anyfunc, Val}; -use crate::{ExternType, GlobalType, MemoryType, Mutability, TableType, ValType}; -use crate::{Func, Store, Trap}; +use crate::{ + ExternRef, ExternType, Func, GlobalType, MemoryType, Mutability, Store, TableType, Trap, + ValType, +}; use anyhow::{anyhow, bail, Result}; use std::slice; use wasmtime_environ::wasm; @@ -299,7 +301,11 @@ fn set_table_item( item: wasmtime_runtime::VMCallerCheckedAnyfunc, ) -> Result<()> { instance - .table_set(table_index, item_index, item) + .table_set( + table_index, + item_index, + runtime::TableElement::FuncRef(item), + ) .map_err(|()| anyhow!("table element index out of bounds")) } @@ -348,7 +354,16 @@ impl Table { pub fn get(&self, index: u32) -> Option { let table_index = self.wasmtime_table_index(); let item = self.instance.table_get(table_index, index)?; - Some(from_checked_anyfunc(item, &self.instance.store)) + match item { + runtime::TableElement::FuncRef(f) => { + Some(from_checked_anyfunc(f, &self.instance.store)) + } + runtime::TableElement::ExternRef(None) => Some(Val::ExternRef(None)), + runtime::TableElement::ExternRef(Some(x)) => Some(Val::ExternRef(Some(ExternRef { + inner: x, + store: self.instance.store.weak(), + }))), + } } /// Writes the `val` provided into `index` within this table.