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 #[cfg(feature = "wamr")]
103 "wamr",
104 #[cfg(feature = "wasmi")]
105 "wasmi"
106 ]))]
107 singlepass: bool,
108
109 #[cfg(feature = "cranelift")]
111 #[clap(short, long, conflicts_with_all = &Vec::<&str>::from_iter([
112 #[cfg(feature = "llvm")]
113 "llvm",
114 #[cfg(feature = "v8")]
115 "v8",
116 #[cfg(feature = "singlepass")]
117 "singlepass",
118 #[cfg(feature = "wamr")]
119 "wamr",
120 #[cfg(feature = "wasmi")]
121 "wasmi"
122 ]))]
123 cranelift: bool,
124
125 #[cfg(feature = "llvm")]
127 #[clap(short, long, conflicts_with_all = &Vec::<&str>::from_iter([
128 #[cfg(feature = "cranelift")]
129 "cranelift",
130 #[cfg(feature = "v8")]
131 "v8",
132 #[cfg(feature = "singlepass")]
133 "singlepass",
134 #[cfg(feature = "wamr")]
135 "wamr",
136 #[cfg(feature = "wasmi")]
137 "wasmi"
138 ]))]
139 llvm: bool,
140
141 #[cfg(feature = "v8")]
143 #[clap(long, conflicts_with_all = &Vec::<&str>::from_iter([
144 #[cfg(feature = "cranelift")]
145 "cranelift",
146 #[cfg(feature = "llvm")]
147 "llvm",
148 #[cfg(feature = "singlepass")]
149 "singlepass",
150 #[cfg(feature = "wamr")]
151 "wamr",
152 #[cfg(feature = "wasmi")]
153 "wasmi"
154 ]))]
155 v8: bool,
156
157 #[cfg(feature = "wamr")]
159 #[clap(long, conflicts_with_all = &Vec::<&str>::from_iter([
160 #[cfg(feature = "cranelift")]
161 "cranelift",
162 #[cfg(feature = "llvm")]
163 "llvm",
164 #[cfg(feature = "singlepass")]
165 "singlepass",
166 #[cfg(feature = "v8")]
167 "v8",
168 #[cfg(feature = "wasmi")]
169 "wasmi"
170 ]))]
171 wamr: bool,
172
173 #[cfg(feature = "wasmi")]
175 #[clap(long, conflicts_with_all = &Vec::<&str>::from_iter([
176 #[cfg(feature = "cranelift")]
177 "cranelift",
178 #[cfg(feature = "llvm")]
179 "llvm",
180 #[cfg(feature = "singlepass")]
181 "singlepass",
182 #[cfg(feature = "v8")]
183 "v8",
184 #[cfg(feature = "wamr")]
185 "wamr"
186 ]))]
187 wasmi: bool,
188
189 #[clap(long)]
193 enable_verifier: bool,
194
195 #[clap(long, alias = "llvm-debug-dir")]
199 pub(crate) compiler_debug_dir: Option<PathBuf>,
200
201 #[clap(long, value_enum)]
205 profiler: Option<Profiler>,
206
207 #[cfg(feature = "llvm")]
209 #[clap(long, hide = true)]
210 _enable_pass_params_opt: bool,
211
212 #[clap(long, alias = "llvm-num-threads")]
214 compiler_threads: Option<NonZero<usize>>,
215
216 #[clap(long = "enable-nan-canonicalization")]
219 enable_nan_canonicalization: bool,
220
221 #[clap(flatten)]
222 features: WasmFeatures,
223}
224
225#[derive(Clone, Debug)]
226pub enum Profiler {
227 Perfmap,
229}
230
231impl FromStr for Profiler {
232 type Err = anyhow::Error;
233
234 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
235 match s.to_lowercase().as_str() {
236 "perfmap" => Ok(Self::Perfmap),
237 _ => Err(anyhow::anyhow!("Unrecognized profiler: {s}")),
238 }
239 }
240}
241
242impl RuntimeOptions {
243 pub fn get_available_backends(&self) -> Result<Vec<BackendType>> {
244 #[cfg(feature = "cranelift")]
246 {
247 if self.cranelift {
248 return Ok(vec![BackendType::Cranelift]);
249 }
250 }
251
252 #[cfg(feature = "llvm")]
253 {
254 if self.llvm {
255 return Ok(vec![BackendType::LLVM]);
256 }
257 }
258
259 #[cfg(feature = "singlepass")]
260 {
261 if self.singlepass {
262 return Ok(vec![BackendType::Singlepass]);
263 }
264 }
265
266 #[cfg(feature = "wamr")]
267 {
268 if self.wamr {
269 return Ok(vec![BackendType::Wamr]);
270 }
271 }
272
273 #[cfg(feature = "v8")]
274 {
275 if self.v8 {
276 return Ok(vec![BackendType::V8]);
277 }
278 }
279
280 #[cfg(feature = "wasmi")]
281 {
282 if self.wasmi {
283 return Ok(vec![BackendType::Wasmi]);
284 }
285 }
286
287 Ok(BackendType::enabled())
288 }
289
290 pub fn filter_backends_by_features(
292 backends: Vec<BackendType>,
293 required_features: &Features,
294 target: &Target,
295 ) -> Vec<BackendType> {
296 backends
297 .into_iter()
298 .filter(|backend| backend.supports_features(required_features, target))
299 .collect()
300 }
301
302 pub fn get_store(&self) -> Result<Store> {
303 let engine = self.get_engine(&Target::default())?;
304 Ok(Store::new(engine))
305 }
306
307 pub fn get_engine(&self, target: &Target) -> Result<Engine> {
308 let backends = self.get_available_backends()?;
309 let backend = backends.first().context("no compiler backend enabled")?;
310 backend.get_engine(target, self)
311 }
312
313 pub fn get_engine_for_module(&self, module_contents: &[u8], target: &Target) -> Result<Engine> {
314 let required_features = self
315 .detect_features_from_wasm(module_contents)
316 .unwrap_or_default();
317
318 self.get_engine_for_features(&required_features, target)
319 }
320
321 pub fn get_engine_for_features(
322 &self,
323 required_features: &Features,
324 target: &Target,
325 ) -> Result<Engine> {
326 let backends = self.get_available_backends()?;
327 let filtered_backends =
328 Self::filter_backends_by_features(backends.clone(), required_features, target);
329
330 if filtered_backends.is_empty() {
331 let enabled_backends = BackendType::enabled();
332 if backends.len() == 1 && enabled_backends.len() > 1 {
333 let filtered_backends =
335 Self::filter_backends_by_features(enabled_backends, required_features, target);
336 let extra_text: String = if !filtered_backends.is_empty() {
337 format!(". You can use --{} instead", filtered_backends[0])
338 } else {
339 "".to_string()
340 };
341 bail!(
342 "The {} backend does not support the required features for the Wasm module{}",
343 backends[0],
344 extra_text
345 );
346 } else {
347 bail!(
348 "No backends support the required features for the Wasm module. Feel free to open an issue at https://github.com/wasmerio/wasmer/issues"
349 );
350 }
351 }
352 filtered_backends.first().unwrap().get_engine(target, self)
353 }
354
355 #[cfg(feature = "compiler")]
356 pub fn get_features(&self, default_features: &Features) -> Result<Features> {
358 if self.features.all {
359 return Ok(Features::all());
360 }
361
362 let mut result = default_features.clone();
363 if !self.features.disable_threads {
364 result.threads(true);
365 }
366 if self.features.disable_threads {
367 result.threads(false);
368 }
369 if self.features.multi_value {
370 result.multi_value(true);
371 }
372 if self.features.simd {
373 result.simd(true);
374 }
375 if self.features.bulk_memory {
376 result.bulk_memory(true);
377 }
378 if self.features.reference_types {
379 result.reference_types(true);
380 }
381 Ok(result)
382 }
383
384 #[cfg(feature = "compiler")]
385 pub fn get_configured_features(&self) -> Result<Features> {
387 let features = Features::default();
388 self.get_features(&features)
389 }
390
391 pub fn detect_features_from_wasm(
393 &self,
394 wasm_bytes: &[u8],
395 ) -> Result<Features, wasmparser::BinaryReaderError> {
396 if self.features.all {
397 return Ok(Features::all());
398 }
399
400 let mut features = Features::detect_from_wasm(wasm_bytes)?;
401
402 if !self.features.disable_threads {
404 features.threads(true);
405 }
406 if self.features.reference_types {
407 features.reference_types(true);
408 }
409 if self.features.simd {
410 features.simd(true);
411 }
412 if self.features.bulk_memory {
413 features.bulk_memory(true);
414 }
415 if self.features.multi_value {
416 features.multi_value(true);
417 }
418 if self.features.tail_call {
419 features.tail_call(true);
420 }
421 if self.features.module_linking {
422 features.module_linking(true);
423 }
424 if self.features.multi_memory {
425 features.multi_memory(true);
426 }
427 if self.features.memory64 {
428 features.memory64(true);
429 }
430 if self.features.exceptions {
431 features.exceptions(true);
432 }
433
434 Ok(features)
435 }
436
437 #[cfg(feature = "compiler")]
438 pub fn get_sys_compiler_engine_for_target(
439 &self,
440 target: Target,
441 ) -> std::result::Result<Engine, anyhow::Error> {
442 let backends = self.get_available_backends()?;
443 let compiler_config = self.get_sys_compiler_config(backends.first().unwrap())?;
444 let default_features = compiler_config.default_features_for_target(&target);
445 let features = self.get_features(&default_features)?;
446 Ok(wasmer_compiler::EngineBuilder::new(compiler_config)
447 .set_features(Some(features))
448 .set_target(Some(target))
449 .engine()
450 .into())
451 }
452
453 #[allow(unused_variables)]
454 #[cfg(feature = "compiler")]
455 pub(crate) fn get_sys_compiler_config(
456 &self,
457 rt: &BackendType,
458 ) -> Result<Box<dyn CompilerConfig>> {
459 let compiler_config: Box<dyn CompilerConfig> = match rt {
460 BackendType::Headless => bail!("The headless engine can't be chosen"),
461 #[cfg(feature = "singlepass")]
462 BackendType::Singlepass => {
463 let mut config = wasmer_compiler_singlepass::Singlepass::new();
464 if self.enable_verifier {
465 config.enable_verifier();
466 }
467 if self.enable_nan_canonicalization {
468 config.canonicalize_nans(true);
469 }
470 if let Some(p) = &self.profiler {
471 match p {
472 Profiler::Perfmap => config.enable_perfmap(),
473 }
474 }
475 if let Some(mut debug_dir) = self.compiler_debug_dir.clone() {
476 use wasmer_compiler_singlepass::SinglepassCallbacks;
477
478 debug_dir.push("singlepass");
479 config.callbacks(Some(SinglepassCallbacks::new(debug_dir)?));
480 }
481 if let Some(num_threads) = self.compiler_threads {
482 config.num_threads(num_threads);
483 }
484 Box::new(config)
485 }
486 #[cfg(feature = "cranelift")]
487 BackendType::Cranelift => {
488 let mut config = wasmer_compiler_cranelift::Cranelift::new();
489 if self.enable_verifier {
490 config.enable_verifier();
491 }
492 if self.enable_nan_canonicalization {
493 config.canonicalize_nans(true);
494 }
495 if let Some(p) = &self.profiler {
496 match p {
497 Profiler::Perfmap => config.enable_perfmap(),
498 }
499 }
500 if let Some(mut debug_dir) = self.compiler_debug_dir.clone() {
501 use wasmer_compiler_cranelift::CraneliftCallbacks;
502
503 debug_dir.push("cranelift");
504 config.callbacks(Some(CraneliftCallbacks::new(debug_dir)?));
505 }
506 if let Some(num_threads) = self.compiler_threads {
507 config.num_threads(num_threads);
508 }
509 Box::new(config)
510 }
511 #[cfg(feature = "llvm")]
512 BackendType::LLVM => {
513 use wasmer_compiler_llvm::LLVMCallbacks;
514 use wasmer_types::entity::EntityRef;
515 let mut config = LLVM::new();
516 config.enable_non_volatile_memops();
517 config.enable_readonly_funcref_table();
518
519 if let Some(num_threads) = self.compiler_threads {
520 config.num_threads(num_threads);
521 }
522
523 if let Some(mut debug_dir) = self.compiler_debug_dir.clone() {
524 debug_dir.push("llvm");
525 config.callbacks(Some(LLVMCallbacks::new(debug_dir)?));
526 config.verbose_asm(true);
527 }
528 if self.enable_verifier {
529 config.enable_verifier();
530 }
531 if self.enable_nan_canonicalization {
532 config.canonicalize_nans(true);
533 }
534 if let Some(p) = &self.profiler {
535 match p {
536 Profiler::Perfmap => config.enable_perfmap(),
537 }
538 }
539
540 Box::new(config)
541 }
542 BackendType::V8 | BackendType::Wamr | BackendType::Wasmi => unreachable!(),
543 #[cfg(not(all(feature = "singlepass", feature = "cranelift", feature = "llvm")))]
544 compiler => {
545 bail!("The `{compiler}` compiler is not included in this binary.")
546 }
547 };
548
549 #[allow(unreachable_code)]
550 Ok(compiler_config)
551 }
552}
553
554#[derive(Debug, PartialEq, Eq, Clone, Copy)]
556#[allow(clippy::upper_case_acronyms, dead_code)]
557pub enum BackendType {
558 Singlepass,
560
561 Cranelift,
563
564 LLVM,
566
567 V8,
569
570 Wamr,
572
573 Wasmi,
575
576 #[allow(dead_code)]
578 Headless,
579}
580
581impl BackendType {
582 pub fn enabled() -> Vec<Self> {
584 vec![
585 #[cfg(feature = "cranelift")]
586 Self::Cranelift,
587 #[cfg(feature = "llvm")]
588 Self::LLVM,
589 #[cfg(feature = "singlepass")]
590 Self::Singlepass,
591 #[cfg(feature = "v8")]
592 Self::V8,
593 #[cfg(feature = "wamr")]
594 Self::Wamr,
595 #[cfg(feature = "wasmi")]
596 Self::Wasmi,
597 ]
598 }
599
600 pub fn get_engine(&self, target: &Target, runtime_opts: &RuntimeOptions) -> Result<Engine> {
604 match self {
605 #[cfg(feature = "singlepass")]
606 Self::Singlepass => {
607 let mut config = wasmer_compiler_singlepass::Singlepass::new();
608 let supported_features = config.supported_features_for_target(target);
609 if runtime_opts.enable_verifier {
610 config.enable_verifier();
611 }
612 if runtime_opts.enable_nan_canonicalization {
613 config.canonicalize_nans(true);
614 }
615 if let Some(p) = &runtime_opts.profiler {
616 match p {
617 Profiler::Perfmap => config.enable_perfmap(),
618 }
619 }
620 if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
621 use wasmer_compiler_singlepass::SinglepassCallbacks;
622
623 debug_dir.push("singlepass");
624 config.callbacks(Some(SinglepassCallbacks::new(debug_dir)?));
625 }
626 if let Some(num_threads) = runtime_opts.compiler_threads {
627 config.num_threads(num_threads);
628 }
629 let engine = wasmer_compiler::EngineBuilder::new(config)
630 .set_features(Some(supported_features))
631 .set_target(Some(target.clone()))
632 .engine()
633 .into();
634 Ok(engine)
635 }
636 #[cfg(feature = "cranelift")]
637 Self::Cranelift => {
638 let mut config = wasmer_compiler_cranelift::Cranelift::new();
639 let supported_features = config.supported_features_for_target(target);
640 if runtime_opts.enable_verifier {
641 config.enable_verifier();
642 }
643 if runtime_opts.enable_nan_canonicalization {
644 config.canonicalize_nans(true);
645 }
646 if let Some(p) = &runtime_opts.profiler {
647 match p {
648 Profiler::Perfmap => config.enable_perfmap(),
649 }
650 }
651 if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
652 use wasmer_compiler_cranelift::CraneliftCallbacks;
653
654 debug_dir.push("cranelift");
655 config.callbacks(Some(CraneliftCallbacks::new(debug_dir)?));
656 }
657 if let Some(num_threads) = runtime_opts.compiler_threads {
658 config.num_threads(num_threads);
659 }
660 let engine = wasmer_compiler::EngineBuilder::new(config)
661 .set_features(Some(supported_features))
662 .set_target(Some(target.clone()))
663 .engine()
664 .into();
665 Ok(engine)
666 }
667 #[cfg(feature = "llvm")]
668 Self::LLVM => {
669 use wasmer_compiler_llvm::LLVMCallbacks;
670 use wasmer_types::entity::EntityRef;
671
672 let mut config = wasmer_compiler_llvm::LLVM::new();
673 config.enable_non_volatile_memops();
674 config.enable_readonly_funcref_table();
675
676 let supported_features = config.supported_features_for_target(target);
677 if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
678 debug_dir.push("llvm");
679 config.callbacks(Some(LLVMCallbacks::new(debug_dir)?));
680 config.verbose_asm(true);
681 }
682 if runtime_opts.enable_verifier {
683 config.enable_verifier();
684 }
685 if runtime_opts.enable_nan_canonicalization {
686 config.canonicalize_nans(true);
687 }
688
689 if let Some(num_threads) = runtime_opts.compiler_threads {
690 config.num_threads(num_threads);
691 }
692
693 if let Some(p) = &runtime_opts.profiler {
694 match p {
695 Profiler::Perfmap => config.enable_perfmap(),
696 }
697 }
698
699 let engine = wasmer_compiler::EngineBuilder::new(config)
700 .set_features(Some(supported_features))
701 .set_target(Some(target.clone()))
702 .engine()
703 .into();
704 Ok(engine)
705 }
706 #[cfg(feature = "v8")]
707 Self::V8 => Ok(wasmer::v8::V8::new().into()),
708 #[cfg(feature = "wamr")]
709 Self::Wamr => Ok(wasmer::wamr::Wamr::new().into()),
710 #[cfg(feature = "wasmi")]
711 Self::Wasmi => Ok(wasmer::wasmi::Wasmi::new().into()),
712 Self::Headless => bail!("Headless is not a valid runtime to instantiate directly"),
713 #[allow(unreachable_patterns)]
714 _ => bail!("Unsupported backend type"),
715 }
716 }
717
718 #[allow(unreachable_code)]
720 pub fn supports_features(&self, required_features: &Features, target: &Target) -> bool {
721 let backend_kind = match self {
723 #[cfg(feature = "singlepass")]
724 Self::Singlepass => wasmer::BackendKind::Singlepass,
725 #[cfg(feature = "cranelift")]
726 Self::Cranelift => wasmer::BackendKind::Cranelift,
727 #[cfg(feature = "llvm")]
728 Self::LLVM => wasmer::BackendKind::LLVM,
729 #[cfg(feature = "v8")]
730 Self::V8 => wasmer::BackendKind::V8,
731 #[cfg(feature = "wamr")]
732 Self::Wamr => wasmer::BackendKind::Wamr,
733 #[cfg(feature = "wasmi")]
734 Self::Wasmi => wasmer::BackendKind::Wasmi,
735 Self::Headless => return false, #[allow(unreachable_patterns)]
737 _ => return false,
738 };
739
740 let supported = wasmer::Engine::supported_features_for_backend(&backend_kind, target);
742
743 if !supported.contains_features(required_features) {
745 return false;
746 }
747
748 true
749 }
750}
751
752impl From<&BackendType> for wasmer::BackendKind {
753 fn from(backend_type: &BackendType) -> Self {
754 match backend_type {
755 #[cfg(feature = "singlepass")]
756 BackendType::Singlepass => wasmer::BackendKind::Singlepass,
757 #[cfg(feature = "cranelift")]
758 BackendType::Cranelift => wasmer::BackendKind::Cranelift,
759 #[cfg(feature = "llvm")]
760 BackendType::LLVM => wasmer::BackendKind::LLVM,
761 #[cfg(feature = "v8")]
762 BackendType::V8 => wasmer::BackendKind::V8,
763 #[cfg(feature = "wamr")]
764 BackendType::Wamr => wasmer::BackendKind::Wamr,
765 #[cfg(feature = "wasmi")]
766 BackendType::Wasmi => wasmer::BackendKind::Wasmi,
767 _ => {
768 #[cfg(feature = "sys")]
769 {
770 wasmer::BackendKind::Headless
771 }
772 #[cfg(not(feature = "sys"))]
773 {
774 unreachable!("No backend enabled!")
775 }
776 }
777 }
778 }
779}
780
781impl std::fmt::Display for BackendType {
782 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
783 write!(
784 f,
785 "{}",
786 match self {
787 Self::Singlepass => "singlepass",
788 Self::Cranelift => "cranelift",
789 Self::LLVM => "llvm",
790 Self::V8 => "v8",
791 Self::Wamr => "wamr",
792 Self::Wasmi => "wasmi",
793 Self::Headless => "headless",
794 }
795 )
796 }
797}