Skip to content

Commit 3a3e887

Browse files
committed
[GSB] Cache the nested types of an equivalence class.
The GSB performs repeated lookups of the same nested type (by name) within a given equivalence class. Cache the results of this lookup.
1 parent 70861f6 commit 3a3e887

File tree

2 files changed

+69
-24
lines changed

2 files changed

+69
-24
lines changed

include/swift/AST/GenericSignatureBuilder.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,17 @@ class GenericSignatureBuilder {
240240
/// anchor was cached.
241241
unsigned numMembers;
242242
} archetypeAnchorCache;
243+
244+
/// Describes a cached nested type.
245+
struct CachedNestedType {
246+
unsigned numConformancesPresent;
247+
CanType superclassPresent;
248+
llvm::TinyPtrVector<TypeDecl *> types;
249+
};
250+
251+
/// Cached nested-type information, which contains the best declaration
252+
/// for a given name.
253+
llvm::SmallDenseMap<Identifier, CachedNestedType> nestedTypeNameCache;
243254
};
244255

245256
friend class RequirementSource;

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 58 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ STATISTIC(NumArchetypeAnchorCacheHits,
7676
"# of hits in the archetype anchor cache");
7777
STATISTIC(NumArchetypeAnchorCacheMisses,
7878
"# of misses in the archetype anchor cache");
79+
STATISTIC(NumNestedTypeCacheHits,
80+
"# of hits in the equivalence class nested type cache");
81+
STATISTIC(NumNestedTypeCacheMisses,
82+
"# of misses in the equivalence class nested type cache");
7983
STATISTIC(NumProcessDelayedRequirements,
8084
"# of times we process delayed requirements");
8185
STATISTIC(NumProcessDelayedRequirementsUnchanged,
@@ -1585,6 +1589,38 @@ static int compareAssociatedTypes(AssociatedTypeDecl *assocType1,
15851589
TypeDecl *EquivalenceClass::lookupNestedType(
15861590
Identifier name,
15871591
SmallVectorImpl<TypeDecl *> *otherConcreteTypes) {
1592+
// Populates the result structures from the given cache entry.
1593+
auto populateResult = [&](const CachedNestedType &cache) -> TypeDecl * {
1594+
if (otherConcreteTypes)
1595+
otherConcreteTypes->clear();
1596+
1597+
// If there aren't any types in the cache, we're done.
1598+
if (cache.types.empty()) return nullptr;
1599+
1600+
// The first type in the cache is always the final result.
1601+
// Collect the rest in the concrete-declarations list, if needed.
1602+
if (otherConcreteTypes) {
1603+
for (auto type : ArrayRef<TypeDecl *>(cache.types).slice(1)) {
1604+
otherConcreteTypes->push_back(type);
1605+
}
1606+
}
1607+
1608+
return cache.types.front();
1609+
};
1610+
1611+
// If we have a cached value that is up-to-date, use that.
1612+
auto cached = nestedTypeNameCache.find(name);
1613+
if (cached != nestedTypeNameCache.end() &&
1614+
cached->second.numConformancesPresent == conformsTo.size() &&
1615+
(!superclass ||
1616+
cached->second.superclassPresent == superclass->getCanonicalType())) {
1617+
++NumNestedTypeCacheHits;
1618+
return populateResult(cached->second);
1619+
}
1620+
1621+
// Cache miss; go compute the result.
1622+
++NumNestedTypeCacheMisses;
1623+
15881624
// Look for types with the given name in protocols that we know about.
15891625
AssociatedTypeDecl *bestAssocType = nullptr;
15901626
SmallVector<TypeDecl *, 4> concreteDecls;
@@ -1653,34 +1689,32 @@ TypeDecl *EquivalenceClass::lookupNestedType(
16531689
}
16541690
}
16551691

1656-
// If we have an associated type, that's our result.
1692+
// Form the new cache entry.
1693+
CachedNestedType entry;
1694+
entry.numConformancesPresent = conformsTo.size();
1695+
entry.superclassPresent =
1696+
superclass ? superclass->getCanonicalType() : CanType();
16571697
if (bestAssocType) {
1658-
// Populate the list of concrete types, if requested.
1659-
if (otherConcreteTypes)
1660-
otherConcreteTypes->assign(concreteDecls.begin(), concreteDecls.end());
1661-
1662-
return bestAssocType;
1663-
}
1664-
1665-
// If there were no concrete types either, lookup failed.
1666-
if (concreteDecls.empty())
1667-
return nullptr;
1668-
1669-
// Find the best concrete type.
1670-
auto bestConcreteTypeIter =
1671-
std::min_element(concreteDecls.begin(), concreteDecls.end(),
1672-
[](TypeDecl *type1, TypeDecl *type2) {
1673-
return TypeDecl::compare(type1, type2) < 0;
1674-
});
1698+
entry.types.push_back(bestAssocType);
1699+
entry.types.insert(entry.types.end(),
1700+
concreteDecls.begin(), concreteDecls.end());
1701+
} else if (!concreteDecls.empty()) {
1702+
// Find the best concrete type.
1703+
auto bestConcreteTypeIter =
1704+
std::min_element(concreteDecls.begin(), concreteDecls.end(),
1705+
[](TypeDecl *type1, TypeDecl *type2) {
1706+
return TypeDecl::compare(type1, type2) < 0;
1707+
});
16751708

1676-
// If were asked to provide all of the concrete types, do so now.
1677-
if (otherConcreteTypes) {
1678-
otherConcreteTypes->assign(concreteDecls.begin(), bestConcreteTypeIter);
1679-
otherConcreteTypes->insert(otherConcreteTypes->end(),
1680-
bestConcreteTypeIter + 1, concreteDecls.end());
1709+
// Put the best concrete type first; the rest will follow.
1710+
entry.types.push_back(*bestConcreteTypeIter);
1711+
entry.types.insert(entry.types.end(),
1712+
concreteDecls.begin(), bestConcreteTypeIter);
1713+
entry.types.insert(entry.types.end(),
1714+
bestConcreteTypeIter + 1, concreteDecls.end());
16811715
}
16821716

1683-
return *bestConcreteTypeIter;
1717+
return populateResult((nestedTypeNameCache[name] = std::move(entry)));
16841718
}
16851719

16861720
void EquivalenceClass::dump(llvm::raw_ostream &out) const {

0 commit comments

Comments
 (0)