Skip to main content

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