moved crates in lib/ to src/, renamed crates, modified some files' text (#660)

moved crates in lib/ to src/, renamed crates, modified some files' text (#660)
This commit is contained in:
lazypassion
2019-01-28 18:56:54 -05:00
committed by Dan Gohman
parent 54959cf5bb
commit 747ad3c4c5
508 changed files with 94 additions and 92 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,477 @@
//! "Dummy" implementations of `ModuleEnvironment` and `FuncEnvironment` for testing
//! wasm translation. For complete implementations of `ModuleEnvironment` and
//! `FuncEnvironment`, see [wasmtime-environ] in [Wasmtime].
//!
//! [wasmtime-environ]: https://crates.io/crates/wasmtime-environ
//! [Wasmtime]: https://github.com/CraneStation/wasmtime
use crate::environ::{FuncEnvironment, GlobalVariable, ModuleEnvironment, ReturnMode, WasmResult};
use crate::func_translator::FuncTranslator;
use crate::translation_utils::{
DefinedFuncIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex, Table,
TableIndex,
};
use cast;
use cranelift_codegen::cursor::FuncCursor;
use cranelift_codegen::ir::immediates::{Offset32, Uimm64};
use cranelift_codegen::ir::types::*;
use cranelift_codegen::ir::{self, InstBuilder};
use cranelift_codegen::isa::TargetFrontendConfig;
use cranelift_entity::{EntityRef, PrimaryMap};
use std::boxed::Box;
use std::string::String;
use std::vec::Vec;
/// Compute a `ir::ExternalName` for a given wasm function index.
fn get_func_name(func_index: FuncIndex) -> ir::ExternalName {
ir::ExternalName::user(0, func_index.as_u32())
}
/// A collection of names under which a given entity is exported.
pub struct Exportable<T> {
/// A wasm entity.
pub entity: T,
/// Names under which the entity is exported.
pub export_names: Vec<String>,
}
impl<T> Exportable<T> {
pub fn new(entity: T) -> Self {
Self {
entity,
export_names: Vec::new(),
}
}
}
/// The main state belonging to a `DummyEnvironment`. This is split out from
/// `DummyEnvironment` to allow it to be borrowed separately from the
/// `FuncTranslator` field.
pub struct DummyModuleInfo {
/// Target description relevant to frontends producing Cranelift IR.
config: TargetFrontendConfig,
/// Signatures as provided by `declare_signature`.
pub signatures: PrimaryMap<SignatureIndex, ir::Signature>,
/// Module and field names of imported functions as provided by `declare_func_import`.
pub imported_funcs: Vec<(String, String)>,
/// Module and field names of imported globals as provided by `declare_global_import`.
pub imported_globals: Vec<(String, String)>,
/// Module and field names of imported tables as provided by `declare_table_import`.
pub imported_tables: Vec<(String, String)>,
/// Module and field names of imported memories as provided by `declare_memory_import`.
pub imported_memories: Vec<(String, String)>,
/// Functions, imported and local.
pub functions: PrimaryMap<FuncIndex, Exportable<SignatureIndex>>,
/// Function bodies.
pub function_bodies: PrimaryMap<DefinedFuncIndex, ir::Function>,
/// Tables as provided by `declare_table`.
pub tables: PrimaryMap<TableIndex, Exportable<Table>>,
/// Memories as provided by `declare_memory`.
pub memories: PrimaryMap<MemoryIndex, Exportable<Memory>>,
/// Globals as provided by `declare_global`.
pub globals: PrimaryMap<GlobalIndex, Exportable<Global>>,
/// The start function.
pub start_func: Option<FuncIndex>,
}
impl DummyModuleInfo {
/// Creates a new `DummyModuleInfo` instance.
pub fn new(config: TargetFrontendConfig) -> Self {
Self {
config,
signatures: PrimaryMap::new(),
imported_funcs: Vec::new(),
imported_globals: Vec::new(),
imported_tables: Vec::new(),
imported_memories: Vec::new(),
functions: PrimaryMap::new(),
function_bodies: PrimaryMap::new(),
tables: PrimaryMap::new(),
memories: PrimaryMap::new(),
globals: PrimaryMap::new(),
start_func: None,
}
}
}
/// 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.
pub struct DummyEnvironment {
/// Module information.
pub info: DummyModuleInfo,
/// Function translation.
trans: FuncTranslator,
/// Vector of wasm bytecode size for each function.
pub func_bytecode_sizes: Vec<usize>,
/// How to return from functions.
return_mode: ReturnMode,
}
impl DummyEnvironment {
/// Creates a new `DummyEnvironment` instance.
pub fn new(config: TargetFrontendConfig, return_mode: ReturnMode) -> Self {
Self {
info: DummyModuleInfo::new(config),
trans: FuncTranslator::new(),
func_bytecode_sizes: Vec::new(),
return_mode,
}
}
/// Return a `DummyFuncEnvironment` for translating functions within this
/// `DummyEnvironment`.
pub fn func_env(&self) -> DummyFuncEnvironment {
DummyFuncEnvironment::new(&self.info, self.return_mode)
}
fn get_func_type(&self, func_index: FuncIndex) -> SignatureIndex {
self.info.functions[func_index].entity
}
/// Return the number of imported functions within this `DummyEnvironment`.
pub fn get_num_func_imports(&self) -> usize {
self.info.imported_funcs.len()
}
}
/// The `FuncEnvironment` implementation for use by the `DummyEnvironment`.
pub struct DummyFuncEnvironment<'dummy_environment> {
pub mod_info: &'dummy_environment DummyModuleInfo,
return_mode: ReturnMode,
}
impl<'dummy_environment> DummyFuncEnvironment<'dummy_environment> {
pub fn new(mod_info: &'dummy_environment DummyModuleInfo, return_mode: ReturnMode) -> Self {
Self {
mod_info,
return_mode,
}
}
// Create a signature for `sigidx` amended with a `vmctx` argument after the standard wasm
// arguments.
fn vmctx_sig(&self, sigidx: SignatureIndex) -> ir::Signature {
let mut sig = self.mod_info.signatures[sigidx].clone();
sig.params.push(ir::AbiParam::special(
self.pointer_type(),
ir::ArgumentPurpose::VMContext,
));
sig
}
}
impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environment> {
fn target_config(&self) -> TargetFrontendConfig {
self.mod_info.config
}
fn make_global(&mut self, func: &mut ir::Function, index: GlobalIndex) -> GlobalVariable {
// Just create a dummy `vmctx` global.
let offset = cast::i32((index.index() * 8) + 8).unwrap().into();
let vmctx = func.create_global_value(ir::GlobalValueData::VMContext {});
GlobalVariable::Memory {
gv: vmctx,
offset,
ty: self.mod_info.globals[index].entity.ty,
}
}
fn make_heap(&mut self, func: &mut ir::Function, _index: MemoryIndex) -> ir::Heap {
// Create a static heap whose base address is stored at `vmctx+0`.
let addr = func.create_global_value(ir::GlobalValueData::VMContext);
let gv = func.create_global_value(ir::GlobalValueData::Load {
base: addr,
offset: Offset32::new(0),
global_type: self.pointer_type(),
readonly: true,
});
func.create_heap(ir::HeapData {
base: gv,
min_size: 0.into(),
offset_guard_size: 0x8000_0000.into(),
style: ir::HeapStyle::Static {
bound: 0x1_0000_0000.into(),
},
index_type: I32,
})
}
fn make_table(&mut self, func: &mut ir::Function, _index: TableIndex) -> ir::Table {
// Create a table whose base address is stored at `vmctx+0`.
let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
let base_gv = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: Offset32::new(0),
global_type: self.pointer_type(),
readonly: true, // when tables in wasm become "growable", revisit whether this can be readonly or not.
});
let bound_gv = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: Offset32::new(0),
global_type: I32,
readonly: true,
});
func.create_table(ir::TableData {
base_gv,
min_size: Uimm64::new(0),
bound_gv,
element_size: Uimm64::from(u64::from(self.pointer_bytes()) * 2),
index_type: I32,
})
}
fn make_indirect_sig(&mut self, func: &mut ir::Function, index: SignatureIndex) -> ir::SigRef {
// A real implementation would probably change the calling convention and add `vmctx` and
// signature index arguments.
func.import_signature(self.vmctx_sig(index))
}
fn make_direct_func(&mut self, func: &mut ir::Function, index: FuncIndex) -> ir::FuncRef {
let sigidx = self.mod_info.functions[index].entity;
// A real implementation would probably add a `vmctx` argument.
// And maybe attempt some signature de-duplication.
let signature = func.import_signature(self.vmctx_sig(sigidx));
let name = get_func_name(index);
func.import_function(ir::ExtFuncData {
name,
signature,
colocated: false,
})
}
fn translate_call_indirect(
&mut self,
mut pos: FuncCursor,
_table_index: TableIndex,
_table: ir::Table,
_sig_index: SignatureIndex,
sig_ref: ir::SigRef,
callee: ir::Value,
call_args: &[ir::Value],
) -> WasmResult<ir::Inst> {
// Pass the current function's vmctx parameter on to the callee.
let vmctx = pos
.func
.special_param(ir::ArgumentPurpose::VMContext)
.expect("Missing vmctx parameter");
// The `callee` value is an index into a table of function pointers.
// Apparently, that table is stored at absolute address 0 in this dummy environment.
// TODO: Generate bounds checking code.
let ptr = self.pointer_type();
let callee_offset = if ptr == I32 {
pos.ins().imul_imm(callee, 4)
} else {
let ext = pos.ins().uextend(I64, callee);
pos.ins().imul_imm(ext, 4)
};
let mflags = ir::MemFlags::trusted();
let func_ptr = pos.ins().load(ptr, mflags, callee_offset, 0);
// Build a value list for the indirect call instruction containing the callee, call_args,
// and the vmctx parameter.
let mut args = ir::ValueList::default();
args.push(func_ptr, &mut pos.func.dfg.value_lists);
args.extend(call_args.iter().cloned(), &mut pos.func.dfg.value_lists);
args.push(vmctx, &mut pos.func.dfg.value_lists);
Ok(pos
.ins()
.CallIndirect(ir::Opcode::CallIndirect, INVALID, sig_ref, args)
.0)
}
fn translate_call(
&mut self,
mut pos: FuncCursor,
_callee_index: FuncIndex,
callee: ir::FuncRef,
call_args: &[ir::Value],
) -> WasmResult<ir::Inst> {
// Pass the current function's vmctx parameter on to the callee.
let vmctx = pos
.func
.special_param(ir::ArgumentPurpose::VMContext)
.expect("Missing vmctx parameter");
// Build a value list for the call instruction containing the call_args and the vmctx
// parameter.
let mut args = ir::ValueList::default();
args.extend(call_args.iter().cloned(), &mut pos.func.dfg.value_lists);
args.push(vmctx, &mut pos.func.dfg.value_lists);
Ok(pos.ins().Call(ir::Opcode::Call, INVALID, callee, args).0)
}
fn translate_memory_grow(
&mut self,
mut pos: FuncCursor,
_index: MemoryIndex,
_heap: ir::Heap,
_val: ir::Value,
) -> WasmResult<ir::Value> {
Ok(pos.ins().iconst(I32, -1))
}
fn translate_memory_size(
&mut self,
mut pos: FuncCursor,
_index: MemoryIndex,
_heap: ir::Heap,
) -> WasmResult<ir::Value> {
Ok(pos.ins().iconst(I32, -1))
}
fn return_mode(&self) -> ReturnMode {
self.return_mode
}
}
impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
fn target_config(&self) -> TargetFrontendConfig {
self.info.config
}
fn declare_signature(&mut self, sig: ir::Signature) {
self.info.signatures.push(sig);
}
fn declare_func_import(
&mut self,
sig_index: SignatureIndex,
module: &'data str,
field: &'data str,
) {
assert_eq!(
self.info.functions.len(),
self.info.imported_funcs.len(),
"Imported functions must be declared first"
);
self.info.functions.push(Exportable::new(sig_index));
self.info
.imported_funcs
.push((String::from(module), String::from(field)));
}
fn declare_func_type(&mut self, sig_index: SignatureIndex) {
self.info.functions.push(Exportable::new(sig_index));
}
fn declare_global(&mut self, global: Global) {
self.info.globals.push(Exportable::new(global));
}
fn declare_global_import(&mut self, global: Global, module: &'data str, field: &'data str) {
self.info.globals.push(Exportable::new(global));
self.info
.imported_globals
.push((String::from(module), String::from(field)));
}
fn declare_table(&mut self, table: Table) {
self.info.tables.push(Exportable::new(table));
}
fn declare_table_import(&mut self, table: Table, module: &'data str, field: &'data str) {
self.info.tables.push(Exportable::new(table));
self.info
.imported_tables
.push((String::from(module), String::from(field)));
}
fn declare_table_elements(
&mut self,
_table_index: TableIndex,
_base: Option<GlobalIndex>,
_offset: usize,
_elements: Box<[FuncIndex]>,
) {
// We do nothing
}
fn declare_memory(&mut self, memory: Memory) {
self.info.memories.push(Exportable::new(memory));
}
fn declare_memory_import(&mut self, memory: Memory, module: &'data str, field: &'data str) {
self.info.memories.push(Exportable::new(memory));
self.info
.imported_memories
.push((String::from(module), String::from(field)));
}
fn declare_data_initialization(
&mut self,
_memory_index: MemoryIndex,
_base: Option<GlobalIndex>,
_offset: usize,
_data: &'data [u8],
) {
// We do nothing
}
fn declare_func_export(&mut self, func_index: FuncIndex, name: &'data str) {
self.info.functions[func_index]
.export_names
.push(String::from(name));
}
fn declare_table_export(&mut self, table_index: TableIndex, name: &'data str) {
self.info.tables[table_index]
.export_names
.push(String::from(name));
}
fn declare_memory_export(&mut self, memory_index: MemoryIndex, name: &'data str) {
self.info.memories[memory_index]
.export_names
.push(String::from(name));
}
fn declare_global_export(&mut self, global_index: GlobalIndex, name: &'data str) {
self.info.globals[global_index]
.export_names
.push(String::from(name));
}
fn declare_start_func(&mut self, func_index: FuncIndex) {
debug_assert!(self.info.start_func.is_none());
self.info.start_func = Some(func_index);
}
fn define_function_body(&mut self, body_bytes: &'data [u8]) -> WasmResult<()> {
let func = {
let mut func_environ = DummyFuncEnvironment::new(&self.info, self.return_mode);
let func_index =
FuncIndex::new(self.get_num_func_imports() + self.info.function_bodies.len());
let name = get_func_name(func_index);
let sig = func_environ.vmctx_sig(self.get_func_type(func_index));
let mut func = ir::Function::with_name_signature(name, sig);
self.trans
.translate(body_bytes, &mut func, &mut func_environ)?;
func
};
self.func_bytecode_sizes.push(body_bytes.len());
self.info.function_bodies.push(func);
Ok(())
}
}

