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