llvm-project/clang/lib/Analysis/IntervalPartition.cpp
Yitzhak Mandelbaum f4cf51c99c [clang][CFG] Add support for partitioning CFG into intervals.
Adds support for the classic dataflow algorithm that partitions a flow graph
into distinct intervals. C.f. Dragon book, pp. 664-666.

A version of this algorithm exists in LLVM (see llvm/Analysis/Interval.h and
related files), but it is specific to LLVM, is a recursive (vs iterative)
algorithm, and uses many layers of abstraction that seem unnecessary for CFG
purposes.

This patch is part 1 of 2. The next patch will generalize the code to work on
intervals, to support computation of the limit flow graph.

Differential Revision: https://reviews.llvm.org/D152263
2023-06-27 17:07:54 +00:00

117 lines
4.0 KiB
C++

//===- IntervalPartition.cpp - CFG Partitioning into Intervals --*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines functionality for partitioning a CFG into intervals.
//
//===----------------------------------------------------------------------===//
#include "clang/Analysis/Analyses/IntervalPartition.h"
#include "clang/Analysis/CFG.h"
#include "llvm/ADT/BitVector.h"
#include <queue>
#include <set>
#include <vector>
namespace clang {
static CFGInterval buildInterval(llvm::BitVector &Partitioned,
const CFGBlock &Header) {
CFGInterval Interval(&Header);
Partitioned.set(Header.getBlockID());
// Elements must not be null. Duplicates are prevented using `Workset`, below.
std::queue<const CFGBlock *> Worklist;
llvm::BitVector Workset(Header.getParent()->getNumBlockIDs(), false);
for (const CFGBlock *S : Header.succs())
if (S != nullptr)
if (auto SID = S->getBlockID(); !Partitioned.test(SID)) {
// Successors are unique, so we don't test against `Workset` before
// adding to `Worklist`.
Worklist.push(S);
Workset.set(SID);
}
// Contains successors of blocks in the interval that couldn't be added to the
// interval on their first encounter. This occurs when they have a predecessor
// that is either definitively outside the interval or hasn't been considered
// yet. In the latter case, we'll revisit the block through some other path
// from the interval. At the end of processing the worklist, we filter out any
// that ended up in the interval to produce the output set of interval
// successors. It may contain duplicates -- ultimately, all relevant elements
// are added to `Interval.Successors`, which is a set.
std::vector<const CFGBlock *> MaybeSuccessors;
while (!Worklist.empty()) {
const auto *B = Worklist.front();
auto ID = B->getBlockID();
Worklist.pop();
Workset.reset(ID);
// Check whether all predecessors are in the interval, in which case `B`
// is included as well.
bool AllInInterval = true;
for (const CFGBlock *P : B->preds())
if (Interval.Blocks.find(P) == Interval.Blocks.end()) {
MaybeSuccessors.push_back(B);
AllInInterval = false;
break;
}
if (AllInInterval) {
Interval.Blocks.insert(B);
Partitioned.set(ID);
for (const CFGBlock *S : B->succs())
if (S != nullptr)
if (auto SID = S->getBlockID();
!Partitioned.test(SID) && !Workset.test(SID)) {
Worklist.push(S);
Workset.set(SID);
}
}
}
// Any block successors not in the current interval are interval successors.
for (const CFGBlock *B : MaybeSuccessors)
if (Interval.Blocks.find(B) == Interval.Blocks.end())
Interval.Successors.insert(B);
return Interval;
}
CFGInterval buildInterval(const CFG &Cfg, const CFGBlock &Header) {
llvm::BitVector Partitioned(Cfg.getNumBlockIDs(), false);
return buildInterval(Partitioned, Header);
}
std::vector<CFGInterval> partitionIntoIntervals(const CFG &Cfg) {
std::vector<CFGInterval> Intervals;
llvm::BitVector Partitioned(Cfg.getNumBlockIDs(), false);
auto &EntryBlock = Cfg.getEntry();
Intervals.push_back(buildInterval(Partitioned, EntryBlock));
std::queue<const CFGBlock *> Successors;
for (const auto *S : Intervals[0].Successors)
Successors.push(S);
while (!Successors.empty()) {
const auto *B = Successors.front();
Successors.pop();
if (Partitioned.test(B->getBlockID()))
continue;
// B has not been partitioned, but it has a predecessor that has.
CFGInterval I = buildInterval(Partitioned, *B);
for (const auto *S : I.Successors)
Successors.push(S);
Intervals.push_back(std::move(I));
}
return Intervals;
}
} // namespace clang