@@ -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.
140156template <typename T, typename SubType>
141157struct 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+
301373private:
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 }
0 commit comments