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