Make tunables a Trait

This commit is contained in:
Syrus
2020-05-02 14:28:25 -07:00
parent b5ebd7b084
commit e343c9ea9b
12 changed files with 156 additions and 126 deletions

2
Cargo.lock generated
View File

@@ -1456,6 +1456,8 @@ dependencies = [
"cfg-if", "cfg-if",
"indexmap", "indexmap",
"libc", "libc",
"more-asserts",
"target-lexicon",
"tempfile", "tempfile",
"thiserror", "thiserror",
"wasm-common", "wasm-common",

View File

@@ -20,6 +20,8 @@ indexmap = { version = "1.3.2", features = ["serde-1"] }
cfg-if = "0.1.10" cfg-if = "0.1.10"
wat = { version = "1.0.15", optional = true } wat = { version = "1.0.15", optional = true }
thiserror = "1.0.16" thiserror = "1.0.16"
more-asserts = "0.2.1"
target-lexicon = { version = "0.10.0", default-features = false }
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
winapi = "0.3.8" winapi = "0.3.8"

View File

@@ -9,6 +9,7 @@ mod memory_view;
mod module; mod module;
mod ptr; mod ptr;
mod store; mod store;
mod tunables;
mod types; mod types;
pub use crate::exports::{ExportError, Exportable, Exports}; pub use crate::exports::{ExportError, Exportable, Exports};
@@ -19,6 +20,7 @@ pub use crate::memory_view::MemoryView;
pub use crate::module::Module; pub use crate::module::Module;
pub use crate::ptr::{Array, Item, WasmPtr}; pub use crate::ptr::{Array, Item, WasmPtr};
pub use crate::store::{Engine, Store, StoreObject}; pub use crate::store::{Engine, Store, StoreObject};
pub use crate::tunables::Tunables;
pub use crate::types::{ pub use crate::types::{
AnyRef, ExportType, ExternType, FuncType, GlobalType, HostInfo, HostRef, ImportType, AnyRef, ExportType, ExternType, FuncType, GlobalType, HostInfo, HostRef, ImportType,
MemoryType, Mutability, TableType, Val, ValType, MemoryType, Mutability, TableType, Val, ValType,

View File

@@ -1,3 +1,4 @@
use crate::tunables::Tunables;
use std::sync::Arc; use std::sync::Arc;
use wasmer_compiler::CompilerConfig; use wasmer_compiler::CompilerConfig;
use wasmer_jit::JITEngine; use wasmer_jit::JITEngine;
@@ -72,7 +73,9 @@ impl PartialEq for Store {
))] ))]
impl Default for Store { impl Default for Store {
fn default() -> Store { fn default() -> Store {
Store::new(&Engine::new(&Self::default_compiler_config())) let config = Self::default_compiler_config();
let tunables = Tunables::for_target(config.target().triple());
Store::new(&Engine::new(&config, tunables))
} }
} }

111
lib/api/src/tunables.rs Normal file
View File

