Skip to main content

wasmer_compiler_singlepass/
codegen.rs

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