xref: /llvm-project/clang/unittests/Analysis/FlowSensitive/DeterminismTest.cpp (revision e6f63a942a45e3545332cd9a43982a69a4d5667b)
17d935d08SSam McCall //===- unittests/Analysis/FlowSensitive/DeterminismTest.cpp ---------------===//
27d935d08SSam McCall //
37d935d08SSam McCall // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47d935d08SSam McCall // See https://llvm.org/LICENSE.txt for license information.
57d935d08SSam McCall // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67d935d08SSam McCall //
77d935d08SSam McCall //===----------------------------------------------------------------------===//
87d935d08SSam McCall 
97d935d08SSam McCall #include "TestingSupport.h"
107d935d08SSam McCall #include "clang/AST/Decl.h"
1159ff3adcSmartinboehme #include "clang/Analysis/FlowSensitive/AdornedCFG.h"
127d935d08SSam McCall #include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
137d935d08SSam McCall #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
147d935d08SSam McCall #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
157d935d08SSam McCall #include "clang/Analysis/FlowSensitive/Formula.h"
167d935d08SSam McCall #include "clang/Analysis/FlowSensitive/NoopAnalysis.h"
177d935d08SSam McCall #include "clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h"
187d935d08SSam McCall #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
197d935d08SSam McCall #include "clang/Basic/LLVM.h"
207d935d08SSam McCall #include "clang/Testing/TestAST.h"
217d935d08SSam McCall #include "llvm/Support/Error.h"
227d935d08SSam McCall #include "llvm/Support/raw_ostream.h"
237d935d08SSam McCall #include "gtest/gtest.h"
247d935d08SSam McCall #include <memory>
257d935d08SSam McCall #include <string>
267d935d08SSam McCall 
277d935d08SSam McCall namespace clang::dataflow {
287d935d08SSam McCall 
297d935d08SSam McCall // Run a no-op analysis, and return a textual representation of the
307d935d08SSam McCall // flow-condition at function exit.
analyzeAndPrintExitCondition(llvm::StringRef Code)317d935d08SSam McCall std::string analyzeAndPrintExitCondition(llvm::StringRef Code) {
327d935d08SSam McCall   DataflowAnalysisContext DACtx(std::make_unique<WatchedLiteralsSolver>());
33*e6f63a94Smartinboehme   TestInputs Inputs(Code);
34*e6f63a94Smartinboehme   Inputs.Language = TestLanguage::Lang_CXX17;
35*e6f63a94Smartinboehme   clang::TestAST AST(Inputs);
367d935d08SSam McCall   const auto *Target =
377d935d08SSam McCall       cast<FunctionDecl>(test::findValueDecl(AST.context(), "target"));
387d935d08SSam McCall   Environment InitEnv(DACtx, *Target);
3959ff3adcSmartinboehme   auto ACFG = cantFail(AdornedCFG::build(*Target));
407d935d08SSam McCall 
417d935d08SSam McCall   NoopAnalysis Analysis(AST.context(), DataflowAnalysisOptions{});
427d935d08SSam McCall 
4359ff3adcSmartinboehme   auto Result = runDataflowAnalysis(ACFG, Analysis, InitEnv);
447d935d08SSam McCall   EXPECT_FALSE(!Result) << Result.takeError();
457d935d08SSam McCall 
4659ff3adcSmartinboehme   Atom FinalFC = (*Result)[ACFG.getCFG().getExit().getBlockID()]
477d935d08SSam McCall                      ->Env.getFlowConditionToken();
487d935d08SSam McCall   std::string Textual;
497d935d08SSam McCall   llvm::raw_string_ostream OS(Textual);
507d935d08SSam McCall   DACtx.dumpFlowCondition(FinalFC, OS);
517d935d08SSam McCall   return Textual;
527d935d08SSam McCall }
537d935d08SSam McCall 
TEST(DeterminismTest,NestedSwitch)547d935d08SSam McCall TEST(DeterminismTest, NestedSwitch) {
557d935d08SSam McCall   // Example extracted from real-world code that had wildly nondeterministic
567d935d08SSam McCall   // analysis times.
577d935d08SSam McCall   // Its flow condition depends on the order we join predecessor blocks.
587d935d08SSam McCall   const char *Code = R"cpp(
597d935d08SSam McCall     struct Tree;
607d935d08SSam McCall     struct Rep {
617d935d08SSam McCall       Tree *tree();
627d935d08SSam McCall       int length;
637d935d08SSam McCall     };
647d935d08SSam McCall     struct Tree {
657d935d08SSam McCall       int height();
667d935d08SSam McCall       Rep *edge(int);
677d935d08SSam McCall       int length;
687d935d08SSam McCall     };
697d935d08SSam McCall     struct RetVal {};
707d935d08SSam McCall     int getInt();
717d935d08SSam McCall     bool maybe();
727d935d08SSam McCall 
737d935d08SSam McCall     RetVal make(int size);
747d935d08SSam McCall     inline RetVal target(int size, Tree& self) {
757d935d08SSam McCall       Tree* tree = &self;
767d935d08SSam McCall       const int height = self.height();
777d935d08SSam McCall       Tree* n1 = tree;
787d935d08SSam McCall       Tree* n2 = tree;
797d935d08SSam McCall       switch (height) {
807d935d08SSam McCall         case 3:
817d935d08SSam McCall           tree = tree->edge(0)->tree();
827d935d08SSam McCall           if (maybe()) return {};
837d935d08SSam McCall           n2 = tree;
847d935d08SSam McCall         case 2:
857d935d08SSam McCall           tree = tree->edge(0)->tree();
867d935d08SSam McCall           n1 = tree;
877d935d08SSam McCall           if (maybe()) return {};
887d935d08SSam McCall         case 1:
897d935d08SSam McCall           tree = tree->edge(0)->tree();
907d935d08SSam McCall           if (maybe()) return {};
917d935d08SSam McCall         case 0:
927d935d08SSam McCall           Rep* edge = tree->edge(0);
937d935d08SSam McCall           if (maybe()) return {};
947d935d08SSam McCall           int avail = getInt();
957d935d08SSam McCall           if (avail == 0) return {};
967d935d08SSam McCall           int delta = getInt();
977d935d08SSam McCall           RetVal span = {};
987d935d08SSam McCall           edge->length += delta;
997d935d08SSam McCall           switch (height) {
1007d935d08SSam McCall             case 3:
1017d935d08SSam McCall               n1->length += delta;
1027d935d08SSam McCall             case 2:
1037d935d08SSam McCall               n1->length += delta;
1047d935d08SSam McCall             case 1:
1057d935d08SSam McCall               n1->length += delta;
1067d935d08SSam McCall             case 0:
1077d935d08SSam McCall               n1->length += delta;
1087d935d08SSam McCall               return span;
1097d935d08SSam McCall           }
1107d935d08SSam McCall           break;
1117d935d08SSam McCall       }
1127d935d08SSam McCall       return make(size);
1137d935d08SSam McCall     }
1147d935d08SSam McCall   )cpp";
1157d935d08SSam McCall 
1167d935d08SSam McCall   std::string Cond = analyzeAndPrintExitCondition(Code);
1177d935d08SSam McCall   for (unsigned I = 0; I < 10; ++I)
1187d935d08SSam McCall     EXPECT_EQ(Cond, analyzeAndPrintExitCondition(Code));
1197d935d08SSam McCall }
1207d935d08SSam McCall 
TEST(DeterminismTest,ValueMergeOrder)1217d935d08SSam McCall TEST(DeterminismTest, ValueMergeOrder) {
1227d935d08SSam McCall   // Artificial example whose final flow condition variable numbering depends
1237d935d08SSam McCall   // on the order in which we merge a, b, and c.
1247d935d08SSam McCall   const char *Code = R"cpp(
1257d935d08SSam McCall     bool target(bool a, bool b, bool c) {
1267d935d08SSam McCall       if (a)
1277d935d08SSam McCall         b = c;
1287d935d08SSam McCall       else
1297d935d08SSam McCall         c = b;
1307d935d08SSam McCall       return a && b && c;
1317d935d08SSam McCall     }
1327d935d08SSam McCall   )cpp";
1337d935d08SSam McCall 
1347d935d08SSam McCall   std::string Cond = analyzeAndPrintExitCondition(Code);
1357d935d08SSam McCall   for (unsigned I = 0; I < 10; ++I)
1367d935d08SSam McCall     EXPECT_EQ(Cond, analyzeAndPrintExitCondition(Code));
1377d935d08SSam McCall }
1387d935d08SSam McCall 
1397d935d08SSam McCall } // namespace clang::dataflow
140