wasmer_compiler_singlepass/
codegen.rs

1#[cfg(feature = "unwind")]
2use crate::dwarf::WriterRelocate;
3
4use crate::{
5    address_map::get_function_address_map,
6    codegen_error,
7    common_decl::*,
8    config::Singlepass,
9    location::{Location, Reg},
10    machine::{
11        AssemblyComment, FinalizedAssembly, Label, Machine, NATIVE_PAGE_SIZE, UnsignedCondition,
12    },
13    unwind::UnwindFrame,
14};
15#[cfg(feature = "unwind")]
16use gimli::write::Address;
17use itertools::Itertools;
18use smallvec::{SmallVec, smallvec};
19use std::{
20    cmp,
21    collections::HashMap,
22    iter,
23    ops::{AddAssign, Neg, SubAssign},
24};
25use target_lexicon::Architecture;
26
27use wasmer_compiler::{
28    FunctionBodyData,
29    misc::CompiledKind,
30    types::{
31        function::{CompiledFunction, CompiledFunctionFrameInfo, FunctionBody},
32        relocation::{Relocation, RelocationTarget},
33        section::SectionIndex,
34    },
35    wasmparser::{
36        BlockType as WpTypeOrFuncType, HeapType as WpHeapType, Operator, RefType as WpRefType,
37        ValType as WpType,
38    },
39};
40
41#[cfg(feature = "unwind")]
42use wasmer_compiler::types::unwind::CompiledFunctionUnwindInfo;
43
44use wasmer_types::target::CallingConvention;
45use wasmer_types::{
46    CompileError, FunctionIndex, FunctionType, GlobalIndex, LocalFunctionIndex, LocalMemoryIndex,
47    MemoryIndex, MemoryStyle, ModuleInfo, SignatureIndex, TableIndex, TableStyle, TrapCode, Type,
48    VMBuiltinFunctionIndex, VMOffsets,
49    entity::{EntityRef, PrimaryMap},
50};
51
52#[allow(type_alias_bounds)]
53type LocationWithCanonicalization<M: Machine> = (Location<M::GPR, M::SIMD>, CanonicalizeType);
54
55/// Stack offset tracking in bytes where we track the maximum offset.
56#[derive(Default)]
57struct TrackedStackOffset {
58    offset: usize,
59    maximum_offset: usize,
60}
61
62impl TrackedStackOffset {
63    fn get(&self) -> usize {
64        self.offset
65    }
66
67    fn track_temporary_extra_allocation(&mut self, extra: usize) {
68        self.maximum_offset = self.maximum_offset.max(self.offset + extra);
69    }
70}
71
72impl AddAssign<usize> for TrackedStackOffset {
73    fn add_assign(&mut self, rhs: usize) {
74        self.offset += rhs;
75        self.maximum_offset = self.maximum_offset.max(self.offset);
76    }
77}
78
79impl SubAssign<usize> for TrackedStackOffset {
80    fn sub_assign(&mut self, rhs: usize) {
81        self.offset -= rhs;
82    }
83}
84
85/// The singlepass per-function code generator.
86pub struct FuncGen<'a, M: Machine> {
87    // Immutable properties assigned at creation time.
88    /// Static module information.
89    module: &'a ModuleInfo,
90
91    /// ModuleInfo compilation config.
92    config: &'a Singlepass,
93
94    /// Offsets of vmctx fields.
95    vmoffsets: &'a VMOffsets,
96
97    // // Memory plans.
98    memory_styles: &'a PrimaryMap<MemoryIndex, MemoryStyle>,
99
100    /// Function signature.
101    signature: FunctionType,
102
103    // Working storage.
104    /// Memory locations of local variables.
105    locals: Vec<Location<M::GPR, M::SIMD>>,
106
107    /// Types of local variables, including arguments.
108    local_types: Vec<WpType>,
109
110    /// Value stack.
111    value_stack: Vec<LocationWithCanonicalization<M>>,
112
113    /// A list of frames describing the current control stack.
114    control_stack: Vec<ControlFrame<M>>,
115
116    /// Stack offset tracking in bytes.
117    stack_offset: TrackedStackOffset,
118
119    save_area_offset: Option<usize>,
120
121    /// Low-level machine state.
122    machine: M,
123
124    /// Nesting level of unreachable code.
125    unreachable_depth: usize,
126
127    /// Index of a function defined locally inside the WebAssembly module.
128    local_func_index: LocalFunctionIndex,
129
130    /// Relocation information.
131    relocations: Vec<Relocation>,
132
133    /// A set of special labels for trapping.
134    special_labels: SpecialLabelSet,
135
136    /// Calling convention to use.
137    calling_convention: CallingConvention,
138
139    /// Name of the function.
140    function_name: String,
141
142    /// Assembly comments.
143    assembly_comments: HashMap<usize, AssemblyComment>,
144}
145
146struct SpecialLabelSet {
147    integer_division_by_zero: Label,
148    integer_overflow: Label,
149    heap_access_oob: Label,
150    table_access_oob: Label,
151    indirect_call_null: Label,
152    bad_signature: Label,
153    unaligned_atomic: Label,
154}
155
156/// Type of a pending canonicalization floating point value.
157/// Sometimes we don't have the type information elsewhere and therefore we need to track it here.
158#[derive(Copy, Clone, Debug)]
159pub(crate) enum CanonicalizeType {
160    None,
161    F32,
162    F64,
163}
164
165impl CanonicalizeType {
166    fn to_size(self) -> Option<Size> {
167        match self {
168            CanonicalizeType::F32 => Some(Size::S32),
169            CanonicalizeType::F64 => Some(Size::S64),
170            CanonicalizeType::None => None,
171        }
172    }
173
174    fn promote(self) -> Result<Self, CompileError> {
175        match self {
176            CanonicalizeType::None => Ok(CanonicalizeType::None),
177            CanonicalizeType::F32 => Ok(CanonicalizeType::F64),
178            CanonicalizeType::F64 => codegen_error!("cannot promote F64"),
179        }
180    }
181
182    fn demote(self) -> Result<Self, CompileError> {
183        match self {
184            CanonicalizeType::None => Ok(CanonicalizeType::None),
185            CanonicalizeType::F32 => codegen_error!("cannot demote F64"),
186            CanonicalizeType::F64 => Ok(CanonicalizeType::F32),
187        }
188    }
189}
190
191trait WpTypeExt {
192    fn is_float(&self) -> bool;
193}
194
195impl WpTypeExt for WpType {
196    fn is_float(&self) -> bool {
197        matches!(self, WpType::F32 | WpType::F64)
198    }
199}
200
201#[derive(Clone)]
202pub enum ControlState<M: Machine> {
203    Function,
204    Block,
205    Loop,
206    If {
207        label_else: Label,
208        // Store the input parameters for the If block, as they'll need to be
209        // restored when processing the Else block (if present).
210        inputs: SmallVec<[LocationWithCanonicalization<M>; 1]>,
211    },
212    Else,
213}
214
215#[derive(Clone)]
216struct ControlFrame<M: Machine> {
217    pub state: ControlState<M>,
218    pub label: Label,
219    pub param_types: SmallVec<[WpType; 8]>,
220    pub return_types: SmallVec<[WpType; 1]>,
221    /// Value stack depth at the beginning of the frame (including params and results).
222    value_stack_depth: usize,
223}
224
225impl<M: Machine> ControlFrame<M> {
226    // Get value stack depth at the end of the frame.
227    fn value_stack_depth_after(&self) -> usize {
228        let mut depth: usize = self.value_stack_depth - self.param_types.len();
229
230        // For Loop, we have to use another slot for params that implements the PHI operation.
231        if matches!(self.state, ControlState::Loop) {
232            depth -= self.param_types.len();
233        }
234
235        depth
236    }
237
238    /// Returns the value stack depth at which resources should be deallocated.
239    /// For loops, this preserves PHI arguments by excluding them from deallocation.
240    fn value_stack_depth_for_release(&self) -> usize {
241        self.value_stack_depth - self.param_types.len()
242    }
243}
244
245fn type_to_wp_type(ty: &Type) -> WpType {
246    match ty {
247        Type::I32 => WpType::I32,
248        Type::I64 => WpType::I64,
249        Type::F32 => WpType::F32,
250        Type::F64 => WpType::F64,
251        Type::V128 => WpType::V128,
252        Type::ExternRef => WpType::Ref(WpRefType::new(true, WpHeapType::EXTERN).unwrap()),
253        Type::FuncRef => WpType::Ref(WpRefType::new(true, WpHeapType::FUNC).unwrap()),
254        Type::ExceptionRef => todo!(),
255    }
256}
257
258/// Abstraction for a 2-input, 1-output operator. Can be an integer/floating-point
259/// binop/cmpop.
260struct I2O1<R: Reg, S: Reg> {
261    loc_a: Location<R, S>,
262    loc_b: Location<R, S>,
263    ret: Location<R, S>,
264}
265
266/// Type of native call we emit.
267enum NativeCallType {
268    IncludeVMCtxArgument,
269    Unreachable,
270}
271
272const RED_ZONE_SIZE: usize = 32;
273
274impl<'a, M: Machine> FuncGen<'a, M> {
275    /// Acquires location from the machine state.
276    ///
277    /// If the returned location is used for stack value, `release_location` needs to be called on it;
278    /// Otherwise, if the returned locations is used for a local, `release_location` does not need to be called on it.
279    fn acquire_location(&mut self, ty: &WpType) -> Result<Location<M::GPR, M::SIMD>, CompileError> {
280        let loc = match *ty {
281            WpType::F32 | WpType::F64 => self.machine.pick_simd().map(Location::SIMD),
282            WpType::I32 | WpType::I64 => self.machine.pick_gpr().map(Location::GPR),
283            WpType::Ref(ty) if ty.is_extern_ref() || ty.is_func_ref() => {
284                self.machine.pick_gpr().map(Location::GPR)
285            }
286            _ => codegen_error!("can't acquire location for type {:?}", ty),
287        };
288
289        let Some(loc) = loc else {
290            return self.acquire_location_on_stack();
291        };
292
293        if let Location::GPR(x) = loc {
294            self.machine.reserve_gpr(x);
295        } else if let Location::SIMD(x) = loc {
296            self.machine.reserve_simd(x);
297        }
298        Ok(loc)
299    }
300
301    /// Acquire location that will live on the stack.
302    fn acquire_location_on_stack(&mut self) -> Result<Location<M::GPR, M::SIMD>, CompileError> {
303        self.stack_offset += 8;
304        let loc = self.machine.local_on_stack(self.stack_offset.get() as i32);
305        self.machine
306            .extend_stack(self.machine.round_stack_adjust(8) as u32)?;
307
308        Ok(loc)
309    }
310
311    /// Releases locations used for stack value.
312    fn release_locations(
313        &mut self,
314        locs: &[LocationWithCanonicalization<M>],
315    ) -> Result<(), CompileError> {
316        self.release_stack_locations(locs)?;
317        self.release_reg_locations(locs)
318    }
319
320    fn release_reg_locations(
321        &mut self,
322        locs: &[LocationWithCanonicalization<M>],
323    ) -> Result<(), CompileError> {
324        for (loc, _) in locs.iter().rev() {
325            match *loc {
326                Location::GPR(ref x) => {
327                    self.machine.release_gpr(*x);
328                }
329                Location::SIMD(ref x) => {
330                    self.machine.release_simd(*x);
331                }
332                _ => {}
333            }
334        }
335        Ok(())
336    }
337
338    fn release_stack_locations(
339        &mut self,
340        locs: &[LocationWithCanonicalization<M>],
341    ) -> Result<(), CompileError> {
342        for (loc, _) in locs.iter().rev() {
343            if let Location::Memory(..) = *loc {
344                self.check_location_on_stack(loc, self.stack_offset.get())?;
345                self.stack_offset -= 8;
346                self.machine
347                    .truncate_stack(self.machine.round_stack_adjust(8) as u32)?;
348            }
349        }
350
351        Ok(())
352    }
353
354    fn release_stack_locations_keep_stack_offset(
355        &mut self,
356        stack_depth: usize,
357    ) -> Result<(), CompileError> {
358        let mut stack_offset = self.stack_offset.get();
359        let locs = &self.value_stack[stack_depth..];
360
361        for (loc, _) in locs.iter().rev() {
362            if let Location::Memory(..) = *loc {
363                self.check_location_on_stack(loc, stack_offset)?;
364                stack_offset -= 8;
365                self.machine
366                    .truncate_stack(self.machine.round_stack_adjust(8) as u32)?;
367            }
368        }
369
370        Ok(())
371    }
372
373    fn check_location_on_stack(
374        &self,
375        loc: &Location<M::GPR, M::SIMD>,
376        expected_stack_offset: usize,
377    ) -> Result<(), CompileError> {
378        let Location::Memory(reg, offset) = loc else {
379            codegen_error!("Expected stack memory location");
380        };
381        if reg != &self.machine.local_pointer() {
382            codegen_error!("Expected location pointer for value on stack");
383        }
384        if *offset >= 0 {
385            codegen_error!("Invalid memory offset {offset}");
386        }
387        let offset = offset.neg() as usize;
388        if offset != expected_stack_offset {
389            codegen_error!(
390                "Invalid memory offset {offset}!={}",
391                self.stack_offset.get()
392            );
393        }
394        Ok(())
395    }
396
397    /// Allocate return slots for block operands (Block, If, Loop) and swap them with
398    /// the corresponding input parameters on the value stack.
399    ///
400    /// This method reserves memory slots that can accommodate both integer and
401    /// floating-point types, then swaps these slots with the last `stack_slots`
402    /// values on the stack to position them correctly for the block's return values.
403    /// that are already present at the value stack.
404    fn allocate_return_slots_and_swap(
405        &mut self,
406        stack_slots: usize,
407        return_slots: usize,
408    ) -> Result<(), CompileError> {
409        // No shuffling needed.
410        if return_slots == 0 {
411            return Ok(());
412        }
413
414        /* To allocate N return slots, we first allocate N additional stack (memory) slots and then "shift" the
415        existing stack slots. This results in the layout: [value stack before frame, ret0, ret1, ret2, ..., retN, arg0, arg1, ..., argN],
416        where some of the argN values may reside in registers and others in memory on the stack. */
417        let latest_slots = self
418            .value_stack
419            .drain(self.value_stack.len() - stack_slots..)
420            .collect_vec();
421        let extra_slots = (0..return_slots)
422            .map(|_| self.acquire_location_on_stack())
423            .collect::<Result<Vec<_>, _>>()?;
424
425        let mut all_memory_slots = latest_slots
426            .iter()
427            .filter_map(|(loc, _)| {
428                if let Location::Memory(..) = loc {
429                    Some(loc)
430                } else {
431                    None
432                }
433            })
434            .chain(extra_slots.iter())
435            .collect_vec();
436
437        // First put the newly allocated return values to the value stack.
438        self.value_stack.extend(
439            all_memory_slots
440                .iter()
441                .take(return_slots)
442                .map(|loc| (**loc, CanonicalizeType::None)),
443        );
444
445        // Then map all memory stack slots to a new location (in reverse order).
446        let mut new_params_reversed = Vec::new();
447        for (loc, canonicalize) in latest_slots.iter().rev() {
448            let mapped_loc = if matches!(loc, Location::Memory(..)) {
449                let dest = all_memory_slots.pop().unwrap();
450                self.machine.emit_relaxed_mov(Size::S64, *loc, *dest)?;
451                *dest
452            } else {
453                *loc
454            };
455            new_params_reversed.push((mapped_loc, *canonicalize));
456        }
457        self.value_stack
458            .extend(new_params_reversed.into_iter().rev());
459
460        Ok(())
461    }
462
463    #[allow(clippy::type_complexity)]
464    fn init_locals(
465        &mut self,
466        n: usize,
467        sig: FunctionType,
468        calling_convention: CallingConvention,
469    ) -> Result<Vec<Location<M::GPR, M::SIMD>>, CompileError> {
470        self.add_assembly_comment(AssemblyComment::InitializeLocals);
471
472        // How many machine stack slots will all the locals use?
473        let num_mem_slots = (0..n)
474            .filter(|&x| self.machine.is_local_on_stack(x))
475            .count();
476
477        // Total size (in bytes) of the pre-allocated "static area" for this function's
478        // locals and callee-saved registers.
479        let mut static_area_size: usize = 0;
480
481        // Callee-saved registers used for locals.
482        // Keep this consistent with the "Save callee-saved registers" code below.
483        for i in 0..n {
484            // If a local is not stored on stack, then it is allocated to a callee-saved register.
485            if !self.machine.is_local_on_stack(i) {
486                static_area_size += 8;
487            }
488        }
489
490        // Callee-saved vmctx.
491        static_area_size += 8;
492
493        // Some ABI (like Windows) needs extract reg save
494        static_area_size += 8 * self.machine.list_to_save(calling_convention).len();
495
496        // Total size of callee saved registers.
497        let callee_saved_regs_size = static_area_size;
498
499        // Now we can determine concrete locations for locals.
500        let locations: Vec<Location<M::GPR, M::SIMD>> = (0..n)
501            .map(|i| self.machine.get_local_location(i, callee_saved_regs_size))
502            .collect();
503
504        // Add size of locals on stack.
505        static_area_size += num_mem_slots * 8;
506
507        // Allocate save area, without actually writing to it.
508        static_area_size = self.machine.round_stack_adjust(static_area_size);
509
510        // Stack probe.
511        //
512        // `rep stosq` writes data from low address to high address and may skip the stack guard page.
513        // so here we probe it explicitly when needed.
514        for i in (sig.params().len()..n)
515            .step_by(NATIVE_PAGE_SIZE / 8)
516            .skip(1)
517        {
518            self.machine.zero_location(Size::S64, locations[i])?;
519        }
520
521        self.machine.extend_stack(static_area_size as _)?;
522
523        // Save callee-saved registers.
524        for loc in locations.iter() {
525            if let Location::GPR(_) = *loc {
526                self.stack_offset += 8;
527                self.machine
528                    .move_local(self.stack_offset.get() as i32, *loc)?;
529            }
530        }
531
532        // Save the Reg use for vmctx.
533        self.stack_offset += 8;
534        self.machine.move_local(
535            self.stack_offset.get() as i32,
536            Location::GPR(self.machine.get_vmctx_reg()),
537        )?;
538
539        // Check if need to same some CallingConvention specific regs
540        let regs_to_save = self.machine.list_to_save(calling_convention);
541        for loc in regs_to_save.iter() {
542            self.stack_offset += 8;
543            self.machine
544                .move_local(self.stack_offset.get() as i32, *loc)?;
545        }
546
547        // Save the offset of register save area.
548        self.save_area_offset = Some(self.stack_offset.get());
549
550        // Load in-register parameters into the allocated locations.
551        // Locals are allocated on the stack from higher address to lower address,
552        // so we won't skip the stack guard page here.
553        let mut stack_offset: usize = 0;
554        for (i, param) in sig.params().iter().enumerate() {
555            let sz = match *param {
556                Type::I32 | Type::F32 => Size::S32,
557                Type::I64 | Type::F64 => Size::S64,
558                Type::ExternRef | Type::FuncRef => Size::S64,
559                _ => {
560                    codegen_error!("singlepass init_local unimplemented type: {param}")
561                }
562            };
563            let loc = self.machine.get_call_param_location(
564                sig.results().len(),
565                i + 1,
566                sz,
567                &mut stack_offset,
568                calling_convention,
569            );
570            self.machine
571                .move_location_extend(sz, false, loc, Size::S64, locations[i])?;
572        }
573
574        // Load vmctx into it's GPR.
575        self.machine.move_location(
576            Size::S64,
577            Location::GPR(
578                self.machine
579                    .get_simple_param_location(0, calling_convention),
580            ),
581            Location::GPR(self.machine.get_vmctx_reg()),
582        )?;
583
584        // Initialize all normal locals to zero.
585        let mut init_stack_loc_cnt = 0;
586        let mut last_stack_loc = Location::Memory(self.machine.local_pointer(), i32::MAX);
587        for location in locations.iter().take(n).skip(sig.params().len()) {
588            match location {
589                Location::Memory(_, _) => {
590                    init_stack_loc_cnt += 1;
591                    last_stack_loc = cmp::min(last_stack_loc, *location);
592                }
593                Location::GPR(_) => {
594                    self.machine.zero_location(Size::S64, *location)?;
595                }
596                _ => codegen_error!("singlepass init_local unreachable"),
597            }
598        }
599        if init_stack_loc_cnt > 0 {
600            self.machine
601                .init_stack_loc(init_stack_loc_cnt, last_stack_loc)?;
602        }
603
604        // Add the size of all locals allocated to stack.
605        self.stack_offset += static_area_size - callee_saved_regs_size;
606
607        Ok(locations)
608    }
609
610    fn finalize_locals(
611        &mut self,
612        calling_convention: CallingConvention,
613    ) -> Result<(), CompileError> {
614        // Unwind stack to the "save area".
615        self.machine
616            .restore_saved_area(self.save_area_offset.unwrap() as i32)?;
617
618        let regs_to_save = self.machine.list_to_save(calling_convention);
619        for loc in regs_to_save.iter().rev() {
620            self.machine.pop_location(*loc)?;
621        }
622
623        // Restore register used by vmctx.
624        self.machine
625            .pop_location(Location::GPR(self.machine.get_vmctx_reg()))?;
626
627        // Restore callee-saved registers.
628        for loc in self.locals.iter().rev() {
629            if let Location::GPR(_) = *loc {
630                self.machine.pop_location(*loc)?;
631            }
632        }
633        Ok(())
634    }
635
636    /// Set the source location of the Wasm to the given offset.
637    pub fn set_srcloc(&mut self, offset: u32) {
638        self.machine.set_srcloc(offset);
639    }
640
641    fn get_location_released(
642        &mut self,
643        loc: (Location<M::GPR, M::SIMD>, CanonicalizeType),
644    ) -> Result<LocationWithCanonicalization<M>, CompileError> {
645        self.release_locations(&[loc])?;
646        Ok(loc)
647    }
648
649    fn pop_value_released(&mut self) -> Result<LocationWithCanonicalization<M>, CompileError> {
650        let loc = self.value_stack.pop().ok_or_else(|| {
651            CompileError::Codegen("pop_value_released: value stack is empty".to_owned())
652        })?;
653        self.get_location_released(loc)?;
654        Ok(loc)
655    }
656
657    /// Prepare data for binary operator with 2 inputs and 1 output.
658    fn i2o1_prepare(
659        &mut self,
660        ty: WpType,
661        canonicalize: CanonicalizeType,
662    ) -> Result<I2O1<M::GPR, M::SIMD>, CompileError> {
663        let loc_b = self.pop_value_released()?.0;
664        let loc_a = self.pop_value_released()?.0;
665        let ret = self.acquire_location(&ty)?;
666        self.value_stack.push((ret, canonicalize));
667        Ok(I2O1 { loc_a, loc_b, ret })
668    }
669
670    /// Emits a Native ABI call sequence.
671    ///
672    /// The caller MUST NOT hold any temporary registers allocated by `acquire_temp_gpr` when calling
673    /// this function.
674    fn emit_call_native<
675        I: Iterator<Item = (Location<M::GPR, M::SIMD>, CanonicalizeType)>,
676        J: Iterator<Item = WpType>,
677        K: Iterator<Item = WpType>,
678        F: FnOnce(&mut Self) -> Result<(), CompileError>,
679    >(
680        &mut self,
681        cb: F,
682        params: I,
683        params_type: J,
684        return_types: K,
685        call_type: NativeCallType,
686    ) -> Result<(), CompileError> {
687        let params = params.collect_vec();
688        let stack_params = params
689            .iter()
690            .copied()
691            .filter(|(param, _)| {
692                if let Location::Memory(reg, _) = param {
693                    debug_assert_eq!(reg, &self.machine.local_pointer());
694                    true
695                } else {
696                    false
697                }
698            })
699            .collect_vec();
700        let get_size = |param_type: WpType| match param_type {
701            WpType::F32 | WpType::I32 => Size::S32,
702            WpType::V128 => unimplemented!(),
703            _ => Size::S64,
704        };
705        let param_sizes = params_type.map(get_size).collect_vec();
706        let return_value_sizes = return_types.map(get_size).collect_vec();
707
708        /* We're going to reuse the memory param locations for the return values. Any extra needed slots will be allocated on stack. */
709        let used_stack_params = stack_params
710            .iter()
711            .take(return_value_sizes.len())
712            .copied()
713            .collect_vec();
714        let mut return_values = used_stack_params.clone();
715        let extra_return_values = (0..return_value_sizes.len().saturating_sub(stack_params.len()))
716            .map(|_| -> Result<_, CompileError> {
717                Ok((self.acquire_location_on_stack()?, CanonicalizeType::None))
718            })
719            .collect::<Result<Vec<_>, _>>()?;
720        return_values.extend(extra_return_values);
721
722        // Release the parameter slots that live in registers.
723        self.release_reg_locations(&params)?;
724
725        // Save used GPRs. Preserve correct stack alignment
726        let used_gprs = self.machine.get_used_gprs();
727        let mut used_stack = self.machine.push_used_gpr(&used_gprs)?;
728
729        // Save used SIMD registers.
730        let used_simds = self.machine.get_used_simd();
731        if !used_simds.is_empty() {
732            used_stack += self.machine.push_used_simd(&used_simds)?;
733        }
734        // mark the GPR used for Call as used
735        self.machine
736            .reserve_unused_temp_gpr(self.machine.get_gpr_for_call());
737
738        let calling_convention = self.calling_convention;
739
740        let stack_padding: usize = match calling_convention {
741            CallingConvention::WindowsFastcall => 32,
742            _ => 0,
743        };
744
745        let mut stack_offset: usize = 0;
746        // Allocate space for return values relative to SP (the allocation happens in reverse order, thus start with return slots).
747        let mut return_args = Vec::with_capacity(return_value_sizes.len());
748        for i in 0..return_value_sizes.len() {
749            return_args.push(self.machine.get_return_value_location(
750                i,
751                &mut stack_offset,
752                self.calling_convention,
753            ));
754        }
755
756        // Allocate space for arguments relative to SP.
757        let mut args = Vec::with_capacity(params.len());
758        for (i, param_size) in param_sizes.iter().enumerate() {
759            args.push(self.machine.get_param_location(
760                match call_type {
761                    NativeCallType::IncludeVMCtxArgument => 1,
762                    NativeCallType::Unreachable => 0,
763                } + i,
764                *param_size,
765                &mut stack_offset,
766                calling_convention,
767            ));
768        }
769
770        // Align stack to 16 bytes.
771        let stack_unaligned =
772            (self.machine.round_stack_adjust(self.stack_offset.get()) + used_stack + stack_offset)
773                % 16;
774        if stack_unaligned != 0 {
775            stack_offset += 16 - stack_unaligned;
776        }
777        self.machine.extend_stack(stack_offset as u32)?;
778
779        #[allow(clippy::type_complexity)]
780        let mut call_movs = Vec::new();
781        // Prepare register & stack parameters.
782        for (i, ((param, _), param_size)) in
783            (params.iter().zip(param_sizes.iter())).enumerate().rev()
784        {
785            let loc = args[i];
786            match loc {
787                Location::GPR(x) => {
788                    call_movs.push((*param, x, *param_size));
789                }
790                Location::Memory(_, _) => {
791                    self.machine
792                        .move_location_for_native(param_sizes[i], *param, loc)?;
793                }
794                _ => {
795                    return Err(CompileError::Codegen(
796                        "emit_call_native loc: unreachable code".to_owned(),
797                    ));
798                }
799            }
800        }
801
802        // Sort register moves so that register are not overwritten before read.
803        Self::sort_call_movs(&mut call_movs);
804
805        // Emit register moves.
806        for (loc, gpr, size) in call_movs {
807            if loc != Location::GPR(gpr) {
808                self.machine
809                    .move_location(Size::S64, loc, Location::GPR(gpr))?;
810            }
811            // Adjust the argument if required by ABI
812            self.machine.adjust_gpr_param_location(gpr, size)?;
813        }
814
815        if matches!(call_type, NativeCallType::IncludeVMCtxArgument) {
816            // Put vmctx as the first parameter.
817            self.machine.move_location(
818                Size::S64,
819                Location::GPR(self.machine.get_vmctx_reg()),
820                Location::GPR(
821                    self.machine
822                        .get_simple_param_location(0, calling_convention),
823                ),
824            )?; // vmctx
825        }
826
827        if stack_padding > 0 {
828            self.machine.extend_stack(stack_padding as u32)?;
829        }
830        self.stack_offset
831            .track_temporary_extra_allocation(stack_offset + stack_padding + used_stack);
832        // release the GPR used for call
833        self.machine.release_gpr(self.machine.get_gpr_for_call());
834
835        let begin = self.machine.assembler_get_offset().0;
836        cb(self)?;
837        if matches!(call_type, NativeCallType::Unreachable) {
838            let end = self.machine.assembler_get_offset().0;
839            self.machine.mark_address_range_with_trap_code(
840                TrapCode::UnreachableCodeReached,
841                begin,
842                end,
843            );
844        }
845
846        // Take the returned values from the fn call.
847        for (i, &return_type) in return_value_sizes.iter().enumerate() {
848            self.machine.move_location_for_native(
849                return_type,
850                return_args[i],
851                return_values[i].0,
852            )?;
853        }
854
855        // Restore stack.
856        if stack_offset + stack_padding > 0 {
857            self.machine
858                .truncate_stack((stack_offset + stack_padding) as u32)?;
859        }
860
861        // Restore SIMDs.
862        if !used_simds.is_empty() {
863            self.machine.pop_used_simd(&used_simds)?;
864        }
865
866        // Restore GPRs.
867        self.machine.pop_used_gpr(&used_gprs)?;
868
869        // We are re-using the params for the return values, thus release just the chunk
870        // we're not planning to use!
871        let params_to_release =
872            &stack_params[cmp::min(stack_params.len(), return_value_sizes.len())..];
873        self.release_stack_locations(params_to_release)?;
874
875        self.value_stack.extend(return_values);
876
877        Ok(())
878    }
879
880    /// Emits a memory operation.
881    fn op_memory<
882        F: FnOnce(&mut Self, bool, bool, i32, Label, Label) -> Result<(), CompileError>,
883    >(
884        &mut self,
885        cb: F,
886    ) -> Result<(), CompileError> {
887        let need_check = match self.memory_styles[MemoryIndex::new(0)] {
888            MemoryStyle::Static { .. } => false,
889            MemoryStyle::Dynamic { .. } => true,
890        };
891
892        let offset = if self.module.num_imported_memories != 0 {
893            self.vmoffsets
894                .vmctx_vmmemory_import_definition(MemoryIndex::new(0))
895        } else {
896            self.vmoffsets
897                .vmctx_vmmemory_definition(LocalMemoryIndex::new(0))
898        };
899        cb(
900            self,
901            need_check,
902            self.module.num_imported_memories != 0,
903            offset as i32,
904            self.special_labels.heap_access_oob,
905            self.special_labels.unaligned_atomic,
906        )
907    }
908
909    fn emit_head(&mut self) -> Result<(), CompileError> {
910        self.add_assembly_comment(AssemblyComment::FunctionPrologue);
911        self.machine.emit_function_prolog()?;
912
913        // Initialize locals.
914        self.locals = self.init_locals(
915            self.local_types.len(),
916            self.signature.clone(),
917            self.calling_convention,
918        )?;
919
920        // simulate "red zone" if not supported by the platform
921        self.add_assembly_comment(AssemblyComment::RedZone);
922        self.stack_offset += RED_ZONE_SIZE;
923        self.machine.extend_stack(RED_ZONE_SIZE as u32)?;
924
925        let return_types: SmallVec<_> = self
926            .signature
927            .results()
928            .iter()
929            .map(type_to_wp_type)
930            .collect();
931
932        // Push return value slots for the function return on the stack.
933        self.value_stack.extend((0..return_types.len()).map(|i| {
934            (
935                self.machine
936                    .get_call_return_value_location(i, self.calling_convention),
937                CanonicalizeType::None,
938            )
939        }));
940
941        self.control_stack.push(ControlFrame {
942            state: ControlState::Function,
943            label: self.machine.get_label(),
944            value_stack_depth: return_types.len(),
945            param_types: smallvec![],
946            return_types,
947        });
948
949        // TODO: Full preemption by explicit signal checking
950
951        // We insert set StackOverflow as the default trap that can happen
952        // anywhere in the function prologue.
953        self.machine.insert_stackoverflow();
954        self.add_assembly_comment(AssemblyComment::FunctionBody);
955
956        Ok(())
957    }
958
959    #[allow(clippy::too_many_arguments)]
960    pub fn new(
961        module: &'a ModuleInfo,
962        config: &'a Singlepass,
963        vmoffsets: &'a VMOffsets,
964        memory_styles: &'a PrimaryMap<MemoryIndex, MemoryStyle>,
965        _table_styles: &'a PrimaryMap<TableIndex, TableStyle>,
966        local_func_index: LocalFunctionIndex,
967        local_types_excluding_arguments: &[WpType],
968        machine: M,
969        calling_convention: CallingConvention,
970    ) -> Result<FuncGen<'a, M>, CompileError> {
971        let func_index = module.func_index(local_func_index);
972        let sig_index = module.functions[func_index];
973        let signature = module.signatures[sig_index].clone();
974
975        let mut local_types: Vec<_> = signature.params().iter().map(type_to_wp_type).collect();
976        local_types.extend_from_slice(local_types_excluding_arguments);
977
978        let mut machine = machine;
979        let special_labels = SpecialLabelSet {
980            integer_division_by_zero: machine.get_label(),
981            integer_overflow: machine.get_label(),
982            heap_access_oob: machine.get_label(),
983            table_access_oob: machine.get_label(),
984            indirect_call_null: machine.get_label(),
985            bad_signature: machine.get_label(),
986            unaligned_atomic: machine.get_label(),
987        };
988        let function_name = module
989            .function_names
990            .get(&func_index)
991            .map(|fname| fname.to_string())
992            .unwrap_or_else(|| format!("function_{}", func_index.as_u32()));
993
994        let mut fg = FuncGen {
995            module,
996            config,
997            vmoffsets,
998            memory_styles,
999            // table_styles,
1000            signature,
1001            locals: vec![], // initialization deferred to emit_head
1002            local_types,
1003            value_stack: vec![],
1004            control_stack: vec![],
1005            stack_offset: TrackedStackOffset::default(),
1006            save_area_offset: None,
1007            machine,
1008            unreachable_depth: 0,
1009            local_func_index,
1010            relocations: vec![],
1011            special_labels,
1012            calling_convention,
1013            function_name,
1014            assembly_comments: HashMap::new(),
1015        };
1016        fg.emit_head()?;
1017        Ok(fg)
1018    }
1019
1020    pub fn has_control_frames(&self) -> bool {
1021        !self.control_stack.is_empty()
1022    }
1023
1024    /// Moves the top `return_values` items from the value stack into the
1025    /// preallocated return slots starting at `value_stack_depth_after`.
1026    ///
1027    /// Used when completing Block/If/Loop constructs or returning from the
1028    /// function. Applies NaN canonicalization when enabled and supported.
1029    fn emit_return_values(
1030        &mut self,
1031        value_stack_depth_after: usize,
1032        return_values: usize,
1033    ) -> Result<(), CompileError> {
1034        for (i, (stack_value, canonicalize)) in self
1035            .value_stack
1036            .iter()
1037            .rev()
1038            .take(return_values)
1039            .enumerate()
1040        {
1041            let dst = self.value_stack[value_stack_depth_after - i - 1].0;
1042            if let Some(canonicalize_size) = canonicalize.to_size()
1043                && self.config.enable_nan_canonicalization
1044            {
1045                self.machine
1046                    .canonicalize_nan(canonicalize_size, *stack_value, dst)?;
1047            } else {
1048                self.machine
1049                    .emit_relaxed_mov(Size::S64, *stack_value, dst)?;
1050            }
1051        }
1052
1053        Ok(())
1054    }
1055
1056    /// Similar to `emit_return_values`, except it stores the `return_values` items into the slots
1057    /// preallocated for parameters of a loop.
1058    fn emit_loop_params_store(
1059        &mut self,
1060        value_stack_depth_after: usize,
1061        param_count: usize,
1062    ) -> Result<(), CompileError> {
1063        for (i, (stack_value, _)) in self
1064            .value_stack
1065            .iter()
1066            .rev()
1067            .take(param_count)
1068            .rev()
1069            .enumerate()
1070        {
1071            let dst = self.value_stack[value_stack_depth_after + i].0;
1072            self.machine
1073                .emit_relaxed_mov(Size::S64, *stack_value, dst)?;
1074        }
1075
1076        Ok(())
1077    }
1078
1079    fn return_types_for_block(&self, block_type: WpTypeOrFuncType) -> SmallVec<[WpType; 1]> {
1080        match block_type {
1081            WpTypeOrFuncType::Empty => smallvec![],
1082            WpTypeOrFuncType::Type(inner_ty) => smallvec![inner_ty],
1083            WpTypeOrFuncType::FuncType(sig_index) => SmallVec::from_iter(
1084                self.module.signatures[SignatureIndex::from_u32(sig_index)]
1085                    .results()
1086                    .iter()
1087                    .map(type_to_wp_type),
1088            ),
1089        }
1090    }
1091
1092    fn param_types_for_block(&self, block_type: WpTypeOrFuncType) -> SmallVec<[WpType; 8]> {
1093        match block_type {
1094            WpTypeOrFuncType::Empty | WpTypeOrFuncType::Type(_) => smallvec![],
1095            WpTypeOrFuncType::FuncType(sig_index) => SmallVec::from_iter(
1096                self.module.signatures[SignatureIndex::from_u32(sig_index)]
1097                    .params()
1098                    .iter()
1099                    .map(type_to_wp_type),
1100            ),
1101        }
1102    }
1103
1104    pub fn feed_operator(&mut self, op: Operator) -> Result<(), CompileError> {
1105        let was_unreachable;
1106
1107        if self.unreachable_depth > 0 {
1108            was_unreachable = true;
1109
1110            match op {
1111                Operator::Block { .. } | Operator::Loop { .. } | Operator::If { .. } => {
1112                    self.unreachable_depth += 1;
1113                }
1114                Operator::End => {
1115                    self.unreachable_depth -= 1;
1116                }
1117                Operator::Else
1118                    if self.unreachable_depth == 1
1119                        && self.control_stack.last().is_some_and(|frame| {
1120                            matches!(frame.state, ControlState::If { .. })
1121                        }) =>
1122                {
1123                    // We are in a reachable true branch
1124                    self.unreachable_depth -= 1;
1125                }
1126
1127                _ => {}
1128            }
1129            if self.unreachable_depth > 0 {
1130                return Ok(());
1131            }
1132        } else {
1133            was_unreachable = false;
1134        }
1135
1136        match op {
1137            Operator::GlobalGet { global_index } => {
1138                let global_index = GlobalIndex::from_u32(global_index);
1139
1140                let ty = type_to_wp_type(&self.module.globals[global_index].ty);
1141                let loc = self.acquire_location(&ty)?;
1142                self.value_stack.push((loc, CanonicalizeType::None));
1143
1144                let (src, tmp) = if let Some(local_global_index) =
1145                    self.module.local_global_index(global_index)
1146                {
1147                    let offset = self.vmoffsets.vmctx_vmglobal_definition(local_global_index);
1148                    (
1149                        Location::Memory(self.machine.get_vmctx_reg(), offset as i32),
1150                        None,
1151                    )
1152                } else {
1153                    // Imported globals require one level of indirection.
1154                    let tmp = self.machine.acquire_temp_gpr().unwrap();
1155                    let offset = self
1156                        .vmoffsets
1157                        .vmctx_vmglobal_import_definition(global_index);
1158                    self.machine.emit_relaxed_mov(
1159                        Size::S64,
1160                        Location::Memory(self.machine.get_vmctx_reg(), offset as i32),
1161                        Location::GPR(tmp),
1162                    )?;
1163                    (Location::Memory(tmp, 0), Some(tmp))
1164                };
1165
1166                self.machine.emit_relaxed_mov(Size::S64, src, loc)?;
1167
1168                if let Some(tmp) = tmp {
1169                    self.machine.release_gpr(tmp);
1170                }
1171            }
1172            Operator::GlobalSet { global_index } => {
1173                let global_index = GlobalIndex::from_u32(global_index);
1174                let (dst, tmp) = if let Some(local_global_index) =
1175                    self.module.local_global_index(global_index)
1176                {
1177                    let offset = self.vmoffsets.vmctx_vmglobal_definition(local_global_index);
1178                    (
1179                        Location::Memory(self.machine.get_vmctx_reg(), offset as i32),
1180                        None,
1181                    )
1182                } else {
1183                    // Imported globals require one level of indirection.
1184                    let tmp = self.machine.acquire_temp_gpr().unwrap();
1185                    let offset = self
1186                        .vmoffsets
1187                        .vmctx_vmglobal_import_definition(global_index);
1188                    self.machine.emit_relaxed_mov(
1189                        Size::S64,
1190                        Location::Memory(self.machine.get_vmctx_reg(), offset as i32),
1191                        Location::GPR(tmp),
1192                    )?;
1193                    (Location::Memory(tmp, 0), Some(tmp))
1194                };
1195                let (loc, canonicalize) = self.pop_value_released()?;
1196                if let Some(canonicalize_size) = canonicalize.to_size() {
1197                    if self.config.enable_nan_canonicalization {
1198                        self.machine.canonicalize_nan(canonicalize_size, loc, dst)?;
1199                    } else {
1200                        self.machine.emit_relaxed_mov(Size::S64, loc, dst)?;
1201                    }
1202                } else {
1203                    self.machine.emit_relaxed_mov(Size::S64, loc, dst)?;
1204                }
1205                if let Some(tmp) = tmp {
1206                    self.machine.release_gpr(tmp);
1207                }
1208            }
1209            Operator::LocalGet { local_index } => {
1210                let local_index = local_index as usize;
1211                let ret = self.acquire_location(&WpType::I64)?;
1212                self.machine
1213                    .emit_relaxed_mov(Size::S64, self.locals[local_index], ret)?;
1214                self.value_stack.push((ret, CanonicalizeType::None));
1215            }
1216            Operator::LocalSet { local_index } => {
1217                let local_index = local_index as usize;
1218                let (loc, canonicalize) = self.pop_value_released()?;
1219
1220                if self.local_types[local_index].is_float()
1221                    && let Some(canonicalize_size) = canonicalize.to_size()
1222                {
1223                    if self.config.enable_nan_canonicalization {
1224                        self.machine.canonicalize_nan(
1225                            canonicalize_size,
1226                            loc,
1227                            self.locals[local_index],
1228                        )
1229                    } else {
1230                        self.machine
1231                            .emit_relaxed_mov(Size::S64, loc, self.locals[local_index])
1232                    }
1233                } else {
1234                    self.machine
1235                        .emit_relaxed_mov(Size::S64, loc, self.locals[local_index])
1236                }?;
1237            }
1238            Operator::LocalTee { local_index } => {
1239                let local_index = local_index as usize;
1240                let (loc, canonicalize) = *self.value_stack.last().unwrap();
1241
1242                if self.local_types[local_index].is_float()
1243                    && let Some(canonicalize_size) = canonicalize.to_size()
1244                {
1245                    if self.config.enable_nan_canonicalization {
1246                        self.machine.canonicalize_nan(
1247                            canonicalize_size,
1248                            loc,
1249                            self.locals[local_index],
1250                        )
1251                    } else {
1252                        self.machine
1253                            .emit_relaxed_mov(Size::S64, loc, self.locals[local_index])
1254                    }
1255                } else {
1256                    self.machine
1257                        .emit_relaxed_mov(Size::S64, loc, self.locals[local_index])
1258                }?;
1259            }
1260            Operator::I32Const { value } => {
1261                self.value_stack
1262                    .push((Location::Imm32(value as u32), CanonicalizeType::None));
1263            }
1264            Operator::I32Add => {
1265                let I2O1 { loc_a, loc_b, ret } =
1266                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1267                self.machine.emit_binop_add32(loc_a, loc_b, ret)?;
1268            }
1269            Operator::I32Sub => {
1270                let I2O1 { loc_a, loc_b, ret } =
1271                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1272                self.machine.emit_binop_sub32(loc_a, loc_b, ret)?;
1273            }
1274            Operator::I32Mul => {
1275                let I2O1 { loc_a, loc_b, ret } =
1276                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1277                self.machine.emit_binop_mul32(loc_a, loc_b, ret)?;
1278            }
1279            Operator::I32DivU => {
1280                let I2O1 { loc_a, loc_b, ret } =
1281                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1282                self.machine.emit_binop_udiv32(
1283                    loc_a,
1284                    loc_b,
1285                    ret,
1286                    self.special_labels.integer_division_by_zero,
1287                )?;
1288            }
1289            Operator::I32DivS => {
1290                let I2O1 { loc_a, loc_b, ret } =
1291                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1292                self.machine.emit_binop_sdiv32(
1293                    loc_a,
1294                    loc_b,
1295                    ret,
1296                    self.special_labels.integer_division_by_zero,
1297                    self.special_labels.integer_overflow,
1298                )?;
1299            }
1300            Operator::I32RemU => {
1301                let I2O1 { loc_a, loc_b, ret } =
1302                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1303                self.machine.emit_binop_urem32(
1304                    loc_a,
1305                    loc_b,
1306                    ret,
1307                    self.special_labels.integer_division_by_zero,
1308                )?;
1309            }
1310            Operator::I32RemS => {
1311                let I2O1 { loc_a, loc_b, ret } =
1312                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1313                self.machine.emit_binop_srem32(
1314                    loc_a,
1315                    loc_b,
1316                    ret,
1317                    self.special_labels.integer_division_by_zero,
1318                )?;
1319            }
1320            Operator::I32And => {
1321                let I2O1 { loc_a, loc_b, ret } =
1322                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1323                self.machine.emit_binop_and32(loc_a, loc_b, ret)?;
1324            }
1325            Operator::I32Or => {
1326                let I2O1 { loc_a, loc_b, ret } =
1327                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1328                self.machine.emit_binop_or32(loc_a, loc_b, ret)?;
1329            }
1330            Operator::I32Xor => {
1331                let I2O1 { loc_a, loc_b, ret } =
1332                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1333                self.machine.emit_binop_xor32(loc_a, loc_b, ret)?;
1334            }
1335            Operator::I32Eq => {
1336                let I2O1 { loc_a, loc_b, ret } =
1337                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1338                self.machine.i32_cmp_eq(loc_a, loc_b, ret)?;
1339            }
1340            Operator::I32Ne => {
1341                let I2O1 { loc_a, loc_b, ret } =
1342                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1343                self.machine.i32_cmp_ne(loc_a, loc_b, ret)?;
1344            }
1345            Operator::I32Eqz => {
1346                let loc_a = self.pop_value_released()?.0;
1347                let ret = self.acquire_location(&WpType::I32)?;
1348                self.machine.i32_cmp_eq(loc_a, Location::Imm32(0), ret)?;
1349                self.value_stack.push((ret, CanonicalizeType::None));
1350            }
1351            Operator::I32Clz => {
1352                let loc = self.pop_value_released()?.0;
1353                let ret = self.acquire_location(&WpType::I32)?;
1354                self.value_stack.push((ret, CanonicalizeType::None));
1355                self.machine.i32_clz(loc, ret)?;
1356            }
1357            Operator::I32Ctz => {
1358                let loc = self.pop_value_released()?.0;
1359                let ret = self.acquire_location(&WpType::I32)?;
1360                self.value_stack.push((ret, CanonicalizeType::None));
1361                self.machine.i32_ctz(loc, ret)?;
1362            }
1363            Operator::I32Popcnt => {
1364                let loc = self.pop_value_released()?.0;
1365                let ret = self.acquire_location(&WpType::I32)?;
1366                self.value_stack.push((ret, CanonicalizeType::None));
1367                self.machine.i32_popcnt(loc, ret)?;
1368            }
1369            Operator::I32Shl => {
1370                let I2O1 { loc_a, loc_b, ret } =
1371                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1372                self.machine.i32_shl(loc_a, loc_b, ret)?;
1373            }
1374            Operator::I32ShrU => {
1375                let I2O1 { loc_a, loc_b, ret } =
1376                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1377                self.machine.i32_shr(loc_a, loc_b, ret)?;
1378            }
1379            Operator::I32ShrS => {
1380                let I2O1 { loc_a, loc_b, ret } =
1381                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1382                self.machine.i32_sar(loc_a, loc_b, ret)?;
1383            }
1384            Operator::I32Rotl => {
1385                let I2O1 { loc_a, loc_b, ret } =
1386                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1387                self.machine.i32_rol(loc_a, loc_b, ret)?;
1388            }
1389            Operator::I32Rotr => {
1390                let I2O1 { loc_a, loc_b, ret } =
1391                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1392                self.machine.i32_ror(loc_a, loc_b, ret)?;
1393            }
1394            Operator::I32LtU => {
1395                let I2O1 { loc_a, loc_b, ret } =
1396                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1397                self.machine.i32_cmp_lt_u(loc_a, loc_b, ret)?;
1398            }
1399            Operator::I32LeU => {
1400                let I2O1 { loc_a, loc_b, ret } =
1401                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1402                self.machine.i32_cmp_le_u(loc_a, loc_b, ret)?;
1403            }
1404            Operator::I32GtU => {
1405                let I2O1 { loc_a, loc_b, ret } =
1406                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1407                self.machine.i32_cmp_gt_u(loc_a, loc_b, ret)?;
1408            }
1409            Operator::I32GeU => {
1410                let I2O1 { loc_a, loc_b, ret } =
1411                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1412                self.machine.i32_cmp_ge_u(loc_a, loc_b, ret)?;
1413            }
1414            Operator::I32LtS => {
1415                let I2O1 { loc_a, loc_b, ret } =
1416                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1417                self.machine.i32_cmp_lt_s(loc_a, loc_b, ret)?;
1418            }
1419            Operator::I32LeS => {
1420                let I2O1 { loc_a, loc_b, ret } =
1421                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1422                self.machine.i32_cmp_le_s(loc_a, loc_b, ret)?;
1423            }
1424            Operator::I32GtS => {
1425                let I2O1 { loc_a, loc_b, ret } =
1426                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1427                self.machine.i32_cmp_gt_s(loc_a, loc_b, ret)?;
1428            }
1429            Operator::I32GeS => {
1430                let I2O1 { loc_a, loc_b, ret } =
1431                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1432                self.machine.i32_cmp_ge_s(loc_a, loc_b, ret)?;
1433            }
1434            Operator::I64Const { value } => {
1435                let value = value as u64;
1436                self.value_stack
1437                    .push((Location::Imm64(value), CanonicalizeType::None));
1438            }
1439            Operator::I64Add => {
1440                let I2O1 { loc_a, loc_b, ret } =
1441                    self.i2o1_prepare(WpType::I64, CanonicalizeType::None)?;
1442                self.machine.emit_binop_add64(loc_a, loc_b, ret)?;
1443            }
1444            Operator::I64Sub => {
1445                let I2O1 { loc_a, loc_b, ret } =
1446                    self.i2o1_prepare(WpType::I64, CanonicalizeType::None)?;
1447                self.machine.emit_binop_sub64(loc_a, loc_b, ret)?;
1448            }
1449            Operator::I64Mul => {
1450                let I2O1 { loc_a, loc_b, ret } =
1451                    self.i2o1_prepare(WpType::I64, CanonicalizeType::None)?;
1452                self.machine.emit_binop_mul64(loc_a, loc_b, ret)?;
1453            }
1454            Operator::I64DivU => {
1455                let I2O1 { loc_a, loc_b, ret } =
1456                    self.i2o1_prepare(WpType::I64, CanonicalizeType::None)?;
1457                self.machine.emit_binop_udiv64(
1458                    loc_a,
1459                    loc_b,
1460                    ret,
1461                    self.special_labels.integer_division_by_zero,
1462                )?;
1463            }
1464            Operator::I64DivS => {
1465                let I2O1 { loc_a, loc_b, ret } =
1466                    self.i2o1_prepare(WpType::I64, CanonicalizeType::None)?;
1467                self.machine.emit_binop_sdiv64(
1468                    loc_a,
1469                    loc_b,
1470                    ret,
1471                    self.special_labels.integer_division_by_zero,
1472                    self.special_labels.integer_overflow,
1473                )?;
1474            }
1475            Operator::I64RemU => {
1476                let I2O1 { loc_a, loc_b, ret } =
1477                    self.i2o1_prepare(WpType::I64, CanonicalizeType::None)?;
1478                self.machine.emit_binop_urem64(
1479                    loc_a,
1480                    loc_b,
1481                    ret,
1482                    self.special_labels.integer_division_by_zero,
1483                )?;
1484            }
1485            Operator::I64RemS => {
1486                let I2O1 { loc_a, loc_b, ret } =
1487                    self.i2o1_prepare(WpType::I64, CanonicalizeType::None)?;
1488                self.machine.emit_binop_srem64(
1489                    loc_a,
1490                    loc_b,
1491                    ret,
1492                    self.special_labels.integer_division_by_zero,
1493                )?;
1494            }
1495            Operator::I64And => {
1496                let I2O1 { loc_a, loc_b, ret } =
1497                    self.i2o1_prepare(WpType::I64, CanonicalizeType::None)?;
1498                self.machine.emit_binop_and64(loc_a, loc_b, ret)?;
1499            }
1500            Operator::I64Or => {
1501                let I2O1 { loc_a, loc_b, ret } =
1502                    self.i2o1_prepare(WpType::I64, CanonicalizeType::None)?;
1503                self.machine.emit_binop_or64(loc_a, loc_b, ret)?;
1504            }
1505            Operator::I64Xor => {
1506                let I2O1 { loc_a, loc_b, ret } =
1507                    self.i2o1_prepare(WpType::I64, CanonicalizeType::None)?;
1508                self.machine.emit_binop_xor64(loc_a, loc_b, ret)?;
1509            }
1510            Operator::I64Eq => {
1511                let I2O1 { loc_a, loc_b, ret } =
1512                    self.i2o1_prepare(WpType::I64, CanonicalizeType::None)?;
1513                self.machine.i64_cmp_eq(loc_a, loc_b, ret)?;
1514            }
1515            Operator::I64Ne => {
1516                let I2O1 { loc_a, loc_b, ret } =
1517                    self.i2o1_prepare(WpType::I64, CanonicalizeType::None)?;
1518                self.machine.i64_cmp_ne(loc_a, loc_b, ret)?;
1519            }
1520            Operator::I64Eqz => {
1521                let loc_a = self.pop_value_released()?.0;
1522                let ret = self.acquire_location(&WpType::I64)?;
1523                self.machine.i64_cmp_eq(loc_a, Location::Imm64(0), ret)?;
1524                self.value_stack.push((ret, CanonicalizeType::None));
1525            }
1526            Operator::I64Clz => {
1527                let loc = self.pop_value_released()?.0;
1528                let ret = self.acquire_location(&WpType::I64)?;
1529                self.value_stack.push((ret, CanonicalizeType::None));
1530                self.machine.i64_clz(loc, ret)?;
1531            }
1532            Operator::I64Ctz => {
1533                let loc = self.pop_value_released()?.0;
1534                let ret = self.acquire_location(&WpType::I64)?;
1535                self.value_stack.push((ret, CanonicalizeType::None));
1536                self.machine.i64_ctz(loc, ret)?;
1537            }
1538            Operator::I64Popcnt => {
1539                let loc = self.pop_value_released()?.0;
1540                let ret = self.acquire_location(&WpType::I64)?;
1541                self.value_stack.push((ret, CanonicalizeType::None));
1542                self.machine.i64_popcnt(loc, ret)?;
1543            }
1544            Operator::I64Shl => {
1545                let I2O1 { loc_a, loc_b, ret } =
1546                    self.i2o1_prepare(WpType::I64, CanonicalizeType::None)?;
1547                self.machine.i64_shl(loc_a, loc_b, ret)?;
1548            }
1549            Operator::I64ShrU => {
1550                let I2O1 { loc_a, loc_b, ret } =
1551                    self.i2o1_prepare(WpType::I64, CanonicalizeType::None)?;
1552                self.machine.i64_shr(loc_a, loc_b, ret)?;
1553            }
1554            Operator::I64ShrS => {
1555                let I2O1 { loc_a, loc_b, ret } =
1556                    self.i2o1_prepare(WpType::I64, CanonicalizeType::None)?;
1557                self.machine.i64_sar(loc_a, loc_b, ret)?;
1558            }
1559            Operator::I64Rotl => {
1560                let I2O1 { loc_a, loc_b, ret } =
1561                    self.i2o1_prepare(WpType::I64, CanonicalizeType::None)?;
1562                self.machine.i64_rol(loc_a, loc_b, ret)?;
1563            }
1564            Operator::I64Rotr => {
1565                let I2O1 { loc_a, loc_b, ret } =
1566                    self.i2o1_prepare(WpType::I64, CanonicalizeType::None)?;
1567                self.machine.i64_ror(loc_a, loc_b, ret)?;
1568            }
1569            Operator::I64LtU => {
1570                let I2O1 { loc_a, loc_b, ret } =
1571                    self.i2o1_prepare(WpType::I64, CanonicalizeType::None)?;
1572                self.machine.i64_cmp_lt_u(loc_a, loc_b, ret)?;
1573            }
1574            Operator::I64LeU => {
1575                let I2O1 { loc_a, loc_b, ret } =
1576                    self.i2o1_prepare(WpType::I64, CanonicalizeType::None)?;
1577                self.machine.i64_cmp_le_u(loc_a, loc_b, ret)?;
1578            }
1579            Operator::I64GtU => {
1580                let I2O1 { loc_a, loc_b, ret } =
1581                    self.i2o1_prepare(WpType::I64, CanonicalizeType::None)?;
1582                self.machine.i64_cmp_gt_u(loc_a, loc_b, ret)?;
1583            }
1584            Operator::I64GeU => {
1585                let I2O1 { loc_a, loc_b, ret } =
1586                    self.i2o1_prepare(WpType::I64, CanonicalizeType::None)?;
1587                self.machine.i64_cmp_ge_u(loc_a, loc_b, ret)?;
1588            }
1589            Operator::I64LtS => {
1590                let I2O1 { loc_a, loc_b, ret } =
1591                    self.i2o1_prepare(WpType::I64, CanonicalizeType::None)?;
1592                self.machine.i64_cmp_lt_s(loc_a, loc_b, ret)?;
1593            }
1594            Operator::I64LeS => {
1595                let I2O1 { loc_a, loc_b, ret } =
1596                    self.i2o1_prepare(WpType::I64, CanonicalizeType::None)?;
1597                self.machine.i64_cmp_le_s(loc_a, loc_b, ret)?;
1598            }
1599            Operator::I64GtS => {
1600                let I2O1 { loc_a, loc_b, ret } =
1601                    self.i2o1_prepare(WpType::I64, CanonicalizeType::None)?;
1602                self.machine.i64_cmp_gt_s(loc_a, loc_b, ret)?;
1603            }
1604            Operator::I64GeS => {
1605                let I2O1 { loc_a, loc_b, ret } =
1606                    self.i2o1_prepare(WpType::I64, CanonicalizeType::None)?;
1607                self.machine.i64_cmp_ge_s(loc_a, loc_b, ret)?;
1608            }
1609            Operator::I64ExtendI32U => {
1610                let loc = self.pop_value_released()?.0;
1611                let ret = self.acquire_location(&WpType::I64)?;
1612                self.value_stack.push((ret, CanonicalizeType::None));
1613                self.machine.emit_relaxed_mov(Size::S32, loc, ret)?;
1614
1615                // A 32-bit memory write does not automatically clear the upper 32 bits of a 64-bit word.
1616                // So, we need to explicitly write zero to the upper half here.
1617                if let Location::Memory(base, off) = ret {
1618                    self.machine.emit_relaxed_mov(
1619                        Size::S32,
1620                        Location::Imm32(0),
1621                        Location::Memory(base, off + 4),
1622                    )?;
1623                }
1624            }
1625            Operator::I64ExtendI32S => {
1626                let loc = self.pop_value_released()?.0;
1627                let ret = self.acquire_location(&WpType::I64)?;
1628                self.value_stack.push((ret, CanonicalizeType::None));
1629                self.machine
1630                    .emit_relaxed_sign_extension(Size::S32, loc, Size::S64, ret)?;
1631            }
1632            Operator::I32Extend8S => {
1633                let loc = self.pop_value_released()?.0;
1634                let ret = self.acquire_location(&WpType::I32)?;
1635                self.value_stack.push((ret, CanonicalizeType::None));
1636
1637                self.machine
1638                    .emit_relaxed_sign_extension(Size::S8, loc, Size::S32, ret)?;
1639            }
1640            Operator::I32Extend16S => {
1641                let loc = self.pop_value_released()?.0;
1642                let ret = self.acquire_location(&WpType::I32)?;
1643                self.value_stack.push((ret, CanonicalizeType::None));
1644
1645                self.machine
1646                    .emit_relaxed_sign_extension(Size::S16, loc, Size::S32, ret)?;
1647            }
1648            Operator::I64Extend8S => {
1649                let loc = self.pop_value_released()?.0;
1650                let ret = self.acquire_location(&WpType::I64)?;
1651                self.value_stack.push((ret, CanonicalizeType::None));
1652
1653                self.machine
1654                    .emit_relaxed_sign_extension(Size::S8, loc, Size::S64, ret)?;
1655            }
1656            Operator::I64Extend16S => {
1657                let loc = self.pop_value_released()?.0;
1658                let ret = self.acquire_location(&WpType::I64)?;
1659                self.value_stack.push((ret, CanonicalizeType::None));
1660
1661                self.machine
1662                    .emit_relaxed_sign_extension(Size::S16, loc, Size::S64, ret)?;
1663            }
1664            Operator::I64Extend32S => {
1665                let loc = self.pop_value_released()?.0;
1666                let ret = self.acquire_location(&WpType::I64)?;
1667                self.value_stack.push((ret, CanonicalizeType::None));
1668
1669                self.machine
1670                    .emit_relaxed_sign_extension(Size::S32, loc, Size::S64, ret)?;
1671            }
1672            Operator::I32WrapI64 => {
1673                let loc = self.pop_value_released()?.0;
1674                let ret = self.acquire_location(&WpType::I32)?;
1675                self.value_stack.push((ret, CanonicalizeType::None));
1676                self.machine.emit_relaxed_mov(Size::S32, loc, ret)?;
1677            }
1678
1679            Operator::F32Const { value } => {
1680                self.value_stack
1681                    .push((Location::Imm32(value.bits()), CanonicalizeType::None));
1682            }
1683            Operator::F32Add => {
1684                let I2O1 { loc_a, loc_b, ret } =
1685                    self.i2o1_prepare(WpType::F64, CanonicalizeType::F32)?;
1686                self.machine.f32_add(loc_a, loc_b, ret)?;
1687            }
1688            Operator::F32Sub => {
1689                let I2O1 { loc_a, loc_b, ret } =
1690                    self.i2o1_prepare(WpType::F64, CanonicalizeType::F32)?;
1691                self.machine.f32_sub(loc_a, loc_b, ret)?;
1692            }
1693            Operator::F32Mul => {
1694                let I2O1 { loc_a, loc_b, ret } =
1695                    self.i2o1_prepare(WpType::F64, CanonicalizeType::F32)?;
1696                self.machine.f32_mul(loc_a, loc_b, ret)?;
1697            }
1698            Operator::F32Div => {
1699                let I2O1 { loc_a, loc_b, ret } =
1700                    self.i2o1_prepare(WpType::F64, CanonicalizeType::F32)?;
1701                self.machine.f32_div(loc_a, loc_b, ret)?;
1702            }
1703            Operator::F32Max => {
1704                let I2O1 { loc_a, loc_b, ret } =
1705                    self.i2o1_prepare(WpType::F64, CanonicalizeType::None)?;
1706                self.machine.f32_max(loc_a, loc_b, ret)?;
1707            }
1708            Operator::F32Min => {
1709                let I2O1 { loc_a, loc_b, ret } =
1710                    self.i2o1_prepare(WpType::F64, CanonicalizeType::None)?;
1711                self.machine.f32_min(loc_a, loc_b, ret)?;
1712            }
1713            Operator::F32Eq => {
1714                let I2O1 { loc_a, loc_b, ret } =
1715                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1716                self.machine.f32_cmp_eq(loc_a, loc_b, ret)?;
1717            }
1718            Operator::F32Ne => {
1719                let I2O1 { loc_a, loc_b, ret } =
1720                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1721                self.machine.f32_cmp_ne(loc_a, loc_b, ret)?;
1722            }
1723            Operator::F32Lt => {
1724                let I2O1 { loc_a, loc_b, ret } =
1725                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1726                self.machine.f32_cmp_lt(loc_a, loc_b, ret)?;
1727            }
1728            Operator::F32Le => {
1729                let I2O1 { loc_a, loc_b, ret } =
1730                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1731                self.machine.f32_cmp_le(loc_a, loc_b, ret)?;
1732            }
1733            Operator::F32Gt => {
1734                let I2O1 { loc_a, loc_b, ret } =
1735                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1736                self.machine.f32_cmp_gt(loc_a, loc_b, ret)?;
1737            }
1738            Operator::F32Ge => {
1739                let I2O1 { loc_a, loc_b, ret } =
1740                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1741                self.machine.f32_cmp_ge(loc_a, loc_b, ret)?;
1742            }
1743            Operator::F32Nearest => {
1744                let loc = self.pop_value_released()?.0;
1745                let ret = self.acquire_location(&WpType::F64)?;
1746                self.value_stack.push((ret, CanonicalizeType::F32));
1747                self.machine.f32_nearest(loc, ret)?;
1748            }
1749            Operator::F32Floor => {
1750                let loc = self.pop_value_released()?.0;
1751                let ret = self.acquire_location(&WpType::F64)?;
1752                self.value_stack.push((ret, CanonicalizeType::F32));
1753                self.machine.f32_floor(loc, ret)?;
1754            }
1755            Operator::F32Ceil => {
1756                let loc = self.pop_value_released()?.0;
1757                let ret = self.acquire_location(&WpType::F64)?;
1758                self.value_stack.push((ret, CanonicalizeType::F32));
1759                self.machine.f32_ceil(loc, ret)?;
1760            }
1761            Operator::F32Trunc => {
1762                let loc = self.pop_value_released()?.0;
1763                let ret = self.acquire_location(&WpType::F64)?;
1764                self.value_stack.push((ret, CanonicalizeType::F32));
1765                self.machine.f32_trunc(loc, ret)?;
1766            }
1767            Operator::F32Sqrt => {
1768                let loc = self.pop_value_released()?.0;
1769                let ret = self.acquire_location(&WpType::F64)?;
1770                self.value_stack.push((ret, CanonicalizeType::F32));
1771                self.machine.f32_sqrt(loc, ret)?;
1772            }
1773
1774            Operator::F32Copysign => {
1775                let loc_b = self.pop_value_released()?;
1776                let loc_a = self.pop_value_released()?;
1777                let ret = self.acquire_location(&WpType::F32)?;
1778                self.value_stack.push((ret, CanonicalizeType::None));
1779
1780                let tmp1 = self.machine.acquire_temp_gpr().unwrap();
1781                let tmp2 = self.machine.acquire_temp_gpr().unwrap();
1782
1783                if self.config.enable_nan_canonicalization {
1784                    for ((loc, fp), tmp) in [(loc_a, tmp1), (loc_b, tmp2)] {
1785                        if fp.to_size().is_some() {
1786                            self.machine
1787                                .canonicalize_nan(Size::S32, loc, Location::GPR(tmp))?
1788                        } else {
1789                            self.machine
1790                                .move_location(Size::S32, loc, Location::GPR(tmp))?
1791                        }
1792                    }
1793                } else {
1794                    self.machine
1795                        .move_location(Size::S32, loc_a.0, Location::GPR(tmp1))?;
1796                    self.machine
1797                        .move_location(Size::S32, loc_b.0, Location::GPR(tmp2))?;
1798                }
1799                self.machine.emit_i32_copysign(tmp1, tmp2)?;
1800                self.machine
1801                    .move_location(Size::S32, Location::GPR(tmp1), ret)?;
1802                self.machine.release_gpr(tmp2);
1803                self.machine.release_gpr(tmp1);
1804            }
1805
1806            Operator::F32Abs => {
1807                // Preserve canonicalization state.
1808
1809                let loc = self.pop_value_released()?.0;
1810                let ret = self.acquire_location(&WpType::F32)?;
1811                self.value_stack.push((ret, CanonicalizeType::None));
1812
1813                self.machine.f32_abs(loc, ret)?;
1814            }
1815
1816            Operator::F32Neg => {
1817                // Preserve canonicalization state.
1818
1819                let loc = self.pop_value_released()?.0;
1820                let ret = self.acquire_location(&WpType::F32)?;
1821                self.value_stack.push((ret, CanonicalizeType::None));
1822
1823                self.machine.f32_neg(loc, ret)?;
1824            }
1825
1826            Operator::F64Const { value } => {
1827                self.value_stack
1828                    .push((Location::Imm64(value.bits()), CanonicalizeType::None));
1829            }
1830            Operator::F64Add => {
1831                let I2O1 { loc_a, loc_b, ret } =
1832                    self.i2o1_prepare(WpType::F64, CanonicalizeType::F64)?;
1833                self.machine.f64_add(loc_a, loc_b, ret)?;
1834            }
1835            Operator::F64Sub => {
1836                let I2O1 { loc_a, loc_b, ret } =
1837                    self.i2o1_prepare(WpType::F64, CanonicalizeType::F64)?;
1838                self.machine.f64_sub(loc_a, loc_b, ret)?;
1839            }
1840            Operator::F64Mul => {
1841                let I2O1 { loc_a, loc_b, ret } =
1842                    self.i2o1_prepare(WpType::F64, CanonicalizeType::F64)?;
1843                self.machine.f64_mul(loc_a, loc_b, ret)?;
1844            }
1845            Operator::F64Div => {
1846                let I2O1 { loc_a, loc_b, ret } =
1847                    self.i2o1_prepare(WpType::F64, CanonicalizeType::F64)?;
1848                self.machine.f64_div(loc_a, loc_b, ret)?;
1849            }
1850            Operator::F64Max => {
1851                let I2O1 { loc_a, loc_b, ret } =
1852                    self.i2o1_prepare(WpType::F64, CanonicalizeType::None)?;
1853                self.machine.f64_max(loc_a, loc_b, ret)?;
1854            }
1855            Operator::F64Min => {
1856                let I2O1 { loc_a, loc_b, ret } =
1857                    self.i2o1_prepare(WpType::F64, CanonicalizeType::None)?;
1858                self.machine.f64_min(loc_a, loc_b, ret)?;
1859            }
1860            Operator::F64Eq => {
1861                let I2O1 { loc_a, loc_b, ret } =
1862                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1863                self.machine.f64_cmp_eq(loc_a, loc_b, ret)?;
1864            }
1865            Operator::F64Ne => {
1866                let I2O1 { loc_a, loc_b, ret } =
1867                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1868                self.machine.f64_cmp_ne(loc_a, loc_b, ret)?;
1869            }
1870            Operator::F64Lt => {
1871                let I2O1 { loc_a, loc_b, ret } =
1872                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1873                self.machine.f64_cmp_lt(loc_a, loc_b, ret)?;
1874            }
1875            Operator::F64Le => {
1876                let I2O1 { loc_a, loc_b, ret } =
1877                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1878                self.machine.f64_cmp_le(loc_a, loc_b, ret)?;
1879            }
1880            Operator::F64Gt => {
1881                let I2O1 { loc_a, loc_b, ret } =
1882                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1883                self.machine.f64_cmp_gt(loc_a, loc_b, ret)?;
1884            }
1885            Operator::F64Ge => {
1886                let I2O1 { loc_a, loc_b, ret } =
1887                    self.i2o1_prepare(WpType::I32, CanonicalizeType::None)?;
1888                self.machine.f64_cmp_ge(loc_a, loc_b, ret)?;
1889            }
1890            Operator::F64Nearest => {
1891                let loc = self.pop_value_released()?.0;
1892                let ret = self.acquire_location(&WpType::F64)?;
1893                self.value_stack.push((ret, CanonicalizeType::F64));
1894                self.machine.f64_nearest(loc, ret)?;
1895            }
1896            Operator::F64Floor => {
1897                let loc = self.pop_value_released()?.0;
1898                let ret = self.acquire_location(&WpType::F64)?;
1899                self.value_stack.push((ret, CanonicalizeType::F64));
1900                self.machine.f64_floor(loc, ret)?;
1901            }
1902            Operator::F64Ceil => {
1903                let loc = self.pop_value_released()?.0;
1904                let ret = self.acquire_location(&WpType::F64)?;
1905                self.value_stack.push((ret, CanonicalizeType::F64));
1906                self.machine.f64_ceil(loc, ret)?;
1907            }
1908            Operator::F64Trunc => {
1909                let loc = self.pop_value_released()?.0;
1910                let ret = self.acquire_location(&WpType::F64)?;
1911                self.value_stack.push((ret, CanonicalizeType::F64));
1912                self.machine.f64_trunc(loc, ret)?;
1913            }
1914            Operator::F64Sqrt => {
1915                let loc = self.pop_value_released()?.0;
1916                let ret = self.acquire_location(&WpType::F64)?;
1917                self.value_stack.push((ret, CanonicalizeType::F64));
1918                self.machine.f64_sqrt(loc, ret)?;
1919            }
1920
1921            Operator::F64Copysign => {
1922                let loc_b = self.pop_value_released()?;
1923                let loc_a = self.pop_value_released()?;
1924                let ret = self.acquire_location(&WpType::F64)?;
1925                self.value_stack.push((ret, CanonicalizeType::None));
1926
1927                let tmp1 = self.machine.acquire_temp_gpr().unwrap();
1928                let tmp2 = self.machine.acquire_temp_gpr().unwrap();
1929
1930                if self.config.enable_nan_canonicalization {
1931                    for ((loc, fp), tmp) in [(loc_a, tmp1), (loc_b, tmp2)] {
1932                        if fp.to_size().is_some() {
1933                            self.machine
1934                                .canonicalize_nan(Size::S64, loc, Location::GPR(tmp))?
1935                        } else {
1936                            self.machine
1937                                .move_location(Size::S64, loc, Location::GPR(tmp))?
1938                        }
1939                    }
1940                } else {
1941                    self.machine
1942                        .move_location(Size::S64, loc_a.0, Location::GPR(tmp1))?;
1943                    self.machine
1944                        .move_location(Size::S64, loc_b.0, Location::GPR(tmp2))?;
1945                }
1946                self.machine.emit_i64_copysign(tmp1, tmp2)?;
1947                self.machine
1948                    .move_location(Size::S64, Location::GPR(tmp1), ret)?;
1949
1950                self.machine.release_gpr(tmp2);
1951                self.machine.release_gpr(tmp1);
1952            }
1953
1954            Operator::F64Abs => {
1955                let (loc, canonicalize) = self.pop_value_released()?;
1956                let ret = self.acquire_location(&WpType::F64)?;
1957                self.value_stack.push((ret, canonicalize));
1958
1959                self.machine.f64_abs(loc, ret)?;
1960            }
1961
1962            Operator::F64Neg => {
1963                let (loc, canonicalize) = self.pop_value_released()?;
1964                let ret = self.acquire_location(&WpType::F64)?;
1965                self.value_stack.push((ret, canonicalize));
1966
1967                self.machine.f64_neg(loc, ret)?;
1968            }
1969
1970            Operator::F64PromoteF32 => {
1971                let (loc, canonicalize) = self.pop_value_released()?;
1972                let ret = self.acquire_location(&WpType::F64)?;
1973                self.value_stack.push((ret, canonicalize.promote()?));
1974                self.machine.convert_f64_f32(loc, ret)?;
1975            }
1976            Operator::F32DemoteF64 => {
1977                let (loc, canonicalize) = self.pop_value_released()?;
1978                let ret = self.acquire_location(&WpType::F64)?;
1979                self.value_stack.push((ret, canonicalize.demote()?));
1980                self.machine.convert_f32_f64(loc, ret)?;
1981            }
1982
1983            Operator::I32ReinterpretF32 => {
1984                let (loc, canonicalize) = self.pop_value_released()?;
1985                let ret = self.acquire_location(&WpType::I32)?;
1986                self.value_stack.push((ret, CanonicalizeType::None));
1987
1988                if !self.config.enable_nan_canonicalization
1989                    || matches!(canonicalize, CanonicalizeType::None)
1990                {
1991                    if loc != ret {
1992                        self.machine.emit_relaxed_mov(Size::S32, loc, ret)?;
1993                    }
1994                } else {
1995                    self.machine.canonicalize_nan(Size::S32, loc, ret)?;
1996                }
1997            }
1998            Operator::F32ReinterpretI32 => {
1999                let loc = self.pop_value_released()?.0;
2000                let ret = self.acquire_location(&WpType::F32)?;
2001                self.value_stack.push((ret, CanonicalizeType::None));
2002
2003                if loc != ret {
2004                    self.machine.emit_relaxed_mov(Size::S32, loc, ret)?;
2005                }
2006            }
2007
2008            Operator::I64ReinterpretF64 => {
2009                let (loc, canonicalize) = self.pop_value_released()?;
2010                let ret = self.acquire_location(&WpType::I64)?;
2011                self.value_stack.push((ret, CanonicalizeType::None));
2012
2013                if !self.config.enable_nan_canonicalization
2014                    || matches!(canonicalize, CanonicalizeType::None)
2015                {
2016                    if loc != ret {
2017                        self.machine.emit_relaxed_mov(Size::S64, loc, ret)?;
2018                    }
2019                } else {
2020                    self.machine.canonicalize_nan(Size::S64, loc, ret)?;
2021                }
2022            }
2023            Operator::F64ReinterpretI64 => {
2024                let loc = self.pop_value_released()?.0;
2025                let ret = self.acquire_location(&WpType::F64)?;
2026                self.value_stack.push((ret, CanonicalizeType::None));
2027
2028                if loc != ret {
2029                    self.machine.emit_relaxed_mov(Size::S64, loc, ret)?;
2030                }
2031            }
2032
2033            Operator::I32TruncF32U => {
2034                let loc = self.pop_value_released()?.0;
2035                let ret = self.acquire_location(&WpType::I32)?;
2036                self.value_stack.push((ret, CanonicalizeType::None));
2037
2038                self.machine.convert_i32_f32(loc, ret, false, false)?;
2039            }
2040
2041            Operator::I32TruncSatF32U => {
2042                let loc = self.pop_value_released()?.0;
2043                let ret = self.acquire_location(&WpType::I32)?;
2044                self.value_stack.push((ret, CanonicalizeType::None));
2045
2046                self.machine.convert_i32_f32(loc, ret, false, true)?;
2047            }
2048
2049            Operator::I32TruncF32S => {
2050                let loc = self.pop_value_released()?.0;
2051                let ret = self.acquire_location(&WpType::I32)?;
2052                self.value_stack.push((ret, CanonicalizeType::None));
2053
2054                self.machine.convert_i32_f32(loc, ret, true, false)?;
2055            }
2056            Operator::I32TruncSatF32S => {
2057                let loc = self.pop_value_released()?.0;
2058                let ret = self.acquire_location(&WpType::I32)?;
2059                self.value_stack.push((ret, CanonicalizeType::None));
2060
2061                self.machine.convert_i32_f32(loc, ret, true, true)?;
2062            }
2063
2064            Operator::I64TruncF32S => {
2065                let loc = self.pop_value_released()?.0;
2066                let ret = self.acquire_location(&WpType::I64)?;
2067                self.value_stack.push((ret, CanonicalizeType::None));
2068
2069                self.machine.convert_i64_f32(loc, ret, true, false)?;
2070            }
2071
2072            Operator::I64TruncSatF32S => {
2073                let loc = self.pop_value_released()?.0;
2074                let ret = self.acquire_location(&WpType::I64)?;
2075                self.value_stack.push((ret, CanonicalizeType::None));
2076
2077                self.machine.convert_i64_f32(loc, ret, true, true)?;
2078            }
2079
2080            Operator::I64TruncF32U => {
2081                let loc = self.pop_value_released()?.0;
2082                let ret = self.acquire_location(&WpType::I64)?;
2083                self.value_stack.push((ret, CanonicalizeType::None));
2084
2085                self.machine.convert_i64_f32(loc, ret, false, false)?;
2086            }
2087            Operator::I64TruncSatF32U => {
2088                let loc = self.pop_value_released()?.0;
2089                let ret = self.acquire_location(&WpType::I64)?;
2090                self.value_stack.push((ret, CanonicalizeType::None));
2091
2092                self.machine.convert_i64_f32(loc, ret, false, true)?;
2093            }
2094
2095            Operator::I32TruncF64U => {
2096                let loc = self.pop_value_released()?.0;
2097                let ret = self.acquire_location(&WpType::I32)?;
2098                self.value_stack.push((ret, CanonicalizeType::None));
2099
2100                self.machine.convert_i32_f64(loc, ret, false, false)?;
2101            }
2102
2103            Operator::I32TruncSatF64U => {
2104                let loc = self.pop_value_released()?.0;
2105                let ret = self.acquire_location(&WpType::I32)?;
2106                self.value_stack.push((ret, CanonicalizeType::None));
2107
2108                self.machine.convert_i32_f64(loc, ret, false, true)?;
2109            }
2110
2111            Operator::I32TruncF64S => {
2112                let loc = self.pop_value_released()?.0;
2113                let ret = self.acquire_location(&WpType::I32)?;
2114                self.value_stack.push((ret, CanonicalizeType::None));
2115
2116                self.machine.convert_i32_f64(loc, ret, true, false)?;
2117            }
2118
2119            Operator::I32TruncSatF64S => {
2120                let loc = self.pop_value_released()?.0;
2121                let ret = self.acquire_location(&WpType::I32)?;
2122                self.value_stack.push((ret, CanonicalizeType::None));
2123
2124                self.machine.convert_i32_f64(loc, ret, true, true)?;
2125            }
2126
2127            Operator::I64TruncF64S => {
2128                let loc = self.pop_value_released()?.0;
2129                let ret = self.acquire_location(&WpType::I64)?;
2130                self.value_stack.push((ret, CanonicalizeType::None));
2131
2132                self.machine.convert_i64_f64(loc, ret, true, false)?;
2133            }
2134
2135            Operator::I64TruncSatF64S => {
2136                let loc = self.pop_value_released()?.0;
2137                let ret = self.acquire_location(&WpType::I64)?;
2138                self.value_stack.push((ret, CanonicalizeType::None));
2139
2140                self.machine.convert_i64_f64(loc, ret, true, true)?;
2141            }
2142
2143            Operator::I64TruncF64U => {
2144                let loc = self.pop_value_released()?.0;
2145                let ret = self.acquire_location(&WpType::I64)?;
2146                self.value_stack.push((ret, CanonicalizeType::None));
2147
2148                self.machine.convert_i64_f64(loc, ret, false, false)?;
2149            }
2150
2151            Operator::I64TruncSatF64U => {
2152                let loc = self.pop_value_released()?.0;
2153                let ret = self.acquire_location(&WpType::I64)?;
2154                self.value_stack.push((ret, CanonicalizeType::None));
2155
2156                self.machine.convert_i64_f64(loc, ret, false, true)?;
2157            }
2158
2159            Operator::F32ConvertI32S => {
2160                let loc = self.pop_value_released()?.0;
2161                let ret = self.acquire_location(&WpType::F32)?;
2162                self.value_stack.push((ret, CanonicalizeType::None));
2163
2164                self.machine.convert_f32_i32(loc, true, ret)?;
2165            }
2166            Operator::F32ConvertI32U => {
2167                let loc = self.pop_value_released()?.0;
2168                let ret = self.acquire_location(&WpType::F32)?;
2169                self.value_stack.push((ret, CanonicalizeType::None));
2170
2171                self.machine.convert_f32_i32(loc, false, ret)?;
2172            }
2173            Operator::F32ConvertI64S => {
2174                let loc = self.pop_value_released()?.0;
2175                let ret = self.acquire_location(&WpType::F32)?;
2176                self.value_stack.push((ret, CanonicalizeType::None));
2177
2178                self.machine.convert_f32_i64(loc, true, ret)?;
2179            }
2180            Operator::F32ConvertI64U => {
2181                let loc = self.pop_value_released()?.0;
2182                let ret = self.acquire_location(&WpType::F32)?;
2183                self.value_stack.push((ret, CanonicalizeType::None));
2184
2185                self.machine.convert_f32_i64(loc, false, ret)?;
2186            }
2187
2188            Operator::F64ConvertI32S => {
2189                let loc = self.pop_value_released()?.0;
2190                let ret = self.acquire_location(&WpType::F64)?;
2191                self.value_stack.push((ret, CanonicalizeType::None));
2192
2193                self.machine.convert_f64_i32(loc, true, ret)?;
2194            }
2195            Operator::F64ConvertI32U => {
2196                let loc = self.pop_value_released()?.0;
2197                let ret = self.acquire_location(&WpType::F64)?;
2198                self.value_stack.push((ret, CanonicalizeType::None));
2199
2200                self.machine.convert_f64_i32(loc, false, ret)?;
2201            }
2202            Operator::F64ConvertI64S => {
2203                let loc = self.pop_value_released()?.0;
2204                let ret = self.acquire_location(&WpType::F64)?;
2205                self.value_stack.push((ret, CanonicalizeType::None));
2206
2207                self.machine.convert_f64_i64(loc, true, ret)?;
2208            }
2209            Operator::F64ConvertI64U => {
2210                let loc = self.pop_value_released()?.0;
2211                let ret = self.acquire_location(&WpType::F64)?;
2212                self.value_stack.push((ret, CanonicalizeType::None));
2213
2214                self.machine.convert_f64_i64(loc, false, ret)?;
2215            }
2216
2217            Operator::Call { function_index } => {
2218                let function_index = function_index as usize;
2219
2220                let sig_index = *self
2221                    .module
2222                    .functions
2223                    .get(FunctionIndex::new(function_index))
2224                    .unwrap();
2225                let sig = self.module.signatures.get(sig_index).unwrap();
2226                let param_types: SmallVec<[WpType; 8]> =
2227                    sig.params().iter().map(type_to_wp_type).collect();
2228                let return_types: SmallVec<[WpType; 1]> =
2229                    sig.results().iter().map(type_to_wp_type).collect();
2230
2231                let params: SmallVec<[_; 8]> = self
2232                    .value_stack
2233                    .drain(self.value_stack.len() - param_types.len()..)
2234                    .collect();
2235
2236                // Pop arguments off the FP stack and canonicalize them if needed.
2237                //
2238                // Canonicalization state will be lost across function calls, so early canonicalization
2239                // is necessary here.
2240                if self.config.enable_nan_canonicalization {
2241                    for (loc, canonicalize) in params.iter() {
2242                        if let Some(size) = canonicalize.to_size() {
2243                            self.machine.canonicalize_nan(size, *loc, *loc)?;
2244                        }
2245                    }
2246                }
2247
2248                // Imported functions are called through trampolines placed as custom sections.
2249                let reloc_target = if function_index < self.module.num_imported_functions {
2250                    RelocationTarget::CustomSection(SectionIndex::new(function_index))
2251                } else {
2252                    RelocationTarget::LocalFunc(LocalFunctionIndex::new(
2253                        function_index - self.module.num_imported_functions,
2254                    ))
2255                };
2256                let calling_convention = self.calling_convention;
2257
2258                self.emit_call_native(
2259                    |this| {
2260                        let offset = this
2261                            .machine
2262                            .mark_instruction_with_trap_code(TrapCode::StackOverflow);
2263                        let mut relocations = this
2264                            .machine
2265                            .emit_call_with_reloc(calling_convention, reloc_target)?;
2266                        this.machine.mark_instruction_address_end(offset);
2267                        this.relocations.append(&mut relocations);
2268                        Ok(())
2269                    },
2270                    params.iter().copied(),
2271                    param_types.iter().copied(),
2272                    return_types.iter().copied(),
2273                    NativeCallType::IncludeVMCtxArgument,
2274                )?;
2275            }
2276            Operator::CallIndirect {
2277                type_index,
2278                table_index,
2279            } => {
2280                // TODO: removed restriction on always being table idx 0;
2281                // does any code depend on this?
2282                let table_index = TableIndex::new(table_index as _);
2283                let index = SignatureIndex::new(type_index as usize);
2284                let sig = self.module.signatures.get(index).unwrap();
2285                let expected_sig_hash = self.module.signature_hashes.get(index).unwrap();
2286                let table = self.module.tables.get(table_index).unwrap();
2287                let local_fixed_funcref_table = self
2288                    .module
2289                    .local_table_index(table_index)
2290                    .filter(|_| table.is_fixed_funcref_table());
2291                let param_types: SmallVec<[WpType; 8]> =
2292                    sig.params().iter().map(type_to_wp_type).collect();
2293                let return_types: SmallVec<[WpType; 1]> =
2294                    sig.results().iter().map(type_to_wp_type).collect();
2295
2296                let func_index = self.pop_value_released()?.0;
2297
2298                let params: SmallVec<[_; 8]> = self
2299                    .value_stack
2300                    .drain(self.value_stack.len() - param_types.len()..)
2301                    .collect();
2302
2303                // Pop arguments off the FP stack and canonicalize them if needed.
2304                //
2305                // Canonicalization state will be lost across function calls, so early canonicalization
2306                // is necessary here.
2307                if self.config.enable_nan_canonicalization {
2308                    for (loc, canonicalize) in params.iter() {
2309                        if let Some(size) = canonicalize.to_size() {
2310                            self.machine.canonicalize_nan(size, *loc, *loc)?;
2311                        }
2312                    }
2313                }
2314
2315                let table_base = self.machine.acquire_temp_gpr().unwrap();
2316                let table_count = self.machine.acquire_temp_gpr().unwrap();
2317                let sig_hash = self.machine.acquire_temp_gpr().unwrap();
2318
2319                if let Some(local_table_index) = local_fixed_funcref_table {
2320                    self.machine.move_location(
2321                        Size::S64,
2322                        Location::GPR(self.machine.get_vmctx_reg()),
2323                        Location::GPR(table_base),
2324                    )?;
2325                    self.machine.location_add(
2326                        Size::S64,
2327                        Location::Imm32(
2328                            self.vmoffsets
2329                                .vmctx_fixed_funcref_table_anyfuncs(local_table_index)
2330                                .expect("fixed funcref table must have inline VMContext storage"),
2331                        ),
2332                        Location::GPR(table_base),
2333                        false,
2334                    )?;
2335                    self.machine.move_location(
2336                        Size::S32,
2337                        Location::Imm32(table.minimum),
2338                        Location::GPR(table_count),
2339                    )?;
2340                } else if let Some(local_table_index) = self.module.local_table_index(table_index) {
2341                    let (vmctx_offset_base, vmctx_offset_len) = (
2342                        self.vmoffsets.vmctx_vmtable_definition(local_table_index),
2343                        self.vmoffsets
2344                            .vmctx_vmtable_definition_current_elements(local_table_index),
2345                    );
2346                    self.machine.move_location(
2347                        Size::S64,
2348                        Location::Memory(self.machine.get_vmctx_reg(), vmctx_offset_base as i32),
2349                        Location::GPR(table_base),
2350                    )?;
2351                    self.machine.move_location(
2352                        Size::S32,
2353                        Location::Memory(self.machine.get_vmctx_reg(), vmctx_offset_len as i32),
2354                        Location::GPR(table_count),
2355                    )?;
2356                } else {
2357                    // Do an indirection.
2358                    let import_offset = self.vmoffsets.vmctx_vmtable_import(table_index);
2359                    self.machine.move_location(
2360                        Size::S64,
2361                        Location::Memory(self.machine.get_vmctx_reg(), import_offset as i32),
2362                        Location::GPR(table_base),
2363                    )?;
2364
2365                    // Load len.
2366                    self.machine.move_location(
2367                        Size::S32,
2368                        Location::Memory(
2369                            table_base,
2370                            self.vmoffsets.vmtable_definition_current_elements() as _,
2371                        ),
2372                        Location::GPR(table_count),
2373                    )?;
2374
2375                    // Load base.
2376                    self.machine.move_location(
2377                        Size::S64,
2378                        Location::Memory(table_base, self.vmoffsets.vmtable_definition_base() as _),
2379                        Location::GPR(table_base),
2380                    )?;
2381                }
2382
2383                self.machine.jmp_on_condition(
2384                    UnsignedCondition::BelowEqual,
2385                    Size::S32,
2386                    Location::GPR(table_count),
2387                    func_index,
2388                    self.special_labels.table_access_oob,
2389                )?;
2390                self.machine
2391                    .move_location(Size::S32, func_index, Location::GPR(table_count))?;
2392                self.machine.emit_imul_imm32(
2393                    Size::S64,
2394                    if local_fixed_funcref_table.is_some() {
2395                        self.vmoffsets.size_of_vmcaller_checked_anyfunc() as u32
2396                    } else {
2397                        self.vmoffsets.size_of_vm_funcref() as u32
2398                    },
2399                    table_count,
2400                )?;
2401                self.machine.location_add(
2402                    Size::S64,
2403                    Location::GPR(table_base),
2404                    Location::GPR(table_count),
2405                    false,
2406                )?;
2407
2408                if local_fixed_funcref_table.is_some() {
2409                    self.machine.move_location(
2410                        Size::S64,
2411                        Location::Memory(
2412                            table_count,
2413                            self.vmoffsets.vmcaller_checked_anyfunc_func_ptr() as i32,
2414                        ),
2415                        Location::GPR(table_base),
2416                    )?;
2417                    self.machine.jmp_on_condition(
2418                        UnsignedCondition::Equal,
2419                        Size::S64,
2420                        Location::GPR(table_base),
2421                        Location::Imm32(0),
2422                        self.special_labels.indirect_call_null,
2423                    )?;
2424                } else {
2425                    // deref the table to get a VMFuncRef
2426                    self.machine.move_location(
2427                        Size::S64,
2428                        Location::Memory(
2429                            table_count,
2430                            self.vmoffsets.vm_funcref_anyfunc_ptr() as i32,
2431                        ),
2432                        Location::GPR(table_count),
2433                    )?;
2434                    // Trap if the FuncRef is null
2435                    self.machine.jmp_on_condition(
2436                        UnsignedCondition::Equal,
2437                        Size::S64,
2438                        Location::GPR(table_count),
2439                        Location::Imm32(0),
2440                        self.special_labels.indirect_call_null,
2441                    )?;
2442                }
2443                self.machine.move_location(
2444                    Size::S32,
2445                    Location::Imm32(expected_sig_hash.as_u32()),
2446                    Location::GPR(sig_hash),
2447                )?;
2448
2449                // Trap if signature mismatches.
2450                self.machine.jmp_on_condition(
2451                    UnsignedCondition::NotEqual,
2452                    Size::S32,
2453                    Location::GPR(sig_hash),
2454                    Location::Memory(
2455                        table_count,
2456                        (self.vmoffsets.vmcaller_checked_anyfunc_signature_hash() as usize) as i32,
2457                    ),
2458                    self.special_labels.bad_signature,
2459                )?;
2460                self.machine.release_gpr(sig_hash);
2461                self.machine.release_gpr(table_count);
2462                self.machine.release_gpr(table_base);
2463
2464                let gpr_for_call = self.machine.get_gpr_for_call();
2465                if table_count != gpr_for_call {
2466                    self.machine.move_location(
2467                        Size::S64,
2468                        Location::GPR(table_count),
2469                        Location::GPR(gpr_for_call),
2470                    )?;
2471                }
2472
2473                let vmcaller_checked_anyfunc_func_ptr =
2474                    self.vmoffsets.vmcaller_checked_anyfunc_func_ptr() as usize;
2475                let vmcaller_checked_anyfunc_vmctx =
2476                    self.vmoffsets.vmcaller_checked_anyfunc_vmctx() as usize;
2477                let calling_convention = self.calling_convention;
2478
2479                self.emit_call_native(
2480                    |this| {
2481                        let offset = this
2482                            .machine
2483                            .mark_instruction_with_trap_code(TrapCode::StackOverflow);
2484
2485                        // We set the context pointer
2486                        this.machine.move_location(
2487                            Size::S64,
2488                            Location::Memory(gpr_for_call, vmcaller_checked_anyfunc_vmctx as i32),
2489                            Location::GPR(
2490                                this.machine
2491                                    .get_simple_param_location(0, calling_convention),
2492                            ),
2493                        )?;
2494
2495                        this.machine.emit_call_location(Location::Memory(
2496                            gpr_for_call,
2497                            vmcaller_checked_anyfunc_func_ptr as i32,
2498                        ))?;
2499                        this.machine.mark_instruction_address_end(offset);
2500                        Ok(())
2501                    },
2502                    params.iter().copied(),
2503                    param_types.iter().copied(),
2504                    return_types.iter().copied(),
2505                    NativeCallType::IncludeVMCtxArgument,
2506                )?;
2507            }
2508            Operator::If { blockty } => {
2509                let label_end = self.machine.get_label();
2510                let label_else = self.machine.get_label();
2511
2512                let return_types = self.return_types_for_block(blockty);
2513                let param_types = self.param_types_for_block(blockty);
2514                self.allocate_return_slots_and_swap(param_types.len() + 1, return_types.len())?;
2515
2516                let cond = self.pop_value_released()?.0;
2517
2518                /* We might hit a situation where an Operator::If is missing an Operator::Else. In such a situation,
2519                the result value just fallthrough from the If block inputs! However, we don't know the information upfront. */
2520                if param_types.len() == return_types.len() {
2521                    for (input, return_value) in self
2522                        .value_stack
2523                        .iter()
2524                        .rev()
2525                        .take(param_types.len())
2526                        .zip(self.value_stack.iter().rev().skip(param_types.len()))
2527                    {
2528                        self.machine
2529                            .emit_relaxed_mov(Size::S64, input.0, return_value.0)?;
2530                    }
2531                }
2532
2533                let frame = ControlFrame {
2534                    state: ControlState::If {
2535                        label_else,
2536                        inputs: SmallVec::from_iter(
2537                            self.value_stack
2538                                .iter()
2539                                .rev()
2540                                .take(param_types.len())
2541                                .rev()
2542                                .copied(),
2543                        ),
2544                    },
2545                    label: label_end,
2546                    param_types,
2547                    return_types,
2548                    value_stack_depth: self.value_stack.len(),
2549                };
2550                self.control_stack.push(frame);
2551                self.machine.jmp_on_condition(
2552                    UnsignedCondition::Equal,
2553                    Size::S32,
2554                    cond,
2555                    Location::Imm32(0),
2556                    label_else,
2557                )?;
2558            }
2559            Operator::Else => {
2560                let frame = self.control_stack.last().unwrap();
2561
2562                if !was_unreachable && !frame.return_types.is_empty() {
2563                    self.emit_return_values(
2564                        frame.value_stack_depth_after(),
2565                        frame.return_types.len(),
2566                    )?;
2567                }
2568
2569                let frame = &self.control_stack.last_mut().unwrap();
2570                let locs = self
2571                    .value_stack
2572                    .drain(frame.value_stack_depth_after()..)
2573                    .collect_vec();
2574                self.release_locations(&locs)?;
2575                let frame = &mut self.control_stack.last_mut().unwrap();
2576
2577                // The Else block must be provided the very same inputs as the previous If block had,
2578                // and so we need to copy the already consumed stack values.
2579                let ControlState::If {
2580                    label_else,
2581                    ref inputs,
2582                } = frame.state
2583                else {
2584                    panic!("Operator::Else must be connected to Operator::If statement");
2585                };
2586                for (input, _) in inputs {
2587                    match input {
2588                        Location::GPR(x) => {
2589                            self.machine.reserve_gpr(*x);
2590                        }
2591                        Location::SIMD(x) => {
2592                            self.machine.reserve_simd(*x);
2593                        }
2594                        Location::Memory(reg, _) => {
2595                            debug_assert_eq!(reg, &self.machine.local_pointer());
2596                            self.stack_offset += 8;
2597                        }
2598                        _ => {}
2599                    }
2600                }
2601                self.value_stack.extend(inputs);
2602
2603                self.machine.jmp_unconditional(frame.label)?;
2604                self.machine.emit_label(label_else)?;
2605                frame.state = ControlState::Else;
2606            }
2607            // `TypedSelect` must be used for extern refs so ref counting should
2608            // be done with TypedSelect. But otherwise they're the same.
2609            Operator::TypedSelect { .. } | Operator::Select => {
2610                let cond = self.pop_value_released()?.0;
2611                let (v_b, canonicalize_b) = self.pop_value_released()?;
2612                let (v_a, canonicalize_a) = self.pop_value_released()?;
2613                let ret = self.acquire_location(&WpType::I64)?;
2614                self.value_stack.push((ret, CanonicalizeType::None));
2615
2616                let end_label = self.machine.get_label();
2617                let zero_label = self.machine.get_label();
2618
2619                self.machine.jmp_on_condition(
2620                    UnsignedCondition::Equal,
2621                    Size::S32,
2622                    cond,
2623                    Location::Imm32(0),
2624                    zero_label,
2625                )?;
2626                if self.config.enable_nan_canonicalization
2627                    && let Some(size) = canonicalize_a.to_size()
2628                {
2629                    self.machine.canonicalize_nan(size, v_a, ret)?;
2630                } else if v_a != ret {
2631                    self.machine.emit_relaxed_mov(Size::S64, v_a, ret)?;
2632                }
2633                self.machine.jmp_unconditional(end_label)?;
2634                self.machine.emit_label(zero_label)?;
2635                if self.config.enable_nan_canonicalization
2636                    && let Some(size) = canonicalize_b.to_size()
2637                {
2638                    self.machine.canonicalize_nan(size, v_b, ret)?;
2639                } else if v_b != ret {
2640                    self.machine.emit_relaxed_mov(Size::S64, v_b, ret)?;
2641                }
2642                self.machine.emit_label(end_label)?;
2643            }
2644            Operator::Block { blockty } => {
2645                let return_types = self.return_types_for_block(blockty);
2646                let param_types = self.param_types_for_block(blockty);
2647                self.allocate_return_slots_and_swap(param_types.len(), return_types.len())?;
2648
2649                let frame = ControlFrame {
2650                    state: ControlState::Block,
2651                    label: self.machine.get_label(),
2652                    param_types,
2653                    return_types,
2654                    value_stack_depth: self.value_stack.len(),
2655                };
2656                self.control_stack.push(frame);
2657            }
2658            Operator::Loop { blockty } => {
2659                self.machine.align_for_loop()?;
2660                let label = self.machine.get_label();
2661
2662                let return_types = self.return_types_for_block(blockty);
2663                let param_types = self.param_types_for_block(blockty);
2664                let params_count = param_types.len();
2665                // We need extra space for params as we need to implement the PHI operation.
2666                self.allocate_return_slots_and_swap(
2667                    param_types.len(),
2668                    param_types.len() + return_types.len(),
2669                )?;
2670
2671                self.control_stack.push(ControlFrame {
2672                    state: ControlState::Loop,
2673                    label,
2674                    param_types: param_types.clone(),
2675                    return_types: return_types.clone(),
2676                    value_stack_depth: self.value_stack.len(),
2677                });
2678
2679                // For proper PHI implementation, we must copy pre-loop params to PHI params.
2680                let params = self
2681                    .value_stack
2682                    .drain((self.value_stack.len() - params_count)..)
2683                    .collect_vec();
2684                for (param, phi_param) in params.iter().rev().zip(self.value_stack.iter().rev()) {
2685                    self.machine
2686                        .emit_relaxed_mov(Size::S64, param.0, phi_param.0)?;
2687                }
2688                self.release_locations(&params)?;
2689
2690                self.machine.emit_label(label)?;
2691
2692                // Put on the stack PHI inputs for further use.
2693                let phi_params = self
2694                    .value_stack
2695                    .iter()
2696                    .rev()
2697                    .take(params_count)
2698                    .rev()
2699                    .copied()
2700                    .collect_vec();
2701                for (i, phi_param) in phi_params.into_iter().enumerate() {
2702                    let loc = self.acquire_location(&param_types[i])?;
2703                    self.machine.emit_relaxed_mov(Size::S64, phi_param.0, loc)?;
2704                    self.value_stack.push((loc, phi_param.1));
2705                }
2706
2707                // TODO: Re-enable interrupt signal check without branching
2708            }
2709            Operator::Nop => {}
2710            Operator::MemorySize { mem } => {
2711                let memory_index = MemoryIndex::new(mem as usize);
2712                self.machine.move_location(
2713                    Size::S64,
2714                    Location::Memory(
2715                        self.machine.get_vmctx_reg(),
2716                        self.vmoffsets.vmctx_builtin_function(
2717                            if self.module.local_memory_index(memory_index).is_some() {
2718                                VMBuiltinFunctionIndex::get_memory32_size_index()
2719                            } else {
2720                                VMBuiltinFunctionIndex::get_imported_memory32_size_index()
2721                            },
2722                        ) as i32,
2723                    ),
2724                    Location::GPR(self.machine.get_gpr_for_call()),
2725                )?;
2726                self.emit_call_native(
2727                    |this| {
2728                        this.machine
2729                            .emit_call_register(this.machine.get_gpr_for_call())
2730                    },
2731                    // [vmctx, memory_index]
2732                    iter::once((
2733                        Location::Imm32(memory_index.index() as u32),
2734                        CanonicalizeType::None,
2735                    )),
2736                    iter::once(WpType::I32),
2737                    iter::once(WpType::I32),
2738                    NativeCallType::IncludeVMCtxArgument,
2739                )?;
2740            }
2741            Operator::MemoryInit { data_index, mem } => {
2742                let len = self.value_stack.pop().unwrap();
2743                let src = self.value_stack.pop().unwrap();
2744                let dst = self.value_stack.pop().unwrap();
2745
2746                self.machine.move_location(
2747                    Size::S64,
2748                    Location::Memory(
2749                        self.machine.get_vmctx_reg(),
2750                        self.vmoffsets
2751                            .vmctx_builtin_function(VMBuiltinFunctionIndex::get_memory_init_index())
2752                            as i32,
2753                    ),
2754                    Location::GPR(self.machine.get_gpr_for_call()),
2755                )?;
2756
2757                self.emit_call_native(
2758                    |this| {
2759                        this.machine
2760                            .emit_call_register(this.machine.get_gpr_for_call())
2761                    },
2762                    // [vmctx, memory_index, data_index, dst, src, len]
2763                    [
2764                        (Location::Imm32(mem), CanonicalizeType::None),
2765                        (Location::Imm32(data_index), CanonicalizeType::None),
2766                        dst,
2767                        src,
2768                        len,
2769                    ]
2770                    .iter()
2771                    .cloned(),
2772                    [
2773                        WpType::I32,
2774                        WpType::I32,
2775                        WpType::I32,
2776                        WpType::I32,
2777                        WpType::I32,
2778                    ]
2779                    .iter()
2780                    .cloned(),
2781                    iter::empty(),
2782                    NativeCallType::IncludeVMCtxArgument,
2783                )?;
2784            }
2785            Operator::DataDrop { data_index } => {
2786                self.machine.move_location(
2787                    Size::S64,
2788                    Location::Memory(
2789                        self.machine.get_vmctx_reg(),
2790                        self.vmoffsets
2791                            .vmctx_builtin_function(VMBuiltinFunctionIndex::get_data_drop_index())
2792                            as i32,
2793                    ),
2794                    Location::GPR(self.machine.get_gpr_for_call()),
2795                )?;
2796
2797                self.emit_call_native(
2798                    |this| {
2799                        this.machine
2800                            .emit_call_register(this.machine.get_gpr_for_call())
2801                    },
2802                    // [vmctx, data_index]
2803                    iter::once((Location::Imm32(data_index), CanonicalizeType::None)),
2804                    iter::once(WpType::I32),
2805                    iter::empty(),
2806                    NativeCallType::IncludeVMCtxArgument,
2807                )?;
2808            }
2809            Operator::MemoryCopy { src_mem, .. } => {
2810                // ignore until we support multiple memories
2811                let len = self.value_stack.pop().unwrap();
2812                let src_pos = self.value_stack.pop().unwrap();
2813                let dst_pos = self.value_stack.pop().unwrap();
2814
2815                let memory_index = MemoryIndex::new(src_mem as usize);
2816                let (memory_copy_index, memory_index) =
2817                    if self.module.local_memory_index(memory_index).is_some() {
2818                        (
2819                            VMBuiltinFunctionIndex::get_memory_copy_index(),
2820                            memory_index,
2821                        )
2822                    } else {
2823                        (
2824                            VMBuiltinFunctionIndex::get_imported_memory_copy_index(),
2825                            memory_index,
2826                        )
2827                    };
2828
2829                self.machine.move_location(
2830                    Size::S64,
2831                    Location::Memory(
2832                        self.machine.get_vmctx_reg(),
2833                        self.vmoffsets.vmctx_builtin_function(memory_copy_index) as i32,
2834                    ),
2835                    Location::GPR(self.machine.get_gpr_for_call()),
2836                )?;
2837
2838                self.emit_call_native(
2839                    |this| {
2840                        this.machine
2841                            .emit_call_register(this.machine.get_gpr_for_call())
2842                    },
2843                    // [vmctx, memory_index, dst, src, len]
2844                    [
2845                        (
2846                            Location::Imm32(memory_index.index() as u32),
2847                            CanonicalizeType::None,
2848                        ),
2849                        dst_pos,
2850                        src_pos,
2851                        len,
2852                    ]
2853                    .iter()
2854                    .cloned(),
2855                    [WpType::I32, WpType::I32, WpType::I32, WpType::I32]
2856                        .iter()
2857                        .cloned(),
2858                    iter::empty(),
2859                    NativeCallType::IncludeVMCtxArgument,
2860                )?;
2861            }
2862            Operator::MemoryFill { mem } => {
2863                let len = self.value_stack.pop().unwrap();
2864                let val = self.value_stack.pop().unwrap();
2865                let dst = self.value_stack.pop().unwrap();
2866
2867                let memory_index = MemoryIndex::new(mem as usize);
2868                let (memory_fill_index, memory_index) =
2869                    if self.module.local_memory_index(memory_index).is_some() {
2870                        (
2871                            VMBuiltinFunctionIndex::get_memory_fill_index(),
2872                            memory_index,
2873                        )
2874                    } else {
2875                        (
2876                            VMBuiltinFunctionIndex::get_imported_memory_fill_index(),
2877                            memory_index,
2878                        )
2879                    };
2880
2881                self.machine.move_location(
2882                    Size::S64,
2883                    Location::Memory(
2884                        self.machine.get_vmctx_reg(),
2885                        self.vmoffsets.vmctx_builtin_function(memory_fill_index) as i32,
2886                    ),
2887                    Location::GPR(self.machine.get_gpr_for_call()),
2888                )?;
2889
2890                self.emit_call_native(
2891                    |this| {
2892                        this.machine
2893                            .emit_call_register(this.machine.get_gpr_for_call())
2894                    },
2895                    // [vmctx, memory_index, dst, src, len]
2896                    [
2897                        (
2898                            Location::Imm32(memory_index.index() as u32),
2899                            CanonicalizeType::None,
2900                        ),
2901                        dst,
2902                        val,
2903                        len,
2904                    ]
2905                    .iter()
2906                    .cloned(),
2907                    [WpType::I32, WpType::I32, WpType::I32, WpType::I32]
2908                        .iter()
2909                        .cloned(),
2910                    iter::empty(),
2911                    NativeCallType::IncludeVMCtxArgument,
2912                )?;
2913            }
2914            Operator::MemoryGrow { mem } => {
2915                let memory_index = MemoryIndex::new(mem as usize);
2916                let param_pages = self.value_stack.pop().unwrap();
2917
2918                self.machine.move_location(
2919                    Size::S64,
2920                    Location::Memory(
2921                        self.machine.get_vmctx_reg(),
2922                        self.vmoffsets.vmctx_builtin_function(
2923                            if self.module.local_memory_index(memory_index).is_some() {
2924                                VMBuiltinFunctionIndex::get_memory32_grow_index()
2925                            } else {
2926                                VMBuiltinFunctionIndex::get_imported_memory32_grow_index()
2927                            },
2928                        ) as i32,
2929                    ),
2930                    Location::GPR(self.machine.get_gpr_for_call()),
2931                )?;
2932
2933                self.emit_call_native(
2934                    |this| {
2935                        this.machine
2936                            .emit_call_register(this.machine.get_gpr_for_call())
2937                    },
2938                    // [vmctx, val, memory_index]
2939                    [
2940                        param_pages,
2941                        (
2942                            Location::Imm32(memory_index.index() as u32),
2943                            CanonicalizeType::None,
2944                        ),
2945                    ]
2946                    .iter()
2947                    .cloned(),
2948                    [WpType::I32, WpType::I32].iter().cloned(),
2949                    iter::once(WpType::I32),
2950                    NativeCallType::IncludeVMCtxArgument,
2951                )?;
2952            }
2953            Operator::I32Load { ref memarg } => {
2954                let target = self.pop_value_released()?.0;
2955                let ret = self.acquire_location(&WpType::I32)?;
2956                self.value_stack.push((ret, CanonicalizeType::None));
2957                self.op_memory(
2958                    |this,
2959                     need_check,
2960                     imported_memories,
2961                     offset,
2962                     heap_access_oob,
2963                     unaligned_atomic| {
2964                        this.machine.i32_load(
2965                            target,
2966                            memarg,
2967                            ret,
2968                            need_check,
2969                            imported_memories,
2970                            offset,
2971                            heap_access_oob,
2972                            unaligned_atomic,
2973                        )
2974                    },
2975                )?;
2976            }
2977            Operator::F32Load { ref memarg } => {
2978                let target = self.pop_value_released()?.0;
2979                let ret = self.acquire_location(&WpType::F32)?;
2980                self.value_stack.push((ret, CanonicalizeType::None));
2981                self.op_memory(
2982                    |this,
2983                     need_check,
2984                     imported_memories,
2985                     offset,
2986                     heap_access_oob,
2987                     unaligned_atomic| {
2988                        this.machine.f32_load(
2989                            target,
2990                            memarg,
2991                            ret,
2992                            need_check,
2993                            imported_memories,
2994                            offset,
2995                            heap_access_oob,
2996                            unaligned_atomic,
2997                        )
2998                    },
2999                )?;
3000            }
3001            Operator::I32Load8U { ref memarg } => {
3002                let target = self.pop_value_released()?.0;
3003                let ret = self.acquire_location(&WpType::I32)?;
3004                self.value_stack.push((ret, CanonicalizeType::None));
3005                self.op_memory(
3006                    |this,
3007                     need_check,
3008                     imported_memories,
3009                     offset,
3010                     heap_access_oob,
3011                     unaligned_atomic| {
3012                        this.machine.i32_load_8u(
3013                            target,
3014                            memarg,
3015                            ret,
3016                            need_check,
3017                            imported_memories,
3018                            offset,
3019                            heap_access_oob,
3020                            unaligned_atomic,
3021                        )
3022                    },
3023                )?;
3024            }
3025            Operator::I32Load8S { ref memarg } => {
3026                let target = self.pop_value_released()?.0;
3027                let ret = self.acquire_location(&WpType::I32)?;
3028                self.value_stack.push((ret, CanonicalizeType::None));
3029                self.op_memory(
3030                    |this,
3031                     need_check,
3032                     imported_memories,
3033                     offset,
3034                     heap_access_oob,
3035                     unaligned_atomic| {
3036                        this.machine.i32_load_8s(
3037                            target,
3038                            memarg,
3039                            ret,
3040                            need_check,
3041                            imported_memories,
3042                            offset,
3043                            heap_access_oob,
3044                            unaligned_atomic,
3045                        )
3046                    },
3047                )?;
3048            }
3049            Operator::I32Load16U { ref memarg } => {
3050                let target = self.pop_value_released()?.0;
3051                let ret = self.acquire_location(&WpType::I32)?;
3052                self.value_stack.push((ret, CanonicalizeType::None));
3053                self.op_memory(
3054                    |this,
3055                     need_check,
3056                     imported_memories,
3057                     offset,
3058                     heap_access_oob,
3059                     unaligned_atomic| {
3060                        this.machine.i32_load_16u(
3061                            target,
3062                            memarg,
3063                            ret,
3064                            need_check,
3065                            imported_memories,
3066                            offset,
3067                            heap_access_oob,
3068                            unaligned_atomic,
3069                        )
3070                    },
3071                )?;
3072            }
3073            Operator::I32Load16S { ref memarg } => {
3074                let target = self.pop_value_released()?.0;
3075                let ret = self.acquire_location(&WpType::I32)?;
3076                self.value_stack.push((ret, CanonicalizeType::None));
3077                self.op_memory(
3078                    |this,
3079                     need_check,
3080                     imported_memories,
3081                     offset,
3082                     heap_access_oob,
3083                     unaligned_atomic| {
3084                        this.machine.i32_load_16s(
3085                            target,
3086                            memarg,
3087                            ret,
3088                            need_check,
3089                            imported_memories,
3090                            offset,
3091                            heap_access_oob,
3092                            unaligned_atomic,
3093                        )
3094                    },
3095                )?;
3096            }
3097            Operator::I32Store { ref memarg } => {
3098                let target_value = self.pop_value_released()?.0;
3099                let target_addr = self.pop_value_released()?.0;
3100                self.op_memory(
3101                    |this,
3102                     need_check,
3103                     imported_memories,
3104                     offset,
3105                     heap_access_oob,
3106                     unaligned_atomic| {
3107                        this.machine.i32_save(
3108                            target_value,
3109                            memarg,
3110                            target_addr,
3111                            need_check,
3112                            imported_memories,
3113                            offset,
3114                            heap_access_oob,
3115                            unaligned_atomic,
3116                        )
3117                    },
3118                )?;
3119            }
3120            Operator::F32Store { ref memarg } => {
3121                let (target_value, canonicalize) = self.pop_value_released()?;
3122                let target_addr = self.pop_value_released()?.0;
3123                self.op_memory(
3124                    |this,
3125                     need_check,
3126                     imported_memories,
3127                     offset,
3128                     heap_access_oob,
3129                     unaligned_atomic| {
3130                        this.machine.f32_save(
3131                            target_value,
3132                            memarg,
3133                            target_addr,
3134                            self.config.enable_nan_canonicalization
3135                                && !matches!(canonicalize, CanonicalizeType::None),
3136                            need_check,
3137                            imported_memories,
3138                            offset,
3139                            heap_access_oob,
3140                            unaligned_atomic,
3141                        )
3142                    },
3143                )?;
3144            }
3145            Operator::I32Store8 { ref memarg } => {
3146                let target_value = self.pop_value_released()?.0;
3147                let target_addr = self.pop_value_released()?.0;
3148                self.op_memory(
3149                    |this,
3150                     need_check,
3151                     imported_memories,
3152                     offset,
3153                     heap_access_oob,
3154                     unaligned_atomic| {
3155                        this.machine.i32_save_8(
3156                            target_value,
3157                            memarg,
3158                            target_addr,
3159                            need_check,
3160                            imported_memories,
3161                            offset,
3162                            heap_access_oob,
3163                            unaligned_atomic,
3164                        )
3165                    },
3166                )?;
3167            }
3168            Operator::I32Store16 { ref memarg } => {
3169                let target_value = self.pop_value_released()?.0;
3170                let target_addr = self.pop_value_released()?.0;
3171                self.op_memory(
3172                    |this,
3173                     need_check,
3174                     imported_memories,
3175                     offset,
3176                     heap_access_oob,
3177                     unaligned_atomic| {
3178                        this.machine.i32_save_16(
3179                            target_value,
3180                            memarg,
3181                            target_addr,
3182                            need_check,
3183                            imported_memories,
3184                            offset,
3185                            heap_access_oob,
3186                            unaligned_atomic,
3187                        )
3188                    },
3189                )?;
3190            }
3191            Operator::I64Load { ref memarg } => {
3192                let target = self.pop_value_released()?.0;
3193                let ret = self.acquire_location(&WpType::I64)?;
3194                self.value_stack.push((ret, CanonicalizeType::None));
3195                self.op_memory(
3196                    |this,
3197                     need_check,
3198                     imported_memories,
3199                     offset,
3200                     heap_access_oob,
3201                     unaligned_atomic| {
3202                        this.machine.i64_load(
3203                            target,
3204                            memarg,
3205                            ret,
3206                            need_check,
3207                            imported_memories,
3208                            offset,
3209                            heap_access_oob,
3210                            unaligned_atomic,
3211                        )
3212                    },
3213                )?;
3214            }
3215            Operator::F64Load { ref memarg } => {
3216                let target = self.pop_value_released()?.0;
3217                let ret = self.acquire_location(&WpType::F64)?;
3218                self.value_stack.push((ret, CanonicalizeType::None));
3219                self.op_memory(
3220                    |this,
3221                     need_check,
3222                     imported_memories,
3223                     offset,
3224                     heap_access_oob,
3225                     unaligned_atomic| {
3226                        this.machine.f64_load(
3227                            target,
3228                            memarg,
3229                            ret,
3230                            need_check,
3231                            imported_memories,
3232                            offset,
3233                            heap_access_oob,
3234                            unaligned_atomic,
3235                        )
3236                    },
3237                )?;
3238            }
3239            Operator::I64Load8U { ref memarg } => {
3240                let target = self.pop_value_released()?.0;
3241                let ret = self.acquire_location(&WpType::I64)?;
3242                self.value_stack.push((ret, CanonicalizeType::None));
3243                self.op_memory(
3244                    |this,
3245                     need_check,
3246                     imported_memories,
3247                     offset,
3248                     heap_access_oob,
3249                     unaligned_atomic| {
3250                        this.machine.i64_load_8u(
3251                            target,
3252                            memarg,
3253                            ret,
3254                            need_check,
3255                            imported_memories,
3256                            offset,
3257                            heap_access_oob,
3258                            unaligned_atomic,
3259                        )
3260                    },
3261                )?;
3262            }
3263            Operator::I64Load8S { ref memarg } => {
3264                let target = self.pop_value_released()?.0;
3265                let ret = self.acquire_location(&WpType::I64)?;
3266                self.value_stack.push((ret, CanonicalizeType::None));
3267                self.op_memory(
3268                    |this,
3269                     need_check,
3270                     imported_memories,
3271                     offset,
3272                     heap_access_oob,
3273                     unaligned_atomic| {
3274                        this.machine.i64_load_8s(
3275                            target,
3276                            memarg,
3277                            ret,
3278                            need_check,
3279                            imported_memories,
3280                            offset,
3281                            heap_access_oob,
3282                            unaligned_atomic,
3283                        )
3284                    },
3285                )?;
3286            }
3287            Operator::I64Load16U { ref memarg } => {
3288                let target = self.pop_value_released()?.0;
3289                let ret = self.acquire_location(&WpType::I64)?;
3290                self.value_stack.push((ret, CanonicalizeType::None));
3291                self.op_memory(
3292                    |this,
3293                     need_check,
3294                     imported_memories,
3295                     offset,
3296                     heap_access_oob,
3297                     unaligned_atomic| {
3298                        this.machine.i64_load_16u(
3299                            target,
3300                            memarg,
3301                            ret,
3302                            need_check,
3303                            imported_memories,
3304                            offset,
3305                            heap_access_oob,
3306                            unaligned_atomic,
3307                        )
3308                    },
3309                )?;
3310            }
3311            Operator::I64Load16S { ref memarg } => {
3312                let target = self.pop_value_released()?.0;
3313                let ret = self.acquire_location(&WpType::I64)?;
3314                self.value_stack.push((ret, CanonicalizeType::None));
3315                self.op_memory(
3316                    |this,
3317                     need_check,
3318                     imported_memories,
3319                     offset,
3320                     heap_access_oob,
3321                     unaligned_atomic| {
3322                        this.machine.i64_load_16s(
3323                            target,
3324                            memarg,
3325                            ret,
3326                            need_check,
3327                            imported_memories,
3328                            offset,
3329                            heap_access_oob,
3330                            unaligned_atomic,
3331                        )
3332                    },
3333                )?;
3334            }
3335            Operator::I64Load32U { ref memarg } => {
3336                let target = self.pop_value_released()?.0;
3337                let ret = self.acquire_location(&WpType::I64)?;
3338                self.value_stack.push((ret, CanonicalizeType::None));
3339                self.op_memory(
3340                    |this,
3341                     need_check,
3342                     imported_memories,
3343                     offset,
3344                     heap_access_oob,
3345                     unaligned_atomic| {
3346                        this.machine.i64_load_32u(
3347                            target,
3348                            memarg,
3349                            ret,
3350                            need_check,
3351                            imported_memories,
3352                            offset,
3353                            heap_access_oob,
3354                            unaligned_atomic,
3355                        )
3356                    },
3357                )?;
3358            }
3359            Operator::I64Load32S { ref memarg } => {
3360                let target = self.pop_value_released()?.0;
3361                let ret = self.acquire_location(&WpType::I64)?;
3362                self.value_stack.push((ret, CanonicalizeType::None));
3363                self.op_memory(
3364                    |this,
3365                     need_check,
3366                     imported_memories,
3367                     offset,
3368                     heap_access_oob,
3369                     unaligned_atomic| {
3370                        this.machine.i64_load_32s(
3371                            target,
3372                            memarg,
3373                            ret,
3374                            need_check,
3375                            imported_memories,
3376                            offset,
3377                            heap_access_oob,
3378                            unaligned_atomic,
3379                        )
3380                    },
3381                )?;
3382            }
3383            Operator::I64Store { ref memarg } => {
3384                let target_value = self.pop_value_released()?.0;
3385                let target_addr = self.pop_value_released()?.0;
3386
3387                self.op_memory(
3388                    |this,
3389                     need_check,
3390                     imported_memories,
3391                     offset,
3392                     heap_access_oob,
3393                     unaligned_atomic| {
3394                        this.machine.i64_save(
3395                            target_value,
3396                            memarg,
3397                            target_addr,
3398                            need_check,
3399                            imported_memories,
3400                            offset,
3401                            heap_access_oob,
3402                            unaligned_atomic,
3403                        )
3404                    },
3405                )?;
3406            }
3407            Operator::F64Store { ref memarg } => {
3408                let (target_value, canonicalize) = self.pop_value_released()?;
3409                let target_addr = self.pop_value_released()?.0;
3410                self.op_memory(
3411                    |this,
3412                     need_check,
3413                     imported_memories,
3414                     offset,
3415                     heap_access_oob,
3416                     unaligned_atomic| {
3417                        this.machine.f64_save(
3418                            target_value,
3419                            memarg,
3420                            target_addr,
3421                            self.config.enable_nan_canonicalization
3422                                && !matches!(canonicalize, CanonicalizeType::None),
3423                            need_check,
3424                            imported_memories,
3425                            offset,
3426                            heap_access_oob,
3427                            unaligned_atomic,
3428                        )
3429                    },
3430                )?;
3431            }
3432            Operator::I64Store8 { ref memarg } => {
3433                let target_value = self.pop_value_released()?.0;
3434                let target_addr = self.pop_value_released()?.0;
3435                self.op_memory(
3436                    |this,
3437                     need_check,
3438                     imported_memories,
3439                     offset,
3440                     heap_access_oob,
3441                     unaligned_atomic| {
3442                        this.machine.i64_save_8(
3443                            target_value,
3444                            memarg,
3445                            target_addr,
3446                            need_check,
3447                            imported_memories,
3448                            offset,
3449                            heap_access_oob,
3450                            unaligned_atomic,
3451                        )
3452                    },
3453                )?;
3454            }
3455            Operator::I64Store16 { ref memarg } => {
3456                let target_value = self.pop_value_released()?.0;
3457                let target_addr = self.pop_value_released()?.0;
3458                self.op_memory(
3459                    |this,
3460                     need_check,
3461                     imported_memories,
3462                     offset,
3463                     heap_access_oob,
3464                     unaligned_atomic| {
3465                        this.machine.i64_save_16(
3466                            target_value,
3467                            memarg,
3468                            target_addr,
3469                            need_check,
3470                            imported_memories,
3471                            offset,
3472                            heap_access_oob,
3473                            unaligned_atomic,
3474                        )
3475                    },
3476                )?;
3477            }
3478            Operator::I64Store32 { ref memarg } => {
3479                let target_value = self.pop_value_released()?.0;
3480                let target_addr = self.pop_value_released()?.0;
3481                self.op_memory(
3482                    |this,
3483                     need_check,
3484                     imported_memories,
3485                     offset,
3486                     heap_access_oob,
3487                     unaligned_atomic| {
3488                        this.machine.i64_save_32(
3489                            target_value,
3490                            memarg,
3491                            target_addr,
3492                            need_check,
3493                            imported_memories,
3494                            offset,
3495                            heap_access_oob,
3496                            unaligned_atomic,
3497                        )
3498                    },
3499                )?;
3500            }
3501            Operator::Unreachable => {
3502                self.machine.move_location(
3503                    Size::S64,
3504                    Location::Memory(
3505                        self.machine.get_vmctx_reg(),
3506                        self.vmoffsets
3507                            .vmctx_builtin_function(VMBuiltinFunctionIndex::get_raise_trap_index())
3508                            as i32,
3509                    ),
3510                    Location::GPR(self.machine.get_gpr_for_call()),
3511                )?;
3512
3513                self.emit_call_native(
3514                    |this| {
3515                        this.machine
3516                            .emit_call_register(this.machine.get_gpr_for_call())
3517                    },
3518                    // [trap_code]
3519                    [(
3520                        Location::Imm32(TrapCode::UnreachableCodeReached as u32),
3521                        CanonicalizeType::None,
3522                    )]
3523                    .iter()
3524                    .cloned(),
3525                    [WpType::I32].iter().cloned(),
3526                    iter::empty(),
3527                    NativeCallType::Unreachable,
3528                )?;
3529                self.unreachable_depth = 1;
3530            }
3531            Operator::Return => {
3532                let frame = &self.control_stack[0];
3533                if !frame.return_types.is_empty() {
3534                    self.emit_return_values(
3535                        frame.value_stack_depth_after(),
3536                        frame.return_types.len(),
3537                    )?;
3538                }
3539                let frame = &self.control_stack[0];
3540                let frame_depth = frame.value_stack_depth_for_release();
3541                let label = frame.label;
3542                self.release_stack_locations_keep_stack_offset(frame_depth)?;
3543                self.machine.jmp_unconditional(label)?;
3544                self.unreachable_depth = 1;
3545            }
3546            Operator::Br { relative_depth } => {
3547                let frame =
3548                    &self.control_stack[self.control_stack.len() - 1 - (relative_depth as usize)];
3549                if matches!(frame.state, ControlState::Loop) {
3550                    // Store into the PHI params of the loop, not to the return values.
3551                    self.emit_loop_params_store(
3552                        frame.value_stack_depth_after(),
3553                        frame.param_types.len(),
3554                    )?;
3555                } else if !frame.return_types.is_empty() {
3556                    self.emit_return_values(
3557                        frame.value_stack_depth_after(),
3558                        frame.return_types.len(),
3559                    )?;
3560                }
3561                let stack_len = self.control_stack.len();
3562                let frame = &mut self.control_stack[stack_len - 1 - (relative_depth as usize)];
3563                let frame_depth = frame.value_stack_depth_for_release();
3564                let label = frame.label;
3565
3566                self.release_stack_locations_keep_stack_offset(frame_depth)?;
3567                self.machine.jmp_unconditional(label)?;
3568                self.unreachable_depth = 1;
3569            }
3570            Operator::BrIf { relative_depth } => {
3571                let after = self.machine.get_label();
3572                let cond = self.pop_value_released()?.0;
3573                self.machine.jmp_on_condition(
3574                    UnsignedCondition::Equal,
3575                    Size::S32,
3576                    cond,
3577                    Location::Imm32(0),
3578                    after,
3579                )?;
3580
3581                let frame =
3582                    &self.control_stack[self.control_stack.len() - 1 - (relative_depth as usize)];
3583                if matches!(frame.state, ControlState::Loop) {
3584                    // Store into the PHI params of the loop, not to the return values.
3585                    self.emit_loop_params_store(
3586                        frame.value_stack_depth_after(),
3587                        frame.param_types.len(),
3588                    )?;
3589                } else if !frame.return_types.is_empty() {
3590                    self.emit_return_values(
3591                        frame.value_stack_depth_after(),
3592                        frame.return_types.len(),
3593                    )?;
3594                }
3595                let stack_len = self.control_stack.len();
3596                let frame = &mut self.control_stack[stack_len - 1 - (relative_depth as usize)];
3597                let stack_depth = frame.value_stack_depth_for_release();
3598                let label = frame.label;
3599                self.release_stack_locations_keep_stack_offset(stack_depth)?;
3600                self.machine.jmp_unconditional(label)?;
3601
3602                self.machine.emit_label(after)?;
3603            }
3604            Operator::BrTable { ref targets } => {
3605                let default_target = targets.default();
3606                let targets = targets
3607                    .targets()
3608                    .collect::<Result<Vec<_>, _>>()
3609                    .map_err(|e| CompileError::Codegen(format!("BrTable read_table: {e:?}")))?;
3610                let cond = self.pop_value_released()?.0;
3611                let table_label = self.machine.get_label();
3612                let mut table: Vec<Label> = vec![];
3613                let default_br = self.machine.get_label();
3614                self.machine.jmp_on_condition(
3615                    UnsignedCondition::AboveEqual,
3616                    Size::S32,
3617                    cond,
3618                    Location::Imm32(targets.len() as u32),
3619                    default_br,
3620                )?;
3621
3622                self.machine.emit_jmp_to_jumptable(table_label, cond)?;
3623
3624                for target in targets.iter() {
3625                    let label = self.machine.get_label();
3626                    self.machine.emit_label(label)?;
3627                    table.push(label);
3628                    let frame =
3629                        &self.control_stack[self.control_stack.len() - 1 - (*target as usize)];
3630                    if matches!(frame.state, ControlState::Loop) {
3631                        // Store into the PHI params of the loop, not to the return values.
3632                        self.emit_loop_params_store(
3633                            frame.value_stack_depth_after(),
3634                            frame.param_types.len(),
3635                        )?;
3636                    } else if !frame.return_types.is_empty() {
3637                        self.emit_return_values(
3638                            frame.value_stack_depth_after(),
3639                            frame.return_types.len(),
3640                        )?;
3641                    }
3642                    let frame =
3643                        &self.control_stack[self.control_stack.len() - 1 - (*target as usize)];
3644                    let stack_depth = frame.value_stack_depth_for_release();
3645                    let label = frame.label;
3646                    self.release_stack_locations_keep_stack_offset(stack_depth)?;
3647                    self.machine.jmp_unconditional(label)?;
3648                }
3649                self.machine.emit_label(default_br)?;
3650
3651                {
3652                    let frame = &self.control_stack
3653                        [self.control_stack.len() - 1 - (default_target as usize)];
3654                    if matches!(frame.state, ControlState::Loop) {
3655                        // Store into the PHI params of the loop, not to the return values.
3656                        self.emit_loop_params_store(
3657                            frame.value_stack_depth_after(),
3658                            frame.param_types.len(),
3659                        )?;
3660                    } else if !frame.return_types.is_empty() {
3661                        self.emit_return_values(
3662                            frame.value_stack_depth_after(),
3663                            frame.return_types.len(),
3664                        )?;
3665                    }
3666                    let frame = &self.control_stack
3667                        [self.control_stack.len() - 1 - (default_target as usize)];
3668                    let stack_depth = frame.value_stack_depth_for_release();
3669                    let label = frame.label;
3670                    self.release_stack_locations_keep_stack_offset(stack_depth)?;
3671                    self.machine.jmp_unconditional(label)?;
3672                }
3673
3674                self.machine.emit_label(table_label)?;
3675                for x in table {
3676                    self.machine.jmp_unconditional(x)?;
3677                }
3678                self.unreachable_depth = 1;
3679            }
3680            Operator::Drop => {
3681                self.pop_value_released()?;
3682            }
3683            Operator::End => {
3684                let frame = self.control_stack.pop().unwrap();
3685
3686                if !was_unreachable && !frame.return_types.is_empty() {
3687                    self.emit_return_values(
3688                        frame.value_stack_depth_after(),
3689                        frame.return_types.len(),
3690                    )?;
3691                }
3692
3693                if self.control_stack.is_empty() {
3694                    self.machine.emit_label(frame.label)?;
3695                    self.finalize_locals(self.calling_convention)?;
3696                    self.machine.emit_function_epilog()?;
3697
3698                    // Make a copy of the return value in XMM0, as required by the SysV CC.
3699                    #[allow(clippy::collapsible_if, reason = "hard to read otherwise")]
3700                    if let Ok(&return_type) = self.signature.results().iter().exactly_one()
3701                        && (return_type == Type::F32 || return_type == Type::F64)
3702                    {
3703                        self.machine.emit_function_return_float()?;
3704                    }
3705                    self.machine.emit_ret()?;
3706                } else {
3707                    let released = &self.value_stack.clone()[frame.value_stack_depth_after()..];
3708                    self.release_locations(released)?;
3709                    self.value_stack.truncate(frame.value_stack_depth_after());
3710
3711                    if !matches!(frame.state, ControlState::Loop) {
3712                        self.machine.emit_label(frame.label)?;
3713                    }
3714
3715                    if let ControlState::If { label_else, .. } = frame.state {
3716                        self.machine.emit_label(label_else)?;
3717                    }
3718
3719                    // At this point the return values are properly sitting in the value_stack and are properly canonicalized.
3720                }
3721            }
3722            Operator::AtomicFence => {
3723                // Fence is a nop.
3724                //
3725                // Fence was added to preserve information about fences from
3726                // source languages. If in the future Wasm extends the memory
3727                // model, and if we hadn't recorded what fences used to be there,
3728                // it would lead to data races that weren't present in the
3729                // original source language.
3730                self.machine.emit_memory_fence()?;
3731            }
3732            Operator::I32AtomicLoad { ref memarg } => {
3733                let target = self.pop_value_released()?.0;
3734                let ret = self.acquire_location(&WpType::I32)?;
3735                self.value_stack.push((ret, CanonicalizeType::None));
3736                self.op_memory(
3737                    |this,
3738                     need_check,
3739                     imported_memories,
3740                     offset,
3741                     heap_access_oob,
3742                     unaligned_atomic| {
3743                        this.machine.i32_atomic_load(
3744                            target,
3745                            memarg,
3746                            ret,
3747                            need_check,
3748                            imported_memories,
3749                            offset,
3750                            heap_access_oob,
3751                            unaligned_atomic,
3752                        )
3753                    },
3754                )?;
3755            }
3756            Operator::I32AtomicLoad8U { ref memarg } => {
3757                let target = self.pop_value_released()?.0;
3758                let ret = self.acquire_location(&WpType::I32)?;
3759                self.value_stack.push((ret, CanonicalizeType::None));
3760                self.op_memory(
3761                    |this,
3762                     need_check,
3763                     imported_memories,
3764                     offset,
3765                     heap_access_oob,
3766                     unaligned_atomic| {
3767                        this.machine.i32_atomic_load_8u(
3768                            target,
3769                            memarg,
3770                            ret,
3771                            need_check,
3772                            imported_memories,
3773                            offset,
3774                            heap_access_oob,
3775                            unaligned_atomic,
3776                        )
3777                    },
3778                )?;
3779            }
3780            Operator::I32AtomicLoad16U { ref memarg } => {
3781                let target = self.pop_value_released()?.0;
3782                let ret = self.acquire_location(&WpType::I32)?;
3783                self.value_stack.push((ret, CanonicalizeType::None));
3784                self.op_memory(
3785                    |this,
3786                     need_check,
3787                     imported_memories,
3788                     offset,
3789                     heap_access_oob,
3790                     unaligned_atomic| {
3791                        this.machine.i32_atomic_load_16u(
3792                            target,
3793                            memarg,
3794                            ret,
3795                            need_check,
3796                            imported_memories,
3797                            offset,
3798                            heap_access_oob,
3799                            unaligned_atomic,
3800                        )
3801                    },
3802                )?;
3803            }
3804            Operator::I32AtomicStore { ref memarg } => {
3805                let target_value = self.pop_value_released()?.0;
3806                let target_addr = self.pop_value_released()?.0;
3807                self.op_memory(
3808                    |this,
3809                     need_check,
3810                     imported_memories,
3811                     offset,
3812                     heap_access_oob,
3813                     unaligned_atomic| {
3814                        this.machine.i32_atomic_save(
3815                            target_value,
3816                            memarg,
3817                            target_addr,
3818                            need_check,
3819                            imported_memories,
3820                            offset,
3821                            heap_access_oob,
3822                            unaligned_atomic,
3823                        )
3824                    },
3825                )?;
3826            }
3827            Operator::I32AtomicStore8 { ref memarg } => {
3828                let target_value = self.pop_value_released()?.0;
3829                let target_addr = self.pop_value_released()?.0;
3830                self.op_memory(
3831                    |this,
3832                     need_check,
3833                     imported_memories,
3834                     offset,
3835                     heap_access_oob,
3836                     unaligned_atomic| {
3837                        this.machine.i32_atomic_save_8(
3838                            target_value,
3839                            memarg,
3840                            target_addr,
3841                            need_check,
3842                            imported_memories,
3843                            offset,
3844                            heap_access_oob,
3845                            unaligned_atomic,
3846                        )
3847                    },
3848                )?;
3849            }
3850            Operator::I32AtomicStore16 { ref memarg } => {
3851                let target_value = self.pop_value_released()?.0;
3852                let target_addr = self.pop_value_released()?.0;
3853                self.op_memory(
3854                    |this,
3855                     need_check,
3856                     imported_memories,
3857                     offset,
3858                     heap_access_oob,
3859                     unaligned_atomic| {
3860                        this.machine.i32_atomic_save_16(
3861                            target_value,
3862                            memarg,
3863                            target_addr,
3864                            need_check,
3865                            imported_memories,
3866                            offset,
3867                            heap_access_oob,
3868                            unaligned_atomic,
3869                        )
3870                    },
3871                )?;
3872            }
3873            Operator::I64AtomicLoad { ref memarg } => {
3874                let target = self.pop_value_released()?.0;
3875                let ret = self.acquire_location(&WpType::I64)?;
3876                self.value_stack.push((ret, CanonicalizeType::None));
3877                self.op_memory(
3878                    |this,
3879                     need_check,
3880                     imported_memories,
3881                     offset,
3882                     heap_access_oob,
3883                     unaligned_atomic| {
3884                        this.machine.i64_atomic_load(
3885                            target,
3886                            memarg,
3887                            ret,
3888                            need_check,
3889                            imported_memories,
3890                            offset,
3891                            heap_access_oob,
3892                            unaligned_atomic,
3893                        )
3894                    },
3895                )?;
3896            }
3897            Operator::I64AtomicLoad8U { ref memarg } => {
3898                let target = self.pop_value_released()?.0;
3899                let ret = self.acquire_location(&WpType::I64)?;
3900                self.value_stack.push((ret, CanonicalizeType::None));
3901                self.op_memory(
3902                    |this,
3903                     need_check,
3904                     imported_memories,
3905                     offset,
3906                     heap_access_oob,
3907                     unaligned_atomic| {
3908                        this.machine.i64_atomic_load_8u(
3909                            target,
3910                            memarg,
3911                            ret,
3912                            need_check,
3913                            imported_memories,
3914                            offset,
3915                            heap_access_oob,
3916                            unaligned_atomic,
3917                        )
3918                    },
3919                )?;
3920            }
3921            Operator::I64AtomicLoad16U { ref memarg } => {
3922                let target = self.pop_value_released()?.0;
3923                let ret = self.acquire_location(&WpType::I64)?;
3924                self.value_stack.push((ret, CanonicalizeType::None));
3925                self.op_memory(
3926                    |this,
3927                     need_check,
3928                     imported_memories,
3929                     offset,
3930                     heap_access_oob,
3931                     unaligned_atomic| {
3932                        this.machine.i64_atomic_load_16u(
3933                            target,
3934                            memarg,
3935                            ret,
3936                            need_check,
3937                            imported_memories,
3938                            offset,
3939                            heap_access_oob,
3940                            unaligned_atomic,
3941                        )
3942                    },
3943                )?;
3944            }
3945            Operator::I64AtomicLoad32U { ref memarg } => {
3946                let target = self.pop_value_released()?.0;
3947                let ret = self.acquire_location(&WpType::I64)?;
3948                self.value_stack.push((ret, CanonicalizeType::None));
3949                self.op_memory(
3950                    |this,
3951                     need_check,
3952                     imported_memories,
3953                     offset,
3954                     heap_access_oob,
3955                     unaligned_atomic| {
3956                        this.machine.i64_atomic_load_32u(
3957                            target,
3958                            memarg,
3959                            ret,
3960                            need_check,
3961                            imported_memories,
3962                            offset,
3963                            heap_access_oob,
3964                            unaligned_atomic,
3965                        )
3966                    },
3967                )?;
3968            }
3969            Operator::I64AtomicStore { ref memarg } => {
3970                let target_value = self.pop_value_released()?.0;
3971                let target_addr = self.pop_value_released()?.0;
3972                self.op_memory(
3973                    |this,
3974                     need_check,
3975                     imported_memories,
3976                     offset,
3977                     heap_access_oob,
3978                     unaligned_atomic| {
3979                        this.machine.i64_atomic_save(
3980                            target_value,
3981                            memarg,
3982                            target_addr,
3983                            need_check,
3984                            imported_memories,
3985                            offset,
3986                            heap_access_oob,
3987                            unaligned_atomic,
3988                        )
3989                    },
3990                )?;
3991            }
3992            Operator::I64AtomicStore8 { ref memarg } => {
3993                let target_value = self.pop_value_released()?.0;
3994                let target_addr = self.pop_value_released()?.0;
3995                self.op_memory(
3996                    |this,
3997                     need_check,
3998                     imported_memories,
3999                     offset,
4000                     heap_access_oob,
4001                     unaligned_atomic| {
4002                        this.machine.i64_atomic_save_8(
4003                            target_value,
4004                            memarg,
4005                            target_addr,
4006                            need_check,
4007                            imported_memories,
4008                            offset,
4009                            heap_access_oob,
4010                            unaligned_atomic,
4011                        )
4012                    },
4013                )?;
4014            }
4015            Operator::I64AtomicStore16 { ref memarg } => {
4016                let target_value = self.pop_value_released()?.0;
4017                let target_addr = self.pop_value_released()?.0;
4018                self.op_memory(
4019                    |this,
4020                     need_check,
4021                     imported_memories,
4022                     offset,
4023                     heap_access_oob,
4024                     unaligned_atomic| {
4025                        this.machine.i64_atomic_save_16(
4026                            target_value,
4027                            memarg,
4028                            target_addr,
4029                            need_check,
4030                            imported_memories,
4031                            offset,
4032                            heap_access_oob,
4033                            unaligned_atomic,
4034                        )
4035                    },
4036                )?;
4037            }
4038            Operator::I64AtomicStore32 { ref memarg } => {
4039                let target_value = self.pop_value_released()?.0;
4040                let target_addr = self.pop_value_released()?.0;
4041                self.op_memory(
4042                    |this,
4043                     need_check,
4044                     imported_memories,
4045                     offset,
4046                     heap_access_oob,
4047                     unaligned_atomic| {
4048                        this.machine.i64_atomic_save_32(
4049                            target_value,
4050                            memarg,
4051                            target_addr,
4052                            need_check,
4053                            imported_memories,
4054                            offset,
4055                            heap_access_oob,
4056                            unaligned_atomic,
4057                        )
4058                    },
4059                )?;
4060            }
4061            Operator::I32AtomicRmwAdd { ref memarg } => {
4062                let loc = self.pop_value_released()?.0;
4063                let target = self.pop_value_released()?.0;
4064                let ret = self.acquire_location(&WpType::I32)?;
4065                self.value_stack.push((ret, CanonicalizeType::None));
4066                self.op_memory(
4067                    |this,
4068                     need_check,
4069                     imported_memories,
4070                     offset,
4071                     heap_access_oob,
4072                     unaligned_atomic| {
4073                        this.machine.i32_atomic_add(
4074                            loc,
4075                            target,
4076                            memarg,
4077                            ret,
4078                            need_check,
4079                            imported_memories,
4080                            offset,
4081                            heap_access_oob,
4082                            unaligned_atomic,
4083                        )
4084                    },
4085                )?;
4086            }
4087            Operator::I64AtomicRmwAdd { ref memarg } => {
4088                let loc = self.pop_value_released()?.0;
4089                let target = self.pop_value_released()?.0;
4090                let ret = self.acquire_location(&WpType::I64)?;
4091                self.value_stack.push((ret, CanonicalizeType::None));
4092                self.op_memory(
4093                    |this,
4094                     need_check,
4095                     imported_memories,
4096                     offset,
4097                     heap_access_oob,
4098                     unaligned_atomic| {
4099                        this.machine.i64_atomic_add(
4100                            loc,
4101                            target,
4102                            memarg,
4103                            ret,
4104                            need_check,
4105                            imported_memories,
4106                            offset,
4107                            heap_access_oob,
4108                            unaligned_atomic,
4109                        )
4110                    },
4111                )?;
4112            }
4113            Operator::I32AtomicRmw8AddU { ref memarg } => {
4114                let loc = self.pop_value_released()?.0;
4115                let target = self.pop_value_released()?.0;
4116                let ret = self.acquire_location(&WpType::I32)?;
4117                self.value_stack.push((ret, CanonicalizeType::None));
4118                self.op_memory(
4119                    |this,
4120                     need_check,
4121                     imported_memories,
4122                     offset,
4123                     heap_access_oob,
4124                     unaligned_atomic| {
4125                        this.machine.i32_atomic_add_8u(
4126                            loc,
4127                            target,
4128                            memarg,
4129                            ret,
4130                            need_check,
4131                            imported_memories,
4132                            offset,
4133                            heap_access_oob,
4134                            unaligned_atomic,
4135                        )
4136                    },
4137                )?;
4138            }
4139            Operator::I32AtomicRmw16AddU { ref memarg } => {
4140                let loc = self.pop_value_released()?.0;
4141                let target = self.pop_value_released()?.0;
4142                let ret = self.acquire_location(&WpType::I32)?;
4143                self.value_stack.push((ret, CanonicalizeType::None));
4144                self.op_memory(
4145                    |this,
4146                     need_check,
4147                     imported_memories,
4148                     offset,
4149                     heap_access_oob,
4150                     unaligned_atomic| {
4151                        this.machine.i32_atomic_add_16u(
4152                            loc,
4153                            target,
4154                            memarg,
4155                            ret,
4156                            need_check,
4157                            imported_memories,
4158                            offset,
4159                            heap_access_oob,
4160                            unaligned_atomic,
4161                        )
4162                    },
4163                )?;
4164            }
4165            Operator::I64AtomicRmw8AddU { ref memarg } => {
4166                let loc = self.pop_value_released()?.0;
4167                let target = self.pop_value_released()?.0;
4168                let ret = self.acquire_location(&WpType::I64)?;
4169                self.value_stack.push((ret, CanonicalizeType::None));
4170                self.op_memory(
4171                    |this,
4172                     need_check,
4173                     imported_memories,
4174                     offset,
4175                     heap_access_oob,
4176                     unaligned_atomic| {
4177                        this.machine.i64_atomic_add_8u(
4178                            loc,
4179                            target,
4180                            memarg,
4181                            ret,
4182                            need_check,
4183                            imported_memories,
4184                            offset,
4185                            heap_access_oob,
4186                            unaligned_atomic,
4187                        )
4188                    },
4189                )?;
4190            }
4191            Operator::I64AtomicRmw16AddU { ref memarg } => {
4192                let loc = self.pop_value_released()?.0;
4193                let target = self.pop_value_released()?.0;
4194                let ret = self.acquire_location(&WpType::I64)?;
4195                self.value_stack.push((ret, CanonicalizeType::None));
4196                self.op_memory(
4197                    |this,
4198                     need_check,
4199                     imported_memories,
4200                     offset,
4201                     heap_access_oob,
4202                     unaligned_atomic| {
4203                        this.machine.i64_atomic_add_16u(
4204                            loc,
4205                            target,
4206                            memarg,
4207                            ret,
4208                            need_check,
4209                            imported_memories,
4210                            offset,
4211                            heap_access_oob,
4212                            unaligned_atomic,
4213                        )
4214                    },
4215                )?;
4216            }
4217            Operator::I64AtomicRmw32AddU { ref memarg } => {
4218                let loc = self.pop_value_released()?.0;
4219                let target = self.pop_value_released()?.0;
4220                let ret = self.acquire_location(&WpType::I64)?;
4221                self.value_stack.push((ret, CanonicalizeType::None));
4222                self.op_memory(
4223                    |this,
4224                     need_check,
4225                     imported_memories,
4226                     offset,
4227                     heap_access_oob,
4228                     unaligned_atomic| {
4229                        this.machine.i64_atomic_add_32u(
4230                            loc,
4231                            target,
4232                            memarg,
4233                            ret,
4234                            need_check,
4235                            imported_memories,
4236                            offset,
4237                            heap_access_oob,
4238                            unaligned_atomic,
4239                        )
4240                    },
4241                )?;
4242            }
4243            Operator::I32AtomicRmwSub { ref memarg } => {
4244                let loc = self.pop_value_released()?.0;
4245                let target = self.pop_value_released()?.0;
4246                let ret = self.acquire_location(&WpType::I32)?;
4247                self.value_stack.push((ret, CanonicalizeType::None));
4248                self.op_memory(
4249                    |this,
4250                     need_check,
4251                     imported_memories,
4252                     offset,
4253                     heap_access_oob,
4254                     unaligned_atomic| {
4255                        this.machine.i32_atomic_sub(
4256                            loc,
4257                            target,
4258                            memarg,
4259                            ret,
4260                            need_check,
4261                            imported_memories,
4262                            offset,
4263                            heap_access_oob,
4264                            unaligned_atomic,
4265                        )
4266                    },
4267                )?;
4268            }
4269            Operator::I64AtomicRmwSub { ref memarg } => {
4270                let loc = self.pop_value_released()?.0;
4271                let target = self.pop_value_released()?.0;
4272                let ret = self.acquire_location(&WpType::I64)?;
4273                self.value_stack.push((ret, CanonicalizeType::None));
4274                self.op_memory(
4275                    |this,
4276                     need_check,
4277                     imported_memories,
4278                     offset,
4279                     heap_access_oob,
4280                     unaligned_atomic| {
4281                        this.machine.i64_atomic_sub(
4282                            loc,
4283                            target,
4284                            memarg,
4285                            ret,
4286                            need_check,
4287                            imported_memories,
4288                            offset,
4289                            heap_access_oob,
4290                            unaligned_atomic,
4291                        )
4292                    },
4293                )?;
4294            }
4295            Operator::I32AtomicRmw8SubU { ref memarg } => {
4296                let loc = self.pop_value_released()?.0;
4297                let target = self.pop_value_released()?.0;
4298                let ret = self.acquire_location(&WpType::I32)?;
4299                self.value_stack.push((ret, CanonicalizeType::None));
4300                self.op_memory(
4301                    |this,
4302                     need_check,
4303                     imported_memories,
4304                     offset,
4305                     heap_access_oob,
4306                     unaligned_atomic| {
4307                        this.machine.i32_atomic_sub_8u(
4308                            loc,
4309                            target,
4310                            memarg,
4311                            ret,
4312                            need_check,
4313                            imported_memories,
4314                            offset,
4315                            heap_access_oob,
4316                            unaligned_atomic,
4317                        )
4318                    },
4319                )?;
4320            }
4321            Operator::I32AtomicRmw16SubU { ref memarg } => {
4322                let loc = self.pop_value_released()?.0;
4323                let target = self.pop_value_released()?.0;
4324                let ret = self.acquire_location(&WpType::I32)?;
4325                self.value_stack.push((ret, CanonicalizeType::None));
4326                self.op_memory(
4327                    |this,
4328                     need_check,
4329                     imported_memories,
4330                     offset,
4331                     heap_access_oob,
4332                     unaligned_atomic| {
4333                        this.machine.i32_atomic_sub_16u(
4334                            loc,
4335                            target,
4336                            memarg,
4337                            ret,
4338                            need_check,
4339                            imported_memories,
4340                            offset,
4341                            heap_access_oob,
4342                            unaligned_atomic,
4343                        )
4344                    },
4345                )?;
4346            }
4347            Operator::I64AtomicRmw8SubU { ref memarg } => {
4348                let loc = self.pop_value_released()?.0;
4349                let target = self.pop_value_released()?.0;
4350                let ret = self.acquire_location(&WpType::I64)?;
4351                self.value_stack.push((ret, CanonicalizeType::None));
4352                self.op_memory(
4353                    |this,
4354                     need_check,
4355                     imported_memories,
4356                     offset,
4357                     heap_access_oob,
4358                     unaligned_atomic| {
4359                        this.machine.i64_atomic_sub_8u(
4360                            loc,
4361                            target,
4362                            memarg,
4363                            ret,
4364                            need_check,
4365                            imported_memories,
4366                            offset,
4367                            heap_access_oob,
4368                            unaligned_atomic,
4369                        )
4370                    },
4371                )?;
4372            }
4373            Operator::I64AtomicRmw16SubU { ref memarg } => {
4374                let loc = self.pop_value_released()?.0;
4375                let target = self.pop_value_released()?.0;
4376                let ret = self.acquire_location(&WpType::I64)?;
4377                self.value_stack.push((ret, CanonicalizeType::None));
4378                self.op_memory(
4379                    |this,
4380                     need_check,
4381                     imported_memories,
4382                     offset,
4383                     heap_access_oob,
4384                     unaligned_atomic| {
4385                        this.machine.i64_atomic_sub_16u(
4386                            loc,
4387                            target,
4388                            memarg,
4389                            ret,
4390                            need_check,
4391                            imported_memories,
4392                            offset,
4393                            heap_access_oob,
4394                            unaligned_atomic,
4395                        )
4396                    },
4397                )?;
4398            }
4399            Operator::I64AtomicRmw32SubU { ref memarg } => {
4400                let loc = self.pop_value_released()?.0;
4401                let target = self.pop_value_released()?.0;
4402                let ret = self.acquire_location(&WpType::I64)?;
4403                self.value_stack.push((ret, CanonicalizeType::None));
4404                self.op_memory(
4405                    |this,
4406                     need_check,
4407                     imported_memories,
4408                     offset,
4409                     heap_access_oob,
4410                     unaligned_atomic| {
4411                        this.machine.i64_atomic_sub_32u(
4412                            loc,
4413                            target,
4414                            memarg,
4415                            ret,
4416                            need_check,
4417                            imported_memories,
4418                            offset,
4419                            heap_access_oob,
4420                            unaligned_atomic,
4421                        )
4422                    },
4423                )?;
4424            }
4425            Operator::I32AtomicRmwAnd { ref memarg } => {
4426                let loc = self.pop_value_released()?.0;
4427                let target = self.pop_value_released()?.0;
4428                let ret = self.acquire_location(&WpType::I32)?;
4429                self.value_stack.push((ret, CanonicalizeType::None));
4430                self.op_memory(
4431                    |this,
4432                     need_check,
4433                     imported_memories,
4434                     offset,
4435                     heap_access_oob,
4436                     unaligned_atomic| {
4437                        this.machine.i32_atomic_and(
4438                            loc,
4439                            target,
4440                            memarg,
4441                            ret,
4442                            need_check,
4443                            imported_memories,
4444                            offset,
4445                            heap_access_oob,
4446                            unaligned_atomic,
4447                        )
4448                    },
4449                )?;
4450            }
4451            Operator::I64AtomicRmwAnd { ref memarg } => {
4452                let loc = self.pop_value_released()?.0;
4453                let target = self.pop_value_released()?.0;
4454                let ret = self.acquire_location(&WpType::I64)?;
4455                self.value_stack.push((ret, CanonicalizeType::None));
4456                self.op_memory(
4457                    |this,
4458                     need_check,
4459                     imported_memories,
4460                     offset,
4461                     heap_access_oob,
4462                     unaligned_atomic| {
4463                        this.machine.i64_atomic_and(
4464                            loc,
4465                            target,
4466                            memarg,
4467                            ret,
4468                            need_check,
4469                            imported_memories,
4470                            offset,
4471                            heap_access_oob,
4472                            unaligned_atomic,
4473                        )
4474                    },
4475                )?;
4476            }
4477            Operator::I32AtomicRmw8AndU { ref memarg } => {
4478                let loc = self.pop_value_released()?.0;
4479                let target = self.pop_value_released()?.0;
4480                let ret = self.acquire_location(&WpType::I32)?;
4481                self.value_stack.push((ret, CanonicalizeType::None));
4482                self.op_memory(
4483                    |this,
4484                     need_check,
4485                     imported_memories,
4486                     offset,
4487                     heap_access_oob,
4488                     unaligned_atomic| {
4489                        this.machine.i32_atomic_and_8u(
4490                            loc,
4491                            target,
4492                            memarg,
4493                            ret,
4494                            need_check,
4495                            imported_memories,
4496                            offset,
4497                            heap_access_oob,
4498                            unaligned_atomic,
4499                        )
4500                    },
4501                )?;
4502            }
4503            Operator::I32AtomicRmw16AndU { ref memarg } => {
4504                let loc = self.pop_value_released()?.0;
4505                let target = self.pop_value_released()?.0;
4506                let ret = self.acquire_location(&WpType::I32)?;
4507                self.value_stack.push((ret, CanonicalizeType::None));
4508                self.op_memory(
4509                    |this,
4510                     need_check,
4511                     imported_memories,
4512                     offset,
4513                     heap_access_oob,
4514                     unaligned_atomic| {
4515                        this.machine.i32_atomic_and_16u(
4516                            loc,
4517                            target,
4518                            memarg,
4519                            ret,
4520                            need_check,
4521                            imported_memories,
4522                            offset,
4523                            heap_access_oob,
4524                            unaligned_atomic,
4525                        )
4526                    },
4527                )?;
4528            }
4529            Operator::I64AtomicRmw8AndU { ref memarg } => {
4530                let loc = self.pop_value_released()?.0;
4531                let target = self.pop_value_released()?.0;
4532                let ret = self.acquire_location(&WpType::I64)?;
4533                self.value_stack.push((ret, CanonicalizeType::None));
4534                self.op_memory(
4535                    |this,
4536                     need_check,
4537                     imported_memories,
4538                     offset,
4539                     heap_access_oob,
4540                     unaligned_atomic| {
4541                        this.machine.i64_atomic_and_8u(
4542                            loc,
4543                            target,
4544                            memarg,
4545                            ret,
4546                            need_check,
4547                            imported_memories,
4548                            offset,
4549                            heap_access_oob,
4550                            unaligned_atomic,
4551                        )
4552                    },
4553                )?;
4554            }
4555            Operator::I64AtomicRmw16AndU { ref memarg } => {
4556                let loc = self.pop_value_released()?.0;
4557                let target = self.pop_value_released()?.0;
4558                let ret = self.acquire_location(&WpType::I64)?;
4559                self.value_stack.push((ret, CanonicalizeType::None));
4560                self.op_memory(
4561                    |this,
4562                     need_check,
4563                     imported_memories,
4564                     offset,
4565                     heap_access_oob,
4566                     unaligned_atomic| {
4567                        this.machine.i64_atomic_and_16u(
4568                            loc,
4569                            target,
4570                            memarg,
4571                            ret,
4572                            need_check,
4573                            imported_memories,
4574                            offset,
4575                            heap_access_oob,
4576                            unaligned_atomic,
4577                        )
4578                    },
4579                )?;
4580            }
4581            Operator::I64AtomicRmw32AndU { ref memarg } => {
4582                let loc = self.pop_value_released()?.0;
4583                let target = self.pop_value_released()?.0;
4584                let ret = self.acquire_location(&WpType::I64)?;
4585                self.value_stack.push((ret, CanonicalizeType::None));
4586                self.op_memory(
4587                    |this,
4588                     need_check,
4589                     imported_memories,
4590                     offset,
4591                     heap_access_oob,
4592                     unaligned_atomic| {
4593                        this.machine.i64_atomic_and_32u(
4594                            loc,
4595                            target,
4596                            memarg,
4597                            ret,
4598                            need_check,
4599                            imported_memories,
4600                            offset,
4601                            heap_access_oob,
4602                            unaligned_atomic,
4603                        )
4604                    },
4605                )?;
4606            }
4607            Operator::I32AtomicRmwOr { ref memarg } => {
4608                let loc = self.pop_value_released()?.0;
4609                let target = self.pop_value_released()?.0;
4610                let ret = self.acquire_location(&WpType::I32)?;
4611                self.value_stack.push((ret, CanonicalizeType::None));
4612                self.op_memory(
4613                    |this,
4614                     need_check,
4615                     imported_memories,
4616                     offset,
4617                     heap_access_oob,
4618                     unaligned_atomic| {
4619                        this.machine.i32_atomic_or(
4620                            loc,
4621                            target,
4622                            memarg,
4623                            ret,
4624                            need_check,
4625                            imported_memories,
4626                            offset,
4627                            heap_access_oob,
4628                            unaligned_atomic,
4629                        )
4630                    },
4631                )?;
4632            }
4633            Operator::I64AtomicRmwOr { ref memarg } => {
4634                let loc = self.pop_value_released()?.0;
4635                let target = self.pop_value_released()?.0;
4636                let ret = self.acquire_location(&WpType::I64)?;
4637                self.value_stack.push((ret, CanonicalizeType::None));
4638                self.op_memory(
4639                    |this,
4640                     need_check,
4641                     imported_memories,
4642                     offset,
4643                     heap_access_oob,
4644                     unaligned_atomic| {
4645                        this.machine.i64_atomic_or(
4646                            loc,
4647                            target,
4648                            memarg,
4649                            ret,
4650                            need_check,
4651                            imported_memories,
4652                            offset,
4653                            heap_access_oob,
4654                            unaligned_atomic,
4655                        )
4656                    },
4657                )?;
4658            }
4659            Operator::I32AtomicRmw8OrU { ref memarg } => {
4660                let loc = self.pop_value_released()?.0;
4661                let target = self.pop_value_released()?.0;
4662                let ret = self.acquire_location(&WpType::I32)?;
4663                self.value_stack.push((ret, CanonicalizeType::None));
4664                self.op_memory(
4665                    |this,
4666                     need_check,
4667                     imported_memories,
4668                     offset,
4669                     heap_access_oob,
4670                     unaligned_atomic| {
4671                        this.machine.i32_atomic_or_8u(
4672                            loc,
4673                            target,
4674                            memarg,
4675                            ret,
4676                            need_check,
4677                            imported_memories,
4678                            offset,
4679                            heap_access_oob,
4680                            unaligned_atomic,
4681                        )
4682                    },
4683                )?;
4684            }
4685            Operator::I32AtomicRmw16OrU { ref memarg } => {
4686                let loc = self.pop_value_released()?.0;
4687                let target = self.pop_value_released()?.0;
4688                let ret = self.acquire_location(&WpType::I32)?;
4689                self.value_stack.push((ret, CanonicalizeType::None));
4690                self.op_memory(
4691                    |this,
4692                     need_check,
4693                     imported_memories,
4694                     offset,
4695                     heap_access_oob,
4696                     unaligned_atomic| {
4697                        this.machine.i32_atomic_or_16u(
4698                            loc,
4699                            target,
4700                            memarg,
4701                            ret,
4702                            need_check,
4703                            imported_memories,
4704                            offset,
4705                            heap_access_oob,
4706                            unaligned_atomic,
4707                        )
4708                    },
4709                )?;
4710            }
4711            Operator::I64AtomicRmw8OrU { ref memarg } => {
4712                let loc = self.pop_value_released()?.0;
4713                let target = self.pop_value_released()?.0;
4714                let ret = self.acquire_location(&WpType::I64)?;
4715                self.value_stack.push((ret, CanonicalizeType::None));
4716                self.op_memory(
4717                    |this,
4718                     need_check,
4719                     imported_memories,
4720                     offset,
4721                     heap_access_oob,
4722                     unaligned_atomic| {
4723                        this.machine.i64_atomic_or_8u(
4724                            loc,
4725                            target,
4726                            memarg,
4727                            ret,
4728                            need_check,
4729                            imported_memories,
4730                            offset,
4731                            heap_access_oob,
4732                            unaligned_atomic,
4733                        )
4734                    },
4735                )?;
4736            }
4737            Operator::I64AtomicRmw16OrU { ref memarg } => {
4738                let loc = self.pop_value_released()?.0;
4739                let target = self.pop_value_released()?.0;
4740                let ret = self.acquire_location(&WpType::I64)?;
4741                self.value_stack.push((ret, CanonicalizeType::None));
4742                self.op_memory(
4743                    |this,
4744                     need_check,
4745                     imported_memories,
4746                     offset,
4747                     heap_access_oob,
4748                     unaligned_atomic| {
4749                        this.machine.i64_atomic_or_16u(
4750                            loc,
4751                            target,
4752                            memarg,
4753                            ret,
4754                            need_check,
4755                            imported_memories,
4756                            offset,
4757                            heap_access_oob,
4758                            unaligned_atomic,
4759                        )
4760                    },
4761                )?;
4762            }
4763            Operator::I64AtomicRmw32OrU { ref memarg } => {
4764                let loc = self.pop_value_released()?.0;
4765                let target = self.pop_value_released()?.0;
4766                let ret = self.acquire_location(&WpType::I64)?;
4767                self.value_stack.push((ret, CanonicalizeType::None));
4768                self.op_memory(
4769                    |this,
4770                     need_check,
4771                     imported_memories,
4772                     offset,
4773                     heap_access_oob,
4774                     unaligned_atomic| {
4775                        this.machine.i64_atomic_or_32u(
4776                            loc,
4777                            target,
4778                            memarg,
4779                            ret,
4780                            need_check,
4781                            imported_memories,
4782                            offset,
4783                            heap_access_oob,
4784                            unaligned_atomic,
4785                        )
4786                    },
4787                )?;
4788            }
4789            Operator::I32AtomicRmwXor { ref memarg } => {
4790                let loc = self.pop_value_released()?.0;
4791                let target = self.pop_value_released()?.0;
4792                let ret = self.acquire_location(&WpType::I32)?;
4793                self.value_stack.push((ret, CanonicalizeType::None));
4794                self.op_memory(
4795                    |this,
4796                     need_check,
4797                     imported_memories,
4798                     offset,
4799                     heap_access_oob,
4800                     unaligned_atomic| {
4801                        this.machine.i32_atomic_xor(
4802                            loc,
4803                            target,
4804                            memarg,
4805                            ret,
4806                            need_check,
4807                            imported_memories,
4808                            offset,
4809                            heap_access_oob,
4810                            unaligned_atomic,
4811                        )
4812                    },
4813                )?;
4814            }
4815            Operator::I64AtomicRmwXor { ref memarg } => {
4816                let loc = self.pop_value_released()?.0;
4817                let target = self.pop_value_released()?.0;
4818                let ret = self.acquire_location(&WpType::I64)?;
4819                self.value_stack.push((ret, CanonicalizeType::None));
4820                self.op_memory(
4821                    |this,
4822                     need_check,
4823                     imported_memories,
4824                     offset,
4825                     heap_access_oob,
4826                     unaligned_atomic| {
4827                        this.machine.i64_atomic_xor(
4828                            loc,
4829                            target,
4830                            memarg,
4831                            ret,
4832                            need_check,
4833                            imported_memories,
4834                            offset,
4835                            heap_access_oob,
4836                            unaligned_atomic,
4837                        )
4838                    },
4839                )?;
4840            }
4841            Operator::I32AtomicRmw8XorU { ref memarg } => {
4842                let loc = self.pop_value_released()?.0;
4843                let target = self.pop_value_released()?.0;
4844                let ret = self.acquire_location(&WpType::I32)?;
4845                self.value_stack.push((ret, CanonicalizeType::None));
4846                self.op_memory(
4847                    |this,
4848                     need_check,
4849                     imported_memories,
4850                     offset,
4851                     heap_access_oob,
4852                     unaligned_atomic| {
4853                        this.machine.i32_atomic_xor_8u(
4854                            loc,
4855                            target,
4856                            memarg,
4857                            ret,
4858                            need_check,
4859                            imported_memories,
4860                            offset,
4861                            heap_access_oob,
4862                            unaligned_atomic,
4863                        )
4864                    },
4865                )?;
4866            }
4867            Operator::I32AtomicRmw16XorU { ref memarg } => {
4868                let loc = self.pop_value_released()?.0;
4869                let target = self.pop_value_released()?.0;
4870                let ret = self.acquire_location(&WpType::I32)?;
4871                self.value_stack.push((ret, CanonicalizeType::None));
4872                self.op_memory(
4873                    |this,
4874                     need_check,
4875                     imported_memories,
4876                     offset,
4877                     heap_access_oob,
4878                     unaligned_atomic| {
4879                        this.machine.i32_atomic_xor_16u(
4880                            loc,
4881                            target,
4882                            memarg,
4883                            ret,
4884                            need_check,
4885                            imported_memories,
4886                            offset,
4887                            heap_access_oob,
4888                            unaligned_atomic,
4889                        )
4890                    },
4891                )?;
4892            }
4893            Operator::I64AtomicRmw8XorU { ref memarg } => {
4894                let loc = self.pop_value_released()?.0;
4895                let target = self.pop_value_released()?.0;
4896                let ret = self.acquire_location(&WpType::I64)?;
4897                self.value_stack.push((ret, CanonicalizeType::None));
4898                self.op_memory(
4899                    |this,
4900                     need_check,
4901                     imported_memories,
4902                     offset,
4903                     heap_access_oob,
4904                     unaligned_atomic| {
4905                        this.machine.i64_atomic_xor_8u(
4906                            loc,
4907                            target,
4908                            memarg,
4909                            ret,
4910                            need_check,
4911                            imported_memories,
4912                            offset,
4913                            heap_access_oob,
4914                            unaligned_atomic,
4915                        )
4916                    },
4917                )?;
4918            }
4919            Operator::I64AtomicRmw16XorU { ref memarg } => {
4920                let loc = self.pop_value_released()?.0;
4921                let target = self.pop_value_released()?.0;
4922                let ret = self.acquire_location(&WpType::I64)?;
4923                self.value_stack.push((ret, CanonicalizeType::None));
4924                self.op_memory(
4925                    |this,
4926                     need_check,
4927                     imported_memories,
4928                     offset,
4929                     heap_access_oob,
4930                     unaligned_atomic| {
4931                        this.machine.i64_atomic_xor_16u(
4932                            loc,
4933                            target,
4934                            memarg,
4935                            ret,
4936                            need_check,
4937                            imported_memories,
4938                            offset,
4939                            heap_access_oob,
4940                            unaligned_atomic,
4941                        )
4942                    },
4943                )?;
4944            }
4945            Operator::I64AtomicRmw32XorU { ref memarg } => {
4946                let loc = self.pop_value_released()?.0;
4947                let target = self.pop_value_released()?.0;
4948                let ret = self.acquire_location(&WpType::I64)?;
4949                self.value_stack.push((ret, CanonicalizeType::None));
4950                self.op_memory(
4951                    |this,
4952                     need_check,
4953                     imported_memories,
4954                     offset,
4955                     heap_access_oob,
4956                     unaligned_atomic| {
4957                        this.machine.i64_atomic_xor_32u(
4958                            loc,
4959                            target,
4960                            memarg,
4961                            ret,
4962                            need_check,
4963                            imported_memories,
4964                            offset,
4965                            heap_access_oob,
4966                            unaligned_atomic,
4967                        )
4968                    },
4969                )?;
4970            }
4971            Operator::I32AtomicRmwXchg { ref memarg } => {
4972                let loc = self.pop_value_released()?.0;
4973                let target = self.pop_value_released()?.0;
4974                let ret = self.acquire_location(&WpType::I32)?;
4975                self.value_stack.push((ret, CanonicalizeType::None));
4976                self.op_memory(
4977                    |this,
4978                     need_check,
4979                     imported_memories,
4980                     offset,
4981                     heap_access_oob,
4982                     unaligned_atomic| {
4983                        this.machine.i32_atomic_xchg(
4984                            loc,
4985                            target,
4986                            memarg,
4987                            ret,
4988                            need_check,
4989                            imported_memories,
4990                            offset,
4991                            heap_access_oob,
4992                            unaligned_atomic,
4993                        )
4994                    },
4995                )?;
4996            }
4997            Operator::I64AtomicRmwXchg { ref memarg } => {
4998                let loc = self.pop_value_released()?.0;
4999                let target = self.pop_value_released()?.0;
5000                let ret = self.acquire_location(&WpType::I64)?;
5001                self.value_stack.push((ret, CanonicalizeType::None));
5002                self.op_memory(
5003                    |this,
5004                     need_check,
5005                     imported_memories,
5006                     offset,
5007                     heap_access_oob,
5008                     unaligned_atomic| {
5009                        this.machine.i64_atomic_xchg(
5010                            loc,
5011                            target,
5012                            memarg,
5013                            ret,
5014                            need_check,
5015                            imported_memories,
5016                            offset,
5017                            heap_access_oob,
5018                            unaligned_atomic,
5019                        )
5020                    },
5021                )?;
5022            }
5023            Operator::I32AtomicRmw8XchgU { ref memarg } => {
5024                let loc = self.pop_value_released()?.0;
5025                let target = self.pop_value_released()?.0;
5026                let ret = self.acquire_location(&WpType::I32)?;
5027                self.value_stack.push((ret, CanonicalizeType::None));
5028                self.op_memory(
5029                    |this,
5030                     need_check,
5031                     imported_memories,
5032                     offset,
5033                     heap_access_oob,
5034                     unaligned_atomic| {
5035                        this.machine.i32_atomic_xchg_8u(
5036                            loc,
5037                            target,
5038                            memarg,
5039                            ret,
5040                            need_check,
5041                            imported_memories,
5042                            offset,
5043                            heap_access_oob,
5044                            unaligned_atomic,
5045                        )
5046                    },
5047                )?;
5048            }
5049            Operator::I32AtomicRmw16XchgU { ref memarg } => {
5050                let loc = self.pop_value_released()?.0;
5051                let target = self.pop_value_released()?.0;
5052                let ret = self.acquire_location(&WpType::I32)?;
5053                self.value_stack.push((ret, CanonicalizeType::None));
5054                self.op_memory(
5055                    |this,
5056                     need_check,
5057                     imported_memories,
5058                     offset,
5059                     heap_access_oob,
5060                     unaligned_atomic| {
5061                        this.machine.i32_atomic_xchg_16u(
5062                            loc,
5063                            target,
5064                            memarg,
5065                            ret,
5066                            need_check,
5067                            imported_memories,
5068                            offset,
5069                            heap_access_oob,
5070                            unaligned_atomic,
5071                        )
5072                    },
5073                )?;
5074            }
5075            Operator::I64AtomicRmw8XchgU { ref memarg } => {
5076                let loc = self.pop_value_released()?.0;
5077                let target = self.pop_value_released()?.0;
5078                let ret = self.acquire_location(&WpType::I64)?;
5079                self.value_stack.push((ret, CanonicalizeType::None));
5080                self.op_memory(
5081                    |this,
5082                     need_check,
5083                     imported_memories,
5084                     offset,
5085                     heap_access_oob,
5086                     unaligned_atomic| {
5087                        this.machine.i64_atomic_xchg_8u(
5088                            loc,
5089                            target,
5090                            memarg,
5091                            ret,
5092                            need_check,
5093                            imported_memories,
5094                            offset,
5095                            heap_access_oob,
5096                            unaligned_atomic,
5097                        )
5098                    },
5099                )?;
5100            }
5101            Operator::I64AtomicRmw16XchgU { ref memarg } => {
5102                let loc = self.pop_value_released()?.0;
5103                let target = self.pop_value_released()?.0;
5104                let ret = self.acquire_location(&WpType::I64)?;
5105                self.value_stack.push((ret, CanonicalizeType::None));
5106                self.op_memory(
5107                    |this,
5108                     need_check,
5109                     imported_memories,
5110                     offset,
5111                     heap_access_oob,
5112                     unaligned_atomic| {
5113                        this.machine.i64_atomic_xchg_16u(
5114                            loc,
5115                            target,
5116                            memarg,
5117                            ret,
5118                            need_check,
5119                            imported_memories,
5120                            offset,
5121                            heap_access_oob,
5122                            unaligned_atomic,
5123                        )
5124                    },
5125                )?;
5126            }
5127            Operator::I64AtomicRmw32XchgU { ref memarg } => {
5128                let loc = self.pop_value_released()?.0;
5129                let target = self.pop_value_released()?.0;
5130                let ret = self.acquire_location(&WpType::I64)?;
5131                self.value_stack.push((ret, CanonicalizeType::None));
5132                self.op_memory(
5133                    |this,
5134                     need_check,
5135                     imported_memories,
5136                     offset,
5137                     heap_access_oob,
5138                     unaligned_atomic| {
5139                        this.machine.i64_atomic_xchg_32u(
5140                            loc,
5141                            target,
5142                            memarg,
5143                            ret,
5144                            need_check,
5145                            imported_memories,
5146                            offset,
5147                            heap_access_oob,
5148                            unaligned_atomic,
5149                        )
5150                    },
5151                )?;
5152            }
5153            Operator::I32AtomicRmwCmpxchg { ref memarg } => {
5154                let new = self.pop_value_released()?.0;
5155                let cmp = self.pop_value_released()?.0;
5156                let target = self.pop_value_released()?.0;
5157                let ret = self.acquire_location(&WpType::I32)?;
5158                self.value_stack.push((ret, CanonicalizeType::None));
5159                self.op_memory(
5160                    |this,
5161                     need_check,
5162                     imported_memories,
5163                     offset,
5164                     heap_access_oob,
5165                     unaligned_atomic| {
5166                        this.machine.i32_atomic_cmpxchg(
5167                            new,
5168                            cmp,
5169                            target,
5170                            memarg,
5171                            ret,
5172                            need_check,
5173                            imported_memories,
5174                            offset,
5175                            heap_access_oob,
5176                            unaligned_atomic,
5177                        )
5178                    },
5179                )?;
5180            }
5181            Operator::I64AtomicRmwCmpxchg { ref memarg } => {
5182                let new = self.pop_value_released()?.0;
5183                let cmp = self.pop_value_released()?.0;
5184                let target = self.pop_value_released()?.0;
5185                let ret = self.acquire_location(&WpType::I64)?;
5186                self.value_stack.push((ret, CanonicalizeType::None));
5187                self.op_memory(
5188                    |this,
5189                     need_check,
5190                     imported_memories,
5191                     offset,
5192                     heap_access_oob,
5193                     unaligned_atomic| {
5194                        this.machine.i64_atomic_cmpxchg(
5195                            new,
5196                            cmp,
5197                            target,
5198                            memarg,
5199                            ret,
5200                            need_check,
5201                            imported_memories,
5202                            offset,
5203                            heap_access_oob,
5204                            unaligned_atomic,
5205                        )
5206                    },
5207                )?;
5208            }
5209            Operator::I32AtomicRmw8CmpxchgU { ref memarg } => {
5210                let new = self.pop_value_released()?.0;
5211                let cmp = self.pop_value_released()?.0;
5212                let target = self.pop_value_released()?.0;
5213                let ret = self.acquire_location(&WpType::I32)?;
5214                self.value_stack.push((ret, CanonicalizeType::None));
5215                self.op_memory(
5216                    |this,
5217                     need_check,
5218                     imported_memories,
5219                     offset,
5220                     heap_access_oob,
5221                     unaligned_atomic| {
5222                        this.machine.i32_atomic_cmpxchg_8u(
5223                            new,
5224                            cmp,
5225                            target,
5226                            memarg,
5227                            ret,
5228                            need_check,
5229                            imported_memories,
5230                            offset,
5231                            heap_access_oob,
5232                            unaligned_atomic,
5233                        )
5234                    },
5235                )?;
5236            }
5237            Operator::I32AtomicRmw16CmpxchgU { ref memarg } => {
5238                let new = self.pop_value_released()?.0;
5239                let cmp = self.pop_value_released()?.0;
5240                let target = self.pop_value_released()?.0;
5241                let ret = self.acquire_location(&WpType::I32)?;
5242                self.value_stack.push((ret, CanonicalizeType::None));
5243                self.op_memory(
5244                    |this,
5245                     need_check,
5246                     imported_memories,
5247                     offset,
5248                     heap_access_oob,
5249                     unaligned_atomic| {
5250                        this.machine.i32_atomic_cmpxchg_16u(
5251                            new,
5252                            cmp,
5253                            target,
5254                            memarg,
5255                            ret,
5256                            need_check,
5257                            imported_memories,
5258                            offset,
5259                            heap_access_oob,
5260                            unaligned_atomic,
5261                        )
5262                    },
5263                )?;
5264            }
5265            Operator::I64AtomicRmw8CmpxchgU { ref memarg } => {
5266                let new = self.pop_value_released()?.0;
5267                let cmp = self.pop_value_released()?.0;
5268                let target = self.pop_value_released()?.0;
5269                let ret = self.acquire_location(&WpType::I64)?;
5270                self.value_stack.push((ret, CanonicalizeType::None));
5271                self.op_memory(
5272                    |this,
5273                     need_check,
5274                     imported_memories,
5275                     offset,
5276                     heap_access_oob,
5277                     unaligned_atomic| {
5278                        this.machine.i64_atomic_cmpxchg_8u(
5279                            new,
5280                            cmp,
5281                            target,
5282                            memarg,
5283                            ret,
5284                            need_check,
5285                            imported_memories,
5286                            offset,
5287                            heap_access_oob,
5288                            unaligned_atomic,
5289                        )
5290                    },
5291                )?;
5292            }
5293            Operator::I64AtomicRmw16CmpxchgU { ref memarg } => {
5294                let new = self.pop_value_released()?.0;
5295                let cmp = self.pop_value_released()?.0;
5296                let target = self.pop_value_released()?.0;
5297                let ret = self.acquire_location(&WpType::I64)?;
5298                self.value_stack.push((ret, CanonicalizeType::None));
5299                self.op_memory(
5300                    |this,
5301                     need_check,
5302                     imported_memories,
5303                     offset,
5304                     heap_access_oob,
5305                     unaligned_atomic| {
5306                        this.machine.i64_atomic_cmpxchg_16u(
5307                            new,
5308                            cmp,
5309                            target,
5310                            memarg,
5311                            ret,
5312                            need_check,
5313                            imported_memories,
5314                            offset,
5315                            heap_access_oob,
5316                            unaligned_atomic,
5317                        )
5318                    },
5319                )?;
5320            }
5321            Operator::I64AtomicRmw32CmpxchgU { ref memarg } => {
5322                let new = self.pop_value_released()?.0;
5323                let cmp = self.pop_value_released()?.0;
5324                let target = self.pop_value_released()?.0;
5325                let ret = self.acquire_location(&WpType::I64)?;
5326                self.value_stack.push((ret, CanonicalizeType::None));
5327                self.op_memory(
5328                    |this,
5329                     need_check,
5330                     imported_memories,
5331                     offset,
5332                     heap_access_oob,
5333                     unaligned_atomic| {
5334                        this.machine.i64_atomic_cmpxchg_32u(
5335                            new,
5336                            cmp,
5337                            target,
5338                            memarg,
5339                            ret,
5340                            need_check,
5341                            imported_memories,
5342                            offset,
5343                            heap_access_oob,
5344                            unaligned_atomic,
5345                        )
5346                    },
5347                )?;
5348            }
5349
5350            Operator::RefNull { .. } => {
5351                self.value_stack
5352                    .push((Location::Imm64(0), CanonicalizeType::None));
5353            }
5354            Operator::RefFunc { function_index } => {
5355                self.machine.move_location(
5356                    Size::S64,
5357                    Location::Memory(
5358                        self.machine.get_vmctx_reg(),
5359                        self.vmoffsets
5360                            .vmctx_builtin_function(VMBuiltinFunctionIndex::get_func_ref_index())
5361                            as i32,
5362                    ),
5363                    Location::GPR(self.machine.get_gpr_for_call()),
5364                )?;
5365
5366                self.emit_call_native(
5367                    |this| {
5368                        this.machine
5369                            .emit_call_register(this.machine.get_gpr_for_call())
5370                    },
5371                    // [vmctx, func_index] -> funcref
5372                    iter::once((
5373                        Location::Imm32(function_index as u32),
5374                        CanonicalizeType::None,
5375                    )),
5376                    iter::once(WpType::I32),
5377                    iter::once(WpType::Ref(WpRefType::new(true, WpHeapType::FUNC).unwrap())),
5378                    NativeCallType::IncludeVMCtxArgument,
5379                )?;
5380            }
5381            Operator::RefIsNull => {
5382                let loc_a = self.pop_value_released()?.0;
5383                let ret = self.acquire_location(&WpType::I32)?;
5384                self.machine.i64_cmp_eq(loc_a, Location::Imm64(0), ret)?;
5385                self.value_stack.push((ret, CanonicalizeType::None));
5386            }
5387            Operator::TableSet { table: index } => {
5388                let table_index = TableIndex::new(index as _);
5389                let value = self.value_stack.pop().unwrap();
5390                let index = self.value_stack.pop().unwrap();
5391
5392                self.machine.move_location(
5393                    Size::S64,
5394                    Location::Memory(
5395                        self.machine.get_vmctx_reg(),
5396                        self.vmoffsets.vmctx_builtin_function(
5397                            if self.module.local_table_index(table_index).is_some() {
5398                                VMBuiltinFunctionIndex::get_table_set_index()
5399                            } else {
5400                                VMBuiltinFunctionIndex::get_imported_table_set_index()
5401                            },
5402                        ) as i32,
5403                    ),
5404                    Location::GPR(self.machine.get_gpr_for_call()),
5405                )?;
5406
5407                self.emit_call_native(
5408                    |this| {
5409                        this.machine
5410                            .emit_call_register(this.machine.get_gpr_for_call())
5411                    },
5412                    // [vmctx, table_index, elem_index, reftype]
5413                    [
5414                        (
5415                            Location::Imm32(table_index.index() as u32),
5416                            CanonicalizeType::None,
5417                        ),
5418                        index,
5419                        value,
5420                    ]
5421                    .iter()
5422                    .cloned(),
5423                    [WpType::I32, WpType::I32, WpType::I64].iter().cloned(),
5424                    iter::empty(),
5425                    NativeCallType::IncludeVMCtxArgument,
5426                )?;
5427            }
5428            Operator::TableGet { table: index } => {
5429                let table_index = TableIndex::new(index as _);
5430                let index = self.value_stack.pop().unwrap();
5431
5432                self.machine.move_location(
5433                    Size::S64,
5434                    Location::Memory(
5435                        self.machine.get_vmctx_reg(),
5436                        self.vmoffsets.vmctx_builtin_function(
5437                            if self.module.local_table_index(table_index).is_some() {
5438                                VMBuiltinFunctionIndex::get_table_get_index()
5439                            } else {
5440                                VMBuiltinFunctionIndex::get_imported_table_get_index()
5441                            },
5442                        ) as i32,
5443                    ),
5444                    Location::GPR(self.machine.get_gpr_for_call()),
5445                )?;
5446
5447                self.emit_call_native(
5448                    |this| {
5449                        this.machine
5450                            .emit_call_register(this.machine.get_gpr_for_call())
5451                    },
5452                    // [vmctx, table_index, elem_index] -> reftype
5453                    [
5454                        (
5455                            Location::Imm32(table_index.index() as u32),
5456                            CanonicalizeType::None,
5457                        ),
5458                        index,
5459                    ]
5460                    .iter()
5461                    .cloned(),
5462                    [WpType::I32, WpType::I32].iter().cloned(),
5463                    iter::once(WpType::Ref(WpRefType::new(true, WpHeapType::FUNC).unwrap())),
5464                    NativeCallType::IncludeVMCtxArgument,
5465                )?;
5466            }
5467            Operator::TableSize { table: index } => {
5468                let table_index = TableIndex::new(index as _);
5469
5470                self.machine.move_location(
5471                    Size::S64,
5472                    Location::Memory(
5473                        self.machine.get_vmctx_reg(),
5474                        self.vmoffsets.vmctx_builtin_function(
5475                            if self.module.local_table_index(table_index).is_some() {
5476                                VMBuiltinFunctionIndex::get_table_size_index()
5477                            } else {
5478                                VMBuiltinFunctionIndex::get_imported_table_size_index()
5479                            },
5480                        ) as i32,
5481                    ),
5482                    Location::GPR(self.machine.get_gpr_for_call()),
5483                )?;
5484
5485                self.emit_call_native(
5486                    |this| {
5487                        this.machine
5488                            .emit_call_register(this.machine.get_gpr_for_call())
5489                    },
5490                    // [vmctx, table_index] -> i32
5491                    iter::once((
5492                        Location::Imm32(table_index.index() as u32),
5493                        CanonicalizeType::None,
5494                    )),
5495                    iter::once(WpType::I32),
5496                    iter::once(WpType::I32),
5497                    NativeCallType::IncludeVMCtxArgument,
5498                )?;
5499            }
5500            Operator::TableGrow { table: index } => {
5501                let table_index = TableIndex::new(index as _);
5502                let delta = self.value_stack.pop().unwrap();
5503                let init_value = self.value_stack.pop().unwrap();
5504
5505                self.machine.move_location(
5506                    Size::S64,
5507                    Location::Memory(
5508                        self.machine.get_vmctx_reg(),
5509                        self.vmoffsets.vmctx_builtin_function(
5510                            if self.module.local_table_index(table_index).is_some() {
5511                                VMBuiltinFunctionIndex::get_table_grow_index()
5512                            } else {
5513                                VMBuiltinFunctionIndex::get_imported_table_grow_index()
5514                            },
5515                        ) as i32,
5516                    ),
5517                    Location::GPR(self.machine.get_gpr_for_call()),
5518                )?;
5519
5520                self.emit_call_native(
5521                    |this| {
5522                        this.machine
5523                            .emit_call_register(this.machine.get_gpr_for_call())
5524                    },
5525                    // [vmctx, init_value, delta, table_index] -> u32
5526                    [
5527                        init_value,
5528                        delta,
5529                        (
5530                            Location::Imm32(table_index.index() as u32),
5531                            CanonicalizeType::None,
5532                        ),
5533                    ]
5534                    .iter()
5535                    .cloned(),
5536                    [WpType::I64, WpType::I32, WpType::I32].iter().cloned(),
5537                    iter::once(WpType::I32),
5538                    NativeCallType::IncludeVMCtxArgument,
5539                )?;
5540            }
5541            Operator::TableCopy {
5542                dst_table,
5543                src_table,
5544            } => {
5545                let len = self.value_stack.pop().unwrap();
5546                let src = self.value_stack.pop().unwrap();
5547                let dest = self.value_stack.pop().unwrap();
5548
5549                self.machine.move_location(
5550                    Size::S64,
5551                    Location::Memory(
5552                        self.machine.get_vmctx_reg(),
5553                        self.vmoffsets
5554                            .vmctx_builtin_function(VMBuiltinFunctionIndex::get_table_copy_index())
5555                            as i32,
5556                    ),
5557                    Location::GPR(self.machine.get_gpr_for_call()),
5558                )?;
5559
5560                self.emit_call_native(
5561                    |this| {
5562                        this.machine
5563                            .emit_call_register(this.machine.get_gpr_for_call())
5564                    },
5565                    // [vmctx, dst_table_index, src_table_index, dst, src, len]
5566                    [
5567                        (Location::Imm32(dst_table), CanonicalizeType::None),
5568                        (Location::Imm32(src_table), CanonicalizeType::None),
5569                        dest,
5570                        src,
5571                        len,
5572                    ]
5573                    .iter()
5574                    .cloned(),
5575                    [
5576                        WpType::I32,
5577                        WpType::I32,
5578                        WpType::I32,
5579                        WpType::I32,
5580                        WpType::I32,
5581                    ]
5582                    .iter()
5583                    .cloned(),
5584                    iter::empty(),
5585                    NativeCallType::IncludeVMCtxArgument,
5586                )?;
5587            }
5588
5589            Operator::TableFill { table } => {
5590                let len = self.value_stack.pop().unwrap();
5591                let val = self.value_stack.pop().unwrap();
5592                let dest = self.value_stack.pop().unwrap();
5593
5594                self.machine.move_location(
5595                    Size::S64,
5596                    Location::Memory(
5597                        self.machine.get_vmctx_reg(),
5598                        self.vmoffsets
5599                            .vmctx_builtin_function(VMBuiltinFunctionIndex::get_table_fill_index())
5600                            as i32,
5601                    ),
5602                    Location::GPR(self.machine.get_gpr_for_call()),
5603                )?;
5604
5605                self.emit_call_native(
5606                    |this| {
5607                        this.machine
5608                            .emit_call_register(this.machine.get_gpr_for_call())
5609                    },
5610                    // [vmctx, table_index, start_idx, item, len]
5611                    [
5612                        (Location::Imm32(table), CanonicalizeType::None),
5613                        dest,
5614                        val,
5615                        len,
5616                    ]
5617                    .iter()
5618                    .cloned(),
5619                    [WpType::I32, WpType::I32, WpType::I64, WpType::I32]
5620                        .iter()
5621                        .cloned(),
5622                    iter::empty(),
5623                    NativeCallType::IncludeVMCtxArgument,
5624                )?;
5625            }
5626            Operator::TableInit { elem_index, table } => {
5627                let len = self.value_stack.pop().unwrap();
5628                let src = self.value_stack.pop().unwrap();
5629                let dest = self.value_stack.pop().unwrap();
5630
5631                self.machine.move_location(
5632                    Size::S64,
5633                    Location::Memory(
5634                        self.machine.get_vmctx_reg(),
5635                        self.vmoffsets
5636                            .vmctx_builtin_function(VMBuiltinFunctionIndex::get_table_init_index())
5637                            as i32,
5638                    ),
5639                    Location::GPR(self.machine.get_gpr_for_call()),
5640                )?;
5641
5642                self.emit_call_native(
5643                    |this| {
5644                        this.machine
5645                            .emit_call_register(this.machine.get_gpr_for_call())
5646                    },
5647                    // [vmctx, table_index, elem_index, dst, src, len]
5648                    [
5649                        (Location::Imm32(table), CanonicalizeType::None),
5650                        (Location::Imm32(elem_index), CanonicalizeType::None),
5651                        dest,
5652                        src,
5653                        len,
5654                    ]
5655                    .iter()
5656                    .cloned(),
5657                    [
5658                        WpType::I32,
5659                        WpType::I32,
5660                        WpType::I32,
5661                        WpType::I32,
5662                        WpType::I32,
5663                    ]
5664                    .iter()
5665                    .cloned(),
5666                    iter::empty(),
5667                    NativeCallType::IncludeVMCtxArgument,
5668                )?;
5669            }
5670            Operator::ElemDrop { elem_index } => {
5671                self.machine.move_location(
5672                    Size::S64,
5673                    Location::Memory(
5674                        self.machine.get_vmctx_reg(),
5675                        self.vmoffsets
5676                            .vmctx_builtin_function(VMBuiltinFunctionIndex::get_elem_drop_index())
5677                            as i32,
5678                    ),
5679                    Location::GPR(self.machine.get_gpr_for_call()),
5680                )?;
5681
5682                self.emit_call_native(
5683                    |this| {
5684                        this.machine
5685                            .emit_call_register(this.machine.get_gpr_for_call())
5686                    },
5687                    // [vmctx, elem_index]
5688                    iter::once((Location::Imm32(elem_index), CanonicalizeType::None)),
5689                    [WpType::I32].iter().cloned(),
5690                    iter::empty(),
5691                    NativeCallType::IncludeVMCtxArgument,
5692                )?;
5693            }
5694            Operator::MemoryAtomicWait32 { ref memarg } => {
5695                let timeout = self.value_stack.pop().unwrap();
5696                let val = self.value_stack.pop().unwrap();
5697                let dst = self.value_stack.pop().unwrap();
5698
5699                let memory_index = MemoryIndex::new(memarg.memory as usize);
5700                let (memory_atomic_wait32, memory_index) =
5701                    if self.module.local_memory_index(memory_index).is_some() {
5702                        (
5703                            VMBuiltinFunctionIndex::get_memory_atomic_wait32_index(),
5704                            memory_index,
5705                        )
5706                    } else {
5707                        (
5708                            VMBuiltinFunctionIndex::get_imported_memory_atomic_wait32_index(),
5709                            memory_index,
5710                        )
5711                    };
5712
5713                self.machine.move_location(
5714                    Size::S64,
5715                    Location::Memory(
5716                        self.machine.get_vmctx_reg(),
5717                        self.vmoffsets.vmctx_builtin_function(memory_atomic_wait32) as i32,
5718                    ),
5719                    Location::GPR(self.machine.get_gpr_for_call()),
5720                )?;
5721
5722                self.emit_call_native(
5723                    |this| {
5724                        this.machine
5725                            .emit_call_register(this.machine.get_gpr_for_call())
5726                    },
5727                    // [vmctx, memory_index, dst, src, timeout]
5728                    [
5729                        (
5730                            Location::Imm32(memory_index.index() as u32),
5731                            CanonicalizeType::None,
5732                        ),
5733                        dst,
5734                        val,
5735                        timeout,
5736                    ]
5737                    .iter()
5738                    .cloned(),
5739                    [WpType::I32, WpType::I32, WpType::I32, WpType::I64]
5740                        .iter()
5741                        .cloned(),
5742                    iter::once(WpType::I32),
5743                    NativeCallType::IncludeVMCtxArgument,
5744                )?;
5745            }
5746            Operator::MemoryAtomicWait64 { ref memarg } => {
5747                let timeout = self.value_stack.pop().unwrap();
5748                let val = self.value_stack.pop().unwrap();
5749                let dst = self.value_stack.pop().unwrap();
5750
5751                let memory_index = MemoryIndex::new(memarg.memory as usize);
5752                let (memory_atomic_wait64, memory_index) =
5753                    if self.module.local_memory_index(memory_index).is_some() {
5754                        (
5755                            VMBuiltinFunctionIndex::get_memory_atomic_wait64_index(),
5756                            memory_index,
5757                        )
5758                    } else {
5759                        (
5760                            VMBuiltinFunctionIndex::get_imported_memory_atomic_wait64_index(),
5761                            memory_index,
5762                        )
5763                    };
5764
5765                self.machine.move_location(
5766                    Size::S64,
5767                    Location::Memory(
5768                        self.machine.get_vmctx_reg(),
5769                        self.vmoffsets.vmctx_builtin_function(memory_atomic_wait64) as i32,
5770                    ),
5771                    Location::GPR(self.machine.get_gpr_for_call()),
5772                )?;
5773
5774                self.emit_call_native(
5775                    |this| {
5776                        this.machine
5777                            .emit_call_register(this.machine.get_gpr_for_call())
5778                    },
5779                    // [vmctx, memory_index, dst, src, timeout]
5780                    [
5781                        (
5782                            Location::Imm32(memory_index.index() as u32),
5783                            CanonicalizeType::None,
5784                        ),
5785                        dst,
5786                        val,
5787                        timeout,
5788                    ]
5789                    .iter()
5790                    .cloned(),
5791                    [WpType::I32, WpType::I32, WpType::I64, WpType::I64]
5792                        .iter()
5793                        .cloned(),
5794                    iter::once(WpType::I32),
5795                    NativeCallType::IncludeVMCtxArgument,
5796                )?;
5797            }
5798            Operator::MemoryAtomicNotify { ref memarg } => {
5799                let cnt = self.value_stack.pop().unwrap();
5800                let dst = self.value_stack.pop().unwrap();
5801
5802                let memory_index = MemoryIndex::new(memarg.memory as usize);
5803                let (memory_atomic_notify, memory_index) =
5804                    if self.module.local_memory_index(memory_index).is_some() {
5805                        (
5806                            VMBuiltinFunctionIndex::get_memory_atomic_notify_index(),
5807                            memory_index,
5808                        )
5809                    } else {
5810                        (
5811                            VMBuiltinFunctionIndex::get_imported_memory_atomic_notify_index(),
5812                            memory_index,
5813                        )
5814                    };
5815
5816                self.machine.move_location(
5817                    Size::S64,
5818                    Location::Memory(
5819                        self.machine.get_vmctx_reg(),
5820                        self.vmoffsets.vmctx_builtin_function(memory_atomic_notify) as i32,
5821                    ),
5822                    Location::GPR(self.machine.get_gpr_for_call()),
5823                )?;
5824
5825                self.emit_call_native(
5826                    |this| {
5827                        this.machine
5828                            .emit_call_register(this.machine.get_gpr_for_call())
5829                    },
5830                    // [vmctx, memory_index, dst, cnt]
5831                    [
5832                        (
5833                            Location::Imm32(memory_index.index() as u32),
5834                            CanonicalizeType::None,
5835                        ),
5836                        dst,
5837                        cnt,
5838                    ]
5839                    .iter()
5840                    .cloned(),
5841                    [WpType::I32, WpType::I32, WpType::I32].iter().cloned(),
5842                    iter::once(WpType::I32),
5843                    NativeCallType::IncludeVMCtxArgument,
5844                )?;
5845            }
5846            _ => {
5847                return Err(CompileError::Codegen(format!(
5848                    "not yet implemented: {op:?}"
5849                )));
5850            }
5851        }
5852
5853        Ok(())
5854    }
5855
5856    fn add_assembly_comment(&mut self, comment: AssemblyComment) {
5857        // Collect assembly comments only if we're going to emit them.
5858        if self.config.callbacks.is_some() {
5859            self.assembly_comments
5860                .insert(self.machine.get_offset().0, comment);
5861        }
5862    }
5863
5864    pub fn finalize(
5865        mut self,
5866        data: &FunctionBodyData,
5867        arch: Architecture,
5868    ) -> Result<(CompiledFunction, Option<UnwindFrame>), CompileError> {
5869        self.stack_offset -= RED_ZONE_SIZE;
5870
5871        self.add_assembly_comment(AssemblyComment::TrapHandlersTable);
5872        // Generate actual code for special labels.
5873        self.machine
5874            .emit_label(self.special_labels.integer_division_by_zero)?;
5875        self.machine
5876            .emit_illegal_op(TrapCode::IntegerDivisionByZero)?;
5877
5878        self.machine
5879            .emit_label(self.special_labels.integer_overflow)?;
5880        self.machine.emit_illegal_op(TrapCode::IntegerOverflow)?;
5881
5882        self.machine
5883            .emit_label(self.special_labels.heap_access_oob)?;
5884        self.machine
5885            .emit_illegal_op(TrapCode::HeapAccessOutOfBounds)?;
5886
5887        self.machine
5888            .emit_label(self.special_labels.table_access_oob)?;
5889        self.machine
5890            .emit_illegal_op(TrapCode::TableAccessOutOfBounds)?;
5891
5892        self.machine
5893            .emit_label(self.special_labels.indirect_call_null)?;
5894        self.machine.emit_illegal_op(TrapCode::IndirectCallToNull)?;
5895
5896        self.machine.emit_label(self.special_labels.bad_signature)?;
5897        self.machine.emit_illegal_op(TrapCode::BadSignature)?;
5898
5899        self.machine
5900            .emit_label(self.special_labels.unaligned_atomic)?;
5901        self.machine.emit_illegal_op(TrapCode::UnalignedAtomic)?;
5902
5903        // Notify the assembler backend to generate necessary code at end of function.
5904        self.machine.finalize_function()?;
5905
5906        let body_len = self.machine.assembler_get_offset().0;
5907
5908        #[cfg_attr(not(feature = "unwind"), allow(unused_mut))]
5909        let mut unwind_info = None;
5910        #[cfg_attr(not(feature = "unwind"), allow(unused_mut))]
5911        let mut fde = None;
5912        #[cfg(feature = "unwind")]
5913        match self.calling_convention {
5914            CallingConvention::SystemV | CallingConvention::AppleAarch64 => {
5915                let unwind = self.machine.gen_dwarf_unwind_info(body_len);
5916                if let Some(unwind) = unwind {
5917                    fde = Some(unwind.to_fde(Address::Symbol {
5918                        symbol: WriterRelocate::FUNCTION_SYMBOL,
5919                        addend: self.local_func_index.index() as _,
5920                    }));
5921                    unwind_info = Some(CompiledFunctionUnwindInfo::Dwarf);
5922                }
5923            }
5924            CallingConvention::WindowsFastcall => {
5925                let unwind = self.machine.gen_windows_unwind_info(body_len);
5926                if let Some(unwind) = unwind {
5927                    unwind_info = Some(CompiledFunctionUnwindInfo::WindowsX64(unwind));
5928                }
5929            }
5930            _ => (),
5931        };
5932
5933        let address_map =
5934            get_function_address_map(self.machine.instructions_address_map(), data, body_len);
5935        let traps = self.machine.collect_trap_information();
5936        let FinalizedAssembly {
5937            mut body,
5938            assembly_comments,
5939        } = self.machine.assembler_finalize(self.assembly_comments)?;
5940        body.shrink_to_fit();
5941
5942        if let Some(callbacks) = self.config.callbacks.as_ref() {
5943            callbacks.obj_memory_buffer(
5944                &CompiledKind::Local(self.local_func_index, self.function_name.clone()),
5945                &self.module.hash_string(),
5946                &body,
5947            );
5948            callbacks.asm_memory_buffer(
5949                &CompiledKind::Local(self.local_func_index, self.function_name.clone()),
5950                &self.module.hash_string(),
5951                arch,
5952                &body,
5953                assembly_comments,
5954            )?;
5955        }
5956
5957        Ok((
5958            CompiledFunction {
5959                body: FunctionBody { body, unwind_info },
5960                relocations: self.relocations.clone(),
5961                frame_info: CompiledFunctionFrameInfo { traps, address_map },
5962                maximum_stack_usage: Some(self.stack_offset.maximum_offset),
5963            },
5964            fde,
5965        ))
5966    }
5967    // FIXME: This implementation seems to be not enough to resolve all kinds of register dependencies
5968    // at call place.
5969    #[allow(clippy::type_complexity)]
5970    fn sort_call_movs(movs: &mut [(Location<M::GPR, M::SIMD>, M::GPR, Size)]) {
5971        for i in 0..movs.len() {
5972            for j in (i + 1)..movs.len() {
5973                if let Location::GPR(src_gpr) = movs[j].0
5974                    && src_gpr == movs[i].1
5975                {
5976                    movs.swap(i, j);
5977                }
5978            }
5979        }
5980    }
5981
5982    // Cycle detector. Uncomment this to debug possibly incorrect call-mov sequences.
5983    /*
5984    {
5985        use std::collections::{HashMap, HashSet, VecDeque};
5986        let mut mov_map: HashMap<GPR, HashSet<GPR>> = HashMap::new();
5987        for mov in movs.iter() {
5988            if let Location::GPR(src_gpr) = mov.0 {
5989                if src_gpr != mov.1 {
5990                    mov_map.entry(src_gpr).or_insert_with(|| HashSet::new()).insert(mov.1);
5991                }
5992            }
5993        }
5994
5995        for (start, _) in mov_map.iter() {
5996            let mut q: VecDeque<GPR> = VecDeque::new();
5997            let mut black: HashSet<GPR> = HashSet::new();
5998
5999            q.push_back(*start);
6000            black.insert(*start);
6001
6002            while q.len() > 0 {
6003                let reg = q.pop_front().unwrap();
6004                let empty_set = HashSet::new();
6005                for x in mov_map.get(&reg).unwrap_or(&empty_set).iter() {
6006                    if black.contains(x) {
6007                        panic!("cycle detected");
6008                    }
6009                    q.push_back(*x);
6010                    black.insert(*x);
6011                }
6012            }
6013        }
6014    }
6015    */
6016}