Skip to main content

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::Features;
12use wasmer_types::{CompilationProgressCallback, 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, SignatureIndex,
25    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, VMSharedSignatureIndex,
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) -> VMSharedSignatureIndex {
161        let compiler = self.inner();
162        compiler.signatures().register(func_type)
163    }
164
165    /// Lookup a signature
166    #[cfg(not(target_arch = "wasm32"))]
167    pub fn lookup_signature(&self, sig: VMSharedSignatureIndex) -> Option<FunctionType> {
168        let compiler = self.inner();
169        compiler.signatures().lookup(sig)
170    }
171
172    /// Validates a WebAssembly module
173    #[cfg(feature = "compiler")]
174    pub fn validate(&self, binary: &[u8]) -> Result<(), CompileError> {
175        self.inner().validate(binary)
176    }
177
178    /// Compile a WebAssembly binary
179    #[cfg(feature = "compiler")]
180    #[cfg(not(target_arch = "wasm32"))]
181    pub fn compile(&self, binary: &[u8]) -> Result<Arc<Artifact>, CompileError> {
182        Ok(Arc::new(Artifact::new(
183            self,
184            binary,
185            self.tunables.as_ref(),
186            None,
187        )?))
188    }
189
190    /// Compile a WebAssembly binary with a progress callback.
191    #[cfg(feature = "compiler")]
192    pub fn compile_with_progress(
193        &self,
194        binary: &[u8],
195        progress_callback: Option<CompilationProgressCallback>,
196    ) -> Result<Arc<Artifact>, CompileError> {
197        Ok(Arc::new(Artifact::new(
198            self,
199            binary,
200            self.tunables.as_ref(),
201            progress_callback,
202        )?))
203    }
204
205    /// Compile a WebAssembly binary
206    #[cfg(not(feature = "compiler"))]
207    #[cfg(not(target_arch = "wasm32"))]
208    pub fn compile(
209        &self,
210        _binary: &[u8],
211        _tunables: &dyn Tunables,
212    ) -> Result<Arc<Artifact>, CompileError> {
213        Err(CompileError::Codegen(
214            "The Engine is operating in headless mode, so it can not compile Modules.".to_string(),
215        ))
216    }
217
218    #[cfg(not(target_arch = "wasm32"))]
219    /// Deserializes a WebAssembly module which was previously serialized with
220    /// [`wasmer::Module::serialize`].
221    ///
222    /// # Safety
223    ///
224    /// See [`Artifact::deserialize_unchecked`].
225    pub unsafe fn deserialize_unchecked(
226        &self,
227        bytes: OwnedBuffer,
228    ) -> Result<Arc<Artifact>, DeserializeError> {
229        unsafe { Ok(Arc::new(Artifact::deserialize_unchecked(self, bytes)?)) }
230    }
231
232    /// Deserializes a WebAssembly module which was previously serialized with
233    /// [`wasmer::Module::serialize`].
234    ///
235    /// # Safety
236    ///
237    /// See [`Artifact::deserialize`].
238    #[cfg(not(target_arch = "wasm32"))]
239    pub unsafe fn deserialize(
240        &self,
241        bytes: OwnedBuffer,
242    ) -> Result<Arc<Artifact>, DeserializeError> {
243        unsafe { Ok(Arc::new(Artifact::deserialize(self, bytes)?)) }
244    }
245
246    /// Deserializes a WebAssembly module from a path.
247    ///
248    /// # Safety
249    /// See [`Artifact::deserialize`].
250    #[cfg(not(target_arch = "wasm32"))]
251    pub unsafe fn deserialize_from_file(
252        &self,
253        file_ref: &Path,
254    ) -> Result<Arc<Artifact>, DeserializeError> {
255        unsafe {
256            let file = std::fs::File::open(file_ref)?;
257            self.deserialize(
258                OwnedBuffer::from_file(&file)
259                    .map_err(|e| DeserializeError::Generic(e.to_string()))?,
260            )
261        }
262    }
263
264    /// Deserialize from a file path.
265    ///
266    /// # Safety
267    ///
268    /// See [`Artifact::deserialize_unchecked`].
269    #[cfg(not(target_arch = "wasm32"))]
270    pub unsafe fn deserialize_from_file_unchecked(
271        &self,
272        file_ref: &Path,
273    ) -> Result<Arc<Artifact>, DeserializeError> {
274        unsafe {
275            let file = std::fs::File::open(file_ref)?;
276            self.deserialize_unchecked(
277                OwnedBuffer::from_file(&file)
278                    .map_err(|e| DeserializeError::Generic(e.to_string()))?,
279            )
280        }
281    }
282
283    /// A unique identifier for this object.
284    ///
285    /// This exists to allow us to compare two Engines for equality. Otherwise,
286    /// comparing two trait objects unsafely relies on implementation details
287    /// of trait representation.
288    pub fn id(&self) -> &EngineId {
289        &self.engine_id
290    }
291
292    /// Clone the engine
293    pub fn cloned(&self) -> Self {
294        self.clone()
295    }
296
297    /// Attach a Tunable to this engine
298    #[cfg(not(target_arch = "wasm32"))]
299    pub fn set_tunables(&mut self, tunables: impl Tunables + Send + Sync + 'static) {
300        self.tunables = Arc::new(tunables);
301    }
302
303    /// Get a reference to attached Tunable of this engine
304    #[cfg(not(target_arch = "wasm32"))]
305    pub fn tunables(&self) -> &dyn Tunables {
306        self.tunables.as_ref()
307    }
308
309    /// Add suggested optimizations to this engine.
310    ///
311    /// # Note
312    ///
313    /// Not every backend supports every optimization. This function may fail (i.e. not set the
314    /// suggested optimizations) silently if the underlying engine backend does not support one or
315    /// more optimizations.
316    pub fn with_opts(
317        &mut self,
318        _suggested_opts: &wasmer_types::target::UserCompilerOptimizations,
319    ) -> Result<(), CompileError> {
320        #[cfg(feature = "compiler")]
321        {
322            let mut i = self.inner_mut();
323            if let Some(ref mut c) = i.compiler {
324                c.with_opts(_suggested_opts)?;
325            }
326        }
327
328        Ok(())
329    }
330}
331
332impl std::fmt::Debug for Engine {
333    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
334        write!(f, "{}", self.deterministic_id())
335    }
336}
337
338/// The inner contents of `Engine`
339pub struct EngineInner {
340    #[cfg(feature = "compiler")]
341    /// The compiler and cpu features
342    compiler: Option<Box<dyn Compiler>>,
343    #[cfg(feature = "compiler")]
344    /// The compiler and cpu features
345    features: Features,
346    /// The code memory is responsible of publishing the compiled
347    /// functions to memory.
348    #[cfg(not(target_arch = "wasm32"))]
349    code_memory: Vec<CodeMemory>,
350    /// The signature registry is used mainly to operate with trampolines
351    /// performantly.
352    #[cfg(not(target_arch = "wasm32"))]
353    signatures: SignatureRegistry,
354}
355
356impl std::fmt::Debug for EngineInner {
357    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
358        let mut formatter = f.debug_struct("EngineInner");
359        #[cfg(feature = "compiler")]
360        {
361            formatter.field("compiler", &self.compiler);
362            formatter.field("features", &self.features);
363        }
364
365        #[cfg(not(target_arch = "wasm32"))]
366        {
367            formatter.field("signatures", &self.signatures);
368        }
369
370        formatter.finish()
371    }
372}
373
374impl EngineInner {
375    /// Gets the compiler associated to this engine.
376    #[cfg(feature = "compiler")]
377    pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> {
378        match self.compiler.as_ref() {
379            None => Err(CompileError::Codegen(
380                "No compiler compiled into executable".to_string(),
381            )),
382            Some(compiler) => Ok(&**compiler),
383        }
384    }
385
386    /// Validate the module
387    #[cfg(feature = "compiler")]
388    pub fn validate(&self, data: &[u8]) -> Result<(), CompileError> {
389        let compiler = self.compiler()?;
390        compiler.validate_module(&self.features, data)
391    }
392
393    /// The Wasm features
394    #[cfg(feature = "compiler")]
395    pub fn features(&self) -> &Features {
396        &self.features
397    }
398
399    /// Allocate compiled functions into memory
400    #[cfg(not(target_arch = "wasm32"))]
401    #[allow(clippy::type_complexity)]
402    pub(crate) fn allocate<'a, FunctionBody, CustomSection>(
403        &'a mut self,
404        _module: &wasmer_types::ModuleInfo,
405        functions: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
406        function_call_trampolines: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
407        dynamic_function_trampolines: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
408        custom_sections: impl ExactSizeIterator<Item = &'a CustomSection> + Clone + 'a,
409    ) -> Result<
410        (
411            PrimaryMap<LocalFunctionIndex, FunctionExtent>,
412            PrimaryMap<SignatureIndex, VMTrampoline>,
413            PrimaryMap<FunctionIndex, FunctionBodyPtr>,
414            PrimaryMap<SectionIndex, SectionBodyPtr>,
415        ),
416        CompileError,
417    >
418    where
419        FunctionBody: FunctionBodyLike<'a> + 'a,
420        CustomSection: CustomSectionLike<'a> + 'a,
421    {
422        let functions_len = functions.len();
423        let function_call_trampolines_len = function_call_trampolines.len();
424
425        let function_bodies = functions
426            .chain(function_call_trampolines)
427            .chain(dynamic_function_trampolines)
428            .collect::<Vec<_>>();
429        let (executable_sections, data_sections): (Vec<_>, _) = custom_sections
430            .clone()
431            .partition(|section| section.protection() == CustomSectionProtection::ReadExecute);
432        self.code_memory.push(CodeMemory::new());
433
434        let (mut allocated_functions, allocated_executable_sections, allocated_data_sections) =
435            self.code_memory
436                .last_mut()
437                .unwrap()
438                .allocate(
439                    function_bodies.as_slice(),
440                    executable_sections.as_slice(),
441                    data_sections.as_slice(),
442                )
443                .map_err(|message| {
444                    CompileError::Resource(format!(
445                        "failed to allocate memory for functions: {message}",
446                    ))
447                })?;
448
449        let allocated_functions_result = allocated_functions
450            .drain(0..functions_len)
451            .map(|slice| FunctionExtent {
452                ptr: FunctionBodyPtr(slice.as_ptr()),
453                length: slice.len(),
454            })
455            .collect::<PrimaryMap<LocalFunctionIndex, _>>();
456
457        let mut allocated_function_call_trampolines: PrimaryMap<SignatureIndex, VMTrampoline> =
458            PrimaryMap::new();
459        for ptr in allocated_functions
460            .drain(0..function_call_trampolines_len)
461            .map(|slice| slice.as_ptr())
462        {
463            let trampoline =
464                unsafe { std::mem::transmute::<*const VMFunctionBody, VMTrampoline>(ptr) };
465            allocated_function_call_trampolines.push(trampoline);
466        }
467
468        let allocated_dynamic_function_trampolines = allocated_functions
469            .drain(..)
470            .map(|slice| FunctionBodyPtr(slice.as_ptr()))
471            .collect::<PrimaryMap<FunctionIndex, _>>();
472
473        let mut exec_iter = allocated_executable_sections.iter();
474        let mut data_iter = allocated_data_sections.iter();
475        let allocated_custom_sections = custom_sections
476            .map(|section| {
477                SectionBodyPtr(
478                    if section.protection() == CustomSectionProtection::ReadExecute {
479                        exec_iter.next()
480                    } else {
481                        data_iter.next()
482                    }
483                    .unwrap()
484                    .as_ptr(),
485                )
486            })
487            .collect::<PrimaryMap<SectionIndex, _>>();
488        Ok((
489            allocated_functions_result,
490            allocated_function_call_trampolines,
491            allocated_dynamic_function_trampolines,
492            allocated_custom_sections,
493        ))
494    }
495
496    #[cfg(not(target_arch = "wasm32"))]
497    /// Make memory containing compiled code executable.
498    pub(crate) fn publish_compiled_code(&mut self) {
499        self.code_memory.last_mut().unwrap().publish();
500    }
501
502    #[cfg(not(target_arch = "wasm32"))]
503    #[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
504    /// Register DWARF-type exception handling information associated with the code.
505    pub(crate) fn publish_eh_frame(&mut self, eh_frame: Option<&[u8]>) -> Result<(), CompileError> {
506        self.code_memory
507            .last_mut()
508            .unwrap()
509            .unwind_registry_mut()
510            .publish_eh_frame(eh_frame)
511            .map_err(|e| {
512                CompileError::Resource(format!("Error while publishing the unwind code: {e}"))
513            })?;
514        Ok(())
515    }
516    #[cfg(all(target_os = "macos", target_arch = "aarch64"))]
517    /// Register macos-specific exception handling information associated with the code.
518    pub(crate) fn publish_compact_unwind(
519        &mut self,
520        compact_unwind: &[u8],
521        eh_personality_addr_in_got: Option<usize>,
522    ) -> Result<(), CompileError> {
523        self.code_memory
524            .last_mut()
525            .unwrap()
526            .unwind_registry_mut()
527            .publish_compact_unwind(compact_unwind, eh_personality_addr_in_got)
528            .map_err(|e| {
529                CompileError::Resource(format!("Error while publishing the unwind code: {e}"))
530            })?;
531        Ok(())
532    }
533
534    /// Shared signature registry.
535    #[cfg(not(target_arch = "wasm32"))]
536    pub fn signatures(&self) -> &SignatureRegistry {
537        &self.signatures
538    }
539
540    #[cfg(not(target_arch = "wasm32"))]
541    /// Register the frame info for the code memory
542    pub(crate) fn register_frame_info(&mut self, frame_info: GlobalFrameInfoRegistration) {
543        self.code_memory
544            .last_mut()
545            .unwrap()
546            .register_frame_info(frame_info);
547    }
548
549    #[cfg(all(not(target_arch = "wasm32"), feature = "compiler"))]
550    pub(crate) fn register_perfmap(
551        &self,
552        finished_functions: &PrimaryMap<LocalFunctionIndex, FunctionExtent>,
553        module_info: &ModuleInfo,
554    ) -> Result<(), CompileError> {
555        if self
556            .compiler
557            .as_ref()
558            .is_some_and(|v| v.get_perfmap_enabled())
559        {
560            let filename = format!("/tmp/perf-{}.map", std::process::id());
561            let mut file = std::io::BufWriter::new(std::fs::File::create(filename).unwrap());
562
563            for (func_index, code) in finished_functions.iter() {
564                let func_index = module_info.func_index(func_index);
565                let name = if let Some(func_name) = module_info.function_names.get(&func_index) {
566                    func_name.clone()
567                } else {
568                    format!("{:p}", code.ptr.0)
569                };
570
571                let sanitized_name = name.replace(['\n', '\r'], "_");
572                let line = format!(
573                    "{:p} {:x} {}\n",
574                    code.ptr.0 as *const _, code.length, sanitized_name
575                );
576                write!(file, "{line}").map_err(|e| CompileError::Codegen(e.to_string()))?;
577                file.flush()
578                    .map_err(|e| CompileError::Codegen(e.to_string()))?;
579            }
580        }
581
582        Ok(())
583    }
584}
585
586#[cfg(feature = "compiler")]
587impl From<Box<dyn CompilerConfig>> for Engine {
588    fn from(config: Box<dyn CompilerConfig>) -> Self {
589        EngineBuilder::new(config).engine()
590    }
591}
592
593impl From<EngineBuilder> for Engine {
594    fn from(engine_builder: EngineBuilder) -> Self {
595        engine_builder.engine()
596    }
597}
598
599impl From<&Self> for Engine {
600    fn from(engine_ref: &Self) -> Self {
601        engine_ref.cloned()
602    }
603}
604
605#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
606#[repr(transparent)]
607/// A unique identifier for an Engine.
608pub struct EngineId {
609    id: usize,
610}
611
612impl EngineId {
613    /// Format this identifier as a string.
614    pub fn id(&self) -> String {
615        format!("{}", &self.id)
616    }
617}
618
619impl Clone for EngineId {
620    fn clone(&self) -> Self {
621        Self::default()
622    }
623}
624
625impl Default for EngineId {
626    fn default() -> Self {
627        static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
628        Self {
629            id: NEXT_ID.fetch_add(1, SeqCst),
630        }
631    }
632}