@@ -0,0 +1,111 @@
use more_asserts::assert_ge;
use std::cmp::min;
use target_lexicon::{OperatingSystem, PointerWidth, Triple, HOST};
use wasm_common::{MemoryType, Pages, TableType};
use wasmer_runtime::{LinearMemory, Table};
use wasmer_runtime::{MemoryPlan, MemoryStyle, TablePlan, TableStyle};
/// Tunable parameters for WebAssembly compilation.
#[derive(Clone)]
pub struct Tunables {
/// For static heaps, the size in wasm pages of the heap protected by bounds checking.
pub static_memory_bound: Pages,
/// The size in bytes of the offset guard for static heaps.
pub static_memory_offset_guard_size: u64,
/// The size in bytes of the offset guard for dynamic heaps.
pub dynamic_memory_offset_guard_size: u64,
}
impl Tunables {
/// Get the `Tunables` for a specific Target
pub fn for_target(triple: &Triple) -> Self {
let pointer_width: PointerWidth = triple.pointer_width().unwrap();
let (mut static_memory_bound, mut static_memory_offset_guard_size): (Pages, u64) =
match pointer_width {
PointerWidth::U16 => (0x400.into(), 0x1000),
PointerWidth::U32 => (0x4000.into(), 0x1_0000),
// Static Memory Bound:
// Allocating 4 GiB of address space let us avoid the
// need for explicit bounds checks.
// Static Memory Guard size:
// Allocating 2 GiB of address space lets us translate wasm
// offsets into x86 offsets as aggressively as we can.
PointerWidth::U64 => (0x1_0000.into(), 0x8000_0000),
};
// Allocate a small guard to optimize common cases but without
// wasting too much memory.
let dynamic_memory_offset_guard_size: u64 = 0x1_0000;
match triple.operating_system {
OperatingSystem::Windows => {
// For now, use a smaller footprint on Windows so that we don't
// don't outstrip the paging file.
static_memory_bound = min(static_memory_bound, 0x100.into());
static_memory_offset_guard_size = min(static_memory_offset_guard_size, 0x10000);
}
_ => {}
}
Self {
static_memory_bound,
static_memory_offset_guard_size,
dynamic_memory_offset_guard_size,
}
}
}
impl wasmer_jit::Tunables for Tunables {
/// Get a `MemoryPlan` for the provided `MemoryType`
fn memory_plan(&self, memory: MemoryType) -> MemoryPlan {
// A heap with a maximum that doesn't exceed the static memory bound specified by the
// tunables make it static.
//
// If the module doesn't declare an explicit maximum treat it as 4GiB.
let maximum = memory.maximum.unwrap_or(Pages::max_value());
if maximum <= self.static_memory_bound {
assert_ge!(self.static_memory_bound, memory.minimum);
MemoryPlan {
memory,
style: MemoryStyle::Static {
bound: self.static_memory_bound,
},
offset_guard_size: self.static_memory_offset_guard_size,
}
} else {
MemoryPlan {
memory,
style: MemoryStyle::Dynamic,
offset_guard_size: self.dynamic_memory_offset_guard_size,
}
}
}
/// Get a `TablePlan` for the provided `TableType`
fn table_plan(&self, table: TableType) -> TablePlan {
TablePlan {
table,
style: TableStyle::CallerChecksSignature,
}
}
/// Create a memory given a memory type
fn create_memory(&self, memory_type: MemoryType) -> Result<LinearMemory, String> {
let plan = self.memory_plan(memory_type);
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);
Table::new(&plan)
}
}
impl Default for Tunables {
fn default() -> Self {
Self::for_target(&HOST)
}
}

View File

