diff --git a/scripts/test/fuzzing.py b/scripts/test/fuzzing.py index bc0d206ea76..7f8897835ea 100644 --- a/scripts/test/fuzzing.py +++ b/scripts/test/fuzzing.py @@ -116,6 +116,8 @@ 'vacuum-removable-if-unused.wast', 'vacuum-removable-if-unused-func.wast', 'strip-toolchain-annotations-func.wast', + # Not fully implemented. + 'waitqueue.wast', ] diff --git a/src/ir/possible-constant.h b/src/ir/possible-constant.h index a75dd76299a..95acb6f9bca 100644 --- a/src/ir/possible-constant.h +++ b/src/ir/possible-constant.h @@ -120,8 +120,12 @@ struct PossibleConstantValues { value = val.and_(Literal(uint32_t(0xffff))); } break; + case Field::WaitQueue: + WASM_UNREACHABLE("waitqueue not implemented"); + break; case Field::not_packed: WASM_UNREACHABLE("unexpected packed type"); + break; } } diff --git a/src/parser/contexts.h b/src/parser/contexts.h index 5ab172224e9..3bd58aa3100 100644 --- a/src/parser/contexts.h +++ b/src/parser/contexts.h @@ -149,6 +149,7 @@ struct NullTypeParserCtx { StorageT makeI8() { return Ok{}; } StorageT makeI16() { return Ok{}; } + StorageT makeWaitQueue() { return Ok{}; } StorageT makeStorageType(TypeT) { return Ok{}; } FieldT makeFieldType(StorageT, Mutability) { return Ok{}; } @@ -308,6 +309,7 @@ template struct TypeParserCtx { StorageT makeI8() { return Field(Field::i8, Immutable); } StorageT makeI16() { return Field(Field::i16, Immutable); } + StorageT makeWaitQueue() { return Field(Field::WaitQueue, Immutable); } StorageT makeStorageType(TypeT type) { return Field(type, Immutable); } FieldT makeFieldType(FieldT field, Mutability mutability) { diff --git a/src/parser/parsers.h b/src/parser/parsers.h index d606563f1c1..2a3a5ec5a4e 100644 --- a/src/parser/parsers.h +++ b/src/parser/parsers.h @@ -717,6 +717,10 @@ template Result storagetype(Ctx& ctx) { if (ctx.in.takeKeyword("i16"sv)) { return ctx.makeI16(); } + if (ctx.in.takeKeyword("waitqueue"sv)) { + return ctx.makeWaitQueue(); + } + auto type = valtype(ctx); CHECK_ERR(type); return ctx.makeStorageType(*type); diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 125040aa2d2..52a6ad9dfeb 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -369,8 +369,9 @@ enum EncodedType { f64 = -0x4, // 0x7c v128 = -0x5, // 0x7b // packed types - i8 = -0x8, // 0x78 - i16 = -0x9, // 0x77 + i8 = -0x8, // 0x78 + i16 = -0x9, // 0x77 + waitQueue = -0x24, // 0x5c // reference types nullfuncref = -0xd, // 0x73 nullexternref = -0xe, // 0x72 diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 20578e03089..c2dff5ea34b 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -2752,6 +2752,10 @@ class ExpressionRunner : public OverriddenVisitor { memcpy(&i, p, sizeof(i)); return truncateForPacking(Literal(int32_t(i)), field); } + case Field::WaitQueue: { + WASM_UNREACHABLE("waitqueue not implemented"); + break; + } } WASM_UNREACHABLE("unexpected type"); } diff --git a/src/wasm-type.h b/src/wasm-type.h index b7165de5307..2aefa64a364 100644 --- a/src/wasm-type.h +++ b/src/wasm-type.h @@ -701,6 +701,7 @@ struct Field { not_packed, i8, i16, + WaitQueue, } packedType; // applicable iff type=i32 Mutability mutable_; @@ -1108,6 +1109,139 @@ inline bool HeapType::isBottom() const { return false; } +struct HeapTypeInfo { + using type_t = HeapType; + // Used in assertions to ensure that temporary types don't leak into the + // global store. + bool isTemp = false; + bool isOpen = false; + Shareability share = Unshared; + // The supertype of this HeapType, if it exists. + HeapTypeInfo* supertype = nullptr; + // The descriptor of this HeapType, if it exists. + HeapTypeInfo* descriptor = nullptr; + // The HeapType described by this one, if it exists. + HeapTypeInfo* described = nullptr; + // The recursion group of this type or null if the recursion group is trivial + // (i.e. contains only this type). + std::vector* recGroup = nullptr; + size_t recGroupIndex = 0; + HeapTypeKind kind; + union { + Signature signature; + Continuation continuation; + Struct struct_; + Array array; + }; + + HeapTypeInfo(Signature sig) : kind(HeapTypeKind::Func), signature(sig) {} + HeapTypeInfo(Continuation continuation) + : kind(HeapTypeKind::Cont), continuation(continuation) {} + HeapTypeInfo(const Struct& struct_) + : kind(HeapTypeKind::Struct), struct_(struct_) {} + HeapTypeInfo(Struct&& struct_) + : kind(HeapTypeKind::Struct), struct_(std::move(struct_)) {} + HeapTypeInfo(Array array) : kind(HeapTypeKind::Array), array(array) {} + ~HeapTypeInfo(); + + constexpr bool isSignature() const { return kind == HeapTypeKind::Func; } + constexpr bool isContinuation() const { return kind == HeapTypeKind::Cont; } + constexpr bool isStruct() const { return kind == HeapTypeKind::Struct; } + constexpr bool isArray() const { return kind == HeapTypeKind::Array; } + constexpr bool isData() const { return isStruct() || isArray(); } +}; + +template struct TypeGraphWalkerBase { + void walkRoot(Type* type) { + assert(taskList.empty()); + taskList.push_back(Task::scan(type)); + doWalk(); + } + + void walkRoot(HeapType* ht) { + assert(taskList.empty()); + taskList.push_back(Task::scan(ht)); + doWalk(); + } + +protected: + Self& self() { return *static_cast(this); } + + void scanType(Type* type) { + if (type->isTuple()) { + auto& types = const_cast(type->getTuple()); + for (auto it = types.rbegin(); it != types.rend(); ++it) { + taskList.push_back(Task::scan(&*it)); + } + } + } + + void scanHeapType(HeapType* ht) { + if (ht->isBasic()) { + return; + } + assert(!ht->isBasic()); + auto* info = reinterpret_cast(ht->getID()); + + switch (info->kind) { + case HeapTypeKind::Func: + taskList.push_back(Task::scan(&info->signature.results)); + taskList.push_back(Task::scan(&info->signature.params)); + break; + case HeapTypeKind::Cont: + taskList.push_back(Task::scan(&info->continuation.type)); + break; + case HeapTypeKind::Struct: { + auto& fields = info->struct_.fields; + for (auto field = fields.rbegin(); field != fields.rend(); ++field) { + taskList.push_back(Task::scan(&field->type)); + } + break; + } + case HeapTypeKind::Array: + taskList.push_back(Task::scan(&info->array.element.type)); + break; + case HeapTypeKind::Basic: + WASM_UNREACHABLE("unexpected kind"); + } + } + +private: + struct Task { + enum Kind { + ScanType, + ScanHeapType, + } kind; + union { + Type* type; + HeapType* heapType; + }; + static Task scan(Type* type) { return Task(type, ScanType); } + static Task scan(HeapType* ht) { return Task(ht, ScanHeapType); } + + private: + Task(Type* type, Kind kind) : kind(kind), type(type) {} + Task(HeapType* ht, Kind kind) : kind(kind), heapType(ht) {} + }; + + std::vector taskList; + + void doWalk() { + while (!taskList.empty()) { + auto curr = taskList.back(); + taskList.pop_back(); + switch (curr.kind) { + case Task::ScanType: + self().scanType(curr.type); + break; + case Task::ScanHeapType: + self().scanHeapType(curr.heapType); + break; + } + } + } +}; + } // namespace wasm namespace std { diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 23077d236a9..95aad7ba501 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -2008,6 +2008,8 @@ void WasmBinaryWriter::writeField(const Field& field) { o << S32LEB(BinaryConsts::EncodedType::i8); } else if (field.packedType == Field::i16) { o << S32LEB(BinaryConsts::EncodedType::i16); + } else if (field.packedType == Field::WaitQueue) { + o << S32LEB(BinaryConsts::EncodedType::waitQueue); } else { WASM_UNREACHABLE("invalid packed type"); } @@ -2733,6 +2735,10 @@ void WasmBinaryReader::readTypes() { auto mutable_ = readMutability(); return Field(Field::i16, mutable_); } + if (typeCode == BinaryConsts::EncodedType::waitQueue) { + auto mutable_ = readMutability(); + return Field(Field::WaitQueue, mutable_); + } // It's a regular wasm value. auto type = makeType(typeCode); auto mutable_ = readMutability(); diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp index 4f229ab2b92..b60f2842898 100644 --- a/src/wasm/wasm-type.cpp +++ b/src/wasm/wasm-type.cpp @@ -38,48 +38,6 @@ namespace { using RecGroupInfo = std::vector; -struct HeapTypeInfo { - using type_t = HeapType; - // Used in assertions to ensure that temporary types don't leak into the - // global store. - bool isTemp = false; - bool isOpen = false; - Shareability share = Unshared; - // The supertype of this HeapType, if it exists. - HeapTypeInfo* supertype = nullptr; - // The descriptor of this HeapType, if it exists. - HeapTypeInfo* descriptor = nullptr; - // The HeapType described by this one, if it exists. - HeapTypeInfo* described = nullptr; - // The recursion group of this type or null if the recursion group is trivial - // (i.e. contains only this type). - RecGroupInfo* recGroup = nullptr; - size_t recGroupIndex = 0; - HeapTypeKind kind; - union { - Signature signature; - Continuation continuation; - Struct struct_; - Array array; - }; - - HeapTypeInfo(Signature sig) : kind(HeapTypeKind::Func), signature(sig) {} - HeapTypeInfo(Continuation continuation) - : kind(HeapTypeKind::Cont), continuation(continuation) {} - HeapTypeInfo(const Struct& struct_) - : kind(HeapTypeKind::Struct), struct_(struct_) {} - HeapTypeInfo(Struct&& struct_) - : kind(HeapTypeKind::Struct), struct_(std::move(struct_)) {} - HeapTypeInfo(Array array) : kind(HeapTypeKind::Array), array(array) {} - ~HeapTypeInfo(); - - constexpr bool isSignature() const { return kind == HeapTypeKind::Func; } - constexpr bool isContinuation() const { return kind == HeapTypeKind::Cont; } - constexpr bool isStruct() const { return kind == HeapTypeKind::Struct; } - constexpr bool isArray() const { return kind == HeapTypeKind::Array; } - constexpr bool isData() const { return isStruct() || isArray(); } -}; - // Helper for coinductively checking whether a pair of Types or HeapTypes are in // a subtype relation. struct SubTyper { @@ -223,7 +181,7 @@ namespace { HeapTypeInfo* getHeapTypeInfo(HeapType ht) { assert(!ht.isBasic()); - return (HeapTypeInfo*)ht.getID(); + return reinterpret_cast(ht.getID()); } HeapType asHeapType(std::unique_ptr& info) { @@ -242,95 +200,6 @@ bool isTemp(HeapType type) { // from reference types to the referenced heap types are not walked, so // subclasses should handle referenced heap types when their reference types are // visited. -template struct TypeGraphWalkerBase { - void walkRoot(Type* type) { - assert(taskList.empty()); - taskList.push_back(Task::scan(type)); - doWalk(); - } - - void walkRoot(HeapType* ht) { - assert(taskList.empty()); - taskList.push_back(Task::scan(ht)); - doWalk(); - } - -protected: - Self& self() { return *static_cast(this); } - - void scanType(Type* type) { - if (type->isTuple()) { - auto& types = const_cast(type->getTuple()); - for (auto it = types.rbegin(); it != types.rend(); ++it) { - taskList.push_back(Task::scan(&*it)); - } - } - } - - void scanHeapType(HeapType* ht) { - if (ht->isBasic()) { - return; - } - auto* info = getHeapTypeInfo(*ht); - switch (info->kind) { - case HeapTypeKind::Func: - taskList.push_back(Task::scan(&info->signature.results)); - taskList.push_back(Task::scan(&info->signature.params)); - break; - case HeapTypeKind::Cont: - taskList.push_back(Task::scan(&info->continuation.type)); - break; - case HeapTypeKind::Struct: { - auto& fields = info->struct_.fields; - for (auto field = fields.rbegin(); field != fields.rend(); ++field) { - taskList.push_back(Task::scan(&field->type)); - } - break; - } - case HeapTypeKind::Array: - taskList.push_back(Task::scan(&info->array.element.type)); - break; - case HeapTypeKind::Basic: - WASM_UNREACHABLE("unexpected kind"); - } - } - -private: - struct Task { - enum Kind { - ScanType, - ScanHeapType, - } kind; - union { - Type* type; - HeapType* heapType; - }; - static Task scan(Type* type) { return Task(type, ScanType); } - static Task scan(HeapType* ht) { return Task(ht, ScanHeapType); } - - private: - Task(Type* type, Kind kind) : kind(kind), type(type) {} - Task(HeapType* ht, Kind kind) : kind(kind), heapType(ht) {} - }; - - std::vector taskList; - - void doWalk() { - while (!taskList.empty()) { - auto curr = taskList.back(); - taskList.pop_back(); - switch (curr.kind) { - case Task::ScanType: - self().scanType(curr.type); - break; - case Task::ScanHeapType: - self().scanHeapType(curr.heapType); - break; - } - } - } -}; - // A type graph walker that scans each each direct HeapType child of the root. template struct HeapTypeChildWalker : TypeGraphWalkerBase { void scanType(Type* type) { @@ -1485,6 +1354,9 @@ unsigned Field::getByteSize() const { return 2; case Field::PackedType::not_packed: return 4; + case Field::PackedType::WaitQueue: + WASM_UNREACHABLE("waitqueue not implemented"); + break; } WASM_UNREACHABLE("impossible packed type"); } @@ -1876,6 +1748,8 @@ std::ostream& TypePrinter::print(const Field& field) { os << "i8"; } else if (packedType == Field::PackedType::i16) { os << "i16"; + } else if (packedType == Field::PackedType::WaitQueue) { + os << "waitqueue"; } else { WASM_UNREACHABLE("unexpected packed type"); } diff --git a/test/lit/waitqueue.wast b/test/lit/waitqueue.wast new file mode 100644 index 00000000000..1a3e9726674 --- /dev/null +++ b/test/lit/waitqueue.wast @@ -0,0 +1,9 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. +;; RUN: wasm-opt %s -all --roundtrip -S -o - | filecheck %s --check-prefix=RTRIP +(module + ;; RTRIP: (type $t (struct (field waitqueue))) + (type $t (struct (field waitqueue))) + + ;; use $t so roundtripping doesn't drop the definition + (global (ref null $t) (ref.null $t)) +)