1 //! This module is the central place for machine code emission. 2 //! It defines an implementation of wasmparser's Visitor trait 3 //! for `CodeGen`; which defines a visitor per op-code, 4 //! which validates and dispatches to the corresponding 5 //! machine code emitter. 6 7 use crate::abi::ABI; 8 use crate::codegen::{control_index, Callee, CodeGen, ControlStackFrame, FnCall}; 9 use crate::masm::{ 10 DivKind, FloatCmpKind, IntCmpKind, MacroAssembler, OperandSize, RegImm, RemKind, RoundingMode, 11 ShiftKind, 12 }; 13 use crate::stack::{TypedReg, Val}; 14 use cranelift_codegen::ir::TrapCode; 15 use smallvec::SmallVec; 16 use wasmparser::BrTable; 17 use wasmparser::{BlockType, Ieee32, Ieee64, VisitOperator}; 18 use wasmtime_environ::{ 19 FuncIndex, GlobalIndex, TableIndex, TableStyle, TypeIndex, WasmHeapType, WasmType, 20 FUNCREF_INIT_BIT, 21 }; 22 23 /// A macro to define unsupported WebAssembly operators. 24 /// 25 /// This macro calls itself recursively; 26 /// 1. It no-ops when matching a supported operator. 27 /// 2. Defines the visitor function and panics when 28 /// matching an unsupported operator. 29 macro_rules! def_unsupported { 30 ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => { 31 $( 32 def_unsupported!( 33 emit 34 $op 35 36 fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output { 37 $($(let _ = $arg;)*)? 38 todo!(stringify!($op)) 39 } 40 ); 41 )* 42 }; 43 44 (emit I32Const $($rest:tt)*) => {}; 45 (emit I64Const $($rest:tt)*) => {}; 46 (emit F32Const $($rest:tt)*) => {}; 47 (emit F64Const $($rest:tt)*) => {}; 48 (emit F32Add $($rest:tt)*) => {}; 49 (emit F64Add $($rest:tt)*) => {}; 50 (emit F32Sub $($rest:tt)*) => {}; 51 (emit F64Sub $($rest:tt)*) => {}; 52 (emit F32Mul $($rest:tt)*) => {}; 53 (emit F64Mul $($rest:tt)*) => {}; 54 (emit F32Div $($rest:tt)*) => {}; 55 (emit F64Div $($rest:tt)*) => {}; 56 (emit F32Min $($rest:tt)*) => {}; 57 (emit F64Min $($rest:tt)*) => {}; 58 (emit F32Max $($rest:tt)*) => {}; 59 (emit F64Max $($rest:tt)*) => {}; 60 (emit F32Copysign $($rest:tt)*) => {}; 61 (emit F64Copysign $($rest:tt)*) => {}; 62 (emit F32Abs $($rest:tt)*) => {}; 63 (emit F64Abs $($rest:tt)*) => {}; 64 (emit F32Neg $($rest:tt)*) => {}; 65 (emit F64Neg $($rest:tt)*) => {}; 66 (emit F32Floor $($rest:tt)*) => {}; 67 (emit F64Floor $($rest:tt)*) => {}; 68 (emit F32Ceil $($rest:tt)*) => {}; 69 (emit F64Ceil $($rest:tt)*) => {}; 70 (emit F32Nearest $($rest:tt)*) => {}; 71 (emit F64Nearest $($rest:tt)*) => {}; 72 (emit F32Trunc $($rest:tt)*) => {}; 73 (emit F64Trunc $($rest:tt)*) => {}; 74 (emit F32Sqrt $($rest:tt)*) => {}; 75 (emit F64Sqrt $($rest:tt)*) => {}; 76 (emit F32Eq $($rest:tt)*) => {}; 77 (emit F64Eq $($rest:tt)*) => {}; 78 (emit F32Ne $($rest:tt)*) => {}; 79 (emit F64Ne $($rest:tt)*) => {}; 80 (emit F32Lt $($rest:tt)*) => {}; 81 (emit F64Lt $($rest:tt)*) => {}; 82 (emit F32Gt $($rest:tt)*) => {}; 83 (emit F64Gt $($rest:tt)*) => {}; 84 (emit F32Le $($rest:tt)*) => {}; 85 (emit F64Le $($rest:tt)*) => {}; 86 (emit F32Ge $($rest:tt)*) => {}; 87 (emit F64Ge $($rest:tt)*) => {}; 88 (emit I32Add $($rest:tt)*) => {}; 89 (emit I64Add $($rest:tt)*) => {}; 90 (emit I32Sub $($rest:tt)*) => {}; 91 (emit I32Mul $($rest:tt)*) => {}; 92 (emit I32DivS $($rest:tt)*) => {}; 93 (emit I32DivU $($rest:tt)*) => {}; 94 (emit I64DivS $($rest:tt)*) => {}; 95 (emit I64DivU $($rest:tt)*) => {}; 96 (emit I64RemU $($rest:tt)*) => {}; 97 (emit I64RemS $($rest:tt)*) => {}; 98 (emit I32RemU $($rest:tt)*) => {}; 99 (emit I32RemS $($rest:tt)*) => {}; 100 (emit I64Mul $($rest:tt)*) => {}; 101 (emit I64Sub $($rest:tt)*) => {}; 102 (emit I32Eq $($rest:tt)*) => {}; 103 (emit I64Eq $($rest:tt)*) => {}; 104 (emit I32Ne $($rest:tt)*) => {}; 105 (emit I64Ne $($rest:tt)*) => {}; 106 (emit I32LtS $($rest:tt)*) => {}; 107 (emit I64LtS $($rest:tt)*) => {}; 108 (emit I32LtU $($rest:tt)*) => {}; 109 (emit I64LtU $($rest:tt)*) => {}; 110 (emit I32LeS $($rest:tt)*) => {}; 111 (emit I64LeS $($rest:tt)*) => {}; 112 (emit I32LeU $($rest:tt)*) => {}; 113 (emit I64LeU $($rest:tt)*) => {}; 114 (emit I32GtS $($rest:tt)*) => {}; 115 (emit I64GtS $($rest:tt)*) => {}; 116 (emit I32GtU $($rest:tt)*) => {}; 117 (emit I64GtU $($rest:tt)*) => {}; 118 (emit I32GeS $($rest:tt)*) => {}; 119 (emit I64GeS $($rest:tt)*) => {}; 120 (emit I32GeU $($rest:tt)*) => {}; 121 (emit I64GeU $($rest:tt)*) => {}; 122 (emit I32Eqz $($rest:tt)*) => {}; 123 (emit I64Eqz $($rest:tt)*) => {}; 124 (emit I32And $($rest:tt)*) => {}; 125 (emit I64And $($rest:tt)*) => {}; 126 (emit I32Or $($rest:tt)*) => {}; 127 (emit I64Or $($rest:tt)*) => {}; 128 (emit I32Xor $($rest:tt)*) => {}; 129 (emit I64Xor $($rest:tt)*) => {}; 130 (emit I32Shl $($rest:tt)*) => {}; 131 (emit I64Shl $($rest:tt)*) => {}; 132 (emit I32ShrS $($rest:tt)*) => {}; 133 (emit I64ShrS $($rest:tt)*) => {}; 134 (emit I32ShrU $($rest:tt)*) => {}; 135 (emit I64ShrU $($rest:tt)*) => {}; 136 (emit I32Rotl $($rest:tt)*) => {}; 137 (emit I64Rotl $($rest:tt)*) => {}; 138 (emit I32Rotr $($rest:tt)*) => {}; 139 (emit I64Rotr $($rest:tt)*) => {}; 140 (emit I32Clz $($rest:tt)*) => {}; 141 (emit I64Clz $($rest:tt)*) => {}; 142 (emit I32Ctz $($rest:tt)*) => {}; 143 (emit I64Ctz $($rest:tt)*) => {}; 144 (emit I32Popcnt $($rest:tt)*) => {}; 145 (emit I64Popcnt $($rest:tt)*) => {}; 146 (emit LocalGet $($rest:tt)*) => {}; 147 (emit LocalSet $($rest:tt)*) => {}; 148 (emit Call $($rest:tt)*) => {}; 149 (emit End $($rest:tt)*) => {}; 150 (emit Nop $($rest:tt)*) => {}; 151 (emit If $($rest:tt)*) => {}; 152 (emit Else $($rest:tt)*) => {}; 153 (emit Block $($rest:tt)*) => {}; 154 (emit Loop $($rest:tt)*) => {}; 155 (emit Br $($rest:tt)*) => {}; 156 (emit BrIf $($rest:tt)*) => {}; 157 (emit Return $($rest:tt)*) => {}; 158 (emit Unreachable $($rest:tt)*) => {}; 159 (emit LocalTee $($rest:tt)*) => {}; 160 (emit GlobalGet $($rest:tt)*) => {}; 161 (emit GlobalSet $($rest:tt)*) => {}; 162 (emit Select $($rest:tt)*) => {}; 163 (emit Drop $($rest:tt)*) => {}; 164 (emit BrTable $($rest:tt)*) => {}; 165 (emit CallIndirect $($rest:tt)*) => {}; 166 (emit TableInit $($rest:tt)*) => {}; 167 (emit TableCopy $($rest:tt)*) => {}; 168 (emit TableGet $($rest:tt)*) => {}; 169 (emit TableSet $($rest:tt)*) => {}; 170 (emit TableGrow $($rest:tt)*) => {}; 171 (emit TableSize $($rest:tt)*) => {}; 172 (emit TableFill $($rest:tt)*) => {}; 173 (emit ElemDrop $($rest:tt)*) => {}; 174 175 (emit $unsupported:tt $($rest:tt)*) => {$($rest)*}; 176 } 177 178 impl<'a, 'translation, 'data, M> VisitOperator<'a> for CodeGen<'a, 'translation, 'data, M> 179 where 180 M: MacroAssembler, 181 { 182 type Output = (); 183 184 fn visit_i32_const(&mut self, val: i32) { 185 self.context.stack.push(Val::i32(val)); 186 } 187 188 fn visit_i64_const(&mut self, val: i64) { 189 self.context.stack.push(Val::i64(val)); 190 } 191 192 fn visit_f32_const(&mut self, val: Ieee32) { 193 self.context.stack.push(Val::f32(val)); 194 } 195 196 fn visit_f64_const(&mut self, val: Ieee64) { 197 self.context.stack.push(Val::f64(val)); 198 } 199 200 fn visit_f32_add(&mut self) { 201 self.context.binop( 202 self.masm, 203 OperandSize::S32, 204 &mut |masm: &mut M, dst, src, size| { 205 masm.float_add(dst, dst, src, size); 206 }, 207 ); 208 } 209 210 fn visit_f64_add(&mut self) { 211 self.context.binop( 212 self.masm, 213 OperandSize::S64, 214 &mut |masm: &mut M, dst, src, size| { 215 masm.float_add(dst, dst, src, size); 216 }, 217 ); 218 } 219 220 fn visit_f32_sub(&mut self) { 221 self.context.binop( 222 self.masm, 223 OperandSize::S32, 224 &mut |masm: &mut M, dst, src, size| { 225 masm.float_sub(dst, dst, src, size); 226 }, 227 ); 228 } 229 230 fn visit_f64_sub(&mut self) { 231 self.context.binop( 232 self.masm, 233 OperandSize::S64, 234 &mut |masm: &mut M, dst, src, size| { 235 masm.float_sub(dst, dst, src, size); 236 }, 237 ); 238 } 239 240 fn visit_f32_mul(&mut self) { 241 self.context.binop( 242 self.masm, 243 OperandSize::S32, 244 &mut |masm: &mut M, dst, src, size| { 245 masm.float_mul(dst, dst, src, size); 246 }, 247 ); 248 } 249 250 fn visit_f64_mul(&mut self) { 251 self.context.binop( 252 self.masm, 253 OperandSize::S64, 254 &mut |masm: &mut M, dst, src, size| { 255 masm.float_mul(dst, dst, src, size); 256 }, 257 ); 258 } 259 260 fn visit_f32_div(&mut self) { 261 self.context.binop( 262 self.masm, 263 OperandSize::S32, 264 &mut |masm: &mut M, dst, src, size| { 265 masm.float_div(dst, dst, src, size); 266 }, 267 ); 268 } 269 270 fn visit_f64_div(&mut self) { 271 self.context.binop( 272 self.masm, 273 OperandSize::S64, 274 &mut |masm: &mut M, dst, src, size| { 275 masm.float_div(dst, dst, src, size); 276 }, 277 ); 278 } 279 280 fn visit_f32_min(&mut self) { 281 self.context.binop( 282 self.masm, 283 OperandSize::S32, 284 &mut |masm: &mut M, dst, src, size| { 285 masm.float_min(dst, dst, src, size); 286 }, 287 ); 288 } 289 290 fn visit_f64_min(&mut self) { 291 self.context.binop( 292 self.masm, 293 OperandSize::S64, 294 &mut |masm: &mut M, dst, src, size| { 295 masm.float_min(dst, dst, src, size); 296 }, 297 ); 298 } 299 300 fn visit_f32_max(&mut self) { 301 self.context.binop( 302 self.masm, 303 OperandSize::S32, 304 &mut |masm: &mut M, dst, src, size| { 305 masm.float_max(dst, dst, src, size); 306 }, 307 ); 308 } 309 310 fn visit_f64_max(&mut self) { 311 self.context.binop( 312 self.masm, 313 OperandSize::S64, 314 &mut |masm: &mut M, dst, src, size| { 315 masm.float_max(dst, dst, src, size); 316 }, 317 ); 318 } 319 320 fn visit_f32_copysign(&mut self) { 321 self.context.binop( 322 self.masm, 323 OperandSize::S32, 324 &mut |masm: &mut M, dst, src, size| { 325 masm.float_copysign(dst, dst, src, size); 326 }, 327 ); 328 } 329 330 fn visit_f64_copysign(&mut self) { 331 self.context.binop( 332 self.masm, 333 OperandSize::S64, 334 &mut |masm: &mut M, dst, src, size| { 335 masm.float_copysign(dst, dst, src, size); 336 }, 337 ); 338 } 339 340 fn visit_f32_abs(&mut self) { 341 self.context 342 .unop(self.masm, OperandSize::S32, &mut |masm, reg, size| { 343 masm.float_abs(reg, size); 344 }); 345 } 346 347 fn visit_f64_abs(&mut self) { 348 self.context 349 .unop(self.masm, OperandSize::S64, &mut |masm, reg, size| { 350 masm.float_abs(reg, size); 351 }); 352 } 353 354 fn visit_f32_neg(&mut self) { 355 self.context 356 .unop(self.masm, OperandSize::S32, &mut |masm, reg, size| { 357 masm.float_neg(reg, size); 358 }); 359 } 360 361 fn visit_f64_neg(&mut self) { 362 self.context 363 .unop(self.masm, OperandSize::S64, &mut |masm, reg, size| { 364 masm.float_neg(reg, size); 365 }); 366 } 367 368 fn visit_f32_floor(&mut self) { 369 self.masm 370 .float_round(RoundingMode::Down, &mut self.context, OperandSize::S32); 371 } 372 373 fn visit_f64_floor(&mut self) { 374 self.masm 375 .float_round(RoundingMode::Down, &mut self.context, OperandSize::S64); 376 } 377 378 fn visit_f32_ceil(&mut self) { 379 self.masm 380 .float_round(RoundingMode::Up, &mut self.context, OperandSize::S32); 381 } 382 383 fn visit_f64_ceil(&mut self) { 384 self.masm 385 .float_round(RoundingMode::Up, &mut self.context, OperandSize::S64); 386 } 387 388 fn visit_f32_nearest(&mut self) { 389 self.masm 390 .float_round(RoundingMode::Nearest, &mut self.context, OperandSize::S32); 391 } 392 393 fn visit_f64_nearest(&mut self) { 394 self.masm 395 .float_round(RoundingMode::Nearest, &mut self.context, OperandSize::S64); 396 } 397 398 fn visit_f32_trunc(&mut self) { 399 self.masm 400 .float_round(RoundingMode::Zero, &mut self.context, OperandSize::S32); 401 } 402 403 fn visit_f64_trunc(&mut self) { 404 self.masm 405 .float_round(RoundingMode::Zero, &mut self.context, OperandSize::S64); 406 } 407 408 fn visit_f32_sqrt(&mut self) { 409 self.context 410 .unop(self.masm, OperandSize::S32, &mut |masm, reg, size| { 411 masm.float_sqrt(reg, reg, size); 412 }); 413 } 414 415 fn visit_f64_sqrt(&mut self) { 416 self.context 417 .unop(self.masm, OperandSize::S64, &mut |masm, reg, size| { 418 masm.float_sqrt(reg, reg, size); 419 }); 420 } 421 422 fn visit_f32_eq(&mut self) { 423 self.context.float_cmp_op( 424 self.masm, 425 OperandSize::S32, 426 &mut |masm: &mut M, dst, src1, src2, size| { 427 masm.float_cmp_with_set(src1, src2, dst, FloatCmpKind::Eq, size); 428 }, 429 ); 430 } 431 432 fn visit_f64_eq(&mut self) { 433 self.context.float_cmp_op( 434 self.masm, 435 OperandSize::S64, 436 &mut |masm: &mut M, dst, src1, src2, size| { 437 masm.float_cmp_with_set(src1, src2, dst, FloatCmpKind::Eq, size); 438 }, 439 ); 440 } 441 442 fn visit_f32_ne(&mut self) { 443 self.context.float_cmp_op( 444 self.masm, 445 OperandSize::S32, 446 &mut |masm: &mut M, dst, src1, src2, size| { 447 masm.float_cmp_with_set(src1, src2, dst, FloatCmpKind::Ne, size); 448 }, 449 ); 450 } 451 452 fn visit_f64_ne(&mut self) { 453 self.context.float_cmp_op( 454 self.masm, 455 OperandSize::S64, 456 &mut |masm: &mut M, dst, src1, src2, size| { 457 masm.float_cmp_with_set(src1, src2, dst, FloatCmpKind::Ne, size); 458 }, 459 ); 460 } 461 462 fn visit_f32_lt(&mut self) { 463 self.context.float_cmp_op( 464 self.masm, 465 OperandSize::S32, 466 &mut |masm: &mut M, dst, src1, src2, size| { 467 masm.float_cmp_with_set(src1, src2, dst, FloatCmpKind::Lt, size); 468 }, 469 ); 470 } 471 472 fn visit_f64_lt(&mut self) { 473 self.context.float_cmp_op( 474 self.masm, 475 OperandSize::S64, 476 &mut |masm: &mut M, dst, src1, src2, size| { 477 masm.float_cmp_with_set(src1, src2, dst, FloatCmpKind::Lt, size); 478 }, 479 ); 480 } 481 482 fn visit_f32_gt(&mut self) { 483 self.context.float_cmp_op( 484 self.masm, 485 OperandSize::S32, 486 &mut |masm: &mut M, dst, src1, src2, size| { 487 masm.float_cmp_with_set(src1, src2, dst, FloatCmpKind::Gt, size); 488 }, 489 ); 490 } 491 492 fn visit_f64_gt(&mut self) { 493 self.context.float_cmp_op( 494 self.masm, 495 OperandSize::S64, 496 &mut |masm: &mut M, dst, src1, src2, size| { 497 masm.float_cmp_with_set(src1, src2, dst, FloatCmpKind::Gt, size); 498 }, 499 ); 500 } 501 502 fn visit_f32_le(&mut self) { 503 self.context.float_cmp_op( 504 self.masm, 505 OperandSize::S32, 506 &mut |masm: &mut M, dst, src1, src2, size| { 507 masm.float_cmp_with_set(src1, src2, dst, FloatCmpKind::Le, size); 508 }, 509 ); 510 } 511 512 fn visit_f64_le(&mut self) { 513 self.context.float_cmp_op( 514 self.masm, 515 OperandSize::S64, 516 &mut |masm: &mut M, dst, src1, src2, size| { 517 masm.float_cmp_with_set(src1, src2, dst, FloatCmpKind::Le, size); 518 }, 519 ); 520 } 521 522 fn visit_f32_ge(&mut self) { 523 self.context.float_cmp_op( 524 self.masm, 525 OperandSize::S32, 526 &mut |masm: &mut M, dst, src1, src2, size| { 527 masm.float_cmp_with_set(src1, src2, dst, FloatCmpKind::Ge, size); 528 }, 529 ); 530 } 531 532 fn visit_f64_ge(&mut self) { 533 self.context.float_cmp_op( 534 self.masm, 535 OperandSize::S64, 536 &mut |masm: &mut M, dst, src1, src2, size| { 537 masm.float_cmp_with_set(src1, src2, dst, FloatCmpKind::Ge, size); 538 }, 539 ); 540 } 541 542 fn visit_i32_add(&mut self) { 543 self.context.i32_binop(self.masm, |masm, dst, src, size| { 544 masm.add(dst, dst, src, size); 545 }); 546 } 547 548 fn visit_i64_add(&mut self) { 549 self.context.i64_binop(self.masm, |masm, dst, src, size| { 550 masm.add(dst, dst, src, size); 551 }); 552 } 553 554 fn visit_i32_sub(&mut self) { 555 self.context.i32_binop(self.masm, |masm, dst, src, size| { 556 masm.sub(dst, dst, src, size); 557 }); 558 } 559 560 fn visit_i64_sub(&mut self) { 561 self.context.i64_binop(self.masm, |masm, dst, src, size| { 562 masm.sub(dst, dst, src, size); 563 }); 564 } 565 566 fn visit_i32_mul(&mut self) { 567 self.context.i32_binop(self.masm, |masm, dst, src, size| { 568 masm.mul(dst, dst, src, size); 569 }); 570 } 571 572 fn visit_i64_mul(&mut self) { 573 self.context.i64_binop(self.masm, |masm, dst, src, size| { 574 masm.mul(dst, dst, src, size); 575 }); 576 } 577 578 fn visit_i32_div_s(&mut self) { 579 use DivKind::*; 580 use OperandSize::*; 581 582 self.masm.div(&mut self.context, Signed, S32); 583 } 584 585 fn visit_i32_div_u(&mut self) { 586 use DivKind::*; 587 use OperandSize::*; 588 589 self.masm.div(&mut self.context, Unsigned, S32); 590 } 591 592 fn visit_i64_div_s(&mut self) { 593 use DivKind::*; 594 use OperandSize::*; 595 596 self.masm.div(&mut self.context, Signed, S64); 597 } 598 599 fn visit_i64_div_u(&mut self) { 600 use DivKind::*; 601 use OperandSize::*; 602 603 self.masm.div(&mut self.context, Unsigned, S64); 604 } 605 606 fn visit_i32_rem_s(&mut self) { 607 use OperandSize::*; 608 use RemKind::*; 609 610 self.masm.rem(&mut self.context, Signed, S32); 611 } 612 613 fn visit_i32_rem_u(&mut self) { 614 use OperandSize::*; 615 use RemKind::*; 616 617 self.masm.rem(&mut self.context, Unsigned, S32); 618 } 619 620 fn visit_i64_rem_s(&mut self) { 621 use OperandSize::*; 622 use RemKind::*; 623 624 self.masm.rem(&mut self.context, Signed, S64); 625 } 626 627 fn visit_i64_rem_u(&mut self) { 628 use OperandSize::*; 629 use RemKind::*; 630 631 self.masm.rem(&mut self.context, Unsigned, S64); 632 } 633 634 fn visit_i32_eq(&mut self) { 635 self.cmp_i32s(IntCmpKind::Eq); 636 } 637 638 fn visit_i64_eq(&mut self) { 639 self.cmp_i64s(IntCmpKind::Eq); 640 } 641 642 fn visit_i32_ne(&mut self) { 643 self.cmp_i32s(IntCmpKind::Ne); 644 } 645 646 fn visit_i64_ne(&mut self) { 647 self.cmp_i64s(IntCmpKind::Ne); 648 } 649 650 fn visit_i32_lt_s(&mut self) { 651 self.cmp_i32s(IntCmpKind::LtS); 652 } 653 654 fn visit_i64_lt_s(&mut self) { 655 self.cmp_i64s(IntCmpKind::LtS); 656 } 657 658 fn visit_i32_lt_u(&mut self) { 659 self.cmp_i32s(IntCmpKind::LtU); 660 } 661 662 fn visit_i64_lt_u(&mut self) { 663 self.cmp_i64s(IntCmpKind::LtU); 664 } 665 666 fn visit_i32_le_s(&mut self) { 667 self.cmp_i32s(IntCmpKind::LeS); 668 } 669 670 fn visit_i64_le_s(&mut self) { 671 self.cmp_i64s(IntCmpKind::LeS); 672 } 673 674 fn visit_i32_le_u(&mut self) { 675 self.cmp_i32s(IntCmpKind::LeU); 676 } 677 678 fn visit_i64_le_u(&mut self) { 679 self.cmp_i64s(IntCmpKind::LeU); 680 } 681 682 fn visit_i32_gt_s(&mut self) { 683 self.cmp_i32s(IntCmpKind::GtS); 684 } 685 686 fn visit_i64_gt_s(&mut self) { 687 self.cmp_i64s(IntCmpKind::GtS); 688 } 689 690 fn visit_i32_gt_u(&mut self) { 691 self.cmp_i32s(IntCmpKind::GtU); 692 } 693 694 fn visit_i64_gt_u(&mut self) { 695 self.cmp_i64s(IntCmpKind::GtU); 696 } 697 698 fn visit_i32_ge_s(&mut self) { 699 self.cmp_i32s(IntCmpKind::GeS); 700 } 701 702 fn visit_i64_ge_s(&mut self) { 703 self.cmp_i64s(IntCmpKind::GeS); 704 } 705 706 fn visit_i32_ge_u(&mut self) { 707 self.cmp_i32s(IntCmpKind::GeU); 708 } 709 710 fn visit_i64_ge_u(&mut self) { 711 self.cmp_i64s(IntCmpKind::GeU); 712 } 713 714 fn visit_i32_eqz(&mut self) { 715 use OperandSize::*; 716 717 self.context.unop(self.masm, S32, &mut |masm, reg, size| { 718 masm.cmp_with_set(RegImm::i32(0), reg.into(), IntCmpKind::Eq, size); 719 }); 720 } 721 722 fn visit_i64_eqz(&mut self) { 723 use OperandSize::*; 724 725 self.context.unop(self.masm, S64, &mut |masm, reg, size| { 726 masm.cmp_with_set(RegImm::i64(0), reg.into(), IntCmpKind::Eq, size); 727 }); 728 } 729 730 fn visit_i32_clz(&mut self) { 731 use OperandSize::*; 732 733 self.context.unop(self.masm, S32, &mut |masm, reg, size| { 734 masm.clz(reg, reg, size); 735 }); 736 } 737 738 fn visit_i64_clz(&mut self) { 739 use OperandSize::*; 740 741 self.context.unop(self.masm, S64, &mut |masm, reg, size| { 742 masm.clz(reg, reg, size); 743 }); 744 } 745 746 fn visit_i32_ctz(&mut self) { 747 use OperandSize::*; 748 749 self.context.unop(self.masm, S32, &mut |masm, reg, size| { 750 masm.ctz(reg, reg, size); 751 }); 752 } 753 754 fn visit_i64_ctz(&mut self) { 755 use OperandSize::*; 756 757 self.context.unop(self.masm, S64, &mut |masm, reg, size| { 758 masm.ctz(reg, reg, size); 759 }); 760 } 761 762 fn visit_i32_and(&mut self) { 763 self.context.i32_binop(self.masm, |masm, dst, src, size| { 764 masm.and(dst, dst, src, size); 765 }); 766 } 767 768 fn visit_i64_and(&mut self) { 769 self.context.i64_binop(self.masm, |masm, dst, src, size| { 770 masm.and(dst, dst, src, size); 771 }); 772 } 773 774 fn visit_i32_or(&mut self) { 775 self.context.i32_binop(self.masm, |masm, dst, src, size| { 776 masm.or(dst, dst, src, size); 777 }); 778 } 779 780 fn visit_i64_or(&mut self) { 781 self.context.i64_binop(self.masm, |masm, dst, src, size| { 782 masm.or(dst, dst, src, size); 783 }); 784 } 785 786 fn visit_i32_xor(&mut self) { 787 self.context.i32_binop(self.masm, |masm, dst, src, size| { 788 masm.xor(dst, dst, src, size); 789 }); 790 } 791 792 fn visit_i64_xor(&mut self) { 793 self.context.i64_binop(self.masm, |masm, dst, src, size| { 794 masm.xor(dst, dst, src, size); 795 }); 796 } 797 798 fn visit_i32_shl(&mut self) { 799 use OperandSize::*; 800 use ShiftKind::*; 801 802 self.masm.shift(&mut self.context, Shl, S32); 803 } 804 805 fn visit_i64_shl(&mut self) { 806 use OperandSize::*; 807 use ShiftKind::*; 808 809 self.masm.shift(&mut self.context, Shl, S64); 810 } 811 812 fn visit_i32_shr_s(&mut self) { 813 use OperandSize::*; 814 use ShiftKind::*; 815 816 self.masm.shift(&mut self.context, ShrS, S32); 817 } 818 819 fn visit_i64_shr_s(&mut self) { 820 use OperandSize::*; 821 use ShiftKind::*; 822 823 self.masm.shift(&mut self.context, ShrS, S64); 824 } 825 826 fn visit_i32_shr_u(&mut self) { 827 use OperandSize::*; 828 use ShiftKind::*; 829 830 self.masm.shift(&mut self.context, ShrU, S32); 831 } 832 833 fn visit_i64_shr_u(&mut self) { 834 use OperandSize::*; 835 use ShiftKind::*; 836 837 self.masm.shift(&mut self.context, ShrU, S64); 838 } 839 840 fn visit_i32_rotl(&mut self) { 841 use OperandSize::*; 842 use ShiftKind::*; 843 844 self.masm.shift(&mut self.context, Rotl, S32); 845 } 846 847 fn visit_i64_rotl(&mut self) { 848 use OperandSize::*; 849 use ShiftKind::*; 850 851 self.masm.shift(&mut self.context, Rotl, S64); 852 } 853 854 fn visit_i32_rotr(&mut self) { 855 use OperandSize::*; 856 use ShiftKind::*; 857 858 self.masm.shift(&mut self.context, Rotr, S32); 859 } 860 861 fn visit_i64_rotr(&mut self) { 862 use OperandSize::*; 863 use ShiftKind::*; 864 865 self.masm.shift(&mut self.context, Rotr, S64); 866 } 867 868 fn visit_end(&mut self) { 869 if !self.context.reachable { 870 self.handle_unreachable_end(); 871 } else { 872 let mut control = self.control_frames.pop().unwrap(); 873 let is_outermost = self.control_frames.len() == 0; 874 // If it's not the outermost control stack frame, emit the the full "end" sequence, 875 // which involves, popping results from the value stack, pushing results back to the 876 // value stack and binding the exit label. 877 // Else, pop values from the value stack and bind the exit label. 878 if !is_outermost { 879 control.emit_end(self.masm, &mut self.context); 880 } else { 881 if let Some(data) = control.results() { 882 self.context.pop_abi_results(data, self.masm); 883 } 884 control.bind_exit_label(self.masm); 885 } 886 } 887 } 888 889 fn visit_i32_popcnt(&mut self) { 890 use OperandSize::*; 891 self.masm.popcnt(&mut self.context, S32); 892 } 893 894 fn visit_i64_popcnt(&mut self) { 895 use OperandSize::*; 896 897 self.masm.popcnt(&mut self.context, S64); 898 } 899 900 fn visit_local_get(&mut self, index: u32) { 901 use WasmType::*; 902 let context = &mut self.context; 903 let slot = context 904 .frame 905 .get_local(index) 906 .unwrap_or_else(|| panic!("valid local at slot = {}", index)); 907 match slot.ty { 908 I32 | I64 | F32 | F64 => context.stack.push(Val::local(index, slot.ty)), 909 Ref(rt) => match rt.heap_type { 910 WasmHeapType::Func => context.stack.push(Val::local(index, slot.ty)), 911 ht => unimplemented!("Support for WasmHeapType: {ht}"), 912 }, 913 t => unimplemented!("Support local type: {t}"), 914 } 915 } 916 917 fn visit_local_set(&mut self, index: u32) { 918 let src = self.emit_set_local(index); 919 self.context.free_reg(src); 920 } 921 922 fn visit_call(&mut self, index: u32) { 923 let callee = self.env.callee_from_index(FuncIndex::from_u32(index)); 924 FnCall::emit::<M, M::Ptr, _>(self.masm, &mut self.context, |_| callee.clone()); 925 } 926 927 fn visit_call_indirect(&mut self, type_index: u32, table_index: u32, _: u8) { 928 // Spill now because `emit_lazy_init_funcref` and the `FnCall::emit` 929 // invocations will both trigger spills since they both call functions. 930 // However, the machine instructions for the spill emitted by 931 // `emit_lazy_funcref` will be jumped over if the funcref was previously 932 // initialized which may result in the machine stack becoming 933 // unbalanced. 934 self.context.spill(self.masm); 935 936 let type_index = TypeIndex::from_u32(type_index); 937 let table_index = TableIndex::from_u32(table_index); 938 939 self.emit_lazy_init_funcref(table_index); 940 941 // Perform the indirect call. 942 // This code assumes that [`Self::emit_lazy_init_funcref`] will 943 // push the funcref to the value stack. 944 match self.env.translation.module.table_plans[table_index].style { 945 TableStyle::CallerChecksSignature => { 946 let funcref_ptr = self.context.stack.peek().map(|v| v.unwrap_reg()).unwrap(); 947 self.masm 948 .trapz(funcref_ptr.into(), TrapCode::IndirectCallToNull); 949 self.emit_typecheck_funcref(funcref_ptr.into(), type_index); 950 } 951 } 952 953 FnCall::emit::<M, M::Ptr, _>(self.masm, &mut self.context, |_| { 954 self.env.funcref(type_index) 955 }) 956 } 957 958 fn visit_table_init(&mut self, elem: u32, table: u32) { 959 let ptr_type = self.env.ptr_type(); 960 let vmctx = TypedReg::new(ptr_type, <M::ABI as ABI>::vmctx_reg()); 961 962 debug_assert!(self.context.stack.len() >= 3); 963 let at = self.context.stack.len() - 3; 964 965 self.context.stack.insert_many( 966 at, 967 [ 968 vmctx.into(), 969 table.try_into().unwrap(), 970 elem.try_into().unwrap(), 971 ], 972 ); 973 FnCall::emit::<M, M::Ptr, _>(self.masm, &mut self.context, |cx| { 974 Callee::Builtin(cx.builtins.table_init::<M::ABI, M::Ptr>()) 975 }); 976 } 977 978 fn visit_table_copy(&mut self, dst: u32, src: u32) { 979 let ptr_type = self.env.ptr_type(); 980 let vmctx = TypedReg::new(ptr_type, <M::ABI as ABI>::vmctx_reg()); 981 debug_assert!(self.context.stack.len() >= 3); 982 let at = self.context.stack.len() - 3; 983 self.context.stack.insert_many( 984 at, 985 [ 986 vmctx.into(), 987 dst.try_into().unwrap(), 988 src.try_into().unwrap(), 989 ], 990 ); 991 992 FnCall::emit::<M, M::Ptr, _>(self.masm, &mut self.context, |context| { 993 Callee::Builtin(context.builtins.table_copy::<M::ABI, M::Ptr>()) 994 }); 995 } 996 997 fn visit_table_get(&mut self, table: u32) { 998 let table_index = TableIndex::from_u32(table); 999 let plan = self.env.table_plan(table_index); 1000 let heap_type = plan.table.wasm_ty.heap_type; 1001 let style = &plan.style; 1002 1003 match heap_type { 1004 WasmHeapType::Func => match style { 1005 TableStyle::CallerChecksSignature => self.emit_lazy_init_funcref(table_index), 1006 }, 1007 t => unimplemented!("Support for WasmHeapType: {t}"), 1008 } 1009 } 1010 1011 fn visit_table_grow(&mut self, table: u32) { 1012 let ptr_type = self.env.ptr_type(); 1013 let vmctx = TypedReg::new(ptr_type, <M::ABI as ABI>::vmctx_reg()); 1014 let table_index = TableIndex::from_u32(table); 1015 let table_plan = self.env.table_plan(table_index); 1016 let builtin = match table_plan.table.wasm_ty.heap_type { 1017 WasmHeapType::Func => self 1018 .context 1019 .builtins 1020 .table_grow_func_ref::<M::ABI, M::Ptr>(), 1021 ty => unimplemented!("Support for HeapType: {ty}"), 1022 }; 1023 1024 let len = self.context.stack.len(); 1025 // table.grow` requires at least 2 elements on the value stack. 1026 debug_assert!(len >= 2); 1027 let at = len - 2; 1028 1029 // The table_grow builtin expects the parameters in a different 1030 // order. 1031 // The value stack at this point should contain: 1032 // [ init_value | delta ] (stack top) 1033 // but the builtin function expects the init value as the last 1034 // argument. 1035 self.context.stack.inner_mut().swap(len - 1, len - 2); 1036 self.context 1037 .stack 1038 .insert_many(at, [vmctx.into(), table.try_into().unwrap()]); 1039 1040 FnCall::emit::<M, M::Ptr, _>(self.masm, &mut self.context, |_| { 1041 Callee::Builtin(builtin.clone()) 1042 }); 1043 } 1044 1045 fn visit_table_size(&mut self, table: u32) { 1046 let table_index = TableIndex::from_u32(table); 1047 let table_data = self.env.resolve_table_data(table_index); 1048 self.masm.table_size(&table_data, &mut self.context); 1049 } 1050 1051 fn visit_table_fill(&mut self, table: u32) { 1052 let ptr_type = self.env.ptr_type(); 1053 let vmctx = TypedReg::new(ptr_type, <M::ABI as ABI>::vmctx_reg()); 1054 let table_index = TableIndex::from_u32(table); 1055 let table_plan = self.env.table_plan(table_index); 1056 let builtin = match table_plan.table.wasm_ty.heap_type { 1057 WasmHeapType::Func => self 1058 .context 1059 .builtins 1060 .table_fill_func_ref::<M::ABI, M::Ptr>(), 1061 ty => unimplemented!("Support for heap type: {ty}"), 1062 }; 1063 1064 let len = self.context.stack.len(); 1065 debug_assert!(len >= 3); 1066 let at = len - 3; 1067 self.context 1068 .stack 1069 .insert_many(at, [vmctx.into(), table.try_into().unwrap()]); 1070 FnCall::emit::<M, M::Ptr, _>(self.masm, &mut self.context, |_| { 1071 Callee::Builtin(builtin.clone()) 1072 }) 1073 } 1074 1075 fn visit_table_set(&mut self, table: u32) { 1076 let ptr_type = self.env.ptr_type(); 1077 let table_index = TableIndex::from_u32(table); 1078 let table_data = self.env.resolve_table_data(table_index); 1079 let plan = self.env.table_plan(table_index); 1080 match plan.table.wasm_ty.heap_type { 1081 WasmHeapType::Func => match plan.style { 1082 TableStyle::CallerChecksSignature => { 1083 let value = self.context.pop_to_reg(self.masm, None); 1084 let index = self.context.pop_to_reg(self.masm, None); 1085 let base = self.context.any_gpr(self.masm); 1086 let elem_addr = self.masm.table_elem_address( 1087 index.into(), 1088 base, 1089 &table_data, 1090 &mut self.context, 1091 ); 1092 1093 // Set the initialized bit. 1094 self.masm.or( 1095 value.into(), 1096 value.into(), 1097 RegImm::i64(FUNCREF_INIT_BIT as i64), 1098 ptr_type.into(), 1099 ); 1100 1101 self.masm.store_ptr(value.into(), elem_addr); 1102 1103 self.context.free_reg(value); 1104 self.context.free_reg(index); 1105 self.context.free_reg(base); 1106 } 1107 }, 1108 ty => unimplemented!("Support for WasmHeapType: {ty}"), 1109 }; 1110 } 1111 1112 fn visit_elem_drop(&mut self, index: u32) { 1113 let ptr_type = self.env.ptr_type(); 1114 let elem_drop = self.context.builtins.elem_drop::<M::ABI, M::Ptr>(); 1115 let vmctx = TypedReg::new(ptr_type, <M::ABI as ABI>::vmctx_reg()); 1116 self.context 1117 .stack 1118 .extend([vmctx.into(), index.try_into().unwrap()]); 1119 FnCall::emit::<M, M::Ptr, _>(self.masm, &mut self.context, |_| { 1120 Callee::Builtin(elem_drop.clone()) 1121 }); 1122 } 1123 1124 fn visit_nop(&mut self) {} 1125 1126 fn visit_if(&mut self, blockty: BlockType) { 1127 self.control_frames.push(ControlStackFrame::r#if( 1128 self.env.resolve_block_results_data::<M::ABI>(blockty), 1129 self.env.resolve_block_type_info(blockty), 1130 self.masm, 1131 &mut self.context, 1132 )); 1133 } 1134 1135 fn visit_else(&mut self) { 1136 if !self.context.reachable { 1137 self.handle_unreachable_else(); 1138 } else { 1139 let control = self 1140 .control_frames 1141 .last_mut() 1142 .unwrap_or_else(|| panic!("Expected active control stack frame for else")); 1143 control.emit_else(self.masm, &mut self.context); 1144 } 1145 } 1146 1147 fn visit_block(&mut self, blockty: BlockType) { 1148 self.control_frames.push(ControlStackFrame::block( 1149 self.env.resolve_block_results_data::<M::ABI>(blockty), 1150 self.env.resolve_block_type_info(blockty), 1151 self.masm, 1152 &mut self.context, 1153 )); 1154 } 1155 1156 fn visit_loop(&mut self, blockty: BlockType) { 1157 self.control_frames.push(ControlStackFrame::r#loop( 1158 self.env.resolve_block_type_info(blockty), 1159 self.masm, 1160 &mut self.context, 1161 )); 1162 } 1163 1164 fn visit_br(&mut self, depth: u32) { 1165 let index = control_index(depth, self.control_frames.len()); 1166 let frame = &mut self.control_frames[index]; 1167 self.context 1168 .unconditional_jump(frame, self.masm, |masm, cx, frame| { 1169 if let Some(r) = frame.as_target_results() { 1170 cx.pop_abi_results(r, masm); 1171 } 1172 }); 1173 } 1174 1175 fn visit_br_if(&mut self, depth: u32) { 1176 let index = control_index(depth, self.control_frames.len()); 1177 let frame = &mut self.control_frames[index]; 1178 frame.set_as_target(); 1179 1180 let top = if let Some(data) = frame.as_target_results() { 1181 let top = self.context.without::<TypedReg, M, _>( 1182 data.results.regs(), 1183 self.masm, 1184 |ctx, masm| ctx.pop_to_reg(masm, None), 1185 ); 1186 self.context.top_abi_results(data, self.masm); 1187 top 1188 } else { 1189 self.context.pop_to_reg(self.masm, None) 1190 }; 1191 1192 // Emit instructions to balance the machine stack if the frame has 1193 // a different offset. 1194 let current_sp_offset = self.masm.sp_offset(); 1195 let (_, frame_sp_offset) = frame.base_stack_len_and_sp(); 1196 let (label, cmp, needs_cleanup) = if current_sp_offset > frame_sp_offset { 1197 (self.masm.get_label(), IntCmpKind::Eq, true) 1198 } else { 1199 (*frame.label(), IntCmpKind::Ne, false) 1200 }; 1201 1202 self.masm 1203 .branch(cmp, top.reg.into(), top.reg.into(), label, OperandSize::S32); 1204 self.context.free_reg(top); 1205 1206 if needs_cleanup { 1207 // Emit instructions to balance the stack and jump if not falling 1208 // through. 1209 self.masm.ensure_sp_for_jump(frame_sp_offset); 1210 self.masm.jmp(*frame.label()); 1211 1212 // Restore sp_offset to what it was for falling through and emit 1213 // fallthrough label. 1214 self.masm.reset_stack_pointer(current_sp_offset); 1215 self.masm.bind(label); 1216 } 1217 } 1218 1219 fn visit_br_table(&mut self, targets: BrTable<'a>) { 1220 // +1 to account for the default target. 1221 let len = targets.len() + 1; 1222 // SmallVec<[_; 5]> to match the binary emission layer (e.g 1223 // see `JmpTableSeq'), but here we use 5 instead since we 1224 // bundle the default target as the last element in the array. 1225 let labels: SmallVec<[_; 5]> = (0..len).map(|_| self.masm.get_label()).collect(); 1226 1227 let default_index = control_index(targets.default(), self.control_frames.len()); 1228 let default_result = self.control_frames[default_index].as_target_results(); 1229 1230 let (index, tmp) = if let Some(data) = default_result { 1231 let index_and_tmp = self.context.without::<(TypedReg, _), M, _>( 1232 data.results.regs(), 1233 self.masm, 1234 |cx, masm| (cx.pop_to_reg(masm, None), cx.any_gpr(masm)), 1235 ); 1236 1237 // Materialize any constants or locals into their result representation, 1238 // so that when reachability is restored, they are correctly located. 1239 self.context.top_abi_results(data, self.masm); 1240 index_and_tmp 1241 } else { 1242 ( 1243 self.context.pop_to_reg(self.masm, None), 1244 self.context.any_gpr(self.masm), 1245 ) 1246 }; 1247 1248 self.masm.jmp_table(&labels, index.into(), tmp); 1249 // Save the original stack pointer offset; we will reset the stack 1250 // pointer to this offset after jumping to each of the targets. Each 1251 // jump might adjust the stack according to the base offset of the 1252 // target. 1253 let current_sp = self.masm.sp_offset(); 1254 1255 for (t, l) in targets 1256 .targets() 1257 .into_iter() 1258 .chain(std::iter::once(Ok(targets.default()))) 1259 .zip(labels.iter()) 1260 { 1261 let control_index = control_index(t.unwrap(), self.control_frames.len()); 1262 let frame = &mut self.control_frames[control_index]; 1263 // Reset the stack pointer to its original offset. This is needed 1264 // because each jump will potentially adjust the stack pointer 1265 // according to the base offset of the target. 1266 self.masm.reset_stack_pointer(current_sp); 1267 1268 // NB: We don't perform any result handling as it was 1269 // already taken care of above before jumping to the 1270 // jump table. 1271 self.masm.bind(*l); 1272 // Ensure that the stack pointer is correctly positioned before 1273 // jumping to the jump table code. 1274 let (_, offset) = frame.base_stack_len_and_sp(); 1275 self.masm.ensure_sp_for_jump(offset); 1276 self.masm.jmp(*frame.label()); 1277 frame.set_as_target(); 1278 } 1279 // Finally reset the stack pointer to the original location. 1280 // The reachability analysis, will ensure it's correctly located 1281 // once reachability is restored. 1282 self.masm.reset_stack_pointer(current_sp); 1283 self.context.reachable = false; 1284 self.context.free_reg(index.reg); 1285 self.context.free_reg(tmp); 1286 } 1287 1288 fn visit_return(&mut self) { 1289 // Grab the outermost frame, which is the function's body 1290 // frame. We don't rely on [`codegen::control_index`] since 1291 // this frame is implicit and we know that it should exist at 1292 // index 0. 1293 let outermost = &mut self.control_frames[0]; 1294 self.context 1295 .unconditional_jump(outermost, self.masm, |masm, cx, frame| { 1296 if let Some(data) = frame.as_target_results() { 1297 cx.pop_abi_results(data, masm); 1298 } 1299 }); 1300 } 1301 1302 fn visit_unreachable(&mut self) { 1303 self.masm.unreachable(); 1304 self.context.reachable = false; 1305 // Set the implicit outermost frame as target to perform the necessary 1306 // stack clean up. 1307 let outermost = &mut self.control_frames[0]; 1308 outermost.set_as_target(); 1309 } 1310 1311 fn visit_local_tee(&mut self, index: u32) { 1312 let typed_reg = self.emit_set_local(index); 1313 self.context.stack.push(typed_reg.into()); 1314 } 1315 1316 fn visit_global_get(&mut self, global_index: u32) { 1317 let index = GlobalIndex::from_u32(global_index); 1318 let (ty, offset) = self.env.resolve_global_type_and_offset(index); 1319 let addr = self 1320 .masm 1321 .address_at_reg(<M::ABI as ABI>::vmctx_reg(), offset); 1322 let dst = self.context.reg_for_type(ty, self.masm); 1323 self.masm.load(addr, dst, ty.into()); 1324 self.context.stack.push(Val::reg(dst, ty)); 1325 } 1326 1327 fn visit_global_set(&mut self, global_index: u32) { 1328 let index = GlobalIndex::from_u32(global_index); 1329 let (ty, offset) = self.env.resolve_global_type_and_offset(index); 1330 let addr = self 1331 .masm 1332 .address_at_reg(<M::ABI as ABI>::vmctx_reg(), offset); 1333 let typed_reg = self.context.pop_to_reg(self.masm, None); 1334 self.context.free_reg(typed_reg.reg); 1335 self.masm.store(typed_reg.reg.into(), addr, ty.into()); 1336 } 1337 1338 fn visit_drop(&mut self) { 1339 self.context.drop_last(1, |regalloc, val| match val { 1340 Val::Reg(tr) => regalloc.free(tr.reg.into()), 1341 Val::Memory(m) => self.masm.free_stack(m.slot.size), 1342 _ => {} 1343 }); 1344 } 1345 1346 fn visit_select(&mut self) { 1347 let cond = self.context.pop_to_reg(self.masm, None); 1348 let val2 = self.context.pop_to_reg(self.masm, None); 1349 let val1 = self.context.pop_to_reg(self.masm, None); 1350 self.masm 1351 .cmp(RegImm::i32(0), cond.reg.into(), OperandSize::S32); 1352 // Conditionally move val1 to val2 if the the comparision is 1353 // not zero. 1354 self.masm 1355 .cmov(val1.into(), val2.into(), IntCmpKind::Ne, val1.ty.into()); 1356 self.context.stack.push(val2.into()); 1357 self.context.free_reg(val1.reg); 1358 self.context.free_reg(cond); 1359 } 1360 1361 wasmparser::for_each_operator!(def_unsupported); 1362 } 1363 1364 impl<'a, 'translation, 'data, M> CodeGen<'a, 'translation, 'data, M> 1365 where 1366 M: MacroAssembler, 1367 { 1368 fn cmp_i32s(&mut self, kind: IntCmpKind) { 1369 self.context.i32_binop(self.masm, |masm, dst, src, size| { 1370 masm.cmp_with_set(src, dst, kind, size); 1371 }); 1372 } 1373 1374 fn cmp_i64s(&mut self, kind: IntCmpKind) { 1375 self.context 1376 .i64_binop(self.masm, move |masm, dst, src, size| { 1377 masm.cmp_with_set(src, dst, kind, size); 1378 }); 1379 } 1380 } 1381 1382 impl From<WasmType> for OperandSize { 1383 fn from(ty: WasmType) -> OperandSize { 1384 match ty { 1385 WasmType::I32 | WasmType::F32 => OperandSize::S32, 1386 WasmType::I64 | WasmType::F64 => OperandSize::S64, 1387 WasmType::Ref(rt) => { 1388 match rt.heap_type { 1389 // TODO: Harcoded size, assuming 64-bit support only. Once 1390 // Wasmtime supports 32-bit architectures, this will need 1391 // to be updated in such a way that the calculation of the 1392 // OperandSize will depend on the target's pointer size. 1393 WasmHeapType::Func => OperandSize::S64, 1394 t => unimplemented!("Support for WasmHeapType: {t}"), 1395 } 1396 } 1397 ty => unimplemented!("Support for WasmType {ty}"), 1398 } 1399 } 1400 } 1401