@@ -12,7 +12,7 @@ use wasm_common::entity::PrimaryMap;
use wasm_common::{FuncType, LocalFuncIndex, MemoryIndex, TableIndex}; use wasm_common::{FuncType, LocalFuncIndex, MemoryIndex, TableIndex};
use wasmer_compiler::{ use wasmer_compiler::{
Compilation, CompileError, Compiler as BaseCompiler, CompilerConfig, FunctionBody, Compilation, CompileError, Compiler as BaseCompiler, CompilerConfig, FunctionBody,
FunctionBodyData, ModuleTranslationState, FunctionBodyData, ModuleTranslationState, Target,
}; };
use wasmer_runtime::{ use wasmer_runtime::{
InstanceHandle, MemoryPlan, Module, SignatureRegistry, TablePlan, VMFunctionBody, InstanceHandle, MemoryPlan, Module, SignatureRegistry, TablePlan, VMFunctionBody,
@@ -23,18 +23,16 @@ use wasmer_runtime::{
#[derive(Clone)] #[derive(Clone)]
pub struct JITEngine { pub struct JITEngine {
inner: Arc<RefCell<JITEngineInner>>, inner: Arc<RefCell<JITEngineInner>>,
tunables: Arc<Tunables>, tunables: Arc<Box<dyn Tunables>>,
} }
impl JITEngine { impl JITEngine {
/// Create a new JIT Engine given config /// Create a new JIT Engine given config
pub fn new<T: CompilerConfig>(config: &T) -> Self pub fn new<C: CompilerConfig>(config: &C, tunables: impl Tunables + 'static) -> Self
where where
T: ?Sized, C: ?Sized,
{ {
let compiler = config.compiler(); let compiler = config.compiler();
let tunables = Tunables::for_target(compiler.target().triple());
Self { Self {
inner: Arc::new(RefCell::new(JITEngineInner { inner: Arc::new(RefCell::new(JITEngineInner {
compiler, compiler,
@@ -42,13 +40,13 @@ impl JITEngine {
code_memory: CodeMemory::new(), code_memory: CodeMemory::new(),
signatures: SignatureRegistry::new(), signatures: SignatureRegistry::new(),
})), })),
tunables: Arc::new(tunables), tunables: Arc::new(Box::new(tunables)),
} }
} }
/// Get the tunables /// Get the tunables
pub fn tunables(&self) -> &Tunables { pub fn tunables(&self) -> &dyn Tunables {
&self.tunables &**self.tunables
} }
pub(crate) fn compiler(&self) -> std::cell::Ref<'_, JITEngineInner> { pub(crate) fn compiler(&self) -> std::cell::Ref<'_, JITEngineInner> {
@@ -133,7 +131,7 @@ impl JITEngineInner {
/// Compile the given function bodies. /// Compile the given function bodies.
pub(crate) fn compile_module<'data>( pub(crate) fn compile_module<'data>(
&mut self, &self,
module: &Module, module: &Module,
module_translation: &ModuleTranslationState, module_translation: &ModuleTranslationState,
function_body_inputs: PrimaryMap<LocalFuncIndex, FunctionBodyData<'data>>, function_body_inputs: PrimaryMap<LocalFuncIndex, FunctionBodyData<'data>>,

View File

@@ -1,109 +1,18 @@
use more_asserts::assert_ge; use wasm_common::{MemoryType, TableType};
use std::cmp::min;
use target_lexicon::{OperatingSystem, PointerWidth, Triple, HOST};
use wasm_common::{MemoryType, Pages, TableType};
use wasmer_runtime::{LinearMemory, Table}; use wasmer_runtime::{LinearMemory, Table};
use wasmer_runtime::{MemoryPlan, MemoryStyle, TablePlan, TableStyle}; use wasmer_runtime::{MemoryPlan, TablePlan};
/// Tunable parameters for WebAssembly compilation.
#[derive(Clone)]
pub struct Tunables {
/// For static heaps, the size in wasm pages of the heap protected by bounds checking.
pub static_memory_bound: Pages,
/// The size in bytes of the offset guard for static heaps.
pub static_memory_offset_guard_size: u64,
/// The size in bytes of the offset guard for dynamic heaps.
pub dynamic_memory_offset_guard_size: u64,
}
impl Tunables {
/// Get the `Tunables` for a specific Target
pub fn for_target(triple: &Triple) -> Self {
let pointer_width: PointerWidth = triple.pointer_width().unwrap();
let (mut static_memory_bound, mut static_memory_offset_guard_size): (Pages, u64) =
match pointer_width {
PointerWidth::U16 => (0x400.into(), 0x1000),
PointerWidth::U32 => (0x4000.into(), 0x1_0000),
// Static Memory Bound:
// Allocating 4 GiB of address space let us avoid the
// need for explicit bounds checks.
// Static Memory Guard size:
// Allocating 2 GiB of address space lets us translate wasm
// offsets into x86 offsets as aggressively as we can.
PointerWidth::U64 => (0x1_0000.into(), 0x8000_0000),
};
// Allocate a small guard to optimize common cases but without
// wasting too much memory.
let dynamic_memory_offset_guard_size: u64 = 0x1_0000;
match triple.operating_system {
OperatingSystem::Windows => {
// For now, use a smaller footprint on Windows so that we don't
// don't outstrip the paging file.
static_memory_bound = min(static_memory_bound, 0x100.into());
static_memory_offset_guard_size = min(static_memory_offset_guard_size, 0x10000);
}
_ => {}
}
Self {
static_memory_bound,
static_memory_offset_guard_size,
dynamic_memory_offset_guard_size,
}
}
/// Tunables for an engine
pub trait Tunables {
/// Get a `MemoryPlan` for the provided `MemoryType` /// Get a `MemoryPlan` for the provided `MemoryType`
pub fn memory_plan(&self, memory: MemoryType) -> MemoryPlan { fn memory_plan(&self, memory: MemoryType) -> MemoryPlan;
// A heap with a maximum that doesn't exceed the static memory bound specified by the
// tunables make it static.
//
// If the module doesn't declare an explicit maximum treat it as 4GiB.
let maximum = memory.maximum.unwrap_or(Pages::max_value());
if maximum <= self.static_memory_bound {
assert_ge!(self.static_memory_bound, memory.minimum);
MemoryPlan {
memory,
style: MemoryStyle::Static {
bound: self.static_memory_bound,
},
offset_guard_size: self.static_memory_offset_guard_size,
}
} else {
MemoryPlan {
memory,
style: MemoryStyle::Dynamic,
offset_guard_size: self.dynamic_memory_offset_guard_size,
}
}
}
/// Get a `TablePlan` for the provided `TableType` /// Get a `TablePlan` for the provided `TableType`
pub fn table_plan(&self, table: TableType) -> TablePlan { fn table_plan(&self, table: TableType) -> TablePlan;
TablePlan {
table,
style: TableStyle::CallerChecksSignature,
}
}
/// Create a memory given a memory type /// Create a memory given a memory type
pub fn create_memory(&self, memory_type: MemoryType) -> Result<LinearMemory, String> { fn create_memory(&self, memory_type: MemoryType) -> Result<LinearMemory, String>;
let plan = self.memory_plan(memory_type);
LinearMemory::new(&plan)
}
/// Create a memory given a memory type /// Create a memory given a memory type
pub fn create_table(&self, table_type: TableType) -> Table { fn create_table(&self, table_type: TableType) -> Table;
let plan = self.table_plan(table_type);
Table::new(&plan)
}
}
impl Default for Tunables {
fn default() -> Self {
Self::for_target(&HOST)
}
} }

View File

@@ -75,10 +75,7 @@ impl Run {
/// Execute the run command /// Execute the run command
pub fn execute(&self) -> Result<()> { pub fn execute(&self) -> Result<()> {
let (compiler_config, compiler_name) = self.compiler.get_config()?; let (store, compiler_name) = self.compiler.get_store()?;
let engine = Engine::new(&*compiler_config);
let store = Store::new(&engine);
let contents = std::fs::read(self.path.clone())?; let contents = std::fs::read(self.path.clone())?;
// We try to get it from cache, in case caching is enabled // We try to get it from cache, in case caching is enabled
// and the file length is greater than 4KB. // and the file length is greater than 4KB.

View File

@@ -18,9 +18,7 @@ pub struct Validate {
impl Validate { impl Validate {
/// Runs logic for the `validate` subcommand /// Runs logic for the `validate` subcommand
pub fn execute(&self) -> Result<()> { pub fn execute(&self) -> Result<()> {
let (compiler_config, _compiler_name) = self.compiler.get_config()?; let (store, _compiler_name) = self.compiler.get_store()?;
let engine = Engine::new(&*compiler_config);
let store = Store::new(&engine);
let module_contents = std::fs::read(&self.path)?; let module_contents = std::fs::read(&self.path)?;
Module::validate(&store, &module_contents) Module::validate(&store, &module_contents)
.with_context(|| "Unable to validate the file")?; .with_context(|| "Unable to validate the file")?;

View File

@@ -3,7 +3,6 @@ use crate::compiler::CompilerOptions;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use std::path::PathBuf; use std::path::PathBuf;
use structopt::StructOpt; use structopt::StructOpt;
use wasmer::*;
use wasmer_wast::Wast as WastSpectest; use wasmer_wast::Wast as WastSpectest;
#[derive(Debug, StructOpt)] #[derive(Debug, StructOpt)]
@@ -24,9 +23,7 @@ pub struct Wast {
impl Wast { impl Wast {
/// Runs logic for the `validate` subcommand /// Runs logic for the `validate` subcommand
pub fn execute(&self) -> Result<()> { pub fn execute(&self) -> Result<()> {
let (compiler_config, _compiler_name) = self.compiler.get_config()?; let (store, _compiler_name) = self.compiler.get_store()?;
let engine = Engine::new(&*compiler_config);
let store = Store::new(&engine);
let mut wast = WastSpectest::new_with_spectest(store); let mut wast = WastSpectest::new_with_spectest(store);
wast.fail_fast = self.fail_fast; wast.fail_fast = self.fail_fast;
wast.run_file(&self.path) wast.run_file(&self.path)

View File

@@ -91,8 +91,7 @@ impl CompilerOptions {
} }
/// Get the Compiler Config for the current options /// Get the Compiler Config for the current options
pub fn get_config(&self) -> Result<(Box<dyn CompilerConfig>, String)> { fn get_config(&self, compiler: Compiler) -> Result<Box<dyn CompilerConfig>> {
let compiler = self.get_compiler()?;
let config: Box<dyn CompilerConfig> = match compiler { let config: Box<dyn CompilerConfig> = match compiler {
#[cfg(feature = "compiler-singlepass")] #[cfg(feature = "compiler-singlepass")]
Compiler::Singlepass => { Compiler::Singlepass => {
@@ -119,6 +118,17 @@ impl CompilerOptions {
compiler.to_string() compiler.to_string()
), ),
}; };
return Ok((config, compiler.to_string())); return Ok(config);
}
/// Get's the store
pub fn get_store(&self) -> Result<(Store, String)> {
let compiler = self.get_compiler()?;
let compiler_name = compiler.to_string();
let compiler_config = self.get_config(compiler)?;
let tunables = Tunables::for_target(compiler_config.target().triple());
let engine = Engine::new(&*compiler_config, tunables);
let store = Store::new(&engine);
Ok((store, compiler_name))
} }
} }

View File

@@ -1,6 +1,6 @@
use std::path::Path; use std::path::Path;
use test_utils::get_compiler_config_from_str; use test_utils::get_compiler_config_from_str;
use wasmer::{Engine, Store}; use wasmer::{Engine, Store, Tunables};
use wasmer_wast::Wast; use wasmer_wast::Wast;
// The generated tests (from build.rs) look like: // The generated tests (from build.rs) look like:
@@ -20,7 +20,8 @@ fn run_wast(wast_path: &str, compiler: &str) -> anyhow::Result<()> {
let try_nan_canonicalization = wast_path.contains("nan-canonicalization"); let try_nan_canonicalization = wast_path.contains("nan-canonicalization");
let mut compiler_config = get_compiler_config_from_str(compiler, try_nan_canonicalization); let mut compiler_config = get_compiler_config_from_str(compiler, try_nan_canonicalization);
compiler_config.features_mut().multi_value(true); compiler_config.features_mut().multi_value(true);
let store = Store::new(&Engine::new(&*compiler_config)); let tunables = Tunables::for_target(compiler_config.target().triple());
let store = Store::new(&Engine::new(&*compiler_config, tunables));
let mut wast = Wast::new_with_spectest(store); let mut wast = Wast::new_with_spectest(store);
let path = Path::new(wast_path); let path = Path::new(wast_path);
wast.run_file(path) wast.run_file(path)