wasmer_engine_universal/
engine.rs

1//! Universal compilation.
2
3use crate::CodeMemory;
4use crate::UniversalArtifact;
5use loupe::MemoryUsage;
6use std::sync::{Arc, Mutex};
7#[cfg(feature = "compiler")]
8use wasmer_compiler::Compiler;
9use wasmer_compiler::{
10    CompileError, CustomSection, CustomSectionProtection, FunctionBody, SectionIndex, Target,
11};
12use wasmer_engine::{Artifact, DeserializeError, Engine, EngineId, FunctionExtent, Tunables};
13use wasmer_engine_universal_artifact::UniversalEngineBuilder;
14use wasmer_types::entity::PrimaryMap;
15use wasmer_types::{
16    Features, FunctionIndex, FunctionType, LocalFunctionIndex, ModuleInfo, SignatureIndex,
17};
18use wasmer_vm::{
19    FuncDataRegistry, FunctionBodyPtr, SectionBodyPtr, SignatureRegistry, VMCallerCheckedAnyfunc,
20    VMFuncRef, VMFunctionBody, VMSharedSignatureIndex, VMTrampoline,
21};
22
23/// A WebAssembly `Universal` Engine.
24#[derive(Clone, MemoryUsage)]
25pub struct UniversalEngine {
26    inner: Arc<Mutex<UniversalEngineInner>>,
27    /// The target for the compiler
28    target: Arc<Target>,
29    engine_id: EngineId,
30}
31
32impl UniversalEngine {
33    /// Create a new `UniversalEngine` with the given config
34    #[cfg(feature = "compiler")]
35    pub fn new(compiler: Box<dyn Compiler>, target: Target, features: Features) -> Self {
36        Self {
37            inner: Arc::new(Mutex::new(UniversalEngineInner {
38                builder: UniversalEngineBuilder::new(Some(compiler), features),
39                code_memory: vec![],
40                signatures: SignatureRegistry::new(),
41                func_data: Arc::new(FuncDataRegistry::new()),
42            })),
43            target: Arc::new(target),
44            engine_id: EngineId::default(),
45        }
46    }
47
48    /// Create a headless `UniversalEngine`
49    ///
50    /// A headless engine is an engine without any compiler attached.
51    /// This is useful for assuring a minimal runtime for running
52    /// WebAssembly modules.
53    ///
54    /// For example, for running in IoT devices where compilers are very
55    /// expensive, or also to optimize startup speed.
56    ///
57    /// # Important
58    ///
59    /// Headless engines can't compile or validate any modules,
60    /// they just take already processed Modules (via `Module::serialize`).
61    pub fn headless() -> Self {
62        Self {
63            inner: Arc::new(Mutex::new(UniversalEngineInner {
64                builder: UniversalEngineBuilder::new(None, Features::default()),
65                code_memory: vec![],
66                signatures: SignatureRegistry::new(),
67                func_data: Arc::new(FuncDataRegistry::new()),
68            })),
69            target: Arc::new(Target::default()),
70            engine_id: EngineId::default(),
71        }
72    }
73
74    pub(crate) fn inner(&self) -> std::sync::MutexGuard<'_, UniversalEngineInner> {
75        self.inner.lock().unwrap()
76    }
77
78    pub(crate) fn inner_mut(&self) -> std::sync::MutexGuard<'_, UniversalEngineInner> {
79        self.inner.lock().unwrap()
80    }
81}
82
83impl Engine for UniversalEngine {
84    /// The target
85    fn target(&self) -> &Target {
86        &self.target
87    }
88
89    /// Register a signature
90    fn register_signature(&self, func_type: &FunctionType) -> VMSharedSignatureIndex {
91        let compiler = self.inner();
92        compiler.signatures().register(func_type)
93    }
94
95    fn register_function_metadata(&self, func_data: VMCallerCheckedAnyfunc) -> VMFuncRef {
96        let compiler = self.inner();
97        compiler.func_data().register(func_data)
98    }
99
100    /// Lookup a signature
101    fn lookup_signature(&self, sig: VMSharedSignatureIndex) -> Option<FunctionType> {
102        let compiler = self.inner();
103        compiler.signatures().lookup(sig)
104    }
105
106    /// Validates a WebAssembly module
107    fn validate(&self, binary: &[u8]) -> Result<(), CompileError> {
108        self.inner().validate(binary)
109    }
110
111    /// Compile a WebAssembly binary
112    #[cfg(feature = "compiler")]
113    fn compile(
114        &self,
115        binary: &[u8],
116        tunables: &dyn Tunables,
117    ) -> Result<Arc<dyn Artifact>, CompileError> {
118        Ok(Arc::new(UniversalArtifact::new(&self, binary, tunables)?))
119    }
120
121    /// Compile a WebAssembly binary
122    #[cfg(not(feature = "compiler"))]
123    fn compile(
124        &self,
125        _binary: &[u8],
126        _tunables: &dyn Tunables,
127    ) -> Result<Arc<dyn Artifact>, CompileError> {
128        Err(CompileError::Codegen(
129            "The UniversalEngine is operating in headless mode, so it can not compile Modules."
130                .to_string(),
131        ))
132    }
133
134    /// Deserializes a WebAssembly module
135    unsafe fn deserialize(&self, bytes: &[u8]) -> Result<Arc<dyn Artifact>, DeserializeError> {
136        Ok(Arc::new(UniversalArtifact::deserialize(&self, &bytes)?))
137    }
138
139    fn id(&self) -> &EngineId {
140        &self.engine_id
141    }
142
143    fn cloned(&self) -> Arc<dyn Engine + Send + Sync> {
144        Arc::new(self.clone())
145    }
146}
147
148/// The inner contents of `UniversalEngine`
149#[derive(MemoryUsage)]
150pub struct UniversalEngineInner {
151    /// The builder (include compiler and cpu features)
152    builder: UniversalEngineBuilder,
153    /// The code memory is responsible of publishing the compiled
154    /// functions to memory.
155    code_memory: Vec<CodeMemory>,
156    /// The signature registry is used mainly to operate with trampolines
157    /// performantly.
158    signatures: SignatureRegistry,
159    /// The backing storage of `VMFuncRef`s. This centralized store ensures that 2
160    /// functions with the same `VMCallerCheckedAnyfunc` will have the same `VMFuncRef`.
161    /// It also guarantees that the `VMFuncRef`s stay valid until the engine is dropped.
162    func_data: Arc<FuncDataRegistry>,
163}
164
165impl UniversalEngineInner {
166    /// Gets the compiler associated to this engine.
167    #[cfg(feature = "compiler")]
168    pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> {
169        self.builder.compiler()
170    }
171
172    /// Validate the module
173    pub fn validate<'data>(&self, data: &'data [u8]) -> Result<(), CompileError> {
174        self.builder.validate(data)
175    }
176
177    /// The Wasm features
178    pub fn features(&self) -> &Features {
179        self.builder.features()
180    }
181
182    pub fn builder_mut(&mut self) -> &mut UniversalEngineBuilder {
183        &mut self.builder
184    }
185
186    /// Allocate compiled functions into memory
187    #[allow(clippy::type_complexity)]
188    pub(crate) fn allocate(
189        &mut self,
190        _module: &ModuleInfo,
191        functions: &PrimaryMap<LocalFunctionIndex, FunctionBody>,
192        function_call_trampolines: &PrimaryMap<SignatureIndex, FunctionBody>,
193        dynamic_function_trampolines: &PrimaryMap<FunctionIndex, FunctionBody>,
194        custom_sections: &PrimaryMap<SectionIndex, CustomSection>,
195    ) -> Result<
196        (
197            PrimaryMap<LocalFunctionIndex, FunctionExtent>,
198            PrimaryMap<SignatureIndex, VMTrampoline>,
199            PrimaryMap<FunctionIndex, FunctionBodyPtr>,
200            PrimaryMap<SectionIndex, SectionBodyPtr>,
201        ),
202        CompileError,
203    > {
204        let function_bodies = functions
205            .values()
206            .chain(function_call_trampolines.values())
207            .chain(dynamic_function_trampolines.values())
208            .collect::<Vec<_>>();
209        let (executable_sections, data_sections): (Vec<_>, _) = custom_sections
210            .values()
211            .partition(|section| section.protection == CustomSectionProtection::ReadExecute);
212        self.code_memory.push(CodeMemory::new());
213
214        let (mut allocated_functions, allocated_executable_sections, allocated_data_sections) =
215            self.code_memory
216                .last_mut()
217                .unwrap()
218                .allocate(
219                    function_bodies.as_slice(),
220                    executable_sections.as_slice(),
221                    data_sections.as_slice(),
222                )
223                .map_err(|message| {
224                    CompileError::Resource(format!(
225                        "failed to allocate memory for functions: {}",
226                        message
227                    ))
228                })?;
229
230        let allocated_functions_result = allocated_functions
231            .drain(0..functions.len())
232            .map(|slice| FunctionExtent {
233                ptr: FunctionBodyPtr(slice.as_ptr()),
234                length: slice.len(),
235            })
236            .collect::<PrimaryMap<LocalFunctionIndex, _>>();
237
238        let mut allocated_function_call_trampolines: PrimaryMap<SignatureIndex, VMTrampoline> =
239            PrimaryMap::new();
240        for ptr in allocated_functions
241            .drain(0..function_call_trampolines.len())
242            .map(|slice| slice.as_ptr())
243        {
244            let trampoline =
245                unsafe { std::mem::transmute::<*const VMFunctionBody, VMTrampoline>(ptr) };
246            allocated_function_call_trampolines.push(trampoline);
247        }
248
249        let allocated_dynamic_function_trampolines = allocated_functions
250            .drain(..)
251            .map(|slice| FunctionBodyPtr(slice.as_ptr()))
252            .collect::<PrimaryMap<FunctionIndex, _>>();
253
254        let mut exec_iter = allocated_executable_sections.iter();
255        let mut data_iter = allocated_data_sections.iter();
256        let allocated_custom_sections = custom_sections
257            .iter()
258            .map(|(_, section)| {
259                SectionBodyPtr(
260                    if section.protection == CustomSectionProtection::ReadExecute {
261                        exec_iter.next()
262                    } else {
263                        data_iter.next()
264                    }
265                    .unwrap()
266                    .as_ptr(),
267                )
268            })
269            .collect::<PrimaryMap<SectionIndex, _>>();
270
271        Ok((
272            allocated_functions_result,
273            allocated_function_call_trampolines,
274            allocated_dynamic_function_trampolines,
275            allocated_custom_sections,
276        ))
277    }
278
279    /// Make memory containing compiled code executable.
280    pub(crate) fn publish_compiled_code(&mut self) {
281        self.code_memory.last_mut().unwrap().publish();
282    }
283
284    /// Register DWARF-type exception handling information associated with the code.
285    pub(crate) fn publish_eh_frame(&mut self, eh_frame: Option<&[u8]>) -> Result<(), CompileError> {
286        self.code_memory
287            .last_mut()
288            .unwrap()
289            .unwind_registry_mut()
290            .publish(eh_frame)
291            .map_err(|e| {
292                CompileError::Resource(format!("Error while publishing the unwind code: {}", e))
293            })?;
294        Ok(())
295    }
296
297    /// Shared signature registry.
298    pub fn signatures(&self) -> &SignatureRegistry {
299        &self.signatures
300    }
301
302    /// Shared func metadata registry.
303    pub(crate) fn func_data(&self) -> &Arc<FuncDataRegistry> {
304        &self.func_data
305    }
306}