diff options
-rw-r--r-- | src/ir/possible-contents.cpp | 92 | ||||
-rw-r--r-- | src/ir/possible-contents.h | 5 | ||||
-rw-r--r-- | test/gtest/possible-contents.cpp | 28 |
3 files changed, 89 insertions, 36 deletions
diff --git a/src/ir/possible-contents.cpp b/src/ir/possible-contents.cpp index 3430bc872..916a39f2c 100644 --- a/src/ir/possible-contents.cpp +++ b/src/ir/possible-contents.cpp @@ -140,8 +140,21 @@ PossibleContents PossibleContents::combine(const PossibleContents& a, return ConeType{lub, newDepth}; } -void PossibleContents::intersectWithFullCone(const PossibleContents& other) { - assert(other.isFullConeType()); +void PossibleContents::intersect(const PossibleContents& other) { + // This does not yet handle all possible content. + assert(other.isFullConeType() || other.isLiteral() || other.isNone()); + + if (*this == other) { + // Nothing changes. + return; + } + + if (!haveIntersection(*this, other)) { + // There is no intersection at all. + // Note that this code path handles |this| or |other| being None. + value = None(); + return; + } if (isSubContents(other, *this)) { // The intersection is just |other|. @@ -150,16 +163,18 @@ void PossibleContents::intersectWithFullCone(const PossibleContents& other) { return; } - if (!haveIntersection(*this, other)) { - // There is no intersection at all. - // Note that this code path handles |this| being None. + if (isSubContents(*this, other)) { + // The intersection is just |this|. + return; + } + + if (isLiteral() || other.isLiteral()) { + // We've ruled out either being a subcontents of the other. A literal has + // no other intersection possibility. value = None(); return; } - // There is an intersection here. Note that this implies |this| is a reference - // type, as it has an intersection with |other| which is a full cone type - // (which must be a reference type). auto type = getType(); auto otherType = other.getType(); auto heapType = type.getHeapType(); @@ -290,13 +305,19 @@ bool PossibleContents::haveIntersection(const PossibleContents& a, return true; } + if (a == b) { + // The intersection is equal to them. + return true; + } + auto aType = a.getType(); auto bType = b.getType(); if (!aType.isRef() || !bType.isRef()) { // At least one is not a reference. The only way they can intersect is if - // the type is identical. - return aType == bType; + // the type is identical, and they are not both literals (we've already + // ruled out them being identical earlier). + return aType == bType && (!a.isLiteral() || !b.isLiteral()); } // From here on we focus on references. @@ -350,15 +371,37 @@ bool PossibleContents::haveIntersection(const PossibleContents& a, bool PossibleContents::isSubContents(const PossibleContents& a, const PossibleContents& b) { - // TODO: Everything else. For now we only call this when |a| or |b| is a full - // cone type. + if (a == b) { + return true; + } + + if (a.isNone()) { + return true; + } + + if (b.isNone()) { + return false; + } + + if (a.isMany()) { + return false; + } + + if (b.isMany()) { + return true; + } + + if (a.isLiteral()) { + // Note we already checked for |a == b| above. We need b to be a set that + // contains the literal a. + return !b.isLiteral() && Type::isSubType(a.getType(), b.getType()); + } + + if (b.isLiteral()) { + return false; + } + if (b.isFullConeType()) { - if (a.isNone()) { - return true; - } - if (a.isMany()) { - return false; - } if (a.isNull()) { return b.getType().isNullable(); } @@ -366,12 +409,11 @@ bool PossibleContents::isSubContents(const PossibleContents& a, } if (a.isFullConeType()) { - // We've already ruled out b being a full cone type before, so the only way - // |a| can be contained in |b| is if |b| is everything. - return b.isMany(); + // We've already ruled out b being a full cone type before. + return false; } - WASM_UNREACHABLE("a or b must be a full cone"); + WASM_UNREACHABLE("unhandled case of isSubContents"); } namespace { @@ -1932,7 +1974,7 @@ void Flower::filterExpressionContents(PossibleContents& contents, // The maximal contents here are the declared type and all subtypes. Nothing // else can pass through, so filter such things out. auto maximalContents = PossibleContents::fullConeType(type); - contents.intersectWithFullCone(maximalContents); + contents.intersect(maximalContents); if (contents.isNone()) { // Nothing was left here at all. return; @@ -1966,9 +2008,7 @@ void Flower::filterExpressionContents(PossibleContents& contents, normalizeConeType(contents); // There is a chance that the intersection is equal to the maximal contents, - // which would mean nothing more can arrive here. (Note that we can't - // normalize |maximalContents| before the intersection as - // intersectWithFullCone assumes a full/infinite cone.) + // which would mean nothing more can arrive here. normalizeConeType(maximalContents); if (contents == maximalContents) { diff --git a/src/ir/possible-contents.h b/src/ir/possible-contents.h index b4326688e..018cf197c 100644 --- a/src/ir/possible-contents.h +++ b/src/ir/possible-contents.h @@ -176,9 +176,8 @@ public: } // 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 - // full cone. - void intersectWithFullCone(const PossibleContents& other); + // only their intersection. + void intersect(const PossibleContents& other); bool isNone() const { return std::get_if<None>(&value); } bool isLiteral() const { return std::get_if<Literal>(&value); } diff --git a/test/gtest/possible-contents.cpp b/test/gtest/possible-contents.cpp index 85a4e86fd..e10773550 100644 --- a/test/gtest/possible-contents.cpp +++ b/test/gtest/possible-contents.cpp @@ -325,7 +325,7 @@ TEST_F(PossibleContentsTest, TestIntersection) { assertHaveIntersection(exactI32, exactI32); assertHaveIntersection(i32Zero, i32Zero); assertHaveIntersection(exactFuncSignatureType, exactFuncSignatureType); - assertHaveIntersection(i32Zero, i32One); // TODO: this could be inferred false + assertLackIntersection(i32Zero, i32One); // Exact types only differing by nullability can intersect (not on the null, // but on something else). @@ -426,12 +426,12 @@ TEST_F(PossibleContentsTest, TestIntersectWithCombinations) { assertHaveIntersection(normalizedCone, item); } - // Test intersectWithFullCone() method, which is supported with a full - // cone type. In that case we can test that the intersection of A with - // A + B is simply A. + // Test intersect() method, which is supported with a full cone type. + // In that case we can test that the intersection of A with A + B is + // simply A. if (combination.isFullConeType()) { auto intersection = item; - intersection.intersectWithFullCone(combination); + intersection.intersect(combination); EXPECT_EQ(intersection, item); #if BINARYEN_TEST_DEBUG if (intersection != item) { @@ -522,9 +522,8 @@ void assertIntersection(PossibleContents a, PossibleContents b, PossibleContents result) { auto intersection = a; - intersection.intersectWithFullCone(b); + intersection.intersect(b); EXPECT_EQ(intersection, result); - EXPECT_EQ(PossibleContents::haveIntersection(a, b), !result.isNone()); } @@ -810,6 +809,21 @@ TEST_F(PossibleContentsTest, TestStructCones) { assertIntersection( literalNullA, PossibleContents::fullConeType(funcref), none); + // Computing intersections is also supported with a Literal. + assertIntersection(i32Zero, i32Zero, i32Zero); + assertIntersection(i32One, i32Zero, none); + assertIntersection(i32Global1, i32Zero, i32Zero); + assertIntersection(funcGlobal, i32Zero, none); + assertIntersection( + PossibleContents::fullConeType(Type::i32), i32Zero, i32Zero); + assertIntersection(PossibleContents::fullConeType(Type::f64), i32Zero, none); + + // Computing intersections is also supported with empty contents. + assertIntersection(none, none, none); + assertIntersection(literalNullA, none, none); + assertIntersection(funcGlobal, none, none); + assertIntersection(PossibleContents::fullConeType(signature), none, none); + // Subcontents. This API only supports the case where one of the inputs is a // full cone type. // First, compare exact types to such a cone. |