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