void LocalTypeDataCache:: addAbstractForFulfillments(IRGenFunction &IGF, FulfillmentMap &&fulfillments, llvm::function_ref<AbstractSource()> createSource) { // Add the source lazily. Optional<unsigned> sourceIndex; auto getSourceIndex = [&]() -> unsigned { if (!sourceIndex) { AbstractSources.emplace_back(createSource()); sourceIndex = AbstractSources.size() - 1; } return *sourceIndex; }; for (auto &fulfillment : fulfillments) { CanType type = CanType(fulfillment.first.first); LocalTypeDataKind localDataKind; // For now, ignore witness-table fulfillments when they're not for // archetypes. if (ProtocolDecl *protocol = fulfillment.first.second) { if (auto archetype = dyn_cast<ArchetypeType>(type)) { auto conformsTo = archetype->getConformsTo(); auto it = std::find(conformsTo.begin(), conformsTo.end(), protocol); if (it == conformsTo.end()) continue; localDataKind = LocalTypeDataKind::forAbstractProtocolWitnessTable(*it); } else { continue; } } else { // Ignore type metadata fulfillments for non-dependent types that // we can produce very cheaply. We don't want to end up emitting // the type metadata for Int by chasing through N layers of metadata // just because that path happens to be in the cache. if (!type->hasArchetype() && isTypeMetadataAccessTrivial(IGF.IGM, type)) { continue; } localDataKind = LocalTypeDataKind::forTypeMetadata(); } // Find the chain for the key. auto key = getKey(type, localDataKind); auto &chain = Map[key]; // Check whether there's already an entry that's at least as good as the // fulfillment. Optional<unsigned> fulfillmentCost; auto getFulfillmentCost = [&]() -> unsigned { if (!fulfillmentCost) fulfillmentCost = fulfillment.second.Path.cost(); return *fulfillmentCost; }; bool isConditional = IGF.isConditionalDominancePoint(); bool foundBetter = false; for (CacheEntry *cur = chain.Root, *last = nullptr; cur; last = cur, cur = cur->getNext()) { // Ensure the entry is acceptable. if (!IGF.isActiveDominancePointDominatedBy(cur->DefinitionPoint)) continue; // Ensure that the entry isn't better than the fulfillment. auto curCost = cur->cost(); if (curCost == 0 || curCost <= getFulfillmentCost()) { foundBetter = true; break; } // If the entry is defined at the current point, (1) we know there // won't be a better entry and (2) we should remove it. if (cur->DefinitionPoint == IGF.getActiveDominancePoint() && !isConditional) { // Splice it out of the chain. assert(!cur->isConditional()); chain.eraseEntry(last, cur); break; } } if (foundBetter) continue; // Okay, make a new entry. // Register with the conditional dominance scope if necessary. if (isConditional) { IGF.registerConditionalLocalTypeDataKey(key); } // Allocate the new entry. auto newEntry = new AbstractCacheEntry(IGF.getActiveDominancePoint(), isConditional, getSourceIndex(), std::move(fulfillment.second.Path)); // Add it to the front of the chain. chain.push_front(newEntry); } }
MetadataResponse LocalTypeDataCache::tryGet(IRGenFunction &IGF, LocalTypeDataKey key, bool allowAbstract, DynamicMetadataRequest request) { // Use the caching key. key = key.getCachingKey(); auto it = Map.find(key); if (it == Map.end()) return MetadataResponse(); auto &chain = it->second; CacheEntry *best = nullptr; Optional<OperationCost> bestCost; CacheEntry *next = chain.Root; while (next) { CacheEntry *cur = next; next = cur->getNext(); // Ignore abstract entries if so requested. if (!allowAbstract && !isa<ConcreteCacheEntry>(cur)) continue; // Ignore unacceptable entries. if (!IGF.isActiveDominancePointDominatedBy(cur->DefinitionPoint)) continue; // If there's a collision, compare by cost, ignoring higher-cost entries. if (best) { // Compute the cost of the best entry if we haven't done so already. // If that's zero, go ahead and short-circuit out. if (!bestCost) { bestCost = best->costForRequest(key, request); if (*bestCost == OperationCost::Free) break; } auto curCost = cur->costForRequest(key, request); if (curCost >= *bestCost) continue; // Replace the best cost and fall through. bestCost = curCost; } best = cur; } // If we didn't find anything, we're done. if (!best) return MetadataResponse(); // Okay, we've found the best entry available. switch (best->getKind()) { // For concrete caches, this is easy. case CacheEntry::Kind::Concrete: { auto entry = cast<ConcreteCacheEntry>(best); if (entry->immediatelySatisfies(key, request)) return entry->Value; assert(key.Kind.isAnyTypeMetadata()); // Emit a dynamic check that the type metadata matches the request. // TODO: we could potentially end up calling this redundantly with a // dynamic request. Fortunately, those are used only in very narrow // circumstances. auto response = emitCheckTypeMetadataState(IGF, request, entry->Value); // Add a concrete entry for the checked result. IGF.setScopedLocalTypeData(key, response); return response; } // For abstract caches, we need to follow a path. case CacheEntry::Kind::Abstract: { auto entry = cast<AbstractCacheEntry>(best); // Follow the path. auto &source = AbstractSources[entry->SourceIndex]; auto response = entry->follow(IGF, source, request); // Following the path automatically caches at every point along it, // including the end. assert(chain.Root->DefinitionPoint == IGF.getActiveDominancePoint()); assert(isa<ConcreteCacheEntry>(chain.Root)); return response; } } llvm_unreachable("bad cache entry kind"); }