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
518 if let Some(num_threads) = self.compiler_threads {
519 config.num_threads(num_threads);
520 }
521
522 if let Some(mut debug_dir) = self.compiler_debug_dir.clone() {
523 debug_dir.push("llvm");
524 config.callbacks(Some(LLVMCallbacks::new(debug_dir)?));
525 config.verbose_asm(true);
526 }
527 if self.enable_verifier {
528 config.enable_verifier();
529 }
530 if self.enable_nan_canonicalization {
531 config.canonicalize_nans(true);
532 }
533 if let Some(p) = &self.profiler {
534 match p {
535 Profiler::Perfmap => config.enable_perfmap(),
536 }
537 }
538
539 Box::new(config)
540 }
541 BackendType::V8 | BackendType::Wamr | BackendType::Wasmi => unreachable!(),
542 #[cfg(not(all(feature = "singlepass", feature = "cranelift", feature = "llvm")))]
543 compiler => {
544 bail!("The `{compiler}` compiler is not included in this binary.")
545 }
546 };
547
548 #[allow(unreachable_code)]
549 Ok(compiler_config)
550 }
551}
552
553#[derive(Debug, PartialEq, Eq, Clone, Copy)]
555#[allow(clippy::upper_case_acronyms, dead_code)]
556pub enum BackendType {
557 Singlepass,
559
560 Cranelift,
562
563 LLVM,
565
566 V8,
568
569 Wamr,
571
572 Wasmi,
574
575 #[allow(dead_code)]
577 Headless,
578}
579
580impl BackendType {
581 pub fn enabled() -> Vec<Self> {
583 vec![
584 #[cfg(feature = "cranelift")]
585 Self::Cranelift,
586 #[cfg(feature = "llvm")]
587 Self::LLVM,
588 #[cfg(feature = "singlepass")]
589 Self::Singlepass,
590 #[cfg(feature = "v8")]
591 Self::V8,
592 #[cfg(feature = "wamr")]
593 Self::Wamr,
594 #[cfg(feature = "wasmi")]
595 Self::Wasmi,
596 ]
597 }
598
599 pub fn get_engine(&self, target: &Target, runtime_opts: &RuntimeOptions) -> Result<Engine> {
603 match self {
604 #[cfg(feature = "singlepass")]
605 Self::Singlepass => {
606 let mut config = wasmer_compiler_singlepass::Singlepass::new();
607 let supported_features = config.supported_features_for_target(target);
608 if runtime_opts.enable_verifier {
609 config.enable_verifier();
610 }
611 if runtime_opts.enable_nan_canonicalization {
612 config.canonicalize_nans(true);
613 }
614 if let Some(p) = &runtime_opts.profiler {
615 match p {
616 Profiler::Perfmap => config.enable_perfmap(),
617 }
618 }
619 if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
620 use wasmer_compiler_singlepass::SinglepassCallbacks;
621
622 debug_dir.push("singlepass");
623 config.callbacks(Some(SinglepassCallbacks::new(debug_dir)?));
624 }
625 if let Some(num_threads) = runtime_opts.compiler_threads {
626 config.num_threads(num_threads);
627 }
628 let engine = wasmer_compiler::EngineBuilder::new(config)
629 .set_features(Some(supported_features))
630 .set_target(Some(target.clone()))
631 .engine()
632 .into();
633 Ok(engine)
634 }
635 #[cfg(feature = "cranelift")]
636 Self::Cranelift => {
637 let mut config = wasmer_compiler_cranelift::Cranelift::new();
638 let supported_features = config.supported_features_for_target(target);
639 if runtime_opts.enable_verifier {
640 config.enable_verifier();
641 }
642 if runtime_opts.enable_nan_canonicalization {
643 config.canonicalize_nans(true);
644 }
645 if let Some(p) = &runtime_opts.profiler {
646 match p {
647 Profiler::Perfmap => config.enable_perfmap(),
648 }
649 }
650 if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
651 use wasmer_compiler_cranelift::CraneliftCallbacks;
652
653 debug_dir.push("cranelift");
654 config.callbacks(Some(CraneliftCallbacks::new(debug_dir)?));
655 }
656 if let Some(num_threads) = runtime_opts.compiler_threads {
657 config.num_threads(num_threads);
658 }
659 let engine = wasmer_compiler::EngineBuilder::new(config)
660 .set_features(Some(supported_features))
661 .set_target(Some(target.clone()))
662 .engine()
663 .into();
664 Ok(engine)
665 }
666 #[cfg(feature = "llvm")]
667 Self::LLVM => {
668 use wasmer_compiler_llvm::LLVMCallbacks;
669 use wasmer_types::entity::EntityRef;
670
671 let mut config = wasmer_compiler_llvm::LLVM::new();
672 config.enable_non_volatile_memops();
673
674 let supported_features = config.supported_features_for_target(target);
675 if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
676 debug_dir.push("llvm");
677 config.callbacks(Some(LLVMCallbacks::new(debug_dir)?));
678 config.verbose_asm(true);
679 }
680 if runtime_opts.enable_verifier {
681 config.enable_verifier();
682 }
683 if runtime_opts.enable_nan_canonicalization {
684 config.canonicalize_nans(true);
685 }
686
687 if let Some(num_threads) = runtime_opts.compiler_threads {
688 config.num_threads(num_threads);
689 }
690
691 if let Some(p) = &runtime_opts.profiler {
692 match p {
693 Profiler::Perfmap => config.enable_perfmap(),
694 }
695 }
696
697 let engine = wasmer_compiler::EngineBuilder::new(config)
698 .set_features(Some(supported_features))
699 .set_target(Some(target.clone()))
700 .engine()
701 .into();
702 Ok(engine)
703 }
704 #[cfg(feature = "v8")]
705 Self::V8 => Ok(wasmer::v8::V8::new().into()),
706 #[cfg(feature = "wamr")]
707 Self::Wamr => Ok(wasmer::wamr::Wamr::new().into()),
708 #[cfg(feature = "wasmi")]
709 Self::Wasmi => Ok(wasmer::wasmi::Wasmi::new().into()),
710 Self::Headless => bail!("Headless is not a valid runtime to instantiate directly"),
711 #[allow(unreachable_patterns)]
712 _ => bail!("Unsupported backend type"),
713 }
714 }
715
716 #[allow(unreachable_code)]
718 pub fn supports_features(&self, required_features: &Features, target: &Target) -> bool {
719 let backend_kind = match self {
721 #[cfg(feature = "singlepass")]
722 Self::Singlepass => wasmer::BackendKind::Singlepass,
723 #[cfg(feature = "cranelift")]
724 Self::Cranelift => wasmer::BackendKind::Cranelift,
725 #[cfg(feature = "llvm")]
726 Self::LLVM => wasmer::BackendKind::LLVM,
727 #[cfg(feature = "v8")]
728 Self::V8 => wasmer::BackendKind::V8,
729 #[cfg(feature = "wamr")]
730 Self::Wamr => wasmer::BackendKind::Wamr,
731 #[cfg(feature = "wasmi")]
732 Self::Wasmi => wasmer::BackendKind::Wasmi,
733 Self::Headless => return false, #[allow(unreachable_patterns)]
735 _ => return false,
736 };
737
738 let supported = wasmer::Engine::supported_features_for_backend(&backend_kind, target);
740
741 if !supported.contains_features(required_features) {
743 return false;
744 }
745
746 true
747 }
748}
749
750impl From<&BackendType> for wasmer::BackendKind {
751 fn from(backend_type: &BackendType) -> Self {
752 match backend_type {
753 #[cfg(feature = "singlepass")]
754 BackendType::Singlepass => wasmer::BackendKind::Singlepass,
755 #[cfg(feature = "cranelift")]
756 BackendType::Cranelift => wasmer::BackendKind::Cranelift,
757 #[cfg(feature = "llvm")]
758 BackendType::LLVM => wasmer::BackendKind::LLVM,
759 #[cfg(feature = "v8")]
760 BackendType::V8 => wasmer::BackendKind::V8,
761 #[cfg(feature = "wamr")]
762 BackendType::Wamr => wasmer::BackendKind::Wamr,
763 #[cfg(feature = "wasmi")]
764 BackendType::Wasmi => wasmer::BackendKind::Wasmi,
765 _ => {
766 #[cfg(feature = "sys")]
767 {
768 wasmer::BackendKind::Headless
769 }
770 #[cfg(not(feature = "sys"))]
771 {
772 unreachable!("No backend enabled!")
773 }
774 }
775 }
776 }
777}
778
779impl std::fmt::Display for BackendType {
780 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
781 write!(
782 f,
783 "{}",
784 match self {
785 Self::Singlepass => "singlepass",
786 Self::Cranelift => "cranelift",
787 Self::LLVM => "llvm",
788 Self::V8 => "v8",
789 Self::Wamr => "wamr",
790 Self::Wasmi => "wasmi",
791 Self::Headless => "headless",
792 }
793 )
794 }
795}