tor-browser

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

WasmTypeDef.cpp (18428B)


      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 2015 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 #include "wasm/WasmTypeDef.h"
     20 
     21 #include "mozilla/MathAlgorithms.h"
     22 
     23 #include "jit/JitOptions.h"
     24 #include "js/friend/ErrorMessages.h"  // JSMSG_*
     25 #include "js/HashTable.h"
     26 #include "js/Printf.h"
     27 #include "js/Value.h"
     28 #include "threading/ExclusiveData.h"
     29 #include "vm/Runtime.h"
     30 #include "vm/StringType.h"
     31 #include "wasm/WasmCodegenConstants.h"
     32 #include "wasm/WasmGcObject.h"
     33 #include "wasm/WasmJS.h"
     34 
     35 #include "gc/ObjectKind-inl.h"
     36 
     37 using namespace js;
     38 using namespace js::jit;
     39 using namespace js::wasm;
     40 
     41 using mozilla::CheckedInt32;
     42 using mozilla::CheckedUint32;
     43 using mozilla::IsPowerOfTwo;
     44 using mozilla::MallocSizeOf;
     45 
     46 // [SMDOC] Immediate type signature encoding
     47 //
     48 // call_indirect requires a signature check to ensure the dynamic callee type
     49 // matches the static specified callee type. This involves comparing whether
     50 // the two function types are equal. We canonicalize function types so that
     51 // comparing the pointers of the types will indicate if they're equal. The
     52 // canonicalized function types are loaded from the instance at runtime.
     53 //
     54 // For the common case of simple/small function types, we can avoid the cost
     55 // of loading the function type pointers from the instance by having an
     56 // alternate 'immediate' form that encodes a function type in a constant.
     57 // We encode the function types such that bitwise equality implies the original
     58 // function types were equal. We use a tag bit such that if one of the types
     59 // is a pointer and the other an immediate, they will not compare as equal.
     60 //
     61 // The encoding is optimized for common function types that have at most one
     62 // result and an arbitrary amount of arguments.
     63 //
     64 // [
     65 //   1 bit : tag (always 1),
     66 //   1 bit : numResults,
     67 //   3 bits : numArgs,
     68 //   numResults * 3 bits : results,
     69 //   numArgs * 3 bits : args
     70 // ]
     71 // (lsb -> msb order)
     72 //
     73 // Any function type that cannot be encoded in the above format is falls back
     74 // to the pointer representation.
     75 //
     76 
     77 //=========================================================================
     78 // ImmediateType
     79 
     80 // ImmediateType is 32-bits to ensure it's easy to materialize the constant
     81 // on all platforms.
     82 using ImmediateType = uint32_t;
     83 static const unsigned sTotalBits = sizeof(ImmediateType) * 8;
     84 static const unsigned sTagBits = 1;
     85 static const unsigned sNumResultsBits = 1;
     86 static const unsigned sNumArgsBits = 3;
     87 static const unsigned sValTypeBits = 3;
     88 static const unsigned sMaxValTypes = 8;
     89 
     90 static_assert(((1 << sNumResultsBits) - 1) + ((1 << sNumArgsBits) - 1) ==
     91                  sMaxValTypes,
     92              "sNumResultsBits, sNumArgsBits, sMaxValTypes are consistent");
     93 
     94 static_assert(sTagBits + sNumResultsBits + sNumArgsBits +
     95                      sValTypeBits * sMaxValTypes <=
     96                  sTotalBits,
     97              "have room");
     98 
     99 static bool IsImmediateValType(ValType vt) {
    100  switch (vt.kind()) {
    101    case ValType::I32:
    102    case ValType::I64:
    103    case ValType::F32:
    104    case ValType::F64:
    105    case ValType::V128:
    106      return true;
    107    case ValType::Ref:
    108      // We don't have space to encode nullability, so we optimize for
    109      // non-nullable types.
    110      if (!vt.isNullable()) {
    111        return false;
    112      }
    113      switch (vt.refType().kind()) {
    114        case RefType::Func:
    115        case RefType::Extern:
    116        case RefType::Any:
    117          return true;
    118        default:
    119          return false;
    120      }
    121    default:
    122      return false;
    123  }
    124 }
    125 
    126 static unsigned EncodeImmediateValType(ValType vt) {
    127  // We have run out of bits for each type, anything new must increase the
    128  // sValTypeBits.
    129  static_assert(7 < (1 << sValTypeBits), "enough space for ValType kind");
    130 
    131  switch (vt.kind()) {
    132    case ValType::I32:
    133      return 0;
    134    case ValType::I64:
    135      return 1;
    136    case ValType::F32:
    137      return 2;
    138    case ValType::F64:
    139      return 3;
    140    case ValType::V128:
    141      return 4;
    142    case ValType::Ref:
    143      MOZ_ASSERT(vt.isNullable());
    144      switch (vt.refType().kind()) {
    145        case RefType::Func:
    146          return 5;
    147        case RefType::Extern:
    148          return 6;
    149        case RefType::Any:
    150          return 7;
    151        default:
    152          MOZ_CRASH("bad RefType");
    153      }
    154    default:
    155      MOZ_CRASH("bad ValType");
    156  }
    157 }
    158 
    159 static bool IsImmediateFuncType(const FuncType& funcType) {
    160  const ValTypeVector& results = funcType.results();
    161  const ValTypeVector& args = funcType.args();
    162 
    163  // Check the number of results and args fits
    164  if (results.length() > ((1 << sNumResultsBits) - 1) ||
    165      args.length() > ((1 << sNumArgsBits) - 1)) {
    166    return false;
    167  }
    168 
    169  // Ensure every result is compatible
    170  for (ValType v : results) {
    171    if (!IsImmediateValType(v)) {
    172      return false;
    173    }
    174  }
    175 
    176  // Ensure every arg is compatible
    177  for (ValType v : args) {
    178    if (!IsImmediateValType(v)) {
    179      return false;
    180    }
    181  }
    182 
    183  return true;
    184 }
    185 
    186 static ImmediateType EncodeNumResults(uint32_t numResults) {
    187  MOZ_ASSERT(numResults <= (1 << sNumResultsBits) - 1);
    188  return numResults;
    189 }
    190 
    191 static ImmediateType EncodeNumArgs(uint32_t numArgs) {
    192  MOZ_ASSERT(numArgs <= (1 << sNumArgsBits) - 1);
    193  return numArgs;
    194 }
    195 
    196 static ImmediateType EncodeImmediateFuncType(const FuncType& funcType) {
    197  ImmediateType immediate = FuncType::ImmediateBit;
    198  uint32_t shift = sTagBits;
    199 
    200  // Encode the results
    201  immediate |= EncodeNumResults(funcType.results().length()) << shift;
    202  shift += sNumResultsBits;
    203 
    204  for (ValType resultType : funcType.results()) {
    205    immediate |= EncodeImmediateValType(resultType) << shift;
    206    shift += sValTypeBits;
    207  }
    208 
    209  // Encode the args
    210  immediate |= EncodeNumArgs(funcType.args().length()) << shift;
    211  shift += sNumArgsBits;
    212 
    213  for (ValType argType : funcType.args()) {
    214    immediate |= EncodeImmediateValType(argType) << shift;
    215    shift += sValTypeBits;
    216  }
    217 
    218  MOZ_ASSERT(shift <= sTotalBits);
    219  return immediate;
    220 }
    221 
    222 //=========================================================================
    223 // FuncType
    224 
    225 void FuncType::initImmediateTypeId(bool isFinal, const TypeDef* superTypeDef,
    226                                   uint32_t recGroupLength) {
    227  // To improve the performance of the structural type check in
    228  // the call_indirect function prologue, we attempt to encode the
    229  // entire function type into an immediate such that bitwise equality
    230  // implies structural equality. With the GC proposal, we don't
    231  // want to generalize the immediate form for the new type system, so
    232  // we don't use it when a type is non-final (i.e. may have sub types), or
    233  // has super types, or is in a recursion group with other types.
    234  //
    235  // If non-final types are allowed, then the type can have subtypes, and we
    236  // should therefore do a full subtype check on call_indirect, which
    237  // doesn't work well with immediates. If the type has a super type, the
    238  // same reason applies. And finally, types in recursion groups of
    239  // size > 1 may not be considered equivalent even if they are
    240  // structurally equivalent in every respect.
    241  if (!isFinal || superTypeDef || recGroupLength != 1) {
    242    immediateTypeId_ = NO_IMMEDIATE_TYPE_ID;
    243    return;
    244  }
    245 
    246  // Otherwise, try to encode this function type into an immediate.
    247  if (!IsImmediateFuncType(*this)) {
    248    immediateTypeId_ = NO_IMMEDIATE_TYPE_ID;
    249    return;
    250  }
    251  immediateTypeId_ = EncodeImmediateFuncType(*this);
    252 }
    253 
    254 bool FuncType::canHaveJitEntry() const {
    255  return !hasUnexposableArgOrRet() &&
    256         !temporarilyUnsupportedReftypeForEntry() &&
    257         !temporarilyUnsupportedResultCountForJitEntry() &&
    258         JitOptions.enableWasmJitEntry;
    259 }
    260 
    261 bool FuncType::canHaveJitExit() const {
    262  return !hasUnexposableArgOrRet() && !temporarilyUnsupportedReftypeForExit() &&
    263         !hasInt64Arg() && !temporarilyUnsupportedResultCountForJitExit() &&
    264         JitOptions.enableWasmJitExit;
    265 }
    266 
    267 size_t FuncType::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const {
    268  return args_.sizeOfExcludingThis(mallocSizeOf);
    269 }
    270 
    271 //=========================================================================
    272 // StructType
    273 
    274 bool StructType::init() {
    275  isDefaultable_ = true;
    276 
    277  // Ensures the inline storage area is word-aligned.
    278  static_assert((sizeof(WasmStructObject) % sizeof(uintptr_t)) == 0);
    279 
    280  MOZ_ASSERT(fieldAccessPaths_.empty() && outlineTraceOffsets_.empty() &&
    281             inlineTraceOffsets_.empty());
    282  if (!fieldAccessPaths_.reserve(fields_.length())) {
    283    return false;
    284  }
    285 
    286  // So as to guarantee the value will fit in payloadOffsetIL_, since it is
    287  // a uint8_t.
    288  static_assert(WasmStructObject_Size_ASSUMED <
    289                (1 << (8 * sizeof(StructType::payloadOffsetIL_))));
    290  payloadOffsetIL_ = WasmStructObject_Size_ASSUMED;
    291 
    292  StructLayout layout;
    293  if (!layout.init(payloadOffsetIL_, WasmStructObject_MaxInlineBytes_ASSUMED)) {
    294    return false;
    295  }
    296 
    297  for (FieldType& field : fields_) {
    298    // Get a placement decision for the field
    299    FieldAccessPath path;
    300    if (!layout.addField(field.type.size(), &path)) {
    301      return false;
    302    }
    303 
    304    // Add the access path to the vector thereof
    305    fieldAccessPaths_.infallibleAppend(path);
    306 
    307    // If any field is not defaultable, this whole struct is not defaultable
    308    if (!field.type.isDefaultable()) {
    309      isDefaultable_ = false;
    310    }
    311 
    312    // If this field is a ref, add it to the trace vectors
    313    if (field.type.isRefRepr()) {
    314      if (path.hasOOL()) {
    315        if (!outlineTraceOffsets_.append(path.oolOffset())) {
    316          return false;
    317        }
    318      } else {
    319        if (!inlineTraceOffsets_.append(path.ilOffset())) {
    320          return false;
    321        }
    322      }
    323    }
    324  }
    325 
    326  if (layout.hasOOL()) {
    327    totalSizeOOL_ = layout.totalSizeOOL();
    328    FieldAccessPath oolPointerPath = layout.oolPointerPath();
    329    MOZ_ASSERT(!oolPointerPath.hasOOL());
    330    oolPointerOffset_ = oolPointerPath.ilOffset();
    331  } else {
    332    totalSizeOOL_ = 0;
    333    oolPointerOffset_ = StructType::InvalidOffset;
    334  }
    335 
    336  totalSizeIL_ = layout.totalSizeIL();
    337  // payloadOffsetIL_ set above
    338  allocKind_ = gc::GetGCObjectKindForBytes(totalSizeIL_);
    339  // isDefaultable_ set above
    340 
    341  return true;
    342 }
    343 
    344 /* static */
    345 bool StructType::createImmutable(const ValTypeVector& types,
    346                                 StructType* struct_) {
    347  FieldTypeVector fields;
    348  if (!fields.resize(types.length())) {
    349    return false;
    350  }
    351  for (size_t i = 0; i < types.length(); i++) {
    352    fields[i].type = StorageType(types[i].packed());
    353    fields[i].isMutable = false;
    354  }
    355  *struct_ = StructType(std::move(fields));
    356  return struct_->init();
    357 }
    358 
    359 size_t StructType::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const {
    360  return fields_.sizeOfExcludingThis(mallocSizeOf);
    361 }
    362 
    363 size_t ArrayType::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const {
    364  return 0;
    365 }
    366 
    367 size_t TypeDef::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const {
    368  switch (kind_) {
    369    case TypeDefKind::Struct: {
    370      return structType_.sizeOfExcludingThis(mallocSizeOf);
    371    }
    372    case TypeDefKind::Func: {
    373      return funcType_.sizeOfExcludingThis(mallocSizeOf);
    374    }
    375    case TypeDefKind::Array: {
    376      return arrayType_.sizeOfExcludingThis(mallocSizeOf);
    377    }
    378    case TypeDefKind::None: {
    379      return 0;
    380    }
    381    default:
    382      break;
    383  }
    384  MOZ_ASSERT_UNREACHABLE();
    385  return 0;
    386 }
    387 
    388 //=========================================================================
    389 // SuperTypeVector
    390 
    391 /* static */
    392 size_t SuperTypeVector::offsetOfSTVInVector(uint32_t subTypingDepth) {
    393  return offsetof(SuperTypeVector, types_) + sizeof(void*) * subTypingDepth;
    394 }
    395 
    396 /* static */
    397 size_t SuperTypeVector::lengthForTypeDef(const TypeDef& typeDef) {
    398  return std::max(uint32_t(typeDef.subTypingDepth()) + 1,
    399                  MinSuperTypeVectorLength);
    400 }
    401 
    402 /* static */
    403 size_t SuperTypeVector::byteSizeForTypeDef(const TypeDef& typeDef) {
    404  static_assert(
    405      sizeof(SuperTypeVector) + sizeof(void*) * (MaxSubTypingDepth + 1) <=
    406          UINT16_MAX,
    407      "cannot overflow");
    408  return sizeof(SuperTypeVector) + (sizeof(void*) * lengthForTypeDef(typeDef));
    409 }
    410 
    411 /* static */
    412 const SuperTypeVector* SuperTypeVector::createMultipleForRecGroup(
    413    RecGroup* recGroup) {
    414  // Pre-size the amount of space needed for all the super type vectors in this
    415  // recursion group.
    416  CheckedUint32 totalBytes = 0;
    417  for (uint32_t typeIndex = 0; typeIndex < recGroup->numTypes(); typeIndex++) {
    418    totalBytes +=
    419        SuperTypeVector::byteSizeForTypeDef(recGroup->type(typeIndex));
    420  }
    421  if (!totalBytes.isValid()) {
    422    return nullptr;
    423  }
    424 
    425  // Allocate the batch, and retain reference to the first one.
    426  SuperTypeVector* firstVector =
    427      (SuperTypeVector*)js_malloc(totalBytes.value());
    428  if (!firstVector) {
    429    return nullptr;
    430  }
    431 
    432  // Initialize the vectors, one by one
    433  SuperTypeVector* currentVector = firstVector;
    434  for (uint32_t typeIndex = 0; typeIndex < recGroup->numTypes(); typeIndex++) {
    435    TypeDef& typeDef = recGroup->type(typeIndex);
    436 
    437    // Compute the size again to know where the next vector can be found.
    438    size_t vectorByteSize = SuperTypeVector::byteSizeForTypeDef(typeDef);
    439 
    440    // Make the typedef and the vector point at each other.
    441    typeDef.setSuperTypeVector(currentVector);
    442    currentVector->typeDef_ = &typeDef;
    443    currentVector->subTypingDepth_ = typeDef.subTypingDepth();
    444 
    445    // Every vector stores all ancestor types and itself.
    446    currentVector->length_ = SuperTypeVector::lengthForTypeDef(typeDef);
    447 
    448    // Initialize the entries in the vector
    449    const TypeDef* currentTypeDef = &typeDef;
    450    for (uint32_t index = 0; index < currentVector->length(); index++) {
    451      uint32_t reverseIndex = currentVector->length() - index - 1;
    452 
    453      // If this entry is required just to hit the minimum size, then
    454      // initialize it to null.
    455      if (reverseIndex > typeDef.subTypingDepth()) {
    456        currentVector->types_[reverseIndex] = nullptr;
    457        continue;
    458      }
    459 
    460      // Otherwise we should always be iterating at the same depth as our
    461      // currentTypeDef.
    462      MOZ_ASSERT(reverseIndex == currentTypeDef->subTypingDepth());
    463 
    464      currentVector->types_[reverseIndex] = currentTypeDef->superTypeVector();
    465      currentTypeDef = currentTypeDef->superTypeDef();
    466    }
    467 
    468    // There should be no more super types left over
    469    MOZ_ASSERT(currentTypeDef == nullptr);
    470 
    471    // Advance to the next super type vector
    472    currentVector =
    473        (SuperTypeVector*)(((const char*)currentVector) + vectorByteSize);
    474  }
    475 
    476  return firstVector;
    477 }
    478 
    479 //=========================================================================
    480 // TypeIdSet and TypeContext
    481 
    482 struct RecGroupHashPolicy {
    483  using Lookup = const SharedRecGroup&;
    484 
    485  static HashNumber hash(Lookup lookup) { return lookup->hash(); }
    486 
    487  static bool match(const SharedRecGroup& lhs, Lookup rhs) {
    488    return RecGroup::isoEquals(*rhs, *lhs);
    489  }
    490 };
    491 
    492 // A global hash set of recursion groups for use in fast type equality checks.
    493 class TypeIdSet {
    494  using Set = HashSet<SharedRecGroup, RecGroupHashPolicy, SystemAllocPolicy>;
    495  Set set_;
    496 
    497 public:
    498  // Attempt to insert a recursion group into the set, returning an existing
    499  // recursion group if there was one.
    500  SharedRecGroup insert(SharedRecGroup recGroup) {
    501    Set::AddPtr p = set_.lookupForAdd(recGroup);
    502    if (p) {
    503      // A canonical recursion group already existed, return it.
    504      return *p;
    505    }
    506 
    507    // Insert this recursion group into the set, and return it as the canonical
    508    // recursion group instance.
    509    if (!set_.add(p, recGroup)) {
    510      return nullptr;
    511    }
    512    return recGroup;
    513  }
    514 
    515  void purge() {
    516    // TODO: this is not guaranteed to remove all types that are not referenced
    517    // from outside the canonical set, as removing a type may make a previous
    518    // type we've visited now only have one ref and be eligible to be freed.
    519    //
    520    // Solving this either involves iterating to a fixed point, or else a much
    521    // more invasive change to the lifetime management of recursion groups.
    522    for (auto iter = set_.modIter(); !iter.done(); iter.next()) {
    523      if (iter.get()->hasOneRef()) {
    524        iter.remove();
    525      }
    526    }
    527  }
    528 
    529  // Release the provided recursion group reference and remove it from the
    530  // canonical set if it was the last reference. This is one unified method
    531  // because we need to perform the lookup before releasing the reference, but
    532  // need to release the reference in order to see if it was the last reference
    533  // outside the canonical set.
    534  void clearRecGroup(SharedRecGroup* recGroupCell) {
    535    if (Set::Ptr p = set_.lookup(*recGroupCell)) {
    536      *recGroupCell = nullptr;
    537      if ((*p)->hasOneRef()) {
    538        set_.remove(p);
    539      }
    540    } else {
    541      *recGroupCell = nullptr;
    542    }
    543  }
    544 };
    545 
    546 MOZ_RUNINIT ExclusiveData<TypeIdSet> typeIdSet(mutexid::WasmTypeIdSet);
    547 
    548 void wasm::PurgeCanonicalTypes() {
    549  ExclusiveData<TypeIdSet>::Guard locked = typeIdSet.lock();
    550  locked->purge();
    551 }
    552 
    553 SharedRecGroup TypeContext::canonicalizeGroup(SharedRecGroup recGroup) {
    554  ExclusiveData<TypeIdSet>::Guard locked = typeIdSet.lock();
    555  return locked->insert(recGroup);
    556 }
    557 
    558 TypeContext::~TypeContext() {
    559  ExclusiveData<TypeIdSet>::Guard locked = typeIdSet.lock();
    560 
    561  // Clear out the recursion groups in this module, freeing them from the
    562  // canonical type set if needed.
    563  //
    564  // We iterate backwards here so that we free every previous recursion group
    565  // that may be referring to the current recursion group we're freeing. This
    566  // is possible due to recursion groups being ordered.
    567  for (int32_t groupIndex = recGroups_.length() - 1; groupIndex >= 0;
    568       groupIndex--) {
    569    // Try to remove this entry from the canonical set if we have the last
    570    // strong reference. The entry may not exist if canonicalization failed
    571    // and this type context was aborted. This will clear the reference in the
    572    // vector.
    573    locked->clearRecGroup(&recGroups_[groupIndex]);
    574  }
    575 }