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