-
Notifications
You must be signed in to change notification settings - Fork 816
[Wasm GC] [GUFA] Add initial ConeType support #5116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 12 commits
51cb0b9
6dc7292
ec0b3d6
617690a
f750d67
a34e470
afe8b5d
3af1107
c7c6b93
fa7a116
7927fbe
e504b0b
9a057dc
932da84
e164411
f50a8d4
973586d
8261dc0
650ce4e
c0aa7e4
3a1dbfd
85f31a5
81bcf3e
43da163
dee3da0
c6bc347
47fffc4
1773856
af84e99
36ebd4c
2b1bfc7
8da6585
fbf99a9
5b577e6
5b9eaba
ad3aefa
126fd22
c435d5a
6624aa3
ec5ccc2
35b43af
a78b734
eda95c9
cfd79c6
6769447
2443410
932b1e7
1765c4f
bbeb87d
e855951
48d99a0
9e742f1
5df1d38
a61239d
ab75451
7e7d36b
4f2ad9e
416b586
1bcab96
433f4be
680041c
5af82bb
e511255
fdc9930
c9383c7
4680ecd
b1f5fb9
a68dbfe
3cff9cb
fd277a3
9b8e7b6
4662f54
4b50ecc
d4f0e7f
883cad8
953548e
282b678
9a77fed
1217eeb
aa74ce9
aea36da
ad825a7
9af078c
bfd3509
8ca04e4
ce84508
44baef0
cb68fe1
0e3c2b2
0fc4e13
54fa5cc
446c2de
c27187e
e830086
6a3a208
d7152dd
dc5e63a
643b539
3d4b099
5fc0d71
f5454be
64b7136
3ef1a32
07762db
6fd8c96
f6806ad
18c7348
2bc7826
0a20071
03eb8c5
e46a096
0dc1cb1
552e2e8
97cd211
3b611f9
d792619
f3bcdc7
ef917a2
cbfffd0
781f043
3cf100d
5808d74
6102dca
8618c0a
a2269de
824c864
2290fd2
c898267
d24fc0b
9999985
9737075
1f272ab
b280cc1
6f182f8
85e2ac3
415a8ea
05ea12a
d87fc2b
78002dd
fd520f8
2e0e687
6926cb1
9cfdc3f
9117223
83d720a
a1245ad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -89,6 +89,15 @@ void PossibleContents::combine(const PossibleContents& other) { | |
return; | ||
} | ||
|
||
auto lub = Type::getLeastUpperBound(type, otherType); | ||
if (lub == Type::none) { | ||
// The types are not in the same hierarchy. | ||
value = Many(); | ||
return; | ||
} | ||
|
||
// 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()) { | ||
// Only one of them can be null here, since we already handled the case | ||
|
@@ -110,43 +119,35 @@ void PossibleContents::combine(const PossibleContents& other) { | |
} | ||
} | ||
|
||
// Before we give up and return Many, try to find a ConeType that describes | ||
// both inputs. | ||
auto lub = Type::getLeastUpperBound(type, otherType); | ||
if (lub != Type::none) { | ||
// We found a shared ancestor. Next 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; | ||
Index newDepth; | ||
if (depth == FullDepth || otherDepth == FullDepth) { | ||
// At least one has full (infinite) depth, so we know the new depth must | ||
// be the same. | ||
newDepth = FullDepth; | ||
} else { | ||
// The depth we need under the lub is how far from the lub we are, plus | ||
// 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 lubDepthFromRoot = lub.getHeapType().getDepth(); | ||
assert(lubDepthFromRoot <= depthFromRoot); | ||
assert(lubDepthFromRoot <= otherDepthFromRoot); | ||
Index depthUnderLub = depthFromRoot - lubDepthFromRoot + depth; | ||
Index otherDepthUnderLub = | ||
otherDepthFromRoot - lubDepthFromRoot + otherDepth; | ||
|
||
// The total cone must be big enough to contain all the above. | ||
newDepth = std::max(depthUnderLub, otherDepthUnderLub); | ||
} | ||
|
||
value = ConeType{lub, newDepth}; | ||
return; | ||
} | ||
|
||
// Nothing else possible combines in an interesting way; emit a Many. | ||
value = Many(); | ||
// 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; | ||
Index newDepth; | ||
if (depth == FullDepth || otherDepth == FullDepth) { | ||
// At least one has full (infinite) depth, so we know the new depth must | ||
// be the same. | ||
newDepth = FullDepth; | ||
} else { | ||
// The depth we need under the lub is how far from the lub we are, plus | ||
// 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 lubDepthFromRoot = lub.getHeapType().getDepth(); | ||
assert(lubDepthFromRoot <= depthFromRoot); | ||
assert(lubDepthFromRoot <= otherDepthFromRoot); | ||
Index depthUnderLub = depthFromRoot - lubDepthFromRoot + depth; | ||
Index otherDepthUnderLub = | ||
otherDepthFromRoot - lubDepthFromRoot + otherDepth; | ||
|
||
// The total cone must be big enough to contain all the above. | ||
newDepth = std::max(depthUnderLub, otherDepthUnderLub); | ||
} | ||
|
||
value = ConeType{lub, newDepth}; | ||
} | ||
|
||
void PossibleContents::intersectWithFullCone(const PossibleContents& other) { | ||
|
@@ -166,6 +167,9 @@ void PossibleContents::intersectWithFullCone(const PossibleContents& other) { | |
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(); | ||
|
@@ -190,12 +194,12 @@ void PossibleContents::intersectWithFullCone(const PossibleContents& other) { | |
return; | ||
} | ||
|
||
// If the heap types are not compatible then the intersection is either | ||
// nothing or a null. | ||
// If the heap types are not compatible then they are in separate hierarchies | ||
// and there is no intersection. | ||
auto isSubType = HeapType::isSubType(heapType, otherHeapType); | ||
auto otherIsSubType = HeapType::isSubType(otherHeapType, heapType); | ||
if (!isSubType && !otherIsSubType) { | ||
setNoneOrNull(); | ||
value = None(); | ||
return; | ||
} | ||
|
||
|
@@ -213,10 +217,7 @@ void PossibleContents::intersectWithFullCone(const PossibleContents& other) { | |
// refactoring. | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, atm we just have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would be helpful, thanks! |
||
|
||
// An interesting non-empty intersection that is a new cone which differs from | ||
// both the original ones. (This must be an intersection of cones, since by | ||
// assumption |other| is a cone, and another cone is the only shape that can | ||
// have a non-empty intersection with it that differs from them both.) | ||
// Intersect the cones, as there is no more specific information we can use. | ||
auto depthFromRoot = heapType.getDepth(); | ||
auto otherDepthFromRoot = otherHeapType.getDepth(); | ||
|
||
|
@@ -249,8 +250,8 @@ void PossibleContents::intersectWithFullCone(const PossibleContents& other) { | |
// heapType .. | ||
// \ | ||
*/ | ||
// E.g. if |this| is a cone of size 10, and |otherHeapType| is an immediate | ||
// subtype of |this|, then the new cone must be of size 9. | ||
// E.g. if |this| is a cone of depth 10, and |otherHeapType| is an immediate | ||
// subtype of |this|, then the new cone must be of depth 9. | ||
auto newDepth = getCone().depth; | ||
if (newHeapType == otherHeapType) { | ||
assert(depthFromRoot <= otherDepthFromRoot); | ||
|
@@ -292,26 +293,21 @@ bool PossibleContents::haveIntersection(const PossibleContents& a, | |
|
||
// From here on we focus on references. | ||
|
||
if (aType.isNullable() && bType.isNullable()) { | ||
// Null is possible on both sides. Assume that an intersection can exist, | ||
// but we could be more precise here and check if the types belong to | ||
// different hierarchies, in which case the nulls would differ TODO. For | ||
// now we only use this API from the RefEq logic, so this is fully precise. | ||
auto aHeapType = aType.getHeapType(); | ||
auto bHeapType = bType.getHeapType(); | ||
|
||
if (aType.isNullable() && bType.isNullable() && | ||
aHeapType.getBottom() == bHeapType.getBottom()) { | ||
// A compatible null is possible on both sides. | ||
return true; | ||
} | ||
|
||
// We ruled out a null on both sides, so at least one is non-nullable. If the | ||
// other is a null then no chance for an intersection remains. | ||
// We ruled out having a compatible null on both sides. If one is simply a | ||
// null then no chance for an intersection remains. | ||
if (a.isNull() || b.isNull()) { | ||
return false; | ||
} | ||
|
||
// From here on we focus on references and can ignore the case of null - any | ||
// intersection must be of a non-null value, so we can focus on the heap | ||
// types. | ||
auto aHeapType = aType.getHeapType(); | ||
auto bHeapType = bType.getHeapType(); | ||
|
||
auto aSubB = HeapType::isSubType(aHeapType, bHeapType); | ||
auto bSubA = HeapType::isSubType(bHeapType, aHeapType); | ||
if (!aSubB && !bSubA) { | ||
|
@@ -320,6 +316,10 @@ bool PossibleContents::haveIntersection(const PossibleContents& a, | |
return false; | ||
} | ||
|
||
// From here on we focus on references and can ignore the case of null - any | ||
// intersection must be of a non-null value, so we can focus on the heap | ||
// types. | ||
|
||
auto aDepthFromRoot = aHeapType.getDepth(); | ||
auto bDepthFromRoot = bHeapType.getDepth(); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -244,7 +244,8 @@ struct GUFAOptimizer | |
if (refType.isRef()) { | ||
// We have some knowledge of the type here. Use that to optimize: RefTest | ||
// returns 1 if the input is of a subtype of the intended type, that is, | ||
// we are looking for a type in that cone of types. | ||
// we are looking for a type in that cone of types. (Note that we use a | ||
// non-nullable cone since only a non-null can pass the test.) | ||
auto intendedContents = | ||
PossibleContents::fullConeType(Type(curr->intendedType, NonNullable)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be helpful to have a short comment on why this is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added. |
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -284,6 +284,13 @@ TEST_F(PossibleContentsTest, TestOracleMinimal) { | |
void assertHaveIntersection(PossibleContents a, PossibleContents b) { | ||
EXPECT_TRUE(PossibleContents::haveIntersection(a, b)); | ||
EXPECT_TRUE(PossibleContents::haveIntersection(b, a)); | ||
#if BINARYEN_TEST_DEBUG | ||
if (!PossibleContents::haveIntersection(a, b) || | ||
!PossibleContents::haveIntersection(b, a)) { | ||
std::cout << "\nFailure: no intersection:\n" << a << '\n' << b << '\n'; | ||
abort(); | ||
} | ||
#endif | ||
Comment on lines
+287
to
+293
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need this? If this is the case, doesn't this crash in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
void assertLackIntersection(PossibleContents a, PossibleContents b) { | ||
EXPECT_FALSE(PossibleContents::haveIntersection(a, b)); | ||
|
@@ -304,10 +311,12 @@ TEST_F(PossibleContentsTest, TestIntersection) { | |
assertLackIntersection(exactI32, exactAnyref); | ||
assertLackIntersection(i32Zero, exactAnyref); | ||
|
||
// But nullable ones can - the null can be the intersection. | ||
assertHaveIntersection(exactFuncSignatureType, exactAnyref); | ||
// But nullable ones can - the null can be the intersection, if they are not | ||
// in separate hierarchies. | ||
assertHaveIntersection(exactFuncSignatureType, funcNull); | ||
assertHaveIntersection(anyNull, funcNull); | ||
|
||
assertLackIntersection(exactFuncSignatureType, exactAnyref); | ||
assertLackIntersection(anyNull, funcNull); | ||
|
||
// Identical types might. | ||
assertHaveIntersection(exactI32, exactI32); | ||
|
@@ -326,12 +335,8 @@ TEST_F(PossibleContentsTest, TestIntersection) { | |
assertHaveIntersection(funcGlobal, exactNonNullFuncSignatureType); | ||
assertHaveIntersection(nonNullFuncGlobal, exactNonNullFuncSignatureType); | ||
|
||
// Neither is a subtype of the other, but nulls are possible, so a null can be | ||
// the intersection. | ||
assertHaveIntersection(funcGlobal, anyGlobal); | ||
|
||
// Without null on one side, we cannot intersect. | ||
assertLackIntersection(nonNullFuncGlobal, anyGlobal); | ||
// Separate hierarchies. | ||
assertLackIntersection(funcGlobal, anyGlobal); | ||
} | ||
|
||
TEST_F(PossibleContentsTest, TestIntersectWithCombinations) { | ||
|
@@ -662,6 +667,15 @@ TEST_F(PossibleContentsTest, TestStructCones) { | |
assertLackIntersection(PossibleContents::coneType(nnA, 1), | ||
PossibleContents::coneType(nnD, 100)); | ||
|
||
// Neither is a subtype of the other, but nulls are possible, so a null can be | ||
// the intersection. | ||
assertHaveIntersection(PossibleContents::fullConeType(nullA), | ||
PossibleContents::fullConeType(nullE)); | ||
|
||
// Without null on one side, we cannot intersect. | ||
assertLackIntersection(PossibleContents::fullConeType(nnA), | ||
PossibleContents::fullConeType(nullE)); | ||
|
||
// Computing intersections is supported with a full cone type. | ||
assertIntersection(none, PossibleContents::fullConeType(nnA), none); | ||
assertIntersection(many, | ||
|
@@ -729,6 +743,10 @@ TEST_F(PossibleContentsTest, TestStructCones) { | |
PossibleContents::fullConeType(signature), | ||
PossibleContents::fullConeType(signature)); | ||
|
||
// Incompatible hierarchies have no intersection. | ||
assertIntersection( | ||
literalNullA, PossibleContents::fullConeType(funcref), 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. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The case of
None
andMany
are handled above, but canthis
be aLiteral
orGlobal
? In that case, what it doesn't have a heap type?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does look unclear, I'll add a comment. The reason is the intersection with another reference type is not empty, so this must be a reference type itself.