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