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(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(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(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
464 Box::new(config)
465 }
466 #[cfg(feature = "cranelift")]
467 BackendType::Cranelift => {
468 let mut config = wasmer_compiler_cranelift::Cranelift::new();
469 if self.enable_verifier {
470 config.enable_verifier();
471 }
472 if let Some(p) = &self.profiler {
473 match p {
474 Profiler::Perfmap => config.enable_perfmap(),
475 }
476 }
477 if let Some(mut debug_dir) = self.compiler_debug_dir.clone() {
478 use wasmer_compiler_cranelift::CraneliftCallbacks;
479
480 debug_dir.push("cranelift");
481 config.callbacks(Some(CraneliftCallbacks::new(debug_dir)?));
482 }
483 Box::new(config)
484 }
485 #[cfg(feature = "llvm")]
486 BackendType::LLVM => {
487 use wasmer_compiler_llvm::LLVMCallbacks;
488 use wasmer_types::entity::EntityRef;
489 let mut config = LLVM::new();
490
491 if self.enable_pass_params_opt {
492 config.enable_pass_params_opt();
493 }
494
495 if let Some(num_threads) = self.llvm_num_threads {
496 config.num_threads(num_threads);
497 }
498
499 if let Some(mut debug_dir) = self.compiler_debug_dir.clone() {
500 debug_dir.push("llvm");
501 config.callbacks(Some(LLVMCallbacks::new(debug_dir)?));
502 }
503 if self.enable_verifier {
504 config.enable_verifier();
505 }
506 if let Some(p) = &self.profiler {
507 match p {
508 Profiler::Perfmap => config.enable_perfmap(),
509 }
510 }
511
512 Box::new(config)
513 }
514 BackendType::V8 | BackendType::Wamr | BackendType::Wasmi => unreachable!(),
515 #[cfg(not(all(feature = "singlepass", feature = "cranelift", feature = "llvm")))]
516 compiler => {
517 bail!("The `{compiler}` compiler is not included in this binary.")
518 }
519 };
520
521 #[allow(unreachable_code)]
522 Ok(compiler_config)
523 }
524}
525
526#[derive(Debug, PartialEq, Eq, Clone, Copy)]
528#[allow(clippy::upper_case_acronyms, dead_code)]
529pub enum BackendType {
530 Singlepass,
532
533 Cranelift,
535
536 LLVM,
538
539 V8,
541
542 Wamr,
544
545 Wasmi,
547
548 #[allow(dead_code)]
550 Headless,
551}
552
553impl BackendType {
554 pub fn enabled() -> Vec<Self> {
556 vec![
557 #[cfg(feature = "cranelift")]
558 Self::Cranelift,
559 #[cfg(feature = "llvm")]
560 Self::LLVM,
561 #[cfg(feature = "singlepass")]
562 Self::Singlepass,
563 #[cfg(feature = "v8")]
564 Self::V8,
565 #[cfg(feature = "wamr")]
566 Self::Wamr,
567 #[cfg(feature = "wasmi")]
568 Self::Wasmi,
569 ]
570 }
571
572 pub fn get_engine(
574 &self,
575 target: &Target,
576 features: &Features,
577 runtime_opts: &RuntimeOptions,
578 ) -> Result<Engine> {
579 match self {
580 #[cfg(feature = "singlepass")]
581 Self::Singlepass => {
582 let mut config = wasmer_compiler_singlepass::Singlepass::new();
583 if runtime_opts.enable_verifier {
584 config.enable_verifier();
585 }
586 if let Some(p) = &runtime_opts.profiler {
587 match p {
588 Profiler::Perfmap => config.enable_perfmap(),
589 }
590 }
591 let engine = wasmer_compiler::EngineBuilder::new(config)
592 .set_features(Some(features.clone()))
593 .set_target(Some(target.clone()))
594 .engine()
595 .into();
596 Ok(engine)
597 }
598 #[cfg(feature = "cranelift")]
599 Self::Cranelift => {
600 let mut config = wasmer_compiler_cranelift::Cranelift::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_cranelift::CraneliftCallbacks;
611
612 debug_dir.push("cranelift");
613 config.callbacks(Some(CraneliftCallbacks::new(debug_dir)?));
614 }
615 let engine = wasmer_compiler::EngineBuilder::new(config)
616 .set_features(Some(features.clone()))
617 .set_target(Some(target.clone()))
618 .engine()
619 .into();
620 Ok(engine)
621 }
622 #[cfg(feature = "llvm")]
623 Self::LLVM => {
624 use wasmer_compiler_llvm::LLVMCallbacks;
625 use wasmer_types::entity::EntityRef;
626
627 let mut config = wasmer_compiler_llvm::LLVM::new();
628
629 if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
630 debug_dir.push("llvm");
631 config.callbacks(Some(LLVMCallbacks::new(debug_dir)?));
632 }
633 if runtime_opts.enable_verifier {
634 config.enable_verifier();
635 }
636
637 if runtime_opts.enable_pass_params_opt {
638 config.enable_pass_params_opt();
639 }
640
641 if let Some(num_threads) = runtime_opts.llvm_num_threads {
642 config.num_threads(num_threads);
643 }
644
645 if let Some(p) = &runtime_opts.profiler {
646 match p {
647 Profiler::Perfmap => config.enable_perfmap(),
648 }
649 }
650
651 let engine = wasmer_compiler::EngineBuilder::new(config)
652 .set_features(Some(features.clone()))
653 .set_target(Some(target.clone()))
654 .engine()
655 .into();
656 Ok(engine)
657 }
658 #[cfg(feature = "v8")]
659 Self::V8 => Ok(wasmer::v8::V8::new().into()),
660 #[cfg(feature = "wamr")]
661 Self::Wamr => Ok(wasmer::wamr::Wamr::new().into()),
662 #[cfg(feature = "wasmi")]
663 Self::Wasmi => Ok(wasmer::wasmi::Wasmi::new().into()),
664 Self::Headless => bail!("Headless is not a valid runtime to instantiate directly"),
665 #[allow(unreachable_patterns)]
666 _ => bail!("Unsupported backend type"),
667 }
668 }
669
670 #[allow(unreachable_code)]
672 pub fn supports_features(&self, required_features: &Features, target: &Target) -> bool {
673 let backend_kind = match self {
675 #[cfg(feature = "singlepass")]
676 Self::Singlepass => wasmer::BackendKind::Singlepass,
677 #[cfg(feature = "cranelift")]
678 Self::Cranelift => wasmer::BackendKind::Cranelift,
679 #[cfg(feature = "llvm")]
680 Self::LLVM => wasmer::BackendKind::LLVM,
681 #[cfg(feature = "v8")]
682 Self::V8 => wasmer::BackendKind::V8,
683 #[cfg(feature = "wamr")]
684 Self::Wamr => wasmer::BackendKind::Wamr,
685 #[cfg(feature = "wasmi")]
686 Self::Wasmi => wasmer::BackendKind::Wasmi,
687 Self::Headless => return false, #[allow(unreachable_patterns)]
689 _ => return false,
690 };
691
692 let supported = wasmer::Engine::supported_features_for_backend(&backend_kind, target);
694
695 if !supported.contains_features(required_features) {
697 return false;
698 }
699
700 true
701 }
702}
703
704impl From<&BackendType> for wasmer::BackendKind {
705 fn from(backend_type: &BackendType) -> Self {
706 match backend_type {
707 #[cfg(feature = "singlepass")]
708 BackendType::Singlepass => wasmer::BackendKind::Singlepass,
709 #[cfg(feature = "cranelift")]
710 BackendType::Cranelift => wasmer::BackendKind::Cranelift,
711 #[cfg(feature = "llvm")]
712 BackendType::LLVM => wasmer::BackendKind::LLVM,
713 #[cfg(feature = "v8")]
714 BackendType::V8 => wasmer::BackendKind::V8,
715 #[cfg(feature = "wamr")]
716 BackendType::Wamr => wasmer::BackendKind::Wamr,
717 #[cfg(feature = "wasmi")]
718 BackendType::Wasmi => wasmer::BackendKind::Wasmi,
719 _ => {
720 #[cfg(feature = "sys")]
721 {
722 wasmer::BackendKind::Headless
723 }
724 #[cfg(not(feature = "sys"))]
725 {
726 unreachable!("No backend enabled!")
727 }
728 }
729 }
730 }
731}
732
733impl std::fmt::Display for BackendType {
734 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
735 write!(
736 f,
737 "{}",
738 match self {
739 Self::Singlepass => "singlepass",
740 Self::Cranelift => "cranelift",
741 Self::LLVM => "llvm",
742 Self::V8 => "v8",
743 Self::Wamr => "wamr",
744 Self::Wasmi => "wasmi",
745 Self::Headless => "headless",
746 }
747 )
748 }
749}