View File

@@ -0,0 +1,9 @@
//! Support for configurable wasm translation.
mod dummy;
mod spec;
pub use crate::environ::dummy::DummyEnvironment;
pub use crate::environ::spec::{
FuncEnvironment, GlobalVariable, ModuleEnvironment, ReturnMode, WasmError, WasmResult,
};

View File

@@ -0,0 +1,360 @@
//! All the runtime support necessary for the wasm to cranelift translation is formalized by the
//! traits `FunctionEnvironment` and `ModuleEnvironment`.
//!
//! There are skeleton implementations of these traits in the `dummy` module, and complete
//! implementations in [Wasmtime].
//!
//! [Wasmtime]: https://github.com/CraneStation/wasmtime
use crate::translation_utils::{
FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex, Table, TableIndex,
};
use core::convert::From;
use cranelift_codegen::cursor::FuncCursor;
use cranelift_codegen::ir::immediates::Offset32;
use cranelift_codegen::ir::{self, InstBuilder};
use cranelift_codegen::isa::TargetFrontendConfig;
use failure_derive::Fail;
use std::boxed::Box;
use wasmparser::BinaryReaderError;
/// The value of a WebAssembly global variable.
#[derive(Clone, Copy)]
pub enum GlobalVariable {
/// This is a constant global with a value known at compile time.
Const(ir::Value),
/// This is a variable in memory that should be referenced through a `GlobalValue`.
Memory {
/// The address of the global variable storage.
gv: ir::GlobalValue,
/// An offset to add to the address.
offset: Offset32,
/// The global variable's type.
ty: ir::Type,
},
}
/// A WebAssembly translation error.
///
/// When a WebAssembly function can't be translated, one of these error codes will be returned
/// to describe the failure.
#[derive(Fail, Debug, PartialEq, Eq)]
pub enum WasmError {
/// The input WebAssembly code is invalid.
///
/// This error code is used by a WebAssembly translator when it encounters invalid WebAssembly
/// code. This should never happen for validated WebAssembly code.
#[fail(display = "Invalid input WebAssembly code at offset {}: {}", _1, _0)]
InvalidWebAssembly {
/// A string describing the validation error.
message: &'static str,
/// The bytecode offset where the error occurred.
offset: usize,
},
/// A feature used by the WebAssembly code is not supported by the embedding environment.
///
/// Embedding environments may have their own limitations and feature restrictions.
#[fail(display = "Unsupported feature: {}", _0)]
Unsupported(&'static str),
/// An implementation limit was exceeded.
///
/// Cranelift can compile very large and complicated functions, but the [implementation has
/// limits][limits] that cause compilation to fail when they are exceeded.
///
/// [limits]: https://cranelift.readthedocs.io/en/latest/ir.html#implementation-limits
#[fail(display = "Implementation limit exceeded")]
ImplLimitExceeded,
}
impl From<BinaryReaderError> for WasmError {
/// Convert from a `BinaryReaderError` to a `WasmError`.
fn from(e: BinaryReaderError) -> Self {
let BinaryReaderError { message, offset } = e;
WasmError::InvalidWebAssembly { message, offset }
}
}
/// A convenient alias for a `Result` that uses `WasmError` as the error type.
pub type WasmResult<T> = Result<T, WasmError>;
/// How to return from functions.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum ReturnMode {
/// Use normal return instructions as needed.
NormalReturns,
/// Use a single fallthrough return at the end of the function.
FallthroughReturn,
}
/// Environment affecting the translation of a single WebAssembly function.
///
/// A `FuncEnvironment` trait object is required to translate a WebAssembly function to Cranelift
/// IR. The function environment provides information about the WebAssembly module as well as the
/// runtime environment.
pub trait FuncEnvironment {
/// Get the information needed to produce Cranelift IR for the given target.
fn target_config(&self) -> TargetFrontendConfig;
/// Get the Cranelift integer type to use for native pointers.
///
/// This returns `I64` for 64-bit architectures and `I32` for 32-bit architectures.
fn pointer_type(&self) -> ir::Type {
ir::Type::int(u16::from(self.target_config().pointer_bits())).unwrap()
}
/// Get the size of a native pointer, in bytes.
fn pointer_bytes(&self) -> u8 {
self.target_config().pointer_bytes()
}
/// Set up the necessary preamble definitions in `func` to access the global variable
/// identified by `index`.
///
/// The index space covers both imported globals and globals defined by the module.
///
/// Return the global variable reference that should be used to access the global and the
/// WebAssembly type of the global.
fn make_global(&mut self, func: &mut ir::Function, index: GlobalIndex) -> GlobalVariable;
/// Set up the necessary preamble definitions in `func` to access the linear memory identified
/// by `index`.
///
/// The index space covers both imported and locally declared memories.
fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> ir::Heap;
/// Set up the necessary preamble definitions in `func` to access the table identified
/// by `index`.
///
/// The index space covers both imported and locally declared tables.
fn make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> ir::Table;
/// Set up a signature definition in the preamble of `func` that can be used for an indirect
/// call with signature `index`.
///
/// The signature may contain additional arguments needed for an indirect call, but the
/// arguments marked as `ArgumentPurpose::Normal` must correspond to the WebAssembly signature
/// arguments.
///
/// The signature will only be used for indirect calls, even if the module has direct function
/// calls with the same WebAssembly type.
fn make_indirect_sig(&mut self, func: &mut ir::Function, index: SignatureIndex) -> ir::SigRef;
/// Set up an external function definition in the preamble of `func` that can be used to
/// directly call the function `index`.
///
/// The index space covers both imported functions and functions defined in the current module.
///
/// The function's signature may contain additional arguments needed for a direct call, but the
/// arguments marked as `ArgumentPurpose::Normal` must correspond to the WebAssembly signature
/// arguments.
///
/// The function's signature will only be used for direct calls, even if the module has
/// indirect calls with the same WebAssembly type.
fn make_direct_func(&mut self, func: &mut ir::Function, index: FuncIndex) -> ir::FuncRef;
/// Translate a `call_indirect` WebAssembly instruction at `pos`.
///
/// Insert instructions at `pos` for an indirect call to the function `callee` in the table
/// `table_index` with WebAssembly signature `sig_index`. The `callee` value will have type
/// `i32`.
///
/// The signature `sig_ref` was previously created by `make_indirect_sig()`.
///
/// Return the call instruction whose results are the WebAssembly return values.
#[cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))]
fn translate_call_indirect(
&mut self,
pos: FuncCursor,
table_index: TableIndex,
table: ir::Table,
sig_index: SignatureIndex,
sig_ref: ir::SigRef,
callee: ir::Value,
call_args: &[ir::Value],
) -> WasmResult<ir::Inst>;
/// Translate a `call` WebAssembly instruction at `pos`.
///
/// Insert instructions at `pos` for a direct call to the function `callee_index`.
///
/// The function reference `callee` was previously created by `make_direct_func()`.
///
/// Return the call instruction whose results are the WebAssembly return values.
fn translate_call(
&mut self,
mut pos: FuncCursor,
_callee_index: FuncIndex,
callee: ir::FuncRef,
call_args: &[ir::Value],
) -> WasmResult<ir::Inst> {
Ok(pos.ins().call(callee, call_args))
}
/// Translate a `memory.grow` WebAssembly instruction.
///
/// The `index` provided identifies the linear memory to grow, and `heap` is the heap reference
/// returned by `make_heap` for the same index.
///
/// The `val` value is the requested memory size in pages.
///
/// Returns the old size (in pages) of the memory.
fn translate_memory_grow(
&mut self,
pos: FuncCursor,
index: MemoryIndex,
heap: ir::Heap,
val: ir::Value,
) -> WasmResult<ir::Value>;
/// Translates a `memory.size` WebAssembly instruction.
///
/// The `index` provided identifies the linear memory to query, and `heap` is the heap reference
/// returned by `make_heap` for the same index.
///
/// Returns the size in pages of the memory.
fn translate_memory_size(
&mut self,
pos: FuncCursor,
index: MemoryIndex,
heap: ir::Heap,
) -> WasmResult<ir::Value>;
/// Emit code at the beginning of every wasm loop.
///
/// This can be used to insert explicit interrupt or safepoint checking at
/// the beginnings of loops.
fn translate_loop_header(&mut self, _pos: FuncCursor) {
// By default, don't emit anything.
}
/// Should the code be structured to use a single `fallthrough_return` instruction at the end
/// of the function body, rather than `return` instructions as needed? This is used by VMs
/// to append custom epilogues.
fn return_mode(&self) -> ReturnMode {
ReturnMode::NormalReturns
}
}
/// An object satisfying the `ModuleEnvironment` trait can be passed as argument to the
/// [`translate_module`](fn.translate_module.html) function. These methods should not be called
/// by the user, they are only for `cranelift-wasm` internal use.
pub trait ModuleEnvironment<'data> {
/// Get the information needed to produce Cranelift IR for the current target.
fn target_config(&self) -> TargetFrontendConfig;
/// Provides the number of signatures up front. By default this does nothing, but
/// implementations can use this to preallocate memory if desired.
fn reserve_signatures(&mut self, _num: u32) {}
/// Declares a function signature to the environment.
fn declare_signature(&mut self, sig: ir::Signature);
/// Provides the number of imports up front. By default this does nothing, but
/// implementations can use this to preallocate memory if desired.
fn reserve_imports(&mut self, _num: u32) {}
/// Declares a function import to the environment.
fn declare_func_import(
&mut self,
sig_index: SignatureIndex,
module: &'data str,
field: &'data str,
);
/// Declares a table import to the environment.
fn declare_table_import(&mut self, table: Table, module: &'data str, field: &'data str);
/// Declares a memory import to the environment.
fn declare_memory_import(&mut self, memory: Memory, module: &'data str, field: &'data str);
/// Declares a global import to the environment.
fn declare_global_import(&mut self, global: Global, module: &'data str, field: &'data str);
/// Notifies the implementation that all imports have been declared.
fn finish_imports(&mut self) {}
/// Provides the number of defined functions up front. By default this does nothing, but
/// implementations can use this to preallocate memory if desired.
fn reserve_func_types(&mut self, _num: u32) {}
/// Declares the type (signature) of a local function in the module.
fn declare_func_type(&mut self, sig_index: SignatureIndex);
/// Provides the number of defined tables up front. By default this does nothing, but
/// implementations can use this to preallocate memory if desired.
fn reserve_tables(&mut self, _num: u32) {}
/// Declares a table to the environment.
fn declare_table(&mut self, table: Table);
/// Provides the number of defined memories up front. By default this does nothing, but
/// implementations can use this to preallocate memory if desired.
fn reserve_memories(&mut self, _num: u32) {}
/// Declares a memory to the environment
fn declare_memory(&mut self, memory: Memory);
/// Provides the number of defined globals up front. By default this does nothing, but
/// implementations can use this to preallocate memory if desired.
fn reserve_globals(&mut self, _num: u32) {}
/// Declares a global to the environment.
fn declare_global(&mut self, global: Global);
/// Provides the number of exports up front. By default this does nothing, but
/// implementations can use this to preallocate memory if desired.
fn reserve_exports(&mut self, _num: u32) {}
/// Declares a function export to the environment.
fn declare_func_export(&mut self, func_index: FuncIndex, name: &'data str);
/// Declares a table export to the environment.
fn declare_table_export(&mut self, table_index: TableIndex, name: &'data str);
/// Declares a memory export to the environment.
fn declare_memory_export(&mut self, memory_index: MemoryIndex, name: &'data str);
/// Declares a global export to the environment.
fn declare_global_export(&mut self, global_index: GlobalIndex, name: &'data str);
/// Notifies the implementation that all exports have been declared.
fn finish_exports(&mut self) {}
/// Declares the optional start function.
fn declare_start_func(&mut self, index: FuncIndex);
/// Provides the number of element initializers up front. By default this does nothing, but
/// implementations can use this to preallocate memory if desired.
fn reserve_table_elements(&mut self, _num: u32) {}
/// Fills a declared table with references to functions in the module.
fn declare_table_elements(
&mut self,
table_index: TableIndex,
base: Option<GlobalIndex>,
offset: usize,
elements: Box<[FuncIndex]>,
);
/// Provides the contents of a function body.
///
/// Note there's no `reserve_function_bodies` function because the number of
/// functions is already provided by `reserve_func_types`.
fn define_function_body(&mut self, body_bytes: &'data [u8]) -> WasmResult<()>;
/// Provides the number of data initializers up front. By default this does nothing, but
/// implementations can use this to preallocate memory if desired.
fn reserve_data_initializers(&mut self, _num: u32) {}
/// Fills a declared memory with bytes at module instantiation.
fn declare_data_initialization(
&mut self,
memory_index: MemoryIndex,
base: Option<GlobalIndex>,
offset: usize,
data: &'data [u8],
);
}

View File

@@ -0,0 +1,362 @@
//! Stand-alone WebAssembly to Cranelift IR translator.
//!
//! This module defines the `FuncTranslator` type which can translate a single WebAssembly
//! function to Cranelift IR guided by a `FuncEnvironment` which provides information about the
//! WebAssembly module and the runtime environment.
use crate::code_translator::translate_operator;
use crate::environ::{FuncEnvironment, ReturnMode, WasmResult};
use crate::state::TranslationState;
use cranelift_codegen::entity::EntityRef;
use cranelift_codegen::ir::{self, Ebb, InstBuilder};
use cranelift_codegen::timing;
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
use log::info;
use wasmparser::{self, BinaryReader};
/// WebAssembly to Cranelift IR function translator.
///
/// A `FuncTranslator` is used to translate a binary WebAssembly function into Cranelift IR guided
/// by a `FuncEnvironment` object. A single translator instance can be reused to translate multiple
/// functions which will reduce heap allocation traffic.
pub struct FuncTranslator {
func_ctx: FunctionBuilderContext,
state: TranslationState,
}
impl FuncTranslator {
/// Create a new translator.
pub fn new() -> Self {
Self {
func_ctx: FunctionBuilderContext::new(),
state: TranslationState::new(),
}
}
/// Translate a binary WebAssembly function.
///
/// The `code` slice contains the binary WebAssembly *function code* as it appears in the code
/// section of a WebAssembly module, not including the initial size of the function code. The
/// slice is expected to contain two parts:
///
/// - The declaration of *locals*, and
/// - The function *body* as an expression.
///
/// See [the WebAssembly specification][wasm].
///
/// [wasm]: https://webassembly.github.io/spec/binary/modules.html#code-section
///
/// The Cranelift IR function `func` should be completely empty except for the `func.signature`
/// and `func.name` fields. The signature may contain special-purpose arguments which are not
/// regarded as WebAssembly local variables. Any signature arguments marked as
/// `ArgumentPurpose::Normal` are made accessible as WebAssembly local variables.
///
pub fn translate<FE: FuncEnvironment + ?Sized>(
&mut self,
code: &[u8],
func: &mut ir::Function,
environ: &mut FE,
) -> WasmResult<()> {
self.translate_from_reader(BinaryReader::new(code), func, environ)
}
/// Translate a binary WebAssembly function from a `BinaryReader`.
pub fn translate_from_reader<FE: FuncEnvironment + ?Sized>(
&mut self,
mut reader: BinaryReader,
func: &mut ir::Function,
environ: &mut FE,
) -> WasmResult<()> {
let _tt = timing::wasm_translate_function();
info!(
"translate({} bytes, {}{})",
reader.bytes_remaining(),
func.name,
func.signature
);
debug_assert_eq!(func.dfg.num_ebbs(), 0, "Function must be empty");
debug_assert_eq!(func.dfg.num_insts(), 0, "Function must be empty");
// This clears the `FunctionBuilderContext`.
let mut builder = FunctionBuilder::new(func, &mut self.func_ctx);
let entry_block = builder.create_ebb();
builder.append_ebb_params_for_function_params(entry_block);
builder.switch_to_block(entry_block); // This also creates values for the arguments.
builder.seal_block(entry_block);
// Make sure the entry block is inserted in the layout before we make any callbacks to
// `environ`. The callback functions may need to insert things in the entry block.
builder.ensure_inserted_ebb();
let num_params = declare_wasm_parameters(&mut builder, entry_block);
// Set up the translation state with a single pushed control block representing the whole
// function and its return values.
let exit_block = builder.create_ebb();
builder.append_ebb_params_for_function_returns(exit_block);
self.state.initialize(&builder.func.signature, exit_block);
parse_local_decls(&mut reader, &mut builder, num_params)?;
parse_function_body(reader, &mut builder, &mut self.state, environ)?;
builder.finalize();
Ok(())
}
}
/// Declare local variables for the signature parameters that correspond to WebAssembly locals.
///
/// Return the number of local variables declared.
fn declare_wasm_parameters(builder: &mut FunctionBuilder, entry_block: Ebb) -> usize {
let sig_len = builder.func.signature.params.len();
let mut next_local = 0;
for i in 0..sig_len {
let param_type = builder.func.signature.params[i];
// There may be additional special-purpose parameters following the normal WebAssembly
// signature parameters. For example, a `vmctx` pointer.
if param_type.purpose == ir::ArgumentPurpose::Normal {
// This is a normal WebAssembly signature parameter, so create a local for it.
let local = Variable::new(next_local);
builder.declare_var(local, param_type.value_type);
next_local += 1;
let param_value = builder.ebb_params(entry_block)[i];
builder.def_var(local, param_value);
}
}
next_local
}
/// Parse the local variable declarations that precede the function body.
///
/// Declare local variables, starting from `num_params`.
fn parse_local_decls(
reader: &mut BinaryReader,
builder: &mut FunctionBuilder,
num_params: usize,
) -> WasmResult<()> {
let mut next_local = num_params;
let local_count = reader.read_local_count()?;
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)?;
declare_locals(builder, count, ty, &mut next_local);
}
Ok(())
}
/// Declare `count` local variables of the same type, starting from `next_local`.
///
/// Fail of too many locals are declared in the function, or if the type is not valid for a local.
fn declare_locals(
builder: &mut FunctionBuilder,
count: u32,
wasm_type: wasmparser::Type,
next_local: &mut usize,
) {
// All locals are initialized to 0.
use wasmparser::Type::*;
let zeroval = match wasm_type {
I32 => builder.ins().iconst(ir::types::I32, 0),
I64 => builder.ins().iconst(ir::types::I64, 0),
F32 => builder.ins().f32const(ir::immediates::Ieee32::with_bits(0)),
F64 => builder.ins().f64const(ir::immediates::Ieee64::with_bits(0)),
_ => panic!("invalid local type"),
};
let ty = builder.func.dfg.value_type(zeroval);
for _ in 0..count {
let local = Variable::new(*next_local);
builder.declare_var(local, ty);
builder.def_var(local, zeroval);
*next_local += 1;
}
}
/// Parse the function body in `reader`.
///
/// 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>(
mut reader: BinaryReader,
builder: &mut FunctionBuilder,
state: &mut TranslationState,
environ: &mut FE,
) -> WasmResult<()> {
// 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() {
builder.set_srcloc(cur_srcloc(&reader));
let op = reader.read_operator()?;
translate_operator(op, builder, state, environ)?;
}
// The final `End` operator left us in the exit block where we need to manually add a return
// instruction.
//
// If the exit block is unreachable, it may not have the correct arguments, so we would
// generate a return instruction that doesn't match the signature.
if state.reachable {
debug_assert!(builder.is_pristine());
if !builder.is_unreachable() {
match environ.return_mode() {
ReturnMode::NormalReturns => builder.ins().return_(&state.stack),
ReturnMode::FallthroughReturn => builder.ins().fallthrough_return(&state.stack),
};
}
}
// Discard any remaining values on the stack. Either we just returned them,
// or the end of the function is unreachable.
state.stack.clear();
debug_assert!(reader.eof());
Ok(())
}
/// Get the current source location from a reader.
fn cur_srcloc(reader: &BinaryReader) -> ir::SourceLoc {
// We record source locations as byte code offsets relative to the beginning of the function.
// This will wrap around of a single function's byte code is larger than 4 GB, but a) the
// WebAssembly format doesn't allow for that, and b) that would hit other Cranelift
// implementation limits anyway.
ir::SourceLoc::new(reader.current_position() as u32)
}
#[cfg(test)]
mod tests {
use super::{FuncTranslator, ReturnMode};
use crate::environ::DummyEnvironment;
use cranelift_codegen::ir::types::I32;
use cranelift_codegen::{ir, isa, settings, Context};
use log::debug;
use target_lexicon::PointerWidth;
#[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 mut trans = FuncTranslator::new();
let flags = settings::Flags::new(settings::builder());
let runtime = DummyEnvironment::new(
isa::TargetFrontendConfig {
default_call_conv: isa::CallConv::Fast,
pointer_width: PointerWidth::U64,
},
ReturnMode::NormalReturns,
);
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));
trans
.translate(&BODY, &mut ctx.func, &mut runtime.func_env())
.unwrap();
debug!("{}", ctx.func.display(None));
ctx.verify(&flags).unwrap();
}
#[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 mut trans = FuncTranslator::new();
let flags = settings::Flags::new(settings::builder());
let runtime = DummyEnvironment::new(
isa::TargetFrontendConfig {
default_call_conv: isa::CallConv::Fast,
pointer_width: PointerWidth::U64,
},
ReturnMode::NormalReturns,
);
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));
trans
.translate(&BODY, &mut ctx.func, &mut runtime.func_env())
.unwrap();
debug!("{}", ctx.func.display(None));
ctx.verify(&flags).unwrap();
}
#[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 mut trans = FuncTranslator::new();
let flags = settings::Flags::new(settings::builder());
let runtime = DummyEnvironment::new(
isa::TargetFrontendConfig {
default_call_conv: isa::CallConv::Fast,
pointer_width: PointerWidth::U64,
},
ReturnMode::NormalReturns,
);
let mut ctx = Context::new();
ctx.func.name = ir::ExternalName::testcase("infloop");
ctx.func.signature.returns.push(ir::AbiParam::new(I32));
trans
.translate(&BODY, &mut ctx.func, &mut runtime.func_env())
.unwrap();
debug!("{}", ctx.func.display(None));
ctx.verify(&flags).unwrap();
}
}

