Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / js / src / wasm / WasmTypeDef.h
bloba0d44e647b89736f820a4c6493eb0c0194a3709e
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:
4 * Copyright 2021 Mozilla Foundation
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #ifndef wasm_type_def_h
20 #define wasm_type_def_h
22 #include "mozilla/Assertions.h"
23 #include "mozilla/CheckedInt.h"
24 #include "mozilla/HashTable.h"
26 #include "js/RefCounted.h"
28 #include "wasm/WasmCodegenConstants.h"
29 #include "wasm/WasmCompileArgs.h"
30 #include "wasm/WasmConstants.h"
31 #include "wasm/WasmSerialize.h"
32 #include "wasm/WasmUtility.h"
33 #include "wasm/WasmValType.h"
35 namespace js {
36 namespace wasm {
38 using mozilla::CheckedInt32;
39 using mozilla::MallocSizeOf;
41 class RecGroup;
43 //=========================================================================
44 // Function types
46 // The FuncType class represents a WebAssembly function signature which takes a
47 // list of value types and returns an expression type. The engine uses two
48 // in-memory representations of the argument Vector's memory (when elements do
49 // not fit inline): normal malloc allocation (via SystemAllocPolicy) and
50 // allocation in a LifoAlloc (via LifoAllocPolicy). The former FuncType objects
51 // can have any lifetime since they own the memory. The latter FuncType objects
52 // must not outlive the associated LifoAlloc mark/release interval (which is
53 // currently the duration of module validation+compilation). Thus, long-lived
54 // objects like WasmModule must use malloced allocation.
56 class FuncType {
57 ValTypeVector args_;
58 ValTypeVector results_;
59 // A packed structural type identifier for use in the call_indirect type
60 // check in the prologue of functions. If this function type cannot fit in
61 // this immediate, it will be NO_IMMEDIATE_TYPE_ID.
63 // This is initialized in DecodeTypeSection once we have the full recursion
64 // group around.
65 uint32_t immediateTypeId_ = NO_IMMEDIATE_TYPE_ID;
67 // This function type cannot be packed into an immediate for call_indirect
68 // signature checks.
69 static const uint32_t NO_IMMEDIATE_TYPE_ID = UINT32_MAX;
71 // Entry from JS to wasm via the JIT is currently unimplemented for
72 // functions that return multiple values.
73 bool temporarilyUnsupportedResultCountForJitEntry() const {
74 return results().length() > MaxResultsForJitEntry;
76 // Calls out from wasm to JS that return multiple values is currently
77 // unsupported.
78 bool temporarilyUnsupportedResultCountForJitExit() const {
79 return results().length() > MaxResultsForJitExit;
81 // For JS->wasm jit entries, temporarily disallow certain types until the
82 // stubs generator is improved.
83 // * ref params may be nullable externrefs
84 // * ref results may not be type indices
85 // V128 types are excluded per spec but are guarded against separately.
86 bool temporarilyUnsupportedReftypeForEntry() const {
87 for (ValType arg : args()) {
88 if (arg.isRefType() && (!arg.isExternRef() || !arg.isNullable())) {
89 return true;
92 for (ValType result : results()) {
93 if (result.isTypeRef()) {
94 return true;
97 return false;
99 // For wasm->JS jit exits, temporarily disallow certain types until
100 // the stubs generator is improved.
101 // * ref results may be nullable externrefs
102 // Unexposable types must be guarded against separately.
103 bool temporarilyUnsupportedReftypeForExit() const {
104 for (ValType result : results()) {
105 if (result.isRefType() &&
106 (!result.isExternRef() || !result.isNullable())) {
107 return true;
110 return false;
113 public:
114 FuncType() = default;
115 FuncType(ValTypeVector&& args, ValTypeVector&& results)
116 : args_(std::move(args)), results_(std::move(results)) {}
118 FuncType(FuncType&&) = default;
119 FuncType& operator=(FuncType&&) = default;
121 [[nodiscard]] bool clone(const FuncType& src) {
122 MOZ_ASSERT(args_.empty());
123 MOZ_ASSERT(results_.empty());
124 immediateTypeId_ = src.immediateTypeId_;
125 return args_.appendAll(src.args_) && results_.appendAll(src.results_);
128 ValType arg(unsigned i) const { return args_[i]; }
129 const ValTypeVector& args() const { return args_; }
130 ValType result(unsigned i) const { return results_[i]; }
131 const ValTypeVector& results() const { return results_; }
133 void initImmediateTypeId(bool gcEnabled, bool isFinal,
134 const TypeDef* superTypeDef,
135 uint32_t recGroupLength);
136 bool hasImmediateTypeId() const {
137 return immediateTypeId_ != NO_IMMEDIATE_TYPE_ID;
139 uint32_t immediateTypeId() const {
140 MOZ_ASSERT(hasImmediateTypeId());
141 return immediateTypeId_;
144 // The lsb for every immediate type id is set to distinguish an immediate type
145 // id from a type id represented by a pointer to the global hash type set.
146 static const uint32_t ImmediateBit = 0x1;
148 HashNumber hash(const RecGroup* recGroup) const {
149 HashNumber hn = 0;
150 for (const ValType& vt : args_) {
151 hn = mozilla::AddToHash(hn, vt.forMatch(recGroup).hash());
153 for (const ValType& vt : results_) {
154 hn = mozilla::AddToHash(hn, vt.forMatch(recGroup).hash());
156 return hn;
159 // Matches two function types for isorecursive equality. See
160 // "Matching type definitions" in WasmValType.h for more background.
161 static bool matches(const RecGroup* lhsRecGroup, const FuncType& lhs,
162 const RecGroup* rhsRecGroup, const FuncType& rhs) {
163 if (lhs.args_.length() != rhs.args_.length() ||
164 lhs.results_.length() != rhs.results_.length()) {
165 return false;
167 for (uint32_t i = 0; i < lhs.args_.length(); i++) {
168 if (lhs.args_[i].forMatch(lhsRecGroup) !=
169 rhs.args_[i].forMatch(rhsRecGroup)) {
170 return false;
173 for (uint32_t i = 0; i < lhs.results_.length(); i++) {
174 if (lhs.results_[i].forMatch(lhsRecGroup) !=
175 rhs.results_[i].forMatch(rhsRecGroup)) {
176 return false;
179 return true;
182 // Checks if every arg and result of the specified function types are bitwise
183 // equal. Type references must therefore point to exactly the same type
184 // definition instance.
185 static bool strictlyEquals(const FuncType& lhs, const FuncType& rhs) {
186 return EqualContainers(lhs.args(), rhs.args()) &&
187 EqualContainers(lhs.results(), rhs.results());
190 // Checks if two function types are compatible in a given subtyping
191 // relationship.
192 static bool canBeSubTypeOf(const FuncType& subType,
193 const FuncType& superType) {
194 // A subtype must have exactly as many arguments as its supertype
195 if (subType.args().length() != superType.args().length()) {
196 return false;
199 // A subtype must have exactly as many returns as its supertype
200 if (subType.results().length() != superType.results().length()) {
201 return false;
204 // Function result types are covariant
205 for (uint32_t i = 0; i < superType.results().length(); i++) {
206 if (!ValType::isSubTypeOf(subType.results()[i], superType.results()[i])) {
207 return false;
211 // Function argument types are contravariant
212 for (uint32_t i = 0; i < superType.args().length(); i++) {
213 if (!ValType::isSubTypeOf(superType.args()[i], subType.args()[i])) {
214 return false;
218 return true;
221 bool canHaveJitEntry() const;
222 bool canHaveJitExit() const;
224 bool hasInt64Arg() const {
225 for (ValType arg : args()) {
226 if (arg.kind() == ValType::Kind::I64) {
227 return true;
230 return false;
233 bool hasUnexposableArgOrRet() const {
234 for (ValType arg : args()) {
235 if (!arg.isExposable()) {
236 return true;
239 for (ValType result : results()) {
240 if (!result.isExposable()) {
241 return true;
244 return false;
247 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
248 WASM_DECLARE_FRIEND_SERIALIZE(FuncType);
251 //=========================================================================
252 // Structure types
254 // The Module owns a dense array of StructType values that represent the
255 // structure types that the module knows about. It is created from the sparse
256 // array of types in the ModuleEnvironment when the Module is created.
258 struct StructField {
259 StorageType type;
260 uint32_t offset;
261 bool isMutable;
263 HashNumber hash(const RecGroup* recGroup) const {
264 HashNumber hn = 0;
265 hn = mozilla::AddToHash(hn, type.forMatch(recGroup).hash());
266 hn = mozilla::AddToHash(hn, HashNumber(isMutable));
267 return hn;
270 // Checks if two struct fields are compatible in a given subtyping
271 // relationship.
272 static bool canBeSubTypeOf(const StructField& subType,
273 const StructField& superType) {
274 // Mutable fields are invariant w.r.t. field types
275 if (subType.isMutable && superType.isMutable) {
276 return subType.type == superType.type;
279 // Immutable fields are covariant w.r.t. field types
280 if (!subType.isMutable && !superType.isMutable) {
281 return StorageType::isSubTypeOf(subType.type, superType.type);
284 return false;
288 using StructFieldVector = Vector<StructField, 0, SystemAllocPolicy>;
290 using InlineTraceOffsetVector = Vector<uint32_t, 2, SystemAllocPolicy>;
291 using OutlineTraceOffsetVector = Vector<uint32_t, 0, SystemAllocPolicy>;
293 class StructType {
294 public:
295 StructFieldVector fields_; // Field type, offset, and mutability
296 uint32_t size_; // The size of the type in bytes.
297 InlineTraceOffsetVector inlineTraceOffsets_;
298 OutlineTraceOffsetVector outlineTraceOffsets_;
300 public:
301 StructType() : size_(0) {}
303 explicit StructType(StructFieldVector&& fields)
304 : fields_(std::move(fields)), size_(0) {}
306 StructType(StructType&&) = default;
307 StructType& operator=(StructType&&) = default;
309 [[nodiscard]] bool clone(const StructType& src) {
310 if (!fields_.appendAll(src.fields_)) {
311 return false;
313 size_ = src.size_;
314 return true;
317 [[nodiscard]] bool init();
319 bool isDefaultable() const {
320 for (auto& field : fields_) {
321 if (!field.type.isDefaultable()) {
322 return false;
325 return true;
328 HashNumber hash(const RecGroup* recGroup) const {
329 HashNumber hn = 0;
330 for (const StructField& field : fields_) {
331 hn = mozilla::AddToHash(hn, field.hash(recGroup));
333 return hn;
336 // Matches two struct types for isorecursive equality. See
337 // "Matching type definitions" in WasmValType.h for more background.
338 static bool matches(const RecGroup* lhsRecGroup, const StructType& lhs,
339 const RecGroup* rhsRecGroup, const StructType& rhs) {
340 if (lhs.fields_.length() != rhs.fields_.length()) {
341 return false;
343 for (uint32_t i = 0; i < lhs.fields_.length(); i++) {
344 const StructField& lhsField = lhs.fields_[i];
345 const StructField& rhsField = rhs.fields_[i];
346 if (lhsField.isMutable != rhsField.isMutable ||
347 lhsField.type.forMatch(lhsRecGroup) !=
348 rhsField.type.forMatch(rhsRecGroup)) {
349 return false;
352 return true;
355 // Checks if two struct types are compatible in a given subtyping
356 // relationship.
357 static bool canBeSubTypeOf(const StructType& subType,
358 const StructType& superType) {
359 // A subtype must have at least as many fields as its supertype
360 if (subType.fields_.length() < superType.fields_.length()) {
361 return false;
364 // Every field that is in both superType and subType must be compatible
365 for (uint32_t i = 0; i < superType.fields_.length(); i++) {
366 if (!StructField::canBeSubTypeOf(subType.fields_[i],
367 superType.fields_[i])) {
368 return false;
371 return true;
374 static bool createImmutable(const ValTypeVector& types, StructType* struct_);
376 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
377 WASM_DECLARE_FRIEND_SERIALIZE(StructType);
380 using StructTypeVector = Vector<StructType, 0, SystemAllocPolicy>;
382 // Utility for computing field offset and alignments, and total size for
383 // structs and tags. This is complicated by fact that a WasmStructObject has
384 // an inline area, which is used first, and if that fills up an optional
385 // C++-heap-allocated outline area is used. We need to be careful not to
386 // split any data item across the boundary. This is ensured as follows:
388 // (1) the possible field sizes are 1, 2, 4, 8 and 16 only.
389 // (2) each field is "naturally aligned" -- aligned to its size.
390 // (3) MaxInlineBytes (the size of the inline area) % 16 == 0.
392 // From (1) and (2), it follows that all fields are placed so that their first
393 // and last bytes fall within the same 16-byte chunk. That is,
394 // offset_of_first_byte_of_field / 16 == offset_of_last_byte_of_field / 16.
396 // Given that, it follows from (3) that all fields fall completely within
397 // either the inline or outline areas; no field crosses the boundary.
398 class StructLayout {
399 CheckedInt32 sizeSoFar = 0;
400 uint32_t structAlignment = 1;
402 public:
403 // The field adders return the offset of the the field.
404 CheckedInt32 addField(StorageType type);
406 // The close method rounds up the structure size to the appropriate
407 // alignment and returns that size.
408 CheckedInt32 close();
411 //=========================================================================
412 // Array types
414 class ArrayType {
415 public:
416 // The kind of value stored in this array
417 StorageType elementType_;
418 // Whether this array is mutable or not
419 bool isMutable_;
421 public:
422 ArrayType() : isMutable_(false) {}
423 ArrayType(StorageType elementType, bool isMutable)
424 : elementType_(elementType), isMutable_(isMutable) {}
426 ArrayType(const ArrayType&) = default;
427 ArrayType& operator=(const ArrayType&) = default;
429 ArrayType(ArrayType&&) = default;
430 ArrayType& operator=(ArrayType&&) = default;
432 [[nodiscard]] bool clone(const ArrayType& src) {
433 elementType_ = src.elementType_;
434 isMutable_ = src.isMutable_;
435 return true;
438 bool isDefaultable() const { return elementType_.isDefaultable(); }
440 HashNumber hash(const RecGroup* recGroup) const {
441 HashNumber hn = 0;
442 hn = mozilla::AddToHash(hn, elementType_.forMatch(recGroup).hash());
443 hn = mozilla::AddToHash(hn, HashNumber(isMutable_));
444 return hn;
447 // Matches two array types for isorecursive equality. See
448 // "Matching type definitions" in WasmValType.h for more background.
449 static bool matches(const RecGroup* lhsRecGroup, const ArrayType& lhs,
450 const RecGroup* rhsRecGroup, const ArrayType& rhs) {
451 return lhs.isMutable_ == rhs.isMutable_ &&
452 lhs.elementType_.forMatch(lhsRecGroup) ==
453 rhs.elementType_.forMatch(rhsRecGroup);
456 // Checks if two arrays are compatible in a given subtyping relationship.
457 static bool canBeSubTypeOf(const ArrayType& subType,
458 const ArrayType& superType) {
459 // Mutable fields are invariant w.r.t. field types
460 if (subType.isMutable_ && superType.isMutable_) {
461 return subType.elementType_ == superType.elementType_;
464 // Immutable fields are covariant w.r.t. field types
465 if (!subType.isMutable_ && !superType.isMutable_) {
466 return StorageType::isSubTypeOf(subType.elementType_,
467 superType.elementType_);
470 return true;
473 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
476 WASM_DECLARE_CACHEABLE_POD(ArrayType);
478 using ArrayTypeVector = Vector<ArrayType, 0, SystemAllocPolicy>;
480 //=========================================================================
481 // SuperTypeVector
483 // [SMDOC] Super type vector
485 // A super type vector is a vector representation of the linked list of super
486 // types that a type definition has. Every element is a raw pointer to another
487 // super type vector - they are one-to-one with type definitions, so they are
488 // functionally equivalent. It is possible to form a vector here because
489 // subtypes in wasm form trees, not DAGs, with every type having at most one
490 // super type.
492 // The first element in the vector is the 'root' type definition without a
493 // super type. The last element is to the type definition itself.
495 // ## Subtype checking
497 // The only purpose of a super type vector is to support constant time
498 // subtyping checks. This is not free, it comes at the cost of worst case N^2
499 // metadata size growth. We limit the max subtyping depth to counter this.
501 // To perform a subtype check we rely on the following:
502 // (1) a type A is a subtype (<:) of type B iff:
503 // type A == type B OR
504 // type B is reachable by following declared super types of type A
505 // (2) we order super type vectors from least to most derived types
506 // (3) the 'subtyping depth' of all type definitions is statically known
508 // With the above, we know that if type B is a super type of type A, that it
509 // must be in A's super type vector at type B's subtyping depth. We can
510 // therefore just do an index and comparison to determine if that's the case.
512 // ## Example
514 // For the following type section:
515 // ..
516 // 12: (type (struct))
517 // ..
518 // 34: (type (sub 12 (struct)))
519 // ..
520 // 56: (type (sub 34 (struct)))
521 // ..
522 // 78: (type (sub 56 (struct)))
523 // ..
525 // (type 12) would have the following super type vector:
526 // [(type 12)]
528 // (type 78) would have the following super type vector:
529 // [(type 12), (type 34), (type 56), (type 78)]
531 // Checking that (type 78) <: (type 12) can use the fact that (type 12) will
532 // always be present at depth 0 of any super type vector it is in, and
533 // therefore check the vector at that index.
535 // ## Minimum sizing
537 // As a further optimization to avoid bounds checking, we guarantee that all
538 // super type vectors are at least `MinSuperTypeVectorLength`. All checks
539 // against indices that we know statically are at/below that can skip bounds
540 // checking. Extra entries added to reach the minimum size are initialized to
541 // null.
542 class SuperTypeVector {
543 SuperTypeVector() : typeDef_(nullptr), length_(0) {}
545 // The TypeDef for which this is the supertype vector. That TypeDef should
546 // point back to this SuperTypeVector.
547 const TypeDef* typeDef_;
549 // A cached copy of subTypingDepth from TypeDef.
550 uint32_t subTypingDepth_;
552 // The length of types stored inline below.
553 uint32_t length_;
555 public:
556 // Raw pointers to the super types of this type definition. Ordered from
557 // least-derived to most-derived. Do not add any fields after this point.
558 const SuperTypeVector* types_[0];
560 // Batch allocate super type vectors for all the types in a recursion group.
561 // Returns a pointer to the first super type vector, which can be used to
562 // free all vectors.
563 [[nodiscard]] static const SuperTypeVector* createMultipleForRecGroup(
564 RecGroup* recGroup);
566 const TypeDef* typeDef() const { return typeDef_; }
568 uint32_t length() const { return length_; }
570 const SuperTypeVector* type(size_t index) const {
571 MOZ_ASSERT(index < length_);
572 return types_[index];
575 // The length of a super type vector for a specific type def.
576 static size_t lengthForTypeDef(const TypeDef& typeDef);
577 // The byte size of a super type vector for a specific type def.
578 static size_t byteSizeForTypeDef(const TypeDef& typeDef);
580 static size_t offsetOfSubTypingDepth() {
581 return offsetof(SuperTypeVector, subTypingDepth_);
583 static size_t offsetOfLength() { return offsetof(SuperTypeVector, length_); }
584 static size_t offsetOfSelfTypeDef() {
585 return offsetof(SuperTypeVector, typeDef_);
587 static size_t offsetOfSTVInVector(uint32_t subTypingDepth);
590 // Ensure it is safe to use `sizeof(SuperTypeVector)` to find the offset of
591 // `types_[0]`.
592 static_assert(offsetof(SuperTypeVector, types_) == sizeof(SuperTypeVector));
594 //=========================================================================
595 // TypeDef and supporting types
597 // A tagged container for the various types that can be present in a wasm
598 // module's type section.
600 enum class TypeDefKind : uint8_t {
601 None = 0,
602 Func,
603 Struct,
604 Array,
607 class TypeDef {
608 uint32_t offsetToRecGroup_;
610 // The supertype vector for this TypeDef. That SuperTypeVector should point
611 // back to this TypeDef.
612 const SuperTypeVector* superTypeVector_;
614 const TypeDef* superTypeDef_;
615 uint16_t subTypingDepth_;
616 bool isFinal_;
617 TypeDefKind kind_;
618 union {
619 FuncType funcType_;
620 StructType structType_;
621 ArrayType arrayType_;
624 void setRecGroup(RecGroup* recGroup) {
625 uintptr_t recGroupAddr = (uintptr_t)recGroup;
626 uintptr_t typeDefAddr = (uintptr_t)this;
627 MOZ_ASSERT(typeDefAddr > recGroupAddr);
628 MOZ_ASSERT(typeDefAddr - recGroupAddr <= UINT32_MAX);
629 offsetToRecGroup_ = typeDefAddr - recGroupAddr;
632 public:
633 explicit TypeDef(RecGroup* recGroup)
634 : offsetToRecGroup_(0),
635 superTypeVector_(nullptr),
636 superTypeDef_(nullptr),
637 subTypingDepth_(0),
638 isFinal_(true),
639 kind_(TypeDefKind::None) {
640 setRecGroup(recGroup);
643 ~TypeDef() {
644 switch (kind_) {
645 case TypeDefKind::Func:
646 funcType_.~FuncType();
647 break;
648 case TypeDefKind::Struct:
649 structType_.~StructType();
650 break;
651 case TypeDefKind::Array:
652 arrayType_.~ArrayType();
653 break;
654 case TypeDefKind::None:
655 break;
659 TypeDef& operator=(FuncType&& that) noexcept {
660 MOZ_ASSERT(isNone());
661 kind_ = TypeDefKind::Func;
662 new (&funcType_) FuncType(std::move(that));
663 return *this;
666 TypeDef& operator=(StructType&& that) noexcept {
667 MOZ_ASSERT(isNone());
668 kind_ = TypeDefKind::Struct;
669 new (&structType_) StructType(std::move(that));
670 return *this;
673 TypeDef& operator=(ArrayType&& that) noexcept {
674 MOZ_ASSERT(isNone());
675 kind_ = TypeDefKind::Array;
676 new (&arrayType_) ArrayType(std::move(that));
677 return *this;
680 const SuperTypeVector* superTypeVector() const { return superTypeVector_; }
682 void setSuperTypeVector(const SuperTypeVector* superTypeVector) {
683 superTypeVector_ = superTypeVector;
686 static size_t offsetOfKind() { return offsetof(TypeDef, kind_); }
688 static size_t offsetOfSuperTypeVector() {
689 return offsetof(TypeDef, superTypeVector_);
692 static size_t offsetOfSubTypingDepth() {
693 return offsetof(TypeDef, subTypingDepth_);
696 const TypeDef* superTypeDef() const { return superTypeDef_; }
698 bool isFinal() const { return isFinal_; }
700 uint16_t subTypingDepth() const { return subTypingDepth_; }
702 const RecGroup& recGroup() const {
703 uintptr_t typeDefAddr = (uintptr_t)this;
704 uintptr_t recGroupAddr = typeDefAddr - offsetToRecGroup_;
705 return *(const RecGroup*)recGroupAddr;
708 TypeDefKind kind() const { return kind_; }
710 bool isNone() const { return kind_ == TypeDefKind::None; }
712 bool isFuncType() const { return kind_ == TypeDefKind::Func; }
714 bool isStructType() const { return kind_ == TypeDefKind::Struct; }
716 bool isArrayType() const { return kind_ == TypeDefKind::Array; }
718 const FuncType& funcType() const {
719 MOZ_ASSERT(isFuncType());
720 return funcType_;
723 FuncType& funcType() {
724 MOZ_ASSERT(isFuncType());
725 return funcType_;
728 const StructType& structType() const {
729 MOZ_ASSERT(isStructType());
730 return structType_;
733 StructType& structType() {
734 MOZ_ASSERT(isStructType());
735 return structType_;
738 const ArrayType& arrayType() const {
739 MOZ_ASSERT(isArrayType());
740 return arrayType_;
743 ArrayType& arrayType() {
744 MOZ_ASSERT(isArrayType());
745 return arrayType_;
748 // Get a value that can be used for matching type definitions across
749 // different recursion groups.
750 static inline uintptr_t forMatch(const TypeDef* typeDef,
751 const RecGroup* recGroup);
753 HashNumber hash() const {
754 HashNumber hn = HashNumber(kind_);
755 hn = mozilla::AddToHash(hn, TypeDef::forMatch(superTypeDef_, &recGroup()));
756 hn = mozilla::AddToHash(hn, isFinal_);
757 switch (kind_) {
758 case TypeDefKind::Func:
759 hn = mozilla::AddToHash(hn, funcType_.hash(&recGroup()));
760 break;
761 case TypeDefKind::Struct:
762 hn = mozilla::AddToHash(hn, structType_.hash(&recGroup()));
763 break;
764 case TypeDefKind::Array:
765 hn = mozilla::AddToHash(hn, arrayType_.hash(&recGroup()));
766 break;
767 case TypeDefKind::None:
768 break;
770 return hn;
773 // Matches two type definitions for isorecursive equality. See
774 // "Matching type definitions" in WasmValType.h for more background.
775 static bool matches(const TypeDef& lhs, const TypeDef& rhs) {
776 if (lhs.kind_ != rhs.kind_) {
777 return false;
779 if (lhs.isFinal_ != rhs.isFinal_) {
780 return false;
782 if (TypeDef::forMatch(lhs.superTypeDef_, &lhs.recGroup()) !=
783 TypeDef::forMatch(rhs.superTypeDef_, &rhs.recGroup())) {
784 return false;
786 switch (lhs.kind_) {
787 case TypeDefKind::Func:
788 return FuncType::matches(&lhs.recGroup(), lhs.funcType_,
789 &rhs.recGroup(), rhs.funcType_);
790 case TypeDefKind::Struct:
791 return StructType::matches(&lhs.recGroup(), lhs.structType_,
792 &rhs.recGroup(), rhs.structType_);
793 case TypeDefKind::Array:
794 return ArrayType::matches(&lhs.recGroup(), lhs.arrayType_,
795 &rhs.recGroup(), rhs.arrayType_);
796 case TypeDefKind::None:
797 MOZ_CRASH("can't match TypeDefKind::None");
799 return false;
802 // Checks if two type definitions are compatible in a given subtyping
803 // relationship.
804 static bool canBeSubTypeOf(const TypeDef* subType, const TypeDef* superType) {
805 if (subType->kind() != superType->kind()) {
806 return false;
809 // A subtype can't declare a final super type.
810 if (superType->isFinal()) {
811 return false;
814 switch (subType->kind_) {
815 case TypeDefKind::Func:
816 return FuncType::canBeSubTypeOf(subType->funcType_,
817 superType->funcType_);
818 case TypeDefKind::Struct:
819 return StructType::canBeSubTypeOf(subType->structType_,
820 superType->structType_);
821 case TypeDefKind::Array:
822 return ArrayType::canBeSubTypeOf(subType->arrayType_,
823 superType->arrayType_);
824 case TypeDefKind::None:
825 MOZ_CRASH();
827 return false;
830 void setSuperTypeDef(const TypeDef* superTypeDef) {
831 superTypeDef_ = superTypeDef;
832 subTypingDepth_ = superTypeDef_->subTypingDepth_ + 1;
835 void setFinal(const bool value) { isFinal_ = value; }
837 // Checks if `subTypeDef` is a declared sub type of `superTypeDef`.
838 static bool isSubTypeOf(const TypeDef* subTypeDef,
839 const TypeDef* superTypeDef) {
840 // Fast path for when the types are equal
841 if (MOZ_LIKELY(subTypeDef == superTypeDef)) {
842 return true;
844 const SuperTypeVector* subSTV = subTypeDef->superTypeVector();
846 // During construction of a recursion group, the super type vector may not
847 // have been computed yet, in which case we need to fall back to a linear
848 // search.
849 if (!subSTV) {
850 while (subTypeDef) {
851 if (subTypeDef == superTypeDef) {
852 return true;
854 subTypeDef = subTypeDef->superTypeDef();
856 return false;
859 // The supertype vector does exist. So check it points back here.
860 MOZ_ASSERT(subSTV->typeDef() == subTypeDef);
862 // We need to check if `superTypeDef` is one of `subTypeDef`s super types
863 // by checking in `subTypeDef`s super type vector. We can use the static
864 // information of the depth of `superTypeDef` to index directly into the
865 // vector.
866 uint32_t subTypingDepth = superTypeDef->subTypingDepth();
867 if (subTypingDepth >= subSTV->length()) {
868 return false;
871 const SuperTypeVector* superSTV = superTypeDef->superTypeVector();
872 MOZ_ASSERT(superSTV);
873 MOZ_ASSERT(superSTV->typeDef() == superTypeDef);
875 return subSTV->type(subTypingDepth) == superSTV;
878 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
879 WASM_DECLARE_FRIEND_SERIALIZE(TypeDef);
882 using SharedTypeDef = RefPtr<const TypeDef>;
883 using MutableTypeDef = RefPtr<TypeDef>;
885 using TypeDefVector = Vector<TypeDef, 0, SystemAllocPolicy>;
886 using TypeDefPtrVector = Vector<const TypeDef*, 0, SystemAllocPolicy>;
888 using TypeDefPtrToIndexMap =
889 HashMap<const TypeDef*, uint32_t, PointerHasher<const TypeDef*>,
890 SystemAllocPolicy>;
892 //=========================================================================
893 // RecGroup
895 // A recursion group is a set of type definitions that may refer to each other
896 // or to type definitions in another recursion group. There is an ordering
897 // restriction on type references such that references across recursion groups
898 // must be acyclic.
900 // Type definitions are stored inline in their containing recursion group, and
901 // have an offset to their containing recursion group. Recursion groups are
902 // atomically refcounted and hold strong references to other recursion groups
903 // they depend on.
905 // Type equality is structural in WebAssembly, and we canonicalize recursion
906 // groups while building them so that pointer equality of types implies
907 // equality of types. There is a global hash set of weak pointers to recursion
908 // groups that holds the current canonical instance of a recursion group.
909 class RecGroup : public AtomicRefCounted<RecGroup> {
910 // Whether this recursion group has been finished and acquired strong
911 // references to external recursion groups.
912 bool finalizedTypes_;
913 // The number of types stored in this recursion group.
914 uint32_t numTypes_;
915 // The batch allocated super type vectors for all type definitions in this
916 // recursion group.
917 const SuperTypeVector* vectors_;
918 // The first type definition stored inline in this recursion group.
919 TypeDef types_[0];
921 friend class TypeContext;
923 explicit RecGroup(uint32_t numTypes)
924 : finalizedTypes_(false), numTypes_(numTypes), vectors_(nullptr) {}
926 // Compute the size in bytes of a recursion group with the specified amount
927 // of types.
928 static constexpr size_t sizeOfRecGroup(uint32_t numTypes) {
929 static_assert(MaxTypes <= SIZE_MAX / sizeof(TypeDef));
930 return sizeof(RecGroup) + sizeof(TypeDef) * numTypes;
933 // Allocate a recursion group with the specified amount of types. The type
934 // definitions will be ready to be filled in. Users must call `finish` once
935 // type definitions are initialized so that strong references to external
936 // recursion groups are taken.
937 static RefPtr<RecGroup> allocate(uint32_t numTypes) {
938 // Allocate the recursion group with the correct size
939 RecGroup* recGroup = (RecGroup*)js_malloc(sizeOfRecGroup(numTypes));
940 if (!recGroup) {
941 return nullptr;
944 // Construct the recursion group and types that are stored inline
945 new (recGroup) RecGroup(numTypes);
946 for (uint32_t i = 0; i < numTypes; i++) {
947 new (recGroup->types_ + i) TypeDef(recGroup);
949 return recGroup;
952 // Finish initialization by acquiring strong references to groups referenced
953 // by type definitions.
954 [[nodiscard]] bool finalizeDefinitions() {
955 MOZ_ASSERT(!finalizedTypes_);
956 // Super type vectors are only needed for GC and have a size/time impact
957 // that we don't want to encur until we're ready for it. Only use them when
958 // GC is built into the binary.
959 vectors_ = SuperTypeVector::createMultipleForRecGroup(this);
960 if (!vectors_) {
961 return false;
963 visitReferencedGroups([](const RecGroup* recGroup) { recGroup->AddRef(); });
964 finalizedTypes_ = true;
965 return true;
968 // Visit every external recursion group that is referenced by the types in
969 // this recursion group.
970 template <typename Visitor>
971 void visitReferencedGroups(Visitor visitor) const {
972 auto visitValType = [this, visitor](ValType type) {
973 if (type.isTypeRef() && &type.typeDef()->recGroup() != this) {
974 visitor(&type.typeDef()->recGroup());
977 auto visitStorageType = [this, visitor](StorageType type) {
978 if (type.isTypeRef() && &type.typeDef()->recGroup() != this) {
979 visitor(&type.typeDef()->recGroup());
983 for (uint32_t i = 0; i < numTypes_; i++) {
984 const TypeDef& typeDef = types_[i];
986 if (typeDef.superTypeDef() &&
987 &typeDef.superTypeDef()->recGroup() != this) {
988 visitor(&typeDef.superTypeDef()->recGroup());
991 switch (typeDef.kind()) {
992 case TypeDefKind::Func: {
993 const FuncType& funcType = typeDef.funcType();
994 for (auto type : funcType.args()) {
995 visitValType(type);
997 for (auto type : funcType.results()) {
998 visitValType(type);
1000 break;
1002 case TypeDefKind::Struct: {
1003 const StructType& structType = typeDef.structType();
1004 for (const auto& field : structType.fields_) {
1005 visitStorageType(field.type);
1007 break;
1009 case TypeDefKind::Array: {
1010 const ArrayType& arrayType = typeDef.arrayType();
1011 visitStorageType(arrayType.elementType_);
1012 break;
1014 case TypeDefKind::None: {
1015 MOZ_CRASH();
1021 public:
1022 ~RecGroup() {
1023 // Release the referenced recursion groups if we acquired references to
1024 // them. Do this before the type definitions are destroyed below.
1025 if (finalizedTypes_) {
1026 finalizedTypes_ = false;
1027 visitReferencedGroups(
1028 [](const RecGroup* recGroup) { recGroup->Release(); });
1031 if (vectors_) {
1032 js_free((void*)vectors_);
1033 vectors_ = nullptr;
1036 // Call destructors on all the type definitions.
1037 for (uint32_t i = 0; i < numTypes_; i++) {
1038 type(i).~TypeDef();
1042 // Recursion groups cannot be copied or moved
1043 RecGroup& operator=(const RecGroup&) = delete;
1044 RecGroup& operator=(RecGroup&&) = delete;
1046 // Get the type definition at the group type index (not module type index).
1047 TypeDef& type(uint32_t groupTypeIndex) {
1048 // We cannot mutate type definitions after we've finalized them
1049 MOZ_ASSERT(!finalizedTypes_);
1050 return types_[groupTypeIndex];
1052 const TypeDef& type(uint32_t groupTypeIndex) const {
1053 return types_[groupTypeIndex];
1056 // The number of types stored in this recursion group.
1057 uint32_t numTypes() const { return numTypes_; }
1059 // Get the index of a type definition that's in this recursion group.
1060 uint32_t indexOf(const TypeDef* typeDef) const {
1061 MOZ_ASSERT(typeDef >= types_);
1062 size_t groupTypeIndex = (size_t)(typeDef - types_);
1063 MOZ_ASSERT(groupTypeIndex < numTypes());
1064 return (uint32_t)groupTypeIndex;
1067 HashNumber hash() const {
1068 HashNumber hn = 0;
1069 for (uint32_t i = 0; i < numTypes(); i++) {
1070 hn = mozilla::AddToHash(hn, types_[i].hash());
1072 return hn;
1075 // Matches two recursion groups for isorecursive equality. See
1076 // "Matching type definitions" in WasmValType.h for more background.
1077 static bool matches(const RecGroup& lhs, const RecGroup& rhs) {
1078 if (lhs.numTypes() != rhs.numTypes()) {
1079 return false;
1081 for (uint32_t i = 0; i < lhs.numTypes(); i++) {
1082 if (!TypeDef::matches(lhs.type(i), rhs.type(i))) {
1083 return false;
1086 return true;
1090 // Remove all types from the canonical type set that are not referenced from
1091 // outside the type set.
1092 extern void PurgeCanonicalTypes();
1094 using SharedRecGroup = RefPtr<const RecGroup>;
1095 using MutableRecGroup = RefPtr<RecGroup>;
1096 using SharedRecGroupVector = Vector<SharedRecGroup, 0, SystemAllocPolicy>;
1098 //=========================================================================
1099 // TypeContext
1101 // A type context holds the recursion groups and corresponding type definitions
1102 // defined in a module.
1103 class TypeContext : public AtomicRefCounted<TypeContext> {
1104 FeatureArgs features_;
1105 // The pending recursion group that is currently being constructed
1106 MutableRecGroup pendingRecGroup_;
1107 // An in-order list of all the recursion groups defined in this module
1108 SharedRecGroupVector recGroups_;
1109 // An in-order list of the type definitions in the module. Each type is
1110 // stored in a recursion group.
1111 TypeDefPtrVector types_;
1112 // A map from type definition to the original module index.
1113 TypeDefPtrToIndexMap moduleIndices_;
1115 static SharedRecGroup canonicalizeGroup(SharedRecGroup recGroup);
1117 public:
1118 TypeContext() = default;
1119 explicit TypeContext(const FeatureArgs& features) : features_(features) {}
1120 ~TypeContext();
1122 size_t sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const {
1123 return types_.sizeOfExcludingThis(mallocSizeOf) +
1124 moduleIndices_.shallowSizeOfExcludingThis(mallocSizeOf);
1127 // Disallow copy, allow move initialization
1128 TypeContext(const TypeContext&) = delete;
1129 TypeContext& operator=(const TypeContext&) = delete;
1130 TypeContext(TypeContext&&) = delete;
1131 TypeContext& operator=(TypeContext&&) = delete;
1133 // Begin creating a recursion group with the specified number of types.
1134 // Returns a recursion group to be filled in with type definitions. This must
1135 // be paired with `endGroup`.
1136 [[nodiscard]] MutableRecGroup startRecGroup(uint32_t numTypes) {
1137 // We must not have a pending group
1138 MOZ_ASSERT(!pendingRecGroup_);
1140 // Create the group and add it to the list of groups
1141 MutableRecGroup recGroup = RecGroup::allocate(numTypes);
1142 if (!recGroup || !addRecGroup(recGroup)) {
1143 return nullptr;
1146 // Store this group for later use in endRecGroup
1147 pendingRecGroup_ = recGroup;
1148 return recGroup;
1151 // Finish creation of a recursion group after type definitions have been
1152 // initialized. This must be paired with `startGroup`.
1153 [[nodiscard]] bool endRecGroup() {
1154 // We must have started a recursion group
1155 MOZ_ASSERT(pendingRecGroup_);
1156 MutableRecGroup recGroup = pendingRecGroup_;
1157 pendingRecGroup_ = nullptr;
1159 // Finalize the type definitions in the recursion group
1160 if (!recGroup->finalizeDefinitions()) {
1161 return false;
1164 // Canonicalize the recursion group
1165 SharedRecGroup canonicalRecGroup = canonicalizeGroup(recGroup);
1166 if (!canonicalRecGroup) {
1167 return false;
1170 // Nothing left to do if this group became the canonical group
1171 if (canonicalRecGroup == recGroup) {
1172 return true;
1175 // Store the canonical group into the list
1176 recGroups_.back() = canonicalRecGroup;
1178 // Overwrite all the entries we stored into the index space maps when we
1179 // started this group.
1180 MOZ_ASSERT(recGroup->numTypes() == canonicalRecGroup->numTypes());
1181 for (uint32_t groupTypeIndex = 0; groupTypeIndex < recGroup->numTypes();
1182 groupTypeIndex++) {
1183 uint32_t typeIndex = length() - recGroup->numTypes() + groupTypeIndex;
1184 const TypeDef* oldTypeDef = types_[typeIndex];
1185 const TypeDef* canonTypeDef = &canonicalRecGroup->type(groupTypeIndex);
1187 types_[typeIndex] = canonTypeDef;
1188 moduleIndices_.remove(oldTypeDef);
1190 // Ensure there is an module index entry pointing to the canonical type
1191 // definition. Don't overwrite it if it already exists, serialization
1192 // relies on the module index map pointing to the first occurrence of a
1193 // type definition to avoid creating forward references that didn't exist
1194 // in the original module.
1195 auto canonTypeIndexEntry = moduleIndices_.lookupForAdd(canonTypeDef);
1196 if (!canonTypeIndexEntry &&
1197 !moduleIndices_.add(canonTypeIndexEntry, canonTypeDef, typeIndex)) {
1198 return false;
1202 return true;
1205 // Finish creation of a recursion group after type definitions have been
1206 // initialized. This must be paired with `startGroup`.
1207 [[nodiscard]] bool addRecGroup(SharedRecGroup recGroup) {
1208 // We must not have a pending group
1209 MOZ_ASSERT(!pendingRecGroup_);
1211 // Add it to the list of groups
1212 if (!recGroups_.append(recGroup)) {
1213 return false;
1216 // Store the types of the group into our index space maps. These may get
1217 // overwritten if this group is being added by `startRecGroup` and we
1218 // overwrite it with a canonical group in `endRecGroup`. We need to do
1219 // this before finishing though, because these entries will be used by
1220 // decoding and error printing.
1221 for (uint32_t groupTypeIndex = 0; groupTypeIndex < recGroup->numTypes();
1222 groupTypeIndex++) {
1223 const TypeDef* typeDef = &recGroup->type(groupTypeIndex);
1224 uint32_t typeIndex = types_.length();
1225 if (!types_.append(typeDef) || !moduleIndices_.put(typeDef, typeIndex)) {
1226 return false;
1229 return true;
1232 template <typename T>
1233 [[nodiscard]] const TypeDef* addType(T&& type) {
1234 MutableRecGroup recGroup = startRecGroup(1);
1235 if (!recGroup) {
1236 return nullptr;
1238 recGroup->type(0) = std::move(type);
1239 if (!endRecGroup()) {
1240 return nullptr;
1242 return &this->type(length() - 1);
1245 const TypeDef& type(uint32_t index) const { return *types_[index]; }
1246 const TypeDef& operator[](uint32_t index) const { return *types_[index]; }
1248 bool empty() const { return types_.empty(); }
1249 uint32_t length() const { return types_.length(); }
1251 const SharedRecGroupVector& groups() const { return recGroups_; }
1253 // Map from type definition to index
1255 uint32_t indexOf(const TypeDef& typeDef) const {
1256 auto moduleIndex = moduleIndices_.readonlyThreadsafeLookup(&typeDef);
1257 MOZ_RELEASE_ASSERT(moduleIndex.found());
1258 return moduleIndex->value();
1262 using SharedTypeContext = RefPtr<const TypeContext>;
1263 using MutableTypeContext = RefPtr<TypeContext>;
1265 //=========================================================================
1266 // TypeHandle
1268 // An unambiguous strong reference to a type definition in a specific type
1269 // context.
1270 class TypeHandle {
1271 private:
1272 SharedTypeContext context_;
1273 uint32_t index_;
1275 public:
1276 TypeHandle(SharedTypeContext context, uint32_t index)
1277 : context_(context), index_(index) {
1278 MOZ_ASSERT(index_ < context_->length());
1280 TypeHandle(SharedTypeContext context, const TypeDef& def)
1281 : context_(context), index_(context->indexOf(def)) {}
1283 TypeHandle(const TypeHandle&) = default;
1284 TypeHandle& operator=(const TypeHandle&) = default;
1286 const SharedTypeContext& context() const { return context_; }
1287 uint32_t index() const { return index_; }
1288 const TypeDef& def() const { return context_->type(index_); }
1291 //=========================================================================
1292 // misc
1294 /* static */
1295 inline uintptr_t TypeDef::forMatch(const TypeDef* typeDef,
1296 const RecGroup* recGroup) {
1297 // TypeDef is aligned sufficiently to allow a tag to distinguish a local type
1298 // reference (index) from a non-local type reference (pointer).
1299 static_assert(alignof(TypeDef) > 1);
1300 MOZ_ASSERT((uintptr_t(typeDef) & 0x1) == 0);
1302 // Return a tagged index for local type references
1303 if (typeDef && &typeDef->recGroup() == recGroup) {
1304 return uintptr_t(recGroup->indexOf(typeDef)) | 0x1;
1307 // Return an untagged pointer for non-local type references
1308 return uintptr_t(typeDef);
1311 /* static */
1312 inline MatchTypeCode MatchTypeCode::forMatch(PackedTypeCode ptc,
1313 const RecGroup* recGroup) {
1314 MatchTypeCode mtc = {};
1315 mtc.typeCode = PackedRepr(ptc.typeCode());
1316 mtc.typeRef = TypeDef::forMatch(ptc.typeDef(), recGroup);
1317 mtc.nullable = ptc.isNullable();
1318 return mtc;
1321 template <class T>
1322 void PackedType<T>::AddRef() const {
1323 if (!isRefType()) {
1324 return;
1326 refType().AddRef();
1328 template <class T>
1329 void PackedType<T>::Release() const {
1330 if (!isRefType()) {
1331 return;
1333 refType().Release();
1336 void RefType::AddRef() const {
1337 if (!isTypeRef()) {
1338 return;
1340 typeDef()->recGroup().AddRef();
1342 void RefType::Release() const {
1343 if (!isTypeRef()) {
1344 return;
1346 typeDef()->recGroup().Release();
1349 inline RefTypeHierarchy RefType::hierarchy() const {
1350 switch (kind()) {
1351 case RefType::Func:
1352 case RefType::NoFunc:
1353 return RefTypeHierarchy::Func;
1354 case RefType::Extern:
1355 case RefType::NoExtern:
1356 return RefTypeHierarchy::Extern;
1357 case RefType::Exn:
1358 return RefTypeHierarchy::Exn;
1359 case RefType::Any:
1360 case RefType::None:
1361 case RefType::I31:
1362 case RefType::Eq:
1363 case RefType::Struct:
1364 case RefType::Array:
1365 return RefTypeHierarchy::Any;
1366 case RefType::TypeRef:
1367 switch (typeDef()->kind()) {
1368 case TypeDefKind::Struct:
1369 case TypeDefKind::Array:
1370 return RefTypeHierarchy::Any;
1371 case TypeDefKind::Func:
1372 return RefTypeHierarchy::Func;
1373 case TypeDefKind::None:
1374 MOZ_CRASH();
1377 MOZ_CRASH("switch is exhaustive");
1380 inline TableRepr RefType::tableRepr() const {
1381 switch (hierarchy()) {
1382 case RefTypeHierarchy::Any:
1383 case RefTypeHierarchy::Extern:
1384 case RefTypeHierarchy::Exn:
1385 return TableRepr::Ref;
1386 case RefTypeHierarchy::Func:
1387 return TableRepr::Func;
1389 MOZ_CRASH("switch is exhaustive");
1392 inline bool RefType::isFuncHierarchy() const {
1393 return hierarchy() == RefTypeHierarchy::Func;
1395 inline bool RefType::isExternHierarchy() const {
1396 return hierarchy() == RefTypeHierarchy::Extern;
1398 inline bool RefType::isAnyHierarchy() const {
1399 return hierarchy() == RefTypeHierarchy::Any;
1401 inline bool RefType::isExnHierarchy() const {
1402 return hierarchy() == RefTypeHierarchy::Exn;
1405 /* static */
1406 inline bool RefType::isSubTypeOf(RefType subType, RefType superType) {
1407 // Anything is a subtype of itself.
1408 if (subType == superType) {
1409 return true;
1412 // A subtype must have the same nullability as the supertype or the
1413 // supertype must be nullable.
1414 if (subType.isNullable() && !superType.isNullable()) {
1415 return false;
1418 // Non type-index references are subtypes if they have the same kind
1419 if (!subType.isTypeRef() && !superType.isTypeRef() &&
1420 subType.kind() == superType.kind()) {
1421 return true;
1424 // eqref is a subtype of anyref
1425 if (subType.isEq() && superType.isAny()) {
1426 return true;
1429 // i31ref is a subtype of eqref
1430 if (subType.isI31() && (superType.isAny() || superType.isEq())) {
1431 return true;
1434 // structref/arrayref are subtypes of eqref and anyref
1435 if ((subType.isStruct() || subType.isArray()) &&
1436 (superType.isAny() || superType.isEq())) {
1437 return true;
1440 // Structs are subtypes of structref, eqref and anyref
1441 if (subType.isTypeRef() && subType.typeDef()->isStructType() &&
1442 (superType.isAny() || superType.isEq() || superType.isStruct())) {
1443 return true;
1446 // Arrays are subtypes of arrayref, eqref and anyref
1447 if (subType.isTypeRef() && subType.typeDef()->isArrayType() &&
1448 (superType.isAny() || superType.isEq() || superType.isArray())) {
1449 return true;
1452 // Funcs are subtypes of funcref
1453 if (subType.isTypeRef() && subType.typeDef()->isFuncType() &&
1454 superType.isFunc()) {
1455 return true;
1458 // Type references can be subtypes
1459 if (subType.isTypeRef() && superType.isTypeRef()) {
1460 return TypeDef::isSubTypeOf(subType.typeDef(), superType.typeDef());
1463 // No func is the bottom type of the func hierarchy
1464 if (subType.isNoFunc() && superType.hierarchy() == RefTypeHierarchy::Func) {
1465 return true;
1468 // No extern is the bottom type of the extern hierarchy
1469 if (subType.isNoExtern() &&
1470 superType.hierarchy() == RefTypeHierarchy::Extern) {
1471 return true;
1474 // None is the bottom type of the any hierarchy
1475 if (subType.isNone() && superType.hierarchy() == RefTypeHierarchy::Any) {
1476 return true;
1479 return false;
1482 /* static */
1483 inline bool RefType::castPossible(RefType sourceType, RefType destType) {
1484 // Nullable types always have null in common.
1485 if (sourceType.isNullable() && destType.isNullable()) {
1486 return true;
1489 // At least one of the types is non-nullable, so the only common values can be
1490 // non-null. Therefore, if either type is a bottom type, common values are
1491 // impossible.
1492 if (sourceType.isRefBottom() || destType.isRefBottom()) {
1493 return false;
1496 // After excluding bottom types, our type hierarchy is a tree, and after
1497 // excluding nulls, subtype relationships are sufficient to tell if the types
1498 // share any values. If neither type is a subtype of the other, then they are
1499 // on different branches of the tree and completely disjoint.
1500 RefType sourceNonNull = sourceType.withIsNullable(false);
1501 RefType destNonNull = destType.withIsNullable(false);
1502 return RefType::isSubTypeOf(sourceNonNull, destNonNull) ||
1503 RefType::isSubTypeOf(destNonNull, sourceNonNull);
1506 //=========================================================================
1507 // [SMDOC] Signatures and runtime types
1509 // TypeIdDesc describes the runtime representation of a TypeDef suitable for
1510 // type equality checks. The kind of representation depends on whether the type
1511 // is a function or a GC type. This design is in flux and will evolve.
1513 // # Function types
1515 // For functions in the general case, a FuncType is allocated and stored in a
1516 // process-wide hash table, so that pointer equality implies structural
1517 // equality. This process does not correctly handle type references (which would
1518 // require hash-consing of infinite-trees), but that's okay while
1519 // function-references and gc-types are experimental.
1521 // A pointer to the hash table entry is stored in the global data
1522 // area for each instance, and TypeIdDesc gives the offset to this entry.
1524 // ## Immediate function types
1526 // As an optimization for the 99% case where the FuncType has a small number of
1527 // parameters, the FuncType is bit-packed into a uint32 immediate value so that
1528 // integer equality implies structural equality. Both cases can be handled with
1529 // a single comparison by always setting the LSB for the immediates
1530 // (the LSB is necessarily 0 for allocated FuncType pointers due to alignment).
1532 // # GC types
1534 // For GC types, an entry is always created in the global data area and a
1535 // unique RttValue (see wasm/WasmGcObject.h) is stored there. This RttValue
1536 // is the value given by 'rtt.canon $t' for each type definition. As each entry
1537 // is given a unique value and no canonicalization is done (which would require
1538 // hash-consing of infinite-trees), this is not yet spec compliant.
1540 // # wasm::Instance and the global type context
1542 // As GC objects may outlive the module they are created in, types are
1543 // additionally transferred to a wasm::Context (which is part of JSContext) upon
1544 // instantiation. This wasm::Context contains the 'global type context' that
1545 // RTTValues refer to by type index. Types are never freed from the global type
1546 // context as that would shift the index space. In the future, this will be
1547 // fixed.
1549 } // namespace wasm
1550 } // namespace js
1552 #endif // wasm_type_def_h