Refactor the compilation and instantiation pipeline.

wasmtime-execute is now wasmtime-jit. Move `JITCode` and the TargetIsa
into a new `Compiler` type. `InstancePlus` is no more, with trampoline
functionality now handled by `Compiler`.
This commit is contained in:
Dan Gohman
2019-01-03 06:59:46 -08:00
parent 450a279e18
commit 7592c99f3b
46 changed files with 1225 additions and 1073 deletions

View File

@@ -28,7 +28,7 @@ cranelift-entity = "0.26.0"
cranelift-wasm = "0.26.0" cranelift-wasm = "0.26.0"
wasmtime-environ = { path = "lib/environ" } wasmtime-environ = { path = "lib/environ" }
wasmtime-runtime = { path = "lib/runtime" } wasmtime-runtime = { path = "lib/runtime" }
wasmtime-execute = { path = "lib/execute" } wasmtime-jit = { path = "lib/jit" }
wasmtime-obj = { path = "lib/obj" } wasmtime-obj = { path = "lib/obj" }
wasmtime-wast = { path = "lib/wast" } wasmtime-wast = { path = "lib/wast" }
docopt = "1.0.1" docopt = "1.0.1"

View File

@@ -53,7 +53,10 @@ fn test_directory(out: &mut File, testsuite: &str) -> io::Result<()> {
.expect("testsuite filename should be representable as a string") .expect("testsuite filename should be representable as a string")
.replace("-", "_") .replace("-", "_")
)?; )?;
writeln!(out, " use super::{{native_isa, Path, WastContext}};")?; writeln!(
out,
" use super::{{native_isa, Path, WastContext, Compiler}};"
)?;
for dir_entry in dir_entries { for dir_entry in dir_entries {
write_testsuite_tests(out, dir_entry, testsuite)?; write_testsuite_tests(out, dir_entry, testsuite)?;
} }
@@ -78,8 +81,12 @@ fn write_testsuite_tests(out: &mut File, dir_entry: DirEntry, testsuite: &str) -
" fn {}() {{", " fn {}() {{",
avoid_keywords(&stemstr.replace("-", "_")) avoid_keywords(&stemstr.replace("-", "_"))
)?; )?;
writeln!(out, " let mut wast_context = WastContext::new();")?;
writeln!(out, " let isa = native_isa();")?; writeln!(out, " let isa = native_isa();")?;
writeln!(out, " let compiler = Compiler::new(isa);")?;
writeln!(
out,
" let mut wast_context = WastContext::new(Box::new(compiler));"
)?;
writeln!(out, " wast_context")?; writeln!(out, " wast_context")?;
writeln!(out, " .register_spectest()")?; writeln!(out, " .register_spectest()")?;
writeln!( writeln!(
@@ -87,7 +94,7 @@ fn write_testsuite_tests(out: &mut File, dir_entry: DirEntry, testsuite: &str) -
" .expect(\"instantiating \\\"spectest\\\"\");" " .expect(\"instantiating \\\"spectest\\\"\");"
)?; )?;
writeln!(out, " wast_context")?; writeln!(out, " wast_context")?;
write!(out, " .run_file(&*isa, Path::new(\"")?; write!(out, " .run_file(Path::new(\"")?;
// Write out the string with escape_debug to prevent special characters such // Write out the string with escape_debug to prevent special characters such
// as backslash from being reinterpreted. // as backslash from being reinterpreted.
for c in path.display().to_string().chars() { for c in path.display().to_string().chars() {

View File

@@ -9,7 +9,7 @@ cargo-fuzz = true
[dependencies] [dependencies]
wasmtime-environ = { path = "../lib/environ" } wasmtime-environ = { path = "../lib/environ" }
wasmtime-execute = { path = "../lib/execute" } wasmtime-jit = { path = "../lib/jit" }
cranelift-codegen = "0.26.0" cranelift-codegen = "0.26.0"
cranelift-wasm = "0.26.0" cranelift-wasm = "0.26.0"
cranelift-native = "0.26.0" cranelift-native = "0.26.0"

View File

@@ -6,7 +6,7 @@ extern crate cranelift_codegen;
extern crate cranelift_native; extern crate cranelift_native;
extern crate wasmparser; extern crate wasmparser;
extern crate wasmtime_environ; extern crate wasmtime_environ;
extern crate wasmtime_execute; extern crate wasmtime_jit;
use cranelift_codegen::settings; use cranelift_codegen::settings;
use wasmparser::validate; use wasmparser::validate;
@@ -28,9 +28,8 @@ fuzz_target!(|data: &[u8]| {
Err(_) => return, Err(_) => return,
}; };
let imports_resolver = |_env: &str, _function: &str| None; let imports_resolver = |_env: &str, _function: &str| None;
let _exec = let _exec = match wasmtime_jit::compile_and_link_module(&*isa, &translation, imports_resolver) {
match wasmtime_execute::compile_and_link_module(&*isa, &translation, imports_resolver) { Ok(x) => x,
Ok(x) => x, Err(_) => return,
Err(_) => return, };
};
}); });

View File

