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 }
520 if self.enable_verifier {
521 config.enable_verifier();
522 }
523 if let Some(p) = &self.profiler {
524 match p {
525 Profiler::Perfmap => config.enable_perfmap(),
526 }
527 }
528
529 Box::new(config)
530 }
531 BackendType::V8 | BackendType::Wamr | BackendType::Wasmi => unreachable!(),
532 #[cfg(not(all(feature = "singlepass", feature = "cranelift", feature = "llvm")))]
533 compiler => {
534 bail!("The `{compiler}` compiler is not included in this binary.")
535 }
536 };
537
538 #[allow(unreachable_code)]
539 Ok(compiler_config)
540 }
541}
542
543#[derive(Debug, PartialEq, Eq, Clone, Copy)]
545#[allow(clippy::upper_case_acronyms, dead_code)]
546pub enum BackendType {
547 Singlepass,
549
550 Cranelift,
552
553 LLVM,
555
556 V8,
558
559 Wamr,
561
562 Wasmi,
564
565 #[allow(dead_code)]
567 Headless,
568}
569
570impl BackendType {
571 pub fn enabled() -> Vec<Self> {
573 vec![
574 #[cfg(feature = "cranelift")]
575 Self::Cranelift,
576 #[cfg(feature = "llvm")]
577 Self::LLVM,
578 #[cfg(feature = "singlepass")]
579 Self::Singlepass,
580 #[cfg(feature = "v8")]
581 Self::V8,
582 #[cfg(feature = "wamr")]
583 Self::Wamr,
584 #[cfg(feature = "wasmi")]
585 Self::Wasmi,
586 ]
587 }
588
589 pub fn get_engine(
591 &self,
592 target: &Target,
593 features: &Features,
594 runtime_opts: &RuntimeOptions,
595 ) -> Result<Engine> {
596 match self {
597 #[cfg(feature = "singlepass")]
598 Self::Singlepass => {
599 let mut config = wasmer_compiler_singlepass::Singlepass::new();
600 if runtime_opts.enable_verifier {
601 config.enable_verifier();
602 }
603 if let Some(p) = &runtime_opts.profiler {
604 match p {
605 Profiler::Perfmap => config.enable_perfmap(),
606 }
607 }
608 if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
609 use wasmer_compiler_singlepass::SinglepassCallbacks;
610
611 debug_dir.push("singlepass");
612 config.callbacks(Some(SinglepassCallbacks::new(debug_dir)?));
613 }
614 if let Some(num_threads) = runtime_opts.compiler_threads {
615 config.num_threads(num_threads);
616 }
617 let engine = wasmer_compiler::EngineBuilder::new(config)
618 .set_features(Some(features.clone()))
619 .set_target(Some(target.clone()))
620 .engine()
621 .into();
622 Ok(engine)
623 }
624 #[cfg(feature = "cranelift")]
625 Self::Cranelift => {
626 let mut config = wasmer_compiler_cranelift::Cranelift::new();
627 if runtime_opts.enable_verifier {
628 config.enable_verifier();
629 }
630 if let Some(p) = &runtime_opts.profiler {
631 match p {
632 Profiler::Perfmap => config.enable_perfmap(),
633 }
634 }
635 if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
636 use wasmer_compiler_cranelift::CraneliftCallbacks;
637
638 debug_dir.push("cranelift");
639 config.callbacks(Some(CraneliftCallbacks::new(debug_dir)?));
640 }
641 if let Some(num_threads) = runtime_opts.compiler_threads {
642 config.num_threads(num_threads);
643 }
644 let engine = wasmer_compiler::EngineBuilder::new(config)
645 .set_features(Some(features.clone()))
646 .set_target(Some(target.clone()))
647 .engine()
648 .into();
649 Ok(engine)
650 }
651 #[cfg(feature = "llvm")]
652 Self::LLVM => {
653 use wasmer_compiler_llvm::LLVMCallbacks;
654 use wasmer_types::entity::EntityRef;
655
656 let mut config = wasmer_compiler_llvm::LLVM::new();
657
658 if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
659 debug_dir.push("llvm");
660 config.callbacks(Some(LLVMCallbacks::new(debug_dir)?));
661 }
662 if runtime_opts.enable_verifier {
663 config.enable_verifier();
664 }
665
666 if runtime_opts.enable_pass_params_opt {
667 config.enable_pass_params_opt();
668 }
669
670 if let Some(num_threads) = runtime_opts.compiler_threads {
671 config.num_threads(num_threads);
672 }
673
674 if let Some(p) = &runtime_opts.profiler {
675 match p {
676 Profiler::Perfmap => config.enable_perfmap(),
677 }
678 }
679
680 let engine = wasmer_compiler::EngineBuilder::new(config)
681 .set_features(Some(features.clone()))
682 .set_target(Some(target.clone()))
683 .engine()
684 .into();
685 Ok(engine)
686 }
687 #[cfg(feature = "v8")]
688 Self::V8 => Ok(wasmer::v8::V8::new().into()),
689 #[cfg(feature = "wamr")]
690 Self::Wamr => Ok(wasmer::wamr::Wamr::new().into()),
691 #[cfg(feature = "wasmi")]
692 Self::Wasmi => Ok(wasmer::wasmi::Wasmi::new().into()),
693 Self::Headless => bail!("Headless is not a valid runtime to instantiate directly"),
694 #[allow(unreachable_patterns)]
695 _ => bail!("Unsupported backend type"),
696 }
697 }
698
699 #[allow(unreachable_code)]
701 pub fn supports_features(&self, required_features: &Features, target: &Target) -> bool {
702 let backend_kind = match self {
704 #[cfg(feature = "singlepass")]
705 Self::Singlepass => wasmer::BackendKind::Singlepass,
706 #[cfg(feature = "cranelift")]
707 Self::Cranelift => wasmer::BackendKind::Cranelift,
708 #[cfg(feature = "llvm")]
709 Self::LLVM => wasmer::BackendKind::LLVM,
710 #[cfg(feature = "v8")]
711 Self::V8 => wasmer::BackendKind::V8,
712 #[cfg(feature = "wamr")]
713 Self::Wamr => wasmer::BackendKind::Wamr,
714 #[cfg(feature = "wasmi")]
715 Self::Wasmi => wasmer::BackendKind::Wasmi,
716 Self::Headless => return false, #[allow(unreachable_patterns)]
718 _ => return false,
719 };
720
721 let supported = wasmer::Engine::supported_features_for_backend(&backend_kind, target);
723
724 if !supported.contains_features(required_features) {
726 return false;
727 }
728
729 true
730 }
731}
732
733impl From<&BackendType> for wasmer::BackendKind {
734 fn from(backend_type: &BackendType) -> Self {
735 match backend_type {
736 #[cfg(feature = "singlepass")]
737 BackendType::Singlepass => wasmer::BackendKind::Singlepass,
738 #[cfg(feature = "cranelift")]
739 BackendType::Cranelift => wasmer::BackendKind::Cranelift,
740 #[cfg(feature = "llvm")]
741 BackendType::LLVM => wasmer::BackendKind::LLVM,
742 #[cfg(feature = "v8")]
743 BackendType::V8 => wasmer::BackendKind::V8,
744 #[cfg(feature = "wamr")]
745 BackendType::Wamr => wasmer::BackendKind::Wamr,
746 #[cfg(feature = "wasmi")]
747 BackendType::Wasmi => wasmer::BackendKind::Wasmi,
748 _ => {
749 #[cfg(feature = "sys")]
750 {
751 wasmer::BackendKind::Headless
752 }
753 #[cfg(not(feature = "sys"))]
754 {
755 unreachable!("No backend enabled!")
756 }
757 }
758 }
759 }
760}
761
762impl std::fmt::Display for BackendType {
763 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
764 write!(
765 f,
766 "{}",
767 match self {
768 Self::Singlepass => "singlepass",
769 Self::Cranelift => "cranelift",
770 Self::LLVM => "llvm",
771 Self::V8 => "v8",
772 Self::Wamr => "wamr",
773 Self::Wasmi => "wasmi",
774 Self::Headless => "headless",
775 }
776 )
777 }
778}