-
Notifications
You must be signed in to change notification settings - Fork 809
[Custom Descriptors] Optimize away unneeded descriptors in GlobalTypeOptimization #7872
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 1 commit
56d5165
440016f
bdf1045
1e24c1d
1e958b5
c3ee741
6086dc9
7ac2730
b826d63
427297f
a2c4914
991a129
b457ed2
5b25a96
b91aef6
a5697f3
48c66ca
e002c61
5be8c21
33e9e87
3ed905a
7b205cc
b62a6ec
98aa913
c6f45fe
4f05e6f
1e4d63f
757e50b
42f00fc
9075189
cf29d90
4116501
62ef969
bd5d8fc
7a51d78
fed87f1
a1d7068
0a4dc43
90f1fb5
6b722ef
9e1409a
c774441
08cc60b
a490a8e
88340c3
88cc7dc
57f120c
8a4286a
567ecbd
2950719
03ff3e5
55e7065
d406990
b240e30
d0ec85a
276a188
46634b3
eb266ee
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 |
---|---|---|
|
@@ -136,7 +136,7 @@ struct GlobalTypeOptimization : public Pass { | |
std::unordered_map<HeapType, std::vector<Index>> indexesAfterRemovals; | ||
|
||
// The types that no longer need a descriptor. | ||
std::unordered_set<HeapType> haveUnneededDescriptors; | ||
std::unordered_map<HeapType, std::optional<HeapType>> haveUnneededDescriptors; | ||
|
||
void run(Module* module) override { | ||
if (!module->features.hasGC()) { | ||
|
@@ -408,9 +408,9 @@ struct GlobalTypeOptimization : public Pass { | |
// could trap, we'd have no easy way to remove it in a global scope. | ||
// TODO: We could check and handle the global scope specifically, but | ||
// the trapsNeverHappen flag avoids this problem entirely anyhow. | ||
if (!dataFromSubsAndSupers.desc.hasRead && | ||
(!dataFromSubsAndSupers.desc.hasWrite || trapsNeverHappen)) { | ||
haveUnneededDescriptors.insert(type); | ||
if (!dataFromSupers.desc.hasRead && | ||
(!dataFromSupers.desc.hasWrite || trapsNeverHappen)) { | ||
haveUnneededDescriptors[type] = std::nullopt; | ||
} | ||
} | ||
} | ||
|
@@ -422,12 +422,12 @@ struct GlobalTypeOptimization : public Pass { | |
// B -> B.desc | ||
// | ||
// Here the descriptors subtype, but *not* the describees. We cannot | ||
// remove A's descriptor without also removing $B's, so we need to propagate | ||
// that "must remain a descriptor" property among descriptors. | ||
// remove A's descriptor by itself, not without also removing B's, so we | ||
// need to do something more (see below). | ||
if (!haveUnneededDescriptors.empty()) { | ||
|
||
// To find problem situations, we'll progagate the property of a | ||
// descriptor being needed because of descriptor subtyping. | ||
struct DescriptorInfo { | ||
// Whether this descriptor is needed - it must keep describing. | ||
bool needed = false; | ||
|
||
bool combine(const DescriptorInfo& other) { | ||
|
@@ -457,12 +457,24 @@ struct GlobalTypeOptimization : public Pass { | |
// Propagate. | ||
descPropagator.propagateToSuperAndSubTypes(map); | ||
|
||
// Remove optimization opportunities that the propagation ruled out. | ||
// Find optimization opportunities that the propagation ruled out. | ||
for (auto& [type, info] : map) { | ||
if (info.needed) { | ||
auto described = type.getDescribedType(); | ||
assert(described); | ||
haveUnneededDescriptors.erase(*described); | ||
if (haveUnneededDescriptors.count(*described)) { | ||
// We are in a situation like on the left: | ||
// | ||
// A -> A.desc A A.desc <- A2 | ||
// ^ => ^ | ||
// B -> B.desc B -> B.desc | ||
// | ||
// We want to remove A's descriptor, but cannot remove B's. To do | ||
// that, we add a new type A2 for A.desc to describe, which keeps | ||
// the property that A.desc and B.desc are a parent/child pair of | ||
// descriptors, which is necessary for validation. | ||
haveUnneededDescriptors[*described] = HeapType(described->getStruct()); | ||
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. No need to copy |
||
} | ||
} | ||
} | ||
} | ||
|
@@ -555,10 +567,15 @@ struct GlobalTypeOptimization : public Pass { | |
typeBuilder.setDescriptor(i, std::nullopt); | ||
} | ||
|
||
// Remove an unneeded describes. | ||
// Remove or replace describings. | ||
if (auto described = oldType.getDescribedType()) { | ||
if (parent.haveUnneededDescriptors.count(*described)) { | ||
typeBuilder.setDescribed(i, std::nullopt); | ||
auto iter = parent.haveUnneededDescriptors.find(*described); | ||
if (iter != parent.haveUnneededDescriptors.end()) { | ||
auto value = iter->second; | ||
if (value) { | ||
value = getTempHeapType(*value); | ||
} | ||
typeBuilder.setDescribed(i, value); | ||
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. This isn't quite right. You need to grow the type builder to make space for the new described type, put an empty struct in that new last entry of the type builder, set it to be described by the current type, and set the current type to describe the new type. 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. Makes sense - this is definitely not as simple as I'd hoped. We need to add some kind of new hook in TypeRewriter that lets the user grow the TypeBuilder, and also to somehow set up the right order (we can't append the described type after the descriptor). I added a TODO for this. 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. Oh right, that ordering makes it extra tricky... |
||
} | ||
} | ||
} | ||
|
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.
Ah, propagating descriptor info up to types without descriptors actually produces worse results here. If two different types have descriptors and they share a supertype without a descriptor, we should not propagate constraints on one descriptor to the other.
Here
propagateToSuperAndSubTypes
will currently propagate descriptor information from$A
to$B
and vice versa. But that's overly conservative.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.
(Actually it doesn't matter in this particular case because the property being propagated is not tied to the descriptor field, but I guess this reminded me of the issue.)
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.
Hmm, this is descPropagator: it propagates along descriptors. In your example, the describees are where there is subtyping.
And, for the descriptors, I think this problem can't happen? Doesn't a supertype of a descriptor need to be a descriptor?
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.
Looks like our last comments here raced. Are you saying this might happen elsewhere?
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.
Right, I realized after I wrote the opening comment that there's no problem with this particular code. But e.g. using
$A.desc
should not force$B.desc
to be kept around.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.
Thanks, yes, now I see. Fixed in the last commit: now that we have proper descriptor subtype propagation (in the code after it in the source), we can propagate describees only to subtypes, allowing that optimization.
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.
We should still fix the propagator not to propagate descriptor information through types that don't have descriptors, though.
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.
Done in d0ec85a
I don't quite see how to test that, though, but it is at least good for efficiency.
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 might become more important in CFP?
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.
Hmm, yeah, other passes might notice, good point.