|
|
@ -10,6 +10,7 @@ use crate::environ::{ |
|
|
|
WasmFuncType, WasmResult, |
|
|
|
}; |
|
|
|
use crate::func_translator::FuncTranslator; |
|
|
|
use crate::state::FuncTranslationState; |
|
|
|
use crate::translation_utils::{ |
|
|
|
DataIndex, DefinedFuncIndex, ElemIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, |
|
|
|
Table, TableIndex, TypeIndex, |
|
|
@ -25,7 +26,7 @@ use cranelift_frontend::FunctionBuilder; |
|
|
|
use std::boxed::Box; |
|
|
|
use std::string::String; |
|
|
|
use std::vec::Vec; |
|
|
|
use wasmparser::{FuncValidator, FunctionBody, ValidatorResources, WasmFeatures}; |
|
|
|
use wasmparser::{FuncValidator, FunctionBody, Operator, ValidatorResources, WasmFeatures}; |
|
|
|
|
|
|
|
/// Compute a `ir::ExternalName` for a given wasm function index.
|
|
|
|
fn get_func_name(func_index: FuncIndex) -> ir::ExternalName { |
|
|
@ -111,6 +112,31 @@ impl DummyModuleInfo { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// State for tracking and checking reachability at each operator. Used for unit testing with the
|
|
|
|
/// `DummyEnvironment`.
|
|
|
|
#[derive(Clone)] |
|
|
|
pub struct ExpectedReachability { |
|
|
|
/// Before- and after-reachability
|
|
|
|
reachability: Vec<(bool, bool)>, |
|
|
|
before_idx: usize, |
|
|
|
after_idx: usize, |
|
|
|
} |
|
|
|
|
|
|
|
impl ExpectedReachability { |
|
|
|
fn check_before(&mut self, reachable: bool) { |
|
|
|
assert_eq!(reachable, self.reachability[self.before_idx].0); |
|
|
|
self.before_idx += 1; |
|
|
|
} |
|
|
|
fn check_after(&mut self, reachable: bool) { |
|
|
|
assert_eq!(reachable, self.reachability[self.after_idx].1); |
|
|
|
self.after_idx += 1; |
|
|
|
} |
|
|
|
fn check_end(&self) { |
|
|
|
assert_eq!(self.before_idx, self.reachability.len()); |
|
|
|
assert_eq!(self.after_idx, self.reachability.len()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// This `ModuleEnvironment` implementation is a "naïve" one, doing essentially nothing and
|
|
|
|
/// emitting placeholders when forced to. Don't try to execute code translated for this
|
|
|
|
/// environment, essentially here for translation debug purposes.
|
|
|
@ -135,6 +161,9 @@ pub struct DummyEnvironment { |
|
|
|
|
|
|
|
/// Function names.
|
|
|
|
function_names: SecondaryMap<FuncIndex, String>, |
|
|
|
|
|
|
|
/// Expected reachability data (before/after for each op) to assert. This is used for testing.
|
|
|
|
expected_reachability: Option<ExpectedReachability>, |
|
|
|
} |
|
|
|
|
|
|
|
impl DummyEnvironment { |
|
|
@ -148,13 +177,18 @@ impl DummyEnvironment { |
|
|
|
debug_info, |
|
|
|
module_name: None, |
|
|
|
function_names: SecondaryMap::new(), |
|
|
|
expected_reachability: None, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// Return a `DummyFuncEnvironment` for translating functions within this
|
|
|
|
/// `DummyEnvironment`.
|
|
|
|
pub fn func_env(&self) -> DummyFuncEnvironment { |
|
|
|
DummyFuncEnvironment::new(&self.info, self.return_mode) |
|
|
|
DummyFuncEnvironment::new( |
|
|
|
&self.info, |
|
|
|
self.return_mode, |
|
|
|
self.expected_reachability.clone(), |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
fn get_func_type(&self, func_index: FuncIndex) -> TypeIndex { |
|
|
@ -171,6 +205,17 @@ impl DummyEnvironment { |
|
|
|
pub fn get_func_name(&self, func_index: FuncIndex) -> Option<&str> { |
|
|
|
self.function_names.get(func_index).map(String::as_ref) |
|
|
|
} |
|
|
|
|
|
|
|
/// Test reachability bits before and after every opcode during translation, as provided by the
|
|
|
|
/// `FuncTranslationState`. This is generally used only for unit tests. This is applied to
|
|
|
|
/// every function in the module (so is likely only useful for test modules with one function).
|
|
|
|
pub fn test_expected_reachability(&mut self, reachability: Vec<(bool, bool)>) { |
|
|
|
self.expected_reachability = Some(ExpectedReachability { |
|
|
|
reachability, |
|
|
|
before_idx: 0, |
|
|
|
after_idx: 0, |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// The `FuncEnvironment` implementation for use by the `DummyEnvironment`.
|
|
|
@ -178,13 +223,21 @@ pub struct DummyFuncEnvironment<'dummy_environment> { |
|
|
|
pub mod_info: &'dummy_environment DummyModuleInfo, |
|
|
|
|
|
|
|
return_mode: ReturnMode, |
|
|
|
|
|
|
|
/// Expected reachability data (before/after for each op) to assert. This is used for testing.
|
|
|
|
expected_reachability: Option<ExpectedReachability>, |
|
|
|
} |
|
|
|
|
|
|
|
impl<'dummy_environment> DummyFuncEnvironment<'dummy_environment> { |
|
|
|
pub fn new(mod_info: &'dummy_environment DummyModuleInfo, return_mode: ReturnMode) -> Self { |
|
|
|
pub fn new( |
|
|
|
mod_info: &'dummy_environment DummyModuleInfo, |
|
|
|
return_mode: ReturnMode, |
|
|
|
expected_reachability: Option<ExpectedReachability>, |
|
|
|
) -> Self { |
|
|
|
Self { |
|
|
|
mod_info, |
|
|
|
return_mode, |
|
|
|
expected_reachability, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -307,6 +360,41 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ |
|
|
|
})) |
|
|
|
} |
|
|
|
|
|
|
|
fn before_translate_operator( |
|
|
|
&mut self, |
|
|
|
_op: &Operator, |
|
|
|
_builder: &mut FunctionBuilder, |
|
|
|
state: &FuncTranslationState, |
|
|
|
) -> WasmResult<()> { |
|
|
|
if let Some(ref mut r) = &mut self.expected_reachability { |
|
|
|
r.check_before(state.reachable()); |
|
|
|
} |
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
|
|
|
|
fn after_translate_operator( |
|
|
|
&mut self, |
|
|
|
_op: &Operator, |
|
|
|
_builder: &mut FunctionBuilder, |
|
|
|
state: &FuncTranslationState, |
|
|
|
) -> WasmResult<()> { |
|
|
|
if let Some(ref mut r) = &mut self.expected_reachability { |
|
|
|
r.check_after(state.reachable()); |
|
|
|
} |
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
|
|
|
|
fn after_translate_function( |
|
|
|
&mut self, |
|
|
|
_builder: &mut FunctionBuilder, |
|
|
|
_state: &FuncTranslationState, |
|
|
|
) -> WasmResult<()> { |
|
|
|
if let Some(ref mut r) = &mut self.expected_reachability { |
|
|
|
r.check_end(); |
|
|
|
} |
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
|
|
|
|
fn translate_call_indirect( |
|
|
|
&mut self, |
|
|
|
mut pos: FuncCursor, |
|
|
@ -746,7 +834,11 @@ impl<'data> ModuleEnvironment<'data> for DummyEnvironment { |
|
|
|
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 mut func_environ = DummyFuncEnvironment::new( |
|
|
|
&self.info, |
|
|
|
self.return_mode, |
|
|
|
self.expected_reachability.clone(), |
|
|
|
); |
|
|
|
let func_index = |
|
|
|
FuncIndex::new(self.get_num_func_imports() + self.info.function_bodies.len()); |
|
|
|
let name = get_func_name(func_index); |
|
|
|