SILWitnessTable * SILModule::lookUpWitnessTable(const ProtocolConformance *C, bool deserializeLazily) { assert(C && "null conformance passed to lookUpWitnessTable"); const NormalProtocolConformance *NormalC = C->getRootNormalConformance(); // Attempt to lookup the witness table from the table. auto found = WitnessTableMap.find(NormalC); if (found == WitnessTableMap.end()) { #ifndef NDEBUG // Make sure that all witness tables are in the witness table lookup // cache. // // This code should not be hit normally since we add witness tables to the // lookup cache when we create them. We don't just assert here since there // is the potential for a conformance without a witness table to be passed // to this function. for (SILWitnessTable &WT : witnessTables) assert(WT.getConformance() != NormalC && "Found witness table that is not" " in the witness table lookup cache."); #endif return nullptr; } SILWitnessTable *wtable = found->second; assert(wtable != nullptr && "Should never map a conformance to a null witness" " table."); // If we have a definition, return it. if (wtable->isDefinition()) return wtable; // If the module is at or past the Lowered stage, then we can't do any // further deserialization, since pre-IRGen SIL lowering changes the types // of definitions to make them incompatible with canonical serialized SIL. switch (getStage()) { case SILStage::Canonical: case SILStage::Raw: break; case SILStage::Lowered: return wtable; } // Otherwise try to deserialize it. If we succeed return the deserialized // function. // // *NOTE* In practice, wtable will be deserializedTable, but I do not want to rely // on that behavior for now. if (deserializeLazily) if (auto deserialized = getSILLoader()->lookupWitnessTable(wtable)) return deserialized; // If we fail, just return the declaration. return wtable; }
SILWitnessTable * SILModule::lookUpWitnessTable(const ProtocolConformance *C, bool deserializeLazily) { assert(C && "null conformance passed to lookUpWitnessTable"); const NormalProtocolConformance *NormalC = C->getRootNormalConformance(); // Attempt to lookup the witness table from the table. auto found = WitnessTableMap.find(NormalC); if (found == WitnessTableMap.end()) { #ifndef NDEBUG // Make sure that all witness tables are in the witness table lookup // cache. // // This code should not be hit normally since we add witness tables to the // lookup cache when we create them. We don't just assert here since there // is the potential for a conformance without a witness table to be passed // to this function. for (SILWitnessTable &WT : witnessTables) assert(WT.getConformance() != NormalC && "Found witness table that is not" " in the witness table lookup cache."); #endif return nullptr; } SILWitnessTable *wT = found->second; assert(wT != nullptr && "Should never map a conformance to a null witness" " table."); // If we have a definition, return it. if (wT->isDefinition()) return wT; // Otherwise try to deserialize it. If we succeed return the deserialized // function. // // *NOTE* In practice, wT will be deserializedTable, but I do not want to rely // on that behavior for now. if (deserializeLazily) if (auto deserializedTable = getSILLoader()->lookupWitnessTable(wT)) return deserializedTable; // If we fail, just return the declaration. return wT; }
std::pair<SILWitnessTable *, ArrayRef<Substitution>> SILModule:: lookUpWitnessTable(const ProtocolConformance *C, bool deserializeLazily) { // If we have a null conformance passed in (a legal value), just return // nullptr. ArrayRef<Substitution> Subs; if (!C) return {nullptr, Subs}; // Walk down to the base NormalProtocolConformance. const ProtocolConformance *ParentC = C; while (!isa<NormalProtocolConformance>(ParentC)) { switch (ParentC->getKind()) { case ProtocolConformanceKind::Normal: llvm_unreachable("should have exited the loop?!"); case ProtocolConformanceKind::Inherited: ParentC = cast<InheritedProtocolConformance>(ParentC) ->getInheritedConformance(); break; case ProtocolConformanceKind::Specialized: { auto SC = cast<SpecializedProtocolConformance>(ParentC); ParentC = SC->getGenericConformance(); assert(Subs.empty() && "multiple conformance specializations?!"); Subs = SC->getGenericSubstitutions(); break; } } } const NormalProtocolConformance *NormalC = cast<NormalProtocolConformance>(ParentC); // If the normal conformance is for a generic type, and we didn't hit a // specialized conformance, collect the substitutions from the generic type. // FIXME: The AST should do this for us. if (NormalC->getType()->isSpecialized() && Subs.empty()) { Subs = NormalC->getType() ->gatherAllSubstitutions(NormalC->getDeclContext()->getParentModule(), nullptr); } // Attempt to lookup the witness table from the table. auto found = WitnessTableLookupCache.find(NormalC); if (found == WitnessTableLookupCache.end()) { #ifndef NDEBUG // Make sure that all witness tables are in the witness table lookup // cache. // // This code should not be hit normally since we add witness tables to the // lookup cache when we create them. We don't just assert here since there // is the potential for a conformance without a witness table to be passed // to this function. for (SILWitnessTable &WT : witnessTables) assert(WT.getConformance() != NormalC && "Found witness table that is not" " in the witness table lookup cache."); #endif return {nullptr, Subs}; } SILWitnessTable *wT = found->second; assert(wT != nullptr && "Should never map a conformance to a null witness" " table."); // If we have a definition, return it. if (wT->isDefinition()) return {wT, Subs}; // Otherwise try to deserialize it. If we succeed return the deserialized // function. // // *NOTE* In practice, wT will be deserializedTable, but I do not want to rely // on that behavior for now. if (deserializeLazily) if (auto deserializedTable = getSILLoader()->lookupWitnessTable(wT)) return {deserializedTable, Subs}; // If we fail, just return the declaration. return {wT, Subs}; }
void SILLinkerVisitor::visitProtocolConformance( ProtocolConformanceRef ref, const Optional<SILDeclRef> &Member) { // If an abstract protocol conformance was passed in, just return false. if (ref.isAbstract()) return; bool mustDeserialize = mustDeserializeProtocolConformance(Mod, ref); // Otherwise try and lookup a witness table for C. auto C = ref.getConcrete(); if (!VisitedConformances.insert(C).second) return; SILWitnessTable *WT = Mod.lookUpWitnessTable(C, true); // If we don't find any witness table for the conformance, bail and return // false. if (!WT) { Mod.createWitnessTableDeclaration( C, getLinkageForProtocolConformance( C->getRootNormalConformance(), NotForDefinition)); // Adding the declaration may allow us to now deserialize the body. // Force the body if we must deserialize this witness table. if (mustDeserialize) { WT = Mod.lookUpWitnessTable(C, true); assert(WT && WT->isDefinition() && "unable to deserialize witness table when we must?!"); } else { return; } } // If the looked up witness table is a declaration, there is nothing we can // do here. Just bail and return false. if (WT->isDeclaration()) return; auto maybeVisitRelatedConformance = [&](ProtocolConformanceRef c) { // Formally all conformances referenced by a used conformance are used. // However, eagerly visiting them all at this point leads to a large blowup // in the amount of SIL we read in. For optimization purposes we can defer // reading in most conformances until we need them for devirtualization. // However, we *must* pull in shared clang-importer-derived conformances // we potentially use, since we may not otherwise have a local definition. if (mustDeserializeProtocolConformance(Mod, c)) visitProtocolConformance(c, None); }; // For each entry in the witness table... for (auto &E : WT->getEntries()) { switch (E.getKind()) { // If the entry is a witness method... case SILWitnessTable::WitnessKind::Method: { // And we are only interested in deserializing a specific requirement // and don't have that requirement, don't deserialize this method. if (Member.hasValue() && E.getMethodWitness().Requirement != *Member) continue; // The witness could be removed by dead function elimination. if (!E.getMethodWitness().Witness) continue; // Otherwise, deserialize the witness if it has shared linkage, or if // we were asked to deserialize everything. maybeAddFunctionToWorklist(E.getMethodWitness().Witness); break; } // If the entry is a related witness table, see whether we need to // eagerly deserialize it. case SILWitnessTable::WitnessKind::BaseProtocol: { auto baseConformance = E.getBaseProtocolWitness().Witness; maybeVisitRelatedConformance(ProtocolConformanceRef(baseConformance)); break; } case SILWitnessTable::WitnessKind::AssociatedTypeProtocol: { auto assocConformance = E.getAssociatedTypeProtocolWitness().Witness; maybeVisitRelatedConformance(assocConformance); break; } case SILWitnessTable::WitnessKind::AssociatedType: case SILWitnessTable::WitnessKind::Invalid: break; } } }