//===--- AllocBoxToStack.cpp - Promote alloc_box to alloc_stack -----------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2016 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/SILOptimizer/PassManager/Passes.h" #include "swift/SIL/Dominance.h" #include "swift/SIL/Mangle.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILCloner.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/Local.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 //===----------------------------------------------------------------------===// /// This is a list we use to store a set of indices. We create the set by /// sorting, uniquing at the appropriate time. The reason why it makes sense to /// just use a sorted vector with std::count is because generally functions do /// not have that many arguments and even fewer promoted arguments. using ParamIndexList = llvm::SmallVector; static SILInstruction* findUnexpectedBoxUse(SILValue Box, bool examinePartialApply, bool inAppliedFunction, llvm::SmallVectorImpl &); static bool partialApplyArgumentEscapes(Operand *O); // Propagate liveness backwards from an initial set of blocks in our // LiveIn set. static void propagateLiveness(llvm::SmallPtrSetImpl &LiveIn, SILBasicBlock *DefBB) { // First populate a worklist of predecessors. llvm::SmallVector 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).second) 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 &LiveIn) { for (auto &Succ : BB->getSuccessors()) 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 bool addLastRelease(SILValue V, SILBasicBlock *BB, llvm::SmallVectorImpl &Releases) { for (auto I = BB->rbegin(); I != BB->rend(); ++I) { if (isa(*I) || isa(*I)) { if (I->getOperand(0) != V) continue; Releases.push_back(&*I); return true; } } return false; } // 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 bool getFinalReleases(AllocBoxInst *ABI, llvm::SmallVectorImpl &Releases) { llvm::SmallPtrSet LiveIn; llvm::SmallPtrSet UseBlocks; auto *DefBB = ABI->getParent(); auto seenRelease = false; SILInstruction *OneRelease = nullptr; // 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(); auto *BB = User->getParent(); if (isa(User)) continue; 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(User) || isa(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 true; } 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)) if (!addLastRelease(ABI, BB, Releases)) return false; return true; } /// \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(User) || isa(User) || isa(User) || isa(User)) return false; if (auto *Store = dyn_cast(User)) { if (Store->getDest() == UI->get()) return false; } else if (auto *Assign = dyn_cast(User)) { if (Assign->getDest() == UI->get()) return false; } return true; } static bool partialApplyEscapes(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; if (auto apply = dyn_cast(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 (isIndirectConvention( apply->getArgumentConvention(UI->getOperandNumber()-1))) continue; // Optionally drill down into an apply to see if the operand is // captured in or returned from the apply. if (examineApply && !partialApplyArgumentEscapes(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(User)) { auto args = partialApply->getArguments(); auto params = partialApply->getSubstCalleeType() ->getParameters(); params = params.slice(params.size() - args.size(), args.size()); if (params[UI->getOperandNumber()-1].isIndirect()) { if (partialApplyEscapes(partialApply, /*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(Call)) return dyn_cast(Apply->getCallee()); else return dyn_cast(cast(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(O->getUser()) || isa(O->getUser()) && "Expected apply or partial_apply!"); auto OperandIndex = O->getOperandNumber(); assert(OperandIndex != 0 && "Operand cannot be the applied function!"); // The applied function is the first operand. auto ParamIndex = OperandIndex - 1; if (auto *Apply = dyn_cast(O->getUser())) { assert(Apply->getSubstCalleeType()->getNumSILArguments() == Apply->getArguments().size() && "Expected all arguments to be supplied!"); (void) Apply; } else { auto *PartialApply = cast(O->getUser()); auto FnType = PartialApply->getSubstCalleeType(); auto ArgCount = PartialApply->getArguments().size(); assert(ArgCount <= FnType->getParameters().size()); ParamIndex += (FnType->getNumSILArguments() - ArgCount); } 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. static SILArgument *getParameterForOperand(SILFunction *F, Operand *O) { assert(F && !F->empty() && "Expected a function with a body!"); auto &Entry = F->front(); size_t ParamIndex = getParameterIndexForOperand(O); return Entry.getBBArg(ParamIndex); } /// Return a pointer to the SILFunction called by Call if we can /// determine which function 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 partialApplyArgumentEscapes(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 = SILValue(getParameterForOperand(F, O)); return partialApplyEscapes(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 promoted 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 PromotedOperands; auto Param = SILValue(getParameterForOperand(F, O)); return !findUnexpectedBoxUse(Param, /* examinePartialApply = */ false, /* inAppliedFunction = */ true, PromotedOperands); } /// 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 &PromotedOperands) { assert((Box->getType().is() || Box->getType() == SILType::getNativeObjectType(Box->getType().getASTContext())) && "Expected an object pointer!"); llvm::SmallVector LocalPromotedOperands; // 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. // Projections are fine as well. if (isa(User) || isa(User) || isa(User) || (!inAppliedFunction && isa(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(User)) if (examinePartialApply && checkPartialApplyBody(UI) && !partialApplyEscapes(PAI, /* examineApply = */ true)) { LocalPromotedOperands.push_back(UI); continue; } return User; } PromotedOperands.append(LocalPromotedOperands.begin(), LocalPromotedOperands.end()); return nullptr; } /// canPromoteAllocBox - Can we promote this alloc_box to an alloc_stack? static bool canPromoteAllocBox(AllocBoxInst *ABI, llvm::SmallVectorImpl &PromotedOperands){ // Scan all of the uses of the address of the box to see if any // disqualifies the box from being promoted to the stack. if (auto *User = findUnexpectedBoxUse(ABI, /* examinePartialApply = */ true, /* inAppliedFunction = */ false, PromotedOperands)) { (void)User; // 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 bool rewriteAllocBoxAsAllocStack(AllocBoxInst *ABI, llvm::SmallVectorImpl &Returns) { DEBUG(llvm::dbgs() << "*** Promoting alloc_box to stack: " << *ABI); llvm::SmallVector FinalReleases; if (!getFinalReleases(ABI, FinalReleases)) return false; // Promote this alloc_box to an alloc_stack. Insert the alloc_stack // at the beginning of the function. auto &Entry = ABI->getFunction()->front(); SILBuilder BuildAlloc(&Entry, Entry.begin()); BuildAlloc.setCurrentDebugScope(ABI->getDebugScope()); auto *ASI = BuildAlloc.createAllocStack(ABI->getLoc(), ABI->getElementType(), ABI->getVarInfo()); // 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(Use->getUser())) { PBI->replaceAllUsesWith(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()) if (auto *MUI = dyn_cast(UI->getUser())) { assert(ASI->hasOneUse() && "alloc_stack used by mark_uninitialized, but not exclusively!"); PointerResult = MUI; break; } auto &Lowering = ABI->getModule().getTypeLowering(ABI->getElementType()); auto Loc = CleanupLocation::get(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(LastRelease)) continue; SILBuilderWithScope BuildDestroy(LastRelease); BuildDestroy.emitDestroyAddrAndFold(Loc, PointerResult); } } for (auto Return : Returns) { SILBuilderWithScope BuildDealloc(Return); BuildDealloc.createDeallocStack(Loc, ASI); } // 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(User) || isa(User) || isa(User) || isa(User)); User->eraseFromParent(); } return true; } namespace { /// \brief A SILCloner subclass which clones a closure function while /// promoting some of its box parameters to stack addresses. class PromotedParamCloner : public SILClonerWithScopes { public: friend class SILVisitor; friend class SILCloner; PromotedParamCloner(SILFunction *Orig, ParamIndexList &PromotedParamIndices, llvm::StringRef ClonedName); void populateCloned(); SILFunction *getCloned() { return &getBuilder().getFunction(); } private: static SILFunction *initCloned(SILFunction *Orig, ParamIndexList &PromotedParamIndices, llvm::StringRef ClonedName); void visitStrongReleaseInst(StrongReleaseInst *Inst); void visitStrongRetainInst(StrongRetainInst *Inst); void visitProjectBoxInst(ProjectBoxInst *Inst); SILFunction *Orig; ParamIndexList &PromotedParamIndices; // The values in the original function that are promoted to stack // references. llvm::SmallSet PromotedParameters; }; } // end anonymous namespace. PromotedParamCloner::PromotedParamCloner(SILFunction *Orig, ParamIndexList &PromotedParamIndices, llvm::StringRef ClonedName) : SILClonerWithScopes(*initCloned(Orig, PromotedParamIndices, ClonedName)), Orig(Orig), PromotedParamIndices(PromotedParamIndices) { assert(Orig->getDebugScope()->getParentFunction() != getCloned()->getDebugScope()->getParentFunction()); } static std::string getClonedName(SILFunction *F, ParamIndexList &PromotedParamIndices) { Mangle::Mangler M; auto P = SpecializationPass::AllocBoxToStack; FunctionSignatureSpecializationMangler FSSM(P, M, F); for (unsigned i : PromotedParamIndices) FSSM.setArgumentBoxToStack(i); FSSM.mangle(); return M.finalize(); } /// \brief Create the function corresponding to the clone of the /// original closure with the signature modified to reflect promoted /// parameters (which are specified by PromotedParamIndices). SILFunction* PromotedParamCloner::initCloned(SILFunction *Orig, ParamIndexList &PromotedParamIndices, llvm::StringRef ClonedName) { SILModule &M = Orig->getModule(); SmallVector ClonedInterfaceArgTys; // Generate a new parameter list with deleted parameters removed. SILFunctionType *OrigFTI = Orig->getLoweredFunctionType(); unsigned Index = OrigFTI->getNumIndirectResults(); for (auto ¶m : OrigFTI->getParameters()) { if (count(PromotedParamIndices, Index)) { auto paramTy = param.getType()->castTo() ->getBoxedAddressType(); auto promotedParam = SILParameterInfo(paramTy.getSwiftRValueType(), ParameterConvention::Indirect_InoutAliasable); ClonedInterfaceArgTys.push_back(promotedParam); } else { ClonedInterfaceArgTys.push_back(param); } ++Index; } // Create the new function type for the cloned function with some of // the parameters promoted. auto ClonedTy = SILFunctionType::get(OrigFTI->getGenericSignature(), OrigFTI->getExtInfo(), OrigFTI->getCalleeConvention(), ClonedInterfaceArgTys, OrigFTI->getAllResults(), OrigFTI->getOptionalErrorResult(), 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 = M.getOrCreateFunction( SILLinkage::Shared, ClonedName, ClonedTy, Orig->getContextGenericParams(), Orig->getLocation(), Orig->isBare(), IsNotTransparent, Orig->isFragile(), Orig->isThunk(), Orig->getClassVisibility(), Orig->getInlineStrategy(), Orig->getEffectsKind(), Orig, Orig->getDebugScope()); for (auto &Attr : Orig->getSemanticsAttrs()) { Fn->addSemanticsAttr(Attr); } Fn->setDeclCtx(Orig->getDeclContext()); return Fn; } /// \brief Populate the body of the cloned closure, modifying instructions as /// necessary to take into consideration the removed parameters. void PromotedParamCloner::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 (count(PromotedParamIndices, ArgNo)) { // Create a new argument with the promoted type. auto promotedTy = (*I)->getType().castTo() ->getBoxedAddressType(); auto promotedArg = new (M) SILArgument(ClonedEntryBB, promotedTy, (*I)->getDecl()); PromotedParameters.insert(*I); // Map any projections of the box to the promoted argument. for (auto use : (*I)->getUses()) { if (auto project = dyn_cast(use->getUser())) { ValueMap.insert(std::make_pair(project, promotedArg)); } } } else { // Create a new argument which copies the original argument. SILValue MappedValue = new (M) SILArgument(ClonedEntryBB, (*I)->getType(), (*I)->getDecl()); ValueMap.insert(std::make_pair(*I, MappedValue)); } ++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 with /// a ReleaseValue of the new object type argument, otherwise it is handled /// normally. void PromotedParamCloner::visitStrongReleaseInst(StrongReleaseInst *Inst) { // If it's a release of a promoted parameter, just drop the instruction. if (PromotedParameters.count(Inst->getOperand())) return; SILCloner::visitStrongReleaseInst(Inst); } void PromotedParamCloner::visitStrongRetainInst(StrongRetainInst *Inst) { // If it's a retain of a promoted parameter, just drop the instruction. if (PromotedParameters.count(Inst->getOperand())) return; SILCloner::visitStrongRetainInst(Inst); } 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 if (PromotedParameters.count(Inst->getOperand())) return; SILCloner::visitProjectBoxInst(Inst); } /// Specialize a partial_apply by promoting the parameters indicated by /// indices. We expect these parameters to be replaced by stack address /// references. static PartialApplyInst * specializePartialApply(PartialApplyInst *PartialApply, ParamIndexList &PromotedParamIndices, bool &CFGChanged) { auto *FRI = cast(PartialApply->getCallee()); assert(FRI && "Expected a direct partial_apply!"); auto *F = FRI->getReferencedFunction(); assert(F && "Expected a referenced function!"); std::string ClonedName = getClonedName(F, PromotedParamIndices); auto &M = PartialApply->getModule(); SILFunction *ClonedFn; if (auto *PrevFn = M.lookUpFunction(ClonedName)) { ClonedFn = PrevFn; } else { // Clone the function the existing partial_apply references. PromotedParamCloner Cloner(F, PromotedParamIndices, ClonedName); Cloner.populateCloned(); ClonedFn = Cloner.getCloned(); } // Now create the new partial_apply using the cloned function. llvm::SmallVector Args; ValueLifetimeAnalysis::Frontier PAFrontier; // Promote the arguments that need promotion. for (auto &O : PartialApply->getArgumentOperands()) { auto ParamIndex = getParameterIndexForOperand(&O); if (!count(PromotedParamIndices, ParamIndex)) { Args.push_back(O.get()); 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(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(BoxUse->getUser())) { for (auto PBIUse : PBI->getUses()) { numAddrUses++; if (auto MUI = dyn_cast(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); Args.push_back(promoted); if (PAFrontier.empty()) { ValueLifetimeAnalysis VLA(PartialApply); CFGChanged |= !VLA.computeFrontier(PAFrontier, ValueLifetimeAnalysis::AllowToModifyCFG); assert(!PAFrontier.empty() && "partial_apply must have at least one use " "to release the returned function"); } // Insert releases after each point where the partial_apply becomes dead. for (SILInstruction *FrontierInst : PAFrontier) { SILBuilderWithScope Builder(FrontierInst); Builder.emitStrongReleaseAndFold(PartialApply->getLoc(), O.get()); } } SILBuilderWithScope Builder(PartialApply); // Build the function_ref and partial_apply. SILValue FunctionRef = Builder.createFunctionRef(PartialApply->getLoc(), ClonedFn); CanSILFunctionType CanFnTy = ClonedFn->getLoweredFunctionType(); auto const &Subs = PartialApply->getSubstitutions(); CanSILFunctionType SubstCalleeTy = CanFnTy->substGenericArgs(M, M.getSwiftModule(), Subs); return Builder.createPartialApply(PartialApply->getLoc(), FunctionRef, SILType::getPrimitiveObjectType(SubstCalleeTy), PartialApply->getSubstitutions(), Args, PartialApply->getType()); } static void rewritePartialApplies(llvm::SmallVectorImpl &PromotedOperands, bool &CFGChanged) { llvm::DenseMap IndexMap; ParamIndexList Indices; // Build a map from partial_apply to the indices of the operands // that will be promoted in our rewritten version. for (auto *O : PromotedOperands) { auto ParamIndexNumber = getParameterIndexForOperand(O); Indices.clear(); Indices.push_back(ParamIndexNumber); auto *PartialApply = cast(O->getUser()); llvm::DenseMap::iterator It; bool Inserted; std::tie(It, Inserted) = IndexMap.insert(std::make_pair(PartialApply, Indices)); if (!Inserted) It->second.push_back(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; // Sort the indices and unique them. std::sort(Indices.begin(), Indices.end()); Indices.erase(std::unique(Indices.begin(), Indices.end()), Indices.end()); auto *Replacement = specializePartialApply(PartialApply, Indices, CFGChanged); PartialApply->replaceAllUsesWith(Replacement); auto *FRI = cast(PartialApply->getCallee()); PartialApply->eraseFromParent(); // TODO: Erase from module if there are no more uses. if (FRI->use_empty()) FRI->eraseFromParent(); } } static unsigned rewritePromotedBoxes(llvm::SmallVectorImpl &Promoted, llvm::SmallVectorImpl &PromotedOperands, llvm::SmallVectorImpl &Returns, bool &CFGChanged) { // First we'll rewrite any partial applies that we can to remove the // box container pointer from the operands. rewritePartialApplies(PromotedOperands, CFGChanged); unsigned Count = 0; auto rend = Promoted.rend(); for (auto I = Promoted.rbegin(); I != rend; ++I) { auto *ABI = *I; if (rewriteAllocBoxAsAllocStack(ABI, Returns)) { ++Count; ABI->eraseFromParent(); } } return Count; } namespace { class AllocBoxToStack : public SILFunctionTransform { /// The entry point to the transformation. void run() override { llvm::SmallVector Promotable; llvm::SmallVector PromotedOperands; llvm::SmallVector Returns; for (auto &BB : *getFunction()) { auto *Term = BB.getTerminator(); if (isa(Term) || isa(Term)) Returns.push_back(Term); for (auto &I : BB) if (auto *ABI = dyn_cast(&I)) if (canPromoteAllocBox(ABI, PromotedOperands)) Promotable.push_back(ABI); } if (!Promotable.empty()) { bool CFGChanged = false; auto Count = rewritePromotedBoxes(Promotable, PromotedOperands, Returns, CFGChanged); NumStackPromoted += Count; invalidateAnalysis(CFGChanged ? SILAnalysis::InvalidationKind::FunctionBody : SILAnalysis::InvalidationKind::CallsAndInstructions); } } StringRef getName() override { return "AllocBox-To-Stack Optimization"; } }; } // end anonymous namespace SILTransform *swift::createAllocBoxToStack() { return new AllocBoxToStack(); }