diff --git a/cranelift-wasm/src/code_translator.rs b/cranelift-wasm/src/code_translator.rs index 9bb4b0379..c0d1218cc 100644 --- a/cranelift-wasm/src/code_translator.rs +++ b/cranelift-wasm/src/code_translator.rs @@ -55,7 +55,7 @@ pub fn translate_operator( 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( ***********************************************************************************/ 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( // 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( } 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( 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( /// 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( 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); diff --git a/cranelift-wasm/src/environ/dummy.rs b/cranelift-wasm/src/environ/dummy.rs index 64ca5e7ca..6b6f21016 100644 --- a/cranelift-wasm/src/environ/dummy.rs +++ b/cranelift-wasm/src/environ/dummy.rs @@ -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(()) diff --git a/cranelift-wasm/src/environ/mod.rs b/cranelift-wasm/src/environ/mod.rs index 4b7405ea7..1cdb0b292 100644 --- a/cranelift-wasm/src/environ/mod.rs +++ b/cranelift-wasm/src/environ/mod.rs @@ -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, }; diff --git a/cranelift-wasm/src/environ/spec.rs b/cranelift-wasm/src/environ/spec.rs index 77fc6e2d1..5a38d7da6 100644 --- a/cranelift-wasm/src/environ/spec.rs +++ b/cranelift-wasm/src/environ/spec.rs @@ -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<()> { diff --git a/cranelift-wasm/src/func_translator.rs b/cranelift-wasm/src/func_translator.rs index 08d248157..8f5d49bcd 100644 --- a/cranelift-wasm/src/func_translator.rs +++ b/cranelift-wasm/src/func_translator.rs @@ -193,6 +193,7 @@ fn declare_locals( 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)), }; diff --git a/cranelift-wasm/src/lib.rs b/cranelift-wasm/src/lib.rs index a50bd7932..28e436112 100644 --- a/cranelift-wasm/src/lib.rs +++ b/cranelift-wasm/src/lib.rs @@ -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; diff --git a/cranelift-wasm/src/sections_translator.rs b/cranelift-wasm/src/sections_translator.rs index 0a7c678f4..ae980cfcf 100644 --- a/cranelift-wasm/src/sections_translator.rs +++ b/cranelift-wasm/src/sections_translator.rs @@ -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, }; diff --git a/cranelift-wasm/src/translation_utils.rs b/cranelift-wasm/src/translation_utils.rs index 1e5e963b4..9d38d9dbb 100644 --- a/cranelift-wasm/src/translation_utils.rs +++ b/cranelift-wasm/src/translation_utils.rs @@ -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 { +pub fn type_to_type( + ty: wasmparser::Type, + environ: &PE, +) -> WasmResult { 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> { +pub fn tabletype_to_type( + ty: wasmparser::Type, + environ: &PE, +) -> WasmResult> { 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( builder: &mut FunctionBuilder, params: &[wasmparser::Type], + environ: &PE, ) -> WasmResult { 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); } diff --git a/src/clif-util.rs b/src/clif-util.rs index 0c6110f02..b40f4d26e 100755 --- a/src/clif-util.rs +++ b/src/clif-util.rs @@ -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"), ) }; diff --git a/src/wasm.rs b/src/wasm.rs index a4ee4c866..041dd792b 100644 --- a/src/wasm.rs +++ b/src/wasm.rs @@ -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,