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 #[cfg(feature = "wamr")]
103 "wamr",
104 #[cfg(feature = "wasmi")]
105 "wasmi"
106 ]))]
107 singlepass: bool,
108
109 #[cfg(feature = "cranelift")]
111 #[clap(short, long, conflicts_with_all = &Vec::<&str>::from_iter([
112 #[cfg(feature = "llvm")]
113 "llvm",
114 #[cfg(feature = "v8")]
115 "v8",
116 #[cfg(feature = "singlepass")]
117 "singlepass",
118 #[cfg(feature = "wamr")]
119 "wamr",
120 #[cfg(feature = "wasmi")]
121 "wasmi"
122 ]))]
123 cranelift: bool,
124
125 #[cfg(feature = "llvm")]
127 #[clap(short, long, conflicts_with_all = &Vec::<&str>::from_iter([
128 #[cfg(feature = "cranelift")]
129 "cranelift",
130 #[cfg(feature = "v8")]
131 "v8",
132 #[cfg(feature = "singlepass")]
133 "singlepass",
134 #[cfg(feature = "wamr")]
135 "wamr",
136 #[cfg(feature = "wasmi")]
137 "wasmi"
138 ]))]
139 llvm: bool,
140
141 #[cfg(feature = "v8")]
143 #[clap(long, conflicts_with_all = &Vec::<&str>::from_iter([
144 #[cfg(feature = "cranelift")]
145 "cranelift",
146 #[cfg(feature = "llvm")]
147 "llvm",
148 #[cfg(feature = "singlepass")]
149 "singlepass",
150 #[cfg(feature = "wamr")]
151 "wamr",
152 #[cfg(feature = "wasmi")]
153 "wasmi"
154 ]))]
155 v8: bool,
156
157 #[cfg(feature = "wamr")]
159 #[clap(long, conflicts_with_all = &Vec::<&str>::from_iter([
160 #[cfg(feature = "cranelift")]
161 "cranelift",
162 #[cfg(feature = "llvm")]
163 "llvm",
164 #[cfg(feature = "singlepass")]
165 "singlepass",
166 #[cfg(feature = "v8")]
167 "v8",
168 #[cfg(feature = "wasmi")]
169 "wasmi"
170 ]))]
171 wamr: bool,
172
173 #[cfg(feature = "wasmi")]
175 #[clap(long, conflicts_with_all = &Vec::<&str>::from_iter([
176 #[cfg(feature = "cranelift")]
177 "cranelift",
178 #[cfg(feature = "llvm")]
179 "llvm",
180 #[cfg(feature = "singlepass")]
181 "singlepass",
182 #[cfg(feature = "v8")]
183 "v8",
184 #[cfg(feature = "wamr")]
185 "wamr"
186 ]))]
187 wasmi: bool,
188
189 #[clap(long)]
193 enable_verifier: bool,
194
195 #[clap(long, alias = "llvm-debug-dir")]
199 pub(crate) compiler_debug_dir: Option<PathBuf>,
200
201 #[clap(long, value_enum)]
205 profiler: Option<Profiler>,
206
207 #[cfg(feature = "llvm")]
209 #[clap(long)]
210 _enable_pass_params_opt: bool,
211
212 #[clap(long, alias = "llvm-num-threads")]
214 compiler_threads: Option<NonZero<usize>>,
215
216 #[clap(flatten)]
217 features: WasmFeatures,
218}
219
220#[derive(Clone, Debug)]
221pub enum Profiler {
222 Perfmap,
224}
225
226impl FromStr for Profiler {
227 type Err = anyhow::Error;
228
229 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
230 match s.to_lowercase().as_str() {
231 "perfmap" => Ok(Self::Perfmap),
232 _ => Err(anyhow::anyhow!("Unrecognized profiler: {s}")),
233 }
234 }
235}
236
237impl RuntimeOptions {
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 = "wamr")]
262 {
263 if self.wamr {
264 return Ok(vec![BackendType::Wamr]);
265 }
266 }
267
268 #[cfg(feature = "v8")]
269 {
270 if self.v8 {
271 return Ok(vec![BackendType::V8]);
272 }
273 }
274
275 #[cfg(feature = "wasmi")]
276 {
277 if self.wasmi {
278 return Ok(vec![BackendType::Wasmi]);
279 }
280 }
281
282 Ok(BackendType::enabled())
283 }
284
285 pub fn filter_backends_by_features(
287 backends: Vec<BackendType>,
288 required_features: &Features,
289 target: &Target,
290 ) -> Vec<BackendType> {
291 backends
292 .into_iter()
293 .filter(|backend| backend.supports_features(required_features, target))
294 .collect()
295 }
296
297 pub fn get_store(&self) -> Result<Store> {
298 let engine = self.get_engine(&Target::default())?;
299 Ok(Store::new(engine))
300 }
301
302 pub fn get_engine(&self, target: &Target) -> Result<Engine> {
303 let backends = self.get_available_backends()?;
304 let backend = backends.first().context("no compiler backend enabled")?;
305 backend.get_engine(target, self)
306 }
307
308 pub fn get_engine_for_module(&self, module_contents: &[u8], target: &Target) -> Result<Engine> {
309 let required_features = self
310 .detect_features_from_wasm(module_contents)
311 .unwrap_or_default();
312
313 self.get_engine_for_features(&required_features, target)
314 }
315
316 pub fn get_engine_for_features(
317 &self,
318 required_features: &Features,
319 target: &Target,
320 ) -> Result<Engine> {
321 let backends = self.get_available_backends()?;
322 let filtered_backends =
323 Self::filter_backends_by_features(backends.clone(), required_features, target);
324
325 if filtered_backends.is_empty() {
326 let enabled_backends = BackendType::enabled();
327 if backends.len() == 1 && enabled_backends.len() > 1 {
328 let filtered_backends =
330 Self::filter_backends_by_features(enabled_backends, required_features, target);
331 let extra_text: String = if !filtered_backends.is_empty() {
332 format!(". You can use --{} instead", filtered_backends[0])
333 } else {
334 "".to_string()
335 };
336 bail!(
337 "The {} backend does not support the required features for the Wasm module{}",
338 backends[0],
339 extra_text
340 );
341 } else {
342 bail!(
343 "No backends support the required features for the Wasm module. Feel free to open an issue at https://github.com/wasmerio/wasmer/issues"
344 );
345 }
346 }
347 filtered_backends.first().unwrap().get_engine(target, self)
348 }
349
350 #[cfg(feature = "compiler")]
351 pub fn get_features(&self, default_features: &Features) -> Result<Features> {
353 if self.features.all {
354 return Ok(Features::all());
355 }
356
357 let mut result = default_features.clone();
358 if !self.features.disable_threads {
359 result.threads(true);
360 }
361 if self.features.disable_threads {
362 result.threads(false);
363 }
364 if self.features.multi_value {
365 result.multi_value(true);
366 }
367 if self.features.simd {
368 result.simd(true);
369 }
370 if self.features.bulk_memory {
371 result.bulk_memory(true);
372 }
373 if self.features.reference_types {
374 result.reference_types(true);
375 }
376 Ok(result)
377 }
378
379 #[cfg(feature = "compiler")]
380 pub fn get_configured_features(&self) -> Result<Features> {
382 let features = Features::default();
383 self.get_features(&features)
384 }
385
386 pub fn detect_features_from_wasm(
388 &self,
389 wasm_bytes: &[u8],
390 ) -> Result<Features, wasmparser::BinaryReaderError> {
391 if self.features.all {
392 return Ok(Features::all());
393 }
394
395 let mut features = Features::detect_from_wasm(wasm_bytes)?;
396
397 if !self.features.disable_threads {
399 features.threads(true);
400 }
401 if self.features.reference_types {
402 features.reference_types(true);
403 }
404 if self.features.simd {
405 features.simd(true);
406 }
407 if self.features.bulk_memory {
408 features.bulk_memory(true);
409 }
410 if self.features.multi_value {
411 features.multi_value(true);
412 }
413 if self.features.tail_call {
414 features.tail_call(true);
415 }
416 if self.features.module_linking {
417 features.module_linking(true);
418 }
419 if self.features.multi_memory {
420 features.multi_memory(true);
421 }
422 if self.features.memory64 {
423 features.memory64(true);
424 }
425 if self.features.exceptions {
426 features.exceptions(true);
427 }
428
429 Ok(features)
430 }
431
432 #[cfg(feature = "compiler")]
433 pub fn get_sys_compiler_engine_for_target(
434 &self,
435 target: Target,
436 ) -> std::result::Result<Engine, anyhow::Error> {
437 let backends = self.get_available_backends()?;
438 let compiler_config = self.get_sys_compiler_config(backends.first().unwrap())?;
439 let default_features = compiler_config.default_features_for_target(&target);
440 let features = self.get_features(&default_features)?;
441 Ok(wasmer_compiler::EngineBuilder::new(compiler_config)
442 .set_features(Some(features))
443 .set_target(Some(target))
444 .engine()
445 .into())
446 }
447
448 #[allow(unused_variables)]
449 #[cfg(feature = "compiler")]
450 pub(crate) fn get_sys_compiler_config(
451 &self,
452 rt: &BackendType,
453 ) -> Result<Box<dyn CompilerConfig>> {
454 let compiler_config: Box<dyn CompilerConfig> = match rt {
455 BackendType::Headless => bail!("The headless engine can't be chosen"),
456 #[cfg(feature = "singlepass")]
457 BackendType::Singlepass => {
458 let mut config = wasmer_compiler_singlepass::Singlepass::new();
459 if self.enable_verifier {
460 config.enable_verifier();
461 }
462 if let Some(p) = &self.profiler {
463 match p {
464 Profiler::Perfmap => config.enable_perfmap(),
465 }
466 }
467 if let Some(mut debug_dir) = self.compiler_debug_dir.clone() {
468 use wasmer_compiler_singlepass::SinglepassCallbacks;
469
470 debug_dir.push("singlepass");
471 config.callbacks(Some(SinglepassCallbacks::new(debug_dir)?));
472 }
473 if let Some(num_threads) = self.compiler_threads {
474 config.num_threads(num_threads);
475 }
476 Box::new(config)
477 }
478 #[cfg(feature = "cranelift")]
479 BackendType::Cranelift => {
480 let mut config = wasmer_compiler_cranelift::Cranelift::new();
481 if self.enable_verifier {
482 config.enable_verifier();
483 }
484 if let Some(p) = &self.profiler {
485 match p {
486 Profiler::Perfmap => config.enable_perfmap(),
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 config.enable_non_volatile_memops();
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(&self, target: &Target, runtime_opts: &RuntimeOptions) -> Result<Engine> {
589 match self {
590 #[cfg(feature = "singlepass")]
591 Self::Singlepass => {
592 let mut config = wasmer_compiler_singlepass::Singlepass::new();
593 let supported_features = config.supported_features_for_target(target);
594 if runtime_opts.enable_verifier {
595 config.enable_verifier();
596 }
597 if let Some(p) = &runtime_opts.profiler {
598 match p {
599 Profiler::Perfmap => config.enable_perfmap(),
600 }
601 }
602 if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
603 use wasmer_compiler_singlepass::SinglepassCallbacks;
604
605 debug_dir.push("singlepass");
606 config.callbacks(Some(SinglepassCallbacks::new(debug_dir)?));
607 }
608 if let Some(num_threads) = runtime_opts.compiler_threads {
609 config.num_threads(num_threads);
610 }
611 let engine = wasmer_compiler::EngineBuilder::new(config)
612 .set_features(Some(supported_features))
613 .set_target(Some(target.clone()))
614 .engine()
615 .into();
616 Ok(engine)
617 }
618 #[cfg(feature = "cranelift")]
619 Self::Cranelift => {
620 let mut config = wasmer_compiler_cranelift::Cranelift::new();
621 let supported_features = config.supported_features_for_target(target);
622 if runtime_opts.enable_verifier {
623 config.enable_verifier();
624 }
625 if let Some(p) = &runtime_opts.profiler {
626 match p {
627 Profiler::Perfmap => config.enable_perfmap(),
628 }
629 }
630 if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
631 use wasmer_compiler_cranelift::CraneliftCallbacks;
632
633 debug_dir.push("cranelift");
634 config.callbacks(Some(CraneliftCallbacks::new(debug_dir)?));
635 }
636 if let Some(num_threads) = runtime_opts.compiler_threads {
637 config.num_threads(num_threads);
638 }
639 let engine = wasmer_compiler::EngineBuilder::new(config)
640 .set_features(Some(supported_features))
641 .set_target(Some(target.clone()))
642 .engine()
643 .into();
644 Ok(engine)
645 }
646 #[cfg(feature = "llvm")]
647 Self::LLVM => {
648 use wasmer_compiler_llvm::LLVMCallbacks;
649 use wasmer_types::entity::EntityRef;
650
651 let mut config = wasmer_compiler_llvm::LLVM::new();
652 config.enable_non_volatile_memops();
653
654 let supported_features = config.supported_features_for_target(target);
655 if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
656 debug_dir.push("llvm");
657 config.callbacks(Some(LLVMCallbacks::new(debug_dir)?));
658 config.verbose_asm(true);
659 }
660 if runtime_opts.enable_verifier {
661 config.enable_verifier();
662 }
663
664 if let Some(num_threads) = runtime_opts.compiler_threads {
665 config.num_threads(num_threads);
666 }
667
668 if let Some(p) = &runtime_opts.profiler {
669 match p {
670 Profiler::Perfmap => config.enable_perfmap(),
671 }
672 }
673
674 let engine = wasmer_compiler::EngineBuilder::new(config)
675 .set_features(Some(supported_features))
676 .set_target(Some(target.clone()))
677 .engine()
678 .into();
679 Ok(engine)
680 }
681 #[cfg(feature = "v8")]
682 Self::V8 => Ok(wasmer::v8::V8::new().into()),
683 #[cfg(feature = "wamr")]
684 Self::Wamr => Ok(wasmer::wamr::Wamr::new().into()),
685 #[cfg(feature = "wasmi")]
686 Self::Wasmi => Ok(wasmer::wasmi::Wasmi::new().into()),
687 Self::Headless => bail!("Headless is not a valid runtime to instantiate directly"),
688 #[allow(unreachable_patterns)]
689 _ => bail!("Unsupported backend type"),
690 }
691 }
692
693 #[allow(unreachable_code)]
695 pub fn supports_features(&self, required_features: &Features, target: &Target) -> bool {
696 let backend_kind = match self {
698 #[cfg(feature = "singlepass")]
699 Self::Singlepass => wasmer::BackendKind::Singlepass,
700 #[cfg(feature = "cranelift")]
701 Self::Cranelift => wasmer::BackendKind::Cranelift,
702 #[cfg(feature = "llvm")]
703 Self::LLVM => wasmer::BackendKind::LLVM,
704 #[cfg(feature = "v8")]
705 Self::V8 => wasmer::BackendKind::V8,
706 #[cfg(feature = "wamr")]
707 Self::Wamr => wasmer::BackendKind::Wamr,
708 #[cfg(feature = "wasmi")]
709 Self::Wasmi => wasmer::BackendKind::Wasmi,
710 Self::Headless => return false, #[allow(unreachable_patterns)]
712 _ => return false,
713 };
714
715 let supported = wasmer::Engine::supported_features_for_backend(&backend_kind, target);
717
718 if !supported.contains_features(required_features) {
720 return false;
721 }
722
723 true
724 }
725}
726
727impl From<&BackendType> for wasmer::BackendKind {
728 fn from(backend_type: &BackendType) -> Self {
729 match backend_type {
730 #[cfg(feature = "singlepass")]
731 BackendType::Singlepass => wasmer::BackendKind::Singlepass,
732 #[cfg(feature = "cranelift")]
733 BackendType::Cranelift => wasmer::BackendKind::Cranelift,
734 #[cfg(feature = "llvm")]
735 BackendType::LLVM => wasmer::BackendKind::LLVM,
736 #[cfg(feature = "v8")]
737 BackendType::V8 => wasmer::BackendKind::V8,
738 #[cfg(feature = "wamr")]
739 BackendType::Wamr => wasmer::BackendKind::Wamr,
740 #[cfg(feature = "wasmi")]
741 BackendType::Wasmi => wasmer::BackendKind::Wasmi,
742 _ => {
743 #[cfg(feature = "sys")]
744 {
745 wasmer::BackendKind::Headless
746 }
747 #[cfg(not(feature = "sys"))]
748 {
749 unreachable!("No backend enabled!")
750 }
751 }
752 }
753 }
754}
755
756impl std::fmt::Display for BackendType {
757 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
758 write!(
759 f,
760 "{}",
761 match self {
762 Self::Singlepass => "singlepass",
763 Self::Cranelift => "cranelift",
764 Self::LLVM => "llvm",
765 Self::V8 => "v8",
766 Self::Wamr => "wamr",
767 Self::Wasmi => "wasmi",
768 Self::Headless => "headless",
769 }
770 )
771 }
772}