diff options
-rw-r--r-- | lib/Transforms/Scalar/PRE.cpp | 629 |
1 files changed, 0 insertions, 629 deletions
diff --git a/lib/Transforms/Scalar/PRE.cpp b/lib/Transforms/Scalar/PRE.cpp deleted file mode 100644 index 1ee923a442..0000000000 --- a/lib/Transforms/Scalar/PRE.cpp +++ /dev/null @@ -1,629 +0,0 @@ -//===- PRE.cpp - Partial Redundancy Elimination ---------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file implements the well-known Partial Redundancy Elimination -// optimization, using an SSA formulation based on e-paths. See this paper for -// more information: -// -// E-path_PRE: partial redundancy elimination made easy -// By: Dhananjay M. Dhamdhere In: ACM SIGPLAN Notices. Vol 37, #8, 2002 -// http://doi.acm.org/10.1145/596992.597004 -// -// This file actually implements a sparse version of the algorithm, using SSA -// and CFG properties instead of bit-vectors. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Pass.h" -#include "llvm/Function.h" -#include "llvm/Type.h" -#include "llvm/Instructions.h" -#include "llvm/Support/CFG.h" -#include "llvm/Analysis/Dominators.h" -#include "llvm/Analysis/PostDominators.h" -#include "llvm/Analysis/ValueNumbering.h" -#include "llvm/Transforms/Scalar.h" -#include "llvm/Support/Debug.h" -#include "llvm/ADT/DepthFirstIterator.h" -#include "llvm/ADT/PostOrderIterator.h" -#include "llvm/ADT/Statistic.h" -#include "llvm/ADT/hash_set" -#include "llvm/ADT/hash_map" -using namespace llvm; - -namespace { - Statistic<> NumExprsEliminated("pre", "Number of expressions constantified"); - Statistic<> NumRedundant ("pre", "Number of redundant exprs eliminated"); - Statistic<> NumInserted ("pre", "Number of expressions inserted"); - - struct PRE : public FunctionPass { - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AU.addRequiredID(BreakCriticalEdgesID); // No critical edges for now! - AU.addRequired<PostDominatorTree>(); - AU.addRequired<PostDominanceFrontier>(); - AU.addRequired<DominatorSet>(); - AU.addRequired<DominatorTree>(); - AU.addRequired<DominanceFrontier>(); - AU.addRequired<ValueNumbering>(); - } - virtual bool runOnFunction(Function &F); - - private: - // Block information - Map basic blocks in a function back and forth to - // unsigned integers. - std::vector<BasicBlock*> BlockMapping; - hash_map<BasicBlock*, unsigned> BlockNumbering; - - // ProcessedExpressions - Keep track of which expressions have already been - // processed. - hash_set<Instruction*> ProcessedExpressions; - - // Provide access to the various analyses used... - DominatorSet *DS; - DominatorTree *DT; PostDominatorTree *PDT; - DominanceFrontier *DF; PostDominanceFrontier *PDF; - ValueNumbering *VN; - - // AvailableBlocks - Contain a mapping of blocks with available expression - // values to the expression value itself. This can be used as an efficient - // way to find out if the expression is available in the block, and if so, - // which version to use. This map is only used while processing a single - // expression. - // - typedef hash_map<BasicBlock*, Instruction*> AvailableBlocksTy; - AvailableBlocksTy AvailableBlocks; - - bool ProcessBlock(BasicBlock *BB); - - // Anticipatibility calculation... - void MarkPostDominatingBlocksAnticipatible(PostDominatorTree::Node *N, - std::vector<char> &AntBlocks, - Instruction *Occurrence); - void CalculateAnticipatiblityForOccurrence(unsigned BlockNo, - std::vector<char> &AntBlocks, - Instruction *Occurrence); - void CalculateAnticipatibleBlocks(const std::map<unsigned, Instruction*> &D, - std::vector<char> &AnticipatibleBlocks); - - // PRE for an expression - void MarkOccurrenceAvailableInAllDominatedBlocks(Instruction *Occurrence, - BasicBlock *StartBlock); - void ReplaceDominatedAvailableOccurrencesWith(Instruction *NewOcc, - DominatorTree::Node *N); - bool ProcessExpression(Instruction *I); - }; - - RegisterOpt<PRE> Z("pre", "Partial Redundancy Elimination"); -} - -FunctionPass* llvm::createPREPass() { return new PRE(); } - -bool PRE::runOnFunction(Function &F) { - VN = &getAnalysis<ValueNumbering>(); - DS = &getAnalysis<DominatorSet>(); - DT = &getAnalysis<DominatorTree>(); - DF = &getAnalysis<DominanceFrontier>(); - PDT = &getAnalysis<PostDominatorTree>(); - PDF = &getAnalysis<PostDominanceFrontier>(); - - DEBUG(std::cerr << "\n*** Running PRE on func '" << F.getName() << "'...\n"); - - // Number the basic blocks based on a reverse post-order traversal of the CFG - // so that all predecessors of a block (ignoring back edges) are visited - // before a block is visited. - // - BlockMapping.reserve(F.size()); - { - ReversePostOrderTraversal<Function*> RPOT(&F); - DEBUG(std::cerr << "Block order: "); - for (ReversePostOrderTraversal<Function*>::rpo_iterator I = RPOT.begin(), - E = RPOT.end(); I != E; ++I) { - // Keep track of mapping... - BasicBlock *BB = *I; - BlockNumbering.insert(std::make_pair(BB, BlockMapping.size())); - BlockMapping.push_back(BB); - DEBUG(std::cerr << BB->getName() << " "); - } - DEBUG(std::cerr << "\n"); - } - - // Traverse the current function depth-first in dominator-tree order. This - // ensures that we see all definitions before their uses (except for PHI - // nodes), allowing us to hoist dependent expressions correctly. - bool Changed = false; - for (unsigned i = 0, e = BlockMapping.size(); i != e; ++i) - Changed |= ProcessBlock(BlockMapping[i]); - - // Free memory - BlockMapping.clear(); - BlockNumbering.clear(); - ProcessedExpressions.clear(); - return Changed; -} - - -// ProcessBlock - Process any expressions first seen in this block... -// -bool PRE::ProcessBlock(BasicBlock *BB) { - bool Changed = false; - - // DISABLED: This pass invalidates iterators and then uses them. - return false; - - // PRE expressions first defined in this block... - for (BasicBlock::iterator I = BB->begin(); I != BB->end(); ) - if (ProcessExpression(I++)) - Changed = true; - - return Changed; -} - -void PRE::MarkPostDominatingBlocksAnticipatible(PostDominatorTree::Node *N, - std::vector<char> &AntBlocks, - Instruction *Occurrence) { - unsigned BlockNo = BlockNumbering[N->getBlock()]; - - if (AntBlocks[BlockNo]) return; // Already known to be anticipatible?? - - // Check to see if any of the operands are defined in this block, if so, the - // entry of this block does not anticipate the expression. This computes - // "transparency". - for (unsigned i = 0, e = Occurrence->getNumOperands(); i != e; ++i) - if (Instruction *I = dyn_cast<Instruction>(Occurrence->getOperand(i))) - if (I->getParent() == N->getBlock()) // Operand is defined in this block! - return; - - if (isa<LoadInst>(Occurrence)) - return; // FIXME: compute transparency for load instructions using AA - - // Insert block into AntBlocks list... - AntBlocks[BlockNo] = true; - - for (PostDominatorTree::Node::iterator I = N->begin(), E = N->end(); I != E; - ++I) - MarkPostDominatingBlocksAnticipatible(*I, AntBlocks, Occurrence); -} - -void PRE::CalculateAnticipatiblityForOccurrence(unsigned BlockNo, - std::vector<char> &AntBlocks, - Instruction *Occurrence) { - if (AntBlocks[BlockNo]) return; // Block already anticipatible! - - BasicBlock *BB = BlockMapping[BlockNo]; - - // For each occurrence, mark all post-dominated blocks as anticipatible... - MarkPostDominatingBlocksAnticipatible(PDT->getNode(BB), AntBlocks, - Occurrence); - - // Next, mark any blocks in the post-dominance frontier as anticipatible iff - // all successors are anticipatible. - // - PostDominanceFrontier::iterator PDFI = PDF->find(BB); - if (PDFI != DF->end()) - for (std::set<BasicBlock*>::iterator DI = PDFI->second.begin(); - DI != PDFI->second.end(); ++DI) { - BasicBlock *PDFBlock = *DI; - bool AllSuccessorsAnticipatible = true; - for (succ_iterator SI = succ_begin(PDFBlock), SE = succ_end(PDFBlock); - SI != SE; ++SI) - if (!AntBlocks[BlockNumbering[*SI]]) { - AllSuccessorsAnticipatible = false; - break; - } - - if (AllSuccessorsAnticipatible) - CalculateAnticipatiblityForOccurrence(BlockNumbering[PDFBlock], - AntBlocks, Occurrence); - } -} - - -void PRE::CalculateAnticipatibleBlocks(const std::map<unsigned, - Instruction*> &Defs, - std::vector<char> &AntBlocks) { - // Initialize to zeros... - AntBlocks.resize(BlockMapping.size()); - - // Loop over all of the expressions... - for (std::map<unsigned, Instruction*>::const_iterator I = Defs.begin(), - E = Defs.end(); I != E; ++I) - CalculateAnticipatiblityForOccurrence(I->first, AntBlocks, I->second); -} - -/// MarkOccurrenceAvailableInAllDominatedBlocks - Add entries to AvailableBlocks -/// for all nodes dominated by the occurrence to indicate that it is now the -/// available occurrence to use in any of these blocks. -/// -void PRE::MarkOccurrenceAvailableInAllDominatedBlocks(Instruction *Occurrence, - BasicBlock *BB) { - // FIXME: There are much more efficient ways to get the blocks dominated - // by a block. Use them. - // - DominatorTree::Node *N = DT->getNode(Occurrence->getParent()); - for (df_iterator<DominatorTree::Node*> DI = df_begin(N), E = df_end(N); - DI != E; ++DI) - AvailableBlocks[(*DI)->getBlock()] = Occurrence; -} - -/// ReplaceDominatedAvailableOccurrencesWith - This loops over the region -/// dominated by N, replacing any available expressions with NewOcc. -void PRE::ReplaceDominatedAvailableOccurrencesWith(Instruction *NewOcc, - DominatorTree::Node *N) { - BasicBlock *BB = N->getBlock(); - Instruction *&ExistingAvailableVal = AvailableBlocks[BB]; - - // If there isn't a definition already active in this node, make this the new - // active definition... - if (ExistingAvailableVal == 0) { - ExistingAvailableVal = NewOcc; - - for (DominatorTree::Node::iterator I = N->begin(), E = N->end(); I != E;++I) - ReplaceDominatedAvailableOccurrencesWith(NewOcc, *I); - } else { - // If there is already an active definition in this block, replace it with - // NewOcc, and force it into all dominated blocks. - DEBUG(std::cerr << " Replacing dominated occ %" - << ExistingAvailableVal->getName() << " with %" << NewOcc->getName() - << "\n"); - assert(ExistingAvailableVal != NewOcc && "NewOcc already inserted??"); - ExistingAvailableVal->replaceAllUsesWith(NewOcc); - ++NumRedundant; - - assert(ExistingAvailableVal->getParent() == BB && - "OldOcc not defined in current block?"); - BB->getInstList().erase(ExistingAvailableVal); - - // Mark NewOCC as the Available expression in all blocks dominated by BB - for (df_iterator<DominatorTree::Node*> DI = df_begin(N), E = df_end(N); - DI != E; ++DI) - AvailableBlocks[(*DI)->getBlock()] = NewOcc; - } -} - - -/// ProcessExpression - Given an expression (instruction) process the -/// instruction to remove any partial redundancies induced by equivalent -/// computations. Note that we only need to PRE each expression once, so we -/// keep track of whether an expression has been PRE'd already, and don't PRE an -/// expression again. Expressions may be seen multiple times because process -/// the entire equivalence class at once, which may leave expressions later in -/// the control path. -/// -bool PRE::ProcessExpression(Instruction *Expr) { - if (Expr->mayWriteToMemory() || Expr->getType() == Type::VoidTy || - isa<PHINode>(Expr)) - return false; // Cannot move expression - if (ProcessedExpressions.count(Expr)) return false; // Already processed. - - // Ok, this is the first time we have seen the expression. Build a set of - // equivalent expressions using SSA def/use information. We consider - // expressions to be equivalent if they are the same opcode and have - // equivalent operands. As a special case for SSA, values produced by PHI - // nodes are considered to be equivalent to all of their operands. - // - std::vector<Value*> Values; - VN->getEqualNumberNodes(Expr, Values); - -#if 0 - // FIXME: This should handle PHI nodes correctly. To do this, we need to - // consider expressions of the following form equivalent to this set of - // expressions: - // - // If an operand is a PHI node, add any occurrences of the expression with the - // PHI operand replaced with the PHI node operands. This is only valid if the - // PHI operand occurrences exist in blocks post-dominated by the incoming edge - // of the PHI node. -#endif - - // We have to be careful to handle expression definitions which dominated by - // other expressions. These can be directly eliminated in favor of their - // dominating value. Keep track of which blocks contain definitions (the key) - // and if a block contains a definition, which instruction it is. - // - std::map<unsigned, Instruction*> Definitions; - Definitions.insert(std::make_pair(BlockNumbering[Expr->getParent()], Expr)); - - bool Changed = false; - - // Look at all of the equal values. If any of the values is not an - // instruction, replace all other expressions immediately with it (it must be - // an argument or a constant or something). Otherwise, convert the list of - // values into a list of expression (instruction) definitions ordering - // according to their dominator tree ordering. - // - Value *NonInstValue = 0; - for (unsigned i = 0, e = Values.size(); i != e; ++i) - if (Instruction *I = dyn_cast<Instruction>(Values[i])) { - Instruction *&BlockInst = Definitions[BlockNumbering[I->getParent()]]; - if (BlockInst && BlockInst != I) { // Eliminate direct redundancy - if (DS->dominates(I, BlockInst)) { // I dom BlockInst - BlockInst->replaceAllUsesWith(I); - BlockInst->getParent()->getInstList().erase(BlockInst); - } else { // BlockInst dom I - I->replaceAllUsesWith(BlockInst); - I->getParent()->getInstList().erase(I); - I = BlockInst; - } - ++NumRedundant; - } - BlockInst = I; - } else { - NonInstValue = Values[i]; - } - - std::vector<Value*>().swap(Values); // Done with the values list - - if (NonInstValue) { - // This is the good, though unlikely, case where we find out that this - // expression is equal to a constant or argument directly. We can replace - // this and all of the other equivalent instructions with the value - // directly. - // - for (std::map<unsigned, Instruction*>::iterator I = Definitions.begin(), - E = Definitions.end(); I != E; ++I) { - Instruction *Inst = I->second; - // Replace the value with the specified non-instruction value. - Inst->replaceAllUsesWith(NonInstValue); // Fixup any uses - Inst->getParent()->getInstList().erase(Inst); // Erase the instruction - } - NumExprsEliminated += Definitions.size(); - return true; // Program modified! - } - - // There are no expressions equal to this one. Exit early. - assert(!Definitions.empty() && "no equal expressions??"); -#if 0 - if (Definitions.size() == 1) { - ProcessedExpressions.insert(Definitions.begin()->second); - return Changed; - } -#endif - DEBUG(std::cerr << "\n====--- Expression: " << *Expr); - const Type *ExprType = Expr->getType(); - - // AnticipatibleBlocks - Blocks where the current expression is anticipatible. - // This is logically std::vector<bool> but using 'char' for performance. - std::vector<char> AnticipatibleBlocks; - - // Calculate all of the blocks which the current expression is anticipatible. - CalculateAnticipatibleBlocks(Definitions, AnticipatibleBlocks); - - // Print out anticipatible blocks... - DEBUG(std::cerr << "AntBlocks: "; - for (unsigned i = 0, e = AnticipatibleBlocks.size(); i != e; ++i) - if (AnticipatibleBlocks[i]) - std::cerr << BlockMapping[i]->getName() <<" "; - std::cerr << "\n";); - - - - // AvailabilityFrontier - Calculates the availability frontier for the current - // expression. The availability frontier contains the blocks on the dominance - // frontier of the current available expressions, iff they anticipate a - // definition of the expression. - hash_set<unsigned> AvailabilityFrontier; - - Instruction *NonPHIOccurrence = 0; - - while (!Definitions.empty() || !AvailabilityFrontier.empty()) { - if (!Definitions.empty() && - (AvailabilityFrontier.empty() || - Definitions.begin()->first < *AvailabilityFrontier.begin())) { - Instruction *Occurrence = Definitions.begin()->second; - BasicBlock *BB = Occurrence->getParent(); - Definitions.erase(Definitions.begin()); - - DEBUG(std::cerr << "PROCESSING Occurrence: " << *Occurrence); - - // Check to see if there is already an incoming value for this block... - AvailableBlocksTy::iterator LBI = AvailableBlocks.find(BB); - if (LBI != AvailableBlocks.end()) { - // Yes, there is a dominating definition for this block. Replace this - // occurrence with the incoming value. - if (LBI->second != Occurrence) { - DEBUG(std::cerr << " replacing with: " << *LBI->second); - Occurrence->replaceAllUsesWith(LBI->second); - BB->getInstList().erase(Occurrence); // Delete instruction - ++NumRedundant; - } - } else { - ProcessedExpressions.insert(Occurrence); - if (!isa<PHINode>(Occurrence)) - NonPHIOccurrence = Occurrence; // Keep an occurrence of this expr - - // Okay, there is no incoming value for this block, so this expression - // is a new definition that is good for this block and all blocks - // dominated by it. Add this information to the AvailableBlocks map. - // - MarkOccurrenceAvailableInAllDominatedBlocks(Occurrence, BB); - - // Update the dominance frontier for the definitions so far... if a node - // in the dominator frontier now has all of its predecessors available, - // and the block is in an anticipatible region, we can insert a PHI node - // in that block. - DominanceFrontier::iterator DFI = DF->find(BB); - if (DFI != DF->end()) { - for (std::set<BasicBlock*>::iterator DI = DFI->second.begin(); - DI != DFI->second.end(); ++DI) { - BasicBlock *DFBlock = *DI; - unsigned DFBlockID = BlockNumbering[DFBlock]; - if (AnticipatibleBlocks[DFBlockID]) { - // Check to see if any of the predecessors of this block on the - // frontier are not available... - bool AnyNotAvailable = false; - for (pred_iterator PI = pred_begin(DFBlock), - PE = pred_end(DFBlock); PI != PE; ++PI) - if (!AvailableBlocks.count(*PI)) { - AnyNotAvailable = true; - break; - } - - // If any predecessor blocks are not available, add the node to - // the current expression dominance frontier. - if (AnyNotAvailable) { - AvailabilityFrontier.insert(DFBlockID); - } else { - // This block is no longer in the availability frontier, it IS - // available. - AvailabilityFrontier.erase(DFBlockID); - - // If all of the predecessor blocks are available (and the block - // anticipates a definition along the path to the exit), we need - // to insert a new PHI node in this block. This block serves as - // a new definition for the expression, extending the available - // region. - // - PHINode *PN = new PHINode(ExprType, Expr->getName()+".pre", - DFBlock->begin()); - ProcessedExpressions.insert(PN); - - DEBUG(std::cerr << " INSERTING PHI on frontier: " << *PN); - - // Add the incoming blocks for the PHI node - for (pred_iterator PI = pred_begin(DFBlock), - PE = pred_end(DFBlock); PI != PE; ++PI) - if (*PI != DFBlock) - PN->addIncoming(AvailableBlocks[*PI], *PI); - else // edge from the current block - PN->addIncoming(PN, DFBlock); - - Instruction *&BlockOcc = Definitions[DFBlockID]; - if (BlockOcc) { - DEBUG(std::cerr <<" PHI superceeds occurrence: "<< - *BlockOcc); - BlockOcc->replaceAllUsesWith(PN); - BlockOcc->getParent()->getInstList().erase(BlockOcc); - ++NumRedundant; - } - BlockOcc = PN; - } - } - } - } - } - - } else { - // Otherwise we must be looking at a node in the availability frontier! - unsigned AFBlockID = *AvailabilityFrontier.begin(); - AvailabilityFrontier.erase(AvailabilityFrontier.begin()); - BasicBlock *AFBlock = BlockMapping[AFBlockID]; - - // We eliminate the partial redundancy on this frontier by inserting a PHI - // node into this block, merging any incoming available versions into the - // PHI and inserting a new computation into predecessors without an - // incoming value. Note that we would have to insert the expression on - // the edge if the predecessor didn't anticipate the expression and we - // didn't break critical edges. - // - PHINode *PN = new PHINode(ExprType, Expr->getName()+".PRE", - AFBlock->begin()); - DEBUG(std::cerr << "INSERTING PHI for PR: " << *PN); - - // If there is a pending occurrence in this block, make sure to replace it - // with the PHI node... - std::map<unsigned, Instruction*>::iterator EDFI = - Definitions.find(AFBlockID); - if (EDFI != Definitions.end()) { - // There is already an occurrence in this block. Replace it with PN and - // remove it. - Instruction *OldOcc = EDFI->second; - DEBUG(std::cerr << " Replaces occurrence: " << *OldOcc); - OldOcc->replaceAllUsesWith(PN); - AFBlock->getInstList().erase(OldOcc); - Definitions.erase(EDFI); - ++NumRedundant; - } - - for (pred_iterator PI = pred_begin(AFBlock), PE = pred_end(AFBlock); - PI != PE; ++PI) { - BasicBlock *Pred = *PI; - AvailableBlocksTy::iterator LBI = AvailableBlocks.find(Pred); - if (LBI != AvailableBlocks.end()) { // If there is a available value - PN->addIncoming(LBI->second, Pred); // for this pred, use it. - } else { // No available value yet... - unsigned PredID = BlockNumbering[Pred]; - - // Is the predecessor the same block that we inserted the PHI into? - // (self loop) - if (Pred == AFBlock) { - // Yes, reuse the incoming value here... - PN->addIncoming(PN, Pred); - } else { - // No, we must insert a new computation into this block and add it - // to the definitions list... - assert(NonPHIOccurrence && "No non-phi occurrences seen so far???"); - Instruction *New = NonPHIOccurrence->clone(); - New->setName(NonPHIOccurrence->getName() + ".PRE-inserted"); - ProcessedExpressions.insert(New); - - DEBUG(std::cerr << " INSERTING OCCURRRENCE: " << *New); - - // Insert it into the bottom of the predecessor, right before the - // terminator instruction... - Pred->getInstList().insert(Pred->getTerminator(), New); - - // Make this block be the available definition for any blocks it - // dominates. The ONLY case that this can affect more than just the - // block itself is when we are moving a computation to a loop - // header. In all other cases, because we don't have critical - // edges, the node is guaranteed to only dominate itself. - // - ReplaceDominatedAvailableOccurrencesWith(New, DT->getNode(Pred)); - - // Add it as an incoming value on this edge to the PHI node - PN->addIncoming(New, Pred); - NonPHIOccurrence = New; - NumInserted++; - } - } - } - - // Find out if there is already an available value in this block. If so, - // we need to replace the available value with the PHI node. This can - // only happen when we just inserted a PHI node on a backedge. - // - AvailableBlocksTy::iterator LBBlockAvailableValIt = - AvailableBlocks.find(AFBlock); - if (LBBlockAvailableValIt != AvailableBlocks.end()) { - if (LBBlockAvailableValIt->second->getParent() == AFBlock) { - Instruction *OldVal = LBBlockAvailableValIt->second; - OldVal->replaceAllUsesWith(PN); // Use the new PHI node now - ++NumRedundant; - DEBUG(std::cerr << " PHI replaces available value: %" - << OldVal->getName() << "\n"); - - // Loop over all of the blocks dominated by this PHI node, and change - // the AvailableBlocks entries to be the PHI node instead of the old - // instruction. - MarkOccurrenceAvailableInAllDominatedBlocks(PN, AFBlock); - - AFBlock->getInstList().erase(OldVal); // Delete old instruction! - - // The resultant PHI node is a new definition of the value! - Definitions.insert(std::make_pair(AFBlockID, PN)); - } else { - // If the value is not defined in this block, that means that an - // inserted occurrence in a predecessor is now the live value for the - // region (occurs when hoisting loop invariants, f.e.). In this case, - // the PHI node should actually just be removed. - assert(PN->use_empty() && "No uses should exist for dead PHI node!"); - PN->getParent()->getInstList().erase(PN); - } - } else { - // The resultant PHI node is a new definition of the value! - Definitions.insert(std::make_pair(AFBlockID, PN)); - } - } - } - - AvailableBlocks.clear(); - - return Changed; -} - |