@@ -1,5 +1,6 @@
This is the `wasmtime-environ` crate, which contains the implementations This is the `wasmtime-environ` crate, which contains the implementations
of the `ModuleEnvironment` and `FuncEnvironment` traits from of the `ModuleEnvironment` and `FuncEnvironment` traits from
[`cranelift-wasm`](https://crates.io/crates/cranelift-wasm). They effectively [`cranelift-wasm`](https://crates.io/crates/cranelift-wasm). They effectively
implement an ABI for basic wasm compilation, which can be used for JITing, implement an ABI for basic wasm compilation that defines how linear memories
native object files, or other purposes. are allocated, how indirect calls work, and other details. They can be used
for JITing, native object files, or other purposes.

View File

@@ -3,16 +3,9 @@
use cranelift_codegen::binemit; use cranelift_codegen::binemit;
use cranelift_codegen::ir; use cranelift_codegen::ir;
use cranelift_codegen::ir::ExternalName; use cranelift_codegen::CodegenError;
use cranelift_codegen::isa;
use cranelift_codegen::{CodegenError, Context};
use cranelift_entity::PrimaryMap; use cranelift_entity::PrimaryMap;
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, FuncTranslator, WasmError}; use cranelift_wasm::{DefinedFuncIndex, FuncIndex, WasmError};
use func_environ::{
get_func_name, get_imported_memory32_grow_name, get_imported_memory32_size_name,
get_memory32_grow_name, get_memory32_size_name, FuncEnvironment,
};
use module::Module;
use std::vec::Vec; use std::vec::Vec;
/// The result of compiling a WebAssemby module's functions. /// The result of compiling a WebAssemby module's functions.
@@ -29,71 +22,6 @@ impl Compilation {
} }
} }
/// Implementation of a relocation sink that just saves all the information for later
pub struct RelocSink {
/// Relocations recorded for the function.
pub func_relocs: Vec<Relocation>,
}
impl binemit::RelocSink for RelocSink {
fn reloc_ebb(
&mut self,
_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_ebb_offset: binemit::CodeOffset,
) {
// This should use the `offsets` field of `ir::Function`.
panic!("ebb headers not yet implemented");
}
fn reloc_external(
&mut self,
offset: binemit::CodeOffset,
reloc: binemit::Reloc,
name: &ExternalName,
addend: binemit::Addend,
) {
let reloc_target = if *name == get_memory32_grow_name() {
RelocationTarget::Memory32Grow
} else if *name == get_imported_memory32_grow_name() {
RelocationTarget::ImportedMemory32Grow
} else if *name == get_memory32_size_name() {
RelocationTarget::Memory32Size
} else if *name == get_imported_memory32_size_name() {
RelocationTarget::ImportedMemory32Size
} else if let ExternalName::User { namespace, index } = *name {
debug_assert!(namespace == 0);
RelocationTarget::UserFunc(FuncIndex::from_u32(index))
} else if let ExternalName::LibCall(libcall) = *name {
RelocationTarget::LibCall(libcall)
} else {
panic!("unrecognized external name")
};
self.func_relocs.push(Relocation {
reloc,
reloc_target,
offset,
addend,
});
}
fn reloc_jt(
&mut self,
_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_jt: ir::JumpTable,
) {
panic!("jump tables not yet implemented");
}
}
impl RelocSink {
/// Return a new `RelocSink` instance.
pub fn new() -> Self {
Self {
func_relocs: Vec::new(),
}
}
}
/// A record of a relocation to perform. /// A record of a relocation to perform.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Relocation { pub struct Relocation {
@@ -127,44 +55,6 @@ pub enum RelocationTarget {
/// Relocations to apply to function bodies. /// Relocations to apply to function bodies.
pub type Relocations = PrimaryMap<DefinedFuncIndex, Vec<Relocation>>; pub type Relocations = PrimaryMap<DefinedFuncIndex, Vec<Relocation>>;
/// Compile the module, producing a compilation result with associated
/// relocations.
pub fn compile_module<'data, 'module>(
module: &'module Module,
function_body_inputs: &PrimaryMap<DefinedFuncIndex, &'data [u8]>,
isa: &isa::TargetIsa,
) -> Result<(Compilation, Relocations), CompileError> {
let mut functions = PrimaryMap::new();
let mut relocations = PrimaryMap::new();
for (i, input) in function_body_inputs.iter() {
let func_index = module.func_index(i);
let mut context = Context::new();
context.func.name = get_func_name(func_index);
context.func.signature = module.signatures[module.functions[func_index]].clone();
let mut trans = FuncTranslator::new();
trans
.translate(
input,
&mut context.func,
&mut FuncEnvironment::new(isa, module),
)
.map_err(CompileError::Wasm)?;
let mut code_buf: Vec<u8> = Vec::new();
let mut reloc_sink = RelocSink::new();
let mut trap_sink = binemit::NullTrapSink {};
context
.compile_and_emit(isa, &mut code_buf, &mut reloc_sink, &mut trap_sink)
.map_err(CompileError::Codegen)?;
functions.push(code_buf);
relocations.push(reloc_sink.func_relocs);
}
// TODO: Reorganize where we create the Vec for the resolved imports.
Ok((Compilation::new(functions), relocations))
}
/// An error while compiling WebAssembly to machine code. /// An error while compiling WebAssembly to machine code.
#[derive(Fail, Debug)] #[derive(Fail, Debug)]
pub enum CompileError { pub enum CompileError {

View File

@@ -0,0 +1,119 @@
//! Support for compiling with Cranelift.
use compilation::{Compilation, CompileError, Relocation, RelocationTarget, Relocations};
use cranelift_codegen::binemit;
use cranelift_codegen::ir;
use cranelift_codegen::ir::ExternalName;
use cranelift_codegen::isa;
use cranelift_codegen::Context;
use cranelift_entity::PrimaryMap;
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, FuncTranslator};
use func_environ::{
get_func_name, get_imported_memory32_grow_name, get_imported_memory32_size_name,
get_memory32_grow_name, get_memory32_size_name, FuncEnvironment,
};
use module::Module;
use std::vec::Vec;
/// Implementation of a relocation sink that just saves all the information for later
struct RelocSink {
/// Relocations recorded for the function.
func_relocs: Vec<Relocation>,
}
impl binemit::RelocSink for RelocSink {
fn reloc_ebb(
&mut self,
_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_ebb_offset: binemit::CodeOffset,
) {
// This should use the `offsets` field of `ir::Function`.
panic!("ebb headers not yet implemented");
}
fn reloc_external(
&mut self,
offset: binemit::CodeOffset,
reloc: binemit::Reloc,
name: &ExternalName,
addend: binemit::Addend,
) {
let reloc_target = if *name == get_memory32_grow_name() {
RelocationTarget::Memory32Grow
} else if *name == get_imported_memory32_grow_name() {
RelocationTarget::ImportedMemory32Grow
} else if *name == get_memory32_size_name() {
RelocationTarget::Memory32Size
} else if *name == get_imported_memory32_size_name() {
RelocationTarget::ImportedMemory32Size
} else if let ExternalName::User { namespace, index } = *name {
debug_assert!(namespace == 0);
RelocationTarget::UserFunc(FuncIndex::from_u32(index))
} else if let ExternalName::LibCall(libcall) = *name {
RelocationTarget::LibCall(libcall)
} else {
panic!("unrecognized external name")
};
self.func_relocs.push(Relocation {
reloc,
reloc_target,
offset,
addend,
});
}
fn reloc_jt(
&mut self,
_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_jt: ir::JumpTable,
) {
panic!("jump tables not yet implemented");
}
}
impl RelocSink {
/// Return a new `RelocSink` instance.
pub fn new() -> Self {
Self {
func_relocs: Vec::new(),
}
}
}
/// Compile the module using Cranelift, producing a compilation result with
/// associated relocations.
pub fn compile_module<'data, 'module>(
module: &'module Module,
function_body_inputs: PrimaryMap<DefinedFuncIndex, &'data [u8]>,
isa: &isa::TargetIsa,
) -> Result<(Compilation, Relocations), CompileError> {
let mut functions = PrimaryMap::new();
let mut relocations = PrimaryMap::new();
for (i, input) in function_body_inputs.into_iter() {
let func_index = module.func_index(i);
let mut context = Context::new();
context.func.name = get_func_name(func_index);
context.func.signature = module.signatures[module.functions[func_index]].clone();
let mut trans = FuncTranslator::new();
trans
.translate(
input,
&mut context.func,
&mut FuncEnvironment::new(isa.frontend_config(), module),
)
.map_err(CompileError::Wasm)?;
let mut code_buf: Vec<u8> = Vec::new();
let mut reloc_sink = RelocSink::new();
let mut trap_sink = binemit::NullTrapSink {};
context
.compile_and_emit(isa, &mut code_buf, &mut reloc_sink, &mut trap_sink)
.map_err(CompileError::Codegen)?;
functions.push(code_buf);
relocations.push(reloc_sink.func_relocs);
}
// TODO: Reorganize where we create the Vec for the resolved imports.
Ok((Compilation::new(functions), relocations))
}

View File

@@ -7,7 +7,7 @@ use cranelift_codegen::ir::types::*;
use cranelift_codegen::ir::{ use cranelift_codegen::ir::{
AbiParam, ArgumentPurpose, ExtFuncData, FuncRef, Function, InstBuilder, Signature, AbiParam, ArgumentPurpose, ExtFuncData, FuncRef, Function, InstBuilder, Signature,
}; };
use cranelift_codegen::isa; use cranelift_codegen::isa::TargetFrontendConfig;
use cranelift_entity::EntityRef; use cranelift_entity::EntityRef;
use cranelift_wasm::{ use cranelift_wasm::{
self, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex, GlobalIndex, self, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex, GlobalIndex,
@@ -50,8 +50,8 @@ pub fn get_imported_memory32_size_name() -> ir::ExternalName {
/// The FuncEnvironment implementation for use by the `ModuleEnvironment`. /// The FuncEnvironment implementation for use by the `ModuleEnvironment`.
pub struct FuncEnvironment<'module_environment> { pub struct FuncEnvironment<'module_environment> {
/// Compilation setting flags. /// Target-specified configuration.
isa: &'module_environment isa::TargetIsa, target_config: TargetFrontendConfig,
/// The module-level environment which this function-level environment belongs to. /// The module-level environment which this function-level environment belongs to.
module: &'module_environment Module, module: &'module_environment Module,
@@ -104,12 +104,9 @@ pub struct FuncEnvironment<'module_environment> {
} }
impl<'module_environment> FuncEnvironment<'module_environment> { impl<'module_environment> FuncEnvironment<'module_environment> {
pub fn new( pub fn new(target_config: TargetFrontendConfig, module: &'module_environment Module) -> Self {
isa: &'module_environment isa::TargetIsa,
module: &'module_environment Module,
) -> Self {
Self { Self {
isa, target_config,
module, module,
vmctx: None, vmctx: None,
imported_functions_base: None, imported_functions_base: None,
@@ -124,12 +121,12 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
imported_memory32_size_extfunc: None, imported_memory32_size_extfunc: None,
memory_grow_extfunc: None, memory_grow_extfunc: None,
imported_memory_grow_extfunc: None, imported_memory_grow_extfunc: None,
offsets: VMOffsets::new(isa.pointer_bytes()), offsets: VMOffsets::new(target_config.pointer_bytes()),
} }
} }
fn pointer_type(&self) -> ir::Type { fn pointer_type(&self) -> ir::Type {
self.isa.frontend_config().pointer_type() self.target_config.pointer_type()
} }
fn vmctx(&mut self, func: &mut Function) -> ir::GlobalValue { fn vmctx(&mut self, func: &mut Function) -> ir::GlobalValue {
@@ -253,7 +250,7 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext), AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
], ],
returns: vec![AbiParam::new(I32)], returns: vec![AbiParam::new(I32)],
call_conv: self.isa.frontend_config().default_call_conv, call_conv: self.target_config.default_call_conv,
}) })
} }
@@ -303,7 +300,7 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext), AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
], ],
returns: vec![AbiParam::new(I32)], returns: vec![AbiParam::new(I32)],
call_conv: self.isa.frontend_config().default_call_conv, call_conv: self.target_config.default_call_conv,
}) })
} }
@@ -348,8 +345,8 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
} }
impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'module_environment> { impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'module_environment> {
fn target_config(&self) -> isa::TargetFrontendConfig { fn target_config(&self) -> TargetFrontendConfig {
self.isa.frontend_config() self.target_config
} }
fn make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> ir::Table { fn make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> ir::Table {

View File

@@ -45,12 +45,13 @@ mod module_environ;
mod tunables; mod tunables;
mod vmoffsets; mod vmoffsets;
pub use compilation::{ pub mod cranelift;
compile_module, Compilation, CompileError, RelocSink, Relocation, RelocationTarget, Relocations,
}; pub use compilation::{Compilation, CompileError, Relocation, RelocationTarget, Relocations};
pub use module::{Export, MemoryPlan, MemoryStyle, Module, TableElements, TablePlan, TableStyle}; pub use module::{Export, MemoryPlan, MemoryStyle, Module, TableElements, TablePlan, TableStyle};
pub use module_environ::{ pub use module_environ::{
translate_signature, DataInitializer, ModuleEnvironment, ModuleTranslation, translate_signature, DataInitializer, DataInitializerLocation, ModuleEnvironment,
ModuleTranslation,
}; };
pub use tunables::Tunables; pub use tunables::Tunables;
pub use vmoffsets::VMOffsets; pub use vmoffsets::VMOffsets;

View File

@@ -6,7 +6,6 @@ use cranelift_wasm::{
DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex, Global, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex, Global,
GlobalIndex, Memory, MemoryIndex, SignatureIndex, Table, TableIndex, GlobalIndex, Memory, MemoryIndex, SignatureIndex, Table, TableIndex,
}; };
use std::cmp;
use std::collections::HashMap; use std::collections::HashMap;
use std::string::String; use std::string::String;
use std::vec::Vec; use std::vec::Vec;

View File

@@ -1,6 +1,6 @@
use cranelift_codegen::ir; use cranelift_codegen::ir;
use cranelift_codegen::ir::{AbiParam, ArgumentPurpose}; use cranelift_codegen::ir::{AbiParam, ArgumentPurpose};
use cranelift_codegen::isa; use cranelift_codegen::isa::TargetFrontendConfig;
use cranelift_entity::PrimaryMap; use cranelift_entity::PrimaryMap;
use cranelift_wasm::{ use cranelift_wasm::{
self, translate_module, DefinedFuncIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, self, translate_module, DefinedFuncIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex,
@@ -13,139 +13,149 @@ use std::string::String;
use std::vec::Vec; use std::vec::Vec;
use tunables::Tunables; use tunables::Tunables;
/// Object containing the standalone environment information. To be passed after creation as /// The result of translating via `ModuleEnvironment`. Function bodies are not
/// argument to `compile_module`. /// yet translated, and data initializers have not yet been copied out of the
pub struct ModuleEnvironment<'data, 'module> { /// original buffer.
pub struct ModuleTranslation<'data> {
/// Compilation setting flags. /// Compilation setting flags.
isa: &'module isa::TargetIsa, pub target_config: TargetFrontendConfig,
/// Module information. /// Module information.
module: &'module mut Module, pub module: Module,
/// References to information to be decoded later. /// References to the function bodies.
lazy: LazyContents<'data>, pub function_body_inputs: PrimaryMap<DefinedFuncIndex, &'data [u8]>,
/// References to the data initializers.
pub data_initializers: Vec<DataInitializer<'data>>,
/// Tunable parameters. /// Tunable parameters.
tunables: Tunables, pub tunables: Tunables,
} }
impl<'data, 'module> ModuleEnvironment<'data, 'module> { impl<'data> ModuleTranslation<'data> {
/// Allocates the enironment data structures with the given isa. /// Return a new `FuncEnvironment` for translating a function.
pub fn new( pub fn func_env(&self) -> FuncEnvironment {
isa: &'module isa::TargetIsa, FuncEnvironment::new(self.target_config, &self.module)
module: &'module mut Module, }
tunables: Tunables, }
) -> Self {
/// Object containing the standalone environment information.
pub struct ModuleEnvironment<'data> {
/// The result to be filled in.
result: ModuleTranslation<'data>,
}
impl<'data> ModuleEnvironment<'data> {
/// Allocates the enironment data structures.
pub fn new(target_config: TargetFrontendConfig, tunables: Tunables) -> Self {
Self { Self {
isa, result: ModuleTranslation {
module, target_config,
lazy: LazyContents::new(), module: Module::new(),
tunables, function_body_inputs: PrimaryMap::new(),
data_initializers: Vec::new(),
tunables,
},
} }
} }
fn pointer_type(&self) -> ir::Type { fn pointer_type(&self) -> ir::Type {
self.isa.frontend_config().pointer_type() self.result.target_config.pointer_type()
} }
/// Translate the given wasm module data using this environment. This consumes the /// Translate a wasm module using this environment. This consumes the
/// `ModuleEnvironment` with its mutable reference to the `Module` and produces a /// `ModuleEnvironment` and produces a `ModuleTranslation`.
/// `ModuleTranslation` with an immutable reference to the `Module` (which has pub fn translate(mut self, data: &'data [u8]) -> WasmResult<ModuleTranslation<'data>> {
/// become fully populated).
pub fn translate(mut self, data: &'data [u8]) -> WasmResult<ModuleTranslation<'data, 'module>> {
translate_module(data, &mut self)?; translate_module(data, &mut self)?;
Ok(ModuleTranslation { Ok(self.result)
isa: self.isa,
module: self.module,
lazy: self.lazy,
tunables: self.tunables,
})
} }
} }
/// This trait is useful for `translate_module` because it tells how to translate /// This trait is useful for `translate_module` because it tells how to translate
/// enironment-dependent wasm instructions. These functions should not be called by the user. /// enironment-dependent wasm instructions. These functions should not be called by the user.
impl<'data, 'module> cranelift_wasm::ModuleEnvironment<'data> impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data> {
for ModuleEnvironment<'data, 'module> fn target_config(&self) -> TargetFrontendConfig {
{ self.result.target_config
fn target_config(&self) -> isa::TargetFrontendConfig {
self.isa.frontend_config()
} }
fn declare_signature(&mut self, sig: &ir::Signature) { fn declare_signature(&mut self, sig: &ir::Signature) {
let sig = translate_signature(sig.clone(), self.pointer_type()); let sig = translate_signature(sig.clone(), self.pointer_type());
// TODO: Deduplicate signatures. // TODO: Deduplicate signatures.
self.module.signatures.push(sig); self.result.module.signatures.push(sig);
} }
fn get_signature(&self, sig_index: SignatureIndex) -> &ir::Signature { fn get_signature(&self, sig_index: SignatureIndex) -> &ir::Signature {
&self.module.signatures[sig_index] &self.result.module.signatures[sig_index]
} }
fn declare_func_import(&mut self, sig_index: SignatureIndex, module: &str, field: &str) { fn declare_func_import(&mut self, sig_index: SignatureIndex, module: &str, field: &str) {
debug_assert_eq!( debug_assert_eq!(
self.module.functions.len(), self.result.module.functions.len(),
self.module.imported_funcs.len(), self.result.module.imported_funcs.len(),
"Imported functions must be declared first" "Imported functions must be declared first"
); );
self.module.functions.push(sig_index); self.result.module.functions.push(sig_index);
self.module self.result
.module
.imported_funcs .imported_funcs
.push((String::from(module), String::from(field))); .push((String::from(module), String::from(field)));
} }
fn get_num_func_imports(&self) -> usize { fn get_num_func_imports(&self) -> usize {
self.module.imported_funcs.len() self.result.module.imported_funcs.len()
} }
fn declare_func_type(&mut self, sig_index: SignatureIndex) { fn declare_func_type(&mut self, sig_index: SignatureIndex) {
self.module.functions.push(sig_index); self.result.module.functions.push(sig_index);
} }
fn get_func_type(&self, func_index: FuncIndex) -> SignatureIndex { fn get_func_type(&self, func_index: FuncIndex) -> SignatureIndex {
self.module.functions[func_index] self.result.module.functions[func_index]
} }
fn declare_global_import(&mut self, global: Global, module: &str, field: &str) { fn declare_global_import(&mut self, global: Global, module: &str, field: &str) {
debug_assert_eq!( debug_assert_eq!(
self.module.globals.len(), self.result.module.globals.len(),
self.module.imported_globals.len(), self.result.module.imported_globals.len(),
"Imported globals must be declared first" "Imported globals must be declared first"
); );
self.module.globals.push(global); self.result.module.globals.push(global);
self.module self.result
.module
.imported_globals .imported_globals
.push((String::from(module), String::from(field))); .push((String::from(module), String::from(field)));
} }
fn declare_global(&mut self, global: Global) { fn declare_global(&mut self, global: Global) {
self.module.globals.push(global); self.result.module.globals.push(global);
} }
fn get_global(&self, global_index: GlobalIndex) -> &Global { fn get_global(&self, global_index: GlobalIndex) -> &Global {
&self.module.globals[global_index] &self.result.module.globals[global_index]
} }
fn declare_table_import(&mut self, table: Table, module: &str, field: &str) { fn declare_table_import(&mut self, table: Table, module: &str, field: &str) {
debug_assert_eq!( debug_assert_eq!(
self.module.table_plans.len(), self.result.module.table_plans.len(),
self.module.imported_tables.len(), self.result.module.imported_tables.len(),
"Imported tables must be declared first" "Imported tables must be declared first"
); );
let plan = TablePlan::for_table(table, &self.tunables); let plan = TablePlan::for_table(table, &self.result.tunables);
self.module.table_plans.push(plan); self.result.module.table_plans.push(plan);
self.module self.result
.module
.imported_tables .imported_tables
.push((String::from(module), String::from(field))); .push((String::from(module), String::from(field)));
} }
fn declare_table(&mut self, table: Table) { fn declare_table(&mut self, table: Table) {
let plan = TablePlan::for_table(table, &self.tunables); let plan = TablePlan::for_table(table, &self.result.tunables);
self.module.table_plans.push(plan); self.result.module.table_plans.push(plan);
} }
fn declare_table_elements( fn declare_table_elements(
@@ -155,7 +165,7 @@ impl<'data, 'module> cranelift_wasm::ModuleEnvironment<'data>
offset: usize, offset: usize,
elements: Vec<FuncIndex>, elements: Vec<FuncIndex>,
) { ) {
self.module.table_elements.push(TableElements { self.result.module.table_elements.push(TableElements {
table_index, table_index,
base, base,
offset, offset,
@@ -165,21 +175,22 @@ impl<'data, 'module> cranelift_wasm::ModuleEnvironment<'data>
fn declare_memory_import(&mut self, memory: Memory, module: &str, field: &str) { fn declare_memory_import(&mut self, memory: Memory, module: &str, field: &str) {
debug_assert_eq!( debug_assert_eq!(
self.module.memory_plans.len(), self.result.module.memory_plans.len(),
self.module.imported_memories.len(), self.result.module.imported_memories.len(),
"Imported memories must be declared first" "Imported memories must be declared first"
); );
let plan = MemoryPlan::for_memory(memory, &self.tunables); let plan = MemoryPlan::for_memory(memory, &self.result.tunables);
self.module.memory_plans.push(plan); self.result.module.memory_plans.push(plan);
self.module self.result
.module
.imported_memories .imported_memories
.push((String::from(module), String::from(field))); .push((String::from(module), String::from(field)));
} }
fn declare_memory(&mut self, memory: Memory) { fn declare_memory(&mut self, memory: Memory) {
let plan = MemoryPlan::for_memory(memory, &self.tunables); let plan = MemoryPlan::for_memory(memory, &self.result.tunables);
self.module.memory_plans.push(plan); self.result.module.memory_plans.push(plan);
} }
fn declare_data_initialization( fn declare_data_initialization(
@@ -189,71 +200,55 @@ impl<'data, 'module> cranelift_wasm::ModuleEnvironment<'data>
offset: usize, offset: usize,
data: &'data [u8], data: &'data [u8],
) { ) {
self.lazy.data_initializers.push(DataInitializer { self.result.data_initializers.push(DataInitializer {
memory_index, location: DataInitializerLocation {
base, memory_index,
offset, base,
offset,
},
data, data,
}); });
} }
fn declare_func_export(&mut self, func_index: FuncIndex, name: &str) { fn declare_func_export(&mut self, func_index: FuncIndex, name: &str) {
self.module self.result
.module
.exports .exports
.insert(String::from(name), Export::Function(func_index)); .insert(String::from(name), Export::Function(func_index));
} }
fn declare_table_export(&mut self, table_index: TableIndex, name: &str) { fn declare_table_export(&mut self, table_index: TableIndex, name: &str) {
self.module self.result
.module
.exports .exports
.insert(String::from(name), Export::Table(table_index)); .insert(String::from(name), Export::Table(table_index));
} }
fn declare_memory_export(&mut self, memory_index: MemoryIndex, name: &str) { fn declare_memory_export(&mut self, memory_index: MemoryIndex, name: &str) {
self.module self.result
.module
.exports .exports
.insert(String::from(name), Export::Memory(memory_index)); .insert(String::from(name), Export::Memory(memory_index));
} }
fn declare_global_export(&mut self, global_index: GlobalIndex, name: &str) { fn declare_global_export(&mut self, global_index: GlobalIndex, name: &str) {
self.module self.result
.module
.exports .exports
.insert(String::from(name), Export::Global(global_index)); .insert(String::from(name), Export::Global(global_index));
} }
fn declare_start_func(&mut self, func_index: FuncIndex) { fn declare_start_func(&mut self, func_index: FuncIndex) {
debug_assert!(self.module.start_func.is_none()); debug_assert!(self.result.module.start_func.is_none());
self.module.start_func = Some(func_index); self.result.module.start_func = Some(func_index);
} }
fn define_function_body(&mut self, body_bytes: &'data [u8]) -> WasmResult<()> { fn define_function_body(&mut self, body_bytes: &'data [u8]) -> WasmResult<()> {
self.lazy.function_body_inputs.push(body_bytes); self.result.function_body_inputs.push(body_bytes);
Ok(()) Ok(())
} }
} }
/// The result of translating via `ModuleEnvironment`.
pub struct ModuleTranslation<'data, 'module> {
/// Compilation setting flags.
pub isa: &'module isa::TargetIsa,
/// Module information.
pub module: &'module Module,
/// Pointers into the raw data buffer.
pub lazy: LazyContents<'data>,
/// Tunable parameters.
pub tunables: Tunables,
}
impl<'data, 'module> ModuleTranslation<'data, 'module> {
/// Return a new `FuncEnvironment` for translating a function.
pub fn func_env(&self) -> FuncEnvironment {
FuncEnvironment::new(self.isa, &self.module)
}
}
/// Add environment-specific function parameters. /// Add environment-specific function parameters.
pub fn translate_signature(mut sig: ir::Signature, pointer_type: ir::Type) -> ir::Signature { pub fn translate_signature(mut sig: ir::Signature, pointer_type: ir::Type) -> ir::Signature {
sig.params sig.params
@@ -261,33 +256,25 @@ pub fn translate_signature(mut sig: ir::Signature, pointer_type: ir::Type) -> ir
sig sig
} }
/// A data initializer for linear memory. /// A memory index and offset within that memory where a data initialization
pub struct DataInitializer<'data> { /// should is to be performed.
#[derive(Clone)]
pub struct DataInitializerLocation {
/// The index of the memory to initialize. /// The index of the memory to initialize.
pub memory_index: MemoryIndex, pub memory_index: MemoryIndex,
/// Optionally a globalvar base to initialize at. /// Optionally a globalvar base to initialize at.
pub base: Option<GlobalIndex>, pub base: Option<GlobalIndex>,
/// A constant offset to initialize at. /// A constant offset to initialize at.
pub offset: usize, pub offset: usize,
}
/// A data initializer for linear memory.
pub struct DataInitializer<'data> {
/// The location where the initialization is to be performed.
pub location: DataInitializerLocation,
/// The initialization data. /// The initialization data.
pub data: &'data [u8], pub data: &'data [u8],
} }
/// References to the input wasm data buffer to be decoded and processed later,
/// separately from the main module translation.
pub struct LazyContents<'data> {
/// References to the function bodies.
pub function_body_inputs: PrimaryMap<DefinedFuncIndex, &'data [u8]>,
/// References to the data initializers.
pub data_initializers: Vec<DataInitializer<'data>>,
}
impl<'data> LazyContents<'data> {
pub fn new() -> Self {
Self {
function_body_inputs: PrimaryMap::new(),
data_initializers: Vec::new(),
}
}
}

View File

@@ -1,4 +1,4 @@
//! Offsets and sizes of various structs in wasmtime-execute's vmcontext //! Offsets and sizes of various structs in wasmtime-runtime's vmcontext
//! module. //! module.
use cranelift_codegen::ir; use cranelift_codegen::ir;

View File

@@ -1,4 +0,0 @@
This is the `wasmtime-execute` crate, which contains wasm runtime support,
supporting the wasm ABI defined by [`wasmtime-environ`].
[`wasmtime-environ`]: https://crates.io/crates/wasmtime-environ

View File

@@ -1,142 +0,0 @@
//! Support for performing actions with a wasm module from the outside.
use cranelift_codegen::ir;
use link::LinkError;
use std::fmt;
use std::string::String;
use std::vec::Vec;
use wasmtime_environ::CompileError;
use wasmtime_runtime::InstantiationError;
/// A runtime value.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum RuntimeValue {
/// A runtime value with type i32.
I32(i32),
/// A runtime value with type i64.
I64(i64),
/// A runtime value with type f32.
F32(u32),
/// A runtime value with type f64.
F64(u64),
}
impl RuntimeValue {
/// Return the type of this `RuntimeValue`.
pub fn value_type(self) -> ir::Type {
match self {
RuntimeValue::I32(_) => ir::types::I32,
RuntimeValue::I64(_) => ir::types::I64,
RuntimeValue::F32(_) => ir::types::F32,
RuntimeValue::F64(_) => ir::types::F64,
}
}
/// Assuming this `RuntimeValue` holds an `i32`, return that value.
pub fn unwrap_i32(self) -> i32 {
match self {
RuntimeValue::I32(x) => x,
_ => panic!("unwrapping value of type {} as i32", self.value_type()),
}
}
/// Assuming this `RuntimeValue` holds an `i64`, return that value.
pub fn unwrap_i64(self) -> i64 {
match self {
RuntimeValue::I64(x) => x,
_ => panic!("unwrapping value of type {} as i64", self.value_type()),
}
}
/// Assuming this `RuntimeValue` holds an `f32`, return that value.
pub fn unwrap_f32(self) -> f32 {
f32::from_bits(self.unwrap_f32_bits())
}
/// Assuming this `RuntimeValue` holds an `f32`, return the bits of that value as a `u32`.
pub fn unwrap_f32_bits(self) -> u32 {
match self {
RuntimeValue::F32(x) => x,
_ => panic!("unwrapping value of type {} as f32", self.value_type()),
}
}
/// Assuming this `RuntimeValue` holds an `f64`, return that value.
pub fn unwrap_f64(self) -> f64 {
f64::from_bits(self.unwrap_f64_bits())
}
/// Assuming this `RuntimeValue` holds an `f64`, return the bits of that value as a `u64`.
pub fn unwrap_f64_bits(self) -> u64 {
match self {
RuntimeValue::F64(x) => x,
_ => panic!("unwrapping value of type {} as f64", self.value_type()),
}
}
}
impl fmt::Display for RuntimeValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
RuntimeValue::I32(x) => write!(f, "{}: i32", x),
RuntimeValue::I64(x) => write!(f, "{}: i64", x),
RuntimeValue::F32(x) => write!(f, "{}: f32", x),
RuntimeValue::F64(x) => write!(f, "{}: f64", x),
}
}
}
/// The result of invoking a wasm function or reading a wasm global.
#[derive(Debug)]
pub enum ActionOutcome {
/// The action returned normally. Its return values are provided.
Returned {
/// The return values.
values: Vec<RuntimeValue>,
},
/// A trap occurred while the action was executing.
Trapped {
/// The trap message.
message: String,
},
}
/// An error detected while invoking a wasm function or reading a wasm global.
/// Note that at this level, traps are not reported errors, but are rather
/// returned through `ActionOutcome`.
#[derive(Fail, Debug)]
pub enum ActionError {
/// No field with the specified name was present.
#[fail(display = "Unknown field: {}", _0)]
Field(String),
/// The field was present but was the wrong kind (eg. function, table, global, or memory).
#[fail(display = "Kind error: {}", _0)]
Kind(String),
/// The field was present but was the wrong type (eg. i32, i64, f32, or f64).
#[fail(display = "Type error: {}", _0)]
Type(String),
/// The module did not pass validation.
#[fail(display = "Validation error: {}", _0)]
Validate(String),
/// A wasm translation error occured.
#[fail(display = "WebAssembly compilation error: {}", _0)]
Compile(CompileError),
/// Some runtime resource was unavailable or insufficient, or the start function
/// trapped.
#[fail(display = "Instantiation error: {}", _0)]
Instantiate(InstantiationError),
/// Link error.
#[fail(display = "Link error: {}", _0)]
Link(LinkError),
/// Start function trapped.
#[fail(display = "Start function trapped: {}", _0)]
Start(String),
}

