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