IonTypes.h (28237B)
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 #ifndef jit_IonTypes_h 8 #define jit_IonTypes_h 9 10 #include "mozilla/HashFunctions.h" 11 12 #include <algorithm> 13 #include <stdint.h> 14 15 #include "jstypes.h" 16 #include "NamespaceImports.h" 17 18 #include "jit/ABIFunctionType.h" 19 20 #include "js/ScalarType.h" // js::Scalar::Type 21 #include "js/Value.h" 22 23 namespace js { 24 25 // Each IonScript has a unique compilation id. This is used to sweep/ignore 26 // constraints for IonScripts that have been invalidated/destroyed. 27 class IonCompilationId { 28 // Use two 32-bit integers instead of uint64_t to avoid 8-byte alignment on 29 // some 32-bit platforms. 30 uint32_t idLo_; 31 uint32_t idHi_; 32 33 public: 34 explicit IonCompilationId(uint64_t id) 35 : idLo_(id & UINT32_MAX), idHi_(id >> 32) {} 36 bool operator==(const IonCompilationId& other) const { 37 return idLo_ == other.idLo_ && idHi_ == other.idHi_; 38 } 39 bool operator!=(const IonCompilationId& other) const { 40 return !operator==(other); 41 } 42 }; 43 44 namespace jit { 45 46 using RecoverOffset = uint32_t; 47 using SnapshotOffset = uint32_t; 48 49 // The maximum size of any buffer associated with an assembler or code object. 50 // This is chosen to not overflow a signed integer, leaving room for an extra 51 // bit on offsets. 52 static const uint32_t MAX_BUFFER_SIZE = (1 << 30) - 1; 53 54 // Maximum number of scripted arg slots. 55 static const uint32_t SNAPSHOT_MAX_NARGS = 127; 56 57 static const SnapshotOffset INVALID_RECOVER_OFFSET = uint32_t(-1); 58 static const SnapshotOffset INVALID_SNAPSHOT_OFFSET = uint32_t(-1); 59 60 /* 61 * [SMDOC] Avoiding repeated bailouts / invalidations 62 * 63 * To avoid getting trapped in a "compilation -> bailout -> invalidation -> 64 * recompilation -> bailout -> invalidation -> ..." loop, every snapshot in 65 * Warp code is assigned a BailoutKind. If we bail out at that snapshot, 66 * FinishBailoutToBaseline will examine the BailoutKind and take appropriate 67 * action. In general: 68 * 69 * 1. If the bailing instruction comes from transpiled CacheIR, then when we 70 * bail out and continue execution in the baseline interpreter, the 71 * corresponding stub should fail a guard. As a result, we will either 72 * increment the enteredCount for a subsequent stub or attach a new stub, 73 * either of which will prevent WarpOracle from transpiling the failing stub 74 * when we recompile. 75 * 76 * Note: this means that every CacheIR op that can bail out in Warp must 77 * have an equivalent guard in the baseline CacheIR implementation. 78 * 79 * FirstExecution works according to the same principles: we have never hit 80 * this IC before, but after we bail to baseline we will attach a stub and 81 * recompile with better CacheIR information. 82 * 83 * 2. If the bailout occurs because an assumption we made in WarpBuilder was 84 * invalidated, then FinishBailoutToBaseline will set a flag on the script 85 * to avoid that assumption in the future: for example, UninitializedLexical. 86 * 87 * 3. Similarly, if the bailing instruction is generated or modified by a MIR 88 * optimization, then FinishBailoutToBaseline will set a flag on the script 89 * to make that optimization more conservative in the future. Examples 90 * include LICM, EagerTruncation, and HoistBoundsCheck. 91 * 92 * 4. Some bailouts can't be handled in Warp, even after a recompile. For 93 * example, Warp does not support catching exceptions. If this happens 94 * too often, then the cost of bailing out repeatedly outweighs the 95 * benefit of Warp compilation, so we invalidate the script and disable 96 * Warp compilation. 97 * 98 * 5. Some bailouts don't happen in performance-sensitive code: for example, 99 * the |debugger| statement. We just ignore those. 100 */ 101 enum class BailoutKind : uint8_t { 102 Unknown, 103 104 // An instruction generated by the transpiler. If this instruction bails out, 105 // attaching a new stub in baseline will invalidate the current Warp script 106 // and avoid a bailout loop. 107 TranspiledCacheIR, 108 109 // A GuardMultipleShapes instruction generated by the transpiler after stub 110 // folding. If this instruction bails out, we will return to baseline and see 111 // if we add a new case to the folded stub. If we do, this should not count as 112 // a bailout for the purpose of eventually invalidating this script. 113 // 114 // If the script containing the folded stub was inlined monomorphically, 115 // there's no direct connection between the inner script and the outer script. 116 // We store the inner and outer scripts so that we know which outer script to 117 // notify if we successfully add a new case to the folded stub. 118 StubFoldingGuardMultipleShapes, 119 120 // An optimistic unbox on the cold path for a non-Value phi failed. If this 121 // instruction bails out, we will invalidate the script and mark the 122 // HadSpeculativePhiBailout flag on the script. 123 SpeculativePhi, 124 125 // A conversion inserted by a type policy. If this instruction bails out, 126 // we expect to throw an error. If this happens too frequently, we will 127 // invalidate the current Warp script and disable recompilation. 128 TypePolicy, 129 130 // An instruction hoisted by LICM. If this instruction bails out, we will 131 // bail out to baseline to see if we attach a new stub. If we do, then the 132 // more than once, we will invalidate the current Warp script and 133 // mark the hadLICMInvalidation flag on the script. 134 LICM, 135 136 // An instruction moved up by InstructionReordering. If this 137 // instruction bails out, we will mark the ReorderingBailout flag on 138 // the script. If this happens too frequently, we will invalidate 139 // the script. 140 InstructionReordering, 141 142 // An instruction created or hoisted by tryHoistBoundsCheck. 143 // If this instruction bails out, we will invalidate the current Warp script 144 // and mark the HoistBoundsCheckBailout flag on the script. 145 HoistBoundsCheck, 146 147 // An eager truncation generated by range analysis. 148 // If this instruction bails out, we will invalidate the current Warp script 149 // and mark the EagerTruncationBailout flag on the script. 150 EagerTruncation, 151 152 // A folded unbox instruction generated by FoldLoadsWithUnbox. 153 // If this instruction bails out, we will invalidate the current Warp script 154 // and mark the UnboxFoldingBailout flag on the script. 155 UnboxFolding, 156 157 // An inevitable bailout (MBail instruction or type barrier that always bails) 158 Inevitable, 159 160 // Bailing out during a VM call. Many possible causes that are hard 161 // to distinguish statically at snapshot construction time. 162 // We just lump them together. 163 DuringVMCall, 164 165 // A spread call or funapply had more than JIT_ARGS_LENGTH_MAX arguments. 166 // We bail out to handle this in the VM. If this happens too frequently, 167 // we will invalidate the current Warp script and disable recompilation. 168 TooManyArguments, 169 170 // We hit an active |debugger;| statement. 171 Debugger, 172 173 // We hit this code for the first time. 174 FirstExecution, 175 176 // A lexical check failed. We will set lexical checks as unmovable. 177 UninitializedLexical, 178 179 // A bailout to baseline from Ion on exception to handle Debugger hooks. 180 IonExceptionDebugMode, 181 182 // A bailout to baseline from Ion on exception to handle a finally block. 183 Finally, 184 185 // We returned to a stack frame after invalidating its IonScript. 186 OnStackInvalidation, 187 188 // We returned to a stack frame while calling the |return| method of an 189 // iterator, and we have to throw an exception because the return value 190 // was not an object. 191 ThrowCheckIsObject, 192 193 // These two are similar to ThrowCheckIsObject. We have to throw an exception 194 // because the result of a Proxy get trap didn't match the requirements. 195 ThrowProxyTrapMustReportSameValue, 196 ThrowProxyTrapMustReportUndefined, 197 198 // We have executed code that should be unreachable, and need to assert. 199 Unreachable, 200 201 Limit 202 }; 203 204 inline const char* BailoutKindString(BailoutKind kind) { 205 switch (kind) { 206 case BailoutKind::Unknown: 207 return "Unknown"; 208 case BailoutKind::TranspiledCacheIR: 209 return "TranspiledCacheIR"; 210 case BailoutKind::StubFoldingGuardMultipleShapes: 211 return "StubFoldingGuardMultipleShapes"; 212 case BailoutKind::SpeculativePhi: 213 return "SpeculativePhi"; 214 case BailoutKind::TypePolicy: 215 return "TypePolicy"; 216 case BailoutKind::LICM: 217 return "LICM"; 218 case BailoutKind::InstructionReordering: 219 return "InstructionReordering"; 220 case BailoutKind::HoistBoundsCheck: 221 return "HoistBoundsCheck"; 222 case BailoutKind::EagerTruncation: 223 return "EagerTruncation"; 224 case BailoutKind::UnboxFolding: 225 return "UnboxFolding"; 226 case BailoutKind::Inevitable: 227 return "Inevitable"; 228 case BailoutKind::DuringVMCall: 229 return "DuringVMCall"; 230 case BailoutKind::TooManyArguments: 231 return "TooManyArguments"; 232 case BailoutKind::Debugger: 233 return "Debugger"; 234 case BailoutKind::FirstExecution: 235 return "FirstExecution"; 236 case BailoutKind::UninitializedLexical: 237 return "UninitializedLexical"; 238 case BailoutKind::IonExceptionDebugMode: 239 return "IonExceptionDebugMode"; 240 case BailoutKind::Finally: 241 return "Finally"; 242 case BailoutKind::OnStackInvalidation: 243 return "OnStackInvalidation"; 244 case BailoutKind::ThrowCheckIsObject: 245 return "ThrowCheckIsObject"; 246 case BailoutKind::ThrowProxyTrapMustReportSameValue: 247 return "ThrowProxyTrapMustReportSameValue"; 248 case BailoutKind::ThrowProxyTrapMustReportUndefined: 249 return "ThrowProxyTrapMustReportUndefined"; 250 case BailoutKind::Unreachable: 251 return "Unreachable"; 252 253 case BailoutKind::Limit: 254 break; 255 } 256 257 MOZ_CRASH("Invalid BailoutKind"); 258 } 259 260 static const uint32_t ELEMENT_TYPE_BITS = 5; 261 static const uint32_t ELEMENT_TYPE_SHIFT = 0; 262 static const uint32_t ELEMENT_TYPE_MASK = (1 << ELEMENT_TYPE_BITS) - 1; 263 static const uint32_t VECTOR_TYPE_BITS = 1; 264 static const uint32_t VECTOR_TYPE_SHIFT = 265 ELEMENT_TYPE_BITS + ELEMENT_TYPE_SHIFT; 266 static const uint32_t VECTOR_TYPE_MASK = (1 << VECTOR_TYPE_BITS) - 1; 267 268 // The integer SIMD types have a lot of operations that do the exact same thing 269 // for signed and unsigned integer types. Sometimes it is simpler to treat 270 // signed and unsigned integer SIMD types as the same type, using a SimdSign to 271 // distinguish the few cases where there is a difference. 272 enum class SimdSign { 273 // Signedness is not applicable to this type. (i.e., Float or Bool). 274 NotApplicable, 275 // Treat as an unsigned integer with a range 0 .. 2^N-1. 276 Unsigned, 277 // Treat as a signed integer in two's complement encoding. 278 Signed, 279 }; 280 281 class SimdConstant { 282 public: 283 enum Type { 284 Int8x16, 285 Int16x8, 286 Int32x4, 287 Int64x2, 288 Float32x4, 289 Float64x2, 290 Undefined = -1 291 }; 292 293 using I8x16 = int8_t[16]; 294 using I16x8 = int16_t[8]; 295 using I32x4 = int32_t[4]; 296 using I64x2 = int64_t[2]; 297 using F32x4 = float[4]; 298 using F64x2 = double[2]; 299 300 private: 301 Type type_; 302 union { 303 I8x16 i8x16; 304 I16x8 i16x8; 305 I32x4 i32x4; 306 I64x2 i64x2; 307 F32x4 f32x4; 308 F64x2 f64x2; 309 } u; 310 311 bool defined() const { return type_ != Undefined; } 312 313 public: 314 // Doesn't have a default constructor, as it would prevent it from being 315 // included in unions. 316 317 static SimdConstant CreateX16(const int8_t* array) { 318 SimdConstant cst; 319 cst.type_ = Int8x16; 320 memcpy(cst.u.i8x16, array, sizeof(cst.u)); 321 return cst; 322 } 323 static SimdConstant SplatX16(int8_t v) { 324 SimdConstant cst; 325 cst.type_ = Int8x16; 326 std::fill_n(cst.u.i8x16, 16, v); 327 return cst; 328 } 329 static SimdConstant CreateX8(const int16_t* array) { 330 SimdConstant cst; 331 cst.type_ = Int16x8; 332 memcpy(cst.u.i16x8, array, sizeof(cst.u)); 333 return cst; 334 } 335 static SimdConstant SplatX8(int16_t v) { 336 SimdConstant cst; 337 cst.type_ = Int16x8; 338 std::fill_n(cst.u.i16x8, 8, v); 339 return cst; 340 } 341 static SimdConstant CreateX4(const int32_t* array) { 342 SimdConstant cst; 343 cst.type_ = Int32x4; 344 memcpy(cst.u.i32x4, array, sizeof(cst.u)); 345 return cst; 346 } 347 static SimdConstant SplatX4(int32_t v) { 348 SimdConstant cst; 349 cst.type_ = Int32x4; 350 std::fill_n(cst.u.i32x4, 4, v); 351 return cst; 352 } 353 static SimdConstant CreateX2(const int64_t* array) { 354 SimdConstant cst; 355 cst.type_ = Int64x2; 356 memcpy(cst.u.i64x2, array, sizeof(cst.u)); 357 return cst; 358 } 359 static SimdConstant SplatX2(int64_t v) { 360 SimdConstant cst; 361 cst.type_ = Int64x2; 362 std::fill_n(cst.u.i64x2, 2, v); 363 return cst; 364 } 365 static SimdConstant CreateX4(const float* array) { 366 SimdConstant cst; 367 cst.type_ = Float32x4; 368 memcpy(cst.u.f32x4, array, sizeof(cst.u)); 369 return cst; 370 } 371 static SimdConstant SplatX4(float v) { 372 SimdConstant cst; 373 cst.type_ = Float32x4; 374 std::fill_n(cst.u.f32x4, 4, v); 375 return cst; 376 } 377 static SimdConstant CreateX2(const double* array) { 378 SimdConstant cst; 379 cst.type_ = Float64x2; 380 memcpy(cst.u.f64x2, array, sizeof(cst.u)); 381 return cst; 382 } 383 static SimdConstant SplatX2(double v) { 384 SimdConstant cst; 385 cst.type_ = Float64x2; 386 std::fill_n(cst.u.f64x2, 2, v); 387 return cst; 388 } 389 390 // Overloads for use by templates. 391 static SimdConstant CreateSimd128(const int8_t* array) { 392 return CreateX16(array); 393 } 394 static SimdConstant CreateSimd128(const int16_t* array) { 395 return CreateX8(array); 396 } 397 static SimdConstant CreateSimd128(const int32_t* array) { 398 return CreateX4(array); 399 } 400 static SimdConstant CreateSimd128(const int64_t* array) { 401 return CreateX2(array); 402 } 403 static SimdConstant CreateSimd128(const float* array) { 404 return CreateX4(array); 405 } 406 static SimdConstant CreateSimd128(const double* array) { 407 return CreateX2(array); 408 } 409 410 Type type() const { 411 MOZ_ASSERT(defined()); 412 return type_; 413 } 414 415 bool isFloatingType() const { 416 MOZ_ASSERT(defined()); 417 return type_ >= Float32x4; 418 } 419 420 bool isIntegerType() const { 421 MOZ_ASSERT(defined()); 422 return type_ <= Int64x2; 423 } 424 425 // Get the raw bytes of the constant. 426 const void* bytes() const { return u.i8x16; } 427 428 const I8x16& asInt8x16() const { 429 MOZ_ASSERT(defined() && type_ == Int8x16); 430 return u.i8x16; 431 } 432 433 const I16x8& asInt16x8() const { 434 MOZ_ASSERT(defined() && type_ == Int16x8); 435 return u.i16x8; 436 } 437 438 const I32x4& asInt32x4() const { 439 MOZ_ASSERT(defined() && type_ == Int32x4); 440 return u.i32x4; 441 } 442 443 const I64x2& asInt64x2() const { 444 MOZ_ASSERT(defined() && type_ == Int64x2); 445 return u.i64x2; 446 } 447 448 const F32x4& asFloat32x4() const { 449 MOZ_ASSERT(defined() && type_ == Float32x4); 450 return u.f32x4; 451 } 452 453 const F64x2& asFloat64x2() const { 454 MOZ_ASSERT(defined() && type_ == Float64x2); 455 return u.f64x2; 456 } 457 458 bool bitwiseEqual(const SimdConstant& rhs) const { 459 MOZ_ASSERT(defined() && rhs.defined()); 460 return memcmp(&u, &rhs.u, sizeof(u)) == 0; 461 } 462 463 bool isZeroBits() const { 464 MOZ_ASSERT(defined()); 465 return u.i64x2[0] == 0 && u.i64x2[1] == 0; 466 } 467 468 bool isOneBits() const { 469 MOZ_ASSERT(defined()); 470 return ~u.i64x2[0] == 0 && ~u.i64x2[1] == 0; 471 } 472 473 // SimdConstant is a HashPolicy. Currently we discriminate by type, but it 474 // may be that we should only be discriminating by int vs float. 475 using Lookup = SimdConstant; 476 477 static HashNumber hash(const SimdConstant& val) { 478 uint32_t hash = mozilla::HashBytes(&val.u, sizeof(val.u)); 479 return mozilla::AddToHash(hash, val.type_); 480 } 481 482 static bool match(const SimdConstant& lhs, const SimdConstant& rhs) { 483 return lhs.type() == rhs.type() && lhs.bitwiseEqual(rhs); 484 } 485 }; 486 487 enum class IntConversionInputKind { NumbersOnly, Any }; 488 489 // The ordering of this enumeration is important: Anything < Value is a 490 // specialized type. Furthermore, anything < String has trivial conversion to 491 // a number. 492 enum class MIRType : uint8_t { 493 Undefined, 494 Null, 495 Boolean, 496 Int32, 497 Int64, 498 IntPtr, 499 Double, 500 Float32, 501 // Types above have trivial conversion to a number. 502 String, 503 Symbol, 504 BigInt, 505 Simd128, 506 // Types above are primitive (including undefined and null). 507 Object, 508 MagicOptimizedOut, // JS_OPTIMIZED_OUT magic value. 509 MagicHole, // JS_ELEMENTS_HOLE magic value. 510 MagicIsConstructing, // JS_IS_CONSTRUCTING magic value. 511 MagicUninitializedLexical, // JS_UNINITIALIZED_LEXICAL magic value. 512 // Types above are specialized. 513 Value, 514 None, // Invalid, used as a placeholder. 515 Slots, // A slots vector 516 Elements, // An elements vector 517 Pointer, // An opaque pointer that receives no special treatment 518 WasmAnyRef, // Wasm Ref/AnyRef/NullRef: a raw JSObject* or a raw (void*)0 519 WasmStructData, // A WasmStructObject data pointer (to OOL storage only) 520 WasmArrayData, // A WasmArrayObject data pointer (to IL or OOL storage) 521 StackResults, // Wasm multi-value stack result area, which may contain refs 522 Shape, // A Shape pointer. 523 Last = Shape 524 }; 525 526 static inline MIRType TargetWordMIRType() { 527 #ifdef JS_64BIT 528 return MIRType::Int64; 529 #else 530 return MIRType::Int32; 531 #endif 532 } 533 534 static inline MIRType MIRTypeFromValueType(JSValueType type) { 535 // This function does not deal with magic types. Magic constants should be 536 // filtered out in MIRTypeFromValue. 537 switch (type) { 538 case JSVAL_TYPE_DOUBLE: 539 return MIRType::Double; 540 case JSVAL_TYPE_INT32: 541 return MIRType::Int32; 542 case JSVAL_TYPE_UNDEFINED: 543 return MIRType::Undefined; 544 case JSVAL_TYPE_STRING: 545 return MIRType::String; 546 case JSVAL_TYPE_SYMBOL: 547 return MIRType::Symbol; 548 case JSVAL_TYPE_BIGINT: 549 return MIRType::BigInt; 550 case JSVAL_TYPE_BOOLEAN: 551 return MIRType::Boolean; 552 case JSVAL_TYPE_NULL: 553 return MIRType::Null; 554 case JSVAL_TYPE_OBJECT: 555 return MIRType::Object; 556 case JSVAL_TYPE_UNKNOWN: 557 return MIRType::Value; 558 default: 559 MOZ_CRASH("unexpected jsval type"); 560 } 561 } 562 563 static inline JSValueType ValueTypeFromMIRType(MIRType type) { 564 switch (type) { 565 case MIRType::Undefined: 566 return JSVAL_TYPE_UNDEFINED; 567 case MIRType::Null: 568 return JSVAL_TYPE_NULL; 569 case MIRType::Boolean: 570 return JSVAL_TYPE_BOOLEAN; 571 case MIRType::Int32: 572 return JSVAL_TYPE_INT32; 573 case MIRType::Float32: // Fall through, there's no JSVAL for Float32 574 case MIRType::Double: 575 return JSVAL_TYPE_DOUBLE; 576 case MIRType::String: 577 return JSVAL_TYPE_STRING; 578 case MIRType::Symbol: 579 return JSVAL_TYPE_SYMBOL; 580 case MIRType::BigInt: 581 return JSVAL_TYPE_BIGINT; 582 case MIRType::MagicOptimizedOut: 583 case MIRType::MagicHole: 584 case MIRType::MagicIsConstructing: 585 case MIRType::MagicUninitializedLexical: 586 return JSVAL_TYPE_MAGIC; 587 case MIRType::Object: 588 return JSVAL_TYPE_OBJECT; 589 default: 590 break; 591 } 592 MOZ_CRASH("bad type"); 593 } 594 595 static inline JSValueTag MIRTypeToTag(MIRType type) { 596 return JSVAL_TYPE_TO_TAG(ValueTypeFromMIRType(type)); 597 } 598 599 static inline size_t MIRTypeToSize(MIRType type) { 600 switch (type) { 601 case MIRType::Int32: 602 return 4; 603 case MIRType::Int64: 604 return 8; 605 case MIRType::Float32: 606 return 4; 607 case MIRType::Double: 608 return 8; 609 case MIRType::Simd128: 610 return 16; 611 case MIRType::Pointer: 612 case MIRType::WasmAnyRef: 613 return sizeof(uintptr_t); 614 default: 615 MOZ_CRASH("MIRTypeToSize - unhandled case"); 616 } 617 } 618 619 static inline const char* StringFromMIRType(MIRType type) { 620 switch (type) { 621 case MIRType::Undefined: 622 return "Undefined"; 623 case MIRType::Null: 624 return "Null"; 625 case MIRType::Boolean: 626 return "Bool"; 627 case MIRType::Int32: 628 return "Int32"; 629 case MIRType::Int64: 630 return "Int64"; 631 case MIRType::IntPtr: 632 return "IntPtr"; 633 case MIRType::Double: 634 return "Double"; 635 case MIRType::Float32: 636 return "Float32"; 637 case MIRType::String: 638 return "String"; 639 case MIRType::Symbol: 640 return "Symbol"; 641 case MIRType::BigInt: 642 return "BigInt"; 643 case MIRType::Object: 644 return "Object"; 645 case MIRType::MagicOptimizedOut: 646 return "MagicOptimizedOut"; 647 case MIRType::MagicHole: 648 return "MagicHole"; 649 case MIRType::MagicIsConstructing: 650 return "MagicIsConstructing"; 651 case MIRType::MagicUninitializedLexical: 652 return "MagicUninitializedLexical"; 653 case MIRType::Value: 654 return "Value"; 655 case MIRType::None: 656 return "None"; 657 case MIRType::Slots: 658 return "Slots"; 659 case MIRType::Elements: 660 return "Elements"; 661 case MIRType::Pointer: 662 return "Pointer"; 663 case MIRType::WasmAnyRef: 664 return "WasmAnyRef"; 665 case MIRType::WasmStructData: 666 return "WasmStructData"; 667 case MIRType::WasmArrayData: 668 return "WasmArrayData"; 669 case MIRType::StackResults: 670 return "StackResults"; 671 case MIRType::Shape: 672 return "Shape"; 673 case MIRType::Simd128: 674 return "Simd128"; 675 } 676 MOZ_CRASH("Unknown MIRType."); 677 } 678 679 static inline bool IsIntType(MIRType type) { 680 return type == MIRType::Int32 || type == MIRType::Int64 || 681 type == MIRType::IntPtr; 682 } 683 684 static inline bool IsNumberType(MIRType type) { 685 return type == MIRType::Int32 || type == MIRType::Double || 686 type == MIRType::Float32 || type == MIRType::Int64 || 687 type == MIRType::IntPtr; 688 } 689 690 static inline bool IsTypeRepresentableAsDouble(MIRType type) { 691 return type == MIRType::Int32 || type == MIRType::Double || 692 type == MIRType::Float32; 693 } 694 695 static inline bool IsFloatingPointType(MIRType type) { 696 return type == MIRType::Double || type == MIRType::Float32; 697 } 698 699 static inline bool IsNullOrUndefined(MIRType type) { 700 return type == MIRType::Null || type == MIRType::Undefined; 701 } 702 703 static inline bool IsMagicType(MIRType type) { 704 return type == MIRType::MagicHole || type == MIRType::MagicOptimizedOut || 705 type == MIRType::MagicIsConstructing || 706 type == MIRType::MagicUninitializedLexical; 707 } 708 709 static inline bool IsNonGCThing(MIRType type) { 710 return type == MIRType::Undefined || type == MIRType::Null || 711 type == MIRType::Boolean || IsNumberType(type); 712 } 713 714 static inline MIRType ScalarTypeToMIRType(Scalar::Type type) { 715 switch (type) { 716 case Scalar::Int8: 717 case Scalar::Uint8: 718 case Scalar::Int16: 719 case Scalar::Uint16: 720 case Scalar::Int32: 721 case Scalar::Uint32: 722 case Scalar::Uint8Clamped: 723 return MIRType::Int32; 724 case Scalar::Int64: 725 return MIRType::Int64; 726 case Scalar::Float32: 727 return MIRType::Float32; 728 case Scalar::Float64: 729 return MIRType::Double; 730 case Scalar::Float16: 731 case Scalar::BigInt64: 732 case Scalar::BigUint64: 733 MOZ_CRASH("NYI"); 734 case Scalar::Simd128: 735 return MIRType::Simd128; 736 case Scalar::MaxTypedArrayViewType: 737 break; 738 } 739 MOZ_CRASH("unexpected kind"); 740 } 741 742 static constexpr bool NeedsPostBarrier(MIRType type) { 743 MOZ_ASSERT(type != MIRType::Value); 744 return type == MIRType::Object || type == MIRType::String || 745 type == MIRType::BigInt; 746 } 747 748 #ifdef DEBUG 749 750 // Track the pipeline of opcodes which has produced a snapshot. 751 # define TRACK_SNAPSHOTS 1 752 753 // Make sure registers are not modified between an instruction and 754 // its OsiPoint. 755 # define CHECK_OSIPOINT_REGISTERS 1 756 757 #endif // DEBUG 758 759 // Rounding modes for round instructions. 760 enum class RoundingMode { Down, Up, NearestTiesToEven, TowardsZero }; 761 762 // If a function contains no calls, we can assume the caller has checked the 763 // stack limit up to this maximum frame size. This works because the jit stack 764 // limit has a generous buffer before the real end of the native stack. 765 static const uint32_t MAX_UNCHECKED_LEAF_FRAME_SIZE = 64; 766 767 // Truncating conversion modifiers. 768 using TruncFlags = uint32_t; 769 static const TruncFlags TRUNC_UNSIGNED = TruncFlags(1) << 0; 770 static const TruncFlags TRUNC_SATURATING = TruncFlags(1) << 1; 771 772 enum BranchDirection { FALSE_BRANCH, TRUE_BRANCH }; 773 774 template <typename T> 775 constexpr T SplatByteToUInt(uint8_t val, uint8_t x) { 776 T splatted = val; 777 for (; x > 1; x--) { 778 splatted |= splatted << 8; 779 } 780 return splatted; 781 } 782 783 // Resume information for a frame, stored in a resume point. 784 enum class ResumeMode : uint8_t { 785 // Innermost frame. Resume at the next bytecode op when bailing out. 786 ResumeAfter, 787 788 // Innermost frame. This resume point captures an additional value 789 // that is not on the expression stack. Resume at the next bytecode 790 // op when bailing out, but first check that the intermediate value 791 // is an object. This is used if calling the |return| method for a 792 // CloseIter causes an invalidation bailout. 793 ResumeAfterCheckIsObject, 794 795 // Similar to ResumeAfterCheckIsObject, but we must check that the result 796 // of a proxy get trap aligns with what the spec requires. 797 ResumeAfterCheckProxyGetResult, 798 799 // Innermost frame. Resume at the current bytecode op when bailing out. 800 ResumeAt, 801 802 // Outer frame for an inlined "standard" call at an IsInvokeOp bytecode op. 803 InlinedStandardCall, 804 805 // Outer frame for an inlined js::fun_call at an IsInvokeOp bytecode op. 806 InlinedFunCall, 807 808 // Outer frame for an inlined getter/setter at a Get*/Set* bytecode op. 809 InlinedAccessor, 810 811 Last = InlinedAccessor 812 }; 813 814 inline const char* ResumeModeToString(ResumeMode mode) { 815 switch (mode) { 816 case ResumeMode::ResumeAfter: 817 return "ResumeAfter"; 818 case ResumeMode::ResumeAt: 819 return "ResumeAt"; 820 case ResumeMode::InlinedStandardCall: 821 return "InlinedStandardCall"; 822 case ResumeMode::InlinedFunCall: 823 return "InlinedFunCall"; 824 case ResumeMode::InlinedAccessor: 825 return "InlinedAccessor"; 826 case ResumeMode::ResumeAfterCheckIsObject: 827 return "ResumeAfterCheckIsObject"; 828 case ResumeMode::ResumeAfterCheckProxyGetResult: 829 return "ResumeAfterCheckProxyGetResult"; 830 } 831 MOZ_CRASH("Invalid mode"); 832 } 833 834 inline bool IsResumeAfter(ResumeMode mode) { 835 switch (mode) { 836 case ResumeMode::ResumeAfter: 837 case ResumeMode::ResumeAfterCheckIsObject: 838 case ResumeMode::ResumeAfterCheckProxyGetResult: 839 return true; 840 default: 841 return false; 842 } 843 } 844 845 // The number of intermediate values captured by this resume point 846 // that aren't on the expression stack, but are needed during bailouts. 847 inline uint32_t NumIntermediateValues(ResumeMode mode) { 848 switch (mode) { 849 case ResumeMode::ResumeAfterCheckProxyGetResult: 850 return 2; 851 case ResumeMode::ResumeAfterCheckIsObject: 852 return 1; 853 default: 854 return 0; 855 } 856 } 857 858 // Type that represents a constant JS::Value that's either available at compile 859 // time or (for Cells allocated in the nursery) represented by an index into the 860 // nursery-Values list. 861 // 862 // See the SMDOC comment for WarpObjectField for an explanation of how this is 863 // used. 864 class ValueOrNurseryValueIndex { 865 Value val_; 866 867 explicit ValueOrNurseryValueIndex(Value val) : val_(val) {} 868 869 public: 870 static ValueOrNurseryValueIndex fromValue(Value val) { 871 ValueOrNurseryValueIndex res(val); 872 MOZ_ASSERT(res.toValue() == val); 873 return res; 874 } 875 static ValueOrNurseryValueIndex fromValueOrNurseryIndex(Value val) { 876 return ValueOrNurseryValueIndex(val); 877 } 878 static ValueOrNurseryValueIndex fromNurseryIndex(uint32_t index) { 879 MOZ_RELEASE_ASSERT(index < UINT32_MAX - JS_WHY_MAGIC_COUNT); 880 Value v = JS::MagicValueUint32(JS_WHY_MAGIC_COUNT + index); 881 ValueOrNurseryValueIndex res(v); 882 MOZ_ASSERT(res.toNurseryValueIndex() == index); 883 return res; 884 } 885 886 bool isValue() const { 887 return !val_.isMagic() || val_.magicUint32() < JS_WHY_MAGIC_COUNT; 888 } 889 Value toValue() const { 890 MOZ_ASSERT(isValue()); 891 return val_; 892 } 893 uint32_t toNurseryValueIndex() const { 894 MOZ_ASSERT(!isValue()); 895 return val_.magicUint32() - JS_WHY_MAGIC_COUNT; 896 } 897 898 uint64_t asRawBits() const { return val_.asRawBits(); } 899 900 bool operator==(ValueOrNurseryValueIndex other) const { 901 return val_ == other.val_; 902 } 903 bool operator!=(ValueOrNurseryValueIndex other) const { 904 return val_ != other.val_; 905 } 906 }; 907 908 } // namespace jit 909 } // namespace js 910 911 #endif /* jit_IonTypes_h */