Next Generation WASM Microkernel Operating System
at trap_handler 3381 lines 146 kB view raw
1// Copyright 2025 Jonas Kruckenberg 2// 3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or 4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or 5// http://opensource.org/licenses/MIT>, at your option. This file may not be 6// copied, modified, or distributed except according to those terms. 7 8use crate::util::zip_eq::IteratorExt; 9use crate::wasm::cranelift::CraneliftGlobal; 10use crate::wasm::cranelift::env::TranslationEnvironment; 11use crate::wasm::cranelift::state::{ControlStackFrame, ElseData, FuncTranslationState}; 12use crate::wasm::cranelift::utils::{ 13 block_with_params, blocktype_params_results, f32_translation, f64_translation, 14}; 15use crate::wasm::indices::{ 16 DataIndex, ElemIndex, FuncIndex, GlobalIndex, MemoryIndex, TableIndex, TypeIndex, 17}; 18use crate::wasm::trap::{TRAP_NULL_REFERENCE, TRAP_UNREACHABLE}; 19use alloc::vec; 20use alloc::vec::Vec; 21use anyhow::bail; 22use cranelift_codegen::ir; 23use cranelift_codegen::ir::condcodes::{FloatCC, IntCC}; 24use cranelift_codegen::ir::immediates::Offset32; 25use cranelift_codegen::ir::types::{ 26 F32, F32X4, F64, F64X2, I8, I8X16, I16, I16X8, I32, I32X4, I64, I64X2, 27}; 28use cranelift_codegen::ir::{ 29 AtomicRmwOp, ConstantData, JumpTableData, MemFlags, TrapCode, ValueLabel, 30}; 31use cranelift_codegen::ir::{InstBuilder, Type, Value}; 32use cranelift_entity::packed_option::ReservedValue; 33use cranelift_frontend::{FunctionBuilder, Variable}; 34use hashbrown::{HashMap, hash_map}; 35use smallvec::SmallVec; 36use wasmparser::{FuncValidator, MemArg, Operator, WasmModuleResources}; 37 38/// Like `Option<T>` but specifically for passing information about transitions 39/// from reachable to unreachable state and the like from callees to callers. 40/// 41/// Marked `must_use` to force callers to update 42/// `FuncTranslationState::reachable` as necessary. 43#[derive(PartialEq, Eq)] 44#[must_use] 45pub enum Reachability<T> { 46 /// The Wasm execution state is reachable, here is a `T`. 47 Reachable(T), 48 /// The Wasm execution state has been determined to be statically 49 /// unreachable. It is the receiver of this value's responsibility to update 50 /// `FuncTranslationState::reachable` as necessary. 51 Unreachable, 52} 53 54/// Given a `Reachability<T>`, unwrap the inner `T` or, when unreachable, set 55/// `state.reachable = false` and return. 56/// 57/// Used in combination with calling `prepare_addr` and `prepare_atomic_addr` 58/// when we can statically determine that a Wasm access will unconditionally 59/// trap. 60macro_rules! unwrap_or_return_unreachable_state { 61 ($state:ident, $value:expr) => { 62 match $value { 63 Reachability::Reachable(x) => x, 64 Reachability::Unreachable => { 65 $state.reachable = false; 66 return Ok(()); 67 } 68 } 69 }; 70} 71 72/// Translates wasm operators into Cranelift IR instructions. 73#[expect(clippy::too_many_lines, reason = "This is the big match statement")] 74pub fn translate_operator( 75 validator: &mut FuncValidator<impl WasmModuleResources>, 76 op: &Operator, 77 builder: &mut FunctionBuilder, 78 state: &mut FuncTranslationState, 79 env: &mut TranslationEnvironment, 80) -> crate::Result<()> { 81 if !state.reachable { 82 translate_unreachable_operator(validator, op, builder, state, env); 83 return Ok(()); 84 } 85 86 // Given that we believe the current block is reachable, the FunctionBuilder ought to agree. 87 debug_assert!(!builder.is_unreachable()); 88 89 match op { 90 Operator::Unreachable => { 91 builder.ins().trap(TRAP_UNREACHABLE); 92 state.reachable = false; 93 } 94 Operator::Nop => { 95 // We do nothing 96 } 97 Operator::Drop => { 98 state.pop1(); 99 } 100 Operator::Select => { 101 let (mut arg1, mut arg2, cond) = state.pop3(); 102 if builder.func.dfg.value_type(arg1).is_vector() { 103 arg1 = optionally_bitcast_vector(arg1, I8X16, builder); 104 } 105 if builder.func.dfg.value_type(arg2).is_vector() { 106 arg2 = optionally_bitcast_vector(arg2, I8X16, builder); 107 } 108 state.push1(builder.ins().select(cond, arg1, arg2)); 109 } 110 /***************************** Control flow blocks ********************************** 111 * When starting a control flow block, we create a new `Block` that will hold the code 112 * after the block, and we push a frame on the control stack. Depending on the type 113 * of block, we create a new `Block` for the body of the block with an associated 114 * jump instruction. 115 * 116 * The `End` instruction pops the last control frame from the control stack, seals 117 * the destination block (since `br` instructions targeting it only appear inside the 118 * block and have already been translated) and modify the value stack to use the 119 * possible `Block`'s arguments values. 120 ***********************************************************************************/ 121 Operator::Block { blockty } => { 122 let (params, results) = blocktype_params_results(validator, *blockty); 123 let next = block_with_params(builder, results.clone(), env); 124 state.push_block(next, params.len(), results.len()); 125 } 126 Operator::Loop { blockty } => { 127 let (params, results) = blocktype_params_results(validator, *blockty); 128 let loop_body = block_with_params(builder, params.clone(), env); 129 let next = block_with_params(builder, results.clone(), env); 130 canonicalise_then_jump(builder, loop_body, state.peekn(params.len())); 131 state.push_loop(loop_body, next, params.len(), results.len()); 132 133 // Pop the initial `Block` actuals and replace them with the `Block`'s 134 // params since control flow joins at the top of the loop. 135 state.popn(params.len()); 136 state 137 .stack 138 .extend_from_slice(builder.block_params(loop_body)); 139 140 builder.switch_to_block(loop_body); 141 // env.before_loop_header(builder)?; 142 } 143 Operator::If { blockty } => { 144 let val = state.pop1(); 145 146 let next_block = builder.create_block(); 147 let (params, results) = blocktype_params_results(validator, *blockty); 148 let (destination, else_data) = if params.clone().eq(results.clone()) { 149 // It is possible there is no `else` block, so we will only 150 // allocate a block for it if/when we find the `else`. For now, 151 // we if the condition isn't true, then we jump directly to the 152 // destination block following the whole `if...end`. If we do end 153 // up discovering an `else`, then we will allocate a block for it 154 // and go back and patch the jump. 155 let destination = block_with_params(builder, results.clone(), env); 156 let branch_inst = canonicalise_brif( 157 builder, 158 val, 159 next_block, 160 &[], 161 destination, 162 state.peekn(params.len()), 163 ); 164 ( 165 destination, 166 ElseData::NoElse { 167 branch_inst, 168 placeholder: destination, 169 }, 170 ) 171 } else { 172 // The `if` type signature is not valid without an `else` block, 173 // so we eagerly allocate the `else` block here. 174 let destination = block_with_params(builder, results.clone(), env); 175 let else_block = block_with_params(builder, params.clone(), env); 176 canonicalise_brif( 177 builder, 178 val, 179 next_block, 180 &[], 181 else_block, 182 state.peekn(params.len()), 183 ); 184 builder.seal_block(else_block); 185 (destination, ElseData::WithElse { else_block }) 186 }; 187 188 builder.seal_block(next_block); // Only predecessor is the current block. 189 builder.switch_to_block(next_block); 190 191 // Here we append an argument to a Block targeted by an argumentless jump instruction 192 // But in fact there are two cases: 193 // - either the If does not have a Else clause, in that case ty = EmptyBlock 194 // and we add nothing; 195 // - either the If have an Else clause, in that case the destination of this jump 196 // instruction will be changed later when we parse the Else operator. 197 state.push_if( 198 destination, 199 else_data, 200 params.len(), 201 results.len(), 202 *blockty, 203 ); 204 } 205 Operator::Else => { 206 let i = state.control_stack.len().checked_sub(1).unwrap(); 207 match *state.control_stack.get_mut(i).unwrap() { 208 ControlStackFrame::If { 209 ref else_data, 210 head_is_reachable, 211 ref mut consequent_ends_reachable, 212 num_return_values, 213 blocktype, 214 destination, 215 .. 216 } => { 217 // We finished the consequent, so record its final 218 // reachability state. 219 debug_assert!(consequent_ends_reachable.is_none()); 220 *consequent_ends_reachable = Some(state.reachable); 221 222 if head_is_reachable { 223 // We have a branch from the head of the `if` to the `else`. 224 state.reachable = true; 225 226 // Ensure we have a block for the `else` block (it may have 227 // already been pre-allocated, see `ElseData` for details). 228 let else_block = match *else_data { 229 ElseData::NoElse { 230 branch_inst, 231 placeholder, 232 } => { 233 let (params, _results) = 234 blocktype_params_results(validator, blocktype); 235 debug_assert_eq!(params.len(), num_return_values); 236 let else_block = block_with_params(builder, params.clone(), env); 237 canonicalise_then_jump( 238 builder, 239 destination, 240 state.peekn(params.len()), 241 ); 242 state.popn(params.len()); 243 244 builder.change_jump_destination( 245 branch_inst, 246 placeholder, 247 else_block, 248 ); 249 builder.seal_block(else_block); 250 else_block 251 } 252 ElseData::WithElse { else_block } => { 253 canonicalise_then_jump( 254 builder, 255 destination, 256 state.peekn(num_return_values), 257 ); 258 state.popn(num_return_values); 259 else_block 260 } 261 }; 262 263 // You might be expecting that we push the parameters for this 264 // `else` block here, something like this: 265 // 266 // state.pushn(&control_stack_frame.params); 267 // 268 // We don't do that because they are already on the top of the stack 269 // for us: we pushed the parameters twice when we saw the initial 270 // `if` so that we wouldn't have to save the parameters in the 271 // `ControlStackFrame` as another `Vec` allocation. 272 273 builder.switch_to_block(else_block); 274 275 // We don't bother updating the control frame's `ElseData` 276 // to `WithElse` because nothing else will read it. 277 } 278 } 279 _ => unreachable!(), 280 } 281 } 282 Operator::End => { 283 let frame = state.control_stack.pop().unwrap(); 284 let next_block = frame.following_code(); 285 let return_count = frame.num_return_values(); 286 let return_args = state.peekn_mut(return_count); 287 288 canonicalise_then_jump(builder, next_block, return_args); 289 // You might expect that if we just finished an `if` block that 290 // didn't have a corresponding `else` block, then we would clean 291 // up our duplicate set of parameters that we pushed earlier 292 // right here. However, we don't have to explicitly do that, 293 // since we truncate the stack back to the original height 294 // below. 295 296 builder.switch_to_block(next_block); 297 builder.seal_block(next_block); 298 299 // If it is a loop we also have to seal the body loop block 300 if let ControlStackFrame::Loop { header, .. } = frame { 301 builder.seal_block(header); 302 } 303 304 frame.truncate_value_stack_to_original_size(&mut state.stack); 305 state 306 .stack 307 .extend_from_slice(builder.block_params(next_block)); 308 } 309 /**************************** Branch instructions ********************************* 310 * The branch instructions all have as arguments a target nesting level, which 311 * corresponds to how many control stack frames do we have to pop to get the 312 * destination `Block`. 313 * 314 * Once the destination `Block` is found, we sometimes have to declare a certain depth 315 * of the stack unreachable, because some branch instructions are terminator. 316 * 317 * The `br_table` case is much more complicated because Cranelift's `br_table` instruction 318 * does not support jump arguments like all the other branch instructions. That is why, in 319 * the case where we would use jump arguments for every other branch instruction, we 320 * need to split the critical edges leaving the `br_tables` by creating one `Block` per 321 * table destination; the `br_table` will point to these newly created `Blocks` and these 322 * `Block`s contain only a jump instruction pointing to the final destination, this time with 323 * jump arguments. 324 * 325 * This system is also implemented in Cranelift's SSA construction algorithm, because 326 * `use_var` located in a destination `Block` of a `br_table` might trigger the addition 327 * of jump arguments in each predecessor branch instruction, one of which might be a 328 * `br_table`. 329 ***********************************************************************************/ 330 Operator::Br { relative_depth } => { 331 // FIXME wow this is ugly 332 let i = state 333 .control_stack 334 .len() 335 .checked_sub(1) 336 .unwrap() 337 .checked_sub(usize::try_from(*relative_depth).unwrap()) 338 .unwrap(); 339 340 let (return_count, br_destination) = { 341 let frame = &mut state.control_stack[i]; 342 // We signal that all the code that follows until the next End is unreachable 343 frame.set_branched_to_exit(); 344 let return_count = if frame.is_loop() { 345 frame.num_param_values() 346 } else { 347 frame.num_return_values() 348 }; 349 (return_count, frame.br_destination()) 350 }; 351 let destination_args = state.peekn_mut(return_count); 352 canonicalise_then_jump(builder, br_destination, destination_args); 353 state.popn(return_count); 354 state.reachable = false; 355 } 356 Operator::BrIf { relative_depth } => translate_br_if(*relative_depth, builder, state), 357 Operator::BrTable { targets } => { 358 let default = targets.default(); 359 let mut min_depth = default; 360 for depth in targets.targets() { 361 let depth = depth?; 362 if depth < min_depth { 363 min_depth = depth; 364 } 365 } 366 let jump_args_count = { 367 // FIXME wow this is ugly 368 let i = state 369 .control_stack 370 .len() 371 .checked_sub(1) 372 .unwrap() 373 .checked_sub(usize::try_from(min_depth).unwrap()) 374 .unwrap(); 375 376 let min_depth_frame = &state.control_stack[i]; 377 if min_depth_frame.is_loop() { 378 min_depth_frame.num_param_values() 379 } else { 380 min_depth_frame.num_return_values() 381 } 382 }; 383 let val = state.pop1(); 384 let mut data = Vec::with_capacity(targets.len() as usize); 385 if jump_args_count == 0 { 386 // No jump arguments 387 for depth in targets.targets() { 388 let depth = depth?; 389 let block = { 390 // FIXME wow this is ugly 391 let i = state 392 .control_stack 393 .len() 394 .checked_sub(1) 395 .unwrap() 396 .checked_sub(usize::try_from(depth).unwrap()) 397 .unwrap(); 398 399 let frame = &mut state.control_stack[i]; 400 frame.set_branched_to_exit(); 401 frame.br_destination() 402 }; 403 data.push(builder.func.dfg.block_call(block, &[])); 404 } 405 let block = { 406 // FIXME wow this is ugly 407 let i = state 408 .control_stack 409 .len() 410 .checked_sub(1) 411 .unwrap() 412 .checked_sub(usize::try_from(default).unwrap()) 413 .unwrap(); 414 415 let frame = &mut state.control_stack[i]; 416 frame.set_branched_to_exit(); 417 frame.br_destination() 418 }; 419 let block = builder.func.dfg.block_call(block, &[]); 420 let jt = builder.create_jump_table(JumpTableData::new(block, &data)); 421 builder.ins().br_table(val, jt); 422 } else { 423 // Here we have jump arguments, but Cranelift's br_table doesn't support them 424 // We then proceed to split the edges going out of the br_table 425 let return_count = jump_args_count; 426 let mut dest_block_sequence = vec![]; 427 let mut dest_block_map = HashMap::new(); 428 for depth in targets.targets() { 429 let depth = depth?; 430 let branch_block = match dest_block_map.entry(depth as usize) { 431 hash_map::Entry::Occupied(entry) => *entry.get(), 432 hash_map::Entry::Vacant(entry) => { 433 let block = builder.create_block(); 434 dest_block_sequence.push((depth as usize, block)); 435 *entry.insert(block) 436 } 437 }; 438 data.push(builder.func.dfg.block_call(branch_block, &[])); 439 } 440 let default_branch_block = match dest_block_map.entry(default as usize) { 441 hash_map::Entry::Occupied(entry) => *entry.get(), 442 hash_map::Entry::Vacant(entry) => { 443 let block = builder.create_block(); 444 dest_block_sequence.push((default as usize, block)); 445 *entry.insert(block) 446 } 447 }; 448 let default_branch_block = builder.func.dfg.block_call(default_branch_block, &[]); 449 let jt = builder.create_jump_table(JumpTableData::new(default_branch_block, &data)); 450 builder.ins().br_table(val, jt); 451 for (depth, dest_block) in dest_block_sequence { 452 builder.switch_to_block(dest_block); 453 builder.seal_block(dest_block); 454 let real_dest_block = { 455 // FIXME wow this is ugly 456 let i = state 457 .control_stack 458 .len() 459 .checked_sub(1) 460 .unwrap() 461 .checked_sub(depth) 462 .unwrap(); 463 464 let frame = &mut state.control_stack[i]; 465 frame.set_branched_to_exit(); 466 frame.br_destination() 467 }; 468 let destination_args = state.peekn_mut(return_count); 469 canonicalise_then_jump(builder, real_dest_block, destination_args); 470 } 471 state.popn(return_count); 472 } 473 state.reachable = false; 474 } 475 Operator::Return => { 476 let return_count = { 477 let frame = &mut state.control_stack[0]; 478 frame.num_return_values() 479 }; 480 { 481 let return_args = state.peekn_mut(return_count); 482 // env.handle_before_return(&return_args, builder)?; 483 bitcast_wasm_returns(return_args, builder, env); 484 builder.ins().return_(return_args); 485 } 486 state.popn(return_count); 487 state.reachable = false; 488 } 489 /************************************ Calls **************************************** 490 * The call instructions pop off their arguments from the stack and append their 491 * return values to it. `call_indirect` needs environment support because there is an 492 * argument referring to an index in the external functions table of the module. 493 ************************************************************************************/ 494 Operator::Call { function_index } => { 495 let function_index = FuncIndex::from_u32(*function_index); 496 let (fref, num_args) = state.get_direct_func(builder.func, function_index, env); 497 498 // Bitcast any vector arguments to their default type, I8X16, before calling. 499 let args = state.peekn_mut(num_args); 500 bitcast_wasm_params( 501 builder.func.dfg.ext_funcs[fref].signature, 502 args, 503 builder, 504 env, 505 ); 506 507 let call = env.translate_call(builder, function_index, fref, args); 508 let inst_results = builder.inst_results(call); 509 debug_assert_eq!( 510 inst_results.len(), 511 builder.func.dfg.signatures[builder.func.dfg.ext_funcs[fref].signature] 512 .returns 513 .len(), 514 "translate_call results should match the call signature" 515 ); 516 state.popn(num_args); 517 state.pushn(inst_results); 518 } 519 Operator::CallIndirect { 520 type_index, 521 table_index, 522 } => { 523 let type_index = TypeIndex::from_u32(*type_index); 524 // `type_index` is the index of the function's signature and 525 // `table_index` is the index of the table to search the function 526 // in. 527 let (sigref, num_args) = state.get_indirect_sig(builder.func, type_index, env); 528 let callee = state.pop1(); 529 530 // Bitcast any vector arguments to their default type, I8X16, before calling. 531 let args = state.peekn_mut(num_args); 532 bitcast_wasm_params(sigref, args, builder, env); 533 534 let table_index = TableIndex::from_u32(*table_index); 535 let table = state.get_table(builder.func, table_index, env).clone(); 536 537 let call = unwrap_or_return_unreachable_state!(state, { 538 env.translate_call_indirect( 539 builder, 540 table_index, 541 &table, 542 type_index, 543 sigref, 544 callee, 545 state.peekn(num_args), 546 ) 547 }); 548 549 let inst_results = builder.inst_results(call); 550 debug_assert_eq!( 551 inst_results.len(), 552 builder.func.dfg.signatures[sigref].returns.len(), 553 "translate_call_indirect results should match the call signature" 554 ); 555 state.popn(num_args); 556 state.pushn(inst_results); 557 } 558 /******************************* Tail Calls ****************************************** 559 * The tail call instructions pop their arguments from the stack and 560 * then permanently transfer control to their callee. The indirect 561 * version requires environment support (while the direct version can 562 * optionally be hooked but doesn't require it) it interacts with the 563 * VM's runtime state via tables. 564 ************************************************************************************/ 565 Operator::ReturnCall { function_index } => { 566 let function_index = FuncIndex::from_u32(*function_index); 567 let (fref, num_args) = state.get_direct_func(builder.func, function_index, env); 568 569 // Bitcast any vector arguments to their default type, I8X16, before calling. 570 let args = state.peekn_mut(num_args); 571 bitcast_wasm_params( 572 builder.func.dfg.ext_funcs[fref].signature, 573 args, 574 builder, 575 env, 576 ); 577 578 env.translate_return_call(builder, function_index, fref, args)?; 579 580 state.popn(num_args); 581 state.reachable = false; 582 } 583 Operator::ReturnCallIndirect { 584 type_index, 585 table_index, 586 } => { 587 let type_index = TypeIndex::from_u32(*type_index); 588 // `type_index` is the index of the function's signature and 589 // `table_index` is the index of the table to search the function 590 // in. 591 let (sigref, num_args) = state.get_indirect_sig(builder.func, type_index, env); 592 let callee = state.pop1(); 593 594 // Bitcast any vector arguments to their default type, I8X16, before calling. 595 let args = state.peekn_mut(num_args); 596 bitcast_wasm_params(sigref, args, builder, env); 597 598 env.translate_return_call_indirect( 599 builder, 600 TableIndex::from_u32(*table_index), 601 type_index, 602 sigref, 603 callee, 604 state.peekn(num_args), 605 )?; 606 607 state.popn(num_args); 608 state.reachable = false; 609 } 610 /********************************** Locals **************************************** 611 * `get_local` and `set_local` are treated as non-SSA variables and will completely 612 * disappear in the Cranelift Code 613 ***********************************************************************************/ 614 Operator::LocalGet { local_index } => { 615 let val = builder.use_var(Variable::from_u32(*local_index)); 616 state.push1(val); 617 let label = ValueLabel::from_u32(*local_index); 618 builder.set_val_label(val, label); 619 } 620 Operator::LocalSet { local_index } => { 621 let mut val = state.pop1(); 622 623 // Ensure SIMD values are cast to their default Cranelift type, I8x16. 624 let ty = builder.func.dfg.value_type(val); 625 if ty.is_vector() { 626 val = optionally_bitcast_vector(val, I8X16, builder); 627 } 628 629 builder.def_var(Variable::from_u32(*local_index), val); 630 let label = ValueLabel::from_u32(*local_index); 631 builder.set_val_label(val, label); 632 } 633 Operator::LocalTee { local_index } => { 634 let mut val = state.peek1(); 635 636 // Ensure SIMD values are cast to their default Cranelift type, I8x16. 637 let ty = builder.func.dfg.value_type(val); 638 if ty.is_vector() { 639 val = optionally_bitcast_vector(val, I8X16, builder); 640 } 641 642 builder.def_var(Variable::from_u32(*local_index), val); 643 let label = ValueLabel::from_u32(*local_index); 644 builder.set_val_label(val, label); 645 } 646 /********************************** Globals **************************************** 647 * `get_global` and `set_global` are handled by the environment. 648 ***********************************************************************************/ 649 Operator::GlobalGet { global_index } => { 650 let global_index = GlobalIndex::from_u32(*global_index); 651 let val = match *state.get_global(builder.func, global_index, env) { 652 CraneliftGlobal::Const(val) => val, 653 CraneliftGlobal::Memory { gv, offset, ty } => { 654 let addr = builder.ins().global_value(env.pointer_type(), gv); 655 let mut flags = MemFlags::trusted(); 656 // Put globals in the "table" abstract mem category as well. 657 flags.set_alias_region(Some(ir::AliasRegion::Table)); 658 builder.ins().load(ty, flags, addr, offset) 659 } 660 CraneliftGlobal::Custom => { 661 env.translate_custom_global_get(builder, global_index)? 662 } 663 }; 664 state.push1(val); 665 } 666 Operator::GlobalSet { global_index } => { 667 let global_index = GlobalIndex::from_u32(*global_index); 668 match *state.get_global(builder.func, global_index, env) { 669 CraneliftGlobal::Const(_) => panic!("global #{global_index:?} is a constant"), 670 CraneliftGlobal::Memory { gv, offset, ty } => { 671 let addr = builder.ins().global_value(env.pointer_type(), gv); 672 let mut flags = MemFlags::trusted(); 673 // Put globals in the "table" abstract mem category as well. 674 flags.set_alias_region(Some(ir::AliasRegion::Table)); 675 let mut val = state.pop1(); 676 // Ensure SIMD values are cast to their default Cranelift type, I8x16. 677 if ty.is_vector() { 678 val = optionally_bitcast_vector(val, I8X16, builder); 679 } 680 debug_assert_eq!(ty, builder.func.dfg.value_type(val)); 681 builder.ins().store(flags, val, addr, offset); 682 } 683 CraneliftGlobal::Custom => { 684 let val = state.pop1(); 685 env.translate_custom_global_set(builder, global_index, val)?; 686 } 687 } 688 } 689 /******************************* Memory management *********************************** 690 * Memory management is handled by environment. It is usually translated into calls to 691 * special functions. 692 ************************************************************************************/ 693 Operator::MemoryGrow { mem } => { 694 // The WebAssembly MVP only supports one linear memory, but we expect the reserved 695 // argument to be a memory index. 696 let mem_index = MemoryIndex::from_u32(*mem); 697 let delta = state.pop1(); 698 let ret = env.translate_memory_grow(builder.cursor(), mem_index, delta); 699 state.push1(ret); 700 } 701 Operator::MemorySize { mem } => { 702 let mem_index = MemoryIndex::from_u32(*mem); 703 let ret = env.translate_memory_size(builder.cursor(), mem_index)?; 704 state.push1(ret); 705 } 706 707 Operator::I32Const { value } => { 708 state.push1(builder.ins().iconst(I32, i64::from(*value))); 709 } 710 Operator::I64Const { value } => state.push1(builder.ins().iconst(I64, *value)), 711 Operator::F32Const { value } => { 712 state.push1(builder.ins().f32const(f32_translation(*value))); 713 } 714 Operator::F64Const { value } => { 715 state.push1(builder.ins().f64const(f64_translation(*value))); 716 } 717 718 // integer operators 719 Operator::I32Add | Operator::I64Add => { 720 let (arg1, arg2) = state.pop2(); 721 state.push1(builder.ins().iadd(arg1, arg2)); 722 } 723 Operator::I32Sub | Operator::I64Sub => { 724 let (arg1, arg2) = state.pop2(); 725 state.push1(builder.ins().isub(arg1, arg2)); 726 } 727 Operator::I32Mul | Operator::I64Mul => { 728 let (arg1, arg2) = state.pop2(); 729 state.push1(builder.ins().imul(arg1, arg2)); 730 } 731 Operator::I32DivS | Operator::I64DivS => { 732 let (arg1, arg2) = state.pop2(); 733 state.push1(builder.ins().sdiv(arg1, arg2)); 734 } 735 Operator::I32DivU | Operator::I64DivU => { 736 let (arg1, arg2) = state.pop2(); 737 state.push1(builder.ins().udiv(arg1, arg2)); 738 } 739 Operator::I32RemS | Operator::I64RemS => { 740 let (arg1, arg2) = state.pop2(); 741 state.push1(builder.ins().srem(arg1, arg2)); 742 } 743 Operator::I32RemU | Operator::I64RemU => { 744 let (arg1, arg2) = state.pop2(); 745 state.push1(builder.ins().urem(arg1, arg2)); 746 } 747 Operator::I32And | Operator::I64And => { 748 let (arg1, arg2) = state.pop2(); 749 state.push1(builder.ins().band(arg1, arg2)); 750 } 751 Operator::I32Or | Operator::I64Or => { 752 let (arg1, arg2) = state.pop2(); 753 state.push1(builder.ins().bor(arg1, arg2)); 754 } 755 Operator::I32Xor | Operator::I64Xor => { 756 let (arg1, arg2) = state.pop2(); 757 state.push1(builder.ins().bxor(arg1, arg2)); 758 } 759 Operator::I32Shl | Operator::I64Shl => { 760 let (arg1, arg2) = state.pop2(); 761 state.push1(builder.ins().ishl(arg1, arg2)); 762 } 763 Operator::I32ShrS | Operator::I64ShrS => { 764 let (arg1, arg2) = state.pop2(); 765 state.push1(builder.ins().sshr(arg1, arg2)); 766 } 767 Operator::I32ShrU | Operator::I64ShrU => { 768 let (arg1, arg2) = state.pop2(); 769 state.push1(builder.ins().ushr(arg1, arg2)); 770 } 771 Operator::I32Rotl | Operator::I64Rotl => { 772 let (arg1, arg2) = state.pop2(); 773 state.push1(builder.ins().rotl(arg1, arg2)); 774 } 775 Operator::I32Rotr | Operator::I64Rotr => { 776 let (arg1, arg2) = state.pop2(); 777 state.push1(builder.ins().rotr(arg1, arg2)); 778 } 779 Operator::I32Clz | Operator::I64Clz => { 780 let arg = state.pop1(); 781 state.push1(builder.ins().clz(arg)); 782 } 783 Operator::I32Ctz | Operator::I64Ctz => { 784 let arg = state.pop1(); 785 state.push1(builder.ins().ctz(arg)); 786 } 787 Operator::I32Popcnt | Operator::I64Popcnt => { 788 let arg = state.pop1(); 789 state.push1(builder.ins().popcnt(arg)); 790 } 791 Operator::I32WrapI64 => { 792 let val = state.pop1(); 793 state.push1(builder.ins().ireduce(I32, val)); 794 } 795 Operator::I64ExtendI32S => { 796 let val = state.pop1(); 797 state.push1(builder.ins().sextend(I64, val)); 798 } 799 Operator::I64ExtendI32U => { 800 let val = state.pop1(); 801 state.push1(builder.ins().uextend(I64, val)); 802 } 803 804 // floating-point operators 805 Operator::F32Add | Operator::F64Add => { 806 let (arg1, arg2) = state.pop2(); 807 state.push1(builder.ins().fadd(arg1, arg2)); 808 } 809 Operator::F32Sub | Operator::F64Sub => { 810 let (arg1, arg2) = state.pop2(); 811 state.push1(builder.ins().fsub(arg1, arg2)); 812 } 813 Operator::F32Mul | Operator::F64Mul => { 814 let (arg1, arg2) = state.pop2(); 815 state.push1(builder.ins().fmul(arg1, arg2)); 816 } 817 Operator::F32Div | Operator::F64Div => { 818 let (arg1, arg2) = state.pop2(); 819 state.push1(builder.ins().fdiv(arg1, arg2)); 820 } 821 Operator::F32Min | Operator::F64Min => { 822 let (arg1, arg2) = state.pop2(); 823 state.push1(builder.ins().fmin(arg1, arg2)); 824 } 825 Operator::F32Max | Operator::F64Max => { 826 let (arg1, arg2) = state.pop2(); 827 state.push1(builder.ins().fmax(arg1, arg2)); 828 } 829 Operator::F32Copysign | Operator::F64Copysign => { 830 let (arg1, arg2) = state.pop2(); 831 state.push1(builder.ins().fcopysign(arg1, arg2)); 832 } 833 Operator::F32Sqrt | Operator::F64Sqrt => { 834 let arg = state.pop1(); 835 state.push1(builder.ins().sqrt(arg)); 836 } 837 Operator::F32Ceil | Operator::F64Ceil => { 838 let arg = state.pop1(); 839 state.push1(builder.ins().ceil(arg)); 840 } 841 Operator::F32Floor | Operator::F64Floor => { 842 let arg = state.pop1(); 843 state.push1(builder.ins().floor(arg)); 844 } 845 Operator::F32Trunc | Operator::F64Trunc => { 846 let arg = state.pop1(); 847 state.push1(builder.ins().trunc(arg)); 848 } 849 Operator::F32Nearest | Operator::F64Nearest => { 850 let arg = state.pop1(); 851 state.push1(builder.ins().nearest(arg)); 852 } 853 Operator::F32Abs | Operator::F64Abs => { 854 let val = state.pop1(); 855 state.push1(builder.ins().fabs(val)); 856 } 857 Operator::F32Neg | Operator::F64Neg => { 858 let arg = state.pop1(); 859 state.push1(builder.ins().fneg(arg)); 860 } 861 Operator::I32TruncF64S | Operator::I32TruncF32S => { 862 let val = state.pop1(); 863 state.push1(builder.ins().fcvt_to_sint(I32, val)); 864 } 865 Operator::I32TruncF64U | Operator::I32TruncF32U => { 866 let val = state.pop1(); 867 state.push1(builder.ins().fcvt_to_uint(I32, val)); 868 } 869 Operator::I64TruncF64U | Operator::I64TruncF32U => { 870 let val = state.pop1(); 871 state.push1(builder.ins().fcvt_to_uint(I64, val)); 872 } 873 Operator::I64TruncF64S | Operator::I64TruncF32S => { 874 let val = state.pop1(); 875 state.push1(builder.ins().fcvt_to_sint(I64, val)); 876 } 877 Operator::F32DemoteF64 => { 878 let val = state.pop1(); 879 state.push1(builder.ins().fdemote(F32, val)); 880 } 881 Operator::F32ConvertI64S | Operator::F32ConvertI32S => { 882 let val = state.pop1(); 883 state.push1(builder.ins().fcvt_from_sint(F32, val)); 884 } 885 Operator::F32ConvertI64U | Operator::F32ConvertI32U => { 886 let val = state.pop1(); 887 state.push1(builder.ins().fcvt_from_uint(F32, val)); 888 } 889 Operator::F64ConvertI64S | Operator::F64ConvertI32S => { 890 let val = state.pop1(); 891 state.push1(builder.ins().fcvt_from_sint(F64, val)); 892 } 893 Operator::F64ConvertI64U | Operator::F64ConvertI32U => { 894 let val = state.pop1(); 895 state.push1(builder.ins().fcvt_from_uint(F64, val)); 896 } 897 Operator::F64PromoteF32 => { 898 let val = state.pop1(); 899 state.push1(builder.ins().fpromote(F64, val)); 900 } 901 Operator::F32ReinterpretI32 => { 902 let val = state.pop1(); 903 state.push1(builder.ins().bitcast(F32, MemFlags::new(), val)); 904 } 905 Operator::F64ReinterpretI64 => { 906 let val = state.pop1(); 907 state.push1(builder.ins().bitcast(F64, MemFlags::new(), val)); 908 } 909 Operator::I32ReinterpretF32 => { 910 let val = state.pop1(); 911 state.push1(builder.ins().bitcast(I32, MemFlags::new(), val)); 912 } 913 Operator::I64ReinterpretF64 => { 914 let val = state.pop1(); 915 state.push1(builder.ins().bitcast(I64, MemFlags::new(), val)); 916 } 917 918 // comparison operators 919 Operator::I32LtS | Operator::I64LtS => { 920 translate_icmp(IntCC::SignedLessThan, builder, state); 921 } 922 Operator::I32LtU | Operator::I64LtU => { 923 translate_icmp(IntCC::UnsignedLessThan, builder, state); 924 } 925 Operator::I32LeS | Operator::I64LeS => { 926 translate_icmp(IntCC::SignedLessThanOrEqual, builder, state); 927 } 928 Operator::I32LeU | Operator::I64LeU => { 929 translate_icmp(IntCC::UnsignedLessThanOrEqual, builder, state); 930 } 931 Operator::I32GtS | Operator::I64GtS => { 932 translate_icmp(IntCC::SignedGreaterThan, builder, state); 933 } 934 Operator::I32GtU | Operator::I64GtU => { 935 translate_icmp(IntCC::UnsignedGreaterThan, builder, state); 936 } 937 Operator::I32GeS | Operator::I64GeS => { 938 translate_icmp(IntCC::SignedGreaterThanOrEqual, builder, state); 939 } 940 Operator::I32GeU | Operator::I64GeU => { 941 translate_icmp(IntCC::UnsignedGreaterThanOrEqual, builder, state); 942 } 943 Operator::I32Eqz | Operator::I64Eqz => { 944 let arg = state.pop1(); 945 let val = builder.ins().icmp_imm(IntCC::Equal, arg, 0); 946 state.push1(builder.ins().uextend(I32, val)); 947 } 948 Operator::I32Eq | Operator::I64Eq => translate_icmp(IntCC::Equal, builder, state), 949 Operator::F32Eq | Operator::F64Eq => translate_fcmp(FloatCC::Equal, builder, state), 950 Operator::I32Ne | Operator::I64Ne => translate_icmp(IntCC::NotEqual, builder, state), 951 Operator::F32Ne | Operator::F64Ne => translate_fcmp(FloatCC::NotEqual, builder, state), 952 Operator::F32Gt | Operator::F64Gt => translate_fcmp(FloatCC::GreaterThan, builder, state), 953 Operator::F32Ge | Operator::F64Ge => { 954 translate_fcmp(FloatCC::GreaterThanOrEqual, builder, state); 955 } 956 Operator::F32Lt | Operator::F64Lt => translate_fcmp(FloatCC::LessThan, builder, state), 957 Operator::F32Le | Operator::F64Le => { 958 translate_fcmp(FloatCC::LessThanOrEqual, builder, state); 959 } 960 961 /******************************* Load instructions *********************************** 962 * Wasm specifies an integer alignment flag, but we drop it in Cranelift. 963 * The memory base address is provided by the environment. 964 ************************************************************************************/ 965 Operator::I32Load8U { memarg } => { 966 unwrap_or_return_unreachable_state!( 967 state, 968 translate_load(memarg, ir::Opcode::Uload8, I32, builder, state, env) 969 ); 970 } 971 Operator::I32Load16U { memarg } => { 972 unwrap_or_return_unreachable_state!( 973 state, 974 translate_load(memarg, ir::Opcode::Uload16, I32, builder, state, env) 975 ); 976 } 977 Operator::I32Load8S { memarg } => { 978 unwrap_or_return_unreachable_state!( 979 state, 980 translate_load(memarg, ir::Opcode::Sload8, I32, builder, state, env) 981 ); 982 } 983 Operator::I32Load16S { memarg } => { 984 unwrap_or_return_unreachable_state!( 985 state, 986 translate_load(memarg, ir::Opcode::Sload16, I32, builder, state, env) 987 ); 988 } 989 Operator::I64Load8U { memarg } => { 990 unwrap_or_return_unreachable_state!( 991 state, 992 translate_load(memarg, ir::Opcode::Uload8, I64, builder, state, env) 993 ); 994 } 995 Operator::I64Load16U { memarg } => { 996 unwrap_or_return_unreachable_state!( 997 state, 998 translate_load(memarg, ir::Opcode::Uload16, I64, builder, state, env) 999 ); 1000 } 1001 Operator::I64Load8S { memarg } => { 1002 unwrap_or_return_unreachable_state!( 1003 state, 1004 translate_load(memarg, ir::Opcode::Sload8, I64, builder, state, env) 1005 ); 1006 } 1007 Operator::I64Load16S { memarg } => { 1008 unwrap_or_return_unreachable_state!( 1009 state, 1010 translate_load(memarg, ir::Opcode::Sload16, I64, builder, state, env) 1011 ); 1012 } 1013 Operator::I64Load32S { memarg } => { 1014 unwrap_or_return_unreachable_state!( 1015 state, 1016 translate_load(memarg, ir::Opcode::Sload32, I64, builder, state, env) 1017 ); 1018 } 1019 Operator::I64Load32U { memarg } => { 1020 unwrap_or_return_unreachable_state!( 1021 state, 1022 translate_load(memarg, ir::Opcode::Uload32, I64, builder, state, env) 1023 ); 1024 } 1025 Operator::I32Load { memarg } => { 1026 unwrap_or_return_unreachable_state!( 1027 state, 1028 translate_load(memarg, ir::Opcode::Load, I32, builder, state, env) 1029 ); 1030 } 1031 Operator::F32Load { memarg } => { 1032 unwrap_or_return_unreachable_state!( 1033 state, 1034 translate_load(memarg, ir::Opcode::Load, F32, builder, state, env) 1035 ); 1036 } 1037 Operator::I64Load { memarg } => { 1038 unwrap_or_return_unreachable_state!( 1039 state, 1040 translate_load(memarg, ir::Opcode::Load, I64, builder, state, env) 1041 ); 1042 } 1043 Operator::F64Load { memarg } => { 1044 unwrap_or_return_unreachable_state!( 1045 state, 1046 translate_load(memarg, ir::Opcode::Load, F64, builder, state, env) 1047 ); 1048 } 1049 1050 /****************************** Store instructions *********************************** 1051 * Wasm specifies an integer alignment flag but we drop it in Cranelift. 1052 * The memory base address is provided by the environment. 1053 ************************************************************************************/ 1054 Operator::I32Store { memarg } 1055 | Operator::I64Store { memarg } 1056 | Operator::F32Store { memarg } 1057 | Operator::F64Store { memarg } => { 1058 translate_store(memarg, ir::Opcode::Store, builder, state, env)?; 1059 } 1060 Operator::I32Store8 { memarg } | Operator::I64Store8 { memarg } => { 1061 translate_store(memarg, ir::Opcode::Istore8, builder, state, env)?; 1062 } 1063 Operator::I32Store16 { memarg } | Operator::I64Store16 { memarg } => { 1064 translate_store(memarg, ir::Opcode::Istore16, builder, state, env)?; 1065 } 1066 Operator::I64Store32 { memarg } => { 1067 translate_store(memarg, ir::Opcode::Istore32, builder, state, env)?; 1068 } 1069 1070 // Sign-extension 1071 // https://github.com/WebAssembly/sign-extension-ops 1072 Operator::I32Extend8S => { 1073 let val = state.pop1(); 1074 state.push1(builder.ins().ireduce(I8, val)); 1075 let val = state.pop1(); 1076 state.push1(builder.ins().sextend(I32, val)); 1077 } 1078 Operator::I32Extend16S => { 1079 let val = state.pop1(); 1080 state.push1(builder.ins().ireduce(I16, val)); 1081 let val = state.pop1(); 1082 state.push1(builder.ins().sextend(I32, val)); 1083 } 1084 Operator::I64Extend8S => { 1085 let val = state.pop1(); 1086 state.push1(builder.ins().ireduce(I8, val)); 1087 let val = state.pop1(); 1088 state.push1(builder.ins().sextend(I64, val)); 1089 } 1090 Operator::I64Extend16S => { 1091 let val = state.pop1(); 1092 state.push1(builder.ins().ireduce(I16, val)); 1093 let val = state.pop1(); 1094 state.push1(builder.ins().sextend(I64, val)); 1095 } 1096 Operator::I64Extend32S => { 1097 let val = state.pop1(); 1098 state.push1(builder.ins().ireduce(I32, val)); 1099 let val = state.pop1(); 1100 state.push1(builder.ins().sextend(I64, val)); 1101 } 1102 1103 // Non-trapping Float-to-int Conversions 1104 // https://github.com/WebAssembly/nontrapping-float-to-int-conversions 1105 Operator::I32TruncSatF64S | Operator::I32TruncSatF32S => { 1106 let val = state.pop1(); 1107 state.push1(builder.ins().fcvt_to_sint_sat(I32, val)); 1108 } 1109 Operator::I32TruncSatF64U | Operator::I32TruncSatF32U => { 1110 let val = state.pop1(); 1111 state.push1(builder.ins().fcvt_to_uint_sat(I32, val)); 1112 } 1113 Operator::I64TruncSatF64S | Operator::I64TruncSatF32S => { 1114 let val = state.pop1(); 1115 state.push1(builder.ins().fcvt_to_sint_sat(I64, val)); 1116 } 1117 Operator::I64TruncSatF64U | Operator::I64TruncSatF32U => { 1118 let val = state.pop1(); 1119 state.push1(builder.ins().fcvt_to_uint_sat(I64, val)); 1120 } 1121 1122 // reference-types 1123 // https://github.com/WebAssembly/reference-types 1124 Operator::TableFill { table } => { 1125 let table_index = TableIndex::from_u32(*table); 1126 let len = state.pop1(); 1127 let val = state.pop1(); 1128 let dest = state.pop1(); 1129 env.translate_table_fill(builder.cursor(), table_index, dest, val, len)?; 1130 } 1131 Operator::TableGet { table: index } => { 1132 let table_index = TableIndex::from_u32(*index); 1133 let index = state.pop1(); 1134 state.push1(env.translate_table_get(builder.cursor(), table_index, index)?); 1135 } 1136 Operator::TableSet { table: index } => { 1137 let table_index = TableIndex::from_u32(*index); 1138 let value = state.pop1(); 1139 let index = state.pop1(); 1140 env.translate_table_set(builder.cursor(), table_index, value, index)?; 1141 } 1142 Operator::TableGrow { table: index } => { 1143 let table_index = TableIndex::from_u32(*index); 1144 let delta = state.pop1(); 1145 let init_value = state.pop1(); 1146 state.push1(env.translate_table_grow( 1147 builder.cursor(), 1148 table_index, 1149 delta, 1150 init_value, 1151 )?); 1152 } 1153 Operator::TableSize { table: index } => { 1154 state.push1(env.translate_table_size(builder.cursor(), TableIndex::from_u32(*index))?); 1155 } 1156 Operator::RefNull { hty } => { 1157 let hty = env.convert_heap_type(*hty); 1158 state.push1(env.translate_ref_null(builder.cursor(), &hty)); 1159 } 1160 Operator::RefIsNull => { 1161 let value = state.pop1(); 1162 state.push1(env.translate_ref_is_null(builder.cursor(), value)); 1163 } 1164 Operator::RefFunc { function_index } => { 1165 let index = FuncIndex::from_u32(*function_index); 1166 state.push1(env.translate_ref_func(builder.cursor(), index)?); 1167 } 1168 Operator::TypedSelect { ty: _ } => { 1169 // We ignore the explicit type parameter as it is only needed for 1170 // validation, which we require to have been performed before 1171 // translation. 1172 let (mut arg1, mut arg2, cond) = state.pop3(); 1173 if builder.func.dfg.value_type(arg1).is_vector() { 1174 arg1 = optionally_bitcast_vector(arg1, I8X16, builder); 1175 } 1176 if builder.func.dfg.value_type(arg2).is_vector() { 1177 arg2 = optionally_bitcast_vector(arg2, I8X16, builder); 1178 } 1179 state.push1(builder.ins().select(cond, arg1, arg2)); 1180 } 1181 // 1182 // // bulk memory operations 1183 // // https://github.com/WebAssembly/bulk-memory-operations 1184 Operator::MemoryInit { data_index, mem } => { 1185 let mem_index = MemoryIndex::from_u32(*mem); 1186 let len = state.pop1(); 1187 let src = state.pop1(); 1188 let dest = state.pop1(); 1189 env.translate_memory_init( 1190 builder.cursor(), 1191 mem_index, 1192 DataIndex::from_u32(*data_index), 1193 dest, 1194 src, 1195 len, 1196 ); 1197 } 1198 Operator::MemoryCopy { src_mem, dst_mem } => { 1199 let src_index = MemoryIndex::from_u32(*src_mem); 1200 let dst_index = MemoryIndex::from_u32(*dst_mem); 1201 let len = state.pop1(); 1202 let src_pos = state.pop1(); 1203 let dst_pos = state.pop1(); 1204 env.translate_memory_copy( 1205 builder.cursor(), 1206 src_index, 1207 dst_index, 1208 src_pos, 1209 dst_pos, 1210 len, 1211 ); 1212 } 1213 Operator::MemoryFill { mem } => { 1214 let mem_index = MemoryIndex::from_u32(*mem); 1215 let len = state.pop1(); 1216 let val = state.pop1(); 1217 let dest = state.pop1(); 1218 env.translate_memory_fill(builder.cursor(), mem_index, dest, val, len); 1219 } 1220 Operator::DataDrop { data_index } => { 1221 env.translate_data_drop(builder.cursor(), DataIndex::from_u32(*data_index)); 1222 } 1223 Operator::TableInit { 1224 elem_index, 1225 table: table_index, 1226 } => { 1227 let len = state.pop1(); 1228 let src = state.pop1(); 1229 let dest = state.pop1(); 1230 env.translate_table_init( 1231 builder.cursor(), 1232 TableIndex::from_u32(*table_index), 1233 ElemIndex::from_u32(*elem_index), 1234 dest, 1235 src, 1236 len, 1237 )?; 1238 } 1239 Operator::TableCopy { 1240 dst_table: dst_table_index, 1241 src_table: src_table_index, 1242 } => { 1243 let len = state.pop1(); 1244 let src = state.pop1(); 1245 let dest = state.pop1(); 1246 env.translate_table_copy( 1247 builder.cursor(), 1248 TableIndex::from_u32(*dst_table_index), 1249 TableIndex::from_u32(*src_table_index), 1250 dest, 1251 src, 1252 len, 1253 )?; 1254 } 1255 Operator::ElemDrop { elem_index } => { 1256 env.translate_elem_drop(builder.cursor(), ElemIndex::from_u32(*elem_index))?; 1257 } 1258 1259 // threads 1260 // https://github.com/WebAssembly/threads 1261 Operator::MemoryAtomicWait32 { memarg } | Operator::MemoryAtomicWait64 { memarg } => { 1262 // The WebAssembly MVP only supports one linear memory and 1263 // wasmparser will ensure that the memory indices specified are 1264 // zero. 1265 let implied_ty = match op { 1266 Operator::MemoryAtomicWait64 { .. } => I64, 1267 Operator::MemoryAtomicWait32 { .. } => I32, 1268 _ => unreachable!(), 1269 }; 1270 let mem_index = MemoryIndex::from_u32(memarg.memory); 1271 let timeout = state.pop1(); // 64 (fixed) 1272 let expected = state.pop1(); // 32 or 64 (per the `Ixx` in `IxxAtomicWait`) 1273 assert_eq!(builder.func.dfg.value_type(expected), implied_ty); 1274 let addr = state.pop1(); 1275 let effective_addr = if memarg.offset == 0 { 1276 addr 1277 } else { 1278 // TODO let index_type = environ.mems()[mem].index_type; 1279 let index_type = I32; 1280 let offset = builder 1281 .ins() 1282 .iconst(index_type, i64::try_from(memarg.offset).unwrap()); 1283 builder 1284 .ins() 1285 .uadd_overflow_trap(addr, offset, TrapCode::HEAP_OUT_OF_BOUNDS) 1286 }; 1287 // `fn translate_atomic_wait` can inspect the type of `expected` to figure out what 1288 // code it needs to generate, if it wants. 1289 let res = env.translate_atomic_wait( 1290 builder.cursor(), 1291 mem_index, 1292 effective_addr, 1293 expected, 1294 timeout, 1295 )?; 1296 state.push1(res); 1297 } 1298 Operator::MemoryAtomicNotify { memarg } => { 1299 let mem_index = MemoryIndex::from_u32(memarg.memory); 1300 let count = state.pop1(); // 32 (fixed) 1301 let addr = state.pop1(); 1302 let effective_addr = if memarg.offset == 0 { 1303 addr 1304 } else { 1305 // TODO let index_type = environ.mems()[mem].index_type; 1306 let index_type = I32; 1307 let offset = builder 1308 .ins() 1309 .iconst(index_type, i64::try_from(memarg.offset).unwrap()); 1310 builder 1311 .ins() 1312 .uadd_overflow_trap(addr, offset, TrapCode::HEAP_OUT_OF_BOUNDS) 1313 }; 1314 let res = 1315 env.translate_atomic_notify(builder.cursor(), mem_index, effective_addr, count)?; 1316 state.push1(res); 1317 } 1318 Operator::I32AtomicLoad { memarg } => { 1319 translate_atomic_load(I32, I32, memarg, builder, state, env)?; 1320 } 1321 Operator::I64AtomicLoad { memarg } => { 1322 translate_atomic_load(I64, I64, memarg, builder, state, env)?; 1323 } 1324 Operator::I32AtomicLoad8U { memarg } => { 1325 translate_atomic_load(I32, I8, memarg, builder, state, env)?; 1326 } 1327 Operator::I32AtomicLoad16U { memarg } => { 1328 translate_atomic_load(I32, I16, memarg, builder, state, env)?; 1329 } 1330 Operator::I64AtomicLoad8U { memarg } => { 1331 translate_atomic_load(I64, I8, memarg, builder, state, env)?; 1332 } 1333 Operator::I64AtomicLoad16U { memarg } => { 1334 translate_atomic_load(I64, I16, memarg, builder, state, env)?; 1335 } 1336 Operator::I64AtomicLoad32U { memarg } => { 1337 translate_atomic_load(I64, I32, memarg, builder, state, env)?; 1338 } 1339 1340 Operator::I32AtomicStore { memarg } => { 1341 translate_atomic_store(I32, memarg, builder, state, env)?; 1342 } 1343 Operator::I64AtomicStore { memarg } => { 1344 translate_atomic_store(I64, memarg, builder, state, env)?; 1345 } 1346 Operator::I32AtomicStore8 { memarg } => { 1347 translate_atomic_store(I8, memarg, builder, state, env)?; 1348 } 1349 Operator::I32AtomicStore16 { memarg } => { 1350 translate_atomic_store(I16, memarg, builder, state, env)?; 1351 } 1352 Operator::I64AtomicStore8 { memarg } => { 1353 translate_atomic_store(I8, memarg, builder, state, env)?; 1354 } 1355 Operator::I64AtomicStore16 { memarg } => { 1356 translate_atomic_store(I16, memarg, builder, state, env)?; 1357 } 1358 Operator::I64AtomicStore32 { memarg } => { 1359 translate_atomic_store(I32, memarg, builder, state, env)?; 1360 } 1361 1362 Operator::I32AtomicRmwAdd { memarg } => { 1363 translate_atomic_rmw(I32, I32, AtomicRmwOp::Add, memarg, builder, state, env)?; 1364 } 1365 Operator::I64AtomicRmwAdd { memarg } => { 1366 translate_atomic_rmw(I64, I64, AtomicRmwOp::Add, memarg, builder, state, env)?; 1367 } 1368 Operator::I32AtomicRmw8AddU { memarg } => { 1369 translate_atomic_rmw(I32, I8, AtomicRmwOp::Add, memarg, builder, state, env)?; 1370 } 1371 Operator::I32AtomicRmw16AddU { memarg } => { 1372 translate_atomic_rmw(I32, I16, AtomicRmwOp::Add, memarg, builder, state, env)?; 1373 } 1374 Operator::I64AtomicRmw8AddU { memarg } => { 1375 translate_atomic_rmw(I64, I8, AtomicRmwOp::Add, memarg, builder, state, env)?; 1376 } 1377 Operator::I64AtomicRmw16AddU { memarg } => { 1378 translate_atomic_rmw(I64, I16, AtomicRmwOp::Add, memarg, builder, state, env)?; 1379 } 1380 Operator::I64AtomicRmw32AddU { memarg } => { 1381 translate_atomic_rmw(I64, I32, AtomicRmwOp::Add, memarg, builder, state, env)?; 1382 } 1383 1384 Operator::I32AtomicRmwSub { memarg } => { 1385 translate_atomic_rmw(I32, I32, AtomicRmwOp::Sub, memarg, builder, state, env)?; 1386 } 1387 Operator::I64AtomicRmwSub { memarg } => { 1388 translate_atomic_rmw(I64, I64, AtomicRmwOp::Sub, memarg, builder, state, env)?; 1389 } 1390 Operator::I32AtomicRmw8SubU { memarg } => { 1391 translate_atomic_rmw(I32, I8, AtomicRmwOp::Sub, memarg, builder, state, env)?; 1392 } 1393 Operator::I32AtomicRmw16SubU { memarg } => { 1394 translate_atomic_rmw(I32, I16, AtomicRmwOp::Sub, memarg, builder, state, env)?; 1395 } 1396 Operator::I64AtomicRmw8SubU { memarg } => { 1397 translate_atomic_rmw(I64, I8, AtomicRmwOp::Sub, memarg, builder, state, env)?; 1398 } 1399 Operator::I64AtomicRmw16SubU { memarg } => { 1400 translate_atomic_rmw(I64, I16, AtomicRmwOp::Sub, memarg, builder, state, env)?; 1401 } 1402 Operator::I64AtomicRmw32SubU { memarg } => { 1403 translate_atomic_rmw(I64, I32, AtomicRmwOp::Sub, memarg, builder, state, env)?; 1404 } 1405 1406 Operator::I32AtomicRmwAnd { memarg } => { 1407 translate_atomic_rmw(I32, I32, AtomicRmwOp::And, memarg, builder, state, env)?; 1408 } 1409 Operator::I64AtomicRmwAnd { memarg } => { 1410 translate_atomic_rmw(I64, I64, AtomicRmwOp::And, memarg, builder, state, env)?; 1411 } 1412 Operator::I32AtomicRmw8AndU { memarg } => { 1413 translate_atomic_rmw(I32, I8, AtomicRmwOp::And, memarg, builder, state, env)?; 1414 } 1415 Operator::I32AtomicRmw16AndU { memarg } => { 1416 translate_atomic_rmw(I32, I16, AtomicRmwOp::And, memarg, builder, state, env)?; 1417 } 1418 Operator::I64AtomicRmw8AndU { memarg } => { 1419 translate_atomic_rmw(I64, I8, AtomicRmwOp::And, memarg, builder, state, env)?; 1420 } 1421 Operator::I64AtomicRmw16AndU { memarg } => { 1422 translate_atomic_rmw(I64, I16, AtomicRmwOp::And, memarg, builder, state, env)?; 1423 } 1424 Operator::I64AtomicRmw32AndU { memarg } => { 1425 translate_atomic_rmw(I64, I32, AtomicRmwOp::And, memarg, builder, state, env)?; 1426 } 1427 1428 Operator::I32AtomicRmwOr { memarg } => { 1429 translate_atomic_rmw(I32, I32, AtomicRmwOp::Or, memarg, builder, state, env)?; 1430 } 1431 Operator::I64AtomicRmwOr { memarg } => { 1432 translate_atomic_rmw(I64, I64, AtomicRmwOp::Or, memarg, builder, state, env)?; 1433 } 1434 Operator::I32AtomicRmw8OrU { memarg } => { 1435 translate_atomic_rmw(I32, I8, AtomicRmwOp::Or, memarg, builder, state, env)?; 1436 } 1437 Operator::I32AtomicRmw16OrU { memarg } => { 1438 translate_atomic_rmw(I32, I16, AtomicRmwOp::Or, memarg, builder, state, env)?; 1439 } 1440 Operator::I64AtomicRmw8OrU { memarg } => { 1441 translate_atomic_rmw(I64, I8, AtomicRmwOp::Or, memarg, builder, state, env)?; 1442 } 1443 Operator::I64AtomicRmw16OrU { memarg } => { 1444 translate_atomic_rmw(I64, I16, AtomicRmwOp::Or, memarg, builder, state, env)?; 1445 } 1446 Operator::I64AtomicRmw32OrU { memarg } => { 1447 translate_atomic_rmw(I64, I32, AtomicRmwOp::Or, memarg, builder, state, env)?; 1448 } 1449 1450 Operator::I32AtomicRmwXor { memarg } => { 1451 translate_atomic_rmw(I32, I32, AtomicRmwOp::Xor, memarg, builder, state, env)?; 1452 } 1453 Operator::I64AtomicRmwXor { memarg } => { 1454 translate_atomic_rmw(I64, I64, AtomicRmwOp::Xor, memarg, builder, state, env)?; 1455 } 1456 Operator::I32AtomicRmw8XorU { memarg } => { 1457 translate_atomic_rmw(I32, I8, AtomicRmwOp::Xor, memarg, builder, state, env)?; 1458 } 1459 Operator::I32AtomicRmw16XorU { memarg } => { 1460 translate_atomic_rmw(I32, I16, AtomicRmwOp::Xor, memarg, builder, state, env)?; 1461 } 1462 Operator::I64AtomicRmw8XorU { memarg } => { 1463 translate_atomic_rmw(I64, I8, AtomicRmwOp::Xor, memarg, builder, state, env)?; 1464 } 1465 Operator::I64AtomicRmw16XorU { memarg } => { 1466 translate_atomic_rmw(I64, I16, AtomicRmwOp::Xor, memarg, builder, state, env)?; 1467 } 1468 Operator::I64AtomicRmw32XorU { memarg } => { 1469 translate_atomic_rmw(I64, I32, AtomicRmwOp::Xor, memarg, builder, state, env)?; 1470 } 1471 1472 Operator::I32AtomicRmwXchg { memarg } => { 1473 translate_atomic_rmw(I32, I32, AtomicRmwOp::Xchg, memarg, builder, state, env)?; 1474 } 1475 Operator::I64AtomicRmwXchg { memarg } => { 1476 translate_atomic_rmw(I64, I64, AtomicRmwOp::Xchg, memarg, builder, state, env)?; 1477 } 1478 Operator::I32AtomicRmw8XchgU { memarg } => { 1479 translate_atomic_rmw(I32, I8, AtomicRmwOp::Xchg, memarg, builder, state, env)?; 1480 } 1481 Operator::I32AtomicRmw16XchgU { memarg } => { 1482 translate_atomic_rmw(I32, I16, AtomicRmwOp::Xchg, memarg, builder, state, env)?; 1483 } 1484 Operator::I64AtomicRmw8XchgU { memarg } => { 1485 translate_atomic_rmw(I64, I8, AtomicRmwOp::Xchg, memarg, builder, state, env)?; 1486 } 1487 Operator::I64AtomicRmw16XchgU { memarg } => { 1488 translate_atomic_rmw(I64, I16, AtomicRmwOp::Xchg, memarg, builder, state, env)?; 1489 } 1490 Operator::I64AtomicRmw32XchgU { memarg } => { 1491 translate_atomic_rmw(I64, I32, AtomicRmwOp::Xchg, memarg, builder, state, env)?; 1492 } 1493 1494 Operator::I32AtomicRmwCmpxchg { memarg } => { 1495 translate_atomic_cas(I32, I32, memarg, builder, state, env)?; 1496 } 1497 Operator::I64AtomicRmwCmpxchg { memarg } => { 1498 translate_atomic_cas(I64, I64, memarg, builder, state, env)?; 1499 } 1500 Operator::I32AtomicRmw8CmpxchgU { memarg } => { 1501 translate_atomic_cas(I32, I8, memarg, builder, state, env)?; 1502 } 1503 Operator::I32AtomicRmw16CmpxchgU { memarg } => { 1504 translate_atomic_cas(I32, I16, memarg, builder, state, env)?; 1505 } 1506 Operator::I64AtomicRmw8CmpxchgU { memarg } => { 1507 translate_atomic_cas(I64, I8, memarg, builder, state, env)?; 1508 } 1509 Operator::I64AtomicRmw16CmpxchgU { memarg } => { 1510 translate_atomic_cas(I64, I16, memarg, builder, state, env)?; 1511 } 1512 Operator::I64AtomicRmw32CmpxchgU { memarg } => { 1513 translate_atomic_cas(I64, I32, memarg, builder, state, env)?; 1514 } 1515 Operator::AtomicFence { .. } => { 1516 builder.ins().fence(); 1517 } 1518 1519 // 128-bit SIMD 1520 // - https://github.com/webassembly/simd 1521 // - https://webassembly.github.io/simd/core/binary/instructions.html 1522 Operator::V128Const { value } => { 1523 let data = value.bytes().to_vec().into(); 1524 let handle = builder.func.dfg.constants.insert(data); 1525 let value = builder.ins().vconst(I8X16, handle); 1526 // the v128.const is typed in CLIF as a I8x16 but bitcast to a different type 1527 // before use 1528 state.push1(value); 1529 } 1530 Operator::V128Load { memarg } => { 1531 unwrap_or_return_unreachable_state!( 1532 state, 1533 translate_load(memarg, ir::Opcode::Load, I8X16, builder, state, env) 1534 ); 1535 } 1536 Operator::V128Load8x8S { memarg } => { 1537 let index = state.pop1(); 1538 let mem = state.get_memory(builder.func, MemoryIndex::from_u32(memarg.memory), env); 1539 let (flags, _, base) = unwrap_or_return_unreachable_state!( 1540 state, 1541 mem.prepare_addr(builder, index, 8, memarg, env) 1542 ); 1543 let loaded = builder.ins().sload8x8(flags, base, 0i32); 1544 state.push1(loaded); 1545 } 1546 Operator::V128Load8x8U { memarg } => { 1547 let index = state.pop1(); 1548 let mem = state.get_memory(builder.func, MemoryIndex::from_u32(memarg.memory), env); 1549 let (flags, _, base) = unwrap_or_return_unreachable_state!( 1550 state, 1551 mem.prepare_addr(builder, index, 8, memarg, env) 1552 ); 1553 let loaded = builder.ins().uload8x8(flags, base, 0i32); 1554 state.push1(loaded); 1555 } 1556 Operator::V128Load16x4S { memarg } => { 1557 let index = state.pop1(); 1558 let mem = state.get_memory(builder.func, MemoryIndex::from_u32(memarg.memory), env); 1559 let (flags, _, base) = unwrap_or_return_unreachable_state!( 1560 state, 1561 mem.prepare_addr(builder, index, 8, memarg, env) 1562 ); 1563 let loaded = builder.ins().sload16x4(flags, base, 0i32); 1564 state.push1(loaded); 1565 } 1566 Operator::V128Load16x4U { memarg } => { 1567 let index = state.pop1(); 1568 let mem = state.get_memory(builder.func, MemoryIndex::from_u32(memarg.memory), env); 1569 let (flags, _, base) = unwrap_or_return_unreachable_state!( 1570 state, 1571 mem.prepare_addr(builder, index, 8, memarg, env) 1572 ); 1573 let loaded = builder.ins().uload16x4(flags, base, 0i32); 1574 state.push1(loaded); 1575 } 1576 Operator::V128Load32x2S { memarg } => { 1577 let index = state.pop1(); 1578 let mem = state.get_memory(builder.func, MemoryIndex::from_u32(memarg.memory), env); 1579 let (flags, _, base) = unwrap_or_return_unreachable_state!( 1580 state, 1581 mem.prepare_addr(builder, index, 8, memarg, env) 1582 ); 1583 let loaded = builder.ins().sload32x2(flags, base, 0i32); 1584 state.push1(loaded); 1585 } 1586 Operator::V128Load32x2U { memarg } => { 1587 let index = state.pop1(); 1588 let mem = state.get_memory(builder.func, MemoryIndex::from_u32(memarg.memory), env); 1589 let (flags, _, base) = unwrap_or_return_unreachable_state!( 1590 state, 1591 mem.prepare_addr(builder, index, 8, memarg, env) 1592 ); 1593 let loaded = builder.ins().uload32x2(flags, base, 0i32); 1594 state.push1(loaded); 1595 } 1596 Operator::V128Store { .. } => todo!(), 1597 Operator::I8x16Splat | Operator::I16x8Splat => { 1598 let reduced = builder.ins().ireduce(type_of(op).lane_type(), state.pop1()); 1599 let splatted = builder.ins().splat(type_of(op), reduced); 1600 state.push1(splatted); 1601 } 1602 Operator::I32x4Splat 1603 | Operator::I64x2Splat 1604 | Operator::F32x4Splat 1605 | Operator::F64x2Splat => { 1606 let splatted = builder.ins().splat(type_of(op), state.pop1()); 1607 state.push1(splatted); 1608 } 1609 Operator::V128Load8Splat { memarg } 1610 | Operator::V128Load16Splat { memarg } 1611 | Operator::V128Load32Splat { memarg } 1612 | Operator::V128Load64Splat { memarg } => { 1613 unwrap_or_return_unreachable_state!( 1614 state, 1615 translate_load( 1616 memarg, 1617 ir::Opcode::Load, 1618 type_of(op).lane_type(), 1619 builder, 1620 state, 1621 env, 1622 ) 1623 ); 1624 let splatted = builder.ins().splat(type_of(op), state.pop1()); 1625 state.push1(splatted); 1626 } 1627 Operator::V128Load32Zero { memarg } | Operator::V128Load64Zero { memarg } => { 1628 unwrap_or_return_unreachable_state!( 1629 state, 1630 translate_load( 1631 memarg, 1632 ir::Opcode::Load, 1633 type_of(op).lane_type(), 1634 builder, 1635 state, 1636 env, 1637 ) 1638 ); 1639 let as_vector = builder.ins().scalar_to_vector(type_of(op), state.pop1()); 1640 state.push1(as_vector); 1641 } 1642 Operator::V128Load8Lane { memarg, lane } 1643 | Operator::V128Load16Lane { memarg, lane } 1644 | Operator::V128Load32Lane { memarg, lane } 1645 | Operator::V128Load64Lane { memarg, lane } => { 1646 let vector = pop1_with_bitcast(state, type_of(op), builder); 1647 unwrap_or_return_unreachable_state!( 1648 state, 1649 translate_load( 1650 memarg, 1651 ir::Opcode::Load, 1652 type_of(op).lane_type(), 1653 builder, 1654 state, 1655 env, 1656 ) 1657 ); 1658 let replacement = state.pop1(); 1659 state.push1(builder.ins().insertlane(vector, replacement, *lane)); 1660 } 1661 Operator::V128Store8Lane { memarg, lane } 1662 | Operator::V128Store16Lane { memarg, lane } 1663 | Operator::V128Store32Lane { memarg, lane } 1664 | Operator::V128Store64Lane { memarg, lane } => { 1665 let vector = pop1_with_bitcast(state, type_of(op), builder); 1666 state.push1(builder.ins().extractlane(vector, *lane)); 1667 translate_store(memarg, ir::Opcode::Store, builder, state, env)?; 1668 } 1669 Operator::I8x16ExtractLaneS { lane } | Operator::I16x8ExtractLaneS { lane } => { 1670 let vector = pop1_with_bitcast(state, type_of(op), builder); 1671 let extracted = builder.ins().extractlane(vector, *lane); 1672 state.push1(builder.ins().sextend(I32, extracted)); 1673 } 1674 Operator::I8x16ExtractLaneU { lane } | Operator::I16x8ExtractLaneU { lane } => { 1675 let vector = pop1_with_bitcast(state, type_of(op), builder); 1676 let extracted = builder.ins().extractlane(vector, *lane); 1677 state.push1(builder.ins().uextend(I32, extracted)); 1678 // On x86, PEXTRB zeroes the upper bits of the destination register of extractlane so 1679 // uextend could be elided; for now, uextend is needed for Cranelift's type checks to 1680 // work. 1681 } 1682 Operator::I32x4ExtractLane { lane } 1683 | Operator::I64x2ExtractLane { lane } 1684 | Operator::F32x4ExtractLane { lane } 1685 | Operator::F64x2ExtractLane { lane } => { 1686 let vector = pop1_with_bitcast(state, type_of(op), builder); 1687 state.push1(builder.ins().extractlane(vector, *lane)); 1688 } 1689 Operator::I8x16ReplaceLane { lane } | Operator::I16x8ReplaceLane { lane } => { 1690 let (vector, replacement) = state.pop2(); 1691 let ty = type_of(op); 1692 let reduced = builder.ins().ireduce(ty.lane_type(), replacement); 1693 let vector = optionally_bitcast_vector(vector, ty, builder); 1694 state.push1(builder.ins().insertlane(vector, reduced, *lane)); 1695 } 1696 Operator::I32x4ReplaceLane { lane } 1697 | Operator::I64x2ReplaceLane { lane } 1698 | Operator::F32x4ReplaceLane { lane } 1699 | Operator::F64x2ReplaceLane { lane } => { 1700 let (vector, replacement) = state.pop2(); 1701 let vector = optionally_bitcast_vector(vector, type_of(op), builder); 1702 state.push1(builder.ins().insertlane(vector, replacement, *lane)); 1703 } 1704 Operator::I8x16Shuffle { lanes, .. } => { 1705 let (a, b) = pop2_with_bitcast(state, I8X16, builder); 1706 let lanes = ConstantData::from(lanes.as_ref()); 1707 let mask = builder.func.dfg.immediates.push(lanes); 1708 let shuffled = builder.ins().shuffle(a, b, mask); 1709 state.push1(shuffled); 1710 // At this point the original types of a and b are lost; users of this value (i.e. this 1711 // WASM-to-CLIF translator) may need to bitcast for type-correctness. This is due 1712 // to WASM using the less specific v128 type for certain operations and more specific 1713 // types (e.g. i8x16) for others. 1714 } 1715 Operator::I8x16Swizzle => { 1716 let (a, b) = pop2_with_bitcast(state, I8X16, builder); 1717 state.push1(builder.ins().swizzle(a, b)); 1718 } 1719 Operator::I8x16Add | Operator::I16x8Add | Operator::I32x4Add | Operator::I64x2Add => { 1720 let (a, b) = pop2_with_bitcast(state, type_of(op), builder); 1721 state.push1(builder.ins().iadd(a, b)); 1722 } 1723 Operator::I8x16AddSatS | Operator::I16x8AddSatS => { 1724 let (a, b) = pop2_with_bitcast(state, type_of(op), builder); 1725 state.push1(builder.ins().sadd_sat(a, b)); 1726 } 1727 Operator::I8x16AddSatU | Operator::I16x8AddSatU => { 1728 let (a, b) = pop2_with_bitcast(state, type_of(op), builder); 1729 state.push1(builder.ins().uadd_sat(a, b)); 1730 } 1731 Operator::I8x16Sub | Operator::I16x8Sub | Operator::I32x4Sub | Operator::I64x2Sub => { 1732 let (a, b) = pop2_with_bitcast(state, type_of(op), builder); 1733 state.push1(builder.ins().isub(a, b)); 1734 } 1735 Operator::I8x16SubSatS | Operator::I16x8SubSatS => { 1736 let (a, b) = pop2_with_bitcast(state, type_of(op), builder); 1737 state.push1(builder.ins().ssub_sat(a, b)); 1738 } 1739 Operator::I8x16SubSatU | Operator::I16x8SubSatU => { 1740 let (a, b) = pop2_with_bitcast(state, type_of(op), builder); 1741 state.push1(builder.ins().usub_sat(a, b)); 1742 } 1743 Operator::I8x16MinS | Operator::I16x8MinS | Operator::I32x4MinS => { 1744 let (a, b) = pop2_with_bitcast(state, type_of(op), builder); 1745 state.push1(builder.ins().smin(a, b)); 1746 } 1747 Operator::I8x16MinU | Operator::I16x8MinU | Operator::I32x4MinU => { 1748 let (a, b) = pop2_with_bitcast(state, type_of(op), builder); 1749 state.push1(builder.ins().umin(a, b)); 1750 } 1751 Operator::I8x16MaxS | Operator::I16x8MaxS | Operator::I32x4MaxS => { 1752 let (a, b) = pop2_with_bitcast(state, type_of(op), builder); 1753 state.push1(builder.ins().smax(a, b)); 1754 } 1755 Operator::I8x16MaxU | Operator::I16x8MaxU | Operator::I32x4MaxU => { 1756 let (a, b) = pop2_with_bitcast(state, type_of(op), builder); 1757 state.push1(builder.ins().umax(a, b)); 1758 } 1759 Operator::I8x16AvgrU | Operator::I16x8AvgrU => { 1760 let (a, b) = pop2_with_bitcast(state, type_of(op), builder); 1761 state.push1(builder.ins().avg_round(a, b)); 1762 } 1763 Operator::I8x16Neg | Operator::I16x8Neg | Operator::I32x4Neg | Operator::I64x2Neg => { 1764 let a = pop1_with_bitcast(state, type_of(op), builder); 1765 state.push1(builder.ins().ineg(a)); 1766 } 1767 Operator::I8x16Abs | Operator::I16x8Abs | Operator::I32x4Abs | Operator::I64x2Abs => { 1768 let a = pop1_with_bitcast(state, type_of(op), builder); 1769 state.push1(builder.ins().iabs(a)); 1770 } 1771 Operator::I16x8Mul | Operator::I32x4Mul | Operator::I64x2Mul => { 1772 let (a, b) = pop2_with_bitcast(state, type_of(op), builder); 1773 state.push1(builder.ins().imul(a, b)); 1774 } 1775 Operator::V128Or => { 1776 let (a, b) = pop2_with_bitcast(state, type_of(op), builder); 1777 state.push1(builder.ins().bor(a, b)); 1778 } 1779 Operator::V128Xor => { 1780 let (a, b) = pop2_with_bitcast(state, type_of(op), builder); 1781 state.push1(builder.ins().bxor(a, b)); 1782 } 1783 Operator::V128And => { 1784 let (a, b) = pop2_with_bitcast(state, type_of(op), builder); 1785 state.push1(builder.ins().band(a, b)); 1786 } 1787 Operator::V128AndNot => { 1788 let (a, b) = pop2_with_bitcast(state, type_of(op), builder); 1789 state.push1(builder.ins().band_not(a, b)); 1790 } 1791 Operator::V128Not => { 1792 let a = state.pop1(); 1793 state.push1(builder.ins().bnot(a)); 1794 } 1795 Operator::I8x16Shl | Operator::I16x8Shl | Operator::I32x4Shl | Operator::I64x2Shl => { 1796 let (a, b) = state.pop2(); 1797 let bitcast_a = optionally_bitcast_vector(a, type_of(op), builder); 1798 // The spec expects to shift with `b mod lanewidth`; This is directly compatible 1799 // with cranelift's instruction. 1800 state.push1(builder.ins().ishl(bitcast_a, b)); 1801 } 1802 Operator::I8x16ShrU | Operator::I16x8ShrU | Operator::I32x4ShrU | Operator::I64x2ShrU => { 1803 let (a, b) = state.pop2(); 1804 let bitcast_a = optionally_bitcast_vector(a, type_of(op), builder); 1805 // The spec expects to shift with `b mod lanewidth`; This is directly compatible 1806 // with cranelift's instruction. 1807 state.push1(builder.ins().ushr(bitcast_a, b)); 1808 } 1809 Operator::I8x16ShrS | Operator::I16x8ShrS | Operator::I32x4ShrS | Operator::I64x2ShrS => { 1810 let (a, b) = state.pop2(); 1811 let bitcast_a = optionally_bitcast_vector(a, type_of(op), builder); 1812 // The spec expects to shift with `b mod lanewidth`; This is directly compatible 1813 // with cranelift's instruction. 1814 state.push1(builder.ins().sshr(bitcast_a, b)); 1815 } 1816 Operator::V128Bitselect => { 1817 let (a, b, c) = pop3_with_bitcast(state, I8X16, builder); 1818 // The CLIF operand ordering is slightly different and the types of all three 1819 // operands must match (hence the bitcast). 1820 state.push1(builder.ins().bitselect(c, a, b)); 1821 } 1822 Operator::V128AnyTrue => { 1823 let a = pop1_with_bitcast(state, type_of(op), builder); 1824 let bool_result = builder.ins().vany_true(a); 1825 state.push1(builder.ins().uextend(I32, bool_result)); 1826 } 1827 Operator::I8x16AllTrue 1828 | Operator::I16x8AllTrue 1829 | Operator::I32x4AllTrue 1830 | Operator::I64x2AllTrue => { 1831 let a = pop1_with_bitcast(state, type_of(op), builder); 1832 let bool_result = builder.ins().vall_true(a); 1833 state.push1(builder.ins().uextend(I32, bool_result)); 1834 } 1835 Operator::I8x16Bitmask 1836 | Operator::I16x8Bitmask 1837 | Operator::I32x4Bitmask 1838 | Operator::I64x2Bitmask => { 1839 let a = pop1_with_bitcast(state, type_of(op), builder); 1840 state.push1(builder.ins().vhigh_bits(I32, a)); 1841 } 1842 Operator::I8x16Eq | Operator::I16x8Eq | Operator::I32x4Eq | Operator::I64x2Eq => { 1843 translate_vector_icmp(IntCC::Equal, type_of(op), builder, state); 1844 } 1845 Operator::I8x16Ne | Operator::I16x8Ne | Operator::I32x4Ne | Operator::I64x2Ne => { 1846 translate_vector_icmp(IntCC::NotEqual, type_of(op), builder, state); 1847 } 1848 Operator::I8x16GtS | Operator::I16x8GtS | Operator::I32x4GtS | Operator::I64x2GtS => { 1849 translate_vector_icmp(IntCC::SignedGreaterThan, type_of(op), builder, state); 1850 } 1851 Operator::I8x16LtS | Operator::I16x8LtS | Operator::I32x4LtS | Operator::I64x2LtS => { 1852 translate_vector_icmp(IntCC::SignedLessThan, type_of(op), builder, state); 1853 } 1854 Operator::I8x16GtU | Operator::I16x8GtU | Operator::I32x4GtU => { 1855 translate_vector_icmp(IntCC::UnsignedGreaterThan, type_of(op), builder, state); 1856 } 1857 Operator::I8x16LtU | Operator::I16x8LtU | Operator::I32x4LtU => { 1858 translate_vector_icmp(IntCC::UnsignedLessThan, type_of(op), builder, state); 1859 } 1860 Operator::I8x16GeS | Operator::I16x8GeS | Operator::I32x4GeS | Operator::I64x2GeS => { 1861 translate_vector_icmp(IntCC::SignedGreaterThanOrEqual, type_of(op), builder, state); 1862 } 1863 Operator::I8x16LeS | Operator::I16x8LeS | Operator::I32x4LeS | Operator::I64x2LeS => { 1864 translate_vector_icmp(IntCC::SignedLessThanOrEqual, type_of(op), builder, state); 1865 } 1866 Operator::I8x16GeU | Operator::I16x8GeU | Operator::I32x4GeU => translate_vector_icmp( 1867 IntCC::UnsignedGreaterThanOrEqual, 1868 type_of(op), 1869 builder, 1870 state, 1871 ), 1872 Operator::I8x16LeU | Operator::I16x8LeU | Operator::I32x4LeU => { 1873 translate_vector_icmp(IntCC::UnsignedLessThanOrEqual, type_of(op), builder, state); 1874 } 1875 Operator::F32x4Eq | Operator::F64x2Eq => { 1876 translate_vector_fcmp(FloatCC::Equal, type_of(op), builder, state); 1877 } 1878 Operator::F32x4Ne | Operator::F64x2Ne => { 1879 translate_vector_fcmp(FloatCC::NotEqual, type_of(op), builder, state); 1880 } 1881 Operator::F32x4Lt | Operator::F64x2Lt => { 1882 translate_vector_fcmp(FloatCC::LessThan, type_of(op), builder, state); 1883 } 1884 Operator::F32x4Gt | Operator::F64x2Gt => { 1885 translate_vector_fcmp(FloatCC::GreaterThan, type_of(op), builder, state); 1886 } 1887 Operator::F32x4Le | Operator::F64x2Le => { 1888 translate_vector_fcmp(FloatCC::LessThanOrEqual, type_of(op), builder, state); 1889 } 1890 Operator::F32x4Ge | Operator::F64x2Ge => { 1891 translate_vector_fcmp(FloatCC::GreaterThanOrEqual, type_of(op), builder, state); 1892 } 1893 Operator::F32x4Add | Operator::F64x2Add => { 1894 let (a, b) = pop2_with_bitcast(state, type_of(op), builder); 1895 state.push1(builder.ins().fadd(a, b)); 1896 } 1897 Operator::F32x4Sub | Operator::F64x2Sub => { 1898 let (a, b) = pop2_with_bitcast(state, type_of(op), builder); 1899 state.push1(builder.ins().fsub(a, b)); 1900 } 1901 Operator::F32x4Mul | Operator::F64x2Mul => { 1902 let (a, b) = pop2_with_bitcast(state, type_of(op), builder); 1903 state.push1(builder.ins().fmul(a, b)); 1904 } 1905 Operator::F32x4Div | Operator::F64x2Div => { 1906 let (a, b) = pop2_with_bitcast(state, type_of(op), builder); 1907 state.push1(builder.ins().fdiv(a, b)); 1908 } 1909 Operator::F32x4Max | Operator::F64x2Max => { 1910 let (a, b) = pop2_with_bitcast(state, type_of(op), builder); 1911 state.push1(builder.ins().fmax(a, b)); 1912 } 1913 Operator::F32x4Min | Operator::F64x2Min => { 1914 let (a, b) = pop2_with_bitcast(state, type_of(op), builder); 1915 state.push1(builder.ins().fmin(a, b)); 1916 } 1917 Operator::F32x4PMax | Operator::F64x2PMax => { 1918 // Note the careful ordering here with respect to `fcmp` and 1919 // `bitselect`. This matches the spec definition of: 1920 // 1921 // fpmax(z1, z2) = 1922 // * If z1 is less than z2 then return z2. 1923 // * Else return z1. 1924 let ty = type_of(op); 1925 let (a, b) = pop2_with_bitcast(state, ty, builder); 1926 let cmp = builder.ins().fcmp(FloatCC::LessThan, a, b); 1927 let cmp = optionally_bitcast_vector(cmp, ty, builder); 1928 state.push1(builder.ins().bitselect(cmp, b, a)); 1929 } 1930 Operator::F32x4PMin | Operator::F64x2PMin => { 1931 // Note the careful ordering here which is similar to `pmax` above: 1932 // 1933 // fpmin(z1, z2) = 1934 // * If z2 is less than z1 then return z2. 1935 // * Else return z1. 1936 let ty = type_of(op); 1937 let (a, b) = pop2_with_bitcast(state, ty, builder); 1938 let cmp = builder.ins().fcmp(FloatCC::LessThan, b, a); 1939 let cmp = optionally_bitcast_vector(cmp, ty, builder); 1940 state.push1(builder.ins().bitselect(cmp, b, a)); 1941 } 1942 Operator::F32x4Sqrt | Operator::F64x2Sqrt => { 1943 let a = pop1_with_bitcast(state, type_of(op), builder); 1944 state.push1(builder.ins().sqrt(a)); 1945 } 1946 Operator::F32x4Neg | Operator::F64x2Neg => { 1947 let a = pop1_with_bitcast(state, type_of(op), builder); 1948 state.push1(builder.ins().fneg(a)); 1949 } 1950 Operator::F32x4Abs | Operator::F64x2Abs => { 1951 let a = pop1_with_bitcast(state, type_of(op), builder); 1952 state.push1(builder.ins().fabs(a)); 1953 } 1954 Operator::F32x4ConvertI32x4S => { 1955 let a = pop1_with_bitcast(state, I32X4, builder); 1956 state.push1(builder.ins().fcvt_from_sint(F32X4, a)); 1957 } 1958 Operator::F32x4ConvertI32x4U => { 1959 let a = pop1_with_bitcast(state, I32X4, builder); 1960 state.push1(builder.ins().fcvt_from_uint(F32X4, a)); 1961 } 1962 Operator::F64x2ConvertLowI32x4S => { 1963 let a = pop1_with_bitcast(state, I32X4, builder); 1964 let widened_a = builder.ins().swiden_low(a); 1965 state.push1(builder.ins().fcvt_from_sint(F64X2, widened_a)); 1966 } 1967 Operator::F64x2ConvertLowI32x4U => { 1968 let a = pop1_with_bitcast(state, I32X4, builder); 1969 let widened_a = builder.ins().uwiden_low(a); 1970 state.push1(builder.ins().fcvt_from_uint(F64X2, widened_a)); 1971 } 1972 Operator::F64x2PromoteLowF32x4 => { 1973 let a = pop1_with_bitcast(state, F32X4, builder); 1974 state.push1(builder.ins().fvpromote_low(a)); 1975 } 1976 Operator::F32x4DemoteF64x2Zero => { 1977 let a = pop1_with_bitcast(state, F64X2, builder); 1978 state.push1(builder.ins().fvdemote(a)); 1979 } 1980 Operator::I32x4TruncSatF32x4S => { 1981 let a = pop1_with_bitcast(state, F32X4, builder); 1982 state.push1(builder.ins().fcvt_to_sint_sat(I32X4, a)); 1983 } 1984 Operator::I32x4TruncSatF64x2SZero => { 1985 let a = pop1_with_bitcast(state, F64X2, builder); 1986 let converted_a = builder.ins().fcvt_to_sint_sat(I64X2, a); 1987 let handle = builder.func.dfg.constants.insert(vec![0u8; 16].into()); 1988 let zero = builder.ins().vconst(I64X2, handle); 1989 1990 state.push1(builder.ins().snarrow(converted_a, zero)); 1991 } 1992 // FIXME(#5913): the relaxed instructions here are translated the same 1993 // as the saturating instructions, even when the code generator 1994 // configuration allow for different semantics across hosts. On x86, 1995 // however, it's theoretically possible to have a slightly more optimal 1996 // lowering which accounts for NaN differently, although the lowering is 1997 // still not trivial (e.g. one instruction). At this time the 1998 // more-optimal-but-still-large lowering for x86 is not implemented so 1999 // the relaxed instructions are listed here instead of down below with 2000 // the other relaxed instructions. An x86-specific implementation (or 2001 // perhaps for other backends too) should be added and the codegen for 2002 // the relaxed instruction should conditionally be different. 2003 Operator::I32x4RelaxedTruncF32x4U | Operator::I32x4TruncSatF32x4U => { 2004 let a = pop1_with_bitcast(state, F32X4, builder); 2005 state.push1(builder.ins().fcvt_to_uint_sat(I32X4, a)); 2006 } 2007 Operator::I32x4RelaxedTruncF64x2UZero | Operator::I32x4TruncSatF64x2UZero => { 2008 let a = pop1_with_bitcast(state, F64X2, builder); 2009 let converted_a = builder.ins().fcvt_to_uint_sat(I64X2, a); 2010 let handle = builder.func.dfg.constants.insert(vec![0u8; 16].into()); 2011 let zero = builder.ins().vconst(I64X2, handle); 2012 2013 state.push1(builder.ins().uunarrow(converted_a, zero)); 2014 } 2015 Operator::I8x16NarrowI16x8S => { 2016 let (a, b) = pop2_with_bitcast(state, I16X8, builder); 2017 state.push1(builder.ins().snarrow(a, b)); 2018 } 2019 Operator::I16x8NarrowI32x4S => { 2020 let (a, b) = pop2_with_bitcast(state, I32X4, builder); 2021 state.push1(builder.ins().snarrow(a, b)); 2022 } 2023 Operator::I8x16NarrowI16x8U => { 2024 let (a, b) = pop2_with_bitcast(state, I16X8, builder); 2025 state.push1(builder.ins().unarrow(a, b)); 2026 } 2027 Operator::I16x8NarrowI32x4U => { 2028 let (a, b) = pop2_with_bitcast(state, I32X4, builder); 2029 state.push1(builder.ins().unarrow(a, b)); 2030 } 2031 Operator::I16x8ExtendLowI8x16S => { 2032 let a = pop1_with_bitcast(state, I8X16, builder); 2033 state.push1(builder.ins().swiden_low(a)); 2034 } 2035 Operator::I16x8ExtendHighI8x16S => { 2036 let a = pop1_with_bitcast(state, I8X16, builder); 2037 state.push1(builder.ins().swiden_high(a)); 2038 } 2039 Operator::I16x8ExtendLowI8x16U => { 2040 let a = pop1_with_bitcast(state, I8X16, builder); 2041 state.push1(builder.ins().uwiden_low(a)); 2042 } 2043 Operator::I16x8ExtendHighI8x16U => { 2044 let a = pop1_with_bitcast(state, I8X16, builder); 2045 state.push1(builder.ins().uwiden_high(a)); 2046 } 2047 Operator::I32x4ExtendLowI16x8S => { 2048 let a = pop1_with_bitcast(state, I16X8, builder); 2049 state.push1(builder.ins().swiden_low(a)); 2050 } 2051 Operator::I32x4ExtendHighI16x8S => { 2052 let a = pop1_with_bitcast(state, I16X8, builder); 2053 state.push1(builder.ins().swiden_high(a)); 2054 } 2055 Operator::I32x4ExtendLowI16x8U => { 2056 let a = pop1_with_bitcast(state, I16X8, builder); 2057 state.push1(builder.ins().uwiden_low(a)); 2058 } 2059 Operator::I32x4ExtendHighI16x8U => { 2060 let a = pop1_with_bitcast(state, I16X8, builder); 2061 state.push1(builder.ins().uwiden_high(a)); 2062 } 2063 Operator::I64x2ExtendLowI32x4S => { 2064 let a = pop1_with_bitcast(state, I32X4, builder); 2065 state.push1(builder.ins().swiden_low(a)); 2066 } 2067 Operator::I64x2ExtendHighI32x4S => { 2068 let a = pop1_with_bitcast(state, I32X4, builder); 2069 state.push1(builder.ins().swiden_high(a)); 2070 } 2071 Operator::I64x2ExtendLowI32x4U => { 2072 let a = pop1_with_bitcast(state, I32X4, builder); 2073 state.push1(builder.ins().uwiden_low(a)); 2074 } 2075 Operator::I64x2ExtendHighI32x4U => { 2076 let a = pop1_with_bitcast(state, I32X4, builder); 2077 state.push1(builder.ins().uwiden_high(a)); 2078 } 2079 Operator::I16x8ExtAddPairwiseI8x16S => { 2080 let a = pop1_with_bitcast(state, I8X16, builder); 2081 let widen_low = builder.ins().swiden_low(a); 2082 let widen_high = builder.ins().swiden_high(a); 2083 state.push1(builder.ins().iadd_pairwise(widen_low, widen_high)); 2084 } 2085 Operator::I32x4ExtAddPairwiseI16x8S => { 2086 let a = pop1_with_bitcast(state, I16X8, builder); 2087 let widen_low = builder.ins().swiden_low(a); 2088 let widen_high = builder.ins().swiden_high(a); 2089 state.push1(builder.ins().iadd_pairwise(widen_low, widen_high)); 2090 } 2091 Operator::I16x8ExtAddPairwiseI8x16U => { 2092 let a = pop1_with_bitcast(state, I8X16, builder); 2093 let widen_low = builder.ins().uwiden_low(a); 2094 let widen_high = builder.ins().uwiden_high(a); 2095 state.push1(builder.ins().iadd_pairwise(widen_low, widen_high)); 2096 } 2097 Operator::I32x4ExtAddPairwiseI16x8U => { 2098 let a = pop1_with_bitcast(state, I16X8, builder); 2099 let widen_low = builder.ins().uwiden_low(a); 2100 let widen_high = builder.ins().uwiden_high(a); 2101 state.push1(builder.ins().iadd_pairwise(widen_low, widen_high)); 2102 } 2103 Operator::F32x4Ceil | Operator::F64x2Ceil => { 2104 // This is something of a misuse of `type_of`, because that produces the return type 2105 // of `op`. In this case we want the arg type, but we know it's the same as the 2106 // return type. Same for the 3 cases below. 2107 let arg = pop1_with_bitcast(state, type_of(op), builder); 2108 state.push1(builder.ins().ceil(arg)); 2109 } 2110 Operator::F32x4Floor | Operator::F64x2Floor => { 2111 let arg = pop1_with_bitcast(state, type_of(op), builder); 2112 state.push1(builder.ins().floor(arg)); 2113 } 2114 Operator::F32x4Trunc | Operator::F64x2Trunc => { 2115 let arg = pop1_with_bitcast(state, type_of(op), builder); 2116 state.push1(builder.ins().trunc(arg)); 2117 } 2118 Operator::F32x4Nearest | Operator::F64x2Nearest => { 2119 let arg = pop1_with_bitcast(state, type_of(op), builder); 2120 state.push1(builder.ins().nearest(arg)); 2121 } 2122 Operator::I32x4DotI16x8S => { 2123 let (a, b) = pop2_with_bitcast(state, I16X8, builder); 2124 let alo = builder.ins().swiden_low(a); 2125 let blo = builder.ins().swiden_low(b); 2126 let lo = builder.ins().imul(alo, blo); 2127 let ahi = builder.ins().swiden_high(a); 2128 let bhi = builder.ins().swiden_high(b); 2129 let hi = builder.ins().imul(ahi, bhi); 2130 state.push1(builder.ins().iadd_pairwise(lo, hi)); 2131 } 2132 Operator::I8x16Popcnt => { 2133 let arg = pop1_with_bitcast(state, type_of(op), builder); 2134 state.push1(builder.ins().popcnt(arg)); 2135 } 2136 Operator::I16x8Q15MulrSatS => { 2137 let (a, b) = pop2_with_bitcast(state, I16X8, builder); 2138 state.push1(builder.ins().sqmul_round_sat(a, b)); 2139 } 2140 Operator::I16x8ExtMulLowI8x16S => { 2141 let (a, b) = pop2_with_bitcast(state, I8X16, builder); 2142 let a_low = builder.ins().swiden_low(a); 2143 let b_low = builder.ins().swiden_low(b); 2144 state.push1(builder.ins().imul(a_low, b_low)); 2145 } 2146 Operator::I16x8ExtMulHighI8x16S => { 2147 let (a, b) = pop2_with_bitcast(state, I8X16, builder); 2148 let a_high = builder.ins().swiden_high(a); 2149 let b_high = builder.ins().swiden_high(b); 2150 state.push1(builder.ins().imul(a_high, b_high)); 2151 } 2152 Operator::I16x8ExtMulLowI8x16U => { 2153 let (a, b) = pop2_with_bitcast(state, I8X16, builder); 2154 let a_low = builder.ins().uwiden_low(a); 2155 let b_low = builder.ins().uwiden_low(b); 2156 state.push1(builder.ins().imul(a_low, b_low)); 2157 } 2158 Operator::I16x8ExtMulHighI8x16U => { 2159 let (a, b) = pop2_with_bitcast(state, I8X16, builder); 2160 let a_high = builder.ins().uwiden_high(a); 2161 let b_high = builder.ins().uwiden_high(b); 2162 state.push1(builder.ins().imul(a_high, b_high)); 2163 } 2164 Operator::I32x4ExtMulLowI16x8S => { 2165 let (a, b) = pop2_with_bitcast(state, I16X8, builder); 2166 let a_low = builder.ins().swiden_low(a); 2167 let b_low = builder.ins().swiden_low(b); 2168 state.push1(builder.ins().imul(a_low, b_low)); 2169 } 2170 Operator::I32x4ExtMulHighI16x8S => { 2171 let (a, b) = pop2_with_bitcast(state, I16X8, builder); 2172 let a_high = builder.ins().swiden_high(a); 2173 let b_high = builder.ins().swiden_high(b); 2174 state.push1(builder.ins().imul(a_high, b_high)); 2175 } 2176 Operator::I32x4ExtMulLowI16x8U => { 2177 let (a, b) = pop2_with_bitcast(state, I16X8, builder); 2178 let a_low = builder.ins().uwiden_low(a); 2179 let b_low = builder.ins().uwiden_low(b); 2180 state.push1(builder.ins().imul(a_low, b_low)); 2181 } 2182 Operator::I32x4ExtMulHighI16x8U => { 2183 let (a, b) = pop2_with_bitcast(state, I16X8, builder); 2184 let a_high = builder.ins().uwiden_high(a); 2185 let b_high = builder.ins().uwiden_high(b); 2186 state.push1(builder.ins().imul(a_high, b_high)); 2187 } 2188 Operator::I64x2ExtMulLowI32x4S => { 2189 let (a, b) = pop2_with_bitcast(state, I32X4, builder); 2190 let a_low = builder.ins().swiden_low(a); 2191 let b_low = builder.ins().swiden_low(b); 2192 state.push1(builder.ins().imul(a_low, b_low)); 2193 } 2194 Operator::I64x2ExtMulHighI32x4S => { 2195 let (a, b) = pop2_with_bitcast(state, I32X4, builder); 2196 let a_high = builder.ins().swiden_high(a); 2197 let b_high = builder.ins().swiden_high(b); 2198 state.push1(builder.ins().imul(a_high, b_high)); 2199 } 2200 Operator::I64x2ExtMulLowI32x4U => { 2201 let (a, b) = pop2_with_bitcast(state, I32X4, builder); 2202 let a_low = builder.ins().uwiden_low(a); 2203 let b_low = builder.ins().uwiden_low(b); 2204 state.push1(builder.ins().imul(a_low, b_low)); 2205 } 2206 Operator::I64x2ExtMulHighI32x4U => { 2207 let (a, b) = pop2_with_bitcast(state, I32X4, builder); 2208 let a_high = builder.ins().uwiden_high(a); 2209 let b_high = builder.ins().uwiden_high(b); 2210 state.push1(builder.ins().imul(a_high, b_high)); 2211 } 2212 2213 // Relaxed SIMD operators 2214 // https://github.com/WebAssembly/relaxed-simd 2215 Operator::F32x4RelaxedMax | Operator::F64x2RelaxedMax => { 2216 let ty = type_of(op); 2217 let (a, b) = pop2_with_bitcast(state, ty, builder); 2218 state.push1(if env.relaxed_simd_deterministic() || !env.is_x86() { 2219 // Deterministic semantics match the `fmax` instruction, or 2220 // the `fAAxBB.max` wasm instruction. 2221 builder.ins().fmax(a, b) 2222 } else { 2223 // Note that this matches the `pmax` translation which has 2224 // careful ordering of its operands to trigger 2225 // pattern-matches in the x86 backend. 2226 let cmp = builder.ins().fcmp(FloatCC::LessThan, a, b); 2227 let cmp = optionally_bitcast_vector(cmp, ty, builder); 2228 builder.ins().bitselect(cmp, b, a) 2229 }); 2230 } 2231 2232 Operator::F32x4RelaxedMin | Operator::F64x2RelaxedMin => { 2233 let ty = type_of(op); 2234 let (a, b) = pop2_with_bitcast(state, ty, builder); 2235 state.push1(if env.relaxed_simd_deterministic() || !env.is_x86() { 2236 // Deterministic semantics match the `fmin` instruction, or 2237 // the `fAAxBB.min` wasm instruction. 2238 builder.ins().fmin(a, b) 2239 } else { 2240 // Note that this matches the `pmin` translation which has 2241 // careful ordering of its operands to trigger 2242 // pattern-matches in the x86 backend. 2243 let cmp = builder.ins().fcmp(FloatCC::LessThan, b, a); 2244 let cmp = optionally_bitcast_vector(cmp, ty, builder); 2245 builder.ins().bitselect(cmp, b, a) 2246 }); 2247 } 2248 2249 Operator::I8x16RelaxedSwizzle => { 2250 let (a, b) = pop2_with_bitcast(state, I8X16, builder); 2251 state.push1( 2252 if env.relaxed_simd_deterministic() || !env.use_x86_pshufb_for_relaxed_swizzle() { 2253 // Deterministic semantics match the `i8x16.swizzle` 2254 // instruction which is the CLIF `swizzle`. 2255 builder.ins().swizzle(a, b) 2256 } else { 2257 builder.ins().x86_pshufb(a, b) 2258 }, 2259 ); 2260 } 2261 2262 Operator::F32x4RelaxedMadd | Operator::F64x2RelaxedMadd => { 2263 let (a, b, c) = pop3_with_bitcast(state, type_of(op), builder); 2264 state.push1( 2265 if env.relaxed_simd_deterministic() || env.has_native_fma() { 2266 // Deterministic semantics are "fused multiply and add" 2267 // which the CLIF `fma` guarantees. 2268 builder.ins().fma(a, b, c) 2269 } else { 2270 let mul = builder.ins().fmul(a, b); 2271 builder.ins().fadd(mul, c) 2272 }, 2273 ); 2274 } 2275 Operator::F32x4RelaxedNmadd | Operator::F64x2RelaxedNmadd => { 2276 let (a, b, c) = pop3_with_bitcast(state, type_of(op), builder); 2277 let a = builder.ins().fneg(a); 2278 state.push1( 2279 if env.relaxed_simd_deterministic() || env.has_native_fma() { 2280 // Deterministic semantics are "fused multiply and add" 2281 // which the CLIF `fma` guarantees. 2282 builder.ins().fma(a, b, c) 2283 } else { 2284 let mul = builder.ins().fmul(a, b); 2285 builder.ins().fadd(mul, c) 2286 }, 2287 ); 2288 } 2289 2290 Operator::I8x16RelaxedLaneselect 2291 | Operator::I16x8RelaxedLaneselect 2292 | Operator::I32x4RelaxedLaneselect 2293 | Operator::I64x2RelaxedLaneselect => { 2294 let ty = type_of(op); 2295 let (a, b, c) = pop3_with_bitcast(state, ty, builder); 2296 // Note that the variable swaps here are intentional due to 2297 // the difference of the order of the wasm op and the clif 2298 // op. 2299 state.push1( 2300 if env.relaxed_simd_deterministic() 2301 || !env.use_x86_blendv_for_relaxed_laneselect(ty) 2302 { 2303 // Deterministic semantics are a `bitselect` along the lines 2304 // of the wasm `v128.bitselect` instruction. 2305 builder.ins().bitselect(c, a, b) 2306 } else { 2307 builder.ins().x86_blendv(c, a, b) 2308 }, 2309 ); 2310 } 2311 2312 Operator::I32x4RelaxedTruncF32x4S => { 2313 let a = pop1_with_bitcast(state, F32X4, builder); 2314 state.push1(if env.relaxed_simd_deterministic() || !env.is_x86() { 2315 // Deterministic semantics are to match the 2316 // `i32x4.trunc_sat_f32x4_s` instruction. 2317 builder.ins().fcvt_to_sint_sat(I32X4, a) 2318 } else { 2319 builder.ins().x86_cvtt2dq(I32X4, a) 2320 }); 2321 } 2322 Operator::I32x4RelaxedTruncF64x2SZero => { 2323 let a = pop1_with_bitcast(state, F64X2, builder); 2324 let converted_a = if env.relaxed_simd_deterministic() || !env.is_x86() { 2325 // Deterministic semantics are to match the 2326 // `i32x4.trunc_sat_f64x2_s_zero` instruction. 2327 builder.ins().fcvt_to_sint_sat(I64X2, a) 2328 } else { 2329 builder.ins().x86_cvtt2dq(I64X2, a) 2330 }; 2331 let handle = builder.func.dfg.constants.insert(vec![0u8; 16].into()); 2332 let zero = builder.ins().vconst(I64X2, handle); 2333 2334 state.push1(builder.ins().snarrow(converted_a, zero)); 2335 } 2336 Operator::I16x8RelaxedQ15mulrS => { 2337 let (a, b) = pop2_with_bitcast(state, I16X8, builder); 2338 state.push1( 2339 if env.relaxed_simd_deterministic() || !env.use_x86_pmulhrsw_for_relaxed_q15mul() { 2340 // Deterministic semantics are to match the 2341 // `i16x8.q15mulr_sat_s` instruction. 2342 builder.ins().sqmul_round_sat(a, b) 2343 } else { 2344 builder.ins().x86_pmulhrsw(a, b) 2345 }, 2346 ); 2347 } 2348 Operator::I16x8RelaxedDotI8x16I7x16S => { 2349 let (a, b) = pop2_with_bitcast(state, I8X16, builder); 2350 state.push1( 2351 if env.relaxed_simd_deterministic() || !env.use_x86_pmaddubsw_for_dot() { 2352 // Deterministic semantics are to treat both operands as 2353 // signed integers and perform the dot product. 2354 let alo = builder.ins().swiden_low(a); 2355 let blo = builder.ins().swiden_low(b); 2356 let lo = builder.ins().imul(alo, blo); 2357 let ahi = builder.ins().swiden_high(a); 2358 let bhi = builder.ins().swiden_high(b); 2359 let hi = builder.ins().imul(ahi, bhi); 2360 builder.ins().iadd_pairwise(lo, hi) 2361 } else { 2362 builder.ins().x86_pmaddubsw(a, b) 2363 }, 2364 ); 2365 } 2366 2367 Operator::I32x4RelaxedDotI8x16I7x16AddS => { 2368 let c = pop1_with_bitcast(state, I32X4, builder); 2369 let (a, b) = pop2_with_bitcast(state, I8X16, builder); 2370 let dot = if env.relaxed_simd_deterministic() || !env.use_x86_pmaddubsw_for_dot() { 2371 // Deterministic semantics are to treat both operands as 2372 // signed integers and perform the dot product. 2373 let alo = builder.ins().swiden_low(a); 2374 let blo = builder.ins().swiden_low(b); 2375 let lo = builder.ins().imul(alo, blo); 2376 let ahi = builder.ins().swiden_high(a); 2377 let bhi = builder.ins().swiden_high(b); 2378 let hi = builder.ins().imul(ahi, bhi); 2379 builder.ins().iadd_pairwise(lo, hi) 2380 } else { 2381 builder.ins().x86_pmaddubsw(a, b) 2382 }; 2383 let dotlo = builder.ins().swiden_low(dot); 2384 let dothi = builder.ins().swiden_high(dot); 2385 let dot32 = builder.ins().iadd_pairwise(dotlo, dothi); 2386 state.push1(builder.ins().iadd(dot32, c)); 2387 } 2388 2389 // Typed Function references 2390 // https://github.com/WebAssembly/function-references 2391 Operator::CallRef { type_index } => { 2392 // Get function signature 2393 // `index` is the index of the function's signature and `table_index` is the index of 2394 // the table to search the function in. 2395 let (sigref, num_args) = 2396 state.get_indirect_sig(builder.func, TypeIndex::from_u32(*type_index), env); 2397 let callee = state.pop1(); 2398 2399 // Get the `callee` operand type and check whether it's `Some(Some(<reference type>))` and 2400 // that the reference type is non-nullable in which case we can omit the null check. 2401 // If `get_operand_type` returns `Some(None)` that means it doesn't know in which case we 2402 // default to explicit null checks too. 2403 let ty = validator.get_operand_type(0); 2404 let needs_null_check = ty.expect("expected operand on stack").is_none_or(|ty| { 2405 let ty = ty.as_reference_type().expect("expected reference type"); 2406 2407 ty.is_nullable() 2408 }); 2409 2410 // Bitcast any vector arguments to their default type, I8X16, before calling. 2411 let args = state.peekn_mut(num_args); 2412 bitcast_wasm_params(sigref, args, builder, env); 2413 2414 let call = env.translate_call_ref( 2415 builder, 2416 sigref, 2417 callee, 2418 state.peekn(num_args), 2419 needs_null_check, 2420 )?; 2421 2422 let inst_results = builder.inst_results(call); 2423 debug_assert_eq!( 2424 inst_results.len(), 2425 builder.func.dfg.signatures[sigref].returns.len(), 2426 "translate_call_ref results should match the call signature" 2427 ); 2428 state.popn(num_args); 2429 state.pushn(inst_results); 2430 } 2431 Operator::RefAsNonNull => { 2432 let r = state.pop1(); 2433 let is_null = env.translate_ref_is_null(builder.cursor(), r); 2434 builder.ins().trapnz(is_null, TRAP_NULL_REFERENCE); 2435 state.push1(r); 2436 } 2437 Operator::BrOnNull { relative_depth } => { 2438 let r = state.pop1(); 2439 let (br_destination, inputs) = translate_br_if_args(*relative_depth, state); 2440 let is_null = env.translate_ref_is_null(builder.cursor(), r); 2441 let else_block = builder.create_block(); 2442 canonicalise_brif(builder, is_null, br_destination, inputs, else_block, &[]); 2443 2444 builder.seal_block(else_block); // The only predecessor is the current block. 2445 builder.switch_to_block(else_block); 2446 state.push1(r); 2447 } 2448 Operator::BrOnNonNull { relative_depth } => { 2449 // We write this a bit differently from the spec to avoid an extra 2450 // block/branch and the typed accounting thereof. Instead of the 2451 // spec's approach, it's described as such: 2452 // Peek the value val from the stack. 2453 // If val is ref.null ht, then: pop the value val from the stack. 2454 // Else: Execute the instruction (br relative_depth). 2455 let is_null = env.translate_ref_is_null(builder.cursor(), state.peek1()); 2456 let (br_destination, inputs) = translate_br_if_args(*relative_depth, state); 2457 let else_block = builder.create_block(); 2458 canonicalise_brif(builder, is_null, else_block, &[], br_destination, inputs); 2459 2460 // In the null case, pop the ref 2461 state.pop1(); 2462 2463 builder.seal_block(else_block); // The only predecessor is the current block. 2464 2465 // The rest of the translation operates on our is null case, which is 2466 // currently an empty block 2467 builder.switch_to_block(else_block); 2468 } 2469 Operator::ReturnCallRef { type_index } => { 2470 // Get function signature 2471 // `index` is the index of the function's signature and `table_index` is the index of 2472 // the table to search the function in. 2473 let (sigref, num_args) = 2474 state.get_indirect_sig(builder.func, TypeIndex::from_u32(*type_index), env); 2475 let callee = state.pop1(); 2476 2477 // Bitcast any vector arguments to their default type, I8X16, before calling. 2478 let args = state.peekn_mut(num_args); 2479 bitcast_wasm_params(sigref, args, builder, env); 2480 2481 env.translate_return_call_ref(builder, sigref, callee, state.peekn(num_args))?; 2482 2483 state.popn(num_args); 2484 state.reachable = false; 2485 } 2486 2487 // Garbage Collection 2488 // http://github.com/WebAssembly/gc 2489 Operator::RefI31 => { 2490 let val = state.pop1(); 2491 let i31ref = env.translate_ref_i31(builder.cursor(), val)?; 2492 state.push1(i31ref); 2493 } 2494 Operator::I31GetS => { 2495 let i31ref = state.pop1(); 2496 let val = env.translate_i31_get_s(builder.cursor(), i31ref)?; 2497 state.push1(val); 2498 } 2499 Operator::I31GetU => { 2500 let i31ref = state.pop1(); 2501 let val = env.translate_i31_get_u(builder.cursor(), i31ref)?; 2502 state.push1(val); 2503 } 2504 Operator::StructNew { .. } 2505 | Operator::StructNewDefault { .. } 2506 | Operator::StructGet { .. } 2507 | Operator::StructGetS { .. } 2508 | Operator::StructGetU { .. } 2509 | Operator::StructSet { .. } 2510 | Operator::ArrayNew { .. } 2511 | Operator::ArrayNewDefault { .. } 2512 | Operator::ArrayNewFixed { .. } 2513 | Operator::ArrayNewData { .. } 2514 | Operator::ArrayNewElem { .. } 2515 | Operator::ArrayGet { .. } 2516 | Operator::ArrayGetS { .. } 2517 | Operator::ArrayGetU { .. } 2518 | Operator::ArraySet { .. } 2519 | Operator::ArrayLen 2520 | Operator::ArrayFill { .. } 2521 | Operator::ArrayCopy { .. } 2522 | Operator::ArrayInitData { .. } 2523 | Operator::ArrayInitElem { .. } 2524 | Operator::RefTestNonNull { .. } 2525 | Operator::RefTestNullable { .. } 2526 | Operator::RefCastNonNull { .. } 2527 | Operator::RefCastNullable { .. } 2528 | Operator::BrOnCast { .. } 2529 | Operator::BrOnCastFail { .. } 2530 | Operator::AnyConvertExtern 2531 | Operator::ExternConvertAny 2532 | Operator::RefEq => { 2533 bail!( 2534 "Feature used by the WebAssembly code is not yet supported: Garbage Collection Proposal" 2535 ); 2536 } 2537 2538 /******************************* Active Proposals *****************************************/ 2539 // memory control (experimental) 2540 // https://github.com/WebAssembly/memory-control 2541 Operator::MemoryDiscard { .. } => { 2542 bail!( 2543 "Feature used by the WebAssembly code is not yet supported: Memory Control Proposal" 2544 ); 2545 } 2546 2547 // shared-everything threads 2548 // https://github.com/WebAssembly/shared-everything-threads 2549 Operator::GlobalAtomicGet { .. } 2550 | Operator::GlobalAtomicSet { .. } 2551 | Operator::GlobalAtomicRmwAdd { .. } 2552 | Operator::GlobalAtomicRmwSub { .. } 2553 | Operator::GlobalAtomicRmwAnd { .. } 2554 | Operator::GlobalAtomicRmwOr { .. } 2555 | Operator::GlobalAtomicRmwXor { .. } 2556 | Operator::GlobalAtomicRmwXchg { .. } 2557 | Operator::GlobalAtomicRmwCmpxchg { .. } 2558 | Operator::TableAtomicGet { .. } 2559 | Operator::TableAtomicSet { .. } 2560 | Operator::TableAtomicRmwXchg { .. } 2561 | Operator::TableAtomicRmwCmpxchg { .. } 2562 | Operator::StructAtomicGet { .. } 2563 | Operator::StructAtomicGetS { .. } 2564 | Operator::StructAtomicGetU { .. } 2565 | Operator::StructAtomicSet { .. } 2566 | Operator::StructAtomicRmwAdd { .. } 2567 | Operator::StructAtomicRmwSub { .. } 2568 | Operator::StructAtomicRmwAnd { .. } 2569 | Operator::StructAtomicRmwOr { .. } 2570 | Operator::StructAtomicRmwXor { .. } 2571 | Operator::StructAtomicRmwXchg { .. } 2572 | Operator::StructAtomicRmwCmpxchg { .. } 2573 | Operator::ArrayAtomicGet { .. } 2574 | Operator::ArrayAtomicGetS { .. } 2575 | Operator::ArrayAtomicGetU { .. } 2576 | Operator::ArrayAtomicSet { .. } 2577 | Operator::ArrayAtomicRmwAdd { .. } 2578 | Operator::ArrayAtomicRmwSub { .. } 2579 | Operator::ArrayAtomicRmwAnd { .. } 2580 | Operator::ArrayAtomicRmwOr { .. } 2581 | Operator::ArrayAtomicRmwXor { .. } 2582 | Operator::ArrayAtomicRmwXchg { .. } 2583 | Operator::ArrayAtomicRmwCmpxchg { .. } 2584 | Operator::RefI31Shared => { 2585 bail!( 2586 "Feature used by the WebAssembly code is not yet supported: Shared-Everything Threads Proposal" 2587 ); 2588 } 2589 2590 // Exception handling 2591 // https://github.com/WebAssembly/exception-handling 2592 Operator::TryTable { .. } | Operator::Throw { .. } | Operator::ThrowRef => { 2593 bail!( 2594 "Feature used by the WebAssembly code is not yet supported: Exception Handling Proposal" 2595 ); 2596 } 2597 // Deprecated old instructions from the exceptions proposal 2598 Operator::Try { .. } 2599 | Operator::Catch { .. } 2600 | Operator::Rethrow { .. } 2601 | Operator::Delegate { .. } 2602 | Operator::CatchAll => { 2603 bail!( 2604 "Feature used by the WebAssembly code is not yet supported: Legacy Exception Handling Proposal" 2605 ); 2606 } 2607 2608 // Stack switching 2609 // https://github.com/WebAssembly/stack-switching 2610 Operator::ContNew { .. } 2611 | Operator::ContBind { .. } 2612 | Operator::Suspend { .. } 2613 | Operator::Resume { .. } 2614 | Operator::ResumeThrow { .. } 2615 | Operator::Switch { .. } => { 2616 bail!( 2617 "Feature used by the WebAssembly code is not yet supported: Stack Switching Proposal" 2618 ); 2619 } 2620 2621 // wide arithmetic 2622 // https://github.com/WebAssembly/wide-arithmetic 2623 Operator::I64Add128 2624 | Operator::I64Sub128 2625 | Operator::I64MulWideS 2626 | Operator::I64MulWideU => { 2627 bail!( 2628 "Feature used by the WebAssembly code is not yet supported: Wide Arithmetic Proposal" 2629 ); 2630 } 2631 2632 _ => unimplemented!(), 2633 } 2634 2635 Ok(()) 2636} 2637 2638/// Deals with a Wasm instruction located in an unreachable portion of the code. Most of them 2639/// are dropped but special ones like `End` or `Else` signal the potential end of the unreachable 2640/// portion so the translation state must be updated accordingly. 2641fn translate_unreachable_operator( 2642 validator: &FuncValidator<impl WasmModuleResources>, 2643 op: &Operator, 2644 builder: &mut FunctionBuilder, 2645 state: &mut FuncTranslationState, 2646 env: &mut TranslationEnvironment, 2647) { 2648 debug_assert!(!state.reachable); 2649 match *op { 2650 Operator::If { blockty } => { 2651 // Push a placeholder control stack entry. The if isn't reachable, 2652 // so we don't have any branches anywhere. 2653 state.push_if( 2654 ir::Block::reserved_value(), 2655 ElseData::NoElse { 2656 branch_inst: ir::Inst::reserved_value(), 2657 placeholder: ir::Block::reserved_value(), 2658 }, 2659 0, 2660 0, 2661 blockty, 2662 ); 2663 } 2664 Operator::Loop { blockty: _ } | Operator::Block { blockty: _ } => { 2665 state.push_block(ir::Block::reserved_value(), 0, 0); 2666 } 2667 Operator::Else => { 2668 let i = state.control_stack.len().checked_sub(1).unwrap(); 2669 match state.control_stack[i] { 2670 ControlStackFrame::If { 2671 ref else_data, 2672 head_is_reachable, 2673 ref mut consequent_ends_reachable, 2674 blocktype, 2675 .. 2676 } => { 2677 debug_assert!(consequent_ends_reachable.is_none()); 2678 *consequent_ends_reachable = Some(state.reachable); 2679 2680 if head_is_reachable { 2681 // We have a branch from the head of the `if` to the `else`. 2682 state.reachable = true; 2683 2684 let else_block = match *else_data { 2685 ElseData::NoElse { 2686 branch_inst, 2687 placeholder, 2688 } => { 2689 let (params, _results) = 2690 blocktype_params_results(validator, blocktype); 2691 let else_block = block_with_params(builder, params, env); 2692 let frame = state.control_stack.last().unwrap(); 2693 frame.truncate_value_stack_to_else_params(&mut state.stack); 2694 2695 // We change the target of the branch instruction. 2696 builder.change_jump_destination( 2697 branch_inst, 2698 placeholder, 2699 else_block, 2700 ); 2701 builder.seal_block(else_block); 2702 else_block 2703 } 2704 ElseData::WithElse { else_block } => { 2705 let frame = state.control_stack.last().unwrap(); 2706 frame.truncate_value_stack_to_else_params(&mut state.stack); 2707 else_block 2708 } 2709 }; 2710 2711 builder.switch_to_block(else_block); 2712 2713 // Again, no need to push the parameters for the `else`, 2714 // since we already did when we saw the original `if`. See 2715 // the comment for translating `Operator::Else` in 2716 // `translate_operator` for details. 2717 } 2718 } 2719 _ => unreachable!(), 2720 } 2721 } 2722 Operator::End => { 2723 let stack = &mut state.stack; 2724 let control_stack = &mut state.control_stack; 2725 let frame = control_stack.pop().unwrap(); 2726 2727 // Pop unused parameters from stack. 2728 frame.truncate_value_stack_to_original_size(stack); 2729 2730 let reachable_anyway = match frame { 2731 // If it is a loop we also have to seal the body loop block 2732 ControlStackFrame::Loop { header, .. } => { 2733 builder.seal_block(header); 2734 // And loops can't have branches to the end. 2735 false 2736 } 2737 // Since we are only in this function when in unreachable code, 2738 // we know that the alternative just ended unreachable. Whether 2739 // the following block is reachable depends on if the consequent 2740 // ended reachable or not. 2741 ControlStackFrame::If { 2742 head_is_reachable, 2743 consequent_ends_reachable: Some(consequent_ends_reachable), 2744 .. 2745 } => head_is_reachable && consequent_ends_reachable, 2746 // If we never set `consequent_ends_reachable` then that means 2747 // we are finishing the consequent now, and there was no 2748 // `else`. Whether the following block is reachable depends only 2749 // on if the head was reachable. 2750 ControlStackFrame::If { 2751 head_is_reachable, 2752 consequent_ends_reachable: None, 2753 .. 2754 } => head_is_reachable, 2755 // All other control constructs are already handled. 2756 _ => false, 2757 }; 2758 2759 if frame.exit_is_branched_to() || reachable_anyway { 2760 builder.switch_to_block(frame.following_code()); 2761 builder.seal_block(frame.following_code()); 2762 2763 // And add the return values of the block but only if the next block is reachable 2764 // (which corresponds to testing if the stack depth is 1) 2765 stack.extend_from_slice(builder.block_params(frame.following_code())); 2766 state.reachable = true; 2767 } 2768 } 2769 _ => { 2770 // We don't parse because this is unreachable code 2771 } 2772 } 2773} 2774 2775/// Translate a load instruction. 2776/// 2777/// Returns the execution state's reachability after the load is translated. 2778fn translate_load( 2779 memarg: &MemArg, 2780 opcode: ir::Opcode, 2781 result_ty: Type, 2782 builder: &mut FunctionBuilder, 2783 state: &mut FuncTranslationState, 2784 env: &mut TranslationEnvironment, 2785) -> Reachability<()> { 2786 let memory_index = MemoryIndex::from_u32(memarg.memory); 2787 let index = state.pop1(); 2788 let mem_op_size = mem_op_size(opcode, result_ty); 2789 2790 let mem = state.get_memory(builder.func, memory_index, env); 2791 let (flags, _wasm_index, base) = 2792 match mem.prepare_addr(builder, index, mem_op_size, memarg, env) { 2793 Reachability::Unreachable => return Reachability::Unreachable, 2794 Reachability::Reachable((f, i, b)) => (f, i, b), 2795 }; 2796 2797 let (load, dfg) = builder 2798 .ins() 2799 .Load(opcode, result_ty, flags, Offset32::new(0), base); 2800 state.push1(dfg.first_result(load)); 2801 2802 Reachability::Reachable(()) 2803} 2804 2805/// Translate a store instruction. 2806fn translate_store( 2807 memarg: &MemArg, 2808 opcode: ir::Opcode, 2809 builder: &mut FunctionBuilder, 2810 state: &mut FuncTranslationState, 2811 env: &mut TranslationEnvironment, 2812) -> crate::Result<()> { 2813 let memory_index = MemoryIndex::from_u32(memarg.memory); 2814 let val = state.pop1(); 2815 let index = state.pop1(); 2816 let val_ty = builder.func.dfg.value_type(val); 2817 let mem_op_size = mem_op_size(opcode, val_ty); 2818 2819 let mem = state.get_memory(builder.func, memory_index, env); 2820 let (flags, _wasm_index, base) = unwrap_or_return_unreachable_state!( 2821 state, 2822 mem.prepare_addr(builder, index, mem_op_size, memarg, env) 2823 ); 2824 2825 builder 2826 .ins() 2827 .Store(opcode, val_ty, flags, Offset32::new(0), val, base); 2828 Ok(()) 2829} 2830 2831fn translate_atomic_rmw( 2832 _widened_ty: Type, 2833 _access_ty: Type, 2834 _op: AtomicRmwOp, 2835 _memarg: &MemArg, 2836 _builder: &mut FunctionBuilder, 2837 _state: &mut FuncTranslationState, 2838 _env: &mut TranslationEnvironment, 2839) -> crate::Result<()> { 2840 todo!() 2841} 2842 2843fn translate_atomic_cas( 2844 _widened_ty: Type, 2845 _access_ty: Type, 2846 _memarg: &MemArg, 2847 _builder: &mut FunctionBuilder, 2848 _state: &mut FuncTranslationState, 2849 _env: &mut TranslationEnvironment, 2850) -> crate::Result<()> { 2851 todo!() 2852} 2853 2854fn translate_atomic_load( 2855 _widened_ty: Type, 2856 _access_ty: Type, 2857 _memarg: &MemArg, 2858 _builder: &mut FunctionBuilder, 2859 _state: &mut FuncTranslationState, 2860 _env: &mut TranslationEnvironment, 2861) -> crate::Result<()> { 2862 todo!() 2863} 2864 2865fn translate_atomic_store( 2866 _access_ty: Type, 2867 _memarg: &MemArg, 2868 _builder: &mut FunctionBuilder, 2869 _state: &mut FuncTranslationState, 2870 _env: &mut TranslationEnvironment, 2871) -> crate::Result<()> { 2872 todo!() 2873} 2874 2875fn mem_op_size(opcode: ir::Opcode, ty: Type) -> u8 { 2876 match opcode { 2877 ir::Opcode::Istore8 | ir::Opcode::Sload8 | ir::Opcode::Uload8 => 1, 2878 ir::Opcode::Istore16 | ir::Opcode::Sload16 | ir::Opcode::Uload16 => 2, 2879 ir::Opcode::Istore32 | ir::Opcode::Sload32 | ir::Opcode::Uload32 => 4, 2880 ir::Opcode::Store | ir::Opcode::Load => u8::try_from(ty.bytes()).unwrap(), 2881 _ => panic!("unknown size of mem op for {opcode:?}"), 2882 } 2883} 2884 2885fn translate_icmp(cc: IntCC, builder: &mut FunctionBuilder, state: &mut FuncTranslationState) { 2886 let (arg0, arg1) = state.pop2(); 2887 let val = builder.ins().icmp(cc, arg0, arg1); 2888 state.push1(builder.ins().uextend(I32, val)); 2889} 2890 2891fn translate_vector_icmp( 2892 cc: IntCC, 2893 needed_type: Type, 2894 builder: &mut FunctionBuilder, 2895 state: &mut FuncTranslationState, 2896) { 2897 let (a, b) = state.pop2(); 2898 let bitcast_a = optionally_bitcast_vector(a, needed_type, builder); 2899 let bitcast_b = optionally_bitcast_vector(b, needed_type, builder); 2900 state.push1(builder.ins().icmp(cc, bitcast_a, bitcast_b)); 2901} 2902 2903fn translate_fcmp(cc: FloatCC, builder: &mut FunctionBuilder, state: &mut FuncTranslationState) { 2904 let (arg0, arg1) = state.pop2(); 2905 let val = builder.ins().fcmp(cc, arg0, arg1); 2906 state.push1(builder.ins().uextend(I32, val)); 2907} 2908 2909fn translate_vector_fcmp( 2910 cc: FloatCC, 2911 needed_type: Type, 2912 builder: &mut FunctionBuilder, 2913 state: &mut FuncTranslationState, 2914) { 2915 let (a, b) = state.pop2(); 2916 let bitcast_a = optionally_bitcast_vector(a, needed_type, builder); 2917 let bitcast_b = optionally_bitcast_vector(b, needed_type, builder); 2918 state.push1(builder.ins().fcmp(cc, bitcast_a, bitcast_b)); 2919} 2920 2921fn translate_br_if( 2922 relative_depth: u32, 2923 builder: &mut FunctionBuilder, 2924 state: &mut FuncTranslationState, 2925) { 2926 let val = state.pop1(); 2927 let (br_destination, inputs) = translate_br_if_args(relative_depth, state); 2928 let next_block = builder.create_block(); 2929 canonicalise_brif(builder, val, br_destination, inputs, next_block, &[]); 2930 2931 builder.seal_block(next_block); // The only predecessor is the current block. 2932 builder.switch_to_block(next_block); 2933} 2934 2935fn translate_br_if_args( 2936 relative_depth: u32, 2937 state: &mut FuncTranslationState, 2938) -> (ir::Block, &mut [Value]) { 2939 // FIXME fix this ugly mess 2940 let i = state 2941 .control_stack 2942 .len() 2943 .checked_sub(1) 2944 .unwrap() 2945 .checked_sub(usize::try_from(relative_depth).unwrap()) 2946 .unwrap(); 2947 2948 let (return_count, br_destination) = { 2949 let frame = &mut state.control_stack[i]; 2950 // The values returned by the branch are still available for the reachable 2951 // code that comes after it 2952 frame.set_branched_to_exit(); 2953 let return_count = if frame.is_loop() { 2954 frame.num_param_values() 2955 } else { 2956 frame.num_return_values() 2957 }; 2958 (return_count, frame.br_destination()) 2959 }; 2960 let inputs = state.peekn_mut(return_count); 2961 (br_destination, inputs) 2962} 2963 2964/// Determine the returned value type of a WebAssembly operator 2965fn type_of(operator: &Operator) -> Type { 2966 match operator { 2967 Operator::V128Load { .. } 2968 | Operator::V128Store { .. } 2969 | Operator::V128Const { .. } 2970 | Operator::V128Not 2971 | Operator::V128And 2972 | Operator::V128AndNot 2973 | Operator::V128Or 2974 | Operator::V128Xor 2975 | Operator::V128AnyTrue 2976 | Operator::V128Bitselect => I8X16, // default type representing V128 2977 2978 Operator::I8x16Shuffle { .. } 2979 | Operator::I8x16Splat 2980 | Operator::V128Load8Splat { .. } 2981 | Operator::V128Load8Lane { .. } 2982 | Operator::V128Store8Lane { .. } 2983 | Operator::I8x16ExtractLaneS { .. } 2984 | Operator::I8x16ExtractLaneU { .. } 2985 | Operator::I8x16ReplaceLane { .. } 2986 | Operator::I8x16Eq 2987 | Operator::I8x16Ne 2988 | Operator::I8x16LtS 2989 | Operator::I8x16LtU 2990 | Operator::I8x16GtS 2991 | Operator::I8x16GtU 2992 | Operator::I8x16LeS 2993 | Operator::I8x16LeU 2994 | Operator::I8x16GeS 2995 | Operator::I8x16GeU 2996 | Operator::I8x16Neg 2997 | Operator::I8x16Abs 2998 | Operator::I8x16AllTrue 2999 | Operator::I8x16Shl 3000 | Operator::I8x16ShrS 3001 | Operator::I8x16ShrU 3002 | Operator::I8x16Add 3003 | Operator::I8x16AddSatS 3004 | Operator::I8x16AddSatU 3005 | Operator::I8x16Sub 3006 | Operator::I8x16SubSatS 3007 | Operator::I8x16SubSatU 3008 | Operator::I8x16MinS 3009 | Operator::I8x16MinU 3010 | Operator::I8x16MaxS 3011 | Operator::I8x16MaxU 3012 | Operator::I8x16AvgrU 3013 | Operator::I8x16Bitmask 3014 | Operator::I8x16Popcnt 3015 | Operator::I8x16RelaxedLaneselect => I8X16, 3016 3017 Operator::I16x8Splat 3018 | Operator::V128Load16Splat { .. } 3019 | Operator::V128Load16Lane { .. } 3020 | Operator::V128Store16Lane { .. } 3021 | Operator::I16x8ExtractLaneS { .. } 3022 | Operator::I16x8ExtractLaneU { .. } 3023 | Operator::I16x8ReplaceLane { .. } 3024 | Operator::I16x8Eq 3025 | Operator::I16x8Ne 3026 | Operator::I16x8LtS 3027 | Operator::I16x8LtU 3028 | Operator::I16x8GtS 3029 | Operator::I16x8GtU 3030 | Operator::I16x8LeS 3031 | Operator::I16x8LeU 3032 | Operator::I16x8GeS 3033 | Operator::I16x8GeU 3034 | Operator::I16x8Neg 3035 | Operator::I16x8Abs 3036 | Operator::I16x8AllTrue 3037 | Operator::I16x8Shl 3038 | Operator::I16x8ShrS 3039 | Operator::I16x8ShrU 3040 | Operator::I16x8Add 3041 | Operator::I16x8AddSatS 3042 | Operator::I16x8AddSatU 3043 | Operator::I16x8Sub 3044 | Operator::I16x8SubSatS 3045 | Operator::I16x8SubSatU 3046 | Operator::I16x8MinS 3047 | Operator::I16x8MinU 3048 | Operator::I16x8MaxS 3049 | Operator::I16x8MaxU 3050 | Operator::I16x8AvgrU 3051 | Operator::I16x8Mul 3052 | Operator::I16x8Bitmask 3053 | Operator::I16x8RelaxedLaneselect => I16X8, 3054 3055 Operator::I32x4Splat 3056 | Operator::V128Load32Splat { .. } 3057 | Operator::V128Load32Lane { .. } 3058 | Operator::V128Store32Lane { .. } 3059 | Operator::I32x4ExtractLane { .. } 3060 | Operator::I32x4ReplaceLane { .. } 3061 | Operator::I32x4Eq 3062 | Operator::I32x4Ne 3063 | Operator::I32x4LtS 3064 | Operator::I32x4LtU 3065 | Operator::I32x4GtS 3066 | Operator::I32x4GtU 3067 | Operator::I32x4LeS 3068 | Operator::I32x4LeU 3069 | Operator::I32x4GeS 3070 | Operator::I32x4GeU 3071 | Operator::I32x4Neg 3072 | Operator::I32x4Abs 3073 | Operator::I32x4AllTrue 3074 | Operator::I32x4Shl 3075 | Operator::I32x4ShrS 3076 | Operator::I32x4ShrU 3077 | Operator::I32x4Add 3078 | Operator::I32x4Sub 3079 | Operator::I32x4Mul 3080 | Operator::I32x4MinS 3081 | Operator::I32x4MinU 3082 | Operator::I32x4MaxS 3083 | Operator::I32x4MaxU 3084 | Operator::I32x4Bitmask 3085 | Operator::I32x4TruncSatF32x4S 3086 | Operator::I32x4TruncSatF32x4U 3087 | Operator::I32x4RelaxedLaneselect 3088 | Operator::V128Load32Zero { .. } => I32X4, 3089 3090 Operator::I64x2Splat 3091 | Operator::V128Load64Splat { .. } 3092 | Operator::V128Load64Lane { .. } 3093 | Operator::V128Store64Lane { .. } 3094 | Operator::I64x2ExtractLane { .. } 3095 | Operator::I64x2ReplaceLane { .. } 3096 | Operator::I64x2Eq 3097 | Operator::I64x2Ne 3098 | Operator::I64x2LtS 3099 | Operator::I64x2GtS 3100 | Operator::I64x2LeS 3101 | Operator::I64x2GeS 3102 | Operator::I64x2Neg 3103 | Operator::I64x2Abs 3104 | Operator::I64x2AllTrue 3105 | Operator::I64x2Shl 3106 | Operator::I64x2ShrS 3107 | Operator::I64x2ShrU 3108 | Operator::I64x2Add 3109 | Operator::I64x2Sub 3110 | Operator::I64x2Mul 3111 | Operator::I64x2Bitmask 3112 | Operator::I64x2RelaxedLaneselect 3113 | Operator::V128Load64Zero { .. } => I64X2, 3114 3115 Operator::F32x4Splat 3116 | Operator::F32x4ExtractLane { .. } 3117 | Operator::F32x4ReplaceLane { .. } 3118 | Operator::F32x4Eq 3119 | Operator::F32x4Ne 3120 | Operator::F32x4Lt 3121 | Operator::F32x4Gt 3122 | Operator::F32x4Le 3123 | Operator::F32x4Ge 3124 | Operator::F32x4Abs 3125 | Operator::F32x4Neg 3126 | Operator::F32x4Sqrt 3127 | Operator::F32x4Add 3128 | Operator::F32x4Sub 3129 | Operator::F32x4Mul 3130 | Operator::F32x4Div 3131 | Operator::F32x4Min 3132 | Operator::F32x4Max 3133 | Operator::F32x4PMin 3134 | Operator::F32x4PMax 3135 | Operator::F32x4ConvertI32x4S 3136 | Operator::F32x4ConvertI32x4U 3137 | Operator::F32x4Ceil 3138 | Operator::F32x4Floor 3139 | Operator::F32x4Trunc 3140 | Operator::F32x4Nearest 3141 | Operator::F32x4RelaxedMax 3142 | Operator::F32x4RelaxedMin 3143 | Operator::F32x4RelaxedMadd 3144 | Operator::F32x4RelaxedNmadd => F32X4, 3145 3146 Operator::F64x2Splat 3147 | Operator::F64x2ExtractLane { .. } 3148 | Operator::F64x2ReplaceLane { .. } 3149 | Operator::F64x2Eq 3150 | Operator::F64x2Ne 3151 | Operator::F64x2Lt 3152 | Operator::F64x2Gt 3153 | Operator::F64x2Le 3154 | Operator::F64x2Ge 3155 | Operator::F64x2Abs 3156 | Operator::F64x2Neg 3157 | Operator::F64x2Sqrt 3158 | Operator::F64x2Add 3159 | Operator::F64x2Sub 3160 | Operator::F64x2Mul 3161 | Operator::F64x2Div 3162 | Operator::F64x2Min 3163 | Operator::F64x2Max 3164 | Operator::F64x2PMin 3165 | Operator::F64x2PMax 3166 | Operator::F64x2Ceil 3167 | Operator::F64x2Floor 3168 | Operator::F64x2Trunc 3169 | Operator::F64x2Nearest 3170 | Operator::F64x2RelaxedMax 3171 | Operator::F64x2RelaxedMin 3172 | Operator::F64x2RelaxedMadd 3173 | Operator::F64x2RelaxedNmadd => F64X2, 3174 3175 _ => unimplemented!( 3176 "Currently only SIMD instructions are mapped to their return type; the \ 3177 following instruction is not mapped: {:?}", 3178 operator 3179 ), 3180 } 3181} 3182 3183/// Some SIMD operations only operate on I8X16 in CLIF; this will convert them to that type by 3184/// adding a bitcast if necessary. 3185fn optionally_bitcast_vector( 3186 value: Value, 3187 needed_type: Type, 3188 builder: &mut FunctionBuilder, 3189) -> Value { 3190 if builder.func.dfg.value_type(value) != needed_type { 3191 let mut flags = MemFlags::new(); 3192 flags.set_endianness(ir::Endianness::Little); 3193 builder.ins().bitcast(needed_type, flags, value) 3194 } else { 3195 value 3196 } 3197} 3198 3199#[inline(always)] 3200fn is_non_canonical_v128(ty: Type) -> bool { 3201 matches!(ty, I64X2 | I32X4 | I16X8 | F32X4 | F64X2) 3202} 3203 3204/// Cast to I8X16, any vector values in `values` that are of "non-canonical" type (meaning, not 3205/// I8X16), and return them in a slice. A pre-scan is made to determine whether any casts are 3206/// actually necessary, and if not, the original slice is returned. Otherwise the cast values 3207/// are returned in a slice that belongs to the caller-supplied `SmallVec`. 3208fn canonicalise_v128_values<'a>( 3209 tmp_canonicalised: &'a mut SmallVec<[Value; 16]>, 3210 builder: &mut FunctionBuilder, 3211 values: &'a [Value], 3212) -> &'a [Value] { 3213 debug_assert!(tmp_canonicalised.is_empty()); 3214 // First figure out if any of the parameters need to be cast. Mostly they don't need to be. 3215 let any_non_canonical = values 3216 .iter() 3217 .any(|v| is_non_canonical_v128(builder.func.dfg.value_type(*v))); 3218 // Hopefully we take this exit most of the time, hence doing no mem allocation. 3219 if !any_non_canonical { 3220 return values; 3221 } 3222 // Otherwise we'll have to cast, and push the resulting `Value`s into `canonicalised`. 3223 for v in values { 3224 tmp_canonicalised.push(if is_non_canonical_v128(builder.func.dfg.value_type(*v)) { 3225 let mut flags = MemFlags::new(); 3226 flags.set_endianness(ir::Endianness::Little); 3227 builder.ins().bitcast(I8X16, flags, *v) 3228 } else { 3229 *v 3230 }); 3231 } 3232 tmp_canonicalised.as_slice() 3233} 3234 3235/// Generate a `jump` instruction, but first cast all 128-bit vector values to I8X16 if they 3236/// don't have that type. This is done in somewhat roundabout way so as to ensure that we 3237/// almost never have to do any mem allocation. 3238fn canonicalise_then_jump( 3239 builder: &mut FunctionBuilder, 3240 destination: ir::Block, 3241 params: &[Value], 3242) -> ir::Inst { 3243 let mut tmp_canonicalised = SmallVec::<[Value; 16]>::new(); 3244 let canonicalised = canonicalise_v128_values(&mut tmp_canonicalised, builder, params); 3245 builder.ins().jump(destination, canonicalised) 3246} 3247 3248/// The same but for a `brif` instruction. 3249fn canonicalise_brif( 3250 builder: &mut FunctionBuilder, 3251 cond: Value, 3252 block_then: ir::Block, 3253 params_then: &[Value], 3254 block_else: ir::Block, 3255 params_else: &[Value], 3256) -> ir::Inst { 3257 let mut tmp_canonicalised_then = SmallVec::<[Value; 16]>::new(); 3258 let canonicalised_then = 3259 canonicalise_v128_values(&mut tmp_canonicalised_then, builder, params_then); 3260 let mut tmp_canonicalised_else = SmallVec::<[Value; 16]>::new(); 3261 let canonicalised_else = 3262 canonicalise_v128_values(&mut tmp_canonicalised_else, builder, params_else); 3263 builder.ins().brif( 3264 cond, 3265 block_then, 3266 canonicalised_then, 3267 block_else, 3268 canonicalised_else, 3269 ) 3270} 3271 3272/// A helper for popping and bitcasting a single value; since SIMD values can lose their type by 3273/// using v128 (i.e. CLIF's I8x16) we must re-type the values using a bitcast to avoid CLIF 3274/// typing issues. 3275fn pop1_with_bitcast( 3276 state: &mut FuncTranslationState, 3277 needed_type: Type, 3278 builder: &mut FunctionBuilder, 3279) -> Value { 3280 optionally_bitcast_vector(state.pop1(), needed_type, builder) 3281} 3282 3283/// A helper for popping and bitcasting two values; since SIMD values can lose their type by 3284/// using v128 (i.e. CLIF's I8x16) we must re-type the values using a bitcast to avoid CLIF 3285/// typing issues. 3286fn pop2_with_bitcast( 3287 state: &mut FuncTranslationState, 3288 needed_type: Type, 3289 builder: &mut FunctionBuilder, 3290) -> (Value, Value) { 3291 let (a, b) = state.pop2(); 3292 let bitcast_a = optionally_bitcast_vector(a, needed_type, builder); 3293 let bitcast_b = optionally_bitcast_vector(b, needed_type, builder); 3294 (bitcast_a, bitcast_b) 3295} 3296 3297fn pop3_with_bitcast( 3298 state: &mut FuncTranslationState, 3299 needed_type: Type, 3300 builder: &mut FunctionBuilder, 3301) -> (Value, Value, Value) { 3302 let (a, b, c) = state.pop3(); 3303 let bitcast_a = optionally_bitcast_vector(a, needed_type, builder); 3304 let bitcast_b = optionally_bitcast_vector(b, needed_type, builder); 3305 let bitcast_c = optionally_bitcast_vector(c, needed_type, builder); 3306 (bitcast_a, bitcast_b, bitcast_c) 3307} 3308 3309fn bitcast_arguments<'a>( 3310 builder: &FunctionBuilder, 3311 arguments: &'a mut [Value], 3312 params: &[ir::AbiParam], 3313 param_predicate: impl Fn(usize) -> bool, 3314) -> Vec<(Type, &'a mut Value)> { 3315 let filtered_param_types = params 3316 .iter() 3317 .enumerate() 3318 .filter(|(i, _)| param_predicate(*i)) 3319 .map(|(_, param)| param.value_type); 3320 3321 let pairs = filtered_param_types.zip_eq(arguments); 3322 3323 // The arguments which need to be bitcasted are those which have some vector type but the type 3324 // expected by the parameter is not the same vector type as that of the provided argument. 3325 pairs 3326 .filter(|(param_type, _)| param_type.is_vector()) 3327 .filter(|(param_type, arg)| { 3328 let arg_type = builder.func.dfg.value_type(**arg); 3329 assert!( 3330 arg_type.is_vector(), 3331 "unexpected type mismatch: expected {}, argument {} was actually of type {}", 3332 param_type, 3333 *arg, 3334 arg_type 3335 ); 3336 3337 // This is the same check that would be done by `optionally_bitcast_vector`, except we 3338 // can't take a mutable borrow of the FunctionBuilder here, so we defer inserting the 3339 // bitcast instruction to the caller. 3340 arg_type != *param_type 3341 }) 3342 .collect() 3343} 3344 3345/// A helper for bitcasting a sequence of return values for the function currently being built. If 3346/// a value is a vector type that does not match its expected type, this will modify the value in 3347/// place to point to the result of a `bitcast`. This conversion is necessary to parse Wasm 3348/// code that uses `V128` as function parameters (or implicitly in block parameters) and still use 3349/// specific CLIF types (e.g. `I32X4`) in the function body. 3350pub fn bitcast_wasm_returns( 3351 arguments: &mut [Value], 3352 builder: &mut FunctionBuilder, 3353 env: &mut TranslationEnvironment, 3354) { 3355 let changes = bitcast_arguments(builder, arguments, &builder.func.signature.returns, |i| { 3356 env.is_wasm_return(&builder.func.signature, i) 3357 }); 3358 for (t, arg) in changes { 3359 let mut flags = MemFlags::new(); 3360 flags.set_endianness(ir::Endianness::Little); 3361 *arg = builder.ins().bitcast(t, flags, *arg); 3362 } 3363} 3364 3365/// Like `bitcast_wasm_returns`, but for the parameters being passed to a specified callee. 3366fn bitcast_wasm_params( 3367 callee_signature: ir::SigRef, 3368 arguments: &mut [Value], 3369 builder: &mut FunctionBuilder, 3370 env: &mut TranslationEnvironment, 3371) { 3372 let callee_signature = &builder.func.dfg.signatures[callee_signature]; 3373 let changes = bitcast_arguments(builder, arguments, &callee_signature.params, |i| { 3374 env.is_wasm_parameter(i) 3375 }); 3376 for (t, arg) in changes { 3377 let mut flags = MemFlags::new(); 3378 flags.set_endianness(ir::Endianness::Little); 3379 *arg = builder.ins().bitcast(t, flags, *arg); 3380 } 3381}