wasmer_compiler_cranelift/translator/
code_translator.rs

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