View File

@@ -1,288 +0,0 @@
use action::{ActionError, ActionOutcome, RuntimeValue};
use cranelift_codegen::{ir, isa};
use cranelift_entity::{BoxedSlice, PrimaryMap};
use cranelift_wasm::DefinedFuncIndex;
use jit_code::JITCode;
use link::link_module;
use resolver::Resolver;
use std::boxed::Box;
use std::cmp::max;
use std::rc::Rc;
use std::slice;
use std::string::String;
use std::vec::Vec;
use std::{mem, ptr};
use target_tunables::target_tunables;
use trampoline_park::TrampolinePark;
use wasmtime_environ::{
compile_module, Compilation, CompileError, DataInitializer, Module, ModuleEnvironment,
};
use wasmtime_runtime::{
wasmtime_call_trampoline, Export, Imports, Instance, InstantiationError, VMFunctionBody,
};
/// `InstancePlus` holds an `Instance` and adds support for performing actions
/// such as the "invoke" command in wast.
///
/// TODO: Think of a better name.
#[derive(Debug)]
pub struct InstancePlus {
/// The contained instance.
pub instance: Box<Instance>,
/// Trampolines for calling into JIT code.
trampolines: TrampolinePark,
}
impl InstancePlus {
/// Create a new `InstancePlus` by compiling the wasm module in `data` and instatiating it.
pub fn new(
jit_code: &mut JITCode,
isa: &isa::TargetIsa,
data: &[u8],
resolver: &mut Resolver,
) -> Result<Self, ActionError> {
let mut module = Module::new();
let tunables = target_tunables(isa.triple());
let (lazy_function_body_inputs, lazy_data_initializers) = {
let environ = ModuleEnvironment::new(isa, &mut module, tunables);
let translation = environ
.translate(&data)
.map_err(|error| ActionError::Compile(CompileError::Wasm(error)))?;
(
translation.lazy.function_body_inputs,
translation.lazy.data_initializers,
)
};
let (compilation, relocations) = compile_module(&module, &lazy_function_body_inputs, isa)
.map_err(ActionError::Compile)?;
let allocated_functions = allocate_functions(jit_code, compilation).map_err(|message| {
ActionError::Instantiate(InstantiationError::Resource(format!(
"failed to allocate memory for functions: {}",
message
)))
})?;
let imports = link_module(&module, &allocated_functions, relocations, resolver)
.map_err(ActionError::Link)?;
// Gather up the pointers to the compiled functions.
let finished_functions: BoxedSlice<DefinedFuncIndex, *const VMFunctionBody> =
allocated_functions
.into_iter()
.map(|(_index, allocated)| {
let fatptr: *const [VMFunctionBody] = *allocated;
fatptr as *const VMFunctionBody
})
.collect::<PrimaryMap<_, _>>()
.into_boxed_slice();
// Make all code compiled thus far executable.
jit_code.publish();
Self::with_parts(
Rc::new(module),
finished_functions,
imports,
lazy_data_initializers,
)
}
/// Construct a new `InstancePlus` from the parts needed to produce an `Instance`.
pub fn with_parts(
module: Rc<Module>,
finished_functions: BoxedSlice<DefinedFuncIndex, *const VMFunctionBody>,
imports: Imports,
data_initializers: Vec<DataInitializer>,
) -> Result<Self, ActionError> {
let instance = Instance::new(module, finished_functions, imports, data_initializers)
.map_err(ActionError::Instantiate)?;
Ok(Self::with_instance(instance))
}
/// Construct a new `InstancePlus` from an existing instance.
pub fn with_instance(instance: Box<Instance>) -> Self {
Self {
instance,
trampolines: TrampolinePark::new(),
}
}
/// Invoke a function in this `Instance` identified by an export name.
pub fn invoke(
&mut self,
jit_code: &mut JITCode,
isa: &isa::TargetIsa,
function_name: &str,
args: &[RuntimeValue],
) -> Result<ActionOutcome, ActionError> {
let (address, signature, callee_vmctx) = match self.instance.lookup(function_name) {
Some(Export::Function {
address,
signature,
vmctx,
}) => (address, signature, vmctx),
Some(_) => {
return Err(ActionError::Kind(format!(
"exported item \"{}\" is not a function",
function_name
)))
}
None => {
return Err(ActionError::Field(format!(
"no export named \"{}\"",
function_name
)))
}
};
for (index, value) in args.iter().enumerate() {
assert_eq!(value.value_type(), signature.params[index].value_type);
}
// TODO: Support values larger than u64. And pack the values into memory
// instead of just using fixed-sized slots.
let mut values_vec: Vec<u64> = Vec::new();
let value_size = mem::size_of::<u64>();
values_vec.resize(max(signature.params.len(), signature.returns.len()), 0u64);
// Store the argument values into `values_vec`.
for (index, arg) in args.iter().enumerate() {
unsafe {
let ptr = values_vec.as_mut_ptr().add(index);
match arg {
RuntimeValue::I32(x) => ptr::write(ptr as *mut i32, *x),
RuntimeValue::I64(x) => ptr::write(ptr as *mut i64, *x),
RuntimeValue::F32(x) => ptr::write(ptr as *mut u32, *x),
RuntimeValue::F64(x) => ptr::write(ptr as *mut u64, *x),
}
}
}
// Get the trampoline to call for this function.
let exec_code_buf = self
.trampolines
.get(jit_code, isa, address, &signature, value_size)?;
// Make all JIT code produced thus far executable.
jit_code.publish();
// Call the trampoline.
if let Err(message) = unsafe {
wasmtime_call_trampoline(
exec_code_buf,
values_vec.as_mut_ptr() as *mut u8,
callee_vmctx,
)
} {
return Ok(ActionOutcome::Trapped { message });
}
// Load the return values out of `values_vec`.
let values = signature
.returns
.iter()
.enumerate()
.map(|(index, abi_param)| unsafe {
let ptr = values_vec.as_ptr().add(index);
match abi_param.value_type {
ir::types::I32 => RuntimeValue::I32(ptr::read(ptr as *const i32)),
ir::types::I64 => RuntimeValue::I64(ptr::read(ptr as *const i64)),
ir::types::F32 => RuntimeValue::F32(ptr::read(ptr as *const u32)),
ir::types::F64 => RuntimeValue::F64(ptr::read(ptr as *const u64)),
other => panic!("unsupported value type {:?}", other),
}
})
.collect();
Ok(ActionOutcome::Returned { values })
}
/// Returns a slice of the contents of allocated linear memory.
pub fn inspect_memory(
&self,
memory_name: &str,
start: usize,
len: usize,
) -> Result<&[u8], ActionError> {
let definition = match unsafe { self.instance.lookup_immutable(memory_name) } {
Some(Export::Memory {
definition,
memory: _memory,
vmctx: _vmctx,
}) => definition,
Some(_) => {
return Err(ActionError::Kind(format!(
"exported item \"{}\" is not a linear memory",
memory_name
)))
}
None => {
return Err(ActionError::Field(format!(
"no export named \"{}\"",
memory_name
)))
}
};
Ok(unsafe {
let memory_def = &*definition;
&slice::from_raw_parts(memory_def.base, memory_def.current_length)[start..start + len]
})
}
/// Read a global in this `Instance` identified by an export name.
pub fn get(&self, global_name: &str) -> Result<RuntimeValue, ActionError> {
let (definition, global) = match unsafe { self.instance.lookup_immutable(global_name) } {
Some(Export::Global { definition, global }) => (definition, global),
Some(_) => {
return Err(ActionError::Kind(format!(
"exported item \"{}\" is not a global variable",
global_name
)))
}
None => {
return Err(ActionError::Field(format!(
"no export named \"{}\"",
global_name
)))
}
};
unsafe {
let global_def = &*definition;
Ok(match global.ty {
ir::types::I32 => RuntimeValue::I32(*global_def.as_i32()),
ir::types::I64 => RuntimeValue::I64(*global_def.as_i64()),
ir::types::F32 => RuntimeValue::F32(*global_def.as_f32_bits()),
ir::types::F64 => RuntimeValue::F64(*global_def.as_f64_bits()),
other => {
return Err(ActionError::Type(format!(
"global with type {} not supported",
other
)))
}
})
}
}
}
fn allocate_functions(
jit_code: &mut JITCode,
compilation: Compilation,
) -> Result<PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>, String> {
let mut result = PrimaryMap::with_capacity(compilation.functions.len());
for (_, body) in compilation.functions.into_iter() {
let fatptr: *mut [VMFunctionBody] = jit_code.allocate_copy_of_byte_slice(body)?;
result.push(fatptr);
}
Ok(result)
}

View File

