Skip to main content

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