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