1 use crate::prelude::*;
2 #[cfg(feature = "runtime")]
3 use crate::runtime::type_registry::TypeRegistry;
4 #[cfg(feature = "runtime")]
5 use crate::runtime::vm::GcRuntime;
6 use crate::sync::OnceLock;
7 use crate::Config;
8 use alloc::sync::Arc;
9 use core::sync::atomic::{AtomicU64, Ordering};
10 #[cfg(any(feature = "cranelift", feature = "winch"))]
11 use object::write::{Object, StandardSegment};
12 use object::SectionKind;
13 #[cfg(feature = "std")]
14 use std::path::Path;
15 use wasmparser::WasmFeatures;
16 use wasmtime_environ::obj;
17 use wasmtime_environ::{FlagValue, ObjectKind, Tunables};
18 
19 mod serialization;
20 
21 /// An `Engine` which is a global context for compilation and management of wasm
22 /// modules.
23 ///
24 /// An engine can be safely shared across threads and is a cheap cloneable
25 /// handle to the actual engine. The engine itself will be deallocated once all
26 /// references to it have gone away.
27 ///
28 /// Engines store global configuration preferences such as compilation settings,
29 /// enabled features, etc. You'll likely only need at most one of these for a
30 /// program.
31 ///
32 /// ## Engines and `Clone`
33 ///
34 /// Using `clone` on an `Engine` is a cheap operation. It will not create an
35 /// entirely new engine, but rather just a new reference to the existing engine.
36 /// In other words it's a shallow copy, not a deep copy.
37 ///
38 /// ## Engines and `Default`
39 ///
40 /// You can create an engine with default configuration settings using
41 /// `Engine::default()`. Be sure to consult the documentation of [`Config`] for
42 /// default settings.
43 #[derive(Clone)]
44 pub struct Engine {
45     inner: Arc<EngineInner>,
46 }
47 
48 struct EngineInner {
49     config: Config,
50     tunables: Tunables,
51     #[cfg(any(feature = "cranelift", feature = "winch"))]
52     compiler: Box<dyn wasmtime_environ::Compiler>,
53     #[cfg(feature = "runtime")]
54     allocator: Box<dyn crate::runtime::vm::InstanceAllocator + Send + Sync>,
55     #[cfg(feature = "runtime")]
56     gc_runtime: Arc<dyn GcRuntime>,
57     #[cfg(feature = "runtime")]
58     profiler: Box<dyn crate::profiling_agent::ProfilingAgent>,
59     #[cfg(feature = "runtime")]
60     signatures: TypeRegistry,
61     #[cfg(feature = "runtime")]
62     epoch: AtomicU64,
63 
64     /// One-time check of whether the compiler's settings, if present, are
65     /// compatible with the native host.
66     #[cfg(any(feature = "cranelift", feature = "winch"))]
67     compatible_with_native_host: OnceLock<Result<(), String>>,
68 }
69 
70 impl Default for Engine {
71     fn default() -> Engine {
72         Engine::new(&Config::default()).unwrap()
73     }
74 }
75 
76 impl Engine {
77     /// Creates a new [`Engine`] with the specified compilation and
78     /// configuration settings.
79     ///
80     /// # Errors
81     ///
82     /// This method can fail if the `config` is invalid or some
83     /// configurations are incompatible.
84     ///
85     /// For example, feature `reference_types` will need to set
86     /// the compiler setting `enable_safepoints` and `unwind_info`
87     /// to `true`, but explicitly disable these two compiler settings
88     /// will cause errors.
89     pub fn new(config: &Config) -> Result<Engine> {
90         #[cfg(feature = "runtime")]
91         {
92             // Ensure that crate::runtime::vm's signal handlers are
93             // configured. This is the per-program initialization required for
94             // handling traps, such as configuring signals, vectored exception
95             // handlers, etc.
96             crate::runtime::vm::init_traps(
97                 crate::module::get_wasm_trap,
98                 config.macos_use_mach_ports,
99             );
100             #[cfg(feature = "debug-builtins")]
101             crate::runtime::vm::debug_builtins::ensure_exported();
102         }
103 
104         let config = {
105             let mut config = config.clone();
106             config.conditionally_enable_defaults();
107             config
108         };
109 
110         let tunables = config.validate()?;
111 
112         #[cfg(any(feature = "cranelift", feature = "winch"))]
113         let (config, compiler) = config.build_compiler(&tunables)?;
114 
115         Ok(Engine {
116             inner: Arc::new(EngineInner {
117                 #[cfg(any(feature = "cranelift", feature = "winch"))]
118                 compiler,
119                 #[cfg(feature = "runtime")]
120                 allocator: config.build_allocator(&tunables)?,
121                 #[cfg(feature = "runtime")]
122                 gc_runtime: config.build_gc_runtime()?,
123                 #[cfg(feature = "runtime")]
124                 profiler: config.build_profiler()?,
125                 #[cfg(feature = "runtime")]
126                 signatures: TypeRegistry::new(),
127                 #[cfg(feature = "runtime")]
128                 epoch: AtomicU64::new(0),
129                 #[cfg(any(feature = "cranelift", feature = "winch"))]
130                 compatible_with_native_host: OnceLock::new(),
131                 config,
132                 tunables,
133             }),
134         })
135     }
136 
137     /// Returns the configuration settings that this engine is using.
138     #[inline]
139     pub fn config(&self) -> &Config {
140         &self.inner.config
141     }
142 
143     pub(crate) fn run_maybe_parallel<
144         A: Send,
145         B: Send,
146         E: Send,
147         F: Fn(A) -> Result<B, E> + Send + Sync,
148     >(
149         &self,
150         input: Vec<A>,
151         f: F,
152     ) -> Result<Vec<B>, E> {
153         if self.config().parallel_compilation {
154             #[cfg(feature = "parallel-compilation")]
155             {
156                 use rayon::prelude::*;
157                 return input
158                     .into_par_iter()
159                     .map(|a| f(a))
160                     .collect::<Result<Vec<B>, E>>();
161             }
162         }
163 
164         // In case the parallel-compilation feature is disabled or the parallel_compilation config
165         // was turned off dynamically fallback to the non-parallel version.
166         input
167             .into_iter()
168             .map(|a| f(a))
169             .collect::<Result<Vec<B>, E>>()
170     }
171 
172     /// Take a weak reference to this engine.
173     pub fn weak(&self) -> EngineWeak {
174         EngineWeak {
175             inner: Arc::downgrade(&self.inner),
176         }
177     }
178 
179     pub(crate) fn tunables(&self) -> &Tunables {
180         &self.inner.tunables
181     }
182 
183     /// Returns whether the engine `a` and `b` refer to the same configuration.
184     #[inline]
185     pub fn same(a: &Engine, b: &Engine) -> bool {
186         Arc::ptr_eq(&a.inner, &b.inner)
187     }
188 
189     /// Detects whether the bytes provided are a precompiled object produced by
190     /// Wasmtime.
191     ///
192     /// This function will inspect the header of `bytes` to determine if it
193     /// looks like a precompiled core wasm module or a precompiled component.
194     /// This does not validate the full structure or guarantee that
195     /// deserialization will succeed, instead it helps higher-levels of the
196     /// stack make a decision about what to do next when presented with the
197     /// `bytes` as an input module.
198     ///
199     /// If the `bytes` looks like a precompiled object previously produced by
200     /// [`Module::serialize`](crate::Module::serialize),
201     /// [`Component::serialize`](crate::component::Component::serialize),
202     /// [`Engine::precompile_module`], or [`Engine::precompile_component`], then
203     /// this will return `Some(...)` indicating so. Otherwise `None` is
204     /// returned.
205     pub fn detect_precompiled(&self, bytes: &[u8]) -> Option<Precompiled> {
206         serialization::detect_precompiled_bytes(bytes)
207     }
208 
209     /// Like [`Engine::detect_precompiled`], but performs the detection on a file.
210     #[cfg(feature = "std")]
211     pub fn detect_precompiled_file(&self, path: impl AsRef<Path>) -> Result<Option<Precompiled>> {
212         serialization::detect_precompiled_file(path)
213     }
214 
215     /// Returns the target triple which this engine is compiling code for
216     /// and/or running code for.
217     pub(crate) fn target(&self) -> target_lexicon::Triple {
218         // If a compiler is configured, use that target.
219         #[cfg(any(feature = "cranelift", feature = "winch"))]
220         return self.compiler().triple().clone();
221 
222         // ... otherwise it's the native target
223         #[cfg(not(any(feature = "cranelift", feature = "winch")))]
224         return target_lexicon::Triple::host();
225     }
226 
227     /// Verify that this engine's configuration is compatible with loading
228     /// modules onto the native host platform.
229     ///
230     /// This method is used as part of `Module::new` to ensure that this
231     /// engine can indeed load modules for the configured compiler (if any).
232     /// Note that if cranelift is disabled this trivially returns `Ok` because
233     /// loaded serialized modules are checked separately.
234     pub(crate) fn check_compatible_with_native_host(&self) -> Result<()> {
235         #[cfg(any(feature = "cranelift", feature = "winch"))]
236         {
237             self.inner
238                 .compatible_with_native_host
239                 .get_or_init(|| self._check_compatible_with_native_host())
240                 .clone()
241                 .map_err(anyhow::Error::msg)
242         }
243         #[cfg(not(any(feature = "cranelift", feature = "winch")))]
244         {
245             Ok(())
246         }
247     }
248 
249     fn _check_compatible_with_native_host(&self) -> Result<(), String> {
250         #[cfg(any(feature = "cranelift", feature = "winch"))]
251         {
252             let compiler = self.compiler();
253 
254             // Check to see that the config's target matches the host
255             let target = compiler.triple();
256             if *target != target_lexicon::Triple::host() {
257                 return Err(format!(
258                     "target '{}' specified in the configuration does not match the host",
259                     target
260                 ));
261             }
262 
263             // Also double-check all compiler settings
264             for (key, value) in compiler.flags().iter() {
265                 self.check_compatible_with_shared_flag(key, value)?;
266             }
267             for (key, value) in compiler.isa_flags().iter() {
268                 self.check_compatible_with_isa_flag(key, value)?;
269             }
270         }
271         Ok(())
272     }
273 
274     /// Checks to see whether the "shared flag", something enabled for
275     /// individual compilers, is compatible with the native host platform.
276     ///
277     /// This is used both when validating an engine's compilation settings are
278     /// compatible with the host as well as when deserializing modules from
279     /// disk to ensure they're compatible with the current host.
280     ///
281     /// Note that most of the settings here are not configured by users that
282     /// often. While theoretically possible via `Config` methods the more
283     /// interesting flags are the ISA ones below. Typically the values here
284     /// represent global configuration for wasm features. Settings here
285     /// currently rely on the compiler informing us of all settings, including
286     /// those disabled. Settings then fall in a few buckets:
287     ///
288     /// * Some settings must be enabled, such as `preserve_frame_pointers`.
289     /// * Some settings must have a particular value, such as
290     ///   `libcall_call_conv`.
291     /// * Some settings do not matter as to their value, such as `opt_level`.
292     pub(crate) fn check_compatible_with_shared_flag(
293         &self,
294         flag: &str,
295         value: &FlagValue,
296     ) -> Result<(), String> {
297         let target = self.target();
298         let ok = match flag {
299             // These settings must all have be enabled, since their value
300             // can affect the way the generated code performs or behaves at
301             // runtime.
302             "libcall_call_conv" => *value == FlagValue::Enum("isa_default".into()),
303             "preserve_frame_pointers" => *value == FlagValue::Bool(true),
304             "enable_probestack" => *value == FlagValue::Bool(crate::config::probestack_supported(target.architecture)),
305             "probestack_strategy" => *value == FlagValue::Enum("inline".into()),
306 
307             // Features wasmtime doesn't use should all be disabled, since
308             // otherwise if they are enabled it could change the behavior of
309             // generated code.
310             "enable_llvm_abi_extensions" => *value == FlagValue::Bool(false),
311             "enable_pinned_reg" => *value == FlagValue::Bool(false),
312             "use_colocated_libcalls" => *value == FlagValue::Bool(false),
313             "use_pinned_reg_as_heap_base" => *value == FlagValue::Bool(false),
314 
315             // If reference types (or anything that depends on reference types,
316             // like typed function references and GC) are enabled this must be
317             // enabled, otherwise this setting can have any value.
318             "enable_safepoints" => {
319                 if self.config().features.contains(WasmFeatures::REFERENCE_TYPES) {
320                     *value == FlagValue::Bool(true)
321                 } else {
322                     return Ok(())
323                 }
324             }
325 
326             // Windows requires unwind info as part of its ABI.
327             "unwind_info" => {
328                 if target.operating_system == target_lexicon::OperatingSystem::Windows {
329                     *value == FlagValue::Bool(true)
330                 } else {
331                     return Ok(())
332                 }
333             }
334 
335             // These settings don't affect the interface or functionality of
336             // the module itself, so their configuration values shouldn't
337             // matter.
338             "enable_heap_access_spectre_mitigation"
339             | "enable_table_access_spectre_mitigation"
340             | "enable_nan_canonicalization"
341             | "enable_jump_tables"
342             | "enable_float"
343             | "enable_verifier"
344             | "enable_pcc"
345             | "regalloc_checker"
346             | "regalloc_verbose_logs"
347             | "is_pic"
348             | "bb_padding_log2_minus_one"
349             | "machine_code_cfg_info"
350             | "tls_model" // wasmtime doesn't use tls right now
351             | "opt_level" // opt level doesn't change semantics
352             | "enable_alias_analysis" // alias analysis-based opts don't change semantics
353             | "probestack_size_log2" // probestack above asserted disabled
354             | "regalloc" // shouldn't change semantics
355             | "enable_incremental_compilation_cache_checks" // shouldn't change semantics
356             | "enable_atomics" => return Ok(()),
357 
358             // Everything else is unknown and needs to be added somewhere to
359             // this list if encountered.
360             _ => {
361                 return Err(format!("unknown shared setting {:?} configured to {:?}", flag, value))
362             }
363         };
364 
365         if !ok {
366             return Err(format!(
367                 "setting {:?} is configured to {:?} which is not supported",
368                 flag, value,
369             ));
370         }
371         Ok(())
372     }
373 
374     /// Same as `check_compatible_with_native_host` except used for ISA-specific
375     /// flags. This is used to test whether a configured ISA flag is indeed
376     /// available on the host platform itself.
377     pub(crate) fn check_compatible_with_isa_flag(
378         &self,
379         flag: &str,
380         value: &FlagValue,
381     ) -> Result<(), String> {
382         match value {
383             // ISA flags are used for things like CPU features, so if they're
384             // disabled then it's compatible with the native host.
385             FlagValue::Bool(false) => return Ok(()),
386 
387             // Fall through below where we test at runtime that features are
388             // available.
389             FlagValue::Bool(true) => {}
390 
391             // Only `bool` values are supported right now, other settings would
392             // need more support here.
393             _ => {
394                 return Err(format!(
395                     "isa-specific feature {:?} configured to unknown value {:?}",
396                     flag, value
397                 ))
398             }
399         }
400 
401         let host_feature = match flag {
402             // aarch64 features to detect
403             "has_lse" => "lse",
404             "has_pauth" => "paca",
405 
406             // aarch64 features which don't need detection
407             // No effect on its own.
408             "sign_return_address_all" => return Ok(()),
409             // The pointer authentication instructions act as a `NOP` when
410             // unsupported, so it is safe to enable them.
411             "sign_return_address" => return Ok(()),
412             // No effect on its own.
413             "sign_return_address_with_bkey" => return Ok(()),
414             // The `BTI` instruction acts as a `NOP` when unsupported, so it
415             // is safe to enable it regardless of whether the host supports it
416             // or not.
417             "use_bti" => return Ok(()),
418 
419             // s390x features to detect
420             "has_vxrs_ext2" => "vxrs_ext2",
421             "has_mie2" => "mie2",
422 
423             // x64 features to detect
424             "has_sse3" => "sse3",
425             "has_ssse3" => "ssse3",
426             "has_sse41" => "sse4.1",
427             "has_sse42" => "sse4.2",
428             "has_popcnt" => "popcnt",
429             "has_avx" => "avx",
430             "has_avx2" => "avx2",
431             "has_fma" => "fma",
432             "has_bmi1" => "bmi1",
433             "has_bmi2" => "bmi2",
434             "has_avx512bitalg" => "avx512bitalg",
435             "has_avx512dq" => "avx512dq",
436             "has_avx512f" => "avx512f",
437             "has_avx512vl" => "avx512vl",
438             "has_avx512vbmi" => "avx512vbmi",
439             "has_lzcnt" => "lzcnt",
440 
441             _ => {
442                 // FIXME: should enumerate risc-v features and plumb them
443                 // through to the `detect_host_feature` function.
444                 if cfg!(target_arch = "riscv64") && flag != "not_a_flag" {
445                     return Ok(());
446                 }
447                 return Err(format!(
448                     "don't know how to test for target-specific flag {flag:?} at runtime"
449                 ));
450             }
451         };
452 
453         let detect = match self.config().detect_host_feature {
454             Some(detect) => detect,
455             None => {
456                 return Err(format!(
457                     "cannot determine if host feature {host_feature:?} is \
458                      available at runtime, configure a probing function with \
459                      `Config::detect_host_feature`"
460                 ))
461             }
462         };
463 
464         match detect(host_feature) {
465             Some(true) => Ok(()),
466             Some(false) => Err(format!(
467                 "compilation setting {flag:?} is enabled, but not \
468                  available on the host",
469             )),
470             None => Err(format!(
471                 "failed to detect if target-specific flag {flag:?} is \
472                  available at runtime"
473             )),
474         }
475     }
476 }
477 
478 #[cfg(any(feature = "cranelift", feature = "winch"))]
479 impl Engine {
480     pub(crate) fn compiler(&self) -> &dyn wasmtime_environ::Compiler {
481         &*self.inner.compiler
482     }
483 
484     /// Ahead-of-time (AOT) compiles a WebAssembly module.
485     ///
486     /// The `bytes` provided must be in one of two formats:
487     ///
488     /// * A [binary-encoded][binary] WebAssembly module. This is always supported.
489     /// * A [text-encoded][text] instance of the WebAssembly text format.
490     ///   This is only supported when the `wat` feature of this crate is enabled.
491     ///   If this is supplied then the text format will be parsed before validation.
492     ///   Note that the `wat` feature is enabled by default.
493     ///
494     /// This method may be used to compile a module for use with a different target
495     /// host. The output of this method may be used with
496     /// [`Module::deserialize`](crate::Module::deserialize) on hosts compatible
497     /// with the [`Config`](crate::Config) associated with this [`Engine`].
498     ///
499     /// The output of this method is safe to send to another host machine for later
500     /// execution. As the output is already a compiled module, translation and code
501     /// generation will be skipped and this will improve the performance of constructing
502     /// a [`Module`](crate::Module) from the output of this method.
503     ///
504     /// [binary]: https://webassembly.github.io/spec/core/binary/index.html
505     /// [text]: https://webassembly.github.io/spec/core/text/index.html
506     pub fn precompile_module(&self, bytes: &[u8]) -> Result<Vec<u8>> {
507         crate::CodeBuilder::new(self)
508             .wasm(bytes, None)?
509             .compile_module_serialized()
510     }
511 
512     /// Same as [`Engine::precompile_module`] except for a
513     /// [`Component`](crate::component::Component)
514     #[cfg(feature = "component-model")]
515     pub fn precompile_component(&self, bytes: &[u8]) -> Result<Vec<u8>> {
516         crate::CodeBuilder::new(self)
517             .wasm(bytes, None)?
518             .compile_component_serialized()
519     }
520 
521     /// Produces a blob of bytes by serializing the `engine`'s configuration data to
522     /// be checked, perhaps in a different process, with the `check_compatible`
523     /// method below.
524     ///
525     /// The blob of bytes is inserted into the object file specified to become part
526     /// of the final compiled artifact.
527     pub(crate) fn append_compiler_info(&self, obj: &mut Object<'_>) {
528         serialization::append_compiler_info(self, obj, &serialization::Metadata::new(&self))
529     }
530 
531     #[cfg(any(feature = "cranelift", feature = "winch"))]
532     pub(crate) fn append_bti(&self, obj: &mut Object<'_>) {
533         let section = obj.add_section(
534             obj.segment_name(StandardSegment::Data).to_vec(),
535             obj::ELF_WASM_BTI.as_bytes().to_vec(),
536             SectionKind::ReadOnlyData,
537         );
538         let contents = if self.compiler().is_branch_protection_enabled() {
539             1
540         } else {
541             0
542         };
543         obj.append_section_data(section, &[contents], 1);
544     }
545 }
546 
547 /// Return value from the [`Engine::detect_precompiled`] API.
548 #[derive(PartialEq, Eq, Copy, Clone, Debug)]
549 pub enum Precompiled {
550     /// The input bytes look like a precompiled core wasm module.
551     Module,
552     /// The input bytes look like a precompiled wasm component.
553     Component,
554 }
555 
556 #[cfg(feature = "runtime")]
557 impl Engine {
558     /// Eagerly initialize thread-local functionality shared by all [`Engine`]s.
559     ///
560     /// Wasmtime's implementation on some platforms may involve per-thread
561     /// setup that needs to happen whenever WebAssembly is invoked. This setup
562     /// can take on the order of a few hundred microseconds, whereas the
563     /// overhead of calling WebAssembly is otherwise on the order of a few
564     /// nanoseconds. This setup cost is paid once per-OS-thread. If your
565     /// application is sensitive to the latencies of WebAssembly function
566     /// calls, even those that happen first on a thread, then this function
567     /// can be used to improve the consistency of each call into WebAssembly
568     /// by explicitly frontloading the cost of the one-time setup per-thread.
569     ///
570     /// Note that this function is not required to be called in any embedding.
571     /// Wasmtime will automatically initialize thread-local-state as necessary
572     /// on calls into WebAssembly. This is provided for use cases where the
573     /// latency of WebAssembly calls are extra-important, which is not
574     /// necessarily true of all embeddings.
575     pub fn tls_eager_initialize() {
576         crate::runtime::vm::tls_eager_initialize();
577     }
578 
579     pub(crate) fn allocator(&self) -> &dyn crate::runtime::vm::InstanceAllocator {
580         self.inner.allocator.as_ref()
581     }
582 
583     pub(crate) fn gc_runtime(&self) -> &Arc<dyn GcRuntime> {
584         &self.inner.gc_runtime
585     }
586 
587     pub(crate) fn profiler(&self) -> &dyn crate::profiling_agent::ProfilingAgent {
588         self.inner.profiler.as_ref()
589     }
590 
591     #[cfg(feature = "cache")]
592     pub(crate) fn cache_config(&self) -> &wasmtime_cache::CacheConfig {
593         &self.config().cache_config
594     }
595 
596     pub(crate) fn signatures(&self) -> &TypeRegistry {
597         &self.inner.signatures
598     }
599 
600     pub(crate) fn epoch_counter(&self) -> &AtomicU64 {
601         &self.inner.epoch
602     }
603 
604     pub(crate) fn current_epoch(&self) -> u64 {
605         self.epoch_counter().load(Ordering::Relaxed)
606     }
607 
608     /// Increments the epoch.
609     ///
610     /// When using epoch-based interruption, currently-executing Wasm
611     /// code within this engine will trap or yield "soon" when the
612     /// epoch deadline is reached or exceeded. (The configuration, and
613     /// the deadline, are set on the `Store`.) The intent of the
614     /// design is for this method to be called by the embedder at some
615     /// regular cadence, for example by a thread that wakes up at some
616     /// interval, or by a signal handler.
617     ///
618     /// See [`Config::epoch_interruption`](crate::Config::epoch_interruption)
619     /// for an introduction to epoch-based interruption and pointers
620     /// to the other relevant methods.
621     ///
622     /// When performing `increment_epoch` in a separate thread, consider using
623     /// [`Engine::weak`] to hold an [`EngineWeak`](crate::EngineWeak) and
624     /// performing [`EngineWeak::upgrade`](crate::EngineWeak::upgrade) on each
625     /// tick, so that the epoch ticking thread does not keep an [`Engine`] alive
626     /// longer than any of its consumers.
627     ///
628     /// ## Signal Safety
629     ///
630     /// This method is signal-safe: it does not make any syscalls, and
631     /// performs only an atomic increment to the epoch value in
632     /// memory.
633     pub fn increment_epoch(&self) {
634         self.inner.epoch.fetch_add(1, Ordering::Relaxed);
635     }
636 
637     /// Returns a [`std::hash::Hash`] that can be used to check precompiled WebAssembly compatibility.
638     ///
639     /// The outputs of [`Engine::precompile_module`] and [`Engine::precompile_component`]
640     /// are compatible with a different [`Engine`] instance only if the two engines use
641     /// compatible [`Config`]s. If this Hash matches between two [`Engine`]s then binaries
642     /// from one are guaranteed to deserialize in the other.
643     #[cfg(any(feature = "cranelift", feature = "winch"))]
644     pub fn precompile_compatibility_hash(&self) -> impl std::hash::Hash + '_ {
645         crate::compile::HashedEngineCompileEnv(self)
646     }
647 
648     /// Executes `f1` and `f2` in parallel if parallel compilation is enabled at
649     /// both runtime and compile time, otherwise runs them synchronously.
650     #[allow(dead_code)] // only used for the component-model feature right now
651     pub(crate) fn join_maybe_parallel<T, U>(
652         &self,
653         f1: impl FnOnce() -> T + Send,
654         f2: impl FnOnce() -> U + Send,
655     ) -> (T, U)
656     where
657         T: Send,
658         U: Send,
659     {
660         if self.config().parallel_compilation {
661             #[cfg(feature = "parallel-compilation")]
662             return rayon::join(f1, f2);
663         }
664         (f1(), f2())
665     }
666 
667     /// Loads a `CodeMemory` from the specified in-memory slice, copying it to a
668     /// uniquely owned mmap.
669     ///
670     /// The `expected` marker here is whether the bytes are expected to be a
671     /// precompiled module or a component.
672     pub(crate) fn load_code_bytes(
673         &self,
674         bytes: &[u8],
675         expected: ObjectKind,
676     ) -> Result<Arc<crate::CodeMemory>> {
677         self.load_code(crate::runtime::vm::MmapVec::from_slice(bytes)?, expected)
678     }
679 
680     /// Like `load_code_bytes`, but creates a mmap from a file on disk.
681     #[cfg(feature = "std")]
682     pub(crate) fn load_code_file(
683         &self,
684         path: &Path,
685         expected: ObjectKind,
686     ) -> Result<Arc<crate::CodeMemory>> {
687         self.load_code(
688             crate::runtime::vm::MmapVec::from_file(path).with_context(|| {
689                 format!("failed to create file mapping for: {}", path.display())
690             })?,
691             expected,
692         )
693     }
694 
695     pub(crate) fn load_code(
696         &self,
697         mmap: crate::runtime::vm::MmapVec,
698         expected: ObjectKind,
699     ) -> Result<Arc<crate::CodeMemory>> {
700         serialization::check_compatible(self, &mmap, expected)?;
701         let mut code = crate::CodeMemory::new(mmap)?;
702         code.publish()?;
703         Ok(Arc::new(code))
704     }
705 }
706 
707 /// A weak reference to an [`Engine`].
708 #[derive(Clone)]
709 pub struct EngineWeak {
710     inner: alloc::sync::Weak<EngineInner>,
711 }
712 
713 impl EngineWeak {
714     /// Upgrade this weak reference into an [`Engine`]. Returns `None` if
715     /// strong references (the [`Engine`] type itself) no longer exist.
716     pub fn upgrade(&self) -> Option<Engine> {
717         alloc::sync::Weak::upgrade(&self.inner).map(|inner| Engine { inner })
718     }
719 }
720