Merge remote-tracking branch 'origin/master' into singlepass

This commit is contained in:
losfair
2020-05-04 23:22:05 +08:00
46 changed files with 496 additions and 376 deletions

4
Cargo.lock generated
View File

@@ -1224,9 +1224,9 @@ dependencies = [
[[package]]
name = "smallvec"
version = "1.3.0"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05720e22615919e4734f6a99ceae50d00226c3c5aca406e102ebc33298214e0a"
checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4"
[[package]]
name = "stable_deref_trait"

View File

@@ -20,16 +20,16 @@ publish = false
autoexamples = false
[dependencies]
wasmer = { path = "lib/api" }
wasmer-compiler = { path = "lib/compiler" }
wasmer-compiler-cranelift = { path = "lib/compiler-cranelift", optional = true }
wasmer-compiler-singlepass = { path = "lib/compiler-singlepass", optional = true }
wasmer-compiler-llvm = { path = "lib/compiler-llvm", optional = true }
wasmer-jit = { path = "lib/jit" }
wasmer-wasi = { path = "lib/wasi", optional = true }
wasmer-wasi-experimental-io-devices = { path = "lib/wasi-experimental-io-devices", optional = true }
wasmer-wast = { path = "tests/lib/wast", optional = true }
wasmer-cache = { path = "lib/cache", optional = true }
wasmer = { version = "0.16.2", path = "lib/api" }
wasmer-compiler = { version = "0.16.2", path = "lib/compiler" }
wasmer-compiler-cranelift = { version = "0.16.2", path = "lib/compiler-cranelift", optional = true }
wasmer-compiler-singlepass = { version = "0.16.2", path = "lib/compiler-singlepass", optional = true }
wasmer-compiler-llvm = { version = "0.16.2", path = "lib/compiler-llvm", optional = true }
wasmer-jit = { version = "0.16.2", path = "lib/jit" }
wasmer-wasi = { version = "0.16.2", path = "lib/wasi", optional = true }
wasmer-wasi-experimental-io-devices = { version = "0.16.2", path = "lib/wasi-experimental-io-devices", optional = true }
wasmer-wast = { version = "0.16.2", path = "tests/lib/wast", optional = true }
wasmer-cache = { version = "0.16.2", path = "lib/cache", optional = true }
atty = "0.2"
anyhow = "1.0.28"
structopt = { version = "0.3", features = ["suggestions"] }
@@ -47,19 +47,15 @@ rustc_version = "0.2"
[dev-dependencies]
anyhow = "1.0.28"
test-utils = { path = "tests/lib/test-utils" }
wasmer = { path = "lib/api" }
wasmer-compiler = { path = "lib/compiler" }
wasmer-jit = { path = "lib/jit" }
wasmer-wast = { path = "tests/lib/wast" }
[features]
# Don't add the backend features in default, please add them on the Makefile
# since we might want to autoconfigure them depending on the availability on the host.
# default = ["wasi"]
default = ["wast", "wasi", "experimental-io-devices", "compiler-cranelift", "cache"]
cache = ["wasmer-cache"]
wast = ["wasmer-wast"]
wasi = ["wasmer-wasi"]
compiler = ["wasmer-jit/compiler"]
experimental-io-devices = [
"wasmer-wasi-experimental-io-devices"
]
@@ -67,14 +63,20 @@ compiler-singlepass = [
"test-utils/compiler-singlepass",
"wasmer/compiler-singlepass",
"wasmer-compiler-singlepass",
"compiler",
]
compiler-cranelift = [
"test-utils/compiler-cranelift",
"wasmer/compiler-cranelift",
"wasmer-compiler-cranelift",
"compiler",
]
compiler-llvm = [
"test-utils/compiler-llvm",
"wasmer/compiler-llvm",
"wasmer-compiler-llvm",
"compiler",
]
# [profile.release]
# lto = "fat"

View File

@@ -38,14 +38,18 @@ maintenance = { status = "actively-developed" }
[features]
default = ["wat", "default-compiler-cranelift"]
compiler = ["wasmer-jit/compiler"]
compiler-singlepass = [
"wasmer-compiler-singlepass"
"wasmer-compiler-singlepass",
"compiler",
]
compiler-cranelift = [
"wasmer-compiler-cranelift"
"wasmer-compiler-cranelift",
"compiler",
]
compiler-llvm = [
"wasmer-compiler-llvm"
"wasmer-compiler-llvm",
"compiler",
]
default-compiler-singlepass = [
"compiler-singlepass"

View File

@@ -231,7 +231,9 @@ fn set_table_item(
impl Table {
pub fn new(store: &Store, ty: TableType, init: Val) -> Result<Table, RuntimeError> {
let item = init.into_checked_anyfunc(store)?;
let table = store.engine().tunables().create_table(ty);
let tunables = store.engine().tunables();
let table_plan = tunables.table_plan(ty);
let table = tunables.create_table(table_plan).unwrap();
let definition = table.vmtable();
for i in 0..definition.current_elements {
@@ -345,7 +347,10 @@ pub struct Memory {
impl Memory {
pub fn new(store: &Store, ty: MemoryType) -> Memory {
let memory = store.engine().tunables().create_memory(ty).unwrap();
let tunables = store.engine().tunables();
let memory_plan = tunables.memory_plan(ty);
let memory = tunables.create_memory(memory_plan).unwrap();
let definition = memory.vmmemory();
Memory {

View File

@@ -194,13 +194,15 @@ impl Extend<((String, String), Export)> for ImportObject {
///
///
/// # Usage:
/// ```ignore
/// use wasmer::{imports, func};
/// ```
/// # use wasmer::{Func, Store};
/// # let store = Store::default();
/// use wasmer::imports;
///
/// let import_object = imports! {
/// "env" => {
/// "foo" => func!(foo),
/// },
/// "foo" => Func::new(&store, foo)
/// }
/// };
///
/// fn foo(n: i32) -> i32 {
@@ -224,24 +226,8 @@ macro_rules! imports {
}};
}
/// Generate an [`Namespace`] easily with the `namespace!` macro.
///
/// [`Namespace`]: struct.Namespace.html
///
///
/// # Usage:
/// ```ignore
/// use wasmer::{namespace, func};
///
/// let env = namespace! {
/// "foo" => func!(foo),
/// };
///
/// fn foo(n: i32) -> i32 {
/// n
/// }
/// ```
#[macro_export]
#[doc(hidden)]
macro_rules! namespace {
($( $imp_name:expr => $import_item:expr ),* $(,)? ) => {
$crate::import_namespace!({ $( $imp_name => $import_item, )* })

View File

@@ -27,7 +27,10 @@ pub use crate::types::{
};
pub use wasm_common::{ValueType, WasmExternType, WasmTypeList};
pub use wasmer_compiler::{CompilerConfig, Features, Target};
#[cfg(feature = "compiler")]
pub use wasmer_compiler::CompilerConfig;
pub use wasmer_compiler::{Features, Target};
pub use wasmer_jit::{
DeserializeError, InstantiationError, LinkError, RuntimeError, SerializeError,
};

View File

@@ -184,7 +184,17 @@ impl Module {
&self,
resolver: &dyn Resolver,
) -> Result<InstanceHandle, InstantiationError> {
self.store.engine().instantiate(&self.compiled, resolver)
unsafe {
let instance_handle = self.store.engine().instantiate(&self.compiled, resolver)?;
// After the instance handle is created, we need to initialize
// the data, call the start function and so. However, if any
// of this steps traps, we still need to keep the instance alive
// as some of the Instance elements may have placed in other
// instance tables.
self.compiled.finish_instantiation(&instance_handle)?;
Ok(instance_handle)
}
}
/// Returns the name of the current module.

View File

@@ -1,21 +0,0 @@
use wasmer_jit::Resolver;
use std::iter::FromIterator;
pub struct IndexResolver {
externs: Vec<Extern>,
}
impl Resolver for IndexResolver {
fn resolve(&self, index: u32, _module: &str, _name: &str) -> Option<Export> {
self.externs.get(index as usize).map(|extern_| extern_.to_export())
}
}
impl FromIterator<Extern> for IndexResolver {
fn from_iter<I: IntoIterator<Item = Extern>>(iter: I) -> Self {
let mut externs = Vec::new();
for extern_ in iter {
externs.push(extern_);
}
IndexResolver { externs }
}
}

View File

@@ -1,5 +1,6 @@
use crate::tunables::Tunables;
use std::sync::Arc;
#[cfg(feature = "compiler")]
use wasmer_compiler::CompilerConfig;
use wasmer_jit::JITEngine;

View File

@@ -92,14 +92,12 @@ impl wasmer_jit::Tunables for Tunables {
}
/// Create a memory given a memory type
fn create_memory(&self, memory_type: MemoryType) -> Result<LinearMemory, String> {
let plan = self.memory_plan(memory_type);
fn create_memory(&self, plan: MemoryPlan) -> Result<LinearMemory, String> {
LinearMemory::new(&plan)
}
/// Create a memory given a memory type
fn create_table(&self, table_type: TableType) -> Table {
let plan = self.table_plan(table_type);
fn create_table(&self, plan: TablePlan) -> Result<Table, String> {
Table::new(&plan)
}
}

View File

@@ -16,7 +16,7 @@ hashbrown = { version = "0.7.2", optional = true }
tracing = "0.1"
cranelift-codegen = { version = "0.62.0", default-features = false }
cranelift-frontend = { version = "0.62.0", default-features = false }
wasmer-compiler = { path = "../compiler", version = "0.16.2", default-features = false }
wasmer-compiler = { path = "../compiler", version = "0.16.2", features = ["translator"], default-features = false }
wasmer-runtime = { path = "../runtime", version = "0.16.2" }
wasm-common = { path = "../wasm-common", version = "0.16.2", default-features = false }
rayon = "1.3.0"

View File

@@ -10,7 +10,7 @@ edition = "2018"
readme = "README.md"
[dependencies]
wasmer-compiler = { path = "../compiler", version = "0.16.2" }
wasmer-compiler = { path = "../compiler", version = "0.16.2", features = ["translator"] }
wasmer-runtime = { path = "../runtime", version = "0.16.2" }
wasm-common = { path = "../wasm-common", version = "0.16.2" }
target-lexicon = { version = "0.10.0", default-features = false }

View File

@@ -12,7 +12,7 @@ use wasm_common::{FuncIndex, FuncType, LocalFuncIndex, MemoryIndex, TableIndex};
use wasmer_compiler::{
Compilation, CompileError, CompiledFunction, Compiler, CompilerConfig, CustomSection,
CustomSectionProtection, FunctionBody, FunctionBodyData, ModuleTranslationState, Relocation,
RelocationTarget, SectionIndex, Target, TrapInformation,
RelocationTarget, SectionBody, SectionIndex, Target, TrapInformation,
};
use wasmer_runtime::{MemoryPlan, Module, TablePlan, TrapCode};
@@ -71,7 +71,7 @@ impl Compiler for LLVMCompiler {
let mut used_readonly_section = false;
let mut readonly_section = CustomSection {
protection: CustomSectionProtection::Read,
bytes: Vec::new(),
bytes: SectionBody::default(),
};
for (func_index, _) in &module.functions {
@@ -112,7 +112,7 @@ impl Compiler for LLVMCompiler {
}
};
let offset = section.bytes.len() as i64;
section.bytes.extend(&custom_section.bytes);
section.bytes.append(&custom_section.bytes);
// TODO: we're needlessly rescanning the whole list.
for local_relocation in &local_relocations {
if local_relocation.local_section_index == local_idx {

View File

@@ -40,7 +40,7 @@ use wasmer_compiler::{
to_wasm_error, wasm_unsupported, Addend, CodeOffset, CompileError, CompiledFunction,
CompiledFunctionFrameInfo, CompiledFunctionUnwindInfo, CustomSection, CustomSectionProtection,
FunctionAddressMap, FunctionBody, FunctionBodyData, Relocation, RelocationKind,
RelocationTarget, SourceLoc, WasmResult,
RelocationTarget, SectionBody, SourceLoc, WasmResult,
};
use wasmer_runtime::libcalls::LibCall;
use wasmer_runtime::Module as WasmerCompilerModule;
@@ -384,7 +384,7 @@ impl FuncTranslator {
required_custom_sections.len(),
CustomSection {
protection: CustomSectionProtection::Read,
bytes: vec![],
bytes: SectionBody::default(),
},
);
for section in object.get_sections() {

View File

@@ -13,7 +13,7 @@ edition = "2018"
[dependencies]
hashbrown = { version = "0.7.2", optional = true }
wasmer-compiler = { path = "../compiler", version = "0.16.2", default-features = false }
wasmer-compiler = { path = "../compiler", version = "0.16.2", features = ["translator"], default-features = false }
wasmer-runtime = { path = "../runtime", version = "0.16.2" }
wasm-common = { path = "../wasm-common", version = "0.16.2", default-features = false }
rayon = "1.3.0"

View File

@@ -26,6 +26,10 @@ raw-cpuid = "7.0.3"
[features]
default = ["std", "enable-serde"]
# This feature is for compiler implementors, it enables using `Compiler` and
# `CompilerConfig`, as well as the included wasmparser.
# Disable this feature if you just want a headless engine.
translator = []
std = []
core = ["hashbrown"]
enable-serde = ["serde", "serde_bytes"]

View File

@@ -1,10 +1,11 @@
//! This module mainly outputs the `Compiler` trait that custom
//! compilers will need to implement.
use crate::config::Target;
use crate::error::CompileError;
use crate::function::{Compilation, FunctionBody};
use crate::std::boxed::Box;
use crate::std::vec::Vec;
use crate::target::Target;
use crate::FunctionBodyData;
use crate::ModuleTranslationState;
use wasm_common::entity::PrimaryMap;
@@ -13,6 +14,22 @@ use wasmer_runtime::Module;
use wasmer_runtime::{MemoryPlan, TablePlan};
use wasmparser::{validate, OperatorValidatorConfig, ValidatingParserConfig};
/// The compiler configuration options.
///
/// This options must have WebAssembly `Features` and a specific
/// `Target` to compile to.
pub trait CompilerConfig {
/// Gets the WebAssembly features
fn features(&self) -> &Features;
/// Gets the target that we will use for compiling
/// the WebAssembly module
fn target(&self) -> &Target;
/// Gets the custom compiler config
fn compiler(&self) -> Box<dyn Compiler>;
}
/// An implementation of a Compiler from parsed WebAssembly module to Compiled native code.
pub trait Compiler {
/// Gets the target associated with this compiler

View File

@@ -1,5 +1,4 @@
use crate::std::string::String;
use crate::translator::WasmError;
use thiserror::Error;
// Compilation Errors
@@ -28,3 +27,39 @@ pub enum CompileError {
#[error("Insufficient resources: {0}")]
Resource(String),
}
/// A WebAssembly translation error.
///
/// When a WebAssembly function can't be translated, one of these error codes will be returned
/// to describe the failure.
#[derive(Error, Debug)]
pub enum WasmError {
/// The input WebAssembly code is invalid.
///
/// This error code is used by a WebAssembly translator when it encounters invalid WebAssembly
/// code. This should never happen for validated WebAssembly code.
#[error("Invalid input WebAssembly code at offset {offset}: {message}")]
InvalidWebAssembly {
/// A string describing the validation error.
message: String,
/// The bytecode offset where the error occurred.
offset: usize,
},
/// A feature used by the WebAssembly code is not supported by the embedding environment.
///
/// Embedding environments may have their own limitations and feature restrictions.
#[error("Unsupported feature: {0}")]
Unsupported(String),
/// An implementation limit was exceeded.
#[error("Implementation limit exceeded")]
ImplLimitExceeded,
/// A generic error.
#[error("{0}")]
Generic(String),
}
/// A convenient alias for a `Result` that uses `WasmError` as the error type.
pub type WasmResult<T> = Result<T, WasmError>;

View File

@@ -5,7 +5,7 @@
//! * `jit`: to generate a JIT
//! * `obj`: to generate a native object
use crate::section::{CustomSection, SectionIndex};
use crate::section::{CustomSection, SectionBody, SectionIndex};
use crate::std::vec::Vec;
use crate::trap::TrapInformation;
use crate::{CompiledFunctionUnwindInfo, FunctionAddressMap, JumpTableOffsets, Relocation};
@@ -130,6 +130,14 @@ impl Compilation {
.map(|(_, func)| func.frame_info.clone())
.collect::<PrimaryMap<LocalFuncIndex, _>>()
}
/// Gets custom section data.
pub fn get_custom_sections(&self) -> PrimaryMap<SectionIndex, SectionBody> {
self.custom_sections
.iter()
.map(|(_, section)| section.bytes.clone())
.collect::<PrimaryMap<SectionIndex, _>>()
}
}
impl<'a> IntoIterator for &'a Compilation {

View File

@@ -38,41 +38,46 @@ use hashbrown::HashMap;
use std::collections::HashMap;
mod address_map;
#[cfg(feature = "translator")]
mod compiler;
mod config;
mod error;
mod function;
mod jump_table;
mod relocation;
mod target;
mod trap;
mod unwind;
#[cfg(feature = "translator")]
#[macro_use]
mod translator;
mod section;
mod sourceloc;
pub use crate::address_map::{FunctionAddressMap, InstructionAddressMap};
pub use crate::compiler::Compiler;
pub use crate::config::{
Architecture, CallingConvention, CompilerConfig, CpuFeature, Features, OperatingSystem, Target,
Triple,
};
pub use crate::error::CompileError;
#[cfg(feature = "translator")]
pub use crate::compiler::{Compiler, CompilerConfig};
pub use crate::error::{CompileError, WasmError, WasmResult};
pub use crate::function::{
Compilation, CompiledFunction, CompiledFunctionFrameInfo, CustomSections, FunctionBody,
Functions,
};
pub use crate::jump_table::{JumpTable, JumpTableOffsets};
pub use crate::relocation::{Relocation, RelocationKind, RelocationTarget, Relocations};
pub use crate::section::{CustomSection, CustomSectionProtection, SectionIndex};
pub use crate::section::{CustomSection, CustomSectionProtection, SectionBody, SectionIndex};
pub use crate::sourceloc::SourceLoc;
pub use crate::target::{
Architecture, CallingConvention, CpuFeature, OperatingSystem, Target, Triple,
};
#[cfg(feature = "translator")]
pub use crate::translator::{
to_wasm_error, translate_module, FunctionBodyData, ModuleEnvironment, ModuleTranslation,
ModuleTranslationState, WasmError, WasmResult,
ModuleTranslationState,
};
pub use crate::trap::TrapInformation;
pub use crate::unwind::{CompiledFunctionUnwindInfo, FDERelocEntry, FunctionTableReloc};
pub use wasm_common::Features;
/// wasmparser is exported as a module to slim compiler dependencies
pub mod wasmparser {
pub use wasmparser::*;

View File

@@ -46,6 +46,31 @@ pub struct CustomSection {
/// > (the start of the memory pointer).
/// > We might need to create another field for alignment in case it's
/// > needed in the future.
#[serde(with = "serde_bytes")]
pub bytes: Vec<u8>,
pub bytes: SectionBody,
}
/// The bytes in the section.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
pub struct SectionBody(#[serde(with = "serde_bytes")] Vec<u8>);
impl SectionBody {
/// Extend the section with the bytes given.
pub fn extend(&mut self, contents: &[u8]) {
self.0.extend(contents);
}
/// Extends the section by appending bytes from another section.
pub fn append(&mut self, body: &SectionBody) {
self.0.extend(&body.0);
}
/// Returns a raw pointer to the section's buffer.
pub fn as_ptr(&self) -> *const u8 {
self.0.as_ptr()
}
/// Returns the length of this section in bytes.
pub fn len(&self) -> usize {
self.0.len()
}
}

View File

@@ -1,10 +1,8 @@
//! The configuration for the
use crate::compiler::Compiler;
use crate::std::boxed::Box;
//! Target configuration
use enumset::{EnumSet, EnumSetType};
pub use target_lexicon::{Architecture, CallingConvention, OperatingSystem, Triple};
pub use wasm_common::Features;
use crate::std::boxed::Box;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
use raw_cpuid::CpuId;
@@ -140,19 +138,3 @@ impl Default for Target {
}
}
}
/// The compiler configuration options.
///
/// This options must have WebAssembly `Features` and a specific
/// `Target` to compile to.
pub trait CompilerConfig {
/// Gets the WebAssembly features
fn features(&self) -> &Features;
/// Gets the target that we will use for compiling
/// the WebAssembly module
fn target(&self) -> &Target;
/// Gets the custom compiler config
fn compiler(&self) -> Box<dyn Compiler>;
}

View File

@@ -1,9 +1,9 @@
use super::error::{WasmError, WasmResult};
use super::module::translate_module;
use super::state::ModuleTranslationState;
use crate::std::borrow::ToOwned;
use crate::std::string::ToString;
use crate::std::{boxed::Box, string::String, vec::Vec};
use crate::{WasmError, WasmResult};
use std::convert::TryFrom;
use std::sync::Arc;
use wasm_common::entity::PrimaryMap;

View File

@@ -1,39 +1,7 @@
use crate::std::string::String;
use crate::WasmError;
use thiserror::Error;
use wasmparser::BinaryReaderError;
/// A WebAssembly translation error.
///
/// When a WebAssembly function can't be translated, one of these error codes will be returned
/// to describe the failure.
#[derive(Error, Debug)]
pub enum WasmError {
/// The input WebAssembly code is invalid.
///
/// This error code is used by a WebAssembly translator when it encounters invalid WebAssembly
/// code. This should never happen for validated WebAssembly code.
#[error("Invalid input WebAssembly code at offset {offset}: {message}")]
InvalidWebAssembly {
/// A string describing the validation error.
message: String,
/// The bytecode offset where the error occurred.
offset: usize,
},
/// A feature used by the WebAssembly code is not supported by the embedding environment.
///
/// Embedding environments may have their own limitations and feature restrictions.
#[error("Unsupported feature: {0}")]
Unsupported(String),
/// An implementation limit was exceeded.
#[error("Implementation limit exceeded")]
ImplLimitExceeded,
/// A generic error.
#[error("{0}")]
Generic(String),
}
/// Return an `Err(WasmError::Unsupported(msg))` where `msg` the string built by calling `format!`
/// on the arguments to this macro.
#[macro_export]
@@ -48,6 +16,3 @@ pub fn to_wasm_error(e: BinaryReaderError) -> WasmError {
offset: e.offset(),
}
}
/// A convenient alias for a `Result` that uses `WasmError` as the error type.
pub type WasmResult<T> = Result<T, WasmError>;

View File

@@ -13,6 +13,6 @@ mod error;
mod sections;
pub use self::environ::{FunctionBodyData, ModuleEnvironment, ModuleTranslation};
pub use self::error::{to_wasm_error, WasmError, WasmResult};
pub use self::error::to_wasm_error;
pub use self::module::translate_module;
pub use self::state::ModuleTranslationState;

View File

@@ -1,13 +1,14 @@
//! Translation skeleton that traverses the whole WebAssembly module and call helper functions
//! to deal with each part of it.
use super::environ::ModuleEnvironment;
use super::error::{to_wasm_error, WasmResult};
use super::error::to_wasm_error;
use super::sections::{
parse_code_section, parse_data_section, parse_element_section, parse_export_section,
parse_function_section, parse_global_section, parse_import_section, parse_memory_section,
parse_name_section, parse_start_section, parse_table_section, parse_type_section,
};
use super::state::ModuleTranslationState;
use crate::WasmResult;
use wasmparser::{CustomSectionContent, ModuleReader, SectionContent};
/// Translate a sequence of bytes forming a valid Wasm binary into a

View File

@@ -8,9 +8,10 @@
//! is handled, according to the semantics of WebAssembly, to only specific expressions that are
//! interpreted on the fly.
use super::environ::ModuleEnvironment;
use super::error::{to_wasm_error, WasmError, WasmResult};
use super::error::to_wasm_error;
use super::state::ModuleTranslationState;
use crate::{wasm_unsupported, HashMap};
use crate::{WasmError, WasmResult};
use core::convert::TryFrom;
use std::boxed::Box;
use std::vec::Vec;

View File

@@ -29,5 +29,10 @@ lazy_static = "1.4"
[target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3.8", features = ["winnt", "impl-default"] }
[features]
# Enable the `compiler` feature if you want the engine to compile
# and not be only on headless mode.
compiler = []
[badges]
maintenance = { status = "actively-developed" }

View File

@@ -9,11 +9,10 @@ use std::cell::RefCell;
use std::collections::HashMap;
use std::sync::Arc;
use wasm_common::entity::PrimaryMap;
use wasm_common::{FuncType, LocalFuncIndex, MemoryIndex, TableIndex};
use wasmer_compiler::{
Compilation, CompileError, Compiler as BaseCompiler, CompilerConfig, FunctionBody,
FunctionBodyData, ModuleTranslationState, Target,
};
use wasm_common::{FuncType, LocalFuncIndex, MemoryIndex, SignatureIndex, TableIndex};
use wasmer_compiler::{Compilation, CompileError, FunctionBody, Target};
#[cfg(feature = "compiler")]
use wasmer_compiler::{Compiler, CompilerConfig};
use wasmer_runtime::{
InstanceHandle, MemoryPlan, Module, SignatureRegistry, TablePlan, VMFunctionBody,
VMSharedSignatureIndex, VMTrampoline,
@@ -29,7 +28,8 @@ pub struct JITEngine {
impl JITEngine {
const MAGIC_HEADER: &'static [u8] = b"\0wasmer-jit";
/// Create a new JIT Engine given config
/// Create a new `JITEngine` with the given config
#[cfg(feature = "compiler")]
pub fn new<C: CompilerConfig>(config: &C, tunables: impl Tunables + 'static) -> Self
where
C: ?Sized,
@@ -37,7 +37,33 @@ impl JITEngine {
let compiler = config.compiler();
Self {
inner: Arc::new(RefCell::new(JITEngineInner {
compiler,
compiler: Some(compiler),
trampolines: HashMap::new(),
code_memory: CodeMemory::new(),
signatures: SignatureRegistry::new(),
})),
tunables: Arc::new(Box::new(tunables)),
}
}
/// Create a headless `JITEngine`
///
/// A headless engine is an engine without any compiler attached.
/// This is useful for assuring a minimal runtime for running
/// WebAssembly modules.
///
/// For example, for running in IoT devices where compilers are very
/// expensive, or also to optimize startup speed.
///
/// # Important
///
/// Headless engines can't compile or validate any modules,
/// they just take already processed Modules (via `Module::serialize`).
pub fn headless(tunables: impl Tunables + 'static) -> Self {
Self {
inner: Arc::new(RefCell::new(JITEngineInner {
#[cfg(feature = "compiler")]
compiler: None,
trampolines: HashMap::new(),
code_memory: CodeMemory::new(),
signatures: SignatureRegistry::new(),
@@ -93,7 +119,7 @@ impl JITEngine {
}
/// Instantiates a WebAssembly module
pub fn instantiate(
pub unsafe fn instantiate(
&self,
compiled_module: &CompiledModule,
resolver: &dyn Resolver,
@@ -126,7 +152,8 @@ impl JITEngine {
/// The inner contents of `JITEngine`
pub struct JITEngineInner {
/// The compiler
compiler: Box<dyn BaseCompiler>,
#[cfg(feature = "compiler")]
compiler: Option<Box<dyn Compiler>>,
/// Pointers to trampoline functions used to enter particular signatures
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
/// The code memory is responsible of publishing the compiled
@@ -139,38 +166,34 @@ pub struct JITEngineInner {
impl JITEngineInner {
/// Gets the compiler associated to this JIT
pub fn compiler(&self) -> &dyn BaseCompiler {
&*self.compiler
#[cfg(feature = "compiler")]
pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> {
if self.compiler.is_none() {
return Err(CompileError::Codegen("The JITEngine is operating in headless mode, so it can only execute already compiled Modules.".to_string()));
}
Ok(&**self.compiler.as_ref().unwrap())
}
/// Validate the module
#[cfg(feature = "compiler")]
pub fn validate<'data>(&self, data: &'data [u8]) -> Result<(), CompileError> {
self.compiler().validate_module(data)
self.compiler()?.validate_module(data)
}
/// Validate the module
#[cfg(not(feature = "compiler"))]
pub fn validate<'data>(&self, data: &'data [u8]) -> Result<(), CompileError> {
Err(CompileError::Validate(
"Validation is only enabled with the compiler feature".to_string(),
))
}
/// Compile the given function bodies.
pub(crate) fn compile_module<'data>(
&self,
module: &Module,
module_translation: &ModuleTranslationState,
function_body_inputs: PrimaryMap<LocalFuncIndex, FunctionBodyData<'data>>,
memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
table_plans: PrimaryMap<TableIndex, TablePlan>,
) -> Result<Compilation, CompileError> {
self.compiler.compile_module(
module,
module_translation,
function_body_inputs,
memory_plans,
table_plans,
)
}
/// Compile the given function bodies.
pub(crate) fn compile<'data>(
pub(crate) fn allocate<'data>(
&mut self,
module: &Module,
functions: &PrimaryMap<LocalFuncIndex, FunctionBody>,
trampolines: &PrimaryMap<SignatureIndex, FunctionBody>,
) -> Result<PrimaryMap<LocalFuncIndex, *mut [VMFunctionBody]>, CompileError> {
// Allocate all of the compiled functions into executable memory,
// copying over their contents.
@@ -184,35 +207,27 @@ impl JITEngineInner {
))
})?;
// Trampoline generation.
// We do it in two steps:
// 1. Generate only the trampolines for the signatures that are unique
// 2. Push the compiled code to memory
let mut unique_signatures: HashMap<VMSharedSignatureIndex, FuncType> = HashMap::new();
// for sig in module.exported_signatures() {
for sig in module.signatures.values() {
let index = self.signatures.register(&sig);
if unique_signatures.contains_key(&index) {
for (sig_index, compiled_function) in trampolines.iter() {
let func_type = module.signatures.get(sig_index).unwrap();
let index = self.signatures.register(&func_type);
if self.trampolines.contains_key(&index) {
// We don't need to allocate the trampoline in case
// it's signature is already allocated.
continue;
}
unique_signatures.insert(index, sig.clone());
}
let compiled_trampolines = self
.compiler
.compile_wasm_trampolines(&unique_signatures.values().cloned().collect::<Vec<_>>())?;
for ((index, _), compiled_function) in
unique_signatures.iter().zip(compiled_trampolines.iter())
{
let ptr = self
.code_memory
.allocate_for_function(&compiled_function)
.map_err(|message| CompileError::Resource(message))?
.map_err(|message| {
CompileError::Resource(format!(
"failed to allocate memory for trampolines: {}",
message
))
})?
.as_ptr();
let trampoline =
unsafe { std::mem::transmute::<*const VMFunctionBody, VMTrampoline>(ptr) };
self.trampolines.insert(*index, trampoline);
self.trampolines.insert(index, trampoline);
}
Ok(allocated_functions)
}

View File

@@ -48,7 +48,5 @@ pub use crate::resolver::{resolve_imports, NullResolver, Resolver};
pub use crate::trap::*;
pub use crate::tunables::Tunables;
pub use wasmer_compiler::CompilerConfig;
/// Version number of this crate.
pub const VERSION: &str = env!("CARGO_PKG_VERSION");

View File

@@ -3,7 +3,10 @@
use std::ptr::write_unaligned;
use wasm_common::entity::{EntityRef, PrimaryMap};
use wasm_common::LocalFuncIndex;
use wasmer_compiler::{JumpTable, JumpTableOffsets, RelocationKind, RelocationTarget, Relocations};
use wasmer_compiler::{
JumpTable, JumpTableOffsets, RelocationKind, RelocationTarget, Relocations, SectionBody,
SectionIndex,
};
use wasmer_runtime::Module;
use wasmer_runtime::VMFunctionBody;
@@ -15,6 +18,7 @@ pub fn link_module(
allocated_functions: &PrimaryMap<LocalFuncIndex, *mut [VMFunctionBody]>,
jt_offsets: &PrimaryMap<LocalFuncIndex, JumpTableOffsets>,
relocations: Relocations,
allocated_sections: &PrimaryMap<SectionIndex, SectionBody>,
) {
for (i, function_relocs) in relocations.into_iter() {
for r in function_relocs {
@@ -24,8 +28,8 @@ pub fn link_module(
fatptr as *const VMFunctionBody as usize
}
RelocationTarget::LibCall(libcall) => libcall.function_pointer(),
RelocationTarget::CustomSection(_custom_section) => {
unimplemented!("Custom Sections not yet implemented");
RelocationTarget::CustomSection(custom_section) => {
allocated_sections[custom_section].as_ptr() as usize
}
RelocationTarget::JumpTable(func_index, jt) => {
let offset = *jt_offsets

View File

@@ -1,7 +1,5 @@
//! 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.
//! Define `CompiledModule` to allow compiling and instantiating to be
//! done as separate steps.
use crate::engine::{JITEngine, JITEngineInner};
use crate::error::{DeserializeError, SerializeError};
@@ -14,6 +12,7 @@ use crate::serialize::{
use crate::trap::register as register_frame_info;
use crate::trap::GlobalFrameInfoRegistration;
use crate::trap::RuntimeError;
use crate::tunables::Tunables;
use serde::{Deserialize, Serialize};
use std::any::Any;
use std::sync::{Arc, Mutex};
@@ -23,6 +22,7 @@ use wasm_common::{
MemoryIndex, OwnedDataInitializer, SignatureIndex, TableIndex,
};
use wasmer_compiler::CompileError;
#[cfg(feature = "compiler")]
use wasmer_compiler::ModuleEnvironment;
use wasmer_runtime::{
InstanceHandle, LinearMemory, Module, SignatureRegistry, Table, VMFunctionBody,
@@ -42,6 +42,7 @@ pub struct CompiledModule {
impl CompiledModule {
/// Compile a data buffer into a `CompiledModule`, which may then be instantiated.
#[cfg(feature = "compiler")]
pub fn new(jit: &JITEngine, data: &[u8]) -> Result<Self, CompileError> {
let environ = ModuleEnvironment::new();
let mut jit_compiler = jit.compiler_mut();
@@ -64,13 +65,29 @@ impl CompiledModule {
.map(|(_index, table_type)| tunables.table_plan(*table_type))
.collect();
let compilation = jit_compiler.compile_module(
let compiler = jit_compiler.compiler()?;
// Compile the Module
let compilation = compiler.compile_module(
&translation.module,
translation.module_translation.as_ref().unwrap(),
translation.function_body_inputs,
memory_plans.clone(),
table_plans.clone(),
)?;
// Compile the trampolines
let func_types = translation
.module
.signatures
.values()
.cloned()
.collect::<Vec<_>>();
let trampolines = compiler
.compile_wasm_trampolines(&func_types)?
.into_iter()
.collect::<PrimaryMap<SignatureIndex, _>>();
let data_initializers = translation
.data_initializers
.iter()
@@ -89,10 +106,13 @@ impl CompiledModule {
function_relocations: compilation.get_relocations(),
function_jt_offsets: compilation.get_jt_offsets(),
function_frame_info: frame_infos,
trampolines,
custom_sections: compilation.get_custom_sections(),
};
let serializable = SerializableModule {
compilation: serializable_compilation,
module: Arc::new(translation.module),
features: jit_compiler.compiler()?.features().clone(),
data_initializers,
memory_plans,
table_plans,
@@ -100,6 +120,14 @@ impl CompiledModule {
Self::from_parts(&mut jit_compiler, serializable)
}
/// Compile a data buffer into a `CompiledModule`, which may then be instantiated.
#[cfg(not(feature = "compiler"))]
pub fn new(jit: &JITEngine, data: &[u8]) -> Result<Self, CompileError> {
Err(CompileError::Codegen(
"Compilation is not enabled in the engine".to_string(),
))
}
/// Serialize a CompiledModule
pub fn serialize(&self) -> Result<Vec<u8>, SerializeError> {
// let mut s = flexbuffers::FlexbufferSerializer::new();
@@ -126,9 +154,10 @@ impl CompiledModule {
jit_compiler: &mut JITEngineInner,
serializable: SerializableModule,
) -> Result<Self, CompileError> {
let finished_functions = jit_compiler.compile(
let finished_functions = jit_compiler.allocate(
&serializable.module,
&serializable.compilation.function_bodies,
&serializable.compilation.trampolines,
)?;
link_module(
@@ -136,6 +165,7 @@ impl CompiledModule {
&finished_functions,
&serializable.compilation.function_jt_offsets,
serializable.compilation.function_relocations.clone(),
&serializable.compilation.custom_sections,
);
// Compute indices into the shared signature table.
@@ -170,10 +200,6 @@ impl CompiledModule {
/// 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.
///
/// # Unsafety
///
/// See `InstanceHandle::new`
@@ -184,17 +210,8 @@ impl CompiledModule {
host_state: Box<dyn Any>,
) -> Result<InstanceHandle, InstantiationError> {
let jit_compiler = jit.compiler();
let is_bulk_memory: bool = jit_compiler.compiler().features().bulk_memory;
let tunables = jit.tunables();
let sig_registry: &SignatureRegistry = jit_compiler.signatures();
let data_initializers = self
.serializable
.data_initializers
.iter()
.map(|init| DataInitializer {
location: init.location.clone(),
data: &*init.data,
})
.collect::<Vec<_>>();
let imports = resolve_imports(
&self.serializable.module,
&sig_registry,
@@ -204,10 +221,18 @@ impl CompiledModule {
)
.map_err(InstantiationError::Link)?;
let finished_memories = create_memories(&self.serializable.module, self.memory_plans())
.map_err(InstantiationError::Link)?;
let finished_tables = create_tables(&self.serializable.module, self.table_plans());
let finished_globals = create_globals(&self.serializable.module);
let finished_memories = tunables
.create_memories(&self.serializable.module, self.memory_plans())
.map_err(InstantiationError::Link)?
.into_boxed_slice();
let finished_tables = tunables
.create_tables(&self.serializable.module, self.table_plans())
.map_err(InstantiationError::Link)?
.into_boxed_slice();
let finished_globals = tunables
.create_globals(&self.serializable.module)
.map_err(InstantiationError::Link)?
.into_boxed_slice();
// Register the frame info for the module
self.register_frame_info();
@@ -219,14 +244,39 @@ impl CompiledModule {
finished_tables,
finished_globals,
imports,
&data_initializers,
self.signatures.clone(),
is_bulk_memory,
host_state,
)
.map_err(|trap| InstantiationError::Start(RuntimeError::from_trap(trap)))
}
/// Finish instantiation of a `InstanceHandle`
///
/// # Unsafety
///
/// See `InstanceHandle::finish_instantiation`
pub unsafe fn finish_instantiation(
&self,
handle: &InstanceHandle,
) -> Result<(), InstantiationError> {
let is_bulk_memory: bool = self.serializable.features.bulk_memory;
handle
.finish_instantiation(is_bulk_memory, &self.data_initializers())
.map_err(|trap| InstantiationError::Start(RuntimeError::from_trap(trap)))
}
/// Returns data initializers to pass to `InstanceHandle::initialize`
pub fn data_initializers(&self) -> Vec<DataInitializer<'_>> {
self.serializable
.data_initializers
.iter()
.map(|init| DataInitializer {
location: init.location.clone(),
data: &*init.data,
})
.collect::<Vec<_>>()
}
/// Return a reference-counting pointer to a module.
pub fn module(&self) -> &Arc<Module> {
&self.serializable.module
@@ -259,46 +309,3 @@ impl CompiledModule {
));
}
}
/// Allocate memory for just the memories of the current module.
fn create_memories(
module: &Module,
memory_plans: &PrimaryMap<MemoryIndex, MemoryPlan>,
) -> Result<BoxedSlice<LocalMemoryIndex, LinearMemory>, LinkError> {
let num_imports = module.num_imported_memories;
let mut memories: PrimaryMap<LocalMemoryIndex, _> =
PrimaryMap::with_capacity(module.memories.len() - num_imports);
for index in num_imports..module.memories.len() {
let plan = memory_plans[MemoryIndex::new(index)].clone();
memories.push(LinearMemory::new(&plan).map_err(LinkError::Resource)?);
}
Ok(memories.into_boxed_slice())
}
/// Allocate memory for just the tables of the current module.
fn create_tables(
module: &Module,
table_plans: &PrimaryMap<TableIndex, TablePlan>,
) -> BoxedSlice<LocalTableIndex, Table> {
let num_imports = module.num_imported_tables;
let mut tables: PrimaryMap<LocalTableIndex, _> =
PrimaryMap::with_capacity(module.tables.len() - num_imports);
for index in num_imports..module.tables.len() {
let plan = table_plans[TableIndex::new(index)].clone();
tables.push(Table::new(&plan));
}
tables.into_boxed_slice()
}
/// Allocate memory for just the globals of the current module,
/// with initializers applied.
fn create_globals(module: &Module) -> BoxedSlice<LocalGlobalIndex, VMGlobalDefinition> {
let num_imports = module.num_imported_globals;
let mut vmctx_globals = PrimaryMap::with_capacity(module.globals.len() - num_imports);
for _ in &module.globals.values().as_slice()[num_imports..] {
vmctx_globals.push(VMGlobalDefinition::new());
}
vmctx_globals.into_boxed_slice()
}

View File

@@ -3,7 +3,6 @@
use crate::error::{ImportError, LinkError};
use more_asserts::assert_ge;
use std::collections::HashSet;
use wasm_common::entity::PrimaryMap;
use wasm_common::{ExternType, ImportIndex, MemoryIndex, TableIndex};
use wasmer_runtime::{
@@ -95,8 +94,6 @@ pub fn resolve_imports(
memory_plans: &PrimaryMap<MemoryIndex, MemoryPlan>,
_table_plans: &PrimaryMap<TableIndex, TablePlan>,
) -> Result<Imports, LinkError> {
let dependencies = HashSet::new();
let mut function_imports = PrimaryMap::with_capacity(module.num_imported_funcs);
let mut table_imports = PrimaryMap::with_capacity(module.num_imported_tables);
let mut memory_imports = PrimaryMap::with_capacity(module.num_imported_memories);
@@ -125,16 +122,12 @@ pub fn resolve_imports(
}
match resolved {
Export::Function(ref f) => {
// TODO: Syrus - fix this
// dependencies.insert(unsafe { InstanceHandle::from_vmctx(f.vmctx) });
function_imports.push(VMFunctionImport {
body: f.address,
vmctx: f.vmctx,
});
}
Export::Table(ref t) => {
// TODO: Syrus - fix this
// dependencies.insert(unsafe { InstanceHandle::from_vmctx(t.vmctx) });
table_imports.push(VMTableImport {
definition: t.definition,
from: t.from,
@@ -168,16 +161,13 @@ pub fn resolve_imports(
}
}
// TODO: Syrus - fix this
// dependencies.insert(unsafe { InstanceHandle::from_vmctx(m.vmctx) });
memory_imports.push(VMMemoryImport {
definition: m.definition,
from: m.from,
});
}
Export::Global(ref g) => {
// TODO: Syrus - fix this
// dependencies.insert(unsafe { InstanceHandle::from_vmctx(g.vmctx) });
global_imports.push(VMGlobalImport {
definition: g.definition,
});
@@ -186,7 +176,6 @@ pub fn resolve_imports(
}
Ok(Imports::new(
dependencies,
function_imports,
table_imports,
memory_imports,

View File

@@ -3,11 +3,16 @@ use serde::ser::Serializer;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::sync::Arc;
use wasmer_compiler::{CompiledFunctionFrameInfo, FunctionBody, JumpTableOffsets, Relocation};
use wasmer_compiler::{
CompiledFunctionFrameInfo, FunctionBody, JumpTableOffsets, Relocation, SectionBody,
SectionIndex,
};
use wasmer_runtime::Module;
use wasm_common::entity::PrimaryMap;
use wasm_common::{LocalFuncIndex, MemoryIndex, OwnedDataInitializer, TableIndex};
use wasm_common::{
Features, LocalFuncIndex, MemoryIndex, OwnedDataInitializer, SignatureIndex, TableIndex,
};
use wasmer_runtime::{MemoryPlan, TablePlan};
/// The compilation related data for a serialized modules
@@ -20,6 +25,8 @@ pub struct SerializableCompilation {
// to allow lazy frame_info deserialization, we convert it to it's lazy binary
// format upon serialization.
pub function_frame_info: PrimaryMap<LocalFuncIndex, SerializableFunctionFrameInfo>,
pub trampolines: PrimaryMap<SignatureIndex, FunctionBody>,
pub custom_sections: PrimaryMap<SectionIndex, SectionBody>,
}
/// Serializable struct that is able to serialize from and to
@@ -27,6 +34,7 @@ pub struct SerializableCompilation {
#[derive(Serialize, Deserialize)]
pub struct SerializableModule {
pub compilation: SerializableCompilation,
pub features: Features,
pub module: Arc<Module>,
pub data_initializers: Box<[OwnedDataInitializer]>,
// Plans for that module

View File

@@ -1,5 +1,10 @@
use wasm_common::{MemoryType, TableType};
use wasmer_runtime::{LinearMemory, Table};
use crate::error::LinkError;
use wasm_common::entity::{EntityRef, PrimaryMap};
use wasm_common::{
GlobalIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex, MemoryType,
TableIndex, TableType,
};
use wasmer_runtime::{LinearMemory, Module, Table, VMGlobalDefinition};
use wasmer_runtime::{MemoryPlan, TablePlan};
/// Tunables for an engine
@@ -11,8 +16,56 @@ pub trait Tunables {
fn table_plan(&self, table: TableType) -> TablePlan;
/// Create a memory given a memory type
fn create_memory(&self, memory_type: MemoryType) -> Result<LinearMemory, String>;
fn create_memory(&self, memory_type: MemoryPlan) -> Result<LinearMemory, String>;
/// Create a memory given a memory type
fn create_table(&self, table_type: TableType) -> Table;
fn create_table(&self, table_type: TablePlan) -> Result<Table, String>;
/// Allocate memory for just the memories of the current module.
fn create_memories(
&self,
module: &Module,
memory_plans: &PrimaryMap<MemoryIndex, MemoryPlan>,
) -> Result<PrimaryMap<LocalMemoryIndex, LinearMemory>, LinkError> {
let num_imports = module.num_imported_memories;
let mut memories: PrimaryMap<LocalMemoryIndex, _> =
PrimaryMap::with_capacity(module.memories.len() - num_imports);
for index in num_imports..module.memories.len() {
let plan = memory_plans[MemoryIndex::new(index)].clone();
memories.push(self.create_memory(plan).map_err(LinkError::Resource)?);
}
Ok(memories)
}
/// Allocate memory for just the tables of the current module.
fn create_tables(
&self,
module: &Module,
table_plans: &PrimaryMap<TableIndex, TablePlan>,
) -> Result<PrimaryMap<LocalTableIndex, Table>, LinkError> {
let num_imports = module.num_imported_tables;
let mut tables: PrimaryMap<LocalTableIndex, _> =
PrimaryMap::with_capacity(module.tables.len() - num_imports);
for index in num_imports..module.tables.len() {
let plan = table_plans[TableIndex::new(index)].clone();
tables.push(self.create_table(plan).map_err(LinkError::Resource)?);
}
Ok(tables)
}
/// Allocate memory for just the globals of the current module,
/// with initializers applied.
fn create_globals(
&self,
module: &Module,
) -> Result<PrimaryMap<LocalGlobalIndex, VMGlobalDefinition>, LinkError> {
let num_imports = module.num_imported_globals;
let mut vmctx_globals = PrimaryMap::with_capacity(module.globals.len() - num_imports);
for _ in &module.globals.values().as_slice()[num_imports..] {
vmctx_globals.push(VMGlobalDefinition::new());
}
Ok(vmctx_globals)
}
}

View File

@@ -1,4 +1,3 @@
use crate::instance::InstanceHandle;
use crate::vmcontext::{VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport};
use std::collections::HashSet;
use wasm_common::entity::{BoxedSlice, PrimaryMap};
@@ -7,9 +6,6 @@ use wasm_common::{FuncIndex, GlobalIndex, MemoryIndex, TableIndex};
/// Resolved import pointers.
#[derive(Clone)]
pub struct Imports {
/// The set of instances that the imports depend on.
pub dependencies: HashSet<InstanceHandle>,
/// Resolved addresses for imported functions.
pub functions: BoxedSlice<FuncIndex, VMFunctionImport>,
@@ -26,14 +22,12 @@ pub struct Imports {
impl Imports {
/// Construct a new `Imports` instance.
pub fn new(
dependencies: HashSet<InstanceHandle>,
function_imports: PrimaryMap<FuncIndex, VMFunctionImport>,
table_imports: PrimaryMap<TableIndex, VMTableImport>,
memory_imports: PrimaryMap<MemoryIndex, VMMemoryImport>,
global_imports: PrimaryMap<GlobalIndex, VMGlobalImport>,
) -> Self {
Self {
dependencies,
functions: function_imports.into_boxed_slice(),
tables: table_imports.into_boxed_slice(),
memories: memory_imports.into_boxed_slice(),
@@ -44,7 +38,6 @@ impl Imports {
/// Construct a new `Imports` instance with no imports.
pub fn none() -> Self {
Self {
dependencies: HashSet::new(),
functions: PrimaryMap::new().into_boxed_slice(),
tables: PrimaryMap::new().into_boxed_slice(),
memories: PrimaryMap::new().into_boxed_slice(),

View File

@@ -35,7 +35,7 @@ cfg_if::cfg_if! {
impl InstanceHandle {
/// Set a custom signal handler
pub fn set_signal_handler<H>(&mut self, handler: H)
pub fn set_signal_handler<H>(&self, handler: H)
where
H: 'static + Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool,
{
@@ -47,7 +47,7 @@ cfg_if::cfg_if! {
impl InstanceHandle {
/// Set a custom signal handler
pub fn set_signal_handler<H>(&mut self, handler: H)
pub fn set_signal_handler<H>(&self, handler: H)
where
H: 'static + Fn(winapi::um::winnt::PEXCEPTION_POINTERS) -> bool,
{
@@ -62,14 +62,6 @@ cfg_if::cfg_if! {
/// This is repr(C) to ensure that the vmctx field is last.
#[repr(C)]
pub(crate) struct Instance {
/// The number of references to this `Instance`.
refcount: Cell<usize>,
/// `Instance`s from which this `Instance` imports. These won't
/// create reference cycles because wasm instances can't cyclically
/// import from each other.
dependencies: HashSet<InstanceHandle>,
/// The `Module` this `Instance` was instantiated from.
module: Arc<Module>,
@@ -788,9 +780,7 @@ impl InstanceHandle {
finished_tables: BoxedSlice<LocalTableIndex, Table>,
finished_globals: BoxedSlice<LocalGlobalIndex, VMGlobalDefinition>,
imports: Imports,
data_initializers: &[DataInitializer<'_>],
vmshared_signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
is_bulk_memory: bool,
host_state: Box<dyn Any>,
) -> Result<Self, Trap> {
let vmctx_tables = finished_tables
@@ -813,8 +803,6 @@ impl InstanceHandle {
let handle = {
let instance = Instance {
refcount: Cell::new(1),
dependencies: imports.dependencies,
module,
offsets,
memories: finished_memories,
@@ -883,29 +871,42 @@ impl InstanceHandle {
VMBuiltinFunctionsArray::initialized(),
);
// Ensure that our signal handlers are ready for action.
init_traps();
// Perform infallible initialization in this constructor, while fallible
// initialization is deferred to the `initialize` method.
initialize_passive_elements(instance);
initialize_globals(instance);
Ok(handle)
}
/// Finishes the instantiation process started by `Instance::new`.
///
/// Only safe to call immediately after instantiation.
pub unsafe fn finish_instantiation(
&self,
is_bulk_memory: bool,
data_initializers: &[DataInitializer<'_>],
) -> Result<(), Trap> {
// Check initializer bounds before initializing anything. Only do this
// when bulk memory is disabled, since the bulk memory proposal changes
// instantiation such that the intermediate results of failed
// initializations are visible.
if !is_bulk_memory {
check_table_init_bounds(instance)?;
check_memory_init_bounds(instance, data_initializers)?;
check_table_init_bounds(self.instance())?;
check_memory_init_bounds(self.instance(), data_initializers)?;
}
// Apply the initializers.
initialize_tables(instance)?;
initialize_passive_elements(instance);
initialize_memories(instance, data_initializers)?;
initialize_globals(instance);
// Ensure that our signal handlers are ready for action.
init_traps();
initialize_tables(self.instance())?;
initialize_memories(self.instance(), data_initializers)?;
// The WebAssembly spec specifies that the start function is
// invoked automatically at instantiation time.
instance.invoke_start_function()?;
Ok(handle)
self.instance().invoke_start_function()?;
Ok(())
}
/// Create a new `InstanceHandle` pointing at the instance
@@ -916,7 +917,6 @@ impl InstanceHandle {
/// be a `VMContext` allocated as part of an `Instance`.
pub unsafe fn from_vmctx(vmctx: *mut VMContext) -> Self {
let instance = (&mut *vmctx).instance();
instance.refcount.set(instance.refcount.get() + 1);
Self {
instance: instance as *const Instance as *mut Instance,
}
@@ -1031,32 +1031,36 @@ impl InstanceHandle {
pub(crate) fn instance(&self) -> &Instance {
unsafe { &*(self.instance as *const Instance) }
}
/// Deallocates memory associated with this instance.
///
/// Note that this is unsafe because there might be other handles to this
/// `InstanceHandle` elsewhere, and there's nothing preventing usage of
/// this handle after this function is called.
pub unsafe fn dealloc(&self) {
let instance = self.instance();
let layout = instance.alloc_layout();
ptr::drop_in_place(self.instance);
alloc::dealloc(self.instance.cast(), layout);
}
}
impl Clone for InstanceHandle {
fn clone(&self) -> Self {
let instance = self.instance();
instance.refcount.set(instance.refcount.get() + 1);
Self {
instance: self.instance,
}
}
}
impl Drop for InstanceHandle {
fn drop(&mut self) {
let instance = self.instance();
let count = instance.refcount.get();
instance.refcount.set(count - 1);
if count == 1 {
let layout = instance.alloc_layout();
unsafe {
ptr::drop_in_place(self.instance);
alloc::dealloc(self.instance.cast(), layout);
}
}
}
}
// TODO: uncomment this, as we need to store the handles
// in the store, and once the store is dropped, then the instances
// will too.
// impl Drop for InstanceHandle {
// fn drop(&mut self) {
// unsafe { self.dealloc() }
// }
// }
fn check_table_init_bounds(instance: &Instance) -> Result<(), Trap> {
let module = Arc::clone(&instance.module);

View File

@@ -19,20 +19,22 @@ pub struct Table {
impl Table {
/// Create a new table instance with specified minimum and maximum number of elements.
pub fn new(plan: &TablePlan) -> Self {
pub fn new(plan: &TablePlan) -> Result<Self, String> {
match plan.table.ty {
Type::FuncRef => (),
ty => unimplemented!("tables of types other than anyfunc ({})", ty),
ty => return Err(format!("tables of types other than anyfunc ({})", ty)),
};
match plan.style {
TableStyle::CallerChecksSignature => Self {
TableStyle::CallerChecksSignature => Ok(Self {
vec: RefCell::new(vec![
VMCallerCheckedAnyfunc::default();
usize::try_from(plan.table.minimum).unwrap()
usize::try_from(plan.table.minimum).map_err(|_| {
"Table minimum is bigger than usize".to_string()
})?
]),
maximum: plan.table.maximum,
plan: plan.clone(),
},
}),
}
}

View File

@@ -1,8 +1,12 @@
#[cfg(feature = "enable-serde")]
use serde::{Deserialize, Serialize};
/// Controls which experimental features will be enabled.
/// Features usually have a corresponding [WebAssembly proposal].
///
/// [WebAssembly proposal]: https://github.com/WebAssembly/proposals
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct Features {
/// Threads proposal should be enabled
pub threads: bool,

View File

@@ -99,7 +99,8 @@ impl Run {
}
// If WASI is enabled, try to execute it with it
if cfg!(feature = "wasi") {
#[cfg(feature = "wasi")]
{
let wasi_version = Wasi::get_version(&module);
if let Some(version) = wasi_version {
let program_name = self
@@ -129,9 +130,9 @@ impl Run {
fn get_module(&self) -> Result<Module> {
let contents = std::fs::read(self.path.clone())?;
if Engine::is_deserializable(&contents) {
let (compiler_config, _compiler_name) = self.compiler.get_compiler_config()?;
let tunables = self.compiler.get_tunables(&*compiler_config);
let engine = Engine::new(&*compiler_config, tunables);
// We get the tunables for the current host
let tunables = Tunables::default();
let engine = Engine::headless(tunables);
let store = Store::new(&engine);
let module = unsafe { Module::deserialize(&store, &contents)? };
return Ok(module);

View File

@@ -34,25 +34,6 @@ pub struct WasmFeatures {
pub all: bool,
}
#[derive(Debug, Clone, StructOpt)]
/// The compiler options
pub struct Compiler {
/// Use Singlepass compiler
#[structopt(long, conflicts_with_all = &["cranelift", "llvm"])]
singlepass: bool,
/// Use Cranelift compiler
#[structopt(long, conflicts_with_all = &["singlepass", "llvm"])]
cranelift: bool,
/// Use LLVM compiler
#[structopt(long, conflicts_with_all = &["singlepass", "cranelifft"])]
llvm: bool,
#[structopt(flatten)]
features: WasmFeatures,
}
/// Get the cache dir
pub fn get_cache_dir() -> PathBuf {
match env::var("WASMER_CACHE_DIR") {

View File

@@ -62,6 +62,7 @@ impl FromStr for Compiler {
}
}
#[cfg(feature = "compiler")]
impl StoreOptions {
fn get_compiler(&self) -> Result<Compiler> {
if self.cranelift {
@@ -91,6 +92,7 @@ impl StoreOptions {
}
/// Get the Compiler Config for the current options
#[allow(unused_variables)]
fn get_config(&self, compiler: Compiler) -> Result<Box<dyn CompilerConfig>> {
let config: Box<dyn CompilerConfig> = match compiler {
#[cfg(feature = "compiler-singlepass")]
@@ -122,7 +124,7 @@ impl StoreOptions {
}
/// Get's the compiler config
pub fn get_compiler_config(&self) -> Result<(Box<dyn CompilerConfig>, String)> {
fn get_compiler_config(&self) -> Result<(Box<dyn CompilerConfig>, String)> {
let compiler = self.get_compiler()?;
let compiler_name = compiler.to_string();
let compiler_config = self.get_config(compiler)?;
@@ -143,3 +145,15 @@ impl StoreOptions {
Ok((store, compiler_name))
}
}
#[cfg(not(feature = "compiler"))]
impl StoreOptions {
/// Get the store (headless engine)
pub fn get_store(&self) -> Result<(Store, String)> {
// Get the tunables for the current host
let tunables = Tunables::default();
let engine = Engine::headless(tunables);
let store = Store::new(&engine);
Ok((store, "headless".to_string()))
}
}

View File

@@ -15,12 +15,16 @@ wasmer-compiler-cranelift = { path = "../../../lib/compiler-cranelift", version
wasmer-compiler-llvm = { path = "../../../lib/compiler-llvm", version = "0.16.2", optional = true }
[features]
compiler = []
compiler-singlepass = [
"wasmer-compiler-singlepass",
"compiler",
]
compiler-cranelift = [
"wasmer-compiler-cranelift",
"compiler",
]
compiler-llvm = [
"wasmer-compiler-llvm",
"compiler",
]

View File

@@ -1,3 +1,5 @@
#![cfg(feature = "compiler")]
use wasmer_compiler::{CompilerConfig, Features, Target};
pub fn get_compiler_config_from_str(

View File

@@ -1,3 +1,5 @@
#![cfg(feature = "compiler")]
use std::path::Path;
use test_utils::get_compiler_config_from_str;
use wasmer::{Engine, Features, Store, Tunables};
@@ -16,7 +18,10 @@ use wasmer_wast::Wast;
include!(concat!(env!("OUT_DIR"), "/generated_spectests.rs"));
fn run_wast(wast_path: &str, compiler: &str) -> anyhow::Result<()> {
println!("Running wast {} with {}", wast_path, compiler);
println!(
"Running wast `{}` with the {} compiler",
wast_path, compiler
);
let try_nan_canonicalization = wast_path.contains("nan-canonicalization");
let mut features = Features::default();
if wast_path.contains("bulk-memory") {