@@ -29,21 +29,58 @@ using StructField = std::pair<HeapType, Index>;
2929
3030namespace StructUtils {
3131
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+
3253// A vector of a template type's values. One such vector will be used per struct
3354// type, where each element in the vector represents a field. We always assume
3455// that the vectors are pre-initialized to the right length before accessing any
3556// data, which this class enforces using assertions, and which is implemented in
3657// StructValuesMap.
3758template <typename T> struct StructValues : public std ::vector<T> {
3859 T& operator [](size_t index) {
60+ if (index == DescriptorIndex) {
61+ return desc;
62+ }
3963 assert (index < this ->size ());
4064 return std::vector<T>::operator [](index);
4165 }
4266
4367 const T& operator [](size_t index) const {
68+ if (index == DescriptorIndex) {
69+ return desc;
70+ }
4471 assert (index < this ->size ());
4572 return std::vector<T>::operator [](index);
4673 }
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;
4784};
4885
4986// Maps heap types to a StructValues for that heap type.
@@ -69,6 +106,7 @@ struct StructValuesMap : public std::unordered_map<HeapType, StructValues<T>> {
69106 for (Index i = 0 ; i < info.size (); i++) {
70107 combinedInfos[type][i].combine (info[i]);
71108 }
109+ combinedInfos[type].desc .combine (info.desc );
72110 }
73111 }
74112
@@ -80,6 +118,8 @@ struct StructValuesMap : public std::unordered_map<HeapType, StructValues<T>> {
80118 x.dump (o);
81119 o << " " ;
82120 };
121+ o << " desc: " ;
122+ vec.desc .dump (o);
83123 o << ' \n ' ;
84124 }
85125 }
@@ -129,14 +169,17 @@ struct FunctionStructValuesMap
129169//
130170// void noteCopy(HeapType type, Index index, T& info);
131171//
132- // * Note a read
172+ // * Note a read.
133173//
134174// void noteRead(HeapType type, Index index, T& info);
135175//
136176// We track information from struct.new and struct.set/struct.get separately,
137177// because in struct.new we know more about the type - we know the actual exact
138178// type being written to, and not just that it is of a subtype of the
139179// 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.
140183template <typename T, typename SubType>
141184struct StructScanner
142185 : public WalkerPass<PostWalker<StructScanner<T, SubType>>> {
@@ -168,6 +211,10 @@ struct StructScanner
168211 noteExpressionOrCopy (curr->operands [i], heapType, i, infos[i]);
169212 }
170213 }
214+
215+ if (curr->desc ) {
216+ self ().noteExpression (curr->desc , heapType, DescriptorIndex, infos.desc );
217+ }
171218 }
172219
173220 void visitStructSet (StructSet* curr) {
@@ -236,6 +283,42 @@ struct StructScanner
236283 noteExpressionOrCopy (curr->replacement , heapType, index, info);
237284 }
238285
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+
239322 void
240323 noteExpressionOrCopy (Expression* expr, HeapType type, Index index, T& info) {
241324 // Look at the value falling through, if it has the exact same type
@@ -268,8 +351,8 @@ struct StructScanner
268351 FunctionStructValuesMap<T>& functionSetGetInfos;
269352};
270353
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
273356//
274357// to.combine(from)
275358//
@@ -286,18 +369,32 @@ template<typename T> class TypeHierarchyPropagator {
286369
287370 SubTypes subTypes;
288371
372+ // Propagate given a StructValuesMap, which means we need to take into
373+ // account fields.
289374 void propagateToSuperTypes (StructValuesMap<T>& infos) {
290375 propagate (infos, false , true );
291376 }
292-
293377 void propagateToSubTypes (StructValuesMap<T>& infos) {
294378 propagate (infos, true , false );
295379 }
296-
297380 void propagateToSuperAndSubTypes (StructValuesMap<T>& infos) {
298381 propagate (infos, true , true );
299382 }
300383
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+
301398private:
302399 void propagate (StructValuesMap<T>& combinedInfos,
303400 bool toSubTypes,
@@ -320,6 +417,11 @@ template<typename T> class TypeHierarchyPropagator {
320417 work.push (*superType);
321418 }
322419 }
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+ }
323425 }
324426 }
325427
@@ -333,6 +435,41 @@ template<typename T> class TypeHierarchyPropagator {
333435 work.push (subType);
334436 }
335437 }
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+ }
336473 }
337474 }
338475 }
0 commit comments