66
cranelift/wasm/src/lib.rs Normal file
View File

@@ -0,0 +1,66 @@
//! Performs translation from a wasm module in binary format to the in-memory form
//! of Cranelift IR. More particularly, it translates the code of all the functions bodies and
//! interacts with an environment implementing the
//! [`ModuleEnvironment`](trait.ModuleEnvironment.html)
//! trait to deal with tables, globals and linear memory.
//!
//! The crate provides a `DummyEnvironment` struct that will allow to translate the code of the
//! functions but will fail at execution.
//!
//! The main function of this module is [`translate_module`](fn.translate_module.html).
#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)]
#![warn(unused_import_braces)]
#![cfg_attr(feature = "std", deny(unstable_features))]
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))]
#![cfg_attr(
feature = "cargo-clippy",
warn(
clippy::float_arithmetic,
clippy::mut_mut,
clippy::nonminimal_bool,
clippy::option_map_unwrap_or,
clippy::option_map_unwrap_or_else,
clippy::print_stdout,
clippy::unicode_not_nfc,
clippy::use_self
)
)]
#![no_std]
#![cfg_attr(not(feature = "std"), feature(alloc))]
#[cfg(not(feature = "std"))]
#[macro_use]
extern crate alloc as std;
#[cfg(feature = "std")]
#[macro_use]
extern crate std;
#[cfg(not(feature = "std"))]
use hashmap_core::{map as hash_map, HashMap};
#[cfg(feature = "std")]
use std::collections::{hash_map, HashMap};
mod code_translator;
mod environ;
mod func_translator;
mod module_translator;
mod sections_translator;
mod state;
mod translation_utils;
pub use crate::environ::{
DummyEnvironment, FuncEnvironment, GlobalVariable, ModuleEnvironment, ReturnMode, WasmError,
WasmResult,
};
pub use crate::func_translator::FuncTranslator;
pub use crate::module_translator::translate_module;
pub use crate::translation_utils::{
DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex, Global,
GlobalIndex, GlobalInit, Memory, MemoryIndex, SignatureIndex, Table, TableElementType,
TableIndex,
};
/// Version number of this crate.
pub const VERSION: &str = env!("CARGO_PKG_VERSION");

