Skip to main content

wasmer_compiler/engine/
inner.rs

1use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
2use std::sync::{Arc, Mutex};
3
4use crate::engine::builder::EngineBuilder;
5#[cfg(feature = "compiler")]
6use crate::{Compiler, CompilerConfig, Debugger};
7
8#[cfg(not(target_arch = "wasm32"))]
9use wasmer_types::CompilationProgressCallback;
10#[cfg(feature = "compiler")]
11use wasmer_types::Features;
12use wasmer_types::{CompileError, target::Target};
13
14#[cfg(not(target_arch = "wasm32"))]
15use shared_buffer::OwnedBuffer;
16#[cfg(not(target_arch = "wasm32"))]
17use std::ffi::c_void;
18#[cfg(all(not(target_arch = "wasm32"), feature = "compiler"))]
19use std::io::Write;
20#[cfg(not(target_arch = "wasm32"))]
21use std::io::{Read, Seek};
22#[cfg(all(not(target_arch = "wasm32"), unix))]
23use std::os::fd::RawFd;
24#[cfg(not(target_arch = "wasm32"))]
25use std::path::Path;
26#[cfg(all(not(target_arch = "wasm32"), feature = "compiler"))]
27use wasmer_types::ModuleInfo;
28#[cfg(not(target_arch = "wasm32"))]
29use wasmer_types::{
30    DeserializeError, FunctionIndex, FunctionType, LocalFunctionIndex, SignatureHash,
31    SignatureIndex, entity::PrimaryMap,
32};
33
34#[cfg(not(target_arch = "wasm32"))]
35use crate::{
36    Artifact, BaseTunables, CodeMemory, FunctionExtent, GlobalFrameInfoRegistration, Tunables,
37    engine::mapped_binary::MemoryMappedBinary,
38    types::{
39        function::FunctionBodyLike,
40        section::{CustomSectionLike, CustomSectionProtection, SectionIndex},
41    },
42};
43
44#[cfg(not(target_arch = "wasm32"))]
45use wasmer_vm::{
46    FunctionBodyPtr, SectionBodyPtr, SignatureRegistry, VMFunctionBody, VMSignatureHash,
47    VMTrampoline,
48};
49
50/// A WebAssembly Engine.
51#[derive(Clone)]
52pub struct Engine {
53    inner: Arc<Mutex<EngineInner>>,
54    /// The target for the compiler
55    target: Arc<Target>,
56    engine_id: EngineId,
57    #[cfg(not(target_arch = "wasm32"))]
58    tunables: Arc<dyn Tunables + Send + Sync>,
59    name: String,
60}
61
62impl Engine {
63    /// Create a new `Engine` with the given config
64    #[cfg(feature = "compiler")]
65    pub fn new(
66        compiler_config: Box<dyn CompilerConfig>,
67        target: Target,
68        features: Features,
69    ) -> Self {
70        #[cfg(not(target_arch = "wasm32"))]
71        let tunables = BaseTunables::new();
72        let compiler = compiler_config.compiler();
73        let name = format!("engine-{}", compiler.name());
74        Self {
75            inner: Arc::new(Mutex::new(EngineInner {
76                compiler: Some(compiler),
77                features,
78                #[cfg(not(target_arch = "wasm32"))]
79                code_memory: vec![],
80                #[cfg(not(target_arch = "wasm32"))]
81                elf_mapped_binary: vec![],
82                #[cfg(not(target_arch = "wasm32"))]
83                signatures: SignatureRegistry::new(),
84            })),
85            target: Arc::new(target),
86            engine_id: EngineId::default(),
87            #[cfg(not(target_arch = "wasm32"))]
88            tunables: Arc::new(tunables),
89            name,
90        }
91    }
92
93    /// Returns the name of this engine
94    pub fn name(&self) -> &str {
95        self.name.as_str()
96    }
97
98    /// Returns the deterministic id of this engine
99    pub fn deterministic_id(&self) -> String {
100        #[cfg(feature = "compiler")]
101        {
102            let i = self.inner();
103            if let Some(ref c) = i.compiler {
104                return c.deterministic_id();
105            } else {
106                return self.name.clone();
107            }
108        }
109
110        #[allow(unreachable_code)]
111        {
112            self.name.to_string()
113        }
114    }
115
116    /// Returns the format used for artifacts produced by this engine.
117    pub fn artifact_format(&self) -> String {
118        #[cfg(feature = "compiler")]
119        {
120            let i = self.inner();
121            if let Some(ref c) = i.compiler {
122                return c.artifact_format();
123            }
124        }
125
126        #[allow(unreachable_code)]
127        self.name.to_string()
128    }
129
130    /// Create a headless `Engine`
131    ///
132    /// A headless engine is an engine without any compiler attached.
133    /// This is useful for assuring a minimal runtime for running
134    /// WebAssembly modules.
135    ///
136    /// For example, for running in IoT devices where compilers are very
137    /// expensive, or also to optimize startup speed.
138    ///
139    /// # Important
140    ///
141    /// Headless engines can't compile or validate any modules,
142    /// they just take already processed Modules (via `Module::serialize`).
143    pub fn headless() -> Self {
144        let target = Target::default();
145        #[cfg(not(target_arch = "wasm32"))]
146        let tunables = BaseTunables::new();
147        Self {
148            inner: Arc::new(Mutex::new(EngineInner {
149                #[cfg(feature = "compiler")]
150                compiler: None,
151                #[cfg(feature = "compiler")]
152                features: Features::default(),
153                #[cfg(not(target_arch = "wasm32"))]
154                code_memory: vec![],
155                #[cfg(not(target_arch = "wasm32"))]
156                elf_mapped_binary: vec![],
157                #[cfg(not(target_arch = "wasm32"))]
158                signatures: SignatureRegistry::new(),
159            })),
160            target: Arc::new(target),
161            engine_id: EngineId::default(),
162            #[cfg(not(target_arch = "wasm32"))]
163            tunables: Arc::new(tunables),
164            name: "engine-headless".to_string(),
165        }
166    }
167
168    /// Get reference to `EngineInner`.
169    pub fn inner(&self) -> std::sync::MutexGuard<'_, EngineInner> {
170        self.inner.lock().unwrap()
171    }
172
173    /// Get mutable reference to `EngineInner`.
174    pub fn inner_mut(&self) -> std::sync::MutexGuard<'_, EngineInner> {
175        self.inner.lock().unwrap()
176    }
177
178    /// Gets the target
179    pub fn target(&self) -> &Target {
180        &self.target
181    }
182
183    /// Register a signature
184    #[cfg(not(target_arch = "wasm32"))]
185    pub fn register_signature(&self, func_type: &FunctionType) -> VMSignatureHash {
186        let compiler = self.inner();
187        compiler
188            .signatures()
189            .register(func_type, SignatureHash(func_type.signature_hash()))
190    }
191
192    /// Look up a registered signature by its hash.
193    #[cfg(not(target_arch = "wasm32"))]
194    pub fn lookup_signature(&self, sig_hash: VMSignatureHash) -> Option<FunctionType> {
195        let compiler = self.inner();
196        compiler.signatures().lookup_signature(sig_hash)
197    }
198
199    /// Validates a WebAssembly module
200    #[cfg(feature = "compiler")]
201    pub fn validate(&self, binary: &[u8]) -> Result<(), CompileError> {
202        self.inner().validate(binary)
203    }
204
205    /// Compile a WebAssembly binary
206    #[cfg(feature = "compiler")]
207    #[cfg(not(target_arch = "wasm32"))]
208    pub fn compile(&self, binary: &[u8]) -> Result<Arc<Artifact>, CompileError> {
209        Ok(Arc::new(Artifact::new(
210            self,
211            binary,
212            self.tunables.as_ref(),
213            None,
214        )?))
215    }
216
217    /// Compile a WebAssembly binary with a progress callback.
218    #[cfg(feature = "compiler")]
219    pub fn compile_with_progress(
220        &self,
221        binary: &[u8],
222        progress_callback: Option<CompilationProgressCallback>,
223    ) -> Result<Arc<Artifact>, CompileError> {
224        Ok(Arc::new(Artifact::new(
225            self,
226            binary,
227            self.tunables.as_ref(),
228            progress_callback,
229        )?))
230    }
231
232    /// Compile a WebAssembly binary (the progress_callback argument is unused).
233    #[cfg(not(feature = "compiler"))]
234    #[cfg(not(target_arch = "wasm32"))]
235    pub fn compile_with_progress(
236        &self,
237        binary: &[u8],
238        _progress_callback: Option<CompilationProgressCallback>,
239    ) -> Result<Arc<Artifact>, CompileError> {
240        self.compile(binary, self.tunables.as_ref())
241    }
242
243    /// Compile a WebAssembly binary
244    #[cfg(not(feature = "compiler"))]
245    #[cfg(not(target_arch = "wasm32"))]
246    pub fn compile(
247        &self,
248        _binary: &[u8],
249        _tunables: &dyn Tunables,
250    ) -> Result<Arc<Artifact>, CompileError> {
251        Err(CompileError::Codegen(
252            "The Engine is operating in headless mode, so it can not compile Modules.".to_string(),
253        ))
254    }
255
256    #[cfg(not(target_arch = "wasm32"))]
257    /// Deserializes a WebAssembly module which was previously serialized with
258    /// [`wasmer::Module::serialize`].
259    ///
260    /// # Safety
261    ///
262    /// See [`Artifact::deserialize_unchecked`].
263    pub unsafe fn deserialize_unchecked(
264        &self,
265        bytes: OwnedBuffer,
266    ) -> Result<Arc<Artifact>, DeserializeError> {
267        unsafe { Ok(Arc::new(Artifact::deserialize_unchecked(self, bytes)?)) }
268    }
269
270    /// Deserializes a WebAssembly module which was previously serialized with
271    /// [`wasmer::Module::serialize`].
272    ///
273    /// # Safety
274    ///
275    /// See [`Artifact::deserialize`].
276    #[cfg(not(target_arch = "wasm32"))]
277    pub unsafe fn deserialize(
278        &self,
279        bytes: OwnedBuffer,
280    ) -> Result<Arc<Artifact>, DeserializeError> {
281        unsafe { Ok(Arc::new(Artifact::deserialize(self, bytes)?)) }
282    }
283
284    /// Deserializes a WebAssembly module from a path.
285    ///
286    /// # Safety
287    /// See [`Artifact::deserialize`].
288    #[cfg(not(target_arch = "wasm32"))]
289    pub unsafe fn deserialize_from_file(
290        &self,
291        file_ref: &Path,
292    ) -> Result<Arc<Artifact>, DeserializeError> {
293        unsafe {
294            let mut file = std::fs::File::open(file_ref)?;
295            let mut magic = [0; 4];
296            let is_elf = file.read_exact(&mut magic).is_ok() && magic == object::elf::ELFMAG;
297            if is_elf {
298                return Ok(Arc::new(Artifact::deserialize_file(self, file_ref)?));
299            }
300            file.rewind()?;
301            self.deserialize(
302                OwnedBuffer::from_file(&file)
303                    .map_err(|e| DeserializeError::Generic(e.to_string()))?,
304            )
305        }
306    }
307
308    /// Deserialize from a file path.
309    ///
310    /// # Safety
311    ///
312    /// See [`Artifact::deserialize_unchecked`].
313    #[cfg(not(target_arch = "wasm32"))]
314    pub unsafe fn deserialize_from_file_unchecked(
315        &self,
316        file_ref: &Path,
317    ) -> Result<Arc<Artifact>, DeserializeError> {
318        unsafe {
319            let file = std::fs::File::open(file_ref)?;
320            self.deserialize_unchecked(
321                OwnedBuffer::from_file(&file)
322                    .map_err(|e| DeserializeError::Generic(e.to_string()))?,
323            )
324        }
325    }
326
327    /// A unique identifier for this object.
328    ///
329    /// This exists to allow us to compare two Engines for equality. Otherwise,
330    /// comparing two trait objects unsafely relies on implementation details
331    /// of trait representation.
332    pub fn id(&self) -> &EngineId {
333        &self.engine_id
334    }
335
336    /// Clone the engine
337    pub fn cloned(&self) -> Self {
338        self.clone()
339    }
340
341    /// Attach a Tunable to this engine
342    #[cfg(not(target_arch = "wasm32"))]
343    pub fn set_tunables(&mut self, tunables: impl Tunables + Send + Sync + 'static) {
344        self.tunables = Arc::new(tunables);
345    }
346
347    /// Get a reference to attached Tunable of this engine
348    #[cfg(not(target_arch = "wasm32"))]
349    pub fn tunables(&self) -> &dyn Tunables {
350        self.tunables.as_ref()
351    }
352
353    /// Add suggested optimizations to this engine.
354    #[deprecated(note = "User compilation options are currently unused")]
355    pub fn with_opts(
356        &mut self,
357        _suggested_opts: &wasmer_types::target::UserCompilerOptimizations,
358    ) -> Result<(), CompileError> {
359        Ok(())
360    }
361}
362
363impl std::fmt::Debug for Engine {
364    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
365        write!(f, "{}", self.deterministic_id())
366    }
367}
368
369/// The inner contents of `Engine`
370pub struct EngineInner {
371    #[cfg(feature = "compiler")]
372    /// The compiler and cpu features
373    compiler: Option<Box<dyn Compiler>>,
374    #[cfg(feature = "compiler")]
375    /// The compiler and cpu features
376    features: Features,
377    /// The code memory is responsible of publishing the compiled
378    /// functions to memory.
379    #[cfg(not(target_arch = "wasm32"))]
380    code_memory: Vec<CodeMemory>,
381    /// Memory-mapped ELF artifact image, produced by `--experimental-artifact`.
382    #[cfg(not(target_arch = "wasm32"))]
383    elf_mapped_binary: Vec<MemoryMappedBinary>,
384    /// The signature registry is used mainly to operate with trampolines
385    /// performantly.
386    #[cfg(not(target_arch = "wasm32"))]
387    signatures: SignatureRegistry,
388}
389
390impl std::fmt::Debug for EngineInner {
391    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
392        let mut formatter = f.debug_struct("EngineInner");
393        #[cfg(feature = "compiler")]
394        {
395            formatter.field("compiler", &self.compiler);
396            formatter.field("features", &self.features);
397        }
398
399        #[cfg(not(target_arch = "wasm32"))]
400        {
401            formatter.field("signatures", &self.signatures);
402        }
403
404        formatter.finish()
405    }
406}
407
408impl EngineInner {
409    /// Gets the compiler associated to this engine.
410    #[cfg(feature = "compiler")]
411    pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> {
412        match self.compiler.as_ref() {
413            None => Err(CompileError::Codegen(
414                "No compiler compiled into executable".to_string(),
415            )),
416            Some(compiler) => Ok(&**compiler),
417        }
418    }
419
420    /// Validate the module
421    #[cfg(feature = "compiler")]
422    pub fn validate(&self, data: &[u8]) -> Result<(), CompileError> {
423        let compiler = self.compiler()?;
424        compiler.validate_module(&self.features, data)
425    }
426
427    /// The Wasm features
428    #[cfg(feature = "compiler")]
429    pub fn features(&self) -> &Features {
430        &self.features
431    }
432
433    /// Allocate compiled functions into memory
434    #[cfg(not(target_arch = "wasm32"))]
435    #[allow(clippy::type_complexity)]
436    pub(crate) fn allocate<'a, FunctionBody, CustomSection>(
437        &'a mut self,
438        _module: &wasmer_types::ModuleInfo,
439        functions: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
440        function_call_trampolines: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
441        dynamic_function_trampolines: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
442        custom_sections: impl ExactSizeIterator<Item = &'a CustomSection> + Clone + 'a,
443    ) -> Result<
444        (
445            PrimaryMap<LocalFunctionIndex, FunctionExtent>,
446            PrimaryMap<SignatureIndex, VMTrampoline>,
447            PrimaryMap<FunctionIndex, FunctionBodyPtr>,
448            PrimaryMap<SectionIndex, SectionBodyPtr>,
449        ),
450        CompileError,
451    >
452    where
453        FunctionBody: FunctionBodyLike<'a> + 'a,
454        CustomSection: CustomSectionLike<'a> + 'a,
455    {
456        let functions_len = functions.len();
457        let function_call_trampolines_len = function_call_trampolines.len();
458
459        let function_bodies = functions
460            .chain(function_call_trampolines)
461            .chain(dynamic_function_trampolines)
462            .collect::<Vec<_>>();
463        let (executable_sections, data_sections): (Vec<_>, _) = custom_sections
464            .clone()
465            .partition(|section| section.protection() == CustomSectionProtection::ReadExecute);
466        self.code_memory.push(CodeMemory::new());
467
468        let (mut allocated_functions, allocated_executable_sections, allocated_data_sections) =
469            self.code_memory
470                .last_mut()
471                .unwrap()
472                .allocate(
473                    function_bodies.as_slice(),
474                    executable_sections.as_slice(),
475                    data_sections.as_slice(),
476                )
477                .map_err(|message| {
478                    CompileError::Resource(format!(
479                        "failed to allocate memory for functions: {message}",
480                    ))
481                })?;
482
483        let allocated_functions_result = allocated_functions
484            .drain(0..functions_len)
485            .map(|slice| FunctionExtent {
486                ptr: FunctionBodyPtr(slice.as_ptr()),
487                length: slice.len(),
488            })
489            .collect::<PrimaryMap<LocalFunctionIndex, _>>();
490
491        let mut allocated_function_call_trampolines: PrimaryMap<SignatureIndex, VMTrampoline> =
492            PrimaryMap::new();
493        for ptr in allocated_functions
494            .drain(0..function_call_trampolines_len)
495            .map(|slice| slice.as_ptr())
496        {
497            let trampoline =
498                unsafe { std::mem::transmute::<*const VMFunctionBody, VMTrampoline>(ptr) };
499            allocated_function_call_trampolines.push(trampoline);
500        }
501
502        let allocated_dynamic_function_trampolines = allocated_functions
503            .drain(..)
504            .map(|slice| FunctionBodyPtr(slice.as_ptr()))
505            .collect::<PrimaryMap<FunctionIndex, _>>();
506
507        let mut exec_iter = allocated_executable_sections.iter();
508        let mut data_iter = allocated_data_sections.iter();
509        let allocated_custom_sections = custom_sections
510            .map(|section| {
511                SectionBodyPtr(
512                    if section.protection() == CustomSectionProtection::ReadExecute {
513                        exec_iter.next()
514                    } else {
515                        data_iter.next()
516                    }
517                    .unwrap()
518                    .as_ptr(),
519                )
520            })
521            .collect::<PrimaryMap<SectionIndex, _>>();
522        Ok((
523            allocated_functions_result,
524            allocated_function_call_trampolines,
525            allocated_dynamic_function_trampolines,
526            allocated_custom_sections,
527        ))
528    }
529
530    #[cfg(not(target_arch = "wasm32"))]
531    /// Make memory containing compiled code executable.
532    pub(crate) fn publish_compiled_code(&mut self) {
533        self.code_memory.last_mut().unwrap().publish();
534    }
535
536    #[cfg(not(target_arch = "wasm32"))]
537    #[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
538    /// Register DWARF-type exception handling information associated with the code.
539    pub(crate) fn publish_eh_frame(&mut self, eh_frame: Option<&[u8]>) -> Result<(), CompileError> {
540        self.code_memory
541            .last_mut()
542            .unwrap()
543            .unwind_registry_mut()
544            .publish_eh_frame(eh_frame)
545            .map_err(|e| {
546                CompileError::Resource(format!("Error while publishing the unwind code: {e}"))
547            })?;
548        Ok(())
549    }
550    #[cfg(all(target_os = "macos", target_arch = "aarch64"))]
551    /// Register macos-specific exception handling information associated with the code.
552    pub(crate) fn publish_compact_unwind(
553        &mut self,
554        compact_unwind: &[u8],
555        eh_personality_addr_in_got: Option<usize>,
556    ) -> Result<(), CompileError> {
557        self.code_memory
558            .last_mut()
559            .unwrap()
560            .unwind_registry_mut()
561            .publish_compact_unwind(compact_unwind, eh_personality_addr_in_got)
562            .map_err(|e| {
563                CompileError::Resource(format!("Error while publishing the unwind code: {e}"))
564            })?;
565        Ok(())
566    }
567
568    /// Memory-map a compiled ELF artifact image, keeping the mapping alive
569    /// for the lifetime of the engine. Returns the base address of the
570    /// mapping, which section/symbol offsets from the image are relative to.
571    #[cfg(not(target_arch = "wasm32"))]
572    pub(crate) fn map_elf_binary<'a, R: object::ReadRef<'a>>(
573        &mut self,
574        object_file: &object::File<'a, R>,
575        data: &[u8],
576    ) -> Result<*mut c_void, CompileError> {
577        let map = MemoryMappedBinary::try_from_bytes(object_file, data)
578            .map_err(CompileError::Resource)?;
579        let base = map.base();
580        self.elf_mapped_binary.push(map);
581        Ok(base)
582    }
583
584    /// Memory-map a compiled ELF artifact directly from a file.
585    #[cfg(all(not(target_arch = "wasm32"), unix))]
586    pub(crate) fn map_elf_binary_file<'a, R: object::ReadRef<'a>>(
587        &mut self,
588        object_file: &object::File<'a, R>,
589        file: RawFd,
590    ) -> Result<*mut c_void, CompileError> {
591        let map =
592            MemoryMappedBinary::try_from_file(object_file, file).map_err(CompileError::Resource)?;
593        let base = map.base();
594        self.elf_mapped_binary.push(map);
595        Ok(base)
596    }
597
598    #[cfg(all(not(target_arch = "wasm32"), unix, feature = "compiler"))]
599    pub(crate) fn debugger(&self) -> Option<Debugger> {
600        self.compiler
601            .as_ref()
602            .and_then(|compiler| compiler.get_debugger())
603    }
604
605    #[cfg(all(not(target_arch = "wasm32"), unix, feature = "compiler"))]
606    pub(crate) fn register_debugger(
607        &self,
608        path: &Path,
609        base: *mut c_void,
610        debugger: Debugger,
611    ) -> Result<(), CompileError> {
612        use std::io::Write as _;
613
614        let path = path
615            .canonicalize()
616            .unwrap_or_else(|_| path.to_path_buf())
617            .to_string_lossy()
618            .to_string();
619        let (command, source_command) = match debugger {
620            Debugger::Gdb => (
621                format!("add-symbol-file \"{path}\" -o 0x{:x}", base as usize),
622                "source",
623            ),
624            Debugger::Lldb => (
625                format!(
626                    "target modules add \"{path}\"\ntarget modules load --file \"{path}\" --slide 0x{:x}",
627                    base as usize
628                ),
629                "command source",
630            ),
631        };
632        let filename = format!(
633            "/tmp/wasmer-{}.{}",
634            debugger.to_string().to_lowercase(),
635            std::process::id()
636        );
637        let mut file = std::fs::OpenOptions::new()
638            .append(true)
639            .create(true)
640            .open(&filename)
641            .map_err(|error| CompileError::Resource(format!("Cannot open {filename}: {error}")))?;
642        writeln!(file, "{command}")
643            .map_err(|error| CompileError::Resource(format!("Cannot write {filename}: {error}")))?;
644
645        eprintln!("**************************");
646        eprintln!("For debugging under {debugger}, use: {source_command} {filename}");
647        eprintln!("**************************");
648        Ok(())
649    }
650
651    /// Register DWARF-type exception handling information associated with the code.
652    #[cfg(not(target_arch = "wasm32"))]
653    pub(crate) fn publish_elf_eh_frame(
654        &mut self,
655        address: u64,
656        size: u64,
657    ) -> Result<(), CompileError> {
658        self.elf_mapped_binary
659            .last_mut()
660            .unwrap()
661            .publish_eh_frame_section(address, size)
662            .map_err(|e| {
663                CompileError::Resource(format!("Error while publishing the unwind code: {e}"))
664            })
665    }
666
667    /// Shared signature registry.
668    #[cfg(not(target_arch = "wasm32"))]
669    pub fn signatures(&self) -> &SignatureRegistry {
670        &self.signatures
671    }
672
673    #[cfg(not(target_arch = "wasm32"))]
674    /// Register the frame info for the code memory
675    pub(crate) fn register_frame_info(&mut self, frame_info: GlobalFrameInfoRegistration) {
676        self.code_memory
677            .last_mut()
678            .unwrap()
679            .register_frame_info(frame_info);
680    }
681
682    #[cfg(not(target_arch = "wasm32"))]
683    /// Register the frame info for the most recently mapped ELF binary.
684    pub(crate) fn register_elf_frame_info(&mut self, frame_info: GlobalFrameInfoRegistration) {
685        self.elf_mapped_binary
686            .last_mut()
687            .unwrap()
688            .register_frame_info(frame_info);
689    }
690
691    #[cfg(all(not(target_arch = "wasm32"), feature = "compiler"))]
692    pub(crate) fn register_perfmap(
693        &self,
694        finished_functions: &PrimaryMap<LocalFunctionIndex, FunctionExtent>,
695        module_info: &ModuleInfo,
696    ) -> Result<(), CompileError> {
697        if self
698            .compiler
699            .as_ref()
700            .is_some_and(|v| v.get_perfmap_enabled())
701        {
702            use std::fs::OpenOptions;
703
704            let filename = format!("/tmp/perf-{}.map", std::process::id());
705            // We might be loading shared libraries and so we must append to the file.
706            let file = OpenOptions::new()
707                .append(true)
708                .create(true)
709                .open(&filename)
710                .map_err(|e| {
711                    CompileError::Codegen(format!("failed to open perf map file {filename}: {e}"))
712                })?;
713            let mut file = std::io::BufWriter::new(file);
714
715            for (func_index, code) in finished_functions.iter() {
716                let func_index = module_info.func_index(func_index);
717                if let Some(func_name) = module_info.function_names.get(&func_index) {
718                    let sanitized_name = func_name.replace(['\n', '\r'], "_");
719                    let line = format!(
720                        "{:p} {:x} {sanitized_name}\n",
721                        code.ptr.0 as *const _, code.length
722                    );
723                    write!(file, "{line}").map_err(|e| CompileError::Codegen(e.to_string()))?;
724                }
725            }
726
727            file.flush()
728                .map_err(|e| CompileError::Codegen(e.to_string()))?;
729        }
730
731        Ok(())
732    }
733}
734
735#[cfg(feature = "compiler")]
736impl From<Box<dyn CompilerConfig>> for Engine {
737    fn from(config: Box<dyn CompilerConfig>) -> Self {
738        EngineBuilder::new(config).engine()
739    }
740}
741
742impl From<EngineBuilder> for Engine {
743    fn from(engine_builder: EngineBuilder) -> Self {
744        engine_builder.engine()
745    }
746}
747
748impl From<&Self> for Engine {
749    fn from(engine_ref: &Self) -> Self {
750        engine_ref.cloned()
751    }
752}
753
754#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
755#[repr(transparent)]
756/// A unique identifier for an Engine.
757pub struct EngineId {
758    id: usize,
759}
760
761impl EngineId {
762    /// Format this identifier as a string.
763    pub fn id(&self) -> String {
764        format!("{}", self.id)
765    }
766}
767
768impl Clone for EngineId {
769    fn clone(&self) -> Self {
770        Self::default()
771    }
772}
773
774impl Default for EngineId {
775    fn default() -> Self {
776        static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
777        Self {
778            id: NEXT_ID.fetch_add(1, SeqCst),
779        }
780    }
781}