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"
38 using mozilla::CheckedInt32
;
39 using mozilla::MallocSizeOf
;
43 //=========================================================================
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.
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
65 uint32_t immediateTypeId_
= NO_IMMEDIATE_TYPE_ID
;
67 // This function type cannot be packed into an immediate for call_indirect
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
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())) {
92 for (ValType result
: results()) {
93 if (result
.isTypeRef()) {
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())) {
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 {
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());
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()) {
167 for (uint32_t i
= 0; i
< lhs
.args_
.length(); i
++) {
168 if (lhs
.args_
[i
].forMatch(lhsRecGroup
) !=
169 rhs
.args_
[i
].forMatch(rhsRecGroup
)) {
173 for (uint32_t i
= 0; i
< lhs
.results_
.length(); i
++) {
174 if (lhs
.results_
[i
].forMatch(lhsRecGroup
) !=
175 rhs
.results_
[i
].forMatch(rhsRecGroup
)) {
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
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()) {
199 // A subtype must have exactly as many returns as its supertype
200 if (subType
.results().length() != superType
.results().length()) {
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
])) {
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
])) {
221 bool canHaveJitEntry() const;
222 bool canHaveJitExit() const;
224 bool hasInt64Arg() const {
225 for (ValType arg
: args()) {
226 if (arg
.kind() == ValType::Kind::I64
) {
233 bool hasUnexposableArgOrRet() const {
234 for (ValType arg
: args()) {
235 if (!arg
.isExposable()) {
239 for (ValType result
: results()) {
240 if (!result
.isExposable()) {
247 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
) const;
248 WASM_DECLARE_FRIEND_SERIALIZE(FuncType
);
251 //=========================================================================
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.
263 HashNumber
hash(const RecGroup
* recGroup
) const {
265 hn
= mozilla::AddToHash(hn
, type
.forMatch(recGroup
).hash());
266 hn
= mozilla::AddToHash(hn
, HashNumber(isMutable
));
270 // Checks if two struct fields are compatible in a given subtyping
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
);
288 using StructFieldVector
= Vector
<StructField
, 0, SystemAllocPolicy
>;
290 using InlineTraceOffsetVector
= Vector
<uint32_t, 2, SystemAllocPolicy
>;
291 using OutlineTraceOffsetVector
= Vector
<uint32_t, 0, SystemAllocPolicy
>;
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_
;
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_
)) {
317 [[nodiscard
]] bool init();
319 bool isDefaultable() const {
320 for (auto& field
: fields_
) {
321 if (!field
.type
.isDefaultable()) {
328 HashNumber
hash(const RecGroup
* recGroup
) const {
330 for (const StructField
& field
: fields_
) {
331 hn
= mozilla::AddToHash(hn
, field
.hash(recGroup
));
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()) {
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
)) {
355 // Checks if two struct types are compatible in a given subtyping
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()) {
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
])) {
374 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
) const;
375 WASM_DECLARE_FRIEND_SERIALIZE(StructType
);
378 using StructTypeVector
= Vector
<StructType
, 0, SystemAllocPolicy
>;
380 // Utility for computing field offset and alignments, and total size for
381 // structs and tags. This is complicated by fact that a WasmStructObject has
382 // an inline area, which is used first, and if that fills up an optional
383 // C++-heap-allocated outline area is used. We need to be careful not to
384 // split any data item across the boundary. This is ensured as follows:
386 // (1) the possible field sizes are 1, 2, 4, 8 and 16 only.
387 // (2) each field is "naturally aligned" -- aligned to its size.
388 // (3) MaxInlineBytes (the size of the inline area) % 16 == 0.
390 // From (1) and (2), it follows that all fields are placed so that their first
391 // and last bytes fall within the same 16-byte chunk. That is,
392 // offset_of_first_byte_of_field / 16 == offset_of_last_byte_of_field / 16.
394 // Given that, it follows from (3) that all fields fall completely within
395 // either the inline or outline areas; no field crosses the boundary.
397 CheckedInt32 sizeSoFar
= 0;
398 uint32_t structAlignment
= 1;
401 // The field adders return the offset of the the field.
402 CheckedInt32
addField(StorageType type
);
404 // The close method rounds up the structure size to the appropriate
405 // alignment and returns that size.
406 CheckedInt32
close();
409 //=========================================================================
414 // The kind of value stored in this array
415 StorageType elementType_
;
416 // Whether this array is mutable or not
420 ArrayType() : isMutable_(false) {}
421 ArrayType(StorageType elementType
, bool isMutable
)
422 : elementType_(elementType
), isMutable_(isMutable
) {}
424 ArrayType(const ArrayType
&) = default;
425 ArrayType
& operator=(const ArrayType
&) = default;
427 ArrayType(ArrayType
&&) = default;
428 ArrayType
& operator=(ArrayType
&&) = default;
430 [[nodiscard
]] bool clone(const ArrayType
& src
) {
431 elementType_
= src
.elementType_
;
432 isMutable_
= src
.isMutable_
;
436 bool isDefaultable() const { return elementType_
.isDefaultable(); }
438 HashNumber
hash(const RecGroup
* recGroup
) const {
440 hn
= mozilla::AddToHash(hn
, elementType_
.forMatch(recGroup
).hash());
441 hn
= mozilla::AddToHash(hn
, HashNumber(isMutable_
));
445 // Matches two array types for isorecursive equality. See
446 // "Matching type definitions" in WasmValType.h for more background.
447 static bool matches(const RecGroup
* lhsRecGroup
, const ArrayType
& lhs
,
448 const RecGroup
* rhsRecGroup
, const ArrayType
& rhs
) {
449 return lhs
.isMutable_
== rhs
.isMutable_
&&
450 lhs
.elementType_
.forMatch(lhsRecGroup
) ==
451 rhs
.elementType_
.forMatch(rhsRecGroup
);
454 // Checks if two arrays are compatible in a given subtyping relationship.
455 static bool canBeSubTypeOf(const ArrayType
& subType
,
456 const ArrayType
& superType
) {
457 // Mutable fields are invariant w.r.t. field types
458 if (subType
.isMutable_
&& superType
.isMutable_
) {
459 return subType
.elementType_
== superType
.elementType_
;
462 // Immutable fields are covariant w.r.t. field types
463 if (!subType
.isMutable_
&& !superType
.isMutable_
) {
464 return StorageType::isSubTypeOf(subType
.elementType_
,
465 superType
.elementType_
);
471 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
) const;
474 WASM_DECLARE_CACHEABLE_POD(ArrayType
);
476 using ArrayTypeVector
= Vector
<ArrayType
, 0, SystemAllocPolicy
>;
478 //=========================================================================
481 // [SMDOC] Super type vector
483 // A super type vector is a vector representation of the linked list of super
484 // types that a type definition has. Every element is a raw pointer to another
485 // super type vector - they are one-to-one with type definitions, so they are
486 // functionally equivalent. It is possible to form a vector here because
487 // subtypes in wasm form trees, not DAGs, with every type having at most one
490 // The first element in the vector is the 'root' type definition without a
491 // super type. The last element is to the type definition itself.
493 // ## Subtype checking
495 // The only purpose of a super type vector is to support constant time
496 // subtyping checks. This is not free, it comes at the cost of worst case N^2
497 // metadata size growth. We limit the max subtyping depth to counter this.
499 // To perform a subtype check we rely on the following:
500 // (1) a type A is a subtype (<:) of type B iff:
501 // type A == type B OR
502 // type B is reachable by following declared super types of type A
503 // (2) we order super type vectors from least to most derived types
504 // (3) the 'subtyping depth' of all type definitions is statically known
506 // With the above, we know that if type B is a super type of type A, that it
507 // must be in A's super type vector at type B's subtyping depth. We can
508 // therefore just do an index and comparison to determine if that's the case.
512 // For the following type section:
514 // 12: (type (struct))
516 // 34: (type (sub 12 (struct)))
518 // 56: (type (sub 34 (struct)))
520 // 78: (type (sub 56 (struct)))
523 // (type 12) would have the following super type vector:
526 // (type 78) would have the following super type vector:
527 // [(type 12), (type 34), (type 56), (type 78)]
529 // Checking that (type 78) <: (type 12) can use the fact that (type 12) will
530 // always be present at depth 0 of any super type vector it is in, and
531 // therefore check the vector at that index.
535 // As a further optimization to avoid bounds checking, we guarantee that all
536 // super type vectors are at least `MinSuperTypeVectorLength`. All checks
537 // against indices that we know statically are at/below that can skip bounds
538 // checking. Extra entries added to reach the minimum size are initialized to
540 class SuperTypeVector
{
541 SuperTypeVector() : typeDef_(nullptr), length_(0) {}
543 // The TypeDef for which this is the supertype vector. That TypeDef should
544 // point back to this SuperTypeVector.
545 const TypeDef
* typeDef_
;
547 // A cached copy of subTypingDepth from TypeDef.
548 uint32_t subTypingDepth_
;
550 // The length of types stored inline below.
554 // Raw pointers to the super types of this type definition. Ordered from
555 // least-derived to most-derived. Do not add any fields after this point.
556 const SuperTypeVector
* types_
[0];
558 // Batch allocate super type vectors for all the types in a recursion group.
559 // Returns a pointer to the first super type vector, which can be used to
561 [[nodiscard
]] static const SuperTypeVector
* createMultipleForRecGroup(
564 const TypeDef
* typeDef() const { return typeDef_
; }
566 uint32_t length() const { return length_
; }
568 const SuperTypeVector
* type(size_t index
) const {
569 MOZ_ASSERT(index
< length_
);
570 return types_
[index
];
573 // The length of a super type vector for a specific type def.
574 static size_t lengthForTypeDef(const TypeDef
& typeDef
);
575 // The byte size of a super type vector for a specific type def.
576 static size_t byteSizeForTypeDef(const TypeDef
& typeDef
);
578 static size_t offsetOfSubTypingDepth() {
579 return offsetof(SuperTypeVector
, subTypingDepth_
);
581 static size_t offsetOfLength() { return offsetof(SuperTypeVector
, length_
); }
582 static size_t offsetOfSelfTypeDef() {
583 return offsetof(SuperTypeVector
, typeDef_
);
585 static size_t offsetOfSTVInVector(uint32_t subTypingDepth
);
588 // Ensure it is safe to use `sizeof(SuperTypeVector)` to find the offset of
590 static_assert(offsetof(SuperTypeVector
, types_
) == sizeof(SuperTypeVector
));
592 //=========================================================================
593 // TypeDef and supporting types
595 // A tagged container for the various types that can be present in a wasm
596 // module's type section.
598 enum class TypeDefKind
: uint8_t {
606 uint32_t offsetToRecGroup_
;
608 // The supertype vector for this TypeDef. That SuperTypeVector should point
609 // back to this TypeDef.
610 const SuperTypeVector
* superTypeVector_
;
612 const TypeDef
* superTypeDef_
;
613 uint16_t subTypingDepth_
;
618 StructType structType_
;
619 ArrayType arrayType_
;
622 void setRecGroup(RecGroup
* recGroup
) {
623 uintptr_t recGroupAddr
= (uintptr_t)recGroup
;
624 uintptr_t typeDefAddr
= (uintptr_t)this;
625 MOZ_ASSERT(typeDefAddr
> recGroupAddr
);
626 MOZ_ASSERT(typeDefAddr
- recGroupAddr
<= UINT32_MAX
);
627 offsetToRecGroup_
= typeDefAddr
- recGroupAddr
;
631 explicit TypeDef(RecGroup
* recGroup
)
632 : offsetToRecGroup_(0),
633 superTypeVector_(nullptr),
634 superTypeDef_(nullptr),
637 kind_(TypeDefKind::None
) {
638 setRecGroup(recGroup
);
643 case TypeDefKind::Func
:
644 funcType_
.~FuncType();
646 case TypeDefKind::Struct
:
647 structType_
.~StructType();
649 case TypeDefKind::Array
:
650 arrayType_
.~ArrayType();
652 case TypeDefKind::None
:
657 TypeDef
& operator=(FuncType
&& that
) noexcept
{
658 MOZ_ASSERT(isNone());
659 kind_
= TypeDefKind::Func
;
660 new (&funcType_
) FuncType(std::move(that
));
664 TypeDef
& operator=(StructType
&& that
) noexcept
{
665 MOZ_ASSERT(isNone());
666 kind_
= TypeDefKind::Struct
;
667 new (&structType_
) StructType(std::move(that
));
671 TypeDef
& operator=(ArrayType
&& that
) noexcept
{
672 MOZ_ASSERT(isNone());
673 kind_
= TypeDefKind::Array
;
674 new (&arrayType_
) ArrayType(std::move(that
));
678 const SuperTypeVector
* superTypeVector() const { return superTypeVector_
; }
680 void setSuperTypeVector(const SuperTypeVector
* superTypeVector
) {
681 superTypeVector_
= superTypeVector
;
684 static size_t offsetOfKind() { return offsetof(TypeDef
, kind_
); }
686 static size_t offsetOfSuperTypeVector() {
687 return offsetof(TypeDef
, superTypeVector_
);
690 static size_t offsetOfSubTypingDepth() {
691 return offsetof(TypeDef
, subTypingDepth_
);
694 const TypeDef
* superTypeDef() const { return superTypeDef_
; }
696 bool isFinal() const { return isFinal_
; }
698 uint16_t subTypingDepth() const { return subTypingDepth_
; }
700 const RecGroup
& recGroup() const {
701 uintptr_t typeDefAddr
= (uintptr_t)this;
702 uintptr_t recGroupAddr
= typeDefAddr
- offsetToRecGroup_
;
703 return *(const RecGroup
*)recGroupAddr
;
706 TypeDefKind
kind() const { return kind_
; }
708 bool isNone() const { return kind_
== TypeDefKind::None
; }
710 bool isFuncType() const { return kind_
== TypeDefKind::Func
; }
712 bool isStructType() const { return kind_
== TypeDefKind::Struct
; }
714 bool isArrayType() const { return kind_
== TypeDefKind::Array
; }
716 const FuncType
& funcType() const {
717 MOZ_ASSERT(isFuncType());
721 FuncType
& funcType() {
722 MOZ_ASSERT(isFuncType());
726 const StructType
& structType() const {
727 MOZ_ASSERT(isStructType());
731 StructType
& structType() {
732 MOZ_ASSERT(isStructType());
736 const ArrayType
& arrayType() const {
737 MOZ_ASSERT(isArrayType());
741 ArrayType
& arrayType() {
742 MOZ_ASSERT(isArrayType());
746 // Get a value that can be used for matching type definitions across
747 // different recursion groups.
748 static inline uintptr_t forMatch(const TypeDef
* typeDef
,
749 const RecGroup
* recGroup
);
751 HashNumber
hash() const {
752 HashNumber hn
= HashNumber(kind_
);
753 hn
= mozilla::AddToHash(hn
, TypeDef::forMatch(superTypeDef_
, &recGroup()));
754 hn
= mozilla::AddToHash(hn
, isFinal_
);
756 case TypeDefKind::Func
:
757 hn
= mozilla::AddToHash(hn
, funcType_
.hash(&recGroup()));
759 case TypeDefKind::Struct
:
760 hn
= mozilla::AddToHash(hn
, structType_
.hash(&recGroup()));
762 case TypeDefKind::Array
:
763 hn
= mozilla::AddToHash(hn
, arrayType_
.hash(&recGroup()));
765 case TypeDefKind::None
:
771 // Matches two type definitions for isorecursive equality. See
772 // "Matching type definitions" in WasmValType.h for more background.
773 static bool matches(const TypeDef
& lhs
, const TypeDef
& rhs
) {
774 if (lhs
.kind_
!= rhs
.kind_
) {
777 if (lhs
.isFinal_
!= rhs
.isFinal_
) {
780 if (TypeDef::forMatch(lhs
.superTypeDef_
, &lhs
.recGroup()) !=
781 TypeDef::forMatch(rhs
.superTypeDef_
, &rhs
.recGroup())) {
785 case TypeDefKind::Func
:
786 return FuncType::matches(&lhs
.recGroup(), lhs
.funcType_
,
787 &rhs
.recGroup(), rhs
.funcType_
);
788 case TypeDefKind::Struct
:
789 return StructType::matches(&lhs
.recGroup(), lhs
.structType_
,
790 &rhs
.recGroup(), rhs
.structType_
);
791 case TypeDefKind::Array
:
792 return ArrayType::matches(&lhs
.recGroup(), lhs
.arrayType_
,
793 &rhs
.recGroup(), rhs
.arrayType_
);
794 case TypeDefKind::None
:
795 MOZ_CRASH("can't match TypeDefKind::None");
800 // Checks if two type definitions are compatible in a given subtyping
802 static bool canBeSubTypeOf(const TypeDef
* subType
, const TypeDef
* superType
) {
803 if (subType
->kind() != superType
->kind()) {
807 // A subtype can't declare a final super type.
808 if (superType
->isFinal()) {
812 switch (subType
->kind_
) {
813 case TypeDefKind::Func
:
814 return FuncType::canBeSubTypeOf(subType
->funcType_
,
815 superType
->funcType_
);
816 case TypeDefKind::Struct
:
817 return StructType::canBeSubTypeOf(subType
->structType_
,
818 superType
->structType_
);
819 case TypeDefKind::Array
:
820 return ArrayType::canBeSubTypeOf(subType
->arrayType_
,
821 superType
->arrayType_
);
822 case TypeDefKind::None
:
828 void setSuperTypeDef(const TypeDef
* superTypeDef
) {
829 superTypeDef_
= superTypeDef
;
830 subTypingDepth_
= superTypeDef_
->subTypingDepth_
+ 1;
833 void setFinal(const bool value
) { isFinal_
= value
; }
835 // Checks if `subTypeDef` is a declared sub type of `superTypeDef`.
836 static bool isSubTypeOf(const TypeDef
* subTypeDef
,
837 const TypeDef
* superTypeDef
) {
838 // Fast path for when the types are equal
839 if (MOZ_LIKELY(subTypeDef
== superTypeDef
)) {
842 const SuperTypeVector
* subSTV
= subTypeDef
->superTypeVector();
844 // During construction of a recursion group, the super type vector may not
845 // have been computed yet, in which case we need to fall back to a linear
849 if (subTypeDef
== superTypeDef
) {
852 subTypeDef
= subTypeDef
->superTypeDef();
857 // The supertype vector does exist. So check it points back here.
858 MOZ_ASSERT(subSTV
->typeDef() == subTypeDef
);
860 // We need to check if `superTypeDef` is one of `subTypeDef`s super types
861 // by checking in `subTypeDef`s super type vector. We can use the static
862 // information of the depth of `superTypeDef` to index directly into the
864 uint32_t subTypingDepth
= superTypeDef
->subTypingDepth();
865 if (subTypingDepth
>= subSTV
->length()) {
869 const SuperTypeVector
* superSTV
= superTypeDef
->superTypeVector();
870 MOZ_ASSERT(superSTV
);
871 MOZ_ASSERT(superSTV
->typeDef() == superTypeDef
);
873 return subSTV
->type(subTypingDepth
) == superSTV
;
876 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
) const;
877 WASM_DECLARE_FRIEND_SERIALIZE(TypeDef
);
880 using SharedTypeDef
= RefPtr
<const TypeDef
>;
881 using MutableTypeDef
= RefPtr
<TypeDef
>;
883 using TypeDefVector
= Vector
<TypeDef
, 0, SystemAllocPolicy
>;
884 using TypeDefPtrVector
= Vector
<const TypeDef
*, 0, SystemAllocPolicy
>;
886 using TypeDefPtrToIndexMap
=
887 HashMap
<const TypeDef
*, uint32_t, PointerHasher
<const TypeDef
*>,
890 //=========================================================================
893 // A recursion group is a set of type definitions that may refer to each other
894 // or to type definitions in another recursion group. There is an ordering
895 // restriction on type references such that references across recursion groups
898 // Type definitions are stored inline in their containing recursion group, and
899 // have an offset to their containing recursion group. Recursion groups are
900 // atomically refcounted and hold strong references to other recursion groups
903 // Type equality is structural in WebAssembly, and we canonicalize recursion
904 // groups while building them so that pointer equality of types implies
905 // equality of types. There is a global hash set of weak pointers to recursion
906 // groups that holds the current canonical instance of a recursion group.
907 class RecGroup
: public AtomicRefCounted
<RecGroup
> {
908 // Whether this recursion group has been finished and acquired strong
909 // references to external recursion groups.
910 bool finalizedTypes_
;
911 // The number of types stored in this recursion group.
913 // The batch allocated super type vectors for all type definitions in this
915 const SuperTypeVector
* vectors_
;
916 // The first type definition stored inline in this recursion group.
919 friend class TypeContext
;
921 explicit RecGroup(uint32_t numTypes
)
922 : finalizedTypes_(false), numTypes_(numTypes
), vectors_(nullptr) {}
924 // Compute the size in bytes of a recursion group with the specified amount
926 static constexpr size_t sizeOfRecGroup(uint32_t numTypes
) {
927 static_assert(MaxTypes
<= SIZE_MAX
/ sizeof(TypeDef
));
928 return sizeof(RecGroup
) + sizeof(TypeDef
) * numTypes
;
931 // Allocate a recursion group with the specified amount of types. The type
932 // definitions will be ready to be filled in. Users must call `finish` once
933 // type definitions are initialized so that strong references to external
934 // recursion groups are taken.
935 static RefPtr
<RecGroup
> allocate(uint32_t numTypes
) {
936 // Allocate the recursion group with the correct size
937 RecGroup
* recGroup
= (RecGroup
*)js_malloc(sizeOfRecGroup(numTypes
));
942 // Construct the recursion group and types that are stored inline
943 new (recGroup
) RecGroup(numTypes
);
944 for (uint32_t i
= 0; i
< numTypes
; i
++) {
945 new (recGroup
->types_
+ i
) TypeDef(recGroup
);
950 // Finish initialization by acquiring strong references to groups referenced
951 // by type definitions.
952 [[nodiscard
]] bool finalizeDefinitions() {
953 MOZ_ASSERT(!finalizedTypes_
);
954 // Super type vectors are only needed for GC and have a size/time impact
955 // that we don't want to encur until we're ready for it. Only use them when
956 // GC is built into the binary.
957 vectors_
= SuperTypeVector::createMultipleForRecGroup(this);
961 visitReferencedGroups([](const RecGroup
* recGroup
) { recGroup
->AddRef(); });
962 finalizedTypes_
= true;
966 // Visit every external recursion group that is referenced by the types in
967 // this recursion group.
968 template <typename Visitor
>
969 void visitReferencedGroups(Visitor visitor
) const {
970 auto visitValType
= [this, visitor
](ValType type
) {
971 if (type
.isTypeRef() && &type
.typeDef()->recGroup() != this) {
972 visitor(&type
.typeDef()->recGroup());
975 auto visitStorageType
= [this, visitor
](StorageType type
) {
976 if (type
.isTypeRef() && &type
.typeDef()->recGroup() != this) {
977 visitor(&type
.typeDef()->recGroup());
981 for (uint32_t i
= 0; i
< numTypes_
; i
++) {
982 const TypeDef
& typeDef
= types_
[i
];
984 if (typeDef
.superTypeDef() &&
985 &typeDef
.superTypeDef()->recGroup() != this) {
986 visitor(&typeDef
.superTypeDef()->recGroup());
989 switch (typeDef
.kind()) {
990 case TypeDefKind::Func
: {
991 const FuncType
& funcType
= typeDef
.funcType();
992 for (auto type
: funcType
.args()) {
995 for (auto type
: funcType
.results()) {
1000 case TypeDefKind::Struct
: {
1001 const StructType
& structType
= typeDef
.structType();
1002 for (const auto& field
: structType
.fields_
) {
1003 visitStorageType(field
.type
);
1007 case TypeDefKind::Array
: {
1008 const ArrayType
& arrayType
= typeDef
.arrayType();
1009 visitStorageType(arrayType
.elementType_
);
1012 case TypeDefKind::None
: {
1021 // Release the referenced recursion groups if we acquired references to
1022 // them. Do this before the type definitions are destroyed below.
1023 if (finalizedTypes_
) {
1024 finalizedTypes_
= false;
1025 visitReferencedGroups(
1026 [](const RecGroup
* recGroup
) { recGroup
->Release(); });
1030 js_free((void*)vectors_
);
1034 // Call destructors on all the type definitions.
1035 for (uint32_t i
= 0; i
< numTypes_
; i
++) {
1040 // Recursion groups cannot be copied or moved
1041 RecGroup
& operator=(const RecGroup
&) = delete;
1042 RecGroup
& operator=(RecGroup
&&) = delete;
1044 // Get the type definition at the group type index (not module type index).
1045 TypeDef
& type(uint32_t groupTypeIndex
) {
1046 // We cannot mutate type definitions after we've finalized them
1047 MOZ_ASSERT(!finalizedTypes_
);
1048 return types_
[groupTypeIndex
];
1050 const TypeDef
& type(uint32_t groupTypeIndex
) const {
1051 return types_
[groupTypeIndex
];
1054 // The number of types stored in this recursion group.
1055 uint32_t numTypes() const { return numTypes_
; }
1057 // Get the index of a type definition that's in this recursion group.
1058 uint32_t indexOf(const TypeDef
* typeDef
) const {
1059 MOZ_ASSERT(typeDef
>= types_
);
1060 size_t groupTypeIndex
= (size_t)(typeDef
- types_
);
1061 MOZ_ASSERT(groupTypeIndex
< numTypes());
1062 return (uint32_t)groupTypeIndex
;
1065 HashNumber
hash() const {
1067 for (uint32_t i
= 0; i
< numTypes(); i
++) {
1068 hn
= mozilla::AddToHash(hn
, types_
[i
].hash());
1073 // Matches two recursion groups for isorecursive equality. See
1074 // "Matching type definitions" in WasmValType.h for more background.
1075 static bool matches(const RecGroup
& lhs
, const RecGroup
& rhs
) {
1076 if (lhs
.numTypes() != rhs
.numTypes()) {
1079 for (uint32_t i
= 0; i
< lhs
.numTypes(); i
++) {
1080 if (!TypeDef::matches(lhs
.type(i
), rhs
.type(i
))) {
1088 // Remove all types from the canonical type set that are not referenced from
1089 // outside the type set.
1090 extern void PurgeCanonicalTypes();
1092 using SharedRecGroup
= RefPtr
<const RecGroup
>;
1093 using MutableRecGroup
= RefPtr
<RecGroup
>;
1094 using SharedRecGroupVector
= Vector
<SharedRecGroup
, 0, SystemAllocPolicy
>;
1096 //=========================================================================
1099 // A type context holds the recursion groups and corresponding type definitions
1100 // defined in a module.
1101 class TypeContext
: public AtomicRefCounted
<TypeContext
> {
1102 FeatureArgs features_
;
1103 // The pending recursion group that is currently being constructed
1104 MutableRecGroup pendingRecGroup_
;
1105 // An in-order list of all the recursion groups defined in this module
1106 SharedRecGroupVector recGroups_
;
1107 // An in-order list of the type definitions in the module. Each type is
1108 // stored in a recursion group.
1109 TypeDefPtrVector types_
;
1110 // A map from type definition to the original module index.
1111 TypeDefPtrToIndexMap moduleIndices_
;
1113 static SharedRecGroup
canonicalizeGroup(SharedRecGroup recGroup
);
1116 TypeContext() = default;
1117 explicit TypeContext(const FeatureArgs
& features
) : features_(features
) {}
1120 size_t sizeOfExcludingThis(MallocSizeOf mallocSizeOf
) const {
1121 return types_
.sizeOfExcludingThis(mallocSizeOf
) +
1122 moduleIndices_
.shallowSizeOfExcludingThis(mallocSizeOf
);
1125 // Disallow copy, allow move initialization
1126 TypeContext(const TypeContext
&) = delete;
1127 TypeContext
& operator=(const TypeContext
&) = delete;
1128 TypeContext(TypeContext
&&) = delete;
1129 TypeContext
& operator=(TypeContext
&&) = delete;
1131 // Begin creating a recursion group with the specified number of types.
1132 // Returns a recursion group to be filled in with type definitions. This must
1133 // be paired with `endGroup`.
1134 [[nodiscard
]] MutableRecGroup
startRecGroup(uint32_t numTypes
) {
1135 // We must not have a pending group
1136 MOZ_ASSERT(!pendingRecGroup_
);
1138 // Create the group and add it to the list of groups
1139 MutableRecGroup recGroup
= RecGroup::allocate(numTypes
);
1140 if (!recGroup
|| !addRecGroup(recGroup
)) {
1144 // Store this group for later use in endRecGroup
1145 pendingRecGroup_
= recGroup
;
1149 // Finish creation of a recursion group after type definitions have been
1150 // initialized. This must be paired with `startGroup`.
1151 [[nodiscard
]] bool endRecGroup() {
1152 // We must have started a recursion group
1153 MOZ_ASSERT(pendingRecGroup_
);
1154 MutableRecGroup recGroup
= pendingRecGroup_
;
1155 pendingRecGroup_
= nullptr;
1157 // Finalize the type definitions in the recursion group
1158 if (!recGroup
->finalizeDefinitions()) {
1162 // Canonicalize the recursion group
1163 SharedRecGroup canonicalRecGroup
= canonicalizeGroup(recGroup
);
1164 if (!canonicalRecGroup
) {
1168 // Nothing left to do if this group became the canonical group
1169 if (canonicalRecGroup
== recGroup
) {
1173 // Store the canonical group into the list
1174 recGroups_
.back() = canonicalRecGroup
;
1176 // Overwrite all the entries we stored into the index space maps when we
1177 // started this group.
1178 MOZ_ASSERT(recGroup
->numTypes() == canonicalRecGroup
->numTypes());
1179 for (uint32_t groupTypeIndex
= 0; groupTypeIndex
< recGroup
->numTypes();
1181 uint32_t typeIndex
= length() - recGroup
->numTypes() + groupTypeIndex
;
1182 const TypeDef
* oldTypeDef
= types_
[typeIndex
];
1183 const TypeDef
* canonTypeDef
= &canonicalRecGroup
->type(groupTypeIndex
);
1185 types_
[typeIndex
] = canonTypeDef
;
1186 moduleIndices_
.remove(oldTypeDef
);
1188 // Ensure there is an module index entry pointing to the canonical type
1189 // definition. Don't overwrite it if it already exists, serialization
1190 // relies on the module index map pointing to the first occurrence of a
1191 // type definition to avoid creating forward references that didn't exist
1192 // in the original module.
1193 auto canonTypeIndexEntry
= moduleIndices_
.lookupForAdd(canonTypeDef
);
1194 if (!canonTypeIndexEntry
&&
1195 !moduleIndices_
.add(canonTypeIndexEntry
, canonTypeDef
, typeIndex
)) {
1203 // Finish creation of a recursion group after type definitions have been
1204 // initialized. This must be paired with `startGroup`.
1205 [[nodiscard
]] bool addRecGroup(SharedRecGroup recGroup
) {
1206 // We must not have a pending group
1207 MOZ_ASSERT(!pendingRecGroup_
);
1209 // Add it to the list of groups
1210 if (!recGroups_
.append(recGroup
)) {
1214 // Store the types of the group into our index space maps. These may get
1215 // overwritten if this group is being added by `startRecGroup` and we
1216 // overwrite it with a canonical group in `endRecGroup`. We need to do
1217 // this before finishing though, because these entries will be used by
1218 // decoding and error printing.
1219 for (uint32_t groupTypeIndex
= 0; groupTypeIndex
< recGroup
->numTypes();
1221 const TypeDef
* typeDef
= &recGroup
->type(groupTypeIndex
);
1222 uint32_t typeIndex
= types_
.length();
1223 if (!types_
.append(typeDef
) || !moduleIndices_
.put(typeDef
, typeIndex
)) {
1230 template <typename T
>
1231 [[nodiscard
]] bool addType(T
&& type
) {
1232 MutableRecGroup recGroup
= startRecGroup(1);
1236 recGroup
->type(0) = std::move(type
);
1237 return endRecGroup();
1240 const TypeDef
& type(uint32_t index
) const { return *types_
[index
]; }
1241 const TypeDef
& operator[](uint32_t index
) const { return *types_
[index
]; }
1243 bool empty() const { return types_
.empty(); }
1244 uint32_t length() const { return types_
.length(); }
1246 const SharedRecGroupVector
& groups() const { return recGroups_
; }
1248 // Map from type definition to index
1250 uint32_t indexOf(const TypeDef
& typeDef
) const {
1251 auto moduleIndex
= moduleIndices_
.readonlyThreadsafeLookup(&typeDef
);
1252 MOZ_RELEASE_ASSERT(moduleIndex
.found());
1253 return moduleIndex
->value();
1257 using SharedTypeContext
= RefPtr
<const TypeContext
>;
1258 using MutableTypeContext
= RefPtr
<TypeContext
>;
1260 //=========================================================================
1263 // An unambiguous strong reference to a type definition in a specific type
1267 SharedTypeContext context_
;
1271 TypeHandle(SharedTypeContext context
, uint32_t index
)
1272 : context_(context
), index_(index
) {
1273 MOZ_ASSERT(index_
< context_
->length());
1275 TypeHandle(SharedTypeContext context
, const TypeDef
& def
)
1276 : context_(context
), index_(context
->indexOf(def
)) {}
1278 TypeHandle(const TypeHandle
&) = default;
1279 TypeHandle
& operator=(const TypeHandle
&) = default;
1281 const SharedTypeContext
& context() const { return context_
; }
1282 uint32_t index() const { return index_
; }
1283 const TypeDef
& def() const { return context_
->type(index_
); }
1286 //=========================================================================
1290 inline uintptr_t TypeDef::forMatch(const TypeDef
* typeDef
,
1291 const RecGroup
* recGroup
) {
1292 // TypeDef is aligned sufficiently to allow a tag to distinguish a local type
1293 // reference (index) from a non-local type reference (pointer).
1294 static_assert(alignof(TypeDef
) > 1);
1295 MOZ_ASSERT((uintptr_t(typeDef
) & 0x1) == 0);
1297 // Return a tagged index for local type references
1298 if (typeDef
&& &typeDef
->recGroup() == recGroup
) {
1299 return uintptr_t(recGroup
->indexOf(typeDef
)) | 0x1;
1302 // Return an untagged pointer for non-local type references
1303 return uintptr_t(typeDef
);
1307 inline MatchTypeCode
MatchTypeCode::forMatch(PackedTypeCode ptc
,
1308 const RecGroup
* recGroup
) {
1309 MatchTypeCode mtc
= {};
1310 mtc
.typeCode
= PackedRepr(ptc
.typeCode());
1311 mtc
.typeRef
= TypeDef::forMatch(ptc
.typeDef(), recGroup
);
1312 mtc
.nullable
= ptc
.isNullable();
1317 void PackedType
<T
>::AddRef() const {
1324 void PackedType
<T
>::Release() const {
1328 refType().Release();
1331 void RefType::AddRef() const {
1335 typeDef()->recGroup().AddRef();
1337 void RefType::Release() const {
1341 typeDef()->recGroup().Release();
1344 inline RefTypeHierarchy
RefType::hierarchy() const {
1347 case RefType::NoFunc
:
1348 return RefTypeHierarchy::Func
;
1349 case RefType::Extern
:
1350 case RefType::NoExtern
:
1351 return RefTypeHierarchy::Extern
;
1353 return RefTypeHierarchy::Exn
;
1358 case RefType::Struct
:
1359 case RefType::Array
:
1360 return RefTypeHierarchy::Any
;
1361 case RefType::TypeRef
:
1362 switch (typeDef()->kind()) {
1363 case TypeDefKind::Struct
:
1364 case TypeDefKind::Array
:
1365 return RefTypeHierarchy::Any
;
1366 case TypeDefKind::Func
:
1367 return RefTypeHierarchy::Func
;
1368 case TypeDefKind::None
:
1372 MOZ_CRASH("switch is exhaustive");
1375 inline TableRepr
RefType::tableRepr() const {
1376 switch (hierarchy()) {
1377 case RefTypeHierarchy::Any
:
1378 case RefTypeHierarchy::Extern
:
1379 case RefTypeHierarchy::Exn
:
1380 return TableRepr::Ref
;
1381 case RefTypeHierarchy::Func
:
1382 return TableRepr::Func
;
1384 MOZ_CRASH("switch is exhaustive");
1387 inline bool RefType::isFuncHierarchy() const {
1388 return hierarchy() == RefTypeHierarchy::Func
;
1390 inline bool RefType::isExternHierarchy() const {
1391 return hierarchy() == RefTypeHierarchy::Extern
;
1393 inline bool RefType::isAnyHierarchy() const {
1394 return hierarchy() == RefTypeHierarchy::Any
;
1396 inline bool RefType::isExnHierarchy() const {
1397 return hierarchy() == RefTypeHierarchy::Exn
;
1401 inline bool RefType::isSubTypeOf(RefType subType
, RefType superType
) {
1402 // Anything is a subtype of itself.
1403 if (subType
== superType
) {
1407 // A subtype must have the same nullability as the supertype or the
1408 // supertype must be nullable.
1409 if (subType
.isNullable() && !superType
.isNullable()) {
1413 // Non type-index references are subtypes if they have the same kind
1414 if (!subType
.isTypeRef() && !superType
.isTypeRef() &&
1415 subType
.kind() == superType
.kind()) {
1419 // eqref is a subtype of anyref
1420 if (subType
.isEq() && superType
.isAny()) {
1424 // i31ref is a subtype of eqref
1425 if (subType
.isI31() && (superType
.isAny() || superType
.isEq())) {
1429 // structref/arrayref are subtypes of eqref and anyref
1430 if ((subType
.isStruct() || subType
.isArray()) &&
1431 (superType
.isAny() || superType
.isEq())) {
1435 // Structs are subtypes of structref, eqref and anyref
1436 if (subType
.isTypeRef() && subType
.typeDef()->isStructType() &&
1437 (superType
.isAny() || superType
.isEq() || superType
.isStruct())) {
1441 // Arrays are subtypes of arrayref, eqref and anyref
1442 if (subType
.isTypeRef() && subType
.typeDef()->isArrayType() &&
1443 (superType
.isAny() || superType
.isEq() || superType
.isArray())) {
1447 // Funcs are subtypes of funcref
1448 if (subType
.isTypeRef() && subType
.typeDef()->isFuncType() &&
1449 superType
.isFunc()) {
1453 // Type references can be subtypes
1454 if (subType
.isTypeRef() && superType
.isTypeRef()) {
1455 return TypeDef::isSubTypeOf(subType
.typeDef(), superType
.typeDef());
1458 // No func is the bottom type of the func hierarchy
1459 if (subType
.isNoFunc() && superType
.hierarchy() == RefTypeHierarchy::Func
) {
1463 // No extern is the bottom type of the extern hierarchy
1464 if (subType
.isNoExtern() &&
1465 superType
.hierarchy() == RefTypeHierarchy::Extern
) {
1469 // None is the bottom type of the any hierarchy
1470 if (subType
.isNone() && superType
.hierarchy() == RefTypeHierarchy::Any
) {
1478 inline bool RefType::castPossible(RefType sourceType
, RefType destType
) {
1479 // Nullable types always have null in common.
1480 if (sourceType
.isNullable() && destType
.isNullable()) {
1484 // At least one of the types is non-nullable, so the only common values can be
1485 // non-null. Therefore, if either type is a bottom type, common values are
1487 if (sourceType
.isRefBottom() || destType
.isRefBottom()) {
1491 // After excluding bottom types, our type hierarchy is a tree, and after
1492 // excluding nulls, subtype relationships are sufficient to tell if the types
1493 // share any values. If neither type is a subtype of the other, then they are
1494 // on different branches of the tree and completely disjoint.
1495 RefType sourceNonNull
= sourceType
.withIsNullable(false);
1496 RefType destNonNull
= destType
.withIsNullable(false);
1497 return RefType::isSubTypeOf(sourceNonNull
, destNonNull
) ||
1498 RefType::isSubTypeOf(destNonNull
, sourceNonNull
);
1501 //=========================================================================
1502 // [SMDOC] Signatures and runtime types
1504 // TypeIdDesc describes the runtime representation of a TypeDef suitable for
1505 // type equality checks. The kind of representation depends on whether the type
1506 // is a function or a GC type. This design is in flux and will evolve.
1510 // For functions in the general case, a FuncType is allocated and stored in a
1511 // process-wide hash table, so that pointer equality implies structural
1512 // equality. This process does not correctly handle type references (which would
1513 // require hash-consing of infinite-trees), but that's okay while
1514 // function-references and gc-types are experimental.
1516 // A pointer to the hash table entry is stored in the global data
1517 // area for each instance, and TypeIdDesc gives the offset to this entry.
1519 // ## Immediate function types
1521 // As an optimization for the 99% case where the FuncType has a small number of
1522 // parameters, the FuncType is bit-packed into a uint32 immediate value so that
1523 // integer equality implies structural equality. Both cases can be handled with
1524 // a single comparison by always setting the LSB for the immediates
1525 // (the LSB is necessarily 0 for allocated FuncType pointers due to alignment).
1529 // For GC types, an entry is always created in the global data area and a
1530 // unique RttValue (see wasm/WasmGcObject.h) is stored there. This RttValue
1531 // is the value given by 'rtt.canon $t' for each type definition. As each entry
1532 // is given a unique value and no canonicalization is done (which would require
1533 // hash-consing of infinite-trees), this is not yet spec compliant.
1535 // # wasm::Instance and the global type context
1537 // As GC objects may outlive the module they are created in, types are
1538 // additionally transferred to a wasm::Context (which is part of JSContext) upon
1539 // instantiation. This wasm::Context contains the 'global type context' that
1540 // RTTValues refer to by type index. Types are never freed from the global type
1541 // context as that would shift the index space. In the future, this will be
1547 #endif // wasm_type_def_h