wasmer_compiler_cranelift/translator/
code_translator.rs

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