Path: blob/main/contrib/llvm-project/clang/lib/Analysis/IntervalPartition.cpp
35234 views
//===- IntervalPartition.cpp - CFG Partitioning into Intervals --*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file defines functionality for partitioning a CFG into intervals.9//10//===----------------------------------------------------------------------===//1112#include "clang/Analysis/Analyses/IntervalPartition.h"13#include "clang/Analysis/CFG.h"14#include "llvm/ADT/BitVector.h"15#include "llvm/ADT/STLExtras.h"16#include <optional>17#include <queue>18#include <vector>1920namespace clang {2122// Intermediate data used in constructing a CFGIntervalNode.23template <typename Node> struct BuildResult {24// Use a vector to maintain the insertion order. Given the expected small25// number of nodes, vector should be sufficiently efficient. Elements must not26// be null.27std::vector<const Node *> Nodes;28// Elements must not be null.29llvm::SmallDenseSet<const Node *> Successors;30};3132namespace internal {33static unsigned getID(const CFGBlock &B) { return B.getBlockID(); }34static unsigned getID(const CFGIntervalNode &I) { return I.ID; }3536// `Node` must be one of `CFGBlock` or `CFGIntervalNode`.37template <typename Node>38BuildResult<Node> buildInterval(llvm::BitVector &Partitioned,39const Node *Header) {40assert(Header != nullptr);41BuildResult<Node> Interval;42Interval.Nodes.push_back(Header);43Partitioned.set(getID(*Header));4445// FIXME: Compare performance against using RPO to consider nodes, rather than46// following successors.47//48// Elements must not be null. Duplicates are prevented using `Workset`, below.49std::queue<const Node *> Worklist;50llvm::BitVector Workset(Partitioned.size(), false);51for (const Node *S : Header->succs())52if (S != nullptr)53if (auto SID = getID(*S); !Partitioned.test(SID)) {54// Successors are unique, so we don't test against `Workset` before55// adding to `Worklist`.56Worklist.push(S);57Workset.set(SID);58}5960// Contains successors of blocks in the interval that couldn't be added to the61// interval on their first encounter. This occurs when they have a predecessor62// that is either definitively outside the interval or hasn't been considered63// yet. In the latter case, we'll revisit the block through some other path64// from the interval. At the end of processing the worklist, we filter out any65// that ended up in the interval to produce the output set of interval66// successors. Elements are never null.67std::vector<const Node *> MaybeSuccessors;6869while (!Worklist.empty()) {70const auto *B = Worklist.front();71auto ID = getID(*B);72Worklist.pop();73Workset.reset(ID);7475// Check whether all predecessors are in the interval, in which case `B`76// is included as well.77bool AllInInterval = llvm::all_of(B->preds(), [&](const Node *P) {78return llvm::is_contained(Interval.Nodes, P);79});80if (AllInInterval) {81Interval.Nodes.push_back(B);82Partitioned.set(ID);83for (const Node *S : B->succs())84if (S != nullptr)85if (auto SID = getID(*S);86!Partitioned.test(SID) && !Workset.test(SID)) {87Worklist.push(S);88Workset.set(SID);89}90} else {91MaybeSuccessors.push_back(B);92}93}9495// Any block successors not in the current interval are interval successors.96for (const Node *B : MaybeSuccessors)97if (!llvm::is_contained(Interval.Nodes, B))98Interval.Successors.insert(B);99100return Interval;101}102103template <typename Node>104void fillIntervalNode(CFGIntervalGraph &Graph,105std::vector<CFGIntervalNode *> &Index,106std::queue<const Node *> &Successors,107llvm::BitVector &Partitioned, const Node *Header) {108BuildResult<Node> Result = buildInterval(Partitioned, Header);109for (const auto *S : Result.Successors)110Successors.push(S);111112CFGIntervalNode &Interval = Graph.emplace_back(Graph.size());113114// Index the nodes of the new interval. The index maps nodes from the input115// graph (specifically, `Result.Nodes`) to identifiers of nodes in the output116// graph. In this case, the new interval has identifier `ID` so all of its117// nodes (`Result.Nodes`) map to `ID`.118for (const auto *N : Result.Nodes) {119assert(N != nullptr);120assert(getID(*N) < Index.size());121Index[getID(*N)] = &Interval;122}123124if constexpr (std::is_same_v<std::decay_t<Node>, CFGBlock>)125Interval.Nodes = std::move(Result.Nodes);126else {127std::vector<const CFGBlock *> Nodes;128// Flatten the sub vectors into a single list.129size_t Count = 0;130for (auto &N : Result.Nodes)131Count += N->Nodes.size();132Nodes.reserve(Count);133for (auto &N : Result.Nodes)134Nodes.insert(Nodes.end(), N->Nodes.begin(), N->Nodes.end());135Interval.Nodes = std::move(Nodes);136}137}138139template <typename Node>140CFGIntervalGraph partitionIntoIntervalsImpl(unsigned NumBlockIDs,141const Node *EntryBlock) {142assert(EntryBlock != nullptr);143CFGIntervalGraph Graph;144// `Index` maps all of the nodes of the input graph to the interval to which145// they are assigned in the output graph. The values (interval pointers) are146// never null.147std::vector<CFGIntervalNode *> Index(NumBlockIDs, nullptr);148149// Lists header nodes (from the input graph) and their associated150// interval. Since header nodes can vary in type and are only needed within151// this function, we record them separately from `CFGIntervalNode`. This152// choice enables to express `CFGIntervalNode` without using a variant.153std::vector<std::pair<const Node *, CFGIntervalNode *>> Intervals;154llvm::BitVector Partitioned(NumBlockIDs, false);155std::queue<const Node *> Successors;156157fillIntervalNode(Graph, Index, Successors, Partitioned, EntryBlock);158Intervals.emplace_back(EntryBlock, &Graph.back());159160while (!Successors.empty()) {161const auto *B = Successors.front();162Successors.pop();163assert(B != nullptr);164if (Partitioned.test(getID(*B)))165continue;166167// B has not been partitioned, but it has a predecessor that has. Create a168// new interval from `B`.169fillIntervalNode(Graph, Index, Successors, Partitioned, B);170Intervals.emplace_back(B, &Graph.back());171}172173// Go back and patch up all the Intervals -- the successors and predecessors.174for (auto [H, N] : Intervals) {175// Map input-graph predecessors to output-graph nodes and mark those as176// predecessors of `N`. Then, mark `N` as a successor of said predecessor.177for (const Node *P : H->preds()) {178if (P == nullptr)179continue;180181assert(getID(*P) < NumBlockIDs);182CFGIntervalNode *Pred = Index[getID(*P)];183if (Pred == nullptr)184// Unreachable node.185continue;186if (Pred != N // Not a backedge.187&& N->Predecessors.insert(Pred).second)188// Note: given the guard above, which guarantees we only ever insert189// unique elements, we could use a simple list (like `vector`) for190// `Successors`, rather than a set.191Pred->Successors.insert(N);192}193}194195return Graph;196}197198std::vector<const CFGBlock *> buildInterval(const CFGBlock *Header) {199llvm::BitVector Partitioned(Header->getParent()->getNumBlockIDs(), false);200return buildInterval(Partitioned, Header).Nodes;201}202203CFGIntervalGraph partitionIntoIntervals(const CFG &Cfg) {204return partitionIntoIntervalsImpl(Cfg.getNumBlockIDs(), &Cfg.getEntry());205}206207CFGIntervalGraph partitionIntoIntervals(const CFGIntervalGraph &Graph) {208return partitionIntoIntervalsImpl(Graph.size(), &Graph[0]);209}210} // namespace internal211212std::optional<std::vector<const CFGBlock *>> getIntervalWTO(const CFG &Cfg) {213// Backing storage for the allocated nodes in each graph.214unsigned PrevSize = Cfg.size();215if (PrevSize == 0)216return {};217internal::CFGIntervalGraph Graph = internal::partitionIntoIntervals(Cfg);218unsigned Size = Graph.size();219while (Size > 1 && Size < PrevSize) {220PrevSize = Graph.size();221Graph = internal::partitionIntoIntervals(Graph);222Size = Graph.size();223}224if (Size > 1)225// Not reducible.226return std::nullopt;227228assert(Size != 0);229return std::move(Graph[0].Nodes);230}231232WTOCompare::WTOCompare(const WeakTopologicalOrdering &WTO) {233if (WTO.empty())234return;235auto N = WTO[0]->getParent()->getNumBlockIDs();236BlockOrder.resize(N, 0);237for (unsigned I = 0, S = WTO.size(); I < S; ++I)238BlockOrder[WTO[I]->getBlockID()] = I + 1;239}240} // namespace clang241242243