Skip to main content

winch_codegen/codegen/
mod.rs

1use crate::{
2    Result,
3    abi::{ABIOperand, ABISig, RetArea, vmctx},
4    bail,
5    codegen::BlockSig,
6    ensure, format_err,
7    isa::reg::{Reg, RegClass, writable},
8    masm::{
9        AtomicWaitKind, Extend, Imm, IntCmpKind, IntScratch, LaneSelector, LoadKind,
10        MacroAssembler, OperandSize, RegImm, RmwOp, SPOffset, ShiftKind, StoreKind, TrapCode,
11        UNTRUSTED_FLAGS, Zero,
12    },
13    stack::{TypedReg, Val},
14};
15use cranelift_codegen::{
16    binemit::CodeOffset,
17    ir::{RelSourceLoc, SourceLoc},
18};
19use smallvec::SmallVec;
20use std::marker::PhantomData;
21use wasmparser::{
22    BinaryReader, FuncValidator, MemArg, Operator, OperatorsReader, ValidatorResources,
23    VisitOperator, VisitSimdOperator,
24};
25use wasmtime_cranelift::{TRAP_BAD_SIGNATURE, TRAP_HEAP_MISALIGNED, TRAP_TABLE_OUT_OF_BOUNDS};
26use wasmtime_environ::{
27    DataIndex, ElemIndex, FUNCREF_INIT_BIT, FUNCREF_MASK, GlobalIndex, IndexType, MemoryIndex,
28    MemoryKind, MemoryTunables, PtrSize, TableIndex, Tunables, TypeIndex, WasmHeapType,
29    WasmValType,
30};
31
32mod context;
33pub(crate) use context::*;
34mod env;
35pub use env::*;
36mod call;
37pub(crate) use call::*;
38mod control;
39pub(crate) use control::*;
40mod builtin;
41pub use builtin::*;
42pub(crate) mod bounds;
43
44use bounds::{Bounds, ImmOffset, Index};
45
46mod phase;
47pub(crate) use phase::*;
48
49mod error;
50pub(crate) use error::*;
51
52/// Branch states in the compiler, enabling the derivation of the
53/// reachability state.
54pub(crate) trait BranchState {
55    /// Whether the compiler will enter in an unreachable state after
56    /// the branch is emitted.
57    fn unreachable_state_after_emission() -> bool;
58}
59
60/// A conditional branch state, with a fallthrough.
61pub(crate) struct ConditionalBranch;
62
63impl BranchState for ConditionalBranch {
64    fn unreachable_state_after_emission() -> bool {
65        false
66    }
67}
68
69/// Unconditional branch state.
70pub(crate) struct UnconditionalBranch;
71
72impl BranchState for UnconditionalBranch {
73    fn unreachable_state_after_emission() -> bool {
74        true
75    }
76}
77
78/// Holds metadata about the source code location and the machine code emission.
79/// The fields of this struct are opaque and are not interpreted in any way.
80/// They serve as a mapping between source code and machine code.
81#[derive(Default)]
82pub(crate) struct SourceLocation {
83    /// The base source location.
84    pub base: Option<SourceLoc>,
85    /// The current relative source code location along with its associated
86    /// machine code offset.
87    pub current: (CodeOffset, RelSourceLoc),
88}
89
90/// The code generation abstraction.
91pub(crate) struct CodeGen<'a, 'translation: 'a, 'data: 'translation, M, P>
92where
93    M: MacroAssembler,
94    P: CodeGenPhase,
95{
96    /// The ABI-specific representation of the function signature, excluding results.
97    pub sig: ABISig,
98
99    /// The code generation context.
100    pub context: CodeGenContext<'a, P>,
101
102    /// A reference to the function compilation environment.
103    pub env: FuncEnv<'a, 'translation, 'data, M::Ptr>,
104
105    /// The MacroAssembler.
106    pub masm: &'a mut M,
107
108    /// Stack frames for control flow.
109    // NB The 64 is set arbitrarily, we can adjust it as
110    // we see fit.
111    pub control_frames: SmallVec<[ControlStackFrame; 64]>,
112
113    /// Information about the source code location.
114    pub source_location: SourceLocation,
115
116    /// Compilation settings for code generation.
117    pub tunables: &'a Tunables,
118
119    /// Local counter to track fuel consumption.
120    pub fuel_consumed: i64,
121    phase: PhantomData<P>,
122}
123
124impl<'a, 'translation, 'data, M> CodeGen<'a, 'translation, 'data, M, Prologue>
125where
126    M: MacroAssembler,
127{
128    pub fn new(
129        tunables: &'a Tunables,
130        masm: &'a mut M,
131        context: CodeGenContext<'a, Prologue>,
132        env: FuncEnv<'a, 'translation, 'data, M::Ptr>,
133        sig: ABISig,
134    ) -> CodeGen<'a, 'translation, 'data, M, Prologue> {
135        Self {
136            sig,
137            context,
138            masm,
139            env,
140            tunables,
141            source_location: Default::default(),
142            control_frames: Default::default(),
143            // Empty functions should consume at least 1 fuel unit.
144            fuel_consumed: 1,
145            phase: PhantomData,
146        }
147    }
148
149    /// Code generation prologue.
150    pub fn emit_prologue(mut self) -> Result<CodeGen<'a, 'translation, 'data, M, Emission>> {
151        let vmctx = self
152            .sig
153            .params()
154            .first()
155            .ok_or_else(|| format_err!(CodeGenError::vmcontext_arg_expected()))?
156            .unwrap_reg();
157
158        self.masm.start_source_loc(Default::default())?;
159        // We need to use the vmctx parameter before pinning it for stack checking.
160        self.masm.prologue(vmctx)?;
161
162        // Pin the `VMContext` pointer.
163        self.masm.mov(
164            writable!(vmctx!(M)),
165            vmctx.into(),
166            self.env.ptr_type().try_into()?,
167        )?;
168
169        self.masm.reserve_stack(self.context.frame.locals_size)?;
170        self.spill_register_arguments()?;
171
172        let defined_locals_range = &self.context.frame.defined_locals_range;
173        self.masm.zero_mem_range(defined_locals_range.as_range())?;
174
175        // Save the results base parameter register into its slot.
176
177        if self.sig.params.has_retptr() {
178            match self.sig.params.unwrap_results_area_operand() {
179                ABIOperand::Reg { ty, reg, .. } => {
180                    let results_base_slot = self.context.frame.results_base_slot.as_ref().unwrap();
181                    ensure!(
182                        results_base_slot.addressed_from_sp(),
183                        CodeGenError::sp_addressing_expected(),
184                    );
185                    let addr = self.masm.local_address(results_base_slot)?;
186                    self.masm.store((*reg).into(), addr, (*ty).try_into()?)?;
187                }
188                // The result base parameter is a stack parameter, addressed
189                // from FP.
190                _ => {}
191            }
192        }
193
194        self.masm.end_source_loc()?;
195
196        Ok(CodeGen {
197            sig: self.sig,
198            context: self.context.for_emission(),
199            masm: self.masm,
200            env: self.env,
201            tunables: self.tunables,
202            source_location: self.source_location,
203            control_frames: self.control_frames,
204            fuel_consumed: self.fuel_consumed,
205            phase: PhantomData,
206        })
207    }
208
209    fn spill_register_arguments(&mut self) -> Result<()> {
210        use WasmValType::*;
211        for (operand, slot) in self
212            .sig
213            .params_without_retptr()
214            .iter()
215            .zip(self.context.frame.locals())
216        {
217            match (operand, slot) {
218                (ABIOperand::Reg { ty, reg, .. }, slot) => {
219                    let addr = self.masm.local_address(slot)?;
220                    match &ty {
221                        I32 | I64 | F32 | F64 | V128 => {
222                            self.masm.store((*reg).into(), addr, (*ty).try_into()?)?;
223                        }
224                        Ref(rt) => match rt.heap_type {
225                            WasmHeapType::Func | WasmHeapType::Extern => {
226                                self.masm.store_ptr(*reg, addr)?;
227                            }
228                            _ => bail!(CodeGenError::unsupported_wasm_type()),
229                        },
230                    }
231                }
232                // Skip non-register arguments
233                _ => {}
234            }
235        }
236        Ok(())
237    }
238}
239
240impl<'a, 'translation, 'data, M> CodeGen<'a, 'translation, 'data, M, Emission>
241where
242    M: MacroAssembler,
243{
244    /// Emit the function body to machine code.
245    pub fn emit(
246        &mut self,
247        body: BinaryReader<'a>,
248        validator: &mut FuncValidator<ValidatorResources>,
249    ) -> Result<()> {
250        self.emit_body(body, validator)
251            .and_then(|_| self.emit_end())?;
252
253        Ok(())
254    }
255
256    /// Pops a control frame from the control frame stack.
257    pub fn pop_control_frame(&mut self) -> Result<ControlStackFrame> {
258        self.control_frames
259            .pop()
260            .ok_or_else(|| format_err!(CodeGenError::control_frame_expected()))
261    }
262
263    /// Derives a [RelSourceLoc] from a [SourceLoc].
264    pub fn source_loc_from(&mut self, loc: SourceLoc) -> RelSourceLoc {
265        if self.source_location.base.is_none() && !loc.is_default() {
266            self.source_location.base = Some(loc);
267        }
268
269        RelSourceLoc::from_base_offset(self.source_location.base.unwrap_or_default(), loc)
270    }
271
272    /// The following two helpers, handle else or end instructions when the
273    /// compiler has entered into an unreachable code state. These instructions
274    /// must be observed to determine if the reachability state should be
275    /// restored.
276    ///
277    /// When the compiler is in an unreachable state, all the other instructions
278    /// are not visited.
279    pub fn handle_unreachable_else(&mut self) -> Result<()> {
280        let frame = self
281            .control_frames
282            .last_mut()
283            .ok_or_else(|| CodeGenError::control_frame_expected())?;
284        ensure!(frame.is_if(), CodeGenError::if_control_frame_expected());
285        if frame.is_next_sequence_reachable() {
286            // We entered an unreachable state when compiling the
287            // if-then branch, but if the `if` was reachable at
288            // entry, the if-else branch will be reachable.
289            self.context.reachable = true;
290            frame.ensure_stack_state(self.masm, &mut self.context)?;
291            frame.bind_else(self.masm, &mut self.context)?;
292        }
293        Ok(())
294    }
295
296    pub fn handle_unreachable_end(&mut self) -> Result<()> {
297        let mut frame = self.pop_control_frame()?;
298        // We just popped the outermost block.
299        let is_outermost = self.control_frames.len() == 0;
300
301        if frame.is_next_sequence_reachable() {
302            self.context.reachable = true;
303            frame.ensure_stack_state(self.masm, &mut self.context)?;
304            frame.bind_end(self.masm, &mut self.context)
305        } else if is_outermost {
306            // If we reach the end of the function in an unreachable
307            // state, perform the necessary cleanup to leave the stack
308            // and SP in the expected state.  The compiler can enter
309            // in this state through an infinite loop.
310            frame.ensure_stack_state(self.masm, &mut self.context)
311        } else {
312            Ok(())
313        }
314    }
315
316    fn emit_body(
317        &mut self,
318        body: BinaryReader<'a>,
319        validator: &mut FuncValidator<ValidatorResources>,
320    ) -> Result<()> {
321        self.maybe_emit_fuel_check()?;
322
323        self.maybe_emit_epoch_check()?;
324
325        // Once we have emitted the epilogue and reserved stack space for the locals, we push the
326        // base control flow block.
327        self.control_frames.push(ControlStackFrame::block(
328            BlockSig::from_sig(self.sig.clone()),
329            self.masm,
330            &mut self.context,
331        )?);
332
333        // Set the return area of the results *after* initializing the block. In
334        // the function body block case, we'll treat the results as any other
335        // case, addressed from the stack pointer, and when ending the function
336        // the return area will be set to the return pointer.
337        if self.sig.params.has_retptr() {
338            self.sig
339                .results
340                .set_ret_area(RetArea::slot(self.context.frame.results_base_slot.unwrap()));
341        }
342
343        let mut ops = OperatorsReader::new(body);
344        while !ops.eof() {
345            let offset = ops.original_position();
346            ops.visit_operator(&mut ValidateThenVisit(
347                validator.simd_visitor(offset),
348                self,
349                offset,
350            ))??;
351        }
352        ops.finish()?;
353        return Ok(());
354
355        struct ValidateThenVisit<'a, T, U>(T, &'a mut U, usize);
356
357        macro_rules! validate_then_visit {
358            ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $ann:tt)*) => {
359                $(
360                    fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output {
361                        self.0.$visit($($($arg.clone()),*)?)?;
362                        let op = Operator::$op $({ $($arg: $arg.clone()),* })?;
363                        if self.1.visit(&op) {
364                            self.1.before_visit_op(&op, self.2)?;
365                            let res = self.1.$visit($($($arg),*)?)?;
366                            self.1.after_visit_op()?;
367                            Ok(res)
368                        } else {
369                            Ok(())
370                        }
371                    }
372                )*
373            };
374        }
375
376        fn visit_op_when_unreachable(op: &Operator) -> bool {
377            use Operator::*;
378            match op {
379                If { .. } | Block { .. } | Loop { .. } | Else | End => true,
380                _ => false,
381            }
382        }
383
384        /// Trait to handle hooks that must happen before and after visiting an
385        /// operator.
386        trait VisitorHooks {
387            /// Hook prior to visiting an operator.
388            fn before_visit_op(&mut self, operator: &Operator, offset: usize) -> Result<()>;
389            /// Hook after visiting an operator.
390            fn after_visit_op(&mut self) -> Result<()>;
391
392            /// Returns `true` if the operator will be visited.
393            ///
394            /// Operators will be visited if the following invariants are met:
395            /// * The compiler is in a reachable state.
396            /// * The compiler is in an unreachable state, but the current
397            ///   operator is a control flow operator. These operators need to be
398            ///   visited in order to keep the control stack frames balanced and
399            ///   to determine if the reachability state must be restored.
400            fn visit(&self, op: &Operator) -> bool;
401        }
402
403        impl<'a, 'translation, 'data, M: MacroAssembler> VisitorHooks
404            for CodeGen<'a, 'translation, 'data, M, Emission>
405        {
406            fn visit(&self, op: &Operator) -> bool {
407                self.context.reachable || visit_op_when_unreachable(op)
408            }
409
410            fn before_visit_op(&mut self, operator: &Operator, offset: usize) -> Result<()> {
411                // Handle source location mapping.
412                self.source_location_before_visit_op(offset)?;
413
414                // Handle fuel.
415                if self.tunables.consume_fuel {
416                    self.fuel_before_visit_op(operator)?;
417                }
418                Ok(())
419            }
420
421            fn after_visit_op(&mut self) -> Result<()> {
422                // Handle source code location mapping.
423                self.source_location_after_visit_op()
424            }
425        }
426
427        impl<'a, T, U> VisitOperator<'a> for ValidateThenVisit<'_, T, U>
428        where
429            T: VisitSimdOperator<'a, Output = wasmparser::Result<()>>,
430            U: VisitSimdOperator<'a, Output = Result<()>> + VisitorHooks,
431        {
432            type Output = U::Output;
433
434            fn simd_visitor(
435                &mut self,
436            ) -> Option<&mut dyn VisitSimdOperator<'a, Output = Self::Output>>
437            where
438                T:,
439            {
440                Some(self)
441            }
442
443            wasmparser::for_each_visit_operator!(validate_then_visit);
444        }
445
446        impl<'a, T, U> VisitSimdOperator<'a> for ValidateThenVisit<'_, T, U>
447        where
448            T: VisitSimdOperator<'a, Output = wasmparser::Result<()>>,
449            U: VisitSimdOperator<'a, Output = Result<()>> + VisitorHooks,
450        {
451            wasmparser::for_each_visit_simd_operator!(validate_then_visit);
452        }
453    }
454
455    /// Emits a a series of instructions that will type check a function reference call.
456    pub fn emit_typecheck_funcref(
457        &mut self,
458        funcref_ptr: Reg,
459        type_index: TypeIndex,
460    ) -> Result<()> {
461        let ptr_size: OperandSize = self.env.ptr_type().try_into()?;
462        let sig_index_bytes = self.env.vmoffsets.size_of_vmshared_type_index();
463        let sig_size = OperandSize::from_bytes(sig_index_bytes);
464        let sig_index = self.env.translation.module.types[type_index].unwrap_module_type_index();
465        let sig_offset = sig_index
466            .as_u32()
467            .checked_mul(sig_index_bytes.into())
468            .unwrap();
469        let signatures_base_offset = self.env.vmoffsets.ptr.vmctx_type_ids_array();
470        let funcref_sig_offset = self.env.vmoffsets.ptr.vm_func_ref_type_index();
471        // Get the caller id.
472        let caller_id = self.context.any_gpr(self.masm)?;
473
474        self.masm.with_scratch::<IntScratch, _>(|masm, scratch| {
475            // Load the signatures address into the scratch register.
476            masm.load(
477                masm.address_at_vmctx(signatures_base_offset.into())?,
478                scratch.writable(),
479                ptr_size,
480            )?;
481
482            masm.load(
483                masm.address_at_reg(scratch.inner(), sig_offset)?,
484                writable!(caller_id),
485                sig_size,
486            )
487        })?;
488
489        let callee_id = self.context.any_gpr(self.masm)?;
490        self.masm.load(
491            self.masm
492                .address_at_reg(funcref_ptr, funcref_sig_offset.into())?,
493            writable!(callee_id),
494            sig_size,
495        )?;
496
497        // Typecheck.
498        self.masm
499            .cmp(caller_id, callee_id.into(), OperandSize::S32)?;
500        self.masm.trapif(IntCmpKind::Ne, TRAP_BAD_SIGNATURE)?;
501        self.context.free_reg(callee_id);
502        self.context.free_reg(caller_id);
503        wasmtime_environ::error::Ok(())
504    }
505
506    /// Emit the usual function end instruction sequence.
507    fn emit_end(&mut self) -> Result<()> {
508        // The implicit body block is treated a normal block (it pushes results
509        // to the stack); so when reaching the end, we pop them taking as
510        // reference the current function's signature.
511        let base = SPOffset::from_u32(self.context.frame.locals_size);
512        self.masm.start_source_loc(Default::default())?;
513        if self.context.reachable {
514            ControlStackFrame::pop_abi_results_impl(
515                &mut self.sig.results,
516                &mut self.context,
517                self.masm,
518                |results, _, _| Ok(results.ret_area().copied()),
519            )?;
520        } else {
521            // If we reach the end of the function in an unreachable code state,
522            // simply truncate to the expected values.
523            // The compiler could enter this state through an infinite loop.
524            self.context.truncate_stack_to(0)?;
525            self.masm.reset_stack_pointer(base)?;
526        }
527        ensure!(
528            self.context.stack.len() == 0,
529            CodeGenError::unexpected_value_in_value_stack()
530        );
531        self.masm.free_stack(self.context.frame.locals_size)?;
532        self.masm.epilogue()?;
533        self.masm.end_source_loc()?;
534        Ok(())
535    }
536
537    /// Pops the value at the stack top and assigns it to the local at
538    /// the given index, returning the typed register holding the
539    /// source value.
540    pub fn emit_set_local(&mut self, index: u32) -> Result<TypedReg> {
541        // Materialize any references to the same local index that are in the
542        // value stack by spilling.
543        if self.context.stack.contains_latent_local(index) {
544            self.context.spill(self.masm)?;
545        }
546        let src = self.context.pop_to_reg(self.masm, None)?;
547        // Need to get address of local after `pop_to_reg` since `pop_to_reg`
548        // will pop the machine stack causing an incorrect address to be
549        // calculated.
550        let (ty, addr) = self.context.frame.get_local_address(index, self.masm)?;
551        self.masm
552            .store(RegImm::reg(src.reg), addr, ty.try_into()?)?;
553
554        Ok(src)
555    }
556
557    /// Loads the address of the given global.
558    pub fn emit_get_global_addr(&mut self, index: GlobalIndex) -> Result<(WasmValType, Reg, u32)> {
559        let data = self.env.resolve_global(index);
560
561        if data.imported {
562            let global_base = self.masm.address_at_reg(vmctx!(M), data.offset)?;
563            let dst = self.context.any_gpr(self.masm)?;
564            self.masm.load_ptr(global_base, writable!(dst))?;
565            Ok((data.ty, dst, 0))
566        } else {
567            Ok((data.ty, vmctx!(M), data.offset))
568        }
569    }
570
571    pub fn emit_table_get(&mut self, table_index: TableIndex) -> Result<()> {
572        let table = self.env.table(table_index);
573        let heap_type = table.ref_type.heap_type;
574        ensure!(
575            heap_type == WasmHeapType::Func,
576            CodeGenError::unsupported_wasm_type()
577        );
578        ensure!(
579            self.tunables.table_lazy_init,
580            CodeGenError::unsupported_table_eager_init()
581        );
582        let table_data = self.env.resolve_table_data(table_index);
583        let ptr_type = self.env.ptr_type();
584        let builtin = self.env.builtins.table_get_lazy_init_func_ref::<M::ABI>()?;
585
586        // Request the builtin's result register and use it to hold the table
587        // element value. We preemptively spill and request this register to
588        // avoid conflict at the control flow merge below. Requesting the result
589        // register is safe since we know ahead-of-time the builtin's signature.
590        self.context.spill(self.masm)?;
591        let elem_value: Reg = self.context.reg(
592            builtin.sig().results.unwrap_singleton().unwrap_reg(),
593            self.masm,
594        )?;
595
596        let index = self.context.pop_to_reg(self.masm, None)?;
597        let base = self.context.any_gpr(self.masm)?;
598
599        let elem_addr = self.emit_compute_table_elem_addr(index.into(), base, &table_data)?;
600        self.masm.load_ptr(elem_addr, writable!(elem_value))?;
601        // Free the register used as base, once we have loaded the element
602        // address into the element value register.
603        self.context.free_reg(base);
604
605        let (defined, cont) = (self.masm.get_label()?, self.masm.get_label()?);
606
607        // Push the built-in arguments to the stack.
608        self.context
609            .stack
610            .extend([table_index.as_u32().try_into().unwrap(), index.into()]);
611
612        self.masm.branch(
613            IntCmpKind::Ne,
614            elem_value,
615            elem_value.into(),
616            defined,
617            ptr_type.try_into()?,
618        )?;
619        // Free the element value register.
620        // This is safe since the FnCall::emit call below, will ensure
621        // that the result register is placed on the value stack.
622        self.context.free_reg(elem_value);
623        FnCall::emit::<M>(
624            &mut self.env,
625            self.masm,
626            &mut self.context,
627            Callee::Builtin(builtin.clone()),
628        )?;
629
630        // We know the signature of the libcall in this case, so we assert that there's
631        // one element in the stack and that it's  the ABI signature's result register.
632        let top = self
633            .context
634            .stack
635            .peek()
636            .ok_or_else(|| CodeGenError::missing_values_in_stack())?;
637        let top = top.unwrap_reg();
638        ensure!(
639            top.reg == elem_value,
640            CodeGenError::table_element_value_expected()
641        );
642        self.masm.jmp(cont)?;
643
644        // In the defined case, mask the funcref address in place, by peeking into the
645        // last element of the value stack, which was pushed by the `indirect` function
646        // call above.
647        //
648        // Note that `FUNCREF_MASK` as type `usize` but here we want a 64-bit
649        // value so assert its actual value and then use a `-2` literal.
650        self.masm.bind(defined)?;
651        assert_eq!(FUNCREF_MASK as isize, -2);
652        let imm = RegImm::i64(-2);
653        let dst = top.into();
654        self.masm
655            .and(writable!(dst), dst, imm, top.ty.try_into()?)?;
656
657        self.masm.bind(cont)
658    }
659
660    /// Emit the `table.set` operation for a function-reference table.
661    ///
662    /// Expects the value stack to contain `[index, value]` (with `value` on
663    /// top) and consumes both.
664    pub fn emit_table_set(&mut self, table_index: TableIndex) -> Result<()> {
665        let table = self.env.table(table_index);
666        ensure!(
667            table.ref_type.heap_type == WasmHeapType::Func,
668            CodeGenError::unsupported_wasm_type()
669        );
670        ensure!(
671            self.tunables.table_lazy_init,
672            CodeGenError::unsupported_table_eager_init()
673        );
674        let ptr_type = self.env.ptr_type();
675        let table_data = self.env.resolve_table_data(table_index);
676        let value = self.context.pop_to_reg(self.masm, None)?;
677        let index = self.context.pop_to_reg(self.masm, None)?;
678        let base = self.context.any_gpr(self.masm)?;
679        let elem_addr = self.emit_compute_table_elem_addr(index.into(), base, &table_data)?;
680        // Set the initialized bit.
681        self.masm.or(
682            writable!(value.into()),
683            value.into(),
684            RegImm::i64(FUNCREF_INIT_BIT as i64),
685            ptr_type.try_into()?,
686        )?;
687
688        self.masm.store_ptr(value.into(), elem_addr)?;
689
690        self.context.free_reg(value);
691        self.context.free_reg(index);
692        self.context.free_reg(base);
693        Ok(())
694    }
695
696    /// Emit the `table.grow` operation.
697    pub fn emit_table_grow(&mut self, table_index: TableIndex) -> Result<()> {
698        let ptr_type = self.env.ptr_type();
699        let idx_type = self.env.table(table_index).idx_type;
700
701        // Duplicate the `delta` argument on the stack since we'll need it at
702        // the end if growth succeeds.
703        let delta = self.context.pop_to_reg(self.masm, None)?;
704        let tmp = self.context.any_gpr(self.masm)?;
705        self.masm
706            .mov(writable!(tmp), delta.reg.into(), delta.ty.try_into()?)?;
707        self.context.stack.push(TypedReg::new(delta.ty, tmp).into());
708        self.context.stack.push(delta.into());
709
710        // Invoke the `table.grow` builtin on the host which will return whether
711        // the growth succeeded, and if so where it's located.
712        let at = self.context.stack.ensure_index_at(1)?;
713        let builtin = self.env.builtins.table_grow::<M::ABI>()?;
714        let builtin = self.prepare_builtin_defined_table_arg(table_index, at, builtin)?;
715        FnCall::emit::<M>(&mut self.env, self.masm, &mut self.context, builtin)?;
716
717        // Pop everything that's on the stack now. The builtin took `delta` and
718        // pushed a result, and then peel off our duplicate of `delta` plus the
719        // initialization element of `table.grow` itself.
720        let result = self.context.pop_to_reg(self.masm, None)?;
721        let len = self.context.pop_to_reg(self.masm, None)?;
722        let init = self.context.pop_to_reg(self.masm, None)?;
723
724        // Save a copy of `result` on the stack since we'll need it after
725        // `table.fill` is done.
726        let tmp_result = self.context.any_gpr(self.masm)?;
727        self.masm.mov(
728            writable!(tmp_result),
729            result.reg.into(),
730            result.ty.try_into()?,
731        )?;
732        self.context
733            .stack
734            .push(TypedReg::new(result.ty, tmp_result).into());
735
736        // Test if the result of growth is -1. If it is, then we're done.
737        // Otherwise fall through to `table.fill`.
738        let done = self.masm.get_label()?;
739        self.masm.branch(
740            IntCmpKind::Eq,
741            result.reg,
742            RegImm::i64(-1),
743            done,
744            OperandSize::S64,
745        )?;
746
747        // Prepare the arguments for `table.fill` in the order the wasm
748        // instruction expects.
749        self.context.stack.push(result.into());
750        self.context.stack.push(init.into());
751        self.context.stack.push(len.into());
752        self.emit_table_fill(table_index)?;
753
754        self.masm.bind(done)?;
755
756        // Similar to the memory.grow builtin, `table.grow` returns a
757        // pointer, however, we need to ensure that the returned index
758        // is representative of the address space for tables.
759        match (ptr_type, idx_type) {
760            (WasmValType::I64, IndexType::I64) => Ok(()),
761            (WasmValType::I64, IndexType::I32) => {
762                let top: Reg = self.context.pop_to_reg(self.masm, None)?.into();
763                self.masm.wrap(writable!(top), top)?;
764                self.context.stack.push(TypedReg::i32(top).into());
765                Ok(())
766            }
767
768            _ => Err(format_err!(CodeGenError::unsupported_32_bit_platform())),
769        }
770    }
771
772    /// Emit the `table.fill` operation.
773    pub fn emit_table_fill(&mut self, table_index: TableIndex) -> Result<()> {
774        // Put all of this opcode's arguments into registers.
775        let len = self.context.pop_to_reg(self.masm, None)?;
776        let init = self.context.pop_to_reg(self.masm, None)?;
777        let offset = self.context.pop_to_reg(self.masm, None)?;
778
779        // Perform a bounds check to see if `offset+len` is inbounds.
780        let table_data = self.env.resolve_table_data(table_index);
781        self.emit_compute_table_size(&table_data)?;
782        let table_size = self.context.pop_to_reg(self.masm, None)?;
783        let tmp = self.context.any_gpr(self.masm)?;
784        let idx_size = table_data.index_type().try_into()?;
785        self.masm.mov(writable!(tmp), offset.reg.into(), idx_size)?;
786        self.masm.checked_uadd(
787            writable!(tmp),
788            tmp,
789            len.reg.into(),
790            idx_size,
791            TRAP_TABLE_OUT_OF_BOUNDS,
792        )?;
793        self.masm.cmp(tmp, table_size.reg.into(), idx_size)?;
794        self.masm
795            .trapif(IntCmpKind::GtU, TRAP_TABLE_OUT_OF_BOUNDS)?;
796        self.context.free_reg(tmp);
797        self.context.free_reg(table_size);
798
799        let header = self.masm.get_label()?;
800        let exit = self.masm.get_label()?;
801
802        self.masm.bind(header)?;
803
804        // Exit the loop once there are no more elements to copy.
805        self.masm.branch(
806            IntCmpKind::Eq,
807            len.reg,
808            RegImm::i64(0),
809            exit,
810            OperandSize::S64,
811        )?;
812
813        // Duplicate `offset`, where we're writing, and `init` what we're
814        // writing, into temporary registers. These are used by `emit_table_set`
815        // below.
816        let tmp_index = self.context.any_gpr(self.masm)?;
817        let tmp_init = self.context.any_gpr(self.masm)?;
818        self.masm
819            .mov(writable!(tmp_index), offset.reg.into(), OperandSize::S64)?;
820        self.masm
821            .mov(writable!(tmp_init), init.reg.into(), OperandSize::S64)?;
822
823        // Spill all this loop's variables onto the stack.
824        self.context.stack.push(TypedReg::i64(len.reg).into());
825        self.context.stack.push(TypedReg::i64(init.reg).into());
826        self.context.stack.push(TypedReg::i64(offset.reg).into());
827
828        // Emit `table.set`, consuming our temporary registers.
829        self.context.stack.push(TypedReg::i64(tmp_index).into());
830        self.context.stack.push(TypedReg::i64(tmp_init).into());
831        self.emit_table_set(table_index)?;
832
833        // Reload this loop's variables into the same registers as the start of
834        // the loop.
835        self.context.pop_to_reg(self.masm, Some(offset.reg))?;
836        self.context.pop_to_reg(self.masm, Some(init.reg))?;
837        self.context.pop_to_reg(self.masm, Some(len.reg))?;
838
839        // Advance the destination we're writing to, and decrement the number of
840        // elements left to write.
841        self.masm.add(
842            writable!(offset.reg),
843            offset.reg,
844            RegImm::i64(1),
845            OperandSize::S64,
846        )?;
847        self.masm.sub(
848            writable!(len.reg),
849            len.reg,
850            RegImm::i64(1),
851            OperandSize::S64,
852        )?;
853        self.masm.jmp(header)?;
854
855        self.masm.bind(exit)?;
856
857        self.context.free_reg(offset);
858        self.context.free_reg(init);
859        self.context.free_reg(len);
860        Ok(())
861    }
862
863    /// Emits a bounds check for the range `[idx, idx + len)` against the
864    /// current size of `table_data`, trapping with `TRAP_TABLE_OUT_OF_BOUNDS`
865    /// if the range is out-of-bounds.
866    ///
867    /// Both `idx` and `len` are expected to be 64-bit values.
868    fn emit_table_range_bounds_check(
869        &mut self,
870        table_data: &TableData,
871        idx: Reg,
872        len: Reg,
873    ) -> Result<()> {
874        self.emit_compute_table_size(table_data)?;
875        let size = self.context.pop_to_reg(self.masm, None)?;
876
877        // Compute `end = idx + len`, trapping on overflow, and then trap if
878        // `end > size`.
879        let end = self.context.any_gpr(self.masm)?;
880        self.masm
881            .mov(writable!(end), idx.into(), OperandSize::S64)?;
882        self.masm.checked_uadd(
883            writable!(end),
884            end,
885            len.into(),
886            OperandSize::S64,
887            TRAP_TABLE_OUT_OF_BOUNDS,
888        )?;
889        self.masm.cmp(end, size.reg.into(), OperandSize::S64)?;
890        self.masm
891            .trapif(IntCmpKind::GtU, TRAP_TABLE_OUT_OF_BOUNDS)?;
892
893        self.context.free_reg(size);
894        self.context.free_reg(end);
895        Ok(())
896    }
897
898    /// Emit the `table.copy` operation.
899    pub fn emit_table_copy(&mut self, dst_table: TableIndex, src_table: TableIndex) -> Result<()> {
900        let dst_data = self.env.resolve_table_data(dst_table);
901        let src_data = self.env.resolve_table_data(src_table);
902
903        // The value stack contains `[dst, src, len]` (top is `len`).
904        let len = self.context.pop_to_reg(self.masm, None)?;
905        let src = self.context.pop_to_reg(self.masm, None)?;
906        let dst = self.context.pop_to_reg(self.masm, None)?;
907
908        // Zero-extend each operand to a full 64-bit value so that the
909        // arithmetic and bounds checks below can uniformly operate on 64-bit
910        // quantities regardless of the table's index type.
911        for op in [&len, &src, &dst] {
912            if op.ty == WasmValType::I32 {
913                self.masm.extend(
914                    writable!(op.reg),
915                    op.reg,
916                    Extend::<Zero>::I64Extend32.into(),
917                )?;
918            }
919        }
920
921        // Bounds check both ranges up-front; `table.copy` traps without
922        // copying anything if either range is out-of-bounds.
923        self.emit_table_range_bounds_check(&src_data, src.reg, len.reg)?;
924        self.emit_table_range_bounds_check(&dst_data, dst.reg, len.reg)?;
925
926        // Decide the copy direction. If `dst <= src` then do a forwards copy
927        // and otherwise it's backwards.
928        let step = self.context.any_gpr(self.masm)?;
929        let forward = self.masm.get_label()?;
930        let setup_done = self.masm.get_label()?;
931        self.masm.branch(
932            IntCmpKind::LeU,
933            dst.reg,
934            src.reg.into(),
935            forward,
936            OperandSize::S64,
937        )?;
938        // Backwards: start at the last element and walk down.
939        {
940            self.masm
941                .mov(writable!(step), RegImm::i64(-1), OperandSize::S64)?;
942            self.masm.add(
943                writable!(src.reg),
944                src.reg,
945                len.reg.into(),
946                OperandSize::S64,
947            )?;
948            self.masm.sub(
949                writable!(src.reg),
950                src.reg,
951                RegImm::i64(1),
952                OperandSize::S64,
953            )?;
954            self.masm.add(
955                writable!(dst.reg),
956                dst.reg,
957                len.reg.into(),
958                OperandSize::S64,
959            )?;
960            self.masm.sub(
961                writable!(dst.reg),
962                dst.reg,
963                RegImm::i64(1),
964                OperandSize::S64,
965            )?;
966        }
967        self.masm.jmp(setup_done)?;
968        // Forwards: start at the first element and walk up.
969        self.masm.bind(forward)?;
970        {
971            self.masm
972                .mov(writable!(step), RegImm::i64(1), OperandSize::S64)?;
973        }
974
975        self.masm.bind(setup_done)?;
976
977        let header = self.masm.get_label()?;
978        let exit = self.masm.get_label()?;
979
980        self.masm.bind(header)?;
981
982        // Exit the loop once there are no more elements to copy.
983        self.masm.branch(
984            IntCmpKind::Eq,
985            len.reg,
986            RegImm::i64(0),
987            exit,
988            OperandSize::S64,
989        )?;
990
991        // Spill all loop variables to the stack for the body of the loop.
992        // These will get reloaded back into the same registers at the end of
993        // the loop.
994        self.context.stack.push(TypedReg::i64(step).into());
995        self.context.stack.push(TypedReg::i64(len.reg).into());
996        self.context.stack.push(TypedReg::i64(dst.reg).into());
997        self.context.stack.push(TypedReg::i64(src.reg).into());
998
999        // Do a `table.get` followed by a `table.set`. Note that this'll redo
1000        // bounds checks which technically aren't necessary, but it's less code
1001        // duplication/complexity in Winch.
1002        //
1003        // Note that `dst` and `src` are on the stack and are needed for these
1004        // operations. They're also needed at the end of the loop, so some
1005        // stack-shuffling is necessary to "dup" the right values and get
1006        // everything in the expected shapes for `emit_table_{get,set}`.
1007        {
1008            let tmp_src = self.context.pop_to_reg(self.masm, None)?;
1009            let s = self.context.any_gpr(self.masm)?;
1010            self.masm
1011                .mov(writable!(s), tmp_src.reg.into(), OperandSize::S64)?;
1012            self.context.stack.push(tmp_src.into());
1013            self.context.stack.push(TypedReg::i64(s).into());
1014            self.emit_table_get(src_table)?;
1015            let funcref = self.context.pop_to_reg(self.masm, None)?;
1016
1017            let tmp_src = self.context.pop_to_reg(self.masm, None)?;
1018            let tmp_dst = self.context.pop_to_reg(self.masm, None)?;
1019
1020            let d = self.context.any_gpr(self.masm)?;
1021            self.masm
1022                .mov(writable!(d), tmp_dst.reg.into(), OperandSize::S64)?;
1023            self.context.stack.push(tmp_dst.into());
1024            self.context.stack.push(tmp_src.into());
1025            self.context.stack.push(TypedReg::i64(d).into());
1026            self.context.stack.push(funcref.into());
1027            self.emit_table_set(dst_table)?;
1028        }
1029
1030        // Reload loop variables specifically back into the same registers to
1031        // ensure that modifications below are picked up on the next iteration.
1032        self.context.pop_to_reg(self.masm, Some(src.reg))?;
1033        self.context.pop_to_reg(self.masm, Some(dst.reg))?;
1034        self.context.pop_to_reg(self.masm, Some(len.reg))?;
1035        self.context.pop_to_reg(self.masm, Some(step))?;
1036
1037        // Advance the running indices and decrement the remaining count.
1038        self.masm
1039            .add(writable!(dst.reg), dst.reg, step.into(), OperandSize::S64)?;
1040        self.masm
1041            .add(writable!(src.reg), src.reg, step.into(), OperandSize::S64)?;
1042        self.masm.sub(
1043            writable!(len.reg),
1044            len.reg,
1045            RegImm::i64(1),
1046            OperandSize::S64,
1047        )?;
1048
1049        self.masm.jmp(header)?;
1050
1051        self.masm.bind(exit)?;
1052
1053        self.context.free_reg(src);
1054        self.context.free_reg(dst);
1055        self.context.free_reg(len);
1056        self.context.free_reg(step);
1057        Ok(())
1058    }
1059
1060    /// Emits a series of instructions to bounds check and calculate the address
1061    /// of the given WebAssembly memory.
1062    /// This function returns a register containing the requested address.
1063    ///
1064    /// In essence, when computing the heap address for a WebAssembly load or
1065    /// store instruction the objective is to ensure that such access is safe,
1066    /// but also to perform the least amount of checks, and rely on the system to
1067    /// detect illegal memory accesses where applicable.
1068    ///
1069    /// Winch follows almost the same principles as Cranelift when it comes to
1070    /// bounds checks, for a more detailed explanation refer to
1071    /// prepare_addr in wasmtime-cranelift.
1072    ///
1073    /// Winch implementation differs in that, it defaults to the general case
1074    /// for dynamic heaps rather than optimizing for doing the least amount of
1075    /// work possible at runtime, this is done to align with Winch's principle
1076    /// of doing the least amount of work possible at compile time. For static
1077    /// heaps, Winch does a bit more of work, given that some of the cases that
1078    /// are checked against, can benefit compilation times, like for example,
1079    /// detecting an out of bounds access at compile time.
1080    pub fn emit_compute_heap_address(
1081        &mut self,
1082        heap: &HeapData,
1083        memarg: &MemArg,
1084        access_size: OperandSize,
1085    ) -> Result<Option<Reg>> {
1086        let ptr_size: OperandSize = self.env.ptr_type().try_into()?;
1087        let enable_spectre_mitigation = self.env.heap_access_spectre_mitigation();
1088        let add_offset_and_access_size = |offset: ImmOffset, access_size: OperandSize| {
1089            (access_size.bytes() as u64) + (offset.as_u32() as u64)
1090        };
1091
1092        let index = Index::from_typed_reg(self.context.pop_to_reg(self.masm, None)?);
1093
1094        let offset = bounds::ensure_index_and_offset(
1095            self.masm,
1096            index,
1097            memarg.offset,
1098            heap.index_type().try_into()?,
1099        )?;
1100        let offset_with_access_size = add_offset_and_access_size(offset, access_size);
1101
1102        let memory_tunables = MemoryTunables::new(self.tunables, MemoryKind::LinearMemory);
1103        let can_elide_bounds_check = heap
1104            .memory
1105            .can_elide_bounds_check(&memory_tunables, self.env.page_size_log2);
1106
1107        let addr = if offset_with_access_size > heap.memory.maximum_byte_size().unwrap_or(u64::MAX)
1108            || (!self.tunables.memory_may_move
1109                && offset_with_access_size > self.tunables.memory_reservation)
1110        {
1111            // Detect at compile time if the access is out of bounds.
1112            // Doing so will put the compiler in an unreachable code state,
1113            // optimizing the work that the compiler has to do until the
1114            // reachability is restored or when reaching the end of the
1115            // function.
1116
1117            self.emit_fuel_increment()?;
1118            self.masm.trap(TrapCode::HEAP_OUT_OF_BOUNDS)?;
1119            self.context.reachable = false;
1120            None
1121
1122        // Account for the case in which we can completely elide the bounds
1123        // checks.
1124        //
1125        // This case, makes use of the fact that if a memory access uses
1126        // a 32-bit index, then we be certain that
1127        //
1128        //      index <= u32::MAX
1129        //
1130        // Therefore if any 32-bit index access occurs in the region
1131        // represented by
1132        //
1133        //      bound + guard_size - (offset + access_size)
1134        //
1135        // We are certain that it's in bounds or that the underlying virtual
1136        // memory subsystem will report an illegal access at runtime.
1137        //
1138        // Note:
1139        //
1140        // * bound - (offset + access_size) cannot wrap, because it's checked
1141        // in the condition above.
1142        // * bound + heap.offset_guard_size is guaranteed to not overflow if
1143        // the heap configuration is correct, given that it's address must
1144        // fit in 64-bits.
1145        // * If the heap type is 32-bits, the offset is at most u32::MAX, so
1146        // no  adjustment is needed as part of
1147        // [bounds::ensure_index_and_offset].
1148        } else if can_elide_bounds_check
1149            && u64::from(u32::MAX)
1150                <= self.tunables.memory_reservation + self.tunables.memory_guard_size
1151                    - offset_with_access_size
1152        {
1153            assert!(can_elide_bounds_check);
1154            assert!(heap.index_type() == WasmValType::I32);
1155            let addr = self.context.any_gpr(self.masm)?;
1156            bounds::load_heap_addr_unchecked(self.masm, &heap, index, offset, addr, ptr_size)?;
1157            Some(addr)
1158
1159        // Account for the case of a static memory size. The access is out
1160        // of bounds if:
1161        //
1162        // index > bound - (offset + access_size)
1163        //
1164        // bound - (offset + access_size) cannot wrap, because we already
1165        // checked that (offset + access_size) > bound, above.
1166        } else if let Some(static_size) = heap.memory.static_heap_size() {
1167            let bounds = Bounds::from_u64(static_size);
1168            let addr = bounds::load_heap_addr_checked(
1169                self.masm,
1170                &mut self.context,
1171                ptr_size,
1172                &heap,
1173                enable_spectre_mitigation,
1174                bounds,
1175                index,
1176                offset,
1177                |masm, bounds, index| {
1178                    let adjusted_bounds = bounds.as_u64() - offset_with_access_size;
1179                    let index_reg = index.as_typed_reg().reg;
1180                    masm.cmp(
1181                        index_reg,
1182                        RegImm::i64(adjusted_bounds as i64),
1183                        // Similar to the dynamic heap case, even though the
1184                        // offset and access size are bound through the heap
1185                        // type, when added they can overflow, resulting in
1186                        // an erroneous comparison, therefore we rely on the
1187                        // target pointer size.
1188                        ptr_size,
1189                    )?;
1190                    Ok(IntCmpKind::GtU)
1191                },
1192            )?;
1193            Some(addr)
1194        } else {
1195            // Account for the general case for bounds-checked memories. The
1196            // access is out of bounds if:
1197            // * index + offset + access_size overflows
1198            //   OR
1199            // * index + offset + access_size > bound
1200            let bounds = bounds::load_dynamic_heap_bounds::<_>(
1201                &mut self.context,
1202                self.masm,
1203                &heap,
1204                ptr_size,
1205            )?;
1206
1207            let index_reg = index.as_typed_reg().reg;
1208            // Allocate a temporary register to hold
1209            //      index + offset + access_size
1210            //  which will serve as the check condition.
1211            let index_offset_and_access_size = self.context.any_gpr(self.masm)?;
1212
1213            // Move the value of the index to the
1214            // index_offset_and_access_size register to perform the overflow
1215            // check to avoid clobbering the initial index value.
1216            //
1217            // We derive size of the operation from the heap type since:
1218            //
1219            // * This is the first assignment to the
1220            // `index_offset_and_access_size` register
1221            //
1222            // * The memory64 proposal specifies that the index is bound to
1223            // the heap type instead of hardcoding it to 32-bits (i32).
1224            self.masm.mov(
1225                writable!(index_offset_and_access_size),
1226                index_reg.into(),
1227                heap.index_type().try_into()?,
1228            )?;
1229            // Perform
1230            // index = index + offset + access_size, trapping if the
1231            // addition overflows.
1232            //
1233            // We use the target's pointer size rather than depending on the heap
1234            // type since we want to check for overflow; even though the
1235            // offset and access size are guaranteed to be bounded by the heap
1236            // type, when added, if used with the wrong operand size, their
1237            // result could be clamped, resulting in an erroneous overflow
1238            // check.
1239            self.masm.checked_uadd(
1240                writable!(index_offset_and_access_size),
1241                index_offset_and_access_size,
1242                RegImm::i64(offset_with_access_size as i64),
1243                ptr_size,
1244                TrapCode::HEAP_OUT_OF_BOUNDS,
1245            )?;
1246
1247            let addr = bounds::load_heap_addr_checked(
1248                self.masm,
1249                &mut self.context,
1250                ptr_size,
1251                &heap,
1252                enable_spectre_mitigation,
1253                bounds,
1254                index,
1255                offset,
1256                |masm, bounds, _| {
1257                    let bounds_reg = bounds.as_typed_reg().reg;
1258                    masm.cmp(
1259                        index_offset_and_access_size,
1260                        bounds_reg.into(),
1261                        // We use the pointer size to keep the bounds
1262                        // comparison consistent with the result of the
1263                        // overflow check above.
1264                        ptr_size,
1265                    )?;
1266                    Ok(IntCmpKind::GtU)
1267                },
1268            )?;
1269            self.context.free_reg(bounds.as_typed_reg().reg);
1270            self.context.free_reg(index_offset_and_access_size);
1271            Some(addr)
1272        };
1273
1274        self.context.free_reg(index.as_typed_reg().reg);
1275        Ok(addr)
1276    }
1277
1278    /// Emit checks to ensure that the address at `memarg` is
1279    /// correctly aligned for the access size.
1280    fn emit_check_align(
1281        &mut self,
1282        heap: &HeapData,
1283        memarg: &MemArg,
1284        access_size: OperandSize,
1285    ) -> Result<()> {
1286        if access_size.bytes() > 1 {
1287            let heap_ty_size: OperandSize = heap.index_type().try_into()?;
1288            let addr = *self
1289                .context
1290                .stack
1291                .peek()
1292                .ok_or_else(|| CodeGenError::missing_values_in_stack())?;
1293            let tmp = self.context.any_gpr(self.masm)?;
1294            self.context.move_val_to_reg(&addr, tmp, self.masm)?;
1295
1296            if memarg.offset != 0 {
1297                self.masm.add(
1298                    writable!(tmp),
1299                    tmp,
1300                    RegImm::Imm(Imm::I64(memarg.offset)),
1301                    heap_ty_size,
1302                )?;
1303            }
1304
1305            self.masm.and(
1306                writable!(tmp),
1307                tmp,
1308                RegImm::Imm(Imm::I32(access_size.bytes() - 1)),
1309                heap_ty_size,
1310            )?;
1311
1312            self.masm.cmp(tmp, RegImm::Imm(Imm::i64(0)), heap_ty_size)?;
1313            self.masm.trapif(IntCmpKind::Ne, TRAP_HEAP_MISALIGNED)?;
1314            self.context.free_reg(tmp);
1315        }
1316
1317        Ok(())
1318    }
1319
1320    pub fn emit_compute_heap_address_align_checked(
1321        &mut self,
1322        heap: &HeapData,
1323        memarg: &MemArg,
1324        access_size: OperandSize,
1325    ) -> Result<Option<Reg>> {
1326        self.emit_check_align(heap, memarg, access_size)?;
1327        self.emit_compute_heap_address(heap, memarg, access_size)
1328    }
1329
1330    /// Emit a WebAssembly load.
1331    pub fn emit_wasm_load(
1332        &mut self,
1333        arg: &MemArg,
1334        target_type: WasmValType,
1335        kind: LoadKind,
1336    ) -> Result<()> {
1337        let emit_load = |this: &mut Self, dst, addr, kind| -> Result<()> {
1338            let src = this.masm.address_at_reg(addr, 0)?;
1339            this.masm.wasm_load(src, writable!(dst), kind)?;
1340            this.context
1341                .stack
1342                .push(TypedReg::new(target_type, dst).into());
1343            this.context.free_reg(addr);
1344            Ok(())
1345        };
1346
1347        let memory_index = MemoryIndex::from_u32(arg.memory);
1348        let heap = self.env.resolve_heap(memory_index);
1349
1350        // Ensure that the destination register is not allocated if
1351        // `emit_compute_heap_address` does not return an address.
1352        match kind {
1353            LoadKind::VectorLane(_) => {
1354                // Destination vector register is at the top of the stack and
1355                // `emit_compute_heap_address` expects an integer register
1356                // containing the address to load to be at the top of the stack.
1357                let dst = self.context.pop_to_reg(self.masm, None)?;
1358                let addr =
1359                    self.emit_compute_heap_address(&heap, &arg, kind.derive_operand_size())?;
1360                if let Some(addr) = addr {
1361                    emit_load(self, dst.reg, addr, kind)?;
1362                } else {
1363                    self.context.free_reg(dst);
1364                }
1365            }
1366            _ => {
1367                let maybe_addr = match kind {
1368                    LoadKind::Atomic(_, _) => self.emit_compute_heap_address_align_checked(
1369                        &heap,
1370                        &arg,
1371                        kind.derive_operand_size(),
1372                    )?,
1373                    _ => self.emit_compute_heap_address(&heap, &arg, kind.derive_operand_size())?,
1374                };
1375
1376                if let Some(addr) = maybe_addr {
1377                    let dst = match target_type {
1378                        WasmValType::I32 | WasmValType::I64 => self.context.any_gpr(self.masm)?,
1379                        WasmValType::F32 | WasmValType::F64 => self.context.any_fpr(self.masm)?,
1380                        WasmValType::V128 => self.context.reg_for_type(target_type, self.masm)?,
1381                        _ => bail!(CodeGenError::unsupported_wasm_type()),
1382                    };
1383
1384                    emit_load(self, dst, addr, kind)?;
1385                }
1386            }
1387        }
1388
1389        Ok(())
1390    }
1391
1392    /// Emit a WebAssembly store.
1393    pub fn emit_wasm_store(&mut self, arg: &MemArg, kind: StoreKind) -> Result<()> {
1394        let memory_index = MemoryIndex::from_u32(arg.memory);
1395        let heap = self.env.resolve_heap(memory_index);
1396        let src = self.context.pop_to_reg(self.masm, None)?;
1397
1398        let maybe_addr = match kind {
1399            StoreKind::Atomic(size) => {
1400                self.emit_compute_heap_address_align_checked(&heap, &arg, size)?
1401            }
1402            StoreKind::Operand(size) | StoreKind::VectorLane(LaneSelector { size, .. }) => {
1403                self.emit_compute_heap_address(&heap, &arg, size)?
1404            }
1405        };
1406
1407        if let Some(addr) = maybe_addr {
1408            self.masm
1409                .wasm_store(src.reg, self.masm.address_at_reg(addr, 0)?, kind)?;
1410
1411            self.context.free_reg(addr);
1412        }
1413        self.context.free_reg(src);
1414
1415        Ok(())
1416    }
1417
1418    /// Loads the address of the table element at a given index. Returns the
1419    /// address of the table element using the provided register as base.
1420    pub fn emit_compute_table_elem_addr(
1421        &mut self,
1422        index: Reg,
1423        base: Reg,
1424        table_data: &TableData,
1425    ) -> Result<M::Address> {
1426        let bound = self.context.any_gpr(self.masm)?;
1427        let tmp = self.context.any_gpr(self.masm)?;
1428        let ptr_size: OperandSize = self.env.ptr_type().try_into()?;
1429
1430        if let Some(offset) = table_data.import_from {
1431            // If the table data declares a particular offset base,
1432            // load the address into a register to further use it as
1433            // the table address.
1434            self.masm
1435                .load_ptr(self.masm.address_at_vmctx(offset)?, writable!(base))?;
1436        } else {
1437            // Else, simply move the vmctx register into the addr register as
1438            // the base to calculate the table address.
1439            self.masm.mov(writable!(base), vmctx!(M).into(), ptr_size)?;
1440        };
1441
1442        // OOB check.
1443        let bound_addr = self
1444            .masm
1445            .address_at_reg(base, table_data.current_elems_offset)?;
1446        let bound_size = table_data.current_elements_size;
1447        self.masm.load(bound_addr, writable!(bound), bound_size)?;
1448        self.masm.cmp(index, bound.into(), bound_size)?;
1449        self.masm
1450            .trapif(IntCmpKind::GeU, TRAP_TABLE_OUT_OF_BOUNDS)?;
1451
1452        // Move the index into the scratch register to calculate the table
1453        // element address.
1454        // Moving the value of the index register to the scratch register
1455        // also avoids overwriting the context of the index register.
1456        self.masm.with_scratch::<IntScratch, _>(|masm, scratch| {
1457            masm.mov(scratch.writable(), index.into(), bound_size)?;
1458            masm.mul(
1459                scratch.writable(),
1460                scratch.inner(),
1461                RegImm::i32(table_data.element_size.bytes() as i32),
1462                table_data.element_size,
1463            )?;
1464            masm.load_ptr(
1465                masm.address_at_reg(base, table_data.offset)?,
1466                writable!(base),
1467            )?;
1468            // Copy the value of the table base into a temporary register
1469            // so that we can use it later in case of a misspeculation.
1470            masm.mov(writable!(tmp), base.into(), ptr_size)?;
1471            // Calculate the address of the table element.
1472            masm.add(writable!(base), base, scratch.inner().into(), ptr_size)
1473        })?;
1474        if self.env.table_access_spectre_mitigation() {
1475            // Perform a bounds check and override the value of the
1476            // table element address in case the index is out of bounds.
1477            self.masm.cmp(index, bound.into(), bound_size)?;
1478            self.masm
1479                .cmov(writable!(base), tmp, IntCmpKind::GeU, ptr_size)?;
1480        }
1481        self.context.free_reg(bound);
1482        self.context.free_reg(tmp);
1483        self.masm.address_at_reg(base, 0)
1484    }
1485
1486    /// Retrieves the size of the table, pushing the result to the value stack.
1487    pub fn emit_compute_table_size(&mut self, table_data: &TableData) -> Result<()> {
1488        let size = self.context.any_gpr(self.masm)?;
1489        let ptr_size: OperandSize = self.env.ptr_type().try_into()?;
1490
1491        self.masm.with_scratch::<IntScratch, _>(|masm, scratch| {
1492            if let Some(offset) = table_data.import_from {
1493                masm.load_ptr(masm.address_at_vmctx(offset)?, scratch.writable())?;
1494            } else {
1495                masm.mov(scratch.writable(), vmctx!(M).into(), ptr_size)?;
1496            };
1497
1498            let size_addr =
1499                masm.address_at_reg(scratch.inner(), table_data.current_elems_offset)?;
1500            masm.load(size_addr, writable!(size), table_data.current_elements_size)
1501        })?;
1502
1503        let dst = TypedReg::new(table_data.index_type(), size);
1504        self.context.stack.push(dst.into());
1505        Ok(())
1506    }
1507
1508    /// Retrieves the size of the memory, pushing the result to the value stack.
1509    fn load_memory_length(&mut self, heap_data: &HeapData, size_reg: Reg) -> Result<()> {
1510        self.masm.with_scratch::<IntScratch, _>(|masm, scratch| {
1511            let base = if let Some(offset) = heap_data.import_from {
1512                masm.load_ptr(masm.address_at_vmctx(offset)?, scratch.writable())?;
1513                scratch.inner()
1514            } else {
1515                vmctx!(M)
1516            };
1517
1518            let size_addr = masm.address_at_reg(base, heap_data.current_length_offset)?;
1519            masm.load_ptr(size_addr, writable!(size_reg))
1520        })?;
1521        Ok(())
1522    }
1523
1524    /// Retrieves the size of the memory, pushing the result to the value stack.
1525    pub fn emit_compute_memory_size(&mut self, heap_data: &HeapData) -> Result<()> {
1526        let size_reg = self.context.any_gpr(self.masm)?;
1527        self.load_memory_length(heap_data, size_reg)?;
1528
1529        // Emit a shift to get the size in pages rather than in bytes.
1530        let dst = TypedReg::new(heap_data.index_type(), size_reg);
1531        let pow = heap_data.memory.page_size_log2;
1532        self.masm.shift_ir(
1533            writable!(dst.reg),
1534            Imm::i32(pow as i32),
1535            dst.into(),
1536            ShiftKind::ShrU,
1537            self.env.ptr_type().try_into()?,
1538        )?;
1539        self.context.stack.push(dst.into());
1540        Ok(())
1541    }
1542
1543    /// Emit a bounds check for `ptr+len` and put the native address for this
1544    /// wasm address into `dst`.
1545    fn emit_bounds_check_and_compute_addr(
1546        &mut self,
1547        heap: &HeapData,
1548        dst: Reg,
1549        ptr: Reg,
1550        len: Reg,
1551    ) -> Result<()> {
1552        let ptr_size: OperandSize = self.env.ptr_type().try_into()?;
1553        let idx_size: OperandSize = heap.index_type().try_into()?;
1554        // Compute `dst = ptr + len` trapping on overflow. For an `i32` index
1555        // type the operands are zero-extended to 64-bit so overflow is
1556        // impossible.
1557        match idx_size {
1558            OperandSize::S32 => {
1559                self.masm
1560                    .extend(writable!(dst), ptr, Extend::<Zero>::I64Extend32.into())?;
1561                self.masm.add_uextend(
1562                    writable!(dst),
1563                    dst,
1564                    len,
1565                    OperandSize::S32,
1566                    OperandSize::S64,
1567                )?;
1568            }
1569            OperandSize::S64 => {
1570                self.masm
1571                    .mov(writable!(dst), ptr.into(), OperandSize::S64)?;
1572                self.masm.checked_uadd(
1573                    writable!(dst),
1574                    dst,
1575                    len.into(),
1576                    OperandSize::S64,
1577                    TrapCode::HEAP_OUT_OF_BOUNDS,
1578                )?;
1579            }
1580            _ => unreachable!(),
1581        }
1582
1583        // Load the current size in bytes of the memory, and trap if
1584        // `dst > size_in_bytes`.
1585        let size_in_bytes = self.context.any_gpr(self.masm)?;
1586        self.load_memory_length(&heap, size_in_bytes)?;
1587        assert!(ptr_size == OperandSize::S64);
1588        self.masm.cmp(dst, size_in_bytes.into(), ptr_size)?;
1589        self.masm
1590            .trapif(IntCmpKind::GtU, TrapCode::HEAP_OUT_OF_BOUNDS)?;
1591        self.context.free_reg(size_in_bytes);
1592
1593        // Compute `dst = memory_base + ptr`.
1594        bounds::load_heap_addr_unchecked(
1595            self.masm,
1596            &heap,
1597            Index::from_typed_reg(TypedReg::new(heap.index_type(), ptr)),
1598            ImmOffset::from_u32(0),
1599            dst,
1600            ptr_size,
1601        )?;
1602        Ok(())
1603    }
1604
1605    /// Emit the `memory.copy` operation.
1606    pub fn emit_memory_copy(&mut self, dst_mem: MemoryIndex, src_mem: MemoryIndex) -> Result<()> {
1607        let dst_heap = self.env.resolve_heap(dst_mem);
1608        let src_heap = self.env.resolve_heap(src_mem);
1609        let dst_idx_size: OperandSize = dst_heap.index_type().try_into()?;
1610        let src_idx_size: OperandSize = src_heap.index_type().try_into()?;
1611
1612        let len = self.context.pop_to_reg(self.masm, None)?;
1613        let src = self.context.pop_to_reg(self.masm, None)?;
1614        let dst = self.context.pop_to_reg(self.masm, None)?;
1615
1616        // For 32-bit linear memories go ahead and make sure `len` is zero
1617        // extended within its register ensuring that the full 64-bits of the
1618        // register are defined. This assists in situations like cross-memory
1619        // copies where one memory is 32-bit and one is 64-bit and the same
1620        // register can be used for the length in both bounds checks below.
1621        if dst_idx_size == OperandSize::S32 || src_idx_size == OperandSize::S32 {
1622            self.masm.extend(
1623                writable!(len.reg),
1624                len.reg,
1625                Extend::<Zero>::I64Extend32.into(),
1626            )?;
1627        }
1628
1629        let dst_raw_addr = self.context.any_gpr(self.masm)?;
1630        self.emit_bounds_check_and_compute_addr(&dst_heap, dst_raw_addr, dst.reg, len.reg)?;
1631        self.context.free_reg(dst);
1632
1633        let src_raw_addr = self.context.any_gpr(self.masm)?;
1634        self.emit_bounds_check_and_compute_addr(&src_heap, src_raw_addr, src.reg, len.reg)?;
1635        self.context.free_reg(src);
1636
1637        self.context
1638            .stack
1639            .push(TypedReg::new(self.env.ptr_type(), dst_raw_addr).into());
1640        self.context
1641            .stack
1642            .push(TypedReg::new(self.env.ptr_type(), src_raw_addr).into());
1643        self.context
1644            .stack
1645            .push(TypedReg::new(self.env.ptr_type(), len.reg).into());
1646
1647        let builtin = self.env.builtins.memory_copy::<M::ABI>()?;
1648        FnCall::emit::<M>(
1649            &mut self.env,
1650            self.masm,
1651            &mut self.context,
1652            Callee::Builtin(builtin),
1653        )?;
1654        Ok(())
1655    }
1656
1657    /// Emit the `memory.fill` operation.
1658    pub fn emit_memory_fill(&mut self, mem: MemoryIndex) -> Result<()> {
1659        let heap = self.env.resolve_heap(mem);
1660        let ptr_size: OperandSize = self.env.ptr_type().try_into()?;
1661        let idx_size: OperandSize = heap.index_type().try_into()?;
1662
1663        // The wasm stack at this point is `[dst, val, len]`.
1664        let len = self.context.pop_to_reg(self.masm, None)?;
1665        let val = self.context.pop_to_reg(self.masm, None)?;
1666        let dst = self.context.pop_to_reg(self.masm, None)?;
1667
1668        let raw_addr = self.context.any_gpr(self.masm)?;
1669        self.emit_bounds_check_and_compute_addr(&heap, raw_addr, dst.reg, len.reg)?;
1670        self.context.free_reg(dst);
1671
1672        // The libcall takes the length as a host-pointer-sized integer, so
1673        // zero-extend if the wasm index type is smaller.
1674        let len_reg = len.reg;
1675        if idx_size == OperandSize::S32 && ptr_size == OperandSize::S64 {
1676            self.masm.extend(
1677                writable!(len_reg),
1678                len_reg,
1679                Extend::<Zero>::I64Extend32.into(),
1680            )?;
1681        }
1682
1683        // Set up the call arguments: `[dst_ptr, val, len]`.
1684        self.context
1685            .stack
1686            .push(TypedReg::new(self.env.ptr_type(), raw_addr).into());
1687        self.context.stack.push(val.into());
1688        self.context
1689            .stack
1690            .push(TypedReg::new(self.env.ptr_type(), len_reg).into());
1691
1692        let builtin = self.env.builtins.memory_fill::<M::ABI>()?;
1693        FnCall::emit::<M>(
1694            &mut self.env,
1695            self.masm,
1696            &mut self.context,
1697            Callee::Builtin(builtin),
1698        )?;
1699        Ok(())
1700    }
1701
1702    /// Emit the `memory.init` operation.
1703    pub fn emit_memory_init(&mut self, segment: DataIndex, mem: MemoryIndex) -> Result<()> {
1704        let dst_heap = self.env.resolve_heap(mem);
1705
1706        let len = self.context.pop_to_reg(self.masm, None)?;
1707        let src = self.context.pop_to_reg(self.masm, None)?;
1708        let dst = self.context.pop_to_reg(self.masm, None)?;
1709
1710        // Make sure `len` is zero extended within its register ensuring that
1711        // the full 64-bits of the register are defined. This assists in
1712        // situations like cross-memory copies where one memory is 32-bit and
1713        // one is 64-bit and the same register can be used for the length in
1714        // both bounds checks below.
1715        self.masm.extend(
1716            writable!(len.reg),
1717            len.reg,
1718            Extend::<Zero>::I64Extend32.into(),
1719        )?;
1720
1721        let dst_raw_addr = self.context.any_gpr(self.masm)?;
1722        self.emit_bounds_check_and_compute_addr(&dst_heap, dst_raw_addr, dst.reg, len.reg)?;
1723        self.context.free_reg(dst);
1724
1725        let runtime_data_index = match self.env.translation.runtime_data_map[segment] {
1726            Some(i) => i,
1727
1728            // Active data segments always have length zero, so this is only
1729            // valid of src and len are both zero.
1730            None => {
1731                self.masm.cmp(src.reg, RegImm::i32(0), OperandSize::S32)?;
1732                self.masm
1733                    .trapif(IntCmpKind::Ne, TrapCode::HEAP_OUT_OF_BOUNDS)?;
1734                self.masm.cmp(len.reg, RegImm::i32(0), OperandSize::S32)?;
1735                self.masm
1736                    .trapif(IntCmpKind::Ne, TrapCode::HEAP_OUT_OF_BOUNDS)?;
1737                self.context.free_reg(dst_raw_addr);
1738                self.context.free_reg(src);
1739                self.context.free_reg(len);
1740                return Ok(());
1741            }
1742        };
1743
1744        // Bounds check this passive data segment. Load its
1745        // dynamically-specified length and see if that's in the range
1746        // of `src+len`.
1747        let data_segment_length_offset = self
1748            .env
1749            .vmoffsets
1750            .vmctx_runtime_data_length(runtime_data_index);
1751        let tmp1 = self.context.any_gpr(self.masm)?;
1752        let tmp2 = self.context.any_gpr(self.masm)?;
1753        self.masm.load(
1754            self.masm.address_at_vmctx(data_segment_length_offset)?,
1755            writable!(tmp1),
1756            OperandSize::S32,
1757        )?;
1758        self.masm
1759            .mov(writable!(tmp2), src.reg.into(), OperandSize::S32)?;
1760        self.masm.checked_uadd(
1761            writable!(tmp2),
1762            tmp2,
1763            len.reg.into(),
1764            OperandSize::S32,
1765            TrapCode::HEAP_OUT_OF_BOUNDS,
1766        )?;
1767        self.masm.cmp(tmp2, tmp1.into(), OperandSize::S32)?;
1768        self.masm
1769            .trapif(IntCmpKind::GtU, TrapCode::HEAP_OUT_OF_BOUNDS)?;
1770        self.context.free_reg(tmp2);
1771
1772        // Calculate the src pointer by loading the base of the passive segment
1773        // and adding in the `src` offset.
1774        let data_segment_base_offset = self
1775            .env
1776            .vmoffsets
1777            .vmctx_runtime_data_base(runtime_data_index);
1778        self.masm.load(
1779            self.masm.address_at_vmctx(data_segment_base_offset)?,
1780            writable!(tmp1),
1781            OperandSize::S64,
1782        )?;
1783        self.masm.add_uextend(
1784            writable!(tmp1),
1785            tmp1,
1786            src.reg,
1787            OperandSize::S32,
1788            OperandSize::S64,
1789        )?;
1790        self.context.free_reg(src);
1791
1792        // And finally, the final step is calling the `memory_copy` libcall.
1793        self.context.stack.push(TypedReg::i64(dst_raw_addr).into());
1794        self.context.stack.push(TypedReg::i64(tmp1).into());
1795        self.context.stack.push(len.into());
1796        let builtin = self.env.builtins.memory_copy::<M::ABI>()?;
1797        FnCall::emit::<M>(
1798            &mut self.env,
1799            self.masm,
1800            &mut self.context,
1801            Callee::Builtin(builtin),
1802        )?;
1803        Ok(())
1804    }
1805
1806    pub fn emit_data_drop(&mut self, data_index: DataIndex) -> Result<()> {
1807        let runtime_data_index = match self.env.translation.runtime_data_map[data_index] {
1808            Some(idx) => idx,
1809            // Active data segments do nothing when dropped, so this is a noop.
1810            None => return Ok(()),
1811        };
1812        let data_segment_offset = self
1813            .env
1814            .vmoffsets
1815            .vmctx_runtime_data_length(runtime_data_index);
1816        let len_addr = self.masm.address_at_vmctx(data_segment_offset)?;
1817        self.masm.store(RegImm::i32(0), len_addr, OperandSize::S32)
1818    }
1819
1820    /// Implementation of `table.init`
1821    pub fn emit_table_init(
1822        &mut self,
1823        elem_index: ElemIndex,
1824        table_index: TableIndex,
1825    ) -> Result<()> {
1826        let builtin_base = self.env.builtins.passive_elem_segment_base::<M::ABI>()?;
1827        let builtin_len = self.env.builtins.passive_elem_segment_len::<M::ABI>()?;
1828
1829        // Push the passive segment's length and base onto the stack.
1830        match self.env.translation.passive_elem_map[elem_index] {
1831            Some(idx) => {
1832                self.context.stack.extend([idx.as_u32().try_into()?]);
1833                FnCall::emit::<M>(
1834                    &mut self.env,
1835                    self.masm,
1836                    &mut self.context,
1837                    Callee::Builtin(builtin_len),
1838                )?;
1839                self.context.stack.extend([idx.as_u32().try_into()?]);
1840                FnCall::emit::<M>(
1841                    &mut self.env,
1842                    self.masm,
1843                    &mut self.context,
1844                    Callee::Builtin(builtin_base),
1845                )?;
1846            }
1847            // Active data segments have 0 length and a null base pointer.
1848            None => {
1849                let tmp = self.context.any_gpr(self.masm)?;
1850                self.masm
1851                    .mov(writable!(tmp), RegImm::i64(0), OperandSize::S64)?;
1852                self.context
1853                    .stack
1854                    .push(TypedReg::new(WasmValType::I64, tmp).into());
1855
1856                let tmp = self.context.any_gpr(self.masm)?;
1857                self.masm
1858                    .mov(writable!(tmp), RegImm::i64(0), OperandSize::S64)?;
1859                self.context
1860                    .stack
1861                    .push(TypedReg::new(WasmValType::I64, tmp).into());
1862            }
1863        };
1864
1865        // Push the table's current length onto the stack.
1866        let table_data = self.env.resolve_table_data(table_index);
1867        let idx_size = table_data.index_type().try_into()?;
1868        self.emit_compute_table_size(&table_data)?;
1869
1870        // And now pop off everything we have for this instruction to work with
1871        // it all below.
1872        let table_size = self.context.pop_to_reg(self.masm, None)?;
1873        let segment_base = self.context.pop_to_reg(self.masm, None)?;
1874        let segment_len = self.context.pop_to_reg(self.masm, None)?;
1875        let len = self.context.pop_to_reg(self.masm, None)?;
1876        let segment_off = self.context.pop_to_reg(self.masm, None)?;
1877        let table_off = self.context.pop_to_reg(self.masm, None)?;
1878
1879        // Zero-extend the length to make it easier to work with below for
1880        // 64-bit tables.
1881        if len.ty == WasmValType::I32 {
1882            self.masm.extend(
1883                writable!(len.reg),
1884                len.reg,
1885                Extend::<Zero>::I64Extend32.into(),
1886            )?;
1887        }
1888
1889        // Perform a bounds check to see if `segment_off+len` is inbounds.
1890        let tmp = self.context.any_gpr(self.masm)?;
1891        {
1892            self.masm
1893                .mov(writable!(tmp), segment_off.reg.into(), OperandSize::S32)?;
1894            self.masm.checked_uadd(
1895                writable!(tmp),
1896                tmp,
1897                len.reg.into(),
1898                OperandSize::S32,
1899                TRAP_TABLE_OUT_OF_BOUNDS,
1900            )?;
1901            self.masm
1902                .cmp(tmp, segment_len.reg.into(), OperandSize::S32)?;
1903            self.masm
1904                .trapif(IntCmpKind::GtU, TRAP_TABLE_OUT_OF_BOUNDS)?;
1905            self.context.free_reg(segment_len);
1906        }
1907
1908        // Perform a bounds check to see if `table_off+len` is inbounds.
1909        {
1910            self.masm
1911                .mov(writable!(tmp), table_off.reg.into(), idx_size)?;
1912            self.masm.checked_uadd(
1913                writable!(tmp),
1914                tmp,
1915                len.reg.into(),
1916                idx_size,
1917                TRAP_TABLE_OUT_OF_BOUNDS,
1918            )?;
1919            self.masm.cmp(tmp, table_size.reg.into(), idx_size)?;
1920            self.masm
1921                .trapif(IntCmpKind::GtU, TRAP_TABLE_OUT_OF_BOUNDS)?;
1922            self.context.free_reg(table_size);
1923        }
1924        self.context.free_reg(tmp);
1925
1926        // Calculate the base address of the segment that we're reading from.
1927        {
1928            self.masm.extend(
1929                writable!(segment_off.reg),
1930                segment_off.reg,
1931                Extend::<Zero>::I64Extend32.into(),
1932            )?;
1933            self.masm.mul(
1934                writable!(segment_off.reg),
1935                segment_off.reg,
1936                RegImm::i64(16),
1937                OperandSize::S64,
1938            )?;
1939            self.masm.add(
1940                writable!(segment_base.reg),
1941                segment_base.reg,
1942                segment_off.reg.into(),
1943                OperandSize::S64,
1944            )?;
1945            self.context.free_reg(segment_off);
1946        }
1947
1948        // Now run `table.set` in a loop with the values read from the element
1949        // segment.
1950        let header = self.masm.get_label()?;
1951        let exit = self.masm.get_label()?;
1952
1953        self.masm.bind(header)?;
1954        {
1955            self.masm.branch(
1956                IntCmpKind::Eq,
1957                len.reg,
1958                RegImm::i64(0),
1959                exit,
1960                OperandSize::S64,
1961            )?;
1962
1963            // Read `*mut VMFuncRef` from `ValRaw`, and then increment the
1964            // `segment_base` pointer.
1965            let funcref = self.context.any_gpr(self.masm)?;
1966            self.masm.load_ptr(
1967                self.masm.address_at_reg(segment_base.reg, 0)?,
1968                writable!(funcref),
1969            )?;
1970            self.masm.add(
1971                writable!(segment_base.reg),
1972                segment_base.reg,
1973                RegImm::i64(16),
1974                OperandSize::S64,
1975            )?;
1976
1977            // Spill context/variables for the table.set, and note that
1978            // `table_off` is duplicated here as one version is consumed by the
1979            // `table.set` and the other persists across the loop.
1980            self.context.stack.push(segment_base.into());
1981            self.context.stack.push(len.into());
1982            let table_off_copy = self.context.any_gpr(self.masm)?;
1983            self.masm.mov(
1984                writable!(table_off_copy),
1985                table_off.reg.into(),
1986                table_off.ty.try_into()?,
1987            )?;
1988            self.context.stack.push(table_off.into());
1989            self.context
1990                .stack
1991                .push(TypedReg::new(table_off.ty, table_off_copy).into());
1992            self.context
1993                .stack
1994                .push(TypedReg::new(WasmValType::FUNCREF, funcref).into());
1995            self.emit_table_set(table_index)?;
1996
1997            // Pop loop variables into their original registers for the loop.
1998            self.context.pop_to_reg(self.masm, Some(table_off.reg))?;
1999            self.context.pop_to_reg(self.masm, Some(len.reg))?;
2000            self.context.pop_to_reg(self.masm, Some(segment_base.reg))?;
2001
2002            // Increment the table index to copy next
2003            self.masm.add(
2004                writable!(table_off.reg),
2005                table_off.reg,
2006                RegImm::i64(1),
2007                table_off.ty.try_into()?,
2008            )?;
2009
2010            // Decrement the number of remaining elements to copy, used as the
2011            // loop's exit condition above.
2012            self.masm.sub(
2013                writable!(len.reg),
2014                len.reg,
2015                RegImm::i64(1),
2016                OperandSize::S64,
2017            )?;
2018        }
2019        self.masm.jmp(header)?;
2020
2021        self.masm.bind(exit)?;
2022
2023        self.context.free_reg(segment_base);
2024        self.context.free_reg(len);
2025        self.context.free_reg(table_off);
2026        Ok(())
2027    }
2028
2029    /// Implementation of `elem.drop`
2030    pub fn emit_elem_drop(&mut self, elem_index: ElemIndex) -> Result<()> {
2031        let passive_elem_index = match self.env.translation.passive_elem_map[elem_index] {
2032            Some(idx) => idx,
2033            // Active elem segments do nothing when dropped, so this is a noop.
2034            None => return Ok(()),
2035        };
2036        let builtin = self.env.builtins.passive_elem_segment_drop::<M::ABI>()?;
2037        self.context
2038            .stack
2039            .extend([passive_elem_index.as_u32().try_into()?]);
2040        FnCall::emit::<M>(
2041            &mut self.env,
2042            self.masm,
2043            &mut self.context,
2044            Callee::Builtin(builtin),
2045        )?;
2046        self.context.pop_and_free(self.masm)
2047    }
2048
2049    /// Checks if fuel consumption is enabled and emits a series of instructions
2050    /// that check the current fuel usage by performing a zero-comparison with
2051    /// the number of units stored in `VMStoreContext`.
2052    pub fn maybe_emit_fuel_check(&mut self) -> Result<()> {
2053        if !self.tunables.consume_fuel {
2054            return Ok(());
2055        }
2056
2057        self.emit_fuel_increment()?;
2058        let out_of_fuel = self.env.builtins.out_of_gas::<M::ABI>()?;
2059        let fuel_reg = self.context.without::<Result<Reg>, M, _>(
2060            &out_of_fuel.sig().regs,
2061            self.masm,
2062            |cx, masm| cx.any_gpr(masm),
2063        )??;
2064
2065        self.emit_load_fuel_consumed(fuel_reg)?;
2066
2067        // The  continuation label if the current fuel is under the limit.
2068        let continuation = self.masm.get_label()?;
2069
2070        // Spill locals and registers to avoid conflicts at the out-of-fuel
2071        // control flow merge.
2072        self.context.spill(self.masm)?;
2073        // Fuel is stored as a negative i64, so if the number is less than zero,
2074        // we're still under the fuel limits.
2075        self.masm.branch(
2076            IntCmpKind::LtS,
2077            fuel_reg,
2078            RegImm::i64(0),
2079            continuation,
2080            OperandSize::S64,
2081        )?;
2082        // Out-of-fuel branch.
2083        FnCall::emit::<M>(
2084            &mut self.env,
2085            self.masm,
2086            &mut self.context,
2087            Callee::Builtin(out_of_fuel.clone()),
2088        )?;
2089        self.context.pop_and_free(self.masm)?;
2090
2091        // Under fuel limits branch.
2092        self.masm.bind(continuation)?;
2093        self.context.free_reg(fuel_reg);
2094
2095        Ok(())
2096    }
2097
2098    /// Emits a series of instructions that load the `fuel_consumed` field from
2099    /// `VMStoreContext`.
2100    fn emit_load_fuel_consumed(&mut self, fuel_reg: Reg) -> Result<()> {
2101        let store_context_offset = self.env.vmoffsets.ptr.vmctx_store_context();
2102        let fuel_offset = self.env.vmoffsets.ptr.vmstore_context_fuel_consumed();
2103        self.masm.load_ptr(
2104            self.masm
2105                .address_at_vmctx(u32::from(store_context_offset))?,
2106            writable!(fuel_reg),
2107        )?;
2108
2109        self.masm.load(
2110            self.masm.address_at_reg(fuel_reg, u32::from(fuel_offset))?,
2111            writable!(fuel_reg),
2112            // Fuel is an i64.
2113            OperandSize::S64,
2114        )
2115    }
2116
2117    /// Checks if epoch interruption is configured and emits a series of
2118    /// instructions that check the current epoch against its deadline.
2119    pub fn maybe_emit_epoch_check(&mut self) -> Result<()> {
2120        if !self.tunables.epoch_interruption {
2121            return Ok(());
2122        }
2123
2124        // The continuation branch if the current epoch hasn't reached the
2125        // configured deadline.
2126        let cont = self.masm.get_label()?;
2127        let new_epoch = self.env.builtins.new_epoch::<M::ABI>()?;
2128
2129        // Checks for runtime limits (e.g., fuel, epoch) are special since they
2130        // require inserting arbitrary function calls and control flow.
2131        // Special care must be taken to ensure that all invariants are met. In
2132        // this case, since `new_epoch` takes an argument and returns a value,
2133        // we must ensure that any registers used to hold the current epoch
2134        // value and deadline are not going to be needed later on by the
2135        // function call.
2136        let (epoch_deadline_reg, epoch_counter_reg) =
2137            self.context.without::<Result<(Reg, Reg)>, M, _>(
2138                &new_epoch.sig().regs,
2139                self.masm,
2140                |cx, masm| Ok((cx.any_gpr(masm)?, cx.any_gpr(masm)?)),
2141            )??;
2142
2143        self.emit_load_epoch_deadline_and_counter(epoch_deadline_reg, epoch_counter_reg)?;
2144
2145        // Spill locals and registers to avoid conflicts at the control flow
2146        // merge below.
2147        self.context.spill(self.masm)?;
2148        self.masm.branch(
2149            IntCmpKind::LtU,
2150            epoch_counter_reg,
2151            RegImm::reg(epoch_deadline_reg),
2152            cont,
2153            OperandSize::S64,
2154        )?;
2155        // Epoch deadline reached branch.
2156        FnCall::emit::<M>(
2157            &mut self.env,
2158            self.masm,
2159            &mut self.context,
2160            Callee::Builtin(new_epoch.clone()),
2161        )?;
2162        // `new_epoch` returns the new deadline. However we don't
2163        // perform any caching, so we simply drop this value.
2164        self.visit_drop()?;
2165
2166        // Under epoch deadline branch.
2167        self.masm.bind(cont)?;
2168
2169        self.context.free_reg(epoch_deadline_reg);
2170        self.context.free_reg(epoch_counter_reg);
2171        Ok(())
2172    }
2173
2174    fn emit_load_epoch_deadline_and_counter(
2175        &mut self,
2176        epoch_deadline_reg: Reg,
2177        epoch_counter_reg: Reg,
2178    ) -> Result<()> {
2179        let epoch_ptr_offset = self.env.vmoffsets.ptr.vmctx_epoch_ptr();
2180        let store_context_offset = self.env.vmoffsets.ptr.vmctx_store_context();
2181        let epoch_deadline_offset = self.env.vmoffsets.ptr.vmstore_context_epoch_deadline();
2182
2183        // Load the current epoch value into `epoch_counter_var`.
2184        self.masm.load_ptr(
2185            self.masm.address_at_vmctx(u32::from(epoch_ptr_offset))?,
2186            writable!(epoch_counter_reg),
2187        )?;
2188
2189        // `epoch_deadline_var` contains the address of the value, so we need
2190        // to extract it.
2191        self.masm.load(
2192            self.masm.address_at_reg(epoch_counter_reg, 0)?,
2193            writable!(epoch_counter_reg),
2194            OperandSize::S64,
2195        )?;
2196
2197        // Load the `VMStoreContext`.
2198        self.masm.load_ptr(
2199            self.masm
2200                .address_at_vmctx(u32::from(store_context_offset))?,
2201            writable!(epoch_deadline_reg),
2202        )?;
2203
2204        self.masm.load(
2205            self.masm
2206                .address_at_reg(epoch_deadline_reg, u32::from(epoch_deadline_offset))?,
2207            writable!(epoch_deadline_reg),
2208            // The deadline value is a u64.
2209            OperandSize::S64,
2210        )
2211    }
2212
2213    /// Increments the fuel consumed in `VMStoreContext` by flushing
2214    /// `self.fuel_consumed` to memory.
2215    fn emit_fuel_increment(&mut self) -> Result<()> {
2216        let fuel_at_point = std::mem::replace(&mut self.fuel_consumed, 0);
2217        if fuel_at_point == 0 {
2218            return Ok(());
2219        }
2220
2221        let store_context_offset = self.env.vmoffsets.ptr.vmctx_store_context();
2222        let fuel_offset = self.env.vmoffsets.ptr.vmstore_context_fuel_consumed();
2223        let limits_reg = self.context.any_gpr(self.masm)?;
2224
2225        // Load `VMStoreContext` into the `limits_reg` reg.
2226        self.masm.load_ptr(
2227            self.masm
2228                .address_at_vmctx(u32::from(store_context_offset))?,
2229            writable!(limits_reg),
2230        )?;
2231
2232        self.masm.with_scratch::<IntScratch, _>(|masm, scratch| {
2233            // Load the fuel consumed at point into the scratch register.
2234            masm.load(
2235                masm.address_at_reg(limits_reg, u32::from(fuel_offset))?,
2236                scratch.writable(),
2237                OperandSize::S64,
2238            )?;
2239
2240            // Add the fuel consumed at point with the value in the scratch
2241            // register.
2242            masm.add(
2243                scratch.writable(),
2244                scratch.inner(),
2245                RegImm::i64(fuel_at_point),
2246                OperandSize::S64,
2247            )?;
2248
2249            // Store the updated fuel consumed to `VMStoreContext`.
2250            masm.store(
2251                scratch.inner().into(),
2252                masm.address_at_reg(limits_reg, u32::from(fuel_offset))?,
2253                OperandSize::S64,
2254            )
2255        })?;
2256
2257        self.context.free_reg(limits_reg);
2258
2259        Ok(())
2260    }
2261
2262    /// Hook to handle fuel before visiting an operator.
2263    fn fuel_before_visit_op(&mut self, op: &Operator) -> Result<()> {
2264        if !self.context.reachable {
2265            // `self.fuel_consumed` must be correctly flushed to memory when
2266            // entering an unreachable state.
2267            ensure!(self.fuel_consumed == 0, CodeGenError::illegal_fuel_state())
2268        }
2269
2270        // Generally, most instructions require 1 fuel unit.
2271        //
2272        // However, there are exceptions, which are detailed in the code below.
2273        // Note that the fuel accounting semantics align with those of
2274        // Cranelift; for further information, refer to
2275        // `crates/cranelift/src/func_environ.rs`.
2276        //
2277        // The primary distinction between the two implementations is that Winch
2278        // does not utilize a local-based cache to track fuel consumption.
2279        // Instead, each increase in fuel necessitates loading from and storing
2280        // to memory.
2281        //
2282        // Memory traffic will undoubtedly impact runtime performance. One
2283        // potential optimization is to designate a register as non-allocatable,
2284        // when fuel consumption is enabled, effectively using it as a local
2285        // fuel cache.
2286        self.fuel_consumed += self.tunables.operator_cost.cost(op);
2287
2288        match op {
2289            Operator::Unreachable
2290            | Operator::Loop { .. }
2291            | Operator::If { .. }
2292            | Operator::Else { .. }
2293            | Operator::Br { .. }
2294            | Operator::BrIf { .. }
2295            | Operator::BrTable { .. }
2296            | Operator::End
2297            | Operator::Return
2298            | Operator::CallIndirect { .. }
2299            | Operator::Call { .. }
2300            | Operator::ReturnCall { .. }
2301            | Operator::ReturnCallIndirect { .. } => self.emit_fuel_increment(),
2302            _ => Ok(()),
2303        }
2304    }
2305
2306    // Hook to handle source location mapping before visiting an operator.
2307    fn source_location_before_visit_op(&mut self, offset: usize) -> Result<()> {
2308        let loc = SourceLoc::new(offset as u32);
2309        let rel = self.source_loc_from(loc);
2310        self.source_location.current = self.masm.start_source_loc(rel)?;
2311        Ok(())
2312    }
2313
2314    // Hook to handle source location mapping after visiting an operator.
2315    fn source_location_after_visit_op(&mut self) -> Result<()> {
2316        // Because in Winch binary emission is done in a single pass
2317        // and because the MachBuffer performs optimizations during
2318        // emission, we have to be careful when calling
2319        // [`MacroAssembler::end_source_location`] to avoid breaking the
2320        // invariant that checks that the end [CodeOffset] must be equal
2321        // or greater than the start [CodeOffset].
2322        if self.masm.current_code_offset()? >= self.source_location.current.0 {
2323            self.masm.end_source_loc()?;
2324        }
2325
2326        Ok(())
2327    }
2328
2329    pub(crate) fn emit_atomic_rmw(
2330        &mut self,
2331        arg: &MemArg,
2332        op: RmwOp,
2333        size: OperandSize,
2334        extend: Option<Extend<Zero>>,
2335    ) -> Result<()> {
2336        let memory_index = MemoryIndex::from_u32(arg.memory);
2337        let heap = self.env.resolve_heap(memory_index);
2338        // We need to pop-push the operand to compute the address before passing control over to
2339        // masm, because some architectures may have specific requirements for the registers used
2340        // in some atomic operations.
2341        let operand = self.context.pop_to_reg(self.masm, None)?;
2342        if let Some(addr) = self.emit_compute_heap_address_align_checked(&heap, arg, size)? {
2343            let src = self.masm.address_at_reg(addr, 0)?;
2344            self.context.stack.push(operand.into());
2345            self.masm
2346                .atomic_rmw(&mut self.context, src, size, op, UNTRUSTED_FLAGS, extend)?;
2347            self.context.free_reg(addr);
2348        }
2349
2350        Ok(())
2351    }
2352
2353    pub(crate) fn emit_atomic_cmpxchg(
2354        &mut self,
2355        arg: &MemArg,
2356        size: OperandSize,
2357        extend: Option<Extend<Zero>>,
2358    ) -> Result<()> {
2359        // At this point in the stack we have:
2360        //    [ address, expected, replacement ]
2361        //
2362        // Therefore, emission for this instruction is a bit
2363        // trickier. The address for the CAS is the 3rd from the top
2364        // of the stack, and we must emit instruction to compute the
2365        // actual address with
2366        // `emit_compute_heap_address_align_checked`, while we still
2367        // have access to self. However, some ISAs have requirements
2368        // with regard to the registers used for some arguments, so we
2369        // need to pass the context to the masm. To solve this issue,
2370        // we pop the two first arguments from the stack, compute the
2371        // address, push back the arguments, and hand over the control
2372        // to masm. The implementer of `atomic_cas` can expect to find
2373        // `expected` and `replacement` at the top the context's
2374        // stack.
2375
2376        let replacement = self.context.pop_to_reg(self.masm, None)?;
2377        let expected = self.context.pop_to_reg(self.masm, None)?;
2378
2379        let memory_index = MemoryIndex::from_u32(arg.memory);
2380        let heap = self.env.resolve_heap(memory_index);
2381        if let Some(addr) = self.emit_compute_heap_address_align_checked(&heap, arg, size)? {
2382            self.context.stack.push(expected.into());
2383            self.context.stack.push(replacement.into());
2384
2385            let src = self.masm.address_at_reg(addr, 0)?;
2386            self.masm
2387                .atomic_cas(&mut self.context, src, size, UNTRUSTED_FLAGS, extend)?;
2388
2389            self.context.free_reg(addr);
2390        }
2391        Ok(())
2392    }
2393
2394    /// Emit the sequence of instruction for a `memory.atomic.wait*`.
2395    pub fn emit_atomic_wait(&mut self, arg: &MemArg, kind: AtomicWaitKind) -> Result<()> {
2396        // The `memory_atomic_wait*` builtins expect the following arguments:
2397        // - `memory`, as u32
2398        // - `address`, as u64
2399        // - `expected`, as either u64 or u32
2400        // - `timeout`, as u64
2401        // At this point our stack only contains the `timeout`, the `expected` and the address, so
2402        // we need to:
2403        // - insert the memory as the first argument
2404        // - compute the actual memory offset from the `MemArg`, if necessary.
2405        // Note that the builtin function performs the alignment and bounds checks for us, so we
2406        // don't need to emit that.
2407
2408        let timeout = self.context.pop_to_reg(self.masm, None)?;
2409        let expected = self.context.pop_to_reg(self.masm, None)?;
2410        let addr = self.context.pop_to_reg(self.masm, None)?;
2411
2412        // Put the target memory index as the first argument.
2413        let stack_len = self.context.stack.len();
2414        let builtin = match kind {
2415            AtomicWaitKind::Wait32 => self.env.builtins.memory_atomic_wait32::<M::ABI>()?,
2416            AtomicWaitKind::Wait64 => self.env.builtins.memory_atomic_wait64::<M::ABI>()?,
2417        };
2418        let builtin = self.prepare_builtin_defined_memory_arg(
2419            MemoryIndex::from_u32(arg.memory),
2420            stack_len,
2421            builtin,
2422        )?;
2423
2424        if arg.offset != 0 {
2425            self.masm.checked_uadd(
2426                writable!(addr.reg),
2427                addr.reg,
2428                RegImm::i64(arg.offset as i64),
2429                OperandSize::S64,
2430                TrapCode::HEAP_OUT_OF_BOUNDS,
2431            )?;
2432        }
2433
2434        self.context
2435            .stack
2436            .push(TypedReg::new(WasmValType::I64, addr.reg).into());
2437        self.context.stack.push(expected.into());
2438        self.context.stack.push(timeout.into());
2439
2440        FnCall::emit::<M>(&mut self.env, self.masm, &mut self.context, builtin)?;
2441
2442        Ok(())
2443    }
2444
2445    pub fn emit_atomic_notify(&mut self, arg: &MemArg) -> Result<()> {
2446        // The memory `memory_atomic_notify` builtin expects the following arguments:
2447        // - `memory`, as u32
2448        // - `address`, as u64
2449        // - `count`: as u32
2450        // At this point our stack only contains the `count` and the `address`, so we need to:
2451        // - insert the memory as the first argument
2452        // - compute the actual memory offset from the `MemArg`, if necessary.
2453        // Note that the builtin function performs the alignment and bounds checks for us, so we
2454        // don't need to emit that.
2455
2456        // pop the arguments from the stack.
2457        let count = self.context.pop_to_reg(self.masm, None)?;
2458        let addr = self.context.pop_to_reg(self.masm, None)?;
2459
2460        // Put the target memory index as the first argument.
2461        let builtin = self.env.builtins.memory_atomic_notify::<M::ABI>()?;
2462        let stack_len = self.context.stack.len();
2463        let builtin = self.prepare_builtin_defined_memory_arg(
2464            MemoryIndex::from_u32(arg.memory),
2465            stack_len,
2466            builtin,
2467        )?;
2468
2469        if arg.offset != 0 {
2470            self.masm.checked_uadd(
2471                writable!(addr.reg),
2472                addr.reg,
2473                RegImm::i64(arg.offset as i64),
2474                OperandSize::S64,
2475                TrapCode::HEAP_OUT_OF_BOUNDS,
2476            )?;
2477        }
2478
2479        // push remaining arguments.
2480        self.context
2481            .stack
2482            .push(TypedReg::new(WasmValType::I64, addr.reg).into());
2483        self.context.stack.push(count.into());
2484
2485        FnCall::emit::<M>(&mut self.env, self.masm, &mut self.context, builtin)?;
2486
2487        Ok(())
2488    }
2489
2490    pub fn prepare_builtin_defined_memory_arg(
2491        &mut self,
2492        mem: MemoryIndex,
2493        defined_index_at: usize,
2494        builtin: BuiltinFunction,
2495    ) -> Result<Callee> {
2496        match self.env.translation.module.defined_memory_index(mem) {
2497            // This memory is defined in this module, so the vmctx is this
2498            // module's vmctx and the memory index is `defined` as returned here.
2499            Some(defined) => {
2500                self.context
2501                    .stack
2502                    .insert_many(defined_index_at, &[defined.as_u32().try_into()?]);
2503                Ok(Callee::Builtin(builtin))
2504            }
2505
2506            // This memory is not defined in this module, so the defined index
2507            // is loaded from the `VMMemoryImport` and the vmctx is loaded from
2508            // the vmctx itself.
2509            None => {
2510                let vmimport = self.env.vmoffsets.vmctx_vmmemory_import(mem);
2511                let vmctx_offset = vmimport + u32::from(self.env.vmoffsets.vmmemory_import_vmctx());
2512                let index_offset = vmimport + u32::from(self.env.vmoffsets.vmmemory_import_index());
2513                let index_addr = self.masm.address_at_vmctx(index_offset)?;
2514                let index_dst = self.context.reg_for_class(RegClass::Int, self.masm)?;
2515                self.masm
2516                    .load(index_addr, writable!(index_dst), OperandSize::S32)?;
2517                self.context
2518                    .stack
2519                    .insert_many(defined_index_at, &[Val::reg(index_dst, WasmValType::I32)]);
2520                Ok(Callee::BuiltinWithDifferentVmctx(builtin, vmctx_offset))
2521            }
2522        }
2523    }
2524
2525    /// Same as `prepare_builtin_defined_memory_arg`, but for tables.
2526    pub fn prepare_builtin_defined_table_arg(
2527        &mut self,
2528        table: TableIndex,
2529        defined_index_at: usize,
2530        builtin: BuiltinFunction,
2531    ) -> Result<Callee> {
2532        match self.env.translation.module.defined_table_index(table) {
2533            Some(defined) => {
2534                self.context
2535                    .stack
2536                    .insert_many(defined_index_at, &[defined.as_u32().try_into()?]);
2537                Ok(Callee::Builtin(builtin))
2538            }
2539            None => {
2540                let vmimport = self.env.vmoffsets.vmctx_vmtable_import(table);
2541                let vmctx_offset = vmimport + u32::from(self.env.vmoffsets.vmtable_import_vmctx());
2542                let index_offset = vmimport + u32::from(self.env.vmoffsets.vmtable_import_index());
2543                let index_addr = self.masm.address_at_vmctx(index_offset)?;
2544                let index_dst = self.context.reg_for_class(RegClass::Int, self.masm)?;
2545                self.masm
2546                    .load(index_addr, writable!(index_dst), OperandSize::S32)?;
2547                self.context
2548                    .stack
2549                    .insert_many(defined_index_at, &[Val::reg(index_dst, WasmValType::I32)]);
2550                Ok(Callee::BuiltinWithDifferentVmctx(builtin, vmctx_offset))
2551            }
2552        }
2553    }
2554}
2555
2556/// Returns the index of the [`ControlStackFrame`] for the given
2557/// depth.
2558pub fn control_index(depth: u32, control_length: usize) -> Result<usize> {
2559    (control_length - 1)
2560        .checked_sub(depth as usize)
2561        .ok_or_else(|| format_err!(CodeGenError::control_frame_expected()))
2562}