Skip to content

Commit 2db676d

Browse files
committed
RequirementMachine: Homotopy reduction deletes rules with unresolved name symbols first
1 parent 871b4d1 commit 2db676d

File tree

3 files changed

+107
-20
lines changed

3 files changed

+107
-20
lines changed

lib/AST/RequirementMachine/HomotopyReduction.cpp

Lines changed: 89 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -574,9 +574,29 @@ void HomotopyGenerator::dump(llvm::raw_ostream &out,
574574
out << " [deleted]";
575575
}
576576

577+
/// Find a rule to delete by looking through all 3-cells for rewrite rules appearing
578+
/// once in empty context. Returns a redundant rule to delete if one was found,
579+
/// otherwise returns None.
580+
///
581+
/// Minimization performs three passes over the rewrite system, with the
582+
/// \p firstPass and \p redundantConformances parameters as follows:
583+
///
584+
/// 1) First, rules involving unresolved name symbols are deleted, with
585+
/// \p firstPass equal to true and \p redundantConformances equal to nullptr.
586+
///
587+
/// 2) Second, rules that are not conformance rules are deleted, with
588+
/// \p firstPass equal to false and \p redundantConformances equal to nullptr.
589+
///
590+
/// 3) Finally, conformance rules are deleted after computing a minimal set of
591+
/// generating conformances, with \p firstPass equal to false and
592+
/// \p redundantConformances equal to the set of conformance rules that are
593+
/// not generating conformances.
577594
Optional<unsigned> RewriteSystem::
578-
findRuleToDelete(RewritePath &replacementPath,
579-
const llvm::DenseSet<unsigned> *redundantConformances) {
595+
findRuleToDelete(bool firstPass,
596+
const llvm::DenseSet<unsigned> *redundantConformances,
597+
RewritePath &replacementPath) {
598+
assert(!firstPass || redundantConformances == nullptr);
599+
580600
for (auto &loop : HomotopyGenerators) {
581601
if (loop.isDeleted())
582602
continue;
@@ -602,6 +622,13 @@ findRuleToDelete(RewritePath &replacementPath,
602622
if (rule.isPermanent())
603623
return false;
604624

625+
// Other rules involving unresolved name symbols are eliminated in
626+
// the first pass.
627+
if (firstPass)
628+
return rule.containsUnresolvedSymbols();
629+
630+
assert(!rule.containsUnresolvedSymbols());
631+
605632
// Protocol conformance rules are eliminated via a different
606633
// algorithm which computes "generating conformances".
607634
if (rule.isProtocolConformanceRule()) {
@@ -631,6 +658,8 @@ findRuleToDelete(RewritePath &replacementPath,
631658
return None;
632659
}
633660

661+
/// Delete a rewrite rule that is known to be redundant, replacing all
662+
/// occurrences of the rule in all 3-cells with the replacement path.
634663
void RewriteSystem::deleteRule(unsigned ruleID,
635664
const RewritePath &replacementPath) {
636665
if (Debug.contains(DebugFlags::HomotopyReduction)) {
@@ -695,19 +724,44 @@ void RewriteSystem::deleteRule(unsigned ruleID,
695724
}
696725
}
697726

727+
void RewriteSystem::performHomotopyReduction(
728+
bool firstPass,
729+
const llvm::DenseSet<unsigned> *redundantConformances) {
730+
while (true) {
731+
RewritePath replacementPath;
732+
auto optRuleID = findRuleToDelete(firstPass,
733+
redundantConformances,
734+
replacementPath);
735+
736+
// If there no redundant rules remain in this pass, stop.
737+
if (!optRuleID)
738+
return;
739+
740+
deleteRule(*optRuleID, replacementPath);
741+
}
742+
}
743+
698744
/// Use the 3-cells to delete redundant rewrite rules via a series of Tietze
699745
/// transformations, updating and simplifying existing 3-cells as each rule
700746
/// is deleted.
701747
void RewriteSystem::minimizeRewriteSystem() {
702-
// First, eliminate all redundant rules that are not conformance rules.
703-
while (true) {
704-
RewritePath replacementPath;
705-
if (auto optRuleID = findRuleToDelete(replacementPath, nullptr))
706-
deleteRule(*optRuleID, replacementPath);
707-
else
708-
break;
748+
/// Begin by normalizing all 3-cells to cyclically-reduced left-canonical
749+
/// form.
750+
for (auto &loop : HomotopyGenerators) {
751+
if (loop.isDeleted())
752+
continue;
753+
754+
loop.normalize(*this);
709755
}
710756

757+
// First pass: Eliminate all redundant rules involving unresolved types.
758+
performHomotopyReduction(/*firstPass=*/true,
759+
/*redundantConformances=*/nullptr);
760+
761+
// Second pass: Eliminate all redundant rules that are not conformance rules.
762+
performHomotopyReduction(/*firstPass=*/false,
763+
/*redundantConformances=*/nullptr);
764+
711765
// Now find a minimal set of generating conformances.
712766
//
713767
// FIXME: For now this just produces a set of redundant conformances, but
@@ -716,15 +770,9 @@ void RewriteSystem::minimizeRewriteSystem() {
716770
llvm::DenseSet<unsigned> redundantConformances;
717771
computeGeneratingConformances(redundantConformances);
718772

719-
// Now, eliminate all redundant conformance rules.
720-
while (true) {
721-
RewritePath replacementPath;
722-
if (auto optRuleID = findRuleToDelete(replacementPath,
723-
&redundantConformances))
724-
deleteRule(*optRuleID, replacementPath);
725-
else
726-
break;
727-
}
773+
// Third pass: Eliminate all redundant conformance rules.
774+
performHomotopyReduction(/*firstPass=*/false,
775+
/*redundantConformances=*/&redundantConformances);
728776

729777
// Assert if homotopy reduction failed to eliminate a redundant conformance,
730778
// since this suggests a misunderstanding on my part.
@@ -741,6 +789,29 @@ void RewriteSystem::minimizeRewriteSystem() {
741789
abort();
742790
}
743791
}
792+
793+
// Assert if homotopy reduction failed to eliminate a rewrite rule which was
794+
// deleted because either it's left hand side can be reduced by some other
795+
// rule, or because it's right hand side can be reduced further.
796+
for (const auto &rule : Rules) {
797+
// Note that sometimes permanent rules can be simplified, but they can never
798+
// be redundant.
799+
if (rule.isPermanent()) {
800+
if (rule.isRedundant()) {
801+
llvm::errs() << "Permanent rule is redundant: " << rule << "\n\n";
802+
dump(llvm::errs());
803+
abort();
804+
}
805+
806+
continue;
807+
}
808+
809+
if (rule.isSimplified() && !rule.isRedundant()) {
810+
llvm::errs() << "Simplified rule is not redundant: " << rule << "\n\n";
811+
dump(llvm::errs());
812+
abort();
813+
}
814+
}
744815
}
745816

746817
/// Verify that each 3-cell is a valid loop around its basepoint.

lib/AST/RequirementMachine/RewriteSystem.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ bool Rule::isProtocolConformanceRule() const {
6060
return false;
6161
}
6262

63+
bool Rule::containsUnresolvedSymbols() const {
64+
for (auto symbol : LHS) {
65+
if (symbol.getKind() == Symbol::Kind::Name)
66+
return true;
67+
}
68+
69+
return false;
70+
}
71+
6372
void Rule::dump(llvm::raw_ostream &out) const {
6473
out << LHS << " => " << RHS;
6574
if (Permanent)

lib/AST/RequirementMachine/RewriteSystem.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ class Rule final {
120120
return LHS.size();
121121
}
122122

123+
bool containsUnresolvedSymbols() const;
124+
123125
void dump(llvm::raw_ostream &out) const;
124126

125127
friend llvm::raw_ostream &operator<<(llvm::raw_ostream &out,
@@ -453,11 +455,16 @@ class RewriteSystem final {
453455
//////////////////////////////////////////////////////////////////////////////
454456

455457
Optional<unsigned>
456-
findRuleToDelete(RewritePath &replacementPath,
457-
const llvm::DenseSet<unsigned> *redundantConformances);
458+
findRuleToDelete(bool firstPass,
459+
const llvm::DenseSet<unsigned> *redundantConformances,
460+
RewritePath &replacementPath);
458461

459462
void deleteRule(unsigned ruleID, const RewritePath &replacementPath);
460463

464+
void performHomotopyReduction(
465+
bool firstPass,
466+
const llvm::DenseSet<unsigned> *redundantConformances);
467+
461468
void minimizeRewriteSystem();
462469

463470
void verifyHomotopyGenerators() const;

0 commit comments

Comments
 (0)