cranelift_wasm/
code_translator.rs

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