@@ -1,153 +0,0 @@
use action::ActionError;
use cranelift_codegen::ir::InstBuilder;
use cranelift_codegen::Context;
use cranelift_codegen::{binemit, ir, isa};
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
use jit_code::JITCode;
use std::collections::HashMap;
use std::fmt;
use std::vec::Vec;
use wasmtime_environ::{CompileError, RelocSink};
use wasmtime_runtime::{InstantiationError, VMFunctionBody};
pub struct TrampolinePark {
/// Memoized per-function trampolines.
memoized: HashMap<*const VMFunctionBody, *const VMFunctionBody>,
/// The `FunctionBuilderContext`, shared between function compilations.
fn_builder_ctx: FunctionBuilderContext,
}
impl TrampolinePark {
pub fn new() -> Self {
Self {
memoized: HashMap::new(),
fn_builder_ctx: FunctionBuilderContext::new(),
}
}
pub fn get(
&mut self,
jit_code: &mut JITCode,
isa: &isa::TargetIsa,
callee_address: *const VMFunctionBody,
signature: &ir::Signature,
value_size: usize,
) -> Result<*const VMFunctionBody, ActionError> {
use std::collections::hash_map::Entry::{Occupied, Vacant};
Ok(match self.memoized.entry(callee_address) {
Occupied(entry) => *entry.get(),
Vacant(entry) => {
let body = make_trampoline(
&mut self.fn_builder_ctx,
jit_code,
isa,
callee_address,
signature,
value_size,
)?;
entry.insert(body);
body
}
})
}
}
impl fmt::Debug for TrampolinePark {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// The `fn_builder_ctx` field is just a cache and has no logical state.
write!(f, "{:?}", self.memoized)
}
}
fn make_trampoline(
fn_builder_ctx: &mut FunctionBuilderContext,
jit_code: &mut JITCode,
isa: &isa::TargetIsa,
callee_address: *const VMFunctionBody,
signature: &ir::Signature,
value_size: usize,
) -> Result<*const VMFunctionBody, ActionError> {
let pointer_type = isa.pointer_type();
let mut wrapper_sig = ir::Signature::new(isa.frontend_config().default_call_conv);
// Add the `values_vec` parameter.
wrapper_sig.params.push(ir::AbiParam::new(pointer_type));
// Add the `vmctx` parameter.
wrapper_sig.params.push(ir::AbiParam::special(
pointer_type,
ir::ArgumentPurpose::VMContext,
));
let mut context = Context::new();
context.func = ir::Function::with_name_signature(ir::ExternalName::user(0, 0), wrapper_sig);
{
let mut builder = FunctionBuilder::new(&mut context.func, fn_builder_ctx);
let block0 = builder.create_ebb();
builder.append_ebb_params_for_function_params(block0);
builder.switch_to_block(block0);
builder.seal_block(block0);
let mut callee_args = Vec::new();
let pointer_type = isa.pointer_type();
let (values_vec_ptr_val, vmctx_ptr_val) = {
let params = builder.func.dfg.ebb_params(block0);
(params[0], params[1])
};
// Load the argument values out of `values_vec`.
let mflags = ir::MemFlags::trusted();
for (i, r) in signature.params.iter().enumerate() {
let value = match r.purpose {
ir::ArgumentPurpose::Normal => builder.ins().load(
r.value_type,
mflags,
values_vec_ptr_val,
(i * value_size) as i32,
),
ir::ArgumentPurpose::VMContext => vmctx_ptr_val,
other => panic!("unsupported argument purpose {}", other),
};
callee_args.push(value);
}
let new_sig = builder.import_signature(signature.clone());
// TODO: It's possible to make this a direct call. We just need Cranelift
// to support functions declared with an immediate integer address.
// ExternalName::Absolute(u64). Let's do it.
let callee_value = builder.ins().iconst(pointer_type, callee_address as i64);
let call = builder
.ins()
.call_indirect(new_sig, callee_value, &callee_args);
let results = builder.func.dfg.inst_results(call).to_vec();
// Store the return values into `values_vec`.
let mflags = ir::MemFlags::trusted();
for (i, r) in results.iter().enumerate() {
builder
.ins()
.store(mflags, *r, values_vec_ptr_val, (i * value_size) as i32);
}
builder.ins().return_(&[]);
builder.finalize()
}
let mut code_buf: Vec<u8> = Vec::new();
let mut reloc_sink = RelocSink::new();
let mut trap_sink = binemit::NullTrapSink {};
context
.compile_and_emit(isa, &mut code_buf, &mut reloc_sink, &mut trap_sink)
.map_err(|error| ActionError::Compile(CompileError::Codegen(error)))?;
assert!(reloc_sink.func_relocs.is_empty());
Ok(jit_code
.allocate_copy_of_byte_slice(&code_buf)
.map_err(|message| ActionError::Instantiate(InstantiationError::Resource(message)))?
.as_ptr())
}

View File

@@ -1,5 +1,5 @@
[package] [package]
name = "wasmtime-execute" name = "wasmtime-jit"
version = "0.1.0" version = "0.1.0"
authors = ["The Cranelift Project Developers"] authors = ["The Cranelift Project Developers"]
publish = false publish = false

6
lib/jit/README.md Normal file
View File

@@ -0,0 +1,6 @@
This is the `wasmtime-jit` crate, which contains JIT-based execution
for wasm, using the wasm ABI defined by [`wasmtime-environ`] and the
runtime support provided by [`wasmtime-runtime`].
[`wasmtime-environ`]: https://crates.io/crates/wasmtime-environ
[`wasmtime-runtime`]: https://crates.io/crates/wasmtime-runtime

285
lib/jit/src/action.rs Normal file
View File

@@ -0,0 +1,285 @@
//! Support for performing actions with a wasm module from the outside.
use compiler::Compiler;
use cranelift_codegen::ir;
use instantiate::SetupError;
use std::cmp::max;
use std::string::String;
use std::vec::Vec;
use std::{fmt, mem, ptr, slice};
use wasmtime_runtime::{wasmtime_call_trampoline, Export, Instance};
/// A runtime value.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum RuntimeValue {
/// A runtime value with type i32.
I32(i32),
/// A runtime value with type i64.
I64(i64),
/// A runtime value with type f32.
F32(u32),
/// A runtime value with type f64.
F64(u64),
}
impl RuntimeValue {
/// Return the type of this `RuntimeValue`.
pub fn value_type(self) -> ir::Type {
match self {
RuntimeValue::I32(_) => ir::types::I32,
RuntimeValue::I64(_) => ir::types::I64,
RuntimeValue::F32(_) => ir::types::F32,
RuntimeValue::F64(_) => ir::types::F64,
}
}
/// Assuming this `RuntimeValue` holds an `i32`, return that value.
pub fn unwrap_i32(self) -> i32 {
match self {
RuntimeValue::I32(x) => x,
_ => panic!("unwrapping value of type {} as i32", self.value_type()),
}
}
/// Assuming this `RuntimeValue` holds an `i64`, return that value.
pub fn unwrap_i64(self) -> i64 {
match self {
RuntimeValue::I64(x) => x,
_ => panic!("unwrapping value of type {} as i64", self.value_type()),
}
}
/// Assuming this `RuntimeValue` holds an `f32`, return that value.
pub fn unwrap_f32(self) -> f32 {
f32::from_bits(self.unwrap_f32_bits())
}
/// Assuming this `RuntimeValue` holds an `f32`, return the bits of that value as a `u32`.
pub fn unwrap_f32_bits(self) -> u32 {
match self {
RuntimeValue::F32(x) => x,
_ => panic!("unwrapping value of type {} as f32", self.value_type()),
}
}
/// Assuming this `RuntimeValue` holds an `f64`, return that value.
pub fn unwrap_f64(self) -> f64 {
f64::from_bits(self.unwrap_f64_bits())
}
/// Assuming this `RuntimeValue` holds an `f64`, return the bits of that value as a `u64`.
pub fn unwrap_f64_bits(self) -> u64 {
match self {
RuntimeValue::F64(x) => x,
_ => panic!("unwrapping value of type {} as f64", self.value_type()),
}
}
}
impl fmt::Display for RuntimeValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
RuntimeValue::I32(x) => write!(f, "{}: i32", x),
RuntimeValue::I64(x) => write!(f, "{}: i64", x),
RuntimeValue::F32(x) => write!(f, "{}: f32", x),
RuntimeValue::F64(x) => write!(f, "{}: f64", x),
}
}
}
/// The result of invoking a wasm function or reading a wasm global.
#[derive(Debug)]
pub enum ActionOutcome {
/// The action returned normally. Its return values are provided.
Returned {
/// The return values.
values: Vec<RuntimeValue>,
},
/// A trap occurred while the action was executing.
Trapped {
/// The trap message.
message: String,
},
}
/// An error detected while invoking a wasm function or reading a wasm global.
/// Note that at this level, traps are not reported errors, but are rather
/// returned through `ActionOutcome`.
#[derive(Fail, Debug)]
pub enum ActionError {
/// An internal implementation error occurred.
#[fail(display = "{}", _0)]
Setup(SetupError),
/// No field with the specified name was present.
#[fail(display = "Unknown field: {}", _0)]
Field(String),
/// The field was present but was the wrong kind (eg. function, table, global, or memory).
#[fail(display = "Kind error: {}", _0)]
Kind(String),
/// The field was present but was the wrong type (eg. i32, i64, f32, or f64).
#[fail(display = "Type error: {}", _0)]
Type(String),
}
/// Invoke a function in an `Instance` identified by an export name.
pub fn invoke(
compiler: &mut Compiler,
instance: &mut Instance,
function_name: &str,
args: &[RuntimeValue],
) -> Result<ActionOutcome, ActionError> {
let (address, signature, callee_vmctx) = match instance.lookup(function_name) {
Some(Export::Function {
address,
signature,
vmctx,
}) => (address, signature, vmctx),
Some(_) => {
return Err(ActionError::Kind(format!(
"exported item \"{}\" is not a function",
function_name
)))
}
None => {
return Err(ActionError::Field(format!(
"no export named \"{}\"",
function_name
)))
}
};
for (index, value) in args.iter().enumerate() {
assert_eq!(value.value_type(), signature.params[index].value_type);
}
// TODO: Support values larger than u64. And pack the values into memory
// instead of just using fixed-sized slots.
let mut values_vec: Vec<u64> = Vec::new();
let value_size = mem::size_of::<u64>();
values_vec.resize(max(signature.params.len(), signature.returns.len()), 0u64);
// Store the argument values into `values_vec`.
for (index, arg) in args.iter().enumerate() {
unsafe {
let ptr = values_vec.as_mut_ptr().add(index);
match arg {
RuntimeValue::I32(x) => ptr::write(ptr as *mut i32, *x),
RuntimeValue::I64(x) => ptr::write(ptr as *mut i64, *x),
RuntimeValue::F32(x) => ptr::write(ptr as *mut u32, *x),
RuntimeValue::F64(x) => ptr::write(ptr as *mut u64, *x),
}
}
}
// Get the trampoline to call for this function.
let exec_code_buf = compiler
.get_trampoline(address, &signature, value_size)
.map_err(ActionError::Setup)?;
// Make all JIT code produced thus far executable.
compiler.publish_compiled_code();
// Call the trampoline.
if let Err(message) = unsafe {
wasmtime_call_trampoline(
exec_code_buf,
values_vec.as_mut_ptr() as *mut u8,
callee_vmctx,
)
} {
return Ok(ActionOutcome::Trapped { message });
}
// Load the return values out of `values_vec`.
let values = signature
.returns
.iter()
.enumerate()
.map(|(index, abi_param)| unsafe {
let ptr = values_vec.as_ptr().add(index);
match abi_param.value_type {
ir::types::I32 => RuntimeValue::I32(ptr::read(ptr as *const i32)),
ir::types::I64 => RuntimeValue::I64(ptr::read(ptr as *const i64)),
ir::types::F32 => RuntimeValue::F32(ptr::read(ptr as *const u32)),
ir::types::F64 => RuntimeValue::F64(ptr::read(ptr as *const u64)),
other => panic!("unsupported value type {:?}", other),
}
})
.collect();
Ok(ActionOutcome::Returned { values })
}
/// Returns a slice of the contents of allocated linear memory.
pub fn inspect_memory<'instance>(
instance: &'instance Instance,
memory_name: &str,
start: usize,
len: usize,
) -> Result<&'instance [u8], ActionError> {
let definition = match unsafe { instance.lookup_immutable(memory_name) } {
Some(Export::Memory {
definition,
memory: _memory,
vmctx: _vmctx,
}) => definition,
Some(_) => {
return Err(ActionError::Kind(format!(
"exported item \"{}\" is not a linear memory",
memory_name
)))
}
None => {
return Err(ActionError::Field(format!(
"no export named \"{}\"",
memory_name
)))
}
};
Ok(unsafe {
let memory_def = &*definition;
&slice::from_raw_parts(memory_def.base, memory_def.current_length)[start..start + len]
})
}
/// Read a global in this `Instance` identified by an export name.
pub fn get(instance: &Instance, global_name: &str) -> Result<RuntimeValue, ActionError> {
let (definition, global) = match unsafe { instance.lookup_immutable(global_name) } {
Some(Export::Global { definition, global }) => (definition, global),
Some(_) => {
return Err(ActionError::Kind(format!(
"exported item \"{}\" is not a global variable",
global_name
)))
}
None => {
return Err(ActionError::Field(format!(
"no export named \"{}\"",
global_name
)))
}
};
unsafe {
let global_def = &*definition;
Ok(match global.ty {
ir::types::I32 => RuntimeValue::I32(*global_def.as_i32()),
ir::types::I64 => RuntimeValue::I64(*global_def.as_i64()),
ir::types::F32 => RuntimeValue::F32(*global_def.as_f32_bits()),
ir::types::F64 => RuntimeValue::F64(*global_def.as_f64_bits()),
other => {
return Err(ActionError::Type(format!(
"global with type {} not supported",
other
)))
}
})
}
}

View File

