//===--- SILMem2Reg.cpp - Promotes AllocStacks to registers ---------------===// // // 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 // //===----------------------------------------------------------------------===// // // This pass promotes AllocStack instructions into virtual register // references. It only handles load, store and deallocation // instructions. The algorithm is based on: // // Sreedhar and Gao. A linear time algorithm for placing phi-nodes. POPL '95. // //===----------------------------------------------------------------------===// #define DEBUG_TYPE "sil-mem2reg" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/AST/DiagnosticsSIL.h" #include "swift/SIL/Dominance.h" #include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILModule.h" #include "swift/SIL/Projection.h" #include "swift/SIL/TypeLowering.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/CFG.h" #include "swift/SILOptimizer/Utils/Local.h" #include "swift/SILOptimizer/Analysis/DominanceAnalysis.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Debug.h" #include #include using namespace swift; STATISTIC(NumAllocStackFound, "Number of AllocStack found"); STATISTIC(NumAllocStackCaptured, "Number of AllocStack captured"); STATISTIC(NumInstRemoved, "Number of Instructions removed"); STATISTIC(NumPhiPlaced, "Number of Phi blocks placed"); namespace { typedef llvm::DomTreeNodeBase DomTreeNode; typedef llvm::DenseMap DomTreeLevelMap; /// Promotes a single AllocStackInst into registers.. class StackAllocationPromoter { typedef llvm::DenseSet BlockSet; typedef llvm::DenseMap BlockToInstMap; // Use a priority queue keyed on dominator tree level so that inserted nodes // are handled from the bottom of the dom tree upwards. typedef std::pair DomTreeNodePair; typedef std::priority_queue, llvm::less_second> NodePriorityQueue; /// The AllocStackInst that we are handling. AllocStackInst *ASI; /// The deallocation Instruction. This value could be NULL if there are /// multiple deallocations. DeallocStackInst *DSI; /// Dominator info. DominanceInfo *DT; /// Map from dominator tree node to tree level. DomTreeLevelMap &DomTreeLevels; /// The builder used to create new instructions during register promotion. SILBuilder &B; /// Records the last store instruction in each block for a specific /// AllocStackInst. BlockToInstMap LastStoreInBlock; public: /// C'tor. StackAllocationPromoter(AllocStackInst *Asi, DominanceInfo *Di, DomTreeLevelMap &DomTreeLevels, SILBuilder &B) : ASI(Asi), DSI(0), DT(Di), DomTreeLevels(DomTreeLevels), B(B) { // Scan the users in search of a deallocation instruction. for (auto UI = ASI->use_begin(), E = ASI->use_end(); UI != E; ++UI) if (DeallocStackInst *D = dyn_cast(UI->getUser())) { // Don't record multiple dealloc instructions. if (DSI) { DSI = 0; break; } // Record the deallocation instruction. DSI = D; } } /// Promote the Allocation. void run(); private: /// \brief Promote AllocStacks into SSA. void promoteAllocationToPhi(); /// \brief Replace the dummy nodes with new block arguments. void addBlockArguments(BlockSet &PhiBlocks); /// \brief Fix all of the branch instructions and the uses to use /// the AllocStack definitions (which include stores and Phis). void fixBranchesAndUses(BlockSet &Blocks); /// \brief update the branch instructions with the new Phi argument. /// The blocks in \p PhiBlocks are blocks that define a value, \p Dest is /// the branch destination, and \p Pred is the predecessors who's branch we /// modify. void fixPhiPredBlock(BlockSet &PhiBlocks, SILBasicBlock *Dest, SILBasicBlock *Pred); /// \brief Get the value for this AllocStack variable that is /// flowing out of StartBB. SILValue getLiveOutValue(BlockSet &PhiBlocks, SILBasicBlock *StartBB); /// \brief Get the value for this AllocStack variable that is /// flowing into BB. SILValue getLiveInValue(BlockSet &PhiBlocks, SILBasicBlock *BB); /// \brief Prune AllocStacks usage in the function. Scan the function /// and remove in-block usage of the AllocStack. Leave only the first /// load and the last store. void pruneAllocStackUsage(); /// \brief Promote all of the AllocStacks in a single basic block in one /// linear scan. This function deletes all of the loads and stores except /// for the first load and the last store. /// \returns the last StoreInst found or zero if none found. StoreInst *promoteAllocationInBlock(SILBasicBlock *BB); }; } // end of namespace namespace { /// Promote memory to registers class MemoryToRegisters { /// The function that we are optimizing. SILFunction &F; /// Dominators. DominanceInfo *DT; /// The builder used to create new instructions during register promotion. SILBuilder B; /// \brief Check if the AllocStackInst \p ASI is only written into. bool isWriteOnlyAllocation(AllocStackInst *ASI, bool Promoted = false); /// \brief Promote all of the AllocStacks in a single basic block in one /// linear scan. Note: This function deletes all of the users of the /// AllocStackInst, including the DeallocStackInst but it does not remove the /// AllocStackInst itself! void removeSingleBlockAllocation(AllocStackInst *ASI); public: /// C'tor MemoryToRegisters(SILFunction &Func, DominanceInfo *Dt) : F(Func), DT(Dt), B(Func) {} /// \brief Promote memory to registers. Return True on change. bool run(); }; } // end anonymous namespace. /// Returns true if \p I is an address of a LoadInst, skipping struct and /// tuple address projections. Sets \p singleBlock to null if the load (or /// it's address is not in \p singleBlock. static bool isAddressForLoad(SILInstruction *I, SILBasicBlock *&singleBlock) { if (isa(I)) return true; if (!isa(I) && !isa(I)) return false; // Recursively search for other (non-)loads in the instruction's uses. for (auto UI : I->getUses()) { SILInstruction *II = UI->getUser(); if (II->getParent() != singleBlock) singleBlock = nullptr; if (!isAddressForLoad(II, singleBlock)) return false; } return true; } /// Returns true if this AllocStacks is captured. /// Sets \p inSingleBlock to true if all uses of \p ASI are in a single block. static bool isCaptured(AllocStackInst *ASI, bool &inSingleBlock) { SILBasicBlock *singleBlock = ASI->getParent(); // For all users of the AllocStack instruction. for (auto UI = ASI->use_begin(), E = ASI->use_end(); UI != E; ++UI) { SILInstruction *II = UI->getUser(); if (II->getParent() != singleBlock) singleBlock = nullptr; // Loads are okay. if (isAddressForLoad(II, singleBlock)) continue; // We can store into an AllocStack (but not the pointer). if (StoreInst *SI = dyn_cast(II)) if (SI->getDest() == ASI) continue; // Deallocation is also okay, as are DebugValueAddr. We will turn // the latter into DebugValue. if (isa(II) || isa(II)) continue; // Destroys of loadable types can be rewritten as releases, so // they are fine. if (auto *DAI = dyn_cast(II)) if (DAI->getOperand()->getType().isLoadable(DAI->getModule())) continue; // Other instructions are assumed to capture the AllocStack. DEBUG(llvm::dbgs() << "*** AllocStack is captured by: " << *II); return true; } // None of the users capture the AllocStack. inSingleBlock = (singleBlock != nullptr); return false; } /// Returns true if the AllocStack is only stored into. bool MemoryToRegisters::isWriteOnlyAllocation(AllocStackInst *ASI, bool Promoted) { // For all users of the AllocStack: for (auto UI = ASI->use_begin(), E = ASI->use_end(); UI != E; ++UI) { SILInstruction *II = UI->getUser(); // It is okay to store into this AllocStack. if (StoreInst *SI = dyn_cast(II)) if (!isa(SI->getSrc())) continue; // Deallocation is also okay. if (isa(II)) continue; // If we haven't already promoted the AllocStack, we may see // DebugValueAddr uses. if (!Promoted && isa(II)) continue; // Destroys of loadable types can be rewritten as releases, so // they are fine. if (auto *DAI = dyn_cast(II)) if (!Promoted && DAI->getOperand()->getType().isLoadable(DAI->getModule())) continue; // Can't do anything else with it. DEBUG(llvm::dbgs() << "*** AllocStack has non-write use: " << *II); return false; } return true; } /// Promote a DebugValueAddr to a DebugValue of the given value. static void promoteDebugValueAddr(DebugValueAddrInst *DVAI, SILValue Value, SILBuilder &B) { assert(Value && "Expected valid value"); B.setInsertionPoint(DVAI); B.setCurrentDebugScope(DVAI->getDebugScope()); B.createDebugValue(DVAI->getLoc(), Value, DVAI->getVarInfo()); DVAI->eraseFromParent(); } /// Returns true if \p I is a load which loads from \p ASI. static bool isLoadFromStack(SILInstruction *I, AllocStackInst *ASI) { if (!isa(I)) return false; // Skip struct and tuple address projections. ValueBase *op = I->getOperand(0); while (op != ASI) { if (!isa(op) && !isa(op)) return false; op = cast(op)->getOperand(0); } return true; } /// Collects all load instructions which (transitively) use \p I as address. static void collectLoads(SILInstruction *I, SmallVectorImpl &Loads) { if (LoadInst *load = dyn_cast(I)) { Loads.push_back(load); return; } if (!isa(I) && !isa(I)) return; // Recursively search for other loads in the instruction's uses. for (auto UI : I->getUses()) { collectLoads(UI->getUser(), Loads); } } static void replaceLoad(LoadInst *LI, SILValue val, AllocStackInst *ASI) { ProjectionPath projections(val->getType()); SILValue op = LI->getOperand(); while (op != ASI) { assert(isa(op) || isa(op)); SILInstruction *Inst = cast(op); projections.push_back(Projection(Inst)); op = Inst->getOperand(0); } SILBuilder builder(LI); for (auto iter = projections.rbegin(); iter != projections.rend(); ++iter) { const Projection &projection = *iter; val = projection.createObjectProjection(builder, LI->getLoc(), val).get(); } op = LI->getOperand(); LI->replaceAllUsesWith(val); LI->eraseFromParent(); while (op != ASI && op->use_empty()) { assert(isa(op) || isa(op)); SILInstruction *Inst = cast(op); SILValue next = Inst->getOperand(0); Inst->eraseFromParent(); op = next; } } static void replaceDestroy(DestroyAddrInst *DAI, SILValue NewValue) { assert(DAI->getOperand()->getType().isLoadable(DAI->getModule()) && "Unexpected promotion of address-only type!"); assert(NewValue && "Expected a value to release!"); SILBuilderWithScope Builder(DAI); auto Ty = DAI->getOperand()->getType(); auto &TL = DAI->getModule().getTypeLowering(Ty); TL.emitLoweredReleaseValue(Builder, DAI->getLoc(), NewValue, Lowering::TypeLowering::LoweringStyle::DeepNoEnum); DAI->eraseFromParent(); } StoreInst * StackAllocationPromoter::promoteAllocationInBlock(SILBasicBlock *BB) { DEBUG(llvm::dbgs() << "*** Promoting ASI in block: " << *ASI); // We don't know the value of the alloca until we find the first store. SILValue RunningVal = SILValue(); // Keep track of the last StoreInst that we found. StoreInst *LastStore = nullptr; // For all instructions in the block. for (auto BBI = BB->begin(), E = BB->end(); BBI != E;) { SILInstruction *Inst = &*BBI; ++BBI; if (isLoadFromStack(Inst, ASI)) { if (RunningVal) { // If we are loading from the AllocStackInst and we already know the // content of the Alloca then use it. DEBUG(llvm::dbgs() << "*** Promoting load: " << *Inst); replaceLoad(cast(Inst), RunningVal, ASI); NumInstRemoved++; } else if (Inst->getOperand(0) == ASI) { // If we don't know the content of the AllocStack then the loaded // value *is* the new value; DEBUG(llvm::dbgs() << "*** First load: " << *Inst); RunningVal = Inst; } continue; } // Remove stores and record the value that we are saving as the running // value. if (StoreInst *SI = dyn_cast(Inst)) { if (SI->getDest() != ASI) continue; // The stored value is the new running value. RunningVal = SI->getSrc(); // If we met a store before this one, delete it. if (LastStore) { NumInstRemoved++; DEBUG(llvm::dbgs() << "*** Removing redundant store: " << *LastStore); LastStore->eraseFromParent(); } LastStore = SI; continue; } // Replace debug_value_addr with debug_value of the promoted value // if we have a valid value to use at this point. Otherwise we'll // promote this when we deal with hooking up phis. if (auto *DVAI = dyn_cast(Inst)) { if (DVAI->getOperand() == ASI && RunningVal) promoteDebugValueAddr(DVAI, RunningVal, B); continue; } // Replace destroys with a release of the value. if (auto *DAI = dyn_cast(Inst)) { if (DAI->getOperand() == ASI && RunningVal) { replaceDestroy(DAI, RunningVal); } continue; } // Stop on deallocation. if (DeallocStackInst *DSI = dyn_cast(Inst)) { if (DSI->getOperand() == ASI) break; } } if (LastStore) { DEBUG(llvm::dbgs() << "*** Finished promotion. Last store: " << *LastStore); } else { DEBUG(llvm::dbgs() << "*** Finished promotion with no stores.\n"); } return LastStore; } void MemoryToRegisters::removeSingleBlockAllocation(AllocStackInst *ASI) { DEBUG(llvm::dbgs() << "*** Promoting in-block: " << *ASI); SILBasicBlock *BB = ASI->getParent(); // The default value of the AllocStack is NULL because we don't have // uninitialized variables in Swift. SILValue RunningVal = SILValue(); // For all instructions in the block. for (auto BBI = BB->begin(), E = BB->end(); BBI != E;) { SILInstruction *Inst = &*BBI; ++BBI; // Remove instructions that we are loading from. Replace the loaded value // with our running value. if (isLoadFromStack(Inst, ASI)) { if (!RunningVal) { assert(ASI->getElementType().isVoid() && "Expected initialization of non-void type!"); RunningVal = SILUndef::get(ASI->getElementType(), ASI->getModule()); } replaceLoad(cast(Inst), RunningVal, ASI); NumInstRemoved++; continue; } // Remove stores and record the value that we are saving as the running // value. if (StoreInst *SI = dyn_cast(Inst)) { if (SI->getDest() == ASI) { RunningVal = SI->getSrc(); Inst->eraseFromParent(); NumInstRemoved++; continue; } } // Replace debug_value_addr with debug_value of the promoted value. if (auto *DVAI = dyn_cast(Inst)) { if (DVAI->getOperand() == ASI) { if (RunningVal) { promoteDebugValueAddr(DVAI, RunningVal, B); } else { // Drop debug_value_addr of uninitialized void values. assert(ASI->getElementType().isVoid() && "Expected initialization of non-void type!"); DVAI->eraseFromParent(); } } continue; } // Replace destroys with a release of the value. if (auto *DAI = dyn_cast(Inst)) { if (DAI->getOperand() == ASI) { replaceDestroy(DAI, RunningVal); } continue; } // Remove deallocation. if (DeallocStackInst *DSI = dyn_cast(Inst)) { if (DSI->getOperand() == ASI) { Inst->eraseFromParent(); NumInstRemoved++; // No need to continue scanning after deallocation. break; } } } } void StackAllocationPromoter::addBlockArguments(BlockSet &PhiBlocks) { DEBUG(llvm::dbgs() << "*** Adding new block arguments.\n"); SILModule &M = ASI->getModule(); for (auto Block : PhiBlocks) new (M) SILArgument(Block, ASI->getElementType()); } SILValue StackAllocationPromoter::getLiveOutValue(BlockSet &PhiBlocks, SILBasicBlock *StartBB) { DEBUG(llvm::dbgs() << "*** Searching for a value definition.\n"); // Walk the Dom tree in search of a defining value: for (DomTreeNode *Node = DT->getNode(StartBB); Node; Node = Node->getIDom()) { SILBasicBlock *BB = Node->getBlock(); // If there is a store (that must come after the phi), use its value. BlockToInstMap::iterator it = LastStoreInBlock.find(BB); if (it != LastStoreInBlock.end()) if (StoreInst *St = dyn_cast_or_null(it->second)) { DEBUG(llvm::dbgs() << "*** Found Store def " << *St->getSrc()); return St->getSrc(); } // If there is a Phi definition in this block: if (PhiBlocks.count(BB)) { // Return the dummy instruction that represents the new value that we will // add to the basic block. SILValue Phi = BB->getBBArg(BB->getNumBBArg()-1); DEBUG(llvm::dbgs() << "*** Found a dummy Phi def " << *Phi); return Phi; } // Move to the next dominating block. DEBUG(llvm::dbgs() << "*** Walking up the iDOM.\n"); } DEBUG(llvm::dbgs() << "*** Could not find a Def. Using Undef.\n"); return SILUndef::get(ASI->getElementType(), ASI->getModule()); } SILValue StackAllocationPromoter::getLiveInValue(BlockSet &PhiBlocks, SILBasicBlock *BB) { // First, check if there is a Phi value in the current block. We know that // our loads happen before stores, so we need to first check for Phi nodes // in the first block, but stores first in all other stores in the idom // chain. if (PhiBlocks.count(BB)) { DEBUG(llvm::dbgs() << "*** Found a local Phi definition.\n"); return BB->getBBArg(BB->getNumBBArg()-1); } if (BB->pred_empty() || !DT->getNode(BB)) return SILUndef::get(ASI->getElementType(), ASI->getModule()); // No phi for this value in this block means that the value flowing // out of the immediate dominator reaches here. DomTreeNode *IDom = DT->getNode(BB)->getIDom(); assert(IDom && "Attempt to get live-in value for alloc_stack in entry block!"); return getLiveOutValue(PhiBlocks, IDom->getBlock()); } void StackAllocationPromoter::fixPhiPredBlock(BlockSet &PhiBlocks, SILBasicBlock *Dest, SILBasicBlock *Pred) { TermInst *TI = Pred->getTerminator(); DEBUG(llvm::dbgs() << "*** Fixing the terminator " << TI << ".\n"); SILValue Def = getLiveOutValue(PhiBlocks, Pred); DEBUG(llvm::dbgs() << "*** Found the definition: " << *Def); addArgumentToBranch(Def, Dest, TI); TI->eraseFromParent(); } void StackAllocationPromoter::fixBranchesAndUses(BlockSet &PhiBlocks) { // First update uses of the value. SmallVector collectedLoads; for (auto UI = ASI->use_begin(), E = ASI->use_end(); UI != E;) { auto *Inst = UI->getUser(); UI++; bool removedUser = false; collectedLoads.clear(); collectLoads(Inst, collectedLoads); for (LoadInst *LI : collectedLoads) { SILValue Def; // If this block has no predecessors then nothing dominates it and // the instruction is unreachable. If the instruction we're // examining is a value, replace it with undef. Either way, delete // the instruction and move on. SILBasicBlock *BB = LI->getParent(); Def = getLiveInValue(PhiBlocks, BB); DEBUG(llvm::dbgs() << "*** Replacing " << *LI << " with Def " << *Def); // Replace the load with the definition that we found. replaceLoad(LI, Def, ASI); removedUser = true; NumInstRemoved++; } if (removedUser) continue; // If this block has no predecessors then nothing dominates it and // the instruction is unreachable. Delete the instruction and move // on. SILBasicBlock *BB = Inst->getParent(); if (auto *DVAI = dyn_cast(Inst)) { // Replace DebugValueAddr with DebugValue. SILValue Def = getLiveInValue(PhiBlocks, BB); promoteDebugValueAddr(DVAI, Def, B); NumInstRemoved++; continue; } // Replace destroys with a release of the value. if (auto *DAI = dyn_cast(Inst)) { SILValue Def = getLiveInValue(PhiBlocks, BB); replaceDestroy(DAI, Def); continue; } } // Now that all of the uses are fixed we can fix the branches that point // to the blocks with the added arguments. // For each Block with a new Phi argument: for (auto Block : PhiBlocks) { // Fix all predecessors. for (auto PBBI = Block->getPreds().begin(), E = Block->getPreds().end(); PBBI != E;) { auto *PBB = *PBBI; ++PBBI; assert(PBB && "Invalid block!"); fixPhiPredBlock(PhiBlocks, Block, PBB); } } } void StackAllocationPromoter::pruneAllocStackUsage() { DEBUG(llvm::dbgs() << "*** Pruning : " << *ASI); BlockSet Blocks; // Insert all of the blocks that ASI is live in. for (auto UI = ASI->use_begin(), E = ASI->use_end(); UI != E; ++UI) Blocks.insert(UI->getUser()->getParent()); // Clear AllocStack state. LastStoreInBlock.clear(); for (auto Block : Blocks) { StoreInst *SI = promoteAllocationInBlock(Block); LastStoreInBlock[Block] = SI; } DEBUG(llvm::dbgs() << "*** Finished pruning : " << *ASI); } /// Compute the dominator tree levels for DT. static void computeDomTreeLevels(DominanceInfo *DT, DomTreeLevelMap &DomTreeLevels) { // TODO: This should happen once per function. SmallVector Worklist; DomTreeNode *Root = DT->getRootNode(); DomTreeLevels[Root] = 0; Worklist.push_back(Root); while (!Worklist.empty()) { DomTreeNode *Node = Worklist.pop_back_val(); unsigned ChildLevel = DomTreeLevels[Node] + 1; for (auto CI = Node->begin(), CE = Node->end(); CI != CE; ++CI) { DomTreeLevels[*CI] = ChildLevel; Worklist.push_back(*CI); } } } void StackAllocationPromoter::promoteAllocationToPhi() { DEBUG(llvm::dbgs() << "*** Placing Phis for : " << *ASI); // A list of blocks that will require new Phi values. BlockSet PhiBlocks; // The "piggy-bank" data-structure that we use for processing the dom-tree // bottom-up. NodePriorityQueue PQ; // Collect all of the stores into the AllocStack. We know that at this point // we have at most one store per block. for (auto UI = ASI->use_begin(), E = ASI->use_end(); UI != E; ++UI) { SILInstruction *II = UI->getUser(); // We need to place Phis for this block. if (isa(II)) { // If the block is in the dom tree (dominated by the entry block). if (DomTreeNode *Node = DT->getNode(II->getParent())) PQ.push(std::make_pair(Node, DomTreeLevels[Node])); } } DEBUG(llvm::dbgs() << "*** Found: " << PQ.size() << " Defs\n"); // A list of nodes for which we already calculated the dominator frontier. llvm::SmallPtrSet Visited; SmallVector Worklist; // Scan all of the definitions in the function bottom-up using the priority // queue. while (!PQ.empty()) { DomTreeNodePair RootPair = PQ.top(); PQ.pop(); DomTreeNode *Root = RootPair.first; unsigned RootLevel = RootPair.second; // Walk all dom tree children of Root, inspecting their successors. Only // J-edges, whose target level is at most Root's level are added to the // dominance frontier. Worklist.clear(); Worklist.push_back(Root); while (!Worklist.empty()) { DomTreeNode *Node = Worklist.pop_back_val(); SILBasicBlock *BB = Node->getBlock(); // For all successors of the node: for (auto &Succ : BB->getSuccessors()) { DomTreeNode *SuccNode = DT->getNode(Succ); // Skip D-edges (edges that are dom-tree edges). if (SuccNode->getIDom() == Node) continue; // Ignore J-edges that point to nodes that are not smaller or equal // to the root level. unsigned SuccLevel = DomTreeLevels[SuccNode]; if (SuccLevel > RootLevel) continue; // Ignore visited nodes. if (!Visited.insert(SuccNode).second) continue; // If the new PHInode is not dominated by the allocation then it's dead. if (!DT->dominates(ASI->getParent(), SuccNode->getBlock())) continue; // If the new PHInode is properly dominated by the deallocation then it // is obviously a dead PHInode, so we don't need to insert it. if (DSI && DT->properlyDominates(DSI->getParent(), SuccNode->getBlock())) continue; // The successor node is a new PHINode. If this is a new PHI node // then it may require additional definitions, so add it to the PQ. if (PhiBlocks.insert(Succ).second) PQ.push(std::make_pair(SuccNode, SuccLevel)); } // Add the children in the dom-tree to the worklist. for (auto CI = Node->begin(), CE = Node->end(); CI != CE; ++CI) if (!Visited.count(*CI)) Worklist.push_back(*CI); } } DEBUG(llvm::dbgs() << "*** Found: " << PhiBlocks.size() << " new PHIs\n"); NumPhiPlaced += PhiBlocks.size(); // At this point we calculated the locations of all of the new Phi values. // Next, add the Phi values and promote all of the loads and stores into the // new locations. // Replace the dummy values with new block arguments. addBlockArguments(PhiBlocks); // Hook up the Phi nodes, loads, and debug_value_addr with incoming values. fixBranchesAndUses(PhiBlocks); DEBUG(llvm::dbgs() << "*** Finished placing Phis ***\n"); } void StackAllocationPromoter::run() { // Reduce the number of load/stores in the function to minimum. // After this phase we are left with up to one load and store // per block and the last store is recorded. pruneAllocStackUsage(); // Replace AllocStacks with Phi-nodes. promoteAllocationToPhi(); } bool MemoryToRegisters::run() { bool Changed = false; Changed = splitAllCriticalEdges(F, true, DT, nullptr); // Compute dominator tree node levels for the function. DomTreeLevelMap DomTreeLevels; computeDomTreeLevels(DT, DomTreeLevels); for (auto &BB : F) { auto I = BB.begin(), E = BB.end(); while (I != E) { SILInstruction *Inst = &*I; AllocStackInst *ASI = dyn_cast(Inst); if (!ASI) { ++I; continue; } DEBUG(llvm::dbgs() << "*** Memory to register looking at: " << *I); NumAllocStackFound++; // Don't handle captured AllocStacks. bool inSingleBlock = false; if (isCaptured(ASI, inSingleBlock)) { NumAllocStackCaptured++; ++I; continue; } // Remove write-only AllocStacks. if (isWriteOnlyAllocation(ASI)) { eraseUsesOfInstruction(ASI); DEBUG(llvm::dbgs() << "*** Deleting store-only AllocStack: " << *ASI); I++; ASI->eraseFromParent(); Changed = true; NumInstRemoved++; continue; } // For AllocStacks that are only used within a single basic blocks, use // the linear sweep to remove the AllocStack. if (inSingleBlock) { removeSingleBlockAllocation(ASI); DEBUG(llvm::dbgs() << "*** Deleting single block AllocStackInst: " << *ASI); I++; ASI->eraseFromParent(); NumInstRemoved++; Changed = true; continue; } DEBUG(llvm::dbgs() << "*** Need to insert Phis for " << *ASI); // Promote this allocation. StackAllocationPromoter(ASI, DT, DomTreeLevels, B).run(); // Make sure that all of the allocations were promoted into registers. assert(isWriteOnlyAllocation(ASI, /* Promoted =*/ true) && "Non-write uses left behind"); // ... and erase the allocation. eraseUsesOfInstruction(ASI); I++; ASI->eraseFromParent(); NumInstRemoved++; Changed = true; } } return Changed; } namespace { class SILMem2Reg : public SILFunctionTransform { void run() override { SILFunction *F = getFunction(); DEBUG(llvm::dbgs() << "** Mem2Reg on function: " << F->getName() <<" **\n"); DominanceAnalysis* DA = PM->getAnalysis(); bool Changed = MemoryToRegisters(*F, DA->get(F)).run(); if (Changed) invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions); } StringRef getName() override { return "SIL Mem2Reg"; } }; } // end anonymous namespace SILTransform *swift::createMem2Reg() { return new SILMem2Reg(); }