1 //===- unittests/Analysis/IntervalPartitionTest.cpp -----------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "clang/Analysis/Analyses/IntervalPartition.h"
10 #include "CFGBuildResult.h"
11 #include "clang/Analysis/CFG.h"
12 #include "llvm/Support/raw_ostream.h"
13 #include "gmock/gmock.h"
14 #include "gtest/gtest.h"
15 #include <type_traits>
20 llvm::raw_ostream
&operator<<(llvm::raw_ostream
&OS
,
21 const std::vector
<const CFGBlock
*> &Nodes
) {
23 for (const auto *B
: Nodes
)
24 OS
<< B
->getBlockID() << ", ";
29 void PrintTo(const std::vector
<const CFGBlock
*> &Nodes
, std::ostream
*OS
) {
31 llvm::raw_string_ostream
StringOS(Result
);
37 llvm::raw_ostream
&operator<<(llvm::raw_ostream
&OS
, const CFGIntervalNode
&I
) {
38 OS
<< "Interval{ID = " << I
.ID
<< ", ";
40 for (const auto *B
: I
.Nodes
)
41 OS
<< B
->getBlockID() << ", ";
43 for (const auto *P
: I
.Predecessors
)
46 for (const auto *P
: I
.Successors
)
52 llvm::raw_ostream
&operator<<(llvm::raw_ostream
&OS
,
53 const CFGIntervalGraph
&G
) {
55 for (const auto &I
: G
) {
62 void PrintTo(const CFGIntervalNode
&I
, std::ostream
*OS
) {
64 llvm::raw_string_ostream
StringOS(Result
);
69 void PrintTo(const CFGIntervalGraph
&G
, std::ostream
*OS
) {
71 for (const auto &I
: G
) {
77 } // namespace internal
81 using ::clang::analysis::BuildCFG
;
82 using ::clang::analysis::BuildResult
;
83 using ::clang::internal::buildInterval
;
84 using ::clang::internal::partitionIntoIntervals
;
85 using ::testing::ElementsAre
;
86 using ::testing::IsEmpty
;
87 using ::testing::Optional
;
88 using ::testing::Property
;
89 using ::testing::UnorderedElementsAre
;
91 MATCHER_P(intervalID
, ID
, "") { return arg
->ID
== ID
; }
93 template <typename
... T
> auto blockIDs(T
... IDs
) {
94 return UnorderedElementsAre(Property(&CFGBlock::getBlockID
, IDs
)...);
97 template <typename
... T
> auto blockOrder(T
... IDs
) {
98 return ElementsAre(Property(&CFGBlock::getBlockID
, IDs
)...);
101 MATCHER_P3(isInterval
, ID
, Preds
, Succs
, "") {
102 return testing::Matches(ID
)(arg
.ID
) &&
103 testing::Matches(Preds
)(arg
.Predecessors
) &&
104 testing::Matches(Succs
)(arg
.Successors
);
107 MATCHER_P4(isInterval
, ID
, Nodes
, Preds
, Succs
, "") {
108 return testing::Matches(ID
)(arg
.ID
) && testing::Matches(Nodes
)(arg
.Nodes
) &&
109 testing::Matches(Preds
)(arg
.Predecessors
) &&
110 testing::Matches(Succs
)(arg
.Successors
);
113 TEST(BuildInterval
, PartitionSimpleOneInterval
) {
114 const char *Code
= R
"(void f() {
119 BuildResult Result
= BuildCFG(Code
);
120 EXPECT_EQ(BuildResult::BuiltCFG
, Result
.getStatus());
122 CFG
*cfg
= Result
.getCFG();
124 // Basic correctness checks.
125 ASSERT_EQ(cfg
->size(), 3u);
127 auto &EntryBlock
= cfg
->getEntry();
129 std::vector
<const CFGBlock
*> I
= buildInterval(&EntryBlock
);
130 EXPECT_EQ(I
.size(), 3u);
133 TEST(BuildInterval
, PartitionIfThenOneInterval
) {
134 const char *Code
= R
"(void f() {
142 BuildResult Result
= BuildCFG(Code
);
143 EXPECT_EQ(BuildResult::BuiltCFG
, Result
.getStatus());
145 CFG
*cfg
= Result
.getCFG();
147 // Basic correctness checks.
148 ASSERT_EQ(cfg
->size(), 6u);
150 auto &EntryBlock
= cfg
->getEntry();
152 std::vector
<const CFGBlock
*> I
= buildInterval(&EntryBlock
);
153 EXPECT_EQ(I
.size(), 6u);
156 TEST(BuildInterval
, PartitionWhileMultipleIntervals
) {
157 const char *Code
= R
"(void f() {
163 BuildResult Result
= BuildCFG(Code
);
164 ASSERT_EQ(BuildResult::BuiltCFG
, Result
.getStatus());
166 CFG
*cfg
= Result
.getCFG();
167 ASSERT_EQ(cfg
->size(), 7u);
169 auto *EntryBlock
= &cfg
->getEntry();
170 CFGBlock
*InitXBlock
= *EntryBlock
->succ_begin();
171 CFGBlock
*LoopHeadBlock
= *InitXBlock
->succ_begin();
173 std::vector
<const CFGBlock
*> I1
= buildInterval(EntryBlock
);
174 EXPECT_THAT(I1
, ElementsAre(EntryBlock
, InitXBlock
));
176 std::vector
<const CFGBlock
*> I2
= buildInterval(LoopHeadBlock
);
177 EXPECT_EQ(I2
.size(), 5u);
180 TEST(PartitionIntoIntervals
, PartitionIfThenOneInterval
) {
181 const char *Code
= R
"(void f() {
189 BuildResult Result
= BuildCFG(Code
);
190 ASSERT_EQ(BuildResult::BuiltCFG
, Result
.getStatus());
192 auto Graph
= partitionIntoIntervals(*Result
.getCFG());
193 EXPECT_EQ(Graph
.size(), 1u);
194 EXPECT_THAT(Graph
, ElementsAre(isInterval(0, IsEmpty(), IsEmpty())));
197 TEST(PartitionIntoIntervals
, PartitionWhileTwoIntervals
) {
198 const char *Code
= R
"(void f() {
204 BuildResult Result
= BuildCFG(Code
);
205 ASSERT_EQ(BuildResult::BuiltCFG
, Result
.getStatus());
207 auto Graph
= partitionIntoIntervals(*Result
.getCFG());
211 isInterval(0, IsEmpty(), UnorderedElementsAre(intervalID(1u))),
212 isInterval(1, UnorderedElementsAre(intervalID(0u)), IsEmpty())));
215 TEST(PartitionIntoIntervals
, PartitionNestedWhileThreeIntervals
) {
216 const char *Code
= R
"(void f() {
225 BuildResult Result
= BuildCFG(Code
);
226 ASSERT_EQ(BuildResult::BuiltCFG
, Result
.getStatus());
228 auto Graph
= partitionIntoIntervals(*Result
.getCFG());
232 isInterval(0, IsEmpty(), UnorderedElementsAre(intervalID(1u))),
233 isInterval(1, UnorderedElementsAre(intervalID(0u), intervalID(2u)),
234 UnorderedElementsAre(intervalID(2u))),
235 isInterval(2, UnorderedElementsAre(intervalID(1u)),
236 UnorderedElementsAre(intervalID(1u)))));
239 TEST(PartitionIntoIntervals
, PartitionSequentialWhileThreeIntervals
) {
240 const char *Code
= R
"(void f() {
249 BuildResult Result
= BuildCFG(Code
);
250 ASSERT_EQ(BuildResult::BuiltCFG
, Result
.getStatus());
252 auto Graph
= partitionIntoIntervals(*Result
.getCFG());
256 isInterval(0, IsEmpty(), UnorderedElementsAre(intervalID(1u))),
257 isInterval(1, UnorderedElementsAre(intervalID(0u)),
258 UnorderedElementsAre(intervalID(2u))),
259 isInterval(2, UnorderedElementsAre(intervalID(1u)), IsEmpty())));
262 TEST(PartitionIntoIntervals
, LimitReducibleSequentialWhile
) {
263 const char *Code
= R
"(void f() {
272 BuildResult Result
= BuildCFG(Code
);
273 ASSERT_EQ(BuildResult::BuiltCFG
, Result
.getStatus());
275 auto Graph
= partitionIntoIntervals(*Result
.getCFG());
278 ElementsAre(isInterval(0, blockOrder(9, 8), IsEmpty(),
279 UnorderedElementsAre(intervalID(1u))),
280 isInterval(1, blockOrder(7, 6, 4, 5),
281 UnorderedElementsAre(intervalID(0u)),
282 UnorderedElementsAre(intervalID(2u))),
283 isInterval(2, blockOrder(3, 2, 0, 1),
284 UnorderedElementsAre(intervalID(1u)), IsEmpty())));
286 auto Graph2
= partitionIntoIntervals(Graph
);
287 EXPECT_THAT(Graph2
, ElementsAre(isInterval(
288 0, blockOrder(9, 8, 7, 6, 4, 5, 3, 2, 0, 1),
289 IsEmpty(), IsEmpty())));
292 TEST(PartitionIntoIntervals
, LimitReducibleNestedWhile
) {
293 const char *Code
= R
"(void f() {
302 BuildResult Result
= BuildCFG(Code
);
303 ASSERT_EQ(BuildResult::BuiltCFG
, Result
.getStatus());
305 auto Graph
= partitionIntoIntervals(*Result
.getCFG());
307 ElementsAre(isInterval(0, blockOrder(9, 8), IsEmpty(),
308 UnorderedElementsAre(intervalID(1u))),
309 isInterval(1, blockOrder(7, 6, 1, 0),
310 UnorderedElementsAre(intervalID(0u),
312 UnorderedElementsAre(intervalID(2u))),
313 isInterval(2, blockOrder(5, 4, 2, 3),
314 UnorderedElementsAre(intervalID(1u)),
315 UnorderedElementsAre(intervalID(1u)))));
317 auto Graph2
= partitionIntoIntervals(Graph
);
320 ElementsAre(isInterval(0, blockOrder(9, 8), IsEmpty(),
321 UnorderedElementsAre(intervalID(1u))),
322 isInterval(1, blockOrder(7, 6, 1, 0, 5, 4, 2, 3),
323 UnorderedElementsAre(intervalID(0u)), IsEmpty())));
325 auto Graph3
= partitionIntoIntervals(Graph2
);
326 EXPECT_THAT(Graph3
, ElementsAre(isInterval(
327 0, blockOrder(9, 8, 7, 6, 1, 0, 5, 4, 2, 3),
328 IsEmpty(), IsEmpty())));
331 TEST(GetIntervalWTO
, SequentialWhile
) {
332 const char *Code
= R
"(void f() {
341 BuildResult Result
= BuildCFG(Code
);
342 ASSERT_EQ(BuildResult::BuiltCFG
, Result
.getStatus());
343 EXPECT_THAT(getIntervalWTO(*Result
.getCFG()),
344 Optional(blockOrder(9, 8, 7, 6, 4, 5, 3, 2, 0, 1)));
347 TEST(GetIntervalWTO
, NestedWhile
) {
348 const char *Code
= R
"(void f() {
357 BuildResult Result
= BuildCFG(Code
);
358 ASSERT_EQ(BuildResult::BuiltCFG
, Result
.getStatus());
359 EXPECT_THAT(getIntervalWTO(*Result
.getCFG()),
360 Optional(blockOrder(9, 8, 7, 6, 1, 0, 5, 4, 2, 3)));
363 TEST(GetIntervalWTO
, UnreachablePred
) {
364 const char *Code
= R
"(
365 void target(bool Foo) {
370 __builtin_unreachable();
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)));
379 TEST(WTOCompare
, UnreachableBlock
) {
380 const char *Code
= R
"(
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
));