summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/wasm-type.h188
-rw-r--r--src/wasm/wasm-binary.cpp64
-rw-r--r--src/wasm/wasm-s-parser.cpp12
-rw-r--r--src/wasm/wasm-type.cpp893
4 files changed, 633 insertions, 524 deletions
diff --git a/src/wasm-type.h b/src/wasm-type.h
index 4d842cdca..0e159b3a9 100644
--- a/src/wasm-type.h
+++ b/src/wasm-type.h
@@ -32,11 +32,24 @@
namespace wasm {
+// The types defined in this file. All of them are small and typically passed by
+// value except for `Tuple` and `Struct`, which may own an unbounded amount of
+// data.
+class Type;
+class HeapType;
+struct Tuple;
+struct Signature;
+struct Field;
+struct Struct;
+struct Array;
+struct Rtt;
+
class Type {
// The `id` uniquely represents each type, so type equality is just a
// comparison of the ids. For basic types the `id` is just the `BasicType`
// enum value below, and for constructed types the `id` is the address of the
// canonical representation of the type, making lookups cheap for all types.
+ // Since `Type` is really just a single integer, it should be passed by value.
uintptr_t id;
public:
@@ -54,29 +67,30 @@ public:
anyref,
eqref,
i31ref,
- _last_basic_id = i31ref,
};
+ static constexpr BasicType _last_basic_type = i31ref;
- Type() = default;
+ Type() : id(none) {}
// BasicType can be implicitly upgraded to Type
- constexpr Type(BasicType id) : id(id){};
+ constexpr Type(BasicType id) : id(id) {}
- // But converting raw uint32_t is more dangerous, so make it explicit
- explicit Type(uint64_t id) : id(id){};
+ // But converting raw uint64_t is more dangerous, so make it explicit
+ explicit Type(uint64_t id) : id(id) {}
// Construct tuple from a list of single types
Type(std::initializer_list<Type>);
// Construct from tuple description
- explicit Type(const struct Tuple&);
+ Type(const Tuple&);
+ Type(Tuple&&);
// Construct from a heap type description. Also covers construction from
// Signature, Struct or Array via implicit conversion to HeapType.
- explicit Type(const struct HeapType&, bool nullable);
+ Type(HeapType, bool nullable);
// Construct from rtt description
- explicit Type(const struct Rtt&);
+ Type(Rtt);
// Predicates
// Compound Concrete
@@ -102,8 +116,8 @@ public:
// │ Tuple ║ │ x │ │ x │ │
// │ Rtt ║ │ x │ x │ x │ │
// └─────────────╨───┴───┴───┴───┴───────┘
- constexpr bool isBasic() const { return id <= _last_basic_id; }
- constexpr bool isCompound() const { return id > _last_basic_id; }
+ constexpr bool isBasic() const { return id <= _last_basic_type; }
+ constexpr bool isCompound() const { return id > _last_basic_type; }
constexpr bool isConcrete() const { return id >= i32; }
constexpr bool isInteger() const { return id == i32 || id == i64; }
constexpr bool isFloat() const { return id == f32 || id == f64; }
@@ -143,9 +157,9 @@ public:
// otherwise ambiguous whether to convert both this and other to int or
// convert other to Type.
bool operator==(const Type& other) const { return id == other.id; }
- bool operator==(const BasicType& otherId) const { return id == otherId; }
+ bool operator==(const BasicType& other) const { return id == other; }
bool operator!=(const Type& other) const { return id != other.id; }
- bool operator!=(const BasicType& otherId) const { return id != otherId; }
+ bool operator!=(const BasicType& other) const { return id != other; }
// Order types by some notion of simplicity
bool operator<(const Type& other) const;
@@ -161,7 +175,7 @@ public:
FeatureSet getFeatures() const;
// Gets the heap type corresponding to this type
- const HeapType& getHeapType() const;
+ HeapType getHeapType() const;
// Returns a number type based on its size in bytes and whether it is a float
// type.
@@ -254,17 +268,90 @@ struct ResultType {
std::string toString() const;
};
+class HeapType {
+ // Unlike `Type`, which represents the types of values on the WebAssembly
+ // stack, `HeapType` is used to describe the structures that reference types
+ // refer to. HeapTypes are canonicalized and interned exactly like Types and
+ // should also be passed by value.
+ uintptr_t id;
+
+public:
+ enum BasicHeapType : uint32_t {
+ func,
+ ext,
+ exn,
+ any,
+ eq,
+ i31,
+ };
+ static constexpr BasicHeapType _last_basic_type = i31;
+
+ // BasicHeapType can be implicitly upgraded to HeapType
+ constexpr HeapType(BasicHeapType id) : id(id) {}
+
+ // But converting raw uint64_t is more dangerous, so make it explicit
+ explicit HeapType(uint64_t id) : id(id) {}
+
+ HeapType(Signature signature);
+ HeapType(const Struct& struct_);
+ HeapType(Struct&& struct_);
+ HeapType(Array array);
+
+ constexpr bool isBasic() const { return id <= _last_basic_type; }
+ constexpr bool isCompound() const { return id > _last_basic_type; }
+ bool isFunction() const;
+ bool isSignature() const;
+ bool isStruct() const;
+ bool isArray() const;
+
+ Signature getSignature() const;
+ const Struct& getStruct() const;
+ Array getArray() const;
+
+ constexpr uint64_t getID() const { return id; }
+ constexpr BasicHeapType getBasic() const {
+ assert(isBasic() && "Basic heap type expected");
+ return static_cast<BasicHeapType>(id);
+ }
+
+ // (In)equality must be defined for both HeapType and BasicHeapType because it
+ // is otherwise ambiguous whether to convert both this and other to int or
+ // convert other to HeapType.
+ bool operator==(const HeapType& other) const { return id == other.id; }
+ bool operator==(const BasicHeapType& other) const { return id == other; }
+ bool operator!=(const HeapType& other) const { return id != other.id; }
+ bool operator!=(const BasicHeapType& other) const { return id != other; }
+
+ bool operator<(const HeapType& other) const;
+ std::string toString() const;
+};
+
typedef std::vector<Type> TypeList;
+// Passed by reference rather than by value because it can own an unbounded
+// amount of data.
struct Tuple {
TypeList types;
Tuple() : types() {}
- Tuple(std::initializer_list<Type> types) : types(types) {}
- Tuple(const TypeList& types) : types(types) {}
- Tuple(TypeList&& types) : types(std::move(types)) {}
+ Tuple(std::initializer_list<Type> types) : types(types) { validate(); }
+ Tuple(const TypeList& types) : types(types) { validate(); }
+ Tuple(TypeList&& types) : types(std::move(types)) { validate(); }
bool operator==(const Tuple& other) const { return types == other.types; }
bool operator!=(const Tuple& other) const { return !(*this == other); }
+ bool operator<(const Tuple& other) const { return types < other.types; }
std::string toString() const;
+
+ // Prevent accidental copies
+ Struct& operator=(const Struct&) = delete;
+
+private:
+ void validate() {
+#ifndef NDEBUG
+ for (auto type : types) {
+ assert(type.isSingle());
+ }
+#endif
+ }
};
struct Signature {
@@ -316,6 +403,8 @@ struct Field {
typedef std::vector<Field> FieldList;
+// Passed by reference rather than by value because it can own an unbounded
+// amount of data.
struct Struct {
FieldList fields;
Struct(const Struct& other) : fields(other.fields) {}
@@ -325,85 +414,30 @@ struct Struct {
bool operator!=(const Struct& other) const { return !(*this == other); }
bool operator<(const Struct& other) const { return fields < other.fields; }
std::string toString() const;
+
+ // Prevent accidental copies
+ Struct& operator=(const Struct&) = delete;
};
struct Array {
Field element;
Array(const Array& other) : element(other.element) {}
- Array(const Field& element) : element(element) {}
- Array(Field&& element) : element(std::move(element)) {}
+ Array(Field element) : element(element) {}
bool operator==(const Array& other) const { return element == other.element; }
bool operator!=(const Array& other) const { return !(*this == other); }
bool operator<(const Array& other) const { return element < other.element; }
std::string toString() const;
};
-struct HeapType {
- enum Kind {
- FuncKind,
- ExternKind,
- ExnKind,
- AnyKind,
- EqKind,
- I31Kind,
- _last_basic_kind = I31Kind,
- SignatureKind,
- StructKind,
- ArrayKind,
- } kind;
- union {
- Signature signature;
- Struct struct_;
- Array array;
- };
- HeapType(Kind kind) : kind(kind) { assert(kind <= _last_basic_kind); }
- HeapType(const Signature& signature)
- : kind(SignatureKind), signature(signature) {}
- HeapType(Signature&& signature)
- : kind(SignatureKind), signature(std::move(signature)) {}
- HeapType(const Struct& struct_) : kind(StructKind), struct_(struct_) {}
- HeapType(Struct&& struct_) : kind(StructKind), struct_(std::move(struct_)) {}
- HeapType(const Array& array) : kind(ArrayKind), array(array) {}
- HeapType(Array&& array) : kind(ArrayKind), array(std::move(array)) {}
- HeapType(const HeapType& other);
- ~HeapType();
-
- bool isBasic() const { return kind <= _last_basic_kind; }
- bool isSignature() const { return kind == SignatureKind; }
- Signature getSignature() const {
- assert(isSignature() && "Not a signature");
- return signature;
- }
- bool isStruct() const { return kind == StructKind; }
- const Struct& getStruct() const {
- assert(isStruct() && "Not a struct");
- return struct_;
- }
- bool isArray() const { return kind == ArrayKind; }
- const Array& getArray() const {
- assert(isArray() && "Not an array");
- return array;
- }
- bool isException() const { return kind == ExnKind; }
-
- bool operator==(const HeapType& other) const;
- bool operator!=(const HeapType& other) const { return !(*this == other); }
- bool operator<(const HeapType& other) const;
- HeapType& operator=(const HeapType& other);
- std::string toString() const;
-};
-
struct Rtt {
uint32_t depth;
HeapType heapType;
- Rtt(uint32_t depth, const HeapType& heapType)
- : depth(depth), heapType(heapType) {}
- Rtt(uint32_t depth, HeapType&& heapType)
- : depth(depth), heapType(std::move(heapType)) {}
+ Rtt(uint32_t depth, HeapType heapType) : depth(depth), heapType(heapType) {}
bool operator==(const Rtt& other) const {
return depth == other.depth && heapType == other.heapType;
}
bool operator!=(const Rtt& other) const { return !(*this == other); }
+ bool operator<(const Rtt& other) const;
std::string toString() const;
};
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index e70dc4a29..25c1eea8c 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -1034,29 +1034,29 @@ void WasmBinaryWriter::writeHeapType(HeapType type) {
return;
}
int ret = 0;
- switch (type.kind) {
- case HeapType::FuncKind:
- ret = BinaryConsts::EncodedHeapType::func;
- break;
- case HeapType::ExternKind:
- ret = BinaryConsts::EncodedHeapType::extern_;
- break;
- case HeapType::ExnKind:
- ret = BinaryConsts::EncodedHeapType::exn;
- break;
- case HeapType::AnyKind:
- ret = BinaryConsts::EncodedHeapType::any;
- break;
- case HeapType::EqKind:
- ret = BinaryConsts::EncodedHeapType::eq;
- break;
- case HeapType::I31Kind:
- ret = BinaryConsts::EncodedHeapType::i31;
- break;
- case HeapType::SignatureKind:
- case HeapType::StructKind:
- case HeapType::ArrayKind:
- WASM_UNREACHABLE("TODO: compound GC types");
+ if (type.isBasic()) {
+ switch (type.getBasic()) {
+ case HeapType::func:
+ ret = BinaryConsts::EncodedHeapType::func;
+ break;
+ case HeapType::ext:
+ ret = BinaryConsts::EncodedHeapType::extern_;
+ break;
+ case HeapType::exn:
+ ret = BinaryConsts::EncodedHeapType::exn;
+ break;
+ case HeapType::any:
+ ret = BinaryConsts::EncodedHeapType::any;
+ break;
+ case HeapType::eq:
+ ret = BinaryConsts::EncodedHeapType::eq;
+ break;
+ case HeapType::i31:
+ ret = BinaryConsts::EncodedHeapType::i31;
+ break;
+ }
+ } else {
+ WASM_UNREACHABLE("TODO: compound GC types");
}
o << S64LEB(ret); // TODO: Actually s33
}
@@ -1401,17 +1401,17 @@ HeapType WasmBinaryBuilder::getHeapType() {
}
switch (type) {
case BinaryConsts::EncodedHeapType::func:
- return HeapType::FuncKind;
+ return HeapType::func;
case BinaryConsts::EncodedHeapType::extern_:
- return HeapType::ExternKind;
+ return HeapType::ext;
case BinaryConsts::EncodedHeapType::exn:
- return HeapType::ExnKind;
+ return HeapType::exn;
case BinaryConsts::EncodedHeapType::any:
- return HeapType::AnyKind;
+ return HeapType::any;
case BinaryConsts::EncodedHeapType::eq:
- return HeapType::EqKind;
+ return HeapType::eq;
case BinaryConsts::EncodedHeapType::i31:
- return HeapType::I31Kind;
+ return HeapType::i31;
default:
throwError("invalid wasm heap type: " + std::to_string(type));
}
@@ -5630,7 +5630,8 @@ bool WasmBinaryBuilder::maybeVisitStructGet(Expression*& out, uint32_t code) {
default:
return false;
}
- auto type = getHeapType();
+ // This type annotation is unused. Beware it needing to be used in the future!
+ getHeapType();
curr->index = getU32LEB();
curr->ref = popNonVoidExpression();
curr->finalize();
@@ -5643,7 +5644,8 @@ bool WasmBinaryBuilder::maybeVisitStructSet(Expression*& out, uint32_t code) {
return false;
}
auto* curr = allocator.alloc<StructSet>();
- auto type = getHeapType();
+ // This type annotation is unused. Beware it needing to be used in the future!
+ getHeapType();
curr->index = getU32LEB();
curr->ref = popNonVoidExpression();
curr->value = popNonVoidExpression();
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index 37592fcd5..21104d0fe 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -879,32 +879,32 @@ HeapType SExpressionWasmBuilder::stringToHeapType(const char* str,
bool prefix) {
if (str[0] == 'a') {
if (str[1] == 'n' && str[2] == 'y' && (prefix || str[3] == 0)) {
- return HeapType::AnyKind;
+ return HeapType::any;
}
}
if (str[0] == 'e') {
if (str[1] == 'q' && (prefix || str[2] == 0)) {
- return HeapType::EqKind;
+ return HeapType::eq;
}
if (str[1] == 'x') {
if (str[2] == 'n' && (prefix || str[3] == 0)) {
- return HeapType::ExnKind;
+ return HeapType::exn;
}
if (str[2] == 't' && str[3] == 'e' && str[4] == 'r' && str[5] == 'n' &&
(prefix || str[6] == 0)) {
- return HeapType::ExternKind;
+ return HeapType::ext;
}
}
}
if (str[0] == 'i') {
if (str[1] == '3' && str[2] == '1' && (prefix || str[3] == 0)) {
- return HeapType::I31Kind;
+ return HeapType::i31;
}
}
if (str[0] == 'f') {
if (str[1] == 'u' && str[2] == 'n' && str[3] == 'c' &&
(prefix || str[4] == 0)) {
- return HeapType::FuncKind;
+ return HeapType::func;
}
}
throw ParseException(std::string("invalid wasm heap type: ") + str);
diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp
index 22a7c9888..48b37a864 100644
--- a/src/wasm/wasm-type.cpp
+++ b/src/wasm/wasm-type.cpp
@@ -27,7 +27,10 @@
namespace wasm {
+namespace {
+
struct TypeInfo {
+ using type_t = Type;
enum Kind {
TupleKind,
RefKind,
@@ -45,44 +48,11 @@ struct TypeInfo {
TypeInfo(const Tuple& tuple) : kind(TupleKind), tuple(tuple) {}
TypeInfo(Tuple&& tuple) : kind(TupleKind), tuple(std::move(tuple)) {}
- TypeInfo(const HeapType& heapType, bool nullable)
+ TypeInfo(HeapType heapType, bool nullable)
: kind(RefKind), ref{heapType, nullable} {}
- TypeInfo(HeapType&& heapType, bool nullable)
- : kind(RefKind), ref{std::move(heapType), nullable} {}
- TypeInfo(const Rtt& rtt) : kind(RttKind), rtt(rtt) {}
- TypeInfo(Rtt&& rtt) : kind(RttKind), rtt(std::move(rtt)) {}
- TypeInfo(const TypeInfo& other) {
- kind = other.kind;
- switch (kind) {
- case TupleKind:
- new (&tuple) auto(other.tuple);
- return;
- case RefKind:
- new (&ref) auto(other.ref);
- return;
- case RttKind:
- new (&rtt) auto(other.rtt);
- return;
- }
- WASM_UNREACHABLE("unexpected kind");
- }
- ~TypeInfo() {
- switch (kind) {
- case TupleKind: {
- tuple.~Tuple();
- return;
- }
- case RefKind: {
- ref.~Ref();
- return;
- }
- case RttKind: {
- rtt.~Rtt();
- return;
- }
- }
- WASM_UNREACHABLE("unexpected kind");
- }
+ TypeInfo(Rtt rtt) : kind(RttKind), rtt(rtt) {}
+ TypeInfo(const TypeInfo& other);
+ ~TypeInfo();
constexpr bool isTuple() const { return kind == TupleKind; }
constexpr bool isRef() const { return kind == RefKind; }
@@ -90,257 +60,272 @@ struct TypeInfo {
bool isNullable() const { return kind == RefKind && ref.nullable; }
- bool operator==(const TypeInfo& other) const {
- if (kind != other.kind) {
- return false;
- }
- switch (kind) {
- case TupleKind:
- return tuple == other.tuple;
- case RefKind:
- return ref.heapType == other.ref.heapType &&
- ref.nullable == other.ref.nullable;
- case RttKind:
- return rtt == other.rtt;
- }
- WASM_UNREACHABLE("unexpected kind");
- }
+ bool operator==(const TypeInfo& other) const;
bool operator!=(const TypeInfo& other) const { return !(*this == other); }
- TypeInfo& operator=(const TypeInfo& other) {
- if (&other != this) {
- this->~TypeInfo();
- new (this) auto(other);
- }
- return *this;
- }
-
- std::string toString() const;
+ bool operator<(const TypeInfo& other) const;
};
-std::ostream& operator<<(std::ostream&, TypeInfo);
+struct HeapTypeInfo {
+ using type_t = HeapType;
+ enum Kind {
+ SignatureKind,
+ StructKind,
+ ArrayKind,
+ } kind;
+ union {
+ Signature signature;
+ Struct struct_;
+ Array array;
+ };
+
+ HeapTypeInfo(Signature sig) : kind(SignatureKind), signature(sig) {}
+ HeapTypeInfo(const Struct& struct_) : kind(StructKind), struct_(struct_) {}
+ HeapTypeInfo(Struct&& struct_)
+ : kind(StructKind), struct_(std::move(struct_)) {}
+ HeapTypeInfo(Array array) : kind(ArrayKind), array(array) {}
+ HeapTypeInfo(const HeapTypeInfo& other);
+ ~HeapTypeInfo();
+
+ constexpr bool isSignature() const { return kind == SignatureKind; }
+ constexpr bool isStruct() const { return kind == StructKind; }
+ constexpr bool isArray() const { return kind == ArrayKind; }
+
+ bool operator==(const HeapTypeInfo& other) const;
+ bool operator!=(const HeapTypeInfo& other) const { return !(*this == other); }
+ bool operator<(const HeapTypeInfo& other) const;
+};
+} // anonymous namespace
} // namespace wasm
namespace std {
-template<> class hash<wasm::TypeList> {
+template<> class hash<wasm::TypeInfo> {
public:
- size_t operator()(const wasm::TypeList& types) const {
- auto digest = wasm::hash(types.size());
- for (auto type : types) {
- wasm::rehash(digest, type);
- }
- return digest;
- }
+ size_t operator()(const wasm::TypeInfo&) const;
};
-template<> class hash<wasm::FieldList> {
+template<> class hash<wasm::HeapTypeInfo> {
public:
- size_t operator()(const wasm::FieldList& fields) const {
- auto digest = wasm::hash(fields.size());
- for (auto field : fields) {
- wasm::rehash(digest, field);
- }
- return digest;
- }
+ size_t operator()(const wasm::HeapTypeInfo&) const;
};
-template<> class hash<wasm::TypeInfo> {
-public:
- size_t operator()(const wasm::TypeInfo& info) const {
- auto digest = wasm::hash(info.kind);
- switch (info.kind) {
- case wasm::TypeInfo::TupleKind: {
- wasm::rehash(digest, info.tuple);
- return digest;
- }
- case wasm::TypeInfo::RefKind: {
- wasm::rehash(digest, info.ref.heapType);
- wasm::rehash(digest, info.ref.nullable);
- return digest;
- }
- case wasm::TypeInfo::RttKind: {
- wasm::rehash(digest, info.rtt);
- return digest;
- }
- }
- WASM_UNREACHABLE("unexpected kind");
- }
-};
+} // namespace std
-size_t hash<wasm::Type>::operator()(const wasm::Type& type) const {
- return wasm::hash(type.getID());
-}
+namespace wasm {
+namespace {
-size_t hash<wasm::Tuple>::operator()(const wasm::Tuple& tuple) const {
- return wasm::hash(tuple.types);
-}
+TypeInfo* getTypeInfo(Type type) { return (TypeInfo*)type.getID(); }
+HeapTypeInfo* getHeapTypeInfo(HeapType ht) { return (HeapTypeInfo*)ht.getID(); }
-size_t hash<wasm::Signature>::operator()(const wasm::Signature& sig) const {
- auto digest = wasm::hash(sig.params);
- wasm::rehash(digest, sig.results);
- return digest;
+TypeInfo::TypeInfo(const TypeInfo& other) {
+ kind = other.kind;
+ switch (kind) {
+ case TupleKind:
+ new (&tuple) auto(other.tuple);
+ return;
+ case RefKind:
+ new (&ref) auto(other.ref);
+ return;
+ case RttKind:
+ new (&rtt) auto(other.rtt);
+ return;
+ }
+ WASM_UNREACHABLE("unexpected kind");
}
-size_t hash<wasm::Field>::operator()(const wasm::Field& field) const {
- auto digest = wasm::hash(field.type);
- wasm::rehash(digest, field.packedType);
- wasm::rehash(digest, field.mutable_);
- // Note that the name is not hashed here - it is pure metadata for printing
- // purposes only.
- return digest;
+TypeInfo::~TypeInfo() {
+ switch (kind) {
+ case TupleKind:
+ tuple.~Tuple();
+ return;
+ case RefKind:
+ ref.~Ref();
+ return;
+ case RttKind:
+ rtt.~Rtt();
+ return;
+ }
+ WASM_UNREACHABLE("unexpected kind");
}
-size_t hash<wasm::Struct>::operator()(const wasm::Struct& struct_) const {
- return wasm::hash(struct_.fields);
+bool TypeInfo::operator==(const TypeInfo& other) const {
+ if (kind != other.kind) {
+ return false;
+ }
+ switch (kind) {
+ case TupleKind:
+ return tuple == other.tuple;
+ case RefKind:
+ return ref.heapType == other.ref.heapType &&
+ ref.nullable == other.ref.nullable;
+ case RttKind:
+ return rtt == other.rtt;
+ }
+ WASM_UNREACHABLE("unexpected kind");
}
-size_t hash<wasm::Array>::operator()(const wasm::Array& array) const {
- return wasm::hash(array.element);
+bool TypeInfo::operator<(const TypeInfo& other) const {
+ if (kind != other.kind) {
+ return kind < other.kind;
+ }
+ switch (kind) {
+ case TupleKind:
+ return tuple < other.tuple;
+ case RefKind:
+ if (ref.nullable != other.ref.nullable) {
+ return ref.nullable < other.ref.nullable;
+ }
+ return ref.heapType < other.ref.heapType;
+ case RttKind:
+ return rtt < other.rtt;
+ }
+ WASM_UNREACHABLE("unexpected kind");
}
-size_t hash<wasm::HeapType>::operator()(const wasm::HeapType& heapType) const {
- auto digest = wasm::hash(heapType.kind);
- switch (heapType.kind) {
- case wasm::HeapType::FuncKind:
- case wasm::HeapType::ExternKind:
- case wasm::HeapType::AnyKind:
- case wasm::HeapType::EqKind:
- case wasm::HeapType::I31Kind:
- case wasm::HeapType::ExnKind:
- return digest;
- case wasm::HeapType::SignatureKind:
- wasm::rehash(digest, heapType.signature);
- return digest;
- case wasm::HeapType::StructKind:
- wasm::rehash(digest, heapType.struct_);
- return digest;
- case wasm::HeapType::ArrayKind:
- wasm::rehash(digest, heapType.array);
- return digest;
+HeapTypeInfo::HeapTypeInfo(const HeapTypeInfo& other) {
+ kind = other.kind;
+ switch (kind) {
+ case SignatureKind:
+ new (&signature) auto(other.signature);
+ return;
+ case StructKind:
+ new (&struct_) auto(other.struct_);
+ return;
+ case ArrayKind:
+ new (&array) auto(other.array);
+ return;
}
WASM_UNREACHABLE("unexpected kind");
}
-size_t hash<wasm::Rtt>::operator()(const wasm::Rtt& rtt) const {
- auto digest = wasm::hash(rtt.depth);
- wasm::rehash(digest, rtt.heapType);
- return digest;
+HeapTypeInfo::~HeapTypeInfo() {
+ switch (kind) {
+ case SignatureKind:
+ signature.~Signature();
+ return;
+ case StructKind:
+ struct_.~Struct();
+ return;
+ case ArrayKind:
+ array.~Array();
+ return;
+ }
+ WASM_UNREACHABLE("unexpected kind");
}
-} // namespace std
-
-namespace wasm {
+bool HeapTypeInfo::operator==(const HeapTypeInfo& other) const {
+ if (kind != other.kind) {
+ return false;
+ }
+ switch (kind) {
+ case SignatureKind:
+ return signature == other.signature;
+ case StructKind:
+ return struct_ == other.struct_;
+ case ArrayKind:
+ return array == other.array;
+ }
+ WASM_UNREACHABLE("unexpected kind");
+}
-namespace {
+bool HeapTypeInfo::operator<(const HeapTypeInfo& other) const {
+ if (kind != other.kind) {
+ return kind < other.kind;
+ }
+ switch (kind) {
+ case SignatureKind:
+ return signature < other.signature;
+ case StructKind:
+ return struct_ < other.struct_;
+ case ArrayKind:
+ return array < other.array;
+ }
+ WASM_UNREACHABLE("unexpected kind");
+}
-struct TypeStore {
+template<typename Info> struct Store {
std::mutex mutex;
- // Track unique_ptrs for constructed types to avoid leaks
- std::vector<std::unique_ptr<TypeInfo>> constructedTypes;
-
- // Maps from constructed types to the canonical Type ID.
- std::unordered_map<TypeInfo, uintptr_t> indices = {
- // If a Type is constructed from a list of types, the list of types becomes
- // implicitly converted to a TypeInfo before canonicalizing its id. This is
- // also the case if a list of just one type is provided, even though such a
- // list of types will be canonicalized to the BasicType of the single type.
- // As such, the following entries are solely placeholders to enable the
- // lookup of lists of just one type to the BasicType of the single type.
- {TypeInfo(Tuple()), Type::none},
- {TypeInfo({Type::unreachable}), Type::unreachable},
- {TypeInfo({Type::i32}), Type::i32},
- {TypeInfo({Type::i64}), Type::i64},
- {TypeInfo({Type::f32}), Type::f32},
- {TypeInfo({Type::f64}), Type::f64},
- {TypeInfo({Type::v128}), Type::v128},
- {TypeInfo({Type::funcref}), Type::funcref},
- {TypeInfo(HeapType(HeapType::FuncKind), true), Type::funcref},
- {TypeInfo({Type::externref}), Type::externref},
- {TypeInfo(HeapType(HeapType::ExternKind), true), Type::externref},
- {TypeInfo({Type::exnref}), Type::exnref},
- {TypeInfo(HeapType(HeapType::ExnKind), true), Type::exnref},
- {TypeInfo({Type::anyref}), Type::anyref},
- {TypeInfo(HeapType(HeapType::AnyKind), true), Type::anyref},
- {TypeInfo({Type::eqref}), Type::eqref},
- {TypeInfo(HeapType(HeapType::EqKind), true), Type::eqref},
- {TypeInfo({Type::i31ref}), Type::i31ref},
- {TypeInfo(HeapType(HeapType::I31Kind), false), Type::i31ref},
- };
+ // Track unique_ptrs for constructed types to avoid leaks.
+ std::vector<std::unique_ptr<Info>> constructedTypes;
- uintptr_t canonicalize(const TypeInfo& info) {
+ // Maps from constructed types to their canonical Type IDs.
+ std::unordered_map<Info, uintptr_t> typeIDs;
+
+ typename Info::type_t canonicalize(const Info& info) {
std::lock_guard<std::mutex> lock(mutex);
- auto indexIt = indices.find(info);
- if (indexIt != indices.end()) {
- return indexIt->second;
+ auto indexIt = typeIDs.find(info);
+ if (indexIt != typeIDs.end()) {
+ return typename Info::type_t(indexIt->second);
}
- auto ptr = std::make_unique<TypeInfo>(info);
+ auto ptr = std::make_unique<Info>(info);
auto id = uintptr_t(ptr.get());
constructedTypes.push_back(std::move(ptr));
- assert(id > Type::_last_basic_id);
- indices[info] = id;
- return id;
+ assert(id > Info::type_t::_last_basic_type);
+ typeIDs[info] = id;
+ return typename Info::type_t(id);
}
+};
- Type makeType(const Tuple& tuple) {
- auto& types = tuple.types;
-#ifndef NDEBUG
- for (Type t : types) {
- assert(t.isSingle());
- }
-#endif
- if (types.size() == 0) {
- return Type::none;
- }
- if (types.size() == 1) {
- return types[0];
+struct TypeStore : Store<TypeInfo> {
+ Type canonicalize(TypeInfo info) {
+ if (info.isTuple()) {
+ if (info.tuple.types.size() == 0) {
+ return Type::none;
+ }
+ if (info.tuple.types.size() == 1) {
+ return info.tuple.types[0];
+ }
}
- return Type(canonicalize(TypeInfo(tuple)));
- }
-
- Type makeType(const HeapType& heapType, bool nullable) {
-#ifndef NDEBUG
- switch (heapType.kind) {
- case HeapType::FuncKind:
- case HeapType::ExternKind:
- case HeapType::AnyKind:
- case HeapType::EqKind:
- case HeapType::I31Kind:
- case HeapType::ExnKind:
- case HeapType::SignatureKind:
- break;
- case HeapType::StructKind:
- for (Field f : heapType.struct_.fields) {
- assert(f.type.isSingle());
+ if (info.isRef() && info.ref.heapType.isBasic()) {
+ if (info.ref.nullable) {
+ switch (info.ref.heapType.getBasic()) {
+ case HeapType::func:
+ return Type::funcref;
+ case HeapType::ext:
+ return Type::externref;
+ case HeapType::exn:
+ return Type::exnref;
+ case HeapType::any:
+ return Type::anyref;
+ case HeapType::eq:
+ return Type::eqref;
+ case HeapType::i31:
+ break;
}
- break;
- case HeapType::ArrayKind:
- assert(heapType.array.element.type.isSingle());
- break;
+ } else {
+ if (info.ref.heapType == HeapType::i31) {
+ return Type::i31ref;
+ }
+ }
}
-#endif
- return Type(canonicalize(TypeInfo(heapType, nullable)));
+ return Store<TypeInfo>::canonicalize(info);
}
-
- Type makeType(const Rtt& rtt) { return Type(canonicalize(TypeInfo(rtt))); }
};
-TypeStore globalTypeStore;
+using HeapTypeStore = Store<HeapTypeInfo>;
-TypeInfo* getTypeInfo(const Type& type) { return (TypeInfo*)type.getID(); }
+TypeStore globalTypeStore;
+HeapTypeStore globalHeapTypeStore;
} // anonymous namespace
Type::Type(std::initializer_list<Type> types) : Type(Tuple(types)) {}
-Type::Type(const Tuple& tuple) { *this = globalTypeStore.makeType(tuple); }
+Type::Type(const Tuple& tuple) {
+ new (this) Type(globalTypeStore.canonicalize(tuple));
+}
-Type::Type(const HeapType& heapType, bool nullable) {
- *this = globalTypeStore.makeType(heapType, nullable);
+Type::Type(Tuple&& tuple) {
+ new (this) Type(globalTypeStore.canonicalize(std::move(tuple)));
}
-Type::Type(const Rtt& rtt) { *this = globalTypeStore.makeType(rtt); }
+Type::Type(HeapType heapType, bool nullable) {
+ new (this) Type(globalTypeStore.canonicalize(TypeInfo(heapType, nullable)));
+}
+
+Type::Type(Rtt rtt) { new (this) Type(globalTypeStore.canonicalize(rtt)); }
bool Type::isTuple() const {
if (isBasic()) {
@@ -363,7 +348,7 @@ bool Type::isFunction() const {
return id == funcref;
} else {
auto* info = getTypeInfo(*this);
- return info->isRef() && info->ref.heapType.isSignature();
+ return info->isRef() && info->ref.heapType.isFunction();
}
}
@@ -372,7 +357,7 @@ bool Type::isException() const {
return id == exnref;
} else {
auto* info = getTypeInfo(*this);
- return info->isRef() && info->ref.heapType.isException();
+ return info->isRef() && info->ref.heapType == HeapType::exn;
}
}
@@ -397,25 +382,20 @@ bool Type::isStruct() const { return isRef() && getHeapType().isStruct(); }
bool Type::isArray() const { return isRef() && getHeapType().isArray(); }
bool Type::operator<(const Type& other) const {
- auto comp = [](const Type& a, const Type& b) {
- if (a.isBasic() && b.isBasic()) {
- return a.getBasic() < b.getBasic();
- }
- if (a.isBasic()) {
- return true;
- }
- if (b.isBasic()) {
- return false;
- }
- // Both are compound.
- if (a.isNullable() != b.isNullable()) {
- return a.isNullable();
- }
- return a.getHeapType() < b.getHeapType();
- };
- return std::lexicographical_compare(
- begin(), end(), other.begin(), other.end(), comp);
-}
+ if (*this == other) {
+ return false;
+ }
+ if (isBasic() && other.isBasic()) {
+ return getBasic() < other.getBasic();
+ }
+ if (isBasic()) {
+ return true;
+ }
+ if (other.isBasic()) {
+ return false;
+ }
+ return *getTypeInfo(*this) < *getTypeInfo(other);
+};
unsigned Type::getByteSize() const {
// TODO: alignment?
@@ -512,39 +492,43 @@ FeatureSet Type::getFeatures() const {
return getSingleFeatures(*this);
}
-// getHeapType() returns a const HeapType&, so we need a canonical object to
-// return for the basic types, so that we don't create a temporary copy on each
-// call.
-namespace statics {
-static HeapType funcHeapType(HeapType::FuncKind),
- externHeapType(HeapType::ExternKind), exnHeapType(HeapType::ExnKind),
- anyHeapType(HeapType::AnyKind), eqHeapType(HeapType::EqKind),
- i31HeapType(HeapType::I31Kind);
-}
-
-const HeapType& Type::getHeapType() const {
- if (isRef()) {
- if (isCompound()) {
- return getTypeInfo(*this)->ref.heapType;
- }
+HeapType Type::getHeapType() const {
+ if (isBasic()) {
switch (getBasic()) {
- case funcref:
- return statics::funcHeapType;
- case externref:
- return statics::externHeapType;
- case exnref:
- return statics::exnHeapType;
- case anyref:
- return statics::anyHeapType;
- case eqref:
- return statics::eqHeapType;
- case i31ref:
- return statics::i31HeapType;
- default:
+ case Type::none:
+ case Type::unreachable:
+ case Type::i32:
+ case Type::i64:
+ case Type::f32:
+ case Type::f64:
+ case Type::v128:
break;
+ case Type::funcref:
+ return HeapType::func;
+ case Type::externref:
+ return HeapType::ext;
+ case Type::exnref:
+ return HeapType::exn;
+ case Type::anyref:
+ return HeapType::any;
+ case Type::eqref:
+ return HeapType::eq;
+ case Type::i31ref:
+ return HeapType::i31;
}
+ WASM_UNREACHABLE("Unexpected type");
+ } else {
+ auto* info = getTypeInfo(*this);
+ switch (info->kind) {
+ case TypeInfo::TupleKind:
+ break;
+ case TypeInfo::RefKind:
+ return info->ref.heapType;
+ case TypeInfo::RttKind:
+ return info->rtt.heapType;
+ }
+ WASM_UNREACHABLE("Unexpected type");
}
- WASM_UNREACHABLE("unexpected type");
}
Type Type::get(unsigned byteSize, bool float_) {
@@ -672,93 +656,90 @@ const Type& Type::operator[](size_t index) const {
}
}
-HeapType::HeapType(const HeapType& other) : kind(other.kind) {
- switch (kind) {
- case FuncKind:
- case ExternKind:
- case AnyKind:
- case EqKind:
- case I31Kind:
- case ExnKind:
- return;
- case SignatureKind:
- new (&signature) auto(other.signature);
- return;
- case StructKind:
- new (&struct_) auto(other.struct_);
- return;
- case ArrayKind:
- new (&array) auto(other.array);
- return;
- }
- WASM_UNREACHABLE("unexpected kind");
+HeapType::HeapType(Signature signature) {
+ new (this) HeapType(globalHeapTypeStore.canonicalize(signature));
}
-HeapType::~HeapType() {
- switch (kind) {
- case FuncKind:
- case ExternKind:
- case AnyKind:
- case EqKind:
- case I31Kind:
- case ExnKind:
- return;
- case SignatureKind:
- signature.~Signature();
- return;
- case StructKind:
- struct_.~Struct();
- return;
- case ArrayKind:
- array.~Array();
- return;
+HeapType::HeapType(const Struct& struct_) {
+ new (this) HeapType(globalHeapTypeStore.canonicalize(struct_));
+}
+
+HeapType::HeapType(Struct&& struct_) {
+ new (this) HeapType(globalHeapTypeStore.canonicalize(std::move(struct_)));
+}
+
+HeapType::HeapType(Array array) {
+ new (this) HeapType(globalHeapTypeStore.canonicalize(array));
+}
+
+bool HeapType::isFunction() const {
+ if (isBasic()) {
+ return id == func;
+ } else {
+ return getHeapTypeInfo(*this)->isSignature();
}
}
-bool HeapType::operator==(const HeapType& other) const {
- if (kind != other.kind) {
+bool HeapType::isSignature() const {
+ if (isBasic()) {
return false;
+ } else {
+ return getHeapTypeInfo(*this)->isSignature();
}
- switch (kind) {
- case FuncKind:
- case ExternKind:
- case AnyKind:
- case EqKind:
- case I31Kind:
- case ExnKind:
- return true;
- case SignatureKind:
- return signature == other.signature;
- case StructKind:
- return struct_ == other.struct_;
- case ArrayKind:
- return array == other.array;
+}
+
+bool HeapType::isStruct() const {
+ if (isBasic()) {
+ return false;
+ } else {
+ return getHeapTypeInfo(*this)->isStruct();
}
- WASM_UNREACHABLE("unexpected kind");
}
bool HeapType::operator<(const HeapType& other) const {
- if (kind != other.kind) {
- return kind < other.kind;
+ if (*this == other) {
+ return false;
+ }
+ if (isBasic() && other.isBasic()) {
+ return getBasic() < other.getBasic();
}
- if (isSignature()) {
- return getSignature() < other.getSignature();
+ if (isBasic()) {
+ return true;
}
- if (isStruct()) {
- return getStruct() < other.getStruct();
+ if (other.isBasic()) {
+ return false;
}
- if (isArray()) {
- return getArray() < other.getArray();
+ return *getHeapTypeInfo(*this) < *getHeapTypeInfo(other);
+}
+
+bool HeapType::isArray() const {
+ if (isBasic()) {
+ return false;
+ } else {
+ return getHeapTypeInfo(*this)->isArray();
}
- WASM_UNREACHABLE("unimplemented type comparison");
}
-HeapType& HeapType::operator=(const HeapType& other) {
- if (&other != this) {
- this->~HeapType();
- new (this) auto(other);
+Signature HeapType::getSignature() const {
+ assert(isSignature());
+ return getHeapTypeInfo(*this)->signature;
+}
+
+const Struct& HeapType::getStruct() const {
+ assert(isStruct());
+ return getHeapTypeInfo(*this)->struct_;
+}
+
+Array HeapType::getArray() const {
+ assert(isArray());
+ return getHeapTypeInfo(*this)->array;
+}
+
+bool Signature::operator<(const Signature& other) const {
+ if (results != other.results) {
+ return results < other.results;
}
- return *this;
+ return params < other.params;
}
bool Field::operator<(const Field& other) const {
@@ -771,6 +752,13 @@ bool Field::operator<(const Field& other) const {
return type < other.type;
}
+bool Rtt::operator<(const Rtt& other) const {
+ if (depth != other.depth) {
+ return depth < other.depth;
+ }
+ return heapType < other.heapType;
+}
+
namespace {
std::ostream&
@@ -809,65 +797,41 @@ std::string HeapType::toString() const { return genericToString(*this); }
std::string Rtt::toString() const { return genericToString(*this); }
-std::string TypeInfo::toString() const { return genericToString(*this); }
-
-bool Signature::operator<(const Signature& other) const {
- if (results < other.results) {
- return true;
- } else if (other.results < results) {
- return false;
- } else {
- return params < other.params;
- }
-}
+std::ostream& operator<<(std::ostream&, TypeInfo);
+std::ostream& operator<<(std::ostream&, HeapTypeInfo);
std::ostream& operator<<(std::ostream& os, Type type) {
if (type.isBasic()) {
switch (type.getBasic()) {
case Type::none:
- os << "none";
- break;
+ return os << "none";
case Type::unreachable:
- os << "unreachable";
- break;
+ return os << "unreachable";
case Type::i32:
- os << "i32";
- break;
+ return os << "i32";
case Type::i64:
- os << "i64";
- break;
+ return os << "i64";
case Type::f32:
- os << "f32";
- break;
+ return os << "f32";
case Type::f64:
- os << "f64";
- break;
+ return os << "f64";
case Type::v128:
- os << "v128";
- break;
+ return os << "v128";
case Type::funcref:
- os << "funcref";
- break;
+ return os << "funcref";
case Type::externref:
- os << "externref";
- break;
+ return os << "externref";
case Type::exnref:
- os << "exnref";
- break;
+ return os << "exnref";
case Type::anyref:
- os << "anyref";
- break;
+ return os << "anyref";
case Type::eqref:
- os << "eqref";
- break;
+ return os << "eqref";
case Type::i31ref:
- os << "i31ref";
- break;
+ return os << "i31ref";
}
- } else {
- os << *getTypeInfo(type);
}
- return os;
+ return os << *getTypeInfo(type);
}
std::ostream& operator<<(std::ostream& os, ParamType param) {
@@ -943,27 +907,23 @@ std::ostream& operator<<(std::ostream& os, Array array) {
}
std::ostream& operator<<(std::ostream& os, HeapType heapType) {
- switch (heapType.kind) {
- case wasm::HeapType::FuncKind:
- return os << "func";
- case wasm::HeapType::ExternKind:
- return os << "extern";
- case wasm::HeapType::AnyKind:
- return os << "any";
- case wasm::HeapType::EqKind:
- return os << "eq";
- case wasm::HeapType::I31Kind:
- return os << "i31";
- case wasm::HeapType::ExnKind:
- return os << "exn";
- case wasm::HeapType::SignatureKind:
- return os << heapType.signature;
- case wasm::HeapType::StructKind:
- return os << heapType.struct_;
- case wasm::HeapType::ArrayKind:
- return os << heapType.array;
+ if (heapType.isBasic()) {
+ switch (heapType.getBasic()) {
+ case HeapType::func:
+ return os << "func";
+ case HeapType::ext:
+ return os << "extern";
+ case HeapType::exn:
+ return os << "exn";
+ case HeapType::any:
+ return os << "any";
+ case HeapType::eq:
+ return os << "eq";
+ case HeapType::i31:
+ return os << "i31";
+ }
}
- WASM_UNREACHABLE("unexpected kind");
+ return os << *getHeapTypeInfo(heapType);
}
std::ostream& operator<<(std::ostream& os, Rtt rtt) {
@@ -989,4 +949,117 @@ std::ostream& operator<<(std::ostream& os, TypeInfo info) {
WASM_UNREACHABLE("unexpected kind");
}
+std::ostream& operator<<(std::ostream& os, HeapTypeInfo info) {
+ switch (info.kind) {
+ case HeapTypeInfo::SignatureKind:
+ return os << info.signature;
+ case HeapTypeInfo::StructKind:
+ return os << info.struct_;
+ case HeapTypeInfo::ArrayKind:
+ return os << info.array;
+ }
+ WASM_UNREACHABLE("unexpected kind");
+}
+
} // namespace wasm
+
+namespace std {
+
+template<> class hash<wasm::TypeList> {
+public:
+ size_t operator()(const wasm::TypeList& types) const {
+ auto digest = wasm::hash(types.size());
+ for (auto type : types) {
+ wasm::rehash(digest, type);
+ }
+ return digest;
+ }
+};
+
+template<> class hash<wasm::FieldList> {
+public:
+ size_t operator()(const wasm::FieldList& fields) const {
+ auto digest = wasm::hash(fields.size());
+ for (auto field : fields) {
+ wasm::rehash(digest, field);
+ }
+ return digest;
+ }
+};
+
+size_t hash<wasm::TypeInfo>::operator()(const wasm::TypeInfo& info) const {
+ auto digest = wasm::hash(info.kind);
+ switch (info.kind) {
+ case wasm::TypeInfo::TupleKind:
+ wasm::rehash(digest, info.tuple);
+ return digest;
+ case wasm::TypeInfo::RefKind:
+ wasm::rehash(digest, info.ref.heapType);
+ wasm::rehash(digest, info.ref.nullable);
+ return digest;
+ case wasm::TypeInfo::RttKind:
+ wasm::rehash(digest, info.rtt);
+ return digest;
+ }
+ WASM_UNREACHABLE("unexpected kind");
+}
+
+size_t hash<wasm::HeapTypeInfo>::
+operator()(const wasm::HeapTypeInfo& info) const {
+ auto digest = wasm::hash(info.kind);
+ switch (info.kind) {
+ case wasm::HeapTypeInfo::SignatureKind:
+ wasm::rehash(digest, info.signature);
+ return digest;
+ case wasm::HeapTypeInfo::StructKind:
+ wasm::rehash(digest, info.struct_);
+ return digest;
+ case wasm::HeapTypeInfo::ArrayKind:
+ wasm::rehash(digest, info.array);
+ return digest;
+ }
+ WASM_UNREACHABLE("unexpected kind");
+}
+
+size_t hash<wasm::Type>::operator()(const wasm::Type& type) const {
+ return wasm::hash(type.getID());
+}
+
+size_t hash<wasm::Tuple>::operator()(const wasm::Tuple& tuple) const {
+ return wasm::hash(tuple.types);
+}
+
+size_t hash<wasm::Signature>::operator()(const wasm::Signature& sig) const {
+ auto digest = wasm::hash(sig.params);
+ wasm::rehash(digest, sig.results);
+ return digest;
+}
+
+size_t hash<wasm::Field>::operator()(const wasm::Field& field) const {
+ auto digest = wasm::hash(field.type);
+ wasm::rehash(digest, field.packedType);
+ wasm::rehash(digest, field.mutable_);
+ // Note that the name is not hashed here - it is pure metadata for printing
+ // purposes only.
+ return digest;
+}
+
+size_t hash<wasm::Struct>::operator()(const wasm::Struct& struct_) const {
+ return wasm::hash(struct_.fields);
+}
+
+size_t hash<wasm::Array>::operator()(const wasm::Array& array) const {
+ return wasm::hash(array.element);
+}
+
+size_t hash<wasm::HeapType>::operator()(const wasm::HeapType& heapType) const {
+ return wasm::hash(heapType.getID());
+}
+
+size_t hash<wasm::Rtt>::operator()(const wasm::Rtt& rtt) const {
+ auto digest = wasm::hash(rtt.depth);
+ wasm::rehash(digest, rtt.heapType);
+ return digest;
+}
+
+} // namespace std