wasmer_compiler/engine/
inner.rs

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