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
5 changes: 4 additions & 1 deletion src/passes/Print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3946,7 +3946,10 @@ std::ostream& operator<<(std::ostream& o, wasm::ModuleType pair) {
std::ostream& operator<<(std::ostream& o, wasm::ModuleHeapType pair) {
if (auto it = pair.first.typeNames.find(pair.second);
it != pair.first.typeNames.end()) {
return o << it->second.name;
return o << '$' << it->second.name;
}
if (pair.second.isBasic()) {
return o << pair.second;
}
return o << "(unnamed)";
}
Expand Down
211 changes: 115 additions & 96 deletions src/passes/Unsubtyping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,108 @@ struct TypeTree {
}
};

struct Unsubtyping : Pass {
// There are two contexts where we have to note subtypings and casts: in the
// initial parallel analysis of the module and in the follow-on fixed point
// analysis over the type tree. Most of the logic is the same in both cases, but
// the final update of data structures is different. This CRTP utility
// deduplicates the shared logic.
template<typename Self> struct Noter {
Self& self() { return *static_cast<Self*>(this); }

void noteSubtype(HeapType sub, HeapType super) {
// Bottom types are uninteresting, but other basic heap types can be
// interesting because of their interactions with casts.
if (sub == super || sub.isBottom()) {
return;
}
DBG(std::cerr << "noting " << ModuleHeapType(*wasm, sub)
<< " <: " << ModuleHeapType(*wasm, super) << '\n');
self().doNoteSubtype(sub, super);
}

void noteSubtype(Type sub, Type super) {
if (sub.isTuple()) {
assert(super.isTuple() && sub.size() == super.size());
for (size_t i = 0, size = sub.size(); i < size; ++i) {
noteSubtype(sub[i], super[i]);
}
return;
}
if (!sub.isRef() || !super.isRef()) {
return;
}
noteSubtype(sub.getHeapType(), super.getHeapType());
}

void noteSubtype(Type sub, Expression* super) {
noteSubtype(sub, super->type);
}

void noteSubtype(Expression* sub, Type super) {
noteSubtype(sub->type, super);
}

void noteSubtype(Expression* sub, Expression* super) {
noteSubtype(sub->type, super->type);
}

void noteDescriptor(HeapType described, HeapType descriptor) {
DBG(std::cerr << "noting " << ModuleHeapType(*wasm, described) << " -> "
<< ModuleHeapType(*wasm, descriptor) << '\n');
self().doNoteDescriptor(described, descriptor);
}

void noteDescribed(HeapType type) {
auto desc = type.getDescriptorType();
assert(desc);
noteDescriptor(type, *desc);
}

void noteDescriptor(HeapType type) {
auto desc = type.getDescribedType();
assert(desc);
noteDescriptor(*desc, type);
}

void noteCast(HeapType src, Type dstType) {
auto dst = dstType.getHeapType();
// Casts to self and casts that must fail because they have incompatible
// types are uninteresting.
if (dst == src) {
return;
}
if (HeapType::isSubType(dst, src)) {
if (dstType.isExact()) {
// This cast only tests that the exact destination type is a subtype
// of the source type and does not impose additional requirements on
// subtypes of the destination type like a normal cast does.
noteSubtype(dst, src);
return;
}
self().doNoteCast(src, dst);
return;
}
if (HeapType::isSubType(src, dst)) {
// This is an upcast that will always succeed, but only if we ensure
// src <: dst.
noteSubtype(src, dst);
}
}

void noteCast(Expression* src, Type dst) {
if (src->type.isRef() && dst.isRef()) {
noteCast(src->type.getHeapType(), dst);
}
}

void noteCast(Expression* src, Expression* dst) {
if (src->type.isRef() && dst->type.isRef()) {
noteCast(src->type.getHeapType(), dst->type);
}
}
};

struct Unsubtyping : Pass, Noter<Unsubtyping> {
// The kind of work to process.
enum class Kind { Subtype, Descriptor };
// (sub, super) pairs that we have discovered but not yet processed.
Expand Down Expand Up @@ -482,34 +583,13 @@ struct Unsubtyping : Pass {
ReFinalize().run(getPassRunner(), wasm);
}

void noteSubtype(HeapType sub, HeapType super) {
// Bottom types are uninteresting, but other basic heap types can be
// interesting because of their interactions with casts.
if (sub == super || sub.isBottom()) {
return;
}
DBG(std::cerr << "noting " << ModuleHeapType(*wasm, sub)
<< " <: " << ModuleHeapType(*wasm, super) << '\n');
void doNoteSubtype(HeapType sub, HeapType super) {
work.push_back({Kind::Subtype, sub, super});
}

void noteSubtype(Type sub, Type super) {
if (sub.isTuple()) {
assert(super.isTuple() && sub.size() == super.size());
for (size_t i = 0, size = sub.size(); i < size; ++i) {
noteSubtype(sub[i], super[i]);
}
return;
}
if (!sub.isRef() || !super.isRef()) {
return;
}
noteSubtype(sub.getHeapType(), super.getHeapType());
}
void doNoteCast(HeapType src, HeapType dst) { casts[src].push_back(dst); }

void noteDescriptor(HeapType described, HeapType descriptor) {
DBG(std::cerr << "noting " << ModuleHeapType(*wasm, described) << " -> "
<< ModuleHeapType(*wasm, descriptor) << '\n');
void doNoteDescriptor(HeapType described, HeapType descriptor) {
work.push_back({Kind::Descriptor, described, descriptor});
}

Expand Down Expand Up @@ -538,7 +618,8 @@ struct Unsubtyping : Pass {
};

struct Collector
: ControlFlowWalker<Collector, SubtypingDiscoverer<Collector>> {
: ControlFlowWalker<Collector, SubtypingDiscoverer<Collector>>,
Noter<Collector> {
using Super =
ControlFlowWalker<Collector, SubtypingDiscoverer<Collector>>;

Expand All @@ -548,35 +629,10 @@ struct Unsubtyping : Pass {
Collector(Info& info, bool trapsNeverHappen)
: info(info), trapsNeverHappen(trapsNeverHappen) {}

void noteSubtype(Type sub, Type super) {
if (sub.isTuple()) {
assert(super.isTuple() && sub.size() == super.size());
for (size_t i = 0, size = sub.size(); i < size; ++i) {
noteSubtype(sub[i], super[i]);
}
return;
}
if (!sub.isRef() || !super.isRef()) {
return;
}
noteSubtype(sub.getHeapType(), super.getHeapType());
}
void noteSubtype(HeapType sub, HeapType super) {
assert(HeapType::isSubType(sub, super));
if (sub == super || sub.isBottom()) {
return;
}
void doNoteSubtype(HeapType sub, HeapType super) {
info.subtypings.insert({sub, super});
}
void noteSubtype(Type sub, Expression* super) {
noteSubtype(sub, super->type);
}
void noteSubtype(Expression* sub, Type super) {
noteSubtype(sub->type, super);
}
void noteSubtype(Expression* sub, Expression* super) {
noteSubtype(sub->type, super->type);
}

void noteNonFlowSubtype(Expression* sub, Type super) {
// This expression's type must be a subtype of |super|, but the value
// does not flow anywhere - this is a static constraint. As the value
Expand All @@ -603,52 +659,15 @@ struct Unsubtyping : Pass {
// Otherwise, we must take this into account.
noteSubtype(sub, super);
}
void noteCast(HeapType src, Type dstType) {
auto dst = dstType.getHeapType();
// Casts to self and casts that must fail because they have incompatible
// types are uninteresting.
if (dst == src) {
return;
}
if (HeapType::isSubType(dst, src)) {
if (dstType.isExact()) {
// This cast only tests that the exact destination type is a subtype
// of the source type and does not impose additional requirements on
// subtypes of the destination type like a normal cast does.
info.subtypings.insert({dst, src});
return;
}
info.casts.insert({src, dst});
return;
}
if (HeapType::isSubType(src, dst)) {
// This is an upcast that will always succeed, but only if we ensure
// src <: dst.
info.subtypings.insert({src, dst});
}
}
void noteCast(Expression* src, Type dst) {
if (src->type.isRef() && dst.isRef()) {
noteCast(src->type.getHeapType(), dst);
}
}
void noteCast(Expression* src, Expression* dst) {
if (src->type.isRef() && dst->type.isRef()) {
noteCast(src->type.getHeapType(), dst->type);
}
}

// Visitors for finding required descriptors.
void noteDescribed(HeapType type) {
auto desc = type.getDescriptorType();
assert(desc);
info.descriptors.insert({type, *desc});
void doNoteCast(HeapType src, HeapType dst) {
info.casts.insert({src, dst});
}
void noteDescriptor(HeapType type) {
auto desc = type.getDescribedType();
assert(desc);
info.descriptors.insert({*desc, type});

void doNoteDescriptor(HeapType described, HeapType descriptor) {
info.descriptors.insert({described, descriptor});
}

void visitRefGetDesc(RefGetDesc* curr) {
Super::visitRefGetDesc(curr);
if (!curr->ref->type.isStruct()) {
Expand Down
Loading