1 //===- unittests/Analysis/FlowSensitive/DeterminismTest.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 "TestingSupport.h"
10 #include "clang/AST/Decl.h"
11 #include "clang/Analysis/FlowSensitive/ControlFlowContext.h"
12 #include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
13 #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
14 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
15 #include "clang/Analysis/FlowSensitive/Formula.h"
16 #include "clang/Analysis/FlowSensitive/NoopAnalysis.h"
17 #include "clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h"
18 #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
19 #include "clang/Basic/LLVM.h"
20 #include "clang/Testing/TestAST.h"
21 #include "llvm/Support/Error.h"
22 #include "llvm/Support/raw_ostream.h"
23 #include "gtest/gtest.h"
27 namespace clang::dataflow
{
29 // Run a no-op analysis, and return a textual representation of the
30 // flow-condition at function exit.
31 std::string
analyzeAndPrintExitCondition(llvm::StringRef Code
) {
32 DataflowAnalysisContext
DACtx(std::make_unique
<WatchedLiteralsSolver
>());
33 clang::TestAST
AST(Code
);
35 cast
<FunctionDecl
>(test::findValueDecl(AST
.context(), "target"));
36 Environment
InitEnv(DACtx
, *Target
);
37 auto CFCtx
= cantFail(ControlFlowContext::build(*Target
));
39 NoopAnalysis
Analysis(AST
.context(), DataflowAnalysisOptions
{});
41 auto Result
= runDataflowAnalysis(CFCtx
, Analysis
, InitEnv
);
42 EXPECT_FALSE(!Result
) << Result
.takeError();
44 Atom FinalFC
= (*Result
)[CFCtx
.getCFG().getExit().getBlockID()]
45 ->Env
.getFlowConditionToken();
47 llvm::raw_string_ostream
OS(Textual
);
48 DACtx
.dumpFlowCondition(FinalFC
, OS
);
52 TEST(DeterminismTest
, NestedSwitch
) {
53 // Example extracted from real-world code that had wildly nondeterministic
55 // Its flow condition depends on the order we join predecessor blocks.
56 const char *Code
= R
"cpp(
71 RetVal make(int size);
72 inline RetVal target(int size, Tree& self) {
74 const int height = self.height();
79 tree = tree->edge(0)->tree();
80 if (maybe()) return {};
83 tree = tree->edge(0)->tree();
85 if (maybe()) return {};
87 tree = tree->edge(0)->tree();
88 if (maybe()) return {};
90 Rep* edge = tree->edge(0);
91 if (maybe()) return {};
93 if (avail == 0) return {};
96 edge->length += delta;
114 std::string Cond
= analyzeAndPrintExitCondition(Code
);
115 for (unsigned I
= 0; I
< 10; ++I
)
116 EXPECT_EQ(Cond
, analyzeAndPrintExitCondition(Code
));
119 TEST(DeterminismTest
, ValueMergeOrder
) {
120 // Artificial example whose final flow condition variable numbering depends
121 // on the order in which we merge a, b, and c.
122 const char *Code
= R
"cpp(
123 bool target(bool a, bool b, bool c) {
132 std::string Cond
= analyzeAndPrintExitCondition(Code
);
133 for (unsigned I
= 0; I
< 10; ++I
)
134 EXPECT_EQ(Cond
, analyzeAndPrintExitCondition(Code
));
137 } // namespace clang::dataflow