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::Features;
12use wasmer_types::{CompilationProgressCallback, 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, SignatureIndex,
25 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, VMSharedSignatureIndex,
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) -> VMSharedSignatureIndex {
161 let compiler = self.inner();
162 compiler.signatures().register(func_type)
163 }
164
165 #[cfg(not(target_arch = "wasm32"))]
167 pub fn lookup_signature(&self, sig: VMSharedSignatureIndex) -> Option<FunctionType> {
168 let compiler = self.inner();
169 compiler.signatures().lookup(sig)
170 }
171
172 #[cfg(feature = "compiler")]
174 pub fn validate(&self, binary: &[u8]) -> Result<(), CompileError> {
175 self.inner().validate(binary)
176 }
177
178 #[cfg(feature = "compiler")]
180 #[cfg(not(target_arch = "wasm32"))]
181 pub fn compile(&self, binary: &[u8]) -> Result<Arc<Artifact>, CompileError> {
182 Ok(Arc::new(Artifact::new(
183 self,
184 binary,
185 self.tunables.as_ref(),
186 None,
187 )?))
188 }
189
190 #[cfg(feature = "compiler")]
192 pub fn compile_with_progress(
193 &self,
194 binary: &[u8],
195 progress_callback: Option<CompilationProgressCallback>,
196 ) -> Result<Arc<Artifact>, CompileError> {
197 Ok(Arc::new(Artifact::new(
198 self,
199 binary,
200 self.tunables.as_ref(),
201 progress_callback,
202 )?))
203 }
204
205 #[cfg(not(feature = "compiler"))]
207 #[cfg(not(target_arch = "wasm32"))]
208 pub fn compile(
209 &self,
210 _binary: &[u8],
211 _tunables: &dyn Tunables,
212 ) -> Result<Arc<Artifact>, CompileError> {
213 Err(CompileError::Codegen(
214 "The Engine is operating in headless mode, so it can not compile Modules.".to_string(),
215 ))
216 }
217
218 #[cfg(not(target_arch = "wasm32"))]
219 pub unsafe fn deserialize_unchecked(
226 &self,
227 bytes: OwnedBuffer,
228 ) -> Result<Arc<Artifact>, DeserializeError> {
229 unsafe { Ok(Arc::new(Artifact::deserialize_unchecked(self, bytes)?)) }
230 }
231
232 #[cfg(not(target_arch = "wasm32"))]
239 pub unsafe fn deserialize(
240 &self,
241 bytes: OwnedBuffer,
242 ) -> Result<Arc<Artifact>, DeserializeError> {
243 unsafe { Ok(Arc::new(Artifact::deserialize(self, bytes)?)) }
244 }
245
246 #[cfg(not(target_arch = "wasm32"))]
251 pub unsafe fn deserialize_from_file(
252 &self,
253 file_ref: &Path,
254 ) -> Result<Arc<Artifact>, DeserializeError> {
255 unsafe {
256 let file = std::fs::File::open(file_ref)?;
257 self.deserialize(
258 OwnedBuffer::from_file(&file)
259 .map_err(|e| DeserializeError::Generic(e.to_string()))?,
260 )
261 }
262 }
263
264 #[cfg(not(target_arch = "wasm32"))]
270 pub unsafe fn deserialize_from_file_unchecked(
271 &self,
272 file_ref: &Path,
273 ) -> Result<Arc<Artifact>, DeserializeError> {
274 unsafe {
275 let file = std::fs::File::open(file_ref)?;
276 self.deserialize_unchecked(
277 OwnedBuffer::from_file(&file)
278 .map_err(|e| DeserializeError::Generic(e.to_string()))?,
279 )
280 }
281 }
282
283 pub fn id(&self) -> &EngineId {
289 &self.engine_id
290 }
291
292 pub fn cloned(&self) -> Self {
294 self.clone()
295 }
296
297 #[cfg(not(target_arch = "wasm32"))]
299 pub fn set_tunables(&mut self, tunables: impl Tunables + Send + Sync + 'static) {
300 self.tunables = Arc::new(tunables);
301 }
302
303 #[cfg(not(target_arch = "wasm32"))]
305 pub fn tunables(&self) -> &dyn Tunables {
306 self.tunables.as_ref()
307 }
308
309 pub fn with_opts(
317 &mut self,
318 _suggested_opts: &wasmer_types::target::UserCompilerOptimizations,
319 ) -> Result<(), CompileError> {
320 #[cfg(feature = "compiler")]
321 {
322 let mut i = self.inner_mut();
323 if let Some(ref mut c) = i.compiler {
324 c.with_opts(_suggested_opts)?;
325 }
326 }
327
328 Ok(())
329 }
330}
331
332impl std::fmt::Debug for Engine {
333 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
334 write!(f, "{}", self.deterministic_id())
335 }
336}
337
338pub struct EngineInner {
340 #[cfg(feature = "compiler")]
341 compiler: Option<Box<dyn Compiler>>,
343 #[cfg(feature = "compiler")]
344 features: Features,
346 #[cfg(not(target_arch = "wasm32"))]
349 code_memory: Vec<CodeMemory>,
350 #[cfg(not(target_arch = "wasm32"))]
353 signatures: SignatureRegistry,
354}
355
356impl std::fmt::Debug for EngineInner {
357 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
358 let mut formatter = f.debug_struct("EngineInner");
359 #[cfg(feature = "compiler")]
360 {
361 formatter.field("compiler", &self.compiler);
362 formatter.field("features", &self.features);
363 }
364
365 #[cfg(not(target_arch = "wasm32"))]
366 {
367 formatter.field("signatures", &self.signatures);
368 }
369
370 formatter.finish()
371 }
372}
373
374impl EngineInner {
375 #[cfg(feature = "compiler")]
377 pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> {
378 match self.compiler.as_ref() {
379 None => Err(CompileError::Codegen(
380 "No compiler compiled into executable".to_string(),
381 )),
382 Some(compiler) => Ok(&**compiler),
383 }
384 }
385
386 #[cfg(feature = "compiler")]
388 pub fn validate(&self, data: &[u8]) -> Result<(), CompileError> {
389 let compiler = self.compiler()?;
390 compiler.validate_module(&self.features, data)
391 }
392
393 #[cfg(feature = "compiler")]
395 pub fn features(&self) -> &Features {
396 &self.features
397 }
398
399 #[cfg(not(target_arch = "wasm32"))]
401 #[allow(clippy::type_complexity)]
402 pub(crate) fn allocate<'a, FunctionBody, CustomSection>(
403 &'a mut self,
404 _module: &wasmer_types::ModuleInfo,
405 functions: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
406 function_call_trampolines: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
407 dynamic_function_trampolines: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
408 custom_sections: impl ExactSizeIterator<Item = &'a CustomSection> + Clone + 'a,
409 ) -> Result<
410 (
411 PrimaryMap<LocalFunctionIndex, FunctionExtent>,
412 PrimaryMap<SignatureIndex, VMTrampoline>,
413 PrimaryMap<FunctionIndex, FunctionBodyPtr>,
414 PrimaryMap<SectionIndex, SectionBodyPtr>,
415 ),
416 CompileError,
417 >
418 where
419 FunctionBody: FunctionBodyLike<'a> + 'a,
420 CustomSection: CustomSectionLike<'a> + 'a,
421 {
422 let functions_len = functions.len();
423 let function_call_trampolines_len = function_call_trampolines.len();
424
425 let function_bodies = functions
426 .chain(function_call_trampolines)
427 .chain(dynamic_function_trampolines)
428 .collect::<Vec<_>>();
429 let (executable_sections, data_sections): (Vec<_>, _) = custom_sections
430 .clone()
431 .partition(|section| section.protection() == CustomSectionProtection::ReadExecute);
432 self.code_memory.push(CodeMemory::new());
433
434 let (mut allocated_functions, allocated_executable_sections, allocated_data_sections) =
435 self.code_memory
436 .last_mut()
437 .unwrap()
438 .allocate(
439 function_bodies.as_slice(),
440 executable_sections.as_slice(),
441 data_sections.as_slice(),
442 )
443 .map_err(|message| {
444 CompileError::Resource(format!(
445 "failed to allocate memory for functions: {message}",
446 ))
447 })?;
448
449 let allocated_functions_result = allocated_functions
450 .drain(0..functions_len)
451 .map(|slice| FunctionExtent {
452 ptr: FunctionBodyPtr(slice.as_ptr()),
453 length: slice.len(),
454 })
455 .collect::<PrimaryMap<LocalFunctionIndex, _>>();
456
457 let mut allocated_function_call_trampolines: PrimaryMap<SignatureIndex, VMTrampoline> =
458 PrimaryMap::new();
459 for ptr in allocated_functions
460 .drain(0..function_call_trampolines_len)
461 .map(|slice| slice.as_ptr())
462 {
463 let trampoline =
464 unsafe { std::mem::transmute::<*const VMFunctionBody, VMTrampoline>(ptr) };
465 allocated_function_call_trampolines.push(trampoline);
466 }
467
468 let allocated_dynamic_function_trampolines = allocated_functions
469 .drain(..)
470 .map(|slice| FunctionBodyPtr(slice.as_ptr()))
471 .collect::<PrimaryMap<FunctionIndex, _>>();
472
473 let mut exec_iter = allocated_executable_sections.iter();
474 let mut data_iter = allocated_data_sections.iter();
475 let allocated_custom_sections = custom_sections
476 .map(|section| {
477 SectionBodyPtr(
478 if section.protection() == CustomSectionProtection::ReadExecute {
479 exec_iter.next()
480 } else {
481 data_iter.next()
482 }
483 .unwrap()
484 .as_ptr(),
485 )
486 })
487 .collect::<PrimaryMap<SectionIndex, _>>();
488 Ok((
489 allocated_functions_result,
490 allocated_function_call_trampolines,
491 allocated_dynamic_function_trampolines,
492 allocated_custom_sections,
493 ))
494 }
495
496 #[cfg(not(target_arch = "wasm32"))]
497 pub(crate) fn publish_compiled_code(&mut self) {
499 self.code_memory.last_mut().unwrap().publish();
500 }
501
502 #[cfg(not(target_arch = "wasm32"))]
503 #[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
504 pub(crate) fn publish_eh_frame(&mut self, eh_frame: Option<&[u8]>) -> Result<(), CompileError> {
506 self.code_memory
507 .last_mut()
508 .unwrap()
509 .unwind_registry_mut()
510 .publish_eh_frame(eh_frame)
511 .map_err(|e| {
512 CompileError::Resource(format!("Error while publishing the unwind code: {e}"))
513 })?;
514 Ok(())
515 }
516 #[cfg(all(target_os = "macos", target_arch = "aarch64"))]
517 pub(crate) fn publish_compact_unwind(
519 &mut self,
520 compact_unwind: &[u8],
521 eh_personality_addr_in_got: Option<usize>,
522 ) -> Result<(), CompileError> {
523 self.code_memory
524 .last_mut()
525 .unwrap()
526 .unwind_registry_mut()
527 .publish_compact_unwind(compact_unwind, eh_personality_addr_in_got)
528 .map_err(|e| {
529 CompileError::Resource(format!("Error while publishing the unwind code: {e}"))
530 })?;
531 Ok(())
532 }
533
534 #[cfg(not(target_arch = "wasm32"))]
536 pub fn signatures(&self) -> &SignatureRegistry {
537 &self.signatures
538 }
539
540 #[cfg(not(target_arch = "wasm32"))]
541 pub(crate) fn register_frame_info(&mut self, frame_info: GlobalFrameInfoRegistration) {
543 self.code_memory
544 .last_mut()
545 .unwrap()
546 .register_frame_info(frame_info);
547 }
548
549 #[cfg(all(not(target_arch = "wasm32"), feature = "compiler"))]
550 pub(crate) fn register_perfmap(
551 &self,
552 finished_functions: &PrimaryMap<LocalFunctionIndex, FunctionExtent>,
553 module_info: &ModuleInfo,
554 ) -> Result<(), CompileError> {
555 if self
556 .compiler
557 .as_ref()
558 .is_some_and(|v| v.get_perfmap_enabled())
559 {
560 let filename = format!("/tmp/perf-{}.map", std::process::id());
561 let mut file = std::io::BufWriter::new(std::fs::File::create(filename).unwrap());
562
563 for (func_index, code) in finished_functions.iter() {
564 let func_index = module_info.func_index(func_index);
565 let name = if let Some(func_name) = module_info.function_names.get(&func_index) {
566 func_name.clone()
567 } else {
568 format!("{:p}", code.ptr.0)
569 };
570
571 let sanitized_name = name.replace(['\n', '\r'], "_");
572 let line = format!(
573 "{:p} {:x} {}\n",
574 code.ptr.0 as *const _, code.length, sanitized_name
575 );
576 write!(file, "{line}").map_err(|e| CompileError::Codegen(e.to_string()))?;
577 file.flush()
578 .map_err(|e| CompileError::Codegen(e.to_string()))?;
579 }
580 }
581
582 Ok(())
583 }
584}
585
586#[cfg(feature = "compiler")]
587impl From<Box<dyn CompilerConfig>> for Engine {
588 fn from(config: Box<dyn CompilerConfig>) -> Self {
589 EngineBuilder::new(config).engine()
590 }
591}
592
593impl From<EngineBuilder> for Engine {
594 fn from(engine_builder: EngineBuilder) -> Self {
595 engine_builder.engine()
596 }
597}
598
599impl From<&Self> for Engine {
600 fn from(engine_ref: &Self) -> Self {
601 engine_ref.cloned()
602 }
603}
604
605#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
606#[repr(transparent)]
607pub struct EngineId {
609 id: usize,
610}
611
612impl EngineId {
613 pub fn id(&self) -> String {
615 format!("{}", &self.id)
616 }
617}
618
619impl Clone for EngineId {
620 fn clone(&self) -> Self {
621 Self::default()
622 }
623}
624
625impl Default for EngineId {
626 fn default() -> Self {
627 static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
628 Self {
629 id: NEXT_ID.fetch_add(1, SeqCst),
630 }
631 }
632}