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:
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,6 @@ use crate::environ::{
|
||||
WasmFuncType, WasmResult,
|
||||
};
|
||||
use crate::func_translator::FuncTranslator;
|
||||
use crate::state::ModuleTranslationState;
|
||||
use crate::translation_utils::{
|
||||
DataIndex, DefinedFuncIndex, ElemIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex,
|
||||
SignatureIndex, Table, TableIndex,
|
||||
@@ -26,6 +25,7 @@ use cranelift_frontend::FunctionBuilder;
|
||||
use std::boxed::Box;
|
||||
use std::string::String;
|
||||
use std::vec::Vec;
|
||||
use wasmparser::{FuncValidator, FunctionBody, ValidatorResources, WasmFeatures};
|
||||
|
||||
/// Compute a `ir::ExternalName` for a given wasm function index.
|
||||
fn get_func_name(func_index: FuncIndex) -> ir::ExternalName {
|
||||
@@ -738,10 +738,11 @@ impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
|
||||
|
||||
fn define_function_body(
|
||||
&mut self,
|
||||
module_translation_state: &ModuleTranslationState,
|
||||
body_bytes: &'data [u8],
|
||||
body_offset: usize,
|
||||
mut validator: FuncValidator<ValidatorResources>,
|
||||
body: FunctionBody<'data>,
|
||||
) -> WasmResult<()> {
|
||||
self.func_bytecode_sizes
|
||||
.push(body.get_binary_reader().bytes_remaining());
|
||||
let func = {
|
||||
let mut func_environ = DummyFuncEnvironment::new(&self.info, self.return_mode);
|
||||
let func_index =
|
||||
@@ -752,16 +753,10 @@ impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
|
||||
if self.debug_info {
|
||||
func.collect_debug_info();
|
||||
}
|
||||
self.trans.translate(
|
||||
module_translation_state,
|
||||
body_bytes,
|
||||
body_offset,
|
||||
&mut func,
|
||||
&mut func_environ,
|
||||
)?;
|
||||
self.trans
|
||||
.translate_body(&mut validator, body, &mut func, &mut func_environ)?;
|
||||
func
|
||||
};
|
||||
self.func_bytecode_sizes.push(body_bytes.len());
|
||||
self.info.function_bodies.push(func);
|
||||
Ok(())
|
||||
}
|
||||
@@ -773,4 +768,14 @@ impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
|
||||
fn declare_func_name(&mut self, func_index: FuncIndex, name: &'data str) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
//!
|
||||
//! [Wasmtime]: https://github.com/bytecodealliance/wasmtime
|
||||
|
||||
use crate::state::{FuncTranslationState, ModuleTranslationState};
|
||||
use crate::state::FuncTranslationState;
|
||||
use crate::translation_utils::{
|
||||
DataIndex, ElemIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex,
|
||||
Table, TableIndex,
|
||||
@@ -23,8 +23,8 @@ use serde::{Deserialize, Serialize};
|
||||
use std::boxed::Box;
|
||||
use std::string::ToString;
|
||||
use thiserror::Error;
|
||||
use wasmparser::BinaryReaderError;
|
||||
use wasmparser::Operator;
|
||||
use wasmparser::ValidatorResources;
|
||||
use wasmparser::{BinaryReaderError, FuncValidator, FunctionBody, Operator, WasmFeatures};
|
||||
|
||||
/// WebAssembly value type -- equivalent of `wasmparser`'s Type.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
@@ -798,9 +798,8 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment {
|
||||
/// Provides the contents of a function body.
|
||||
fn define_function_body(
|
||||
&mut self,
|
||||
module_translation_state: &ModuleTranslationState,
|
||||
body_bytes: &'data [u8],
|
||||
body_offset: usize,
|
||||
validator: FuncValidator<ValidatorResources>,
|
||||
body: FunctionBody<'data>,
|
||||
) -> WasmResult<()>;
|
||||
|
||||
/// 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<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the list of enabled wasm features this translation will be using.
|
||||
fn wasm_features(&self) -> WasmFeatures {
|
||||
WasmFeatures::default()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
use crate::code_translator::{bitcast_arguments, translate_operator, wasm_param_types};
|
||||
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::wasm_unsupported;
|
||||
use core::convert::TryInto;
|
||||
@@ -14,7 +14,7 @@ use cranelift_codegen::entity::EntityRef;
|
||||
use cranelift_codegen::ir::{self, Block, InstBuilder, ValueLabel};
|
||||
use cranelift_codegen::timing;
|
||||
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
|
||||
use wasmparser::{self, BinaryReader};
|
||||
use wasmparser::{self, BinaryReader, FuncValidator, FunctionBody, WasmModuleResources};
|
||||
|
||||
/// WebAssembly to Cranelift IR function translator.
|
||||
///
|
||||
@@ -55,29 +55,30 @@ impl FuncTranslator {
|
||||
///
|
||||
pub fn translate<FE: FuncEnvironment + ?Sized>(
|
||||
&mut self,
|
||||
module_translation_state: &ModuleTranslationState,
|
||||
validator: &mut FuncValidator<impl WasmModuleResources>,
|
||||
code: &[u8],
|
||||
code_offset: usize,
|
||||
func: &mut ir::Function,
|
||||
environ: &mut FE,
|
||||
) -> WasmResult<()> {
|
||||
self.translate_from_reader(
|
||||
module_translation_state,
|
||||
BinaryReader::new_with_offset(code, code_offset),
|
||||
self.translate_body(
|
||||
validator,
|
||||
FunctionBody::new(code_offset, code),
|
||||
func,
|
||||
environ,
|
||||
)
|
||||
}
|
||||
|
||||
/// Translate a binary WebAssembly function from a `BinaryReader`.
|
||||
pub fn translate_from_reader<FE: FuncEnvironment + ?Sized>(
|
||||
/// Translate a binary WebAssembly function from a `FunctionBody`.
|
||||
pub fn translate_body<FE: FuncEnvironment + ?Sized>(
|
||||
&mut self,
|
||||
module_translation_state: &ModuleTranslationState,
|
||||
mut reader: BinaryReader,
|
||||
validator: &mut FuncValidator<impl WasmModuleResources>,
|
||||
body: FunctionBody<'_>,
|
||||
func: &mut ir::Function,
|
||||
environ: &mut FE,
|
||||
) -> WasmResult<()> {
|
||||
let _tt = timing::wasm_translate_function();
|
||||
let mut reader = body.get_binary_reader();
|
||||
log::debug!(
|
||||
"translate({} bytes, {}{})",
|
||||
reader.bytes_remaining(),
|
||||
@@ -107,14 +108,8 @@ impl FuncTranslator {
|
||||
builder.append_block_params_for_function_returns(exit_block);
|
||||
self.state.initialize(&builder.func.signature, exit_block);
|
||||
|
||||
parse_local_decls(&mut reader, &mut builder, num_params, environ)?;
|
||||
parse_function_body(
|
||||
module_translation_state,
|
||||
reader,
|
||||
&mut builder,
|
||||
&mut self.state,
|
||||
environ,
|
||||
)?;
|
||||
parse_local_decls(&mut reader, &mut builder, num_params, environ, validator)?;
|
||||
parse_function_body(validator, reader, &mut builder, &mut self.state, environ)?;
|
||||
|
||||
builder.finalize();
|
||||
Ok(())
|
||||
@@ -161,14 +156,17 @@ fn parse_local_decls<FE: FuncEnvironment + ?Sized>(
|
||||
builder: &mut FunctionBuilder,
|
||||
num_params: usize,
|
||||
environ: &mut FE,
|
||||
validator: &mut FuncValidator<impl WasmModuleResources>,
|
||||
) -> WasmResult<()> {
|
||||
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 {
|
||||
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)?;
|
||||
}
|
||||
|
||||
@@ -218,7 +216,7 @@ fn declare_locals<FE: FuncEnvironment + ?Sized>(
|
||||
/// This assumes that the local variable declarations have already been parsed and function
|
||||
/// arguments and locals are declared in the builder.
|
||||
fn parse_function_body<FE: FuncEnvironment + ?Sized>(
|
||||
module_translation_state: &ModuleTranslationState,
|
||||
validator: &mut FuncValidator<impl WasmModuleResources>,
|
||||
mut reader: BinaryReader,
|
||||
builder: &mut FunctionBuilder,
|
||||
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.
|
||||
debug_assert_eq!(state.control_stack.len(), 1, "State not initialized");
|
||||
|
||||
// Keep going until the final `End` operator which pops the outermost block.
|
||||
while !state.control_stack.is_empty() {
|
||||
while !reader.eof() {
|
||||
let pos = reader.original_position();
|
||||
builder.set_srcloc(cur_srcloc(&reader));
|
||||
let op = reader.read_operator()?;
|
||||
validator.op(pos, &op)?;
|
||||
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)?;
|
||||
}
|
||||
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
|
||||
// instruction.
|
||||
@@ -261,8 +262,6 @@ fn parse_function_body<FE: FuncEnvironment + ?Sized>(
|
||||
// or the end of the function is unreachable.
|
||||
state.stack.clear();
|
||||
|
||||
debug_assert!(reader.eof());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -277,26 +276,27 @@ fn cur_srcloc(reader: &BinaryReader) -> ir::SourceLoc {
|
||||
mod tests {
|
||||
use super::{FuncTranslator, ReturnMode};
|
||||
use crate::environ::DummyEnvironment;
|
||||
use crate::ModuleTranslationState;
|
||||
use cranelift_codegen::ir::types::I32;
|
||||
use cranelift_codegen::{ir, isa, settings, Context};
|
||||
use log::debug;
|
||||
use target_lexicon::PointerWidth;
|
||||
use wasmparser::{
|
||||
FuncValidator, FunctionBody, Parser, ValidPayload, Validator, ValidatorResources,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn small1() {
|
||||
// Implicit return.
|
||||
//
|
||||
// (func $small1 (param i32) (result i32)
|
||||
// (i32.add (get_local 0) (i32.const 1))
|
||||
// )
|
||||
const BODY: [u8; 7] = [
|
||||
0x00, // local decl count
|
||||
0x20, 0x00, // get_local 0
|
||||
0x41, 0x01, // i32.const 1
|
||||
0x6a, // i32.add
|
||||
0x0b, // end
|
||||
];
|
||||
let wasm = wat::parse_str(
|
||||
"
|
||||
(module
|
||||
(func $small2 (param i32) (result i32)
|
||||
(i32.add (get_local 0) (i32.const 1))
|
||||
)
|
||||
)
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut trans = FuncTranslator::new();
|
||||
let flags = settings::Flags::new(settings::builder());
|
||||
@@ -309,21 +309,15 @@ mod tests {
|
||||
false,
|
||||
);
|
||||
|
||||
let module_translation_state = ModuleTranslationState::new();
|
||||
let mut ctx = Context::new();
|
||||
|
||||
ctx.func.name = ir::ExternalName::testcase("small1");
|
||||
ctx.func.signature.params.push(ir::AbiParam::new(I32));
|
||||
ctx.func.signature.returns.push(ir::AbiParam::new(I32));
|
||||
|
||||
let (body, mut validator) = extract_func(&wasm);
|
||||
trans
|
||||
.translate(
|
||||
&module_translation_state,
|
||||
&BODY,
|
||||
0,
|
||||
&mut ctx.func,
|
||||
&mut runtime.func_env(),
|
||||
)
|
||||
.translate_body(&mut validator, body, &mut ctx.func, &mut runtime.func_env())
|
||||
.unwrap();
|
||||
debug!("{}", ctx.func.display(None));
|
||||
ctx.verify(&flags).unwrap();
|
||||
@@ -332,18 +326,16 @@ mod tests {
|
||||
#[test]
|
||||
fn small2() {
|
||||
// Same as above, but with an explicit return instruction.
|
||||
//
|
||||
// (func $small2 (param i32) (result i32)
|
||||
// (return (i32.add (get_local 0) (i32.const 1)))
|
||||
// )
|
||||
const BODY: [u8; 8] = [
|
||||
0x00, // local decl count
|
||||
0x20, 0x00, // get_local 0
|
||||
0x41, 0x01, // i32.const 1
|
||||
0x6a, // i32.add
|
||||
0x0f, // return
|
||||
0x0b, // end
|
||||
];
|
||||
let wasm = wat::parse_str(
|
||||
"
|
||||
(module
|
||||
(func $small2 (param i32) (result i32)
|
||||
(return (i32.add (get_local 0) (i32.const 1)))
|
||||
)
|
||||
)
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut trans = FuncTranslator::new();
|
||||
let flags = settings::Flags::new(settings::builder());
|
||||
@@ -356,21 +348,15 @@ mod tests {
|
||||
false,
|
||||
);
|
||||
|
||||
let module_translation_state = ModuleTranslationState::new();
|
||||
let mut ctx = Context::new();
|
||||
|
||||
ctx.func.name = ir::ExternalName::testcase("small2");
|
||||
ctx.func.signature.params.push(ir::AbiParam::new(I32));
|
||||
ctx.func.signature.returns.push(ir::AbiParam::new(I32));
|
||||
|
||||
let (body, mut validator) = extract_func(&wasm);
|
||||
trans
|
||||
.translate(
|
||||
&module_translation_state,
|
||||
&BODY,
|
||||
0,
|
||||
&mut ctx.func,
|
||||
&mut runtime.func_env(),
|
||||
)
|
||||
.translate_body(&mut validator, body, &mut ctx.func, &mut runtime.func_env())
|
||||
.unwrap();
|
||||
debug!("{}", ctx.func.display(None));
|
||||
ctx.verify(&flags).unwrap();
|
||||
@@ -379,27 +365,21 @@ mod tests {
|
||||
#[test]
|
||||
fn infloop() {
|
||||
// An infinite loop, no return instructions.
|
||||
//
|
||||
// (func $infloop (result i32)
|
||||
// (local i32)
|
||||
// (loop (result i32)
|
||||
// (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
|
||||
0x6a, // i32.add
|
||||
0x21, 0x00, // set_local 0
|
||||
0x0c, 0x00, // br 0
|
||||
0x0b, // end
|
||||
0x0b, // end
|
||||
];
|
||||
let wasm = wat::parse_str(
|
||||
"
|
||||
(module
|
||||
(func $infloop (result i32)
|
||||
(local i32)
|
||||
(loop (result i32)
|
||||
(i32.add (get_local 0) (i32.const 1))
|
||||
(set_local 0)
|
||||
(br 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut trans = FuncTranslator::new();
|
||||
let flags = settings::Flags::new(settings::builder());
|
||||
@@ -412,22 +392,27 @@ mod tests {
|
||||
false,
|
||||
);
|
||||
|
||||
let module_translation_state = ModuleTranslationState::new();
|
||||
let mut ctx = Context::new();
|
||||
|
||||
ctx.func.name = ir::ExternalName::testcase("infloop");
|
||||
ctx.func.signature.returns.push(ir::AbiParam::new(I32));
|
||||
|
||||
let (body, mut validator) = extract_func(&wasm);
|
||||
trans
|
||||
.translate(
|
||||
&module_translation_state,
|
||||
&BODY,
|
||||
0,
|
||||
&mut ctx.func,
|
||||
&mut runtime.func_env(),
|
||||
)
|
||||
.translate_body(&mut validator, body, &mut ctx.func, &mut runtime.func_env())
|
||||
.unwrap();
|
||||
debug!("{}", ctx.func.display(None));
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,5 +72,10 @@ pub use crate::translation_utils::{
|
||||
};
|
||||
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.
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::sections_translator::{
|
||||
};
|
||||
use crate::state::ModuleTranslationState;
|
||||
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
|
||||
/// [`Function`](cranelift_codegen::ir::Function).
|
||||
@@ -18,75 +18,105 @@ pub fn translate_module<'data>(
|
||||
) -> WasmResult<ModuleTranslationState> {
|
||||
let _tt = timing::wasm_translate_module();
|
||||
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) {
|
||||
match payload? {
|
||||
Payload::Version { .. } | Payload::End => {}
|
||||
Payload::Version { num, range } => {
|
||||
validator.version(num, &range)?;
|
||||
}
|
||||
Payload::End => {
|
||||
validator.end()?;
|
||||
}
|
||||
|
||||
Payload::TypeSection(types) => {
|
||||
validator.type_section(&types)?;
|
||||
parse_type_section(types, &mut module_translation_state, environ)?;
|
||||
}
|
||||
|
||||
Payload::ImportSection(imports) => {
|
||||
validator.import_section(&imports)?;
|
||||
parse_import_section(imports, environ)?;
|
||||
}
|
||||
|
||||
Payload::FunctionSection(functions) => {
|
||||
validator.function_section(&functions)?;
|
||||
parse_function_section(functions, environ)?;
|
||||
}
|
||||
|
||||
Payload::TableSection(tables) => {
|
||||
validator.table_section(&tables)?;
|
||||
parse_table_section(tables, environ)?;
|
||||
}
|
||||
|
||||
Payload::MemorySection(memories) => {
|
||||
validator.memory_section(&memories)?;
|
||||
parse_memory_section(memories, environ)?;
|
||||
}
|
||||
|
||||
Payload::GlobalSection(globals) => {
|
||||
validator.global_section(&globals)?;
|
||||
parse_global_section(globals, environ)?;
|
||||
}
|
||||
|
||||
Payload::ExportSection(exports) => {
|
||||
validator.export_section(&exports)?;
|
||||
parse_export_section(exports, environ)?;
|
||||
}
|
||||
|
||||
Payload::StartSection { func, .. } => {
|
||||
Payload::StartSection { func, range } => {
|
||||
validator.start_section(func, &range)?;
|
||||
parse_start_section(func, environ)?;
|
||||
}
|
||||
|
||||
Payload::ElementSection(elements) => {
|
||||
validator.element_section(&elements)?;
|
||||
parse_element_section(elements, environ)?;
|
||||
}
|
||||
|
||||
Payload::CodeSectionStart { count, range, .. } => {
|
||||
validator.code_section_start(count, &range)?;
|
||||
environ.reserve_function_bodies(count, range.start as u64);
|
||||
}
|
||||
|
||||
Payload::CodeSectionEntry(code) => {
|
||||
let mut code = code.get_binary_reader();
|
||||
let size = code.bytes_remaining();
|
||||
let offset = code.original_position();
|
||||
environ.define_function_body(
|
||||
&module_translation_state,
|
||||
code.read_bytes(size)?,
|
||||
offset,
|
||||
)?;
|
||||
Payload::CodeSectionEntry(body) => {
|
||||
let func_validator = validator.code_section_entry()?;
|
||||
environ.define_function_body(func_validator, body)?;
|
||||
}
|
||||
|
||||
Payload::DataSection(data) => {
|
||||
validator.data_section(&data)?;
|
||||
parse_data_section(data, environ)?;
|
||||
}
|
||||
|
||||
Payload::DataCountSection { count, .. } => {
|
||||
Payload::DataCountSection { count, range } => {
|
||||
validator.data_count_section(count, &range)?;
|
||||
environ.reserve_passive_data(count)?;
|
||||
}
|
||||
|
||||
Payload::ModuleSection(_)
|
||||
| Payload::InstanceSection(_)
|
||||
| Payload::AliasSection(_)
|
||||
| Payload::ModuleCodeSectionStart { .. }
|
||||
| Payload::ModuleCodeSectionEntry { .. } => {
|
||||
Payload::ModuleSection(s) => {
|
||||
validator.module_section(&s)?;
|
||||
unimplemented!("module linking not implemented yet")
|
||||
}
|
||||
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")
|
||||
}
|
||||
|
||||
@@ -105,7 +135,10 @@ pub fn translate_module<'data>(
|
||||
|
||||
Payload::CustomSection { name, data, .. } => environ.custom_section(name, data)?,
|
||||
|
||||
Payload::UnknownSection { .. } => unreachable!(),
|
||||
Payload::UnknownSection { id, range, .. } => {
|
||||
validator.unknown_section(id, &range)?;
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ use wasmparser::{
|
||||
self, Data, DataKind, DataSectionReader, Element, ElementItem, ElementItems, ElementKind,
|
||||
ElementSectionReader, Export, ExportSectionReader, ExternalKind, FunctionSectionReader,
|
||||
GlobalSectionReader, GlobalType, ImportSectionEntryType, ImportSectionReader,
|
||||
MemorySectionReader, MemoryType, NameSectionReader, Naming, Operator, TableSectionReader, Type,
|
||||
MemorySectionReader, MemoryType, NameSectionReader, Naming, Operator, TableSectionReader,
|
||||
TypeDef, TypeSectionReader,
|
||||
};
|
||||
|
||||
@@ -88,7 +88,7 @@ pub fn parse_import_section<'data>(
|
||||
ImportSectionEntryType::Module(_sig) | ImportSectionEntryType::Instance(_sig) => {
|
||||
unimplemented!("module linking not implemented yet")
|
||||
}
|
||||
ImportSectionEntryType::Memory(MemoryType {
|
||||
ImportSectionEntryType::Memory(MemoryType::M32 {
|
||||
limits: ref memlimits,
|
||||
shared,
|
||||
}) => {
|
||||
@@ -102,6 +102,9 @@ pub fn parse_import_section<'data>(
|
||||
field_name,
|
||||
)?;
|
||||
}
|
||||
ImportSectionEntryType::Memory(MemoryType::M64 { .. }) => {
|
||||
unimplemented!();
|
||||
}
|
||||
ImportSectionEntryType::Global(ref ty) => {
|
||||
environ.declare_global_import(
|
||||
Global {
|
||||
@@ -189,11 +192,16 @@ pub fn parse_memory_section(
|
||||
|
||||
for entry in memories {
|
||||
let memory = entry?;
|
||||
environ.declare_memory(Memory {
|
||||
minimum: memory.limits.initial,
|
||||
maximum: memory.limits.maximum,
|
||||
shared: memory.shared,
|
||||
})?;
|
||||
match memory {
|
||||
MemoryType::M32 { limits, shared } => {
|
||||
environ.declare_memory(Memory {
|
||||
minimum: limits.initial,
|
||||
maximum: limits.maximum,
|
||||
shared: shared,
|
||||
})?;
|
||||
}
|
||||
MemoryType::M64 { .. } => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -313,13 +321,7 @@ pub fn parse_element_section<'data>(
|
||||
environ.reserve_table_elements(elements.get_count())?;
|
||||
|
||||
for (index, entry) in elements.into_iter().enumerate() {
|
||||
let Element { kind, items, ty } = entry?;
|
||||
if ty != Type::FuncRef {
|
||||
return Err(wasm_unsupported!(
|
||||
"unsupported table element type: {:?}",
|
||||
ty
|
||||
));
|
||||
}
|
||||
let Element { kind, items, ty: _ } = entry?;
|
||||
let segments = read_elems(&items)?;
|
||||
match kind {
|
||||
ElementKind::Active {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
//! Helper functions and structures for the translation.
|
||||
use crate::environ::{TargetEnvironment, WasmResult, WasmType};
|
||||
use crate::state::ModuleTranslationState;
|
||||
use crate::wasm_unsupported;
|
||||
use core::convert::TryInto;
|
||||
use core::u32;
|
||||
@@ -10,7 +9,7 @@ use cranelift_codegen::ir::immediates::V128Imm;
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
#[cfg(feature = "enable-serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
use wasmparser;
|
||||
use wasmparser::{FuncValidator, WasmFuncType, WasmModuleResources};
|
||||
|
||||
/// Index type of a function (imported or defined) inside the WebAssembly module.
|
||||
#[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.
|
||||
pub fn blocktype_params_results(
|
||||
module_translation_state: &ModuleTranslationState,
|
||||
pub fn blocktype_params_results<'a, T>(
|
||||
validator: &'a FuncValidator<T>,
|
||||
ty_or_ft: wasmparser::TypeOrFuncType,
|
||||
) -> WasmResult<(&[wasmparser::Type], &[wasmparser::Type])> {
|
||||
Ok(match ty_or_ft {
|
||||
wasmparser::TypeOrFuncType::Type(ty) => match ty {
|
||||
wasmparser::Type::I32 => (&[], &[wasmparser::Type::I32]),
|
||||
wasmparser::Type::I64 => (&[], &[wasmparser::Type::I64]),
|
||||
wasmparser::Type::F32 => (&[], &[wasmparser::Type::F32]),
|
||||
wasmparser::Type::F64 => (&[], &[wasmparser::Type::F64]),
|
||||
wasmparser::Type::V128 => (&[], &[wasmparser::Type::V128]),
|
||||
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)),
|
||||
},
|
||||
wasmparser::TypeOrFuncType::FuncType(ty_index) => {
|
||||
let sig_idx = SignatureIndex::from_u32(ty_index);
|
||||
let (ref params, ref returns) = module_translation_state.wasm_types[sig_idx];
|
||||
(&*params, &*returns)
|
||||
) -> WasmResult<(
|
||||
impl ExactSizeIterator<Item = wasmparser::Type> + Clone + 'a,
|
||||
impl ExactSizeIterator<Item = wasmparser::Type> + Clone + 'a,
|
||||
)>
|
||||
where
|
||||
T: WasmModuleResources,
|
||||
{
|
||||
return Ok(match ty_or_ft {
|
||||
wasmparser::TypeOrFuncType::Type(ty) => {
|
||||
let (params, results): (&'static [wasmparser::Type], &'static [wasmparser::Type]) =
|
||||
match ty {
|
||||
wasmparser::Type::I32 => (&[], &[wasmparser::Type::I32]),
|
||||
wasmparser::Type::I64 => (&[], &[wasmparser::Type::I64]),
|
||||
wasmparser::Type::F32 => (&[], &[wasmparser::Type::F32]),
|
||||
wasmparser::Type::F64 => (&[], &[wasmparser::Type::F64]),
|
||||
wasmparser::Type::V128 => (&[], &[wasmparser::Type::V128]),
|
||||
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.
|
||||
pub fn block_with_params<PE: TargetEnvironment + ?Sized>(
|
||||
builder: &mut FunctionBuilder,
|
||||
params: &[wasmparser::Type],
|
||||
params: impl IntoIterator<Item = wasmparser::Type>,
|
||||
environ: &PE,
|
||||
) -> WasmResult<ir::Block> {
|
||||
let block = builder.create_block();
|
||||
for ty in params.iter() {
|
||||
for ty in params {
|
||||
match ty {
|
||||
wasmparser::Type::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);
|
||||
}
|
||||
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 => {
|
||||
builder.append_block_param(block, ir::types::I8X16);
|
||||
|
||||
Reference in New Issue
Block a user