Browse Source

fix(wasmtime):`Config` methods should be idempotent (#4252)

This commit refactored `Config` to use a seperate `CompilerConfig` field instead
of operating on `CompilerBuilder` directly to make all its methods idempotent.

Fixes #4189
pull/4263/head
Pure White 2 years ago
committed by GitHub
parent
commit
258dc9de42
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 11
      RELEASES.md
  2. 4
      crates/c-api/include/wasmtime/config.h
  3. 10
      crates/c-api/src/config.rs
  4. 6
      crates/cli-flags/src/lib.rs
  5. 5
      crates/cranelift/src/builder.rs
  6. 3
      crates/environ/src/compilation.rs
  7. 8
      crates/fuzzing/src/generators.rs
  8. 328
      crates/wasmtime/src/config.rs
  9. 25
      crates/wasmtime/src/engine.rs
  10. 2
      crates/wasmtime/src/module.rs
  11. 2
      crates/wasmtime/src/trampoline/func.rs
  12. 2
      fuzz/fuzz_targets/compile.rs
  13. 4
      tests/all/relocs.rs

11
RELEASES.md

@ -8,6 +8,17 @@ Unreleased.
### Changed ### Changed
* The `Config::strategy`, `Config::cranelift_flag_enable`, `Config::cranelift_flag_set`
and `Config::profiler` APIs now return `&mut Self` instead of `Result<&mut Self>`
since the validation is deferred until `Engine::new`.
[#4252](https://github.com/bytecodealliance/wasmtime/pull/4252)
### Fixed
* A refactor of `Config` was made to fix an issue that the order of calls to `Config`
matters now, which may lead to unexpected behavior.
[#4252](https://github.com/bytecodealliance/wasmtime/pull/4252)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
## 0.38.0 ## 0.38.0

4
crates/c-api/include/wasmtime/config.h

@ -201,7 +201,7 @@ WASMTIME_CONFIG_PROP(void, wasm_memory64, bool)
* If the compilation strategy selected could not be enabled then an error is * If the compilation strategy selected could not be enabled then an error is
* returned. * returned.
*/ */
WASMTIME_CONFIG_PROP(wasmtime_error_t*, strategy, wasmtime_strategy_t) WASMTIME_CONFIG_PROP(void, strategy, wasmtime_strategy_t)
/** /**
* \brief Configures whether Cranelift's debug verifier is enabled. * \brief Configures whether Cranelift's debug verifier is enabled.
@ -238,7 +238,7 @@ WASMTIME_CONFIG_PROP(void, cranelift_opt_level, wasmtime_opt_level_t)
* *
* This setting in #WASMTIME_PROFILING_STRATEGY_NONE by default. * This setting in #WASMTIME_PROFILING_STRATEGY_NONE by default.
*/ */
WASMTIME_CONFIG_PROP(wasmtime_error_t*, profiler, wasmtime_profiling_strategy_t) WASMTIME_CONFIG_PROP(void, profiler, wasmtime_profiling_strategy_t)
/** /**
* \brief Configures the maximum size for memory to be considered "static" * \brief Configures the maximum size for memory to be considered "static"

10
crates/c-api/src/config.rs

@ -103,13 +103,12 @@ pub extern "C" fn wasmtime_config_wasm_memory64_set(c: &mut wasm_config_t, enabl
pub extern "C" fn wasmtime_config_strategy_set( pub extern "C" fn wasmtime_config_strategy_set(
c: &mut wasm_config_t, c: &mut wasm_config_t,
strategy: wasmtime_strategy_t, strategy: wasmtime_strategy_t,
) -> Option<Box<wasmtime_error_t>> { ) {
use wasmtime_strategy_t::*; use wasmtime_strategy_t::*;
let result = c.config.strategy(match strategy { c.config.strategy(match strategy {
WASMTIME_STRATEGY_AUTO => Strategy::Auto, WASMTIME_STRATEGY_AUTO => Strategy::Auto,
WASMTIME_STRATEGY_CRANELIFT => Strategy::Cranelift, WASMTIME_STRATEGY_CRANELIFT => Strategy::Cranelift,
}); });
handle_result(result, |_cfg| {})
} }
#[no_mangle] #[no_mangle]
@ -145,13 +144,12 @@ pub extern "C" fn wasmtime_config_cranelift_opt_level_set(
pub extern "C" fn wasmtime_config_profiler_set( pub extern "C" fn wasmtime_config_profiler_set(
c: &mut wasm_config_t, c: &mut wasm_config_t,
strategy: wasmtime_profiling_strategy_t, strategy: wasmtime_profiling_strategy_t,
) -> Option<Box<wasmtime_error_t>> { ) {
use wasmtime_profiling_strategy_t::*; use wasmtime_profiling_strategy_t::*;
let result = c.config.profiler(match strategy { c.config.profiler(match strategy {
WASMTIME_PROFILING_STRATEGY_NONE => ProfilingStrategy::None, WASMTIME_PROFILING_STRATEGY_NONE => ProfilingStrategy::None,
WASMTIME_PROFILING_STRATEGY_JITDUMP => ProfilingStrategy::JitDump, WASMTIME_PROFILING_STRATEGY_JITDUMP => ProfilingStrategy::JitDump,
}); });
handle_result(result, |_cfg| {})
} }
#[no_mangle] #[no_mangle]

6
crates/cli-flags/src/lib.rs

@ -260,20 +260,20 @@ impl CommonOptions {
.cranelift_debug_verifier(self.enable_cranelift_debug_verifier) .cranelift_debug_verifier(self.enable_cranelift_debug_verifier)
.debug_info(self.debug_info) .debug_info(self.debug_info)
.cranelift_opt_level(self.opt_level()) .cranelift_opt_level(self.opt_level())
.profiler(pick_profiling_strategy(self.jitdump, self.vtune)?)? .profiler(pick_profiling_strategy(self.jitdump, self.vtune)?)
.cranelift_nan_canonicalization(self.enable_cranelift_nan_canonicalization); .cranelift_nan_canonicalization(self.enable_cranelift_nan_canonicalization);
self.enable_wasm_features(&mut config); self.enable_wasm_features(&mut config);
for name in &self.cranelift_enable { for name in &self.cranelift_enable {
unsafe { unsafe {
config.cranelift_flag_enable(name)?; config.cranelift_flag_enable(name);
} }
} }
for (name, value) in &self.cranelift_set { for (name, value) in &self.cranelift_set {
unsafe { unsafe {
config.cranelift_flag_set(name, value)?; config.cranelift_flag_set(name, value);
} }
} }

5
crates/cranelift/src/builder.rs

@ -9,7 +9,6 @@ use cranelift_codegen::settings::{self, Configurable, SetError};
use std::fmt; use std::fmt;
use wasmtime_environ::{CompilerBuilder, Setting, SettingKind}; use wasmtime_environ::{CompilerBuilder, Setting, SettingKind};
#[derive(Clone)]
struct Builder { struct Builder {
flags: settings::Builder, flags: settings::Builder,
isa_flags: isa::Builder, isa_flags: isa::Builder,
@ -55,10 +54,6 @@ impl CompilerBuilder for Builder {
self.isa_flags.triple() self.isa_flags.triple()
} }
fn clone(&self) -> Box<dyn CompilerBuilder> {
Box::new(Clone::clone(self))
}
fn target(&mut self, target: target_lexicon::Triple) -> Result<()> { fn target(&mut self, target: target_lexicon::Triple) -> Result<()> {
self.isa_flags = isa::lookup(target)?; self.isa_flags = isa::lookup(target)?;
Ok(()) Ok(())

3
crates/environ/src/compilation.rs

@ -76,9 +76,6 @@ pub enum CompileError {
/// This is used in Wasmtime to separate compiler implementations, currently /// This is used in Wasmtime to separate compiler implementations, currently
/// mostly used to separate Cranelift from Wasmtime itself. /// mostly used to separate Cranelift from Wasmtime itself.
pub trait CompilerBuilder: Send + Sync + fmt::Debug { pub trait CompilerBuilder: Send + Sync + fmt::Debug {
/// Like the `Clone` trait, but for the boxed trait object.
fn clone(&self) -> Box<dyn CompilerBuilder>;
/// Sets the target of compilation to the target specified. /// Sets the target of compilation to the target specified.
fn target(&mut self, target: target_lexicon::Triple) -> Result<()>; fn target(&mut self, target: target_lexicon::Triple) -> Result<()>;

8
crates/fuzzing/src/generators.rs

@ -421,8 +421,7 @@ impl Config {
if self.wasmtime.force_jump_veneers { if self.wasmtime.force_jump_veneers {
unsafe { unsafe {
cfg.cranelift_flag_set("wasmtime_linkopt_force_jump_veneer", "true") cfg.cranelift_flag_set("wasmtime_linkopt_force_jump_veneer", "true");
.unwrap();
} }
} }
@ -431,8 +430,7 @@ impl Config {
cfg.cranelift_flag_set( cfg.cranelift_flag_set(
"wasmtime_linkopt_padding_between_functions", "wasmtime_linkopt_padding_between_functions",
&pad.to_string(), &pad.to_string(),
) );
.unwrap();
} }
} }
@ -658,7 +656,7 @@ impl CodegenSettings {
config.target(target).unwrap(); config.target(target).unwrap();
for (key, value) in flags { for (key, value) in flags {
unsafe { unsafe {
config.cranelift_flag_set(key, value).unwrap(); config.cranelift_flag_set(key, value);
} }
} }
} }

328
crates/wasmtime/src/config.rs

@ -2,15 +2,18 @@ use crate::memory::MemoryCreator;
use crate::trampoline::MemoryCreatorProxy; use crate::trampoline::MemoryCreatorProxy;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::cmp;
use std::fmt; use std::fmt;
#[cfg(feature = "cache")] #[cfg(feature = "cache")]
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use std::{
cmp,
collections::{HashMap, HashSet},
};
use wasmparser::WasmFeatures; use wasmparser::WasmFeatures;
#[cfg(feature = "cache")] #[cfg(feature = "cache")]
use wasmtime_cache::CacheConfig; use wasmtime_cache::CacheConfig;
use wasmtime_environ::{CompilerBuilder, Tunables}; use wasmtime_environ::Tunables;
use wasmtime_jit::{JitDumpAgent, NullProfilerAgent, ProfilingAgent, VTuneAgent}; use wasmtime_jit::{JitDumpAgent, NullProfilerAgent, ProfilingAgent, VTuneAgent};
use wasmtime_runtime::{InstanceAllocator, OnDemandInstanceAllocator, RuntimeMemoryCreator}; use wasmtime_runtime::{InstanceAllocator, OnDemandInstanceAllocator, RuntimeMemoryCreator};
@ -79,14 +82,18 @@ impl Default for ModuleVersionStrategy {
/// and customize its behavior. /// and customize its behavior.
/// ///
/// This structure exposed a builder-like interface and is primarily consumed by /// 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 { pub struct Config {
#[cfg(compiler)] #[cfg(compiler)]
pub(crate) compiler: Box<dyn CompilerBuilder>, compiler_config: CompilerConfig,
pub(crate) tunables: Tunables, pub(crate) tunables: Tunables,
#[cfg(feature = "cache")] #[cfg(feature = "cache")]
pub(crate) cache_config: CacheConfig, 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) mem_creator: Option<Arc<dyn RuntimeMemoryCreator>>,
pub(crate) allocation_strategy: InstanceAllocationStrategy, pub(crate) allocation_strategy: InstanceAllocationStrategy,
pub(crate) max_wasm_stack: usize, pub(crate) max_wasm_stack: usize,
@ -103,6 +110,54 @@ pub struct Config {
pub(crate) force_memory_init_memfd: bool, 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 { impl Config {
/// Creates a new configuration object with the default configuration /// Creates a new configuration object with the default configuration
/// specified. /// specified.
@ -110,10 +165,10 @@ impl Config {
let mut ret = Self { let mut ret = Self {
tunables: Tunables::default(), tunables: Tunables::default(),
#[cfg(compiler)] #[cfg(compiler)]
compiler: compiler_builder(Strategy::Auto).unwrap(), compiler_config: CompilerConfig::default(),
#[cfg(feature = "cache")] #[cfg(feature = "cache")]
cache_config: CacheConfig::new_cache_disabled(), cache_config: CacheConfig::new_cache_disabled(),
profiler: Arc::new(NullProfilerAgent), profiling_strategy: ProfilingStrategy::None,
mem_creator: None, mem_creator: None,
allocation_strategy: InstanceAllocationStrategy::OnDemand, allocation_strategy: InstanceAllocationStrategy::OnDemand,
// 512k of stack -- note that this is chosen currently to not be too // 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 #[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
pub fn target(&mut self, target: &str) -> Result<&mut Self> { pub fn target(&mut self, target: &str) -> Result<&mut Self> {
use std::str::FromStr; use std::str::FromStr;
self.compiler self.compiler_config.target =
.target(target_lexicon::Triple::from_str(target).map_err(|e| anyhow::anyhow!(e))?)?; Some(target_lexicon::Triple::from_str(target).map_err(|e| anyhow::anyhow!(e))?);
Ok(self) Ok(self)
} }
@ -290,20 +345,6 @@ impl Config {
/// will always return `None`. /// will always return `None`.
pub fn wasm_backtrace(&mut self, enable: bool) -> &mut Self { pub fn wasm_backtrace(&mut self, enable: bool) -> &mut Self {
self.wasm_backtrace = enable; 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 self
} }
@ -507,8 +548,8 @@ impl Config {
/// be enabled through this method for appropriate wasm modules. /// be enabled through this method for appropriate wasm modules.
/// ///
/// This feature gates items such as shared memories and atomic /// This feature gates items such as shared memories and atomic
/// instructions. Note that enabling the threads feature will /// instructions. Note that the threads feature depends on the
/// also enable the bulk memory feature. /// bulk memory feature, which is enabled by default.
/// ///
/// This is `false` by default. /// This is `false` by default.
/// ///
@ -517,13 +558,14 @@ impl Config {
/// > expected. This should not be enabled in a production setting right /// > expected. This should not be enabled in a production setting right
/// > now. /// > 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 /// [threads]: https://github.com/webassembly/threads
pub fn wasm_threads(&mut self, enable: bool) -> &mut Self { pub fn wasm_threads(&mut self, enable: bool) -> &mut Self {
self.features.threads = enable; self.features.threads = enable;
// The threads proposal depends on the bulk memory proposal
if enable {
self.wasm_bulk_memory(true);
}
self self
} }
@ -533,38 +575,18 @@ impl Config {
/// This feature gates items such as the `externref` and `funcref` types as /// This feature gates items such as the `externref` and `funcref` types as
/// well as allowing a module to define multiple tables. /// well as allowing a module to define multiple tables.
/// ///
/// Note that enabling the reference types feature will also enable the bulk /// Note that the reference types proposal depends on the bulk memory proposal.
/// memory feature.
/// ///
/// This feature is `true` by default. /// 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 /// [proposal]: https://github.com/webassembly/reference-types
pub fn wasm_reference_types(&mut self, enable: bool) -> &mut Self { pub fn wasm_reference_types(&mut self, enable: bool) -> &mut Self {
self.features.reference_types = enable; 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 self
} }
@ -586,13 +608,6 @@ impl Config {
/// [relaxed simd proposal]: https://github.com/WebAssembly/relaxed-simd /// [relaxed simd proposal]: https://github.com/WebAssembly/relaxed-simd
pub fn wasm_simd(&mut self, enable: bool) -> &mut Self { pub fn wasm_simd(&mut self, enable: bool) -> &mut Self {
self.features.simd = enable; 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 self
} }
@ -604,6 +619,15 @@ impl Config {
/// ///
/// This is `true` by default. /// 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 /// [proposal]: https://github.com/webassembly/bulk-memory-operations
pub fn wasm_bulk_memory(&mut self, enable: bool) -> &mut Self { pub fn wasm_bulk_memory(&mut self, enable: bool) -> &mut Self {
self.features.bulk_memory = enable; self.features.bulk_memory = enable;
@ -674,30 +698,30 @@ impl Config {
/// and its documentation. /// and its documentation.
/// ///
/// The default value for this is `Strategy::Auto`. /// 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(compiler)]
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs #[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
pub fn strategy(&mut self, strategy: Strategy) -> Result<&mut Self> { pub fn strategy(&mut self, strategy: Strategy) -> &mut Self {
self.compiler = compiler_builder(strategy)?; self.compiler_config.strategy = strategy;
Ok(self) self
} }
/// Creates a default profiler based on the profiling strategy chosen. /// Creates a default profiler based on the profiling strategy chosen.
/// ///
/// Profiler creation calls the type's default initializer where the purpose is /// Profiler creation calls the type's default initializer where the purpose is
/// really just to put in place the type used for profiling. /// 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 { /// Some [`ProfilingStrategy`] require specific platforms or particular feature
ProfilingStrategy::JitDump => Arc::new(JitDumpAgent::new()?) as Arc<dyn ProfilingAgent>, /// to be enabled, such as `ProfilingStrategy::JitDump` requires the `jitdump`
ProfilingStrategy::VTune => Arc::new(VTuneAgent::new()?) as Arc<dyn ProfilingAgent>, /// feature.
ProfilingStrategy::None => Arc::new(NullProfilerAgent), ///
}; /// # Errors
Ok(self) ///
/// 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. /// 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 #[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
pub fn cranelift_debug_verifier(&mut self, enable: bool) -> &mut Self { pub fn cranelift_debug_verifier(&mut self, enable: bool) -> &mut Self {
let val = if enable { "true" } else { "false" }; let val = if enable { "true" } else { "false" };
self.compiler self.compiler_config
.set("enable_verifier", val) .settings
.expect("should be valid flag"); .insert("enable_verifier".to_string(), val.to_string());
self self
} }
@ -733,9 +757,9 @@ impl Config {
OptLevel::Speed => "speed", OptLevel::Speed => "speed",
OptLevel::SpeedAndSize => "speed_and_size", OptLevel::SpeedAndSize => "speed_and_size",
}; };
self.compiler self.compiler_config
.set("opt_level", val) .settings
.expect("should be valid flag"); .insert("opt_level".to_string(), val.to_string());
self self
} }
@ -751,9 +775,9 @@ impl Config {
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs #[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
pub fn cranelift_nan_canonicalization(&mut self, enable: bool) -> &mut Self { pub fn cranelift_nan_canonicalization(&mut self, enable: bool) -> &mut Self {
let val = if enable { "true" } else { "false" }; let val = if enable { "true" } else { "false" };
self.compiler self.compiler_config
.set("enable_nan_canonicalization", val) .settings
.expect("should be valid flag"); .insert("enable_nan_canonicalization".to_string(), val.to_string());
self self
} }
@ -770,12 +794,14 @@ impl Config {
/// ///
/// # Errors /// # 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(compiler)]
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs #[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
pub unsafe fn cranelift_flag_enable(&mut self, flag: &str) -> Result<&mut Self> { pub unsafe fn cranelift_flag_enable(&mut self, flag: &str) -> &mut Self {
self.compiler.enable(flag)?; self.compiler_config.flags.insert(flag.to_string());
Ok(self) self
} }
/// Allows settings another Cranelift flag defined by a flag name and value. This allows /// 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 /// Since Cranelift flags may be unstable, this method should not be considered to be stable
/// either; other `Config` functions should be preferred for stability. /// 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. /// resulting in execution hazards.
/// ///
/// # Errors /// # Errors
/// ///
/// This method can fail if the flag's name does not exist, or the value is not appropriate for /// The validation of the flags are deferred until the engine is being built, and thus may
/// the flag type. /// 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(compiler)]
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs #[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> { pub unsafe fn cranelift_flag_set(&mut self, name: &str, value: &str) -> &mut Self {
self.compiler.set(name, value)?; self.compiler_config
Ok(self) .settings
.insert(name.to_string(), value.to_string());
self
} }
/// Loads cache configuration specified at `path`. /// Loads cache configuration specified at `path`.
@ -1294,6 +1328,16 @@ impl Config {
self 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>> { pub(crate) fn build_allocator(&self) -> Result<Box<dyn InstanceAllocator>> {
#[cfg(feature = "async")] #[cfg(feature = "async")]
let stack_size = self.async_stack_size; let stack_size = self.async_stack_size;
@ -1318,12 +1362,59 @@ impl Config {
)?)), )?)),
} }
} }
}
#[cfg(compiler)] pub(crate) fn build_profiler(&self) -> Result<Box<dyn ProfilingAgent>> {
fn compiler_builder(strategy: Strategy) -> Result<Box<dyn CompilerBuilder>> { Ok(match self.profiling_strategy {
match strategy { ProfilingStrategy::JitDump => Box::new(JitDumpAgent::new()?) as Box<dyn ProfilingAgent>,
Strategy::Auto | Strategy::Cranelift => Ok(wasmtime_cranelift::builder()), 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()); debug_assert!(page_size.is_power_of_two());
val.checked_add(page_size - 1) val.checked_add(page_size - 1)
.map(|val| val & !(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 { 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 { impl fmt::Debug for Config {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut f = f.debug_struct("Config"); let mut f = f.debug_struct("Config");
@ -1398,7 +1462,7 @@ impl fmt::Debug for Config {
.field("parallel_compilation", &self.parallel_compilation); .field("parallel_compilation", &self.parallel_compilation);
#[cfg(compiler)] #[cfg(compiler)]
{ {
f.field("compiler", &self.compiler); f.field("compiler_config", &self.compiler_config);
} }
f.finish() f.finish()
} }
@ -1408,7 +1472,7 @@ impl fmt::Debug for Config {
/// ///
/// This is used as an argument to the [`Config::strategy`] method. /// This is used as an argument to the [`Config::strategy`] method.
#[non_exhaustive] #[non_exhaustive]
#[derive(Clone, Debug)] #[derive(Clone, Debug, Copy)]
pub enum Strategy { pub enum Strategy {
/// An indicator that the compilation strategy should be automatically /// An indicator that the compilation strategy should be automatically
/// selected. /// selected.

25
crates/wasmtime/src/engine.rs

@ -9,6 +9,7 @@ use std::sync::Arc;
#[cfg(feature = "cache")] #[cfg(feature = "cache")]
use wasmtime_cache::CacheConfig; use wasmtime_cache::CacheConfig;
use wasmtime_environ::FlagValue; use wasmtime_environ::FlagValue;
use wasmtime_jit::ProfilingAgent;
use wasmtime_runtime::{debug_builtins, CompiledModuleIdAllocator, InstanceAllocator}; use wasmtime_runtime::{debug_builtins, CompiledModuleIdAllocator, InstanceAllocator};
/// An `Engine` which is a global context for compilation and management of wasm /// An `Engine` which is a global context for compilation and management of wasm
@ -43,6 +44,7 @@ struct EngineInner {
#[cfg(compiler)] #[cfg(compiler)]
compiler: Box<dyn wasmtime_environ::Compiler>, compiler: Box<dyn wasmtime_environ::Compiler>,
allocator: Box<dyn InstanceAllocator>, allocator: Box<dyn InstanceAllocator>,
profiler: Box<dyn ProfilingAgent>,
signatures: SignatureRegistry, signatures: SignatureRegistry,
epoch: AtomicU64, epoch: AtomicU64,
unique_id_allocator: CompiledModuleIdAllocator, unique_id_allocator: CompiledModuleIdAllocator,
@ -55,6 +57,16 @@ struct EngineInner {
impl Engine { impl Engine {
/// Creates a new [`Engine`] with the specified compilation and /// Creates a new [`Engine`] with the specified compilation and
/// configuration settings. /// configuration settings.
///
/// # Errors
///
/// This method can fail if the `config` is invalid or some
/// configurations are incompatible.
///
/// For example, feature `reference_types` will need to set
/// the compiler setting `enable_safepoints` and `unwind_info`
/// to `true`, but explicitly disable these two compiler settings
/// will cause errors.
pub fn new(config: &Config) -> Result<Engine> { pub fn new(config: &Config) -> Result<Engine> {
// Ensure that wasmtime_runtime's signal handlers are configured. This // Ensure that wasmtime_runtime's signal handlers are configured. This
// is the per-program initialization required for handling traps, such // is the per-program initialization required for handling traps, such
@ -64,15 +76,22 @@ impl Engine {
let registry = SignatureRegistry::new(); let registry = SignatureRegistry::new();
let mut config = config.clone(); let mut config = config.clone();
config.validate()?;
#[cfg(compiler)]
let compiler = config.build_compiler()?;
let allocator = config.build_allocator()?; let allocator = config.build_allocator()?;
allocator.adjust_tunables(&mut config.tunables); allocator.adjust_tunables(&mut config.tunables);
let profiler = config.build_profiler()?;
Ok(Engine { Ok(Engine {
inner: Arc::new(EngineInner { inner: Arc::new(EngineInner {
#[cfg(compiler)] #[cfg(compiler)]
compiler: config.compiler.build()?, compiler,
config, config,
allocator, allocator,
profiler,
signatures: registry, signatures: registry,
epoch: AtomicU64::new(0), epoch: AtomicU64::new(0),
unique_id_allocator: CompiledModuleIdAllocator::new(), unique_id_allocator: CompiledModuleIdAllocator::new(),
@ -117,6 +136,10 @@ impl Engine {
self.inner.allocator.as_ref() self.inner.allocator.as_ref()
} }
pub(crate) fn profiler(&self) -> &dyn ProfilingAgent {
self.inner.profiler.as_ref()
}
#[cfg(feature = "cache")] #[cfg(feature = "cache")]
pub(crate) fn cache_config(&self) -> &CacheConfig { pub(crate) fn cache_config(&self) -> &CacheConfig {
&self.config().cache_config &self.config().cache_config

2
crates/wasmtime/src/module.rs

@ -493,7 +493,7 @@ impl Module {
let module = Arc::new(CompiledModule::from_artifacts( let module = Arc::new(CompiledModule::from_artifacts(
mmap, mmap,
info, info,
&*engine.config().profiler, engine.profiler(),
engine.unique_id_allocator(), engine.unique_id_allocator(),
)?); )?);

2
crates/wasmtime/src/trampoline/func.rs

@ -127,7 +127,7 @@ where
let mut code_memory = CodeMemory::new(obj); let mut code_memory = CodeMemory::new(obj);
let code = code_memory.publish()?; let code = code_memory.publish()?;
register_trampolines(engine.config().profiler.as_ref(), &code.obj); register_trampolines(engine.profiler(), &code.obj);
// Extract the host/wasm trampolines from the results of compilation since // Extract the host/wasm trampolines from the results of compilation since
// we know their start/length. // we know their start/length.

2
fuzz/fuzz_targets/compile.rs

@ -12,7 +12,7 @@ fn create_engine() -> Engine {
// the generated code at all; it only does extra checking after // the generated code at all; it only does extra checking after
// compilation. // compilation.
unsafe { unsafe {
config.cranelift_flag_enable("regalloc_checker").unwrap(); config.cranelift_flag_enable("regalloc_checker");
} }
Engine::new(&config).expect("Could not construct Engine") Engine::new(&config).expect("Could not construct Engine")
} }

4
tests/all/relocs.rs

@ -21,7 +21,7 @@ fn store_with_padding(padding: usize) -> Result<Store<()>> {
config.cranelift_flag_set( config.cranelift_flag_set(
"wasmtime_linkopt_padding_between_functions", "wasmtime_linkopt_padding_between_functions",
&padding.to_string(), &padding.to_string(),
)?; );
} }
let engine = Engine::new(&config)?; let engine = Engine::new(&config)?;
Ok(Store::new(&engine, ())) Ok(Store::new(&engine, ()))
@ -78,7 +78,7 @@ fn mixed() -> Result<()> {
fn mixed_forced() -> Result<()> { fn mixed_forced() -> Result<()> {
let mut config = Config::new(); let mut config = Config::new();
unsafe { unsafe {
config.cranelift_flag_set("wasmtime_linkopt_force_jump_veneer", "true")?; config.cranelift_flag_set("wasmtime_linkopt_force_jump_veneer", "true");
} }
let engine = Engine::new(&config)?; let engine = Engine::new(&config)?;
test_many_call_module(Store::new(&engine, ())) test_many_call_module(Store::new(&engine, ()))

Loading…
Cancel
Save