Skip to content

Commit 77b8dfe

Browse files
committed
RequirementMachine: Homotopy reduction prefers to delete less-canonical rules
Instead of deleting lower numbered rules, compare the LHS, and if they're equal, compare the RHS, then use that to pick the rule to delete.
1 parent f8fb412 commit 77b8dfe

File tree

2 files changed

+83
-58
lines changed

2 files changed

+83
-58
lines changed

lib/AST/RequirementMachine/HomotopyReduction.cpp

Lines changed: 78 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,6 @@ RewritePath::findRulesAppearingOnceInEmptyContext() const {
218218
result.push_back(rule);
219219
}
220220

221-
std::sort(result.begin(), result.end());
222221
return result;
223222
}
224223

@@ -574,6 +573,64 @@ void HomotopyGenerator::dump(llvm::raw_ostream &out,
574573
out << " [deleted]";
575574
}
576575

576+
/// Check if a rewrite rule is a candidate for deletion in this pass of the
577+
/// minimization algorithm.
578+
bool RewriteSystem::
579+
isCandidateForDeletion(unsigned ruleID,
580+
bool firstPass,
581+
const llvm::DenseSet<unsigned> *redundantConformances) const {
582+
const auto &rule = getRule(ruleID);
583+
584+
// We should not find a rule that has already been marked redundant
585+
// here; it should have already been replaced with a rewrite path
586+
// in all homotopy generators.
587+
assert(!rule.isRedundant());
588+
589+
// Associated type introduction rules are 'permanent'. They're
590+
// not worth eliminating since they are re-added every time; it
591+
// is better to find other candidates to eliminate in the same
592+
// 3-cell instead.
593+
if (rule.isPermanent())
594+
return false;
595+
596+
// Other rules involving unresolved name symbols are derived from an
597+
// associated type introduction rule together with a conformance rule.
598+
// They are eliminated in the first pass.
599+
if (firstPass)
600+
return rule.getLHS().containsUnresolvedSymbols();
601+
602+
// In the second and third pass we should not have any rules involving
603+
// unresolved name symbols, except for permanent rules which were
604+
// already skipped above.
605+
//
606+
// FIXME: This isn't true with invalid code.
607+
assert(!rule.getLHS().containsUnresolvedSymbols());
608+
609+
// Protocol conformance rules are eliminated via a different
610+
// algorithm which computes "generating conformances".
611+
//
612+
// The first and second passes skip protocol conformance rules.
613+
//
614+
// The third pass eliminates any protocol conformance rule which is
615+
// redundant according to both homotopy reduction and the generating
616+
// conformances algorithm.
617+
//
618+
// Later on, we verify that any conformance redundant via generating
619+
// conformances was also redundant via homotopy reduction. This
620+
// means that the set of generating conformances is always a superset
621+
// (or equal to) of the set of minimal protocol conformance
622+
// requirements that homotopy reduction alone would produce.
623+
if (rule.isProtocolConformanceRule()) {
624+
if (!redundantConformances)
625+
return false;
626+
627+
if (!redundantConformances->count(ruleID))
628+
return false;
629+
}
630+
631+
return true;
632+
}
633+
577634
/// Find a rule to delete by looking through all 3-cells for rewrite rules appearing
578635
/// once in empty context. Returns a redundant rule to delete if one was found,
579636
/// otherwise returns None.
@@ -604,63 +661,26 @@ findRuleToDelete(bool firstPass,
604661
SmallVector<unsigned> redundancyCandidates =
605662
loop.Path.findRulesAppearingOnceInEmptyContext();
606663

607-
auto found = std::find_if(
608-
redundancyCandidates.begin(),
609-
redundancyCandidates.end(),
610-
[&](unsigned ruleID) -> bool {
611-
const auto &rule = getRule(ruleID);
612-
613-
// We should not find a rule that has already been marked redundant
614-
// here; it should have already been replaced with a rewrite path
615-
// in all homotopy generators.
616-
assert(!rule.isRedundant());
617-
618-
// Associated type introduction rules are 'permanent'. They're
619-
// not worth eliminating since they are re-added every time; it
620-
// is better to find other candidates to eliminate in the same
621-
// 3-cell instead.
622-
if (rule.isPermanent())
623-
return false;
624-
625-
// Other rules involving unresolved name symbols are derived from an
626-
// associated type introduction rule together with a conformance rule.
627-
// They are eliminated in the first pass.
628-
if (firstPass)
629-
return rule.getLHS().containsUnresolvedSymbols();
630-
631-
// In the second and third pass we should not have any rules involving
632-
// unresolved name symbols, except for permanent rules which were
633-
// already skipped above.
634-
//
635-
// FIXME: This isn't true with invalid code.
636-
assert(!rule.getLHS().containsUnresolvedSymbols());
637-
638-
// Protocol conformance rules are eliminated via a different
639-
// algorithm which computes "generating conformances".
640-
//
641-
// The first and second passes skip protocol conformance rules.
642-
//
643-
// The third pass eliminates any protocol conformance rule which is
644-
// redundant according to both homotopy reduction and the generating
645-
// conformances algorithm.
646-
//
647-
// Later on, we verify that any conformance redundant via generating
648-
// conformances was also redundant via homotopy reduction. This
649-
// means that the set of generating conformances is always a superset
650-
// (or equal to) of the set of minimal protocol conformance
651-
// requirements that homotopy reduction alone would produce.
652-
if (rule.isProtocolConformanceRule()) {
653-
if (!redundantConformances)
654-
return false;
655-
656-
if (!redundantConformances->count(ruleID))
657-
return false;
658-
}
659-
660-
return true;
661-
});
662-
663-
if (found == redundancyCandidates.end())
664+
Optional<unsigned> found;
665+
666+
for (unsigned ruleID : redundancyCandidates) {
667+
if (!isCandidateForDeletion(ruleID, firstPass, redundantConformances))
668+
continue;
669+
670+
if (!found) {
671+
found = ruleID;
672+
continue;
673+
}
674+
675+
const auto &rule = getRule(ruleID);
676+
const auto &otherRule = getRule(*found);
677+
678+
// Prefer to delete "less canonical" rules.
679+
if (rule.compare(otherRule, Protos) > 0)
680+
found = ruleID;
681+
}
682+
683+
if (!found)
664684
continue;
665685

666686
auto ruleID = *found;

lib/AST/RequirementMachine/RewriteSystem.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,11 @@ class RewriteSystem final {
493493
///
494494
//////////////////////////////////////////////////////////////////////////////
495495

496+
bool
497+
isCandidateForDeletion(unsigned ruleID,
498+
bool firstPass,
499+
const llvm::DenseSet<unsigned> *redundantConformances) const;
500+
496501
Optional<unsigned>
497502
findRuleToDelete(bool firstPass,
498503
const llvm::DenseSet<unsigned> *redundantConformances,

0 commit comments

Comments
 (0)