Validate modules while translating (#2059)

* Validate modules while translating

This commit is a change to cranelift-wasm to validate each function body
as it is translated. Additionally top-level module translation functions
will perform module validation. This commit builds on changes in
wasmparser to perform module validation interwtwined with parsing and
translation. This will be necessary for future wasm features such as
module linking where the type behind a function index, for example, can
be far away in another module. Additionally this also brings a nice
benefit where parsing the binary only happens once (instead of having an
up-front serial validation step) and validation can happen in parallel
for each function.

Most of the changes in this commit are plumbing to make sure everything
lines up right. The major functional change here is that module
compilation should be faster by validating in parallel (or skipping
function validation entirely in the case of a cache hit). Otherwise from
a user-facing perspective nothing should be that different.

This commit does mean that cranelift's translation now inherently
validates the input wasm module. This means that the Spidermonkey
integration of cranelift-wasm will also be validating the function as
it's being translated with cranelift. The associated PR for wasmparser
(bytecodealliance/wasmparser#62) provides the necessary tools to create
a `FuncValidator` for Gecko, but this is something I'll want careful
review for before landing!

* Read function operators until EOF

This way we can let the validator take care of any issues with
mismatched `end` instructions and/or trailing operators/bytes.
This commit is contained in:
Alex Crichton
2020-10-05 11:02:01 -05:00
committed by GitHub
parent df8f85f4bc
commit 2c6841041d
34 changed files with 709 additions and 790 deletions

28
Cargo.lock generated
View File

@@ -585,11 +585,12 @@ dependencies = [
"cranelift-entity", "cranelift-entity",
"cranelift-frontend", "cranelift-frontend",
"hashbrown 0.7.2", "hashbrown 0.7.2",
"itertools 0.9.0",
"log", "log",
"serde", "serde",
"target-lexicon", "target-lexicon",
"thiserror", "thiserror",
"wasmparser 0.59.0", "wasmparser 0.62.0",
"wat", "wat",
] ]
@@ -1102,7 +1103,7 @@ dependencies = [
"staticvec", "staticvec",
"thiserror", "thiserror",
"typemap", "typemap",
"wasmparser 0.59.0", "wasmparser 0.62.0",
"wat", "wat",
] ]
@@ -2336,18 +2337,18 @@ checksum = "32fddd575d477c6e9702484139cf9f23dcd554b06d185ed0f56c857dd3a47aa6"
[[package]] [[package]]
name = "wasmparser" name = "wasmparser"
version = "0.59.0" version = "0.62.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a950e6a618f62147fd514ff445b2a0b53120d382751960797f85f058c7eda9b9" checksum = "e36b5b8441a5d83ea606c9eb904a3ee3889ebfeda1df1a5c48b84725239d93ce"
[[package]] [[package]]
name = "wasmprinter" name = "wasmprinter"
version = "0.2.6" version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "334551eb8b0b1be16cf366a54ce9b541ac32a96e9b51e67ebbae1696f108f112" checksum = "adc9e10f7145e1c15f16c809d6c0937ab51a79478f53458fb78ded3491819a94"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"wasmparser 0.59.0", "wasmparser 0.62.0",
] ]
[[package]] [[package]]
@@ -2367,7 +2368,7 @@ dependencies = [
"smallvec", "smallvec",
"target-lexicon", "target-lexicon",
"tempfile", "tempfile",
"wasmparser 0.59.0", "wasmparser 0.62.0",
"wasmtime-cache", "wasmtime-cache",
"wasmtime-environ", "wasmtime-environ",
"wasmtime-jit", "wasmtime-jit",
@@ -2445,6 +2446,7 @@ dependencies = [
"test-programs", "test-programs",
"tracing-subscriber", "tracing-subscriber",
"wasi-common", "wasi-common",
"wasmparser 0.62.0",
"wasmtime", "wasmtime",
"wasmtime-cache", "wasmtime-cache",
"wasmtime-debug", "wasmtime-debug",
@@ -2479,7 +2481,7 @@ dependencies = [
"object 0.21.1", "object 0.21.1",
"target-lexicon", "target-lexicon",
"thiserror", "thiserror",
"wasmparser 0.59.0", "wasmparser 0.62.0",
"wasmtime-environ", "wasmtime-environ",
] ]
@@ -2498,7 +2500,7 @@ dependencies = [
"more-asserts", "more-asserts",
"serde", "serde",
"thiserror", "thiserror",
"wasmparser 0.59.0", "wasmparser 0.62.0",
] ]
[[package]] [[package]]
@@ -2526,7 +2528,7 @@ dependencies = [
"env_logger", "env_logger",
"log", "log",
"rayon", "rayon",
"wasmparser 0.59.0", "wasmparser 0.62.0",
"wasmprinter", "wasmprinter",
"wasmtime", "wasmtime",
"wasmtime-wast", "wasmtime-wast",
@@ -2553,7 +2555,7 @@ dependencies = [
"serde", "serde",
"target-lexicon", "target-lexicon",
"thiserror", "thiserror",
"wasmparser 0.59.0", "wasmparser 0.62.0",
"wasmtime-cranelift", "wasmtime-cranelift",
"wasmtime-debug", "wasmtime-debug",
"wasmtime-environ", "wasmtime-environ",
@@ -2570,7 +2572,7 @@ version = "0.20.0"
dependencies = [ dependencies = [
"cranelift-codegen", "cranelift-codegen",
"lightbeam", "lightbeam",
"wasmparser 0.59.0", "wasmparser 0.62.0",
"wasmtime-environ", "wasmtime-environ",
] ]

View File

@@ -42,6 +42,7 @@ libc = "0.2.60"
log = "0.4.8" log = "0.4.8"
rayon = "1.2.1" rayon = "1.2.1"
humantime = "1.3.0" humantime = "1.3.0"
wasmparser = "0.62"
[dev-dependencies] [dev-dependencies]
env_logger = "0.7.1" env_logger = "0.7.1"

View File

@@ -12,11 +12,12 @@ keywords = ["webassembly", "wasm"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
wasmparser = { version = "0.59.0", default-features = false } wasmparser = { version = "0.62.0", default-features = false }
cranelift-codegen = { path = "../codegen", version = "0.67.0", default-features = false } cranelift-codegen = { path = "../codegen", version = "0.67.0", default-features = false }
cranelift-entity = { path = "../entity", version = "0.67.0" } cranelift-entity = { path = "../entity", version = "0.67.0" }
cranelift-frontend = { path = "../frontend", version = "0.67.0", default-features = false } cranelift-frontend = { path = "../frontend", version = "0.67.0", default-features = false }
hashbrown = { version = "0.7", optional = true } hashbrown = { version = "0.7", optional = true }
itertools = "0.9.0"
log = { version = "0.4.6", default-features = false } log = { version = "0.4.6", default-features = false }
serde = { version = "1.0.94", features = ["derive"], optional = true } serde = { version = "1.0.94", features = ["derive"], optional = true }
thiserror = "1.0.4" thiserror = "1.0.4"

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,6 @@ use crate::environ::{
WasmFuncType, WasmResult, WasmFuncType, WasmResult,
}; };
use crate::func_translator::FuncTranslator; use crate::func_translator::FuncTranslator;
use crate::state::ModuleTranslationState;
use crate::translation_utils::{ use crate::translation_utils::{
DataIndex, DefinedFuncIndex, ElemIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, DataIndex, DefinedFuncIndex, ElemIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex,
SignatureIndex, Table, TableIndex, SignatureIndex, Table, TableIndex,
@@ -26,6 +25,7 @@ use cranelift_frontend::FunctionBuilder;
use std::boxed::Box; use std::boxed::Box;
use std::string::String; use std::string::String;
use std::vec::Vec; use std::vec::Vec;
use wasmparser::{FuncValidator, FunctionBody, ValidatorResources, WasmFeatures};
/// Compute a `ir::ExternalName` for a given wasm function index. /// Compute a `ir::ExternalName` for a given wasm function index.
fn get_func_name(func_index: FuncIndex) -> ir::ExternalName { fn get_func_name(func_index: FuncIndex) -> ir::ExternalName {
@@ -738,10 +738,11 @@ impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
fn define_function_body( fn define_function_body(
&mut self, &mut self,
module_translation_state: &ModuleTranslationState, mut validator: FuncValidator<ValidatorResources>,
body_bytes: &'data [u8], body: FunctionBody<'data>,
body_offset: usize,
) -> WasmResult<()> { ) -> WasmResult<()> {
self.func_bytecode_sizes
.push(body.get_binary_reader().bytes_remaining());
let func = { let func = {
let mut func_environ = DummyFuncEnvironment::new(&self.info, self.return_mode); let mut func_environ = DummyFuncEnvironment::new(&self.info, self.return_mode);
let func_index = let func_index =
@@ -752,16 +753,10 @@ impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
if self.debug_info { if self.debug_info {
func.collect_debug_info(); func.collect_debug_info();
} }
self.trans.translate( self.trans
module_translation_state, .translate_body(&mut validator, body, &mut func, &mut func_environ)?;
body_bytes,
body_offset,
&mut func,
&mut func_environ,
)?;
func func
}; };
self.func_bytecode_sizes.push(body_bytes.len());
self.info.function_bodies.push(func); self.info.function_bodies.push(func);
Ok(()) Ok(())
} }
@@ -773,4 +768,14 @@ impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
fn declare_func_name(&mut self, func_index: FuncIndex, name: &'data str) { fn declare_func_name(&mut self, func_index: FuncIndex, name: &'data str) {
self.function_names[func_index] = String::from(name); self.function_names[func_index] = String::from(name);
} }
fn wasm_features(&self) -> WasmFeatures {
WasmFeatures {
multi_value: true,
simd: true,
reference_types: true,
bulk_memory: true,
..WasmFeatures::default()
}
}
} }

View File

@@ -6,7 +6,7 @@
//! //!
//! [Wasmtime]: https://github.com/bytecodealliance/wasmtime //! [Wasmtime]: https://github.com/bytecodealliance/wasmtime
use crate::state::{FuncTranslationState, ModuleTranslationState}; use crate::state::FuncTranslationState;
use crate::translation_utils::{ use crate::translation_utils::{
DataIndex, ElemIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex, DataIndex, ElemIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex,
Table, TableIndex, Table, TableIndex,
@@ -23,8 +23,8 @@ use serde::{Deserialize, Serialize};
use std::boxed::Box; use std::boxed::Box;
use std::string::ToString; use std::string::ToString;
use thiserror::Error; use thiserror::Error;
use wasmparser::BinaryReaderError; use wasmparser::ValidatorResources;
use wasmparser::Operator; use wasmparser::{BinaryReaderError, FuncValidator, FunctionBody, Operator, WasmFeatures};
/// WebAssembly value type -- equivalent of `wasmparser`'s Type. /// WebAssembly value type -- equivalent of `wasmparser`'s Type.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
@@ -798,9 +798,8 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment {
/// Provides the contents of a function body. /// Provides the contents of a function body.
fn define_function_body( fn define_function_body(
&mut self, &mut self,
module_translation_state: &ModuleTranslationState, validator: FuncValidator<ValidatorResources>,
body_bytes: &'data [u8], body: FunctionBody<'data>,
body_offset: usize,
) -> WasmResult<()>; ) -> WasmResult<()>;
/// Provides the number of data initializers up front. By default this does nothing, but /// Provides the number of data initializers up front. By default this does nothing, but
@@ -841,4 +840,9 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment {
fn custom_section(&mut self, _name: &'data str, _data: &'data [u8]) -> WasmResult<()> { fn custom_section(&mut self, _name: &'data str, _data: &'data [u8]) -> WasmResult<()> {
Ok(()) Ok(())
} }
/// Returns the list of enabled wasm features this translation will be using.
fn wasm_features(&self) -> WasmFeatures {
WasmFeatures::default()
}
} }

View File

@@ -6,7 +6,7 @@
use crate::code_translator::{bitcast_arguments, translate_operator, wasm_param_types}; use crate::code_translator::{bitcast_arguments, translate_operator, wasm_param_types};
use crate::environ::{FuncEnvironment, ReturnMode, WasmResult}; use crate::environ::{FuncEnvironment, ReturnMode, WasmResult};
use crate::state::{FuncTranslationState, ModuleTranslationState}; use crate::state::FuncTranslationState;
use crate::translation_utils::get_vmctx_value_label; use crate::translation_utils::get_vmctx_value_label;
use crate::wasm_unsupported; use crate::wasm_unsupported;
use core::convert::TryInto; use core::convert::TryInto;
@@ -14,7 +14,7 @@ use cranelift_codegen::entity::EntityRef;
use cranelift_codegen::ir::{self, Block, InstBuilder, ValueLabel}; use cranelift_codegen::ir::{self, Block, InstBuilder, ValueLabel};
use cranelift_codegen::timing; use cranelift_codegen::timing;
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable}; use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
use wasmparser::{self, BinaryReader}; use wasmparser::{self, BinaryReader, FuncValidator, FunctionBody, WasmModuleResources};
/// WebAssembly to Cranelift IR function translator. /// WebAssembly to Cranelift IR function translator.
/// ///
@@ -55,29 +55,30 @@ impl FuncTranslator {
/// ///
pub fn translate<FE: FuncEnvironment + ?Sized>( pub fn translate<FE: FuncEnvironment + ?Sized>(
&mut self, &mut self,
module_translation_state: &ModuleTranslationState, validator: &mut FuncValidator<impl WasmModuleResources>,
code: &[u8], code: &[u8],
code_offset: usize, code_offset: usize,
func: &mut ir::Function, func: &mut ir::Function,
environ: &mut FE, environ: &mut FE,
) -> WasmResult<()> { ) -> WasmResult<()> {
self.translate_from_reader( self.translate_body(
module_translation_state, validator,
BinaryReader::new_with_offset(code, code_offset), FunctionBody::new(code_offset, code),
func, func,
environ, environ,
) )
} }
/// Translate a binary WebAssembly function from a `BinaryReader`. /// Translate a binary WebAssembly function from a `FunctionBody`.
pub fn translate_from_reader<FE: FuncEnvironment + ?Sized>( pub fn translate_body<FE: FuncEnvironment + ?Sized>(
&mut self, &mut self,
module_translation_state: &ModuleTranslationState, validator: &mut FuncValidator<impl WasmModuleResources>,
mut reader: BinaryReader, body: FunctionBody<'_>,
func: &mut ir::Function, func: &mut ir::Function,
environ: &mut FE, environ: &mut FE,
) -> WasmResult<()> { ) -> WasmResult<()> {
let _tt = timing::wasm_translate_function(); let _tt = timing::wasm_translate_function();
let mut reader = body.get_binary_reader();
log::debug!( log::debug!(
"translate({} bytes, {}{})", "translate({} bytes, {}{})",
reader.bytes_remaining(), reader.bytes_remaining(),
@@ -107,14 +108,8 @@ impl FuncTranslator {
builder.append_block_params_for_function_returns(exit_block); builder.append_block_params_for_function_returns(exit_block);
self.state.initialize(&builder.func.signature, exit_block); self.state.initialize(&builder.func.signature, exit_block);
parse_local_decls(&mut reader, &mut builder, num_params, environ)?; parse_local_decls(&mut reader, &mut builder, num_params, environ, validator)?;
parse_function_body( parse_function_body(validator, reader, &mut builder, &mut self.state, environ)?;
module_translation_state,
reader,
&mut builder,
&mut self.state,
environ,
)?;
builder.finalize(); builder.finalize();
Ok(()) Ok(())
@@ -161,14 +156,17 @@ fn parse_local_decls<FE: FuncEnvironment + ?Sized>(
builder: &mut FunctionBuilder, builder: &mut FunctionBuilder,
num_params: usize, num_params: usize,
environ: &mut FE, environ: &mut FE,
validator: &mut FuncValidator<impl WasmModuleResources>,
) -> WasmResult<()> { ) -> WasmResult<()> {
let mut next_local = num_params; let mut next_local = num_params;
let local_count = reader.read_local_count()?; let local_count = reader.read_var_u32()?;
let mut locals_total = 0;
for _ in 0..local_count { for _ in 0..local_count {
builder.set_srcloc(cur_srcloc(reader)); builder.set_srcloc(cur_srcloc(reader));
let (count, ty) = reader.read_local_decl(&mut locals_total)?; let pos = reader.original_position();
let count = reader.read_var_u32()?;
let ty = reader.read_type()?;
validator.define_locals(pos, count, ty)?;
declare_locals(builder, count, ty, &mut next_local, environ)?; declare_locals(builder, count, ty, &mut next_local, environ)?;
} }
@@ -218,7 +216,7 @@ fn declare_locals<FE: FuncEnvironment + ?Sized>(
/// This assumes that the local variable declarations have already been parsed and function /// This assumes that the local variable declarations have already been parsed and function
/// arguments and locals are declared in the builder. /// arguments and locals are declared in the builder.
fn parse_function_body<FE: FuncEnvironment + ?Sized>( fn parse_function_body<FE: FuncEnvironment + ?Sized>(
module_translation_state: &ModuleTranslationState, validator: &mut FuncValidator<impl WasmModuleResources>,
mut reader: BinaryReader, mut reader: BinaryReader,
builder: &mut FunctionBuilder, builder: &mut FunctionBuilder,
state: &mut FuncTranslationState, state: &mut FuncTranslationState,
@@ -227,14 +225,17 @@ fn parse_function_body<FE: FuncEnvironment + ?Sized>(
// The control stack is initialized with a single block representing the whole function. // The control stack is initialized with a single block representing the whole function.
debug_assert_eq!(state.control_stack.len(), 1, "State not initialized"); debug_assert_eq!(state.control_stack.len(), 1, "State not initialized");
// Keep going until the final `End` operator which pops the outermost block. while !reader.eof() {
while !state.control_stack.is_empty() { let pos = reader.original_position();
builder.set_srcloc(cur_srcloc(&reader)); builder.set_srcloc(cur_srcloc(&reader));
let op = reader.read_operator()?; let op = reader.read_operator()?;
validator.op(pos, &op)?;
environ.before_translate_operator(&op, builder, state)?; environ.before_translate_operator(&op, builder, state)?;
translate_operator(module_translation_state, &op, builder, state, environ)?; translate_operator(validator, &op, builder, state, environ)?;
environ.after_translate_operator(&op, builder, state)?; environ.after_translate_operator(&op, builder, state)?;
} }
let pos = reader.original_position();
validator.finish(pos)?;
// The final `End` operator left us in the exit block where we need to manually add a return // The final `End` operator left us in the exit block where we need to manually add a return
// instruction. // instruction.
@@ -261,8 +262,6 @@ fn parse_function_body<FE: FuncEnvironment + ?Sized>(
// or the end of the function is unreachable. // or the end of the function is unreachable.
state.stack.clear(); state.stack.clear();
debug_assert!(reader.eof());
Ok(()) Ok(())
} }
@@ -277,26 +276,27 @@ fn cur_srcloc(reader: &BinaryReader) -> ir::SourceLoc {
mod tests { mod tests {
use super::{FuncTranslator, ReturnMode}; use super::{FuncTranslator, ReturnMode};
use crate::environ::DummyEnvironment; use crate::environ::DummyEnvironment;
use crate::ModuleTranslationState;
use cranelift_codegen::ir::types::I32; use cranelift_codegen::ir::types::I32;
use cranelift_codegen::{ir, isa, settings, Context}; use cranelift_codegen::{ir, isa, settings, Context};
use log::debug; use log::debug;
use target_lexicon::PointerWidth; use target_lexicon::PointerWidth;
use wasmparser::{
FuncValidator, FunctionBody, Parser, ValidPayload, Validator, ValidatorResources,
};
#[test] #[test]
fn small1() { fn small1() {
// Implicit return. // Implicit return.
// let wasm = wat::parse_str(
// (func $small1 (param i32) (result i32) "
// (i32.add (get_local 0) (i32.const 1)) (module
// ) (func $small2 (param i32) (result i32)
const BODY: [u8; 7] = [ (i32.add (get_local 0) (i32.const 1))
0x00, // local decl count )
0x20, 0x00, // get_local 0 )
0x41, 0x01, // i32.const 1 ",
0x6a, // i32.add )
0x0b, // end .unwrap();
];
let mut trans = FuncTranslator::new(); let mut trans = FuncTranslator::new();
let flags = settings::Flags::new(settings::builder()); let flags = settings::Flags::new(settings::builder());
@@ -309,21 +309,15 @@ mod tests {
false, false,
); );
let module_translation_state = ModuleTranslationState::new();
let mut ctx = Context::new(); let mut ctx = Context::new();
ctx.func.name = ir::ExternalName::testcase("small1"); ctx.func.name = ir::ExternalName::testcase("small1");
ctx.func.signature.params.push(ir::AbiParam::new(I32)); ctx.func.signature.params.push(ir::AbiParam::new(I32));
ctx.func.signature.returns.push(ir::AbiParam::new(I32)); ctx.func.signature.returns.push(ir::AbiParam::new(I32));
let (body, mut validator) = extract_func(&wasm);
trans trans
.translate( .translate_body(&mut validator, body, &mut ctx.func, &mut runtime.func_env())
&module_translation_state,
&BODY,
0,
&mut ctx.func,
&mut runtime.func_env(),
)
.unwrap(); .unwrap();
debug!("{}", ctx.func.display(None)); debug!("{}", ctx.func.display(None));
ctx.verify(&flags).unwrap(); ctx.verify(&flags).unwrap();
@@ -332,18 +326,16 @@ mod tests {
#[test] #[test]
fn small2() { fn small2() {
// Same as above, but with an explicit return instruction. // Same as above, but with an explicit return instruction.
// let wasm = wat::parse_str(
// (func $small2 (param i32) (result i32) "
// (return (i32.add (get_local 0) (i32.const 1))) (module
// ) (func $small2 (param i32) (result i32)
const BODY: [u8; 8] = [ (return (i32.add (get_local 0) (i32.const 1)))
0x00, // local decl count )
0x20, 0x00, // get_local 0 )
0x41, 0x01, // i32.const 1 ",
0x6a, // i32.add )
0x0f, // return .unwrap();
0x0b, // end
];
let mut trans = FuncTranslator::new(); let mut trans = FuncTranslator::new();
let flags = settings::Flags::new(settings::builder()); let flags = settings::Flags::new(settings::builder());
@@ -356,21 +348,15 @@ mod tests {
false, false,
); );
let module_translation_state = ModuleTranslationState::new();
let mut ctx = Context::new(); let mut ctx = Context::new();
ctx.func.name = ir::ExternalName::testcase("small2"); ctx.func.name = ir::ExternalName::testcase("small2");
ctx.func.signature.params.push(ir::AbiParam::new(I32)); ctx.func.signature.params.push(ir::AbiParam::new(I32));
ctx.func.signature.returns.push(ir::AbiParam::new(I32)); ctx.func.signature.returns.push(ir::AbiParam::new(I32));
let (body, mut validator) = extract_func(&wasm);
trans trans
.translate( .translate_body(&mut validator, body, &mut ctx.func, &mut runtime.func_env())
&module_translation_state,
&BODY,
0,
&mut ctx.func,
&mut runtime.func_env(),
)
.unwrap(); .unwrap();
debug!("{}", ctx.func.display(None)); debug!("{}", ctx.func.display(None));
ctx.verify(&flags).unwrap(); ctx.verify(&flags).unwrap();
@@ -379,27 +365,21 @@ mod tests {
#[test] #[test]
fn infloop() { fn infloop() {
// An infinite loop, no return instructions. // An infinite loop, no return instructions.
// let wasm = wat::parse_str(
// (func $infloop (result i32) "
// (local i32) (module
// (loop (result i32) (func $infloop (result i32)
// (i32.add (get_local 0) (i32.const 1)) (local i32)
// (set_local 0) (loop (result i32)
// (br 0) (i32.add (get_local 0) (i32.const 1))
// ) (set_local 0)
// ) (br 0)
const BODY: [u8; 16] = [ )
0x01, // 1 local decl. )
0x01, 0x7f, // 1 i32 local. )
0x03, 0x7f, // loop i32 ",
0x20, 0x00, // get_local 0 )
0x41, 0x01, // i32.const 0 .unwrap();
0x6a, // i32.add
0x21, 0x00, // set_local 0
0x0c, 0x00, // br 0
0x0b, // end
0x0b, // end
];
let mut trans = FuncTranslator::new(); let mut trans = FuncTranslator::new();
let flags = settings::Flags::new(settings::builder()); let flags = settings::Flags::new(settings::builder());
@@ -412,22 +392,27 @@ mod tests {
false, false,
); );
let module_translation_state = ModuleTranslationState::new();
let mut ctx = Context::new(); let mut ctx = Context::new();
ctx.func.name = ir::ExternalName::testcase("infloop"); ctx.func.name = ir::ExternalName::testcase("infloop");
ctx.func.signature.returns.push(ir::AbiParam::new(I32)); ctx.func.signature.returns.push(ir::AbiParam::new(I32));
let (body, mut validator) = extract_func(&wasm);
trans trans
.translate( .translate_body(&mut validator, body, &mut ctx.func, &mut runtime.func_env())
&module_translation_state,
&BODY,
0,
&mut ctx.func,
&mut runtime.func_env(),
)
.unwrap(); .unwrap();
debug!("{}", ctx.func.display(None)); debug!("{}", ctx.func.display(None));
ctx.verify(&flags).unwrap(); ctx.verify(&flags).unwrap();
} }
fn extract_func(wat: &[u8]) -> (FunctionBody<'_>, FuncValidator<ValidatorResources>) {
let mut validator = Validator::new();
for payload in Parser::new(0).parse_all(wat) {
match validator.payload(&payload.unwrap()).unwrap() {
ValidPayload::Func(validator, body) => return (body, validator),
_ => {}
}
}
panic!("failed to find function");
}
} }

View File

@@ -72,5 +72,10 @@ pub use crate::translation_utils::{
}; };
pub use cranelift_frontend::FunctionBuilder; pub use cranelift_frontend::FunctionBuilder;
// Convenience reexport of the wasmparser crate that we're linking against,
// since a number of types in `wasmparser` show up in the public API of
// `cranelift-wasm`.
pub use wasmparser;
/// Version number of this crate. /// Version number of this crate.
pub const VERSION: &str = env!("CARGO_PKG_VERSION"); pub const VERSION: &str = env!("CARGO_PKG_VERSION");

View File

@@ -8,7 +8,7 @@ use crate::sections_translator::{
}; };
use crate::state::ModuleTranslationState; use crate::state::ModuleTranslationState;
use cranelift_codegen::timing; use cranelift_codegen::timing;
use wasmparser::{NameSectionReader, Parser, Payload}; use wasmparser::{NameSectionReader, Parser, Payload, Validator};
/// Translate a sequence of bytes forming a valid Wasm binary into a list of valid Cranelift IR /// Translate a sequence of bytes forming a valid Wasm binary into a list of valid Cranelift IR
/// [`Function`](cranelift_codegen::ir::Function). /// [`Function`](cranelift_codegen::ir::Function).
@@ -18,75 +18,105 @@ pub fn translate_module<'data>(
) -> WasmResult<ModuleTranslationState> { ) -> WasmResult<ModuleTranslationState> {
let _tt = timing::wasm_translate_module(); let _tt = timing::wasm_translate_module();
let mut module_translation_state = ModuleTranslationState::new(); let mut module_translation_state = ModuleTranslationState::new();
let mut validator = Validator::new();
validator.wasm_features(environ.wasm_features());
for payload in Parser::new(0).parse_all(data) { for payload in Parser::new(0).parse_all(data) {
match payload? { match payload? {
Payload::Version { .. } | Payload::End => {} Payload::Version { num, range } => {
validator.version(num, &range)?;
}
Payload::End => {
validator.end()?;
}
Payload::TypeSection(types) => { Payload::TypeSection(types) => {
validator.type_section(&types)?;
parse_type_section(types, &mut module_translation_state, environ)?; parse_type_section(types, &mut module_translation_state, environ)?;
} }
Payload::ImportSection(imports) => { Payload::ImportSection(imports) => {
validator.import_section(&imports)?;
parse_import_section(imports, environ)?; parse_import_section(imports, environ)?;
} }
Payload::FunctionSection(functions) => { Payload::FunctionSection(functions) => {
validator.function_section(&functions)?;
parse_function_section(functions, environ)?; parse_function_section(functions, environ)?;
} }
Payload::TableSection(tables) => { Payload::TableSection(tables) => {
validator.table_section(&tables)?;
parse_table_section(tables, environ)?; parse_table_section(tables, environ)?;
} }
Payload::MemorySection(memories) => { Payload::MemorySection(memories) => {
validator.memory_section(&memories)?;
parse_memory_section(memories, environ)?; parse_memory_section(memories, environ)?;
} }
Payload::GlobalSection(globals) => { Payload::GlobalSection(globals) => {
validator.global_section(&globals)?;
parse_global_section(globals, environ)?; parse_global_section(globals, environ)?;
} }
Payload::ExportSection(exports) => { Payload::ExportSection(exports) => {
validator.export_section(&exports)?;
parse_export_section(exports, environ)?; parse_export_section(exports, environ)?;
} }
Payload::StartSection { func, .. } => { Payload::StartSection { func, range } => {
validator.start_section(func, &range)?;
parse_start_section(func, environ)?; parse_start_section(func, environ)?;
} }
Payload::ElementSection(elements) => { Payload::ElementSection(elements) => {
validator.element_section(&elements)?;
parse_element_section(elements, environ)?; parse_element_section(elements, environ)?;
} }
Payload::CodeSectionStart { count, range, .. } => { Payload::CodeSectionStart { count, range, .. } => {
validator.code_section_start(count, &range)?;
environ.reserve_function_bodies(count, range.start as u64); environ.reserve_function_bodies(count, range.start as u64);
} }
Payload::CodeSectionEntry(code) => { Payload::CodeSectionEntry(body) => {
let mut code = code.get_binary_reader(); let func_validator = validator.code_section_entry()?;
let size = code.bytes_remaining(); environ.define_function_body(func_validator, body)?;
let offset = code.original_position();
environ.define_function_body(
&module_translation_state,
code.read_bytes(size)?,
offset,
)?;
} }
Payload::DataSection(data) => { Payload::DataSection(data) => {
validator.data_section(&data)?;
parse_data_section(data, environ)?; parse_data_section(data, environ)?;
} }
Payload::DataCountSection { count, .. } => { Payload::DataCountSection { count, range } => {
validator.data_count_section(count, &range)?;
environ.reserve_passive_data(count)?; environ.reserve_passive_data(count)?;
} }
Payload::ModuleSection(_) Payload::ModuleSection(s) => {
| Payload::InstanceSection(_) validator.module_section(&s)?;
| Payload::AliasSection(_) unimplemented!("module linking not implemented yet")
| Payload::ModuleCodeSectionStart { .. } }
| Payload::ModuleCodeSectionEntry { .. } => { Payload::InstanceSection(s) => {
validator.instance_section(&s)?;
unimplemented!("module linking not implemented yet")
}
Payload::AliasSection(s) => {
validator.alias_section(&s)?;
unimplemented!("module linking not implemented yet")
}
Payload::ModuleCodeSectionStart {
count,
range,
size: _,
} => {
validator.module_code_section_start(count, &range)?;
unimplemented!("module linking not implemented yet")
}
Payload::ModuleCodeSectionEntry { .. } => {
unimplemented!("module linking not implemented yet") unimplemented!("module linking not implemented yet")
} }
@@ -105,7 +135,10 @@ pub fn translate_module<'data>(
Payload::CustomSection { name, data, .. } => environ.custom_section(name, data)?, Payload::CustomSection { name, data, .. } => environ.custom_section(name, data)?,
Payload::UnknownSection { .. } => unreachable!(), Payload::UnknownSection { id, range, .. } => {
validator.unknown_section(id, &range)?;
unreachable!();
}
} }
} }

View File

@@ -26,7 +26,7 @@ use wasmparser::{
self, Data, DataKind, DataSectionReader, Element, ElementItem, ElementItems, ElementKind, self, Data, DataKind, DataSectionReader, Element, ElementItem, ElementItems, ElementKind,
ElementSectionReader, Export, ExportSectionReader, ExternalKind, FunctionSectionReader, ElementSectionReader, Export, ExportSectionReader, ExternalKind, FunctionSectionReader,
GlobalSectionReader, GlobalType, ImportSectionEntryType, ImportSectionReader, GlobalSectionReader, GlobalType, ImportSectionEntryType, ImportSectionReader,
MemorySectionReader, MemoryType, NameSectionReader, Naming, Operator, TableSectionReader, Type, MemorySectionReader, MemoryType, NameSectionReader, Naming, Operator, TableSectionReader,
TypeDef, TypeSectionReader, TypeDef, TypeSectionReader,
}; };
@@ -88,7 +88,7 @@ pub fn parse_import_section<'data>(
ImportSectionEntryType::Module(_sig) | ImportSectionEntryType::Instance(_sig) => { ImportSectionEntryType::Module(_sig) | ImportSectionEntryType::Instance(_sig) => {
unimplemented!("module linking not implemented yet") unimplemented!("module linking not implemented yet")
} }
ImportSectionEntryType::Memory(MemoryType { ImportSectionEntryType::Memory(MemoryType::M32 {
limits: ref memlimits, limits: ref memlimits,
shared, shared,
}) => { }) => {
@@ -102,6 +102,9 @@ pub fn parse_import_section<'data>(
field_name, field_name,
)?; )?;
} }
ImportSectionEntryType::Memory(MemoryType::M64 { .. }) => {
unimplemented!();
}
ImportSectionEntryType::Global(ref ty) => { ImportSectionEntryType::Global(ref ty) => {
environ.declare_global_import( environ.declare_global_import(
Global { Global {
@@ -189,11 +192,16 @@ pub fn parse_memory_section(
for entry in memories { for entry in memories {
let memory = entry?; let memory = entry?;
environ.declare_memory(Memory { match memory {
minimum: memory.limits.initial, MemoryType::M32 { limits, shared } => {
maximum: memory.limits.maximum, environ.declare_memory(Memory {
shared: memory.shared, minimum: limits.initial,
})?; maximum: limits.maximum,
shared: shared,
})?;
}
MemoryType::M64 { .. } => unimplemented!(),
}
} }
Ok(()) Ok(())
@@ -313,13 +321,7 @@ pub fn parse_element_section<'data>(
environ.reserve_table_elements(elements.get_count())?; environ.reserve_table_elements(elements.get_count())?;
for (index, entry) in elements.into_iter().enumerate() { for (index, entry) in elements.into_iter().enumerate() {
let Element { kind, items, ty } = entry?; let Element { kind, items, ty: _ } = entry?;
if ty != Type::FuncRef {
return Err(wasm_unsupported!(
"unsupported table element type: {:?}",
ty
));
}
let segments = read_elems(&items)?; let segments = read_elems(&items)?;
match kind { match kind {
ElementKind::Active { ElementKind::Active {

View File

@@ -1,6 +1,5 @@
//! Helper functions and structures for the translation. //! Helper functions and structures for the translation.
use crate::environ::{TargetEnvironment, WasmResult, WasmType}; use crate::environ::{TargetEnvironment, WasmResult, WasmType};
use crate::state::ModuleTranslationState;
use crate::wasm_unsupported; use crate::wasm_unsupported;
use core::convert::TryInto; use core::convert::TryInto;
use core::u32; use core::u32;
@@ -10,7 +9,7 @@ use cranelift_codegen::ir::immediates::V128Imm;
use cranelift_frontend::FunctionBuilder; use cranelift_frontend::FunctionBuilder;
#[cfg(feature = "enable-serde")] #[cfg(feature = "enable-serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use wasmparser; use wasmparser::{FuncValidator, WasmFuncType, WasmModuleResources};
/// Index type of a function (imported or defined) inside the WebAssembly module. /// Index type of a function (imported or defined) inside the WebAssembly module.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
@@ -194,38 +193,56 @@ pub fn tabletype_to_type<PE: TargetEnvironment + ?Sized>(
} }
/// Get the parameter and result types for the given Wasm blocktype. /// Get the parameter and result types for the given Wasm blocktype.
pub fn blocktype_params_results( pub fn blocktype_params_results<'a, T>(
module_translation_state: &ModuleTranslationState, validator: &'a FuncValidator<T>,
ty_or_ft: wasmparser::TypeOrFuncType, ty_or_ft: wasmparser::TypeOrFuncType,
) -> WasmResult<(&[wasmparser::Type], &[wasmparser::Type])> { ) -> WasmResult<(
Ok(match ty_or_ft { impl ExactSizeIterator<Item = wasmparser::Type> + Clone + 'a,
wasmparser::TypeOrFuncType::Type(ty) => match ty { impl ExactSizeIterator<Item = wasmparser::Type> + Clone + 'a,
wasmparser::Type::I32 => (&[], &[wasmparser::Type::I32]), )>
wasmparser::Type::I64 => (&[], &[wasmparser::Type::I64]), where
wasmparser::Type::F32 => (&[], &[wasmparser::Type::F32]), T: WasmModuleResources,
wasmparser::Type::F64 => (&[], &[wasmparser::Type::F64]), {
wasmparser::Type::V128 => (&[], &[wasmparser::Type::V128]), return Ok(match ty_or_ft {
wasmparser::Type::ExternRef => (&[], &[wasmparser::Type::ExternRef]), wasmparser::TypeOrFuncType::Type(ty) => {
wasmparser::Type::FuncRef => (&[], &[wasmparser::Type::FuncRef]), let (params, results): (&'static [wasmparser::Type], &'static [wasmparser::Type]) =
wasmparser::Type::EmptyBlockType => (&[], &[]), match ty {
ty => return Err(wasm_unsupported!("blocktype_params_results: type {:?}", ty)), wasmparser::Type::I32 => (&[], &[wasmparser::Type::I32]),
}, wasmparser::Type::I64 => (&[], &[wasmparser::Type::I64]),
wasmparser::TypeOrFuncType::FuncType(ty_index) => { wasmparser::Type::F32 => (&[], &[wasmparser::Type::F32]),
let sig_idx = SignatureIndex::from_u32(ty_index); wasmparser::Type::F64 => (&[], &[wasmparser::Type::F64]),
let (ref params, ref returns) = module_translation_state.wasm_types[sig_idx]; wasmparser::Type::V128 => (&[], &[wasmparser::Type::V128]),
(&*params, &*returns) wasmparser::Type::ExternRef => (&[], &[wasmparser::Type::ExternRef]),
wasmparser::Type::FuncRef => (&[], &[wasmparser::Type::FuncRef]),
wasmparser::Type::EmptyBlockType => (&[], &[]),
ty => return Err(wasm_unsupported!("blocktype_params_results: type {:?}", ty)),
};
(
itertools::Either::Left(params.iter().copied()),
itertools::Either::Left(results.iter().copied()),
)
} }
}) wasmparser::TypeOrFuncType::FuncType(ty_index) => {
let ty = validator
.resources()
.func_type_at(ty_index)
.expect("should be valid");
(
itertools::Either::Right(ty.inputs()),
itertools::Either::Right(ty.outputs()),
)
}
});
} }
/// Create a `Block` with the given Wasm parameters. /// Create a `Block` with the given Wasm parameters.
pub fn block_with_params<PE: TargetEnvironment + ?Sized>( pub fn block_with_params<PE: TargetEnvironment + ?Sized>(
builder: &mut FunctionBuilder, builder: &mut FunctionBuilder,
params: &[wasmparser::Type], params: impl IntoIterator<Item = wasmparser::Type>,
environ: &PE, environ: &PE,
) -> WasmResult<ir::Block> { ) -> WasmResult<ir::Block> {
let block = builder.create_block(); let block = builder.create_block();
for ty in params.iter() { for ty in params {
match ty { match ty {
wasmparser::Type::I32 => { wasmparser::Type::I32 => {
builder.append_block_param(block, ir::types::I32); builder.append_block_param(block, ir::types::I32);
@@ -240,7 +257,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((*ty).try_into()?)); builder.append_block_param(block, environ.reference_type(ty.try_into()?));
} }
wasmparser::Type::V128 => { wasmparser::Type::V128 => {
builder.append_block_param(block, ir::types::I8X16); builder.append_block_param(block, ir::types::I8X16);

View File

@@ -4,9 +4,6 @@ use cranelift_codegen::settings::{self, Flags};
use cranelift_codegen::verifier; use cranelift_codegen::verifier;
use cranelift_wasm::{translate_module, DummyEnvironment, FuncIndex, ReturnMode}; use cranelift_wasm::{translate_module, DummyEnvironment, FuncIndex, ReturnMode};
use std::fs; use std::fs;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::path::Path; use std::path::Path;
use std::str::FromStr; use std::str::FromStr;
use target_lexicon::triple; use target_lexicon::triple;
@@ -69,20 +66,13 @@ fn use_name_section() {
); );
} }
fn read_file(path: &Path) -> io::Result<Vec<u8>> {
let mut buf: Vec<u8> = Vec::new();
let mut file = File::open(path)?;
file.read_to_end(&mut buf)?;
Ok(buf)
}
fn read_module(path: &Path) -> Vec<u8> { fn read_module(path: &Path) -> Vec<u8> {
match path.extension() { match path.extension() {
None => { None => {
panic!("the file extension is not wasm or wat"); panic!("the file extension is not wasm or wat");
} }
Some(ext) => match ext.to_str() { Some(ext) => match ext.to_str() {
Some("wasm") => read_file(path).expect("error reading wasm file"), Some("wasm") => std::fs::read(path).expect("error reading wasm file"),
Some("wat") => wat::parse_file(path) Some("wat") => wat::parse_file(path)
.map_err(|e| e.to_string()) .map_err(|e| e.to_string())
.expect("failed to parse wat"), .expect("failed to parse wat"),

View File

@@ -1,5 +1,6 @@
(module (module
(data $passive "this is a passive data segment") (data $passive "this is a passive data segment")
(memory 0)
(func (export "init") (param i32 i32 i32) (func (export "init") (param i32 i32 i32)
local.get 0 ;; dst local.get 0 ;; dst

View File

@@ -6,7 +6,7 @@
global.get 2 global.get 2
global.get 3) global.get 3)
(global (export "externref-imported") externref (ref.func $imported)) (global (export "externref-imported") externref (ref.null extern))
(global (export "externref-local") externref (ref.func $local)) (global (export "externref-local") externref (ref.null extern))
(global (export "funcref-imported") funcref (ref.func $imported)) (global (export "funcref-imported") funcref (ref.func $imported))
(global (export "funcref-local") funcref (ref.func $local))) (global (export "funcref-local") funcref (ref.func $local)))

View File

@@ -267,8 +267,11 @@ fn get_function_address_map<'data>(
// Generate artificial srcloc for function start/end to identify boundary // Generate artificial srcloc for function start/end to identify boundary
// within module. Similar to FuncTranslator::cur_srcloc(): it will wrap around // within module. Similar to FuncTranslator::cur_srcloc(): it will wrap around
// if byte code is larger than 4 GB. // if byte code is larger than 4 GB.
let start_srcloc = ir::SourceLoc::new(data.module_offset as u32); let data = data.body.get_binary_reader();
let end_srcloc = ir::SourceLoc::new((data.module_offset + data.data.len()) as u32); let offset = data.original_position();
let len = data.bytes_remaining();
let start_srcloc = ir::SourceLoc::new(offset as u32);
let end_srcloc = ir::SourceLoc::new((offset + len) as u32);
FunctionAddressMap { FunctionAddressMap {
instructions, instructions,
@@ -302,7 +305,7 @@ impl Compiler for Cranelift {
&self, &self,
translation: &ModuleTranslation<'_>, translation: &ModuleTranslation<'_>,
func_index: DefinedFuncIndex, func_index: DefinedFuncIndex,
input: &FunctionBodyData<'_>, mut input: FunctionBodyData<'_>,
isa: &dyn isa::TargetIsa, isa: &dyn isa::TargetIsa,
) -> Result<CompiledFunction, CompileError> { ) -> Result<CompiledFunction, CompileError> {
let module = &translation.module; let module = &translation.module;
@@ -351,14 +354,15 @@ impl Compiler for Cranelift {
}); });
context.func.stack_limit = Some(stack_limit); context.func.stack_limit = Some(stack_limit);
let mut func_translator = self.take_translator(); let mut func_translator = self.take_translator();
let result = func_translator.translate( let result = func_translator.translate_body(
translation.module_translation.as_ref().unwrap(), &mut input.validator,
input.data, input.body.clone(),
input.module_offset,
&mut context.func, &mut context.func,
&mut func_env, &mut func_env,
); );
self.save_translator(func_translator); if result.is_ok() {
self.save_translator(func_translator);
}
result?; result?;
let mut code_buf: Vec<u8> = Vec::new(); let mut code_buf: Vec<u8> = Vec::new();
@@ -381,7 +385,7 @@ impl Compiler for Cranelift {
CompileError::Codegen(pretty_error(&context.func, Some(isa), error)) CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
})?; })?;
let address_transform = get_function_address_map(&context, input, code_buf.len(), isa); let address_transform = get_function_address_map(&context, &input, code_buf.len(), isa);
let ranges = if tunables.debug_info { let ranges = if tunables.debug_info {
let ranges = context.build_value_labels_ranges(isa).map_err(|error| { let ranges = context.build_value_labels_ranges(isa).map_err(|error| {

View File

@@ -13,7 +13,7 @@ edition = "2018"
[dependencies] [dependencies]
gimli = "0.22.0" gimli = "0.22.0"
wasmparser = "0.59.0" wasmparser = "0.62.0"
object = { version = "0.21.1", default-features = false, features = ["read", "write"] } object = { version = "0.21.1", default-features = false, features = ["read", "write"] }
wasmtime-environ = { path = "../environ", version = "0.20.0" } wasmtime-environ = { path = "../environ", version = "0.20.0" }
target-lexicon = { version = "0.11.0", default-features = false } target-lexicon = { version = "0.11.0", default-features = false }

View File

@@ -16,7 +16,7 @@ anyhow = "1.0"
cranelift-codegen = { path = "../../cranelift/codegen", version = "0.67.0", features = ["enable-serde"] } cranelift-codegen = { path = "../../cranelift/codegen", version = "0.67.0", features = ["enable-serde"] }
cranelift-entity = { path = "../../cranelift/entity", version = "0.67.0", features = ["enable-serde"] } cranelift-entity = { path = "../../cranelift/entity", version = "0.67.0", features = ["enable-serde"] }
cranelift-wasm = { path = "../../cranelift/wasm", version = "0.67.0", features = ["enable-serde"] } cranelift-wasm = { path = "../../cranelift/wasm", version = "0.67.0", features = ["enable-serde"] }
wasmparser = "0.59.0" wasmparser = "0.62.0"
indexmap = { version = "1.0.2", features = ["serde-1"] } indexmap = { version = "1.0.2", features = ["serde-1"] }
thiserror = "1.0.4" thiserror = "1.0.4"
serde = { version = "1.0.94", features = ["derive"] } serde = { version = "1.0.94", features = ["derive"] }

View File

@@ -103,7 +103,7 @@ pub trait Compiler: Send + Sync {
&self, &self,
translation: &ModuleTranslation<'_>, translation: &ModuleTranslation<'_>,
index: DefinedFuncIndex, index: DefinedFuncIndex,
data: &FunctionBodyData<'_>, data: FunctionBodyData<'_>,
isa: &dyn isa::TargetIsa, isa: &dyn isa::TargetIsa,
) -> Result<CompiledFunction, CompileError>; ) -> Result<CompiledFunction, CompileError>;
} }

View File

@@ -15,12 +15,14 @@ use std::convert::TryFrom;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use wasmparser::Type as WasmType; use wasmparser::Type as WasmType;
use wasmparser::{FuncValidator, FunctionBody, ValidatorResources, WasmFeatures};
/// Object containing the standalone environment information. /// Object containing the standalone environment information.
pub struct ModuleEnvironment<'data> { pub struct ModuleEnvironment<'data> {
/// The result to be filled in. /// The result to be filled in.
result: ModuleTranslation<'data>, result: ModuleTranslation<'data>,
code_index: u32, code_index: u32,
features: WasmFeatures,
} }
/// The result of translating via `ModuleEnvironment`. Function bodies are not /// The result of translating via `ModuleEnvironment`. Function bodies are not
@@ -50,13 +52,11 @@ pub struct ModuleTranslation<'data> {
} }
/// Contains function data: byte code and its offset in the module. /// Contains function data: byte code and its offset in the module.
#[derive(Hash)]
pub struct FunctionBodyData<'a> { pub struct FunctionBodyData<'a> {
/// Body byte code. /// The body of the function, containing code and locals.
pub data: &'a [u8], pub body: FunctionBody<'a>,
/// Validator for the function body
/// Body offset in the module file. pub validator: FuncValidator<ValidatorResources>,
pub module_offset: usize,
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
@@ -102,7 +102,11 @@ pub struct FunctionMetadata {
impl<'data> ModuleEnvironment<'data> { impl<'data> ModuleEnvironment<'data> {
/// Allocates the environment data structures. /// Allocates the environment data structures.
pub fn new(target_config: TargetFrontendConfig, tunables: &Tunables) -> Self { pub fn new(
target_config: TargetFrontendConfig,
tunables: &Tunables,
features: &WasmFeatures,
) -> Self {
Self { Self {
result: ModuleTranslation { result: ModuleTranslation {
target_config, target_config,
@@ -118,6 +122,7 @@ impl<'data> ModuleEnvironment<'data> {
}, },
}, },
code_index: 0, code_index: 0,
features: *features,
} }
} }
@@ -442,21 +447,15 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
fn define_function_body( fn define_function_body(
&mut self, &mut self,
_module_translation: &ModuleTranslationState, validator: FuncValidator<ValidatorResources>,
body_bytes: &'data [u8], body: FunctionBody<'data>,
body_offset: usize,
) -> WasmResult<()> { ) -> WasmResult<()> {
self.result.function_body_inputs.push(FunctionBodyData {
data: body_bytes,
module_offset: body_offset,
});
if let Some(info) = &mut self.result.debuginfo { if let Some(info) = &mut self.result.debuginfo {
let func_index = self.code_index + self.result.module.num_imported_funcs as u32; let func_index = self.code_index + self.result.module.num_imported_funcs as u32;
let func_index = FuncIndex::from_u32(func_index); let func_index = FuncIndex::from_u32(func_index);
let sig_index = self.result.module.functions[func_index]; let sig_index = self.result.module.functions[func_index];
let sig = &self.result.module.signatures[sig_index]; let sig = &self.result.module.signatures[sig_index];
let mut locals = Vec::new(); let mut locals = Vec::new();
let body = wasmparser::FunctionBody::new(body_offset, body_bytes);
for pair in body.get_locals_reader()? { for pair in body.get_locals_reader()? {
locals.push(pair?); locals.push(pair?);
} }
@@ -465,6 +464,9 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
params: sig.0.params.iter().cloned().map(|i| i.into()).collect(), params: sig.0.params.iter().cloned().map(|i| i.into()).collect(),
}); });
} }
self.result
.function_body_inputs
.push(FunctionBodyData { validator, body });
self.code_index += 1; self.code_index += 1;
Ok(()) Ok(())
} }
@@ -564,6 +566,10 @@ and for re-adding support for interface types you can see this issue:
_ => Ok(()), _ => Ok(()),
} }
} }
fn wasm_features(&self) -> WasmFeatures {
self.features
}
} }
/// Add environment-specific function parameters. /// Add environment-specific function parameters.

View File

@@ -13,8 +13,8 @@ binaryen = { version = "0.10.0", optional = true }
env_logger = "0.7.1" env_logger = "0.7.1"
log = "0.4.8" log = "0.4.8"
rayon = "1.2.1" rayon = "1.2.1"
wasmparser = "0.59.0" wasmparser = "0.62.0"
wasmprinter = "0.2.6" wasmprinter = "0.2.8"
wasmtime = { path = "../wasmtime" } wasmtime = { path = "../wasmtime" }
wasmtime-wast = { path = "../wast" } wasmtime-wast = { path = "../wast" }

View File

@@ -219,7 +219,10 @@ fn predict_rss(wasm: &[u8]) -> Result<usize> {
// the minimum amount of memory to our predicted rss. // the minimum amount of memory to our predicted rss.
Payload::MemorySection(s) => { Payload::MemorySection(s) => {
for entry in s { for entry in s {
let initial = entry?.limits.initial as usize; let initial = match entry? {
MemoryType::M32 { limits, .. } => limits.initial as usize,
MemoryType::M64 { limits } => limits.initial as usize,
};
prediction += initial * 64 * 1024; prediction += initial * 64 * 1024;
} }
} }

View File

@@ -28,7 +28,7 @@ rayon = { version = "1.0", optional = true }
region = "2.1.0" region = "2.1.0"
thiserror = "1.0.4" thiserror = "1.0.4"
target-lexicon = { version = "0.11.0", default-features = false } target-lexicon = { version = "0.11.0", default-features = false }
wasmparser = "0.59.0" wasmparser = "0.62.0"
more-asserts = "0.2.1" more-asserts = "0.2.1"
anyhow = "1.0" anyhow = "1.0"
cfg-if = "0.1.9" cfg-if = "0.1.9"

View File

@@ -4,6 +4,8 @@ use crate::instantiate::SetupError;
use crate::object::{build_object, ObjectUnwindInfo}; use crate::object::{build_object, ObjectUnwindInfo};
use object::write::Object; use object::write::Object;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::mem;
use wasmparser::WasmFeatures;
use wasmtime_debug::{emit_dwarf, DwarfSection}; use wasmtime_debug::{emit_dwarf, DwarfSection};
use wasmtime_environ::entity::EntityRef; use wasmtime_environ::entity::EntityRef;
use wasmtime_environ::isa::{TargetFrontendConfig, TargetIsa}; use wasmtime_environ::isa::{TargetFrontendConfig, TargetIsa};
@@ -40,11 +42,17 @@ pub struct Compiler {
compiler: Box<dyn EnvCompiler>, compiler: Box<dyn EnvCompiler>,
strategy: CompilationStrategy, strategy: CompilationStrategy,
tunables: Tunables, tunables: Tunables,
features: WasmFeatures,
} }
impl Compiler { impl Compiler {
/// Construct a new `Compiler`. /// Construct a new `Compiler`.
pub fn new(isa: Box<dyn TargetIsa>, strategy: CompilationStrategy, tunables: Tunables) -> Self { pub fn new(
isa: Box<dyn TargetIsa>,
strategy: CompilationStrategy,
tunables: Tunables,
features: WasmFeatures,
) -> Self {
Self { Self {
isa, isa,
strategy, strategy,
@@ -56,6 +64,7 @@ impl Compiler {
CompilationStrategy::Lightbeam => Box::new(wasmtime_lightbeam::Lightbeam), CompilationStrategy::Lightbeam => Box::new(wasmtime_lightbeam::Lightbeam),
}, },
tunables, tunables,
features,
} }
} }
} }
@@ -107,20 +116,26 @@ impl Compiler {
&self.tunables &self.tunables
} }
/// Return the enabled wasm features.
pub fn features(&self) -> &WasmFeatures {
&self.features
}
/// Compile the given function bodies. /// Compile the given function bodies.
pub fn compile<'data>( pub fn compile<'data>(
&self, &self,
translation: &ModuleTranslation, translation: &mut ModuleTranslation,
) -> Result<Compilation, SetupError> { ) -> Result<Compilation, SetupError> {
let functions = mem::take(&mut translation.function_body_inputs);
cfg_if::cfg_if! { cfg_if::cfg_if! {
if #[cfg(feature = "parallel-compilation")] { if #[cfg(feature = "parallel-compilation")] {
use rayon::prelude::*; use rayon::prelude::*;
let iter = translation.function_body_inputs let iter = functions
.iter() .into_iter()
.collect::<Vec<_>>() .collect::<Vec<_>>()
.into_par_iter(); .into_par_iter();
} else { } else {
let iter = translation.function_body_inputs.iter(); let iter = functions.into_iter();
} }
} }
let funcs = iter let funcs = iter
@@ -161,12 +176,14 @@ impl Hash for Compiler {
compiler: _, compiler: _,
isa, isa,
tunables, tunables,
features,
} = self; } = self;
// Hash compiler's flags: compilation strategy, isa, frontend config, // Hash compiler's flags: compilation strategy, isa, frontend config,
// misc tunables. // misc tunables.
strategy.hash(hasher); strategy.hash(hasher);
isa.triple().hash(hasher); isa.triple().hash(hasher);
features.hash(hasher);
// TODO: if this `to_string()` is too expensive then we should upstream // TODO: if this `to_string()` is too expensive then we should upstream
// a native hashing ability of flags into cranelift itself, but // a native hashing ability of flags into cranelift itself, but
// compilation and/or cache loading is relatively expensive so seems // compilation and/or cache loading is relatively expensive so seems

View File

@@ -82,9 +82,13 @@ struct FunctionInfo {
impl CompilationArtifacts { impl CompilationArtifacts {
/// Builds compilation artifacts. /// Builds compilation artifacts.
pub fn build(compiler: &Compiler, data: &[u8]) -> Result<Self, SetupError> { pub fn build(compiler: &Compiler, data: &[u8]) -> Result<Self, SetupError> {
let environ = ModuleEnvironment::new(compiler.frontend_config(), compiler.tunables()); let environ = ModuleEnvironment::new(
compiler.frontend_config(),
compiler.tunables(),
compiler.features(),
);
let translation = environ let mut translation = environ
.translate(data) .translate(data)
.map_err(|error| SetupError::Compile(CompileError::Wasm(error)))?; .map_err(|error| SetupError::Compile(CompileError::Wasm(error)))?;
@@ -92,7 +96,7 @@ impl CompilationArtifacts {
obj, obj,
unwind_info, unwind_info,
funcs, funcs,
} = compiler.compile(&translation)?; } = compiler.compile(&mut translation)?;
let ModuleTranslation { let ModuleTranslation {
module, module,

View File

@@ -24,7 +24,7 @@ smallvec = "1.0.0"
staticvec = "0.10" staticvec = "0.10"
thiserror = "1.0.9" thiserror = "1.0.9"
typemap = "0.3" typemap = "0.3"
wasmparser = "0.59.0" wasmparser = "0.62.0"
[dev-dependencies] [dev-dependencies]
lazy_static = "1.2" lazy_static = "1.2"

View File

@@ -437,8 +437,9 @@ pub struct MemoryImmediate {
impl From<WasmMemoryImmediate> for MemoryImmediate { impl From<WasmMemoryImmediate> for MemoryImmediate {
fn from(other: WasmMemoryImmediate) -> Self { fn from(other: WasmMemoryImmediate) -> Self {
assert_eq!(other.memory, 0);
MemoryImmediate { MemoryImmediate {
flags: other.flags, flags: other.align.into(),
offset: other.offset, offset: other.offset,
} }
} }
@@ -544,12 +545,8 @@ pub enum Operator<Label> {
Store32 { Store32 {
memarg: MemoryImmediate, memarg: MemoryImmediate,
}, },
MemorySize { MemorySize,
reserved: u32, MemoryGrow,
},
MemoryGrow {
reserved: u32,
},
Const(Value), Const(Value),
Eq(SignlessType), Eq(SignlessType),
Ne(SignlessType), Ne(SignlessType),
@@ -1940,12 +1937,13 @@ where
} }
WasmOperator::BrTable { table } => { WasmOperator::BrTable { table } => {
self.unreachable = true; self.unreachable = true;
let (targets, default) = table.read_table()?; let mut targets = table.targets().collect::<Result<Vec<_>, _>>()?;
let default = targets.pop().unwrap().0;
let control_frames = &mut self.control_frames; let control_frames = &mut self.control_frames;
let stack = &self.stack; let stack = &self.stack;
let targets = targets let targets = targets
.into_iter() .into_iter()
.map(|&depth| { .map(|(depth, _)| {
control_frames[depth as _].mark_branched_to(); control_frames[depth as _].mark_branched_to();
let block = &control_frames[depth as _]; let block = &control_frames[depth as _];
@@ -2121,8 +2119,8 @@ where
WasmOperator::I64Store32 { memarg } => one(Operator::Store32 { WasmOperator::I64Store32 { memarg } => one(Operator::Store32 {
memarg: memarg.into(), memarg: memarg.into(),
}), }),
WasmOperator::MemorySize { reserved } => one(Operator::MemorySize { reserved }), WasmOperator::MemorySize { .. } => one(Operator::MemorySize),
WasmOperator::MemoryGrow { reserved } => one(Operator::MemoryGrow { reserved }), WasmOperator::MemoryGrow { .. } => one(Operator::MemoryGrow),
WasmOperator::I32Const { value } => one(Operator::Const(Value::I32(value))), WasmOperator::I32Const { value } => one(Operator::Const(Value::I32(value))),
WasmOperator::I64Const { value } => one(Operator::Const(Value::I64(value))), WasmOperator::I64Const { value } => one(Operator::Const(Value::I64(value))),
WasmOperator::F32Const { value } => one(Operator::Const(Value::F32(value.into()))), WasmOperator::F32Const { value } => one(Operator::Const(Value::F32(value.into()))),

View File

@@ -10,7 +10,7 @@ use memoffset::offset_of;
use std::{convert::TryInto, mem}; use std::{convert::TryInto, mem};
use thiserror::Error; use thiserror::Error;
use wasmparser::{FuncType, MemoryType, Parser, Payload, Type}; use wasmparser::{FuncType, MemoryType, Parser, Payload, ResizableLimits, Type};
pub trait AsValueType { pub trait AsValueType {
const TYPE: Type; const TYPE: Type;
@@ -104,12 +104,12 @@ pub struct TranslatedModule {
ctx: SimpleContext, ctx: SimpleContext,
// TODO: Should we wrap this in a `Mutex` so that calling functions from multiple // TODO: Should we wrap this in a `Mutex` so that calling functions from multiple
// threads doesn't cause data races? // threads doesn't cause data races?
memory: Option<MemoryType>, memory: Option<ResizableLimits>,
} }
impl TranslatedModule { impl TranslatedModule {
pub fn instantiate(self) -> ExecutableModule { pub fn instantiate(self) -> ExecutableModule {
let mem_size = self.memory.map(|m| m.limits.initial).unwrap_or(0) as usize; let mem_size = self.memory.map(|limits| limits.initial).unwrap_or(0) as usize;
let mem: BoxSlice<_> = vec![0u8; mem_size * WASM_PAGE_SIZE] let mem: BoxSlice<_> = vec![0u8; mem_size * WASM_PAGE_SIZE]
.into_boxed_slice() .into_boxed_slice()
.into(); .into();
@@ -535,12 +535,19 @@ pub fn translate_only(data: &[u8]) -> Result<TranslatedModule, Error> {
if !mem.is_empty() { if !mem.is_empty() {
let mem = mem[0]; let mem = mem[0];
if Some(mem.limits.initial) != mem.limits.maximum { let limits = match mem {
MemoryType::M32 {
limits,
shared: false,
} => limits,
_ => return Err(Error::Input("unsupported memory".to_string())),
};
if Some(limits.initial) != limits.maximum {
return Err(Error::Input( return Err(Error::Input(
"Custom memory limits not supported in lightbeam".to_string(), "Custom memory limits not supported in lightbeam".to_string(),
)); ));
} }
output.memory = Some(mem); output.memory = Some(limits);
} }
} }
Payload::GlobalSection(s) => { Payload::GlobalSection(s) => {

View File

@@ -13,6 +13,6 @@ edition = "2018"
[dependencies] [dependencies]
lightbeam = { path = "..", version = "0.20.0" } lightbeam = { path = "..", version = "0.20.0" }
wasmparser = "0.59" wasmparser = "0.62"
cranelift-codegen = { path = "../../../cranelift/codegen", version = "0.67.0" } cranelift-codegen = { path = "../../../cranelift/codegen", version = "0.67.0" }
wasmtime-environ = { path = "../../environ", version = "0.20.0" } wasmtime-environ = { path = "../../environ", version = "0.20.0" }

View File

@@ -24,7 +24,7 @@ impl Compiler for Lightbeam {
&self, &self,
translation: &ModuleTranslation, translation: &ModuleTranslation,
i: DefinedFuncIndex, i: DefinedFuncIndex,
function_body: &FunctionBodyData<'_>, function_body: FunctionBodyData<'_>,
isa: &dyn isa::TargetIsa, isa: &dyn isa::TargetIsa,
) -> Result<CompiledFunction, CompileError> { ) -> Result<CompiledFunction, CompileError> {
if translation.tunables.debug_info { if translation.tunables.debug_info {
@@ -49,7 +49,7 @@ impl Compiler for Lightbeam {
offsets: &mut NullOffsetSink, offsets: &mut NullOffsetSink,
}, },
i.as_u32(), i.as_u32(),
wasmparser::FunctionBody::new(0, function_body.data), function_body.body,
) )
.map_err(|e| CompileError::Codegen(format!("Failed to translate function: {}", e)))?; .map_err(|e| CompileError::Codegen(format!("Failed to translate function: {}", e)))?;

View File

@@ -15,8 +15,8 @@ wasmtime-environ = { path = "../environ", version = "0.20.0" }
wasmtime-jit = { path = "../jit", version = "0.20.0" } wasmtime-jit = { path = "../jit", version = "0.20.0" }
wasmtime-cache = { path = "../cache", version = "0.20.0", optional = true } wasmtime-cache = { path = "../cache", version = "0.20.0", optional = true }
wasmtime-profiling = { path = "../profiling", version = "0.20.0" } wasmtime-profiling = { path = "../profiling", version = "0.20.0" }
wasmparser = "0.59.0"
target-lexicon = { version = "0.11.0", default-features = false } target-lexicon = { version = "0.11.0", default-features = false }
wasmparser = "0.62.0"
anyhow = "1.0.19" anyhow = "1.0.19"
region = "2.2.0" region = "2.2.0"
libc = "0.2" libc = "0.2"

View File

@@ -38,7 +38,7 @@ fn instantiate(
let instance = store.add_instance(instance); let instance = store.add_instance(instance);
instance instance
.initialize( .initialize(
config.wasm_bulk_memory, config.features.bulk_memory,
&compiled_module.data_initializers(), &compiled_module.data_initializers(),
) )
.map_err(|e| -> Error { .map_err(|e| -> Error {

View File

@@ -4,6 +4,7 @@ use crate::types::{EntityType, ExportType, ExternType, ImportType};
use anyhow::{bail, Context, Result}; use anyhow::{bail, Context, Result};
use std::path::Path; use std::path::Path;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use wasmparser::Validator;
#[cfg(feature = "cache")] #[cfg(feature = "cache")]
use wasmtime_cache::ModuleCacheEntry; use wasmtime_cache::ModuleCacheEntry;
use wasmtime_jit::{CompilationArtifacts, CompiledModule}; use wasmtime_jit::{CompilationArtifacts, CompiledModule};
@@ -238,70 +239,6 @@ impl Module {
/// # } /// # }
/// ``` /// ```
pub fn from_binary(engine: &Engine, binary: &[u8]) -> Result<Module> { pub fn from_binary(engine: &Engine, binary: &[u8]) -> Result<Module> {
Module::validate(engine, binary)?;
// Note that the call to `from_binary_unchecked` here should be ok
// because we previously validated the binary, meaning we're guaranteed
// to pass a valid binary for `engine`.
unsafe { Module::from_binary_unchecked(engine, binary) }
}
/// Creates a new WebAssembly `Module` from the given in-memory `binary`
/// data, skipping validation and asserting that `binary` is a valid
/// WebAssembly module.
///
/// This function is the same as [`Module::new`] except that it skips the
/// call to [`Module::validate`] and it does not support the text format of
/// WebAssembly. The WebAssembly binary is not validated for
/// correctness and it is simply assumed as valid.
///
/// For more information about creation of a module and the `engine` argument
/// see the documentation of [`Module::new`].
///
/// # Unsafety
///
/// This function is `unsafe` due to the unchecked assumption that the input
/// `binary` is valid. If the `binary` is not actually a valid wasm binary it
/// may cause invalid machine code to get generated, cause panics, etc.
///
/// It is only safe to call this method if [`Module::validate`] succeeds on
/// the same arguments passed to this function.
///
/// # Errors
///
/// This function may fail for many of the same reasons as [`Module::new`].
/// While this assumes that the binary is valid it still needs to actually
/// be somewhat valid for decoding purposes, and the basics of decoding can
/// still fail.
pub unsafe fn from_binary_unchecked(engine: &Engine, binary: &[u8]) -> Result<Module> {
Module::compile(engine, binary)
}
/// Validates `binary` input data as a WebAssembly binary given the
/// configuration in `engine`.
///
/// This function will perform a speedy validation of the `binary` input
/// WebAssembly module (which is in [binary form][binary], the text format
/// is not accepted by this function) and return either `Ok` or `Err`
/// depending on the results of validation. The `engine` argument indicates
/// configuration for WebAssembly features, for example, which are used to
/// indicate what should be valid and what shouldn't be.
///
/// Validation automatically happens as part of [`Module::new`], but is a
/// requirement for [`Module::from_binary_unchecked`] to be safe.
///
/// # Errors
///
/// If validation fails for any reason (type check error, usage of a feature
/// that wasn't enabled, etc) then an error with a description of the
/// validation issue will be returned.
///
/// [binary]: https://webassembly.github.io/spec/core/binary/index.html
pub fn validate(engine: &Engine, binary: &[u8]) -> Result<()> {
engine.config().validator().validate_all(binary)?;
Ok(())
}
unsafe fn compile(engine: &Engine, binary: &[u8]) -> Result<Self> {
#[cfg(feature = "cache")] #[cfg(feature = "cache")]
let artifacts = ModuleCacheEntry::new("wasmtime", engine.cache_config()) let artifacts = ModuleCacheEntry::new("wasmtime", engine.cache_config())
.get_data((engine.compiler(), binary), |(compiler, binary)| { .get_data((engine.compiler(), binary), |(compiler, binary)| {
@@ -323,6 +260,32 @@ impl Module {
}) })
} }
/// Validates `binary` input data as a WebAssembly binary given the
/// configuration in `engine`.
///
/// This function will perform a speedy validation of the `binary` input
/// WebAssembly module (which is in [binary form][binary], the text format
/// is not accepted by this function) and return either `Ok` or `Err`
/// depending on the results of validation. The `engine` argument indicates
/// configuration for WebAssembly features, for example, which are used to
/// indicate what should be valid and what shouldn't be.
///
/// Validation automatically happens as part of [`Module::new`].
///
/// # Errors
///
/// If validation fails for any reason (type check error, usage of a feature
/// that wasn't enabled, etc) then an error with a description of the
/// validation issue will be returned.
///
/// [binary]: https://webassembly.github.io/spec/core/binary/index.html
pub fn validate(engine: &Engine, binary: &[u8]) -> Result<()> {
let mut validator = Validator::new();
validator.wasm_features(engine.config().features);
validator.validate_all(binary)?;
Ok(())
}
/// Serialize compilation artifacts to the buffer. See also `deseriaize`. /// Serialize compilation artifacts to the buffer. See also `deseriaize`.
pub fn serialize(&self) -> Result<Vec<u8>> { pub fn serialize(&self) -> Result<Vec<u8>> {
let artifacts = ( let artifacts = (

View File

@@ -12,7 +12,7 @@ use std::path::Path;
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use std::sync::Arc; use std::sync::Arc;
use target_lexicon::Triple; use target_lexicon::Triple;
use wasmparser::Validator; use wasmparser::WasmFeatures;
#[cfg(feature = "cache")] #[cfg(feature = "cache")]
use wasmtime_cache::CacheConfig; use wasmtime_cache::CacheConfig;
use wasmtime_environ::settings::{self, Configurable, SetError}; use wasmtime_environ::settings::{self, Configurable, SetError};
@@ -45,11 +45,7 @@ pub struct Config {
pub(crate) profiler: Arc<dyn ProfilingAgent>, pub(crate) profiler: Arc<dyn ProfilingAgent>,
pub(crate) memory_creator: Option<MemoryCreatorProxy>, pub(crate) memory_creator: Option<MemoryCreatorProxy>,
pub(crate) max_wasm_stack: usize, pub(crate) max_wasm_stack: usize,
wasm_threads: bool, pub(crate) features: WasmFeatures,
wasm_reference_types: bool,
pub(crate) wasm_bulk_memory: bool,
wasm_simd: bool,
wasm_multi_value: bool,
} }
impl Config { impl Config {
@@ -98,11 +94,7 @@ impl Config {
profiler: Arc::new(NullProfilerAgent), profiler: Arc::new(NullProfilerAgent),
memory_creator: None, memory_creator: None,
max_wasm_stack: 1 << 20, max_wasm_stack: 1 << 20,
wasm_threads: false, features: WasmFeatures::default(),
wasm_reference_types: cfg!(target_arch = "x86_64"),
wasm_bulk_memory: true,
wasm_simd: false,
wasm_multi_value: true,
} }
} }
@@ -167,7 +159,7 @@ impl Config {
/// ///
/// [threads]: https://github.com/webassembly/threads /// [threads]: https://github.com/webassembly/threads
pub fn wasm_threads(&mut self, enable: bool) -> &mut Self { pub fn wasm_threads(&mut self, enable: bool) -> &mut Self {
self.wasm_threads = enable; self.features.threads = enable;
// The threads proposal depends on the bulk memory proposal // The threads proposal depends on the bulk memory proposal
if enable { if enable {
self.wasm_bulk_memory(true); self.wasm_bulk_memory(true);
@@ -189,7 +181,7 @@ impl Config {
/// ///
/// [proposal]: https://github.com/webassembly/reference-types /// [proposal]: https://github.com/webassembly/reference-types
pub fn wasm_reference_types(&mut self, enable: bool) -> &mut Self { pub fn wasm_reference_types(&mut self, enable: bool) -> &mut Self {
self.wasm_reference_types = enable; self.features.reference_types = enable;
self.flags self.flags
.set("enable_safepoints", if enable { "true" } else { "false" }) .set("enable_safepoints", if enable { "true" } else { "false" })
@@ -224,7 +216,7 @@ impl Config {
/// ///
/// [proposal]: https://github.com/webassembly/simd /// [proposal]: https://github.com/webassembly/simd
pub fn wasm_simd(&mut self, enable: bool) -> &mut Self { pub fn wasm_simd(&mut self, enable: bool) -> &mut Self {
self.wasm_simd = enable; self.features.simd = enable;
let val = if enable { "true" } else { "false" }; let val = if enable { "true" } else { "false" };
self.flags self.flags
.set("enable_simd", val) .set("enable_simd", val)
@@ -242,7 +234,7 @@ impl Config {
/// ///
/// [proposal]: https://github.com/webassembly/bulk-memory-operations /// [proposal]: https://github.com/webassembly/bulk-memory-operations
pub fn wasm_bulk_memory(&mut self, enable: bool) -> &mut Self { pub fn wasm_bulk_memory(&mut self, enable: bool) -> &mut Self {
self.wasm_bulk_memory = enable; self.features.bulk_memory = enable;
self self
} }
@@ -256,7 +248,7 @@ impl Config {
/// ///
/// [proposal]: https://github.com/webassembly/multi-value /// [proposal]: https://github.com/webassembly/multi-value
pub fn wasm_multi_value(&mut self, enable: bool) -> &mut Self { pub fn wasm_multi_value(&mut self, enable: bool) -> &mut Self {
self.wasm_multi_value = enable; self.features.multi_value = enable;
self self
} }
@@ -615,19 +607,9 @@ impl Config {
self.isa_flags.clone().finish(settings::Flags::new(flags)) self.isa_flags.clone().finish(settings::Flags::new(flags))
} }
pub(crate) fn validator(&self) -> Validator {
let mut ret = Validator::new();
ret.wasm_threads(self.wasm_threads)
.wasm_bulk_memory(self.wasm_bulk_memory)
.wasm_multi_value(self.wasm_multi_value)
.wasm_reference_types(self.wasm_reference_types)
.wasm_simd(self.wasm_simd);
return ret;
}
fn build_compiler(&self) -> Compiler { fn build_compiler(&self) -> Compiler {
let isa = self.target_isa(); let isa = self.target_isa();
Compiler::new(isa, self.strategy, self.tunables.clone()) Compiler::new(isa, self.strategy, self.tunables.clone(), self.features)
} }
/// Hashes/fingerprints compiler setting to ensure that compatible /// Hashes/fingerprints compiler setting to ensure that compatible
@@ -666,11 +648,11 @@ impl fmt::Debug for Config {
f.debug_struct("Config") f.debug_struct("Config")
.field("debug_info", &self.tunables.debug_info) .field("debug_info", &self.tunables.debug_info)
.field("strategy", &self.strategy) .field("strategy", &self.strategy)
.field("wasm_threads", &self.wasm_threads) .field("wasm_threads", &self.features.threads)
.field("wasm_reference_types", &self.wasm_reference_types) .field("wasm_reference_types", &self.features.reference_types)
.field("wasm_bulk_memory", &self.wasm_bulk_memory) .field("wasm_bulk_memory", &self.features.bulk_memory)
.field("wasm_simd", &self.wasm_simd) .field("wasm_simd", &self.features.simd)
.field("wasm_multi_value", &self.wasm_multi_value) .field("wasm_multi_value", &self.features.multi_value)
.field( .field(
"flags", "flags",
&settings::Flags::new(self.flags.clone()).to_string(), &settings::Flags::new(self.flags.clone()).to_string(),

View File

@@ -1,6 +1,7 @@
use anyhow::{bail, Context as _, Result}; use anyhow::{bail, Context as _, Result};
use object::write::Object; use object::write::Object;
use target_lexicon::Triple; use target_lexicon::Triple;
use wasmparser::WasmFeatures;
use wasmtime::Strategy; use wasmtime::Strategy;
use wasmtime_environ::{settings, settings::Configurable, ModuleEnvironment, Tunables}; use wasmtime_environ::{settings, settings::Configurable, ModuleEnvironment, Tunables};
use wasmtime_jit::{native, Compiler}; use wasmtime_jit::{native, Compiler};
@@ -19,6 +20,7 @@ pub fn compile_to_obj(
None => native::builder(), None => native::builder(),
}; };
let mut flag_builder = settings::builder(); let mut flag_builder = settings::builder();
let mut features = WasmFeatures::default();
// There are two possible traps for division, and this way // There are two possible traps for division, and this way
// we get the proper one if code traps. // we get the proper one if code traps.
@@ -26,6 +28,7 @@ pub fn compile_to_obj(
if enable_simd { if enable_simd {
flag_builder.enable("enable_simd").unwrap(); flag_builder.enable("enable_simd").unwrap();
features.simd = true;
} }
match opt_level { match opt_level {
@@ -57,12 +60,13 @@ pub fn compile_to_obj(
s => bail!("unknown compilation strategy {:?}", s), s => bail!("unknown compilation strategy {:?}", s),
}, },
tunables.clone(), tunables.clone(),
features.clone(),
); );
let environ = ModuleEnvironment::new(compiler.isa().frontend_config(), &tunables); let environ = ModuleEnvironment::new(compiler.isa().frontend_config(), &tunables, &features);
let translation = environ let mut translation = environ
.translate(wasm) .translate(wasm)
.context("failed to translate module")?; .context("failed to translate module")?;
let compilation = compiler.compile(&translation)?; let compilation = compiler.compile(&mut translation)?;
Ok(compilation.obj) Ok(compilation.obj)
} }