summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Lively <7121787+tlively@users.noreply.github.com>2022-06-14 07:38:56 -0700
committerGitHub <noreply@github.com>2022-06-14 07:38:56 -0700
commit79211c2726f57e1d8a10733e2e130fa8b2543606 (patch)
tree3dfd4ad163ea04d9048d2e941f4d25de6ed46243
parent0657f612ce6fcbe8165d9f41c3e730273a9c7c64 (diff)
downloadbinaryen-79211c2726f57e1d8a10733e2e130fa8b2543606.tar.gz
binaryen-79211c2726f57e1d8a10733e2e130fa8b2543606.tar.bz2
binaryen-79211c2726f57e1d8a10733e2e130fa8b2543606.zip
[NFC] Optimize non-equirecursive LUB calculations (#4722)
Equirecursive LUB calculations potentially require building new recursive heap types that did not already exist in the system, so they have a complicated code path that uses a TypeBuilder to construct a LUB from the ground up. In contrast, nominal and isorecursive LUB calculations never introduce new heap types, so computing their LUBs is much simpler. Previously we were using the same code path with the TypeBuilder for all type systems out of convenience, but this commit factors out the LUB calculations for nominal and isorecursive types into a separate code path that does not use a TypeBuilder. Not only should this make LUB calculations faster for GC workloads, it also avoids a mysterious race condition during parallel LUB calculations with isorecursive types that resulted in a temporary type escaping from one thread and being used-after-free from another thread. It would be good to fix that bug properly, but it is very difficult to investigate. Sweeping it under the rug instead is the best trade off for now. Fixes #4719.
-rw-r--r--src/wasm/wasm-type.cpp237
1 files changed, 148 insertions, 89 deletions
diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp
index b3dd61a8e..0092f933d 100644
--- a/src/wasm/wasm-type.cpp
+++ b/src/wasm/wasm-type.cpp
@@ -187,8 +187,6 @@ private:
// used directly.
std::optional<Type> lub(Type a, Type b);
HeapType lub(HeapType a, HeapType b);
- HeapType::BasicHeapType lub(HeapType::BasicHeapType a,
- HeapType::BasicHeapType b);
std::optional<Tuple> lub(const Tuple& a, const Tuple& b);
std::optional<Field> lub(const Field& a, const Field& b);
std::optional<Signature> lub(const Signature& a, const Signature& b);
@@ -582,6 +580,52 @@ HeapType asCanonical(HeapType type) {
}
}
+HeapType::BasicHeapType getBasicHeapSupertype(HeapType type) {
+ if (type.isBasic()) {
+ return type.getBasic();
+ }
+ auto* info = getHeapTypeInfo(type);
+ switch (info->kind) {
+ case HeapTypeInfo::BasicKind:
+ break;
+ case HeapTypeInfo::SignatureKind:
+ return HeapType::func;
+ case HeapTypeInfo::StructKind:
+ case HeapTypeInfo::ArrayKind:
+ return HeapType::data;
+ }
+ WASM_UNREACHABLE("unexpected kind");
+};
+
+HeapType getBasicHeapTypeLUB(HeapType::BasicHeapType a,
+ HeapType::BasicHeapType b) {
+ if (a == b) {
+ return a;
+ }
+ // Canonicalize to have `a` be the lesser type.
+ if (unsigned(a) > unsigned(b)) {
+ std::swap(a, b);
+ }
+ switch (a) {
+ case HeapType::func:
+ case HeapType::any:
+ return HeapType::any;
+ case HeapType::eq:
+ if (b == HeapType::i31 || b == HeapType::data) {
+ return HeapType::eq;
+ }
+ return HeapType::any;
+ case HeapType::i31:
+ if (b == HeapType::data) {
+ return HeapType::eq;
+ }
+ return HeapType::any;
+ case HeapType::data:
+ return HeapType::any;
+ }
+ WASM_UNREACHABLE("unexpected basic type");
+}
+
TypeInfo::TypeInfo(const TypeInfo& other) {
kind = other.kind;
switch (kind) {
@@ -1222,11 +1266,59 @@ std::vector<HeapType> Type::getHeapTypeChildren() {
}
bool Type::hasLeastUpperBound(Type a, Type b) {
- return TypeBounder().hasLeastUpperBound(a, b);
+ if (getTypeSystem() == TypeSystem::Equirecursive) {
+ return TypeBounder().hasLeastUpperBound(a, b);
+ }
+ return getLeastUpperBound(a, b) != Type::none;
}
Type Type::getLeastUpperBound(Type a, Type b) {
- return TypeBounder().getLeastUpperBound(a, b);
+ if (a == b) {
+ return a;
+ }
+ if (getTypeSystem() == TypeSystem::Equirecursive) {
+ return TypeBounder().getLeastUpperBound(a, b);
+ }
+ if (a == Type::unreachable) {
+ return b;
+ }
+ if (b == Type::unreachable) {
+ return a;
+ }
+ if (a.isTuple() && b.isTuple()) {
+ auto size = a.size();
+ if (size != b.size()) {
+ return Type::none;
+ }
+ std::vector<Type> elems;
+ elems.reserve(size);
+ for (size_t i = 0; i < size; ++i) {
+ auto lub = Type::getLeastUpperBound(a[i], b[i]);
+ if (lub == Type::none) {
+ return Type::none;
+ }
+ elems.push_back(lub);
+ }
+ return Type(elems);
+ }
+ if (a.isRef() && b.isRef()) {
+ auto nullability =
+ (a.isNullable() || b.isNullable()) ? Nullable : NonNullable;
+ auto heapType =
+ HeapType::getLeastUpperBound(a.getHeapType(), b.getHeapType());
+ return Type(heapType, nullability);
+ }
+ if (a.isRtt() && b.isRtt()) {
+ auto heapType = a.getHeapType();
+ if (heapType != b.getHeapType()) {
+ return Type::none;
+ }
+ auto rttA = a.getRtt(), rttB = b.getRtt();
+ auto depth = rttA.depth == rttB.depth ? rttA.depth : Rtt::NoDepth;
+ return Rtt(depth, heapType);
+ }
+ return Type::none;
+ WASM_UNREACHABLE("unexpected type");
}
size_t Type::size() const {
@@ -1426,7 +1518,53 @@ std::vector<HeapType> HeapType::getReferencedHeapTypes() const {
}
HeapType HeapType::getLeastUpperBound(HeapType a, HeapType b) {
- return TypeBounder().getLeastUpperBound(a, b);
+ if (a == b) {
+ return a;
+ }
+ if (getTypeSystem() == TypeSystem::Equirecursive) {
+ return TypeBounder().getLeastUpperBound(a, b);
+ }
+ if (a.isBasic() || b.isBasic()) {
+ return getBasicHeapTypeLUB(getBasicHeapSupertype(a),
+ getBasicHeapSupertype(b));
+ }
+
+ auto* infoA = getHeapTypeInfo(a);
+ auto* infoB = getHeapTypeInfo(b);
+
+ if (infoA->kind != infoB->kind) {
+ return getBasicHeapTypeLUB(getBasicHeapSupertype(a),
+ getBasicHeapSupertype(b));
+ }
+
+ // Walk up the subtype tree to find the LUB. Ascend the tree from both `a`
+ // and `b` in lockstep. The first type we see for a second time must be the
+ // LUB because there are no cycles and the only way to encounter a type
+ // twice is for it to be on the path above both `a` and `b`.
+ std::unordered_set<HeapTypeInfo*> seen;
+ seen.insert(infoA);
+ seen.insert(infoB);
+ while (true) {
+ auto* nextA = infoA->supertype;
+ auto* nextB = infoB->supertype;
+ if (nextA == nullptr && nextB == nullptr) {
+ // Did not find a LUB in the subtype tree.
+ return getBasicHeapTypeLUB(getBasicHeapSupertype(a),
+ getBasicHeapSupertype(b));
+ }
+ if (nextA) {
+ if (!seen.insert(nextA).second) {
+ return HeapType(uintptr_t(nextA));
+ }
+ infoA = nextA;
+ }
+ if (nextB) {
+ if (!seen.insert(nextB).second) {
+ return HeapType(uintptr_t(nextB));
+ }
+ infoB = nextB;
+ }
+ }
}
// Recursion groups with single elements are encoded as that single element's
@@ -1763,70 +1901,20 @@ HeapType TypeBounder::lub(HeapType a, HeapType b) {
return a;
}
- auto getBasicApproximation = [](HeapType x) {
- if (x.isBasic()) {
- return x.getBasic();
- }
- auto* info = getHeapTypeInfo(x);
- switch (info->kind) {
- case HeapTypeInfo::BasicKind:
- break;
- case HeapTypeInfo::SignatureKind:
- return HeapType::func;
- case HeapTypeInfo::StructKind:
- case HeapTypeInfo::ArrayKind:
- return HeapType::data;
- }
- WASM_UNREACHABLE("unexpected kind");
- };
-
- auto getBasicLUB = [&]() {
- return lub(getBasicApproximation(a), getBasicApproximation(b));
- };
-
if (a.isBasic() || b.isBasic()) {
- return getBasicLUB();
+ return getBasicHeapTypeLUB(getBasicHeapSupertype(a),
+ getBasicHeapSupertype(b));
}
HeapTypeInfo* infoA = getHeapTypeInfo(a);
HeapTypeInfo* infoB = getHeapTypeInfo(b);
if (infoA->kind != infoB->kind) {
- return getBasicLUB();
+ return getBasicHeapTypeLUB(getBasicHeapSupertype(a),
+ getBasicHeapSupertype(b));
}
- if (typeSystem == TypeSystem::Nominal ||
- typeSystem == TypeSystem::Isorecursive) {
- // Walk up the subtype tree to find the LUB. Ascend the tree from both `a`
- // and `b` in lockstep. The first type we see for a second time must be the
- // LUB because there are no cycles and the only way to encounter a type
- // twice is for it to be on the path above both `a` and `b`.
- std::unordered_set<HeapTypeInfo*> seen;
- auto* currA = infoA;
- auto* currB = infoB;
- seen.insert(currA);
- seen.insert(currB);
- while (true) {
- auto* nextA = currA->supertype;
- auto* nextB = currB->supertype;
- if (nextA == nullptr && nextB == nullptr) {
- // Did not find a LUB in the subtype tree.
- return getBasicLUB();
- }
- if (nextA) {
- if (!seen.insert(nextA).second) {
- return HeapType(uintptr_t(nextA));
- }
- currA = nextA;
- }
- if (nextB) {
- if (!seen.insert(nextB).second) {
- return HeapType(uintptr_t(nextB));
- }
- currB = nextB;
- }
- }
- }
+ assert(getTypeSystem() == TypeSystem::Equirecursive);
// Allocate a new slot to construct the LUB of this pair if we have not
// already seen it before. Canonicalize the pair to have the element with the
@@ -1865,35 +1953,6 @@ HeapType TypeBounder::lub(HeapType a, HeapType b) {
WASM_UNREACHABLE("unexpected kind");
}
-HeapType::BasicHeapType TypeBounder::lub(HeapType::BasicHeapType a,
- HeapType::BasicHeapType b) {
- if (a == b) {
- return a;
- }
- // Canonicalize to have `x` be the lesser type.
- if (unsigned(a) > unsigned(b)) {
- std::swap(a, b);
- }
- switch (a) {
- case HeapType::func:
- case HeapType::any:
- return HeapType::any;
- case HeapType::eq:
- if (b == HeapType::i31 || b == HeapType::data) {
- return HeapType::eq;
- }
- return HeapType::any;
- case HeapType::i31:
- if (b == HeapType::data) {
- return HeapType::eq;
- }
- return HeapType::any;
- case HeapType::data:
- return HeapType::any;
- }
- WASM_UNREACHABLE("unexpected basic type");
-}
-
std::optional<Tuple> TypeBounder::lub(const Tuple& a, const Tuple& b) {
if (a.types.size() != b.types.size()) {
return {};