tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 */