46
46
//
47
47
// Also, for a conformance rule (V.[P] => V) to be redundant, a stronger
48
48
// condition is needed than appearing once in a loop and without context;
49
- // the rule must not be a _generating conformance_. The algorithm for computing
50
- // a minimal set of generating conformances is implemented in
51
- // GeneratingConformances.cpp.
49
+ // the rule must not be a _minimal conformance_. The algorithm for computing
50
+ // minimal conformances is implemented in MinimalConformances.cpp.
52
51
//
53
52
// ===----------------------------------------------------------------------===//
54
53
@@ -66,9 +65,17 @@ using namespace rewriting;
66
65
67
66
// / A rewrite rule is redundant if it appears exactly once in a loop
68
67
// / without context.
69
- llvm::SmallVector<unsigned , 1 >
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 >
70
73
RewriteLoop::findRulesAppearingOnceInEmptyContext (
71
74
const RewriteSystem &system) const {
75
+ // If we're allowed to use the cached result, return that.
76
+ if (!Dirty)
77
+ return RulesInEmptyContext;
78
+
72
79
// Rules appearing in empty context (possibly more than once).
73
80
llvm::SmallDenseSet<unsigned , 2 > rulesInEmptyContext;
74
81
@@ -94,30 +101,35 @@ RewriteLoop::findRulesAppearingOnceInEmptyContext(
94
101
case RewriteStep::SuperclassConformance:
95
102
case RewriteStep::ConcreteTypeWitness:
96
103
case RewriteStep::SameTypeWitness:
104
+ case RewriteStep::AbstractTypeWitness:
97
105
break ;
98
106
}
99
107
100
108
evaluator.apply (step, system);
101
109
}
102
110
111
+ auto *mutThis = const_cast <RewriteLoop *>(this );
112
+ mutThis->RulesInEmptyContext .clear ();
113
+
103
114
// Collect all rules that we saw exactly once in empty context.
104
- SmallVector<unsigned , 1 > result;
105
115
for (auto rule : rulesInEmptyContext) {
106
116
auto found = ruleMultiplicity.find (rule);
107
117
assert (found != ruleMultiplicity.end ());
108
118
109
119
if (found->second == 1 )
110
- result .push_back (rule);
120
+ mutThis-> RulesInEmptyContext .push_back (rule);
111
121
}
112
122
113
- return result;
123
+ // Cache the result for later.
124
+ mutThis->Dirty = 0 ;
125
+ return RulesInEmptyContext;
114
126
}
115
127
116
128
// / If a rewrite loop contains an explicit rule in empty context, propagate the
117
129
// / explicit bit to all other rules appearing in empty context within the same
118
130
// / loop.
119
131
// /
120
- // / When computing generating conformances we prefer to eliminate non-explicit
132
+ // / When computing minimal conformances we prefer to eliminate non-explicit
121
133
// / rules, as a heuristic to ensure that minimized conformance requirements
122
134
// / remain in the same protocol as originally written, in cases where they can
123
135
// / be moved between protocols.
@@ -145,7 +157,7 @@ RewriteLoop::findRulesAppearingOnceInEmptyContext(
145
157
// / explicit bit from the original rule to the canonical rule.
146
158
void RewriteSystem::propagateExplicitBits () {
147
159
for (const auto &loop : Loops) {
148
- SmallVector< unsigned , 1 > rulesInEmptyContext =
160
+ auto rulesInEmptyContext =
149
161
loop.findRulesAppearingOnceInEmptyContext (*this );
150
162
151
163
bool sawExplicitRule = false ;
@@ -212,6 +224,7 @@ RewritePath RewritePath::splitCycleAtRule(unsigned ruleID) const {
212
224
case RewriteStep::SuperclassConformance:
213
225
case RewriteStep::ConcreteTypeWitness:
214
226
case RewriteStep::SameTypeWitness:
227
+ case RewriteStep::AbstractTypeWitness:
215
228
break ;
216
229
}
217
230
@@ -312,6 +325,7 @@ bool RewritePath::replaceRuleWithPath(unsigned ruleID,
312
325
case RewriteStep::SuperclassConformance:
313
326
case RewriteStep::ConcreteTypeWitness:
314
327
case RewriteStep::SameTypeWitness:
328
+ case RewriteStep::AbstractTypeWitness:
315
329
newSteps.push_back (step);
316
330
break ;
317
331
}
@@ -321,56 +335,6 @@ bool RewritePath::replaceRuleWithPath(unsigned ruleID,
321
335
return true ;
322
336
}
323
337
324
- // / Check if a rewrite rule is a candidate for deletion in this pass of the
325
- // / minimization algorithm.
326
- bool RewriteSystem::
327
- isCandidateForDeletion (unsigned ruleID,
328
- const llvm::DenseSet<unsigned > *redundantConformances) const {
329
- const auto &rule = getRule (ruleID);
330
-
331
- // We should not find a rule that has already been marked redundant
332
- // here; it should have already been replaced with a rewrite path
333
- // in all homotopy generators.
334
- assert (!rule.isRedundant ());
335
-
336
- // Associated type introduction rules are 'permanent'. They're
337
- // not worth eliminating since they are re-added every time; it
338
- // is better to find other candidates to eliminate in the same
339
- // loop instead.
340
- if (rule.isPermanent ())
341
- return false ;
342
-
343
- // Other rules involving unresolved name symbols are derived from an
344
- // associated type introduction rule together with a conformance rule.
345
- // They are eliminated in the first pass.
346
- if (rule.getLHS ().containsUnresolvedSymbols ())
347
- return true ;
348
-
349
- // Protocol conformance rules are eliminated via a different
350
- // algorithm which computes "generating conformances".
351
- //
352
- // The first pass skips protocol conformance rules.
353
- //
354
- // The second pass eliminates any protocol conformance rule which is
355
- // redundant according to both homotopy reduction and the generating
356
- // conformances algorithm.
357
- //
358
- // Later on, we verify that any conformance redundant via generating
359
- // conformances was also redundant via homotopy reduction. This
360
- // means that the set of generating conformances is always a superset
361
- // (or equal to) of the set of minimal protocol conformance
362
- // requirements that homotopy reduction alone would produce.
363
- if (rule.isAnyConformanceRule ()) {
364
- if (!redundantConformances)
365
- return false ;
366
-
367
- if (!redundantConformances->count (ruleID))
368
- return false ;
369
- }
370
-
371
- return true ;
372
- }
373
-
374
338
// / Find a rule to delete by looking through all loops for rewrite rules appearing
375
339
// / once in empty context. Returns a redundant rule to delete if one was found,
376
340
// / otherwise returns None.
@@ -380,13 +344,13 @@ isCandidateForDeletion(unsigned ruleID,
380
344
// / 1) First, rules that are not conformance rules are deleted, with
381
345
// / \p redundantConformances equal to nullptr.
382
346
// /
383
- // / 2) Second, generating conformances are computed.
347
+ // / 2) Second, minimal conformances are computed.
384
348
// /
385
349
// / 3) Finally, redundant conformance rules are deleted, with
386
350
// / \p redundantConformances equal to the set of conformance rules that are
387
- // / not generating conformances.
351
+ // / not minimal conformances.
388
352
Optional<unsigned > RewriteSystem::
389
- findRuleToDelete (const llvm::DenseSet< unsigned > *redundantConformances ,
353
+ findRuleToDelete (llvm::function_ref< bool ( unsigned )> isRedundantRuleFn ,
390
354
RewritePath &replacementPath) {
391
355
SmallVector<std::pair<unsigned , unsigned >, 2 > redundancyCandidates;
392
356
for (unsigned loopID : indices (Loops)) {
@@ -408,15 +372,28 @@ findRuleToDelete(const llvm::DenseSet<unsigned> *redundantConformances,
408
372
409
373
for (const auto &pair : redundancyCandidates) {
410
374
unsigned ruleID = pair.second ;
411
- if (!isCandidateForDeletion (ruleID, redundantConformances))
375
+ const auto &rule = getRule (ruleID);
376
+
377
+ // We should not find a rule that has already been marked redundant
378
+ // here; it should have already been replaced with a rewrite path
379
+ // in all homotopy generators.
380
+ assert (!rule.isRedundant ());
381
+
382
+ // Associated type introduction rules are 'permanent'. They're
383
+ // not worth eliminating since they are re-added every time; it
384
+ // is better to find other candidates to eliminate in the same
385
+ // loop instead.
386
+ if (rule.isPermanent ())
387
+ continue ;
388
+
389
+ if (!isRedundantRuleFn (ruleID))
412
390
continue ;
413
391
414
392
if (!found) {
415
393
found = pair;
416
394
continue ;
417
395
}
418
396
419
- const auto &rule = getRule (ruleID);
420
397
const auto &otherRule = getRule (found->second );
421
398
422
399
// Prefer to delete "less canonical" rules.
@@ -457,8 +434,8 @@ void RewriteSystem::deleteRule(unsigned ruleID,
457
434
llvm::dbgs () << " \n " ;
458
435
}
459
436
460
- // Replace all occurrences of the rule with the replacement path and
461
- // normalize all loops.
437
+ // Replace all occurrences of the rule with the replacement path in
438
+ // all remaining rewrite loops.
462
439
for (auto &loop : Loops) {
463
440
if (loop.isDeleted ())
464
441
continue ;
@@ -467,6 +444,10 @@ void RewriteSystem::deleteRule(unsigned ruleID,
467
444
if (!changed)
468
445
continue ;
469
446
447
+ // The loop's path has changed, so we must invalidate the cached
448
+ // result of findRulesAppearingOnceInEmptyContext().
449
+ loop.markDirty ();
450
+
470
451
if (Debug.contains (DebugFlags::HomotopyReduction)) {
471
452
llvm::dbgs () << " ** Updated loop: " ;
472
453
loop.dump (llvm::dbgs (), *this );
@@ -476,10 +457,10 @@ void RewriteSystem::deleteRule(unsigned ruleID,
476
457
}
477
458
478
459
void RewriteSystem::performHomotopyReduction (
479
- const llvm::DenseSet< unsigned > *redundantConformances ) {
460
+ llvm::function_ref< bool ( unsigned )> isRedundantRuleFn ) {
480
461
while (true ) {
481
462
RewritePath replacementPath;
482
- auto optRuleID = findRuleToDelete (redundantConformances ,
463
+ auto optRuleID = findRuleToDelete (isRedundantRuleFn ,
483
464
replacementPath);
484
465
485
466
// If no redundant rules remain which can be eliminated by this pass, stop.
@@ -505,26 +486,60 @@ void RewriteSystem::minimizeRewriteSystem() {
505
486
506
487
propagateExplicitBits ();
507
488
508
- // First pass: Eliminate all redundant rules that are not conformance rules.
509
- performHomotopyReduction (/* redundantConformances=*/ nullptr );
489
+ // First pass:
490
+ // - Eliminate all simplified non-conformance rules.
491
+ // - Eliminate all rules with unresolved symbols.
492
+ performHomotopyReduction ([&](unsigned ruleID) -> bool {
493
+ const auto &rule = getRule (ruleID);
494
+
495
+ if (rule.isSimplified () &&
496
+ !rule.isAnyConformanceRule ())
497
+ return true ;
498
+
499
+ // Other rules involving unresolved name symbols are derived from an
500
+ // associated type introduction rule together with a conformance rule.
501
+ // They are eliminated in the first pass.
502
+ if (rule.getLHS ().containsUnresolvedSymbols ())
503
+ return true ;
510
504
511
- // Now find a minimal set of generating conformances.
505
+ return false ;
506
+ });
507
+
508
+ // Now compute a set of minimal conformances.
512
509
//
513
510
// FIXME: For now this just produces a set of redundant conformances, but
514
- // it should actually output the canonical generating conformance equation
515
- // for each non-generating conformance. We can then use information to
511
+ // it should actually output the canonical minimal conformance equation
512
+ // for each non-minimal conformance. We can then use information to
516
513
// compute conformance access paths, instead of the current "brute force"
517
514
// algorithm used for that purpose.
518
515
llvm::DenseSet<unsigned > redundantConformances;
519
- computeGeneratingConformances (redundantConformances);
516
+ computeMinimalConformances (redundantConformances);
520
517
521
- // Second pass: Eliminate all redundant conformance rules.
522
- performHomotopyReduction (/* redundantConformances=*/ &redundantConformances);
518
+ // Second pass: Eliminate all non-minimal conformance rules.
519
+ performHomotopyReduction ([&](unsigned ruleID) -> bool {
520
+ const auto &rule = getRule (ruleID);
521
+
522
+ if (rule.isAnyConformanceRule () &&
523
+ redundantConformances.count (ruleID))
524
+ return true ;
525
+
526
+ return false ;
527
+ });
528
+
529
+ // Third pass: Eliminate all other redundant non-conformance rules.
530
+ performHomotopyReduction ([&](unsigned ruleID) -> bool {
531
+ const auto &rule = getRule (ruleID);
532
+
533
+ if (!rule.isAnyConformanceRule ())
534
+ return true ;
535
+
536
+ return false ;
537
+ });
523
538
524
539
// Check invariants after homotopy reduction.
525
540
verifyRewriteLoops ();
526
541
verifyRedundantConformances (redundantConformances);
527
- verifyMinimizedRules ();
542
+ verifyMinimizedRules (redundantConformances );
528
543
}
529
544
530
545
// / In a conformance-valid rewrite system, any rule with unresolved symbols on
@@ -637,7 +652,7 @@ void RewriteSystem::verifyRewriteLoops() const {
637
652
// / Assert if homotopy reduction failed to eliminate a redundant conformance,
638
653
// / since this suggests a misunderstanding on my part.
639
654
void RewriteSystem::verifyRedundantConformances (
640
- llvm::DenseSet<unsigned > redundantConformances) const {
655
+ const llvm::DenseSet<unsigned > & redundantConformances) const {
641
656
#ifndef NDEBUG
642
657
for (unsigned ruleID : redundantConformances) {
643
658
const auto &rule = getRule (ruleID);
@@ -661,9 +676,12 @@ void RewriteSystem::verifyRedundantConformances(
661
676
662
677
// Assert if homotopy reduction failed to eliminate a rewrite rule it was
663
678
// supposed to delete.
664
- void RewriteSystem::verifyMinimizedRules () const {
679
+ void RewriteSystem::verifyMinimizedRules (
680
+ const llvm::DenseSet<unsigned > &redundantConformances) const {
665
681
#ifndef NDEBUG
666
- for (const auto &rule : Rules) {
682
+ for (unsigned ruleID : indices (Rules)) {
683
+ const auto &rule = getRule (ruleID);
684
+
667
685
// Note that sometimes permanent rules can be simplified, but they can never
668
686
// be redundant.
669
687
if (rule.isPermanent ()) {
@@ -687,6 +705,15 @@ void RewriteSystem::verifyMinimizedRules() const {
687
705
dump (llvm::errs ());
688
706
abort ();
689
707
}
708
+
709
+ if (rule.isRedundant () &&
710
+ rule.isAnyConformanceRule () &&
711
+ !rule.containsUnresolvedSymbols () &&
712
+ !redundantConformances.count (ruleID)) {
713
+ llvm::errs () << " Minimal conformance is redundant: " << rule << " \n\n " ;
714
+ dump (llvm::errs ());
715
+ abort ();
716
+ }
690
717
}
691
718
#endif
692
719
}
0 commit comments