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