1use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
2use std::sync::{Arc, Mutex};
3
4use crate::engine::builder::EngineBuilder;
5#[cfg(feature = "compiler")]
6use crate::{Compiler, CompilerConfig};
7
8use wasmer_types::{CompilationProgressCallback, Features};
9use wasmer_types::{CompileError, target::Target};
10
11#[cfg(not(target_arch = "wasm32"))]
12use shared_buffer::OwnedBuffer;
13#[cfg(all(not(target_arch = "wasm32"), feature = "compiler"))]
14use std::io::Write;
15#[cfg(not(target_arch = "wasm32"))]
16use std::path::Path;
17#[cfg(all(not(target_arch = "wasm32"), feature = "compiler"))]
18use wasmer_types::ModuleInfo;
19#[cfg(not(target_arch = "wasm32"))]
20use wasmer_types::{
21 DeserializeError, FunctionIndex, FunctionType, LocalFunctionIndex, SignatureHash,
22 SignatureIndex, entity::PrimaryMap,
23};
24
25#[cfg(not(target_arch = "wasm32"))]
26use crate::{
27 Artifact, BaseTunables, CodeMemory, FunctionExtent, GlobalFrameInfoRegistration, Tunables,
28 types::{
29 function::FunctionBodyLike,
30 section::{CustomSectionLike, CustomSectionProtection, SectionIndex},
31 },
32};
33
34#[cfg(not(target_arch = "wasm32"))]
35use wasmer_vm::{
36 FunctionBodyPtr, SectionBodyPtr, SignatureRegistry, VMFunctionBody, VMSignatureHash,
37 VMTrampoline,
38};
39
40#[derive(Clone)]
42pub struct Engine {
43 inner: Arc<Mutex<EngineInner>>,
44 target: Arc<Target>,
46 engine_id: EngineId,
47 #[cfg(not(target_arch = "wasm32"))]
48 tunables: Arc<dyn Tunables + Send + Sync>,
49 name: String,
50}
51
52impl Engine {
53 #[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 }
79 }
80
81 pub fn name(&self) -> &str {
83 self.name.as_str()
84 }
85
86 pub fn deterministic_id(&self) -> String {
88 #[cfg(feature = "compiler")]
89 {
90 let i = self.inner();
91 if let Some(ref c) = i.compiler {
92 return c.deterministic_id();
93 } else {
94 return self.name.clone();
95 }
96 }
97
98 #[allow(unreachable_code)]
99 {
100 self.name.to_string()
101 }
102 }
103
104 pub fn headless() -> Self {
118 let target = Target::default();
119 #[cfg(not(target_arch = "wasm32"))]
120 let tunables = BaseTunables::for_target(&target);
121 Self {
122 inner: Arc::new(Mutex::new(EngineInner {
123 #[cfg(feature = "compiler")]
124 compiler: None,
125 #[cfg(feature = "compiler")]
126 features: Features::default(),
127 #[cfg(not(target_arch = "wasm32"))]
128 code_memory: vec![],
129 #[cfg(not(target_arch = "wasm32"))]
130 signatures: SignatureRegistry::new(),
131 })),
132 target: Arc::new(target),
133 engine_id: EngineId::default(),
134 #[cfg(not(target_arch = "wasm32"))]
135 tunables: Arc::new(tunables),
136 name: "engine-headless".to_string(),
137 }
138 }
139
140 pub fn inner(&self) -> std::sync::MutexGuard<'_, EngineInner> {
142 self.inner.lock().unwrap()
143 }
144
145 pub fn inner_mut(&self) -> std::sync::MutexGuard<'_, EngineInner> {
147 self.inner.lock().unwrap()
148 }
149
150 pub fn target(&self) -> &Target {
152 &self.target
153 }
154
155 #[cfg(not(target_arch = "wasm32"))]
157 pub fn register_signature(&self, func_type: &FunctionType) -> VMSignatureHash {
158 let compiler = self.inner();
159 compiler
160 .signatures()
161 .register(func_type, SignatureHash(func_type.signature_hash()))
162 }
163
164 #[cfg(not(target_arch = "wasm32"))]
166 pub fn lookup_signature(&self, sig_hash: VMSignatureHash) -> Option<FunctionType> {
167 let compiler = self.inner();
168 compiler.signatures().lookup_signature(sig_hash)
169 }
170
171 #[cfg(feature = "compiler")]
173 pub fn validate(&self, binary: &[u8]) -> Result<(), CompileError> {
174 self.inner().validate(binary)
175 }
176
177 #[cfg(feature = "compiler")]
179 #[cfg(not(target_arch = "wasm32"))]
180 pub fn compile(&self, binary: &[u8]) -> Result<Arc<Artifact>, CompileError> {
181 Ok(Arc::new(Artifact::new(
182 self,
183 binary,
184 self.tunables.as_ref(),
185 None,
186 )?))
187 }
188
189 #[cfg(feature = "compiler")]
191 pub fn compile_with_progress(
192 &self,
193 binary: &[u8],
194 progress_callback: Option<CompilationProgressCallback>,
195 ) -> Result<Arc<Artifact>, CompileError> {
196 Ok(Arc::new(Artifact::new(
197 self,
198 binary,
199 self.tunables.as_ref(),
200 progress_callback,
201 )?))
202 }
203
204 #[cfg(not(feature = "compiler"))]
206 #[cfg(not(target_arch = "wasm32"))]
207 pub fn compile_with_progress(
208 &self,
209 binary: &[u8],
210 _progress_callback: Option<CompilationProgressCallback>,
211 ) -> Result<Arc<Artifact>, CompileError> {
212 self.compile(binary, self.tunables.as_ref())
213 }
214
215 #[cfg(not(feature = "compiler"))]
217 #[cfg(not(target_arch = "wasm32"))]
218 pub fn compile(
219 &self,
220 _binary: &[u8],
221 _tunables: &dyn Tunables,
222 ) -> Result<Arc<Artifact>, CompileError> {
223 Err(CompileError::Codegen(
224 "The Engine is operating in headless mode, so it can not compile Modules.".to_string(),
225 ))
226 }
227
228 #[cfg(not(target_arch = "wasm32"))]
229 pub unsafe fn deserialize_unchecked(
236 &self,
237 bytes: OwnedBuffer,
238 ) -> Result<Arc<Artifact>, DeserializeError> {
239 unsafe { Ok(Arc::new(Artifact::deserialize_unchecked(self, bytes)?)) }
240 }
241
242 #[cfg(not(target_arch = "wasm32"))]
249 pub unsafe fn deserialize(
250 &self,
251 bytes: OwnedBuffer,
252 ) -> Result<Arc<Artifact>, DeserializeError> {
253 unsafe { Ok(Arc::new(Artifact::deserialize(self, bytes)?)) }
254 }
255
256 #[cfg(not(target_arch = "wasm32"))]
261 pub unsafe fn deserialize_from_file(
262 &self,
263 file_ref: &Path,
264 ) -> Result<Arc<Artifact>, DeserializeError> {
265 unsafe {
266 let file = std::fs::File::open(file_ref)?;
267 self.deserialize(
268 OwnedBuffer::from_file(&file)
269 .map_err(|e| DeserializeError::Generic(e.to_string()))?,
270 )
271 }
272 }
273
274 #[cfg(not(target_arch = "wasm32"))]
280 pub unsafe fn deserialize_from_file_unchecked(
281 &self,
282 file_ref: &Path,
283 ) -> Result<Arc<Artifact>, DeserializeError> {
284 unsafe {
285 let file = std::fs::File::open(file_ref)?;
286 self.deserialize_unchecked(
287 OwnedBuffer::from_file(&file)
288 .map_err(|e| DeserializeError::Generic(e.to_string()))?,
289 )
290 }
291 }
292
293 pub fn id(&self) -> &EngineId {
299 &self.engine_id
300 }
301
302 pub fn cloned(&self) -> Self {
304 self.clone()
305 }
306
307 #[cfg(not(target_arch = "wasm32"))]
309 pub fn set_tunables(&mut self, tunables: impl Tunables + Send + Sync + 'static) {
310 self.tunables = Arc::new(tunables);
311 }
312
313 #[cfg(not(target_arch = "wasm32"))]
315 pub fn tunables(&self) -> &dyn Tunables {
316 self.tunables.as_ref()
317 }
318
319 pub fn with_opts(
327 &mut self,
328 _suggested_opts: &wasmer_types::target::UserCompilerOptimizations,
329 ) -> Result<(), CompileError> {
330 #[cfg(feature = "compiler")]
331 {
332 let mut i = self.inner_mut();
333 if let Some(ref mut c) = i.compiler {
334 c.with_opts(_suggested_opts)?;
335 }
336 }
337
338 Ok(())
339 }
340}
341
342impl std::fmt::Debug for Engine {
343 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
344 write!(f, "{}", self.deterministic_id())
345 }
346}
347
348pub struct EngineInner {
350 #[cfg(feature = "compiler")]
351 compiler: Option<Box<dyn Compiler>>,
353 #[cfg(feature = "compiler")]
354 features: Features,
356 #[cfg(not(target_arch = "wasm32"))]
359 code_memory: Vec<CodeMemory>,
360 #[cfg(not(target_arch = "wasm32"))]
363 signatures: SignatureRegistry,
364}
365
366impl std::fmt::Debug for EngineInner {
367 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
368 let mut formatter = f.debug_struct("EngineInner");
369 #[cfg(feature = "compiler")]
370 {
371 formatter.field("compiler", &self.compiler);
372 formatter.field("features", &self.features);
373 }
374
375 #[cfg(not(target_arch = "wasm32"))]
376 {
377 formatter.field("signatures", &self.signatures);
378 }
379
380 formatter.finish()
381 }
382}
383
384impl EngineInner {
385 #[cfg(feature = "compiler")]
387 pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> {
388 match self.compiler.as_ref() {
389 None => Err(CompileError::Codegen(
390 "No compiler compiled into executable".to_string(),
391 )),
392 Some(compiler) => Ok(&**compiler),
393 }
394 }
395
396 #[cfg(feature = "compiler")]
398 pub fn validate(&self, data: &[u8]) -> Result<(), CompileError> {
399 let compiler = self.compiler()?;
400 compiler.validate_module(&self.features, data)
401 }
402
403 #[cfg(feature = "compiler")]
405 pub fn features(&self) -> &Features {
406 &self.features
407 }
408
409 #[cfg(not(target_arch = "wasm32"))]
411 #[allow(clippy::type_complexity)]
412 pub(crate) fn allocate<'a, FunctionBody, CustomSection>(
413 &'a mut self,
414 _module: &wasmer_types::ModuleInfo,
415 functions: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
416 function_call_trampolines: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
417 dynamic_function_trampolines: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
418 custom_sections: impl ExactSizeIterator<Item = &'a CustomSection> + Clone + 'a,
419 ) -> Result<
420 (
421 PrimaryMap<LocalFunctionIndex, FunctionExtent>,
422 PrimaryMap<SignatureIndex, VMTrampoline>,
423 PrimaryMap<FunctionIndex, FunctionBodyPtr>,
424 PrimaryMap<SectionIndex, SectionBodyPtr>,
425 ),
426 CompileError,
427 >
428 where
429 FunctionBody: FunctionBodyLike<'a> + 'a,
430 CustomSection: CustomSectionLike<'a> + 'a,
431 {
432 let functions_len = functions.len();
433 let function_call_trampolines_len = function_call_trampolines.len();
434
435 let function_bodies = functions
436 .chain(function_call_trampolines)
437 .chain(dynamic_function_trampolines)
438 .collect::<Vec<_>>();
439 let (executable_sections, data_sections): (Vec<_>, _) = custom_sections
440 .clone()
441 .partition(|section| section.protection() == CustomSectionProtection::ReadExecute);
442 self.code_memory.push(CodeMemory::new());
443
444 let (mut allocated_functions, allocated_executable_sections, allocated_data_sections) =
445 self.code_memory
446 .last_mut()
447 .unwrap()
448 .allocate(
449 function_bodies.as_slice(),
450 executable_sections.as_slice(),
451 data_sections.as_slice(),
452 )
453 .map_err(|message| {
454 CompileError::Resource(format!(
455 "failed to allocate memory for functions: {message}",
456 ))
457 })?;
458
459 let allocated_functions_result = allocated_functions
460 .drain(0..functions_len)
461 .map(|slice| FunctionExtent {
462 ptr: FunctionBodyPtr(slice.as_ptr()),
463 length: slice.len(),
464 })
465 .collect::<PrimaryMap<LocalFunctionIndex, _>>();
466
467 let mut allocated_function_call_trampolines: PrimaryMap<SignatureIndex, VMTrampoline> =
468 PrimaryMap::new();
469 for ptr in allocated_functions
470 .drain(0..function_call_trampolines_len)
471 .map(|slice| slice.as_ptr())
472 {
473 let trampoline =
474 unsafe { std::mem::transmute::<*const VMFunctionBody, VMTrampoline>(ptr) };
475 allocated_function_call_trampolines.push(trampoline);
476 }
477
478 let allocated_dynamic_function_trampolines = allocated_functions
479 .drain(..)
480 .map(|slice| FunctionBodyPtr(slice.as_ptr()))
481 .collect::<PrimaryMap<FunctionIndex, _>>();
482
483 let mut exec_iter = allocated_executable_sections.iter();
484 let mut data_iter = allocated_data_sections.iter();
485 let allocated_custom_sections = custom_sections
486 .map(|section| {
487 SectionBodyPtr(
488 if section.protection() == CustomSectionProtection::ReadExecute {
489 exec_iter.next()
490 } else {
491 data_iter.next()
492 }
493 .unwrap()
494 .as_ptr(),
495 )
496 })
497 .collect::<PrimaryMap<SectionIndex, _>>();
498 Ok((
499 allocated_functions_result,
500 allocated_function_call_trampolines,
501 allocated_dynamic_function_trampolines,
502 allocated_custom_sections,
503 ))
504 }
505
506 #[cfg(not(target_arch = "wasm32"))]
507 pub(crate) fn publish_compiled_code(&mut self) {
509 self.code_memory.last_mut().unwrap().publish();
510 }
511
512 #[cfg(not(target_arch = "wasm32"))]
513 #[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
514 pub(crate) fn publish_eh_frame(&mut self, eh_frame: Option<&[u8]>) -> Result<(), CompileError> {
516 self.code_memory
517 .last_mut()
518 .unwrap()
519 .unwind_registry_mut()
520 .publish_eh_frame(eh_frame)
521 .map_err(|e| {
522 CompileError::Resource(format!("Error while publishing the unwind code: {e}"))
523 })?;
524 Ok(())
525 }
526 #[cfg(all(target_os = "macos", target_arch = "aarch64"))]
527 pub(crate) fn publish_compact_unwind(
529 &mut self,
530 compact_unwind: &[u8],
531 eh_personality_addr_in_got: Option<usize>,
532 ) -> Result<(), CompileError> {
533 self.code_memory
534 .last_mut()
535 .unwrap()
536 .unwind_registry_mut()
537 .publish_compact_unwind(compact_unwind, eh_personality_addr_in_got)
538 .map_err(|e| {
539 CompileError::Resource(format!("Error while publishing the unwind code: {e}"))
540 })?;
541 Ok(())
542 }
543
544 #[cfg(not(target_arch = "wasm32"))]
546 pub fn signatures(&self) -> &SignatureRegistry {
547 &self.signatures
548 }
549
550 #[cfg(not(target_arch = "wasm32"))]
551 pub(crate) fn register_frame_info(&mut self, frame_info: GlobalFrameInfoRegistration) {
553 self.code_memory
554 .last_mut()
555 .unwrap()
556 .register_frame_info(frame_info);
557 }
558
559 #[cfg(all(not(target_arch = "wasm32"), feature = "compiler"))]
560 pub(crate) fn register_perfmap(
561 &self,
562 finished_functions: &PrimaryMap<LocalFunctionIndex, FunctionExtent>,
563 module_info: &ModuleInfo,
564 ) -> Result<(), CompileError> {
565 if self
566 .compiler
567 .as_ref()
568 .is_some_and(|v| v.get_perfmap_enabled())
569 {
570 use std::fs::OpenOptions;
571
572 let filename = format!("/tmp/perf-{}.map", std::process::id());
573 let file = OpenOptions::new()
575 .append(true)
576 .create(true)
577 .open(&filename)
578 .map_err(|e| {
579 CompileError::Codegen(format!("failed to open perf map file {filename}: {e}"))
580 })?;
581 let mut file = std::io::BufWriter::new(file);
582
583 for (func_index, code) in finished_functions.iter() {
584 let func_index = module_info.func_index(func_index);
585 if let Some(func_name) = module_info.function_names.get(&func_index) {
586 let sanitized_name = func_name.replace(['\n', '\r'], "_");
587 let line = format!(
588 "{:p} {:x} {sanitized_name}\n",
589 code.ptr.0 as *const _, code.length
590 );
591 write!(file, "{line}").map_err(|e| CompileError::Codegen(e.to_string()))?;
592 }
593 }
594
595 file.flush()
596 .map_err(|e| CompileError::Codegen(e.to_string()))?;
597 }
598
599 Ok(())
600 }
601}
602
603#[cfg(feature = "compiler")]
604impl From<Box<dyn CompilerConfig>> for Engine {
605 fn from(config: Box<dyn CompilerConfig>) -> Self {
606 EngineBuilder::new(config).engine()
607 }
608}
609
610impl From<EngineBuilder> for Engine {
611 fn from(engine_builder: EngineBuilder) -> Self {
612 engine_builder.engine()
613 }
614}
615
616impl From<&Self> for Engine {
617 fn from(engine_ref: &Self) -> Self {
618 engine_ref.cloned()
619 }
620}
621
622#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
623#[repr(transparent)]
624pub struct EngineId {
626 id: usize,
627}
628
629impl EngineId {
630 pub fn id(&self) -> String {
632 format!("{}", self.id)
633 }
634}
635
636impl Clone for EngineId {
637 fn clone(&self) -> Self {
638 Self::default()
639 }
640}
641
642impl Default for EngineId {
643 fn default() -> Self {
644 static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
645 Self {
646 id: NEXT_ID.fetch_add(1, SeqCst),
647 }
648 }
649}