Skip to content
Merged
Changes from 1 commit
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
56d5165
work
kripken Aug 28, 2025
440016f
work
kripken Aug 28, 2025
bdf1045
work
kripken Aug 28, 2025
1e24c1d
work
kripken Aug 28, 2025
1e958b5
work
kripken Aug 28, 2025
c3ee741
work
kripken Aug 28, 2025
6086dc9
prep
kripken Aug 28, 2025
7ac2730
work
kripken Aug 28, 2025
b826d63
work
kripken Aug 28, 2025
427297f
work
kripken Aug 28, 2025
a2c4914
work
kripken Aug 28, 2025
991a129
work
kripken Aug 28, 2025
b457ed2
work
kripken Aug 28, 2025
5b25a96
work
kripken Aug 28, 2025
b91aef6
work
kripken Aug 28, 2025
a5697f3
work
kripken Aug 28, 2025
48c66ca
work
kripken Aug 28, 2025
e002c61
work
kripken Aug 28, 2025
5be8c21
work
kripken Aug 28, 2025
33e9e87
work
kripken Aug 28, 2025
3ed905a
work
kripken Aug 28, 2025
7b205cc
work
kripken Aug 28, 2025
b62a6ec
work
kripken Aug 28, 2025
98aa913
work
kripken Aug 29, 2025
c6f45fe
work
kripken Aug 29, 2025
4f05e6f
work
kripken Aug 29, 2025
1e4d63f
work
kripken Aug 29, 2025
757e50b
work
kripken Aug 29, 2025
42f00fc
work
kripken Aug 29, 2025
9075189
format
kripken Aug 29, 2025
cf29d90
work
kripken Aug 29, 2025
4116501
work
kripken Aug 29, 2025
62ef969
work
kripken Aug 29, 2025
bd5d8fc
test
kripken Aug 29, 2025
7a51d78
fix
kripken Aug 29, 2025
fed87f1
work
kripken Aug 29, 2025
a1d7068
wip
kripken Aug 29, 2025
0a4dc43
work
kripken Aug 29, 2025
90f1fb5
work
kripken Aug 29, 2025
6b722ef
work
kripken Aug 29, 2025
9e1409a
work
kripken Aug 29, 2025
c774441
work
kripken Aug 29, 2025
08cc60b
feedback: comments
kripken Aug 29, 2025
a490a8e
feedback: remove parallel sets
kripken Aug 29, 2025
88340c3
feedback: test field names
kripken Aug 29, 2025
88cc7dc
feedback: comment
kripken Aug 29, 2025
57f120c
tlively's idea?
kripken Aug 29, 2025
8a4286a
Revert "tlively's idea?"
kripken Aug 29, 2025
567ecbd
Optimize siblings
kripken Aug 29, 2025
2950719
fix nested global effects
kripken Aug 29, 2025
03ff3e5
fix
kripken Sep 2, 2025
55e7065
fix casts+test
kripken Sep 2, 2025
d406990
test
kripken Sep 2, 2025
b240e30
Update test/lit/passes/gto-desc.wast
kripken Sep 2, 2025
d0ec85a
Do not propagate a descriptor to a super without one
kripken Sep 2, 2025
276a188
Merge remote-tracking branch 'myself/gto.desc' into gto.desc
kripken Sep 2, 2025
46634b3
TODO
kripken Sep 2, 2025
eb266ee
Update src/ir/struct-utils.h
kripken Sep 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 30 additions & 13 deletions src/passes/GlobalTypeOptimization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down Expand Up @@ -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;
}
}
}
Expand All @@ -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) {
Expand Down Expand Up @@ -457,12 +457,24 @@ struct GlobalTypeOptimization : public Pass {
// Propagate.
descPropagator.propagateToSuperAndSubTypes(map);
Copy link
Member

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.

(type $super (sub (struct)))

(type $A (sub $super (descriptor $A.desc (struct))))
(type $A.desc (describes $A (struct)))

(type $B (sub $super (descriptor $B.desc (struct))))
(type $B.desc (describes $B (struct)))

Here propagateToSuperAndSubTypes will currently propagate descriptor information from $A to $B and vice versa. But that's overly conservative.

Copy link
Member

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.)

Copy link
Member Author

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?

Copy link
Member Author

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?

Copy link
Member

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.

Copy link
Member Author

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.

Copy link
Member

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.

Copy link
Member Author

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.

Copy link
Member

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?

Copy link
Member Author

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.


// 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());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to copy $A's contents. We can always just use trivial empty structs (which means that haveUnneededDescriptors can remain a set).

}
}
}
}
Expand Down Expand Up @@ -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);
Copy link
Member

Choose a reason for hiding this comment

The 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.

Copy link
Member Author

Choose a reason for hiding this comment

The 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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right, that ordering makes it extra tricky...

}
}
}
Expand Down