@@ -91,15 +91,15 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
9191 // subtyping and new infos (information about struct.news).
9292 std::unique_ptr<Pass> create () override {
9393 return std::make_unique<FunctionOptimizer>(
94- propagatedInfos, subTypes, rawNewInfos , refTest);
94+ propagatedInfos, refTestInfos, subTypes , refTest);
9595 }
9696
9797 FunctionOptimizer (const PCVStructValuesMap& propagatedInfos,
98+ const PCVStructValuesMap& refTestInfos,
9899 const SubTypes& subTypes,
99- const PCVStructValuesMap& rawNewInfos,
100100 bool refTest)
101- : propagatedInfos(propagatedInfos), subTypes(subTypes ),
102- rawNewInfos (rawNewInfos ), refTest(refTest) {}
101+ : propagatedInfos(propagatedInfos), refTestInfos(refTestInfos ),
102+ subTypes (subTypes ), refTest(refTest) {}
103103
104104 template <typename T> std::optional<HeapType> getRelevantHeapType (T* ref) {
105105 auto type = ref->type ;
@@ -210,7 +210,7 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
210210 // on simply applying a constant. However, we can try to use a ref.test, if
211211 // that is allowed.
212212 if (!info.isConstant ()) {
213- if (refTest) {
213+ if (refTest && !ref-> type . isExact () ) {
214214 optimizeUsingRefTest (curr, ref, index);
215215 }
216216 return ;
@@ -233,22 +233,6 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
233233 auto refType = ref->type ;
234234 auto refHeapType = refType.getHeapType ();
235235
236- // We only handle immutable fields in this function, as we will be looking
237- // at |rawNewInfos|. That is, we are trying to see when a type and its
238- // subtypes have different values (so that we can differentiate between them
239- // using a ref.test), and those differences are lost in |propagatedInfos|,
240- // which has propagated to relevant types so that we can do a single check
241- // to see what value could be there. So we need to use something more
242- // precise, |rawNewInfos|, which tracks the values written to struct.news,
243- // where we know the type exactly (unlike with a struct.set). But for that
244- // reason the field must be immutable, so that it is valid to only look at
245- // the struct.news. (A more complex flow analysis could do better here, but
246- // would be far beyond the scope of this pass.)
247- if (index != StructUtils::DescriptorIndex &&
248- GCTypeUtils::getField (refType, index)->mutable_ == Mutable) {
249- return ;
250- }
251-
252236 // We seek two possible constant values. For each we track the constant and
253237 // the types that have that constant. For example, if we have types A, B, C
254238 // and A and B have 42 in their field, and C has 1337, then we'd have this:
@@ -283,13 +267,17 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
283267 return ;
284268 }
285269
286- auto iter = rawNewInfos .find ({type, Exact});
287- if (iter == rawNewInfos .end ()) {
288- // This type has no struct.news , so we can ignore it: it is abstract.
270+ auto iter = refTestInfos .find ({type, Exact});
271+ if (iter == refTestInfos .end ()) {
272+ // This type has no allocations , so we can ignore it: it is abstract.
289273 return ;
290274 }
291275
292276 auto value = iter->second [index];
277+ if (!value.hasNoted ()) {
278+ // Also abstract and ignorable.
279+ return ;
280+ }
293281 if (!value.isConstant ()) {
294282 // The value here is not constant, so give up entirely.
295283 fail = true ;
@@ -409,8 +397,8 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
409397
410398private:
411399 const PCVStructValuesMap& propagatedInfos;
400+ const PCVStructValuesMap& refTestInfos;
412401 const SubTypes& subTypes;
413- const PCVStructValuesMap& rawNewInfos;
414402 const bool refTest;
415403
416404 bool changed = false ;
@@ -492,20 +480,13 @@ struct ConstantFieldPropagation : public Pass {
492480 scanner.runOnModuleCode (runner, module );
493481
494482 // Combine the data from the functions.
495- PCVStructValuesMap combinedNewInfos, combinedSetInfos;
496- functionNewInfos.combineInto (combinedNewInfos );
483+ PCVStructValuesMap combinedSetInfos;
484+ functionNewInfos.combineInto (combinedSetInfos );
497485 functionSetInfos.combineInto (combinedSetInfos);
498486 BoolStructValuesMap combinedCopyInfos;
499487 functionCopyInfos.combineInto (combinedCopyInfos);
500488
501- // Prepare data we will need later.
502- SubTypes subTypes (*module );
503-
504- // Copy the unpropagated data before we propagate. We use this in precise
505- // lookups.
506- auto rawNewInfos = combinedNewInfos;
507-
508- // Handle subtyping. |combinedInfo| so far contains data that represents
489+ // Handle subtyping. |combinedSetInfos| so far contains data that represents
509490 // each struct.new and struct.set's operation on the struct type used in
510491 // that instruction. That is, if we do a struct.set to type T, the value was
511492 // noted for type T. But our actual goal is to answer questions about
@@ -532,10 +513,11 @@ struct ConstantFieldPropagation : public Pass {
532513 // efficient, we therefore propagate information about the possible values
533514 // in each field to both subtypes and supertypes.
534515 //
535- // struct.new on the other hand knows exactly what type is being written to,
536- // and so given a get of $A and a new of $B, the new is relevant for the get
537- // iff $A is a subtype of $B, so we only need to propagate in one direction
538- // there, to supertypes.
516+ // Values written in struct.news are equivalent to values written to exact
517+ // references. In both cases, the propagation to subtypes will not do
518+ // anything because an exact reference has no non-trivial subtypes. This
519+ // works out because a set of a field of an exact reference (or an
520+ // allocation) cannot ever affect the value read out of a subtype's field.
539521 //
540522 // An exception to the above are copies. If a field is copied then even
541523 // struct.new information cannot be assumed to be precise:
@@ -549,36 +531,57 @@ struct ConstantFieldPropagation : public Pass {
549531 // foo(A->f0); // These can contain 20,
550532 // foo(C->f0); // if the copy read from B.
551533 //
552- // To handle that, copied fields are treated like struct.set ones (by
553- // copying the struct.new data to struct.set). Note that we must propagate
554- // copying to subtypes first, as in the example above the struct.new values
555- // of subtypes must be taken into account (that is, A or a subtype is being
556- // copied, so we want to do the same thing for B and C as well as A, since
557- // a copy of A means it could be a copy of B or C).
558- StructUtils::TypeHierarchyPropagator<StructUtils::CombinableBool>
559- boolPropagator (subTypes);
560- boolPropagator.propagateToSubTypesWithExact (combinedCopyInfos);
534+ // The handling of copies is explained below.
535+ SubTypes subTypes (*module );
536+ StructUtils::TypeHierarchyPropagator<PossibleConstantValues> propagator (
537+ subTypes);
538+
539+ // Compute the values without accounting for copies.
540+ PCVStructValuesMap noCopySetInfos = combinedSetInfos;
541+ propagator.propagateToSubTypesWithExact (noCopySetInfos);
542+ propagator.propagateToSuperTypes (noCopySetInfos);
543+
544+ // Now account for copies. A copy takes a value from any subtype
545+ // of the copy source to any subtype of the copy destination. Since we last
546+ // propagated to supertypes, we know the propagated values increase
547+ // monotonically as you go up the type hierarchy. The propagated value in a
548+ // field therefore overapproximates the values in the corresponding field in
549+ // all the subtypes. So for each copy, we can use the propagated value as
550+ // the copied value. Then we will propagate set values again, this time
551+ // including the copied values. We only need to repeat the propagation once;
552+ // if the second propagation discovers greater values in the copied fields,
553+ // it can only be because those greater values were propagated from a
554+ // supertype. In that case, the greater value has also been propagated to
555+ // all subtypes, so repeating the process will not further change anything.
556+ //
557+ // TODO: Track separate sources and destinations of copies rather than
558+ // special-casing copies to self. This would let propagation discover
559+ // greater copied values from unrelated types or even different field
560+ // indices, so we would have to repeatedly propagate taking into account the
561+ // latest discovered copied values until reaching a fixed point.
561562 for (auto & [type, copied] : combinedCopyInfos) {
562- for (Index i = 0 ; i < copied.size (); i++ ) {
563+ for (Index i = 0 ; i < copied.size (); ++i ) {
563564 if (copied[i]) {
564- combinedSetInfos[type][i].combine (combinedNewInfos [type][i]);
565+ combinedSetInfos[type][i].combine (noCopySetInfos [type][i]);
565566 }
566567 }
567568 }
568569
569- StructUtils::TypeHierarchyPropagator<PossibleConstantValues> propagator (
570- subTypes);
571- propagator.propagateToSuperTypes (combinedNewInfos);
572- propagator.propagateToSuperAndSubTypesWithExact (combinedSetInfos);
573-
574- // Combine both sources of information to the final information that gets
575- // care about.
576- PCVStructValuesMap combinedInfos = std::move (combinedNewInfos);
577- combinedSetInfos.combineInto (combinedInfos);
570+ // Propagate the values again, now including values readable by copies.
571+ // RefTest optimization manually checks the values in every subtype to
572+ // make sure they match, so there's no need to propagate values up for that.
573+ // Snapshot the info before propagating up for use in RefTest
574+ // optimization.
575+ PCVStructValuesMap refTestInfos;
576+ propagator.propagateToSubTypesWithExact (combinedSetInfos);
577+ if (refTest) {
578+ refTestInfos = combinedSetInfos;
579+ }
580+ propagator.propagateToSuperTypes (combinedSetInfos);
578581
579582 // Optimize.
580583 // TODO: Skip this if we cannot optimize anything
581- FunctionOptimizer (combinedInfos, subTypes, rawNewInfos , refTest)
584+ FunctionOptimizer (combinedSetInfos, refTestInfos, subTypes , refTest)
582585 .run (runner, module );
583586 }
584587};
0 commit comments