View File

@@ -0,0 +1,143 @@
//! Translation skeleton that traverses the whole WebAssembly module and call helper functions
//! to deal with each part of it.
use crate::environ::{ModuleEnvironment, WasmResult};
use crate::sections_translator::{
parse_code_section, parse_data_section, parse_element_section, parse_export_section,
parse_function_section, parse_global_section, parse_import_section, parse_memory_section,
parse_start_section, parse_table_section, parse_type_section,
};
use cranelift_codegen::timing;
use wasmparser::{ModuleReader, SectionCode};
/// Translate a sequence of bytes forming a valid Wasm binary into a list of valid Cranelift IR
/// [`Function`](../codegen/ir/function/struct.Function.html).
pub fn translate_module<'data>(
data: &'data [u8],
environ: &mut ModuleEnvironment<'data>,
) -> WasmResult<()> {
let _tt = timing::wasm_translate_module();
let mut reader = ModuleReader::new(data)?;
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(());
}
let mut section = reader.read()?;
if let SectionCode::Type = section.code {
let types = section.get_type_section_reader()?;
parse_type_section(types, environ)?;
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(());
}
section = reader.read()?;
}
if let SectionCode::Import = section.code {
let imports = section.get_import_section_reader()?;
parse_import_section(imports, environ)?;
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(());
}
section = reader.read()?;
}
if let SectionCode::Function = section.code {
let functions = section.get_function_section_reader()?;
parse_function_section(functions, environ)?;
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(());
}
section = reader.read()?;
}
if let SectionCode::Table = section.code {
let tables = section.get_table_section_reader()?;
parse_table_section(tables, environ)?;
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(());
}
section = reader.read()?;
}
if let SectionCode::Memory = section.code {
let memories = section.get_memory_section_reader()?;
parse_memory_section(memories, environ)?;
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(());
}
section = reader.read()?;
}
if let SectionCode::Global = section.code {
let globals = section.get_global_section_reader()?;
parse_global_section(globals, environ)?;
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(());
}
section = reader.read()?;
}
if let SectionCode::Export = section.code {
let exports = section.get_export_section_reader()?;
parse_export_section(exports, environ)?;
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(());
}
section = reader.read()?;
}
if let SectionCode::Start = section.code {
let start = section.get_start_section_content()?;
parse_start_section(start, environ)?;
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(());
}
section = reader.read()?;
}
if let SectionCode::Element = section.code {
let elements = section.get_element_section_reader()?;
parse_element_section(elements, environ)?;
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(());
}
section = reader.read()?;
}
if let SectionCode::Code = section.code {
let code = section.get_code_section_reader()?;
parse_code_section(code, environ)?;
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(());
}
section = reader.read()?;
}
if let SectionCode::Data = section.code {
let data = section.get_data_section_reader()?;
parse_data_section(data, environ)?;
}
Ok(())
}

