xref: /freebsd-src/contrib/llvm-project/clang/lib/Analysis/IntervalPartition.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
106c3fb27SDimitry Andric //===- IntervalPartition.cpp - CFG Partitioning into Intervals --*- C++ -*-===//
206c3fb27SDimitry Andric //
306c3fb27SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
406c3fb27SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
506c3fb27SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
606c3fb27SDimitry Andric //
706c3fb27SDimitry Andric //===----------------------------------------------------------------------===//
806c3fb27SDimitry Andric //
906c3fb27SDimitry Andric //  This file defines functionality for partitioning a CFG into intervals.
1006c3fb27SDimitry Andric //
1106c3fb27SDimitry Andric //===----------------------------------------------------------------------===//
1206c3fb27SDimitry Andric 
1306c3fb27SDimitry Andric #include "clang/Analysis/Analyses/IntervalPartition.h"
1406c3fb27SDimitry Andric #include "clang/Analysis/CFG.h"
1506c3fb27SDimitry Andric #include "llvm/ADT/BitVector.h"
16*5f757f3fSDimitry Andric #include "llvm/ADT/STLExtras.h"
17*5f757f3fSDimitry Andric #include <optional>
1806c3fb27SDimitry Andric #include <queue>
1906c3fb27SDimitry Andric #include <vector>
2006c3fb27SDimitry Andric 
2106c3fb27SDimitry Andric namespace clang {
2206c3fb27SDimitry Andric 
23*5f757f3fSDimitry Andric // Intermediate data used in constructing a CFGIntervalNode.
24*5f757f3fSDimitry Andric template <typename Node> struct BuildResult {
25*5f757f3fSDimitry Andric   // Use a vector to maintain the insertion order. Given the expected small
26*5f757f3fSDimitry Andric   // number of nodes, vector should be sufficiently efficient. Elements must not
27*5f757f3fSDimitry Andric   // be null.
28*5f757f3fSDimitry Andric   std::vector<const Node *> Nodes;
29*5f757f3fSDimitry Andric   // Elements must not be null.
30*5f757f3fSDimitry Andric   llvm::SmallDenseSet<const Node *> Successors;
31*5f757f3fSDimitry Andric };
3206c3fb27SDimitry Andric 
33*5f757f3fSDimitry Andric namespace internal {
getID(const CFGBlock & B)34*5f757f3fSDimitry Andric static unsigned getID(const CFGBlock &B) { return B.getBlockID(); }
getID(const CFGIntervalNode & I)35*5f757f3fSDimitry Andric static unsigned getID(const CFGIntervalNode &I) { return I.ID; }
36*5f757f3fSDimitry Andric 
37*5f757f3fSDimitry Andric // `Node` must be one of `CFGBlock` or `CFGIntervalNode`.
38*5f757f3fSDimitry Andric template <typename Node>
buildInterval(llvm::BitVector & Partitioned,const Node * Header)39*5f757f3fSDimitry Andric BuildResult<Node> buildInterval(llvm::BitVector &Partitioned,
40*5f757f3fSDimitry Andric                                 const Node *Header) {
41*5f757f3fSDimitry Andric   assert(Header != nullptr);
42*5f757f3fSDimitry Andric   BuildResult<Node> Interval;
43*5f757f3fSDimitry Andric   Interval.Nodes.push_back(Header);
44*5f757f3fSDimitry Andric   Partitioned.set(getID(*Header));
45*5f757f3fSDimitry Andric 
46*5f757f3fSDimitry Andric   // FIXME: Compare performance against using RPO to consider nodes, rather than
47*5f757f3fSDimitry Andric   // following successors.
48*5f757f3fSDimitry Andric   //
4906c3fb27SDimitry Andric   // Elements must not be null. Duplicates are prevented using `Workset`, below.
50*5f757f3fSDimitry Andric   std::queue<const Node *> Worklist;
51*5f757f3fSDimitry Andric   llvm::BitVector Workset(Partitioned.size(), false);
52*5f757f3fSDimitry Andric   for (const Node *S : Header->succs())
5306c3fb27SDimitry Andric     if (S != nullptr)
54*5f757f3fSDimitry Andric       if (auto SID = getID(*S); !Partitioned.test(SID)) {
5506c3fb27SDimitry Andric         // Successors are unique, so we don't test against `Workset` before
5606c3fb27SDimitry Andric         // adding to `Worklist`.
5706c3fb27SDimitry Andric         Worklist.push(S);
5806c3fb27SDimitry Andric         Workset.set(SID);
5906c3fb27SDimitry Andric       }
6006c3fb27SDimitry Andric 
6106c3fb27SDimitry Andric   // Contains successors of blocks in the interval that couldn't be added to the
6206c3fb27SDimitry Andric   // interval on their first encounter. This occurs when they have a predecessor
6306c3fb27SDimitry Andric   // that is either definitively outside the interval or hasn't been considered
6406c3fb27SDimitry Andric   // yet. In the latter case, we'll revisit the block through some other path
6506c3fb27SDimitry Andric   // from the interval. At the end of processing the worklist, we filter out any
6606c3fb27SDimitry Andric   // that ended up in the interval to produce the output set of interval
67*5f757f3fSDimitry Andric   // successors. Elements are never null.
68*5f757f3fSDimitry Andric   std::vector<const Node *> MaybeSuccessors;
6906c3fb27SDimitry Andric 
7006c3fb27SDimitry Andric   while (!Worklist.empty()) {
7106c3fb27SDimitry Andric     const auto *B = Worklist.front();
72*5f757f3fSDimitry Andric     auto ID = getID(*B);
7306c3fb27SDimitry Andric     Worklist.pop();
7406c3fb27SDimitry Andric     Workset.reset(ID);
7506c3fb27SDimitry Andric 
7606c3fb27SDimitry Andric     // Check whether all predecessors are in the interval, in which case `B`
7706c3fb27SDimitry Andric     // is included as well.
78*5f757f3fSDimitry Andric     bool AllInInterval = llvm::all_of(B->preds(), [&](const Node *P) {
79*5f757f3fSDimitry Andric       return llvm::is_contained(Interval.Nodes, P);
80*5f757f3fSDimitry Andric     });
8106c3fb27SDimitry Andric     if (AllInInterval) {
82*5f757f3fSDimitry Andric       Interval.Nodes.push_back(B);
8306c3fb27SDimitry Andric       Partitioned.set(ID);
84*5f757f3fSDimitry Andric       for (const Node *S : B->succs())
8506c3fb27SDimitry Andric         if (S != nullptr)
86*5f757f3fSDimitry Andric           if (auto SID = getID(*S);
8706c3fb27SDimitry Andric               !Partitioned.test(SID) && !Workset.test(SID)) {
8806c3fb27SDimitry Andric             Worklist.push(S);
8906c3fb27SDimitry Andric             Workset.set(SID);
9006c3fb27SDimitry Andric           }
91*5f757f3fSDimitry Andric     } else {
92*5f757f3fSDimitry Andric       MaybeSuccessors.push_back(B);
9306c3fb27SDimitry Andric     }
9406c3fb27SDimitry Andric   }
9506c3fb27SDimitry Andric 
9606c3fb27SDimitry Andric   // Any block successors not in the current interval are interval successors.
97*5f757f3fSDimitry Andric   for (const Node *B : MaybeSuccessors)
98*5f757f3fSDimitry Andric     if (!llvm::is_contained(Interval.Nodes, B))
9906c3fb27SDimitry Andric       Interval.Successors.insert(B);
10006c3fb27SDimitry Andric 
10106c3fb27SDimitry Andric   return Interval;
10206c3fb27SDimitry Andric }
10306c3fb27SDimitry Andric 
104*5f757f3fSDimitry Andric template <typename Node>
fillIntervalNode(CFGIntervalGraph & Graph,std::vector<CFGIntervalNode * > & Index,std::queue<const Node * > & Successors,llvm::BitVector & Partitioned,const Node * Header)105*5f757f3fSDimitry Andric void fillIntervalNode(CFGIntervalGraph &Graph,
106*5f757f3fSDimitry Andric                       std::vector<CFGIntervalNode *> &Index,
107*5f757f3fSDimitry Andric                       std::queue<const Node *> &Successors,
108*5f757f3fSDimitry Andric                       llvm::BitVector &Partitioned, const Node *Header) {
109*5f757f3fSDimitry Andric   BuildResult<Node> Result = buildInterval(Partitioned, Header);
110*5f757f3fSDimitry Andric   for (const auto *S : Result.Successors)
111*5f757f3fSDimitry Andric     Successors.push(S);
112*5f757f3fSDimitry Andric 
113*5f757f3fSDimitry Andric   CFGIntervalNode &Interval = Graph.emplace_back(Graph.size());
114*5f757f3fSDimitry Andric 
115*5f757f3fSDimitry Andric   // Index the nodes of the new interval. The index maps nodes from the input
116*5f757f3fSDimitry Andric   // graph (specifically, `Result.Nodes`) to identifiers of nodes in the output
117*5f757f3fSDimitry Andric   // graph. In this case, the new interval has identifier `ID` so all of its
118*5f757f3fSDimitry Andric   // nodes (`Result.Nodes`) map to `ID`.
119*5f757f3fSDimitry Andric   for (const auto *N : Result.Nodes) {
120*5f757f3fSDimitry Andric     assert(N != nullptr);
121*5f757f3fSDimitry Andric     assert(getID(*N) < Index.size());
122*5f757f3fSDimitry Andric     Index[getID(*N)] = &Interval;
12306c3fb27SDimitry Andric   }
12406c3fb27SDimitry Andric 
125*5f757f3fSDimitry Andric   if constexpr (std::is_same_v<std::decay_t<Node>, CFGBlock>)
126*5f757f3fSDimitry Andric     Interval.Nodes = std::move(Result.Nodes);
127*5f757f3fSDimitry Andric   else {
128*5f757f3fSDimitry Andric     std::vector<const CFGBlock *> Nodes;
129*5f757f3fSDimitry Andric     // Flatten the sub vectors into a single list.
130*5f757f3fSDimitry Andric     size_t Count = 0;
131*5f757f3fSDimitry Andric     for (auto &N : Result.Nodes)
132*5f757f3fSDimitry Andric       Count += N->Nodes.size();
133*5f757f3fSDimitry Andric     Nodes.reserve(Count);
134*5f757f3fSDimitry Andric     for (auto &N : Result.Nodes)
135*5f757f3fSDimitry Andric       Nodes.insert(Nodes.end(), N->Nodes.begin(), N->Nodes.end());
136*5f757f3fSDimitry Andric     Interval.Nodes = std::move(Nodes);
137*5f757f3fSDimitry Andric   }
138*5f757f3fSDimitry Andric }
13906c3fb27SDimitry Andric 
140*5f757f3fSDimitry Andric template <typename Node>
partitionIntoIntervalsImpl(unsigned NumBlockIDs,const Node * EntryBlock)141*5f757f3fSDimitry Andric CFGIntervalGraph partitionIntoIntervalsImpl(unsigned NumBlockIDs,
142*5f757f3fSDimitry Andric                                             const Node *EntryBlock) {
143*5f757f3fSDimitry Andric   assert(EntryBlock != nullptr);
144*5f757f3fSDimitry Andric   CFGIntervalGraph Graph;
145*5f757f3fSDimitry Andric   // `Index` maps all of the nodes of the input graph to the interval to which
146*5f757f3fSDimitry Andric   // they are assigned in the output graph. The values (interval pointers) are
147*5f757f3fSDimitry Andric   // never null.
148*5f757f3fSDimitry Andric   std::vector<CFGIntervalNode *> Index(NumBlockIDs, nullptr);
149*5f757f3fSDimitry Andric 
150*5f757f3fSDimitry Andric   // Lists header nodes (from the input graph) and their associated
151*5f757f3fSDimitry Andric   // interval. Since header nodes can vary in type and are only needed within
152*5f757f3fSDimitry Andric   // this function, we record them separately from `CFGIntervalNode`. This
153*5f757f3fSDimitry Andric   // choice enables to express `CFGIntervalNode` without using a variant.
154*5f757f3fSDimitry Andric   std::vector<std::pair<const Node *, CFGIntervalNode *>> Intervals;
155*5f757f3fSDimitry Andric   llvm::BitVector Partitioned(NumBlockIDs, false);
156*5f757f3fSDimitry Andric   std::queue<const Node *> Successors;
157*5f757f3fSDimitry Andric 
158*5f757f3fSDimitry Andric   fillIntervalNode(Graph, Index, Successors, Partitioned, EntryBlock);
159*5f757f3fSDimitry Andric   Intervals.emplace_back(EntryBlock, &Graph.back());
16006c3fb27SDimitry Andric 
16106c3fb27SDimitry Andric   while (!Successors.empty()) {
16206c3fb27SDimitry Andric     const auto *B = Successors.front();
16306c3fb27SDimitry Andric     Successors.pop();
164*5f757f3fSDimitry Andric     assert(B != nullptr);
165*5f757f3fSDimitry Andric     if (Partitioned.test(getID(*B)))
16606c3fb27SDimitry Andric       continue;
16706c3fb27SDimitry Andric 
168*5f757f3fSDimitry Andric     // B has not been partitioned, but it has a predecessor that has. Create a
169*5f757f3fSDimitry Andric     // new interval from `B`.
170*5f757f3fSDimitry Andric     fillIntervalNode(Graph, Index, Successors, Partitioned, B);
171*5f757f3fSDimitry Andric     Intervals.emplace_back(B, &Graph.back());
17206c3fb27SDimitry Andric   }
17306c3fb27SDimitry Andric 
174*5f757f3fSDimitry Andric   // Go back and patch up all the Intervals -- the successors and predecessors.
175*5f757f3fSDimitry Andric   for (auto [H, N] : Intervals) {
176*5f757f3fSDimitry Andric     // Map input-graph predecessors to output-graph nodes and mark those as
177*5f757f3fSDimitry Andric     // predecessors of `N`. Then, mark `N` as a successor of said predecessor.
178*5f757f3fSDimitry Andric     for (const Node *P : H->preds()) {
179*5f757f3fSDimitry Andric       if (P == nullptr)
180*5f757f3fSDimitry Andric         continue;
181*5f757f3fSDimitry Andric 
182*5f757f3fSDimitry Andric       assert(getID(*P) < NumBlockIDs);
183*5f757f3fSDimitry Andric       CFGIntervalNode *Pred = Index[getID(*P)];
184*5f757f3fSDimitry Andric       if (Pred == nullptr)
185*5f757f3fSDimitry Andric         // Unreachable node.
186*5f757f3fSDimitry Andric         continue;
187*5f757f3fSDimitry Andric       if (Pred != N // Not a backedge.
188*5f757f3fSDimitry Andric           && N->Predecessors.insert(Pred).second)
189*5f757f3fSDimitry Andric         // Note: given the guard above, which guarantees we only ever insert
190*5f757f3fSDimitry Andric         // unique elements, we could use a simple list (like `vector`) for
191*5f757f3fSDimitry Andric         // `Successors`, rather than a set.
192*5f757f3fSDimitry Andric         Pred->Successors.insert(N);
193*5f757f3fSDimitry Andric     }
19406c3fb27SDimitry Andric   }
19506c3fb27SDimitry Andric 
196*5f757f3fSDimitry Andric   return Graph;
197*5f757f3fSDimitry Andric }
198*5f757f3fSDimitry Andric 
buildInterval(const CFGBlock * Header)199*5f757f3fSDimitry Andric std::vector<const CFGBlock *> buildInterval(const CFGBlock *Header) {
200*5f757f3fSDimitry Andric   llvm::BitVector Partitioned(Header->getParent()->getNumBlockIDs(), false);
201*5f757f3fSDimitry Andric   return buildInterval(Partitioned, Header).Nodes;
202*5f757f3fSDimitry Andric }
203*5f757f3fSDimitry Andric 
partitionIntoIntervals(const CFG & Cfg)204*5f757f3fSDimitry Andric CFGIntervalGraph partitionIntoIntervals(const CFG &Cfg) {
205*5f757f3fSDimitry Andric   return partitionIntoIntervalsImpl(Cfg.getNumBlockIDs(), &Cfg.getEntry());
206*5f757f3fSDimitry Andric }
207*5f757f3fSDimitry Andric 
partitionIntoIntervals(const CFGIntervalGraph & Graph)208*5f757f3fSDimitry Andric CFGIntervalGraph partitionIntoIntervals(const CFGIntervalGraph &Graph) {
209*5f757f3fSDimitry Andric   return partitionIntoIntervalsImpl(Graph.size(), &Graph[0]);
210*5f757f3fSDimitry Andric }
211*5f757f3fSDimitry Andric } // namespace internal
212*5f757f3fSDimitry Andric 
getIntervalWTO(const CFG & Cfg)213*5f757f3fSDimitry Andric std::optional<std::vector<const CFGBlock *>> getIntervalWTO(const CFG &Cfg) {
214*5f757f3fSDimitry Andric   // Backing storage for the allocated nodes in each graph.
215*5f757f3fSDimitry Andric   unsigned PrevSize = Cfg.size();
216*5f757f3fSDimitry Andric   if (PrevSize == 0)
217*5f757f3fSDimitry Andric     return {};
218*5f757f3fSDimitry Andric   internal::CFGIntervalGraph Graph = internal::partitionIntoIntervals(Cfg);
219*5f757f3fSDimitry Andric   unsigned Size = Graph.size();
220*5f757f3fSDimitry Andric   while (Size > 1 && Size < PrevSize) {
221*5f757f3fSDimitry Andric     PrevSize = Graph.size();
222*5f757f3fSDimitry Andric     Graph = internal::partitionIntoIntervals(Graph);
223*5f757f3fSDimitry Andric     Size = Graph.size();
224*5f757f3fSDimitry Andric   }
225*5f757f3fSDimitry Andric   if (Size > 1)
226*5f757f3fSDimitry Andric     // Not reducible.
227*5f757f3fSDimitry Andric     return std::nullopt;
228*5f757f3fSDimitry Andric 
229*5f757f3fSDimitry Andric   assert(Size != 0);
230*5f757f3fSDimitry Andric   return std::move(Graph[0].Nodes);
231*5f757f3fSDimitry Andric }
232*5f757f3fSDimitry Andric 
WTOCompare(const WeakTopologicalOrdering & WTO)233*5f757f3fSDimitry Andric WTOCompare::WTOCompare(const WeakTopologicalOrdering &WTO) {
234*5f757f3fSDimitry Andric   if (WTO.empty())
235*5f757f3fSDimitry Andric     return;
236*5f757f3fSDimitry Andric   auto N = WTO[0]->getParent()->getNumBlockIDs();
237*5f757f3fSDimitry Andric   BlockOrder.resize(N, 0);
238*5f757f3fSDimitry Andric   for (unsigned I = 0, S = WTO.size(); I < S; ++I)
239*5f757f3fSDimitry Andric     BlockOrder[WTO[I]->getBlockID()] = I + 1;
240*5f757f3fSDimitry Andric }
24106c3fb27SDimitry Andric } // namespace clang
242