const Metadata * swift::_searchConformancesByMangledTypeName(const llvm::StringRef typeName) { auto &C = Conformances.get(); const Metadata *foundMetadata = nullptr; pthread_mutex_lock(&C.SectionsToScanLock); unsigned sectionIdx = 0; unsigned endSectionIdx = C.SectionsToScan.size(); for (; sectionIdx < endSectionIdx; ++sectionIdx) { auto §ion = C.SectionsToScan[sectionIdx]; for (const auto &record : section) { if (auto metadata = record.getCanonicalTypeMetadata()) foundMetadata = _matchMetadataByMangledTypeName(typeName, metadata, nullptr); else if (auto pattern = record.getGenericPattern()) foundMetadata = _matchMetadataByMangledTypeName(typeName, nullptr, pattern); if (foundMetadata != nullptr) break; } if (foundMetadata != nullptr) break; } pthread_mutex_unlock(&C.SectionsToScanLock); return foundMetadata; }
const Metadata * swift::_searchConformancesByMangledTypeName(const llvm::StringRef typeName) { auto &C = Conformances.get(); const Metadata *foundMetadata = nullptr; ScopedLock guard(C.SectionsToScanLock); unsigned sectionIdx = 0; unsigned endSectionIdx = C.SectionsToScan.size(); for (; sectionIdx < endSectionIdx; ++sectionIdx) { auto §ion = C.SectionsToScan[sectionIdx]; for (const auto &record : section) { if (auto metadata = record.getCanonicalTypeMetadata()) foundMetadata = _matchMetadataByMangledTypeName(typeName, metadata, nullptr); else if (auto ntd = record.getNominalTypeDescriptor()) foundMetadata = _matchMetadataByMangledTypeName(typeName, nullptr, ntd); if (foundMetadata != nullptr) break; } if (foundMetadata != nullptr) break; } return foundMetadata; }
/// Search the witness table in the ConformanceCache. \returns a pair of the /// WitnessTable pointer and a boolean value True if a definitive value is /// found. \returns false if the type or its superclasses were not found in /// the cache. static std::pair<const WitnessTable *, bool> searchInConformanceCache(const Metadata *type, const ProtocolDescriptor *protocol, ConformanceCacheEntry *&foundEntry) { auto &C = Conformances.get(); auto origType = type; foundEntry = nullptr; recur_inside_cache_lock: // See if we have a cached conformance. Try the specific type first. { // Check if the type-protocol entry exists in the cache entry that we found. if (auto *Value = C.findCached(type, protocol)) { if (Value->isSuccessful()) return std::make_pair(Value->getWitnessTable(), true); // If we're still looking up for the original type, remember that // we found an exact match. if (type == origType) foundEntry = Value; // If we got a cached negative response, check the generation number. if (Value->getFailureGeneration() == C.SectionsToScan.size()) { // We found an entry with a negative value. return std::make_pair(nullptr, true); } } } { // For generic and resilient types, nondependent conformances // are keyed by the nominal type descriptor rather than the // metadata, so try that. auto *description = type->getNominalTypeDescriptor().get(); // Hash and lookup the type-protocol pair in the cache. if (auto *Value = C.findCached(description, protocol)) { if (Value->isSuccessful()) return std::make_pair(Value->getWitnessTable(), true); // We don't try to cache negative responses for generic // patterns. } } // If the type is a class, try its superclass. if (const ClassMetadata *classType = type->getClassObject()) { if (classHasSuperclass(classType)) { type = swift_getObjCClassMetadata(classType->SuperClass); goto recur_inside_cache_lock; } } // We did not find an entry. return std::make_pair(nullptr, false); }
static const TypeContextDescriptor * _findNominalTypeDescriptor(Demangle::NodePointer node) { const TypeContextDescriptor *foundNominal = nullptr; auto &T = TypeMetadataRecords.get(); auto mangledName = Demangle::mangleNode(node); // Look for an existing entry. // Find the bucket for the metadata entry. if (auto Value = T.NominalCache.find(mangledName)) return Value->getDescription(); // Check type metadata records T.SectionsToScanLock.withLock([&] { foundNominal = _searchTypeMetadataRecords(T, node); }); // Check protocol conformances table. Note that this has no support for // resolving generic types yet. if (!foundNominal) foundNominal = _searchConformancesByMangledTypeName(node); if (foundNominal) { T.NominalCache.getOrInsert(mangledName, foundNominal); } return foundNominal; }
static const TypeContextDescriptor * _findNominalTypeDescriptor(Demangle::NodePointer node, Demangle::Demangler &Dem) { const TypeContextDescriptor *foundNominal = nullptr; auto &T = TypeMetadataRecords.get(); // If we have a symbolic reference to a context, resolve it immediately. NodePointer symbolicNode = node; if (symbolicNode->getKind() == Node::Kind::Type) symbolicNode = symbolicNode->getChild(0); if (symbolicNode->getKind() == Node::Kind::SymbolicReference) return cast<TypeContextDescriptor>( (const ContextDescriptor *)symbolicNode->getIndex()); auto mangledName = Demangle::mangleNode(node, [&](const void *context) -> NodePointer { return _buildDemanglingForContext( (const ContextDescriptor *) context, {}, false, Dem); }); // Look for an existing entry. // Find the bucket for the metadata entry. if (auto Value = T.NominalCache.find(mangledName)) return Value->getDescription(); // Check type metadata records foundNominal = _searchTypeMetadataRecords(T, node); // Check protocol conformances table. Note that this has no support for // resolving generic types yet. if (!foundNominal) foundNominal = _searchConformancesByMangledTypeName(node); if (foundNominal) { T.NominalCache.getOrInsert(mangledName, foundNominal); } return foundNominal; }
static const Metadata * _typeByMangledName(const llvm::StringRef typeName) { const Metadata *foundMetadata = nullptr; auto &T = TypeMetadataRecords.get(); // Look for an existing entry. // Find the bucket for the metadata entry. if (auto Value = T.Cache.find(typeName)) return Value->getMetadata(); // Check type metadata records pthread_mutex_lock(&T.SectionsToScanLock); foundMetadata = _searchTypeMetadataRecords(T, typeName); pthread_mutex_unlock(&T.SectionsToScanLock); // Check protocol conformances table. Note that this has no support for // resolving generic types yet. if (!foundMetadata) foundMetadata = _searchConformancesByMangledTypeName(typeName); if (foundMetadata) { T.Cache.getOrInsert(typeName, foundMetadata); } #if SWIFT_OBJC_INTEROP // Check for ObjC class // FIXME does this have any value? any ObjC class with a Swift name // should already be registered as a Swift type. if (foundMetadata == nullptr) { std::string prefixedName("_Tt" + typeName.str()); foundMetadata = reinterpret_cast<ClassMetadata *> (objc_lookUpClass(prefixedName.c_str())); } #endif return foundMetadata; }
static BoxPair::Return _swift_allocBox_(const Metadata *type) { // Get the heap metadata for the box. auto &B = Boxes.get(); const void *typeArg = type; auto entry = B.findOrAdd(&typeArg, 1, [&]() -> BoxCacheEntry* { // Create a new entry for the box. auto entry = BoxCacheEntry::allocate(B.getAllocator(), &typeArg, 1, 0); auto metadata = entry->getData(); metadata->Offset = GenericBoxHeapMetadata::getHeaderOffset(type); metadata->BoxedType = type; return entry; }); auto metadata = entry->getData(); // Allocate and project the box. auto allocation = swift_allocObject(metadata, metadata->getAllocSize(), metadata->getAllocAlignMask()); auto projection = metadata->project(allocation); return BoxPair{allocation, projection}; }
void swift::swift_registerTypeMetadataRecords(const TypeMetadataRecord *begin, const TypeMetadataRecord *end) { auto &T = TypeMetadataRecords.get(); _registerTypeMetadataRecords(T, begin, end); }
const WitnessTable * swift::swift_conformsToProtocol(const Metadata *type, const ProtocolDescriptor *protocol) { auto &C = Conformances.get(); auto origType = type; unsigned numSections = 0; ConformanceCacheEntry *foundEntry; recur: // See if we have a cached conformance. The ConcurrentMap data structure // allows us to insert and search the map concurrently without locking. // We do lock the slow path because the SectionsToScan data structure is not // concurrent. auto FoundConformance = searchInConformanceCache(type, protocol, foundEntry); // The negative answer does not always mean that there is no conformance, // unless it is an exact match on the type. If it is not an exact match, // it may mean that all of the superclasses do not have this conformance, // but the actual type may still have this conformance. if (FoundConformance.second) { if (FoundConformance.first || foundEntry) return FoundConformance.first; } // If we didn't have an up-to-date cache entry, scan the conformance records. C.SectionsToScanLock.lock(); unsigned failedGeneration = ConformanceCacheGeneration; // If we have no new information to pull in (and nobody else pulled in // new information while we waited on the lock), we're done. if (C.SectionsToScan.size() == numSections) { if (failedGeneration != ConformanceCacheGeneration) { // Someone else pulled in new conformances while we were waiting. // Start over with our newly-populated cache. C.SectionsToScanLock.unlock(); type = origType; goto recur; } // Save the failure for this type-protocol pair in the cache. C.cacheFailure(type, protocol); C.SectionsToScanLock.unlock(); return nullptr; } // Update the last known number of sections to scan. numSections = C.SectionsToScan.size(); // Scan only sections that were not scanned yet. unsigned sectionIdx = foundEntry ? foundEntry->getFailureGeneration() : 0; unsigned endSectionIdx = C.SectionsToScan.size(); for (; sectionIdx < endSectionIdx; ++sectionIdx) { auto §ion = C.SectionsToScan[sectionIdx]; // Eagerly pull records for nondependent witnesses into our cache. for (const auto &record : section) { // If the record applies to a specific type, cache it. if (auto metadata = record.getCanonicalTypeMetadata()) { auto P = record.getProtocol(); // Look for an exact match. if (protocol != P) continue; if (!isRelatedType(type, metadata, /*isMetadata=*/true)) continue; // Store the type-protocol pair in the cache. auto witness = record.getWitnessTable(metadata); if (witness) { C.cacheSuccess(metadata, P, witness); } else { C.cacheFailure(metadata, P); } // If the record provides a nondependent witness table for all instances // of a generic type, cache it for the generic pattern. // TODO: "Nondependent witness table" probably deserves its own flag. // An accessor function might still be necessary even if the witness table // can be shared. } else if (record.getTypeKind() == TypeMetadataRecordKind::UniqueNominalTypeDescriptor && record.getConformanceKind() == ProtocolConformanceReferenceKind::WitnessTable) { auto R = record.getNominalTypeDescriptor(); auto P = record.getProtocol(); // Look for an exact match. if (protocol != P) continue; if (!isRelatedType(type, R, /*isMetadata=*/false)) continue; // Store the type-protocol pair in the cache. C.cacheSuccess(R, P, record.getStaticWitnessTable()); } } } ++ConformanceCacheGeneration; C.SectionsToScanLock.unlock(); // Start over with our newly-populated cache. type = origType; goto recur; }
void swift::swift_registerProtocolConformances(const ProtocolConformanceRecord *begin, const ProtocolConformanceRecord *end){ auto &C = Conformances.get(); _registerProtocolConformances(C, begin, end); }
Laziness() : total([&]{ return first.get()+ second.get(); }, total.depends(first).depends(second)) {}
/// Search the witness table in the ConformanceCache. \returns a pair of the /// WitnessTable pointer and a boolean value True if a definitive value is /// found. \returns false if the type or its superclasses were not found in /// the cache. static std::pair<const WitnessTable *, bool> searchInConformanceCache(const Metadata *type, const ProtocolDescriptor *protocol, ConformanceCacheEntry *&foundEntry) { auto &C = Conformances.get(); auto origType = type; foundEntry = nullptr; recur_inside_cache_lock: // See if we have a cached conformance. Try the specific type first. // Hash and lookup the type-protocol pair in the cache. size_t hash = hashTypeProtocolPair(type, protocol); ConcurrentList<ConformanceCacheEntry> &Bucket = C.Cache.findOrAllocateNode(hash); // Check if the type-protocol entry exists in the cache entry that we found. for (auto &Entry : Bucket) { if (!Entry.matches(type, protocol)) continue; if (Entry.isSuccessful()) { return std::make_pair(Entry.getWitnessTable(), true); } if (type == origType) foundEntry = &Entry; // If we got a cached negative response, check the generation number. if (Entry.getFailureGeneration() == C.SectionsToScan.size()) { // We found an entry with a negative value. return std::make_pair(nullptr, true); } } // If the type is generic, see if there's a shared nondependent witness table // for its instances. if (auto generic = type->getGenericPattern()) { // Hash and lookup the type-protocol pair in the cache. size_t hash = hashTypeProtocolPair(generic, protocol); ConcurrentList<ConformanceCacheEntry> &Bucket = C.Cache.findOrAllocateNode(hash); for (auto &Entry : Bucket) { if (!Entry.matches(generic, protocol)) continue; if (Entry.isSuccessful()) { return std::make_pair(Entry.getWitnessTable(), true); } // We don't try to cache negative responses for generic // patterns. } } // If the type is a class, try its superclass. if (const ClassMetadata *classType = type->getClassObject()) { if (classHasSuperclass(classType)) { type = swift_getObjCClassMetadata(classType->SuperClass); goto recur_inside_cache_lock; } } // We did not find an entry. return std::make_pair(nullptr, false); }
const WitnessTable * swift::swift_conformsToProtocol(const Metadata * const type, const ProtocolDescriptor *protocol) { auto &C = Conformances.get(); // See if we have a cached conformance. The ConcurrentMap data structure // allows us to insert and search the map concurrently without locking. // We do lock the slow path because the SectionsToScan data structure is not // concurrent. auto FoundConformance = searchInConformanceCache(type, protocol); // If the result (positive or negative) is authoritative, return it. if (FoundConformance.isAuthoritative) return FoundConformance.witnessTable; auto failureEntry = FoundConformance.failureEntry; // No up-to-date cache entry found. // Acquire the lock so we can scan conformance records. ScopedLock guard(C.SectionsToScanLock); // The world may have changed while we waited for the lock. // If we found an out-of-date negative cache entry before // acquiring the lock, make sure the entry is still negative and out of date. // If we found no entry before acquiring the lock, search the cache again. if (failureEntry) { if (failureEntry->isSuccessful()) { // Somebody else found a conformance. return failureEntry->getWitnessTable(); } if (failureEntry->getFailureGeneration() == C.SectionsToScan.size()) { // Somebody else brought the negative cache entry up to date. return nullptr; } } else { FoundConformance = searchInConformanceCache(type, protocol); if (FoundConformance.isAuthoritative) { // Somebody else found a conformance or cached an up-to-date failure. return FoundConformance.witnessTable; } failureEntry = FoundConformance.failureEntry; } // We are now caught up after acquiring the lock. // Prepare to scan conformance records. // Scan only sections that were not scanned yet. // If we found an out-of-date negative cache entry, // we need not to re-scan the sections that it covers. unsigned startSectionIdx = failureEntry ? failureEntry->getFailureGeneration() : 0; unsigned endSectionIdx = C.SectionsToScan.size(); // If there are no unscanned sections outstanding // then we can cache failure and give up now. if (startSectionIdx == endSectionIdx) { C.cacheFailure(type, protocol); return nullptr; } // Really scan conformance records. for (unsigned sectionIdx = startSectionIdx; sectionIdx < endSectionIdx; ++sectionIdx) { auto §ion = C.SectionsToScan[sectionIdx]; // Eagerly pull records for nondependent witnesses into our cache. for (const auto &record : section) { // If the record applies to a specific type, cache it. if (auto metadata = record.getCanonicalTypeMetadata()) { auto P = record.getProtocol(); // Look for an exact match. if (protocol != P) continue; if (!isRelatedType(type, metadata, /*candidateIsMetadata=*/true)) continue; // Store the type-protocol pair in the cache. auto witness = record.getWitnessTable(metadata); if (witness) { C.cacheSuccess(metadata, P, witness); } else { C.cacheFailure(metadata, P); } // TODO: "Nondependent witness table" probably deserves its own flag. // An accessor function might still be necessary even if the witness table // can be shared. } else if (record.getTypeKind() == TypeMetadataRecordKind::UniqueNominalTypeDescriptor) { auto R = record.getNominalTypeDescriptor(); auto P = record.getProtocol(); // Look for an exact match. if (protocol != P) continue; if (!isRelatedType(type, R, /*candidateIsMetadata=*/false)) continue; // Store the type-protocol pair in the cache. switch (record.getConformanceKind()) { case ProtocolConformanceReferenceKind::WitnessTable: // If the record provides a nondependent witness table for all // instances of a generic type, cache it for the generic pattern. C.cacheSuccess(R, P, record.getStaticWitnessTable()); break; case ProtocolConformanceReferenceKind::WitnessTableAccessor: // If the record provides a dependent witness table accessor, // cache the result for the instantiated type metadata. C.cacheSuccess(type, P, record.getWitnessTable(type)); break; } } } } // Conformance scan is complete. // Search the cache once more, and this time update the cache if necessary. FoundConformance = searchInConformanceCache(type, protocol); if (FoundConformance.isAuthoritative) { return FoundConformance.witnessTable; } else { C.cacheFailure(type, protocol); return nullptr; } }
/// Search for a witness table in the ConformanceCache. static ConformanceCacheResult searchInConformanceCache(const Metadata *type, const ProtocolDescriptor *protocol) { auto &C = Conformances.get(); auto origType = type; ConformanceCacheEntry *failureEntry = nullptr; recur: { // Try the specific type first. if (auto *Value = C.findCached(type, protocol)) { if (Value->isSuccessful()) { // Found a conformance on the type or some superclass. Return it. return ConformanceCacheResult::cachedSuccess(Value->getWitnessTable()); } // Found a negative cache entry. bool isAuthoritative; if (type == origType) { // This negative cache entry is for the original query type. // Remember it so it can be returned later. failureEntry = Value; // An up-to-date entry for the original type is authoritative. isAuthoritative = true; } else { // An up-to-date cached failure for a superclass of the type is not // authoritative: there may be a still-undiscovered conformance // for the original query type. isAuthoritative = false; } // Check if the negative cache entry is up-to-date. // FIXME: Using SectionsToScan.size() outside SectionsToScanLock // is undefined. if (Value->getFailureGeneration() == C.SectionsToScan.size()) { // Negative cache entry is up-to-date. Return failure along with // the original query type's own cache entry, if we found one. // (That entry may be out of date but the caller still has use for it.) return ConformanceCacheResult::cachedFailure(failureEntry, isAuthoritative); } // Negative cache entry is out-of-date. // Continue searching for a better result. } } { // For generic and resilient types, nondependent conformances // are keyed by the nominal type descriptor rather than the // metadata, so try that. const auto description = type->getNominalTypeDescriptor().get(); // Hash and lookup the type-protocol pair in the cache. if (auto *Value = C.findCached(description, protocol)) { if (Value->isSuccessful()) return ConformanceCacheResult::cachedSuccess(Value->getWitnessTable()); // We don't try to cache negative responses for generic // patterns. } } // If the type is a class, try its superclass. if (const ClassMetadata *classType = type->getClassObject()) { if (classHasSuperclass(classType)) { type = swift_getObjCClassMetadata(classType->SuperClass); goto recur; } } // We did not find an up-to-date cache entry. // If we found an out-of-date entry for the original query type then // return it (non-authoritatively). Otherwise return a cache miss. if (failureEntry) return ConformanceCacheResult::cachedFailure(failureEntry, false); else return ConformanceCacheResult::cacheMiss(); }