xref: /wasmtime-44.0.1/src/lib.rs (revision 3f694ae3)
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