Skip to content

[5.1] [Runtime] Add caching based on ABI name to _findContextDescriptor. #26581

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

125 changes: 123 additions & 2 deletions stdlib/public/runtime/MetadataLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "swift/Runtime/Mutex.h"
#include "swift/Strings.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/PointerUnion.h"
Expand Down Expand Up @@ -195,9 +196,20 @@ namespace {
};
} // end anonymous namespace

inline llvm::hash_code llvm::hash_value(StringRef S) {
return hash_combine_range(S.begin(), S.end());
}

struct TypeMetadataPrivateState {
ConcurrentMap<NominalTypeDescriptorCacheEntry> NominalCache;
ConcurrentReadableArray<TypeMetadataSection> SectionsToScan;

llvm::DenseMap<llvm::StringRef,
llvm::SmallDenseSet<const ContextDescriptor *, 1>>
ContextDescriptorCache;
size_t ConformanceDescriptorLastSectionScanned = 0;
size_t TypeContextDescriptorLastSectionScanned = 0;
Mutex ContextDescriptorCacheLock;

TypeMetadataPrivateState() {
initializeTypeMetadataRecordLookup();
Expand All @@ -214,6 +226,29 @@ _registerTypeMetadataRecords(TypeMetadataPrivateState &T,
T.SectionsToScan.push_back(TypeMetadataSection{begin, end});
}

/// Iterate over type metadata sections starting from the given index.
/// The index is updated to the current number of sections. Passing
/// the same index to the next call will iterate over any sections that were
/// added after the previous call.
///
/// Takes a function to call for each section found. The two parameters are
/// the start and end of the section.
static void _forEachTypeMetadataSectionAfter(
TypeMetadataPrivateState &T,
size_t *start,
const std::function<void(const TypeMetadataRecord *,
const TypeMetadataRecord *)> &f) {
auto snapshot = T.SectionsToScan.snapshot();
if (snapshot.Count > *start) {
auto *begin = snapshot.begin() + *start;
auto *end = snapshot.end();
for (auto *section = begin; section != end; section++) {
f(section->Begin, section->End);
}
*start = snapshot.Count;
}
}

void swift::addImageTypeMetadataRecordBlockCallback(const void *records,
uintptr_t recordsSize) {
assert(recordsSize % sizeof(TypeMetadataRecord) == 0
Expand Down Expand Up @@ -603,6 +638,70 @@ _searchTypeMetadataRecords(TypeMetadataPrivateState &T,
return nullptr;
}

// Read ContextDescriptors for any loaded images that haven't already been
// scanned, if any.
static void
_scanAdditionalContextDescriptors(TypeMetadataPrivateState &T) {
_forEachTypeMetadataSectionAfter(
T,
&T.TypeContextDescriptorLastSectionScanned,
[&T](const TypeMetadataRecord *Begin,
const TypeMetadataRecord *End) {
for (const auto *record = Begin; record != End; record++) {
if (auto ntd = record->getContextDescriptor()) {
if (auto type = llvm::dyn_cast<TypeContextDescriptor>(ntd)) {
auto identity = ParsedTypeIdentity::parse(type);
auto name = identity.getABIName();
T.ContextDescriptorCache[name].insert(type);
}
}
}
});

_forEachProtocolConformanceSectionAfter(
&T.ConformanceDescriptorLastSectionScanned,
[&T](const ProtocolConformanceRecord *Begin,
const ProtocolConformanceRecord *End) {
for (const auto *record = Begin; record != End; record++) {
if (auto ntd = record[0]->getTypeDescriptor()) {
if (auto type = llvm::dyn_cast<TypeContextDescriptor>(ntd)) {
auto identity = ParsedTypeIdentity::parse(type);
auto name = identity.getABIName();
T.ContextDescriptorCache[name].insert(type);
}
}
}
});
}

// Search for a ContextDescriptor in the context descriptor cache matching the
// given demangle node. Returns the found node, or nullptr if no match was
// found.
static llvm::SmallDenseSet<const ContextDescriptor *, 1>
_findContextDescriptorInCache(TypeMetadataPrivateState &T,
Demangle::NodePointer node) {
if (node->getNumChildren() < 2)
return { };

auto nameNode = node->getChild(1);

// Declarations synthesized by the Clang importer get a small tag
// string in addition to their name.
if (nameNode->getKind() == Demangle::Node::Kind::RelatedEntityDeclName)
nameNode = nameNode->getChild(1);

if (nameNode->getKind() != Demangle::Node::Kind::Identifier)
return { };

auto name = nameNode->getText();

auto iter = T.ContextDescriptorCache.find(name);
if (iter == T.ContextDescriptorCache.end())
return { };

return iter->getSecond();
}

static const ContextDescriptor *
_findContextDescriptor(Demangle::NodePointer node,
Demangle::Demangler &Dem) {
Expand All @@ -613,9 +712,14 @@ _findContextDescriptor(Demangle::NodePointer node,
NodePointer symbolicNode = node;
if (symbolicNode->getKind() == Node::Kind::Type)
symbolicNode = symbolicNode->getChild(0);
if (symbolicNode->getKind() == Node::Kind::TypeSymbolicReference)
if (symbolicNode->getKind() == Node::Kind::TypeSymbolicReference) {
return cast<TypeContextDescriptor>(
(const ContextDescriptor *)symbolicNode->getIndex());
}

// Nothing to resolve if have a generic parameter.
if (symbolicNode->getKind() == Node::Kind::DependentGenericParamType)
return nullptr;

StringRef mangledName =
Demangle::mangleNode(node, ExpandResolvedSymbolicReferences(Dem), Dem);
Expand All @@ -625,8 +729,25 @@ _findContextDescriptor(Demangle::NodePointer node,
if (auto Value = T.NominalCache.find(mangledName))
return Value->getDescription();

// Scan any newly loaded images for context descriptors, then try the context
// descriptor cache. This must be done with the cache's lock held.
llvm::SmallDenseSet<const ContextDescriptor *, 1> cachedContexts;
{
ScopedLock guard(T.ContextDescriptorCacheLock);
_scanAdditionalContextDescriptors(T);
cachedContexts = _findContextDescriptorInCache(T, node);
}

for (auto cachedContext : cachedContexts) {
if (_contextDescriptorMatchesMangling(cachedContext, node)) {
foundContext = cachedContext;
break;
}
}

// Check type metadata records
foundContext = _searchTypeMetadataRecords(T, node);
if (!foundContext)
foundContext = _searchTypeMetadataRecords(T, node);

// Check protocol conformances table. Note that this has no support for
// resolving generic types yet.
Expand Down
13 changes: 13 additions & 0 deletions stdlib/public/runtime/Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,19 @@ class TypeInfo {
const ContextDescriptor *
_searchConformancesByMangledTypeName(Demangle::NodePointer node);

/// Iterate over protocol conformance sections starting from the given index.
/// The index is updated to the current number of protocol sections. Passing
/// the same index to the next call will iterate over any sections that were
/// added after the previous call.
///
/// Takes a function to call for each section found. The two parameters are
/// the start and end of the section.
void
_forEachProtocolConformanceSectionAfter(
size_t *start,
const std::function<void(const ProtocolConformanceRecord *,
const ProtocolConformanceRecord *)> &f);

Demangle::NodePointer _swift_buildDemanglingForMetadata(const Metadata *type,
Demangle::Demangler &Dem);

Expand Down
16 changes: 16 additions & 0 deletions stdlib/public/runtime/ProtocolConformance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,22 @@ swift::_searchConformancesByMangledTypeName(Demangle::NodePointer node) {
return nullptr;
}

void
swift::_forEachProtocolConformanceSectionAfter(
size_t *start,
const std::function<void(const ProtocolConformanceRecord *,
const ProtocolConformanceRecord *)> &f) {
auto snapshot = Conformances.get().SectionsToScan.snapshot();
if (snapshot.Count > *start) {
auto *begin = snapshot.begin() + *start;
auto *end = snapshot.end();
for (auto *section = begin; section != end; section++) {
f(section->Begin, section->End);
}
*start = snapshot.Count;
}
}

bool swift::_checkGenericRequirements(
llvm::ArrayRef<GenericRequirementDescriptor> requirements,
SmallVectorImpl<const void *> &extraArguments,
Expand Down