wasmer_compiler/engine/
inner.rs

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