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