@@ -7,15 +7,15 @@ use std::{cmp, mem};
use wasmtime_runtime::{Mmap, VMFunctionBody}; use wasmtime_runtime::{Mmap, VMFunctionBody};
/// Memory manager for executable code. /// Memory manager for executable code.
pub struct JITCode { pub(crate) struct CodeMemory {
current: Mmap, current: Mmap,
mmaps: Vec<Mmap>, mmaps: Vec<Mmap>,
position: usize, position: usize,
published: usize, published: usize,
} }
impl JITCode { impl CodeMemory {
/// Create a new `JITCode` instance. /// Create a new `CodeMemory` instance.
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
current: Mmap::new(), current: Mmap::new(),

252
lib/jit/src/compiler.rs Normal file
View File

@@ -0,0 +1,252 @@
//! JIT compilation.
use code_memory::CodeMemory;
use cranelift_codegen::ir::InstBuilder;
use cranelift_codegen::isa::{TargetFrontendConfig, TargetIsa};
use cranelift_codegen::Context;
use cranelift_codegen::{binemit, ir};
use cranelift_entity::PrimaryMap;
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
use cranelift_wasm::DefinedFuncIndex;
use instantiate::SetupError;
use std::boxed::Box;
use std::collections::HashMap;
use std::string::String;
use std::vec::Vec;
use target_tunables::target_tunables;
use wasmtime_environ::cranelift;
use wasmtime_environ::{Compilation, CompileError, Module, Relocations, Tunables};
use wasmtime_runtime::{InstantiationError, VMFunctionBody};
/// A WebAssembly code JIT compiler.
///
/// A `Compiler` instance owns the executable memory that it allocates.
///
/// TODO: Evolve this to support streaming rather than requiring a `&[u8]`
/// containing a whole wasm module at once.
///
/// TODO: Consider using cranelift-module.
pub struct Compiler {
isa: Box<TargetIsa>,
code_memory: CodeMemory,
trampoline_park: HashMap<*const VMFunctionBody, *const VMFunctionBody>,
/// The `FunctionBuilderContext`, shared between trampline function compilations.
fn_builder_ctx: FunctionBuilderContext,
}
impl Compiler {
/// Construct a new `Compiler`.
pub fn new(isa: Box<TargetIsa>) -> Self {
Self {
isa,
code_memory: CodeMemory::new(),
trampoline_park: HashMap::new(),
fn_builder_ctx: FunctionBuilderContext::new(),
}
}
}
impl Compiler {
/// Return the target's frontend configuration settings.
pub fn frontend_config(&self) -> TargetFrontendConfig {
self.isa.frontend_config()
}
/// Return the tunables in use by this engine.
pub fn tunables(&self) -> Tunables {
target_tunables(self.isa.triple())
}
/// Compile the given function bodies.
pub(crate) fn compile<'data>(
&mut self,
module: &Module,
function_body_inputs: PrimaryMap<DefinedFuncIndex, &'data [u8]>,
) -> Result<
(
PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>,
Relocations,
),
SetupError,
> {
let (compilation, relocations) =
cranelift::compile_module(&module, function_body_inputs, &*self.isa)
.map_err(SetupError::Compile)?;
let allocated_functions =
allocate_functions(&mut self.code_memory, compilation).map_err(|message| {
SetupError::Instantiate(InstantiationError::Resource(format!(
"failed to allocate memory for functions: {}",
message
)))
})?;
Ok((allocated_functions, relocations))
}
/// Create a trampoline for invoking a function.
pub(crate) fn get_trampoline(
&mut self,
callee_address: *const VMFunctionBody,
signature: &ir::Signature,
value_size: usize,
) -> Result<*const VMFunctionBody, SetupError> {
use std::collections::hash_map::Entry::{Occupied, Vacant};
Ok(match self.trampoline_park.entry(callee_address) {
Occupied(entry) => *entry.get(),
Vacant(entry) => {
let body = make_trampoline(
&*self.isa,
&mut self.code_memory,
&mut self.fn_builder_ctx,
callee_address,
signature,
value_size,
)?;
entry.insert(body);
body
}
})
}
/// Make memory containing compiled code executable.
pub(crate) fn publish_compiled_code(&mut self) {
self.code_memory.publish();
}
}
/// Create a trampoline for invoking a function.
fn make_trampoline(
isa: &TargetIsa,
code_memory: &mut CodeMemory,
fn_builder_ctx: &mut FunctionBuilderContext,
callee_address: *const VMFunctionBody,
signature: &ir::Signature,
value_size: usize,
) -> Result<*const VMFunctionBody, SetupError> {
let pointer_type = isa.pointer_type();
let mut wrapper_sig = ir::Signature::new(isa.frontend_config().default_call_conv);
// Add the `values_vec` parameter.
wrapper_sig.params.push(ir::AbiParam::new(pointer_type));
// Add the `vmctx` parameter.
wrapper_sig.params.push(ir::AbiParam::special(
pointer_type,
ir::ArgumentPurpose::VMContext,
));
let mut context = Context::new();
context.func = ir::Function::with_name_signature(ir::ExternalName::user(0, 0), wrapper_sig);
{
let mut builder = FunctionBuilder::new(&mut context.func, fn_builder_ctx);
let block0 = builder.create_ebb();
builder.append_ebb_params_for_function_params(block0);
builder.switch_to_block(block0);
builder.seal_block(block0);
let mut callee_args = Vec::new();
let (values_vec_ptr_val, vmctx_ptr_val) = {
let params = builder.func.dfg.ebb_params(block0);
(params[0], params[1])
};
// Load the argument values out of `values_vec`.
let mflags = ir::MemFlags::trusted();
for (i, r) in signature.params.iter().enumerate() {
let value = match r.purpose {
ir::ArgumentPurpose::Normal => builder.ins().load(
r.value_type,
mflags,
values_vec_ptr_val,
(i * value_size) as i32,
),
ir::ArgumentPurpose::VMContext => vmctx_ptr_val,
other => panic!("unsupported argument purpose {}", other),
};
callee_args.push(value);
}
let new_sig = builder.import_signature(signature.clone());
// TODO: It's possible to make this a direct call. We just need Cranelift
// to support functions declared with an immediate integer address.
// ExternalName::Absolute(u64). Let's do it.
let callee_value = builder.ins().iconst(pointer_type, callee_address as i64);
let call = builder
.ins()
.call_indirect(new_sig, callee_value, &callee_args);
let results = builder.func.dfg.inst_results(call).to_vec();
// Store the return values into `values_vec`.
let mflags = ir::MemFlags::trusted();
for (i, r) in results.iter().enumerate() {
builder
.ins()
.store(mflags, *r, values_vec_ptr_val, (i * value_size) as i32);
}
builder.ins().return_(&[]);
builder.finalize()
}
let mut code_buf: Vec<u8> = Vec::new();
let mut reloc_sink = RelocSink {};
let mut trap_sink = binemit::NullTrapSink {};
context
.compile_and_emit(isa, &mut code_buf, &mut reloc_sink, &mut trap_sink)
.map_err(|error| SetupError::Compile(CompileError::Codegen(error)))?;
Ok(code_memory
.allocate_copy_of_byte_slice(&code_buf)
.map_err(|message| SetupError::Instantiate(InstantiationError::Resource(message)))?
.as_ptr())
}
fn allocate_functions(
code_memory: &mut CodeMemory,
compilation: Compilation,
) -> Result<PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>, String> {
let mut result = PrimaryMap::with_capacity(compilation.functions.len());
for (_, body) in compilation.functions.into_iter() {
let fatptr: *mut [VMFunctionBody] = code_memory.allocate_copy_of_byte_slice(body)?;
result.push(fatptr);
}
Ok(result)
}
/// We don't expect trampoline compilation to produce any relocations, so
/// this `RelocSink` just asserts that it doesn't recieve any.
struct RelocSink {}
impl binemit::RelocSink for RelocSink {
fn reloc_ebb(
&mut self,
_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_ebb_offset: binemit::CodeOffset,
) {
panic!("trampoline compilation should not produce ebb relocs");
}
fn reloc_external(
&mut self,
_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_name: &ir::ExternalName,
_addend: binemit::Addend,
) {
panic!("trampoline compilation should not produce external symbol relocs");
}
fn reloc_jt(
&mut self,
_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_jt: ir::JumpTable,
) {
panic!("trampoline compilation should not produce jump table relocs");
}
}

199
lib/jit/src/instantiate.rs Normal file
View File

@@ -0,0 +1,199 @@
//! Define the `instantiate` function, which takes a byte array containing an
//! encoded wasm module and returns a live wasm instance. Also, define
//! `CompiledModule` to allow compiling and instantiating to be done as separate
//! steps.
use compiler::Compiler;
use cranelift_entity::{BoxedSlice, PrimaryMap};
use cranelift_wasm::DefinedFuncIndex;
use link::link_module;
use resolver::Resolver;
use std::boxed::Box;
use std::rc::Rc;
use std::string::String;
use std::vec::Vec;
use wasmtime_environ::{
CompileError, DataInitializer, DataInitializerLocation, Module, ModuleEnvironment,
};
use wasmtime_runtime::{Imports, Instance, InstantiationError, VMFunctionBody};
/// An error condition while setting up a wasm instance, be it validation,
/// compilation, or instantiation.
#[derive(Fail, Debug)]
pub enum SetupError {
/// The module did not pass validation.
#[fail(display = "Validation error: {}", _0)]
Validate(String),
/// A wasm translation error occured.
#[fail(display = "WebAssembly compilation error: {}", _0)]
Compile(CompileError),
/// Some runtime resource was unavailable or insufficient, or the start function
/// trapped.
#[fail(display = "Instantiation error: {}", _0)]
Instantiate(InstantiationError),
}
/// This is similar to `CompiledModule`, but references the data initializers
/// from the wasm buffer rather than holding its own copy.
struct RawCompiledModule<'data> {
module: Module,
finished_functions: BoxedSlice<DefinedFuncIndex, *const VMFunctionBody>,
imports: Imports,
data_initializers: Box<[DataInitializer<'data>]>,
}
impl<'data> RawCompiledModule<'data> {
/// Create a new `RawCompiledModule` by compiling the wasm module in `data` and instatiating it.
fn new(
compiler: &mut Compiler,
data: &'data [u8],
resolver: &mut Resolver,
) -> Result<Self, SetupError> {
let environ = ModuleEnvironment::new(compiler.frontend_config(), compiler.tunables());
let translation = environ
.translate(data)
.map_err(|error| SetupError::Compile(CompileError::Wasm(error)))?;
let (allocated_functions, relocations) =
compiler.compile(&translation.module, translation.function_body_inputs)?;
let imports = link_module(
&translation.module,
&allocated_functions,
relocations,
resolver,
)
.map_err(|err| SetupError::Instantiate(InstantiationError::Link(err)))?;
// Gather up the pointers to the compiled functions.
let finished_functions: BoxedSlice<DefinedFuncIndex, *const VMFunctionBody> =
allocated_functions
.into_iter()
.map(|(_index, allocated)| {
let fatptr: *const [VMFunctionBody] = *allocated;
fatptr as *const VMFunctionBody
})
.collect::<PrimaryMap<_, _>>()
.into_boxed_slice();
// Make all code compiled thus far executable.
compiler.publish_compiled_code();
Ok(Self {
module: translation.module,
finished_functions,
imports,
data_initializers: translation.data_initializers.into_boxed_slice(),
})
}
}
/// A compiled wasm module, ready to be instantiated.
pub struct CompiledModule {
module: Rc<Module>,
finished_functions: BoxedSlice<DefinedFuncIndex, *const VMFunctionBody>,
imports: Imports,
data_initializers: Box<[OwnedDataInitializer]>,
}
impl CompiledModule {
/// Compile a data buffer into a `CompiledModule`, which may then be instantiated.
pub fn new<'data>(
compiler: &mut Compiler,
data: &'data [u8],
resolver: &mut Resolver,
) -> Result<Self, SetupError> {
let raw = RawCompiledModule::<'data>::new(compiler, data, resolver)?;
Ok(Self {
module: Rc::new(raw.module),
finished_functions: raw.finished_functions,
imports: raw.imports,
data_initializers: raw
.data_initializers
.iter()
.map(OwnedDataInitializer::new)
.collect::<Vec<_>>()
.into_boxed_slice(),
})
}
/// Construct a `CompiledModule` from component parts.
pub fn from_parts(
module: Module,
finished_functions: BoxedSlice<DefinedFuncIndex, *const VMFunctionBody>,
imports: Imports,
data_initializers: Box<[OwnedDataInitializer]>,
) -> Self {
Self {
module: Rc::new(module),
finished_functions,
imports,
data_initializers,
}
}
/// Crate an `Instance` from this `CompiledModule`.
///
/// Note that if only one instance of this module is needed, it may be more
/// efficient to call the top-level `instantiate`, since that avoids copying
/// the data initializers.
pub fn instantiate(&mut self) -> Result<Box<Instance>, InstantiationError> {
let data_initializers = self
.data_initializers
.iter()
.map(|init| DataInitializer {
location: init.location.clone(),
data: &*init.data,
})
.collect::<Vec<_>>();
Instance::new(
Rc::clone(&self.module),
self.finished_functions.clone(),
self.imports.clone(),
&data_initializers,
)
}
}
/// Similar to `DataInitializer`, but owns its own copy of the data rather
/// than holding a slice of the original module.
pub struct OwnedDataInitializer {
/// The location where the initialization is to be performed.
location: DataInitializerLocation,
/// The initialization data.
data: Box<[u8]>,
}
impl OwnedDataInitializer {
fn new(borrowed: &DataInitializer) -> Self {
Self {
location: borrowed.location.clone(),
data: borrowed.data.to_vec().into_boxed_slice(),
}
}
}
/// Create a new `Instance` by compiling the wasm module in `data` and instatiating it.
///
/// This is equivalent to createing a `CompiledModule` and calling `instantiate()` on it,
/// but avoids creating an intermediate copy of the data initializers.
pub fn instantiate(
compiler: &mut Compiler,
data: &[u8],
resolver: &mut Resolver,
) -> Result<Box<Instance>, SetupError> {
let raw = RawCompiledModule::new(compiler, data, resolver)?;
Instance::new(
Rc::new(raw.module),
raw.finished_functions,
raw.imports,
&*raw.data_initializers,
)
.map_err(SetupError::Instantiate)
}

View File

