wyt created this revision.
Herald added subscribers: martong, tschuett, xazax.hun, mgorny.
Herald added a reviewer: NoQ.
Herald added a project: All.
wyt requested review of this revision.
Herald added projects: clang, LLVM.
Herald added subscribers: llvm-commits, cfe-commits.
Depends On D129546 <https://reviews.llvm.org/D129546>
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D129547
Files:
clang/docs/tools/clang-formatted-files.txt
clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
clang/lib/Analysis/FlowSensitive/CMakeLists.txt
clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
Index: llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
===================================================================
--- llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
+++ llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
@@ -12,5 +12,6 @@
"Transfer.cpp",
"TypeErasedDataflowAnalysis.cpp",
"WatchedLiteralsSolver.cpp",
+ "DebugSupport.cpp",
]
}
Index: clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
===================================================================
--- /dev/null
+++ clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
@@ -0,0 +1,188 @@
+//===- unittests/Analysis/FlowSensitive/DebugSupportTest.cpp --------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/FlowSensitive/DebugSupport.h"
+#include "TestingSupport.h"
+#include "clang/Analysis/FlowSensitive/Value.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+using namespace clang;
+using namespace dataflow;
+using testing::StrEq;
+
+class BoolValueDebugStringTest : public ::testing::Test {
+protected:
+ test::BoolValueManager Bools;
+};
+
+TEST_F(BoolValueDebugStringTest, AtomicBoolean) {
+ // B0
+ auto B = Bools.atom();
+
+ auto Expected = R"((B0))";
+ EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Negation) {
+ // !B0
+ auto B = Bools.neg(Bools.atom());
+
+ auto Expected = R"((not
+ (B0)))";
+ EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Conjunction) {
+ // B0 ^ B1
+ auto B = Bools.conj(Bools.atom(), Bools.atom());
+
+ auto Expected = R"((and
+ (B0)
+ (B1)))";
+ EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Disjunction) {
+ // B0 v B1
+ auto B = Bools.disj(Bools.atom(), Bools.atom());
+
+ auto Expected = R"((or
+ (B0)
+ (B1)))";
+ EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Implication) {
+ // B0 => B1, implemented as !B0 v B1
+ auto B = Bools.disj(Bools.neg(Bools.atom()), Bools.atom());
+
+ auto Expected = R"((or
+ (not
+ (B0))
+ (B1)))";
+ EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Iff) {
+ // B0 <=> B1, implemented as (!B0 v B1) ^ (B0 v !B1)
+ auto B0 = Bools.atom();
+ auto B1 = Bools.atom();
+ auto B =
+ Bools.conj(Bools.disj(Bools.neg(B0), B1), Bools.disj(B0, Bools.neg(B1)));
+
+ auto Expected = R"((and
+ (or
+ (not
+ (B0))
+ (B1))
+ (or
+ (B0)
+ (not
+ (B1)))))";
+ EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Xor) {
+ // (B0 ^ !B1) V (!B0 ^ B1)
+ auto B0 = Bools.atom();
+ auto B1 = Bools.atom();
+ auto B =
+ Bools.disj(Bools.conj(B0, Bools.neg(B1)), Bools.conj(Bools.neg(B0), B1));
+
+ auto Expected = R"((or
+ (and
+ (B0)
+ (not
+ (B1)))
+ (and
+ (not
+ (B0))
+ (B1))))";
+ EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, NestedBoolean) {
+ // B0 ^ (B1 v (B2 ^ (B3 v B4)))
+ auto B = Bools.conj(
+ Bools.atom(),
+ Bools.disj(
+ Bools.atom(),
+ Bools.conj(Bools.atom(), Bools.disj(Bools.atom(), Bools.atom()))));
+
+ auto Expected = R"((and
+ (B0)
+ (or
+ (B1)
+ (and
+ (B2)
+ (or
+ (B3)
+ (B4))))))";
+ EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, AtomicBooleanWithName) {
+ // True
+ llvm::StringMap<AtomicBoolValue *> NamedBools;
+ auto True = cast<AtomicBoolValue>(Bools.atom());
+ auto B = True;
+
+ auto Expected = R"((True))";
+ EXPECT_THAT(debugString(*B, {{True, "True"}}), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, ComplexBooleanWithNames) {
+ // (Cond ^ Then ^ !Else) v (!Cond ^ !Then ^ Else)
+ auto Cond = cast<AtomicBoolValue>(Bools.atom());
+ auto Then = cast<AtomicBoolValue>(Bools.atom());
+ auto Else = cast<AtomicBoolValue>(Bools.atom());
+ auto B = Bools.disj(
+ Bools.conj(Cond, Bools.conj(Then, Bools.neg(Else))),
+ Bools.conj(Bools.neg(Cond), Bools.conj(Bools.neg(Then), Else)));
+
+ auto Expected = R"((or
+ (and
+ (Cond)
+ (and
+ (Then)
+ (not
+ (Else))))
+ (and
+ (not
+ (Cond))
+ (and
+ (not
+ (Then))
+ (Else)))))";
+ EXPECT_THAT(debugString(*B, {{Cond, "Cond"}, {Then, "Then"}, {Else, "Else"}}),
+ StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, ComplexBooleanWithSomeNames) {
+ // (False && B0) v (True v B1)
+ llvm::StringMap<AtomicBoolValue *> NamedBools;
+ auto True = cast<AtomicBoolValue>(Bools.atom());
+ auto False = cast<AtomicBoolValue>(Bools.atom());
+ auto B = Bools.disj(Bools.conj(False, Bools.atom()),
+ Bools.disj(True, Bools.atom()));
+
+ auto Expected = R"((or
+ (and
+ (False)
+ (B0))
+ (or
+ (True)
+ (B1))))";
+ EXPECT_THAT(debugString(*B, {{True, "True"}, {False, "False"}}),
+ StrEq(Expected));
+}
+
+} // namespace
Index: clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
===================================================================
--- /dev/null
+++ clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
@@ -0,0 +1,98 @@
+//===- DebugSupport.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines functions which generate more readable forms of data
+// structures used in the dataflow analyses, for debugging purposes.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/FlowSensitive/DebugSupport.h"
+#include "clang/Analysis/FlowSensitive/Value.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FormatAdapters.h"
+#include "llvm/Support/FormatVariadic.h"
+
+namespace clang {
+namespace dataflow {
+
+using llvm::fmt_pad;
+using llvm::formatv;
+
+class DebugStringGenerator {
+public:
+ explicit DebugStringGenerator(
+ llvm::DenseMap<AtomicBoolValue *, std::string> AtomNames)
+ : Counter(0), AtomNames(AtomNames) {
+ llvm::StringSet<> Names;
+ for (auto &N : AtomNames) {
+ assert(Names.insert(N.second).second &&
+ "The same name must not assigned to different atoms");
+ }
+ }
+
+ /// Returns a string representation of a boolean value `B`.
+ std::string debugString(BoolValue &B, size_t Depth = 0) {
+ std::string S;
+ switch (B.getKind()) {
+ case Value::Kind::AtomicBool: {
+ S = formatv("({0})", getAtomName(&cast<AtomicBoolValue>(B)));
+ break;
+ }
+ case Value::Kind::Conjunction: {
+ auto &C = cast<ConjunctionValue>(B);
+ S = formatv("(and\n{0}\n{1})",
+ debugString(C.getLeftSubValue(), Depth + 1),
+ debugString(C.getRightSubValue(), Depth + 1));
+ break;
+ }
+ case Value::Kind::Disjunction: {
+ auto &D = cast<DisjunctionValue>(B);
+ S = formatv("(or\n{0}\n{1})", debugString(D.getLeftSubValue(), Depth + 1),
+ debugString(D.getRightSubValue(), Depth + 1));
+ break;
+ }
+ case Value::Kind::Negation: {
+ auto &N = cast<NegationValue>(B);
+ S = formatv("(not\n{0})", debugString(N.getSubVal(), Depth + 1));
+ break;
+ }
+ default:
+ llvm_unreachable("Unhandled value kind");
+ }
+ auto Indent = Depth * 4;
+ return formatv("{0}", fmt_pad(S, Indent, 0));
+ }
+
+private:
+ /// Returns the name assigned to `Atom`, either user-specified or created by
+ /// default rules (B0, B1, ...).
+ std::string getAtomName(AtomicBoolValue *Atom) {
+ auto Entry = AtomNames.try_emplace(Atom, formatv("B{0}", Counter));
+ if (Entry.second) {
+ Counter++;
+ }
+ return Entry.first->second;
+ }
+
+ // Keep track of number of atoms without a user-specified name, used to assign
+ // non-repeating default names to such atoms.
+ size_t Counter;
+
+ // Keep track of names assigned to atoms.
+ llvm::DenseMap<AtomicBoolValue *, std::string> AtomNames;
+};
+
+std::string debugString(BoolValue &B,
+ llvm::DenseMap<AtomicBoolValue *, std::string> Names) {
+ return DebugStringGenerator(Names).debugString(B);
+}
+
+} // namespace dataflow
+} // namespace clang
Index: clang/lib/Analysis/FlowSensitive/CMakeLists.txt
===================================================================
--- clang/lib/Analysis/FlowSensitive/CMakeLists.txt
+++ clang/lib/Analysis/FlowSensitive/CMakeLists.txt
@@ -5,6 +5,7 @@
Transfer.cpp
TypeErasedDataflowAnalysis.cpp
WatchedLiteralsSolver.cpp
+ DebugSupport.cpp
LINK_LIBS
clangAnalysis
Index: clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
===================================================================
--- /dev/null
+++ clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
@@ -0,0 +1,36 @@
+//===-- DebugSupport.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines functions which generate more readable forms of data
+// structures used in the dataflow analyses, for debugging purposes.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_DEBUGSUPPORT_H_
+#define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_DEBUGSUPPORT_H_
+
+#include <string>
+
+#include "clang/Analysis/FlowSensitive/Value.h"
+#include "llvm/ADT/DenseMap.h"
+
+namespace clang {
+namespace dataflow {
+/// Returns a string representation for a boolean value `B`.
+///
+/// Atomic booleans appearing in the boolean value `B` are assigned to labels
+/// either specified in `AtomNames` or created by default rules as B0, B1, ...
+std::string
+debugString(BoolValue &B,
+ llvm::DenseMap<AtomicBoolValue *, std::string> AtomNames);
+inline std::string debugString(BoolValue &B) { return debugString(B, {{}}); }
+
+} // namespace dataflow
+} // namespace clang
+
+#endif // LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_DEBUGSUPPORT_H_
Index: clang/docs/tools/clang-formatted-files.txt
===================================================================
--- clang/docs/tools/clang-formatted-files.txt
+++ clang/docs/tools/clang-formatted-files.txt
@@ -129,6 +129,7 @@
clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
clang/include/clang/Analysis/FlowSensitive/DataflowLattice.h
clang/include/clang/Analysis/FlowSensitive/DataflowWorklist.h
+clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
clang/include/clang/Analysis/FlowSensitive/MapLattice.h
clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h
clang/include/clang/Analysis/FlowSensitive/NoopLattice.h
@@ -309,6 +310,7 @@
clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp
clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
clang/lib/Analysis/FlowSensitive/Transfer.cpp
clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
clang/lib/Analysis/FlowSensitive/WatchedLiteralsSolver.cpp
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits