Skip to content

Commit

Permalink
Merge pull request #1901 from fitzgen/ref-func
Browse files Browse the repository at this point in the history
Support for `funcref`s, `ref.func`, and `table.grow` with `funcref`s
  • Loading branch information
fitzgen authored Jun 24, 2020
2 parents 91980b2 + 58bb5dd commit 1ea2f47
Show file tree
Hide file tree
Showing 42 changed files with 709 additions and 340 deletions.
4 changes: 3 additions & 1 deletion build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,9 @@ fn ignore(testsuite: &str, testname: &str, strategy: &str) -> bool {

("reference_types", "table_copy_on_imported_tables")
| ("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
// supports reference types on x64.
return env::var("CARGO_CFG_TARGET_ARCH").unwrap() != "x86_64";
Expand Down
46 changes: 27 additions & 19 deletions cranelift/wasm/src/code_translator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
// `index` is the index of the function's signature and `table_index` is the index of
// the table to search the function in.
let (sigref, num_args) = state.get_indirect_sig(builder.func, *index, environ)?;
let table = state.get_table(builder.func, *table_index, environ)?;
let table = state.get_or_create_table(builder.func, *table_index, environ)?;
let callee = state.pop1();

// Bitcast any vector arguments to their default type, I8X16, before calling.
Expand Down Expand Up @@ -1039,15 +1039,14 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
Operator::F32Le | Operator::F64Le => {
translate_fcmp(FloatCC::LessThanOrEqual, builder, state)
}
Operator::RefNull { ty: _ } => state.push1(builder.ins().null(environ.reference_type())),
Operator::RefNull { ty } => state.push1(environ.translate_ref_null(builder.cursor(), *ty)?),
Operator::RefIsNull { ty: _ } => {
let arg = state.pop1();
let val = builder.ins().is_null(arg);
let val_int = builder.ins().bint(I32, val);
state.push1(val_int);
let value = state.pop1();
state.push1(environ.translate_ref_is_null(builder.cursor(), value)?);
}
Operator::RefFunc { function_index } => {
state.push1(environ.translate_ref_func(builder.cursor(), *function_index)?);
let index = FuncIndex::from_u32(*function_index);
state.push1(environ.translate_ref_func(builder.cursor(), index)?);
}
Operator::AtomicNotify { .. }
| Operator::I32AtomicWait { .. }
Expand Down Expand Up @@ -1163,41 +1162,50 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
environ.translate_data_drop(builder.cursor(), *segment)?;
}
Operator::TableSize { table: index } => {
let table = state.get_table(builder.func, *index, environ)?;
let table = state.get_or_create_table(builder.func, *index, environ)?;
state.push1(environ.translate_table_size(
builder.cursor(),
TableIndex::from_u32(*index),
table,
)?);
}
Operator::TableGrow { table } => {
let table_index = TableIndex::from_u32(*table);
Operator::TableGrow { table: index } => {
let table_index = TableIndex::from_u32(*index);
let table = state.get_or_create_table(builder.func, *index, environ)?;
let delta = state.pop1();
let init_value = state.pop1();
state.push1(environ.translate_table_grow(
builder.cursor(),
table_index,
table,
delta,
init_value,
)?);
}
Operator::TableGet { table } => {
let table_index = TableIndex::from_u32(*table);
Operator::TableGet { table: index } => {
let table_index = TableIndex::from_u32(*index);
let table = state.get_or_create_table(builder.func, *index, environ)?;
let index = state.pop1();
state.push1(environ.translate_table_get(builder.cursor(), table_index, index)?);
state.push1(environ.translate_table_get(
builder.cursor(),
table_index,
table,
index,
)?);
}
Operator::TableSet { table } => {
let table_index = TableIndex::from_u32(*table);
Operator::TableSet { table: index } => {
let table_index = TableIndex::from_u32(*index);
let table = state.get_or_create_table(builder.func, *index, environ)?;
let value = state.pop1();
let index = state.pop1();
environ.translate_table_set(builder.cursor(), table_index, value, index)?;
environ.translate_table_set(builder.cursor(), table_index, table, value, index)?;
}
Operator::TableCopy {
dst_table: dst_table_index,
src_table: src_table_index,
} => {
let dst_table = state.get_table(builder.func, *dst_table_index, environ)?;
let src_table = state.get_table(builder.func, *src_table_index, environ)?;
let dst_table = state.get_or_create_table(builder.func, *dst_table_index, environ)?;
let src_table = state.get_or_create_table(builder.func, *src_table_index, environ)?;
let len = state.pop1();
let src = state.pop1();
let dest = state.pop1();
Expand All @@ -1223,7 +1231,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
segment,
table: table_index,
} => {
let table = state.get_table(builder.func, *table_index, environ)?;
let table = state.get_or_create_table(builder.func, *table_index, environ)?;
let len = state.pop1();
let src = state.pop1();
let dest = state.pop1();
Expand Down
13 changes: 12 additions & 1 deletion cranelift/wasm/src/environ/dummy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,14 @@ impl<'dummy_environment> DummyFuncEnvironment<'dummy_environment> {
));
sig
}

fn reference_type(&self) -> ir::Type {
match self.pointer_type() {
ir::types::I32 => ir::types::R32,
ir::types::I64 => ir::types::R64,
_ => panic!("unsupported pointer type"),
}
}
}

impl<'dummy_environment> TargetEnvironment for DummyFuncEnvironment<'dummy_environment> {
Expand Down Expand Up @@ -435,6 +443,7 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
&mut self,
mut pos: FuncCursor,
_table_index: TableIndex,
_table: ir::Table,
_delta: ir::Value,
_init_value: ir::Value,
) -> WasmResult<ir::Value> {
Expand All @@ -445,6 +454,7 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
&mut self,
mut pos: FuncCursor,
_table_index: TableIndex,
_table: ir::Table,
_index: ir::Value,
) -> WasmResult<ir::Value> {
Ok(pos.ins().null(self.reference_type()))
Expand All @@ -454,6 +464,7 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
&mut self,
_pos: FuncCursor,
_table_index: TableIndex,
_table: ir::Table,
_value: ir::Value,
_index: ir::Value,
) -> WasmResult<()> {
Expand Down Expand Up @@ -505,7 +516,7 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
fn translate_ref_func(
&mut self,
mut pos: FuncCursor,
_func_index: u32,
_func_index: FuncIndex,
) -> WasmResult<ir::Value> {
Ok(pos.ins().null(self.reference_type()))
}
Expand Down
53 changes: 48 additions & 5 deletions cranelift/wasm/src/environ/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,15 @@ pub trait TargetEnvironment {
self.target_config().pointer_bytes()
}

/// Get the Cranelift reference type to use for native references.
///
/// This returns `R64` for 64-bit architectures and `R32` for 32-bit architectures.
fn reference_type(&self) -> ir::Type {
/// Get the Cranelift reference type to use for the given Wasm reference
/// type.
///
/// By default, this returns `R64` for 64-bit architectures and `R32` for
/// 32-bit architectures. If you override this, then you should also
/// override `FuncEnvironment::{translate_ref_null, translate_ref_is_null}`
/// as well.
fn reference_type(&self, ty: WasmType) -> ir::Type {
let _ = ty;
match self.pointer_type() {
ir::types::I32 => ir::types::R32,
ir::types::I64 => ir::types::R64,
Expand Down Expand Up @@ -355,6 +360,7 @@ pub trait FuncEnvironment: TargetEnvironment {
&mut self,
pos: FuncCursor,
table_index: TableIndex,
table: ir::Table,
delta: ir::Value,
init_value: ir::Value,
) -> WasmResult<ir::Value>;
Expand All @@ -364,6 +370,7 @@ pub trait FuncEnvironment: TargetEnvironment {
&mut self,
pos: FuncCursor,
table_index: TableIndex,
table: ir::Table,
index: ir::Value,
) -> WasmResult<ir::Value>;

Expand All @@ -372,6 +379,7 @@ pub trait FuncEnvironment: TargetEnvironment {
&mut self,
pos: FuncCursor,
table_index: TableIndex,
table: ir::Table,
value: ir::Value,
index: ir::Value,
) -> WasmResult<()>;
Expand Down Expand Up @@ -416,8 +424,43 @@ pub trait FuncEnvironment: TargetEnvironment {
/// Translate a `elem.drop` WebAssembly instruction.
fn translate_elem_drop(&mut self, pos: FuncCursor, seg_index: u32) -> WasmResult<()>;

/// Translate a `ref.null T` WebAssembly instruction.
///
/// By default, translates into a null reference type.
///
/// Override this if you don't use Cranelift reference types for all Wasm
/// reference types (e.g. you use a raw pointer for `funcref`s) or if the
/// null sentinel is not a null reference type pointer for your type. If you
/// override this method, then you should also override
/// `translate_ref_is_null` as well.
fn translate_ref_null(&mut self, mut pos: FuncCursor, ty: WasmType) -> WasmResult<ir::Value> {
let _ = ty;
Ok(pos.ins().null(self.reference_type(ty)))
}

/// Translate a `ref.is_null` WebAssembly instruction.
///
/// By default, assumes that `value` is a Cranelift reference type, and that
/// a null Cranelift reference type is the null value for all Wasm reference
/// types.
///
/// If you override this method, you probably also want to override
/// `translate_ref_null` as well.
fn translate_ref_is_null(
&mut self,
mut pos: FuncCursor,
value: ir::Value,
) -> WasmResult<ir::Value> {
let is_null = pos.ins().is_null(value);
Ok(pos.ins().bint(ir::types::I32, is_null))
}

/// Translate a `ref.func` WebAssembly instruction.
fn translate_ref_func(&mut self, pos: FuncCursor, func_index: u32) -> WasmResult<ir::Value>;
fn translate_ref_func(
&mut self,
pos: FuncCursor,
func_index: FuncIndex,
) -> WasmResult<ir::Value>;

/// Translate a `global.get` WebAssembly instruction at `pos` for a global
/// that is custom.
Expand Down
3 changes: 1 addition & 2 deletions cranelift/wasm/src/func_translator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,7 @@ fn declare_locals<FE: FuncEnvironment + ?Sized>(
let constant_handle = builder.func.dfg.constants.insert([0; 16].to_vec().into());
builder.ins().vconst(ir::types::I8X16, constant_handle)
}
ExternRef => builder.ins().null(environ.reference_type()),
FuncRef => builder.ins().null(environ.reference_type()),
ExternRef | FuncRef => environ.translate_ref_null(builder.cursor(), wasm_type)?,
ty => return Err(wasm_unsupported!("unsupported local type {:?}", ty)),
};

Expand Down
4 changes: 4 additions & 0 deletions cranelift/wasm/src/sections_translator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ pub fn parse_import_section<'data>(
ImportSectionEntryType::Global(ref ty) => {
environ.declare_global_import(
Global {
wasm_ty: ty.content_type,
ty: type_to_type(ty.content_type, environ).unwrap(),
mutability: ty.mutable,
initializer: GlobalInit::Import,
Expand All @@ -108,6 +109,7 @@ pub fn parse_import_section<'data>(
ImportSectionEntryType::Table(ref tab) => {
environ.declare_table_import(
Table {
wasm_ty: tab.element_type,
ty: match tabletype_to_type(tab.element_type, environ)? {
Some(t) => TableElementType::Val(t),
None => TableElementType::Func,
Expand Down Expand Up @@ -157,6 +159,7 @@ pub fn parse_table_section(
for entry in tables {
let table = entry?;
environ.declare_table(Table {
wasm_ty: table.element_type,
ty: match tabletype_to_type(table.element_type, environ)? {
Some(t) => TableElementType::Val(t),
None => TableElementType::Func,
Expand Down Expand Up @@ -227,6 +230,7 @@ pub fn parse_global_section(
}
};
let global = Global {
wasm_ty: content_type,
ty: type_to_type(content_type, environ).unwrap(),
mutability: mutable,
initializer,
Expand Down
4 changes: 2 additions & 2 deletions cranelift/wasm/src/state/func_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ pub struct FuncTranslationState {
heaps: HashMap<MemoryIndex, ir::Heap>,

// Map of tables that have been created by `FuncEnvironment::make_table`.
tables: HashMap<TableIndex, ir::Table>,
pub(crate) tables: HashMap<TableIndex, ir::Table>,

// Map of indirect call signatures that have been created by
// `FuncEnvironment::make_indirect_sig()`.
Expand Down Expand Up @@ -446,7 +446,7 @@ impl FuncTranslationState {

/// Get the `Table` reference that should be used to access table `index`.
/// Create the reference if necessary.
pub(crate) fn get_table<FE: FuncEnvironment + ?Sized>(
pub(crate) fn get_or_create_table<FE: FuncEnvironment + ?Sized>(
&mut self,
func: &mut ir::Function,
index: u32,
Expand Down
24 changes: 17 additions & 7 deletions cranelift/wasm/src/translation_utils.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! Helper functions and structures for the translation.
use crate::environ::{TargetEnvironment, WasmResult};
use crate::environ::{TargetEnvironment, WasmResult, WasmType};
use crate::state::ModuleTranslationState;
use crate::wasm_unsupported;
use core::u32;
Expand Down Expand Up @@ -67,10 +67,18 @@ entity_impl!(DataIndex);
pub struct ElemIndex(u32);
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)]
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,
/// A flag indicating whether the value may change at runtime.
pub mutability: bool,
Expand Down Expand Up @@ -104,7 +112,9 @@ pub enum GlobalInit {
/// WebAssembly table.
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
pub struct Table {
/// The type of data stored in elements of the table.
/// The table elements' Wasm type.
pub wasm_ty: WasmType,
/// The table elements' Cranelift type.
pub ty: TableElementType,
/// The minimum number of elements in the table.
pub minimum: u32,
Expand Down Expand Up @@ -143,7 +153,7 @@ pub fn type_to_type<PE: TargetEnvironment + ?Sized>(
wasmparser::Type::F32 => Ok(ir::types::F32),
wasmparser::Type::F64 => Ok(ir::types::F64),
wasmparser::Type::V128 => Ok(ir::types::I8X16),
wasmparser::Type::ExternRef | wasmparser::Type::FuncRef => Ok(environ.reference_type()),
wasmparser::Type::ExternRef | wasmparser::Type::FuncRef => Ok(environ.reference_type(ty)),
ty => Err(wasm_unsupported!("type_to_type: wasm type {:?}", ty)),
}
}
Expand All @@ -160,7 +170,7 @@ pub fn tabletype_to_type<PE: TargetEnvironment + ?Sized>(
wasmparser::Type::F32 => Ok(Some(ir::types::F32)),
wasmparser::Type::F64 => Ok(Some(ir::types::F64)),
wasmparser::Type::V128 => Ok(Some(ir::types::I8X16)),
wasmparser::Type::ExternRef => Ok(Some(environ.reference_type())),
wasmparser::Type::ExternRef => Ok(Some(environ.reference_type(ty))),
wasmparser::Type::FuncRef => Ok(None),
ty => Err(wasm_unsupported!(
"tabletype_to_type: table wasm type {:?}",
Expand Down Expand Up @@ -216,7 +226,7 @@ pub fn block_with_params<PE: TargetEnvironment + ?Sized>(
builder.append_block_param(block, ir::types::F64);
}
wasmparser::Type::ExternRef | wasmparser::Type::FuncRef => {
builder.append_block_param(block, environ.reference_type());
builder.append_block_param(block, environ.reference_type(*ty));
}
wasmparser::Type::V128 => {
builder.append_block_param(block, ir::types::I8X16);
Expand Down
Loading

0 comments on commit 1ea2f47

Please sign in to comment.