Skip to content

Commit 5ced0f8

Browse files
committed
RequirementMachine: Avoid some wasted work when re-building the property map
1 parent 380dba1 commit 5ced0f8

File tree

3 files changed

+77
-36
lines changed

3 files changed

+77
-36
lines changed

lib/AST/RequirementMachine/PropertyMap.cpp

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -363,22 +363,8 @@ PropertyMap::buildPropertyMap(unsigned maxIterations,
363363

364364
for (const auto &bucket : properties) {
365365
for (auto property : bucket) {
366-
auto existingRuleID = addProperty(property.key, property.symbol,
367-
property.ruleID, inducedRules);
368-
if (existingRuleID) {
369-
// The GSB only dropped the new rule in the case of a conflicting
370-
// superclass requirement, so maintain that behavior here.
371-
auto &existingRule = System.getRule(*existingRuleID);
372-
if (existingRule.isPropertyRule()->getKind() !=
373-
Symbol::Kind::Superclass) {
374-
if (existingRule.getRHS().size() == property.key.size())
375-
existingRule.markConflicting();
376-
}
377-
378-
auto &newRule = System.getRule(property.ruleID);
379-
assert(newRule.getRHS().size() == property.key.size());
380-
newRule.markConflicting();
381-
}
366+
addProperty(property.key, property.symbol,
367+
property.ruleID, inducedRules);
382368
}
383369
}
384370

lib/AST/RequirementMachine/PropertyMap.h

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,27 @@ class PropertyMap {
168168
using ConcreteTypeInDomain = std::pair<CanType, ArrayRef<const ProtocolDecl *>>;
169169
llvm::DenseMap<ConcreteTypeInDomain, Term> ConcreteTypeInDomainMap;
170170

171+
// Building the property map introduces new induced rules, which
172+
// runs another round of Knuth-Bendix completion, which rebuilds the
173+
// property map again.
174+
//
175+
// To avoid wasted work from re-introducing the same induced rules,
176+
// we track the rules we've seen already on previous builds.
177+
178+
/// Maps a pair of rules where the first is a conformance rule and the
179+
/// second is a superclass or concrete type rule, to a concrete
180+
/// conformance.
171181
llvm::DenseMap<std::pair<unsigned, unsigned>, ProtocolConformance *>
172182
ConcreteConformances;
173183

184+
/// When a type parameter is subject to two requirements of the same
185+
/// kind, we have a pair of rewrite rules T.[p1] => T and T.[p2] => T.
186+
///
187+
/// One of these rules might imply the other. Keep track of these pairs
188+
/// to avoid wasted work from recording the same rewrite loop more than
189+
/// once.
190+
llvm::DenseSet<std::pair<unsigned, unsigned>> CheckedRulePairs;
191+
174192
DebugOptions Debug;
175193

176194
PropertyBag *getOrCreateProperties(Term key);
@@ -199,9 +217,11 @@ class PropertyMap {
199217

200218
private:
201219
void clear();
202-
Optional<unsigned>
203-
addProperty(Term key, Symbol property, unsigned ruleID,
204-
SmallVectorImpl<InducedRule> &inducedRules);
220+
221+
bool checkRulePairOnce(unsigned firstRuleID, unsigned secondRuleID);
222+
223+
void addProperty(Term key, Symbol property, unsigned ruleID,
224+
SmallVectorImpl<InducedRule> &inducedRules);
205225

206226
void computeConcreteTypeInDomainMap();
207227
void concretizeNestedTypesFromConcreteParents(

lib/AST/RequirementMachine/PropertyUnification.cpp

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@
3737
using namespace swift;
3838
using namespace rewriting;
3939

40+
/// Returns true if we have not processed this pair of rules before.
41+
bool PropertyMap::checkRulePairOnce(unsigned firstRuleID,
42+
unsigned secondRuleID) {
43+
return CheckedRulePairs.insert(
44+
std::make_pair(firstRuleID, secondRuleID)).second;
45+
}
46+
4047
unsigned RewriteSystem::recordRelation(Symbol lhs, Symbol rhs) {
4148
auto key = std::make_pair(lhs, rhs);
4249
auto found = RelationMap.find(key);
@@ -113,6 +120,24 @@ static void recordRelation(unsigned lhsRuleID, unsigned rhsRuleID,
113120
path);
114121
}
115122

123+
static void recordConflict(Term key,
124+
unsigned existingRuleID,
125+
unsigned newRuleID,
126+
RewriteSystem &system) {
127+
// The GSB only dropped the new rule in the case of a conflicting
128+
// superclass requirement, so maintain that behavior here.
129+
auto &existingRule = system.getRule(existingRuleID);
130+
if (existingRule.isPropertyRule()->getKind() !=
131+
Symbol::Kind::Superclass) {
132+
if (existingRule.getRHS().size() == key.size())
133+
existingRule.markConflicting();
134+
}
135+
136+
auto &newRule = system.getRule(newRuleID);
137+
assert(newRule.getRHS().size() == key.size());
138+
newRule.markConflicting();
139+
}
140+
116141
/// This method takes a concrete type that was derived from a concrete type
117142
/// produced by RewriteSystemBuilder::getConcreteSubstitutionSchema(),
118143
/// either by extracting a structural sub-component or performing a (Swift AST)
@@ -385,10 +410,7 @@ static std::pair<Symbol, bool> unifySuperclasses(
385410

386411
/// Record a protocol conformance, layout or superclass constraint on the given
387412
/// key. Must be called in monotonically non-decreasing key order.
388-
///
389-
/// If there was a conflict, returns the conflicting rule ID; otherwise
390-
/// returns None.
391-
Optional<unsigned> PropertyMap::addProperty(
413+
void PropertyMap::addProperty(
392414
Term key, Symbol property, unsigned ruleID,
393415
SmallVectorImpl<InducedRule> &inducedRules) {
394416
assert(property.isProperty());
@@ -400,7 +422,7 @@ Optional<unsigned> PropertyMap::addProperty(
400422
case Symbol::Kind::Protocol:
401423
props->ConformsTo.push_back(property.getProtocol());
402424
props->ConformsToRules.push_back(ruleID);
403-
return None;
425+
return;
404426

405427
case Symbol::Kind::Layout: {
406428
auto newLayout = property.getLayoutConstraint();
@@ -415,18 +437,27 @@ Optional<unsigned> PropertyMap::addProperty(
415437
auto mergedLayout = props->Layout.merge(property.getLayoutConstraint());
416438

417439
// If the intersection is invalid, we have a conflict.
418-
if (!mergedLayout->isKnownLayout())
419-
return props->LayoutRule;
440+
if (!mergedLayout->isKnownLayout()) {
441+
recordConflict(key, *props->LayoutRule, ruleID, System);
442+
return;
443+
}
420444

421445
// If the intersection is equal to the existing layout requirement,
422446
// the new layout requirement is redundant.
423447
if (mergedLayout == props->Layout) {
424-
recordRelation(*props->LayoutRule, ruleID, System, inducedRules, debug);
448+
if (checkRulePairOnce(*props->LayoutRule, ruleID)) {
449+
recordRelation(*props->LayoutRule, ruleID, System,
450+
inducedRules, debug);
451+
}
425452

426453
// If the intersection is equal to the new layout requirement, the
427454
// existing layout requirement is redundant.
428455
} else if (mergedLayout == newLayout) {
429-
recordRelation(ruleID, *props->LayoutRule, System, inducedRules, debug);
456+
if (checkRulePairOnce(*props->LayoutRule, ruleID)) {
457+
recordRelation(ruleID, *props->LayoutRule, System,
458+
inducedRules, debug);
459+
}
460+
430461
props->LayoutRule = ruleID;
431462
} else {
432463
llvm::errs() << "Arbitrary intersection of layout requirements is "
@@ -435,7 +466,7 @@ Optional<unsigned> PropertyMap::addProperty(
435466
}
436467
}
437468

438-
return None;
469+
return;
439470
}
440471

441472
case Symbol::Kind::Superclass: {
@@ -451,11 +482,13 @@ Optional<unsigned> PropertyMap::addProperty(
451482
inducedRules, debug);
452483
props->Superclass = pair.first;
453484
bool conflict = pair.second;
454-
if (conflict)
455-
return props->SuperclassRule;
485+
if (conflict) {
486+
recordConflict(key, *props->SuperclassRule, ruleID, System);
487+
return;
488+
}
456489
}
457490

458-
return None;
491+
return;
459492
}
460493

461494
case Symbol::Kind::ConcreteType: {
@@ -467,16 +500,18 @@ Optional<unsigned> PropertyMap::addProperty(
467500
bool conflict = unifyConcreteTypes(*props->ConcreteType, property,
468501
System.getRewriteContext(),
469502
inducedRules, debug);
470-
if (conflict)
471-
return props->ConcreteTypeRule;
503+
if (conflict) {
504+
recordConflict(key, *props->ConcreteTypeRule, ruleID, System);
505+
return;
506+
}
472507
}
473508

474-
return None;
509+
return;
475510
}
476511

477512
case Symbol::Kind::ConcreteConformance:
478513
// FIXME
479-
return None;
514+
return;
480515

481516
case Symbol::Kind::Name:
482517
case Symbol::Kind::GenericParam:

0 commit comments

Comments
 (0)