| 1 | //===-- Arena.cpp ---------------------------------------------------------===// |
| 2 | // |
| 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 |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "clang/Analysis/FlowSensitive/Arena.h" |
| 10 | #include "clang/Analysis/FlowSensitive/Formula.h" |
| 11 | #include "clang/Analysis/FlowSensitive/Value.h" |
| 12 | #include "llvm/Support/Error.h" |
| 13 | #include <string> |
| 14 | |
| 15 | namespace clang::dataflow { |
| 16 | |
| 17 | static std::pair<const Formula *, const Formula *> |
| 18 | canonicalFormulaPair(const Formula &LHS, const Formula &RHS) { |
| 19 | auto Res = std::make_pair(x: &LHS, y: &RHS); |
| 20 | if (&RHS < &LHS) // FIXME: use a deterministic order instead |
| 21 | std::swap(a&: Res.first, b&: Res.second); |
| 22 | return Res; |
| 23 | } |
| 24 | |
| 25 | template <class Key, class ComputeFunc> |
| 26 | static const Formula &cached(llvm::DenseMap<Key, const Formula *> &Cache, Key K, |
| 27 | ComputeFunc &&Compute) { |
| 28 | auto [It, Inserted] = Cache.try_emplace(std::forward<Key>(K)); |
| 29 | if (Inserted) |
| 30 | It->second = Compute(); |
| 31 | return *It->second; |
| 32 | } |
| 33 | |
| 34 | const Formula &Arena::makeAtomRef(Atom A) { |
| 35 | return cached(Cache&: AtomRefs, K: A, Compute: [&] { |
| 36 | return &Formula::create(Alloc, K: Formula::AtomRef, Operands: {}, |
| 37 | Value: static_cast<unsigned>(A)); |
| 38 | }); |
| 39 | } |
| 40 | |
| 41 | const Formula &Arena::makeAnd(const Formula &LHS, const Formula &RHS) { |
| 42 | return cached(Cache&: Ands, K: canonicalFormulaPair(LHS, RHS), Compute: [&] { |
| 43 | if (&LHS == &RHS) |
| 44 | return &LHS; |
| 45 | if (LHS.kind() == Formula::Literal) |
| 46 | return LHS.literal() ? &RHS : &LHS; |
| 47 | if (RHS.kind() == Formula::Literal) |
| 48 | return RHS.literal() ? &LHS : &RHS; |
| 49 | |
| 50 | return &Formula::create(Alloc, K: Formula::And, Operands: {&LHS, &RHS}); |
| 51 | }); |
| 52 | } |
| 53 | |
| 54 | const Formula &Arena::makeOr(const Formula &LHS, const Formula &RHS) { |
| 55 | return cached(Cache&: Ors, K: canonicalFormulaPair(LHS, RHS), Compute: [&] { |
| 56 | if (&LHS == &RHS) |
| 57 | return &LHS; |
| 58 | if (LHS.kind() == Formula::Literal) |
| 59 | return LHS.literal() ? &LHS : &RHS; |
| 60 | if (RHS.kind() == Formula::Literal) |
| 61 | return RHS.literal() ? &RHS : &LHS; |
| 62 | |
| 63 | return &Formula::create(Alloc, K: Formula::Or, Operands: {&LHS, &RHS}); |
| 64 | }); |
| 65 | } |
| 66 | |
| 67 | const Formula &Arena::makeNot(const Formula &Val) { |
| 68 | return cached(Cache&: Nots, K: &Val, Compute: [&] { |
| 69 | if (Val.kind() == Formula::Not) |
| 70 | return Val.operands()[0]; |
| 71 | if (Val.kind() == Formula::Literal) |
| 72 | return &makeLiteral(Value: !Val.literal()); |
| 73 | |
| 74 | return &Formula::create(Alloc, K: Formula::Not, Operands: {&Val}); |
| 75 | }); |
| 76 | } |
| 77 | |
| 78 | const Formula &Arena::makeImplies(const Formula &LHS, const Formula &RHS) { |
| 79 | return cached(Cache&: Implies, K: std::make_pair(x: &LHS, y: &RHS), Compute: [&] { |
| 80 | if (&LHS == &RHS) |
| 81 | return &makeLiteral(Value: true); |
| 82 | if (LHS.kind() == Formula::Literal) |
| 83 | return LHS.literal() ? &RHS : &makeLiteral(Value: true); |
| 84 | if (RHS.kind() == Formula::Literal) |
| 85 | return RHS.literal() ? &RHS : &makeNot(Val: LHS); |
| 86 | |
| 87 | return &Formula::create(Alloc, K: Formula::Implies, Operands: {&LHS, &RHS}); |
| 88 | }); |
| 89 | } |
| 90 | |
| 91 | const Formula &Arena::makeEquals(const Formula &LHS, const Formula &RHS) { |
| 92 | return cached(Cache&: Equals, K: canonicalFormulaPair(LHS, RHS), Compute: [&] { |
| 93 | if (&LHS == &RHS) |
| 94 | return &makeLiteral(Value: true); |
| 95 | if (LHS.kind() == Formula::Literal) |
| 96 | return LHS.literal() ? &RHS : &makeNot(Val: RHS); |
| 97 | if (RHS.kind() == Formula::Literal) |
| 98 | return RHS.literal() ? &LHS : &makeNot(Val: LHS); |
| 99 | |
| 100 | return &Formula::create(Alloc, K: Formula::Equal, Operands: {&LHS, &RHS}); |
| 101 | }); |
| 102 | } |
| 103 | |
| 104 | IntegerValue &Arena::makeIntLiteral(llvm::APInt Value) { |
| 105 | auto [It, Inserted] = IntegerLiterals.try_emplace(Key: Value, Args: nullptr); |
| 106 | |
| 107 | if (Inserted) |
| 108 | It->second = &create<IntegerValue>(); |
| 109 | return *It->second; |
| 110 | } |
| 111 | |
| 112 | BoolValue &Arena::makeBoolValue(const Formula &F) { |
| 113 | auto [It, Inserted] = FormulaValues.try_emplace(Key: &F); |
| 114 | if (Inserted) |
| 115 | It->second = (F.kind() == Formula::AtomRef) |
| 116 | ? (BoolValue *)&create<AtomicBoolValue>(args: F) |
| 117 | : &create<FormulaBoolValue>(args: F); |
| 118 | return *It->second; |
| 119 | } |
| 120 | |
| 121 | namespace { |
| 122 | const Formula *parse(Arena &A, llvm::StringRef &In) { |
| 123 | auto EatSpaces = [&] { In = In.ltrim(Char: ' '); }; |
| 124 | EatSpaces(); |
| 125 | |
| 126 | if (In.consume_front(Prefix: "!" )) { |
| 127 | if (auto *Arg = parse(A, In)) |
| 128 | return &A.makeNot(Val: *Arg); |
| 129 | return nullptr; |
| 130 | } |
| 131 | |
| 132 | if (In.consume_front(Prefix: "(" )) { |
| 133 | auto *Arg1 = parse(A, In); |
| 134 | if (!Arg1) |
| 135 | return nullptr; |
| 136 | |
| 137 | EatSpaces(); |
| 138 | decltype(&Arena::makeOr) Op; |
| 139 | if (In.consume_front(Prefix: "|" )) |
| 140 | Op = &Arena::makeOr; |
| 141 | else if (In.consume_front(Prefix: "&" )) |
| 142 | Op = &Arena::makeAnd; |
| 143 | else if (In.consume_front(Prefix: "=>" )) |
| 144 | Op = &Arena::makeImplies; |
| 145 | else if (In.consume_front(Prefix: "=" )) |
| 146 | Op = &Arena::makeEquals; |
| 147 | else |
| 148 | return nullptr; |
| 149 | |
| 150 | auto *Arg2 = parse(A, In); |
| 151 | if (!Arg2) |
| 152 | return nullptr; |
| 153 | |
| 154 | EatSpaces(); |
| 155 | if (!In.consume_front(Prefix: ")" )) |
| 156 | return nullptr; |
| 157 | |
| 158 | return &(A.*Op)(*Arg1, *Arg2); |
| 159 | } |
| 160 | |
| 161 | // For now, only support unnamed variables V0, V1 etc. |
| 162 | // FIXME: parse e.g. "X" by allocating an atom and storing a name somewhere. |
| 163 | if (In.consume_front(Prefix: "V" )) { |
| 164 | std::underlying_type_t<Atom> At; |
| 165 | if (In.consumeInteger(Radix: 10, Result&: At)) |
| 166 | return nullptr; |
| 167 | return &A.makeAtomRef(A: static_cast<Atom>(At)); |
| 168 | } |
| 169 | |
| 170 | if (In.consume_front(Prefix: "true" )) |
| 171 | return &A.makeLiteral(Value: true); |
| 172 | if (In.consume_front(Prefix: "false" )) |
| 173 | return &A.makeLiteral(Value: false); |
| 174 | |
| 175 | return nullptr; |
| 176 | } |
| 177 | |
| 178 | class FormulaParseError : public llvm::ErrorInfo<FormulaParseError> { |
| 179 | std::string Formula; |
| 180 | unsigned Offset; |
| 181 | |
| 182 | public: |
| 183 | static char ID; |
| 184 | FormulaParseError(llvm::StringRef Formula, unsigned Offset) |
| 185 | : Formula(Formula), Offset(Offset) {} |
| 186 | |
| 187 | void log(raw_ostream &OS) const override { |
| 188 | OS << "bad formula at offset " << Offset << "\n" ; |
| 189 | OS << Formula << "\n" ; |
| 190 | OS.indent(NumSpaces: Offset) << "^" ; |
| 191 | } |
| 192 | |
| 193 | std::error_code convertToErrorCode() const override { |
| 194 | return std::make_error_code(e: std::errc::invalid_argument); |
| 195 | } |
| 196 | }; |
| 197 | |
| 198 | char FormulaParseError::ID = 0; |
| 199 | |
| 200 | } // namespace |
| 201 | |
| 202 | llvm::Expected<const Formula &> Arena::parseFormula(llvm::StringRef In) { |
| 203 | llvm::StringRef Rest = In; |
| 204 | auto *Result = parse(A&: *this, In&: Rest); |
| 205 | if (!Result) // parse() hit something unparseable |
| 206 | return llvm::make_error<FormulaParseError>(Args&: In, Args: In.size() - Rest.size()); |
| 207 | Rest = Rest.ltrim(); |
| 208 | if (!Rest.empty()) // parse didn't consume all the input |
| 209 | return llvm::make_error<FormulaParseError>(Args&: In, Args: In.size() - Rest.size()); |
| 210 | return *Result; |
| 211 | } |
| 212 | |
| 213 | } // namespace clang::dataflow |
| 214 | |