View File

@@ -0,0 +1,331 @@
//! Helper functions to gather information for each of the non-function sections of a
//! WebAssembly module.
//!
//! The code of theses helper function is straightforward since it is only about reading metadata
//! about linear memories, tables, globals, etc. and storing them for later use.
//!
//! The special case of the initialize expressions for table elements offsets or global variables
//! is handled, according to the semantics of WebAssembly, to only specific expressions that are
//! interpreted on the fly.
use crate::environ::{ModuleEnvironment, WasmResult};
use crate::translation_utils::{
type_to_type, FuncIndex, Global, GlobalIndex, GlobalInit, Memory, MemoryIndex, SignatureIndex,
Table, TableElementType, TableIndex,
};
use core::str::from_utf8;
use cranelift_codegen::ir::{self, AbiParam, Signature};
use cranelift_entity::EntityRef;
use std::vec::Vec;
use wasmparser::{
self, CodeSectionReader, Data, DataSectionReader, Element, ElementSectionReader, Export,
ExportSectionReader, ExternalKind, FuncType, FunctionSectionReader, GlobalSectionReader,
GlobalType, ImportSectionEntryType, ImportSectionReader, MemorySectionReader, MemoryType,
Operator, TableSectionReader, TypeSectionReader,
};
/// Parses the Type section of the wasm module.
pub fn parse_type_section(
types: TypeSectionReader,
environ: &mut ModuleEnvironment,
) -> WasmResult<()> {
environ.reserve_signatures(types.get_count());
for entry in types {
match entry? {
FuncType {
form: wasmparser::Type::Func,
ref params,
ref returns,
} => {
let mut sig = Signature::new(environ.target_config().default_call_conv);
sig.params.extend(params.iter().map(|ty| {
let cret_arg: ir::Type = type_to_type(*ty)
.expect("only numeric types are supported in function signatures");
AbiParam::new(cret_arg)
}));
sig.returns.extend(returns.iter().map(|ty| {
let cret_arg: ir::Type = type_to_type(*ty)
.expect("only numeric types are supported in function signatures");
AbiParam::new(cret_arg)
}));
environ.declare_signature(sig);
}
ref s => panic!("unsupported type: {:?}", s),
}
}
Ok(())
}
/// Parses the Import section of the wasm module.
pub fn parse_import_section<'data>(
imports: ImportSectionReader<'data>,
environ: &mut ModuleEnvironment<'data>,
) -> WasmResult<()> {
environ.reserve_imports(imports.get_count());
for entry in imports {
let import = entry?;
// The input has already been validated, so we should be able to
// assume valid UTF-8 and use `from_utf8_unchecked` if performance
// becomes a concern here.
let module_name = from_utf8(import.module).unwrap();
let field_name = from_utf8(import.field).unwrap();
match import.ty {
ImportSectionEntryType::Function(sig) => {
environ.declare_func_import(SignatureIndex::from_u32(sig), module_name, field_name);
}
ImportSectionEntryType::Memory(MemoryType {
limits: ref memlimits,
shared,
}) => {
environ.declare_memory_import(
Memory {
minimum: memlimits.initial,
maximum: memlimits.maximum,
shared,
},
module_name,
field_name,
);
}
ImportSectionEntryType::Global(ref ty) => {
environ.declare_global_import(
Global {
ty: type_to_type(ty.content_type).unwrap(),
mutability: ty.mutable,
initializer: GlobalInit::Import,
},
module_name,
field_name,
);
}
ImportSectionEntryType::Table(ref tab) => {
environ.declare_table_import(
Table {
ty: match type_to_type(tab.element_type) {
Ok(t) => TableElementType::Val(t),
Err(()) => TableElementType::Func,
},
minimum: tab.limits.initial,
maximum: tab.limits.maximum,
},
module_name,
field_name,
);
}
}
}
environ.finish_imports();
Ok(())
}
/// Parses the Function section of the wasm module.
pub fn parse_function_section(
functions: FunctionSectionReader,
environ: &mut ModuleEnvironment,
) -> WasmResult<()> {
environ.reserve_func_types(functions.get_count());
for entry in functions {
let sigindex = entry?;
environ.declare_func_type(SignatureIndex::from_u32(sigindex));
}
Ok(())
}
/// Parses the Table section of the wasm module.
pub fn parse_table_section(
tables: TableSectionReader,
environ: &mut ModuleEnvironment,
) -> WasmResult<()> {
environ.reserve_tables(tables.get_count());
for entry in tables {
let table = entry?;
environ.declare_table(Table {
ty: match type_to_type(table.element_type) {
Ok(t) => TableElementType::Val(t),
Err(()) => TableElementType::Func,
},
minimum: table.limits.initial,
maximum: table.limits.maximum,
});
}
Ok(())
}
/// Parses the Memory section of the wasm module.
pub fn parse_memory_section(
memories: MemorySectionReader,
environ: &mut ModuleEnvironment,
) -> WasmResult<()> {
environ.reserve_memories(memories.get_count());
for entry in memories {
let memory = entry?;
environ.declare_memory(Memory {
minimum: memory.limits.initial,
maximum: memory.limits.maximum,
shared: memory.shared,
});
}
Ok(())
}
/// Parses the Global section of the wasm module.
pub fn parse_global_section(
globals: GlobalSectionReader,
environ: &mut ModuleEnvironment,
) -> WasmResult<()> {
environ.reserve_globals(globals.get_count());
for entry in globals {
let wasmparser::Global {
ty: GlobalType {
content_type,
mutable,
},
init_expr,
} = entry?;
let mut init_expr_reader = init_expr.get_binary_reader();
let initializer = match init_expr_reader.read_operator()? {
Operator::I32Const { value } => GlobalInit::I32Const(value),
Operator::I64Const { value } => GlobalInit::I64Const(value),
Operator::F32Const { value } => GlobalInit::F32Const(value.bits()),
Operator::F64Const { value } => GlobalInit::F64Const(value.bits()),
Operator::GetGlobal { global_index } => {
GlobalInit::GetGlobal(GlobalIndex::from_u32(global_index))
}
ref s => panic!("unsupported init expr in global section: {:?}", s),
};
let global = Global {
ty: type_to_type(content_type).unwrap(),
mutability: mutable,
initializer,
};
environ.declare_global(global);
}
Ok(())
}
/// Parses the Export section of the wasm module.
pub fn parse_export_section<'data>(
exports: ExportSectionReader<'data>,
environ: &mut ModuleEnvironment<'data>,
) -> WasmResult<()> {
environ.reserve_exports(exports.get_count());
for entry in exports {
let Export {
field,
ref kind,
index,
} = entry?;
// The input has already been validated, so we should be able to
// assume valid UTF-8 and use `from_utf8_unchecked` if performance
// becomes a concern here.
let name = from_utf8(field).unwrap();
let index = index as usize;
match *kind {
ExternalKind::Function => environ.declare_func_export(FuncIndex::new(index), name),
ExternalKind::Table => environ.declare_table_export(TableIndex::new(index), name),
ExternalKind::Memory => environ.declare_memory_export(MemoryIndex::new(index), name),
ExternalKind::Global => environ.declare_global_export(GlobalIndex::new(index), name),
}
}
environ.finish_exports();
Ok(())
}
/// Parses the Start section of the wasm module.
pub fn parse_start_section(index: u32, environ: &mut ModuleEnvironment) -> WasmResult<()> {
environ.declare_start_func(FuncIndex::from_u32(index));
Ok(())
}
/// Parses the Element section of the wasm module.
pub fn parse_element_section<'data>(
elements: ElementSectionReader<'data>,
environ: &mut ModuleEnvironment,
) -> WasmResult<()> {
environ.reserve_table_elements(elements.get_count());
for entry in elements {
let Element {
table_index,
init_expr,
items,
} = entry?;
let mut init_expr_reader = init_expr.get_binary_reader();
let (base, offset) = match init_expr_reader.read_operator()? {
Operator::I32Const { value } => (None, value as u32 as usize),
Operator::GetGlobal { global_index } => (Some(GlobalIndex::from_u32(global_index)), 0),
ref s => panic!("unsupported init expr in element section: {:?}", s),
};
let items_reader = items.get_items_reader()?;
let mut elems = Vec::with_capacity(cast::usize(items_reader.get_count()));
for item in items_reader {
let x = item?;
elems.push(FuncIndex::from_u32(x));
}
environ.declare_table_elements(
TableIndex::from_u32(table_index),
base,
offset,
elems.into_boxed_slice(),
)
}
Ok(())
}
/// Parses the Code section of the wasm module.
pub fn parse_code_section<'data>(
code: CodeSectionReader<'data>,
environ: &mut ModuleEnvironment<'data>,
) -> WasmResult<()> {
for body in code {
let mut reader = body?.get_binary_reader();
let size = reader.bytes_remaining();
environ.define_function_body(reader.read_bytes(size)?)?;
}
Ok(())
}
/// Parses the Data section of the wasm module.
pub fn parse_data_section<'data>(
data: DataSectionReader<'data>,
environ: &mut ModuleEnvironment<'data>,
) -> WasmResult<()> {
environ.reserve_data_initializers(data.get_count());
for entry in data {
let Data {
memory_index,
init_expr,
data,
} = entry?;
let mut init_expr_reader = init_expr.get_binary_reader();
let (base, offset) = match init_expr_reader.read_operator()? {
Operator::I32Const { value } => (None, value as u32 as usize),
Operator::GetGlobal { global_index } => (Some(GlobalIndex::from_u32(global_index)), 0),
ref s => panic!("unsupported init expr in data section: {:?}", s),
};
environ.declare_data_initialization(
MemoryIndex::from_u32(memory_index),
base,
offset,
data,
);
}
Ok(())
}

