mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[deserialization] Deserialize transparent functions lazily iff they will be used in mandatory inlining.
Swift SVN r14490
This commit is contained in:
@@ -58,6 +58,9 @@ public:
|
||||
|
||||
/// Stop optimizing after the Nth pass. For debugging purposes.
|
||||
unsigned NumOptPassesToRun = UINT_MAX;
|
||||
|
||||
/// Are we debugging sil serialization.
|
||||
bool DebugSerialization = false;
|
||||
};
|
||||
|
||||
} // end namespace swift
|
||||
|
||||
@@ -128,6 +128,10 @@ def sil_print_all : Flag<["-"], "sil-print-all">,
|
||||
def sil_time_transforms : Flag<["-"], "sil-time-transforms">,
|
||||
HelpText<"Time each SIL transform invocation">;
|
||||
|
||||
def sil_debug_serialization : Flag<["-"], "sil-debug-serialization">,
|
||||
HelpText<"Only run mandatory inlining and do not allow inlining to eliminate "
|
||||
"dead functions. (for debugging only)">;
|
||||
|
||||
def use_malloc : Flag<["-"], "use-malloc">,
|
||||
HelpText<"Allocate internal data structures using malloc "
|
||||
"(for memory debugging)">;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "swift/AST/ASTContext.h"
|
||||
#include "swift/AST/Builtins.h"
|
||||
#include "swift/AST/Module.h"
|
||||
#include "swift/AST/SILOptions.h"
|
||||
#include "swift/Basic/LangOptions.h"
|
||||
#include "swift/Basic/Range.h"
|
||||
#include "swift/SIL/SILDeclRef.h"
|
||||
@@ -76,6 +77,7 @@ public:
|
||||
using GlobalListType = llvm::ilist<SILGlobalVariable>;
|
||||
using VTableListType = llvm::ilist<SILVTable>;
|
||||
using WitnessTableListType = llvm::ilist<SILWitnessTable>;
|
||||
using LinkingMode = SILOptions::LinkingMode;
|
||||
|
||||
private:
|
||||
friend class SILBasicBlock;
|
||||
@@ -317,6 +319,13 @@ public:
|
||||
return FunctionTable.lookup(name);
|
||||
}
|
||||
|
||||
/// Attempt to link the SILFunction. Returns true if linking succeeded, false
|
||||
/// otherwise.
|
||||
///
|
||||
/// \return false if the linking failed.
|
||||
bool linkFunction(SILFunction *Fun,
|
||||
LinkingMode LinkAll=LinkingMode::LinkNormal);
|
||||
|
||||
/// \brief Return the declaration of a utility function that can,
|
||||
/// but needn't, be shared between modules.
|
||||
SILFunction *getOrCreateSharedFunction(SILLocation loc,
|
||||
|
||||
@@ -17,8 +17,9 @@
|
||||
#ifndef SWIFT_SILPASSES_PASSES_H
|
||||
#define SWIFT_SILPASSES_PASSES_H
|
||||
|
||||
#include "swift/SIL/SILModule.h"
|
||||
|
||||
namespace swift {
|
||||
class SILModule;
|
||||
class SILOptions;
|
||||
class SILTransform;
|
||||
|
||||
@@ -51,7 +52,8 @@ namespace swift {
|
||||
SILTransform *createPredictableMemoryOptimizations();
|
||||
SILTransform *createConstantPropagation();
|
||||
SILTransform *createDCE();
|
||||
SILTransform *createMandatoryInlining();
|
||||
SILTransform *createMandatoryInlining(SILModule::LinkingMode Mode,
|
||||
bool ShouldCleanup=true);
|
||||
SILTransform *createSILCleanup();
|
||||
SILTransform *createEmitDFDiagnostics();
|
||||
|
||||
|
||||
@@ -647,6 +647,7 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args,
|
||||
Opts.VerifyAll = Args.hasArg(OPT_sil_verify_all);
|
||||
Opts.PrintAll = Args.hasArg(OPT_sil_print_all);
|
||||
Opts.TimeTransforms = Args.hasArg(OPT_sil_time_transforms);
|
||||
Opts.DebugSerialization = Args.hasArg(OPT_sil_debug_serialization);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -16,8 +16,12 @@
|
||||
#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"
|
||||
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
|
||||
@@ -243,3 +247,82 @@ const BuiltinInfo &SILModule::getBuiltinInfo(Identifier ID) {
|
||||
|
||||
return Info;
|
||||
}
|
||||
|
||||
bool SILModule::linkFunction(SILFunction *Fun, SILModule::LinkingMode Mode) {
|
||||
// If we are not linking anything bail.
|
||||
if (Mode == LinkingMode::LinkNone)
|
||||
return nullptr;
|
||||
|
||||
bool LinkAll = Mode == LinkingMode::LinkAll;
|
||||
// First attempt to link in Fun. If we fail, bail.
|
||||
auto NewFn = SILLoader->lookupSILFunction(Fun);
|
||||
if (!NewFn)
|
||||
return false;
|
||||
++NumFuncLinked;
|
||||
|
||||
// Ok, we succeeded in linking in Fun. Transitively link in the functions that
|
||||
// Fun references.
|
||||
SmallVector<SILFunction *, 128> Worklist;
|
||||
Worklist.push_back(NewFn);
|
||||
while (!Worklist.empty()) {
|
||||
auto Fn = Worklist.pop_back_val();
|
||||
|
||||
for (auto &BB : *Fn)
|
||||
for (auto I = BB.begin(), E = BB.end(); I != E; I++) {
|
||||
SILFunction *CalleeFunction = nullptr;
|
||||
bool TryLinking = false;
|
||||
if (ApplyInst *AI = dyn_cast<ApplyInst>(I)) {
|
||||
SILValue Callee = AI->getCallee();
|
||||
// Handles FunctionRefInst only.
|
||||
if (FunctionRefInst *FRI = dyn_cast<FunctionRefInst>(Callee.getDef())) {
|
||||
CalleeFunction = FRI->getReferencedFunction();
|
||||
// When EnableLinkAll is true, we always link the Callee.
|
||||
TryLinking = LinkAll || AI->isTransparent() ||
|
||||
CalleeFunction->getLinkage() == SILLinkage::Shared;
|
||||
}
|
||||
} else if (PartialApplyInst *PAI = dyn_cast<PartialApplyInst>(I)) {
|
||||
SILValue Callee = PAI->getCallee();
|
||||
// Handles FunctionRefInst only.
|
||||
if (FunctionRefInst *FRI = dyn_cast<FunctionRefInst>(Callee.getDef())) {
|
||||
CalleeFunction = FRI->getReferencedFunction();
|
||||
// When EnableLinkAll is true, we always link the Callee.
|
||||
TryLinking = LinkAll || CalleeFunction->isTransparent() ||
|
||||
CalleeFunction->getLinkage() == SILLinkage::Shared;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else if (FunctionRefInst *FRI = dyn_cast<FunctionRefInst>(I)) {
|
||||
// When EnableLinkAll is true, we link the function referenced by
|
||||
// FunctionRefInst.
|
||||
CalleeFunction = LinkAll ? FRI->getReferencedFunction() :
|
||||
nullptr;
|
||||
TryLinking = LinkAll;
|
||||
}
|
||||
|
||||
if (!CalleeFunction)
|
||||
continue;
|
||||
|
||||
// The ExternalSource may wish to rewrite non-empty bodies.
|
||||
if (ExternalSource)
|
||||
if (auto NewFn = ExternalSource->lookupSILFunction(CalleeFunction)) {
|
||||
Worklist.push_back(NewFn);
|
||||
++NumFuncLinked;
|
||||
continue;
|
||||
}
|
||||
|
||||
CalleeFunction->setBare(IsBare);
|
||||
|
||||
if (CalleeFunction->empty())
|
||||
// Try to find the definition in a serialized module when callee is
|
||||
// currently empty.
|
||||
if (TryLinking)
|
||||
if (auto NewFn = SILLoader->lookupSILFunction(CalleeFunction)) {
|
||||
Worklist.push_back(NewFn);
|
||||
++NumFuncLinked;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -201,7 +201,8 @@ cleanupCalleeValue(SILValue CalleeValue, ArrayRef<SILValue> CaptureArgs,
|
||||
static SILFunction *
|
||||
getCalleeFunction(ApplyInst* AI, bool &IsThick,
|
||||
SmallVectorImpl<SILValue>& CaptureArgs,
|
||||
SmallVectorImpl<SILValue>& FullArgs) {
|
||||
SmallVectorImpl<SILValue>& FullArgs,
|
||||
SILModule::LinkingMode Mode) {
|
||||
if (!AI->isTransparent())
|
||||
return nullptr;
|
||||
|
||||
@@ -278,10 +279,17 @@ getCalleeFunction(ApplyInst* AI, bool &IsThick,
|
||||
assert(CalleeValue.getResultNumber() == 0);
|
||||
|
||||
SILFunction *CalleeFunction = FRI->getReferencedFunction();
|
||||
if (!CalleeFunction || CalleeFunction->empty() ||
|
||||
|
||||
if (!CalleeFunction ||
|
||||
(CalleeFunction->getAbstractCC() != AbstractCC::Freestanding &&
|
||||
CalleeFunction->getAbstractCC() != AbstractCC::Method))
|
||||
return nullptr;
|
||||
|
||||
// If CalleeFunction is a declaration, see if we can load it. If we fail to
|
||||
// load it, bail.
|
||||
if (CalleeFunction->empty() && !AI->getModule().linkFunction(CalleeFunction,
|
||||
Mode))
|
||||
return nullptr;
|
||||
return CalleeFunction;
|
||||
}
|
||||
|
||||
@@ -301,6 +309,7 @@ getCalleeFunction(ApplyInst* AI, bool &IsThick,
|
||||
/// \returns true if successful, false if failed due to circular inlining.
|
||||
static bool
|
||||
runOnFunctionRecursively(SILFunction *F, ApplyInst* AI,
|
||||
SILModule::LinkingMode Mode,
|
||||
DenseFunctionSet &FullyInlinedSet,
|
||||
ImmutableFunctionSet::Factory &SetFactory,
|
||||
ImmutableFunctionSet CurrentInliningSet) {
|
||||
@@ -318,7 +327,7 @@ runOnFunctionRecursively(SILFunction *F, ApplyInst* AI,
|
||||
diag::circular_transparent);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Add to the current inlining set (immutably, so we only affect the set
|
||||
// during this call and recursive subcalls).
|
||||
CurrentInliningSet = SetFactory.add(CurrentInliningSet, F);
|
||||
@@ -339,15 +348,17 @@ runOnFunctionRecursively(SILFunction *F, ApplyInst* AI,
|
||||
SILValue CalleeValue = InnerAI->getCallee();
|
||||
bool IsThick;
|
||||
SILFunction *CalleeFunction = getCalleeFunction(InnerAI, IsThick,
|
||||
CaptureArgs, FullArgs);
|
||||
CaptureArgs, FullArgs,
|
||||
Mode);
|
||||
if (!CalleeFunction) {
|
||||
++I;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Then recursively process it first before trying to inline it.
|
||||
if (!runOnFunctionRecursively(CalleeFunction, InnerAI, FullyInlinedSet,
|
||||
SetFactory, CurrentInliningSet)) {
|
||||
if (!runOnFunctionRecursively(CalleeFunction, InnerAI, Mode,
|
||||
FullyInlinedSet, SetFactory,
|
||||
CurrentInliningSet)) {
|
||||
// If we failed due to circular inlining, then emit some notes to
|
||||
// trace back the failure if we have more information.
|
||||
// FIXME: possibly it could be worth recovering and attempting other
|
||||
@@ -363,7 +374,7 @@ runOnFunctionRecursively(SILFunction *F, ApplyInst* AI,
|
||||
}
|
||||
|
||||
auto *ApplyBlock = InnerAI->getParent();
|
||||
|
||||
|
||||
// Inline function at I, which also changes I to refer to the first
|
||||
// instruction inlined in the case that it succeeds. We purposely
|
||||
// process the inlined body after inlining, because the inlining may
|
||||
@@ -420,52 +431,60 @@ runOnFunctionRecursively(SILFunction *F, ApplyInst* AI,
|
||||
// Top Level Driver
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
static void performSILMandatoryInlining(SILModule *M) {
|
||||
DenseFunctionSet FullyInlinedSet;
|
||||
ImmutableFunctionSet::Factory SetFactory;
|
||||
for (auto &F : *M)
|
||||
runOnFunctionRecursively(&F, nullptr, FullyInlinedSet, SetFactory,
|
||||
SetFactory.getEmptySet());
|
||||
|
||||
// Now that we've inlined some functions, clean up. If there are any
|
||||
// transparent functions that are deserialized from another module that are
|
||||
// now unused, just remove them from the module.
|
||||
//
|
||||
// We do this with a simple linear scan, because transparent functions that
|
||||
// reference each other have already been flattened.
|
||||
for (auto FI = M->begin(), E = M->end(); FI != E; ) {
|
||||
SILFunction &F = *FI++;
|
||||
|
||||
if (F.getRefCount() != 0) continue;
|
||||
|
||||
// We can always remove transparent functions. We can also remove functions
|
||||
// that came from closures.
|
||||
if (!F.isTransparent() &&
|
||||
(!F.hasLocation() || !F.getLocation().isASTNode<Expr>() ||
|
||||
!F.getLocation().isASTNode<AbstractClosureExpr>()))
|
||||
continue;
|
||||
|
||||
// We discard functions that don't have external linkage, e.g. deserialized
|
||||
// functions, internal functions, and thunks. Being marked transparent
|
||||
// controls this.
|
||||
if (isPossiblyUsedExternally(F.getLinkage())) continue;
|
||||
|
||||
// Okay, just erase the function from the module.
|
||||
M->getFunctionList().erase(&F);
|
||||
}
|
||||
}
|
||||
|
||||
class MandatoryInlining : public SILModuleTransform {
|
||||
|
||||
SILModule::LinkingMode Mode;
|
||||
bool ShouldCleanup;
|
||||
public:
|
||||
MandatoryInlining(SILModule::LinkingMode M, bool C) : SILModuleTransform(),
|
||||
Mode(M),
|
||||
ShouldCleanup(C) {}
|
||||
private:
|
||||
/// The entry point to the transformation.
|
||||
void run() {
|
||||
performSILMandatoryInlining(getModule());
|
||||
SILModule *M = getModule();
|
||||
DenseFunctionSet FullyInlinedSet;
|
||||
ImmutableFunctionSet::Factory SetFactory;
|
||||
for (auto &F : *M)
|
||||
runOnFunctionRecursively(&F, nullptr, Mode, FullyInlinedSet, SetFactory,
|
||||
SetFactory.getEmptySet());
|
||||
|
||||
if (!ShouldCleanup)
|
||||
return;
|
||||
|
||||
// Now that we've inlined some functions, clean up. If there are any
|
||||
// transparent functions that are deserialized from another module that are
|
||||
// now unused, just remove them from the module.
|
||||
//
|
||||
// We do this with a simple linear scan, because transparent functions that
|
||||
// reference each other have already been flattened.
|
||||
for (auto FI = M->begin(), E = M->end(); FI != E; ) {
|
||||
SILFunction &F = *FI++;
|
||||
|
||||
if (F.getRefCount() != 0) continue;
|
||||
|
||||
// We can always remove transparent functions. We can also remove functions
|
||||
// that came from closures.
|
||||
if (!F.isTransparent() &&
|
||||
(!F.hasLocation() || !F.getLocation().isASTNode<Expr>() ||
|
||||
!F.getLocation().isASTNode<AbstractClosureExpr>()))
|
||||
continue;
|
||||
|
||||
// We discard functions that don't have external linkage, e.g. deserialized
|
||||
// functions, internal functions, and thunks. Being marked transparent
|
||||
// controls this.
|
||||
if (isPossiblyUsedExternally(F.getLinkage())) continue;
|
||||
|
||||
// Okay, just erase the function from the module.
|
||||
M->getFunctionList().erase(&F);
|
||||
}
|
||||
|
||||
invalidateAnalysis(SILAnalysis::InvalidationKind::All);
|
||||
}
|
||||
|
||||
StringRef getName() override { return "Mandatory Inlining"; }
|
||||
};
|
||||
|
||||
SILTransform *swift::createMandatoryInlining() {
|
||||
return new MandatoryInlining();
|
||||
SILTransform *swift::createMandatoryInlining(SILModule::LinkingMode Mode,
|
||||
bool ShouldCleanup) {
|
||||
return new MandatoryInlining(Mode, ShouldCleanup);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,18 @@ bool swift::runSILDiagnosticPasses(SILModule &Module,
|
||||
PM.registerAnalysis(createCallGraphAnalysis(&Module));
|
||||
PM.registerAnalysis(createAliasAnalysis(&Module));
|
||||
PM.registerAnalysis(createDominanceAnalysis(&Module));
|
||||
PM.add(createMandatoryInlining());
|
||||
|
||||
// If we are asked do debug serialization, instead of running all diagnostic
|
||||
// passes, just run mandatory inlining with dead transparent function cleanup
|
||||
// disabled.
|
||||
PM.add(createMandatoryInlining(Options.LinkMode,
|
||||
!Options.DebugSerialization/*ShouldCleanup*/));
|
||||
if (Options.DebugSerialization) {
|
||||
PM.run();
|
||||
return Ctx.hadError();
|
||||
}
|
||||
|
||||
// Otherwise run the rest of diagnostics.
|
||||
PM.add(createCapturePromotion());
|
||||
PM.add(createAllocBoxToStack());
|
||||
PM.add(createInOutDeshadowing());
|
||||
|
||||
@@ -141,10 +141,6 @@ static bool performCompile(CompilerInstance &Instance,
|
||||
SM = performSILGeneration(*PrimarySourceFile);
|
||||
else
|
||||
SM = performSILGeneration(Instance.getMainModule());
|
||||
|
||||
// Link in transparent functions.
|
||||
if (Invocation.getSILOptions().LinkMode > SILOptions::LinkNone)
|
||||
performSILLinking(SM.get(), false);
|
||||
}
|
||||
|
||||
// We've been told to emit SIL after SILGen, so write it now.
|
||||
|
||||
@@ -323,7 +323,7 @@ int main(int argc, char **argv) {
|
||||
PM.add(createInOutDeshadowing());
|
||||
break;
|
||||
case PassKind::MandatoryInlining:
|
||||
PM.add(createMandatoryInlining());
|
||||
PM.add(createMandatoryInlining(SILOpts.LinkMode));
|
||||
break;
|
||||
case PassKind::PredictableMemoryOpt:
|
||||
PM.add(createPredictableMemoryOptimizations());
|
||||
|
||||
Reference in New Issue
Block a user