mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Refactor SILLinkerVisitor into its own local files, Linker.{h,cpp}. This is still hidden in the SIL library.
This is only used by SILModule but is not integral to a SILModule so it makes sense to have it in its own file. It keeps SILModule.cpp more focused. We still keep it in a private header though since it is only meant to be used by SILModule.cpp. Swift SVN r25985
This commit is contained in:
@@ -26,6 +26,7 @@ add_swift_library(swiftSIL
|
|||||||
LoopInfo.cpp
|
LoopInfo.cpp
|
||||||
Projection.cpp
|
Projection.cpp
|
||||||
Mangle.cpp
|
Mangle.cpp
|
||||||
|
Linker.cpp
|
||||||
LINK_LIBRARIES
|
LINK_LIBRARIES
|
||||||
swiftSerialization
|
swiftSerialization
|
||||||
swiftSema)
|
swiftSema)
|
||||||
|
|||||||
329
lib/SIL/Linker.cpp
Normal file
329
lib/SIL/Linker.cpp
Normal file
@@ -0,0 +1,329 @@
|
|||||||
|
//===--- Linker.cpp -------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#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/ADT/Statistic.h"
|
||||||
|
#include "llvm/Support/Debug.h"
|
||||||
|
|
||||||
|
using namespace swift;
|
||||||
|
using namespace Lowering;
|
||||||
|
|
||||||
|
STATISTIC(NumFuncLinked, "Number of SIL functions linked");
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Utility
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
/// \return True if the function \p F should be imported into the current
|
||||||
|
/// module.
|
||||||
|
static bool shouldImportFunction(SILFunction *F) {
|
||||||
|
// Skip functions that are marked with the 'no import' tag. These
|
||||||
|
// are functions that we don't want to copy from the module.
|
||||||
|
if (F->hasSemanticsString("stdlib_binary_only"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Linker Helpers
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
/// Process F, recursively deserializing any thing F may reference.
|
||||||
|
bool SILLinkerVisitor::processFunction(SILFunction *F) {
|
||||||
|
if (Mode == LinkingMode::LinkNone)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!shouldImportFunction(F))
|
||||||
|
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 *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.second);
|
||||||
|
|
||||||
|
// 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.second->isExternalDeclaration()) {
|
||||||
|
Result = true;
|
||||||
|
addFunctionToWorklist(P.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Visitors
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
bool SILLinkerVisitor::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<FunctionRefInst>(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() &&
|
||||||
|
!hasSharedVisibility(Callee->getLinkage()))
|
||||||
|
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 SILLinkerVisitor::visitPartialApplyInst(PartialApplyInst *PAI) {
|
||||||
|
auto *FRI = dyn_cast<FunctionRefInst>(PAI->getCallee());
|
||||||
|
if (!FRI)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
SILFunction *Callee = FRI->getReferencedFunction();
|
||||||
|
if (!isLinkAll() && !Callee->isTransparent() &&
|
||||||
|
!hasSharedVisibility(Callee->getLinkage()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
addFunctionToWorklist(Callee);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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() && !Callee->isTransparent() &&
|
||||||
|
!hasSharedVisibility(Callee->getLinkage()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
addFunctionToWorklist(FRI->getReferencedFunction());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SILLinkerVisitor::visitProtocolConformance(
|
||||||
|
ProtocolConformance *C, const Optional<SILDeclRef> &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, TypeConverter::getLinkageForProtocolConformance(
|
||||||
|
C->getRootNormalConformance(), NotForDefinition));
|
||||||
|
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;
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (ProtocolConformance *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 (ProtocolConformance *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 (!shouldImportFunction(Fn))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (auto &BB : *Fn) {
|
||||||
|
for (auto &I : BB) {
|
||||||
|
// Should we try linking?
|
||||||
|
if (visit(&I)) {
|
||||||
|
for (auto F : FunctionDeserializationWorklist) {
|
||||||
|
|
||||||
|
if (!shouldImportFunction(F))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
93
lib/SIL/Linker.h
Normal file
93
lib/SIL/Linker.h
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
//===--- Linker.h --------------------------------------------*- C++ -*----===//
|
||||||
|
//
|
||||||
|
// 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/SILDebugScope.h"
|
||||||
|
#include "swift/SIL/SILExternalSource.h"
|
||||||
|
#include "swift/SIL/SILVisitor.h"
|
||||||
|
#include "swift/SIL/SILModule.h"
|
||||||
|
#include "swift/Serialization/SerializedSILLoader.h"
|
||||||
|
|
||||||
|
namespace swift {
|
||||||
|
|
||||||
|
/// Visitor that knows how to link in dependencies of SILInstructions.
|
||||||
|
class SILLinkerVisitor : public SILInstructionVisitor<SILLinkerVisitor, bool> {
|
||||||
|
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<SILFunction *, 128> Worklist;
|
||||||
|
|
||||||
|
/// A list of callees of the current instruction being visited. cleared after
|
||||||
|
/// every instruction is visited.
|
||||||
|
llvm::SmallVector<SILFunction *, 4> 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);
|
||||||
|
|
||||||
|
/// 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);
|
||||||
|
|
||||||
|
/// 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);
|
||||||
|
bool visitPartialApplyInst(PartialApplyInst *PAI);
|
||||||
|
bool visitFunctionRefInst(FunctionRefInst *FRI);
|
||||||
|
bool visitProtocolConformance(ProtocolConformance *C,
|
||||||
|
const Optional<SILDeclRef> &Member);
|
||||||
|
bool visitWitnessMethodInst(WitnessMethodInst *WMI) {
|
||||||
|
return visitProtocolConformance(WMI->getConformance(), WMI->getMember());
|
||||||
|
}
|
||||||
|
bool visitInitExistentialAddrInst(InitExistentialAddrInst *IEI);
|
||||||
|
bool visitInitExistentialRefInst(InitExistentialRefInst *IERI);
|
||||||
|
bool visitAllocRefInst(AllocRefInst *ARI);
|
||||||
|
bool visitMetatypeInst(MetatypeInst *MI);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Main loop of the visitor. Called by one of the other *visit* methods.
|
||||||
|
bool process();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // end anonymous namespace.
|
||||||
@@ -11,8 +11,9 @@
|
|||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#define DEBUG_TYPE "sil-module"
|
#define DEBUG_TYPE "sil-module"
|
||||||
#include "swift/SIL/SILDebugScope.h"
|
|
||||||
#include "swift/SIL/SILModule.h"
|
#include "swift/SIL/SILModule.h"
|
||||||
|
#include "Linker.h"
|
||||||
|
#include "swift/SIL/SILDebugScope.h"
|
||||||
#include "swift/SIL/SILExternalSource.h"
|
#include "swift/SIL/SILExternalSource.h"
|
||||||
#include "swift/SIL/SILVisitor.h"
|
#include "swift/SIL/SILVisitor.h"
|
||||||
#include "swift/Serialization/SerializedSILLoader.h"
|
#include "swift/Serialization/SerializedSILLoader.h"
|
||||||
@@ -25,8 +26,6 @@
|
|||||||
using namespace swift;
|
using namespace swift;
|
||||||
using namespace Lowering;
|
using namespace Lowering;
|
||||||
|
|
||||||
STATISTIC(NumFuncLinked, "Number of SIL functions linked");
|
|
||||||
|
|
||||||
namespace swift {
|
namespace swift {
|
||||||
/// SILTypeList - The uniqued backing store for the SILValue type list. This
|
/// 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
|
/// is only exposed out of SILValue as an ArrayRef of types, so it should
|
||||||
@@ -365,343 +364,6 @@ const BuiltinInfo &SILModule::getBuiltinInfo(Identifier ID) {
|
|||||||
return Info;
|
return Info;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \return True if the function \p F should be imported into the current
|
|
||||||
/// module.
|
|
||||||
static bool shouldImportFunction(SILFunction *F) {
|
|
||||||
// Skip functions that are marked with the 'no import' tag. These
|
|
||||||
// are functions that we don't want to copy from the module.
|
|
||||||
if (F->hasSemanticsString("stdlib_binary_only"))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
/// Visitor that knows how to link in dependencies of SILInstructions.
|
|
||||||
class SILLinkerVisitor : public SILInstructionVisitor<SILLinkerVisitor, bool> {
|
|
||||||
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<SILFunction *, 128> Worklist;
|
|
||||||
|
|
||||||
/// A list of callees of the current instruction being visited. cleared after
|
|
||||||
/// every instruction is visited.
|
|
||||||
llvm::SmallVector<SILFunction *, 4> 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 (!shouldImportFunction(F))
|
|
||||||
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<FunctionRefInst>(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() &&
|
|
||||||
!hasSharedVisibility(Callee->getLinkage()))
|
|
||||||
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<FunctionRefInst>(PAI->getCallee());
|
|
||||||
if (!FRI)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
SILFunction *Callee = FRI->getReferencedFunction();
|
|
||||||
if (!isLinkAll() && !Callee->isTransparent() &&
|
|
||||||
!hasSharedVisibility(Callee->getLinkage()))
|
|
||||||
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() &&
|
|
||||||
!hasSharedVisibility(Callee->getLinkage()))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
addFunctionToWorklist(FRI->getReferencedFunction());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visitProtocolConformance(ProtocolConformance *C,
|
|
||||||
const Optional<SILDeclRef> &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,
|
|
||||||
TypeConverter::getLinkageForProtocolConformance(
|
|
||||||
C->getRootNormalConformance(), NotForDefinition));
|
|
||||||
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;
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return performFuncDeserialization;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visitWitnessMethodInst(WitnessMethodInst *WMI) {
|
|
||||||
return visitProtocolConformance(WMI->getConformance(), WMI->getMember());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool 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 (ProtocolConformance *C : IEI->getConformances()) {
|
|
||||||
performFuncDeserialization |=
|
|
||||||
visitProtocolConformance(C, Optional<SILDeclRef>());
|
|
||||||
}
|
|
||||||
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<SILDeclRef>());
|
|
||||||
}
|
|
||||||
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<MetatypeType>().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();
|
|
||||||
|
|
||||||
if (!shouldImportFunction(Fn))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
for (auto &BB : *Fn) {
|
|
||||||
for (auto &I : BB) {
|
|
||||||
// Should we try linking?
|
|
||||||
if (visit(&I)) {
|
|
||||||
for (auto F : FunctionDeserializationWorklist) {
|
|
||||||
|
|
||||||
if (!shouldImportFunction(F))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
SILFunction *SILModule::lookUpFunction(SILDeclRef fnRef) {
|
SILFunction *SILModule::lookUpFunction(SILDeclRef fnRef) {
|
||||||
llvm::SmallString<32> name;
|
llvm::SmallString<32> name;
|
||||||
fnRef.mangle(name);
|
fnRef.mangle(name);
|
||||||
|
|||||||
Reference in New Issue
Block a user