use crate::prelude::*;
use alloc::sync::Arc;
use bitflags::Flags;
use core::fmt;
use core::str::FromStr;
#[cfg(any(feature = "cache", feature = "cranelift", feature = "winch"))]
use std::path::Path;
use wasmparser::WasmFeatures;
use wasmtime_environ::{ConfigTunables, TripleExt, Tunables};
#[cfg(feature = "runtime")]
use crate::memory::MemoryCreator;
#[cfg(feature = "runtime")]
use crate::profiling_agent::{self, ProfilingAgent};
#[cfg(feature = "runtime")]
use crate::runtime::vm::{
GcRuntime, InstanceAllocator, OnDemandInstanceAllocator, RuntimeMemoryCreator,
};
#[cfg(feature = "runtime")]
use crate::trampoline::MemoryCreatorProxy;
#[cfg(feature = "async")]
use crate::stack::{StackCreator, StackCreatorProxy};
#[cfg(feature = "async")]
use wasmtime_fiber::RuntimeFiberStackCreator;
#[cfg(feature = "runtime")]
pub use crate::runtime::code_memory::CustomCodeMemory;
#[cfg(feature = "cache")]
pub use wasmtime_cache::{Cache, CacheConfig};
#[cfg(all(feature = "incremental-cache", feature = "cranelift"))]
pub use wasmtime_environ::CacheStore;
#[derive(Clone)]
#[non_exhaustive]
pub enum InstanceAllocationStrategy {
OnDemand,
#[cfg(feature = "pooling-allocator")]
Pooling(PoolingAllocationConfig),
}
impl InstanceAllocationStrategy {
#[cfg(feature = "pooling-allocator")]
pub fn pooling() -> Self {
Self::Pooling(Default::default())
}
}
impl Default for InstanceAllocationStrategy {
fn default() -> Self {
Self::OnDemand
}
}
#[cfg(feature = "pooling-allocator")]
impl From<PoolingAllocationConfig> for InstanceAllocationStrategy {
fn from(cfg: PoolingAllocationConfig) -> InstanceAllocationStrategy {
InstanceAllocationStrategy::Pooling(cfg)
}
}
#[derive(Clone)]
pub enum ModuleVersionStrategy {
WasmtimeVersion,
Custom(String),
None,
}
impl Default for ModuleVersionStrategy {
fn default() -> Self {
ModuleVersionStrategy::WasmtimeVersion
}
}
impl core::hash::Hash for ModuleVersionStrategy {
fn hash<H: core::hash::Hasher>(&self, hasher: &mut H) {
match self {
Self::WasmtimeVersion => env!("CARGO_PKG_VERSION").hash(hasher),
Self::Custom(s) => s.hash(hasher),
Self::None => {}
};
}
}
#[derive(Clone)]
pub struct Config {
#[cfg(any(feature = "cranelift", feature = "winch"))]
compiler_config: CompilerConfig,
target: Option<target_lexicon::Triple>,
#[cfg(feature = "gc")]
collector: Collector,
profiling_strategy: ProfilingStrategy,
tunables: ConfigTunables,
#[cfg(feature = "cache")]
pub(crate) cache: Option<Cache>,
#[cfg(feature = "runtime")]
pub(crate) mem_creator: Option<Arc<dyn RuntimeMemoryCreator>>,
#[cfg(feature = "runtime")]
pub(crate) custom_code_memory: Option<Arc<dyn CustomCodeMemory>>,
pub(crate) allocation_strategy: InstanceAllocationStrategy,
pub(crate) max_wasm_stack: usize,
pub(crate) enabled_features: WasmFeatures,
pub(crate) disabled_features: WasmFeatures,
pub(crate) wasm_backtrace: bool,
pub(crate) wasm_backtrace_details_env_used: bool,
pub(crate) native_unwind_info: Option<bool>,
#[cfg(any(feature = "async", feature = "stack-switching"))]
pub(crate) async_stack_size: usize,
#[cfg(feature = "async")]
pub(crate) async_stack_zeroing: bool,
#[cfg(feature = "async")]
pub(crate) stack_creator: Option<Arc<dyn RuntimeFiberStackCreator>>,
pub(crate) async_support: bool,
pub(crate) module_version: ModuleVersionStrategy,
pub(crate) parallel_compilation: bool,
pub(crate) memory_guaranteed_dense_image_size: u64,
pub(crate) force_memory_init_memfd: bool,
pub(crate) wmemcheck: bool,
#[cfg(feature = "coredump")]
pub(crate) coredump_on_trap: bool,
pub(crate) macos_use_mach_ports: bool,
pub(crate) detect_host_feature: Option<fn(&str) -> Option<bool>>,
}
#[cfg(any(feature = "cranelift", feature = "winch"))]
#[derive(Debug, Clone)]
struct CompilerConfig {
strategy: Option<Strategy>,
settings: crate::hash_map::HashMap<String, String>,
flags: crate::hash_set::HashSet<String>,
#[cfg(all(feature = "incremental-cache", feature = "cranelift"))]
cache_store: Option<Arc<dyn CacheStore>>,
clif_dir: Option<std::path::PathBuf>,
wmemcheck: bool,
}
#[cfg(any(feature = "cranelift", feature = "winch"))]
impl CompilerConfig {
fn new() -> Self {
Self {
strategy: Strategy::Auto.not_auto(),
settings: Default::default(),
flags: Default::default(),
#[cfg(all(feature = "incremental-cache", feature = "cranelift"))]
cache_store: None,
clif_dir: None,
wmemcheck: false,
}
}
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(any(feature = "cranelift", feature = "winch"))]
impl Default for CompilerConfig {
fn default() -> Self {
Self::new()
}
}
impl Config {
pub fn new() -> Self {
let mut ret = Self {
tunables: ConfigTunables::default(),
#[cfg(any(feature = "cranelift", feature = "winch"))]
compiler_config: CompilerConfig::default(),
target: None,
#[cfg(feature = "gc")]
collector: Collector::default(),
#[cfg(feature = "cache")]
cache: None,
profiling_strategy: ProfilingStrategy::None,
#[cfg(feature = "runtime")]
mem_creator: None,
#[cfg(feature = "runtime")]
custom_code_memory: None,
allocation_strategy: InstanceAllocationStrategy::OnDemand,
max_wasm_stack: 512 * 1024,
wasm_backtrace: true,
wasm_backtrace_details_env_used: false,
native_unwind_info: None,
enabled_features: WasmFeatures::empty(),
disabled_features: WasmFeatures::empty(),
#[cfg(any(feature = "async", feature = "stack-switching"))]
async_stack_size: 2 << 20,
#[cfg(feature = "async")]
async_stack_zeroing: false,
#[cfg(feature = "async")]
stack_creator: None,
async_support: false,
module_version: ModuleVersionStrategy::default(),
parallel_compilation: !cfg!(miri),
memory_guaranteed_dense_image_size: 16 << 20,
force_memory_init_memfd: false,
wmemcheck: false,
#[cfg(feature = "coredump")]
coredump_on_trap: false,
macos_use_mach_ports: !cfg!(miri),
#[cfg(feature = "std")]
detect_host_feature: Some(detect_host_feature),
#[cfg(not(feature = "std"))]
detect_host_feature: None,
};
#[cfg(any(feature = "cranelift", feature = "winch"))]
{
ret.cranelift_debug_verifier(false);
ret.cranelift_opt_level(OptLevel::Speed);
if cfg!(miri) {
ret.cranelift_opt_level(OptLevel::None);
}
}
ret.wasm_backtrace_details(WasmBacktraceDetails::Environment);
ret
}
pub fn target(&mut self, target: &str) -> Result<&mut Self> {
self.target =
Some(target_lexicon::Triple::from_str(target).map_err(|e| anyhow::anyhow!(e))?);
Ok(self)
}
#[cfg(all(feature = "incremental-cache", feature = "cranelift"))]
pub fn enable_incremental_compilation(
&mut self,
cache_store: Arc<dyn CacheStore>,
) -> Result<&mut Self> {
self.compiler_config.cache_store = Some(cache_store);
Ok(self)
}
#[cfg(feature = "async")]
pub fn async_support(&mut self, enable: bool) -> &mut Self {
self.async_support = enable;
self
}
pub fn debug_info(&mut self, enable: bool) -> &mut Self {
self.tunables.generate_native_debuginfo = Some(enable);
self
}
pub fn wasm_backtrace(&mut self, enable: bool) -> &mut Self {
self.wasm_backtrace = enable;
self
}
pub fn wasm_backtrace_details(&mut self, enable: WasmBacktraceDetails) -> &mut Self {
self.wasm_backtrace_details_env_used = false;
self.tunables.parse_wasm_debuginfo = match enable {
WasmBacktraceDetails::Enable => Some(true),
WasmBacktraceDetails::Disable => Some(false),
WasmBacktraceDetails::Environment => {
#[cfg(feature = "std")]
{
self.wasm_backtrace_details_env_used = true;
std::env::var("WASMTIME_BACKTRACE_DETAILS")
.map(|s| Some(s == "1"))
.unwrap_or(Some(false))
}
#[cfg(not(feature = "std"))]
{
Some(false)
}
}
};
self
}
pub fn native_unwind_info(&mut self, enable: bool) -> &mut Self {
self.native_unwind_info = Some(enable);
self
}
pub fn consume_fuel(&mut self, enable: bool) -> &mut Self {
self.tunables.consume_fuel = Some(enable);
self
}
pub fn epoch_interruption(&mut self, enable: bool) -> &mut Self {
self.tunables.epoch_interruption = Some(enable);
self
}
pub fn max_wasm_stack(&mut self, size: usize) -> &mut Self {
self.max_wasm_stack = size;
self
}
#[cfg(any(feature = "async", feature = "stack-switching"))]
pub fn async_stack_size(&mut self, size: usize) -> &mut Self {
self.async_stack_size = size;
self
}
#[cfg(feature = "async")]
pub fn async_stack_zeroing(&mut self, enable: bool) -> &mut Self {
self.async_stack_zeroing = enable;
self
}
fn wasm_feature(&mut self, flag: WasmFeatures, enable: bool) -> &mut Self {
self.enabled_features.set(flag, enable);
self.disabled_features.set(flag, !enable);
self
}
pub fn wasm_tail_call(&mut self, enable: bool) -> &mut Self {
self.wasm_feature(WasmFeatures::TAIL_CALL, enable);
self
}
pub fn wasm_custom_page_sizes(&mut self, enable: bool) -> &mut Self {
self.wasm_feature(WasmFeatures::CUSTOM_PAGE_SIZES, enable);
self
}
#[cfg(feature = "threads")]
pub fn wasm_threads(&mut self, enable: bool) -> &mut Self {
self.wasm_feature(WasmFeatures::THREADS, enable);
self
}
pub fn wasm_shared_everything_threads(&mut self, enable: bool) -> &mut Self {
self.wasm_feature(WasmFeatures::SHARED_EVERYTHING_THREADS, enable);
self
}
#[cfg(feature = "gc")]
pub fn wasm_reference_types(&mut self, enable: bool) -> &mut Self {
self.wasm_feature(WasmFeatures::REFERENCE_TYPES, enable);
self
}
#[cfg(feature = "gc")]
pub fn wasm_function_references(&mut self, enable: bool) -> &mut Self {
self.wasm_feature(WasmFeatures::FUNCTION_REFERENCES, enable);
self
}
pub fn wasm_wide_arithmetic(&mut self, enable: bool) -> &mut Self {
self.wasm_feature(WasmFeatures::WIDE_ARITHMETIC, enable);
self
}
#[cfg(feature = "gc")]
pub fn wasm_gc(&mut self, enable: bool) -> &mut Self {
self.wasm_feature(WasmFeatures::GC, enable);
self
}
pub fn wasm_simd(&mut self, enable: bool) -> &mut Self {
self.wasm_feature(WasmFeatures::SIMD, enable);
self
}
pub fn wasm_relaxed_simd(&mut self, enable: bool) -> &mut Self {
self.wasm_feature(WasmFeatures::RELAXED_SIMD, enable);
self
}
pub fn relaxed_simd_deterministic(&mut self, enable: bool) -> &mut Self {
self.tunables.relaxed_simd_deterministic = Some(enable);
self
}
pub fn wasm_bulk_memory(&mut self, enable: bool) -> &mut Self {
self.wasm_feature(WasmFeatures::BULK_MEMORY, enable);
self
}
pub fn wasm_multi_value(&mut self, enable: bool) -> &mut Self {
self.wasm_feature(WasmFeatures::MULTI_VALUE, enable);
self
}
pub fn wasm_multi_memory(&mut self, enable: bool) -> &mut Self {
self.wasm_feature(WasmFeatures::MULTI_MEMORY, enable);
self
}
pub fn wasm_memory64(&mut self, enable: bool) -> &mut Self {
self.wasm_feature(WasmFeatures::MEMORY64, enable);
self
}
pub fn wasm_extended_const(&mut self, enable: bool) -> &mut Self {
self.wasm_feature(WasmFeatures::EXTENDED_CONST, enable);
self
}
pub fn wasm_stack_switching(&mut self, enable: bool) -> &mut Self {
self.wasm_feature(WasmFeatures::STACK_SWITCHING, enable);
self
}
#[cfg(feature = "component-model")]
pub fn wasm_component_model(&mut self, enable: bool) -> &mut Self {
self.wasm_feature(WasmFeatures::COMPONENT_MODEL, enable);
self
}
#[cfg(feature = "component-model-async")]
pub fn wasm_component_model_async(&mut self, enable: bool) -> &mut Self {
self.wasm_feature(WasmFeatures::CM_ASYNC, enable);
self
}
#[cfg(feature = "component-model-async")]
pub fn wasm_component_model_async_builtins(&mut self, enable: bool) -> &mut Self {
self.wasm_feature(WasmFeatures::CM_ASYNC_BUILTINS, enable);
self
}
#[cfg(feature = "component-model-async")]
pub fn wasm_component_model_async_stackful(&mut self, enable: bool) -> &mut Self {
self.wasm_feature(WasmFeatures::CM_ASYNC_STACKFUL, enable);
self
}
#[cfg(feature = "component-model")]
pub fn wasm_component_model_error_context(&mut self, enable: bool) -> &mut Self {
self.wasm_feature(WasmFeatures::CM_ERROR_CONTEXT, enable);
self
}
#[cfg(feature = "component-model")]
pub fn wasm_component_model_gc(&mut self, enable: bool) -> &mut Self {
self.wasm_feature(WasmFeatures::CM_GC, enable);
self
}
#[doc(hidden)] pub fn wasm_exceptions(&mut self, enable: bool) -> &mut Self {
self.wasm_feature(WasmFeatures::EXCEPTIONS, enable);
self
}
#[doc(hidden)] #[deprecated = "This configuration option only exists for internal \
usage with the spec testsuite. It may be removed at \
any time and without warning. Do not rely on it!"]
pub fn wasm_legacy_exceptions(&mut self, enable: bool) -> &mut Self {
self.wasm_feature(WasmFeatures::LEGACY_EXCEPTIONS, enable);
self
}
#[cfg(any(feature = "cranelift", feature = "winch"))]
pub fn strategy(&mut self, strategy: Strategy) -> &mut Self {
self.compiler_config.strategy = strategy.not_auto();
self
}
#[cfg(feature = "gc")]
pub fn collector(&mut self, collector: Collector) -> &mut Self {
self.collector = collector;
self
}
pub fn profiler(&mut self, profile: ProfilingStrategy) -> &mut Self {
self.profiling_strategy = profile;
self
}
#[cfg(any(feature = "cranelift", feature = "winch"))]
pub fn cranelift_debug_verifier(&mut self, enable: bool) -> &mut Self {
let val = if enable { "true" } else { "false" };
self.compiler_config
.settings
.insert("enable_verifier".to_string(), val.to_string());
self
}
#[cfg(any(feature = "cranelift", feature = "winch"))]
pub fn cranelift_opt_level(&mut self, level: OptLevel) -> &mut Self {
let val = match level {
OptLevel::None => "none",
OptLevel::Speed => "speed",
OptLevel::SpeedAndSize => "speed_and_size",
};
self.compiler_config
.settings
.insert("opt_level".to_string(), val.to_string());
self
}
#[cfg(any(feature = "cranelift", feature = "winch"))]
pub fn cranelift_regalloc_algorithm(&mut self, algo: RegallocAlgorithm) -> &mut Self {
let val = match algo {
RegallocAlgorithm::Backtracking => "backtracking",
};
self.compiler_config
.settings
.insert("regalloc_algorithm".to_string(), val.to_string());
self
}
#[cfg(any(feature = "cranelift", feature = "winch"))]
pub fn cranelift_nan_canonicalization(&mut self, enable: bool) -> &mut Self {
let val = if enable { "true" } else { "false" };
self.compiler_config
.settings
.insert("enable_nan_canonicalization".to_string(), val.to_string());
self
}
#[cfg(any(feature = "cranelift", feature = "winch"))]
pub fn cranelift_pcc(&mut self, enable: bool) -> &mut Self {
let val = if enable { "true" } else { "false" };
self.compiler_config
.settings
.insert("enable_pcc".to_string(), val.to_string());
self
}
#[cfg(any(feature = "cranelift", feature = "winch"))]
pub unsafe fn cranelift_flag_enable(&mut self, flag: &str) -> &mut Self {
self.compiler_config.flags.insert(flag.to_string());
self
}
#[cfg(any(feature = "cranelift", feature = "winch"))]
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
}
#[cfg(feature = "cache")]
pub fn cache(&mut self, cache: Option<Cache>) -> &mut Self {
self.cache = cache;
self
}
#[cfg(feature = "runtime")]
pub fn with_host_memory(&mut self, mem_creator: Arc<dyn MemoryCreator>) -> &mut Self {
self.mem_creator = Some(Arc::new(MemoryCreatorProxy(mem_creator)));
self
}
#[cfg(feature = "async")]
pub fn with_host_stack(&mut self, stack_creator: Arc<dyn StackCreator>) -> &mut Self {
self.stack_creator = Some(Arc::new(StackCreatorProxy(stack_creator)));
self
}
#[cfg(feature = "runtime")]
pub fn with_custom_code_memory(
&mut self,
custom_code_memory: Option<Arc<dyn CustomCodeMemory>>,
) -> &mut Self {
self.custom_code_memory = custom_code_memory;
self
}
pub fn allocation_strategy(
&mut self,
strategy: impl Into<InstanceAllocationStrategy>,
) -> &mut Self {
self.allocation_strategy = strategy.into();
self
}
pub fn memory_reservation(&mut self, bytes: u64) -> &mut Self {
self.tunables.memory_reservation = Some(bytes);
self
}
pub fn memory_may_move(&mut self, enable: bool) -> &mut Self {
self.tunables.memory_may_move = Some(enable);
self
}
pub fn memory_guard_size(&mut self, bytes: u64) -> &mut Self {
self.tunables.memory_guard_size = Some(bytes);
self
}
pub fn memory_reservation_for_growth(&mut self, bytes: u64) -> &mut Self {
self.tunables.memory_reservation_for_growth = Some(bytes);
self
}
pub fn guard_before_linear_memory(&mut self, enable: bool) -> &mut Self {
self.tunables.guard_before_linear_memory = Some(enable);
self
}
pub fn table_lazy_init(&mut self, table_lazy_init: bool) -> &mut Self {
self.tunables.table_lazy_init = Some(table_lazy_init);
self
}
pub fn module_version(&mut self, strategy: ModuleVersionStrategy) -> Result<&mut Self> {
match strategy {
ModuleVersionStrategy::Custom(ref v) => {
if v.as_bytes().len() > 255 {
bail!("custom module version cannot be more than 255 bytes: {}", v);
}
}
_ => {}
}
self.module_version = strategy;
Ok(self)
}
#[cfg(feature = "parallel-compilation")]
pub fn parallel_compilation(&mut self, parallel: bool) -> &mut Self {
self.parallel_compilation = parallel;
self
}
pub fn generate_address_map(&mut self, generate: bool) -> &mut Self {
self.tunables.generate_address_map = Some(generate);
self
}
pub fn memory_init_cow(&mut self, enable: bool) -> &mut Self {
self.tunables.memory_init_cow = Some(enable);
self
}
pub fn force_memory_init_memfd(&mut self, enable: bool) -> &mut Self {
self.force_memory_init_memfd = enable;
self
}
#[cfg(feature = "coredump")]
pub fn coredump_on_trap(&mut self, enable: bool) -> &mut Self {
self.coredump_on_trap = enable;
self
}
#[cfg(any(feature = "cranelift", feature = "winch"))]
pub fn wmemcheck(&mut self, enable: bool) -> &mut Self {
self.wmemcheck = enable;
self.compiler_config.wmemcheck = enable;
self
}
pub fn memory_guaranteed_dense_image_size(&mut self, size_in_bytes: u64) -> &mut Self {
self.memory_guaranteed_dense_image_size = size_in_bytes;
self
}
fn compiler_panicking_wasm_features(&self) -> WasmFeatures {
#[cfg(any(feature = "cranelift", feature = "winch"))]
match self.compiler_config.strategy {
None | Some(Strategy::Cranelift) => {
let mut unsupported = WasmFeatures::empty();
if self.compiler_target().is_pulley() {
unsupported |= WasmFeatures::THREADS;
unsupported |= WasmFeatures::STACK_SWITCHING;
}
use target_lexicon::*;
match self.compiler_target() {
Triple {
architecture: Architecture::X86_64 | Architecture::X86_64h,
operating_system:
OperatingSystem::Linux
| OperatingSystem::MacOSX(_)
| OperatingSystem::Darwin(_),
..
} => {
}
_ => {
unsupported |= WasmFeatures::STACK_SWITCHING;
}
}
unsupported
}
Some(Strategy::Winch) => {
let mut unsupported = WasmFeatures::GC
| WasmFeatures::FUNCTION_REFERENCES
| WasmFeatures::RELAXED_SIMD
| WasmFeatures::TAIL_CALL
| WasmFeatures::GC_TYPES
| WasmFeatures::EXCEPTIONS
| WasmFeatures::LEGACY_EXCEPTIONS
| WasmFeatures::STACK_SWITCHING;
match self.compiler_target().architecture {
target_lexicon::Architecture::Aarch64(_) => {
unsupported |= WasmFeatures::THREADS;
unsupported |= WasmFeatures::WIDE_ARITHMETIC;
}
_ => {}
}
unsupported
}
Some(Strategy::Auto) => unreachable!(),
}
#[cfg(not(any(feature = "cranelift", feature = "winch")))]
return WasmFeatures::empty();
}
fn features(&self) -> WasmFeatures {
let mut features = WasmFeatures::WASM2;
features |= WasmFeatures::MULTI_MEMORY;
features |= WasmFeatures::RELAXED_SIMD;
features |= WasmFeatures::TAIL_CALL;
features |= WasmFeatures::EXTENDED_CONST;
features |= WasmFeatures::MEMORY64;
features.set(WasmFeatures::GC_TYPES, cfg!(feature = "gc"));
features.set(WasmFeatures::THREADS, cfg!(feature = "threads"));
features.set(
WasmFeatures::COMPONENT_MODEL,
cfg!(feature = "component-model"),
);
features = features & !self.compiler_panicking_wasm_features();
debug_assert!((self.enabled_features & self.disabled_features).is_empty());
features &= !self.disabled_features;
features |= self.enabled_features;
features
}
pub(crate) fn compiler_target(&self) -> target_lexicon::Triple {
if let Some(target) = self.target.clone() {
return target;
}
if cfg!(default_target_pulley) {
return target_lexicon::Triple::pulley_host();
}
target_lexicon::Triple::host()
}
pub(crate) fn validate(&self) -> Result<(Tunables, WasmFeatures)> {
let features = self.features();
let unsupported = features & self.compiler_panicking_wasm_features();
if !unsupported.is_empty() {
for flag in WasmFeatures::FLAGS.iter() {
if !unsupported.contains(*flag.value()) {
continue;
}
bail!(
"the wasm_{} feature is not supported on this compiler configuration",
flag.name().to_lowercase()
);
}
panic!("should have returned an error by now")
}
#[cfg(any(feature = "async", feature = "stack-switching"))]
if self.async_support && self.max_wasm_stack > self.async_stack_size {
bail!("max_wasm_stack size cannot exceed the async_stack_size");
}
if self.max_wasm_stack == 0 {
bail!("max_wasm_stack size cannot be zero");
}
#[cfg(not(feature = "wmemcheck"))]
if self.wmemcheck {
bail!("wmemcheck (memory checker) was requested but is not enabled in this build");
}
let mut tunables = Tunables::default_for_target(&self.compiler_target())?;
if self.target.is_none() {
if !cfg!(has_native_signals) {
tunables.signals_based_traps = cfg!(has_native_signals);
tunables.memory_guard_size = 0;
}
if !cfg!(has_virtual_memory) {
tunables.memory_reservation = 0;
tunables.memory_reservation_for_growth = 1 << 20; tunables.memory_init_cow = false;
}
}
self.tunables.configure(&mut tunables);
#[cfg(any(feature = "cranelift", feature = "winch"))]
{
tunables.winch_callable = self.compiler_config.strategy == Some(Strategy::Winch);
}
tunables.collector = if features.gc_types() {
#[cfg(feature = "gc")]
{
use wasmtime_environ::Collector as EnvCollector;
Some(match self.collector.try_not_auto()? {
Collector::DeferredReferenceCounting => EnvCollector::DeferredReferenceCounting,
Collector::Null => EnvCollector::Null,
Collector::Auto => unreachable!(),
})
}
#[cfg(not(feature = "gc"))]
bail!("cannot use GC types: the `gc` feature was disabled at compile time")
} else {
None
};
Ok((tunables, features))
}
#[cfg(feature = "runtime")]
pub(crate) fn build_allocator(
&self,
tunables: &Tunables,
) -> Result<Box<dyn InstanceAllocator + Send + Sync>> {
#[cfg(feature = "async")]
let (stack_size, stack_zeroing) = (self.async_stack_size, self.async_stack_zeroing);
#[cfg(not(feature = "async"))]
let (stack_size, stack_zeroing) = (0, false);
let _ = tunables;
match &self.allocation_strategy {
InstanceAllocationStrategy::OnDemand => {
#[allow(unused_mut)]
let mut allocator = Box::new(OnDemandInstanceAllocator::new(
self.mem_creator.clone(),
stack_size,
stack_zeroing,
));
#[cfg(feature = "async")]
if let Some(stack_creator) = &self.stack_creator {
allocator.set_stack_creator(stack_creator.clone());
}
Ok(allocator)
}
#[cfg(feature = "pooling-allocator")]
InstanceAllocationStrategy::Pooling(config) => {
let mut config = config.config;
config.stack_size = stack_size;
config.async_stack_zeroing = stack_zeroing;
Ok(Box::new(crate::runtime::vm::PoolingInstanceAllocator::new(
&config, tunables,
)?))
}
}
}
#[cfg(feature = "runtime")]
pub(crate) fn build_gc_runtime(&self) -> Result<Option<Arc<dyn GcRuntime>>> {
if !self.features().gc_types() {
return Ok(None);
}
#[cfg(not(feature = "gc"))]
bail!("cannot create a GC runtime: the `gc` feature was disabled at compile time");
#[cfg(feature = "gc")]
#[cfg_attr(
not(any(feature = "gc-null", feature = "gc-drc")),
allow(unused_variables, unreachable_code)
)]
{
Ok(Some(match self.collector.try_not_auto()? {
#[cfg(feature = "gc-drc")]
Collector::DeferredReferenceCounting => {
Arc::new(crate::runtime::vm::DrcCollector::default()) as Arc<dyn GcRuntime>
}
#[cfg(not(feature = "gc-drc"))]
Collector::DeferredReferenceCounting => unreachable!(),
#[cfg(feature = "gc-null")]
Collector::Null => {
Arc::new(crate::runtime::vm::NullCollector::default()) as Arc<dyn GcRuntime>
}
#[cfg(not(feature = "gc-null"))]
Collector::Null => unreachable!(),
Collector::Auto => unreachable!(),
}))
}
}
#[cfg(feature = "runtime")]
pub(crate) fn build_profiler(&self) -> Result<Box<dyn ProfilingAgent>> {
Ok(match self.profiling_strategy {
ProfilingStrategy::PerfMap => profiling_agent::new_perfmap()?,
ProfilingStrategy::JitDump => profiling_agent::new_jitdump()?,
ProfilingStrategy::VTune => profiling_agent::new_vtune()?,
ProfilingStrategy::None => profiling_agent::new_null(),
ProfilingStrategy::Pulley => profiling_agent::new_pulley()?,
})
}
#[cfg(any(feature = "cranelift", feature = "winch"))]
pub(crate) fn build_compiler(
mut self,
tunables: &Tunables,
features: WasmFeatures,
) -> Result<(Self, Box<dyn wasmtime_environ::Compiler>)> {
let target = self.compiler_target();
let target_for_builder =
if self.target.is_none() && target == target_lexicon::Triple::host() {
None
} else {
Some(target.clone())
};
let mut compiler = match self.compiler_config.strategy {
#[cfg(feature = "cranelift")]
Some(Strategy::Cranelift) => wasmtime_cranelift::builder(target_for_builder)?,
#[cfg(not(feature = "cranelift"))]
Some(Strategy::Cranelift) => bail!("cranelift support not compiled in"),
#[cfg(feature = "winch")]
Some(Strategy::Winch) => wasmtime_winch::builder(target_for_builder)?,
#[cfg(not(feature = "winch"))]
Some(Strategy::Winch) => bail!("winch support not compiled in"),
None | Some(Strategy::Auto) => unreachable!(),
};
if let Some(path) = &self.compiler_config.clif_dir {
compiler.clif_dir(path)?;
}
self.compiler_config
.settings
.insert("probestack_strategy".into(), "inline".into());
self.compiler_config
.flags
.insert("enable_probestack".into());
self.compiler_config
.flags
.insert("enable_multi_ret_implicit_sret".into());
if let Some(unwind_requested) = self.native_unwind_info {
if !self
.compiler_config
.ensure_setting_unset_or_given("unwind_info", &unwind_requested.to_string())
{
bail!(
"incompatible settings requested for Cranelift and Wasmtime `unwind-info` settings"
);
}
}
if target.operating_system == target_lexicon::OperatingSystem::Windows {
if !self
.compiler_config
.ensure_setting_unset_or_given("unwind_info", "true")
{
bail!("`native_unwind_info` cannot be disabled on Windows");
}
}
self.compiler_config
.settings
.insert("preserve_frame_pointers".into(), "true".into());
if !tunables.signals_based_traps {
let mut ok = self
.compiler_config
.ensure_setting_unset_or_given("enable_table_access_spectre_mitigation", "false");
ok = ok
&& self.compiler_config.ensure_setting_unset_or_given(
"enable_heap_access_spectre_mitigation",
"false",
);
if !ok {
bail!(
"when signals-based traps are disabled then spectre \
mitigations must also be disabled"
);
}
}
if features.contains(WasmFeatures::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 features.contains(WasmFeatures::RELAXED_SIMD) && !features.contains(WasmFeatures::SIMD) {
bail!("cannot disable the simd proposal but enable the relaxed simd proposal");
}
if features.contains(WasmFeatures::STACK_SWITCHING) {
use target_lexicon::OperatingSystem;
let model = match target.operating_system {
OperatingSystem::Windows => "update_windows_tib",
OperatingSystem::Linux
| OperatingSystem::MacOSX(_)
| OperatingSystem::Darwin(_) => "basic",
_ => bail!("stack-switching feature not supported on this platform "),
};
if !self
.compiler_config
.ensure_setting_unset_or_given("stack_switch_model", model)
{
bail!(
"compiler option 'stack_switch_model' must be set to '{}' on this platform",
model
);
}
}
for (k, v) in self.compiler_config.settings.iter() {
compiler.set(k, v)?;
}
for flag in self.compiler_config.flags.iter() {
compiler.enable(flag)?;
}
#[cfg(all(feature = "incremental-cache", feature = "cranelift"))]
if let Some(cache_store) = &self.compiler_config.cache_store {
compiler.enable_incremental_compilation(cache_store.clone())?;
}
compiler.set_tunables(tunables.clone())?;
compiler.wmemcheck(self.compiler_config.wmemcheck);
Ok((self, compiler.build()?))
}
#[cfg(feature = "component-model")]
pub fn debug_adapter_modules(&mut self, debug: bool) -> &mut Self {
self.tunables.debug_adapter_modules = Some(debug);
self
}
#[cfg(any(feature = "cranelift", feature = "winch"))]
pub fn emit_clif(&mut self, path: &Path) -> &mut Self {
self.compiler_config.clif_dir = Some(path.to_path_buf());
self
}
pub fn macos_use_mach_ports(&mut self, mach_ports: bool) -> &mut Self {
self.macos_use_mach_ports = mach_ports;
self
}
pub unsafe fn detect_host_feature(&mut self, detect: fn(&str) -> Option<bool>) -> &mut Self {
self.detect_host_feature = Some(detect);
self
}
pub fn signals_based_traps(&mut self, enable: bool) -> &mut Self {
self.tunables.signals_based_traps = Some(enable);
self
}
}
impl Default for Config {
fn default() -> Config {
Config::new()
}
}
impl fmt::Debug for Config {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut f = f.debug_struct("Config");
let features = self.features();
for flag in WasmFeatures::FLAGS.iter() {
f.field(
&format!("wasm_{}", flag.name().to_lowercase()),
&features.contains(*flag.value()),
);
}
f.field("parallel_compilation", &self.parallel_compilation);
#[cfg(any(feature = "cranelift", feature = "winch"))]
{
f.field("compiler_config", &self.compiler_config);
}
self.tunables.format(&mut f);
f.finish()
}
}
#[non_exhaustive]
#[derive(PartialEq, Eq, Clone, Debug, Copy)]
pub enum Strategy {
Auto,
Cranelift,
Winch,
}
#[cfg(any(feature = "winch", feature = "cranelift"))]
impl Strategy {
fn not_auto(&self) -> Option<Strategy> {
match self {
Strategy::Auto => {
if cfg!(feature = "cranelift") {
Some(Strategy::Cranelift)
} else if cfg!(feature = "winch") {
Some(Strategy::Winch)
} else {
None
}
}
other => Some(*other),
}
}
}
#[non_exhaustive]
#[derive(PartialEq, Eq, Clone, Debug, Copy)]
pub enum Collector {
Auto,
DeferredReferenceCounting,
Null,
}
impl Default for Collector {
fn default() -> Collector {
Collector::Auto
}
}
#[cfg(feature = "gc")]
impl Collector {
fn not_auto(&self) -> Option<Collector> {
match self {
Collector::Auto => {
if cfg!(feature = "gc-drc") {
Some(Collector::DeferredReferenceCounting)
} else if cfg!(feature = "gc-null") {
Some(Collector::Null)
} else {
None
}
}
other => Some(*other),
}
}
fn try_not_auto(&self) -> Result<Self> {
match self.not_auto() {
#[cfg(feature = "gc-drc")]
Some(c @ Collector::DeferredReferenceCounting) => Ok(c),
#[cfg(not(feature = "gc-drc"))]
Some(Collector::DeferredReferenceCounting) => bail!(
"cannot create an engine using the deferred reference-counting \
collector because the `gc-drc` feature was not enabled at \
compile time",
),
#[cfg(feature = "gc-null")]
Some(c @ Collector::Null) => Ok(c),
#[cfg(not(feature = "gc-null"))]
Some(Collector::Null) => bail!(
"cannot create an engine using the null collector because \
the `gc-null` feature was not enabled at compile time",
),
Some(Collector::Auto) => unreachable!(),
None => bail!(
"cannot create an engine with GC support when none of the \
collectors are available; enable one of the following \
features: `gc-drc`, `gc-null`",
),
}
}
}
#[non_exhaustive]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum OptLevel {
None,
Speed,
SpeedAndSize,
}
#[non_exhaustive]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum RegallocAlgorithm {
Backtracking,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ProfilingStrategy {
None,
PerfMap,
JitDump,
VTune,
Pulley,
}
#[derive(Debug, Clone, Copy)]
pub enum WasmBacktraceDetails {
Enable,
Disable,
Environment,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum MpkEnabled {
Auto,
Enable,
Disable,
}
#[cfg(feature = "pooling-allocator")]
#[derive(Debug, Clone, Default)]
pub struct PoolingAllocationConfig {
config: crate::runtime::vm::PoolingInstanceAllocatorConfig,
}
#[cfg(feature = "pooling-allocator")]
impl PoolingAllocationConfig {
pub fn new() -> PoolingAllocationConfig {
PoolingAllocationConfig::default()
}
pub fn max_unused_warm_slots(&mut self, max: u32) -> &mut Self {
self.config.max_unused_warm_slots = max;
self
}
pub fn decommit_batch_size(&mut self, batch_size: usize) -> &mut Self {
self.config.decommit_batch_size = batch_size;
self
}
#[cfg(feature = "async")]
pub fn async_stack_keep_resident(&mut self, size: usize) -> &mut Self {
self.config.async_stack_keep_resident = size;
self
}
pub fn linear_memory_keep_resident(&mut self, size: usize) -> &mut Self {
self.config.linear_memory_keep_resident = size;
self
}
pub fn table_keep_resident(&mut self, size: usize) -> &mut Self {
self.config.table_keep_resident = size;
self
}
pub fn total_component_instances(&mut self, count: u32) -> &mut Self {
self.config.limits.total_component_instances = count;
self
}
pub fn max_component_instance_size(&mut self, size: usize) -> &mut Self {
self.config.limits.component_instance_size = size;
self
}
pub fn max_core_instances_per_component(&mut self, count: u32) -> &mut Self {
self.config.limits.max_core_instances_per_component = count;
self
}
pub fn max_memories_per_component(&mut self, count: u32) -> &mut Self {
self.config.limits.max_memories_per_component = count;
self
}
pub fn max_tables_per_component(&mut self, count: u32) -> &mut Self {
self.config.limits.max_tables_per_component = count;
self
}
pub fn total_memories(&mut self, count: u32) -> &mut Self {
self.config.limits.total_memories = count;
self
}
pub fn total_tables(&mut self, count: u32) -> &mut Self {
self.config.limits.total_tables = count;
self
}
#[cfg(feature = "async")]
pub fn total_stacks(&mut self, count: u32) -> &mut Self {
self.config.limits.total_stacks = count;
self
}
pub fn total_core_instances(&mut self, count: u32) -> &mut Self {
self.config.limits.total_core_instances = count;
self
}
pub fn max_core_instance_size(&mut self, size: usize) -> &mut Self {
self.config.limits.core_instance_size = size;
self
}
pub fn max_tables_per_module(&mut self, tables: u32) -> &mut Self {
self.config.limits.max_tables_per_module = tables;
self
}
pub fn table_elements(&mut self, elements: usize) -> &mut Self {
self.config.limits.table_elements = elements;
self
}
pub fn max_memories_per_module(&mut self, memories: u32) -> &mut Self {
self.config.limits.max_memories_per_module = memories;
self
}
pub fn max_memory_size(&mut self, bytes: usize) -> &mut Self {
self.config.limits.max_memory_size = bytes;
self
}
#[cfg(feature = "memory-protection-keys")]
pub fn memory_protection_keys(&mut self, enable: MpkEnabled) -> &mut Self {
self.config.memory_protection_keys = enable;
self
}
#[cfg(feature = "memory-protection-keys")]
pub fn max_memory_protection_keys(&mut self, max: usize) -> &mut Self {
self.config.max_memory_protection_keys = max;
self
}
#[cfg(feature = "memory-protection-keys")]
pub fn are_memory_protection_keys_available() -> bool {
crate::runtime::vm::mpk::is_supported()
}
#[cfg(feature = "gc")]
pub fn total_gc_heaps(&mut self, count: u32) -> &mut Self {
self.config.limits.total_gc_heaps = count;
self
}
}
#[cfg(feature = "std")]
fn detect_host_feature(feature: &str) -> Option<bool> {
#[cfg(target_arch = "aarch64")]
{
return match feature {
"lse" => Some(std::arch::is_aarch64_feature_detected!("lse")),
"paca" => Some(std::arch::is_aarch64_feature_detected!("paca")),
"fp16" => Some(std::arch::is_aarch64_feature_detected!("fp16")),
_ => None,
};
}
#[cfg(all(target_arch = "s390x", target_os = "linux"))]
{
let v = unsafe { libc::getauxval(libc::AT_HWCAP) };
const HWCAP_S390X_VXRS_EXT2: libc::c_ulong = 32768;
return match feature {
"vxrs_ext2" | "mie2" => Some((v & HWCAP_S390X_VXRS_EXT2) != 0),
_ => None,
};
}
#[cfg(target_arch = "riscv64")]
{
return match feature {
_ => Some(true),
};
}
#[cfg(target_arch = "x86_64")]
{
return match feature {
"cmpxchg16b" => Some(std::is_x86_feature_detected!("cmpxchg16b")),
"sse3" => Some(std::is_x86_feature_detected!("sse3")),
"ssse3" => Some(std::is_x86_feature_detected!("ssse3")),
"sse4.1" => Some(std::is_x86_feature_detected!("sse4.1")),
"sse4.2" => Some(std::is_x86_feature_detected!("sse4.2")),
"popcnt" => Some(std::is_x86_feature_detected!("popcnt")),
"avx" => Some(std::is_x86_feature_detected!("avx")),
"avx2" => Some(std::is_x86_feature_detected!("avx2")),
"fma" => Some(std::is_x86_feature_detected!("fma")),
"bmi1" => Some(std::is_x86_feature_detected!("bmi1")),
"bmi2" => Some(std::is_x86_feature_detected!("bmi2")),
"avx512bitalg" => Some(std::is_x86_feature_detected!("avx512bitalg")),
"avx512dq" => Some(std::is_x86_feature_detected!("avx512dq")),
"avx512f" => Some(std::is_x86_feature_detected!("avx512f")),
"avx512vl" => Some(std::is_x86_feature_detected!("avx512vl")),
"avx512vbmi" => Some(std::is_x86_feature_detected!("avx512vbmi")),
"lzcnt" => Some(std::is_x86_feature_detected!("lzcnt")),
_ => None,
};
}
#[allow(unreachable_code)]
{
let _ = feature;
return None;
}
}