Skip to content

Commit 2c5a0d3

Browse files
authored
[clang][CFG] Change child order in Reverse Post Order (RPO) iteration. (#80030)
The CFG orders the blocks of loop bodies before those of loop successors (both numerically, and in the successor order of the loop condition block). So, RPO necessarily reverses that order, placing the loop successor *before* the loop body. For many analyses, particularly those that converge to a fixpoint, this results in potentially significant extra work, because loop successors will necessarily need to be reconsidered once the algorithm has reached a fixpoint on the loop body. This definition of CFG graph traits reverses the order of children, so that loop bodies will come first in an RPO. Then, the algorithm can fully evaluate the loop and only then consider successor blocks.
1 parent 576c4df commit 2c5a0d3

File tree

3 files changed

+43
-18
lines changed

3 files changed

+43
-18
lines changed

clang/include/clang/Analysis/Analyses/PostOrderCFGView.h

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,41 @@ class PostOrderCFGView : public ManagedAnalysis {
7070
};
7171

7272
private:
73-
using po_iterator = llvm::po_iterator<const CFG *, CFGBlockSet, true>;
73+
// The CFG orders the blocks of loop bodies before those of loop successors
74+
// (both numerically, and in the successor order of the loop condition
75+
// block). So, RPO necessarily reverses that order, placing the loop successor
76+
// *before* the loop body. For many analyses, particularly those that converge
77+
// to a fixpoint, this results in potentially significant extra work because
78+
// loop successors will necessarily need to be reconsidered once the algorithm
79+
// has reached a fixpoint on the loop body.
80+
//
81+
// This definition of CFG graph traits reverses the order of children, so that
82+
// loop bodies will come first in an RPO.
83+
struct CFGLoopBodyFirstTraits {
84+
using NodeRef = const ::clang::CFGBlock *;
85+
using ChildIteratorType = ::clang::CFGBlock::const_succ_reverse_iterator;
86+
87+
static ChildIteratorType child_begin(NodeRef N) { return N->succ_rbegin(); }
88+
static ChildIteratorType child_end(NodeRef N) { return N->succ_rend(); }
89+
90+
using nodes_iterator = ::clang::CFG::const_iterator;
91+
92+
static NodeRef getEntryNode(const ::clang::CFG *F) {
93+
return &F->getEntry();
94+
}
95+
96+
static nodes_iterator nodes_begin(const ::clang::CFG *F) {
97+
return F->nodes_begin();
98+
}
99+
100+
static nodes_iterator nodes_end(const ::clang::CFG *F) {
101+
return F->nodes_end();
102+
}
103+
104+
static unsigned size(const ::clang::CFG *F) { return F->size(); }
105+
};
106+
using po_iterator =
107+
llvm::po_iterator<const CFG *, CFGBlockSet, true, CFGLoopBodyFirstTraits>;
74108
std::vector<const CFGBlock *> Blocks;
75109

76110
using BlockOrderTy = llvm::DenseMap<const CFGBlock *, unsigned>;

clang/unittests/Analysis/CFGTest.cpp

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -276,14 +276,6 @@ TEST(CFG, Worklists) {
276276
ForwardNodes.begin()));
277277
}
278278

279-
// RPO: 876321054
280-
// WTO: 876534210
281-
// So, we construct the WTO order accordingly from the reference order.
282-
std::vector<const CFGBlock *> WTOOrder = {
283-
ReferenceOrder[0], ReferenceOrder[1], ReferenceOrder[2],
284-
ReferenceOrder[7], ReferenceOrder[3], ReferenceOrder[8],
285-
ReferenceOrder[4], ReferenceOrder[5], ReferenceOrder[6]};
286-
287279
{
288280
using ::testing::ElementsAreArray;
289281
std::optional<WeakTopologicalOrdering> WTO = getIntervalWTO(*CFG);
@@ -297,7 +289,7 @@ TEST(CFG, Worklists) {
297289
while (const CFGBlock *B = WTOWorklist.dequeue())
298290
WTONodes.push_back(B);
299291

300-
EXPECT_THAT(WTONodes, ElementsAreArray(WTOOrder));
292+
EXPECT_THAT(WTONodes, ElementsAreArray(*WTO));
301293
}
302294

303295
std::reverse(ReferenceOrder.begin(), ReferenceOrder.end());

clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
66
#include "llvm/Testing/Support/Error.h"
77
#include "gtest/gtest.h"
8-
#include <optional>
98

109
namespace clang::dataflow::test {
1110
namespace {
@@ -90,7 +89,7 @@ class TestLogger : public Logger {
9089
AnalysisInputs<TestAnalysis> makeInputs() {
9190
const char *Code = R"cpp(
9291
int target(bool b, int p, int q) {
93-
return b ? p : q;
92+
return b ? p : q;
9493
}
9594
)cpp";
9695
static const std::vector<std::string> Args = {
@@ -125,17 +124,17 @@ transfer()
125124
recordState(Elements=2, Branches=0, Joins=0)
126125
recordState(Elements=2, Branches=0, Joins=0)
127126
128-
enterBlock(3, false)
129-
transferBranch(0)
127+
enterBlock(2, false)
128+
transferBranch(1)
130129
recordState(Elements=2, Branches=1, Joins=0)
131-
enterElement(q)
130+
enterElement(p)
132131
transfer()
133132
recordState(Elements=3, Branches=1, Joins=0)
134133
135-
enterBlock(2, false)
136-
transferBranch(1)
134+
enterBlock(3, false)
135+
transferBranch(0)
137136
recordState(Elements=2, Branches=1, Joins=0)
138-
enterElement(p)
137+
enterElement(q)
139138
transfer()
140139
recordState(Elements=3, Branches=1, Joins=0)
141140

0 commit comments

Comments
 (0)