wasmer_compiler_singlepass/
codegen.rs

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