diff options
-rw-r--r-- | src/ir/module-utils.h | 7 | ||||
-rw-r--r-- | src/passes/Print.cpp | 10 | ||||
-rw-r--r-- | src/tools/tool-options.h | 8 | ||||
-rw-r--r-- | src/wasm-binary.h | 9 | ||||
-rw-r--r-- | src/wasm-type.h | 2 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 32 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 21 | ||||
-rw-r--r-- | src/wasm/wasm-type.cpp | 12 | ||||
-rw-r--r-- | test/lit/nominal-bad.wast | 29 | ||||
-rw-r--r-- | test/lit/nominal-chain.wast | 26 | ||||
-rw-r--r-- | test/lit/nominal-good.wast | 42 |
11 files changed, 182 insertions, 16 deletions
diff --git a/src/ir/module-utils.h b/src/ir/module-utils.h index 486838aea..aaf484036 100644 --- a/src/ir/module-utils.h +++ b/src/ir/module-utils.h @@ -566,6 +566,13 @@ inline void collectHeapTypes(Module& wasm, counts.note(child); } } + HeapType super; + if (ht.getSuperType(super)) { + if (!counts.count(super)) { + newTypes.insert(super); + } + counts.note(super); + } } // Sort by frequency and then original insertion order. diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 47db029ab..dbe4f2557 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -2496,7 +2496,7 @@ struct PrintSExpression : public UnifiedExpressionVisitor<PrintSExpression> { } o << ')'; } - void handleHeapType(HeapType type) { + void handleHeapType(HeapType type, Module* module) { if (type.isSignature()) { handleSignature(type.getSignature()); } else if (type.isArray()) { @@ -2506,6 +2506,12 @@ struct PrintSExpression : public UnifiedExpressionVisitor<PrintSExpression> { } else { o << type; } + HeapType super; + if (type.getSuperType(super)) { + o << " (extends "; + TypeNamePrinter(o, module).print(super); + o << ')'; + } } void visitExport(Export* curr) { o << '('; @@ -2889,7 +2895,7 @@ struct PrintSExpression : public UnifiedExpressionVisitor<PrintSExpression> { printMedium(o, "type") << ' '; TypeNamePrinter(o, curr).print(type); o << ' '; - handleHeapType(type); + handleHeapType(type, curr); o << ")" << maybeNewLine; } ModuleUtils::iterImportedMemories( diff --git a/src/tools/tool-options.h b/src/tools/tool-options.h index bc768c1da..49eca1adc 100644 --- a/src/tools/tool-options.h +++ b/src/tools/tool-options.h @@ -114,6 +114,14 @@ struct ToolOptions : public Options { value = argument.substr(colon + 1); } passOptions.arguments[key] = value; + }) + .add("--nominal", + "", + "Use the prototype nominal type system instead of the normal " + "equirecursive type system.", + Options::Arguments::Zero, + [](Options* o, const std::string& argument) { + setTypeSystem(TypeSystem::Nominal); }); } diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 715600f14..ca4d8612c 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -380,9 +380,12 @@ enum EncodedType { rtt = -0x18, // 0x68 dataref = -0x19, // 0x67 // func_type form - Func = -0x20, // 0x60 - Struct = -0x21, // 0x5f - Array = -0x22, // 0x5e + Func = -0x20, // 0x60 + Struct = -0x21, // 0x5f + Array = -0x22, // 0x5e + FuncExtending = -0x23, // 0x5d + StructExtending = -0x24, // 0x5c + ArrayExtending = -0x25, // 0x5b // block_type Empty = -0x40 // 0x40 }; diff --git a/src/wasm-type.h b/src/wasm-type.h index ff7e9af9c..12b4f3df7 100644 --- a/src/wasm-type.h +++ b/src/wasm-type.h @@ -363,6 +363,8 @@ public: const Struct& getStruct() const; Array getArray() const; + bool getSuperType(HeapType& out) const; + constexpr TypeID getID() const { return id; } constexpr BasicHeapType getBasic() const { assert(isBasic() && "Basic heap type expected"); diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index db253fa6e..a4df4ed16 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -219,8 +219,11 @@ void WasmBinaryWriter::writeTypes() { for (Index i = 0; i < types.size(); ++i) { auto type = types[i]; BYN_TRACE("write " << type << std::endl); + HeapType super; + bool hasSuper = type.getSuperType(super); if (type.isSignature()) { - o << S32LEB(BinaryConsts::EncodedType::Func); + o << S32LEB(hasSuper ? BinaryConsts::EncodedType::FuncExtending + : BinaryConsts::EncodedType::Func); auto sig = type.getSignature(); for (auto& sigType : {sig.params, sig.results}) { o << U32LEB(sigType.size()); @@ -229,18 +232,23 @@ void WasmBinaryWriter::writeTypes() { } } } else if (type.isStruct()) { - o << S32LEB(BinaryConsts::EncodedType::Struct); + o << S32LEB(hasSuper ? BinaryConsts::EncodedType::StructExtending + : BinaryConsts::EncodedType::Struct); auto fields = type.getStruct().fields; o << U32LEB(fields.size()); for (const auto& field : fields) { writeField(field); } } else if (type.isArray()) { - o << S32LEB(BinaryConsts::EncodedType::Array); + o << S32LEB(hasSuper ? BinaryConsts::EncodedType::ArrayExtending + : BinaryConsts::EncodedType::Array); writeField(type.getArray().element); } else { WASM_UNREACHABLE("TODO GC type writing"); } + if (hasSuper) { + o << U32LEB(getTypeIndex(super)); + } } finishSection(start); } @@ -1901,15 +1909,27 @@ void WasmBinaryBuilder::readTypes() { for (size_t i = 0; i < numTypes; i++) { BYN_TRACE("read one\n"); auto form = getS32LEB(); - if (form == BinaryConsts::EncodedType::Func) { + if (form == BinaryConsts::EncodedType::Func || + form == BinaryConsts::EncodedType::FuncExtending) { builder[i] = readSignatureDef(); - } else if (form == BinaryConsts::EncodedType::Struct) { + } else if (form == BinaryConsts::EncodedType::Struct || + form == BinaryConsts::EncodedType::StructExtending) { builder[i] = readStructDef(); - } else if (form == BinaryConsts::EncodedType::Array) { + } else if (form == BinaryConsts::EncodedType::Array || + form == BinaryConsts::EncodedType::ArrayExtending) { builder[i] = Array(readFieldDef()); } else { throwError("bad type form " + std::to_string(form)); } + if (form == BinaryConsts::EncodedType::FuncExtending || + form == BinaryConsts::EncodedType::StructExtending || + form == BinaryConsts::EncodedType::ArrayExtending) { + auto superIndex = getU32LEB(); + if (superIndex >= numTypes) { + throwError("bad supertype index " + std::to_string(superIndex)); + } + builder[i].subTypeOf(builder[superIndex]); + } } types = builder.build(); diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 8c0fb9b2d..6ca0bcfd1 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -50,8 +50,9 @@ int unhex(char c) { namespace wasm { -static Name STRUCT("struct"), FIELD("field"), ARRAY("array"), I8("i8"), - I16("i16"), RTT("rtt"), DECLARE("declare"), ITEM("item"), OFFSET("offset"); +static Name STRUCT("struct"), FIELD("field"), ARRAY("array"), + EXTENDS("extends"), I8("i8"), I16("i16"), RTT("rtt"), DECLARE("declare"), + ITEM("item"), OFFSET("offset"); static Address getAddress(const Element* s) { return atoll(s->c_str()); } @@ -857,15 +858,25 @@ void SExpressionWasmBuilder::preParseHeapTypes(Element& module) { Element& def = elem[1]->dollared() ? *elem[2] : *elem[1]; Element& kind = *def[0]; if (kind == FUNC) { - builder[index++] = parseSignatureDef(def); + builder[index] = parseSignatureDef(def); } else if (kind == STRUCT) { builder[index] = parseStructDef(def, index); - index++; } else if (kind == ARRAY) { - builder[index++] = parseArrayDef(def); + builder[index] = parseArrayDef(def); } else { throw ParseException("unknown heaptype kind", kind.line, kind.col); } + if (elementStartsWith(elem[elem.size() - 1], EXTENDS)) { + // '(' 'extends' $supertype ')' + Element& extends = *elem[elem.size() - 1]; + auto it = typeIndices.find(extends[1]->c_str()); + if (it == typeIndices.end()) { + throw ParseException( + "unknown dollared function type", elem.line, elem.col); + } + builder[index].subTypeOf(builder[it->second]); + } + ++index; }); types = builder.build(); diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp index 96cb0b557..9bae42e98 100644 --- a/src/wasm/wasm-type.cpp +++ b/src/wasm/wasm-type.cpp @@ -1176,6 +1176,18 @@ Array HeapType::getArray() const { return getHeapTypeInfo(*this)->array; } +bool HeapType::getSuperType(HeapType& out) const { + if (isBasic()) { + return false; + } + HeapTypeInfo* super = getHeapTypeInfo(*this)->supertype; + if (super != nullptr) { + out = HeapType(uintptr_t(super)); + return true; + } + return false; +} + bool HeapType::isSubType(HeapType left, HeapType right) { return SubTyper().isSubType(left, right); } diff --git a/test/lit/nominal-bad.wast b/test/lit/nominal-bad.wast new file mode 100644 index 000000000..43a1eea95 --- /dev/null +++ b/test/lit/nominal-bad.wast @@ -0,0 +1,29 @@ +;; RUN: not wasm-opt %s -all --nominal -S -o - 2>&1 | filecheck %s + +;; CHECK: [wasm-validator error in function make-super-struct] function body type must match +;; CHECK: [wasm-validator error in function make-super-array] function body type must match + +(module + + (type $sub-struct (struct i32 i64)) + (type $super-struct (struct i32)) + + (type $sub-array (array (ref $sub-struct))) + (type $super-array (array (ref $super-struct))) + + (func $make-sub-struct (result (ref $sub-struct)) + (unreachable) + ) + + (func $make-super-struct (result (ref $super-struct)) + (call $make-sub-struct) + ) + + (func $make-sub-array (result (ref $sub-array)) + (unreachable) + ) + + (func $make-super-array (result (ref $super-array)) + (call $make-sub-array) + ) +) diff --git a/test/lit/nominal-chain.wast b/test/lit/nominal-chain.wast new file mode 100644 index 000000000..b6b48a191 --- /dev/null +++ b/test/lit/nominal-chain.wast @@ -0,0 +1,26 @@ +;; RUN: wasm-opt %s -all --nominal -S -o - | filecheck %s +;; RUN: wasm-opt %s -all --nominal --roundtrip -S -o - | filecheck %s + +;; Check that intermediate types in subtype chains are also included in the +;; output module, even if there are no other references to those intermediate +;; types. + +(module + + ;; CHECK: (type $root (struct )) + ;; CHECK-NEXT: (type $none_=>_ref?|$root| (func (result (ref null $root)))) + ;; CHECK-NEXT: (type $leaf (struct (field i32) (field i64) (field f32) (field f64)) (extends $twig)) + ;; CHECK-NEXT: (type $twig (struct (field i32) (field i64) (field f32)) (extends $branch)) + ;; CHECK-NEXT: (type $branch (struct (field i32) (field i64)) (extends $trunk)) + ;; CHECK-NEXT: (type $trunk (struct (field i32)) (extends $root)) + + (type $leaf (struct i32 i64 f32 f64) (extends $twig)) + (type $twig (struct i32 i64 f32) (extends $branch)) + (type $branch (struct i32 i64) (extends $trunk)) + (type $trunk (struct i32) (extends $root)) + (type $root (struct)) + + (func $make-root (result (ref null $root)) + (ref.null $leaf) + ) +) diff --git a/test/lit/nominal-good.wast b/test/lit/nominal-good.wast new file mode 100644 index 000000000..d63b0506a --- /dev/null +++ b/test/lit/nominal-good.wast @@ -0,0 +1,42 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. +;; RUN: wasm-opt %s -all --nominal -S -o - | filecheck %s +;; RUN: wasm-opt %s -all --nominal --roundtrip -S -o - | filecheck %s + +(module + + (type $sub-struct (struct i32 i64) (extends $super-struct)) + (type $super-struct (struct i32)) + + (type $sub-array (array (ref $sub-struct)) (extends $super-array)) + (type $super-array (array (ref $super-struct))) + + ;; TODO: signature types as well, once functions store their HeapTypes. + + ;; CHECK: (func $make-sub-struct (result (ref $sub-struct)) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $make-sub-struct (result (ref $sub-struct)) + (unreachable) + ) + + ;; CHECK: (func $make-super-struct (result (ref $super-struct)) + ;; CHECK-NEXT: (call $make-sub-struct) + ;; CHECK-NEXT: ) + (func $make-super-struct (result (ref $super-struct)) + (call $make-sub-struct) + ) + + ;; CHECK: (func $make-sub-array (result (ref $sub-array)) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $make-sub-array (result (ref $sub-array)) + (unreachable) + ) + + ;; CHECK: (func $make-super-array (result (ref $super-array)) + ;; CHECK-NEXT: (call $make-sub-array) + ;; CHECK-NEXT: ) + (func $make-super-array (result (ref $super-array)) + (call $make-sub-array) + ) +) |