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
19 changes: 14 additions & 5 deletions src/wasm-type.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ using Tuple = TypeList;

enum Nullability { NonNullable, Nullable };
enum Mutability { Immutable, Mutable };
enum Exactness { Inexact, Exact };

// HeapType name information used for printing.
struct TypeNames {
Expand Down Expand Up @@ -314,10 +315,10 @@ class Type {

// Construct from a heap type description. Also covers construction from
// Signature, Struct or Array via implicit conversion to HeapType.
Type(HeapType heapType, Nullability nullable)
: Type(heapType.getID() | (nullable == Nullable ? NullMask : 0)) {
assert(heapType.isBasic() ||

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why remove the isBasic part?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's redundant since basic heap types should always satisfy the right hand side, anyway.

!(heapType.getID() & (TupleMask | NullMask | ExactMask)));
Type(HeapType heapType, Nullability nullable, Exactness exact = Inexact)
: Type(heapType.getID() | (nullable == Nullable ? NullMask : 0) |
(exact == Exact ? ExactMask : 0)) {
assert(!(heapType.getID() & (TupleMask | NullMask | ExactMask)));
}

// Predicates
Expand Down Expand Up @@ -366,6 +367,8 @@ class Type {
bool isRef() const { return !isBasic() && !(id & TupleMask); }
bool isNullable() const { return isRef() && (id & NullMask); }
bool isNonNullable() const { return isRef() && !(id & NullMask); }
bool isExact() const { return isRef() && (id & ExactMask); }
bool isInexact() const { return isRef() && !(id & ExactMask); }
HeapType getHeapType() const {
assert(isRef());
return HeapType(id & ~(NullMask | ExactMask));
Expand All @@ -390,6 +393,10 @@ class Type {
Nullability getNullability() const {
return isNullable() ? Nullable : NonNullable;
}
Exactness getExactness() const {
assert(isRef());
return isExact() ? Exact : Inexact;
}

private:
template<bool (Type::*pred)() const> bool hasPredicate() {
Expand Down Expand Up @@ -755,7 +762,9 @@ struct TypeBuilder {
// TypeBuilder's HeapTypes. For Ref types, the HeapType may be a temporary
// HeapType owned by this builder or a canonical HeapType.
Type getTempTupleType(const Tuple&);
Type getTempRefType(HeapType heapType, Nullability nullable);
Type getTempRefType(HeapType heapType,
Nullability nullable,
Exactness exact = Inexact);

// Declare the HeapType being built at index `i` to be an immediate subtype of
// the given HeapType.
Expand Down
110 changes: 82 additions & 28 deletions src/wasm/wasm-type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -776,11 +776,19 @@ Type Type::getLeastUpperBound(Type a, Type b) {
return Type(elems);
}
if (a.isRef() && b.isRef()) {
if (auto heapType =
HeapType::getLeastUpperBound(a.getHeapType(), b.getHeapType())) {
auto heapTypeA = a.getHeapType();
auto heapTypeB = b.getHeapType();
if (auto heapType = HeapType::getLeastUpperBound(heapTypeA, heapTypeB)) {
auto nullability =
(a.isNullable() || b.isNullable()) ? Nullable : NonNullable;
return Type(*heapType, nullability);
auto exactness = (a.isInexact() || b.isInexact()) ? Inexact : Exact;
// The LUB can only be exact if the heap types are the same or one of them
// is bottom.
if (heapTypeA != heapTypeB && !heapTypeA.isBottom() &&
!heapTypeB.isBottom()) {
exactness = Inexact;
}
return Type(*heapType, nullability, exactness);
}
}
return Type::none;
Expand Down Expand Up @@ -814,6 +822,7 @@ Type Type::getGreatestLowerBound(Type a, Type b) {
}
auto nullability =
(a.isNonNullable() || b.isNonNullable()) ? NonNullable : Nullable;
auto exactness = (a.isExact() || b.isExact()) ? Exact : Inexact;
HeapType heapType;
if (HeapType::isSubType(heapA, heapB)) {
heapType = heapA;
Expand All @@ -822,7 +831,13 @@ Type Type::getGreatestLowerBound(Type a, Type b) {
} else {
heapType = heapA.getBottom();
}
return Type(heapType, nullability);
// If one of the types is exact, but the GLB heap type is different than its
// heap type, then we must make the GLB heap type bottom.
if ((a.isExact() && heapType != heapA) ||
(b.isExact() && heapType != heapB)) {
heapType = heapA.getBottom();
}
return Type(heapType, nullability, exactness);
}

const Type& Type::Iterator::operator*() const {
Expand Down Expand Up @@ -1432,14 +1447,24 @@ bool SubTyper::isSubType(Type a, Type b) {
if (a == Type::unreachable) {
return true;
}
if (a.isRef() && b.isRef()) {
return (a.isNullable() == b.isNullable() || !a.isNullable()) &&
isSubType(a.getHeapType(), b.getHeapType());
}
if (a.isTuple() && b.isTuple()) {
return isSubType(a.getTuple(), b.getTuple());
}
return false;
if (!a.isRef() || !b.isRef()) {
return false;
}
if (a.isNullable() && !b.isNullable()) {
return false;
}
if (a.isInexact() && !b.isInexact()) {
return false;
}
auto heapTypeA = a.getHeapType();
auto heapTypeB = b.getHeapType();
if (b.isExact() && !heapTypeA.isBottom()) {
return heapTypeA == heapTypeB;
}
return isSubType(heapTypeA, heapTypeB);
}

bool SubTyper::isSubType(HeapType a, HeapType b) {
Expand Down Expand Up @@ -1586,44 +1611,69 @@ std::ostream& TypePrinter::print(Type type) {
} else if (type.isRef()) {
auto heapType = type.getHeapType();
if (type.isNullable() && heapType.isBasic() && !heapType.isShared()) {
if (type.isExact()) {
os << "(exact ";
}
// Print shorthands for certain basic heap types.
switch (heapType.getBasic(Unshared)) {
case HeapType::ext:
return os << "externref";
os << "externref";
break;
case HeapType::func:
return os << "funcref";
os << "funcref";
break;
case HeapType::cont:
return os << "contref";
os << "contref";
break;
case HeapType::any:
return os << "anyref";
os << "anyref";
break;
case HeapType::eq:
return os << "eqref";
os << "eqref";
break;
case HeapType::i31:
return os << "i31ref";
os << "i31ref";
break;
case HeapType::struct_:
return os << "structref";
os << "structref";
break;
case HeapType::array:
return os << "arrayref";
os << "arrayref";
break;
case HeapType::exn:
return os << "exnref";
os << "exnref";
break;
case HeapType::string:
return os << "stringref";
os << "stringref";
break;
case HeapType::none:
return os << "nullref";
os << "nullref";
break;
case HeapType::noext:
return os << "nullexternref";
os << "nullexternref";
break;
case HeapType::nofunc:
return os << "nullfuncref";
os << "nullfuncref";
break;
case HeapType::nocont:
return os << "nullcontref";
os << "nullcontref";
break;
case HeapType::noexn:
return os << "nullexnref";
os << "nullexnref";
break;
}
if (type.isExact()) {
os << ')';
}
return os;
}
os << "(ref ";
if (type.isNullable()) {
os << "null ";
}
if (type.isExact()) {
os << "exact ";
}
printHeapTypeName(heapType);
os << ')';
} else {
Expand Down Expand Up @@ -1851,8 +1901,9 @@ size_t RecGroupHasher::hash(Type type) const {
return digest;
}
assert(type.isRef());
rehash(digest, type.getNullability());
rehash(digest, hash(type.getHeapType()));
wasm::rehash(digest, type.getNullability());
wasm::rehash(digest, type.getExactness());
hash_combine(digest, hash(type.getHeapType()));
return digest;
}

Expand Down Expand Up @@ -1974,6 +2025,7 @@ bool RecGroupEquator::eq(Type a, Type b) const {
}
if (a.isRef() && b.isRef()) {
return a.getNullability() == b.getNullability() &&
a.getExactness() == b.getExactness() &&
eq(a.getHeapType(), b.getHeapType());
}
return false;
Expand Down Expand Up @@ -2164,8 +2216,10 @@ Type TypeBuilder::getTempTupleType(const Tuple& tuple) {
return impl->tupleStore.insert(tuple);
}

Type TypeBuilder::getTempRefType(HeapType type, Nullability nullable) {
return Type(type, nullable);
Type TypeBuilder::getTempRefType(HeapType type,
Nullability nullable,
Exactness exact) {
return Type(type, nullable, exact);
}

void TypeBuilder::setSubType(size_t i, std::optional<HeapType> super) {
Expand Down
Loading