blob: e2d36f8c55e63c3badddf2047fed6112ba5e3f4a [file] [log] [blame]
//===--- Linker.cpp -------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "sil-linker"
#include "Linker.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/Debug.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/SubstitutionMap.h"
#include "swift/ClangImporter/ClangModule.h"
#include "swift/SIL/FormalLinkage.h"
#include <functional>
using namespace swift;
using namespace Lowering;
STATISTIC(NumFuncLinked, "Number of SIL functions linked");
//===----------------------------------------------------------------------===//
// Linker Helpers
//===----------------------------------------------------------------------===//
/// Process F, recursively deserializing any thing F may reference.
bool SILLinkerVisitor::processFunction(SILFunction *F) {
if (Mode == LinkingMode::LinkNone)
return false;
// If F is a declaration, first deserialize it.
if (F->isExternalDeclaration()) {
auto *NewFn = Loader->lookupSILFunction(F);
if (!NewFn || NewFn->isExternalDeclaration())
return false;
F = NewFn;
}
++NumFuncLinked;
// Try to transitively deserialize everything referenced by this
// function.
Worklist.push_back(F);
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 *SILLinkerVisitor::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.Implementation);
// And then transitively deserialize all SIL referenced by those functions.
process();
// Return the deserialized Vtbl.
return Vtbl;
}
bool SILLinkerVisitor::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.Implementation->isExternalDeclaration()) {
Result = true;
addFunctionToWorklist(P.Implementation);
}
}
return Result;
}
//===----------------------------------------------------------------------===//
// Visitors
//===----------------------------------------------------------------------===//
bool SILLinkerVisitor::visitApplyInst(ApplyInst *AI) {
bool performFuncDeserialization = false;
if (auto sig = AI->getCallee()->getType().castTo<SILFunctionType>()
->getGenericSignature()) {
performFuncDeserialization |= visitApplySubstitutions(
sig->getSubstitutionMap(AI->getSubstitutions()));
}
// Ok we have a function ref inst, grab the callee.
SILFunction *Callee = AI->getReferencedFunction();
if (!Callee)
return performFuncDeserialization;
if (isLinkAll() ||
hasSharedVisibility(Callee->getLinkage())) {
addFunctionToWorklist(Callee);
return true;
}
return performFuncDeserialization;
}
bool SILLinkerVisitor::visitPartialApplyInst(PartialApplyInst *PAI) {
bool performFuncDeserialization = false;
if (auto sig = PAI->getCallee()->getType().castTo<SILFunctionType>()
->getGenericSignature()) {
performFuncDeserialization |= visitApplySubstitutions(
sig->getSubstitutionMap(PAI->getSubstitutions()));
}
SILFunction *Callee = PAI->getReferencedFunction();
if (!Callee)
return performFuncDeserialization;
if (isLinkAll() ||
hasSharedVisibility(Callee->getLinkage())) {
addFunctionToWorklist(Callee);
return true;
}
return performFuncDeserialization;
}
bool SILLinkerVisitor::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() ||
hasSharedVisibility(Callee->getLinkage())) {
addFunctionToWorklist(FRI->getReferencedFunction());
return true;
}
return false;
}
// Eagerly visiting all used conformances 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.
static bool mustDeserializeProtocolConformance(SILModule &M,
ProtocolConformanceRef c) {
if (!c.isConcrete())
return false;
auto conformance = c.getConcrete()->getRootNormalConformance();
return M.Types.protocolRequiresWitnessTable(conformance->getProtocol())
&& isa<ClangModuleUnit>(conformance->getDeclContext()
->getModuleScopeContext());
}
bool SILLinkerVisitor::visitProtocolConformance(
ProtocolConformanceRef ref, const Optional<SILDeclRef> &Member) {
// If an abstract protocol conformance was passed in, just return false.
if (ref.isAbstract())
return false;
bool mustDeserialize = mustDeserializeProtocolConformance(Mod, ref);
// Otherwise try and lookup a witness table for C.
auto C = ref.getConcrete();
if (!VisitedConformances.insert(C).second)
return false;
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 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;
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))
performFuncDeserialization |= 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 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);
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;
}
}
return performFuncDeserialization;
}
bool SILLinkerVisitor::visitApplySubstitutions(const SubstitutionMap &subs) {
bool performFuncDeserialization = false;
for (auto &reqt : subs.getGenericSignature()->getRequirements()) {
switch (reqt.getKind()) {
case RequirementKind::Conformance: {
auto conformance = subs.lookupConformance(
reqt.getFirstType()->getCanonicalType(),
cast<ProtocolDecl>(reqt.getSecondType()->getAnyNominal()))
.getValue();
// Formally all conformances referenced in a function application are
// used. However, eagerly visiting them all at this point leads to a
// large blowup in the amount of SIL we read in, and we aren't very
// systematic about laziness. 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, conformance)) {
performFuncDeserialization |=
visitProtocolConformance(conformance, None);
}
break;
}
case RequirementKind::Layout:
case RequirementKind::SameType:
case RequirementKind::Superclass:
break;
}
}
return performFuncDeserialization;
}
bool SILLinkerVisitor::visitInitExistentialAddrInst(
InitExistentialAddrInst *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 open_existential_addr/witness_method before the
// init_existential_inst.
bool performFuncDeserialization = false;
for (ProtocolConformanceRef C : IEI->getConformances()) {
performFuncDeserialization |=
visitProtocolConformance(C, Optional<SILDeclRef>());
}
return performFuncDeserialization;
}
bool SILLinkerVisitor::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 (ProtocolConformanceRef C : IERI->getConformances()) {
performFuncDeserialization |=
visitProtocolConformance(C, Optional<SILDeclRef>());
}
return performFuncDeserialization;
}
bool SILLinkerVisitor::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 SILLinkerVisitor::visitMetatypeInst(MetatypeInst *MI) {
CanType instTy = MI->getType().castTo<MetatypeType>().getInstanceType();
ClassDecl *C = instTy.getClassOrBoundGenericClass();
if (!C)
return false;
return linkInVTable(C);
}
//===----------------------------------------------------------------------===//
// Top Level Routine
//===----------------------------------------------------------------------===//
// Main loop of the visitor. Called by one of the other *visit* methods.
bool SILLinkerVisitor::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();
if (Fn->getModule().isSerialized()) {
// If the containing module has been serialized,
// Remove The Serialized state (if any)
// This allows for more optimizations
Fn->setSerialized(IsSerialized_t::IsNotSerialized);
}
DEBUG(llvm::dbgs() << "Process imports in function: "
<< Fn->getName() << "\n");
for (auto &BB : *Fn) {
for (auto &I : BB) {
// Should we try linking?
if (visit(&I)) {
for (auto *F : FunctionDeserializationWorklist) {
DEBUG(llvm::dbgs() << "Imported function: "
<< F->getName() << "\n");
F->setBare(IsBare);
if (F->isExternalDeclaration()) {
if (auto *NewFn = Loader->lookupSILFunction(F)) {
if (NewFn->isExternalDeclaration())
continue;
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;
}