diff --git a/llvm/include/llvm/Analysis/DomTreeUpdater.h b/llvm/include/llvm/Analysis/DomTreeUpdater.h index 2b838a311440e..ddb958455ccd7 100644 --- a/llvm/include/llvm/Analysis/DomTreeUpdater.h +++ b/llvm/include/llvm/Analysis/DomTreeUpdater.h @@ -15,8 +15,6 @@ #define LLVM_ANALYSIS_DOMTREEUPDATER_H #include "llvm/ADT/SmallPtrSet.h" -#include "llvm/Analysis/GenericDomTreeUpdater.h" -#include "llvm/Analysis/PostDominators.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/ValueHandle.h" #include "llvm/Support/Compiler.h" @@ -25,17 +23,66 @@ #include namespace llvm { +class PostDominatorTree; -class DomTreeUpdater - : public GenericDomTreeUpdater { - friend GenericDomTreeUpdater; - +class DomTreeUpdater { public: - using Base = - GenericDomTreeUpdater; - using Base::Base; + enum class UpdateStrategy : unsigned char { Eager = 0, Lazy = 1 }; + + explicit DomTreeUpdater(UpdateStrategy Strategy_) : Strategy(Strategy_) {} + DomTreeUpdater(DominatorTree &DT_, UpdateStrategy Strategy_) + : DT(&DT_), Strategy(Strategy_) {} + DomTreeUpdater(DominatorTree *DT_, UpdateStrategy Strategy_) + : DT(DT_), Strategy(Strategy_) {} + DomTreeUpdater(PostDominatorTree &PDT_, UpdateStrategy Strategy_) + : PDT(&PDT_), Strategy(Strategy_) {} + DomTreeUpdater(PostDominatorTree *PDT_, UpdateStrategy Strategy_) + : PDT(PDT_), Strategy(Strategy_) {} + DomTreeUpdater(DominatorTree &DT_, PostDominatorTree &PDT_, + UpdateStrategy Strategy_) + : DT(&DT_), PDT(&PDT_), Strategy(Strategy_) {} + DomTreeUpdater(DominatorTree *DT_, PostDominatorTree *PDT_, + UpdateStrategy Strategy_) + : DT(DT_), PDT(PDT_), Strategy(Strategy_) {} + + ~DomTreeUpdater() { flush(); } + + /// Returns true if the current strategy is Lazy. + bool isLazy() const { return Strategy == UpdateStrategy::Lazy; }; + + /// Returns true if the current strategy is Eager. + bool isEager() const { return Strategy == UpdateStrategy::Eager; }; + + /// Returns true if it holds a DominatorTree. + bool hasDomTree() const { return DT != nullptr; } + + /// Returns true if it holds a PostDominatorTree. + bool hasPostDomTree() const { return PDT != nullptr; } + + /// Returns true if there is BasicBlock awaiting deletion. + /// The deletion will only happen until a flush event and + /// all available trees are up-to-date. + /// Returns false under Eager UpdateStrategy. + bool hasPendingDeletedBB() const { return !DeletedBBs.empty(); } + + /// Returns true if DelBB is awaiting deletion. + /// Returns false under Eager UpdateStrategy. + bool isBBPendingDeletion(BasicBlock *DelBB) const; + + /// Returns true if either of DT or PDT is valid and the tree has at + /// least one update pending. If DT or PDT is nullptr it is treated + /// as having no pending updates. This function does not check + /// whether there is BasicBlock awaiting deletion. + /// Returns false under Eager UpdateStrategy. + bool hasPendingUpdates() const; + + /// Returns true if there are DominatorTree updates queued. + /// Returns false under Eager UpdateStrategy or DT is nullptr. + bool hasPendingDomTreeUpdates() const; + + /// Returns true if there are PostDominatorTree updates queued. + /// Returns false under Eager UpdateStrategy or PDT is nullptr. + bool hasPendingPostDomTreeUpdates() const; ///@{ /// \name Mutation APIs @@ -58,6 +105,51 @@ class DomTreeUpdater /// Although GenericDomTree provides several update primitives, /// it is not encouraged to use these APIs directly. + /// Submit updates to all available trees. + /// The Eager Strategy flushes updates immediately while the Lazy Strategy + /// queues the updates. + /// + /// Note: The "existence" of an edge in a CFG refers to the CFG which DTU is + /// in sync with + all updates before that single update. + /// + /// CAUTION! + /// 1. It is required for the state of the LLVM IR to be updated + /// *before* submitting the updates because the internal update routine will + /// analyze the current state of the CFG to determine whether an update + /// is valid. + /// 2. It is illegal to submit any update that has already been submitted, + /// i.e., you are supposed not to insert an existent edge or delete a + /// nonexistent edge. + void applyUpdates(ArrayRef Updates); + + /// Submit updates to all available trees. It will also + /// 1. discard duplicated updates, + /// 2. remove invalid updates. (Invalid updates means deletion of an edge that + /// still exists or insertion of an edge that does not exist.) + /// The Eager Strategy flushes updates immediately while the Lazy Strategy + /// queues the updates. + /// + /// Note: The "existence" of an edge in a CFG refers to the CFG which DTU is + /// in sync with + all updates before that single update. + /// + /// CAUTION! + /// 1. It is required for the state of the LLVM IR to be updated + /// *before* submitting the updates because the internal update routine will + /// analyze the current state of the CFG to determine whether an update + /// is valid. + /// 2. It is illegal to submit any update that has already been submitted, + /// i.e., you are supposed not to insert an existent edge or delete a + /// nonexistent edge. + /// 3. It is only legal to submit updates to an edge in the order CFG changes + /// are made. The order you submit updates on different edges is not + /// restricted. + void applyUpdatesPermissive(ArrayRef Updates); + + /// Notify DTU that the entry block was replaced. + /// Recalculate all available trees and flush all BasicBlocks + /// awaiting deletion immediately. + void recalculate(Function &F); + /// Delete DelBB. DelBB will be removed from its Parent and /// erased from available trees if it exists and finally get deleted. /// Under Eager UpdateStrategy, DelBB will be processed immediately. @@ -80,6 +172,33 @@ class DomTreeUpdater ///@} + ///@{ + /// \name Flush APIs + /// + /// CAUTION! By the moment these flush APIs are called, the current CFG needs + /// to be the same as the CFG which DTU is in sync with + all updates + /// submitted. + + /// Flush DomTree updates and return DomTree. + /// It flushes Deleted BBs if both trees are up-to-date. + /// It must only be called when it has a DomTree. + DominatorTree &getDomTree(); + + /// Flush PostDomTree updates and return PostDomTree. + /// It flushes Deleted BBs if both trees are up-to-date. + /// It must only be called when it has a PostDomTree. + PostDominatorTree &getPostDomTree(); + + /// Apply all pending updates to available trees and flush all BasicBlocks + /// awaiting deletion. + + void flush(); + + ///@} + + /// Debug method to help view the internal state of this class. + LLVM_DUMP_METHOD void dump() const; + private: class CallBackOnDeletion final : public CallbackVH { public: @@ -97,7 +216,16 @@ class DomTreeUpdater } }; + SmallVector PendUpdates; + size_t PendDTUpdateIndex = 0; + size_t PendPDTUpdateIndex = 0; + DominatorTree *DT = nullptr; + PostDominatorTree *PDT = nullptr; + const UpdateStrategy Strategy; + SmallPtrSet DeletedBBs; std::vector Callbacks; + bool IsRecalculatingDomTree = false; + bool IsRecalculatingPostDomTree = false; /// First remove all the instructions of DelBB and then make sure DelBB has a /// valid terminator instruction which is necessary to have when DelBB still @@ -109,28 +237,32 @@ class DomTreeUpdater /// Returns true if at least one BasicBlock is deleted. bool forceFlushDeletedBB(); - /// Debug method to help view the internal state of this class. - LLVM_DUMP_METHOD void dump() const { - Base::dump(); -#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) - raw_ostream &OS = dbgs(); - OS << "Pending Callbacks:\n"; - int Index = 0; - for (const auto &BB : Callbacks) { - OS << " " << Index << " : "; - ++Index; - if (BB->hasName()) - OS << BB->getName() << "("; - else - OS << "(no_name)("; - OS << BB << ")\n"; - } -#endif - } -}; + /// Helper function to apply all pending DomTree updates. + void applyDomTreeUpdates(); + + /// Helper function to apply all pending PostDomTree updates. + void applyPostDomTreeUpdates(); + + /// Helper function to flush deleted BasicBlocks if all available + /// trees are up-to-date. + void tryFlushDeletedBB(); -extern template class GenericDomTreeUpdater; + /// Drop all updates applied by all available trees and delete BasicBlocks if + /// all available trees are up-to-date. + void dropOutOfDateUpdates(); + + /// Erase Basic Block node that has been unlinked from Function + /// in the DomTree and PostDomTree. + void eraseDelBBNode(BasicBlock *DelBB); + + /// Returns true if the update appears in the LLVM IR. + /// It is used to check whether an update is valid in + /// insertEdge/deleteEdge or is unnecessary in the batch update. + bool isUpdateValid(DominatorTree::UpdateType Update) const; + + /// Returns true if the update is self dominance. + bool isSelfDominance(DominatorTree::UpdateType Update) const; +}; } // namespace llvm #endif // LLVM_ANALYSIS_DOMTREEUPDATER_H diff --git a/llvm/include/llvm/Analysis/GenericDomTreeUpdater.h b/llvm/include/llvm/Analysis/GenericDomTreeUpdater.h deleted file mode 100644 index 7092c67083a67..0000000000000 --- a/llvm/include/llvm/Analysis/GenericDomTreeUpdater.h +++ /dev/null @@ -1,525 +0,0 @@ -//===- GenericDomTreeUpdater.h ----------------------------------*- 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 defines the GenericDomTreeUpdater class, which provides a uniform -// way to update dominator tree related data structures. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_ANALYSIS_GENERICDOMTREEUPDATER_H -#define LLVM_ANALYSIS_GENERICDOMTREEUPDATER_H - -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/SmallSet.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" - -namespace llvm { - -template -class GenericDomTreeUpdater { - DerivedT &derived() { return *static_cast(this); } - const DerivedT &derived() const { - return *static_cast(this); - } - -public: - enum class UpdateStrategy : unsigned char { Eager = 0, Lazy = 1 }; - using BasicBlockT = typename DomTreeT::NodeType; - - explicit GenericDomTreeUpdater(UpdateStrategy Strategy_) - : Strategy(Strategy_) {} - GenericDomTreeUpdater(DomTreeT &DT_, UpdateStrategy Strategy_) - : DT(&DT_), Strategy(Strategy_) {} - GenericDomTreeUpdater(DomTreeT *DT_, UpdateStrategy Strategy_) - : DT(DT_), Strategy(Strategy_) {} - GenericDomTreeUpdater(PostDomTreeT &PDT_, UpdateStrategy Strategy_) - : PDT(&PDT_), Strategy(Strategy_) {} - GenericDomTreeUpdater(PostDomTreeT *PDT_, UpdateStrategy Strategy_) - : PDT(PDT_), Strategy(Strategy_) {} - GenericDomTreeUpdater(DomTreeT &DT_, PostDomTreeT &PDT_, - UpdateStrategy Strategy_) - : DT(&DT_), PDT(&PDT_), Strategy(Strategy_) {} - GenericDomTreeUpdater(DomTreeT *DT_, PostDomTreeT *PDT_, - UpdateStrategy Strategy_) - : DT(DT_), PDT(PDT_), Strategy(Strategy_) {} - - ~GenericDomTreeUpdater() { flush(); } - - /// Returns true if the current strategy is Lazy. - bool isLazy() const { return Strategy == UpdateStrategy::Lazy; }; - - /// Returns true if the current strategy is Eager. - bool isEager() const { return Strategy == UpdateStrategy::Eager; }; - - /// Returns true if it holds a DomTreeT. - bool hasDomTree() const { return DT != nullptr; } - - /// Returns true if it holds a PostDomTreeT. - bool hasPostDomTree() const { return PDT != nullptr; } - - /// Returns true if there is BasicBlockT awaiting deletion. - /// The deletion will only happen until a flush event and - /// all available trees are up-to-date. - /// Returns false under Eager UpdateStrategy. - bool hasPendingDeletedBB() const { return !DeletedBBs.empty(); } - - /// Returns true if DelBB is awaiting deletion. - /// Returns false under Eager UpdateStrategy. - bool isBBPendingDeletion(BasicBlockT *DelBB) const { - if (Strategy == UpdateStrategy::Eager || DeletedBBs.empty()) - return false; - return DeletedBBs.contains(DelBB); - } - - /// Returns true if either of DT or PDT is valid and the tree has at - /// least one update pending. If DT or PDT is nullptr it is treated - /// as having no pending updates. This function does not check - /// whether there is MachineBasicBlock awaiting deletion. - /// Returns false under Eager UpdateStrategy. - bool hasPendingUpdates() const { - return hasPendingDomTreeUpdates() || hasPendingPostDomTreeUpdates(); - } - - /// Returns true if there are DomTreeT updates queued. - /// Returns false under Eager UpdateStrategy or DT is nullptr. - bool hasPendingDomTreeUpdates() const { - if (!DT) - return false; - return PendUpdates.size() != PendDTUpdateIndex; - } - - /// Returns true if there are PostDomTreeT updates queued. - /// Returns false under Eager UpdateStrategy or PDT is nullptr. - bool hasPendingPostDomTreeUpdates() const { - if (!PDT) - return false; - return PendUpdates.size() != PendPDTUpdateIndex; - } - - ///@{ - /// \name Mutation APIs - /// - /// These methods provide APIs for submitting updates to the DomTreeT and - /// the PostDominatorTree. - /// - /// Note: There are two strategies to update the DomTreeT and the - /// PostDominatorTree: - /// 1. Eager UpdateStrategy: Updates are submitted and then flushed - /// immediately. - /// 2. Lazy UpdateStrategy: Updates are submitted but only flushed when you - /// explicitly call Flush APIs. It is recommended to use this update strategy - /// when you submit a bunch of updates multiple times which can then - /// add up to a large number of updates between two queries on the - /// DomTreeT. The incremental updater can reschedule the updates or - /// decide to recalculate the dominator tree in order to speedup the updating - /// process depending on the number of updates. - /// - /// Although GenericDomTree provides several update primitives, - /// it is not encouraged to use these APIs directly. - - /// Notify DTU that the entry block was replaced. - /// Recalculate all available trees and flush all BasicBlocks - /// awaiting deletion immediately. - template void recalculate(FuncT &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. - derived().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(); - } - - /// Submit updates to all available trees. - /// The Eager Strategy flushes updates immediately while the Lazy Strategy - /// queues the updates. - /// - /// Note: The "existence" of an edge in a CFG refers to the CFG which DTU is - /// in sync with + all updates before that single update. - /// - /// CAUTION! - /// 1. It is required for the state of the LLVM IR to be updated - /// *before* submitting the updates because the internal update routine will - /// analyze the current state of the CFG to determine whether an update - /// is valid. - /// 2. It is illegal to submit any update that has already been submitted, - /// i.e., you are supposed not to insert an existent edge or delete a - /// nonexistent edge. - void applyUpdates(ArrayRef Updates) { - if (!DT && !PDT) - return; - - if (Strategy == UpdateStrategy::Lazy) { - PendUpdates.reserve(PendUpdates.size() + Updates.size()); - for (const auto &U : Updates) - if (!isSelfDominance(U)) - PendUpdates.push_back(U); - - return; - } - - if (DT) - DT->applyUpdates(Updates); - if (PDT) - PDT->applyUpdates(Updates); - } - - /// Submit updates to all available trees. It will also - /// 1. discard duplicated updates, - /// 2. remove invalid updates. (Invalid updates means deletion of an edge that - /// still exists or insertion of an edge that does not exist.) - /// The Eager Strategy flushes updates immediately while the Lazy Strategy - /// queues the updates. - /// - /// Note: The "existence" of an edge in a CFG refers to the CFG which DTU is - /// in sync with + all updates before that single update. - /// - /// CAUTION! - /// 1. It is required for the state of the LLVM IR to be updated - /// *before* submitting the updates because the internal update routine will - /// analyze the current state of the CFG to determine whether an update - /// is valid. - /// 2. It is illegal to submit any update that has already been submitted, - /// i.e., you are supposed not to insert an existent edge or delete a - /// nonexistent edge. - /// 3. It is only legal to submit updates to an edge in the order CFG changes - /// are made. The order you submit updates on different edges is not - /// restricted. - void applyUpdatesPermissive(ArrayRef Updates) { - if (!DT && !PDT) - return; - - SmallSet, 8> Seen; - SmallVector 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); - } - - ///@} - - ///@{ - /// \name Flush APIs - /// - /// CAUTION! By the moment these flush APIs are called, the current CFG needs - /// to be the same as the CFG which DTU is in sync with + all updates - /// submitted. - - /// Flush DomTree updates and return DomTree. - /// It flushes Deleted BBs if both trees are up-to-date. - /// It must only be called when it has a DomTree. - DomTreeT &getDomTree() { - assert(DT && "Invalid acquisition of a null DomTree"); - applyDomTreeUpdates(); - dropOutOfDateUpdates(); - return *DT; - } - - /// Flush PostDomTree updates and return PostDomTree. - /// It flushes Deleted BBs if both trees are up-to-date. - /// It must only be called when it has a PostDomTree. - PostDomTreeT &getPostDomTree() { - assert(PDT && "Invalid acquisition of a null PostDomTree"); - applyPostDomTreeUpdates(); - dropOutOfDateUpdates(); - return *PDT; - } - - /// Apply all pending updates to available trees and flush all BasicBlocks - /// awaiting deletion. - - void flush() { - applyDomTreeUpdates(); - applyPostDomTreeUpdates(); - dropOutOfDateUpdates(); - } - - ///@} - - /// Debug method to help view the internal state of this class. - LLVM_DUMP_METHOD void dump() const { -#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) - 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 = - [&](typename ArrayRef::const_iterator - begin, - typename ArrayRef::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() == DomTreeT::Insert) - OS << "Insert, "; - else - OS << "Delete, "; - BasicBlockT *From = U.getFrom(); - if (From) { - auto S = From->getName(); - if (!From->hasName()) - S = "(no name)"; - OS << S << "(" << From << "), "; - } else { - OS << "(badref), "; - } - BasicBlockT *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 (const auto *BB : DeletedBBs) { - OS << " " << Index << " : "; - ++Index; - if (BB->hasName()) - OS << BB->getName() << "("; - else - OS << "(no_name)("; - OS << BB << ")\n"; - } -#endif - } - -protected: - SmallVector PendUpdates; - size_t PendDTUpdateIndex = 0; - size_t PendPDTUpdateIndex = 0; - DomTreeT *DT = nullptr; - PostDomTreeT *PDT = nullptr; - const UpdateStrategy Strategy; - SmallPtrSet DeletedBBs; - bool IsRecalculatingDomTree = false; - bool IsRecalculatingPostDomTree = false; - - /// Returns true if the update is self dominance. - bool isSelfDominance(typename DomTreeT::UpdateType Update) const { - // Won't affect DomTree and PostDomTree. - return Update.getFrom() == Update.getTo(); - } - - /// Helper function to apply all pending DomTree updates. - void 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(I, E)); - PendDTUpdateIndex = PendUpdates.size(); - } - } - - /// Helper function to apply all pending PostDomTree updates. - void 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(I, E)); - PendPDTUpdateIndex = PendUpdates.size(); - } - } - - /// Returns true if the update appears in the LLVM IR. - /// It is used to check whether an update is valid in - /// insertEdge/deleteEdge or is unnecessary in the batch update. - bool isUpdateValid(typename DomTreeT::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::is_contained(successors(From), 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 == DomTreeT::Insert && !HasEdge) - return false; - - // Edge exists in IR. - if (Kind == DomTreeT::Delete && HasEdge) - return false; - - return true; - } - - /// Erase Basic Block node that has been unlinked from Function - /// in the DomTree and PostDomTree. - void eraseDelBBNode(BasicBlockT *DelBB) { - if (DT && !IsRecalculatingDomTree) - if (DT->getNode(DelBB)) - DT->eraseNode(DelBB); - - if (PDT && !IsRecalculatingPostDomTree) - if (PDT->getNode(DelBB)) - PDT->eraseNode(DelBB); - } - - /// Helper function to flush deleted BasicBlocks if all available - /// trees are up-to-date. - void tryFlushDeletedBB() { - if (!hasPendingUpdates()) - derived().forceFlushDeletedBB(); - } - - /// Drop all updates applied by all available trees and delete BasicBlocks if - /// all available trees are up-to-date. - void dropOutOfDateUpdates() { - if (Strategy == 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; - } -}; - -} // namespace llvm - -#endif // LLVM_ANALYSIS_GENERICDOMTREEUPDATER_H diff --git a/llvm/include/llvm/CodeGen/MachineBasicBlock.h b/llvm/include/llvm/CodeGen/MachineBasicBlock.h index e4919ecabd705..5b6be3a96b2fb 100644 --- a/llvm/include/llvm/CodeGen/MachineBasicBlock.h +++ b/llvm/include/llvm/CodeGen/MachineBasicBlock.h @@ -238,9 +238,6 @@ class MachineBasicBlock BB = nullptr; } - /// Check if there is a name of corresponding LLVM basic block. - bool hasName() const; - /// Return the name of the corresponding LLVM basic block, or an empty string. StringRef getName() const; diff --git a/llvm/include/llvm/CodeGen/MachineDomTreeUpdater.h b/llvm/include/llvm/CodeGen/MachineDomTreeUpdater.h deleted file mode 100644 index 8bc572e6e6af0..0000000000000 --- a/llvm/include/llvm/CodeGen/MachineDomTreeUpdater.h +++ /dev/null @@ -1,72 +0,0 @@ -//===- llvm/CodeGen/MachineDomTreeUpdater.h -----------------------*- 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 exposes interfaces to post dominance information for -// target-specific code. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CODEGEN_MACHINEDOMTREEUPDATER_H -#define LLVM_CODEGEN_MACHINEDOMTREEUPDATER_H - -#include "llvm/ADT/SmallPtrSet.h" -#include "llvm/Analysis/GenericDomTreeUpdater.h" -#include "llvm/CodeGen/MachineDominators.h" -#include "llvm/CodeGen/MachinePostDominators.h" -#include "llvm/IR/ValueHandle.h" -#include "llvm/Support/Compiler.h" -#include -#include -#include - -namespace llvm { - -class MachineDomTreeUpdater - : public GenericDomTreeUpdater { - friend GenericDomTreeUpdater; - -public: - using Base = - GenericDomTreeUpdater; - using Base::Base; - - ///@{ - /// \name Mutation APIs - /// - - /// Delete DelBB. DelBB will be removed from its Parent and - /// erased from available trees if it exists and finally get deleted. - /// Under Eager UpdateStrategy, DelBB will be processed immediately. - /// Under Lazy UpdateStrategy, DelBB will be queued until a flush event and - /// all available trees are up-to-date. Assert if any instruction of DelBB is - /// modified while awaiting deletion. When both DT and PDT are nullptrs, DelBB - /// will be queued until flush() is called. - void deleteBB(MachineBasicBlock *DelBB); - - ///@} - -private: - /// First remove all the instructions of DelBB and then make sure DelBB has a - /// valid terminator instruction which is necessary to have when DelBB still - /// has to be inside of its parent Function while awaiting deletion under Lazy - /// UpdateStrategy to prevent other routines from asserting the state of the - /// IR is inconsistent. Assert if DelBB is nullptr or has predecessors. - void validateDeleteBB(MachineBasicBlock *DelBB); - - /// Returns true if at least one MachineBasicBlock is deleted. - bool forceFlushDeletedBB(); -}; - -extern template class GenericDomTreeUpdater< - MachineDomTreeUpdater, MachineDominatorTree, MachinePostDominatorTree>; -} // namespace llvm -#endif // LLVM_CODEGEN_MACHINEDOMTREEUPDATER_H diff --git a/llvm/include/llvm/CodeGen/MachinePostDominators.h b/llvm/include/llvm/CodeGen/MachinePostDominators.h index 58cfb278b7962..904c5fcdfc68b 100644 --- a/llvm/include/llvm/CodeGen/MachinePostDominators.h +++ b/llvm/include/llvm/CodeGen/MachinePostDominators.h @@ -48,7 +48,6 @@ class MachinePostDominatorTree : public PostDomTreeBase { public: MachinePostDominatorTree() = default; - explicit MachinePostDominatorTree(MachineFunction &MF) { recalculate(MF); } MachinePostDominatorTree(MachineFunction &MF) { recalculate(MF); } diff --git a/llvm/lib/Analysis/DomTreeUpdater.cpp b/llvm/lib/Analysis/DomTreeUpdater.cpp index 676cb87210176..0a16fc9a76d29 100644 --- a/llvm/lib/Analysis/DomTreeUpdater.cpp +++ b/llvm/lib/Analysis/DomTreeUpdater.cpp @@ -23,8 +23,79 @@ namespace llvm { -template class GenericDomTreeUpdater; +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::is_contained(successors(From), 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(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(I, E)); + PendPDTUpdateIndex = PendUpdates.size(); + } +} + +void DomTreeUpdater::tryFlushDeletedBB() { + if (!hasPendingUpdates()) + forceFlushDeletedBB(); +} bool DomTreeUpdater::forceFlushDeletedBB() { if (DeletedBBs.empty()) @@ -46,6 +117,58 @@ bool DomTreeUpdater::forceFlushDeletedBB() { 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.contains(DelBB); +} + // 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 @@ -78,6 +201,16 @@ void DomTreeUpdater::callbackDeleteBB( 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."); @@ -94,4 +227,215 @@ void DomTreeUpdater::validateDeleteBB(BasicBlock *DelBB) { new UnreachableInst(DelBB->getContext(), DelBB); } +void DomTreeUpdater::applyUpdates(ArrayRef Updates) { + if (!DT && !PDT) + return; + + if (Strategy == UpdateStrategy::Lazy) { + PendUpdates.reserve(PendUpdates.size() + Updates.size()); + 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 Updates) { + if (!DT && !PDT) + return; + + SmallSet, 8> Seen; + SmallVector 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::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::const_iterator begin, + ArrayRef::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 (const 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 (const auto &BB : Callbacks) { + OS << " " << Index << " : "; + ++Index; + if (BB->hasName()) + OS << BB->getName() << "("; + else + OS << "(no_name)("; + OS << BB << ")\n"; + } +} +#endif } // namespace llvm diff --git a/llvm/lib/CodeGen/CMakeLists.txt b/llvm/lib/CodeGen/CMakeLists.txt index f1607f85c5b31..41fc555c93e93 100644 --- a/llvm/lib/CodeGen/CMakeLists.txt +++ b/llvm/lib/CodeGen/CMakeLists.txt @@ -116,7 +116,6 @@ add_llvm_component_library(LLVMCodeGen MachineCheckDebugify.cpp MachineCycleAnalysis.cpp MachineDebugify.cpp - MachineDomTreeUpdater.cpp MachineDominanceFrontier.cpp MachineDominators.cpp MachineFrameInfo.cpp diff --git a/llvm/lib/CodeGen/MachineBasicBlock.cpp b/llvm/lib/CodeGen/MachineBasicBlock.cpp index 533ab7cccaeb7..abf43e39ee9a6 100644 --- a/llvm/lib/CodeGen/MachineBasicBlock.cpp +++ b/llvm/lib/CodeGen/MachineBasicBlock.cpp @@ -315,12 +315,6 @@ bool MachineBasicBlock::isLegalToHoistInto() const { return true; } -bool MachineBasicBlock::hasName() const { - if (const BasicBlock *LBB = getBasicBlock()) - return LBB->hasName(); - return false; -} - StringRef MachineBasicBlock::getName() const { if (const BasicBlock *LBB = getBasicBlock()) return LBB->getName(); diff --git a/llvm/lib/CodeGen/MachineDomTreeUpdater.cpp b/llvm/lib/CodeGen/MachineDomTreeUpdater.cpp deleted file mode 100644 index 86b3185ca90a3..0000000000000 --- a/llvm/lib/CodeGen/MachineDomTreeUpdater.cpp +++ /dev/null @@ -1,62 +0,0 @@ -//===- MachineDomTreeUpdater.cpp -----------------------------------------===// -// -// 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 MachineDomTreeUpdater class, which provides a -// uniform way to update dominator tree related data structures. -// -//===----------------------------------------------------------------------===// - -#include "llvm/CodeGen/MachineDomTreeUpdater.h" -#include "llvm/ADT/SmallSet.h" -#include "llvm/CodeGen/MachinePostDominators.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/Instructions.h" -#include "llvm/Support/GenericDomTree.h" -#include -#include -#include - -namespace llvm { - -template class GenericDomTreeUpdater< - MachineDomTreeUpdater, MachineDominatorTree, MachinePostDominatorTree>; - -bool MachineDomTreeUpdater::forceFlushDeletedBB() { - if (DeletedBBs.empty()) - return false; - - for (auto *BB : DeletedBBs) { - eraseDelBBNode(BB); - BB->eraseFromParent(); - } - DeletedBBs.clear(); - return true; -} - -// The DT and PDT require the nodes related to updates -// are not deleted when update functions are called. -// So MachineBasicBlock deletions must be pended when the -// UpdateStrategy is Lazy. When the UpdateStrategy is -// Eager, the MachineBasicBlock will be deleted immediately. -void MachineDomTreeUpdater::deleteBB(MachineBasicBlock *DelBB) { - validateDeleteBB(DelBB); - if (Strategy == UpdateStrategy::Lazy) { - DeletedBBs.insert(DelBB); - return; - } - - eraseDelBBNode(DelBB); - DelBB->eraseFromParent(); -} - -void MachineDomTreeUpdater::validateDeleteBB(MachineBasicBlock *DelBB) { - assert(DelBB && "Invalid push_back of nullptr DelBB."); - assert(DelBB->pred_empty() && "DelBB has one or more predecessors."); -} - -} // namespace llvm diff --git a/llvm/unittests/CodeGen/CMakeLists.txt b/llvm/unittests/CodeGen/CMakeLists.txt index 963cdcc0275e1..dbbacdd95ec9f 100644 --- a/llvm/unittests/CodeGen/CMakeLists.txt +++ b/llvm/unittests/CodeGen/CMakeLists.txt @@ -32,7 +32,6 @@ add_llvm_unittest(CodeGenTests LowLevelTypeTest.cpp LexicalScopesTest.cpp MachineBasicBlockTest.cpp - MachineDomTreeUpdaterTest.cpp MachineInstrBundleIteratorTest.cpp MachineInstrTest.cpp MachineOperandTest.cpp diff --git a/llvm/unittests/CodeGen/MachineDomTreeUpdaterTest.cpp b/llvm/unittests/CodeGen/MachineDomTreeUpdaterTest.cpp deleted file mode 100644 index e123ba847e622..0000000000000 --- a/llvm/unittests/CodeGen/MachineDomTreeUpdaterTest.cpp +++ /dev/null @@ -1,276 +0,0 @@ -//===- MachineDomTreeUpdaterTest.cpp - MachineDomTreeUpdater unit tests ---===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "llvm/CodeGen/MachineDomTreeUpdater.h" -#include "llvm/Analysis/CGSCCPassManager.h" -#include "llvm/Analysis/LoopAnalysisManager.h" -#include "llvm/CodeGen/MIRParser/MIRParser.h" -#include "llvm/CodeGen/MachineFunctionAnalysis.h" -#include "llvm/CodeGen/MachineModuleInfo.h" -#include "llvm/CodeGen/MachinePassManager.h" -#include "llvm/CodeGen/MachinePostDominators.h" -#include "llvm/CodeGen/SelectionDAG.h" -#include "llvm/CodeGen/TargetLowering.h" -#include "llvm/MC/TargetRegistry.h" -#include "llvm/Passes/PassBuilder.h" -#include "llvm/Support/SourceMgr.h" -#include "llvm/Support/TargetSelect.h" -#include "llvm/Target/TargetMachine.h" -#include "gtest/gtest.h" - -using namespace llvm; - -class MachineDomTreeUpdaterTest : public testing::Test { -public: - LLVMContext Context; - std::unique_ptr TM; - std::unique_ptr M; - std::unique_ptr MMI; - std::unique_ptr MIR; - - LoopAnalysisManager LAM; - MachineFunctionAnalysisManager MFAM; - FunctionAnalysisManager FAM; - CGSCCAnalysisManager CGAM; - ModuleAnalysisManager MAM; - - ModulePassManager MPM; - FunctionPassManager FPM; - MachineFunctionPassManager MFPM; - - static void SetUpTestCase() { - InitializeAllTargets(); - InitializeAllTargetMCs(); - } - - void SetUp() override { - Triple TargetTriple("x86_64-unknown-linux-gnu"); - std::string Error; - const Target *T = TargetRegistry::lookupTarget("", TargetTriple, Error); - if (!T) - GTEST_SKIP(); - TargetOptions Options; - TM = std::unique_ptr( - T->createTargetMachine("X86", "", "", Options, std::nullopt)); - if (!TM) - GTEST_SKIP(); - MMI = std::make_unique( - static_cast(TM.get())); - - PassBuilder PB(TM.get()); - PB.registerModuleAnalyses(MAM); - PB.registerCGSCCAnalyses(CGAM); - PB.registerFunctionAnalyses(FAM); - PB.registerLoopAnalyses(LAM); - PB.registerMachineFunctionAnalyses(MFAM); - PB.crossRegisterProxies(LAM, FAM, CGAM, MAM, &MFAM); - MAM.registerPass([&] { return MachineModuleAnalysis(*MMI); }); - } - - bool parseMIR(StringRef MIRCode, const char *FnName) { - SMDiagnostic Diagnostic; - std::unique_ptr MBuffer = MemoryBuffer::getMemBuffer(MIRCode); - MIR = createMIRParser(std::move(MBuffer), Context); - if (!MIR) - return false; - - M = MIR->parseIRModule(); - M->setDataLayout(TM->createDataLayout()); - - if (MIR->parseMachineFunctions(*M, MAM)) { - M.reset(); - return false; - } - - return true; - } -}; - -TEST_F(MachineDomTreeUpdaterTest, EagerUpdateBasicOperations) { - StringRef MIRString = R"( ---- | - define i64 @f0(i64 %i, ptr %p) { - bb0: - store i64 %i, ptr %p, align 4 - switch i64 %i, label %bb1 [ - i64 1, label %bb2 - i64 2, label %bb3 - ] - bb1: ; preds = %bb0 - ret i64 1 - bb2: ; preds = %bb0 - ret i64 2 - bb3: ; preds = %bb0 - ret i64 3 - } -... ---- -name: f0 -body: | - bb.0.bb0: - successors: %bb.2, %bb.4 - liveins: $rdi, $rsi - - %1:gr32 = COPY $rsi - %0:gr64 = COPY $rdi - MOV64mr %1, 1, $noreg, 0, $noreg, %0 :: (store (s64) into %ir.p) - %2:gr64 = SUB64ri32 %0, 1, implicit-def $eflags - JCC_1 %bb.2, 4, implicit $eflags - JMP_1 %bb.4 - - bb.4.bb0: - successors: %bb.3, %bb.1 - - %3:gr64 = SUB64ri32 %0, 2, implicit-def $eflags - JCC_1 %bb.3, 4, implicit $eflags - JMP_1 %bb.1 - - bb.1.bb1: - %6:gr64 = MOV32ri64 1 - $rax = COPY %6 - RET 0, $rax - - bb.2.bb2: - %5:gr64 = MOV32ri64 2 - $rax = COPY %5 - RET 0, $rax - - bb.3.bb3: - %4:gr64 = MOV32ri64 3 - $rax = COPY %4 - RET 0, $rax - -... -)"; - - ASSERT_TRUE(parseMIR(MIRString, "f0")); - - auto &MF = - FAM.getResult(*M->getFunction("f0")).getMF(); - - MachineDominatorTree DT(MF); - MachinePostDominatorTree PDT(MF); - MachineDomTreeUpdater DTU(DT, PDT, - MachineDomTreeUpdater::UpdateStrategy::Eager); - - ASSERT_TRUE(DTU.hasDomTree()); - ASSERT_TRUE(DTU.hasPostDomTree()); - ASSERT_TRUE(DTU.isEager()); - ASSERT_FALSE(DTU.isLazy()); - ASSERT_TRUE(DTU.getDomTree().verify()); - ASSERT_TRUE(DTU.getPostDomTree().verify()); - ASSERT_FALSE(DTU.hasPendingUpdates()); - - auto B = MF.begin(); - [[maybe_unused]] auto BB0 = B; - auto BB1 = ++B; - auto BB2 = ++B; - [[maybe_unused]] auto BB3 = ++B; - auto BB4 = ++B; - EXPECT_EQ(BB1->succ_size(), 2u); - ASSERT_TRUE(DT.dominates(&*BB1, &*BB2)); - ASSERT_TRUE(DT.dominates(&*BB1, &*BB4)); - BB1->removeSuccessor(&*BB4); - DTU.deleteBB(&*BB4); - EXPECT_EQ(BB1->succ_size(), 1u); - ASSERT_TRUE(DT.dominates(&*BB1, &*BB2)); - ASSERT_EQ(DT.getNode(&*BB4), nullptr); -} - -TEST_F(MachineDomTreeUpdaterTest, LazyUpdateBasicOperations) { - StringRef MIRString = R"( ---- | - define i64 @f0(i64 %i, ptr %p) { - bb0: - store i64 %i, ptr %p, align 4 - switch i64 %i, label %bb1 [ - i64 1, label %bb2 - i64 2, label %bb3 - ] - bb1: ; preds = %bb0 - ret i64 1 - bb2: ; preds = %bb0 - ret i64 2 - bb3: ; preds = %bb0 - ret i64 3 - } -... ---- -name: f0 -body: | - bb.0.bb0: - successors: %bb.2, %bb.4 - liveins: $rdi, $rsi - - %1:gr32 = COPY $rsi - %0:gr64 = COPY $rdi - MOV64mr %1, 1, $noreg, 0, $noreg, %0 :: (store (s64) into %ir.p) - %2:gr64 = SUB64ri32 %0, 1, implicit-def $eflags - JCC_1 %bb.2, 4, implicit $eflags - JMP_1 %bb.4 - - bb.4.bb0: - successors: %bb.3, %bb.1 - - %3:gr64 = SUB64ri32 %0, 2, implicit-def $eflags - JCC_1 %bb.3, 4, implicit $eflags - JMP_1 %bb.1 - - bb.1.bb1: - %6:gr64 = MOV32ri64 1 - $rax = COPY %6 - RET 0, $rax - - bb.2.bb2: - %5:gr64 = MOV32ri64 2 - $rax = COPY %5 - RET 0, $rax - - bb.3.bb3: - %4:gr64 = MOV32ri64 3 - $rax = COPY %4 - RET 0, $rax - -... -)"; - - ASSERT_TRUE(parseMIR(MIRString, "f0")); - - auto &MF = - FAM.getResult(*M->getFunction("f0")).getMF(); - - MachineDominatorTree DT(MF); - MachinePostDominatorTree PDT(MF); - MachineDomTreeUpdater DTU(DT, PDT, - MachineDomTreeUpdater::UpdateStrategy::Lazy); - - ASSERT_TRUE(DTU.hasDomTree()); - ASSERT_TRUE(DTU.hasPostDomTree()); - ASSERT_FALSE(DTU.isEager()); - ASSERT_TRUE(DTU.isLazy()); - ASSERT_TRUE(DTU.getDomTree().verify()); - ASSERT_TRUE(DTU.getPostDomTree().verify()); - ASSERT_FALSE(DTU.hasPendingUpdates()); - - auto B = MF.begin(); - [[maybe_unused]] auto BB0 = B; - auto BB1 = ++B; - auto BB2 = ++B; - [[maybe_unused]] auto BB3 = ++B; - auto BB4 = ++B; - EXPECT_EQ(BB1->succ_size(), 2u); - ASSERT_TRUE(DT.dominates(&*BB1, &*BB2)); - ASSERT_TRUE(DT.dominates(&*BB1, &*BB4)); - BB1->removeSuccessor(&*BB4); - DTU.deleteBB(&*BB4); - EXPECT_EQ(BB1->succ_size(), 1u); - ASSERT_TRUE(DT.dominates(&*BB1, &*BB2)); - ASSERT_NE(DT.getNode(&*BB4), nullptr); - DTU.flush(); - ASSERT_EQ(DT.getNode(&*BB4), nullptr); -}