Skip to content

Commit 7ac88c4

Browse files
authored
[Custom Descriptors] Optimize away unneeded descriptors in GlobalTypeOptimization (#7872)
This adds descriptor support to struct-utils.h as an extra field. It is reported as index "-1" when an index is used in the API, allowing passes to skip it where irrelevant. That new support is then used in a single pass, GTO, where we just see if a descriptor can be removed, and remove it if so.
1 parent 64ba239 commit 7ac88c4

File tree

6 files changed

+1586
-45
lines changed

6 files changed

+1586
-45
lines changed

src/ir/struct-utils.h

Lines changed: 117 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@ template<typename T> struct StructValues : public std::vector<T> {
4444
assert(index < this->size());
4545
return std::vector<T>::operator[](index);
4646
}
47+
48+
// Store the descriptor as another field. (This could be a std::optional to
49+
// indicate that the descriptor's existence depends on the type, but that
50+
// would add overhead & code clutter (type checks). If there is no descriptor,
51+
// this will just hang around with the default values, not harming anything
52+
// except perhaps for looking a little odd during debugging. And whenever we
53+
// combine() a non-existent descriptor, we are doing unneeded work, but the
54+
// data here is typically just a few bools, so it is simpler and likely
55+
// faster to just copy those rather than check if the type has a descriptor.)
56+
T desc;
4757
};
4858

4959
// Maps heap types to a StructValues for that heap type.
@@ -69,6 +79,7 @@ struct StructValuesMap : public std::unordered_map<HeapType, StructValues<T>> {
6979
for (Index i = 0; i < info.size(); i++) {
7080
combinedInfos[type][i].combine(info[i]);
7181
}
82+
combinedInfos[type].desc.combine(info.desc);
7283
}
7384
}
7485

@@ -80,6 +91,8 @@ struct StructValuesMap : public std::unordered_map<HeapType, StructValues<T>> {
8091
x.dump(o);
8192
o << " ";
8293
};
94+
o << " desc: ";
95+
vec.desc.dump(o);
8396
o << '\n';
8497
}
8598
}
@@ -129,14 +142,17 @@ struct FunctionStructValuesMap
129142
//
130143
// void noteCopy(HeapType type, Index index, T& info);
131144
//
132-
// * Note a read
145+
// * Note a read.
133146
//
134147
// void noteRead(HeapType type, Index index, T& info);
135148
//
136149
// We track information from struct.new and struct.set/struct.get separately,
137150
// because in struct.new we know more about the type - we know the actual exact
138151
// type being written to, and not just that it is of a subtype of the
139152
// instruction's type, which helps later.
153+
//
154+
// Descriptors are treated as fields in that we call the above functions on
155+
// them. We pass DescriptorIndex for their index as a fake value.
140156
template<typename T, typename SubType>
141157
struct StructScanner
142158
: public WalkerPass<PostWalker<StructScanner<T, SubType>>> {
@@ -146,6 +162,8 @@ struct StructScanner
146162

147163
SubType& self() { return *static_cast<SubType*>(this); }
148164

165+
static const Index DescriptorIndex = -1;
166+
149167
StructScanner(FunctionStructValuesMap<T>& functionNewInfos,
150168
FunctionStructValuesMap<T>& functionSetGetInfos)
151169
: functionNewInfos(functionNewInfos),
@@ -168,6 +186,10 @@ struct StructScanner
168186
noteExpressionOrCopy(curr->operands[i], heapType, i, infos[i]);
169187
}
170188
}
189+
190+
if (curr->desc) {
191+
self().noteExpression(curr->desc, heapType, DescriptorIndex, infos.desc);
192+
}
171193
}
172194

173195
void visitStructSet(StructSet* curr) {
@@ -236,6 +258,42 @@ struct StructScanner
236258
noteExpressionOrCopy(curr->replacement, heapType, index, info);
237259
}
238260

261+
void visitRefCast(RefCast* curr) {
262+
if (curr->desc) {
263+
// We may try to read a descriptor from anything arriving in |curr->ref|,
264+
// but the only things that matter are the things we cast to: other types
265+
// can lack a descriptor, and are skipped anyhow. So the only effective
266+
// read is of the cast type.
267+
handleDescRead(curr->getCastType());
268+
}
269+
}
270+
271+
void visitBrOn(BrOn* curr) {
272+
if (curr->desc &&
273+
(curr->op == BrOnCastDesc || curr->op == BrOnCastDescFail)) {
274+
handleDescRead(curr->getCastType());
275+
}
276+
}
277+
278+
void visitRefGetDesc(RefGetDesc* curr) {
279+
// Unlike a cast, anything in |curr->ref| may be read from.
280+
handleDescRead(curr->ref->type);
281+
}
282+
283+
void handleDescRead(Type type) {
284+
if (type == Type::unreachable) {
285+
return;
286+
}
287+
auto heapType = type.getHeapType();
288+
if (heapType.isStruct()) {
289+
// Any subtype of the reference here may be read from.
290+
self().noteRead(heapType,
291+
DescriptorIndex,
292+
functionSetGetInfos[this->getFunction()][heapType].desc);
293+
return;
294+
}
295+
}
296+
239297
void
240298
noteExpressionOrCopy(Expression* expr, HeapType type, Index index, T& info) {
241299
// Look at the value falling through, if it has the exact same type
@@ -268,8 +326,8 @@ struct StructScanner
268326
FunctionStructValuesMap<T>& functionSetGetInfos;
269327
};
270328

