diff options
-rw-r--r-- | src/ir/type-updating.cpp | 1 | ||||
-rw-r--r-- | src/passes/Print.cpp | 21 | ||||
-rw-r--r-- | src/passes/TypeMerging.cpp | 11 | ||||
-rw-r--r-- | src/passes/TypeSSA.cpp | 6 | ||||
-rw-r--r-- | src/tools/fuzzing/heap-types.cpp | 10 | ||||
-rw-r--r-- | src/tools/wasm-fuzz-types.cpp | 5 | ||||
-rw-r--r-- | src/wasm-binary.h | 9 | ||||
-rw-r--r-- | src/wasm-type.h | 7 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 39 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 17 | ||||
-rw-r--r-- | src/wasm/wasm-type.cpp | 90 | ||||
-rw-r--r-- | test/gtest/type-builder.cpp | 32 | ||||
-rw-r--r-- | test/lit/fuzz-types.test | 91 | ||||
-rw-r--r-- | test/lit/isorecursive-good.wast | 46 | ||||
-rw-r--r-- | test/lit/passes/type-merging.wast | 6 | ||||
-rw-r--r-- | test/lit/passes/type-ssa.wast | 59 | ||||
-rw-r--r-- | test/passes/translate-to-fuzz_all-features_metrics_noprint.txt | 78 |
17 files changed, 356 insertions, 172 deletions
diff --git a/src/ir/type-updating.cpp b/src/ir/type-updating.cpp index aa042d86e..e11705812 100644 --- a/src/ir/type-updating.cpp +++ b/src/ir/type-updating.cpp @@ -70,6 +70,7 @@ void GlobalTypeRewriter::update() { // Create the temporary heap types. i = 0; for (auto [type, _] : typeIndices) { + typeBuilder[i].setFinal(type.isFinal()); if (type.isSignature()) { auto sig = type.getSignature(); TypeList newParams, newResults; diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 5d49ad900..67d0dbdcc 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -3017,13 +3017,20 @@ struct PrintSExpression : public UnifiedExpressionVisitor<PrintSExpression> { o << ')'; } void handleHeapType(HeapType type) { - bool hasSuper = false; - // TODO: Consider finality once we support that. - if (auto super = type.getSuperType()) { - hasSuper = true; + auto super = type.getSuperType(); + bool useSub = false; + // TODO: Once we parse MVP signature types as final, use the MVP shorthand + // for final types without supertypes. + if (super || type.isFinal()) { + useSub = true; o << "(sub "; - TypeNamePrinter(o, currModule).print(*super); - o << ' '; + if (type.isFinal()) { + o << "final "; + } + if (super) { + TypeNamePrinter(o, currModule).print(*super); + o << ' '; + } } if (type.isSignature()) { handleSignature(type); @@ -3034,7 +3041,7 @@ struct PrintSExpression : public UnifiedExpressionVisitor<PrintSExpression> { } else { o << type; } - if (hasSuper) { + if (useSub) { o << ')'; } } diff --git a/src/passes/TypeMerging.cpp b/src/passes/TypeMerging.cpp index 1897adc3c..fa36eb767 100644 --- a/src/passes/TypeMerging.cpp +++ b/src/passes/TypeMerging.cpp @@ -467,6 +467,9 @@ bool shapeEq(HeapType a, HeapType b) { // Check whether `a` and `b` have the same top-level structure, including the // position and identity of any children that are not included as transitions // in the DFA, i.e. any children that are not nontrivial references. + if (a.isFinal() != b.isFinal()) { + return false; + } if (a.isStruct() && b.isStruct()) { return shapeEq(a.getStruct(), b.getStruct()); } @@ -480,15 +483,15 @@ bool shapeEq(HeapType a, HeapType b) { } size_t shapeHash(HeapType a) { - size_t digest; + size_t digest = hash(a.isFinal()); if (a.isStruct()) { - digest = hash(0); + rehash(digest, 0); hash_combine(digest, shapeHash(a.getStruct())); } else if (a.isArray()) { - digest = hash(1); + rehash(digest, 1); hash_combine(digest, shapeHash(a.getArray())); } else if (a.isSignature()) { - digest = hash(2); + rehash(digest, 2); hash_combine(digest, shapeHash(a.getSignature())); } else { WASM_UNREACHABLE("unexpected kind"); diff --git a/src/passes/TypeSSA.cpp b/src/passes/TypeSSA.cpp index 33433083f..25575e70e 100644 --- a/src/passes/TypeSSA.cpp +++ b/src/passes/TypeSSA.cpp @@ -109,6 +109,7 @@ std::vector<HeapType> ensureTypesAreInNewRecGroup(RecGroup recGroup, if (auto super = type.getSuperType()) { builder[i].subTypeOf(*super); } + builder[i].setFinal(type.isFinal()); } // Implement the hash as a struct with "random" fields, and add it. @@ -306,6 +307,11 @@ struct TypeSSA : public Pass { return false; } + if (curr->type.getHeapType().isFinal()) { + // We can't create new subtypes of a final type anyway. + return false; + } + // Look for at least one interesting operand. We will consider each operand // against the declared type, that is, the type declared for where it is // stored. If it has more information than the declared type then it is diff --git a/src/tools/fuzzing/heap-types.cpp b/src/tools/fuzzing/heap-types.cpp index 26cc518be..2f77c5765 100644 --- a/src/tools/fuzzing/heap-types.cpp +++ b/src/tools/fuzzing/heap-types.cpp @@ -85,6 +85,13 @@ struct HeapTypeGeneratorImpl { } } + // Types without nontrivial subtypes may be marked final. + for (Index i = 0; i < builder.size(); ++i) { + if (subtypeIndices[i].size() == 1 && rand.oneIn(2)) { + builder[i].setFinal(); + } + } + // Initialize the recursion groups. recGroupEnds.reserve(builder.size()); // Create isorecursive recursion groups. Choose an expected group size @@ -878,7 +885,7 @@ std::vector<HeapType> Inhabitator::build() { start += size; } - // Establish supertypes. + // Establish supertypes and finality. for (size_t i = 0; i < types.size(); ++i) { if (auto super = types[i].getSuperType()) { if (auto it = typeIndices.find(*super); it != typeIndices.end()) { @@ -887,6 +894,7 @@ std::vector<HeapType> Inhabitator::build() { builder[i].subTypeOf(*super); } } + builder[i].setFinal(types[i].isFinal()); } auto built = builder.build(); diff --git a/src/tools/wasm-fuzz-types.cpp b/src/tools/wasm-fuzz-types.cpp index 4f480ba9d..50f1e1e3c 100644 --- a/src/tools/wasm-fuzz-types.cpp +++ b/src/tools/wasm-fuzz-types.cpp @@ -262,6 +262,11 @@ void Fuzzer::checkCanonicalization() { } } + // Set finality + for (size_t i = 0; i < types.size(); ++i) { + builder[i].setFinal(types[i].isFinal()); + } + // Set up recursion groups and record group ends to ensure we only select // valid children. recGroupEnds.reserve(builder.size()); diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 745358cbc..c6b3e7b6f 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -384,10 +384,11 @@ enum EncodedType { nullfuncref = -0x18, // 0x68 nullref = -0x1b, // 0x65 // type forms - Func = -0x20, // 0x60 - Struct = -0x21, // 0x5f - Array = -0x22, // 0x5e - Sub = -0x30, // 0x50 + Func = -0x20, // 0x60 + Struct = -0x21, // 0x5f + Array = -0x22, // 0x5e + Sub = -0x30, // 0x50 + SubFinal = -0x32, // 0x4e // prototype nominal forms we still parse FuncSubtype = -0x23, // 0x5d StructSubtype = -0x24, // 0x5c diff --git a/src/wasm-type.h b/src/wasm-type.h index a8a331ace..323ef0207 100644 --- a/src/wasm-type.h +++ b/src/wasm-type.h @@ -359,6 +359,7 @@ public: bool isArray() const; bool isString() const; bool isBottom() const; + bool isFinal() const; Signature getSignature() const; const Struct& getStruct() const; @@ -584,6 +585,8 @@ struct TypeBuilder { // not overlap or go out of bounds. void createRecGroup(size_t i, size_t length); + void setFinal(size_t i, bool final = true); + enum class ErrorReason { // There is a cycle in the supertype relation. SelfSupertype, @@ -645,6 +648,10 @@ struct TypeBuilder { builder.setSubType(index, other); return *this; } + Entry& setFinal(bool final = true) { + builder.setFinal(index, final); + return *this; + } }; Entry operator[](size_t i) { return Entry{*this, i}; } diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index d9b7e1a84..639f4b848 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -236,6 +236,19 @@ void WasmBinaryWriter::writeTypes() { lastGroup = currGroup; } } + + // As a temporary measure, detect which types have subtypes and always use + // `sub` or `sub final` for these types. The standard says that types without + // `sub` or `sub final` are final, but we currently treat them as non-final. + // To avoid unsafe ambiguity, only use the short form for types that it would + // be safe to treat as final, i.e. types without subtypes. + std::vector<bool> hasSubtypes(indexedTypes.types.size()); + for (auto type : indexedTypes.types) { + if (auto super = type.getSuperType()) { + hasSubtypes[indexedTypes.indices[*super]] = true; + } + } + BYN_TRACE("== writeTypes\n"); auto start = startSection(BinaryConsts::Section::Type); o << U32LEB(numGroups); @@ -251,10 +264,21 @@ void WasmBinaryWriter::writeTypes() { lastGroup = currGroup; // Emit the type definition. BYN_TRACE("write " << type << std::endl); - if (auto super = type.getSuperType()) { - // Subtype constructor and vector of 1 supertype. - o << S32LEB(BinaryConsts::EncodedType::Sub) << U32LEB(1); - writeHeapType(*super); + auto super = type.getSuperType(); + // TODO: Use the binary shorthand for final types once we parse MVP + // signatures as final. + if (type.isFinal() || super || hasSubtypes[i]) { + if (type.isFinal()) { + o << S32LEB(BinaryConsts::EncodedType::SubFinal); + } else { + o << S32LEB(BinaryConsts::EncodedType::Sub); + } + if (super) { + o << U32LEB(1); + writeHeapType(*super); + } else { + o << U32LEB(0); + } } if (type.isSignature()) { o << S32LEB(BinaryConsts::EncodedType::Func); @@ -2210,7 +2234,12 @@ void WasmBinaryReader::readTypes() { form = getS32LEB(); } std::optional<uint32_t> superIndex; - if (form == BinaryConsts::EncodedType::Sub) { + if (form == BinaryConsts::EncodedType::Sub || + form == BinaryConsts::EncodedType::SubFinal) { + if (form == BinaryConsts::EncodedType::SubFinal) { + // TODO: Interpret type definitions without any `sub` as final as well. + builder[i].setFinal(); + } uint32_t supers = getU32LEB(); if (supers > 0) { if (supers != 1) { diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index e99200d58..9b7a1a90d 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -52,7 +52,8 @@ namespace wasm { static Name STRUCT("struct"), FIELD("field"), ARRAY("array"), FUNC_SUBTYPE("func_subtype"), STRUCT_SUBTYPE("struct_subtype"), ARRAY_SUBTYPE("array_subtype"), EXTENDS("extends"), REC("rec"), I8("i8"), - I16("i16"), DECLARE("declare"), ITEM("item"), OFFSET("offset"), SUB("sub"); + I16("i16"), DECLARE("declare"), ITEM("item"), OFFSET("offset"), SUB("sub"), + FINAL("final"); static Address getAddress(const Element* s) { return std::stoll(s->toString()); @@ -926,11 +927,19 @@ void SExpressionWasmBuilder::preParseHeapTypes(Element& module) { Element& kind = *def[0]; Element* super = nullptr; if (kind == SUB) { - if (def.size() != 3) { + Index i = 1; + if (*def[i] == FINAL) { + builder[index].setFinal(); + ++i; + } + if (def[i]->dollared()) { + super = def[i]; + ++i; + } + Element& subtype = *def[i++]; + if (i != def.size()) { throw ParseException("invalid 'sub' form", kind.line, kind.col); } - super = def[1]; - Element& subtype = *def[2]; if (!subtype.isList() || subtype.size() < 1) { throw ParseException( "invalid subtype definition", subtype.line, subtype.col); diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp index aa08c52c2..6baf0feb0 100644 --- a/src/wasm/wasm-type.cpp +++ b/src/wasm/wasm-type.cpp @@ -85,6 +85,7 @@ struct HeapTypeInfo { // Used in assertions to ensure that temporary types don't leak into the // global store. bool isTemp = false; + bool isFinal = false; // The supertype of this HeapType, if it exists. HeapTypeInfo* supertype = nullptr; // The recursion group of this type or null if the recursion group is trivial @@ -153,12 +154,9 @@ struct TypePrinter { std::ostream& print(HeapType type); std::ostream& print(const Tuple& tuple); std::ostream& print(const Field& field); - std::ostream& print(const Signature& sig, - std::optional<HeapType> super = std::nullopt); - std::ostream& print(const Struct& struct_, - std::optional<HeapType> super = std::nullopt); - std::ostream& print(const Array& array, - std::optional<HeapType> super = std::nullopt); + std::ostream& print(const Signature& sig); + std::ostream& print(const Struct& struct_); + std::ostream& print(const Array& array); }; struct RecGroupHasher { @@ -1156,6 +1154,14 @@ bool HeapType::isBottom() const { return false; } +bool HeapType::isFinal() const { + if (isBasic()) { + return false; + } else { + return getHeapTypeInfo(*this)->isFinal; + } +} + Signature HeapType::getSignature() const { assert(isSignature()); return getHeapTypeInfo(*this)->signature; @@ -1735,19 +1741,38 @@ std::ostream& TypePrinter::print(HeapType type) { if (isTemp(type)) { os << "(; temp ;) "; } + #if TRACE_CANONICALIZATION os << "(;" << ((type.getID() >> 4) % 1000) << ";)"; #endif + + // TODO: Use shorthand for final types once we parse MVP signatures as final. + bool useSub = false; + auto super = type.getSuperType(); + if (super || type.isFinal()) { + useSub = true; + os << "(sub "; + if (type.isFinal()) { + os << "final "; + } + if (super) { + printHeapTypeName(*super); + os << ' '; + } + } if (type.isSignature()) { - print(type.getSignature(), type.getSuperType()); + print(type.getSignature()); } else if (type.isStruct()) { - print(type.getStruct(), type.getSuperType()); + print(type.getStruct()); } else if (type.isArray()) { - print(type.getArray(), type.getSuperType()); + print(type.getArray()); } else { WASM_UNREACHABLE("unexpected type"); } - return os << ")"; + if (useSub) { + os << ')'; + } + return os << ')'; } std::ostream& TypePrinter::print(const Tuple& tuple) { @@ -1783,8 +1808,7 @@ std::ostream& TypePrinter::print(const Field& field) { return os; } -std::ostream& TypePrinter::print(const Signature& sig, - std::optional<HeapType> super) { +std::ostream& TypePrinter::print(const Signature& sig) { auto printPrefixed = [&](const char* prefix, Type type) { os << '(' << prefix; for (Type t : type) { @@ -1795,9 +1819,6 @@ std::ostream& TypePrinter::print(const Signature& sig, }; os << "(func"; - if (super) { - os << "_subtype"; - } if (sig.params.getID() != Type::none) { os << ' '; printPrefixed("param", sig.params); @@ -1806,19 +1827,11 @@ std::ostream& TypePrinter::print(const Signature& sig, os << ' '; printPrefixed("result", sig.results); } - if (super) { - os << ' '; - printHeapTypeName(*super); - } return os << ')'; } -std::ostream& TypePrinter::print(const Struct& struct_, - std::optional<HeapType> super) { +std::ostream& TypePrinter::print(const Struct& struct_) { os << "(struct"; - if (super) { - os << "_subtype"; - } if (struct_.fields.size()) { os << " (field"; } @@ -1829,25 +1842,12 @@ std::ostream& TypePrinter::print(const Struct& struct_, if (struct_.fields.size()) { os << ')'; } - if (super) { - os << ' '; - printHeapTypeName(*super); - } return os << ')'; } -std::ostream& TypePrinter::print(const Array& array, - std::optional<HeapType> super) { - os << "(array"; - if (super) { - os << "_subtype"; - } - os << ' '; +std::ostream& TypePrinter::print(const Array& array) { + os << "(array "; print(array.element); - if (super) { - os << ' '; - printHeapTypeName(*super); - } return os << ')'; } @@ -1916,6 +1916,7 @@ size_t RecGroupHasher::hash(const HeapTypeInfo& info) const { if (info.supertype) { hash_combine(digest, hash(HeapType(uintptr_t(info.supertype)))); } + wasm::rehash(digest, info.isFinal); wasm::rehash(digest, info.kind); switch (info.kind) { case HeapTypeInfo::SignatureKind: @@ -2038,6 +2039,9 @@ bool RecGroupEquator::eq(const HeapTypeInfo& a, const HeapTypeInfo& b) const { return false; } } + if (a.isFinal != b.isFinal) { + return false; + } if (a.kind != b.kind) { return false; } @@ -2291,9 +2295,17 @@ void TypeBuilder::createRecGroup(size_t index, size_t length) { {RecGroup(uintptr_t(groupInfo.get())), std::move(groupInfo)}); } +void TypeBuilder::setFinal(size_t i, bool final) { + assert(i < size() && "index out of bounds"); + impl->entries[i].info->isFinal = final; +} + namespace { bool isValidSupertype(const HeapTypeInfo& sub, const HeapTypeInfo& super) { + if (super.isFinal) { + return false; + } if (sub.kind != super.kind) { return false; } diff --git a/test/gtest/type-builder.cpp b/test/gtest/type-builder.cpp index 0744e2b5e..1c30d9cb9 100644 --- a/test/gtest/type-builder.cpp +++ b/test/gtest/type-builder.cpp @@ -256,6 +256,22 @@ TEST_F(TypeTest, InvalidSupertype) { EXPECT_EQ(error->index, 1u); } +TEST_F(TypeTest, InvalidFinalSupertype) { + TypeBuilder builder(2); + builder[0] = Struct{}; + builder[1] = Struct{}; + builder[0].setFinal(); + builder[1].subTypeOf(builder[0]); + + auto result = builder.build(); + EXPECT_FALSE(result); + + const auto* error = result.getError(); + ASSERT_TRUE(error); + EXPECT_EQ(error->reason, TypeBuilder::ErrorReason::InvalidSupertype); + EXPECT_EQ(error->index, 1u); +} + TEST_F(TypeTest, ForwardReferencedChild) { TypeBuilder builder(3); builder.createRecGroup(0, 2); @@ -434,6 +450,22 @@ TEST_F(TypeTest, CanonicalizeSupertypes) { EXPECT_NE(built[4], built[5]); } +TEST_F(TypeTest, CanonicalizeFinal) { + // Types are different if their finality flag is different. + TypeBuilder builder(2); + builder[0] = Struct{}; + builder[1] = Struct{}; + builder[0].setFinal(); + + auto result = builder.build(); + ASSERT_TRUE(result); + auto built = *result; + + EXPECT_NE(built[0], built[1]); + EXPECT_TRUE(built[0].isFinal()); + EXPECT_FALSE(built[1].isFinal()); +} + TEST_F(TypeTest, HeapTypeConstructors) { HeapType sig(Signature(Type::i32, Type::i32)); HeapType struct_(Struct({Field(Type(sig, Nullable), Mutable)})); diff --git a/test/lit/fuzz-types.test b/test/lit/fuzz-types.test index c1748e646..43a9947f2 100644 --- a/test/lit/fuzz-types.test +++ b/test/lit/fuzz-types.test @@ -1,66 +1,59 @@ -;; RUN: wasm-fuzz-types -v --seed=1 | filecheck %s +;; RUN: wasm-fuzz-types -v --seed=0 | filecheck %s -;; CHECK: (rec -;; CHECK-NEXT: (type $0 (struct (field (mut (ref $1)) f64 v128 (mut (ref null $1)) (mut (ref null $0)) (ref $1)))) -;; CHECK-NEXT: (type $1 (struct (field (mut v128) (mut (ref null $1)) (mut i8) (mut i16) (mut i16)))) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $2 (array (mut i16))) -;; CHECK-NEXT: (type $3 (func)) -;; CHECK-NEXT: ) +;; CHECK: Built 20 types: ;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $4 (func (param f32) (result f64))) -;; CHECK-NEXT: (type $5 (array v128)) -;; CHECK-NEXT: (type $6 (array (mut (ref null $3)))) -;; CHECK-NEXT: (type $7 (func (param v128) (result f32))) -;; CHECK-NEXT: (type $8 (struct_subtype (field (mut v128) (mut (ref null $1)) (mut i8) (mut i16) (mut i16) (mut i64)) $1)) +;; CHECK-NEXT: (type $0 (struct (field i32))) +;; CHECK-NEXT: (type $1 (func (param (ref $2)) (result externref))) +;; CHECK-NEXT: (type $2 (struct)) ;; CHECK-NEXT: ) -;; CHECK-NEXT: (type $9 (func_subtype (param v128) (result f32) $7)) -;; CHECK-NEXT: (type $10 (struct_subtype (field (mut (ref $1)) f64 v128 (mut (ref null $1)) (mut (ref null $0)) (ref $1)) $0)) ;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $11 (struct)) -;; CHECK-NEXT: (type $12 (struct (field (mut i32) (mut i32) v128))) +;; CHECK-NEXT: (type $3 (sub $0 (struct (field i32 (ref $5) (ref $5))))) +;; CHECK-NEXT: (type $4 (sub final $3 (struct (field i32 (ref $5) (ref $5) i8 (ref null $13) (mut i64))))) +;; CHECK-NEXT: (type $5 (array (mut f64))) +;; CHECK-NEXT: (type $6 (sub final $1 (func (param anyref) (result externref)))) +;; CHECK-NEXT: (type $7 (sub $2 (struct (field (mut (ref null $14)) (ref $3))))) +;; CHECK-NEXT: (type $8 (sub $1 (func (param (ref struct)) (result (ref extern))))) +;; CHECK-NEXT: (type $9 (sub $5 (array (mut f64)))) +;; CHECK-NEXT: (type $10 (sub $8 (func (param (ref any)) (result (ref noextern))))) +;; CHECK-NEXT: (type $11 (array (mut anyref))) +;; CHECK-NEXT: (type $12 (sub $2 (struct (field (mut f64))))) +;; CHECK-NEXT: (type $13 (sub $1 (func (param (ref $2)) (result (ref extern))))) +;; CHECK-NEXT: (type $14 (array (mut (ref null $14)))) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $13 (func (param (ref null $4)) (result f32))) -;; CHECK-NEXT: (type $14 (array_subtype (mut i16) $2)) -;; CHECK-NEXT: (type $15 (struct (field (mut v128) (ref extern) (mut (ref $15))))) -;; CHECK-NEXT: (type $16 (struct_subtype (field (mut v128) (ref extern) (mut (ref $15))) $15)) -;; CHECK-NEXT: (type $17 (struct_subtype (field (mut (ref $1)) f64 v128 (mut (ref null $1)) (mut (ref null $0)) (ref $1)) $0)) -;; CHECK-NEXT: (type $18 (func_subtype (param f32) (result f64) $4)) +;; CHECK-NEXT: (type $15 (sub $11 (array (mut anyref)))) +;; CHECK-NEXT: (type $16 (sub $12 (struct (field (mut f64))))) +;; CHECK-NEXT: (type $17 (sub final (struct (field f32 (mut i32) i8 (ref array))))) +;; CHECK-NEXT: (type $18 (sub final $11 (array (mut anyref)))) +;; CHECK-NEXT: (type $19 (sub $9 (array (mut f64)))) ;; CHECK-NEXT: ) -;; CHECK-NEXT: (type $19 (func (result v128))) ;; CHECK-NEXT: ;; CHECK-NEXT: Inhabitable types: ;; CHECK-NEXT: ;; CHECK-NEXT: Built 20 types: ;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $0 (struct (field (mut (ref $1)) f64 v128 (mut (ref null $1)) (mut (ref null $0)) (ref $1)))) -;; CHECK-NEXT: (type $1 (struct (field (mut v128) (mut (ref null $1)) (mut i8) (mut i16) (mut i16)))) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $2 (array (mut i16))) -;; CHECK-NEXT: (type $3 (func)) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $4 (func (param f32) (result f64))) -;; CHECK-NEXT: (type $5 (array v128)) -;; CHECK-NEXT: (type $6 (array (mut (ref null $3)))) -;; CHECK-NEXT: (type $7 (func (param v128) (result f32))) -;; CHECK-NEXT: (type $8 (struct_subtype (field (mut v128) (mut (ref null $1)) (mut i8) (mut i16) (mut i16) (mut i64)) $1)) +;; CHECK-NEXT: (type $0 (struct (field i32))) +;; CHECK-NEXT: (type $1 (func (param (ref $2)) (result externref))) +;; CHECK-NEXT: (type $2 (struct)) ;; CHECK-NEXT: ) -;; CHECK-NEXT: (type $9 (func_subtype (param v128) (result f32) $7)) -;; CHECK-NEXT: (type $10 (struct_subtype (field (mut (ref $1)) f64 v128 (mut (ref null $1)) (mut (ref null $0)) (ref $1)) $0)) ;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $11 (struct)) -;; CHECK-NEXT: (type $12 (struct (field (mut i32) (mut i32) v128))) +;; CHECK-NEXT: (type $3 (sub $0 (struct (field i32 (ref $5) (ref $5))))) +;; CHECK-NEXT: (type $4 (sub final $3 (struct (field i32 (ref $5) (ref $5) i8 (ref null $13) (mut i64))))) +;; CHECK-NEXT: (type $5 (array (mut f64))) +;; CHECK-NEXT: (type $6 (sub final $1 (func (param anyref) (result externref)))) +;; CHECK-NEXT: (type $7 (sub $2 (struct (field (mut (ref null $14)) (ref $3))))) +;; CHECK-NEXT: (type $8 (sub $1 (func (param (ref struct)) (result (ref extern))))) +;; CHECK-NEXT: (type $9 (sub $5 (array (mut f64)))) +;; CHECK-NEXT: (type $10 (sub $8 (func (param (ref any)) (result (ref noextern))))) +;; CHECK-NEXT: (type $11 (array (mut anyref))) +;; CHECK-NEXT: (type $12 (sub $2 (struct (field (mut f64))))) +;; CHECK-NEXT: (type $13 (sub $1 (func (param (ref $2)) (result (ref extern))))) +;; CHECK-NEXT: (type $14 (array (mut (ref null $14)))) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (rec -;; CHECK-NEXT: (type $13 (func (param (ref null $4)) (result f32))) -;; CHECK-NEXT: (type $14 (array_subtype (mut i16) $2)) -;; CHECK-NEXT: (type $15 (struct (field (mut v128) externref (mut (ref null $15))))) -;; CHECK-NEXT: (type $16 (struct_subtype (field (mut v128) externref (mut (ref null $15))) $15)) -;; CHECK-NEXT: (type $17 (struct_subtype (field (mut (ref $1)) f64 v128 (mut (ref null $1)) (mut (ref null $0)) (ref $1)) $0)) -;; CHECK-NEXT: (type $18 (func_subtype (param f32) (result f64) $4)) +;; CHECK-NEXT: (type $15 (sub $11 (array (mut anyref)))) +;; CHECK-NEXT: (type $16 (sub $12 (struct (field (mut f64))))) +;; CHECK-NEXT: (type $17 (sub final (struct (field f32 (mut i32) i8 (ref array))))) +;; CHECK-NEXT: (type $18 (sub final $11 (array (mut anyref)))) +;; CHECK-NEXT: (type $19 (sub $9 (array (mut f64)))) ;; CHECK-NEXT: ) -;; CHECK-NEXT: (type $19 (func (result v128))) diff --git a/test/lit/isorecursive-good.wast b/test/lit/isorecursive-good.wast index 8e15d3190..1d5e9c7df 100644 --- a/test/lit/isorecursive-good.wast +++ b/test/lit/isorecursive-good.wast @@ -7,66 +7,82 @@ (rec ;; CHECK: (rec ;; CHECK-NEXT: (type $super-struct (struct (field i32))) - (type $super-struct (struct i32)) + (type $super-struct (sub (struct i32))) ;; CHECK: (type $sub-struct (sub $super-struct (struct (field i32) (field i64)))) (type $sub-struct (sub $super-struct (struct i32 i64))) + ;; CHECK: (type $final-struct (sub final $sub-struct (struct (field i32) (field i64) (field f32)))) + (type $final-struct (sub final $sub-struct (struct i32 i64 f32))) ) (rec ;; CHECK: (rec ;; CHECK-NEXT: (type $super-array (array (ref $super-struct))) - (type $super-array (array (ref $super-struct))) + (type $super-array (sub (array (ref $super-struct)))) ;; CHECK: (type $sub-array (sub $super-array (array (ref $sub-struct)))) (type $sub-array (sub $super-array (array (ref $sub-struct)))) + ;; CHECK: (type $final-array (sub final $sub-array (array (ref $final-struct)))) + (type $final-array (sub final $sub-array (array (ref $final-struct)))) ) (rec ;; CHECK: (rec ;; CHECK-NEXT: (type $super-func (func (param (ref $sub-array)) (result (ref $super-array)))) - (type $super-func (func (param (ref $sub-array)) (result (ref $super-array)))) + (type $super-func (sub (func (param (ref $sub-array)) (result (ref $super-array))))) ;; CHECK: (type $sub-func (sub $super-func (func (param (ref $super-array)) (result (ref $sub-array))))) (type $sub-func (sub $super-func (func (param (ref $super-array)) (result (ref $sub-array))))) + ;; CHECK: (type $final-func (sub final $sub-func (func (param (ref $super-array)) (result (ref $final-array))))) + (type $final-func (sub final $sub-func (func (param (ref $super-array)) (result (ref $final-array))))) ) + ;; CHECK: (type $final-root (sub final (struct ))) + (type $final-root (sub final (struct))) + ;; CHECK: (func $make-super-struct (type $none_=>_ref|$super-struct|) (result (ref $super-struct)) - ;; CHECK-NEXT: (call $make-sub-struct) + ;; CHECK-NEXT: (call $make-final-struct) ;; CHECK-NEXT: ) (func $make-super-struct (result (ref $super-struct)) - (call $make-sub-struct) + (call $make-final-struct) ) - ;; CHECK: (func $make-sub-struct (type $none_=>_ref|$sub-struct|) (result (ref $sub-struct)) + ;; CHECK: (func $make-final-struct (type $none_=>_ref|$final-struct|) (result (ref $final-struct)) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) - (func $make-sub-struct (result (ref $sub-struct)) + (func $make-final-struct (result (ref $final-struct)) (unreachable) ) ;; CHECK: (func $make-super-array (type $none_=>_ref|$super-array|) (result (ref $super-array)) - ;; CHECK-NEXT: (call $make-sub-array) + ;; CHECK-NEXT: (call $make-final-array) ;; CHECK-NEXT: ) (func $make-super-array (result (ref $super-array)) - (call $make-sub-array) + (call $make-final-array) ) - ;; CHECK: (func $make-sub-array (type $none_=>_ref|$sub-array|) (result (ref $sub-array)) + ;; CHECK: (func $make-final-array (type $none_=>_ref|$final-array|) (result (ref $final-array)) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) - (func $make-sub-array (result (ref $sub-array)) + (func $make-final-array (result (ref $final-array)) (unreachable) ) ;; CHECK: (func $make-super-func (type $none_=>_ref|$super-func|) (result (ref $super-func)) - ;; CHECK-NEXT: (call $make-sub-func) + ;; CHECK-NEXT: (call $make-final-func) ;; CHECK-NEXT: ) (func $make-super-func (result (ref $super-func)) - (call $make-sub-func) + (call $make-final-func) + ) + + ;; CHECK: (func $make-final-func (type $none_=>_ref|$final-func|) (result (ref $final-func)) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $make-final-func (result (ref $final-func)) + (unreachable) ) - ;; CHECK: (func $make-sub-func (type $none_=>_ref|$sub-func|) (result (ref $sub-func)) + ;; CHECK: (func $make-final-root (type $none_=>_ref|$final-root|) (result (ref $final-root)) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) - (func $make-sub-func (result (ref $sub-func)) + (func $make-final-root (result (ref $final-root)) (unreachable) ) ) diff --git a/test/lit/passes/type-merging.wast b/test/lit/passes/type-merging.wast index 639810886..37d5337ac 100644 --- a/test/lit/passes/type-merging.wast +++ b/test/lit/passes/type-merging.wast @@ -7,6 +7,8 @@ ;; CHECK-NEXT: (type $A (struct (field anyref))) (type $A (struct_subtype (field anyref) data)) (type $B (struct_subtype (field anyref) $A)) + ;; CHECK: (type $G (sub final $A (struct (field anyref)))) + ;; CHECK: (type $F (sub $A (struct (field anyref)))) ;; CHECK: (type $E (sub $A (struct (field eqref)))) @@ -18,6 +20,7 @@ (type $D (struct_subtype (field (ref any)) $A)) (type $E (struct_subtype (field eqref) $A)) (type $F (struct_subtype (field anyref) $A)) + (type $G (sub final $A (struct (field anyref)))) ) ;; CHECK: (type $none_=>_none (func)) @@ -29,6 +32,7 @@ ;; CHECK-NEXT: (local $d (ref null $D)) ;; CHECK-NEXT: (local $e (ref null $E)) ;; CHECK-NEXT: (local $f (ref null $F)) + ;; CHECK-NEXT: (local $g (ref null $G)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (ref.cast null $A ;; CHECK-NEXT: (local.get $a) @@ -53,6 +57,8 @@ (local $e (ref null $E)) ;; $F cannot because it has a cast. (local $f (ref null $F)) + ;; $G cannot because it changes finality. + (local $g (ref null $G)) ;; A cast of $A has no effect. (drop diff --git a/test/lit/passes/type-ssa.wast b/test/lit/passes/type-ssa.wast index 1ed55ff7d..6980cb46e 100644 --- a/test/lit/passes/type-ssa.wast +++ b/test/lit/passes/type-ssa.wast @@ -74,6 +74,65 @@ ) ) +;; The same module as before, except that now the type is final, so we cannot +;; create any subtypes. +(module + ;; CHECK: (type $struct (sub final (struct (field i32)))) + (type $struct (sub final (struct (field i32)))) + + ;; CHECK: (type $none_=>_none (func)) + + ;; CHECK: (global $g (ref $struct) (struct.new $struct + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: )) + (global $g (ref $struct) (struct.new $struct + (i32.const 42) + )) + + ;; CHECK: (global $h (ref $struct) (struct.new $struct + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: )) + (global $h (ref $struct) (struct.new $struct + (i32.const 42) + )) + + ;; CHECK: (func $foo (type $none_=>_none) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new_default $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $struct + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $foo + (drop + (struct.new_default $struct) + ) + (drop + (struct.new $struct + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $another-func (type $none_=>_none) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $struct + ;; CHECK-NEXT: (i32.const 100) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $another-func + (drop + (struct.new $struct + (i32.const 100) + ) + ) + ) +) + ;; Some of these are uninteresting and should not get a new type. (module diff --git a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt index b5efc4c85..11da6a35b 100644 --- a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt +++ b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt @@ -1,50 +1,40 @@ total - [exports] : 9 - [funcs] : 16 - [globals] : 16 + [exports] : 3 + [funcs] : 6 + [globals] : 1 [imports] : 5 [memories] : 1 [memory-data] : 20 - [table-data] : 2 + [table-data] : 0 [tables] : 1 - [tags] : 1 - [total] : 666 - [vars] : 32 - ArrayFill : 1 - ArrayLen : 1 - ArrayNew : 7 - ArrayNewFixed : 3 - AtomicCmpxchg : 1 - AtomicFence : 2 - AtomicNotify : 1 - Binary : 81 - Block : 84 - Break : 8 - Call : 23 - Const : 152 - Drop : 6 - GlobalGet : 40 - GlobalSet : 38 - I31Get : 1 - I31New : 3 - If : 29 - Load : 19 - LocalGet : 39 - LocalSet : 20 - Loop : 5 - Nop : 9 - RefAs : 5 - RefEq : 2 - RefFunc : 6 - RefIsNull : 2 - RefNull : 9 + [tags] : 2 + [total] : 463 + [vars] : 19 + ArrayCopy : 1 + ArrayLen : 6 + ArrayNew : 8 + ArraySet : 1 + AtomicFence : 1 + Binary : 79 + Block : 49 + Break : 5 + Call : 7 + CallRef : 2 + Const : 96 + Drop : 2 + GlobalGet : 20 + GlobalSet : 20 + If : 17 + Load : 18 + LocalGet : 44 + LocalSet : 34 + Loop : 6 + Nop : 6 + RefAs : 3 + RefFunc : 2 + RefNull : 3 Return : 4 - Select : 1 - Store : 4 - StructGet : 1 - StructNew : 10 - Try : 2 - TupleExtract : 1 - TupleMake : 3 - Unary : 23 - Unreachable : 20 + Store : 2 + StructNew : 1 + Unary : 14 + Unreachable : 12 |