Skip to content

Commit 91aa964

Browse files
committed
[GSB] Minimize the right-hand sides of rules in the term-rewriting system.
Introduce the first step of a minimization algorithm for the term-rewriting system used to produce anchors of equivalence classes. This step simplifies the right-hand sides of each rewrite rule, so that each rewrite step goes to the minimal result. This code is currently not enabled; it *can* be enabled by minimizing before computing anchors, but it ends up pessimizing compile times to do so.
1 parent 8346917 commit 91aa964

File tree

2 files changed

+181
-25
lines changed

2 files changed

+181
-25
lines changed

include/swift/AST/GenericSignatureBuilder.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,9 @@ class GenericSignatureBuilder {
447447

448448
/// Add a rewrite rule that makes \c otherPA a part of the given equivalence
449449
/// class.
450-
void addSameTypeRewriteRule(EquivalenceClass *equivClass,
450+
///
451+
/// \returns true if a new rewrite rule was added, and false otherwise.
452+
bool addSameTypeRewriteRule(EquivalenceClass *equivClass,
451453
PotentialArchetype *otherPA);
452454

453455
/// \brief Add a new conformance requirement specifying that the given

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 178 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,12 @@ STATISTIC(NumDelayedRequirementUnresolved,
107107
"Delayed requirements left unresolved");
108108
STATISTIC(NumConditionalRequirementsAdded,
109109
"# of conditional requirements added");
110+
STATISTIC(NumRewriteMinimizations,
111+
"# of rewrite system minimizations performed");
112+
STATISTIC(NumRewriteRhsSimplified,
113+
"# of rewrite rule right-hand sides simplified");
114+
STATISTIC(NumRewriteRhsSimplifiedToLhs,
115+
"# of rewrite rule right-hand sides simplified to lhs (and removed)");
110116

111117
namespace {
112118

@@ -158,6 +164,16 @@ class RewritePath {
158164
/// types.
159165
Optional<RewritePath> static createPath(Type type);
160166

167+
/// Decompose a type into a path.
168+
///
169+
/// \param path Will be filled in with the components of the path, in
170+
/// reverse order.
171+
///
172+
/// \returns the generic parameter at the start of the path, or \c None if
173+
///
174+
Optional<GenericParamKey>
175+
static createPath(Type type, SmallVectorImpl<AssociatedTypeDecl *> &path);
176+
161177
/// Compute the longer common prefix between this path and \c other.
162178
RewritePath commonPath(const RewritePath &other) const;
163179

@@ -258,7 +274,10 @@ class RewriteTreeNode {
258274
///
259275
/// \param replacementPath The sequence of associated type declarations
260276
/// with which a match will be replaced.
261-
void addRewriteRule(RelativeRewritePath matchPath,
277+
///
278+
/// \returns true if a rewrite rule was added, and false if it already
279+
/// existed.
280+
bool addRewriteRule(RelativeRewritePath matchPath,
262281
RewritePath replacementPath);
263282

264283
/// Enumerate all of the paths to which the given matched path can be
@@ -293,7 +312,9 @@ class RewriteTreeNode {
293312
unsigned prefixLength);
294313

295314
/// Merge the given rewrite tree into \c other.
296-
void mergeInto(RewriteTreeNode *other);
315+
///
316+
/// \returns true if any rules were created by this merge.
317+
bool mergeInto(RewriteTreeNode *other);
297318

298319
/// An action to perform for the given rule
299320
class RuleAction {
@@ -386,6 +407,13 @@ struct GenericSignatureBuilder::Implementation {
386407
DenseMap<const EquivalenceClass *, std::unique_ptr<RewriteTreeNode>>
387408
RewriteTreeRoots;
388409

410+
/// The generation number for the term-rewriting system, which is
411+
/// increased every time a new rule gets added.
412+
unsigned RewriteGeneration = 0;
413+
414+
/// The generation at which the term-rewriting system was last minimized.
415+
unsigned LastRewriteMinimizedGeneration = 0;
416+
389417
/// The generation number, which is incremented whenever we successfully
390418
/// introduce a new constraint.
391419
unsigned Generation = 0;
@@ -396,6 +424,9 @@ struct GenericSignatureBuilder::Implementation {
396424
/// Whether we are currently processing delayed requirements.
397425
bool ProcessingDelayedRequirements = false;
398426

427+
/// Whether we are currently minimizing the term-rewriting system.
428+
bool MinimizingRewriteSystem = false;
429+
399430
/// Whether there were any errors.
400431
bool HadAnyError = false;
401432

@@ -426,6 +457,16 @@ struct GenericSignatureBuilder::Implementation {
426457
/// creating it if needed.
427458
RewriteTreeNode *getOrCreateRewriteTreeRoot(
428459
const EquivalenceClass *equivClass);
460+
461+
/// Minimize the rewrite tree by minimizing the right-hand sides and
462+
/// (TBD) removing redundant rules.
463+
void minimizeRewriteTree(GenericSignatureBuilder &builder);
464+
465+
private:
466+
/// Minimize the right-hand sides of the rewrite tree, simplifying them
467+
/// as far as possible and removing any changes that result in trivial
468+
/// rules.
469+
void minimizeRewriteTreeRhs(GenericSignatureBuilder &builder);
429470
};
430471

431472
#pragma mark Memory management
@@ -3095,6 +3136,16 @@ RewritePath::RewritePath(Optional<GenericParamKey> base,
30953136

30963137
Optional<RewritePath> RewritePath::createPath(Type type) {
30973138
SmallVector<AssociatedTypeDecl *, 4> path;
3139+
if (auto genericParam = createPath(type, path)) {
3140+
return RewritePath(*genericParam, path, Reverse);
3141+
}
3142+
3143+
return None;
3144+
}
3145+
3146+
Optional<GenericParamKey>
3147+
RewritePath::createPath(Type type,
3148+
SmallVectorImpl<AssociatedTypeDecl *> &path) {
30983149
while (auto depMemTy = type->getAs<DependentMemberType>()) {
30993150
auto assocType = depMemTy->getAssocType();
31003151
if (!assocType) return None;
@@ -3106,7 +3157,7 @@ Optional<RewritePath> RewritePath::createPath(Type type) {
31063157
auto genericParam = type->getAs<GenericTypeParamType>();
31073158
if (!genericParam) return None;
31083159

3109-
return RewritePath(GenericParamKey(genericParam), path, Reverse);
3160+
return GenericParamKey(genericParam);
31103161
}
31113162

31123163
RewritePath RewritePath::commonPath(const RewritePath &other) const {
@@ -3219,25 +3270,25 @@ class OrderTreeRewriteNode {
32193270
};
32203271
}
32213272

3222-
void RewriteTreeNode::addRewriteRule(RelativeRewritePath matchPath,
3273+
bool RewriteTreeNode::addRewriteRule(RelativeRewritePath matchPath,
32233274
RewritePath replacementPath) {
32243275
// If the match path is empty, we're adding the rewrite rule to this node.
32253276
if (matchPath.empty()) {
32263277
// If we don't already have a rewrite rule, add it.
32273278
if (!hasRewriteRule()) {
32283279
setRewriteRule(replacementPath);
3229-
return;
3280+
return true;
32303281
}
32313282

32323283
// If we already have this rewrite rule, we're done.
3233-
if (getRewriteRule() == replacementPath) return;
3284+
if (getRewriteRule() == replacementPath) return false;
32343285

32353286
// Check whether any of the continuation children matches.
32363287
auto insertPos = children.begin();
32373288
while (insertPos != children.end() && !(*insertPos)->getMatch()) {
32383289
if ((*insertPos)->hasRewriteRule() &&
32393290
(*insertPos)->getRewriteRule() == replacementPath)
3240-
return;
3291+
return false;
32413292

32423293
++insertPos;
32433294
}
@@ -3247,7 +3298,7 @@ void RewriteTreeNode::addRewriteRule(RelativeRewritePath matchPath,
32473298
auto newChild = new RewriteTreeNode(nullptr);
32483299
newChild->setRewriteRule(replacementPath);
32493300
children.insert(insertPos, newChild);
3250-
return;
3301+
return true;
32513302
}
32523303

32533304
// Find (or create) a child node describing the next step in the match.
@@ -3260,7 +3311,7 @@ void RewriteTreeNode::addRewriteRule(RelativeRewritePath matchPath,
32603311
}
32613312

32623313
// Add the rewrite rule to the child.
3263-
(*childPos)->addRewriteRule(matchPath.slice(1), replacementPath);
3314+
return (*childPos)->addRewriteRule(matchPath.slice(1), replacementPath);
32643315
}
32653316

32663317
void RewriteTreeNode::enumerateRewritePathsImpl(
@@ -3324,14 +3375,18 @@ RewriteTreeNode::bestRewritePath(GenericParamKey base, RelativeRewritePath path,
33243375
return best;
33253376
}
33263377

3327-
void RewriteTreeNode::mergeInto(RewriteTreeNode *other) {
3378+
bool RewriteTreeNode::mergeInto(RewriteTreeNode *other) {
33283379
// FIXME: A destructive version of this operation would be more efficient,
33293380
// since we generally don't care about \c other after doing this.
3330-
(void)enumerateRules([other](RelativeRewritePath lhs,
3331-
const RewritePath &rhs) {
3332-
other->addRewriteRule(lhs, rhs);
3381+
bool anyAdded = false;
3382+
(void)enumerateRules([other, &anyAdded](RelativeRewritePath lhs,
3383+
const RewritePath &rhs) {
3384+
if (other->addRewriteRule(lhs, rhs))
3385+
anyAdded = true;
33333386
return RuleAction::none();
33343387
});
3388+
3389+
return anyAdded;
33353390
}
33363391

33373392
bool RewriteTreeNode::enumerateRulesRec(
@@ -3436,22 +3491,120 @@ GenericSignatureBuilder::Implementation::getOrCreateRewriteTreeRoot(
34363491
return root.get();
34373492
}
34383493

3439-
void GenericSignatureBuilder::addSameTypeRewriteRule(
3494+
void GenericSignatureBuilder::Implementation::minimizeRewriteTree(
3495+
GenericSignatureBuilder &builder) {
3496+
// Only perform minimization if the term-rewriting tree has changed.
3497+
if (LastRewriteMinimizedGeneration == RewriteGeneration
3498+
|| MinimizingRewriteSystem)
3499+
return;
3500+
3501+
++NumRewriteMinimizations;
3502+
llvm::SaveAndRestore<bool> minimizingRewriteSystem(MinimizingRewriteSystem,
3503+
true);
3504+
SWIFT_DEFER {
3505+
LastRewriteMinimizedGeneration = RewriteGeneration;
3506+
};
3507+
3508+
minimizeRewriteTreeRhs(builder);
3509+
}
3510+
3511+
void GenericSignatureBuilder::Implementation::minimizeRewriteTreeRhs(
3512+
GenericSignatureBuilder &builder) {
3513+
assert(MinimizingRewriteSystem);
3514+
3515+
// Minimize the right-hand sides of each rule in the tree.
3516+
for (auto &equivClass : EquivalenceClasses) {
3517+
auto root = RewriteTreeRoots.find(&equivClass);
3518+
if (root == RewriteTreeRoots.end()) continue;
3519+
3520+
// Stores the anchor base and path, when we've computed it.
3521+
Optional<RewritePath> anchorPath;
3522+
3523+
// Make sure that anchorBase/anchorPath are populated.
3524+
auto populateAnchor = [&] {
3525+
if (anchorPath) return false;
3526+
3527+
// Get the pieces of the path for the anchor.
3528+
anchorPath = RewritePath::createPath(equivClass.getAnchor(builder, { }));
3529+
if (!anchorPath) return true;
3530+
3531+
return false;
3532+
};
3533+
3534+
ASTContext &ctx = builder.getASTContext();
3535+
root->second->enumerateRules([&](RelativeRewritePath lhs,
3536+
const RewritePath &rhs) {
3537+
// Compute the type of the right-hand side.
3538+
Type rhsType;
3539+
if (rhs.getBase()) {
3540+
rhsType = rhs.formDependentType(ctx);
3541+
} else {
3542+
// We need the anchor of this equivalence class.
3543+
if (populateAnchor())
3544+
return RewriteTreeNode::RuleAction::none();
3545+
3546+
// Add the right-hand side to the anchor path we have.
3547+
SmallVector<AssociatedTypeDecl *, 4> absoluteRhsPath;
3548+
absoluteRhsPath.append(anchorPath->getPath().begin(),
3549+
anchorPath->getPath().end());
3550+
absoluteRhsPath.append(rhs.getPath().begin(), rhs.getPath().end());
3551+
rhsType = formDependentType(ctx, *anchorPath->getBase(),
3552+
absoluteRhsPath);
3553+
}
3554+
3555+
// Compute the canonical type for the right-hand side.
3556+
Type canonicalRhsType = builder.getCanonicalTypeParameter(rhsType);
3557+
3558+
// If the canonicalized result is equivalent to the right-hand side we
3559+
// had, there's nothing to do.
3560+
if (rhsType->isEqual(canonicalRhsType))
3561+
return RewriteTreeNode::RuleAction::none();
3562+
3563+
// We have a canonical replacement path. Determine its encoding and
3564+
// perform the replacement.
3565+
++NumRewriteRhsSimplified;
3566+
3567+
// Determine replacement path, which might be relative to the anchor.
3568+
auto canonicalRhsPath = *RewritePath::createPath(canonicalRhsType);
3569+
populateAnchor();
3570+
if (auto prefix = anchorPath->commonPath(canonicalRhsPath)) {
3571+
unsigned prefixLength = prefix.getPath().size();
3572+
RelativeRewritePath replacementRhsPath =
3573+
canonicalRhsPath.getPath().slice(prefixLength);
3574+
3575+
// If the left and right-hand sides are equivalent, just remove the
3576+
// rule.
3577+
if (lhs == replacementRhsPath) {
3578+
++NumRewriteRhsSimplifiedToLhs;
3579+
return RewriteTreeNode::RuleAction::remove();
3580+
}
3581+
3582+
RewritePath replacementRhs(None, replacementRhsPath,
3583+
RewritePath::Forward);
3584+
return RewriteTreeNode::RuleAction::replace(std::move(replacementRhs));
3585+
}
3586+
3587+
return RewriteTreeNode::RuleAction::replace(canonicalRhsPath);
3588+
});
3589+
}
3590+
}
3591+
3592+
bool GenericSignatureBuilder::addSameTypeRewriteRule(
34403593
EquivalenceClass *equivClass,
34413594
PotentialArchetype *otherPA){
34423595
// Simplify both sides in the hope of uncovering a common path.
34433596
Type simplifiedType1 = equivClass->getAnchor(*this, { });
3444-
if (!simplifiedType1) return;
3597+
if (!simplifiedType1) return false;
34453598

34463599
Type simplifiedType2;
34473600
if (auto otherEquivClass = otherPA->getEquivalenceClassIfPresent())
34483601
simplifiedType2 = otherEquivClass->getAnchor(*this, { });
34493602
else
34503603
simplifiedType2 = getCanonicalTypeParameter(otherPA->getDependentType({ }));
3451-
if (!simplifiedType2) return;
3604+
if (!simplifiedType2) return false;
34523605

34533606
// We already effectively have this rewrite rule.
3454-
if (simplifiedType1->isEqual(simplifiedType2)) return;
3607+
if (simplifiedType1->isEqual(simplifiedType2)) return false;
34553608

34563609
auto path1 = *RewritePath::createPath(simplifiedType1);
34573610
auto path2 = *RewritePath::createPath(simplifiedType2);
@@ -3476,10 +3629,9 @@ void GenericSignatureBuilder::addSameTypeRewriteRule(
34763629

34773630
// Add the rewrite rule.
34783631
auto root = Impl->getOrCreateRewriteTreeRoot(equivClass);
3479-
root->addRewriteRule(relPath1,
3480-
RewritePath(None, relPath2, RewritePath::Forward));
3481-
3482-
return;
3632+
return root->addRewriteRule(
3633+
relPath1,
3634+
RewritePath(None, relPath2, RewritePath::Forward));
34833635
}
34843636

34853637
// Otherwise, form a rewrite rule with absolute paths.
@@ -3499,7 +3651,7 @@ void GenericSignatureBuilder::addSameTypeRewriteRule(
34993651
assert(baseEquivClass && "Base cannot be resolved?");
35003652

35013653
auto root = Impl->getOrCreateRewriteTreeRoot(baseEquivClass);
3502-
root->addRewriteRule(path1.getPath(), path2);
3654+
return root->addRewriteRule(path1.getPath(), path2);
35033655
}
35043656

35053657
Type GenericSignatureBuilder::getCanonicalTypeParameter(Type type) {
@@ -4648,7 +4800,8 @@ GenericSignatureBuilder::addSameTypeRequirementBetweenArchetypes(
46484800

46494801
// Add a rewrite rule to map T2 down to the anchor.
46504802
auto equivClass = T1->getOrCreateEquivalenceClass(*this);
4651-
addSameTypeRewriteRule(equivClass, T2);
4803+
if (addSameTypeRewriteRule(equivClass, T2))
4804+
++Impl->RewriteGeneration;
46524805

46534806
// Merge the equivalence classes.
46544807
equivClass->modified(*this);
@@ -4679,7 +4832,8 @@ GenericSignatureBuilder::addSameTypeRequirementBetweenArchetypes(
46794832
if (auto rewriteRoot2 = Impl->getOrCreateRewriteTreeRoot(equivClass2)) {
46804833
if (auto rewriteRoot1 = Impl->getOrCreateRewriteTreeRoot(equivClass)) {
46814834
// Merge the second rewrite tree into the first.
4682-
rewriteRoot2->mergeInto(rewriteRoot1);
4835+
if (rewriteRoot2->mergeInto(rewriteRoot1))
4836+
++Impl->RewriteGeneration;
46834837
Impl->RewriteTreeRoots.erase(equivClass2);
46844838
} else {
46854839
// Take the second rewrite tree and make it the first.

0 commit comments

Comments
 (0)