wasmer_compiler_cranelift/translator/
code_translator.rs

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