mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Enable SIL parsing and SIL serialization of semantics. We add one more field to SILFunctionLayout for semantics. We should refactor handling of attributes at SIL level, right now they are in SILFunction as bool or std::string and in SIL serializer as a 1-bit field or an ID field. rdar://17525564 Swift SVN r19434
750 lines
27 KiB
C++
750 lines
27 KiB
C++
//===--- AllocBoxToStack.cpp - Promote alloc_box to alloc_stack -----------===//
|
|
//
|
|
// 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 "allocbox-to-stack"
|
|
#include "swift/SILPasses/Passes.h"
|
|
#include "swift/SIL/Dominance.h"
|
|
#include "swift/SIL/SILArgument.h"
|
|
#include "swift/SIL/SILBuilder.h"
|
|
#include "swift/SIL/SILCloner.h"
|
|
#include "swift/SILPasses/Transforms.h"
|
|
#include "llvm/ADT/DenseMap.h"
|
|
#include "llvm/ADT/SmallPtrSet.h"
|
|
#include "llvm/ADT/SmallSet.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/Statistic.h"
|
|
#include "llvm/Support/Debug.h"
|
|
using namespace swift;
|
|
|
|
STATISTIC(NumStackPromoted, "Number of alloc_box's promoted to the stack");
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// alloc_box Promotion
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
typedef llvm::SmallSet<unsigned int, 4> ParamIndexSet;
|
|
|
|
static SILInstruction* findUnexpectedBoxUse(SILValue Box,
|
|
bool examinePartialApply,
|
|
bool inAppliedFunction,
|
|
llvm::SmallVectorImpl<Operand*> &);
|
|
static bool operandEscapesApply(Operand *O);
|
|
|
|
// Propagate liveness backwards from an initial set of blocks in our
|
|
// LiveIn set.
|
|
static void propagateLiveness(llvm::SmallPtrSetImpl<SILBasicBlock*> &LiveIn,
|
|
SILBasicBlock *DefBB) {
|
|
|
|
// First populate a worklist of predecessors.
|
|
llvm::SmallVector<SILBasicBlock*, 64> Worklist;
|
|
for (auto *BB : LiveIn)
|
|
for (auto Pred : BB->getPreds())
|
|
Worklist.push_back(Pred);
|
|
|
|
// Now propagate liveness backwards until we hit the alloc_box.
|
|
while (!Worklist.empty()) {
|
|
auto *BB = Worklist.pop_back_val();
|
|
|
|
// If it's already in the set, then we've already queued and/or
|
|
// processed the predecessors.
|
|
if (BB == DefBB || !LiveIn.insert(BB))
|
|
continue;
|
|
|
|
for (auto Pred : BB->getPreds())
|
|
Worklist.push_back(Pred);
|
|
}
|
|
}
|
|
|
|
// Is any successor of BB in the LiveIn set?
|
|
static bool successorHasLiveIn(SILBasicBlock *BB,
|
|
llvm::SmallPtrSetImpl<SILBasicBlock*> &LiveIn) {
|
|
for (auto &Succ : BB->getSuccs())
|
|
if (LiveIn.count(Succ))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
// Walk backwards in BB looking for strong_release or dealloc_box of
|
|
// the given value, and add it to Releases.
|
|
static void addLastRelease(SILValue V, SILBasicBlock *BB,
|
|
llvm::SmallVectorImpl<SILInstruction*> &Releases) {
|
|
for (auto I = BB->rbegin(); I != BB->rend(); ++I) {
|
|
if (isa<StrongReleaseInst>(*I) || isa<DeallocBoxInst>(*I)) {
|
|
if (I->getOperand(0) != V)
|
|
continue;
|
|
|
|
Releases.push_back(&*I);
|
|
return;
|
|
}
|
|
assert((!isa<StrongRetainInst>(*I) || I->getOperand(0) != V) &&
|
|
"Did not expect retain after final release/dealloc!");
|
|
|
|
}
|
|
|
|
llvm_unreachable("Did not find release/dealloc!");
|
|
}
|
|
|
|
// Find the final releases of the alloc_box along any given path.
|
|
// These can include paths from a release back to the alloc_box in a
|
|
// loop.
|
|
static void getFinalReleases(AllocBoxInst *ABI,
|
|
llvm::SmallVectorImpl<SILInstruction*> &Releases) {
|
|
llvm::SmallPtrSet<SILBasicBlock*, 16> LiveIn;
|
|
llvm::SmallPtrSet<SILBasicBlock*, 16> UseBlocks;
|
|
|
|
auto *DefBB = ABI->getParent();
|
|
|
|
auto seenRelease = false;
|
|
SILInstruction *OneRelease = nullptr;
|
|
|
|
auto Box = ABI->getContainerResult();
|
|
|
|
// We'll treat this like a liveness problem where the alloc_box is
|
|
// the def. Each block that has a use of the owning pointer has the
|
|
// value live-in unless it is the block with the alloc_box.
|
|
for (auto UI : Box.getUses()) {
|
|
auto *User = UI->getUser();
|
|
auto *BB = User->getParent();
|
|
|
|
if (BB != DefBB)
|
|
LiveIn.insert(BB);
|
|
|
|
// Also keep track of the blocks with uses.
|
|
UseBlocks.insert(BB);
|
|
|
|
// Try to speed up the trivial case of single release/dealloc.
|
|
if (isa<StrongReleaseInst>(User) || isa<DeallocBoxInst>(User)) {
|
|
if (!seenRelease)
|
|
OneRelease = User;
|
|
else
|
|
OneRelease = nullptr;
|
|
|
|
seenRelease = true;
|
|
}
|
|
}
|
|
|
|
// Only a single release/dealloc? We're done!
|
|
if (OneRelease) {
|
|
Releases.push_back(OneRelease);
|
|
return;
|
|
}
|
|
|
|
propagateLiveness(LiveIn, DefBB);
|
|
|
|
// Now examine each block we saw a use in. If it has no successors
|
|
// that are in LiveIn, then the last use in the block is the final
|
|
// release/dealloc.
|
|
for (auto *BB : UseBlocks)
|
|
if (!successorHasLiveIn(BB, LiveIn))
|
|
addLastRelease(Box, BB, Releases);
|
|
}
|
|
|
|
/// \brief Returns True if the operand or one of its users is captured.
|
|
static bool useCaptured(Operand *UI) {
|
|
auto *User = UI->getUser();
|
|
|
|
// These instructions do not cause the address to escape.
|
|
if (isa<CopyAddrInst>(User) ||
|
|
isa<LoadInst>(User) ||
|
|
isa<ProtocolMethodInst>(User) ||
|
|
isa<DebugValueInst>(User) ||
|
|
isa<DebugValueAddrInst>(User) ||
|
|
isa<StrongReleaseInst>(User) ||
|
|
isa<StrongRetainInst>(User) ||
|
|
isa<DeallocBoxInst>(User))
|
|
return false;
|
|
|
|
if (auto *Store = dyn_cast<StoreInst>(User)) {
|
|
if (Store->getDest() == UI->get())
|
|
return false;
|
|
} else if (auto *Assign = dyn_cast<AssignInst>(User)) {
|
|
if (Assign->getDest() == UI->get())
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool canValueEscape(SILValue V, bool examineApply) {
|
|
for (auto UI : V.getUses()) {
|
|
auto *User = UI->getUser();
|
|
|
|
// These instructions do not cause the address to escape.
|
|
if (!useCaptured(UI))
|
|
continue;
|
|
|
|
// These instructions only cause the value to escape if they are used in
|
|
// a way that escapes. Recursively check that the uses of the instruction
|
|
// don't escape and collect all of the uses of the value.
|
|
if (isa<StructElementAddrInst>(User) || isa<TupleElementAddrInst>(User) ||
|
|
isa<ProjectExistentialInst>(User) || isa<OpenExistentialInst>(User) ||
|
|
isa<MarkUninitializedInst>(User) || isa<AddressToPointerInst>(User) ||
|
|
isa<PointerToAddressInst>(User)) {
|
|
if (canValueEscape(User, examineApply))
|
|
return true;
|
|
continue;
|
|
}
|
|
|
|
if (auto apply = dyn_cast<ApplyInst>(User)) {
|
|
// Applying a function does not cause the function to escape.
|
|
if (UI->getOperandNumber() == 0)
|
|
continue;
|
|
|
|
// apply instructions do not capture the pointer when it is passed
|
|
// indirectly
|
|
if (apply->getSubstCalleeType()
|
|
->getInterfaceParameters()[UI->getOperandNumber()-1].isIndirect())
|
|
continue;
|
|
|
|
// Optionally drill down into an apply to see if the operand is
|
|
// captured in or returned from the apply.
|
|
if (examineApply && !operandEscapesApply(UI))
|
|
continue;
|
|
}
|
|
|
|
// partial_apply instructions do not allow the pointer to escape
|
|
// when it is passed indirectly, unless the partial_apply itself
|
|
// escapes
|
|
if (auto partialApply = dyn_cast<PartialApplyInst>(User)) {
|
|
auto args = partialApply->getArguments();
|
|
auto params = partialApply->getSubstCalleeType()
|
|
->getInterfaceParameters();
|
|
params = params.slice(params.size() - args.size(), args.size());
|
|
if (params[UI->getOperandNumber()-1].isIndirect()) {
|
|
if (canValueEscape(User, /*examineApply = */ true))
|
|
return true;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/// Given an apply or partial_apply, return the direct callee or
|
|
/// nullptr if this is not a direct call.
|
|
static FunctionRefInst *getDirectCallee(SILInstruction *Call) {
|
|
if (auto *Apply = dyn_cast<ApplyInst>(Call))
|
|
return dyn_cast<FunctionRefInst>(Apply->getCallee());
|
|
else
|
|
return dyn_cast<FunctionRefInst>(cast<PartialApplyInst>(Call)->getCallee());
|
|
}
|
|
|
|
/// Given an operand of a direct apply or partial_apply of a function,
|
|
/// return the index of the parameter used in the body of the function
|
|
/// to represent this operand.
|
|
static size_t getParameterIndexForOperand(Operand *O) {
|
|
assert(isa<ApplyInst>(O->getUser()) || isa<PartialApplyInst>(O->getUser()) &&
|
|
"Expected apply or partial_apply!");
|
|
|
|
CanSILFunctionType Type;
|
|
size_t ArgCount;
|
|
if (auto *Apply = dyn_cast<ApplyInst>(O->getUser())) {
|
|
Type = Apply->getSubstCalleeType();
|
|
ArgCount = Apply->getArguments().size();
|
|
assert(Type->getInterfaceParameters().size() == ArgCount &&
|
|
"Expected all arguments to be supplied!");
|
|
} else {
|
|
auto *PartialApply = cast<PartialApplyInst>(O->getUser());
|
|
Type = PartialApply->getSubstCalleeType();
|
|
ArgCount = PartialApply->getArguments().size();
|
|
}
|
|
|
|
size_t ParamCount = Type->getInterfaceParameters().size();
|
|
assert(ParamCount >= ArgCount && "Expected fewer arguments to function!");
|
|
|
|
auto OperandIndex = O->getOperandNumber();
|
|
assert(OperandIndex != 0 && "Operand cannot be the applied function!");
|
|
|
|
// The applied function is the first operand.
|
|
auto ParamIndex = (ParamCount - ArgCount) + OperandIndex - 1;
|
|
|
|
return ParamIndex;
|
|
}
|
|
|
|
/// Given an operand of a direct apply or partial_apply of a function,
|
|
/// return the parameter used in the body of the function to represent
|
|
/// this operand. For non-direct calls and for functions that we
|
|
/// cannot examine the body of, return an empty SILValue.
|
|
static SILValue getParameterForOperand(SILFunction *F, Operand *O) {
|
|
assert(F && !F->empty() && "Expected a function with a body!");
|
|
|
|
auto &Entry = F->front();
|
|
size_t ParamIndex = getParameterIndexForOperand(O);
|
|
|
|
auto *Param = Entry.getBBArg(ParamIndex);
|
|
return SILValue(Param);
|
|
}
|
|
|
|
/// Return a pointer to the SILFunction called by Call if we can
|
|
/// determine which funciton that is, and we have a body for that
|
|
/// function. Otherwise return nullptr.
|
|
static SILFunction *getFunctionBody(SILInstruction *Call) {
|
|
if (auto *FRI = getDirectCallee(Call))
|
|
if (auto *F = FRI->getReferencedFunction())
|
|
if (!F->empty())
|
|
return F;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/// Could this operand to an apply escape that function by being
|
|
/// stored or returned?
|
|
static bool operandEscapesApply(Operand *O) {
|
|
SILFunction *F = getFunctionBody(O->getUser());
|
|
// If we cannot examine the function body, assume the worst.
|
|
if (!F)
|
|
return true;
|
|
|
|
// Check the uses of the operand, but do not recurse down into other
|
|
// apply instructions.
|
|
auto Param = getParameterForOperand(F, O);
|
|
return canValueEscape(Param, /* examineApply = */ false);
|
|
}
|
|
|
|
/// checkPartialApplyBody - Check the body of a partial apply to see
|
|
/// if the box pointer argument passed to it has uses that would
|
|
/// disqualify it from being protmoted to a stack location. Return
|
|
/// true if this partial apply will not block our promoting the box.
|
|
static bool checkPartialApplyBody(Operand *O) {
|
|
SILFunction *F = getFunctionBody(O->getUser());
|
|
// If we cannot examine the function body, assume the worst.
|
|
if (!F)
|
|
return false;
|
|
|
|
// We don't actually use these because we're not recursively
|
|
// rewriting the partial applies we find.
|
|
llvm::SmallVector<Operand *, 1> ElidedOperands;
|
|
auto Param = getParameterForOperand(F, O);
|
|
return !findUnexpectedBoxUse(Param, /* examinePartialApply = */ false,
|
|
/* inAppliedFunction = */ true,
|
|
ElidedOperands);
|
|
}
|
|
|
|
|
|
/// findUnexpectedBoxUse - Validate that the uses of a pointer to a
|
|
/// box do not eliminate it from consideration for promotion to a
|
|
/// stack element. Optionally examine the body of partial_apply
|
|
/// to see if there is an unexpected use inside. Return the
|
|
/// instruction with the unexpected use if we find one.
|
|
static SILInstruction* findUnexpectedBoxUse(SILValue Box,
|
|
bool examinePartialApply,
|
|
bool inAppliedFunction,
|
|
llvm::SmallVectorImpl<Operand *> &ElidedOperands) {
|
|
assert((Box.getType() ==
|
|
SILType::getNativeObjectType(Box.getType().getASTContext())) &&
|
|
"Expected an object pointer!");
|
|
|
|
llvm::SmallVector<Operand *, 4> LocalElidedOperands;
|
|
|
|
// Scan all of the uses of the retain count value, collecting all
|
|
// the releases and validating that we don't have an unexpected
|
|
// user.
|
|
for (auto UI : Box.getUses()) {
|
|
auto *User = UI->getUser();
|
|
|
|
// Retains and releases are fine. Deallocs are fine if we're not
|
|
// examining a function that the alloc_box was passed into.
|
|
if (isa<StrongRetainInst>(User) || isa<StrongReleaseInst>(User) ||
|
|
(!inAppliedFunction && isa<DeallocBoxInst>(User)))
|
|
continue;
|
|
|
|
// For partial_apply, if we've been asked to examine the body, the
|
|
// uses of the argument are okay there, and the partial_apply
|
|
// itself cannot escape, then everything is fine.
|
|
if (auto *PAI = dyn_cast<PartialApplyInst>(User))
|
|
if (examinePartialApply && checkPartialApplyBody(UI) &&
|
|
!canValueEscape(PAI, /* examineApply = */ true)) {
|
|
LocalElidedOperands.push_back(UI);
|
|
continue;
|
|
}
|
|
|
|
return User;
|
|
}
|
|
|
|
ElidedOperands.append(LocalElidedOperands.begin(), LocalElidedOperands.end());
|
|
return nullptr;
|
|
}
|
|
|
|
/// canPromoteAllocBox - Can we promote this alloc_box to an alloc_stack?
|
|
static bool canPromoteAllocBox(AllocBoxInst *ABI,
|
|
llvm::SmallVectorImpl<Operand *> &ElidedOperands) {
|
|
// Scan all of the uses of the address of the box to see if any
|
|
// disqualifies the box from being promoted tot he stack.
|
|
if (auto *User = findUnexpectedBoxUse(ABI->getContainerResult(),
|
|
/* examinePartialApply = */ true,
|
|
/* inAppliedFunction = */ false,
|
|
ElidedOperands)) {
|
|
// Otherwise, we have an unexpected use.
|
|
DEBUG(llvm::dbgs() << "*** Failed to promote alloc_box in @"
|
|
<< ABI->getFunction()->getName() << ": " << *ABI
|
|
<< " Due to user: " << *User << "\n");
|
|
|
|
return false;
|
|
}
|
|
|
|
// Okay, it looks like this value doesn't escape.
|
|
return true;
|
|
}
|
|
|
|
/// rewriteAllocBoxAsAllocStack - Replace uses of the alloc_box with a
|
|
/// new alloc_stack, but do not delete the alloc_box yet.
|
|
static void rewriteAllocBoxAsAllocStack(AllocBoxInst *ABI,
|
|
llvm::SmallVectorImpl<TermInst *> &Returns) {
|
|
DEBUG(llvm::dbgs() << "*** Promoting alloc_box to stack: " << *ABI);
|
|
|
|
llvm::SmallVector<SILInstruction*, 4> FinalReleases;
|
|
getFinalReleases(ABI, FinalReleases);
|
|
|
|
// Promote this alloc_box to an alloc_stack. Insert the alloc_stack
|
|
// at the beginning of the funtion.
|
|
auto &Entry = ABI->getFunction()->front();
|
|
SILBuilder BuildAlloc(&Entry, Entry.begin());
|
|
auto *ASI = BuildAlloc.createAllocStack(ABI->getLoc(), ABI->getElementType());
|
|
ASI->setDebugScope(ABI->getDebugScope());
|
|
|
|
// Replace all uses of the address of the box's contained value with
|
|
// the address of the stack location.
|
|
ABI->getAddressResult().replaceAllUsesWith(ASI->getAddressResult());
|
|
|
|
// Check to see if the alloc_box was used by a mark_uninitialized instruction.
|
|
// If so, any uses of the pointer result need to keep using the MUI, not the
|
|
// alloc_stack directly. If we don't do this, DI will miss the uses.
|
|
SILValue PointerResult = ASI->getAddressResult();
|
|
for (auto UI : ASI->getAddressResult().getUses())
|
|
if (auto *MUI = dyn_cast<MarkUninitializedInst>(UI->getUser())) {
|
|
assert(ASI->getAddressResult().hasOneUse() &&
|
|
"alloc_stack used by mark_uninialized, but not exclusively!");
|
|
PointerResult = MUI;
|
|
break;
|
|
}
|
|
|
|
auto &Lowering = ABI->getModule().getTypeLowering(ABI->getElementType());
|
|
auto Loc = CleanupLocation::getCleanupLocation(ABI->getLoc());
|
|
|
|
// For non-trivial types, insert destroys for each final release-like
|
|
// instruction we found that isn't an explicit dealloc_box.
|
|
if (!Lowering.isTrivial()) {
|
|
for (auto LastRelease : FinalReleases) {
|
|
if (isa<DeallocBoxInst>(LastRelease))
|
|
continue;
|
|
|
|
SILBuilder BuildDestroy(LastRelease);
|
|
BuildDestroy.emitDestroyAddr(Loc, PointerResult);
|
|
}
|
|
}
|
|
|
|
for (auto Return : Returns) {
|
|
SILBuilder BuildDealloc(Return);
|
|
BuildDealloc.createDeallocStack(Loc, ASI->getContainerResult());
|
|
}
|
|
|
|
// Remove any retain and release instructions. Since all uses of result #1
|
|
// are gone, this only walks through uses of result #0 (the retain count
|
|
// pointer).
|
|
while (!ABI->use_empty()) {
|
|
auto *User = (*ABI->use_begin())->getUser();
|
|
assert(isa<StrongReleaseInst>(User) || isa<StrongRetainInst>(User) ||
|
|
isa<DeallocBoxInst>(User));
|
|
|
|
User->eraseFromParent();
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
/// \brief A SILCloner subclass which clones a closure function while
|
|
/// removing some of the parameters, which are either completely dead
|
|
/// or only used in retains and releases (which will not be cloned).
|
|
class DeadParamCloner : public SILClonerWithScopes<DeadParamCloner> {
|
|
public:
|
|
friend class SILVisitor<DeadParamCloner>;
|
|
friend class SILCloner<DeadParamCloner>;
|
|
|
|
DeadParamCloner(SILFunction *Orig, ParamIndexSet &DeadParamIndices);
|
|
|
|
void populateCloned();
|
|
|
|
SILFunction *getCloned() { return &getBuilder().getFunction(); }
|
|
|
|
private:
|
|
static SILFunction *initCloned(SILFunction *Orig,
|
|
ParamIndexSet &DeadParamIndices);
|
|
|
|
void visitStrongReleaseInst(StrongReleaseInst *Inst);
|
|
void visitStrongRetainInst(StrongRetainInst *Inst);
|
|
|
|
SILFunction *Orig;
|
|
ParamIndexSet &DeadParamIndices;
|
|
|
|
// The values in the original function that are either dead or only
|
|
// used in retains and releases.
|
|
llvm::SmallSet<SILValue, 4> DeadParameters;
|
|
};
|
|
} // end anonymous namespace.
|
|
|
|
DeadParamCloner::DeadParamCloner(SILFunction *Orig,
|
|
ParamIndexSet &DeadParamIndices)
|
|
: SILClonerWithScopes<DeadParamCloner>(*initCloned(Orig, DeadParamIndices)),
|
|
Orig(Orig), DeadParamIndices(DeadParamIndices) {
|
|
}
|
|
|
|
/// \brief Create the function corresponding to the clone of the
|
|
/// original closure with the signature modified to reflect removed
|
|
/// parameters (which are specified by DeadParamIndices).
|
|
SILFunction*
|
|
DeadParamCloner::initCloned(SILFunction *Orig,
|
|
ParamIndexSet &DeadParamIndices) {
|
|
SILModule &M = Orig->getModule();
|
|
|
|
// Suffix the function name with "_specializedX", where X is the first integer
|
|
// that does not result in a conflict.
|
|
// TODO: come up with a good mangling for this transformation and
|
|
// use shared linkage.
|
|
unsigned Counter = 0;
|
|
std::string ClonedName;
|
|
do {
|
|
ClonedName.clear();
|
|
llvm::raw_string_ostream buffer(ClonedName);
|
|
buffer << Orig->getName() << "_specialized" << Counter++;
|
|
} while (M.lookUpFunction(ClonedName));
|
|
|
|
SmallVector<SILParameterInfo, 4> ClonedInterfaceArgTys;
|
|
|
|
// Generate a new parameter list with deleted parameters removed.
|
|
SILFunctionType *OrigFTI = Orig->getLoweredFunctionType();
|
|
unsigned Index = 0;
|
|
for (auto ¶m : OrigFTI->getInterfaceParameters()) {
|
|
if (!DeadParamIndices.count(Index))
|
|
ClonedInterfaceArgTys.push_back(param);
|
|
++Index;
|
|
}
|
|
|
|
// Create the new function type for the cloned function with some of
|
|
// the parameters removed.
|
|
auto ClonedTy =
|
|
SILFunctionType::get(OrigFTI->getGenericSignature(),
|
|
OrigFTI->getExtInfo(),
|
|
OrigFTI->getCalleeConvention(),
|
|
ClonedInterfaceArgTys,
|
|
OrigFTI->getInterfaceResult(),
|
|
M.getASTContext());
|
|
|
|
assert((Orig->isTransparent() || Orig->isBare() || Orig->getLocation())
|
|
&& "SILFunction missing location");
|
|
assert((Orig->isTransparent() || Orig->isBare() || Orig->getDebugScope())
|
|
&& "SILFunction missing DebugScope");
|
|
assert(!Orig->isGlobalInit() && "Global initializer cannot be cloned");
|
|
auto Fn = SILFunction::create(M, Orig->getLinkage(), ClonedName, ClonedTy,
|
|
Orig->getContextGenericParams(),
|
|
Orig->getLocation(), Orig->isBare(),
|
|
IsNotTransparent, Orig->isNoinline(), Orig,
|
|
Orig->getDebugScope());
|
|
Fn->setSemanticsAttr(Orig->getSemanticsAttr());
|
|
return Fn;
|
|
}
|
|
|
|
/// \brief Populate the body of the cloned closure, modifying instructions as
|
|
/// necessary to take into consideration the removed parameters.
|
|
void
|
|
DeadParamCloner::populateCloned() {
|
|
SILFunction *Cloned = getCloned();
|
|
SILModule &M = Cloned->getModule();
|
|
|
|
// Create arguments for the entry block
|
|
SILBasicBlock *OrigEntryBB = Orig->begin();
|
|
SILBasicBlock *ClonedEntryBB = new (M) SILBasicBlock(Cloned);
|
|
unsigned ArgNo = 0;
|
|
auto I = OrigEntryBB->bbarg_begin(), E = OrigEntryBB->bbarg_end();
|
|
while (I != E) {
|
|
if (!DeadParamIndices.count(ArgNo)) {
|
|
// Create a new argument which copies the original argument.
|
|
SILValue MappedValue =
|
|
new (M) SILArgument((*I)->getType(), ClonedEntryBB, (*I)->getDecl());
|
|
ValueMap.insert(std::make_pair(*I, MappedValue));
|
|
} else {
|
|
DeadParameters.insert(SILValue(*I));
|
|
}
|
|
++ArgNo;
|
|
++I;
|
|
}
|
|
|
|
getBuilder().setInsertionPoint(ClonedEntryBB);
|
|
BBMap.insert(std::make_pair(OrigEntryBB, ClonedEntryBB));
|
|
// Recursively visit original BBs in depth-first preorder, starting with the
|
|
// entry block, cloning all instructions other than terminators.
|
|
visitSILBasicBlock(OrigEntryBB);
|
|
|
|
// Now iterate over the BBs and fix up the terminators.
|
|
for (auto BI = BBMap.begin(), BE = BBMap.end(); BI != BE; ++BI) {
|
|
getBuilder().setInsertionPoint(BI->second);
|
|
visit(BI->first->getTerminator());
|
|
}
|
|
}
|
|
|
|
/// \brief Handle a strong_release instruction during cloning of a closure; if
|
|
/// it is a strong release of a promoted box argument, then it is replaced wit
|
|
/// a ReleaseValue of the new object type argument, otherwise it is handled
|
|
/// normally.
|
|
void
|
|
DeadParamCloner::visitStrongReleaseInst(StrongReleaseInst *Inst) {
|
|
// If it's a release of a dead parameter, just drop the instruction.
|
|
if (DeadParameters.count(Inst->getOperand()))
|
|
return;
|
|
|
|
SILCloner<DeadParamCloner>::visitStrongReleaseInst(Inst);
|
|
}
|
|
|
|
void
|
|
DeadParamCloner::visitStrongRetainInst(StrongRetainInst *Inst) {
|
|
// If it's a retain of a dead parameter, just drop the instruction.
|
|
if (DeadParameters.count(Inst->getOperand()))
|
|
return;
|
|
|
|
SILCloner<DeadParamCloner>::visitStrongRetainInst(Inst);
|
|
}
|
|
|
|
/// Specialize a partial_apply by removing the parameters indicated by
|
|
/// indices. We expect these parameters to be either dead, or used
|
|
/// only by retains and releases.
|
|
static PartialApplyInst *specializePartialApply(PartialApplyInst *PartialApply,
|
|
ParamIndexSet &DeadParamIndices) {
|
|
auto *FRI = cast<FunctionRefInst>(PartialApply->getCallee());
|
|
assert(FRI && "Expected a direct partial_apply!");
|
|
auto *F = FRI->getReferencedFunction();
|
|
assert(F && "Expected a referenced function!");
|
|
|
|
// Clone the function the existing partial_apply references.
|
|
DeadParamCloner Cloner(F, DeadParamIndices);
|
|
Cloner.populateCloned();
|
|
|
|
// Now create the new partial_apply using the cloned function.
|
|
llvm::SmallVector<SILValue, 16> Args;
|
|
|
|
// Only use the arguments that are not dead.
|
|
for (auto &O : PartialApply->getArgumentOperands()) {
|
|
auto ParamIndex = getParameterIndexForOperand(&O);
|
|
if (!DeadParamIndices.count(ParamIndex))
|
|
Args.push_back(O.get());
|
|
}
|
|
|
|
// Build the function_ref and partial_apply.
|
|
SILBuilder Builder(PartialApply);
|
|
SILValue FunctionRef = Builder.createFunctionRef(PartialApply->getLoc(),
|
|
Cloner.getCloned());
|
|
CanSILFunctionType CanFnTy = Cloner.getCloned()->getLoweredFunctionType();
|
|
auto &M = PartialApply->getModule();
|
|
auto const &Subs = PartialApply->getSubstitutions();
|
|
CanSILFunctionType SubstCalleeTy = CanFnTy->substInterfaceGenericArgs(M,
|
|
M.getSwiftModule(),
|
|
Subs);
|
|
return Builder.createPartialApply(PartialApply->getLoc(), FunctionRef,
|
|
SILType::getPrimitiveObjectType(SubstCalleeTy),
|
|
PartialApply->getSubstitutions(), Args,
|
|
PartialApply->getType());
|
|
}
|
|
|
|
static void
|
|
rewritePartialApplies(llvm::SmallVectorImpl<Operand *> &ElidedOperands) {
|
|
llvm::DenseMap<PartialApplyInst *, ParamIndexSet> IndexMap;
|
|
ParamIndexSet Indices;
|
|
|
|
// Build a map from partial_apply to the indices of the operands
|
|
// that will not be in our rewritten version.
|
|
for (auto *O : ElidedOperands) {
|
|
auto ParamIndexNumber = getParameterIndexForOperand(O);
|
|
|
|
Indices.clear();
|
|
Indices.insert(ParamIndexNumber);
|
|
|
|
auto *PartialApply = cast<PartialApplyInst>(O->getUser());
|
|
llvm::DenseMap<PartialApplyInst *, ParamIndexSet>::iterator It;
|
|
bool Inserted;
|
|
std::tie(It, Inserted) = IndexMap.insert(std::make_pair(PartialApply,
|
|
Indices));
|
|
if (!Inserted)
|
|
It->second.insert(ParamIndexNumber);
|
|
}
|
|
|
|
// Clone the referenced function of each partial_apply, removing the
|
|
// operands that we will not need, and remove the existing
|
|
// partial_apply.
|
|
for (auto &It : IndexMap) {
|
|
auto *PartialApply = It.first;
|
|
auto &Indices = It.second;
|
|
auto *Replacement = specializePartialApply(PartialApply, Indices);
|
|
PartialApply->replaceAllUsesWith(Replacement);
|
|
|
|
auto *FRI = cast<FunctionRefInst>(PartialApply->getCallee());
|
|
PartialApply->eraseFromParent();
|
|
|
|
// TODO: Erase from module if there are no more uses.
|
|
if (FRI->use_empty())
|
|
FRI->eraseFromParent();
|
|
}
|
|
}
|
|
|
|
static void
|
|
rewritePromotedBoxes(llvm::SmallVectorImpl<AllocBoxInst *> &Promoted,
|
|
llvm::SmallVectorImpl<Operand *> &ElidedOperands,
|
|
llvm::SmallVectorImpl<TermInst *> &Returns) {
|
|
// First we'll rewrite any partial applies that we can to remove the
|
|
// box container pointer from the operands.
|
|
rewritePartialApplies(ElidedOperands);
|
|
|
|
auto rend = Promoted.rend();
|
|
for (auto I = Promoted.rbegin(); I != rend; ++I) {
|
|
auto *ABI = *I;
|
|
rewriteAllocBoxAsAllocStack(ABI, Returns);
|
|
ABI->eraseFromParent();
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
class AllocBoxToStack : public SILFunctionTransform {
|
|
/// The entry point to the transformation.
|
|
void run() {
|
|
llvm::SmallVector<AllocBoxInst *, 8> Promoted;
|
|
llvm::SmallVector<Operand *, 8> ElidedOperands;
|
|
llvm::SmallVector<TermInst *, 8> Returns;
|
|
|
|
for (auto &BB : *getFunction()) {
|
|
auto *Term = BB.getTerminator();
|
|
if (isa<AutoreleaseReturnInst>(Term) ||
|
|
isa<ReturnInst>(Term))
|
|
Returns.push_back(Term);
|
|
|
|
for (auto &I : BB)
|
|
if (auto *ABI = dyn_cast<AllocBoxInst>(&I))
|
|
if (canPromoteAllocBox(ABI, ElidedOperands))
|
|
Promoted.push_back(ABI);
|
|
}
|
|
|
|
if (!Promoted.empty()) {
|
|
rewritePromotedBoxes(Promoted, ElidedOperands, Returns);
|
|
NumStackPromoted += Promoted.size();
|
|
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
|
|
}
|
|
}
|
|
|
|
StringRef getName() override { return "AllocBox-To-Stack Optimization"; }
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
SILTransform *swift::createAllocBoxToStack() {
|
|
return new AllocBoxToStack();
|
|
}
|