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, LLVM and Singlepass.
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    /// Sets the number of threads used to compile the input module(s).
170    #[clap(long, alias = "llvm-num-threads")]
171    compiler_threads: Option<NonZero<usize>>,
172
173    /// Enable NaN canonicalization during compilation to produce deterministic
174    /// canonical quiet NaNs (QNaNs) across architectures.
175    #[clap(long = "enable-nan-canonicalization")]
176    enable_nan_canonicalization: bool,
177
178    /// Disable LLVM non-volatile memory operations.
179    ///
180    /// Available for LLVM.
181    #[cfg(feature = "llvm")]
182    #[clap(long = "disable-non-volatile-memops")]
183    disable_non_volatile_memops: bool,
184
185    /// Allow unaligned memory accesses.
186    ///
187    /// This feature is experimental and currently supports only Cranelift scalar types
188    /// and Singlepass on RISC-V for integral types.
189    #[clap(long = "enable-experimental-unaligned-memory-accesses")]
190    enable_experimental_unaligned_memory_accesses: bool,
191
192    #[clap(flatten)]
193    features: WasmFeatures,
194}
195
196#[derive(Clone, Debug)]
197pub enum Profiler {
198    /// Perfmap-based profilers.
199    Perfmap,
200    /// GDB command file.
201    Gdb,
202    /// LLDB command file.
203    Lldb,
204}
205
206impl FromStr for Profiler {
207    type Err = anyhow::Error;
208
209    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
210        match s.to_lowercase().as_str() {
211            "perfmap" => Ok(Self::Perfmap),
212            "gdb" => Ok(Self::Gdb),
213            "lldb" => Ok(Self::Lldb),
214            _ => Err(anyhow::anyhow!("Unrecognized profiler: {s}")),
215        }
216    }
217}
218
219impl RuntimeOptions {
220    fn validate_profiler(&self) -> Result<()> {
221        if self.experimental_artifact && !cfg!(target_os = "linux") {
222            bail!("--experimental-artifact is only supported on Linux");
223        }
224        if !self.experimental_artifact {
225            match self.profiler {
226                Some(Profiler::Gdb) => {
227                    bail!("The gdb profiler requires --experimental-artifact")
228                }
229                Some(Profiler::Lldb) => {
230                    bail!("The lldb profiler requires --experimental-artifact")
231                }
232                _ => {}
233            }
234        }
235        Ok(())
236    }
237
238    pub fn get_available_backends(&self) -> Result<Vec<BackendType>> {
239        // If a specific backend is explicitly requested, use it
240        #[cfg(feature = "cranelift")]
241        {
242            if self.cranelift {
243                return Ok(vec![BackendType::Cranelift]);
244            }
245        }
246
247        #[cfg(feature = "llvm")]
248        {
249            if self.llvm {
250                return Ok(vec![BackendType::LLVM]);
251            }
252        }
253
254        #[cfg(feature = "singlepass")]
255        {
256            if self.singlepass {
257                return Ok(vec![BackendType::Singlepass]);
258            }
259        }
260
261        #[cfg(feature = "v8")]
262        {
263            if self.v8 {
264                return Ok(vec![BackendType::V8]);
265            }
266        }
267
268        Ok(BackendType::enabled())
269    }
270
271    /// Filter enabled backends based on required WebAssembly features
272    pub fn filter_backends_by_features(
273        backends: Vec<BackendType>,
274        required_features: &Features,
275        target: &Target,
276    ) -> Vec<BackendType> {
277        backends
278            .into_iter()
279            .filter(|backend| backend.supports_features(required_features, target))
280            .collect()
281    }
282
283    pub fn get_store(&self) -> Result<Store> {
284        let engine = self.get_engine(&Target::default())?;
285        Ok(Store::new(engine))
286    }
287
288    pub fn get_engine(&self, target: &Target) -> Result<Engine> {
289        let backends = self.get_available_backends()?;
290        let backend = backends.first().context("no compiler backend enabled")?;
291        backend.get_engine(target, self)
292    }
293
294    pub fn get_engine_for_module(&self, module_contents: &[u8], target: &Target) -> Result<Engine> {
295        let required_features = self
296            .detect_features_from_wasm(module_contents)
297            .unwrap_or_default();
298
299        self.get_engine_for_features(&required_features, target)
300    }
301
302    pub fn get_engine_for_features(
303        &self,
304        required_features: &Features,
305        target: &Target,
306    ) -> Result<Engine> {
307        let backends = self.get_available_backends()?;
308        let filtered_backends =
309            Self::filter_backends_by_features(backends.clone(), required_features, target);
310
311        if filtered_backends.is_empty() {
312            let enabled_backends = BackendType::enabled();
313            if backends.len() == 1 && enabled_backends.len() > 1 {
314                // If the user has chosen an specific backend, we can suggest to use another one
315                let filtered_backends =
316                    Self::filter_backends_by_features(enabled_backends, required_features, target);
317                let extra_text: String = if !filtered_backends.is_empty() {
318                    format!(". You can use --{} instead", filtered_backends[0])
319                } else {
320                    "".to_string()
321                };
322                bail!(
323                    "The {} backend does not support the required features for the Wasm module{}",
324                    backends[0],
325                    extra_text
326                );
327            } else {
328                bail!(
329                    "No backends support the required features for the Wasm module. Feel free to open an issue at https://github.com/wasmerio/wasmer/issues"
330                );
331            }
332        }
333        filtered_backends.first().unwrap().get_engine(target, self)
334    }
335
336    #[cfg(feature = "compiler")]
337    /// Get the enabled Wasm features.
338    pub fn get_features(&self, default_features: &Features) -> Result<Features> {
339        if self.features.all {
340            return Ok(Features::all());
341        }
342
343        let mut result = default_features.clone();
344        if !self.features.disable_threads {
345            result.threads(true);
346        }
347        if self.features.disable_threads {
348            result.threads(false);
349        }
350        if self.features.multi_value {
351            result.multi_value(true);
352        }
353        if self.features.simd {
354            result.simd(true);
355        }
356        if self.features.bulk_memory {
357            result.bulk_memory(true);
358        }
359        if self.features.reference_types {
360            result.reference_types(true);
361        }
362        Ok(result)
363    }
364
365    #[cfg(feature = "compiler")]
366    /// Get a copy of the default features with user-configured options
367    pub fn get_configured_features(&self) -> Result<Features> {
368        let features = Features::default();
369        self.get_features(&features)
370    }
371
372    /// Detect features from a WebAssembly module binary.
373    pub fn detect_features_from_wasm(
374        &self,
375        wasm_bytes: &[u8],
376    ) -> Result<Features, wasmparser::BinaryReaderError> {
377        if self.features.all {
378            return Ok(Features::all());
379        }
380
381        let mut features = Features::detect_from_wasm(wasm_bytes)?;
382
383        // Merge with user-configured features
384        if !self.features.disable_threads {
385            features.threads(true);
386        }
387        if self.features.reference_types {
388            features.reference_types(true);
389        }
390        if self.features.simd {
391            features.simd(true);
392        }
393        if self.features.bulk_memory {
394            features.bulk_memory(true);
395        }
396        if self.features.multi_value {
397            features.multi_value(true);
398        }
399        if self.features.tail_call {
400            features.tail_call(true);
401        }
402        if self.features.module_linking {
403            features.module_linking(true);
404        }
405        if self.features.memory64 {
406            features.memory64(true);
407        }
408        if self.features.exceptions {
409            features.exceptions(true);
410        }
411
412        Ok(features)
413    }
414
415    #[cfg(feature = "compiler")]
416    pub fn get_sys_compiler_engine_for_target(
417        &self,
418        target: Target,
419    ) -> std::result::Result<Engine, anyhow::Error> {
420        let backends = self.get_available_backends()?;
421        let compiler_config = self.get_sys_compiler_config(backends.first().unwrap())?;
422        let default_features = compiler_config.default_features_for_target(&target);
423        let features = self.get_features(&default_features)?;
424        Ok(wasmer_compiler::EngineBuilder::new(compiler_config)
425            .set_features(Some(features))
426            .set_target(Some(target))
427            .engine()
428            .into())
429    }
430
431    #[allow(unused_variables)]
432    #[cfg(feature = "compiler")]
433    pub(crate) fn get_sys_compiler_config(
434        &self,
435        rt: &BackendType,
436    ) -> Result<Box<dyn CompilerConfig>> {
437        self.validate_profiler()?;
438        let compiler_config: Box<dyn CompilerConfig> = match rt {
439            BackendType::Headless => bail!("The headless engine can't be chosen"),
440            #[cfg(feature = "singlepass")]
441            BackendType::Singlepass => {
442                let mut config = wasmer_compiler_singlepass::Singlepass::new();
443                if self.enable_experimental_unaligned_memory_accesses {
444                    config.allow_experimental_unaligned_memory_accesses(true);
445                }
446                if self.enable_verifier {
447                    config.enable_verifier();
448                }
449                if self.enable_nan_canonicalization {
450                    config.canonicalize_nans(true);
451                }
452                if let Some(p) = &self.profiler {
453                    match p {
454                        Profiler::Perfmap => config.enable_perfmap(),
455                        Profiler::Gdb => config.enable_debugger(Debugger::Gdb),
456                        Profiler::Lldb => config.enable_debugger(Debugger::Lldb),
457                    }
458                }
459                if let Some(mut debug_dir) = self.compiler_debug_dir.clone() {
460                    use wasmer_compiler_singlepass::SinglepassCallbacks;
461
462                    debug_dir.push("singlepass");
463                    config.callbacks(Some(SinglepassCallbacks::new(debug_dir)?));
464                }
465                if let Some(num_threads) = self.compiler_threads {
466                    config.num_threads(num_threads);
467                }
468                Box::new(config)
469            }
470            #[cfg(feature = "cranelift")]
471            BackendType::Cranelift => {
472                let mut config = wasmer_compiler_cranelift::Cranelift::new();
473                if self.enable_experimental_unaligned_memory_accesses {
474                    config.allow_experimental_unaligned_memory_accesses(true);
475                }
476                if self.enable_verifier {
477                    config.enable_verifier();
478                }
479                if self.enable_nan_canonicalization {
480                    config.canonicalize_nans(true);
481                }
482                if let Some(p) = &self.profiler {
483                    match p {
484                        Profiler::Perfmap => config.enable_perfmap(),
485                        Profiler::Gdb => config.enable_debugger(Debugger::Gdb),
486                        Profiler::Lldb => config.enable_debugger(Debugger::Lldb),
487                    }
488                }
489                if let Some(mut debug_dir) = self.compiler_debug_dir.clone() {
490                    use wasmer_compiler_cranelift::CraneliftCallbacks;
491
492                    debug_dir.push("cranelift");
493                    config.callbacks(Some(CraneliftCallbacks::new(debug_dir)?));
494                }
495                if let Some(num_threads) = self.compiler_threads {
496                    config.num_threads(num_threads);
497                }
498                Box::new(config)
499            }
500            #[cfg(feature = "llvm")]
501            BackendType::LLVM => {
502                use wasmer_compiler_llvm::LLVMCallbacks;
503                use wasmer_types::entity::EntityRef;
504                let mut config = LLVM::new();
505                if !self.disable_non_volatile_memops {
506                    config.enable_non_volatile_memops();
507                }
508                config.enable_readonly_funcref_table();
509
510                if let Some(num_threads) = self.compiler_threads {
511                    config.num_threads(num_threads);
512                }
513
514                if let Some(mut debug_dir) = self.compiler_debug_dir.clone() {
515                    debug_dir.push("llvm");
516                    config.callbacks(Some(LLVMCallbacks::new(debug_dir)?));
517                    config.verbose_asm(true);
518                }
519                if self.enable_verifier {
520                    config.enable_verifier();
521                }
522                if self.enable_nan_canonicalization {
523                    config.canonicalize_nans(true);
524                }
525                if let Some(p) = &self.profiler {
526                    match p {
527                        Profiler::Perfmap => config.enable_perfmap(),
528                        Profiler::Gdb => config.enable_debugger(Debugger::Gdb),
529                        Profiler::Lldb => config.enable_debugger(Debugger::Lldb),
530                    }
531                }
532
533                Box::new(config)
534            }
535            BackendType::V8 => unreachable!(),
536            #[cfg(not(all(feature = "singlepass", feature = "cranelift", feature = "llvm")))]
537            compiler => {
538                bail!("The `{compiler}` compiler is not included in this binary.")
539            }
540        };
541
542        #[allow(unreachable_code)]
543        {
544            let mut compiler_config = compiler_config;
545            if self.experimental_artifact {
546                compiler_config.experimental_artifact(true);
547            }
548            Ok(compiler_config)
549        }
550    }
551}
552
553/// The compiler used for the store
554#[derive(Debug, PartialEq, Eq, Clone, Copy)]
555#[allow(clippy::upper_case_acronyms, dead_code)]
556pub enum BackendType {
557    /// Singlepass compiler
558    Singlepass,
559
560    /// Cranelift compiler
561    Cranelift,
562
563    /// LLVM compiler
564    LLVM,
565
566    /// V8 runtime
567    V8,
568
569    /// Headless compiler
570    #[allow(dead_code)]
571    Headless,
572}
573
574impl BackendType {
575    /// Return all enabled compilers
576    pub fn enabled() -> Vec<Self> {
577        vec![
578            #[cfg(feature = "cranelift")]
579            Self::Cranelift,
580            #[cfg(feature = "llvm")]
581            Self::LLVM,
582            #[cfg(feature = "singlepass")]
583            Self::Singlepass,
584            #[cfg(feature = "v8")]
585            Self::V8,
586        ]
587    }
588
589    /// Returns an engine for this backend type.
590    /// We enable every feature the engine supports, since the same engine may later be used
591    /// with a module that requires more features than the one used during engine detection.
592    pub fn get_engine(&self, target: &Target, runtime_opts: &RuntimeOptions) -> Result<Engine> {
593        runtime_opts.validate_profiler()?;
594        match self {
595            #[cfg(feature = "singlepass")]
596            Self::Singlepass => {
597                let mut config = wasmer_compiler_singlepass::Singlepass::new();
598                if runtime_opts.experimental_artifact {
599                    config.experimental_artifact(true);
600                }
601                if runtime_opts.enable_experimental_unaligned_memory_accesses {
602                    config.allow_experimental_unaligned_memory_accesses(true);
603                }
604                let supported_features = config.supported_features_for_target(target);
605                if runtime_opts.enable_verifier {
606                    config.enable_verifier();
607                }
608                if runtime_opts.enable_nan_canonicalization {
609                    config.canonicalize_nans(true);
610                }
611                if let Some(p) = &runtime_opts.profiler {
612                    match p {
613                        Profiler::Perfmap => config.enable_perfmap(),
614                        Profiler::Gdb => config.enable_debugger(Debugger::Gdb),
615                        Profiler::Lldb => config.enable_debugger(Debugger::Lldb),
616                    }
617                }
618                if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
619                    use wasmer_compiler_singlepass::SinglepassCallbacks;
620
621                    debug_dir.push("singlepass");
622                    config.callbacks(Some(SinglepassCallbacks::new(debug_dir)?));
623                }
624                if let Some(num_threads) = runtime_opts.compiler_threads {
625                    config.num_threads(num_threads);
626                }
627                let engine = wasmer_compiler::EngineBuilder::new(config)
628                    .set_features(Some(supported_features))
629                    .set_target(Some(target.clone()))
630                    .engine()
631                    .into();
632                Ok(engine)
633            }
634            #[cfg(feature = "cranelift")]
635            Self::Cranelift => {
636                let mut config = wasmer_compiler_cranelift::Cranelift::new();
637                if runtime_opts.experimental_artifact {
638                    config.experimental_artifact(true);
639                }
640                if runtime_opts.enable_experimental_unaligned_memory_accesses {
641                    config.allow_experimental_unaligned_memory_accesses(true);
642                }
643                let supported_features = config.supported_features_for_target(target);
644                if runtime_opts.enable_verifier {
645                    config.enable_verifier();
646                }
647                if runtime_opts.enable_nan_canonicalization {
648                    config.canonicalize_nans(true);
649                }
650                if let Some(p) = &runtime_opts.profiler {
651                    match p {
652                        Profiler::Perfmap => config.enable_perfmap(),
653                        Profiler::Gdb => config.enable_debugger(Debugger::Gdb),
654                        Profiler::Lldb => config.enable_debugger(Debugger::Lldb),
655                    }
656                }
657                if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
658                    use wasmer_compiler_cranelift::CraneliftCallbacks;
659
660                    debug_dir.push("cranelift");
661                    config.callbacks(Some(CraneliftCallbacks::new(debug_dir)?));
662                }
663                if let Some(num_threads) = runtime_opts.compiler_threads {
664                    config.num_threads(num_threads);
665                }
666                let engine = wasmer_compiler::EngineBuilder::new(config)
667                    .set_features(Some(supported_features))
668                    .set_target(Some(target.clone()))
669                    .engine()
670                    .into();
671                Ok(engine)
672            }
673            #[cfg(feature = "llvm")]
674            Self::LLVM => {
675                use wasmer_compiler_llvm::LLVMCallbacks;
676                use wasmer_types::entity::EntityRef;
677
678                let mut config = wasmer_compiler_llvm::LLVM::new();
679                if runtime_opts.experimental_artifact {
680                    config.experimental_artifact(true);
681                }
682                if !runtime_opts.disable_non_volatile_memops {
683                    config.enable_non_volatile_memops();
684                }
685                config.enable_readonly_funcref_table();
686
687                let supported_features = config.supported_features_for_target(target);
688                if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
689                    debug_dir.push("llvm");
690                    config.callbacks(Some(LLVMCallbacks::new(debug_dir)?));
691                    config.verbose_asm(true);
692                }
693                if runtime_opts.enable_verifier {
694                    config.enable_verifier();
695                }
696                if runtime_opts.enable_nan_canonicalization {
697                    config.canonicalize_nans(true);
698                }
699
700                if let Some(num_threads) = runtime_opts.compiler_threads {
701                    config.num_threads(num_threads);
702                }
703
704                if let Some(p) = &runtime_opts.profiler {
705                    match p {
706                        Profiler::Perfmap => config.enable_perfmap(),
707                        Profiler::Gdb => config.enable_debugger(Debugger::Gdb),
708                        Profiler::Lldb => config.enable_debugger(Debugger::Lldb),
709                    }
710                }
711
712                let engine = wasmer_compiler::EngineBuilder::new(config)
713                    .set_features(Some(supported_features))
714                    .set_target(Some(target.clone()))
715                    .engine()
716                    .into();
717                Ok(engine)
718            }
719            #[cfg(feature = "v8")]
720            Self::V8 => Ok(wasmer::v8::V8::new().into()),
721            Self::Headless => bail!("Headless is not a valid runtime to instantiate directly"),
722            #[allow(unreachable_patterns)]
723            _ => bail!("Unsupported backend type"),
724        }
725    }
726
727    /// Check if this backend supports all the required WebAssembly features
728    #[allow(unreachable_code)]
729    pub fn supports_features(&self, required_features: &Features, target: &Target) -> bool {
730        // Map BackendType to the corresponding wasmer::BackendKind
731        let backend_kind = match self {
732            #[cfg(feature = "singlepass")]
733            Self::Singlepass => wasmer::BackendKind::Singlepass,
734            #[cfg(feature = "cranelift")]
735            Self::Cranelift => wasmer::BackendKind::Cranelift,
736            #[cfg(feature = "llvm")]
737            Self::LLVM => wasmer::BackendKind::LLVM,
738            #[cfg(feature = "v8")]
739            Self::V8 => wasmer::BackendKind::V8,
740            Self::Headless => return false, // Headless can't compile
741            #[allow(unreachable_patterns)]
742            _ => return false,
743        };
744
745        // Get the supported features from the backend
746        let supported = wasmer::Engine::supported_features_for_backend(&backend_kind, target);
747
748        // Check if the backend supports all required features
749        if !supported.contains_features(required_features) {
750            return false;
751        }
752
753        true
754    }
755}
756
757impl From<&BackendType> for wasmer::BackendKind {
758    fn from(backend_type: &BackendType) -> Self {
759        match backend_type {
760            #[cfg(feature = "singlepass")]
761            BackendType::Singlepass => wasmer::BackendKind::Singlepass,
762            #[cfg(feature = "cranelift")]
763            BackendType::Cranelift => wasmer::BackendKind::Cranelift,
764            #[cfg(feature = "llvm")]
765            BackendType::LLVM => wasmer::BackendKind::LLVM,
766            #[cfg(feature = "v8")]
767            BackendType::V8 => wasmer::BackendKind::V8,
768            _ => {
769                #[cfg(feature = "sys")]
770                {
771                    wasmer::BackendKind::Headless
772                }
773                #[cfg(not(feature = "sys"))]
774                {
775                    unreachable!("No backend enabled!")
776                }
777            }
778        }
779    }
780}
781
782impl std::fmt::Display for BackendType {
783    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
784        write!(
785            f,
786            "{}",
787            match self {
788                Self::Singlepass => "singlepass",
789                Self::Cranelift => "cranelift",
790                Self::LLVM => "llvm",
791                Self::V8 => "v8",
792                Self::Headless => "headless",
793            }
794        )
795    }
796}