Skip to main content

wasmer_cli/
backend.rs

1//! Common module with common used structures across different
2//! commands.
3
4// NOTE: A lot of this code depends on feature flags.
5// To not go crazy with annotations, some lints are disabled for the whole
6// module.
7#![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, Debugger};
22
23use wasmer::Engine;
24
25#[derive(Debug, clap::Parser, Clone, Default)]
26/// The WebAssembly features that can be passed through the
27/// Command Line args.
28pub struct WasmFeatures {
29    /// Enable support for the SIMD proposal.
30    #[clap(long = "enable-simd")]
31    pub simd: bool,
32
33    /// Disable support for the threads proposal.
34    #[clap(long = "disable-threads")]
35    pub disable_threads: bool,
36
37    /// Deprecated, threads are enabled by default.
38    #[clap(long = "enable-threads")]
39    pub _threads: bool,
40
41    /// Enable support for the reference types proposal.
42    #[clap(long = "enable-reference-types")]
43    pub reference_types: bool,
44
45    /// Enable support for the multi-value proposal. Singlepass support for multi-value is
46    /// experimental and does not include integration with host functions returning multiple values.
47    #[clap(long = "enable-multi-value")]
48    pub multi_value: bool,
49
50    /// Enable support for the bulk memory proposal.
51    #[clap(long = "enable-bulk-memory")]
52    pub bulk_memory: bool,
53
54    /// Enable support for the tail call proposal.
55    #[clap(long = "enable-tail-call")]
56    pub tail_call: bool,
57
58    /// Enable support for the module linking proposal.
59    #[clap(long = "enable-module-linking")]
60    pub module_linking: bool,
61
62    /// Deprecated, multi memory is enabled by default.
63    #[clap(long = "enable-multi-memory")]
64    pub _multi_memory: bool,
65
66    /// Enable support for the memory64 proposal.
67    #[clap(long = "enable-memory64")]
68    pub memory64: bool,
69
70    /// Enable support for the exceptions proposal.
71    #[clap(long = "enable-exceptions")]
72    pub exceptions: bool,
73
74    /// Enable support for the relaxed SIMD proposal.
75    #[clap(long = "enable-relaxed-simd")]
76    pub relaxed_simd: bool,
77
78    /// Enable support for the extended constant expressions proposal.
79    #[clap(long = "enable-extended-const")]
80    pub extended_const: bool,
81
82    /// Enable support for the wide arithmetic proposal.
83    #[clap(long = "wide-arithmetic")]
84    pub wide_arithmetic: bool,
85
86    /// Enable support for all pre-standard proposals.
87    #[clap(long = "enable-all")]
88    pub all: bool,
89}
90
91#[derive(Debug, Clone, clap::Parser, Default)]
92/// The compiler options
93pub struct RuntimeOptions {
94    /// Use Singlepass compiler.
95    #[cfg(feature = "singlepass")]
96    #[clap(short, long, conflicts_with_all = &Vec::<&str>::from_iter([
97        #[cfg(feature = "llvm")]
98        "llvm", 
99        #[cfg(feature = "v8")]
100        "v8", 
101        #[cfg(feature = "cranelift")]
102        "cranelift",         
103    ]))]
104    singlepass: bool,
105
106    /// Use Cranelift compiler.
107    #[cfg(feature = "cranelift")]
108    #[clap(short, long, conflicts_with_all = &Vec::<&str>::from_iter([
109        #[cfg(feature = "llvm")]
110        "llvm", 
111        #[cfg(feature = "v8")]
112        "v8", 
113        #[cfg(feature = "singlepass")]
114        "singlepass", 
115    ]))]
116    cranelift: bool,
117
118    /// Use LLVM compiler.
119    #[cfg(feature = "llvm")]
120    #[clap(short, long, conflicts_with_all = &Vec::<&str>::from_iter([
121        #[cfg(feature = "cranelift")]
122        "cranelift", 
123        #[cfg(feature = "v8")]
124        "v8", 
125        #[cfg(feature = "singlepass")]
126        "singlepass", 
127    ]))]
128    llvm: bool,
129
130    /// Use the V8 runtime.
131    #[cfg(feature = "v8")]
132    #[clap(long, conflicts_with_all = &Vec::<&str>::from_iter([
133        #[cfg(feature = "cranelift")]
134        "cranelift", 
135        #[cfg(feature = "llvm")]
136        "llvm", 
137        #[cfg(feature = "singlepass")]
138        "singlepass", 
139    ]))]
140    v8: bool,
141
142    /// Use the experimental artifact format.
143    #[clap(long = "experimental-artifact")]
144    experimental_artifact: bool,
145
146    /// Enable compiler internal verification.
147    ///
148    /// Available for Cranelift (enabled always for LLVM).
149    #[clap(long)]
150    enable_verifier: bool,
151
152    /// Debug directory, where IR and object files will be written to.
153    ///
154    /// Available for Cranelift, LLVM and Singlepass.
155    #[clap(long, alias = "llvm-debug-dir")]
156    pub(crate) compiler_debug_dir: Option<PathBuf>,
157
158    /// Enable a profiler.
159    ///
160    /// Available for Cranelift, LLVM and Singlepass.
161    #[clap(long, value_enum)]
162    profiler: Option<Profiler>,
163
164    /// Deprecated option as m0 optimization always play role if we use a static memory
165    #[cfg(feature = "llvm")]
166    #[clap(long, hide = true)]
167    _enable_pass_params_opt: bool,
168
169    /// For the LLVM compiler, disable passing the pointer to the first memory as a hidden argument.
170    #[cfg(feature = "llvm")]
171    #[clap(long)]
172    disable_m0_pass_param_opt: bool,
173
174    /// Sets the number of threads used to compile the input module(s).
175    #[clap(long, alias = "llvm-num-threads")]
176    compiler_threads: Option<NonZero<usize>>,
177
178    /// Enable NaN canonicalization during compilation to produce deterministic
179    /// canonical quiet NaNs (QNaNs) across architectures.
180    #[clap(long = "enable-nan-canonicalization")]
181    enable_nan_canonicalization: bool,
182
183    /// Disable LLVM non-volatile memory operations.
184    ///
185    /// Available for LLVM.
186    #[cfg(feature = "llvm")]
187    #[clap(long = "disable-non-volatile-memops")]
188    disable_non_volatile_memops: bool,
189
190    /// Allow unaligned memory accesses.
191    ///
192    /// This feature is experimental and currently supports only Cranelift scalar types
193    /// and Singlepass on RISC-V for integral types.
194    #[clap(long = "enable-experimental-unaligned-memory-accesses")]
195    enable_experimental_unaligned_memory_accesses: bool,
196
197    #[clap(flatten)]
198    features: WasmFeatures,
199}
200
201#[derive(Clone, Debug)]
202pub enum Profiler {
203    /// Perfmap-based profilers.
204    Perfmap,
205    /// GDB command file.
206    Gdb,
207    /// LLDB command file.
208    Lldb,
209}
210
211impl FromStr for Profiler {
212    type Err = anyhow::Error;
213
214    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
215        match s.to_lowercase().as_str() {
216            "perfmap" => Ok(Self::Perfmap),
217            "gdb" => Ok(Self::Gdb),
218            "lldb" => Ok(Self::Lldb),
219            _ => Err(anyhow::anyhow!("Unrecognized profiler: {s}")),
220        }
221    }
222}
223
224impl RuntimeOptions {
225    fn validate_profiler(&self) -> Result<()> {
226        if self.experimental_artifact && !cfg!(target_os = "linux") {
227            bail!("--experimental-artifact is only supported on Linux");
228        }
229        if !self.experimental_artifact {
230            match self.profiler {
231                Some(Profiler::Gdb) => {
232                    bail!("The gdb profiler requires --experimental-artifact")
233                }
234                Some(Profiler::Lldb) => {
235                    bail!("The lldb profiler requires --experimental-artifact")
236                }
237                _ => {}
238            }
239        }
240        Ok(())
241    }
242
243    pub fn get_available_backends(&self) -> Result<Vec<BackendType>> {
244        // If a specific backend is explicitly requested, use it
245        #[cfg(feature = "cranelift")]
246        {
247            if self.cranelift {
248                return Ok(vec![BackendType::Cranelift]);
249            }
250        }
251
252        #[cfg(feature = "llvm")]
253        {
254            if self.llvm {
255                return Ok(vec![BackendType::LLVM]);
256            }
257        }
258
259        #[cfg(feature = "singlepass")]
260        {
261            if self.singlepass {
262                return Ok(vec![BackendType::Singlepass]);
263            }
264        }
265
266        #[cfg(feature = "v8")]
267        {
268            if self.v8 {
269                return Ok(vec![BackendType::V8]);
270            }
271        }
272
273        Ok(BackendType::enabled())
274    }
275
276    /// Filter enabled backends based on required WebAssembly features
277    pub fn filter_backends_by_features(
278        backends: Vec<BackendType>,
279        required_features: &Features,
280        target: &Target,
281    ) -> Vec<BackendType> {
282        backends
283            .into_iter()
284            .filter(|backend| backend.supports_features(required_features, target))
285            .collect()
286    }
287
288    pub fn get_store(&self) -> Result<Store> {
289        let engine = self.get_engine(&Target::default())?;
290        Ok(Store::new(engine))
291    }
292
293    pub fn get_engine(&self, target: &Target) -> Result<Engine> {
294        let backends = self.get_available_backends()?;
295        let backend = backends.first().context("no compiler backend enabled")?;
296        backend.get_engine(target, self)
297    }
298
299    pub fn get_engine_for_module(&self, module_contents: &[u8], target: &Target) -> Result<Engine> {
300        let required_features = self
301            .detect_features_from_wasm(module_contents)
302            .unwrap_or_default();
303
304        self.get_engine_for_features(&required_features, target)
305    }
306
307    pub fn get_engine_for_features(
308        &self,
309        required_features: &Features,
310        target: &Target,
311    ) -> Result<Engine> {
312        let backends = self.get_available_backends()?;
313        let filtered_backends =
314            Self::filter_backends_by_features(backends.clone(), required_features, target);
315
316        if filtered_backends.is_empty() {
317            let enabled_backends = BackendType::enabled();
318            if backends.len() == 1 && enabled_backends.len() > 1 {
319                // If the user has chosen an specific backend, we can suggest to use another one
320                let filtered_backends =
321                    Self::filter_backends_by_features(enabled_backends, required_features, target);
322                let extra_text: String = if !filtered_backends.is_empty() {
323                    format!(". You can use --{} instead", filtered_backends[0])
324                } else {
325                    "".to_string()
326                };
327                bail!(
328                    "The {} backend does not support the required features for the Wasm module{}",
329                    backends[0],
330                    extra_text
331                );
332            } else {
333                bail!(
334                    "No backends support the required features for the Wasm module. Feel free to open an issue at https://github.com/wasmerio/wasmer/issues"
335                );
336            }
337        }
338        filtered_backends.first().unwrap().get_engine(target, self)
339    }
340
341    #[cfg(feature = "compiler")]
342    /// Get the enabled Wasm features.
343    pub fn get_features(&self, default_features: &Features) -> Result<Features> {
344        if self.features.all {
345            return Ok(Features::all());
346        }
347
348        let mut result = default_features.clone();
349        if !self.features.disable_threads {
350            result.threads(true);
351        }
352        if self.features.disable_threads {
353            result.threads(false);
354        }
355        if self.features.multi_value {
356            result.multi_value(true);
357        }
358        if self.features.simd {
359            result.simd(true);
360        }
361        if self.features.bulk_memory {
362            result.bulk_memory(true);
363        }
364        if self.features.reference_types {
365            result.reference_types(true);
366        }
367        Ok(result)
368    }
369
370    #[cfg(feature = "compiler")]
371    /// Get a copy of the default features with user-configured options
372    pub fn get_configured_features(&self) -> Result<Features> {
373        let features = Features::default();
374        self.get_features(&features)
375    }
376
377    /// Detect features from a WebAssembly module binary.
378    pub fn detect_features_from_wasm(
379        &self,
380        wasm_bytes: &[u8],
381    ) -> Result<Features, wasmparser::BinaryReaderError> {
382        if self.features.all {
383            return Ok(Features::all());
384        }
385
386        let mut features = Features::detect_from_wasm(wasm_bytes)?;
387
388        // Merge with user-configured features
389        if !self.features.disable_threads {
390            features.threads(true);
391        }
392        if self.features.reference_types {
393            features.reference_types(true);
394        }
395        if self.features.simd {
396            features.simd(true);
397        }
398        if self.features.bulk_memory {
399            features.bulk_memory(true);
400        }
401        if self.features.multi_value {
402            features.multi_value(true);
403        }
404        if self.features.tail_call {
405            features.tail_call(true);
406        }
407        if self.features.module_linking {
408            features.module_linking(true);
409        }
410        if self.features.memory64 {
411            features.memory64(true);
412        }
413        if self.features.exceptions {
414            features.exceptions(true);
415        }
416
417        Ok(features)
418    }
419
420    #[cfg(feature = "compiler")]
421    pub fn get_sys_compiler_engine_for_target(
422        &self,
423        target: Target,
424    ) -> std::result::Result<Engine, anyhow::Error> {
425        let backends = self.get_available_backends()?;
426        let compiler_config = self.get_sys_compiler_config(backends.first().unwrap())?;
427        let default_features = compiler_config.default_features_for_target(&target);
428        let features = self.get_features(&default_features)?;
429        Ok(wasmer_compiler::EngineBuilder::new(compiler_config)
430            .set_features(Some(features))
431            .set_target(Some(target))
432            .engine()
433            .into())
434    }
435
436    #[allow(unused_variables)]
437    #[cfg(feature = "compiler")]
438    pub(crate) fn get_sys_compiler_config(
439        &self,
440        rt: &BackendType,
441    ) -> Result<Box<dyn CompilerConfig>> {
442        self.validate_profiler()?;
443        let compiler_config: Box<dyn CompilerConfig> = match rt {
444            BackendType::Headless => bail!("The headless engine can't be chosen"),
445            #[cfg(feature = "singlepass")]
446            BackendType::Singlepass => {
447                let mut config = wasmer_compiler_singlepass::Singlepass::new();
448                if self.enable_experimental_unaligned_memory_accesses {
449                    config.allow_experimental_unaligned_memory_accesses(true);
450                }
451                if self.enable_nan_canonicalization {
452                    config.canonicalize_nans(true);
453                }
454                if let Some(p) = &self.profiler {
455                    match p {
456                        Profiler::Perfmap => config.enable_perfmap(),
457                        Profiler::Gdb => config.enable_debugger(Debugger::Gdb),
458                        Profiler::Lldb => config.enable_debugger(Debugger::Lldb),
459                    }
460                }
461                if let Some(mut debug_dir) = self.compiler_debug_dir.clone() {
462                    use wasmer_compiler_singlepass::SinglepassCallbacks;
463
464                    debug_dir.push("singlepass");
465                    config.callbacks(Some(SinglepassCallbacks::new(debug_dir)?));
466                }
467                if let Some(num_threads) = self.compiler_threads {
468                    config.num_threads(num_threads);
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_experimental_unaligned_memory_accesses {
476                    config.allow_experimental_unaligned_memory_accesses(true);
477                }
478                if self.enable_verifier {
479                    config.enable_verifier();
480                }
481                if self.enable_nan_canonicalization {
482                    config.canonicalize_nans(true);
483                }
484                if let Some(p) = &self.profiler {
485                    match p {
486                        Profiler::Perfmap => config.enable_perfmap(),
487                        Profiler::Gdb => config.enable_debugger(Debugger::Gdb),
488                        Profiler::Lldb => config.enable_debugger(Debugger::Lldb),
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                if self.disable_m0_pass_param_opt {
508                    config.enable_m0_pass_param(false);
509                }
510                if !self.disable_non_volatile_memops {
511                    config.enable_non_volatile_memops();
512                }
513                config.enable_readonly_funcref_table();
514
515                if let Some(num_threads) = self.compiler_threads {
516                    config.num_threads(num_threads);
517                }
518
519                if let Some(mut debug_dir) = self.compiler_debug_dir.clone() {
520                    debug_dir.push("llvm");
521                    config.callbacks(Some(LLVMCallbacks::new(debug_dir)?));
522                    config.verbose_asm(true);
523                }
524                if self.enable_nan_canonicalization {
525                    config.canonicalize_nans(true);
526                }
527                if let Some(p) = &self.profiler {
528                    match p {
529                        Profiler::Perfmap => config.enable_perfmap(),
530                        Profiler::Gdb => config.enable_debugger(Debugger::Gdb),
531                        Profiler::Lldb => config.enable_debugger(Debugger::Lldb),
532                    }
533                }
534
535                Box::new(config)
536            }
537            BackendType::V8 => unreachable!(),
538            #[cfg(not(all(feature = "singlepass", feature = "cranelift", feature = "llvm")))]
539            compiler => {
540                bail!("The `{compiler}` compiler is not included in this binary.")
541            }
542        };
543
544        #[allow(unreachable_code)]
545        {
546            let mut compiler_config = compiler_config;
547            if self.experimental_artifact {
548                compiler_config.experimental_artifact(true);
549            }
550            Ok(compiler_config)
551        }
552    }
553}
554
555/// The compiler used for the store
556#[derive(Debug, PartialEq, Eq, Clone, Copy)]
557#[allow(clippy::upper_case_acronyms, dead_code)]
558pub enum BackendType {
559    /// Singlepass compiler
560    Singlepass,
561
562    /// Cranelift compiler
563    Cranelift,
564
565    /// LLVM compiler
566    LLVM,
567
568    /// V8 runtime
569    V8,
570
571    /// Headless compiler
572    #[allow(dead_code)]
573    Headless,
574}
575
576impl BackendType {
577    /// Return all enabled compilers
578    pub fn enabled() -> Vec<Self> {
579        vec![
580            #[cfg(feature = "cranelift")]
581            Self::Cranelift,
582            #[cfg(feature = "llvm")]
583            Self::LLVM,
584            #[cfg(feature = "singlepass")]
585            Self::Singlepass,
586            #[cfg(feature = "v8")]
587            Self::V8,
588        ]
589    }
590
591    /// Returns an engine for this backend type.
592    /// We enable every feature the engine supports, since the same engine may later be used
593    /// with a module that requires more features than the one used during engine detection.
594    pub fn get_engine(&self, target: &Target, runtime_opts: &RuntimeOptions) -> Result<Engine> {
595        runtime_opts.validate_profiler()?;
596        match self {
597            #[cfg(feature = "singlepass")]
598            Self::Singlepass => {
599                let mut config = wasmer_compiler_singlepass::Singlepass::new();
600                if runtime_opts.experimental_artifact {
601                    config.experimental_artifact(true);
602                }
603                if runtime_opts.enable_experimental_unaligned_memory_accesses {
604                    config.allow_experimental_unaligned_memory_accesses(true);
605                }
606                let supported_features = config.supported_features_for_target(target);
607                if runtime_opts.enable_nan_canonicalization {
608                    config.canonicalize_nans(true);
609                }
610                if let Some(p) = &runtime_opts.profiler {
611                    match p {
612                        Profiler::Perfmap => config.enable_perfmap(),
613                        Profiler::Gdb => config.enable_debugger(Debugger::Gdb),
614                        Profiler::Lldb => config.enable_debugger(Debugger::Lldb),
615                    }
616                }
617                if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
618                    use wasmer_compiler_singlepass::SinglepassCallbacks;
619
620                    debug_dir.push("singlepass");
621                    config.callbacks(Some(SinglepassCallbacks::new(debug_dir)?));
622                }
623                if let Some(num_threads) = runtime_opts.compiler_threads {
624                    config.num_threads(num_threads);
625                }
626                let engine = wasmer_compiler::EngineBuilder::new(config)
627                    .set_features(Some(supported_features))
628                    .set_target(Some(target.clone()))
629                    .engine()
630                    .into();
631                Ok(engine)
632            }
633            #[cfg(feature = "cranelift")]
634            Self::Cranelift => {
635                let mut config = wasmer_compiler_cranelift::Cranelift::new();
636                if runtime_opts.experimental_artifact {
637                    config.experimental_artifact(true);
638                }
639                if runtime_opts.enable_experimental_unaligned_memory_accesses {
640                    config.allow_experimental_unaligned_memory_accesses(true);
641                }
642                let supported_features = config.supported_features_for_target(target);
643                if runtime_opts.enable_verifier {
644                    config.enable_verifier();
645                }
646                if runtime_opts.enable_nan_canonicalization {
647                    config.canonicalize_nans(true);
648                }
649                if let Some(p) = &runtime_opts.profiler {
650                    match p {
651                        Profiler::Perfmap => config.enable_perfmap(),
652                        Profiler::Gdb => config.enable_debugger(Debugger::Gdb),
653                        Profiler::Lldb => config.enable_debugger(Debugger::Lldb),
654                    }
655                }
656                if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
657                    use wasmer_compiler_cranelift::CraneliftCallbacks;
658
659                    debug_dir.push("cranelift");
660                    config.callbacks(Some(CraneliftCallbacks::new(debug_dir)?));
661                }
662                if let Some(num_threads) = runtime_opts.compiler_threads {
663                    config.num_threads(num_threads);
664                }
665                let engine = wasmer_compiler::EngineBuilder::new(config)
666                    .set_features(Some(supported_features))
667                    .set_target(Some(target.clone()))
668                    .engine()
669                    .into();
670                Ok(engine)
671            }
672            #[cfg(feature = "llvm")]
673            Self::LLVM => {
674                use wasmer_compiler_llvm::LLVMCallbacks;
675                use wasmer_types::entity::EntityRef;
676
677                let mut config = wasmer_compiler_llvm::LLVM::new();
678                if runtime_opts.disable_m0_pass_param_opt {
679                    config.enable_m0_pass_param(false);
680                }
681                if runtime_opts.experimental_artifact {
682                    config.experimental_artifact(true);
683                }
684                if !runtime_opts.disable_non_volatile_memops {
685                    config.enable_non_volatile_memops();
686                }
687                config.enable_readonly_funcref_table();
688
689                let supported_features = config.supported_features_for_target(target);
690                if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
691                    debug_dir.push("llvm");
692                    config.callbacks(Some(LLVMCallbacks::new(debug_dir)?));
693                    config.verbose_asm(true);
694                }
695                if runtime_opts.enable_nan_canonicalization {
696                    config.canonicalize_nans(true);
697                }
698
699                if let Some(num_threads) = runtime_opts.compiler_threads {
700                    config.num_threads(num_threads);
701                }
702
703                if let Some(p) = &runtime_opts.profiler {
704                    match p {
705                        Profiler::Perfmap => config.enable_perfmap(),
706                        Profiler::Gdb => config.enable_debugger(Debugger::Gdb),
707                        Profiler::Lldb => config.enable_debugger(Debugger::Lldb),
708                    }
709                }
710
711                let engine = wasmer_compiler::EngineBuilder::new(config)
712                    .set_features(Some(supported_features))
713                    .set_target(Some(target.clone()))
714                    .engine()
715                    .into();
716                Ok(engine)
717            }
718            #[cfg(feature = "v8")]
719            Self::V8 => Ok(wasmer::v8::V8::new().into()),
720            Self::Headless => bail!("Headless is not a valid runtime to instantiate directly"),
721            #[allow(unreachable_patterns)]
722            _ => bail!("Unsupported backend type"),
723        }
724    }
725
726    /// Check if this backend supports all the required WebAssembly features
727    #[allow(unreachable_code)]
728    pub fn supports_features(&self, required_features: &Features, target: &Target) -> bool {
729        // Map BackendType to the corresponding wasmer::BackendKind
730        let backend_kind = match self {
731            #[cfg(feature = "singlepass")]
732            Self::Singlepass => wasmer::BackendKind::Singlepass,
733            #[cfg(feature = "cranelift")]
734            Self::Cranelift => wasmer::BackendKind::Cranelift,
735            #[cfg(feature = "llvm")]
736            Self::LLVM => wasmer::BackendKind::LLVM,
737            #[cfg(feature = "v8")]
738            Self::V8 => wasmer::BackendKind::V8,
739            Self::Headless => return false, // Headless can't compile
740            #[allow(unreachable_patterns)]
741            _ => return false,
742        };
743
744        // Get the supported features from the backend
745        let supported = wasmer::Engine::supported_features_for_backend(&backend_kind, target);
746
747        // Check if the backend supports all required features
748        if !supported.contains_features(required_features) {
749            return false;
750        }
751
752        true
753    }
754}
755
756impl From<&BackendType> for wasmer::BackendKind {
757    fn from(backend_type: &BackendType) -> Self {
758        match backend_type {
759            #[cfg(feature = "singlepass")]
760            BackendType::Singlepass => wasmer::BackendKind::Singlepass,
761            #[cfg(feature = "cranelift")]
762            BackendType::Cranelift => wasmer::BackendKind::Cranelift,
763            #[cfg(feature = "llvm")]
764            BackendType::LLVM => wasmer::BackendKind::LLVM,
765            #[cfg(feature = "v8")]
766            BackendType::V8 => wasmer::BackendKind::V8,
767            _ => {
768                #[cfg(feature = "sys")]
769                {
770                    wasmer::BackendKind::Headless
771                }
772                #[cfg(not(feature = "sys"))]
773                {
774                    unreachable!("No backend enabled!")
775                }
776            }
777        }
778    }
779}
780
781impl std::fmt::Display for BackendType {
782    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
783        write!(
784            f,
785            "{}",
786            match self {
787                Self::Singlepass => "singlepass",
788                Self::Cranelift => "cranelift",
789                Self::LLVM => "llvm",
790                Self::V8 => "v8",
791                Self::Headless => "headless",
792            }
793        )
794    }
795}