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