@@ -44,6 +44,16 @@ template<typename T> struct StructValues : public std::vector<T> {
44
44
assert (index < this ->size ());
45
45
return std::vector<T>::operator [](index);
46
46
}
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;
47
57
};
48
58
49
59
// Maps heap types to a StructValues for that heap type.
@@ -69,6 +79,7 @@ struct StructValuesMap : public std::unordered_map<HeapType, StructValues<T>> {
69
79
for (Index i = 0 ; i < info.size (); i++) {
70
80
combinedInfos[type][i].combine (info[i]);
71
81
}
82
+ combinedInfos[type].desc .combine (info.desc );
72
83
}
73
84
}
74
85
@@ -80,6 +91,8 @@ struct StructValuesMap : public std::unordered_map<HeapType, StructValues<T>> {
80
91
x.dump (o);
81
92
o << " " ;
82
93
};
94
+ o << " desc: " ;
95
+ vec.desc .dump (o);
83
96
o << ' \n ' ;
84
97
}
85
98
}
@@ -129,14 +142,17 @@ struct FunctionStructValuesMap
129
142
//
130
143
// void noteCopy(HeapType type, Index index, T& info);
131
144
//
132
- // * Note a read
145
+ // * Note a read.
133
146
//
134
147
// void noteRead(HeapType type, Index index, T& info);
135
148
//
136
149
// We track information from struct.new and struct.set/struct.get separately,
137
150
// because in struct.new we know more about the type - we know the actual exact
138
151
// type being written to, and not just that it is of a subtype of the
139
152
// 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.
140
156
template <typename T, typename SubType>
141
157
struct StructScanner
142
158
: public WalkerPass<PostWalker<StructScanner<T, SubType>>> {
@@ -146,6 +162,8 @@ struct StructScanner
146
162
147
163
SubType& self () { return *static_cast <SubType*>(this ); }
148
164
165
+ static const Index DescriptorIndex = -1 ;
166
+
149
167
StructScanner (FunctionStructValuesMap<T>& functionNewInfos,
150
168
FunctionStructValuesMap<T>& functionSetGetInfos)
151
169
: functionNewInfos(functionNewInfos),
@@ -168,6 +186,10 @@ struct StructScanner
168
186
noteExpressionOrCopy (curr->operands [i], heapType, i, infos[i]);
169
187
}
170
188
}
189
+
190
+ if (curr->desc ) {
191
+ self ().noteExpression (curr->desc , heapType, DescriptorIndex, infos.desc );
192
+ }
171
193
}
172
194
173
195
void visitStructSet (StructSet* curr) {
@@ -236,6 +258,42 @@ struct StructScanner
236
258
noteExpressionOrCopy (curr->replacement , heapType, index, info);
237
259
}
238
260
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
+
239
297
void
240
298
noteExpressionOrCopy (Expression* expr, HeapType type, Index index, T& info) {
241
299
// Look at the value falling through, if it has the exact same type
@@ -268,8 +326,8 @@ struct StructScanner
268
326
FunctionStructValuesMap<T>& functionSetGetInfos;
269
327
};
270
328
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
273
331
//
274
332
// to.combine(from)
275
333
//
@@ -286,18 +344,32 @@ template<typename T> class TypeHierarchyPropagator {
286
344
287
345
SubTypes subTypes;
288
346
347
+ // Propagate given a StructValuesMap, which means we need to take into
348
+ // account fields.
289
349
void propagateToSuperTypes (StructValuesMap<T>& infos) {
290
350
propagate (infos, false , true );
291
351
}
292
-
293
352
void propagateToSubTypes (StructValuesMap<T>& infos) {
294
353
propagate (infos, true , false );
295
354
}
296
-
297
355
void propagateToSuperAndSubTypes (StructValuesMap<T>& infos) {
298
356
propagate (infos, true , true );
299
357
}
300
358
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
+
301
373
private:
302
374
void propagate (StructValuesMap<T>& combinedInfos,
303
375
bool toSubTypes,
@@ -320,6 +392,11 @@ template<typename T> class TypeHierarchyPropagator {
320
392
work.push (*superType);
321
393
}
322
394
}
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
+ }
323
400
}
324
401
}
325
402
@@ -333,6 +410,41 @@ template<typename T> class TypeHierarchyPropagator {
333
410
work.push (subType);
334
411
}
335
412
}
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
+ }
336
448
}
337
449
}
338
450
}
0 commit comments