summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ir/possible-contents.cpp93
-rw-r--r--src/ir/possible-contents.h11
-rw-r--r--test/gtest/possible-contents.cpp15
3 files changed, 62 insertions, 57 deletions
diff --git a/src/ir/possible-contents.cpp b/src/ir/possible-contents.cpp
index 3b48a5122..a81520cd4 100644
--- a/src/ir/possible-contents.cpp
+++ b/src/ir/possible-contents.cpp
@@ -42,90 +42,84 @@ std::ostream& operator<<(std::ostream& stream,
namespace wasm {
-void PossibleContents::combine(const PossibleContents& other) {
- auto type = getType();
- auto otherType = other.getType();
+PossibleContents PossibleContents::combine(const PossibleContents& a,
+ const PossibleContents& b) {
+ auto aType = a.getType();
+ auto bType = b.getType();
// First handle the trivial cases of them being equal, or one of them is
// None or Many.
- if (*this == other) {
- return;
+ if (a == b) {
+ return a;
}
- if (other.isNone()) {
- return;
+ if (b.isNone()) {
+ return a;
}
- if (isNone()) {
- value = other.value;
- return;
+ if (a.isNone()) {
+ return b;
}
- if (isMany()) {
- return;
+ if (a.isMany()) {
+ return a;
}
- if (other.isMany()) {
- value = Many();
- return;
+ if (b.isMany()) {
+ return b;
}
- if (!type.isRef() || !otherType.isRef()) {
+ if (!aType.isRef() || !bType.isRef()) {
// At least one is not a reference. The only possibility left for a useful
// combination here is if they have the same type (since we've already ruled
// out the case of them being equal). If they have the same type then
// neither is a reference and we can emit an exact type (since subtyping is
// not relevant for non-references).
- if (type == otherType) {
- value = ExactType(type);
+ if (aType == bType) {
+ return ExactType(aType);
} else {
- value = Many();
+ return Many();
}
- return;
}
// Special handling for references from here.
- if (isNull() && other.isNull()) {
- // These must be nulls in different hierarchies, otherwise this would have
- // been handled by the `*this == other` case above.
- assert(type != otherType);
- value = Many();
- return;
+ if (a.isNull() && b.isNull()) {
+ // These must be nulls in different hierarchies, otherwise a would have
+ // been handled by the `a == b` case above.
+ assert(aType != bType);
+ return Many();
}
- auto lub = Type::getLeastUpperBound(type, otherType);
+ auto lub = Type::getLeastUpperBound(aType, bType);
if (lub == Type::none) {
// The types are not in the same hierarchy.
- value = Many();
- return;
+ return Many();
}
// From here we can assume there is a useful LUB.
// Nulls can be combined in by just adding nullability to a type.
- if (isNull() || other.isNull()) {
+ if (a.isNull() || b.isNull()) {
// Only one of them can be null here, since we already handled the case
// where they were both null.
- assert(!isNull() || !other.isNull());
- // If only one is a null then we can use the type info from the other, and
+ assert(!a.isNull() || !b.isNull());
+ // If only one is a null then we can use the type info from the b, and
// just add in nullability. For example, a literal of type T and a null
// becomes an exact type of T that allows nulls, and so forth.
auto mixInNull = [](ConeType cone) {
cone.type = Type(cone.type.getHeapType(), Nullable);
return cone;
};
- if (!isNull()) {
- value = mixInNull(getCone());
- return;
- } else if (!other.isNull()) {
- value = mixInNull(other.getCone());
- return;
+ if (!a.isNull()) {
+ return mixInNull(a.getCone());
+ } else if (!b.isNull()) {
+ return mixInNull(b.getCone());
}
}
// Find a ConeType that describes both inputs, using the shared ancestor which
// is the LUB. We need to find how big a cone we need: the cone must be big
// enough to contain both the inputs.
- auto depth = getCone().depth;
- auto otherDepth = other.getCone().depth;
+ auto aDepth = a.getCone().depth;
+ auto bDepth = b.getCone().depth;
Index newDepth;
- if (depth == FullDepth || otherDepth == FullDepth) {
+ if (aDepth == FullDepth || bDepth == FullDepth) {
// At least one has full (infinite) depth, so we know the new depth must
// be the same.
newDepth = FullDepth;
@@ -134,20 +128,19 @@ void PossibleContents::combine(const PossibleContents& other) {
// the depth of our cone.
// TODO: we could make a single loop that also does the LUB, at the same
// time, and also avoids calling getDepth() which loops once more?
- auto depthFromRoot = type.getHeapType().getDepth();
- auto otherDepthFromRoot = otherType.getHeapType().getDepth();
+ auto aDepthFromRoot = aType.getHeapType().getDepth();
+ auto bDepthFromRoot = bType.getHeapType().getDepth();
auto lubDepthFromRoot = lub.getHeapType().getDepth();
- assert(lubDepthFromRoot <= depthFromRoot);
- assert(lubDepthFromRoot <= otherDepthFromRoot);
- Index depthUnderLub = depthFromRoot - lubDepthFromRoot + depth;
- Index otherDepthUnderLub =
- otherDepthFromRoot - lubDepthFromRoot + otherDepth;
+ assert(lubDepthFromRoot <= aDepthFromRoot);
+ assert(lubDepthFromRoot <= bDepthFromRoot);
+ Index aDepthUnderLub = aDepthFromRoot - lubDepthFromRoot + aDepth;
+ Index bDepthUnderLub = bDepthFromRoot - lubDepthFromRoot + bDepth;
// The total cone must be big enough to contain all the above.
- newDepth = std::max(depthUnderLub, otherDepthUnderLub);
+ newDepth = std::max(aDepthUnderLub, bDepthUnderLub);
}
- value = ConeType{lub, newDepth};
+ return ConeType{lub, newDepth};
}
void PossibleContents::intersectWithFullCone(const PossibleContents& other) {
diff --git a/src/ir/possible-contents.h b/src/ir/possible-contents.h
index cf92a578a..b7c9bfafd 100644
--- a/src/ir/possible-contents.h
+++ b/src/ir/possible-contents.h
@@ -103,12 +103,12 @@ class PossibleContents {
// full cone of all subtypes for that type.
static ConeType FullConeType(Type type) { return ConeType{type, FullDepth}; }
+ template<typename T> PossibleContents(T value) : value(value) {}
+
public:
PossibleContents() : value(None()) {}
PossibleContents(const PossibleContents& other) = default;
- template<typename T> explicit PossibleContents(T val) : value(val) {}
-
// Most users will use one of the following static functions to construct a
// new instance:
@@ -163,7 +163,12 @@ public:
// Combine the information in a given PossibleContents to this one. The
// contents here will then include whatever content was possible in |other|.
- void combine(const PossibleContents& other);
+ [[nodiscard]] static PossibleContents combine(const PossibleContents& a,
+ const PossibleContents& b);
+
+ void combine(const PossibleContents& other) {
+ *this = PossibleContents::combine(*this, other);
+ }
// Removes anything not in |other| from this object, so that it ends up with
// only their intersection. Currently this only handles an intersection with a
diff --git a/test/gtest/possible-contents.cpp b/test/gtest/possible-contents.cpp
index 63486e7a9..ea2a0c0f0 100644
--- a/test/gtest/possible-contents.cpp
+++ b/test/gtest/possible-contents.cpp
@@ -25,17 +25,24 @@ template<typename T> void assertNotEqualSymmetric(const T& a, const T& b) {
// Asserts a combined with b (in any order) is equal to c.
template<typename T>
void assertCombination(const T& a, const T& b, const T& c) {
- T temp1 = a;
- temp1.combine(b);
+ T temp1 = PossibleContents::combine(a, b);
assertEqualSymmetric(temp1, c);
// Also check the type, as nulls will compare equal even if their types
// differ. We want to make sure even the types are identical.
assertEqualSymmetric(temp1.getType(), c.getType());
- T temp2 = b;
- temp2.combine(a);
+ T temp2 = PossibleContents::combine(b, a);
assertEqualSymmetric(temp2, c);
assertEqualSymmetric(temp2.getType(), c.getType());
+
+ // Verify the shorthand API works like the static one.
+ T temp3 = a;
+ temp3.combine(b);
+ assertEqualSymmetric(temp3, temp1);
+
+ T temp4 = b;
+ temp4.combine(a);
+ assertEqualSymmetric(temp4, temp2);
}
// Parse a module from text and return it.