tor-browser

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

WasmValType.h (31809B)


      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 *
      4 * Copyright 2021 Mozilla Foundation
      5 *
      6 * Licensed under the Apache License, Version 2.0 (the "License");
      7 * you may not use this file except in compliance with the License.
      8 * You may obtain a copy of the License at
      9 *
     10 *     http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing, software
     13 * distributed under the License is distributed on an "AS IS" BASIS,
     14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 * See the License for the specific language governing permissions and
     16 * limitations under the License.
     17 */
     18 
     19 #ifndef wasm_valtype_h
     20 #define wasm_valtype_h
     21 
     22 #include "mozilla/Maybe.h"
     23 
     24 #include "jit/IonTypes.h"
     25 #include "wasm/WasmConstants.h"
     26 #include "wasm/WasmSerialize.h"
     27 #include "wasm/WasmTypeDecls.h"
     28 
     29 namespace js {
     30 namespace wasm {
     31 
     32 // A PackedTypeCode represents any value type.
     33 union PackedTypeCode {
     34 public:
     35  using PackedRepr = uint64_t;
     36 
     37 private:
     38  static constexpr size_t NullableBits = 1;
     39  static constexpr size_t TypeCodeBits = 8;
     40  static constexpr size_t TypeDefBits = 48;
     41  static constexpr size_t PointerTagBits = 2;
     42  static constexpr size_t UnusedBits = 5;
     43 
     44  static_assert(NullableBits + TypeCodeBits + TypeDefBits + PointerTagBits +
     45                        UnusedBits ==
     46                    (sizeof(PackedRepr) * 8),
     47                "enough bits");
     48 
     49  PackedRepr bits_;
     50  struct {
     51    PackedRepr nullable_ : NullableBits;
     52    PackedRepr typeCode_ : TypeCodeBits;
     53    // A pointer to the TypeDef this type references. We use 48-bits for this,
     54    // and rely on system memory allocators not allocating outside of this
     55    // range. This is also assumed by JS::Value, and so should be safe here.
     56    PackedRepr typeDef_ : TypeDefBits;
     57    // Reserve the bottom two bits for use as a tagging scheme for BlockType
     58    // and ResultType, which can encode a ValType inside themselves in special
     59    // cases.
     60    PackedRepr pointerTag_ : PointerTagBits;
     61    // The remaining bits are unused, but still need to be explicitly
     62    // initialized to zero.
     63    PackedRepr unused_ : UnusedBits;
     64  };
     65 
     66  explicit constexpr PackedTypeCode(PackedRepr bits) : bits_(bits) {}
     67 
     68  constexpr PackedTypeCode(PackedRepr nullable, PackedRepr typeCode,
     69                           PackedRepr typeDef)
     70      : nullable_(nullable),
     71        typeCode_(typeCode),
     72        typeDef_(typeDef),
     73        pointerTag_(0),
     74        unused_(0) {}
     75 
     76 public:
     77  PackedTypeCode() = default;
     78 
     79  static constexpr PackedRepr NoTypeCode = ((uint64_t)1 << TypeCodeBits) - 1;
     80 
     81  static constexpr PackedTypeCode invalid() {
     82    return PackedTypeCode{false, NoTypeCode, 0};
     83  }
     84 
     85  static constexpr PackedTypeCode fromBits(PackedRepr bits) {
     86    return PackedTypeCode{bits};
     87  }
     88 
     89  static PackedTypeCode pack(TypeCode tc, const TypeDef* typeDef,
     90                             bool isNullable) {
     91    MOZ_ASSERT(uint32_t(tc) <= ((1 << TypeCodeBits) - 1));
     92    MOZ_ASSERT_IF(tc != AbstractTypeRefCode, typeDef == nullptr);
     93    MOZ_ASSERT_IF(tc == AbstractTypeRefCode, typeDef != nullptr);
     94    auto tydef = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(typeDef));
     95 #if defined(JS_64BIT) && defined(DEBUG)
     96    // Double check that `typeDef` only has 48 significant bits, with the top
     97    // 16 being zero.  This is necessary since we will only store the lowest
     98    // 48 bits of it, as noted above.  There's no equivalent check on 32 bit
     99    // targets since we can store the whole pointer.
    100    static_assert(sizeof(int64_t) == sizeof(uintptr_t));
    101    MOZ_ASSERT((tydef >> TypeDefBits) == 0);
    102 #endif
    103    return PackedTypeCode{isNullable, PackedRepr(tc), tydef};
    104  }
    105 
    106  static constexpr PackedTypeCode pack(TypeCode tc, bool nullable) {
    107    MOZ_ASSERT(uint32_t(tc) <= ((1 << TypeCodeBits) - 1));
    108    MOZ_ASSERT(tc != AbstractTypeRefCode);
    109    return PackedTypeCode{nullable, PackedRepr(tc), 0};
    110  }
    111 
    112  static constexpr PackedTypeCode pack(TypeCode tc) { return pack(tc, false); }
    113 
    114  constexpr bool isValid() const { return typeCode_ != NoTypeCode; }
    115 
    116  PackedRepr bits() const { return bits_; }
    117 
    118  constexpr TypeCode typeCode() const {
    119    MOZ_ASSERT(isValid());
    120    return TypeCode(typeCode_);
    121  }
    122 
    123  // Return the TypeCode, but return AbstractReferenceTypeCode for any reference
    124  // type.
    125  //
    126  // This function is very, very hot, hence what would normally be a switch on
    127  // the value `c` to map the reference types to AbstractReferenceTypeCode has
    128  // been distilled into a simple comparison; this is fastest.  Should type
    129  // codes become too complicated for this to work then a lookup table also has
    130  // better performance than a switch.
    131  //
    132  // An alternative is for the PackedTypeCode to represent something closer to
    133  // what ValType needs, so that this decoding step is not necessary, but that
    134  // moves complexity elsewhere, and the perf gain here would be only about 1%
    135  // for baseline compilation throughput.
    136  constexpr TypeCode typeCodeAbstracted() const {
    137    TypeCode tc = typeCode();
    138    return tc < LowestPrimitiveTypeCode ? AbstractReferenceTypeCode : tc;
    139  }
    140 
    141  // Return whether this type is a reference type.
    142  bool isRefType() const {
    143    return typeCodeAbstracted() == AbstractReferenceTypeCode;
    144  }
    145 
    146  // Return whether this type is represented by a reference at runtime.
    147  bool isRefRepr() const { return typeCode() < LowestPrimitiveTypeCode; }
    148 
    149  const TypeDef* typeDef() const {
    150    MOZ_ASSERT(isValid());
    151    // On a 64-bit target, this reconstitutes the pointer by zero-extending
    152    // the lowest TypeDefBits bits of `typeDef_`.  On a 32-bit target, the
    153    // pointer is stored exactly in the lowest 32 bits of `typeDef_`.
    154    return (const TypeDef*)(uintptr_t)typeDef_;
    155  }
    156 
    157  bool isNullable() const {
    158    MOZ_ASSERT(isValid());
    159    return bool(nullable_);
    160  }
    161 
    162  PackedTypeCode withIsNullable(bool nullable) const {
    163    MOZ_ASSERT(isRefType());
    164    PackedTypeCode mutated = *this;
    165    mutated.nullable_ = (PackedRepr)nullable;
    166    return mutated;
    167  }
    168 
    169  bool operator==(const PackedTypeCode& rhs) const {
    170    return bits_ == rhs.bits_;
    171  }
    172  bool operator!=(const PackedTypeCode& rhs) const {
    173    return bits_ != rhs.bits_;
    174  }
    175 };
    176 
    177 static_assert(sizeof(PackedTypeCode) == sizeof(uint64_t), "packed");
    178 
    179 // A SerializableTypeCode represents any value type in a form that can be
    180 // serialized and deserialized.
    181 union SerializableTypeCode {
    182  using PackedRepr = uintptr_t;
    183 
    184  static constexpr size_t NullableBits = 1;
    185  static constexpr size_t TypeCodeBits = 8;
    186  static constexpr size_t TypeIndexBits = 20;
    187 
    188  PackedRepr bits;
    189  struct {
    190    PackedRepr nullable : NullableBits;
    191    PackedRepr typeCode : TypeCodeBits;
    192    PackedRepr typeIndex : TypeIndexBits;
    193  };
    194 
    195  WASM_CHECK_CACHEABLE_POD(bits);
    196 
    197  static constexpr PackedRepr NoTypeIndex = (1 << TypeIndexBits) - 1;
    198 
    199  static_assert(NullableBits + TypeCodeBits + TypeIndexBits <=
    200                    (sizeof(PackedRepr) * 8),
    201                "enough bits");
    202  static_assert(NoTypeIndex < (1 << TypeIndexBits), "enough bits");
    203  static_assert(MaxTypes < NoTypeIndex, "enough bits");
    204 
    205  // Defined in WasmSerialize.cpp
    206  static inline SerializableTypeCode serialize(PackedTypeCode ptc,
    207                                               const TypeContext& types);
    208  inline PackedTypeCode deserialize(const TypeContext& types);
    209 };
    210 
    211 WASM_DECLARE_CACHEABLE_POD(SerializableTypeCode);
    212 static_assert(sizeof(SerializableTypeCode) == sizeof(uintptr_t), "packed");
    213 
    214 // [SMDOC] Comparing type definitions
    215 //
    216 // WebAssembly type equality is structural, and we implement canonicalization
    217 // such that equality of pointers to type definitions means that the type
    218 // definitions are structurally equal.
    219 //
    220 // 'IsoEquals' is the algorithm used to determine if two types are equal while
    221 // canonicalizing types.
    222 //
    223 // A iso equals type code encodes a type code for use in equality and hashing
    224 // matching. It normalizes type references that are local to a recursion group
    225 // so that they can be bitwise compared to type references from other recursion
    226 // groups.
    227 //
    228 // This is useful for the following example:
    229 //   (rec (func $a))
    230 //   (rec
    231 //     (func $b)
    232 //     (struct
    233 //       (field (ref $a)))
    234 //       (field (ref $b)))
    235 //   )
    236 //   (rec
    237 //     (func $c)
    238 //     (struct
    239 //       (field (ref $a)))
    240 //       (field (ref $c)))
    241 //   )
    242 //
    243 // The last two recursion groups are identical and should canonicalize to the
    244 // same instance. However, they will be initially represented as two separate
    245 // recursion group instances each with an array type instance with element
    246 // types that point to the function type instance before them. A bitwise
    247 // comparison of the element type pointers would fail.
    248 //
    249 // To solve this, we use `IsoEqualsTypeCode` to convert the example to:
    250 //   (rec (func $a))
    251 //   (rec
    252 //     (func $b)
    253 //     (struct
    254 //       (field (ref nonlocal $a)))
    255 //       (field (ref local 0)))
    256 //   )
    257 //   (rec
    258 //     (func $c)
    259 //     (struct
    260 //       (field (ref nonlocal $a)))
    261 //       (field (ref local 0)))
    262 //   )
    263 //
    264 // Now, comparing the element types will see that these are local type
    265 // references of the same kinds. `IsoEqualsTypeCode` performs the same mechanism
    266 // as `tie` in the MVP presentation of type equality [1].
    267 //
    268 // [1]
    269 // https://github.com/WebAssembly/gc/blob/main/proposals/gc/MVP.md#equivalence
    270 union IsoEqualsTypeCode {
    271  using PackedRepr = uint64_t;
    272 
    273  static constexpr size_t NullableBits = 1;
    274  static constexpr size_t TypeCodeBits = 8;
    275  static constexpr size_t TypeRefBits = 48;
    276 
    277  PackedRepr bits;
    278  struct {
    279    PackedRepr nullable : NullableBits;
    280    PackedRepr typeCode : TypeCodeBits;
    281    PackedRepr typeRef : TypeRefBits;
    282  };
    283 
    284  WASM_CHECK_CACHEABLE_POD(bits);
    285 
    286  static_assert(NullableBits + TypeCodeBits + TypeRefBits <=
    287                    (sizeof(PackedRepr) * 8),
    288                "enough bits");
    289 
    290  // Defined in WasmTypeDef.h to avoid a cycle while allowing inlining
    291  static inline IsoEqualsTypeCode forIsoEquals(PackedTypeCode ptc,
    292                                               const RecGroup* recGroup);
    293 
    294  bool operator==(IsoEqualsTypeCode other) const { return bits == other.bits; }
    295  bool operator!=(IsoEqualsTypeCode other) const { return bits != other.bits; }
    296  HashNumber hash() const { return HashNumber(bits); }
    297 };
    298 
    299 // An enum that describes the representation classes for tables; The table
    300 // element type is mapped into this by Table::repr().
    301 
    302 enum class TableRepr { Ref, Func };
    303 
    304 // An enum that describes the different type hierarchies.
    305 
    306 enum class RefTypeHierarchy { Func, Extern, Exn, Any };
    307 
    308 // The RefType carries more information about types t for which t.isRefType()
    309 // is true.
    310 
    311 class RefType {
    312 public:
    313  enum Kind {
    314    Func = uint8_t(TypeCode::FuncRef),
    315    Extern = uint8_t(TypeCode::ExternRef),
    316    Exn = uint8_t(TypeCode::ExnRef),
    317    Any = uint8_t(TypeCode::AnyRef),
    318    NoFunc = uint8_t(TypeCode::NullFuncRef),
    319    NoExtern = uint8_t(TypeCode::NullExternRef),
    320    NoExn = uint8_t(TypeCode::NullExnRef),
    321    None = uint8_t(TypeCode::NullAnyRef),
    322    Eq = uint8_t(TypeCode::EqRef),
    323    I31 = uint8_t(TypeCode::I31Ref),
    324    Struct = uint8_t(TypeCode::StructRef),
    325    Array = uint8_t(TypeCode::ArrayRef),
    326    TypeRef = uint8_t(AbstractTypeRefCode)
    327  };
    328 
    329 private:
    330  PackedTypeCode ptc_;
    331 
    332  RefType(Kind kind, bool nullable)
    333      : ptc_(PackedTypeCode::pack(TypeCode(kind), nullable)) {
    334    MOZ_ASSERT(isValid());
    335  }
    336 
    337  RefType(const TypeDef* typeDef, bool nullable)
    338      : ptc_(PackedTypeCode::pack(AbstractTypeRefCode, typeDef, nullable)) {
    339    MOZ_ASSERT(isValid());
    340  }
    341 
    342 public:
    343  RefType() : ptc_(PackedTypeCode::invalid()) {}
    344  explicit RefType(PackedTypeCode ptc) : ptc_(ptc) { MOZ_ASSERT(isValid()); }
    345 
    346  static RefType fromKind(Kind kind, bool nullable) {
    347    MOZ_ASSERT(kind != TypeRef);
    348    return RefType(kind, nullable);
    349  }
    350 
    351  static RefType fromTypeCode(TypeCode tc, bool nullable) {
    352    MOZ_ASSERT(tc != AbstractTypeRefCode);
    353    return RefType(Kind(tc), nullable);
    354  }
    355 
    356  static RefType fromTypeDef(const TypeDef* typeDef, bool nullable) {
    357    return RefType(typeDef, nullable);
    358  }
    359 
    360  Kind kind() const { return Kind(ptc_.typeCode()); }
    361 
    362  const TypeDef* typeDef() const { return ptc_.typeDef(); }
    363 
    364  PackedTypeCode packed() const { return ptc_; }
    365  PackedTypeCode* addressOfPacked() { return &ptc_; }
    366  const PackedTypeCode* addressOfPacked() const { return &ptc_; }
    367 
    368  // Further restricts PackedTypeCode::isValid() to only those TypeCodes which
    369  // represent a wasm reference type.
    370  bool isValid() const {
    371    if (!ptc_.isValid()) {
    372      return false;
    373    }
    374 
    375    MOZ_ASSERT((ptc_.typeCode() == AbstractTypeRefCode) ==
    376               (ptc_.typeDef() != nullptr));
    377    switch (ptc_.typeCode()) {
    378      case TypeCode::FuncRef:
    379      case TypeCode::ExternRef:
    380      case TypeCode::ExnRef:
    381      case TypeCode::AnyRef:
    382      case TypeCode::EqRef:
    383      case TypeCode::I31Ref:
    384      case TypeCode::StructRef:
    385      case TypeCode::ArrayRef:
    386      case TypeCode::NullFuncRef:
    387      case TypeCode::NullExternRef:
    388      case TypeCode::NullExnRef:
    389      case TypeCode::NullAnyRef:
    390      case AbstractTypeRefCode:
    391        return true;
    392      default:
    393        return false;
    394    }
    395  }
    396 
    397  static RefType func() { return RefType(Func, true); }
    398  static RefType extern_() { return RefType(Extern, true); }
    399  static RefType exn() { return RefType(Exn, true); }
    400  static RefType any() { return RefType(Any, true); }
    401  static RefType nofunc() { return RefType(NoFunc, true); }
    402  static RefType noextern() { return RefType(NoExtern, true); }
    403  static RefType noexn() { return RefType(NoExn, true); }
    404  static RefType none() { return RefType(None, true); }
    405  static RefType eq() { return RefType(Eq, true); }
    406  static RefType i31() { return RefType(I31, true); }
    407  static RefType struct_() { return RefType(Struct, true); }
    408  static RefType array() { return RefType(Array, true); }
    409 
    410  bool isFunc() const { return kind() == RefType::Func; }
    411  bool isExtern() const { return kind() == RefType::Extern; }
    412  bool isAny() const { return kind() == RefType::Any; }
    413  bool isNoFunc() const { return kind() == RefType::NoFunc; }
    414  bool isNoExtern() const { return kind() == RefType::NoExtern; }
    415  bool isNoExn() const { return kind() == RefType::NoExn; }
    416  bool isNone() const { return kind() == RefType::None; }
    417  bool isEq() const { return kind() == RefType::Eq; }
    418  bool isI31() const { return kind() == RefType::I31; }
    419  bool isStruct() const { return kind() == RefType::Struct; }
    420  bool isArray() const { return kind() == RefType::Array; }
    421  bool isTypeRef() const { return kind() == RefType::TypeRef; }
    422 
    423  bool isNullable() const { return bool(ptc_.isNullable()); }
    424  RefType asNonNullable() const { return withIsNullable(false); }
    425  RefType withIsNullable(bool nullable) const {
    426    return RefType(ptc_.withIsNullable(nullable));
    427  }
    428 
    429  bool isRefBottom() const {
    430    return isNone() || isNoFunc() || isNoExtern() || isNoExn();
    431  }
    432 
    433  // These methods are defined in WasmTypeDef.h to avoid a cycle while allowing
    434  // inlining.
    435  inline RefTypeHierarchy hierarchy() const;
    436  inline TableRepr tableRepr() const;
    437  inline bool isFuncHierarchy() const;
    438  inline bool isExternHierarchy() const;
    439  inline bool isAnyHierarchy() const;
    440  inline bool isExnHierarchy() const;
    441  static bool isSubTypeOf(RefType subType, RefType superType);
    442  static bool castPossible(RefType sourceType, RefType destType);
    443 
    444  // If we have two references, one of type `a` and one of type `b`, return
    445  // true if there is any possibility that they might point at the same thing.
    446  // That can only happen if either they are the same type or if one type is a
    447  // subtype of the other.  Note, this can only be used for types in the same
    448  // hierarchy.
    449  static bool valuesMightAlias(RefType a, RefType b) {
    450    MOZ_RELEASE_ASSERT(a.hierarchy() == b.hierarchy());
    451    // The exact-same-type case is subsumed by `isSubTypeOf`.
    452    return RefType::isSubTypeOf(a, b) || RefType::isSubTypeOf(b, a);
    453  }
    454 
    455  // Gets the top of the given type's hierarchy, e.g. Any for structs and
    456  // arrays, and Func for funcs.
    457  RefType topType() const;
    458 
    459  // Gets the bottom of the given type's hierarchy, e.g. None for structs and
    460  // arrays, and NoFunc for funcs.
    461  RefType bottomType() const;
    462 
    463  static RefType leastUpperBound(RefType a, RefType b);
    464 
    465  // Gets the TypeDefKind associated with this RefType, e.g. TypeDefKind::Struct
    466  // for RefType::Struct.
    467  TypeDefKind typeDefKind() const;
    468 
    469  inline void AddRef() const;
    470  inline void Release() const;
    471 
    472  bool operator==(const RefType& that) const { return ptc_ == that.ptc_; }
    473  bool operator!=(const RefType& that) const { return ptc_ != that.ptc_; }
    474 };
    475 
    476 using RefTypeVector = Vector<RefType, 0, SystemAllocPolicy>;
    477 
    478 class StorageTypeTraits {
    479 public:
    480  enum Kind {
    481    I8 = uint8_t(TypeCode::I8),
    482    I16 = uint8_t(TypeCode::I16),
    483    I32 = uint8_t(TypeCode::I32),
    484    I64 = uint8_t(TypeCode::I64),
    485    F32 = uint8_t(TypeCode::F32),
    486    F64 = uint8_t(TypeCode::F64),
    487    V128 = uint8_t(TypeCode::V128),
    488    Ref = uint8_t(AbstractReferenceTypeCode),
    489  };
    490 
    491  static bool isValidTypeCode(TypeCode tc) {
    492    switch (tc) {
    493      case TypeCode::I8:
    494      case TypeCode::I16:
    495      case TypeCode::I32:
    496      case TypeCode::I64:
    497      case TypeCode::F32:
    498      case TypeCode::F64:
    499 #ifdef ENABLE_WASM_SIMD
    500      case TypeCode::V128:
    501 #endif
    502      case TypeCode::FuncRef:
    503      case TypeCode::ExternRef:
    504      case TypeCode::ExnRef:
    505      case TypeCode::NullExnRef:
    506      case TypeCode::AnyRef:
    507      case TypeCode::EqRef:
    508      case TypeCode::I31Ref:
    509      case TypeCode::StructRef:
    510      case TypeCode::ArrayRef:
    511      case TypeCode::NullFuncRef:
    512      case TypeCode::NullExternRef:
    513      case TypeCode::NullAnyRef:
    514      case AbstractTypeRefCode:
    515        return true;
    516      default:
    517        return false;
    518    }
    519  }
    520 
    521  static bool isNumberTypeCode(TypeCode tc) {
    522    switch (tc) {
    523      case TypeCode::I32:
    524      case TypeCode::I64:
    525      case TypeCode::F32:
    526      case TypeCode::F64:
    527        return true;
    528      default:
    529        return false;
    530    }
    531  }
    532 
    533  static bool isPackedTypeCode(TypeCode tc) {
    534    switch (tc) {
    535      case TypeCode::I8:
    536      case TypeCode::I16:
    537        return true;
    538      default:
    539        return false;
    540    }
    541  }
    542 
    543  static bool isVectorTypeCode(TypeCode tc) {
    544    switch (tc) {
    545 #ifdef ENABLE_WASM_SIMD
    546      case TypeCode::V128:
    547        return true;
    548 #endif
    549      default:
    550        return false;
    551    }
    552  }
    553 };
    554 
    555 class ValTypeTraits {
    556 public:
    557  enum Kind {
    558    I32 = uint8_t(TypeCode::I32),
    559    I64 = uint8_t(TypeCode::I64),
    560    F32 = uint8_t(TypeCode::F32),
    561    F64 = uint8_t(TypeCode::F64),
    562    V128 = uint8_t(TypeCode::V128),
    563    Ref = uint8_t(AbstractReferenceTypeCode),
    564  };
    565 
    566  static const char* KindEnumName(Kind kind) {
    567    switch (kind) {
    568      case Kind::I32:
    569        return "I32";
    570      case Kind::I64:
    571        return "I64";
    572      case Kind::F32:
    573        return "F32";
    574      case Kind::F64:
    575        return "F64";
    576      case Kind::V128:
    577        return "V128";
    578      case Kind::Ref:
    579        return "Ref";
    580    }
    581    MOZ_CRASH("Unknown kind");
    582  }
    583 
    584  static constexpr bool isValidTypeCode(TypeCode tc) {
    585    switch (tc) {
    586      case TypeCode::I32:
    587      case TypeCode::I64:
    588      case TypeCode::F32:
    589      case TypeCode::F64:
    590 #ifdef ENABLE_WASM_SIMD
    591      case TypeCode::V128:
    592 #endif
    593      case TypeCode::FuncRef:
    594      case TypeCode::ExternRef:
    595      case TypeCode::ExnRef:
    596      case TypeCode::NullExnRef:
    597      case TypeCode::AnyRef:
    598      case TypeCode::EqRef:
    599      case TypeCode::I31Ref:
    600      case TypeCode::StructRef:
    601      case TypeCode::ArrayRef:
    602      case TypeCode::NullFuncRef:
    603      case TypeCode::NullExternRef:
    604      case TypeCode::NullAnyRef:
    605      case AbstractTypeRefCode:
    606        return true;
    607      default:
    608        return false;
    609    }
    610  }
    611 
    612  static bool isNumberTypeCode(TypeCode tc) {
    613    switch (tc) {
    614      case TypeCode::I32:
    615      case TypeCode::I64:
    616      case TypeCode::F32:
    617      case TypeCode::F64:
    618        return true;
    619      default:
    620        return false;
    621    }
    622  }
    623 
    624  static bool isPackedTypeCode(TypeCode tc) { return false; }
    625 
    626  static bool isVectorTypeCode(TypeCode tc) {
    627    switch (tc) {
    628 #ifdef ENABLE_WASM_SIMD
    629      case TypeCode::V128:
    630        return true;
    631 #endif
    632      default:
    633        return false;
    634    }
    635  }
    636 };
    637 
    638 // The PackedType represents the storage type of a WebAssembly location, whether
    639 // parameter, local, field, or global. See specializations below for ValType and
    640 // StorageType.
    641 
    642 template <class T>
    643 class PackedType : public T {
    644 public:
    645  using Kind = typename T::Kind;
    646 
    647 protected:
    648  PackedTypeCode tc_;
    649 
    650  explicit PackedType(TypeCode c) : tc_(PackedTypeCode::pack(c)) {
    651    MOZ_ASSERT(c != AbstractTypeRefCode);
    652    MOZ_ASSERT(isValid());
    653  }
    654 
    655  TypeCode typeCode() const {
    656    MOZ_ASSERT(isValid());
    657    return tc_.typeCode();
    658  }
    659 
    660 public:
    661  constexpr PackedType() : tc_(PackedTypeCode::invalid()) {}
    662 
    663  MOZ_IMPLICIT constexpr PackedType(Kind c)
    664      : tc_(PackedTypeCode::pack(TypeCode(c))) {
    665    MOZ_ASSERT(c != Kind::Ref);
    666    MOZ_ASSERT(isValid());
    667  }
    668 
    669  MOZ_IMPLICIT PackedType(RefType rt) : tc_(rt.packed()) {
    670    MOZ_ASSERT(isValid());
    671  }
    672 
    673  explicit constexpr PackedType(PackedTypeCode ptc) : tc_(ptc) {
    674    MOZ_ASSERT(isValid());
    675  }
    676 
    677  inline void AddRef() const;
    678  inline void Release() const;
    679 
    680  static constexpr PackedType i32() { return PackedType(PackedType::I32); }
    681  static constexpr PackedType f32() { return PackedType(PackedType::F32); }
    682  static constexpr PackedType i64() { return PackedType(PackedType::I64); }
    683  static constexpr PackedType f64() { return PackedType(PackedType::F64); }
    684 
    685  static PackedType fromMIRType(jit::MIRType mty) {
    686    switch (mty) {
    687      case jit::MIRType::Int32:
    688        return PackedType::I32;
    689        break;
    690      case jit::MIRType::Int64:
    691        return PackedType::I64;
    692        break;
    693      case jit::MIRType::Float32:
    694        return PackedType::F32;
    695        break;
    696      case jit::MIRType::Double:
    697        return PackedType::F64;
    698        break;
    699      case jit::MIRType::Simd128:
    700        return PackedType::V128;
    701        break;
    702      case jit::MIRType::WasmAnyRef:
    703        return PackedType::Ref;
    704      default:
    705        MOZ_CRASH("fromMIRType: unexpected type");
    706    }
    707  }
    708 
    709  static PackedType fromNonRefTypeCode(TypeCode tc) {
    710 #ifdef DEBUG
    711    switch (tc) {
    712      case TypeCode::I8:
    713      case TypeCode::I16:
    714      case TypeCode::I32:
    715      case TypeCode::I64:
    716      case TypeCode::F32:
    717      case TypeCode::F64:
    718      case TypeCode::V128:
    719        break;
    720      default:
    721        MOZ_CRASH("Bad type code");
    722    }
    723 #endif
    724    return PackedType(tc);
    725  }
    726 
    727  static PackedType fromBitsUnsafe(PackedTypeCode::PackedRepr bits) {
    728    return PackedType(PackedTypeCode::fromBits(bits));
    729  }
    730 
    731  constexpr bool isValid() const {
    732    if (!tc_.isValid()) {
    733      return false;
    734    }
    735    return T::isValidTypeCode(tc_.typeCode());
    736  }
    737 
    738  IsoEqualsTypeCode forIsoEquals(const RecGroup* recGroup) const {
    739    return IsoEqualsTypeCode::forIsoEquals(tc_, recGroup);
    740  }
    741 
    742  PackedTypeCode packed() const {
    743    MOZ_ASSERT(isValid());
    744    return tc_;
    745  }
    746  PackedTypeCode* addressOfPacked() { return &tc_; }
    747  const PackedTypeCode* addressOfPacked() const { return &tc_; }
    748 
    749  PackedTypeCode::PackedRepr bitsUnsafe() const {
    750    MOZ_ASSERT(isValid());
    751    return tc_.bits();
    752  }
    753 
    754  bool isNumber() const { return T::isNumberTypeCode(tc_.typeCode()); }
    755 
    756  bool isPacked() const { return T::isPackedTypeCode(tc_.typeCode()); }
    757 
    758  bool isVector() const { return T::isVectorTypeCode(tc_.typeCode()); }
    759 
    760  bool isRefType() const { return tc_.isRefType(); }
    761 
    762  bool isFuncRef() const { return tc_.typeCode() == TypeCode::FuncRef; }
    763 
    764  bool isExternRef() const { return tc_.typeCode() == TypeCode::ExternRef; }
    765 
    766  bool isExnRef() const { return tc_.typeCode() == TypeCode::ExnRef; }
    767 
    768  bool isAnyRef() const { return tc_.typeCode() == TypeCode::AnyRef; }
    769 
    770  bool isNoFunc() const { return tc_.typeCode() == TypeCode::NullFuncRef; }
    771 
    772  bool isNoExtern() const { return tc_.typeCode() == TypeCode::NullExternRef; }
    773 
    774  bool isNoExn() const { return tc_.typeCode() == TypeCode::NullExnRef; }
    775 
    776  bool isNone() const { return tc_.typeCode() == TypeCode::NullAnyRef; }
    777 
    778  bool isEqRef() const { return tc_.typeCode() == TypeCode::EqRef; }
    779 
    780  bool isI31Ref() const { return tc_.typeCode() == TypeCode::I31Ref; }
    781 
    782  bool isStructRef() const { return tc_.typeCode() == TypeCode::StructRef; }
    783 
    784  bool isArrayRef() const { return tc_.typeCode() == TypeCode::ArrayRef; }
    785 
    786  bool isTypeRef() const { return tc_.typeCode() == AbstractTypeRefCode; }
    787 
    788  bool isRefRepr() const { return tc_.isRefRepr(); }
    789 
    790  // Returns whether the type has a default value.
    791  bool isDefaultable() const { return !(isRefType() && !isNullable()); }
    792 
    793  // Returns whether the type has a representation in JS.
    794  bool isExposable() const {
    795 #if defined(ENABLE_WASM_SIMD)
    796    return kind() != Kind::V128 && !isExnRef() && !isNoExn();
    797 #else
    798    return !isExnRef() && !isNoExn();
    799 #endif
    800  }
    801 
    802  bool isNullable() const { return tc_.isNullable(); }
    803 
    804  const TypeDef* typeDef() const { return tc_.typeDef(); }
    805 
    806  Kind kind() const { return Kind(tc_.typeCodeAbstracted()); }
    807 
    808  RefType refType() const {
    809    MOZ_ASSERT(isRefType());
    810    return RefType(tc_);
    811  }
    812 
    813  RefType::Kind refTypeKind() const {
    814    MOZ_ASSERT(isRefType());
    815    return RefType(tc_).kind();
    816  }
    817 
    818  // Some types are encoded as JS::Value when they escape from Wasm (when passed
    819  // as parameters to imports or returned from exports).  For ExternRef the
    820  // Value encoding is pretty much a requirement.  For other types it's a choice
    821  // that may (temporarily) simplify some code.
    822  bool isEncodedAsJSValueOnEscape() const { return isRefType(); }
    823 
    824  uint32_t size() const {
    825    switch (tc_.typeCodeAbstracted()) {
    826      case TypeCode::I8:
    827        return 1;
    828      case TypeCode::I16:
    829        return 2;
    830      case TypeCode::I32:
    831        return 4;
    832      case TypeCode::I64:
    833        return 8;
    834      case TypeCode::F32:
    835        return 4;
    836      case TypeCode::F64:
    837        return 8;
    838      case TypeCode::V128:
    839        return 16;
    840      case AbstractReferenceTypeCode:
    841        return sizeof(void*);
    842      default:
    843        MOZ_ASSERT_UNREACHABLE();
    844        return 0;
    845    }
    846  }
    847  uint32_t alignmentInStruct() const { return size(); }
    848  uint32_t indexingShift() const {
    849    switch (size()) {
    850      case 1:
    851        return 0;
    852      case 2:
    853        return 1;
    854      case 4:
    855        return 2;
    856      case 8:
    857        return 3;
    858      case 16:
    859        return 4;
    860      default:
    861        MOZ_ASSERT_UNREACHABLE();
    862        return 0;
    863    }
    864  }
    865 
    866  PackedType<ValTypeTraits> widenToValType() const {
    867    switch (tc_.typeCodeAbstracted()) {
    868      case TypeCode::I8:
    869      case TypeCode::I16:
    870        return PackedType<ValTypeTraits>::I32;
    871      default:
    872        return PackedType<ValTypeTraits>(tc_);
    873    }
    874  }
    875 
    876  PackedType<ValTypeTraits> valType() const {
    877    MOZ_ASSERT(isValType());
    878    return PackedType<ValTypeTraits>(tc_);
    879  }
    880 
    881  // Note, ToMIRType is only correct within Wasm, where an AnyRef is represented
    882  // as a pointer.  At the JS/wasm boundary, an AnyRef can be represented as a
    883  // JS::Value, and the type translation may have to be handled specially and on
    884  // a case-by-case basis.
    885  constexpr jit::MIRType toMIRType() const {
    886    switch (tc_.typeCodeAbstracted()) {
    887      case TypeCode::I32:
    888        return jit::MIRType::Int32;
    889      case TypeCode::I64:
    890        return jit::MIRType::Int64;
    891      case TypeCode::F32:
    892        return jit::MIRType::Float32;
    893      case TypeCode::F64:
    894        return jit::MIRType::Double;
    895      case TypeCode::V128:
    896        return jit::MIRType::Simd128;
    897      case AbstractReferenceTypeCode:
    898        return jit::MIRType::WasmAnyRef;
    899      default:
    900        MOZ_CRASH("bad type");
    901    }
    902  }
    903 
    904  MaybeRefType toMaybeRefType() const;
    905 
    906  bool isValType() const {
    907    switch (tc_.typeCode()) {
    908      case TypeCode::I8:
    909      case TypeCode::I16:
    910        return false;
    911      default:
    912        return true;
    913    }
    914  }
    915 
    916  PackedType<StorageTypeTraits> storageType() const {
    917    MOZ_ASSERT(isValid());
    918    return PackedType<StorageTypeTraits>(tc_);
    919  }
    920 
    921  static bool isSubTypeOf(PackedType subType, PackedType superType) {
    922    // Anything is a subtype of itself.
    923    if (subType == superType) {
    924      return true;
    925    }
    926 
    927    // A reference may be a subtype of another reference
    928    if (subType.isRefType() && superType.isRefType()) {
    929      return RefType::isSubTypeOf(subType.refType(), superType.refType());
    930    }
    931 
    932    return false;
    933  }
    934 
    935  bool operator==(const PackedType& that) const {
    936    MOZ_ASSERT(isValid() && that.isValid());
    937    return tc_ == that.tc_;
    938  }
    939 
    940  bool operator!=(const PackedType& that) const {
    941    MOZ_ASSERT(isValid() && that.isValid());
    942    return tc_ != that.tc_;
    943  }
    944 
    945  bool operator==(Kind that) const {
    946    MOZ_ASSERT(isValid());
    947    MOZ_ASSERT(that != Kind::Ref);
    948    return Kind(typeCode()) == that;
    949  }
    950 
    951  bool operator!=(Kind that) const { return !(*this == that); }
    952 };
    953 
    954 using StorageType = PackedType<StorageTypeTraits>;
    955 
    956 // The dominant use of this data type is for locals and args, and profiling
    957 // with ZenGarden and Tanks suggests an initial size of 16 minimises heap
    958 // allocation, both in terms of blocks and bytes.
    959 using ValTypeVector = Vector<ValType, 16, SystemAllocPolicy>;
    960 
    961 // A MaybeRefType behaves like a mozilla::Maybe<RefType>, but saves space by
    962 // reusing PackedTypeCode. If the inner RefType is not valid, it will be
    963 // considered Nothing; otherwise it will be considered Some.
    964 class MaybeRefType {
    965 private:
    966  RefType inner_;
    967 
    968 public:
    969  // Creates a MaybeRefType that isNothing().
    970  MaybeRefType() { MOZ_ASSERT(isNothing()); }
    971 
    972  // Creates a MaybeRefType that isSome().
    973  explicit MaybeRefType(RefType type) : inner_(type) {
    974    MOZ_RELEASE_ASSERT(isSome());
    975  }
    976 
    977  bool isSome() const { return inner_.isValid(); }
    978  bool isNothing() const { return !isSome(); }
    979 
    980  /* Returns the inner RefType by value. Unsafe unless |isSome()|. */
    981  RefType& value() & {
    982    MOZ_RELEASE_ASSERT(isSome());
    983    return inner_;
    984  };
    985  const RefType& value() const& {
    986    MOZ_RELEASE_ASSERT(isSome());
    987    return inner_;
    988  };
    989 
    990  RefType& valueOr(RefType& aDefault) {
    991    if (isSome()) {
    992      return value();
    993    }
    994    return aDefault;
    995  }
    996  const RefType& valueOr(const RefType& aDefault) {
    997    if (isSome()) {
    998      return value();
    999    }
   1000    return aDefault;
   1001  }
   1002 
   1003  bool operator==(const MaybeRefType& other) { return inner_ == other.inner_; }
   1004  bool operator!=(const MaybeRefType& other) { return inner_ != other.inner_; }
   1005 
   1006  explicit operator bool() const { return isSome(); }
   1007 
   1008  mozilla::Maybe<wasm::RefTypeHierarchy> hierarchy() const {
   1009    if (isSome()) {
   1010      return mozilla::Some(value().hierarchy());
   1011    }
   1012    return mozilla::Nothing();
   1013  }
   1014 
   1015  static MaybeRefType leastUpperBound(MaybeRefType a, MaybeRefType b) {
   1016    if (a.isSome() && b.isSome()) {
   1017      return MaybeRefType(RefType::leastUpperBound(a.value(), b.value()));
   1018    }
   1019    return MaybeRefType();
   1020  }
   1021 };
   1022 
   1023 template <class T>
   1024 MaybeRefType PackedType<T>::toMaybeRefType() const {
   1025  if (!isRefType()) {
   1026    return MaybeRefType();
   1027  }
   1028  return MaybeRefType(refType());
   1029 };
   1030 
   1031 // ValType utilities
   1032 
   1033 extern bool ToValType(JSContext* cx, HandleValue v, ValType* out);
   1034 extern bool ToRefType(JSContext* cx, HandleValue v, RefType* out);
   1035 
   1036 extern UniqueChars ToString(RefType type, const TypeContext* types);
   1037 extern UniqueChars ToString(ValType type, const TypeContext* types);
   1038 extern UniqueChars ToString(StorageType type, const TypeContext* types);
   1039 extern UniqueChars ToString(const mozilla::Maybe<ValType>& type,
   1040                            const TypeContext* types);
   1041 extern UniqueChars ToString(const MaybeRefType& type, const TypeContext* types);
   1042 
   1043 }  // namespace wasm
   1044 }  // namespace js
   1045 
   1046 #endif  // wasm_valtype_h