@ -2,15 +2,18 @@ use crate::memory::MemoryCreator;
use crate ::trampoline ::MemoryCreatorProxy ;
use anyhow ::{ bail , Result } ;
use serde ::{ Deserialize , Serialize } ;
use std ::cmp ;
use std ::fmt ;
#[ cfg(feature = " cache " ) ]
use std ::path ::Path ;
use std ::sync ::Arc ;
use std ::{
cmp ,
collections ::{ HashMap , HashSet } ,
} ;
use wasmparser ::WasmFeatures ;
#[ cfg(feature = " cache " ) ]
use wasmtime_cache ::CacheConfig ;
use wasmtime_environ ::{ CompilerBuilder , Tunables } ;
use wasmtime_environ ::Tunables ;
use wasmtime_jit ::{ JitDumpAgent , NullProfilerAgent , ProfilingAgent , VTuneAgent } ;
use wasmtime_runtime ::{ InstanceAllocator , OnDemandInstanceAllocator , RuntimeMemoryCreator } ;
@ -79,14 +82,18 @@ impl Default for ModuleVersionStrategy {
/// and customize its behavior.
///
/// This structure exposed a builder-like interface and is primarily consumed by
/// [`Engine::new()`](crate::Engine::new)
/// [`Engine::new()`](crate::Engine::new).
///
/// The validation of `Config` is deferred until the engine is being built, thus
/// a problematic config may cause `Engine::new` to fail.
#[ derive(Clone) ]
pub struct Config {
#[ cfg(compiler) ]
pub ( crate ) compiler : Box < dyn CompilerBuilder > ,
compiler_config : CompilerConfig ,
pub ( crate ) tunables : Tunables ,
#[ cfg(feature = " cache " ) ]
pub ( crate ) cache_config : CacheConfig ,
pub ( crate ) profiler : Arc < dyn ProfilingAgent > ,
pub ( crate ) profiling_strategy : ProfilingStrategy ,
pub ( crate ) mem_creator : Option < Arc < dyn RuntimeMemoryCreator > > ,
pub ( crate ) allocation_strategy : InstanceAllocationStrategy ,
pub ( crate ) max_wasm_stack : usize ,
@ -103,6 +110,54 @@ pub struct Config {
pub ( crate ) force_memory_init_memfd : bool ,
}
/// User-provided configuration for the compiler.
#[ cfg(compiler) ]
#[ derive(Debug, Clone) ]
struct CompilerConfig {
strategy : Strategy ,
target : Option < target_lexicon ::Triple > ,
settings : HashMap < String , String > ,
flags : HashSet < String > ,
}
#[ cfg(compiler) ]
impl CompilerConfig {
fn new ( strategy : Strategy ) -> Self {
Self {
strategy ,
target : None ,
settings : HashMap ::new ( ) ,
flags : HashSet ::new ( ) ,
}
}
/// Ensures that the key is not set or equals to the given value.
/// If the key is not set, it will be set to the given value.
///
/// # Returns
///
/// Returns true if successfully set or already had the given setting
/// value, or false if the setting was explicitly set to something
/// else previously.
fn ensure_setting_unset_or_given ( & mut self , k : & str , v : & str ) -> bool {
if let Some ( value ) = self . settings . get ( k ) {
if value ! = v {
return false ;
}
} else {
self . settings . insert ( k . to_string ( ) , v . to_string ( ) ) ;
}
true
}
}
#[ cfg(compiler) ]
impl Default for CompilerConfig {
fn default ( ) -> Self {
Self ::new ( Strategy ::Auto )
}
}
impl Config {
/// Creates a new configuration object with the default configuration
/// specified.
@ -110,10 +165,10 @@ impl Config {
let mut ret = Self {
tunables : Tunables ::default ( ) ,
#[ cfg(compiler) ]
compiler : compiler_builder ( Strategy ::Auto ) . unwrap ( ) ,
compiler_config : CompilerConfig ::default ( ) ,
#[ cfg(feature = " cache " ) ]
cache_config : CacheConfig ::new_cache_disabled ( ) ,
profiler : Arc ::new ( NullProfilerAgent ) ,
profiling_strategy : ProfilingStrategy ::None ,
mem_creator : None ,
allocation_strategy : InstanceAllocationStrategy ::OnDemand ,
// 512k of stack -- note that this is chosen currently to not be too
@ -166,8 +221,8 @@ impl Config {
#[ cfg_attr(nightlydoc, doc(cfg(feature = " cranelift " ))) ] // see build.rs
pub fn target ( & mut self , target : & str ) -> Result < & mut Self > {
use std ::str ::FromStr ;
self . compiler
. target ( target_lexicon ::Triple ::from_str ( target ) . map_err ( | e | anyhow ::anyhow ! ( e ) ) ? ) ? ;
self . compiler_config . target =
Some ( target_lexicon ::Triple ::from_str ( target ) . map_err ( | e | anyhow ::anyhow ! ( e ) ) ? ) ;
Ok ( self )
}
@ -290,20 +345,6 @@ impl Config {
/// will always return `None`.
pub fn wasm_backtrace ( & mut self , enable : bool ) -> & mut Self {
self . wasm_backtrace = enable ;
#[ cfg(compiler) ]
{
// unwind_info must be enabled when either backtraces or reference types are enabled:
self . compiler
. set (
"unwind_info" ,
if enable | | self . features . reference_types {
"true"
} else {
"false"
} ,
)
. unwrap ( ) ;
}
self
}
@ -507,8 +548,8 @@ impl Config {
/// be enabled through this method for appropriate wasm modules.
///
/// This feature gates items such as shared memories and atomic
/// instructions. Note that enabling the threads feature will
/// also enable the bulk memory feature.
/// instructions. Note that the threads feature depends on the
/// bulk memory feature, which is enabled by default .
///
/// This is `false` by default.
///
@ -517,13 +558,14 @@ impl Config {
/// > expected. This should not be enabled in a production setting right
/// > now.
///
/// # Errors
///
/// The validation of this feature are deferred until the engine is being built,
/// and thus may cause `Engine::new` fail if the `bulk_memory` feature is disabled.
///
/// [threads]: https://github.com/webassembly/threads
pub fn wasm_threads ( & mut self , enable : bool ) -> & mut Self {
self . features . threads = enable ;
// The threads proposal depends on the bulk memory proposal
if enable {
self . wasm_bulk_memory ( true ) ;
}
self
}
@ -533,38 +575,18 @@ impl Config {
/// This feature gates items such as the `externref` and `funcref` types as
/// well as allowing a module to define multiple tables.
///
/// Note that enabling the reference types feature will also enable the bulk
/// memory feature.
/// Note that the reference types proposal depends on the bulk memory proposal.
///
/// This feature is `true` by default.
///
/// # Errors
///
/// The validation of this feature are deferred until the engine is being built,
/// and thus may cause `Engine::new` fail if the `bulk_memory` feature is disabled.
///
/// [proposal]: https://github.com/webassembly/reference-types
pub fn wasm_reference_types ( & mut self , enable : bool ) -> & mut Self {
self . features . reference_types = enable ;
#[ cfg(compiler) ]
{
self . compiler
. set ( "enable_safepoints" , if enable { "true" } else { "false" } )
. unwrap ( ) ;
// unwind_info must be enabled when either backtraces or reference types are enabled:
self . compiler
. set (
"unwind_info" ,
if enable | | self . wasm_backtrace {
"true"
} else {
"false"
} ,
)
. unwrap ( ) ;
}
// The reference types proposal depends on the bulk memory proposal.
if enable {
self . wasm_bulk_memory ( true ) ;
}
self
}
@ -586,13 +608,6 @@ impl Config {
/// [relaxed simd proposal]: https://github.com/WebAssembly/relaxed-simd
pub fn wasm_simd ( & mut self , enable : bool ) -> & mut Self {
self . features . simd = enable ;
#[ cfg(compiler) ]
{
let val = if enable { "true" } else { "false" } ;
self . compiler
. set ( "enable_simd" , val )
. expect ( "should be valid flag" ) ;
}
self
}
@ -604,6 +619,15 @@ impl Config {
///
/// This is `true` by default.
///
/// Feature `reference_types`, which is also `true` by default, requires
/// this feature to be enabled. Thus disabling this feature must also disable
/// `reference_types` as well using [`wasm_reference_types`](crate::Config::wasm_reference_types).
///
/// # Errors
///
/// Disabling this feature without disabling `reference_types` will cause
/// `Engine::new` to fail.
///
/// [proposal]: https://github.com/webassembly/bulk-memory-operations
pub fn wasm_bulk_memory ( & mut self , enable : bool ) -> & mut Self {
self . features . bulk_memory = enable ;
@ -674,30 +698,30 @@ impl Config {
/// and its documentation.
///
/// The default value for this is `Strategy::Auto`.
///
/// # Errors
///
/// Some compilation strategies require compile-time options of `wasmtime`
/// itself to be set, but if they're not set and the strategy is specified
/// here then an error will be returned.
#[ cfg(compiler) ]
#[ cfg_attr(nightlydoc, doc(cfg(feature = " cranelift " ))) ] // see build.rs
pub fn strategy ( & mut self , strategy : Strategy ) -> Result < & mut Self > {
self . compiler = compiler_builder ( strategy ) ? ;
Ok ( self )
pub fn strategy ( & mut self , strategy : Strategy ) -> & mut Self {
self . compiler_config . strategy = strategy ;
self
}
/// Creates a default profiler based on the profiling strategy chosen.
///
/// Profiler creation calls the type's default initializer where the purpose is
/// really just to put in place the type used for profiling.
pub fn profiler ( & mut self , profile : ProfilingStrategy ) -> Result < & mut Self > {
self . profiler = match profile {
ProfilingStrategy ::JitDump = > Arc ::new ( JitDumpAgent ::new ( ) ? ) as Arc < dyn ProfilingAgent > ,
ProfilingStrategy ::VTune = > Arc ::new ( VTuneAgent ::new ( ) ? ) as Arc < dyn ProfilingAgent > ,
ProfilingStrategy ::None = > Arc ::new ( NullProfilerAgent ) ,
} ;
Ok ( self )
///
/// Some [`ProfilingStrategy`] require specific platforms or particular feature
/// to be enabled, such as `ProfilingStrategy::JitDump` requires the `jitdump`
/// feature.
///
/// # Errors
///
/// The validation of this field is deferred until the engine is being built, and thus may
/// cause `Engine::new` fail if the required feature is disabled, or the platform is not
/// supported.
pub fn profiler ( & mut self , profile : ProfilingStrategy ) -> & mut Self {
self . profiling_strategy = profile ;
self
}
/// Configures whether the debug verifier of Cranelift is enabled or not.
@ -712,9 +736,9 @@ impl Config {
#[ cfg_attr(nightlydoc, doc(cfg(feature = " cranelift " ))) ] // see build.rs
pub fn cranelift_debug_verifier ( & mut self , enable : bool ) -> & mut Self {
let val = if enable { "true" } else { "false" } ;
self . compiler
. set ( "enable_verifier" , val )
. expect ( "should be valid flag" ) ;
self . compiler_config
. settings
. insert ( "enable_verifier" . to_string ( ) , val . to_string ( ) ) ;
self
}
@ -733,9 +757,9 @@ impl Config {
OptLevel ::Speed = > "speed" ,
OptLevel ::SpeedAndSize = > "speed_and_size" ,
} ;
self . compiler
. set ( "opt_level" , val )
. expect ( "should be valid flag" ) ;
self . compiler_config
. settings
. insert ( "opt_level" . to_string ( ) , val . to_string ( ) ) ;
self
}
@ -751,9 +775,9 @@ impl Config {
#[ cfg_attr(nightlydoc, doc(cfg(feature = " cranelift " ))) ] // see build.rs
pub fn cranelift_nan_canonicalization ( & mut self , enable : bool ) -> & mut Self {
let val = if enable { "true" } else { "false" } ;
self . compiler
. set ( "enable_nan_canonicalization" , val )
. expect ( "should be valid flag" ) ;
self . compiler_config
. settings
. insert ( "enable_nan_canonicalization" . to_string ( ) , val . to_string ( ) ) ;
self
}
@ -770,12 +794,14 @@ impl Config {
///
/// # Errors
///
/// This method can fail if the flag's name does not exist.
/// The validation of the flags are deferred until the engine is being built, and thus may
/// cause `Engine::new` fail if the flag's name does not exist, or the value is not appropriate
/// for the flag type.
#[ cfg(compiler) ]
#[ cfg_attr(nightlydoc, doc(cfg(feature = " cranelift " ))) ] // see build.rs
pub unsafe fn cranelift_flag_enable ( & mut self , flag : & str ) -> Result < & mut Self > {
self . compiler . enable ( flag ) ? ;
Ok ( self )
pub unsafe fn cranelift_flag_enable ( & mut self , flag : & str ) -> & mut Self {
self . compiler_config . flags . insert ( flag . to_string ( ) ) ;
self
}
/// Allows settings another Cranelift flag defined by a flag name and value. This allows
@ -784,18 +810,26 @@ impl Config {
/// Since Cranelift flags may be unstable, this method should not be considered to be stable
/// either; other `Config` functions should be preferred for stability.
///
/// Note that this is marked as unsafe, because setting the wrong flag might break invariants,
/// # Safety
///
/// This is marked as unsafe, because setting the wrong flag might break invariants,
/// resulting in execution hazards.
///
/// # Errors
///
/// This method can fail if the flag's name does not exist, or the value is not appropriate for
/// the flag type.
/// The validation of the flags are deferred until the engine is being built, and thus may
/// cause `Engine::new` fail if the flag's name does not exist, or incompatible with other
/// settings.
///
/// For example, feature `wasm_backtrace` will set `unwind_info` to `true`, but if it's
/// manually set to false then it will fail.
#[ cfg(compiler) ]
#[ cfg_attr(nightlydoc, doc(cfg(feature = " cranelift " ))) ] // see build.rs
pub unsafe fn cranelift_flag_set ( & mut self , name : & str , value : & str ) -> Result < & mut Self > {
self . compiler . set ( name , value ) ? ;
Ok ( self )
pub unsafe fn cranelift_flag_set ( & mut self , name : & str , value : & str ) -> & mut Self {
self . compiler_config
. settings
. insert ( name . to_string ( ) , value . to_string ( ) ) ;
self
}
/// Loads cache configuration specified at `path`.
@ -1294,6 +1328,16 @@ impl Config {
self
}
pub ( crate ) fn validate ( & self ) -> Result < ( ) > {
if self . features . reference_types & & ! self . features . bulk_memory {
bail ! ( "feature 'reference_types' requires 'bulk_memory' to be enabled" ) ;
}
if self . features . threads & & ! self . features . bulk_memory {
bail ! ( "feature 'threads' requires 'bulk_memory' to be enabled" ) ;
}
Ok ( ( ) )
}
pub ( crate ) fn build_allocator ( & self ) -> Result < Box < dyn InstanceAllocator > > {
#[ cfg(feature = " async " ) ]
let stack_size = self . async_stack_size ;
@ -1318,12 +1362,59 @@ impl Config {
) ? ) ) ,
}
}
}
#[ cfg(compiler) ]
fn compiler_builder ( strategy : Strategy ) -> Result < Box < dyn CompilerBuilder > > {
match strategy {
Strategy ::Auto | Strategy ::Cranelift = > Ok ( wasmtime_cranelift ::builder ( ) ) ,
pub ( crate ) fn build_profiler ( & self ) -> Result < Box < dyn ProfilingAgent > > {
Ok ( match self . profiling_strategy {
ProfilingStrategy ::JitDump = > Box ::new ( JitDumpAgent ::new ( ) ? ) as Box < dyn ProfilingAgent > ,
ProfilingStrategy ::VTune = > Box ::new ( VTuneAgent ::new ( ) ? ) as Box < dyn ProfilingAgent > ,
ProfilingStrategy ::None = > Box ::new ( NullProfilerAgent ) ,
} )
}
#[ cfg(compiler) ]
pub ( crate ) fn build_compiler ( & mut self ) -> Result < Box < dyn wasmtime_environ ::Compiler > > {
let mut compiler = match self . compiler_config . strategy {
Strategy ::Auto | Strategy ::Cranelift = > wasmtime_cranelift ::builder ( ) ,
} ;
if let Some ( target ) = & self . compiler_config . target {
compiler . target ( target . clone ( ) ) ? ;
}
// check for incompatible compiler options and set required values
if self . wasm_backtrace | | self . features . reference_types {
if ! self
. compiler_config
. ensure_setting_unset_or_given ( "unwind_info" , "true" )
{
bail ! ( "compiler option 'unwind_info' must be enabled when either 'backtraces' or 'reference types' are enabled" ) ;
}
}
if self . features . reference_types {
if ! self
. compiler_config
. ensure_setting_unset_or_given ( "enable_safepoints" , "true" )
{
bail ! ( "compiler option 'enable_safepoints' must be enabled when 'reference types' is enabled" ) ;
}
}
if self . features . simd {
if ! self
. compiler_config
. ensure_setting_unset_or_given ( "enable_simd" , "true" )
{
bail ! ( "compiler option 'enable_simd' must be enabled when 'simd' is enabled" ) ;
}
}
// Apply compiler settings and flags
for ( k , v ) in self . compiler_config . settings . iter ( ) {
compiler . set ( k , v ) ? ;
}
for flag in self . compiler_config . flags . iter ( ) {
compiler . enable ( flag ) ? ;
}
compiler . build ( )
}
}
@ -1332,7 +1423,7 @@ fn round_up_to_pages(val: u64) -> u64 {
debug_assert ! ( page_size . is_power_of_two ( ) ) ;
val . checked_add ( page_size - 1 )
. map ( | val | val & ! ( page_size - 1 ) )
. unwrap_or ( u64 ::max_value ( ) / page_size + 1 )
. unwrap_or ( u64 ::MAX / page_size + 1 )
}
impl Default for Config {
@ -1341,33 +1432,6 @@ impl Default for Config {
}
}
impl Clone for Config {
fn clone ( & self ) -> Config {
Config {
#[ cfg(compiler) ]
compiler : self . compiler . clone ( ) ,
tunables : self . tunables . clone ( ) ,
#[ cfg(feature = " cache " ) ]
cache_config : self . cache_config . clone ( ) ,
profiler : self . profiler . clone ( ) ,
features : self . features . clone ( ) ,
mem_creator : self . mem_creator . clone ( ) ,
allocation_strategy : self . allocation_strategy . clone ( ) ,
max_wasm_stack : self . max_wasm_stack ,
wasm_backtrace : self . wasm_backtrace ,
wasm_backtrace_details_env_used : self . wasm_backtrace_details_env_used ,
async_support : self . async_support ,
#[ cfg(feature = " async " ) ]
async_stack_size : self . async_stack_size ,
module_version : self . module_version . clone ( ) ,
parallel_compilation : self . parallel_compilation ,
memory_init_cow : self . memory_init_cow ,
memory_guaranteed_dense_image_size : self . memory_guaranteed_dense_image_size ,
force_memory_init_memfd : self . force_memory_init_memfd ,
}
}
}
impl fmt ::Debug for Config {
fn fmt ( & self , f : & mut fmt ::Formatter ) -> fmt ::Result {
let mut f = f . debug_struct ( "Config" ) ;
@ -1398,7 +1462,7 @@ impl fmt::Debug for Config {
. field ( "parallel_compilation" , & self . parallel_compilation ) ;
#[ cfg(compiler) ]
{
f . field ( "compiler" , & self . compiler ) ;
f . field ( "compiler_config " , & self . compiler_config ) ;
}
f . finish ( )
}
@ -1408,7 +1472,7 @@ impl fmt::Debug for Config {
///
/// This is used as an argument to the [`Config::strategy`] method.
#[ non_exhaustive ]
#[ derive(Clone, Debug) ]
#[ derive(Clone, Debug, Copy ) ]
pub enum Strategy {
/// An indicator that the compilation strategy should be automatically
/// selected.