@@ -70,6 +70,7 @@ void RewriteLoop::recompute(const RewriteSystem &system) {
7070 Dirty = 0 ;
7171
7272 ProjectionCount = 0 ;
73+ DecomposeCount = 0 ;
7374
7475 // Rules appearing in empty context (possibly more than once).
7576 llvm::SmallDenseSet<unsigned , 2 > rulesInEmptyContext;
@@ -93,9 +94,12 @@ void RewriteLoop::recompute(const RewriteSystem &system) {
9394 ++ProjectionCount;
9495 break ;
9596
97+ case RewriteStep::Decompose:
98+ ++DecomposeCount;
99+ break ;
100+
96101 case RewriteStep::PrefixSubstitutions:
97102 case RewriteStep::Shift:
98- case RewriteStep::Decompose:
99103 case RewriteStep::Relation:
100104 case RewriteStep::DecomposeConcrete:
101105 case RewriteStep::RightConcreteProjection:
@@ -134,6 +138,14 @@ unsigned RewriteLoop::getProjectionCount(
134138 return ProjectionCount;
135139}
136140
141+ // / The number of Decompose steps, used by the elimination order to prioritize
142+ // / loops that are not concrete simplifications.
143+ unsigned RewriteLoop::getDecomposeCount (
144+ const RewriteSystem &system) const {
145+ const_cast <RewriteLoop *>(this )->recompute (system);
146+ return DecomposeCount;
147+ }
148+
137149// / If a rewrite loop contains an explicit rule in empty context, propagate the
138150// / explicit bit to all other rules appearing in empty context within the same
139151// / loop.
@@ -428,56 +440,108 @@ findRuleToDelete(llvm::function_ref<bool(unsigned)> isRedundantRuleFn) {
428440 const auto &loop = Loops[pair.first ];
429441 const auto &otherLoop = Loops[found->first ];
430442
431- // If one of the rules was a concrete unification projection, prefer to
432- // eliminate the *other* rule.
433- //
434- // For example, if 'X.T == G<U, V>' is implied by the conformance on X,
435- // and the following three rules are defined in the current protocol:
436- //
437- // X.T == G<Int, W>
438- // X.U == Int
439- // X.V == W
440- //
441- // Then we can either eliminate a) alone, or b) and c). Since b) and c)
442- // are projections, they are "simpler", and we would rather keep both and
443- // eliminate a).
444- unsigned projectionCount = loop.getProjectionCount (*this );
445- unsigned otherProjectionCount = otherLoop.getProjectionCount (*this );
446-
447- if (projectionCount != otherProjectionCount) {
448- if (projectionCount < otherProjectionCount)
449- found = pair;
450-
451- continue ;
443+ {
444+ // If one of the rules was a concrete unification projection, prefer to
445+ // eliminate the *other* rule.
446+ //
447+ // For example, if 'X.T == G<U, V>' is implied by the conformance on X,
448+ // and the following three rules are defined in the current protocol:
449+ //
450+ // a) X.T == G<Int, W>
451+ // b) X.U == Int
452+ // c) X.V == W
453+ //
454+ // Then we can either eliminate a) alone, or b) and c). Since b) and c)
455+ // are projections, they are "simpler", and we would rather keep both and
456+ // eliminate a).
457+ unsigned projectionCount = loop.getProjectionCount (*this );
458+ unsigned otherProjectionCount = otherLoop.getProjectionCount (*this );
459+
460+ if (projectionCount != otherProjectionCount) {
461+ if (projectionCount < otherProjectionCount)
462+ found = pair;
463+
464+ continue ;
465+ }
452466 }
453467
454- // If one of the rules is a concrete type requirement, prefer to
455- // eliminate the *other* rule.
456- bool ruleIsConcrete = rule.getLHS ().back ().hasSubstitutions ();
457- bool otherRuleIsConcrete = otherRule.getLHS ().back ().hasSubstitutions ();
468+ {
469+ // If one of the rules is a concrete type requirement, prefer to
470+ // eliminate the *other* rule.
471+ bool ruleIsConcrete = rule.getLHS ().back ().hasSubstitutions ();
472+ bool otherRuleIsConcrete = otherRule.getLHS ().back ().hasSubstitutions ();
458473
459- if (ruleIsConcrete != otherRuleIsConcrete) {
460- if (otherRuleIsConcrete)
461- found = pair;
474+ if (ruleIsConcrete != otherRuleIsConcrete) {
475+ if (otherRuleIsConcrete)
476+ found = pair;
462477
463- continue ;
478+ continue ;
479+ }
464480 }
465481
466- // Otherwise, perform a shortlex comparison on (LHS, RHS).
467- Optional<int > comparison = rule.compare (otherRule, Context);
468- if (!comparison.hasValue ()) {
469- // Two rules (T.[C] => T) and (T.[C'] => T) are incomparable if
470- // C and C' are superclass, concrete type or concrete conformance
471- // symbols.
472- found = pair;
473- continue ;
482+ {
483+ // If both are concrete type requirements, prefer to eliminate the
484+ // one with the more deeply nested type.
485+ unsigned ruleNesting = rule.getNesting ();
486+ unsigned otherRuleNesting = otherRule.getNesting ();
487+
488+ if (ruleNesting != otherRuleNesting) {
489+ if (ruleNesting > otherRuleNesting)
490+ found = pair;
491+
492+ continue ;
493+ }
474494 }
475495
476- if (*comparison > 0 ) {
477- // Otherwise, if the new rule is less canonical than the best one so
478- // far, it becomes the new candidate for elimination.
479- found = pair;
480- continue ;
496+ {
497+ // Otherwise, perform a shortlex comparison on (LHS, RHS).
498+ Optional<int > comparison = rule.compare (otherRule, Context);
499+
500+ if (!comparison.hasValue ()) {
501+ // Two rules (T.[C] => T) and (T.[C'] => T) are incomparable if
502+ // C and C' are superclass, concrete type or concrete conformance
503+ // symbols.
504+ found = pair;
505+ continue ;
506+ }
507+
508+ if (*comparison == 0 ) {
509+ // Given two rewrite loops that both eliminate the same rule, prefer
510+ // the one that was not recorded by substitution simplification;
511+ // substitution simplification rules contain the projections in
512+ // context, which then prevents the projections from being eliminated.
513+ //
514+ // An example is if you have two rules implied by conformances on X,
515+ //
516+ // a) X.T == G<Y>
517+ // b) X.T == G<Z>
518+ //
519+ // then the induced rule Y == Z is a projection.
520+ //
521+ // The rule X.T == G<Z> can be eliminated with a loop that begins at
522+ // X.T.[concrete: G<Y>] followed by a decomposition and rewrite of
523+ // Y into Z, finally followed by an inverse decomposition back to
524+ // X.T.[concrete: G<Z>].
525+ //
526+ // However, if we can eliminate G<Y> via some other loop, we prefer
527+ // to do that, since that might *also* allow us to eliminate Y == Z.
528+ unsigned decomposeCount = loop.getDecomposeCount (*this );
529+ unsigned otherDecomposeCount = otherLoop.getDecomposeCount (*this );
530+
531+ if (decomposeCount != otherDecomposeCount) {
532+ if (decomposeCount < otherDecomposeCount)
533+ found = pair;
534+
535+ continue ;
536+ }
537+ }
538+
539+ if (*comparison > 0 ) {
540+ // Otherwise, if the new rule is less canonical than the best one so
541+ // far, it becomes the new candidate for elimination.
542+ found = pair;
543+ continue ;
544+ }
481545 }
482546 }
483547
0 commit comments