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