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