271-
// Helper class to propagate information about fields to sub- and/or super-
272-
// classes in the type hierarchy. While propagating it calls a method
329+
// Helper class to propagate information to sub- and/or super- classes in the
330+
// type hierarchy. While propagating it calls a method
273331
//
274332
// to.combine(from)
275333
//
@@ -286,18 +344,32 @@ template<typename T> class TypeHierarchyPropagator {
286344

287345
SubTypes subTypes;
288346

347+
// Propagate given a StructValuesMap, which means we need to take into
348+
// account fields.
289349
void propagateToSuperTypes(StructValuesMap<T>& infos) {
290350
propagate(infos, false, true);
291351
}
292-
293352
void propagateToSubTypes(StructValuesMap<T>& infos) {
294353
propagate(infos, true, false);
295354
}
296-
297355
void propagateToSuperAndSubTypes(StructValuesMap<T>& infos) {
298356
propagate(infos, true, true);
299357
}
300358

359+
// Propagate on a simpler map of structs and infos (that is, not using
360+
// separate values for the fields, as StructValuesMap does). This is useful
361+
// when not tracking individual fields, but something more general about
362+
// types.
363+
using StructMap = std::unordered_map<HeapType, T>;
364+
365+
void propagateToSuperTypes(StructMap& infos) {
366+
propagate(infos, false, true);
367+
}
368+
void propagateToSubTypes(StructMap& infos) { propagate(infos, true, false); }
369+
void propagateToSuperAndSubTypes(StructMap& infos) {
370+
propagate(infos, true, true);
371+
}
372+
301373
private:
302374
void propagate(StructValuesMap<T>& combinedInfos,
303375
bool toSubTypes,
@@ -320,6 +392,11 @@ template<typename T> class TypeHierarchyPropagator {
320392
work.push(*superType);
321393
}
322394
}
395+
// Propagate the descriptor to the super, if the super has one.
396+
if (superType->getDescriptorType() &&
397+
superInfos.desc.combine(infos.desc)) {
398+
work.push(*superType);
399+
}
323400
}
324401
}
325402

@@ -333,6 +410,41 @@ template<typename T> class TypeHierarchyPropagator {
333410
work.push(subType);
334411
}
335412
}
413+
// Propagate the descriptor.
414+
if (subInfos.desc.combine(infos.desc)) {
415+
work.push(subType);
416+
}
417+
}
418+
}
419+
}
420+
}
421+
422+
void propagate(StructMap& combinedInfos, bool toSubTypes, bool toSuperTypes) {
423+
UniqueDeferredQueue<HeapType> work;
424+
for (auto& [type, _] : combinedInfos) {
425+
work.push(type);
426+
}
427+
while (!work.empty()) {
428+
auto type = work.pop();
429+
auto& info = combinedInfos[type];
430+
431+
if (toSuperTypes) {
432+
// Propagate to the supertype.
433+
if (auto superType = type.getDeclaredSuperType()) {
434+
auto& superInfo = combinedInfos[*superType];
435+
if (superInfo.combine(info)) {
436+
work.push(*superType);
437+
}
438+
}
439+
}
440+
441+
if (toSubTypes) {
442+
// Propagate shared fields to the subtypes.
443+
for (auto subType : subTypes.getImmediateSubTypes(type)) {
444+
auto& subInfo = combinedInfos[subType];
445+
if (subInfo.combine(info)) {
446+
work.push(subType);
447+
}
336448
}
337449
}
338450
}

src/passes/ConstantFieldPropagation.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,11 @@ struct PCVScanner
443443
}
444444

445445
void noteCopy(HeapType type, Index index, PossibleConstantValues& info) {
446+
if (index == DescriptorIndex) {
447+
// We cannot continue on below, where we index into the vector of values.
448+
return;
449+
}
450+
446451
// Note copies, as they must be considered later. See the comment on the
447452
// propagation of values below.
448453
functionCopyInfos[getFunction()][type][index] = true;

0 commit comments

Comments
 (0)