@@ -29,21 +29,58 @@ using StructField = std::pair<HeapType, Index>;
29
29
30
30
namespace StructUtils {
31
31
32
+ // A value that has a single bool, and implements combine() so it can be used in
33
+ // StructValues.
34
+ struct CombinableBool {
35
+ bool value = false ;
36
+
37
+ CombinableBool () {}
38
+ CombinableBool (bool value) : value(value) {}
39
+
40
+ operator bool () const { return value; }
41
+
42
+ bool combine (const CombinableBool& other) {
43
+ if (!value && other.value ) {
44
+ value = true ;
45
+ return true ;
46
+ }
47
+ return false ;
48
+ }
49
+ };
50
+
51
+ static const Index DescriptorIndex = -1 ;
52
+
32
53
// A vector of a template type's values. One such vector will be used per struct
33
54
// type, where each element in the vector represents a field. We always assume
34
55
// that the vectors are pre-initialized to the right length before accessing any
35
56
// data, which this class enforces using assertions, and which is implemented in
36
57
// StructValuesMap.
37
58
template <typename T> struct StructValues : public std ::vector<T> {
38
59
T& operator [](size_t index) {
60
+ if (index == DescriptorIndex) {
61
+ return desc;
62
+ }
39
63
assert (index < this ->size ());
40
64
return std::vector<T>::operator [](index);
41
65
}
42
66
43
67
const T& operator [](size_t index) const {
68
+ if (index == DescriptorIndex) {
69
+ return desc;
70
+ }
44
71
assert (index < this ->size ());
45
72
return std::vector<T>::operator [](index);
46
73
}
74
+
75
+ // Store the descriptor as another field. (This could be a std::optional to
76
+ // indicate that the descriptor's existence depends on the type, but that
77
+ // would add overhead & code clutter (type checks). If there is no descriptor,
78
+ // this will just hang around with the default values, not harming anything
79
+ // except perhaps for looking a little odd during debugging. And whenever we
80
+ // combine() a non-existent descriptor, we are doing unneeded work, but the
81
+ // data here is typically just a few bools, so it is simpler and likely
82
+ // faster to just copy those rather than check if the type has a descriptor.)
83
+ T desc;
47
84
};
48
85
49
86
// Maps heap types to a StructValues for that heap type.
@@ -69,6 +106,7 @@ struct StructValuesMap : public std::unordered_map<HeapType, StructValues<T>> {
69
106
for (Index i = 0 ; i < info.size (); i++) {
70
107
combinedInfos[type][i].combine (info[i]);
71
108
}
109
+ combinedInfos[type].desc .combine (info.desc );
72
110
}
73
111
}
74
112
@@ -80,6 +118,8 @@ struct StructValuesMap : public std::unordered_map<HeapType, StructValues<T>> {
80
118
x.dump (o);
81
119
o << " " ;
82
120
};
121
+ o << " desc: " ;
122
+ vec.desc .dump (o);
83
123
o << ' \n ' ;
84
124
}
85
125
}
@@ -129,14 +169,17 @@ struct FunctionStructValuesMap
129
169
//
130
170
// void noteCopy(HeapType type, Index index, T& info);
131
171
//
132
- // * Note a read
172
+ // * Note a read.
133
173
//
134
174
// void noteRead(HeapType type, Index index, T& info);
135
175
//
136
176
// We track information from struct.new and struct.set/struct.get separately,
137
177
// because in struct.new we know more about the type - we know the actual exact
138
178
// type being written to, and not just that it is of a subtype of the
139
179
// instruction's type, which helps later.
180
+ //
181
+ // Descriptors are treated as fields in that we call the above functions on
182
+ // them. We pass DescriptorIndex for their index as a fake value.
140
183
template <typename T, typename SubType>
141
184
struct StructScanner
142
185
: public WalkerPass<PostWalker<StructScanner<T, SubType>>> {
@@ -168,6 +211,10 @@ struct StructScanner
168
211
noteExpressionOrCopy (curr->operands [i], heapType, i, infos[i]);
169
212
}
170
213
}
214
+
215
+ if (curr->desc ) {
216
+ self ().noteExpression (curr->desc , heapType, DescriptorIndex, infos.desc );
217
+ }
171
218
}
172
219
173
220
void visitStructSet (StructSet* curr) {
@@ -236,6 +283,42 @@ struct StructScanner
236
283
noteExpressionOrCopy (curr->replacement , heapType, index, info);
237
284
}
238
285
286
+ void visitRefCast (RefCast* curr) {
287
+ if (curr->desc ) {
288
+ // We may try to read a descriptor from anything arriving in |curr->ref|,
289
+ // but the only things that matter are the things we cast to: other types
290
+ // can lack a descriptor, and are skipped anyhow. So the only effective
291
+ // read is of the cast type.
292
+ handleDescRead (curr->getCastType ());
293
+ }
294
+ }
295
+
296
+ void visitBrOn (BrOn* curr) {
297
+ if (curr->desc &&
298
+ (curr->op == BrOnCastDesc || curr->op == BrOnCastDescFail)) {
299
+ handleDescRead (curr->getCastType ());
300
+ }
301
+ }
302
+
303
+ void visitRefGetDesc (RefGetDesc* curr) {
304
+ // Unlike a cast, anything in |curr->ref| may be read from.
305
+ handleDescRead (curr->ref ->type );
306
+ }
307
+
308
+ void handleDescRead (Type type) {
309
+ if (type == Type::unreachable) {
310
+ return ;
311
+ }
312
+ auto heapType = type.getHeapType ();
313
+ if (heapType.isStruct ()) {
314
+ // Any subtype of the reference here may be read from.
315
+ self ().noteRead (heapType,
316
+ DescriptorIndex,
317
+ functionSetGetInfos[this ->getFunction ()][heapType].desc );
318
+ return ;
319
+ }
320
+ }
321
+
239
322
void
240
323
noteExpressionOrCopy (Expression* expr, HeapType type, Index index, T& info) {
241
324
// Look at the value falling through, if it has the exact same type
@@ -268,8 +351,8 @@ struct StructScanner
268
351
FunctionStructValuesMap<T>& functionSetGetInfos;
269
352
};
270
353
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
354
+ // Helper class to propagate information to sub- and/or super- classes in the
355
+ // type hierarchy. While propagating it calls a method
273
356
//
274
357
// to.combine(from)
275
358
//
@@ -286,18 +369,32 @@ template<typename T> class TypeHierarchyPropagator {
286
369
287
370
SubTypes subTypes;
288
371
372
+ // Propagate given a StructValuesMap, which means we need to take into
373
+ // account fields.
289
374
void propagateToSuperTypes (StructValuesMap<T>& infos) {
290
375
propagate (infos, false , true );
291
376
}
292
-
293
377
void propagateToSubTypes (StructValuesMap<T>& infos) {
294
378
propagate (infos, true , false );
295
379
}
296
-
297
380
void propagateToSuperAndSubTypes (StructValuesMap<T>& infos) {
298
381
propagate (infos, true , true );
299
382
}
300
383
384
+ // Propagate on a simpler map of structs and infos (that is, not using
385
+ // separate values for the fields, as StructValuesMap does). This is useful
386
+ // when not tracking individual fields, but something more general about
387
+ // types.
388
+ using StructMap = std::unordered_map<HeapType, T>;
389
+
390
+ void propagateToSuperTypes (StructMap& infos) {
391
+ propagate (infos, false , true );
392
+ }
393
+ void propagateToSubTypes (StructMap& infos) { propagate (infos, true , false ); }
394
+ void propagateToSuperAndSubTypes (StructMap& infos) {
395
+ propagate (infos, true , true );
396
+ }
397
+
301
398
private:
302
399
void propagate (StructValuesMap<T>& combinedInfos,
303
400
bool toSubTypes,
@@ -320,6 +417,11 @@ template<typename T> class TypeHierarchyPropagator {
320
417
work.push (*superType);
321
418
}
322
419
}
420
+ // Propagate the descriptor to the super, if the super has one.
421
+ if (superType->getDescriptorType () &&
422
+ superInfos.desc .combine (infos.desc )) {
423
+ work.push (*superType);
424
+ }
323
425
}
324
426
}
325
427
@@ -333,6 +435,41 @@ template<typename T> class TypeHierarchyPropagator {
333
435
work.push (subType);
334
436
}
335
437
}
438
+ // Propagate the descriptor.
439
+ if (subInfos.desc .combine (infos.desc )) {
440
+ work.push (subType);
441
+ }
442
+ }
443
+ }
444
+ }
445
+ }
446
+
447
+ void propagate (StructMap& combinedInfos, bool toSubTypes, bool toSuperTypes) {
448
+ UniqueDeferredQueue<HeapType> work;
449
+ for (auto & [type, _] : combinedInfos) {
450
+ work.push (type);
451
+ }
452
+ while (!work.empty ()) {
453
+ auto type = work.pop ();
454
+ auto & info = combinedInfos[type];
455
+
456
+ if (toSuperTypes) {
457
+ // Propagate to the supertype.
458
+ if (auto superType = type.getDeclaredSuperType ()) {
459
+ auto & superInfo = combinedInfos[*superType];
460
+ if (superInfo.combine (info)) {
461
+ work.push (*superType);
462
+ }
463
+ }
464
+ }
465
+
466
+ if (toSubTypes) {
467
+ // Propagate shared fields to the subtypes.
468
+ for (auto subType : subTypes.getImmediateSubTypes (type)) {
469
+ auto & subInfo = combinedInfos[subType];
470
+ if (subInfo.combine (info)) {
471
+ work.push (subType);
472
+ }
336
473
}
337
474
}
338
475
}
0 commit comments