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")]
48 pub multi_value: bool,
49
50 #[clap(long = "enable-bulk-memory")]
52 pub bulk_memory: bool,
53
54 #[clap(long = "enable-tail-call")]
56 pub tail_call: bool,
57
58 #[clap(long = "enable-module-linking")]
60 pub module_linking: bool,
61
62 #[clap(long = "enable-multi-memory")]
64 pub _multi_memory: bool,
65
66 #[clap(long = "enable-memory64")]
68 pub memory64: bool,
69
70 #[clap(long = "enable-exceptions")]
72 pub exceptions: bool,
73
74 #[clap(long = "enable-relaxed-simd")]
76 pub relaxed_simd: bool,
77
78 #[clap(long = "enable-extended-const")]
80 pub extended_const: bool,
81
82 #[clap(long = "wide-arithmetic")]
84 pub wide_arithmetic: bool,
85
86 #[clap(long = "enable-all")]
88 pub all: bool,
89}
90
91#[derive(Debug, Clone, clap::Parser, Default)]
92pub struct RuntimeOptions {
94 #[cfg(feature = "singlepass")]
96 #[clap(short, long, conflicts_with_all = &Vec::<&str>::from_iter([
97 #[cfg(feature = "llvm")]
98 "llvm",
99 #[cfg(feature = "v8")]
100 "v8",
101 #[cfg(feature = "cranelift")]
102 "cranelift",
103 ]))]
104 singlepass: bool,
105
106 #[cfg(feature = "cranelift")]
108 #[clap(short, long, conflicts_with_all = &Vec::<&str>::from_iter([
109 #[cfg(feature = "llvm")]
110 "llvm",
111 #[cfg(feature = "v8")]
112 "v8",
113 #[cfg(feature = "singlepass")]
114 "singlepass",
115 ]))]
116 cranelift: bool,
117
118 #[cfg(feature = "llvm")]
120 #[clap(short, long, conflicts_with_all = &Vec::<&str>::from_iter([
121 #[cfg(feature = "cranelift")]
122 "cranelift",
123 #[cfg(feature = "v8")]
124 "v8",
125 #[cfg(feature = "singlepass")]
126 "singlepass",
127 ]))]
128 llvm: bool,
129
130 #[cfg(feature = "v8")]
132 #[clap(long, conflicts_with_all = &Vec::<&str>::from_iter([
133 #[cfg(feature = "cranelift")]
134 "cranelift",
135 #[cfg(feature = "llvm")]
136 "llvm",
137 #[cfg(feature = "singlepass")]
138 "singlepass",
139 ]))]
140 v8: bool,
141
142 #[clap(long = "experimental-artifact")]
144 experimental_artifact: bool,
145
146 #[clap(long)]
150 enable_verifier: bool,
151
152 #[clap(long, alias = "llvm-debug-dir")]
156 pub(crate) compiler_debug_dir: Option<PathBuf>,
157
158 #[clap(long, value_enum)]
162 profiler: Option<Profiler>,
163
164 #[cfg(feature = "llvm")]
166 #[clap(long, hide = true)]
167 _enable_pass_params_opt: bool,
168
169 #[cfg(feature = "llvm")]
171 #[clap(long)]
172 disable_m0_pass_param_opt: bool,
173
174 #[clap(long, alias = "llvm-num-threads")]
176 compiler_threads: Option<NonZero<usize>>,
177
178 #[clap(long = "enable-nan-canonicalization")]
181 enable_nan_canonicalization: bool,
182
183 #[cfg(feature = "llvm")]
187 #[clap(long = "disable-non-volatile-memops")]
188 disable_non_volatile_memops: bool,
189
190 #[clap(long = "enable-experimental-unaligned-memory-accesses")]
195 enable_experimental_unaligned_memory_accesses: bool,
196
197 #[clap(flatten)]
198 features: WasmFeatures,
199}
200
201#[derive(Clone, Debug)]
202pub enum Profiler {
203 Perfmap,
205 Gdb,
207 Lldb,
209}
210
211impl FromStr for Profiler {
212 type Err = anyhow::Error;
213
214 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
215 match s.to_lowercase().as_str() {
216 "perfmap" => Ok(Self::Perfmap),
217 "gdb" => Ok(Self::Gdb),
218 "lldb" => Ok(Self::Lldb),
219 _ => Err(anyhow::anyhow!("Unrecognized profiler: {s}")),
220 }
221 }
222}
223
224impl RuntimeOptions {
225 fn validate_profiler(&self) -> Result<()> {
226 if self.experimental_artifact && !cfg!(target_os = "linux") {
227 bail!("--experimental-artifact is only supported on Linux");
228 }
229 if !self.experimental_artifact {
230 match self.profiler {
231 Some(Profiler::Gdb) => {
232 bail!("The gdb profiler requires --experimental-artifact")
233 }
234 Some(Profiler::Lldb) => {
235 bail!("The lldb profiler requires --experimental-artifact")
236 }
237 _ => {}
238 }
239 }
240 Ok(())
241 }
242
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 = "v8")]
267 {
268 if self.v8 {
269 return Ok(vec![BackendType::V8]);
270 }
271 }
272
273 Ok(BackendType::enabled())
274 }
275
276 pub fn filter_backends_by_features(
278 backends: Vec<BackendType>,
279 required_features: &Features,
280 target: &Target,
281 ) -> Vec<BackendType> {
282 backends
283 .into_iter()
284 .filter(|backend| backend.supports_features(required_features, target))
285 .collect()
286 }
287
288 pub fn get_store(&self) -> Result<Store> {
289 let engine = self.get_engine(&Target::default())?;
290 Ok(Store::new(engine))
291 }
292
293 pub fn get_engine(&self, target: &Target) -> Result<Engine> {
294 let backends = self.get_available_backends()?;
295 let backend = backends.first().context("no compiler backend enabled")?;
296 backend.get_engine(target, self)
297 }
298
299 pub fn get_engine_for_module(&self, module_contents: &[u8], target: &Target) -> Result<Engine> {
300 let required_features = self
301 .detect_features_from_wasm(module_contents)
302 .unwrap_or_default();
303
304 self.get_engine_for_features(&required_features, target)
305 }
306
307 pub fn get_engine_for_features(
308 &self,
309 required_features: &Features,
310 target: &Target,
311 ) -> Result<Engine> {
312 let backends = self.get_available_backends()?;
313 let filtered_backends =
314 Self::filter_backends_by_features(backends.clone(), required_features, target);
315
316 if filtered_backends.is_empty() {
317 let enabled_backends = BackendType::enabled();
318 if backends.len() == 1 && enabled_backends.len() > 1 {
319 let filtered_backends =
321 Self::filter_backends_by_features(enabled_backends, required_features, target);
322 let extra_text: String = if !filtered_backends.is_empty() {
323 format!(". You can use --{} instead", filtered_backends[0])
324 } else {
325 "".to_string()
326 };
327 bail!(
328 "The {} backend does not support the required features for the Wasm module{}",
329 backends[0],
330 extra_text
331 );
332 } else {
333 bail!(
334 "No backends support the required features for the Wasm module. Feel free to open an issue at https://github.com/wasmerio/wasmer/issues"
335 );
336 }
337 }
338 filtered_backends.first().unwrap().get_engine(target, self)
339 }
340
341 #[cfg(feature = "compiler")]
342 pub fn get_features(&self, default_features: &Features) -> Result<Features> {
344 if self.features.all {
345 return Ok(Features::all());
346 }
347
348 let mut result = default_features.clone();
349 if !self.features.disable_threads {
350 result.threads(true);
351 }
352 if self.features.disable_threads {
353 result.threads(false);
354 }
355 if self.features.multi_value {
356 result.multi_value(true);
357 }
358 if self.features.simd {
359 result.simd(true);
360 }
361 if self.features.bulk_memory {
362 result.bulk_memory(true);
363 }
364 if self.features.reference_types {
365 result.reference_types(true);
366 }
367 Ok(result)
368 }
369
370 #[cfg(feature = "compiler")]
371 pub fn get_configured_features(&self) -> Result<Features> {
373 let features = Features::default();
374 self.get_features(&features)
375 }
376
377 pub fn detect_features_from_wasm(
379 &self,
380 wasm_bytes: &[u8],
381 ) -> Result<Features, wasmparser::BinaryReaderError> {
382 if self.features.all {
383 return Ok(Features::all());
384 }
385
386 let mut features = Features::detect_from_wasm(wasm_bytes)?;
387
388 if !self.features.disable_threads {
390 features.threads(true);
391 }
392 if self.features.reference_types {
393 features.reference_types(true);
394 }
395 if self.features.simd {
396 features.simd(true);
397 }
398 if self.features.bulk_memory {
399 features.bulk_memory(true);
400 }
401 if self.features.multi_value {
402 features.multi_value(true);
403 }
404 if self.features.tail_call {
405 features.tail_call(true);
406 }
407 if self.features.module_linking {
408 features.module_linking(true);
409 }
410 if self.features.memory64 {
411 features.memory64(true);
412 }
413 if self.features.exceptions {
414 features.exceptions(true);
415 }
416
417 Ok(features)
418 }
419
420 #[cfg(feature = "compiler")]
421 pub fn get_sys_compiler_engine_for_target(
422 &self,
423 target: Target,
424 ) -> std::result::Result<Engine, anyhow::Error> {
425 let backends = self.get_available_backends()?;
426 let compiler_config = self.get_sys_compiler_config(backends.first().unwrap())?;
427 let default_features = compiler_config.default_features_for_target(&target);
428 let features = self.get_features(&default_features)?;
429 Ok(wasmer_compiler::EngineBuilder::new(compiler_config)
430 .set_features(Some(features))
431 .set_target(Some(target))
432 .engine()
433 .into())
434 }
435
436 #[allow(unused_variables)]
437 #[cfg(feature = "compiler")]
438 pub(crate) fn get_sys_compiler_config(
439 &self,
440 rt: &BackendType,
441 ) -> Result<Box<dyn CompilerConfig>> {
442 self.validate_profiler()?;
443 let compiler_config: Box<dyn CompilerConfig> = match rt {
444 BackendType::Headless => bail!("The headless engine can't be chosen"),
445 #[cfg(feature = "singlepass")]
446 BackendType::Singlepass => {
447 let mut config = wasmer_compiler_singlepass::Singlepass::new();
448 if self.enable_experimental_unaligned_memory_accesses {
449 config.allow_experimental_unaligned_memory_accesses(true);
450 }
451 if self.enable_nan_canonicalization {
452 config.canonicalize_nans(true);
453 }
454 if let Some(p) = &self.profiler {
455 match p {
456 Profiler::Perfmap => config.enable_perfmap(),
457 Profiler::Gdb => config.enable_debugger(Debugger::Gdb),
458 Profiler::Lldb => config.enable_debugger(Debugger::Lldb),
459 }
460 }
461 if let Some(mut debug_dir) = self.compiler_debug_dir.clone() {
462 use wasmer_compiler_singlepass::SinglepassCallbacks;
463
464 debug_dir.push("singlepass");
465 config.callbacks(Some(SinglepassCallbacks::new(debug_dir)?));
466 }
467 if let Some(num_threads) = self.compiler_threads {
468 config.num_threads(num_threads);
469 }
470 Box::new(config)
471 }
472 #[cfg(feature = "cranelift")]
473 BackendType::Cranelift => {
474 let mut config = wasmer_compiler_cranelift::Cranelift::new();
475 if self.enable_experimental_unaligned_memory_accesses {
476 config.allow_experimental_unaligned_memory_accesses(true);
477 }
478 if self.enable_verifier {
479 config.enable_verifier();
480 }
481 if self.enable_nan_canonicalization {
482 config.canonicalize_nans(true);
483 }
484 if let Some(p) = &self.profiler {
485 match p {
486 Profiler::Perfmap => config.enable_perfmap(),
487 Profiler::Gdb => config.enable_debugger(Debugger::Gdb),
488 Profiler::Lldb => config.enable_debugger(Debugger::Lldb),
489 }
490 }
491 if let Some(mut debug_dir) = self.compiler_debug_dir.clone() {
492 use wasmer_compiler_cranelift::CraneliftCallbacks;
493
494 debug_dir.push("cranelift");
495 config.callbacks(Some(CraneliftCallbacks::new(debug_dir)?));
496 }
497 if let Some(num_threads) = self.compiler_threads {
498 config.num_threads(num_threads);
499 }
500 Box::new(config)
501 }
502 #[cfg(feature = "llvm")]
503 BackendType::LLVM => {
504 use wasmer_compiler_llvm::LLVMCallbacks;
505 use wasmer_types::entity::EntityRef;
506 let mut config = LLVM::new();
507 if self.disable_m0_pass_param_opt {
508 config.enable_m0_pass_param(false);
509 }
510 if !self.disable_non_volatile_memops {
511 config.enable_non_volatile_memops();
512 }
513 config.enable_readonly_funcref_table();
514
515 if let Some(num_threads) = self.compiler_threads {
516 config.num_threads(num_threads);
517 }
518
519 if let Some(mut debug_dir) = self.compiler_debug_dir.clone() {
520 debug_dir.push("llvm");
521 config.callbacks(Some(LLVMCallbacks::new(debug_dir)?));
522 config.verbose_asm(true);
523 }
524 if self.enable_nan_canonicalization {
525 config.canonicalize_nans(true);
526 }
527 if let Some(p) = &self.profiler {
528 match p {
529 Profiler::Perfmap => config.enable_perfmap(),
530 Profiler::Gdb => config.enable_debugger(Debugger::Gdb),
531 Profiler::Lldb => config.enable_debugger(Debugger::Lldb),
532 }
533 }
534
535 Box::new(config)
536 }
537 BackendType::V8 => unreachable!(),
538 #[cfg(not(all(feature = "singlepass", feature = "cranelift", feature = "llvm")))]
539 compiler => {
540 bail!("The `{compiler}` compiler is not included in this binary.")
541 }
542 };
543
544 #[allow(unreachable_code)]
545 {
546 let mut compiler_config = compiler_config;
547 if self.experimental_artifact {
548 compiler_config.experimental_artifact(true);
549 }
550 Ok(compiler_config)
551 }
552 }
553}
554
555#[derive(Debug, PartialEq, Eq, Clone, Copy)]
557#[allow(clippy::upper_case_acronyms, dead_code)]
558pub enum BackendType {
559 Singlepass,
561
562 Cranelift,
564
565 LLVM,
567
568 V8,
570
571 #[allow(dead_code)]
573 Headless,
574}
575
576impl BackendType {
577 pub fn enabled() -> Vec<Self> {
579 vec![
580 #[cfg(feature = "cranelift")]
581 Self::Cranelift,
582 #[cfg(feature = "llvm")]
583 Self::LLVM,
584 #[cfg(feature = "singlepass")]
585 Self::Singlepass,
586 #[cfg(feature = "v8")]
587 Self::V8,
588 ]
589 }
590
591 pub fn get_engine(&self, target: &Target, runtime_opts: &RuntimeOptions) -> Result<Engine> {
595 runtime_opts.validate_profiler()?;
596 match self {
597 #[cfg(feature = "singlepass")]
598 Self::Singlepass => {
599 let mut config = wasmer_compiler_singlepass::Singlepass::new();
600 if runtime_opts.experimental_artifact {
601 config.experimental_artifact(true);
602 }
603 if runtime_opts.enable_experimental_unaligned_memory_accesses {
604 config.allow_experimental_unaligned_memory_accesses(true);
605 }
606 let supported_features = config.supported_features_for_target(target);
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.disable_m0_pass_param_opt {
679 config.enable_m0_pass_param(false);
680 }
681 if runtime_opts.experimental_artifact {
682 config.experimental_artifact(true);
683 }
684 if !runtime_opts.disable_non_volatile_memops {
685 config.enable_non_volatile_memops();
686 }
687 config.enable_readonly_funcref_table();
688
689 let supported_features = config.supported_features_for_target(target);
690 if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
691 debug_dir.push("llvm");
692 config.callbacks(Some(LLVMCallbacks::new(debug_dir)?));
693 config.verbose_asm(true);
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}