Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/ir/type-updating.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ GlobalTypeRewriter::TypeMap GlobalTypeRewriter::rebuildTypes(
i = 0;
for (auto [type, _] : typeIndices) {
typeBuilder[i].setOpen(type.isOpen());
typeBuilder[i].setShared(type.isShared());
if (type.isSignature()) {
auto sig = type.getSignature();
TypeList newParams, newResults;
Expand Down
3 changes: 3 additions & 0 deletions src/parser/contexts.h
Original file line number Diff line number Diff line change
Expand Up @@ -907,6 +907,7 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
void addStructType(StructT) {}
void addArrayType(ArrayT) {}
void setOpen() {}
void setShared() {}
Result<> addSubtype(Index) { return Ok{}; }
void finishSubtype(Name name, Index pos) {
// TODO: type annotations
Expand Down Expand Up @@ -1077,6 +1078,8 @@ struct ParseTypeDefsCtx : TypeParserCtx<ParseTypeDefsCtx> {

void setOpen() { builder[index].setOpen(); }

void setShared() { builder[index].setShared(); }

Result<> addSubtype(Index super) {
if (super >= builder.size()) {
return in.err("supertype index out of bounds");
Expand Down
36 changes: 26 additions & 10 deletions src/parser/parsers.h
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,8 @@ template<typename Ctx>
Result<typename Ctx::TypeUseT> typeuse(Ctx&, bool allowNames = true);
MaybeResult<ImportNames> inlineImport(Lexer&);
Result<std::vector<Name>> inlineExports(Lexer&);
template<typename Ctx> Result<> strtype(Ctx&);
template<typename Ctx> Result<> comptype(Ctx&);
template<typename Ctx> Result<> sharecomptype(Ctx&);
template<typename Ctx> MaybeResult<typename Ctx::ModuleNameT> subtype(Ctx&);
template<typename Ctx> MaybeResult<> deftype(Ctx&);
template<typename Ctx> MaybeResult<typename Ctx::LocalsT> locals(Ctx&);
Expand Down Expand Up @@ -2734,11 +2735,11 @@ Result<std::vector<Name>> inlineExports(Lexer& in) {
return exports;
}

// strtype ::= ft:functype => ft
// | ct:conttype => ct
// | st:structtype => st
// | at:arraytype => at
template<typename Ctx> Result<> strtype(Ctx& ctx) {
// comptype ::= ft:functype => ft
// | ct:conttype => ct
// | st:structtype => st
// | at:arraytype => at
template<typename Ctx> Result<> comptype(Ctx& ctx) {
if (auto type = functype(ctx)) {
CHECK_ERR(type);
ctx.addFuncType(*type);
Expand All @@ -2762,8 +2763,23 @@ template<typename Ctx> Result<> strtype(Ctx& ctx) {
return ctx.in.err("expected type description");
}

// subtype ::= '(' 'type' id? '(' 'sub' typeidx? strtype ')' ')'
// | '(' 'type' id? strtype ')'
// sharecomptype ::= '(' 'shared' t:comptype ')' => shared t
// | t:comptype => unshared t
template<typename Ctx> Result<> sharecomptype(Ctx& ctx) {
if (ctx.in.takeSExprStart("shared"sv)) {
ctx.setShared();
CHECK_ERR(comptype(ctx));
if (!ctx.in.takeRParen()) {
return ctx.in.err("expected end of shared comptype");
}
} else {
CHECK_ERR(comptype(ctx));
}
return Ok{};
}

// subtype ::= '(' 'type' id? '(' 'sub' typeidx? sharecomptype ')' ')'
// | '(' 'type' id? sharecomptype ')'
template<typename Ctx> MaybeResult<> subtype(Ctx& ctx) {
auto pos = ctx.in.getPos();

Expand All @@ -2785,13 +2801,13 @@ template<typename Ctx> MaybeResult<> subtype(Ctx& ctx) {
CHECK_ERR(ctx.addSubtype(*super));
}

CHECK_ERR(strtype(ctx));
CHECK_ERR(sharecomptype(ctx));

if (!ctx.in.takeRParen()) {
return ctx.in.err("expected end of subtype definition");
}
} else {
CHECK_ERR(strtype(ctx));
CHECK_ERR(sharecomptype(ctx));
}

if (!ctx.in.takeRParen()) {
Expand Down
4 changes: 4 additions & 0 deletions src/passes/TypeMerging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,9 @@ bool shapeEq(HeapType a, HeapType b) {
if (a.isOpen() != b.isOpen()) {
return false;
}
if (a.isShared() != b.isShared()) {
return false;
}
if (a.isStruct() && b.isStruct()) {
return shapeEq(a.getStruct(), b.getStruct());
}
Expand All @@ -555,6 +558,7 @@ bool shapeEq(HeapType a, HeapType b) {

size_t shapeHash(HeapType a) {
size_t digest = hash(a.isOpen());
rehash(digest, a.isShared());
if (a.isStruct()) {
rehash(digest, 0);
hash_combine(digest, shapeHash(a.getStruct()));
Expand Down
11 changes: 8 additions & 3 deletions src/wasm-type.h
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ class HeapType {
bool isString() const;
bool isBottom() const;
bool isOpen() const;
bool isShared() const;

Signature getSignature() const;
Continuation getContinuation() const;
Expand Down Expand Up @@ -614,16 +615,16 @@ struct TypeBuilder {
Type getTempTupleType(const Tuple&);
Type getTempRefType(HeapType heapType, Nullability nullable);

// In nominal mode, or for nominal types, declare the HeapType being built at
// index `i` to be an immediate subtype of the given HeapType. Does nothing
// for equirecursive types.
// Declare the HeapType being built at index `i` to be an immediate subtype of
// the given HeapType.
void setSubType(size_t i, HeapType super);

// Create a new recursion group covering slots [i, i + length). Groups must
// not overlap or go out of bounds.
void createRecGroup(size_t i, size_t length);

void setOpen(size_t i, bool open = true);
void setShared(size_t i, bool shared = true);

enum class ErrorReason {
// There is a cycle in the supertype relation.
Expand Down Expand Up @@ -696,6 +697,10 @@ struct TypeBuilder {
builder.setOpen(index, open);
return *this;
}
Entry& setShared(bool shared = true) {
builder.setShared(index, shared);
return *this;
}
};

Entry operator[](size_t i) { return Entry{*this, i}; }
Expand Down
28 changes: 28 additions & 0 deletions src/wasm/wasm-type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ struct HeapTypeInfo {
// global store.
bool isTemp = false;
bool isOpen = false;
bool isShared = 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
Expand Down Expand Up @@ -1251,6 +1252,15 @@ bool HeapType::isOpen() const {
}
}

bool HeapType::isShared() const {
if (isBasic()) {
// TODO: shared basic heap types
return false;
} else {
return getHeapTypeInfo(*this)->isShared;
}
}

Signature HeapType::getSignature() const {
assert(isSignature());
return getHeapTypeInfo(*this)->signature;
Expand Down Expand Up @@ -1953,6 +1963,9 @@ std::ostream& TypePrinter::print(HeapType type) {
os << ' ';
}
}
if (type.isShared()) {
os << "(shared ";
}
if (type.isSignature()) {
print(type.getSignature());
} else if (type.isContinuation()) {
Expand All @@ -1964,6 +1977,9 @@ std::ostream& TypePrinter::print(HeapType type) {
} else {
WASM_UNREACHABLE("unexpected type");
}
if (type.isShared()) {
os << ')';
}
if (useSub) {
os << ')';
}
Expand Down Expand Up @@ -2121,6 +2137,7 @@ size_t RecGroupHasher::hash(const HeapTypeInfo& info) const {
hash_combine(digest, hash(HeapType(uintptr_t(info.supertype))));
}
wasm::rehash(digest, info.isOpen);
wasm::rehash(digest, info.isShared);
wasm::rehash(digest, info.kind);
switch (info.kind) {
case HeapTypeInfo::SignatureKind:
Expand Down Expand Up @@ -2257,6 +2274,9 @@ bool RecGroupEquator::eq(const HeapTypeInfo& a, const HeapTypeInfo& b) const {
if (a.isOpen != b.isOpen) {
return false;
}
if (a.isShared != b.isShared) {
return false;
}
if (a.kind != b.kind) {
return false;
}
Expand Down Expand Up @@ -2532,12 +2552,20 @@ void TypeBuilder::setOpen(size_t i, bool open) {
impl->entries[i].info->isOpen = open;
}

void TypeBuilder::setShared(size_t i, bool shared) {
assert(i < size() && "index out of bounds");
impl->entries[i].info->isShared = shared;
}

namespace {

bool isValidSupertype(const HeapTypeInfo& sub, const HeapTypeInfo& super) {
if (!super.isOpen) {
return false;
}
if (sub.isShared != super.isShared) {
return false;
}
if (sub.kind != super.kind) {
return false;
}
Expand Down
34 changes: 34 additions & 0 deletions test/gtest/type-builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,40 @@ TEST_F(TypeTest, InvalidFinalSupertype) {
EXPECT_EQ(error->index, 1u);
}

TEST_F(TypeTest, InvalidSharedSupertype) {
TypeBuilder builder(2);
builder[0] = Struct{};
builder[1] = Struct{};
builder[0].setShared(true);
builder[1].setShared(false);
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, InvalidUnsharedSupertype) {
TypeBuilder builder(2);
builder[0] = Struct{};
builder[1] = Struct{};
builder[0].setShared(false);
builder[1].setShared(true);
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);
Expand Down
74 changes: 74 additions & 0 deletions test/lit/passes/type-merging-shared.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
;; RUN: foreach %s %t wasm-opt --closed-world --type-merging --remove-unused-types -all -S -o - | filecheck %s

(module
;; Shared and non-shared types are not merged.
;; CHECK: (rec
;; CHECK-NEXT: (type $C' (shared (func)))

;; CHECK: (type $B' (shared (array i8)))

;; CHECK: (type $B (array i8))

;; CHECK: (type $A' (shared (struct )))

;; CHECK: (type $A (struct ))
(type $A (struct))
(type $A' (shared (struct)))
(type $B (array i8))
(type $B' (shared (array i8)))
;; CHECK: (type $C (func))
(type $C (func))
(type $C' (shared (func)))

;; CHECK: (func $foo (type $C)
;; CHECK-NEXT: (local $a (ref null $A))
;; CHECK-NEXT: (local $a' (ref null $A'))
;; CHECK-NEXT: (local $b (ref null $B))
;; CHECK-NEXT: (local $b' (ref null $B'))
;; CHECK-NEXT: (local $c (ref null $C))
;; CHECK-NEXT: (local $c' (ref null $C'))
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $foo
(local $a (ref null $A))
(local $a' (ref null $A'))
(local $b (ref null $B))
(local $b' (ref null $B'))
(local $c (ref null $C))
(local $c' (ref null $C'))
)
)

(module
;; But two shared types can be merged.
;; CHECK: (rec
;; CHECK-NEXT: (type $B (shared (array i8)))

;; CHECK: (type $A (shared (struct )))
(type $A (shared (struct)))
(type $A' (shared (struct)))
(type $B (shared (array i8)))
(type $B' (shared (array i8)))
;; CHECK: (type $C (shared (func)))
(type $C (shared (func)))
(type $C' (shared (func)))

;; CHECK: (func $foo (type $C)
;; CHECK-NEXT: (local $a (ref null $A))
;; CHECK-NEXT: (local $a' (ref null $A))
;; CHECK-NEXT: (local $b (ref null $B))
;; CHECK-NEXT: (local $b' (ref null $B))
;; CHECK-NEXT: (local $c (ref null $C))
;; CHECK-NEXT: (local $c' (ref null $C))
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $foo
(local $a (ref null $A))
(local $a' (ref null $A'))
(local $b (ref null $B))
(local $b' (ref null $B'))
(local $c (ref null $C))
(local $c' (ref null $C'))
)
)