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::{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)]
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    /// Enable support for the multi memory proposal.
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 all pre-standard proposals.
82    #[clap(long = "enable-all")]
83    pub all: bool,
84}
85
86#[derive(Debug, Clone, clap::Parser, Default)]
87/// The compiler options
88pub struct RuntimeOptions {
89    /// Use Singlepass compiler.
90    #[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    /// Use Cranelift compiler.
106    #[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    /// Use LLVM compiler.
122    #[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    /// Use the V8 runtime.
138    #[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    /// Use WAMR.
154    #[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    /// Use the wasmi runtime.
170    #[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    /// Enable compiler internal verification.
186    ///
187    /// Available for cranelift, LLVM and singlepass.
188    #[clap(long)]
189    enable_verifier: bool,
190
191    /// Enable a profiler.
192    ///
193    /// Available for cranelift, LLVM and singlepass.
194    #[clap(long, value_enum)]
195    profiler: Option<Profiler>,
196
197    /// LLVM debug directory, where IR and object files will be written to.
198    ///
199    /// Only available for the LLVM compiler.
200    #[clap(long)]
201    llvm_debug_dir: Option<PathBuf>,
202
203    /// Only available for the LLVM compiler. Enable the "pass-params" optimization, where the first (#0)
204    /// global and the first (#0) memory passed between guest functions as explicit parameters.
205    #[clap(long)]
206    enable_pass_params_opt: bool,
207
208    /// Only available for the LLVM compiler. Sets the number of threads used to compile the
209    /// input module(s).
210    #[clap(long)]
211    llvm_num_threads: Option<NonZero<usize>>,
212
213    #[clap(flatten)]
214    features: WasmFeatures,
215}
216
217#[derive(Clone, Debug)]
218pub enum Profiler {
219    /// Perfmap-based profilers.
220    Perfmap,
221}
222
223impl FromStr for Profiler {
224    type Err = anyhow::Error;
225
226    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
227        match s.to_lowercase().as_str() {
228            "perfmap" => Ok(Self::Perfmap),
229            _ => Err(anyhow::anyhow!("Unrecognized profiler: {s}")),
230        }
231    }
232}
233
234impl RuntimeOptions {
235    pub fn get_available_backends(&self) -> Result<Vec<BackendType>> {
236        // If a specific backend is explicitly requested, use it
237        #[cfg(feature = "cranelift")]
238        {
239            if self.cranelift {
240                return Ok(vec![BackendType::Cranelift]);
241            }
242        }
243
244        #[cfg(feature = "llvm")]
245        {
246            if self.llvm {
247                return Ok(vec![BackendType::LLVM]);
248            }
249        }
250
251        #[cfg(feature = "singlepass")]
252        {
253            if self.singlepass {
254                return Ok(vec![BackendType::Singlepass]);
255            }
256        }
257
258        #[cfg(feature = "wamr")]
259        {
260            if self.wamr {
261                return Ok(vec![BackendType::Wamr]);
262            }
263        }
264
265        #[cfg(feature = "v8")]
266        {
267            if self.v8 {
268                return Ok(vec![BackendType::V8]);
269            }
270        }
271
272        #[cfg(feature = "wasmi")]
273        {
274            if self.wasmi {
275                return Ok(vec![BackendType::Wasmi]);
276            }
277        }
278
279        Ok(BackendType::enabled())
280    }
281
282    /// Filter enabled backends based on required WebAssembly features
283    pub fn filter_backends_by_features(
284        backends: Vec<BackendType>,
285        required_features: &Features,
286        target: &Target,
287    ) -> Vec<BackendType> {
288        backends
289            .into_iter()
290            .filter(|backend| backend.supports_features(required_features, target))
291            .collect()
292    }
293
294    pub fn get_store(&self) -> Result<Store> {
295        let engine = self.get_engine(&Target::default())?;
296        Ok(Store::new(engine))
297    }
298
299    pub fn get_engine(&self, target: &Target) -> Result<Engine> {
300        let backends = self.get_available_backends()?;
301        let backend = backends.first().unwrap();
302        let backend_kind = wasmer::BackendKind::from(backend);
303        let required_features = wasmer::Engine::default_features_for_backend(&backend_kind, target);
304        backend.get_engine(target, &required_features, self)
305    }
306
307    pub fn get_engine_for_module(&self, module_contents: &[u8], target: &Target) -> Result<Engine> {
308        let required_features = self
309            .detect_features_from_wasm(module_contents)
310            .unwrap_or_default();
311
312        self.get_engine_for_features(&required_features, target)
313    }
314
315    pub fn get_engine_for_features(
316        &self,
317        required_features: &Features,
318        target: &Target,
319    ) -> Result<Engine> {
320        let backends = self.get_available_backends()?;
321        let filtered_backends =
322            Self::filter_backends_by_features(backends.clone(), required_features, target);
323
324        if filtered_backends.is_empty() {
325            let enabled_backends = BackendType::enabled();
326            if backends.len() == 1 && enabled_backends.len() > 1 {
327                // If the user has chosen an specific backend, we can suggest to use another one
328                let filtered_backends =
329                    Self::filter_backends_by_features(enabled_backends, required_features, target);
330                let extra_text: String = if !filtered_backends.is_empty() {
331                    format!(". You can use --{} instead", filtered_backends[0])
332                } else {
333                    "".to_string()
334                };
335                bail!(
336                    "The {} backend does not support the required features for the Wasm module{}",
337                    backends[0],
338                    extra_text
339                );
340            } else {
341                bail!(
342                    "No backends support the required features for the Wasm module. Feel free to open an issue at https://github.com/wasmerio/wasmer/issues"
343                );
344            }
345        }
346        filtered_backends
347            .first()
348            .unwrap()
349            .get_engine(target, required_features, self)
350    }
351
352    #[cfg(feature = "compiler")]
353    /// Get the enaled Wasm features.
354    pub fn get_features(&self, features: &Features) -> Result<Features> {
355        let mut result = features.clone();
356        if !self.features.disable_threads || self.features.all {
357            result.threads(true);
358        }
359        if self.features.disable_threads && !self.features.all {
360            result.threads(false);
361        }
362        if self.features.multi_value || self.features.all {
363            result.multi_value(true);
364        }
365        if self.features.simd || self.features.all {
366            result.simd(true);
367        }
368        if self.features.bulk_memory || self.features.all {
369            result.bulk_memory(true);
370        }
371        if self.features.reference_types || self.features.all {
372            result.reference_types(true);
373        }
374        Ok(result)
375    }
376
377    #[cfg(feature = "compiler")]
378    /// Get a copy of the default features with user-configured options
379    pub fn get_configured_features(&self) -> Result<Features> {
380        let features = Features::default();
381        self.get_features(&features)
382    }
383
384    /// Detect features from a WebAssembly module binary.
385    pub fn detect_features_from_wasm(
386        &self,
387        wasm_bytes: &[u8],
388    ) -> Result<Features, wasmparser::BinaryReaderError> {
389        let mut features = Features::detect_from_wasm(wasm_bytes)?;
390
391        // Merge with user-configured features
392        if !self.features.disable_threads || self.features.all {
393            features.threads(true);
394        }
395        if self.features.reference_types || self.features.all {
396            features.reference_types(true);
397        }
398        if self.features.simd || self.features.all {
399            features.simd(true);
400        }
401        if self.features.bulk_memory || self.features.all {
402            features.bulk_memory(true);
403        }
404        if self.features.multi_value || self.features.all {
405            features.multi_value(true);
406        }
407        if self.features.tail_call || self.features.all {
408            features.tail_call(true);
409        }
410        if self.features.module_linking || self.features.all {
411            features.module_linking(true);
412        }
413        if self.features.multi_memory || self.features.all {
414            features.multi_memory(true);
415        }
416        if self.features.memory64 || self.features.all {
417            features.memory64(true);
418        }
419        if self.features.exceptions || self.features.all {
420            features.exceptions(true);
421        }
422
423        Ok(features)
424    }
425
426    #[cfg(feature = "compiler")]
427    pub fn get_sys_compiler_engine_for_target(
428        &self,
429        target: Target,
430    ) -> std::result::Result<Engine, anyhow::Error> {
431        let backends = self.get_available_backends()?;
432        let compiler_config = self.get_sys_compiler_config(backends.first().unwrap())?;
433        let default_features = compiler_config.default_features_for_target(&target);
434        let features = self.get_features(&default_features)?;
435        Ok(wasmer_compiler::EngineBuilder::new(compiler_config)
436            .set_features(Some(features))
437            .set_target(Some(target))
438            .engine()
439            .into())
440    }
441
442    #[allow(unused_variables)]
443    #[cfg(feature = "compiler")]
444    pub(crate) fn get_sys_compiler_config(
445        &self,
446        rt: &BackendType,
447    ) -> Result<Box<dyn CompilerConfig>> {
448        let compiler_config: Box<dyn CompilerConfig> = match rt {
449            BackendType::Headless => bail!("The headless engine can't be chosen"),
450            #[cfg(feature = "singlepass")]
451            BackendType::Singlepass => {
452                let mut config = wasmer_compiler_singlepass::Singlepass::new();
453                if self.enable_verifier {
454                    config.enable_verifier();
455                }
456                if let Some(p) = &self.profiler {
457                    match p {
458                        Profiler::Perfmap => config.enable_perfmap(),
459                    }
460                }
461
462                Box::new(config)
463            }
464            #[cfg(feature = "cranelift")]
465            BackendType::Cranelift => {
466                let mut config = wasmer_compiler_cranelift::Cranelift::new();
467                if self.enable_verifier {
468                    config.enable_verifier();
469                }
470                if let Some(p) = &self.profiler {
471                    match p {
472                        Profiler::Perfmap => config.enable_perfmap(),
473                    }
474                }
475                Box::new(config)
476            }
477            #[cfg(feature = "llvm")]
478            BackendType::LLVM => {
479                use std::{fmt, fs::File, io::Write};
480
481                use wasmer_compiler_llvm::{
482                    CompiledKind, InkwellMemoryBuffer, InkwellModule, LLVM, LLVMCallbacks,
483                };
484                use wasmer_types::entity::EntityRef;
485                let mut config = LLVM::new();
486
487                if self.enable_pass_params_opt {
488                    config.enable_pass_params_opt();
489                }
490
491                if let Some(num_threads) = self.llvm_num_threads {
492                    config.num_threads(num_threads);
493                }
494
495                struct Callbacks {
496                    debug_dir: PathBuf,
497                }
498                impl Callbacks {
499                    fn new(debug_dir: PathBuf) -> Result<Self> {
500                        // Create the debug dir in case it doesn't exist
501                        std::fs::create_dir_all(&debug_dir)?;
502                        Ok(Self { debug_dir })
503                    }
504                }
505                // Converts a kind into a filename, that we will use to dump
506                // the contents of the IR object file to.
507                fn types_to_signature(types: &[Type]) -> String {
508                    types
509                        .iter()
510                        .map(|ty| match ty {
511                            Type::I32 => "i".to_string(),
512                            Type::I64 => "I".to_string(),
513                            Type::F32 => "f".to_string(),
514                            Type::F64 => "F".to_string(),
515                            Type::V128 => "v".to_string(),
516                            Type::ExternRef => "e".to_string(),
517                            Type::FuncRef => "r".to_string(),
518                            Type::ExceptionRef => "x".to_string(),
519                        })
520                        .collect::<Vec<_>>()
521                        .join("")
522                }
523                // Converts a kind into a filename, that we will use to dump
524                // the contents of the IR object file to.
525                fn function_kind_to_filename(kind: &CompiledKind) -> String {
526                    match kind {
527                        CompiledKind::Local(local_index) => {
528                            format!("function_{}", local_index.index())
529                        }
530                        CompiledKind::FunctionCallTrampoline(func_type) => format!(
531                            "trampoline_call_{}_{}",
532                            types_to_signature(func_type.params()),
533                            types_to_signature(func_type.results())
534                        ),
535                        CompiledKind::DynamicFunctionTrampoline(func_type) => format!(
536                            "trampoline_dynamic_{}_{}",
537                            types_to_signature(func_type.params()),
538                            types_to_signature(func_type.results())
539                        ),
540                        CompiledKind::Module => "module".into(),
541                    }
542                }
543                impl LLVMCallbacks for Callbacks {
544                    fn preopt_ir(&self, kind: &CompiledKind, module: &InkwellModule) {
545                        let mut path = self.debug_dir.clone();
546                        path.push(format!("{}.preopt.ll", function_kind_to_filename(kind)));
547                        module
548                            .print_to_file(&path)
549                            .expect("Error while dumping pre optimized LLVM IR");
550                    }
551                    fn postopt_ir(&self, kind: &CompiledKind, module: &InkwellModule) {
552                        let mut path = self.debug_dir.clone();
553                        path.push(format!("{}.postopt.ll", function_kind_to_filename(kind)));
554                        module
555                            .print_to_file(&path)
556                            .expect("Error while dumping post optimized LLVM IR");
557                    }
558                    fn obj_memory_buffer(
559                        &self,
560                        kind: &CompiledKind,
561                        memory_buffer: &InkwellMemoryBuffer,
562                    ) {
563                        let mut path = self.debug_dir.clone();
564                        path.push(format!("{}.o", function_kind_to_filename(kind)));
565                        let mem_buf_slice = memory_buffer.as_slice();
566                        let mut file = File::create(path)
567                            .expect("Error while creating debug object file from LLVM IR");
568                        let mut pos = 0;
569                        while pos < mem_buf_slice.len() {
570                            pos += file.write(&mem_buf_slice[pos..]).unwrap();
571                        }
572                    }
573                    fn asm_memory_buffer(
574                        &self,
575                        kind: &CompiledKind,
576                        asm_memory_buffer: &InkwellMemoryBuffer,
577                    ) {
578                        let mut path = self.debug_dir.clone();
579                        path.push(format!("{}.s", function_kind_to_filename(kind)));
580                        let mem_buf_slice = asm_memory_buffer.as_slice();
581                        let mut file = File::create(path)
582                            .expect("Error while creating debug object file from LLVM IR");
583                        let mut pos = 0;
584                        while pos < mem_buf_slice.len() {
585                            pos += file.write(&mem_buf_slice[pos..]).unwrap();
586                        }
587                    }
588                }
589
590                impl fmt::Debug for Callbacks {
591                    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
592                        write!(f, "LLVMCallbacks")
593                    }
594                }
595
596                if let Some(ref llvm_debug_dir) = self.llvm_debug_dir {
597                    config.callbacks(Some(Arc::new(Callbacks::new(llvm_debug_dir.clone())?)));
598                }
599                if self.enable_verifier {
600                    config.enable_verifier();
601                }
602                if let Some(p) = &self.profiler {
603                    match p {
604                        Profiler::Perfmap => config.enable_perfmap(),
605                    }
606                }
607
608                Box::new(config)
609            }
610            BackendType::V8 | BackendType::Wamr | BackendType::Wasmi => unreachable!(),
611            #[cfg(not(all(feature = "singlepass", feature = "cranelift", feature = "llvm")))]
612            compiler => {
613                bail!(
614                    "The `{}` compiler is not included in this binary.",
615                    compiler.to_string()
616                )
617            }
618        };
619
620        #[allow(unreachable_code)]
621        Ok(compiler_config)
622    }
623}
624
625/// The compiler used for the store
626#[derive(Debug, PartialEq, Eq, Clone, Copy)]
627#[allow(clippy::upper_case_acronyms, dead_code)]
628pub enum BackendType {
629    /// Singlepass compiler
630    Singlepass,
631
632    /// Cranelift compiler
633    Cranelift,
634
635    /// LLVM compiler
636    LLVM,
637
638    /// V8 runtime
639    V8,
640
641    /// Wamr runtime
642    Wamr,
643
644    /// Wasmi runtime
645    Wasmi,
646
647    /// Headless compiler
648    #[allow(dead_code)]
649    Headless,
650}
651
652impl BackendType {
653    /// Return all enabled compilers
654    pub fn enabled() -> Vec<Self> {
655        vec![
656            #[cfg(feature = "cranelift")]
657            Self::Cranelift,
658            #[cfg(feature = "llvm")]
659            Self::LLVM,
660            #[cfg(feature = "singlepass")]
661            Self::Singlepass,
662            #[cfg(feature = "v8")]
663            Self::V8,
664            #[cfg(feature = "wamr")]
665            Self::Wamr,
666            #[cfg(feature = "wasmi")]
667            Self::Wasmi,
668        ]
669    }
670
671    /// Get an engine for this backend type
672    pub fn get_engine(
673        &self,
674        target: &Target,
675        features: &Features,
676        runtime_opts: &RuntimeOptions,
677    ) -> Result<Engine> {
678        match self {
679            #[cfg(feature = "singlepass")]
680            Self::Singlepass => {
681                let mut config = wasmer_compiler_singlepass::Singlepass::new();
682                if runtime_opts.enable_verifier {
683                    config.enable_verifier();
684                }
685                if let Some(p) = &runtime_opts.profiler {
686                    match p {
687                        Profiler::Perfmap => config.enable_perfmap(),
688                    }
689                }
690                let engine = wasmer_compiler::EngineBuilder::new(config)
691                    .set_features(Some(features.clone()))
692                    .set_target(Some(target.clone()))
693                    .engine()
694                    .into();
695                Ok(engine)
696            }
697            #[cfg(feature = "cranelift")]
698            Self::Cranelift => {
699                let mut config = wasmer_compiler_cranelift::Cranelift::new();
700                if runtime_opts.enable_verifier {
701                    config.enable_verifier();
702                }
703                if let Some(p) = &runtime_opts.profiler {
704                    match p {
705                        Profiler::Perfmap => config.enable_perfmap(),
706                    }
707                }
708                let engine = wasmer_compiler::EngineBuilder::new(config)
709                    .set_features(Some(features.clone()))
710                    .set_target(Some(target.clone()))
711                    .engine()
712                    .into();
713                Ok(engine)
714            }
715            #[cfg(feature = "llvm")]
716            Self::LLVM => {
717                use std::{fmt, fs::File, io::Write};
718
719                use wasmer_compiler_llvm::{
720                    CompiledKind, InkwellMemoryBuffer, InkwellModule, LLVM, LLVMCallbacks,
721                };
722                use wasmer_types::entity::EntityRef;
723
724                let mut config = wasmer_compiler_llvm::LLVM::new();
725
726                struct Callbacks {
727                    debug_dir: PathBuf,
728                }
729                impl Callbacks {
730                    fn new(debug_dir: PathBuf) -> Result<Self> {
731                        // Create the debug dir in case it doesn't exist
732                        std::fs::create_dir_all(&debug_dir)?;
733                        Ok(Self { debug_dir })
734                    }
735                }
736                // Converts a kind into a filename, that we will use to dump
737                // the contents of the IR object file to.
738                fn types_to_signature(types: &[Type]) -> String {
739                    types
740                        .iter()
741                        .map(|ty| match ty {
742                            Type::I32 => "i".to_string(),
743                            Type::I64 => "I".to_string(),
744                            Type::F32 => "f".to_string(),
745                            Type::F64 => "F".to_string(),
746                            Type::V128 => "v".to_string(),
747                            Type::ExternRef => "e".to_string(),
748                            Type::FuncRef => "r".to_string(),
749                            Type::ExceptionRef => "x".to_string(),
750                        })
751                        .collect::<Vec<_>>()
752                        .join("")
753                }
754                // Converts a kind into a filename, that we will use to dump
755                // the contents of the IR object file to.
756                fn function_kind_to_filename(kind: &CompiledKind) -> String {
757                    match kind {
758                        CompiledKind::Local(local_index) => {
759                            format!("function_{}", local_index.index())
760                        }
761                        CompiledKind::FunctionCallTrampoline(func_type) => format!(
762                            "trampoline_call_{}_{}",
763                            types_to_signature(func_type.params()),
764                            types_to_signature(func_type.results())
765                        ),
766                        CompiledKind::DynamicFunctionTrampoline(func_type) => format!(
767                            "trampoline_dynamic_{}_{}",
768                            types_to_signature(func_type.params()),
769                            types_to_signature(func_type.results())
770                        ),
771                        CompiledKind::Module => "module".into(),
772                    }
773                }
774                impl LLVMCallbacks for Callbacks {
775                    fn preopt_ir(&self, kind: &CompiledKind, module: &InkwellModule) {
776                        let mut path = self.debug_dir.clone();
777                        path.push(format!("{}.preopt.ll", function_kind_to_filename(kind)));
778                        module
779                            .print_to_file(&path)
780                            .expect("Error while dumping pre optimized LLVM IR");
781                    }
782                    fn postopt_ir(&self, kind: &CompiledKind, module: &InkwellModule) {
783                        let mut path = self.debug_dir.clone();
784                        path.push(format!("{}.postopt.ll", function_kind_to_filename(kind)));
785                        module
786                            .print_to_file(&path)
787                            .expect("Error while dumping post optimized LLVM IR");
788                    }
789                    fn obj_memory_buffer(
790                        &self,
791                        kind: &CompiledKind,
792                        memory_buffer: &InkwellMemoryBuffer,
793                    ) {
794                        let mut path = self.debug_dir.clone();
795                        path.push(format!("{}.o", function_kind_to_filename(kind)));
796                        let mem_buf_slice = memory_buffer.as_slice();
797                        let mut file = File::create(path)
798                            .expect("Error while creating debug object file from LLVM IR");
799                        let mut pos = 0;
800                        while pos < mem_buf_slice.len() {
801                            pos += file.write(&mem_buf_slice[pos..]).unwrap();
802                        }
803                    }
804                    fn asm_memory_buffer(
805                        &self,
806                        kind: &CompiledKind,
807                        asm_memory_buffer: &InkwellMemoryBuffer,
808                    ) {
809                        let mut path = self.debug_dir.clone();
810                        path.push(format!("{}.s", function_kind_to_filename(kind)));
811                        let mem_buf_slice = asm_memory_buffer.as_slice();
812                        let mut file = File::create(path)
813                            .expect("Error while creating debug object file from LLVM IR");
814                        let mut pos = 0;
815                        while pos < mem_buf_slice.len() {
816                            pos += file.write(&mem_buf_slice[pos..]).unwrap();
817                        }
818                    }
819                }
820
821                impl fmt::Debug for Callbacks {
822                    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
823                        write!(f, "LLVMCallbacks")
824                    }
825                }
826
827                if let Some(ref llvm_debug_dir) = runtime_opts.llvm_debug_dir {
828                    config.callbacks(Some(Arc::new(Callbacks::new(llvm_debug_dir.clone())?)));
829                }
830                if runtime_opts.enable_verifier {
831                    config.enable_verifier();
832                }
833
834                if runtime_opts.enable_pass_params_opt {
835                    config.enable_pass_params_opt();
836                }
837
838                if let Some(num_threads) = runtime_opts.llvm_num_threads {
839                    config.num_threads(num_threads);
840                }
841
842                if let Some(p) = &runtime_opts.profiler {
843                    match p {
844                        Profiler::Perfmap => config.enable_perfmap(),
845                    }
846                }
847
848                let engine = wasmer_compiler::EngineBuilder::new(config)
849                    .set_features(Some(features.clone()))
850                    .set_target(Some(target.clone()))
851                    .engine()
852                    .into();
853                Ok(engine)
854            }
855            #[cfg(feature = "v8")]
856            Self::V8 => Ok(wasmer::v8::V8::new().into()),
857            #[cfg(feature = "wamr")]
858            Self::Wamr => Ok(wasmer::wamr::Wamr::new().into()),
859            #[cfg(feature = "wasmi")]
860            Self::Wasmi => Ok(wasmer::wasmi::Wasmi::new().into()),
861            Self::Headless => bail!("Headless is not a valid runtime to instantiate directly"),
862            #[allow(unreachable_patterns)]
863            _ => bail!("Unsupported backend type"),
864        }
865    }
866
867    /// Check if this backend supports all the required WebAssembly features
868    #[allow(unreachable_code)]
869    pub fn supports_features(&self, required_features: &Features, target: &Target) -> bool {
870        // Map BackendType to the corresponding wasmer::BackendKind
871        let backend_kind = match self {
872            #[cfg(feature = "singlepass")]
873            Self::Singlepass => wasmer::BackendKind::Singlepass,
874            #[cfg(feature = "cranelift")]
875            Self::Cranelift => wasmer::BackendKind::Cranelift,
876            #[cfg(feature = "llvm")]
877            Self::LLVM => wasmer::BackendKind::LLVM,
878            #[cfg(feature = "v8")]
879            Self::V8 => wasmer::BackendKind::V8,
880            #[cfg(feature = "wamr")]
881            Self::Wamr => wasmer::BackendKind::Wamr,
882            #[cfg(feature = "wasmi")]
883            Self::Wasmi => wasmer::BackendKind::Wasmi,
884            Self::Headless => return false, // Headless can't compile
885            #[allow(unreachable_patterns)]
886            _ => return false,
887        };
888
889        // Get the supported features from the backend
890        let supported = wasmer::Engine::supported_features_for_backend(&backend_kind, target);
891
892        // Check if the backend supports all required features
893        if !supported.contains_features(required_features) {
894            return false;
895        }
896
897        true
898    }
899}
900
901impl From<&BackendType> for wasmer::BackendKind {
902    fn from(backend_type: &BackendType) -> Self {
903        match backend_type {
904            #[cfg(feature = "singlepass")]
905            BackendType::Singlepass => wasmer::BackendKind::Singlepass,
906            #[cfg(feature = "cranelift")]
907            BackendType::Cranelift => wasmer::BackendKind::Cranelift,
908            #[cfg(feature = "llvm")]
909            BackendType::LLVM => wasmer::BackendKind::LLVM,
910            #[cfg(feature = "v8")]
911            BackendType::V8 => wasmer::BackendKind::V8,
912            #[cfg(feature = "wamr")]
913            BackendType::Wamr => wasmer::BackendKind::Wamr,
914            #[cfg(feature = "wasmi")]
915            BackendType::Wasmi => wasmer::BackendKind::Wasmi,
916            _ => {
917                #[cfg(feature = "sys")]
918                {
919                    wasmer::BackendKind::Headless
920                }
921                #[cfg(not(feature = "sys"))]
922                {
923                    unreachable!("No backend enabled!")
924                }
925            }
926        }
927    }
928}
929
930impl std::fmt::Display for BackendType {
931    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
932        write!(
933            f,
934            "{}",
935            match self {
936                Self::Singlepass => "singlepass",
937                Self::Cranelift => "cranelift",
938                Self::LLVM => "llvm",
939                Self::V8 => "v8",
940                Self::Wamr => "wamr",
941                Self::Wasmi => "wasmi",
942                Self::Headless => "headless",
943            }
944        )
945    }
946}