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

Commit

Permalink
Add wasm reference/pointers translation. (#1073)
Browse files Browse the repository at this point in the history
  • Loading branch information
yurydelendik authored Dec 6, 2019
1 parent 78769a1 commit c4762e5
Show file tree
Hide file tree
Showing 10 changed files with 85 additions and 44 deletions.
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

0 comments on commit c4762e5

Please sign in to comment.