Skip to main content

wasmer_compiler_singlepass/
codegen.rs

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