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