summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/wasm/wasm-type.cpp223
1 files changed, 147 insertions, 76 deletions
diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp
index dcc385fb8..772f256b7 100644
--- a/src/wasm/wasm-type.cpp
+++ b/src/wasm/wasm-type.cpp
@@ -33,6 +33,7 @@ namespace {
struct TypeInfo {
using type_t = Type;
+ bool isTemp = false;
enum Kind {
TupleKind,
RefKind,
@@ -68,6 +69,8 @@ struct TypeInfo {
struct HeapTypeInfo {
using type_t = HeapType;
+ bool isTemp = false;
+ bool isSelfReferential = false;
enum Kind {
SignatureKind,
StructKind,
@@ -196,6 +199,23 @@ HeapTypeInfo* getHeapTypeInfo(HeapType ht) {
return (HeapTypeInfo*)ht.getID();
}
+Type markTemp(Type type) {
+ if (!type.isBasic()) {
+ getTypeInfo(type)->isTemp = true;
+ }
+ return type;
+}
+
+bool isTemp(Type type) { return !type.isBasic() && getTypeInfo(type)->isTemp; }
+
+bool isTemp(HeapType type) {
+ return !type.isBasic() && getHeapTypeInfo(type)->isTemp;
+}
+
+bool isSelfReferential(HeapType type) {
+ return !type.isBasic() && getHeapTypeInfo(type)->isSelfReferential;
+}
+
TypeInfo::TypeInfo(const TypeInfo& other) {
kind = other.kind;
switch (kind) {
@@ -245,6 +265,7 @@ bool TypeInfo::operator==(const TypeInfo& other) const {
HeapTypeInfo::HeapTypeInfo(const HeapTypeInfo& other) {
kind = other.kind;
+ isSelfReferential = other.isSelfReferential;
switch (kind) {
case SignatureKind:
new (&signature) auto(other.signature);
@@ -306,60 +327,14 @@ template<typename Info> struct Store {
// Maps from constructed types to their canonical Type IDs.
std::unordered_map<Info, uintptr_t> typeIDs;
- TypeID recordCanonical(std::unique_ptr<Info>&& info) {
- TypeID id = uintptr_t(info.get());
- assert(id > Info::type_t::_last_basic_type);
- typeIDs[*info] = id;
- constructedTypes.emplace_back(std::move(info));
- return id;
- }
+ bool isGlobalStore();
- typename Info::type_t canonicalize(const Info& info) {
- std::lock_guard<std::mutex> lock(mutex);
- auto indexIt = typeIDs.find(info);
- if (indexIt != typeIDs.end()) {
- return typename Info::type_t(indexIt->second);
- }
- return typename Info::type_t(recordCanonical(std::make_unique<Info>(info)));
- }
+ TypeID recordCanonical(std::unique_ptr<Info>&& info);
+ typename Info::type_t canonicalize(const Info& info);
};
struct TypeStore : Store<TypeInfo> {
- Type canonicalize(TypeInfo info) {
- if (info.isTuple()) {
- if (info.tuple.types.size() == 0) {
- return Type::none;
- }
- if (info.tuple.types.size() == 1) {
- return info.tuple.types[0];
- }
- }
- if (info.isRef() && info.ref.heapType.isBasic()) {
- if (info.ref.nullable) {
- switch (info.ref.heapType.getBasic()) {
- case HeapType::func:
- return Type::funcref;
- case HeapType::ext:
- return Type::externref;
- case HeapType::any:
- return Type::anyref;
- case HeapType::eq:
- return Type::eqref;
- case HeapType::i31:
- case HeapType::data:
- break;
- }
- } else {
- if (info.ref.heapType == HeapType::i31) {
- return Type::i31ref;
- }
- if (info.ref.heapType == HeapType::data) {
- return Type::dataref;
- }
- }
- }
- return Store<TypeInfo>::canonicalize(info);
- }
+ Type canonicalize(TypeInfo info);
};
using HeapTypeStore = Store<HeapTypeInfo>;
@@ -380,23 +355,97 @@ template<> struct MetaTypeInfo<HeapType> {
static HeapTypeInfo* getInfo(HeapType ht) { return getHeapTypeInfo(ht); }
};
+template<typename Info> bool Store<Info>::isGlobalStore() {
+ return this == &MetaTypeInfo<typename Info::type_t>::globalStore;
+}
+
+template<typename Info>
+TypeID Store<Info>::recordCanonical(std::unique_ptr<Info>&& info) {
+ assert((!isGlobalStore() || !info->isTemp) && "Leaking temporary type!");
+ TypeID id = uintptr_t(info.get());
+ assert(id > Info::type_t::_last_basic_type);
+ typeIDs[*info] = id;
+ constructedTypes.emplace_back(std::move(info));
+ return id;
+}
+
+template<typename Info>
+typename Info::type_t Store<Info>::canonicalize(const Info& info) {
+ std::lock_guard<std::mutex> lock(mutex);
+ auto indexIt = typeIDs.find(info);
+ if (indexIt != typeIDs.end()) {
+ return typename Info::type_t(indexIt->second);
+ }
+ return typename Info::type_t(recordCanonical(std::make_unique<Info>(info)));
+}
+
+Type TypeStore::canonicalize(TypeInfo info) {
+ if (info.isTuple()) {
+ if (info.tuple.types.size() == 0) {
+ return Type::none;
+ }
+ if (info.tuple.types.size() == 1) {
+ return info.tuple.types[0];
+ }
+ }
+ if (info.isRef() && info.ref.heapType.isBasic()) {
+ if (info.ref.nullable) {
+ switch (info.ref.heapType.getBasic()) {
+ case HeapType::func:
+ return Type::funcref;
+ case HeapType::ext:
+ return Type::externref;
+ case HeapType::any:
+ return Type::anyref;
+ case HeapType::eq:
+ return Type::eqref;
+ case HeapType::i31:
+ case HeapType::data:
+ break;
+ }
+ } else {
+ if (info.ref.heapType == HeapType::i31) {
+ return Type::i31ref;
+ }
+ if (info.ref.heapType == HeapType::data) {
+ return Type::dataref;
+ }
+ }
+ }
+ return Store<TypeInfo>::canonicalize(info);
+}
+
} // anonymous namespace
Type::Type(std::initializer_list<Type> types) : Type(Tuple(types)) {}
Type::Type(const Tuple& tuple) {
+#ifndef NDEBUG
+ for (auto type : tuple.types) {
+ assert(!isTemp(type) && "Leaking temporary type!");
+ }
+#endif
new (this) Type(globalTypeStore.canonicalize(tuple));
}
Type::Type(Tuple&& tuple) {
+#ifndef NDEBUG
+ for (auto type : tuple.types) {
+ assert(!isTemp(type) && "Leaking temporary type!");
+ }
+#endif
new (this) Type(globalTypeStore.canonicalize(std::move(tuple)));
}
Type::Type(HeapType heapType, Nullability nullable) {
+ assert(!isTemp(heapType) && "Leaking temporary type!");
new (this) Type(globalTypeStore.canonicalize(TypeInfo(heapType, nullable)));
}
-Type::Type(Rtt rtt) { new (this) Type(globalTypeStore.canonicalize(rtt)); }
+Type::Type(Rtt rtt) {
+ assert(!isTemp(rtt.heapType) && "Leaking temporary type!");
+ new (this) Type(globalTypeStore.canonicalize(rtt));
+}
bool Type::isTuple() const {
if (isBasic()) {
@@ -659,19 +708,32 @@ const Type& Type::operator[](size_t index) const {
}
}
-HeapType::HeapType(Signature signature) {
- new (this) HeapType(globalHeapTypeStore.canonicalize(signature));
+HeapType::HeapType(Signature sig) {
+ assert(!isTemp(sig.params) && "Leaking temporary type!");
+ assert(!isTemp(sig.results) && "Leaking temporary type!");
+ new (this) HeapType(globalHeapTypeStore.canonicalize(sig));
}
HeapType::HeapType(const Struct& struct_) {
+#ifndef NDEBUG
+ for (const auto& field : struct_.fields) {
+ assert(!isTemp(field.type) && "Leaking temporary type!");
+ }
+#endif
new (this) HeapType(globalHeapTypeStore.canonicalize(struct_));
}
HeapType::HeapType(Struct&& struct_) {
+#ifndef NDEBUG
+ for (const auto& field : struct_.fields) {
+ assert(!isTemp(field.type) && "Leaking temporary type!");
+ }
+#endif
new (this) HeapType(globalHeapTypeStore.canonicalize(std::move(struct_)));
}
HeapType::HeapType(Array array) {
+ assert(!isTemp(array.element.type) && "Leaking temporary type!");
new (this) HeapType(globalHeapTypeStore.canonicalize(array));
}
@@ -830,7 +892,7 @@ bool TypeComparator::lessThan(HeapType a, HeapType b) {
if (b.isBasic()) {
return false;
}
- // As we recurse, we will coinductively assume that a < b unless proven
+ // As we recurse, we will coinductively assume that a == b unless proven
// otherwise.
seen.insert({a, b});
return lessThan(*getHeapTypeInfo(a), *getHeapTypeInfo(b));
@@ -1211,7 +1273,8 @@ struct TypeBuilder::Impl {
info = std::make_unique<HeapTypeInfo>(Signature());
}
void set(HeapTypeInfo&& hti) {
- *info = hti;
+ *info = std::move(hti);
+ info->isTemp = true;
initialized = true;
}
HeapType get() { return HeapType(TypeID(info.get())); }
@@ -1249,18 +1312,25 @@ void TypeBuilder::setHeapType(size_t i, Array array) {
}
Type TypeBuilder::getTempTupleType(const Tuple& tuple) {
- return impl->typeStore.canonicalize(tuple);
+ Type ret = impl->typeStore.canonicalize(tuple);
+ if (tuple.types.size() > 1) {
+ return markTemp(ret);
+ } else {
+ // No new tuple was created, so the result might not be temporary.
+ return ret;
+ }
}
Type TypeBuilder::getTempRefType(size_t i, Nullability nullable) {
assert(i < impl->entries.size() && "Index out of bounds");
- return impl->typeStore.canonicalize(
- TypeInfo(impl->entries[i].get(), nullable));
+ return markTemp(
+ impl->typeStore.canonicalize(TypeInfo(impl->entries[i].get(), nullable)));
}
Type TypeBuilder::getTempRttType(size_t i, uint32_t depth) {
assert(i < impl->entries.size() && "Index out of bounds");
- return impl->typeStore.canonicalize(Rtt(depth, impl->entries[i].get()));
+ return markTemp(
+ impl->typeStore.canonicalize(Rtt(depth, impl->entries[i].get())));
}
namespace {
@@ -1300,9 +1370,6 @@ struct Canonicalizer {
std::unordered_map<Type, std::vector<Type*>> typeLocations;
std::unordered_map<HeapType, std::vector<HeapType*>> heapTypeLocations;
- // These heap types will not participate in canonicalization.
- std::unordered_set<HeapType> selfReferentialHeapTypes;
-
// Maps Types and HeapTypes backed by the TypeBuilder's Stores to globally
// canonical Types and HeapTypes.
std::unordered_map<Type, Type> canonicalTypes;
@@ -1378,7 +1445,7 @@ Canonicalizer::Canonicalizer(TypeBuilder& builder) : builder(builder) {
// considered canonical and outlive the TypeBuilder.
std::lock_guard<std::mutex> lock(globalHeapTypeStore.mutex);
for (auto& entry : builder.impl->entries) {
- if (selfReferentialHeapTypes.count(entry.get())) {
+ if (isSelfReferential(entry.get())) {
globalHeapTypeStore.recordCanonical(std::move(entry.info));
}
}
@@ -1451,6 +1518,12 @@ void Canonicalizer::findSelfReferentialHeapTypes() {
// find these because they must be their own direct children. See
// https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm.
+ auto mark = [&](HeapType type) {
+ auto* info = getHeapTypeInfo(type);
+ info->isSelfReferential = true;
+ info->isTemp = false;
+ };
+
// Get the HeapType children of a HeapType, skipping all intermediate Types.
auto getChildren = [&](HeapType type) {
std::unordered_set<HeapType> results;
@@ -1504,7 +1577,7 @@ void Canonicalizer::findSelfReferentialHeapTypes() {
// the strongly connected components. If a type is directly
// self-referential, mark it here so we don't miss it later.
if (child == curr) {
- selfReferentialHeapTypes.insert(child);
+ mark(curr);
}
}
@@ -1515,9 +1588,9 @@ void Canonicalizer::findSelfReferentialHeapTypes() {
// more than one element, they are all self referential HeapTypes.
// Self-referential types with SCC size one were already accounted for.
if (stack.back() != curr) {
- selfReferentialHeapTypes.insert(curr);
+ mark(curr);
while (stack.back() != curr) {
- selfReferentialHeapTypes.insert(stack.back());
+ mark(stack.back());
stackElems.erase(stack.back());
stack.pop_back();
}
@@ -1552,20 +1625,18 @@ std::vector<Canonicalizer::Item> Canonicalizer::getOrderedItems() {
// significant bottleneck and investigate using better data structures and
// algorithms.
- // Remove self-referential HeapTypes to cut cycles.
+ // Remove self-referential HeapTypes to cut cycles and collect the remaining
+ // types to be sorted.
auto childrenDAG = children;
- for (HeapType type : selfReferentialHeapTypes) {
- childrenDAG.erase(type.getID());
- for (auto& kv : childrenDAG) {
- kv.second.erase(type.getID());
- }
- }
-
- // Collect the remaining types that will be sorted.
std::unordered_set<TypeID> toSort;
for (auto& entry : builder.impl->entries) {
HeapType curr = entry.get();
- if (!selfReferentialHeapTypes.count(curr)) {
+ if (isSelfReferential(curr)) {
+ childrenDAG.erase(curr.getID());
+ for (auto& kv : childrenDAG) {
+ kv.second.erase(curr.getID());
+ }
+ } else {
toSort.insert(curr.getID());
}
}