tor-browser

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

WasmExprType.h (9575B)


      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_expr_type_h
     20 #define wasm_expr_type_h
     21 
     22 #include <stdint.h>
     23 
     24 #include "wasm/WasmTypeDef.h"
     25 #include "wasm/WasmValType.h"
     26 
     27 namespace js {
     28 namespace wasm {
     29 
     30 template <typename PointerType>
     31 class TaggedValue {
     32 public:
     33  enum Kind {
     34    ImmediateKind1 = 0,
     35    ImmediateKind2 = 1,
     36    PointerKind1 = 2,
     37    PointerKind2 = 3
     38  };
     39  using PackedRepr = uint64_t;
     40  static_assert(std::is_same<PackedTypeCode::PackedRepr, uint64_t>(),
     41                "can use pointer tagging with PackedTypeCode");
     42 
     43 private:
     44  PackedRepr bits_;
     45 
     46  static constexpr PackedRepr PayloadShift = 2;
     47  static constexpr PackedRepr KindMask = 0x3;
     48  static constexpr PackedRepr PointerKindBit = 0x2;
     49 
     50  constexpr static bool IsPointerKind(Kind kind) {
     51    return PackedRepr(kind) & PointerKindBit;
     52  }
     53  constexpr static bool IsImmediateKind(Kind kind) {
     54    return !IsPointerKind(kind);
     55  }
     56 
     57  static_assert(IsImmediateKind(ImmediateKind1), "immediate kind 1");
     58  static_assert(IsImmediateKind(ImmediateKind2), "immediate kind 2");
     59  static_assert(IsPointerKind(PointerKind1), "pointer kind 1");
     60  static_assert(IsPointerKind(PointerKind2), "pointer kind 2");
     61 
     62  static PackedRepr PackImmediate(Kind kind, PackedRepr imm) {
     63    MOZ_ASSERT(IsImmediateKind(kind));
     64    MOZ_ASSERT((PackedRepr(kind) & KindMask) == kind);
     65    MOZ_ASSERT((imm & (PackedRepr(KindMask)
     66                       << ((sizeof(PackedRepr) * 8) - PayloadShift))) == 0);
     67    return PackedRepr(kind) | (PackedRepr(imm) << PayloadShift);
     68  }
     69 
     70  static PackedRepr PackPointer(Kind kind, PointerType* ptr) {
     71    PackedRepr ptrBits = reinterpret_cast<PackedRepr>(ptr);
     72    MOZ_ASSERT(IsPointerKind(kind));
     73    MOZ_ASSERT((PackedRepr(kind) & KindMask) == kind);
     74    MOZ_ASSERT((ptrBits & KindMask) == 0);
     75    return PackedRepr(kind) | ptrBits;
     76  }
     77 
     78 public:
     79  TaggedValue(Kind kind, PackedRepr imm) : bits_(PackImmediate(kind, imm)) {}
     80  TaggedValue(Kind kind, PointerType* ptr) : bits_(PackPointer(kind, ptr)) {}
     81 
     82  PackedRepr bits() const { return bits_; }
     83  Kind kind() const { return Kind(bits() & KindMask); }
     84  PackedRepr immediate() const {
     85    MOZ_ASSERT(IsImmediateKind(kind()));
     86    return mozilla::AssertedCast<PackedRepr>(bits() >> PayloadShift);
     87  }
     88  PointerType* pointer() const {
     89    MOZ_ASSERT(IsPointerKind(kind()));
     90    return reinterpret_cast<PointerType*>(bits() & ~KindMask);
     91  }
     92 };
     93 
     94 // ResultType represents the WebAssembly spec's `resulttype`. Semantically, a
     95 // result type is just a vec(valtype).  For effiency, though, the ResultType
     96 // value is packed into a word, with separate encodings for these 3 cases:
     97 //  []
     98 //  [valtype]
     99 //  pointer to ValTypeVector
    100 //
    101 // Additionally there is an encoding indicating uninitialized ResultType
    102 // values.
    103 //
    104 // Generally in the latter case the ValTypeVector is the args() or results() of
    105 // a FuncType in the compilation unit, so as long as the lifetime of the
    106 // ResultType value is less than the OpIter, we can just borrow the pointer
    107 // without ownership or copying.
    108 class ResultType {
    109  using Tagged = TaggedValue<const ValTypeVector>;
    110  Tagged tagged_;
    111 
    112  enum Kind {
    113    EmptyKind = Tagged::ImmediateKind1,
    114    SingleKind = Tagged::ImmediateKind2,
    115    VectorKind = Tagged::PointerKind1,
    116    InvalidKind = Tagged::PointerKind2,
    117  };
    118 
    119  ResultType(Kind kind, Tagged::PackedRepr imm)
    120      : tagged_(Tagged::Kind(kind), imm) {}
    121  explicit ResultType(const ValTypeVector* ptr)
    122      : tagged_(Tagged::Kind(VectorKind), ptr) {}
    123 
    124  Kind kind() const { return Kind(tagged_.kind()); }
    125 
    126  ValType singleValType() const {
    127    MOZ_ASSERT(kind() == SingleKind);
    128    return ValType(PackedTypeCode::fromBits(tagged_.immediate()));
    129  }
    130 
    131  const ValTypeVector& values() const {
    132    MOZ_ASSERT(kind() == VectorKind);
    133    return *tagged_.pointer();
    134  }
    135 
    136 public:
    137  ResultType() : tagged_(Tagged::Kind(InvalidKind), nullptr) {}
    138 
    139  static ResultType Empty() {
    140    return ResultType(EmptyKind, Tagged::PackedRepr(0));
    141  }
    142  static ResultType Single(ValType vt) {
    143    return ResultType(SingleKind, vt.bitsUnsafe());
    144  }
    145  static ResultType Vector(const ValTypeVector& vals) {
    146    switch (vals.length()) {
    147      case 0:
    148        return Empty();
    149      case 1:
    150        return Single(vals[0]);
    151      default:
    152        return ResultType(&vals);
    153    }
    154  }
    155 
    156  [[nodiscard]] bool cloneToVector(ValTypeVector* out) {
    157    MOZ_ASSERT(out->empty());
    158    switch (kind()) {
    159      case EmptyKind:
    160        return true;
    161      case SingleKind:
    162        return out->append(singleValType());
    163      case VectorKind:
    164        return out->appendAll(values());
    165      default:
    166        MOZ_CRASH("bad resulttype");
    167    }
    168  }
    169 
    170  bool valid() const { return kind() != InvalidKind; }
    171  bool empty() const { return kind() == EmptyKind; }
    172 
    173  size_t length() const {
    174    switch (kind()) {
    175      case EmptyKind:
    176        return 0;
    177      case SingleKind:
    178        return 1;
    179      case VectorKind:
    180        return values().length();
    181      default:
    182        MOZ_CRASH("bad resulttype");
    183    }
    184  }
    185 
    186  // See also wasm::CheckIsSubtypeOf in WasmValidate.cpp.
    187  static bool isSubTypeOf(ResultType subType, ResultType superType) {
    188    if (subType.length() != superType.length()) {
    189      return false;
    190    }
    191    for (size_t i = 0; i < subType.length(); i++) {
    192      if (!ValType::isSubTypeOf(subType[i], superType[i])) {
    193        return false;
    194      }
    195    }
    196    return true;
    197  }
    198 
    199  // Polyfill the Span API, which is polyfilling the std library
    200  size_t size() const { return length(); }
    201 
    202  ValType operator[](size_t i) const {
    203    switch (kind()) {
    204      case SingleKind:
    205        MOZ_ASSERT(i == 0);
    206        return singleValType();
    207      case VectorKind:
    208        return values()[i];
    209      default:
    210        MOZ_CRASH("bad resulttype");
    211    }
    212  }
    213 };
    214 
    215 // BlockType represents the WebAssembly spec's `blocktype`. Semantically, a
    216 // block type is just a (vec(valtype) -> vec(valtype)) with four special
    217 // encodings which are represented explicitly in BlockType:
    218 //  [] -> []
    219 //  [] -> [valtype]
    220 //  [params] -> [results] via pointer to FuncType
    221 //  [] -> [results] via pointer to FuncType (ignoring [params])
    222 
    223 class BlockType {
    224  using Tagged = TaggedValue<const FuncType>;
    225  Tagged tagged_;
    226 
    227  enum Kind {
    228    VoidToVoidKind = Tagged::ImmediateKind1,
    229    VoidToSingleKind = Tagged::ImmediateKind2,
    230    FuncKind = Tagged::PointerKind1,
    231    FuncResultsKind = Tagged::PointerKind2
    232  };
    233 
    234  BlockType(Kind kind, Tagged::PackedRepr imm)
    235      : tagged_(Tagged::Kind(kind), imm) {}
    236  BlockType(Kind kind, const FuncType& type)
    237      : tagged_(Tagged::Kind(kind), &type) {}
    238 
    239  Kind kind() const { return Kind(tagged_.kind()); }
    240  ValType singleValType() const {
    241    MOZ_ASSERT(kind() == VoidToSingleKind);
    242    return ValType(PackedTypeCode::fromBits(tagged_.immediate()));
    243  }
    244 
    245  const FuncType& funcType() const { return *tagged_.pointer(); }
    246 
    247 public:
    248  BlockType()
    249      : tagged_(Tagged::Kind(VoidToVoidKind),
    250                PackedTypeCode::invalid().bits()) {}
    251 
    252  static BlockType VoidToVoid() {
    253    return BlockType(VoidToVoidKind, Tagged::PackedRepr(0));
    254  }
    255  static BlockType VoidToSingle(ValType vt) {
    256    return BlockType(VoidToSingleKind, vt.bitsUnsafe());
    257  }
    258  static BlockType Func(const FuncType& type) {
    259    if (type.args().length() == 0) {
    260      return FuncResults(type);
    261    }
    262    return BlockType(FuncKind, type);
    263  }
    264  static BlockType FuncResults(const FuncType& type) {
    265    switch (type.results().length()) {
    266      case 0:
    267        return VoidToVoid();
    268      case 1:
    269        return VoidToSingle(type.results()[0]);
    270      default:
    271        return BlockType(FuncResultsKind, type);
    272    }
    273  }
    274 
    275  ResultType params() const {
    276    switch (kind()) {
    277      case VoidToVoidKind:
    278      case VoidToSingleKind:
    279      case FuncResultsKind:
    280        return ResultType::Empty();
    281      case FuncKind:
    282        return ResultType::Vector(funcType().args());
    283      default:
    284        MOZ_CRASH("unexpected kind");
    285    }
    286  }
    287 
    288  ResultType results() const {
    289    switch (kind()) {
    290      case VoidToVoidKind:
    291        return ResultType::Empty();
    292      case VoidToSingleKind:
    293        return ResultType::Single(singleValType());
    294      case FuncKind:
    295      case FuncResultsKind:
    296        return ResultType::Vector(funcType().results());
    297      default:
    298        MOZ_CRASH("unexpected kind");
    299    }
    300  }
    301 
    302  bool operator==(BlockType rhs) const {
    303    if (kind() != rhs.kind()) {
    304      return false;
    305    }
    306    switch (kind()) {
    307      case VoidToVoidKind:
    308      case VoidToSingleKind:
    309        return tagged_.bits() == rhs.tagged_.bits();
    310      case FuncKind:
    311        return FuncType::strictlyEquals(funcType(), rhs.funcType());
    312      case FuncResultsKind:
    313        return EqualContainers(funcType().results(), rhs.funcType().results());
    314      default:
    315        MOZ_CRASH("unexpected kind");
    316    }
    317  }
    318 
    319  bool operator!=(BlockType rhs) const { return !(*this == rhs); }
    320 };
    321 
    322 }  // namespace wasm
    323 }  // namespace js
    324 
    325 #endif  // wasm_expr_type_h