MIR-wasm.h (110769B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* 8 * Everything needed to build actual MIR instructions: the actual opcodes and 9 * instructions, the instruction interface, and use chains. 10 */ 11 12 #ifndef jit_MIR_wasm_h 13 #define jit_MIR_wasm_h 14 15 #include "mozilla/HashFunctions.h" 16 #include "mozilla/Vector.h" 17 #ifdef JS_JITSPEW 18 # include "mozilla/Sprintf.h" 19 #endif 20 21 #include <algorithm> 22 23 #include "jit/MIR.h" 24 #include "util/DifferentialTesting.h" 25 #include "wasm/WasmGcObject.h" 26 27 namespace js { 28 29 class WasmInstanceObject; 30 31 namespace wasm { 32 class FuncExport; 33 extern uint32_t MIRTypeToABIResultSize(jit::MIRType); 34 } // namespace wasm 35 36 namespace jit { 37 38 class MWasmNullConstant : public MNullaryInstruction { 39 mozilla::Maybe<wasm::RefTypeHierarchy> hierarchy_; 40 41 explicit MWasmNullConstant(wasm::MaybeRefType type) 42 : MNullaryInstruction(classOpcode), 43 hierarchy_(type.isSome() ? mozilla::Some(type.value().hierarchy()) 44 : mozilla::Nothing()) { 45 setResultType(MIRType::WasmAnyRef); 46 setMovable(); 47 if (type.isSome()) { 48 initWasmRefType(wasm::MaybeRefType(type.value().bottomType())); 49 } 50 } 51 52 public: 53 INSTRUCTION_HEADER(WasmNullConstant) 54 TRIVIAL_NEW_WRAPPERS 55 56 mozilla::Maybe<wasm::RefTypeHierarchy> hierarchy() const { 57 return hierarchy_; 58 } 59 60 HashNumber valueHash() const override; 61 bool congruentTo(const MDefinition* ins) const override { 62 return ins->isWasmNullConstant() && 63 hierarchy() == ins->toWasmNullConstant()->hierarchy(); 64 } 65 AliasSet getAliasSet() const override { return AliasSet::None(); } 66 67 ALLOW_CLONE(MWasmNullConstant) 68 }; 69 70 // Floating-point value as created by wasm. Just a constant value, used to 71 // effectively inhibit all the MIR optimizations. This uses the same LIR nodes 72 // as a MConstant of the same type would. 73 class MWasmFloatConstant : public MNullaryInstruction { 74 union { 75 float f32_; 76 double f64_; 77 #ifdef ENABLE_WASM_SIMD 78 int8_t s128_[16]; 79 uint64_t bits_[2]; 80 #else 81 uint64_t bits_[1]; 82 #endif 83 } u; 84 85 explicit MWasmFloatConstant(MIRType type) : MNullaryInstruction(classOpcode) { 86 u.bits_[0] = 0; 87 #ifdef ENABLE_WASM_SIMD 88 u.bits_[1] = 0; 89 #endif 90 setResultType(type); 91 } 92 93 public: 94 INSTRUCTION_HEADER(WasmFloatConstant) 95 96 static MWasmFloatConstant* NewDouble(TempAllocator& alloc, double d) { 97 auto* ret = new (alloc) MWasmFloatConstant(MIRType::Double); 98 ret->u.f64_ = d; 99 return ret; 100 } 101 102 static MWasmFloatConstant* NewFloat32(TempAllocator& alloc, float f) { 103 auto* ret = new (alloc) MWasmFloatConstant(MIRType::Float32); 104 ret->u.f32_ = f; 105 return ret; 106 } 107 108 #ifdef ENABLE_WASM_SIMD 109 static MWasmFloatConstant* NewSimd128(TempAllocator& alloc, 110 const SimdConstant& s) { 111 auto* ret = new (alloc) MWasmFloatConstant(MIRType::Simd128); 112 memcpy(ret->u.s128_, s.bytes(), 16); 113 return ret; 114 } 115 #endif 116 117 HashNumber valueHash() const override; 118 bool congruentTo(const MDefinition* ins) const override; 119 AliasSet getAliasSet() const override { return AliasSet::None(); } 120 121 const double& toDouble() const { 122 MOZ_ASSERT(type() == MIRType::Double); 123 return u.f64_; 124 } 125 const float& toFloat32() const { 126 MOZ_ASSERT(type() == MIRType::Float32); 127 return u.f32_; 128 } 129 #ifdef ENABLE_WASM_SIMD 130 const SimdConstant toSimd128() const { 131 MOZ_ASSERT(type() == MIRType::Simd128); 132 return SimdConstant::CreateX16(u.s128_); 133 } 134 #endif 135 #ifdef JS_JITSPEW 136 void getExtras(ExtrasCollector* extras) const override { 137 char buf[64]; 138 switch (type()) { 139 case MIRType::Float32: 140 SprintfLiteral(buf, "f32{%e}", (double)u.f32_); 141 break; 142 case MIRType::Double: 143 SprintfLiteral(buf, "f64{%e}", u.f64_); 144 break; 145 # ifdef ENABLE_WASM_SIMD 146 case MIRType::Simd128: 147 SprintfLiteral(buf, "v128{[1]=%016llx:[0]=%016llx}", 148 (unsigned long long int)u.bits_[1], 149 (unsigned long long int)u.bits_[0]); 150 break; 151 # endif 152 default: 153 SprintfLiteral(buf, "!!getExtras: missing case!!"); 154 break; 155 } 156 extras->add(buf); 157 } 158 #endif 159 160 ALLOW_CLONE(MWasmFloatConstant) 161 }; 162 163 // Converts a uint32 to a float32 (coming from wasm). 164 class MWasmUnsignedToFloat32 : public MUnaryInstruction, 165 public NoTypePolicy::Data { 166 explicit MWasmUnsignedToFloat32(MDefinition* def) 167 : MUnaryInstruction(classOpcode, def) { 168 setResultType(MIRType::Float32); 169 setMovable(); 170 } 171 172 public: 173 INSTRUCTION_HEADER(WasmUnsignedToFloat32) 174 TRIVIAL_NEW_WRAPPERS 175 176 MDefinition* foldsTo(TempAllocator& alloc) override; 177 bool congruentTo(const MDefinition* ins) const override { 178 return congruentIfOperandsEqual(ins); 179 } 180 AliasSet getAliasSet() const override { return AliasSet::None(); } 181 182 bool canProduceFloat32() const override { return true; } 183 184 ALLOW_CLONE(MWasmUnsignedToFloat32) 185 }; 186 187 class MWasmNewI31Ref : public MUnaryInstruction, public NoTypePolicy::Data { 188 explicit MWasmNewI31Ref(MDefinition* input) 189 : MUnaryInstruction(classOpcode, input) { 190 MOZ_ASSERT(input->type() == MIRType::Int32); 191 setResultType(MIRType::WasmAnyRef); 192 setMovable(); 193 initWasmRefType(wasm::MaybeRefType(wasm::RefType::i31().asNonNullable())); 194 } 195 196 public: 197 INSTRUCTION_HEADER(WasmNewI31Ref) 198 TRIVIAL_NEW_WRAPPERS 199 200 bool congruentTo(const MDefinition* ins) const override { 201 return congruentIfOperandsEqual(ins); 202 } 203 204 AliasSet getAliasSet() const override { return AliasSet::None(); } 205 }; 206 207 // The same as MWasmTruncateToInt64 but with the Instance dependency. 208 // It used only for arm now because on arm we need to call builtin to truncate 209 // to i64. 210 class MWasmBuiltinTruncateToInt64 : public MAryInstruction<2>, 211 public NoTypePolicy::Data { 212 TruncFlags flags_; 213 wasm::TrapSiteDesc trapSiteDesc_; 214 215 MWasmBuiltinTruncateToInt64(MDefinition* def, MDefinition* instance, 216 TruncFlags flags, 217 const wasm::TrapSiteDesc& trapSiteDesc) 218 : MAryInstruction(classOpcode), 219 flags_(flags), 220 trapSiteDesc_(trapSiteDesc) { 221 initOperand(0, def); 222 initOperand(1, instance); 223 224 setResultType(MIRType::Int64); 225 setGuard(); // neither removable nor movable because of possible 226 // side-effects. 227 } 228 229 public: 230 INSTRUCTION_HEADER(WasmBuiltinTruncateToInt64) 231 NAMED_OPERANDS((0, input), (1, instance)); 232 TRIVIAL_NEW_WRAPPERS 233 234 bool isUnsigned() const { return flags_ & TRUNC_UNSIGNED; } 235 bool isSaturating() const { return flags_ & TRUNC_SATURATING; } 236 TruncFlags flags() const { return flags_; } 237 const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; } 238 239 bool congruentTo(const MDefinition* ins) const override { 240 return congruentIfOperandsEqual(ins) && 241 ins->toWasmBuiltinTruncateToInt64()->flags() == flags_; 242 } 243 AliasSet getAliasSet() const override { return AliasSet::None(); } 244 }; 245 246 class MWasmTruncateToInt64 : public MUnaryInstruction, 247 public NoTypePolicy::Data { 248 TruncFlags flags_; 249 wasm::TrapSiteDesc trapSiteDesc_; 250 251 MWasmTruncateToInt64(MDefinition* def, TruncFlags flags, 252 const wasm::TrapSiteDesc& trapSiteDesc) 253 : MUnaryInstruction(classOpcode, def), 254 flags_(flags), 255 trapSiteDesc_(trapSiteDesc) { 256 setResultType(MIRType::Int64); 257 setGuard(); // neither removable nor movable because of possible 258 // side-effects. 259 } 260 261 public: 262 INSTRUCTION_HEADER(WasmTruncateToInt64) 263 TRIVIAL_NEW_WRAPPERS 264 265 bool isUnsigned() const { return flags_ & TRUNC_UNSIGNED; } 266 bool isSaturating() const { return flags_ & TRUNC_SATURATING; } 267 TruncFlags flags() const { return flags_; } 268 const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; } 269 270 bool congruentTo(const MDefinition* ins) const override { 271 return congruentIfOperandsEqual(ins) && 272 ins->toWasmTruncateToInt64()->flags() == flags_; 273 } 274 AliasSet getAliasSet() const override { return AliasSet::None(); } 275 }; 276 277 // Truncate a value to an int32, with wasm semantics: this will trap when the 278 // value is out of range. 279 class MWasmTruncateToInt32 : public MUnaryInstruction, 280 public NoTypePolicy::Data { 281 TruncFlags flags_; 282 wasm::TrapSiteDesc trapSiteDesc_; 283 284 explicit MWasmTruncateToInt32(MDefinition* def, TruncFlags flags, 285 const wasm::TrapSiteDesc& trapSiteDesc) 286 : MUnaryInstruction(classOpcode, def), 287 flags_(flags), 288 trapSiteDesc_(trapSiteDesc) { 289 setResultType(MIRType::Int32); 290 setGuard(); // neither removable nor movable because of possible 291 // side-effects. 292 } 293 294 public: 295 INSTRUCTION_HEADER(WasmTruncateToInt32) 296 TRIVIAL_NEW_WRAPPERS 297 298 bool isUnsigned() const { return flags_ & TRUNC_UNSIGNED; } 299 bool isSaturating() const { return flags_ & TRUNC_SATURATING; } 300 TruncFlags flags() const { return flags_; } 301 const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; } 302 303 MDefinition* foldsTo(TempAllocator& alloc) override; 304 305 bool congruentTo(const MDefinition* ins) const override { 306 return congruentIfOperandsEqual(ins) && 307 ins->toWasmTruncateToInt32()->flags() == flags_; 308 } 309 310 AliasSet getAliasSet() const override { return AliasSet::None(); } 311 }; 312 313 // It is like MTruncateToInt32 but with instance dependency. 314 class MWasmBuiltinTruncateToInt32 : public MAryInstruction<2>, 315 public ToInt32Policy::Data { 316 wasm::TrapSiteDesc trapSiteDesc_; 317 318 MWasmBuiltinTruncateToInt32( 319 MDefinition* def, MDefinition* instance, 320 wasm::TrapSiteDesc trapSiteDesc = wasm::TrapSiteDesc()) 321 : MAryInstruction(classOpcode), trapSiteDesc_(trapSiteDesc) { 322 initOperand(0, def); 323 initOperand(1, instance); 324 setResultType(MIRType::Int32); 325 setMovable(); 326 327 // Guard unless the conversion is known to be non-effectful & non-throwing. 328 if (MTruncateToInt32::mightHaveSideEffects(def)) { 329 setGuard(); 330 } 331 } 332 333 public: 334 INSTRUCTION_HEADER(WasmBuiltinTruncateToInt32) 335 NAMED_OPERANDS((0, input), (1, instance)) 336 TRIVIAL_NEW_WRAPPERS 337 338 bool congruentTo(const MDefinition* ins) const override { 339 return congruentIfOperandsEqual(ins); 340 } 341 AliasSet getAliasSet() const override { return AliasSet::None(); } 342 343 const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; } 344 345 ALLOW_CLONE(MWasmBuiltinTruncateToInt32) 346 }; 347 348 class MWasmBuiltinDivI64 : public MAryInstruction<3>, public ArithPolicy::Data { 349 bool canBeNegativeZero_; 350 bool canBeNegativeOverflow_; 351 bool canBeDivideByZero_; 352 bool canBeNegativeDividend_; 353 bool unsigned_; // If false, signedness will be derived from operands 354 bool trapOnError_; 355 wasm::TrapSiteDesc trapSiteDesc_; 356 357 MWasmBuiltinDivI64(MDefinition* left, MDefinition* right, 358 MDefinition* instance) 359 : MAryInstruction(classOpcode), 360 canBeNegativeZero_(true), 361 canBeNegativeOverflow_(true), 362 canBeDivideByZero_(true), 363 canBeNegativeDividend_(true), 364 unsigned_(false), 365 trapOnError_(false) { 366 initOperand(0, left); 367 initOperand(1, right); 368 initOperand(2, instance); 369 370 setResultType(MIRType::Int64); 371 setMovable(); 372 } 373 374 public: 375 INSTRUCTION_HEADER(WasmBuiltinDivI64) 376 377 NAMED_OPERANDS((0, lhs), (1, rhs), (2, instance)) 378 379 static MWasmBuiltinDivI64* New( 380 TempAllocator& alloc, MDefinition* left, MDefinition* right, 381 MDefinition* instance, bool unsignd, bool trapOnError = false, 382 wasm::TrapSiteDesc trapSiteDesc = wasm::TrapSiteDesc()) { 383 auto* wasm64Div = new (alloc) MWasmBuiltinDivI64(left, right, instance); 384 wasm64Div->unsigned_ = unsignd; 385 wasm64Div->trapOnError_ = trapOnError; 386 wasm64Div->trapSiteDesc_ = trapSiteDesc; 387 if (trapOnError) { 388 wasm64Div->setGuard(); // not removable because of possible side-effects. 389 wasm64Div->setNotMovable(); 390 } 391 return wasm64Div; 392 } 393 394 bool canBeNegativeZero() const { return canBeNegativeZero_; } 395 void setCanBeNegativeZero(bool negativeZero) { 396 canBeNegativeZero_ = negativeZero; 397 } 398 399 bool canBeNegativeOverflow() const { return canBeNegativeOverflow_; } 400 401 bool canBeDivideByZero() const { return canBeDivideByZero_; } 402 403 bool canBeNegativeDividend() const { 404 // "Dividend" is an ambiguous concept for unsigned truncated 405 // division, because of the truncation procedure: 406 // ((x>>>0)/2)|0, for example, gets transformed in 407 // MWasmDiv::truncate into a node with lhs representing x (not 408 // x>>>0) and rhs representing the constant 2; in other words, 409 // the MIR node corresponds to "cast operands to unsigned and 410 // divide" operation. In this case, is the dividend x or is it 411 // x>>>0? In order to resolve such ambiguities, we disallow 412 // the usage of this method for unsigned division. 413 MOZ_ASSERT(!unsigned_); 414 return canBeNegativeDividend_; 415 } 416 417 bool isUnsigned() const { return unsigned_; } 418 419 bool trapOnError() const { return trapOnError_; } 420 const wasm::TrapSiteDesc& trapSiteDesc() const { 421 MOZ_ASSERT(trapSiteDesc_.isValid()); 422 return trapSiteDesc_; 423 } 424 425 ALLOW_CLONE(MWasmBuiltinDivI64) 426 }; 427 428 class MWasmBuiltinModD : public MAryInstruction<3>, public ArithPolicy::Data { 429 wasm::BytecodeOffset bytecodeOffset_; 430 431 MWasmBuiltinModD(MDefinition* left, MDefinition* right, MDefinition* instance, 432 MIRType type) 433 : MAryInstruction(classOpcode) { 434 initOperand(0, left); 435 initOperand(1, right); 436 initOperand(2, instance); 437 438 setResultType(type); 439 setMovable(); 440 } 441 442 public: 443 INSTRUCTION_HEADER(WasmBuiltinModD) 444 NAMED_OPERANDS((0, lhs), (1, rhs), (2, instance)) 445 446 static MWasmBuiltinModD* New( 447 TempAllocator& alloc, MDefinition* left, MDefinition* right, 448 MDefinition* instance, MIRType type, 449 wasm::BytecodeOffset bytecodeOffset = wasm::BytecodeOffset()) { 450 auto* wasmBuiltinModD = 451 new (alloc) MWasmBuiltinModD(left, right, instance, type); 452 wasmBuiltinModD->bytecodeOffset_ = bytecodeOffset; 453 return wasmBuiltinModD; 454 } 455 456 wasm::BytecodeOffset bytecodeOffset() const { 457 MOZ_ASSERT(bytecodeOffset_.isValid()); 458 return bytecodeOffset_; 459 } 460 461 ALLOW_CLONE(MWasmBuiltinModD) 462 }; 463 464 class MWasmBuiltinModI64 : public MAryInstruction<3>, public ArithPolicy::Data { 465 bool unsigned_; // If false, signedness will be derived from operands 466 bool canBeNegativeDividend_; 467 bool canBeDivideByZero_; 468 bool trapOnError_; 469 wasm::TrapSiteDesc trapSiteDesc_; 470 471 MWasmBuiltinModI64(MDefinition* left, MDefinition* right, 472 MDefinition* instance) 473 : MAryInstruction(classOpcode), 474 unsigned_(false), 475 canBeNegativeDividend_(true), 476 canBeDivideByZero_(true), 477 trapOnError_(false) { 478 initOperand(0, left); 479 initOperand(1, right); 480 initOperand(2, instance); 481 482 setResultType(MIRType::Int64); 483 setMovable(); 484 } 485 486 public: 487 INSTRUCTION_HEADER(WasmBuiltinModI64) 488 489 NAMED_OPERANDS((0, lhs), (1, rhs), (2, instance)) 490 491 static MWasmBuiltinModI64* New( 492 TempAllocator& alloc, MDefinition* left, MDefinition* right, 493 MDefinition* instance, bool unsignd, bool trapOnError = false, 494 wasm::TrapSiteDesc trapSiteDesc = wasm::TrapSiteDesc()) { 495 auto* mod = new (alloc) MWasmBuiltinModI64(left, right, instance); 496 mod->unsigned_ = unsignd; 497 mod->trapOnError_ = trapOnError; 498 mod->trapSiteDesc_ = trapSiteDesc; 499 if (trapOnError) { 500 mod->setGuard(); // not removable because of possible side-effects. 501 mod->setNotMovable(); 502 } 503 return mod; 504 } 505 506 bool canBeNegativeDividend() const { 507 MOZ_ASSERT(!unsigned_); 508 return canBeNegativeDividend_; 509 } 510 511 bool canBeDivideByZero() const { return canBeDivideByZero_; } 512 513 bool isUnsigned() const { return unsigned_; } 514 515 bool trapOnError() const { return trapOnError_; } 516 const wasm::TrapSiteDesc& trapSiteDesc() const { 517 MOZ_ASSERT(trapSiteDesc_.isValid()); 518 return trapSiteDesc_; 519 } 520 521 ALLOW_CLONE(MWasmBuiltinModI64) 522 }; 523 524 // Check whether we need to fire the interrupt handler (in wasm code). 525 class MWasmInterruptCheck : public MUnaryInstruction, 526 public NoTypePolicy::Data { 527 wasm::TrapSiteDesc trapSiteDesc_; 528 529 MWasmInterruptCheck(MDefinition* instance, 530 const wasm::TrapSiteDesc& trapSiteDesc) 531 : MUnaryInstruction(classOpcode, instance), trapSiteDesc_(trapSiteDesc) { 532 setGuard(); 533 } 534 535 public: 536 INSTRUCTION_HEADER(WasmInterruptCheck) 537 TRIVIAL_NEW_WRAPPERS 538 NAMED_OPERANDS((0, instance)) 539 540 AliasSet getAliasSet() const override { return AliasSet::None(); } 541 const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; } 542 543 ALLOW_CLONE(MWasmInterruptCheck) 544 }; 545 546 // Directly jumps to the indicated trap, leaving Wasm code and reporting a 547 // runtime error. 548 549 class MWasmTrap : public MAryControlInstruction<0, 0>, 550 public NoTypePolicy::Data { 551 wasm::Trap trap_; 552 wasm::TrapSiteDesc trapSiteDesc_; 553 554 explicit MWasmTrap(wasm::Trap trap, const wasm::TrapSiteDesc& trapSiteDesc) 555 : MAryControlInstruction(classOpcode), 556 trap_(trap), 557 trapSiteDesc_(trapSiteDesc) {} 558 559 public: 560 INSTRUCTION_HEADER(WasmTrap) 561 TRIVIAL_NEW_WRAPPERS 562 563 AliasSet getAliasSet() const override { return AliasSet::None(); } 564 565 wasm::Trap trap() const { return trap_; } 566 const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; } 567 }; 568 569 // Flips the input's sign bit, independently of the rest of the number's 570 // payload. Note this is different from multiplying by minus-one, which has 571 // side-effects for e.g. NaNs. 572 class MWasmNeg : public MUnaryInstruction, public NoTypePolicy::Data { 573 MWasmNeg(MDefinition* op, MIRType type) : MUnaryInstruction(classOpcode, op) { 574 setResultType(type); 575 setMovable(); 576 } 577 578 public: 579 INSTRUCTION_HEADER(WasmNeg) 580 TRIVIAL_NEW_WRAPPERS 581 AliasSet getAliasSet() const override { return AliasSet::None(); } 582 ALLOW_CLONE(MWasmNeg) 583 }; 584 585 // Machine-level bitwise AND/OR/XOR, avoiding all JS-level complexity embodied 586 // in MBinaryBitwiseInstruction. 587 class MWasmBinaryBitwise : public MBinaryInstruction, 588 public NoTypePolicy::Data { 589 public: 590 enum class SubOpcode { And, Or, Xor }; 591 592 protected: 593 MWasmBinaryBitwise(MDefinition* left, MDefinition* right, MIRType type, 594 SubOpcode subOpcode) 595 : MBinaryInstruction(classOpcode, left, right), subOpcode_(subOpcode) { 596 MOZ_ASSERT(type == MIRType::Int32 || type == MIRType::Int64); 597 setResultType(type); 598 setMovable(); 599 setCommutative(); 600 } 601 602 public: 603 INSTRUCTION_HEADER(WasmBinaryBitwise) 604 TRIVIAL_NEW_WRAPPERS 605 606 SubOpcode subOpcode() const { return subOpcode_; } 607 MDefinition* foldsTo(TempAllocator& alloc) override; 608 609 bool congruentTo(const MDefinition* ins) const override { 610 return ins->isWasmBinaryBitwise() && 611 ins->toWasmBinaryBitwise()->subOpcode() == subOpcode() && 612 binaryCongruentTo(ins); 613 } 614 615 AliasSet getAliasSet() const override { return AliasSet::None(); } 616 617 #ifdef JS_JITSPEW 618 void getExtras(ExtrasCollector* extras) const override { 619 const char* what = "!!unknown!!"; 620 switch (subOpcode()) { 621 case SubOpcode::And: 622 what = "And"; 623 break; 624 case SubOpcode::Or: 625 what = "Or"; 626 break; 627 case SubOpcode::Xor: 628 what = "Xor"; 629 break; 630 } 631 extras->add(what); 632 } 633 #endif 634 635 private: 636 SubOpcode subOpcode_; 637 638 ALLOW_CLONE(MWasmBinaryBitwise) 639 }; 640 641 class MWasmLoadInstance : public MUnaryInstruction, public NoTypePolicy::Data { 642 uint32_t offset_; 643 AliasSet aliases_; 644 645 explicit MWasmLoadInstance(MDefinition* instance, uint32_t offset, 646 MIRType type, AliasSet aliases) 647 : MUnaryInstruction(classOpcode, instance), 648 offset_(offset), 649 aliases_(aliases) { 650 // Different instance data have different alias classes and only those 651 // classes are allowed. 652 MOZ_ASSERT( 653 aliases_.flags() == AliasSet::Load(AliasSet::WasmHeapMeta).flags() || 654 aliases_.flags() == AliasSet::Load(AliasSet::WasmTableMeta).flags() || 655 aliases_.flags() == 656 AliasSet::Load(AliasSet::WasmPendingException).flags() || 657 aliases_.flags() == AliasSet::None().flags()); 658 659 // The only types supported at the moment. 660 MOZ_ASSERT(type == MIRType::Pointer || type == MIRType::Int32 || 661 type == MIRType::Int64 || type == MIRType::WasmAnyRef); 662 663 setMovable(); 664 setResultType(type); 665 } 666 667 public: 668 INSTRUCTION_HEADER(WasmLoadInstance) 669 TRIVIAL_NEW_WRAPPERS 670 NAMED_OPERANDS((0, instance)) 671 672 uint32_t offset() const { return offset_; } 673 674 bool congruentTo(const MDefinition* ins) const override { 675 return op() == ins->op() && 676 offset() == ins->toWasmLoadInstance()->offset() && 677 type() == ins->type(); 678 } 679 680 HashNumber valueHash() const override { 681 HashNumber hash = MUnaryInstruction::valueHash(); 682 hash = addU32ToHash(hash, offset()); 683 return hash; 684 } 685 686 AliasSet getAliasSet() const override { return aliases_; } 687 }; 688 689 class MWasmStoreInstance : public MBinaryInstruction, 690 public NoTypePolicy::Data { 691 uint32_t offset_; 692 AliasSet aliases_; 693 694 explicit MWasmStoreInstance(MDefinition* instance, MDefinition* value, 695 uint32_t offset, MIRType type, AliasSet aliases) 696 : MBinaryInstruction(classOpcode, instance, value), 697 offset_(offset), 698 aliases_(aliases) { 699 // Different instance data have different alias classes and only those 700 // classes are allowed. 701 MOZ_ASSERT(aliases_.flags() == 702 AliasSet::Store(AliasSet::WasmPendingException).flags()); 703 704 // The only types supported at the moment. 705 MOZ_ASSERT(type == MIRType::Pointer || type == MIRType::Int32 || 706 type == MIRType::Int64 || type == MIRType::WasmAnyRef); 707 } 708 709 public: 710 INSTRUCTION_HEADER(WasmStoreInstance) 711 TRIVIAL_NEW_WRAPPERS 712 NAMED_OPERANDS((0, instance), (1, value)) 713 714 uint32_t offset() const { return offset_; } 715 716 AliasSet getAliasSet() const override { return aliases_; } 717 }; 718 719 class MWasmHeapReg : public MNullaryInstruction { 720 AliasSet aliases_; 721 722 explicit MWasmHeapReg(AliasSet aliases) 723 : MNullaryInstruction(classOpcode), aliases_(aliases) { 724 setMovable(); 725 setResultType(MIRType::Pointer); 726 } 727 728 public: 729 INSTRUCTION_HEADER(WasmHeapReg) 730 TRIVIAL_NEW_WRAPPERS 731 732 bool congruentTo(const MDefinition* ins) const override { 733 return ins->isWasmHeapReg(); 734 } 735 736 AliasSet getAliasSet() const override { return aliases_; } 737 }; 738 739 // For memory32, bounds check nodes are of type Int32 on 32-bit systems for both 740 // wasm and asm.js code, as well as on 64-bit systems for asm.js code and for 741 // wasm code that is known to have a bounds check limit that fits into 32 bits. 742 // They are of type Int64 only on 64-bit systems for wasm code with 4GB heaps. 743 // There is no way for nodes of both types to be present in the same function. 744 // Should this change, then BCE must be updated to take type into account. 745 // 746 // For memory64, bounds check nodes are always of type Int64. 747 748 class MWasmBoundsCheck : public MBinaryInstruction, public NoTypePolicy::Data { 749 public: 750 enum Target { 751 // If using the following options, `targetIndex` must be specified. 752 Memory, 753 Table, 754 // Everything else. Currently used for arrays in the GC proposal. If using 755 // this, targetIndex should not be used. 756 Other, 757 }; 758 759 private: 760 wasm::TrapSiteDesc trapSiteDesc_; 761 Target target_; 762 uint32_t targetIndex_; 763 764 explicit MWasmBoundsCheck(MDefinition* index, MDefinition* boundsCheckLimit, 765 const wasm::TrapSiteDesc& trapSiteDesc, 766 Target target, uint32_t targetIndex = UINT32_MAX) 767 : MBinaryInstruction(classOpcode, index, boundsCheckLimit), 768 trapSiteDesc_(trapSiteDesc), 769 target_(target), 770 targetIndex_(targetIndex) { 771 MOZ_ASSERT(index->type() == boundsCheckLimit->type()); 772 MOZ_ASSERT_IF(target == Memory || target == Table, 773 targetIndex != UINT32_MAX); 774 MOZ_ASSERT_IF(target == Other, targetIndex == UINT32_MAX); 775 776 // Bounds check is effectful: it throws for OOB. 777 setGuard(); 778 779 if (JitOptions.spectreIndexMasking) { 780 setResultType(index->type()); 781 } 782 } 783 784 public: 785 INSTRUCTION_HEADER(WasmBoundsCheck) 786 TRIVIAL_NEW_WRAPPERS 787 NAMED_OPERANDS((0, index), (1, boundsCheckLimit)) 788 789 AliasSet getAliasSet() const override { return AliasSet::None(); } 790 791 const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; } 792 Target target() const { return target_; } 793 uint32_t targetIndex() const { return targetIndex_; } 794 795 bool isRedundant() const { return !isGuard(); } 796 void setRedundant() { setNotGuard(); } 797 798 bool congruentTo(const MDefinition* ins) const override { 799 return congruentIfOperandsEqual(ins) && 800 ins->toWasmBoundsCheck()->target() == target() && 801 ins->toWasmBoundsCheck()->targetIndex() == targetIndex(); 802 } 803 804 ALLOW_CLONE(MWasmBoundsCheck) 805 }; 806 807 class MWasmAddOffset : public MUnaryInstruction, public NoTypePolicy::Data { 808 uint64_t offset_; 809 wasm::TrapSiteDesc trapSiteDesc_; 810 811 MWasmAddOffset(MDefinition* base, uint64_t offset, 812 const wasm::TrapSiteDesc& trapSiteDesc) 813 : MUnaryInstruction(classOpcode, base), 814 offset_(offset), 815 trapSiteDesc_(trapSiteDesc) { 816 setGuard(); 817 MOZ_ASSERT(base->type() == MIRType::Int32 || 818 base->type() == MIRType::Int64); 819 setResultType(base->type()); 820 } 821 822 public: 823 INSTRUCTION_HEADER(WasmAddOffset) 824 TRIVIAL_NEW_WRAPPERS 825 NAMED_OPERANDS((0, base)) 826 827 MDefinition* foldsTo(TempAllocator& alloc) override; 828 829 AliasSet getAliasSet() const override { return AliasSet::None(); } 830 831 uint64_t offset() const { return offset_; } 832 const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; } 833 }; 834 835 class MWasmAlignmentCheck : public MUnaryInstruction, 836 public NoTypePolicy::Data { 837 uint32_t byteSize_; 838 wasm::TrapSiteDesc trapSiteDesc_; 839 840 explicit MWasmAlignmentCheck(MDefinition* index, uint32_t byteSize, 841 const wasm::TrapSiteDesc& trapSiteDesc) 842 : MUnaryInstruction(classOpcode, index), 843 byteSize_(byteSize), 844 trapSiteDesc_(trapSiteDesc) { 845 MOZ_ASSERT(mozilla::IsPowerOfTwo(byteSize)); 846 // Alignment check is effectful: it throws for unaligned. 847 setGuard(); 848 } 849 850 public: 851 INSTRUCTION_HEADER(WasmAlignmentCheck) 852 TRIVIAL_NEW_WRAPPERS 853 NAMED_OPERANDS((0, index)) 854 855 bool congruentTo(const MDefinition* ins) const override; 856 857 AliasSet getAliasSet() const override { return AliasSet::None(); } 858 859 uint32_t byteSize() const { return byteSize_; } 860 861 const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; } 862 863 ALLOW_CLONE(MWasmAlignmentCheck) 864 }; 865 866 class MWasmLoad 867 : public MVariadicInstruction, // memoryBase is nullptr on some platforms 868 public NoTypePolicy::Data { 869 wasm::MemoryAccessDesc access_; 870 871 explicit MWasmLoad(const wasm::MemoryAccessDesc& access, MIRType resultType) 872 : MVariadicInstruction(classOpcode), access_(access) { 873 setGuard(); 874 setResultType(resultType); 875 } 876 877 public: 878 INSTRUCTION_HEADER(WasmLoad) 879 NAMED_OPERANDS((0, base), (1, memoryBase)); 880 881 static MWasmLoad* New(TempAllocator& alloc, MDefinition* memoryBase, 882 MDefinition* base, const wasm::MemoryAccessDesc& access, 883 MIRType resultType) { 884 MWasmLoad* load = new (alloc) MWasmLoad(access, resultType); 885 if (!load->init(alloc, 1 + !!memoryBase)) { 886 return nullptr; 887 } 888 889 load->initOperand(0, base); 890 if (memoryBase) { 891 load->initOperand(1, memoryBase); 892 } 893 894 return load; 895 } 896 897 const wasm::MemoryAccessDesc& access() const { return access_; } 898 899 AliasSet getAliasSet() const override { 900 // When a barrier is needed, make the instruction effectful by giving 901 // it a "store" effect. 902 if (access_.isAtomic()) { 903 return AliasSet::Store(AliasSet::WasmHeap); 904 } 905 return AliasSet::Load(AliasSet::WasmHeap); 906 } 907 908 bool hasMemoryBase() const { return numOperands() > 1; } 909 910 #ifdef JS_JITSPEW 911 void getExtras(ExtrasCollector* extras) const override { 912 char buf[64]; 913 SprintfLiteral(buf, "(offs=%lld)", (long long int)access().offset64()); 914 extras->add(buf); 915 } 916 #endif 917 918 // Unfortunately we cannot use ALLOW_CLONE here, due to the variable number 919 // of operands. 920 bool canClone() const override { return true; } 921 MInstruction* clone(TempAllocator& alloc, 922 const MDefinitionVector& inputs) const override { 923 MInstruction* res = 924 MWasmLoad::New(alloc, hasMemoryBase() ? memoryBase() : nullptr, base(), 925 access(), type()); 926 if (!res) { 927 return nullptr; 928 } 929 for (size_t i = 0; i < numOperands(); i++) { 930 res->replaceOperand(i, inputs[i]); 931 } 932 return res; 933 } 934 }; 935 936 class MWasmStore : public MVariadicInstruction, public NoTypePolicy::Data { 937 wasm::MemoryAccessDesc access_; 938 939 explicit MWasmStore(const wasm::MemoryAccessDesc& access) 940 : MVariadicInstruction(classOpcode), access_(access) { 941 setGuard(); 942 } 943 944 public: 945 INSTRUCTION_HEADER(WasmStore) 946 NAMED_OPERANDS((0, base), (1, value), (2, memoryBase)) 947 948 static MWasmStore* New(TempAllocator& alloc, MDefinition* memoryBase, 949 MDefinition* base, 950 const wasm::MemoryAccessDesc& access, 951 MDefinition* value) { 952 MWasmStore* store = new (alloc) MWasmStore(access); 953 if (!store->init(alloc, 2 + !!memoryBase)) { 954 return nullptr; 955 } 956 957 store->initOperand(0, base); 958 store->initOperand(1, value); 959 if (memoryBase) { 960 store->initOperand(2, memoryBase); 961 } 962 963 return store; 964 } 965 966 const wasm::MemoryAccessDesc& access() const { return access_; } 967 968 AliasSet getAliasSet() const override { 969 return AliasSet::Store(AliasSet::WasmHeap); 970 } 971 972 bool hasMemoryBase() const { return numOperands() > 2; } 973 974 #ifdef JS_JITSPEW 975 void getExtras(ExtrasCollector* extras) const override { 976 char buf[64]; 977 SprintfLiteral(buf, "(offs=%lld)", (long long int)access().offset64()); 978 extras->add(buf); 979 } 980 #endif 981 982 bool canClone() const override { return true; } 983 MInstruction* clone(TempAllocator& alloc, 984 const MDefinitionVector& inputs) const override { 985 MInstruction* res = 986 MWasmStore::New(alloc, hasMemoryBase() ? memoryBase() : nullptr, base(), 987 access(), value()); 988 if (!res) { 989 return nullptr; 990 } 991 for (size_t i = 0; i < numOperands(); i++) { 992 res->replaceOperand(i, inputs[i]); 993 } 994 return res; 995 } 996 }; 997 998 class MAsmJSMemoryAccess { 999 Scalar::Type accessType_; 1000 bool needsBoundsCheck_; 1001 1002 public: 1003 explicit MAsmJSMemoryAccess(Scalar::Type accessType) 1004 : accessType_(accessType), needsBoundsCheck_(true) { 1005 MOZ_ASSERT(accessType != Scalar::Uint8Clamped); 1006 } 1007 1008 Scalar::Type accessType() const { return accessType_; } 1009 unsigned byteSize() const { return TypedArrayElemSize(accessType()); } 1010 bool needsBoundsCheck() const { return needsBoundsCheck_; } 1011 1012 wasm::MemoryAccessDesc access() const { 1013 return wasm::MemoryAccessDesc(0, accessType_, Scalar::byteSize(accessType_), 1014 0, wasm::TrapSiteDesc(), false); 1015 } 1016 1017 void removeBoundsCheck() { needsBoundsCheck_ = false; } 1018 }; 1019 1020 class MAsmJSLoadHeap 1021 : public MVariadicInstruction, // 1 plus optional memoryBase and 1022 // boundsCheckLimit 1023 public MAsmJSMemoryAccess, 1024 public NoTypePolicy::Data { 1025 uint32_t memoryBaseIndex_; 1026 1027 explicit MAsmJSLoadHeap(uint32_t memoryBaseIndex, Scalar::Type accessType) 1028 : MVariadicInstruction(classOpcode), 1029 MAsmJSMemoryAccess(accessType), 1030 memoryBaseIndex_(memoryBaseIndex) { 1031 setResultType(ScalarTypeToMIRType(accessType)); 1032 } 1033 1034 public: 1035 INSTRUCTION_HEADER(AsmJSLoadHeap) 1036 NAMED_OPERANDS((0, base), (1, boundsCheckLimit)) 1037 1038 static MAsmJSLoadHeap* New(TempAllocator& alloc, MDefinition* memoryBase, 1039 MDefinition* base, MDefinition* boundsCheckLimit, 1040 Scalar::Type accessType) { 1041 uint32_t nextIndex = 2; 1042 uint32_t memoryBaseIndex = memoryBase ? nextIndex++ : UINT32_MAX; 1043 1044 MAsmJSLoadHeap* load = 1045 new (alloc) MAsmJSLoadHeap(memoryBaseIndex, accessType); 1046 if (!load->init(alloc, nextIndex)) { 1047 return nullptr; 1048 } 1049 1050 load->initOperand(0, base); 1051 load->initOperand(1, boundsCheckLimit); 1052 if (memoryBase) { 1053 load->initOperand(memoryBaseIndex, memoryBase); 1054 } 1055 1056 return load; 1057 } 1058 1059 bool hasMemoryBase() const { return memoryBaseIndex_ != UINT32_MAX; } 1060 MDefinition* memoryBase() const { 1061 MOZ_ASSERT(hasMemoryBase()); 1062 return getOperand(memoryBaseIndex_); 1063 } 1064 1065 bool congruentTo(const MDefinition* ins) const override; 1066 AliasSet getAliasSet() const override { 1067 return AliasSet::Load(AliasSet::WasmHeap); 1068 } 1069 AliasType mightAlias(const MDefinition* def) const override; 1070 }; 1071 1072 class MAsmJSStoreHeap 1073 : public MVariadicInstruction, // 2 plus optional memoryBase and 1074 // boundsCheckLimit 1075 public MAsmJSMemoryAccess, 1076 public NoTypePolicy::Data { 1077 uint32_t memoryBaseIndex_; 1078 1079 explicit MAsmJSStoreHeap(uint32_t memoryBaseIndex, Scalar::Type accessType) 1080 : MVariadicInstruction(classOpcode), 1081 MAsmJSMemoryAccess(accessType), 1082 memoryBaseIndex_(memoryBaseIndex) {} 1083 1084 public: 1085 INSTRUCTION_HEADER(AsmJSStoreHeap) 1086 NAMED_OPERANDS((0, base), (1, value), (2, boundsCheckLimit)) 1087 1088 static MAsmJSStoreHeap* New(TempAllocator& alloc, MDefinition* memoryBase, 1089 MDefinition* base, MDefinition* boundsCheckLimit, 1090 Scalar::Type accessType, MDefinition* v) { 1091 uint32_t nextIndex = 3; 1092 uint32_t memoryBaseIndex = memoryBase ? nextIndex++ : UINT32_MAX; 1093 1094 MAsmJSStoreHeap* store = 1095 new (alloc) MAsmJSStoreHeap(memoryBaseIndex, accessType); 1096 if (!store->init(alloc, nextIndex)) { 1097 return nullptr; 1098 } 1099 1100 store->initOperand(0, base); 1101 store->initOperand(1, v); 1102 store->initOperand(2, boundsCheckLimit); 1103 if (memoryBase) { 1104 store->initOperand(memoryBaseIndex, memoryBase); 1105 } 1106 1107 return store; 1108 } 1109 1110 bool hasMemoryBase() const { return memoryBaseIndex_ != UINT32_MAX; } 1111 MDefinition* memoryBase() const { 1112 MOZ_ASSERT(hasMemoryBase()); 1113 return getOperand(memoryBaseIndex_); 1114 } 1115 1116 AliasSet getAliasSet() const override { 1117 return AliasSet::Store(AliasSet::WasmHeap); 1118 } 1119 }; 1120 1121 class MWasmCompareExchangeHeap : public MVariadicInstruction, 1122 public NoTypePolicy::Data { 1123 wasm::MemoryAccessDesc access_; 1124 wasm::BytecodeOffset bytecodeOffset_; 1125 1126 explicit MWasmCompareExchangeHeap(const wasm::MemoryAccessDesc& access, 1127 wasm::BytecodeOffset bytecodeOffset) 1128 : MVariadicInstruction(classOpcode), 1129 access_(access), 1130 bytecodeOffset_(bytecodeOffset) { 1131 setGuard(); // Not removable 1132 setResultType(ScalarTypeToMIRType(access.type())); 1133 } 1134 1135 public: 1136 INSTRUCTION_HEADER(WasmCompareExchangeHeap) 1137 NAMED_OPERANDS((0, base), (1, oldValue), (2, newValue), (3, instance), 1138 (4, memoryBase)) 1139 1140 static MWasmCompareExchangeHeap* New(TempAllocator& alloc, 1141 wasm::BytecodeOffset bytecodeOffset, 1142 MDefinition* memoryBase, 1143 MDefinition* base, 1144 const wasm::MemoryAccessDesc& access, 1145 MDefinition* oldv, MDefinition* newv, 1146 MDefinition* instance) { 1147 MWasmCompareExchangeHeap* cas = 1148 new (alloc) MWasmCompareExchangeHeap(access, bytecodeOffset); 1149 if (!cas->init(alloc, 4 + !!memoryBase)) { 1150 return nullptr; 1151 } 1152 cas->initOperand(0, base); 1153 cas->initOperand(1, oldv); 1154 cas->initOperand(2, newv); 1155 cas->initOperand(3, instance); 1156 if (memoryBase) { 1157 cas->initOperand(4, memoryBase); 1158 } 1159 return cas; 1160 } 1161 1162 const wasm::MemoryAccessDesc& access() const { return access_; } 1163 wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; } 1164 1165 AliasSet getAliasSet() const override { 1166 return AliasSet::Store(AliasSet::WasmHeap); 1167 } 1168 1169 bool hasMemoryBase() const { return numOperands() > 4; } 1170 }; 1171 1172 class MWasmAtomicExchangeHeap : public MVariadicInstruction, 1173 public NoTypePolicy::Data { 1174 wasm::MemoryAccessDesc access_; 1175 wasm::BytecodeOffset bytecodeOffset_; 1176 1177 explicit MWasmAtomicExchangeHeap(const wasm::MemoryAccessDesc& access, 1178 wasm::BytecodeOffset bytecodeOffset) 1179 : MVariadicInstruction(classOpcode), 1180 access_(access), 1181 bytecodeOffset_(bytecodeOffset) { 1182 setGuard(); // Not removable 1183 setResultType(ScalarTypeToMIRType(access.type())); 1184 } 1185 1186 public: 1187 INSTRUCTION_HEADER(WasmAtomicExchangeHeap) 1188 NAMED_OPERANDS((0, base), (1, value), (2, instance), (3, memoryBase)) 1189 1190 static MWasmAtomicExchangeHeap* New(TempAllocator& alloc, 1191 wasm::BytecodeOffset bytecodeOffset, 1192 MDefinition* memoryBase, 1193 MDefinition* base, 1194 const wasm::MemoryAccessDesc& access, 1195 MDefinition* value, 1196 MDefinition* instance) { 1197 MWasmAtomicExchangeHeap* xchg = 1198 new (alloc) MWasmAtomicExchangeHeap(access, bytecodeOffset); 1199 if (!xchg->init(alloc, 3 + !!memoryBase)) { 1200 return nullptr; 1201 } 1202 1203 xchg->initOperand(0, base); 1204 xchg->initOperand(1, value); 1205 xchg->initOperand(2, instance); 1206 if (memoryBase) { 1207 xchg->initOperand(3, memoryBase); 1208 } 1209 1210 return xchg; 1211 } 1212 1213 const wasm::MemoryAccessDesc& access() const { return access_; } 1214 wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; } 1215 1216 AliasSet getAliasSet() const override { 1217 return AliasSet::Store(AliasSet::WasmHeap); 1218 } 1219 1220 bool hasMemoryBase() const { return numOperands() > 3; } 1221 }; 1222 1223 class MWasmAtomicBinopHeap : public MVariadicInstruction, 1224 public NoTypePolicy::Data { 1225 AtomicOp op_; 1226 wasm::MemoryAccessDesc access_; 1227 wasm::BytecodeOffset bytecodeOffset_; 1228 1229 explicit MWasmAtomicBinopHeap(AtomicOp op, 1230 const wasm::MemoryAccessDesc& access, 1231 wasm::BytecodeOffset bytecodeOffset) 1232 : MVariadicInstruction(classOpcode), 1233 op_(op), 1234 access_(access), 1235 bytecodeOffset_(bytecodeOffset) { 1236 setGuard(); // Not removable 1237 setResultType(ScalarTypeToMIRType(access.type())); 1238 } 1239 1240 public: 1241 INSTRUCTION_HEADER(WasmAtomicBinopHeap) 1242 NAMED_OPERANDS((0, base), (1, value), (2, instance), (3, memoryBase)) 1243 1244 static MWasmAtomicBinopHeap* New(TempAllocator& alloc, 1245 wasm::BytecodeOffset bytecodeOffset, 1246 AtomicOp op, MDefinition* memoryBase, 1247 MDefinition* base, 1248 const wasm::MemoryAccessDesc& access, 1249 MDefinition* v, MDefinition* instance) { 1250 MWasmAtomicBinopHeap* binop = 1251 new (alloc) MWasmAtomicBinopHeap(op, access, bytecodeOffset); 1252 if (!binop->init(alloc, 3 + !!memoryBase)) { 1253 return nullptr; 1254 } 1255 1256 binop->initOperand(0, base); 1257 binop->initOperand(1, v); 1258 binop->initOperand(2, instance); 1259 if (memoryBase) { 1260 binop->initOperand(3, memoryBase); 1261 } 1262 1263 return binop; 1264 } 1265 1266 AtomicOp operation() const { return op_; } 1267 const wasm::MemoryAccessDesc& access() const { return access_; } 1268 wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; } 1269 1270 AliasSet getAliasSet() const override { 1271 return AliasSet::Store(AliasSet::WasmHeap); 1272 } 1273 1274 bool hasMemoryBase() const { return numOperands() > 3; } 1275 }; 1276 1277 class MWasmLoadInstanceDataField : public MUnaryInstruction, 1278 public NoTypePolicy::Data { 1279 unsigned instanceDataOffset_; 1280 bool isConstant_; 1281 1282 MWasmLoadInstanceDataField( 1283 MIRType type, unsigned instanceDataOffset, bool isConstant, 1284 MDefinition* instance, 1285 wasm::MaybeRefType maybeRefType = wasm::MaybeRefType()) 1286 : MUnaryInstruction(classOpcode, instance), 1287 instanceDataOffset_(instanceDataOffset), 1288 isConstant_(isConstant) { 1289 MOZ_ASSERT(IsNumberType(type) || type == MIRType::Simd128 || 1290 type == MIRType::Pointer || type == MIRType::WasmAnyRef); 1291 setResultType(type); 1292 setMovable(); 1293 initWasmRefType(maybeRefType); 1294 } 1295 1296 public: 1297 INSTRUCTION_HEADER(WasmLoadInstanceDataField) 1298 TRIVIAL_NEW_WRAPPERS 1299 NAMED_OPERANDS((0, instance)) 1300 1301 unsigned instanceDataOffset() const { return instanceDataOffset_; } 1302 1303 HashNumber valueHash() const override; 1304 bool congruentTo(const MDefinition* ins) const override; 1305 MDefinition* foldsTo(TempAllocator& alloc) override; 1306 1307 AliasSet getAliasSet() const override { 1308 return isConstant_ ? AliasSet::None() 1309 : AliasSet::Load(AliasSet::WasmInstanceData); 1310 } 1311 1312 AliasType mightAlias(const MDefinition* def) const override; 1313 1314 #ifdef JS_JITSPEW 1315 void getExtras(ExtrasCollector* extras) const override { 1316 char buf[96]; 1317 SprintfLiteral(buf, "(offs=%lld, isConst=%s)", 1318 (long long int)instanceDataOffset_, 1319 isConstant_ ? "true" : "false"); 1320 extras->add(buf); 1321 } 1322 #endif 1323 1324 ALLOW_CLONE(MWasmLoadInstanceDataField) 1325 }; 1326 1327 class MWasmLoadGlobalCell : public MUnaryInstruction, 1328 public NoTypePolicy::Data { 1329 MWasmLoadGlobalCell(MIRType type, MDefinition* cellPtr, 1330 wasm::ValType globalType) 1331 : MUnaryInstruction(classOpcode, cellPtr) { 1332 setResultType(type); 1333 setMovable(); 1334 initWasmRefType(globalType.toMaybeRefType()); 1335 } 1336 1337 public: 1338 INSTRUCTION_HEADER(WasmLoadGlobalCell) 1339 TRIVIAL_NEW_WRAPPERS 1340 NAMED_OPERANDS((0, cellPtr)) 1341 1342 // The default valueHash is good enough, because there are no non-operand 1343 // fields. 1344 bool congruentTo(const MDefinition* ins) const override; 1345 1346 AliasSet getAliasSet() const override { 1347 return AliasSet::Load(AliasSet::WasmGlobalCell); 1348 } 1349 1350 AliasType mightAlias(const MDefinition* def) const override; 1351 1352 ALLOW_CLONE(MWasmLoadGlobalCell) 1353 }; 1354 1355 class MWasmLoadTableElement : public MBinaryInstruction, 1356 public NoTypePolicy::Data { 1357 MWasmLoadTableElement(MDefinition* elements, MDefinition* index, 1358 wasm::RefType refType) 1359 : MBinaryInstruction(classOpcode, elements, index) { 1360 setResultType(MIRType::WasmAnyRef); 1361 setMovable(); 1362 initWasmRefType(wasm::MaybeRefType(refType)); 1363 } 1364 1365 public: 1366 INSTRUCTION_HEADER(WasmLoadTableElement) 1367 TRIVIAL_NEW_WRAPPERS 1368 NAMED_OPERANDS((0, elements)) 1369 NAMED_OPERANDS((1, index)) 1370 1371 AliasSet getAliasSet() const override { 1372 return AliasSet::Load(AliasSet::WasmTableElement); 1373 } 1374 }; 1375 1376 class MWasmStoreInstanceDataField : public MBinaryInstruction, 1377 public NoTypePolicy::Data { 1378 MWasmStoreInstanceDataField(unsigned instanceDataOffset, MDefinition* value, 1379 MDefinition* instance) 1380 : MBinaryInstruction(classOpcode, value, instance), 1381 instanceDataOffset_(instanceDataOffset) {} 1382 1383 unsigned instanceDataOffset_; 1384 1385 public: 1386 INSTRUCTION_HEADER(WasmStoreInstanceDataField) 1387 TRIVIAL_NEW_WRAPPERS 1388 NAMED_OPERANDS((0, value), (1, instance)) 1389 1390 unsigned instanceDataOffset() const { return instanceDataOffset_; } 1391 1392 AliasSet getAliasSet() const override { 1393 return AliasSet::Store(AliasSet::WasmInstanceData); 1394 } 1395 }; 1396 1397 class MWasmStoreGlobalCell : public MBinaryInstruction, 1398 public NoTypePolicy::Data { 1399 MWasmStoreGlobalCell(MDefinition* value, MDefinition* cellPtr) 1400 : MBinaryInstruction(classOpcode, value, cellPtr) {} 1401 1402 public: 1403 INSTRUCTION_HEADER(WasmStoreGlobalCell) 1404 TRIVIAL_NEW_WRAPPERS 1405 NAMED_OPERANDS((0, value), (1, cellPtr)) 1406 1407 AliasSet getAliasSet() const override { 1408 return AliasSet::Store(AliasSet::WasmGlobalCell); 1409 } 1410 1411 ALLOW_CLONE(MWasmStoreGlobalCell) 1412 }; 1413 1414 class MWasmStoreStackResult : public MBinaryInstruction, 1415 public NoTypePolicy::Data { 1416 MWasmStoreStackResult(MDefinition* stackResultArea, uint32_t offset, 1417 MDefinition* value) 1418 : MBinaryInstruction(classOpcode, stackResultArea, value), 1419 offset_(offset) {} 1420 1421 uint32_t offset_; 1422 1423 public: 1424 INSTRUCTION_HEADER(WasmStoreStackResult) 1425 TRIVIAL_NEW_WRAPPERS 1426 NAMED_OPERANDS((0, stackResultArea), (1, value)) 1427 1428 uint32_t offset() const { return offset_; } 1429 1430 AliasSet getAliasSet() const override { 1431 return AliasSet::Store(AliasSet::WasmStackResult); 1432 } 1433 }; 1434 1435 // Represents a known-good derived pointer into an object or memory region (in 1436 // the most general sense) that will not move while the derived pointer is live. 1437 // The `offset` *must* be a valid offset into the object represented by `base`; 1438 // hence overflow in the address calculation will never be an issue. `offset` 1439 // must be representable as a 31-bit unsigned integer. 1440 // 1441 // DO NOT use this with a base value of any JS-heap-resident object type. 1442 // Such a value would need to be adjusted during GC, yet we have no mechanism 1443 // to do that. See bug 1810090. 1444 1445 class MWasmDerivedPointer : public MUnaryInstruction, 1446 public NoTypePolicy::Data { 1447 MWasmDerivedPointer(MDefinition* base, size_t offset) 1448 : MUnaryInstruction(classOpcode, base), offset_(uint32_t(offset)) { 1449 MOZ_ASSERT(offset <= INT32_MAX); 1450 // Do not change this to allow `base` to be a GC-heap allocated type. 1451 MOZ_ASSERT(base->type() == MIRType::Pointer || 1452 base->type() == TargetWordMIRType()); 1453 setResultType(MIRType::Pointer); 1454 setMovable(); 1455 } 1456 1457 uint32_t offset_; 1458 1459 public: 1460 INSTRUCTION_HEADER(WasmDerivedPointer) 1461 TRIVIAL_NEW_WRAPPERS 1462 NAMED_OPERANDS((0, base)) 1463 1464 uint32_t offset() const { return offset_; } 1465 1466 AliasSet getAliasSet() const override { return AliasSet::None(); } 1467 1468 HashNumber valueHash() const override { 1469 HashNumber hash = MUnaryInstruction::valueHash(); 1470 hash = addU32ToHash(hash, offset()); 1471 return hash; 1472 } 1473 bool congruentTo(const MDefinition* ins) const override { 1474 return congruentIfOperandsEqual(ins) && 1475 ins->toWasmDerivedPointer()->offset() == offset(); 1476 } 1477 1478 #ifdef JS_JITSPEW 1479 void getExtras(ExtrasCollector* extras) const override { 1480 char buf[64]; 1481 SprintfLiteral(buf, "(offs=%lld)", (long long int)offset_); 1482 extras->add(buf); 1483 } 1484 #endif 1485 1486 ALLOW_CLONE(MWasmDerivedPointer) 1487 }; 1488 1489 // As with MWasmDerivedPointer, DO NOT use this with a base value of any 1490 // JS-heap-resident object type. 1491 class MWasmDerivedIndexPointer : public MBinaryInstruction, 1492 public NoTypePolicy::Data { 1493 MWasmDerivedIndexPointer(MDefinition* base, MDefinition* index, Scale scale) 1494 : MBinaryInstruction(classOpcode, base, index), scale_(scale) { 1495 // Do not change this to allow `base` to be a GC-heap allocated type. 1496 MOZ_ASSERT(base->type() == MIRType::Pointer); 1497 setResultType(MIRType::Pointer); 1498 setMovable(); 1499 } 1500 1501 Scale scale_; 1502 1503 public: 1504 INSTRUCTION_HEADER(WasmDerivedIndexPointer) 1505 TRIVIAL_NEW_WRAPPERS 1506 NAMED_OPERANDS((0, base)) 1507 NAMED_OPERANDS((1, index)) 1508 1509 Scale scale() const { return scale_; } 1510 1511 AliasSet getAliasSet() const override { return AliasSet::None(); } 1512 1513 bool congruentTo(const MDefinition* ins) const override { 1514 return congruentIfOperandsEqual(ins) && 1515 ins->toWasmDerivedIndexPointer()->scale() == scale(); 1516 } 1517 1518 ALLOW_CLONE(MWasmDerivedIndexPointer) 1519 }; 1520 1521 // Whether to perform a pre-write barrier for a wasm store reference. 1522 enum class WasmPreBarrierKind : uint8_t { None, Normal }; 1523 1524 // Whether to perform a post-write barrier for a wasm store reference. 1525 enum class WasmPostBarrierKind : uint8_t { None, Edge, WholeCell }; 1526 1527 // Stores a reference to an address. This performs a pre-barrier on the address, 1528 // but not a post-barrier. A post-barrier must be performed separately, if it's 1529 // required. The accessed location is `valueBase + valueOffset`. The latter 1530 // must be be representable as a 31-bit unsigned integer. 1531 1532 class MWasmStoreRef : public MAryInstruction<3>, public NoTypePolicy::Data { 1533 uint32_t offset_; 1534 AliasSet::Flag aliasSet_; 1535 WasmPreBarrierKind preBarrierKind_; 1536 1537 MWasmStoreRef(MDefinition* instance, MDefinition* valueBase, 1538 size_t valueOffset, MDefinition* value, AliasSet::Flag aliasSet, 1539 WasmPreBarrierKind preBarrierKind) 1540 : MAryInstruction<3>(classOpcode), 1541 offset_(uint32_t(valueOffset)), 1542 aliasSet_(aliasSet), 1543 preBarrierKind_(preBarrierKind) { 1544 MOZ_ASSERT(valueOffset <= INT32_MAX); 1545 MOZ_ASSERT(valueBase->type() == MIRType::Pointer || 1546 valueBase->type() == MIRType::StackResults); 1547 MOZ_ASSERT(value->type() == MIRType::WasmAnyRef); 1548 initOperand(0, instance); 1549 initOperand(1, valueBase); 1550 initOperand(2, value); 1551 } 1552 1553 public: 1554 INSTRUCTION_HEADER(WasmStoreRef) 1555 TRIVIAL_NEW_WRAPPERS 1556 NAMED_OPERANDS((0, instance), (1, valueBase), (2, value)) 1557 1558 uint32_t offset() const { return offset_; } 1559 AliasSet getAliasSet() const override { return AliasSet::Store(aliasSet_); } 1560 WasmPreBarrierKind preBarrierKind() const { return preBarrierKind_; } 1561 1562 #ifdef JS_JITSPEW 1563 void getExtras(ExtrasCollector* extras) const override { 1564 char buf[64]; 1565 SprintfLiteral(buf, "(offs=%lld)", (long long int)offset_); 1566 extras->add(buf); 1567 } 1568 #endif 1569 }; 1570 1571 // Given a value being written to another object, update the generational store 1572 // buffer if the value is in the nursery and object is in the tenured heap. 1573 class MWasmPostWriteBarrierWholeCell : public MTernaryInstruction, 1574 public NoTypePolicy::Data { 1575 MWasmPostWriteBarrierWholeCell(MDefinition* instance, MDefinition* object, 1576 MDefinition* value) 1577 : MTernaryInstruction(classOpcode, instance, object, value) { 1578 MOZ_ASSERT(object->type() == MIRType::WasmAnyRef); 1579 MOZ_ASSERT(value->type() == MIRType::WasmAnyRef); 1580 setGuard(); 1581 } 1582 1583 public: 1584 INSTRUCTION_HEADER(WasmPostWriteBarrierWholeCell) 1585 TRIVIAL_NEW_WRAPPERS 1586 NAMED_OPERANDS((0, instance), (1, object), (2, value)) 1587 1588 AliasSet getAliasSet() const override { return AliasSet::None(); } 1589 1590 ALLOW_CLONE(MWasmPostWriteBarrierWholeCell) 1591 }; 1592 1593 // Given a value being written to another object, update the generational store 1594 // buffer if the value is in the nursery and object is in the tenured heap. 1595 class MWasmPostWriteBarrierEdgeAtIndex : public MAryInstruction<5>, 1596 public NoTypePolicy::Data { 1597 uint32_t elemSize_; 1598 1599 MWasmPostWriteBarrierEdgeAtIndex(MDefinition* instance, MDefinition* object, 1600 MDefinition* valueBase, MDefinition* index, 1601 uint32_t scale, MDefinition* value) 1602 : MAryInstruction<5>(classOpcode), elemSize_(scale) { 1603 MOZ_ASSERT(object->type() == MIRType::WasmAnyRef); 1604 MOZ_ASSERT(value->type() == MIRType::WasmAnyRef); 1605 initOperand(0, instance); 1606 initOperand(1, object); 1607 initOperand(2, valueBase); 1608 initOperand(3, index); 1609 initOperand(4, value); 1610 setGuard(); 1611 } 1612 1613 public: 1614 INSTRUCTION_HEADER(WasmPostWriteBarrierEdgeAtIndex) 1615 TRIVIAL_NEW_WRAPPERS 1616 NAMED_OPERANDS((0, instance), (1, object), (2, valueBase), (3, index), 1617 (4, value)) 1618 1619 AliasSet getAliasSet() const override { return AliasSet::None(); } 1620 uint32_t elemSize() const { return elemSize_; } 1621 1622 ALLOW_CLONE(MWasmPostWriteBarrierEdgeAtIndex) 1623 }; 1624 1625 class MWasmParameter : public MNullaryInstruction { 1626 ABIArg abi_; 1627 1628 MWasmParameter(ABIArg abi, MIRType mirType, 1629 wasm::MaybeRefType refType = wasm::MaybeRefType()) 1630 : MNullaryInstruction(classOpcode), abi_(abi) { 1631 setResultType(mirType); 1632 initWasmRefType(refType); 1633 } 1634 1635 public: 1636 INSTRUCTION_HEADER(WasmParameter) 1637 TRIVIAL_NEW_WRAPPERS 1638 // MWasmParameter has no getAliasSet routine. Hence it acquires the default 1639 // aliases-everything setting. This doesn't matter in practice because these 1640 // nodes only appear at the start of the function's entry block, and in any 1641 // case they are not marked as movable. 1642 1643 ABIArg abi() const { return abi_; } 1644 }; 1645 1646 class MWasmReturn : public MAryControlInstruction<2, 0>, 1647 public NoTypePolicy::Data { 1648 MWasmReturn(MDefinition* ins, MDefinition* instance) 1649 : MAryControlInstruction(classOpcode) { 1650 initOperand(0, ins); 1651 initOperand(1, instance); 1652 } 1653 1654 public: 1655 INSTRUCTION_HEADER(WasmReturn) 1656 TRIVIAL_NEW_WRAPPERS 1657 1658 AliasSet getAliasSet() const override { return AliasSet::None(); } 1659 }; 1660 1661 class MWasmReturnVoid : public MAryControlInstruction<1, 0>, 1662 public NoTypePolicy::Data { 1663 explicit MWasmReturnVoid(MDefinition* instance) 1664 : MAryControlInstruction(classOpcode) { 1665 initOperand(0, instance); 1666 } 1667 1668 public: 1669 INSTRUCTION_HEADER(WasmReturnVoid) 1670 TRIVIAL_NEW_WRAPPERS 1671 1672 AliasSet getAliasSet() const override { return AliasSet::None(); } 1673 }; 1674 1675 class MWasmStackArg : public MUnaryInstruction, public NoTypePolicy::Data { 1676 MWasmStackArg(uint32_t spOffset, MDefinition* ins) 1677 : MUnaryInstruction(classOpcode, ins), spOffset_(spOffset) {} 1678 1679 uint32_t spOffset_; 1680 1681 public: 1682 INSTRUCTION_HEADER(WasmStackArg) 1683 TRIVIAL_NEW_WRAPPERS 1684 NAMED_OPERANDS((0, arg)) 1685 1686 uint32_t spOffset() const { return spOffset_; } 1687 void incrementOffset(uint32_t inc) { spOffset_ += inc; } 1688 AliasSet getAliasSet() const override { 1689 return AliasSet::Store(AliasSet::Flag::Any); 1690 } 1691 1692 ALLOW_CLONE(MWasmStackArg) 1693 }; 1694 1695 template <typename Location> 1696 class MWasmResultBase : public MNullaryInstruction { 1697 Location loc_; 1698 1699 protected: 1700 MWasmResultBase(Opcode op, MIRType type, Location loc) 1701 : MNullaryInstruction(op), loc_(loc) { 1702 setResultType(type); 1703 setCallResultCapture(); 1704 } 1705 1706 public: 1707 Location loc() { return loc_; } 1708 }; 1709 1710 class MWasmRegisterResult : public MWasmResultBase<Register> { 1711 MWasmRegisterResult(MIRType type, Register reg, 1712 wasm::MaybeRefType maybeRefType = wasm::MaybeRefType()) 1713 : MWasmResultBase(classOpcode, type, reg) { 1714 initWasmRefType(maybeRefType); 1715 } 1716 1717 public: 1718 INSTRUCTION_HEADER(WasmRegisterResult) 1719 TRIVIAL_NEW_WRAPPERS 1720 AliasSet getAliasSet() const override { return AliasSet::None(); } 1721 }; 1722 1723 class MWasmFloatRegisterResult : public MWasmResultBase<FloatRegister> { 1724 MWasmFloatRegisterResult(MIRType type, FloatRegister reg) 1725 : MWasmResultBase(classOpcode, type, reg) {} 1726 1727 public: 1728 INSTRUCTION_HEADER(WasmFloatRegisterResult) 1729 TRIVIAL_NEW_WRAPPERS 1730 AliasSet getAliasSet() const override { return AliasSet::None(); } 1731 }; 1732 1733 class MWasmSystemFloatRegisterResult : public MWasmResultBase<FloatRegister> { 1734 MWasmSystemFloatRegisterResult(MIRType type, FloatRegister reg, bool hardFP) 1735 : MWasmResultBase(classOpcode, type, reg), hardFP_(hardFP) {} 1736 1737 bool hardFP_; 1738 1739 public: 1740 INSTRUCTION_HEADER(WasmSystemFloatRegisterResult) 1741 TRIVIAL_NEW_WRAPPERS 1742 AliasSet getAliasSet() const override { return AliasSet::None(); } 1743 1744 bool hardFP() const { return hardFP_; } 1745 }; 1746 1747 class MWasmRegister64Result : public MWasmResultBase<Register64> { 1748 explicit MWasmRegister64Result(Register64 reg) 1749 : MWasmResultBase(classOpcode, MIRType::Int64, reg) {} 1750 1751 public: 1752 INSTRUCTION_HEADER(WasmRegister64Result) 1753 TRIVIAL_NEW_WRAPPERS 1754 AliasSet getAliasSet() const override { return AliasSet::None(); } 1755 }; 1756 1757 class MWasmStackResultArea : public MNullaryInstruction { 1758 public: 1759 class StackResult { 1760 // Offset in bytes from lowest address of stack result area. 1761 uint32_t offset_; 1762 MIRType type_; 1763 1764 public: 1765 StackResult() : type_(MIRType::Undefined) {} 1766 StackResult(uint32_t offset, MIRType type) : offset_(offset), type_(type) {} 1767 1768 bool initialized() const { return type_ != MIRType::Undefined; } 1769 uint32_t offset() const { 1770 MOZ_ASSERT(initialized()); 1771 return offset_; 1772 } 1773 MIRType type() const { 1774 MOZ_ASSERT(initialized()); 1775 return type_; 1776 } 1777 uint32_t endOffset() const { 1778 return offset() + wasm::MIRTypeToABIResultSize(type()); 1779 } 1780 }; 1781 1782 private: 1783 FixedList<StackResult> results_; 1784 uint32_t base_; 1785 1786 explicit MWasmStackResultArea() 1787 : MNullaryInstruction(classOpcode), base_(UINT32_MAX) { 1788 setResultType(MIRType::StackResults); 1789 } 1790 1791 void assertInitialized() const { 1792 MOZ_ASSERT(results_.length() != 0); 1793 #ifdef DEBUG 1794 for (size_t i = 0; i < results_.length(); i++) { 1795 MOZ_ASSERT(results_[i].initialized()); 1796 } 1797 #endif 1798 } 1799 1800 bool baseInitialized() const { return base_ != UINT32_MAX; } 1801 1802 public: 1803 INSTRUCTION_HEADER(WasmStackResultArea) 1804 TRIVIAL_NEW_WRAPPERS 1805 1806 [[nodiscard]] bool init(TempAllocator& alloc, size_t stackResultCount) { 1807 MOZ_ASSERT(results_.length() == 0); 1808 MOZ_ASSERT(stackResultCount > 0); 1809 if (!results_.init(alloc, stackResultCount)) { 1810 return false; 1811 } 1812 for (size_t n = 0; n < stackResultCount; n++) { 1813 results_[n] = StackResult(); 1814 } 1815 return true; 1816 } 1817 1818 size_t resultCount() const { return results_.length(); } 1819 const StackResult& result(size_t n) const { 1820 MOZ_ASSERT(results_[n].initialized()); 1821 return results_[n]; 1822 } 1823 void initResult(size_t n, const StackResult& loc) { 1824 MOZ_ASSERT(!results_[n].initialized()); 1825 MOZ_ASSERT((n == 0) == (loc.offset() == 0)); 1826 MOZ_ASSERT_IF(n > 0, loc.offset() >= result(n - 1).endOffset()); 1827 results_[n] = loc; 1828 } 1829 1830 uint32_t byteSize() const { 1831 assertInitialized(); 1832 return result(resultCount() - 1).endOffset(); 1833 } 1834 1835 // Stack index indicating base of stack area. 1836 uint32_t base() const { 1837 MOZ_ASSERT(baseInitialized()); 1838 return base_; 1839 } 1840 void setBase(uint32_t base) { 1841 MOZ_ASSERT(!baseInitialized()); 1842 base_ = base; 1843 MOZ_ASSERT(baseInitialized()); 1844 } 1845 }; 1846 1847 class MWasmStackResult : public MUnaryInstruction, public NoTypePolicy::Data { 1848 uint32_t resultIdx_; 1849 1850 MWasmStackResult(MWasmStackResultArea* resultArea, size_t idx) 1851 : MUnaryInstruction(classOpcode, resultArea), resultIdx_(idx) { 1852 setResultType(result().type()); 1853 setCallResultCapture(); 1854 } 1855 1856 public: 1857 INSTRUCTION_HEADER(WasmStackResult) 1858 TRIVIAL_NEW_WRAPPERS 1859 NAMED_OPERANDS((0, resultArea)) 1860 1861 const MWasmStackResultArea::StackResult& result() const { 1862 return resultArea()->toWasmStackResultArea()->result(resultIdx_); 1863 } 1864 }; 1865 1866 // Mixin class for wasm calls that may or may not be catchable. 1867 class MWasmCallBase { 1868 public: 1869 struct Arg { 1870 AnyRegister reg; 1871 MDefinition* def; 1872 Arg(AnyRegister reg, MDefinition* def) : reg(reg), def(def) {} 1873 }; 1874 using Args = Vector<Arg, 8, SystemAllocPolicy>; 1875 1876 protected: 1877 wasm::CallSiteDesc desc_; 1878 wasm::CalleeDesc callee_; 1879 wasm::FailureMode builtinMethodFailureMode_; 1880 wasm::Trap builtinMethodFailureTrap_; 1881 FixedList<AnyRegister> argRegs_; 1882 uint32_t stackArgAreaSizeUnaligned_; 1883 ABIArg instanceArg_; 1884 bool inTry_; 1885 size_t tryNoteIndex_; 1886 1887 MWasmCallBase(const wasm::CallSiteDesc& desc, const wasm::CalleeDesc& callee, 1888 uint32_t stackArgAreaSizeUnaligned, bool inTry, 1889 size_t tryNoteIndex) 1890 : desc_(desc), 1891 callee_(callee), 1892 builtinMethodFailureMode_(wasm::FailureMode::Infallible), 1893 stackArgAreaSizeUnaligned_(stackArgAreaSizeUnaligned), 1894 inTry_(inTry), 1895 tryNoteIndex_(tryNoteIndex) {} 1896 1897 template <class MVariadicT> 1898 [[nodiscard]] bool initWithArgs(TempAllocator& alloc, MVariadicT* ins, 1899 const Args& args, 1900 MDefinition* tableAddressOrRef) { 1901 if (!argRegs_.init(alloc, args.length())) { 1902 return false; 1903 } 1904 for (size_t i = 0; i < argRegs_.length(); i++) { 1905 argRegs_[i] = args[i].reg; 1906 } 1907 1908 if (!ins->init(alloc, argRegs_.length() + (tableAddressOrRef ? 1 : 0))) { 1909 return false; 1910 } 1911 // FixedList doesn't initialize its elements, so do an unchecked init. 1912 for (size_t i = 0; i < argRegs_.length(); i++) { 1913 ins->initOperand(i, args[i].def); 1914 } 1915 if (tableAddressOrRef) { 1916 ins->initOperand(argRegs_.length(), tableAddressOrRef); 1917 } 1918 return true; 1919 } 1920 1921 public: 1922 static bool IsWasmCall(MDefinition* def) { 1923 return def->isWasmCallCatchable() || def->isWasmCallUncatchable() || 1924 def->isWasmReturnCall(); 1925 } 1926 1927 size_t numArgs() const { return argRegs_.length(); } 1928 AnyRegister registerForArg(size_t index) const { 1929 MOZ_ASSERT(index < numArgs()); 1930 return argRegs_[index]; 1931 } 1932 const wasm::CallSiteDesc& desc() const { return desc_; } 1933 const wasm::CalleeDesc& callee() const { return callee_; } 1934 wasm::FailureMode builtinMethodFailureMode() const { 1935 MOZ_ASSERT(callee_.which() == wasm::CalleeDesc::BuiltinInstanceMethod); 1936 return builtinMethodFailureMode_; 1937 } 1938 wasm::Trap builtinMethodFailureTrap() const { 1939 MOZ_ASSERT(callee_.which() == wasm::CalleeDesc::BuiltinInstanceMethod); 1940 return builtinMethodFailureTrap_; 1941 } 1942 uint32_t stackArgAreaSizeUnaligned() const { 1943 return stackArgAreaSizeUnaligned_; 1944 } 1945 1946 const ABIArg& instanceArg() const { return instanceArg_; } 1947 1948 bool inTry() const { return inTry_; } 1949 size_t tryNoteIndex() const { return tryNoteIndex_; } 1950 1951 static AliasSet wasmCallAliasSet() { 1952 // This is ok because: 1953 // - numElements is immutable 1954 // - the GC will rewrite any array data pointers on move 1955 AliasSet exclude = AliasSet(AliasSet::WasmArrayNumElements) | 1956 AliasSet(AliasSet::WasmArrayDataPointer); 1957 return AliasSet::Store(AliasSet::Any) & ~exclude; 1958 } 1959 }; 1960 1961 // A wasm call that is catchable. This instruction is a control instruction, 1962 // and terminates the block it is on. A normal return will proceed in a the 1963 // fallthrough block. An exceptional return will unwind into the landing pad 1964 // block for this call. The landing pad block must begin with an 1965 // MWasmCallLandingPrePad. 1966 class MWasmCallCatchable final : public MVariadicControlInstruction<2>, 1967 public MWasmCallBase, 1968 public NoTypePolicy::Data { 1969 MWasmCallCatchable(const wasm::CallSiteDesc& desc, 1970 const wasm::CalleeDesc& callee, 1971 uint32_t stackArgAreaSizeUnaligned, size_t tryNoteIndex) 1972 : MVariadicControlInstruction(classOpcode), 1973 MWasmCallBase(desc, callee, stackArgAreaSizeUnaligned, true, 1974 tryNoteIndex) {} 1975 1976 public: 1977 INSTRUCTION_HEADER(WasmCallCatchable) 1978 1979 static MWasmCallCatchable* New( 1980 TempAllocator& alloc, const wasm::CallSiteDesc& desc, 1981 const wasm::CalleeDesc& callee, const Args& args, 1982 uint32_t stackArgAreaSizeUnaligned, uint32_t tryNoteIndex, 1983 MBasicBlock* fallthroughBlock, MBasicBlock* prePadBlock, 1984 MDefinition* tableAddressOrRef = nullptr); 1985 1986 static MWasmCallCatchable* NewBuiltinInstanceMethodCall( 1987 TempAllocator& alloc, const wasm::CallSiteDesc& desc, 1988 const wasm::SymbolicAddress builtin, wasm::FailureMode failureMode, 1989 wasm::Trap failureTrap, const ABIArg& instanceArg, const Args& args, 1990 uint32_t stackArgAreaSizeUnaligned, uint32_t tryNoteIndex, 1991 MBasicBlock* fallthroughBlock, MBasicBlock* prePadBlock); 1992 1993 bool possiblyCalls() const override { return true; } 1994 AliasSet getAliasSet() const override { return wasmCallAliasSet(); } 1995 1996 static const size_t FallthroughBranchIndex = 0; 1997 static const size_t PrePadBranchIndex = 1; 1998 }; 1999 2000 // A wasm call that is not catchable. This instruction is not a control 2001 // instruction, and therefore is not a block terminator. 2002 class MWasmCallUncatchable final : public MVariadicInstruction, 2003 public MWasmCallBase, 2004 public NoTypePolicy::Data { 2005 MWasmCallUncatchable(const wasm::CallSiteDesc& desc, 2006 const wasm::CalleeDesc& callee, 2007 uint32_t stackArgAreaSizeUnaligned) 2008 : MVariadicInstruction(classOpcode), 2009 MWasmCallBase(desc, callee, stackArgAreaSizeUnaligned, false, 0) {} 2010 2011 public: 2012 INSTRUCTION_HEADER(WasmCallUncatchable) 2013 2014 static MWasmCallUncatchable* New(TempAllocator& alloc, 2015 const wasm::CallSiteDesc& desc, 2016 const wasm::CalleeDesc& callee, 2017 const Args& args, 2018 uint32_t stackArgAreaSizeUnaligned, 2019 MDefinition* tableAddressOrRef = nullptr); 2020 2021 static MWasmCallUncatchable* NewBuiltinInstanceMethodCall( 2022 TempAllocator& alloc, const wasm::CallSiteDesc& desc, 2023 const wasm::SymbolicAddress builtin, wasm::FailureMode failureMode, 2024 wasm::Trap failureTrap, const ABIArg& instanceArg, const Args& args, 2025 uint32_t stackArgAreaSizeUnaligned); 2026 2027 bool possiblyCalls() const override { return true; } 2028 AliasSet getAliasSet() const override { return wasmCallAliasSet(); } 2029 }; 2030 2031 class MWasmReturnCall final : public MVariadicControlInstruction<0>, 2032 public MWasmCallBase, 2033 public NoTypePolicy::Data { 2034 MWasmReturnCall(const wasm::CallSiteDesc& desc, 2035 const wasm::CalleeDesc& callee, 2036 uint32_t stackArgAreaSizeUnaligned) 2037 : MVariadicControlInstruction(classOpcode), 2038 MWasmCallBase(desc, callee, stackArgAreaSizeUnaligned, false, 0) {} 2039 2040 public: 2041 INSTRUCTION_HEADER(WasmReturnCall) 2042 2043 static MWasmReturnCall* New(TempAllocator& alloc, 2044 const wasm::CallSiteDesc& desc, 2045 const wasm::CalleeDesc& callee, const Args& args, 2046 uint32_t stackArgAreaSizeUnaligned, 2047 MDefinition* tableAddressOrRef = nullptr); 2048 2049 bool possiblyCalls() const override { return true; } 2050 }; 2051 2052 // A marker instruction for a block which is the landing pad for a catchable 2053 // wasm call. This instruction does not emit any code, only filling in 2054 // metadata. This instruction must be the first instruction added to the 2055 // landing pad block. 2056 class MWasmCallLandingPrePad : public MNullaryInstruction { 2057 // The block of the call that may unwind to this landing pad. 2058 MBasicBlock* callBlock_; 2059 // The index of the try note to initialize a landing pad for. 2060 size_t tryNoteIndex_; 2061 2062 explicit MWasmCallLandingPrePad(MBasicBlock* callBlock, size_t tryNoteIndex) 2063 : MNullaryInstruction(classOpcode), 2064 callBlock_(callBlock), 2065 tryNoteIndex_(tryNoteIndex) { 2066 setGuard(); 2067 } 2068 2069 public: 2070 INSTRUCTION_HEADER(WasmCallLandingPrePad) 2071 TRIVIAL_NEW_WRAPPERS 2072 2073 AliasSet getAliasSet() const override { return AliasSet::None(); } 2074 2075 size_t tryNoteIndex() { return tryNoteIndex_; } 2076 MBasicBlock* callBlock() { return callBlock_; } 2077 }; 2078 2079 class MWasmSelect : public MTernaryInstruction, public NoTypePolicy::Data { 2080 MWasmSelect(MDefinition* trueExpr, MDefinition* falseExpr, 2081 MDefinition* condExpr) 2082 : MTernaryInstruction(classOpcode, trueExpr, falseExpr, condExpr) { 2083 MOZ_ASSERT(condExpr->type() == MIRType::Int32); 2084 MOZ_ASSERT(trueExpr->type() == falseExpr->type()); 2085 setResultType(trueExpr->type()); 2086 setMovable(); 2087 } 2088 2089 public: 2090 INSTRUCTION_HEADER(WasmSelect) 2091 TRIVIAL_NEW_WRAPPERS 2092 NAMED_OPERANDS((0, trueExpr), (1, falseExpr), (2, condExpr)) 2093 2094 AliasSet getAliasSet() const override { return AliasSet::None(); } 2095 2096 bool congruentTo(const MDefinition* ins) const override { 2097 return congruentIfOperandsEqual(ins); 2098 } 2099 2100 wasm::MaybeRefType computeWasmRefType() const override { 2101 return wasm::MaybeRefType::leastUpperBound(trueExpr()->wasmRefType(), 2102 falseExpr()->wasmRefType()); 2103 } 2104 2105 MDefinition* foldsTo(TempAllocator& alloc) override; 2106 2107 ALLOW_CLONE(MWasmSelect) 2108 }; 2109 2110 // Wasm SIMD. 2111 // 2112 // See comment in WasmIonCompile.cpp for a justification for these nodes. 2113 2114 // (v128, v128, v128) -> v128 effect-free operation. 2115 class MWasmTernarySimd128 : public MTernaryInstruction, 2116 public NoTypePolicy::Data { 2117 wasm::SimdOp simdOp_; 2118 2119 MWasmTernarySimd128(MDefinition* v0, MDefinition* v1, MDefinition* v2, 2120 wasm::SimdOp simdOp) 2121 : MTernaryInstruction(classOpcode, v0, v1, v2), simdOp_(simdOp) { 2122 setMovable(); 2123 setResultType(MIRType::Simd128); 2124 } 2125 2126 public: 2127 INSTRUCTION_HEADER(WasmTernarySimd128) 2128 TRIVIAL_NEW_WRAPPERS 2129 NAMED_OPERANDS((0, v0), (1, v1), (2, v2)) 2130 2131 AliasSet getAliasSet() const override { return AliasSet::None(); } 2132 bool congruentTo(const MDefinition* ins) const override { 2133 return congruentIfOperandsEqual(ins) && 2134 simdOp() == ins->toWasmTernarySimd128()->simdOp(); 2135 } 2136 #ifdef ENABLE_WASM_SIMD 2137 MDefinition* foldsTo(TempAllocator& alloc) override; 2138 2139 // If the control mask of a bitselect allows the operation to be specialized 2140 // as a shuffle and it is profitable to specialize it on this platform, return 2141 // true and the appropriate shuffle mask. 2142 bool specializeBitselectConstantMaskAsShuffle(int8_t shuffle[16]); 2143 // Checks if more relaxed version of lane select can be used. It returns true 2144 // if a bit mask input expected to be all 0s or 1s for entire 8-bit lanes, 2145 // false otherwise. 2146 bool canRelaxBitselect(); 2147 #endif 2148 2149 wasm::SimdOp simdOp() const { return simdOp_; } 2150 2151 ALLOW_CLONE(MWasmTernarySimd128) 2152 }; 2153 2154 // (v128, v128) -> v128 effect-free operations. 2155 class MWasmBinarySimd128 : public MBinaryInstruction, 2156 public NoTypePolicy::Data { 2157 wasm::SimdOp simdOp_; 2158 2159 MWasmBinarySimd128(MDefinition* lhs, MDefinition* rhs, bool commutative, 2160 wasm::SimdOp simdOp) 2161 : MBinaryInstruction(classOpcode, lhs, rhs), simdOp_(simdOp) { 2162 setMovable(); 2163 setResultType(MIRType::Simd128); 2164 if (commutative) { 2165 setCommutative(); 2166 } 2167 } 2168 2169 public: 2170 INSTRUCTION_HEADER(WasmBinarySimd128) 2171 TRIVIAL_NEW_WRAPPERS 2172 2173 AliasSet getAliasSet() const override { return AliasSet::None(); } 2174 bool congruentTo(const MDefinition* ins) const override { 2175 return congruentIfOperandsEqual(ins) && 2176 ins->toWasmBinarySimd128()->simdOp() == simdOp_; 2177 } 2178 #ifdef ENABLE_WASM_SIMD 2179 MDefinition* foldsTo(TempAllocator& alloc) override; 2180 2181 // Checks if pmaddubsw operation is supported. 2182 bool canPmaddubsw(); 2183 #endif 2184 2185 wasm::SimdOp simdOp() const { return simdOp_; } 2186 2187 // Platform-dependent specialization. 2188 bool specializeForConstantRhs(); 2189 2190 ALLOW_CLONE(MWasmBinarySimd128) 2191 }; 2192 2193 // (v128, const) -> v128 effect-free operations. 2194 class MWasmBinarySimd128WithConstant : public MUnaryInstruction, 2195 public NoTypePolicy::Data { 2196 SimdConstant rhs_; 2197 wasm::SimdOp simdOp_; 2198 2199 MWasmBinarySimd128WithConstant(MDefinition* lhs, const SimdConstant& rhs, 2200 wasm::SimdOp simdOp) 2201 : MUnaryInstruction(classOpcode, lhs), rhs_(rhs), simdOp_(simdOp) { 2202 setMovable(); 2203 setResultType(MIRType::Simd128); 2204 } 2205 2206 public: 2207 INSTRUCTION_HEADER(WasmBinarySimd128WithConstant) 2208 TRIVIAL_NEW_WRAPPERS 2209 2210 AliasSet getAliasSet() const override { return AliasSet::None(); } 2211 bool congruentTo(const MDefinition* ins) const override { 2212 return congruentIfOperandsEqual(ins) && 2213 ins->toWasmBinarySimd128WithConstant()->simdOp() == simdOp_ && 2214 rhs_.bitwiseEqual(ins->toWasmBinarySimd128WithConstant()->rhs()); 2215 } 2216 2217 wasm::SimdOp simdOp() const { return simdOp_; } 2218 MDefinition* lhs() const { return input(); } 2219 const SimdConstant& rhs() const { return rhs_; } 2220 2221 ALLOW_CLONE(MWasmBinarySimd128WithConstant) 2222 }; 2223 2224 // (v128, scalar, imm) -> v128 effect-free operations. 2225 class MWasmReplaceLaneSimd128 : public MBinaryInstruction, 2226 public NoTypePolicy::Data { 2227 uint32_t laneIndex_; 2228 wasm::SimdOp simdOp_; 2229 2230 MWasmReplaceLaneSimd128(MDefinition* lhs, MDefinition* rhs, 2231 uint32_t laneIndex, wasm::SimdOp simdOp) 2232 : MBinaryInstruction(classOpcode, lhs, rhs), 2233 laneIndex_(laneIndex), 2234 simdOp_(simdOp) { 2235 setMovable(); 2236 setResultType(MIRType::Simd128); 2237 } 2238 2239 public: 2240 INSTRUCTION_HEADER(WasmReplaceLaneSimd128) 2241 TRIVIAL_NEW_WRAPPERS 2242 2243 AliasSet getAliasSet() const override { return AliasSet::None(); } 2244 bool congruentTo(const MDefinition* ins) const override { 2245 return congruentIfOperandsEqual(ins) && 2246 ins->toWasmReplaceLaneSimd128()->simdOp() == simdOp_ && 2247 ins->toWasmReplaceLaneSimd128()->laneIndex() == laneIndex_; 2248 } 2249 2250 uint32_t laneIndex() const { return laneIndex_; } 2251 wasm::SimdOp simdOp() const { return simdOp_; } 2252 2253 ALLOW_CLONE(MWasmReplaceLaneSimd128) 2254 }; 2255 2256 // (scalar) -> v128 effect-free operations. 2257 class MWasmScalarToSimd128 : public MUnaryInstruction, 2258 public NoTypePolicy::Data { 2259 wasm::SimdOp simdOp_; 2260 2261 MWasmScalarToSimd128(MDefinition* src, wasm::SimdOp simdOp) 2262 : MUnaryInstruction(classOpcode, src), simdOp_(simdOp) { 2263 setMovable(); 2264 setResultType(MIRType::Simd128); 2265 } 2266 2267 public: 2268 INSTRUCTION_HEADER(WasmScalarToSimd128) 2269 TRIVIAL_NEW_WRAPPERS 2270 2271 AliasSet getAliasSet() const override { return AliasSet::None(); } 2272 bool congruentTo(const MDefinition* ins) const override { 2273 return congruentIfOperandsEqual(ins) && 2274 ins->toWasmScalarToSimd128()->simdOp() == simdOp_; 2275 } 2276 #ifdef ENABLE_WASM_SIMD 2277 MDefinition* foldsTo(TempAllocator& alloc) override; 2278 #endif 2279 2280 wasm::SimdOp simdOp() const { return simdOp_; } 2281 2282 ALLOW_CLONE(MWasmScalarToSimd128) 2283 }; 2284 2285 // (v128, imm) -> scalar effect-free operations. 2286 class MWasmReduceSimd128 : public MUnaryInstruction, public NoTypePolicy::Data { 2287 wasm::SimdOp simdOp_; 2288 uint32_t imm_; 2289 2290 MWasmReduceSimd128(MDefinition* src, wasm::SimdOp simdOp, MIRType outType, 2291 uint32_t imm) 2292 : MUnaryInstruction(classOpcode, src), simdOp_(simdOp), imm_(imm) { 2293 setMovable(); 2294 setResultType(outType); 2295 } 2296 2297 public: 2298 INSTRUCTION_HEADER(WasmReduceSimd128) 2299 TRIVIAL_NEW_WRAPPERS 2300 2301 AliasSet getAliasSet() const override { return AliasSet::None(); } 2302 bool congruentTo(const MDefinition* ins) const override { 2303 return congruentIfOperandsEqual(ins) && 2304 ins->toWasmReduceSimd128()->simdOp() == simdOp_ && 2305 ins->toWasmReduceSimd128()->imm() == imm_; 2306 } 2307 #ifdef ENABLE_WASM_SIMD 2308 MDefinition* foldsTo(TempAllocator& alloc) override; 2309 #endif 2310 2311 uint32_t imm() const { return imm_; } 2312 wasm::SimdOp simdOp() const { return simdOp_; } 2313 2314 ALLOW_CLONE(MWasmReduceSimd128) 2315 }; 2316 2317 class MWasmLoadLaneSimd128 2318 : public MVariadicInstruction, // memoryBase is nullptr on some platforms 2319 public NoTypePolicy::Data { 2320 wasm::MemoryAccessDesc access_; 2321 uint32_t laneSize_; 2322 uint32_t laneIndex_; 2323 uint32_t memoryBaseIndex_; 2324 2325 MWasmLoadLaneSimd128(const wasm::MemoryAccessDesc& access, uint32_t laneSize, 2326 uint32_t laneIndex, uint32_t memoryBaseIndex) 2327 : MVariadicInstruction(classOpcode), 2328 access_(access), 2329 laneSize_(laneSize), 2330 laneIndex_(laneIndex), 2331 memoryBaseIndex_(memoryBaseIndex) { 2332 MOZ_ASSERT(!access_.isAtomic()); 2333 setGuard(); 2334 setResultType(MIRType::Simd128); 2335 } 2336 2337 public: 2338 INSTRUCTION_HEADER(WasmLoadLaneSimd128) 2339 NAMED_OPERANDS((0, base), (1, value)); 2340 2341 static MWasmLoadLaneSimd128* New(TempAllocator& alloc, 2342 MDefinition* memoryBase, MDefinition* base, 2343 const wasm::MemoryAccessDesc& access, 2344 uint32_t laneSize, uint32_t laneIndex, 2345 MDefinition* value) { 2346 uint32_t nextIndex = 2; 2347 uint32_t memoryBaseIndex = memoryBase ? nextIndex++ : UINT32_MAX; 2348 2349 MWasmLoadLaneSimd128* load = new (alloc) 2350 MWasmLoadLaneSimd128(access, laneSize, laneIndex, memoryBaseIndex); 2351 if (!load->init(alloc, nextIndex)) { 2352 return nullptr; 2353 } 2354 2355 load->initOperand(0, base); 2356 load->initOperand(1, value); 2357 if (memoryBase) { 2358 load->initOperand(memoryBaseIndex, memoryBase); 2359 } 2360 2361 return load; 2362 } 2363 2364 const wasm::MemoryAccessDesc& access() const { return access_; } 2365 uint32_t laneSize() const { return laneSize_; } 2366 uint32_t laneIndex() const { return laneIndex_; } 2367 bool hasMemoryBase() const { return memoryBaseIndex_ != UINT32_MAX; } 2368 MDefinition* memoryBase() const { 2369 MOZ_ASSERT(hasMemoryBase()); 2370 return getOperand(memoryBaseIndex_); 2371 } 2372 2373 AliasSet getAliasSet() const override { 2374 return AliasSet::Load(AliasSet::WasmHeap); 2375 } 2376 }; 2377 2378 class MWasmStoreLaneSimd128 : public MVariadicInstruction, 2379 public NoTypePolicy::Data { 2380 wasm::MemoryAccessDesc access_; 2381 uint32_t laneSize_; 2382 uint32_t laneIndex_; 2383 uint32_t memoryBaseIndex_; 2384 2385 explicit MWasmStoreLaneSimd128(const wasm::MemoryAccessDesc& access, 2386 uint32_t laneSize, uint32_t laneIndex, 2387 uint32_t memoryBaseIndex) 2388 : MVariadicInstruction(classOpcode), 2389 access_(access), 2390 laneSize_(laneSize), 2391 laneIndex_(laneIndex), 2392 memoryBaseIndex_(memoryBaseIndex) { 2393 MOZ_ASSERT(!access_.isAtomic()); 2394 setGuard(); 2395 setResultType(MIRType::Simd128); 2396 } 2397 2398 public: 2399 INSTRUCTION_HEADER(WasmStoreLaneSimd128) 2400 NAMED_OPERANDS((0, base), (1, value)) 2401 2402 static MWasmStoreLaneSimd128* New(TempAllocator& alloc, 2403 MDefinition* memoryBase, MDefinition* base, 2404 const wasm::MemoryAccessDesc& access, 2405 uint32_t laneSize, uint32_t laneIndex, 2406 MDefinition* value) { 2407 uint32_t nextIndex = 2; 2408 uint32_t memoryBaseIndex = memoryBase ? nextIndex++ : UINT32_MAX; 2409 2410 MWasmStoreLaneSimd128* store = new (alloc) 2411 MWasmStoreLaneSimd128(access, laneSize, laneIndex, memoryBaseIndex); 2412 if (!store->init(alloc, nextIndex)) { 2413 return nullptr; 2414 } 2415 2416 store->initOperand(0, base); 2417 store->initOperand(1, value); 2418 if (memoryBase) { 2419 store->initOperand(memoryBaseIndex, memoryBase); 2420 } 2421 2422 return store; 2423 } 2424 2425 const wasm::MemoryAccessDesc& access() const { return access_; } 2426 uint32_t laneSize() const { return laneSize_; } 2427 uint32_t laneIndex() const { return laneIndex_; } 2428 bool hasMemoryBase() const { return memoryBaseIndex_ != UINT32_MAX; } 2429 MDefinition* memoryBase() const { 2430 MOZ_ASSERT(hasMemoryBase()); 2431 return getOperand(memoryBaseIndex_); 2432 } 2433 2434 AliasSet getAliasSet() const override { 2435 return AliasSet::Store(AliasSet::WasmHeap); 2436 } 2437 }; 2438 2439 // End Wasm SIMD 2440 2441 class MIonToWasmCall final : public MVariadicInstruction, 2442 public NoTypePolicy::Data { 2443 CompilerGCPointer<WasmInstanceObject*> instanceObj_; 2444 const wasm::FuncExport& funcExport_; 2445 2446 MIonToWasmCall(WasmInstanceObject* instanceObj, MIRType resultType, 2447 const wasm::FuncExport& funcExport) 2448 : MVariadicInstruction(classOpcode), 2449 instanceObj_(instanceObj), 2450 funcExport_(funcExport) { 2451 setResultType(resultType); 2452 } 2453 2454 public: 2455 INSTRUCTION_HEADER(IonToWasmCall); 2456 2457 static MIonToWasmCall* New(TempAllocator& alloc, 2458 WasmInstanceObject* instanceObj, 2459 const wasm::FuncExport& funcExport); 2460 2461 void initArg(size_t i, MDefinition* arg) { initOperand(i, arg); } 2462 2463 WasmInstanceObject* instanceObject() const { return instanceObj_; } 2464 wasm::Instance* instance() const { return &instanceObj_->instance(); } 2465 const wasm::FuncExport& funcExport() const { return funcExport_; } 2466 bool possiblyCalls() const override { return true; } 2467 #ifdef DEBUG 2468 bool isConsistentFloat32Use(MUse* use) const override; 2469 #endif 2470 }; 2471 2472 // For accesses to wasm object fields, we need to be able to describe 8- and 2473 // 16-bit accesses. But MIRType can't represent those. Hence these two 2474 // supplemental enums, used for reading and writing fields respectively. 2475 2476 // Indicates how to widen an 8- or 16-bit value (when it is read from memory). 2477 enum class MWideningOp : uint8_t { None, FromU16, FromS16, FromU8, FromS8 }; 2478 2479 #ifdef JS_JITSPEW 2480 static inline const char* StringFromMWideningOp(MWideningOp op) { 2481 switch (op) { 2482 case MWideningOp::None: 2483 return "None"; 2484 case MWideningOp::FromU16: 2485 return "FromU16"; 2486 case MWideningOp::FromS16: 2487 return "FromS16"; 2488 case MWideningOp::FromU8: 2489 return "FromU8"; 2490 case MWideningOp::FromS8: 2491 return "FromS8"; 2492 default: 2493 break; 2494 } 2495 MOZ_CRASH("Unknown MWideningOp"); 2496 } 2497 #endif 2498 2499 // Indicates how to narrow a 32-bit value (when it is written to memory). The 2500 // operation is a simple truncate. 2501 enum class MNarrowingOp : uint8_t { None, To16, To8 }; 2502 2503 #ifdef JS_JITSPEW 2504 static inline const char* StringFromMNarrowingOp(MNarrowingOp op) { 2505 switch (op) { 2506 case MNarrowingOp::None: 2507 return "None"; 2508 case MNarrowingOp::To16: 2509 return "To16"; 2510 case MNarrowingOp::To8: 2511 return "To8"; 2512 default: 2513 break; 2514 } 2515 MOZ_CRASH("Unknown MNarrowingOp"); 2516 } 2517 #endif 2518 2519 // Loads a value from a location, denoted as a fixed offset from a base 2520 // pointer. This field may be any value type, including references. No 2521 // barriers are performed. 2522 // 2523 // This instruction can extend the lifetime of an optional `keepAlive` 2524 // parameter to match the lifetime of this instruction. This is necessary if 2525 // the base pointer is owned by some GC'ed object, which means that the GC 2526 // object must have the same lifetime as all uses of it's owned pointers. 2527 // No code to access the keepAlive value is generated. 2528 // 2529 // `offset` must be representable as a 31-bit unsigned integer. 2530 // 2531 // An optional structFieldIndex can be given for struct accesses and used in 2532 // scalar replacement. 2533 class MWasmLoadField : public MBinaryInstruction, public NoTypePolicy::Data { 2534 uint32_t offset_; 2535 mozilla::Maybe<uint32_t> structFieldIndex_; 2536 MWideningOp wideningOp_; 2537 AliasSet aliases_; 2538 wasm::MaybeTrapSiteDesc maybeTrap_; 2539 mozilla::Maybe<wasm::RefTypeHierarchy> hierarchy_; 2540 2541 MWasmLoadField(MDefinition* base, MDefinition* keepAlive, size_t offset, 2542 mozilla::Maybe<uint32_t> structFieldIndex, MIRType type, 2543 MWideningOp wideningOp, AliasSet aliases, 2544 wasm::MaybeTrapSiteDesc maybeTrap = mozilla::Nothing(), 2545 wasm::MaybeRefType maybeRefType = wasm::MaybeRefType()) 2546 : MBinaryInstruction(classOpcode, base, keepAlive ? keepAlive : base), 2547 offset_(uint32_t(offset)), 2548 structFieldIndex_(structFieldIndex), 2549 wideningOp_(wideningOp), 2550 aliases_(aliases), 2551 maybeTrap_(std::move(maybeTrap)), 2552 hierarchy_(maybeRefType.hierarchy()) { 2553 MOZ_ASSERT(offset <= INT32_MAX); 2554 // "if you want to widen the value when it is loaded, the destination type 2555 // must be Int32". 2556 MOZ_ASSERT_IF(wideningOp != MWideningOp::None, type == MIRType::Int32); 2557 // Check the alias set is one of the expected kinds. 2558 MOZ_ASSERT( 2559 aliases.flags() == 2560 AliasSet::Load(AliasSet::WasmStructOutlineDataPointer).flags() || 2561 aliases.flags() == 2562 AliasSet::Load(AliasSet::WasmStructInlineDataArea).flags() || 2563 aliases.flags() == 2564 AliasSet::Load(AliasSet::WasmStructOutlineDataArea).flags() || 2565 aliases.flags() == 2566 AliasSet::Load(AliasSet::WasmArrayNumElements).flags() || 2567 aliases.flags() == 2568 AliasSet::Load(AliasSet::WasmArrayDataPointer).flags() || 2569 aliases.flags() == 2570 AliasSet::Load(AliasSet::WasmArrayDataArea).flags() || 2571 aliases.flags() == AliasSet::Load(AliasSet::Any).flags()); 2572 // Check the alias set is consistent with the result type. 2573 MOZ_ASSERT( 2574 (aliases.flags() == 2575 AliasSet::Load(AliasSet::WasmStructOutlineDataPointer).flags()) == 2576 (type == MIRType::WasmStructData)); 2577 MOZ_ASSERT((aliases.flags() == 2578 AliasSet::Load(AliasSet::WasmArrayDataPointer).flags()) == 2579 (type == MIRType::WasmArrayData)); 2580 setResultType(type); 2581 if (maybeTrap_) { 2582 // This is safe, but see bug 1992059 for associated details. 2583 setGuard(); 2584 } else { 2585 setMovable(); 2586 } 2587 initWasmRefType(maybeRefType); 2588 } 2589 2590 public: 2591 INSTRUCTION_HEADER(WasmLoadField) 2592 TRIVIAL_NEW_WRAPPERS 2593 NAMED_OPERANDS((0, base), (1, keepAlive)) 2594 2595 uint32_t offset() const { return offset_; } 2596 mozilla::Maybe<uint32_t> structFieldIndex() const { 2597 return structFieldIndex_; 2598 } 2599 MWideningOp wideningOp() const { return wideningOp_; } 2600 AliasSet getAliasSet() const override { return aliases_; } 2601 wasm::MaybeTrapSiteDesc maybeTrap() const { return maybeTrap_; } 2602 mozilla::Maybe<wasm::RefTypeHierarchy> hierarchy() const { 2603 return hierarchy_; 2604 } 2605 2606 bool congruentTo(const MDefinition* ins) const override { 2607 if (!ins->isWasmLoadField()) { 2608 return false; 2609 } 2610 const MWasmLoadField* other = ins->toWasmLoadField(); 2611 return congruentIfOperandsEqual(other) && offset() == other->offset() && 2612 structFieldIndex() == other->structFieldIndex() && 2613 wideningOp() == other->wideningOp() && 2614 getAliasSet().flags() == other->getAliasSet().flags() && 2615 hierarchy() == other->hierarchy(); 2616 } 2617 2618 virtual AliasType mightAlias(const MDefinition* ins) const override; 2619 2620 #ifdef JS_JITSPEW 2621 void getExtras(ExtrasCollector* extras) const override { 2622 char buf[96]; 2623 SprintfLiteral(buf, "(offs=%lld, wideningOp=%s)", (long long int)offset_, 2624 StringFromMWideningOp(wideningOp_)); 2625 extras->add(buf); 2626 } 2627 #endif 2628 2629 ALLOW_CLONE(MWasmLoadField) 2630 }; 2631 2632 // Loads a value from base pointer, given an index and element size. This field 2633 // may be any value type, including references. No barriers are performed. 2634 // 2635 // The element size is implicitly defined by MIRType and MWideningOp. For 2636 // example, MIRType::Float32 indicates an element size of 32 bits, and 2637 // MIRType::Int32 and MWideningOp::FromU16 together indicate an element size of 2638 // 16 bits. 2639 // 2640 // This instruction takes an optional second object `keepAlive` that must be 2641 // kept alive, as described for MWasmLoadField above. 2642 class MWasmLoadElement : public MTernaryInstruction, public NoTypePolicy::Data { 2643 MWideningOp wideningOp_; 2644 Scale scale_; 2645 AliasSet aliases_; 2646 wasm::MaybeTrapSiteDesc maybeTrap_; 2647 2648 MWasmLoadElement(MDefinition* base, MDefinition* keepAlive, 2649 MDefinition* index, MIRType type, MWideningOp wideningOp, 2650 Scale scale, AliasSet aliases, 2651 wasm::MaybeTrapSiteDesc maybeTrap = mozilla::Nothing(), 2652 wasm::MaybeRefType maybeRefType = wasm::MaybeRefType()) 2653 : MTernaryInstruction(classOpcode, base, index, 2654 keepAlive ? keepAlive : base), 2655 wideningOp_(wideningOp), 2656 scale_(scale), 2657 aliases_(aliases), 2658 maybeTrap_(std::move(maybeTrap)) { 2659 MOZ_ASSERT(base->type() == MIRType::WasmArrayData); 2660 MOZ_ASSERT(aliases.flags() == 2661 AliasSet::Load(AliasSet::WasmArrayDataArea).flags() || 2662 aliases.flags() == AliasSet::Load(AliasSet::Any).flags()); 2663 setResultType(type); 2664 if (maybeTrap_) { 2665 setGuard(); 2666 } 2667 initWasmRefType(maybeRefType); 2668 } 2669 2670 public: 2671 INSTRUCTION_HEADER(WasmLoadElement) 2672 TRIVIAL_NEW_WRAPPERS 2673 NAMED_OPERANDS((0, base), (1, index), (2, keepAlive)) 2674 2675 MWideningOp wideningOp() const { return wideningOp_; } 2676 Scale scale() const { return scale_; } 2677 AliasSet getAliasSet() const override { return aliases_; } 2678 wasm::MaybeTrapSiteDesc maybeTrap() const { return maybeTrap_; } 2679 2680 #ifdef JS_JITSPEW 2681 void getExtras(ExtrasCollector* extras) const override { 2682 char buf[96]; 2683 SprintfLiteral(buf, "(wideningOp=%s, scale=%s)", 2684 StringFromMWideningOp(wideningOp_), StringFromScale(scale_)); 2685 extras->add(buf); 2686 } 2687 #endif 2688 2689 ALLOW_CLONE(MWasmLoadElement) 2690 }; 2691 2692 // Stores a non-reference value to anlocation, denoted as a fixed offset from 2693 // a base pointer, which (it is assumed) is within a wasm object. This field 2694 // may be any value type, _excluding_ references. References _must_ use the 2695 // 'Ref' variant of this instruction. The offset must be representable as a 2696 // 31-bit unsigned integer. 2697 // 2698 // This instruction takes a second object `keepAlive` that must be kept alive, 2699 // as described for MWasmLoadField above. 2700 class MWasmStoreField : public MTernaryInstruction, public NoTypePolicy::Data { 2701 uint32_t offset_; 2702 mozilla::Maybe<uint32_t> structFieldIndex_; 2703 MNarrowingOp narrowingOp_; 2704 AliasSet aliases_; 2705 wasm::MaybeTrapSiteDesc maybeTrap_; 2706 2707 MWasmStoreField(MDefinition* base, MDefinition* keepAlive, size_t offset, 2708 mozilla::Maybe<uint32_t> structFieldIndex, MDefinition* value, 2709 MNarrowingOp narrowingOp, AliasSet aliases, 2710 wasm::MaybeTrapSiteDesc maybeTrap = mozilla::Nothing()) 2711 : MTernaryInstruction(classOpcode, base, value, 2712 keepAlive ? keepAlive : base), 2713 offset_(uint32_t(offset)), 2714 structFieldIndex_(structFieldIndex), 2715 narrowingOp_(narrowingOp), 2716 aliases_(aliases), 2717 maybeTrap_(std::move(maybeTrap)) { 2718 MOZ_ASSERT(offset <= INT32_MAX); 2719 MOZ_ASSERT(value->type() != MIRType::WasmAnyRef); 2720 // "if you want to narrow the value when it is stored, the source type 2721 // must be Int32". 2722 MOZ_ASSERT_IF(narrowingOp != MNarrowingOp::None, 2723 value->type() == MIRType::Int32); 2724 MOZ_ASSERT( 2725 aliases.flags() == 2726 AliasSet::Store(AliasSet::WasmStructInlineDataArea).flags() || 2727 aliases.flags() == 2728 AliasSet::Store(AliasSet::WasmStructOutlineDataArea).flags() || 2729 aliases.flags() == 2730 AliasSet::Store(AliasSet::WasmArrayDataArea).flags() || 2731 aliases.flags() == AliasSet::Store(AliasSet::Any).flags()); 2732 if (maybeTrap_) { 2733 setGuard(); 2734 } 2735 } 2736 2737 public: 2738 INSTRUCTION_HEADER(WasmStoreField) 2739 TRIVIAL_NEW_WRAPPERS 2740 NAMED_OPERANDS((0, base), (1, value), (2, keepAlive)) 2741 2742 uint32_t offset() const { return offset_; } 2743 mozilla::Maybe<uint32_t> structFieldIndex() const { 2744 return structFieldIndex_; 2745 } 2746 MNarrowingOp narrowingOp() const { return narrowingOp_; } 2747 AliasSet getAliasSet() const override { return aliases_; } 2748 wasm::MaybeTrapSiteDesc maybeTrap() const { return maybeTrap_; } 2749 2750 #ifdef JS_JITSPEW 2751 void getExtras(ExtrasCollector* extras) const override { 2752 char buf[96]; 2753 SprintfLiteral(buf, "(offs=%lld, narrowingOp=%s)", (long long int)offset_, 2754 StringFromMNarrowingOp(narrowingOp_)); 2755 extras->add(buf); 2756 } 2757 #endif 2758 2759 ALLOW_CLONE(MWasmStoreField) 2760 }; 2761 2762 // Stores a reference value to a location, denoted as a fixed offset from a 2763 // base pointer, which (it is assumed) is within a wasm object. This 2764 // instruction emits a pre-barrier. A post barrier _must_ be performed 2765 // separately. The offset must be representable as a 31-bit unsigned integer. 2766 // 2767 // This instruction takes a second object `keepAlive` that must be kept alive, 2768 // as described for MWasmLoadField above. 2769 class MWasmStoreFieldRef : public MAryInstruction<4>, 2770 public NoTypePolicy::Data { 2771 uint32_t offset_; 2772 mozilla::Maybe<uint32_t> structFieldIndex_; 2773 AliasSet aliases_; 2774 wasm::MaybeTrapSiteDesc maybeTrap_; 2775 WasmPreBarrierKind preBarrierKind_; 2776 2777 MWasmStoreFieldRef(MDefinition* instance, MDefinition* base, 2778 MDefinition* keepAlive, size_t offset, 2779 mozilla::Maybe<uint32_t> structFieldIndex, 2780 MDefinition* value, AliasSet aliases, 2781 wasm::MaybeTrapSiteDesc maybeTrap, 2782 WasmPreBarrierKind preBarrierKind) 2783 : MAryInstruction<4>(classOpcode), 2784 offset_(uint32_t(offset)), 2785 structFieldIndex_(structFieldIndex), 2786 aliases_(aliases), 2787 maybeTrap_(std::move(maybeTrap)), 2788 preBarrierKind_(preBarrierKind) { 2789 MOZ_ASSERT(base->type() == TargetWordMIRType() || 2790 base->type() == MIRType::Pointer || 2791 base->type() == MIRType::WasmAnyRef || 2792 base->type() == MIRType::WasmStructData || 2793 base->type() == MIRType::WasmArrayData); 2794 MOZ_ASSERT(offset <= INT32_MAX); 2795 MOZ_ASSERT(value->type() == MIRType::WasmAnyRef); 2796 MOZ_ASSERT( 2797 aliases.flags() == 2798 AliasSet::Store(AliasSet::WasmStructInlineDataArea).flags() || 2799 aliases.flags() == 2800 AliasSet::Store(AliasSet::WasmStructOutlineDataArea).flags() || 2801 aliases.flags() == 2802 AliasSet::Store(AliasSet::WasmArrayDataArea).flags() || 2803 aliases.flags() == AliasSet::Store(AliasSet::Any).flags()); 2804 initOperand(0, instance); 2805 initOperand(1, base); 2806 initOperand(2, value); 2807 initOperand(3, keepAlive ? keepAlive : base); 2808 if (maybeTrap_) { 2809 setGuard(); 2810 } 2811 } 2812 2813 public: 2814 INSTRUCTION_HEADER(WasmStoreFieldRef) 2815 TRIVIAL_NEW_WRAPPERS 2816 NAMED_OPERANDS((0, instance), (1, base), (2, value), (3, keepAlive)) 2817 2818 uint32_t offset() const { return offset_; } 2819 mozilla::Maybe<uint32_t> structFieldIndex() const { 2820 return structFieldIndex_; 2821 } 2822 AliasSet getAliasSet() const override { return aliases_; } 2823 wasm::MaybeTrapSiteDesc maybeTrap() const { return maybeTrap_; } 2824 WasmPreBarrierKind preBarrierKind() const { return preBarrierKind_; } 2825 2826 #ifdef JS_JITSPEW 2827 void getExtras(ExtrasCollector* extras) const override { 2828 char buf[64]; 2829 SprintfLiteral(buf, "(offs=%lld)", (long long int)offset_); 2830 extras->add(buf); 2831 } 2832 #endif 2833 2834 ALLOW_CLONE(MWasmStoreFieldRef) 2835 }; 2836 2837 // Stores a non-reference value to a base pointer, given an index and element 2838 // size. This field may be any value type, excluding references. References MUST 2839 // use the 'Ref' variant of this instruction. 2840 // 2841 // The element size is implicitly defined by MIRType and MNarrowingOp. For 2842 // example, MIRType::Float32 indicates an element size of 32 bits, and 2843 // MIRType::Int32 and MNarrowingOp::To16 together indicate an element size of 16 2844 // bits. 2845 // 2846 // This instruction takes a second object `keepAlive` that must be kept alive, 2847 // as described for MWasmLoadField above. 2848 class MWasmStoreElement : public MQuaternaryInstruction, 2849 public NoTypePolicy::Data { 2850 MNarrowingOp narrowingOp_; 2851 Scale scale_; 2852 AliasSet aliases_; 2853 wasm::MaybeTrapSiteDesc maybeTrap_; 2854 2855 MWasmStoreElement(MDefinition* base, MDefinition* index, MDefinition* value, 2856 MDefinition* keepAlive, MNarrowingOp narrowingOp, 2857 Scale scale, AliasSet aliases, 2858 wasm::MaybeTrapSiteDesc maybeTrap = mozilla::Nothing()) 2859 : MQuaternaryInstruction(classOpcode, base, index, value, 2860 keepAlive ? keepAlive : base), 2861 narrowingOp_(narrowingOp), 2862 scale_(scale), 2863 aliases_(aliases), 2864 maybeTrap_(std::move(maybeTrap)) { 2865 MOZ_ASSERT(base->type() == MIRType::WasmArrayData); 2866 MOZ_ASSERT(value->type() != MIRType::WasmAnyRef); 2867 // "if you want to narrow the value when it is stored, the source type 2868 // must be Int32". 2869 MOZ_ASSERT_IF(narrowingOp != MNarrowingOp::None, 2870 value->type() == MIRType::Int32); 2871 MOZ_ASSERT(aliases.flags() == 2872 AliasSet::Store(AliasSet::WasmArrayDataArea).flags() || 2873 aliases.flags() == AliasSet::Store(AliasSet::Any).flags()); 2874 if (maybeTrap_) { 2875 setGuard(); 2876 } 2877 } 2878 2879 public: 2880 INSTRUCTION_HEADER(WasmStoreElement) 2881 TRIVIAL_NEW_WRAPPERS 2882 NAMED_OPERANDS((0, base), (1, index), (2, value), (3, keepAlive)) 2883 2884 MNarrowingOp narrowingOp() const { return narrowingOp_; } 2885 Scale scale() const { return scale_; } 2886 AliasSet getAliasSet() const override { return aliases_; } 2887 wasm::MaybeTrapSiteDesc maybeTrap() const { return maybeTrap_; } 2888 2889 #ifdef JS_JITSPEW 2890 void getExtras(ExtrasCollector* extras) const override { 2891 char buf[96]; 2892 SprintfLiteral(buf, "(narrowingOp=%s, scale=%s)", 2893 StringFromMNarrowingOp(narrowingOp_), 2894 StringFromScale(scale_)); 2895 extras->add(buf); 2896 } 2897 #endif 2898 2899 ALLOW_CLONE(MWasmStoreElement) 2900 }; 2901 2902 // Stores a reference value to a base pointer, given an index and element size. 2903 // This instruction emits a pre-barrier. A post barrier MUST be performed 2904 // separately. 2905 // 2906 // The element size is implicitly defined by MIRType and MNarrowingOp, as 2907 // described for MWasmStoreElement above. 2908 // 2909 // This instruction takes a second object `ka` that must be kept alive, as 2910 // described for MWasmLoadField above. 2911 class MWasmStoreElementRef : public MAryInstruction<5>, 2912 public NoTypePolicy::Data { 2913 AliasSet aliases_; 2914 wasm::MaybeTrapSiteDesc maybeTrap_; 2915 WasmPreBarrierKind preBarrierKind_; 2916 2917 MWasmStoreElementRef(MDefinition* instance, MDefinition* base, 2918 MDefinition* index, MDefinition* value, 2919 MDefinition* keepAlive, AliasSet aliases, 2920 wasm::MaybeTrapSiteDesc maybeTrap, 2921 WasmPreBarrierKind preBarrierKind) 2922 : MAryInstruction<5>(classOpcode), 2923 aliases_(aliases), 2924 maybeTrap_(std::move(maybeTrap)), 2925 preBarrierKind_(preBarrierKind) { 2926 MOZ_ASSERT(base->type() == MIRType::WasmArrayData); 2927 MOZ_ASSERT(value->type() == MIRType::WasmAnyRef); 2928 MOZ_ASSERT(aliases.flags() == 2929 AliasSet::Store(AliasSet::WasmArrayDataArea).flags() || 2930 aliases.flags() == AliasSet::Store(AliasSet::Any).flags()); 2931 initOperand(0, instance); 2932 initOperand(1, base); 2933 initOperand(2, index); 2934 initOperand(3, value); 2935 initOperand(4, keepAlive ? keepAlive : base); 2936 if (maybeTrap_) { 2937 setGuard(); 2938 } 2939 } 2940 2941 public: 2942 INSTRUCTION_HEADER(WasmStoreElementRef) 2943 TRIVIAL_NEW_WRAPPERS 2944 NAMED_OPERANDS((0, instance), (1, base), (2, index), (3, value), 2945 (4, keepAlive)) 2946 2947 AliasSet getAliasSet() const override { return aliases_; } 2948 wasm::MaybeTrapSiteDesc maybeTrap() const { return maybeTrap_; } 2949 WasmPreBarrierKind preBarrierKind() const { return preBarrierKind_; } 2950 2951 ALLOW_CLONE(MWasmStoreElementRef) 2952 }; 2953 2954 class MWasmRefAsNonNull : public MUnaryInstruction, public NoTypePolicy::Data { 2955 wasm::TrapSiteDesc trapSiteDesc_; 2956 2957 MWasmRefAsNonNull(MDefinition* ref, const wasm::TrapSiteDesc& trapSiteDesc) 2958 : MUnaryInstruction(classOpcode, ref), trapSiteDesc_(trapSiteDesc) { 2959 setResultType(MIRType::WasmAnyRef); 2960 setGuard(); 2961 } 2962 2963 public: 2964 INSTRUCTION_HEADER(WasmRefAsNonNull) 2965 TRIVIAL_NEW_WRAPPERS 2966 NAMED_OPERANDS((0, ref)) 2967 2968 const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; } 2969 2970 bool congruentTo(const MDefinition* ins) const override { 2971 return congruentIfOperandsEqual(ins); 2972 } 2973 2974 wasm::MaybeRefType computeWasmRefType() const override { 2975 if (ref()->wasmRefType().isNothing()) { 2976 return wasm::MaybeRefType(); 2977 } 2978 return wasm::MaybeRefType(ref()->wasmRefType().value().asNonNullable()); 2979 } 2980 2981 MDefinition* foldsTo(TempAllocator& alloc) override; 2982 AliasSet getAliasSet() const override { return AliasSet::None(); } 2983 2984 ALLOW_CLONE(MWasmRefAsNonNull) 2985 }; 2986 2987 // Tests if the wasm ref `ref` is a subtype of `destType` and returns the 2988 // boolean representing the result. 2989 class MWasmRefTestAbstract : public MUnaryInstruction, 2990 public NoTypePolicy::Data { 2991 wasm::RefType destType_; 2992 2993 MWasmRefTestAbstract(MDefinition* ref, wasm::RefType destType) 2994 : MUnaryInstruction(classOpcode, ref), destType_(destType) { 2995 MOZ_ASSERT(!destType.isTypeRef()); 2996 setResultType(MIRType::Int32); 2997 setMovable(); 2998 } 2999 3000 public: 3001 INSTRUCTION_HEADER(WasmRefTestAbstract) 3002 TRIVIAL_NEW_WRAPPERS 3003 NAMED_OPERANDS((0, ref)) 3004 3005 wasm::RefType destType() const { return destType_; }; 3006 3007 AliasSet getAliasSet() const override { return AliasSet::None(); } 3008 3009 bool congruentTo(const MDefinition* ins) const override { 3010 return congruentIfOperandsEqual(ins) && 3011 destType() == ins->toWasmRefTestAbstract()->destType(); 3012 } 3013 3014 HashNumber valueHash() const override { 3015 HashNumber hn = MUnaryInstruction::valueHash(); 3016 hn = addU64ToHash(hn, destType().packed().bits()); 3017 return hn; 3018 } 3019 3020 MDefinition* foldsTo(TempAllocator& alloc) override; 3021 3022 ALLOW_CLONE(MWasmRefTestAbstract) 3023 }; 3024 3025 // Tests if the wasm ref `ref` is a subtype of `superSTV` and returns the 3026 // boolean representing the result. 3027 // 3028 // The actual super type definition must be known at compile time, so that the 3029 // subtyping depth of super type depth can be used. 3030 class MWasmRefTestConcrete : public MBinaryInstruction, 3031 public NoTypePolicy::Data { 3032 wasm::RefType destType_; 3033 3034 MWasmRefTestConcrete(MDefinition* ref, MDefinition* superSTV, 3035 wasm::RefType destType) 3036 : MBinaryInstruction(classOpcode, ref, superSTV), destType_(destType) { 3037 MOZ_ASSERT(destType.isTypeRef()); 3038 setResultType(MIRType::Int32); 3039 setMovable(); 3040 } 3041 3042 public: 3043 INSTRUCTION_HEADER(WasmRefTestConcrete) 3044 TRIVIAL_NEW_WRAPPERS 3045 NAMED_OPERANDS((0, ref), (1, superSTV)) 3046 3047 wasm::RefType destType() const { return destType_; }; 3048 3049 AliasSet getAliasSet() const override { return AliasSet::None(); } 3050 3051 bool congruentTo(const MDefinition* ins) const override { 3052 return congruentIfOperandsEqual(ins) && 3053 destType() == ins->toWasmRefTestConcrete()->destType(); 3054 } 3055 3056 HashNumber valueHash() const override { 3057 HashNumber hn = MBinaryInstruction::valueHash(); 3058 hn = addU64ToHash(hn, destType().packed().bits()); 3059 return hn; 3060 } 3061 3062 MDefinition* foldsTo(TempAllocator& alloc) override; 3063 3064 ALLOW_CLONE(MWasmRefTestConcrete) 3065 }; 3066 3067 // Tests if the wasm ref `ref` is a subtype of `destType` and if so returns the 3068 // ref, otherwise it does a wasm trap. 3069 class MWasmRefCastAbstract : public MUnaryInstruction, 3070 public NoTypePolicy::Data { 3071 wasm::RefType destType_; 3072 wasm::TrapSiteDesc trapSiteDesc_; 3073 3074 MWasmRefCastAbstract(MDefinition* ref, wasm::RefType destType, 3075 wasm::TrapSiteDesc&& trapSiteDesc) 3076 : MUnaryInstruction(classOpcode, ref), 3077 destType_(destType), 3078 trapSiteDesc_(std::move(trapSiteDesc)) { 3079 MOZ_ASSERT(!destType.isTypeRef()); 3080 setResultType(MIRType::WasmAnyRef); 3081 // This may trap, which requires this to be a guard. 3082 setGuard(); 3083 initWasmRefType(wasm::MaybeRefType(destType)); 3084 } 3085 3086 public: 3087 INSTRUCTION_HEADER(WasmRefCastAbstract) 3088 TRIVIAL_NEW_WRAPPERS 3089 NAMED_OPERANDS((0, ref)) 3090 AliasSet getAliasSet() const override { return AliasSet::None(); } 3091 3092 wasm::RefType destType() const { return destType_; }; 3093 const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; } 3094 3095 MDefinition* foldsTo(TempAllocator& alloc) override; 3096 }; 3097 3098 // Tests if the wasm ref `ref` is a subtype of `superSTV`, if so return the 3099 // ref, otherwise do a wasm trap. 3100 // 3101 // The actual super type definition must be known at compile time, so that the 3102 // subtyping depth of super type depth can be used. 3103 class MWasmRefCastConcrete : public MBinaryInstruction, 3104 public NoTypePolicy::Data { 3105 wasm::RefType destType_; 3106 wasm::TrapSiteDesc trapSiteDesc_; 3107 3108 MWasmRefCastConcrete(MDefinition* ref, MDefinition* superSTV, 3109 wasm::RefType destType, 3110 wasm::TrapSiteDesc&& trapSiteDesc) 3111 : MBinaryInstruction(classOpcode, ref, superSTV), 3112 destType_(destType), 3113 trapSiteDesc_(std::move(trapSiteDesc)) { 3114 MOZ_ASSERT(destType.isTypeRef()); 3115 setResultType(MIRType::WasmAnyRef); 3116 // This may trap, which requires this to be a guard. 3117 setGuard(); 3118 initWasmRefType(wasm::MaybeRefType(destType)); 3119 } 3120 3121 public: 3122 INSTRUCTION_HEADER(WasmRefCastConcrete) 3123 TRIVIAL_NEW_WRAPPERS 3124 NAMED_OPERANDS((0, ref), (1, superSTV)) 3125 AliasSet getAliasSet() const override { return AliasSet::None(); } 3126 3127 wasm::RefType destType() const { return destType_; }; 3128 const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; } 3129 3130 MDefinition* foldsTo(TempAllocator& alloc) override; 3131 }; 3132 3133 class MWasmRefConvertAnyExtern : public MUnaryInstruction, 3134 public NoTypePolicy::Data { 3135 wasm::RefType::Kind destTypeKind_; 3136 3137 MWasmRefConvertAnyExtern(MDefinition* ref, wasm::RefType::Kind destTypeKind) 3138 : MUnaryInstruction(classOpcode, ref), destTypeKind_(destTypeKind) { 3139 MOZ_ASSERT(destTypeKind_ == wasm::RefType::Kind::Any || 3140 destTypeKind_ == wasm::RefType::Kind::Extern); 3141 setResultType(MIRType::WasmAnyRef); 3142 setMovable(); 3143 } 3144 3145 public: 3146 INSTRUCTION_HEADER(WasmRefConvertAnyExtern) 3147 TRIVIAL_NEW_WRAPPERS 3148 NAMED_OPERANDS((0, ref)) 3149 3150 wasm::RefType::Kind destTypeKind() const { return destTypeKind_; } 3151 3152 wasm::MaybeRefType computeWasmRefType() const override { 3153 bool nullable = true; 3154 if (ref()->wasmRefType().isSome()) { 3155 nullable = ref()->wasmRefType().value().isNullable(); 3156 }; 3157 return wasm::MaybeRefType(wasm::RefType::fromKind(destTypeKind_, nullable)); 3158 } 3159 3160 bool congruentTo(const MDefinition* ins) const override { 3161 return congruentIfOperandsEqual(ins) && 3162 destTypeKind() == ins->toWasmRefConvertAnyExtern()->destTypeKind(); 3163 } 3164 3165 HashNumber valueHash() const override { 3166 HashNumber hn = MUnaryInstruction::valueHash(); 3167 hn = addU32ToHash(hn, destTypeKind()); 3168 return hn; 3169 } 3170 3171 AliasSet getAliasSet() const override { return AliasSet::None(); } 3172 }; 3173 3174 // Represents the contents of all fields of a wasm struct. 3175 // This class will be used for scalar replacement of wasm structs. 3176 class MWasmStructState : public TempObject { 3177 private: 3178 MDefinition* wasmStruct_; 3179 // Represents the fields of this struct. 3180 Vector<MDefinition*, 0, JitAllocPolicy> fields_; 3181 3182 explicit MWasmStructState(TempAllocator& alloc, MDefinition* structObject) 3183 : wasmStruct_(structObject), fields_(alloc) {} 3184 3185 public: 3186 static MWasmStructState* New(TempAllocator& alloc, MDefinition* structObject); 3187 static MWasmStructState* Copy(TempAllocator& alloc, MWasmStructState* state); 3188 3189 // Init the fields_ vector. 3190 [[nodiscard]] bool init(); 3191 3192 size_t numFields() const { return fields_.length(); } 3193 MDefinition* wasmStruct() const { return wasmStruct_; } 3194 3195 // Get the field value based on the position of the field in the struct. 3196 MDefinition* getField(uint32_t index) const { return fields_[index]; } 3197 // Set the field offset based on the position of the field in the struct. 3198 void setField(uint32_t index, MDefinition* def) { fields_[index] = def; } 3199 }; 3200 3201 class MWasmNewStructObject : public MBinaryInstruction, 3202 public NoTypePolicy::Data { 3203 private: 3204 const wasm::TypeDef* typeDef_; 3205 bool zeroFields_; 3206 wasm::TrapSiteDesc trapSiteDesc_; 3207 3208 MWasmNewStructObject(MDefinition* instance, MDefinition* allocSite, 3209 const wasm::TypeDef* typeDef, bool zeroFields, 3210 const wasm::TrapSiteDesc& trapSiteDesc) 3211 : MBinaryInstruction(classOpcode, instance, allocSite), 3212 typeDef_(typeDef), 3213 zeroFields_(zeroFields), 3214 trapSiteDesc_(trapSiteDesc) { 3215 MOZ_ASSERT(typeDef->isStructType()); 3216 setResultType(MIRType::WasmAnyRef); 3217 initWasmRefType( 3218 wasm::MaybeRefType(wasm::RefType::fromTypeDef(typeDef_, false))); 3219 } 3220 3221 public: 3222 INSTRUCTION_HEADER(WasmNewStructObject) 3223 TRIVIAL_NEW_WRAPPERS 3224 NAMED_OPERANDS((0, instance), (1, allocSite)) 3225 3226 AliasSet getAliasSet() const override { 3227 if (js::SupportDifferentialTesting()) { 3228 // Consider allocations effectful for differential testing. 3229 return MDefinition::getAliasSet(); 3230 } 3231 return AliasSet::None(); 3232 } 3233 const wasm::TypeDef& typeDef() { return *typeDef_; } 3234 const wasm::StructType& structType() const { return typeDef_->structType(); } 3235 bool isOutline() const { return typeDef_->structType().hasOOL(); } 3236 bool zeroFields() const { return zeroFields_; } 3237 const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; } 3238 gc::AllocKind allocKind() const { return typeDef_->structType().allocKind_; } 3239 }; 3240 3241 class MWasmNewArrayObject : public MTernaryInstruction, 3242 public NoTypePolicy::Data { 3243 private: 3244 const wasm::TypeDef* typeDef_; 3245 bool zeroFields_; 3246 wasm::TrapSiteDesc trapSiteDesc_; 3247 3248 MWasmNewArrayObject(MDefinition* instance, MDefinition* numElements, 3249 MDefinition* allocSite, const wasm::TypeDef* typeDef, 3250 bool zeroFields, const wasm::TrapSiteDesc& trapSiteDesc) 3251 : MTernaryInstruction(classOpcode, instance, numElements, allocSite), 3252 typeDef_(typeDef), 3253 zeroFields_(zeroFields), 3254 trapSiteDesc_(trapSiteDesc) { 3255 MOZ_ASSERT(typeDef->isArrayType()); 3256 setResultType(MIRType::WasmAnyRef); 3257 initWasmRefType( 3258 wasm::MaybeRefType(wasm::RefType::fromTypeDef(typeDef_, false))); 3259 } 3260 3261 public: 3262 INSTRUCTION_HEADER(WasmNewArrayObject) 3263 TRIVIAL_NEW_WRAPPERS 3264 NAMED_OPERANDS((0, instance), (1, numElements), (2, allocSite)) 3265 3266 AliasSet getAliasSet() const override { 3267 if (js::SupportDifferentialTesting()) { 3268 // Consider allocations effectful for differential testing. 3269 return MDefinition::getAliasSet(); 3270 } 3271 return AliasSet::None(); 3272 } 3273 const wasm::TypeDef& typeDef() { return *typeDef_; } 3274 const wasm::ArrayType& arrayType() const { return typeDef_->arrayType(); } 3275 uint32_t elemSize() const { 3276 return typeDef_->arrayType().elementType().size(); 3277 } 3278 bool zeroFields() const { return zeroFields_; } 3279 const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; } 3280 }; 3281 3282 #undef INSTRUCTION_HEADER 3283 3284 #ifdef ENABLE_WASM_SIMD 3285 MWasmShuffleSimd128* BuildWasmShuffleSimd128(TempAllocator& alloc, 3286 const int8_t* control, 3287 MDefinition* lhs, 3288 MDefinition* rhs); 3289 #endif // ENABLE_WASM_SIMD 3290 3291 } // namespace jit 3292 } // namespace js 3293 3294 #endif /* jit_MIR_wasm_h */