diff --git a/Cargo.lock b/Cargo.lock index 5fe0859d37dd..0db9da3cffbc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3394,7 +3394,6 @@ dependencies = [ "criterion", "env_logger 0.10.0", "filecheck", - "humantime 2.1.0", "libc", "listenfd", "log", @@ -3438,6 +3437,7 @@ dependencies = [ "anyhow", "clap", "file-per-thread-logger", + "humantime 2.1.0", "pretty_env_logger 0.5.0", "rayon", "wasmtime", diff --git a/Cargo.toml b/Cargo.toml index a02f2ac4f96c..c5f924b3b2c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,6 @@ wasmtime-runtime = { workspace = true } clap = { workspace = true, features = ["color", "suggestions", "derive"] } anyhow = { workspace = true } target-lexicon = { workspace = true } -humantime = "2.0.0" once_cell = { workspace = true } listenfd = "1.0.0" wat = { workspace = true } diff --git a/ci/run-wasi-nn-example.sh b/ci/run-wasi-nn-example.sh index 09947da16d65..74bab3bd30fc 100755 --- a/ci/run-wasi-nn-example.sh +++ b/ci/run-wasi-nn-example.sh @@ -39,8 +39,7 @@ pushd $WASMTIME_DIR/crates/wasi-nn/examples/classification-example cargo build --release --target=wasm32-wasi cp target/wasm32-wasi/release/wasi-nn-example.wasm $TMP_DIR popd -cargo run -- run --mapdir fixture::$TMP_DIR \ - --wasi-modules=experimental-wasi-nn $TMP_DIR/wasi-nn-example.wasm +cargo run -- run --dir fixture::$TMP_DIR -S nn $TMP_DIR/wasi-nn-example.wasm # Build and run another example, this time using Wasmtime's graph flag to # preload the model. @@ -48,8 +47,8 @@ pushd $WASMTIME_DIR/crates/wasi-nn/examples/classification-example-named cargo build --release --target=wasm32-wasi cp target/wasm32-wasi/release/wasi-nn-example-named.wasm $TMP_DIR popd -cargo run -- run --mapdir fixture::$TMP_DIR --wasi-nn-graph openvino::$TMP_DIR \ - --wasi-modules=experimental-wasi-nn $TMP_DIR/wasi-nn-example-named.wasm +cargo run -- run --dir fixture::$TMP_DIR -S nn,nn-graph=openvino::$TMP_DIR \ + $TMP_DIR/wasi-nn-example-named.wasm # Clean up the temporary directory only if it was not specified (users may want # to keep the directory around). diff --git a/crates/bench-api/src/lib.rs b/crates/bench-api/src/lib.rs index 3a29eb94dc23..a2ec33a9f5fd 100644 --- a/crates/bench-api/src/lib.rs +++ b/crates/bench-api/src/lib.rs @@ -143,7 +143,7 @@ use std::slice; use std::{env, path::PathBuf}; use target_lexicon::Triple; use wasmtime::{Engine, Instance, Linker, Module, Store}; -use wasmtime_cli_flags::{CommonOptions, WasiModules}; +use wasmtime_cli_flags::CommonOptions; use wasmtime_wasi::{sync::WasiCtxBuilder, I32Exit, WasiCtx}; pub type ExitCode = c_int; @@ -423,7 +423,7 @@ struct HostState { impl BenchState { fn new( - options: CommonOptions, + mut options: CommonOptions, compilation_timer: *mut u8, compilation_start: extern "C" fn(*mut u8), compilation_end: extern "C" fn(*mut u8), @@ -456,17 +456,15 @@ impl BenchState { Ok(()) })?; - let epoch_interruption = options.epoch_interruption; - let fuel = options.fuel; + let epoch_interruption = options.wasm.epoch_interruption.unwrap_or(false); + let fuel = options.wasm.fuel; - let wasi_modules = options.wasi_modules.unwrap_or(WasiModules::default()); - - if wasi_modules.wasi_common { + if options.wasi.common != Some(false) { wasmtime_wasi::add_to_linker(&mut linker, |cx| &mut cx.wasi)?; } #[cfg(feature = "wasi-nn")] - if wasi_modules.wasi_nn { + if options.wasi.nn == Some(true) { wasmtime_wasi_nn::witx::add_to_linker(&mut linker, |cx| &mut cx.wasi_nn)?; } diff --git a/crates/cli-flags/Cargo.toml b/crates/cli-flags/Cargo.toml index a2254f0a7e8d..e15b4d14f149 100644 --- a/crates/cli-flags/Cargo.toml +++ b/crates/cli-flags/Cargo.toml @@ -15,6 +15,7 @@ file-per-thread-logger = { workspace = true } pretty_env_logger = { workspace = true } rayon = "1.5.0" wasmtime = { workspace = true } +humantime = "2.0.0" [features] default = [ diff --git a/crates/cli-flags/src/lib.rs b/crates/cli-flags/src/lib.rs index dadb400b53df..9df8c1147ab8 100644 --- a/crates/cli-flags/src/lib.rs +++ b/crates/cli-flags/src/lib.rs @@ -3,62 +3,12 @@ #![deny(trivial_numeric_casts, unused_extern_crates, unstable_features)] #![warn(unused_import_braces)] -use anyhow::{bail, Result}; +use anyhow::Result; use clap::Parser; -use std::collections::HashMap; -use std::path::PathBuf; -use wasmtime::{Config, Strategy}; - -pub const SUPPORTED_WASM_FEATURES: &[(&str, &str)] = &[ - ("all", "enables all supported WebAssembly features"), - ( - "bulk-memory", - "enables support for bulk memory instructions", - ), - ( - "multi-memory", - "enables support for the multi-memory proposal", - ), - ("multi-value", "enables support for multi-value functions"), - ("reference-types", "enables support for reference types"), - ("simd", "enables support for proposed SIMD instructions"), - ( - "relaxed-simd", - "enables support for the relaxed simd proposal", - ), - ("tail-call", "enables support for WebAssembly tail calls"), - ("threads", "enables support for WebAssembly threads"), - ("memory64", "enables support for 64-bit memories"), - #[cfg(feature = "component-model")] - ("component-model", "enables support for the component model"), - ( - "function-references", - "enables support for typed function references", - ), -]; - -pub const SUPPORTED_WASI_MODULES: &[(&str, &str)] = &[ - ( - "default", - "enables all stable WASI modules (no experimental modules)", - ), - ( - "wasi-common", - "enables support for the WASI common APIs, see https://github.com/WebAssembly/WASI", - ), - ( - "experimental-wasi-nn", - "enables support for the WASI neural network API (experimental), see https://github.com/WebAssembly/wasi-nn", - ), - ( - "experimental-wasi-threads", - "enables support for the WASI threading API (experimental), see https://github.com/WebAssembly/wasi-threads", - ), - ( - "experimental-wasi-http", - "enables support for the WASI HTTP APIs (experimental), see https://github.com/WebAssembly/wasi-http", - ), -]; +use std::time::Duration; +use wasmtime::Config; + +pub mod opt; fn init_file_per_thread_logger(prefix: &'static str) { file_per_thread_logger::initialize(prefix); @@ -86,168 +36,293 @@ fn init_file_per_thread_logger(prefix: &'static str) { .unwrap(); } +wasmtime_option_group! { + pub struct OptimizeOptions { + /// Optimization level of generated code (0-2, s; default: 0) + pub opt_level: Option, + + /// Byte size of the guard region after dynamic memories are allocated + pub dynamic_memory_guard_size: Option, + + /// Force using a "static" style for all wasm memories + pub static_memory_forced: Option, + + /// Maximum size in bytes of wasm memory before it becomes dynamically + /// relocatable instead of up-front-reserved. + pub static_memory_maximum_size: Option, + + /// Byte size of the guard region after static memories are allocated + pub static_memory_guard_size: Option, + + /// Bytes to reserve at the end of linear memory for growth for dynamic + /// memories. + pub dynamic_memory_reserved_for_growth: Option, + + /// Enable the pooling allocator, in place of the on-demand allocator. + pub pooling_allocator: Option, + + /// Configure attempting to initialize linear memory via a + /// copy-on-write mapping (default: yes) + pub memory_init_cow: Option, + } + + enum Optimize { + ... + } +} + +wasmtime_option_group! { + pub struct CodegenOptions { + /// Either `cranelift` or `winch`. + /// + /// Currently only `cranelift` and `winch` are supported, but not all + /// builds of Wasmtime have both built in. + pub compiler: Option, + /// Enable Cranelift's internal debug verifier (expensive) + pub cranelift_debug_verifier: Option, + /// Whether or not to enable caching of compiled modules. + pub cache: Option, + /// Configuration for compiled module caching. + pub cache_config: Option, + /// Whether or not to enable parallel compilation of modules. + pub parallel_compilation: Option, + + #[prefixed = "cranelift"] + /// Set a cranelift-specific option. Use `wasmtime settings` to see + /// all. + pub cranelift: Vec<(String, Option)>, + } + + enum Codegen { + ... + } +} + +wasmtime_option_group! { + pub struct DebugOptions { + /// Enable generation of DWARF debug information in compiled code. + pub debug_info: Option, + /// Configure whether compiled code can map native addresses to wasm. + pub address_map: Option, + /// Configure whether logging is enabled. + pub logging: Option, + /// Configure whether logs are emitted to files + pub log_to_files: Option, + /// Enable coredump generation to this file after a WebAssembly trap. + pub coredump: Option, + } + + enum Debug { + ... + } +} + +wasmtime_option_group! { + pub struct WasmOptions { + /// Enable canonicalization of all NaN values. + pub nan_canonicalization: Option, + /// Enable execution fuel with N units fuel, trapping after running out + /// of fuel. + /// + /// Most WebAssembly instructions consume 1 unit of fuel. Some + /// instructions, such as `nop`, `drop`, `block`, and `loop`, consume 0 + /// units, as any execution cost associated with them involves other + /// instructions which do consume fuel. + pub fuel: Option, + /// Yield when a global epoch counter changes, allowing for async + /// operation without blocking the executor. + pub epoch_interruption: Option, + /// Maximum stack size, in bytes, that wasm is allowed to consume before a + /// stack overflow is reported. + pub max_wasm_stack: Option, + /// Allow unknown exports when running commands. + pub unknown_exports_allow: Option, + /// Allow the main module to import unknown functions, using an + /// implementation that immediately traps, when running commands. + pub unknown_imports_trap: Option, + /// Allow the main module to import unknown functions, using an + /// implementation that returns default values, when running commands. + pub unknown_imports_default: Option, + /// Enables memory error checking. (see wmemcheck.md for more info) + pub wmemcheck: Option, + /// Maximum size, in bytes, that a linear memory is allowed to reach. + /// + /// Growth beyond this limit will cause `memory.grow` instructions in + /// WebAssembly modules to return -1 and fail. + pub max_memory_size: Option, + /// Maximum size, in table elements, that a table is allowed to reach. + pub max_table_elements: Option, + /// Maximum number of WebAssembly instances allowed to be created. + pub max_instances: Option, + /// Maximum number of WebAssembly tables allowed to be created. + pub max_tables: Option, + /// Maximum number of WebAssembly linear memories allowed to be created. + pub max_memories: Option, + /// Force a trap to be raised on `memory.grow` and `table.grow` failure + /// instead of returning -1 from these instructions. + /// + /// This is not necessarily a spec-compliant option to enable but can be + /// useful for tracking down a backtrace of what is requesting so much + /// memory, for example. + pub trap_on_grow_failure: Option, + /// Maximum execution time of wasm code before timing out (1, 2s, 100ms, etc) + pub timeout: Option, + /// Configures support for all WebAssembly proposals implemented. + pub all_proposals: Option, + /// Configure support for the bulk memory proposal. + pub bulk_memory: Option, + /// Configure support for the multi-memory proposal. + pub multi_memory: Option, + /// Configure support for the multi-value proposal. + pub multi_value: Option, + /// Configure support for the reference-types proposal. + pub reference_types: Option, + /// Configure support for the simd proposal. + pub simd: Option, + /// Configure support for the relaxed-simd proposal. + pub relaxed_simd: Option, + /// Configure forcing deterministic and host-independent behavior of + /// the relaxed-simd instructions. + /// + /// By default these instructions may have architecture-specific behavior as + /// allowed by the specification, but this can be used to force the behavior + /// of these instructions to match the deterministic behavior classified in + /// the specification. Note that enabling this option may come at a + /// performance cost. + pub relaxed_simd_deterministic: Option, + /// Configure support for the tail-call proposal. + pub tail_call: Option, + /// Configure support for the threads proposal. + pub threads: Option, + /// Configure support for the memory64 proposal. + pub memory64: Option, + /// Configure support for the component-model proposal. + pub component_model: Option, + /// Configure support for the function-references proposal. + pub function_references: Option, + } + + enum Wasm { + ... + } +} + +wasmtime_option_group! { + pub struct WasiOptions { + /// Enable support for WASI common APIs + pub common: Option, + /// Enable suport for WASI neural network API (experimental) + pub nn: Option, + /// Enable suport for WASI threading API (experimental) + pub threads: Option, + /// Enable suport for WASI HTTP API (experimental) + pub http: Option, + /// Inherit environment variables and file descriptors following the + /// systemd listen fd specification (UNIX only) + pub listenfd: Option, + /// Grant access to the given TCP listen socket + pub tcplisten: Vec, + /// Implement WASI with preview2 primitives (experimental). + /// + /// Indicates that the implementation of WASI preview1 should be backed by + /// the preview2 implementation for components. + /// + /// This will become the default in the future and this option will be + /// removed. For now this is primarily here for testing. + pub preview2: Option, + /// Pre-load machine learning graphs (i.e., models) for use by wasi-nn. + /// + /// Each use of the flag will preload a ML model from the host directory + /// using the given model encoding. The model will be mapped to the + /// directory name: e.g., `--wasi-nn-graph openvino:/foo/bar` will preload + /// an OpenVINO model named `bar`. Note that which model encodings are + /// available is dependent on the backends implemented in the + /// `wasmtime_wasi_nn` crate. + pub nn_graph: Vec, + /// Flag for WASI preview2 to inherit the host's network within the + /// guest so it has full access to all addresses/ports/etc. + pub inherit_network: Option, + + } + + enum Wasi { + ... + } +} + +#[derive(Debug, Clone)] +pub struct WasiNnGraph { + pub format: String, + pub dir: String, +} + /// Common options for commands that translate WebAssembly modules #[derive(Parser)] -#[cfg_attr(test, derive(Debug, PartialEq))] pub struct CommonOptions { - /// Use specified configuration file - #[clap(long, value_name = "CONFIG_PATH")] - pub config: Option, - - /// Disable logging - #[clap(long, conflicts_with = "log_to_files")] - pub disable_logging: bool, - - /// Log to per-thread log files instead of stderr - #[clap(long)] - pub log_to_files: bool, - - /// Generate debug information - #[clap(short = 'g')] - pub debug_info: bool, - - /// Disable cache system - #[clap(long)] - pub disable_cache: bool, - - /// Disable parallel compilation - #[clap(long)] - pub disable_parallel_compilation: bool, - - /// Enable or disable WebAssembly features - #[clap(long, value_name = "FEATURE,FEATURE,...", value_parser = parse_wasm_features)] - pub wasm_features: Option, - - /// Enable or disable WASI modules - #[clap(long, value_name = "MODULE,MODULE,...", value_parser = parse_wasi_modules)] - pub wasi_modules: Option, - - /// Generate jitdump file (supported on --features=profiling build) - /// Run optimization passes on translated functions, on by default - #[clap(short = 'O', long)] - pub optimize: bool, - - /// Optimization level for generated functions - /// Supported levels: 0 (none), 1, 2 (most), or s (size); default is "most" - #[clap( - long, - value_name = "LEVEL", - value_parser = parse_opt_level, - verbatim_doc_comment, - )] - pub opt_level: Option, - - /// Set a Cranelift setting to a given value. - /// Use `wasmtime settings` to list Cranelift settings for a target. - #[clap( - long = "cranelift-set", - value_name = "NAME=VALUE", - number_of_values = 1, - verbatim_doc_comment, - value_parser = parse_cranelift_flag, - )] - pub cranelift_set: Vec<(String, String)>, - - /// Enable a Cranelift boolean setting or preset. - /// Use `wasmtime settings` to list Cranelift settings for a target. - #[clap( - long, - value_name = "SETTING", - number_of_values = 1, - verbatim_doc_comment - )] - pub cranelift_enable: Vec, - - /// Maximum size in bytes of wasm memory before it becomes dynamically - /// relocatable instead of up-front-reserved. - #[clap(long, value_name = "MAXIMUM")] - pub static_memory_maximum_size: Option, - - /// Force using a "static" style for all wasm memories - #[clap(long)] - pub static_memory_forced: bool, - - /// Byte size of the guard region after static memories are allocated - #[clap(long, value_name = "SIZE")] - pub static_memory_guard_size: Option, - - /// Byte size of the guard region after dynamic memories are allocated - #[clap(long, value_name = "SIZE")] - pub dynamic_memory_guard_size: Option, - - /// Bytes to reserve at the end of linear memory for growth for dynamic - /// memories. - #[clap(long, value_name = "SIZE")] - pub dynamic_memory_reserved_for_growth: Option, - - /// Enable Cranelift's internal debug verifier (expensive) - #[clap(long)] - pub enable_cranelift_debug_verifier: bool, - - /// Enable Cranelift's internal NaN canonicalization - #[clap(long)] - pub enable_cranelift_nan_canonicalization: bool, - - /// Enable execution fuel with N units fuel, where execution will trap after - /// running out of fuel. - /// - /// Most WebAssembly instructions consume 1 unit of fuel. Some instructions, - /// such as `nop`, `drop`, `block`, and `loop`, consume 0 units, as any - /// execution cost associated with them involves other instructions which do - /// consume fuel. - #[clap(long, value_name = "N")] - pub fuel: Option, - - /// Executing wasm code will yield when a global epoch counter - /// changes, allowing for async operation without blocking the - /// executor. - #[clap(long)] - pub epoch_interruption: bool, - - /// Disable the on-by-default address map from native code to wasm code - #[clap(long)] - pub disable_address_map: bool, - - /// Disable the default of attempting to initialize linear memory via a - /// copy-on-write mapping - #[clap(long)] - pub disable_memory_init_cow: bool, - - /// Enable the pooling allocator, in place of the on-demand - /// allocator. - #[cfg(feature = "pooling-allocator")] - #[clap(long)] - pub pooling_allocator: bool, - - /// Maximum stack size, in bytes, that wasm is allowed to consume before a - /// stack overflow is reported. - #[clap(long)] - pub max_wasm_stack: Option, - - /// Whether or not to force deterministic and host-independent behavior of - /// the relaxed-simd instructions. - /// - /// By default these instructions may have architecture-specific behavior as - /// allowed by the specification, but this can be used to force the behavior - /// of these instructions to match the deterministic behavior classified in - /// the specification. Note that enabling this option may come at a - /// performance cost. - #[clap(long)] - pub relaxed_simd_deterministic: bool, - /// Explicitly specify the name of the compiler to use for WebAssembly. - /// - /// Currently only `cranelift` and `winch` are supported, but not all builds - /// of Wasmtime have both built in. - #[clap(long)] - pub compiler: Option, + // These options groups are used to parse `-O` and such options but aren't + // the raw form consumed by the CLI. Instead they're pushed into the `pub` + // fields below as part of the `configure` method. + // + // Ideally clap would support `pub opts: OptimizeOptions` and parse directly + // into that but it does not appear to do so for multiple `-O` flags for + // now. + /// Optimization and tuning related options for wasm performance, `-O help` to + /// see all. + #[clap(short = 'O', long = "optimize", value_name = "KEY[=VAL[,..]]")] + opts_raw: Vec>, + + /// Codegen-related configuration options, `-C help` to see all. + #[clap(short = 'C', long = "codegen", value_name = "KEY[=VAL[,..]]")] + codegen_raw: Vec>, + + /// Debug-related configuration options, `-D help` to see all. + #[clap(short = 'D', long = "debug", value_name = "KEY[=VAL[,..]]")] + debug_raw: Vec>, + + /// Options for configuring semantic execution of WebAssembly, `-W help` to see + /// all. + #[clap(short = 'W', long = "wasm", value_name = "KEY[=VAL[,..]]")] + wasm_raw: Vec>, + + /// Options for configuring WASI and its proposals, `-S help` to see all. + #[clap(short = 'S', long = "wasi", value_name = "KEY[=VAL[,..]]")] + wasi_raw: Vec>, + + // These fields are filled in by the `configure` method below via the + // options parsed from the CLI above. This is what the CLI should use. + #[clap(skip)] + configured: bool, + #[clap(skip)] + pub opts: OptimizeOptions, + #[clap(skip)] + pub codegen: CodegenOptions, + #[clap(skip)] + pub debug: DebugOptions, + #[clap(skip)] + pub wasm: WasmOptions, + #[clap(skip)] + pub wasi: WasiOptions, } impl CommonOptions { - pub fn init_logging(&self) { - if self.disable_logging { + fn configure(&mut self) { + if self.configured { return; } - if self.log_to_files { + self.configured = true; + self.opts.configure_with(&self.opts_raw); + self.codegen.configure_with(&self.codegen_raw); + self.debug.configure_with(&self.debug_raw); + self.wasm.configure_with(&self.wasm_raw); + self.wasi.configure_with(&self.wasi_raw); + } + + pub fn init_logging(&mut self) { + self.configure(); + if self.debug.logging == Some(false) { + return; + } + if self.debug.log_to_files == Some(true) { let prefix = "wasmtime.dbg."; init_file_per_thread_logger(prefix); } else { @@ -255,15 +330,13 @@ impl CommonOptions { } } - pub fn config(&self, target: Option<&str>) -> Result { + pub fn config(&mut self, target: Option<&str>) -> Result { + self.configure(); let mut config = Config::new(); - config.strategy(match self.compiler.as_deref() { - None => Strategy::Auto, - Some("cranelift") => Strategy::Cranelift, - Some("winch") => Strategy::Winch, - Some(s) => bail!("unknown compiler: {s}"), - }); + if let Some(strategy) = self.codegen.compiler { + config.strategy(strategy); + } // Set the target before setting any cranelift options, since the // target will reset any target-specific options. @@ -271,28 +344,37 @@ impl CommonOptions { config.target(target)?; } - config - .cranelift_debug_verifier(self.enable_cranelift_debug_verifier) - .debug_info(self.debug_info) - .cranelift_opt_level(self.opt_level()) - .cranelift_nan_canonicalization(self.enable_cranelift_nan_canonicalization); - - self.enable_wasm_features(&mut config); - - for name in &self.cranelift_enable { - unsafe { - config.cranelift_flag_enable(name); - } + if let Some(enable) = self.codegen.cranelift_debug_verifier { + config.cranelift_debug_verifier(enable); + } + if let Some(enable) = self.debug.debug_info { + config.debug_info(enable); + } + if let Some(level) = self.opts.opt_level { + config.cranelift_opt_level(level); } + if let Some(enable) = self.wasm.nan_canonicalization { + config.cranelift_nan_canonicalization(enable); + } + + self.enable_wasm_features(&mut config)?; - for (name, value) in &self.cranelift_set { + for (name, value) in self.codegen.cranelift.iter() { + let name = name.replace('-', "_"); unsafe { - config.cranelift_flag_set(name, value); + match value { + Some(val) => { + config.cranelift_flag_set(&name, val); + } + None => { + config.cranelift_flag_enable(&name); + } + } } } - if !self.disable_cache { - match &self.config { + if self.codegen.cache != Some(false) { + match &self.codegen.cache_config { Some(path) => { config.cache_config_load(path)?; } @@ -302,494 +384,106 @@ impl CommonOptions { } } - if self.disable_parallel_compilation { - config.parallel_compilation(false); + if let Some(enable) = self.codegen.parallel_compilation { + config.parallel_compilation(enable); } - if let Some(max) = self.static_memory_maximum_size { + if let Some(max) = self.opts.static_memory_maximum_size { config.static_memory_maximum_size(max); } - config.static_memory_forced(self.static_memory_forced); + if let Some(enable) = self.opts.static_memory_forced { + config.static_memory_forced(enable); + } - if let Some(size) = self.static_memory_guard_size { + if let Some(size) = self.opts.static_memory_guard_size { config.static_memory_guard_size(size); } - if let Some(size) = self.dynamic_memory_guard_size { + if let Some(size) = self.opts.dynamic_memory_guard_size { config.dynamic_memory_guard_size(size); } - if let Some(size) = self.dynamic_memory_reserved_for_growth { + if let Some(size) = self.opts.dynamic_memory_reserved_for_growth { config.dynamic_memory_reserved_for_growth(size); } // If fuel has been configured, set the `consume fuel` flag on the config. - if self.fuel.is_some() { + if self.wasm.fuel.is_some() { config.consume_fuel(true); } - config.epoch_interruption(self.epoch_interruption); - config.generate_address_map(!self.disable_address_map); - config.memory_init_cow(!self.disable_memory_init_cow); + if let Some(enable) = self.wasm.epoch_interruption { + config.epoch_interruption(enable); + } + if let Some(enable) = self.debug.address_map { + config.generate_address_map(enable); + } + if let Some(enable) = self.opts.memory_init_cow { + config.memory_init_cow(enable); + } - #[cfg(feature = "pooling-allocator")] - { - if self.pooling_allocator { - config.allocation_strategy(wasmtime::InstanceAllocationStrategy::pooling()); - } + if self.opts.pooling_allocator == Some(true) { + #[cfg(feature = "pooling-allocator")] + config.allocation_strategy(wasmtime::InstanceAllocationStrategy::pooling()); + #[cfg(not(feature = "pooling-allocator"))] + anyhow::bail!("support for the pooling allocator was disabled at compile-time"); } - if let Some(max) = self.max_wasm_stack { + if let Some(max) = self.wasm.max_wasm_stack { config.max_wasm_stack(max); } - config.relaxed_simd_deterministic(self.relaxed_simd_deterministic); + if let Some(enable) = self.wasm.relaxed_simd_deterministic { + config.relaxed_simd_deterministic(enable); + } + if let Some(enable) = self.wasm.wmemcheck { + config.wmemcheck(enable); + } Ok(config) } - pub fn enable_wasm_features(&self, config: &mut Config) { - let WasmFeatures { - simd, - relaxed_simd, - bulk_memory, - reference_types, - multi_value, - tail_call, - threads, - multi_memory, - memory64, - #[cfg(feature = "component-model")] - component_model, - function_references, - } = self.wasm_features.unwrap_or_default(); + pub fn enable_wasm_features(&self, config: &mut Config) -> Result<()> { + let all = self.wasm.all_proposals; - if let Some(enable) = simd { + if let Some(enable) = self.wasm.simd.or(all) { config.wasm_simd(enable); } - if let Some(enable) = relaxed_simd { + if let Some(enable) = self.wasm.relaxed_simd.or(all) { config.wasm_relaxed_simd(enable); } - if let Some(enable) = bulk_memory { + if let Some(enable) = self.wasm.bulk_memory.or(all) { config.wasm_bulk_memory(enable); } - if let Some(enable) = reference_types { + if let Some(enable) = self.wasm.reference_types.or(all) { config.wasm_reference_types(enable); } - if let Some(enable) = function_references { + if let Some(enable) = self.wasm.function_references.or(all) { config.wasm_function_references(enable); } - if let Some(enable) = multi_value { + if let Some(enable) = self.wasm.multi_value.or(all) { config.wasm_multi_value(enable); } - if let Some(enable) = tail_call { + if let Some(enable) = self.wasm.tail_call.or(all) { config.wasm_tail_call(enable); } - if let Some(enable) = threads { + if let Some(enable) = self.wasm.threads.or(all) { config.wasm_threads(enable); } - if let Some(enable) = multi_memory { + if let Some(enable) = self.wasm.multi_memory.or(all) { config.wasm_multi_memory(enable); } - if let Some(enable) = memory64 { + if let Some(enable) = self.wasm.memory64.or(all) { config.wasm_memory64(enable); } - #[cfg(feature = "component-model")] - if let Some(enable) = component_model { + if let Some(enable) = self.wasm.component_model.or(all) { + #[cfg(feature = "component-model")] config.wasm_component_model(enable); - } - } - - pub fn opt_level(&self) -> wasmtime::OptLevel { - match (self.optimize, self.opt_level.clone()) { - (true, _) => wasmtime::OptLevel::Speed, - (false, other) => other.unwrap_or(wasmtime::OptLevel::Speed), - } - } -} - -fn parse_opt_level(opt_level: &str) -> Result { - match opt_level { - "s" => Ok(wasmtime::OptLevel::SpeedAndSize), - "0" => Ok(wasmtime::OptLevel::None), - "1" => Ok(wasmtime::OptLevel::Speed), - "2" => Ok(wasmtime::OptLevel::Speed), - other => bail!( - "unknown optimization level `{}`, only 0,1,2,s accepted", - other - ), - } -} - -#[derive(Default, Clone, Copy)] -#[cfg_attr(test, derive(Debug, PartialEq))] -pub struct WasmFeatures { - pub reference_types: Option, - pub multi_value: Option, - pub bulk_memory: Option, - pub simd: Option, - pub relaxed_simd: Option, - pub tail_call: Option, - pub threads: Option, - pub multi_memory: Option, - pub memory64: Option, - #[cfg(feature = "component-model")] - pub component_model: Option, - pub function_references: Option, -} - -fn parse_wasm_features(features: &str) -> Result { - let features = features.trim(); - - let mut all = None; - let mut values: HashMap<_, _> = SUPPORTED_WASM_FEATURES - .iter() - .map(|(name, _)| (name.to_string(), None)) - .collect(); - - if features == "all" { - all = Some(true); - } else if features == "-all" { - all = Some(false); - } else { - for feature in features.split(',') { - let feature = feature.trim(); - - if feature.is_empty() { - continue; + #[cfg(not(feature = "component-model"))] + if enable && all.is_none() { + anyhow::bail!("support for the component model was disabled at compile-time"); } - - let (feature, value) = if feature.starts_with('-') { - (&feature[1..], false) - } else { - (feature, true) - }; - - if feature == "all" { - bail!("'all' cannot be specified with other WebAssembly features"); - } - - match values.get_mut(feature) { - Some(v) => *v = Some(value), - None => bail!("unsupported WebAssembly feature '{}'", feature), - } - } - } - - Ok(WasmFeatures { - reference_types: all.or(values["reference-types"]), - multi_value: all.or(values["multi-value"]), - bulk_memory: all.or(values["bulk-memory"]), - simd: all.or(values["simd"]), - relaxed_simd: all.or(values["relaxed-simd"]), - tail_call: all.or(values["tail-call"]), - threads: all.or(values["threads"]), - multi_memory: all.or(values["multi-memory"]), - memory64: all.or(values["memory64"]), - #[cfg(feature = "component-model")] - component_model: all.or(values["component-model"]), - function_references: all.or(values["function-references"]), - }) -} - -fn parse_wasi_modules(modules: &str) -> Result { - let modules = modules.trim(); - match modules { - "default" => Ok(WasiModules::default()), - "-default" => Ok(WasiModules::none()), - _ => { - // Starting from the default set of WASI modules, enable or disable a list of - // comma-separated modules. - let mut wasi_modules = WasiModules::default(); - let mut set = |module: &str, enable: bool| match module { - "" => Ok(()), - "wasi-common" => Ok(wasi_modules.wasi_common = enable), - "experimental-wasi-nn" => Ok(wasi_modules.wasi_nn = enable), - "experimental-wasi-threads" => Ok(wasi_modules.wasi_threads = enable), - "experimental-wasi-http" => Ok(wasi_modules.wasi_http = enable), - "default" => bail!("'default' cannot be specified with other WASI modules"), - _ => bail!("unsupported WASI module '{}'", module), - }; - - for module in modules.split(',') { - let module = module.trim(); - let (module, value) = if module.starts_with('-') { - (&module[1..], false) - } else { - (module, true) - }; - set(module, value)?; - } - - Ok(wasi_modules) - } - } -} - -/// Select which WASI modules are available at runtime for use by Wasm programs. -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct WasiModules { - /// Enable the wasi-common implementation; eventually this should be split into its separate - /// parts once the implementation allows for it (e.g. wasi-fs, wasi-clocks, etc.). - pub wasi_common: bool, - - /// Enable the experimental wasi-nn implementation. - pub wasi_nn: bool, - - /// Enable the experimental wasi-threads implementation. - pub wasi_threads: bool, - - /// Enable the experimental wasi-http implementation - pub wasi_http: bool, -} - -impl Default for WasiModules { - fn default() -> Self { - Self { - wasi_common: true, - wasi_nn: false, - wasi_threads: false, - wasi_http: false, } - } -} - -impl WasiModules { - /// Enable no modules. - pub fn none() -> Self { - Self { - wasi_common: false, - wasi_nn: false, - wasi_threads: false, - wasi_http: false, - } - } -} - -fn parse_cranelift_flag(name_and_value: &str) -> Result<(String, String)> { - let mut split = name_and_value.splitn(2, '='); - let name = if let Some(name) = split.next() { - name.to_string() - } else { - bail!("missing name in cranelift flag"); - }; - let value = if let Some(value) = split.next() { - value.to_string() - } else { - bail!("missing value in cranelift flag"); - }; - Ok((name, value)) -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_all_features() -> Result<()> { - let options = CommonOptions::try_parse_from(vec!["foo", "--wasm-features=all"])?; - - let WasmFeatures { - reference_types, - multi_value, - bulk_memory, - simd, - relaxed_simd, - tail_call, - threads, - multi_memory, - memory64, - function_references, - #[cfg(feature = "component-model")] - component_model, - } = options.wasm_features.unwrap(); - - assert_eq!(reference_types, Some(true)); - assert_eq!(multi_value, Some(true)); - assert_eq!(bulk_memory, Some(true)); - assert_eq!(simd, Some(true)); - assert_eq!(tail_call, Some(true)); - assert_eq!(threads, Some(true)); - assert_eq!(multi_memory, Some(true)); - assert_eq!(memory64, Some(true)); - assert_eq!(function_references, Some(true)); - assert_eq!(relaxed_simd, Some(true)); - #[cfg(feature = "component-model")] - assert_eq!(component_model, Some(true)); - Ok(()) } - - #[test] - fn test_no_features() -> Result<()> { - let options = CommonOptions::try_parse_from(vec!["foo", "--wasm-features=-all"])?; - - let WasmFeatures { - reference_types, - multi_value, - bulk_memory, - simd, - relaxed_simd, - tail_call, - threads, - multi_memory, - memory64, - function_references, - #[cfg(feature = "component-model")] - component_model, - } = options.wasm_features.unwrap(); - - assert_eq!(reference_types, Some(false)); - assert_eq!(multi_value, Some(false)); - assert_eq!(bulk_memory, Some(false)); - assert_eq!(simd, Some(false)); - assert_eq!(tail_call, Some(false)); - assert_eq!(threads, Some(false)); - assert_eq!(multi_memory, Some(false)); - assert_eq!(memory64, Some(false)); - assert_eq!(function_references, Some(false)); - assert_eq!(relaxed_simd, Some(false)); - #[cfg(feature = "component-model")] - assert_eq!(component_model, Some(false)); - - Ok(()) - } - - #[test] - fn test_multiple_features() -> Result<()> { - let options = CommonOptions::try_parse_from(vec![ - "foo", - "--wasm-features=-reference-types,simd,multi-memory,memory64", - ])?; - - let WasmFeatures { - reference_types, - multi_value, - bulk_memory, - simd, - relaxed_simd, - tail_call, - threads, - multi_memory, - memory64, - function_references, - #[cfg(feature = "component-model")] - component_model, - } = options.wasm_features.unwrap(); - - assert_eq!(reference_types, Some(false)); - assert_eq!(multi_value, None); - assert_eq!(bulk_memory, None); - assert_eq!(simd, Some(true)); - assert_eq!(tail_call, None); - assert_eq!(threads, None); - assert_eq!(multi_memory, Some(true)); - assert_eq!(memory64, Some(true)); - assert_eq!(function_references, None); - assert_eq!(relaxed_simd, None); - #[cfg(feature = "component-model")] - assert_eq!(component_model, None); - - Ok(()) - } - - macro_rules! feature_test { - ($test_name:ident, $name:ident, $flag:literal) => { - #[test] - fn $test_name() -> Result<()> { - let options = - CommonOptions::try_parse_from(vec!["foo", concat!("--wasm-features=", $flag)])?; - - let WasmFeatures { $name, .. } = options.wasm_features.unwrap(); - - assert_eq!($name, Some(true)); - - let options = CommonOptions::try_parse_from(vec![ - "foo", - concat!("--wasm-features=-", $flag), - ])?; - - let WasmFeatures { $name, .. } = options.wasm_features.unwrap(); - - assert_eq!($name, Some(false)); - - Ok(()) - } - }; - } - - feature_test!( - test_reference_types_feature, - reference_types, - "reference-types" - ); - feature_test!(test_multi_value_feature, multi_value, "multi-value"); - feature_test!(test_bulk_memory_feature, bulk_memory, "bulk-memory"); - feature_test!(test_simd_feature, simd, "simd"); - feature_test!(test_relaxed_simd_feature, relaxed_simd, "relaxed-simd"); - feature_test!(test_tail_call_feature, tail_call, "tail-call"); - feature_test!(test_threads_feature, threads, "threads"); - feature_test!(test_multi_memory_feature, multi_memory, "multi-memory"); - feature_test!(test_memory64_feature, memory64, "memory64"); - - #[test] - fn test_default_modules() { - let options = CommonOptions::try_parse_from(vec!["foo", "--wasi-modules=default"]).unwrap(); - assert_eq!( - options.wasi_modules.unwrap(), - WasiModules { - wasi_common: true, - wasi_nn: false, - wasi_threads: false, - wasi_http: false, - } - ); - } - - #[test] - fn test_empty_modules() { - let options = CommonOptions::try_parse_from(vec!["foo", "--wasi-modules="]).unwrap(); - assert_eq!( - options.wasi_modules.unwrap(), - WasiModules { - wasi_common: true, - wasi_nn: false, - wasi_threads: false, - wasi_http: false - } - ); - } - - #[test] - fn test_some_modules() { - let options = CommonOptions::try_parse_from(vec![ - "foo", - "--wasi-modules=experimental-wasi-nn,-wasi-common", - ]) - .unwrap(); - assert_eq!( - options.wasi_modules.unwrap(), - WasiModules { - wasi_common: false, - wasi_nn: true, - wasi_threads: false, - wasi_http: false, - } - ); - } - - #[test] - fn test_no_modules() { - let options = - CommonOptions::try_parse_from(vec!["foo", "--wasi-modules=-default"]).unwrap(); - assert_eq!( - options.wasi_modules.unwrap(), - WasiModules { - wasi_common: false, - wasi_nn: false, - wasi_threads: false, - wasi_http: false, - } - ); - } } diff --git a/crates/cli-flags/src/opt.rs b/crates/cli-flags/src/opt.rs new file mode 100644 index 000000000000..18a3622bbd58 --- /dev/null +++ b/crates/cli-flags/src/opt.rs @@ -0,0 +1,390 @@ +//! Support for parsing Wasmtime's `-O`, `-W`, etc "option groups" +//! +//! This builds up a clap-derive-like system where there's ideally a single +//! macro `wasmtime_option_group!` which is invoked per-option which enables +//! specifying options in a struct-like syntax where all other boilerplate about +//! option parsing is contained exclusively within this module. + +use crate::WasiNnGraph; +use anyhow::{bail, Result}; +use clap::builder::{StringValueParser, TypedValueParser, ValueParserFactory}; +use clap::error::{Error, ErrorKind}; +use std::marker; +use std::time::Duration; + +#[macro_export] +macro_rules! wasmtime_option_group { + ( + pub struct $opts:ident { + $( + $(#[doc = $doc:tt])* + pub $opt:ident: $container:ident<$payload:ty>, + )+ + + $( + #[prefixed = $prefix:tt] + $(#[doc = $prefixed_doc:tt])* + pub $prefixed:ident: Vec<(String, Option)>, + )? + } + enum $option:ident { + ... + } + ) => { + #[derive(Default, Debug)] + pub struct $opts { + $( + pub $opt: $container<$payload>, + )+ + $( + pub $prefixed: Vec<(String, Option)>, + )? + } + + #[derive(Clone, Debug)] + #[allow(non_camel_case_types)] + enum $option { + $( + $opt($payload), + )+ + $( + $prefixed(String, Option), + )? + } + + impl $crate::opt::WasmtimeOption for $option { + const OPTIONS: &'static [$crate::opt::OptionDesc<$option>] = &[ + $( + $crate::opt::OptionDesc { + name: $crate::opt::OptName::Name(stringify!($opt)), + parse: |_, s| { + Ok($option::$opt( + $crate::opt::WasmtimeOptionValue::parse(s)? + )) + }, + val_help: <$payload as $crate::opt::WasmtimeOptionValue>::VAL_HELP, + docs: concat!($($doc, "\n",)*), + }, + )+ + $( + $crate::opt::OptionDesc { + name: $crate::opt::OptName::Prefix($prefix), + parse: |name, val| { + Ok($option::$prefixed( + name.to_string(), + val.map(|v| v.to_string()), + )) + }, + val_help: "[=val]", + docs: concat!($($prefixed_doc, "\n",)*), + }, + )? + ]; + } + + impl $opts { + fn configure_with(&mut self, opts: &[$crate::opt::CommaSeparated<$option>]) { + for opt in opts.iter().flat_map(|o| o.0.iter()) { + match opt { + $( + $option::$opt(val) => { + let dst = &mut self.$opt; + wasmtime_option_group!(@push $container dst val); + } + )+ + $( + $option::$prefixed(key, val) => self.$prefixed.push((key.clone(), val.clone())), + )? + } + } + } + } + }; + + (@push Option $dst:ident $val:ident) => (*$dst = Some($val.clone())); + (@push Vec $dst:ident $val:ident) => ($dst.push($val.clone())); +} + +/// Parser registered with clap which handles parsing the `...` in `-O ...`. +#[derive(Clone, Debug)] +pub struct CommaSeparated(pub Vec); + +impl ValueParserFactory for CommaSeparated +where + T: WasmtimeOption, +{ + type Parser = CommaSeparatedParser; + + fn value_parser() -> CommaSeparatedParser { + CommaSeparatedParser(marker::PhantomData) + } +} + +#[derive(Clone)] +pub struct CommaSeparatedParser(marker::PhantomData); + +impl TypedValueParser for CommaSeparatedParser +where + T: WasmtimeOption, +{ + type Value = CommaSeparated; + + fn parse_ref( + &self, + cmd: &clap::Command, + arg: Option<&clap::Arg>, + value: &std::ffi::OsStr, + ) -> Result { + let val = StringValueParser::new().parse_ref(cmd, arg, value)?; + + let options = T::OPTIONS; + let arg = arg.expect("should always have an argument"); + let arg_long = arg.get_long().expect("should have a long name specified"); + let arg_short = arg.get_short().expect("should have a short name specified"); + + // Handle `-O help` which dumps all the `-O` options, their messages, + // and then exits. + if val == "help" { + let mut max = 0; + for d in options { + max = max.max(d.name.display_string().len() + d.val_help.len()); + } + println!("Available {arg_long} options:\n"); + for d in options { + print!( + " -{arg_short} {:>1$}", + d.name.display_string(), + max - d.val_help.len() + ); + print!("{}", d.val_help); + print!(" --"); + if val == "help" { + for line in d.docs.lines().map(|s| s.trim()) { + if line.is_empty() { + break; + } + print!(" {line}"); + } + println!(); + } else { + println!(); + for line in d.docs.lines().map(|s| s.trim()) { + let line = line.trim(); + println!(" {line}"); + } + } + } + println!("\npass `-{arg_short} help-long` to see longer-form explanations"); + std::process::exit(0); + } + if val == "help-long" { + println!("Available {arg_long} options:\n"); + for d in options { + println!( + " -{arg_short} {}{} --", + d.name.display_string(), + d.val_help + ); + println!(); + for line in d.docs.lines().map(|s| s.trim()) { + let line = line.trim(); + println!(" {line}"); + } + } + std::process::exit(0); + } + + let mut result = Vec::new(); + for val in val.split(',') { + // Split `k=v` into `k` and `v` where `v` is optional + let mut iter = val.splitn(2, '='); + let key = iter.next().unwrap(); + let key_val = iter.next(); + + // Find `key` within `T::OPTIONS` + let option = options + .iter() + .filter_map(|d| match d.name { + OptName::Name(s) => { + let s = s.replace('_', "-"); + if s == key { + Some((d, s)) + } else { + None + } + } + OptName::Prefix(s) => { + let name = key.strip_prefix(s)?.strip_prefix("-")?; + Some((d, name.to_string())) + } + }) + .next(); + + let (desc, key) = match option { + Some(pair) => pair, + None => { + let err = Error::raw( + ErrorKind::InvalidValue, + format!("unknown -{arg_short} / --{arg_long} option: {key}\n"), + ); + return Err(err.with_cmd(cmd)); + } + }; + + result.push((desc.parse)(&key, key_val).map_err(|e| { + Error::raw( + ErrorKind::InvalidValue, + format!("failed to parse -{arg_short} option `{val}`: {e:?}\n"), + ) + .with_cmd(cmd) + })?) + } + + Ok(CommaSeparated(result)) + } +} + +/// Helper trait used by `CommaSeparated` which contains a list of all options +/// supported by the option group. +pub trait WasmtimeOption: Sized + Send + Sync + Clone + 'static { + const OPTIONS: &'static [OptionDesc]; +} + +pub struct OptionDesc { + pub name: OptName, + pub docs: &'static str, + pub parse: fn(&str, Option<&str>) -> Result, + pub val_help: &'static str, +} + +pub enum OptName { + /// A named option. Note that the `str` here uses `_` instead of `-` because + /// it's derived from Rust syntax. + Name(&'static str), + + /// A prefixed option which strips the specified `name`, then `-`. + Prefix(&'static str), +} + +impl OptName { + fn display_string(&self) -> String { + match self { + OptName::Name(s) => s.replace('_', "-"), + OptName::Prefix(s) => format!("{s}-"), + } + } +} + +/// A helper trait for all types of options that can be parsed. This is what +/// actually parses the `=val` in `key=val` +pub trait WasmtimeOptionValue: Sized { + /// Help text for the value to be specified. + const VAL_HELP: &'static str; + + /// Parses the provided value, if given, returning an error on failure. + fn parse(val: Option<&str>) -> Result; +} + +impl WasmtimeOptionValue for String { + const VAL_HELP: &'static str = "=val"; + fn parse(val: Option<&str>) -> Result { + match val { + Some(val) => Ok(val.to_string()), + None => bail!("value must be specified with `key=val` syntax"), + } + } +} + +impl WasmtimeOptionValue for u32 { + const VAL_HELP: &'static str = "=N"; + fn parse(val: Option<&str>) -> Result { + let val = String::parse(val)?; + Ok(val.parse()?) + } +} + +impl WasmtimeOptionValue for u64 { + const VAL_HELP: &'static str = "=N"; + fn parse(val: Option<&str>) -> Result { + let val = String::parse(val)?; + Ok(val.parse()?) + } +} + +impl WasmtimeOptionValue for usize { + const VAL_HELP: &'static str = "=N"; + fn parse(val: Option<&str>) -> Result { + let val = String::parse(val)?; + Ok(val.parse()?) + } +} + +impl WasmtimeOptionValue for bool { + const VAL_HELP: &'static str = "[=y|n]"; + fn parse(val: Option<&str>) -> Result { + match val { + None | Some("y") | Some("yes") | Some("true") => Ok(true), + Some("n") | Some("no") | Some("false") => Ok(false), + Some(s) => bail!("unknown boolean flag `{s}`, only yes,no, accepted"), + } + } +} + +impl WasmtimeOptionValue for Duration { + const VAL_HELP: &'static str = "=N|Ns|Nms|.."; + fn parse(val: Option<&str>) -> Result { + let s = String::parse(val)?; + // assume an integer without a unit specified is a number of seconds ... + if let Ok(val) = s.parse() { + return Ok(Duration::from_secs(val)); + } + // ... otherwise try to parse it with units such as `3s` or `300ms` + let dur = humantime::parse_duration(&s)?; + Ok(dur) + } +} + +impl WasmtimeOptionValue for wasmtime::OptLevel { + const VAL_HELP: &'static str = "=0|1|2|s"; + fn parse(val: Option<&str>) -> Result { + match String::parse(val)?.as_str() { + "0" => Ok(wasmtime::OptLevel::None), + "1" => Ok(wasmtime::OptLevel::Speed), + "2" => Ok(wasmtime::OptLevel::Speed), + "s" => Ok(wasmtime::OptLevel::SpeedAndSize), + other => bail!( + "unknown optimization level `{}`, only 0,1,2,s accepted", + other + ), + } + } +} + +impl WasmtimeOptionValue for wasmtime::Strategy { + const VAL_HELP: &'static str = "=winch|cranelift"; + fn parse(val: Option<&str>) -> Result { + match String::parse(val)?.as_str() { + "cranelift" => Ok(wasmtime::Strategy::Cranelift), + "winch" => Ok(wasmtime::Strategy::Winch), + other => bail!( + "unknown optimization level `{}`, only 0,1,2,s accepted", + other + ), + } + } +} + +impl WasmtimeOptionValue for WasiNnGraph { + const VAL_HELP: &'static str = "=::"; + fn parse(val: Option<&str>) -> Result { + let val = String::parse(val)?; + let mut parts = val.splitn(2, "::"); + Ok(WasiNnGraph { + format: parts.next().unwrap().to_string(), + dir: match parts.next() { + Some(part) => part.into(), + None => bail!("graph does not contain `::` separator for directory"), + }, + }) + } +} diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs index e6d4c905f50b..69b7435cd6c9 100644 --- a/crates/wasmtime/src/config.rs +++ b/crates/wasmtime/src/config.rs @@ -1841,7 +1841,7 @@ pub enum Strategy { /// Possible optimization levels for the Cranelift codegen backend. #[non_exhaustive] -#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] pub enum OptLevel { /// No optimizations performed, minimizes compilation time by disabling most /// optimizations. diff --git a/docs/WASI-tutorial.md b/docs/WASI-tutorial.md index b6c10767eb80..e1e83faebbc7 100644 --- a/docs/WASI-tutorial.md +++ b/docs/WASI-tutorial.md @@ -237,11 +237,10 @@ here ("Operation not permitted"), rather than Unix access controls of the directories they've been granted. This is true when resolving symbolic links as well. -`wasmtime` also has the ability to remap directories, with the `--mapdir` -command-line option: +`wasmtime` also has the ability to remap directories: ``` -$ wasmtime --dir=. --mapdir=/tmp::/var/tmp demo.wasm test.txt /tmp/somewhere.txt +$ wasmtime --dir=. --dir=/tmp::/var/tmp demo.wasm test.txt /tmp/somewhere.txt $ cat /var/tmp/somewhere.txt hello world ``` diff --git a/docs/cli-logging.md b/docs/cli-logging.md index 333825328bea..b05d2d29e9e4 100644 --- a/docs/cli-logging.md +++ b/docs/cli-logging.md @@ -19,7 +19,7 @@ Hello, world! ``` Wasmtime can also redirect the log messages into log files, with the -`--log-to-files` option. It creates one file per thread within Wasmtime, with +`-D log-to-files` option. It creates one file per thread within Wasmtime, with the files named `wasmtime.dbg.*`. [`log`]: https://crates.io/crates/log diff --git a/docs/examples-coredump.md b/docs/examples-coredump.md index 6a78dd5176f2..419d2eac5b64 100644 --- a/docs/examples-coredump.md +++ b/docs/examples-coredump.md @@ -2,7 +2,7 @@ The following steps describe how to debug using Wasm coredump in Wasmtime: -1. Compile your WebAssembly with debug info enabled; for example: +1. Compile your WebAssembly with debug info enabled; for example: ```sh $ rustc foo.rs --target=wasm32-wasi -C debuginfo=2 @@ -31,7 +31,7 @@ The following steps describe how to debug using Wasm coredump in Wasmtime: 2. Run with Wasmtime and Wasm coredump enabled: ```sh - $ wasmtime --coredump-on-trap=/tmp/coredump foo.wasm + $ wasmtime -D coredump=/tmp/coredump foo.wasm thread 'main' panicked at 'attempt to subtract with overflow', foo.rs:10:7 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/docs/examples-debugging.md b/docs/examples-debugging.md index 327618e2a7f6..c373e05c76de 100644 --- a/docs/examples-debugging.md +++ b/docs/examples-debugging.md @@ -4,7 +4,7 @@ The following steps describe a common way to debug a WebAssembly module in Wasmtime: 1. Compile your WebAssembly with debug info enabled, usually `-g`; for - example: + example: ```sh clang foo.c -g -o foo.wasm @@ -17,10 +17,10 @@ Wasmtime: 3. Use a supported debugger: ```sh - lldb -- wasmtime run -g foo.wasm + lldb -- wasmtime run -D debug-info foo.wasm ``` ```sh - gdb --args wasmtime run -g foo.wasm + gdb --args wasmtime run -D debug-info foo.wasm ``` If you run into trouble, the following discussions might help: @@ -30,7 +30,7 @@ If you run into trouble, the following discussions might help: ([#1953](https://github.com/bytecodealliance/wasmtime/issues/1953)) - With LLDB, call `__vmctx.set()` to set the current context before calling any dereference operators - ([#1482](https://github.com/bytecodealliance/wasmtime/issues/1482)): + ([#1482](https://github.com/bytecodealliance/wasmtime/issues/1482)): ```sh (lldb) p __vmctx->set() (lldb) p *foo diff --git a/docs/examples-profiling-guest.md b/docs/examples-profiling-guest.md index 692a07f4aca2..3ccd6457cb1d 100644 --- a/docs/examples-profiling-guest.md +++ b/docs/examples-profiling-guest.md @@ -10,5 +10,5 @@ To use this profiler with the Wasmtime CLI, pass the - `path` is where to write the profile, `wasmtime-guest-profile.json` by default - `interval` is the duration between samples, 10ms by default -When used with `--wasm-timeout`, the timeout will be rounded up to the nearest +When used with `-W timeout=N`, the timeout will be rounded up to the nearest multiple of the profiling interval. diff --git a/src/commands/compile.rs b/src/commands/compile.rs index ad115cdab20f..a26fee670702 100644 --- a/src/commands/compile.rs +++ b/src/commands/compile.rs @@ -12,8 +12,6 @@ static AFTER_HELP: Lazy = Lazy::new(|| { format!( "By default, no CPU features or presets will be enabled for the compilation.\n\ \n\ - {}\ - \n\ Usage examples:\n\ \n\ Compiling a WebAssembly module for the current platform:\n\ @@ -26,8 +24,7 @@ static AFTER_HELP: Lazy = Lazy::new(|| { \n\ Compiling for a specific platform (Linux) and CPU preset (Skylake):\n\ \n \ - wasmtime compile --target x86_64-unknown-linux --cranelift-enable skylake foo.wasm\n", - crate::FLAG_EXPLANATIONS.as_str() + wasmtime compile --target x86_64-unknown-linux -Ccranelift-skylake foo.wasm\n", ) }); @@ -129,7 +126,7 @@ mod test { let command = CompileCommand::try_parse_from(vec![ "compile", - "--disable-logging", + "-Dlogging=n", "-o", output_path.to_str().unwrap(), input_path.to_str().unwrap(), @@ -160,35 +157,21 @@ mod test { // Set all the x64 flags to make sure they work let command = CompileCommand::try_parse_from(vec![ "compile", - "--disable-logging", - "--cranelift-enable", - "has_sse3", - "--cranelift-enable", - "has_ssse3", - "--cranelift-enable", - "has_sse41", - "--cranelift-enable", - "has_sse42", - "--cranelift-enable", - "has_avx", - "--cranelift-enable", - "has_avx2", - "--cranelift-enable", - "has_fma", - "--cranelift-enable", - "has_avx512dq", - "--cranelift-enable", - "has_avx512vl", - "--cranelift-enable", - "has_avx512f", - "--cranelift-enable", - "has_popcnt", - "--cranelift-enable", - "has_bmi1", - "--cranelift-enable", - "has_bmi2", - "--cranelift-enable", - "has_lzcnt", + "-Dlogging=n", + "-Ccranelift-has-sse3", + "-Ccranelift-has-ssse3", + "-Ccranelift-has-sse41", + "-Ccranelift-has-sse42", + "-Ccranelift-has-avx", + "-Ccranelift-has-avx2", + "-Ccranelift-has-fma", + "-Ccranelift-has-avx512dq", + "-Ccranelift-has-avx512vl", + "-Ccranelift-has-avx512f", + "-Ccranelift-has-popcnt", + "-Ccranelift-has-bmi1", + "-Ccranelift-has-bmi2", + "-Ccranelift-has-lzcnt", "-o", output_path.to_str().unwrap(), input_path.to_str().unwrap(), @@ -211,17 +194,12 @@ mod test { // Set all the aarch64 flags to make sure they work let command = CompileCommand::try_parse_from(vec![ "compile", - "--disable-logging", - "--cranelift-enable", - "has_lse", - "--cranelift-enable", - "has_pauth", - "--cranelift-enable", - "sign_return_address", - "--cranelift-enable", - "sign_return_address_all", - "--cranelift-enable", - "sign_return_address_with_bkey", + "-Dlogging=n", + "-Ccranelift-has-lse", + "-Ccranelift-has-pauth", + "-Ccranelift-sign-return-address", + "-Ccranelift-sign-return-address-all", + "-Ccranelift-sign-return-address-with-bkey", "-o", output_path.to_str().unwrap(), input_path.to_str().unwrap(), @@ -244,9 +222,8 @@ mod test { // aarch64 flags should not be supported let command = CompileCommand::try_parse_from(vec![ "compile", - "--disable-logging", - "--cranelift-enable", - "has_lse", + "-Dlogging=n", + "-Ccranelift-has-lse", "-o", output_path.to_str().unwrap(), input_path.to_str().unwrap(), @@ -278,11 +255,11 @@ mod test { "icelake", "znver1", ] { + let flag = format!("-Ccranelift-{preset}"); let command = CompileCommand::try_parse_from(vec![ "compile", - "--disable-logging", - "--cranelift-enable", - preset, + "-Dlogging=n", + flag.as_str(), "-o", output_path.to_str().unwrap(), input_path.to_str().unwrap(), diff --git a/src/commands/explore.rs b/src/commands/explore.rs index 46c2bd79e0fa..59ca4baa6ed6 100644 --- a/src/commands/explore.rs +++ b/src/commands/explore.rs @@ -28,7 +28,7 @@ pub struct ExploreCommand { impl ExploreCommand { /// Executes the command. - pub fn execute(&self) -> Result<()> { + pub fn execute(mut self) -> Result<()> { self.common.init_logging(); let config = self.common.config(self.target.as_deref())?; diff --git a/src/commands/run.rs b/src/commands/run.rs index 968ad59d2f04..975f21bbd737 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -8,7 +8,6 @@ use anyhow::{anyhow, bail, Context as _, Error, Result}; use clap::builder::{OsStringValueParser, TypedValueParser}; use clap::Parser; -use once_cell::sync::Lazy; use std::ffi::OsStr; use std::ffi::OsString; use std::fs::File; @@ -21,7 +20,8 @@ use wasmtime::{ AsContextMut, Engine, Func, GuestProfiler, Module, Precompiled, Store, StoreLimits, StoreLimitsBuilder, UpdateDeadline, Val, ValType, }; -use wasmtime_cli_flags::{CommonOptions, WasiModules}; +use wasmtime_cli_flags::opt::WasmtimeOptionValue; +use wasmtime_cli_flags::CommonOptions; use wasmtime_wasi::maybe_exit_on_error; use wasmtime_wasi::preview2; use wasmtime_wasi::sync::{ambient_authority, Dir, TcpListener, WasiCtxBuilder}; @@ -58,30 +58,14 @@ fn parse_env_var(s: &str) -> Result<(String, Option)> { )) } -fn parse_map_dirs(s: &str) -> Result<(String, String)> { - let parts: Vec<&str> = s.split("::").collect(); - if parts.len() != 2 { - bail!("must contain exactly one double colon ('::')"); - } - Ok((parts[0].into(), parts[1].into())) -} - -fn parse_graphs(s: &str) -> Result<(String, String)> { - let parts: Vec<&str> = s.split("::").collect(); - if parts.len() != 2 { - bail!("must contain exactly one double colon ('::')"); - } - Ok((parts[0].into(), parts[1].into())) -} - -fn parse_dur(s: &str) -> Result { - // assume an integer without a unit specified is a number of seconds ... - if let Ok(val) = s.parse() { - return Ok(Duration::from_secs(val)); - } - // ... otherwise try to parse it with units such as `3s` or `300ms` - let dur = humantime::parse_duration(s)?; - Ok(dur) +fn parse_dirs(s: &str) -> Result<(String, String)> { + let mut parts = s.split("::"); + let guest = parts.next().unwrap(); + let host = match parts.next() { + Some(host) => host, + None => guest, + }; + Ok((guest.into(), host.into())) } fn parse_preloads(s: &str) -> Result<(String, PathBuf)> { @@ -108,35 +92,19 @@ fn parse_profile(s: &str) -> Result { }), ["guest", path, dur] => Ok(Profile::Guest { path: path.to_string(), - interval: parse_dur(dur)?, + interval: WasmtimeOptionValue::parse(Some(dur))?, }), _ => bail!("unknown profiling strategy: {s}"), } } -static AFTER_HELP: Lazy = Lazy::new(|| crate::FLAG_EXPLANATIONS.to_string()); - /// Runs a WebAssembly module #[derive(Parser)] -#[structopt(name = "run", trailing_var_arg = true, after_help = AFTER_HELP.as_str())] +#[structopt(name = "run", trailing_var_arg = true)] pub struct RunCommand { #[clap(flatten)] common: CommonOptions, - /// Allow unknown exports when running commands. - #[clap(long = "allow-unknown-exports")] - allow_unknown_exports: bool, - - /// Allow the main module to import unknown functions, using an - /// implementation that immediately traps, when running commands. - #[clap(long = "trap-unknown-imports")] - trap_unknown_imports: bool, - - /// Allow the main module to import unknown functions, using an - /// implementation that returns default values, when running commands. - #[clap(long = "default-values-unknown-imports")] - default_values_unknown_imports: bool, - /// Allow executing precompiled WebAssembly modules as `*.cwasm` files. /// /// Note that this option is not safe to pass if the module being passed in @@ -146,22 +114,14 @@ pub struct RunCommand { #[clap(long = "allow-precompiled")] allow_precompiled: bool, - /// Inherit environment variables and file descriptors following the - /// systemd listen fd specification (UNIX only) - #[clap(long = "listenfd")] - listenfd: bool, - - /// Grant access to the given TCP listen socket - #[clap( - long = "tcplisten", - number_of_values = 1, - value_name = "SOCKET ADDRESS" - )] - tcplisten: Vec, - - /// Grant access to the given host directory - #[clap(long = "dir", number_of_values = 1, value_name = "DIRECTORY")] - dirs: Vec, + /// Grant access of a host directory to a guest. + /// + /// If specified as just `GUEST_DIR` then the same directory name on the + /// host is made available within the guest. If specified as `GUEST::HOST` + /// then the `HOST` directory is opened and made available as the name + /// `GUEST` in the guest. + #[clap(long = "dir", value_name = "GUEST_DIR[::HOST_DIR]", value_parser = parse_dirs)] + dirs: Vec<(String, String)>, /// Pass an environment variable to the program. /// @@ -177,21 +137,6 @@ pub struct RunCommand { #[clap(long, value_name = "FUNCTION")] invoke: Option, - /// Grant access to a guest directory mapped as a host directory - #[clap(long = "mapdir", number_of_values = 1, value_name = "GUEST_DIR::HOST_DIR", value_parser = parse_map_dirs)] - map_dirs: Vec<(String, String)>, - - /// Pre-load machine learning graphs (i.e., models) for use by wasi-nn. - /// - /// Each use of the flag will preload a ML model from the host directory - /// using the given model encoding. The model will be mapped to the - /// directory name: e.g., `--wasi-nn-graph openvino:/foo/bar` will preload - /// an OpenVINO model named `bar`. Note that which model encodings are - /// available is dependent on the backends implemented in the - /// `wasmtime_wasi_nn` crate. - #[clap(long = "wasi-nn-graph", value_name = "FORMAT::HOST_DIR", value_parser = parse_graphs)] - graphs: Vec<(String, String)>, - /// The path of the WebAssembly module to run #[clap( required = true, @@ -209,14 +154,6 @@ pub struct RunCommand { )] preloads: Vec<(String, PathBuf)>, - /// Maximum execution time of wasm code before timing out (1, 2s, 100ms, etc) - #[clap( - long = "wasm-timeout", - value_name = "TIME", - value_parser = parse_dur, - )] - wasm_timeout: Option, - /// Profiling strategy (valid options are: perfmap, jitdump, vtune, guest) /// /// The perfmap, jitdump, and vtune profiling strategies integrate Wasmtime @@ -239,65 +176,10 @@ pub struct RunCommand { )] profile: Option, - /// Enable coredump generation after a WebAssembly trap. - #[clap(long = "coredump-on-trap", value_name = "PATH")] - coredump_on_trap: Option, - // NOTE: this must come last for trailing varargs /// The arguments to pass to the module #[clap(value_name = "ARGS")] module_args: Vec, - - /// Maximum size, in bytes, that a linear memory is allowed to reach. - /// - /// Growth beyond this limit will cause `memory.grow` instructions in - /// WebAssembly modules to return -1 and fail. - #[clap(long, value_name = "BYTES")] - max_memory_size: Option, - - /// Maximum size, in table elements, that a table is allowed to reach. - #[clap(long)] - max_table_elements: Option, - - /// Maximum number of WebAssembly instances allowed to be created. - #[clap(long)] - max_instances: Option, - - /// Maximum number of WebAssembly tables allowed to be created. - #[clap(long)] - max_tables: Option, - - /// Maximum number of WebAssembly linear memories allowed to be created. - #[clap(long)] - max_memories: Option, - - /// Force a trap to be raised on `memory.grow` and `table.grow` failure - /// instead of returning -1 from these instructions. - /// - /// This is not necessarily a spec-compliant option to enable but can be - /// useful for tracking down a backtrace of what is requesting so much - /// memory, for example. - #[clap(long)] - trap_on_grow_failure: bool, - - /// Enables memory error checking. - /// - /// See wmemcheck.md for documentation on how to use. - #[clap(long)] - wmemcheck: bool, - - /// Indicates that the implementation of WASI preview1 should be backed by - /// the preview2 implementation for components. - /// - /// This will become the default in the future and this option will be - /// removed. For now this is primarily here for testing. - #[clap(long)] - preview2: bool, - - /// Flag for WASI preview2 to inherit the host's network within the guest so - /// it has full access to all addresses/ports/etc. - #[clap(long)] - inherit_network: bool, } #[derive(Clone)] @@ -338,12 +220,12 @@ impl CliModule { impl RunCommand { /// Executes the command. - pub fn execute(&self) -> Result<()> { + pub fn execute(mut self) -> Result<()> { self.common.init_logging(); let mut config = self.common.config(None)?; - if self.wasm_timeout.is_some() { + if self.common.wasm.timeout.is_some() { config.epoch_interruption(true); } match self.profile { @@ -357,15 +239,13 @@ impl RunCommand { None => {} } - config.wmemcheck(self.wmemcheck); - let engine = Engine::new(&config)?; // Read the wasm module binary either as `*.wat` or a raw binary. let main = self.load_module(&engine, &self.module)?; // Validate coredump-on-trap argument - if let Some(coredump_path) = self.coredump_on_trap.as_ref() { + if let Some(coredump_path) = &self.common.debug.coredump { if coredump_path.contains("%") { bail!("the coredump-on-trap path does not support patterns yet.") } @@ -378,10 +258,10 @@ impl RunCommand { CliLinker::Component(wasmtime::component::Linker::new(&engine)) } }; - if self.allow_unknown_exports { + if let Some(enable) = self.common.wasm.unknown_exports_allow { match &mut linker { CliLinker::Core(l) => { - l.allow_unknown_exports(true); + l.allow_unknown_exports(enable); } #[cfg(feature = "component-model")] CliLinker::Component(_) => { @@ -395,29 +275,30 @@ impl RunCommand { self.populate_with_wasi(&mut linker, &mut store, &main)?; let mut limits = StoreLimitsBuilder::new(); - if let Some(max) = self.max_memory_size { + if let Some(max) = self.common.wasm.max_memory_size { limits = limits.memory_size(max); } - if let Some(max) = self.max_table_elements { + if let Some(max) = self.common.wasm.max_table_elements { limits = limits.table_elements(max); } - if let Some(max) = self.max_instances { + if let Some(max) = self.common.wasm.max_instances { limits = limits.instances(max); } - if let Some(max) = self.max_tables { + if let Some(max) = self.common.wasm.max_tables { limits = limits.tables(max); } - if let Some(max) = self.max_memories { + if let Some(max) = self.common.wasm.max_memories { limits = limits.memories(max); } - store.data_mut().limits = limits - .trap_on_grow_failure(self.trap_on_grow_failure) - .build(); + if let Some(enable) = self.common.wasm.trap_on_grow_failure { + limits = limits.trap_on_grow_failure(enable); + } + store.data_mut().limits = limits.build(); store.limiter(|t| &mut t.limits); // If fuel has been configured, we want to add the configured // fuel amount to this store. - if let Some(fuel) = self.common.fuel { + if let Some(fuel) = self.common.wasm.fuel { store.add_fuel(fuel)?; } @@ -471,15 +352,7 @@ impl RunCommand { fn compute_preopen_dirs(&self) -> Result> { let mut preopen_dirs = Vec::new(); - for dir in self.dirs.iter() { - preopen_dirs.push(( - dir.clone(), - Dir::open_ambient_dir(dir, ambient_authority()) - .with_context(|| format!("failed to open directory '{}'", dir))?, - )); - } - - for (guest, host) in self.map_dirs.iter() { + for (guest, host) in self.dirs.iter() { preopen_dirs.push(( guest.clone(), Dir::open_ambient_dir(host, ambient_authority()) @@ -493,7 +366,7 @@ impl RunCommand { fn compute_preopen_sockets(&self) -> Result> { let mut listeners = vec![]; - for address in &self.tcplisten { + for address in &self.common.wasi.tcplisten { let stdlistener = std::net::TcpListener::bind(address) .with_context(|| format!("failed to bind to address '{}'", address))?; @@ -551,7 +424,7 @@ impl RunCommand { store.as_context_mut().data_mut().guest_profiler = Some(profiler); } - if let Some(timeout) = self.wasm_timeout { + if let Some(timeout) = self.common.wasm.timeout { let mut timeout = (timeout.as_secs_f64() / interval.as_secs_f64()).ceil() as u64; assert!(timeout > 0); store.epoch_deadline_callback(move |mut store| { @@ -593,7 +466,7 @@ impl RunCommand { }); } - if let Some(timeout) = self.wasm_timeout { + if let Some(timeout) = self.common.wasm.timeout { store.set_epoch_deadline(1); let engine = store.engine().clone(); thread::spawn(move || { @@ -614,7 +487,7 @@ impl RunCommand { ) -> Result<()> { // The main module might be allowed to have unknown imports, which // should be defined as traps: - if self.trap_unknown_imports { + if self.common.wasm.unknown_imports_trap == Some(true) { match linker { CliLinker::Core(linker) => { linker.define_unknown_imports_as_traps(module.unwrap_core())?; @@ -624,7 +497,7 @@ impl RunCommand { } // ...or as default values. - if self.default_values_unknown_imports { + if self.common.wasm.unknown_imports_default == Some(true) { match linker { CliLinker::Core(linker) => { linker.define_unknown_imports_as_default_values(module.unwrap_core())?; @@ -759,7 +632,7 @@ impl RunCommand { } fn handle_coredump(&self, err: Error) -> Error { - let coredump_path = match &self.coredump_on_trap { + let coredump_path = match &self.common.debug.coredump { Some(path) => path, None => return err, }; @@ -882,13 +755,7 @@ impl RunCommand { #[cfg(feature = "component-model")] fn ensure_allow_components(&self) -> Result<()> { - if !self - .common - .wasm_features - .unwrap_or_default() - .component_model - .unwrap_or(false) - { + if self.common.wasm.component_model != Some(true) { bail!("cannot execute a component without `--wasm-features component-model`"); } @@ -902,12 +769,10 @@ impl RunCommand { store: &mut Store, module: &CliModule, ) -> Result<()> { - let wasi_modules = self.common.wasi_modules.unwrap_or(WasiModules::default()); - - if wasi_modules.wasi_common { + if self.common.wasi.common != Some(false) { match linker { CliLinker::Core(linker) => { - if self.preview2 { + if self.common.wasi.preview2 == Some(true) { preview2::preview1::add_to_linker_sync(linker)?; self.set_preview2_ctx(store)?; } else { @@ -925,7 +790,7 @@ impl RunCommand { } } - if wasi_modules.wasi_nn { + if self.common.wasi.nn == Some(true) { #[cfg(not(feature = "wasi-nn"))] { bail!("Cannot enable wasi-nn when the binary is not compiled with this feature."); @@ -954,12 +819,19 @@ impl RunCommand { })?; } } - let (backends, registry) = wasmtime_wasi_nn::preload(&self.graphs)?; + let graphs = self + .common + .wasi + .nn_graph + .iter() + .map(|g| (g.format.clone(), g.dir.clone())) + .collect::>(); + let (backends, registry) = wasmtime_wasi_nn::preload(&graphs)?; store.data_mut().wasi_nn = Some(Arc::new(WasiNnCtx::new(backends, registry))); } } - if wasi_modules.wasi_threads { + if self.common.wasi.threads == Some(true) { #[cfg(not(feature = "wasi-threads"))] { // Silence the unused warning for `module` as it is only used in the @@ -987,7 +859,7 @@ impl RunCommand { } } - if wasi_modules.wasi_http { + if self.common.wasi.http == Some(true) { #[cfg(not(feature = "wasi-http"))] { bail!("Cannot enable wasi-http when the binary is not compiled with this feature."); @@ -1025,7 +897,7 @@ impl RunCommand { let mut num_fd: usize = 3; - if self.listenfd { + if self.common.wasi.listenfd == Some(true) { num_fd = ctx_set_listenfd(num_fd, &mut builder)?; } @@ -1055,7 +927,7 @@ impl RunCommand { builder.env(key, &value); } - if self.listenfd { + if self.common.wasi.listenfd == Some(true) { bail!("components do not support --listenfd"); } for _ in self.compute_preopen_sockets()? { @@ -1071,7 +943,7 @@ impl RunCommand { ); } - if self.inherit_network { + if self.common.wasi.inherit_network == Some(true) { builder.inherit_network(ambient_authority()); } diff --git a/src/commands/wast.rs b/src/commands/wast.rs index d1d14307e8e4..9e02ebd0ae89 100644 --- a/src/commands/wast.rs +++ b/src/commands/wast.rs @@ -2,21 +2,14 @@ use anyhow::{Context as _, Result}; use clap::Parser; -use once_cell::sync::Lazy; use std::path::PathBuf; use wasmtime::{Engine, Store}; use wasmtime_cli_flags::CommonOptions; use wasmtime_wast::WastContext; -static AFTER_HELP: Lazy = Lazy::new(|| crate::FLAG_EXPLANATIONS.to_string()); - /// Runs a WebAssembly test script file #[derive(Parser)] -#[clap( - name = "wast", - version, - after_help = AFTER_HELP.as_str(), -)] +#[clap(name = "wast", version)] pub struct WastCommand { #[clap(flatten)] common: CommonOptions, @@ -28,7 +21,7 @@ pub struct WastCommand { impl WastCommand { /// Executes the command. - pub fn execute(self) -> Result<()> { + pub fn execute(mut self) -> Result<()> { self.common.init_logging(); let config = self.common.config(None)?; diff --git a/src/lib.rs b/src/lib.rs index 83e27596e737..5cf21f6fe768 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,41 +10,4 @@ )] #![warn(unused_import_braces)] -use once_cell::sync::Lazy; -use wasmtime_cli_flags::{SUPPORTED_WASI_MODULES, SUPPORTED_WASM_FEATURES}; - -static FLAG_EXPLANATIONS: Lazy = Lazy::new(|| { - use std::fmt::Write; - - let mut s = String::new(); - - // Explain --wasm-features. - writeln!(&mut s, "Supported values for `--wasm-features`:").unwrap(); - writeln!(&mut s).unwrap(); - let max = SUPPORTED_WASM_FEATURES - .iter() - .max_by_key(|(name, _)| name.len()) - .unwrap(); - for (name, desc) in SUPPORTED_WASM_FEATURES.iter() { - writeln!(&mut s, "{:width$} {}", name, desc, width = max.0.len() + 2).unwrap(); - } - writeln!(&mut s).unwrap(); - - // Explain --wasi-modules. - writeln!(&mut s, "Supported values for `--wasi-modules`:").unwrap(); - writeln!(&mut s).unwrap(); - let max = SUPPORTED_WASI_MODULES - .iter() - .max_by_key(|(name, _)| name.len()) - .unwrap(); - for (name, desc) in SUPPORTED_WASI_MODULES.iter() { - writeln!(&mut s, "{:width$} {}", name, desc, width = max.0.len() + 2).unwrap(); - } - - writeln!(&mut s).unwrap(); - writeln!(&mut s, "Features prefixed with '-' will be disabled.").unwrap(); - - s -}); - pub mod commands; diff --git a/tests/all/cli_tests.rs b/tests/all/cli_tests.rs index be10bc1d9d9c..ac0f739584e5 100644 --- a/tests/all/cli_tests.rs +++ b/tests/all/cli_tests.rs @@ -76,7 +76,7 @@ fn run_wasmtime_simple() -> Result<()> { "run", "--invoke", "simple", - "--disable-cache", + "-Ccache=n", wasm.path().to_str().unwrap(), "4", ])?; @@ -90,7 +90,7 @@ fn run_wasmtime_simple_fail_no_args() -> Result<()> { assert!( run_wasmtime(&[ "run", - "--disable-cache", + "-Ccache=n", "--invoke", "simple", wasm.path().to_str().unwrap(), @@ -105,12 +105,12 @@ fn run_wasmtime_simple_fail_no_args() -> Result<()> { fn run_coredump_smoketest() -> Result<()> { let wasm = build_wasm("tests/all/cli_tests/coredump_smoketest.wat")?; let coredump_file = NamedTempFile::new()?; - let coredump_arg = format!("--coredump-on-trap={}", coredump_file.path().display()); + let coredump_arg = format!("-Dcoredump={}", coredump_file.path().display()); let err = run_wasmtime(&[ "run", "--invoke", "a", - "--disable-cache", + "-Ccache=n", &coredump_arg, wasm.path().to_str().unwrap(), ]) @@ -130,7 +130,7 @@ fn run_wasmtime_simple_wat() -> Result<()> { "run", "--invoke", "simple", - "--disable-cache", + "-Ccache=n", wasm.path().to_str().unwrap(), "4", ])?; @@ -139,7 +139,7 @@ fn run_wasmtime_simple_wat() -> Result<()> { "run", "--invoke", "get_f32", - "--disable-cache", + "-Ccache=n", wasm.path().to_str().unwrap(), ])?, "100\n" @@ -149,7 +149,7 @@ fn run_wasmtime_simple_wat() -> Result<()> { "run", "--invoke", "get_f64", - "--disable-cache", + "-Ccache=n", wasm.path().to_str().unwrap(), ])?, "100\n" @@ -161,8 +161,7 @@ fn run_wasmtime_simple_wat() -> Result<()> { #[test] fn run_wasmtime_unreachable_wat() -> Result<()> { let wasm = build_wasm("tests/all/cli_tests/unreachable.wat")?; - let output = - run_wasmtime_for_output(&[wasm.path().to_str().unwrap(), "--disable-cache"], None)?; + let output = run_wasmtime_for_output(&[wasm.path().to_str().unwrap(), "-Ccache=n"], None)?; assert_ne!(output.stderr, b""); assert_eq!(output.stdout, b""); @@ -185,7 +184,7 @@ fn run_wasmtime_unreachable_wat() -> Result<()> { #[test] fn hello_wasi_snapshot0() -> Result<()> { let wasm = build_wasm("tests/all/cli_tests/hello_wasi_snapshot0.wat")?; - let stdout = run_wasmtime(&["--disable-cache", wasm.path().to_str().unwrap()])?; + let stdout = run_wasmtime(&["-Ccache=n", wasm.path().to_str().unwrap()])?; assert_eq!(stdout, "Hello, world!\n"); Ok(()) } @@ -194,7 +193,7 @@ fn hello_wasi_snapshot0() -> Result<()> { #[test] fn hello_wasi_snapshot1() -> Result<()> { let wasm = build_wasm("tests/all/cli_tests/hello_wasi_snapshot1.wat")?; - let stdout = run_wasmtime(&["--disable-cache", wasm.path().to_str().unwrap()])?; + let stdout = run_wasmtime(&["-Ccache=n", wasm.path().to_str().unwrap()])?; assert_eq!(stdout, "Hello, world!\n"); Ok(()) } @@ -205,9 +204,8 @@ fn timeout_in_start() -> Result<()> { let output = run_wasmtime_for_output( &[ "run", - "--wasm-timeout", - "1ms", - "--disable-cache", + "-Wtimeout=1ms", + "-Ccache=n", wasm.path().to_str().unwrap(), ], None, @@ -229,9 +227,8 @@ fn timeout_in_invoke() -> Result<()> { let output = run_wasmtime_for_output( &[ "run", - "--wasm-timeout", - "1ms", - "--disable-cache", + "-Wtimeout=1ms", + "-Ccache=n", wasm.path().to_str().unwrap(), ], None, @@ -251,8 +248,7 @@ fn timeout_in_invoke() -> Result<()> { #[test] fn exit2_wasi_snapshot0() -> Result<()> { let wasm = build_wasm("tests/all/cli_tests/exit2_wasi_snapshot0.wat")?; - let output = - run_wasmtime_for_output(&["--disable-cache", wasm.path().to_str().unwrap()], None)?; + let output = run_wasmtime_for_output(&["-Ccache=n", wasm.path().to_str().unwrap()], None)?; assert_eq!(output.status.code().unwrap(), 2); Ok(()) } @@ -261,8 +257,7 @@ fn exit2_wasi_snapshot0() -> Result<()> { #[test] fn exit2_wasi_snapshot1() -> Result<()> { let wasm = build_wasm("tests/all/cli_tests/exit2_wasi_snapshot1.wat")?; - let output = - run_wasmtime_for_output(&["--disable-cache", wasm.path().to_str().unwrap()], None)?; + let output = run_wasmtime_for_output(&["-Ccache=n", wasm.path().to_str().unwrap()], None)?; assert_eq!(output.status.code().unwrap(), 2); Ok(()) } @@ -271,8 +266,7 @@ fn exit2_wasi_snapshot1() -> Result<()> { #[test] fn exit125_wasi_snapshot0() -> Result<()> { let wasm = build_wasm("tests/all/cli_tests/exit125_wasi_snapshot0.wat")?; - let output = - run_wasmtime_for_output(&["--disable-cache", wasm.path().to_str().unwrap()], None)?; + let output = run_wasmtime_for_output(&["-Ccache=n", wasm.path().to_str().unwrap()], None)?; if cfg!(windows) { assert_eq!(output.status.code().unwrap(), 1); } else { @@ -285,8 +279,7 @@ fn exit125_wasi_snapshot0() -> Result<()> { #[test] fn exit125_wasi_snapshot1() -> Result<()> { let wasm = build_wasm("tests/all/cli_tests/exit125_wasi_snapshot1.wat")?; - let output = - run_wasmtime_for_output(&["--disable-cache", wasm.path().to_str().unwrap()], None)?; + let output = run_wasmtime_for_output(&["-Ccache=n", wasm.path().to_str().unwrap()], None)?; if cfg!(windows) { assert_eq!(output.status.code().unwrap(), 1); } else { @@ -299,8 +292,7 @@ fn exit125_wasi_snapshot1() -> Result<()> { #[test] fn exit126_wasi_snapshot0() -> Result<()> { let wasm = build_wasm("tests/all/cli_tests/exit126_wasi_snapshot0.wat")?; - let output = - run_wasmtime_for_output(&["--disable-cache", wasm.path().to_str().unwrap()], None)?; + let output = run_wasmtime_for_output(&["-Ccache=n", wasm.path().to_str().unwrap()], None)?; assert_eq!(output.status.code().unwrap(), 1); assert!(output.stdout.is_empty()); assert!(String::from_utf8_lossy(&output.stderr).contains("invalid exit status")); @@ -311,8 +303,7 @@ fn exit126_wasi_snapshot0() -> Result<()> { #[test] fn exit126_wasi_snapshot1() -> Result<()> { let wasm = build_wasm("tests/all/cli_tests/exit126_wasi_snapshot1.wat")?; - let output = - run_wasmtime_for_output(&[wasm.path().to_str().unwrap(), "--disable-cache"], None)?; + let output = run_wasmtime_for_output(&[wasm.path().to_str().unwrap(), "-Ccache=n"], None)?; assert_eq!(output.status.code().unwrap(), 1); assert!(output.stdout.is_empty()); assert!(String::from_utf8_lossy(&output.stderr).contains("invalid exit status")); @@ -323,7 +314,7 @@ fn exit126_wasi_snapshot1() -> Result<()> { #[test] fn minimal_command() -> Result<()> { let wasm = build_wasm("tests/all/cli_tests/minimal-command.wat")?; - let stdout = run_wasmtime(&["--disable-cache", wasm.path().to_str().unwrap()])?; + let stdout = run_wasmtime(&["-Ccache=n", wasm.path().to_str().unwrap()])?; assert_eq!(stdout, ""); Ok(()) } @@ -332,7 +323,7 @@ fn minimal_command() -> Result<()> { #[test] fn minimal_reactor() -> Result<()> { let wasm = build_wasm("tests/all/cli_tests/minimal-reactor.wat")?; - let stdout = run_wasmtime(&["--disable-cache", wasm.path().to_str().unwrap()])?; + let stdout = run_wasmtime(&["-Ccache=n", wasm.path().to_str().unwrap()])?; assert_eq!(stdout, ""); Ok(()) } @@ -345,7 +336,7 @@ fn command_invoke() -> Result<()> { "run", "--invoke", "_start", - "--disable-cache", + "-Ccache=n", wasm.path().to_str().unwrap(), ])?; Ok(()) @@ -359,7 +350,7 @@ fn reactor_invoke() -> Result<()> { "run", "--invoke", "_initialize", - "--disable-cache", + "-Ccache=n", wasm.path().to_str().unwrap(), ])?; Ok(()) @@ -371,7 +362,7 @@ fn greeter() -> Result<()> { let wasm = build_wasm("tests/all/cli_tests/greeter_command.wat")?; let stdout = run_wasmtime(&[ "run", - "--disable-cache", + "-Ccache=n", "--preload", "reactor=tests/all/cli_tests/greeter_reactor.wat", wasm.path().to_str().unwrap(), @@ -389,7 +380,7 @@ fn greeter_preload_command() -> Result<()> { let wasm = build_wasm("tests/all/cli_tests/greeter_reactor.wat")?; let stdout = run_wasmtime(&[ "run", - "--disable-cache", + "-Ccache=n", "--preload", "reactor=tests/all/cli_tests/hello_wasi_snapshot1.wat", wasm.path().to_str().unwrap(), @@ -404,7 +395,7 @@ fn greeter_preload_callable_command() -> Result<()> { let wasm = build_wasm("tests/all/cli_tests/greeter_command.wat")?; let stdout = run_wasmtime(&[ "run", - "--disable-cache", + "-Ccache=n", "--preload", "reactor=tests/all/cli_tests/greeter_callable_command.wat", wasm.path().to_str().unwrap(), @@ -418,8 +409,7 @@ fn greeter_preload_callable_command() -> Result<()> { #[test] fn exit_with_saved_fprs() -> Result<()> { let wasm = build_wasm("tests/all/cli_tests/exit_with_saved_fprs.wat")?; - let output = - run_wasmtime_for_output(&["--disable-cache", wasm.path().to_str().unwrap()], None)?; + let output = run_wasmtime_for_output(&["-Ccache=n", wasm.path().to_str().unwrap()], None)?; assert_eq!(output.status.code().unwrap(), 0); assert!(output.stdout.is_empty()); Ok(()) @@ -449,7 +439,7 @@ fn hello_wasi_snapshot0_from_stdin() -> Result<()> { let wasm = build_wasm("tests/all/cli_tests/hello_wasi_snapshot0.wat")?; let stdout = { let path = wasm.path(); - let args: &[&str] = &["--disable-cache", "-"]; + let args: &[&str] = &["-Ccache=n", "-"]; let output = run_wasmtime_for_output(args, Some(path))?; if !output.status.success() { bail!( @@ -555,11 +545,9 @@ fn run_threads() -> Result<()> { let wasm = build_wasm("tests/all/cli_tests/threads.wat")?; let stdout = run_wasmtime(&[ "run", - "--wasi-modules", - "experimental-wasi-threads", - "--wasm-features", - "threads", - "--disable-cache", + "-Wthreads", + "-Sthreads", + "-Ccache=n", wasm.path().to_str().unwrap(), ])?; @@ -583,11 +571,9 @@ fn run_simple_with_wasi_threads() -> Result<()> { let wasm = build_wasm("tests/all/cli_tests/simple.wat")?; let stdout = run_wasmtime(&[ "run", - "--wasi-modules", - "experimental-wasi-threads", - "--wasm-features", - "threads", - "--disable-cache", + "-Wthreads", + "-Sthreads", + "-Ccache=n", "--invoke", "simple", wasm.path().to_str().unwrap(), @@ -685,7 +671,7 @@ fn name_same_as_builtin_command() -> Result<()> { // NB: this will change in Wasmtime 14 when #6737 is relanded. let output = get_wasmtime_command()? .current_dir("tests/all/cli_tests") - .arg("--disable-cache") + .arg("-Ccache=n") .arg("run") .output()?; assert!(!output.status.success(), "expected failure got {output:#?}"); @@ -742,11 +728,7 @@ fn wasi_misaligned_pointer() -> Result<()> { #[ignore] // FIXME(#6811) currently is flaky and may produce no output fn hello_with_preview2() -> Result<()> { let wasm = build_wasm("tests/all/cli_tests/hello_wasi_snapshot1.wat")?; - let stdout = run_wasmtime(&[ - "--disable-cache", - "--preview2", - wasm.path().to_str().unwrap(), - ])?; + let stdout = run_wasmtime(&["-Ccache=n", "--preview2", wasm.path().to_str().unwrap()])?; assert_eq!(stdout, "Hello, world!\n"); Ok(()) } @@ -757,7 +739,7 @@ fn component_missing_feature() -> Result<()> { let path = "tests/all/cli_tests/empty-component.wat"; let wasm = build_wasm(path)?; let output = get_wasmtime_command()? - .arg("--disable-cache") + .arg("-Ccache=n") .arg(wasm.path()) .output()?; assert!(!output.status.success()); @@ -769,7 +751,7 @@ fn component_missing_feature() -> Result<()> { // also tests with raw *.wat input let output = get_wasmtime_command()? - .arg("--disable-cache") + .arg("-Ccache=n") .arg(path) .output()?; assert!(!output.status.success()); @@ -787,7 +769,7 @@ fn component_missing_feature() -> Result<()> { #[test] fn bad_text_syntax() -> Result<()> { let output = get_wasmtime_command()? - .arg("--disable-cache") + .arg("-Ccache=n") .arg("tests/all/cli_tests/bad-syntax.wat") .output()?; assert!(!output.status.success()); @@ -807,11 +789,11 @@ fn run_basic_component() -> Result<()> { // Run both the `*.wasm` binary and the text format run_wasmtime(&[ - "--disable-cache", - "--wasm-features=component-model", + "-Ccache=n", + "-Wcomponent-model", wasm.path().to_str().unwrap(), ])?; - run_wasmtime(&["--disable-cache", "--wasm-features=component-model", path])?; + run_wasmtime(&["-Ccache=n", "-Wcomponent-model", path])?; Ok(()) } @@ -819,14 +801,11 @@ fn run_basic_component() -> Result<()> { #[cfg(feature = "wasi-http")] #[test] fn run_wasi_http_module() -> Result<()> { - let wasm = build_wasm("tests/all/cli_tests/wasi-http.wat")?; let output = run_wasmtime_for_output( &[ - "--preview2", - "--wasi-modules", - "experimental-wasi-http", - "--disable-cache", - wasm.path().to_str().unwrap(), + "-Shttp,preview2", + "-Ccache=no", + "tests/all/cli_tests/wasi-http.wat", ], None, )?; @@ -848,12 +827,12 @@ fn run_precompiled_component() -> Result<()> { "tests/all/cli_tests/component-basic.wat", "-o", cwasm.to_str().unwrap(), - "--wasm-features=component-model", + "-Wcomponent-model", ])?; assert_eq!(stdout, ""); let stdout = run_wasmtime(&[ "run", - "--wasm-features=component-model", + "-Wcomponent-model", "--allow-precompiled", cwasm.to_str().unwrap(), ])?; @@ -867,8 +846,8 @@ fn memory_growth_failure() -> Result<()> { let output = get_wasmtime_command()? .args(&[ "run", - "--wasm-features=memory64", - "--trap-on-grow-failure", + "-Wmemory64", + "-Wtrap-on-grow-failure", "tests/all/cli_tests/memory-grow-failure.wat", ]) .output()?; @@ -886,7 +865,7 @@ fn table_growth_failure() -> Result<()> { let output = get_wasmtime_command()? .args(&[ "run", - "--trap-on-grow-failure", + "-Wtrap-on-grow-failure", "tests/all/cli_tests/table-grow-failure.wat", ]) .output()?; @@ -904,7 +883,7 @@ fn table_growth_failure2() -> Result<()> { let output = get_wasmtime_command()? .args(&[ "run", - "--trap-on-grow-failure", + "-Wtrap-on-grow-failure", "tests/all/cli_tests/table-grow-failure2.wat", ]) .output()?; @@ -916,3 +895,48 @@ fn table_growth_failure2() -> Result<()> { ); Ok(()) } + +#[test] +fn option_group_help() -> Result<()> { + run_wasmtime(&["run", "-Whelp"])?; + run_wasmtime(&["run", "-O", "help"])?; + run_wasmtime(&["run", "--codegen", "help"])?; + run_wasmtime(&["run", "--debug=help"])?; + run_wasmtime(&["run", "-Shelp"])?; + run_wasmtime(&["run", "-Whelp-long"])?; + Ok(()) +} + +#[test] +fn option_group_comma_separated() -> Result<()> { + run_wasmtime(&[ + "run", + "-Wrelaxed-simd,simd", + "tests/all/cli_tests/simple.wat", + ])?; + Ok(()) +} + +#[test] +fn option_group_boolean_parsing() -> Result<()> { + run_wasmtime(&["run", "-Wrelaxed-simd", "tests/all/cli_tests/simple.wat"])?; + run_wasmtime(&["run", "-Wrelaxed-simd=n", "tests/all/cli_tests/simple.wat"])?; + run_wasmtime(&["run", "-Wrelaxed-simd=y", "tests/all/cli_tests/simple.wat"])?; + run_wasmtime(&["run", "-Wrelaxed-simd=no", "tests/all/cli_tests/simple.wat"])?; + run_wasmtime(&[ + "run", + "-Wrelaxed-simd=yes", + "tests/all/cli_tests/simple.wat", + ])?; + run_wasmtime(&[ + "run", + "-Wrelaxed-simd=true", + "tests/all/cli_tests/simple.wat", + ])?; + run_wasmtime(&[ + "run", + "-Wrelaxed-simd=false", + "tests/all/cli_tests/simple.wat", + ])?; + Ok(()) +} diff --git a/tests/all/debug/gdb.rs b/tests/all/debug/gdb.rs index 3ef3518b21ed..befa0351db05 100644 --- a/tests/all/debug/gdb.rs +++ b/tests/all/debug/gdb.rs @@ -57,8 +57,8 @@ fn check_gdb_output(output: &str, directives: &str) -> Result<()> { pub fn test_debug_dwarf_gdb() -> Result<()> { let output = gdb_with_script( &[ - "--disable-cache", - "-g", + "-Ccache=n", + "-Ddebug-info", "--invoke", "fib", "tests/all/debug/testsuite/fib-wasm.wasm", diff --git a/tests/all/debug/lldb.rs b/tests/all/debug/lldb.rs index c8a9d9bc6f73..14c093b31917 100644 --- a/tests/all/debug/lldb.rs +++ b/tests/all/debug/lldb.rs @@ -66,8 +66,8 @@ fn check_lldb_output(output: &str, directives: &str) -> Result<()> { pub fn test_debug_dwarf_lldb() -> Result<()> { let output = lldb_with_script( &[ - "--disable-cache", - "-g", + "-Ccache=n", + "-Ddebug-info", "--invoke", "fib", "tests/all/debug/testsuite/fib-wasm.wasm", @@ -107,8 +107,8 @@ check: exited with status pub fn test_debug_dwarf5_lldb() -> Result<()> { let output = lldb_with_script( &[ - "--disable-cache", - "-g", + "-Ccache=n", + "-Ddebug-info", "--invoke", "fib", "tests/all/debug/testsuite/fib-wasm-dwarf5.wasm", @@ -148,10 +148,9 @@ check: exited with status pub fn test_debug_dwarf_ref() -> Result<()> { let output = lldb_with_script( &[ - "--disable-cache", - "-g", - "--opt-level", - "0", + "-Ccache=n", + "-Oopt-level=0", + "-Ddebug-info", "tests/all/debug/testsuite/fraction-norm.wasm", ], r#"b fraction-norm.cc:26 @@ -183,10 +182,9 @@ check: resuming pub fn test_debug_inst_offsets_are_correct_when_branches_are_removed() -> Result<()> { let output = lldb_with_script( &[ - "--disable-cache", - "-g", - "--opt-level", - "0", + "-Ccache=n", + "-Oopt-level=0", + "-Ddebug-info", "tests/all/debug/testsuite/two_removed_branches.wasm", ], r#"r"#, @@ -211,10 +209,9 @@ check: exited with status pub fn test_spilled_frame_base_is_accessible() -> Result<()> { let output = lldb_with_script( &[ - "--disable-cache", - "-g", - "--opt-level", - "0", + "-Ccache=n", + "-Oopt-level=0", + "-Ddebug-info", "tests/all/debug/testsuite/spilled_frame_base.wasm", ], r#"b spilled_frame_base.c:8 diff --git a/tests/all/wasi_testsuite.rs b/tests/all/wasi_testsuite.rs index a80a7aadecd7..093a633c9826 100644 --- a/tests/all/wasi_testsuite.rs +++ b/tests/all/wasi_testsuite.rs @@ -38,12 +38,7 @@ fn wasi_testsuite() -> Result<()> { )?; run_all( "tests/wasi_testsuite/wasi-threads", - &[ - "--wasi-modules", - "experimental-wasi-threads", - "--wasm-features", - "threads", - ], + &["-Sthreads", "-Wthreads"], &[], )?; Ok(()) @@ -110,11 +105,11 @@ fn build_command>(module: P, extra_flags: &[&str], spec: &Spec) - .ok_or(anyhow!("module has no parent?"))?; // Add arguments. - cmd.args(["run", "--disable-cache"]); + cmd.args(["run", "-Ccache=n"]); cmd.args(extra_flags); if let Some(dirs) = &spec.dirs { for dir in dirs { - cmd.arg("--mapdir"); + cmd.arg("--dir"); cmd.arg(format!("{}::{}", dir, parent_dir.join(dir).display())); } }