-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[Requirement Machine] Diagnose redundant requirements. #41664
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b18e815
3c95cac
be30d76
a154641
3976849
2f7018b
b5d7a01
4b55c30
0085eb0
7ce6504
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -74,24 +74,14 @@ void RewriteLoop::recompute(const RewriteSystem &system) { | |
|
||
ProjectionCount = 0; | ||
DecomposeCount = 0; | ||
|
||
// Rules appearing in empty context (possibly more than once). | ||
llvm::SmallDenseSet<unsigned, 2> rulesInEmptyContext; | ||
|
||
// The number of times each rule appears (with or without context). | ||
llvm::SmallDenseMap<unsigned, unsigned, 2> ruleMultiplicity; | ||
Useful = false; | ||
|
||
RewritePathEvaluator evaluator(Basepoint); | ||
|
||
for (auto step : Path) { | ||
switch (step.Kind) { | ||
case RewriteStep::Rule: { | ||
if (!step.isInContext() && !evaluator.isInContext()) | ||
rulesInEmptyContext.insert(step.getRuleID()); | ||
|
||
++ruleMultiplicity[step.getRuleID()]; | ||
case RewriteStep::Rule: | ||
Useful |= (!step.isInContext() && !evaluator.isInContext()); | ||
break; | ||
} | ||
|
||
case RewriteStep::LeftConcreteProjection: | ||
++ProjectionCount; | ||
|
@@ -112,18 +102,7 @@ void RewriteLoop::recompute(const RewriteSystem &system) { | |
evaluator.apply(step, system); | ||
} | ||
|
||
Useful = !rulesInEmptyContext.empty(); | ||
|
||
RulesInEmptyContext.clear(); | ||
|
||
// Collect all rules that we saw exactly once in empty context. | ||
for (auto rule : rulesInEmptyContext) { | ||
auto found = ruleMultiplicity.find(rule); | ||
assert(found != ruleMultiplicity.end()); | ||
|
||
if (found->second == 1) | ||
RulesInEmptyContext.push_back(rule); | ||
} | ||
RulesInEmptyContext = Path.getRulesInEmptyContext(Basepoint, system); | ||
} | ||
|
||
/// A rewrite rule is redundant if it appears exactly once in a loop | ||
|
@@ -211,6 +190,46 @@ void RewriteSystem::propagateExplicitBits() { | |
} | ||
} | ||
|
||
/// Propagate requirement IDs from redundant rules to their | ||
/// replacements that appear once in empty context. | ||
void RewriteSystem::propagateRedundantRequirementIDs() { | ||
if (Debug.contains(DebugFlags::PropagateRequirementIDs)) { | ||
llvm::dbgs() << "\nPropagating requirement IDs: {"; | ||
} | ||
|
||
for (auto ruleAndReplacement : RedundantRules) { | ||
auto ruleID = ruleAndReplacement.first; | ||
auto rewritePath = ruleAndReplacement.second; | ||
auto &rule = Rules[ruleID]; | ||
|
||
auto requirementID = rule.getRequirementID(); | ||
if (!requirementID.hasValue()) | ||
continue; | ||
|
||
MutableTerm lhs(rule.getLHS()); | ||
for (auto ruleID : rewritePath.getRulesInEmptyContext(lhs, *this)) { | ||
auto &replacement = Rules[ruleID]; | ||
if (!replacement.isPermanent() && | ||
!replacement.getRequirementID().hasValue()) { | ||
if (Debug.contains(DebugFlags::PropagateRequirementIDs)) { | ||
llvm::dbgs() << "\n- propagating ID = " | ||
<< requirementID | ||
<< "\n from "; | ||
rule.dump(llvm::dbgs()); | ||
llvm::dbgs() << "\n to "; | ||
replacement.dump(llvm::dbgs()); | ||
} | ||
|
||
replacement.setRequirementID(requirementID); | ||
} | ||
} | ||
} | ||
|
||
if (Debug.contains(DebugFlags::PropagateRequirementIDs)) { | ||
llvm::dbgs() << "\n}\n"; | ||
} | ||
} | ||
|
||
/// After propagating the 'explicit' bit on rules, process pairs of | ||
/// conflicting rules, marking one or both of the rules as conflicting, | ||
/// which instructs minimization to drop them. | ||
|
@@ -409,6 +428,51 @@ bool RewritePath::replaceRuleWithPath(unsigned ruleID, | |
return true; | ||
} | ||
|
||
SmallVector<unsigned, 1> | ||
RewritePath::getRulesInEmptyContext(const MutableTerm &term, | ||
const RewriteSystem &system) { | ||
// Rules appearing in empty context (possibly more than once). | ||
llvm::SmallDenseSet<unsigned, 2> rulesInEmptyContext; | ||
// The number of times each rule appears (with or without context). | ||
llvm::SmallDenseMap<unsigned, unsigned, 2> ruleFrequency; | ||
|
||
RewritePathEvaluator evaluator(term); | ||
for (auto step : Steps) { | ||
switch (step.Kind) { | ||
case RewriteStep::Rule: { | ||
if (!step.isInContext() && !evaluator.isInContext()) | ||
rulesInEmptyContext.insert(step.getRuleID()); | ||
|
||
++ruleFrequency[step.getRuleID()]; | ||
break; | ||
} | ||
|
||
case RewriteStep::LeftConcreteProjection: | ||
case RewriteStep::Decompose: | ||
case RewriteStep::PrefixSubstitutions: | ||
case RewriteStep::Shift: | ||
case RewriteStep::Relation: | ||
case RewriteStep::DecomposeConcrete: | ||
case RewriteStep::RightConcreteProjection: | ||
break; | ||
} | ||
|
||
evaluator.apply(step, system); | ||
} | ||
|
||
// Collect all rules that we saw exactly once in empty context. | ||
SmallVector<unsigned, 1> rulesOnceInEmptyContext; | ||
for (auto rule : rulesInEmptyContext) { | ||
auto found = ruleFrequency.find(rule); | ||
assert(found != ruleFrequency.end()); | ||
|
||
if (found->second == 1) | ||
rulesOnceInEmptyContext.push_back(rule); | ||
} | ||
|
||
return rulesOnceInEmptyContext; | ||
} | ||
|
||
/// Find a rule to delete by looking through all loops for rewrite rules appearing | ||
/// once in empty context. Returns a pair consisting of a loop ID and a rule ID, | ||
/// otherwise returns None. | ||
|
@@ -654,7 +718,7 @@ void RewriteSystem::performHomotopyReduction( | |
|
||
// If no redundant rules remain which can be eliminated by this pass, stop. | ||
if (!optPair) | ||
return; | ||
break; | ||
|
||
unsigned loopID = optPair->first; | ||
unsigned ruleID = optPair->second; | ||
|
@@ -679,6 +743,8 @@ void RewriteSystem::performHomotopyReduction( | |
|
||
deleteRule(ruleID, replacementPath); | ||
} | ||
|
||
propagateRedundantRequirementIDs(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe this should be done once at the end? (performHomotopyReduction() is called three times) |
||
} | ||
|
||
void RewriteSystem::normalizeRedundantRules() { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -59,7 +59,7 @@ void realizeInheritedRequirements(TypeDecl *decl, Type type, | |
SmallVectorImpl<RequirementError> &errors); | ||
|
||
bool diagnoseRequirementErrors(ASTContext &ctx, | ||
SmallVectorImpl<RequirementError> &errors, | ||
ArrayRef<RequirementError> errors, | ||
bool allowConcreteGenericParams); | ||
|
||
std::pair<MutableTerm, MutableTerm> | ||
|
@@ -104,7 +104,12 @@ struct RuleBuilder { | |
|
||
/// New rules derived from requirements written by the user, which can be | ||
/// eliminated by homotopy reduction. | ||
std::vector<std::pair<MutableTerm, MutableTerm>> RequirementRules; | ||
std::vector<std::tuple<MutableTerm, MutableTerm, Optional<unsigned>>> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I prefer to define a named type if I need a tuple of length > 2 because |
||
RequirementRules; | ||
|
||
/// Requirements written in source code. The requirement ID in the above | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These have been desugared at this point right? Might want to note that. |
||
/// \c RequirementRules vector is an index into this array. | ||
std::vector<StructuralRequirement> WrittenRequirements; | ||
|
||
/// Enables debugging output. Controlled by the -dump-requirement-machine | ||
/// frontend flag. | ||
|
@@ -123,7 +128,8 @@ struct RuleBuilder { | |
void addAssociatedType(const AssociatedTypeDecl *type, | ||
const ProtocolDecl *proto); | ||
void addRequirement(const Requirement &req, | ||
const ProtocolDecl *proto); | ||
const ProtocolDecl *proto, | ||
Optional<unsigned> requirementID); | ||
void addRequirement(const StructuralRequirement &req, | ||
const ProtocolDecl *proto); | ||
void addTypeAlias(const ProtocolTypeAlias &alias, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I agree that 'frequency' is probably better than 'multiplicity' :)