[allocbox-to-stack] Update for semantic sil.

rdar://29870610
This commit is contained in:
Michael Gottesman
2017-04-03 12:19:41 -07:00
parent bac440dba5
commit 43a7f3422e
4 changed files with 1209 additions and 91 deletions

View File

@@ -81,13 +81,21 @@ static bool successorHasLiveIn(SILBasicBlock *BB,
return false;
}
// Walk backwards in BB looking for strong_release or dealloc_box of
// the given value, and add it to Releases.
static SILValue stripOffCopyValue(SILValue V) {
while (auto *CVI = dyn_cast<CopyValueInst>(V)) {
V = CVI->getOperand();
}
return V;
}
// Walk backwards in BB looking for strong_release, destroy_value, or
// dealloc_box of the given value, and add it to releases.
static bool 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)
if (isa<StrongReleaseInst>(*I) || isa<DeallocBoxInst>(*I) ||
isa<DestroyValueInst>(*I)) {
if (stripOffCopyValue(I->getOperand(0)) != V)
continue;
Releases.push_back(&*I);
@@ -114,8 +122,10 @@ static bool getFinalReleases(AllocBoxInst *ABI,
// 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 : ABI->getUses()) {
auto *User = UI->getUser();
llvm::SmallVector<Operand *, 32> Worklist(ABI->use_begin(), ABI->use_end());
while (!Worklist.empty()) {
auto *Op = Worklist.pop_back_val();
auto *User = Op->getUser();
auto *BB = User->getParent();
if (isa<ProjectBoxInst>(User))
@@ -127,8 +137,18 @@ static bool getFinalReleases(AllocBoxInst *ABI,
// Also keep track of the blocks with uses.
UseBlocks.insert(BB);
// If we have a copy value, add its uses to the work list and continue.
//
// We are actually performing multiple operations here. We are eliminating
// copies of the box and the box itself.
if (auto *CVI = dyn_cast<CopyValueInst>(User)) {
std::copy(CVI->use_begin(), CVI->use_end(), std::back_inserter(Worklist));
continue;
}
// Try to speed up the trivial case of single release/dealloc.
if (isa<StrongReleaseInst>(User) || isa<DeallocBoxInst>(User)) {
if (isa<StrongReleaseInst>(User) || isa<DeallocBoxInst>(User) ||
isa<DestroyValueInst>(User)) {
if (!seenRelease)
OneRelease = User;
else
@@ -162,10 +182,9 @@ static bool useCaptured(Operand *UI) {
auto *User = UI->getUser();
// These instructions do not cause the address to escape.
if (isa<DebugValueInst>(User) ||
isa<DebugValueAddrInst>(User) ||
isa<StrongReleaseInst>(User) ||
isa<StrongRetainInst>(User))
if (isa<DebugValueInst>(User) || isa<DebugValueAddrInst>(User) ||
isa<StrongReleaseInst>(User) || isa<StrongRetainInst>(User) ||
isa<DestroyValueInst>(User))
return false;
if (auto *Store = dyn_cast<StoreInst>(User)) {
@@ -180,41 +199,52 @@ static bool useCaptured(Operand *UI) {
}
static bool partialApplyEscapes(SILValue V, bool examineApply) {
SILModuleConventions modConv(*V->getModule());
for (auto UI : V->getUses()) {
auto *User = UI->getUser();
SILModuleConventions ModConv(*V->getModule());
llvm::SmallVector<Operand *, 32> Worklist(V->use_begin(), V->use_end());
while (!Worklist.empty()) {
auto *Op = Worklist.pop_back_val();
// These instructions do not cause the address to escape.
if (!useCaptured(UI))
if (!useCaptured(Op))
continue;
if (auto apply = dyn_cast<ApplyInst>(User)) {
auto *User = Op->getUser();
// If we have a copy_value, the copy value does not cause an escape, but its
// uses might do so... so add the copy_value's uses to the worklist and
// continue.
if (auto *Copy = dyn_cast<CopyValueInst>(User)) {
std::copy(Copy->use_begin(), Copy->use_end(),
std::back_inserter(Worklist));
continue;
}
if (auto *Apply = dyn_cast<ApplyInst>(User)) {
// Applying a function does not cause the function to escape.
if (UI->getOperandNumber() == 0)
if (Op->getOperandNumber() == 0)
continue;
// apply instructions do not capture the pointer when it is passed
// indirectly
if (apply->getArgumentConvention(UI->getOperandNumber() - 1)
if (Apply->getArgumentConvention(Op->getOperandNumber() - 1)
.isIndirectConvention())
continue;
// Optionally drill down into an apply to see if the operand is
// captured in or returned from the apply.
if (examineApply && !partialApplyArgumentEscapes(UI))
if (examineApply && !partialApplyArgumentEscapes(Op))
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()
->getParameters();
params = params.slice(params.size() - args.size(), args.size());
if (modConv.isSILIndirect(params[UI->getOperandNumber() - 1])) {
if (partialApplyEscapes(partialApply, /*examineApply = */ true))
if (auto *PartialApply = dyn_cast<PartialApplyInst>(User)) {
auto Args = PartialApply->getArguments();
auto Params = PartialApply->getSubstCalleeType()->getParameters();
Params = Params.slice(Params.size() - Args.size(), Args.size());
if (ModConv.isSILIndirect(Params[Op->getOperandNumber() - 1])) {
if (partialApplyEscapes(PartialApply, /*examineApply = */ true))
return true;
continue;
}
@@ -343,24 +373,32 @@ static SILInstruction* findUnexpectedBoxUse(SILValue Box,
// 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();
llvm::SmallVector<Operand *, 32> Worklist(Box->use_begin(), Box->use_end());
while (!Worklist.empty()) {
auto *Op = Worklist.pop_back_val();
auto *User = Op->getUser();
// Retains and releases are fine. Deallocs are fine if we're not
// examining a function that the alloc_box was passed into.
// Projections are fine as well.
if (isa<StrongRetainInst>(User) || isa<StrongReleaseInst>(User) ||
isa<ProjectBoxInst>(User) ||
isa<ProjectBoxInst>(User) || isa<DestroyValueInst>(User) ||
(!inAppliedFunction && isa<DeallocBoxInst>(User)))
continue;
// If we have a copy_value, visit the users of the copy_value.
if (auto *CVI = dyn_cast<CopyValueInst>(User)) {
std::copy(CVI->use_begin(), CVI->use_end(), std::back_inserter(Worklist));
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) &&
if (examinePartialApply && checkPartialApplyBody(Op) &&
!partialApplyEscapes(PAI, /* examineApply = */ true)) {
LocalPromotedOperands.push_back(UI);
LocalPromotedOperands.push_back(Op);
continue;
}
@@ -368,7 +406,7 @@ static SILInstruction* findUnexpectedBoxUse(SILValue Box,
}
PromotedOperands.append(LocalPromotedOperands.begin(),
LocalPromotedOperands.end());
LocalPromotedOperands.end());
return nullptr;
}
@@ -394,12 +432,28 @@ static bool canPromoteAllocBox(AllocBoxInst *ABI,
return true;
}
static void replaceProjectBoxUsers(AllocBoxInst *ABI, AllocStackInst *ASI) {
llvm::SmallVector<Operand *, 8> Worklist(ABI->use_begin(), ABI->use_end());
while (!Worklist.empty()) {
auto *Op = Worklist.pop_back_val();
if (auto *PBI = dyn_cast<ProjectBoxInst>(Op->getUser())) {
PBI->replaceAllUsesWith(ASI);
continue;
}
auto *CVI = dyn_cast<CopyValueInst>(Op->getUser());
if (!CVI)
continue;
std::copy(CVI->use_begin(), CVI->use_end(), std::back_inserter(Worklist));
}
}
/// rewriteAllocBoxAsAllocStack - Replace uses of the alloc_box with a
/// new alloc_stack, but do not delete the alloc_box yet.
static bool rewriteAllocBoxAsAllocStack(AllocBoxInst *ABI) {
DEBUG(llvm::dbgs() << "*** Promoting alloc_box to stack: " << *ABI);
llvm::SmallVector<SILInstruction*, 4> FinalReleases;
llvm::SmallVector<SILInstruction *, 4> FinalReleases;
if (!getFinalReleases(ABI, FinalReleases))
return false;
@@ -415,23 +469,20 @@ static bool rewriteAllocBoxAsAllocStack(AllocBoxInst *ABI) {
// Replace all uses of the address of the box's contained value with
// the address of the stack location.
for (Operand *Use : ABI->getUses()) {
if (auto *PBI = dyn_cast<ProjectBoxInst>(Use->getUser())) {
PBI->replaceAllUsesWith(ASI);
}
}
replaceProjectBoxUsers(ABI, ASI);
// 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;
for (auto UI : ASI->getUses())
for (auto UI : ASI->getUses()) {
if (auto *MUI = dyn_cast<MarkUninitializedInst>(UI->getUser())) {
assert(ASI->hasOneUse() &&
"alloc_stack used by mark_uninitialized, but not exclusively!");
PointerResult = MUI;
break;
}
}
assert(ABI->getBoxType()->getLayout()->getFields().size() == 1
&& "promoting multi-field box not implemented");
@@ -452,10 +503,25 @@ static bool rewriteAllocBoxAsAllocStack(AllocBoxInst *ABI) {
// Remove any retain and release instructions. Since all uses of project_box
// are gone, this only walks through uses of the box itself (the retain count
// pointer).
while (!ABI->use_empty()) {
auto *User = (*ABI->use_begin())->getUser();
llvm::SmallVector<SILInstruction *, 8> Worklist;
std::transform(ABI->use_begin(), ABI->use_end(), std::back_inserter(Worklist),
[](Operand *Op) -> SILInstruction * { return Op->getUser(); });
while (!Worklist.empty()) {
auto *User = Worklist.pop_back_val();
if (isa<CopyValueInst>(User)) {
std::transform(
User->use_begin(), User->use_end(), std::back_inserter(Worklist),
[](Operand *Op) -> SILInstruction * { return Op->getUser(); });
User->replaceAllUsesWith(
SILUndef::get(User->getType(), User->getModule()));
User->eraseFromParent();
continue;
}
assert(isa<StrongReleaseInst>(User) || isa<StrongRetainInst>(User) ||
isa<DeallocBoxInst>(User) || isa<ProjectBoxInst>(User));
isa<DeallocBoxInst>(User) || isa<ProjectBoxInst>(User) ||
isa<DestroyValueInst>(User));
User->eraseFromParent();
}
@@ -468,7 +534,7 @@ namespace {
/// \brief A SILCloner subclass which clones a closure function while
/// promoting some of its box parameters to stack addresses.
class PromotedParamCloner : public SILClonerWithScopes<PromotedParamCloner> {
public:
public:
friend class SILVisitor<PromotedParamCloner>;
friend class SILCloner<PromotedParamCloner>;
@@ -480,21 +546,23 @@ class PromotedParamCloner : public SILClonerWithScopes<PromotedParamCloner> {
SILFunction *getCloned() { return &getBuilder().getFunction(); }
private:
static SILFunction *initCloned(SILFunction *Orig, IsSerialized_t Serialized,
ArgIndexList &PromotedArgIndices,
llvm::StringRef ClonedName);
private:
static SILFunction *initCloned(SILFunction *Orig, IsSerialized_t Serialized,
ArgIndexList &PromotedArgIndices,
llvm::StringRef ClonedName);
void visitStrongReleaseInst(StrongReleaseInst *Inst);
void visitStrongRetainInst(StrongRetainInst *Inst);
void visitProjectBoxInst(ProjectBoxInst *Inst);
void visitStrongReleaseInst(StrongReleaseInst *Inst);
void visitDestroyValueInst(DestroyValueInst *Inst);
void visitStrongRetainInst(StrongRetainInst *Inst);
void visitCopyValueInst(CopyValueInst *Inst);
void visitProjectBoxInst(ProjectBoxInst *Inst);
SILFunction *Orig;
ArgIndexList &PromotedArgIndices;
SILFunction *Orig;
ArgIndexList &PromotedArgIndices;
// The values in the original function that are promoted to stack
// references.
llvm::SmallSet<SILValue, 4> PromotedParameters;
// The values in the original function that are promoted to stack
// references.
llvm::SmallSet<SILValue, 4> PromotedParameters;
};
} // end anonymous namespace
@@ -644,6 +712,24 @@ PromotedParamCloner::visitStrongReleaseInst(StrongReleaseInst *Inst) {
SILCloner<PromotedParamCloner>::visitStrongReleaseInst(Inst);
}
/// \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 with
/// a ReleaseValue of the new object type argument, otherwise it is handled
/// normally.
void PromotedParamCloner::visitDestroyValueInst(DestroyValueInst *Inst) {
// If we are a destroy of a promoted parameter, just drop the instruction. We
// look through copy_value to preserve current behavior.
SILInstruction *Tmp = Inst;
while (auto *CopyOp = dyn_cast<CopyValueInst>(Tmp->getOperand(0))) {
Tmp = CopyOp;
}
if (PromotedParameters.count(Tmp->getOperand(0)))
return;
SILCloner<PromotedParamCloner>::visitDestroyValueInst(Inst);
}
void
PromotedParamCloner::visitStrongRetainInst(StrongRetainInst *Inst) {
// If it's a retain of a promoted parameter, just drop the instruction.
@@ -653,8 +739,19 @@ PromotedParamCloner::visitStrongRetainInst(StrongRetainInst *Inst) {
SILCloner<PromotedParamCloner>::visitStrongRetainInst(Inst);
}
void
PromotedParamCloner::visitProjectBoxInst(ProjectBoxInst *Inst) {
void PromotedParamCloner::visitCopyValueInst(CopyValueInst *CVI) {
// If it's a copy of a promoted parameter, just drop the instruction.
auto *Tmp = CVI;
while (auto *CopyOp = dyn_cast<CopyValueInst>(Tmp->getOperand())) {
Tmp = CopyOp;
}
if (PromotedParameters.count(Tmp->getOperand()))
return;
SILCloner<PromotedParamCloner>::visitCopyValueInst(CVI);
}
void PromotedParamCloner::visitProjectBoxInst(ProjectBoxInst *Inst) {
// If it's a projection of a promoted parameter, drop the instruction.
// Its uses will be replaced by the promoted address.
// and replace its uses with
@@ -707,36 +804,15 @@ specializePartialApply(PartialApplyInst *PartialApply,
continue;
}
// If this argument is promoted, it is a box that we're
// turning into an address because we've proven we can
// keep this value on the stack. The partial_apply had ownership
// of this box so we must now release it explicitly when the
// partial_apply is released.
auto box = cast<AllocBoxInst>(O.get());
// If the box address has a MUI, route accesses through it so DI still
// works.
SILInstruction *promoted = nullptr;
int numAddrUses = 0;
for (Operand *BoxUse : box->getUses()) {
if (auto *PBI = dyn_cast<ProjectBoxInst>(BoxUse->getUser())) {
for (auto PBIUse : PBI->getUses()) {
numAddrUses++;
if (auto MUI = dyn_cast<MarkUninitializedInst>(PBIUse->getUser()))
promoted = MUI;
}
}
}
assert((!promoted || numAddrUses == 1) &&
"box value used by mark_uninitialized but not exclusively!");
// We only reuse an existing project_box if it directly follows the
// alloc_box. This makes sure that the project_box dominates the
// partial_apply.
if (!promoted)
promoted = getOrCreateProjectBox(box, 0);
Args.push_back(promoted);
// If this argument is promoted, it is a box that we're turning into an
// address because we've proven we can keep this value on the stack. The
// partial_apply had ownership of this box so we must now release it
// explicitly when the partial_apply is released.
SILInstruction *Box = cast<SILInstruction>(O.get());
assert((isa<AllocBoxInst>(Box) || isa<CopyValueInst>(Box)) &&
"Expected either an alloc box or a copy of an alloc box");
SILBuilder B(Box);
Args.push_back(B.createProjectBox(Box->getLoc(), Box, 0));
if (PAFrontier.empty()) {
ValueLifetimeAnalysis VLA(PartialApply);
@@ -746,10 +822,11 @@ specializePartialApply(PartialApplyInst *PartialApply,
"to release the returned function");
}
// Insert releases after each point where the partial_apply becomes dead.
// Insert destroys of the box at each point where the partial_apply becomes
// dead.
for (SILInstruction *FrontierInst : PAFrontier) {
SILBuilderWithScope Builder(FrontierInst);
Builder.emitStrongReleaseAndFold(PartialApply->getLoc(), O.get());
Builder.createDestroyValue(PartialApply->getLoc(), Box);
}
}