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