reference types: Implement the table.size and table.grow instructions (#1894)

Part of #929
This commit is contained in:
Nick Fitzgerald
2020-06-18 06:57:18 -07:00
committed by GitHub
parent 06a69d18fa
commit bbd99c5bfa
8 changed files with 441 additions and 383 deletions

View File

@@ -7,11 +7,11 @@ use cranelift_codegen::ir::condcodes::*;
use cranelift_codegen::ir::immediates::{Offset32, Uimm64};
use cranelift_codegen::ir::types::*;
use cranelift_codegen::ir::{AbiParam, ArgumentPurpose, Function, InstBuilder, Signature};
use cranelift_codegen::isa::TargetFrontendConfig;
use cranelift_codegen::isa::{self, TargetFrontendConfig};
use cranelift_entity::EntityRef;
use cranelift_wasm::{
self, FuncIndex, GlobalIndex, GlobalVariable, MemoryIndex, SignatureIndex, TableIndex,
TargetEnvironment, WasmError, WasmResult,
self, FuncIndex, GlobalIndex, GlobalVariable, MemoryIndex, SignatureIndex, TableElementType,
TableIndex, TargetEnvironment, WasmError, WasmResult,
};
#[cfg(feature = "lightbeam")]
use cranelift_wasm::{DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex};
@@ -26,65 +26,146 @@ pub fn get_func_name(func_index: FuncIndex) -> ir::ExternalName {
#[derive(Copy, Clone, Debug)]
pub struct BuiltinFunctionIndex(u32);
impl BuiltinFunctionIndex {
macro_rules! declare_builtin_functions {
(
$(
$( #[$attr:meta] )*
$name:ident( $( $param:ident ),* ) -> ( $( $result:ident ),* );
)*
) => {
/// A struct with an `Option<ir::SigRef>` member for every builtin
/// function, to de-duplicate constructing/getting its signature.
struct BuiltinFunctionSignatures {
pointer_type: ir::Type,
reference_type: ir::Type,
call_conv: isa::CallConv,
$(
$name: Option<ir::SigRef>,
)*
}
impl BuiltinFunctionSignatures {
fn new(
pointer_type: ir::Type,
reference_type: ir::Type,
call_conv: isa::CallConv,
) -> Self {
Self {
pointer_type,
reference_type,
call_conv,
$(
$name: None,
)*
}
}
fn vmctx(&self) -> AbiParam {
AbiParam::special(self.pointer_type, ArgumentPurpose::VMContext)
}
fn reference(&self) -> AbiParam {
AbiParam::new(self.reference_type)
}
fn i32(&self) -> AbiParam {
AbiParam::new(I32)
}
$(
fn $name(&mut self, func: &mut Function) -> ir::SigRef {
let sig = self.$name.unwrap_or_else(|| {
func.import_signature(Signature {
params: vec![ $( self.$param() ),* ],
returns: vec![ $( self.$result() ),* ],
call_conv: self.call_conv,
})
});
self.$name = Some(sig);
sig
}
)*
}
impl BuiltinFunctionIndex {
declare_builtin_functions!(
@indices;
0;
$( $( #[$attr] )* $name; )*
);
}
};
// Base case: no more indices to declare, so define the total number of
// function indices.
(
@indices;
$len:expr;
) => {
/// Returns the total number of builtin functions.
pub const fn builtin_functions_total_number() -> u32 {
$len
}
};
// Recursive case: declare the next index, and then keep declaring the rest of
// the indices.
(
@indices;
$index:expr;
$( #[$this_attr:meta] )*
$this_name:ident;
$(
$( #[$rest_attr:meta] )*
$rest_name:ident;
)*
) => {
$( #[$this_attr] )*
pub const fn $this_name() -> Self {
Self($index)
}
declare_builtin_functions!(
@indices;
($index + 1);
$( $( #[$rest_attr] )* $rest_name; )*
);
}
}
declare_builtin_functions! {
/// Returns an index for wasm's `memory.grow` builtin function.
pub const fn get_memory32_grow_index() -> Self {
Self(0)
}
memory32_grow(vmctx, i32, i32) -> (i32);
/// Returns an index for wasm's imported `memory.grow` builtin function.
pub const fn get_imported_memory32_grow_index() -> Self {
Self(1)
}
imported_memory32_grow(vmctx, i32, i32) -> (i32);
/// Returns an index for wasm's `memory.size` builtin function.
pub const fn get_memory32_size_index() -> Self {
Self(2)
}
memory32_size(vmctx, i32) -> (i32);
/// Returns an index for wasm's imported `memory.size` builtin function.
pub const fn get_imported_memory32_size_index() -> Self {
Self(3)
}
imported_memory32_size(vmctx, i32) -> (i32);
/// Returns an index for wasm's `table.copy` when both tables are locally
/// defined.
pub const fn get_table_copy_index() -> Self {
Self(4)
}
table_copy(vmctx, i32, i32, i32, i32, i32) -> ();
/// Returns an index for wasm's `table.init`.
pub const fn get_table_init_index() -> Self {
Self(5)
}
table_init(vmctx, i32, i32, i32, i32, i32) -> ();
/// Returns an index for wasm's `elem.drop`.
pub const fn get_elem_drop_index() -> Self {
Self(6)
}
elem_drop(vmctx, i32) -> ();
/// Returns an index for wasm's `memory.copy` for locally defined memories.
pub const fn get_defined_memory_copy_index() -> Self {
Self(7)
}
defined_memory_copy(vmctx, i32, i32, i32, i32) -> ();
/// Returns an index for wasm's `memory.copy` for imported memories.
pub const fn get_imported_memory_copy_index() -> Self {
Self(8)
}
imported_memory_copy(vmctx, i32, i32, i32, i32) -> ();
/// Returns an index for wasm's `memory.fill` for locally defined memories.
pub const fn get_memory_fill_index() -> Self {
Self(9)
}
memory_fill(vmctx, i32, i32, i32, i32) -> ();
/// Returns an index for wasm's `memory.fill` for imported memories.
pub const fn get_imported_memory_fill_index() -> Self {
Self(10)
}
imported_memory_fill(vmctx, i32, i32, i32, i32) -> ();
/// Returns an index for wasm's `memory.init` instruction.
pub const fn get_memory_init_index() -> Self {
Self(11)
}
memory_init(vmctx, i32, i32, i32, i32, i32) -> ();
/// Returns an index for wasm's `data.drop` instruction.
pub const fn get_data_drop_index() -> Self {
Self(12)
}
/// Returns the total number of builtin functions.
pub const fn builtin_functions_total_number() -> u32 {
13
}
data_drop(vmctx, i32) -> ();
/// Returns an index for Wasm's `table.grow` instruction for `externref`s.
table_grow_extern_ref(vmctx, i32, i32, reference) -> (i32);
}
impl BuiltinFunctionIndex {
/// Create a new `BuiltinFunctionIndex` from its index
pub const fn from_u32(i: u32) -> Self {
Self(i)
@@ -107,37 +188,8 @@ pub struct FuncEnvironment<'module_environment> {
/// The Cranelift global holding the vmctx address.
vmctx: Option<ir::GlobalValue>,
/// The external function signature for implementing wasm's `memory.size`
/// for locally-defined 32-bit memories.
memory32_size_sig: Option<ir::SigRef>,
/// The external function signature for implementing wasm's `memory.grow`
/// for locally-defined memories.
memory_grow_sig: Option<ir::SigRef>,
/// The external function signature for implementing wasm's `table.copy`
/// (it's the same for both local and imported tables).
table_copy_sig: Option<ir::SigRef>,
/// The external function signature for implementing wasm's `table.init`.
table_init_sig: Option<ir::SigRef>,
/// The external function signature for implementing wasm's `elem.drop`.
elem_drop_sig: Option<ir::SigRef>,
/// The external function signature for implementing wasm's `memory.copy`
/// (it's the same for both local and imported memories).
memory_copy_sig: Option<ir::SigRef>,
/// The external function signature for implementing wasm's `memory.fill`
/// (it's the same for both local and imported memories).
memory_fill_sig: Option<ir::SigRef>,
/// The external function signature for implementing wasm's `memory.init`.
memory_init_sig: Option<ir::SigRef>,
/// The external function signature for implementing wasm's `data.drop`.
data_drop_sig: Option<ir::SigRef>,
/// Caches of signatures for builtin functions.
builtin_function_signatures: BuiltinFunctionSignatures,
/// Offsets to struct fields accessed by JIT code.
pub(crate) offsets: VMOffsets,
@@ -151,19 +203,20 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
module: &'module_environment ModuleLocal,
tunables: &'module_environment Tunables,
) -> Self {
let builtin_function_signatures = BuiltinFunctionSignatures::new(
target_config.pointer_type(),
match target_config.pointer_type() {
ir::types::I32 => ir::types::R32,
ir::types::I64 => ir::types::R64,
_ => panic!(),
},
target_config.default_call_conv,
);
Self {
target_config,
module,
vmctx: None,
memory32_size_sig: None,
memory_grow_sig: None,
table_copy_sig: None,
table_init_sig: None,
elem_drop_sig: None,
memory_copy_sig: None,
memory_fill_sig: None,
memory_init_sig: None,
data_drop_sig: None,
builtin_function_signatures,
offsets: VMOffsets::new(target_config.pointer_bytes(), module),
tunables,
}
@@ -181,22 +234,6 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
})
}
fn get_memory_grow_sig(&mut self, func: &mut Function) -> ir::SigRef {
let sig = self.memory_grow_sig.unwrap_or_else(|| {
func.import_signature(Signature {
params: vec![
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
AbiParam::new(I32),
AbiParam::new(I32),
],
returns: vec![AbiParam::new(I32)],
call_conv: self.target_config.default_call_conv,
})
});
self.memory_grow_sig = Some(sig);
sig
}
/// Return the memory.grow function signature to call for the given index, along with the
/// translated index value to pass to it and its index in `VMBuiltinFunctionsArray`.
fn get_memory_grow_func(
@@ -206,34 +243,20 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
) -> (ir::SigRef, usize, BuiltinFunctionIndex) {
if self.module.is_imported_memory(index) {
(
self.get_memory_grow_sig(func),
self.builtin_function_signatures
.imported_memory32_grow(func),
index.index(),
BuiltinFunctionIndex::get_imported_memory32_grow_index(),
BuiltinFunctionIndex::imported_memory32_grow(),
)
} else {
(
self.get_memory_grow_sig(func),
self.builtin_function_signatures.memory32_grow(func),
self.module.defined_memory_index(index).unwrap().index(),
BuiltinFunctionIndex::get_memory32_grow_index(),
BuiltinFunctionIndex::memory32_grow(),
)
}
}
fn get_memory32_size_sig(&mut self, func: &mut Function) -> ir::SigRef {
let sig = self.memory32_size_sig.unwrap_or_else(|| {
func.import_signature(Signature {
params: vec![
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
AbiParam::new(I32),
],
returns: vec![AbiParam::new(I32)],
call_conv: self.target_config.default_call_conv,
})
});
self.memory32_size_sig = Some(sig);
sig
}
/// Return the memory.size function signature to call for the given index, along with the
/// translated index value to pass to it and its index in `VMBuiltinFunctionsArray`.
fn get_memory_size_func(
@@ -243,41 +266,38 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
) -> (ir::SigRef, usize, BuiltinFunctionIndex) {
if self.module.is_imported_memory(index) {
(
self.get_memory32_size_sig(func),
self.builtin_function_signatures
.imported_memory32_size(func),
index.index(),
BuiltinFunctionIndex::get_imported_memory32_size_index(),
BuiltinFunctionIndex::imported_memory32_size(),
)
} else {
(
self.get_memory32_size_sig(func),
self.builtin_function_signatures.memory32_size(func),
self.module.defined_memory_index(index).unwrap().index(),
BuiltinFunctionIndex::get_memory32_size_index(),
BuiltinFunctionIndex::memory32_size(),
)
}
}
fn get_table_copy_sig(&mut self, func: &mut Function) -> ir::SigRef {
let sig = self.table_copy_sig.unwrap_or_else(|| {
func.import_signature(Signature {
params: vec![
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
// Destination table index.
AbiParam::new(I32),
// Source table index.
AbiParam::new(I32),
// Index within destination table.
AbiParam::new(I32),
// Index within source table.
AbiParam::new(I32),
// Number of elements to copy.
AbiParam::new(I32),
],
returns: vec![],
call_conv: self.target_config.default_call_conv,
})
});
self.table_copy_sig = Some(sig);
sig
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(
@@ -286,94 +306,28 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
dst_table_index: TableIndex,
src_table_index: TableIndex,
) -> (ir::SigRef, usize, usize, BuiltinFunctionIndex) {
let sig = self.get_table_copy_sig(func);
let sig = self.builtin_function_signatures.table_copy(func);
(
sig,
dst_table_index.as_u32() as usize,
src_table_index.as_u32() as usize,
BuiltinFunctionIndex::get_table_copy_index(),
BuiltinFunctionIndex::table_copy(),
)
}
fn get_table_init_sig(&mut self, func: &mut Function) -> ir::SigRef {
let sig = self.table_init_sig.unwrap_or_else(|| {
func.import_signature(Signature {
params: vec![
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
// Table index.
AbiParam::new(I32),
// Segment index.
AbiParam::new(I32),
// Destination index within table.
AbiParam::new(I32),
// Source index within segment.
AbiParam::new(I32),
// Number of elements to initialize.
AbiParam::new(I32),
],
returns: vec![],
call_conv: self.target_config.default_call_conv,
})
});
self.table_init_sig = Some(sig);
sig
}
fn get_table_init_func(
&mut self,
func: &mut Function,
table_index: TableIndex,
) -> (ir::SigRef, usize, BuiltinFunctionIndex) {
let sig = self.get_table_init_sig(func);
let sig = self.builtin_function_signatures.table_init(func);
let table_index = table_index.as_u32() as usize;
(
sig,
table_index,
BuiltinFunctionIndex::get_table_init_index(),
)
}
fn get_elem_drop_sig(&mut self, func: &mut Function) -> ir::SigRef {
let sig = self.elem_drop_sig.unwrap_or_else(|| {
func.import_signature(Signature {
params: vec![
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
// Element index.
AbiParam::new(I32),
],
returns: vec![],
call_conv: self.target_config.default_call_conv,
})
});
self.elem_drop_sig = Some(sig);
sig
(sig, table_index, BuiltinFunctionIndex::table_init())
}
fn get_elem_drop_func(&mut self, func: &mut Function) -> (ir::SigRef, BuiltinFunctionIndex) {
let sig = self.get_elem_drop_sig(func);
(sig, BuiltinFunctionIndex::get_elem_drop_index())
}
fn get_memory_copy_sig(&mut self, func: &mut Function) -> ir::SigRef {
let sig = self.memory_copy_sig.unwrap_or_else(|| {
func.import_signature(Signature {
params: vec![
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
// Memory index.
AbiParam::new(I32),
// Destination address.
AbiParam::new(I32),
// Source address.
AbiParam::new(I32),
// Length.
AbiParam::new(I32),
],
returns: vec![],
call_conv: self.target_config.default_call_conv,
})
});
self.memory_copy_sig = Some(sig);
sig
let sig = self.builtin_function_signatures.elem_drop(func);
(sig, BuiltinFunctionIndex::elem_drop())
}
fn get_memory_copy_func(
@@ -381,113 +335,53 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
func: &mut Function,
memory_index: MemoryIndex,
) -> (ir::SigRef, usize, BuiltinFunctionIndex) {
let sig = self.get_memory_copy_sig(func);
if let Some(defined_memory_index) = self.module.defined_memory_index(memory_index) {
(
sig,
self.builtin_function_signatures.defined_memory_copy(func),
defined_memory_index.index(),
BuiltinFunctionIndex::get_defined_memory_copy_index(),
BuiltinFunctionIndex::defined_memory_copy(),
)
} else {
(
sig,
self.builtin_function_signatures.imported_memory_copy(func),
memory_index.index(),
BuiltinFunctionIndex::get_imported_memory_copy_index(),
BuiltinFunctionIndex::imported_memory_copy(),
)
}
}
fn get_memory_fill_sig(&mut self, func: &mut Function) -> ir::SigRef {
let sig = self.memory_fill_sig.unwrap_or_else(|| {
func.import_signature(Signature {
params: vec![
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
// Memory index.
AbiParam::new(I32),
// Destination address.
AbiParam::new(I32),
// Value.
AbiParam::new(I32),
// Length.
AbiParam::new(I32),
],
returns: vec![],
call_conv: self.target_config.default_call_conv,
})
});
self.memory_fill_sig = Some(sig);
sig
}
fn get_memory_fill_func(
&mut self,
func: &mut Function,
memory_index: MemoryIndex,
) -> (ir::SigRef, usize, BuiltinFunctionIndex) {
let sig = self.get_memory_fill_sig(func);
if let Some(defined_memory_index) = self.module.defined_memory_index(memory_index) {
(
sig,
self.builtin_function_signatures.memory_fill(func),
defined_memory_index.index(),
BuiltinFunctionIndex::get_memory_fill_index(),
BuiltinFunctionIndex::memory_fill(),
)
} else {
(
sig,
self.builtin_function_signatures.imported_memory_fill(func),
memory_index.index(),
BuiltinFunctionIndex::get_imported_memory_fill_index(),
BuiltinFunctionIndex::imported_memory_fill(),
)
}
}
fn get_memory_init_sig(&mut self, func: &mut Function) -> ir::SigRef {
let sig = self.memory_init_sig.unwrap_or_else(|| {
func.import_signature(Signature {
params: vec![
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
// Memory index.
AbiParam::new(I32),
// Data index.
AbiParam::new(I32),
// Destination address.
AbiParam::new(I32),
// Source index within the data segment.
AbiParam::new(I32),
// Length.
AbiParam::new(I32),
],
returns: vec![],
call_conv: self.target_config.default_call_conv,
})
});
self.memory_init_sig = Some(sig);
sig
}
fn get_memory_init_func(&mut self, func: &mut Function) -> (ir::SigRef, BuiltinFunctionIndex) {
let sig = self.get_memory_init_sig(func);
(sig, BuiltinFunctionIndex::get_memory_init_index())
}
fn get_data_drop_sig(&mut self, func: &mut Function) -> ir::SigRef {
let sig = self.data_drop_sig.unwrap_or_else(|| {
func.import_signature(Signature {
params: vec![
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
// Data index.
AbiParam::new(I32),
],
returns: vec![],
call_conv: self.target_config.default_call_conv,
})
});
self.data_drop_sig = Some(sig);
sig
(
self.builtin_function_signatures.memory_init(func),
BuiltinFunctionIndex::memory_init(),
)
}
fn get_data_drop_func(&mut self, func: &mut Function) -> (ir::SigRef, BuiltinFunctionIndex) {
let sig = self.get_data_drop_sig(func);
(sig, BuiltinFunctionIndex::get_data_drop_index())
(
self.builtin_function_signatures.data_drop(func),
BuiltinFunctionIndex::data_drop(),
)
}
/// Translates load of builtin function and returns a pair of values `vmctx`
@@ -726,14 +620,29 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
fn translate_table_grow(
&mut self,
_: cranelift_codegen::cursor::FuncCursor<'_>,
_: TableIndex,
_: ir::Value,
_: ir::Value,
mut pos: cranelift_codegen::cursor::FuncCursor<'_>,
table_index: TableIndex,
delta: ir::Value,
init_value: ir::Value,
) -> WasmResult<ir::Value> {
Err(WasmError::Unsupported(
"the `table.grow` instruction is not supported yet".into(),
))
let (func_sig, func_idx, table_index_arg) =
self.get_table_grow_func(&mut pos.func, table_index)?;
let table_index_arg = pos.ins().iconst(I32, table_index_arg as i64);
let (vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx);
let call_inst = pos.ins().call_indirect(
func_sig,
func_addr,
&[vmctx, table_index_arg, delta, init_value],
);
Ok(pos
.func
.dfg
.inst_results(call_inst)
.first()
.copied()
.unwrap())
}
fn translate_table_get(
@@ -1180,13 +1089,12 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
fn translate_table_size(
&mut self,
_pos: FuncCursor,
_index: TableIndex,
_table: ir::Table,
mut pos: FuncCursor,
_table_index: TableIndex,
table: ir::Table,
) -> WasmResult<ir::Value> {
Err(WasmError::Unsupported(
"reference types: `table.size`".to_string(),
))
let size_gv = pos.func.tables[table].bound_gv;
Ok(pos.ins().global_value(ir::types::I32, size_gv))
}
fn translate_table_copy(