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::{CompileError, HashAlgorithm, 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 hash_algorithm: Option<HashAlgorithm>,
54}
55
56impl Engine {
57 #[cfg(feature = "compiler")]
59 pub fn new(
60 compiler_config: Box<dyn CompilerConfig>,
61 target: Target,
62 features: Features,
63 ) -> Self {
64 #[cfg(not(target_arch = "wasm32"))]
65 let tunables = BaseTunables::for_target(&target);
66 let compiler = compiler_config.compiler();
67 let name = format!("engine-{}", compiler.name());
68 Self {
69 inner: Arc::new(Mutex::new(EngineInner {
70 compiler: Some(compiler),
71 features,
72 #[cfg(not(target_arch = "wasm32"))]
73 code_memory: vec![],
74 #[cfg(not(target_arch = "wasm32"))]
75 signatures: SignatureRegistry::new(),
76 })),
77 target: Arc::new(target),
78 engine_id: EngineId::default(),
79 #[cfg(not(target_arch = "wasm32"))]
80 tunables: Arc::new(tunables),
81 name,
82 hash_algorithm: None,
83 }
84 }
85
86 pub fn name(&self) -> &str {
88 self.name.as_str()
89 }
90
91 pub fn set_hash_algorithm(&mut self, hash_algorithm: Option<HashAlgorithm>) {
93 self.hash_algorithm = hash_algorithm;
94 }
95
96 pub fn hash_algorithm(&self) -> Option<HashAlgorithm> {
98 self.hash_algorithm
99 }
100
101 pub fn deterministic_id(&self) -> String {
103 #[cfg(feature = "compiler")]
104 {
105 let i = self.inner();
106 if let Some(ref c) = i.compiler {
107 return c.deterministic_id();
108 } else {
109 return self.name.clone();
110 }
111 }
112
113 #[allow(unreachable_code)]
114 {
115 self.name.to_string()
116 }
117 }
118
119 pub fn headless() -> Self {
133 let target = Target::default();
134 #[cfg(not(target_arch = "wasm32"))]
135 let tunables = BaseTunables::for_target(&target);
136 Self {
137 inner: Arc::new(Mutex::new(EngineInner {
138 #[cfg(feature = "compiler")]
139 compiler: None,
140 #[cfg(feature = "compiler")]
141 features: Features::default(),
142 #[cfg(not(target_arch = "wasm32"))]
143 code_memory: vec![],
144 #[cfg(not(target_arch = "wasm32"))]
145 signatures: SignatureRegistry::new(),
146 })),
147 target: Arc::new(target),
148 engine_id: EngineId::default(),
149 #[cfg(not(target_arch = "wasm32"))]
150 tunables: Arc::new(tunables),
151 name: "engine-headless".to_string(),
152 hash_algorithm: None,
153 }
154 }
155
156 pub fn inner(&self) -> std::sync::MutexGuard<'_, EngineInner> {
158 self.inner.lock().unwrap()
159 }
160
161 pub fn inner_mut(&self) -> std::sync::MutexGuard<'_, EngineInner> {
163 self.inner.lock().unwrap()
164 }
165
166 pub fn target(&self) -> &Target {
168 &self.target
169 }
170
171 #[cfg(not(target_arch = "wasm32"))]
173 pub fn register_signature(&self, func_type: &FunctionType) -> VMSharedSignatureIndex {
174 let compiler = self.inner();
175 compiler.signatures().register(func_type)
176 }
177
178 #[cfg(not(target_arch = "wasm32"))]
180 pub fn lookup_signature(&self, sig: VMSharedSignatureIndex) -> Option<FunctionType> {
181 let compiler = self.inner();
182 compiler.signatures().lookup(sig)
183 }
184
185 #[cfg(feature = "compiler")]
187 pub fn validate(&self, binary: &[u8]) -> Result<(), CompileError> {
188 self.inner().validate(binary)
189 }
190
191 #[cfg(feature = "compiler")]
193 #[cfg(not(target_arch = "wasm32"))]
194 pub fn compile(&self, binary: &[u8]) -> Result<Arc<Artifact>, CompileError> {
195 Ok(Arc::new(Artifact::new(
196 self,
197 binary,
198 self.tunables.as_ref(),
199 self.hash_algorithm,
200 )?))
201 }
202
203 #[cfg(not(feature = "compiler"))]
205 #[cfg(not(target_arch = "wasm32"))]
206 pub fn compile(
207 &self,
208 _binary: &[u8],
209 _tunables: &dyn Tunables,
210 ) -> Result<Arc<Artifact>, CompileError> {
211 Err(CompileError::Codegen(
212 "The Engine is operating in headless mode, so it can not compile Modules.".to_string(),
213 ))
214 }
215
216 #[cfg(not(target_arch = "wasm32"))]
217 pub unsafe fn deserialize_unchecked(
224 &self,
225 bytes: OwnedBuffer,
226 ) -> Result<Arc<Artifact>, DeserializeError> {
227 unsafe { Ok(Arc::new(Artifact::deserialize_unchecked(self, bytes)?)) }
228 }
229
230 #[cfg(not(target_arch = "wasm32"))]
237 pub unsafe fn deserialize(
238 &self,
239 bytes: OwnedBuffer,
240 ) -> Result<Arc<Artifact>, DeserializeError> {
241 unsafe { Ok(Arc::new(Artifact::deserialize(self, bytes)?)) }
242 }
243
244 #[cfg(not(target_arch = "wasm32"))]
249 pub unsafe fn deserialize_from_file(
250 &self,
251 file_ref: &Path,
252 ) -> Result<Arc<Artifact>, DeserializeError> {
253 unsafe {
254 let file = std::fs::File::open(file_ref)?;
255 self.deserialize(
256 OwnedBuffer::from_file(&file)
257 .map_err(|e| DeserializeError::Generic(e.to_string()))?,
258 )
259 }
260 }
261
262 #[cfg(not(target_arch = "wasm32"))]
268 pub unsafe fn deserialize_from_file_unchecked(
269 &self,
270 file_ref: &Path,
271 ) -> Result<Arc<Artifact>, DeserializeError> {
272 unsafe {
273 let file = std::fs::File::open(file_ref)?;
274 self.deserialize_unchecked(
275 OwnedBuffer::from_file(&file)
276 .map_err(|e| DeserializeError::Generic(e.to_string()))?,
277 )
278 }
279 }
280
281 pub fn id(&self) -> &EngineId {
287 &self.engine_id
288 }
289
290 pub fn cloned(&self) -> Self {
292 self.clone()
293 }
294
295 #[cfg(not(target_arch = "wasm32"))]
297 pub fn set_tunables(&mut self, tunables: impl Tunables + Send + Sync + 'static) {
298 self.tunables = Arc::new(tunables);
299 }
300
301 #[cfg(not(target_arch = "wasm32"))]
303 pub fn tunables(&self) -> &dyn Tunables {
304 self.tunables.as_ref()
305 }
306
307 pub fn with_opts(
315 &mut self,
316 _suggested_opts: &wasmer_types::target::UserCompilerOptimizations,
317 ) -> Result<(), CompileError> {
318 #[cfg(feature = "compiler")]
319 {
320 let mut i = self.inner_mut();
321 if let Some(ref mut c) = i.compiler {
322 c.with_opts(_suggested_opts)?;
323 }
324 }
325
326 Ok(())
327 }
328}
329
330impl std::fmt::Debug for Engine {
331 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
332 write!(f, "{}", self.deterministic_id())
333 }
334}
335
336pub struct EngineInner {
338 #[cfg(feature = "compiler")]
339 compiler: Option<Box<dyn Compiler>>,
341 #[cfg(feature = "compiler")]
342 features: Features,
344 #[cfg(not(target_arch = "wasm32"))]
347 code_memory: Vec<CodeMemory>,
348 #[cfg(not(target_arch = "wasm32"))]
351 signatures: SignatureRegistry,
352}
353
354impl std::fmt::Debug for EngineInner {
355 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
356 let mut formatter = f.debug_struct("EngineInner");
357 #[cfg(feature = "compiler")]
358 {
359 formatter.field("compiler", &self.compiler);
360 formatter.field("features", &self.features);
361 }
362
363 #[cfg(not(target_arch = "wasm32"))]
364 {
365 formatter.field("signatures", &self.signatures);
366 }
367
368 formatter.finish()
369 }
370}
371
372impl EngineInner {
373 #[cfg(feature = "compiler")]
375 pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> {
376 match self.compiler.as_ref() {
377 None => Err(CompileError::Codegen(
378 "No compiler compiled into executable".to_string(),
379 )),
380 Some(compiler) => Ok(&**compiler),
381 }
382 }
383
384 #[cfg(feature = "compiler")]
386 pub fn validate(&self, data: &[u8]) -> Result<(), CompileError> {
387 let compiler = self.compiler()?;
388 compiler.validate_module(&self.features, data)
389 }
390
391 #[cfg(feature = "compiler")]
393 pub fn features(&self) -> &Features {
394 &self.features
395 }
396
397 #[cfg(not(target_arch = "wasm32"))]
399 #[allow(clippy::type_complexity)]
400 pub(crate) fn allocate<'a, FunctionBody, CustomSection>(
401 &'a mut self,
402 _module: &wasmer_types::ModuleInfo,
403 functions: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
404 function_call_trampolines: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
405 dynamic_function_trampolines: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
406 custom_sections: impl ExactSizeIterator<Item = &'a CustomSection> + Clone + 'a,
407 ) -> Result<
408 (
409 PrimaryMap<LocalFunctionIndex, FunctionExtent>,
410 PrimaryMap<SignatureIndex, VMTrampoline>,
411 PrimaryMap<FunctionIndex, FunctionBodyPtr>,
412 PrimaryMap<SectionIndex, SectionBodyPtr>,
413 ),
414 CompileError,
415 >
416 where
417 FunctionBody: FunctionBodyLike<'a> + 'a,
418 CustomSection: CustomSectionLike<'a> + 'a,
419 {
420 let functions_len = functions.len();
421 let function_call_trampolines_len = function_call_trampolines.len();
422
423 let function_bodies = functions
424 .chain(function_call_trampolines)
425 .chain(dynamic_function_trampolines)
426 .collect::<Vec<_>>();
427 let (executable_sections, data_sections): (Vec<_>, _) = custom_sections
428 .clone()
429 .partition(|section| section.protection() == CustomSectionProtection::ReadExecute);
430 self.code_memory.push(CodeMemory::new());
431
432 let (mut allocated_functions, allocated_executable_sections, allocated_data_sections) =
433 self.code_memory
434 .last_mut()
435 .unwrap()
436 .allocate(
437 function_bodies.as_slice(),
438 executable_sections.as_slice(),
439 data_sections.as_slice(),
440 )
441 .map_err(|message| {
442 CompileError::Resource(format!(
443 "failed to allocate memory for functions: {message}",
444 ))
445 })?;
446
447 let allocated_functions_result = allocated_functions
448 .drain(0..functions_len)
449 .map(|slice| FunctionExtent {
450 ptr: FunctionBodyPtr(slice.as_ptr()),
451 length: slice.len(),
452 })
453 .collect::<PrimaryMap<LocalFunctionIndex, _>>();
454
455 let mut allocated_function_call_trampolines: PrimaryMap<SignatureIndex, VMTrampoline> =
456 PrimaryMap::new();
457 for ptr in allocated_functions
458 .drain(0..function_call_trampolines_len)
459 .map(|slice| slice.as_ptr())
460 {
461 let trampoline =
462 unsafe { std::mem::transmute::<*const VMFunctionBody, VMTrampoline>(ptr) };
463 allocated_function_call_trampolines.push(trampoline);
464 }
465
466 let allocated_dynamic_function_trampolines = allocated_functions
467 .drain(..)
468 .map(|slice| FunctionBodyPtr(slice.as_ptr()))
469 .collect::<PrimaryMap<FunctionIndex, _>>();
470
471 let mut exec_iter = allocated_executable_sections.iter();
472 let mut data_iter = allocated_data_sections.iter();
473 let allocated_custom_sections = custom_sections
474 .map(|section| {
475 SectionBodyPtr(
476 if section.protection() == CustomSectionProtection::ReadExecute {
477 exec_iter.next()
478 } else {
479 data_iter.next()
480 }
481 .unwrap()
482 .as_ptr(),
483 )
484 })
485 .collect::<PrimaryMap<SectionIndex, _>>();
486 Ok((
487 allocated_functions_result,
488 allocated_function_call_trampolines,
489 allocated_dynamic_function_trampolines,
490 allocated_custom_sections,
491 ))
492 }
493
494 #[cfg(not(target_arch = "wasm32"))]
495 pub(crate) fn publish_compiled_code(&mut self) {
497 self.code_memory.last_mut().unwrap().publish();
498 }
499
500 #[cfg(not(target_arch = "wasm32"))]
501 pub(crate) fn publish_eh_frame(&mut self, eh_frame: Option<&[u8]>) -> Result<(), CompileError> {
503 self.code_memory
504 .last_mut()
505 .unwrap()
506 .unwind_registry_mut()
507 .publish(eh_frame)
508 .map_err(|e| {
509 CompileError::Resource(format!("Error while publishing the unwind code: {e}"))
510 })?;
511 Ok(())
512 }
513
514 #[cfg(not(target_arch = "wasm32"))]
515 pub(crate) fn register_compact_unwind(
517 &mut self,
518 compact_unwind: Option<&[u8]>,
519 eh_personality_addr_in_got: Option<usize>,
520 ) -> Result<(), CompileError> {
521 self.code_memory
522 .last_mut()
523 .unwrap()
524 .unwind_registry_mut()
525 .register_compact_unwind(compact_unwind, eh_personality_addr_in_got)
526 .map_err(|e| {
527 CompileError::Resource(format!("Error while publishing the unwind code: {e}"))
528 })?;
529 Ok(())
530 }
531
532 #[cfg(not(target_arch = "wasm32"))]
534 pub fn signatures(&self) -> &SignatureRegistry {
535 &self.signatures
536 }
537
538 #[cfg(not(target_arch = "wasm32"))]
539 pub(crate) fn register_frame_info(&mut self, frame_info: GlobalFrameInfoRegistration) {
541 self.code_memory
542 .last_mut()
543 .unwrap()
544 .register_frame_info(frame_info);
545 }
546
547 #[cfg(all(not(target_arch = "wasm32"), feature = "compiler"))]
548 pub(crate) fn register_perfmap(
549 &self,
550 finished_functions: &PrimaryMap<LocalFunctionIndex, FunctionExtent>,
551 module_info: &ModuleInfo,
552 ) -> Result<(), CompileError> {
553 if self
554 .compiler
555 .as_ref()
556 .is_some_and(|v| v.get_perfmap_enabled())
557 {
558 let filename = format!("/tmp/perf-{}.map", std::process::id());
559 let mut file = std::io::BufWriter::new(std::fs::File::create(filename).unwrap());
560
561 for (func_index, code) in finished_functions.iter() {
562 let func_index = module_info.func_index(func_index);
563 let name = if let Some(func_name) = module_info.function_names.get(&func_index) {
564 func_name.clone()
565 } else {
566 format!("{:p}", code.ptr.0)
567 };
568
569 let sanitized_name = name.replace(['\n', '\r'], "_");
570 let line = format!(
571 "{:p} {:x} {}\n",
572 code.ptr.0 as *const _, code.length, sanitized_name
573 );
574 write!(file, "{line}").map_err(|e| CompileError::Codegen(e.to_string()))?;
575 file.flush()
576 .map_err(|e| CompileError::Codegen(e.to_string()))?;
577 }
578 }
579
580 Ok(())
581 }
582}
583
584#[cfg(feature = "compiler")]
585impl From<Box<dyn CompilerConfig>> for Engine {
586 fn from(config: Box<dyn CompilerConfig>) -> Self {
587 EngineBuilder::new(config).engine()
588 }
589}
590
591impl From<EngineBuilder> for Engine {
592 fn from(engine_builder: EngineBuilder) -> Self {
593 engine_builder.engine()
594 }
595}
596
597impl From<&Self> for Engine {
598 fn from(engine_ref: &Self) -> Self {
599 engine_ref.cloned()
600 }
601}
602
603#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
604#[repr(transparent)]
605pub struct EngineId {
607 id: usize,
608}
609
610impl EngineId {
611 pub fn id(&self) -> String {
613 format!("{}", &self.id)
614 }
615}
616
617impl Clone for EngineId {
618 fn clone(&self) -> Self {
619 Self::default()
620 }
621}
622
623impl Default for EngineId {
624 fn default() -> Self {
625 static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
626 Self {
627 id: NEXT_ID.fetch_add(1, SeqCst),
628 }
629 }
630}