xref: /llvm-project/clang/lib/Analysis/IntervalPartition.cpp (revision 65c36179be68dda0d1cc5d7e5c5b312a6b52cc0e)
1f4cf51c9SYitzhak Mandelbaum //===- IntervalPartition.cpp - CFG Partitioning into Intervals --*- C++ -*-===//
2f4cf51c9SYitzhak Mandelbaum //
3f4cf51c9SYitzhak Mandelbaum // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4f4cf51c9SYitzhak Mandelbaum // See https://llvm.org/LICENSE.txt for license information.
5f4cf51c9SYitzhak Mandelbaum // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6f4cf51c9SYitzhak Mandelbaum //
7f4cf51c9SYitzhak Mandelbaum //===----------------------------------------------------------------------===//
8f4cf51c9SYitzhak Mandelbaum //
9f4cf51c9SYitzhak Mandelbaum //  This file defines functionality for partitioning a CFG into intervals.
10f4cf51c9SYitzhak Mandelbaum //
11f4cf51c9SYitzhak Mandelbaum //===----------------------------------------------------------------------===//
12f4cf51c9SYitzhak Mandelbaum 
13f4cf51c9SYitzhak Mandelbaum #include "clang/Analysis/Analyses/IntervalPartition.h"
14f4cf51c9SYitzhak Mandelbaum #include "clang/Analysis/CFG.h"
15f4cf51c9SYitzhak Mandelbaum #include "llvm/ADT/BitVector.h"
1626db5e65SYitzhak Mandelbaum #include "llvm/ADT/STLExtras.h"
1726db5e65SYitzhak Mandelbaum #include <optional>
18f4cf51c9SYitzhak Mandelbaum #include <queue>
19f4cf51c9SYitzhak Mandelbaum #include <vector>
20f4cf51c9SYitzhak Mandelbaum 
21f4cf51c9SYitzhak Mandelbaum namespace clang {
22f4cf51c9SYitzhak Mandelbaum 
2326db5e65SYitzhak Mandelbaum // Intermediate data used in constructing a CFGIntervalNode.
2426db5e65SYitzhak Mandelbaum template <typename Node> struct BuildResult {
2526db5e65SYitzhak Mandelbaum   // Use a vector to maintain the insertion order. Given the expected small
26e21b1dd9SYitzhak Mandelbaum   // number of nodes, vector should be sufficiently efficient. Elements must not
27e21b1dd9SYitzhak Mandelbaum   // be null.
2826db5e65SYitzhak Mandelbaum   std::vector<const Node *> Nodes;
29e21b1dd9SYitzhak Mandelbaum   // Elements must not be null.
3026db5e65SYitzhak Mandelbaum   llvm::SmallDenseSet<const Node *> Successors;
3126db5e65SYitzhak Mandelbaum };
32f4cf51c9SYitzhak Mandelbaum 
3326db5e65SYitzhak Mandelbaum namespace internal {
34e21b1dd9SYitzhak Mandelbaum static unsigned getID(const CFGBlock &B) { return B.getBlockID(); }
35e21b1dd9SYitzhak Mandelbaum static unsigned getID(const CFGIntervalNode &I) { return I.ID; }
3626db5e65SYitzhak Mandelbaum 
3726db5e65SYitzhak Mandelbaum // `Node` must be one of `CFGBlock` or `CFGIntervalNode`.
3826db5e65SYitzhak Mandelbaum template <typename Node>
39*65c36179SCongcong Cai static BuildResult<Node> buildInterval(llvm::BitVector &Partitioned,
4026db5e65SYitzhak Mandelbaum                                        const Node *Header) {
41e21b1dd9SYitzhak Mandelbaum   assert(Header != nullptr);
4226db5e65SYitzhak Mandelbaum   BuildResult<Node> Interval;
4326db5e65SYitzhak Mandelbaum   Interval.Nodes.push_back(Header);
44e21b1dd9SYitzhak Mandelbaum   Partitioned.set(getID(*Header));
4526db5e65SYitzhak Mandelbaum 
4626db5e65SYitzhak Mandelbaum   // FIXME: Compare performance against using RPO to consider nodes, rather than
4726db5e65SYitzhak Mandelbaum   // following successors.
4826db5e65SYitzhak Mandelbaum   //
49f4cf51c9SYitzhak Mandelbaum   // Elements must not be null. Duplicates are prevented using `Workset`, below.
5026db5e65SYitzhak Mandelbaum   std::queue<const Node *> Worklist;
5126db5e65SYitzhak Mandelbaum   llvm::BitVector Workset(Partitioned.size(), false);
5226db5e65SYitzhak Mandelbaum   for (const Node *S : Header->succs())
53f4cf51c9SYitzhak Mandelbaum     if (S != nullptr)
54e21b1dd9SYitzhak Mandelbaum       if (auto SID = getID(*S); !Partitioned.test(SID)) {
55f4cf51c9SYitzhak Mandelbaum         // Successors are unique, so we don't test against `Workset` before
56f4cf51c9SYitzhak Mandelbaum         // adding to `Worklist`.
57f4cf51c9SYitzhak Mandelbaum         Worklist.push(S);
58f4cf51c9SYitzhak Mandelbaum         Workset.set(SID);
59f4cf51c9SYitzhak Mandelbaum       }
60f4cf51c9SYitzhak Mandelbaum 
61f4cf51c9SYitzhak Mandelbaum   // Contains successors of blocks in the interval that couldn't be added to the
62f4cf51c9SYitzhak Mandelbaum   // interval on their first encounter. This occurs when they have a predecessor
63f4cf51c9SYitzhak Mandelbaum   // that is either definitively outside the interval or hasn't been considered
64f4cf51c9SYitzhak Mandelbaum   // yet. In the latter case, we'll revisit the block through some other path
65f4cf51c9SYitzhak Mandelbaum   // from the interval. At the end of processing the worklist, we filter out any
66f4cf51c9SYitzhak Mandelbaum   // that ended up in the interval to produce the output set of interval
67e21b1dd9SYitzhak Mandelbaum   // successors. Elements are never null.
6826db5e65SYitzhak Mandelbaum   std::vector<const Node *> MaybeSuccessors;
69f4cf51c9SYitzhak Mandelbaum 
70f4cf51c9SYitzhak Mandelbaum   while (!Worklist.empty()) {
71f4cf51c9SYitzhak Mandelbaum     const auto *B = Worklist.front();
72e21b1dd9SYitzhak Mandelbaum     auto ID = getID(*B);
73f4cf51c9SYitzhak Mandelbaum     Worklist.pop();
74f4cf51c9SYitzhak Mandelbaum     Workset.reset(ID);
75f4cf51c9SYitzhak Mandelbaum 
76f4cf51c9SYitzhak Mandelbaum     // Check whether all predecessors are in the interval, in which case `B`
77f4cf51c9SYitzhak Mandelbaum     // is included as well.
7826db5e65SYitzhak Mandelbaum     bool AllInInterval = llvm::all_of(B->preds(), [&](const Node *P) {
7926db5e65SYitzhak Mandelbaum       return llvm::is_contained(Interval.Nodes, P);
8026db5e65SYitzhak Mandelbaum     });
81f4cf51c9SYitzhak Mandelbaum     if (AllInInterval) {
8226db5e65SYitzhak Mandelbaum       Interval.Nodes.push_back(B);
83f4cf51c9SYitzhak Mandelbaum       Partitioned.set(ID);
8426db5e65SYitzhak Mandelbaum       for (const Node *S : B->succs())
85f4cf51c9SYitzhak Mandelbaum         if (S != nullptr)
86e21b1dd9SYitzhak Mandelbaum           if (auto SID = getID(*S);
87f4cf51c9SYitzhak Mandelbaum               !Partitioned.test(SID) && !Workset.test(SID)) {
88f4cf51c9SYitzhak Mandelbaum             Worklist.push(S);
89f4cf51c9SYitzhak Mandelbaum             Workset.set(SID);
90f4cf51c9SYitzhak Mandelbaum           }
9126db5e65SYitzhak Mandelbaum     } else {
9226db5e65SYitzhak Mandelbaum       MaybeSuccessors.push_back(B);
93f4cf51c9SYitzhak Mandelbaum     }
94f4cf51c9SYitzhak Mandelbaum   }
95f4cf51c9SYitzhak Mandelbaum 
96f4cf51c9SYitzhak Mandelbaum   // Any block successors not in the current interval are interval successors.
9726db5e65SYitzhak Mandelbaum   for (const Node *B : MaybeSuccessors)
9826db5e65SYitzhak Mandelbaum     if (!llvm::is_contained(Interval.Nodes, B))
99f4cf51c9SYitzhak Mandelbaum       Interval.Successors.insert(B);
100f4cf51c9SYitzhak Mandelbaum 
101f4cf51c9SYitzhak Mandelbaum   return Interval;
102f4cf51c9SYitzhak Mandelbaum }
103f4cf51c9SYitzhak Mandelbaum 
10426db5e65SYitzhak Mandelbaum template <typename Node>
105*65c36179SCongcong Cai static void fillIntervalNode(CFGIntervalGraph &Graph,
10626db5e65SYitzhak Mandelbaum                              std::vector<CFGIntervalNode *> &Index,
10726db5e65SYitzhak Mandelbaum                              std::queue<const Node *> &Successors,
10826db5e65SYitzhak Mandelbaum                              llvm::BitVector &Partitioned, const Node *Header) {
10926db5e65SYitzhak Mandelbaum   BuildResult<Node> Result = buildInterval(Partitioned, Header);
11026db5e65SYitzhak Mandelbaum   for (const auto *S : Result.Successors)
11126db5e65SYitzhak Mandelbaum     Successors.push(S);
11226db5e65SYitzhak Mandelbaum 
11326db5e65SYitzhak Mandelbaum   CFGIntervalNode &Interval = Graph.emplace_back(Graph.size());
11426db5e65SYitzhak Mandelbaum 
11526db5e65SYitzhak Mandelbaum   // Index the nodes of the new interval. The index maps nodes from the input
11626db5e65SYitzhak Mandelbaum   // graph (specifically, `Result.Nodes`) to identifiers of nodes in the output
11726db5e65SYitzhak Mandelbaum   // graph. In this case, the new interval has identifier `ID` so all of its
11826db5e65SYitzhak Mandelbaum   // nodes (`Result.Nodes`) map to `ID`.
11926db5e65SYitzhak Mandelbaum   for (const auto *N : Result.Nodes) {
120e21b1dd9SYitzhak Mandelbaum     assert(N != nullptr);
121e21b1dd9SYitzhak Mandelbaum     assert(getID(*N) < Index.size());
122e21b1dd9SYitzhak Mandelbaum     Index[getID(*N)] = &Interval;
123f4cf51c9SYitzhak Mandelbaum   }
124f4cf51c9SYitzhak Mandelbaum 
12526db5e65SYitzhak Mandelbaum   if constexpr (std::is_same_v<std::decay_t<Node>, CFGBlock>)
12626db5e65SYitzhak Mandelbaum     Interval.Nodes = std::move(Result.Nodes);
12726db5e65SYitzhak Mandelbaum   else {
12826db5e65SYitzhak Mandelbaum     std::vector<const CFGBlock *> Nodes;
12926db5e65SYitzhak Mandelbaum     // Flatten the sub vectors into a single list.
13026db5e65SYitzhak Mandelbaum     size_t Count = 0;
13126db5e65SYitzhak Mandelbaum     for (auto &N : Result.Nodes)
13226db5e65SYitzhak Mandelbaum       Count += N->Nodes.size();
13326db5e65SYitzhak Mandelbaum     Nodes.reserve(Count);
13426db5e65SYitzhak Mandelbaum     for (auto &N : Result.Nodes)
13526db5e65SYitzhak Mandelbaum       Nodes.insert(Nodes.end(), N->Nodes.begin(), N->Nodes.end());
13626db5e65SYitzhak Mandelbaum     Interval.Nodes = std::move(Nodes);
13726db5e65SYitzhak Mandelbaum   }
13826db5e65SYitzhak Mandelbaum }
139f4cf51c9SYitzhak Mandelbaum 
14026db5e65SYitzhak Mandelbaum template <typename Node>
141*65c36179SCongcong Cai static CFGIntervalGraph partitionIntoIntervalsImpl(unsigned NumBlockIDs,
14226db5e65SYitzhak Mandelbaum                                                    const Node *EntryBlock) {
14326db5e65SYitzhak Mandelbaum   assert(EntryBlock != nullptr);
14426db5e65SYitzhak Mandelbaum   CFGIntervalGraph Graph;
14526db5e65SYitzhak Mandelbaum   // `Index` maps all of the nodes of the input graph to the interval to which
14626db5e65SYitzhak Mandelbaum   // they are assigned in the output graph. The values (interval pointers) are
14726db5e65SYitzhak Mandelbaum   // never null.
14826db5e65SYitzhak Mandelbaum   std::vector<CFGIntervalNode *> Index(NumBlockIDs, nullptr);
14926db5e65SYitzhak Mandelbaum 
15026db5e65SYitzhak Mandelbaum   // Lists header nodes (from the input graph) and their associated
15126db5e65SYitzhak Mandelbaum   // interval. Since header nodes can vary in type and are only needed within
15226db5e65SYitzhak Mandelbaum   // this function, we record them separately from `CFGIntervalNode`. This
15326db5e65SYitzhak Mandelbaum   // choice enables to express `CFGIntervalNode` without using a variant.
15426db5e65SYitzhak Mandelbaum   std::vector<std::pair<const Node *, CFGIntervalNode *>> Intervals;
15526db5e65SYitzhak Mandelbaum   llvm::BitVector Partitioned(NumBlockIDs, false);
15626db5e65SYitzhak Mandelbaum   std::queue<const Node *> Successors;
15726db5e65SYitzhak Mandelbaum 
15826db5e65SYitzhak Mandelbaum   fillIntervalNode(Graph, Index, Successors, Partitioned, EntryBlock);
15926db5e65SYitzhak Mandelbaum   Intervals.emplace_back(EntryBlock, &Graph.back());
160f4cf51c9SYitzhak Mandelbaum 
161f4cf51c9SYitzhak Mandelbaum   while (!Successors.empty()) {
162f4cf51c9SYitzhak Mandelbaum     const auto *B = Successors.front();
163f4cf51c9SYitzhak Mandelbaum     Successors.pop();
164e21b1dd9SYitzhak Mandelbaum     assert(B != nullptr);
165e21b1dd9SYitzhak Mandelbaum     if (Partitioned.test(getID(*B)))
166f4cf51c9SYitzhak Mandelbaum       continue;
167f4cf51c9SYitzhak Mandelbaum 
16826db5e65SYitzhak Mandelbaum     // B has not been partitioned, but it has a predecessor that has. Create a
16926db5e65SYitzhak Mandelbaum     // new interval from `B`.
17026db5e65SYitzhak Mandelbaum     fillIntervalNode(Graph, Index, Successors, Partitioned, B);
17126db5e65SYitzhak Mandelbaum     Intervals.emplace_back(B, &Graph.back());
172f4cf51c9SYitzhak Mandelbaum   }
173f4cf51c9SYitzhak Mandelbaum 
17426db5e65SYitzhak Mandelbaum   // Go back and patch up all the Intervals -- the successors and predecessors.
17526db5e65SYitzhak Mandelbaum   for (auto [H, N] : Intervals) {
17626db5e65SYitzhak Mandelbaum     // Map input-graph predecessors to output-graph nodes and mark those as
17726db5e65SYitzhak Mandelbaum     // predecessors of `N`. Then, mark `N` as a successor of said predecessor.
17826db5e65SYitzhak Mandelbaum     for (const Node *P : H->preds()) {
179e21b1dd9SYitzhak Mandelbaum       if (P == nullptr)
180e21b1dd9SYitzhak Mandelbaum         continue;
181e21b1dd9SYitzhak Mandelbaum 
182e21b1dd9SYitzhak Mandelbaum       assert(getID(*P) < NumBlockIDs);
183e21b1dd9SYitzhak Mandelbaum       CFGIntervalNode *Pred = Index[getID(*P)];
18426db5e65SYitzhak Mandelbaum       if (Pred == nullptr)
18526db5e65SYitzhak Mandelbaum         // Unreachable node.
18626db5e65SYitzhak Mandelbaum         continue;
18726db5e65SYitzhak Mandelbaum       if (Pred != N // Not a backedge.
18826db5e65SYitzhak Mandelbaum           && N->Predecessors.insert(Pred).second)
18926db5e65SYitzhak Mandelbaum         // Note: given the guard above, which guarantees we only ever insert
19026db5e65SYitzhak Mandelbaum         // unique elements, we could use a simple list (like `vector`) for
19126db5e65SYitzhak Mandelbaum         // `Successors`, rather than a set.
19226db5e65SYitzhak Mandelbaum         Pred->Successors.insert(N);
19326db5e65SYitzhak Mandelbaum     }
194f4cf51c9SYitzhak Mandelbaum   }
195f4cf51c9SYitzhak Mandelbaum 
19626db5e65SYitzhak Mandelbaum   return Graph;
19726db5e65SYitzhak Mandelbaum }
19826db5e65SYitzhak Mandelbaum 
19926db5e65SYitzhak Mandelbaum std::vector<const CFGBlock *> buildInterval(const CFGBlock *Header) {
20026db5e65SYitzhak Mandelbaum   llvm::BitVector Partitioned(Header->getParent()->getNumBlockIDs(), false);
20126db5e65SYitzhak Mandelbaum   return buildInterval(Partitioned, Header).Nodes;
20226db5e65SYitzhak Mandelbaum }
20326db5e65SYitzhak Mandelbaum 
20426db5e65SYitzhak Mandelbaum CFGIntervalGraph partitionIntoIntervals(const CFG &Cfg) {
20526db5e65SYitzhak Mandelbaum   return partitionIntoIntervalsImpl(Cfg.getNumBlockIDs(), &Cfg.getEntry());
20626db5e65SYitzhak Mandelbaum }
20726db5e65SYitzhak Mandelbaum 
20826db5e65SYitzhak Mandelbaum CFGIntervalGraph partitionIntoIntervals(const CFGIntervalGraph &Graph) {
20926db5e65SYitzhak Mandelbaum   return partitionIntoIntervalsImpl(Graph.size(), &Graph[0]);
21026db5e65SYitzhak Mandelbaum }
21126db5e65SYitzhak Mandelbaum } // namespace internal
21226db5e65SYitzhak Mandelbaum 
21326db5e65SYitzhak Mandelbaum std::optional<std::vector<const CFGBlock *>> getIntervalWTO(const CFG &Cfg) {
21426db5e65SYitzhak Mandelbaum   // Backing storage for the allocated nodes in each graph.
21526db5e65SYitzhak Mandelbaum   unsigned PrevSize = Cfg.size();
21626db5e65SYitzhak Mandelbaum   if (PrevSize == 0)
21726db5e65SYitzhak Mandelbaum     return {};
21826db5e65SYitzhak Mandelbaum   internal::CFGIntervalGraph Graph = internal::partitionIntoIntervals(Cfg);
21926db5e65SYitzhak Mandelbaum   unsigned Size = Graph.size();
22026db5e65SYitzhak Mandelbaum   while (Size > 1 && Size < PrevSize) {
22126db5e65SYitzhak Mandelbaum     PrevSize = Graph.size();
22226db5e65SYitzhak Mandelbaum     Graph = internal::partitionIntoIntervals(Graph);
22326db5e65SYitzhak Mandelbaum     Size = Graph.size();
22426db5e65SYitzhak Mandelbaum   }
22526db5e65SYitzhak Mandelbaum   if (Size > 1)
22626db5e65SYitzhak Mandelbaum     // Not reducible.
22726db5e65SYitzhak Mandelbaum     return std::nullopt;
22826db5e65SYitzhak Mandelbaum 
22926db5e65SYitzhak Mandelbaum   assert(Size != 0);
23026db5e65SYitzhak Mandelbaum   return std::move(Graph[0].Nodes);
23126db5e65SYitzhak Mandelbaum }
23226db5e65SYitzhak Mandelbaum 
23326db5e65SYitzhak Mandelbaum WTOCompare::WTOCompare(const WeakTopologicalOrdering &WTO) {
23426db5e65SYitzhak Mandelbaum   if (WTO.empty())
23526db5e65SYitzhak Mandelbaum     return;
23626db5e65SYitzhak Mandelbaum   auto N = WTO[0]->getParent()->getNumBlockIDs();
23726db5e65SYitzhak Mandelbaum   BlockOrder.resize(N, 0);
238e21b1dd9SYitzhak Mandelbaum   for (unsigned I = 0, S = WTO.size(); I < S; ++I)
23926db5e65SYitzhak Mandelbaum     BlockOrder[WTO[I]->getBlockID()] = I + 1;
24026db5e65SYitzhak Mandelbaum }
241f4cf51c9SYitzhak Mandelbaum } // namespace clang
242