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