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