//===--- SILModule.cpp - SILModule implementation -------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #include "swift/SIL/SILModule.h" #include "swift/SIL/SILExternalSource.h" #include "swift/SIL/SILVisitor.h" #include "swift/Serialization/SerializedSILLoader.h" #include "swift/SIL/SILValue.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/Debug.h" #define DEBUG_TYPE "libsil" using namespace swift; STATISTIC(NumFuncLinked, "Number of SIL functions linked"); namespace swift { /// SILTypeList - The uniqued backing store for the SILValue type list. This /// is only exposed out of SILValue as an ArrayRef of types, so it should /// never be used outside of libSIL. class SILTypeList : public llvm::FoldingSetNode { public: unsigned NumTypes; SILType Types[1]; // Actually variable sized. void Profile(llvm::FoldingSetNodeID &ID) const { for (unsigned i = 0, e = NumTypes; i != e; ++i) { ID.AddPointer(Types[i].getOpaqueValue()); } } }; } // end namespace swift. void SILExternalSource::anchor() { } /// SILTypeListUniquingType - This is the type of the folding set maintained by /// SILModule that these things are uniqued into. typedef llvm::FoldingSet SILTypeListUniquingType; class SILModule::SerializationCallback : public SerializedSILLoader::Callback { void didDeserialize(Module *M, SILFunction *fn) override { updateLinkage(fn); } void didDeserialize(Module *M, SILGlobalVariable *var) override { updateLinkage(var); } void didDeserialize(Module *M, SILVTable *vtable) override { // TODO: should vtables get linkage? //updateLinkage(vtable); } void didDeserialize(Module *M, SILWitnessTable *wt) override { updateLinkage(wt); } template void updateLinkage(T *decl) { switch (decl->getLinkage()) { case SILLinkage::Public: decl->setLinkage(SILLinkage::PublicExternal); return; case SILLinkage::Hidden: decl->setLinkage(SILLinkage::HiddenExternal); return; case SILLinkage::Shared: decl->setLinkage(SILLinkage::Shared); return; case SILLinkage::Private: // ? case SILLinkage::PublicExternal: case SILLinkage::HiddenExternal: return; } } }; SILModule::SILModule(Module *SwiftModule) : TheSwiftModule(SwiftModule), Stage(SILStage::Raw), Callback(new SILModule::SerializationCallback()), Types(*this) { TypeListUniquing = new SILTypeListUniquingType(); } SILModule::~SILModule() { // Drop everything functions in this module reference. // // This is necessary since the functions may reference each other. We don't // need to worry about sil_witness_tables since witness tables reference each // other via protocol conformances and sil_vtables don't reference each other // at all. for (SILFunction &F : *this) F.dropAllReferences(); delete (SILTypeListUniquingType*)TypeListUniquing; } SILWitnessTable * SILModule::createWitnessTableDeclaration(ProtocolConformance *C) { // If we are passed in a null conformance (a valid value), just return nullptr // since we can not map a witness table to it. if (!C) return nullptr; // Walk down to the base NormalProtocolConformance. ProtocolConformance *ParentC = C; ArrayRef Subs; while (!isa(ParentC)) { switch (ParentC->getKind()) { case ProtocolConformanceKind::Normal: llvm_unreachable("should have exited the loop?!"); case ProtocolConformanceKind::Inherited: ParentC = cast(ParentC) ->getInheritedConformance(); break; case ProtocolConformanceKind::Specialized: { auto SC = cast(ParentC); ParentC = SC->getGenericConformance(); assert(Subs.empty() && "multiple conformance specializations?!"); Subs = SC->getGenericSubstitutions(); break; } } } NormalProtocolConformance *NormalC = cast(ParentC); SILWitnessTable *WT = SILWitnessTable::create(*this, SILLinkage::PublicExternal, NormalC); return WT; } std::pair> SILModule:: lookUpWitnessTable(const ProtocolConformance *C, bool deserializeLazily) { // If we have a null conformance passed in (a legal value), just return // nullptr. ArrayRef Subs; if (!C) return {nullptr, Subs}; // Walk down to the base NormalProtocolConformance. const ProtocolConformance *ParentC = C; while (!isa(ParentC)) { switch (ParentC->getKind()) { case ProtocolConformanceKind::Normal: llvm_unreachable("should have exited the loop?!"); case ProtocolConformanceKind::Inherited: ParentC = cast(ParentC) ->getInheritedConformance(); break; case ProtocolConformanceKind::Specialized: { auto SC = cast(ParentC); ParentC = SC->getGenericConformance(); assert(Subs.empty() && "multiple conformance specializations?!"); Subs = SC->getGenericSubstitutions(); break; } } } const NormalProtocolConformance *NormalC = cast(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}; } SILFunction *SILModule::getOrCreateSharedFunction(SILLocation loc, StringRef name, CanSILFunctionType type, IsBare_t isBareSILFunction, IsTransparent_t isTransparent) { auto linkage = SILLinkage::Shared; if (auto fn = lookUpFunction(name)) { assert(fn->getLoweredFunctionType() == type); assert(fn->getLinkage() == linkage); return fn; } return SILFunction::create(*this, linkage, name, type, nullptr, loc, isBareSILFunction, isTransparent); } ArrayRef ValueBase::getTypes() const { // No results. if (TypeOrTypeList.isNull()) return ArrayRef(); // Arbitrary list of results. if (auto *TypeList = TypeOrTypeList.dyn_cast()) return ArrayRef(TypeList->Types, TypeList->NumTypes); // Single result. return TypeOrTypeList.get(); } /// getSILTypeList - Get a uniqued pointer to a SIL type list. This can only /// be used by SILValue. SILTypeList *SILModule::getSILTypeList(ArrayRef Types) const { assert(Types.size() > 1 && "Shouldn't use type list for 0 or 1 types"); auto UniqueMap = (SILTypeListUniquingType*)TypeListUniquing; llvm::FoldingSetNodeID ID; for (auto T : Types) { ID.AddPointer(T.getOpaqueValue()); } // If we already have this type list, just return it. void *InsertPoint = 0; if (SILTypeList *TypeList = UniqueMap->FindNodeOrInsertPos(ID, InsertPoint)) return TypeList; // Otherwise, allocate a new one. void *NewListP = BPA.Allocate(sizeof(SILTypeList)+ sizeof(SILType)*(Types.size()-1), alignof(SILTypeList)); SILTypeList *NewList = new (NewListP) SILTypeList(); NewList->NumTypes = Types.size(); std::copy(Types.begin(), Types.end(), NewList->Types); UniqueMap->InsertNode(NewList, InsertPoint); return NewList; } const IntrinsicInfo &SILModule::getIntrinsicInfo(Identifier ID) { unsigned OldSize = IntrinsicIDCache.size(); IntrinsicInfo &Info = IntrinsicIDCache[ID]; // If the element was is in the cache, return it. if (OldSize == IntrinsicIDCache.size()) return Info; // Otherwise, lookup the ID and Type and store them in the map. StringRef NameRef = getBuiltinBaseName(getASTContext(), ID.str(), Info.Types); Info.ID = (llvm::Intrinsic::ID)getLLVMIntrinsicID(NameRef, !Info.Types.empty()); return Info; } const BuiltinInfo &SILModule::getBuiltinInfo(Identifier ID) { unsigned OldSize = BuiltinIDCache.size(); BuiltinInfo &Info = BuiltinIDCache[ID]; // If the element was is in the cache, return it. if (OldSize == BuiltinIDCache.size()) return Info; // Otherwise, lookup the ID and Type and store them in the map. // Find the matching ID. StringRef OperationName = getBuiltinBaseName(getASTContext(), ID.str(), Info.Types); // Several operation names have suffixes and don't match the name from // Builtins.def, so handle those first. if (OperationName.startswith("fence_")) Info.ID = BuiltinValueKind::Fence; else if (OperationName.startswith("cmpxchg_")) Info.ID = BuiltinValueKind::CmpXChg; else if (OperationName.startswith("atomicrmw_")) Info.ID = BuiltinValueKind::AtomicRMW; else { // Switch through the rest of builtins. Info.ID = llvm::StringSwitch(OperationName) #define BUILTIN(ID, Name, Attrs) \ .Case(Name, BuiltinValueKind::ID) #include "swift/AST/Builtins.def" .Default(BuiltinValueKind::None); } return Info; } namespace { /// Visitor that knows how to link in dependencies of SILInstructions. class SILLinkerVisitor : public SILInstructionVisitor { using LinkingMode = SILModule::LinkingMode; /// The SILModule that we are loading from. SILModule &Mod; /// The SILLoader that this visitor is using to link. SerializedSILLoader *Loader; /// The external SIL source to use when linking this module. SILExternalSource *ExternalSource = nullptr; /// Worklist of SILFunctions we are processing. llvm::SmallVector Worklist; /// A list of callees of the current instruction being visited. cleared after /// every instruction is visited. llvm::SmallVector FunctionDeserializationWorklist; /// The current linking mode. LinkingMode Mode; public: SILLinkerVisitor(SILModule &M, SerializedSILLoader *L, SILModule::LinkingMode LinkingMode, SILExternalSource *E = nullptr) : Mod(M), Loader(L), ExternalSource(E), Worklist(), FunctionDeserializationWorklist(), Mode(LinkingMode) { } /// Process F, recursively deserializing any thing F may reference. bool processFunction(SILFunction *F) { if (Mode == LinkingMode::LinkNone) return false; // If F is a declaration, first deserialize it. auto NewFn = F->isExternalDeclaration() ? Loader->lookupSILFunction(F) : F; if (!NewFn || NewFn->empty()) return false; ++NumFuncLinked; // Try to transitively deserialize everything referenced by NewFn. Worklist.push_back(NewFn); process(); // Since we successfully processed at least one function, return true. return true; } /// Deserialize the VTable mapped to C if it exists and all SIL the VTable /// transitively references. /// /// This method assumes that the caller made sure that no vtable existed in /// Mod. SILVTable *processClassDecl(const ClassDecl *C) { // If we are not linking anything, bail. if (Mode == LinkingMode::LinkNone) return nullptr; // Attempt to load the VTable from the SerializedSILLoader. If we // fail... bail... SILVTable *Vtbl = Loader->lookupVTable(C); if (!Vtbl) return nullptr; // Otherwise, add all the vtable functions in Vtbl to the function // processing list... for (auto &E : Vtbl->getEntries()) Worklist.push_back(E.second); // And then transitively deserialize all SIL referenced by those functions. process(); // Return the deserialized Vtbl. return Vtbl; } /// We do not want to visit callee functions if we just have a value base. bool visitValueBase(ValueBase *V) { return false; } bool visitApplyInst(ApplyInst *AI) { // If we don't have a function ref inst, just return false. We do not have // interesting callees. auto *FRI = dyn_cast(AI->getCallee()); if (!FRI) return false; // Ok we have a function ref inst, grab the callee. SILFunction *Callee = FRI->getReferencedFunction(); // If the linking mode is not link all, AI is not transparent, and the // callee is not shared, we don't want to perform any linking. if (!isLinkAll() && !AI->isTransparent() && Callee->getLinkage() != SILLinkage::Shared) return false; // Otherwise we want to try and link in the callee... Add it to the callee // list and return true. addFunctionToWorklist(Callee); return true; } bool visitPartialApplyInst(PartialApplyInst *PAI) { auto *FRI = dyn_cast(PAI->getCallee()); if (!FRI) return false; SILFunction *Callee = FRI->getReferencedFunction(); if (!isLinkAll() && !Callee->isTransparent() && Callee->getLinkage() != SILLinkage::Shared) return false; addFunctionToWorklist(Callee); return true; } bool visitFunctionRefInst(FunctionRefInst *FRI) { // Needed to handle closures which are no longer applied, but are left // behind as dead code. This shouldn't happen, but if it does don't get into // an inconsistent state. SILFunction *Callee = FRI->getReferencedFunction(); if (!isLinkAll() && !Callee->isTransparent() && Callee->getLinkage() != SILLinkage::Shared) return false; addFunctionToWorklist(FRI->getReferencedFunction()); return true; } bool visitProtocolConformance(ProtocolConformance *C, const Optional &Member) { // If a null protocol conformance was passed in, just return false. if (!C) return false; // Otherwise try and lookup a witness table for C. SILWitnessTable *WT = Mod.lookUpWitnessTable(C).first; // If we don't find any witness table for the conformance, bail and return // false. if (!WT) { Mod.createWitnessTableDeclaration(C); return false; } // 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 false; bool performFuncDeserialization = false; // For each entry in the witness table... for (auto &E : WT->getEntries()) { // If the entry is a witness method... if (E.getKind() == 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; // Otherwise if it is the requirement we are looking for or we just want // to deserialize everything, add the function to the list of functions // to deserialize. performFuncDeserialization = true; addFunctionToWorklist(E.getMethodWitness().Witness); } } return performFuncDeserialization; } bool visitWitnessMethodInst(WitnessMethodInst *WMI) { return visitProtocolConformance(WMI->getConformance(), WMI->getMember()); } bool visitInitExistentialInst(InitExistentialInst *IEI) { // Link in all protocol conformances that this touches. // // TODO: There might be a two step solution where the init_existential_inst // causes the witness table to be brought in as a declaration and then the // protocol method inst causes the actual deserialization. For now we are // not going to be smart about this to enable avoiding any issues with // visiting the protocol_method before the init_existential_inst. bool performFuncDeserialization = false; for (ProtocolConformance *C : IEI->getConformances()) { performFuncDeserialization |= visitProtocolConformance(C, Optional()); } return performFuncDeserialization; } bool visitInitExistentialRefInst(InitExistentialRefInst *IERI) { // Link in all protocol conformances that this touches. // // TODO: There might be a two step solution where the init_existential_inst // causes the witness table to be brought in as a declaration and then the // protocol method inst causes the actual deserialization. For now we are // not going to be smart about this to enable avoiding any issues with // visiting the protocol_method before the init_existential_inst. bool performFuncDeserialization = false; for (ProtocolConformance *C : IERI->getConformances()) { performFuncDeserialization |= visitProtocolConformance(C, Optional()); } return performFuncDeserialization; } bool visitAllocRefInst(AllocRefInst *ARI) { // Grab the class decl from the alloc ref inst. ClassDecl *D = ARI->getType().getClassOrBoundGenericClass(); if (!D) return false; return linkInVTable(D); } bool visitMetatypeInst(MetatypeInst *MI) { CanType instTy = MI->getType().castTo().getInstanceType(); ClassDecl *C = instTy.getClassOrBoundGenericClass(); if (!C) return false; return linkInVTable(C); } private: /// Add a function to our function worklist for processing. void addFunctionToWorklist(SILFunction *F) { FunctionDeserializationWorklist.push_back(F); } /// Is the current mode link all? Link all implies we should try and link /// everything, not just transparent/shared functions. bool isLinkAll() const { return Mode == LinkingMode::LinkAll; } bool linkInVTable(ClassDecl *D) { // Attempt to lookup the Vtbl from the SILModule. SILVTable *Vtbl = Mod.lookUpVTable(D); // If the SILModule does not have the VTable, attempt to deserialize the // VTable. If we fail to do that as well, bail. if (!Vtbl || !(Vtbl = Loader->lookupVTable(D->getName()))) return false; // Ok we found our VTable. Visit each function referenced by the VTable. If // any of the functions are external declarations, add them to the worklist // for processing. bool Result = false; for (auto P : Vtbl->getEntries()) { if (P.second->isExternalDeclaration()) { Result = true; addFunctionToWorklist(P.second); } } return Result; } // Main loop of the visitor. Called by one of the other *visit* methods. bool process() { // Process everything transitively referenced by one of the functions in the // worklist. bool Result = false; while (!Worklist.empty()) { auto Fn = Worklist.pop_back_val(); for (auto &BB : *Fn) { for (auto &I : BB) { // Should we try linking? if (visit(&I)) { for (auto F : FunctionDeserializationWorklist) { // The ExternalSource may wish to rewrite non-empty bodies. if (!F->empty() && ExternalSource) if (auto NewFn = ExternalSource->lookupSILFunction(F)) { NewFn->verify(); Worklist.push_back(NewFn); ++NumFuncLinked; Result = true; continue; } F->setBare(IsBare); if (F->empty()) if (auto NewFn = Loader->lookupSILFunction(F)) { NewFn->verify(); Worklist.push_back(NewFn); Result = true; ++NumFuncLinked; } } FunctionDeserializationWorklist.clear(); } else { assert(FunctionDeserializationWorklist.empty() && "Worklist should " "always be empty if visit does not return true."); } } } } // If we return true, we deserialized at least one function. return Result; } }; } // end anonymous namespace. bool SILModule::linkFunction(SILFunction *Fun, SILModule::LinkingMode Mode) { return SILLinkerVisitor(*this, getSILLoader(), Mode, ExternalSource).processFunction(Fun); } void SILModule::linkAllWitnessTables() { getSILLoader()->getAllWitnessTables(); } void SILModule::linkAllVTables() { getSILLoader()->getAllVTables(); } SILVTable *SILModule::lookUpVTable(const ClassDecl *C) { if (!C) return nullptr; // First try to look up R from the lookup table. auto R = VTableLookupTable.find(C); if (R != VTableLookupTable.end()) return R->second; // If that fails, try to deserialize it. If that fails, return nullptr. SILVTable *Vtbl = SILLinkerVisitor(*this, getSILLoader(), SILModule::LinkingMode::LinkAll, ExternalSource).processClassDecl(C); if (!Vtbl) return nullptr; // If we succeeded, map C -> VTbl in the table and return VTbl. VTableLookupTable[C] = Vtbl; return Vtbl; } SerializedSILLoader *SILModule::getSILLoader() { // If the SILLoader is null, create it. if (!SILLoader) SILLoader = SerializedSILLoader::create(getASTContext(), this, Callback.get()); // Return the SerializedSILLoader. return SILLoader.get(); } /// \brief Given a protocol \p Proto, a member method \p Member and a concrete /// class type \p ConcreteTy, search the witness tables and return the static /// function that matches the member with any specializations may be /// required. Notice that we do not scan the class hierarchy, just the concrete /// class type. std::tuple> SILModule::findFuncInWitnessTable(const ProtocolConformance *C, SILDeclRef Member) { // Look up the witness table associated with our protocol conformance from the // SILModule. auto Ret = lookUpWitnessTable(C); // If no witness table was found, bail. if (!Ret.first) { DEBUG(llvm::dbgs() << " Failed speculative lookup of witness for: "; C->dump()); return {nullptr, nullptr, ArrayRef()}; } // Okay, we found the correct witness table. Now look for the method. for (auto &Entry : Ret.first->getEntries()) { // Look at method entries only. if (Entry.getKind() != SILWitnessTable::WitnessKind::Method) continue; SILWitnessTable::MethodWitness MethodEntry = Entry.getMethodWitness(); // Check if this is the member we were looking for. if (MethodEntry.Requirement != Member) continue; return {MethodEntry.Witness, Ret.first, Ret.second}; } return {nullptr, nullptr, ArrayRef()}; } static ClassDecl *getClassDeclSuperClass(ClassDecl *Class) { Type T = Class->getSuperclass(); if (!T) return nullptr; return T->getCanonicalType()->getClassOrBoundGenericClass(); } SILFunction * SILModule:: lookUpSILFunctionFromVTable(ClassDecl *Class, SILDeclRef Member) { // Until we reach the top of the class hierarchy... while (Class) { // Try to lookup a VTable for Class from the module... auto *Vtbl = lookUpVTable(Class); // If the lookup fails, skip Class and attempt to resolve the method in // the VTable of the super class of Class if it exists... if (!Vtbl) { Class = getClassDeclSuperClass(Class); continue; } // Ok, we have a VTable. Try to lookup the SILFunction implementation from // the VTable. if (SILFunction *F = Vtbl->getImplementation(*this, Member)) return F; // If we fail to lookup the SILFunction, again skip Class and attempt to // resolve the method in the VTable of the super class of Class if such a // super class exists. Class = getClassDeclSuperClass(Class); } return nullptr; }