summaryrefslogtreecommitdiffstats
path: root/js/src/wasm/WasmValType.h
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /js/src/wasm/WasmValType.h
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/wasm/WasmValType.h')
-rw-r--r--js/src/wasm/WasmValType.h890
1 files changed, 890 insertions, 0 deletions
diff --git a/js/src/wasm/WasmValType.h b/js/src/wasm/WasmValType.h
new file mode 100644
index 0000000000..e0fdd43249
--- /dev/null
+++ b/js/src/wasm/WasmValType.h
@@ -0,0 +1,890 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ *
+ * Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef wasm_valtype_h
+#define wasm_valtype_h
+
+#include "mozilla/HashTable.h"
+#include "mozilla/Maybe.h"
+
+#include <type_traits>
+
+#include "jit/IonTypes.h"
+#include "wasm/WasmConstants.h"
+#include "wasm/WasmSerialize.h"
+#include "wasm/WasmTypeDecls.h"
+
+namespace js {
+namespace wasm {
+
+using mozilla::Maybe;
+
+class RecGroup;
+class TypeDef;
+class TypeContext;
+enum class TypeDefKind : uint8_t;
+
+// A PackedTypeCode represents any value type.
+union PackedTypeCode {
+ public:
+ using PackedRepr = uint64_t;
+
+ private:
+ static constexpr size_t NullableBits = 1;
+ static constexpr size_t TypeCodeBits = 8;
+ static constexpr size_t TypeDefBits = 48;
+ static constexpr size_t PointerTagBits = 2;
+
+ static_assert(NullableBits + TypeCodeBits + TypeDefBits + PointerTagBits <=
+ (sizeof(PackedRepr) * 8),
+ "enough bits");
+
+ PackedRepr bits_;
+ struct {
+ PackedRepr nullable_ : NullableBits;
+ PackedRepr typeCode_ : TypeCodeBits;
+ // A pointer to the TypeDef this type references. We use 48-bits for this,
+ // and rely on system memory allocators not allocating outside of this
+ // range. This is also assumed by JS::Value, and so should be safe here.
+ PackedRepr typeDef_ : TypeDefBits;
+ // Reserve the bottom two bits for use as a tagging scheme for BlockType
+ // and ResultType, which can encode a ValType inside themselves in special
+ // cases.
+ PackedRepr pointerTag_ : PointerTagBits;
+ };
+
+ public:
+ static constexpr PackedRepr NoTypeCode = ((uint64_t)1 << TypeCodeBits) - 1;
+
+ static PackedTypeCode invalid() {
+ PackedTypeCode ptc = {};
+ ptc.typeCode_ = NoTypeCode;
+ return ptc;
+ }
+
+ static constexpr PackedTypeCode fromBits(PackedRepr bits) {
+ PackedTypeCode ptc = {};
+ ptc.bits_ = bits;
+ return ptc;
+ }
+
+ static PackedTypeCode pack(TypeCode tc, const TypeDef* typeDef,
+ bool isNullable) {
+ MOZ_ASSERT(uint32_t(tc) <= ((1 << TypeCodeBits) - 1));
+ MOZ_ASSERT_IF(tc != AbstractTypeRefCode, typeDef == nullptr);
+ MOZ_ASSERT_IF(tc == AbstractTypeRefCode, typeDef != nullptr);
+ // Double check that the type definition was allocated within 48-bits, as
+ // noted above.
+ MOZ_ASSERT((uint64_t)typeDef <= ((uint64_t)1 << TypeDefBits) - 1);
+ PackedTypeCode ptc = {};
+ ptc.typeCode_ = PackedRepr(tc);
+ ptc.typeDef_ = (uintptr_t)typeDef;
+ ptc.nullable_ = isNullable;
+ return ptc;
+ }
+
+ static PackedTypeCode pack(TypeCode tc, bool nullable) {
+ return pack(tc, nullptr, nullable);
+ }
+
+ static PackedTypeCode pack(TypeCode tc) { return pack(tc, nullptr, false); }
+
+ bool isValid() const { return typeCode_ != NoTypeCode; }
+
+ PackedRepr bits() const { return bits_; }
+
+ TypeCode typeCode() const {
+ MOZ_ASSERT(isValid());
+ return TypeCode(typeCode_);
+ }
+
+ // Return the TypeCode, but return AbstractReferenceTypeCode for any reference
+ // type.
+ //
+ // This function is very, very hot, hence what would normally be a switch on
+ // the value `c` to map the reference types to AbstractReferenceTypeCode has
+ // been distilled into a simple comparison; this is fastest. Should type
+ // codes become too complicated for this to work then a lookup table also has
+ // better performance than a switch.
+ //
+ // An alternative is for the PackedTypeCode to represent something closer to
+ // what ValType needs, so that this decoding step is not necessary, but that
+ // moves complexity elsewhere, and the perf gain here would be only about 1%
+ // for baseline compilation throughput.
+ TypeCode typeCodeAbstracted() const {
+ TypeCode tc = typeCode();
+ return tc < LowestPrimitiveTypeCode ? AbstractReferenceTypeCode : tc;
+ }
+
+ // Return whether this type is a reference type.
+ bool isRefType() const {
+ return typeCodeAbstracted() == AbstractReferenceTypeCode;
+ }
+
+ // Return whether this type is represented by a reference at runtime.
+ bool isRefRepr() const { return typeCode() < LowestPrimitiveTypeCode; }
+
+ const TypeDef* typeDef() const {
+ MOZ_ASSERT(isValid());
+ return (const TypeDef*)(uintptr_t)typeDef_;
+ }
+
+ bool isNullable() const {
+ MOZ_ASSERT(isValid());
+ return bool(nullable_);
+ }
+
+ PackedTypeCode withIsNullable(bool nullable) const {
+ MOZ_ASSERT(isRefType());
+ PackedTypeCode mutated = *this;
+ mutated.nullable_ = (PackedRepr)nullable;
+ return mutated;
+ }
+
+ bool operator==(const PackedTypeCode& rhs) const {
+ return bits_ == rhs.bits_;
+ }
+ bool operator!=(const PackedTypeCode& rhs) const {
+ return bits_ != rhs.bits_;
+ }
+};
+
+static_assert(sizeof(PackedTypeCode) == sizeof(uint64_t), "packed");
+
+// A SerializableTypeCode represents any value type in a form that can be
+// serialized and deserialized.
+union SerializableTypeCode {
+ using PackedRepr = uintptr_t;
+
+ static constexpr size_t NullableBits = 1;
+ static constexpr size_t TypeCodeBits = 8;
+ static constexpr size_t TypeIndexBits = 20;
+
+ PackedRepr bits;
+ struct {
+ PackedRepr nullable : NullableBits;
+ PackedRepr typeCode : TypeCodeBits;
+ PackedRepr typeIndex : TypeIndexBits;
+ };
+
+ WASM_CHECK_CACHEABLE_POD(bits);
+
+ static constexpr PackedRepr NoTypeIndex = (1 << TypeIndexBits) - 1;
+
+ static_assert(NullableBits + TypeCodeBits + TypeIndexBits <=
+ (sizeof(PackedRepr) * 8),
+ "enough bits");
+ static_assert(NoTypeIndex < (1 << TypeIndexBits), "enough bits");
+ static_assert(MaxTypes < NoTypeIndex, "enough bits");
+
+ // Defined in WasmSerialize.cpp
+ static inline SerializableTypeCode serialize(PackedTypeCode ptc,
+ const TypeContext& types);
+ inline PackedTypeCode deserialize(const TypeContext& types);
+};
+
+WASM_DECLARE_CACHEABLE_POD(SerializableTypeCode);
+static_assert(sizeof(SerializableTypeCode) == sizeof(uintptr_t), "packed");
+
+// [SMDOC] Matching type definitions
+//
+// WebAssembly type equality is structural, and we implement canonicalization
+// such that equality of pointers to type definitions means that the type
+// definitions are structurally equal.
+//
+// 'Matching' is the algorithm used to determine if two types are equal while
+// canonicalizing types.
+//
+// A match type code encodes a type code for use in equality and hashing
+// matching. It normalizes type references that are local to a recursion group
+// so that they can be bitwise compared to type references from other recursion
+// groups.
+//
+// This is useful for the following example:
+// (rec (func $a))
+// (rec
+// (func $b)
+// (struct
+// (field (ref $a)))
+// (field (ref $b)))
+// )
+// (rec
+// (func $c)
+// (struct
+// (field (ref $a)))
+// (field (ref $c)))
+// )
+//
+// The last two recursion groups are identical and should canonicalize to the
+// same instance. However, they will be initially represented as two separate
+// recursion group instances each with an array type instance with element
+// types that point to the function type instance before them. A bitwise
+// comparison of the element type pointers would fail.
+//
+// To solve this, we use `MatchTypeCode` to convert the example to:
+// (rec (func $a))
+// (rec
+// (func $b)
+// (struct
+// (field (ref nonlocal $a)))
+// (field (ref local 0)))
+// )
+// (rec
+// (func $c)
+// (struct
+// (field (ref nonlocal $a)))
+// (field (ref local 0)))
+// )
+//
+// Now, comparing the element types will see that these are local type
+// references of the same kinds. `MatchTypeCode` performs the same mechanism
+// as `tie` in the MVP presentation of type equality [1].
+//
+// [1]
+// https://github.com/WebAssembly/gc/blob/main/proposals/gc/MVP.md#equivalence
+union MatchTypeCode {
+ using PackedRepr = uint64_t;
+
+ static constexpr size_t NullableBits = 1;
+ static constexpr size_t TypeCodeBits = 8;
+ static constexpr size_t TypeRefBits = 48;
+
+ PackedRepr bits;
+ struct {
+ PackedRepr nullable : NullableBits;
+ PackedRepr typeCode : TypeCodeBits;
+ PackedRepr typeRef : TypeRefBits;
+ };
+
+ WASM_CHECK_CACHEABLE_POD(bits);
+
+ static_assert(NullableBits + TypeCodeBits + TypeRefBits <=
+ (sizeof(PackedRepr) * 8),
+ "enough bits");
+
+ // Defined in WasmTypeDef.h to avoid a cycle while allowing inlining
+ static inline MatchTypeCode forMatch(PackedTypeCode ptc,
+ const RecGroup* recGroup);
+
+ bool operator==(MatchTypeCode other) const { return bits == other.bits; }
+ bool operator!=(MatchTypeCode other) const { return bits != other.bits; }
+ HashNumber hash() const { return HashNumber(bits); }
+};
+
+// An enum that describes the representation classes for tables; The table
+// element type is mapped into this by Table::repr().
+
+enum class TableRepr { Ref, Func };
+
+// An enum that describes the different type hierarchies.
+
+enum class RefTypeHierarchy { Func, Extern, Any };
+
+// The RefType carries more information about types t for which t.isRefType()
+// is true.
+
+class RefType {
+ public:
+ enum Kind {
+ Func = uint8_t(TypeCode::FuncRef),
+ Extern = uint8_t(TypeCode::ExternRef),
+ Any = uint8_t(TypeCode::AnyRef),
+ NoFunc = uint8_t(TypeCode::NullFuncRef),
+ NoExtern = uint8_t(TypeCode::NullExternRef),
+ None = uint8_t(TypeCode::NullAnyRef),
+ Eq = uint8_t(TypeCode::EqRef),
+ Struct = uint8_t(TypeCode::StructRef),
+ Array = uint8_t(TypeCode::ArrayRef),
+ TypeRef = uint8_t(AbstractTypeRefCode)
+ };
+
+ private:
+ PackedTypeCode ptc_;
+
+ RefType(Kind kind, bool nullable)
+ : ptc_(PackedTypeCode::pack(TypeCode(kind), nullable)) {
+ MOZ_ASSERT(isValid());
+ }
+
+ RefType(const TypeDef* typeDef, bool nullable)
+ : ptc_(PackedTypeCode::pack(AbstractTypeRefCode, typeDef, nullable)) {
+ MOZ_ASSERT(isValid());
+ }
+
+ public:
+ RefType() : ptc_(PackedTypeCode::invalid()) {}
+ explicit RefType(PackedTypeCode ptc) : ptc_(ptc) { MOZ_ASSERT(isValid()); }
+
+ static RefType fromTypeCode(TypeCode tc, bool nullable) {
+ MOZ_ASSERT(tc != AbstractTypeRefCode);
+ return RefType(Kind(tc), nullable);
+ }
+
+ static RefType fromTypeDef(const TypeDef* typeDef, bool nullable) {
+ return RefType(typeDef, nullable);
+ }
+
+ Kind kind() const { return Kind(ptc_.typeCode()); }
+
+ const TypeDef* typeDef() const { return ptc_.typeDef(); }
+
+ PackedTypeCode packed() const { return ptc_; }
+ PackedTypeCode* addressOfPacked() { return &ptc_; }
+ const PackedTypeCode* addressOfPacked() const { return &ptc_; }
+
+#ifdef DEBUG
+ bool isValid() const {
+ MOZ_ASSERT((ptc_.typeCode() == AbstractTypeRefCode) ==
+ (ptc_.typeDef() != nullptr));
+ switch (ptc_.typeCode()) {
+ case TypeCode::FuncRef:
+ case TypeCode::ExternRef:
+ case TypeCode::AnyRef:
+ case TypeCode::EqRef:
+ case TypeCode::StructRef:
+ case TypeCode::ArrayRef:
+ case TypeCode::NullFuncRef:
+ case TypeCode::NullExternRef:
+ case TypeCode::NullAnyRef:
+ case AbstractTypeRefCode:
+ return true;
+ default:
+ return false;
+ }
+ }
+#endif
+
+ static RefType func() { return RefType(Func, true); }
+ static RefType extern_() { return RefType(Extern, true); }
+ static RefType any() { return RefType(Any, true); }
+ static RefType nofunc() { return RefType(NoFunc, true); }
+ static RefType noextern() { return RefType(NoExtern, true); }
+ static RefType none() { return RefType(None, true); }
+ static RefType eq() { return RefType(Eq, true); }
+ static RefType struct_() { return RefType(Struct, true); }
+ static RefType array() { return RefType(Array, true); }
+
+ bool isFunc() const { return kind() == RefType::Func; }
+ bool isExtern() const { return kind() == RefType::Extern; }
+ bool isAny() const { return kind() == RefType::Any; }
+ bool isNoFunc() const { return kind() == RefType::NoFunc; }
+ bool isNoExtern() const { return kind() == RefType::NoExtern; }
+ bool isNone() const { return kind() == RefType::None; }
+ bool isEq() const { return kind() == RefType::Eq; }
+ bool isStruct() const { return kind() == RefType::Struct; }
+ bool isArray() const { return kind() == RefType::Array; }
+ bool isTypeRef() const { return kind() == RefType::TypeRef; }
+
+ bool isNullable() const { return bool(ptc_.isNullable()); }
+ RefType asNonNullable() const { return withIsNullable(false); }
+ RefType withIsNullable(bool nullable) const {
+ return RefType(ptc_.withIsNullable(nullable));
+ }
+
+ bool isRefBottom() const { return isNone() || isNoFunc() || isNoExtern(); }
+
+ // These methods are defined in WasmTypeDef.h to avoid a cycle while allowing
+ // inlining.
+ inline RefTypeHierarchy hierarchy() const;
+ inline TableRepr tableRepr() const;
+ inline bool isFuncHierarchy() const;
+ inline bool isExternHierarchy() const;
+ inline bool isAnyHierarchy() const;
+ static bool isSubTypeOf(RefType subType, RefType superType);
+ static bool castPossible(RefType sourceType, RefType destType);
+
+ // Gets the top of the given type's hierarchy, e.g. Any for structs and
+ // arrays, and Func for funcs
+ RefType topType() const;
+
+ // Gets the TypeDefKind associated with this RefType, e.g. TypeDefKind::Struct
+ // for RefType::Struct.
+ TypeDefKind typeDefKind() const;
+
+ bool operator==(const RefType& that) const { return ptc_ == that.ptc_; }
+ bool operator!=(const RefType& that) const { return ptc_ != that.ptc_; }
+};
+
+class FieldTypeTraits {
+ public:
+ enum Kind {
+ I8 = uint8_t(TypeCode::I8),
+ I16 = uint8_t(TypeCode::I16),
+ I32 = uint8_t(TypeCode::I32),
+ I64 = uint8_t(TypeCode::I64),
+ F32 = uint8_t(TypeCode::F32),
+ F64 = uint8_t(TypeCode::F64),
+ V128 = uint8_t(TypeCode::V128),
+ Ref = uint8_t(AbstractReferenceTypeCode),
+ };
+
+ static bool isValidTypeCode(TypeCode tc) {
+ switch (tc) {
+#ifdef ENABLE_WASM_GC
+ case TypeCode::I8:
+ case TypeCode::I16:
+#endif
+ case TypeCode::I32:
+ case TypeCode::I64:
+ case TypeCode::F32:
+ case TypeCode::F64:
+#ifdef ENABLE_WASM_SIMD
+ case TypeCode::V128:
+#endif
+ case TypeCode::FuncRef:
+ case TypeCode::ExternRef:
+#ifdef ENABLE_WASM_GC
+ case TypeCode::AnyRef:
+ case TypeCode::EqRef:
+ case TypeCode::StructRef:
+ case TypeCode::ArrayRef:
+ case TypeCode::NullFuncRef:
+ case TypeCode::NullExternRef:
+ case TypeCode::NullAnyRef:
+#endif
+#ifdef ENABLE_WASM_FUNCTION_REFERENCES
+ case AbstractTypeRefCode:
+#endif
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ static bool isNumberTypeCode(TypeCode tc) {
+ switch (tc) {
+ case TypeCode::I32:
+ case TypeCode::I64:
+ case TypeCode::F32:
+ case TypeCode::F64:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ static bool isPackedTypeCode(TypeCode tc) {
+ switch (tc) {
+#ifdef ENABLE_WASM_GC
+ case TypeCode::I8:
+ case TypeCode::I16:
+ return true;
+#endif
+ default:
+ return false;
+ }
+ }
+
+ static bool isVectorTypeCode(TypeCode tc) {
+ switch (tc) {
+#ifdef ENABLE_WASM_SIMD
+ case TypeCode::V128:
+ return true;
+#endif
+ default:
+ return false;
+ }
+ }
+};
+
+class ValTypeTraits {
+ public:
+ enum Kind {
+ I32 = uint8_t(TypeCode::I32),
+ I64 = uint8_t(TypeCode::I64),
+ F32 = uint8_t(TypeCode::F32),
+ F64 = uint8_t(TypeCode::F64),
+ V128 = uint8_t(TypeCode::V128),
+ Ref = uint8_t(AbstractReferenceTypeCode),
+ };
+
+ static bool isValidTypeCode(TypeCode tc) {
+ switch (tc) {
+ case TypeCode::I32:
+ case TypeCode::I64:
+ case TypeCode::F32:
+ case TypeCode::F64:
+#ifdef ENABLE_WASM_SIMD
+ case TypeCode::V128:
+#endif
+ case TypeCode::FuncRef:
+ case TypeCode::ExternRef:
+#ifdef ENABLE_WASM_GC
+ case TypeCode::AnyRef:
+ case TypeCode::EqRef:
+ case TypeCode::StructRef:
+ case TypeCode::ArrayRef:
+ case TypeCode::NullFuncRef:
+ case TypeCode::NullExternRef:
+ case TypeCode::NullAnyRef:
+#endif
+#ifdef ENABLE_WASM_FUNCTION_REFERENCES
+ case AbstractTypeRefCode:
+#endif
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ static bool isNumberTypeCode(TypeCode tc) {
+ switch (tc) {
+ case TypeCode::I32:
+ case TypeCode::I64:
+ case TypeCode::F32:
+ case TypeCode::F64:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ static bool isPackedTypeCode(TypeCode tc) { return false; }
+
+ static bool isVectorTypeCode(TypeCode tc) {
+ switch (tc) {
+#ifdef ENABLE_WASM_SIMD
+ case TypeCode::V128:
+ return true;
+#endif
+ default:
+ return false;
+ }
+ }
+};
+
+// The PackedType represents the storage type of a WebAssembly location, whether
+// parameter, local, field, or global. See specializations below for ValType and
+// FieldType.
+
+template <class T>
+class PackedType : public T {
+ public:
+ using Kind = typename T::Kind;
+
+ protected:
+ PackedTypeCode tc_;
+
+ explicit PackedType(TypeCode c) : tc_(PackedTypeCode::pack(c)) {
+ MOZ_ASSERT(c != AbstractTypeRefCode);
+ MOZ_ASSERT(isValid());
+ }
+
+ TypeCode typeCode() const {
+ MOZ_ASSERT(isValid());
+ return tc_.typeCode();
+ }
+
+ public:
+ PackedType() : tc_(PackedTypeCode::invalid()) {}
+
+ MOZ_IMPLICIT PackedType(Kind c) : tc_(PackedTypeCode::pack(TypeCode(c))) {
+ MOZ_ASSERT(c != Kind::Ref);
+ MOZ_ASSERT(isValid());
+ }
+
+ MOZ_IMPLICIT PackedType(RefType rt) : tc_(rt.packed()) {
+ MOZ_ASSERT(isValid());
+ }
+
+ explicit PackedType(PackedTypeCode ptc) : tc_(ptc) { MOZ_ASSERT(isValid()); }
+
+ static PackedType fromMIRType(jit::MIRType mty) {
+ switch (mty) {
+ case jit::MIRType::Int32:
+ return PackedType::I32;
+ break;
+ case jit::MIRType::Int64:
+ return PackedType::I64;
+ break;
+ case jit::MIRType::Float32:
+ return PackedType::F32;
+ break;
+ case jit::MIRType::Double:
+ return PackedType::F64;
+ break;
+ case jit::MIRType::Simd128:
+ return PackedType::V128;
+ break;
+ case jit::MIRType::RefOrNull:
+ return PackedType::Ref;
+ default:
+ MOZ_CRASH("fromMIRType: unexpected type");
+ }
+ }
+
+ static PackedType fromNonRefTypeCode(TypeCode tc) {
+#ifdef DEBUG
+ switch (tc) {
+ case TypeCode::I8:
+ case TypeCode::I16:
+ case TypeCode::I32:
+ case TypeCode::I64:
+ case TypeCode::F32:
+ case TypeCode::F64:
+ case TypeCode::V128:
+ break;
+ default:
+ MOZ_CRASH("Bad type code");
+ }
+#endif
+ return PackedType(tc);
+ }
+
+ static PackedType fromBitsUnsafe(PackedTypeCode::PackedRepr bits) {
+ return PackedType(PackedTypeCode::fromBits(bits));
+ }
+
+ bool isValid() const {
+ if (!tc_.isValid()) {
+ return false;
+ }
+ return T::isValidTypeCode(tc_.typeCode());
+ }
+
+ MatchTypeCode forMatch(const RecGroup* recGroup) const {
+ return MatchTypeCode::forMatch(tc_, recGroup);
+ }
+
+ PackedTypeCode packed() const {
+ MOZ_ASSERT(isValid());
+ return tc_;
+ }
+ PackedTypeCode* addressOfPacked() { return &tc_; }
+ const PackedTypeCode* addressOfPacked() const { return &tc_; }
+
+ PackedTypeCode::PackedRepr bitsUnsafe() const {
+ MOZ_ASSERT(isValid());
+ return tc_.bits();
+ }
+
+ bool isNumber() const { return T::isNumberTypeCode(tc_.typeCode()); }
+
+ bool isPacked() const { return T::isPackedTypeCode(tc_.typeCode()); }
+
+ bool isVector() const { return T::isVectorTypeCode(tc_.typeCode()); }
+
+ bool isRefType() const { return tc_.isRefType(); }
+
+ bool isFuncRef() const { return tc_.typeCode() == TypeCode::FuncRef; }
+
+ bool isExternRef() const { return tc_.typeCode() == TypeCode::ExternRef; }
+
+ bool isAnyRef() const { return tc_.typeCode() == TypeCode::AnyRef; }
+
+ bool isNoFunc() const { return tc_.typeCode() == TypeCode::NullFuncRef; }
+
+ bool isNoExtern() const { return tc_.typeCode() == TypeCode::NullExternRef; }
+
+ bool isNone() const { return tc_.typeCode() == TypeCode::NullAnyRef; }
+
+ bool isEqRef() const { return tc_.typeCode() == TypeCode::EqRef; }
+
+ bool isStructRef() const { return tc_.typeCode() == TypeCode::StructRef; }
+
+ bool isArrayRef() const { return tc_.typeCode() == TypeCode::ArrayRef; }
+
+ bool isTypeRef() const { return tc_.typeCode() == AbstractTypeRefCode; }
+
+ bool isRefRepr() const { return tc_.isRefRepr(); }
+
+ // Returns whether the type has a default value.
+ bool isDefaultable() const { return !(isRefType() && !isNullable()); }
+
+ // Returns whether the type has a representation in JS.
+ bool isExposable() const {
+#if defined(ENABLE_WASM_SIMD)
+ return kind() != Kind::V128;
+#else
+ return true;
+#endif
+ }
+
+ bool isNullable() const { return tc_.isNullable(); }
+
+ const TypeDef* typeDef() const { return tc_.typeDef(); }
+
+ Kind kind() const { return Kind(tc_.typeCodeAbstracted()); }
+
+ RefType refType() const {
+ MOZ_ASSERT(isRefType());
+ return RefType(tc_);
+ }
+
+ RefType::Kind refTypeKind() const {
+ MOZ_ASSERT(isRefType());
+ return RefType(tc_).kind();
+ }
+
+ // Some types are encoded as JS::Value when they escape from Wasm (when passed
+ // as parameters to imports or returned from exports). For ExternRef the
+ // Value encoding is pretty much a requirement. For other types it's a choice
+ // that may (temporarily) simplify some code.
+ bool isEncodedAsJSValueOnEscape() const { return isRefType(); }
+
+ uint32_t size() const {
+ switch (tc_.typeCodeAbstracted()) {
+ case TypeCode::I8:
+ return 1;
+ case TypeCode::I16:
+ return 2;
+ case TypeCode::I32:
+ return 4;
+ case TypeCode::I64:
+ return 8;
+ case TypeCode::F32:
+ return 4;
+ case TypeCode::F64:
+ return 8;
+ case TypeCode::V128:
+ return 16;
+ case AbstractReferenceTypeCode:
+ return sizeof(void*);
+ default:
+ MOZ_ASSERT_UNREACHABLE();
+ return 0;
+ }
+ }
+ uint32_t alignmentInStruct() const { return size(); }
+ uint32_t indexingShift() const {
+ switch (size()) {
+ case 1:
+ return 0;
+ case 2:
+ return 1;
+ case 4:
+ return 2;
+ case 8:
+ return 3;
+ case 16:
+ return 4;
+ default:
+ MOZ_ASSERT_UNREACHABLE();
+ return 0;
+ }
+ }
+
+ PackedType<ValTypeTraits> widenToValType() const {
+ switch (tc_.typeCodeAbstracted()) {
+ case TypeCode::I8:
+ case TypeCode::I16:
+ return PackedType<ValTypeTraits>::I32;
+ default:
+ return PackedType<ValTypeTraits>(tc_);
+ }
+ }
+
+ PackedType<ValTypeTraits> valType() const {
+ MOZ_ASSERT(isValType());
+ return PackedType<ValTypeTraits>(tc_);
+ }
+
+ // Note, ToMIRType is only correct within Wasm, where an AnyRef is represented
+ // as a pointer. At the JS/wasm boundary, an AnyRef can be represented as a
+ // JS::Value, and the type translation may have to be handled specially and on
+ // a case-by-case basis.
+ jit::MIRType toMIRType() const {
+ switch (tc_.typeCodeAbstracted()) {
+ case TypeCode::I32:
+ return jit::MIRType::Int32;
+ case TypeCode::I64:
+ return jit::MIRType::Int64;
+ case TypeCode::F32:
+ return jit::MIRType::Float32;
+ case TypeCode::F64:
+ return jit::MIRType::Double;
+ case TypeCode::V128:
+ return jit::MIRType::Simd128;
+ case AbstractReferenceTypeCode:
+ return jit::MIRType::RefOrNull;
+ default:
+ MOZ_CRASH("bad type");
+ }
+ }
+
+ bool isValType() const {
+ switch (tc_.typeCode()) {
+ case TypeCode::I8:
+ case TypeCode::I16:
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ PackedType<FieldTypeTraits> fieldType() const {
+ MOZ_ASSERT(isValid());
+ return PackedType<FieldTypeTraits>(tc_);
+ }
+
+ static bool isSubTypeOf(PackedType subType, PackedType superType) {
+ // Anything is a subtype of itself.
+ if (subType == superType) {
+ return true;
+ }
+
+ // A reference may be a subtype of another reference
+ if (subType.isRefType() && superType.isRefType()) {
+ return RefType::isSubTypeOf(subType.refType(), superType.refType());
+ }
+
+ return false;
+ }
+
+ bool operator==(const PackedType& that) const {
+ MOZ_ASSERT(isValid() && that.isValid());
+ return tc_ == that.tc_;
+ }
+
+ bool operator!=(const PackedType& that) const {
+ MOZ_ASSERT(isValid() && that.isValid());
+ return tc_ != that.tc_;
+ }
+
+ bool operator==(Kind that) const {
+ MOZ_ASSERT(isValid());
+ MOZ_ASSERT(that != Kind::Ref);
+ return Kind(typeCode()) == that;
+ }
+
+ bool operator!=(Kind that) const { return !(*this == that); }
+};
+
+using ValType = PackedType<ValTypeTraits>;
+using FieldType = PackedType<FieldTypeTraits>;
+
+// The dominant use of this data type is for locals and args, and profiling
+// with ZenGarden and Tanks suggests an initial size of 16 minimises heap
+// allocation, both in terms of blocks and bytes.
+using ValTypeVector = Vector<ValType, 16, SystemAllocPolicy>;
+
+// ValType utilities
+
+extern bool ToValType(JSContext* cx, HandleValue v, ValType* out);
+extern bool ToRefType(JSContext* cx, HandleValue v, RefType* out);
+
+extern UniqueChars ToString(RefType type, const TypeContext* types);
+extern UniqueChars ToString(ValType type, const TypeContext* types);
+extern UniqueChars ToString(FieldType type, const TypeContext* types);
+extern UniqueChars ToString(const Maybe<ValType>& type,
+ const TypeContext* types);
+
+} // namespace wasm
+} // namespace js
+
+#endif // wasm_valtype_h