cranelift-wasm: Allow more customization of ref type representations

* Allow different Cranelift IR types to be used for different Wasm reference
  types.

* Do not assume that all Wasm reference types are always a Cranelift IR
  reference type. For example, `funcref`s might not need GC in some
  implementations, and can therefore be represented with a pointer rather than a
  reference type.
This commit is contained in:
Nick Fitzgerald
2020-06-23 15:19:22 -07:00
parent 28fccaedc4
commit 03165e0cb5
5 changed files with 54 additions and 13 deletions

View File

@@ -1039,12 +1039,10 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
Operator::F32Le | Operator::F64Le => { Operator::F32Le | Operator::F64Le => {
translate_fcmp(FloatCC::LessThanOrEqual, builder, state) 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: _ } => { Operator::RefIsNull { ty: _ } => {
let arg = state.pop1(); let value = state.pop1();
let val = builder.ins().is_null(arg); state.push1(environ.translate_ref_is_null(builder.cursor(), value)?);
let val_int = builder.ins().bint(I32, val);
state.push1(val_int);
} }
Operator::RefFunc { function_index } => { Operator::RefFunc { function_index } => {
state.push1(environ.translate_ref_func(builder.cursor(), *function_index)?); state.push1(environ.translate_ref_func(builder.cursor(), *function_index)?);

View File

@@ -197,6 +197,14 @@ impl<'dummy_environment> DummyFuncEnvironment<'dummy_environment> {
)); ));
sig 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> { impl<'dummy_environment> TargetEnvironment for DummyFuncEnvironment<'dummy_environment> {

View File

@@ -133,10 +133,15 @@ pub trait TargetEnvironment {
self.target_config().pointer_bytes() self.target_config().pointer_bytes()
} }
/// Get the Cranelift reference type to use for native references. /// Get the Cranelift reference type to use for the given Wasm reference
/// type.
/// ///
/// This returns `R64` for 64-bit architectures and `R32` for 32-bit architectures. /// By default, this returns `R64` for 64-bit architectures and `R32` for
fn reference_type(&self) -> ir::Type { /// 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() { match self.pointer_type() {
ir::types::I32 => ir::types::R32, ir::types::I32 => ir::types::R32,
ir::types::I64 => ir::types::R64, ir::types::I64 => ir::types::R64,
@@ -419,6 +424,37 @@ pub trait FuncEnvironment: TargetEnvironment {
/// Translate a `elem.drop` WebAssembly instruction. /// Translate a `elem.drop` WebAssembly instruction.
fn translate_elem_drop(&mut self, pos: FuncCursor, seg_index: u32) -> WasmResult<()>; 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. /// 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: u32) -> WasmResult<ir::Value>;

View File

@@ -196,8 +196,7 @@ fn declare_locals<FE: FuncEnvironment + ?Sized>(
let constant_handle = builder.func.dfg.constants.insert([0; 16].to_vec().into()); let constant_handle = builder.func.dfg.constants.insert([0; 16].to_vec().into());
builder.ins().vconst(ir::types::I8X16, constant_handle) builder.ins().vconst(ir::types::I8X16, constant_handle)
} }
ExternRef => builder.ins().null(environ.reference_type()), ExternRef | FuncRef => environ.translate_ref_null(builder.cursor(), wasm_type)?,
FuncRef => builder.ins().null(environ.reference_type()),
ty => return Err(wasm_unsupported!("unsupported local type {:?}", ty)), ty => return Err(wasm_unsupported!("unsupported local type {:?}", ty)),
}; };

View File

@@ -143,7 +143,7 @@ pub fn type_to_type<PE: TargetEnvironment + ?Sized>(
wasmparser::Type::F32 => Ok(ir::types::F32), wasmparser::Type::F32 => Ok(ir::types::F32),
wasmparser::Type::F64 => Ok(ir::types::F64), wasmparser::Type::F64 => Ok(ir::types::F64),
wasmparser::Type::V128 => Ok(ir::types::I8X16), 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)), ty => Err(wasm_unsupported!("type_to_type: wasm type {:?}", ty)),
} }
} }
@@ -160,7 +160,7 @@ pub fn tabletype_to_type<PE: TargetEnvironment + ?Sized>(
wasmparser::Type::F32 => Ok(Some(ir::types::F32)), wasmparser::Type::F32 => Ok(Some(ir::types::F32)),
wasmparser::Type::F64 => Ok(Some(ir::types::F64)), wasmparser::Type::F64 => Ok(Some(ir::types::F64)),
wasmparser::Type::V128 => Ok(Some(ir::types::I8X16)), 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), wasmparser::Type::FuncRef => Ok(None),
ty => Err(wasm_unsupported!( ty => Err(wasm_unsupported!(
"tabletype_to_type: table wasm type {:?}", "tabletype_to_type: table wasm type {:?}",
@@ -216,7 +216,7 @@ pub fn block_with_params<PE: TargetEnvironment + ?Sized>(
builder.append_block_param(block, ir::types::F64); builder.append_block_param(block, ir::types::F64);
} }
wasmparser::Type::ExternRef | wasmparser::Type::FuncRef => { 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 => { wasmparser::Type::V128 => {
builder.append_block_param(block, ir::types::I8X16); builder.append_block_param(block, ir::types::I8X16);