6363using namespace swift ;
6464using namespace rewriting ;
6565
66- // / A rewrite rule is redundant if it appears exactly once in a loop
67- // / without context.
68- // /
69- // / This method will cache the result; markDirty() must be called after
70- // / the underlying rewrite path is modified to invalidate the cached
71- // / result.
72- ArrayRef<unsigned >
73- RewriteLoop::findRulesAppearingOnceInEmptyContext (
74- const RewriteSystem &system) const {
75- // If we're allowed to use the cached result, return that.
66+ // / Recompute RulesInEmptyContext and DecomposeCount if needed.
67+ void RewriteLoop::recompute (const RewriteSystem &system) {
7668 if (!Dirty)
77- return RulesInEmptyContext;
69+ return ;
70+ Dirty = 0 ;
71+
72+ ProjectionCount = 0 ;
7873
7974 // Rules appearing in empty context (possibly more than once).
8075 llvm::SmallDenseSet<unsigned , 2 > rulesInEmptyContext;
@@ -94,36 +89,51 @@ RewriteLoop::findRulesAppearingOnceInEmptyContext(
9489 break ;
9590 }
9691
92+ case RewriteStep::LeftConcreteProjection:
93+ ++ProjectionCount;
94+ break ;
95+
9796 case RewriteStep::PrefixSubstitutions:
9897 case RewriteStep::Shift:
9998 case RewriteStep::Decompose:
10099 case RewriteStep::Relation:
101100 case RewriteStep::DecomposeConcrete:
102- case RewriteStep::LeftConcreteProjection:
103101 case RewriteStep::RightConcreteProjection:
104102 break ;
105103 }
106104
107105 evaluator.apply (step, system);
108106 }
109107
110- auto *mutThis = const_cast <RewriteLoop *>(this );
111- mutThis->RulesInEmptyContext .clear ();
108+ RulesInEmptyContext.clear ();
112109
113110 // Collect all rules that we saw exactly once in empty context.
114111 for (auto rule : rulesInEmptyContext) {
115112 auto found = ruleMultiplicity.find (rule);
116113 assert (found != ruleMultiplicity.end ());
117114
118115 if (found->second == 1 )
119- mutThis-> RulesInEmptyContext .push_back (rule);
116+ RulesInEmptyContext.push_back (rule);
120117 }
118+ }
121119
122- // Cache the result for later.
123- mutThis->Dirty = 0 ;
120+ // / A rewrite rule is redundant if it appears exactly once in a loop
121+ // / without context.
122+ ArrayRef<unsigned >
123+ RewriteLoop::findRulesAppearingOnceInEmptyContext (
124+ const RewriteSystem &system) const {
125+ const_cast <RewriteLoop *>(this )->recompute (system);
124126 return RulesInEmptyContext;
125127}
126128
129+ // / The number of LeftConcreteProjection steps, used by the elimination order to
130+ // / prioritize loops that are not concrete unification projections.
131+ unsigned RewriteLoop::getProjectionCount (
132+ const RewriteSystem &system) const {
133+ const_cast <RewriteLoop *>(this )->recompute (system);
134+ return ProjectionCount;
135+ }
136+
127137// / If a rewrite loop contains an explicit rule in empty context, propagate the
128138// / explicit bit to all other rules appearing in empty context within the same
129139// / loop.
@@ -375,6 +385,10 @@ findRuleToDelete(llvm::function_ref<bool(unsigned)> isRedundantRuleFn) {
375385
376386 Optional<std::pair<unsigned , unsigned >> found;
377387
388+ if (Debug.contains (DebugFlags::HomotopyReduction)) {
389+ llvm::dbgs () << " \n " ;
390+ }
391+
378392 for (const auto &pair : redundancyCandidates) {
379393 unsigned ruleID = pair.second ;
380394 const auto &rule = getRule (ruleID);
@@ -402,7 +416,7 @@ findRuleToDelete(llvm::function_ref<bool(unsigned)> isRedundantRuleFn) {
402416 continue ;
403417 }
404418
405- if (Debug.contains (DebugFlags::HomotopyReduction )) {
419+ if (Debug.contains (DebugFlags::HomotopyReductionDetail )) {
406420 llvm::dbgs () << " ** Candidate " << rule << " from loop #"
407421 << pair.first << " \n " ;
408422 }
@@ -411,26 +425,42 @@ findRuleToDelete(llvm::function_ref<bool(unsigned)> isRedundantRuleFn) {
411425 // we've found so far.
412426 const auto &otherRule = getRule (found->second );
413427
414- unsigned ruleNesting = rule. getNesting () ;
415- unsigned otherRuleNesting = otherRule. getNesting () ;
428+ const auto &loop = Loops[pair. first ] ;
429+ const auto &otherLoop = Loops[found-> first ] ;
416430
417- // If both rules are concrete type requirements, first compare nesting
418- // depth. This breaks the tie when we have two rules that each imply
419- // the other via an induced rule that comes from a protocol.
431+ // If one of the rules was a concrete unification projection, prefer to
432+ // eliminate the *other* rule.
420433 //
421- // For example,
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:
422436 //
423- // T == G<Int>
424- // U == Int
437+ // X.T == G<Int, W>
438+ // X.U == Int
439+ // X.V == W
425440 //
426- // Where T == G<U> is implied elsewhere.
427- if (ruleNesting > 0 && otherRuleNesting > 0 ) {
428- if (ruleNesting > otherRuleNesting) {
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)
429449 found = pair;
430- continue ;
431- } else if (otherRuleNesting > ruleNesting) {
432- continue ;
433- }
450+
451+ continue ;
452+ }
453+
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.getRHS ().back ().hasSubstitutions ();
458+
459+ if (ruleIsConcrete != otherRuleIsConcrete) {
460+ if (otherRuleIsConcrete)
461+ found = pair;
462+
463+ continue ;
434464 }
435465
436466 // Otherwise, perform a shortlex comparison on (LHS, RHS).
@@ -474,7 +504,8 @@ void RewriteSystem::deleteRule(unsigned ruleID,
474504 const RewritePath &replacementPath) {
475505 // Replace all occurrences of the rule with the replacement path in
476506 // all remaining rewrite loops.
477- for (auto &loop : Loops) {
507+ for (unsigned loopID : indices (Loops)) {
508+ auto &loop = Loops[loopID];
478509 if (loop.isDeleted ())
479510 continue ;
480511
@@ -486,8 +517,8 @@ void RewriteSystem::deleteRule(unsigned ruleID,
486517 // result of findRulesAppearingOnceInEmptyContext().
487518 loop.markDirty ();
488519
489- if (Debug.contains (DebugFlags::HomotopyReduction )) {
490- llvm::dbgs () << " ** Updated loop: " ;
520+ if (Debug.contains (DebugFlags::HomotopyReductionDetail )) {
521+ llvm::dbgs () << " ** Updated loop # " << loopID << " : " ;
491522 loop.dump (llvm::dbgs (), *this );
492523 llvm::dbgs () << " \n " ;
493524 }
@@ -534,6 +565,12 @@ void RewriteSystem::performHomotopyReduction(
534565// /
535566// / Redundant rules are mutated to set their isRedundant() bit.
536567void RewriteSystem::minimizeRewriteSystem () {
568+ if (Debug.contains (DebugFlags::HomotopyReduction)) {
569+ llvm::dbgs () << " -----------------------------\n " ;
570+ llvm::dbgs () << " - Minimizing rewrite system -\n " ;
571+ llvm::dbgs () << " -----------------------------\n " ;
572+ }
573+
537574 assert (Complete);
538575 assert (!Minimized);
539576 Minimized = 1 ;
@@ -545,7 +582,9 @@ void RewriteSystem::minimizeRewriteSystem() {
545582 // - Eliminate all RHS-simplified and substitution-simplified rules.
546583 // - Eliminate all rules with unresolved symbols.
547584 if (Debug.contains (DebugFlags::HomotopyReduction)) {
548- llvm::dbgs () << " \n First pass: simplified and unresolved rules\n\n " ;
585+ llvm::dbgs () << " ---------------------------------------------\n " ;
586+ llvm::dbgs () << " First pass: simplified and unresolved rules -\n " ;
587+ llvm::dbgs () << " ---------------------------------------------\n " ;
549588 }
550589
551590 performHomotopyReduction ([&](unsigned ruleID) -> bool {
@@ -577,7 +616,9 @@ void RewriteSystem::minimizeRewriteSystem() {
577616
578617 // Second pass: Eliminate all non-minimal conformance rules.
579618 if (Debug.contains (DebugFlags::HomotopyReduction)) {
580- llvm::dbgs () << " \n Second pass: non-minimal conformance rules\n\n " ;
619+ llvm::dbgs () << " --------------------------------------------\n " ;
620+ llvm::dbgs () << " Second pass: non-minimal conformance rules -\n " ;
621+ llvm::dbgs () << " --------------------------------------------\n " ;
581622 }
582623
583624 performHomotopyReduction ([&](unsigned ruleID) -> bool {
@@ -592,7 +633,9 @@ void RewriteSystem::minimizeRewriteSystem() {
592633
593634 // Third pass: Eliminate all other redundant non-conformance rules.
594635 if (Debug.contains (DebugFlags::HomotopyReduction)) {
595- llvm::dbgs () << " \n Third pass: all other redundant rules\n\n " ;
636+ llvm::dbgs () << " ---------------------------------------\n " ;
637+ llvm::dbgs () << " Third pass: all other redundant rules -\n " ;
638+ llvm::dbgs () << " ---------------------------------------\n " ;
596639 }
597640
598641 performHomotopyReduction ([&](unsigned ruleID) -> bool {
0 commit comments