Skip to content

Commit 648a837

Browse files
mikeashDougGregor
authored andcommitted
Add caching based on ABI name to _findContextDescriptor.
This is a one-to-many cache that's more speculative than the cache mapping mangled names to context descriptors. Entries found in the cache need to be verified for a match before they can be returned. However, this allows scanning conformance records up front and building up the cache in one scan rather than performing an expensive scan of all conformance records every time the mangled name cache misses. rdar://problem/53560010
1 parent 98b595e commit 648a837

File tree

3 files changed

+103
-1
lines changed

3 files changed

+103
-1
lines changed

stdlib/public/runtime/MetadataLookup.cpp

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "llvm/ADT/PointerIntPair.h"
3333
#include "llvm/ADT/PointerUnion.h"
3434
#include "llvm/ADT/StringExtras.h"
35+
#include "llvm/ADT/TinyPtrVector.h"
3536
#include "Private.h"
3637
#include "CompatibilityOverride.h"
3738
#include "ImageInspection.h"
@@ -195,9 +196,19 @@ namespace {
195196
};
196197
} // end anonymous namespace
197198

199+
llvm::hash_code llvm::hash_value(StringRef S) {
200+
return hash_combine_range(S.begin(), S.end());
201+
}
202+
198203
struct TypeMetadataPrivateState {
199204
ConcurrentMap<NominalTypeDescriptorCacheEntry> NominalCache;
200205
ConcurrentReadableArray<TypeMetadataSection> SectionsToScan;
206+
207+
llvm::DenseMap<llvm::StringRef,
208+
llvm::TinyPtrVector<const ContextDescriptor *>>
209+
ContextDescriptorCache;
210+
size_t ContextDescriptorLastSectionScanned = 0;
211+
Mutex ContextDescriptorCacheLock;
201212

202213
TypeMetadataPrivateState() {
203214
initializeTypeMetadataRecordLookup();
@@ -603,6 +614,60 @@ _searchTypeMetadataRecords(TypeMetadataPrivateState &T,
603614
return nullptr;
604615
}
605616

617+
// Read ContextDescriptors for any loaded images that haven't already been
618+
// scanned, if any.
619+
static void
620+
_scanAdditionalContextDescriptors(TypeMetadataPrivateState &T) {
621+
_forEachProtocolConformanceSectionAfter(
622+
&T.ContextDescriptorLastSectionScanned,
623+
[&T](const ProtocolConformanceRecord *Begin,
624+
const ProtocolConformanceRecord *End) {
625+
for (const auto *record = Begin; record != End; record++) {
626+
if (auto ntd = record[0]->getTypeDescriptor()) {
627+
if (auto type = llvm::dyn_cast<TypeContextDescriptor>(ntd)) {
628+
auto identity = ParsedTypeIdentity::parse(type);
629+
auto name = identity.getABIName();
630+
T.ContextDescriptorCache[name].push_back(type);
631+
}
632+
}
633+
}
634+
});
635+
}
636+
637+
// Search for a ContextDescriptor in the context descriptor cache matching the
638+
// given demangle node. Returns the found node, or nullptr if no match was
639+
// found.
640+
static const ContextDescriptor *
641+
_findContextDescriptorInCache(TypeMetadataPrivateState &T,
642+
Demangle::NodePointer node) {
643+
if (node->getNumChildren() < 2)
644+
return nullptr;
645+
646+
auto nameNode = node->getChild(1);
647+
648+
// Declarations synthesized by the Clang importer get a small tag
649+
// string in addition to their name.
650+
if (nameNode->getKind() == Demangle::Node::Kind::RelatedEntityDeclName)
651+
nameNode = nameNode->getChild(1);
652+
653+
if (nameNode->getKind() != Demangle::Node::Kind::Identifier)
654+
return nullptr;
655+
656+
auto name = nameNode->getText();
657+
658+
auto iter = T.ContextDescriptorCache.find(name);
659+
if (iter == T.ContextDescriptorCache.end())
660+
return nullptr;
661+
662+
for (auto *contextDescriptor : iter->getSecond()) {
663+
if (_contextDescriptorMatchesMangling(contextDescriptor, node)) {
664+
return contextDescriptor;
665+
}
666+
}
667+
668+
return nullptr;
669+
}
670+
606671
static const ContextDescriptor *
607672
_findContextDescriptor(Demangle::NodePointer node,
608673
Demangle::Demangler &Dem) {
@@ -630,8 +695,17 @@ _findContextDescriptor(Demangle::NodePointer node,
630695
if (auto Value = T.NominalCache.find(mangledName))
631696
return Value->getDescription();
632697

698+
// Scan any newly loaded images for context descriptors, then try the context
699+
// descriptor cache. This must be done with the cache's lock held.
700+
{
701+
ScopedLock guard(T.ContextDescriptorCacheLock);
702+
_scanAdditionalContextDescriptors(T);
703+
foundContext = _findContextDescriptorInCache(T, node);
704+
}
705+
633706
// Check type metadata records
634-
foundContext = _searchTypeMetadataRecords(T, node);
707+
if (!foundContext)
708+
foundContext = _searchTypeMetadataRecords(T, node);
635709

636710
// Check protocol conformances table. Note that this has no support for
637711
// resolving generic types yet.

stdlib/public/runtime/Private.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,19 @@ class TypeInfo {
249249
const ContextDescriptor *
250250
_searchConformancesByMangledTypeName(Demangle::NodePointer node);
251251

252+
/// Iterate over protocol conformance sections starting from the given index.
253+
/// The index is updated to the current number of protocol sections. Passing
254+
/// the same index to the next call will iterate over any sections that were
255+
/// added after the previous call.
256+
///
257+
/// Takes a function to call for each section found. The two parameters are
258+
/// the start and end of the section.
259+
void
260+
_forEachProtocolConformanceSectionAfter(
261+
size_t *start,
262+
const std::function<void(const ProtocolConformanceRecord *,
263+
const ProtocolConformanceRecord *)> &f);
264+
252265
Demangle::NodePointer _swift_buildDemanglingForMetadata(const Metadata *type,
253266
Demangle::Demangler &Dem);
254267

stdlib/public/runtime/ProtocolConformance.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,21 @@ swift::_searchConformancesByMangledTypeName(Demangle::NodePointer node) {
634634
return nullptr;
635635
}
636636

637+
void
638+
swift::_forEachProtocolConformanceSectionAfter(
639+
size_t *start,
640+
const std::function<void(const ProtocolConformanceRecord *,
641+
const ProtocolConformanceRecord *)> &f) {
642+
auto snapshot = Conformances.get().SectionsToScan.snapshot();
643+
if (snapshot.Count > *start) {
644+
auto *begin = snapshot.begin() + *start;
645+
auto *end = snapshot.end();
646+
for (auto *section = begin; section != end; section++) {
647+
f(section->Begin, section->End);
648+
}
649+
}
650+
}
651+
637652
bool swift::_checkGenericRequirements(
638653
llvm::ArrayRef<GenericRequirementDescriptor> requirements,
639654
SmallVectorImpl<const void *> &extraArguments,

0 commit comments

Comments
 (0)