1c9666d23SWei Yi Tee //===- DebugSupport.cpp -----------------------------------------*- C++ -*-===//
2c9666d23SWei Yi Tee //
3c9666d23SWei Yi Tee // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4c9666d23SWei Yi Tee // See https://llvm.org/LICENSE.txt for license information.
5c9666d23SWei Yi Tee // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6c9666d23SWei Yi Tee //
7c9666d23SWei Yi Tee //===----------------------------------------------------------------------===//
8c9666d23SWei Yi Tee //
9c9666d23SWei Yi Tee //  This file defines functions which generate more readable forms of data
10c9666d23SWei Yi Tee //  structures used in the dataflow analyses, for debugging purposes.
11c9666d23SWei Yi Tee //
12c9666d23SWei Yi Tee //===----------------------------------------------------------------------===//
13c9666d23SWei Yi Tee 
14b8d83e80SWei Yi Tee #include <utility>
15b8d83e80SWei Yi Tee 
16c9666d23SWei Yi Tee #include "clang/Analysis/FlowSensitive/DebugSupport.h"
17b8d83e80SWei Yi Tee #include "clang/Analysis/FlowSensitive/Solver.h"
18c9666d23SWei Yi Tee #include "clang/Analysis/FlowSensitive/Value.h"
19c9666d23SWei Yi Tee #include "llvm/ADT/DenseMap.h"
20b5414b56SDmitri Gribenko #include "llvm/ADT/STLExtras.h"
21c9666d23SWei Yi Tee #include "llvm/ADT/StringSet.h"
22c9666d23SWei Yi Tee #include "llvm/Support/ErrorHandling.h"
23c9666d23SWei Yi Tee #include "llvm/Support/FormatAdapters.h"
24b8d83e80SWei Yi Tee #include "llvm/Support/FormatCommon.h"
25c9666d23SWei Yi Tee #include "llvm/Support/FormatVariadic.h"
26c9666d23SWei Yi Tee 
27c9666d23SWei Yi Tee namespace clang {
28c9666d23SWei Yi Tee namespace dataflow {
29c9666d23SWei Yi Tee 
30b8d83e80SWei Yi Tee using llvm::AlignStyle;
31c9666d23SWei Yi Tee using llvm::fmt_pad;
32c9666d23SWei Yi Tee using llvm::formatv;
33c9666d23SWei Yi Tee 
debugString(Solver::Result::Assignment Assignment)34ee6aba85SDmitri Gribenko std::string debugString(Solver::Result::Assignment Assignment) {
35ee6aba85SDmitri Gribenko   switch (Assignment) {
36ee6aba85SDmitri Gribenko   case Solver::Result::Assignment::AssignedFalse:
37ee6aba85SDmitri Gribenko     return "False";
38ee6aba85SDmitri Gribenko   case Solver::Result::Assignment::AssignedTrue:
39ee6aba85SDmitri Gribenko     return "True";
40ee6aba85SDmitri Gribenko   }
41ee6aba85SDmitri Gribenko   llvm_unreachable("Booleans can only be assigned true/false");
42ee6aba85SDmitri Gribenko }
43ee6aba85SDmitri Gribenko 
debugString(Solver::Result::Status Status)44ee6aba85SDmitri Gribenko std::string debugString(Solver::Result::Status Status) {
45ee6aba85SDmitri Gribenko   switch (Status) {
46ee6aba85SDmitri Gribenko   case Solver::Result::Status::Satisfiable:
47ee6aba85SDmitri Gribenko     return "Satisfiable";
48ee6aba85SDmitri Gribenko   case Solver::Result::Status::Unsatisfiable:
49ee6aba85SDmitri Gribenko     return "Unsatisfiable";
50ee6aba85SDmitri Gribenko   case Solver::Result::Status::TimedOut:
51ee6aba85SDmitri Gribenko     return "TimedOut";
52ee6aba85SDmitri Gribenko   }
53ee6aba85SDmitri Gribenko   llvm_unreachable("Unhandled SAT check result status");
54ee6aba85SDmitri Gribenko }
55ee6aba85SDmitri Gribenko 
56c9666d23SWei Yi Tee namespace {
57c9666d23SWei Yi Tee 
58c9666d23SWei Yi Tee class DebugStringGenerator {
59c9666d23SWei Yi Tee public:
DebugStringGenerator(llvm::DenseMap<const AtomicBoolValue *,std::string> AtomNamesArg)60c9666d23SWei Yi Tee   explicit DebugStringGenerator(
61c9666d23SWei Yi Tee       llvm::DenseMap<const AtomicBoolValue *, std::string> AtomNamesArg)
62c9666d23SWei Yi Tee       : Counter(0), AtomNames(std::move(AtomNamesArg)) {
63c9666d23SWei Yi Tee #ifndef NDEBUG
64c9666d23SWei Yi Tee     llvm::StringSet<> Names;
65c9666d23SWei Yi Tee     for (auto &N : AtomNames) {
66c9666d23SWei Yi Tee       assert(Names.insert(N.second).second &&
67c9666d23SWei Yi Tee              "The same name must not assigned to different atoms");
68c9666d23SWei Yi Tee     }
69c9666d23SWei Yi Tee #endif
70c9666d23SWei Yi Tee   }
71c9666d23SWei Yi Tee 
72c9666d23SWei Yi Tee   /// Returns a string representation of a boolean value `B`.
debugString(const BoolValue & B,size_t Depth=0)73c9666d23SWei Yi Tee   std::string debugString(const BoolValue &B, size_t Depth = 0) {
74c9666d23SWei Yi Tee     std::string S;
75c9666d23SWei Yi Tee     switch (B.getKind()) {
76c9666d23SWei Yi Tee     case Value::Kind::AtomicBool: {
77c9666d23SWei Yi Tee       S = getAtomName(&cast<AtomicBoolValue>(B));
78c9666d23SWei Yi Tee       break;
79c9666d23SWei Yi Tee     }
80c9666d23SWei Yi Tee     case Value::Kind::Conjunction: {
81c9666d23SWei Yi Tee       auto &C = cast<ConjunctionValue>(B);
82c9666d23SWei Yi Tee       auto L = debugString(C.getLeftSubValue(), Depth + 1);
83c9666d23SWei Yi Tee       auto R = debugString(C.getRightSubValue(), Depth + 1);
84c9666d23SWei Yi Tee       S = formatv("(and\n{0}\n{1})", L, R);
85c9666d23SWei Yi Tee       break;
86c9666d23SWei Yi Tee     }
87c9666d23SWei Yi Tee     case Value::Kind::Disjunction: {
88c9666d23SWei Yi Tee       auto &D = cast<DisjunctionValue>(B);
89c9666d23SWei Yi Tee       auto L = debugString(D.getLeftSubValue(), Depth + 1);
90c9666d23SWei Yi Tee       auto R = debugString(D.getRightSubValue(), Depth + 1);
91c9666d23SWei Yi Tee       S = formatv("(or\n{0}\n{1})", L, R);
92c9666d23SWei Yi Tee       break;
93c9666d23SWei Yi Tee     }
94c9666d23SWei Yi Tee     case Value::Kind::Negation: {
95c9666d23SWei Yi Tee       auto &N = cast<NegationValue>(B);
96c9666d23SWei Yi Tee       S = formatv("(not\n{0})", debugString(N.getSubVal(), Depth + 1));
97c9666d23SWei Yi Tee       break;
98c9666d23SWei Yi Tee     }
99*b5e3dac3SDmitri Gribenko     case Value::Kind::Implication: {
100*b5e3dac3SDmitri Gribenko       auto &IV = cast<ImplicationValue>(B);
101*b5e3dac3SDmitri Gribenko       auto L = debugString(IV.getLeftSubValue(), Depth + 1);
102*b5e3dac3SDmitri Gribenko       auto R = debugString(IV.getRightSubValue(), Depth + 1);
103*b5e3dac3SDmitri Gribenko       S = formatv("(=>\n{0}\n{1})", L, R);
104*b5e3dac3SDmitri Gribenko       break;
105*b5e3dac3SDmitri Gribenko     }
106*b5e3dac3SDmitri Gribenko     case Value::Kind::Biconditional: {
107*b5e3dac3SDmitri Gribenko       auto &BV = cast<BiconditionalValue>(B);
108*b5e3dac3SDmitri Gribenko       auto L = debugString(BV.getLeftSubValue(), Depth + 1);
109*b5e3dac3SDmitri Gribenko       auto R = debugString(BV.getRightSubValue(), Depth + 1);
110*b5e3dac3SDmitri Gribenko       S = formatv("(=\n{0}\n{1})", L, R);
111*b5e3dac3SDmitri Gribenko       break;
112*b5e3dac3SDmitri Gribenko     }
113c9666d23SWei Yi Tee     default:
114c9666d23SWei Yi Tee       llvm_unreachable("Unhandled value kind");
115c9666d23SWei Yi Tee     }
116c9666d23SWei Yi Tee     auto Indent = Depth * 4;
117c9666d23SWei Yi Tee     return formatv("{0}", fmt_pad(S, Indent, 0));
118c9666d23SWei Yi Tee   }
119c9666d23SWei Yi Tee 
debugString(const llvm::DenseSet<BoolValue * > & Constraints)120b5414b56SDmitri Gribenko   std::string debugString(const llvm::DenseSet<BoolValue *> &Constraints) {
121b5414b56SDmitri Gribenko     std::vector<std::string> ConstraintsStrings;
122b5414b56SDmitri Gribenko     ConstraintsStrings.reserve(Constraints.size());
123b5414b56SDmitri Gribenko     for (BoolValue *Constraint : Constraints) {
124b5414b56SDmitri Gribenko       ConstraintsStrings.push_back(debugString(*Constraint));
125b5414b56SDmitri Gribenko     }
126b5414b56SDmitri Gribenko     llvm::sort(ConstraintsStrings);
127b5414b56SDmitri Gribenko 
128b5414b56SDmitri Gribenko     std::string Result;
129b5414b56SDmitri Gribenko     for (const std::string &S : ConstraintsStrings) {
130b5414b56SDmitri Gribenko       Result += S;
131b5414b56SDmitri Gribenko       Result += '\n';
132b5414b56SDmitri Gribenko     }
133b5414b56SDmitri Gribenko     return Result;
134b5414b56SDmitri Gribenko   }
135b5414b56SDmitri Gribenko 
136b8d83e80SWei Yi Tee   /// Returns a string representation of a set of boolean `Constraints` and the
137b8d83e80SWei Yi Tee   /// `Result` of satisfiability checking on the `Constraints`.
debugString(ArrayRef<BoolValue * > & Constraints,const Solver::Result & Result)138589ddd7fSDmitri Gribenko   std::string debugString(ArrayRef<BoolValue *> &Constraints,
139b8d83e80SWei Yi Tee                           const Solver::Result &Result) {
140b8d83e80SWei Yi Tee     auto Template = R"(
141b8d83e80SWei Yi Tee Constraints
142b8d83e80SWei Yi Tee ------------
143b8d83e80SWei Yi Tee {0:$[
144b8d83e80SWei Yi Tee 
145b8d83e80SWei Yi Tee ]}
146b8d83e80SWei Yi Tee ------------
147b8d83e80SWei Yi Tee {1}.
148b8d83e80SWei Yi Tee {2}
149b8d83e80SWei Yi Tee )";
150b8d83e80SWei Yi Tee 
151b8d83e80SWei Yi Tee     std::vector<std::string> ConstraintsStrings;
152b8d83e80SWei Yi Tee     ConstraintsStrings.reserve(Constraints.size());
153b8d83e80SWei Yi Tee     for (auto &Constraint : Constraints) {
154b8d83e80SWei Yi Tee       ConstraintsStrings.push_back(debugString(*Constraint));
155b8d83e80SWei Yi Tee     }
156b8d83e80SWei Yi Tee 
157ee6aba85SDmitri Gribenko     auto StatusString = clang::dataflow::debugString(Result.getStatus());
158b8d83e80SWei Yi Tee     auto Solution = Result.getSolution();
1593c849d0aSFangrui Song     auto SolutionString = Solution ? "\n" + debugString(Solution.value()) : "";
160b8d83e80SWei Yi Tee 
161b8d83e80SWei Yi Tee     return formatv(
162b8d83e80SWei Yi Tee         Template,
163b8d83e80SWei Yi Tee         llvm::make_range(ConstraintsStrings.begin(), ConstraintsStrings.end()),
164b8d83e80SWei Yi Tee         StatusString, SolutionString);
165b8d83e80SWei Yi Tee   }
166b8d83e80SWei Yi Tee 
167c9666d23SWei Yi Tee private:
168b8d83e80SWei Yi Tee   /// Returns a string representation of a truth assignment to atom booleans.
debugString(const llvm::DenseMap<AtomicBoolValue *,Solver::Result::Assignment> & AtomAssignments)169b8d83e80SWei Yi Tee   std::string debugString(
170b8d83e80SWei Yi Tee       const llvm::DenseMap<AtomicBoolValue *, Solver::Result::Assignment>
171b8d83e80SWei Yi Tee           &AtomAssignments) {
172b8d83e80SWei Yi Tee     size_t MaxNameLength = 0;
173b8d83e80SWei Yi Tee     for (auto &AtomName : AtomNames) {
174b8d83e80SWei Yi Tee       MaxNameLength = std::max(MaxNameLength, AtomName.second.size());
175b8d83e80SWei Yi Tee     }
176b8d83e80SWei Yi Tee 
177b8d83e80SWei Yi Tee     std::vector<std::string> Lines;
178b8d83e80SWei Yi Tee     for (auto &AtomAssignment : AtomAssignments) {
179b8d83e80SWei Yi Tee       auto Line = formatv("{0} = {1}",
180b8d83e80SWei Yi Tee                           fmt_align(getAtomName(AtomAssignment.first),
181b8d83e80SWei Yi Tee                                     AlignStyle::Left, MaxNameLength),
182ee6aba85SDmitri Gribenko                           clang::dataflow::debugString(AtomAssignment.second));
183b8d83e80SWei Yi Tee       Lines.push_back(Line);
184b8d83e80SWei Yi Tee     }
185b5414b56SDmitri Gribenko     llvm::sort(Lines);
186b8d83e80SWei Yi Tee 
187b8d83e80SWei Yi Tee     return formatv("{0:$[\n]}", llvm::make_range(Lines.begin(), Lines.end()));
188b8d83e80SWei Yi Tee   }
189b8d83e80SWei Yi Tee 
190c9666d23SWei Yi Tee   /// Returns the name assigned to `Atom`, either user-specified or created by
191c9666d23SWei Yi Tee   /// default rules (B0, B1, ...).
getAtomName(const AtomicBoolValue * Atom)192c9666d23SWei Yi Tee   std::string getAtomName(const AtomicBoolValue *Atom) {
193c9666d23SWei Yi Tee     auto Entry = AtomNames.try_emplace(Atom, formatv("B{0}", Counter));
194c9666d23SWei Yi Tee     if (Entry.second) {
195c9666d23SWei Yi Tee       Counter++;
196c9666d23SWei Yi Tee     }
197c9666d23SWei Yi Tee     return Entry.first->second;
198c9666d23SWei Yi Tee   }
199c9666d23SWei Yi Tee 
200c9666d23SWei Yi Tee   // Keep track of number of atoms without a user-specified name, used to assign
201c9666d23SWei Yi Tee   // non-repeating default names to such atoms.
202c9666d23SWei Yi Tee   size_t Counter;
203c9666d23SWei Yi Tee 
204c9666d23SWei Yi Tee   // Keep track of names assigned to atoms.
205c9666d23SWei Yi Tee   llvm::DenseMap<const AtomicBoolValue *, std::string> AtomNames;
206c9666d23SWei Yi Tee };
207c9666d23SWei Yi Tee 
208c9666d23SWei Yi Tee } // namespace
209c9666d23SWei Yi Tee 
210c9666d23SWei Yi Tee std::string
debugString(const BoolValue & B,llvm::DenseMap<const AtomicBoolValue *,std::string> AtomNames)211c9666d23SWei Yi Tee debugString(const BoolValue &B,
212c9666d23SWei Yi Tee             llvm::DenseMap<const AtomicBoolValue *, std::string> AtomNames) {
213c9666d23SWei Yi Tee   return DebugStringGenerator(std::move(AtomNames)).debugString(B);
214c9666d23SWei Yi Tee }
215c9666d23SWei Yi Tee 
216b8d83e80SWei Yi Tee std::string
debugString(const llvm::DenseSet<BoolValue * > & Constraints,llvm::DenseMap<const AtomicBoolValue *,std::string> AtomNames)217b5414b56SDmitri Gribenko debugString(const llvm::DenseSet<BoolValue *> &Constraints,
218b5414b56SDmitri Gribenko             llvm::DenseMap<const AtomicBoolValue *, std::string> AtomNames) {
219b5414b56SDmitri Gribenko   return DebugStringGenerator(std::move(AtomNames)).debugString(Constraints);
220b5414b56SDmitri Gribenko }
221b5414b56SDmitri Gribenko 
222b5414b56SDmitri Gribenko std::string
debugString(ArrayRef<BoolValue * > Constraints,const Solver::Result & Result,llvm::DenseMap<const AtomicBoolValue *,std::string> AtomNames)223589ddd7fSDmitri Gribenko debugString(ArrayRef<BoolValue *> Constraints, const Solver::Result &Result,
224b8d83e80SWei Yi Tee             llvm::DenseMap<const AtomicBoolValue *, std::string> AtomNames) {
225b8d83e80SWei Yi Tee   return DebugStringGenerator(std::move(AtomNames))
226b8d83e80SWei Yi Tee       .debugString(Constraints, Result);
227b8d83e80SWei Yi Tee }
228b8d83e80SWei Yi Tee 
229c9666d23SWei Yi Tee } // namespace dataflow
230c9666d23SWei Yi Tee } // namespace clang
231