Browse Source

wasmtime-runtime: Allow tables to internally hold `externref`s (#1882)

This commit enables `wasmtime_runtime::Table` to internally hold elements of
either `funcref` (all that is currently supported) or `externref` (newly
introduced in this commit).

This commit updates `Table`'s API, but does NOT generally propagate those
changes outwards all the way through the Wasmtime embedding API. It only does
enough to get everything compiling and the current test suite passing. It is
expected that as we implement more of the reference types spec, we will bubble
these changes out and expose them to the embedding API.
pull/1884/head
Nick Fitzgerald 4 years ago
committed by GitHub
parent
commit
8d671c21e2
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      crates/environ/src/func_environ.rs
  2. 25
      crates/runtime/src/instance.rs
  3. 2
      crates/runtime/src/lib.rs
  4. 123
      crates/runtime/src/table.rs
  5. 23
      crates/wasmtime/src/externals.rs

2
crates/environ/src/func_environ.rs

@ -1192,7 +1192,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
_table: ir::Table,
) -> WasmResult<ir::Value> {
Err(WasmError::Unsupported(
"bulk memory: `table.size`".to_string(),
"reference types: `table.size`".to_string(),
))
}

25
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<VMCallerCheckedAnyfunc> {
fn table_get(&self, table_index: DefinedTableIndex, index: u32) -> Option<TableElement> {
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<VMCallerCheckedAnyfunc> {
pub fn table_get(&self, table_index: DefinedTableIndex, index: u32) -> Option<TableElement> {
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();
}
}

2
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,
};

123
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<Vec<VMCallerCheckedAnyfunc>>,
elements: RefCell<TableElements>,
maximum: Option<u32>,
}
/// An element going into or coming out of a table.
#[derive(Clone, Debug)]
pub enum TableElement {
/// A `funcref`.
FuncRef(VMCallerCheckedAnyfunc),
/// An `exrernref`.
ExternRef(Option<VMExternRef>),
}
#[derive(Debug)]
enum TableElements {
FuncRefs(Vec<VMCallerCheckedAnyfunc>),
ExternRefs(Vec<Option<VMExternRef>>),
}
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<VMCallerCheckedAnyfunc> {
self.vec.borrow().get(index as usize).cloned()
pub fn get(&self, index: u32) -> Option<TableElement> {
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<TableElement> for VMCallerCheckedAnyfunc {
type Error = TableElement;
fn try_from(e: TableElement) -> Result<Self, Self::Error> {
match e {
TableElement::FuncRef(f) => Ok(f),
_ => Err(e),
}
}
}
impl TryFrom<TableElement> for Option<VMExternRef> {
type Error = TableElement;
fn try_from(e: TableElement) -> Result<Self, Self::Error> {
match e {
TableElement::ExternRef(x) => Ok(x),
_ => Err(e),
}
}
}

23
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<Val> {
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.

Loading…
Cancel
Save