linera_wasmer_compiler/engine/
inner.rs

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