Path: blob/main/crates/cranelift/src/translate/code_translator.rs
1692 views
//! This module contains the bulk of the interesting code performing the translation between1//! WebAssembly and Cranelift IR.2//!3//! The translation is done in one pass, opcode by opcode. Two main data structures are used during4//! code translations: the value stack and the control stack. The value stack mimics the execution5//! of the WebAssembly stack machine: each instruction result is pushed onto the stack and6//! instruction arguments are popped off the stack. Similarly, when encountering a control flow7//! block, it is pushed onto the control stack and popped off when encountering the corresponding8//! `End`.9//!10//! Another data structure, the translation state, records information concerning unreachable code11//! status and about if inserting a return at the end of the function is necessary.12//!13//! Some of the WebAssembly instructions need information about the environment for which they14//! are being translated:15//!16//! - the loads and stores need the memory base address;17//! - the `get_global` and `set_global` instructions depend on how the globals are implemented;18//! - `memory.size` and `memory.grow` are runtime functions;19//! - `call_indirect` has to translate the function index into the address of where this20//! is;21//!22//! That is why `translate_function_body` takes an object having the `WasmRuntime` trait as23//! argument.24//!25//! There is extra complexity associated with translation of 128-bit SIMD instructions.26//! Wasm only considers there to be a single 128-bit vector type. But CLIF's type system27//! distinguishes different lane configurations, so considers 8X16, 16X8, 32X4 and 64X2 to be28//! different types. The result is that, in wasm, it's perfectly OK to take the output of (eg)29//! an `add.16x8` and use that as an operand of a `sub.32x4`, without using any cast. But when30//! translated into CLIF, that will cause a verifier error due to the apparent type mismatch.31//!32//! This file works around that problem by liberally inserting `bitcast` instructions in many33//! places -- mostly, before the use of vector values, either as arguments to CLIF instructions34//! or as block actual parameters. These are no-op casts which nevertheless have different35//! input and output types, and are used (mostly) to "convert" 16X8, 32X4 and 64X2-typed vectors36//! to the "canonical" type, 8X16. Hence the functions `optionally_bitcast_vector`,37//! `bitcast_arguments`, `pop*_with_bitcast`, `canonicalise_then_jump`,38//! `canonicalise_then_br{z,nz}`, `is_non_canonical_v128` and `canonicalise_v128_values`.39//! Note that the `bitcast*` functions are occasionally used to convert to some type other than40//! 8X16, but the `canonicalise*` functions always convert to type 8X16.41//!42//! Be careful when adding support for new vector instructions. And when adding new jumps, even43//! if they are apparently don't have any connection to vectors. Never generate any kind of44//! (inter-block) jump directly. Instead use `canonicalise_then_jump` and45//! `canonicalise_then_br{z,nz}`.46//!47//! The use of bitcasts is ugly and inefficient, but currently unavoidable:48//!49//! * they make the logic in this file fragile: miss out a bitcast for any reason, and there is50//! the risk of the system failing in the verifier. At least for debug builds.51//!52//! * in the new backends, they potentially interfere with pattern matching on CLIF -- the53//! patterns need to take into account the presence of bitcast nodes.54//!55//! * in the new backends, they get translated into machine-level vector-register-copy56//! instructions, none of which are actually necessary. We then depend on the register57//! allocator to coalesce them all out.58//!59//! * they increase the total number of CLIF nodes that have to be processed, hence slowing down60//! the compilation pipeline. Also, the extra coalescing work generates a slowdown.61//!62//! A better solution which would avoid all four problems would be to remove the 8X16, 16X8,63//! 32X4 and 64X2 types from CLIF and instead have a single V128 type.64//!65//! For further background see also:66//! <https://github.com/bytecodealliance/wasmtime/issues/1147>67//! ("Too many raw_bitcasts in SIMD code")68//! <https://github.com/bytecodealliance/cranelift/pull/1251>69//! ("Add X128 type to represent WebAssembly's V128 type")70//! <https://github.com/bytecodealliance/cranelift/pull/1236>71//! ("Relax verification to allow I8X16 to act as a default vector type")7273use crate::Reachability;74use crate::bounds_checks::{BoundsCheck, bounds_check_and_compute_addr};75use crate::func_environ::{Extension, FuncEnvironment};76use crate::translate::TargetEnvironment;77use crate::translate::environ::StructFieldsVec;78use crate::translate::stack::{ControlStackFrame, ElseData, FuncTranslationStacks};79use crate::translate::translation_utils::{80block_with_params, blocktype_params_results, f32_translation, f64_translation,81};82use cranelift_codegen::ir::condcodes::{FloatCC, IntCC};83use cranelift_codegen::ir::immediates::Offset32;84use cranelift_codegen::ir::{85self, AtomicRmwOp, ExceptionTag, InstBuilder, JumpTableData, MemFlags, Value, ValueLabel,86};87use cranelift_codegen::ir::{BlockArg, types::*};88use cranelift_codegen::packed_option::ReservedValue;89use cranelift_frontend::{FunctionBuilder, Variable};90use itertools::Itertools;91use smallvec::{SmallVec, ToSmallVec};92use std::collections::{HashMap, hash_map};93use std::vec::Vec;94use wasmparser::{FuncValidator, MemArg, Operator, WasmModuleResources};95use wasmtime_environ::{96DataIndex, ElemIndex, FuncIndex, GlobalIndex, MemoryIndex, TableIndex, TagIndex, TypeConvert,97TypeIndex, WasmHeapType, WasmRefType, WasmResult, WasmValType, wasm_unsupported,98};99100/// Given a `Reachability<T>`, unwrap the inner `T` or, when unreachable, set101/// `state.reachable = false` and return.102///103/// Used in combination with calling `prepare_addr` and `prepare_atomic_addr`104/// when we can statically determine that a Wasm access will unconditionally105/// trap.106macro_rules! unwrap_or_return_unreachable_state {107($state:ident, $value:expr) => {108match $value {109Reachability::Reachable(x) => x,110Reachability::Unreachable => {111$state.reachable = false;112return Ok(());113}114}115};116}117118/// Translates wasm operators into Cranelift IR instructions.119pub fn translate_operator(120validator: &mut FuncValidator<impl WasmModuleResources>,121op: &Operator,122operand_types: Option<&[WasmValType]>,123builder: &mut FunctionBuilder,124stack: &mut FuncTranslationStacks,125environ: &mut FuncEnvironment<'_>,126) -> WasmResult<()> {127log::trace!("Translating Wasm opcode: {op:?}");128129if !stack.reachable {130translate_unreachable_operator(validator, &op, builder, stack, environ)?;131return Ok(());132}133134// Given that we believe the current block is reachable, the FunctionBuilder ought to agree.135debug_assert!(!builder.is_unreachable());136137let operand_types = operand_types.unwrap_or_else(|| {138panic!("should always have operand types available for valid, reachable ops; op = {op:?}")139});140141// This big match treats all Wasm code operators.142match op {143/********************************** Locals ****************************************144* `get_local` and `set_local` are treated as non-SSA variables and will completely145* disappear in the Cranelift Code146***********************************************************************************/147Operator::LocalGet { local_index } => {148let val = builder.use_var(Variable::from_u32(*local_index));149stack.push1(val);150let label = ValueLabel::from_u32(*local_index);151builder.set_val_label(val, label);152}153Operator::LocalSet { local_index } => {154let mut val = stack.pop1();155156// Ensure SIMD values are cast to their default Cranelift type, I8x16.157let ty = builder.func.dfg.value_type(val);158if ty.is_vector() {159val = optionally_bitcast_vector(val, I8X16, builder);160}161162builder.def_var(Variable::from_u32(*local_index), val);163let label = ValueLabel::from_u32(*local_index);164builder.set_val_label(val, label);165}166Operator::LocalTee { local_index } => {167let mut val = stack.peek1();168169// Ensure SIMD values are cast to their default Cranelift type, I8x16.170let ty = builder.func.dfg.value_type(val);171if ty.is_vector() {172val = optionally_bitcast_vector(val, I8X16, builder);173}174175builder.def_var(Variable::from_u32(*local_index), val);176let label = ValueLabel::from_u32(*local_index);177builder.set_val_label(val, label);178}179/********************************** Globals ****************************************180* `get_global` and `set_global` are handled by the environment.181***********************************************************************************/182Operator::GlobalGet { global_index } => {183let global_index = GlobalIndex::from_u32(*global_index);184let val = environ.translate_global_get(builder, global_index)?;185stack.push1(val);186}187Operator::GlobalSet { global_index } => {188let global_index = GlobalIndex::from_u32(*global_index);189let mut val = stack.pop1();190// Ensure SIMD values are cast to their default Cranelift type, I8x16.191if builder.func.dfg.value_type(val).is_vector() {192val = optionally_bitcast_vector(val, I8X16, builder);193}194environ.translate_global_set(builder, global_index, val)?;195}196/********************************* Stack misc ***************************************197* `drop`, `nop`, `unreachable` and `select`.198***********************************************************************************/199Operator::Drop => {200stack.pop1();201}202Operator::Select => {203let (mut arg1, mut arg2, cond) = stack.pop3();204if builder.func.dfg.value_type(arg1).is_vector() {205arg1 = optionally_bitcast_vector(arg1, I8X16, builder);206}207if builder.func.dfg.value_type(arg2).is_vector() {208arg2 = optionally_bitcast_vector(arg2, I8X16, builder);209}210stack.push1(builder.ins().select(cond, arg1, arg2));211}212Operator::TypedSelect { ty: _ } => {213// We ignore the explicit type parameter as it is only needed for214// validation, which we require to have been performed before215// translation.216let (mut arg1, mut arg2, cond) = stack.pop3();217if builder.func.dfg.value_type(arg1).is_vector() {218arg1 = optionally_bitcast_vector(arg1, I8X16, builder);219}220if builder.func.dfg.value_type(arg2).is_vector() {221arg2 = optionally_bitcast_vector(arg2, I8X16, builder);222}223stack.push1(builder.ins().select(cond, arg1, arg2));224}225Operator::Nop => {226// We do nothing227}228Operator::Unreachable => {229environ.trap(builder, crate::TRAP_UNREACHABLE);230stack.reachable = false;231}232/***************************** Control flow blocks **********************************233* When starting a control flow block, we create a new `Block` that will hold the code234* after the block, and we push a frame on the control stack. Depending on the type235* of block, we create a new `Block` for the body of the block with an associated236* jump instruction.237*238* The `End` instruction pops the last control frame from the control stack, seals239* the destination block (since `br` instructions targeting it only appear inside the240* block and have already been translated) and modify the value stack to use the241* possible `Block`'s arguments values.242***********************************************************************************/243Operator::Block { blockty } => {244let (params, results) = blocktype_params_results(validator, *blockty)?;245let next = block_with_params(builder, results.clone(), environ)?;246stack.push_block(next, params.len(), results.len());247}248Operator::Loop { blockty } => {249let (params, results) = blocktype_params_results(validator, *blockty)?;250let loop_body = block_with_params(builder, params.clone(), environ)?;251let next = block_with_params(builder, results.clone(), environ)?;252canonicalise_then_jump(builder, loop_body, stack.peekn(params.len()));253stack.push_loop(loop_body, next, params.len(), results.len());254255// Pop the initial `Block` actuals and replace them with the `Block`'s256// params since control flow joins at the top of the loop.257stack.popn(params.len());258stack259.stack260.extend_from_slice(builder.block_params(loop_body));261262builder.switch_to_block(loop_body);263environ.translate_loop_header(builder)?;264}265Operator::If { blockty } => {266let val = stack.pop1();267268let next_block = builder.create_block();269let (params, results) = blocktype_params_results(validator, *blockty)?;270let (destination, else_data) = if params.clone().eq(results.clone()) {271// It is possible there is no `else` block, so we will only272// allocate a block for it if/when we find the `else`. For now,273// we if the condition isn't true, then we jump directly to the274// destination block following the whole `if...end`. If we do end275// up discovering an `else`, then we will allocate a block for it276// and go back and patch the jump.277let destination = block_with_params(builder, results.clone(), environ)?;278let branch_inst = canonicalise_brif(279builder,280val,281next_block,282&[],283destination,284stack.peekn(params.len()),285);286(287destination,288ElseData::NoElse {289branch_inst,290placeholder: destination,291},292)293} else {294// The `if` type signature is not valid without an `else` block,295// so we eagerly allocate the `else` block here.296let destination = block_with_params(builder, results.clone(), environ)?;297let else_block = block_with_params(builder, params.clone(), environ)?;298canonicalise_brif(299builder,300val,301next_block,302&[],303else_block,304stack.peekn(params.len()),305);306builder.seal_block(else_block);307(destination, ElseData::WithElse { else_block })308};309310builder.seal_block(next_block); // Only predecessor is the current block.311builder.switch_to_block(next_block);312313// Here we append an argument to a Block targeted by an argumentless jump instruction314// But in fact there are two cases:315// - either the If does not have a Else clause, in that case ty = EmptyBlock316// and we add nothing;317// - either the If have an Else clause, in that case the destination of this jump318// instruction will be changed later when we translate the Else operator.319stack.push_if(320destination,321else_data,322params.len(),323results.len(),324*blockty,325);326}327Operator::Else => {328let i = stack.control_stack.len() - 1;329match stack.control_stack[i] {330ControlStackFrame::If {331ref else_data,332head_is_reachable,333ref mut consequent_ends_reachable,334num_return_values,335blocktype,336destination,337..338} => {339// We finished the consequent, so record its final340// reachability state.341debug_assert!(consequent_ends_reachable.is_none());342*consequent_ends_reachable = Some(stack.reachable);343344if head_is_reachable {345// We have a branch from the head of the `if` to the `else`.346stack.reachable = true;347348// Ensure we have a block for the `else` block (it may have349// already been pre-allocated, see `ElseData` for details).350let else_block = match *else_data {351ElseData::NoElse {352branch_inst,353placeholder,354} => {355let (params, _results) =356blocktype_params_results(validator, blocktype)?;357debug_assert_eq!(params.len(), num_return_values);358let else_block =359block_with_params(builder, params.clone(), environ)?;360canonicalise_then_jump(361builder,362destination,363stack.peekn(params.len()),364);365stack.popn(params.len());366367builder.change_jump_destination(368branch_inst,369placeholder,370else_block,371);372builder.seal_block(else_block);373else_block374}375ElseData::WithElse { else_block } => {376canonicalise_then_jump(377builder,378destination,379stack.peekn(num_return_values),380);381stack.popn(num_return_values);382else_block383}384};385386// You might be expecting that we push the parameters for this387// `else` block here, something like this:388//389// state.pushn(&control_stack_frame.params);390//391// We don't do that because they are already on the top of the stack392// for us: we pushed the parameters twice when we saw the initial393// `if` so that we wouldn't have to save the parameters in the394// `ControlStackFrame` as another `Vec` allocation.395396builder.switch_to_block(else_block);397398// We don't bother updating the control frame's `ElseData`399// to `WithElse` because nothing else will read it.400}401}402_ => unreachable!(),403}404}405Operator::End => {406let frame = stack.control_stack.pop().unwrap();407let next_block = frame.following_code();408let return_count = frame.num_return_values();409let return_args = stack.peekn_mut(return_count);410411canonicalise_then_jump(builder, next_block, return_args);412// You might expect that if we just finished an `if` block that413// didn't have a corresponding `else` block, then we would clean414// up our duplicate set of parameters that we pushed earlier415// right here. However, we don't have to explicitly do that,416// since we truncate the stack back to the original height417// below.418419builder.switch_to_block(next_block);420builder.seal_block(next_block);421422// If it is a loop we also have to seal the body loop block423if let ControlStackFrame::Loop { header, .. } = frame {424builder.seal_block(header)425}426427frame.restore_catch_handlers(&mut stack.handlers, builder);428429frame.truncate_value_stack_to_original_size(&mut stack.stack);430stack431.stack432.extend_from_slice(builder.block_params(next_block));433}434/**************************** Branch instructions *********************************435* The branch instructions all have as arguments a target nesting level, which436* corresponds to how many control stack frames do we have to pop to get the437* destination `Block`.438*439* Once the destination `Block` is found, we sometimes have to declare a certain depth440* of the stack unreachable, because some branch instructions are terminator.441*442* The `br_table` case is much more complicated because Cranelift's `br_table` instruction443* does not support jump arguments like all the other branch instructions. That is why, in444* the case where we would use jump arguments for every other branch instruction, we445* need to split the critical edges leaving the `br_tables` by creating one `Block` per446* table destination; the `br_table` will point to these newly created `Blocks` and these447* `Block`s contain only a jump instruction pointing to the final destination, this time with448* jump arguments.449*450* This system is also implemented in Cranelift's SSA construction algorithm, because451* `use_var` located in a destination `Block` of a `br_table` might trigger the addition452* of jump arguments in each predecessor branch instruction, one of which might be a453* `br_table`.454***********************************************************************************/455Operator::Br { relative_depth } => {456let i = stack.control_stack.len() - 1 - (*relative_depth as usize);457let (return_count, br_destination) = {458let frame = &mut stack.control_stack[i];459// We signal that all the code that follows until the next End is unreachable460frame.set_branched_to_exit();461let return_count = if frame.is_loop() {462frame.num_param_values()463} else {464frame.num_return_values()465};466(return_count, frame.br_destination())467};468let destination_args = stack.peekn_mut(return_count);469canonicalise_then_jump(builder, br_destination, destination_args);470stack.popn(return_count);471stack.reachable = false;472}473Operator::BrIf { relative_depth } => translate_br_if(*relative_depth, builder, stack),474Operator::BrTable { targets } => {475let default = targets.default();476let mut min_depth = default;477for depth in targets.targets() {478let depth = depth?;479if depth < min_depth {480min_depth = depth;481}482}483let jump_args_count = {484let i = stack.control_stack.len() - 1 - (min_depth as usize);485let min_depth_frame = &stack.control_stack[i];486if min_depth_frame.is_loop() {487min_depth_frame.num_param_values()488} else {489min_depth_frame.num_return_values()490}491};492let val = stack.pop1();493let mut data = Vec::with_capacity(targets.len() as usize);494if jump_args_count == 0 {495// No jump arguments496for depth in targets.targets() {497let depth = depth?;498let block = {499let i = stack.control_stack.len() - 1 - (depth as usize);500let frame = &mut stack.control_stack[i];501frame.set_branched_to_exit();502frame.br_destination()503};504data.push(builder.func.dfg.block_call(block, &[]));505}506let block = {507let i = stack.control_stack.len() - 1 - (default as usize);508let frame = &mut stack.control_stack[i];509frame.set_branched_to_exit();510frame.br_destination()511};512let block = builder.func.dfg.block_call(block, &[]);513let jt = builder.create_jump_table(JumpTableData::new(block, &data));514builder.ins().br_table(val, jt);515} else {516// Here we have jump arguments, but Cranelift's br_table doesn't support them517// We then proceed to split the edges going out of the br_table518let return_count = jump_args_count;519let mut dest_block_sequence = vec![];520let mut dest_block_map = HashMap::new();521for depth in targets.targets() {522let depth = depth?;523let branch_block = match dest_block_map.entry(depth as usize) {524hash_map::Entry::Occupied(entry) => *entry.get(),525hash_map::Entry::Vacant(entry) => {526let block = builder.create_block();527dest_block_sequence.push((depth as usize, block));528*entry.insert(block)529}530};531data.push(builder.func.dfg.block_call(branch_block, &[]));532}533let default_branch_block = match dest_block_map.entry(default as usize) {534hash_map::Entry::Occupied(entry) => *entry.get(),535hash_map::Entry::Vacant(entry) => {536let block = builder.create_block();537dest_block_sequence.push((default as usize, block));538*entry.insert(block)539}540};541let default_branch_block = builder.func.dfg.block_call(default_branch_block, &[]);542let jt = builder.create_jump_table(JumpTableData::new(default_branch_block, &data));543builder.ins().br_table(val, jt);544for (depth, dest_block) in dest_block_sequence {545builder.switch_to_block(dest_block);546builder.seal_block(dest_block);547let real_dest_block = {548let i = stack.control_stack.len() - 1 - depth;549let frame = &mut stack.control_stack[i];550frame.set_branched_to_exit();551frame.br_destination()552};553let destination_args = stack.peekn_mut(return_count);554canonicalise_then_jump(builder, real_dest_block, destination_args);555}556stack.popn(return_count);557}558stack.reachable = false;559}560Operator::Return => {561let return_count = {562let frame = &mut stack.control_stack[0];563frame.num_return_values()564};565{566let return_args = stack.peekn_mut(return_count);567environ.handle_before_return(&return_args, builder);568bitcast_wasm_returns(return_args, builder);569builder.ins().return_(return_args);570}571stack.popn(return_count);572stack.reachable = false;573}574/********************************** Exception handling **********************************/575Operator::Catch { .. }576| Operator::Rethrow { .. }577| Operator::Delegate { .. }578| Operator::CatchAll => {579return Err(wasm_unsupported!(580"legacy exception handling proposal is not supported"581));582}583584Operator::TryTable { try_table } => {585// First, create a block on the control stack. This also586// updates the handler state that is attached to all calls587// made within this block.588let body = builder.create_block();589let (params, results) = blocktype_params_results(validator, try_table.ty)?;590let next = block_with_params(builder, results.clone(), environ)?;591builder.ins().jump(body, []);592builder.seal_block(body);593594// For each catch clause, create a block with the595// equivalent of `br` to the target (unboxing the exnref596// into stack values or pushing it directly, depending on597// the kind of clause).598let ckpt = stack.handlers.take_checkpoint();599let mut catch_blocks = vec![];600// Process in *reverse* order: see the comment on601// [`HandlerState`]. In brief, this allows us to unify the602// left-to-right matching semantics of a single603// `try_table`'s catch clauses with the inside-out604// (deepest scope first) semantics of nested `try_table`s.605for catch in try_table.catches.iter().rev() {606// This will register the block in `state.handlers`607// under the appropriate tag.608catch_blocks.push(create_catch_block(builder, stack, catch, environ)?);609}610611stack.push_try_table_block(next, catch_blocks, params.len(), results.len(), ckpt);612613// Continue codegen into the main body block.614builder.switch_to_block(body);615}616617Operator::Throw { tag_index } => {618let tag_index = TagIndex::from_u32(*tag_index);619let arity = environ.tag_param_arity(tag_index);620let args = stack.peekn(arity);621environ.translate_exn_throw(builder, tag_index, args, stack.handlers.handlers())?;622stack.popn(arity);623stack.reachable = false;624}625626Operator::ThrowRef => {627let exnref = stack.pop1();628environ.translate_exn_throw_ref(builder, exnref, stack.handlers.handlers())?;629stack.reachable = false;630}631632/************************************ Calls ****************************************633* The call instructions pop off their arguments from the stack and append their634* return values to it. `call_indirect` needs environment support because there is an635* argument referring to an index in the external functions table of the module.636************************************************************************************/637Operator::Call { function_index } => {638let function_index = FuncIndex::from_u32(*function_index);639let ty = environ.module.functions[function_index]640.signature641.unwrap_module_type_index();642let sig_ref = environ.get_or_create_interned_sig_ref(builder.func, ty);643let num_args = environ.num_params_for_func(function_index);644645// Bitcast any vector arguments to their default type, I8X16, before calling.646let args = stack.peekn_mut(num_args);647bitcast_wasm_params(environ, sig_ref, args, builder);648let args = stack.peekn(num_args); // Re-borrow immutably.649650let inst_results = environ.translate_call(651builder,652function_index,653sig_ref,654args,655stack.handlers.handlers(),656)?;657658debug_assert_eq!(659inst_results.len(),660builder.func.dfg.signatures[sig_ref].returns.len(),661"translate_call results should match the call signature"662);663stack.popn(num_args);664stack.pushn(&inst_results);665}666Operator::CallIndirect {667type_index,668table_index,669} => {670// `type_index` is the index of the function's signature and671// `table_index` is the index of the table to search the function672// in.673let type_index = TypeIndex::from_u32(*type_index);674let sigref = environ.get_or_create_sig_ref(builder.func, type_index);675let num_args = environ.num_params_for_function_type(type_index);676let callee = stack.pop1();677678// Bitcast any vector arguments to their default type, I8X16, before calling.679let args = stack.peekn_mut(num_args);680bitcast_wasm_params(environ, sigref, args, builder);681682let inst_results = environ.translate_call_indirect(683builder,684validator.features(),685TableIndex::from_u32(*table_index),686type_index,687sigref,688callee,689stack.peekn(num_args),690stack.handlers.handlers(),691)?;692let inst_results = match inst_results {693Some(results) => results,694None => {695stack.reachable = false;696return Ok(());697}698};699700debug_assert_eq!(701inst_results.len(),702builder.func.dfg.signatures[sigref].returns.len(),703"translate_call_indirect results should match the call signature"704);705stack.popn(num_args);706stack.pushn(&inst_results);707}708/******************************* Tail Calls ******************************************709* The tail call instructions pop their arguments from the stack and710* then permanently transfer control to their callee. The indirect711* version requires environment support (while the direct version can712* optionally be hooked but doesn't require it) it interacts with the713* VM's runtime state via tables.714************************************************************************************/715Operator::ReturnCall { function_index } => {716let function_index = FuncIndex::from_u32(*function_index);717let ty = environ.module.functions[function_index]718.signature719.unwrap_module_type_index();720let sig_ref = environ.get_or_create_interned_sig_ref(builder.func, ty);721let num_args = environ.num_params_for_func(function_index);722723// Bitcast any vector arguments to their default type, I8X16, before calling.724let args = stack.peekn_mut(num_args);725bitcast_wasm_params(environ, sig_ref, args, builder);726727environ.translate_return_call(builder, function_index, sig_ref, args)?;728729stack.popn(num_args);730stack.reachable = false;731}732Operator::ReturnCallIndirect {733type_index,734table_index,735} => {736// `type_index` is the index of the function's signature and737// `table_index` is the index of the table to search the function738// in.739let type_index = TypeIndex::from_u32(*type_index);740let sigref = environ.get_or_create_sig_ref(builder.func, type_index);741let num_args = environ.num_params_for_function_type(type_index);742let callee = stack.pop1();743744// Bitcast any vector arguments to their default type, I8X16, before calling.745let args = stack.peekn_mut(num_args);746bitcast_wasm_params(environ, sigref, args, builder);747748environ.translate_return_call_indirect(749builder,750validator.features(),751TableIndex::from_u32(*table_index),752type_index,753sigref,754callee,755stack.peekn(num_args),756)?;757758stack.popn(num_args);759stack.reachable = false;760}761Operator::ReturnCallRef { type_index } => {762// Get function signature763// `index` is the index of the function's signature and `table_index` is the index of764// the table to search the function in.765let type_index = TypeIndex::from_u32(*type_index);766let sigref = environ.get_or_create_sig_ref(builder.func, type_index);767let num_args = environ.num_params_for_function_type(type_index);768let callee = stack.pop1();769770// Bitcast any vector arguments to their default type, I8X16, before calling.771let args = stack.peekn_mut(num_args);772bitcast_wasm_params(environ, sigref, args, builder);773774environ.translate_return_call_ref(builder, sigref, callee, stack.peekn(num_args))?;775776stack.popn(num_args);777stack.reachable = false;778}779/******************************* Memory management ***********************************780* Memory management is handled by environment. It is usually translated into calls to781* special functions.782************************************************************************************/783Operator::MemoryGrow { mem } => {784// The WebAssembly MVP only supports one linear memory, but we expect the reserved785// argument to be a memory index.786let mem = MemoryIndex::from_u32(*mem);787let _heap = environ.get_or_create_heap(builder.func, mem);788let val = stack.pop1();789environ.before_memory_grow(builder, val, mem);790stack.push1(environ.translate_memory_grow(builder, mem, val)?)791}792Operator::MemorySize { mem } => {793let mem = MemoryIndex::from_u32(*mem);794let _heap = environ.get_or_create_heap(builder.func, mem);795stack.push1(environ.translate_memory_size(builder.cursor(), mem)?);796}797/******************************* Load instructions ***********************************798* Wasm specifies an integer alignment flag but we drop it in Cranelift.799* The memory base address is provided by the environment.800************************************************************************************/801Operator::I32Load8U { memarg } => {802unwrap_or_return_unreachable_state!(803stack,804translate_load(memarg, ir::Opcode::Uload8, I32, builder, stack, environ)?805);806}807Operator::I32Load16U { memarg } => {808unwrap_or_return_unreachable_state!(809stack,810translate_load(memarg, ir::Opcode::Uload16, I32, builder, stack, environ)?811);812}813Operator::I32Load8S { memarg } => {814unwrap_or_return_unreachable_state!(815stack,816translate_load(memarg, ir::Opcode::Sload8, I32, builder, stack, environ)?817);818}819Operator::I32Load16S { memarg } => {820unwrap_or_return_unreachable_state!(821stack,822translate_load(memarg, ir::Opcode::Sload16, I32, builder, stack, environ)?823);824}825Operator::I64Load8U { memarg } => {826unwrap_or_return_unreachable_state!(827stack,828translate_load(memarg, ir::Opcode::Uload8, I64, builder, stack, environ)?829);830}831Operator::I64Load16U { memarg } => {832unwrap_or_return_unreachable_state!(833stack,834translate_load(memarg, ir::Opcode::Uload16, I64, builder, stack, environ)?835);836}837Operator::I64Load8S { memarg } => {838unwrap_or_return_unreachable_state!(839stack,840translate_load(memarg, ir::Opcode::Sload8, I64, builder, stack, environ)?841);842}843Operator::I64Load16S { memarg } => {844unwrap_or_return_unreachable_state!(845stack,846translate_load(memarg, ir::Opcode::Sload16, I64, builder, stack, environ)?847);848}849Operator::I64Load32S { memarg } => {850unwrap_or_return_unreachable_state!(851stack,852translate_load(memarg, ir::Opcode::Sload32, I64, builder, stack, environ)?853);854}855Operator::I64Load32U { memarg } => {856unwrap_or_return_unreachable_state!(857stack,858translate_load(memarg, ir::Opcode::Uload32, I64, builder, stack, environ)?859);860}861Operator::I32Load { memarg } => {862unwrap_or_return_unreachable_state!(863stack,864translate_load(memarg, ir::Opcode::Load, I32, builder, stack, environ)?865);866}867Operator::F32Load { memarg } => {868unwrap_or_return_unreachable_state!(869stack,870translate_load(memarg, ir::Opcode::Load, F32, builder, stack, environ)?871);872}873Operator::I64Load { memarg } => {874unwrap_or_return_unreachable_state!(875stack,876translate_load(memarg, ir::Opcode::Load, I64, builder, stack, environ)?877);878}879Operator::F64Load { memarg } => {880unwrap_or_return_unreachable_state!(881stack,882translate_load(memarg, ir::Opcode::Load, F64, builder, stack, environ)?883);884}885Operator::V128Load { memarg } => {886unwrap_or_return_unreachable_state!(887stack,888translate_load(memarg, ir::Opcode::Load, I8X16, builder, stack, environ)?889);890}891Operator::V128Load8x8S { memarg } => {892//TODO(#6829): add before_load() and before_store() hooks for SIMD loads and stores.893let (flags, _, base) = unwrap_or_return_unreachable_state!(894stack,895prepare_addr(memarg, 8, builder, stack, environ)?896);897let loaded = builder.ins().sload8x8(flags, base, 0);898stack.push1(loaded);899}900Operator::V128Load8x8U { memarg } => {901let (flags, _, base) = unwrap_or_return_unreachable_state!(902stack,903prepare_addr(memarg, 8, builder, stack, environ)?904);905let loaded = builder.ins().uload8x8(flags, base, 0);906stack.push1(loaded);907}908Operator::V128Load16x4S { memarg } => {909let (flags, _, base) = unwrap_or_return_unreachable_state!(910stack,911prepare_addr(memarg, 8, builder, stack, environ)?912);913let loaded = builder.ins().sload16x4(flags, base, 0);914stack.push1(loaded);915}916Operator::V128Load16x4U { memarg } => {917let (flags, _, base) = unwrap_or_return_unreachable_state!(918stack,919prepare_addr(memarg, 8, builder, stack, environ)?920);921let loaded = builder.ins().uload16x4(flags, base, 0);922stack.push1(loaded);923}924Operator::V128Load32x2S { memarg } => {925let (flags, _, base) = unwrap_or_return_unreachable_state!(926stack,927prepare_addr(memarg, 8, builder, stack, environ)?928);929let loaded = builder.ins().sload32x2(flags, base, 0);930stack.push1(loaded);931}932Operator::V128Load32x2U { memarg } => {933let (flags, _, base) = unwrap_or_return_unreachable_state!(934stack,935prepare_addr(memarg, 8, builder, stack, environ)?936);937let loaded = builder.ins().uload32x2(flags, base, 0);938stack.push1(loaded);939}940/****************************** Store instructions ***********************************941* Wasm specifies an integer alignment flag but we drop it in Cranelift.942* The memory base address is provided by the environment.943************************************************************************************/944Operator::I32Store { memarg }945| Operator::I64Store { memarg }946| Operator::F32Store { memarg }947| Operator::F64Store { memarg } => {948translate_store(memarg, ir::Opcode::Store, builder, stack, environ)?;949}950Operator::I32Store8 { memarg } | Operator::I64Store8 { memarg } => {951translate_store(memarg, ir::Opcode::Istore8, builder, stack, environ)?;952}953Operator::I32Store16 { memarg } | Operator::I64Store16 { memarg } => {954translate_store(memarg, ir::Opcode::Istore16, builder, stack, environ)?;955}956Operator::I64Store32 { memarg } => {957translate_store(memarg, ir::Opcode::Istore32, builder, stack, environ)?;958}959Operator::V128Store { memarg } => {960translate_store(memarg, ir::Opcode::Store, builder, stack, environ)?;961}962/****************************** Nullary Operators ************************************/963Operator::I32Const { value } => {964stack.push1(builder.ins().iconst(I32, i64::from(value.cast_unsigned())));965}966Operator::I64Const { value } => stack.push1(builder.ins().iconst(I64, *value)),967Operator::F32Const { value } => {968stack.push1(builder.ins().f32const(f32_translation(*value)));969}970Operator::F64Const { value } => {971stack.push1(builder.ins().f64const(f64_translation(*value)));972}973/******************************* Unary Operators *************************************/974Operator::I32Clz | Operator::I64Clz => {975let arg = stack.pop1();976stack.push1(builder.ins().clz(arg));977}978Operator::I32Ctz | Operator::I64Ctz => {979let arg = stack.pop1();980stack.push1(builder.ins().ctz(arg));981}982Operator::I32Popcnt | Operator::I64Popcnt => {983let arg = stack.pop1();984stack.push1(builder.ins().popcnt(arg));985}986Operator::I64ExtendI32S => {987let val = stack.pop1();988stack.push1(builder.ins().sextend(I64, val));989}990Operator::I64ExtendI32U => {991let val = stack.pop1();992stack.push1(builder.ins().uextend(I64, val));993}994Operator::I32WrapI64 => {995let val = stack.pop1();996stack.push1(builder.ins().ireduce(I32, val));997}998Operator::F32Sqrt | Operator::F64Sqrt => {999let arg = stack.pop1();1000stack.push1(builder.ins().sqrt(arg));1001}1002Operator::F32Ceil => {1003let arg = stack.pop1();1004stack.push1(environ.ceil_f32(builder, arg));1005}1006Operator::F64Ceil => {1007let arg = stack.pop1();1008stack.push1(environ.ceil_f64(builder, arg));1009}1010Operator::F32Floor => {1011let arg = stack.pop1();1012stack.push1(environ.floor_f32(builder, arg));1013}1014Operator::F64Floor => {1015let arg = stack.pop1();1016stack.push1(environ.floor_f64(builder, arg));1017}1018Operator::F32Trunc => {1019let arg = stack.pop1();1020stack.push1(environ.trunc_f32(builder, arg));1021}1022Operator::F64Trunc => {1023let arg = stack.pop1();1024stack.push1(environ.trunc_f64(builder, arg));1025}1026Operator::F32Nearest => {1027let arg = stack.pop1();1028stack.push1(environ.nearest_f32(builder, arg));1029}1030Operator::F64Nearest => {1031let arg = stack.pop1();1032stack.push1(environ.nearest_f64(builder, arg));1033}1034Operator::F32Abs | Operator::F64Abs => {1035let val = stack.pop1();1036stack.push1(builder.ins().fabs(val));1037}1038Operator::F32Neg | Operator::F64Neg => {1039let arg = stack.pop1();1040stack.push1(builder.ins().fneg(arg));1041}1042Operator::F64ConvertI64U | Operator::F64ConvertI32U => {1043let val = stack.pop1();1044stack.push1(builder.ins().fcvt_from_uint(F64, val));1045}1046Operator::F64ConvertI64S | Operator::F64ConvertI32S => {1047let val = stack.pop1();1048stack.push1(builder.ins().fcvt_from_sint(F64, val));1049}1050Operator::F32ConvertI64S | Operator::F32ConvertI32S => {1051let val = stack.pop1();1052stack.push1(builder.ins().fcvt_from_sint(F32, val));1053}1054Operator::F32ConvertI64U | Operator::F32ConvertI32U => {1055let val = stack.pop1();1056stack.push1(builder.ins().fcvt_from_uint(F32, val));1057}1058Operator::F64PromoteF32 => {1059let val = stack.pop1();1060stack.push1(builder.ins().fpromote(F64, val));1061}1062Operator::F32DemoteF64 => {1063let val = stack.pop1();1064stack.push1(builder.ins().fdemote(F32, val));1065}1066Operator::I64TruncF64S | Operator::I64TruncF32S => {1067let val = stack.pop1();1068stack.push1(environ.translate_fcvt_to_sint(builder, I64, val));1069}1070Operator::I32TruncF64S | Operator::I32TruncF32S => {1071let val = stack.pop1();1072stack.push1(environ.translate_fcvt_to_sint(builder, I32, val));1073}1074Operator::I64TruncF64U | Operator::I64TruncF32U => {1075let val = stack.pop1();1076stack.push1(environ.translate_fcvt_to_uint(builder, I64, val));1077}1078Operator::I32TruncF64U | Operator::I32TruncF32U => {1079let val = stack.pop1();1080stack.push1(environ.translate_fcvt_to_uint(builder, I32, val));1081}1082Operator::I64TruncSatF64S | Operator::I64TruncSatF32S => {1083let val = stack.pop1();1084stack.push1(builder.ins().fcvt_to_sint_sat(I64, val));1085}1086Operator::I32TruncSatF64S | Operator::I32TruncSatF32S => {1087let val = stack.pop1();1088stack.push1(builder.ins().fcvt_to_sint_sat(I32, val));1089}1090Operator::I64TruncSatF64U | Operator::I64TruncSatF32U => {1091let val = stack.pop1();1092stack.push1(builder.ins().fcvt_to_uint_sat(I64, val));1093}1094Operator::I32TruncSatF64U | Operator::I32TruncSatF32U => {1095let val = stack.pop1();1096stack.push1(builder.ins().fcvt_to_uint_sat(I32, val));1097}1098Operator::F32ReinterpretI32 => {1099let val = stack.pop1();1100stack.push1(builder.ins().bitcast(F32, MemFlags::new(), val));1101}1102Operator::F64ReinterpretI64 => {1103let val = stack.pop1();1104stack.push1(builder.ins().bitcast(F64, MemFlags::new(), val));1105}1106Operator::I32ReinterpretF32 => {1107let val = stack.pop1();1108stack.push1(builder.ins().bitcast(I32, MemFlags::new(), val));1109}1110Operator::I64ReinterpretF64 => {1111let val = stack.pop1();1112stack.push1(builder.ins().bitcast(I64, MemFlags::new(), val));1113}1114Operator::I32Extend8S => {1115let val = stack.pop1();1116stack.push1(builder.ins().ireduce(I8, val));1117let val = stack.pop1();1118stack.push1(builder.ins().sextend(I32, val));1119}1120Operator::I32Extend16S => {1121let val = stack.pop1();1122stack.push1(builder.ins().ireduce(I16, val));1123let val = stack.pop1();1124stack.push1(builder.ins().sextend(I32, val));1125}1126Operator::I64Extend8S => {1127let val = stack.pop1();1128stack.push1(builder.ins().ireduce(I8, val));1129let val = stack.pop1();1130stack.push1(builder.ins().sextend(I64, val));1131}1132Operator::I64Extend16S => {1133let val = stack.pop1();1134stack.push1(builder.ins().ireduce(I16, val));1135let val = stack.pop1();1136stack.push1(builder.ins().sextend(I64, val));1137}1138Operator::I64Extend32S => {1139let val = stack.pop1();1140stack.push1(builder.ins().ireduce(I32, val));1141let val = stack.pop1();1142stack.push1(builder.ins().sextend(I64, val));1143}1144/****************************** Binary Operators ************************************/1145Operator::I32Add | Operator::I64Add => {1146let (arg1, arg2) = stack.pop2();1147stack.push1(builder.ins().iadd(arg1, arg2));1148}1149Operator::I32And | Operator::I64And => {1150let (arg1, arg2) = stack.pop2();1151stack.push1(builder.ins().band(arg1, arg2));1152}1153Operator::I32Or | Operator::I64Or => {1154let (arg1, arg2) = stack.pop2();1155stack.push1(builder.ins().bor(arg1, arg2));1156}1157Operator::I32Xor | Operator::I64Xor => {1158let (arg1, arg2) = stack.pop2();1159stack.push1(builder.ins().bxor(arg1, arg2));1160}1161Operator::I32Shl | Operator::I64Shl => {1162let (arg1, arg2) = stack.pop2();1163stack.push1(builder.ins().ishl(arg1, arg2));1164}1165Operator::I32ShrS | Operator::I64ShrS => {1166let (arg1, arg2) = stack.pop2();1167stack.push1(builder.ins().sshr(arg1, arg2));1168}1169Operator::I32ShrU | Operator::I64ShrU => {1170let (arg1, arg2) = stack.pop2();1171stack.push1(builder.ins().ushr(arg1, arg2));1172}1173Operator::I32Rotl | Operator::I64Rotl => {1174let (arg1, arg2) = stack.pop2();1175stack.push1(builder.ins().rotl(arg1, arg2));1176}1177Operator::I32Rotr | Operator::I64Rotr => {1178let (arg1, arg2) = stack.pop2();1179stack.push1(builder.ins().rotr(arg1, arg2));1180}1181Operator::F32Add | Operator::F64Add => {1182let (arg1, arg2) = stack.pop2();1183stack.push1(builder.ins().fadd(arg1, arg2));1184}1185Operator::I32Sub | Operator::I64Sub => {1186let (arg1, arg2) = stack.pop2();1187stack.push1(builder.ins().isub(arg1, arg2));1188}1189Operator::F32Sub | Operator::F64Sub => {1190let (arg1, arg2) = stack.pop2();1191stack.push1(builder.ins().fsub(arg1, arg2));1192}1193Operator::I32Mul | Operator::I64Mul => {1194let (arg1, arg2) = stack.pop2();1195stack.push1(builder.ins().imul(arg1, arg2));1196}1197Operator::F32Mul | Operator::F64Mul => {1198let (arg1, arg2) = stack.pop2();1199stack.push1(builder.ins().fmul(arg1, arg2));1200}1201Operator::F32Div | Operator::F64Div => {1202let (arg1, arg2) = stack.pop2();1203stack.push1(builder.ins().fdiv(arg1, arg2));1204}1205Operator::I32DivS | Operator::I64DivS => {1206let (arg1, arg2) = stack.pop2();1207stack.push1(environ.translate_sdiv(builder, arg1, arg2));1208}1209Operator::I32DivU | Operator::I64DivU => {1210let (arg1, arg2) = stack.pop2();1211stack.push1(environ.translate_udiv(builder, arg1, arg2));1212}1213Operator::I32RemS | Operator::I64RemS => {1214let (arg1, arg2) = stack.pop2();1215stack.push1(environ.translate_srem(builder, arg1, arg2));1216}1217Operator::I32RemU | Operator::I64RemU => {1218let (arg1, arg2) = stack.pop2();1219stack.push1(environ.translate_urem(builder, arg1, arg2));1220}1221Operator::F32Min | Operator::F64Min => {1222let (arg1, arg2) = stack.pop2();1223stack.push1(builder.ins().fmin(arg1, arg2));1224}1225Operator::F32Max | Operator::F64Max => {1226let (arg1, arg2) = stack.pop2();1227stack.push1(builder.ins().fmax(arg1, arg2));1228}1229Operator::F32Copysign | Operator::F64Copysign => {1230let (arg1, arg2) = stack.pop2();1231stack.push1(builder.ins().fcopysign(arg1, arg2));1232}1233/**************************** Comparison Operators **********************************/1234Operator::I32LtS | Operator::I64LtS => {1235translate_icmp(IntCC::SignedLessThan, builder, stack)1236}1237Operator::I32LtU | Operator::I64LtU => {1238translate_icmp(IntCC::UnsignedLessThan, builder, stack)1239}1240Operator::I32LeS | Operator::I64LeS => {1241translate_icmp(IntCC::SignedLessThanOrEqual, builder, stack)1242}1243Operator::I32LeU | Operator::I64LeU => {1244translate_icmp(IntCC::UnsignedLessThanOrEqual, builder, stack)1245}1246Operator::I32GtS | Operator::I64GtS => {1247translate_icmp(IntCC::SignedGreaterThan, builder, stack)1248}1249Operator::I32GtU | Operator::I64GtU => {1250translate_icmp(IntCC::UnsignedGreaterThan, builder, stack)1251}1252Operator::I32GeS | Operator::I64GeS => {1253translate_icmp(IntCC::SignedGreaterThanOrEqual, builder, stack)1254}1255Operator::I32GeU | Operator::I64GeU => {1256translate_icmp(IntCC::UnsignedGreaterThanOrEqual, builder, stack)1257}1258Operator::I32Eqz | Operator::I64Eqz => {1259let arg = stack.pop1();1260let val = builder.ins().icmp_imm(IntCC::Equal, arg, 0);1261stack.push1(builder.ins().uextend(I32, val));1262}1263Operator::I32Eq | Operator::I64Eq => translate_icmp(IntCC::Equal, builder, stack),1264Operator::F32Eq | Operator::F64Eq => translate_fcmp(FloatCC::Equal, builder, stack),1265Operator::I32Ne | Operator::I64Ne => translate_icmp(IntCC::NotEqual, builder, stack),1266Operator::F32Ne | Operator::F64Ne => translate_fcmp(FloatCC::NotEqual, builder, stack),1267Operator::F32Gt | Operator::F64Gt => translate_fcmp(FloatCC::GreaterThan, builder, stack),1268Operator::F32Ge | Operator::F64Ge => {1269translate_fcmp(FloatCC::GreaterThanOrEqual, builder, stack)1270}1271Operator::F32Lt | Operator::F64Lt => translate_fcmp(FloatCC::LessThan, builder, stack),1272Operator::F32Le | Operator::F64Le => {1273translate_fcmp(FloatCC::LessThanOrEqual, builder, stack)1274}1275Operator::RefNull { hty } => {1276let hty = environ.convert_heap_type(*hty)?;1277stack.push1(environ.translate_ref_null(builder.cursor(), hty)?)1278}1279Operator::RefIsNull => {1280let value = stack.pop1();1281let [WasmValType::Ref(ty)] = operand_types else {1282unreachable!("validation")1283};1284stack.push1(environ.translate_ref_is_null(builder.cursor(), value, *ty)?);1285}1286Operator::RefFunc { function_index } => {1287let index = FuncIndex::from_u32(*function_index);1288stack.push1(environ.translate_ref_func(builder.cursor(), index)?);1289}1290Operator::MemoryAtomicWait32 { memarg } | Operator::MemoryAtomicWait64 { memarg } => {1291// The WebAssembly MVP only supports one linear memory and1292// wasmparser will ensure that the memory indices specified are1293// zero.1294let implied_ty = match op {1295Operator::MemoryAtomicWait64 { .. } => I64,1296Operator::MemoryAtomicWait32 { .. } => I32,1297_ => unreachable!(),1298};1299let memory_index = MemoryIndex::from_u32(memarg.memory);1300let heap = environ.get_or_create_heap(builder.func, memory_index);1301let timeout = stack.pop1(); // 64 (fixed)1302let expected = stack.pop1(); // 32 or 64 (per the `Ixx` in `IxxAtomicWait`)1303assert!(builder.func.dfg.value_type(expected) == implied_ty);1304let addr = stack.pop1();1305let effective_addr = if memarg.offset == 0 {1306addr1307} else {1308let index_type = environ.heaps()[heap].index_type();1309let offset = builder.ins().iconst(index_type, memarg.offset as i64);1310environ.uadd_overflow_trap(builder, addr, offset, ir::TrapCode::HEAP_OUT_OF_BOUNDS)1311};1312// `fn translate_atomic_wait` can inspect the type of `expected` to figure out what1313// code it needs to generate, if it wants.1314let res = environ.translate_atomic_wait(1315builder,1316memory_index,1317heap,1318effective_addr,1319expected,1320timeout,1321)?;1322stack.push1(res);1323}1324Operator::MemoryAtomicNotify { memarg } => {1325let memory_index = MemoryIndex::from_u32(memarg.memory);1326let heap = environ.get_or_create_heap(builder.func, memory_index);1327let count = stack.pop1(); // 32 (fixed)1328let addr = stack.pop1();1329let effective_addr = if memarg.offset == 0 {1330addr1331} else {1332let index_type = environ.heaps()[heap].index_type();1333let offset = builder.ins().iconst(index_type, memarg.offset as i64);1334environ.uadd_overflow_trap(builder, addr, offset, ir::TrapCode::HEAP_OUT_OF_BOUNDS)1335};1336let res = environ.translate_atomic_notify(1337builder,1338memory_index,1339heap,1340effective_addr,1341count,1342)?;1343stack.push1(res);1344}1345Operator::I32AtomicLoad { memarg } => {1346translate_atomic_load(I32, I32, memarg, builder, stack, environ)?1347}1348Operator::I64AtomicLoad { memarg } => {1349translate_atomic_load(I64, I64, memarg, builder, stack, environ)?1350}1351Operator::I32AtomicLoad8U { memarg } => {1352translate_atomic_load(I32, I8, memarg, builder, stack, environ)?1353}1354Operator::I32AtomicLoad16U { memarg } => {1355translate_atomic_load(I32, I16, memarg, builder, stack, environ)?1356}1357Operator::I64AtomicLoad8U { memarg } => {1358translate_atomic_load(I64, I8, memarg, builder, stack, environ)?1359}1360Operator::I64AtomicLoad16U { memarg } => {1361translate_atomic_load(I64, I16, memarg, builder, stack, environ)?1362}1363Operator::I64AtomicLoad32U { memarg } => {1364translate_atomic_load(I64, I32, memarg, builder, stack, environ)?1365}13661367Operator::I32AtomicStore { memarg } => {1368translate_atomic_store(I32, memarg, builder, stack, environ)?1369}1370Operator::I64AtomicStore { memarg } => {1371translate_atomic_store(I64, memarg, builder, stack, environ)?1372}1373Operator::I32AtomicStore8 { memarg } => {1374translate_atomic_store(I8, memarg, builder, stack, environ)?1375}1376Operator::I32AtomicStore16 { memarg } => {1377translate_atomic_store(I16, memarg, builder, stack, environ)?1378}1379Operator::I64AtomicStore8 { memarg } => {1380translate_atomic_store(I8, memarg, builder, stack, environ)?1381}1382Operator::I64AtomicStore16 { memarg } => {1383translate_atomic_store(I16, memarg, builder, stack, environ)?1384}1385Operator::I64AtomicStore32 { memarg } => {1386translate_atomic_store(I32, memarg, builder, stack, environ)?1387}13881389Operator::I32AtomicRmwAdd { memarg } => {1390translate_atomic_rmw(I32, I32, AtomicRmwOp::Add, memarg, builder, stack, environ)?1391}1392Operator::I64AtomicRmwAdd { memarg } => {1393translate_atomic_rmw(I64, I64, AtomicRmwOp::Add, memarg, builder, stack, environ)?1394}1395Operator::I32AtomicRmw8AddU { memarg } => {1396translate_atomic_rmw(I32, I8, AtomicRmwOp::Add, memarg, builder, stack, environ)?1397}1398Operator::I32AtomicRmw16AddU { memarg } => {1399translate_atomic_rmw(I32, I16, AtomicRmwOp::Add, memarg, builder, stack, environ)?1400}1401Operator::I64AtomicRmw8AddU { memarg } => {1402translate_atomic_rmw(I64, I8, AtomicRmwOp::Add, memarg, builder, stack, environ)?1403}1404Operator::I64AtomicRmw16AddU { memarg } => {1405translate_atomic_rmw(I64, I16, AtomicRmwOp::Add, memarg, builder, stack, environ)?1406}1407Operator::I64AtomicRmw32AddU { memarg } => {1408translate_atomic_rmw(I64, I32, AtomicRmwOp::Add, memarg, builder, stack, environ)?1409}14101411Operator::I32AtomicRmwSub { memarg } => {1412translate_atomic_rmw(I32, I32, AtomicRmwOp::Sub, memarg, builder, stack, environ)?1413}1414Operator::I64AtomicRmwSub { memarg } => {1415translate_atomic_rmw(I64, I64, AtomicRmwOp::Sub, memarg, builder, stack, environ)?1416}1417Operator::I32AtomicRmw8SubU { memarg } => {1418translate_atomic_rmw(I32, I8, AtomicRmwOp::Sub, memarg, builder, stack, environ)?1419}1420Operator::I32AtomicRmw16SubU { memarg } => {1421translate_atomic_rmw(I32, I16, AtomicRmwOp::Sub, memarg, builder, stack, environ)?1422}1423Operator::I64AtomicRmw8SubU { memarg } => {1424translate_atomic_rmw(I64, I8, AtomicRmwOp::Sub, memarg, builder, stack, environ)?1425}1426Operator::I64AtomicRmw16SubU { memarg } => {1427translate_atomic_rmw(I64, I16, AtomicRmwOp::Sub, memarg, builder, stack, environ)?1428}1429Operator::I64AtomicRmw32SubU { memarg } => {1430translate_atomic_rmw(I64, I32, AtomicRmwOp::Sub, memarg, builder, stack, environ)?1431}14321433Operator::I32AtomicRmwAnd { memarg } => {1434translate_atomic_rmw(I32, I32, AtomicRmwOp::And, memarg, builder, stack, environ)?1435}1436Operator::I64AtomicRmwAnd { memarg } => {1437translate_atomic_rmw(I64, I64, AtomicRmwOp::And, memarg, builder, stack, environ)?1438}1439Operator::I32AtomicRmw8AndU { memarg } => {1440translate_atomic_rmw(I32, I8, AtomicRmwOp::And, memarg, builder, stack, environ)?1441}1442Operator::I32AtomicRmw16AndU { memarg } => {1443translate_atomic_rmw(I32, I16, AtomicRmwOp::And, memarg, builder, stack, environ)?1444}1445Operator::I64AtomicRmw8AndU { memarg } => {1446translate_atomic_rmw(I64, I8, AtomicRmwOp::And, memarg, builder, stack, environ)?1447}1448Operator::I64AtomicRmw16AndU { memarg } => {1449translate_atomic_rmw(I64, I16, AtomicRmwOp::And, memarg, builder, stack, environ)?1450}1451Operator::I64AtomicRmw32AndU { memarg } => {1452translate_atomic_rmw(I64, I32, AtomicRmwOp::And, memarg, builder, stack, environ)?1453}14541455Operator::I32AtomicRmwOr { memarg } => {1456translate_atomic_rmw(I32, I32, AtomicRmwOp::Or, memarg, builder, stack, environ)?1457}1458Operator::I64AtomicRmwOr { memarg } => {1459translate_atomic_rmw(I64, I64, AtomicRmwOp::Or, memarg, builder, stack, environ)?1460}1461Operator::I32AtomicRmw8OrU { memarg } => {1462translate_atomic_rmw(I32, I8, AtomicRmwOp::Or, memarg, builder, stack, environ)?1463}1464Operator::I32AtomicRmw16OrU { memarg } => {1465translate_atomic_rmw(I32, I16, AtomicRmwOp::Or, memarg, builder, stack, environ)?1466}1467Operator::I64AtomicRmw8OrU { memarg } => {1468translate_atomic_rmw(I64, I8, AtomicRmwOp::Or, memarg, builder, stack, environ)?1469}1470Operator::I64AtomicRmw16OrU { memarg } => {1471translate_atomic_rmw(I64, I16, AtomicRmwOp::Or, memarg, builder, stack, environ)?1472}1473Operator::I64AtomicRmw32OrU { memarg } => {1474translate_atomic_rmw(I64, I32, AtomicRmwOp::Or, memarg, builder, stack, environ)?1475}14761477Operator::I32AtomicRmwXor { memarg } => {1478translate_atomic_rmw(I32, I32, AtomicRmwOp::Xor, memarg, builder, stack, environ)?1479}1480Operator::I64AtomicRmwXor { memarg } => {1481translate_atomic_rmw(I64, I64, AtomicRmwOp::Xor, memarg, builder, stack, environ)?1482}1483Operator::I32AtomicRmw8XorU { memarg } => {1484translate_atomic_rmw(I32, I8, AtomicRmwOp::Xor, memarg, builder, stack, environ)?1485}1486Operator::I32AtomicRmw16XorU { memarg } => {1487translate_atomic_rmw(I32, I16, AtomicRmwOp::Xor, memarg, builder, stack, environ)?1488}1489Operator::I64AtomicRmw8XorU { memarg } => {1490translate_atomic_rmw(I64, I8, AtomicRmwOp::Xor, memarg, builder, stack, environ)?1491}1492Operator::I64AtomicRmw16XorU { memarg } => {1493translate_atomic_rmw(I64, I16, AtomicRmwOp::Xor, memarg, builder, stack, environ)?1494}1495Operator::I64AtomicRmw32XorU { memarg } => {1496translate_atomic_rmw(I64, I32, AtomicRmwOp::Xor, memarg, builder, stack, environ)?1497}14981499Operator::I32AtomicRmwXchg { memarg } => {1500translate_atomic_rmw(I32, I32, AtomicRmwOp::Xchg, memarg, builder, stack, environ)?1501}1502Operator::I64AtomicRmwXchg { memarg } => {1503translate_atomic_rmw(I64, I64, AtomicRmwOp::Xchg, memarg, builder, stack, environ)?1504}1505Operator::I32AtomicRmw8XchgU { memarg } => {1506translate_atomic_rmw(I32, I8, AtomicRmwOp::Xchg, memarg, builder, stack, environ)?1507}1508Operator::I32AtomicRmw16XchgU { memarg } => {1509translate_atomic_rmw(I32, I16, AtomicRmwOp::Xchg, memarg, builder, stack, environ)?1510}1511Operator::I64AtomicRmw8XchgU { memarg } => {1512translate_atomic_rmw(I64, I8, AtomicRmwOp::Xchg, memarg, builder, stack, environ)?1513}1514Operator::I64AtomicRmw16XchgU { memarg } => {1515translate_atomic_rmw(I64, I16, AtomicRmwOp::Xchg, memarg, builder, stack, environ)?1516}1517Operator::I64AtomicRmw32XchgU { memarg } => {1518translate_atomic_rmw(I64, I32, AtomicRmwOp::Xchg, memarg, builder, stack, environ)?1519}15201521Operator::I32AtomicRmwCmpxchg { memarg } => {1522translate_atomic_cas(I32, I32, memarg, builder, stack, environ)?1523}1524Operator::I64AtomicRmwCmpxchg { memarg } => {1525translate_atomic_cas(I64, I64, memarg, builder, stack, environ)?1526}1527Operator::I32AtomicRmw8CmpxchgU { memarg } => {1528translate_atomic_cas(I32, I8, memarg, builder, stack, environ)?1529}1530Operator::I32AtomicRmw16CmpxchgU { memarg } => {1531translate_atomic_cas(I32, I16, memarg, builder, stack, environ)?1532}1533Operator::I64AtomicRmw8CmpxchgU { memarg } => {1534translate_atomic_cas(I64, I8, memarg, builder, stack, environ)?1535}1536Operator::I64AtomicRmw16CmpxchgU { memarg } => {1537translate_atomic_cas(I64, I16, memarg, builder, stack, environ)?1538}1539Operator::I64AtomicRmw32CmpxchgU { memarg } => {1540translate_atomic_cas(I64, I32, memarg, builder, stack, environ)?1541}15421543Operator::AtomicFence { .. } => {1544builder.ins().fence();1545}1546Operator::MemoryCopy { src_mem, dst_mem } => {1547let src_index = MemoryIndex::from_u32(*src_mem);1548let _src_heap = environ.get_or_create_heap(builder.func, src_index);15491550let dst_index = MemoryIndex::from_u32(*dst_mem);1551let _dst_heap = environ.get_or_create_heap(builder.func, dst_index);15521553let len = stack.pop1();1554let src_pos = stack.pop1();1555let dst_pos = stack.pop1();1556environ.translate_memory_copy(builder, src_index, dst_index, dst_pos, src_pos, len)?;1557}1558Operator::MemoryFill { mem } => {1559let mem = MemoryIndex::from_u32(*mem);1560let _heap = environ.get_or_create_heap(builder.func, mem);1561let len = stack.pop1();1562let val = stack.pop1();1563let dest = stack.pop1();1564environ.translate_memory_fill(builder, mem, dest, val, len)?;1565}1566Operator::MemoryInit { data_index, mem } => {1567let mem = MemoryIndex::from_u32(*mem);1568let _heap = environ.get_or_create_heap(builder.func, mem);1569let len = stack.pop1();1570let src = stack.pop1();1571let dest = stack.pop1();1572environ.translate_memory_init(builder, mem, *data_index, dest, src, len)?;1573}1574Operator::DataDrop { data_index } => {1575environ.translate_data_drop(builder.cursor(), *data_index)?;1576}1577Operator::TableSize { table: index } => {1578stack.push1(1579environ.translate_table_size(builder.cursor(), TableIndex::from_u32(*index))?,1580);1581}1582Operator::TableGrow { table: index } => {1583let table_index = TableIndex::from_u32(*index);1584let delta = stack.pop1();1585let init_value = stack.pop1();1586stack.push1(environ.translate_table_grow(builder, table_index, delta, init_value)?);1587}1588Operator::TableGet { table: index } => {1589let table_index = TableIndex::from_u32(*index);1590let index = stack.pop1();1591stack.push1(environ.translate_table_get(builder, table_index, index)?);1592}1593Operator::TableSet { table: index } => {1594let table_index = TableIndex::from_u32(*index);1595let value = stack.pop1();1596let index = stack.pop1();1597environ.translate_table_set(builder, table_index, value, index)?;1598}1599Operator::TableCopy {1600dst_table: dst_table_index,1601src_table: src_table_index,1602} => {1603let len = stack.pop1();1604let src = stack.pop1();1605let dest = stack.pop1();1606environ.translate_table_copy(1607builder,1608TableIndex::from_u32(*dst_table_index),1609TableIndex::from_u32(*src_table_index),1610dest,1611src,1612len,1613)?;1614}1615Operator::TableFill { table } => {1616let table_index = TableIndex::from_u32(*table);1617let len = stack.pop1();1618let val = stack.pop1();1619let dest = stack.pop1();1620environ.translate_table_fill(builder, table_index, dest, val, len)?;1621}1622Operator::TableInit {1623elem_index,1624table: table_index,1625} => {1626let len = stack.pop1();1627let src = stack.pop1();1628let dest = stack.pop1();1629environ.translate_table_init(1630builder,1631*elem_index,1632TableIndex::from_u32(*table_index),1633dest,1634src,1635len,1636)?;1637}1638Operator::ElemDrop { elem_index } => {1639environ.translate_elem_drop(builder.cursor(), *elem_index)?;1640}1641Operator::V128Const { value } => {1642let data = value.bytes().to_vec().into();1643let handle = builder.func.dfg.constants.insert(data);1644let value = builder.ins().vconst(I8X16, handle);1645// the v128.const is typed in CLIF as a I8x16 but bitcast to a different type1646// before use1647stack.push1(value)1648}1649Operator::I8x16Splat | Operator::I16x8Splat => {1650let reduced = builder.ins().ireduce(type_of(op).lane_type(), stack.pop1());1651let splatted = builder.ins().splat(type_of(op), reduced);1652stack.push1(splatted)1653}1654Operator::I32x4Splat1655| Operator::I64x2Splat1656| Operator::F32x4Splat1657| Operator::F64x2Splat => {1658let splatted = builder.ins().splat(type_of(op), stack.pop1());1659stack.push1(splatted)1660}1661Operator::V128Load8Splat { memarg }1662| Operator::V128Load16Splat { memarg }1663| Operator::V128Load32Splat { memarg }1664| Operator::V128Load64Splat { memarg } => {1665unwrap_or_return_unreachable_state!(1666stack,1667translate_load(1668memarg,1669ir::Opcode::Load,1670type_of(op).lane_type(),1671builder,1672stack,1673environ,1674)?1675);1676let splatted = builder.ins().splat(type_of(op), stack.pop1());1677stack.push1(splatted)1678}1679Operator::V128Load32Zero { memarg } | Operator::V128Load64Zero { memarg } => {1680unwrap_or_return_unreachable_state!(1681stack,1682translate_load(1683memarg,1684ir::Opcode::Load,1685type_of(op).lane_type(),1686builder,1687stack,1688environ,1689)?1690);1691let as_vector = builder.ins().scalar_to_vector(type_of(op), stack.pop1());1692stack.push1(as_vector)1693}1694Operator::V128Load8Lane { memarg, lane }1695| Operator::V128Load16Lane { memarg, lane }1696| Operator::V128Load32Lane { memarg, lane }1697| Operator::V128Load64Lane { memarg, lane } => {1698let vector = pop1_with_bitcast(stack, type_of(op), builder);1699unwrap_or_return_unreachable_state!(1700stack,1701translate_load(1702memarg,1703ir::Opcode::Load,1704type_of(op).lane_type(),1705builder,1706stack,1707environ,1708)?1709);1710let replacement = stack.pop1();1711stack.push1(builder.ins().insertlane(vector, replacement, *lane))1712}1713Operator::V128Store8Lane { memarg, lane }1714| Operator::V128Store16Lane { memarg, lane }1715| Operator::V128Store32Lane { memarg, lane }1716| Operator::V128Store64Lane { memarg, lane } => {1717let vector = pop1_with_bitcast(stack, type_of(op), builder);1718stack.push1(builder.ins().extractlane(vector, *lane));1719translate_store(memarg, ir::Opcode::Store, builder, stack, environ)?;1720}1721Operator::I8x16ExtractLaneS { lane } | Operator::I16x8ExtractLaneS { lane } => {1722let vector = pop1_with_bitcast(stack, type_of(op), builder);1723let extracted = builder.ins().extractlane(vector, *lane);1724stack.push1(builder.ins().sextend(I32, extracted))1725}1726Operator::I8x16ExtractLaneU { lane } | Operator::I16x8ExtractLaneU { lane } => {1727let vector = pop1_with_bitcast(stack, type_of(op), builder);1728let extracted = builder.ins().extractlane(vector, *lane);1729stack.push1(builder.ins().uextend(I32, extracted));1730// On x86, PEXTRB zeroes the upper bits of the destination register of extractlane so1731// uextend could be elided; for now, uextend is needed for Cranelift's type checks to1732// work.1733}1734Operator::I32x4ExtractLane { lane }1735| Operator::I64x2ExtractLane { lane }1736| Operator::F32x4ExtractLane { lane }1737| Operator::F64x2ExtractLane { lane } => {1738let vector = pop1_with_bitcast(stack, type_of(op), builder);1739stack.push1(builder.ins().extractlane(vector, *lane))1740}1741Operator::I8x16ReplaceLane { lane } | Operator::I16x8ReplaceLane { lane } => {1742let (vector, replacement) = stack.pop2();1743let ty = type_of(op);1744let reduced = builder.ins().ireduce(ty.lane_type(), replacement);1745let vector = optionally_bitcast_vector(vector, ty, builder);1746stack.push1(builder.ins().insertlane(vector, reduced, *lane))1747}1748Operator::I32x4ReplaceLane { lane }1749| Operator::I64x2ReplaceLane { lane }1750| Operator::F32x4ReplaceLane { lane }1751| Operator::F64x2ReplaceLane { lane } => {1752let (vector, replacement) = stack.pop2();1753let vector = optionally_bitcast_vector(vector, type_of(op), builder);1754stack.push1(builder.ins().insertlane(vector, replacement, *lane))1755}1756Operator::I8x16Shuffle { lanes, .. } => {1757let (a, b) = pop2_with_bitcast(stack, I8X16, builder);1758stack.push1(environ.i8x16_shuffle(builder, a, b, lanes));1759// At this point the original types of a and b are lost; users of this value (i.e. this1760// WASM-to-CLIF translator) may need to bitcast for type-correctness. This is due1761// to WASM using the less specific v128 type for certain operations and more specific1762// types (e.g. i8x16) for others.1763}1764Operator::I8x16Swizzle => {1765let (a, b) = pop2_with_bitcast(stack, I8X16, builder);1766stack.push1(environ.swizzle(builder, a, b));1767}1768Operator::I8x16Add | Operator::I16x8Add | Operator::I32x4Add | Operator::I64x2Add => {1769let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);1770stack.push1(builder.ins().iadd(a, b))1771}1772Operator::I8x16AddSatS | Operator::I16x8AddSatS => {1773let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);1774stack.push1(builder.ins().sadd_sat(a, b))1775}1776Operator::I8x16AddSatU | Operator::I16x8AddSatU => {1777let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);1778stack.push1(builder.ins().uadd_sat(a, b))1779}1780Operator::I8x16Sub | Operator::I16x8Sub | Operator::I32x4Sub | Operator::I64x2Sub => {1781let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);1782stack.push1(builder.ins().isub(a, b))1783}1784Operator::I8x16SubSatS | Operator::I16x8SubSatS => {1785let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);1786stack.push1(builder.ins().ssub_sat(a, b))1787}1788Operator::I8x16SubSatU | Operator::I16x8SubSatU => {1789let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);1790stack.push1(builder.ins().usub_sat(a, b))1791}1792Operator::I8x16MinS | Operator::I16x8MinS | Operator::I32x4MinS => {1793let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);1794stack.push1(builder.ins().smin(a, b))1795}1796Operator::I8x16MinU | Operator::I16x8MinU | Operator::I32x4MinU => {1797let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);1798stack.push1(builder.ins().umin(a, b))1799}1800Operator::I8x16MaxS | Operator::I16x8MaxS | Operator::I32x4MaxS => {1801let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);1802stack.push1(builder.ins().smax(a, b))1803}1804Operator::I8x16MaxU | Operator::I16x8MaxU | Operator::I32x4MaxU => {1805let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);1806stack.push1(builder.ins().umax(a, b))1807}1808Operator::I8x16AvgrU | Operator::I16x8AvgrU => {1809let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);1810stack.push1(builder.ins().avg_round(a, b))1811}1812Operator::I8x16Neg | Operator::I16x8Neg | Operator::I32x4Neg | Operator::I64x2Neg => {1813let a = pop1_with_bitcast(stack, type_of(op), builder);1814stack.push1(builder.ins().ineg(a))1815}1816Operator::I8x16Abs | Operator::I16x8Abs | Operator::I32x4Abs | Operator::I64x2Abs => {1817let a = pop1_with_bitcast(stack, type_of(op), builder);1818stack.push1(builder.ins().iabs(a))1819}1820Operator::I16x8Mul | Operator::I32x4Mul | Operator::I64x2Mul => {1821let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);1822stack.push1(builder.ins().imul(a, b))1823}1824Operator::V128Or => {1825let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);1826stack.push1(builder.ins().bor(a, b))1827}1828Operator::V128Xor => {1829let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);1830stack.push1(builder.ins().bxor(a, b))1831}1832Operator::V128And => {1833let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);1834stack.push1(builder.ins().band(a, b))1835}1836Operator::V128AndNot => {1837let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);1838stack.push1(builder.ins().band_not(a, b))1839}1840Operator::V128Not => {1841let a = stack.pop1();1842stack.push1(builder.ins().bnot(a));1843}1844Operator::I8x16Shl | Operator::I16x8Shl | Operator::I32x4Shl | Operator::I64x2Shl => {1845let (a, b) = stack.pop2();1846let bitcast_a = optionally_bitcast_vector(a, type_of(op), builder);1847// The spec expects to shift with `b mod lanewidth`; This is directly compatible1848// with cranelift's instruction.1849stack.push1(builder.ins().ishl(bitcast_a, b))1850}1851Operator::I8x16ShrU | Operator::I16x8ShrU | Operator::I32x4ShrU | Operator::I64x2ShrU => {1852let (a, b) = stack.pop2();1853let bitcast_a = optionally_bitcast_vector(a, type_of(op), builder);1854// The spec expects to shift with `b mod lanewidth`; This is directly compatible1855// with cranelift's instruction.1856stack.push1(builder.ins().ushr(bitcast_a, b))1857}1858Operator::I8x16ShrS | Operator::I16x8ShrS | Operator::I32x4ShrS | Operator::I64x2ShrS => {1859let (a, b) = stack.pop2();1860let bitcast_a = optionally_bitcast_vector(a, type_of(op), builder);1861// The spec expects to shift with `b mod lanewidth`; This is directly compatible1862// with cranelift's instruction.1863stack.push1(builder.ins().sshr(bitcast_a, b))1864}1865Operator::V128Bitselect => {1866let (a, b, c) = pop3_with_bitcast(stack, I8X16, builder);1867// The CLIF operand ordering is slightly different and the types of all three1868// operands must match (hence the bitcast).1869stack.push1(builder.ins().bitselect(c, a, b))1870}1871Operator::V128AnyTrue => {1872let a = pop1_with_bitcast(stack, type_of(op), builder);1873let bool_result = builder.ins().vany_true(a);1874stack.push1(builder.ins().uextend(I32, bool_result))1875}1876Operator::I8x16AllTrue1877| Operator::I16x8AllTrue1878| Operator::I32x4AllTrue1879| Operator::I64x2AllTrue => {1880let a = pop1_with_bitcast(stack, type_of(op), builder);1881let bool_result = builder.ins().vall_true(a);1882stack.push1(builder.ins().uextend(I32, bool_result))1883}1884Operator::I8x16Bitmask1885| Operator::I16x8Bitmask1886| Operator::I32x4Bitmask1887| Operator::I64x2Bitmask => {1888let a = pop1_with_bitcast(stack, type_of(op), builder);1889stack.push1(builder.ins().vhigh_bits(I32, a));1890}1891Operator::I8x16Eq | Operator::I16x8Eq | Operator::I32x4Eq | Operator::I64x2Eq => {1892translate_vector_icmp(IntCC::Equal, type_of(op), builder, stack)1893}1894Operator::I8x16Ne | Operator::I16x8Ne | Operator::I32x4Ne | Operator::I64x2Ne => {1895translate_vector_icmp(IntCC::NotEqual, type_of(op), builder, stack)1896}1897Operator::I8x16GtS | Operator::I16x8GtS | Operator::I32x4GtS | Operator::I64x2GtS => {1898translate_vector_icmp(IntCC::SignedGreaterThan, type_of(op), builder, stack)1899}1900Operator::I8x16LtS | Operator::I16x8LtS | Operator::I32x4LtS | Operator::I64x2LtS => {1901translate_vector_icmp(IntCC::SignedLessThan, type_of(op), builder, stack)1902}1903Operator::I8x16GtU | Operator::I16x8GtU | Operator::I32x4GtU => {1904translate_vector_icmp(IntCC::UnsignedGreaterThan, type_of(op), builder, stack)1905}1906Operator::I8x16LtU | Operator::I16x8LtU | Operator::I32x4LtU => {1907translate_vector_icmp(IntCC::UnsignedLessThan, type_of(op), builder, stack)1908}1909Operator::I8x16GeS | Operator::I16x8GeS | Operator::I32x4GeS | Operator::I64x2GeS => {1910translate_vector_icmp(IntCC::SignedGreaterThanOrEqual, type_of(op), builder, stack)1911}1912Operator::I8x16LeS | Operator::I16x8LeS | Operator::I32x4LeS | Operator::I64x2LeS => {1913translate_vector_icmp(IntCC::SignedLessThanOrEqual, type_of(op), builder, stack)1914}1915Operator::I8x16GeU | Operator::I16x8GeU | Operator::I32x4GeU => translate_vector_icmp(1916IntCC::UnsignedGreaterThanOrEqual,1917type_of(op),1918builder,1919stack,1920),1921Operator::I8x16LeU | Operator::I16x8LeU | Operator::I32x4LeU => {1922translate_vector_icmp(IntCC::UnsignedLessThanOrEqual, type_of(op), builder, stack)1923}1924Operator::F32x4Eq | Operator::F64x2Eq => {1925translate_vector_fcmp(FloatCC::Equal, type_of(op), builder, stack)1926}1927Operator::F32x4Ne | Operator::F64x2Ne => {1928translate_vector_fcmp(FloatCC::NotEqual, type_of(op), builder, stack)1929}1930Operator::F32x4Lt | Operator::F64x2Lt => {1931translate_vector_fcmp(FloatCC::LessThan, type_of(op), builder, stack)1932}1933Operator::F32x4Gt | Operator::F64x2Gt => {1934translate_vector_fcmp(FloatCC::GreaterThan, type_of(op), builder, stack)1935}1936Operator::F32x4Le | Operator::F64x2Le => {1937translate_vector_fcmp(FloatCC::LessThanOrEqual, type_of(op), builder, stack)1938}1939Operator::F32x4Ge | Operator::F64x2Ge => {1940translate_vector_fcmp(FloatCC::GreaterThanOrEqual, type_of(op), builder, stack)1941}1942Operator::F32x4Add | Operator::F64x2Add => {1943let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);1944stack.push1(builder.ins().fadd(a, b))1945}1946Operator::F32x4Sub | Operator::F64x2Sub => {1947let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);1948stack.push1(builder.ins().fsub(a, b))1949}1950Operator::F32x4Mul | Operator::F64x2Mul => {1951let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);1952stack.push1(builder.ins().fmul(a, b))1953}1954Operator::F32x4Div | Operator::F64x2Div => {1955let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);1956stack.push1(builder.ins().fdiv(a, b))1957}1958Operator::F32x4Max | Operator::F64x2Max => {1959let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);1960stack.push1(builder.ins().fmax(a, b))1961}1962Operator::F32x4Min | Operator::F64x2Min => {1963let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);1964stack.push1(builder.ins().fmin(a, b))1965}1966Operator::F32x4PMax | Operator::F64x2PMax => {1967// Note the careful ordering here with respect to `fcmp` and1968// `bitselect`. This matches the spec definition of:1969//1970// fpmax(z1, z2) =1971// * If z1 is less than z2 then return z2.1972// * Else return z1.1973let ty = type_of(op);1974let (a, b) = pop2_with_bitcast(stack, ty, builder);1975let cmp = builder.ins().fcmp(FloatCC::LessThan, a, b);1976let cmp = optionally_bitcast_vector(cmp, ty, builder);1977stack.push1(builder.ins().bitselect(cmp, b, a))1978}1979Operator::F32x4PMin | Operator::F64x2PMin => {1980// Note the careful ordering here which is similar to `pmax` above:1981//1982// fpmin(z1, z2) =1983// * If z2 is less than z1 then return z2.1984// * Else return z1.1985let ty = type_of(op);1986let (a, b) = pop2_with_bitcast(stack, ty, builder);1987let cmp = builder.ins().fcmp(FloatCC::LessThan, b, a);1988let cmp = optionally_bitcast_vector(cmp, ty, builder);1989stack.push1(builder.ins().bitselect(cmp, b, a))1990}1991Operator::F32x4Sqrt | Operator::F64x2Sqrt => {1992let a = pop1_with_bitcast(stack, type_of(op), builder);1993stack.push1(builder.ins().sqrt(a))1994}1995Operator::F32x4Neg | Operator::F64x2Neg => {1996let a = pop1_with_bitcast(stack, type_of(op), builder);1997stack.push1(builder.ins().fneg(a))1998}1999Operator::F32x4Abs | Operator::F64x2Abs => {2000let a = pop1_with_bitcast(stack, type_of(op), builder);2001stack.push1(builder.ins().fabs(a))2002}2003Operator::F32x4ConvertI32x4S => {2004let a = pop1_with_bitcast(stack, I32X4, builder);2005stack.push1(builder.ins().fcvt_from_sint(F32X4, a))2006}2007Operator::F32x4ConvertI32x4U => {2008let a = pop1_with_bitcast(stack, I32X4, builder);2009stack.push1(builder.ins().fcvt_from_uint(F32X4, a))2010}2011Operator::F64x2ConvertLowI32x4S => {2012let a = pop1_with_bitcast(stack, I32X4, builder);2013let widened_a = builder.ins().swiden_low(a);2014stack.push1(builder.ins().fcvt_from_sint(F64X2, widened_a));2015}2016Operator::F64x2ConvertLowI32x4U => {2017let a = pop1_with_bitcast(stack, I32X4, builder);2018let widened_a = builder.ins().uwiden_low(a);2019stack.push1(builder.ins().fcvt_from_uint(F64X2, widened_a));2020}2021Operator::F64x2PromoteLowF32x4 => {2022let a = pop1_with_bitcast(stack, F32X4, builder);2023stack.push1(builder.ins().fvpromote_low(a));2024}2025Operator::F32x4DemoteF64x2Zero => {2026let a = pop1_with_bitcast(stack, F64X2, builder);2027stack.push1(builder.ins().fvdemote(a));2028}2029Operator::I32x4TruncSatF32x4S => {2030let a = pop1_with_bitcast(stack, F32X4, builder);2031stack.push1(builder.ins().fcvt_to_sint_sat(I32X4, a))2032}2033Operator::I32x4TruncSatF64x2SZero => {2034let a = pop1_with_bitcast(stack, F64X2, builder);2035let converted_a = builder.ins().fcvt_to_sint_sat(I64X2, a);2036let handle = builder.func.dfg.constants.insert(vec![0u8; 16].into());2037let zero = builder.ins().vconst(I64X2, handle);20382039stack.push1(builder.ins().snarrow(converted_a, zero));2040}20412042// FIXME(#5913): the relaxed instructions here are translated the same2043// as the saturating instructions, even when the code generator2044// configuration allow for different semantics across hosts. On x86,2045// however, it's theoretically possible to have a slightly more optimal2046// lowering which accounts for NaN differently, although the lowering is2047// still not trivial (e.g. one instruction). At this time the2048// more-optimal-but-still-large lowering for x86 is not implemented so2049// the relaxed instructions are listed here instead of down below with2050// the other relaxed instructions. An x86-specific implementation (or2051// perhaps for other backends too) should be added and the codegen for2052// the relaxed instruction should conditionally be different.2053Operator::I32x4RelaxedTruncF32x4U | Operator::I32x4TruncSatF32x4U => {2054let a = pop1_with_bitcast(stack, F32X4, builder);2055stack.push1(builder.ins().fcvt_to_uint_sat(I32X4, a))2056}2057Operator::I32x4RelaxedTruncF64x2UZero | Operator::I32x4TruncSatF64x2UZero => {2058let a = pop1_with_bitcast(stack, F64X2, builder);2059let zero_constant = builder.func.dfg.constants.insert(vec![0u8; 16].into());2060let result = if environ.is_x86() && !environ.isa().has_round() {2061// On x86 the vector lowering for `fcvt_to_uint_sat` requires2062// SSE4.1 `round` instructions. If SSE4.1 isn't available it2063// falls back to a libcall which we don't want in Wasmtime.2064// Handle this by falling back to the scalar implementation2065// which does not require SSE4.1 instructions.2066let lane0 = builder.ins().extractlane(a, 0);2067let lane1 = builder.ins().extractlane(a, 1);2068let lane0_rounded = builder.ins().fcvt_to_uint_sat(I32, lane0);2069let lane1_rounded = builder.ins().fcvt_to_uint_sat(I32, lane1);2070let result = builder.ins().vconst(I32X4, zero_constant);2071let result = builder.ins().insertlane(result, lane0_rounded, 0);2072builder.ins().insertlane(result, lane1_rounded, 1)2073} else {2074let converted_a = builder.ins().fcvt_to_uint_sat(I64X2, a);2075let zero = builder.ins().vconst(I64X2, zero_constant);2076builder.ins().uunarrow(converted_a, zero)2077};2078stack.push1(result);2079}20802081Operator::I8x16NarrowI16x8S => {2082let (a, b) = pop2_with_bitcast(stack, I16X8, builder);2083stack.push1(builder.ins().snarrow(a, b))2084}2085Operator::I16x8NarrowI32x4S => {2086let (a, b) = pop2_with_bitcast(stack, I32X4, builder);2087stack.push1(builder.ins().snarrow(a, b))2088}2089Operator::I8x16NarrowI16x8U => {2090let (a, b) = pop2_with_bitcast(stack, I16X8, builder);2091stack.push1(builder.ins().unarrow(a, b))2092}2093Operator::I16x8NarrowI32x4U => {2094let (a, b) = pop2_with_bitcast(stack, I32X4, builder);2095stack.push1(builder.ins().unarrow(a, b))2096}2097Operator::I16x8ExtendLowI8x16S => {2098let a = pop1_with_bitcast(stack, I8X16, builder);2099stack.push1(builder.ins().swiden_low(a))2100}2101Operator::I16x8ExtendHighI8x16S => {2102let a = pop1_with_bitcast(stack, I8X16, builder);2103stack.push1(builder.ins().swiden_high(a))2104}2105Operator::I16x8ExtendLowI8x16U => {2106let a = pop1_with_bitcast(stack, I8X16, builder);2107stack.push1(builder.ins().uwiden_low(a))2108}2109Operator::I16x8ExtendHighI8x16U => {2110let a = pop1_with_bitcast(stack, I8X16, builder);2111stack.push1(builder.ins().uwiden_high(a))2112}2113Operator::I32x4ExtendLowI16x8S => {2114let a = pop1_with_bitcast(stack, I16X8, builder);2115stack.push1(builder.ins().swiden_low(a))2116}2117Operator::I32x4ExtendHighI16x8S => {2118let a = pop1_with_bitcast(stack, I16X8, builder);2119stack.push1(builder.ins().swiden_high(a))2120}2121Operator::I32x4ExtendLowI16x8U => {2122let a = pop1_with_bitcast(stack, I16X8, builder);2123stack.push1(builder.ins().uwiden_low(a))2124}2125Operator::I32x4ExtendHighI16x8U => {2126let a = pop1_with_bitcast(stack, I16X8, builder);2127stack.push1(builder.ins().uwiden_high(a))2128}2129Operator::I64x2ExtendLowI32x4S => {2130let a = pop1_with_bitcast(stack, I32X4, builder);2131stack.push1(builder.ins().swiden_low(a))2132}2133Operator::I64x2ExtendHighI32x4S => {2134let a = pop1_with_bitcast(stack, I32X4, builder);2135stack.push1(builder.ins().swiden_high(a))2136}2137Operator::I64x2ExtendLowI32x4U => {2138let a = pop1_with_bitcast(stack, I32X4, builder);2139stack.push1(builder.ins().uwiden_low(a))2140}2141Operator::I64x2ExtendHighI32x4U => {2142let a = pop1_with_bitcast(stack, I32X4, builder);2143stack.push1(builder.ins().uwiden_high(a))2144}2145Operator::I16x8ExtAddPairwiseI8x16S => {2146let a = pop1_with_bitcast(stack, I8X16, builder);2147let widen_low = builder.ins().swiden_low(a);2148let widen_high = builder.ins().swiden_high(a);2149stack.push1(builder.ins().iadd_pairwise(widen_low, widen_high));2150}2151Operator::I32x4ExtAddPairwiseI16x8S => {2152let a = pop1_with_bitcast(stack, I16X8, builder);2153let widen_low = builder.ins().swiden_low(a);2154let widen_high = builder.ins().swiden_high(a);2155stack.push1(builder.ins().iadd_pairwise(widen_low, widen_high));2156}2157Operator::I16x8ExtAddPairwiseI8x16U => {2158let a = pop1_with_bitcast(stack, I8X16, builder);2159let widen_low = builder.ins().uwiden_low(a);2160let widen_high = builder.ins().uwiden_high(a);2161stack.push1(builder.ins().iadd_pairwise(widen_low, widen_high));2162}2163Operator::I32x4ExtAddPairwiseI16x8U => {2164let a = pop1_with_bitcast(stack, I16X8, builder);2165let widen_low = builder.ins().uwiden_low(a);2166let widen_high = builder.ins().uwiden_high(a);2167stack.push1(builder.ins().iadd_pairwise(widen_low, widen_high));2168}2169Operator::F32x4Ceil => {2170let arg = pop1_with_bitcast(stack, F32X4, builder);2171stack.push1(environ.ceil_f32x4(builder, arg));2172}2173Operator::F64x2Ceil => {2174let arg = pop1_with_bitcast(stack, F64X2, builder);2175stack.push1(environ.ceil_f64x2(builder, arg));2176}2177Operator::F32x4Floor => {2178let arg = pop1_with_bitcast(stack, F32X4, builder);2179stack.push1(environ.floor_f32x4(builder, arg));2180}2181Operator::F64x2Floor => {2182let arg = pop1_with_bitcast(stack, F64X2, builder);2183stack.push1(environ.floor_f64x2(builder, arg));2184}2185Operator::F32x4Trunc => {2186let arg = pop1_with_bitcast(stack, F32X4, builder);2187stack.push1(environ.trunc_f32x4(builder, arg));2188}2189Operator::F64x2Trunc => {2190let arg = pop1_with_bitcast(stack, F64X2, builder);2191stack.push1(environ.trunc_f64x2(builder, arg));2192}2193Operator::F32x4Nearest => {2194let arg = pop1_with_bitcast(stack, F32X4, builder);2195stack.push1(environ.nearest_f32x4(builder, arg));2196}2197Operator::F64x2Nearest => {2198let arg = pop1_with_bitcast(stack, F64X2, builder);2199stack.push1(environ.nearest_f64x2(builder, arg));2200}2201Operator::I32x4DotI16x8S => {2202let (a, b) = pop2_with_bitcast(stack, I16X8, builder);2203let alow = builder.ins().swiden_low(a);2204let blow = builder.ins().swiden_low(b);2205let low = builder.ins().imul(alow, blow);2206let ahigh = builder.ins().swiden_high(a);2207let bhigh = builder.ins().swiden_high(b);2208let high = builder.ins().imul(ahigh, bhigh);2209stack.push1(builder.ins().iadd_pairwise(low, high));2210}2211Operator::I8x16Popcnt => {2212let arg = pop1_with_bitcast(stack, type_of(op), builder);2213stack.push1(builder.ins().popcnt(arg));2214}2215Operator::I16x8Q15MulrSatS => {2216let (a, b) = pop2_with_bitcast(stack, I16X8, builder);2217stack.push1(builder.ins().sqmul_round_sat(a, b))2218}2219Operator::I16x8ExtMulLowI8x16S => {2220let (a, b) = pop2_with_bitcast(stack, I8X16, builder);2221let a_low = builder.ins().swiden_low(a);2222let b_low = builder.ins().swiden_low(b);2223stack.push1(builder.ins().imul(a_low, b_low));2224}2225Operator::I16x8ExtMulHighI8x16S => {2226let (a, b) = pop2_with_bitcast(stack, I8X16, builder);2227let a_high = builder.ins().swiden_high(a);2228let b_high = builder.ins().swiden_high(b);2229stack.push1(builder.ins().imul(a_high, b_high));2230}2231Operator::I16x8ExtMulLowI8x16U => {2232let (a, b) = pop2_with_bitcast(stack, I8X16, builder);2233let a_low = builder.ins().uwiden_low(a);2234let b_low = builder.ins().uwiden_low(b);2235stack.push1(builder.ins().imul(a_low, b_low));2236}2237Operator::I16x8ExtMulHighI8x16U => {2238let (a, b) = pop2_with_bitcast(stack, I8X16, builder);2239let a_high = builder.ins().uwiden_high(a);2240let b_high = builder.ins().uwiden_high(b);2241stack.push1(builder.ins().imul(a_high, b_high));2242}2243Operator::I32x4ExtMulLowI16x8S => {2244let (a, b) = pop2_with_bitcast(stack, I16X8, builder);2245let a_low = builder.ins().swiden_low(a);2246let b_low = builder.ins().swiden_low(b);2247stack.push1(builder.ins().imul(a_low, b_low));2248}2249Operator::I32x4ExtMulHighI16x8S => {2250let (a, b) = pop2_with_bitcast(stack, I16X8, builder);2251let a_high = builder.ins().swiden_high(a);2252let b_high = builder.ins().swiden_high(b);2253stack.push1(builder.ins().imul(a_high, b_high));2254}2255Operator::I32x4ExtMulLowI16x8U => {2256let (a, b) = pop2_with_bitcast(stack, I16X8, builder);2257let a_low = builder.ins().uwiden_low(a);2258let b_low = builder.ins().uwiden_low(b);2259stack.push1(builder.ins().imul(a_low, b_low));2260}2261Operator::I32x4ExtMulHighI16x8U => {2262let (a, b) = pop2_with_bitcast(stack, I16X8, builder);2263let a_high = builder.ins().uwiden_high(a);2264let b_high = builder.ins().uwiden_high(b);2265stack.push1(builder.ins().imul(a_high, b_high));2266}2267Operator::I64x2ExtMulLowI32x4S => {2268let (a, b) = pop2_with_bitcast(stack, I32X4, builder);2269let a_low = builder.ins().swiden_low(a);2270let b_low = builder.ins().swiden_low(b);2271stack.push1(builder.ins().imul(a_low, b_low));2272}2273Operator::I64x2ExtMulHighI32x4S => {2274let (a, b) = pop2_with_bitcast(stack, I32X4, builder);2275let a_high = builder.ins().swiden_high(a);2276let b_high = builder.ins().swiden_high(b);2277stack.push1(builder.ins().imul(a_high, b_high));2278}2279Operator::I64x2ExtMulLowI32x4U => {2280let (a, b) = pop2_with_bitcast(stack, I32X4, builder);2281let a_low = builder.ins().uwiden_low(a);2282let b_low = builder.ins().uwiden_low(b);2283stack.push1(builder.ins().imul(a_low, b_low));2284}2285Operator::I64x2ExtMulHighI32x4U => {2286let (a, b) = pop2_with_bitcast(stack, I32X4, builder);2287let a_high = builder.ins().uwiden_high(a);2288let b_high = builder.ins().uwiden_high(b);2289stack.push1(builder.ins().imul(a_high, b_high));2290}2291Operator::MemoryDiscard { .. } => {2292return Err(wasm_unsupported!(2293"proposed memory-control operator {:?}",2294op2295));2296}22972298Operator::F32x4RelaxedMax | Operator::F64x2RelaxedMax => {2299let ty = type_of(op);2300let (a, b) = pop2_with_bitcast(stack, ty, builder);2301stack.push1(2302if environ.relaxed_simd_deterministic() || !environ.is_x86() {2303// Deterministic semantics match the `fmax` instruction, or2304// the `fAAxBB.max` wasm instruction.2305builder.ins().fmax(a, b)2306} else {2307// Note that this matches the `pmax` translation which has2308// careful ordering of its operands to trigger2309// pattern-matches in the x86 backend.2310let cmp = builder.ins().fcmp(FloatCC::LessThan, a, b);2311let cmp = optionally_bitcast_vector(cmp, ty, builder);2312builder.ins().bitselect(cmp, b, a)2313},2314)2315}23162317Operator::F32x4RelaxedMin | Operator::F64x2RelaxedMin => {2318let ty = type_of(op);2319let (a, b) = pop2_with_bitcast(stack, ty, builder);2320stack.push1(2321if environ.relaxed_simd_deterministic() || !environ.is_x86() {2322// Deterministic semantics match the `fmin` instruction, or2323// the `fAAxBB.min` wasm instruction.2324builder.ins().fmin(a, b)2325} else {2326// Note that this matches the `pmin` translation which has2327// careful ordering of its operands to trigger2328// pattern-matches in the x86 backend.2329let cmp = builder.ins().fcmp(FloatCC::LessThan, b, a);2330let cmp = optionally_bitcast_vector(cmp, ty, builder);2331builder.ins().bitselect(cmp, b, a)2332},2333);2334}23352336Operator::I8x16RelaxedSwizzle => {2337let (a, b) = pop2_with_bitcast(stack, I8X16, builder);2338stack.push1(environ.relaxed_swizzle(builder, a, b));2339}23402341Operator::F32x4RelaxedMadd => {2342let (a, b, c) = pop3_with_bitcast(stack, type_of(op), builder);2343stack.push1(environ.fma_f32x4(builder, a, b, c));2344}2345Operator::F64x2RelaxedMadd => {2346let (a, b, c) = pop3_with_bitcast(stack, type_of(op), builder);2347stack.push1(environ.fma_f64x2(builder, a, b, c));2348}2349Operator::F32x4RelaxedNmadd => {2350let (a, b, c) = pop3_with_bitcast(stack, type_of(op), builder);2351let a = builder.ins().fneg(a);2352stack.push1(environ.fma_f32x4(builder, a, b, c));2353}2354Operator::F64x2RelaxedNmadd => {2355let (a, b, c) = pop3_with_bitcast(stack, type_of(op), builder);2356let a = builder.ins().fneg(a);2357stack.push1(environ.fma_f64x2(builder, a, b, c));2358}23592360Operator::I8x16RelaxedLaneselect2361| Operator::I16x8RelaxedLaneselect2362| Operator::I32x4RelaxedLaneselect2363| Operator::I64x2RelaxedLaneselect => {2364let ty = type_of(op);2365let (a, b, c) = pop3_with_bitcast(stack, ty, builder);2366// Note that the variable swaps here are intentional due to2367// the difference of the order of the wasm op and the clif2368// op.2369stack.push1(2370if environ.relaxed_simd_deterministic()2371|| !environ.use_x86_blendv_for_relaxed_laneselect(ty)2372{2373// Deterministic semantics are a `bitselect` along the lines2374// of the wasm `v128.bitselect` instruction.2375builder.ins().bitselect(c, a, b)2376} else {2377builder.ins().x86_blendv(c, a, b)2378},2379);2380}23812382Operator::I32x4RelaxedTruncF32x4S => {2383let a = pop1_with_bitcast(stack, F32X4, builder);2384stack.push1(2385if environ.relaxed_simd_deterministic() || !environ.is_x86() {2386// Deterministic semantics are to match the2387// `i32x4.trunc_sat_f32x4_s` instruction.2388builder.ins().fcvt_to_sint_sat(I32X4, a)2389} else {2390builder.ins().x86_cvtt2dq(I32X4, a)2391},2392)2393}2394Operator::I32x4RelaxedTruncF64x2SZero => {2395let a = pop1_with_bitcast(stack, F64X2, builder);2396let converted_a = if environ.relaxed_simd_deterministic() || !environ.is_x86() {2397// Deterministic semantics are to match the2398// `i32x4.trunc_sat_f64x2_s_zero` instruction.2399builder.ins().fcvt_to_sint_sat(I64X2, a)2400} else {2401builder.ins().x86_cvtt2dq(I64X2, a)2402};2403let handle = builder.func.dfg.constants.insert(vec![0u8; 16].into());2404let zero = builder.ins().vconst(I64X2, handle);24052406stack.push1(builder.ins().snarrow(converted_a, zero));2407}2408Operator::I16x8RelaxedQ15mulrS => {2409let (a, b) = pop2_with_bitcast(stack, I16X8, builder);2410stack.push1(2411if environ.relaxed_simd_deterministic()2412|| !environ.use_x86_pmulhrsw_for_relaxed_q15mul()2413{2414// Deterministic semantics are to match the2415// `i16x8.q15mulr_sat_s` instruction.2416builder.ins().sqmul_round_sat(a, b)2417} else {2418builder.ins().x86_pmulhrsw(a, b)2419},2420);2421}2422Operator::I16x8RelaxedDotI8x16I7x16S => {2423let (a, b) = pop2_with_bitcast(stack, I8X16, builder);2424stack.push1(2425if environ.relaxed_simd_deterministic() || !environ.use_x86_pmaddubsw_for_dot() {2426// Deterministic semantics are to treat both operands as2427// signed integers and perform the dot product.2428let alo = builder.ins().swiden_low(a);2429let blo = builder.ins().swiden_low(b);2430let lo = builder.ins().imul(alo, blo);2431let ahi = builder.ins().swiden_high(a);2432let bhi = builder.ins().swiden_high(b);2433let hi = builder.ins().imul(ahi, bhi);2434builder.ins().iadd_pairwise(lo, hi)2435} else {2436builder.ins().x86_pmaddubsw(a, b)2437},2438);2439}24402441Operator::I32x4RelaxedDotI8x16I7x16AddS => {2442let c = pop1_with_bitcast(stack, I32X4, builder);2443let (a, b) = pop2_with_bitcast(stack, I8X16, builder);2444let dot =2445if environ.relaxed_simd_deterministic() || !environ.use_x86_pmaddubsw_for_dot() {2446// Deterministic semantics are to treat both operands as2447// signed integers and perform the dot product.2448let alo = builder.ins().swiden_low(a);2449let blo = builder.ins().swiden_low(b);2450let lo = builder.ins().imul(alo, blo);2451let ahi = builder.ins().swiden_high(a);2452let bhi = builder.ins().swiden_high(b);2453let hi = builder.ins().imul(ahi, bhi);2454builder.ins().iadd_pairwise(lo, hi)2455} else {2456builder.ins().x86_pmaddubsw(a, b)2457};2458let dotlo = builder.ins().swiden_low(dot);2459let dothi = builder.ins().swiden_high(dot);2460let dot32 = builder.ins().iadd_pairwise(dotlo, dothi);2461stack.push1(builder.ins().iadd(dot32, c));2462}24632464Operator::BrOnNull { relative_depth } => {2465let r = stack.pop1();2466let [.., WasmValType::Ref(r_ty)] = operand_types else {2467unreachable!("validation")2468};2469let (br_destination, inputs) = translate_br_if_args(*relative_depth, stack);2470let is_null = environ.translate_ref_is_null(builder.cursor(), r, *r_ty)?;2471let else_block = builder.create_block();2472canonicalise_brif(builder, is_null, br_destination, inputs, else_block, &[]);24732474builder.seal_block(else_block); // The only predecessor is the current block.2475builder.switch_to_block(else_block);2476stack.push1(r);2477}2478Operator::BrOnNonNull { relative_depth } => {2479// We write this a bit differently from the spec to avoid an extra2480// block/branch and the typed accounting thereof. Instead of the2481// spec's approach, it's described as such:2482// Peek the value val from the stack.2483// If val is ref.null ht, then: pop the value val from the stack.2484// Else: Execute the instruction (br relative_depth).2485let r = stack.peek1();2486let [.., WasmValType::Ref(r_ty)] = operand_types else {2487unreachable!("validation")2488};2489let is_null = environ.translate_ref_is_null(builder.cursor(), r, *r_ty)?;2490let (br_destination, inputs) = translate_br_if_args(*relative_depth, stack);2491let else_block = builder.create_block();2492canonicalise_brif(builder, is_null, else_block, &[], br_destination, inputs);24932494// In the null case, pop the ref2495stack.pop1();24962497builder.seal_block(else_block); // The only predecessor is the current block.24982499// The rest of the translation operates on our is null case, which is2500// currently an empty block2501builder.switch_to_block(else_block);2502}2503Operator::CallRef { type_index } => {2504// Get function signature2505// `index` is the index of the function's signature and `table_index` is the index of2506// the table to search the function in.2507let type_index = TypeIndex::from_u32(*type_index);2508let sigref = environ.get_or_create_sig_ref(builder.func, type_index);2509let num_args = environ.num_params_for_function_type(type_index);2510let callee = stack.pop1();25112512// Bitcast any vector arguments to their default type, I8X16, before calling.2513let args = stack.peekn_mut(num_args);2514bitcast_wasm_params(environ, sigref, args, builder);25152516let inst_results = environ.translate_call_ref(2517builder,2518sigref,2519callee,2520stack.peekn(num_args),2521stack.handlers.handlers(),2522)?;25232524debug_assert_eq!(2525inst_results.len(),2526builder.func.dfg.signatures[sigref].returns.len(),2527"translate_call_ref results should match the call signature"2528);2529stack.popn(num_args);2530stack.pushn(&inst_results);2531}2532Operator::RefAsNonNull => {2533let r = stack.pop1();2534let [.., WasmValType::Ref(r_ty)] = operand_types else {2535unreachable!("validation")2536};2537let is_null = environ.translate_ref_is_null(builder.cursor(), r, *r_ty)?;2538environ.trapnz(builder, is_null, crate::TRAP_NULL_REFERENCE);2539stack.push1(r);2540}25412542Operator::RefI31 => {2543let val = stack.pop1();2544let i31ref = environ.translate_ref_i31(builder.cursor(), val)?;2545stack.push1(i31ref);2546}2547Operator::I31GetS => {2548let i31ref = stack.pop1();2549let val = environ.translate_i31_get_s(builder, i31ref)?;2550stack.push1(val);2551}2552Operator::I31GetU => {2553let i31ref = stack.pop1();2554let val = environ.translate_i31_get_u(builder, i31ref)?;2555stack.push1(val);2556}25572558Operator::StructNew { struct_type_index } => {2559let struct_type_index = TypeIndex::from_u32(*struct_type_index);2560let arity = environ.struct_fields_len(struct_type_index)?;2561let fields: StructFieldsVec = stack.peekn(arity).iter().copied().collect();2562stack.popn(arity);2563let struct_ref = environ.translate_struct_new(builder, struct_type_index, fields)?;2564stack.push1(struct_ref);2565}25662567Operator::StructNewDefault { struct_type_index } => {2568let struct_type_index = TypeIndex::from_u32(*struct_type_index);2569let struct_ref = environ.translate_struct_new_default(builder, struct_type_index)?;2570stack.push1(struct_ref);2571}25722573Operator::StructSet {2574struct_type_index,2575field_index,2576} => {2577let struct_type_index = TypeIndex::from_u32(*struct_type_index);2578let val = stack.pop1();2579let struct_ref = stack.pop1();2580environ.translate_struct_set(2581builder,2582struct_type_index,2583*field_index,2584struct_ref,2585val,2586)?;2587}25882589Operator::StructGetS {2590struct_type_index,2591field_index,2592} => {2593let struct_type_index = TypeIndex::from_u32(*struct_type_index);2594let struct_ref = stack.pop1();2595let val = environ.translate_struct_get(2596builder,2597struct_type_index,2598*field_index,2599struct_ref,2600Some(Extension::Sign),2601)?;2602stack.push1(val);2603}26042605Operator::StructGetU {2606struct_type_index,2607field_index,2608} => {2609let struct_type_index = TypeIndex::from_u32(*struct_type_index);2610let struct_ref = stack.pop1();2611let val = environ.translate_struct_get(2612builder,2613struct_type_index,2614*field_index,2615struct_ref,2616Some(Extension::Zero),2617)?;2618stack.push1(val);2619}26202621Operator::StructGet {2622struct_type_index,2623field_index,2624} => {2625let struct_type_index = TypeIndex::from_u32(*struct_type_index);2626let struct_ref = stack.pop1();2627let val = environ.translate_struct_get(2628builder,2629struct_type_index,2630*field_index,2631struct_ref,2632None,2633)?;2634stack.push1(val);2635}26362637Operator::ArrayNew { array_type_index } => {2638let array_type_index = TypeIndex::from_u32(*array_type_index);2639let (elem, len) = stack.pop2();2640let array_ref = environ.translate_array_new(builder, array_type_index, elem, len)?;2641stack.push1(array_ref);2642}2643Operator::ArrayNewDefault { array_type_index } => {2644let array_type_index = TypeIndex::from_u32(*array_type_index);2645let len = stack.pop1();2646let array_ref = environ.translate_array_new_default(builder, array_type_index, len)?;2647stack.push1(array_ref);2648}2649Operator::ArrayNewFixed {2650array_type_index,2651array_size,2652} => {2653let array_type_index = TypeIndex::from_u32(*array_type_index);2654let array_size = usize::try_from(*array_size).unwrap();2655let elems = stack.peekn(array_size);2656let array_ref = environ.translate_array_new_fixed(builder, array_type_index, elems)?;2657stack.popn(array_size);2658stack.push1(array_ref);2659}2660Operator::ArrayNewData {2661array_type_index,2662array_data_index,2663} => {2664let array_type_index = TypeIndex::from_u32(*array_type_index);2665let array_data_index = DataIndex::from_u32(*array_data_index);2666let (data_offset, len) = stack.pop2();2667let array_ref = environ.translate_array_new_data(2668builder,2669array_type_index,2670array_data_index,2671data_offset,2672len,2673)?;2674stack.push1(array_ref);2675}2676Operator::ArrayNewElem {2677array_type_index,2678array_elem_index,2679} => {2680let array_type_index = TypeIndex::from_u32(*array_type_index);2681let array_elem_index = ElemIndex::from_u32(*array_elem_index);2682let (elem_offset, len) = stack.pop2();2683let array_ref = environ.translate_array_new_elem(2684builder,2685array_type_index,2686array_elem_index,2687elem_offset,2688len,2689)?;2690stack.push1(array_ref);2691}2692Operator::ArrayCopy {2693array_type_index_dst,2694array_type_index_src,2695} => {2696let array_type_index_dst = TypeIndex::from_u32(*array_type_index_dst);2697let array_type_index_src = TypeIndex::from_u32(*array_type_index_src);2698let (dst_array, dst_index, src_array, src_index, len) = stack.pop5();2699environ.translate_array_copy(2700builder,2701array_type_index_dst,2702dst_array,2703dst_index,2704array_type_index_src,2705src_array,2706src_index,2707len,2708)?;2709}2710Operator::ArrayFill { array_type_index } => {2711let array_type_index = TypeIndex::from_u32(*array_type_index);2712let (array, index, val, len) = stack.pop4();2713environ.translate_array_fill(builder, array_type_index, array, index, val, len)?;2714}2715Operator::ArrayInitData {2716array_type_index,2717array_data_index,2718} => {2719let array_type_index = TypeIndex::from_u32(*array_type_index);2720let array_data_index = DataIndex::from_u32(*array_data_index);2721let (array, dst_index, src_index, len) = stack.pop4();2722environ.translate_array_init_data(2723builder,2724array_type_index,2725array,2726dst_index,2727array_data_index,2728src_index,2729len,2730)?;2731}2732Operator::ArrayInitElem {2733array_type_index,2734array_elem_index,2735} => {2736let array_type_index = TypeIndex::from_u32(*array_type_index);2737let array_elem_index = ElemIndex::from_u32(*array_elem_index);2738let (array, dst_index, src_index, len) = stack.pop4();2739environ.translate_array_init_elem(2740builder,2741array_type_index,2742array,2743dst_index,2744array_elem_index,2745src_index,2746len,2747)?;2748}2749Operator::ArrayLen => {2750let array = stack.pop1();2751let len = environ.translate_array_len(builder, array)?;2752stack.push1(len);2753}2754Operator::ArrayGet { array_type_index } => {2755let array_type_index = TypeIndex::from_u32(*array_type_index);2756let (array, index) = stack.pop2();2757let elem =2758environ.translate_array_get(builder, array_type_index, array, index, None)?;2759stack.push1(elem);2760}2761Operator::ArrayGetS { array_type_index } => {2762let array_type_index = TypeIndex::from_u32(*array_type_index);2763let (array, index) = stack.pop2();2764let elem = environ.translate_array_get(2765builder,2766array_type_index,2767array,2768index,2769Some(Extension::Sign),2770)?;2771stack.push1(elem);2772}2773Operator::ArrayGetU { array_type_index } => {2774let array_type_index = TypeIndex::from_u32(*array_type_index);2775let (array, index) = stack.pop2();2776let elem = environ.translate_array_get(2777builder,2778array_type_index,2779array,2780index,2781Some(Extension::Zero),2782)?;2783stack.push1(elem);2784}2785Operator::ArraySet { array_type_index } => {2786let array_type_index = TypeIndex::from_u32(*array_type_index);2787let (array, index, elem) = stack.pop3();2788environ.translate_array_set(builder, array_type_index, array, index, elem)?;2789}2790Operator::RefEq => {2791let (r1, r2) = stack.pop2();2792let eq = builder.ins().icmp(ir::condcodes::IntCC::Equal, r1, r2);2793let eq = builder.ins().uextend(ir::types::I32, eq);2794stack.push1(eq);2795}2796Operator::RefTestNonNull { hty } => {2797let r = stack.pop1();2798let [.., WasmValType::Ref(r_ty)] = operand_types else {2799unreachable!("validation")2800};2801let heap_type = environ.convert_heap_type(*hty)?;2802let result = environ.translate_ref_test(2803builder,2804WasmRefType {2805heap_type,2806nullable: false,2807},2808r,2809*r_ty,2810)?;2811stack.push1(result);2812}2813Operator::RefTestNullable { hty } => {2814let r = stack.pop1();2815let [.., WasmValType::Ref(r_ty)] = operand_types else {2816unreachable!("validation")2817};2818let heap_type = environ.convert_heap_type(*hty)?;2819let result = environ.translate_ref_test(2820builder,2821WasmRefType {2822heap_type,2823nullable: true,2824},2825r,2826*r_ty,2827)?;2828stack.push1(result);2829}2830Operator::RefCastNonNull { hty } => {2831let r = stack.pop1();2832let [.., WasmValType::Ref(r_ty)] = operand_types else {2833unreachable!("validation")2834};2835let heap_type = environ.convert_heap_type(*hty)?;2836let cast_okay = environ.translate_ref_test(2837builder,2838WasmRefType {2839heap_type,2840nullable: false,2841},2842r,2843*r_ty,2844)?;2845environ.trapz(builder, cast_okay, crate::TRAP_CAST_FAILURE);2846stack.push1(r);2847}2848Operator::RefCastNullable { hty } => {2849let r = stack.pop1();2850let [.., WasmValType::Ref(r_ty)] = operand_types else {2851unreachable!("validation")2852};2853let heap_type = environ.convert_heap_type(*hty)?;2854let cast_okay = environ.translate_ref_test(2855builder,2856WasmRefType {2857heap_type,2858nullable: true,2859},2860r,2861*r_ty,2862)?;2863environ.trapz(builder, cast_okay, crate::TRAP_CAST_FAILURE);2864stack.push1(r);2865}2866Operator::BrOnCast {2867relative_depth,2868to_ref_type,2869from_ref_type: _,2870} => {2871let r = stack.peek1();2872let [.., WasmValType::Ref(r_ty)] = operand_types else {2873unreachable!("validation")2874};28752876let to_ref_type = environ.convert_ref_type(*to_ref_type)?;2877let cast_is_okay = environ.translate_ref_test(builder, to_ref_type, r, *r_ty)?;28782879let (cast_succeeds_block, inputs) = translate_br_if_args(*relative_depth, stack);2880let cast_fails_block = builder.create_block();2881canonicalise_brif(2882builder,2883cast_is_okay,2884cast_succeeds_block,2885inputs,2886cast_fails_block,2887&[2888// NB: the `cast_fails_block` is dominated by the current2889// block, and therefore doesn't need any block params.2890],2891);28922893// The only predecessor is the current block.2894builder.seal_block(cast_fails_block);28952896// The next Wasm instruction is executed when the cast failed and we2897// did not branch away.2898builder.switch_to_block(cast_fails_block);2899}2900Operator::BrOnCastFail {2901relative_depth,2902to_ref_type,2903from_ref_type: _,2904} => {2905let r = stack.peek1();2906let [.., WasmValType::Ref(r_ty)] = operand_types else {2907unreachable!("validation")2908};29092910let to_ref_type = environ.convert_ref_type(*to_ref_type)?;2911let cast_is_okay = environ.translate_ref_test(builder, to_ref_type, r, *r_ty)?;29122913let (cast_fails_block, inputs) = translate_br_if_args(*relative_depth, stack);2914let cast_succeeds_block = builder.create_block();2915canonicalise_brif(2916builder,2917cast_is_okay,2918cast_succeeds_block,2919&[2920// NB: the `cast_succeeds_block` is dominated by the current2921// block, and therefore doesn't need any block params.2922],2923cast_fails_block,2924inputs,2925);29262927// The only predecessor is the current block.2928builder.seal_block(cast_succeeds_block);29292930// The next Wasm instruction is executed when the cast succeeded and2931// we did not branch away.2932builder.switch_to_block(cast_succeeds_block);2933}29342935Operator::AnyConvertExtern => {2936// Pop an `externref`, push an `anyref`. But they have the same2937// representation, so we don't actually need to do anything.2938}2939Operator::ExternConvertAny => {2940// Pop an `anyref`, push an `externref`. But they have the same2941// representation, so we don't actually need to do anything.2942}29432944Operator::ContNew { cont_type_index } => {2945let cont_type_index = TypeIndex::from_u32(*cont_type_index);2946let arg_types: SmallVec<[_; 8]> = environ2947.continuation_arguments(cont_type_index)2948.to_smallvec();2949let result_types: SmallVec<[_; 8]> =2950environ.continuation_returns(cont_type_index).to_smallvec();2951let r = stack.pop1();2952let contobj = environ.translate_cont_new(builder, r, &arg_types, &result_types)?;2953stack.push1(contobj);2954}2955Operator::ContBind {2956argument_index,2957result_index,2958} => {2959let src_types = environ.continuation_arguments(TypeIndex::from_u32(*argument_index));2960let dst_arity = environ2961.continuation_arguments(TypeIndex::from_u32(*result_index))2962.len();2963let arg_count = src_types.len() - dst_arity;29642965let arg_types = &src_types[0..arg_count];2966for arg_type in arg_types {2967// We can't bind GC objects using cont.bind at the moment: We2968// don't have the necessary infrastructure to traverse the2969// buffers used by cont.bind when looking for GC roots. Thus,2970// this crude check ensures that these buffers can never contain2971// GC roots to begin with.2972if arg_type.is_vmgcref_type_and_not_i31() {2973return Err(wasmtime_environ::WasmError::Unsupported(2974"cont.bind does not support GC types at the moment".into(),2975));2976}2977}29782979let (original_contobj, args) = stack.peekn(arg_count + 1).split_last().unwrap();29802981let new_contobj = environ.translate_cont_bind(builder, *original_contobj, args);29822983stack.popn(arg_count + 1);2984stack.push1(new_contobj);2985}2986Operator::Suspend { tag_index } => {2987let tag_index = TagIndex::from_u32(*tag_index);2988let param_types = environ.tag_params(tag_index).to_vec();2989let return_types: SmallVec<[_; 8]> = environ2990.tag_returns(tag_index)2991.iter()2992.map(|ty| crate::value_type(environ.isa(), *ty))2993.collect();29942995let params = stack.peekn(param_types.len());2996let param_count = params.len();29972998let return_values =2999environ.translate_suspend(builder, tag_index.as_u32(), params, &return_types);30003001stack.popn(param_count);3002stack.pushn(&return_values);3003}3004Operator::Resume {3005cont_type_index,3006resume_table: wasm_resume_table,3007} => {3008// We translate the block indices in the wasm resume_table to actual Blocks.3009let mut clif_resume_table = vec![];3010for handle in &wasm_resume_table.handlers {3011match handle {3012wasmparser::Handle::OnLabel { tag, label } => {3013let i = stack.control_stack.len() - 1 - (*label as usize);3014let frame = &mut stack.control_stack[i];3015// This is side-effecting!3016frame.set_branched_to_exit();3017clif_resume_table.push((*tag, Some(frame.br_destination())));3018}3019wasmparser::Handle::OnSwitch { tag } => {3020clif_resume_table.push((*tag, None));3021}3022}3023}30243025let cont_type_index = TypeIndex::from_u32(*cont_type_index);3026let arity = environ.continuation_arguments(cont_type_index).len();3027let (contobj, call_args) = stack.peekn(arity + 1).split_last().unwrap();30283029let cont_return_vals = environ.translate_resume(3030builder,3031cont_type_index.as_u32(),3032*contobj,3033call_args,3034&clif_resume_table,3035)?;30363037stack.popn(arity + 1); // arguments + continuation3038stack.pushn(&cont_return_vals);3039}3040Operator::ResumeThrow {3041cont_type_index: _,3042tag_index: _,3043resume_table: _,3044} => {3045// TODO(10248) This depends on exception handling3046return Err(wasmtime_environ::WasmError::Unsupported(3047"resume.throw instructions not supported, yet".to_string(),3048));3049}3050Operator::Switch {3051cont_type_index,3052tag_index,3053} => {3054// Arguments of the continuation we are going to switch to3055let continuation_argument_types: SmallVec<[_; 8]> = environ3056.continuation_arguments(TypeIndex::from_u32(*cont_type_index))3057.to_smallvec();3058// Arity includes the continuation argument3059let arity = continuation_argument_types.len();3060let (contobj, switch_args) = stack.peekn(arity).split_last().unwrap();30613062// Type of the continuation we are going to create by suspending the3063// currently running stack3064let current_continuation_type = continuation_argument_types.last().unwrap();3065let current_continuation_type = current_continuation_type.unwrap_ref_type();30663067// Argument types of current_continuation_type. These will in turn3068// be the types of the arguments we receive when someone switches3069// back to this switch instruction3070let current_continuation_arg_types: SmallVec<[_; 8]> =3071match current_continuation_type.heap_type {3072WasmHeapType::ConcreteCont(index) => {3073let mti = index3074.as_module_type_index()3075.expect("Only supporting module type indices on switch for now");30763077environ3078.continuation_arguments(TypeIndex::from_u32(mti.as_u32()))3079.iter()3080.map(|ty| crate::value_type(environ.isa(), *ty))3081.collect()3082}3083_ => panic!("Invalid type on switch"),3084};30853086let switch_return_values = environ.translate_switch(3087builder,3088*tag_index,3089*contobj,3090switch_args,3091¤t_continuation_arg_types,3092)?;30933094stack.popn(arity);3095stack.pushn(&switch_return_values)3096}30973098Operator::GlobalAtomicGet { .. }3099| Operator::GlobalAtomicSet { .. }3100| Operator::GlobalAtomicRmwAdd { .. }3101| Operator::GlobalAtomicRmwSub { .. }3102| Operator::GlobalAtomicRmwOr { .. }3103| Operator::GlobalAtomicRmwXor { .. }3104| Operator::GlobalAtomicRmwAnd { .. }3105| Operator::GlobalAtomicRmwXchg { .. }3106| Operator::GlobalAtomicRmwCmpxchg { .. }3107| Operator::TableAtomicGet { .. }3108| Operator::TableAtomicSet { .. }3109| Operator::TableAtomicRmwXchg { .. }3110| Operator::TableAtomicRmwCmpxchg { .. }3111| Operator::StructAtomicGet { .. }3112| Operator::StructAtomicGetS { .. }3113| Operator::StructAtomicGetU { .. }3114| Operator::StructAtomicSet { .. }3115| Operator::StructAtomicRmwAdd { .. }3116| Operator::StructAtomicRmwSub { .. }3117| Operator::StructAtomicRmwOr { .. }3118| Operator::StructAtomicRmwXor { .. }3119| Operator::StructAtomicRmwAnd { .. }3120| Operator::StructAtomicRmwXchg { .. }3121| Operator::StructAtomicRmwCmpxchg { .. }3122| Operator::ArrayAtomicGet { .. }3123| Operator::ArrayAtomicGetS { .. }3124| Operator::ArrayAtomicGetU { .. }3125| Operator::ArrayAtomicSet { .. }3126| Operator::ArrayAtomicRmwAdd { .. }3127| Operator::ArrayAtomicRmwSub { .. }3128| Operator::ArrayAtomicRmwOr { .. }3129| Operator::ArrayAtomicRmwXor { .. }3130| Operator::ArrayAtomicRmwAnd { .. }3131| Operator::ArrayAtomicRmwXchg { .. }3132| Operator::ArrayAtomicRmwCmpxchg { .. }3133| Operator::RefI31Shared { .. } => {3134return Err(wasm_unsupported!(3135"shared-everything-threads operators are not yet implemented"3136));3137}31383139Operator::I64MulWideS => {3140let (arg1, arg2) = stack.pop2();3141let arg1 = builder.ins().sextend(I128, arg1);3142let arg2 = builder.ins().sextend(I128, arg2);3143let result = builder.ins().imul(arg1, arg2);3144let (lo, hi) = builder.ins().isplit(result);3145stack.push2(lo, hi);3146}3147Operator::I64MulWideU => {3148let (arg1, arg2) = stack.pop2();3149let arg1 = builder.ins().uextend(I128, arg1);3150let arg2 = builder.ins().uextend(I128, arg2);3151let result = builder.ins().imul(arg1, arg2);3152let (lo, hi) = builder.ins().isplit(result);3153stack.push2(lo, hi);3154}3155Operator::I64Add128 => {3156let (arg1, arg2, arg3, arg4) = stack.pop4();3157let arg1 = builder.ins().iconcat(arg1, arg2);3158let arg2 = builder.ins().iconcat(arg3, arg4);3159let result = builder.ins().iadd(arg1, arg2);3160let (res1, res2) = builder.ins().isplit(result);3161stack.push2(res1, res2);3162}3163Operator::I64Sub128 => {3164let (arg1, arg2, arg3, arg4) = stack.pop4();3165let arg1 = builder.ins().iconcat(arg1, arg2);3166let arg2 = builder.ins().iconcat(arg3, arg4);3167let result = builder.ins().isub(arg1, arg2);3168let (res1, res2) = builder.ins().isplit(result);3169stack.push2(res1, res2);3170}31713172// catch-all as `Operator` is `#[non_exhaustive]`3173op => return Err(wasm_unsupported!("operator {op:?}")),3174};3175Ok(())3176}31773178/// Deals with a Wasm instruction located in an unreachable portion of the code. Most of them3179/// are dropped but special ones like `End` or `Else` signal the potential end of the unreachable3180/// portion so the translation state must be updated accordingly.3181fn translate_unreachable_operator(3182validator: &FuncValidator<impl WasmModuleResources>,3183op: &Operator,3184builder: &mut FunctionBuilder,3185stack: &mut FuncTranslationStacks,3186environ: &mut FuncEnvironment<'_>,3187) -> WasmResult<()> {3188debug_assert!(!stack.reachable);3189match *op {3190Operator::If { blockty } => {3191// Push a placeholder control stack entry. The if isn't reachable,3192// so we don't have any branches anywhere.3193stack.push_if(3194ir::Block::reserved_value(),3195ElseData::NoElse {3196branch_inst: ir::Inst::reserved_value(),3197placeholder: ir::Block::reserved_value(),3198},31990,32000,3201blockty,3202);3203}3204Operator::Loop { blockty: _ }3205| Operator::Block { blockty: _ }3206| Operator::TryTable { try_table: _ } => {3207stack.push_block(ir::Block::reserved_value(), 0, 0);3208}3209Operator::Else => {3210let i = stack.control_stack.len() - 1;3211match stack.control_stack[i] {3212ControlStackFrame::If {3213ref else_data,3214head_is_reachable,3215ref mut consequent_ends_reachable,3216blocktype,3217..3218} => {3219debug_assert!(consequent_ends_reachable.is_none());3220*consequent_ends_reachable = Some(stack.reachable);32213222if head_is_reachable {3223// We have a branch from the head of the `if` to the `else`.3224stack.reachable = true;32253226let else_block = match *else_data {3227ElseData::NoElse {3228branch_inst,3229placeholder,3230} => {3231let (params, _results) =3232blocktype_params_results(validator, blocktype)?;3233let else_block = block_with_params(builder, params, environ)?;3234let frame = stack.control_stack.last().unwrap();3235frame.truncate_value_stack_to_else_params(&mut stack.stack);32363237// We change the target of the branch instruction.3238builder.change_jump_destination(3239branch_inst,3240placeholder,3241else_block,3242);3243builder.seal_block(else_block);3244else_block3245}3246ElseData::WithElse { else_block } => {3247let frame = stack.control_stack.last().unwrap();3248frame.truncate_value_stack_to_else_params(&mut stack.stack);3249else_block3250}3251};32523253builder.switch_to_block(else_block);32543255// Again, no need to push the parameters for the `else`,3256// since we already did when we saw the original `if`. See3257// the comment for translating `Operator::Else` in3258// `translate_operator` for details.3259}3260}3261_ => unreachable!(),3262}3263}3264Operator::End => {3265let value_stack = &mut stack.stack;3266let control_stack = &mut stack.control_stack;3267let frame = control_stack.pop().unwrap();32683269frame.restore_catch_handlers(&mut stack.handlers, builder);32703271// Pop unused parameters from stack.3272frame.truncate_value_stack_to_original_size(value_stack);32733274let reachable_anyway = match frame {3275// If it is a loop we also have to seal the body loop block3276ControlStackFrame::Loop { header, .. } => {3277builder.seal_block(header);3278// And loops can't have branches to the end.3279false3280}3281// If we never set `consequent_ends_reachable` then that means3282// we are finishing the consequent now, and there was no3283// `else`. Whether the following block is reachable depends only3284// on if the head was reachable.3285ControlStackFrame::If {3286head_is_reachable,3287consequent_ends_reachable: None,3288..3289} => head_is_reachable,3290// Since we are only in this function when in unreachable code,3291// we know that the alternative just ended unreachable. Whether3292// the following block is reachable depends on if the consequent3293// ended reachable or not.3294ControlStackFrame::If {3295head_is_reachable,3296consequent_ends_reachable: Some(consequent_ends_reachable),3297..3298} => head_is_reachable && consequent_ends_reachable,3299// All other control constructs are already handled.3300_ => false,3301};33023303if frame.exit_is_branched_to() || reachable_anyway {3304builder.switch_to_block(frame.following_code());3305builder.seal_block(frame.following_code());33063307// And add the return values of the block but only if the next block is reachable3308// (which corresponds to testing if the stack depth is 1)3309value_stack.extend_from_slice(builder.block_params(frame.following_code()));3310stack.reachable = true;3311}3312}3313_ => {3314// We don't translate because this is unreachable code3315}3316}33173318Ok(())3319}33203321/// This function is a generalized helper for validating that a wasm-supplied3322/// heap address is in-bounds.3323///3324/// This function takes a litany of parameters and requires that the *Wasm*3325/// address to be verified is at the top of the stack in `state`. This will3326/// generate necessary IR to validate that the heap address is correctly3327/// in-bounds, and various parameters are returned describing the valid *native*3328/// heap address if execution reaches that point.3329///3330/// Returns `None` when the Wasm access will unconditionally trap.3331///3332/// Returns `(flags, wasm_addr, native_addr)`.3333fn prepare_addr(3334memarg: &MemArg,3335access_size: u8,3336builder: &mut FunctionBuilder,3337stack: &mut FuncTranslationStacks,3338environ: &mut FuncEnvironment<'_>,3339) -> WasmResult<Reachability<(MemFlags, Value, Value)>> {3340let index = stack.pop1();33413342let memory_index = MemoryIndex::from_u32(memarg.memory);3343let heap = environ.get_or_create_heap(builder.func, memory_index);33443345// How exactly the bounds check is performed here and what it's performed3346// on is a bit tricky. Generally we want to rely on access violations (e.g.3347// segfaults) to generate traps since that means we don't have to bounds3348// check anything explicitly.3349//3350// (1) If we don't have a guard page of unmapped memory, though, then we3351// can't rely on this trapping behavior through segfaults. Instead we need3352// to bounds-check the entire memory access here which is everything from3353// `addr32 + offset` to `addr32 + offset + width` (not inclusive). In this3354// scenario our adjusted offset that we're checking is `memarg.offset +3355// access_size`. Note that we do saturating arithmetic here to avoid3356// overflow. The addition here is in the 64-bit space, which means that3357// we'll never overflow for 32-bit wasm but for 64-bit this is an issue. If3358// our effective offset is u64::MAX though then it's impossible for for3359// that to actually be a valid offset because otherwise the wasm linear3360// memory would take all of the host memory!3361//3362// (2) If we have a guard page, however, then we can perform a further3363// optimization of the generated code by only checking multiples of the3364// offset-guard size to be more CSE-friendly. Knowing that we have at least3365// 1 page of a guard page we're then able to disregard the `width` since we3366// know it's always less than one page. Our bounds check will be for the3367// first byte which will either succeed and be guaranteed to fault if it's3368// actually out of bounds, or the bounds check itself will fail. In any case3369// we assert that the width is reasonably small for now so this assumption3370// can be adjusted in the future if we get larger widths.3371//3372// Put another way we can say, where `y < offset_guard_size`:3373//3374// n * offset_guard_size + y = offset3375//3376// We'll then pass `n * offset_guard_size` as the bounds check value. If3377// this traps then our `offset` would have trapped anyway. If this check3378// passes we know3379//3380// addr32 + n * offset_guard_size < bound3381//3382// which means3383//3384// addr32 + n * offset_guard_size + y < bound + offset_guard_size3385//3386// because `y < offset_guard_size`, which then means:3387//3388// addr32 + offset < bound + offset_guard_size3389//3390// Since we know that that guard size bytes are all unmapped we're3391// guaranteed that `offset` and the `width` bytes after it are either3392// in-bounds or will hit the guard page, meaning we'll get the desired3393// semantics we want.3394//3395// ---3396//3397// With all that in mind remember that the goal is to bounds check as few3398// things as possible. To facilitate this the "fast path" is expected to be3399// hit like so:3400//3401// * For wasm32, wasmtime defaults to 4gb "static" memories with 2gb guard3402// regions. This means that for all offsets <=2gb, we hit the optimized3403// case for `heap_addr` on static memories 4gb in size in cranelift's3404// legalization of `heap_addr`, eliding the bounds check entirely.3405//3406// * For wasm64 offsets <=2gb will generate a single `heap_addr`3407// instruction, but at this time all heaps are "dynamic" which means that3408// a single bounds check is forced. Ideally we'd do better here, but3409// that's the current state of affairs.3410//3411// Basically we assume that most configurations have a guard page and most3412// offsets in `memarg` are <=2gb, which means we get the fast path of one3413// `heap_addr` instruction plus a hardcoded i32-offset in memory-related3414// instructions.3415let heap = environ.heaps()[heap].clone();3416let addr = match u32::try_from(memarg.offset) {3417// If our offset fits within a u32, then we can place the it into the3418// offset immediate of the `heap_addr` instruction.3419Ok(offset) => bounds_check_and_compute_addr(3420builder,3421environ,3422&heap,3423index,3424BoundsCheck::StaticOffset {3425offset,3426access_size,3427},3428ir::TrapCode::HEAP_OUT_OF_BOUNDS,3429),34303431// If the offset doesn't fit within a u32, then we can't pass it3432// directly into `heap_addr`.3433//3434// One reasonable question you might ask is "why not?". There's no3435// fundamental reason why `heap_addr` *must* take a 32-bit offset. The3436// reason this isn't done, though, is that blindly changing the offset3437// to a 64-bit offset increases the size of the `InstructionData` enum3438// in cranelift by 8 bytes (16 to 24). This can have significant3439// performance implications so the conclusion when this was written was3440// that we shouldn't do that.3441//3442// Without the ability to put the whole offset into the `heap_addr`3443// instruction we need to fold the offset into the address itself with3444// an unsigned addition. In doing so though we need to check for3445// overflow because that would mean the address is out-of-bounds (wasm3446// bounds checks happen on the effective 33 or 65 bit address once the3447// offset is factored in).3448//3449// Once we have the effective address, offset already folded in, then3450// `heap_addr` is used to verify that the address is indeed in-bounds.3451//3452// Note that this is generating what's likely to be at least two3453// branches, one for the overflow and one for the bounds check itself.3454// For now though that should hopefully be ok since 4gb+ offsets are3455// relatively odd/rare. In the future if needed we can look into3456// optimizing this more.3457Err(_) => {3458let offset = builder3459.ins()3460.iconst(heap.index_type(), memarg.offset.cast_signed());3461let adjusted_index = environ.uadd_overflow_trap(3462builder,3463index,3464offset,3465ir::TrapCode::HEAP_OUT_OF_BOUNDS,3466);3467bounds_check_and_compute_addr(3468builder,3469environ,3470&heap,3471adjusted_index,3472BoundsCheck::StaticOffset {3473offset: 0,3474access_size,3475},3476ir::TrapCode::HEAP_OUT_OF_BOUNDS,3477)3478}3479};3480let addr = match addr {3481Reachability::Unreachable => return Ok(Reachability::Unreachable),3482Reachability::Reachable(a) => a,3483};34843485// Note that we don't set `is_aligned` here, even if the load instruction's3486// alignment immediate may says it's aligned, because WebAssembly's3487// immediate field is just a hint, while Cranelift's aligned flag needs a3488// guarantee. WebAssembly memory accesses are always little-endian.3489let mut flags = MemFlags::new();3490flags.set_endianness(ir::Endianness::Little);34913492if heap.pcc_memory_type.is_some() {3493// Proof-carrying code is enabled; check this memory access.3494flags.set_checked();3495}34963497// The access occurs to the `heap` disjoint category of abstract3498// state. This may allow alias analysis to merge redundant loads,3499// etc. when heap accesses occur interleaved with other (table,3500// vmctx, stack) accesses.3501flags.set_alias_region(Some(ir::AliasRegion::Heap));35023503Ok(Reachability::Reachable((flags, index, addr)))3504}35053506fn align_atomic_addr(3507memarg: &MemArg,3508loaded_bytes: u8,3509builder: &mut FunctionBuilder,3510stack: &mut FuncTranslationStacks,3511environ: &mut FuncEnvironment<'_>,3512) {3513// Atomic addresses must all be aligned correctly, and for now we check3514// alignment before we check out-of-bounds-ness. The order of this check may3515// need to be updated depending on the outcome of the official threads3516// proposal itself.3517//3518// Note that with an offset>0 we generate an `iadd_imm` where the result is3519// thrown away after the offset check. This may truncate the offset and the3520// result may overflow as well, but those conditions won't affect the3521// alignment check itself. This can probably be optimized better and we3522// should do so in the future as well.3523if loaded_bytes > 1 {3524let addr = stack.pop1(); // "peek" via pop then push3525stack.push1(addr);3526let effective_addr = if memarg.offset == 0 {3527addr3528} else {3529builder.ins().iadd_imm(addr, memarg.offset.cast_signed())3530};3531debug_assert!(loaded_bytes.is_power_of_two());3532let misalignment = builder3533.ins()3534.band_imm(effective_addr, i64::from(loaded_bytes - 1));3535let f = builder.ins().icmp_imm(IntCC::NotEqual, misalignment, 0);3536environ.trapnz(builder, f, crate::TRAP_HEAP_MISALIGNED);3537}3538}35393540/// Like `prepare_addr` but for atomic accesses.3541///3542/// Returns `None` when the Wasm access will unconditionally trap.3543fn prepare_atomic_addr(3544memarg: &MemArg,3545loaded_bytes: u8,3546builder: &mut FunctionBuilder,3547stack: &mut FuncTranslationStacks,3548environ: &mut FuncEnvironment<'_>,3549) -> WasmResult<Reachability<(MemFlags, Value, Value)>> {3550align_atomic_addr(memarg, loaded_bytes, builder, stack, environ);3551prepare_addr(memarg, loaded_bytes, builder, stack, environ)3552}35533554/// Translate a load instruction.3555///3556/// Returns the execution state's reachability after the load is translated.3557fn translate_load(3558memarg: &MemArg,3559opcode: ir::Opcode,3560result_ty: Type,3561builder: &mut FunctionBuilder,3562stack: &mut FuncTranslationStacks,3563environ: &mut FuncEnvironment<'_>,3564) -> WasmResult<Reachability<()>> {3565let mem_op_size = mem_op_size(opcode, result_ty);3566let (flags, wasm_index, base) =3567match prepare_addr(memarg, mem_op_size, builder, stack, environ)? {3568Reachability::Unreachable => return Ok(Reachability::Unreachable),3569Reachability::Reachable((f, i, b)) => (f, i, b),3570};35713572environ.before_load(builder, mem_op_size, wasm_index, memarg.offset);35733574let (load, dfg) = builder3575.ins()3576.Load(opcode, result_ty, flags, Offset32::new(0), base);3577stack.push1(dfg.first_result(load));3578Ok(Reachability::Reachable(()))3579}35803581/// Translate a store instruction.3582fn translate_store(3583memarg: &MemArg,3584opcode: ir::Opcode,3585builder: &mut FunctionBuilder,3586stack: &mut FuncTranslationStacks,3587environ: &mut FuncEnvironment<'_>,3588) -> WasmResult<()> {3589let val = stack.pop1();3590let val_ty = builder.func.dfg.value_type(val);3591let mem_op_size = mem_op_size(opcode, val_ty);35923593let (flags, wasm_index, base) = unwrap_or_return_unreachable_state!(3594stack,3595prepare_addr(memarg, mem_op_size, builder, stack, environ)?3596);35973598environ.before_store(builder, mem_op_size, wasm_index, memarg.offset);35993600builder3601.ins()3602.Store(opcode, val_ty, flags, Offset32::new(0), val, base);3603Ok(())3604}36053606fn mem_op_size(opcode: ir::Opcode, ty: Type) -> u8 {3607match opcode {3608ir::Opcode::Istore8 | ir::Opcode::Sload8 | ir::Opcode::Uload8 => 1,3609ir::Opcode::Istore16 | ir::Opcode::Sload16 | ir::Opcode::Uload16 => 2,3610ir::Opcode::Istore32 | ir::Opcode::Sload32 | ir::Opcode::Uload32 => 4,3611ir::Opcode::Store | ir::Opcode::Load => u8::try_from(ty.bytes()).unwrap(),3612_ => panic!("unknown size of mem op for {opcode:?}"),3613}3614}36153616fn translate_icmp(cc: IntCC, builder: &mut FunctionBuilder, stack: &mut FuncTranslationStacks) {3617let (arg0, arg1) = stack.pop2();3618let val = builder.ins().icmp(cc, arg0, arg1);3619stack.push1(builder.ins().uextend(I32, val));3620}36213622fn translate_atomic_rmw(3623widened_ty: Type,3624access_ty: Type,3625op: AtomicRmwOp,3626memarg: &MemArg,3627builder: &mut FunctionBuilder,3628stack: &mut FuncTranslationStacks,3629environ: &mut FuncEnvironment<'_>,3630) -> WasmResult<()> {3631let mut arg2 = stack.pop1();3632let arg2_ty = builder.func.dfg.value_type(arg2);36333634// The operation is performed at type `access_ty`, and the old value is zero-extended3635// to type `widened_ty`.3636match access_ty {3637I8 | I16 | I32 | I64 => {}3638_ => {3639return Err(wasm_unsupported!(3640"atomic_rmw: unsupported access type {:?}",3641access_ty3642));3643}3644};3645let w_ty_ok = match widened_ty {3646I32 | I64 => true,3647_ => false,3648};3649assert!(w_ty_ok && widened_ty.bytes() >= access_ty.bytes());36503651assert!(arg2_ty.bytes() >= access_ty.bytes());3652if arg2_ty.bytes() > access_ty.bytes() {3653arg2 = builder.ins().ireduce(access_ty, arg2);3654}36553656let (flags, _, addr) = unwrap_or_return_unreachable_state!(3657stack,3658prepare_atomic_addr(3659memarg,3660u8::try_from(access_ty.bytes()).unwrap(),3661builder,3662stack,3663environ,3664)?3665);36663667let mut res = builder.ins().atomic_rmw(access_ty, flags, op, addr, arg2);3668if access_ty != widened_ty {3669res = builder.ins().uextend(widened_ty, res);3670}3671stack.push1(res);3672Ok(())3673}36743675fn translate_atomic_cas(3676widened_ty: Type,3677access_ty: Type,3678memarg: &MemArg,3679builder: &mut FunctionBuilder,3680stack: &mut FuncTranslationStacks,3681environ: &mut FuncEnvironment<'_>,3682) -> WasmResult<()> {3683let (mut expected, mut replacement) = stack.pop2();3684let expected_ty = builder.func.dfg.value_type(expected);3685let replacement_ty = builder.func.dfg.value_type(replacement);36863687// The compare-and-swap is performed at type `access_ty`, and the old value is zero-extended3688// to type `widened_ty`.3689match access_ty {3690I8 | I16 | I32 | I64 => {}3691_ => {3692return Err(wasm_unsupported!(3693"atomic_cas: unsupported access type {:?}",3694access_ty3695));3696}3697};3698let w_ty_ok = match widened_ty {3699I32 | I64 => true,3700_ => false,3701};3702assert!(w_ty_ok && widened_ty.bytes() >= access_ty.bytes());37033704assert!(expected_ty.bytes() >= access_ty.bytes());3705if expected_ty.bytes() > access_ty.bytes() {3706expected = builder.ins().ireduce(access_ty, expected);3707}3708assert!(replacement_ty.bytes() >= access_ty.bytes());3709if replacement_ty.bytes() > access_ty.bytes() {3710replacement = builder.ins().ireduce(access_ty, replacement);3711}37123713let (flags, _, addr) = unwrap_or_return_unreachable_state!(3714stack,3715prepare_atomic_addr(3716memarg,3717u8::try_from(access_ty.bytes()).unwrap(),3718builder,3719stack,3720environ,3721)?3722);3723let mut res = builder.ins().atomic_cas(flags, addr, expected, replacement);3724if access_ty != widened_ty {3725res = builder.ins().uextend(widened_ty, res);3726}3727stack.push1(res);3728Ok(())3729}37303731fn translate_atomic_load(3732widened_ty: Type,3733access_ty: Type,3734memarg: &MemArg,3735builder: &mut FunctionBuilder,3736stack: &mut FuncTranslationStacks,3737environ: &mut FuncEnvironment<'_>,3738) -> WasmResult<()> {3739// The load is performed at type `access_ty`, and the loaded value is zero extended3740// to `widened_ty`.3741match access_ty {3742I8 | I16 | I32 | I64 => {}3743_ => {3744return Err(wasm_unsupported!(3745"atomic_load: unsupported access type {:?}",3746access_ty3747));3748}3749};3750let w_ty_ok = match widened_ty {3751I32 | I64 => true,3752_ => false,3753};3754assert!(w_ty_ok && widened_ty.bytes() >= access_ty.bytes());37553756let (flags, _, addr) = unwrap_or_return_unreachable_state!(3757stack,3758prepare_atomic_addr(3759memarg,3760u8::try_from(access_ty.bytes()).unwrap(),3761builder,3762stack,3763environ,3764)?3765);3766let mut res = builder.ins().atomic_load(access_ty, flags, addr);3767if access_ty != widened_ty {3768res = builder.ins().uextend(widened_ty, res);3769}3770stack.push1(res);3771Ok(())3772}37733774fn translate_atomic_store(3775access_ty: Type,3776memarg: &MemArg,3777builder: &mut FunctionBuilder,3778stack: &mut FuncTranslationStacks,3779environ: &mut FuncEnvironment<'_>,3780) -> WasmResult<()> {3781let mut data = stack.pop1();3782let data_ty = builder.func.dfg.value_type(data);37833784// The operation is performed at type `access_ty`, and the data to be stored may first3785// need to be narrowed accordingly.3786match access_ty {3787I8 | I16 | I32 | I64 => {}3788_ => {3789return Err(wasm_unsupported!(3790"atomic_store: unsupported access type {:?}",3791access_ty3792));3793}3794};3795let d_ty_ok = match data_ty {3796I32 | I64 => true,3797_ => false,3798};3799assert!(d_ty_ok && data_ty.bytes() >= access_ty.bytes());38003801if data_ty.bytes() > access_ty.bytes() {3802data = builder.ins().ireduce(access_ty, data);3803}38043805let (flags, _, addr) = unwrap_or_return_unreachable_state!(3806stack,3807prepare_atomic_addr(3808memarg,3809u8::try_from(access_ty.bytes()).unwrap(),3810builder,3811stack,3812environ,3813)?3814);3815builder.ins().atomic_store(flags, data, addr);3816Ok(())3817}38183819fn translate_vector_icmp(3820cc: IntCC,3821needed_type: Type,3822builder: &mut FunctionBuilder,3823stack: &mut FuncTranslationStacks,3824) {3825let (a, b) = stack.pop2();3826let bitcast_a = optionally_bitcast_vector(a, needed_type, builder);3827let bitcast_b = optionally_bitcast_vector(b, needed_type, builder);3828stack.push1(builder.ins().icmp(cc, bitcast_a, bitcast_b))3829}38303831fn translate_fcmp(cc: FloatCC, builder: &mut FunctionBuilder, stack: &mut FuncTranslationStacks) {3832let (arg0, arg1) = stack.pop2();3833let val = builder.ins().fcmp(cc, arg0, arg1);3834stack.push1(builder.ins().uextend(I32, val));3835}38363837fn translate_vector_fcmp(3838cc: FloatCC,3839needed_type: Type,3840builder: &mut FunctionBuilder,3841stack: &mut FuncTranslationStacks,3842) {3843let (a, b) = stack.pop2();3844let bitcast_a = optionally_bitcast_vector(a, needed_type, builder);3845let bitcast_b = optionally_bitcast_vector(b, needed_type, builder);3846stack.push1(builder.ins().fcmp(cc, bitcast_a, bitcast_b))3847}38483849fn translate_br_if(3850relative_depth: u32,3851builder: &mut FunctionBuilder,3852stack: &mut FuncTranslationStacks,3853) {3854let val = stack.pop1();3855let (br_destination, inputs) = translate_br_if_args(relative_depth, stack);3856let next_block = builder.create_block();3857canonicalise_brif(builder, val, br_destination, inputs, next_block, &[]);38583859builder.seal_block(next_block); // The only predecessor is the current block.3860builder.switch_to_block(next_block);3861}38623863fn translate_br_if_args(3864relative_depth: u32,3865stack: &mut FuncTranslationStacks,3866) -> (ir::Block, &mut [ir::Value]) {3867let i = stack.control_stack.len() - 1 - (relative_depth as usize);3868let (return_count, br_destination) = {3869let frame = &mut stack.control_stack[i];3870// The values returned by the branch are still available for the reachable3871// code that comes after it3872frame.set_branched_to_exit();3873let return_count = if frame.is_loop() {3874frame.num_param_values()3875} else {3876frame.num_return_values()3877};3878(return_count, frame.br_destination())3879};3880let inputs = stack.peekn_mut(return_count);3881(br_destination, inputs)3882}38833884/// Determine the returned value type of a WebAssembly operator3885fn type_of(operator: &Operator) -> Type {3886match operator {3887Operator::V128Load { .. }3888| Operator::V128Store { .. }3889| Operator::V128Const { .. }3890| Operator::V128Not3891| Operator::V128And3892| Operator::V128AndNot3893| Operator::V128Or3894| Operator::V128Xor3895| Operator::V128AnyTrue3896| Operator::V128Bitselect => I8X16, // default type representing V12838973898Operator::I8x16Shuffle { .. }3899| Operator::I8x16Splat3900| Operator::V128Load8Splat { .. }3901| Operator::V128Load8Lane { .. }3902| Operator::V128Store8Lane { .. }3903| Operator::I8x16ExtractLaneS { .. }3904| Operator::I8x16ExtractLaneU { .. }3905| Operator::I8x16ReplaceLane { .. }3906| Operator::I8x16Eq3907| Operator::I8x16Ne3908| Operator::I8x16LtS3909| Operator::I8x16LtU3910| Operator::I8x16GtS3911| Operator::I8x16GtU3912| Operator::I8x16LeS3913| Operator::I8x16LeU3914| Operator::I8x16GeS3915| Operator::I8x16GeU3916| Operator::I8x16Neg3917| Operator::I8x16Abs3918| Operator::I8x16AllTrue3919| Operator::I8x16Shl3920| Operator::I8x16ShrS3921| Operator::I8x16ShrU3922| Operator::I8x16Add3923| Operator::I8x16AddSatS3924| Operator::I8x16AddSatU3925| Operator::I8x16Sub3926| Operator::I8x16SubSatS3927| Operator::I8x16SubSatU3928| Operator::I8x16MinS3929| Operator::I8x16MinU3930| Operator::I8x16MaxS3931| Operator::I8x16MaxU3932| Operator::I8x16AvgrU3933| Operator::I8x16Bitmask3934| Operator::I8x16Popcnt3935| Operator::I8x16RelaxedLaneselect => I8X16,39363937Operator::I16x8Splat3938| Operator::V128Load16Splat { .. }3939| Operator::V128Load16Lane { .. }3940| Operator::V128Store16Lane { .. }3941| Operator::I16x8ExtractLaneS { .. }3942| Operator::I16x8ExtractLaneU { .. }3943| Operator::I16x8ReplaceLane { .. }3944| Operator::I16x8Eq3945| Operator::I16x8Ne3946| Operator::I16x8LtS3947| Operator::I16x8LtU3948| Operator::I16x8GtS3949| Operator::I16x8GtU3950| Operator::I16x8LeS3951| Operator::I16x8LeU3952| Operator::I16x8GeS3953| Operator::I16x8GeU3954| Operator::I16x8Neg3955| Operator::I16x8Abs3956| Operator::I16x8AllTrue3957| Operator::I16x8Shl3958| Operator::I16x8ShrS3959| Operator::I16x8ShrU3960| Operator::I16x8Add3961| Operator::I16x8AddSatS3962| Operator::I16x8AddSatU3963| Operator::I16x8Sub3964| Operator::I16x8SubSatS3965| Operator::I16x8SubSatU3966| Operator::I16x8MinS3967| Operator::I16x8MinU3968| Operator::I16x8MaxS3969| Operator::I16x8MaxU3970| Operator::I16x8AvgrU3971| Operator::I16x8Mul3972| Operator::I16x8Bitmask3973| Operator::I16x8RelaxedLaneselect => I16X8,39743975Operator::I32x4Splat3976| Operator::V128Load32Splat { .. }3977| Operator::V128Load32Lane { .. }3978| Operator::V128Store32Lane { .. }3979| Operator::I32x4ExtractLane { .. }3980| Operator::I32x4ReplaceLane { .. }3981| Operator::I32x4Eq3982| Operator::I32x4Ne3983| Operator::I32x4LtS3984| Operator::I32x4LtU3985| Operator::I32x4GtS3986| Operator::I32x4GtU3987| Operator::I32x4LeS3988| Operator::I32x4LeU3989| Operator::I32x4GeS3990| Operator::I32x4GeU3991| Operator::I32x4Neg3992| Operator::I32x4Abs3993| Operator::I32x4AllTrue3994| Operator::I32x4Shl3995| Operator::I32x4ShrS3996| Operator::I32x4ShrU3997| Operator::I32x4Add3998| Operator::I32x4Sub3999| Operator::I32x4Mul4000| Operator::I32x4MinS4001| Operator::I32x4MinU4002| Operator::I32x4MaxS4003| Operator::I32x4MaxU4004| Operator::I32x4Bitmask4005| Operator::I32x4TruncSatF32x4S4006| Operator::I32x4TruncSatF32x4U4007| Operator::I32x4RelaxedLaneselect4008| Operator::V128Load32Zero { .. } => I32X4,40094010Operator::I64x2Splat4011| Operator::V128Load64Splat { .. }4012| Operator::V128Load64Lane { .. }4013| Operator::V128Store64Lane { .. }4014| Operator::I64x2ExtractLane { .. }4015| Operator::I64x2ReplaceLane { .. }4016| Operator::I64x2Eq4017| Operator::I64x2Ne4018| Operator::I64x2LtS4019| Operator::I64x2GtS4020| Operator::I64x2LeS4021| Operator::I64x2GeS4022| Operator::I64x2Neg4023| Operator::I64x2Abs4024| Operator::I64x2AllTrue4025| Operator::I64x2Shl4026| Operator::I64x2ShrS4027| Operator::I64x2ShrU4028| Operator::I64x2Add4029| Operator::I64x2Sub4030| Operator::I64x2Mul4031| Operator::I64x2Bitmask4032| Operator::I64x2RelaxedLaneselect4033| Operator::V128Load64Zero { .. } => I64X2,40344035Operator::F32x4Splat4036| Operator::F32x4ExtractLane { .. }4037| Operator::F32x4ReplaceLane { .. }4038| Operator::F32x4Eq4039| Operator::F32x4Ne4040| Operator::F32x4Lt4041| Operator::F32x4Gt4042| Operator::F32x4Le4043| Operator::F32x4Ge4044| Operator::F32x4Abs4045| Operator::F32x4Neg4046| Operator::F32x4Sqrt4047| Operator::F32x4Add4048| Operator::F32x4Sub4049| Operator::F32x4Mul4050| Operator::F32x4Div4051| Operator::F32x4Min4052| Operator::F32x4Max4053| Operator::F32x4PMin4054| Operator::F32x4PMax4055| Operator::F32x4ConvertI32x4S4056| Operator::F32x4ConvertI32x4U4057| Operator::F32x4Ceil4058| Operator::F32x4Floor4059| Operator::F32x4Trunc4060| Operator::F32x4Nearest4061| Operator::F32x4RelaxedMax4062| Operator::F32x4RelaxedMin4063| Operator::F32x4RelaxedMadd4064| Operator::F32x4RelaxedNmadd => F32X4,40654066Operator::F64x2Splat4067| Operator::F64x2ExtractLane { .. }4068| Operator::F64x2ReplaceLane { .. }4069| Operator::F64x2Eq4070| Operator::F64x2Ne4071| Operator::F64x2Lt4072| Operator::F64x2Gt4073| Operator::F64x2Le4074| Operator::F64x2Ge4075| Operator::F64x2Abs4076| Operator::F64x2Neg4077| Operator::F64x2Sqrt4078| Operator::F64x2Add4079| Operator::F64x2Sub4080| Operator::F64x2Mul4081| Operator::F64x2Div4082| Operator::F64x2Min4083| Operator::F64x2Max4084| Operator::F64x2PMin4085| Operator::F64x2PMax4086| Operator::F64x2Ceil4087| Operator::F64x2Floor4088| Operator::F64x2Trunc4089| Operator::F64x2Nearest4090| Operator::F64x2RelaxedMax4091| Operator::F64x2RelaxedMin4092| Operator::F64x2RelaxedMadd4093| Operator::F64x2RelaxedNmadd => F64X2,40944095_ => unimplemented!(4096"Currently only SIMD instructions are mapped to their return type; the \4097following instruction is not mapped: {:?}",4098operator4099),4100}4101}41024103/// Some SIMD operations only operate on I8X16 in CLIF; this will convert them to that type by4104/// adding a bitcast if necessary.4105fn optionally_bitcast_vector(4106value: Value,4107needed_type: Type,4108builder: &mut FunctionBuilder,4109) -> Value {4110if builder.func.dfg.value_type(value) != needed_type {4111let mut flags = MemFlags::new();4112flags.set_endianness(ir::Endianness::Little);4113builder.ins().bitcast(needed_type, flags, value)4114} else {4115value4116}4117}41184119#[inline(always)]4120fn is_non_canonical_v128(ty: ir::Type) -> bool {4121match ty {4122I64X2 | I32X4 | I16X8 | F32X4 | F64X2 => true,4123_ => false,4124}4125}41264127/// Cast to I8X16, any vector values in `values` that are of "non-canonical" type (meaning, not4128/// I8X16), and return them in a slice. A pre-scan is made to determine whether any casts are4129/// actually necessary, and if not, the original slice is returned. Otherwise the cast values4130/// are returned in a slice that belongs to the caller-supplied `SmallVec`.4131fn canonicalise_v128_values<'a>(4132tmp_canonicalised: &'a mut SmallVec<[BlockArg; 16]>,4133builder: &mut FunctionBuilder,4134values: &'a [ir::Value],4135) -> &'a [BlockArg] {4136debug_assert!(tmp_canonicalised.is_empty());4137// Cast, and push the resulting `Value`s into `canonicalised`.4138for v in values {4139let value = if is_non_canonical_v128(builder.func.dfg.value_type(*v)) {4140let mut flags = MemFlags::new();4141flags.set_endianness(ir::Endianness::Little);4142builder.ins().bitcast(I8X16, flags, *v)4143} else {4144*v4145};4146tmp_canonicalised.push(BlockArg::from(value));4147}4148tmp_canonicalised.as_slice()4149}41504151/// Generate a `jump` instruction, but first cast all 128-bit vector values to I8X16 if they4152/// don't have that type. This is done in somewhat roundabout way so as to ensure that we4153/// almost never have to do any heap allocation.4154fn canonicalise_then_jump(4155builder: &mut FunctionBuilder,4156destination: ir::Block,4157params: &[ir::Value],4158) -> ir::Inst {4159let mut tmp_canonicalised = SmallVec::<[_; 16]>::new();4160let canonicalised = canonicalise_v128_values(&mut tmp_canonicalised, builder, params);4161builder.ins().jump(destination, canonicalised)4162}41634164/// The same but for a `brif` instruction.4165fn canonicalise_brif(4166builder: &mut FunctionBuilder,4167cond: ir::Value,4168block_then: ir::Block,4169params_then: &[ir::Value],4170block_else: ir::Block,4171params_else: &[ir::Value],4172) -> ir::Inst {4173let mut tmp_canonicalised_then = SmallVec::<[_; 16]>::new();4174let canonicalised_then =4175canonicalise_v128_values(&mut tmp_canonicalised_then, builder, params_then);4176let mut tmp_canonicalised_else = SmallVec::<[_; 16]>::new();4177let canonicalised_else =4178canonicalise_v128_values(&mut tmp_canonicalised_else, builder, params_else);4179builder.ins().brif(4180cond,4181block_then,4182canonicalised_then,4183block_else,4184canonicalised_else,4185)4186}41874188/// A helper for popping and bitcasting a single value; since SIMD values can lose their type by4189/// using v128 (i.e. CLIF's I8x16) we must re-type the values using a bitcast to avoid CLIF4190/// typing issues.4191fn pop1_with_bitcast(4192stack: &mut FuncTranslationStacks,4193needed_type: Type,4194builder: &mut FunctionBuilder,4195) -> Value {4196optionally_bitcast_vector(stack.pop1(), needed_type, builder)4197}41984199/// A helper for popping and bitcasting two values; since SIMD values can lose their type by4200/// using v128 (i.e. CLIF's I8x16) we must re-type the values using a bitcast to avoid CLIF4201/// typing issues.4202fn pop2_with_bitcast(4203stack: &mut FuncTranslationStacks,4204needed_type: Type,4205builder: &mut FunctionBuilder,4206) -> (Value, Value) {4207let (a, b) = stack.pop2();4208let bitcast_a = optionally_bitcast_vector(a, needed_type, builder);4209let bitcast_b = optionally_bitcast_vector(b, needed_type, builder);4210(bitcast_a, bitcast_b)4211}42124213fn pop3_with_bitcast(4214stack: &mut FuncTranslationStacks,4215needed_type: Type,4216builder: &mut FunctionBuilder,4217) -> (Value, Value, Value) {4218let (a, b, c) = stack.pop3();4219let bitcast_a = optionally_bitcast_vector(a, needed_type, builder);4220let bitcast_b = optionally_bitcast_vector(b, needed_type, builder);4221let bitcast_c = optionally_bitcast_vector(c, needed_type, builder);4222(bitcast_a, bitcast_b, bitcast_c)4223}42244225fn bitcast_arguments<'a>(4226builder: &FunctionBuilder,4227arguments: &'a mut [Value],4228params: &[ir::AbiParam],4229param_predicate: impl Fn(usize) -> bool,4230) -> Vec<(Type, &'a mut Value)> {4231let filtered_param_types = params4232.iter()4233.enumerate()4234.filter(|(i, _)| param_predicate(*i))4235.map(|(_, param)| param.value_type);42364237// zip_eq, from the itertools::Itertools trait, is like Iterator::zip but panics if one4238// iterator ends before the other. The `param_predicate` is required to select exactly as many4239// elements of `params` as there are elements in `arguments`.4240let pairs = filtered_param_types.zip_eq(arguments.iter_mut());42414242// The arguments which need to be bitcasted are those which have some vector type but the type4243// expected by the parameter is not the same vector type as that of the provided argument.4244pairs4245.filter(|(param_type, _)| param_type.is_vector())4246.filter(|(param_type, arg)| {4247let arg_type = builder.func.dfg.value_type(**arg);4248assert!(4249arg_type.is_vector(),4250"unexpected type mismatch: expected {}, argument {} was actually of type {}",4251param_type,4252*arg,4253arg_type4254);42554256// This is the same check that would be done by `optionally_bitcast_vector`, except we4257// can't take a mutable borrow of the FunctionBuilder here, so we defer inserting the4258// bitcast instruction to the caller.4259arg_type != *param_type4260})4261.collect()4262}42634264/// A helper for bitcasting a sequence of return values for the function currently being built. If4265/// a value is a vector type that does not match its expected type, this will modify the value in4266/// place to point to the result of a `bitcast`. This conversion is necessary to translate Wasm4267/// code that uses `V128` as function parameters (or implicitly in block parameters) and still use4268/// specific CLIF types (e.g. `I32X4`) in the function body.4269pub fn bitcast_wasm_returns(arguments: &mut [Value], builder: &mut FunctionBuilder) {4270let changes = bitcast_arguments(builder, arguments, &builder.func.signature.returns, |i| {4271builder.func.signature.returns[i].purpose == ir::ArgumentPurpose::Normal4272});4273for (t, arg) in changes {4274let mut flags = MemFlags::new();4275flags.set_endianness(ir::Endianness::Little);4276*arg = builder.ins().bitcast(t, flags, *arg);4277}4278}42794280/// Like `bitcast_wasm_returns`, but for the parameters being passed to a specified callee.4281fn bitcast_wasm_params(4282environ: &mut FuncEnvironment<'_>,4283callee_signature: ir::SigRef,4284arguments: &mut [Value],4285builder: &mut FunctionBuilder,4286) {4287let callee_signature = &builder.func.dfg.signatures[callee_signature];4288let changes = bitcast_arguments(builder, arguments, &callee_signature.params, |i| {4289environ.is_wasm_parameter(&callee_signature, i)4290});4291for (t, arg) in changes {4292let mut flags = MemFlags::new();4293flags.set_endianness(ir::Endianness::Little);4294*arg = builder.ins().bitcast(t, flags, *arg);4295}4296}42974298fn create_catch_block(4299builder: &mut FunctionBuilder,4300stacks: &mut FuncTranslationStacks,4301catch: &wasmparser::Catch,4302environ: &mut FuncEnvironment<'_>,4303) -> WasmResult<ir::Block> {4304let (is_ref, tag, label) = match catch {4305wasmparser::Catch::One { tag, label } => (false, Some(*tag), *label),4306wasmparser::Catch::OneRef { tag, label } => (true, Some(*tag), *label),4307wasmparser::Catch::All { label } => (false, None, *label),4308wasmparser::Catch::AllRef { label } => (true, None, *label),4309};43104311// We always create a handler block with one blockparam for the4312// one exception payload value that we use (`exn0` block-call4313// argument). This one payload value is the `exnref`. Note,4314// however, that we carry it in a native host-pointer-sized4315// payload (because this is what the exception ABI in Cranelift4316// requires). We then generate the args for the actual branch to4317// the handler block: we add unboxing code to load each value in4318// the exception signature if a specific tag is expected (hence4319// signature is known), and then append the `exnref` itself if we4320// are compiling a `*Ref` variant.43214322let (exn_ref_ty, needs_stack_map) = environ.reference_type(WasmHeapType::Exn);4323let (exn_payload_wasm_ty, exn_payload_ty) = match environ.pointer_type().bits() {432432 => (wasmparser::ValType::I32, I32),432564 => (wasmparser::ValType::I64, I64),4326_ => panic!("Unsupported pointer width"),4327};4328let block = block_with_params(builder, [exn_payload_wasm_ty], environ)?;4329builder.switch_to_block(block);4330let exn_ref = builder.func.dfg.block_params(block)[0];4331debug_assert!(exn_ref_ty.bits() <= exn_payload_ty.bits());4332let exn_ref = if exn_ref_ty.bits() < exn_payload_ty.bits() {4333builder.ins().ireduce(exn_ref_ty, exn_ref)4334} else {4335exn_ref4336};43374338if needs_stack_map {4339builder.declare_value_needs_stack_map(exn_ref);4340}43414342// We encode tag indices from the module directly as Cranelift4343// `ExceptionTag`s. We will translate those to (instance,4344// defined-tag-index) pairs during the unwind walk -- necessarily4345// dynamic because tag imports are provided only at instantiation4346// time.4347let clif_tag = tag.map(|t| ExceptionTag::from_u32(t));43484349stacks.handlers.add_handler(clif_tag, block);43504351let mut params = vec![];43524353if let Some(tag) = tag {4354let tag = TagIndex::from_u32(tag);4355params.extend(environ.translate_exn_unbox(builder, tag, exn_ref)?);4356}4357if is_ref {4358params.push(exn_ref);4359}43604361// Generate the branch itself.4362let i = stacks.control_stack.len() - 1 - (label as usize);4363let frame = &mut stacks.control_stack[i];4364frame.set_branched_to_exit();4365canonicalise_then_jump(builder, frame.br_destination(), ¶ms);43664367Ok(block)4368}436943704371