xref: /llvm-project/clang/lib/Analysis/FlowSensitive/Arena.cpp (revision 65c36179be68dda0d1cc5d7e5c5b312a6b52cc0e)
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