wasmtime_internal_cranelift/translate/
code_translator.rs

1//! This module contains the bulk of the interesting code performing the translation between
2//! WebAssembly and Cranelift IR.
3//!
4//! The translation is done in one pass, opcode by opcode. Two main data structures are used during
5//! code translations: the value stack and the control stack. The value stack mimics the execution
6//! of the WebAssembly stack machine: each instruction result is pushed onto the stack and
7//! instruction arguments are popped off the stack. Similarly, when encountering a control flow
8//! block, it is pushed onto the control stack and popped off when encountering the corresponding
9//! `End`.
10//!
11//! Another data structure, the translation state, records information concerning unreachable code
12//! status and about if inserting a return at the end of the function is necessary.
13//!
14//! Some of the WebAssembly instructions need information about the environment for which they
15//! are being translated:
16//!
17//! - the loads and stores need the memory base address;
18//! - the `get_global` and `set_global` instructions depend on how the globals are implemented;
19//! - `memory.size` and `memory.grow` are runtime functions;
20//! - `call_indirect` has to translate the function index into the address of where this
21//!    is;
22//!
23//! That is why `translate_function_body` takes an object having the `WasmRuntime` trait as
24//! argument.
25//!
26//! There is extra complexity associated with translation of 128-bit SIMD instructions.
27//! Wasm only considers there to be a single 128-bit vector type.  But CLIF's type system
28//! distinguishes different lane configurations, so considers 8X16, 16X8, 32X4 and 64X2 to be
29//! different types.  The result is that, in wasm, it's perfectly OK to take the output of (eg)
30//! an `add.16x8` and use that as an operand of a `sub.32x4`, without using any cast.  But when
31//! translated into CLIF, that will cause a verifier error due to the apparent type mismatch.
32//!
33//! This file works around that problem by liberally inserting `bitcast` instructions in many
34//! places -- mostly, before the use of vector values, either as arguments to CLIF instructions
35//! or as block actual parameters.  These are no-op casts which nevertheless have different
36//! input and output types, and are used (mostly) to "convert" 16X8, 32X4 and 64X2-typed vectors
37//! to the "canonical" type, 8X16.  Hence the functions `optionally_bitcast_vector`,
38//! `bitcast_arguments`, `pop*_with_bitcast`, `canonicalise_then_jump`,
39//! `canonicalise_then_br{z,nz}`, `is_non_canonical_v128` and `canonicalise_v128_values`.
40//! Note that the `bitcast*` functions are occasionally used to convert to some type other than
41//! 8X16, but the `canonicalise*` functions always convert to type 8X16.
42//!
43//! Be careful when adding support for new vector instructions.  And when adding new jumps, even
44//! if they are apparently don't have any connection to vectors.  Never generate any kind of
45//! (inter-block) jump directly.  Instead use `canonicalise_then_jump` and
46//! `canonicalise_then_br{z,nz}`.
47//!
48//! The use of bitcasts is ugly and inefficient, but currently unavoidable:
49//!
50//! * they make the logic in this file fragile: miss out a bitcast for any reason, and there is
51//!   the risk of the system failing in the verifier.  At least for debug builds.
52//!
53//! * in the new backends, they potentially interfere with pattern matching on CLIF -- the
54//!   patterns need to take into account the presence of bitcast nodes.
55//!
56//! * in the new backends, they get translated into machine-level vector-register-copy
57//!   instructions, none of which are actually necessary.  We then depend on the register
58//!   allocator to coalesce them all out.
59//!
60//! * they increase the total number of CLIF nodes that have to be processed, hence slowing down
61//!   the compilation pipeline.  Also, the extra coalescing work generates a slowdown.
62//!
63//! A better solution which would avoid all four problems would be to remove the 8X16, 16X8,
64//! 32X4 and 64X2 types from CLIF and instead have a single V128 type.
65//!
66//! For further background see also:
67//!   <https://github.com/bytecodealliance/wasmtime/issues/1147>
68//!     ("Too many raw_bitcasts in SIMD code")
69//!   <https://github.com/bytecodealliance/cranelift/pull/1251>
70//!     ("Add X128 type to represent WebAssembly's V128 type")
71//!   <https://github.com/bytecodealliance/cranelift/pull/1236>
72//!     ("Relax verification to allow I8X16 to act as a default vector type")
73
74use crate::Reachability;
75use crate::bounds_checks::{BoundsCheck, bounds_check_and_compute_addr};
76use crate::func_environ::{Extension, FuncEnvironment};
77use crate::translate::TargetEnvironment;
78use crate::translate::environ::StructFieldsVec;
79use crate::translate::stack::{ControlStackFrame, ElseData, FuncTranslationStacks};
80use crate::translate::translation_utils::{
81    block_with_params, blocktype_params_results, f32_translation, f64_translation,
82};
83use cranelift_codegen::ir::condcodes::{FloatCC, IntCC};
84use cranelift_codegen::ir::immediates::Offset32;
85use cranelift_codegen::ir::{
86    self, AtomicRmwOp, ExceptionTag, InstBuilder, JumpTableData, MemFlags, Value, ValueLabel,
87};
88use cranelift_codegen::ir::{BlockArg, types::*};
89use cranelift_codegen::packed_option::ReservedValue;
90use cranelift_frontend::{FunctionBuilder, Variable};
91use itertools::Itertools;
92use smallvec::{SmallVec, ToSmallVec};
93use std::collections::{HashMap, hash_map};
94use std::vec::Vec;
95use wasmparser::{FuncValidator, MemArg, Operator, WasmModuleResources};
96use wasmtime_environ::{
97    DataIndex, ElemIndex, FuncIndex, GlobalIndex, MemoryIndex, TableIndex, TagIndex, TypeConvert,
98    TypeIndex, WasmHeapType, WasmRefType, WasmResult, WasmValType, wasm_unsupported,
99};
100
101/// Given a `Reachability<T>`, unwrap the inner `T` or, when unreachable, set
102/// `state.reachable = false` and return.
103///
104/// Used in combination with calling `prepare_addr` and `prepare_atomic_addr`
105/// when we can statically determine that a Wasm access will unconditionally
106/// trap.
107macro_rules! unwrap_or_return_unreachable_state {
108    ($state:ident, $value:expr) => {
109        match $value {
110            Reachability::Reachable(x) => x,
111            Reachability::Unreachable => {
112                $state.reachable = false;
113                return Ok(());
114            }
115        }
116    };
117}
118
119/// Translates wasm operators into Cranelift IR instructions.
120pub fn translate_operator(
121    validator: &mut FuncValidator<impl WasmModuleResources>,
122    op: &Operator,
123    operand_types: Option<&[WasmValType]>,
124    builder: &mut FunctionBuilder,
125    stack: &mut FuncTranslationStacks,
126    environ: &mut FuncEnvironment<'_>,
127) -> WasmResult<()> {
128    log::trace!("Translating Wasm opcode: {op:?}");
129
130    if !stack.reachable {
131        translate_unreachable_operator(validator, &op, builder, stack, environ)?;
132        return Ok(());
133    }
134
135    // Given that we believe the current block is reachable, the FunctionBuilder ought to agree.
136    debug_assert!(!builder.is_unreachable());
137
138    let operand_types = operand_types.unwrap_or_else(|| {
139        panic!("should always have operand types available for valid, reachable ops; op = {op:?}")
140    });
141
142    // This big match treats all Wasm code operators.
143    match op {
144        /********************************** Locals ****************************************
145         *  `get_local` and `set_local` are treated as non-SSA variables and will completely
146         *  disappear in the Cranelift Code
147         ***********************************************************************************/
148        Operator::LocalGet { local_index } => {
149            let val = builder.use_var(Variable::from_u32(*local_index));
150            stack.push1(val);
151            let label = ValueLabel::from_u32(*local_index);
152            builder.set_val_label(val, label);
153        }
154        Operator::LocalSet { local_index } => {
155            let mut val = stack.pop1();
156
157            // Ensure SIMD values are cast to their default Cranelift type, I8x16.
158            let ty = builder.func.dfg.value_type(val);
159            if ty.is_vector() {
160                val = optionally_bitcast_vector(val, I8X16, builder);
161            }
162
163            builder.def_var(Variable::from_u32(*local_index), val);
164            let label = ValueLabel::from_u32(*local_index);
165            builder.set_val_label(val, label);
166        }
167        Operator::LocalTee { local_index } => {
168            let mut val = stack.peek1();
169
170            // Ensure SIMD values are cast to their default Cranelift type, I8x16.
171            let ty = builder.func.dfg.value_type(val);
172            if ty.is_vector() {
173                val = optionally_bitcast_vector(val, I8X16, builder);
174            }
175
176            builder.def_var(Variable::from_u32(*local_index), val);
177            let label = ValueLabel::from_u32(*local_index);
178            builder.set_val_label(val, label);
179        }
180        /********************************** Globals ****************************************
181         *  `get_global` and `set_global` are handled by the environment.
182         ***********************************************************************************/
183        Operator::GlobalGet { global_index } => {
184            let global_index = GlobalIndex::from_u32(*global_index);
185            let val = environ.translate_global_get(builder, global_index)?;
186            stack.push1(val);
187        }
188        Operator::GlobalSet { global_index } => {
189            let global_index = GlobalIndex::from_u32(*global_index);
190            let mut val = stack.pop1();
191            // Ensure SIMD values are cast to their default Cranelift type, I8x16.
192            if builder.func.dfg.value_type(val).is_vector() {
193                val = optionally_bitcast_vector(val, I8X16, builder);
194            }
195            environ.translate_global_set(builder, global_index, val)?;
196        }
197        /********************************* Stack misc ***************************************
198         *  `drop`, `nop`, `unreachable` and `select`.
199         ***********************************************************************************/
200        Operator::Drop => {
201            stack.pop1();
202        }
203        Operator::Select => {
204            let (mut arg1, mut arg2, cond) = stack.pop3();
205            if builder.func.dfg.value_type(arg1).is_vector() {
206                arg1 = optionally_bitcast_vector(arg1, I8X16, builder);
207            }
208            if builder.func.dfg.value_type(arg2).is_vector() {
209                arg2 = optionally_bitcast_vector(arg2, I8X16, builder);
210            }
211            stack.push1(builder.ins().select(cond, arg1, arg2));
212        }
213        Operator::TypedSelect { ty: _ } => {
214            // We ignore the explicit type parameter as it is only needed for
215            // validation, which we require to have been performed before
216            // translation.
217            let (mut arg1, mut arg2, cond) = stack.pop3();
218            if builder.func.dfg.value_type(arg1).is_vector() {
219                arg1 = optionally_bitcast_vector(arg1, I8X16, builder);
220            }
221            if builder.func.dfg.value_type(arg2).is_vector() {
222                arg2 = optionally_bitcast_vector(arg2, I8X16, builder);
223            }
224            stack.push1(builder.ins().select(cond, arg1, arg2));
225        }
226        Operator::Nop => {
227            // We do nothing
228        }
229        Operator::Unreachable => {
230            environ.trap(builder, crate::TRAP_UNREACHABLE);
231            stack.reachable = false;
232        }
233        /***************************** Control flow blocks **********************************
234         *  When starting a control flow block, we create a new `Block` that will hold the code
235         *  after the block, and we push a frame on the control stack. Depending on the type
236         *  of block, we create a new `Block` for the body of the block with an associated
237         *  jump instruction.
238         *
239         *  The `End` instruction pops the last control frame from the control stack, seals
240         *  the destination block (since `br` instructions targeting it only appear inside the
241         *  block and have already been translated) and modify the value stack to use the
242         *  possible `Block`'s arguments values.
243         ***********************************************************************************/
244        Operator::Block { blockty } => {
245            let (params, results) = blocktype_params_results(validator, *blockty)?;
246            let next = block_with_params(builder, results.clone(), environ)?;
247            stack.push_block(next, params.len(), results.len());
248        }
249        Operator::Loop { blockty } => {
250            let (params, results) = blocktype_params_results(validator, *blockty)?;
251            let loop_body = block_with_params(builder, params.clone(), environ)?;
252            let next = block_with_params(builder, results.clone(), environ)?;
253            canonicalise_then_jump(builder, loop_body, stack.peekn(params.len()));
254            stack.push_loop(loop_body, next, params.len(), results.len());
255
256            // Pop the initial `Block` actuals and replace them with the `Block`'s
257            // params since control flow joins at the top of the loop.
258            stack.popn(params.len());
259            stack
260                .stack
261                .extend_from_slice(builder.block_params(loop_body));
262
263            builder.switch_to_block(loop_body);
264            environ.translate_loop_header(builder)?;
265        }
266        Operator::If { blockty } => {
267            let val = stack.pop1();
268
269            let next_block = builder.create_block();
270            let (params, results) = blocktype_params_results(validator, *blockty)?;
271            let (destination, else_data) = if params.clone().eq(results.clone()) {
272                // It is possible there is no `else` block, so we will only
273                // allocate a block for it if/when we find the `else`. For now,
274                // we if the condition isn't true, then we jump directly to the
275                // destination block following the whole `if...end`. If we do end
276                // up discovering an `else`, then we will allocate a block for it
277                // and go back and patch the jump.
278                let destination = block_with_params(builder, results.clone(), environ)?;
279                let branch_inst = canonicalise_brif(
280                    builder,
281                    val,
282                    next_block,
283                    &[],
284                    destination,
285                    stack.peekn(params.len()),
286                );
287                (
288                    destination,
289                    ElseData::NoElse {
290                        branch_inst,
291                        placeholder: destination,
292                    },
293                )
294            } else {
295                // The `if` type signature is not valid without an `else` block,
296                // so we eagerly allocate the `else` block here.
297                let destination = block_with_params(builder, results.clone(), environ)?;
298                let else_block = block_with_params(builder, params.clone(), environ)?;
299                canonicalise_brif(
300                    builder,
301                    val,
302                    next_block,
303                    &[],
304                    else_block,
305                    stack.peekn(params.len()),
306                );
307                builder.seal_block(else_block);
308                (destination, ElseData::WithElse { else_block })
309            };
310
311            builder.seal_block(next_block); // Only predecessor is the current block.
312            builder.switch_to_block(next_block);
313
314            // Here we append an argument to a Block targeted by an argumentless jump instruction
315            // But in fact there are two cases:
316            // - either the If does not have a Else clause, in that case ty = EmptyBlock
317            //   and we add nothing;
318            // - either the If have an Else clause, in that case the destination of this jump
319            //   instruction will be changed later when we translate the Else operator.
320            stack.push_if(
321                destination,
322                else_data,
323                params.len(),
324                results.len(),
325                *blockty,
326            );
327        }
328        Operator::Else => {
329            let i = stack.control_stack.len() - 1;
330            match stack.control_stack[i] {
331                ControlStackFrame::If {
332                    ref else_data,
333                    head_is_reachable,
334                    ref mut consequent_ends_reachable,
335                    num_return_values,
336                    blocktype,
337                    destination,
338                    ..
339                } => {
340                    // We finished the consequent, so record its final
341                    // reachability state.
342                    debug_assert!(consequent_ends_reachable.is_none());
343                    *consequent_ends_reachable = Some(stack.reachable);
344
345                    if head_is_reachable {
346                        // We have a branch from the head of the `if` to the `else`.
347                        stack.reachable = true;
348
349                        // Ensure we have a block for the `else` block (it may have
350                        // already been pre-allocated, see `ElseData` for details).
351                        let else_block = match *else_data {
352                            ElseData::NoElse {
353                                branch_inst,
354                                placeholder,
355                            } => {
356                                let (params, _results) =
357                                    blocktype_params_results(validator, blocktype)?;
358                                debug_assert_eq!(params.len(), num_return_values);
359                                let else_block =
360                                    block_with_params(builder, params.clone(), environ)?;
361                                canonicalise_then_jump(
362                                    builder,
363                                    destination,
364                                    stack.peekn(params.len()),
365                                );
366                                stack.popn(params.len());
367
368                                builder.change_jump_destination(
369                                    branch_inst,
370                                    placeholder,
371                                    else_block,
372                                );
373                                builder.seal_block(else_block);
374                                else_block
375                            }
376                            ElseData::WithElse { else_block } => {
377                                canonicalise_then_jump(
378                                    builder,
379                                    destination,
380                                    stack.peekn(num_return_values),
381                                );
382                                stack.popn(num_return_values);
383                                else_block
384                            }
385                        };
386
387                        // You might be expecting that we push the parameters for this
388                        // `else` block here, something like this:
389                        //
390                        //     state.pushn(&control_stack_frame.params);
391                        //
392                        // We don't do that because they are already on the top of the stack
393                        // for us: we pushed the parameters twice when we saw the initial
394                        // `if` so that we wouldn't have to save the parameters in the
395                        // `ControlStackFrame` as another `Vec` allocation.
396
397                        builder.switch_to_block(else_block);
398
399                        // We don't bother updating the control frame's `ElseData`
400                        // to `WithElse` because nothing else will read it.
401                    }
402                }
403                _ => unreachable!(),
404            }
405        }
406        Operator::End => {
407            let frame = stack.control_stack.pop().unwrap();
408            let next_block = frame.following_code();
409            let return_count = frame.num_return_values();
410            let return_args = stack.peekn_mut(return_count);
411
412            canonicalise_then_jump(builder, next_block, return_args);
413            // You might expect that if we just finished an `if` block that
414            // didn't have a corresponding `else` block, then we would clean
415            // up our duplicate set of parameters that we pushed earlier
416            // right here. However, we don't have to explicitly do that,
417            // since we truncate the stack back to the original height
418            // below.
419
420            builder.switch_to_block(next_block);
421            builder.seal_block(next_block);
422
423            // If it is a loop we also have to seal the body loop block
424            if let ControlStackFrame::Loop { header, .. } = frame {
425                builder.seal_block(header)
426            }
427
428            frame.restore_catch_handlers(&mut stack.handlers, builder);
429
430            frame.truncate_value_stack_to_original_size(&mut stack.stack);
431            stack
432                .stack
433                .extend_from_slice(builder.block_params(next_block));
434        }
435        /**************************** Branch instructions *********************************
436         * The branch instructions all have as arguments a target nesting level, which
437         * corresponds to how many control stack frames do we have to pop to get the
438         * destination `Block`.
439         *
440         * Once the destination `Block` is found, we sometimes have to declare a certain depth
441         * of the stack unreachable, because some branch instructions are terminator.
442         *
443         * The `br_table` case is much more complicated because Cranelift's `br_table` instruction
444         * does not support jump arguments like all the other branch instructions. That is why, in
445         * the case where we would use jump arguments for every other branch instruction, we
446         * need to split the critical edges leaving the `br_tables` by creating one `Block` per
447         * table destination; the `br_table` will point to these newly created `Blocks` and these
448         * `Block`s contain only a jump instruction pointing to the final destination, this time with
449         * jump arguments.
450         *
451         * This system is also implemented in Cranelift's SSA construction algorithm, because
452         * `use_var` located in a destination `Block` of a `br_table` might trigger the addition
453         * of jump arguments in each predecessor branch instruction, one of which might be a
454         * `br_table`.
455         ***********************************************************************************/
456        Operator::Br { relative_depth } => {
457            let i = stack.control_stack.len() - 1 - (*relative_depth as usize);
458            let (return_count, br_destination) = {
459                let frame = &mut stack.control_stack[i];
460                // We signal that all the code that follows until the next End is unreachable
461                frame.set_branched_to_exit();
462                let return_count = if frame.is_loop() {
463                    frame.num_param_values()
464                } else {
465                    frame.num_return_values()
466                };
467                (return_count, frame.br_destination())
468            };
469            let destination_args = stack.peekn_mut(return_count);
470            canonicalise_then_jump(builder, br_destination, destination_args);
471            stack.popn(return_count);
472            stack.reachable = false;
473        }
474        Operator::BrIf { relative_depth } => translate_br_if(*relative_depth, builder, stack),
475        Operator::BrTable { targets } => {
476            let default = targets.default();
477            let mut min_depth = default;
478            for depth in targets.targets() {
479                let depth = depth?;
480                if depth < min_depth {
481                    min_depth = depth;
482                }
483            }
484            let jump_args_count = {
485                let i = stack.control_stack.len() - 1 - (min_depth as usize);
486                let min_depth_frame = &stack.control_stack[i];
487                if min_depth_frame.is_loop() {
488                    min_depth_frame.num_param_values()
489                } else {
490                    min_depth_frame.num_return_values()
491                }
492            };
493            let val = stack.pop1();
494            let mut data = Vec::with_capacity(targets.len() as usize);
495            if jump_args_count == 0 {
496                // No jump arguments
497                for depth in targets.targets() {
498                    let depth = depth?;
499                    let block = {
500                        let i = stack.control_stack.len() - 1 - (depth as usize);
501                        let frame = &mut stack.control_stack[i];
502                        frame.set_branched_to_exit();
503                        frame.br_destination()
504                    };
505                    data.push(builder.func.dfg.block_call(block, &[]));
506                }
507                let block = {
508                    let i = stack.control_stack.len() - 1 - (default as usize);
509                    let frame = &mut stack.control_stack[i];
510                    frame.set_branched_to_exit();
511                    frame.br_destination()
512                };
513                let block = builder.func.dfg.block_call(block, &[]);
514                let jt = builder.create_jump_table(JumpTableData::new(block, &data));
515                builder.ins().br_table(val, jt);
516            } else {
517                // Here we have jump arguments, but Cranelift's br_table doesn't support them
518                // We then proceed to split the edges going out of the br_table
519                let return_count = jump_args_count;
520                let mut dest_block_sequence = vec![];
521                let mut dest_block_map = HashMap::new();
522                for depth in targets.targets() {
523                    let depth = depth?;
524                    let branch_block = match dest_block_map.entry(depth as usize) {
525                        hash_map::Entry::Occupied(entry) => *entry.get(),
526                        hash_map::Entry::Vacant(entry) => {
527                            let block = builder.create_block();
528                            dest_block_sequence.push((depth as usize, block));
529                            *entry.insert(block)
530                        }
531                    };
532                    data.push(builder.func.dfg.block_call(branch_block, &[]));
533                }
534                let default_branch_block = match dest_block_map.entry(default as usize) {
535                    hash_map::Entry::Occupied(entry) => *entry.get(),
536                    hash_map::Entry::Vacant(entry) => {
537                        let block = builder.create_block();
538                        dest_block_sequence.push((default as usize, block));
539                        *entry.insert(block)
540                    }
541                };
542                let default_branch_block = builder.func.dfg.block_call(default_branch_block, &[]);
543                let jt = builder.create_jump_table(JumpTableData::new(default_branch_block, &data));
544                builder.ins().br_table(val, jt);
545                for (depth, dest_block) in dest_block_sequence {
546                    builder.switch_to_block(dest_block);
547                    builder.seal_block(dest_block);
548                    let real_dest_block = {
549                        let i = stack.control_stack.len() - 1 - depth;
550                        let frame = &mut stack.control_stack[i];
551                        frame.set_branched_to_exit();
552                        frame.br_destination()
553                    };
554                    let destination_args = stack.peekn_mut(return_count);
555                    canonicalise_then_jump(builder, real_dest_block, destination_args);
556                }
557                stack.popn(return_count);
558            }
559            stack.reachable = false;
560        }
561        Operator::Return => {
562            let return_count = {
563                let frame = &mut stack.control_stack[0];
564                frame.num_return_values()
565            };
566            {
567                let return_args = stack.peekn_mut(return_count);
568                environ.handle_before_return(&return_args, builder);
569                bitcast_wasm_returns(return_args, builder);
570                builder.ins().return_(return_args);
571            }
572            stack.popn(return_count);
573            stack.reachable = false;
574        }
575        /********************************** Exception handling **********************************/
576        Operator::Catch { .. }
577        | Operator::Rethrow { .. }
578        | Operator::Delegate { .. }
579        | Operator::CatchAll => {
580            return Err(wasm_unsupported!(
581                "legacy exception handling proposal is not supported"
582            ));
583        }
584
585        Operator::TryTable { try_table } => {
586            // First, create a block on the control stack. This also
587            // updates the handler state that is attached to all calls
588            // made within this block.
589            let body = builder.create_block();
590            let (params, results) = blocktype_params_results(validator, try_table.ty)?;
591            let next = block_with_params(builder, results.clone(), environ)?;
592            builder.ins().jump(body, []);
593            builder.seal_block(body);
594
595            // For each catch clause, create a block with the
596            // equivalent of `br` to the target (unboxing the exnref
597            // into stack values or pushing it directly, depending on
598            // the kind of clause).
599            let ckpt = stack.handlers.take_checkpoint();
600            let mut catch_blocks = vec![];
601            // Process in *reverse* order: see the comment on
602            // [`HandlerState`]. In brief, this allows us to unify the
603            // left-to-right matching semantics of a single
604            // `try_table`'s catch clauses with the inside-out
605            // (deepest scope first) semantics of nested `try_table`s.
606            for catch in try_table.catches.iter().rev() {
607                // This will register the block in `state.handlers`
608                // under the appropriate tag.
609                catch_blocks.push(create_catch_block(builder, stack, catch, environ)?);
610            }
611
612            stack.push_try_table_block(next, catch_blocks, params.len(), results.len(), ckpt);
613
614            // Continue codegen into the main body block.
615            builder.switch_to_block(body);
616        }
617
618        Operator::Throw { tag_index } => {
619            let tag_index = TagIndex::from_u32(*tag_index);
620            let arity = environ.tag_param_arity(tag_index);
621            let args = stack.peekn(arity);
622            environ.translate_exn_throw(builder, tag_index, args, stack.handlers.handlers())?;
623            stack.popn(arity);
624            stack.reachable = false;
625        }
626
627        Operator::ThrowRef => {
628            let exnref = stack.pop1();
629            environ.translate_exn_throw_ref(builder, exnref, stack.handlers.handlers())?;
630            stack.reachable = false;
631        }
632
633        /************************************ Calls ****************************************
634         * The call instructions pop off their arguments from the stack and append their
635         * return values to it. `call_indirect` needs environment support because there is an
636         * argument referring to an index in the external functions table of the module.
637         ************************************************************************************/
638        Operator::Call { function_index } => {
639            let function_index = FuncIndex::from_u32(*function_index);
640            let ty = environ.module.functions[function_index]
641                .signature
642                .unwrap_module_type_index();
643            let sig_ref = environ.get_or_create_interned_sig_ref(builder.func, ty);
644            let num_args = environ.num_params_for_func(function_index);
645
646            // Bitcast any vector arguments to their default type, I8X16, before calling.
647            let args = stack.peekn_mut(num_args);
648            bitcast_wasm_params(environ, sig_ref, args, builder);
649            let args = stack.peekn(num_args); // Re-borrow immutably.
650
651            let inst_results = environ.translate_call(
652                builder,
653                function_index,
654                sig_ref,
655                args,
656                stack.handlers.handlers(),
657            )?;
658
659            debug_assert_eq!(
660                inst_results.len(),
661                builder.func.dfg.signatures[sig_ref].returns.len(),
662                "translate_call results should match the call signature"
663            );
664            stack.popn(num_args);
665            stack.pushn(&inst_results);
666        }
667        Operator::CallIndirect {
668            type_index,
669            table_index,
670        } => {
671            // `type_index` is the index of the function's signature and
672            // `table_index` is the index of the table to search the function
673            // in.
674            let type_index = TypeIndex::from_u32(*type_index);
675            let sigref = environ.get_or_create_sig_ref(builder.func, type_index);
676            let num_args = environ.num_params_for_function_type(type_index);
677            let callee = stack.pop1();
678
679            // Bitcast any vector arguments to their default type, I8X16, before calling.
680            let args = stack.peekn_mut(num_args);
681            bitcast_wasm_params(environ, sigref, args, builder);
682
683            let inst_results = environ.translate_call_indirect(
684                builder,
685                validator.features(),
686                TableIndex::from_u32(*table_index),
687                type_index,
688                sigref,
689                callee,
690                stack.peekn(num_args),
691                stack.handlers.handlers(),
692            )?;
693            let inst_results = match inst_results {
694                Some(results) => results,
695                None => {
696                    stack.reachable = false;
697                    return Ok(());
698                }
699            };
700
701            debug_assert_eq!(
702                inst_results.len(),
703                builder.func.dfg.signatures[sigref].returns.len(),
704                "translate_call_indirect results should match the call signature"
705            );
706            stack.popn(num_args);
707            stack.pushn(&inst_results);
708        }
709        /******************************* Tail Calls ******************************************
710         * The tail call instructions pop their arguments from the stack and
711         * then permanently transfer control to their callee. The indirect
712         * version requires environment support (while the direct version can
713         * optionally be hooked but doesn't require it) it interacts with the
714         * VM's runtime state via tables.
715         ************************************************************************************/
716        Operator::ReturnCall { function_index } => {
717            let function_index = FuncIndex::from_u32(*function_index);
718            let ty = environ.module.functions[function_index]
719                .signature
720                .unwrap_module_type_index();
721            let sig_ref = environ.get_or_create_interned_sig_ref(builder.func, ty);
722            let num_args = environ.num_params_for_func(function_index);
723
724            // Bitcast any vector arguments to their default type, I8X16, before calling.
725            let args = stack.peekn_mut(num_args);
726            bitcast_wasm_params(environ, sig_ref, args, builder);
727
728            environ.translate_return_call(builder, function_index, sig_ref, args)?;
729
730            stack.popn(num_args);
731            stack.reachable = false;
732        }
733        Operator::ReturnCallIndirect {
734            type_index,
735            table_index,
736        } => {
737            // `type_index` is the index of the function's signature and
738            // `table_index` is the index of the table to search the function
739            // in.
740            let type_index = TypeIndex::from_u32(*type_index);
741            let sigref = environ.get_or_create_sig_ref(builder.func, type_index);
742            let num_args = environ.num_params_for_function_type(type_index);
743            let callee = stack.pop1();
744
745            // Bitcast any vector arguments to their default type, I8X16, before calling.
746            let args = stack.peekn_mut(num_args);
747            bitcast_wasm_params(environ, sigref, args, builder);
748
749            environ.translate_return_call_indirect(
750                builder,
751                validator.features(),
752                TableIndex::from_u32(*table_index),
753                type_index,
754                sigref,
755                callee,
756                stack.peekn(num_args),
757            )?;
758
759            stack.popn(num_args);
760            stack.reachable = false;
761        }
762        Operator::ReturnCallRef { type_index } => {
763            // Get function signature
764            // `index` is the index of the function's signature and `table_index` is the index of
765            // the table to search the function in.
766            let type_index = TypeIndex::from_u32(*type_index);
767            let sigref = environ.get_or_create_sig_ref(builder.func, type_index);
768            let num_args = environ.num_params_for_function_type(type_index);
769            let callee = stack.pop1();
770
771            // Bitcast any vector arguments to their default type, I8X16, before calling.
772            let args = stack.peekn_mut(num_args);
773            bitcast_wasm_params(environ, sigref, args, builder);
774
775            environ.translate_return_call_ref(builder, sigref, callee, stack.peekn(num_args))?;
776
777            stack.popn(num_args);
778            stack.reachable = false;
779        }
780        /******************************* Memory management ***********************************
781         * Memory management is handled by environment. It is usually translated into calls to
782         * special functions.
783         ************************************************************************************/
784        Operator::MemoryGrow { mem } => {
785            // The WebAssembly MVP only supports one linear memory, but we expect the reserved
786            // argument to be a memory index.
787            let mem = MemoryIndex::from_u32(*mem);
788            let _heap = environ.get_or_create_heap(builder.func, mem);
789            let val = stack.pop1();
790            environ.before_memory_grow(builder, val, mem);
791            stack.push1(environ.translate_memory_grow(builder, mem, val)?)
792        }
793        Operator::MemorySize { mem } => {
794            let mem = MemoryIndex::from_u32(*mem);
795            let _heap = environ.get_or_create_heap(builder.func, mem);
796            stack.push1(environ.translate_memory_size(builder.cursor(), mem)?);
797        }
798        /******************************* Load instructions ***********************************
799         * Wasm specifies an integer alignment flag but we drop it in Cranelift.
800         * The memory base address is provided by the environment.
801         ************************************************************************************/
802        Operator::I32Load8U { memarg } => {
803            unwrap_or_return_unreachable_state!(
804                stack,
805                translate_load(memarg, ir::Opcode::Uload8, I32, builder, stack, environ)?
806            );
807        }
808        Operator::I32Load16U { memarg } => {
809            unwrap_or_return_unreachable_state!(
810                stack,
811                translate_load(memarg, ir::Opcode::Uload16, I32, builder, stack, environ)?
812            );
813        }
814        Operator::I32Load8S { memarg } => {
815            unwrap_or_return_unreachable_state!(
816                stack,
817                translate_load(memarg, ir::Opcode::Sload8, I32, builder, stack, environ)?
818            );
819        }
820        Operator::I32Load16S { memarg } => {
821            unwrap_or_return_unreachable_state!(
822                stack,
823                translate_load(memarg, ir::Opcode::Sload16, I32, builder, stack, environ)?
824            );
825        }
826        Operator::I64Load8U { memarg } => {
827            unwrap_or_return_unreachable_state!(
828                stack,
829                translate_load(memarg, ir::Opcode::Uload8, I64, builder, stack, environ)?
830            );
831        }
832        Operator::I64Load16U { memarg } => {
833            unwrap_or_return_unreachable_state!(
834                stack,
835                translate_load(memarg, ir::Opcode::Uload16, I64, builder, stack, environ)?
836            );
837        }
838        Operator::I64Load8S { memarg } => {
839            unwrap_or_return_unreachable_state!(
840                stack,
841                translate_load(memarg, ir::Opcode::Sload8, I64, builder, stack, environ)?
842            );
843        }
844        Operator::I64Load16S { memarg } => {
845            unwrap_or_return_unreachable_state!(
846                stack,
847                translate_load(memarg, ir::Opcode::Sload16, I64, builder, stack, environ)?
848            );
849        }
850        Operator::I64Load32S { memarg } => {
851            unwrap_or_return_unreachable_state!(
852                stack,
853                translate_load(memarg, ir::Opcode::Sload32, I64, builder, stack, environ)?
854            );
855        }
856        Operator::I64Load32U { memarg } => {
857            unwrap_or_return_unreachable_state!(
858                stack,
859                translate_load(memarg, ir::Opcode::Uload32, I64, builder, stack, environ)?
860            );
861        }
862        Operator::I32Load { memarg } => {
863            unwrap_or_return_unreachable_state!(
864                stack,
865                translate_load(memarg, ir::Opcode::Load, I32, builder, stack, environ)?
866            );
867        }
868        Operator::F32Load { memarg } => {
869            unwrap_or_return_unreachable_state!(
870                stack,
871                translate_load(memarg, ir::Opcode::Load, F32, builder, stack, environ)?
872            );
873        }
874        Operator::I64Load { memarg } => {
875            unwrap_or_return_unreachable_state!(
876                stack,
877                translate_load(memarg, ir::Opcode::Load, I64, builder, stack, environ)?
878            );
879        }
880        Operator::F64Load { memarg } => {
881            unwrap_or_return_unreachable_state!(
882                stack,
883                translate_load(memarg, ir::Opcode::Load, F64, builder, stack, environ)?
884            );
885        }
886        Operator::V128Load { memarg } => {
887            unwrap_or_return_unreachable_state!(
888                stack,
889                translate_load(memarg, ir::Opcode::Load, I8X16, builder, stack, environ)?
890            );
891        }
892        Operator::V128Load8x8S { memarg } => {
893            //TODO(#6829): add before_load() and before_store() hooks for SIMD loads and stores.
894            let (flags, _, base) = unwrap_or_return_unreachable_state!(
895                stack,
896                prepare_addr(memarg, 8, builder, stack, environ)?
897            );
898            let loaded = builder.ins().sload8x8(flags, base, 0);
899            stack.push1(loaded);
900        }
901        Operator::V128Load8x8U { memarg } => {
902            let (flags, _, base) = unwrap_or_return_unreachable_state!(
903                stack,
904                prepare_addr(memarg, 8, builder, stack, environ)?
905            );
906            let loaded = builder.ins().uload8x8(flags, base, 0);
907            stack.push1(loaded);
908        }
909        Operator::V128Load16x4S { memarg } => {
910            let (flags, _, base) = unwrap_or_return_unreachable_state!(
911                stack,
912                prepare_addr(memarg, 8, builder, stack, environ)?
913            );
914            let loaded = builder.ins().sload16x4(flags, base, 0);
915            stack.push1(loaded);
916        }
917        Operator::V128Load16x4U { memarg } => {
918            let (flags, _, base) = unwrap_or_return_unreachable_state!(
919                stack,
920                prepare_addr(memarg, 8, builder, stack, environ)?
921            );
922            let loaded = builder.ins().uload16x4(flags, base, 0);
923            stack.push1(loaded);
924        }
925        Operator::V128Load32x2S { memarg } => {
926            let (flags, _, base) = unwrap_or_return_unreachable_state!(
927                stack,
928                prepare_addr(memarg, 8, builder, stack, environ)?
929            );
930            let loaded = builder.ins().sload32x2(flags, base, 0);
931            stack.push1(loaded);
932        }
933        Operator::V128Load32x2U { memarg } => {
934            let (flags, _, base) = unwrap_or_return_unreachable_state!(
935                stack,
936                prepare_addr(memarg, 8, builder, stack, environ)?
937            );
938            let loaded = builder.ins().uload32x2(flags, base, 0);
939            stack.push1(loaded);
940        }
941        /****************************** Store instructions ***********************************
942         * Wasm specifies an integer alignment flag but we drop it in Cranelift.
943         * The memory base address is provided by the environment.
944         ************************************************************************************/
945        Operator::I32Store { memarg }
946        | Operator::I64Store { memarg }
947        | Operator::F32Store { memarg }
948        | Operator::F64Store { memarg } => {
949            translate_store(memarg, ir::Opcode::Store, builder, stack, environ)?;
950        }
951        Operator::I32Store8 { memarg } | Operator::I64Store8 { memarg } => {
952            translate_store(memarg, ir::Opcode::Istore8, builder, stack, environ)?;
953        }
954        Operator::I32Store16 { memarg } | Operator::I64Store16 { memarg } => {
955            translate_store(memarg, ir::Opcode::Istore16, builder, stack, environ)?;
956        }
957        Operator::I64Store32 { memarg } => {
958            translate_store(memarg, ir::Opcode::Istore32, builder, stack, environ)?;
959        }
960        Operator::V128Store { memarg } => {
961            translate_store(memarg, ir::Opcode::Store, builder, stack, environ)?;
962        }
963        /****************************** Nullary Operators ************************************/
964        Operator::I32Const { value } => {
965            stack.push1(builder.ins().iconst(I32, i64::from(value.cast_unsigned())));
966        }
967        Operator::I64Const { value } => stack.push1(builder.ins().iconst(I64, *value)),
968        Operator::F32Const { value } => {
969            stack.push1(builder.ins().f32const(f32_translation(*value)));
970        }
971        Operator::F64Const { value } => {
972            stack.push1(builder.ins().f64const(f64_translation(*value)));
973        }
974        /******************************* Unary Operators *************************************/
975        Operator::I32Clz | Operator::I64Clz => {
976            let arg = stack.pop1();
977            stack.push1(builder.ins().clz(arg));
978        }
979        Operator::I32Ctz | Operator::I64Ctz => {
980            let arg = stack.pop1();
981            stack.push1(builder.ins().ctz(arg));
982        }
983        Operator::I32Popcnt | Operator::I64Popcnt => {
984            let arg = stack.pop1();
985            stack.push1(builder.ins().popcnt(arg));
986        }
987        Operator::I64ExtendI32S => {
988            let val = stack.pop1();
989            stack.push1(builder.ins().sextend(I64, val));
990        }
991        Operator::I64ExtendI32U => {
992            let val = stack.pop1();
993            stack.push1(builder.ins().uextend(I64, val));
994        }
995        Operator::I32WrapI64 => {
996            let val = stack.pop1();
997            stack.push1(builder.ins().ireduce(I32, val));
998        }
999        Operator::F32Sqrt | Operator::F64Sqrt => {
1000            let arg = stack.pop1();
1001            stack.push1(builder.ins().sqrt(arg));
1002        }
1003        Operator::F32Ceil => {
1004            let arg = stack.pop1();
1005            stack.push1(environ.ceil_f32(builder, arg));
1006        }
1007        Operator::F64Ceil => {
1008            let arg = stack.pop1();
1009            stack.push1(environ.ceil_f64(builder, arg));
1010        }
1011        Operator::F32Floor => {
1012            let arg = stack.pop1();
1013            stack.push1(environ.floor_f32(builder, arg));
1014        }
1015        Operator::F64Floor => {
1016            let arg = stack.pop1();
1017            stack.push1(environ.floor_f64(builder, arg));
1018        }
1019        Operator::F32Trunc => {
1020            let arg = stack.pop1();
1021            stack.push1(environ.trunc_f32(builder, arg));
1022        }
1023        Operator::F64Trunc => {
1024            let arg = stack.pop1();
1025            stack.push1(environ.trunc_f64(builder, arg));
1026        }
1027        Operator::F32Nearest => {
1028            let arg = stack.pop1();
1029            stack.push1(environ.nearest_f32(builder, arg));
1030        }
1031        Operator::F64Nearest => {
1032            let arg = stack.pop1();
1033            stack.push1(environ.nearest_f64(builder, arg));
1034        }
1035        Operator::F32Abs | Operator::F64Abs => {
1036            let val = stack.pop1();
1037            stack.push1(builder.ins().fabs(val));
1038        }
1039        Operator::F32Neg | Operator::F64Neg => {
1040            let arg = stack.pop1();
1041            stack.push1(builder.ins().fneg(arg));
1042        }
1043        Operator::F64ConvertI64U | Operator::F64ConvertI32U => {
1044            let val = stack.pop1();
1045            stack.push1(builder.ins().fcvt_from_uint(F64, val));
1046        }
1047        Operator::F64ConvertI64S | Operator::F64ConvertI32S => {
1048            let val = stack.pop1();
1049            stack.push1(builder.ins().fcvt_from_sint(F64, val));
1050        }
1051        Operator::F32ConvertI64S | Operator::F32ConvertI32S => {
1052            let val = stack.pop1();
1053            stack.push1(builder.ins().fcvt_from_sint(F32, val));
1054        }
1055        Operator::F32ConvertI64U | Operator::F32ConvertI32U => {
1056            let val = stack.pop1();
1057            stack.push1(builder.ins().fcvt_from_uint(F32, val));
1058        }
1059        Operator::F64PromoteF32 => {
1060            let val = stack.pop1();
1061            stack.push1(builder.ins().fpromote(F64, val));
1062        }
1063        Operator::F32DemoteF64 => {
1064            let val = stack.pop1();
1065            stack.push1(builder.ins().fdemote(F32, val));
1066        }
1067        Operator::I64TruncF64S | Operator::I64TruncF32S => {
1068            let val = stack.pop1();
1069            stack.push1(environ.translate_fcvt_to_sint(builder, I64, val));
1070        }
1071        Operator::I32TruncF64S | Operator::I32TruncF32S => {
1072            let val = stack.pop1();
1073            stack.push1(environ.translate_fcvt_to_sint(builder, I32, val));
1074        }
1075        Operator::I64TruncF64U | Operator::I64TruncF32U => {
1076            let val = stack.pop1();
1077            stack.push1(environ.translate_fcvt_to_uint(builder, I64, val));
1078        }
1079        Operator::I32TruncF64U | Operator::I32TruncF32U => {
1080            let val = stack.pop1();
1081            stack.push1(environ.translate_fcvt_to_uint(builder, I32, val));
1082        }
1083        Operator::I64TruncSatF64S | Operator::I64TruncSatF32S => {
1084            let val = stack.pop1();
1085            stack.push1(builder.ins().fcvt_to_sint_sat(I64, val));
1086        }
1087        Operator::I32TruncSatF64S | Operator::I32TruncSatF32S => {
1088            let val = stack.pop1();
1089            stack.push1(builder.ins().fcvt_to_sint_sat(I32, val));
1090        }
1091        Operator::I64TruncSatF64U | Operator::I64TruncSatF32U => {
1092            let val = stack.pop1();
1093            stack.push1(builder.ins().fcvt_to_uint_sat(I64, val));
1094        }
1095        Operator::I32TruncSatF64U | Operator::I32TruncSatF32U => {
1096            let val = stack.pop1();
1097            stack.push1(builder.ins().fcvt_to_uint_sat(I32, val));
1098        }
1099        Operator::F32ReinterpretI32 => {
1100            let val = stack.pop1();
1101            stack.push1(builder.ins().bitcast(F32, MemFlags::new(), val));
1102        }
1103        Operator::F64ReinterpretI64 => {
1104            let val = stack.pop1();
1105            stack.push1(builder.ins().bitcast(F64, MemFlags::new(), val));
1106        }
1107        Operator::I32ReinterpretF32 => {
1108            let val = stack.pop1();
1109            stack.push1(builder.ins().bitcast(I32, MemFlags::new(), val));
1110        }
1111        Operator::I64ReinterpretF64 => {
1112            let val = stack.pop1();
1113            stack.push1(builder.ins().bitcast(I64, MemFlags::new(), val));
1114        }
1115        Operator::I32Extend8S => {
1116            let val = stack.pop1();
1117            stack.push1(builder.ins().ireduce(I8, val));
1118            let val = stack.pop1();
1119            stack.push1(builder.ins().sextend(I32, val));
1120        }
1121        Operator::I32Extend16S => {
1122            let val = stack.pop1();
1123            stack.push1(builder.ins().ireduce(I16, val));
1124            let val = stack.pop1();
1125            stack.push1(builder.ins().sextend(I32, val));
1126        }
1127        Operator::I64Extend8S => {
1128            let val = stack.pop1();
1129            stack.push1(builder.ins().ireduce(I8, val));
1130            let val = stack.pop1();
1131            stack.push1(builder.ins().sextend(I64, val));
1132        }
1133        Operator::I64Extend16S => {
1134            let val = stack.pop1();
1135            stack.push1(builder.ins().ireduce(I16, val));
1136            let val = stack.pop1();
1137            stack.push1(builder.ins().sextend(I64, val));
1138        }
1139        Operator::I64Extend32S => {
1140            let val = stack.pop1();
1141            stack.push1(builder.ins().ireduce(I32, val));
1142            let val = stack.pop1();
1143            stack.push1(builder.ins().sextend(I64, val));
1144        }
1145        /****************************** Binary Operators ************************************/
1146        Operator::I32Add | Operator::I64Add => {
1147            let (arg1, arg2) = stack.pop2();
1148            stack.push1(builder.ins().iadd(arg1, arg2));
1149        }
1150        Operator::I32And | Operator::I64And => {
1151            let (arg1, arg2) = stack.pop2();
1152            stack.push1(builder.ins().band(arg1, arg2));
1153        }
1154        Operator::I32Or | Operator::I64Or => {
1155            let (arg1, arg2) = stack.pop2();
1156            stack.push1(builder.ins().bor(arg1, arg2));
1157        }
1158        Operator::I32Xor | Operator::I64Xor => {
1159            let (arg1, arg2) = stack.pop2();
1160            stack.push1(builder.ins().bxor(arg1, arg2));
1161        }
1162        Operator::I32Shl | Operator::I64Shl => {
1163            let (arg1, arg2) = stack.pop2();
1164            stack.push1(builder.ins().ishl(arg1, arg2));
1165        }
1166        Operator::I32ShrS | Operator::I64ShrS => {
1167            let (arg1, arg2) = stack.pop2();
1168            stack.push1(builder.ins().sshr(arg1, arg2));
1169        }
1170        Operator::I32ShrU | Operator::I64ShrU => {
1171            let (arg1, arg2) = stack.pop2();
1172            stack.push1(builder.ins().ushr(arg1, arg2));
1173        }
1174        Operator::I32Rotl | Operator::I64Rotl => {
1175            let (arg1, arg2) = stack.pop2();
1176            stack.push1(builder.ins().rotl(arg1, arg2));
1177        }
1178        Operator::I32Rotr | Operator::I64Rotr => {
1179            let (arg1, arg2) = stack.pop2();
1180            stack.push1(builder.ins().rotr(arg1, arg2));
1181        }
1182        Operator::F32Add | Operator::F64Add => {
1183            let (arg1, arg2) = stack.pop2();
1184            stack.push1(builder.ins().fadd(arg1, arg2));
1185        }
1186        Operator::I32Sub | Operator::I64Sub => {
1187            let (arg1, arg2) = stack.pop2();
1188            stack.push1(builder.ins().isub(arg1, arg2));
1189        }
1190        Operator::F32Sub | Operator::F64Sub => {
1191            let (arg1, arg2) = stack.pop2();
1192            stack.push1(builder.ins().fsub(arg1, arg2));
1193        }
1194        Operator::I32Mul | Operator::I64Mul => {
1195            let (arg1, arg2) = stack.pop2();
1196            stack.push1(builder.ins().imul(arg1, arg2));
1197        }
1198        Operator::F32Mul | Operator::F64Mul => {
1199            let (arg1, arg2) = stack.pop2();
1200            stack.push1(builder.ins().fmul(arg1, arg2));
1201        }
1202        Operator::F32Div | Operator::F64Div => {
1203            let (arg1, arg2) = stack.pop2();
1204            stack.push1(builder.ins().fdiv(arg1, arg2));
1205        }
1206        Operator::I32DivS | Operator::I64DivS => {
1207            let (arg1, arg2) = stack.pop2();
1208            stack.push1(environ.translate_sdiv(builder, arg1, arg2));
1209        }
1210        Operator::I32DivU | Operator::I64DivU => {
1211            let (arg1, arg2) = stack.pop2();
1212            stack.push1(environ.translate_udiv(builder, arg1, arg2));
1213        }
1214        Operator::I32RemS | Operator::I64RemS => {
1215            let (arg1, arg2) = stack.pop2();
1216            stack.push1(environ.translate_srem(builder, arg1, arg2));
1217        }
1218        Operator::I32RemU | Operator::I64RemU => {
1219            let (arg1, arg2) = stack.pop2();
1220            stack.push1(environ.translate_urem(builder, arg1, arg2));
1221        }
1222        Operator::F32Min | Operator::F64Min => {
1223            let (arg1, arg2) = stack.pop2();
1224            stack.push1(builder.ins().fmin(arg1, arg2));
1225        }
1226        Operator::F32Max | Operator::F64Max => {
1227            let (arg1, arg2) = stack.pop2();
1228            stack.push1(builder.ins().fmax(arg1, arg2));
1229        }
1230        Operator::F32Copysign | Operator::F64Copysign => {
1231            let (arg1, arg2) = stack.pop2();
1232            stack.push1(builder.ins().fcopysign(arg1, arg2));
1233        }
1234        /**************************** Comparison Operators **********************************/
1235        Operator::I32LtS | Operator::I64LtS => {
1236            translate_icmp(IntCC::SignedLessThan, builder, stack)
1237        }
1238        Operator::I32LtU | Operator::I64LtU => {
1239            translate_icmp(IntCC::UnsignedLessThan, builder, stack)
1240        }
1241        Operator::I32LeS | Operator::I64LeS => {
1242            translate_icmp(IntCC::SignedLessThanOrEqual, builder, stack)
1243        }
1244        Operator::I32LeU | Operator::I64LeU => {
1245            translate_icmp(IntCC::UnsignedLessThanOrEqual, builder, stack)
1246        }
1247        Operator::I32GtS | Operator::I64GtS => {
1248            translate_icmp(IntCC::SignedGreaterThan, builder, stack)
1249        }
1250        Operator::I32GtU | Operator::I64GtU => {
1251            translate_icmp(IntCC::UnsignedGreaterThan, builder, stack)
1252        }
1253        Operator::I32GeS | Operator::I64GeS => {
1254            translate_icmp(IntCC::SignedGreaterThanOrEqual, builder, stack)
1255        }
1256        Operator::I32GeU | Operator::I64GeU => {
1257            translate_icmp(IntCC::UnsignedGreaterThanOrEqual, builder, stack)
1258        }
1259        Operator::I32Eqz | Operator::I64Eqz => {
1260            let arg = stack.pop1();
1261            let val = builder.ins().icmp_imm(IntCC::Equal, arg, 0);
1262            stack.push1(builder.ins().uextend(I32, val));
1263        }
1264        Operator::I32Eq | Operator::I64Eq => translate_icmp(IntCC::Equal, builder, stack),
1265        Operator::F32Eq | Operator::F64Eq => translate_fcmp(FloatCC::Equal, builder, stack),
1266        Operator::I32Ne | Operator::I64Ne => translate_icmp(IntCC::NotEqual, builder, stack),
1267        Operator::F32Ne | Operator::F64Ne => translate_fcmp(FloatCC::NotEqual, builder, stack),
1268        Operator::F32Gt | Operator::F64Gt => translate_fcmp(FloatCC::GreaterThan, builder, stack),
1269        Operator::F32Ge | Operator::F64Ge => {
1270            translate_fcmp(FloatCC::GreaterThanOrEqual, builder, stack)
1271        }
1272        Operator::F32Lt | Operator::F64Lt => translate_fcmp(FloatCC::LessThan, builder, stack),
1273        Operator::F32Le | Operator::F64Le => {
1274            translate_fcmp(FloatCC::LessThanOrEqual, builder, stack)
1275        }
1276        Operator::RefNull { hty } => {
1277            let hty = environ.convert_heap_type(*hty)?;
1278            stack.push1(environ.translate_ref_null(builder.cursor(), hty)?)
1279        }
1280        Operator::RefIsNull => {
1281            let value = stack.pop1();
1282            let [WasmValType::Ref(ty)] = operand_types else {
1283                unreachable!("validation")
1284            };
1285            stack.push1(environ.translate_ref_is_null(builder.cursor(), value, *ty)?);
1286        }
1287        Operator::RefFunc { function_index } => {
1288            let index = FuncIndex::from_u32(*function_index);
1289            stack.push1(environ.translate_ref_func(builder.cursor(), index)?);
1290        }
1291        Operator::MemoryAtomicWait32 { memarg } | Operator::MemoryAtomicWait64 { memarg } => {
1292            // The WebAssembly MVP only supports one linear memory and
1293            // wasmparser will ensure that the memory indices specified are
1294            // zero.
1295            let implied_ty = match op {
1296                Operator::MemoryAtomicWait64 { .. } => I64,
1297                Operator::MemoryAtomicWait32 { .. } => I32,
1298                _ => unreachable!(),
1299            };
1300            let memory_index = MemoryIndex::from_u32(memarg.memory);
1301            let heap = environ.get_or_create_heap(builder.func, memory_index);
1302            let timeout = stack.pop1(); // 64 (fixed)
1303            let expected = stack.pop1(); // 32 or 64 (per the `Ixx` in `IxxAtomicWait`)
1304            assert!(builder.func.dfg.value_type(expected) == implied_ty);
1305            let addr = stack.pop1();
1306            let effective_addr = if memarg.offset == 0 {
1307                addr
1308            } else {
1309                let index_type = environ.heaps()[heap].index_type();
1310                let offset = builder.ins().iconst(index_type, memarg.offset as i64);
1311                environ.uadd_overflow_trap(builder, addr, offset, ir::TrapCode::HEAP_OUT_OF_BOUNDS)
1312            };
1313            // `fn translate_atomic_wait` can inspect the type of `expected` to figure out what
1314            // code it needs to generate, if it wants.
1315            let res = environ.translate_atomic_wait(
1316                builder,
1317                memory_index,
1318                heap,
1319                effective_addr,
1320                expected,
1321                timeout,
1322            )?;
1323            stack.push1(res);
1324        }
1325        Operator::MemoryAtomicNotify { memarg } => {
1326            let memory_index = MemoryIndex::from_u32(memarg.memory);
1327            let heap = environ.get_or_create_heap(builder.func, memory_index);
1328            let count = stack.pop1(); // 32 (fixed)
1329            let addr = stack.pop1();
1330            let effective_addr = if memarg.offset == 0 {
1331                addr
1332            } else {
1333                let index_type = environ.heaps()[heap].index_type();
1334                let offset = builder.ins().iconst(index_type, memarg.offset as i64);
1335                environ.uadd_overflow_trap(builder, addr, offset, ir::TrapCode::HEAP_OUT_OF_BOUNDS)
1336            };
1337            let res = environ.translate_atomic_notify(
1338                builder,
1339                memory_index,
1340                heap,
1341                effective_addr,
1342                count,
1343            )?;
1344            stack.push1(res);
1345        }
1346        Operator::I32AtomicLoad { memarg } => {
1347            translate_atomic_load(I32, I32, memarg, builder, stack, environ)?
1348        }
1349        Operator::I64AtomicLoad { memarg } => {
1350            translate_atomic_load(I64, I64, memarg, builder, stack, environ)?
1351        }
1352        Operator::I32AtomicLoad8U { memarg } => {
1353            translate_atomic_load(I32, I8, memarg, builder, stack, environ)?
1354        }
1355        Operator::I32AtomicLoad16U { memarg } => {
1356            translate_atomic_load(I32, I16, memarg, builder, stack, environ)?
1357        }
1358        Operator::I64AtomicLoad8U { memarg } => {
1359            translate_atomic_load(I64, I8, memarg, builder, stack, environ)?
1360        }
1361        Operator::I64AtomicLoad16U { memarg } => {
1362            translate_atomic_load(I64, I16, memarg, builder, stack, environ)?
1363        }
1364        Operator::I64AtomicLoad32U { memarg } => {
1365            translate_atomic_load(I64, I32, memarg, builder, stack, environ)?
1366        }
1367
1368        Operator::I32AtomicStore { memarg } => {
1369            translate_atomic_store(I32, memarg, builder, stack, environ)?
1370        }
1371        Operator::I64AtomicStore { memarg } => {
1372            translate_atomic_store(I64, memarg, builder, stack, environ)?
1373        }
1374        Operator::I32AtomicStore8 { memarg } => {
1375            translate_atomic_store(I8, memarg, builder, stack, environ)?
1376        }
1377        Operator::I32AtomicStore16 { memarg } => {
1378            translate_atomic_store(I16, memarg, builder, stack, environ)?
1379        }
1380        Operator::I64AtomicStore8 { memarg } => {
1381            translate_atomic_store(I8, memarg, builder, stack, environ)?
1382        }
1383        Operator::I64AtomicStore16 { memarg } => {
1384            translate_atomic_store(I16, memarg, builder, stack, environ)?
1385        }
1386        Operator::I64AtomicStore32 { memarg } => {
1387            translate_atomic_store(I32, memarg, builder, stack, environ)?
1388        }
1389
1390        Operator::I32AtomicRmwAdd { memarg } => {
1391            translate_atomic_rmw(I32, I32, AtomicRmwOp::Add, memarg, builder, stack, environ)?
1392        }
1393        Operator::I64AtomicRmwAdd { memarg } => {
1394            translate_atomic_rmw(I64, I64, AtomicRmwOp::Add, memarg, builder, stack, environ)?
1395        }
1396        Operator::I32AtomicRmw8AddU { memarg } => {
1397            translate_atomic_rmw(I32, I8, AtomicRmwOp::Add, memarg, builder, stack, environ)?
1398        }
1399        Operator::I32AtomicRmw16AddU { memarg } => {
1400            translate_atomic_rmw(I32, I16, AtomicRmwOp::Add, memarg, builder, stack, environ)?
1401        }
1402        Operator::I64AtomicRmw8AddU { memarg } => {
1403            translate_atomic_rmw(I64, I8, AtomicRmwOp::Add, memarg, builder, stack, environ)?
1404        }
1405        Operator::I64AtomicRmw16AddU { memarg } => {
1406            translate_atomic_rmw(I64, I16, AtomicRmwOp::Add, memarg, builder, stack, environ)?
1407        }
1408        Operator::I64AtomicRmw32AddU { memarg } => {
1409            translate_atomic_rmw(I64, I32, AtomicRmwOp::Add, memarg, builder, stack, environ)?
1410        }
1411
1412        Operator::I32AtomicRmwSub { memarg } => {
1413            translate_atomic_rmw(I32, I32, AtomicRmwOp::Sub, memarg, builder, stack, environ)?
1414        }
1415        Operator::I64AtomicRmwSub { memarg } => {
1416            translate_atomic_rmw(I64, I64, AtomicRmwOp::Sub, memarg, builder, stack, environ)?
1417        }
1418        Operator::I32AtomicRmw8SubU { memarg } => {
1419            translate_atomic_rmw(I32, I8, AtomicRmwOp::Sub, memarg, builder, stack, environ)?
1420        }
1421        Operator::I32AtomicRmw16SubU { memarg } => {
1422            translate_atomic_rmw(I32, I16, AtomicRmwOp::Sub, memarg, builder, stack, environ)?
1423        }
1424        Operator::I64AtomicRmw8SubU { memarg } => {
1425            translate_atomic_rmw(I64, I8, AtomicRmwOp::Sub, memarg, builder, stack, environ)?
1426        }
1427        Operator::I64AtomicRmw16SubU { memarg } => {
1428            translate_atomic_rmw(I64, I16, AtomicRmwOp::Sub, memarg, builder, stack, environ)?
1429        }
1430        Operator::I64AtomicRmw32SubU { memarg } => {
1431            translate_atomic_rmw(I64, I32, AtomicRmwOp::Sub, memarg, builder, stack, environ)?
1432        }
1433
1434        Operator::I32AtomicRmwAnd { memarg } => {
1435            translate_atomic_rmw(I32, I32, AtomicRmwOp::And, memarg, builder, stack, environ)?
1436        }
1437        Operator::I64AtomicRmwAnd { memarg } => {
1438            translate_atomic_rmw(I64, I64, AtomicRmwOp::And, memarg, builder, stack, environ)?
1439        }
1440        Operator::I32AtomicRmw8AndU { memarg } => {
1441            translate_atomic_rmw(I32, I8, AtomicRmwOp::And, memarg, builder, stack, environ)?
1442        }
1443        Operator::I32AtomicRmw16AndU { memarg } => {
1444            translate_atomic_rmw(I32, I16, AtomicRmwOp::And, memarg, builder, stack, environ)?
1445        }
1446        Operator::I64AtomicRmw8AndU { memarg } => {
1447            translate_atomic_rmw(I64, I8, AtomicRmwOp::And, memarg, builder, stack, environ)?
1448        }
1449        Operator::I64AtomicRmw16AndU { memarg } => {
1450            translate_atomic_rmw(I64, I16, AtomicRmwOp::And, memarg, builder, stack, environ)?
1451        }
1452        Operator::I64AtomicRmw32AndU { memarg } => {
1453            translate_atomic_rmw(I64, I32, AtomicRmwOp::And, memarg, builder, stack, environ)?
1454        }
1455
1456        Operator::I32AtomicRmwOr { memarg } => {
1457            translate_atomic_rmw(I32, I32, AtomicRmwOp::Or, memarg, builder, stack, environ)?
1458        }
1459        Operator::I64AtomicRmwOr { memarg } => {
1460            translate_atomic_rmw(I64, I64, AtomicRmwOp::Or, memarg, builder, stack, environ)?
1461        }
1462        Operator::I32AtomicRmw8OrU { memarg } => {
1463            translate_atomic_rmw(I32, I8, AtomicRmwOp::Or, memarg, builder, stack, environ)?
1464        }
1465        Operator::I32AtomicRmw16OrU { memarg } => {
1466            translate_atomic_rmw(I32, I16, AtomicRmwOp::Or, memarg, builder, stack, environ)?
1467        }
1468        Operator::I64AtomicRmw8OrU { memarg } => {
1469            translate_atomic_rmw(I64, I8, AtomicRmwOp::Or, memarg, builder, stack, environ)?
1470        }
1471        Operator::I64AtomicRmw16OrU { memarg } => {
1472            translate_atomic_rmw(I64, I16, AtomicRmwOp::Or, memarg, builder, stack, environ)?
1473        }
1474        Operator::I64AtomicRmw32OrU { memarg } => {
1475            translate_atomic_rmw(I64, I32, AtomicRmwOp::Or, memarg, builder, stack, environ)?
1476        }
1477
1478        Operator::I32AtomicRmwXor { memarg } => {
1479            translate_atomic_rmw(I32, I32, AtomicRmwOp::Xor, memarg, builder, stack, environ)?
1480        }
1481        Operator::I64AtomicRmwXor { memarg } => {
1482            translate_atomic_rmw(I64, I64, AtomicRmwOp::Xor, memarg, builder, stack, environ)?
1483        }
1484        Operator::I32AtomicRmw8XorU { memarg } => {
1485            translate_atomic_rmw(I32, I8, AtomicRmwOp::Xor, memarg, builder, stack, environ)?
1486        }
1487        Operator::I32AtomicRmw16XorU { memarg } => {
1488            translate_atomic_rmw(I32, I16, AtomicRmwOp::Xor, memarg, builder, stack, environ)?
1489        }
1490        Operator::I64AtomicRmw8XorU { memarg } => {
1491            translate_atomic_rmw(I64, I8, AtomicRmwOp::Xor, memarg, builder, stack, environ)?
1492        }
1493        Operator::I64AtomicRmw16XorU { memarg } => {
1494            translate_atomic_rmw(I64, I16, AtomicRmwOp::Xor, memarg, builder, stack, environ)?
1495        }
1496        Operator::I64AtomicRmw32XorU { memarg } => {
1497            translate_atomic_rmw(I64, I32, AtomicRmwOp::Xor, memarg, builder, stack, environ)?
1498        }
1499
1500        Operator::I32AtomicRmwXchg { memarg } => {
1501            translate_atomic_rmw(I32, I32, AtomicRmwOp::Xchg, memarg, builder, stack, environ)?
1502        }
1503        Operator::I64AtomicRmwXchg { memarg } => {
1504            translate_atomic_rmw(I64, I64, AtomicRmwOp::Xchg, memarg, builder, stack, environ)?
1505        }
1506        Operator::I32AtomicRmw8XchgU { memarg } => {
1507            translate_atomic_rmw(I32, I8, AtomicRmwOp::Xchg, memarg, builder, stack, environ)?
1508        }
1509        Operator::I32AtomicRmw16XchgU { memarg } => {
1510            translate_atomic_rmw(I32, I16, AtomicRmwOp::Xchg, memarg, builder, stack, environ)?
1511        }
1512        Operator::I64AtomicRmw8XchgU { memarg } => {
1513            translate_atomic_rmw(I64, I8, AtomicRmwOp::Xchg, memarg, builder, stack, environ)?
1514        }
1515        Operator::I64AtomicRmw16XchgU { memarg } => {
1516            translate_atomic_rmw(I64, I16, AtomicRmwOp::Xchg, memarg, builder, stack, environ)?
1517        }
1518        Operator::I64AtomicRmw32XchgU { memarg } => {
1519            translate_atomic_rmw(I64, I32, AtomicRmwOp::Xchg, memarg, builder, stack, environ)?
1520        }
1521
1522        Operator::I32AtomicRmwCmpxchg { memarg } => {
1523            translate_atomic_cas(I32, I32, memarg, builder, stack, environ)?
1524        }
1525        Operator::I64AtomicRmwCmpxchg { memarg } => {
1526            translate_atomic_cas(I64, I64, memarg, builder, stack, environ)?
1527        }
1528        Operator::I32AtomicRmw8CmpxchgU { memarg } => {
1529            translate_atomic_cas(I32, I8, memarg, builder, stack, environ)?
1530        }
1531        Operator::I32AtomicRmw16CmpxchgU { memarg } => {
1532            translate_atomic_cas(I32, I16, memarg, builder, stack, environ)?
1533        }
1534        Operator::I64AtomicRmw8CmpxchgU { memarg } => {
1535            translate_atomic_cas(I64, I8, memarg, builder, stack, environ)?
1536        }
1537        Operator::I64AtomicRmw16CmpxchgU { memarg } => {
1538            translate_atomic_cas(I64, I16, memarg, builder, stack, environ)?
1539        }
1540        Operator::I64AtomicRmw32CmpxchgU { memarg } => {
1541            translate_atomic_cas(I64, I32, memarg, builder, stack, environ)?
1542        }
1543
1544        Operator::AtomicFence { .. } => {
1545            builder.ins().fence();
1546        }
1547        Operator::MemoryCopy { src_mem, dst_mem } => {
1548            let src_index = MemoryIndex::from_u32(*src_mem);
1549            let _src_heap = environ.get_or_create_heap(builder.func, src_index);
1550
1551            let dst_index = MemoryIndex::from_u32(*dst_mem);
1552            let _dst_heap = environ.get_or_create_heap(builder.func, dst_index);
1553
1554            let len = stack.pop1();
1555            let src_pos = stack.pop1();
1556            let dst_pos = stack.pop1();
1557            environ.translate_memory_copy(builder, src_index, dst_index, dst_pos, src_pos, len)?;
1558        }
1559        Operator::MemoryFill { mem } => {
1560            let mem = MemoryIndex::from_u32(*mem);
1561            let _heap = environ.get_or_create_heap(builder.func, mem);
1562            let len = stack.pop1();
1563            let val = stack.pop1();
1564            let dest = stack.pop1();
1565            environ.translate_memory_fill(builder, mem, dest, val, len)?;
1566        }
1567        Operator::MemoryInit { data_index, mem } => {
1568            let mem = MemoryIndex::from_u32(*mem);
1569            let _heap = environ.get_or_create_heap(builder.func, mem);
1570            let len = stack.pop1();
1571            let src = stack.pop1();
1572            let dest = stack.pop1();
1573            environ.translate_memory_init(builder, mem, *data_index, dest, src, len)?;
1574        }
1575        Operator::DataDrop { data_index } => {
1576            environ.translate_data_drop(builder.cursor(), *data_index)?;
1577        }
1578        Operator::TableSize { table: index } => {
1579            stack.push1(
1580                environ.translate_table_size(builder.cursor(), TableIndex::from_u32(*index))?,
1581            );
1582        }
1583        Operator::TableGrow { table: index } => {
1584            let table_index = TableIndex::from_u32(*index);
1585            let delta = stack.pop1();
1586            let init_value = stack.pop1();
1587            stack.push1(environ.translate_table_grow(builder, table_index, delta, init_value)?);
1588        }
1589        Operator::TableGet { table: index } => {
1590            let table_index = TableIndex::from_u32(*index);
1591            let index = stack.pop1();
1592            stack.push1(environ.translate_table_get(builder, table_index, index)?);
1593        }
1594        Operator::TableSet { table: index } => {
1595            let table_index = TableIndex::from_u32(*index);
1596            let value = stack.pop1();
1597            let index = stack.pop1();
1598            environ.translate_table_set(builder, table_index, value, index)?;
1599        }
1600        Operator::TableCopy {
1601            dst_table: dst_table_index,
1602            src_table: src_table_index,
1603        } => {
1604            let len = stack.pop1();
1605            let src = stack.pop1();
1606            let dest = stack.pop1();
1607            environ.translate_table_copy(
1608                builder,
1609                TableIndex::from_u32(*dst_table_index),
1610                TableIndex::from_u32(*src_table_index),
1611                dest,
1612                src,
1613                len,
1614            )?;
1615        }
1616        Operator::TableFill { table } => {
1617            let table_index = TableIndex::from_u32(*table);
1618            let len = stack.pop1();
1619            let val = stack.pop1();
1620            let dest = stack.pop1();
1621            environ.translate_table_fill(builder, table_index, dest, val, len)?;
1622        }
1623        Operator::TableInit {
1624            elem_index,
1625            table: table_index,
1626        } => {
1627            let len = stack.pop1();
1628            let src = stack.pop1();
1629            let dest = stack.pop1();
1630            environ.translate_table_init(
1631                builder,
1632                *elem_index,
1633                TableIndex::from_u32(*table_index),
1634                dest,
1635                src,
1636                len,
1637            )?;
1638        }
1639        Operator::ElemDrop { elem_index } => {
1640            environ.translate_elem_drop(builder.cursor(), *elem_index)?;
1641        }
1642        Operator::V128Const { value } => {
1643            let data = value.bytes().to_vec().into();
1644            let handle = builder.func.dfg.constants.insert(data);
1645            let value = builder.ins().vconst(I8X16, handle);
1646            // the v128.const is typed in CLIF as a I8x16 but bitcast to a different type
1647            // before use
1648            stack.push1(value)
1649        }
1650        Operator::I8x16Splat | Operator::I16x8Splat => {
1651            let reduced = builder.ins().ireduce(type_of(op).lane_type(), stack.pop1());
1652            let splatted = builder.ins().splat(type_of(op), reduced);
1653            stack.push1(splatted)
1654        }
1655        Operator::I32x4Splat
1656        | Operator::I64x2Splat
1657        | Operator::F32x4Splat
1658        | Operator::F64x2Splat => {
1659            let splatted = builder.ins().splat(type_of(op), stack.pop1());
1660            stack.push1(splatted)
1661        }
1662        Operator::V128Load8Splat { memarg }
1663        | Operator::V128Load16Splat { memarg }
1664        | Operator::V128Load32Splat { memarg }
1665        | Operator::V128Load64Splat { memarg } => {
1666            unwrap_or_return_unreachable_state!(
1667                stack,
1668                translate_load(
1669                    memarg,
1670                    ir::Opcode::Load,
1671                    type_of(op).lane_type(),
1672                    builder,
1673                    stack,
1674                    environ,
1675                )?
1676            );
1677            let splatted = builder.ins().splat(type_of(op), stack.pop1());
1678            stack.push1(splatted)
1679        }
1680        Operator::V128Load32Zero { memarg } | Operator::V128Load64Zero { memarg } => {
1681            unwrap_or_return_unreachable_state!(
1682                stack,
1683                translate_load(
1684                    memarg,
1685                    ir::Opcode::Load,
1686                    type_of(op).lane_type(),
1687                    builder,
1688                    stack,
1689                    environ,
1690                )?
1691            );
1692            let as_vector = builder.ins().scalar_to_vector(type_of(op), stack.pop1());
1693            stack.push1(as_vector)
1694        }
1695        Operator::V128Load8Lane { memarg, lane }
1696        | Operator::V128Load16Lane { memarg, lane }
1697        | Operator::V128Load32Lane { memarg, lane }
1698        | Operator::V128Load64Lane { memarg, lane } => {
1699            let vector = pop1_with_bitcast(stack, type_of(op), builder);
1700            unwrap_or_return_unreachable_state!(
1701                stack,
1702                translate_load(
1703                    memarg,
1704                    ir::Opcode::Load,
1705                    type_of(op).lane_type(),
1706                    builder,
1707                    stack,
1708                    environ,
1709                )?
1710            );
1711            let replacement = stack.pop1();
1712            stack.push1(builder.ins().insertlane(vector, replacement, *lane))
1713        }
1714        Operator::V128Store8Lane { memarg, lane }
1715        | Operator::V128Store16Lane { memarg, lane }
1716        | Operator::V128Store32Lane { memarg, lane }
1717        | Operator::V128Store64Lane { memarg, lane } => {
1718            let vector = pop1_with_bitcast(stack, type_of(op), builder);
1719            stack.push1(builder.ins().extractlane(vector, *lane));
1720            translate_store(memarg, ir::Opcode::Store, builder, stack, environ)?;
1721        }
1722        Operator::I8x16ExtractLaneS { lane } | Operator::I16x8ExtractLaneS { lane } => {
1723            let vector = pop1_with_bitcast(stack, type_of(op), builder);
1724            let extracted = builder.ins().extractlane(vector, *lane);
1725            stack.push1(builder.ins().sextend(I32, extracted))
1726        }
1727        Operator::I8x16ExtractLaneU { lane } | Operator::I16x8ExtractLaneU { lane } => {
1728            let vector = pop1_with_bitcast(stack, type_of(op), builder);
1729            let extracted = builder.ins().extractlane(vector, *lane);
1730            stack.push1(builder.ins().uextend(I32, extracted));
1731            // On x86, PEXTRB zeroes the upper bits of the destination register of extractlane so
1732            // uextend could be elided; for now, uextend is needed for Cranelift's type checks to
1733            // work.
1734        }
1735        Operator::I32x4ExtractLane { lane }
1736        | Operator::I64x2ExtractLane { lane }
1737        | Operator::F32x4ExtractLane { lane }
1738        | Operator::F64x2ExtractLane { lane } => {
1739            let vector = pop1_with_bitcast(stack, type_of(op), builder);
1740            stack.push1(builder.ins().extractlane(vector, *lane))
1741        }
1742        Operator::I8x16ReplaceLane { lane } | Operator::I16x8ReplaceLane { lane } => {
1743            let (vector, replacement) = stack.pop2();
1744            let ty = type_of(op);
1745            let reduced = builder.ins().ireduce(ty.lane_type(), replacement);
1746            let vector = optionally_bitcast_vector(vector, ty, builder);
1747            stack.push1(builder.ins().insertlane(vector, reduced, *lane))
1748        }
1749        Operator::I32x4ReplaceLane { lane }
1750        | Operator::I64x2ReplaceLane { lane }
1751        | Operator::F32x4ReplaceLane { lane }
1752        | Operator::F64x2ReplaceLane { lane } => {
1753            let (vector, replacement) = stack.pop2();
1754            let vector = optionally_bitcast_vector(vector, type_of(op), builder);
1755            stack.push1(builder.ins().insertlane(vector, replacement, *lane))
1756        }
1757        Operator::I8x16Shuffle { lanes, .. } => {
1758            let (a, b) = pop2_with_bitcast(stack, I8X16, builder);
1759            stack.push1(environ.i8x16_shuffle(builder, a, b, lanes));
1760            // At this point the original types of a and b are lost; users of this value (i.e. this
1761            // WASM-to-CLIF translator) may need to bitcast for type-correctness. This is due
1762            // to WASM using the less specific v128 type for certain operations and more specific
1763            // types (e.g. i8x16) for others.
1764        }
1765        Operator::I8x16Swizzle => {
1766            let (a, b) = pop2_with_bitcast(stack, I8X16, builder);
1767            stack.push1(environ.swizzle(builder, a, b));
1768        }
1769        Operator::I8x16Add | Operator::I16x8Add | Operator::I32x4Add | Operator::I64x2Add => {
1770            let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1771            stack.push1(builder.ins().iadd(a, b))
1772        }
1773        Operator::I8x16AddSatS | Operator::I16x8AddSatS => {
1774            let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1775            stack.push1(builder.ins().sadd_sat(a, b))
1776        }
1777        Operator::I8x16AddSatU | Operator::I16x8AddSatU => {
1778            let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1779            stack.push1(builder.ins().uadd_sat(a, b))
1780        }
1781        Operator::I8x16Sub | Operator::I16x8Sub | Operator::I32x4Sub | Operator::I64x2Sub => {
1782            let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1783            stack.push1(builder.ins().isub(a, b))
1784        }
1785        Operator::I8x16SubSatS | Operator::I16x8SubSatS => {
1786            let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1787            stack.push1(builder.ins().ssub_sat(a, b))
1788        }
1789        Operator::I8x16SubSatU | Operator::I16x8SubSatU => {
1790            let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1791            stack.push1(builder.ins().usub_sat(a, b))
1792        }
1793        Operator::I8x16MinS | Operator::I16x8MinS | Operator::I32x4MinS => {
1794            let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1795            stack.push1(builder.ins().smin(a, b))
1796        }
1797        Operator::I8x16MinU | Operator::I16x8MinU | Operator::I32x4MinU => {
1798            let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1799            stack.push1(builder.ins().umin(a, b))
1800        }
1801        Operator::I8x16MaxS | Operator::I16x8MaxS | Operator::I32x4MaxS => {
1802            let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1803            stack.push1(builder.ins().smax(a, b))
1804        }
1805        Operator::I8x16MaxU | Operator::I16x8MaxU | Operator::I32x4MaxU => {
1806            let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1807            stack.push1(builder.ins().umax(a, b))
1808        }
1809        Operator::I8x16AvgrU | Operator::I16x8AvgrU => {
1810            let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1811            stack.push1(builder.ins().avg_round(a, b))
1812        }
1813        Operator::I8x16Neg | Operator::I16x8Neg | Operator::I32x4Neg | Operator::I64x2Neg => {
1814            let a = pop1_with_bitcast(stack, type_of(op), builder);
1815            stack.push1(builder.ins().ineg(a))
1816        }
1817        Operator::I8x16Abs | Operator::I16x8Abs | Operator::I32x4Abs | Operator::I64x2Abs => {
1818            let a = pop1_with_bitcast(stack, type_of(op), builder);
1819            stack.push1(builder.ins().iabs(a))
1820        }
1821        Operator::I16x8Mul | Operator::I32x4Mul | Operator::I64x2Mul => {
1822            let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1823            stack.push1(builder.ins().imul(a, b))
1824        }
1825        Operator::V128Or => {
1826            let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1827            stack.push1(builder.ins().bor(a, b))
1828        }
1829        Operator::V128Xor => {
1830            let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1831            stack.push1(builder.ins().bxor(a, b))
1832        }
1833        Operator::V128And => {
1834            let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1835            stack.push1(builder.ins().band(a, b))
1836        }
1837        Operator::V128AndNot => {
1838            let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1839            stack.push1(builder.ins().band_not(a, b))
1840        }
1841        Operator::V128Not => {
1842            let a = stack.pop1();
1843            stack.push1(builder.ins().bnot(a));
1844        }
1845        Operator::I8x16Shl | Operator::I16x8Shl | Operator::I32x4Shl | Operator::I64x2Shl => {
1846            let (a, b) = stack.pop2();
1847            let bitcast_a = optionally_bitcast_vector(a, type_of(op), builder);
1848            // The spec expects to shift with `b mod lanewidth`; This is directly compatible
1849            // with cranelift's instruction.
1850            stack.push1(builder.ins().ishl(bitcast_a, b))
1851        }
1852        Operator::I8x16ShrU | Operator::I16x8ShrU | Operator::I32x4ShrU | Operator::I64x2ShrU => {
1853            let (a, b) = stack.pop2();
1854            let bitcast_a = optionally_bitcast_vector(a, type_of(op), builder);
1855            // The spec expects to shift with `b mod lanewidth`; This is directly compatible
1856            // with cranelift's instruction.
1857            stack.push1(builder.ins().ushr(bitcast_a, b))
1858        }
1859        Operator::I8x16ShrS | Operator::I16x8ShrS | Operator::I32x4ShrS | Operator::I64x2ShrS => {
1860            let (a, b) = stack.pop2();
1861            let bitcast_a = optionally_bitcast_vector(a, type_of(op), builder);
1862            // The spec expects to shift with `b mod lanewidth`; This is directly compatible
1863            // with cranelift's instruction.
1864            stack.push1(builder.ins().sshr(bitcast_a, b))
1865        }
1866        Operator::V128Bitselect => {
1867            let (a, b, c) = pop3_with_bitcast(stack, I8X16, builder);
1868            // The CLIF operand ordering is slightly different and the types of all three
1869            // operands must match (hence the bitcast).
1870            stack.push1(builder.ins().bitselect(c, a, b))
1871        }
1872        Operator::V128AnyTrue => {
1873            let a = pop1_with_bitcast(stack, type_of(op), builder);
1874            let bool_result = builder.ins().vany_true(a);
1875            stack.push1(builder.ins().uextend(I32, bool_result))
1876        }
1877        Operator::I8x16AllTrue
1878        | Operator::I16x8AllTrue
1879        | Operator::I32x4AllTrue
1880        | Operator::I64x2AllTrue => {
1881            let a = pop1_with_bitcast(stack, type_of(op), builder);
1882            let bool_result = builder.ins().vall_true(a);
1883            stack.push1(builder.ins().uextend(I32, bool_result))
1884        }
1885        Operator::I8x16Bitmask
1886        | Operator::I16x8Bitmask
1887        | Operator::I32x4Bitmask
1888        | Operator::I64x2Bitmask => {
1889            let a = pop1_with_bitcast(stack, type_of(op), builder);
1890            stack.push1(builder.ins().vhigh_bits(I32, a));
1891        }
1892        Operator::I8x16Eq | Operator::I16x8Eq | Operator::I32x4Eq | Operator::I64x2Eq => {
1893            translate_vector_icmp(IntCC::Equal, type_of(op), builder, stack)
1894        }
1895        Operator::I8x16Ne | Operator::I16x8Ne | Operator::I32x4Ne | Operator::I64x2Ne => {
1896            translate_vector_icmp(IntCC::NotEqual, type_of(op), builder, stack)
1897        }
1898        Operator::I8x16GtS | Operator::I16x8GtS | Operator::I32x4GtS | Operator::I64x2GtS => {
1899            translate_vector_icmp(IntCC::SignedGreaterThan, type_of(op), builder, stack)
1900        }
1901        Operator::I8x16LtS | Operator::I16x8LtS | Operator::I32x4LtS | Operator::I64x2LtS => {
1902            translate_vector_icmp(IntCC::SignedLessThan, type_of(op), builder, stack)
1903        }
1904        Operator::I8x16GtU | Operator::I16x8GtU | Operator::I32x4GtU => {
1905            translate_vector_icmp(IntCC::UnsignedGreaterThan, type_of(op), builder, stack)
1906        }
1907        Operator::I8x16LtU | Operator::I16x8LtU | Operator::I32x4LtU => {
1908            translate_vector_icmp(IntCC::UnsignedLessThan, type_of(op), builder, stack)
1909        }
1910        Operator::I8x16GeS | Operator::I16x8GeS | Operator::I32x4GeS | Operator::I64x2GeS => {
1911            translate_vector_icmp(IntCC::SignedGreaterThanOrEqual, type_of(op), builder, stack)
1912        }
1913        Operator::I8x16LeS | Operator::I16x8LeS | Operator::I32x4LeS | Operator::I64x2LeS => {
1914            translate_vector_icmp(IntCC::SignedLessThanOrEqual, type_of(op), builder, stack)
1915        }
1916        Operator::I8x16GeU | Operator::I16x8GeU | Operator::I32x4GeU => translate_vector_icmp(
1917            IntCC::UnsignedGreaterThanOrEqual,
1918            type_of(op),
1919            builder,
1920            stack,
1921        ),
1922        Operator::I8x16LeU | Operator::I16x8LeU | Operator::I32x4LeU => {
1923            translate_vector_icmp(IntCC::UnsignedLessThanOrEqual, type_of(op), builder, stack)
1924        }
1925        Operator::F32x4Eq | Operator::F64x2Eq => {
1926            translate_vector_fcmp(FloatCC::Equal, type_of(op), builder, stack)
1927        }
1928        Operator::F32x4Ne | Operator::F64x2Ne => {
1929            translate_vector_fcmp(FloatCC::NotEqual, type_of(op), builder, stack)
1930        }
1931        Operator::F32x4Lt | Operator::F64x2Lt => {
1932            translate_vector_fcmp(FloatCC::LessThan, type_of(op), builder, stack)
1933        }
1934        Operator::F32x4Gt | Operator::F64x2Gt => {
1935            translate_vector_fcmp(FloatCC::GreaterThan, type_of(op), builder, stack)
1936        }
1937        Operator::F32x4Le | Operator::F64x2Le => {
1938            translate_vector_fcmp(FloatCC::LessThanOrEqual, type_of(op), builder, stack)
1939        }
1940        Operator::F32x4Ge | Operator::F64x2Ge => {
1941            translate_vector_fcmp(FloatCC::GreaterThanOrEqual, type_of(op), builder, stack)
1942        }
1943        Operator::F32x4Add | Operator::F64x2Add => {
1944            let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1945            stack.push1(builder.ins().fadd(a, b))
1946        }
1947        Operator::F32x4Sub | Operator::F64x2Sub => {
1948            let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1949            stack.push1(builder.ins().fsub(a, b))
1950        }
1951        Operator::F32x4Mul | Operator::F64x2Mul => {
1952            let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1953            stack.push1(builder.ins().fmul(a, b))
1954        }
1955        Operator::F32x4Div | Operator::F64x2Div => {
1956            let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1957            stack.push1(builder.ins().fdiv(a, b))
1958        }
1959        Operator::F32x4Max | Operator::F64x2Max => {
1960            let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1961            stack.push1(builder.ins().fmax(a, b))
1962        }
1963        Operator::F32x4Min | Operator::F64x2Min => {
1964            let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1965            stack.push1(builder.ins().fmin(a, b))
1966        }
1967        Operator::F32x4PMax | Operator::F64x2PMax => {
1968            // Note the careful ordering here with respect to `fcmp` and
1969            // `bitselect`. This matches the spec definition of:
1970            //
1971            //  fpmax(z1, z2) =
1972            //      * If z1 is less than z2 then return z2.
1973            //      * Else return z1.
1974            let ty = type_of(op);
1975            let (a, b) = pop2_with_bitcast(stack, ty, builder);
1976            let cmp = builder.ins().fcmp(FloatCC::LessThan, a, b);
1977            let cmp = optionally_bitcast_vector(cmp, ty, builder);
1978            stack.push1(builder.ins().bitselect(cmp, b, a))
1979        }
1980        Operator::F32x4PMin | Operator::F64x2PMin => {
1981            // Note the careful ordering here which is similar to `pmax` above:
1982            //
1983            //  fpmin(z1, z2) =
1984            //      * If z2 is less than z1 then return z2.
1985            //      * Else return z1.
1986            let ty = type_of(op);
1987            let (a, b) = pop2_with_bitcast(stack, ty, builder);
1988            let cmp = builder.ins().fcmp(FloatCC::LessThan, b, a);
1989            let cmp = optionally_bitcast_vector(cmp, ty, builder);
1990            stack.push1(builder.ins().bitselect(cmp, b, a))
1991        }
1992        Operator::F32x4Sqrt | Operator::F64x2Sqrt => {
1993            let a = pop1_with_bitcast(stack, type_of(op), builder);
1994            stack.push1(builder.ins().sqrt(a))
1995        }
1996        Operator::F32x4Neg | Operator::F64x2Neg => {
1997            let a = pop1_with_bitcast(stack, type_of(op), builder);
1998            stack.push1(builder.ins().fneg(a))
1999        }
2000        Operator::F32x4Abs | Operator::F64x2Abs => {
2001            let a = pop1_with_bitcast(stack, type_of(op), builder);
2002            stack.push1(builder.ins().fabs(a))
2003        }
2004        Operator::F32x4ConvertI32x4S => {
2005            let a = pop1_with_bitcast(stack, I32X4, builder);
2006            stack.push1(builder.ins().fcvt_from_sint(F32X4, a))
2007        }
2008        Operator::F32x4ConvertI32x4U => {
2009            let a = pop1_with_bitcast(stack, I32X4, builder);
2010            stack.push1(builder.ins().fcvt_from_uint(F32X4, a))
2011        }
2012        Operator::F64x2ConvertLowI32x4S => {
2013            let a = pop1_with_bitcast(stack, I32X4, builder);
2014            let widened_a = builder.ins().swiden_low(a);
2015            stack.push1(builder.ins().fcvt_from_sint(F64X2, widened_a));
2016        }
2017        Operator::F64x2ConvertLowI32x4U => {
2018            let a = pop1_with_bitcast(stack, I32X4, builder);
2019            let widened_a = builder.ins().uwiden_low(a);
2020            stack.push1(builder.ins().fcvt_from_uint(F64X2, widened_a));
2021        }
2022        Operator::F64x2PromoteLowF32x4 => {
2023            let a = pop1_with_bitcast(stack, F32X4, builder);
2024            stack.push1(builder.ins().fvpromote_low(a));
2025        }
2026        Operator::F32x4DemoteF64x2Zero => {
2027            let a = pop1_with_bitcast(stack, F64X2, builder);
2028            stack.push1(builder.ins().fvdemote(a));
2029        }
2030        Operator::I32x4TruncSatF32x4S => {
2031            let a = pop1_with_bitcast(stack, F32X4, builder);
2032            stack.push1(builder.ins().fcvt_to_sint_sat(I32X4, a))
2033        }
2034        Operator::I32x4TruncSatF64x2SZero => {
2035            let a = pop1_with_bitcast(stack, F64X2, builder);
2036            let converted_a = builder.ins().fcvt_to_sint_sat(I64X2, a);
2037            let handle = builder.func.dfg.constants.insert(vec![0u8; 16].into());
2038            let zero = builder.ins().vconst(I64X2, handle);
2039
2040            stack.push1(builder.ins().snarrow(converted_a, zero));
2041        }
2042
2043        // FIXME(#5913): the relaxed instructions here are translated the same
2044        // as the saturating instructions, even when the code generator
2045        // configuration allow for different semantics across hosts. On x86,
2046        // however, it's theoretically possible to have a slightly more optimal
2047        // lowering which accounts for NaN differently, although the lowering is
2048        // still not trivial (e.g. one instruction). At this time the
2049        // more-optimal-but-still-large lowering for x86 is not implemented so
2050        // the relaxed instructions are listed here instead of down below with
2051        // the other relaxed instructions. An x86-specific implementation (or
2052        // perhaps for other backends too) should be added and the codegen for
2053        // the relaxed instruction should conditionally be different.
2054        Operator::I32x4RelaxedTruncF32x4U | Operator::I32x4TruncSatF32x4U => {
2055            let a = pop1_with_bitcast(stack, F32X4, builder);
2056            stack.push1(builder.ins().fcvt_to_uint_sat(I32X4, a))
2057        }
2058        Operator::I32x4RelaxedTruncF64x2UZero | Operator::I32x4TruncSatF64x2UZero => {
2059            let a = pop1_with_bitcast(stack, F64X2, builder);
2060            let zero_constant = builder.func.dfg.constants.insert(vec![0u8; 16].into());
2061            let result = if environ.is_x86() && !environ.isa().has_round() {
2062                // On x86 the vector lowering for `fcvt_to_uint_sat` requires
2063                // SSE4.1 `round` instructions. If SSE4.1 isn't available it
2064                // falls back to a libcall which we don't want in Wasmtime.
2065                // Handle this by falling back to the scalar implementation
2066                // which does not require SSE4.1 instructions.
2067                let lane0 = builder.ins().extractlane(a, 0);
2068                let lane1 = builder.ins().extractlane(a, 1);
2069                let lane0_rounded = builder.ins().fcvt_to_uint_sat(I32, lane0);
2070                let lane1_rounded = builder.ins().fcvt_to_uint_sat(I32, lane1);
2071                let result = builder.ins().vconst(I32X4, zero_constant);
2072                let result = builder.ins().insertlane(result, lane0_rounded, 0);
2073                builder.ins().insertlane(result, lane1_rounded, 1)
2074            } else {
2075                let converted_a = builder.ins().fcvt_to_uint_sat(I64X2, a);
2076                let zero = builder.ins().vconst(I64X2, zero_constant);
2077                builder.ins().uunarrow(converted_a, zero)
2078            };
2079            stack.push1(result);
2080        }
2081
2082        Operator::I8x16NarrowI16x8S => {
2083            let (a, b) = pop2_with_bitcast(stack, I16X8, builder);
2084            stack.push1(builder.ins().snarrow(a, b))
2085        }
2086        Operator::I16x8NarrowI32x4S => {
2087            let (a, b) = pop2_with_bitcast(stack, I32X4, builder);
2088            stack.push1(builder.ins().snarrow(a, b))
2089        }
2090        Operator::I8x16NarrowI16x8U => {
2091            let (a, b) = pop2_with_bitcast(stack, I16X8, builder);
2092            stack.push1(builder.ins().unarrow(a, b))
2093        }
2094        Operator::I16x8NarrowI32x4U => {
2095            let (a, b) = pop2_with_bitcast(stack, I32X4, builder);
2096            stack.push1(builder.ins().unarrow(a, b))
2097        }
2098        Operator::I16x8ExtendLowI8x16S => {
2099            let a = pop1_with_bitcast(stack, I8X16, builder);
2100            stack.push1(builder.ins().swiden_low(a))
2101        }
2102        Operator::I16x8ExtendHighI8x16S => {
2103            let a = pop1_with_bitcast(stack, I8X16, builder);
2104            stack.push1(builder.ins().swiden_high(a))
2105        }
2106        Operator::I16x8ExtendLowI8x16U => {
2107            let a = pop1_with_bitcast(stack, I8X16, builder);
2108            stack.push1(builder.ins().uwiden_low(a))
2109        }
2110        Operator::I16x8ExtendHighI8x16U => {
2111            let a = pop1_with_bitcast(stack, I8X16, builder);
2112            stack.push1(builder.ins().uwiden_high(a))
2113        }
2114        Operator::I32x4ExtendLowI16x8S => {
2115            let a = pop1_with_bitcast(stack, I16X8, builder);
2116            stack.push1(builder.ins().swiden_low(a))
2117        }
2118        Operator::I32x4ExtendHighI16x8S => {
2119            let a = pop1_with_bitcast(stack, I16X8, builder);
2120            stack.push1(builder.ins().swiden_high(a))
2121        }
2122        Operator::I32x4ExtendLowI16x8U => {
2123            let a = pop1_with_bitcast(stack, I16X8, builder);
2124            stack.push1(builder.ins().uwiden_low(a))
2125        }
2126        Operator::I32x4ExtendHighI16x8U => {
2127            let a = pop1_with_bitcast(stack, I16X8, builder);
2128            stack.push1(builder.ins().uwiden_high(a))
2129        }
2130        Operator::I64x2ExtendLowI32x4S => {
2131            let a = pop1_with_bitcast(stack, I32X4, builder);
2132            stack.push1(builder.ins().swiden_low(a))
2133        }
2134        Operator::I64x2ExtendHighI32x4S => {
2135            let a = pop1_with_bitcast(stack, I32X4, builder);
2136            stack.push1(builder.ins().swiden_high(a))
2137        }
2138        Operator::I64x2ExtendLowI32x4U => {
2139            let a = pop1_with_bitcast(stack, I32X4, builder);
2140            stack.push1(builder.ins().uwiden_low(a))
2141        }
2142        Operator::I64x2ExtendHighI32x4U => {
2143            let a = pop1_with_bitcast(stack, I32X4, builder);
2144            stack.push1(builder.ins().uwiden_high(a))
2145        }
2146        Operator::I16x8ExtAddPairwiseI8x16S => {
2147            let a = pop1_with_bitcast(stack, I8X16, builder);
2148            let widen_low = builder.ins().swiden_low(a);
2149            let widen_high = builder.ins().swiden_high(a);
2150            stack.push1(builder.ins().iadd_pairwise(widen_low, widen_high));
2151        }
2152        Operator::I32x4ExtAddPairwiseI16x8S => {
2153            let a = pop1_with_bitcast(stack, I16X8, builder);
2154            let widen_low = builder.ins().swiden_low(a);
2155            let widen_high = builder.ins().swiden_high(a);
2156            stack.push1(builder.ins().iadd_pairwise(widen_low, widen_high));
2157        }
2158        Operator::I16x8ExtAddPairwiseI8x16U => {
2159            let a = pop1_with_bitcast(stack, I8X16, builder);
2160            let widen_low = builder.ins().uwiden_low(a);
2161            let widen_high = builder.ins().uwiden_high(a);
2162            stack.push1(builder.ins().iadd_pairwise(widen_low, widen_high));
2163        }
2164        Operator::I32x4ExtAddPairwiseI16x8U => {
2165            let a = pop1_with_bitcast(stack, I16X8, builder);
2166            let widen_low = builder.ins().uwiden_low(a);
2167            let widen_high = builder.ins().uwiden_high(a);
2168            stack.push1(builder.ins().iadd_pairwise(widen_low, widen_high));
2169        }
2170        Operator::F32x4Ceil => {
2171            let arg = pop1_with_bitcast(stack, F32X4, builder);
2172            stack.push1(environ.ceil_f32x4(builder, arg));
2173        }
2174        Operator::F64x2Ceil => {
2175            let arg = pop1_with_bitcast(stack, F64X2, builder);
2176            stack.push1(environ.ceil_f64x2(builder, arg));
2177        }
2178        Operator::F32x4Floor => {
2179            let arg = pop1_with_bitcast(stack, F32X4, builder);
2180            stack.push1(environ.floor_f32x4(builder, arg));
2181        }
2182        Operator::F64x2Floor => {
2183            let arg = pop1_with_bitcast(stack, F64X2, builder);
2184            stack.push1(environ.floor_f64x2(builder, arg));
2185        }
2186        Operator::F32x4Trunc => {
2187            let arg = pop1_with_bitcast(stack, F32X4, builder);
2188            stack.push1(environ.trunc_f32x4(builder, arg));
2189        }
2190        Operator::F64x2Trunc => {
2191            let arg = pop1_with_bitcast(stack, F64X2, builder);
2192            stack.push1(environ.trunc_f64x2(builder, arg));
2193        }
2194        Operator::F32x4Nearest => {
2195            let arg = pop1_with_bitcast(stack, F32X4, builder);
2196            stack.push1(environ.nearest_f32x4(builder, arg));
2197        }
2198        Operator::F64x2Nearest => {
2199            let arg = pop1_with_bitcast(stack, F64X2, builder);
2200            stack.push1(environ.nearest_f64x2(builder, arg));
2201        }
2202        Operator::I32x4DotI16x8S => {
2203            let (a, b) = pop2_with_bitcast(stack, I16X8, builder);
2204            let alow = builder.ins().swiden_low(a);
2205            let blow = builder.ins().swiden_low(b);
2206            let low = builder.ins().imul(alow, blow);
2207            let ahigh = builder.ins().swiden_high(a);
2208            let bhigh = builder.ins().swiden_high(b);
2209            let high = builder.ins().imul(ahigh, bhigh);
2210            stack.push1(builder.ins().iadd_pairwise(low, high));
2211        }
2212        Operator::I8x16Popcnt => {
2213            let arg = pop1_with_bitcast(stack, type_of(op), builder);
2214            stack.push1(builder.ins().popcnt(arg));
2215        }
2216        Operator::I16x8Q15MulrSatS => {
2217            let (a, b) = pop2_with_bitcast(stack, I16X8, builder);
2218            stack.push1(builder.ins().sqmul_round_sat(a, b))
2219        }
2220        Operator::I16x8ExtMulLowI8x16S => {
2221            let (a, b) = pop2_with_bitcast(stack, I8X16, builder);
2222            let a_low = builder.ins().swiden_low(a);
2223            let b_low = builder.ins().swiden_low(b);
2224            stack.push1(builder.ins().imul(a_low, b_low));
2225        }
2226        Operator::I16x8ExtMulHighI8x16S => {
2227            let (a, b) = pop2_with_bitcast(stack, I8X16, builder);
2228            let a_high = builder.ins().swiden_high(a);
2229            let b_high = builder.ins().swiden_high(b);
2230            stack.push1(builder.ins().imul(a_high, b_high));
2231        }
2232        Operator::I16x8ExtMulLowI8x16U => {
2233            let (a, b) = pop2_with_bitcast(stack, I8X16, builder);
2234            let a_low = builder.ins().uwiden_low(a);
2235            let b_low = builder.ins().uwiden_low(b);
2236            stack.push1(builder.ins().imul(a_low, b_low));
2237        }
2238        Operator::I16x8ExtMulHighI8x16U => {
2239            let (a, b) = pop2_with_bitcast(stack, I8X16, builder);
2240            let a_high = builder.ins().uwiden_high(a);
2241            let b_high = builder.ins().uwiden_high(b);
2242            stack.push1(builder.ins().imul(a_high, b_high));
2243        }
2244        Operator::I32x4ExtMulLowI16x8S => {
2245            let (a, b) = pop2_with_bitcast(stack, I16X8, builder);
2246            let a_low = builder.ins().swiden_low(a);
2247            let b_low = builder.ins().swiden_low(b);
2248            stack.push1(builder.ins().imul(a_low, b_low));
2249        }
2250        Operator::I32x4ExtMulHighI16x8S => {
2251            let (a, b) = pop2_with_bitcast(stack, I16X8, builder);
2252            let a_high = builder.ins().swiden_high(a);
2253            let b_high = builder.ins().swiden_high(b);
2254            stack.push1(builder.ins().imul(a_high, b_high));
2255        }
2256        Operator::I32x4ExtMulLowI16x8U => {
2257            let (a, b) = pop2_with_bitcast(stack, I16X8, builder);
2258            let a_low = builder.ins().uwiden_low(a);
2259            let b_low = builder.ins().uwiden_low(b);
2260            stack.push1(builder.ins().imul(a_low, b_low));
2261        }
2262        Operator::I32x4ExtMulHighI16x8U => {
2263            let (a, b) = pop2_with_bitcast(stack, I16X8, builder);
2264            let a_high = builder.ins().uwiden_high(a);
2265            let b_high = builder.ins().uwiden_high(b);
2266            stack.push1(builder.ins().imul(a_high, b_high));
2267        }
2268        Operator::I64x2ExtMulLowI32x4S => {
2269            let (a, b) = pop2_with_bitcast(stack, I32X4, builder);
2270            let a_low = builder.ins().swiden_low(a);
2271            let b_low = builder.ins().swiden_low(b);
2272            stack.push1(builder.ins().imul(a_low, b_low));
2273        }
2274        Operator::I64x2ExtMulHighI32x4S => {
2275            let (a, b) = pop2_with_bitcast(stack, I32X4, builder);
2276            let a_high = builder.ins().swiden_high(a);
2277            let b_high = builder.ins().swiden_high(b);
2278            stack.push1(builder.ins().imul(a_high, b_high));
2279        }
2280        Operator::I64x2ExtMulLowI32x4U => {
2281            let (a, b) = pop2_with_bitcast(stack, I32X4, builder);
2282            let a_low = builder.ins().uwiden_low(a);
2283            let b_low = builder.ins().uwiden_low(b);
2284            stack.push1(builder.ins().imul(a_low, b_low));
2285        }
2286        Operator::I64x2ExtMulHighI32x4U => {
2287            let (a, b) = pop2_with_bitcast(stack, I32X4, builder);
2288            let a_high = builder.ins().uwiden_high(a);
2289            let b_high = builder.ins().uwiden_high(b);
2290            stack.push1(builder.ins().imul(a_high, b_high));
2291        }
2292        Operator::MemoryDiscard { .. } => {
2293            return Err(wasm_unsupported!(
2294                "proposed memory-control operator {:?}",
2295                op
2296            ));
2297        }
2298
2299        Operator::F32x4RelaxedMax | Operator::F64x2RelaxedMax => {
2300            let ty = type_of(op);
2301            let (a, b) = pop2_with_bitcast(stack, ty, builder);
2302            stack.push1(
2303                if environ.relaxed_simd_deterministic() || !environ.is_x86() {
2304                    // Deterministic semantics match the `fmax` instruction, or
2305                    // the `fAAxBB.max` wasm instruction.
2306                    builder.ins().fmax(a, b)
2307                } else {
2308                    // Note that this matches the `pmax` translation which has
2309                    // careful ordering of its operands to trigger
2310                    // pattern-matches in the x86 backend.
2311                    let cmp = builder.ins().fcmp(FloatCC::LessThan, a, b);
2312                    let cmp = optionally_bitcast_vector(cmp, ty, builder);
2313                    builder.ins().bitselect(cmp, b, a)
2314                },
2315            )
2316        }
2317
2318        Operator::F32x4RelaxedMin | Operator::F64x2RelaxedMin => {
2319            let ty = type_of(op);
2320            let (a, b) = pop2_with_bitcast(stack, ty, builder);
2321            stack.push1(
2322                if environ.relaxed_simd_deterministic() || !environ.is_x86() {
2323                    // Deterministic semantics match the `fmin` instruction, or
2324                    // the `fAAxBB.min` wasm instruction.
2325                    builder.ins().fmin(a, b)
2326                } else {
2327                    // Note that this matches the `pmin` translation which has
2328                    // careful ordering of its operands to trigger
2329                    // pattern-matches in the x86 backend.
2330                    let cmp = builder.ins().fcmp(FloatCC::LessThan, b, a);
2331                    let cmp = optionally_bitcast_vector(cmp, ty, builder);
2332                    builder.ins().bitselect(cmp, b, a)
2333                },
2334            );
2335        }
2336
2337        Operator::I8x16RelaxedSwizzle => {
2338            let (a, b) = pop2_with_bitcast(stack, I8X16, builder);
2339            stack.push1(environ.relaxed_swizzle(builder, a, b));
2340        }
2341
2342        Operator::F32x4RelaxedMadd => {
2343            let (a, b, c) = pop3_with_bitcast(stack, type_of(op), builder);
2344            stack.push1(environ.fma_f32x4(builder, a, b, c));
2345        }
2346        Operator::F64x2RelaxedMadd => {
2347            let (a, b, c) = pop3_with_bitcast(stack, type_of(op), builder);
2348            stack.push1(environ.fma_f64x2(builder, a, b, c));
2349        }
2350        Operator::F32x4RelaxedNmadd => {
2351            let (a, b, c) = pop3_with_bitcast(stack, type_of(op), builder);
2352            let a = builder.ins().fneg(a);
2353            stack.push1(environ.fma_f32x4(builder, a, b, c));
2354        }
2355        Operator::F64x2RelaxedNmadd => {
2356            let (a, b, c) = pop3_with_bitcast(stack, type_of(op), builder);
2357            let a = builder.ins().fneg(a);
2358            stack.push1(environ.fma_f64x2(builder, a, b, c));
2359        }
2360
2361        Operator::I8x16RelaxedLaneselect
2362        | Operator::I16x8RelaxedLaneselect
2363        | Operator::I32x4RelaxedLaneselect
2364        | Operator::I64x2RelaxedLaneselect => {
2365            let ty = type_of(op);
2366            let (a, b, c) = pop3_with_bitcast(stack, ty, builder);
2367            // Note that the variable swaps here are intentional due to
2368            // the difference of the order of the wasm op and the clif
2369            // op.
2370            stack.push1(
2371                if environ.relaxed_simd_deterministic()
2372                    || !environ.use_x86_blendv_for_relaxed_laneselect(ty)
2373                {
2374                    // Deterministic semantics are a `bitselect` along the lines
2375                    // of the wasm `v128.bitselect` instruction.
2376                    builder.ins().bitselect(c, a, b)
2377                } else {
2378                    builder.ins().x86_blendv(c, a, b)
2379                },
2380            );
2381        }
2382
2383        Operator::I32x4RelaxedTruncF32x4S => {
2384            let a = pop1_with_bitcast(stack, F32X4, builder);
2385            stack.push1(
2386                if environ.relaxed_simd_deterministic() || !environ.is_x86() {
2387                    // Deterministic semantics are to match the
2388                    // `i32x4.trunc_sat_f32x4_s` instruction.
2389                    builder.ins().fcvt_to_sint_sat(I32X4, a)
2390                } else {
2391                    builder.ins().x86_cvtt2dq(I32X4, a)
2392                },
2393            )
2394        }
2395        Operator::I32x4RelaxedTruncF64x2SZero => {
2396            let a = pop1_with_bitcast(stack, F64X2, builder);
2397            let converted_a = if environ.relaxed_simd_deterministic() || !environ.is_x86() {
2398                // Deterministic semantics are to match the
2399                // `i32x4.trunc_sat_f64x2_s_zero` instruction.
2400                builder.ins().fcvt_to_sint_sat(I64X2, a)
2401            } else {
2402                builder.ins().x86_cvtt2dq(I64X2, a)
2403            };
2404            let handle = builder.func.dfg.constants.insert(vec![0u8; 16].into());
2405            let zero = builder.ins().vconst(I64X2, handle);
2406
2407            stack.push1(builder.ins().snarrow(converted_a, zero));
2408        }
2409        Operator::I16x8RelaxedQ15mulrS => {
2410            let (a, b) = pop2_with_bitcast(stack, I16X8, builder);
2411            stack.push1(
2412                if environ.relaxed_simd_deterministic()
2413                    || !environ.use_x86_pmulhrsw_for_relaxed_q15mul()
2414                {
2415                    // Deterministic semantics are to match the
2416                    // `i16x8.q15mulr_sat_s` instruction.
2417                    builder.ins().sqmul_round_sat(a, b)
2418                } else {
2419                    builder.ins().x86_pmulhrsw(a, b)
2420                },
2421            );
2422        }
2423        Operator::I16x8RelaxedDotI8x16I7x16S => {
2424            let (a, b) = pop2_with_bitcast(stack, I8X16, builder);
2425            stack.push1(
2426                if environ.relaxed_simd_deterministic() || !environ.use_x86_pmaddubsw_for_dot() {
2427                    // Deterministic semantics are to treat both operands as
2428                    // signed integers and perform the dot product.
2429                    let alo = builder.ins().swiden_low(a);
2430                    let blo = builder.ins().swiden_low(b);
2431                    let lo = builder.ins().imul(alo, blo);
2432                    let ahi = builder.ins().swiden_high(a);
2433                    let bhi = builder.ins().swiden_high(b);
2434                    let hi = builder.ins().imul(ahi, bhi);
2435                    builder.ins().iadd_pairwise(lo, hi)
2436                } else {
2437                    builder.ins().x86_pmaddubsw(a, b)
2438                },
2439            );
2440        }
2441
2442        Operator::I32x4RelaxedDotI8x16I7x16AddS => {
2443            let c = pop1_with_bitcast(stack, I32X4, builder);
2444            let (a, b) = pop2_with_bitcast(stack, I8X16, builder);
2445            let dot =
2446                if environ.relaxed_simd_deterministic() || !environ.use_x86_pmaddubsw_for_dot() {
2447                    // Deterministic semantics are to treat both operands as
2448                    // signed integers and perform the dot product.
2449                    let alo = builder.ins().swiden_low(a);
2450                    let blo = builder.ins().swiden_low(b);
2451                    let lo = builder.ins().imul(alo, blo);
2452                    let ahi = builder.ins().swiden_high(a);
2453                    let bhi = builder.ins().swiden_high(b);
2454                    let hi = builder.ins().imul(ahi, bhi);
2455                    builder.ins().iadd_pairwise(lo, hi)
2456                } else {
2457                    builder.ins().x86_pmaddubsw(a, b)
2458                };
2459            let dotlo = builder.ins().swiden_low(dot);
2460            let dothi = builder.ins().swiden_high(dot);
2461            let dot32 = builder.ins().iadd_pairwise(dotlo, dothi);
2462            stack.push1(builder.ins().iadd(dot32, c));
2463        }
2464
2465        Operator::BrOnNull { relative_depth } => {
2466            let r = stack.pop1();
2467            let [.., WasmValType::Ref(r_ty)] = operand_types else {
2468                unreachable!("validation")
2469            };
2470            let (br_destination, inputs) = translate_br_if_args(*relative_depth, stack);
2471            let is_null = environ.translate_ref_is_null(builder.cursor(), r, *r_ty)?;
2472            let else_block = builder.create_block();
2473            canonicalise_brif(builder, is_null, br_destination, inputs, else_block, &[]);
2474
2475            builder.seal_block(else_block); // The only predecessor is the current block.
2476            builder.switch_to_block(else_block);
2477            stack.push1(r);
2478        }
2479        Operator::BrOnNonNull { relative_depth } => {
2480            // We write this a bit differently from the spec to avoid an extra
2481            // block/branch and the typed accounting thereof. Instead of the
2482            // spec's approach, it's described as such:
2483            // Peek the value val from the stack.
2484            // If val is ref.null ht, then: pop the value val from the stack.
2485            // Else: Execute the instruction (br relative_depth).
2486            let r = stack.peek1();
2487            let [.., WasmValType::Ref(r_ty)] = operand_types else {
2488                unreachable!("validation")
2489            };
2490            let is_null = environ.translate_ref_is_null(builder.cursor(), r, *r_ty)?;
2491            let (br_destination, inputs) = translate_br_if_args(*relative_depth, stack);
2492            let else_block = builder.create_block();
2493            canonicalise_brif(builder, is_null, else_block, &[], br_destination, inputs);
2494
2495            // In the null case, pop the ref
2496            stack.pop1();
2497
2498            builder.seal_block(else_block); // The only predecessor is the current block.
2499
2500            // The rest of the translation operates on our is null case, which is
2501            // currently an empty block
2502            builder.switch_to_block(else_block);
2503        }
2504        Operator::CallRef { type_index } => {
2505            // Get function signature
2506            // `index` is the index of the function's signature and `table_index` is the index of
2507            // the table to search the function in.
2508            let type_index = TypeIndex::from_u32(*type_index);
2509            let sigref = environ.get_or_create_sig_ref(builder.func, type_index);
2510            let num_args = environ.num_params_for_function_type(type_index);
2511            let callee = stack.pop1();
2512
2513            // Bitcast any vector arguments to their default type, I8X16, before calling.
2514            let args = stack.peekn_mut(num_args);
2515            bitcast_wasm_params(environ, sigref, args, builder);
2516
2517            let inst_results = environ.translate_call_ref(
2518                builder,
2519                sigref,
2520                callee,
2521                stack.peekn(num_args),
2522                stack.handlers.handlers(),
2523            )?;
2524
2525            debug_assert_eq!(
2526                inst_results.len(),
2527                builder.func.dfg.signatures[sigref].returns.len(),
2528                "translate_call_ref results should match the call signature"
2529            );
2530            stack.popn(num_args);
2531            stack.pushn(&inst_results);
2532        }
2533        Operator::RefAsNonNull => {
2534            let r = stack.pop1();
2535            let [.., WasmValType::Ref(r_ty)] = operand_types else {
2536                unreachable!("validation")
2537            };
2538            let is_null = environ.translate_ref_is_null(builder.cursor(), r, *r_ty)?;
2539            environ.trapnz(builder, is_null, crate::TRAP_NULL_REFERENCE);
2540            stack.push1(r);
2541        }
2542
2543        Operator::RefI31 => {
2544            let val = stack.pop1();
2545            let i31ref = environ.translate_ref_i31(builder.cursor(), val)?;
2546            stack.push1(i31ref);
2547        }
2548        Operator::I31GetS => {
2549            let i31ref = stack.pop1();
2550            let val = environ.translate_i31_get_s(builder, i31ref)?;
2551            stack.push1(val);
2552        }
2553        Operator::I31GetU => {
2554            let i31ref = stack.pop1();
2555            let val = environ.translate_i31_get_u(builder, i31ref)?;
2556            stack.push1(val);
2557        }
2558
2559        Operator::StructNew { struct_type_index } => {
2560            let struct_type_index = TypeIndex::from_u32(*struct_type_index);
2561            let arity = environ.struct_fields_len(struct_type_index)?;
2562            let fields: StructFieldsVec = stack.peekn(arity).iter().copied().collect();
2563            stack.popn(arity);
2564            let struct_ref = environ.translate_struct_new(builder, struct_type_index, fields)?;
2565            stack.push1(struct_ref);
2566        }
2567
2568        Operator::StructNewDefault { struct_type_index } => {
2569            let struct_type_index = TypeIndex::from_u32(*struct_type_index);
2570            let struct_ref = environ.translate_struct_new_default(builder, struct_type_index)?;
2571            stack.push1(struct_ref);
2572        }
2573
2574        Operator::StructSet {
2575            struct_type_index,
2576            field_index,
2577        } => {
2578            let struct_type_index = TypeIndex::from_u32(*struct_type_index);
2579            let val = stack.pop1();
2580            let struct_ref = stack.pop1();
2581            environ.translate_struct_set(
2582                builder,
2583                struct_type_index,
2584                *field_index,
2585                struct_ref,
2586                val,
2587            )?;
2588        }
2589
2590        Operator::StructGetS {
2591            struct_type_index,
2592            field_index,
2593        } => {
2594            let struct_type_index = TypeIndex::from_u32(*struct_type_index);
2595            let struct_ref = stack.pop1();
2596            let val = environ.translate_struct_get(
2597                builder,
2598                struct_type_index,
2599                *field_index,
2600                struct_ref,
2601                Some(Extension::Sign),
2602            )?;
2603            stack.push1(val);
2604        }
2605
2606        Operator::StructGetU {
2607            struct_type_index,
2608            field_index,
2609        } => {
2610            let struct_type_index = TypeIndex::from_u32(*struct_type_index);
2611            let struct_ref = stack.pop1();
2612            let val = environ.translate_struct_get(
2613                builder,
2614                struct_type_index,
2615                *field_index,
2616                struct_ref,
2617                Some(Extension::Zero),
2618            )?;
2619            stack.push1(val);
2620        }
2621
2622        Operator::StructGet {
2623            struct_type_index,
2624            field_index,
2625        } => {
2626            let struct_type_index = TypeIndex::from_u32(*struct_type_index);
2627            let struct_ref = stack.pop1();
2628            let val = environ.translate_struct_get(
2629                builder,
2630                struct_type_index,
2631                *field_index,
2632                struct_ref,
2633                None,
2634            )?;
2635            stack.push1(val);
2636        }
2637
2638        Operator::ArrayNew { array_type_index } => {
2639            let array_type_index = TypeIndex::from_u32(*array_type_index);
2640            let (elem, len) = stack.pop2();
2641            let array_ref = environ.translate_array_new(builder, array_type_index, elem, len)?;
2642            stack.push1(array_ref);
2643        }
2644        Operator::ArrayNewDefault { array_type_index } => {
2645            let array_type_index = TypeIndex::from_u32(*array_type_index);
2646            let len = stack.pop1();
2647            let array_ref = environ.translate_array_new_default(builder, array_type_index, len)?;
2648            stack.push1(array_ref);
2649        }
2650        Operator::ArrayNewFixed {
2651            array_type_index,
2652            array_size,
2653        } => {
2654            let array_type_index = TypeIndex::from_u32(*array_type_index);
2655            let array_size = usize::try_from(*array_size).unwrap();
2656            let elems = stack.peekn(array_size);
2657            let array_ref = environ.translate_array_new_fixed(builder, array_type_index, elems)?;
2658            stack.popn(array_size);
2659            stack.push1(array_ref);
2660        }
2661        Operator::ArrayNewData {
2662            array_type_index,
2663            array_data_index,
2664        } => {
2665            let array_type_index = TypeIndex::from_u32(*array_type_index);
2666            let array_data_index = DataIndex::from_u32(*array_data_index);
2667            let (data_offset, len) = stack.pop2();
2668            let array_ref = environ.translate_array_new_data(
2669                builder,
2670                array_type_index,
2671                array_data_index,
2672                data_offset,
2673                len,
2674            )?;
2675            stack.push1(array_ref);
2676        }
2677        Operator::ArrayNewElem {
2678            array_type_index,
2679            array_elem_index,
2680        } => {
2681            let array_type_index = TypeIndex::from_u32(*array_type_index);
2682            let array_elem_index = ElemIndex::from_u32(*array_elem_index);
2683            let (elem_offset, len) = stack.pop2();
2684            let array_ref = environ.translate_array_new_elem(
2685                builder,
2686                array_type_index,
2687                array_elem_index,
2688                elem_offset,
2689                len,
2690            )?;
2691            stack.push1(array_ref);
2692        }
2693        Operator::ArrayCopy {
2694            array_type_index_dst,
2695            array_type_index_src,
2696        } => {
2697            let array_type_index_dst = TypeIndex::from_u32(*array_type_index_dst);
2698            let array_type_index_src = TypeIndex::from_u32(*array_type_index_src);
2699            let (dst_array, dst_index, src_array, src_index, len) = stack.pop5();
2700            environ.translate_array_copy(
2701                builder,
2702                array_type_index_dst,
2703                dst_array,
2704                dst_index,
2705                array_type_index_src,
2706                src_array,
2707                src_index,
2708                len,
2709            )?;
2710        }
2711        Operator::ArrayFill { array_type_index } => {
2712            let array_type_index = TypeIndex::from_u32(*array_type_index);
2713            let (array, index, val, len) = stack.pop4();
2714            environ.translate_array_fill(builder, array_type_index, array, index, val, len)?;
2715        }
2716        Operator::ArrayInitData {
2717            array_type_index,
2718            array_data_index,
2719        } => {
2720            let array_type_index = TypeIndex::from_u32(*array_type_index);
2721            let array_data_index = DataIndex::from_u32(*array_data_index);
2722            let (array, dst_index, src_index, len) = stack.pop4();
2723            environ.translate_array_init_data(
2724                builder,
2725                array_type_index,
2726                array,
2727                dst_index,
2728                array_data_index,
2729                src_index,
2730                len,
2731            )?;
2732        }
2733        Operator::ArrayInitElem {
2734            array_type_index,
2735            array_elem_index,
2736        } => {
2737            let array_type_index = TypeIndex::from_u32(*array_type_index);
2738            let array_elem_index = ElemIndex::from_u32(*array_elem_index);
2739            let (array, dst_index, src_index, len) = stack.pop4();
2740            environ.translate_array_init_elem(
2741                builder,
2742                array_type_index,
2743                array,
2744                dst_index,
2745                array_elem_index,
2746                src_index,
2747                len,
2748            )?;
2749        }
2750        Operator::ArrayLen => {
2751            let array = stack.pop1();
2752            let len = environ.translate_array_len(builder, array)?;
2753            stack.push1(len);
2754        }
2755        Operator::ArrayGet { array_type_index } => {
2756            let array_type_index = TypeIndex::from_u32(*array_type_index);
2757            let (array, index) = stack.pop2();
2758            let elem =
2759                environ.translate_array_get(builder, array_type_index, array, index, None)?;
2760            stack.push1(elem);
2761        }
2762        Operator::ArrayGetS { array_type_index } => {
2763            let array_type_index = TypeIndex::from_u32(*array_type_index);
2764            let (array, index) = stack.pop2();
2765            let elem = environ.translate_array_get(
2766                builder,
2767                array_type_index,
2768                array,
2769                index,
2770                Some(Extension::Sign),
2771            )?;
2772            stack.push1(elem);
2773        }
2774        Operator::ArrayGetU { array_type_index } => {
2775            let array_type_index = TypeIndex::from_u32(*array_type_index);
2776            let (array, index) = stack.pop2();
2777            let elem = environ.translate_array_get(
2778                builder,
2779                array_type_index,
2780                array,
2781                index,
2782                Some(Extension::Zero),
2783            )?;
2784            stack.push1(elem);
2785        }
2786        Operator::ArraySet { array_type_index } => {
2787            let array_type_index = TypeIndex::from_u32(*array_type_index);
2788            let (array, index, elem) = stack.pop3();
2789            environ.translate_array_set(builder, array_type_index, array, index, elem)?;
2790        }
2791        Operator::RefEq => {
2792            let (r1, r2) = stack.pop2();
2793            let eq = builder.ins().icmp(ir::condcodes::IntCC::Equal, r1, r2);
2794            let eq = builder.ins().uextend(ir::types::I32, eq);
2795            stack.push1(eq);
2796        }
2797        Operator::RefTestNonNull { hty } => {
2798            let r = stack.pop1();
2799            let [.., WasmValType::Ref(r_ty)] = operand_types else {
2800                unreachable!("validation")
2801            };
2802            let heap_type = environ.convert_heap_type(*hty)?;
2803            let result = environ.translate_ref_test(
2804                builder,
2805                WasmRefType {
2806                    heap_type,
2807                    nullable: false,
2808                },
2809                r,
2810                *r_ty,
2811            )?;
2812            stack.push1(result);
2813        }
2814        Operator::RefTestNullable { hty } => {
2815            let r = stack.pop1();
2816            let [.., WasmValType::Ref(r_ty)] = operand_types else {
2817                unreachable!("validation")
2818            };
2819            let heap_type = environ.convert_heap_type(*hty)?;
2820            let result = environ.translate_ref_test(
2821                builder,
2822                WasmRefType {
2823                    heap_type,
2824                    nullable: true,
2825                },
2826                r,
2827                *r_ty,
2828            )?;
2829            stack.push1(result);
2830        }
2831        Operator::RefCastNonNull { hty } => {
2832            let r = stack.pop1();
2833            let [.., WasmValType::Ref(r_ty)] = operand_types else {
2834                unreachable!("validation")
2835            };
2836            let heap_type = environ.convert_heap_type(*hty)?;
2837            let cast_okay = environ.translate_ref_test(
2838                builder,
2839                WasmRefType {
2840                    heap_type,
2841                    nullable: false,
2842                },
2843                r,
2844                *r_ty,
2845            )?;
2846            environ.trapz(builder, cast_okay, crate::TRAP_CAST_FAILURE);
2847            stack.push1(r);
2848        }
2849        Operator::RefCastNullable { hty } => {
2850            let r = stack.pop1();
2851            let [.., WasmValType::Ref(r_ty)] = operand_types else {
2852                unreachable!("validation")
2853            };
2854            let heap_type = environ.convert_heap_type(*hty)?;
2855            let cast_okay = environ.translate_ref_test(
2856                builder,
2857                WasmRefType {
2858                    heap_type,
2859                    nullable: true,
2860                },
2861                r,
2862                *r_ty,
2863            )?;
2864            environ.trapz(builder, cast_okay, crate::TRAP_CAST_FAILURE);
2865            stack.push1(r);
2866        }
2867        Operator::BrOnCast {
2868            relative_depth,
2869            to_ref_type,
2870            from_ref_type: _,
2871        } => {
2872            let r = stack.peek1();
2873            let [.., WasmValType::Ref(r_ty)] = operand_types else {
2874                unreachable!("validation")
2875            };
2876
2877            let to_ref_type = environ.convert_ref_type(*to_ref_type)?;
2878            let cast_is_okay = environ.translate_ref_test(builder, to_ref_type, r, *r_ty)?;
2879
2880            let (cast_succeeds_block, inputs) = translate_br_if_args(*relative_depth, stack);
2881            let cast_fails_block = builder.create_block();
2882            canonicalise_brif(
2883                builder,
2884                cast_is_okay,
2885                cast_succeeds_block,
2886                inputs,
2887                cast_fails_block,
2888                &[
2889                    // NB: the `cast_fails_block` is dominated by the current
2890                    // block, and therefore doesn't need any block params.
2891                ],
2892            );
2893
2894            // The only predecessor is the current block.
2895            builder.seal_block(cast_fails_block);
2896
2897            // The next Wasm instruction is executed when the cast failed and we
2898            // did not branch away.
2899            builder.switch_to_block(cast_fails_block);
2900        }
2901        Operator::BrOnCastFail {
2902            relative_depth,
2903            to_ref_type,
2904            from_ref_type: _,
2905        } => {
2906            let r = stack.peek1();
2907            let [.., WasmValType::Ref(r_ty)] = operand_types else {
2908                unreachable!("validation")
2909            };
2910
2911            let to_ref_type = environ.convert_ref_type(*to_ref_type)?;
2912            let cast_is_okay = environ.translate_ref_test(builder, to_ref_type, r, *r_ty)?;
2913
2914            let (cast_fails_block, inputs) = translate_br_if_args(*relative_depth, stack);
2915            let cast_succeeds_block = builder.create_block();
2916            canonicalise_brif(
2917                builder,
2918                cast_is_okay,
2919                cast_succeeds_block,
2920                &[
2921                    // NB: the `cast_succeeds_block` is dominated by the current
2922                    // block, and therefore doesn't need any block params.
2923                ],
2924                cast_fails_block,
2925                inputs,
2926            );
2927
2928            // The only predecessor is the current block.
2929            builder.seal_block(cast_succeeds_block);
2930
2931            // The next Wasm instruction is executed when the cast succeeded and
2932            // we did not branch away.
2933            builder.switch_to_block(cast_succeeds_block);
2934        }
2935
2936        Operator::AnyConvertExtern => {
2937            // Pop an `externref`, push an `anyref`. But they have the same
2938            // representation, so we don't actually need to do anything.
2939        }
2940        Operator::ExternConvertAny => {
2941            // Pop an `anyref`, push an `externref`. But they have the same
2942            // representation, so we don't actually need to do anything.
2943        }
2944
2945        Operator::ContNew { cont_type_index } => {
2946            let cont_type_index = TypeIndex::from_u32(*cont_type_index);
2947            let arg_types: SmallVec<[_; 8]> = environ
2948                .continuation_arguments(cont_type_index)
2949                .to_smallvec();
2950            let result_types: SmallVec<[_; 8]> =
2951                environ.continuation_returns(cont_type_index).to_smallvec();
2952            let r = stack.pop1();
2953            let contobj = environ.translate_cont_new(builder, r, &arg_types, &result_types)?;
2954            stack.push1(contobj);
2955        }
2956        Operator::ContBind {
2957            argument_index,
2958            result_index,
2959        } => {
2960            let src_types = environ.continuation_arguments(TypeIndex::from_u32(*argument_index));
2961            let dst_arity = environ
2962                .continuation_arguments(TypeIndex::from_u32(*result_index))
2963                .len();
2964            let arg_count = src_types.len() - dst_arity;
2965
2966            let arg_types = &src_types[0..arg_count];
2967            for arg_type in arg_types {
2968                // We can't bind GC objects using cont.bind at the moment: We
2969                // don't have the necessary infrastructure to traverse the
2970                // buffers used by cont.bind when looking for GC roots. Thus,
2971                // this crude check ensures that these buffers can never contain
2972                // GC roots to begin with.
2973                if arg_type.is_vmgcref_type_and_not_i31() {
2974                    return Err(wasmtime_environ::WasmError::Unsupported(
2975                        "cont.bind does not support GC types at the moment".into(),
2976                    ));
2977                }
2978            }
2979
2980            let (original_contobj, args) = stack.peekn(arg_count + 1).split_last().unwrap();
2981
2982            let new_contobj = environ.translate_cont_bind(builder, *original_contobj, args);
2983
2984            stack.popn(arg_count + 1);
2985            stack.push1(new_contobj);
2986        }
2987        Operator::Suspend { tag_index } => {
2988            let tag_index = TagIndex::from_u32(*tag_index);
2989            let param_types = environ.tag_params(tag_index).to_vec();
2990            let return_types: SmallVec<[_; 8]> = environ
2991                .tag_returns(tag_index)
2992                .iter()
2993                .map(|ty| crate::value_type(environ.isa(), *ty))
2994                .collect();
2995
2996            let params = stack.peekn(param_types.len());
2997            let param_count = params.len();
2998
2999            let return_values =
3000                environ.translate_suspend(builder, tag_index.as_u32(), params, &return_types);
3001
3002            stack.popn(param_count);
3003            stack.pushn(&return_values);
3004        }
3005        Operator::Resume {
3006            cont_type_index,
3007            resume_table: wasm_resume_table,
3008        } => {
3009            // We translate the block indices in the wasm resume_table to actual Blocks.
3010            let mut clif_resume_table = vec![];
3011            for handle in &wasm_resume_table.handlers {
3012                match handle {
3013                    wasmparser::Handle::OnLabel { tag, label } => {
3014                        let i = stack.control_stack.len() - 1 - (*label as usize);
3015                        let frame = &mut stack.control_stack[i];
3016                        // This is side-effecting!
3017                        frame.set_branched_to_exit();
3018                        clif_resume_table.push((*tag, Some(frame.br_destination())));
3019                    }
3020                    wasmparser::Handle::OnSwitch { tag } => {
3021                        clif_resume_table.push((*tag, None));
3022                    }
3023                }
3024            }
3025
3026            let cont_type_index = TypeIndex::from_u32(*cont_type_index);
3027            let arity = environ.continuation_arguments(cont_type_index).len();
3028            let (contobj, call_args) = stack.peekn(arity + 1).split_last().unwrap();
3029
3030            let cont_return_vals = environ.translate_resume(
3031                builder,
3032                cont_type_index.as_u32(),
3033                *contobj,
3034                call_args,
3035                &clif_resume_table,
3036            )?;
3037
3038            stack.popn(arity + 1); // arguments + continuation
3039            stack.pushn(&cont_return_vals);
3040        }
3041        Operator::ResumeThrow {
3042            cont_type_index: _,
3043            tag_index: _,
3044            resume_table: _,
3045        } => {
3046            // TODO(10248) This depends on exception handling
3047            return Err(wasmtime_environ::WasmError::Unsupported(
3048                "resume.throw instructions not supported, yet".to_string(),
3049            ));
3050        }
3051        Operator::Switch {
3052            cont_type_index,
3053            tag_index,
3054        } => {
3055            // Arguments of the continuation we are going to switch to
3056            let continuation_argument_types: SmallVec<[_; 8]> = environ
3057                .continuation_arguments(TypeIndex::from_u32(*cont_type_index))
3058                .to_smallvec();
3059            // Arity includes the continuation argument
3060            let arity = continuation_argument_types.len();
3061            let (contobj, switch_args) = stack.peekn(arity).split_last().unwrap();
3062
3063            // Type of the continuation we are going to create by suspending the
3064            // currently running stack
3065            let current_continuation_type = continuation_argument_types.last().unwrap();
3066            let current_continuation_type = current_continuation_type.unwrap_ref_type();
3067
3068            // Argument types of current_continuation_type. These will in turn
3069            // be the types of the arguments we receive when someone switches
3070            // back to this switch instruction
3071            let current_continuation_arg_types: SmallVec<[_; 8]> =
3072                match current_continuation_type.heap_type {
3073                    WasmHeapType::ConcreteCont(index) => {
3074                        let mti = index
3075                            .as_module_type_index()
3076                            .expect("Only supporting module type indices on switch for now");
3077
3078                        environ
3079                            .continuation_arguments(TypeIndex::from_u32(mti.as_u32()))
3080                            .iter()
3081                            .map(|ty| crate::value_type(environ.isa(), *ty))
3082                            .collect()
3083                    }
3084                    _ => panic!("Invalid type on switch"),
3085                };
3086
3087            let switch_return_values = environ.translate_switch(
3088                builder,
3089                *tag_index,
3090                *contobj,
3091                switch_args,
3092                &current_continuation_arg_types,
3093            )?;
3094
3095            stack.popn(arity);
3096            stack.pushn(&switch_return_values)
3097        }
3098
3099        Operator::GlobalAtomicGet { .. }
3100        | Operator::GlobalAtomicSet { .. }
3101        | Operator::GlobalAtomicRmwAdd { .. }
3102        | Operator::GlobalAtomicRmwSub { .. }
3103        | Operator::GlobalAtomicRmwOr { .. }
3104        | Operator::GlobalAtomicRmwXor { .. }
3105        | Operator::GlobalAtomicRmwAnd { .. }
3106        | Operator::GlobalAtomicRmwXchg { .. }
3107        | Operator::GlobalAtomicRmwCmpxchg { .. }
3108        | Operator::TableAtomicGet { .. }
3109        | Operator::TableAtomicSet { .. }
3110        | Operator::TableAtomicRmwXchg { .. }
3111        | Operator::TableAtomicRmwCmpxchg { .. }
3112        | Operator::StructAtomicGet { .. }
3113        | Operator::StructAtomicGetS { .. }
3114        | Operator::StructAtomicGetU { .. }
3115        | Operator::StructAtomicSet { .. }
3116        | Operator::StructAtomicRmwAdd { .. }
3117        | Operator::StructAtomicRmwSub { .. }
3118        | Operator::StructAtomicRmwOr { .. }
3119        | Operator::StructAtomicRmwXor { .. }
3120        | Operator::StructAtomicRmwAnd { .. }
3121        | Operator::StructAtomicRmwXchg { .. }
3122        | Operator::StructAtomicRmwCmpxchg { .. }
3123        | Operator::ArrayAtomicGet { .. }
3124        | Operator::ArrayAtomicGetS { .. }
3125        | Operator::ArrayAtomicGetU { .. }
3126        | Operator::ArrayAtomicSet { .. }
3127        | Operator::ArrayAtomicRmwAdd { .. }
3128        | Operator::ArrayAtomicRmwSub { .. }
3129        | Operator::ArrayAtomicRmwOr { .. }
3130        | Operator::ArrayAtomicRmwXor { .. }
3131        | Operator::ArrayAtomicRmwAnd { .. }
3132        | Operator::ArrayAtomicRmwXchg { .. }
3133        | Operator::ArrayAtomicRmwCmpxchg { .. }
3134        | Operator::RefI31Shared { .. } => {
3135            return Err(wasm_unsupported!(
3136                "shared-everything-threads operators are not yet implemented"
3137            ));
3138        }
3139
3140        Operator::I64MulWideS => {
3141            let (arg1, arg2) = stack.pop2();
3142            let arg1 = builder.ins().sextend(I128, arg1);
3143            let arg2 = builder.ins().sextend(I128, arg2);
3144            let result = builder.ins().imul(arg1, arg2);
3145            let (lo, hi) = builder.ins().isplit(result);
3146            stack.push2(lo, hi);
3147        }
3148        Operator::I64MulWideU => {
3149            let (arg1, arg2) = stack.pop2();
3150            let arg1 = builder.ins().uextend(I128, arg1);
3151            let arg2 = builder.ins().uextend(I128, arg2);
3152            let result = builder.ins().imul(arg1, arg2);
3153            let (lo, hi) = builder.ins().isplit(result);
3154            stack.push2(lo, hi);
3155        }
3156        Operator::I64Add128 => {
3157            let (arg1, arg2, arg3, arg4) = stack.pop4();
3158            let arg1 = builder.ins().iconcat(arg1, arg2);
3159            let arg2 = builder.ins().iconcat(arg3, arg4);
3160            let result = builder.ins().iadd(arg1, arg2);
3161            let (res1, res2) = builder.ins().isplit(result);
3162            stack.push2(res1, res2);
3163        }
3164        Operator::I64Sub128 => {
3165            let (arg1, arg2, arg3, arg4) = stack.pop4();
3166            let arg1 = builder.ins().iconcat(arg1, arg2);
3167            let arg2 = builder.ins().iconcat(arg3, arg4);
3168            let result = builder.ins().isub(arg1, arg2);
3169            let (res1, res2) = builder.ins().isplit(result);
3170            stack.push2(res1, res2);
3171        }
3172
3173        // catch-all as `Operator` is `#[non_exhaustive]`
3174        op => return Err(wasm_unsupported!("operator {op:?}")),
3175    };
3176    Ok(())
3177}
3178
3179/// Deals with a Wasm instruction located in an unreachable portion of the code. Most of them
3180/// are dropped but special ones like `End` or `Else` signal the potential end of the unreachable
3181/// portion so the translation state must be updated accordingly.
3182fn translate_unreachable_operator(
3183    validator: &FuncValidator<impl WasmModuleResources>,
3184    op: &Operator,
3185    builder: &mut FunctionBuilder,
3186    stack: &mut FuncTranslationStacks,
3187    environ: &mut FuncEnvironment<'_>,
3188) -> WasmResult<()> {
3189    debug_assert!(!stack.reachable);
3190    match *op {
3191        Operator::If { blockty } => {
3192            // Push a placeholder control stack entry. The if isn't reachable,
3193            // so we don't have any branches anywhere.
3194            stack.push_if(
3195                ir::Block::reserved_value(),
3196                ElseData::NoElse {
3197                    branch_inst: ir::Inst::reserved_value(),
3198                    placeholder: ir::Block::reserved_value(),
3199                },
3200                0,
3201                0,
3202                blockty,
3203            );
3204        }
3205        Operator::Loop { blockty: _ }
3206        | Operator::Block { blockty: _ }
3207        | Operator::TryTable { try_table: _ } => {
3208            stack.push_block(ir::Block::reserved_value(), 0, 0);
3209        }
3210        Operator::Else => {
3211            let i = stack.control_stack.len() - 1;
3212            match stack.control_stack[i] {
3213                ControlStackFrame::If {
3214                    ref else_data,
3215                    head_is_reachable,
3216                    ref mut consequent_ends_reachable,
3217                    blocktype,
3218                    ..
3219                } => {
3220                    debug_assert!(consequent_ends_reachable.is_none());
3221                    *consequent_ends_reachable = Some(stack.reachable);
3222
3223                    if head_is_reachable {
3224                        // We have a branch from the head of the `if` to the `else`.
3225                        stack.reachable = true;
3226
3227                        let else_block = match *else_data {
3228                            ElseData::NoElse {
3229                                branch_inst,
3230                                placeholder,
3231                            } => {
3232                                let (params, _results) =
3233                                    blocktype_params_results(validator, blocktype)?;
3234                                let else_block = block_with_params(builder, params, environ)?;
3235                                let frame = stack.control_stack.last().unwrap();
3236                                frame.truncate_value_stack_to_else_params(&mut stack.stack);
3237
3238                                // We change the target of the branch instruction.
3239                                builder.change_jump_destination(
3240                                    branch_inst,
3241                                    placeholder,
3242                                    else_block,
3243                                );
3244                                builder.seal_block(else_block);
3245                                else_block
3246                            }
3247                            ElseData::WithElse { else_block } => {
3248                                let frame = stack.control_stack.last().unwrap();
3249                                frame.truncate_value_stack_to_else_params(&mut stack.stack);
3250                                else_block
3251                            }
3252                        };
3253
3254                        builder.switch_to_block(else_block);
3255
3256                        // Again, no need to push the parameters for the `else`,
3257                        // since we already did when we saw the original `if`. See
3258                        // the comment for translating `Operator::Else` in
3259                        // `translate_operator` for details.
3260                    }
3261                }
3262                _ => unreachable!(),
3263            }
3264        }
3265        Operator::End => {
3266            let value_stack = &mut stack.stack;
3267            let control_stack = &mut stack.control_stack;
3268            let frame = control_stack.pop().unwrap();
3269
3270            frame.restore_catch_handlers(&mut stack.handlers, builder);
3271
3272            // Pop unused parameters from stack.
3273            frame.truncate_value_stack_to_original_size(value_stack);
3274
3275            let reachable_anyway = match frame {
3276                // If it is a loop we also have to seal the body loop block
3277                ControlStackFrame::Loop { header, .. } => {
3278                    builder.seal_block(header);
3279                    // And loops can't have branches to the end.
3280                    false
3281                }
3282                // If we never set `consequent_ends_reachable` then that means
3283                // we are finishing the consequent now, and there was no
3284                // `else`. Whether the following block is reachable depends only
3285                // on if the head was reachable.
3286                ControlStackFrame::If {
3287                    head_is_reachable,
3288                    consequent_ends_reachable: None,
3289                    ..
3290                } => head_is_reachable,
3291                // Since we are only in this function when in unreachable code,
3292                // we know that the alternative just ended unreachable. Whether
3293                // the following block is reachable depends on if the consequent
3294                // ended reachable or not.
3295                ControlStackFrame::If {
3296                    head_is_reachable,
3297                    consequent_ends_reachable: Some(consequent_ends_reachable),
3298                    ..
3299                } => head_is_reachable && consequent_ends_reachable,
3300                // All other control constructs are already handled.
3301                _ => false,
3302            };
3303
3304            if frame.exit_is_branched_to() || reachable_anyway {
3305                builder.switch_to_block(frame.following_code());
3306                builder.seal_block(frame.following_code());
3307
3308                // And add the return values of the block but only if the next block is reachable
3309                // (which corresponds to testing if the stack depth is 1)
3310                value_stack.extend_from_slice(builder.block_params(frame.following_code()));
3311                stack.reachable = true;
3312            }
3313        }
3314        _ => {
3315            // We don't translate because this is unreachable code
3316        }
3317    }
3318
3319    Ok(())
3320}
3321
3322/// This function is a generalized helper for validating that a wasm-supplied
3323/// heap address is in-bounds.
3324///
3325/// This function takes a litany of parameters and requires that the *Wasm*
3326/// address to be verified is at the top of the stack in `state`. This will
3327/// generate necessary IR to validate that the heap address is correctly
3328/// in-bounds, and various parameters are returned describing the valid *native*
3329/// heap address if execution reaches that point.
3330///
3331/// Returns `None` when the Wasm access will unconditionally trap.
3332///
3333/// Returns `(flags, wasm_addr, native_addr)`.
3334fn prepare_addr(
3335    memarg: &MemArg,
3336    access_size: u8,
3337    builder: &mut FunctionBuilder,
3338    stack: &mut FuncTranslationStacks,
3339    environ: &mut FuncEnvironment<'_>,
3340) -> WasmResult<Reachability<(MemFlags, Value, Value)>> {
3341    let index = stack.pop1();
3342
3343    let memory_index = MemoryIndex::from_u32(memarg.memory);
3344    let heap = environ.get_or_create_heap(builder.func, memory_index);
3345
3346    // How exactly the bounds check is performed here and what it's performed
3347    // on is a bit tricky. Generally we want to rely on access violations (e.g.
3348    // segfaults) to generate traps since that means we don't have to bounds
3349    // check anything explicitly.
3350    //
3351    // (1) If we don't have a guard page of unmapped memory, though, then we
3352    // can't rely on this trapping behavior through segfaults. Instead we need
3353    // to bounds-check the entire memory access here which is everything from
3354    // `addr32 + offset` to `addr32 + offset + width` (not inclusive). In this
3355    // scenario our adjusted offset that we're checking is `memarg.offset +
3356    // access_size`. Note that we do saturating arithmetic here to avoid
3357    // overflow. The addition here is in the 64-bit space, which means that
3358    // we'll never overflow for 32-bit wasm but for 64-bit this is an issue. If
3359    // our effective offset is u64::MAX though then it's impossible for for
3360    // that to actually be a valid offset because otherwise the wasm linear
3361    // memory would take all of the host memory!
3362    //
3363    // (2) If we have a guard page, however, then we can perform a further
3364    // optimization of the generated code by only checking multiples of the
3365    // offset-guard size to be more CSE-friendly. Knowing that we have at least
3366    // 1 page of a guard page we're then able to disregard the `width` since we
3367    // know it's always less than one page. Our bounds check will be for the
3368    // first byte which will either succeed and be guaranteed to fault if it's
3369    // actually out of bounds, or the bounds check itself will fail. In any case
3370    // we assert that the width is reasonably small for now so this assumption
3371    // can be adjusted in the future if we get larger widths.
3372    //
3373    // Put another way we can say, where `y < offset_guard_size`:
3374    //
3375    //      n * offset_guard_size + y = offset
3376    //
3377    // We'll then pass `n * offset_guard_size` as the bounds check value. If
3378    // this traps then our `offset` would have trapped anyway. If this check
3379    // passes we know
3380    //
3381    //      addr32 + n * offset_guard_size < bound
3382    //
3383    // which means
3384    //
3385    //      addr32 + n * offset_guard_size + y < bound + offset_guard_size
3386    //
3387    // because `y < offset_guard_size`, which then means:
3388    //
3389    //      addr32 + offset < bound + offset_guard_size
3390    //
3391    // Since we know that that guard size bytes are all unmapped we're
3392    // guaranteed that `offset` and the `width` bytes after it are either
3393    // in-bounds or will hit the guard page, meaning we'll get the desired
3394    // semantics we want.
3395    //
3396    // ---
3397    //
3398    // With all that in mind remember that the goal is to bounds check as few
3399    // things as possible. To facilitate this the "fast path" is expected to be
3400    // hit like so:
3401    //
3402    // * For wasm32, wasmtime defaults to 4gb "static" memories with 2gb guard
3403    //   regions. This means that for all offsets <=2gb, we hit the optimized
3404    //   case for `heap_addr` on static memories 4gb in size in cranelift's
3405    //   legalization of `heap_addr`, eliding the bounds check entirely.
3406    //
3407    // * For wasm64 offsets <=2gb will generate a single `heap_addr`
3408    //   instruction, but at this time all heaps are "dynamic" which means that
3409    //   a single bounds check is forced. Ideally we'd do better here, but
3410    //   that's the current state of affairs.
3411    //
3412    // Basically we assume that most configurations have a guard page and most
3413    // offsets in `memarg` are <=2gb, which means we get the fast path of one
3414    // `heap_addr` instruction plus a hardcoded i32-offset in memory-related
3415    // instructions.
3416    let heap = environ.heaps()[heap].clone();
3417    let addr = match u32::try_from(memarg.offset) {
3418        // If our offset fits within a u32, then we can place the it into the
3419        // offset immediate of the `heap_addr` instruction.
3420        Ok(offset) => bounds_check_and_compute_addr(
3421            builder,
3422            environ,
3423            &heap,
3424            index,
3425            BoundsCheck::StaticOffset {
3426                offset,
3427                access_size,
3428            },
3429            ir::TrapCode::HEAP_OUT_OF_BOUNDS,
3430        ),
3431
3432        // If the offset doesn't fit within a u32, then we can't pass it
3433        // directly into `heap_addr`.
3434        //
3435        // One reasonable question you might ask is "why not?". There's no
3436        // fundamental reason why `heap_addr` *must* take a 32-bit offset. The
3437        // reason this isn't done, though, is that blindly changing the offset
3438        // to a 64-bit offset increases the size of the `InstructionData` enum
3439        // in cranelift by 8 bytes (16 to 24). This can have significant
3440        // performance implications so the conclusion when this was written was
3441        // that we shouldn't do that.
3442        //
3443        // Without the ability to put the whole offset into the `heap_addr`
3444        // instruction we need to fold the offset into the address itself with
3445        // an unsigned addition. In doing so though we need to check for
3446        // overflow because that would mean the address is out-of-bounds (wasm
3447        // bounds checks happen on the effective 33 or 65 bit address once the
3448        // offset is factored in).
3449        //
3450        // Once we have the effective address, offset already folded in, then
3451        // `heap_addr` is used to verify that the address is indeed in-bounds.
3452        //
3453        // Note that this is generating what's likely to be at least two
3454        // branches, one for the overflow and one for the bounds check itself.
3455        // For now though that should hopefully be ok since 4gb+ offsets are
3456        // relatively odd/rare. In the future if needed we can look into
3457        // optimizing this more.
3458        Err(_) => {
3459            let offset = builder
3460                .ins()
3461                .iconst(heap.index_type(), memarg.offset.cast_signed());
3462            let adjusted_index = environ.uadd_overflow_trap(
3463                builder,
3464                index,
3465                offset,
3466                ir::TrapCode::HEAP_OUT_OF_BOUNDS,
3467            );
3468            bounds_check_and_compute_addr(
3469                builder,
3470                environ,
3471                &heap,
3472                adjusted_index,
3473                BoundsCheck::StaticOffset {
3474                    offset: 0,
3475                    access_size,
3476                },
3477                ir::TrapCode::HEAP_OUT_OF_BOUNDS,
3478            )
3479        }
3480    };
3481    let addr = match addr {
3482        Reachability::Unreachable => return Ok(Reachability::Unreachable),
3483        Reachability::Reachable(a) => a,
3484    };
3485
3486    // Note that we don't set `is_aligned` here, even if the load instruction's
3487    // alignment immediate may says it's aligned, because WebAssembly's
3488    // immediate field is just a hint, while Cranelift's aligned flag needs a
3489    // guarantee. WebAssembly memory accesses are always little-endian.
3490    let mut flags = MemFlags::new();
3491    flags.set_endianness(ir::Endianness::Little);
3492
3493    if heap.pcc_memory_type.is_some() {
3494        // Proof-carrying code is enabled; check this memory access.
3495        flags.set_checked();
3496    }
3497
3498    // The access occurs to the `heap` disjoint category of abstract
3499    // state. This may allow alias analysis to merge redundant loads,
3500    // etc. when heap accesses occur interleaved with other (table,
3501    // vmctx, stack) accesses.
3502    flags.set_alias_region(Some(ir::AliasRegion::Heap));
3503
3504    Ok(Reachability::Reachable((flags, index, addr)))
3505}
3506
3507fn align_atomic_addr(
3508    memarg: &MemArg,
3509    loaded_bytes: u8,
3510    builder: &mut FunctionBuilder,
3511    stack: &mut FuncTranslationStacks,
3512    environ: &mut FuncEnvironment<'_>,
3513) {
3514    // Atomic addresses must all be aligned correctly, and for now we check
3515    // alignment before we check out-of-bounds-ness. The order of this check may
3516    // need to be updated depending on the outcome of the official threads
3517    // proposal itself.
3518    //
3519    // Note that with an offset>0 we generate an `iadd_imm` where the result is
3520    // thrown away after the offset check. This may truncate the offset and the
3521    // result may overflow as well, but those conditions won't affect the
3522    // alignment check itself. This can probably be optimized better and we
3523    // should do so in the future as well.
3524    if loaded_bytes > 1 {
3525        let addr = stack.pop1(); // "peek" via pop then push
3526        stack.push1(addr);
3527        let effective_addr = if memarg.offset == 0 {
3528            addr
3529        } else {
3530            builder.ins().iadd_imm(addr, memarg.offset.cast_signed())
3531        };
3532        debug_assert!(loaded_bytes.is_power_of_two());
3533        let misalignment = builder
3534            .ins()
3535            .band_imm(effective_addr, i64::from(loaded_bytes - 1));
3536        let f = builder.ins().icmp_imm(IntCC::NotEqual, misalignment, 0);
3537        environ.trapnz(builder, f, crate::TRAP_HEAP_MISALIGNED);
3538    }
3539}
3540
3541/// Like `prepare_addr` but for atomic accesses.
3542///
3543/// Returns `None` when the Wasm access will unconditionally trap.
3544fn prepare_atomic_addr(
3545    memarg: &MemArg,
3546    loaded_bytes: u8,
3547    builder: &mut FunctionBuilder,
3548    stack: &mut FuncTranslationStacks,
3549    environ: &mut FuncEnvironment<'_>,
3550) -> WasmResult<Reachability<(MemFlags, Value, Value)>> {
3551    align_atomic_addr(memarg, loaded_bytes, builder, stack, environ);
3552    prepare_addr(memarg, loaded_bytes, builder, stack, environ)
3553}
3554
3555/// Translate a load instruction.
3556///
3557/// Returns the execution state's reachability after the load is translated.
3558fn translate_load(
3559    memarg: &MemArg,
3560    opcode: ir::Opcode,
3561    result_ty: Type,
3562    builder: &mut FunctionBuilder,
3563    stack: &mut FuncTranslationStacks,
3564    environ: &mut FuncEnvironment<'_>,
3565) -> WasmResult<Reachability<()>> {
3566    let mem_op_size = mem_op_size(opcode, result_ty);
3567    let (flags, wasm_index, base) =
3568        match prepare_addr(memarg, mem_op_size, builder, stack, environ)? {
3569            Reachability::Unreachable => return Ok(Reachability::Unreachable),
3570            Reachability::Reachable((f, i, b)) => (f, i, b),
3571        };
3572
3573    environ.before_load(builder, mem_op_size, wasm_index, memarg.offset);
3574
3575    let (load, dfg) = builder
3576        .ins()
3577        .Load(opcode, result_ty, flags, Offset32::new(0), base);
3578    stack.push1(dfg.first_result(load));
3579    Ok(Reachability::Reachable(()))
3580}
3581
3582/// Translate a store instruction.
3583fn translate_store(
3584    memarg: &MemArg,
3585    opcode: ir::Opcode,
3586    builder: &mut FunctionBuilder,
3587    stack: &mut FuncTranslationStacks,
3588    environ: &mut FuncEnvironment<'_>,
3589) -> WasmResult<()> {
3590    let val = stack.pop1();
3591    let val_ty = builder.func.dfg.value_type(val);
3592    let mem_op_size = mem_op_size(opcode, val_ty);
3593
3594    let (flags, wasm_index, base) = unwrap_or_return_unreachable_state!(
3595        stack,
3596        prepare_addr(memarg, mem_op_size, builder, stack, environ)?
3597    );
3598
3599    environ.before_store(builder, mem_op_size, wasm_index, memarg.offset);
3600
3601    builder
3602        .ins()
3603        .Store(opcode, val_ty, flags, Offset32::new(0), val, base);
3604    Ok(())
3605}
3606
3607fn mem_op_size(opcode: ir::Opcode, ty: Type) -> u8 {
3608    match opcode {
3609        ir::Opcode::Istore8 | ir::Opcode::Sload8 | ir::Opcode::Uload8 => 1,
3610        ir::Opcode::Istore16 | ir::Opcode::Sload16 | ir::Opcode::Uload16 => 2,
3611        ir::Opcode::Istore32 | ir::Opcode::Sload32 | ir::Opcode::Uload32 => 4,
3612        ir::Opcode::Store | ir::Opcode::Load => u8::try_from(ty.bytes()).unwrap(),
3613        _ => panic!("unknown size of mem op for {opcode:?}"),
3614    }
3615}
3616
3617fn translate_icmp(cc: IntCC, builder: &mut FunctionBuilder, stack: &mut FuncTranslationStacks) {
3618    let (arg0, arg1) = stack.pop2();
3619    let val = builder.ins().icmp(cc, arg0, arg1);
3620    stack.push1(builder.ins().uextend(I32, val));
3621}
3622
3623fn translate_atomic_rmw(
3624    widened_ty: Type,
3625    access_ty: Type,
3626    op: AtomicRmwOp,
3627    memarg: &MemArg,
3628    builder: &mut FunctionBuilder,
3629    stack: &mut FuncTranslationStacks,
3630    environ: &mut FuncEnvironment<'_>,
3631) -> WasmResult<()> {
3632    let mut arg2 = stack.pop1();
3633    let arg2_ty = builder.func.dfg.value_type(arg2);
3634
3635    // The operation is performed at type `access_ty`, and the old value is zero-extended
3636    // to type `widened_ty`.
3637    match access_ty {
3638        I8 | I16 | I32 | I64 => {}
3639        _ => {
3640            return Err(wasm_unsupported!(
3641                "atomic_rmw: unsupported access type {:?}",
3642                access_ty
3643            ));
3644        }
3645    };
3646    let w_ty_ok = match widened_ty {
3647        I32 | I64 => true,
3648        _ => false,
3649    };
3650    assert!(w_ty_ok && widened_ty.bytes() >= access_ty.bytes());
3651
3652    assert!(arg2_ty.bytes() >= access_ty.bytes());
3653    if arg2_ty.bytes() > access_ty.bytes() {
3654        arg2 = builder.ins().ireduce(access_ty, arg2);
3655    }
3656
3657    let (flags, _, addr) = unwrap_or_return_unreachable_state!(
3658        stack,
3659        prepare_atomic_addr(
3660            memarg,
3661            u8::try_from(access_ty.bytes()).unwrap(),
3662            builder,
3663            stack,
3664            environ,
3665        )?
3666    );
3667
3668    let mut res = builder.ins().atomic_rmw(access_ty, flags, op, addr, arg2);
3669    if access_ty != widened_ty {
3670        res = builder.ins().uextend(widened_ty, res);
3671    }
3672    stack.push1(res);
3673    Ok(())
3674}
3675
3676fn translate_atomic_cas(
3677    widened_ty: Type,
3678    access_ty: Type,
3679    memarg: &MemArg,
3680    builder: &mut FunctionBuilder,
3681    stack: &mut FuncTranslationStacks,
3682    environ: &mut FuncEnvironment<'_>,
3683) -> WasmResult<()> {
3684    let (mut expected, mut replacement) = stack.pop2();
3685    let expected_ty = builder.func.dfg.value_type(expected);
3686    let replacement_ty = builder.func.dfg.value_type(replacement);
3687
3688    // The compare-and-swap is performed at type `access_ty`, and the old value is zero-extended
3689    // to type `widened_ty`.
3690    match access_ty {
3691        I8 | I16 | I32 | I64 => {}
3692        _ => {
3693            return Err(wasm_unsupported!(
3694                "atomic_cas: unsupported access type {:?}",
3695                access_ty
3696            ));
3697        }
3698    };
3699    let w_ty_ok = match widened_ty {
3700        I32 | I64 => true,
3701        _ => false,
3702    };
3703    assert!(w_ty_ok && widened_ty.bytes() >= access_ty.bytes());
3704
3705    assert!(expected_ty.bytes() >= access_ty.bytes());
3706    if expected_ty.bytes() > access_ty.bytes() {
3707        expected = builder.ins().ireduce(access_ty, expected);
3708    }
3709    assert!(replacement_ty.bytes() >= access_ty.bytes());
3710    if replacement_ty.bytes() > access_ty.bytes() {
3711        replacement = builder.ins().ireduce(access_ty, replacement);
3712    }
3713
3714    let (flags, _, addr) = unwrap_or_return_unreachable_state!(
3715        stack,
3716        prepare_atomic_addr(
3717            memarg,
3718            u8::try_from(access_ty.bytes()).unwrap(),
3719            builder,
3720            stack,
3721            environ,
3722        )?
3723    );
3724    let mut res = builder.ins().atomic_cas(flags, addr, expected, replacement);
3725    if access_ty != widened_ty {
3726        res = builder.ins().uextend(widened_ty, res);
3727    }
3728    stack.push1(res);
3729    Ok(())
3730}
3731
3732fn translate_atomic_load(
3733    widened_ty: Type,
3734    access_ty: Type,
3735    memarg: &MemArg,
3736    builder: &mut FunctionBuilder,
3737    stack: &mut FuncTranslationStacks,
3738    environ: &mut FuncEnvironment<'_>,
3739) -> WasmResult<()> {
3740    // The load is performed at type `access_ty`, and the loaded value is zero extended
3741    // to `widened_ty`.
3742    match access_ty {
3743        I8 | I16 | I32 | I64 => {}
3744        _ => {
3745            return Err(wasm_unsupported!(
3746                "atomic_load: unsupported access type {:?}",
3747                access_ty
3748            ));
3749        }
3750    };
3751    let w_ty_ok = match widened_ty {
3752        I32 | I64 => true,
3753        _ => false,
3754    };
3755    assert!(w_ty_ok && widened_ty.bytes() >= access_ty.bytes());
3756
3757    let (flags, _, addr) = unwrap_or_return_unreachable_state!(
3758        stack,
3759        prepare_atomic_addr(
3760            memarg,
3761            u8::try_from(access_ty.bytes()).unwrap(),
3762            builder,
3763            stack,
3764            environ,
3765        )?
3766    );
3767    let mut res = builder.ins().atomic_load(access_ty, flags, addr);
3768    if access_ty != widened_ty {
3769        res = builder.ins().uextend(widened_ty, res);
3770    }
3771    stack.push1(res);
3772    Ok(())
3773}
3774
3775fn translate_atomic_store(
3776    access_ty: Type,
3777    memarg: &MemArg,
3778    builder: &mut FunctionBuilder,
3779    stack: &mut FuncTranslationStacks,
3780    environ: &mut FuncEnvironment<'_>,
3781) -> WasmResult<()> {
3782    let mut data = stack.pop1();
3783    let data_ty = builder.func.dfg.value_type(data);
3784
3785    // The operation is performed at type `access_ty`, and the data to be stored may first
3786    // need to be narrowed accordingly.
3787    match access_ty {
3788        I8 | I16 | I32 | I64 => {}
3789        _ => {
3790            return Err(wasm_unsupported!(
3791                "atomic_store: unsupported access type {:?}",
3792                access_ty
3793            ));
3794        }
3795    };
3796    let d_ty_ok = match data_ty {
3797        I32 | I64 => true,
3798        _ => false,
3799    };
3800    assert!(d_ty_ok && data_ty.bytes() >= access_ty.bytes());
3801
3802    if data_ty.bytes() > access_ty.bytes() {
3803        data = builder.ins().ireduce(access_ty, data);
3804    }
3805
3806    let (flags, _, addr) = unwrap_or_return_unreachable_state!(
3807        stack,
3808        prepare_atomic_addr(
3809            memarg,
3810            u8::try_from(access_ty.bytes()).unwrap(),
3811            builder,
3812            stack,
3813            environ,
3814        )?
3815    );
3816    builder.ins().atomic_store(flags, data, addr);
3817    Ok(())
3818}
3819
3820fn translate_vector_icmp(
3821    cc: IntCC,
3822    needed_type: Type,
3823    builder: &mut FunctionBuilder,
3824    stack: &mut FuncTranslationStacks,
3825) {
3826    let (a, b) = stack.pop2();
3827    let bitcast_a = optionally_bitcast_vector(a, needed_type, builder);
3828    let bitcast_b = optionally_bitcast_vector(b, needed_type, builder);
3829    stack.push1(builder.ins().icmp(cc, bitcast_a, bitcast_b))
3830}
3831
3832fn translate_fcmp(cc: FloatCC, builder: &mut FunctionBuilder, stack: &mut FuncTranslationStacks) {
3833    let (arg0, arg1) = stack.pop2();
3834    let val = builder.ins().fcmp(cc, arg0, arg1);
3835    stack.push1(builder.ins().uextend(I32, val));
3836}
3837
3838fn translate_vector_fcmp(
3839    cc: FloatCC,
3840    needed_type: Type,
3841    builder: &mut FunctionBuilder,
3842    stack: &mut FuncTranslationStacks,
3843) {
3844    let (a, b) = stack.pop2();
3845    let bitcast_a = optionally_bitcast_vector(a, needed_type, builder);
3846    let bitcast_b = optionally_bitcast_vector(b, needed_type, builder);
3847    stack.push1(builder.ins().fcmp(cc, bitcast_a, bitcast_b))
3848}
3849
3850fn translate_br_if(
3851    relative_depth: u32,
3852    builder: &mut FunctionBuilder,
3853    stack: &mut FuncTranslationStacks,
3854) {
3855    let val = stack.pop1();
3856    let (br_destination, inputs) = translate_br_if_args(relative_depth, stack);
3857    let next_block = builder.create_block();
3858    canonicalise_brif(builder, val, br_destination, inputs, next_block, &[]);
3859
3860    builder.seal_block(next_block); // The only predecessor is the current block.
3861    builder.switch_to_block(next_block);
3862}
3863
3864fn translate_br_if_args(
3865    relative_depth: u32,
3866    stack: &mut FuncTranslationStacks,
3867) -> (ir::Block, &mut [ir::Value]) {
3868    let i = stack.control_stack.len() - 1 - (relative_depth as usize);
3869    let (return_count, br_destination) = {
3870        let frame = &mut stack.control_stack[i];
3871        // The values returned by the branch are still available for the reachable
3872        // code that comes after it
3873        frame.set_branched_to_exit();
3874        let return_count = if frame.is_loop() {
3875            frame.num_param_values()
3876        } else {
3877            frame.num_return_values()
3878        };
3879        (return_count, frame.br_destination())
3880    };
3881    let inputs = stack.peekn_mut(return_count);
3882    (br_destination, inputs)
3883}
3884
3885/// Determine the returned value type of a WebAssembly operator
3886fn type_of(operator: &Operator) -> Type {
3887    match operator {
3888        Operator::V128Load { .. }
3889        | Operator::V128Store { .. }
3890        | Operator::V128Const { .. }
3891        | Operator::V128Not
3892        | Operator::V128And
3893        | Operator::V128AndNot
3894        | Operator::V128Or
3895        | Operator::V128Xor
3896        | Operator::V128AnyTrue
3897        | Operator::V128Bitselect => I8X16, // default type representing V128
3898
3899        Operator::I8x16Shuffle { .. }
3900        | Operator::I8x16Splat
3901        | Operator::V128Load8Splat { .. }
3902        | Operator::V128Load8Lane { .. }
3903        | Operator::V128Store8Lane { .. }
3904        | Operator::I8x16ExtractLaneS { .. }
3905        | Operator::I8x16ExtractLaneU { .. }
3906        | Operator::I8x16ReplaceLane { .. }
3907        | Operator::I8x16Eq
3908        | Operator::I8x16Ne
3909        | Operator::I8x16LtS
3910        | Operator::I8x16LtU
3911        | Operator::I8x16GtS
3912        | Operator::I8x16GtU
3913        | Operator::I8x16LeS
3914        | Operator::I8x16LeU
3915        | Operator::I8x16GeS
3916        | Operator::I8x16GeU
3917        | Operator::I8x16Neg
3918        | Operator::I8x16Abs
3919        | Operator::I8x16AllTrue
3920        | Operator::I8x16Shl
3921        | Operator::I8x16ShrS
3922        | Operator::I8x16ShrU
3923        | Operator::I8x16Add
3924        | Operator::I8x16AddSatS
3925        | Operator::I8x16AddSatU
3926        | Operator::I8x16Sub
3927        | Operator::I8x16SubSatS
3928        | Operator::I8x16SubSatU
3929        | Operator::I8x16MinS
3930        | Operator::I8x16MinU
3931        | Operator::I8x16MaxS
3932        | Operator::I8x16MaxU
3933        | Operator::I8x16AvgrU
3934        | Operator::I8x16Bitmask
3935        | Operator::I8x16Popcnt
3936        | Operator::I8x16RelaxedLaneselect => I8X16,
3937
3938        Operator::I16x8Splat
3939        | Operator::V128Load16Splat { .. }
3940        | Operator::V128Load16Lane { .. }
3941        | Operator::V128Store16Lane { .. }
3942        | Operator::I16x8ExtractLaneS { .. }
3943        | Operator::I16x8ExtractLaneU { .. }
3944        | Operator::I16x8ReplaceLane { .. }
3945        | Operator::I16x8Eq
3946        | Operator::I16x8Ne
3947        | Operator::I16x8LtS
3948        | Operator::I16x8LtU
3949        | Operator::I16x8GtS
3950        | Operator::I16x8GtU
3951        | Operator::I16x8LeS
3952        | Operator::I16x8LeU
3953        | Operator::I16x8GeS
3954        | Operator::I16x8GeU
3955        | Operator::I16x8Neg
3956        | Operator::I16x8Abs
3957        | Operator::I16x8AllTrue
3958        | Operator::I16x8Shl
3959        | Operator::I16x8ShrS
3960        | Operator::I16x8ShrU
3961        | Operator::I16x8Add
3962        | Operator::I16x8AddSatS
3963        | Operator::I16x8AddSatU
3964        | Operator::I16x8Sub
3965        | Operator::I16x8SubSatS
3966        | Operator::I16x8SubSatU
3967        | Operator::I16x8MinS
3968        | Operator::I16x8MinU
3969        | Operator::I16x8MaxS
3970        | Operator::I16x8MaxU
3971        | Operator::I16x8AvgrU
3972        | Operator::I16x8Mul
3973        | Operator::I16x8Bitmask
3974        | Operator::I16x8RelaxedLaneselect => I16X8,
3975
3976        Operator::I32x4Splat
3977        | Operator::V128Load32Splat { .. }
3978        | Operator::V128Load32Lane { .. }
3979        | Operator::V128Store32Lane { .. }
3980        | Operator::I32x4ExtractLane { .. }
3981        | Operator::I32x4ReplaceLane { .. }
3982        | Operator::I32x4Eq
3983        | Operator::I32x4Ne
3984        | Operator::I32x4LtS
3985        | Operator::I32x4LtU
3986        | Operator::I32x4GtS
3987        | Operator::I32x4GtU
3988        | Operator::I32x4LeS
3989        | Operator::I32x4LeU
3990        | Operator::I32x4GeS
3991        | Operator::I32x4GeU
3992        | Operator::I32x4Neg
3993        | Operator::I32x4Abs
3994        | Operator::I32x4AllTrue
3995        | Operator::I32x4Shl
3996        | Operator::I32x4ShrS
3997        | Operator::I32x4ShrU
3998        | Operator::I32x4Add
3999        | Operator::I32x4Sub
4000        | Operator::I32x4Mul
4001        | Operator::I32x4MinS
4002        | Operator::I32x4MinU
4003        | Operator::I32x4MaxS
4004        | Operator::I32x4MaxU
4005        | Operator::I32x4Bitmask
4006        | Operator::I32x4TruncSatF32x4S
4007        | Operator::I32x4TruncSatF32x4U
4008        | Operator::I32x4RelaxedLaneselect
4009        | Operator::V128Load32Zero { .. } => I32X4,
4010
4011        Operator::I64x2Splat
4012        | Operator::V128Load64Splat { .. }
4013        | Operator::V128Load64Lane { .. }
4014        | Operator::V128Store64Lane { .. }
4015        | Operator::I64x2ExtractLane { .. }
4016        | Operator::I64x2ReplaceLane { .. }
4017        | Operator::I64x2Eq
4018        | Operator::I64x2Ne
4019        | Operator::I64x2LtS
4020        | Operator::I64x2GtS
4021        | Operator::I64x2LeS
4022        | Operator::I64x2GeS
4023        | Operator::I64x2Neg
4024        | Operator::I64x2Abs
4025        | Operator::I64x2AllTrue
4026        | Operator::I64x2Shl
4027        | Operator::I64x2ShrS
4028        | Operator::I64x2ShrU
4029        | Operator::I64x2Add
4030        | Operator::I64x2Sub
4031        | Operator::I64x2Mul
4032        | Operator::I64x2Bitmask
4033        | Operator::I64x2RelaxedLaneselect
4034        | Operator::V128Load64Zero { .. } => I64X2,
4035
4036        Operator::F32x4Splat
4037        | Operator::F32x4ExtractLane { .. }
4038        | Operator::F32x4ReplaceLane { .. }
4039        | Operator::F32x4Eq
4040        | Operator::F32x4Ne
4041        | Operator::F32x4Lt
4042        | Operator::F32x4Gt
4043        | Operator::F32x4Le
4044        | Operator::F32x4Ge
4045        | Operator::F32x4Abs
4046        | Operator::F32x4Neg
4047        | Operator::F32x4Sqrt
4048        | Operator::F32x4Add
4049        | Operator::F32x4Sub
4050        | Operator::F32x4Mul
4051        | Operator::F32x4Div
4052        | Operator::F32x4Min
4053        | Operator::F32x4Max
4054        | Operator::F32x4PMin
4055        | Operator::F32x4PMax
4056        | Operator::F32x4ConvertI32x4S
4057        | Operator::F32x4ConvertI32x4U
4058        | Operator::F32x4Ceil
4059        | Operator::F32x4Floor
4060        | Operator::F32x4Trunc
4061        | Operator::F32x4Nearest
4062        | Operator::F32x4RelaxedMax
4063        | Operator::F32x4RelaxedMin
4064        | Operator::F32x4RelaxedMadd
4065        | Operator::F32x4RelaxedNmadd => F32X4,
4066
4067        Operator::F64x2Splat
4068        | Operator::F64x2ExtractLane { .. }
4069        | Operator::F64x2ReplaceLane { .. }
4070        | Operator::F64x2Eq
4071        | Operator::F64x2Ne
4072        | Operator::F64x2Lt
4073        | Operator::F64x2Gt
4074        | Operator::F64x2Le
4075        | Operator::F64x2Ge
4076        | Operator::F64x2Abs
4077        | Operator::F64x2Neg
4078        | Operator::F64x2Sqrt
4079        | Operator::F64x2Add
4080        | Operator::F64x2Sub
4081        | Operator::F64x2Mul
4082        | Operator::F64x2Div
4083        | Operator::F64x2Min
4084        | Operator::F64x2Max
4085        | Operator::F64x2PMin
4086        | Operator::F64x2PMax
4087        | Operator::F64x2Ceil
4088        | Operator::F64x2Floor
4089        | Operator::F64x2Trunc
4090        | Operator::F64x2Nearest
4091        | Operator::F64x2RelaxedMax
4092        | Operator::F64x2RelaxedMin
4093        | Operator::F64x2RelaxedMadd
4094        | Operator::F64x2RelaxedNmadd => F64X2,
4095
4096        _ => unimplemented!(
4097            "Currently only SIMD instructions are mapped to their return type; the \
4098             following instruction is not mapped: {:?}",
4099            operator
4100        ),
4101    }
4102}
4103
4104/// Some SIMD operations only operate on I8X16 in CLIF; this will convert them to that type by
4105/// adding a bitcast if necessary.
4106fn optionally_bitcast_vector(
4107    value: Value,
4108    needed_type: Type,
4109    builder: &mut FunctionBuilder,
4110) -> Value {
4111    if builder.func.dfg.value_type(value) != needed_type {
4112        let mut flags = MemFlags::new();
4113        flags.set_endianness(ir::Endianness::Little);
4114        builder.ins().bitcast(needed_type, flags, value)
4115    } else {
4116        value
4117    }
4118}
4119
4120#[inline(always)]
4121fn is_non_canonical_v128(ty: ir::Type) -> bool {
4122    match ty {
4123        I64X2 | I32X4 | I16X8 | F32X4 | F64X2 => true,
4124        _ => false,
4125    }
4126}
4127
4128/// Cast to I8X16, any vector values in `values` that are of "non-canonical" type (meaning, not
4129/// I8X16), and return them in a slice.  A pre-scan is made to determine whether any casts are
4130/// actually necessary, and if not, the original slice is returned.  Otherwise the cast values
4131/// are returned in a slice that belongs to the caller-supplied `SmallVec`.
4132fn canonicalise_v128_values<'a>(
4133    tmp_canonicalised: &'a mut SmallVec<[BlockArg; 16]>,
4134    builder: &mut FunctionBuilder,
4135    values: &'a [ir::Value],
4136) -> &'a [BlockArg] {
4137    debug_assert!(tmp_canonicalised.is_empty());
4138    // Cast, and push the resulting `Value`s into `canonicalised`.
4139    for v in values {
4140        let value = if is_non_canonical_v128(builder.func.dfg.value_type(*v)) {
4141            let mut flags = MemFlags::new();
4142            flags.set_endianness(ir::Endianness::Little);
4143            builder.ins().bitcast(I8X16, flags, *v)
4144        } else {
4145            *v
4146        };
4147        tmp_canonicalised.push(BlockArg::from(value));
4148    }
4149    tmp_canonicalised.as_slice()
4150}
4151
4152/// Generate a `jump` instruction, but first cast all 128-bit vector values to I8X16 if they
4153/// don't have that type.  This is done in somewhat roundabout way so as to ensure that we
4154/// almost never have to do any heap allocation.
4155fn canonicalise_then_jump(
4156    builder: &mut FunctionBuilder,
4157    destination: ir::Block,
4158    params: &[ir::Value],
4159) -> ir::Inst {
4160    let mut tmp_canonicalised = SmallVec::<[_; 16]>::new();
4161    let canonicalised = canonicalise_v128_values(&mut tmp_canonicalised, builder, params);
4162    builder.ins().jump(destination, canonicalised)
4163}
4164
4165/// The same but for a `brif` instruction.
4166fn canonicalise_brif(
4167    builder: &mut FunctionBuilder,
4168    cond: ir::Value,
4169    block_then: ir::Block,
4170    params_then: &[ir::Value],
4171    block_else: ir::Block,
4172    params_else: &[ir::Value],
4173) -> ir::Inst {
4174    let mut tmp_canonicalised_then = SmallVec::<[_; 16]>::new();
4175    let canonicalised_then =
4176        canonicalise_v128_values(&mut tmp_canonicalised_then, builder, params_then);
4177    let mut tmp_canonicalised_else = SmallVec::<[_; 16]>::new();
4178    let canonicalised_else =
4179        canonicalise_v128_values(&mut tmp_canonicalised_else, builder, params_else);
4180    builder.ins().brif(
4181        cond,
4182        block_then,
4183        canonicalised_then,
4184        block_else,
4185        canonicalised_else,
4186    )
4187}
4188
4189/// A helper for popping and bitcasting a single value; since SIMD values can lose their type by
4190/// using v128 (i.e. CLIF's I8x16) we must re-type the values using a bitcast to avoid CLIF
4191/// typing issues.
4192fn pop1_with_bitcast(
4193    stack: &mut FuncTranslationStacks,
4194    needed_type: Type,
4195    builder: &mut FunctionBuilder,
4196) -> Value {
4197    optionally_bitcast_vector(stack.pop1(), needed_type, builder)
4198}
4199
4200/// A helper for popping and bitcasting two values; since SIMD values can lose their type by
4201/// using v128 (i.e. CLIF's I8x16) we must re-type the values using a bitcast to avoid CLIF
4202/// typing issues.
4203fn pop2_with_bitcast(
4204    stack: &mut FuncTranslationStacks,
4205    needed_type: Type,
4206    builder: &mut FunctionBuilder,
4207) -> (Value, Value) {
4208    let (a, b) = stack.pop2();
4209    let bitcast_a = optionally_bitcast_vector(a, needed_type, builder);
4210    let bitcast_b = optionally_bitcast_vector(b, needed_type, builder);
4211    (bitcast_a, bitcast_b)
4212}
4213
4214fn pop3_with_bitcast(
4215    stack: &mut FuncTranslationStacks,
4216    needed_type: Type,
4217    builder: &mut FunctionBuilder,
4218) -> (Value, Value, Value) {
4219    let (a, b, c) = stack.pop3();
4220    let bitcast_a = optionally_bitcast_vector(a, needed_type, builder);
4221    let bitcast_b = optionally_bitcast_vector(b, needed_type, builder);
4222    let bitcast_c = optionally_bitcast_vector(c, needed_type, builder);
4223    (bitcast_a, bitcast_b, bitcast_c)
4224}
4225
4226fn bitcast_arguments<'a>(
4227    builder: &FunctionBuilder,
4228    arguments: &'a mut [Value],
4229    params: &[ir::AbiParam],
4230    param_predicate: impl Fn(usize) -> bool,
4231) -> Vec<(Type, &'a mut Value)> {
4232    let filtered_param_types = params
4233        .iter()
4234        .enumerate()
4235        .filter(|(i, _)| param_predicate(*i))
4236        .map(|(_, param)| param.value_type);
4237
4238    // zip_eq, from the itertools::Itertools trait, is like Iterator::zip but panics if one
4239    // iterator ends before the other. The `param_predicate` is required to select exactly as many
4240    // elements of `params` as there are elements in `arguments`.
4241    let pairs = filtered_param_types.zip_eq(arguments.iter_mut());
4242
4243    // The arguments which need to be bitcasted are those which have some vector type but the type
4244    // expected by the parameter is not the same vector type as that of the provided argument.
4245    pairs
4246        .filter(|(param_type, _)| param_type.is_vector())
4247        .filter(|(param_type, arg)| {
4248            let arg_type = builder.func.dfg.value_type(**arg);
4249            assert!(
4250                arg_type.is_vector(),
4251                "unexpected type mismatch: expected {}, argument {} was actually of type {}",
4252                param_type,
4253                *arg,
4254                arg_type
4255            );
4256
4257            // This is the same check that would be done by `optionally_bitcast_vector`, except we
4258            // can't take a mutable borrow of the FunctionBuilder here, so we defer inserting the
4259            // bitcast instruction to the caller.
4260            arg_type != *param_type
4261        })
4262        .collect()
4263}
4264
4265/// A helper for bitcasting a sequence of return values for the function currently being built. If
4266/// a value is a vector type that does not match its expected type, this will modify the value in
4267/// place to point to the result of a `bitcast`. This conversion is necessary to translate Wasm
4268/// code that uses `V128` as function parameters (or implicitly in block parameters) and still use
4269/// specific CLIF types (e.g. `I32X4`) in the function body.
4270pub fn bitcast_wasm_returns(arguments: &mut [Value], builder: &mut FunctionBuilder) {
4271    let changes = bitcast_arguments(builder, arguments, &builder.func.signature.returns, |i| {
4272        builder.func.signature.returns[i].purpose == ir::ArgumentPurpose::Normal
4273    });
4274    for (t, arg) in changes {
4275        let mut flags = MemFlags::new();
4276        flags.set_endianness(ir::Endianness::Little);
4277        *arg = builder.ins().bitcast(t, flags, *arg);
4278    }
4279}
4280
4281/// Like `bitcast_wasm_returns`, but for the parameters being passed to a specified callee.
4282fn bitcast_wasm_params(
4283    environ: &mut FuncEnvironment<'_>,
4284    callee_signature: ir::SigRef,
4285    arguments: &mut [Value],
4286    builder: &mut FunctionBuilder,
4287) {
4288    let callee_signature = &builder.func.dfg.signatures[callee_signature];
4289    let changes = bitcast_arguments(builder, arguments, &callee_signature.params, |i| {
4290        environ.is_wasm_parameter(&callee_signature, i)
4291    });
4292    for (t, arg) in changes {
4293        let mut flags = MemFlags::new();
4294        flags.set_endianness(ir::Endianness::Little);
4295        *arg = builder.ins().bitcast(t, flags, *arg);
4296    }
4297}
4298
4299fn create_catch_block(
4300    builder: &mut FunctionBuilder,
4301    stacks: &mut FuncTranslationStacks,
4302    catch: &wasmparser::Catch,
4303    environ: &mut FuncEnvironment<'_>,
4304) -> WasmResult<ir::Block> {
4305    let (is_ref, tag, label) = match catch {
4306        wasmparser::Catch::One { tag, label } => (false, Some(*tag), *label),
4307        wasmparser::Catch::OneRef { tag, label } => (true, Some(*tag), *label),
4308        wasmparser::Catch::All { label } => (false, None, *label),
4309        wasmparser::Catch::AllRef { label } => (true, None, *label),
4310    };
4311
4312    // We always create a handler block with one blockparam for the
4313    // one exception payload value that we use (`exn0` block-call
4314    // argument). This one payload value is the `exnref`. Note,
4315    // however, that we carry it in a native host-pointer-sized
4316    // payload (because this is what the exception ABI in Cranelift
4317    // requires). We then generate the args for the actual branch to
4318    // the handler block: we add unboxing code to load each value in
4319    // the exception signature if a specific tag is expected (hence
4320    // signature is known), and then append the `exnref` itself if we
4321    // are compiling a `*Ref` variant.
4322
4323    let (exn_ref_ty, needs_stack_map) = environ.reference_type(WasmHeapType::Exn);
4324    let (exn_payload_wasm_ty, exn_payload_ty) = match environ.pointer_type().bits() {
4325        32 => (wasmparser::ValType::I32, I32),
4326        64 => (wasmparser::ValType::I64, I64),
4327        _ => panic!("Unsupported pointer width"),
4328    };
4329    let block = block_with_params(builder, [exn_payload_wasm_ty], environ)?;
4330    builder.switch_to_block(block);
4331    let exn_ref = builder.func.dfg.block_params(block)[0];
4332    debug_assert!(exn_ref_ty.bits() <= exn_payload_ty.bits());
4333    let exn_ref = if exn_ref_ty.bits() < exn_payload_ty.bits() {
4334        builder.ins().ireduce(exn_ref_ty, exn_ref)
4335    } else {
4336        exn_ref
4337    };
4338
4339    if needs_stack_map {
4340        builder.declare_value_needs_stack_map(exn_ref);
4341    }
4342
4343    // We encode tag indices from the module directly as Cranelift
4344    // `ExceptionTag`s. We will translate those to (instance,
4345    // defined-tag-index) pairs during the unwind walk -- necessarily
4346    // dynamic because tag imports are provided only at instantiation
4347    // time.
4348    let clif_tag = tag.map(|t| ExceptionTag::from_u32(t));
4349
4350    stacks.handlers.add_handler(clif_tag, block);
4351
4352    let mut params = vec![];
4353
4354    if let Some(tag) = tag {
4355        let tag = TagIndex::from_u32(tag);
4356        params.extend(environ.translate_exn_unbox(builder, tag, exn_ref)?);
4357    }
4358    if is_ref {
4359        params.push(exn_ref);
4360    }
4361
4362    // Generate the branch itself.
4363    let i = stacks.control_stack.len() - 1 - (label as usize);
4364    let frame = &mut stacks.control_stack[i];
4365    frame.set_branched_to_exit();
4366    canonicalise_then_jump(builder, frame.br_destination(), &params);
4367
4368    Ok(block)
4369}