Skip to content

Commit e21b1dd

Browse files
committed
[clang][CFG] Fix 2 memory errors in interval computation.
This fixes 2 bugs and adds corresponding tests. Both related to unreachable blocks. One occured in the `WTOCompare` construction, which assumed the size of the order was the same as the number of blocks in the CFG, which isn't true when some blocks are unreachable. The other assumed predecessor pointers were non-null, which can be false for blocks with unreachable predecessors. Differential Revision: https://reviews.llvm.org/D157033
1 parent 22f63b5 commit e21b1dd

File tree

2 files changed

+55
-16
lines changed

2 files changed

+55
-16
lines changed

clang/lib/Analysis/IntervalPartition.cpp

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,31 +16,32 @@
1616
#include "llvm/ADT/STLExtras.h"
1717
#include <optional>
1818
#include <queue>
19-
#include <set>
2019
#include <vector>
2120

2221
namespace clang {
2322

2423
// Intermediate data used in constructing a CFGIntervalNode.
2524
template <typename Node> struct BuildResult {
2625
// Use a vector to maintain the insertion order. Given the expected small
27-
// number of nodes, vector should be sufficiently efficient.
26+
// number of nodes, vector should be sufficiently efficient. Elements must not
27+
// be null.
2828
std::vector<const Node *> Nodes;
29+
// Elements must not be null.
2930
llvm::SmallDenseSet<const Node *> Successors;
3031
};
3132

3233
namespace internal {
33-
static unsigned getID(const CFGBlock *B) { return B->getBlockID(); }
34-
static unsigned getID(const CFGIntervalNode *I) { return I->ID; }
34+
static unsigned getID(const CFGBlock &B) { return B.getBlockID(); }
35+
static unsigned getID(const CFGIntervalNode &I) { return I.ID; }
3536

3637
// `Node` must be one of `CFGBlock` or `CFGIntervalNode`.
3738
template <typename Node>
3839
BuildResult<Node> buildInterval(llvm::BitVector &Partitioned,
3940
const Node *Header) {
40-
assert(Header);
41+
assert(Header != nullptr);
4142
BuildResult<Node> Interval;
4243
Interval.Nodes.push_back(Header);
43-
Partitioned.set(getID(Header));
44+
Partitioned.set(getID(*Header));
4445

4546
// FIXME: Compare performance against using RPO to consider nodes, rather than
4647
// following successors.
@@ -50,7 +51,7 @@ BuildResult<Node> buildInterval(llvm::BitVector &Partitioned,
5051
llvm::BitVector Workset(Partitioned.size(), false);
5152
for (const Node *S : Header->succs())
5253
if (S != nullptr)
53-
if (auto SID = getID(S); !Partitioned.test(SID)) {
54+
if (auto SID = getID(*S); !Partitioned.test(SID)) {
5455
// Successors are unique, so we don't test against `Workset` before
5556
// adding to `Worklist`.
5657
Worklist.push(S);
@@ -63,12 +64,12 @@ BuildResult<Node> buildInterval(llvm::BitVector &Partitioned,
6364
// yet. In the latter case, we'll revisit the block through some other path
6465
// from the interval. At the end of processing the worklist, we filter out any
6566
// that ended up in the interval to produce the output set of interval
66-
// successors.
67+
// successors. Elements are never null.
6768
std::vector<const Node *> MaybeSuccessors;
6869

6970
while (!Worklist.empty()) {
7071
const auto *B = Worklist.front();
71-
auto ID = getID(B);
72+
auto ID = getID(*B);
7273
Worklist.pop();
7374
Workset.reset(ID);
7475

@@ -82,7 +83,7 @@ BuildResult<Node> buildInterval(llvm::BitVector &Partitioned,
8283
Partitioned.set(ID);
8384
for (const Node *S : B->succs())
8485
if (S != nullptr)
85-
if (auto SID = getID(S);
86+
if (auto SID = getID(*S);
8687
!Partitioned.test(SID) && !Workset.test(SID)) {
8788
Worklist.push(S);
8889
Workset.set(SID);
@@ -116,8 +117,9 @@ void fillIntervalNode(CFGIntervalGraph &Graph,
116117
// graph. In this case, the new interval has identifier `ID` so all of its
117118
// nodes (`Result.Nodes`) map to `ID`.
118119
for (const auto *N : Result.Nodes) {
119-
assert(getID(N) < Index.size());
120-
Index[getID(N)] = &Interval;
120+
assert(N != nullptr);
121+
assert(getID(*N) < Index.size());
122+
Index[getID(*N)] = &Interval;
121123
}
122124

123125
if constexpr (std::is_same_v<std::decay_t<Node>, CFGBlock>)
@@ -159,7 +161,8 @@ CFGIntervalGraph partitionIntoIntervalsImpl(unsigned NumBlockIDs,
159161
while (!Successors.empty()) {
160162
const auto *B = Successors.front();
161163
Successors.pop();
162-
if (Partitioned.test(getID(B)))
164+
assert(B != nullptr);
165+
if (Partitioned.test(getID(*B)))
163166
continue;
164167

165168
// B has not been partitioned, but it has a predecessor that has. Create a
@@ -173,8 +176,11 @@ CFGIntervalGraph partitionIntoIntervalsImpl(unsigned NumBlockIDs,
173176
// Map input-graph predecessors to output-graph nodes and mark those as
174177
// predecessors of `N`. Then, mark `N` as a successor of said predecessor.
175178
for (const Node *P : H->preds()) {
176-
assert(getID(P) < NumBlockIDs);
177-
CFGIntervalNode *Pred = Index[getID(P)];
179+
if (P == nullptr)
180+
continue;
181+
182+
assert(getID(*P) < NumBlockIDs);
183+
CFGIntervalNode *Pred = Index[getID(*P)];
178184
if (Pred == nullptr)
179185
// Unreachable node.
180186
continue;
@@ -229,7 +235,7 @@ WTOCompare::WTOCompare(const WeakTopologicalOrdering &WTO) {
229235
return;
230236
auto N = WTO[0]->getParent()->getNumBlockIDs();
231237
BlockOrder.resize(N, 0);
232-
for (unsigned I = 0; I < N; ++I)
238+
for (unsigned I = 0, S = WTO.size(); I < S; ++I)
233239
BlockOrder[WTO[I]->getBlockID()] = I + 1;
234240
}
235241
} // namespace clang

clang/unittests/Analysis/IntervalPartitionTest.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,5 +360,38 @@ TEST(GetIntervalWTO, NestedWhile) {
360360
Optional(blockOrder(9, 8, 7, 6, 1, 0, 5, 4, 2, 3)));
361361
}
362362

363+
TEST(GetIntervalWTO, UnreachablePred) {
364+
const char *Code = R"(
365+
void target(bool Foo) {
366+
bool Bar = false;
367+
if (Foo)
368+
Bar = Foo;
369+
else
370+
__builtin_unreachable();
371+
(void)0;
372+
})";
373+
BuildResult Result = BuildCFG(Code);
374+
ASSERT_EQ(BuildResult::BuiltCFG, Result.getStatus());
375+
EXPECT_THAT(getIntervalWTO(*Result.getCFG()),
376+
Optional(blockOrder(5, 4, 3, 2, 1, 0)));
377+
}
378+
379+
TEST(WTOCompare, UnreachableBlock) {
380+
const char *Code = R"(
381+
void target() {
382+
while (true) {}
383+
(void)0;
384+
/*[[p]]*/
385+
})";
386+
BuildResult Result = BuildCFG(Code);
387+
ASSERT_EQ(BuildResult::BuiltCFG, Result.getStatus());
388+
std::optional<WeakTopologicalOrdering> WTO = getIntervalWTO(*Result.getCFG());
389+
ASSERT_THAT(WTO, Optional(blockOrder(4, 3, 2)));
390+
auto Cmp = WTOCompare(*WTO);
391+
const CFGBlock &Entry = Result.getCFG()->getEntry();
392+
const CFGBlock &Exit = Result.getCFG()->getExit();
393+
EXPECT_TRUE(Cmp(&Entry, &Exit));
394+
}
395+
363396
} // namespace
364397
} // namespace clang

0 commit comments

Comments
 (0)