106c3fb27SDimitry Andric //===- Formula.h - Boolean formulas -----------------------------*- C++ -*-===//
206c3fb27SDimitry Andric //
306c3fb27SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
406c3fb27SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
506c3fb27SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
606c3fb27SDimitry Andric //
706c3fb27SDimitry Andric //===----------------------------------------------------------------------===//
806c3fb27SDimitry Andric 
906c3fb27SDimitry Andric #ifndef LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_FORMULA_H
1006c3fb27SDimitry Andric #define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_FORMULA_H
1106c3fb27SDimitry Andric 
1206c3fb27SDimitry Andric #include "clang/Basic/LLVM.h"
1306c3fb27SDimitry Andric #include "llvm/ADT/ArrayRef.h"
1406c3fb27SDimitry Andric #include "llvm/ADT/DenseMap.h"
1506c3fb27SDimitry Andric #include "llvm/ADT/DenseMapInfo.h"
1606c3fb27SDimitry Andric #include "llvm/Support/Allocator.h"
1706c3fb27SDimitry Andric #include "llvm/Support/raw_ostream.h"
1806c3fb27SDimitry Andric #include <cassert>
1906c3fb27SDimitry Andric #include <string>
2006c3fb27SDimitry Andric 
2106c3fb27SDimitry Andric namespace clang::dataflow {
2206c3fb27SDimitry Andric 
2306c3fb27SDimitry Andric /// Identifies an atomic boolean variable such as "V1".
2406c3fb27SDimitry Andric ///
2506c3fb27SDimitry Andric /// This often represents an assertion that is interesting to the analysis but
2606c3fb27SDimitry Andric /// cannot immediately be proven true or false. For example:
2706c3fb27SDimitry Andric /// - V1 may mean "the program reaches this point",
2806c3fb27SDimitry Andric /// - V2 may mean "the parameter was null"
2906c3fb27SDimitry Andric ///
3006c3fb27SDimitry Andric /// We can use these variables in formulas to describe relationships we know
3106c3fb27SDimitry Andric /// to be true: "if the parameter was null, the program reaches this point".
3206c3fb27SDimitry Andric /// We also express hypotheses as formulas, and use a SAT solver to check
3306c3fb27SDimitry Andric /// whether they are consistent with the known facts.
3406c3fb27SDimitry Andric enum class Atom : unsigned {};
3506c3fb27SDimitry Andric 
3606c3fb27SDimitry Andric /// A boolean expression such as "true" or "V1 & !V2".
3706c3fb27SDimitry Andric /// Expressions may refer to boolean atomic variables. These should take a
3806c3fb27SDimitry Andric /// consistent true/false value across the set of formulas being considered.
3906c3fb27SDimitry Andric ///
4006c3fb27SDimitry Andric /// (Formulas are always expressions in terms of boolean variables rather than
4106c3fb27SDimitry Andric /// e.g. integers because our underlying model is SAT rather than e.g. SMT).
4206c3fb27SDimitry Andric ///
4306c3fb27SDimitry Andric /// Simple formulas such as "true" and "V1" are self-contained.
4406c3fb27SDimitry Andric /// Compound formulas connect other formulas, e.g. "(V1 & V2) || V3" is an 'or'
4506c3fb27SDimitry Andric /// formula, with pointers to its operands "(V1 & V2)" and "V3" stored as
4606c3fb27SDimitry Andric /// trailing objects.
4706c3fb27SDimitry Andric /// For this reason, Formulas are Arena-allocated and over-aligned.
4806c3fb27SDimitry Andric class Formula;
alignas(const Formula *)4906c3fb27SDimitry Andric class alignas(const Formula *) Formula {
5006c3fb27SDimitry Andric public:
5106c3fb27SDimitry Andric   enum Kind : unsigned {
5206c3fb27SDimitry Andric     /// A reference to an atomic boolean variable.
5306c3fb27SDimitry Andric     /// We name these e.g. "V3", where 3 == atom identity == Value.
5406c3fb27SDimitry Andric     AtomRef,
555f757f3fSDimitry Andric     /// Constant true or false.
565f757f3fSDimitry Andric     Literal,
5706c3fb27SDimitry Andric 
5806c3fb27SDimitry Andric     Not, /// True if its only operand is false
5906c3fb27SDimitry Andric 
6006c3fb27SDimitry Andric     // These kinds connect two operands LHS and RHS
6106c3fb27SDimitry Andric     And,     /// True if LHS and RHS are both true
6206c3fb27SDimitry Andric     Or,      /// True if either LHS or RHS is true
6306c3fb27SDimitry Andric     Implies, /// True if LHS is false or RHS is true
6406c3fb27SDimitry Andric     Equal,   /// True if LHS and RHS have the same truth value
6506c3fb27SDimitry Andric   };
6606c3fb27SDimitry Andric   Kind kind() const { return FormulaKind; }
6706c3fb27SDimitry Andric 
6806c3fb27SDimitry Andric   Atom getAtom() const {
6906c3fb27SDimitry Andric     assert(kind() == AtomRef);
7006c3fb27SDimitry Andric     return static_cast<Atom>(Value);
7106c3fb27SDimitry Andric   }
7206c3fb27SDimitry Andric 
735f757f3fSDimitry Andric   bool literal() const {
745f757f3fSDimitry Andric     assert(kind() == Literal);
755f757f3fSDimitry Andric     return static_cast<bool>(Value);
765f757f3fSDimitry Andric   }
775f757f3fSDimitry Andric 
78*7a6dacacSDimitry Andric   bool isLiteral(bool b) const {
79*7a6dacacSDimitry Andric     return kind() == Literal && static_cast<bool>(Value) == b;
80*7a6dacacSDimitry Andric   }
81*7a6dacacSDimitry Andric 
8206c3fb27SDimitry Andric   ArrayRef<const Formula *> operands() const {
8306c3fb27SDimitry Andric     return ArrayRef(reinterpret_cast<Formula *const *>(this + 1),
8406c3fb27SDimitry Andric                     numOperands(kind()));
8506c3fb27SDimitry Andric   }
8606c3fb27SDimitry Andric 
8706c3fb27SDimitry Andric   using AtomNames = llvm::DenseMap<Atom, std::string>;
8806c3fb27SDimitry Andric   // Produce a stable human-readable representation of this formula.
8906c3fb27SDimitry Andric   // For example: (V3 | !(V1 & V2))
9006c3fb27SDimitry Andric   // If AtomNames is provided, these override the default V0, V1... names.
9106c3fb27SDimitry Andric   void print(llvm::raw_ostream &OS, const AtomNames * = nullptr) const;
9206c3fb27SDimitry Andric 
9306c3fb27SDimitry Andric   // Allocate Formulas using Arena rather than calling this function directly.
945f757f3fSDimitry Andric   static const Formula &create(llvm::BumpPtrAllocator &Alloc, Kind K,
9506c3fb27SDimitry Andric                                ArrayRef<const Formula *> Operands,
9606c3fb27SDimitry Andric                                unsigned Value = 0);
9706c3fb27SDimitry Andric 
9806c3fb27SDimitry Andric private:
9906c3fb27SDimitry Andric   Formula() = default;
10006c3fb27SDimitry Andric   Formula(const Formula &) = delete;
10106c3fb27SDimitry Andric   Formula &operator=(const Formula &) = delete;
10206c3fb27SDimitry Andric 
10306c3fb27SDimitry Andric   static unsigned numOperands(Kind K) {
10406c3fb27SDimitry Andric     switch (K) {
10506c3fb27SDimitry Andric     case AtomRef:
1065f757f3fSDimitry Andric     case Literal:
10706c3fb27SDimitry Andric       return 0;
10806c3fb27SDimitry Andric     case Not:
10906c3fb27SDimitry Andric       return 1;
11006c3fb27SDimitry Andric     case And:
11106c3fb27SDimitry Andric     case Or:
11206c3fb27SDimitry Andric     case Implies:
11306c3fb27SDimitry Andric     case Equal:
11406c3fb27SDimitry Andric       return 2;
11506c3fb27SDimitry Andric     }
11606c3fb27SDimitry Andric     llvm_unreachable("Unhandled Formula::Kind enum");
11706c3fb27SDimitry Andric   }
11806c3fb27SDimitry Andric 
11906c3fb27SDimitry Andric   Kind FormulaKind;
12006c3fb27SDimitry Andric   // Some kinds of formula have scalar values, e.g. AtomRef's atom number.
12106c3fb27SDimitry Andric   unsigned Value;
12206c3fb27SDimitry Andric };
12306c3fb27SDimitry Andric 
12406c3fb27SDimitry Andric // The default names of atoms are V0, V1 etc in order of creation.
12506c3fb27SDimitry Andric inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Atom A) {
12606c3fb27SDimitry Andric   return OS << 'V' << static_cast<unsigned>(A);
12706c3fb27SDimitry Andric }
12806c3fb27SDimitry Andric inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Formula &F) {
12906c3fb27SDimitry Andric   F.print(OS);
13006c3fb27SDimitry Andric   return OS;
13106c3fb27SDimitry Andric }
13206c3fb27SDimitry Andric 
13306c3fb27SDimitry Andric } // namespace clang::dataflow
13406c3fb27SDimitry Andric namespace llvm {
13506c3fb27SDimitry Andric template <> struct DenseMapInfo<clang::dataflow::Atom> {
13606c3fb27SDimitry Andric   using Atom = clang::dataflow::Atom;
13706c3fb27SDimitry Andric   using Underlying = std::underlying_type_t<Atom>;
13806c3fb27SDimitry Andric 
13906c3fb27SDimitry Andric   static inline Atom getEmptyKey() { return Atom(Underlying(-1)); }
14006c3fb27SDimitry Andric   static inline Atom getTombstoneKey() { return Atom(Underlying(-2)); }
14106c3fb27SDimitry Andric   static unsigned getHashValue(const Atom &Val) {
14206c3fb27SDimitry Andric     return DenseMapInfo<Underlying>::getHashValue(Underlying(Val));
14306c3fb27SDimitry Andric   }
14406c3fb27SDimitry Andric   static bool isEqual(const Atom &LHS, const Atom &RHS) { return LHS == RHS; }
14506c3fb27SDimitry Andric };
14606c3fb27SDimitry Andric } // namespace llvm
14706c3fb27SDimitry Andric #endif
148