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