| //===--- TBDGen.cpp - Swift TBD Generation --------------------------------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file implements the entrypoints into TBD file generation. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "swift/TBDGen/TBDGen.h" |
| |
| #include "swift/AST/ASTMangler.h" |
| #include "swift/AST/ASTVisitor.h" |
| #include "swift/AST/Module.h" |
| #include "swift/AST/ParameterList.h" |
| #include "swift/Basic/LLVM.h" |
| #include "swift/IRGen/Linking.h" |
| #include "swift/SIL/FormalLinkage.h" |
| #include "swift/SIL/SILDeclRef.h" |
| #include "swift/SIL/SILWitnessTable.h" |
| #include "swift/SIL/TypeLowering.h" |
| #include "llvm/ADT/StringSet.h" |
| |
| #include "TBDGenVisitor.h" |
| |
| using namespace swift; |
| using namespace swift::irgen; |
| using namespace swift::tbdgen; |
| using StringSet = llvm::StringSet<>; |
| |
| static bool isPrivateDecl(ValueDecl *VD) { |
| return getDeclLinkage(VD) != FormalLinkage::PublicUnique; |
| } |
| |
| static bool isGlobalOrStaticVar(VarDecl *VD) { |
| return VD->isStatic() || VD->getDeclContext()->isModuleScopeContext(); |
| } |
| |
| void TBDGenVisitor::visitPatternBindingDecl(PatternBindingDecl *PBD) { |
| for (auto &entry : PBD->getPatternList()) { |
| auto *var = entry.getAnchoringVarDecl(); |
| if (isPrivateDecl(var)) |
| return; |
| |
| // Non-global variables might have an explicit initializer symbol. |
| if (entry.getInit() && !isGlobalOrStaticVar(var)) { |
| auto declRef = |
| SILDeclRef(var, SILDeclRef::Kind::StoredPropertyInitializer); |
| // Stored property initializers for public properties are currently |
| // public. |
| addSymbol(declRef); |
| } |
| } |
| } |
| |
| void TBDGenVisitor::addSymbol(SILDeclRef declRef) { |
| bool isPrivate = !hasPublicVisibility(declRef.getLinkage(ForDefinition)); |
| // Even private methods of open classes (specifically, private methods that |
| // are in the vtable) have public symbols, because external subclasses |
| // currently need to refer to them by symbol for their own vtable. |
| switch (declRef.getSubclassScope()) { |
| case SubclassScope::External: |
| // Unlike the "truly" public things, private things have public symbols |
| // unconditionally, even if they're theoretically SIL only. |
| if (isPrivate) { |
| isPrivate = false; |
| } |
| break; |
| case SubclassScope::Internal: |
| case SubclassScope::NotApplicable: |
| break; |
| } |
| if (isPrivate) |
| return; |
| |
| // FIXME: this includes too many symbols. There are some that are considered |
| // SIL-only, but it isn't obvious how to determine this (e.g. it seems that |
| // many, but not all, transparent functions result in object-file symbols) |
| |
| addSymbol(declRef.mangle()); |
| } |
| |
| void TBDGenVisitor::addConformances(DeclContext *DC) { |
| for (auto conformance : DC->getLocalConformances()) { |
| auto protocol = conformance->getProtocol(); |
| auto needsWTable = |
| Lowering::TypeConverter::protocolRequiresWitnessTable(protocol); |
| if (!needsWTable) |
| continue; |
| |
| // Only normal conformances get symbols; the others get any public symbols |
| // from their parent normal conformance. |
| auto normalConformance = dyn_cast<NormalProtocolConformance>(conformance); |
| if (!normalConformance) |
| continue; |
| |
| addSymbol(LinkEntity::forDirectProtocolWitnessTable(normalConformance)); |
| addSymbol( |
| LinkEntity::forProtocolWitnessTableAccessFunction(normalConformance)); |
| |
| // FIXME: the logic around visibility in extensions is confusing, and |
| // sometimes witness thunks need to be manually made public. |
| |
| auto conformanceIsFixed = SILWitnessTable::conformanceIsSerialized( |
| normalConformance); |
| auto addSymbolIfNecessary = [&](ValueDecl *valueReq, |
| SILLinkage witnessLinkage) { |
| if (conformanceIsFixed && |
| fixmeWitnessHasLinkageThatNeedsToBePublic(witnessLinkage)) { |
| Mangle::ASTMangler Mangler; |
| addSymbol(Mangler.mangleWitnessThunk(normalConformance, valueReq)); |
| } |
| }; |
| normalConformance->forEachValueWitness(nullptr, [&](ValueDecl *valueReq, |
| Witness witness) { |
| if (isa<AbstractFunctionDecl>(valueReq)) { |
| auto witnessLinkage = |
| SILDeclRef(witness.getDecl()).getLinkage(ForDefinition); |
| addSymbolIfNecessary(valueReq, witnessLinkage); |
| } else if (auto VD = dyn_cast<AbstractStorageDecl>(valueReq)) { |
| // A var or subscript decl needs extra special handling: the things that |
| // end up in the witness table are the accessors, but the compiler only |
| // talks about the actual storage decl in the conformance, so we have to |
| // manually walk over the members, having pulled out something that will |
| // have the right linkage. |
| auto witnessVD = cast<AbstractStorageDecl>(witness.getDecl()); |
| |
| SmallVector<Decl *, 4> members; |
| VD->getAllAccessorFunctions(members); |
| |
| // Grab one of the accessors, and then use that to pull out which of the |
| // getter or setter will have the appropriate linkage. |
| FuncDecl *witnessWithRelevantLinkage; |
| switch (cast<FuncDecl>(members[0])->getAccessorKind()) { |
| case AccessorKind::NotAccessor: |
| llvm_unreachable("must be an accessor"); |
| case AccessorKind::IsGetter: |
| case AccessorKind::IsAddressor: |
| witnessWithRelevantLinkage = witnessVD->getGetter(); |
| break; |
| case AccessorKind::IsSetter: |
| case AccessorKind::IsWillSet: |
| case AccessorKind::IsDidSet: |
| case AccessorKind::IsMaterializeForSet: |
| case AccessorKind::IsMutableAddressor: |
| witnessWithRelevantLinkage = witnessVD->getSetter(); |
| break; |
| } |
| auto witnessLinkage = |
| SILDeclRef(witnessWithRelevantLinkage).getLinkage(ForDefinition); |
| for (auto member : members) { |
| addSymbolIfNecessary(cast<ValueDecl>(member), witnessLinkage); |
| } |
| } |
| }); |
| } |
| } |
| |
| void TBDGenVisitor::visitValueDecl(ValueDecl *VD) { |
| addSymbol(SILDeclRef(VD)); |
| visitMembers(VD); |
| } |
| |
| void TBDGenVisitor::visitAbstractFunctionDecl(AbstractFunctionDecl *AFD) { |
| if (auto FD = dyn_cast<FuncDecl>(AFD)) { |
| // Accessors also appear nested inside the storage decl, which we treat as |
| // the canonical location, so skip if we've got an accessor that isn't |
| // inside the var decl. |
| if (FD->getAccessorStorageDecl() && !InsideAbstractStorageDecl) |
| return; |
| } |
| |
| // Default arguments (of public functions) are public symbols, as the default |
| // values are computed at the call site. |
| auto index = 0; |
| auto paramLists = AFD->getParameterLists(); |
| // Skip the first arguments, which contains Self (etc.), can't be defaulted, |
| // and are ignored for the purposes of default argument indices. |
| if (AFD->getDeclContext()->isTypeContext()) |
| paramLists = paramLists.slice(1); |
| for (auto *paramList : paramLists) { |
| for (auto *param : *paramList) { |
| if (param->getDefaultValue()) |
| addSymbol(SILDeclRef::getDefaultArgGenerator(AFD, index)); |
| index++; |
| } |
| } |
| |
| visitValueDecl(AFD); |
| } |
| |
| void TBDGenVisitor::visitAbstractStorageDecl(AbstractStorageDecl *ASD) { |
| assert(!InsideAbstractStorageDecl && |
| "unexpected nesting of abstract storage decls"); |
| InsideAbstractStorageDecl = true; |
| visitMembers(ASD); |
| InsideAbstractStorageDecl = false; |
| } |
| void TBDGenVisitor::visitVarDecl(VarDecl *VD) { |
| // statically/globally stored variables have some special handling. |
| if (VD->hasStorage() && isGlobalOrStaticVar(VD)) { |
| // The actual variable has a symbol. |
| Mangle::ASTMangler mangler; |
| addSymbol(mangler.mangleEntity(VD, false)); |
| |
| // Top-level variables (*not* statics) in the main file don't get accessors, |
| // despite otherwise looking like globals. |
| if (!FileHasEntryPoint || VD->isStatic()) |
| addSymbol(SILDeclRef(VD, SILDeclRef::Kind::GlobalAccessor)); |
| } |
| |
| visitAbstractStorageDecl(VD); |
| } |
| |
| void TBDGenVisitor::visitNominalTypeDecl(NominalTypeDecl *NTD) { |
| auto declaredType = NTD->getDeclaredType()->getCanonicalType(); |
| |
| addSymbol(LinkEntity::forNominalTypeDescriptor(NTD)); |
| |
| // Generic types do not get metadata directly, only through the function. |
| if (!NTD->isGenericContext()) { |
| addSymbol(LinkEntity::forTypeMetadata(declaredType, |
| TypeMetadataAddress::AddressPoint, |
| /*isPattern=*/false)); |
| } |
| addSymbol(LinkEntity::forTypeMetadataAccessFunction(declaredType)); |
| |
| // There are symbols associated with any protocols this type conforms to. |
| addConformances(NTD); |
| |
| visitMembers(NTD); |
| } |
| |
| void TBDGenVisitor::visitClassDecl(ClassDecl *CD) { |
| if (isPrivateDecl(CD)) |
| return; |
| |
| auto &ctxt = CD->getASTContext(); |
| auto isGeneric = CD->isGenericContext(); |
| auto objCCompatible = ctxt.LangOpts.EnableObjCInterop && !isGeneric; |
| auto isObjC = objCCompatible && CD->isObjC(); |
| |
| // Metaclasses and ObjC class (duh) are a ObjC thing, and so are not needed in |
| // build artifacts/for classes which can't touch ObjC. |
| if (objCCompatible) { |
| if (isObjC) |
| addSymbol(LinkEntity::forObjCClass(CD)); |
| |
| if (CD->getMetaclassKind() == ClassDecl::MetaclassKind::ObjC) |
| addSymbol(LinkEntity::forObjCMetaclass(CD)); |
| else |
| addSymbol(LinkEntity::forSwiftMetaclassStub(CD)); |
| } |
| |
| // Some members of classes get extra handling, beyond members of struct/enums, |
| // so let's walk over them manually. |
| for (auto *member : CD->getMembers()) { |
| auto value = dyn_cast<ValueDecl>(member); |
| if (!value) |
| continue; |
| |
| auto var = dyn_cast<VarDecl>(value); |
| auto hasFieldOffset = var && var->hasStorage() && !var->isStatic(); |
| if (hasFieldOffset) { |
| // FIXME: a field only has one sort of offset, but it is moderately |
| // non-trivial to compute which one. Including both is less painful than |
| // missing the correct one (for now), so we do that. |
| addSymbol(LinkEntity::forFieldOffset(var, /*isIndirect=*/false)); |
| addSymbol(LinkEntity::forFieldOffset(var, /*isIndirect=*/true)); |
| } |
| |
| // The non-allocating forms of the destructors. |
| if (auto dtor = dyn_cast<DestructorDecl>(value)) { |
| // ObjC classes don't have a symbol for their destructor. |
| if (!isObjC) |
| addSymbol(SILDeclRef(dtor, SILDeclRef::Kind::Destroyer)); |
| } |
| } |
| |
| visitNominalTypeDecl(CD); |
| } |
| |
| void TBDGenVisitor::visitConstructorDecl(ConstructorDecl *CD) { |
| if (CD->getParent()->getAsClassOrClassExtensionContext()) { |
| // Class constructors come in two forms, allocating and non-allocating. The |
| // default ValueDecl handling gives the allocating one, so we have to |
| // manually include the non-allocating one. |
| addSymbol(SILDeclRef(CD, SILDeclRef::Kind::Initializer)); |
| } |
| visitAbstractFunctionDecl(CD); |
| } |
| |
| void TBDGenVisitor::visitExtensionDecl(ExtensionDecl *ED) { |
| if (!ED->getExtendedType()->isExistentialType()) { |
| addConformances(ED); |
| } |
| |
| visitMembers(ED); |
| } |
| |
| void TBDGenVisitor::visitProtocolDecl(ProtocolDecl *PD) { |
| if (!PD->isObjC()) |
| addSymbol(LinkEntity::forProtocolDescriptor(PD)); |
| |
| #ifndef NDEBUG |
| // There's no (currently) relevant information about members of a protocol at |
| // individual protocols, each conforming type has to handle them individually |
| // (NB. anything within an active IfConfigDecls also appears outside). Let's |
| // assert this fact: |
| for (auto *member : PD->getMembers()) { |
| auto isExpectedKind = |
| isa<TypeAliasDecl>(member) || isa<AssociatedTypeDecl>(member) || |
| isa<AbstractStorageDecl>(member) || isa<PatternBindingDecl>(member) || |
| isa<AbstractFunctionDecl>(member) || isa<IfConfigDecl>(member); |
| assert(isExpectedKind && |
| "unexpected member of protocol during TBD generation"); |
| } |
| #endif |
| } |
| |
| static void enumeratePublicSymbolsAndWrite(ModuleDecl *M, FileUnit *singleFile, |
| StringSet &symbols, |
| bool hasMultipleIRGenThreads, |
| llvm::raw_ostream *os, |
| StringRef installName) { |
| auto isWholeModule = singleFile == nullptr; |
| const auto &target = M->getASTContext().LangOpts.Target; |
| UniversalLinkageInfo linkInfo(target, hasMultipleIRGenThreads, isWholeModule); |
| |
| TBDGenVisitor visitor(symbols, target, linkInfo, M, installName); |
| |
| auto visitFile = [&](FileUnit *file) { |
| SmallVector<Decl *, 16> decls; |
| file->getTopLevelDecls(decls); |
| |
| visitor.setFileHasEntryPoint(file->hasEntryPoint()); |
| |
| for (auto d : decls) |
| visitor.visit(d); |
| }; |
| |
| if (singleFile) { |
| assert(M == singleFile->getParentModule() && "mismatched file and module"); |
| visitFile(singleFile); |
| } else { |
| for (auto *file : M->getFiles()) { |
| visitFile(file); |
| } |
| } |
| |
| if (os) { |
| // The correct TBD formatting code is temporarily non-open source, so this |
| // is just a list of the symbols. |
| std::vector<StringRef> sorted; |
| for (auto &symbol : symbols) |
| sorted.push_back(symbol.getKey()); |
| std::sort(sorted.begin(), sorted.end()); |
| for (const auto &symbol : sorted) { |
| *os << symbol << "\n"; |
| } |
| } |
| } |
| |
| void swift::enumeratePublicSymbols(FileUnit *file, StringSet &symbols, |
| bool hasMultipleIRGenThreads) { |
| enumeratePublicSymbolsAndWrite( |
| file->getParentModule(), file, symbols, hasMultipleIRGenThreads, |
| nullptr, StringRef()); |
| } |
| void swift::enumeratePublicSymbols(ModuleDecl *M, StringSet &symbols, |
| bool hasMultipleIRGenThreads) { |
| enumeratePublicSymbolsAndWrite(M, nullptr, symbols, hasMultipleIRGenThreads, |
| nullptr, StringRef()); |
| } |
| void swift::writeTBDFile(ModuleDecl *M, llvm::raw_ostream &os, |
| bool hasMultipleIRGenThreads, |
| StringRef installName) { |
| StringSet symbols; |
| enumeratePublicSymbolsAndWrite(M, nullptr, symbols, hasMultipleIRGenThreads, |
| &os, installName); |
| } |