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.
46    #[clap(long = "enable-multi-value")]
47    pub multi_value: bool,
48
49    /// Enable support for the bulk memory proposal.
50    #[clap(long = "enable-bulk-memory")]
51    pub bulk_memory: bool,
52
53    /// Enable support for the tail call proposal.
54    #[clap(long = "enable-tail-call")]
55    pub tail_call: bool,
56
57    /// Enable support for the module linking proposal.
58    #[clap(long = "enable-module-linking")]
59    pub module_linking: bool,
60
61    /// Deprecated, multi memory is enabled by default.
62    #[clap(long = "enable-multi-memory")]
63    pub _multi_memory: bool,
64
65    /// Enable support for the memory64 proposal.
66    #[clap(long = "enable-memory64")]
67    pub memory64: bool,
68
69    /// Enable support for the exceptions proposal.
70    #[clap(long = "enable-exceptions")]
71    pub exceptions: bool,
72
73    /// Enable support for the relaxed SIMD proposal.
74    #[clap(long = "enable-relaxed-simd")]
75    pub relaxed_simd: bool,
76
77    /// Enable support for the extended constant expressions proposal.
78    #[clap(long = "enable-extended-const")]
79    pub extended_const: bool,
80
81    /// Enable support for the wide arithmetic proposal.
82    #[clap(long = "wide-arithmetic")]
83    pub wide_arithmetic: bool,
84
85    /// Enable support for all pre-standard proposals.
86    #[clap(long = "enable-all")]
87    pub all: bool,
88}
89
90#[derive(Debug, Clone, clap::Parser, Default)]
91/// The compiler options
92pub struct RuntimeOptions {
93    /// Use Singlepass compiler.
94    #[cfg(feature = "singlepass")]
95    #[clap(short, long, conflicts_with_all = &Vec::<&str>::from_iter([
96        #[cfg(feature = "llvm")]
97        "llvm", 
98        #[cfg(feature = "v8")]
99        "v8", 
100        #[cfg(feature = "cranelift")]
101        "cranelift",         
102    ]))]
103    singlepass: bool,
104
105    /// Use Cranelift compiler.
106    #[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    ]))]
115    cranelift: bool,
116
117    /// Use LLVM compiler.
118    #[cfg(feature = "llvm")]
119    #[clap(short, long, conflicts_with_all = &Vec::<&str>::from_iter([
120        #[cfg(feature = "cranelift")]
121        "cranelift", 
122        #[cfg(feature = "v8")]
123        "v8", 
124        #[cfg(feature = "singlepass")]
125        "singlepass", 
126    ]))]
127    llvm: bool,
128
129    /// Use the V8 runtime.
130    #[cfg(feature = "v8")]
131    #[clap(long, conflicts_with_all = &Vec::<&str>::from_iter([
132        #[cfg(feature = "cranelift")]
133        "cranelift", 
134        #[cfg(feature = "llvm")]
135        "llvm", 
136        #[cfg(feature = "singlepass")]
137        "singlepass", 
138    ]))]
139    v8: bool,
140
141    /// Use the experimental artifact format.
142    #[clap(long = "experimental-artifact")]
143    experimental_artifact: bool,
144
145    /// Enable compiler internal verification.
146    ///
147    /// Available for Cranelift, LLVM and Singlepass.
148    #[clap(long)]
149    enable_verifier: bool,
150
151    /// Debug directory, where IR and object files will be written to.
152    ///
153    /// Available for Cranelift, LLVM and Singlepass.
154    #[clap(long, alias = "llvm-debug-dir")]
155    pub(crate) compiler_debug_dir: Option<PathBuf>,
156
157    /// Enable a profiler.
158    ///
159    /// Available for Cranelift, LLVM and Singlepass.
160    #[clap(long, value_enum)]
161    profiler: Option<Profiler>,
162
163    /// Deprecated option as m0 optimization always play role if we use a static memory
164    #[cfg(feature = "llvm")]
165    #[clap(long, hide = true)]
166    _enable_pass_params_opt: bool,
167
168    /// Sets the number of threads used to compile the input module(s).
169    #[clap(long, alias = "llvm-num-threads")]
170    compiler_threads: Option<NonZero<usize>>,
171
172    /// Enable NaN canonicalization during compilation to produce deterministic
173    /// canonical quiet NaNs (QNaNs) across architectures.
174    #[clap(long = "enable-nan-canonicalization")]
175    enable_nan_canonicalization: bool,
176
177    /// Disable LLVM non-volatile memory operations.
178    ///
179    /// Available for LLVM.
180    #[cfg(feature = "llvm")]
181    #[clap(long = "disable-non-volatile-memops")]
182    disable_non_volatile_memops: bool,
183
184    /// Allow unaligned memory accesses.
185    ///
186    /// This feature is experimental and currently supports only Cranelift scalar types
187    /// and Singlepass on RISC-V for integral types.
188    #[clap(long = "enable-experimental-unaligned-memory-accesses")]
189    enable_experimental_unaligned_memory_accesses: bool,
190
191    #[clap(flatten)]
192    features: WasmFeatures,
193}
194
195#[derive(Clone, Debug)]
196pub enum Profiler {
197    /// Perfmap-based profilers.
198    Perfmap,
199    /// GDB command file.
200    Gdb,
201    /// LLDB command file.
202    Lldb,
203}
204
205impl FromStr for Profiler {
206    type Err = anyhow::Error;
207
208    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
209        match s.to_lowercase().as_str() {
210            "perfmap" => Ok(Self::Perfmap),
211            "gdb" => Ok(Self::Gdb),
212            "lldb" => Ok(Self::Lldb),
213            _ => Err(anyhow::anyhow!("Unrecognized profiler: {s}")),
214        }
215    }
216}
217
218impl RuntimeOptions {
219    fn validate_profiler(&self) -> Result<()> {
220        if self.experimental_artifact && !cfg!(target_os = "linux") {
221            bail!("--experimental-artifact is only supported on Linux");
222        }
223        if !self.experimental_artifact {
224            match self.profiler {
225                Some(Profiler::Gdb) => {
226                    bail!("The gdb profiler requires --experimental-artifact")
227                }
228                Some(Profiler::Lldb) => {
229                    bail!("The lldb profiler requires --experimental-artifact")
230                }
231                _ => {}
232            }
233        }
234        Ok(())
235    }
236
237    pub fn get_available_backends(&self) -> Result<Vec<BackendType>> {
238        // If a specific backend is explicitly requested, use it
239        #[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 = "v8")]
261        {
262            if self.v8 {
263                return Ok(vec![BackendType::V8]);
264            }
265        }
266
267        Ok(BackendType::enabled())
268    }
269
270    /// Filter enabled backends based on required WebAssembly features
271    pub fn filter_backends_by_features(
272        backends: Vec<BackendType>,
273        required_features: &Features,
274        target: &Target,
275    ) -> Vec<BackendType> {
276        backends
277            .into_iter()
278            .filter(|backend| backend.supports_features(required_features, target))
279            .collect()
280    }
281
282    pub fn get_store(&self) -> Result<Store> {
283        let engine = self.get_engine(&Target::default())?;
284        Ok(Store::new(engine))
285    }
286
287    pub fn get_engine(&self, target: &Target) -> Result<Engine> {
288        let backends = self.get_available_backends()?;
289        let backend = backends.first().context("no compiler backend enabled")?;
290        backend.get_engine(target, self)
291    }
292
293    pub fn get_engine_for_module(&self, module_contents: &[u8], target: &Target) -> Result<Engine> {
294        let required_features = self
295            .detect_features_from_wasm(module_contents)
296            .unwrap_or_default();
297
298        self.get_engine_for_features(&required_features, target)
299    }
300
301    pub fn get_engine_for_features(
302        &self,
303        required_features: &Features,
304        target: &Target,
305    ) -> Result<Engine> {
306        let backends = self.get_available_backends()?;
307        let filtered_backends =
308            Self::filter_backends_by_features(backends.clone(), required_features, target);
309
310        if filtered_backends.is_empty() {
311            let enabled_backends = BackendType::enabled();
312            if backends.len() == 1 && enabled_backends.len() > 1 {
313                // If the user has chosen an specific backend, we can suggest to use another one
314                let filtered_backends =
315                    Self::filter_backends_by_features(enabled_backends, required_features, target);
316                let extra_text: String = if !filtered_backends.is_empty() {
317                    format!(". You can use --{} instead", filtered_backends[0])
318                } else {
319                    "".to_string()
320                };
321                bail!(
322                    "The {} backend does not support the required features for the Wasm module{}",
323                    backends[0],
324                    extra_text
325                );
326            } else {
327                bail!(
328                    "No backends support the required features for the Wasm module. Feel free to open an issue at https://github.com/wasmerio/wasmer/issues"
329                );
330            }
331        }
332        filtered_backends.first().unwrap().get_engine(target, self)
333    }
334
335    #[cfg(feature = "compiler")]
336    /// Get the enabled Wasm features.
337    pub fn get_features(&self, default_features: &Features) -> Result<Features> {
338        if self.features.all {
339            return Ok(Features::all());
340        }
341
342        let mut result = default_features.clone();
343        if !self.features.disable_threads {
344            result.threads(true);
345        }
346        if self.features.disable_threads {
347            result.threads(false);
348        }
349        if self.features.multi_value {
350            result.multi_value(true);
351        }
352        if self.features.simd {
353            result.simd(true);
354        }
355        if self.features.bulk_memory {
356            result.bulk_memory(true);
357        }
358        if self.features.reference_types {
359            result.reference_types(true);
360        }
361        Ok(result)
362    }
363
364    #[cfg(feature = "compiler")]
365    /// Get a copy of the default features with user-configured options
366    pub fn get_configured_features(&self) -> Result<Features> {
367        let features = Features::default();
368        self.get_features(&features)
369    }
370
371    /// Detect features from a WebAssembly module binary.
372    pub fn detect_features_from_wasm(
373        &self,
374        wasm_bytes: &[u8],
375    ) -> Result<Features, wasmparser::BinaryReaderError> {
376        if self.features.all {
377            return Ok(Features::all());
378        }
379
380        let mut features = Features::detect_from_wasm(wasm_bytes)?;
381
382        // Merge with user-configured features
383        if !self.features.disable_threads {
384            features.threads(true);
385        }
386        if self.features.reference_types {
387            features.reference_types(true);
388        }
389        if self.features.simd {
390            features.simd(true);
391        }
392        if self.features.bulk_memory {
393            features.bulk_memory(true);
394        }
395        if self.features.multi_value {
396            features.multi_value(true);
397        }
398        if self.features.tail_call {
399            features.tail_call(true);
400        }
401        if self.features.module_linking {
402            features.module_linking(true);
403        }
404        if self.features.memory64 {
405            features.memory64(true);
406        }
407        if self.features.exceptions {
408            features.exceptions(true);
409        }
410
411        Ok(features)
412    }
413
414    #[cfg(feature = "compiler")]
415    pub fn get_sys_compiler_engine_for_target(
416        &self,
417        target: Target,
418    ) -> std::result::Result<Engine, anyhow::Error> {
419        let backends = self.get_available_backends()?;
420        let compiler_config = self.get_sys_compiler_config(backends.first().unwrap())?;
421        let default_features = compiler_config.default_features_for_target(&target);
422        let features = self.get_features(&default_features)?;
423        Ok(wasmer_compiler::EngineBuilder::new(compiler_config)
424            .set_features(Some(features))
425            .set_target(Some(target))
426            .engine()
427            .into())
428    }
429
430    #[allow(unused_variables)]
431    #[cfg(feature = "compiler")]
432    pub(crate) fn get_sys_compiler_config(
433        &self,
434        rt: &BackendType,
435    ) -> Result<Box<dyn CompilerConfig>> {
436        self.validate_profiler()?;
437        let compiler_config: Box<dyn CompilerConfig> = match rt {
438            BackendType::Headless => bail!("The headless engine can't be chosen"),
439            #[cfg(feature = "singlepass")]
440            BackendType::Singlepass => {
441                let mut config = wasmer_compiler_singlepass::Singlepass::new();
442                if self.enable_experimental_unaligned_memory_accesses {
443                    config.allow_experimental_unaligned_memory_accesses(true);
444                }
445                if self.enable_verifier {
446                    config.enable_verifier();
447                }
448                if self.enable_nan_canonicalization {
449                    config.canonicalize_nans(true);
450                }
451                if let Some(p) = &self.profiler {
452                    match p {
453                        Profiler::Perfmap => config.enable_perfmap(),
454                        Profiler::Gdb => config.enable_debugger(Debugger::Gdb),
455                        Profiler::Lldb => config.enable_debugger(Debugger::Lldb),
456                    }
457                }
458                if let Some(mut debug_dir) = self.compiler_debug_dir.clone() {
459                    use wasmer_compiler_singlepass::SinglepassCallbacks;
460
461                    debug_dir.push("singlepass");
462                    config.callbacks(Some(SinglepassCallbacks::new(debug_dir)?));
463                }
464                if let Some(num_threads) = self.compiler_threads {
465                    config.num_threads(num_threads);
466                }
467                Box::new(config)
468            }
469            #[cfg(feature = "cranelift")]
470            BackendType::Cranelift => {
471                let mut config = wasmer_compiler_cranelift::Cranelift::new();
472                if self.enable_experimental_unaligned_memory_accesses {
473                    config.allow_experimental_unaligned_memory_accesses(true);
474                }
475                if self.enable_verifier {
476                    config.enable_verifier();
477                }
478                if self.enable_nan_canonicalization {
479                    config.canonicalize_nans(true);
480                }
481                if let Some(p) = &self.profiler {
482                    match p {
483                        Profiler::Perfmap => config.enable_perfmap(),
484                        Profiler::Gdb => config.enable_debugger(Debugger::Gdb),
485                        Profiler::Lldb => config.enable_debugger(Debugger::Lldb),
486                    }
487                }
488                if let Some(mut debug_dir) = self.compiler_debug_dir.clone() {
489                    use wasmer_compiler_cranelift::CraneliftCallbacks;
490
491                    debug_dir.push("cranelift");
492                    config.callbacks(Some(CraneliftCallbacks::new(debug_dir)?));
493                }
494                if let Some(num_threads) = self.compiler_threads {
495                    config.num_threads(num_threads);
496                }
497                Box::new(config)
498            }
499            #[cfg(feature = "llvm")]
500            BackendType::LLVM => {
501                use wasmer_compiler_llvm::LLVMCallbacks;
502                use wasmer_types::entity::EntityRef;
503                let mut config = LLVM::new();
504                if !self.disable_non_volatile_memops {
505                    config.enable_non_volatile_memops();
506                }
507                config.enable_readonly_funcref_table();
508
509                if let Some(num_threads) = self.compiler_threads {
510                    config.num_threads(num_threads);
511                }
512
513                if let Some(mut debug_dir) = self.compiler_debug_dir.clone() {
514                    debug_dir.push("llvm");
515                    config.callbacks(Some(LLVMCallbacks::new(debug_dir)?));
516                    config.verbose_asm(true);
517                }
518                if self.enable_verifier {
519                    config.enable_verifier();
520                }
521                if self.enable_nan_canonicalization {
522                    config.canonicalize_nans(true);
523                }
524                if let Some(p) = &self.profiler {
525                    match p {
526                        Profiler::Perfmap => config.enable_perfmap(),
527                        Profiler::Gdb => config.enable_debugger(Debugger::Gdb),
528                        Profiler::Lldb => config.enable_debugger(Debugger::Lldb),
529                    }
530                }
531
532                Box::new(config)
533            }
534            BackendType::V8 => unreachable!(),
535            #[cfg(not(all(feature = "singlepass", feature = "cranelift", feature = "llvm")))]
536            compiler => {
537                bail!("The `{compiler}` compiler is not included in this binary.")
538            }
539        };
540
541        #[allow(unreachable_code)]
542        {
543            let mut compiler_config = compiler_config;
544            if self.experimental_artifact {
545                compiler_config.experimental_artifact(true);
546            }
547            Ok(compiler_config)
548        }
549    }
550}
551
552/// The compiler used for the store
553#[derive(Debug, PartialEq, Eq, Clone, Copy)]
554#[allow(clippy::upper_case_acronyms, dead_code)]
555pub enum BackendType {
556    /// Singlepass compiler
557    Singlepass,
558
559    /// Cranelift compiler
560    Cranelift,
561
562    /// LLVM compiler
563    LLVM,
564
565    /// V8 runtime
566    V8,
567
568    /// Headless compiler
569    #[allow(dead_code)]
570    Headless,
571}
572
573impl BackendType {
574    /// Return all enabled compilers
575    pub fn enabled() -> Vec<Self> {
576        vec![
577            #[cfg(feature = "cranelift")]
578            Self::Cranelift,
579            #[cfg(feature = "llvm")]
580            Self::LLVM,
581            #[cfg(feature = "singlepass")]
582            Self::Singlepass,
583            #[cfg(feature = "v8")]
584            Self::V8,
585        ]
586    }
587
588    /// Returns an engine for this backend type.
589    /// We enable every feature the engine supports, since the same engine may later be used
590    /// with a module that requires more features than the one used during engine detection.
591    pub fn get_engine(&self, target: &Target, runtime_opts: &RuntimeOptions) -> Result<Engine> {
592        runtime_opts.validate_profiler()?;
593        match self {
594            #[cfg(feature = "singlepass")]
595            Self::Singlepass => {
596                let mut config = wasmer_compiler_singlepass::Singlepass::new();
597                if runtime_opts.experimental_artifact {
598                    config.experimental_artifact(true);
599                }
600                if runtime_opts.enable_experimental_unaligned_memory_accesses {
601                    config.allow_experimental_unaligned_memory_accesses(true);
602                }
603                let supported_features = config.supported_features_for_target(target);
604                if runtime_opts.enable_verifier {
605                    config.enable_verifier();
606                }
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.experimental_artifact {
679                    config.experimental_artifact(true);
680                }
681                if !runtime_opts.disable_non_volatile_memops {
682                    config.enable_non_volatile_memops();
683                }
684                config.enable_readonly_funcref_table();
685
686                let supported_features = config.supported_features_for_target(target);
687                if let Some(mut debug_dir) = runtime_opts.compiler_debug_dir.clone() {
688                    debug_dir.push("llvm");
689                    config.callbacks(Some(LLVMCallbacks::new(debug_dir)?));
690                    config.verbose_asm(true);
691                }
692                if runtime_opts.enable_verifier {
693                    config.enable_verifier();
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}