1bf47c1edSSam McCall //===-- Arena.cpp ---------------------------------------------------------===// 2bf47c1edSSam McCall // 3bf47c1edSSam McCall // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4bf47c1edSSam McCall // See https://llvm.org/LICENSE.txt for license information. 5bf47c1edSSam McCall // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6bf47c1edSSam McCall // 7bf47c1edSSam McCall //===----------------------------------------------------------------------===// 8bf47c1edSSam McCall 9bf47c1edSSam McCall #include "clang/Analysis/FlowSensitive/Arena.h" 103f78d6abSSam McCall #include "clang/Analysis/FlowSensitive/Formula.h" 11fc9821a8SSam McCall #include "clang/Analysis/FlowSensitive/Value.h" 123f78d6abSSam McCall #include "llvm/Support/Error.h" 133f78d6abSSam McCall #include <string> 14bf47c1edSSam McCall 15bf47c1edSSam McCall namespace clang::dataflow { 16bf47c1edSSam McCall 17fc9821a8SSam McCall static std::pair<const Formula *, const Formula *> 18fc9821a8SSam McCall canonicalFormulaPair(const Formula &LHS, const Formula &RHS) { 19bf47c1edSSam McCall auto Res = std::make_pair(&LHS, &RHS); 20fc9821a8SSam McCall if (&RHS < &LHS) // FIXME: use a deterministic order instead 21bf47c1edSSam McCall std::swap(Res.first, Res.second); 22bf47c1edSSam McCall return Res; 23bf47c1edSSam McCall } 24bf47c1edSSam McCall 257338eb56SSam McCall template <class Key, class ComputeFunc> 26*65c36179SCongcong Cai static const Formula &cached(llvm::DenseMap<Key, const Formula *> &Cache, Key K, 277338eb56SSam McCall ComputeFunc &&Compute) { 287338eb56SSam McCall auto [It, Inserted] = Cache.try_emplace(std::forward<Key>(K)); 29fc9821a8SSam McCall if (Inserted) 307338eb56SSam McCall It->second = Compute(); 31fc9821a8SSam McCall return *It->second; 32fc9821a8SSam McCall } 33fc9821a8SSam McCall 347338eb56SSam McCall const Formula &Arena::makeAtomRef(Atom A) { 357338eb56SSam McCall return cached(AtomRefs, A, [&] { 367338eb56SSam McCall return &Formula::create(Alloc, Formula::AtomRef, {}, 377338eb56SSam McCall static_cast<unsigned>(A)); 387338eb56SSam McCall }); 397338eb56SSam McCall } 407338eb56SSam McCall 41fc9821a8SSam McCall const Formula &Arena::makeAnd(const Formula &LHS, const Formula &RHS) { 427338eb56SSam McCall return cached(Ands, canonicalFormulaPair(LHS, RHS), [&] { 43bf47c1edSSam McCall if (&LHS == &RHS) 447338eb56SSam McCall return &LHS; 457338eb56SSam McCall if (LHS.kind() == Formula::Literal) 467338eb56SSam McCall return LHS.literal() ? &RHS : &LHS; 477338eb56SSam McCall if (RHS.kind() == Formula::Literal) 487338eb56SSam McCall return RHS.literal() ? &LHS : &RHS; 49bf47c1edSSam McCall 507338eb56SSam McCall return &Formula::create(Alloc, Formula::And, {&LHS, &RHS}); 517338eb56SSam McCall }); 52bf47c1edSSam McCall } 53bf47c1edSSam McCall 54fc9821a8SSam McCall const Formula &Arena::makeOr(const Formula &LHS, const Formula &RHS) { 557338eb56SSam McCall return cached(Ors, canonicalFormulaPair(LHS, RHS), [&] { 56bf47c1edSSam McCall if (&LHS == &RHS) 577338eb56SSam McCall return &LHS; 587338eb56SSam McCall if (LHS.kind() == Formula::Literal) 597338eb56SSam McCall return LHS.literal() ? &LHS : &RHS; 607338eb56SSam McCall if (RHS.kind() == Formula::Literal) 617338eb56SSam McCall return RHS.literal() ? &RHS : &LHS; 62bf47c1edSSam McCall 637338eb56SSam McCall return &Formula::create(Alloc, Formula::Or, {&LHS, &RHS}); 647338eb56SSam McCall }); 65bf47c1edSSam McCall } 66bf47c1edSSam McCall 67fc9821a8SSam McCall const Formula &Arena::makeNot(const Formula &Val) { 687338eb56SSam McCall return cached(Nots, &Val, [&] { 697338eb56SSam McCall if (Val.kind() == Formula::Not) 707338eb56SSam McCall return Val.operands()[0]; 717338eb56SSam McCall if (Val.kind() == Formula::Literal) 727338eb56SSam McCall return &makeLiteral(!Val.literal()); 737338eb56SSam McCall 747338eb56SSam McCall return &Formula::create(Alloc, Formula::Not, {&Val}); 757338eb56SSam McCall }); 76bf47c1edSSam McCall } 77bf47c1edSSam McCall 78fc9821a8SSam McCall const Formula &Arena::makeImplies(const Formula &LHS, const Formula &RHS) { 797338eb56SSam McCall return cached(Implies, std::make_pair(&LHS, &RHS), [&] { 80bf47c1edSSam McCall if (&LHS == &RHS) 817338eb56SSam McCall return &makeLiteral(true); 827338eb56SSam McCall if (LHS.kind() == Formula::Literal) 837338eb56SSam McCall return LHS.literal() ? &RHS : &makeLiteral(true); 847338eb56SSam McCall if (RHS.kind() == Formula::Literal) 857338eb56SSam McCall return RHS.literal() ? &RHS : &makeNot(LHS); 86bf47c1edSSam McCall 877338eb56SSam McCall return &Formula::create(Alloc, Formula::Implies, {&LHS, &RHS}); 887338eb56SSam McCall }); 89bf47c1edSSam McCall } 90bf47c1edSSam McCall 91fc9821a8SSam McCall const Formula &Arena::makeEquals(const Formula &LHS, const Formula &RHS) { 927338eb56SSam McCall return cached(Equals, canonicalFormulaPair(LHS, RHS), [&] { 93bf47c1edSSam McCall if (&LHS == &RHS) 947338eb56SSam McCall return &makeLiteral(true); 957338eb56SSam McCall if (LHS.kind() == Formula::Literal) 967338eb56SSam McCall return LHS.literal() ? &RHS : &makeNot(RHS); 977338eb56SSam McCall if (RHS.kind() == Formula::Literal) 987338eb56SSam McCall return RHS.literal() ? &LHS : &makeNot(LHS); 99bf47c1edSSam McCall 1007338eb56SSam McCall return &Formula::create(Alloc, Formula::Equal, {&LHS, &RHS}); 1017338eb56SSam McCall }); 102bf47c1edSSam McCall } 103bf47c1edSSam McCall 104762cb1d3SMartin Braenne IntegerValue &Arena::makeIntLiteral(llvm::APInt Value) { 105762cb1d3SMartin Braenne auto [It, Inserted] = IntegerLiterals.try_emplace(Value, nullptr); 106762cb1d3SMartin Braenne 107762cb1d3SMartin Braenne if (Inserted) 108762cb1d3SMartin Braenne It->second = &create<IntegerValue>(); 109762cb1d3SMartin Braenne return *It->second; 110762cb1d3SMartin Braenne } 111762cb1d3SMartin Braenne 112fc9821a8SSam McCall BoolValue &Arena::makeBoolValue(const Formula &F) { 113fc9821a8SSam McCall auto [It, Inserted] = FormulaValues.try_emplace(&F); 114fc9821a8SSam McCall if (Inserted) 115fc9821a8SSam McCall It->second = (F.kind() == Formula::AtomRef) 116fc9821a8SSam McCall ? (BoolValue *)&create<AtomicBoolValue>(F) 117fc9821a8SSam McCall : &create<FormulaBoolValue>(F); 118fc9821a8SSam McCall return *It->second; 119fc9821a8SSam McCall } 120fc9821a8SSam McCall 1213f78d6abSSam McCall namespace { 1223f78d6abSSam McCall const Formula *parse(Arena &A, llvm::StringRef &In) { 1233f78d6abSSam McCall auto EatSpaces = [&] { In = In.ltrim(' '); }; 1243f78d6abSSam McCall EatSpaces(); 1253f78d6abSSam McCall 1263f78d6abSSam McCall if (In.consume_front("!")) { 1273f78d6abSSam McCall if (auto *Arg = parse(A, In)) 1283f78d6abSSam McCall return &A.makeNot(*Arg); 1293f78d6abSSam McCall return nullptr; 1303f78d6abSSam McCall } 1313f78d6abSSam McCall 1323f78d6abSSam McCall if (In.consume_front("(")) { 1333f78d6abSSam McCall auto *Arg1 = parse(A, In); 1343f78d6abSSam McCall if (!Arg1) 1353f78d6abSSam McCall return nullptr; 1363f78d6abSSam McCall 1373f78d6abSSam McCall EatSpaces(); 1383f78d6abSSam McCall decltype(&Arena::makeOr) Op; 1393f78d6abSSam McCall if (In.consume_front("|")) 1403f78d6abSSam McCall Op = &Arena::makeOr; 1413f78d6abSSam McCall else if (In.consume_front("&")) 1423f78d6abSSam McCall Op = &Arena::makeAnd; 1433f78d6abSSam McCall else if (In.consume_front("=>")) 1443f78d6abSSam McCall Op = &Arena::makeImplies; 1453f78d6abSSam McCall else if (In.consume_front("=")) 1463f78d6abSSam McCall Op = &Arena::makeEquals; 1473f78d6abSSam McCall else 1483f78d6abSSam McCall return nullptr; 1493f78d6abSSam McCall 1503f78d6abSSam McCall auto *Arg2 = parse(A, In); 1513f78d6abSSam McCall if (!Arg2) 1523f78d6abSSam McCall return nullptr; 1533f78d6abSSam McCall 1543f78d6abSSam McCall EatSpaces(); 1553f78d6abSSam McCall if (!In.consume_front(")")) 1563f78d6abSSam McCall return nullptr; 1573f78d6abSSam McCall 1583f78d6abSSam McCall return &(A.*Op)(*Arg1, *Arg2); 1593f78d6abSSam McCall } 1603f78d6abSSam McCall 1613f78d6abSSam McCall // For now, only support unnamed variables V0, V1 etc. 1623f78d6abSSam McCall // FIXME: parse e.g. "X" by allocating an atom and storing a name somewhere. 1633f78d6abSSam McCall if (In.consume_front("V")) { 1643f78d6abSSam McCall std::underlying_type_t<Atom> At; 1653f78d6abSSam McCall if (In.consumeInteger(10, At)) 1663f78d6abSSam McCall return nullptr; 1673f78d6abSSam McCall return &A.makeAtomRef(static_cast<Atom>(At)); 1683f78d6abSSam McCall } 1693f78d6abSSam McCall 1703f78d6abSSam McCall if (In.consume_front("true")) 1713f78d6abSSam McCall return &A.makeLiteral(true); 1723f78d6abSSam McCall if (In.consume_front("false")) 1733f78d6abSSam McCall return &A.makeLiteral(false); 1743f78d6abSSam McCall 1753f78d6abSSam McCall return nullptr; 1763f78d6abSSam McCall } 1773f78d6abSSam McCall 1783f78d6abSSam McCall class FormulaParseError : public llvm::ErrorInfo<FormulaParseError> { 1793f78d6abSSam McCall std::string Formula; 1803f78d6abSSam McCall unsigned Offset; 1813f78d6abSSam McCall 1823f78d6abSSam McCall public: 1833f78d6abSSam McCall static char ID; 1843f78d6abSSam McCall FormulaParseError(llvm::StringRef Formula, unsigned Offset) 1853f78d6abSSam McCall : Formula(Formula), Offset(Offset) {} 1863f78d6abSSam McCall 1873f78d6abSSam McCall void log(raw_ostream &OS) const override { 1883f78d6abSSam McCall OS << "bad formula at offset " << Offset << "\n"; 1893f78d6abSSam McCall OS << Formula << "\n"; 1903f78d6abSSam McCall OS.indent(Offset) << "^"; 1913f78d6abSSam McCall } 1923f78d6abSSam McCall 1933f78d6abSSam McCall std::error_code convertToErrorCode() const override { 1943f78d6abSSam McCall return std::make_error_code(std::errc::invalid_argument); 1953f78d6abSSam McCall } 1963f78d6abSSam McCall }; 1973f78d6abSSam McCall 1983f78d6abSSam McCall char FormulaParseError::ID = 0; 1993f78d6abSSam McCall 2003f78d6abSSam McCall } // namespace 2013f78d6abSSam McCall 2023f78d6abSSam McCall llvm::Expected<const Formula &> Arena::parseFormula(llvm::StringRef In) { 2033f78d6abSSam McCall llvm::StringRef Rest = In; 2043f78d6abSSam McCall auto *Result = parse(*this, Rest); 2053f78d6abSSam McCall if (!Result) // parse() hit something unparseable 2063f78d6abSSam McCall return llvm::make_error<FormulaParseError>(In, In.size() - Rest.size()); 2073f78d6abSSam McCall Rest = Rest.ltrim(); 2083f78d6abSSam McCall if (!Rest.empty()) // parse didn't consume all the input 2093f78d6abSSam McCall return llvm::make_error<FormulaParseError>(In, In.size() - Rest.size()); 2103f78d6abSSam McCall return *Result; 2113f78d6abSSam McCall } 2123f78d6abSSam McCall 213bf47c1edSSam McCall } // namespace clang::dataflow 214