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