[llvm] [CodeGen] Introduce `MachineDomTreeUpdater` (PR #95369)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 24 05:42:35 PDT 2024
https://github.com/paperchalice updated https://github.com/llvm/llvm-project/pull/95369
>From 3bcdc3e8105de1c9a8500d73474fb2cb0ddb45bf Mon Sep 17 00:00:00 2001
From: PaperChalice <liujunchang97 at outlook.com>
Date: Thu, 13 Jun 2024 16:03:52 +0800
Subject: [PATCH 1/2] [CodeGen] Introduce `MachineDomTreeUpdater` This commit
convert most of `DomTreeUpdater` into `GenericDomTreeUpdater` class template.
There are some differences between the interfaces of `BasicBlock` and
`MachineBasicBlock`, so there are some functions needed to implement by
subclasses.
---
llvm/include/llvm/Analysis/DomTreeUpdater.h | 196 ++-----
.../llvm/Analysis/GenericDomTreeUpdater.h | 525 ++++++++++++++++++
llvm/include/llvm/CodeGen/MachineBasicBlock.h | 3 +
.../llvm/CodeGen/MachineDomTreeUpdater.h | 72 +++
.../llvm/CodeGen/MachinePostDominators.h | 1 +
llvm/lib/Analysis/DomTreeUpdater.cpp | 348 +-----------
llvm/lib/CodeGen/CMakeLists.txt | 1 +
llvm/lib/CodeGen/MachineBasicBlock.cpp | 7 +
llvm/lib/CodeGen/MachineDomTreeUpdater.cpp | 62 +++
llvm/unittests/CodeGen/CMakeLists.txt | 1 +
.../CodeGen/MachineDomTreeUpdaterTest.cpp | 276 +++++++++
11 files changed, 982 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 <vector>
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<DomTreeUpdater, DominatorTree,
+ PostDominatorTree> {
+ friend GenericDomTreeUpdater<DomTreeUpdater, DominatorTree,
+ PostDominatorTree>;
- /// 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<DomTreeUpdater, DominatorTree, PostDominatorTree>;
+ 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<DominatorTree::UpdateType> 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<DominatorTree::UpdateType> 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<DominatorTree::UpdateType, 16> PendUpdates;
- size_t PendDTUpdateIndex = 0;
- size_t PendPDTUpdateIndex = 0;
- DominatorTree *DT = nullptr;
- PostDominatorTree *PDT = nullptr;
- const UpdateStrategy Strategy;
- SmallPtrSet<BasicBlock *, 8> DeletedBBs;
std::vector<CallBackOnDeletion> 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<DomTreeUpdater, DominatorTree,
+ PostDominatorTree>;
} // 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 <typename DerivedT, typename DomTreeT, typename PostDomTreeT>
+class GenericDomTreeUpdater {
+ DerivedT &derived() { return *static_cast<DerivedT *>(this); }
+ const DerivedT &derived() const {
+ return *static_cast<const DerivedT *>(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 <typename FuncT> 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<typename DomTreeT::UpdateType> 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<typename DomTreeT::UpdateType> Updates) {
+ if (!DT && !PDT)
+ return;
+
+ SmallSet<std::pair<BasicBlockT *, BasicBlockT *>, 8> Seen;
+ SmallVector<typename DomTreeT::UpdateType, 8> DeduplicatedUpdates;
+ for (const auto &U : Updates) {
+ auto Edge = std::make_pair(U.getFrom(), U.getTo());
+ // Because it is illegal to submit updates that have already been applied
+ // and updates to an edge need to be strictly ordered,
+ // it is safe to infer the existence of an edge from the first update
+ // to this edge.
+ // If the first update to an edge is "Delete", it means that the edge
+ // existed before. If the first update to an edge is "Insert", it means
+ // that the edge didn't exist before.
+ //
+ // For example, if the user submits {{Delete, A, B}, {Insert, A, B}},
+ // because
+ // 1. it is illegal to submit updates that have already been applied,
+ // i.e., user cannot delete an nonexistent edge,
+ // 2. updates to an edge need to be strictly ordered,
+ // So, initially edge A -> B existed.
+ // We can then safely ignore future updates to this edge and directly
+ // inspect the current CFG:
+ // a. If the edge still exists, because the user cannot insert an existent
+ // edge, so both {Delete, A, B}, {Insert, A, B} actually happened and
+ // resulted in a no-op. DTU won't submit any update in this case.
+ // b. If the edge doesn't exist, we can then infer that {Delete, A, B}
+ // actually happened but {Insert, A, B} was an invalid update which never
+ // happened. DTU will submit {Delete, A, B} in this case.
+ if (!isSelfDominance(U) && Seen.count(Edge) == 0) {
+ Seen.insert(Edge);
+ // If the update doesn't appear in the CFG, it means that
+ // either the change isn't made or relevant operations
+ // result in a no-op.
+ if (isUpdateValid(U)) {
+ if (isLazy())
+ PendUpdates.push_back(U);
+ else
+ DeduplicatedUpdates.push_back(U);
+ }
+ }
+ }
+
+ if (Strategy == UpdateStrategy::Lazy)
+ return;
+
+ if (DT)
+ DT->applyUpdates(DeduplicatedUpdates);
+ if (PDT)
+ PDT->applyUpdates(DeduplicatedUpdates);
+ }
+
+ ///@}
+
+ ///@{
+ /// \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<typename DomTreeT::UpdateType>::const_iterator
+ begin,
+ typename ArrayRef<typename DomTreeT::UpdateType>::const_iterator
+ end) {
+ if (begin == end)
+ OS << " None\n";
+ Index = 0;
+ for (auto It = begin, ItEnd = end; It != ItEnd; ++It) {
+ auto U = *It;
+ OS << " " << Index << " : ";
+ ++Index;
+ if (U.getKind() == 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<typename DomTreeT::UpdateType, 16> PendUpdates;
+ size_t PendDTUpdateIndex = 0;
+ size_t PendPDTUpdateIndex = 0;
+ DomTreeT *DT = nullptr;
+ PostDomTreeT *PDT = nullptr;
+ const UpdateStrategy Strategy;
+ SmallPtrSet<BasicBlockT *, 8> 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<typename DomTreeT::UpdateType>(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<typename DomTreeT::UpdateType>(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..8bc572e6e6af0
--- /dev/null
+++ b/llvm/include/llvm/CodeGen/MachineDomTreeUpdater.h
@@ -0,0 +1,72 @@
+//===- 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 <cstddef>
+#include <functional>
+#include <vector>
+
+namespace llvm {
+
+class MachineDomTreeUpdater
+ : public GenericDomTreeUpdater<MachineDomTreeUpdater, MachineDominatorTree,
+ MachinePostDominatorTree> {
+ friend GenericDomTreeUpdater<MachineDomTreeUpdater, MachineDominatorTree,
+ MachinePostDominatorTree>;
+
+public:
+ using Base =
+ GenericDomTreeUpdater<MachineDomTreeUpdater, MachineDominatorTree,
+ MachinePostDominatorTree>;
+ 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 c047e08266292..8846121a3fb7c 100644
--- a/llvm/include/llvm/CodeGen/MachinePostDominators.h
+++ b/llvm/include/llvm/CodeGen/MachinePostDominators.h
@@ -48,6 +48,7 @@ class MachinePostDominatorTree : public PostDomTreeBase<MachineBasicBlock> {
public:
MachinePostDominatorTree() = default;
+ explicit MachinePostDominatorTree(MachineFunction &MF) { recalculate(MF); }
/// Make findNearestCommonDominator(const NodeT *A, const NodeT *B) available.
using Base::findNearestCommonDominator;
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<DominatorTree::UpdateType>(I, E));
- PendDTUpdateIndex = PendUpdates.size();
- }
-}
-
-void DomTreeUpdater::flush() {
- applyDomTreeUpdates();
- applyPostDomTreeUpdates();
- dropOutOfDateUpdates();
-}
-
-void DomTreeUpdater::applyPostDomTreeUpdates() {
- // No pending PostDomTreeUpdates.
- if (Strategy != UpdateStrategy::Lazy || !PDT)
- return;
-
- // Only apply updates not are applied by PostDomTree.
- if (hasPendingPostDomTreeUpdates()) {
- const auto I = PendUpdates.begin() + PendPDTUpdateIndex;
- const auto E = PendUpdates.end();
- assert(I < E &&
- "Iterator range invalid; there should be PostDomTree updates.");
- PDT->applyUpdates(ArrayRef<DominatorTree::UpdateType>(I, E));
- PendPDTUpdateIndex = PendUpdates.size();
- }
-}
-
-void DomTreeUpdater::tryFlushDeletedBB() {
- if (!hasPendingUpdates())
- forceFlushDeletedBB();
-}
+template class GenericDomTreeUpdater<DomTreeUpdater, DominatorTree,
+ PostDominatorTree>;
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<DominatorTree::UpdateType> 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<DominatorTree::UpdateType> Updates) {
- if (!DT && !PDT)
- return;
-
- SmallSet<std::pair<BasicBlock *, BasicBlock *>, 8> Seen;
- SmallVector<DominatorTree::UpdateType, 8> DeduplicatedUpdates;
- for (const auto &U : Updates) {
- auto Edge = std::make_pair(U.getFrom(), U.getTo());
- // Because it is illegal to submit updates that have already been applied
- // and updates to an edge need to be strictly ordered,
- // it is safe to infer the existence of an edge from the first update
- // to this edge.
- // If the first update to an edge is "Delete", it means that the edge
- // existed before. If the first update to an edge is "Insert", it means
- // that the edge didn't exist before.
- //
- // For example, if the user submits {{Delete, A, B}, {Insert, A, B}},
- // because
- // 1. it is illegal to submit updates that have already been applied,
- // i.e., user cannot delete an nonexistent edge,
- // 2. updates to an edge need to be strictly ordered,
- // So, initially edge A -> B existed.
- // We can then safely ignore future updates to this edge and directly
- // inspect the current CFG:
- // a. If the edge still exists, because the user cannot insert an existent
- // edge, so both {Delete, A, B}, {Insert, A, B} actually happened and
- // resulted in a no-op. DTU won't submit any update in this case.
- // b. If the edge doesn't exist, we can then infer that {Delete, A, B}
- // actually happened but {Insert, A, B} was an invalid update which never
- // happened. DTU will submit {Delete, A, B} in this case.
- if (!isSelfDominance(U) && Seen.count(Edge) == 0) {
- Seen.insert(Edge);
- // If the update doesn't appear in the CFG, it means that
- // either the change isn't made or relevant operations
- // result in a no-op.
- if (isUpdateValid(U)) {
- if (isLazy())
- PendUpdates.push_back(U);
- else
- DeduplicatedUpdates.push_back(U);
- }
- }
- }
-
- if (Strategy == UpdateStrategy::Lazy)
- return;
-
- if (DT)
- DT->applyUpdates(DeduplicatedUpdates);
- if (PDT)
- PDT->applyUpdates(DeduplicatedUpdates);
-}
-
-DominatorTree &DomTreeUpdater::getDomTree() {
- assert(DT && "Invalid acquisition of a null DomTree");
- applyDomTreeUpdates();
- dropOutOfDateUpdates();
- return *DT;
-}
-
-PostDominatorTree &DomTreeUpdater::getPostDomTree() {
- assert(PDT && "Invalid acquisition of a null PostDomTree");
- applyPostDomTreeUpdates();
- dropOutOfDateUpdates();
- return *PDT;
-}
-
-void DomTreeUpdater::dropOutOfDateUpdates() {
- if (Strategy == DomTreeUpdater::UpdateStrategy::Eager)
- return;
-
- tryFlushDeletedBB();
-
- // Drop all updates applied by both trees.
- if (!DT)
- PendDTUpdateIndex = PendUpdates.size();
- if (!PDT)
- PendPDTUpdateIndex = PendUpdates.size();
-
- const size_t dropIndex = std::min(PendDTUpdateIndex, PendPDTUpdateIndex);
- const auto B = PendUpdates.begin();
- const auto E = PendUpdates.begin() + dropIndex;
- assert(B <= E && "Iterator out of range.");
- PendUpdates.erase(B, E);
- // Calculate current index.
- PendDTUpdateIndex -= dropIndex;
- PendPDTUpdateIndex -= dropIndex;
-}
-
-#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-LLVM_DUMP_METHOD void DomTreeUpdater::dump() const {
- raw_ostream &OS = llvm::dbgs();
-
- OS << "Available Trees: ";
- if (DT || PDT) {
- if (DT)
- OS << "DomTree ";
- if (PDT)
- OS << "PostDomTree ";
- OS << "\n";
- } else
- OS << "None\n";
-
- OS << "UpdateStrategy: ";
- if (Strategy == UpdateStrategy::Eager) {
- OS << "Eager\n";
- return;
- } else
- OS << "Lazy\n";
- int Index = 0;
-
- auto printUpdates =
- [&](ArrayRef<DominatorTree::UpdateType>::const_iterator begin,
- ArrayRef<DominatorTree::UpdateType>::const_iterator end) {
- if (begin == end)
- OS << " None\n";
- Index = 0;
- for (auto It = begin, ItEnd = end; It != ItEnd; ++It) {
- auto U = *It;
- OS << " " << Index << " : ";
- ++Index;
- if (U.getKind() == DominatorTree::Insert)
- OS << "Insert, ";
- else
- OS << "Delete, ";
- BasicBlock *From = U.getFrom();
- if (From) {
- auto S = From->getName();
- if (!From->hasName())
- S = "(no name)";
- OS << S << "(" << From << "), ";
- } else {
- OS << "(badref), ";
- }
- BasicBlock *To = U.getTo();
- if (To) {
- auto S = To->getName();
- if (!To->hasName())
- S = "(no_name)";
- OS << S << "(" << To << ")\n";
- } else {
- OS << "(badref)\n";
- }
- }
- };
-
- if (DT) {
- const auto I = PendUpdates.begin() + PendDTUpdateIndex;
- assert(PendUpdates.begin() <= I && I <= PendUpdates.end() &&
- "Iterator out of range.");
- OS << "Applied but not cleared DomTreeUpdates:\n";
- printUpdates(PendUpdates.begin(), I);
- OS << "Pending DomTreeUpdates:\n";
- printUpdates(I, PendUpdates.end());
- }
-
- if (PDT) {
- const auto I = PendUpdates.begin() + PendPDTUpdateIndex;
- assert(PendUpdates.begin() <= I && I <= PendUpdates.end() &&
- "Iterator out of range.");
- OS << "Applied but not cleared PostDomTreeUpdates:\n";
- printUpdates(PendUpdates.begin(), I);
- OS << "Pending PostDomTreeUpdates:\n";
- printUpdates(I, PendUpdates.end());
- }
-
- OS << "Pending DeletedBBs:\n";
- Index = 0;
- for (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 77bf1b165d0cf..8728cb9e11504 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 16505f21f0aad..be8a9e5744ea0 100644
--- a/llvm/lib/CodeGen/MachineBasicBlock.cpp
+++ b/llvm/lib/CodeGen/MachineBasicBlock.cpp
@@ -315,6 +315,13 @@ bool MachineBasicBlock::isLegalToHoistInto() const {
return true;
}
+bool MachineBasicBlock::hasName() const {
+ if (const BasicBlock *LBB = getBasicBlock())
+ return LBB->hasName();
+ else
+ 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 <algorithm>
+#include <functional>
+#include <utility>
+
+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..638a684f0cef6
--- /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<TargetMachine> TM;
+ std::unique_ptr<Module> M;
+ std::unique_ptr<MachineModuleInfo> MMI;
+ std::unique_ptr<MIRParser> 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<TargetMachine>(
+ T->createTargetMachine("X86", "", "", Options, std::nullopt));
+ if (!TM)
+ GTEST_SKIP();
+ MMI = std::make_unique<MachineModuleInfo>(
+ static_cast<LLVMTargetMachine *>(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<MemoryBuffer> 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<MachineFunctionAnalysis>(*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_TRUE(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<MachineFunctionAnalysis>(*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_FALSE(DT.getNode(&*BB4) == nullptr);
+ DTU.flush();
+ ASSERT_TRUE(DT.getNode(&*BB4) == nullptr);
+}
>From 1a1dc2bcd45e27d0bec171521aaff74162813fa8 Mon Sep 17 00:00:00 2001
From: PaperChalice <liujunchang97 at outlook.com>
Date: Mon, 24 Jun 2024 20:41:26 +0800
Subject: [PATCH 2/2] address comments
---
llvm/lib/CodeGen/MachineBasicBlock.cpp | 3 +--
llvm/unittests/CodeGen/MachineDomTreeUpdaterTest.cpp | 6 +++---
2 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/llvm/lib/CodeGen/MachineBasicBlock.cpp b/llvm/lib/CodeGen/MachineBasicBlock.cpp
index be8a9e5744ea0..4e9f9d4b58232 100644
--- a/llvm/lib/CodeGen/MachineBasicBlock.cpp
+++ b/llvm/lib/CodeGen/MachineBasicBlock.cpp
@@ -318,8 +318,7 @@ bool MachineBasicBlock::isLegalToHoistInto() const {
bool MachineBasicBlock::hasName() const {
if (const BasicBlock *LBB = getBasicBlock())
return LBB->hasName();
- else
- return false;
+ return false;
}
StringRef MachineBasicBlock::getName() const {
diff --git a/llvm/unittests/CodeGen/MachineDomTreeUpdaterTest.cpp b/llvm/unittests/CodeGen/MachineDomTreeUpdaterTest.cpp
index 638a684f0cef6..e123ba847e622 100644
--- a/llvm/unittests/CodeGen/MachineDomTreeUpdaterTest.cpp
+++ b/llvm/unittests/CodeGen/MachineDomTreeUpdaterTest.cpp
@@ -179,7 +179,7 @@ body: |
DTU.deleteBB(&*BB4);
EXPECT_EQ(BB1->succ_size(), 1u);
ASSERT_TRUE(DT.dominates(&*BB1, &*BB2));
- ASSERT_TRUE(DT.getNode(&*BB4) == nullptr);
+ ASSERT_EQ(DT.getNode(&*BB4), nullptr);
}
TEST_F(MachineDomTreeUpdaterTest, LazyUpdateBasicOperations) {
@@ -270,7 +270,7 @@ body: |
DTU.deleteBB(&*BB4);
EXPECT_EQ(BB1->succ_size(), 1u);
ASSERT_TRUE(DT.dominates(&*BB1, &*BB2));
- ASSERT_FALSE(DT.getNode(&*BB4) == nullptr);
+ ASSERT_NE(DT.getNode(&*BB4), nullptr);
DTU.flush();
- ASSERT_TRUE(DT.getNode(&*BB4) == nullptr);
+ ASSERT_EQ(DT.getNode(&*BB4), nullptr);
}
More information about the llvm-commits
mailing list