Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Add wasm reference/pointers translation. #1073

Merged
merged 1 commit into from
Dec 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions cranelift-wasm/src/code_translator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
environ: &mut FE,
) -> WasmResult<()> {
if !state.reachable {
translate_unreachable_operator(module_translation_state, &op, builder, state)?;
translate_unreachable_operator(module_translation_state, &op, builder, state, environ)?;
return Ok(());
}

Expand Down Expand Up @@ -139,13 +139,13 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
***********************************************************************************/
Operator::Block { ty } => {
let (params, results) = blocktype_params_results(module_translation_state, *ty)?;
let next = ebb_with_params(builder, results)?;
let next = ebb_with_params(builder, results, environ)?;
state.push_block(next, params.len(), results.len());
}
Operator::Loop { ty } => {
let (params, results) = blocktype_params_results(module_translation_state, *ty)?;
let loop_body = ebb_with_params(builder, params)?;
let next = ebb_with_params(builder, results)?;
let loop_body = ebb_with_params(builder, params, environ)?;
let next = ebb_with_params(builder, results, environ)?;
builder.ins().jump(loop_body, state.peekn(params.len()));
state.push_loop(loop_body, next, params.len(), results.len());

Expand All @@ -168,16 +168,16 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
// destination ebb following the whole `if...end`. If we do end
// up discovering an `else`, then we will allocate an ebb for it
// and go back and patch the jump.
let destination = ebb_with_params(builder, results)?;
let destination = ebb_with_params(builder, results, environ)?;
let branch_inst = builder
.ins()
.brz(val, destination, state.peekn(params.len()));
(destination, ElseData::NoElse { branch_inst })
} else {
// The `if` type signature is not valid without an `else` block,
// so we eagerly allocate the `else` block here.
let destination = ebb_with_params(builder, results)?;
let else_block = ebb_with_params(builder, params)?;
let destination = ebb_with_params(builder, results, environ)?;
let else_block = ebb_with_params(builder, params, environ)?;
builder
.ins()
.brz(val, else_block, state.peekn(params.len()));
Expand Down Expand Up @@ -229,7 +229,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
let (params, _results) =
blocktype_params_results(module_translation_state, blocktype)?;
debug_assert_eq!(params.len(), num_return_values);
let else_ebb = ebb_with_params(builder, params)?;
let else_ebb = ebb_with_params(builder, params, environ)?;
builder.ins().jump(destination, state.peekn(params.len()));
state.popn(params.len());

Expand Down Expand Up @@ -1352,11 +1352,12 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
/// Deals with a Wasm instruction located in an unreachable portion of the code. Most of them
/// are dropped but special ones like `End` or `Else` signal the potential end of the unreachable
/// portion so the translation state must be updated accordingly.
fn translate_unreachable_operator(
fn translate_unreachable_operator<FE: FuncEnvironment + ?Sized>(
module_translation_state: &ModuleTranslationState,
op: &Operator,
builder: &mut FunctionBuilder,
state: &mut FuncTranslationState,
environ: &mut FE,
) -> WasmResult<()> {
debug_assert!(!state.reachable);
match *op {
Expand Down Expand Up @@ -1397,7 +1398,7 @@ fn translate_unreachable_operator(
ElseData::NoElse { branch_inst } => {
let (params, _results) =
blocktype_params_results(module_translation_state, blocktype)?;
let else_ebb = ebb_with_params(builder, params)?;
let else_ebb = ebb_with_params(builder, params, environ)?;

// We change the target of the branch instruction.
builder.change_jump_destination(branch_inst, else_ebb);
Expand Down
12 changes: 9 additions & 3 deletions cranelift-wasm/src/environ/dummy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
//! [wasmtime-environ]: https://crates.io/crates/wasmtime-environ
//! [Wasmtime]: https://github.com/bytecodealliance/wasmtime

use crate::environ::{FuncEnvironment, GlobalVariable, ModuleEnvironment, ReturnMode, WasmResult};
use crate::environ::{
FuncEnvironment, GlobalVariable, ModuleEnvironment, ReturnMode, TargetEnvironment, WasmResult,
};
use crate::func_translator::FuncTranslator;
use crate::state::ModuleTranslationState;
use crate::translation_utils::{
Expand Down Expand Up @@ -192,11 +194,13 @@ impl<'dummy_environment> DummyFuncEnvironment<'dummy_environment> {
}
}

impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environment> {
impl<'dummy_environment> TargetEnvironment for DummyFuncEnvironment<'dummy_environment> {
fn target_config(&self) -> TargetFrontendConfig {
self.mod_info.config
}
}

impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environment> {
fn return_mode(&self) -> ReturnMode {
self.return_mode
}
Expand Down Expand Up @@ -454,11 +458,13 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
}
}

impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
impl TargetEnvironment for DummyEnvironment {
fn target_config(&self) -> TargetFrontendConfig {
self.info.config
}
}

impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
fn declare_signature(&mut self, sig: ir::Signature) -> WasmResult<()> {
self.info.signatures.push(sig);
Ok(())
Expand Down
3 changes: 2 additions & 1 deletion cranelift-wasm/src/environ/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ mod spec;

pub use crate::environ::dummy::DummyEnvironment;
pub use crate::environ::spec::{
FuncEnvironment, GlobalVariable, ModuleEnvironment, ReturnMode, WasmError, WasmResult,
FuncEnvironment, GlobalVariable, ModuleEnvironment, ReturnMode, TargetEnvironment, WasmError,
WasmResult,
};
34 changes: 17 additions & 17 deletions cranelift-wasm/src/environ/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,8 @@ pub enum ReturnMode {
FallthroughReturn,
}

/// Environment affecting the translation of a single WebAssembly function.
///
/// A `FuncEnvironment` trait object is required to translate a WebAssembly function to Cranelift
/// IR. The function environment provides information about the WebAssembly module as well as the
/// runtime environment.
pub trait FuncEnvironment {
/// Environment affecting the translation of a WebAssembly.
pub trait TargetEnvironment {
/// Get the information needed to produce Cranelift IR for the given target.
fn target_config(&self) -> TargetFrontendConfig;

Expand All @@ -124,13 +120,6 @@ pub trait FuncEnvironment {
self.target_config().pointer_bytes()
}

/// Should the code be structured to use a single `fallthrough_return` instruction at the end
/// of the function body, rather than `return` instructions as needed? This is used by VMs
/// to append custom epilogues.
fn return_mode(&self) -> ReturnMode {
ReturnMode::NormalReturns
}

/// Get the Cranelift reference type to use for native references.
///
/// This returns `R64` for 64-bit architectures and `R32` for 32-bit architectures.
Expand All @@ -141,6 +130,20 @@ pub trait FuncEnvironment {
_ => panic!("unsupported pointer type"),
}
}
}

/// Environment affecting the translation of a single WebAssembly function.
///
/// A `FuncEnvironment` trait object is required to translate a WebAssembly function to Cranelift
/// IR. The function environment provides information about the WebAssembly module as well as the
/// runtime environment.
pub trait FuncEnvironment: TargetEnvironment {
/// Should the code be structured to use a single `fallthrough_return` instruction at the end
/// of the function body, rather than `return` instructions as needed? This is used by VMs
/// to append custom epilogues.
fn return_mode(&self) -> ReturnMode {
ReturnMode::NormalReturns
}

/// Set up the necessary preamble definitions in `func` to access the global variable
/// identified by `index`.
Expand Down Expand Up @@ -384,10 +387,7 @@ pub trait FuncEnvironment {
/// An object satisfying the `ModuleEnvironment` trait can be passed as argument to the
/// [`translate_module`](fn.translate_module.html) function. These methods should not be called
/// by the user, they are only for `cranelift-wasm` internal use.
pub trait ModuleEnvironment<'data> {
/// Get the information needed to produce Cranelift IR for the current target.
fn target_config(&self) -> TargetFrontendConfig;

pub trait ModuleEnvironment<'data>: TargetEnvironment {
/// Provides the number of signatures up front. By default this does nothing, but
/// implementations can use this to preallocate memory if desired.
fn reserve_signatures(&mut self, _num: u32) -> WasmResult<()> {
Expand Down
1 change: 1 addition & 0 deletions cranelift-wasm/src/func_translator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ fn declare_locals<FE: FuncEnvironment + ?Sized>(
builder.ins().vconst(ir::types::I8X16, constant_handle)
}
AnyRef => builder.ins().null(environ.reference_type()),
AnyFunc => builder.ins().null(environ.reference_type()),
ty => return Err(wasm_unsupported!("unsupported local type {:?}", ty)),
};

Expand Down
4 changes: 2 additions & 2 deletions cranelift-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ mod state;
mod translation_utils;

pub use crate::environ::{
DummyEnvironment, FuncEnvironment, GlobalVariable, ModuleEnvironment, ReturnMode, WasmError,
WasmResult,
DummyEnvironment, FuncEnvironment, GlobalVariable, ModuleEnvironment, ReturnMode,
TargetEnvironment, WasmError, WasmResult,
};
pub use crate::func_translator::FuncTranslator;
pub use crate::module_translator::translate_module;
Expand Down
16 changes: 9 additions & 7 deletions cranelift-wasm/src/sections_translator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,15 @@ pub fn parse_type_section(
params,
returns,
} => {
let mut sig = Signature::new(environ.target_config().default_call_conv);
let mut sig =
Signature::new(ModuleEnvironment::target_config(environ).default_call_conv);
sig.params.extend(params.iter().map(|ty| {
let cret_arg: ir::Type = type_to_type(*ty)
let cret_arg: ir::Type = type_to_type(*ty, environ)
.expect("only numeric types are supported in function signatures");
AbiParam::new(cret_arg)
}));
sig.returns.extend(returns.iter().map(|ty| {
let cret_arg: ir::Type = type_to_type(*ty)
let cret_arg: ir::Type = type_to_type(*ty, environ)
.expect("only numeric types are supported in function signatures");
AbiParam::new(cret_arg)
}));
Expand Down Expand Up @@ -106,7 +107,7 @@ pub fn parse_import_section<'data>(
ImportSectionEntryType::Global(ref ty) => {
environ.declare_global_import(
Global {
ty: type_to_type(ty.content_type).unwrap(),
ty: type_to_type(ty.content_type, environ).unwrap(),
mutability: ty.mutable,
initializer: GlobalInit::Import,
},
Expand All @@ -117,7 +118,7 @@ pub fn parse_import_section<'data>(
ImportSectionEntryType::Table(ref tab) => {
environ.declare_table_import(
Table {
ty: match tabletype_to_type(tab.element_type)? {
ty: match tabletype_to_type(tab.element_type, environ)? {
Some(t) => TableElementType::Val(t),
None => TableElementType::Func,
},
Expand Down Expand Up @@ -160,7 +161,7 @@ pub fn parse_table_section(
for entry in tables {
let table = entry?;
environ.declare_table(Table {
ty: match tabletype_to_type(table.element_type)? {
ty: match tabletype_to_type(table.element_type, environ)? {
Some(t) => TableElementType::Val(t),
None => TableElementType::Func,
},
Expand Down Expand Up @@ -215,6 +216,7 @@ pub fn parse_global_section(
Operator::V128Const { value } => {
GlobalInit::V128Const(V128Imm::from(value.bytes().to_vec().as_slice()))
}
Operator::RefNull => GlobalInit::RefNullConst,
Operator::GetGlobal { global_index } => {
GlobalInit::GetGlobal(GlobalIndex::from_u32(global_index))
}
Expand All @@ -226,7 +228,7 @@ pub fn parse_global_section(
}
};
let global = Global {
ty: type_to_type(content_type).unwrap(),
ty: type_to_type(content_type, environ).unwrap(),
mutability: mutable,
initializer,
};
Expand Down
24 changes: 20 additions & 4 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::WasmResult;
use crate::environ::{TargetEnvironment, WasmResult};
use crate::state::ModuleTranslationState;
use crate::wasm_unsupported;
use core::u32;
Expand Down Expand Up @@ -83,6 +83,8 @@ pub enum GlobalInit {
V128Const(V128Imm),
/// A `get_global` of another global.
GetGlobal(GlobalIndex),
/// A `ref.null`.
RefNullConst,
///< The global is imported from, and thus initialized by, a different module.
Import,
}
Expand Down Expand Up @@ -119,26 +121,34 @@ pub struct Memory {
}

/// Helper function translating wasmparser types to Cranelift types when possible.
pub fn type_to_type(ty: wasmparser::Type) -> WasmResult<ir::Type> {
pub fn type_to_type<PE: TargetEnvironment + ?Sized>(
ty: wasmparser::Type,
environ: &PE,
) -> WasmResult<ir::Type> {
match ty {
wasmparser::Type::I32 => Ok(ir::types::I32),
wasmparser::Type::I64 => Ok(ir::types::I64),
wasmparser::Type::F32 => Ok(ir::types::F32),
wasmparser::Type::F64 => Ok(ir::types::F64),
wasmparser::Type::V128 => Ok(ir::types::I8X16),
wasmparser::Type::AnyRef | wasmparser::Type::AnyFunc => Ok(environ.reference_type()),
ty => Err(wasm_unsupported!("type_to_type: wasm type {:?}", ty)),
}
}

/// Helper function translating wasmparser possible table types to Cranelift types when possible,
/// or None for Func tables.
pub fn tabletype_to_type(ty: wasmparser::Type) -> WasmResult<Option<ir::Type>> {
pub fn tabletype_to_type<PE: TargetEnvironment + ?Sized>(
ty: wasmparser::Type,
environ: &PE,
) -> WasmResult<Option<ir::Type>> {
match ty {
wasmparser::Type::I32 => Ok(Some(ir::types::I32)),
wasmparser::Type::I64 => Ok(Some(ir::types::I64)),
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::AnyRef => Ok(Some(environ.reference_type())),
wasmparser::Type::AnyFunc => Ok(None),
ty => Err(wasm_unsupported!(
"tabletype_to_type: table wasm type {:?}",
Expand All @@ -159,6 +169,8 @@ pub fn blocktype_params_results(
wasmparser::Type::F32 => (&[], &[wasmparser::Type::F32]),
wasmparser::Type::F64 => (&[], &[wasmparser::Type::F64]),
wasmparser::Type::V128 => (&[], &[wasmparser::Type::V128]),
wasmparser::Type::AnyRef => (&[], &[wasmparser::Type::AnyRef]),
wasmparser::Type::AnyFunc => (&[], &[wasmparser::Type::AnyFunc]),
wasmparser::Type::EmptyBlockType => (&[], &[]),
ty => return Err(wasm_unsupported!("blocktype_params_results: type {:?}", ty)),
},
Expand All @@ -171,9 +183,10 @@ pub fn blocktype_params_results(
}

/// Create an `Ebb` with the given Wasm parameters.
pub fn ebb_with_params(
pub fn ebb_with_params<PE: TargetEnvironment + ?Sized>(
builder: &mut FunctionBuilder,
params: &[wasmparser::Type],
environ: &PE,
) -> WasmResult<ir::Ebb> {
let ebb = builder.create_ebb();
for ty in params.iter() {
Expand All @@ -190,6 +203,9 @@ pub fn ebb_with_params(
wasmparser::Type::F64 => {
builder.append_ebb_param(ebb, ir::types::F64);
}
wasmparser::Type::AnyRef | wasmparser::Type::AnyFunc => {
builder.append_ebb_param(ebb, environ.reference_type());
}
wasmparser::Type::V128 => {
builder.append_ebb_param(ebb, ir::types::I8X16);
}
Expand Down
8 changes: 8 additions & 0 deletions src/clif-util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ fn add_enable_multi_value<'a>() -> clap::Arg<'a, 'a> {
.help("Enable WASM's multi-value support")
}

fn add_enable_reference_types_flag<'a>() -> clap::Arg<'a, 'a> {
Arg::with_name("enable-reference-types")
.long("enable-reference-types")
.help("Enable WASM's reference types operations")
}

fn add_just_decode_flag<'a>() -> clap::Arg<'a, 'a> {
Arg::with_name("just-decode")
.short("t")
Expand Down Expand Up @@ -163,6 +169,7 @@ fn add_wasm_or_compile<'a>(cmd: &str) -> clap::App<'a, 'a> {
.arg(add_debug_flag())
.arg(add_enable_simd_flag())
.arg(add_enable_multi_value())
.arg(add_enable_reference_types_flag())
.arg(add_just_decode_flag())
.arg(add_check_translation_flag())
}
Expand Down Expand Up @@ -316,6 +323,7 @@ fn main() {
rest_cmd.is_present("value-ranges"),
rest_cmd.is_present("enable-simd"),
rest_cmd.is_present("enable-multi-value"),
rest_cmd.is_present("enable-reference-types"),
)
};

Expand Down
Loading