1 //! The Wasmtime command line interface (CLI) crate. 2 //! 3 //! This crate implements the Wasmtime command line tools. 4 5 #![deny( 6 missing_docs, 7 trivial_numeric_casts, 8 unused_extern_crates, 9 unstable_features 10 )] 11 #![warn(unused_import_braces)] 12 #![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../clippy.toml")))] 13 #![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))] 14 #![cfg_attr( 15 feature = "cargo-clippy", 16 warn( 17 clippy::float_arithmetic, 18 clippy::mut_mut, 19 clippy::nonminimal_bool, 20 clippy::map_unwrap_or, 21 clippy::unicode_not_nfc, 22 clippy::use_self 23 ) 24 )] 25 26 pub mod commands; 27 mod obj; 28 29 use anyhow::{bail, Result}; 30 use std::path::PathBuf; 31 use structopt::StructOpt; 32 use wasmtime::{Config, ProfilingStrategy, Strategy}; 33 34 pub use obj::compile_to_obj; 35 36 fn pick_compilation_strategy(cranelift: bool, lightbeam: bool) -> Result<Strategy> { 37 Ok(match (lightbeam, cranelift) { 38 (true, false) => Strategy::Lightbeam, 39 (false, true) => Strategy::Cranelift, 40 (false, false) => Strategy::Auto, 41 (true, true) => bail!("Can't enable --cranelift and --lightbeam at the same time"), 42 }) 43 } 44 45 fn pick_profiling_strategy(jitdump: bool, vtune: bool) -> Result<ProfilingStrategy> { 46 Ok(match (jitdump, vtune) { 47 (true, false) => ProfilingStrategy::JitDump, 48 (false, true) => ProfilingStrategy::VTune, 49 (true, true) => { 50 println!("Can't enable --jitdump and --vtune at the same time. Profiling not enabled."); 51 ProfilingStrategy::None 52 } 53 _ => ProfilingStrategy::None, 54 }) 55 } 56 57 fn init_file_per_thread_logger(prefix: &'static str) { 58 file_per_thread_logger::initialize(prefix); 59 60 // Extending behavior of default spawner: 61 // https://docs.rs/rayon/1.1.0/rayon/struct.ThreadPoolBuilder.html#method.spawn_handler 62 // Source code says DefaultSpawner is implementation detail and 63 // shouldn't be used directly. 64 rayon::ThreadPoolBuilder::new() 65 .spawn_handler(move |thread| { 66 let mut b = std::thread::Builder::new(); 67 if let Some(name) = thread.name() { 68 b = b.name(name.to_owned()); 69 } 70 if let Some(stack_size) = thread.stack_size() { 71 b = b.stack_size(stack_size); 72 } 73 b.spawn(move || { 74 file_per_thread_logger::initialize(prefix); 75 thread.run() 76 })?; 77 Ok(()) 78 }) 79 .build_global() 80 .unwrap(); 81 } 82 83 /// Common options for commands that translate WebAssembly modules 84 #[derive(StructOpt)] 85 struct CommonOptions { 86 /// Use specified configuration file 87 #[structopt(long, parse(from_os_str), value_name = "CONFIG_PATH")] 88 config: Option<PathBuf>, 89 90 /// Use Cranelift for all compilation 91 #[structopt(long, conflicts_with = "lightbeam")] 92 cranelift: bool, 93 94 /// Log to per-thread log files instead of stderr. 95 #[structopt(long)] 96 log_to_files: bool, 97 98 /// Generate debug information 99 #[structopt(short = "g")] 100 debug_info: bool, 101 102 /// Disable cache system 103 #[structopt(long)] 104 disable_cache: bool, 105 106 /// Enable support for proposed SIMD instructions 107 #[structopt(long)] 108 enable_simd: bool, 109 110 /// Enable support for reference types 111 #[structopt(long)] 112 enable_reference_types: Option<bool>, 113 114 /// Enable support for multi-value functions 115 #[structopt(long)] 116 enable_multi_value: Option<bool>, 117 118 /// Enable support for Wasm threads 119 #[structopt(long)] 120 enable_threads: bool, 121 122 /// Enable support for bulk memory instructions 123 #[structopt(long)] 124 enable_bulk_memory: Option<bool>, 125 126 /// Enable support for the multi-memory proposal 127 #[structopt(long)] 128 enable_multi_memory: bool, 129 130 /// Enable support for the module-linking proposal 131 #[structopt(long)] 132 enable_module_linking: bool, 133 134 /// Enable all experimental Wasm features 135 #[structopt(long)] 136 enable_all: bool, 137 138 /// Use Lightbeam for all compilation 139 #[structopt(long, conflicts_with = "cranelift")] 140 lightbeam: bool, 141 142 /// Generate jitdump file (supported on --features=profiling build) 143 #[structopt(long, conflicts_with = "vtune")] 144 jitdump: bool, 145 146 /// Generate vtune (supported on --features=vtune build) 147 #[structopt(long, conflicts_with = "jitdump")] 148 vtune: bool, 149 150 /// Run optimization passes on translated functions, on by default 151 #[structopt(short = "O", long)] 152 optimize: bool, 153 154 /// Optimization level for generated functions (0 (none), 1, 2 (most), or s 155 /// (size)) 156 #[structopt( 157 long, 158 parse(try_from_str = parse_opt_level), 159 default_value = "2", 160 )] 161 opt_level: wasmtime::OptLevel, 162 163 /// Other Cranelift flags to be passed down to Cranelift. 164 #[structopt(long, parse(try_from_str = parse_cranelift_flag))] 165 cranelift_flags: Vec<CraneliftFlag>, 166 167 /// Maximum size in bytes of wasm memory before it becomes dynamically 168 /// relocatable instead of up-front-reserved. 169 #[structopt(long)] 170 static_memory_maximum_size: Option<u64>, 171 172 /// Byte size of the guard region after static memories are allocated. 173 #[structopt(long)] 174 static_memory_guard_size: Option<u64>, 175 176 /// Byte size of the guard region after dynamic memories are allocated. 177 #[structopt(long)] 178 dynamic_memory_guard_size: Option<u64>, 179 180 /// Enable Cranelift's internal debug verifier (expensive) 181 #[structopt(long)] 182 enable_cranelift_debug_verifier: bool, 183 184 /// Enable Cranelift's internal NaN canonicalization 185 #[structopt(long)] 186 enable_cranelift_nan_canonicalization: bool, 187 } 188 189 impl CommonOptions { 190 fn config(&self) -> Result<Config> { 191 let mut config = Config::new(); 192 config 193 .cranelift_debug_verifier(self.enable_cranelift_debug_verifier) 194 .debug_info(self.debug_info) 195 .wasm_simd(self.enable_simd || self.enable_all) 196 .wasm_bulk_memory(self.enable_bulk_memory.unwrap_or(true) || self.enable_all) 197 .wasm_reference_types( 198 self.enable_reference_types 199 .unwrap_or(cfg!(target_arch = "x86_64")) 200 || self.enable_all, 201 ) 202 .wasm_multi_value(self.enable_multi_value.unwrap_or(true) || self.enable_all) 203 .wasm_threads(self.enable_threads || self.enable_all) 204 .wasm_multi_memory(self.enable_multi_memory || self.enable_all) 205 .wasm_module_linking(self.enable_module_linking || self.enable_all) 206 .cranelift_opt_level(self.opt_level()) 207 .strategy(pick_compilation_strategy(self.cranelift, self.lightbeam)?)? 208 .profiler(pick_profiling_strategy(self.jitdump, self.vtune)?)? 209 .cranelift_nan_canonicalization(self.enable_cranelift_nan_canonicalization); 210 for CraneliftFlag { name, value } in &self.cranelift_flags { 211 unsafe { 212 config.cranelift_other_flag(name, value)?; 213 } 214 } 215 if !self.disable_cache { 216 match &self.config { 217 Some(path) => { 218 config.cache_config_load(path)?; 219 } 220 None => { 221 config.cache_config_load_default()?; 222 } 223 } 224 } 225 if let Some(max) = self.static_memory_maximum_size { 226 config.static_memory_maximum_size(max); 227 } 228 if let Some(size) = self.static_memory_guard_size { 229 config.static_memory_guard_size(size); 230 } 231 if let Some(size) = self.dynamic_memory_guard_size { 232 config.dynamic_memory_guard_size(size); 233 } 234 Ok(config) 235 } 236 237 fn opt_level(&self) -> wasmtime::OptLevel { 238 match (self.optimize, self.opt_level.clone()) { 239 (true, _) => wasmtime::OptLevel::Speed, 240 (false, other) => other, 241 } 242 } 243 } 244 245 fn parse_opt_level(opt_level: &str) -> Result<wasmtime::OptLevel> { 246 match opt_level { 247 "s" => Ok(wasmtime::OptLevel::SpeedAndSize), 248 "0" => Ok(wasmtime::OptLevel::None), 249 "1" => Ok(wasmtime::OptLevel::Speed), 250 "2" => Ok(wasmtime::OptLevel::Speed), 251 other => bail!( 252 "unknown optimization level `{}`, only 0,1,2,s accepted", 253 other 254 ), 255 } 256 } 257 258 struct CraneliftFlag { 259 name: String, 260 value: String, 261 } 262 263 fn parse_cranelift_flag(name_and_value: &str) -> Result<CraneliftFlag> { 264 let mut split = name_and_value.splitn(2, '='); 265 let name = if let Some(name) = split.next() { 266 name.to_string() 267 } else { 268 bail!("missing name in cranelift flag"); 269 }; 270 let value = if let Some(value) = split.next() { 271 value.to_string() 272 } else { 273 bail!("missing value in cranelift flag"); 274 }; 275 Ok(CraneliftFlag { name, value }) 276 } 277