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::option_map_unwrap_or, 21 clippy::option_map_unwrap_or_else, 22 clippy::unicode_not_nfc, 23 clippy::use_self 24 ) 25 )] 26 27 pub mod commands; 28 mod obj; 29 30 use anyhow::{bail, Result}; 31 use std::path::PathBuf; 32 use structopt::StructOpt; 33 use wasmtime::{Config, ProfilingStrategy, Strategy}; 34 35 pub use obj::compile_to_obj; 36 37 fn pick_compilation_strategy(cranelift: bool, lightbeam: bool) -> Result<Strategy> { 38 Ok(match (lightbeam, cranelift) { 39 (true, false) => Strategy::Lightbeam, 40 (false, true) => Strategy::Cranelift, 41 (false, false) => Strategy::Auto, 42 (true, true) => bail!("Can't enable --cranelift and --lightbeam at the same time"), 43 }) 44 } 45 46 fn pick_profiling_strategy(jitdump: bool, vtune: bool) -> Result<ProfilingStrategy> { 47 Ok(match (jitdump, vtune) { 48 (true, false) => ProfilingStrategy::JitDump, 49 (false, true) => ProfilingStrategy::VTune, 50 (true, true) => { 51 println!("Can't enable --jitdump and --vtune at the same time. Profiling not enabled."); 52 ProfilingStrategy::None 53 } 54 _ => ProfilingStrategy::None, 55 }) 56 } 57 58 fn init_file_per_thread_logger(prefix: &'static str) { 59 file_per_thread_logger::initialize(prefix); 60 61 // Extending behavior of default spawner: 62 // https://docs.rs/rayon/1.1.0/rayon/struct.ThreadPoolBuilder.html#method.spawn_handler 63 // Source code says DefaultSpawner is implementation detail and 64 // shouldn't be used directly. 65 rayon::ThreadPoolBuilder::new() 66 .spawn_handler(move |thread| { 67 let mut b = std::thread::Builder::new(); 68 if let Some(name) = thread.name() { 69 b = b.name(name.to_owned()); 70 } 71 if let Some(stack_size) = thread.stack_size() { 72 b = b.stack_size(stack_size); 73 } 74 b.spawn(move || { 75 file_per_thread_logger::initialize(prefix); 76 thread.run() 77 })?; 78 Ok(()) 79 }) 80 .build_global() 81 .unwrap(); 82 } 83 84 /// Common options for commands that translate WebAssembly modules 85 #[derive(StructOpt)] 86 struct CommonOptions { 87 /// Use specified configuration file 88 #[structopt(long, parse(from_os_str), value_name = "CONFIG_PATH")] 89 config: Option<PathBuf>, 90 91 /// Use Cranelift for all compilation 92 #[structopt(long, conflicts_with = "lightbeam")] 93 cranelift: bool, 94 95 /// Log to per-thread log files instead of stderr. 96 #[structopt(long)] 97 log_to_files: bool, 98 99 /// Generate debug information 100 #[structopt(short = "g")] 101 debug_info: bool, 102 103 /// Disable cache system 104 #[structopt(long)] 105 disable_cache: bool, 106 107 /// Enable support for proposed SIMD instructions 108 #[structopt(long)] 109 enable_simd: bool, 110 111 /// Enable support for reference types 112 #[structopt(long)] 113 enable_reference_types: bool, 114 115 /// Enable support for multi-value functions 116 #[structopt(long)] 117 enable_multi_value: bool, 118 119 /// Enable support for Wasm threads 120 #[structopt(long)] 121 enable_threads: bool, 122 123 /// Enable support for bulk memory instructions 124 #[structopt(long)] 125 enable_bulk_memory: bool, 126 127 /// Enable all experimental Wasm features 128 #[structopt(long)] 129 enable_all: bool, 130 131 /// Use Lightbeam for all compilation 132 #[structopt(long, conflicts_with = "cranelift")] 133 lightbeam: bool, 134 135 /// Generate jitdump file (supported on --features=profiling build) 136 #[structopt(long, conflicts_with = "vtune")] 137 jitdump: bool, 138 139 /// Generate vtune (supported on --features=vtune build) 140 #[structopt(long, conflicts_with = "jitdump")] 141 vtune: bool, 142 143 /// Run optimization passes on translated functions, on by default 144 #[structopt(short = "O", long)] 145 optimize: bool, 146 147 /// Optimization level for generated functions (0 (none), 1, 2 (most), or s 148 /// (size)) 149 #[structopt( 150 long, 151 parse(try_from_str = parse_opt_level), 152 default_value = "2", 153 )] 154 opt_level: wasmtime::OptLevel, 155 } 156 157 impl CommonOptions { 158 fn config(&self) -> Result<Config> { 159 let mut config = Config::new(); 160 config 161 .cranelift_debug_verifier(cfg!(debug_assertions)) 162 .debug_info(self.debug_info) 163 .wasm_bulk_memory(self.enable_bulk_memory || self.enable_all) 164 .wasm_simd(self.enable_simd || self.enable_all) 165 .wasm_reference_types(self.enable_reference_types || self.enable_all) 166 .wasm_multi_value(self.enable_multi_value || self.enable_all) 167 .wasm_threads(self.enable_threads || self.enable_all) 168 .cranelift_opt_level(self.opt_level()) 169 .strategy(pick_compilation_strategy(self.cranelift, self.lightbeam)?)? 170 .profiler(pick_profiling_strategy(self.jitdump, self.vtune)?)?; 171 if !self.disable_cache { 172 match &self.config { 173 Some(path) => { 174 config.cache_config_load(path)?; 175 } 176 None => { 177 config.cache_config_load_default()?; 178 } 179 } 180 } 181 Ok(config) 182 } 183 184 fn opt_level(&self) -> wasmtime::OptLevel { 185 match (self.optimize, self.opt_level.clone()) { 186 (true, _) => wasmtime::OptLevel::Speed, 187 (false, other) => other, 188 } 189 } 190 } 191 192 fn parse_opt_level(opt_level: &str) -> Result<wasmtime::OptLevel> { 193 match opt_level { 194 "s" => Ok(wasmtime::OptLevel::SpeedAndSize), 195 "0" => Ok(wasmtime::OptLevel::None), 196 "1" => Ok(wasmtime::OptLevel::Speed), 197 "2" => Ok(wasmtime::OptLevel::Speed), 198 other => bail!( 199 "unknown optimization level `{}`, only 0,1,2,s accepted", 200 other 201 ), 202 } 203 } 204