369
cranelift/wasm/src/state.rs Normal file
View File

@@ -0,0 +1,369 @@
//! WebAssembly function translation state.
//!
//! The `TranslationState` struct defined in this module is used to keep track of the WebAssembly
//! value and control stacks during the translation of a single function.
use super::HashMap;
use crate::environ::{FuncEnvironment, GlobalVariable};
use crate::translation_utils::{FuncIndex, GlobalIndex, MemoryIndex, SignatureIndex, TableIndex};
use cranelift_codegen::ir::{self, Ebb, Inst, Value};
use std::vec::Vec;
/// A control stack frame can be an `if`, a `block` or a `loop`, each one having the following
/// fields:
///
/// - `destination`: reference to the `Ebb` that will hold the code after the control block;
/// - `num_return_values`: number of values returned by the control block;
/// - `original_stack_size`: size of the value stack at the beginning of the control block.
///
/// Moreover, the `if` frame has the `branch_inst` field that points to the `brz` instruction
/// separating the `true` and `false` branch. The `loop` frame has a `header` field that references
/// the `Ebb` that contains the beginning of the body of the loop.
#[derive(Debug)]
pub enum ControlStackFrame {
If {
destination: Ebb,
branch_inst: Inst,
num_return_values: usize,
original_stack_size: usize,
exit_is_branched_to: bool,
reachable_from_top: bool,
},
Block {
destination: Ebb,
num_return_values: usize,
original_stack_size: usize,
exit_is_branched_to: bool,
},
Loop {
destination: Ebb,
header: Ebb,
num_return_values: usize,
original_stack_size: usize,
},
}
/// Helper methods for the control stack objects.
impl ControlStackFrame {
pub fn num_return_values(&self) -> usize {
match *self {
ControlStackFrame::If {
num_return_values, ..
}
| ControlStackFrame::Block {
num_return_values, ..
}
| ControlStackFrame::Loop {
num_return_values, ..
} => num_return_values,
}
}
pub fn following_code(&self) -> Ebb {
match *self {
ControlStackFrame::If { destination, .. }
| ControlStackFrame::Block { destination, .. }
| ControlStackFrame::Loop { destination, .. } => destination,
}
}
pub fn br_destination(&self) -> Ebb {
match *self {
ControlStackFrame::If { destination, .. }
| ControlStackFrame::Block { destination, .. } => destination,
ControlStackFrame::Loop { header, .. } => header,
}
}
pub fn original_stack_size(&self) -> usize {
match *self {
ControlStackFrame::If {
original_stack_size,
..
}
| ControlStackFrame::Block {
original_stack_size,
..
}
| ControlStackFrame::Loop {
original_stack_size,
..
} => original_stack_size,
}
}
pub fn is_loop(&self) -> bool {
match *self {
ControlStackFrame::If { .. } | ControlStackFrame::Block { .. } => false,
ControlStackFrame::Loop { .. } => true,
}
}
pub fn exit_is_branched_to(&self) -> bool {
match *self {
ControlStackFrame::If {
exit_is_branched_to,
..
}
| ControlStackFrame::Block {
exit_is_branched_to,
..
} => exit_is_branched_to,
ControlStackFrame::Loop { .. } => false,
}
}
pub fn set_branched_to_exit(&mut self) {
match *self {
ControlStackFrame::If {
ref mut exit_is_branched_to,
..
}
| ControlStackFrame::Block {
ref mut exit_is_branched_to,
..
} => *exit_is_branched_to = true,
ControlStackFrame::Loop { .. } => {}
}
}
}
/// Contains information passed along during the translation and that records:
///
/// - The current value and control stacks.
/// - The depth of the two unreachable control blocks stacks, that are manipulated when translating
/// unreachable code;
pub struct TranslationState {
pub stack: Vec<Value>,
pub control_stack: Vec<ControlStackFrame>,
pub reachable: bool,
// Map of global variables that have already been created by `FuncEnvironment::make_global`.
globals: HashMap<GlobalIndex, GlobalVariable>,
// Map of heaps that have been created by `FuncEnvironment::make_heap`.
heaps: HashMap<MemoryIndex, ir::Heap>,
// Map of tables that have been created by `FuncEnvironment::make_table`.
tables: HashMap<TableIndex, ir::Table>,
// Map of indirect call signatures that have been created by
// `FuncEnvironment::make_indirect_sig()`.
// Stores both the signature reference and the number of WebAssembly arguments
signatures: HashMap<SignatureIndex, (ir::SigRef, usize)>,
// Imported and local functions that have been created by
// `FuncEnvironment::make_direct_func()`.
// Stores both the function reference and the number of WebAssembly arguments
functions: HashMap<FuncIndex, (ir::FuncRef, usize)>,
}
impl TranslationState {
pub fn new() -> Self {
Self {
stack: Vec::new(),
control_stack: Vec::new(),
reachable: true,
globals: HashMap::new(),
heaps: HashMap::new(),
tables: HashMap::new(),
signatures: HashMap::new(),
functions: HashMap::new(),
}
}
fn clear(&mut self) {
debug_assert!(self.stack.is_empty());
debug_assert!(self.control_stack.is_empty());
self.reachable = true;
self.globals.clear();
self.heaps.clear();
self.tables.clear();
self.signatures.clear();
self.functions.clear();
}
/// Initialize the state for compiling a function with the given signature.
///
/// This resets the state to containing only a single block representing the whole function.
/// The exit block is the last block in the function which will contain the return instruction.
pub fn initialize(&mut self, sig: &ir::Signature, exit_block: Ebb) {
self.clear();
self.push_block(
exit_block,
sig.returns
.iter()
.filter(|arg| arg.purpose == ir::ArgumentPurpose::Normal)
.count(),
);
}
/// Push a value.
pub fn push1(&mut self, val: Value) {
self.stack.push(val);
}
/// Push multiple values.
pub fn pushn(&mut self, vals: &[Value]) {
self.stack.extend_from_slice(vals);
}
/// Pop one value.
pub fn pop1(&mut self) -> Value {
self.stack.pop().unwrap()
}
/// Peek at the top of the stack without popping it.
pub fn peek1(&self) -> Value {
*self.stack.last().unwrap()
}
/// Pop two values. Return them in the order they were pushed.
pub fn pop2(&mut self) -> (Value, Value) {
let v2 = self.stack.pop().unwrap();
let v1 = self.stack.pop().unwrap();
(v1, v2)
}
/// Pop three values. Return them in the order they were pushed.
pub fn pop3(&mut self) -> (Value, Value, Value) {
let v3 = self.stack.pop().unwrap();
let v2 = self.stack.pop().unwrap();
let v1 = self.stack.pop().unwrap();
(v1, v2, v3)
}
/// Pop the top `n` values on the stack.
///
/// The popped values are not returned. Use `peekn` to look at them before popping.
pub fn popn(&mut self, n: usize) {
let new_len = self.stack.len() - n;
self.stack.truncate(new_len);
}
/// Peek at the top `n` values on the stack in the order they were pushed.
pub fn peekn(&self, n: usize) -> &[Value] {
&self.stack[self.stack.len() - n..]
}
// Push a block on the control stack.
pub fn push_block(&mut self, following_code: Ebb, num_result_types: usize) {
self.control_stack.push(ControlStackFrame::Block {
destination: following_code,
original_stack_size: self.stack.len(),
num_return_values: num_result_types,
exit_is_branched_to: false,
});
}
// Push a loop on the control stack.
pub fn push_loop(&mut self, header: Ebb, following_code: Ebb, num_result_types: usize) {
self.control_stack.push(ControlStackFrame::Loop {
header,
destination: following_code,
original_stack_size: self.stack.len(),
num_return_values: num_result_types,
});
}
// Push an if on the control stack.
pub fn push_if(&mut self, branch_inst: Inst, following_code: Ebb, num_result_types: usize) {
self.control_stack.push(ControlStackFrame::If {
branch_inst,
destination: following_code,
original_stack_size: self.stack.len(),
num_return_values: num_result_types,
exit_is_branched_to: false,
reachable_from_top: self.reachable,
});
}
}
/// Methods for handling entity references.
impl TranslationState {
/// Get the `GlobalVariable` reference that should be used to access the global variable
/// `index`. Create the reference if necessary.
/// Also return the WebAssembly type of the global.
pub fn get_global<FE: FuncEnvironment + ?Sized>(
&mut self,
func: &mut ir::Function,
index: u32,
environ: &mut FE,
) -> GlobalVariable {
let index = GlobalIndex::from_u32(index);
*self
.globals
.entry(index)
.or_insert_with(|| environ.make_global(func, index))
}
/// Get the `Heap` reference that should be used to access linear memory `index`.
/// Create the reference if necessary.
pub fn get_heap<FE: FuncEnvironment + ?Sized>(
&mut self,
func: &mut ir::Function,
index: u32,
environ: &mut FE,
) -> ir::Heap {
let index = MemoryIndex::from_u32(index);
*self
.heaps
.entry(index)
.or_insert_with(|| environ.make_heap(func, index))
}
/// Get the `Table` reference that should be used to access table `index`.
/// Create the reference if necessary.
pub fn get_table<FE: FuncEnvironment + ?Sized>(
&mut self,
func: &mut ir::Function,
index: u32,
environ: &mut FE,
) -> ir::Table {
let index = TableIndex::from_u32(index);
*self
.tables
.entry(index)
.or_insert_with(|| environ.make_table(func, index))
}
/// Get the `SigRef` reference that should be used to make an indirect call with signature
/// `index`. Also return the number of WebAssembly arguments in the signature.
///
/// Create the signature if necessary.
pub fn get_indirect_sig<FE: FuncEnvironment + ?Sized>(
&mut self,
func: &mut ir::Function,
index: u32,
environ: &mut FE,
) -> (ir::SigRef, usize) {
let index = SignatureIndex::from_u32(index);
*self.signatures.entry(index).or_insert_with(|| {
let sig = environ.make_indirect_sig(func, index);
(sig, normal_args(&func.dfg.signatures[sig]))
})
}
/// Get the `FuncRef` reference that should be used to make a direct call to function
/// `index`. Also return the number of WebAssembly arguments in the signature.
///
/// Create the function reference if necessary.
pub fn get_direct_func<FE: FuncEnvironment + ?Sized>(
&mut self,
func: &mut ir::Function,
index: u32,
environ: &mut FE,
) -> (ir::FuncRef, usize) {
let index = FuncIndex::from_u32(index);
*self.functions.entry(index).or_insert_with(|| {
let fref = environ.make_direct_func(func, index);
let sig = func.dfg.ext_funcs[fref].signature;
(fref, normal_args(&func.dfg.signatures[sig]))
})
}
}
/// Count the number of normal parameters in a signature.
/// Exclude special-purpose parameters that represent runtime stuff and not WebAssembly arguments.
fn normal_args(sig: &ir::Signature) -> usize {
sig.params
.iter()
.filter(|arg| arg.purpose == ir::ArgumentPurpose::Normal)
.count()
}

