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

35
cranelift/wasm/Cargo.toml Normal file
View File

@@ -0,0 +1,35 @@
[package]
name = "cranelift-wasm"
version = "0.28.0"
authors = ["The Cranelift Project Developers"]
description = "Translator from WebAssembly to Cranelift IR"
repository = "https://github.com/CraneStation/cranelift"
license = "Apache-2.0 WITH LLVM-exception"
categories = ["no-std", "wasm"]
readme = "README.md"
keywords = ["webassembly", "wasm"]
edition = "2018"
[dependencies]
wasmparser = { version = "0.23.0", default-features = false }
cranelift-codegen = { path = "../cranelift-codegen", version = "0.28.0", default-features = false }
cranelift-entity = { path = "../cranelift-entity", version = "0.28.0", default-features = false }
cranelift-frontend = { path = "../cranelift-frontend", version = "0.28.0", default-features = false }
hashmap_core = { version = "0.1.9", optional = true }
failure = { version = "0.1.1", default-features = false, features = ["derive"] }
failure_derive = { version = "0.1.1", default-features = false }
log = { version = "0.4.6", default-features = false }
cast = { version = "0.2.2", default-features = false }
[dev-dependencies]
wabt = "0.7.0"
target-lexicon = "0.2.0"
[features]
default = ["std"]
std = ["cranelift-codegen/std", "cranelift-frontend/std", "wasmparser/std"]
core = ["hashmap_core", "cranelift-codegen/core", "cranelift-frontend/core", "wasmparser/core"]
[badges]
maintenance = { status = "experimental" }
travis-ci = { repository = "CraneStation/cranelift" }

220
cranelift/wasm/LICENSE Normal file
View File

@@ -0,0 +1,220 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--- LLVM Exceptions to the Apache 2.0 License ----
As an exception, if, as a result of your compiling your source code, portions
of this Software are embedded into an Object form of such source code, you
may redistribute such embedded portions in such Object form without complying
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
In addition, if you combine or link compiled forms of this Software with
software that is licensed under the GPLv2 ("Combined Software") and if a
court of competent jurisdiction determines that the patent provision (Section
3), the indemnity provision (Section 9) or other Section of the License
conflicts with the conditions of the GPLv2, you may retroactively and
prospectively choose to deem waived or otherwise exclude such Section(s) of
the License, but only in their entirety and only with respect to the Combined
Software.

8
cranelift/wasm/README.md Normal file
View File

@@ -0,0 +1,8 @@
This crate performs the translation from a wasm module in binary format to the
in-memory form of the [Cranelift IR].
If you're looking for a complete WebAssembly implementation that uses this
library, see [Wasmtime].
[Wasmtime]: https://github.com/CraneStation/wasmtime
[Cranelift IR]: https://cranelift.readthedocs.io/en/latest/ir.html

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"),
}
}

View File

@@ -0,0 +1,85 @@
use cranelift_codegen::isa;
use cranelift_codegen::print_errors::pretty_verifier_error;
use cranelift_codegen::settings::{self, Flags};
use cranelift_codegen::verifier;
use cranelift_wasm::{translate_module, DummyEnvironment, ReturnMode};
use std::fs;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::path::Path;
use std::str::FromStr;
use target_lexicon::triple;
use wabt::wat2wasm;
#[test]
fn testsuite() {
let mut paths: Vec<_> = fs::read_dir("../wasmtests")
.unwrap()
.map(|r| r.unwrap())
.filter(|p| {
// Ignore files starting with `.`, which could be editor temporary files
if let Some(stem) = p.path().file_stem() {
if let Some(stemstr) = stem.to_str() {
return !stemstr.starts_with('.');
}
}
false
})
.collect();
paths.sort_by_key(|dir| dir.path());
let flags = Flags::new(settings::builder());
for path in paths {
let path = path.path();
handle_module(&path, &flags, ReturnMode::NormalReturns);
}
}
#[test]
fn use_fallthrough_return() {
let flags = Flags::new(settings::builder());
handle_module(
Path::new("../wasmtests/use_fallthrough_return.wat"),
&flags,
ReturnMode::FallthroughReturn,
);
}
fn read_file(path: &Path) -> io::Result<Vec<u8>> {
let mut buf: Vec<u8> = Vec::new();
let mut file = File::open(path)?;
file.read_to_end(&mut buf)?;
Ok(buf)
}
fn handle_module(path: &Path, flags: &Flags, return_mode: ReturnMode) {
let data = match path.extension() {
None => {
panic!("the file extension is not wasm or wat");
}
Some(ext) => match ext.to_str() {
Some("wasm") => read_file(path).expect("error reading wasm file"),
Some("wat") => {
let wat = read_file(path).expect("error reading wat file");
match wat2wasm(&wat) {
Ok(wasm) => wasm,
Err(e) => {
panic!("error converting wat to wasm: {:?}", e);
}
}
}
None | Some(&_) => panic!("the file extension for {:?} is not wasm or wat", path),
},
};
let triple = triple!("riscv64");
let isa = isa::lookup(triple).unwrap().finish(flags.clone());
let mut dummy_environ = DummyEnvironment::new(isa.frontend_config(), return_mode);
translate_module(&data, &mut dummy_environ).unwrap();
for func in dummy_environ.info.function_bodies.values() {
verifier::verify_function(func, &*isa)
.map_err(|errors| panic!(pretty_verifier_error(func, Some(&*isa), None, errors)))
.unwrap();
}
}