Skip to main content

winch_codegen/codegen/
mod.rs

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