diff options
-rw-r--r-- | src/binaryen-c.cpp | 2 | ||||
-rw-r--r-- | src/passes/Print.cpp | 15 | ||||
-rw-r--r-- | src/tools/fuzzing/fuzzing.cpp | 31 | ||||
-rw-r--r-- | src/tools/fuzzing/heap-types.cpp | 6 | ||||
-rw-r--r-- | src/wasm-binary.h | 2 | ||||
-rw-r--r-- | src/wasm-type.h | 1 | ||||
-rw-r--r-- | src/wasm/literal.cpp | 2 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 16 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 10 | ||||
-rw-r--r-- | src/wasm/wasm-stack.cpp | 7 | ||||
-rw-r--r-- | src/wasm/wasm-type.cpp | 45 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 34 | ||||
-rw-r--r-- | test/example/c-api-kitchen-sink.txt | 8 | ||||
-rw-r--r-- | test/gtest/type-builder.cpp | 24 | ||||
-rw-r--r-- | test/heap-types.wast.from-wast | 5 | ||||
-rw-r--r-- | test/lit/arrays.wast | 72 | ||||
-rw-r--r-- | test/passes/translate-to-fuzz_all-features_metrics_noprint.txt | 70 |
17 files changed, 261 insertions, 89 deletions
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index dccc52131..ec70baef2 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -86,6 +86,7 @@ BinaryenLiteral toBinaryenLiteral(Literal x) { case HeapType::eq: case HeapType::func: case HeapType::data: + case HeapType::array: WASM_UNREACHABLE("invalid type"); case HeapType::string: case HeapType::stringview_wtf8: @@ -138,6 +139,7 @@ Literal fromBinaryenLiteral(BinaryenLiteral x) { case HeapType::eq: case HeapType::func: case HeapType::data: + case HeapType::array: WASM_UNREACHABLE("invalid type"); case HeapType::string: case HeapType::stringview_wtf8: diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index ddd8f9cc1..0f4a314ae 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -108,6 +108,9 @@ bool maybePrintRefShorthand(std::ostream& o, Type type) { case HeapType::data: o << "dataref"; return true; + case HeapType::array: + o << "arrayref"; + return true; case HeapType::string: o << "stringref"; return true; @@ -2234,11 +2237,12 @@ struct PrintExpressionContents TypeNamePrinter(o, wasm).print(curr->ref->type.getHeapType()); } void visitArrayLen(ArrayLen* curr) { - if (printUnreachableOrNullReplacement(curr->ref)) { - return; - } printMedium(o, "array.len "); - TypeNamePrinter(o, wasm).print(curr->ref->type.getHeapType()); + if (curr->ref->type == Type::unreachable) { + TypeNamePrinter(o, wasm).print(HeapType::array); + } else { + TypeNamePrinter(o, wasm).print(curr->ref->type.getHeapType()); + } } void visitArrayCopy(ArrayCopy* curr) { if (printUnreachableOrNullReplacement(curr->srcRef) || @@ -2801,9 +2805,6 @@ struct PrintSExpression : public UnifiedExpressionVisitor<PrintSExpression> { void visitArrayGet(ArrayGet* curr) { maybePrintUnreachableOrNullReplacement(curr, curr->ref->type); } - void visitArrayLen(ArrayLen* curr) { - maybePrintUnreachableOrNullReplacement(curr, curr->ref->type); - } // Module-level visitors void printSupertypeOr(HeapType curr, std::string noSuper) { if (auto super = curr.getSuperType()) { diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index 8023ab4da..cda14c989 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -2069,7 +2069,7 @@ Expression* TranslateToFuzzReader::makeConstBasicRef(Type type) { } return builder.makeI31New(makeConst(Type::i32)); } - case HeapType::data: { + case HeapType::data: assert(wasm.features.hasGC()); // TODO: Construct nontrivial types. For now just create a hard coded // struct or array. @@ -2078,13 +2078,14 @@ Expression* TranslateToFuzzReader::makeConstBasicRef(Type type) { // --nominal mode. static HeapType trivialStruct = HeapType(Struct()); return builder.makeStructNew(trivialStruct, std::vector<Expression*>{}); - } else { - // Use a local static to avoid creating a fresh nominal types in - // --nominal mode. - static HeapType trivialArray = - HeapType(Array(Field(Field::PackedType::i8, Immutable))); - return builder.makeArrayInit(trivialArray, {}); } + [[fallthrough]]; + case HeapType::array: { + // Use a local static to avoid creating a fresh nominal types in + // --nominal mode. + static HeapType trivialArray = + HeapType(Array(Field(Field::PackedType::i8, Immutable))); + return builder.makeArrayInit(trivialArray, {}); } case HeapType::string: case HeapType::stringview_wtf8: @@ -3052,7 +3053,9 @@ Type TranslateToFuzzReader::getSingleConcreteType() { Type(HeapType::i31, Nullable), // Type(HeapType::i31, NonNullable), Type(HeapType::data, Nullable), - Type(HeapType::data, NonNullable))); + Type(HeapType::data, NonNullable), + Type(HeapType::array, Nullable), + Type(HeapType::array, NonNullable))); } Type TranslateToFuzzReader::getReferenceType() { @@ -3171,18 +3174,24 @@ HeapType TranslateToFuzzReader::getSubType(HeapType type) { HeapType::eq, HeapType::i31, HeapType::data, + HeapType::array, HeapType::none); case HeapType::eq: // TODO: nontrivial types as well. assert(wasm.features.hasReferenceTypes()); assert(wasm.features.hasGC()); - return pick( - HeapType::eq, HeapType::i31, HeapType::data, HeapType::none); + return pick(HeapType::eq, + HeapType::i31, + HeapType::data, + HeapType::array, + HeapType::none); case HeapType::i31: return pick(HeapType::i31, HeapType::none); case HeapType::data: // TODO: nontrivial types as well. - return pick(HeapType::data, HeapType::none); + return pick(HeapType::data, HeapType::array, HeapType::none); + case HeapType::array: + return pick(HeapType::array, HeapType::none); case HeapType::string: case HeapType::stringview_wtf8: case HeapType::stringview_wtf16: diff --git a/src/tools/fuzzing/heap-types.cpp b/src/tools/fuzzing/heap-types.cpp index 351035f93..e93aae56c 100644 --- a/src/tools/fuzzing/heap-types.cpp +++ b/src/tools/fuzzing/heap-types.cpp @@ -152,7 +152,7 @@ struct HeapTypeGeneratorImpl { if (rand.oneIn(16)) { return rand.pick(HeapType::noext, HeapType::nofunc, HeapType::none); } - // TODO: strings + // TODO: strings and array return rand.pick(HeapType::func, HeapType::ext, HeapType::any, @@ -326,6 +326,8 @@ struct HeapTypeGeneratorImpl { return HeapType::i31; case HeapType::data: return pickSubData(); + case HeapType::array: + WASM_UNREACHABLE("TODO: fuzz array"); case HeapType::string: case HeapType::stringview_wtf8: case HeapType::stringview_wtf16: @@ -463,6 +465,8 @@ struct HeapTypeGeneratorImpl { return DataKind{}; case HeapType::data: return DataKind{}; + case HeapType::array: + WASM_UNREACHABLE("TODO: fuzz array"); case HeapType::string: case HeapType::stringview_wtf8: case HeapType::stringview_wtf16: diff --git a/src/wasm-binary.h b/src/wasm-binary.h index e85d67be2..709c0c9f9 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -378,6 +378,7 @@ enum EncodedType { i31ref = -0x16, // 0x6a // gc and string reference types dataref = -0x19, // 0x67 + arrayref = -0x1a, // 0x66 stringref = -0x1c, // 0x64 stringview_wtf8 = -0x1d, // 0x63 stringview_wtf16 = -0x1e, // 0x62 @@ -408,6 +409,7 @@ enum EncodedHeapType { eq = -0x13, // 0x6d i31 = -0x16, // 0x6a data = -0x19, // 0x67 + array = -0x1a, // 0x66 string = -0x1c, // 0x64 // stringview/iter constants are identical to type, and cannot be duplicated // here as that would be a compiler error, so add _heap suffixes. See diff --git a/src/wasm-type.h b/src/wasm-type.h index 36c2cbede..dbf99d512 100644 --- a/src/wasm-type.h +++ b/src/wasm-type.h @@ -325,6 +325,7 @@ public: eq, i31, data, + array, string, stringview_wtf8, stringview_wtf16, diff --git a/src/wasm/literal.cpp b/src/wasm/literal.cpp index 09022eea0..8d60a4829 100644 --- a/src/wasm/literal.cpp +++ b/src/wasm/literal.cpp @@ -126,6 +126,7 @@ Literal::Literal(const Literal& other) : type(other.type) { case HeapType::eq: case HeapType::func: case HeapType::data: + case HeapType::array: WASM_UNREACHABLE("invalid type"); case HeapType::string: case HeapType::stringview_wtf8: @@ -523,6 +524,7 @@ std::ostream& operator<<(std::ostream& o, Literal literal) { case HeapType::eq: case HeapType::func: case HeapType::data: + case HeapType::array: WASM_UNREACHABLE("invalid type"); case HeapType::string: case HeapType::stringview_wtf8: diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 5d21f5130..13c28f151 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -1414,6 +1414,9 @@ void WasmBinaryWriter::writeType(Type type) { case HeapType::data: o << S32LEB(BinaryConsts::EncodedType::dataref); return; + case HeapType::array: + o << S32LEB(BinaryConsts::EncodedType::arrayref); + return; case HeapType::string: o << S32LEB(BinaryConsts::EncodedType::stringref); return; @@ -1520,6 +1523,9 @@ void WasmBinaryWriter::writeHeapType(HeapType type) { case HeapType::data: ret = BinaryConsts::EncodedHeapType::data; break; + case HeapType::array: + ret = BinaryConsts::EncodedHeapType::array; + break; case HeapType::string: ret = BinaryConsts::EncodedHeapType::string; break; @@ -1887,6 +1893,9 @@ bool WasmBinaryBuilder::getBasicType(int32_t code, Type& out) { case BinaryConsts::EncodedType::dataref: out = Type(HeapType::data, Nullable); return true; + case BinaryConsts::EncodedType::arrayref: + out = Type(HeapType::array, Nullable); + return true; case BinaryConsts::EncodedType::stringref: out = Type(HeapType::string, Nullable); return true; @@ -1933,6 +1942,9 @@ bool WasmBinaryBuilder::getBasicHeapType(int64_t code, HeapType& out) { case BinaryConsts::EncodedHeapType::data: out = HeapType::data; return true; + case BinaryConsts::EncodedHeapType::array: + out = HeapType::array; + return true; case BinaryConsts::EncodedHeapType::string: out = HeapType::string; return true; @@ -7102,9 +7114,9 @@ bool WasmBinaryBuilder::maybeVisitArrayLen(Expression*& out, uint32_t code) { if (code != BinaryConsts::ArrayLen) { return false; } - auto heapType = getIndexedHeapType(); + // Ignore the type annotation and don't bother validating it. + getU32LEB(); auto* ref = popNonVoidExpression(); - validateHeapTypeUsingChild(ref, heapType); out = Builder(wasm).makeArrayLen(ref); return true; } diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 91d4c129c..41a0619f9 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -1194,6 +1194,9 @@ Type SExpressionWasmBuilder::stringToType(std::string_view str, (str.substr(0, 9) == "structref" && (prefix || str.size() == 9))) { return Type(HeapType::data, Nullable); } + if (str.substr(0, 8) == "arrayref" && (prefix || str.size() == 8)) { + return Type(HeapType::array, Nullable); + } if (str.substr(0, 9) == "stringref" && (prefix || str.size() == 9)) { return Type(HeapType::string, Nullable); } @@ -1243,6 +1246,9 @@ HeapType SExpressionWasmBuilder::stringToHeapType(std::string_view str, (str.substr(0, 6) == "struct" && (prefix || str.size() == 6))) { return HeapType::data; } + if (str.substr(0, 5) == "array" && (prefix || str.size() == 5)) { + return HeapType::array; + } if (str.substr(0, 6) == "string" && (prefix || str.size() == 6)) { return HeapType::string; } @@ -3021,9 +3027,9 @@ Expression* SExpressionWasmBuilder::makeArraySet(Element& s) { } Expression* SExpressionWasmBuilder::makeArrayLen(Element& s) { - auto heapType = parseHeapType(*s[1]); + // Ignore the type annotation and don't bother validating it. + parseHeapType(*s[1]); auto ref = parseExpression(*s[2]); - validateHeapTypeUsingChild(ref, heapType, s); return Builder(wasm).makeArrayLen(ref); } diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index 13d85d338..7c1c121d5 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -2168,12 +2168,9 @@ void BinaryInstWriter::visitArraySet(ArraySet* curr) { } void BinaryInstWriter::visitArrayLen(ArrayLen* curr) { - if (curr->ref->type.isNull()) { - emitUnreachable(); - return; - } o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::ArrayLen); - parent.writeIndexedHeapType(curr->ref->type.getHeapType()); + // Unused type index. + o << U32LEB(0); } void BinaryInstWriter::visitArrayCopy(ArrayCopy* curr) { diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp index b2cde6050..4fa7ff9d5 100644 --- a/src/wasm/wasm-type.cpp +++ b/src/wasm/wasm-type.cpp @@ -567,8 +567,9 @@ HeapType::BasicHeapType getBasicHeapSupertype(HeapType type) { case HeapTypeInfo::SignatureKind: return HeapType::func; case HeapTypeInfo::StructKind: - case HeapTypeInfo::ArrayKind: return HeapType::data; + case HeapTypeInfo::ArrayKind: + return HeapType::array; } WASM_UNREACHABLE("unexpected kind"); }; @@ -598,16 +599,21 @@ std::optional<HeapType> getBasicHeapTypeLUB(HeapType::BasicHeapType a, case HeapType::any: return {HeapType::any}; case HeapType::eq: - if (b == HeapType::i31 || b == HeapType::data) { + if (b == HeapType::i31 || b == HeapType::data || b == HeapType::array) { return {HeapType::eq}; } return {HeapType::any}; case HeapType::i31: - if (b == HeapType::data) { + if (b == HeapType::data || b == HeapType::array) { return {HeapType::eq}; } return {HeapType::any}; case HeapType::data: + if (b == HeapType::array) { + return {HeapType::data}; + } + return {HeapType::any}; + case HeapType::array: case HeapType::string: case HeapType::stringview_wtf8: case HeapType::stringview_wtf16: @@ -1086,13 +1092,14 @@ FeatureSet Type::getFeatures() const { } if (heapType.isBasic()) { switch (heapType.getBasic()) { - case HeapType::BasicHeapType::ext: - case HeapType::BasicHeapType::func: + case HeapType::ext: + case HeapType::func: return FeatureSet::ReferenceTypes; - case HeapType::BasicHeapType::any: - case HeapType::BasicHeapType::eq: - case HeapType::BasicHeapType::i31: - case HeapType::BasicHeapType::data: + case HeapType::any: + case HeapType::eq: + case HeapType::i31: + case HeapType::data: + case HeapType::array: return FeatureSet::ReferenceTypes | FeatureSet::GC; case HeapType::string: case HeapType::stringview_wtf8: @@ -1389,6 +1396,7 @@ bool HeapType::isBottom() const { case eq: case i31: case data: + case array: case string: case stringview_wtf8: case stringview_wtf16: @@ -1441,9 +1449,12 @@ size_t HeapType::getDepth() const { if (!isBasic()) { if (isFunction()) { depth++; - } else if (isData()) { + } else if (isStruct()) { // specific struct types <: data <: eq <: any depth += 3; + } else if (isArray()) { + // specific array types <: array <: data <: eq <: any + depth += 4; } } else { // Some basic types have supers. @@ -1463,6 +1474,9 @@ size_t HeapType::getDepth() const { case HeapType::stringview_iter: depth += 2; break; + case HeapType::array: + depth += 3; + break; case HeapType::none: case HeapType::nofunc: case HeapType::noext: @@ -1484,6 +1498,7 @@ HeapType::BasicHeapType HeapType::getBottom() const { case eq: case i31: case data: + case array: case string: case stringview_wtf8: case stringview_wtf16: @@ -1753,11 +1768,13 @@ bool SubTyper::isSubType(HeapType a, HeapType b) { return a.getBottom() == HeapType::none; case HeapType::eq: return a == HeapType::i31 || a == HeapType::data || - a == HeapType::none || a.isData(); + a == HeapType::array || a == HeapType::none || a.isData(); case HeapType::i31: return a == HeapType::none; case HeapType::data: - return a == HeapType::none || a.isData(); + return a == HeapType::array || a == HeapType::none || a.isData(); + case HeapType::array: + return a == HeapType::none || a.isArray(); case HeapType::string: case HeapType::stringview_wtf8: case HeapType::stringview_wtf16: @@ -2120,6 +2137,8 @@ std::ostream& TypePrinter::print(Type type) { return os << "i31ref"; case HeapType::data: return os << "dataref"; + case HeapType::array: + return os << "arrayref"; case HeapType::string: return os << "stringref"; case HeapType::stringview_wtf8: @@ -2164,6 +2183,8 @@ std::ostream& TypePrinter::print(HeapType type) { return os << "i31"; case HeapType::data: return os << "data"; + case HeapType::array: + return os << "array"; case HeapType::string: return os << "string"; case HeapType::stringview_wtf8: diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 91236fe54..0e7566528 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -2696,9 +2696,8 @@ void FunctionValidator::visitArrayGet(ArrayGet* curr) { if (curr->type == Type::unreachable) { return; } - // TODO: array rather than data once we've implemented that. if (!shouldBeSubType(curr->ref->type, - Type(HeapType::data, Nullable), + Type(HeapType::array, Nullable), curr, "array.get target should be an array reference")) { return; @@ -2707,6 +2706,11 @@ void FunctionValidator::visitArrayGet(ArrayGet* curr) { if (heapType == HeapType::none) { return; } + if (!shouldBeTrue(heapType != HeapType::array, + curr, + "array.get target should be a specific array reference")) { + return; + } const auto& element = heapType.getArray().element; // If the type is not packed, it must be marked internally as unsigned, by // convention. @@ -2725,9 +2729,8 @@ void FunctionValidator::visitArraySet(ArraySet* curr) { if (curr->type == Type::unreachable) { return; } - // TODO: array rather than data once we've implemented that. if (!shouldBeSubType(curr->ref->type, - Type(HeapType::data, Nullable), + Type(HeapType::array, Nullable), curr, "array.set target should be an array reference")) { return; @@ -2736,6 +2739,11 @@ void FunctionValidator::visitArraySet(ArraySet* curr) { if (heapType == HeapType::none) { return; } + if (!shouldBeTrue(heapType != HeapType::array, + curr, + "array.set target should be a specific array reference")) { + return; + } const auto& element = curr->ref->type.getHeapType().getArray().element; shouldBeSubType(curr->value->type, element.type, @@ -2749,6 +2757,10 @@ void FunctionValidator::visitArrayLen(ArrayLen* curr) { getModule()->features.hasGC(), curr, "array.len requires gc to be enabled"); shouldBeEqualOrFirstIsUnreachable( curr->type, Type(Type::i32), curr, "array.len result must be an i32"); + shouldBeSubType(curr->ref->type, + Type(HeapType::array, Nullable), + curr, + "array.len argument must be an array reference"); } void FunctionValidator::visitArrayCopy(ArrayCopy* curr) { @@ -2767,11 +2779,11 @@ void FunctionValidator::visitArrayCopy(ArrayCopy* curr) { return; } if (!shouldBeSubType(curr->srcRef->type, - Type(HeapType::data, Nullable), + Type(HeapType::array, Nullable), curr, "array.copy source should be an array reference") || !shouldBeSubType(curr->destRef->type, - Type(HeapType::data, Nullable), + Type(HeapType::array, Nullable), curr, "array.copy destination should be an array reference")) { return; @@ -2781,6 +2793,16 @@ void FunctionValidator::visitArrayCopy(ArrayCopy* curr) { if (srcHeapType == HeapType::none || destHeapType == HeapType::none) { return; } + if (!shouldBeTrue( + srcHeapType != HeapType::array, + curr, + "array.copy source needs to be a specific array reference") || + !shouldBeTrue( + srcHeapType != HeapType::array, + curr, + "array.copy destination needs to be a specific array reference")) { + return; + } const auto& srcElement = srcHeapType.getArray().element; const auto& destElement = destHeapType.getArray().element; shouldBeSubType(srcElement.type, diff --git a/test/example/c-api-kitchen-sink.txt b/test/example/c-api-kitchen-sink.txt index 3d2b74938..5c6400ce5 100644 --- a/test/example/c-api-kitchen-sink.txt +++ b/test/example/c-api-kitchen-sink.txt @@ -25,10 +25,10 @@ BinaryenHeapTypeAny: 2 BinaryenHeapTypeEq: 3 BinaryenHeapTypeI31: 4 BinaryenHeapTypeData: 5 -BinaryenHeapTypeString: 6 -BinaryenHeapTypeStringviewWTF8: 7 -BinaryenHeapTypeStringviewWTF16: 8 -BinaryenHeapTypeStringviewIter: 9 +BinaryenHeapTypeString: 7 +BinaryenHeapTypeStringviewWTF8: 8 +BinaryenHeapTypeStringviewWTF16: 9 +BinaryenHeapTypeStringviewIter: 10 BinaryenFeatureMVP: 0 BinaryenFeatureAtomics: 1 BinaryenFeatureBulkMemory: 16 diff --git a/test/gtest/type-builder.cpp b/test/gtest/type-builder.cpp index a397041bf..5760838da 100644 --- a/test/gtest/type-builder.cpp +++ b/test/gtest/type-builder.cpp @@ -506,6 +506,7 @@ TEST_F(IsorecursiveTest, TestBasicTypeRelations) { HeapType eq = HeapType::eq; HeapType i31 = HeapType::i31; HeapType data = HeapType::data; + HeapType array = HeapType::array; HeapType string = HeapType::string; HeapType stringview_wtf8 = HeapType::stringview_wtf8; HeapType stringview_wtf16 = HeapType::stringview_wtf16; @@ -551,6 +552,7 @@ TEST_F(IsorecursiveTest, TestBasicTypeRelations) { assertLUB(ext, eq, {}); assertLUB(ext, i31, {}); assertLUB(ext, data, {}); + assertLUB(ext, array, {}); assertLUB(ext, string, {}); assertLUB(ext, stringview_wtf8, {}); assertLUB(ext, stringview_wtf16, {}); @@ -567,6 +569,7 @@ TEST_F(IsorecursiveTest, TestBasicTypeRelations) { assertLUB(func, eq, {}); assertLUB(func, i31, {}); assertLUB(func, data, {}); + assertLUB(func, array, {}); assertLUB(func, string, {}); assertLUB(func, stringview_wtf8, {}); assertLUB(func, stringview_wtf16, {}); @@ -582,6 +585,7 @@ TEST_F(IsorecursiveTest, TestBasicTypeRelations) { assertLUB(any, eq, any); assertLUB(any, i31, any); assertLUB(any, data, any); + assertLUB(any, array, any); assertLUB(any, string, any); assertLUB(any, stringview_wtf8, any); assertLUB(any, stringview_wtf16, any); @@ -596,6 +600,7 @@ TEST_F(IsorecursiveTest, TestBasicTypeRelations) { assertLUB(eq, eq, eq); assertLUB(eq, i31, eq); assertLUB(eq, data, eq); + assertLUB(eq, array, eq); assertLUB(eq, string, any); assertLUB(eq, stringview_wtf8, any); assertLUB(eq, stringview_wtf16, any); @@ -609,6 +614,7 @@ TEST_F(IsorecursiveTest, TestBasicTypeRelations) { assertLUB(i31, i31, i31); assertLUB(i31, data, eq); + assertLUB(i31, array, eq); assertLUB(i31, string, any); assertLUB(i31, stringview_wtf8, any); assertLUB(i31, stringview_wtf16, any); @@ -621,6 +627,7 @@ TEST_F(IsorecursiveTest, TestBasicTypeRelations) { assertLUB(i31, defArray, eq); assertLUB(data, data, data); + assertLUB(data, array, data); assertLUB(data, string, any); assertLUB(data, stringview_wtf8, any); assertLUB(data, stringview_wtf16, any); @@ -632,6 +639,18 @@ TEST_F(IsorecursiveTest, TestBasicTypeRelations) { assertLUB(data, defStruct, data); assertLUB(data, defArray, data); + assertLUB(array, array, array); + assertLUB(array, string, any); + assertLUB(array, stringview_wtf8, any); + assertLUB(array, stringview_wtf16, any); + assertLUB(array, stringview_iter, any); + assertLUB(array, none, array); + assertLUB(array, noext, {}); + assertLUB(array, nofunc, {}); + assertLUB(array, defFunc, {}); + assertLUB(array, defStruct, data); + assertLUB(array, defArray, array); + assertLUB(string, string, string); assertLUB(string, stringview_wtf8, any); assertLUB(string, stringview_wtf16, any); @@ -865,13 +884,14 @@ TEST_F(NominalTest, TestDepth) { C = built[2]; } - // any < eq < data < specific struct and array types + // any :> eq :> data :> array :> specific array types EXPECT_EQ(HeapType(HeapType::any).getDepth(), 0U); EXPECT_EQ(HeapType(HeapType::eq).getDepth(), 1U); EXPECT_EQ(HeapType(HeapType::data).getDepth(), 2U); + EXPECT_EQ(HeapType(HeapType::array).getDepth(), 3U); EXPECT_EQ(A.getDepth(), 3U); EXPECT_EQ(B.getDepth(), 4U); - EXPECT_EQ(C.getDepth(), 3U); + EXPECT_EQ(C.getDepth(), 4U); // Signature types are subtypes of func. EXPECT_EQ(HeapType(HeapType::func).getDepth(), 0U); diff --git a/test/heap-types.wast.from-wast b/test/heap-types.wast.from-wast index dd3067e5a..4994857e2 100644 --- a/test/heap-types.wast.from-wast +++ b/test/heap-types.wast.from-wast @@ -383,10 +383,7 @@ ) (func $unreachables-array-6 (drop - (block ;; (replaces something unreachable we can't emit) - (drop - (unreachable) - ) + (array.len array (unreachable) ) ) diff --git a/test/lit/arrays.wast b/test/lit/arrays.wast new file mode 100644 index 000000000..b79ca1844 --- /dev/null +++ b/test/lit/arrays.wast @@ -0,0 +1,72 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. + +;; Check that array types and operations are emitted properly in the binary format. + +;; RUN: wasm-opt %s -all -S -o - | filecheck %s +;; RUN: wasm-opt %s -all --roundtrip -S -o - | filecheck %s --check-prefix=ROUNDTRIP + +;; Check that we can roundtrip through the text format as well. + +;; RUN: wasm-opt %s -all -S -o - | wasm-opt -all -S -o - | filecheck %s + +(module + (type $byte-array (array (mut i8))) + ;; CHECK: (type $ref|array|_=>_i32 (func (param (ref array)) (result i32))) + + ;; CHECK: (type $nullref_=>_i32 (func (param nullref) (result i32))) + + ;; CHECK: (type $arrayref_=>_i32 (func (param arrayref) (result i32))) + + ;; CHECK: (func $len (param $a (ref array)) (result i32) + ;; CHECK-NEXT: (array.len array + ;; CHECK-NEXT: (local.get $a) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; ROUNDTRIP: (type $ref|array|_=>_i32 (func (param (ref array)) (result i32))) + + ;; ROUNDTRIP: (type $nullref_=>_i32 (func (param nullref) (result i32))) + + ;; ROUNDTRIP: (type $arrayref_=>_i32 (func (param arrayref) (result i32))) + + ;; ROUNDTRIP: (func $len (param $a (ref array)) (result i32) + ;; ROUNDTRIP-NEXT: (array.len array + ;; ROUNDTRIP-NEXT: (local.get $a) + ;; ROUNDTRIP-NEXT: ) + ;; ROUNDTRIP-NEXT: ) + (func $len (param $a (ref array)) (result i32) + ;; TODO: remove the unused type annotation + (array.len $byte-array + (local.get $a) + ) + ) + + ;; CHECK: (func $impossible-len (param $none nullref) (result i32) + ;; CHECK-NEXT: (array.len none + ;; CHECK-NEXT: (local.get $none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; ROUNDTRIP: (func $impossible-len (param $none nullref) (result i32) + ;; ROUNDTRIP-NEXT: (array.len none + ;; ROUNDTRIP-NEXT: (local.get $none) + ;; ROUNDTRIP-NEXT: ) + ;; ROUNDTRIP-NEXT: ) + (func $impossible-len (param $none nullref) (result i32) + (array.len $byte-array + (local.get $none) + ) + ) + + ;; CHECK: (func $unreachable-len (param $a arrayref) (result i32) + ;; CHECK-NEXT: (array.len array + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; ROUNDTRIP: (func $unreachable-len (param $a arrayref) (result i32) + ;; ROUNDTRIP-NEXT: (unreachable) + ;; ROUNDTRIP-NEXT: ) + (func $unreachable-len (param $a arrayref) (result i32) + (array.len $byte-array + (unreachable) + ) + ) +) 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 e984e8be5..8006cb2a6 100644 --- a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt +++ b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt @@ -1,45 +1,49 @@ total - [exports] : 7 - [funcs] : 7 + [exports] : 8 + [funcs] : 11 [globals] : 6 [imports] : 5 [memories] : 1 [memory-data] : 22 - [table-data] : 8 + [table-data] : 7 [tables] : 1 - [tags] : 2 - [total] : 433 - [vars] : 6 + [tags] : 0 + [total] : 748 + [vars] : 19 + ArrayInit : 5 + AtomicFence : 1 AtomicRMW : 1 - Binary : 56 - Block : 36 - Break : 4 - Call : 22 - CallRef : 8 - Const : 113 + Binary : 84 + Block : 90 + Break : 14 + Call : 19 + CallIndirect : 2 + CallRef : 2 + Const : 176 Drop : 5 - GlobalGet : 24 - GlobalSet : 11 + GlobalGet : 58 + GlobalSet : 25 I31Get : 2 - I31New : 7 - If : 14 - Load : 17 - LocalGet : 24 - LocalSet : 17 - Loop : 4 + I31New : 4 + If : 39 + Load : 22 + LocalGet : 31 + LocalSet : 21 + Loop : 14 + MemoryCopy : 1 MemoryFill : 1 - Nop : 1 + Nop : 17 + RefAs : 4 RefEq : 2 - RefFunc : 18 + RefFunc : 9 RefIs : 1 - RefNull : 2 - Return : 12 - SIMDExtract : 1 - SIMDLoad : 1 - Select : 7 - Store : 2 - StructNew : 1 - Switch : 1 - TupleMake : 2 - Unary : 13 - Unreachable : 3 + RefNull : 5 + Return : 29 + SIMDExtract : 3 + SIMDReplace : 1 + Select : 3 + Store : 5 + StructNew : 2 + TupleExtract : 1 + TupleMake : 6 + Unary : 43 |