Skip to content

Commit 7c226dd

Browse files
authored
Merge pull request #42035 from slavapestov/rqm-remove-redundant-rule-normalization
RequirementMachine: Don't normalize replacement paths for redundant rules
2 parents 3c8405a + 6110866 commit 7c226dd

File tree

3 files changed

+93
-91
lines changed

3 files changed

+93
-91
lines changed

lib/AST/RequirementMachine/HomotopyReduction.cpp

Lines changed: 36 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -455,62 +455,6 @@ void RewriteSystem::performHomotopyReduction(
455455

456456
deleteRule(ruleID, replacementPath);
457457
}
458-
459-
propagateRedundantRequirementIDs();
460-
}
461-
462-
void RewriteSystem::normalizeRedundantRules() {
463-
llvm::DenseMap<unsigned, unsigned> RedundantRuleMap;
464-
465-
// A redundant path in the range [0, i-1] might contain rewrite steps naming
466-
// rules that subsequently became redundant in the range [i, e-1].
467-
//
468-
// We back-substitute later rules into earlier paths here.
469-
for (unsigned i = 0, e = RedundantRules.size(); i < e; ++i) {
470-
// Pre-condition: Redundant paths in the range [i+1, e-1] do not involve
471-
// any other redundant rules.
472-
unsigned j = e - i - 1;
473-
474-
// Replace all occurrences of redundant rules with their path at
475-
// RedundantRules[i].
476-
auto &pair = RedundantRules[j];
477-
pair.second.replaceRulesWithPaths(
478-
[&](unsigned ruleID) -> RewritePath * {
479-
auto found = RedundantRuleMap.find(ruleID);
480-
if (found != RedundantRuleMap.end())
481-
return &RedundantRules[found->second].second;
482-
483-
return nullptr;
484-
});
485-
pair.second.computeNormalForm(*this);
486-
487-
RedundantRuleMap[RedundantRules[j].first] = j;
488-
489-
// Post-condition: the path for RedundantRules[i] does not contain any
490-
// redundant rules.
491-
}
492-
493-
if (Debug.contains(DebugFlags::RedundantRules)) {
494-
llvm::dbgs() << "\nRedundant rules:\n";
495-
for (const auto &pair : RedundantRules) {
496-
const auto &rule = getRule(pair.first);
497-
llvm::dbgs() << "- ("
498-
<< rule.getLHS() << " => "
499-
<< rule.getRHS() << ") ::== ";
500-
501-
MutableTerm lhs(rule.getLHS());
502-
pair.second.dump(llvm::dbgs(), lhs, *this);
503-
504-
llvm::dbgs() << "\n";
505-
506-
if (Debug.contains(DebugFlags::RedundantRulesDetail)) {
507-
llvm::dbgs() << "\n";
508-
pair.second.dumpLong(llvm::dbgs(), lhs, *this);
509-
510-
llvm::dbgs() << "\n\n";
511-
}
512-
}
513-
}
514458
}
515459

516460
/// Use the loops to delete redundant rewrite rules via a series of Tietze
@@ -644,12 +588,34 @@ void RewriteSystem::minimizeRewriteSystem() {
644588
return false;
645589
});
646590

647-
normalizeRedundantRules();
591+
propagateRedundantRequirementIDs();
648592

649593
// Check invariants after homotopy reduction.
650594
verifyRewriteLoops();
651595
verifyRedundantConformances(redundantConformances);
652596
verifyMinimizedRules(redundantConformances);
597+
598+
if (Debug.contains(DebugFlags::RedundantRules)) {
599+
llvm::dbgs() << "\nRedundant rules:\n";
600+
for (const auto &pair : RedundantRules) {
601+
const auto &rule = getRule(pair.first);
602+
llvm::dbgs() << "- ("
603+
<< rule.getLHS() << " => "
604+
<< rule.getRHS() << ") ::== ";
605+
606+
MutableTerm lhs(rule.getLHS());
607+
pair.second.dump(llvm::dbgs(), lhs, *this);
608+
609+
llvm::dbgs() << "\n";
610+
611+
if (Debug.contains(DebugFlags::RedundantRulesDetail)) {
612+
llvm::dbgs() << "\n";
613+
pair.second.dumpLong(llvm::dbgs(), lhs, *this);
614+
615+
llvm::dbgs() << "\n\n";
616+
}
617+
}
618+
}
653619
}
654620

