Skip to content

Commit 624f2ba

Browse files
committed
[GSB] Eliminate redundant rules from the rewrite tree.
As part of minimization, example each rule to determine whether minimizing the left-hand-side (while ignoring the rule under question) still produces the right-hand side. If so, the rule is redundant and will be eliminated from the rewrite tree.
1 parent 91aa964 commit 624f2ba

File tree

1 file changed

+138
-44
lines changed

1 file changed

+138
-44
lines changed

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 138 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,17 @@ STATISTIC(NumRewriteRhsSimplified,
113113
"# of rewrite rule right-hand sides simplified");
114114
STATISTIC(NumRewriteRhsSimplifiedToLhs,
115115
"# of rewrite rule right-hand sides simplified to lhs (and removed)");
116+
STATISTIC(NumRewriteRulesRedundant,
117+
"# of rewrite rules that are redundant (and removed)");
116118

117119
namespace {
118120

119121
/// A purely-relative rewrite path consisting of a (possibly empty)
120122
/// sequence of associated type references.
121123
using RelativeRewritePath = ArrayRef<AssociatedTypeDecl *>;
122124

125+
class AnchorPathCache;
126+
123127
/// Describes a rewrite path, which contains an optional base (generic
124128
/// parameter) followed by a sequence of associated type references.
125129
class RewritePath {
@@ -179,8 +183,10 @@ class RewritePath {
179183

180184
/// Form a canonical, dependent type.
181185
///
182-
/// This requires that the rewrite path have a base.
183-
CanType formDependentType(ASTContext &ctx) const;
186+
/// This requires that either the rewrite path have a base, or the
187+
/// \c baseEquivClass to be non-null (which substitutes in a base).
188+
CanType formDependentType(ASTContext &ctx,
189+
AnchorPathCache *anchorPathCache = nullptr) const;
184190

185191
/// Compare the given rewrite paths.
186192
int compare(const RewritePath &other) const;
@@ -198,6 +204,26 @@ class RewritePath {
198204
}
199205
};
200206

207+
/// A cache that lazily computes the anchor path for the given equivalence
208+
/// class.
209+
class AnchorPathCache {
210+
GenericSignatureBuilder &builder;
211+
EquivalenceClass &equivClass;
212+
Optional<RewritePath> anchorPath;
213+
214+
public:
215+
AnchorPathCache(GenericSignatureBuilder &builder,
216+
EquivalenceClass &equivClass)
217+
: builder(builder), equivClass(equivClass) { }
218+
219+
Optional<RewritePath> getAnchorPath() {
220+
if (anchorPath) return anchorPath;
221+
222+
anchorPath = RewritePath::createPath(equivClass.getAnchor(builder, { }));
223+
return anchorPath;
224+
}
225+
};
226+
201227
/// A node within the prefix tree that is used to match associated type
202228
/// references.
203229
class RewriteTreeNode {
@@ -262,11 +288,17 @@ class RewriteTreeNode {
262288
}
263289

264290
/// Retrieve the path to which this node will be rewritten.
265-
const RewritePath &getRewriteRule() const {
291+
const RewritePath &getRewriteRule() const & {
266292
assert(hasRewriteRule());
267293
return rewrite;
268294
}
269295

296+
/// Retrieve the path to which this node will be rewritten.
297+
RewritePath &&getRewriteRule() && {
298+
assert(hasRewriteRule());
299+
return std::move(rewrite);
300+
}
301+
270302
/// Add a new rewrite rule to this tree node.
271303
///
272304
/// \param matchPath The path of associated type declarations that must
@@ -359,9 +391,10 @@ class RewriteTreeNode {
359391
/// right-hand sides of each rule.
360392
///
361393
/// \returns true if the action function returned \c Stop at any point.
362-
bool enumerateRules(llvm::function_ref<EnumerateCallback> fn) {
394+
bool enumerateRules(llvm::function_ref<EnumerateCallback> fn,
395+
bool temporarilyDisableVisitedRule = false) {
363396
SmallVector<AssociatedTypeDecl *, 4> lhs;
364-
return enumerateRulesRec(fn, lhs);
397+
return enumerateRulesRec(fn, temporarilyDisableVisitedRule, lhs);
365398
}
366399

367400
LLVM_ATTRIBUTE_DEPRECATED(void dump() const LLVM_ATTRIBUTE_USED,
@@ -376,6 +409,7 @@ class RewriteTreeNode {
376409
///
377410
/// \returns true if the action function returned \c Stop at any point.
378411
bool enumerateRulesRec(llvm::function_ref<EnumerateCallback> &fn,
412+
bool temporarilyDisableVisitedRule,
379413
llvm::SmallVectorImpl<AssociatedTypeDecl *> &lhs);
380414
};
381415
}
@@ -459,14 +493,19 @@ struct GenericSignatureBuilder::Implementation {
459493
const EquivalenceClass *equivClass);
460494

461495
/// Minimize the rewrite tree by minimizing the right-hand sides and
462-
/// (TBD) removing redundant rules.
496+
/// removing redundant rules.
463497
void minimizeRewriteTree(GenericSignatureBuilder &builder);
464498

465499
private:
466500
/// Minimize the right-hand sides of the rewrite tree, simplifying them
467501
/// as far as possible and removing any changes that result in trivial
468502
/// rules.
469503
void minimizeRewriteTreeRhs(GenericSignatureBuilder &builder);
504+
505+
/// Minimize the right-hand sides of the rewrite tree, simplifying them
506+
/// as far as possible and removing any changes that result in trivial
507+
/// rules.
508+
void removeRewriteTreeRedundancies(GenericSignatureBuilder &builder);
470509
};
471510

472511
#pragma mark Memory management
@@ -2250,6 +2289,9 @@ Type EquivalenceClass::getAnchor(
22502289
return substAnchor();
22512290
}
22522291

2292+
// Always work with a minimized term-rewriting system.
2293+
builder.Impl->minimizeRewriteTree(builder);
2294+
22532295
// Form the anchor.
22542296
bool updatedAnchor = false;
22552297
for (auto member : members) {
@@ -3198,9 +3240,24 @@ static Type formDependentType(ASTContext &ctx, GenericParamKey genericParam,
31983240
path);
31993241
}
32003242

3201-
CanType RewritePath::formDependentType(ASTContext &ctx) const {
3202-
assert(getBase());
3203-
return CanType(::formDependentType(ctx, *getBase(), getPath()));
3243+
CanType RewritePath::formDependentType(
3244+
ASTContext &ctx,
3245+
AnchorPathCache *anchorPathCache) const {
3246+
if (auto base = getBase())
3247+
return CanType(::formDependentType(ctx, *base, getPath()));
3248+
3249+
assert(anchorPathCache && "Need an anchor path cache");
3250+
Optional<RewritePath> anchorPath = anchorPathCache->getAnchorPath();
3251+
if (!anchorPath) return CanType();
3252+
3253+
// Add the relative path to the anchor path.
3254+
SmallVector<AssociatedTypeDecl *, 4> absolutePath;
3255+
absolutePath.append(anchorPath->getPath().begin(),
3256+
anchorPath->getPath().end());
3257+
absolutePath.append(getPath().begin(), getPath().end());
3258+
return CanType(::formDependentType(ctx, *anchorPath->getBase(),
3259+
absolutePath));
3260+
32043261
}
32053262

32063263
int RewritePath::compare(const RewritePath &other) const {
@@ -3391,6 +3448,7 @@ bool RewriteTreeNode::mergeInto(RewriteTreeNode *other) {
33913448

33923449
bool RewriteTreeNode::enumerateRulesRec(
33933450
llvm::function_ref<EnumerateCallback> &fn,
3451+
bool temporarilyDisableVisitedRule,
33943452
llvm::SmallVectorImpl<AssociatedTypeDecl *> &lhs) {
33953453
if (auto assocType = getMatch())
33963454
lhs.push_back(assocType);
@@ -3402,27 +3460,50 @@ bool RewriteTreeNode::enumerateRulesRec(
34023460

34033461
// If there is a rewrite rule, invoke the callback.
34043462
if (hasRewriteRule()) {
3405-
switch (RuleAction action = fn(lhs, getRewriteRule())) {
3463+
// If we're supposed to temporarily disabled the visited rule, do so
3464+
// now.
3465+
Optional<RewritePath> rewriteRule;
3466+
if (temporarilyDisableVisitedRule) {
3467+
rewriteRule = std::move(*this).getRewriteRule();
3468+
removeRewriteRule();
3469+
}
3470+
3471+
// Make sure that we put the rewrite rule back in place if we moved it
3472+
// aside.
3473+
SWIFT_DEFER {
3474+
if (temporarilyDisableVisitedRule && rewriteRule)
3475+
setRewriteRule(*std::move(rewriteRule));
3476+
};
3477+
3478+
switch (auto action =
3479+
fn(lhs, rewriteRule ? *rewriteRule : getRewriteRule())) {
34063480
case RuleAction::None:
34073481
break;
34083482

34093483
case RuleAction::Stop:
34103484
return true;
34113485

34123486
case RuleAction::Remove:
3413-
removeRewriteRule();
3487+
if (temporarilyDisableVisitedRule)
3488+
rewriteRule = None;
3489+
else
3490+
removeRewriteRule();
34143491
break;
34153492

34163493
case RuleAction::Replace:
3417-
removeRewriteRule();
3418-
setRewriteRule(action.path);
3494+
if (temporarilyDisableVisitedRule) {
3495+
rewriteRule = std::move(action.path);
3496+
} else {
3497+
removeRewriteRule();
3498+
setRewriteRule(action.path);
3499+
}
34193500
break;
34203501
}
34213502
}
34223503

34233504
// Recurse into the child nodes.
34243505
for (auto child : children) {
3425-
if (child->enumerateRulesRec(fn, lhs))
3506+
if (child->enumerateRulesRec(fn, temporarilyDisableVisitedRule, lhs))
34263507
return true;
34273508
}
34283509

@@ -3506,6 +3587,7 @@ void GenericSignatureBuilder::Implementation::minimizeRewriteTree(
35063587
};
35073588

35083589
minimizeRewriteTreeRhs(builder);
3590+
removeRewriteTreeRedundancies(builder);
35093591
}
35103592

35113593
void GenericSignatureBuilder::Implementation::minimizeRewriteTreeRhs(
@@ -3517,40 +3599,14 @@ void GenericSignatureBuilder::Implementation::minimizeRewriteTreeRhs(
35173599
auto root = RewriteTreeRoots.find(&equivClass);
35183600
if (root == RewriteTreeRoots.end()) continue;
35193601

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-
};
3602+
AnchorPathCache anchorPathCache(builder, equivClass);
35333603

35343604
ASTContext &ctx = builder.getASTContext();
35353605
root->second->enumerateRules([&](RelativeRewritePath lhs,
35363606
const RewritePath &rhs) {
35373607
// 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-
}
3608+
Type rhsType = rhs.formDependentType(ctx, &anchorPathCache);
3609+
if (!rhsType) return RewriteTreeNode::RuleAction::none();
35543610

35553611
// Compute the canonical type for the right-hand side.
35563612
Type canonicalRhsType = builder.getCanonicalTypeParameter(rhsType);
@@ -3566,7 +3622,7 @@ void GenericSignatureBuilder::Implementation::minimizeRewriteTreeRhs(
35663622

35673623
// Determine replacement path, which might be relative to the anchor.
35683624
auto canonicalRhsPath = *RewritePath::createPath(canonicalRhsType);
3569-
populateAnchor();
3625+
auto anchorPath = anchorPathCache.getAnchorPath();
35703626
if (auto prefix = anchorPath->commonPath(canonicalRhsPath)) {
35713627
unsigned prefixLength = prefix.getPath().size();
35723628
RelativeRewritePath replacementRhsPath =
@@ -3589,6 +3645,44 @@ void GenericSignatureBuilder::Implementation::minimizeRewriteTreeRhs(
35893645
}
35903646
}
35913647

3648+
void GenericSignatureBuilder::Implementation::removeRewriteTreeRedundancies(
3649+
GenericSignatureBuilder &builder) {
3650+
assert(MinimizingRewriteSystem);
3651+
3652+
// Minimize the right-hand sides of each rule in the tree.
3653+
for (auto &equivClass : EquivalenceClasses) {
3654+
auto root = RewriteTreeRoots.find(&equivClass);
3655+
if (root == RewriteTreeRoots.end()) continue;
3656+
3657+
AnchorPathCache anchorPathCache(builder, equivClass);
3658+
3659+
ASTContext &ctx = builder.getASTContext();
3660+
root->second->enumerateRules([&](RelativeRewritePath lhs,
3661+
const RewritePath &rhs) {
3662+
/// Left-hand side type.
3663+
Type lhsType = RewritePath(None, lhs, RewritePath::Forward)
3664+
.formDependentType(ctx, &anchorPathCache);
3665+
if (!lhsType) return RewriteTreeNode::RuleAction::none();
3666+
3667+
// Simplify the left-hand type.
3668+
Type simplifiedLhsType = builder.getCanonicalTypeParameter(lhsType);
3669+
if (!simplifiedLhsType) return RewriteTreeNode::RuleAction::none();
3670+
3671+
// Compute the type of the right-hand side.
3672+
Type rhsType = rhs.formDependentType(ctx, &anchorPathCache);
3673+
if (!rhsType) return RewriteTreeNode::RuleAction::none();
3674+
3675+
if (simplifiedLhsType->isEqual(rhsType)) {
3676+
++NumRewriteRulesRedundant;
3677+
return RewriteTreeNode::RuleAction::remove();
3678+
}
3679+
3680+
return RewriteTreeNode::RuleAction::none();
3681+
},
3682+
/*temporarilyDisableVisitedRule=*/true);
3683+
}
3684+
}
3685+
35923686
bool GenericSignatureBuilder::addSameTypeRewriteRule(
35933687
EquivalenceClass *equivClass,
35943688
PotentialArchetype *otherPA){

0 commit comments

Comments
 (0)