use anyhow::{bail, ensure, Result};
use serde_derive::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::fmt;
#[cfg(any(feature = "cache", feature = "cranelift", feature = "winch"))]
use std::path::Path;
use std::str::FromStr;
use std::sync::Arc;
use target_lexicon::Architecture;
use wasmparser::WasmFeatures;
#[cfg(feature = "cache")]
use wasmtime_cache::CacheConfig;
use wasmtime_environ::Tunables;
#[cfg(feature = "runtime")]
use crate::memory::MemoryCreator;
#[cfg(feature = "runtime")]
use crate::profiling_agent::{self, ProfilingAgent};
#[cfg(feature = "runtime")]
use crate::trampoline::MemoryCreatorProxy;
#[cfg(feature = "runtime")]
use wasmtime_runtime::{
GcRuntime, InstanceAllocator, OnDemandInstanceAllocator, RuntimeMemoryCreator,
};
#[cfg(feature = "async")]
use crate::stack::{StackCreator, StackCreatorProxy};
#[cfg(feature = "async")]
use wasmtime_fiber::RuntimeFiberStackCreator;
#[cfg(all(feature = "incremental-cache", feature = "cranelift"))]
pub use wasmtime_environ::CacheStore;
#[cfg(feature = "pooling-allocator")]
use wasmtime_runtime::mpk;
#[cfg(feature = "pooling-allocator")]
pub use wasmtime_runtime::MpkEnabled;
#[derive(Clone)]
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
}
}
#[derive(Clone)]
pub enum ModuleVersionStrategy {
WasmtimeVersion,
Custom(String),
None,
}
impl Default for ModuleVersionStrategy {
fn default() -> Self {
ModuleVersionStrategy::WasmtimeVersion
}
}
impl std::hash::Hash for ModuleVersionStrategy {
fn hash<H: std::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,
profiling_strategy: ProfilingStrategy,
tunables: ConfigTunables,
#[cfg(feature = "cache")]
pub(crate) cache_config: CacheConfig,
#[cfg(feature = "runtime")]
pub(crate) mem_creator: Option<Arc<dyn RuntimeMemoryCreator>>,
pub(crate) allocation_strategy: InstanceAllocationStrategy,
pub(crate) max_wasm_stack: usize,
pub(crate) features: WasmFeatures,
pub(crate) wasm_backtrace: bool,
pub(crate) wasm_backtrace_details_env_used: bool,
pub(crate) native_unwind_info: Option<bool>,
#[cfg(feature = "async")]
pub(crate) async_stack_size: usize,
#[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_init_cow: bool,
pub(crate) memory_guaranteed_dense_image_size: u64,
pub(crate) force_memory_init_memfd: bool,
pub(crate) wmemcheck: bool,
pub(crate) coredump_on_trap: bool,
pub(crate) macos_use_mach_ports: bool,
}
#[derive(Default, Clone)]
struct ConfigTunables {
static_memory_bound: Option<u64>,
static_memory_offset_guard_size: Option<u64>,
dynamic_memory_offset_guard_size: Option<u64>,
dynamic_memory_growth_reserve: Option<u64>,
generate_native_debuginfo: Option<bool>,
parse_wasm_debuginfo: Option<bool>,
consume_fuel: Option<bool>,
epoch_interruption: Option<bool>,
static_memory_bound_is_maximum: Option<bool>,
guard_before_linear_memory: Option<bool>,
generate_address_map: Option<bool>,
debug_adapter_modules: Option<bool>,
relaxed_simd_deterministic: Option<bool>,
tail_callable: Option<bool>,
}
#[cfg(any(feature = "cranelift", feature = "winch"))]
#[derive(Debug, Clone)]
struct CompilerConfig {
strategy: Strategy,
target: Option<target_lexicon::Triple>,
settings: HashMap<String, String>,
flags: 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(strategy: Strategy) -> Self {
Self {
strategy,
target: None,
settings: HashMap::new(),
flags: HashSet::new(),
#[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(Strategy::Auto)
}
}
impl Config {
pub fn new() -> Self {
let mut ret = Self {
tunables: ConfigTunables::default(),
#[cfg(any(feature = "cranelift", feature = "winch"))]
compiler_config: CompilerConfig::default(),
#[cfg(feature = "cache")]
cache_config: CacheConfig::new_cache_disabled(),
profiling_strategy: ProfilingStrategy::None,
#[cfg(feature = "runtime")]
mem_creator: None,
allocation_strategy: InstanceAllocationStrategy::OnDemand,
max_wasm_stack: 512 * 1024,
wasm_backtrace: true,
wasm_backtrace_details_env_used: false,
native_unwind_info: None,
features: WasmFeatures::default(),
#[cfg(feature = "async")]
async_stack_size: 2 << 20,
#[cfg(feature = "async")]
stack_creator: None,
async_support: false,
module_version: ModuleVersionStrategy::default(),
parallel_compilation: !cfg!(miri),
memory_init_cow: true,
memory_guaranteed_dense_image_size: 16 << 20,
force_memory_init_memfd: false,
wmemcheck: false,
coredump_on_trap: false,
macos_use_mach_ports: !cfg!(miri),
};
#[cfg(any(feature = "cranelift", feature = "winch"))]
{
ret.cranelift_debug_verifier(false);
ret.cranelift_opt_level(OptLevel::Speed);
}
ret.features.reference_types = cfg!(feature = "gc");
ret.features.threads = cfg!(feature = "threads");
ret.features.component_model = cfg!(feature = "component-model");
if !cfg!(feature = "gc") {
ret.features.function_references = false;
ret.features.gc = false;
}
ret.wasm_multi_value(true);
ret.wasm_bulk_memory(true);
ret.wasm_simd(true);
ret.wasm_backtrace_details(WasmBacktraceDetails::Environment);
ret.features.tail_call = false;
ret
}
#[cfg(any(feature = "cranelift", feature = "winch"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "cranelift", feature = "winch"))))]
pub fn target(&mut self, target: &str) -> Result<&mut Self> {
self.compiler_config.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")]
#[cfg_attr(docsrs, doc(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 => {
self.wasm_backtrace_details_env_used = true;
std::env::var("WASMTIME_BACKTRACE_DETAILS")
.map(|s| Some(s == "1"))
.unwrap_or(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(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn async_stack_size(&mut self, size: usize) -> &mut Self {
self.async_stack_size = size;
self
}
pub fn wasm_tail_call(&mut self, enable: bool) -> &mut Self {
self.features.tail_call = enable;
self.tunables.tail_callable = Some(enable);
self
}
#[cfg(feature = "threads")]
#[cfg_attr(docsrs, doc(cfg(feature = "threads")))]
pub fn wasm_threads(&mut self, enable: bool) -> &mut Self {
self.features.threads = enable;
self
}
#[cfg(feature = "gc")]
#[cfg_attr(docsrs, doc(cfg(feature = "gc")))]
pub fn wasm_reference_types(&mut self, enable: bool) -> &mut Self {
self.features.reference_types = enable;
self
}
#[cfg(feature = "gc")]
#[cfg_attr(docsrs, doc(cfg(feature = "gc")))]
pub fn wasm_function_references(&mut self, enable: bool) -> &mut Self {
self.features.function_references = enable;
self
}
#[cfg(feature = "gc")]
#[cfg_attr(docsrs, doc(cfg(feature = "gc")))]
pub fn wasm_gc(&mut self, enable: bool) -> &mut Self {
self.features.gc = enable;
self
}
pub fn wasm_simd(&mut self, enable: bool) -> &mut Self {
self.features.simd = enable;
self
}
pub fn wasm_relaxed_simd(&mut self, enable: bool) -> &mut Self {
self.features.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.features.bulk_memory = enable;
self
}
pub fn wasm_multi_value(&mut self, enable: bool) -> &mut Self {
self.features.multi_value = enable;
self
}
pub fn wasm_multi_memory(&mut self, enable: bool) -> &mut Self {
self.features.multi_memory = enable;
self
}
pub fn wasm_memory64(&mut self, enable: bool) -> &mut Self {
self.features.memory64 = enable;
self
}
#[cfg(feature = "component-model")]
pub fn wasm_component_model(&mut self, enable: bool) -> &mut Self {
self.features.component_model = enable;
self
}
#[cfg(any(feature = "cranelift", feature = "winch"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "cranelift", feature = "winch"))))]
pub fn strategy(&mut self, strategy: Strategy) -> &mut Self {
self.compiler_config.strategy = strategy;
self
}
pub fn profiler(&mut self, profile: ProfilingStrategy) -> &mut Self {
self.profiling_strategy = profile;
self
}
#[cfg(any(feature = "cranelift", feature = "winch"))]
#[cfg_attr(docsrs, doc(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"))]
#[cfg_attr(docsrs, doc(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"))]
#[cfg_attr(docsrs, doc(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"))]
#[cfg_attr(docsrs, doc(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"))]
#[cfg_attr(docsrs, doc(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"))]
#[cfg_attr(docsrs, doc(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")]
#[cfg_attr(docsrs, doc(cfg(feature = "cache")))]
pub fn cache_config_load(&mut self, path: impl AsRef<Path>) -> Result<&mut Self> {
self.cache_config = CacheConfig::from_file(Some(path.as_ref()))?;
Ok(self)
}
#[cfg(feature = "cache")]
#[cfg_attr(docsrs, doc(cfg(feature = "cache")))]
pub fn disable_cache(&mut self) -> &mut Self {
self.cache_config = CacheConfig::new_cache_disabled();
self
}
#[cfg(feature = "cache")]
#[cfg_attr(docsrs, doc(cfg(feature = "cache")))]
pub fn cache_config_load_default(&mut self) -> Result<&mut Self> {
self.cache_config = CacheConfig::from_file(None)?;
Ok(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")]
#[cfg_attr(docsrs, doc(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
}
pub fn allocation_strategy(&mut self, strategy: InstanceAllocationStrategy) -> &mut Self {
self.allocation_strategy = strategy;
self
}
pub fn static_memory_maximum_size(&mut self, max_size: u64) -> &mut Self {
let max_pages = max_size / u64::from(wasmtime_environ::WASM_PAGE_SIZE);
self.tunables.static_memory_bound = Some(max_pages);
self
}
pub fn static_memory_forced(&mut self, force: bool) -> &mut Self {
self.tunables.static_memory_bound_is_maximum = Some(force);
self
}
pub fn static_memory_guard_size(&mut self, guard_size: u64) -> &mut Self {
let guard_size = round_up_to_pages(guard_size);
self.tunables.static_memory_offset_guard_size = Some(guard_size);
self
}
pub fn dynamic_memory_guard_size(&mut self, guard_size: u64) -> &mut Self {
let guard_size = round_up_to_pages(guard_size);
self.tunables.dynamic_memory_offset_guard_size = Some(guard_size);
self
}
pub fn dynamic_memory_reserved_for_growth(&mut self, reserved: u64) -> &mut Self {
self.tunables.dynamic_memory_growth_reserve = Some(round_up_to_pages(reserved));
self
}
pub fn guard_before_linear_memory(&mut self, guard: bool) -> &mut Self {
self.tunables.guard_before_linear_memory = Some(guard);
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")]
#[cfg_attr(docsrs, doc(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.memory_init_cow = enable;
self
}
pub fn force_memory_init_memfd(&mut self, enable: bool) -> &mut Self {
self.force_memory_init_memfd = enable;
self
}
#[cfg(feature = "coredump")]
#[cfg_attr(docsrs, doc(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
}
pub(crate) fn validate(&self) -> Result<Tunables> {
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");
}
if self.features.function_references && !self.features.reference_types {
bail!("feature 'function_references' requires 'reference_types' to be enabled");
}
if self.features.gc && !self.features.function_references {
bail!("feature 'gc' requires 'function_references' to be enabled");
}
#[cfg(feature = "async")]
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");
}
#[cfg(not(any(feature = "cranelift", feature = "winch")))]
let mut tunables = Tunables::default_host();
#[cfg(any(feature = "cranelift", feature = "winch"))]
let mut tunables = match &self.compiler_config.target.as_ref() {
Some(target) => Tunables::default_for_target(target)?,
None => Tunables::default_host(),
};
macro_rules! set_fields {
($($field:ident)*) => (
let ConfigTunables {
$($field,)*
} = &self.tunables;
$(
if let Some(e) = $field {
tunables.$field = *e;
}
)*
)
}
set_fields! {
static_memory_bound
static_memory_offset_guard_size
dynamic_memory_offset_guard_size
dynamic_memory_growth_reserve
generate_native_debuginfo
parse_wasm_debuginfo
consume_fuel
epoch_interruption
static_memory_bound_is_maximum
guard_before_linear_memory
generate_address_map
debug_adapter_modules
relaxed_simd_deterministic
tail_callable
}
#[cfg(any(feature = "cranelift", feature = "winch"))]
{
tunables.winch_callable = match self.compiler_config.strategy {
Strategy::Auto => !cfg!(feature = "cranelift") && cfg!(feature = "winch"),
Strategy::Cranelift => false,
Strategy::Winch => true,
};
}
if tunables.static_memory_offset_guard_size < tunables.dynamic_memory_offset_guard_size {
bail!("static memory guard size cannot be smaller than dynamic memory guard size");
}
Ok(tunables)
}
#[cfg(feature = "runtime")]
pub(crate) fn build_allocator(
&self,
tunables: &Tunables,
) -> Result<Box<dyn InstanceAllocator + Send + Sync>> {
#[cfg(feature = "async")]
let stack_size = self.async_stack_size;
#[cfg(not(feature = "async"))]
let stack_size = 0;
let _ = tunables;
match &self.allocation_strategy {
InstanceAllocationStrategy::OnDemand => {
#[allow(unused_mut)]
let mut allocator = Box::new(OnDemandInstanceAllocator::new(
self.mem_creator.clone(),
stack_size,
));
#[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;
Ok(Box::new(wasmtime_runtime::PoolingInstanceAllocator::new(
&config, tunables,
)?))
}
}
}
#[cfg(feature = "runtime")]
pub(crate) fn build_gc_runtime(&self) -> Result<Arc<dyn GcRuntime>> {
Ok(Arc::new(wasmtime_runtime::default_gc_runtime()) as Arc<dyn GcRuntime>)
}
#[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(),
})
}
#[cfg(any(feature = "cranelift", feature = "winch"))]
pub(crate) fn build_compiler(
mut self,
tunables: &Tunables,
) -> Result<(Self, Box<dyn wasmtime_environ::Compiler>)> {
let target = self.compiler_config.target.clone();
let mut compiler = match self.compiler_config.strategy {
#[cfg(feature = "cranelift")]
Strategy::Auto => wasmtime_cranelift::builder(target)?,
#[cfg(all(feature = "winch", not(feature = "cranelift")))]
Strategy::Auto => wasmtime_winch::builder(target)?,
#[cfg(feature = "cranelift")]
Strategy::Cranelift => wasmtime_cranelift::builder(target)?,
#[cfg(not(feature = "cranelift"))]
Strategy::Cranelift => bail!("cranelift support not compiled in"),
#[cfg(feature = "winch")]
Strategy::Winch => wasmtime_winch::builder(target)?,
#[cfg(not(feature = "winch"))]
Strategy::Winch => bail!("winch support not compiled in"),
};
if let Some(path) = &self.compiler_config.clif_dir {
compiler.clif_dir(path)?;
}
self.compiler_config
.settings
.insert("probestack_strategy".into(), "inline".into());
let host = target_lexicon::Triple::host();
let target = self
.compiler_config
.target
.as_ref()
.unwrap_or(&host)
.clone();
if probestack_supported(target.architecture) {
self.compiler_config
.flags
.insert("enable_probestack".into());
}
if self.features.tail_call {
ensure!(
target.architecture != Architecture::S390x,
"Tail calls are not supported on s390x yet: \
https://github.com/bytecodealliance/wasmtime/issues/6530"
);
}
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 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.relaxed_simd && !self.features.simd {
bail!("cannot disable the simd proposal but enable the relaxed simd proposal");
}
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(feature = "incremental-cache")]
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
}
}
#[cfg(not(feature = "runtime"))]
fn round_up_to_pages(val: u64) -> u64 {
val
}
#[cfg(feature = "runtime")]
fn round_up_to_pages(val: u64) -> u64 {
let page_size = wasmtime_runtime::page_size() as 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 / page_size + 1)
}
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");
f.field("debug_info", &self.tunables.generate_native_debuginfo)
.field("wasm_threads", &self.features.threads)
.field("wasm_reference_types", &self.features.reference_types)
.field(
"wasm_function_references",
&self.features.function_references,
)
.field("wasm_gc", &self.features.gc)
.field("wasm_bulk_memory", &self.features.bulk_memory)
.field("wasm_simd", &self.features.simd)
.field("wasm_relaxed_simd", &self.features.relaxed_simd)
.field("wasm_multi_value", &self.features.multi_value)
.field("parallel_compilation", &self.parallel_compilation);
#[cfg(any(feature = "cranelift", feature = "winch"))]
{
f.field("compiler_config", &self.compiler_config);
}
if let Some(enable) = self.tunables.parse_wasm_debuginfo {
f.field("parse_wasm_debuginfo", &enable);
}
if let Some(size) = self.tunables.static_memory_bound {
f.field(
"static_memory_maximum_size",
&(u64::from(size) * u64::from(wasmtime_environ::WASM_PAGE_SIZE)),
);
}
if let Some(size) = self.tunables.static_memory_offset_guard_size {
f.field("static_memory_guard_size", &size);
}
if let Some(size) = self.tunables.dynamic_memory_offset_guard_size {
f.field("dynamic_memory_guard_size", &size);
}
if let Some(enable) = self.tunables.guard_before_linear_memory {
f.field("guard_before_linear_memory", &enable);
}
f.finish()
}
}
#[non_exhaustive]
#[derive(PartialEq, Eq, Clone, Debug, Copy)]
pub enum Strategy {
Auto,
Cranelift,
Winch,
}
#[non_exhaustive]
#[derive(Copy, Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
pub enum OptLevel {
None,
Speed,
SpeedAndSize,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ProfilingStrategy {
None,
PerfMap,
JitDump,
VTune,
}
#[derive(Debug, Clone, Copy)]
pub enum WasmBacktraceDetails {
Enable,
Disable,
Environment,
}
#[cfg(feature = "pooling-allocator")]
#[derive(Debug, Clone, Default)]
pub struct PoolingAllocationConfig {
config: wasmtime_runtime::PoolingInstanceAllocatorConfig,
}
#[cfg(feature = "pooling-allocator")]
impl PoolingAllocationConfig {
pub fn max_unused_warm_slots(&mut self, max: u32) -> &mut Self {
self.config.max_unused_warm_slots = max;
self
}
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn async_stack_zeroing(&mut self, enable: bool) -> &mut Self {
self.config.async_stack_zeroing = enable;
self
}
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn async_stack_keep_resident(&mut self, size: usize) -> &mut Self {
let size = round_up_to_pages(size as u64) as usize;
self.config.async_stack_keep_resident = size;
self
}
pub fn linear_memory_keep_resident(&mut self, size: usize) -> &mut Self {
let size = round_up_to_pages(size as u64) as usize;
self.config.linear_memory_keep_resident = size;
self
}
pub fn table_keep_resident(&mut self, size: usize) -> &mut Self {
let size = round_up_to_pages(size as u64) as usize;
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: u32) -> &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 memory_pages(&mut self, pages: u64) -> &mut Self {
self.config.limits.memory_pages = pages;
self
}
pub fn memory_protection_keys(&mut self, enable: MpkEnabled) -> &mut Self {
self.config.memory_protection_keys = enable;
self
}
pub fn max_memory_protection_keys(&mut self, max: usize) -> &mut Self {
self.config.max_memory_protection_keys = max;
self
}
pub fn are_memory_protection_keys_available() -> bool {
mpk::is_supported()
}
#[cfg(feature = "gc")]
#[cfg_attr(docsrs, doc(cfg(feature = "gc")))]
pub fn total_gc_heaps(&mut self, count: u32) -> &mut Self {
self.config.limits.total_gc_heaps = count;
self
}
}
pub(crate) fn probestack_supported(arch: Architecture) -> bool {
matches!(
arch,
Architecture::X86_64 | Architecture::Aarch64(_) | Architecture::Riscv64(_)
)
}