655621
/// Returns flags indicating if the rewrite system has unresolved or
@@ -849,7 +815,11 @@ void RewriteSystem::verifyMinimizedRules(
849815
abort();
850816
}
851817

852-
for (const auto &pair : RedundantRules) {
818+
// Replacement paths for redundant rules can only reference other redundant
819+
// rules if those redundant rules were made redundant later, ie if they
820+
// appear later in the array.
821+
llvm::DenseSet<unsigned> laterRedundantRules;
822+
for (const auto &pair : llvm::reverse(RedundantRules)) {
853823
const auto &rule = getRule(pair.first);
854824
if (!rule.isRedundant()) {
855825
llvm::errs() << "Recorded replacement path for non-redundant rule "
@@ -860,14 +830,19 @@ void RewriteSystem::verifyMinimizedRules(
860830

861831
for (const auto &step : pair.second) {
862832
if (step.Kind == RewriteStep::Rule) {
863-
const auto &rule = getRule(step.getRuleID());
864-
if (rule.isRedundant()) {
833+
unsigned otherRuleID = step.getRuleID();
834+
const auto &otherRule = getRule(otherRuleID);
835+
if (otherRule.isRedundant() &&
836+
!laterRedundantRules.count(otherRuleID)) {
865837
llvm::errs() << "Redundant requirement path contains a redundant "
866-
"rule " << rule << "\n";
838+
"rule " << otherRule << "\n";
867839
dump(llvm::errs());
868840
abort();
869841
}
870842
}
871843
}
844+
845+
laterRedundantRules.insert(pair.first);
872846
}
847+
873848
}

lib/AST/RequirementMachine/RewriteSystem.cpp

Lines changed: 57 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -610,46 +610,58 @@ void RewriteSystem::verifyRewriteRules(ValidityPolicy policy) const {
610610
/// emit warnings for in the source code.
611611
void RewriteSystem::computeRedundantRequirementDiagnostics(
612612
SmallVectorImpl<RequirementError> &errors) {
613-
// Map redundant rule IDs to their rewrite path for easy access
614-
// in the `isRedundantRule` lambda.
615-
llvm::SmallDenseMap<unsigned, RewritePath> redundantRules;
616-
for (auto &pair : RedundantRules)
617-
redundantRules[pair.first] = pair.second;
618-
619613
// Collect all rule IDs for each unique requirement ID.
620-
llvm::SmallDenseMap<unsigned, llvm::SmallDenseSet<unsigned, 2>>
614+
llvm::SmallDenseMap<unsigned, llvm::SmallVector<unsigned, 2>>
621615
rulesPerRequirement;
622616

623617
// Collect non-explicit requirements that are not redundant.
624-
llvm::SmallDenseSet<unsigned, 2> impliedRequirements;
618+
llvm::SmallDenseSet<unsigned, 2> nonExplicitNonRedundantRules;
625619

626620
for (unsigned ruleID = FirstLocalRule, e = Rules.size();
627621
ruleID < e; ++ruleID) {
628622
auto &rule = getRules()[ruleID];
629623

630-
if (!rule.getRequirementID().hasValue() &&
631-
!rule.isPermanent() && !rule.isRedundant() &&
632-
isInMinimizationDomain(rule.getLHS().getRootProtocol()))
633-
impliedRequirements.insert(ruleID);
624+
if (rule.isPermanent())
625+
continue;
626+
627+
if (!isInMinimizationDomain(rule.getLHS().getRootProtocol()))
628+
continue;
634629

635630
auto requirementID = rule.getRequirementID();
636-
if (!requirementID.hasValue())
631+
632+
if (!requirementID.hasValue()) {
633+
if (!rule.isRedundant())
634+
nonExplicitNonRedundantRules.insert(ruleID);
635+
637636
continue;
637+
}
638638

639-
rulesPerRequirement[*requirementID].insert(ruleID);
639+
rulesPerRequirement[*requirementID].push_back(ruleID);
640640
}
641641

642-
auto isRedundantRule = [&](unsigned ruleID) {
643-
auto &rule = getRules()[ruleID];
644-
645-
// If this rule is replaced using a non-explicit,
646-
// non-redundant rule, it's not redundant.
647-
auto rewritePath = redundantRules[ruleID];
642+
// Compute the set of redundant rules which transitively reference a
643+
// non-explicit non-redundant rule. This updates nonExplicitNonRedundantRules.
644+
//
645+
// Since earlier redundant paths might reference rules which appear later in
646+
// the list but not vice versa, walk the redundant paths in reverse order.
647+
for (const auto &pair : llvm::reverse(RedundantRules)) {
648+
// Pre-condition: the replacement path only references redundant rules
649+
// which we have already seen. If any of those rules transitively reference
650+
// a non-explicit, non-redundant rule, they have been inserted into the
651+
// nonExplicitNonRedundantRules set on previous iterations.
652+
unsigned ruleID = pair.first;
653+
const auto &rewritePath = pair.second;
654+
655+
// Check if this rewrite path references a rule that is already known to
656+
// either be non-explicit and non-redundant, or reference such a rule via
657+
// it's redundancy path.
648658
for (auto step : rewritePath) {
649659
switch (step.Kind) {
650660
case RewriteStep::Rule: {
651-
if (impliedRequirements.count(step.getRuleID()))
652-
return false;
661+
if (nonExplicitNonRedundantRules.count(step.getRuleID())) {
662+
nonExplicitNonRedundantRules.insert(ruleID);
663+
continue;
664+
}
653665

654666
break;
655667
}
@@ -665,20 +677,37 @@ void RewriteSystem::computeRedundantRequirementDiagnostics(
665677
}
666678
}
667679

668-
return rule.isRedundant();
680+
// Post-condition: If the current replacement path transitively references
681+
// any non-explicit, non-redundant rules, then nonExplicitNonRedundantRules
682+
// contains the current rule.
683+
}
684+
685+
// We diagnose a redundancy if the rule is redundant, and if its replacement
686+
// path does not transitively involve any non-explicit, non-redundant rules.
687+
auto isRedundantRule = [&](unsigned ruleID) {
688+
const auto &rule = getRules()[ruleID];
689+
690+
return (rule.isRedundant() &&
691+
nonExplicitNonRedundantRules.count(ruleID) == 0);
669692
};
670693

694+
// Finally walk through the written requirements, diagnosing any that are
695+
// redundant.
671696
for (auto requirementID : indices(WrittenRequirements)) {
672697
auto requirement = WrittenRequirements[requirementID];
673-
auto pairIt = rulesPerRequirement.find(requirementID);
674698

675699
// Inferred requirements can be re-stated without warning.
676700
if (requirement.inferred)
677701
continue;
678702

679-
// If there are no rules for this structural requirement, then
680-
// the requirement was never added to the rewrite system because
681-
// it is trivially redundant.
703+
auto pairIt = rulesPerRequirement.find(requirementID);
704+
705+
// If there are no rules for this structural requirement, then the
706+
// requirement is unnecessary in the source code.
707+
//
708+
// This means the requirement was determined to be vacuous by
709+
// requirement lowering and produced no rules, or the rewrite rules were
710+
// trivially simplified by RewriteSystem::addRule().
682711
if (pairIt == rulesPerRequirement.end()) {
683712
errors.push_back(
684713
RequirementError::forRedundantRequirement(requirement.req,
@@ -688,7 +717,7 @@ void RewriteSystem::computeRedundantRequirementDiagnostics(
688717

689718
// If all rules derived from this structural requirement are redundant,
690719
// then the requirement is unnecessary in the source code.
691-
auto ruleIDs = pairIt->second;
720+
const auto &ruleIDs = pairIt->second;
692721
if (llvm::all_of(ruleIDs, isRedundantRule)) {
693722
auto requirement = WrittenRequirements[requirementID];
694723
errors.push_back(

lib/AST/RequirementMachine/RewriteSystem.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -367,8 +367,6 @@ class RewriteSystem final {
367367
void computeMinimalConformances(
368368
llvm::DenseSet<unsigned> &redundantConformances);
369369

370-
void normalizeRedundantRules();
371-
372370
public:
373371
void recordRewriteLoop(MutableTerm basepoint,
374372
RewritePath path);

0 commit comments

Comments
 (0)