@@ -36,27 +36,31 @@ extern crate wasmtime_runtime;
#[macro_use] #[macro_use]
extern crate alloc; extern crate alloc;
extern crate failure; extern crate failure;
extern crate target_lexicon;
#[macro_use] #[macro_use]
extern crate failure_derive; extern crate failure_derive;
extern crate target_lexicon;
mod action; mod action;
mod instance_plus; mod code_memory;
mod jit_code; mod compiler;
mod instantiate;
mod link; mod link;
mod namespace; mod namespace;
mod resolver; mod resolver;
mod target_tunables; mod target_tunables;
mod trampoline_park;
pub use action::{ActionError, ActionOutcome, RuntimeValue}; pub use action::{ActionError, ActionOutcome, RuntimeValue};
pub use instance_plus::InstancePlus; pub use compiler::Compiler;
pub use jit_code::JITCode; pub use instantiate::{instantiate, CompiledModule, SetupError};
pub use link::link_module; pub use link::link_module;
pub use namespace::{InstancePlusIndex, Namespace}; pub use namespace::{InstanceIndex, Namespace};
pub use resolver::{NullResolver, Resolver}; pub use resolver::{NullResolver, Resolver};
pub use target_tunables::target_tunables; pub use target_tunables::target_tunables;
// Re-export `Instance` so that users won't need to separately depend on
// wasmtime-runtime in common cases.
pub use wasmtime_runtime::{Instance, InstantiationError};
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
mod std { mod std {
pub use alloc::{boxed, rc, string, vec}; pub use alloc::{boxed, rc, string, vec};

View File

@@ -1,24 +1,20 @@
//! Linking for JIT-compiled code.
use cranelift_codegen::binemit::Reloc; use cranelift_codegen::binemit::Reloc;
use cranelift_entity::PrimaryMap; use cranelift_entity::PrimaryMap;
use cranelift_wasm::{DefinedFuncIndex, Global, GlobalInit, Memory, Table, TableElementType}; use cranelift_wasm::{DefinedFuncIndex, Global, GlobalInit, Memory, Table, TableElementType};
use resolver::Resolver; use resolver::Resolver;
use std::ptr::write_unaligned; use std::ptr::write_unaligned;
use std::string::String;
use std::vec::Vec; use std::vec::Vec;
use wasmtime_environ::{ use wasmtime_environ::{
MemoryPlan, MemoryStyle, Module, Relocation, RelocationTarget, Relocations, TablePlan, MemoryPlan, MemoryStyle, Module, Relocation, RelocationTarget, Relocations, TablePlan,
}; };
use wasmtime_runtime::libcalls; use wasmtime_runtime::libcalls;
use wasmtime_runtime::{ use wasmtime_runtime::{
Export, Imports, VMFunctionBody, VMFunctionImport, VMGlobalImport, VMMemoryImport, Export, Imports, LinkError, VMFunctionBody, VMFunctionImport, VMGlobalImport, VMMemoryImport,
VMTableImport, VMTableImport,
}; };
/// A link error, such as incompatible or unmatched imports/exports.
#[derive(Fail, Debug)]
#[fail(display = "Link error: {}", _0)]
pub struct LinkError(String);
/// Links a module that has been compiled with `compiled_module` in `wasmtime-environ`. /// Links a module that has been compiled with `compiled_module` in `wasmtime-environ`.
pub fn link_module( pub fn link_module(
module: &Module, module: &Module,

View File

@@ -2,19 +2,20 @@
//! to exports. This file provides one possible way to manage multiple instances //! to exports. This file provides one possible way to manage multiple instances
//! and resolve imports to exports among them. //! and resolve imports to exports among them.
use action::{get, inspect_memory, invoke};
use action::{ActionError, ActionOutcome, RuntimeValue}; use action::{ActionError, ActionOutcome, RuntimeValue};
use cranelift_codegen::isa; use compiler::Compiler;
use cranelift_entity::PrimaryMap; use cranelift_entity::PrimaryMap;
use instance_plus::InstancePlus;
use jit_code::JITCode;
use resolver::Resolver; use resolver::Resolver;
use std::boxed::Box;
use std::collections::HashMap; use std::collections::HashMap;
use wasmtime_runtime::Export; use std::string::String;
use wasmtime_runtime::{Export, Instance};
/// An opaque reference to an `InstancePlus`. /// An opaque reference to an `Instance`.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct InstancePlusIndex(u32); pub struct InstanceIndex(u32);
entity_impl!(InstancePlusIndex, "instance"); entity_impl!(InstanceIndex, "instance");
/// A namespace containing instances keyed by name. /// A namespace containing instances keyed by name.
/// ///
@@ -22,10 +23,10 @@ entity_impl!(InstancePlusIndex, "instance");
/// imports using defined exports. /// imports using defined exports.
pub struct Namespace { pub struct Namespace {
/// Mapping from identifiers to indices in `self.instances`. /// Mapping from identifiers to indices in `self.instances`.
names: HashMap<String, InstancePlusIndex>, names: HashMap<String, InstanceIndex>,
/// The instances, available by index. /// The instances, available by index.
instances: PrimaryMap<InstancePlusIndex, InstancePlus>, instances: PrimaryMap<InstanceIndex, Box<Instance>>,
} }
impl Namespace { impl Namespace {
@@ -37,13 +38,13 @@ impl Namespace {
} }
} }
/// Install a new `InstancePlus` in this `Namespace`, optionally with the /// Install a new `Instance` in this `Namespace`, optionally with the
/// given name, and return its index. /// given name, and return its index.
pub fn instance( pub fn instance(
&mut self, &mut self,
instance_name: Option<&str>, instance_name: Option<&str>,
instance: InstancePlus, instance: Box<Instance>,
) -> InstancePlusIndex { ) -> InstanceIndex {
let index = self.instances.push(instance); let index = self.instances.push(instance);
if let Some(instance_name) = instance_name { if let Some(instance_name) = instance_name {
self.names.insert(instance_name.into(), index); self.names.insert(instance_name.into(), index);
@@ -52,41 +53,47 @@ impl Namespace {
} }
/// Get the instance index registered with the given `instance_name`. /// Get the instance index registered with the given `instance_name`.
pub fn get_instance_index(&mut self, instance_name: &str) -> Option<&mut InstancePlusIndex> { pub fn get_instance_index(&mut self, instance_name: &str) -> Option<&mut InstanceIndex> {
self.names.get_mut(instance_name) self.names.get_mut(instance_name)
} }
/// Register an instance with a given name. /// Register an instance with a given name.
pub fn register(&mut self, name: String, index: InstancePlusIndex) { pub fn register(&mut self, name: String, index: InstanceIndex) {
self.names.insert(name, index); self.names.insert(name, index);
} }
/// Invoke an exported function from an instance. /// Invoke an exported function from an instance.
pub fn invoke( pub fn invoke(
&mut self, &mut self,
jit_code: &mut JITCode, compiler: &mut Compiler,
isa: &isa::TargetIsa, index: InstanceIndex,
index: InstancePlusIndex,
field_name: &str, field_name: &str,
args: &[RuntimeValue], args: &[RuntimeValue],
) -> Result<ActionOutcome, ActionError> { ) -> Result<ActionOutcome, ActionError> {
self.instances[index].invoke(jit_code, isa, &field_name, &args) invoke(compiler, &mut self.instances[index], &field_name, &args)
}
/// Get a slice of memory from an instance.
pub fn inspect_memory(
&self,
index: InstanceIndex,
field_name: &str,
start: usize,
len: usize,
) -> Result<&[u8], ActionError> {
inspect_memory(&self.instances[index], &field_name, start, len)
} }
/// Get the value of an exported global from an instance. /// Get the value of an exported global from an instance.
pub fn get( pub fn get(&self, index: InstanceIndex, field_name: &str) -> Result<RuntimeValue, ActionError> {
&mut self, get(&self.instances[index], &field_name)
index: InstancePlusIndex,
field_name: &str,
) -> Result<RuntimeValue, ActionError> {
self.instances[index].get(&field_name)
} }
} }
impl Resolver for Namespace { impl Resolver for Namespace {
fn resolve(&mut self, instance: &str, field: &str) -> Option<Export> { fn resolve(&mut self, instance: &str, field: &str) -> Option<Export> {
if let Some(index) = self.names.get(instance) { if let Some(index) = self.names.get(instance) {
self.instances[*index].instance.lookup(field) self.instances[*index].lookup(field)
} else { } else {
None None
} }

View File

@@ -1,3 +1,6 @@
//! Define the `Resolver` trait, allowing custom resolution for external
//! references.
use wasmtime_runtime::Export; use wasmtime_runtime::Export;
/// Import resolver connects imports with available exported values. /// Import resolver connects imports with available exported values.

View File

@@ -1,7 +1,7 @@
This is the `wasmtime-runtime` crate, which contains wasm runtime library This is the `wasmtime-runtime` crate, which contains wasm runtime library
support, supporting the wasm ABI used by [`wasmtime-environ`], support, supporting the wasm ABI used by [`wasmtime-environ`],
[`wasmtime-execute`], and [`wasmtime-obj`]. [`wasmtime-jit`], and [`wasmtime-obj`].
[`wasmtime-environ`]: https://crates.io/crates/wasmtime-environ [`wasmtime-environ`]: https://crates.io/crates/wasmtime-environ
[`wasmtime-execute`]: https://crates.io/crates/wasmtime-execute [`wasmtime-jit`]: https://crates.io/crates/wasmtime-jit
[`wasmtime-obj`]: https://crates.io/crates/wasmtime-obj [`wasmtime-obj`]: https://crates.io/crates/wasmtime-obj

View File

@@ -640,9 +640,9 @@ WasmTrapHandler(int signum, siginfo_t* info, void* context)
} }
assert(previousSignal); assert(previousSignal);
// This signal is not for any JIT code we expect, so we need to forward // This signal is not for any compiled wasm code we expect, so we need to
// the signal to the next handler. If there is no next handler (SIG_IGN or // forward the signal to the next handler. If there is no next handler (SIG_IGN
// SIG_DFL), then it's time to crash. To do this, we set the signal back to // or SIG_DFL), then it's time to crash. To do this, we set the signal back to
// its original disposition and return. This will cause the faulting op to // its original disposition and return. This will cause the faulting op to
// be re-executed which will crash in the normal way. The advantage of // be re-executed which will crash in the normal way. The advantage of
// doing this to calling _exit() is that we remove ourselves from the crash // doing this to calling _exit() is that we remove ourselves from the crash

View File

@@ -3,7 +3,7 @@ use cranelift_wasm::{FuncIndex, GlobalIndex, MemoryIndex, TableIndex};
use vmcontext::{VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport}; use vmcontext::{VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport};
/// Resolved import pointers. /// Resolved import pointers.
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct Imports { pub struct Imports {
/// Resolved addresses for imported functions. /// Resolved addresses for imported functions.
pub functions: BoxedSlice<FuncIndex, VMFunctionImport>, pub functions: BoxedSlice<FuncIndex, VMFunctionImport>,

View File

@@ -23,6 +23,9 @@ use vmcontext::{
use wasmtime_environ::{DataInitializer, Module}; use wasmtime_environ::{DataInitializer, Module};
/// An Instance of a WebAssemby module. /// An Instance of a WebAssemby module.
///
/// Note that compiled wasm code passes around raw pointers to `Instance`, so
/// this shouldn't be moved.
#[derive(Debug)] #[derive(Debug)]
pub struct Instance { pub struct Instance {
/// The `Module` this `Instance` was instantiated from. /// The `Module` this `Instance` was instantiated from.
@@ -53,7 +56,7 @@ pub struct Instance {
/// WebAssembly global variable data. /// WebAssembly global variable data.
vmctx_globals: BoxedSlice<DefinedGlobalIndex, VMGlobalDefinition>, vmctx_globals: BoxedSlice<DefinedGlobalIndex, VMGlobalDefinition>,
/// Context pointer used by JIT code. /// Context pointer used by compiled wasm code.
vmctx: VMContext, vmctx: VMContext,
} }
@@ -63,7 +66,7 @@ impl Instance {
module: Rc<Module>, module: Rc<Module>,
finished_functions: BoxedSlice<DefinedFuncIndex, *const VMFunctionBody>, finished_functions: BoxedSlice<DefinedFuncIndex, *const VMFunctionBody>,
mut vmctx_imports: Imports, mut vmctx_imports: Imports,
data_initializers: Vec<DataInitializer>, data_initializers: &[DataInitializer],
) -> Result<Box<Self>, InstantiationError> { ) -> Result<Box<Self>, InstantiationError> {
let mut sig_registry = create_and_initialize_signatures(&module); let mut sig_registry = create_and_initialize_signatures(&module);
let mut tables = create_tables(&module); let mut tables = create_tables(&module);
@@ -125,7 +128,7 @@ impl Instance {
// Check initializer bounds before initializing anything. // Check initializer bounds before initializing anything.
check_table_init_bounds(&mut *result)?; check_table_init_bounds(&mut *result)?;
check_memory_init_bounds(&mut *result, &data_initializers)?; check_memory_init_bounds(&mut *result, data_initializers)?;
// Apply the initializers. // Apply the initializers.
initialize_tables(&mut *result)?; initialize_tables(&mut *result)?;
@@ -148,22 +151,22 @@ impl Instance {
Ok(result) Ok(result)
} }
/// Return a reference to the vmctx used by JIT code. /// Return a reference to the vmctx used by compiled wasm code.
pub fn vmctx(&self) -> &VMContext { pub fn vmctx(&self) -> &VMContext {
&self.vmctx &self.vmctx
} }
/// Return a raw pointer to the vmctx used by JIT code. /// Return a raw pointer to the vmctx used by compiled wasm code.
pub fn vmctx_ptr(&self) -> *const VMContext { pub fn vmctx_ptr(&self) -> *const VMContext {
self.vmctx() self.vmctx()
} }
/// Return a mutable reference to the vmctx used by JIT code. /// Return a mutable reference to the vmctx used by compiled wasm code.
pub fn vmctx_mut(&mut self) -> &mut VMContext { pub fn vmctx_mut(&mut self) -> &mut VMContext {
&mut self.vmctx &mut self.vmctx
} }
/// Return a mutable raw pointer to the vmctx used by JIT code. /// Return a mutable raw pointer to the vmctx used by compiled wasm code.
pub fn vmctx_mut_ptr(&mut self) -> *mut VMContext { pub fn vmctx_mut_ptr(&mut self) -> *mut VMContext {
self.vmctx_mut() self.vmctx_mut()
} }
@@ -184,7 +187,7 @@ impl Instance {
.unwrap_or_else(|| panic!("no memory for index {}", memory_index.index())) .unwrap_or_else(|| panic!("no memory for index {}", memory_index.index()))
.grow(delta); .grow(delta);
// Keep current the VMContext pointers used by JIT code. // Keep current the VMContext pointers used by compiled wasm code.
self.vmctx_memories[memory_index] = self.memories[memory_index].vmmemory(); self.vmctx_memories[memory_index] = self.memories[memory_index].vmmemory();
result result
@@ -314,7 +317,7 @@ impl Instance {
/// This requirement is not enforced in the type system, so this function is /// This requirement is not enforced in the type system, so this function is
/// unsafe. /// unsafe.
pub unsafe fn lookup_immutable(&self, field: &str) -> Option<Export> { pub unsafe fn lookup_immutable(&self, field: &str) -> Option<Export> {
let temporary_mut = &mut *(self as *const Instance as *mut Instance); let temporary_mut = &mut *(self as *const Self as *mut Self);
temporary_mut.lookup(field) temporary_mut.lookup(field)
} }
} }
@@ -346,9 +349,9 @@ fn check_table_init_bounds(instance: &mut Instance) -> Result<(), InstantiationE
}; };
if slice.get_mut(start..start + init.elements.len()).is_none() { if slice.get_mut(start..start + init.elements.len()).is_none() {
return Err(InstantiationError::Link( return Err(InstantiationError::Link(LinkError(
"elements segment does not fit".to_owned(), "elements segment does not fit".to_owned(),
)); )));
} }
} }
@@ -361,8 +364,8 @@ fn check_memory_init_bounds(
) -> Result<(), InstantiationError> { ) -> Result<(), InstantiationError> {
for init in data_initializers { for init in data_initializers {
// TODO: Refactor this. // TODO: Refactor this.
let mut start = init.offset; let mut start = init.location.offset;
if let Some(base) = init.base { if let Some(base) = init.location.base {
let global = if let Some(def_index) = instance.module.defined_global_index(base) { let global = if let Some(def_index) = instance.module.defined_global_index(base) {
unsafe { instance.vmctx.global_mut(def_index) } unsafe { instance.vmctx.global_mut(def_index) }
} else { } else {
@@ -372,12 +375,13 @@ fn check_memory_init_bounds(
} }
// TODO: Refactor this. // TODO: Refactor this.
let memory = if let Some(defined_memory_index) = let memory = if let Some(defined_memory_index) = instance
instance.module.defined_memory_index(init.memory_index) .module
.defined_memory_index(init.location.memory_index)
{ {
unsafe { instance.vmctx.memory(defined_memory_index) } unsafe { instance.vmctx.memory(defined_memory_index) }
} else { } else {
let import = &instance.vmctx_imports.memories[init.memory_index]; let import = &instance.vmctx_imports.memories[init.location.memory_index];
let foreign_instance = unsafe { (&mut *(import).vmctx).instance() }; let foreign_instance = unsafe { (&mut *(import).vmctx).instance() };
let foreign_memory = unsafe { &mut *(import).from }; let foreign_memory = unsafe { &mut *(import).from };
let foreign_index = foreign_instance.vmctx().memory_index(foreign_memory); let foreign_index = foreign_instance.vmctx().memory_index(foreign_memory);
@@ -386,9 +390,9 @@ fn check_memory_init_bounds(
let mem_slice = unsafe { slice::from_raw_parts_mut(memory.base, memory.current_length) }; let mem_slice = unsafe { slice::from_raw_parts_mut(memory.base, memory.current_length) };
if mem_slice.get_mut(start..start + init.data.len()).is_none() { if mem_slice.get_mut(start..start + init.data.len()).is_none() {
return Err(InstantiationError::Link( return Err(InstantiationError::Link(LinkError(
"data segment does not fit".to_owned(), "data segment does not fit".to_owned(),
)); )));
} }
} }
@@ -457,9 +461,9 @@ fn initialize_tables(instance: &mut Instance) -> Result<(), InstantiationError>
}; };
} }
} else { } else {
return Err(InstantiationError::Link( return Err(InstantiationError::Link(LinkError(
"elements segment does not fit".to_owned(), "elements segment does not fit".to_owned(),
)); )));
} }
} }
@@ -482,11 +486,11 @@ fn create_memories(
/// Initialize the table memory from the provided initializers. /// Initialize the table memory from the provided initializers.
fn initialize_memories( fn initialize_memories(
instance: &mut Instance, instance: &mut Instance,
data_initializers: Vec<DataInitializer>, data_initializers: &[DataInitializer],
) -> Result<(), InstantiationError> { ) -> Result<(), InstantiationError> {
for init in data_initializers { for init in data_initializers {
let mut start = init.offset; let mut start = init.location.offset;
if let Some(base) = init.base { if let Some(base) = init.location.base {
let global = if let Some(def_index) = instance.module.defined_global_index(base) { let global = if let Some(def_index) = instance.module.defined_global_index(base) {
unsafe { instance.vmctx.global_mut(def_index) } unsafe { instance.vmctx.global_mut(def_index) }
} else { } else {
@@ -495,12 +499,13 @@ fn initialize_memories(
start += unsafe { *(&*global).as_i32() } as u32 as usize; start += unsafe { *(&*global).as_i32() } as u32 as usize;
} }
let memory = if let Some(defined_memory_index) = let memory = if let Some(defined_memory_index) = instance
instance.module.defined_memory_index(init.memory_index) .module
.defined_memory_index(init.location.memory_index)
{ {
unsafe { instance.vmctx.memory(defined_memory_index) } unsafe { instance.vmctx.memory(defined_memory_index) }
} else { } else {
let import = &instance.vmctx_imports.memories[init.memory_index]; let import = &instance.vmctx_imports.memories[init.location.memory_index];
let foreign_instance = unsafe { (&mut *(import).vmctx).instance() }; let foreign_instance = unsafe { (&mut *(import).vmctx).instance() };
let foreign_memory = unsafe { &mut *(import).from }; let foreign_memory = unsafe { &mut *(import).from };
let foreign_index = foreign_instance.vmctx().memory_index(foreign_memory); let foreign_index = foreign_instance.vmctx().memory_index(foreign_memory);
@@ -510,9 +515,9 @@ fn initialize_memories(
if let Some(to_init) = mem_slice.get_mut(start..start + init.data.len()) { if let Some(to_init) = mem_slice.get_mut(start..start + init.data.len()) {
to_init.copy_from_slice(init.data); to_init.copy_from_slice(init.data);
} else { } else {
return Err(InstantiationError::Link( return Err(InstantiationError::Link(LinkError(
"data segment does not fit".to_owned(), "data segment does not fit".to_owned(),
)); )));
} }
} }
@@ -555,6 +560,11 @@ fn initialize_globals(instance: &mut Instance) {
} }
} }
/// An link error while instantiating a module.
#[derive(Fail, Debug)]
#[fail(display = "Link error: {}", _0)]
pub struct LinkError(pub String);
/// An error while instantiating a module. /// An error while instantiating a module.
#[derive(Fail, Debug)] #[derive(Fail, Debug)]
pub enum InstantiationError { pub enum InstantiationError {
@@ -562,9 +572,9 @@ pub enum InstantiationError {
#[fail(display = "Insufficient resources: {}", _0)] #[fail(display = "Insufficient resources: {}", _0)]
Resource(String), Resource(String),
/// A wasm translation error occured. /// A wasm link error occured.
#[fail(display = "Link error: {}", _0)] #[fail(display = "{}", _0)]
Link(String), Link(LinkError),
/// A compilation error occured. /// A compilation error occured.
#[fail(display = "Trap occurred while invoking start function: {}", _0)] #[fail(display = "Trap occurred while invoking start function: {}", _0)]

View File

@@ -60,7 +60,7 @@ pub mod libcalls;
pub use export::Export; pub use export::Export;
pub use imports::Imports; pub use imports::Imports;
pub use instance::{Instance, InstantiationError}; pub use instance::{Instance, InstantiationError, LinkError};
pub use mmap::Mmap; pub use mmap::Mmap;
pub use signalhandlers::{wasmtime_init_eager, wasmtime_init_finish}; pub use signalhandlers::{wasmtime_init_eager, wasmtime_init_finish};
pub use traphandlers::{wasmtime_call, wasmtime_call_trampoline}; pub use traphandlers::{wasmtime_call, wasmtime_call_trampoline};

View File

@@ -1,6 +1,6 @@
//! Runtime library calls. Note that the JIT may sometimes perform these inline //! Runtime library calls. Note that wasm compilers may sometimes perform these
//! rather than calling them, particularly when CPUs have special instructions //! inline rather than calling them, particularly when CPUs have special
//! which compute them directly. //! instructions which compute them directly.
use cranelift_wasm::{DefinedMemoryIndex, MemoryIndex}; use cranelift_wasm::{DefinedMemoryIndex, MemoryIndex};
use vmcontext::VMContext; use vmcontext::VMContext;

View File

@@ -148,7 +148,7 @@ impl LinearMemory {
Some(prev_pages) Some(prev_pages)
} }
/// Return a `VMMemoryDefinition` for exposing the memory to JIT code. /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code.
pub fn vmmemory(&mut self) -> VMMemoryDefinition { pub fn vmmemory(&mut self) -> VMMemoryDefinition {
VMMemoryDefinition { VMMemoryDefinition {
base: self.mmap.as_mut_ptr(), base: self.mmap.as_mut_ptr(),

View File

@@ -110,7 +110,7 @@ impl Mmap {
self.ptr self.ptr
} }
/// Return the lengthof the allocated memory. /// Return the length of the allocated memory.
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.len self.len
} }

View File

@@ -39,7 +39,7 @@ impl Table {
} }
} }
/// Return a `VMTableDefinition` for exposing the table to JIT code. /// Return a `VMTableDefinition` for exposing the table to compiled wasm code.
pub fn vmtable(&mut self) -> VMTableDefinition { pub fn vmtable(&mut self) -> VMTableDefinition {
VMTableDefinition { VMTableDefinition {
base: self.vec.as_mut_ptr() as *mut u8, base: self.vec.as_mut_ptr() as *mut u8,

View File

@@ -1,5 +1,5 @@
//! This file declares `VMContext` and several related structs which contain //! This file declares `VMContext` and several related structs which contain
//! fields that JIT code accesses directly. //! fields that compiled wasm code accesses directly.
use cranelift_entity::EntityRef; use cranelift_entity::EntityRef;
use cranelift_wasm::{ use cranelift_wasm::{
@@ -62,7 +62,7 @@ mod test_vmfunction_body {
} }
} }
/// The fields a JIT needs to access to utilize a WebAssembly table /// The fields compiled code needs to access to utilize a WebAssembly table
/// imported from another instance. /// imported from another instance.
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
#[repr(C)] #[repr(C)]
@@ -98,7 +98,7 @@ mod test_vmtable_import {
} }
} }
/// The fields a JIT needs to access to utilize a WebAssembly linear /// The fields compiled code needs to access to utilize a WebAssembly linear
/// memory imported from another instance. /// memory imported from another instance.
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
#[repr(C)] #[repr(C)]
@@ -134,7 +134,7 @@ mod test_vmmemory_import {
} }
} }
/// The fields a JIT needs to access to utilize a WebAssembly global /// The fields compiled code needs to access to utilize a WebAssembly global
/// variable imported from another instance. /// variable imported from another instance.
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
#[repr(C)] #[repr(C)]
@@ -163,7 +163,7 @@ mod test_vmglobal_import {
} }
} }
/// The fields a JIT needs to access to utilize a WebAssembly linear /// The fields compiled code needs to access to utilize a WebAssembly linear
/// memory defined within the instance, namely the start address and the /// memory defined within the instance, namely the start address and the
/// size in bytes. /// size in bytes.
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@@ -206,7 +206,7 @@ mod test_vmmemory_definition {
} }
} }
/// The fields a JIT needs to access to utilize a WebAssembly table /// The fields compiled code needs to access to utilize a WebAssembly table
/// defined within the instance. /// defined within the instance.
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
#[repr(C)] #[repr(C)]

View File

@@ -14,7 +14,7 @@ cranelift-codegen = "0.26.0"
cranelift-native = "0.26.0" cranelift-native = "0.26.0"
cranelift-wasm = "0.26.0" cranelift-wasm = "0.26.0"
cranelift-entity = "0.26.0" cranelift-entity = "0.26.0"
wasmtime-execute = { path = "../execute" } wasmtime-jit = { path = "../jit" }
wasmtime-runtime = { path = "../runtime" } wasmtime-runtime = { path = "../runtime" }
wasmtime-environ = { path = "../environ" } wasmtime-environ = { path = "../environ" }
wabt = "0.7" wabt = "0.7"

View File

@@ -32,7 +32,7 @@ extern crate target_lexicon;
extern crate wabt; extern crate wabt;
extern crate wasmparser; extern crate wasmparser;
extern crate wasmtime_environ; extern crate wasmtime_environ;
extern crate wasmtime_execute; extern crate wasmtime_jit;
extern crate wasmtime_runtime; extern crate wasmtime_runtime;
mod spectest; mod spectest;

View File

@@ -2,13 +2,12 @@ use cranelift_codegen::ir::types;
use cranelift_codegen::{ir, isa}; use cranelift_codegen::{ir, isa};
use cranelift_entity::PrimaryMap; use cranelift_entity::PrimaryMap;
use cranelift_wasm::{DefinedFuncIndex, Global, GlobalInit, Memory, Table, TableElementType}; use cranelift_wasm::{DefinedFuncIndex, Global, GlobalInit, Memory, Table, TableElementType};
use std::rc::Rc;
use target_lexicon::HOST; use target_lexicon::HOST;
use wasmtime_environ::{ use wasmtime_environ::{
translate_signature, Export, MemoryPlan, MemoryStyle, Module, TablePlan, TableStyle, translate_signature, Export, MemoryPlan, MemoryStyle, Module, TablePlan, TableStyle,
}; };
use wasmtime_execute::{target_tunables, ActionError, InstancePlus}; use wasmtime_jit::{target_tunables, CompiledModule};
use wasmtime_runtime::{Imports, VMFunctionBody}; use wasmtime_runtime::{Imports, Instance, InstantiationError, VMFunctionBody};
extern "C" fn spectest_print() {} extern "C" fn spectest_print() {}
@@ -46,7 +45,7 @@ extern "C" fn spectest_print_f64_f64(x: f64, y: f64) {
/// Return an instance implementing the "spectest" interface used in the /// Return an instance implementing the "spectest" interface used in the
/// spec testsuite. /// spec testsuite.
pub fn instantiate_spectest() -> Result<InstancePlus, ActionError> { pub fn instantiate_spectest() -> Result<Box<Instance>, InstantiationError> {
let call_conv = isa::CallConv::triple_default(&HOST); let call_conv = isa::CallConv::triple_default(&HOST);
let pointer_type = types::Type::triple_pointer_type(&HOST); let pointer_type = types::Type::triple_pointer_type(&HOST);
let mut module = Module::new(); let mut module = Module::new();
@@ -218,10 +217,11 @@ pub fn instantiate_spectest() -> Result<InstancePlus, ActionError> {
let imports = Imports::none(); let imports = Imports::none();
let data_initializers = Vec::new(); let data_initializers = Vec::new();
InstancePlus::with_parts( CompiledModule::from_parts(
Rc::new(module), module,
finished_functions.into_boxed_slice(), finished_functions.into_boxed_slice(),
imports, imports,
data_initializers, data_initializers.into_boxed_slice(),
) )
.instantiate()
} }

View File

@@ -1,12 +1,12 @@
use cranelift_codegen::isa;
use spectest::instantiate_spectest; use spectest::instantiate_spectest;
use std::io::Read; use std::io::Read;
use std::path::Path; use std::path::Path;
use std::{fmt, fs, io, str}; use std::{fmt, fs, io, str};
use wabt::script::{Action, Command, CommandKind, ModuleBinary, ScriptParser, Value}; use wabt::script::{Action, Command, CommandKind, ModuleBinary, ScriptParser, Value};
use wasmparser::{validate, OperatorValidatorConfig, ValidatingParserConfig}; use wasmparser::{validate, OperatorValidatorConfig, ValidatingParserConfig};
use wasmtime_execute::{ use wasmtime_jit::{
ActionError, ActionOutcome, InstancePlus, InstancePlusIndex, JITCode, Namespace, RuntimeValue, instantiate, ActionError, ActionOutcome, Compiler, Instance, InstanceIndex, InstantiationError,
Namespace, RuntimeValue, SetupError,
}; };
/// Translate from a script::Value to a RuntimeValue. /// Translate from a script::Value to a RuntimeValue.
@@ -77,22 +77,22 @@ pub struct WastFileError {
/// to be performed on them. /// to be performed on them.
pub struct WastContext { pub struct WastContext {
/// A namespace of wasm modules, keyed by an optional name. /// A namespace of wasm modules, keyed by an optional name.
current: Option<InstancePlusIndex>, current: Option<InstanceIndex>,
namespace: Namespace, namespace: Namespace,
jit_code: JITCode, compiler: Box<Compiler>,
} }
impl WastContext { impl WastContext {
/// Construct a new instance of `WastContext`. /// Construct a new instance of `WastContext`.
pub fn new() -> Self { pub fn new(compiler: Box<Compiler>) -> Self {
Self { Self {
current: None, current: None,
namespace: Namespace::new(), namespace: Namespace::new(),
jit_code: JITCode::new(), compiler,
} }
} }
fn validate(&mut self, data: &[u8]) -> Result<(), ActionError> { fn validate(&mut self, data: &[u8]) -> Result<(), String> {
let config = ValidatingParserConfig { let config = ValidatingParserConfig {
operator_config: OperatorValidatorConfig { operator_config: OperatorValidatorConfig {
enable_threads: false, enable_threads: false,
@@ -107,26 +107,19 @@ impl WastContext {
Ok(()) Ok(())
} else { } else {
// TODO: Work with wasmparser to get better error messages. // TODO: Work with wasmparser to get better error messages.
Err(ActionError::Validate("module did not validate".to_owned())) Err("module did not validate".to_owned())
} }
} }
fn instantiate( fn instantiate(&mut self, module: ModuleBinary) -> Result<Box<Instance>, SetupError> {
&mut self,
isa: &isa::TargetIsa,
module: ModuleBinary,
) -> Result<InstancePlus, ActionError> {
let data = module.into_vec(); let data = module.into_vec();
self.validate(&data)?; self.validate(&data).map_err(SetupError::Validate)?;
InstancePlus::new(&mut self.jit_code, isa, &data, &mut self.namespace) instantiate(&mut *self.compiler, &data, &mut self.namespace)
} }
fn get_index( fn get_index(&mut self, instance_name: &Option<String>) -> Result<InstanceIndex, WastError> {
&mut self,
instance_name: &Option<String>,
) -> Result<InstancePlusIndex, WastError> {
let index = *if let Some(instance_name) = instance_name { let index = *if let Some(instance_name) = instance_name {
self.namespace self.namespace
.get_instance_index(instance_name) .get_instance_index(instance_name)
@@ -145,24 +138,20 @@ impl WastContext {
} }
/// Register "spectest" which is used by the spec testsuite. /// Register "spectest" which is used by the spec testsuite.
pub fn register_spectest(&mut self) -> Result<(), ActionError> { pub fn register_spectest(&mut self) -> Result<(), InstantiationError> {
let instance = instantiate_spectest()?; let instance = instantiate_spectest()?;
self.namespace.instance(Some("spectest"), instance); self.namespace.instance(Some("spectest"), instance);
Ok(()) Ok(())
} }
/// Perform the action portion of a command. /// Perform the action portion of a command.
fn perform_action( fn perform_action(&mut self, action: Action) -> Result<ActionOutcome, WastError> {
&mut self,
isa: &isa::TargetIsa,
action: Action,
) -> Result<ActionOutcome, WastError> {
match action { match action {
Action::Invoke { Action::Invoke {
module: instance_name, module: instance_name,
field, field,
args, args,
} => self.invoke(isa, instance_name, &field, &args), } => self.invoke(instance_name, &field, &args),
Action::Get { Action::Get {
module: instance_name, module: instance_name,
field, field,
@@ -173,11 +162,10 @@ impl WastContext {
/// Define a module and register it. /// Define a module and register it.
fn module( fn module(
&mut self, &mut self,
isa: &isa::TargetIsa,
instance_name: Option<String>, instance_name: Option<String>,
module: ModuleBinary, module: ModuleBinary,
) -> Result<(), ActionError> { ) -> Result<(), ActionError> {
let instance = self.instantiate(isa, module)?; let instance = self.instantiate(module).map_err(ActionError::Setup)?;
let index = self let index = self
.namespace .namespace
.instance(instance_name.as_ref().map(String::as_str), instance); .instance(instance_name.as_ref().map(String::as_str), instance);
@@ -195,7 +183,6 @@ impl WastContext {
/// Invoke an exported function from an instance. /// Invoke an exported function from an instance.
fn invoke( fn invoke(
&mut self, &mut self,
isa: &isa::TargetIsa,
instance_name: Option<String>, instance_name: Option<String>,
field: &str, field: &str,
args: &[Value], args: &[Value],
@@ -206,7 +193,7 @@ impl WastContext {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let index = self.get_index(&instance_name)?; let index = self.get_index(&instance_name)?;
self.namespace self.namespace
.invoke(&mut self.jit_code, isa, index, &field, &value_args) .invoke(&mut *self.compiler, index, &field, &value_args)
.map_err(WastError::Action) .map_err(WastError::Action)
} }
@@ -227,24 +214,15 @@ impl WastContext {
} }
/// Perform the action of a `PerformAction`. /// Perform the action of a `PerformAction`.
fn perform_action_command( fn perform_action_command(&mut self, action: Action) -> Result<(), WastError> {
&mut self, match self.perform_action(action)? {
isa: &isa::TargetIsa,
action: Action,
) -> Result<(), WastError> {
match self.perform_action(isa, action)? {
ActionOutcome::Returned { .. } => Ok(()), ActionOutcome::Returned { .. } => Ok(()),
ActionOutcome::Trapped { message } => Err(WastError::Trap(message)), ActionOutcome::Trapped { message } => Err(WastError::Trap(message)),
} }
} }
/// Run a wast script from a byte buffer. /// Run a wast script from a byte buffer.
pub fn run_buffer( pub fn run_buffer(&mut self, filename: &str, wast: &[u8]) -> Result<(), WastFileError> {
&mut self,
isa: &isa::TargetIsa,
filename: &str,
wast: &[u8],
) -> Result<(), WastFileError> {
let mut parser = ScriptParser::from_str(str::from_utf8(wast).unwrap()).unwrap(); let mut parser = ScriptParser::from_str(str::from_utf8(wast).unwrap()).unwrap();
while let Some(Command { kind, line }) = parser.next().expect("parser") { while let Some(Command { kind, line }) = parser.next().expect("parser") {
@@ -253,7 +231,7 @@ impl WastContext {
module: instance_name, module: instance_name,
name, name,
} => { } => {
self.module(isa, name, instance_name) self.module(name, instance_name)
.map_err(|error| WastFileError { .map_err(|error| WastFileError {
filename: filename.to_string(), filename: filename.to_string(),
line, line,
@@ -269,7 +247,7 @@ impl WastContext {
})?; })?;
} }
CommandKind::PerformAction(action) => { CommandKind::PerformAction(action) => {
self.perform_action_command(isa, action) self.perform_action_command(action)
.map_err(|error| WastFileError { .map_err(|error| WastFileError {
filename: filename.to_string(), filename: filename.to_string(),
line, line,
@@ -277,13 +255,11 @@ impl WastContext {
})?; })?;
} }
CommandKind::AssertReturn { action, expected } => { CommandKind::AssertReturn { action, expected } => {
match self match self.perform_action(action).map_err(|error| WastFileError {
.perform_action(isa, action) filename: filename.to_string(),
.map_err(|error| WastFileError { line,
filename: filename.to_string(), error,
line, })? {
error,
})? {
ActionOutcome::Returned { values } => { ActionOutcome::Returned { values } => {
for (v, e) in values for (v, e) in values
.iter() .iter()
@@ -312,13 +288,11 @@ impl WastContext {
} }
} }
CommandKind::AssertTrap { action, message } => { CommandKind::AssertTrap { action, message } => {
match self match self.perform_action(action).map_err(|error| WastFileError {
.perform_action(isa, action) filename: filename.to_string(),
.map_err(|error| WastFileError { line,
filename: filename.to_string(), error,
line, })? {
error,
})? {
ActionOutcome::Returned { values } => { ActionOutcome::Returned { values } => {
return Err(WastFileError { return Err(WastFileError {
filename: filename.to_string(), filename: filename.to_string(),
@@ -340,13 +314,11 @@ impl WastContext {
} }
} }
CommandKind::AssertExhaustion { action } => { CommandKind::AssertExhaustion { action } => {
match self match self.perform_action(action).map_err(|error| WastFileError {
.perform_action(isa, action) filename: filename.to_string(),
.map_err(|error| WastFileError { line,
filename: filename.to_string(), error,
line, })? {
error,
})? {
ActionOutcome::Returned { values } => { ActionOutcome::Returned { values } => {
return Err(WastFileError { return Err(WastFileError {
filename: filename.to_string(), filename: filename.to_string(),
@@ -366,13 +338,11 @@ impl WastContext {
} }
} }
CommandKind::AssertReturnCanonicalNan { action } => { CommandKind::AssertReturnCanonicalNan { action } => {
match self match self.perform_action(action).map_err(|error| WastFileError {
.perform_action(isa, action) filename: filename.to_string(),
.map_err(|error| WastFileError { line,
filename: filename.to_string(), error,
line, })? {
error,
})? {
ActionOutcome::Returned { values } => { ActionOutcome::Returned { values } => {
for v in values.iter() { for v in values.iter() {
match v { match v {
@@ -420,13 +390,11 @@ impl WastContext {
} }
} }
CommandKind::AssertReturnArithmeticNan { action } => { CommandKind::AssertReturnArithmeticNan { action } => {
match self match self.perform_action(action).map_err(|error| WastFileError {
.perform_action(isa, action) filename: filename.to_string(),
.map_err(|error| WastFileError { line,
filename: filename.to_string(), error,
line, })? {
error,
})? {
ActionOutcome::Returned { values } => { ActionOutcome::Returned { values } => {
for v in values.iter() { for v in values.iter() {
match v { match v {
@@ -474,7 +442,7 @@ impl WastContext {
} }
} }
CommandKind::AssertInvalid { module, message } => { CommandKind::AssertInvalid { module, message } => {
self.module(isa, None, module).expect_err(&format!( self.module(None, module).expect_err(&format!(
"{}:{}: invalid module was successfully instantiated", "{}:{}: invalid module was successfully instantiated",
filename, line filename, line
)); ));
@@ -484,7 +452,7 @@ impl WastContext {
); );
} }
CommandKind::AssertMalformed { module, message } => { CommandKind::AssertMalformed { module, message } => {
self.module(isa, None, module).expect_err(&format!( self.module(None, module).expect_err(&format!(
"{}:{}: malformed module was successfully instantiated", "{}:{}: malformed module was successfully instantiated",
filename, line filename, line
)); ));
@@ -494,7 +462,7 @@ impl WastContext {
); );
} }
CommandKind::AssertUninstantiable { module, message } => { CommandKind::AssertUninstantiable { module, message } => {
let _err = self.module(isa, None, module).expect_err(&format!( let _err = self.module(None, module).expect_err(&format!(
"{}:{}: uninstantiable module was successfully instantiated", "{}:{}: uninstantiable module was successfully instantiated",
filename, line filename, line
)); ));
@@ -504,7 +472,7 @@ impl WastContext {
); );
} }
CommandKind::AssertUnlinkable { module, message } => { CommandKind::AssertUnlinkable { module, message } => {
let _err = self.module(isa, None, module).expect_err(&format!( let _err = self.module(None, module).expect_err(&format!(
"{}:{}: unlinkable module was successfully linked", "{}:{}: unlinkable module was successfully linked",
filename, line filename, line
)); ));
@@ -520,14 +488,14 @@ impl WastContext {
} }
/// Run a wast script from a file. /// Run a wast script from a file.
pub fn run_file(&mut self, isa: &isa::TargetIsa, path: &Path) -> Result<(), WastFileError> { pub fn run_file(&mut self, path: &Path) -> Result<(), WastFileError> {
let filename = path.display().to_string(); let filename = path.display().to_string();
let buffer = read_to_end(path).map_err(|e| WastFileError { let buffer = read_to_end(path).map_err(|e| WastFileError {
filename, filename,
line: 0, line: 0,
error: WastError::IO(e), error: WastError::IO(e),
})?; })?;
self.run_buffer(isa, &path.display().to_string(), &buffer) self.run_buffer(&path.display().to_string(), &buffer)
} }
} }

View File

@@ -53,7 +53,7 @@ use std::path::PathBuf;
use std::process; use std::process;
use std::str::FromStr; use std::str::FromStr;
use target_lexicon::Triple; use target_lexicon::Triple;
use wasmtime_environ::{compile_module, Module, ModuleEnvironment, Tunables}; use wasmtime_environ::{cranelift, ModuleEnvironment, Tunables};
use wasmtime_obj::emit_module; use wasmtime_obj::emit_module;
const USAGE: &str = " const USAGE: &str = "
@@ -133,30 +133,38 @@ fn handle_module(path: PathBuf, target: &Option<String>, output: &str) -> Result
let mut obj = Artifact::new(isa.triple().clone(), String::from(output)); let mut obj = Artifact::new(isa.triple().clone(), String::from(output));
let mut module = Module::new();
// TODO: Expose the tunables as command-line flags. // TODO: Expose the tunables as command-line flags.
let tunables = Tunables::default(); let tunables = Tunables::default();
let environ = ModuleEnvironment::new(&*isa, &mut module, tunables);
let translation = environ.translate(&data).map_err(|e| e.to_string())?; let (module, lazy_function_body_inputs, lazy_data_initializers) = {
let environ = ModuleEnvironment::new(isa.frontend_config(), tunables);
let translation = environ
.translate(&data)
.map_err(|error| error.to_string())?;
(
translation.module,
translation.function_body_inputs,
translation.data_initializers,
)
};
// FIXME: We need to initialize memory in a way that supports alternate // FIXME: We need to initialize memory in a way that supports alternate
// memory spaces, imported base addresses, and offsets. // memory spaces, imported base addresses, and offsets.
for init in &translation.lazy.data_initializers { for init in lazy_data_initializers.into_iter() {
obj.define("memory", Vec::from(init.data)) obj.define("memory", Vec::from(init.data))
.map_err(|err| format!("{}", err))?; .map_err(|err| format!("{}", err))?;
} }
let (compilation, relocations) = compile_module( let (compilation, relocations) =
&translation.module, cranelift::compile_module(&module, lazy_function_body_inputs, &*isa)
&translation.lazy.function_body_inputs, .map_err(|e| e.to_string())?;
&*isa,
)
.map_err(|e| e.to_string())?;
emit_module(&mut obj, &translation.module, &compilation, &relocations)?; emit_module(&mut obj, &module, &compilation, &relocations)?;
if !translation.module.table_plans.is_empty() { if !module.table_plans.is_empty() {
if translation.module.table_plans.len() > 1 { if module.table_plans.len() > 1 {
return Err(String::from("multiple tables not supported yet")); return Err(String::from("multiple tables not supported yet"));
} }
return Err(String::from("FIXME: implement tables")); return Err(String::from("FIXME: implement tables"));

View File

@@ -33,7 +33,7 @@
extern crate cranelift_codegen; extern crate cranelift_codegen;
extern crate cranelift_native; extern crate cranelift_native;
extern crate docopt; extern crate docopt;
extern crate wasmtime_execute; extern crate wasmtime_jit;
extern crate wasmtime_wast; extern crate wasmtime_wast;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
@@ -41,7 +41,6 @@ extern crate file_per_thread_logger;
extern crate pretty_env_logger; extern crate pretty_env_logger;
extern crate wabt; extern crate wabt;
use cranelift_codegen::isa::TargetIsa;
use cranelift_codegen::settings; use cranelift_codegen::settings;
use cranelift_codegen::settings::Configurable; use cranelift_codegen::settings::Configurable;
use docopt::Docopt; use docopt::Docopt;
@@ -52,7 +51,7 @@ use std::io::prelude::*;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::exit; use std::process::exit;
use wasmtime_execute::{ActionOutcome, InstancePlus, JITCode, Namespace}; use wasmtime_jit::{instantiate, ActionOutcome, Compiler, Namespace};
use wasmtime_wast::instantiate_spectest; use wasmtime_wast::instantiate_spectest;
static LOG_FILENAME_PREFIX: &str = "wasmtime.dbg."; static LOG_FILENAME_PREFIX: &str = "wasmtime.dbg.";
@@ -123,6 +122,7 @@ fn main() {
} }
let isa = isa_builder.finish(settings::Flags::new(flag_builder)); let isa = isa_builder.finish(settings::Flags::new(flag_builder));
let mut compiler = Compiler::new(isa);
let mut namespace = Namespace::new(); let mut namespace = Namespace::new();
@@ -132,11 +132,9 @@ fn main() {
instantiate_spectest().expect("instantiating spectest"), instantiate_spectest().expect("instantiating spectest"),
); );
let mut jit_code = JITCode::new();
for filename in &args.arg_file { for filename in &args.arg_file {
let path = Path::new(&filename); let path = Path::new(&filename);
match handle_module(&mut jit_code, &mut namespace, &args, path, &*isa) { match handle_module(&mut compiler, &mut namespace, &args, path) {
Ok(()) => {} Ok(()) => {}
Err(message) => { Err(message) => {
let name = path.as_os_str().to_string_lossy(); let name = path.as_os_str().to_string_lossy();
@@ -148,11 +146,10 @@ fn main() {
} }
fn handle_module( fn handle_module(
jit_code: &mut JITCode, compiler: &mut Compiler,
namespace: &mut Namespace, namespace: &mut Namespace,
args: &Args, args: &Args,
path: &Path, path: &Path,
isa: &TargetIsa,
) -> Result<(), String> { ) -> Result<(), String> {
let mut data = let mut data =
read_to_end(path.to_path_buf()).map_err(|err| String::from(err.description()))?; read_to_end(path.to_path_buf()).map_err(|err| String::from(err.description()))?;
@@ -162,17 +159,16 @@ fn handle_module(
data = wabt::wat2wasm(data).map_err(|err| String::from(err.description()))?; data = wabt::wat2wasm(data).map_err(|err| String::from(err.description()))?;
} }
// Create a new `InstancePlus` by compiling and instantiating a wasm module. // Create a new `Instance` by compiling and instantiating a wasm module.
let instance_plus = let instance = instantiate(compiler, &data, namespace).map_err(|e| e.to_string())?;
InstancePlus::new(jit_code, isa, &data, namespace).map_err(|e| e.to_string())?;
// Register it in the namespace. // Register it in the namespace.
let index = namespace.instance(None, instance_plus); let index = namespace.instance(None, instance);
// If a function to invoke was given, invoke it. // If a function to invoke was given, invoke it.
if let Some(ref f) = args.flag_invoke { if let Some(ref f) = args.flag_invoke {
match namespace match namespace
.invoke(jit_code, isa, index, &f, &[]) .invoke(compiler, index, &f, &[])
.map_err(|e| e.to_string())? .map_err(|e| e.to_string())?
{ {
ActionOutcome::Returned { .. } => {} ActionOutcome::Returned { .. } => {}
@@ -191,7 +187,7 @@ mod tests {
use cranelift_codegen::settings::Configurable; use cranelift_codegen::settings::Configurable;
use std::path::PathBuf; use std::path::PathBuf;
use wabt; use wabt;
use wasmtime_execute::{InstancePlus, JITCode, NullResolver}; use wasmtime_jit::{instantiate, Compiler, NullResolver};
const PATH_MODULE_RS2WASM_ADD_FUNC: &str = r"filetests/rs2wasm-add-func.wat"; const PATH_MODULE_RS2WASM_ADD_FUNC: &str = r"filetests/rs2wasm-add-func.wat";
@@ -214,8 +210,8 @@ mod tests {
let isa = isa_builder.finish(settings::Flags::new(flag_builder)); let isa = isa_builder.finish(settings::Flags::new(flag_builder));
let mut resolver = NullResolver {}; let mut resolver = NullResolver {};
let mut code = JITCode::new(); let mut compiler = Compiler::new(isa);
let instance = InstancePlus::new(&mut code, &*isa, &data, &mut resolver); let instance = instantiate(&mut compiler, &data, &mut resolver);
assert!(instance.is_ok()); assert!(instance.is_ok());
} }
} }

View File

@@ -28,6 +28,7 @@
extern crate cranelift_codegen; extern crate cranelift_codegen;
extern crate cranelift_native; extern crate cranelift_native;
extern crate docopt; extern crate docopt;
extern crate wasmtime_jit;
extern crate wasmtime_wast; extern crate wasmtime_wast;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
@@ -38,6 +39,7 @@ use cranelift_codegen::settings;
use cranelift_codegen::settings::Configurable; use cranelift_codegen::settings::Configurable;
use docopt::Docopt; use docopt::Docopt;
use std::path::Path; use std::path::Path;
use wasmtime_jit::Compiler;
use wasmtime_wast::WastContext; use wasmtime_wast::WastContext;
static LOG_FILENAME_PREFIX: &str = "cranelift.dbg."; static LOG_FILENAME_PREFIX: &str = "cranelift.dbg.";
@@ -94,7 +96,8 @@ fn main() {
} }
let isa = isa_builder.finish(settings::Flags::new(flag_builder)); let isa = isa_builder.finish(settings::Flags::new(flag_builder));
let mut wast_context = WastContext::new(); let engine = Compiler::new(isa);
let mut wast_context = WastContext::new(Box::new(engine));
wast_context wast_context
.register_spectest() .register_spectest()
@@ -102,7 +105,7 @@ fn main() {
for filename in &args.arg_file { for filename in &args.arg_file {
wast_context wast_context
.run_file(&*isa, Path::new(&filename)) .run_file(Path::new(&filename))
.unwrap_or_else(|e| panic!("{}", e)); .unwrap_or_else(|e| panic!("{}", e));
} }
} }

View File

@@ -1,11 +1,13 @@
extern crate cranelift_codegen; extern crate cranelift_codegen;
extern crate cranelift_native; extern crate cranelift_native;
extern crate wasmtime_jit;
extern crate wasmtime_wast; extern crate wasmtime_wast;
use cranelift_codegen::isa; use cranelift_codegen::isa;
use cranelift_codegen::settings; use cranelift_codegen::settings;
use cranelift_codegen::settings::Configurable; use cranelift_codegen::settings::Configurable;
use std::path::Path; use std::path::Path;
use wasmtime_jit::Compiler;
use wasmtime_wast::WastContext; use wasmtime_wast::WastContext;
include!(concat!(env!("OUT_DIR"), "/wast_testsuite_tests.rs")); include!(concat!(env!("OUT_DIR"), "/wast_testsuite_tests.rs"));