1 //===- IntervalPartition.cpp - CFG Partitioning into Intervals --*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file defines functionality for partitioning a CFG into intervals.
11 //===----------------------------------------------------------------------===//
13 #include "clang/Analysis/Analyses/IntervalPartition.h"
14 #include "clang/Analysis/CFG.h"
15 #include "llvm/ADT/BitVector.h"
16 #include "llvm/ADT/STLExtras.h"
23 // Intermediate data used in constructing a CFGIntervalNode.
24 template <typename Node
> struct BuildResult
{
25 // Use a vector to maintain the insertion order. Given the expected small
26 // number of nodes, vector should be sufficiently efficient. Elements must not
28 std::vector
<const Node
*> Nodes
;
29 // Elements must not be null.
30 llvm::SmallDenseSet
<const Node
*> Successors
;
34 static unsigned getID(const CFGBlock
&B
) { return B
.getBlockID(); }
35 static unsigned getID(const CFGIntervalNode
&I
) { return I
.ID
; }
37 // `Node` must be one of `CFGBlock` or `CFGIntervalNode`.
38 template <typename Node
>
39 BuildResult
<Node
> buildInterval(llvm::BitVector
&Partitioned
,
41 assert(Header
!= nullptr);
42 BuildResult
<Node
> Interval
;
43 Interval
.Nodes
.push_back(Header
);
44 Partitioned
.set(getID(*Header
));
46 // FIXME: Compare performance against using RPO to consider nodes, rather than
47 // following successors.
49 // Elements must not be null. Duplicates are prevented using `Workset`, below.
50 std::queue
<const Node
*> Worklist
;
51 llvm::BitVector
Workset(Partitioned
.size(), false);
52 for (const Node
*S
: Header
->succs())
54 if (auto SID
= getID(*S
); !Partitioned
.test(SID
)) {
55 // Successors are unique, so we don't test against `Workset` before
56 // adding to `Worklist`.
61 // Contains successors of blocks in the interval that couldn't be added to the
62 // interval on their first encounter. This occurs when they have a predecessor
63 // that is either definitively outside the interval or hasn't been considered
64 // yet. In the latter case, we'll revisit the block through some other path
65 // from the interval. At the end of processing the worklist, we filter out any
66 // that ended up in the interval to produce the output set of interval
67 // successors. Elements are never null.
68 std::vector
<const Node
*> MaybeSuccessors
;
70 while (!Worklist
.empty()) {
71 const auto *B
= Worklist
.front();
76 // Check whether all predecessors are in the interval, in which case `B`
77 // is included as well.
78 bool AllInInterval
= llvm::all_of(B
->preds(), [&](const Node
*P
) {
79 return llvm::is_contained(Interval
.Nodes
, P
);
82 Interval
.Nodes
.push_back(B
);
84 for (const Node
*S
: B
->succs())
86 if (auto SID
= getID(*S
);
87 !Partitioned
.test(SID
) && !Workset
.test(SID
)) {
92 MaybeSuccessors
.push_back(B
);
96 // Any block successors not in the current interval are interval successors.
97 for (const Node
*B
: MaybeSuccessors
)
98 if (!llvm::is_contained(Interval
.Nodes
, B
))
99 Interval
.Successors
.insert(B
);
104 template <typename Node
>
105 void fillIntervalNode(CFGIntervalGraph
&Graph
,
106 std::vector
<CFGIntervalNode
*> &Index
,
107 std::queue
<const Node
*> &Successors
,
108 llvm::BitVector
&Partitioned
, const Node
*Header
) {
109 BuildResult
<Node
> Result
= buildInterval(Partitioned
, Header
);
110 for (const auto *S
: Result
.Successors
)
113 CFGIntervalNode
&Interval
= Graph
.emplace_back(Graph
.size());
115 // Index the nodes of the new interval. The index maps nodes from the input
116 // graph (specifically, `Result.Nodes`) to identifiers of nodes in the output
117 // graph. In this case, the new interval has identifier `ID` so all of its
118 // nodes (`Result.Nodes`) map to `ID`.
119 for (const auto *N
: Result
.Nodes
) {
120 assert(N
!= nullptr);
121 assert(getID(*N
) < Index
.size());
122 Index
[getID(*N
)] = &Interval
;
125 if constexpr (std::is_same_v
<std::decay_t
<Node
>, CFGBlock
>)
126 Interval
.Nodes
= std::move(Result
.Nodes
);
128 std::vector
<const CFGBlock
*> Nodes
;
129 // Flatten the sub vectors into a single list.
131 for (auto &N
: Result
.Nodes
)
132 Count
+= N
->Nodes
.size();
133 Nodes
.reserve(Count
);
134 for (auto &N
: Result
.Nodes
)
135 Nodes
.insert(Nodes
.end(), N
->Nodes
.begin(), N
->Nodes
.end());
136 Interval
.Nodes
= std::move(Nodes
);
140 template <typename Node
>
141 CFGIntervalGraph
partitionIntoIntervalsImpl(unsigned NumBlockIDs
,
142 const Node
*EntryBlock
) {
143 assert(EntryBlock
!= nullptr);
144 CFGIntervalGraph Graph
;
145 // `Index` maps all of the nodes of the input graph to the interval to which
146 // they are assigned in the output graph. The values (interval pointers) are
148 std::vector
<CFGIntervalNode
*> Index(NumBlockIDs
, nullptr);
150 // Lists header nodes (from the input graph) and their associated
151 // interval. Since header nodes can vary in type and are only needed within
152 // this function, we record them separately from `CFGIntervalNode`. This
153 // choice enables to express `CFGIntervalNode` without using a variant.
154 std::vector
<std::pair
<const Node
*, CFGIntervalNode
*>> Intervals
;
155 llvm::BitVector
Partitioned(NumBlockIDs
, false);
156 std::queue
<const Node
*> Successors
;
158 fillIntervalNode(Graph
, Index
, Successors
, Partitioned
, EntryBlock
);
159 Intervals
.emplace_back(EntryBlock
, &Graph
.back());
161 while (!Successors
.empty()) {
162 const auto *B
= Successors
.front();
164 assert(B
!= nullptr);
165 if (Partitioned
.test(getID(*B
)))
168 // B has not been partitioned, but it has a predecessor that has. Create a
169 // new interval from `B`.
170 fillIntervalNode(Graph
, Index
, Successors
, Partitioned
, B
);
171 Intervals
.emplace_back(B
, &Graph
.back());
174 // Go back and patch up all the Intervals -- the successors and predecessors.
175 for (auto [H
, N
] : Intervals
) {
176 // Map input-graph predecessors to output-graph nodes and mark those as
177 // predecessors of `N`. Then, mark `N` as a successor of said predecessor.
178 for (const Node
*P
: H
->preds()) {
182 assert(getID(*P
) < NumBlockIDs
);
183 CFGIntervalNode
*Pred
= Index
[getID(*P
)];
187 if (Pred
!= N
// Not a backedge.
188 && N
->Predecessors
.insert(Pred
).second
)
189 // Note: given the guard above, which guarantees we only ever insert
190 // unique elements, we could use a simple list (like `vector`) for
191 // `Successors`, rather than a set.
192 Pred
->Successors
.insert(N
);
199 std::vector
<const CFGBlock
*> buildInterval(const CFGBlock
*Header
) {
200 llvm::BitVector
Partitioned(Header
->getParent()->getNumBlockIDs(), false);
201 return buildInterval(Partitioned
, Header
).Nodes
;
204 CFGIntervalGraph
partitionIntoIntervals(const CFG
&Cfg
) {
205 return partitionIntoIntervalsImpl(Cfg
.getNumBlockIDs(), &Cfg
.getEntry());
208 CFGIntervalGraph
partitionIntoIntervals(const CFGIntervalGraph
&Graph
) {
209 return partitionIntoIntervalsImpl(Graph
.size(), &Graph
[0]);
211 } // namespace internal
213 std::optional
<std::vector
<const CFGBlock
*>> getIntervalWTO(const CFG
&Cfg
) {
214 // Backing storage for the allocated nodes in each graph.
215 unsigned PrevSize
= Cfg
.size();
218 internal::CFGIntervalGraph Graph
= internal::partitionIntoIntervals(Cfg
);
219 unsigned Size
= Graph
.size();
220 while (Size
> 1 && Size
< PrevSize
) {
221 PrevSize
= Graph
.size();
222 Graph
= internal::partitionIntoIntervals(Graph
);
230 return std::move(Graph
[0].Nodes
);
233 WTOCompare::WTOCompare(const WeakTopologicalOrdering
&WTO
) {
236 auto N
= WTO
[0]->getParent()->getNumBlockIDs();
237 BlockOrder
.resize(N
, 0);
238 for (unsigned I
= 0, S
= WTO
.size(); I
< S
; ++I
)
239 BlockOrder
[WTO
[I
]->getBlockID()] = I
+ 1;