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, features: &Features) -> Result<Features> {
357 let mut result = features.clone();
358 if !self.features.disable_threads || self.features.all {
359 result.threads(true);
360 }
361 if self.features.disable_threads && !self.features.all {
362 result.threads(false);
363 }
364 if self.features.multi_value || self.features.all {
365 result.multi_value(true);
366 }
367 if self.features.simd || self.features.all {
368 result.simd(true);
369 }
370 if self.features.bulk_memory || self.features.all {
371 result.bulk_memory(true);
372 }
373 if self.features.reference_types || self.features.all {
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 let mut features = Features::detect_from_wasm(wasm_bytes)?;
392
393 if !self.features.disable_threads || self.features.all {
395 features.threads(true);
396 }
397 if self.features.reference_types || self.features.all {
398 features.reference_types(true);
399 }
400 if self.features.simd || self.features.all {
401 features.simd(true);
402 }
403 if self.features.bulk_memory || self.features.all {
404 features.bulk_memory(true);
405 }
406 if self.features.multi_value || self.features.all {
407 features.multi_value(true);
408 }
409 if self.features.tail_call || self.features.all {
410 features.tail_call(true);
411 }
412 if self.features.module_linking || self.features.all {
413 features.module_linking(true);
414 }
415 if self.features.multi_memory || self.features.all {
416 features.multi_memory(true);
417 }
418 if self.features.memory64 || self.features.all {
419 features.memory64(true);
420 }
421 if self.features.exceptions || self.features.all {
422 features.exceptions(true);
423 }
424
425 Ok(features)
426 }
427
428 #[cfg(feature = "compiler")]
429 pub fn get_sys_compiler_engine_for_target(
430 &self,
431 target: Target,
432 ) -> std::result::Result<Engine, anyhow::Error> {
433 let backends = self.get_available_backends()?;
434 let compiler_config = self.get_sys_compiler_config(backends.first().unwrap())?;
435 let default_features = compiler_config.default_features_for_target(&target);
436 let features = self.get_features(&default_features)?;
437 Ok(wasmer_compiler::EngineBuilder::new(compiler_config)
438 .set_features(Some(features))
439 .set_target(Some(target))
440 .engine()
441 .into())
442 }
443
444 #[allow(unused_variables)]
445 #[cfg(feature = "compiler")]
446 pub(crate) fn get_sys_compiler_config(
447 &self,
448 rt: &BackendType,
449 ) -> Result<Box<dyn CompilerConfig>> {
450 let compiler_config: Box<dyn CompilerConfig> = match rt {
451 BackendType::Headless => bail!("The headless engine can't be chosen"),
452 #[cfg(feature = "singlepass")]
453 BackendType::Singlepass => {
454 let mut config = wasmer_compiler_singlepass::Singlepass::new();
455 if self.enable_verifier {
456 config.enable_verifier();
457 }
458 if let Some(p) = &self.profiler {
459 match p {
460 Profiler::Perfmap => config.enable_perfmap(),
461 }
462 }
463 if let Some(mut debug_dir) = self.compiler_debug_dir.clone() {
464 use wasmer_compiler_singlepass::SinglepassCallbacks;
465
466 debug_dir.push("singlepass");
467 config.callbacks(Some(SinglepassCallbacks::new(debug_dir)?));
468 }
469
470 Box::new(config)
471 }
472 #[cfg(feature = "cranelift")]
473 BackendType::Cranelift => {
474 let mut config = wasmer_compiler_cranelift::Cranelift::new();
475 if self.enable_verifier {
476 config.enable_verifier();
477 }
478 if let Some(p) = &self.profiler {
479 match p {
480 Profiler::Perfmap => config.enable_perfmap(),
481 }
482 }
483 if let Some(mut debug_dir) = self.compiler_debug_dir.clone() {
484 use wasmer_compiler_cranelift::CraneliftCallbacks;
485
486 debug_dir.push("cranelift");
487 config.callbacks(Some(CraneliftCallbacks::new(debug_dir)?));
488 }
489 Box::new(config)
490 }
491 #[cfg(feature = "llvm")]
492 BackendType::LLVM => {
493 use wasmer_compiler_llvm::LLVMCallbacks;
494 use wasmer_types::entity::EntityRef;
495 let mut config = LLVM::new();
496
497 if self.enable_pass_params_opt {
498 config.enable_pass_params_opt();
499 }
500
501 if let Some(num_threads) = self.llvm_num_threads {
502 config.num_threads(num_threads);
503 }
504
505 if let Some(mut debug_dir) = self.compiler_debug_dir.clone() {
506 debug_dir.push("llvm");
507 config.callbacks(Some(LLVMCallbacks::new(debug_dir)?));
508 }
509 if self.enable_verifier {
510 config.enable_verifier();
511 }
512 if let Some(p) = &self.profiler {
513 match p {
514 Profiler::Perfmap => config.enable_perfmap(),
515 }
516 }
517
518 Box::new(config)
519 }
520 BackendType::V8 | BackendType::Wamr | BackendType::Wasmi => unreachable!(),
521 #[cfg(not(all(feature = "singlepass", feature = "cranelift", feature = "llvm")))]
522 compiler => {
523 bail!("The `{compiler}` compiler is not included in this binary.")
524 }
525 };
526
527 #[allow(unreachable_code)]
528 Ok(compiler_config)
529 }
530}
531
532#[derive(Debug, PartialEq, Eq, Clone, Copy)]
534#[allow(clippy::upper_case_acronyms, dead_code)]
535pub enum BackendType {
536 Singlepass,
538
539 Cranelift,
541
542 LLVM,
544
545 V8,
547
548 Wamr,
550
551 Wasmi,
553
554 #[allow(dead_code)]
556 Headless,
557}
558
559impl BackendType {
560 pub fn enabled() -> Vec<Self> {
562 vec![
563 #[cfg(feature = "cranelift")]
564 Self::Cranelift,
565 #[cfg(feature = "llvm")]
566 Self::LLVM,
567 #[cfg(feature = "singlepass")]
568 Self::Singlepass,
569 #[cfg(feature = "v8")]
570 Self::V8,
571 #[cfg(feature = "wamr")]
572 Self::Wamr,
573 #[cfg(feature = "wasmi")]
574 Self::Wasmi,
575 ]
576 }
577
578 pub fn get_engine(
580 &self,
581 target: &Target,
582 features: &Features,
583 runtime_opts: &RuntimeOptions,
584 ) -> Result<Engine> {
585 match self {
586 #[cfg(feature = "singlepass")]
587 Self::Singlepass => {
588 let mut config = wasmer_compiler_singlepass::Singlepass::new();
589 if runtime_opts.enable_verifier {
590 config.enable_verifier();
591 }
592 if let Some(p) = &runtime_opts.profiler {
593 match p {
594 Profiler::Perfmap => config.enable_perfmap(),
595 }
596 }
597 if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
598 use wasmer_compiler_singlepass::SinglepassCallbacks;
599
600 debug_dir.push("singlepass");
601 config.callbacks(Some(SinglepassCallbacks::new(debug_dir)?));
602 }
603 let engine = wasmer_compiler::EngineBuilder::new(config)
604 .set_features(Some(features.clone()))
605 .set_target(Some(target.clone()))
606 .engine()
607 .into();
608 Ok(engine)
609 }
610 #[cfg(feature = "cranelift")]
611 Self::Cranelift => {
612 let mut config = wasmer_compiler_cranelift::Cranelift::new();
613 if runtime_opts.enable_verifier {
614 config.enable_verifier();
615 }
616 if let Some(p) = &runtime_opts.profiler {
617 match p {
618 Profiler::Perfmap => config.enable_perfmap(),
619 }
620 }
621 if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
622 use wasmer_compiler_cranelift::CraneliftCallbacks;
623
624 debug_dir.push("cranelift");
625 config.callbacks(Some(CraneliftCallbacks::new(debug_dir)?));
626 }
627 let engine = wasmer_compiler::EngineBuilder::new(config)
628 .set_features(Some(features.clone()))
629 .set_target(Some(target.clone()))
630 .engine()
631 .into();
632 Ok(engine)
633 }
634 #[cfg(feature = "llvm")]
635 Self::LLVM => {
636 use wasmer_compiler_llvm::LLVMCallbacks;
637 use wasmer_types::entity::EntityRef;
638
639 let mut config = wasmer_compiler_llvm::LLVM::new();
640
641 if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
642 debug_dir.push("llvm");
643 config.callbacks(Some(LLVMCallbacks::new(debug_dir)?));
644 }
645 if runtime_opts.enable_verifier {
646 config.enable_verifier();
647 }
648
649 if runtime_opts.enable_pass_params_opt {
650 config.enable_pass_params_opt();
651 }
652
653 if let Some(num_threads) = runtime_opts.llvm_num_threads {
654 config.num_threads(num_threads);
655 }
656
657 if let Some(p) = &runtime_opts.profiler {
658 match p {
659 Profiler::Perfmap => config.enable_perfmap(),
660 }
661 }
662
663 let engine = wasmer_compiler::EngineBuilder::new(config)
664 .set_features(Some(features.clone()))
665 .set_target(Some(target.clone()))
666 .engine()
667 .into();
668 Ok(engine)
669 }
670 #[cfg(feature = "v8")]
671 Self::V8 => Ok(wasmer::v8::V8::new().into()),
672 #[cfg(feature = "wamr")]
673 Self::Wamr => Ok(wasmer::wamr::Wamr::new().into()),
674 #[cfg(feature = "wasmi")]
675 Self::Wasmi => Ok(wasmer::wasmi::Wasmi::new().into()),
676 Self::Headless => bail!("Headless is not a valid runtime to instantiate directly"),
677 #[allow(unreachable_patterns)]
678 _ => bail!("Unsupported backend type"),
679 }
680 }
681
682 #[allow(unreachable_code)]
684 pub fn supports_features(&self, required_features: &Features, target: &Target) -> bool {
685 let backend_kind = match self {
687 #[cfg(feature = "singlepass")]
688 Self::Singlepass => wasmer::BackendKind::Singlepass,
689 #[cfg(feature = "cranelift")]
690 Self::Cranelift => wasmer::BackendKind::Cranelift,
691 #[cfg(feature = "llvm")]
692 Self::LLVM => wasmer::BackendKind::LLVM,
693 #[cfg(feature = "v8")]
694 Self::V8 => wasmer::BackendKind::V8,
695 #[cfg(feature = "wamr")]
696 Self::Wamr => wasmer::BackendKind::Wamr,
697 #[cfg(feature = "wasmi")]
698 Self::Wasmi => wasmer::BackendKind::Wasmi,
699 Self::Headless => return false, #[allow(unreachable_patterns)]
701 _ => return false,
702 };
703
704 let supported = wasmer::Engine::supported_features_for_backend(&backend_kind, target);
706
707 if !supported.contains_features(required_features) {
709 return false;
710 }
711
712 true
713 }
714}
715
716impl From<&BackendType> for wasmer::BackendKind {
717 fn from(backend_type: &BackendType) -> Self {
718 match backend_type {
719 #[cfg(feature = "singlepass")]
720 BackendType::Singlepass => wasmer::BackendKind::Singlepass,
721 #[cfg(feature = "cranelift")]
722 BackendType::Cranelift => wasmer::BackendKind::Cranelift,
723 #[cfg(feature = "llvm")]
724 BackendType::LLVM => wasmer::BackendKind::LLVM,
725 #[cfg(feature = "v8")]
726 BackendType::V8 => wasmer::BackendKind::V8,
727 #[cfg(feature = "wamr")]
728 BackendType::Wamr => wasmer::BackendKind::Wamr,
729 #[cfg(feature = "wasmi")]
730 BackendType::Wasmi => wasmer::BackendKind::Wasmi,
731 _ => {
732 #[cfg(feature = "sys")]
733 {
734 wasmer::BackendKind::Headless
735 }
736 #[cfg(not(feature = "sys"))]
737 {
738 unreachable!("No backend enabled!")
739 }
740 }
741 }
742 }
743}
744
745impl std::fmt::Display for BackendType {
746 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
747 write!(
748 f,
749 "{}",
750 match self {
751 Self::Singlepass => "singlepass",
752 Self::Cranelift => "cranelift",
753 Self::LLVM => "llvm",
754 Self::V8 => "v8",
755 Self::Wamr => "wamr",
756 Self::Wasmi => "wasmi",
757 Self::Headless => "headless",
758 }
759 )
760 }
761}