Make transparent inlining apply transparency recursively to all calls and also see through applications of partial applications (in SSA form); this is intended to ensure that [auto_closure] arguments of [transparent] functions get inlined, although that's additionally blocked by (apparent?) incompleteness of SSA-ification passes currently and phase ordering issues.

After implementing this I realized that a lot of the logic currently in MandatoryInlining.cpp should be moved into the SILInliner so it can be reused in an optimizing inliner. I plan on doing that refactoring immediately but decided to go ahead and commit this since it's a working incremental step.

Swift SVN r7771
This commit is contained in:
Stephen Lin
2013-08-30 00:25:24 +00:00
parent 1da2dc442e
commit 3828b616d8
5 changed files with 242 additions and 144 deletions

View File

@@ -24,7 +24,7 @@ using namespace swift;
typedef llvm::DenseSet<SILFunction*> DenseFunctionSet;
typedef llvm::ImmutableSet<SILFunction*> ImmutableFunctionSet;
STATISTIC(NumMandatoryInlineSitesInlined,
STATISTIC(NumMandatoryInlines,
"Number of function application sites inlined by the mandatory "
"inlining pass");
@@ -35,6 +35,51 @@ static void diagnose(ASTContext &Context, SourceLoc loc, Diag<T...> diag,
diag, std::forward<U>(args)...);
}
/// \brief Returns the callee SILFunction called at a call site, in the case
/// that the call is transparent (as in, both that the call is marked
/// with the transparent flag and that callee function is actually transparently
/// determinable from the SIL) or nullptr otherwise. This assumes that the SIL
/// is already in SSA form.
///
/// In the case that a non-null value is returned, Args contains effective
/// argument operands for the callee function and PossiblyDeadInsts contains
/// instructions that might be dead after inlining and removal of any dead
/// instructions earlier in the vector.
static SILFunction *
getCalleeFunction(ApplyInst* AI, SmallVectorImpl<SILValue>& Args,
SmallVectorImpl<SILInstruction*>& PossiblyDeadInsts) {
if (!AI->isTransparent())
return nullptr;
Args.clear();
PossiblyDeadInsts.clear();
for (const auto &Arg : AI->getArguments())
Args.push_back(Arg);
SILValue Callee = AI->getCallee();
while (PartialApplyInst *PAI = dyn_cast<PartialApplyInst>(Callee.getDef())) {
assert(Callee.getResultNumber() == 0);
for (const auto &Arg : PAI->getArguments())
Args.push_back(Arg);
Callee = PAI->getCallee();
PossiblyDeadInsts.push_back(PAI);
}
FunctionRefInst *FRI = dyn_cast<FunctionRefInst>(Callee.getDef());
if (!FRI)
return nullptr;
assert(Callee.getResultNumber() == 0);
SILFunction *CalleeFunction = FRI->getFunction();
if (!CalleeFunction || CalleeFunction->empty())
return nullptr;
PossiblyDeadInsts.push_back(FRI);
return CalleeFunction;
}
/// \brief Inlines all mandatory inlined functions into the body of a function,
/// first recursively inlining all mandatory apply instructions in those
/// functions into their bodies if necessary.
@@ -57,7 +102,7 @@ runOnFunctionRecursively(SILFunction *F, ApplyInst* AI,
// Avoid reprocessing functions needlessly
if (FullyInlinedSet.find(F) != FullyInlinedSet.end())
return true;
// Prevent attempt to circularly inline.
if (CurrentInliningSet.contains(F)) {
// This cannot happen on a top-level call, so AI should be non-null.
@@ -73,53 +118,65 @@ runOnFunctionRecursively(SILFunction *F, ApplyInst* AI,
// during this call and recursive subcalls).
CurrentInliningSet = SetFactory.add(CurrentInliningSet, F);
SmallVector<ApplyInst*, 4> ApplySites;
for (auto &BB : *F) {
for (auto &I : BB) {
ApplyInst *InnerAI;
if ((InnerAI = dyn_cast<ApplyInst>(&I)) && InnerAI->isTransparent()) {
// Figure out of this is something we have the body for
// FIXME: once fragile SIL is serialized in modules, these can be
// asserts, since transparent inline functions should always have their
// bodies available.
SILValue Callee = InnerAI->getCallee();
FunctionRefInst *FRI = dyn_cast<FunctionRefInst>(Callee.getDef());
if (!FRI)
continue;
assert(Callee.getResultNumber() == 0);
SILFunction *CalledFunc = FRI->getFunction();
if (!CalledFunc || CalledFunc->empty())
continue;
// Then recursively process it first before trying to inline it.
if (!runOnFunctionRecursively(CalledFunc, InnerAI, 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
// inlines within this same recursive call rather than simply
// propogating the failure.
if (AI) {
SILLocation L = AI->getLoc();
assert(L && "Must have location for transparent inline apply");
diagnose(F->getModule().getASTContext(), L.getStartSourceLoc(),
diag::note_while_inlining);
}
return false;
}
ApplySites.push_back(InnerAI);
SmallVector<SILValue, 16> Args;
SmallVector<SILInstruction*, 16> PossiblyDeadInsts;
SILInliner Inliner(*F, /*MakeTransparent=*/true);
for (auto FI = F->begin(), FE = F->end(); FI != FE; ++FI) {
auto I = FI->begin(), E = FI->end();
while (I != E) {
ApplyInst *InnerAI = dyn_cast<ApplyInst>(I);
if (!InnerAI) {
++I;
continue;
}
}
}
// Do the inlining separately from the inspection loop to avoid iterator
// invalidation issues.
if (!ApplySites.empty()) {
SILInliner Inliner(*F);
for (auto *InnerAI : ApplySites) {
Inliner.inlineFunction(InnerAI);
++NumMandatoryInlineSitesInlined;
SILFunction *CalleeFunction = getCalleeFunction(InnerAI, Args,
PossiblyDeadInsts);
if (!CalleeFunction) {
++I;
continue;
}
// Then recursively process it first before trying to inline it.
if (!runOnFunctionRecursively(CalleeFunction, InnerAI, 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
// inlines within this same recursive call rather than simply
// propogating the failure.
if (AI) {
SILLocation L = AI->getLoc();
assert(L && "Must have location for transparent inline apply");
diagnose(F->getModule().getASTContext(), L.getStartSourceLoc(),
diag::note_while_inlining);
}
return false;
}
// 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
// have exposed new inlining opportunities beyond those present in
// the inlined function when processed independently
if (!Inliner.inlineFunction(I, CalleeFunction, Args)) {
// If inlining failed, then I is left unchanged, so increment it
// before continuing rather than process the same apply instruction
// twice
++I;
continue;
}
// Reposition iterators possibly invalidated by mutation
FI = SILFunction::iterator(I->getParent());
FE = F->end();
E = FI->end();
for (SILInstruction *PossiblyDeadInst : PossiblyDeadInsts)
if (PossiblyDeadInst->use_empty())
PossiblyDeadInst->eraseFromParent();
++NumMandatoryInlines;
}
}