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(not(target_arch = "wasm32"))]
17use std::{io::Write, path::Path};
18#[cfg(not(target_arch = "wasm32"))]
19use wasmer_types::{
20 DeserializeError, FunctionIndex, FunctionType, LocalFunctionIndex, ModuleInfo, SignatureIndex,
21 entity::PrimaryMap,
22};
23
24#[cfg(not(target_arch = "wasm32"))]
25use crate::{
26 Artifact, BaseTunables, CodeMemory, FunctionExtent, GlobalFrameInfoRegistration, Tunables,
27 types::{
28 function::FunctionBodyLike,
29 section::{CustomSectionLike, CustomSectionProtection, SectionIndex},
30 },
31};
32
33#[cfg(not(target_arch = "wasm32"))]
34use wasmer_vm::{
35 FunctionBodyPtr, SectionBodyPtr, SignatureRegistry, VMFunctionBody, VMSharedSignatureIndex,
36 VMTrampoline,
37};
38
39#[derive(Clone)]
41pub struct Engine {
42 inner: Arc<Mutex<EngineInner>>,
43 target: Arc<Target>,
45 engine_id: EngineId,
46 #[cfg(not(target_arch = "wasm32"))]
47 tunables: Arc<dyn Tunables + Send + Sync>,
48 name: String,
49 hash_algorithm: Option<HashAlgorithm>,
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 hash_algorithm: None,
79 }
80 }
81
82 pub fn name(&self) -> &str {
84 self.name.as_str()
85 }
86
87 pub fn set_hash_algorithm(&mut self, hash_algorithm: Option<HashAlgorithm>) {
89 self.hash_algorithm = hash_algorithm;
90 }
91
92 pub fn hash_algorithm(&self) -> Option<HashAlgorithm> {
94 self.hash_algorithm
95 }
96
97 pub fn deterministic_id(&self) -> String {
99 #[cfg(feature = "compiler")]
100 {
101 let i = self.inner();
102 if let Some(ref c) = i.compiler {
103 return c.deterministic_id();
104 } else {
105 return self.name.clone();
106 }
107 }
108
109 #[allow(unreachable_code)]
110 {
111 self.name.to_string()
112 }
113 }
114
115 pub fn headless() -> Self {
129 let target = Target::default();
130 #[cfg(not(target_arch = "wasm32"))]
131 let tunables = BaseTunables::for_target(&target);
132 Self {
133 inner: Arc::new(Mutex::new(EngineInner {
134 #[cfg(feature = "compiler")]
135 compiler: None,
136 #[cfg(feature = "compiler")]
137 features: Features::default(),
138 #[cfg(not(target_arch = "wasm32"))]
139 code_memory: vec![],
140 #[cfg(not(target_arch = "wasm32"))]
141 signatures: SignatureRegistry::new(),
142 })),
143 target: Arc::new(target),
144 engine_id: EngineId::default(),
145 #[cfg(not(target_arch = "wasm32"))]
146 tunables: Arc::new(tunables),
147 name: "engine-headless".to_string(),
148 hash_algorithm: None,
149 }
150 }
151
152 pub fn inner(&self) -> std::sync::MutexGuard<'_, EngineInner> {
154 self.inner.lock().unwrap()
155 }
156
157 pub fn inner_mut(&self) -> std::sync::MutexGuard<'_, EngineInner> {
159 self.inner.lock().unwrap()
160 }
161
162 pub fn target(&self) -> &Target {
164 &self.target
165 }
166
167 #[cfg(not(target_arch = "wasm32"))]
169 pub fn register_signature(&self, func_type: &FunctionType) -> VMSharedSignatureIndex {
170 let compiler = self.inner();
171 compiler.signatures().register(func_type)
172 }
173
174 #[cfg(not(target_arch = "wasm32"))]
176 pub fn lookup_signature(&self, sig: VMSharedSignatureIndex) -> Option<FunctionType> {
177 let compiler = self.inner();
178 compiler.signatures().lookup(sig)
179 }
180
181 #[cfg(feature = "compiler")]
183 pub fn validate(&self, binary: &[u8]) -> Result<(), CompileError> {
184 self.inner().validate(binary)
185 }
186
187 #[cfg(feature = "compiler")]
189 #[cfg(not(target_arch = "wasm32"))]
190 pub fn compile(&self, binary: &[u8]) -> Result<Arc<Artifact>, CompileError> {
191 Ok(Arc::new(Artifact::new(
192 self,
193 binary,
194 self.tunables.as_ref(),
195 self.hash_algorithm,
196 )?))
197 }
198
199 #[cfg(not(feature = "compiler"))]
201 #[cfg(not(target_arch = "wasm32"))]
202 pub fn compile(
203 &self,
204 _binary: &[u8],
205 _tunables: &dyn Tunables,
206 ) -> Result<Arc<Artifact>, CompileError> {
207 Err(CompileError::Codegen(
208 "The Engine is operating in headless mode, so it can not compile Modules.".to_string(),
209 ))
210 }
211
212 #[cfg(not(target_arch = "wasm32"))]
213 pub unsafe fn deserialize_unchecked(
220 &self,
221 bytes: OwnedBuffer,
222 ) -> Result<Arc<Artifact>, DeserializeError> {
223 unsafe { Ok(Arc::new(Artifact::deserialize_unchecked(self, bytes)?)) }
224 }
225
226 #[cfg(not(target_arch = "wasm32"))]
233 pub unsafe fn deserialize(
234 &self,
235 bytes: OwnedBuffer,
236 ) -> Result<Arc<Artifact>, DeserializeError> {
237 unsafe { Ok(Arc::new(Artifact::deserialize(self, bytes)?)) }
238 }
239
240 #[cfg(not(target_arch = "wasm32"))]
245 pub unsafe fn deserialize_from_file(
246 &self,
247 file_ref: &Path,
248 ) -> Result<Arc<Artifact>, DeserializeError> {
249 unsafe {
250 let file = std::fs::File::open(file_ref)?;
251 self.deserialize(
252 OwnedBuffer::from_file(&file)
253 .map_err(|e| DeserializeError::Generic(e.to_string()))?,
254 )
255 }
256 }
257
258 #[cfg(not(target_arch = "wasm32"))]
264 pub unsafe fn deserialize_from_file_unchecked(
265 &self,
266 file_ref: &Path,
267 ) -> Result<Arc<Artifact>, DeserializeError> {
268 unsafe {
269 let file = std::fs::File::open(file_ref)?;
270 self.deserialize_unchecked(
271 OwnedBuffer::from_file(&file)
272 .map_err(|e| DeserializeError::Generic(e.to_string()))?,
273 )
274 }
275 }
276
277 pub fn id(&self) -> &EngineId {
283 &self.engine_id
284 }
285
286 pub fn cloned(&self) -> Self {
288 self.clone()
289 }
290
291 #[cfg(not(target_arch = "wasm32"))]
293 pub fn set_tunables(&mut self, tunables: impl Tunables + Send + Sync + 'static) {
294 self.tunables = Arc::new(tunables);
295 }
296
297 #[cfg(not(target_arch = "wasm32"))]
299 pub fn tunables(&self) -> &dyn Tunables {
300 self.tunables.as_ref()
301 }
302
303 pub fn with_opts(
311 &mut self,
312 _suggested_opts: &wasmer_types::target::UserCompilerOptimizations,
313 ) -> Result<(), CompileError> {
314 #[cfg(feature = "compiler")]
315 {
316 let mut i = self.inner_mut();
317 if let Some(ref mut c) = i.compiler {
318 c.with_opts(_suggested_opts)?;
319 }
320 }
321
322 Ok(())
323 }
324}
325
326impl std::fmt::Debug for Engine {
327 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
328 write!(f, "{}", self.deterministic_id())
329 }
330}
331
332pub struct EngineInner {
334 #[cfg(feature = "compiler")]
335 compiler: Option<Box<dyn Compiler>>,
337 #[cfg(feature = "compiler")]
338 features: Features,
340 #[cfg(not(target_arch = "wasm32"))]
343 code_memory: Vec<CodeMemory>,
344 #[cfg(not(target_arch = "wasm32"))]
347 signatures: SignatureRegistry,
348}
349
350impl std::fmt::Debug for EngineInner {
351 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
352 let mut formatter = f.debug_struct("EngineInner");
353 #[cfg(feature = "compiler")]
354 {
355 formatter.field("compiler", &self.compiler);
356 formatter.field("features", &self.features);
357 }
358
359 #[cfg(not(target_arch = "wasm32"))]
360 {
361 formatter.field("signatures", &self.signatures);
362 }
363
364 formatter.finish()
365 }
366}
367
368impl EngineInner {
369 #[cfg(feature = "compiler")]
371 pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> {
372 match self.compiler.as_ref() {
373 None => Err(CompileError::Codegen(
374 "No compiler compiled into executable".to_string(),
375 )),
376 Some(compiler) => Ok(&**compiler),
377 }
378 }
379
380 #[cfg(feature = "compiler")]
382 pub fn validate(&self, data: &[u8]) -> Result<(), CompileError> {
383 let compiler = self.compiler()?;
384 compiler.validate_module(&self.features, data)
385 }
386
387 #[cfg(feature = "compiler")]
389 pub fn features(&self) -> &Features {
390 &self.features
391 }
392
393 #[cfg(not(target_arch = "wasm32"))]
395 #[allow(clippy::type_complexity)]
396 pub(crate) fn allocate<'a, FunctionBody, CustomSection>(
397 &'a mut self,
398 _module: &wasmer_types::ModuleInfo,
399 functions: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
400 function_call_trampolines: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
401 dynamic_function_trampolines: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
402 custom_sections: impl ExactSizeIterator<Item = &'a CustomSection> + Clone + 'a,
403 ) -> Result<
404 (
405 PrimaryMap<LocalFunctionIndex, FunctionExtent>,
406 PrimaryMap<SignatureIndex, VMTrampoline>,
407 PrimaryMap<FunctionIndex, FunctionBodyPtr>,
408 PrimaryMap<SectionIndex, SectionBodyPtr>,
409 ),
410 CompileError,
411 >
412 where
413 FunctionBody: FunctionBodyLike<'a> + 'a,
414 CustomSection: CustomSectionLike<'a> + 'a,
415 {
416 let functions_len = functions.len();
417 let function_call_trampolines_len = function_call_trampolines.len();
418
419 let function_bodies = functions
420 .chain(function_call_trampolines)
421 .chain(dynamic_function_trampolines)
422 .collect::<Vec<_>>();
423 let (executable_sections, data_sections): (Vec<_>, _) = custom_sections
424 .clone()
425 .partition(|section| section.protection() == CustomSectionProtection::ReadExecute);
426 self.code_memory.push(CodeMemory::new());
427
428 let (mut allocated_functions, allocated_executable_sections, allocated_data_sections) =
429 self.code_memory
430 .last_mut()
431 .unwrap()
432 .allocate(
433 function_bodies.as_slice(),
434 executable_sections.as_slice(),
435 data_sections.as_slice(),
436 )
437 .map_err(|message| {
438 CompileError::Resource(format!(
439 "failed to allocate memory for functions: {message}",
440 ))
441 })?;
442
443 let allocated_functions_result = allocated_functions
444 .drain(0..functions_len)
445 .map(|slice| FunctionExtent {
446 ptr: FunctionBodyPtr(slice.as_ptr()),
447 length: slice.len(),
448 })
449 .collect::<PrimaryMap<LocalFunctionIndex, _>>();
450
451 let mut allocated_function_call_trampolines: PrimaryMap<SignatureIndex, VMTrampoline> =
452 PrimaryMap::new();
453 for ptr in allocated_functions
454 .drain(0..function_call_trampolines_len)
455 .map(|slice| slice.as_ptr())
456 {
457 let trampoline =
458 unsafe { std::mem::transmute::<*const VMFunctionBody, VMTrampoline>(ptr) };
459 allocated_function_call_trampolines.push(trampoline);
460 }
461
462 let allocated_dynamic_function_trampolines = allocated_functions
463 .drain(..)
464 .map(|slice| FunctionBodyPtr(slice.as_ptr()))
465 .collect::<PrimaryMap<FunctionIndex, _>>();
466
467 let mut exec_iter = allocated_executable_sections.iter();
468 let mut data_iter = allocated_data_sections.iter();
469 let allocated_custom_sections = custom_sections
470 .map(|section| {
471 SectionBodyPtr(
472 if section.protection() == CustomSectionProtection::ReadExecute {
473 exec_iter.next()
474 } else {
475 data_iter.next()
476 }
477 .unwrap()
478 .as_ptr(),
479 )
480 })
481 .collect::<PrimaryMap<SectionIndex, _>>();
482 Ok((
483 allocated_functions_result,
484 allocated_function_call_trampolines,
485 allocated_dynamic_function_trampolines,
486 allocated_custom_sections,
487 ))
488 }
489
490 #[cfg(not(target_arch = "wasm32"))]
491 pub(crate) fn publish_compiled_code(&mut self) {
493 self.code_memory.last_mut().unwrap().publish();
494 }
495
496 #[cfg(not(target_arch = "wasm32"))]
497 pub(crate) fn publish_eh_frame(&mut self, eh_frame: Option<&[u8]>) -> Result<(), CompileError> {
499 self.code_memory
500 .last_mut()
501 .unwrap()
502 .unwind_registry_mut()
503 .publish(eh_frame)
504 .map_err(|e| {
505 CompileError::Resource(format!("Error while publishing the unwind code: {e}"))
506 })?;
507 Ok(())
508 }
509
510 #[cfg(not(target_arch = "wasm32"))]
511 pub(crate) fn register_compact_unwind(
513 &mut self,
514 compact_unwind: Option<&[u8]>,
515 eh_personality_addr_in_got: Option<usize>,
516 ) -> Result<(), CompileError> {
517 self.code_memory
518 .last_mut()
519 .unwrap()
520 .unwind_registry_mut()
521 .register_compact_unwind(compact_unwind, eh_personality_addr_in_got)
522 .map_err(|e| {
523 CompileError::Resource(format!("Error while publishing the unwind code: {e}"))
524 })?;
525 Ok(())
526 }
527
528 #[cfg(not(target_arch = "wasm32"))]
530 pub fn signatures(&self) -> &SignatureRegistry {
531 &self.signatures
532 }
533
534 #[cfg(not(target_arch = "wasm32"))]
535 pub(crate) fn register_frame_info(&mut self, frame_info: GlobalFrameInfoRegistration) {
537 self.code_memory
538 .last_mut()
539 .unwrap()
540 .register_frame_info(frame_info);
541 }
542
543 #[cfg(not(target_arch = "wasm32"))]
544 pub(crate) fn register_perfmap(
545 &self,
546 finished_functions: &PrimaryMap<LocalFunctionIndex, FunctionExtent>,
547 module_info: &ModuleInfo,
548 ) -> Result<(), CompileError> {
549 if self
550 .compiler
551 .as_ref()
552 .is_some_and(|v| v.get_perfmap_enabled())
553 {
554 let filename = format!("/tmp/perf-{}.map", std::process::id());
555 let mut file = std::io::BufWriter::new(std::fs::File::create(filename).unwrap());
556
557 for (func_index, code) in finished_functions.iter() {
558 let func_index = module_info.func_index(func_index);
559 let name = if let Some(func_name) = module_info.function_names.get(&func_index) {
560 func_name.clone()
561 } else {
562 format!("{:p}", code.ptr.0)
563 };
564
565 let sanitized_name = name.replace(['\n', '\r'], "_");
566 let line = format!(
567 "{:p} {:x} {}\n",
568 code.ptr.0 as *const _, code.length, sanitized_name
569 );
570 write!(file, "{line}").map_err(|e| CompileError::Codegen(e.to_string()))?;
571 file.flush()
572 .map_err(|e| CompileError::Codegen(e.to_string()))?;
573 }
574 }
575
576 Ok(())
577 }
578}
579
580#[cfg(feature = "compiler")]
581impl From<Box<dyn CompilerConfig>> for Engine {
582 fn from(config: Box<dyn CompilerConfig>) -> Self {
583 EngineBuilder::new(config).engine()
584 }
585}
586
587impl From<EngineBuilder> for Engine {
588 fn from(engine_builder: EngineBuilder) -> Self {
589 engine_builder.engine()
590 }
591}
592
593impl From<&Self> for Engine {
594 fn from(engine_ref: &Self) -> Self {
595 engine_ref.cloned()
596 }
597}
598
599#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
600#[repr(transparent)]
601pub struct EngineId {
603 id: usize,
604}
605
606impl EngineId {
607 pub fn id(&self) -> String {
609 format!("{}", &self.id)
610 }
611}
612
613impl Clone for EngineId {
614 fn clone(&self) -> Self {
615 Self::default()
616 }
617}
618
619impl Default for EngineId {
620 fn default() -> Self {
621 static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
622 Self {
623 id: NEXT_ID.fetch_add(1, SeqCst),
624 }
625 }
626}