Skip to content

Commit c2f92fa

Browse files
authored
[Support] Store dominator tree nodes in a vector (#101705)
Use basic block numbers to store dominator tree nodes in a vector. This avoids frequent map lookups. Use block number epochs to validate that no renumbering occured since the tree was created; after a renumbering, the dominator tree can be updated with updateBlockNumbers(). Block numbers, block number epoch, and max block number are fetched via newly added GraphTraits methods. Graphs which do not implement getNumber() for blocks will use a DenseMap for an ad-hoc numbering.
1 parent 41b83ca commit c2f92fa

File tree

5 files changed

+244
-35
lines changed

5 files changed

+244
-35
lines changed

llvm/include/llvm/ADT/GraphTraits.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,20 @@ struct GraphTraits {
7171
// static unsigned size (GraphType *G)
7272
// Return total number of nodes in the graph
7373

74+
// Optionally implement the following:
75+
// static unsigned getNumber(NodeRef)
76+
// Return a unique number of a node. Numbers are ideally dense, these are
77+
// used to store nodes in a vector.
78+
// static unsigned getMaxNumber(GraphType *G)
79+
// Return the maximum number that getNumber() will return, or 0 if this is
80+
// unknown. Intended for reserving large enough buffers.
81+
// static unsigned getNumberEpoch(GraphType *G)
82+
// Return the "epoch" of the node numbers. Should return a different
83+
// number after renumbering, so users can assert that the epoch didn't
84+
// change => numbers are still valid. If renumberings are not tracked, it
85+
// is always valid to return a constant value. This is solely for to ease
86+
// debugging by having a way to detect use of outdated numbers.
87+
7488
// If anyone tries to use this class without having an appropriate
7589
// specialization, make an error. If you get this error, it's because you
7690
// need to include the appropriate specialization of GraphTraits<> for your

llvm/include/llvm/Support/GenericDomTree.h

Lines changed: 105 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -258,37 +258,40 @@ class DominatorTreeBase {
258258
// Dominators always have a single root, postdominators can have more.
259259
SmallVector<NodeT *, IsPostDom ? 4 : 1> Roots;
260260

261-
using DomTreeNodeMapType =
262-
DenseMap<NodeT *, std::unique_ptr<DomTreeNodeBase<NodeT>>>;
263-
DomTreeNodeMapType DomTreeNodes;
261+
using DomTreeNodeStorageTy =
262+
SmallVector<std::unique_ptr<DomTreeNodeBase<NodeT>>>;
263+
DomTreeNodeStorageTy DomTreeNodes;
264+
// For graphs where blocks don't have numbers, create a numbering here.
265+
DenseMap<const NodeT *, unsigned> NodeNumberMap;
264266
DomTreeNodeBase<NodeT> *RootNode = nullptr;
265267
ParentPtr Parent = nullptr;
266268

267269
mutable bool DFSInfoValid = false;
268270
mutable unsigned int SlowQueries = 0;
271+
unsigned BlockNumberEpoch = 0;
269272

270273
friend struct DomTreeBuilder::SemiNCAInfo<DominatorTreeBase>;
271274

272275
public:
273276
DominatorTreeBase() = default;
274277

275278
DominatorTreeBase(DominatorTreeBase &&Arg)
276-
: Roots(std::move(Arg.Roots)),
277-
DomTreeNodes(std::move(Arg.DomTreeNodes)),
278-
RootNode(Arg.RootNode),
279-
Parent(Arg.Parent),
280-
DFSInfoValid(Arg.DFSInfoValid),
281-
SlowQueries(Arg.SlowQueries) {
279+
: Roots(std::move(Arg.Roots)), DomTreeNodes(std::move(Arg.DomTreeNodes)),
280+
NodeNumberMap(std::move(Arg.NodeNumberMap)), RootNode(Arg.RootNode),
281+
Parent(Arg.Parent), DFSInfoValid(Arg.DFSInfoValid),
282+
SlowQueries(Arg.SlowQueries), BlockNumberEpoch(Arg.BlockNumberEpoch) {
282283
Arg.wipe();
283284
}
284285

285286
DominatorTreeBase &operator=(DominatorTreeBase &&RHS) {
286287
Roots = std::move(RHS.Roots);
287288
DomTreeNodes = std::move(RHS.DomTreeNodes);
289+
NodeNumberMap = std::move(RHS.NodeNumberMap);
288290
RootNode = RHS.RootNode;
289291
Parent = RHS.Parent;
290292
DFSInfoValid = RHS.DFSInfoValid;
291293
SlowQueries = RHS.SlowQueries;
294+
BlockNumberEpoch = RHS.BlockNumberEpoch;
292295
RHS.wipe();
293296
return *this;
294297
}
@@ -333,35 +336,70 @@ class DominatorTreeBase {
333336
if (!std::is_permutation(Roots.begin(), Roots.end(), Other.Roots.begin()))
334337
return true;
335338

336-
const DomTreeNodeMapType &OtherDomTreeNodes = Other.DomTreeNodes;
337-
if (DomTreeNodes.size() != OtherDomTreeNodes.size())
338-
return true;
339-
340-
for (const auto &DomTreeNode : DomTreeNodes) {
341-
NodeT *BB = DomTreeNode.first;
342-
typename DomTreeNodeMapType::const_iterator OI =
343-
OtherDomTreeNodes.find(BB);
344-
if (OI == OtherDomTreeNodes.end())
339+
size_t NumNodes = 0;
340+
// All nodes we have must exist and be equal in the other tree.
341+
for (const auto &Node : DomTreeNodes) {
342+
if (!Node)
343+
continue;
344+
if (Node->compare(Other.getNode(Node->getBlock())))
345345
return true;
346+
NumNodes++;
347+
}
346348

347-
DomTreeNodeBase<NodeT> &MyNd = *DomTreeNode.second;
348-
DomTreeNodeBase<NodeT> &OtherNd = *OI->second;
349+
// If the other tree has more nodes than we have, they're not equal.
350+
size_t NumOtherNodes = 0;
351+
for (const auto &OtherNode : Other.DomTreeNodes)
352+
if (OtherNode)
353+
NumOtherNodes++;
354+
return NumNodes != NumOtherNodes;
355+
}
349356

350-
if (MyNd.compare(&OtherNd))
351-
return true;
357+
private:
358+
template <typename T>
359+
using has_number_t =
360+
decltype(GraphTraits<T *>::getNumber(std::declval<T *>()));
361+
362+
std::optional<unsigned> getNodeIndex(const NodeT *BB) const {
363+
if constexpr (is_detected<has_number_t, NodeT>::value) {
364+
// BB can be nullptr, map nullptr to index 0.
365+
assert(BlockNumberEpoch ==
366+
GraphTraits<ParentPtr>::getNumberEpoch(Parent) &&
367+
"dominator tree used with outdated block numbers");
368+
return BB ? GraphTraits<const NodeT *>::getNumber(BB) + 1 : 0;
369+
} else {
370+
if (auto It = NodeNumberMap.find(BB); It != NodeNumberMap.end())
371+
return It->second;
372+
return std::nullopt;
352373
}
374+
}
353375

354-
return false;
376+
unsigned getNodeIndexForInsert(const NodeT *BB) {
377+
if constexpr (is_detected<has_number_t, NodeT>::value) {
378+
// getNodeIndex will never fail if nodes have getNumber().
379+
unsigned Idx = *getNodeIndex(BB);
380+
if (Idx >= DomTreeNodes.size()) {
381+
unsigned Max = GraphTraits<ParentPtr>::getMaxNumber(Parent);
382+
DomTreeNodes.resize(Max > Idx + 1 ? Max : Idx + 1);
383+
}
384+
return Idx;
385+
} else {
386+
// We might already have a number stored for BB.
387+
unsigned Idx =
388+
NodeNumberMap.try_emplace(BB, DomTreeNodes.size()).first->second;
389+
if (Idx >= DomTreeNodes.size())
390+
DomTreeNodes.resize(Idx + 1);
391+
return Idx;
392+
}
355393
}
356394

395+
public:
357396
/// getNode - return the (Post)DominatorTree node for the specified basic
358397
/// block. This is the same as using operator[] on this class. The result
359398
/// may (but is not required to) be null for a forward (backwards)
360399
/// statically unreachable block.
361400
DomTreeNodeBase<NodeT> *getNode(const NodeT *BB) const {
362-
auto I = DomTreeNodes.find(BB);
363-
if (I != DomTreeNodes.end())
364-
return I->second.get();
401+
if (auto Idx = getNodeIndex(BB); Idx && *Idx < DomTreeNodes.size())
402+
return DomTreeNodes[*Idx].get();
365403
return nullptr;
366404
}
367405

@@ -678,8 +716,10 @@ class DominatorTreeBase {
678716
/// dominate any other blocks. Removes node from its immediate dominator's
679717
/// children list. Deletes dominator node associated with basic block BB.
680718
void eraseNode(NodeT *BB) {
681-
DomTreeNodeBase<NodeT> *Node = getNode(BB);
682-
assert(Node && "Removing node that isn't in dominator tree.");
719+
std::optional<unsigned> IdxOpt = getNodeIndex(BB);
720+
assert(IdxOpt && DomTreeNodes[*IdxOpt] &&
721+
"Removing node that isn't in dominator tree.");
722+
DomTreeNodeBase<NodeT> *Node = DomTreeNodes[*IdxOpt].get();
683723
assert(Node->isLeaf() && "Node is not a leaf node.");
684724

685725
DFSInfoValid = false;
@@ -695,7 +735,8 @@ class DominatorTreeBase {
695735
IDom->Children.pop_back();
696736
}
697737

698-
DomTreeNodes.erase(BB);
738+
DomTreeNodes[*IdxOpt] = nullptr;
739+
NodeNumberMap.erase(BB);
699740

700741
if (!IsPostDom) return;
701742

@@ -786,17 +827,48 @@ class DominatorTreeBase {
786827
DFSInfoValid = true;
787828
}
788829

830+
private:
831+
void updateBlockNumberEpoch() {
832+
// Nothing to do for graphs that don't number their blocks.
833+
if constexpr (is_detected<has_number_t, NodeT>::value)
834+
BlockNumberEpoch = GraphTraits<ParentPtr>::getNumberEpoch(Parent);
835+
}
836+
837+
public:
789838
/// recalculate - compute a dominator tree for the given function
790839
void recalculate(ParentType &Func) {
791840
Parent = &Func;
841+
updateBlockNumberEpoch();
792842
DomTreeBuilder::Calculate(*this);
793843
}
794844

795845
void recalculate(ParentType &Func, ArrayRef<UpdateType> Updates) {
796846
Parent = &Func;
847+
updateBlockNumberEpoch();
797848
DomTreeBuilder::CalculateWithUpdates(*this, Updates);
798849
}
799850

851+
/// Update dominator tree after renumbering blocks.
852+
template <class T_ = NodeT>
853+
std::enable_if_t<is_detected<has_number_t, T_>::value, void>
854+
updateBlockNumbers() {
855+
updateBlockNumberEpoch();
856+
857+
unsigned MaxNumber = GraphTraits<ParentPtr>::getMaxNumber(Parent);
858+
DomTreeNodeStorageTy NewVector;
859+
NewVector.resize(MaxNumber + 1); // +1, because index 0 is for nullptr
860+
for (auto &Node : DomTreeNodes) {
861+
if (!Node)
862+
continue;
863+
unsigned Idx = *getNodeIndex(Node->getBlock());
864+
// getMaxNumber is not necessarily supported
865+
if (Idx >= NewVector.size())
866+
NewVector.resize(Idx + 1);
867+
NewVector[Idx] = std::move(Node);
868+
}
869+
DomTreeNodes = std::move(NewVector);
870+
}
871+
800872
/// verify - checks if the tree is correct. There are 3 level of verification:
801873
/// - Full -- verifies if the tree is correct by making sure all the
802874
/// properties (including the parent and the sibling property)
@@ -817,6 +889,7 @@ class DominatorTreeBase {
817889

818890
void reset() {
819891
DomTreeNodes.clear();
892+
NodeNumberMap.clear();
820893
Roots.clear();
821894
RootNode = nullptr;
822895
Parent = nullptr;
@@ -831,7 +904,8 @@ class DominatorTreeBase {
831904
DomTreeNodeBase<NodeT> *IDom = nullptr) {
832905
auto Node = std::make_unique<DomTreeNodeBase<NodeT>>(BB, IDom);
833906
auto *NodePtr = Node.get();
834-
DomTreeNodes[BB] = std::move(Node);
907+
unsigned NodeIdx = getNodeIndexForInsert(BB);
908+
DomTreeNodes[NodeIdx] = std::move(Node);
835909
if (IDom)
836910
IDom->addChild(NodePtr);
837911
return NodePtr;
@@ -915,6 +989,7 @@ class DominatorTreeBase {
915989
/// assignable and destroyable state, but otherwise invalid.
916990
void wipe() {
917991
DomTreeNodes.clear();
992+
NodeNumberMap.clear();
918993
RootNode = nullptr;
919994
Parent = nullptr;
920995
}

llvm/include/llvm/Support/GenericDomTreeConstruction.h

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,7 +1231,9 @@ struct SemiNCAInfo {
12311231
doFullDFSWalk(DT, AlwaysDescend);
12321232

12331233
for (auto &NodeToTN : DT.DomTreeNodes) {
1234-
const TreeNodePtr TN = NodeToTN.second.get();
1234+
const TreeNodePtr TN = NodeToTN.get();
1235+
if (!TN)
1236+
continue;
12351237
const NodePtr BB = TN->getBlock();
12361238

12371239
// Virtual root has a corresponding virtual CFG node.
@@ -1264,7 +1266,9 @@ struct SemiNCAInfo {
12641266
// Running time: O(N).
12651267
static bool VerifyLevels(const DomTreeT &DT) {
12661268
for (auto &NodeToTN : DT.DomTreeNodes) {
1267-
const TreeNodePtr TN = NodeToTN.second.get();
1269+
const TreeNodePtr TN = NodeToTN.get();
1270+
if (!TN)
1271+
continue;
12681272
const NodePtr BB = TN->getBlock();
12691273
if (!BB) continue;
12701274

@@ -1319,7 +1323,9 @@ struct SemiNCAInfo {
13191323
// For each tree node verify if children's DFS numbers cover their parent's
13201324
// DFS numbers with no gaps.
13211325
for (const auto &NodeToTN : DT.DomTreeNodes) {
1322-
const TreeNodePtr Node = NodeToTN.second.get();
1326+
const TreeNodePtr Node = NodeToTN.get();
1327+
if (!Node)
1328+
continue;
13231329

13241330
// Handle tree leaves.
13251331
if (Node->isLeaf()) {
@@ -1432,7 +1438,9 @@ struct SemiNCAInfo {
14321438
// the nodes it dominated previously will now become unreachable.
14331439
bool verifyParentProperty(const DomTreeT &DT) {
14341440
for (auto &NodeToTN : DT.DomTreeNodes) {
1435-
const TreeNodePtr TN = NodeToTN.second.get();
1441+
const TreeNodePtr TN = NodeToTN.get();
1442+
if (!TN)
1443+
continue;
14361444
const NodePtr BB = TN->getBlock();
14371445
if (!BB || TN->isLeaf())
14381446
continue;
@@ -1466,7 +1474,9 @@ struct SemiNCAInfo {
14661474
// siblings will now still be reachable.
14671475
bool verifySiblingProperty(const DomTreeT &DT) {
14681476
for (auto &NodeToTN : DT.DomTreeNodes) {
1469-
const TreeNodePtr TN = NodeToTN.second.get();
1477+
const TreeNodePtr TN = NodeToTN.get();
1478+
if (!TN)
1479+
continue;
14701480
const NodePtr BB = TN->getBlock();
14711481
if (!BB || TN->isLeaf())
14721482
continue;

llvm/unittests/Support/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ add_llvm_unittest(SupportTests
4444
FileOutputBufferTest.cpp
4545
FormatVariadicTest.cpp
4646
FSUniqueIDTest.cpp
47+
GenericDomTreeTest.cpp
4748
GlobPatternTest.cpp
4849
HashBuilderTest.cpp
4950
IndexedAccessorTest.cpp

0 commit comments

Comments
 (0)