1#![allow(dead_code, unused_imports, unused_variables)]
8
9use std::num::NonZero;
10use std::string::ToString;
11use std::sync::Arc;
12use std::{path::PathBuf, str::FromStr};
13
14use anyhow::{Context, Result, bail};
15#[cfg(feature = "sys")]
16use wasmer::sys::*;
17use wasmer::*;
18use wasmer_types::{Features, target::Target};
19
20#[cfg(feature = "compiler")]
21use wasmer_compiler::CompilerConfig;
22
23use wasmer::Engine;
24
25#[derive(Debug, clap::Parser, Clone, Default)]
26pub struct WasmFeatures {
29 #[clap(long = "enable-simd")]
31 pub simd: bool,
32
33 #[clap(long = "disable-threads")]
35 pub disable_threads: bool,
36
37 #[clap(long = "enable-threads")]
39 pub _threads: bool,
40
41 #[clap(long = "enable-reference-types")]
43 pub reference_types: bool,
44
45 #[clap(long = "enable-multi-value")]
47 pub multi_value: bool,
48
49 #[clap(long = "enable-bulk-memory")]
51 pub bulk_memory: bool,
52
53 #[clap(long = "enable-tail-call")]
55 pub tail_call: bool,
56
57 #[clap(long = "enable-module-linking")]
59 pub module_linking: bool,
60
61 #[clap(long = "enable-multi-memory")]
63 pub multi_memory: bool,
64
65 #[clap(long = "enable-memory64")]
67 pub memory64: bool,
68
69 #[clap(long = "enable-exceptions")]
71 pub exceptions: bool,
72
73 #[clap(long = "enable-relaxed-simd")]
75 pub relaxed_simd: bool,
76
77 #[clap(long = "enable-extended-const")]
79 pub extended_const: bool,
80
81 #[clap(long = "wide-arithmetic")]
83 pub wide_arithmetic: bool,
84
85 #[clap(long = "enable-all")]
87 pub all: bool,
88}
89
90#[derive(Debug, Clone, clap::Parser, Default)]
91pub struct RuntimeOptions {
93 #[cfg(feature = "singlepass")]
95 #[clap(short, long, conflicts_with_all = &Vec::<&str>::from_iter([
96 #[cfg(feature = "llvm")]
97 "llvm",
98 #[cfg(feature = "v8")]
99 "v8",
100 #[cfg(feature = "cranelift")]
101 "cranelift",
102 ]))]
103 singlepass: bool,
104
105 #[cfg(feature = "cranelift")]
107 #[clap(short, long, conflicts_with_all = &Vec::<&str>::from_iter([
108 #[cfg(feature = "llvm")]
109 "llvm",
110 #[cfg(feature = "v8")]
111 "v8",
112 #[cfg(feature = "singlepass")]
113 "singlepass",
114 ]))]
115 cranelift: bool,
116
117 #[cfg(feature = "llvm")]
119 #[clap(short, long, conflicts_with_all = &Vec::<&str>::from_iter([
120 #[cfg(feature = "cranelift")]
121 "cranelift",
122 #[cfg(feature = "v8")]
123 "v8",
124 #[cfg(feature = "singlepass")]
125 "singlepass",
126 ]))]
127 llvm: bool,
128
129 #[cfg(feature = "v8")]
131 #[clap(long, conflicts_with_all = &Vec::<&str>::from_iter([
132 #[cfg(feature = "cranelift")]
133 "cranelift",
134 #[cfg(feature = "llvm")]
135 "llvm",
136 #[cfg(feature = "singlepass")]
137 "singlepass",
138 ]))]
139 v8: bool,
140
141 #[clap(long)]
145 enable_verifier: bool,
146
147 #[clap(long, alias = "llvm-debug-dir")]
151 pub(crate) compiler_debug_dir: Option<PathBuf>,
152
153 #[clap(long, value_enum)]
157 profiler: Option<Profiler>,
158
159 #[cfg(feature = "llvm")]
161 #[clap(long, hide = true)]
162 _enable_pass_params_opt: bool,
163
164 #[clap(long, alias = "llvm-num-threads")]
166 compiler_threads: Option<NonZero<usize>>,
167
168 #[clap(long = "enable-nan-canonicalization")]
171 enable_nan_canonicalization: bool,
172
173 #[cfg(feature = "llvm")]
177 #[clap(long = "disable-non-volatile-memops")]
178 disable_non_volatile_memops: bool,
179
180 #[clap(flatten)]
181 features: WasmFeatures,
182}
183
184#[derive(Clone, Debug)]
185pub enum Profiler {
186 Perfmap,
188}
189
190impl FromStr for Profiler {
191 type Err = anyhow::Error;
192
193 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
194 match s.to_lowercase().as_str() {
195 "perfmap" => Ok(Self::Perfmap),
196 _ => Err(anyhow::anyhow!("Unrecognized profiler: {s}")),
197 }
198 }
199}
200
201impl RuntimeOptions {
202 pub fn get_available_backends(&self) -> Result<Vec<BackendType>> {
203 #[cfg(feature = "cranelift")]
205 {
206 if self.cranelift {
207 return Ok(vec![BackendType::Cranelift]);
208 }
209 }
210
211 #[cfg(feature = "llvm")]
212 {
213 if self.llvm {
214 return Ok(vec![BackendType::LLVM]);
215 }
216 }
217
218 #[cfg(feature = "singlepass")]
219 {
220 if self.singlepass {
221 return Ok(vec![BackendType::Singlepass]);
222 }
223 }
224
225 #[cfg(feature = "v8")]
226 {
227 if self.v8 {
228 return Ok(vec![BackendType::V8]);
229 }
230 }
231
232 Ok(BackendType::enabled())
233 }
234
235 pub fn filter_backends_by_features(
237 backends: Vec<BackendType>,
238 required_features: &Features,
239 target: &Target,
240 ) -> Vec<BackendType> {
241 backends
242 .into_iter()
243 .filter(|backend| backend.supports_features(required_features, target))
244 .collect()
245 }
246
247 pub fn get_store(&self) -> Result<Store> {
248 let engine = self.get_engine(&Target::default())?;
249 Ok(Store::new(engine))
250 }
251
252 pub fn get_engine(&self, target: &Target) -> Result<Engine> {
253 let backends = self.get_available_backends()?;
254 let backend = backends.first().context("no compiler backend enabled")?;
255 backend.get_engine(target, self)
256 }
257
258 pub fn get_engine_for_module(&self, module_contents: &[u8], target: &Target) -> Result<Engine> {
259 let required_features = self
260 .detect_features_from_wasm(module_contents)
261 .unwrap_or_default();
262
263 self.get_engine_for_features(&required_features, target)
264 }
265
266 pub fn get_engine_for_features(
267 &self,
268 required_features: &Features,
269 target: &Target,
270 ) -> Result<Engine> {
271 let backends = self.get_available_backends()?;
272 let filtered_backends =
273 Self::filter_backends_by_features(backends.clone(), required_features, target);
274
275 if filtered_backends.is_empty() {
276 let enabled_backends = BackendType::enabled();
277 if backends.len() == 1 && enabled_backends.len() > 1 {
278 let filtered_backends =
280 Self::filter_backends_by_features(enabled_backends, required_features, target);
281 let extra_text: String = if !filtered_backends.is_empty() {
282 format!(". You can use --{} instead", filtered_backends[0])
283 } else {
284 "".to_string()
285 };
286 bail!(
287 "The {} backend does not support the required features for the Wasm module{}",
288 backends[0],
289 extra_text
290 );
291 } else {
292 bail!(
293 "No backends support the required features for the Wasm module. Feel free to open an issue at https://github.com/wasmerio/wasmer/issues"
294 );
295 }
296 }
297 filtered_backends.first().unwrap().get_engine(target, self)
298 }
299
300 #[cfg(feature = "compiler")]
301 pub fn get_features(&self, default_features: &Features) -> Result<Features> {
303 if self.features.all {
304 return Ok(Features::all());
305 }
306
307 let mut result = default_features.clone();
308 if !self.features.disable_threads {
309 result.threads(true);
310 }
311 if self.features.disable_threads {
312 result.threads(false);
313 }
314 if self.features.multi_value {
315 result.multi_value(true);
316 }
317 if self.features.simd {
318 result.simd(true);
319 }
320 if self.features.bulk_memory {
321 result.bulk_memory(true);
322 }
323 if self.features.reference_types {
324 result.reference_types(true);
325 }
326 Ok(result)
327 }
328
329 #[cfg(feature = "compiler")]
330 pub fn get_configured_features(&self) -> Result<Features> {
332 let features = Features::default();
333 self.get_features(&features)
334 }
335
336 pub fn detect_features_from_wasm(
338 &self,
339 wasm_bytes: &[u8],
340 ) -> Result<Features, wasmparser::BinaryReaderError> {
341 if self.features.all {
342 return Ok(Features::all());
343 }
344
345 let mut features = Features::detect_from_wasm(wasm_bytes)?;
346
347 if !self.features.disable_threads {
349 features.threads(true);
350 }
351 if self.features.reference_types {
352 features.reference_types(true);
353 }
354 if self.features.simd {
355 features.simd(true);
356 }
357 if self.features.bulk_memory {
358 features.bulk_memory(true);
359 }
360 if self.features.multi_value {
361 features.multi_value(true);
362 }
363 if self.features.tail_call {
364 features.tail_call(true);
365 }
366 if self.features.module_linking {
367 features.module_linking(true);
368 }
369 if self.features.multi_memory {
370 features.multi_memory(true);
371 }
372 if self.features.memory64 {
373 features.memory64(true);
374 }
375 if self.features.exceptions {
376 features.exceptions(true);
377 }
378
379 Ok(features)
380 }
381
382 #[cfg(feature = "compiler")]
383 pub fn get_sys_compiler_engine_for_target(
384 &self,
385 target: Target,
386 ) -> std::result::Result<Engine, anyhow::Error> {
387 let backends = self.get_available_backends()?;
388 let compiler_config = self.get_sys_compiler_config(backends.first().unwrap())?;
389 let default_features = compiler_config.default_features_for_target(&target);
390 let features = self.get_features(&default_features)?;
391 Ok(wasmer_compiler::EngineBuilder::new(compiler_config)
392 .set_features(Some(features))
393 .set_target(Some(target))
394 .engine()
395 .into())
396 }
397
398 #[allow(unused_variables)]
399 #[cfg(feature = "compiler")]
400 pub(crate) fn get_sys_compiler_config(
401 &self,
402 rt: &BackendType,
403 ) -> Result<Box<dyn CompilerConfig>> {
404 let compiler_config: Box<dyn CompilerConfig> = match rt {
405 BackendType::Headless => bail!("The headless engine can't be chosen"),
406 #[cfg(feature = "singlepass")]
407 BackendType::Singlepass => {
408 let mut config = wasmer_compiler_singlepass::Singlepass::new();
409 if self.enable_verifier {
410 config.enable_verifier();
411 }
412 if self.enable_nan_canonicalization {
413 config.canonicalize_nans(true);
414 }
415 if let Some(p) = &self.profiler {
416 match p {
417 Profiler::Perfmap => config.enable_perfmap(),
418 }
419 }
420 if let Some(mut debug_dir) = self.compiler_debug_dir.clone() {
421 use wasmer_compiler_singlepass::SinglepassCallbacks;
422
423 debug_dir.push("singlepass");
424 config.callbacks(Some(SinglepassCallbacks::new(debug_dir)?));
425 }
426 if let Some(num_threads) = self.compiler_threads {
427 config.num_threads(num_threads);
428 }
429 Box::new(config)
430 }
431 #[cfg(feature = "cranelift")]
432 BackendType::Cranelift => {
433 let mut config = wasmer_compiler_cranelift::Cranelift::new();
434 if self.enable_verifier {
435 config.enable_verifier();
436 }
437 if self.enable_nan_canonicalization {
438 config.canonicalize_nans(true);
439 }
440 if let Some(p) = &self.profiler {
441 match p {
442 Profiler::Perfmap => config.enable_perfmap(),
443 }
444 }
445 if let Some(mut debug_dir) = self.compiler_debug_dir.clone() {
446 use wasmer_compiler_cranelift::CraneliftCallbacks;
447
448 debug_dir.push("cranelift");
449 config.callbacks(Some(CraneliftCallbacks::new(debug_dir)?));
450 }
451 if let Some(num_threads) = self.compiler_threads {
452 config.num_threads(num_threads);
453 }
454 Box::new(config)
455 }
456 #[cfg(feature = "llvm")]
457 BackendType::LLVM => {
458 use wasmer_compiler_llvm::LLVMCallbacks;
459 use wasmer_types::entity::EntityRef;
460 let mut config = LLVM::new();
461 if !self.disable_non_volatile_memops {
462 config.enable_non_volatile_memops();
463 }
464 config.enable_readonly_funcref_table();
465
466 if let Some(num_threads) = self.compiler_threads {
467 config.num_threads(num_threads);
468 }
469
470 if let Some(mut debug_dir) = self.compiler_debug_dir.clone() {
471 debug_dir.push("llvm");
472 config.callbacks(Some(LLVMCallbacks::new(debug_dir)?));
473 config.verbose_asm(true);
474 }
475 if self.enable_verifier {
476 config.enable_verifier();
477 }
478 if self.enable_nan_canonicalization {
479 config.canonicalize_nans(true);
480 }
481 if let Some(p) = &self.profiler {
482 match p {
483 Profiler::Perfmap => config.enable_perfmap(),
484 }
485 }
486
487 Box::new(config)
488 }
489 BackendType::V8 => unreachable!(),
490 #[cfg(not(all(feature = "singlepass", feature = "cranelift", feature = "llvm")))]
491 compiler => {
492 bail!("The `{compiler}` compiler is not included in this binary.")
493 }
494 };
495
496 #[allow(unreachable_code)]
497 Ok(compiler_config)
498 }
499}
500
501#[derive(Debug, PartialEq, Eq, Clone, Copy)]
503#[allow(clippy::upper_case_acronyms, dead_code)]
504pub enum BackendType {
505 Singlepass,
507
508 Cranelift,
510
511 LLVM,
513
514 V8,
516
517 #[allow(dead_code)]
519 Headless,
520}
521
522impl BackendType {
523 pub fn enabled() -> Vec<Self> {
525 vec![
526 #[cfg(feature = "cranelift")]
527 Self::Cranelift,
528 #[cfg(feature = "llvm")]
529 Self::LLVM,
530 #[cfg(feature = "singlepass")]
531 Self::Singlepass,
532 #[cfg(feature = "v8")]
533 Self::V8,
534 ]
535 }
536
537 pub fn get_engine(&self, target: &Target, runtime_opts: &RuntimeOptions) -> Result<Engine> {
541 match self {
542 #[cfg(feature = "singlepass")]
543 Self::Singlepass => {
544 let mut config = wasmer_compiler_singlepass::Singlepass::new();
545 let supported_features = config.supported_features_for_target(target);
546 if runtime_opts.enable_verifier {
547 config.enable_verifier();
548 }
549 if runtime_opts.enable_nan_canonicalization {
550 config.canonicalize_nans(true);
551 }
552 if let Some(p) = &runtime_opts.profiler {
553 match p {
554 Profiler::Perfmap => config.enable_perfmap(),
555 }
556 }
557 if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
558 use wasmer_compiler_singlepass::SinglepassCallbacks;
559
560 debug_dir.push("singlepass");
561 config.callbacks(Some(SinglepassCallbacks::new(debug_dir)?));
562 }
563 if let Some(num_threads) = runtime_opts.compiler_threads {
564 config.num_threads(num_threads);
565 }
566 let engine = wasmer_compiler::EngineBuilder::new(config)
567 .set_features(Some(supported_features))
568 .set_target(Some(target.clone()))
569 .engine()
570 .into();
571 Ok(engine)
572 }
573 #[cfg(feature = "cranelift")]
574 Self::Cranelift => {
575 let mut config = wasmer_compiler_cranelift::Cranelift::new();
576 let supported_features = config.supported_features_for_target(target);
577 if runtime_opts.enable_verifier {
578 config.enable_verifier();
579 }
580 if runtime_opts.enable_nan_canonicalization {
581 config.canonicalize_nans(true);
582 }
583 if let Some(p) = &runtime_opts.profiler {
584 match p {
585 Profiler::Perfmap => config.enable_perfmap(),
586 }
587 }
588 if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
589 use wasmer_compiler_cranelift::CraneliftCallbacks;
590
591 debug_dir.push("cranelift");
592 config.callbacks(Some(CraneliftCallbacks::new(debug_dir)?));
593 }
594 if let Some(num_threads) = runtime_opts.compiler_threads {
595 config.num_threads(num_threads);
596 }
597 let engine = wasmer_compiler::EngineBuilder::new(config)
598 .set_features(Some(supported_features))
599 .set_target(Some(target.clone()))
600 .engine()
601 .into();
602 Ok(engine)
603 }
604 #[cfg(feature = "llvm")]
605 Self::LLVM => {
606 use wasmer_compiler_llvm::LLVMCallbacks;
607 use wasmer_types::entity::EntityRef;
608
609 let mut config = wasmer_compiler_llvm::LLVM::new();
610 if !runtime_opts.disable_non_volatile_memops {
611 config.enable_non_volatile_memops();
612 }
613 config.enable_readonly_funcref_table();
614
615 let supported_features = config.supported_features_for_target(target);
616 if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
617 debug_dir.push("llvm");
618 config.callbacks(Some(LLVMCallbacks::new(debug_dir)?));
619 config.verbose_asm(true);
620 }
621 if runtime_opts.enable_verifier {
622 config.enable_verifier();
623 }
624 if runtime_opts.enable_nan_canonicalization {
625 config.canonicalize_nans(true);
626 }
627
628 if let Some(num_threads) = runtime_opts.compiler_threads {
629 config.num_threads(num_threads);
630 }
631
632 if let Some(p) = &runtime_opts.profiler {
633 match p {
634 Profiler::Perfmap => config.enable_perfmap(),
635 }
636 }
637
638 let engine = wasmer_compiler::EngineBuilder::new(config)
639 .set_features(Some(supported_features))
640 .set_target(Some(target.clone()))
641 .engine()
642 .into();
643 Ok(engine)
644 }
645 #[cfg(feature = "v8")]
646 Self::V8 => Ok(wasmer::v8::V8::new().into()),
647 Self::Headless => bail!("Headless is not a valid runtime to instantiate directly"),
648 #[allow(unreachable_patterns)]
649 _ => bail!("Unsupported backend type"),
650 }
651 }
652
653 #[allow(unreachable_code)]
655 pub fn supports_features(&self, required_features: &Features, target: &Target) -> bool {
656 let backend_kind = match self {
658 #[cfg(feature = "singlepass")]
659 Self::Singlepass => wasmer::BackendKind::Singlepass,
660 #[cfg(feature = "cranelift")]
661 Self::Cranelift => wasmer::BackendKind::Cranelift,
662 #[cfg(feature = "llvm")]
663 Self::LLVM => wasmer::BackendKind::LLVM,
664 #[cfg(feature = "v8")]
665 Self::V8 => wasmer::BackendKind::V8,
666 Self::Headless => return false, #[allow(unreachable_patterns)]
668 _ => return false,
669 };
670
671 let supported = wasmer::Engine::supported_features_for_backend(&backend_kind, target);
673
674 if !supported.contains_features(required_features) {
676 return false;
677 }
678
679 true
680 }
681}
682
683impl From<&BackendType> for wasmer::BackendKind {
684 fn from(backend_type: &BackendType) -> Self {
685 match backend_type {
686 #[cfg(feature = "singlepass")]
687 BackendType::Singlepass => wasmer::BackendKind::Singlepass,
688 #[cfg(feature = "cranelift")]
689 BackendType::Cranelift => wasmer::BackendKind::Cranelift,
690 #[cfg(feature = "llvm")]
691 BackendType::LLVM => wasmer::BackendKind::LLVM,
692 #[cfg(feature = "v8")]
693 BackendType::V8 => wasmer::BackendKind::V8,
694 _ => {
695 #[cfg(feature = "sys")]
696 {
697 wasmer::BackendKind::Headless
698 }
699 #[cfg(not(feature = "sys"))]
700 {
701 unreachable!("No backend enabled!")
702 }
703 }
704 }
705 }
706}
707
708impl std::fmt::Display for BackendType {
709 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
710 write!(
711 f,
712 "{}",
713 match self {
714 Self::Singlepass => "singlepass",
715 Self::Cranelift => "cranelift",
716 Self::LLVM => "llvm",
717 Self::V8 => "v8",
718 Self::Headless => "headless",
719 }
720 )
721 }
722}