gwasmi/engine/
mod.rs

1//! The `wasmi` interpreter.
2
3pub mod bytecode;
4mod cache;
5pub mod code_map;
6mod config;
7mod const_pool;
8pub mod executor;
9mod func_args;
10mod func_builder;
11mod func_types;
12mod resumable;
13pub mod stack;
14mod traits;
15
16#[cfg(test)]
17mod tests;
18
19pub use self::{
20    bytecode::DropKeep,
21    code_map::CompiledFunc,
22    config::{Config, FuelConsumptionMode},
23    func_builder::{
24        FuncBuilder,
25        FuncTranslatorAllocations,
26        Instr,
27        RelativeDepth,
28        TranslationError,
29    },
30    resumable::{ResumableCall, ResumableInvocation, TypedResumableCall, TypedResumableInvocation},
31    stack::StackLimits,
32    traits::{CallParams, CallResults},
33};
34use self::{
35    bytecode::Instruction,
36    cache::InstanceCache,
37    code_map::CodeMap,
38    const_pool::{ConstPool, ConstPoolView, ConstRef},
39    executor::{execute_wasm, WasmOutcome},
40    func_types::FuncTypeRegistry,
41    resumable::ResumableCallBase,
42    stack::{FuncFrame, Stack, ValueStack},
43};
44pub(crate) use self::{
45    func_args::{FuncFinished, FuncParams, FuncResults},
46    func_types::DedupFuncType,
47};
48use crate::{
49    core::{Trap, TrapCode},
50    func::FuncEntity,
51    AsContext,
52    AsContextMut,
53    Func,
54    FuncType,
55    StoreContextMut,
56};
57use alloc::{sync::Arc, vec::Vec};
58use core::sync::atomic::{AtomicU32, Ordering};
59use spin::{Mutex, RwLock};
60use wasmi_arena::{ArenaIndex, GuardedEntity};
61use wasmi_core::UntypedValue;
62
63/// A unique engine index.
64///
65/// # Note
66///
67/// Used to protect against invalid entity indices.
68#[derive(Debug, Copy, Clone, PartialEq, Eq)]
69pub struct EngineIdx(u32);
70
71impl ArenaIndex for EngineIdx {
72    fn into_usize(self) -> usize {
73        self.0 as _
74    }
75
76    fn from_usize(value: usize) -> Self {
77        let value = value.try_into().unwrap_or_else(|error| {
78            panic!("index {value} is out of bounds as engine index: {error}")
79        });
80        Self(value)
81    }
82}
83
84impl EngineIdx {
85    /// Returns a new unique [`EngineIdx`].
86    fn new() -> Self {
87        /// A static store index counter.
88        static CURRENT_STORE_IDX: AtomicU32 = AtomicU32::new(0);
89        let next_idx = CURRENT_STORE_IDX.fetch_add(1, Ordering::AcqRel);
90        Self(next_idx)
91    }
92}
93
94/// An entity owned by the [`Engine`].
95type Guarded<Idx> = GuardedEntity<EngineIdx, Idx>;
96
97/// The `wasmi` interpreter.
98///
99/// # Note
100///
101/// - The current `wasmi` engine implements a bytecode interpreter.
102/// - This structure is intentionally cheap to copy.
103///   Most of its API has a `&self` receiver, so can be shared easily.
104#[derive(Debug, Clone)]
105pub struct Engine {
106    inner: Arc<EngineInner>,
107}
108
109impl Default for Engine {
110    fn default() -> Self {
111        Self::new(&Config::default())
112    }
113}
114
115impl Engine {
116    /// Creates a new [`Engine`] with default configuration.
117    ///
118    /// # Note
119    ///
120    /// Users should ues [`Engine::default`] to construct a default [`Engine`].
121    pub fn new(config: &Config) -> Self {
122        Self {
123            inner: Arc::new(EngineInner::new(config)),
124        }
125    }
126
127    /// Returns a shared reference to the [`Config`] of the [`Engine`].
128    pub fn config(&self) -> &Config {
129        self.inner.config()
130    }
131
132    /// Returns `true` if both [`Engine`] references `a` and `b` refer to the same [`Engine`].
133    pub fn same(a: &Engine, b: &Engine) -> bool {
134        Arc::ptr_eq(&a.inner, &b.inner)
135    }
136
137    /// Allocates a new function type to the [`Engine`].
138    pub(super) fn alloc_func_type(&self, func_type: FuncType) -> DedupFuncType {
139        self.inner.alloc_func_type(func_type)
140    }
141
142    /// Allocates a new constant value to the [`Engine`].
143    ///
144    /// # Errors
145    ///
146    /// If too many constant values have been allocated for the [`Engine`] this way.
147    pub(super) fn alloc_const(&self, value: UntypedValue) -> Result<ConstRef, TranslationError> {
148        self.inner.alloc_const(value)
149    }
150
151    /// Resolves a deduplicated function type into a [`FuncType`] entity.
152    ///
153    /// # Panics
154    ///
155    /// - If the deduplicated function type is not owned by the engine.
156    /// - If the deduplicated function type cannot be resolved to its entity.
157    pub(super) fn resolve_func_type<F, R>(&self, func_type: &DedupFuncType, f: F) -> R
158    where
159        F: FnOnce(&FuncType) -> R,
160    {
161        self.inner.resolve_func_type(func_type, f)
162    }
163
164    /// Allocates a new uninitialized [`CompiledFunc`] to the [`Engine`].
165    ///
166    /// Returns a [`CompiledFunc`] reference to allow accessing the allocated [`CompiledFunc`].
167    pub(super) fn alloc_func(&self) -> CompiledFunc {
168        self.inner.alloc_func()
169    }
170
171    /// Initializes the uninitialized [`CompiledFunc`] for the [`Engine`].
172    ///
173    /// # Panics
174    ///
175    /// - If `func` is an invalid [`CompiledFunc`] reference for this [`CodeMap`].
176    /// - If `func` refers to an already initialized [`CompiledFunc`].
177    pub(super) fn init_func<I>(
178        &self,
179        func: CompiledFunc,
180        len_locals: usize,
181        local_stack_height: usize,
182        instrs: I,
183    ) where
184        I: IntoIterator<Item = Instruction>,
185    {
186        self.inner
187            .init_func(func, len_locals, local_stack_height, instrs)
188    }
189
190    /// Resolves the [`CompiledFunc`] to the underlying `wasmi` bytecode instructions.
191    ///
192    /// # Note
193    ///
194    /// - This API is mainly intended for unit testing purposes and shall not be used
195    ///   outside of this context. The function bodies are intended to be data private
196    ///   to the `wasmi` interpreter.
197    ///
198    /// # Panics
199    ///
200    /// If the [`CompiledFunc`] is invalid for the [`Engine`].
201    #[cfg(test)]
202    pub(crate) fn resolve_instr(
203        &self,
204        func_body: CompiledFunc,
205        index: usize,
206    ) -> Option<Instruction> {
207        self.inner.resolve_instr(func_body, index)
208    }
209
210    /// Executes the given [`Func`] with parameters `params`.
211    ///
212    /// Stores the execution result into `results` upon a successful execution.
213    ///
214    /// # Note
215    ///
216    /// - Assumes that the `params` and `results` are well typed.
217    ///   Type checks are done at the [`Func::call`] API or when creating
218    ///   a new [`TypedFunc`] instance via [`Func::typed`].
219    /// - The `params` out parameter is in a valid but unspecified state if this
220    ///   function returns with an error.
221    ///
222    /// # Errors
223    ///
224    /// - If `params` are overflowing or underflowing the expected amount of parameters.
225    /// - If the given `results` do not match the the length of the expected results of `func`.
226    /// - When encountering a Wasm or host trap during the execution of `func`.
227    ///
228    /// [`TypedFunc`]: [`crate::TypedFunc`]
229    #[inline]
230    pub(crate) fn execute_func<T, Results>(
231        &self,
232        ctx: StoreContextMut<T>,
233        func: &Func,
234        params: impl CallParams,
235        results: Results,
236    ) -> Result<<Results as CallResults>::Results, Trap>
237    where
238        Results: CallResults,
239    {
240        self.inner.execute_func(ctx, func, params, results)
241    }
242
243    /// Executes the given [`Func`] resumably with parameters `params` and returns.
244    ///
245    /// Stores the execution result into `results` upon a successful execution.
246    /// If the execution encounters a host trap it will return a handle to the user
247    /// that allows to resume the execution at that point.
248    ///
249    /// # Note
250    ///
251    /// - Assumes that the `params` and `results` are well typed.
252    ///   Type checks are done at the [`Func::call`] API or when creating
253    ///   a new [`TypedFunc`] instance via [`Func::typed`].
254    /// - The `params` out parameter is in a valid but unspecified state if this
255    ///   function returns with an error.
256    ///
257    /// # Errors
258    ///
259    /// - If `params` are overflowing or underflowing the expected amount of parameters.
260    /// - If the given `results` do not match the the length of the expected results of `func`.
261    /// - When encountering a Wasm trap during the execution of `func`.
262    /// - When `func` is a host function that traps.
263    ///
264    /// [`TypedFunc`]: [`crate::TypedFunc`]
265    #[inline]
266    pub(crate) fn execute_func_resumable<T, Results>(
267        &self,
268        ctx: StoreContextMut<T>,
269        func: &Func,
270        params: impl CallParams,
271        results: Results,
272    ) -> Result<ResumableCallBase<<Results as CallResults>::Results>, Trap>
273    where
274        Results: CallResults,
275    {
276        self.inner
277            .execute_func_resumable(ctx, func, params, results)
278    }
279
280    /// Resumes the given `invocation` given the `params`.
281    ///
282    /// Stores the execution result into `results` upon a successful execution.
283    /// If the execution encounters a host trap it will return a handle to the user
284    /// that allows to resume the execution at that point.
285    ///
286    /// # Note
287    ///
288    /// - Assumes that the `params` and `results` are well typed.
289    ///   Type checks are done at the [`Func::call`] API or when creating
290    ///   a new [`TypedFunc`] instance via [`Func::typed`].
291    /// - The `params` out parameter is in a valid but unspecified state if this
292    ///   function returns with an error.
293    ///
294    /// # Errors
295    ///
296    /// - If `params` are overflowing or underflowing the expected amount of parameters.
297    /// - If the given `results` do not match the the length of the expected results of `func`.
298    /// - When encountering a Wasm trap during the execution of `func`.
299    /// - When `func` is a host function that traps.
300    ///
301    /// [`TypedFunc`]: [`crate::TypedFunc`]
302    #[inline]
303    pub(crate) fn resume_func<T, Results>(
304        &self,
305        ctx: StoreContextMut<T>,
306        invocation: ResumableInvocation,
307        params: impl CallParams,
308        results: Results,
309    ) -> Result<ResumableCallBase<<Results as CallResults>::Results>, Trap>
310    where
311        Results: CallResults,
312    {
313        self.inner.resume_func(ctx, invocation, params, results)
314    }
315
316    /// Recycles the given [`Stack`] for reuse in the [`Engine`].
317    pub(crate) fn recycle_stack(&self, stack: Stack) {
318        self.inner.recycle_stack(stack)
319    }
320}
321
322/// The internal state of the `wasmi` [`Engine`].
323#[derive(Debug)]
324pub struct EngineInner {
325    /// The [`Config`] of the engine.
326    config: Config,
327    /// Engine resources shared across multiple engine executors.
328    res: RwLock<EngineResources>,
329    /// Reusable engine stacks for Wasm execution.
330    ///
331    /// Concurrently executing Wasm executions each require their own stack to
332    /// operate on. Therefore a Wasm engine is required to provide stacks and
333    /// ideally recycles old ones since creation of a new stack is rather expensive.
334    stacks: Mutex<EngineStacks>,
335}
336
337/// The engine's stacks for reuse.
338///
339/// Rquired for efficient concurrent Wasm executions.
340#[derive(Debug)]
341pub struct EngineStacks {
342    /// Stacks to be (re)used.
343    stacks: Vec<Stack>,
344    /// Stack limits for newly constructed engine stacks.
345    limits: StackLimits,
346    /// How many stacks should be kept for reuse at most.
347    keep: usize,
348}
349
350impl EngineStacks {
351    /// Creates new [`EngineStacks`] with the given [`StackLimits`].
352    pub fn new(config: &Config) -> Self {
353        Self {
354            stacks: Vec::new(),
355            limits: config.stack_limits(),
356            keep: config.cached_stacks(),
357        }
358    }
359
360    /// Reuse or create a new [`Stack`] if none was available.
361    pub fn reuse_or_new(&mut self) -> Stack {
362        match self.stacks.pop() {
363            Some(stack) => stack,
364            None => Stack::new(self.limits),
365        }
366    }
367
368    /// Disose and recycle the `stack`.
369    pub fn recycle(&mut self, stack: Stack) {
370        if !stack.is_empty() && self.stacks.len() < self.keep {
371            self.stacks.push(stack);
372        }
373    }
374}
375
376impl EngineInner {
377    /// Creates a new [`EngineInner`] with the given [`Config`].
378    fn new(config: &Config) -> Self {
379        Self {
380            config: *config,
381            res: RwLock::new(EngineResources::new()),
382            stacks: Mutex::new(EngineStacks::new(config)),
383        }
384    }
385
386    /// Returns a shared reference to the [`Config`] of the [`EngineInner`].
387    fn config(&self) -> &Config {
388        &self.config
389    }
390
391    /// Allocates a new function type to the [`EngineInner`].
392    fn alloc_func_type(&self, func_type: FuncType) -> DedupFuncType {
393        self.res.write().func_types.alloc_func_type(func_type)
394    }
395
396    /// Allocates a new constant value to the [`EngineInner`].
397    ///
398    /// # Errors
399    ///
400    /// If too many constant values have been allocated for the [`EngineInner`] this way.
401    fn alloc_const(&self, value: UntypedValue) -> Result<ConstRef, TranslationError> {
402        self.res.write().const_pool.alloc(value)
403    }
404
405    /// Allocates a new uninitialized [`CompiledFunc`] to the [`EngineInner`].
406    ///
407    /// Returns a [`CompiledFunc`] reference to allow accessing the allocated [`CompiledFunc`].
408    fn alloc_func(&self) -> CompiledFunc {
409        self.res.write().code_map.alloc_func()
410    }
411
412    /// Initializes the uninitialized [`CompiledFunc`] for the [`EngineInner`].
413    ///
414    /// # Panics
415    ///
416    /// - If `func` is an invalid [`CompiledFunc`] reference for this [`CodeMap`].
417    /// - If `func` refers to an already initialized [`CompiledFunc`].
418    fn init_func<I>(
419        &self,
420        func: CompiledFunc,
421        len_locals: usize,
422        local_stack_height: usize,
423        instrs: I,
424    ) where
425        I: IntoIterator<Item = Instruction>,
426    {
427        self.res
428            .write()
429            .code_map
430            .init_func(func, len_locals, local_stack_height, instrs)
431    }
432
433    fn resolve_func_type<F, R>(&self, func_type: &DedupFuncType, f: F) -> R
434    where
435        F: FnOnce(&FuncType) -> R,
436    {
437        f(self.res.read().func_types.resolve_func_type(func_type))
438    }
439
440    #[cfg(test)]
441    fn resolve_instr(&self, func_body: CompiledFunc, index: usize) -> Option<Instruction> {
442        self.res
443            .read()
444            .code_map
445            .get_instr(func_body, index)
446            .copied()
447    }
448
449    fn execute_func<T, Results>(
450        &self,
451        ctx: StoreContextMut<T>,
452        func: &Func,
453        params: impl CallParams,
454        results: Results,
455    ) -> Result<<Results as CallResults>::Results, Trap>
456    where
457        Results: CallResults,
458    {
459        let res = self.res.read();
460        let mut stack = self.stacks.lock().reuse_or_new();
461        let results = EngineExecutor::new(&res, &mut stack)
462            .execute_func(ctx, func, params, results)
463            .map_err(TaggedTrap::into_trap);
464        self.stacks.lock().recycle(stack);
465        results
466    }
467
468    fn execute_func_resumable<T, Results>(
469        &self,
470        mut ctx: StoreContextMut<T>,
471        func: &Func,
472        params: impl CallParams,
473        results: Results,
474    ) -> Result<ResumableCallBase<<Results as CallResults>::Results>, Trap>
475    where
476        Results: CallResults,
477    {
478        let res = self.res.read();
479        let mut stack = self.stacks.lock().reuse_or_new();
480        let results = EngineExecutor::new(&res, &mut stack).execute_func(
481            ctx.as_context_mut(),
482            func,
483            params,
484            results,
485        );
486        match results {
487            Ok(results) => {
488                self.stacks.lock().recycle(stack);
489                Ok(ResumableCallBase::Finished(results))
490            }
491            Err(TaggedTrap::Wasm(trap)) => {
492                self.stacks.lock().recycle(stack);
493                Err(trap)
494            }
495            Err(TaggedTrap::Host {
496                host_func,
497                host_trap,
498            }) => Ok(ResumableCallBase::Resumable(ResumableInvocation::new(
499                ctx.as_context().store.engine().clone(),
500                *func,
501                host_func,
502                host_trap,
503                stack,
504            ))),
505        }
506    }
507
508    fn resume_func<T, Results>(
509        &self,
510        ctx: StoreContextMut<T>,
511        mut invocation: ResumableInvocation,
512        params: impl CallParams,
513        results: Results,
514    ) -> Result<ResumableCallBase<<Results as CallResults>::Results>, Trap>
515    where
516        Results: CallResults,
517    {
518        let res = self.res.read();
519        let host_func = invocation.host_func();
520        let results = EngineExecutor::new(&res, &mut invocation.stack)
521            .resume_func(ctx, host_func, params, results);
522        match results {
523            Ok(results) => {
524                self.stacks.lock().recycle(invocation.take_stack());
525                Ok(ResumableCallBase::Finished(results))
526            }
527            Err(TaggedTrap::Wasm(trap)) => {
528                self.stacks.lock().recycle(invocation.take_stack());
529                Err(trap)
530            }
531            Err(TaggedTrap::Host {
532                host_func,
533                host_trap,
534            }) => {
535                invocation.update(host_func, host_trap);
536                Ok(ResumableCallBase::Resumable(invocation))
537            }
538        }
539    }
540
541    fn recycle_stack(&self, stack: Stack) {
542        self.stacks.lock().recycle(stack);
543    }
544}
545
546/// Engine resources that are immutable during function execution.
547///
548/// Can be shared by multiple engine executors.
549#[derive(Debug)]
550pub struct EngineResources {
551    /// Stores all Wasm function bodies that the interpreter is aware of.
552    code_map: CodeMap,
553    /// A pool of reusable, deduplicated constant values.
554    const_pool: ConstPool,
555    /// Deduplicated function types.
556    ///
557    /// # Note
558    ///
559    /// The engine deduplicates function types to make the equality
560    /// comparison very fast. This helps to speed up indirect calls.
561    func_types: FuncTypeRegistry,
562}
563
564impl EngineResources {
565    /// Creates a new [`EngineResources`].
566    fn new() -> Self {
567        let engine_idx = EngineIdx::new();
568        Self {
569            code_map: CodeMap::default(),
570            const_pool: ConstPool::default(),
571            func_types: FuncTypeRegistry::new(engine_idx),
572        }
573    }
574}
575
576/// Either a Wasm trap or a host trap with its originating host [`Func`].
577#[derive(Debug)]
578enum TaggedTrap {
579    /// The trap is originating from Wasm.
580    Wasm(Trap),
581    /// The trap is originating from a host function.
582    Host { host_func: Func, host_trap: Trap },
583}
584
585impl TaggedTrap {
586    /// Creates a [`TaggedTrap`] from a host error.
587    pub fn host(host_func: Func, host_trap: Trap) -> Self {
588        Self::Host {
589            host_func,
590            host_trap,
591        }
592    }
593
594    /// Returns the [`Trap`] of the [`TaggedTrap`].
595    pub fn into_trap(self) -> Trap {
596        match self {
597            TaggedTrap::Wasm(trap) => trap,
598            TaggedTrap::Host { host_trap, .. } => host_trap,
599        }
600    }
601}
602
603impl From<Trap> for TaggedTrap {
604    fn from(trap: Trap) -> Self {
605        Self::Wasm(trap)
606    }
607}
608
609impl From<TrapCode> for TaggedTrap {
610    fn from(trap_code: TrapCode) -> Self {
611        Self::Wasm(trap_code.into())
612    }
613}
614
615/// The internal state of the `wasmi` engine.
616#[derive(Debug)]
617pub struct EngineExecutor<'engine> {
618    /// Shared and reusable generic engine resources.
619    res: &'engine EngineResources,
620    /// The value and call stacks.
621    stack: &'engine mut Stack,
622}
623
624impl<'engine> EngineExecutor<'engine> {
625    /// Creates a new [`EngineExecutor`] with the given [`StackLimits`].
626    fn new(res: &'engine EngineResources, stack: &'engine mut Stack) -> Self {
627        Self { res, stack }
628    }
629
630    /// Executes the given [`Func`] using the given `params`.
631    ///
632    /// Stores the execution result into `results` upon a successful execution.
633    ///
634    /// # Errors
635    ///
636    /// - If the given `params` do not match the expected parameters of `func`.
637    /// - If the given `results` do not match the the length of the expected results of `func`.
638    /// - When encountering a Wasm or host trap during the execution of `func`.
639    fn execute_func<T, Results>(
640        &mut self,
641        mut ctx: StoreContextMut<T>,
642        func: &Func,
643        params: impl CallParams,
644        results: Results,
645    ) -> Result<<Results as CallResults>::Results, TaggedTrap>
646    where
647        Results: CallResults,
648    {
649        self.stack.reset();
650        self.stack.values.extend(params.call_params());
651        match ctx.as_context().store.inner.resolve_func(func) {
652            FuncEntity::Wasm(wasm_func) => {
653                self.stack
654                    .prepare_wasm_call(wasm_func, &self.res.code_map)?;
655                self.execute_wasm_func(ctx.as_context_mut())?;
656            }
657            FuncEntity::Host(host_func) => {
658                let host_func = *host_func;
659                self.stack.call_host_as_root(
660                    ctx.as_context_mut(),
661                    host_func,
662                    &self.res.func_types,
663                )?;
664            }
665        };
666        let results = self.write_results_back(results);
667        Ok(results)
668    }
669
670    /// Resumes the execution of the given [`Func`] using `params`.
671    ///
672    /// Stores the execution result into `results` upon a successful execution.
673    ///
674    /// # Errors
675    ///
676    /// - If the given `params` do not match the expected parameters of `func`.
677    /// - If the given `results` do not match the the length of the expected results of `func`.
678    /// - When encountering a Wasm or host trap during the execution of `func`.
679    fn resume_func<T, Results>(
680        &mut self,
681        mut ctx: StoreContextMut<T>,
682        host_func: Func,
683        params: impl CallParams,
684        results: Results,
685    ) -> Result<<Results as CallResults>::Results, TaggedTrap>
686    where
687        Results: CallResults,
688    {
689        self.stack
690            .values
691            .drop(host_func.ty(ctx.as_context()).params().len());
692        self.stack.values.extend(params.call_params());
693        assert!(
694            self.stack.frames.peek().is_some(),
695            "a frame must be on the call stack upon resumption"
696        );
697        self.execute_wasm_func(ctx.as_context_mut())?;
698        let results = self.write_results_back(results);
699        Ok(results)
700    }
701
702    /// Writes the results of the function execution back into the `results` buffer.
703    ///
704    /// # Note
705    ///
706    /// The value stack is empty after this operation.
707    ///
708    /// # Panics
709    ///
710    /// - If the `results` buffer length does not match the remaining amount of stack values.
711    #[inline]
712    fn write_results_back<Results>(&mut self, results: Results) -> <Results as CallResults>::Results
713    where
714        Results: CallResults,
715    {
716        results.call_results(self.stack.values.drain())
717    }
718
719    /// Executes the top most Wasm function on the [`Stack`] until the [`Stack`] is empty.
720    ///
721    /// # Errors
722    ///
723    /// When encountering a Wasm or host trap during the execution of `func`.
724    #[inline(never)]
725    fn execute_wasm_func<T>(&mut self, mut ctx: StoreContextMut<T>) -> Result<(), TaggedTrap> {
726        let mut cache = self
727            .stack
728            .frames
729            .peek()
730            .map(FuncFrame::instance)
731            .map(InstanceCache::from)
732            .expect("must have frame on the call stack");
733        loop {
734            match self.execute_wasm(ctx.as_context_mut(), &mut cache)? {
735                WasmOutcome::Return => return Ok(()),
736                WasmOutcome::Call {
737                    ref host_func,
738                    instance,
739                } => {
740                    let func = host_func;
741                    let host_func = match ctx.as_context().store.inner.resolve_func(func) {
742                        FuncEntity::Wasm(_) => unreachable!("`func` must be a host function"),
743                        FuncEntity::Host(host_func) => *host_func,
744                    };
745                    let result = self.stack.call_host_impl(
746                        ctx.as_context_mut(),
747                        host_func,
748                        Some(&instance),
749                        &self.res.func_types,
750                    );
751                    if self.stack.frames.peek().is_some() {
752                        // Case: There is a frame on the call stack.
753                        //
754                        // This is the default case and we can easily make host function
755                        // errors return a resumable call handle.
756                        result.map_err(|trap| TaggedTrap::host(*func, trap))?;
757                    } else {
758                        // Case: No frame is on the call stack. (edge case)
759                        //
760                        // This can happen if the host function was called by a tail call.
761                        // In this case we treat host function errors the same as if we called
762                        // the host function as root and do not allow to resume the call.
763                        result.map_err(TaggedTrap::Wasm)?;
764                    }
765                }
766            }
767        }
768    }
769
770    /// Executes the given function `frame`.
771    ///
772    /// # Note
773    ///
774    /// This executes Wasm instructions until either the execution calls
775    /// into a host function or the Wasm execution has come to an end.
776    ///
777    /// # Errors
778    ///
779    /// If the Wasm execution traps.
780    #[inline(always)]
781    fn execute_wasm<T>(
782        &mut self,
783        ctx: StoreContextMut<T>,
784        cache: &mut InstanceCache,
785    ) -> Result<WasmOutcome, Trap> {
786        /// Converts a [`TrapCode`] into a [`Trap`].
787        ///
788        /// This function exists for performance reasons since its `#[cold]`
789        /// annotation has severe effects on performance.
790        #[inline]
791        #[cold]
792        fn make_trap(code: TrapCode) -> Trap {
793            code.into()
794        }
795
796        let store_inner = &mut ctx.store.inner;
797        let value_stack = &mut self.stack.values;
798        let call_stack = &mut self.stack.frames;
799        let code_map = &self.res.code_map;
800        let const_pool = self.res.const_pool.view();
801        execute_wasm(
802            store_inner,
803            cache,
804            value_stack,
805            call_stack,
806            code_map,
807            const_pool,
808        )
809        .map_err(make_trap)
810    }
811}