From e3ec08b3be3bdc0fa95bd5f874237b6b51103310 Mon Sep 17 00:00:00 2001 From: PaperChalice Date: Thu, 27 Jun 2024 13:11:07 +0800 Subject: [PATCH] Reapply "[CodeGen] Introduce `MachineDomTreeUpdater`" (#96846) This reverts commit 0f8849349ae3d3f2f537ad6ab233a586fb39d375. Resolve conflict in `MachinePostDominators.h` --- llvm/include/llvm/Analysis/DomTreeUpdater.h | 196 ++----- .../llvm/Analysis/GenericDomTreeUpdater.h | 525 ++++++++++++++++++ llvm/include/llvm/CodeGen/MachineBasicBlock.h | 3 + .../llvm/CodeGen/MachineDomTreeUpdater.h | 71 +++ llvm/lib/Analysis/DomTreeUpdater.cpp | 348 +----------- llvm/lib/CodeGen/CMakeLists.txt | 1 + llvm/lib/CodeGen/MachineBasicBlock.cpp | 6 + llvm/lib/CodeGen/MachineDomTreeUpdater.cpp | 62 +++ llvm/unittests/CodeGen/CMakeLists.txt | 1 + .../CodeGen/MachineDomTreeUpdaterTest.cpp | 276 +++++++++ 10 files changed, 979 insertions(+), 510 deletions(-) create mode 100644 llvm/include/llvm/Analysis/GenericDomTreeUpdater.h create mode 100644 llvm/include/llvm/CodeGen/MachineDomTreeUpdater.h create mode 100644 llvm/lib/CodeGen/MachineDomTreeUpdater.cpp create mode 100644 llvm/unittests/CodeGen/MachineDomTreeUpdaterTest.cpp diff --git a/llvm/include/llvm/Analysis/DomTreeUpdater.h b/llvm/include/llvm/Analysis/DomTreeUpdater.h index ddb958455ccd7..2b838a311440e 100644 --- a/llvm/include/llvm/Analysis/DomTreeUpdater.h +++ b/llvm/include/llvm/Analysis/DomTreeUpdater.h @@ -15,6 +15,8 @@ #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" @@ -23,66 +25,17 @@ #include namespace llvm { -class PostDominatorTree; -class DomTreeUpdater { -public: - 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; +class DomTreeUpdater + : public GenericDomTreeUpdater { + friend GenericDomTreeUpdater; - /// 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; +public: + using Base = + GenericDomTreeUpdater; + using Base::Base; ///@{ /// \name Mutation APIs @@ -105,51 +58,6 @@ 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. @@ -172,33 +80,6 @@ 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: @@ -216,16 +97,7 @@ 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 @@ -237,32 +109,28 @@ class DomTreeUpdater { /// Returns true if at least one BasicBlock is deleted. bool forceFlushDeletedBB(); - /// 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(); - - /// 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; + /// 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 + } }; + +extern template class GenericDomTreeUpdater; } // namespace llvm #endif // LLVM_ANALYSIS_DOMTREEUPDATER_H diff --git a/llvm/include/llvm/Analysis/GenericDomTreeUpdater.h b/llvm/include/llvm/Analysis/GenericDomTreeUpdater.h new file mode 100644 index 0000000000000..7092c67083a67 --- /dev/null +++ b/llvm/include/llvm/Analysis/GenericDomTreeUpdater.h @@ -0,0 +1,525 @@ +//===- 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 5b6be3a96b2fb..e4919ecabd705 100644 --- a/llvm/include/llvm/CodeGen/MachineBasicBlock.h +++ b/llvm/include/llvm/CodeGen/MachineBasicBlock.h @@ -238,6 +238,9 @@ 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 new file mode 100644 index 0000000000000..9e59ba5f01ab7 --- /dev/null +++ b/llvm/include/llvm/CodeGen/MachineDomTreeUpdater.h @@ -0,0 +1,71 @@ +//===- 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/lib/Analysis/DomTreeUpdater.cpp b/llvm/lib/Analysis/DomTreeUpdater.cpp index 0a16fc9a76d29..676cb87210176 100644 --- a/llvm/lib/Analysis/DomTreeUpdater.cpp +++ b/llvm/lib/Analysis/DomTreeUpdater.cpp @@ -23,79 +23,8 @@ 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::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(); -} +template class GenericDomTreeUpdater; bool DomTreeUpdater::forceFlushDeletedBB() { if (DeletedBBs.empty()) @@ -117,58 +46,6 @@ 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 @@ -201,16 +78,6 @@ 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."); @@ -227,215 +94,4 @@ 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 41fc555c93e93..f1607f85c5b31 100644 --- a/llvm/lib/CodeGen/CMakeLists.txt +++ b/llvm/lib/CodeGen/CMakeLists.txt @@ -116,6 +116,7 @@ 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 abf43e39ee9a6..533ab7cccaeb7 100644 --- a/llvm/lib/CodeGen/MachineBasicBlock.cpp +++ b/llvm/lib/CodeGen/MachineBasicBlock.cpp @@ -315,6 +315,12 @@ 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 new file mode 100644 index 0000000000000..86b3185ca90a3 --- /dev/null +++ b/llvm/lib/CodeGen/MachineDomTreeUpdater.cpp @@ -0,0 +1,62 @@ +//===- 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 dbbacdd95ec9f..963cdcc0275e1 100644 --- a/llvm/unittests/CodeGen/CMakeLists.txt +++ b/llvm/unittests/CodeGen/CMakeLists.txt @@ -32,6 +32,7 @@ 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 new file mode 100644 index 0000000000000..e123ba847e622 --- /dev/null +++ b/llvm/unittests/CodeGen/MachineDomTreeUpdaterTest.cpp @@ -0,0 +1,276 @@ +//===- 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); +}