View File

@@ -0,0 +1,142 @@
//! Helper functions and structures for the translation.
use core::u32;
use cranelift_codegen::entity::entity_impl;
use cranelift_codegen::ir;
use wasmparser;
/// Index type of a function (imported or defined) inside the WebAssembly module.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct FuncIndex(u32);
entity_impl!(FuncIndex);
/// Index type of a defined function inside the WebAssembly module.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct DefinedFuncIndex(u32);
entity_impl!(DefinedFuncIndex);
/// Index type of a defined table inside the WebAssembly module.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct DefinedTableIndex(u32);
entity_impl!(DefinedTableIndex);
/// Index type of a defined memory inside the WebAssembly module.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct DefinedMemoryIndex(u32);
entity_impl!(DefinedMemoryIndex);
/// Index type of a defined global inside the WebAssembly module.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct DefinedGlobalIndex(u32);
entity_impl!(DefinedGlobalIndex);
/// Index type of a table (imported or defined) inside the WebAssembly module.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct TableIndex(u32);
entity_impl!(TableIndex);
/// Index type of a global variable (imported or defined) inside the WebAssembly module.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct GlobalIndex(u32);
entity_impl!(GlobalIndex);
/// Index type of a linear memory (imported or defined) inside the WebAssembly module.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct MemoryIndex(u32);
entity_impl!(MemoryIndex);
/// Index type of a signature (imported or defined) inside the WebAssembly module.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct SignatureIndex(u32);
entity_impl!(SignatureIndex);
/// WebAssembly global.
#[derive(Debug, Clone, Copy)]
pub struct Global {
/// The type of the value stored in the global.
pub ty: ir::Type,
/// A flag indicating whether the value may change at runtime.
pub mutability: bool,
/// The source of the initial value.
pub initializer: GlobalInit,
}
/// Globals are initialized via the four `const` operators or by referring to another import.
#[derive(Debug, Clone, Copy)]
pub enum GlobalInit {
/// An `i32.const`.
I32Const(i32),
/// An `i64.const`.
I64Const(i64),
/// An `f32.const`.
F32Const(u32),
/// An `f64.const`.
F64Const(u64),
/// A `get_global` of another global.
GetGlobal(GlobalIndex),
///< The global is imported from, and thus initialized by, a different module.
Import,
}
/// WebAssembly table.
#[derive(Debug, Clone, Copy)]
pub struct Table {
/// The type of data stored in elements of the table.
pub ty: TableElementType,
/// The minimum number of elements in the table.
pub minimum: u32,
/// The maximum number of elements in the table.
pub maximum: Option<u32>,
}
/// WebAssembly table element. Can be a function or a scalar type.
#[derive(Debug, Clone, Copy)]
pub enum TableElementType {
/// A scalar type.
Val(ir::Type),
/// A function.
Func,
}
/// WebAssembly linear memory.
#[derive(Debug, Clone, Copy)]
pub struct Memory {
/// The minimum number of pages in the memory.
pub minimum: u32,
/// The maximum number of pages in the memory.
pub maximum: Option<u32>,
/// Whether the memory may be shared between multiple threads.
pub shared: bool,
}
/// Helper function translating wasmparser types to Cranelift types when possible.
pub fn type_to_type(ty: wasmparser::Type) -> Result<ir::Type, ()> {
Ok(match ty {
wasmparser::Type::I32 => ir::types::I32,
wasmparser::Type::I64 => ir::types::I64,
wasmparser::Type::F32 => ir::types::F32,
wasmparser::Type::F64 => ir::types::F64,
_ => return Err(()),
})
}
/// Turns a `wasmparser` `f32` into a `Cranelift` one.
pub fn f32_translation(x: wasmparser::Ieee32) -> ir::immediates::Ieee32 {
ir::immediates::Ieee32::with_bits(x.bits())
}
/// Turns a `wasmparser` `f64` into a `Cranelift` one.
pub fn f64_translation(x: wasmparser::Ieee64) -> ir::immediates::Ieee64 {
ir::immediates::Ieee64::with_bits(x.bits())
}
/// Translate a `wasmparser` type into its `Cranelift` equivalent, when possible
pub fn num_return_values(ty: wasmparser::Type) -> usize {
match ty {
wasmparser::Type::EmptyBlockType => 0,
wasmparser::Type::I32
| wasmparser::Type::F32
| wasmparser::Type::I64
| wasmparser::Type::F64 => 1,
_ => panic!("unsupported return value type"),
}
}