63
63
using namespace swift ;
64
64
using namespace rewriting ;
65
65
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) {
76
68
if (!Dirty)
77
- return RulesInEmptyContext;
69
+ return ;
70
+ Dirty = 0 ;
71
+
72
+ ProjectionCount = 0 ;
78
73
79
74
// Rules appearing in empty context (possibly more than once).
80
75
llvm::SmallDenseSet<unsigned , 2 > rulesInEmptyContext;
@@ -94,36 +89,51 @@ RewriteLoop::findRulesAppearingOnceInEmptyContext(
94
89
break ;
95
90
}
96
91
92
+ case RewriteStep::LeftConcreteProjection:
93
+ ++ProjectionCount;
94
+ break ;
95
+
97
96
case RewriteStep::PrefixSubstitutions:
98
97
case RewriteStep::Shift:
99
98
case RewriteStep::Decompose:
100
99
case RewriteStep::Relation:
101
100
case RewriteStep::DecomposeConcrete:
102
- case RewriteStep::LeftConcreteProjection:
103
101
case RewriteStep::RightConcreteProjection:
104
102
break ;
105
103
}
106
104
107
105
evaluator.apply (step, system);
108
106
}
109
107
110
- auto *mutThis = const_cast <RewriteLoop *>(this );
111
- mutThis->RulesInEmptyContext .clear ();
108
+ RulesInEmptyContext.clear ();
112
109
113
110
// Collect all rules that we saw exactly once in empty context.
114
111
for (auto rule : rulesInEmptyContext) {
115
112
auto found = ruleMultiplicity.find (rule);
116
113
assert (found != ruleMultiplicity.end ());
117
114
118
115
if (found->second == 1 )
119
- mutThis-> RulesInEmptyContext .push_back (rule);
116
+ RulesInEmptyContext.push_back (rule);
120
117
}
118
+ }
121
119
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);
124
126
return RulesInEmptyContext;
125
127
}
126
128
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
+
127
137
// / If a rewrite loop contains an explicit rule in empty context, propagate the
128
138
// / explicit bit to all other rules appearing in empty context within the same
129
139
// / loop.
@@ -375,6 +385,10 @@ findRuleToDelete(llvm::function_ref<bool(unsigned)> isRedundantRuleFn) {
375
385
376
386
Optional<std::pair<unsigned , unsigned >> found;
377
387
388
+ if (Debug.contains (DebugFlags::HomotopyReduction)) {
389
+ llvm::dbgs () << " \n " ;
390
+ }
391
+
378
392
for (const auto &pair : redundancyCandidates) {
379
393
unsigned ruleID = pair.second ;
380
394
const auto &rule = getRule (ruleID);
@@ -402,7 +416,7 @@ findRuleToDelete(llvm::function_ref<bool(unsigned)> isRedundantRuleFn) {
402
416
continue ;
403
417
}
404
418
405
- if (Debug.contains (DebugFlags::HomotopyReduction )) {
419
+ if (Debug.contains (DebugFlags::HomotopyReductionDetail )) {
406
420
llvm::dbgs () << " ** Candidate " << rule << " from loop #"
407
421
<< pair.first << " \n " ;
408
422
}
@@ -411,26 +425,42 @@ findRuleToDelete(llvm::function_ref<bool(unsigned)> isRedundantRuleFn) {
411
425
// we've found so far.
412
426
const auto &otherRule = getRule (found->second );
413
427
414
- unsigned ruleNesting = rule. getNesting () ;
415
- unsigned otherRuleNesting = otherRule. getNesting () ;
428
+ const auto &loop = Loops[pair. first ] ;
429
+ const auto &otherLoop = Loops[found-> first ] ;
416
430
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.
420
433
//
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:
422
436
//
423
- // T == G<Int>
424
- // U == Int
437
+ // X.T == G<Int, W>
438
+ // X.U == Int
439
+ // X.V == W
425
440
//
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)
429
449
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 ;
434
464
}
435
465
436
466
// Otherwise, perform a shortlex comparison on (LHS, RHS).
@@ -474,7 +504,8 @@ void RewriteSystem::deleteRule(unsigned ruleID,
474
504
const RewritePath &replacementPath) {
475
505
// Replace all occurrences of the rule with the replacement path in
476
506
// all remaining rewrite loops.
477
- for (auto &loop : Loops) {
507
+ for (unsigned loopID : indices (Loops)) {
508
+ auto &loop = Loops[loopID];
478
509
if (loop.isDeleted ())
479
510
continue ;
480
511
@@ -486,8 +517,8 @@ void RewriteSystem::deleteRule(unsigned ruleID,
486
517
// result of findRulesAppearingOnceInEmptyContext().
487
518
loop.markDirty ();
488
519
489
- if (Debug.contains (DebugFlags::HomotopyReduction )) {
490
- llvm::dbgs () << " ** Updated loop: " ;
520
+ if (Debug.contains (DebugFlags::HomotopyReductionDetail )) {
521
+ llvm::dbgs () << " ** Updated loop # " << loopID << " : " ;
491
522
loop.dump (llvm::dbgs (), *this );
492
523
llvm::dbgs () << " \n " ;
493
524
}
@@ -534,6 +565,12 @@ void RewriteSystem::performHomotopyReduction(
534
565
// /
535
566
// / Redundant rules are mutated to set their isRedundant() bit.
536
567
void 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
+
537
574
assert (Complete);
538
575
assert (!Minimized);
539
576
Minimized = 1 ;
@@ -545,7 +582,9 @@ void RewriteSystem::minimizeRewriteSystem() {
545
582
// - Eliminate all RHS-simplified and substitution-simplified rules.
546
583
// - Eliminate all rules with unresolved symbols.
547
584
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 " ;
549
588
}
550
589
551
590
performHomotopyReduction ([&](unsigned ruleID) -> bool {
@@ -577,7 +616,9 @@ void RewriteSystem::minimizeRewriteSystem() {
577
616
578
617
// Second pass: Eliminate all non-minimal conformance rules.
579
618
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 " ;
581
622
}
582
623
583
624
performHomotopyReduction ([&](unsigned ruleID) -> bool {
@@ -592,7 +633,9 @@ void RewriteSystem::minimizeRewriteSystem() {
592
633
593
634
// Third pass: Eliminate all other redundant non-conformance rules.
594
635
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 " ;
596
639
}
597
640
598
641
performHomotopyReduction ([&](unsigned ruleID) -> bool {
0 commit comments