|  | //===- DomTreeUpdater.cpp - DomTree/Post DomTree Updater --------*- C++ -*-===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // This file implements the DomTreeUpdater class, which provides a uniform way | 
|  | // to update dominator tree related data structures. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "llvm/Analysis/DomTreeUpdater.h" | 
|  | #include "llvm/ADT/SmallSet.h" | 
|  | #include "llvm/Analysis/PostDominators.h" | 
|  | #include "llvm/IR/Dominators.h" | 
|  | #include "llvm/Support/GenericDomTree.h" | 
|  | #include <algorithm> | 
|  | #include <functional> | 
|  | #include <utility> | 
|  |  | 
|  | namespace llvm { | 
|  |  | 
|  | bool DomTreeUpdater::isUpdateValid( | 
|  | const DominatorTree::UpdateType Update) const { | 
|  | const auto *From = Update.getFrom(); | 
|  | const auto *To = Update.getTo(); | 
|  | const auto Kind = Update.getKind(); | 
|  |  | 
|  | // Discard updates by inspecting the current state of successors of From. | 
|  | // Since isUpdateValid() must be called *after* the Terminator of From is | 
|  | // altered we can determine if the update is unnecessary for batch updates | 
|  | // or invalid for a single update. | 
|  | const bool HasEdge = llvm::any_of( | 
|  | successors(From), [To](const BasicBlock *B) { return B == To; }); | 
|  |  | 
|  | // If the IR does not match the update, | 
|  | // 1. In batch updates, this update is unnecessary. | 
|  | // 2. When called by insertEdge*()/deleteEdge*(), this update is invalid. | 
|  | // Edge does not exist in IR. | 
|  | if (Kind == DominatorTree::Insert && !HasEdge) | 
|  | return false; | 
|  |  | 
|  | // Edge exists in IR. | 
|  | if (Kind == DominatorTree::Delete && HasEdge) | 
|  | return false; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool DomTreeUpdater::isSelfDominance( | 
|  | const DominatorTree::UpdateType Update) const { | 
|  | // Won't affect DomTree and PostDomTree. | 
|  | return Update.getFrom() == Update.getTo(); | 
|  | } | 
|  |  | 
|  | void DomTreeUpdater::applyDomTreeUpdates() { | 
|  | // No pending DomTreeUpdates. | 
|  | if (Strategy != UpdateStrategy::Lazy || !DT) | 
|  | return; | 
|  |  | 
|  | // Only apply updates not are applied by DomTree. | 
|  | if (hasPendingDomTreeUpdates()) { | 
|  | const auto I = PendUpdates.begin() + PendDTUpdateIndex; | 
|  | const auto E = PendUpdates.end(); | 
|  | assert(I < E && "Iterator range invalid; there should be DomTree updates."); | 
|  | DT->applyUpdates(ArrayRef<DominatorTree::UpdateType>(I, E)); | 
|  | PendDTUpdateIndex = PendUpdates.size(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void DomTreeUpdater::flush() { | 
|  | applyDomTreeUpdates(); | 
|  | applyPostDomTreeUpdates(); | 
|  | dropOutOfDateUpdates(); | 
|  | } | 
|  |  | 
|  | void DomTreeUpdater::applyPostDomTreeUpdates() { | 
|  | // No pending PostDomTreeUpdates. | 
|  | if (Strategy != UpdateStrategy::Lazy || !PDT) | 
|  | return; | 
|  |  | 
|  | // Only apply updates not are applied by PostDomTree. | 
|  | if (hasPendingPostDomTreeUpdates()) { | 
|  | const auto I = PendUpdates.begin() + PendPDTUpdateIndex; | 
|  | const auto E = PendUpdates.end(); | 
|  | assert(I < E && | 
|  | "Iterator range invalid; there should be PostDomTree updates."); | 
|  | PDT->applyUpdates(ArrayRef<DominatorTree::UpdateType>(I, E)); | 
|  | PendPDTUpdateIndex = PendUpdates.size(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void DomTreeUpdater::tryFlushDeletedBB() { | 
|  | if (!hasPendingUpdates()) | 
|  | forceFlushDeletedBB(); | 
|  | } | 
|  |  | 
|  | bool DomTreeUpdater::forceFlushDeletedBB() { | 
|  | if (DeletedBBs.empty()) | 
|  | return false; | 
|  |  | 
|  | for (auto *BB : DeletedBBs) { | 
|  | // After calling deleteBB or callbackDeleteBB under Lazy UpdateStrategy, | 
|  | // validateDeleteBB() removes all instructions of DelBB and adds an | 
|  | // UnreachableInst as its terminator. So we check whether the BasicBlock to | 
|  | // delete only has an UnreachableInst inside. | 
|  | assert(BB->getInstList().size() == 1 && | 
|  | isa<UnreachableInst>(BB->getTerminator()) && | 
|  | "DelBB has been modified while awaiting deletion."); | 
|  | BB->removeFromParent(); | 
|  | eraseDelBBNode(BB); | 
|  | delete BB; | 
|  | } | 
|  | DeletedBBs.clear(); | 
|  | Callbacks.clear(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void DomTreeUpdater::recalculate(Function &F) { | 
|  |  | 
|  | if (Strategy == UpdateStrategy::Eager) { | 
|  | if (DT) | 
|  | DT->recalculate(F); | 
|  | if (PDT) | 
|  | PDT->recalculate(F); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // There is little performance gain if we pend the recalculation under | 
|  | // Lazy UpdateStrategy so we recalculate available trees immediately. | 
|  |  | 
|  | // Prevent forceFlushDeletedBB() from erasing DomTree or PostDomTree nodes. | 
|  | IsRecalculatingDomTree = IsRecalculatingPostDomTree = true; | 
|  |  | 
|  | // Because all trees are going to be up-to-date after recalculation, | 
|  | // flush awaiting deleted BasicBlocks. | 
|  | forceFlushDeletedBB(); | 
|  | if (DT) | 
|  | DT->recalculate(F); | 
|  | if (PDT) | 
|  | PDT->recalculate(F); | 
|  |  | 
|  | // Resume forceFlushDeletedBB() to erase DomTree or PostDomTree nodes. | 
|  | IsRecalculatingDomTree = IsRecalculatingPostDomTree = false; | 
|  | PendDTUpdateIndex = PendPDTUpdateIndex = PendUpdates.size(); | 
|  | dropOutOfDateUpdates(); | 
|  | } | 
|  |  | 
|  | bool DomTreeUpdater::hasPendingUpdates() const { | 
|  | return hasPendingDomTreeUpdates() || hasPendingPostDomTreeUpdates(); | 
|  | } | 
|  |  | 
|  | bool DomTreeUpdater::hasPendingDomTreeUpdates() const { | 
|  | if (!DT) | 
|  | return false; | 
|  | return PendUpdates.size() != PendDTUpdateIndex; | 
|  | } | 
|  |  | 
|  | bool DomTreeUpdater::hasPendingPostDomTreeUpdates() const { | 
|  | if (!PDT) | 
|  | return false; | 
|  | return PendUpdates.size() != PendPDTUpdateIndex; | 
|  | } | 
|  |  | 
|  | bool DomTreeUpdater::isBBPendingDeletion(llvm::BasicBlock *DelBB) const { | 
|  | if (Strategy == UpdateStrategy::Eager || DeletedBBs.empty()) | 
|  | return false; | 
|  | return DeletedBBs.count(DelBB) != 0; | 
|  | } | 
|  |  | 
|  | // The DT and PDT require the nodes related to updates | 
|  | // are not deleted when update functions are called. | 
|  | // So BasicBlock deletions must be pended when the | 
|  | // UpdateStrategy is Lazy. When the UpdateStrategy is | 
|  | // Eager, the BasicBlock will be deleted immediately. | 
|  | void DomTreeUpdater::deleteBB(BasicBlock *DelBB) { | 
|  | validateDeleteBB(DelBB); | 
|  | if (Strategy == UpdateStrategy::Lazy) { | 
|  | DeletedBBs.insert(DelBB); | 
|  | return; | 
|  | } | 
|  |  | 
|  | DelBB->removeFromParent(); | 
|  | eraseDelBBNode(DelBB); | 
|  | delete DelBB; | 
|  | } | 
|  |  | 
|  | void DomTreeUpdater::callbackDeleteBB( | 
|  | BasicBlock *DelBB, std::function<void(BasicBlock *)> Callback) { | 
|  | validateDeleteBB(DelBB); | 
|  | if (Strategy == UpdateStrategy::Lazy) { | 
|  | Callbacks.push_back(CallBackOnDeletion(DelBB, Callback)); | 
|  | DeletedBBs.insert(DelBB); | 
|  | return; | 
|  | } | 
|  |  | 
|  | DelBB->removeFromParent(); | 
|  | eraseDelBBNode(DelBB); | 
|  | Callback(DelBB); | 
|  | delete DelBB; | 
|  | } | 
|  |  | 
|  | void DomTreeUpdater::eraseDelBBNode(BasicBlock *DelBB) { | 
|  | if (DT && !IsRecalculatingDomTree) | 
|  | if (DT->getNode(DelBB)) | 
|  | DT->eraseNode(DelBB); | 
|  |  | 
|  | if (PDT && !IsRecalculatingPostDomTree) | 
|  | if (PDT->getNode(DelBB)) | 
|  | PDT->eraseNode(DelBB); | 
|  | } | 
|  |  | 
|  | void DomTreeUpdater::validateDeleteBB(BasicBlock *DelBB) { | 
|  | assert(DelBB && "Invalid push_back of nullptr DelBB."); | 
|  | assert(pred_empty(DelBB) && "DelBB has one or more predecessors."); | 
|  | // DelBB is unreachable and all its instructions are dead. | 
|  | while (!DelBB->empty()) { | 
|  | Instruction &I = DelBB->back(); | 
|  | // Replace used instructions with an arbitrary value (undef). | 
|  | if (!I.use_empty()) | 
|  | I.replaceAllUsesWith(llvm::UndefValue::get(I.getType())); | 
|  | DelBB->getInstList().pop_back(); | 
|  | } | 
|  | // Make sure DelBB has a valid terminator instruction. As long as DelBB is a | 
|  | // Child of Function F it must contain valid IR. | 
|  | new UnreachableInst(DelBB->getContext(), DelBB); | 
|  | } | 
|  |  | 
|  | void DomTreeUpdater::applyUpdates(ArrayRef<DominatorTree::UpdateType> Updates) { | 
|  | if (!DT && !PDT) | 
|  | return; | 
|  |  | 
|  | if (Strategy == UpdateStrategy::Lazy) { | 
|  | for (const auto &U : Updates) | 
|  | if (!isSelfDominance(U)) | 
|  | PendUpdates.push_back(U); | 
|  |  | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (DT) | 
|  | DT->applyUpdates(Updates); | 
|  | if (PDT) | 
|  | PDT->applyUpdates(Updates); | 
|  | } | 
|  |  | 
|  | void DomTreeUpdater::applyUpdatesPermissive( | 
|  | ArrayRef<DominatorTree::UpdateType> Updates) { | 
|  | if (!DT && !PDT) | 
|  | return; | 
|  |  | 
|  | SmallSet<std::pair<BasicBlock *, BasicBlock *>, 8> Seen; | 
|  | SmallVector<DominatorTree::UpdateType, 8> DeduplicatedUpdates; | 
|  | for (const auto &U : Updates) { | 
|  | auto Edge = std::make_pair(U.getFrom(), U.getTo()); | 
|  | // Because it is illegal to submit updates that have already been applied | 
|  | // and updates to an edge need to be strictly ordered, | 
|  | // it is safe to infer the existence of an edge from the first update | 
|  | // to this edge. | 
|  | // If the first update to an edge is "Delete", it means that the edge | 
|  | // existed before. If the first update to an edge is "Insert", it means | 
|  | // that the edge didn't exist before. | 
|  | // | 
|  | // For example, if the user submits {{Delete, A, B}, {Insert, A, B}}, | 
|  | // because | 
|  | // 1. it is illegal to submit updates that have already been applied, | 
|  | // i.e., user cannot delete an nonexistent edge, | 
|  | // 2. updates to an edge need to be strictly ordered, | 
|  | // So, initially edge A -> B existed. | 
|  | // We can then safely ignore future updates to this edge and directly | 
|  | // inspect the current CFG: | 
|  | // a. If the edge still exists, because the user cannot insert an existent | 
|  | // edge, so both {Delete, A, B}, {Insert, A, B} actually happened and | 
|  | // resulted in a no-op. DTU won't submit any update in this case. | 
|  | // b. If the edge doesn't exist, we can then infer that {Delete, A, B} | 
|  | // actually happened but {Insert, A, B} was an invalid update which never | 
|  | // happened. DTU will submit {Delete, A, B} in this case. | 
|  | if (!isSelfDominance(U) && Seen.count(Edge) == 0) { | 
|  | Seen.insert(Edge); | 
|  | // If the update doesn't appear in the CFG, it means that | 
|  | // either the change isn't made or relevant operations | 
|  | // result in a no-op. | 
|  | if (isUpdateValid(U)) { | 
|  | if (isLazy()) | 
|  | PendUpdates.push_back(U); | 
|  | else | 
|  | DeduplicatedUpdates.push_back(U); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (Strategy == UpdateStrategy::Lazy) | 
|  | return; | 
|  |  | 
|  | if (DT) | 
|  | DT->applyUpdates(DeduplicatedUpdates); | 
|  | if (PDT) | 
|  | PDT->applyUpdates(DeduplicatedUpdates); | 
|  | } | 
|  |  | 
|  | DominatorTree &DomTreeUpdater::getDomTree() { | 
|  | assert(DT && "Invalid acquisition of a null DomTree"); | 
|  | applyDomTreeUpdates(); | 
|  | dropOutOfDateUpdates(); | 
|  | return *DT; | 
|  | } | 
|  |  | 
|  | PostDominatorTree &DomTreeUpdater::getPostDomTree() { | 
|  | assert(PDT && "Invalid acquisition of a null PostDomTree"); | 
|  | applyPostDomTreeUpdates(); | 
|  | dropOutOfDateUpdates(); | 
|  | return *PDT; | 
|  | } | 
|  |  | 
|  | void DomTreeUpdater::insertEdge(BasicBlock *From, BasicBlock *To) { | 
|  |  | 
|  | #ifndef NDEBUG | 
|  | assert(isUpdateValid({DominatorTree::Insert, From, To}) && | 
|  | "Inserted edge does not appear in the CFG"); | 
|  | #endif | 
|  |  | 
|  | if (!DT && !PDT) | 
|  | return; | 
|  |  | 
|  | // Won't affect DomTree and PostDomTree; discard update. | 
|  | if (From == To) | 
|  | return; | 
|  |  | 
|  | if (Strategy == UpdateStrategy::Eager) { | 
|  | if (DT) | 
|  | DT->insertEdge(From, To); | 
|  | if (PDT) | 
|  | PDT->insertEdge(From, To); | 
|  | return; | 
|  | } | 
|  |  | 
|  | PendUpdates.push_back({DominatorTree::Insert, From, To}); | 
|  | } | 
|  |  | 
|  | void DomTreeUpdater::insertEdgeRelaxed(BasicBlock *From, BasicBlock *To) { | 
|  | if (From == To) | 
|  | return; | 
|  |  | 
|  | if (!DT && !PDT) | 
|  | return; | 
|  |  | 
|  | if (!isUpdateValid({DominatorTree::Insert, From, To})) | 
|  | return; | 
|  |  | 
|  | if (Strategy == UpdateStrategy::Eager) { | 
|  | if (DT) | 
|  | DT->insertEdge(From, To); | 
|  | if (PDT) | 
|  | PDT->insertEdge(From, To); | 
|  | return; | 
|  | } | 
|  |  | 
|  | PendUpdates.push_back({DominatorTree::Insert, From, To}); | 
|  | } | 
|  |  | 
|  | void DomTreeUpdater::deleteEdge(BasicBlock *From, BasicBlock *To) { | 
|  |  | 
|  | #ifndef NDEBUG | 
|  | assert(isUpdateValid({DominatorTree::Delete, From, To}) && | 
|  | "Deleted edge still exists in the CFG!"); | 
|  | #endif | 
|  |  | 
|  | if (!DT && !PDT) | 
|  | return; | 
|  |  | 
|  | // Won't affect DomTree and PostDomTree; discard update. | 
|  | if (From == To) | 
|  | return; | 
|  |  | 
|  | if (Strategy == UpdateStrategy::Eager) { | 
|  | if (DT) | 
|  | DT->deleteEdge(From, To); | 
|  | if (PDT) | 
|  | PDT->deleteEdge(From, To); | 
|  | return; | 
|  | } | 
|  |  | 
|  | PendUpdates.push_back({DominatorTree::Delete, From, To}); | 
|  | } | 
|  |  | 
|  | void DomTreeUpdater::deleteEdgeRelaxed(BasicBlock *From, BasicBlock *To) { | 
|  | if (From == To) | 
|  | return; | 
|  |  | 
|  | if (!DT && !PDT) | 
|  | return; | 
|  |  | 
|  | if (!isUpdateValid({DominatorTree::Delete, From, To})) | 
|  | return; | 
|  |  | 
|  | if (Strategy == UpdateStrategy::Eager) { | 
|  | if (DT) | 
|  | DT->deleteEdge(From, To); | 
|  | if (PDT) | 
|  | PDT->deleteEdge(From, To); | 
|  | return; | 
|  | } | 
|  |  | 
|  | PendUpdates.push_back({DominatorTree::Delete, From, To}); | 
|  | } | 
|  |  | 
|  | void DomTreeUpdater::dropOutOfDateUpdates() { | 
|  | if (Strategy == DomTreeUpdater::UpdateStrategy::Eager) | 
|  | return; | 
|  |  | 
|  | tryFlushDeletedBB(); | 
|  |  | 
|  | // Drop all updates applied by both trees. | 
|  | if (!DT) | 
|  | PendDTUpdateIndex = PendUpdates.size(); | 
|  | if (!PDT) | 
|  | PendPDTUpdateIndex = PendUpdates.size(); | 
|  |  | 
|  | const size_t dropIndex = std::min(PendDTUpdateIndex, PendPDTUpdateIndex); | 
|  | const auto B = PendUpdates.begin(); | 
|  | const auto E = PendUpdates.begin() + dropIndex; | 
|  | assert(B <= E && "Iterator out of range."); | 
|  | PendUpdates.erase(B, E); | 
|  | // Calculate current index. | 
|  | PendDTUpdateIndex -= dropIndex; | 
|  | PendPDTUpdateIndex -= dropIndex; | 
|  | } | 
|  |  | 
|  | #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) | 
|  | LLVM_DUMP_METHOD void DomTreeUpdater::dump() const { | 
|  | raw_ostream &OS = llvm::dbgs(); | 
|  |  | 
|  | OS << "Available Trees: "; | 
|  | if (DT || PDT) { | 
|  | if (DT) | 
|  | OS << "DomTree "; | 
|  | if (PDT) | 
|  | OS << "PostDomTree "; | 
|  | OS << "\n"; | 
|  | } else | 
|  | OS << "None\n"; | 
|  |  | 
|  | OS << "UpdateStrategy: "; | 
|  | if (Strategy == UpdateStrategy::Eager) { | 
|  | OS << "Eager\n"; | 
|  | return; | 
|  | } else | 
|  | OS << "Lazy\n"; | 
|  | int Index = 0; | 
|  |  | 
|  | auto printUpdates = | 
|  | [&](ArrayRef<DominatorTree::UpdateType>::const_iterator begin, | 
|  | ArrayRef<DominatorTree::UpdateType>::const_iterator end) { | 
|  | if (begin == end) | 
|  | OS << "  None\n"; | 
|  | Index = 0; | 
|  | for (auto It = begin, ItEnd = end; It != ItEnd; ++It) { | 
|  | auto U = *It; | 
|  | OS << "  " << Index << " : "; | 
|  | ++Index; | 
|  | if (U.getKind() == DominatorTree::Insert) | 
|  | OS << "Insert, "; | 
|  | else | 
|  | OS << "Delete, "; | 
|  | BasicBlock *From = U.getFrom(); | 
|  | if (From) { | 
|  | auto S = From->getName(); | 
|  | if (!From->hasName()) | 
|  | S = "(no name)"; | 
|  | OS << S << "(" << From << "), "; | 
|  | } else { | 
|  | OS << "(badref), "; | 
|  | } | 
|  | BasicBlock *To = U.getTo(); | 
|  | if (To) { | 
|  | auto S = To->getName(); | 
|  | if (!To->hasName()) | 
|  | S = "(no_name)"; | 
|  | OS << S << "(" << To << ")\n"; | 
|  | } else { | 
|  | OS << "(badref)\n"; | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | if (DT) { | 
|  | const auto I = PendUpdates.begin() + PendDTUpdateIndex; | 
|  | assert(PendUpdates.begin() <= I && I <= PendUpdates.end() && | 
|  | "Iterator out of range."); | 
|  | OS << "Applied but not cleared DomTreeUpdates:\n"; | 
|  | printUpdates(PendUpdates.begin(), I); | 
|  | OS << "Pending DomTreeUpdates:\n"; | 
|  | printUpdates(I, PendUpdates.end()); | 
|  | } | 
|  |  | 
|  | if (PDT) { | 
|  | const auto I = PendUpdates.begin() + PendPDTUpdateIndex; | 
|  | assert(PendUpdates.begin() <= I && I <= PendUpdates.end() && | 
|  | "Iterator out of range."); | 
|  | OS << "Applied but not cleared PostDomTreeUpdates:\n"; | 
|  | printUpdates(PendUpdates.begin(), I); | 
|  | OS << "Pending PostDomTreeUpdates:\n"; | 
|  | printUpdates(I, PendUpdates.end()); | 
|  | } | 
|  |  | 
|  | OS << "Pending DeletedBBs:\n"; | 
|  | Index = 0; | 
|  | for (auto BB : DeletedBBs) { | 
|  | OS << "  " << Index << " : "; | 
|  | ++Index; | 
|  | if (BB->hasName()) | 
|  | OS << BB->getName() << "("; | 
|  | else | 
|  | OS << "(no_name)("; | 
|  | OS << BB << ")\n"; | 
|  | } | 
|  |  | 
|  | OS << "Pending Callbacks:\n"; | 
|  | Index = 0; | 
|  | for (auto BB : Callbacks) { | 
|  | OS << "  " << Index << " : "; | 
|  | ++Index; | 
|  | if (BB->hasName()) | 
|  | OS << BB->getName() << "("; | 
|  | else | 
|  | OS << "(no_name)("; | 
|  | OS << BB << ")\n"; | 
|  | } | 
|  | } | 
|  | #endif | 
|  | } // namespace llvm |