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    /// Function signature.
66    signature: FunctionType,
67
68    // Working storage.
69    /// Memory locations of local variables.
70    locals: Vec<Location<M::GPR, M::SIMD>>,
71
72    /// Types of local variables, including arguments.
73    local_types: Vec<WpType>,
74
75    /// Value stack.
76    value_stack: Vec<LocationWithCanonicalization<M>>,
77
78    /// A list of frames describing the current control stack.
79    control_stack: Vec<ControlFrame<M>>,
80
81    /// Stack offset tracking in bytes.
82    stack_offset: usize,
83
84    save_area_offset: Option<usize>,
85
86    /// Low-level machine state.
87    machine: M,
88
89    /// Nesting level of unreachable code.
90    unreachable_depth: usize,
91
92    /// Index of a function defined locally inside the WebAssembly module.
93    local_func_index: LocalFunctionIndex,
94
95    /// Relocation information.
96    relocations: Vec<Relocation>,
97
98    /// A set of special labels for trapping.
99    special_labels: SpecialLabelSet,
100
101    /// Calling convention to use.
102    calling_convention: CallingConvention,
103
104    /// Name of the function.
105    function_name: String,
106
107    /// Assembly comments.
108    assembly_comments: HashMap<usize, AssemblyComment>,
109}
110
111struct SpecialLabelSet {
112    integer_division_by_zero: Label,
113    integer_overflow: Label,
114    heap_access_oob: Label,
115    table_access_oob: Label,
116    indirect_call_null: Label,
117    bad_signature: Label,
118    unaligned_atomic: Label,
119}
120
121/// Type of a pending canonicalization floating point value.
122/// Sometimes we don't have the type information elsewhere and therefore we need to track it here.
123#[derive(Copy, Clone, Debug)]
124pub(crate) enum CanonicalizeType {
125    None,
126    F32,
127    F64,
128}
129
130impl CanonicalizeType {
131    fn to_size(self) -> Option<Size> {
132        match self {
133            CanonicalizeType::F32 => Some(Size::S32),
134            CanonicalizeType::F64 => Some(Size::S64),
135            CanonicalizeType::None => None,
136        }
137    }
138
139    fn promote(self) -> Result<Self, CompileError> {
140        match self {
141            CanonicalizeType::None => Ok(CanonicalizeType::None),
142            CanonicalizeType::F32 => Ok(CanonicalizeType::F64),
143            CanonicalizeType::F64 => codegen_error!("cannot promote F64"),
144        }
145    }
146
147    fn demote(self) -> Result<Self, CompileError> {
148        match self {
149            CanonicalizeType::None => Ok(CanonicalizeType::None),
150            CanonicalizeType::F32 => codegen_error!("cannot demote F64"),
151            CanonicalizeType::F64 => Ok(CanonicalizeType::F32),
152        }
153    }
154}
155
156trait WpTypeExt {
157    fn is_float(&self) -> bool;
158}
159
160impl WpTypeExt for WpType {
161    fn is_float(&self) -> bool {
162        matches!(self, WpType::F32 | WpType::F64)
163    }
164}
165
166#[derive(Clone)]
167pub enum ControlState<M: Machine> {
168    Function,
169    Block,
170    Loop,
171    If {
172        label_else: Label,
173        // Store the input parameters for the If block, as they'll need to be
174        // restored when processing the Else block (if present).
175        inputs: SmallVec<[LocationWithCanonicalization<M>; 1]>,
176    },
177    Else,
178}
179
180#[derive(Clone)]
181struct ControlFrame<M: Machine> {
182    pub state: ControlState<M>,
183    pub label: Label,
184    pub param_types: SmallVec<[WpType; 8]>,
185    pub return_types: SmallVec<[WpType; 1]>,
186    /// Value stack depth at the beginning of the frame (including params and results).
187    value_stack_depth: usize,
188}
189
190impl<M: Machine> ControlFrame<M> {
191    // Get value stack depth at the end of the frame.
192    fn value_stack_depth_after(&self) -> usize {
193        let mut depth: usize = self.value_stack_depth - self.param_types.len();
194
195        // For Loop, we have to use another slot for params that implements the PHI operation.
196        if matches!(self.state, ControlState::Loop) {
197            depth -= self.param_types.len();
198        }
199
200        depth
201    }
202
203    /// Returns the value stack depth at which resources should be deallocated.
204    /// For loops, this preserves PHI arguments by excluding them from deallocation.
205    fn value_stack_depth_for_release(&self) -> usize {
206        self.value_stack_depth - self.param_types.len()
207    }
208}
209
210fn type_to_wp_type(ty: &Type) -> WpType {
211    match ty {
212        Type::I32 => WpType::I32,
213        Type::I64 => WpType::I64,
214        Type::F32 => WpType::F32,
215        Type::F64 => WpType::F64,
216        Type::V128 => WpType::V128,
217        Type::ExternRef => WpType::Ref(WpRefType::new(true, WpHeapType::EXTERN).unwrap()),
218        Type::FuncRef => WpType::Ref(WpRefType::new(true, WpHeapType::FUNC).unwrap()),
219        Type::ExceptionRef => todo!(),
220    }
221}
222
223/// Abstraction for a 2-input, 1-output operator. Can be an integer/floating-point
224/// binop/cmpop.
225struct I2O1<R: Reg, S: Reg> {
226    loc_a: Location<R, S>,
227    loc_b: Location<R, S>,
228    ret: Location<R, S>,
229}
230
231/// Type of native call we emit.
232enum NativeCallType {
233    IncludeVMCtxArgument,
234    Unreachable,
235}
236
237impl<'a, M: Machine> FuncGen<'a, M> {
238    /// Acquires location from the machine state.
239    ///
240    /// If the returned location is used for stack value, `release_location` needs to be called on it;
241    /// Otherwise, if the returned locations is used for a local, `release_location` does not need to be called on it.
242    fn acquire_location(&mut self, ty: &WpType) -> Result<Location<M::GPR, M::SIMD>, CompileError> {
243        let loc = match *ty {
244            WpType::F32 | WpType::F64 => self.machine.pick_simd().map(Location::SIMD),
245            WpType::I32 | WpType::I64 => self.machine.pick_gpr().map(Location::GPR),
246            WpType::Ref(ty) if ty.is_extern_ref() || ty.is_func_ref() => {
247                self.machine.pick_gpr().map(Location::GPR)
248            }
249            _ => codegen_error!("can't acquire location for type {:?}", ty),
250        };
251
252        let Some(loc) = loc else {
253            return self.acquire_location_on_stack();
254        };
255
256        if let Location::GPR(x) = loc {
257            self.machine.reserve_gpr(x);
258        } else if let Location::SIMD(x) = loc {
259            self.machine.reserve_simd(x);
260        }
261        Ok(loc)
262    }
263
264    /// Acquire location that will live on the stack.
265    fn acquire_location_on_stack(&mut self) -> Result<Location<M::GPR, M::SIMD>, CompileError> {
266        self.stack_offset += 8;
267        let loc = self.machine.local_on_stack(self.stack_offset as i32);
268        self.machine
269            .extend_stack(self.machine.round_stack_adjust(8) as u32)?;
270
271        Ok(loc)
272    }
273
274    /// Releases locations used for stack value.
275    fn release_locations(
276        &mut self,
277        locs: &[LocationWithCanonicalization<M>],
278    ) -> Result<(), CompileError> {
279        self.release_stack_locations(locs)?;
280        self.release_reg_locations(locs)
281    }
282
283    fn release_reg_locations(
284        &mut self,
285        locs: &[LocationWithCanonicalization<M>],
286    ) -> Result<(), CompileError> {
287        for (loc, _) in locs.iter().rev() {
288            match *loc {
289                Location::GPR(ref x) => {
290                    self.machine.release_gpr(*x);
291                }
292                Location::SIMD(ref x) => {
293                    self.machine.release_simd(*x);
294                }
295                _ => {}
296            }
297        }
298        Ok(())
299    }
300
301    fn release_stack_locations(
302        &mut self,
303        locs: &[LocationWithCanonicalization<M>],
304    ) -> Result<(), CompileError> {
305        for (loc, _) in locs.iter().rev() {
306            if let Location::Memory(..) = *loc {
307                self.check_location_on_stack(loc, self.stack_offset)?;
308                self.stack_offset -= 8;
309                self.machine
310                    .truncate_stack(self.machine.round_stack_adjust(8) as u32)?;
311            }
312        }
313
314        Ok(())
315    }
316
317    fn release_stack_locations_keep_stack_offset(
318        &mut self,
319        stack_depth: usize,
320    ) -> Result<(), CompileError> {
321        let mut stack_offset = self.stack_offset;
322        let locs = &self.value_stack[stack_depth..];
323
324        for (loc, _) in locs.iter().rev() {
325            if let Location::Memory(..) = *loc {
326                self.check_location_on_stack(loc, stack_offset)?;
327                stack_offset -= 8;
328                self.machine
329                    .truncate_stack(self.machine.round_stack_adjust(8) as u32)?;
330            }
331        }
332
333        Ok(())
334    }
335
336    fn check_location_on_stack(
337        &self,
338        loc: &Location<M::GPR, M::SIMD>,
339        expected_stack_offset: usize,
340    ) -> Result<(), CompileError> {
341        let Location::Memory(reg, offset) = loc else {
342            codegen_error!("Expected stack memory location");
343        };
344        if reg != &self.machine.local_pointer() {
345            codegen_error!("Expected location pointer for value on stack");
346        }
347        if *offset >= 0 {
348            codegen_error!("Invalid memory offset {offset}");
349        }
350        let offset = offset.neg() as usize;
351        if offset != expected_stack_offset {
352            codegen_error!("Invalid memory offset {offset}!={}", self.stack_offset);
353        }
354        Ok(())
355    }
356
357    /// Allocate return slots for block operands (Block, If, Loop) and swap them with
358    /// the corresponding input parameters on the value stack.
359    ///
360    /// This method reserves memory slots that can accommodate both integer and
361    /// floating-point types, then swaps these slots with the last `stack_slots`
362    /// values on the stack to position them correctly for the block's return values.
363    /// that are already present at the value stack.
364    fn allocate_return_slots_and_swap(
365        &mut self,
366        stack_slots: usize,
367        return_slots: usize,
368    ) -> Result<(), CompileError> {
369        // No shuffling needed.
370        if return_slots == 0 {
371            return Ok(());
372        }
373
374        /* To allocate N return slots, we first allocate N additional stack (memory) slots and then "shift" the
375        existing stack slots. This results in the layout: [value stack before frame, ret0, ret1, ret2, ..., retN, arg0, arg1, ..., argN],
376        where some of the argN values may reside in registers and others in memory on the stack. */
377        let latest_slots = self
378            .value_stack
379            .drain(self.value_stack.len() - stack_slots..)
380            .collect_vec();
381        let extra_slots = (0..return_slots)
382            .map(|_| self.acquire_location_on_stack())
383            .collect::<Result<Vec<_>, _>>()?;
384
385        let mut all_memory_slots = latest_slots
386            .iter()
387            .filter_map(|(loc, _)| {
388                if let Location::Memory(..) = loc {
389                    Some(loc)
390                } else {
391                    None
392                }
393            })
394            .chain(extra_slots.iter())
395            .collect_vec();
396
397        // First put the newly allocated return values to the value stack.
398        self.value_stack.extend(
399            all_memory_slots
400                .iter()
401                .take(return_slots)
402                .map(|loc| (**loc, CanonicalizeType::None)),
403        );
404
405        // Then map all memory stack slots to a new location (in reverse order).
406        let mut new_params_reversed = Vec::new();
407        for (loc, canonicalize) in latest_slots.iter().rev() {
408            let mapped_loc = if matches!(loc, Location::Memory(..)) {
409                let dest = all_memory_slots.pop().unwrap();
410                self.machine.emit_relaxed_mov(Size::S64, *loc, *dest)?;
411                *dest
412            } else {
413                *loc
414            };
415            new_params_reversed.push((mapped_loc, *canonicalize));
416        }
417        self.value_stack
418            .extend(new_params_reversed.into_iter().rev());
419
420        Ok(())
421    }
422
423    #[allow(clippy::type_complexity)]
424    fn init_locals(
425        &mut self,
426        n: usize,
427        sig: FunctionType,
428        calling_convention: CallingConvention,
429    ) -> Result<Vec<Location<M::GPR, M::SIMD>>, CompileError> {
430        self.add_assembly_comment(AssemblyComment::InitializeLocals);
431
432        // How many machine stack slots will all the locals use?
433        let num_mem_slots = (0..n)
434            .filter(|&x| self.machine.is_local_on_stack(x))
435            .count();
436
437        // Total size (in bytes) of the pre-allocated "static area" for this function's
438        // locals and callee-saved registers.
439        let mut static_area_size: usize = 0;
440
441        // Callee-saved registers used for locals.
442        // Keep this consistent with the "Save callee-saved registers" code below.
443        for i in 0..n {
444            // If a local is not stored on stack, then it is allocated to a callee-saved register.
445            if !self.machine.is_local_on_stack(i) {
446                static_area_size += 8;
447            }
448        }
449
450        // Callee-saved vmctx.
451        static_area_size += 8;
452
453        // Some ABI (like Windows) needs extrat reg save
454        static_area_size += 8 * self.machine.list_to_save(calling_convention).len();
455
456        // Total size of callee saved registers.
457        let callee_saved_regs_size = static_area_size;
458
459        // Now we can determine concrete locations for locals.
460        let locations: Vec<Location<M::GPR, M::SIMD>> = (0..n)
461            .map(|i| self.machine.get_local_location(i, callee_saved_regs_size))
462            .collect();
463
464        // Add size of locals on stack.
465        static_area_size += num_mem_slots * 8;
466
467        // Allocate save area, without actually writing to it.
468        static_area_size = self.machine.round_stack_adjust(static_area_size);
469
470        // Stack probe.
471        //
472        // `rep stosq` writes data from low address to high address and may skip the stack guard page.
473        // so here we probe it explicitly when needed.
474        for i in (sig.params().len()..n)
475            .step_by(NATIVE_PAGE_SIZE / 8)
476            .skip(1)
477        {
478            self.machine.zero_location(Size::S64, locations[i])?;
479        }
480
481        self.machine.extend_stack(static_area_size as _)?;
482
483        // Save callee-saved registers.
484        for loc in locations.iter() {
485            if let Location::GPR(_) = *loc {
486                self.stack_offset += 8;
487                self.machine.move_local(self.stack_offset as i32, *loc)?;
488            }
489        }
490
491        // Save the Reg use for vmctx.
492        self.stack_offset += 8;
493        self.machine.move_local(
494            self.stack_offset as i32,
495            Location::GPR(self.machine.get_vmctx_reg()),
496        )?;
497
498        // Check if need to same some CallingConvention specific regs
499        let regs_to_save = self.machine.list_to_save(calling_convention);
500        for loc in regs_to_save.iter() {
501            self.stack_offset += 8;
502            self.machine.move_local(self.stack_offset as i32, *loc)?;
503        }
504
505        // Save the offset of register save area.
506        self.save_area_offset = Some(self.stack_offset);
507
508        // Load in-register parameters into the allocated locations.
509        // Locals are allocated on the stack from higher address to lower address,
510        // so we won't skip the stack guard page here.
511        let mut stack_offset: usize = 0;
512        for (i, param) in sig.params().iter().enumerate() {
513            let sz = match *param {
514                Type::I32 | Type::F32 => Size::S32,
515                Type::I64 | Type::F64 => Size::S64,
516                Type::ExternRef | Type::FuncRef => Size::S64,
517                _ => {
518                    codegen_error!("singlepass init_local unimplemented type: {param}")
519                }
520            };
521            let loc = self.machine.get_call_param_location(
522                sig.results().len(),
523                i + 1,
524                sz,
525                &mut stack_offset,
526                calling_convention,
527            );
528            self.machine
529                .move_location_extend(sz, false, loc, Size::S64, locations[i])?;
530        }
531
532        // Load vmctx into it's GPR.
533        self.machine.move_location(
534            Size::S64,
535            Location::GPR(
536                self.machine
537                    .get_simple_param_location(0, calling_convention),
538            ),
539            Location::GPR(self.machine.get_vmctx_reg()),
540        )?;
541
542        // Initialize all normal locals to zero.
543        let mut init_stack_loc_cnt = 0;
544        let mut last_stack_loc = Location::Memory(self.machine.local_pointer(), i32::MAX);
545        for location in locations.iter().take(n).skip(sig.params().len()) {
546            match location {
547                Location::Memory(_, _) => {
548                    init_stack_loc_cnt += 1;
549                    last_stack_loc = cmp::min(last_stack_loc, *location);
550                }
551                Location::GPR(_) => {
552                    self.machine.zero_location(Size::S64, *location)?;
553                }
554                _ => codegen_error!("singlepass init_local unreachable"),
555            }
556        }
557        if init_stack_loc_cnt > 0 {
558            self.machine
559                .init_stack_loc(init_stack_loc_cnt, last_stack_loc)?;
560        }
561
562        // Add the size of all locals allocated to stack.
563        self.stack_offset += static_area_size - callee_saved_regs_size;
564
565        Ok(locations)
566    }
567
568    fn finalize_locals(
569        &mut self,
570        calling_convention: CallingConvention,
571    ) -> Result<(), CompileError> {
572        // Unwind stack to the "save area".
573        self.machine
574            .restore_saved_area(self.save_area_offset.unwrap() as i32)?;
575
576        let regs_to_save = self.machine.list_to_save(calling_convention);
577        for loc in regs_to_save.iter().rev() {
578            self.machine.pop_location(*loc)?;
579        }
580
581        // Restore register used by vmctx.
582        self.machine
583            .pop_location(Location::GPR(self.machine.get_vmctx_reg()))?;
584
585        // Restore callee-saved registers.
586        for loc in self.locals.iter().rev() {
587            if let Location::GPR(_) = *loc {
588                self.machine.pop_location(*loc)?;
589            }
590        }
591        Ok(())
592    }
593
594    /// Set the source location of the Wasm to the given offset.
595    pub fn set_srcloc(&mut self, offset: u32) {
596        self.machine.set_srcloc(offset);
597    }
598
599    fn get_location_released(
600        &mut self,
601        loc: (Location<M::GPR, M::SIMD>, CanonicalizeType),
602    ) -> Result<LocationWithCanonicalization<M>, CompileError> {
603        self.release_locations(&[loc])?;
604        Ok(loc)
605    }
606
607    fn pop_value_released(&mut self) -> Result<LocationWithCanonicalization<M>, CompileError> {
608        let loc = self.value_stack.pop().ok_or_else(|| {
609            CompileError::Codegen("pop_value_released: value stack is empty".to_owned())
610        })?;
611        self.get_location_released(loc)?;
612        Ok(loc)
613    }
614
615    /// Prepare data for binary operator with 2 inputs and 1 output.
616    fn i2o1_prepare(
617        &mut self,
618        ty: WpType,
619        canonicalize: CanonicalizeType,
620    ) -> Result<I2O1<M::GPR, M::SIMD>, CompileError> {
621        let loc_b = self.pop_value_released()?.0;
622        let loc_a = self.pop_value_released()?.0;
623        let ret = self.acquire_location(&ty)?;
624        self.value_stack.push((ret, canonicalize));
625        Ok(I2O1 { loc_a, loc_b, ret })
626    }
627
628    /// Emits a Native ABI call sequence.
629    ///
630    /// The caller MUST NOT hold any temporary registers allocated by `acquire_temp_gpr` when calling
631    /// this function.
632    fn emit_call_native<
633        I: Iterator<Item = (Location<M::GPR, M::SIMD>, CanonicalizeType)>,
634        J: Iterator<Item = WpType>,
635        K: Iterator<Item = WpType>,
636        F: FnOnce(&mut Self) -> Result<(), CompileError>,
637    >(
638        &mut self,
639        cb: F,
640        params: I,
641        params_type: J,
642        return_types: K,
643        call_type: NativeCallType,
644    ) -> Result<(), CompileError> {
645        let params = params.collect_vec();
646        let stack_params = params
647            .iter()
648            .copied()
649            .filter(|(param, _)| {
650                if let Location::Memory(reg, _) = param {
651                    debug_assert_eq!(reg, &self.machine.local_pointer());
652                    true
653                } else {
654                    false
655                }
656            })
657            .collect_vec();
658        let get_size = |param_type: WpType| match param_type {
659            WpType::F32 | WpType::I32 => Size::S32,
660            WpType::V128 => unimplemented!(),
661            _ => Size::S64,
662        };
663        let param_sizes = params_type.map(get_size).collect_vec();
664        let return_value_sizes = return_types.map(get_size).collect_vec();
665
666        /* We're going to reuse the memory param locations for the return values. Any extra needed slots will be allocated on stack. */
667        let used_stack_params = stack_params
668            .iter()
669            .take(return_value_sizes.len())
670            .copied()
671            .collect_vec();
672        let mut return_values = used_stack_params.clone();
673        let extra_return_values = (0..return_value_sizes.len().saturating_sub(stack_params.len()))
674            .map(|_| -> Result<_, CompileError> {
675                Ok((self.acquire_location_on_stack()?, CanonicalizeType::None))
676            })
677            .collect::<Result<Vec<_>, _>>()?;
678        return_values.extend(extra_return_values);
679
680        // Release the parameter slots that live in registers.
681        self.release_reg_locations(&params)?;
682
683        // Save used GPRs. Preserve correct stack alignment
684        let used_gprs = self.machine.get_used_gprs();
685        let mut used_stack = self.machine.push_used_gpr(&used_gprs)?;
686
687        // Save used SIMD registers.
688        let used_simds = self.machine.get_used_simd();
689        if !used_simds.is_empty() {
690            used_stack += self.machine.push_used_simd(&used_simds)?;
691        }
692        // mark the GPR used for Call as used
693        self.machine
694            .reserve_unused_temp_gpr(self.machine.get_gpr_for_call());
695
696        let calling_convention = self.calling_convention;
697
698        let stack_padding: usize = match calling_convention {
699            CallingConvention::WindowsFastcall => 32,
700            _ => 0,
701        };
702
703        let mut stack_offset: usize = 0;
704        // Allocate space for return values relative to SP (the allocation happens in reverse order, thus start with return slots).
705        let mut return_args = Vec::with_capacity(return_value_sizes.len());
706        for i in 0..return_value_sizes.len() {
707            return_args.push(self.machine.get_return_value_location(
708                i,
709                &mut stack_offset,
710                self.calling_convention,
711            ));
712        }
713
714        // Allocate space for arguments relative to SP.
715        let mut args = Vec::with_capacity(params.len());
716        for (i, param_size) in param_sizes.iter().enumerate() {
717            args.push(self.machine.get_param_location(
718                match call_type {
719                    NativeCallType::IncludeVMCtxArgument => 1,
720                    NativeCallType::Unreachable => 0,
721                } + i,
722                *param_size,
723                &mut stack_offset,
724                calling_convention,
725            ));
726        }
727
728        // Align stack to 16 bytes.
729        let stack_unaligned =
730            (self.machine.round_stack_adjust(self.stack_offset) + used_stack + stack_offset) % 16;
731        if stack_unaligned != 0 {
732            stack_offset += 16 - stack_unaligned;
733        }
734        self.machine.extend_stack(stack_offset as u32)?;
735
736        #[allow(clippy::type_complexity)]
737        let mut call_movs = Vec::new();
738        // Prepare register & stack parameters.
739        for (i, ((param, _), param_size)) in
740            (params.iter().zip(param_sizes.iter())).enumerate().rev()
741        {
742            let loc = args[i];
743            match loc {
744                Location::GPR(x) => {
745                    call_movs.push((*param, x, *param_size));
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, size) in call_movs {
764            if loc != Location::GPR(gpr) {
765                self.machine
766                    .move_location(Size::S64, loc, Location::GPR(gpr))?;
767            }
768            // Adjust the argument if required by ABI
769            self.machine.adjust_gpr_param_location(gpr, size)?;
770        }
771
772        if matches!(call_type, NativeCallType::IncludeVMCtxArgument) {
773            // Put vmctx as the first parameter.
774            self.machine.move_location(
775                Size::S64,
776                Location::GPR(self.machine.get_vmctx_reg()),
777                Location::GPR(
778                    self.machine
779                        .get_simple_param_location(0, calling_convention),
780                ),
781            )?; // vmctx
782        }
783
784        if stack_padding > 0 {
785            self.machine.extend_stack(stack_padding as u32)?;
786        }
787        // release the GPR used for call
788        self.machine.release_gpr(self.machine.get_gpr_for_call());
789
790        let begin = self.machine.assembler_get_offset().0;
791        cb(self)?;
792        if matches!(call_type, NativeCallType::Unreachable) {
793            let end = self.machine.assembler_get_offset().0;
794            self.machine.mark_address_range_with_trap_code(
795                TrapCode::UnreachableCodeReached,
796                begin,
797                end,
798            );
799        }
800
801        // Take the returned values from the fn call.
802        for (i, &return_type) in return_value_sizes.iter().enumerate() {
803            self.machine.move_location_for_native(
804                return_type,
805                return_args[i],
806                return_values[i].0,
807            )?;
808        }
809
810        // Restore stack.
811        if stack_offset + stack_padding > 0 {
812            self.machine
813                .truncate_stack((stack_offset + stack_padding) as u32)?;
814        }
815
816        // Restore SIMDs.
817        if !used_simds.is_empty() {
818            self.machine.pop_used_simd(&used_simds)?;
819        }
820
821        // Restore GPRs.
822        self.machine.pop_used_gpr(&used_gprs)?;
823
824        // We are re-using the params for the return values, thus release just the chunk
825        // we're not planning to use!
826        let params_to_release =
827            &stack_params[cmp::min(stack_params.len(), return_value_sizes.len())..];
828        self.release_stack_locations(params_to_release)?;
829
830        self.value_stack.extend(return_values);
831
832        Ok(())
833    }
834
835    /// Emits a memory operation.
836    fn op_memory<
837        F: FnOnce(&mut Self, bool, bool, i32, Label, Label) -> Result<(), CompileError>,
838    >(
839        &mut self,
840        cb: F,
841    ) -> Result<(), CompileError> {
842        let need_check = match self.memory_styles[MemoryIndex::new(0)] {
843            MemoryStyle::Static { .. } => false,
844            MemoryStyle::Dynamic { .. } => true,
845        };
846
847        let offset = if self.module.num_imported_memories != 0 {
848            self.vmoffsets
849                .vmctx_vmmemory_import_definition(MemoryIndex::new(0))
850        } else {
851            self.vmoffsets
852                .vmctx_vmmemory_definition(LocalMemoryIndex::new(0))
853        };
854        cb(
855            self,
856            need_check,
857            self.module.num_imported_memories != 0,
858            offset as i32,
859            self.special_labels.heap_access_oob,
860            self.special_labels.unaligned_atomic,
861        )
862    }
863
864    fn emit_head(&mut self) -> Result<(), CompileError> {
865        self.add_assembly_comment(AssemblyComment::FunctionPrologue);
866        self.machine.emit_function_prolog()?;
867
868        // Initialize locals.
869        self.locals = self.init_locals(
870            self.local_types.len(),
871            self.signature.clone(),
872            self.calling_convention,
873        )?;
874
875        // simulate "red zone" if not supported by the platform
876        self.add_assembly_comment(AssemblyComment::RedZone);
877        self.machine.extend_stack(32)?;
878
879        let return_types: SmallVec<_> = self
880            .signature
881            .results()
882            .iter()
883            .map(type_to_wp_type)
884            .collect();
885
886        // Push return value slots for the function return on the stack.
887        self.value_stack.extend((0..return_types.len()).map(|i| {
888            (
889                self.machine
890                    .get_call_return_value_location(i, self.calling_convention),
891                CanonicalizeType::None,
892            )
893        }));
894
895        self.control_stack.push(ControlFrame {
896            state: ControlState::Function,
897            label: self.machine.get_label(),
898            value_stack_depth: return_types.len(),
899            param_types: smallvec![],
900            return_types,
901        });
902
903        // TODO: Full preemption by explicit signal checking
904
905        // We insert set StackOverflow as the default trap that can happen
906        // anywhere in the function prologue.
907        self.machine.insert_stackoverflow();
908        self.add_assembly_comment(AssemblyComment::FunctionBody);
909
910        Ok(())
911    }
912
913    #[allow(clippy::too_many_arguments)]
914    pub fn new(
915        module: &'a ModuleInfo,
916        config: &'a Singlepass,
917        vmoffsets: &'a VMOffsets,
918        memory_styles: &'a PrimaryMap<MemoryIndex, MemoryStyle>,
919        _table_styles: &'a PrimaryMap<TableIndex, TableStyle>,
920        local_func_index: LocalFunctionIndex,
921        local_types_excluding_arguments: &[WpType],
922        machine: M,
923        calling_convention: CallingConvention,
924    ) -> Result<FuncGen<'a, M>, CompileError> {
925        let func_index = module.func_index(local_func_index);
926        let sig_index = module.functions[func_index];
927        let signature = module.signatures[sig_index].clone();
928
929        let mut local_types: Vec<_> = signature.params().iter().map(type_to_wp_type).collect();
930        local_types.extend_from_slice(local_types_excluding_arguments);
931
932        let mut machine = machine;
933        let special_labels = SpecialLabelSet {
934            integer_division_by_zero: machine.get_label(),
935            integer_overflow: machine.get_label(),
936            heap_access_oob: machine.get_label(),
937            table_access_oob: machine.get_label(),
938            indirect_call_null: machine.get_label(),
939            bad_signature: machine.get_label(),
940            unaligned_atomic: machine.get_label(),
941        };
942        let function_name = module
943            .function_names
944            .get(&func_index)
945            .map(|fname| fname.to_string())
946            .unwrap_or_else(|| format!("function_{}", func_index.as_u32()));
947
948        let mut fg = FuncGen {
949            module,
950            config,
951            vmoffsets,
952            memory_styles,
953            // table_styles,
954            signature,
955            locals: vec![], // initialization deferred to emit_head
956            local_types,
957            value_stack: vec![],
958            control_stack: vec![],
959            stack_offset: 0,
960            save_area_offset: None,
961            machine,
962            unreachable_depth: 0,
963            local_func_index,
964            relocations: vec![],
965            special_labels,
966            calling_convention,
967            function_name,
968            assembly_comments: HashMap::new(),
969        };
970        fg.emit_head()?;
971        Ok(fg)
972    }
973
974    pub fn has_control_frames(&self) -> bool {
975        !self.control_stack.is_empty()
976    }
977
978    /// Moves the top `return_values` items from the value stack into the
979    /// preallocated return slots starting at `value_stack_depth_after`.
980    ///
981    /// Used when completing Block/If/Loop constructs or returning from the
982    /// function. Applies NaN canonicalization when enabled and supported.
983    fn emit_return_values(
984        &mut self,
985        value_stack_depth_after: usize,
986        return_values: usize,
987    ) -> Result<(), CompileError> {
988        for (i, (stack_value, canonicalize)) in self
989            .value_stack
990            .iter()
991            .rev()
992            .take(return_values)
993            .enumerate()
994        {
995            let dst = self.value_stack[value_stack_depth_after - i - 1].0;
996            if let Some(canonicalize_size) = canonicalize.to_size()
997                && self.config.enable_nan_canonicalization
998            {
999                self.machine
1000                    .canonicalize_nan(canonicalize_size, *stack_value, dst)?;
1001            } else {
1002                self.machine
1003                    .emit_relaxed_mov(Size::S64, *stack_value, dst)?;
1004            }
1005        }
1006
1007        Ok(())
1008    }
1009
1010    /// Similar to `emit_return_values`, except it stores the `return_values` items into the slots
1011    /// preallocated for parameters of a loop.
1012    fn emit_loop_params_store(
1013        &mut self,
1014        value_stack_depth_after: usize,
1015        param_count: usize,
1016    ) -> Result<(), CompileError> {
1017        for (i, (stack_value, _)) in self
1018            .value_stack
1019            .iter()
1020            .rev()
1021            .take(param_count)
1022            .rev()
1023            .enumerate()
1024        {
1025            let dst = self.value_stack[value_stack_depth_after + i].0;
1026            self.machine
1027                .emit_relaxed_mov(Size::S64, *stack_value, dst)?;
1028        }
1029
1030        Ok(())
1031    }
1032
1033    fn return_types_for_block(&self, block_type: WpTypeOrFuncType) -> SmallVec<[WpType; 1]> {
1034        match block_type {
1035            WpTypeOrFuncType::Empty => smallvec![],
1036            WpTypeOrFuncType::Type(inner_ty) => smallvec![inner_ty],
1037            WpTypeOrFuncType::FuncType(sig_index) => SmallVec::from_iter(
1038                self.module.signatures[SignatureIndex::from_u32(sig_index)]
1039                    .results()
1040                    .iter()
1041                    .map(type_to_wp_type),
1042            ),
1043        }
1044    }
1045
1046    fn param_types_for_block(&self, block_type: WpTypeOrFuncType) -> SmallVec<[WpType; 8]> {
1047        match block_type {
1048            WpTypeOrFuncType::Empty | WpTypeOrFuncType::Type(_) => smallvec![],
1049            WpTypeOrFuncType::FuncType(sig_index) => SmallVec::from_iter(
1050                self.module.signatures[SignatureIndex::from_u32(sig_index)]
1051                    .params()
1052                    .iter()
1053                    .map(type_to_wp_type),
1054            ),
1055        }
1056    }
1057
1058    pub fn feed_operator(&mut self, op: Operator) -> Result<(), CompileError> {
1059        let was_unreachable;
1060
1061        if self.unreachable_depth > 0 {
1062            was_unreachable = true;
1063
1064            match op {
1065                Operator::Block { .. } | Operator::Loop { .. } | Operator::If { .. } => {
1066                    self.unreachable_depth += 1;
1067                }
1068                Operator::End => {
1069                    self.unreachable_depth -= 1;
1070                }
1071                Operator::Else
1072                    if self.unreachable_depth == 1
1073                        && self.control_stack.last().is_some_and(|frame| {
1074                            matches!(frame.state, ControlState::If { .. })
1075                        }) =>
1076                {
1077                    // We are in a reachable true branch
1078                    self.unreachable_depth -= 1;
1079                }
1080
1081                _ => {}
1082            }
1083            if self.unreachable_depth > 0 {
1084                return Ok(());
1085            }
1086        } else {
1087            was_unreachable = false;
1088        }
1089
1090        match op {
1091            Operator::GlobalGet { global_index } => {
1092                let global_index = GlobalIndex::from_u32(global_index);
1093
1094                let ty = type_to_wp_type(&self.module.globals[global_index].ty);
1095                let loc = self.acquire_location(&ty)?;
1096                self.value_stack.push((loc, CanonicalizeType::None));
1097
1098                let (src, tmp) = if let Some(local_global_index) =
1099                    self.module.local_global_index(global_index)
1100                {
1101                    let offset = self.vmoffsets.vmctx_vmglobal_definition(local_global_index);
1102                    (
1103                        Location::Memory(self.machine.get_vmctx_reg(), offset as i32),
1104                        None,
1105                    )
1106                } else {
1107                    // Imported globals require one level of indirection.
1108                    let tmp = self.machine.acquire_temp_gpr().unwrap();
1109                    let offset = self
1110                        .vmoffsets
1111                        .vmctx_vmglobal_import_definition(global_index);
1112                    self.machine.emit_relaxed_mov(
1113                        Size::S64,
1114                        Location::Memory(self.machine.get_vmctx_reg(), offset as i32),
1115                        Location::GPR(tmp),
1116                    )?;
1117                    (Location::Memory(tmp, 0), Some(tmp))
1118                };
1119
1120                self.machine.emit_relaxed_mov(Size::S64, src, loc)?;
1121
1122                if let Some(tmp) = tmp {
1123                    self.machine.release_gpr(tmp);
1124                }
1125            }
1126            Operator::GlobalSet { global_index } => {
1127                let global_index = GlobalIndex::from_u32(global_index);
1128                let (dst, tmp) = if let Some(local_global_index) =
1129                    self.module.local_global_index(global_index)
1130                {
1131                    let offset = self.vmoffsets.vmctx_vmglobal_definition(local_global_index);
1132                    (
1133                        Location::Memory(self.machine.get_vmctx_reg(), offset as i32),
1134                        None,
1135                    )
1136                } else {
1137                    // Imported globals require one level of indirection.
1138                    let tmp = self.machine.acquire_temp_gpr().unwrap();
1139                    let offset = self
1140                        .vmoffsets
1141                        .vmctx_vmglobal_import_definition(global_index);
1142                    self.machine.emit_relaxed_mov(
1143                        Size::S64,
1144                        Location::Memory(self.machine.get_vmctx_reg(), offset as i32),
1145                        Location::GPR(tmp),
1146                    )?;
1147                    (Location::Memory(tmp, 0), Some(tmp))
1148                };
1149                let (loc, canonicalize) = self.pop_value_released()?;
1150                if let Some(canonicalize_size) = canonicalize.to_size() {
1151                    if self.config.enable_nan_canonicalization {
1152                        self.machine.canonicalize_nan(canonicalize_size, loc, dst)?;
1153                    } else {
1154                        self.machine.emit_relaxed_mov(Size::S64, loc, dst)?;
1155                    }
1156                } else {
1157                    self.machine.emit_relaxed_mov(Size::S64, loc, dst)?;
1158                }
1159                if let Some(tmp) = tmp {
1160                    self.machine.release_gpr(tmp);
1161                }
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 expected_sig_hash = self.module.signature_hashes.get(index).unwrap();
2240                let table = self.module.tables.get(table_index).unwrap();
2241                let local_fixed_funcref_table = self
2242                    .module
2243                    .local_table_index(table_index)
2244                    .filter(|_| table.is_fixed_funcref_table());
2245                let param_types: SmallVec<[WpType; 8]> =
2246                    sig.params().iter().map(type_to_wp_type).collect();
2247                let return_types: SmallVec<[WpType; 1]> =
2248                    sig.results().iter().map(type_to_wp_type).collect();
2249
2250                let func_index = self.pop_value_released()?.0;
2251
2252                let params: SmallVec<[_; 8]> = self
2253                    .value_stack
2254                    .drain(self.value_stack.len() - param_types.len()..)
2255                    .collect();
2256
2257                // Pop arguments off the FP stack and canonicalize them if needed.
2258                //
2259                // Canonicalization state will be lost across function calls, so early canonicalization
2260                // is necessary here.
2261                if self.config.enable_nan_canonicalization {
2262                    for (loc, canonicalize) in params.iter() {
2263                        if let Some(size) = canonicalize.to_size() {
2264                            self.machine.canonicalize_nan(size, *loc, *loc)?;
2265                        }
2266                    }
2267                }
2268
2269                let table_base = self.machine.acquire_temp_gpr().unwrap();
2270                let table_count = self.machine.acquire_temp_gpr().unwrap();
2271                let sig_hash = self.machine.acquire_temp_gpr().unwrap();
2272
2273                if let Some(local_table_index) = local_fixed_funcref_table {
2274                    self.machine.move_location(
2275                        Size::S64,
2276                        Location::GPR(self.machine.get_vmctx_reg()),
2277                        Location::GPR(table_base),
2278                    )?;
2279                    self.machine.location_add(
2280                        Size::S64,
2281                        Location::Imm32(
2282                            self.vmoffsets
2283                                .vmctx_fixed_funcref_table_anyfuncs(local_table_index)
2284                                .expect("fixed funcref table must have inline VMContext storage"),
2285                        ),
2286                        Location::GPR(table_base),
2287                        false,
2288                    )?;
2289                    self.machine.move_location(
2290                        Size::S32,
2291                        Location::Imm32(table.minimum),
2292                        Location::GPR(table_count),
2293                    )?;
2294                } else if let Some(local_table_index) = self.module.local_table_index(table_index) {
2295                    let (vmctx_offset_base, vmctx_offset_len) = (
2296                        self.vmoffsets.vmctx_vmtable_definition(local_table_index),
2297                        self.vmoffsets
2298                            .vmctx_vmtable_definition_current_elements(local_table_index),
2299                    );
2300                    self.machine.move_location(
2301                        Size::S64,
2302                        Location::Memory(self.machine.get_vmctx_reg(), vmctx_offset_base as i32),
2303                        Location::GPR(table_base),
2304                    )?;
2305                    self.machine.move_location(
2306                        Size::S32,
2307                        Location::Memory(self.machine.get_vmctx_reg(), vmctx_offset_len as i32),
2308                        Location::GPR(table_count),
2309                    )?;
2310                } else {
2311                    // Do an indirection.
2312                    let import_offset = self.vmoffsets.vmctx_vmtable_import(table_index);
2313                    self.machine.move_location(
2314                        Size::S64,
2315                        Location::Memory(self.machine.get_vmctx_reg(), import_offset as i32),
2316                        Location::GPR(table_base),
2317                    )?;
2318
2319                    // Load len.
2320                    self.machine.move_location(
2321                        Size::S32,
2322                        Location::Memory(
2323                            table_base,
2324                            self.vmoffsets.vmtable_definition_current_elements() as _,
2325                        ),
2326                        Location::GPR(table_count),
2327                    )?;
2328
2329                    // Load base.
2330                    self.machine.move_location(
2331                        Size::S64,
2332                        Location::Memory(table_base, self.vmoffsets.vmtable_definition_base() as _),
2333                        Location::GPR(table_base),
2334                    )?;
2335                }
2336
2337                self.machine.jmp_on_condition(
2338                    UnsignedCondition::BelowEqual,
2339                    Size::S32,
2340                    Location::GPR(table_count),
2341                    func_index,
2342                    self.special_labels.table_access_oob,
2343                )?;
2344                self.machine
2345                    .move_location(Size::S32, func_index, Location::GPR(table_count))?;
2346                self.machine.emit_imul_imm32(
2347                    Size::S64,
2348                    if local_fixed_funcref_table.is_some() {
2349                        self.vmoffsets.size_of_vmcaller_checked_anyfunc() as u32
2350                    } else {
2351                        self.vmoffsets.size_of_vm_funcref() as u32
2352                    },
2353                    table_count,
2354                )?;
2355                self.machine.location_add(
2356                    Size::S64,
2357                    Location::GPR(table_base),
2358                    Location::GPR(table_count),
2359                    false,
2360                )?;
2361
2362                if local_fixed_funcref_table.is_some() {
2363                    self.machine.move_location(
2364                        Size::S64,
2365                        Location::Memory(
2366                            table_count,
2367                            self.vmoffsets.vmcaller_checked_anyfunc_func_ptr() as i32,
2368                        ),
2369                        Location::GPR(table_base),
2370                    )?;
2371                    self.machine.jmp_on_condition(
2372                        UnsignedCondition::Equal,
2373                        Size::S64,
2374                        Location::GPR(table_base),
2375                        Location::Imm32(0),
2376                        self.special_labels.indirect_call_null,
2377                    )?;
2378                } else {
2379                    // deref the table to get a VMFuncRef
2380                    self.machine.move_location(
2381                        Size::S64,
2382                        Location::Memory(
2383                            table_count,
2384                            self.vmoffsets.vm_funcref_anyfunc_ptr() as i32,
2385                        ),
2386                        Location::GPR(table_count),
2387                    )?;
2388                    // Trap if the FuncRef is null
2389                    self.machine.jmp_on_condition(
2390                        UnsignedCondition::Equal,
2391                        Size::S64,
2392                        Location::GPR(table_count),
2393                        Location::Imm32(0),
2394                        self.special_labels.indirect_call_null,
2395                    )?;
2396                }
2397                self.machine.move_location(
2398                    Size::S32,
2399                    Location::Imm32(expected_sig_hash.as_u32()),
2400                    Location::GPR(sig_hash),
2401                )?;
2402
2403                // Trap if signature mismatches.
2404                self.machine.jmp_on_condition(
2405                    UnsignedCondition::NotEqual,
2406                    Size::S32,
2407                    Location::GPR(sig_hash),
2408                    Location::Memory(
2409                        table_count,
2410                        (self.vmoffsets.vmcaller_checked_anyfunc_signature_hash() as usize) as i32,
2411                    ),
2412                    self.special_labels.bad_signature,
2413                )?;
2414                self.machine.release_gpr(sig_hash);
2415                self.machine.release_gpr(table_count);
2416                self.machine.release_gpr(table_base);
2417
2418                let gpr_for_call = self.machine.get_gpr_for_call();
2419                if table_count != gpr_for_call {
2420                    self.machine.move_location(
2421                        Size::S64,
2422                        Location::GPR(table_count),
2423                        Location::GPR(gpr_for_call),
2424                    )?;
2425                }
2426
2427                let vmcaller_checked_anyfunc_func_ptr =
2428                    self.vmoffsets.vmcaller_checked_anyfunc_func_ptr() as usize;
2429                let vmcaller_checked_anyfunc_vmctx =
2430                    self.vmoffsets.vmcaller_checked_anyfunc_vmctx() as usize;
2431                let calling_convention = self.calling_convention;
2432
2433                self.emit_call_native(
2434                    |this| {
2435                        let offset = this
2436                            .machine
2437                            .mark_instruction_with_trap_code(TrapCode::StackOverflow);
2438
2439                        // We set the context pointer
2440                        this.machine.move_location(
2441                            Size::S64,
2442                            Location::Memory(gpr_for_call, vmcaller_checked_anyfunc_vmctx as i32),
2443                            Location::GPR(
2444                                this.machine
2445                                    .get_simple_param_location(0, calling_convention),
2446                            ),
2447                        )?;
2448
2449                        this.machine.emit_call_location(Location::Memory(
2450                            gpr_for_call,
2451                            vmcaller_checked_anyfunc_func_ptr as i32,
2452                        ))?;
2453                        this.machine.mark_instruction_address_end(offset);
2454                        Ok(())
2455                    },
2456                    params.iter().copied(),
2457                    param_types.iter().copied(),
2458                    return_types.iter().copied(),
2459                    NativeCallType::IncludeVMCtxArgument,
2460                )?;
2461            }
2462            Operator::If { blockty } => {
2463                let label_end = self.machine.get_label();
2464                let label_else = self.machine.get_label();
2465
2466                let return_types = self.return_types_for_block(blockty);
2467                let param_types = self.param_types_for_block(blockty);
2468                self.allocate_return_slots_and_swap(param_types.len() + 1, return_types.len())?;
2469
2470                let cond = self.pop_value_released()?.0;
2471
2472                /* We might hit a situation where an Operator::If is missing an Operator::Else. In such a situation,
2473                the result value just fallthrough from the If block inputs! However, we don't know the information upfront. */
2474                if param_types.len() == return_types.len() {
2475                    for (input, return_value) in self
2476                        .value_stack
2477                        .iter()
2478                        .rev()
2479                        .take(param_types.len())
2480                        .zip(self.value_stack.iter().rev().skip(param_types.len()))
2481                    {
2482                        self.machine
2483                            .emit_relaxed_mov(Size::S64, input.0, return_value.0)?;
2484                    }
2485                }
2486
2487                let frame = ControlFrame {
2488                    state: ControlState::If {
2489                        label_else,
2490                        inputs: SmallVec::from_iter(
2491                            self.value_stack
2492                                .iter()
2493                                .rev()
2494                                .take(param_types.len())
2495                                .rev()
2496                                .copied(),
2497                        ),
2498                    },
2499                    label: label_end,
2500                    param_types,
2501                    return_types,
2502                    value_stack_depth: self.value_stack.len(),
2503                };
2504                self.control_stack.push(frame);
2505                self.machine.jmp_on_condition(
2506                    UnsignedCondition::Equal,
2507                    Size::S32,
2508                    cond,
2509                    Location::Imm32(0),
2510                    label_else,
2511                )?;
2512            }
2513            Operator::Else => {
2514                let frame = self.control_stack.last().unwrap();
2515
2516                if !was_unreachable && !frame.return_types.is_empty() {
2517                    self.emit_return_values(
2518                        frame.value_stack_depth_after(),
2519                        frame.return_types.len(),
2520                    )?;
2521                }
2522
2523                let frame = &self.control_stack.last_mut().unwrap();
2524                let locs = self
2525                    .value_stack
2526                    .drain(frame.value_stack_depth_after()..)
2527                    .collect_vec();
2528                self.release_locations(&locs)?;
2529                let frame = &mut self.control_stack.last_mut().unwrap();
2530
2531                // The Else block must be provided the very same inputs as the previous If block had,
2532                // and so we need to copy the already consumed stack values.
2533                let ControlState::If {
2534                    label_else,
2535                    ref inputs,
2536                } = frame.state
2537                else {
2538                    panic!("Operator::Else must be connected to Operator::If statement");
2539                };
2540                for (input, _) in inputs {
2541                    match input {
2542                        Location::GPR(x) => {
2543                            self.machine.reserve_gpr(*x);
2544                        }
2545                        Location::SIMD(x) => {
2546                            self.machine.reserve_simd(*x);
2547                        }
2548                        Location::Memory(reg, _) => {
2549                            debug_assert_eq!(reg, &self.machine.local_pointer());
2550                            self.stack_offset += 8;
2551                        }
2552                        _ => {}
2553                    }
2554                }
2555                self.value_stack.extend(inputs);
2556
2557                self.machine.jmp_unconditional(frame.label)?;
2558                self.machine.emit_label(label_else)?;
2559                frame.state = ControlState::Else;
2560            }
2561            // `TypedSelect` must be used for extern refs so ref counting should
2562            // be done with TypedSelect. But otherwise they're the same.
2563            Operator::TypedSelect { .. } | Operator::Select => {
2564                let cond = self.pop_value_released()?.0;
2565                let (v_b, canonicalize_b) = self.pop_value_released()?;
2566                let (v_a, canonicalize_a) = self.pop_value_released()?;
2567                let ret = self.acquire_location(&WpType::I64)?;
2568                self.value_stack.push((ret, CanonicalizeType::None));
2569
2570                let end_label = self.machine.get_label();
2571                let zero_label = self.machine.get_label();
2572
2573                self.machine.jmp_on_condition(
2574                    UnsignedCondition::Equal,
2575                    Size::S32,
2576                    cond,
2577                    Location::Imm32(0),
2578                    zero_label,
2579                )?;
2580                if self.config.enable_nan_canonicalization
2581                    && let Some(size) = canonicalize_a.to_size()
2582                {
2583                    self.machine.canonicalize_nan(size, v_a, ret)?;
2584                } else if v_a != ret {
2585                    self.machine.emit_relaxed_mov(Size::S64, v_a, ret)?;
2586                }
2587                self.machine.jmp_unconditional(end_label)?;
2588                self.machine.emit_label(zero_label)?;
2589                if self.config.enable_nan_canonicalization
2590                    && let Some(size) = canonicalize_b.to_size()
2591                {
2592                    self.machine.canonicalize_nan(size, v_b, ret)?;
2593                } else if v_b != ret {
2594                    self.machine.emit_relaxed_mov(Size::S64, v_b, ret)?;
2595                }
2596                self.machine.emit_label(end_label)?;
2597            }
2598            Operator::Block { blockty } => {
2599                let return_types = self.return_types_for_block(blockty);
2600                let param_types = self.param_types_for_block(blockty);
2601                self.allocate_return_slots_and_swap(param_types.len(), return_types.len())?;
2602
2603                let frame = ControlFrame {
2604                    state: ControlState::Block,
2605                    label: self.machine.get_label(),
2606                    param_types,
2607                    return_types,
2608                    value_stack_depth: self.value_stack.len(),
2609                };
2610                self.control_stack.push(frame);
2611            }
2612            Operator::Loop { blockty } => {
2613                self.machine.align_for_loop()?;
2614                let label = self.machine.get_label();
2615
2616                let return_types = self.return_types_for_block(blockty);
2617                let param_types = self.param_types_for_block(blockty);
2618                let params_count = param_types.len();
2619                // We need extra space for params as we need to implement the PHI operation.
2620                self.allocate_return_slots_and_swap(
2621                    param_types.len(),
2622                    param_types.len() + return_types.len(),
2623                )?;
2624
2625                self.control_stack.push(ControlFrame {
2626                    state: ControlState::Loop,
2627                    label,
2628                    param_types: param_types.clone(),
2629                    return_types: return_types.clone(),
2630                    value_stack_depth: self.value_stack.len(),
2631                });
2632
2633                // For proper PHI implementation, we must copy pre-loop params to PHI params.
2634                let params = self
2635                    .value_stack
2636                    .drain((self.value_stack.len() - params_count)..)
2637                    .collect_vec();
2638                for (param, phi_param) in params.iter().rev().zip(self.value_stack.iter().rev()) {
2639                    self.machine
2640                        .emit_relaxed_mov(Size::S64, param.0, phi_param.0)?;
2641                }
2642                self.release_locations(&params)?;
2643
2644                self.machine.emit_label(label)?;
2645
2646                // Put on the stack PHI inputs for further use.
2647                let phi_params = self
2648                    .value_stack
2649                    .iter()
2650                    .rev()
2651                    .take(params_count)
2652                    .rev()
2653                    .copied()
2654                    .collect_vec();
2655                for (i, phi_param) in phi_params.into_iter().enumerate() {
2656                    let loc = self.acquire_location(&param_types[i])?;
2657                    self.machine.emit_relaxed_mov(Size::S64, phi_param.0, loc)?;
2658                    self.value_stack.push((loc, phi_param.1));
2659                }
2660
2661                // TODO: Re-enable interrupt signal check without branching
2662            }
2663            Operator::Nop => {}
2664            Operator::MemorySize { mem } => {
2665                let memory_index = MemoryIndex::new(mem as usize);
2666                self.machine.move_location(
2667                    Size::S64,
2668                    Location::Memory(
2669                        self.machine.get_vmctx_reg(),
2670                        self.vmoffsets.vmctx_builtin_function(
2671                            if self.module.local_memory_index(memory_index).is_some() {
2672                                VMBuiltinFunctionIndex::get_memory32_size_index()
2673                            } else {
2674                                VMBuiltinFunctionIndex::get_imported_memory32_size_index()
2675                            },
2676                        ) as i32,
2677                    ),
2678                    Location::GPR(self.machine.get_gpr_for_call()),
2679                )?;
2680                self.emit_call_native(
2681                    |this| {
2682                        this.machine
2683                            .emit_call_register(this.machine.get_gpr_for_call())
2684                    },
2685                    // [vmctx, memory_index]
2686                    iter::once((
2687                        Location::Imm32(memory_index.index() as u32),
2688                        CanonicalizeType::None,
2689                    )),
2690                    iter::once(WpType::I32),
2691                    iter::once(WpType::I32),
2692                    NativeCallType::IncludeVMCtxArgument,
2693                )?;
2694            }
2695            Operator::MemoryInit { data_index, mem } => {
2696                let len = self.value_stack.pop().unwrap();
2697                let src = self.value_stack.pop().unwrap();
2698                let dst = self.value_stack.pop().unwrap();
2699
2700                self.machine.move_location(
2701                    Size::S64,
2702                    Location::Memory(
2703                        self.machine.get_vmctx_reg(),
2704                        self.vmoffsets
2705                            .vmctx_builtin_function(VMBuiltinFunctionIndex::get_memory_init_index())
2706                            as i32,
2707                    ),
2708                    Location::GPR(self.machine.get_gpr_for_call()),
2709                )?;
2710
2711                self.emit_call_native(
2712                    |this| {
2713                        this.machine
2714                            .emit_call_register(this.machine.get_gpr_for_call())
2715                    },
2716                    // [vmctx, memory_index, data_index, dst, src, len]
2717                    [
2718                        (Location::Imm32(mem), CanonicalizeType::None),
2719                        (Location::Imm32(data_index), CanonicalizeType::None),
2720                        dst,
2721                        src,
2722                        len,
2723                    ]
2724                    .iter()
2725                    .cloned(),
2726                    [
2727                        WpType::I32,
2728                        WpType::I32,
2729                        WpType::I32,
2730                        WpType::I32,
2731                        WpType::I32,
2732                    ]
2733                    .iter()
2734                    .cloned(),
2735                    iter::empty(),
2736                    NativeCallType::IncludeVMCtxArgument,
2737                )?;
2738            }
2739            Operator::DataDrop { data_index } => {
2740                self.machine.move_location(
2741                    Size::S64,
2742                    Location::Memory(
2743                        self.machine.get_vmctx_reg(),
2744                        self.vmoffsets
2745                            .vmctx_builtin_function(VMBuiltinFunctionIndex::get_data_drop_index())
2746                            as i32,
2747                    ),
2748                    Location::GPR(self.machine.get_gpr_for_call()),
2749                )?;
2750
2751                self.emit_call_native(
2752                    |this| {
2753                        this.machine
2754                            .emit_call_register(this.machine.get_gpr_for_call())
2755                    },
2756                    // [vmctx, data_index]
2757                    iter::once((Location::Imm32(data_index), CanonicalizeType::None)),
2758                    iter::once(WpType::I32),
2759                    iter::empty(),
2760                    NativeCallType::IncludeVMCtxArgument,
2761                )?;
2762            }
2763            Operator::MemoryCopy { src_mem, .. } => {
2764                // ignore until we support multiple memories
2765                let len = self.value_stack.pop().unwrap();
2766                let src_pos = self.value_stack.pop().unwrap();
2767                let dst_pos = self.value_stack.pop().unwrap();
2768
2769                let memory_index = MemoryIndex::new(src_mem as usize);
2770                let (memory_copy_index, memory_index) =
2771                    if self.module.local_memory_index(memory_index).is_some() {
2772                        (
2773                            VMBuiltinFunctionIndex::get_memory_copy_index(),
2774                            memory_index,
2775                        )
2776                    } else {
2777                        (
2778                            VMBuiltinFunctionIndex::get_imported_memory_copy_index(),
2779                            memory_index,
2780                        )
2781                    };
2782
2783                self.machine.move_location(
2784                    Size::S64,
2785                    Location::Memory(
2786                        self.machine.get_vmctx_reg(),
2787                        self.vmoffsets.vmctx_builtin_function(memory_copy_index) as i32,
2788                    ),
2789                    Location::GPR(self.machine.get_gpr_for_call()),
2790                )?;
2791
2792                self.emit_call_native(
2793                    |this| {
2794                        this.machine
2795                            .emit_call_register(this.machine.get_gpr_for_call())
2796                    },
2797                    // [vmctx, memory_index, dst, src, len]
2798                    [
2799                        (
2800                            Location::Imm32(memory_index.index() as u32),
2801                            CanonicalizeType::None,
2802                        ),
2803                        dst_pos,
2804                        src_pos,
2805                        len,
2806                    ]
2807                    .iter()
2808                    .cloned(),
2809                    [WpType::I32, WpType::I32, WpType::I32, WpType::I32]
2810                        .iter()
2811                        .cloned(),
2812                    iter::empty(),
2813                    NativeCallType::IncludeVMCtxArgument,
2814                )?;
2815            }
2816            Operator::MemoryFill { mem } => {
2817                let len = self.value_stack.pop().unwrap();
2818                let val = self.value_stack.pop().unwrap();
2819                let dst = self.value_stack.pop().unwrap();
2820
2821                let memory_index = MemoryIndex::new(mem as usize);
2822                let (memory_fill_index, memory_index) =
2823                    if self.module.local_memory_index(memory_index).is_some() {
2824                        (
2825                            VMBuiltinFunctionIndex::get_memory_fill_index(),
2826                            memory_index,
2827                        )
2828                    } else {
2829                        (
2830                            VMBuiltinFunctionIndex::get_imported_memory_fill_index(),
2831                            memory_index,
2832                        )
2833                    };
2834
2835                self.machine.move_location(
2836                    Size::S64,
2837                    Location::Memory(
2838                        self.machine.get_vmctx_reg(),
2839                        self.vmoffsets.vmctx_builtin_function(memory_fill_index) as i32,
2840                    ),
2841                    Location::GPR(self.machine.get_gpr_for_call()),
2842                )?;
2843
2844                self.emit_call_native(
2845                    |this| {
2846                        this.machine
2847                            .emit_call_register(this.machine.get_gpr_for_call())
2848                    },
2849                    // [vmctx, memory_index, dst, src, len]
2850                    [
2851                        (
2852                            Location::Imm32(memory_index.index() as u32),
2853                            CanonicalizeType::None,
2854                        ),
2855                        dst,
2856                        val,
2857                        len,
2858                    ]
2859                    .iter()
2860                    .cloned(),
2861                    [WpType::I32, WpType::I32, WpType::I32, WpType::I32]
2862                        .iter()
2863                        .cloned(),
2864                    iter::empty(),
2865                    NativeCallType::IncludeVMCtxArgument,
2866                )?;
2867            }
2868            Operator::MemoryGrow { mem } => {
2869                let memory_index = MemoryIndex::new(mem as usize);
2870                let param_pages = self.value_stack.pop().unwrap();
2871
2872                self.machine.move_location(
2873                    Size::S64,
2874                    Location::Memory(
2875                        self.machine.get_vmctx_reg(),
2876                        self.vmoffsets.vmctx_builtin_function(
2877                            if self.module.local_memory_index(memory_index).is_some() {
2878                                VMBuiltinFunctionIndex::get_memory32_grow_index()
2879                            } else {
2880                                VMBuiltinFunctionIndex::get_imported_memory32_grow_index()
2881                            },
2882                        ) as i32,
2883                    ),
2884                    Location::GPR(self.machine.get_gpr_for_call()),
2885                )?;
2886
2887                self.emit_call_native(
2888                    |this| {
2889                        this.machine
2890                            .emit_call_register(this.machine.get_gpr_for_call())
2891                    },
2892                    // [vmctx, val, memory_index]
2893                    [
2894                        param_pages,
2895                        (
2896                            Location::Imm32(memory_index.index() as u32),
2897                            CanonicalizeType::None,
2898                        ),
2899                    ]
2900                    .iter()
2901                    .cloned(),
2902                    [WpType::I32, WpType::I32].iter().cloned(),
2903                    iter::once(WpType::I32),
2904                    NativeCallType::IncludeVMCtxArgument,
2905                )?;
2906            }
2907            Operator::I32Load { ref memarg } => {
2908                let target = self.pop_value_released()?.0;
2909                let ret = self.acquire_location(&WpType::I32)?;
2910                self.value_stack.push((ret, CanonicalizeType::None));
2911                self.op_memory(
2912                    |this,
2913                     need_check,
2914                     imported_memories,
2915                     offset,
2916                     heap_access_oob,
2917                     unaligned_atomic| {
2918                        this.machine.i32_load(
2919                            target,
2920                            memarg,
2921                            ret,
2922                            need_check,
2923                            imported_memories,
2924                            offset,
2925                            heap_access_oob,
2926                            unaligned_atomic,
2927                        )
2928                    },
2929                )?;
2930            }
2931            Operator::F32Load { ref memarg } => {
2932                let target = self.pop_value_released()?.0;
2933                let ret = self.acquire_location(&WpType::F32)?;
2934                self.value_stack.push((ret, CanonicalizeType::None));
2935                self.op_memory(
2936                    |this,
2937                     need_check,
2938                     imported_memories,
2939                     offset,
2940                     heap_access_oob,
2941                     unaligned_atomic| {
2942                        this.machine.f32_load(
2943                            target,
2944                            memarg,
2945                            ret,
2946                            need_check,
2947                            imported_memories,
2948                            offset,
2949                            heap_access_oob,
2950                            unaligned_atomic,
2951                        )
2952                    },
2953                )?;
2954            }
2955            Operator::I32Load8U { ref memarg } => {
2956                let target = self.pop_value_released()?.0;
2957                let ret = self.acquire_location(&WpType::I32)?;
2958                self.value_stack.push((ret, CanonicalizeType::None));
2959                self.op_memory(
2960                    |this,
2961                     need_check,
2962                     imported_memories,
2963                     offset,
2964                     heap_access_oob,
2965                     unaligned_atomic| {
2966                        this.machine.i32_load_8u(
2967                            target,
2968                            memarg,
2969                            ret,
2970                            need_check,
2971                            imported_memories,
2972                            offset,
2973                            heap_access_oob,
2974                            unaligned_atomic,
2975                        )
2976                    },
2977                )?;
2978            }
2979            Operator::I32Load8S { ref memarg } => {
2980                let target = self.pop_value_released()?.0;
2981                let ret = self.acquire_location(&WpType::I32)?;
2982                self.value_stack.push((ret, CanonicalizeType::None));
2983                self.op_memory(
2984                    |this,
2985                     need_check,
2986                     imported_memories,
2987                     offset,
2988                     heap_access_oob,
2989                     unaligned_atomic| {
2990                        this.machine.i32_load_8s(
2991                            target,
2992                            memarg,
2993                            ret,
2994                            need_check,
2995                            imported_memories,
2996                            offset,
2997                            heap_access_oob,
2998                            unaligned_atomic,
2999                        )
3000                    },
3001                )?;
3002            }
3003            Operator::I32Load16U { ref memarg } => {
3004                let target = self.pop_value_released()?.0;
3005                let ret = self.acquire_location(&WpType::I32)?;
3006                self.value_stack.push((ret, CanonicalizeType::None));
3007                self.op_memory(
3008                    |this,
3009                     need_check,
3010                     imported_memories,
3011                     offset,
3012                     heap_access_oob,
3013                     unaligned_atomic| {
3014                        this.machine.i32_load_16u(
3015                            target,
3016                            memarg,
3017                            ret,
3018                            need_check,
3019                            imported_memories,
3020                            offset,
3021                            heap_access_oob,
3022                            unaligned_atomic,
3023                        )
3024                    },
3025                )?;
3026            }
3027            Operator::I32Load16S { ref memarg } => {
3028                let target = self.pop_value_released()?.0;
3029                let ret = self.acquire_location(&WpType::I32)?;
3030                self.value_stack.push((ret, CanonicalizeType::None));
3031                self.op_memory(
3032                    |this,
3033                     need_check,
3034                     imported_memories,
3035                     offset,
3036                     heap_access_oob,
3037                     unaligned_atomic| {
3038                        this.machine.i32_load_16s(
3039                            target,
3040                            memarg,
3041                            ret,
3042                            need_check,
3043                            imported_memories,
3044                            offset,
3045                            heap_access_oob,
3046                            unaligned_atomic,
3047                        )
3048                    },
3049                )?;
3050            }
3051            Operator::I32Store { ref memarg } => {
3052                let target_value = self.pop_value_released()?.0;
3053                let target_addr = self.pop_value_released()?.0;
3054                self.op_memory(
3055                    |this,
3056                     need_check,
3057                     imported_memories,
3058                     offset,
3059                     heap_access_oob,
3060                     unaligned_atomic| {
3061                        this.machine.i32_save(
3062                            target_value,
3063                            memarg,
3064                            target_addr,
3065                            need_check,
3066                            imported_memories,
3067                            offset,
3068                            heap_access_oob,
3069                            unaligned_atomic,
3070                        )
3071                    },
3072                )?;
3073            }
3074            Operator::F32Store { ref memarg } => {
3075                let (target_value, canonicalize) = self.pop_value_released()?;
3076                let target_addr = self.pop_value_released()?.0;
3077                self.op_memory(
3078                    |this,
3079                     need_check,
3080                     imported_memories,
3081                     offset,
3082                     heap_access_oob,
3083                     unaligned_atomic| {
3084                        this.machine.f32_save(
3085                            target_value,
3086                            memarg,
3087                            target_addr,
3088                            self.config.enable_nan_canonicalization
3089                                && !matches!(canonicalize, CanonicalizeType::None),
3090                            need_check,
3091                            imported_memories,
3092                            offset,
3093                            heap_access_oob,
3094                            unaligned_atomic,
3095                        )
3096                    },
3097                )?;
3098            }
3099            Operator::I32Store8 { ref memarg } => {
3100                let target_value = self.pop_value_released()?.0;
3101                let target_addr = self.pop_value_released()?.0;
3102                self.op_memory(
3103                    |this,
3104                     need_check,
3105                     imported_memories,
3106                     offset,
3107                     heap_access_oob,
3108                     unaligned_atomic| {
3109                        this.machine.i32_save_8(
3110                            target_value,
3111                            memarg,
3112                            target_addr,
3113                            need_check,
3114                            imported_memories,
3115                            offset,
3116                            heap_access_oob,
3117                            unaligned_atomic,
3118                        )
3119                    },
3120                )?;
3121            }
3122            Operator::I32Store16 { ref memarg } => {
3123                let target_value = self.pop_value_released()?.0;
3124                let target_addr = self.pop_value_released()?.0;
3125                self.op_memory(
3126                    |this,
3127                     need_check,
3128                     imported_memories,
3129                     offset,
3130                     heap_access_oob,
3131                     unaligned_atomic| {
3132                        this.machine.i32_save_16(
3133                            target_value,
3134                            memarg,
3135                            target_addr,
3136                            need_check,
3137                            imported_memories,
3138                            offset,
3139                            heap_access_oob,
3140                            unaligned_atomic,
3141                        )
3142                    },
3143                )?;
3144            }
3145            Operator::I64Load { ref memarg } => {
3146                let target = self.pop_value_released()?.0;
3147                let ret = self.acquire_location(&WpType::I64)?;
3148                self.value_stack.push((ret, CanonicalizeType::None));
3149                self.op_memory(
3150                    |this,
3151                     need_check,
3152                     imported_memories,
3153                     offset,
3154                     heap_access_oob,
3155                     unaligned_atomic| {
3156                        this.machine.i64_load(
3157                            target,
3158                            memarg,
3159                            ret,
3160                            need_check,
3161                            imported_memories,
3162                            offset,
3163                            heap_access_oob,
3164                            unaligned_atomic,
3165                        )
3166                    },
3167                )?;
3168            }
3169            Operator::F64Load { ref memarg } => {
3170                let target = self.pop_value_released()?.0;
3171                let ret = self.acquire_location(&WpType::F64)?;
3172                self.value_stack.push((ret, CanonicalizeType::None));
3173                self.op_memory(
3174                    |this,
3175                     need_check,
3176                     imported_memories,
3177                     offset,
3178                     heap_access_oob,
3179                     unaligned_atomic| {
3180                        this.machine.f64_load(
3181                            target,
3182                            memarg,
3183                            ret,
3184                            need_check,
3185                            imported_memories,
3186                            offset,
3187                            heap_access_oob,
3188                            unaligned_atomic,
3189                        )
3190                    },
3191                )?;
3192            }
3193            Operator::I64Load8U { ref memarg } => {
3194                let target = self.pop_value_released()?.0;
3195                let ret = self.acquire_location(&WpType::I64)?;
3196                self.value_stack.push((ret, CanonicalizeType::None));
3197                self.op_memory(
3198                    |this,
3199                     need_check,
3200                     imported_memories,
3201                     offset,
3202                     heap_access_oob,
3203                     unaligned_atomic| {
3204                        this.machine.i64_load_8u(
3205                            target,
3206                            memarg,
3207                            ret,
3208                            need_check,
3209                            imported_memories,
3210                            offset,
3211                            heap_access_oob,
3212                            unaligned_atomic,
3213                        )
3214                    },
3215                )?;
3216            }
3217            Operator::I64Load8S { ref memarg } => {
3218                let target = self.pop_value_released()?.0;
3219                let ret = self.acquire_location(&WpType::I64)?;
3220                self.value_stack.push((ret, CanonicalizeType::None));
3221                self.op_memory(
3222                    |this,
3223                     need_check,
3224                     imported_memories,
3225                     offset,
3226                     heap_access_oob,
3227                     unaligned_atomic| {
3228                        this.machine.i64_load_8s(
3229                            target,
3230                            memarg,
3231                            ret,
3232                            need_check,
3233                            imported_memories,
3234                            offset,
3235                            heap_access_oob,
3236                            unaligned_atomic,
3237                        )
3238                    },
3239                )?;
3240            }
3241            Operator::I64Load16U { ref memarg } => {
3242                let target = self.pop_value_released()?.0;
3243                let ret = self.acquire_location(&WpType::I64)?;
3244                self.value_stack.push((ret, CanonicalizeType::None));
3245                self.op_memory(
3246                    |this,
3247                     need_check,
3248                     imported_memories,
3249                     offset,
3250                     heap_access_oob,
3251                     unaligned_atomic| {
3252                        this.machine.i64_load_16u(
3253                            target,
3254                            memarg,
3255                            ret,
3256                            need_check,
3257                            imported_memories,
3258                            offset,
3259                            heap_access_oob,
3260                            unaligned_atomic,
3261                        )
3262                    },
3263                )?;
3264            }
3265            Operator::I64Load16S { ref memarg } => {
3266                let target = self.pop_value_released()?.0;
3267                let ret = self.acquire_location(&WpType::I64)?;
3268                self.value_stack.push((ret, CanonicalizeType::None));
3269                self.op_memory(
3270                    |this,
3271                     need_check,
3272                     imported_memories,
3273                     offset,
3274                     heap_access_oob,
3275                     unaligned_atomic| {
3276                        this.machine.i64_load_16s(
3277                            target,
3278                            memarg,
3279                            ret,
3280                            need_check,
3281                            imported_memories,
3282                            offset,
3283                            heap_access_oob,
3284                            unaligned_atomic,
3285                        )
3286                    },
3287                )?;
3288            }
3289            Operator::I64Load32U { ref memarg } => {
3290                let target = self.pop_value_released()?.0;
3291                let ret = self.acquire_location(&WpType::I64)?;
3292                self.value_stack.push((ret, CanonicalizeType::None));
3293                self.op_memory(
3294                    |this,
3295                     need_check,
3296                     imported_memories,
3297                     offset,
3298                     heap_access_oob,
3299                     unaligned_atomic| {
3300                        this.machine.i64_load_32u(
3301                            target,
3302                            memarg,
3303                            ret,
3304                            need_check,
3305                            imported_memories,
3306                            offset,
3307                            heap_access_oob,
3308                            unaligned_atomic,
3309                        )
3310                    },
3311                )?;
3312            }
3313            Operator::I64Load32S { ref memarg } => {
3314                let target = self.pop_value_released()?.0;
3315                let ret = self.acquire_location(&WpType::I64)?;
3316                self.value_stack.push((ret, CanonicalizeType::None));
3317                self.op_memory(
3318                    |this,
3319                     need_check,
3320                     imported_memories,
3321                     offset,
3322                     heap_access_oob,
3323                     unaligned_atomic| {
3324                        this.machine.i64_load_32s(
3325                            target,
3326                            memarg,
3327                            ret,
3328                            need_check,
3329                            imported_memories,
3330                            offset,
3331                            heap_access_oob,
3332                            unaligned_atomic,
3333                        )
3334                    },
3335                )?;
3336            }
3337            Operator::I64Store { ref memarg } => {
3338                let target_value = self.pop_value_released()?.0;
3339                let target_addr = self.pop_value_released()?.0;
3340
3341                self.op_memory(
3342                    |this,
3343                     need_check,
3344                     imported_memories,
3345                     offset,
3346                     heap_access_oob,
3347                     unaligned_atomic| {
3348                        this.machine.i64_save(
3349                            target_value,
3350                            memarg,
3351                            target_addr,
3352                            need_check,
3353                            imported_memories,
3354                            offset,
3355                            heap_access_oob,
3356                            unaligned_atomic,
3357                        )
3358                    },
3359                )?;
3360            }
3361            Operator::F64Store { ref memarg } => {
3362                let (target_value, canonicalize) = self.pop_value_released()?;
3363                let target_addr = self.pop_value_released()?.0;
3364                self.op_memory(
3365                    |this,
3366                     need_check,
3367                     imported_memories,
3368                     offset,
3369                     heap_access_oob,
3370                     unaligned_atomic| {
3371                        this.machine.f64_save(
3372                            target_value,
3373                            memarg,
3374                            target_addr,
3375                            self.config.enable_nan_canonicalization
3376                                && !matches!(canonicalize, CanonicalizeType::None),
3377                            need_check,
3378                            imported_memories,
3379                            offset,
3380                            heap_access_oob,
3381                            unaligned_atomic,
3382                        )
3383                    },
3384                )?;
3385            }
3386            Operator::I64Store8 { ref memarg } => {
3387                let target_value = self.pop_value_released()?.0;
3388                let target_addr = self.pop_value_released()?.0;
3389                self.op_memory(
3390                    |this,
3391                     need_check,
3392                     imported_memories,
3393                     offset,
3394                     heap_access_oob,
3395                     unaligned_atomic| {
3396                        this.machine.i64_save_8(
3397                            target_value,
3398                            memarg,
3399                            target_addr,
3400                            need_check,
3401                            imported_memories,
3402                            offset,
3403                            heap_access_oob,
3404                            unaligned_atomic,
3405                        )
3406                    },
3407                )?;
3408            }
3409            Operator::I64Store16 { ref memarg } => {
3410                let target_value = self.pop_value_released()?.0;
3411                let target_addr = self.pop_value_released()?.0;
3412                self.op_memory(
3413                    |this,
3414                     need_check,
3415                     imported_memories,
3416                     offset,
3417                     heap_access_oob,
3418                     unaligned_atomic| {
3419                        this.machine.i64_save_16(
3420                            target_value,
3421                            memarg,
3422                            target_addr,
3423                            need_check,
3424                            imported_memories,
3425                            offset,
3426                            heap_access_oob,
3427                            unaligned_atomic,
3428                        )
3429                    },
3430                )?;
3431            }
3432            Operator::I64Store32 { ref memarg } => {
3433                let target_value = self.pop_value_released()?.0;
3434                let target_addr = self.pop_value_released()?.0;
3435                self.op_memory(
3436                    |this,
3437                     need_check,
3438                     imported_memories,
3439                     offset,
3440                     heap_access_oob,
3441                     unaligned_atomic| {
3442                        this.machine.i64_save_32(
3443                            target_value,
3444                            memarg,
3445                            target_addr,
3446                            need_check,
3447                            imported_memories,
3448                            offset,
3449                            heap_access_oob,
3450                            unaligned_atomic,
3451                        )
3452                    },
3453                )?;
3454            }
3455            Operator::Unreachable => {
3456                self.machine.move_location(
3457                    Size::S64,
3458                    Location::Memory(
3459                        self.machine.get_vmctx_reg(),
3460                        self.vmoffsets
3461                            .vmctx_builtin_function(VMBuiltinFunctionIndex::get_raise_trap_index())
3462                            as i32,
3463                    ),
3464                    Location::GPR(self.machine.get_gpr_for_call()),
3465                )?;
3466
3467                self.emit_call_native(
3468                    |this| {
3469                        this.machine
3470                            .emit_call_register(this.machine.get_gpr_for_call())
3471                    },
3472                    // [trap_code]
3473                    [(
3474                        Location::Imm32(TrapCode::UnreachableCodeReached as u32),
3475                        CanonicalizeType::None,
3476                    )]
3477                    .iter()
3478                    .cloned(),
3479                    [WpType::I32].iter().cloned(),
3480                    iter::empty(),
3481                    NativeCallType::Unreachable,
3482                )?;
3483                self.unreachable_depth = 1;
3484            }
3485            Operator::Return => {
3486                let frame = &self.control_stack[0];
3487                if !frame.return_types.is_empty() {
3488                    self.emit_return_values(
3489                        frame.value_stack_depth_after(),
3490                        frame.return_types.len(),
3491                    )?;
3492                }
3493                let frame = &self.control_stack[0];
3494                let frame_depth = frame.value_stack_depth_for_release();
3495                let label = frame.label;
3496                self.release_stack_locations_keep_stack_offset(frame_depth)?;
3497                self.machine.jmp_unconditional(label)?;
3498                self.unreachable_depth = 1;
3499            }
3500            Operator::Br { relative_depth } => {
3501                let frame =
3502                    &self.control_stack[self.control_stack.len() - 1 - (relative_depth as usize)];
3503                if matches!(frame.state, ControlState::Loop) {
3504                    // Store into the PHI params of the loop, not to the return values.
3505                    self.emit_loop_params_store(
3506                        frame.value_stack_depth_after(),
3507                        frame.param_types.len(),
3508                    )?;
3509                } else if !frame.return_types.is_empty() {
3510                    self.emit_return_values(
3511                        frame.value_stack_depth_after(),
3512                        frame.return_types.len(),
3513                    )?;
3514                }
3515                let stack_len = self.control_stack.len();
3516                let frame = &mut self.control_stack[stack_len - 1 - (relative_depth as usize)];
3517                let frame_depth = frame.value_stack_depth_for_release();
3518                let label = frame.label;
3519
3520                self.release_stack_locations_keep_stack_offset(frame_depth)?;
3521                self.machine.jmp_unconditional(label)?;
3522                self.unreachable_depth = 1;
3523            }
3524            Operator::BrIf { relative_depth } => {
3525                let after = self.machine.get_label();
3526                let cond = self.pop_value_released()?.0;
3527                self.machine.jmp_on_condition(
3528                    UnsignedCondition::Equal,
3529                    Size::S32,
3530                    cond,
3531                    Location::Imm32(0),
3532                    after,
3533                )?;
3534
3535                let frame =
3536                    &self.control_stack[self.control_stack.len() - 1 - (relative_depth as usize)];
3537                if matches!(frame.state, ControlState::Loop) {
3538                    // Store into the PHI params of the loop, not to the return values.
3539                    self.emit_loop_params_store(
3540                        frame.value_stack_depth_after(),
3541                        frame.param_types.len(),
3542                    )?;
3543                } else if !frame.return_types.is_empty() {
3544                    self.emit_return_values(
3545                        frame.value_stack_depth_after(),
3546                        frame.return_types.len(),
3547                    )?;
3548                }
3549                let stack_len = self.control_stack.len();
3550                let frame = &mut self.control_stack[stack_len - 1 - (relative_depth as usize)];
3551                let stack_depth = frame.value_stack_depth_for_release();
3552                let label = frame.label;
3553                self.release_stack_locations_keep_stack_offset(stack_depth)?;
3554                self.machine.jmp_unconditional(label)?;
3555
3556                self.machine.emit_label(after)?;
3557            }
3558            Operator::BrTable { ref targets } => {
3559                let default_target = targets.default();
3560                let targets = targets
3561                    .targets()
3562                    .collect::<Result<Vec<_>, _>>()
3563                    .map_err(|e| CompileError::Codegen(format!("BrTable read_table: {e:?}")))?;
3564                let cond = self.pop_value_released()?.0;
3565                let table_label = self.machine.get_label();
3566                let mut table: Vec<Label> = vec![];
3567                let default_br = self.machine.get_label();
3568                self.machine.jmp_on_condition(
3569                    UnsignedCondition::AboveEqual,
3570                    Size::S32,
3571                    cond,
3572                    Location::Imm32(targets.len() as u32),
3573                    default_br,
3574                )?;
3575
3576                self.machine.emit_jmp_to_jumptable(table_label, cond)?;
3577
3578                for target in targets.iter() {
3579                    let label = self.machine.get_label();
3580                    self.machine.emit_label(label)?;
3581                    table.push(label);
3582                    let frame =
3583                        &self.control_stack[self.control_stack.len() - 1 - (*target as usize)];
3584                    if matches!(frame.state, ControlState::Loop) {
3585                        // Store into the PHI params of the loop, not to the return values.
3586                        self.emit_loop_params_store(
3587                            frame.value_stack_depth_after(),
3588                            frame.param_types.len(),
3589                        )?;
3590                    } else if !frame.return_types.is_empty() {
3591                        self.emit_return_values(
3592                            frame.value_stack_depth_after(),
3593                            frame.return_types.len(),
3594                        )?;
3595                    }
3596                    let frame =
3597                        &self.control_stack[self.control_stack.len() - 1 - (*target as usize)];
3598                    let stack_depth = frame.value_stack_depth_for_release();
3599                    let label = frame.label;
3600                    self.release_stack_locations_keep_stack_offset(stack_depth)?;
3601                    self.machine.jmp_unconditional(label)?;
3602                }
3603                self.machine.emit_label(default_br)?;
3604
3605                {
3606                    let frame = &self.control_stack
3607                        [self.control_stack.len() - 1 - (default_target as usize)];
3608                    if matches!(frame.state, ControlState::Loop) {
3609                        // Store into the PHI params of the loop, not to the return values.
3610                        self.emit_loop_params_store(
3611                            frame.value_stack_depth_after(),
3612                            frame.param_types.len(),
3613                        )?;
3614                    } else if !frame.return_types.is_empty() {
3615                        self.emit_return_values(
3616                            frame.value_stack_depth_after(),
3617                            frame.return_types.len(),
3618                        )?;
3619                    }
3620                    let frame = &self.control_stack
3621                        [self.control_stack.len() - 1 - (default_target as usize)];
3622                    let stack_depth = frame.value_stack_depth_for_release();
3623                    let label = frame.label;
3624                    self.release_stack_locations_keep_stack_offset(stack_depth)?;
3625                    self.machine.jmp_unconditional(label)?;
3626                }
3627
3628                self.machine.emit_label(table_label)?;
3629                for x in table {
3630                    self.machine.jmp_unconditional(x)?;
3631                }
3632                self.unreachable_depth = 1;
3633            }
3634            Operator::Drop => {
3635                self.pop_value_released()?;
3636            }
3637            Operator::End => {
3638                let frame = self.control_stack.pop().unwrap();
3639
3640                if !was_unreachable && !frame.return_types.is_empty() {
3641                    self.emit_return_values(
3642                        frame.value_stack_depth_after(),
3643                        frame.return_types.len(),
3644                    )?;
3645                }
3646
3647                if self.control_stack.is_empty() {
3648                    self.machine.emit_label(frame.label)?;
3649                    self.finalize_locals(self.calling_convention)?;
3650                    self.machine.emit_function_epilog()?;
3651
3652                    // Make a copy of the return value in XMM0, as required by the SysV CC.
3653                    #[allow(clippy::collapsible_if, reason = "hard to read otherwise")]
3654                    if let Ok(&return_type) = self.signature.results().iter().exactly_one()
3655                        && (return_type == Type::F32 || return_type == Type::F64)
3656                    {
3657                        self.machine.emit_function_return_float()?;
3658                    }
3659                    self.machine.emit_ret()?;
3660                } else {
3661                    let released = &self.value_stack.clone()[frame.value_stack_depth_after()..];
3662                    self.release_locations(released)?;
3663                    self.value_stack.truncate(frame.value_stack_depth_after());
3664
3665                    if !matches!(frame.state, ControlState::Loop) {
3666                        self.machine.emit_label(frame.label)?;
3667                    }
3668
3669                    if let ControlState::If { label_else, .. } = frame.state {
3670                        self.machine.emit_label(label_else)?;
3671                    }
3672
3673                    // At this point the return values are properly sitting in the value_stack and are properly canonicalized.
3674                }
3675            }
3676            Operator::AtomicFence => {
3677                // Fence is a nop.
3678                //
3679                // Fence was added to preserve information about fences from
3680                // source languages. If in the future Wasm extends the memory
3681                // model, and if we hadn't recorded what fences used to be there,
3682                // it would lead to data races that weren't present in the
3683                // original source language.
3684                self.machine.emit_memory_fence()?;
3685            }
3686            Operator::I32AtomicLoad { ref memarg } => {
3687                let target = self.pop_value_released()?.0;
3688                let ret = self.acquire_location(&WpType::I32)?;
3689                self.value_stack.push((ret, CanonicalizeType::None));
3690                self.op_memory(
3691                    |this,
3692                     need_check,
3693                     imported_memories,
3694                     offset,
3695                     heap_access_oob,
3696                     unaligned_atomic| {
3697                        this.machine.i32_atomic_load(
3698                            target,
3699                            memarg,
3700                            ret,
3701                            need_check,
3702                            imported_memories,
3703                            offset,
3704                            heap_access_oob,
3705                            unaligned_atomic,
3706                        )
3707                    },
3708                )?;
3709            }
3710            Operator::I32AtomicLoad8U { ref memarg } => {
3711                let target = self.pop_value_released()?.0;
3712                let ret = self.acquire_location(&WpType::I32)?;
3713                self.value_stack.push((ret, CanonicalizeType::None));
3714                self.op_memory(
3715                    |this,
3716                     need_check,
3717                     imported_memories,
3718                     offset,
3719                     heap_access_oob,
3720                     unaligned_atomic| {
3721                        this.machine.i32_atomic_load_8u(
3722                            target,
3723                            memarg,
3724                            ret,
3725                            need_check,
3726                            imported_memories,
3727                            offset,
3728                            heap_access_oob,
3729                            unaligned_atomic,
3730                        )
3731                    },
3732                )?;
3733            }
3734            Operator::I32AtomicLoad16U { ref memarg } => {
3735                let target = self.pop_value_released()?.0;
3736                let ret = self.acquire_location(&WpType::I32)?;
3737                self.value_stack.push((ret, CanonicalizeType::None));
3738                self.op_memory(
3739                    |this,
3740                     need_check,
3741                     imported_memories,
3742                     offset,
3743                     heap_access_oob,
3744                     unaligned_atomic| {
3745                        this.machine.i32_atomic_load_16u(
3746                            target,
3747                            memarg,
3748                            ret,
3749                            need_check,
3750                            imported_memories,
3751                            offset,
3752                            heap_access_oob,
3753                            unaligned_atomic,
3754                        )
3755                    },
3756                )?;
3757            }
3758            Operator::I32AtomicStore { ref memarg } => {
3759                let target_value = self.pop_value_released()?.0;
3760                let target_addr = self.pop_value_released()?.0;
3761                self.op_memory(
3762                    |this,
3763                     need_check,
3764                     imported_memories,
3765                     offset,
3766                     heap_access_oob,
3767                     unaligned_atomic| {
3768                        this.machine.i32_atomic_save(
3769                            target_value,
3770                            memarg,
3771                            target_addr,
3772                            need_check,
3773                            imported_memories,
3774                            offset,
3775                            heap_access_oob,
3776                            unaligned_atomic,
3777                        )
3778                    },
3779                )?;
3780            }
3781            Operator::I32AtomicStore8 { ref memarg } => {
3782                let target_value = self.pop_value_released()?.0;
3783                let target_addr = self.pop_value_released()?.0;
3784                self.op_memory(
3785                    |this,
3786                     need_check,
3787                     imported_memories,
3788                     offset,
3789                     heap_access_oob,
3790                     unaligned_atomic| {
3791                        this.machine.i32_atomic_save_8(
3792                            target_value,
3793                            memarg,
3794                            target_addr,
3795                            need_check,
3796                            imported_memories,
3797                            offset,
3798                            heap_access_oob,
3799                            unaligned_atomic,
3800                        )
3801                    },
3802                )?;
3803            }
3804            Operator::I32AtomicStore16 { ref memarg } => {
3805                let target_value = self.pop_value_released()?.0;
3806                let target_addr = self.pop_value_released()?.0;
3807                self.op_memory(
3808                    |this,
3809                     need_check,
3810                     imported_memories,
3811                     offset,
3812                     heap_access_oob,
3813                     unaligned_atomic| {
3814                        this.machine.i32_atomic_save_16(
3815                            target_value,
3816                            memarg,
3817                            target_addr,
3818                            need_check,
3819                            imported_memories,
3820                            offset,
3821                            heap_access_oob,
3822                            unaligned_atomic,
3823                        )
3824                    },
3825                )?;
3826            }
3827            Operator::I64AtomicLoad { ref memarg } => {
3828                let target = self.pop_value_released()?.0;
3829                let ret = self.acquire_location(&WpType::I64)?;
3830                self.value_stack.push((ret, CanonicalizeType::None));
3831                self.op_memory(
3832                    |this,
3833                     need_check,
3834                     imported_memories,
3835                     offset,
3836                     heap_access_oob,
3837                     unaligned_atomic| {
3838                        this.machine.i64_atomic_load(
3839                            target,
3840                            memarg,
3841                            ret,
3842                            need_check,
3843                            imported_memories,
3844                            offset,
3845                            heap_access_oob,
3846                            unaligned_atomic,
3847                        )
3848                    },
3849                )?;
3850            }
3851            Operator::I64AtomicLoad8U { ref memarg } => {
3852                let target = self.pop_value_released()?.0;
3853                let ret = self.acquire_location(&WpType::I64)?;
3854                self.value_stack.push((ret, CanonicalizeType::None));
3855                self.op_memory(
3856                    |this,
3857                     need_check,
3858                     imported_memories,
3859                     offset,
3860                     heap_access_oob,
3861                     unaligned_atomic| {
3862                        this.machine.i64_atomic_load_8u(
3863                            target,
3864                            memarg,
3865                            ret,
3866                            need_check,
3867                            imported_memories,
3868                            offset,
3869                            heap_access_oob,
3870                            unaligned_atomic,
3871                        )
3872                    },
3873                )?;
3874            }
3875            Operator::I64AtomicLoad16U { ref memarg } => {
3876                let target = self.pop_value_released()?.0;
3877                let ret = self.acquire_location(&WpType::I64)?;
3878                self.value_stack.push((ret, CanonicalizeType::None));
3879                self.op_memory(
3880                    |this,
3881                     need_check,
3882                     imported_memories,
3883                     offset,
3884                     heap_access_oob,
3885                     unaligned_atomic| {
3886                        this.machine.i64_atomic_load_16u(
3887                            target,
3888                            memarg,
3889                            ret,
3890                            need_check,
3891                            imported_memories,
3892                            offset,
3893                            heap_access_oob,
3894                            unaligned_atomic,
3895                        )
3896                    },
3897                )?;
3898            }
3899            Operator::I64AtomicLoad32U { ref memarg } => {
3900                let target = self.pop_value_released()?.0;
3901                let ret = self.acquire_location(&WpType::I64)?;
3902                self.value_stack.push((ret, CanonicalizeType::None));
3903                self.op_memory(
3904                    |this,
3905                     need_check,
3906                     imported_memories,
3907                     offset,
3908                     heap_access_oob,
3909                     unaligned_atomic| {
3910                        this.machine.i64_atomic_load_32u(
3911                            target,
3912                            memarg,
3913                            ret,
3914                            need_check,
3915                            imported_memories,
3916                            offset,
3917                            heap_access_oob,
3918                            unaligned_atomic,
3919                        )
3920                    },
3921                )?;
3922            }
3923            Operator::I64AtomicStore { ref memarg } => {
3924                let target_value = self.pop_value_released()?.0;
3925                let target_addr = self.pop_value_released()?.0;
3926                self.op_memory(
3927                    |this,
3928                     need_check,
3929                     imported_memories,
3930                     offset,
3931                     heap_access_oob,
3932                     unaligned_atomic| {
3933                        this.machine.i64_atomic_save(
3934                            target_value,
3935                            memarg,
3936                            target_addr,
3937                            need_check,
3938                            imported_memories,
3939                            offset,
3940                            heap_access_oob,
3941                            unaligned_atomic,
3942                        )
3943                    },
3944                )?;
3945            }
3946            Operator::I64AtomicStore8 { ref memarg } => {
3947                let target_value = self.pop_value_released()?.0;
3948                let target_addr = self.pop_value_released()?.0;
3949                self.op_memory(
3950                    |this,
3951                     need_check,
3952                     imported_memories,
3953                     offset,
3954                     heap_access_oob,
3955                     unaligned_atomic| {
3956                        this.machine.i64_atomic_save_8(
3957                            target_value,
3958                            memarg,
3959                            target_addr,
3960                            need_check,
3961                            imported_memories,
3962                            offset,
3963                            heap_access_oob,
3964                            unaligned_atomic,
3965                        )
3966                    },
3967                )?;
3968            }
3969            Operator::I64AtomicStore16 { ref memarg } => {
3970                let target_value = self.pop_value_released()?.0;
3971                let target_addr = self.pop_value_released()?.0;
3972                self.op_memory(
3973                    |this,
3974                     need_check,
3975                     imported_memories,
3976                     offset,
3977                     heap_access_oob,
3978                     unaligned_atomic| {
3979                        this.machine.i64_atomic_save_16(
3980                            target_value,
3981                            memarg,
3982                            target_addr,
3983                            need_check,
3984                            imported_memories,
3985                            offset,
3986                            heap_access_oob,
3987                            unaligned_atomic,
3988                        )
3989                    },
3990                )?;
3991            }
3992            Operator::I64AtomicStore32 { ref memarg } => {
3993                let target_value = self.pop_value_released()?.0;
3994                let target_addr = self.pop_value_released()?.0;
3995                self.op_memory(
3996                    |this,
3997                     need_check,
3998                     imported_memories,
3999                     offset,
4000                     heap_access_oob,
4001                     unaligned_atomic| {
4002                        this.machine.i64_atomic_save_32(
4003                            target_value,
4004                            memarg,
4005                            target_addr,
4006                            need_check,
4007                            imported_memories,
4008                            offset,
4009                            heap_access_oob,
4010                            unaligned_atomic,
4011                        )
4012                    },
4013                )?;
4014            }
4015            Operator::I32AtomicRmwAdd { ref memarg } => {
4016                let loc = self.pop_value_released()?.0;
4017                let target = self.pop_value_released()?.0;
4018                let ret = self.acquire_location(&WpType::I32)?;
4019                self.value_stack.push((ret, CanonicalizeType::None));
4020                self.op_memory(
4021                    |this,
4022                     need_check,
4023                     imported_memories,
4024                     offset,
4025                     heap_access_oob,
4026                     unaligned_atomic| {
4027                        this.machine.i32_atomic_add(
4028                            loc,
4029                            target,
4030                            memarg,
4031                            ret,
4032                            need_check,
4033                            imported_memories,
4034                            offset,
4035                            heap_access_oob,
4036                            unaligned_atomic,
4037                        )
4038                    },
4039                )?;
4040            }
4041            Operator::I64AtomicRmwAdd { ref memarg } => {
4042                let loc = self.pop_value_released()?.0;
4043                let target = self.pop_value_released()?.0;
4044                let ret = self.acquire_location(&WpType::I64)?;
4045                self.value_stack.push((ret, CanonicalizeType::None));
4046                self.op_memory(
4047                    |this,
4048                     need_check,
4049                     imported_memories,
4050                     offset,
4051                     heap_access_oob,
4052                     unaligned_atomic| {
4053                        this.machine.i64_atomic_add(
4054                            loc,
4055                            target,
4056                            memarg,
4057                            ret,
4058                            need_check,
4059                            imported_memories,
4060                            offset,
4061                            heap_access_oob,
4062                            unaligned_atomic,
4063                        )
4064                    },
4065                )?;
4066            }
4067            Operator::I32AtomicRmw8AddU { ref memarg } => {
4068                let loc = self.pop_value_released()?.0;
4069                let target = self.pop_value_released()?.0;
4070                let ret = self.acquire_location(&WpType::I32)?;
4071                self.value_stack.push((ret, CanonicalizeType::None));
4072                self.op_memory(
4073                    |this,
4074                     need_check,
4075                     imported_memories,
4076                     offset,
4077                     heap_access_oob,
4078                     unaligned_atomic| {
4079                        this.machine.i32_atomic_add_8u(
4080                            loc,
4081                            target,
4082                            memarg,
4083                            ret,
4084                            need_check,
4085                            imported_memories,
4086                            offset,
4087                            heap_access_oob,
4088                            unaligned_atomic,
4089                        )
4090                    },
4091                )?;
4092            }
4093            Operator::I32AtomicRmw16AddU { ref memarg } => {
4094                let loc = self.pop_value_released()?.0;
4095                let target = self.pop_value_released()?.0;
4096                let ret = self.acquire_location(&WpType::I32)?;
4097                self.value_stack.push((ret, CanonicalizeType::None));
4098                self.op_memory(
4099                    |this,
4100                     need_check,
4101                     imported_memories,
4102                     offset,
4103                     heap_access_oob,
4104                     unaligned_atomic| {
4105                        this.machine.i32_atomic_add_16u(
4106                            loc,
4107                            target,
4108                            memarg,
4109                            ret,
4110                            need_check,
4111                            imported_memories,
4112                            offset,
4113                            heap_access_oob,
4114                            unaligned_atomic,
4115                        )
4116                    },
4117                )?;
4118            }
4119            Operator::I64AtomicRmw8AddU { ref memarg } => {
4120                let loc = self.pop_value_released()?.0;
4121                let target = self.pop_value_released()?.0;
4122                let ret = self.acquire_location(&WpType::I64)?;
4123                self.value_stack.push((ret, CanonicalizeType::None));
4124                self.op_memory(
4125                    |this,
4126                     need_check,
4127                     imported_memories,
4128                     offset,
4129                     heap_access_oob,
4130                     unaligned_atomic| {
4131                        this.machine.i64_atomic_add_8u(
4132                            loc,
4133                            target,
4134                            memarg,
4135                            ret,
4136                            need_check,
4137                            imported_memories,
4138                            offset,
4139                            heap_access_oob,
4140                            unaligned_atomic,
4141                        )
4142                    },
4143                )?;
4144            }
4145            Operator::I64AtomicRmw16AddU { ref memarg } => {
4146                let loc = self.pop_value_released()?.0;
4147                let target = self.pop_value_released()?.0;
4148                let ret = self.acquire_location(&WpType::I64)?;
4149                self.value_stack.push((ret, CanonicalizeType::None));
4150                self.op_memory(
4151                    |this,
4152                     need_check,
4153                     imported_memories,
4154                     offset,
4155                     heap_access_oob,
4156                     unaligned_atomic| {
4157                        this.machine.i64_atomic_add_16u(
4158                            loc,
4159                            target,
4160                            memarg,
4161                            ret,
4162                            need_check,
4163                            imported_memories,
4164                            offset,
4165                            heap_access_oob,
4166                            unaligned_atomic,
4167                        )
4168                    },
4169                )?;
4170            }
4171            Operator::I64AtomicRmw32AddU { ref memarg } => {
4172                let loc = self.pop_value_released()?.0;
4173                let target = self.pop_value_released()?.0;
4174                let ret = self.acquire_location(&WpType::I64)?;
4175                self.value_stack.push((ret, CanonicalizeType::None));
4176                self.op_memory(
4177                    |this,
4178                     need_check,
4179                     imported_memories,
4180                     offset,
4181                     heap_access_oob,
4182                     unaligned_atomic| {
4183                        this.machine.i64_atomic_add_32u(
4184                            loc,
4185                            target,
4186                            memarg,
4187                            ret,
4188                            need_check,
4189                            imported_memories,
4190                            offset,
4191                            heap_access_oob,
4192                            unaligned_atomic,
4193                        )
4194                    },
4195                )?;
4196            }
4197            Operator::I32AtomicRmwSub { ref memarg } => {
4198                let loc = self.pop_value_released()?.0;
4199                let target = self.pop_value_released()?.0;
4200                let ret = self.acquire_location(&WpType::I32)?;
4201                self.value_stack.push((ret, CanonicalizeType::None));
4202                self.op_memory(
4203                    |this,
4204                     need_check,
4205                     imported_memories,
4206                     offset,
4207                     heap_access_oob,
4208                     unaligned_atomic| {
4209                        this.machine.i32_atomic_sub(
4210                            loc,
4211                            target,
4212                            memarg,
4213                            ret,
4214                            need_check,
4215                            imported_memories,
4216                            offset,
4217                            heap_access_oob,
4218                            unaligned_atomic,
4219                        )
4220                    },
4221                )?;
4222            }
4223            Operator::I64AtomicRmwSub { ref memarg } => {
4224                let loc = self.pop_value_released()?.0;
4225                let target = self.pop_value_released()?.0;
4226                let ret = self.acquire_location(&WpType::I64)?;
4227                self.value_stack.push((ret, CanonicalizeType::None));
4228                self.op_memory(
4229                    |this,
4230                     need_check,
4231                     imported_memories,
4232                     offset,
4233                     heap_access_oob,
4234                     unaligned_atomic| {
4235                        this.machine.i64_atomic_sub(
4236                            loc,
4237                            target,
4238                            memarg,
4239                            ret,
4240                            need_check,
4241                            imported_memories,
4242                            offset,
4243                            heap_access_oob,
4244                            unaligned_atomic,
4245                        )
4246                    },
4247                )?;
4248            }
4249            Operator::I32AtomicRmw8SubU { ref memarg } => {
4250                let loc = self.pop_value_released()?.0;
4251                let target = self.pop_value_released()?.0;
4252                let ret = self.acquire_location(&WpType::I32)?;
4253                self.value_stack.push((ret, CanonicalizeType::None));
4254                self.op_memory(
4255                    |this,
4256                     need_check,
4257                     imported_memories,
4258                     offset,
4259                     heap_access_oob,
4260                     unaligned_atomic| {
4261                        this.machine.i32_atomic_sub_8u(
4262                            loc,
4263                            target,
4264                            memarg,
4265                            ret,
4266                            need_check,
4267                            imported_memories,
4268                            offset,
4269                            heap_access_oob,
4270                            unaligned_atomic,
4271                        )
4272                    },
4273                )?;
4274            }
4275            Operator::I32AtomicRmw16SubU { ref memarg } => {
4276                let loc = self.pop_value_released()?.0;
4277                let target = self.pop_value_released()?.0;
4278                let ret = self.acquire_location(&WpType::I32)?;
4279                self.value_stack.push((ret, CanonicalizeType::None));
4280                self.op_memory(
4281                    |this,
4282                     need_check,
4283                     imported_memories,
4284                     offset,
4285                     heap_access_oob,
4286                     unaligned_atomic| {
4287                        this.machine.i32_atomic_sub_16u(
4288                            loc,
4289                            target,
4290                            memarg,
4291                            ret,
4292                            need_check,
4293                            imported_memories,
4294                            offset,
4295                            heap_access_oob,
4296                            unaligned_atomic,
4297                        )
4298                    },
4299                )?;
4300            }
4301            Operator::I64AtomicRmw8SubU { ref memarg } => {
4302                let loc = self.pop_value_released()?.0;
4303                let target = self.pop_value_released()?.0;
4304                let ret = self.acquire_location(&WpType::I64)?;
4305                self.value_stack.push((ret, CanonicalizeType::None));
4306                self.op_memory(
4307                    |this,
4308                     need_check,
4309                     imported_memories,
4310                     offset,
4311                     heap_access_oob,
4312                     unaligned_atomic| {
4313                        this.machine.i64_atomic_sub_8u(
4314                            loc,
4315                            target,
4316                            memarg,
4317                            ret,
4318                            need_check,
4319                            imported_memories,
4320                            offset,
4321                            heap_access_oob,
4322                            unaligned_atomic,
4323                        )
4324                    },
4325                )?;
4326            }
4327            Operator::I64AtomicRmw16SubU { ref memarg } => {
4328                let loc = self.pop_value_released()?.0;
4329                let target = self.pop_value_released()?.0;
4330                let ret = self.acquire_location(&WpType::I64)?;
4331                self.value_stack.push((ret, CanonicalizeType::None));
4332                self.op_memory(
4333                    |this,
4334                     need_check,
4335                     imported_memories,
4336                     offset,
4337                     heap_access_oob,
4338                     unaligned_atomic| {
4339                        this.machine.i64_atomic_sub_16u(
4340                            loc,
4341                            target,
4342                            memarg,
4343                            ret,
4344                            need_check,
4345                            imported_memories,
4346                            offset,
4347                            heap_access_oob,
4348                            unaligned_atomic,
4349                        )
4350                    },
4351                )?;
4352            }
4353            Operator::I64AtomicRmw32SubU { ref memarg } => {
4354                let loc = self.pop_value_released()?.0;
4355                let target = self.pop_value_released()?.0;
4356                let ret = self.acquire_location(&WpType::I64)?;
4357                self.value_stack.push((ret, CanonicalizeType::None));
4358                self.op_memory(
4359                    |this,
4360                     need_check,
4361                     imported_memories,
4362                     offset,
4363                     heap_access_oob,
4364                     unaligned_atomic| {
4365                        this.machine.i64_atomic_sub_32u(
4366                            loc,
4367                            target,
4368                            memarg,
4369                            ret,
4370                            need_check,
4371                            imported_memories,
4372                            offset,
4373                            heap_access_oob,
4374                            unaligned_atomic,
4375                        )
4376                    },
4377                )?;
4378            }
4379            Operator::I32AtomicRmwAnd { ref memarg } => {
4380                let loc = self.pop_value_released()?.0;
4381                let target = self.pop_value_released()?.0;
4382                let ret = self.acquire_location(&WpType::I32)?;
4383                self.value_stack.push((ret, CanonicalizeType::None));
4384                self.op_memory(
4385                    |this,
4386                     need_check,
4387                     imported_memories,
4388                     offset,
4389                     heap_access_oob,
4390                     unaligned_atomic| {
4391                        this.machine.i32_atomic_and(
4392                            loc,
4393                            target,
4394                            memarg,
4395                            ret,
4396                            need_check,
4397                            imported_memories,
4398                            offset,
4399                            heap_access_oob,
4400                            unaligned_atomic,
4401                        )
4402                    },
4403                )?;
4404            }
4405            Operator::I64AtomicRmwAnd { ref memarg } => {
4406                let loc = self.pop_value_released()?.0;
4407                let target = self.pop_value_released()?.0;
4408                let ret = self.acquire_location(&WpType::I64)?;
4409                self.value_stack.push((ret, CanonicalizeType::None));
4410                self.op_memory(
4411                    |this,
4412                     need_check,
4413                     imported_memories,
4414                     offset,
4415                     heap_access_oob,
4416                     unaligned_atomic| {
4417                        this.machine.i64_atomic_and(
4418                            loc,
4419                            target,
4420                            memarg,
4421                            ret,
4422                            need_check,
4423                            imported_memories,
4424                            offset,
4425                            heap_access_oob,
4426                            unaligned_atomic,
4427                        )
4428                    },
4429                )?;
4430            }
4431            Operator::I32AtomicRmw8AndU { ref memarg } => {
4432                let loc = self.pop_value_released()?.0;
4433                let target = self.pop_value_released()?.0;
4434                let ret = self.acquire_location(&WpType::I32)?;
4435                self.value_stack.push((ret, CanonicalizeType::None));
4436                self.op_memory(
4437                    |this,
4438                     need_check,
4439                     imported_memories,
4440                     offset,
4441                     heap_access_oob,
4442                     unaligned_atomic| {
4443                        this.machine.i32_atomic_and_8u(
4444                            loc,
4445                            target,
4446                            memarg,
4447                            ret,
4448                            need_check,
4449                            imported_memories,
4450                            offset,
4451                            heap_access_oob,
4452                            unaligned_atomic,
4453                        )
4454                    },
4455                )?;
4456            }
4457            Operator::I32AtomicRmw16AndU { ref memarg } => {
4458                let loc = self.pop_value_released()?.0;
4459                let target = self.pop_value_released()?.0;
4460                let ret = self.acquire_location(&WpType::I32)?;
4461                self.value_stack.push((ret, CanonicalizeType::None));
4462                self.op_memory(
4463                    |this,
4464                     need_check,
4465                     imported_memories,
4466                     offset,
4467                     heap_access_oob,
4468                     unaligned_atomic| {
4469                        this.machine.i32_atomic_and_16u(
4470                            loc,
4471                            target,
4472                            memarg,
4473                            ret,
4474                            need_check,
4475                            imported_memories,
4476                            offset,
4477                            heap_access_oob,
4478                            unaligned_atomic,
4479                        )
4480                    },
4481                )?;
4482            }
4483            Operator::I64AtomicRmw8AndU { ref memarg } => {
4484                let loc = self.pop_value_released()?.0;
4485                let target = self.pop_value_released()?.0;
4486                let ret = self.acquire_location(&WpType::I64)?;
4487                self.value_stack.push((ret, CanonicalizeType::None));
4488                self.op_memory(
4489                    |this,
4490                     need_check,
4491                     imported_memories,
4492                     offset,
4493                     heap_access_oob,
4494                     unaligned_atomic| {
4495                        this.machine.i64_atomic_and_8u(
4496                            loc,
4497                            target,
4498                            memarg,
4499                            ret,
4500                            need_check,
4501                            imported_memories,
4502                            offset,
4503                            heap_access_oob,
4504                            unaligned_atomic,
4505                        )
4506                    },
4507                )?;
4508            }
4509            Operator::I64AtomicRmw16AndU { ref memarg } => {
4510                let loc = self.pop_value_released()?.0;
4511                let target = self.pop_value_released()?.0;
4512                let ret = self.acquire_location(&WpType::I64)?;
4513                self.value_stack.push((ret, CanonicalizeType::None));
4514                self.op_memory(
4515                    |this,
4516                     need_check,
4517                     imported_memories,
4518                     offset,
4519                     heap_access_oob,
4520                     unaligned_atomic| {
4521                        this.machine.i64_atomic_and_16u(
4522                            loc,
4523                            target,
4524                            memarg,
4525                            ret,
4526                            need_check,
4527                            imported_memories,
4528                            offset,
4529                            heap_access_oob,
4530                            unaligned_atomic,
4531                        )
4532                    },
4533                )?;
4534            }
4535            Operator::I64AtomicRmw32AndU { ref memarg } => {
4536                let loc = self.pop_value_released()?.0;
4537                let target = self.pop_value_released()?.0;
4538                let ret = self.acquire_location(&WpType::I64)?;
4539                self.value_stack.push((ret, CanonicalizeType::None));
4540                self.op_memory(
4541                    |this,
4542                     need_check,
4543                     imported_memories,
4544                     offset,
4545                     heap_access_oob,
4546                     unaligned_atomic| {
4547                        this.machine.i64_atomic_and_32u(
4548                            loc,
4549                            target,
4550                            memarg,
4551                            ret,
4552                            need_check,
4553                            imported_memories,
4554                            offset,
4555                            heap_access_oob,
4556                            unaligned_atomic,
4557                        )
4558                    },
4559                )?;
4560            }
4561            Operator::I32AtomicRmwOr { ref memarg } => {
4562                let loc = self.pop_value_released()?.0;
4563                let target = self.pop_value_released()?.0;
4564                let ret = self.acquire_location(&WpType::I32)?;
4565                self.value_stack.push((ret, CanonicalizeType::None));
4566                self.op_memory(
4567                    |this,
4568                     need_check,
4569                     imported_memories,
4570                     offset,
4571                     heap_access_oob,
4572                     unaligned_atomic| {
4573                        this.machine.i32_atomic_or(
4574                            loc,
4575                            target,
4576                            memarg,
4577                            ret,
4578                            need_check,
4579                            imported_memories,
4580                            offset,
4581                            heap_access_oob,
4582                            unaligned_atomic,
4583                        )
4584                    },
4585                )?;
4586            }
4587            Operator::I64AtomicRmwOr { ref memarg } => {
4588                let loc = self.pop_value_released()?.0;
4589                let target = self.pop_value_released()?.0;
4590                let ret = self.acquire_location(&WpType::I64)?;
4591                self.value_stack.push((ret, CanonicalizeType::None));
4592                self.op_memory(
4593                    |this,
4594                     need_check,
4595                     imported_memories,
4596                     offset,
4597                     heap_access_oob,
4598                     unaligned_atomic| {
4599                        this.machine.i64_atomic_or(
4600                            loc,
4601                            target,
4602                            memarg,
4603                            ret,
4604                            need_check,
4605                            imported_memories,
4606                            offset,
4607                            heap_access_oob,
4608                            unaligned_atomic,
4609                        )
4610                    },
4611                )?;
4612            }
4613            Operator::I32AtomicRmw8OrU { ref memarg } => {
4614                let loc = self.pop_value_released()?.0;
4615                let target = self.pop_value_released()?.0;
4616                let ret = self.acquire_location(&WpType::I32)?;
4617                self.value_stack.push((ret, CanonicalizeType::None));
4618                self.op_memory(
4619                    |this,
4620                     need_check,
4621                     imported_memories,
4622                     offset,
4623                     heap_access_oob,
4624                     unaligned_atomic| {
4625                        this.machine.i32_atomic_or_8u(
4626                            loc,
4627                            target,
4628                            memarg,
4629                            ret,
4630                            need_check,
4631                            imported_memories,
4632                            offset,
4633                            heap_access_oob,
4634                            unaligned_atomic,
4635                        )
4636                    },
4637                )?;
4638            }
4639            Operator::I32AtomicRmw16OrU { ref memarg } => {
4640                let loc = self.pop_value_released()?.0;
4641                let target = self.pop_value_released()?.0;
4642                let ret = self.acquire_location(&WpType::I32)?;
4643                self.value_stack.push((ret, CanonicalizeType::None));
4644                self.op_memory(
4645                    |this,
4646                     need_check,
4647                     imported_memories,
4648                     offset,
4649                     heap_access_oob,
4650                     unaligned_atomic| {
4651                        this.machine.i32_atomic_or_16u(
4652                            loc,
4653                            target,
4654                            memarg,
4655                            ret,
4656                            need_check,
4657                            imported_memories,
4658                            offset,
4659                            heap_access_oob,
4660                            unaligned_atomic,
4661                        )
4662                    },
4663                )?;
4664            }
4665            Operator::I64AtomicRmw8OrU { ref memarg } => {
4666                let loc = self.pop_value_released()?.0;
4667                let target = self.pop_value_released()?.0;
4668                let ret = self.acquire_location(&WpType::I64)?;
4669                self.value_stack.push((ret, CanonicalizeType::None));
4670                self.op_memory(
4671                    |this,
4672                     need_check,
4673                     imported_memories,
4674                     offset,
4675                     heap_access_oob,
4676                     unaligned_atomic| {
4677                        this.machine.i64_atomic_or_8u(
4678                            loc,
4679                            target,
4680                            memarg,
4681                            ret,
4682                            need_check,
4683                            imported_memories,
4684                            offset,
4685                            heap_access_oob,
4686                            unaligned_atomic,
4687                        )
4688                    },
4689                )?;
4690            }
4691            Operator::I64AtomicRmw16OrU { ref memarg } => {
4692                let loc = self.pop_value_released()?.0;
4693                let target = self.pop_value_released()?.0;
4694                let ret = self.acquire_location(&WpType::I64)?;
4695                self.value_stack.push((ret, CanonicalizeType::None));
4696                self.op_memory(
4697                    |this,
4698                     need_check,
4699                     imported_memories,
4700                     offset,
4701                     heap_access_oob,
4702                     unaligned_atomic| {
4703                        this.machine.i64_atomic_or_16u(
4704                            loc,
4705                            target,
4706                            memarg,
4707                            ret,
4708                            need_check,
4709                            imported_memories,
4710                            offset,
4711                            heap_access_oob,
4712                            unaligned_atomic,
4713                        )
4714                    },
4715                )?;
4716            }
4717            Operator::I64AtomicRmw32OrU { ref memarg } => {
4718                let loc = self.pop_value_released()?.0;
4719                let target = self.pop_value_released()?.0;
4720                let ret = self.acquire_location(&WpType::I64)?;
4721                self.value_stack.push((ret, CanonicalizeType::None));
4722                self.op_memory(
4723                    |this,
4724                     need_check,
4725                     imported_memories,
4726                     offset,
4727                     heap_access_oob,
4728                     unaligned_atomic| {
4729                        this.machine.i64_atomic_or_32u(
4730                            loc,
4731                            target,
4732                            memarg,
4733                            ret,
4734                            need_check,
4735                            imported_memories,
4736                            offset,
4737                            heap_access_oob,
4738                            unaligned_atomic,
4739                        )
4740                    },
4741                )?;
4742            }
4743            Operator::I32AtomicRmwXor { ref memarg } => {
4744                let loc = self.pop_value_released()?.0;
4745                let target = self.pop_value_released()?.0;
4746                let ret = self.acquire_location(&WpType::I32)?;
4747                self.value_stack.push((ret, CanonicalizeType::None));
4748                self.op_memory(
4749                    |this,
4750                     need_check,
4751                     imported_memories,
4752                     offset,
4753                     heap_access_oob,
4754                     unaligned_atomic| {
4755                        this.machine.i32_atomic_xor(
4756                            loc,
4757                            target,
4758                            memarg,
4759                            ret,
4760                            need_check,
4761                            imported_memories,
4762                            offset,
4763                            heap_access_oob,
4764                            unaligned_atomic,
4765                        )
4766                    },
4767                )?;
4768            }
4769            Operator::I64AtomicRmwXor { ref memarg } => {
4770                let loc = self.pop_value_released()?.0;
4771                let target = self.pop_value_released()?.0;
4772                let ret = self.acquire_location(&WpType::I64)?;
4773                self.value_stack.push((ret, CanonicalizeType::None));
4774                self.op_memory(
4775                    |this,
4776                     need_check,
4777                     imported_memories,
4778                     offset,
4779                     heap_access_oob,
4780                     unaligned_atomic| {
4781                        this.machine.i64_atomic_xor(
4782                            loc,
4783                            target,
4784                            memarg,
4785                            ret,
4786                            need_check,
4787                            imported_memories,
4788                            offset,
4789                            heap_access_oob,
4790                            unaligned_atomic,
4791                        )
4792                    },
4793                )?;
4794            }
4795            Operator::I32AtomicRmw8XorU { ref memarg } => {
4796                let loc = self.pop_value_released()?.0;
4797                let target = self.pop_value_released()?.0;
4798                let ret = self.acquire_location(&WpType::I32)?;
4799                self.value_stack.push((ret, CanonicalizeType::None));
4800                self.op_memory(
4801                    |this,
4802                     need_check,
4803                     imported_memories,
4804                     offset,
4805                     heap_access_oob,
4806                     unaligned_atomic| {
4807                        this.machine.i32_atomic_xor_8u(
4808                            loc,
4809                            target,
4810                            memarg,
4811                            ret,
4812                            need_check,
4813                            imported_memories,
4814                            offset,
4815                            heap_access_oob,
4816                            unaligned_atomic,
4817                        )
4818                    },
4819                )?;
4820            }
4821            Operator::I32AtomicRmw16XorU { ref memarg } => {
4822                let loc = self.pop_value_released()?.0;
4823                let target = self.pop_value_released()?.0;
4824                let ret = self.acquire_location(&WpType::I32)?;
4825                self.value_stack.push((ret, CanonicalizeType::None));
4826                self.op_memory(
4827                    |this,
4828                     need_check,
4829                     imported_memories,
4830                     offset,
4831                     heap_access_oob,
4832                     unaligned_atomic| {
4833                        this.machine.i32_atomic_xor_16u(
4834                            loc,
4835                            target,
4836                            memarg,
4837                            ret,
4838                            need_check,
4839                            imported_memories,
4840                            offset,
4841                            heap_access_oob,
4842                            unaligned_atomic,
4843                        )
4844                    },
4845                )?;
4846            }
4847            Operator::I64AtomicRmw8XorU { ref memarg } => {
4848                let loc = self.pop_value_released()?.0;
4849                let target = self.pop_value_released()?.0;
4850                let ret = self.acquire_location(&WpType::I64)?;
4851                self.value_stack.push((ret, CanonicalizeType::None));
4852                self.op_memory(
4853                    |this,
4854                     need_check,
4855                     imported_memories,
4856                     offset,
4857                     heap_access_oob,
4858                     unaligned_atomic| {
4859                        this.machine.i64_atomic_xor_8u(
4860                            loc,
4861                            target,
4862                            memarg,
4863                            ret,
4864                            need_check,
4865                            imported_memories,
4866                            offset,
4867                            heap_access_oob,
4868                            unaligned_atomic,
4869                        )
4870                    },
4871                )?;
4872            }
4873            Operator::I64AtomicRmw16XorU { ref memarg } => {
4874                let loc = self.pop_value_released()?.0;
4875                let target = self.pop_value_released()?.0;
4876                let ret = self.acquire_location(&WpType::I64)?;
4877                self.value_stack.push((ret, CanonicalizeType::None));
4878                self.op_memory(
4879                    |this,
4880                     need_check,
4881                     imported_memories,
4882                     offset,
4883                     heap_access_oob,
4884                     unaligned_atomic| {
4885                        this.machine.i64_atomic_xor_16u(
4886                            loc,
4887                            target,
4888                            memarg,
4889                            ret,
4890                            need_check,
4891                            imported_memories,
4892                            offset,
4893                            heap_access_oob,
4894                            unaligned_atomic,
4895                        )
4896                    },
4897                )?;
4898            }
4899            Operator::I64AtomicRmw32XorU { ref memarg } => {
4900                let loc = self.pop_value_released()?.0;
4901                let target = self.pop_value_released()?.0;
4902                let ret = self.acquire_location(&WpType::I64)?;
4903                self.value_stack.push((ret, CanonicalizeType::None));
4904                self.op_memory(
4905                    |this,
4906                     need_check,
4907                     imported_memories,
4908                     offset,
4909                     heap_access_oob,
4910                     unaligned_atomic| {
4911                        this.machine.i64_atomic_xor_32u(
4912                            loc,
4913                            target,
4914                            memarg,
4915                            ret,
4916                            need_check,
4917                            imported_memories,
4918                            offset,
4919                            heap_access_oob,
4920                            unaligned_atomic,
4921                        )
4922                    },
4923                )?;
4924            }
4925            Operator::I32AtomicRmwXchg { ref memarg } => {
4926                let loc = self.pop_value_released()?.0;
4927                let target = self.pop_value_released()?.0;
4928                let ret = self.acquire_location(&WpType::I32)?;
4929                self.value_stack.push((ret, CanonicalizeType::None));
4930                self.op_memory(
4931                    |this,
4932                     need_check,
4933                     imported_memories,
4934                     offset,
4935                     heap_access_oob,
4936                     unaligned_atomic| {
4937                        this.machine.i32_atomic_xchg(
4938                            loc,
4939                            target,
4940                            memarg,
4941                            ret,
4942                            need_check,
4943                            imported_memories,
4944                            offset,
4945                            heap_access_oob,
4946                            unaligned_atomic,
4947                        )
4948                    },
4949                )?;
4950            }
4951            Operator::I64AtomicRmwXchg { ref memarg } => {
4952                let loc = self.pop_value_released()?.0;
4953                let target = self.pop_value_released()?.0;
4954                let ret = self.acquire_location(&WpType::I64)?;
4955                self.value_stack.push((ret, CanonicalizeType::None));
4956                self.op_memory(
4957                    |this,
4958                     need_check,
4959                     imported_memories,
4960                     offset,
4961                     heap_access_oob,
4962                     unaligned_atomic| {
4963                        this.machine.i64_atomic_xchg(
4964                            loc,
4965                            target,
4966                            memarg,
4967                            ret,
4968                            need_check,
4969                            imported_memories,
4970                            offset,
4971                            heap_access_oob,
4972                            unaligned_atomic,
4973                        )
4974                    },
4975                )?;
4976            }
4977            Operator::I32AtomicRmw8XchgU { ref memarg } => {
4978                let loc = self.pop_value_released()?.0;
4979                let target = self.pop_value_released()?.0;
4980                let ret = self.acquire_location(&WpType::I32)?;
4981                self.value_stack.push((ret, CanonicalizeType::None));
4982                self.op_memory(
4983                    |this,
4984                     need_check,
4985                     imported_memories,
4986                     offset,
4987                     heap_access_oob,
4988                     unaligned_atomic| {
4989                        this.machine.i32_atomic_xchg_8u(
4990                            loc,
4991                            target,
4992                            memarg,
4993                            ret,
4994                            need_check,
4995                            imported_memories,
4996                            offset,
4997                            heap_access_oob,
4998                            unaligned_atomic,
4999                        )
5000                    },
5001                )?;
5002            }
5003            Operator::I32AtomicRmw16XchgU { ref memarg } => {
5004                let loc = self.pop_value_released()?.0;
5005                let target = self.pop_value_released()?.0;
5006                let ret = self.acquire_location(&WpType::I32)?;
5007                self.value_stack.push((ret, CanonicalizeType::None));
5008                self.op_memory(
5009                    |this,
5010                     need_check,
5011                     imported_memories,
5012                     offset,
5013                     heap_access_oob,
5014                     unaligned_atomic| {
5015                        this.machine.i32_atomic_xchg_16u(
5016                            loc,
5017                            target,
5018                            memarg,
5019                            ret,
5020                            need_check,
5021                            imported_memories,
5022                            offset,
5023                            heap_access_oob,
5024                            unaligned_atomic,
5025                        )
5026                    },
5027                )?;
5028            }
5029            Operator::I64AtomicRmw8XchgU { ref memarg } => {
5030                let loc = self.pop_value_released()?.0;
5031                let target = self.pop_value_released()?.0;
5032                let ret = self.acquire_location(&WpType::I64)?;
5033                self.value_stack.push((ret, CanonicalizeType::None));
5034                self.op_memory(
5035                    |this,
5036                     need_check,
5037                     imported_memories,
5038                     offset,
5039                     heap_access_oob,
5040                     unaligned_atomic| {
5041                        this.machine.i64_atomic_xchg_8u(
5042                            loc,
5043                            target,
5044                            memarg,
5045                            ret,
5046                            need_check,
5047                            imported_memories,
5048                            offset,
5049                            heap_access_oob,
5050                            unaligned_atomic,
5051                        )
5052                    },
5053                )?;
5054            }
5055            Operator::I64AtomicRmw16XchgU { ref memarg } => {
5056                let loc = self.pop_value_released()?.0;
5057                let target = self.pop_value_released()?.0;
5058                let ret = self.acquire_location(&WpType::I64)?;
5059                self.value_stack.push((ret, CanonicalizeType::None));
5060                self.op_memory(
5061                    |this,
5062                     need_check,
5063                     imported_memories,
5064                     offset,
5065                     heap_access_oob,
5066                     unaligned_atomic| {
5067                        this.machine.i64_atomic_xchg_16u(
5068                            loc,
5069                            target,
5070                            memarg,
5071                            ret,
5072                            need_check,
5073                            imported_memories,
5074                            offset,
5075                            heap_access_oob,
5076                            unaligned_atomic,
5077                        )
5078                    },
5079                )?;
5080            }
5081            Operator::I64AtomicRmw32XchgU { ref memarg } => {
5082                let loc = self.pop_value_released()?.0;
5083                let target = self.pop_value_released()?.0;
5084                let ret = self.acquire_location(&WpType::I64)?;
5085                self.value_stack.push((ret, CanonicalizeType::None));
5086                self.op_memory(
5087                    |this,
5088                     need_check,
5089                     imported_memories,
5090                     offset,
5091                     heap_access_oob,
5092                     unaligned_atomic| {
5093                        this.machine.i64_atomic_xchg_32u(
5094                            loc,
5095                            target,
5096                            memarg,
5097                            ret,
5098                            need_check,
5099                            imported_memories,
5100                            offset,
5101                            heap_access_oob,
5102                            unaligned_atomic,
5103                        )
5104                    },
5105                )?;
5106            }
5107            Operator::I32AtomicRmwCmpxchg { ref memarg } => {
5108                let new = self.pop_value_released()?.0;
5109                let cmp = self.pop_value_released()?.0;
5110                let target = self.pop_value_released()?.0;
5111                let ret = self.acquire_location(&WpType::I32)?;
5112                self.value_stack.push((ret, CanonicalizeType::None));
5113                self.op_memory(
5114                    |this,
5115                     need_check,
5116                     imported_memories,
5117                     offset,
5118                     heap_access_oob,
5119                     unaligned_atomic| {
5120                        this.machine.i32_atomic_cmpxchg(
5121                            new,
5122                            cmp,
5123                            target,
5124                            memarg,
5125                            ret,
5126                            need_check,
5127                            imported_memories,
5128                            offset,
5129                            heap_access_oob,
5130                            unaligned_atomic,
5131                        )
5132                    },
5133                )?;
5134            }
5135            Operator::I64AtomicRmwCmpxchg { ref memarg } => {
5136                let new = self.pop_value_released()?.0;
5137                let cmp = self.pop_value_released()?.0;
5138                let target = self.pop_value_released()?.0;
5139                let ret = self.acquire_location(&WpType::I64)?;
5140                self.value_stack.push((ret, CanonicalizeType::None));
5141                self.op_memory(
5142                    |this,
5143                     need_check,
5144                     imported_memories,
5145                     offset,
5146                     heap_access_oob,
5147                     unaligned_atomic| {
5148                        this.machine.i64_atomic_cmpxchg(
5149                            new,
5150                            cmp,
5151                            target,
5152                            memarg,
5153                            ret,
5154                            need_check,
5155                            imported_memories,
5156                            offset,
5157                            heap_access_oob,
5158                            unaligned_atomic,
5159                        )
5160                    },
5161                )?;
5162            }
5163            Operator::I32AtomicRmw8CmpxchgU { ref memarg } => {
5164                let new = self.pop_value_released()?.0;
5165                let cmp = self.pop_value_released()?.0;
5166                let target = self.pop_value_released()?.0;
5167                let ret = self.acquire_location(&WpType::I32)?;
5168                self.value_stack.push((ret, CanonicalizeType::None));
5169                self.op_memory(
5170                    |this,
5171                     need_check,
5172                     imported_memories,
5173                     offset,
5174                     heap_access_oob,
5175                     unaligned_atomic| {
5176                        this.machine.i32_atomic_cmpxchg_8u(
5177                            new,
5178                            cmp,
5179                            target,
5180                            memarg,
5181                            ret,
5182                            need_check,
5183                            imported_memories,
5184                            offset,
5185                            heap_access_oob,
5186                            unaligned_atomic,
5187                        )
5188                    },
5189                )?;
5190            }
5191            Operator::I32AtomicRmw16CmpxchgU { ref memarg } => {
5192                let new = self.pop_value_released()?.0;
5193                let cmp = self.pop_value_released()?.0;
5194                let target = self.pop_value_released()?.0;
5195                let ret = self.acquire_location(&WpType::I32)?;
5196                self.value_stack.push((ret, CanonicalizeType::None));
5197                self.op_memory(
5198                    |this,
5199                     need_check,
5200                     imported_memories,
5201                     offset,
5202                     heap_access_oob,
5203                     unaligned_atomic| {
5204                        this.machine.i32_atomic_cmpxchg_16u(
5205                            new,
5206                            cmp,
5207                            target,
5208                            memarg,
5209                            ret,
5210                            need_check,
5211                            imported_memories,
5212                            offset,
5213                            heap_access_oob,
5214                            unaligned_atomic,
5215                        )
5216                    },
5217                )?;
5218            }
5219            Operator::I64AtomicRmw8CmpxchgU { ref memarg } => {
5220                let new = self.pop_value_released()?.0;
5221                let cmp = self.pop_value_released()?.0;
5222                let target = self.pop_value_released()?.0;
5223                let ret = self.acquire_location(&WpType::I64)?;
5224                self.value_stack.push((ret, CanonicalizeType::None));
5225                self.op_memory(
5226                    |this,
5227                     need_check,
5228                     imported_memories,
5229                     offset,
5230                     heap_access_oob,
5231                     unaligned_atomic| {
5232                        this.machine.i64_atomic_cmpxchg_8u(
5233                            new,
5234                            cmp,
5235                            target,
5236                            memarg,
5237                            ret,
5238                            need_check,
5239                            imported_memories,
5240                            offset,
5241                            heap_access_oob,
5242                            unaligned_atomic,
5243                        )
5244                    },
5245                )?;
5246            }
5247            Operator::I64AtomicRmw16CmpxchgU { ref memarg } => {
5248                let new = self.pop_value_released()?.0;
5249                let cmp = self.pop_value_released()?.0;
5250                let target = self.pop_value_released()?.0;
5251                let ret = self.acquire_location(&WpType::I64)?;
5252                self.value_stack.push((ret, CanonicalizeType::None));
5253                self.op_memory(
5254                    |this,
5255                     need_check,
5256                     imported_memories,
5257                     offset,
5258                     heap_access_oob,
5259                     unaligned_atomic| {
5260                        this.machine.i64_atomic_cmpxchg_16u(
5261                            new,
5262                            cmp,
5263                            target,
5264                            memarg,
5265                            ret,
5266                            need_check,
5267                            imported_memories,
5268                            offset,
5269                            heap_access_oob,
5270                            unaligned_atomic,
5271                        )
5272                    },
5273                )?;
5274            }
5275            Operator::I64AtomicRmw32CmpxchgU { ref memarg } => {
5276                let new = self.pop_value_released()?.0;
5277                let cmp = self.pop_value_released()?.0;
5278                let target = self.pop_value_released()?.0;
5279                let ret = self.acquire_location(&WpType::I64)?;
5280                self.value_stack.push((ret, CanonicalizeType::None));
5281                self.op_memory(
5282                    |this,
5283                     need_check,
5284                     imported_memories,
5285                     offset,
5286                     heap_access_oob,
5287                     unaligned_atomic| {
5288                        this.machine.i64_atomic_cmpxchg_32u(
5289                            new,
5290                            cmp,
5291                            target,
5292                            memarg,
5293                            ret,
5294                            need_check,
5295                            imported_memories,
5296                            offset,
5297                            heap_access_oob,
5298                            unaligned_atomic,
5299                        )
5300                    },
5301                )?;
5302            }
5303
5304            Operator::RefNull { .. } => {
5305                self.value_stack
5306                    .push((Location::Imm64(0), CanonicalizeType::None));
5307            }
5308            Operator::RefFunc { function_index } => {
5309                self.machine.move_location(
5310                    Size::S64,
5311                    Location::Memory(
5312                        self.machine.get_vmctx_reg(),
5313                        self.vmoffsets
5314                            .vmctx_builtin_function(VMBuiltinFunctionIndex::get_func_ref_index())
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, func_index] -> funcref
5326                    iter::once((
5327                        Location::Imm32(function_index as u32),
5328                        CanonicalizeType::None,
5329                    )),
5330                    iter::once(WpType::I32),
5331                    iter::once(WpType::Ref(WpRefType::new(true, WpHeapType::FUNC).unwrap())),
5332                    NativeCallType::IncludeVMCtxArgument,
5333                )?;
5334            }
5335            Operator::RefIsNull => {
5336                let loc_a = self.pop_value_released()?.0;
5337                let ret = self.acquire_location(&WpType::I32)?;
5338                self.machine.i64_cmp_eq(loc_a, Location::Imm64(0), ret)?;
5339                self.value_stack.push((ret, CanonicalizeType::None));
5340            }
5341            Operator::TableSet { table: index } => {
5342                let table_index = TableIndex::new(index as _);
5343                let value = self.value_stack.pop().unwrap();
5344                let index = self.value_stack.pop().unwrap();
5345
5346                self.machine.move_location(
5347                    Size::S64,
5348                    Location::Memory(
5349                        self.machine.get_vmctx_reg(),
5350                        self.vmoffsets.vmctx_builtin_function(
5351                            if self.module.local_table_index(table_index).is_some() {
5352                                VMBuiltinFunctionIndex::get_table_set_index()
5353                            } else {
5354                                VMBuiltinFunctionIndex::get_imported_table_set_index()
5355                            },
5356                        ) as i32,
5357                    ),
5358                    Location::GPR(self.machine.get_gpr_for_call()),
5359                )?;
5360
5361                self.emit_call_native(
5362                    |this| {
5363                        this.machine
5364                            .emit_call_register(this.machine.get_gpr_for_call())
5365                    },
5366                    // [vmctx, table_index, elem_index, reftype]
5367                    [
5368                        (
5369                            Location::Imm32(table_index.index() as u32),
5370                            CanonicalizeType::None,
5371                        ),
5372                        index,
5373                        value,
5374                    ]
5375                    .iter()
5376                    .cloned(),
5377                    [WpType::I32, WpType::I32, WpType::I64].iter().cloned(),
5378                    iter::empty(),
5379                    NativeCallType::IncludeVMCtxArgument,
5380                )?;
5381            }
5382            Operator::TableGet { table: index } => {
5383                let table_index = TableIndex::new(index as _);
5384                let index = self.value_stack.pop().unwrap();
5385
5386                self.machine.move_location(
5387                    Size::S64,
5388                    Location::Memory(
5389                        self.machine.get_vmctx_reg(),
5390                        self.vmoffsets.vmctx_builtin_function(
5391                            if self.module.local_table_index(table_index).is_some() {
5392                                VMBuiltinFunctionIndex::get_table_get_index()
5393                            } else {
5394                                VMBuiltinFunctionIndex::get_imported_table_get_index()
5395                            },
5396                        ) as i32,
5397                    ),
5398                    Location::GPR(self.machine.get_gpr_for_call()),
5399                )?;
5400
5401                self.emit_call_native(
5402                    |this| {
5403                        this.machine
5404                            .emit_call_register(this.machine.get_gpr_for_call())
5405                    },
5406                    // [vmctx, table_index, elem_index] -> reftype
5407                    [
5408                        (
5409                            Location::Imm32(table_index.index() as u32),
5410                            CanonicalizeType::None,
5411                        ),
5412                        index,
5413                    ]
5414                    .iter()
5415                    .cloned(),
5416                    [WpType::I32, WpType::I32].iter().cloned(),
5417                    iter::once(WpType::Ref(WpRefType::new(true, WpHeapType::FUNC).unwrap())),
5418                    NativeCallType::IncludeVMCtxArgument,
5419                )?;
5420            }
5421            Operator::TableSize { table: index } => {
5422                let table_index = TableIndex::new(index as _);
5423
5424                self.machine.move_location(
5425                    Size::S64,
5426                    Location::Memory(
5427                        self.machine.get_vmctx_reg(),
5428                        self.vmoffsets.vmctx_builtin_function(
5429                            if self.module.local_table_index(table_index).is_some() {
5430                                VMBuiltinFunctionIndex::get_table_size_index()
5431                            } else {
5432                                VMBuiltinFunctionIndex::get_imported_table_size_index()
5433                            },
5434                        ) as i32,
5435                    ),
5436                    Location::GPR(self.machine.get_gpr_for_call()),
5437                )?;
5438
5439                self.emit_call_native(
5440                    |this| {
5441                        this.machine
5442                            .emit_call_register(this.machine.get_gpr_for_call())
5443                    },
5444                    // [vmctx, table_index] -> i32
5445                    iter::once((
5446                        Location::Imm32(table_index.index() as u32),
5447                        CanonicalizeType::None,
5448                    )),
5449                    iter::once(WpType::I32),
5450                    iter::once(WpType::I32),
5451                    NativeCallType::IncludeVMCtxArgument,
5452                )?;
5453            }
5454            Operator::TableGrow { table: index } => {
5455                let table_index = TableIndex::new(index as _);
5456                let delta = self.value_stack.pop().unwrap();
5457                let init_value = self.value_stack.pop().unwrap();
5458
5459                self.machine.move_location(
5460                    Size::S64,
5461                    Location::Memory(
5462                        self.machine.get_vmctx_reg(),
5463                        self.vmoffsets.vmctx_builtin_function(
5464                            if self.module.local_table_index(table_index).is_some() {
5465                                VMBuiltinFunctionIndex::get_table_grow_index()
5466                            } else {
5467                                VMBuiltinFunctionIndex::get_imported_table_grow_index()
5468                            },
5469                        ) as i32,
5470                    ),
5471                    Location::GPR(self.machine.get_gpr_for_call()),
5472                )?;
5473
5474                self.emit_call_native(
5475                    |this| {
5476                        this.machine
5477                            .emit_call_register(this.machine.get_gpr_for_call())
5478                    },
5479                    // [vmctx, init_value, delta, table_index] -> u32
5480                    [
5481                        init_value,
5482                        delta,
5483                        (
5484                            Location::Imm32(table_index.index() as u32),
5485                            CanonicalizeType::None,
5486                        ),
5487                    ]
5488                    .iter()
5489                    .cloned(),
5490                    [WpType::I64, WpType::I32, WpType::I32].iter().cloned(),
5491                    iter::once(WpType::I32),
5492                    NativeCallType::IncludeVMCtxArgument,
5493                )?;
5494            }
5495            Operator::TableCopy {
5496                dst_table,
5497                src_table,
5498            } => {
5499                let len = self.value_stack.pop().unwrap();
5500                let src = self.value_stack.pop().unwrap();
5501                let dest = self.value_stack.pop().unwrap();
5502
5503                self.machine.move_location(
5504                    Size::S64,
5505                    Location::Memory(
5506                        self.machine.get_vmctx_reg(),
5507                        self.vmoffsets
5508                            .vmctx_builtin_function(VMBuiltinFunctionIndex::get_table_copy_index())
5509                            as i32,
5510                    ),
5511                    Location::GPR(self.machine.get_gpr_for_call()),
5512                )?;
5513
5514                self.emit_call_native(
5515                    |this| {
5516                        this.machine
5517                            .emit_call_register(this.machine.get_gpr_for_call())
5518                    },
5519                    // [vmctx, dst_table_index, src_table_index, dst, src, len]
5520                    [
5521                        (Location::Imm32(dst_table), CanonicalizeType::None),
5522                        (Location::Imm32(src_table), CanonicalizeType::None),
5523                        dest,
5524                        src,
5525                        len,
5526                    ]
5527                    .iter()
5528                    .cloned(),
5529                    [
5530                        WpType::I32,
5531                        WpType::I32,
5532                        WpType::I32,
5533                        WpType::I32,
5534                        WpType::I32,
5535                    ]
5536                    .iter()
5537                    .cloned(),
5538                    iter::empty(),
5539                    NativeCallType::IncludeVMCtxArgument,
5540                )?;
5541            }
5542
5543            Operator::TableFill { table } => {
5544                let len = self.value_stack.pop().unwrap();
5545                let val = self.value_stack.pop().unwrap();
5546                let dest = self.value_stack.pop().unwrap();
5547
5548                self.machine.move_location(
5549                    Size::S64,
5550                    Location::Memory(
5551                        self.machine.get_vmctx_reg(),
5552                        self.vmoffsets
5553                            .vmctx_builtin_function(VMBuiltinFunctionIndex::get_table_fill_index())
5554                            as i32,
5555                    ),
5556                    Location::GPR(self.machine.get_gpr_for_call()),
5557                )?;
5558
5559                self.emit_call_native(
5560                    |this| {
5561                        this.machine
5562                            .emit_call_register(this.machine.get_gpr_for_call())
5563                    },
5564                    // [vmctx, table_index, start_idx, item, len]
5565                    [
5566                        (Location::Imm32(table), CanonicalizeType::None),
5567                        dest,
5568                        val,
5569                        len,
5570                    ]
5571                    .iter()
5572                    .cloned(),
5573                    [WpType::I32, WpType::I32, WpType::I64, WpType::I32]
5574                        .iter()
5575                        .cloned(),
5576                    iter::empty(),
5577                    NativeCallType::IncludeVMCtxArgument,
5578                )?;
5579            }
5580            Operator::TableInit { elem_index, table } => {
5581                let len = self.value_stack.pop().unwrap();
5582                let src = self.value_stack.pop().unwrap();
5583                let dest = self.value_stack.pop().unwrap();
5584
5585                self.machine.move_location(
5586                    Size::S64,
5587                    Location::Memory(
5588                        self.machine.get_vmctx_reg(),
5589                        self.vmoffsets
5590                            .vmctx_builtin_function(VMBuiltinFunctionIndex::get_table_init_index())
5591                            as i32,
5592                    ),
5593                    Location::GPR(self.machine.get_gpr_for_call()),
5594                )?;
5595
5596                self.emit_call_native(
5597                    |this| {
5598                        this.machine
5599                            .emit_call_register(this.machine.get_gpr_for_call())
5600                    },
5601                    // [vmctx, table_index, elem_index, dst, src, len]
5602                    [
5603                        (Location::Imm32(table), CanonicalizeType::None),
5604                        (Location::Imm32(elem_index), CanonicalizeType::None),
5605                        dest,
5606                        src,
5607                        len,
5608                    ]
5609                    .iter()
5610                    .cloned(),
5611                    [
5612                        WpType::I32,
5613                        WpType::I32,
5614                        WpType::I32,
5615                        WpType::I32,
5616                        WpType::I32,
5617                    ]
5618                    .iter()
5619                    .cloned(),
5620                    iter::empty(),
5621                    NativeCallType::IncludeVMCtxArgument,
5622                )?;
5623            }
5624            Operator::ElemDrop { elem_index } => {
5625                self.machine.move_location(
5626                    Size::S64,
5627                    Location::Memory(
5628                        self.machine.get_vmctx_reg(),
5629                        self.vmoffsets
5630                            .vmctx_builtin_function(VMBuiltinFunctionIndex::get_elem_drop_index())
5631                            as i32,
5632                    ),
5633                    Location::GPR(self.machine.get_gpr_for_call()),
5634                )?;
5635
5636                self.emit_call_native(
5637                    |this| {
5638                        this.machine
5639                            .emit_call_register(this.machine.get_gpr_for_call())
5640                    },
5641                    // [vmctx, elem_index]
5642                    iter::once((Location::Imm32(elem_index), CanonicalizeType::None)),
5643                    [WpType::I32].iter().cloned(),
5644                    iter::empty(),
5645                    NativeCallType::IncludeVMCtxArgument,
5646                )?;
5647            }
5648            Operator::MemoryAtomicWait32 { ref memarg } => {
5649                let timeout = self.value_stack.pop().unwrap();
5650                let val = self.value_stack.pop().unwrap();
5651                let dst = self.value_stack.pop().unwrap();
5652
5653                let memory_index = MemoryIndex::new(memarg.memory as usize);
5654                let (memory_atomic_wait32, memory_index) =
5655                    if self.module.local_memory_index(memory_index).is_some() {
5656                        (
5657                            VMBuiltinFunctionIndex::get_memory_atomic_wait32_index(),
5658                            memory_index,
5659                        )
5660                    } else {
5661                        (
5662                            VMBuiltinFunctionIndex::get_imported_memory_atomic_wait32_index(),
5663                            memory_index,
5664                        )
5665                    };
5666
5667                self.machine.move_location(
5668                    Size::S64,
5669                    Location::Memory(
5670                        self.machine.get_vmctx_reg(),
5671                        self.vmoffsets.vmctx_builtin_function(memory_atomic_wait32) as i32,
5672                    ),
5673                    Location::GPR(self.machine.get_gpr_for_call()),
5674                )?;
5675
5676                self.emit_call_native(
5677                    |this| {
5678                        this.machine
5679                            .emit_call_register(this.machine.get_gpr_for_call())
5680                    },
5681                    // [vmctx, memory_index, dst, src, timeout]
5682                    [
5683                        (
5684                            Location::Imm32(memory_index.index() as u32),
5685                            CanonicalizeType::None,
5686                        ),
5687                        dst,
5688                        val,
5689                        timeout,
5690                    ]
5691                    .iter()
5692                    .cloned(),
5693                    [WpType::I32, WpType::I32, WpType::I32, WpType::I64]
5694                        .iter()
5695                        .cloned(),
5696                    iter::once(WpType::I32),
5697                    NativeCallType::IncludeVMCtxArgument,
5698                )?;
5699            }
5700            Operator::MemoryAtomicWait64 { ref memarg } => {
5701                let timeout = self.value_stack.pop().unwrap();
5702                let val = self.value_stack.pop().unwrap();
5703                let dst = self.value_stack.pop().unwrap();
5704
5705                let memory_index = MemoryIndex::new(memarg.memory as usize);
5706                let (memory_atomic_wait64, memory_index) =
5707                    if self.module.local_memory_index(memory_index).is_some() {
5708                        (
5709                            VMBuiltinFunctionIndex::get_memory_atomic_wait64_index(),
5710                            memory_index,
5711                        )
5712                    } else {
5713                        (
5714                            VMBuiltinFunctionIndex::get_imported_memory_atomic_wait64_index(),
5715                            memory_index,
5716                        )
5717                    };
5718
5719                self.machine.move_location(
5720                    Size::S64,
5721                    Location::Memory(
5722                        self.machine.get_vmctx_reg(),
5723                        self.vmoffsets.vmctx_builtin_function(memory_atomic_wait64) as i32,
5724                    ),
5725                    Location::GPR(self.machine.get_gpr_for_call()),
5726                )?;
5727
5728                self.emit_call_native(
5729                    |this| {
5730                        this.machine
5731                            .emit_call_register(this.machine.get_gpr_for_call())
5732                    },
5733                    // [vmctx, memory_index, dst, src, timeout]
5734                    [
5735                        (
5736                            Location::Imm32(memory_index.index() as u32),
5737                            CanonicalizeType::None,
5738                        ),
5739                        dst,
5740                        val,
5741                        timeout,
5742                    ]
5743                    .iter()
5744                    .cloned(),
5745                    [WpType::I32, WpType::I32, WpType::I64, WpType::I64]
5746                        .iter()
5747                        .cloned(),
5748                    iter::once(WpType::I32),
5749                    NativeCallType::IncludeVMCtxArgument,
5750                )?;
5751            }
5752            Operator::MemoryAtomicNotify { ref memarg } => {
5753                let cnt = self.value_stack.pop().unwrap();
5754                let dst = self.value_stack.pop().unwrap();
5755
5756                let memory_index = MemoryIndex::new(memarg.memory as usize);
5757                let (memory_atomic_notify, memory_index) =
5758                    if self.module.local_memory_index(memory_index).is_some() {
5759                        (
5760                            VMBuiltinFunctionIndex::get_memory_atomic_notify_index(),
5761                            memory_index,
5762                        )
5763                    } else {
5764                        (
5765                            VMBuiltinFunctionIndex::get_imported_memory_atomic_notify_index(),
5766                            memory_index,
5767                        )
5768                    };
5769
5770                self.machine.move_location(
5771                    Size::S64,
5772                    Location::Memory(
5773                        self.machine.get_vmctx_reg(),
5774                        self.vmoffsets.vmctx_builtin_function(memory_atomic_notify) as i32,
5775                    ),
5776                    Location::GPR(self.machine.get_gpr_for_call()),
5777                )?;
5778
5779                self.emit_call_native(
5780                    |this| {
5781                        this.machine
5782                            .emit_call_register(this.machine.get_gpr_for_call())
5783                    },
5784                    // [vmctx, memory_index, dst, cnt]
5785                    [
5786                        (
5787                            Location::Imm32(memory_index.index() as u32),
5788                            CanonicalizeType::None,
5789                        ),
5790                        dst,
5791                        cnt,
5792                    ]
5793                    .iter()
5794                    .cloned(),
5795                    [WpType::I32, WpType::I32, WpType::I32].iter().cloned(),
5796                    iter::once(WpType::I32),
5797                    NativeCallType::IncludeVMCtxArgument,
5798                )?;
5799            }
5800            _ => {
5801                return Err(CompileError::Codegen(format!(
5802                    "not yet implemented: {op:?}"
5803                )));
5804            }
5805        }
5806
5807        Ok(())
5808    }
5809
5810    fn add_assembly_comment(&mut self, comment: AssemblyComment) {
5811        // Collect assembly comments only if we're going to emit them.
5812        if self.config.callbacks.is_some() {
5813            self.assembly_comments
5814                .insert(self.machine.get_offset().0, comment);
5815        }
5816    }
5817
5818    pub fn finalize(
5819        mut self,
5820        data: &FunctionBodyData,
5821        arch: Architecture,
5822    ) -> Result<(CompiledFunction, Option<UnwindFrame>), CompileError> {
5823        self.add_assembly_comment(AssemblyComment::TrapHandlersTable);
5824        // Generate actual code for special labels.
5825        self.machine
5826            .emit_label(self.special_labels.integer_division_by_zero)?;
5827        self.machine
5828            .emit_illegal_op(TrapCode::IntegerDivisionByZero)?;
5829
5830        self.machine
5831            .emit_label(self.special_labels.integer_overflow)?;
5832        self.machine.emit_illegal_op(TrapCode::IntegerOverflow)?;
5833
5834        self.machine
5835            .emit_label(self.special_labels.heap_access_oob)?;
5836        self.machine
5837            .emit_illegal_op(TrapCode::HeapAccessOutOfBounds)?;
5838
5839        self.machine
5840            .emit_label(self.special_labels.table_access_oob)?;
5841        self.machine
5842            .emit_illegal_op(TrapCode::TableAccessOutOfBounds)?;
5843
5844        self.machine
5845            .emit_label(self.special_labels.indirect_call_null)?;
5846        self.machine.emit_illegal_op(TrapCode::IndirectCallToNull)?;
5847
5848        self.machine.emit_label(self.special_labels.bad_signature)?;
5849        self.machine.emit_illegal_op(TrapCode::BadSignature)?;
5850
5851        self.machine
5852            .emit_label(self.special_labels.unaligned_atomic)?;
5853        self.machine.emit_illegal_op(TrapCode::UnalignedAtomic)?;
5854
5855        // Notify the assembler backend to generate necessary code at end of function.
5856        self.machine.finalize_function()?;
5857
5858        let body_len = self.machine.assembler_get_offset().0;
5859
5860        #[cfg_attr(not(feature = "unwind"), allow(unused_mut))]
5861        let mut unwind_info = None;
5862        #[cfg_attr(not(feature = "unwind"), allow(unused_mut))]
5863        let mut fde = None;
5864        #[cfg(feature = "unwind")]
5865        match self.calling_convention {
5866            CallingConvention::SystemV | CallingConvention::AppleAarch64 => {
5867                let unwind = self.machine.gen_dwarf_unwind_info(body_len);
5868                if let Some(unwind) = unwind {
5869                    fde = Some(unwind.to_fde(Address::Symbol {
5870                        symbol: WriterRelocate::FUNCTION_SYMBOL,
5871                        addend: self.local_func_index.index() as _,
5872                    }));
5873                    unwind_info = Some(CompiledFunctionUnwindInfo::Dwarf);
5874                }
5875            }
5876            CallingConvention::WindowsFastcall => {
5877                let unwind = self.machine.gen_windows_unwind_info(body_len);
5878                if let Some(unwind) = unwind {
5879                    unwind_info = Some(CompiledFunctionUnwindInfo::WindowsX64(unwind));
5880                }
5881            }
5882            _ => (),
5883        };
5884
5885        let address_map =
5886            get_function_address_map(self.machine.instructions_address_map(), data, body_len);
5887        let traps = self.machine.collect_trap_information();
5888        let FinalizedAssembly {
5889            mut body,
5890            assembly_comments,
5891        } = self.machine.assembler_finalize(self.assembly_comments)?;
5892        body.shrink_to_fit();
5893
5894        if let Some(callbacks) = self.config.callbacks.as_ref() {
5895            callbacks.obj_memory_buffer(
5896                &CompiledKind::Local(self.local_func_index, self.function_name.clone()),
5897                &self.module.hash_string(),
5898                &body,
5899            );
5900            callbacks.asm_memory_buffer(
5901                &CompiledKind::Local(self.local_func_index, self.function_name.clone()),
5902                &self.module.hash_string(),
5903                arch,
5904                &body,
5905                assembly_comments,
5906            )?;
5907        }
5908
5909        Ok((
5910            CompiledFunction {
5911                body: FunctionBody { body, unwind_info },
5912                relocations: self.relocations.clone(),
5913                frame_info: CompiledFunctionFrameInfo { traps, address_map },
5914            },
5915            fde,
5916        ))
5917    }
5918    // FIXME: This implementation seems to be not enough to resolve all kinds of register dependencies
5919    // at call place.
5920    #[allow(clippy::type_complexity)]
5921    fn sort_call_movs(movs: &mut [(Location<M::GPR, M::SIMD>, M::GPR, Size)]) {
5922        for i in 0..movs.len() {
5923            for j in (i + 1)..movs.len() {
5924                if let Location::GPR(src_gpr) = movs[j].0
5925                    && src_gpr == movs[i].1
5926                {
5927                    movs.swap(i, j);
5928                }
5929            }
5930        }
5931    }
5932
5933    // Cycle detector. Uncomment this to debug possibly incorrect call-mov sequences.
5934    /*
5935    {
5936        use std::collections::{HashMap, HashSet, VecDeque};
5937        let mut mov_map: HashMap<GPR, HashSet<GPR>> = HashMap::new();
5938        for mov in movs.iter() {
5939            if let Location::GPR(src_gpr) = mov.0 {
5940                if src_gpr != mov.1 {
5941                    mov_map.entry(src_gpr).or_insert_with(|| HashSet::new()).insert(mov.1);
5942                }
5943            }
5944        }
5945
5946        for (start, _) in mov_map.iter() {
5947            let mut q: VecDeque<GPR> = VecDeque::new();
5948            let mut black: HashSet<GPR> = HashSet::new();
5949
5950            q.push_back(*start);
5951            black.insert(*start);
5952
5953            while q.len() > 0 {
5954                let reg = q.pop_front().unwrap();
5955                let empty_set = HashSet::new();
5956                for x in mov_map.get(&reg).unwrap_or(&empty_set).iter() {
5957                    if black.contains(x) {
5958                        panic!("cycle detected");
5959                    }
5960                    q.push_back(*x);
5961                    black.insert(*x);
5962                }
5963            }
5964        }
5965    }
5966    */
5967}