Add wasm reference/pointers translation. (#1073)
This commit is contained in:
@@ -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")
|
||||
@@ -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())
|
||||
}
|
||||
@@ -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"),
|
||||
)
|
||||
};
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ pub fn run(
|
||||
flag_calc_value_ranges: bool,
|
||||
flag_enable_simd: bool,
|
||||
flag_enable_multi_value: bool,
|
||||
flag_enable_reference_types: bool,
|
||||
) -> Result<(), String> {
|
||||
let parsed = parse_sets_and_triple(flag_set, flag_triple)?;
|
||||
|
||||
@@ -68,6 +69,7 @@ pub fn run(
|
||||
flag_calc_value_ranges,
|
||||
flag_enable_simd,
|
||||
flag_enable_multi_value,
|
||||
flag_enable_reference_types,
|
||||
&path.to_path_buf(),
|
||||
&name,
|
||||
parsed.as_fisa(),
|
||||
@@ -87,6 +89,7 @@ fn handle_module(
|
||||
flag_calc_value_ranges: bool,
|
||||
flag_enable_simd: bool,
|
||||
flag_enable_multi_value: bool,
|
||||
flag_enable_reference_types: bool,
|
||||
path: &PathBuf,
|
||||
name: &str,
|
||||
fisa: FlagsOrIsa,
|
||||
@@ -110,6 +113,9 @@ fn handle_module(
|
||||
if flag_enable_multi_value {
|
||||
features.enable_multi_value();
|
||||
}
|
||||
if flag_enable_reference_types {
|
||||
features.enable_reference_types();
|
||||
}
|
||||
|
||||
module_binary = match wat2wasm_with_features(&module_binary, features) {
|
||||
Ok(data) => data,
|
||||
|
||||
@@ -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(());
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -168,7 +168,7 @@ 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()));
|
||||
@@ -176,8 +176,8 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
} 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()));
|
||||
@@ -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());
|
||||
|
||||
@@ -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 {
|
||||
@@ -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);
|
||||
|
||||
@@ -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::{
|
||||
@@ -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
|
||||
}
|
||||
@@ -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(())
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
@@ -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`.
|
||||
@@ -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<()> {
|
||||
|
||||
@@ -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)),
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
}));
|
||||
@@ -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,
|
||||
},
|
||||
@@ -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,
|
||||
},
|
||||
@@ -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,
|
||||
},
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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 {:?}",
|
||||
@@ -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)),
|
||||
},
|
||||
@@ -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() {
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user