106c3fb27SDimitry Andric //===-- Arena.cpp ---------------------------------------------------------===//
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 #include "clang/Analysis/FlowSensitive/Arena.h"
10*5f757f3fSDimitry Andric #include "clang/Analysis/FlowSensitive/Formula.h"
1106c3fb27SDimitry Andric #include "clang/Analysis/FlowSensitive/Value.h"
12*5f757f3fSDimitry Andric #include "llvm/Support/Error.h"
13*5f757f3fSDimitry Andric #include <string>
1406c3fb27SDimitry Andric 
1506c3fb27SDimitry Andric namespace clang::dataflow {
1606c3fb27SDimitry Andric 
1706c3fb27SDimitry Andric static std::pair<const Formula *, const Formula *>
canonicalFormulaPair(const Formula & LHS,const Formula & RHS)1806c3fb27SDimitry Andric canonicalFormulaPair(const Formula &LHS, const Formula &RHS) {
1906c3fb27SDimitry Andric   auto Res = std::make_pair(&LHS, &RHS);
2006c3fb27SDimitry Andric   if (&RHS < &LHS) // FIXME: use a deterministic order instead
2106c3fb27SDimitry Andric     std::swap(Res.first, Res.second);
2206c3fb27SDimitry Andric   return Res;
2306c3fb27SDimitry Andric }
2406c3fb27SDimitry Andric 
25*5f757f3fSDimitry Andric template <class Key, class ComputeFunc>
cached(llvm::DenseMap<Key,const Formula * > & Cache,Key K,ComputeFunc && Compute)26*5f757f3fSDimitry Andric const Formula &cached(llvm::DenseMap<Key, const Formula *> &Cache, Key K,
27*5f757f3fSDimitry Andric                       ComputeFunc &&Compute) {
28*5f757f3fSDimitry Andric   auto [It, Inserted] = Cache.try_emplace(std::forward<Key>(K));
2906c3fb27SDimitry Andric   if (Inserted)
30*5f757f3fSDimitry Andric     It->second = Compute();
3106c3fb27SDimitry Andric   return *It->second;
3206c3fb27SDimitry Andric }
3306c3fb27SDimitry Andric 
makeAtomRef(Atom A)34*5f757f3fSDimitry Andric const Formula &Arena::makeAtomRef(Atom A) {
35*5f757f3fSDimitry Andric   return cached(AtomRefs, A, [&] {
36*5f757f3fSDimitry Andric     return &Formula::create(Alloc, Formula::AtomRef, {},
37*5f757f3fSDimitry Andric                             static_cast<unsigned>(A));
38*5f757f3fSDimitry Andric   });
39*5f757f3fSDimitry Andric }
40*5f757f3fSDimitry Andric 
makeAnd(const Formula & LHS,const Formula & RHS)4106c3fb27SDimitry Andric const Formula &Arena::makeAnd(const Formula &LHS, const Formula &RHS) {
42*5f757f3fSDimitry Andric   return cached(Ands, canonicalFormulaPair(LHS, RHS), [&] {
4306c3fb27SDimitry Andric     if (&LHS == &RHS)
44*5f757f3fSDimitry Andric       return &LHS;
45*5f757f3fSDimitry Andric     if (LHS.kind() == Formula::Literal)
46*5f757f3fSDimitry Andric       return LHS.literal() ? &RHS : &LHS;
47*5f757f3fSDimitry Andric     if (RHS.kind() == Formula::Literal)
48*5f757f3fSDimitry Andric       return RHS.literal() ? &LHS : &RHS;
4906c3fb27SDimitry Andric 
50*5f757f3fSDimitry Andric     return &Formula::create(Alloc, Formula::And, {&LHS, &RHS});
51*5f757f3fSDimitry Andric   });
5206c3fb27SDimitry Andric }
5306c3fb27SDimitry Andric 
makeOr(const Formula & LHS,const Formula & RHS)5406c3fb27SDimitry Andric const Formula &Arena::makeOr(const Formula &LHS, const Formula &RHS) {
55*5f757f3fSDimitry Andric   return cached(Ors, canonicalFormulaPair(LHS, RHS), [&] {
5606c3fb27SDimitry Andric     if (&LHS == &RHS)
57*5f757f3fSDimitry Andric       return &LHS;
58*5f757f3fSDimitry Andric     if (LHS.kind() == Formula::Literal)
59*5f757f3fSDimitry Andric       return LHS.literal() ? &LHS : &RHS;
60*5f757f3fSDimitry Andric     if (RHS.kind() == Formula::Literal)
61*5f757f3fSDimitry Andric       return RHS.literal() ? &RHS : &LHS;
6206c3fb27SDimitry Andric 
63*5f757f3fSDimitry Andric     return &Formula::create(Alloc, Formula::Or, {&LHS, &RHS});
64*5f757f3fSDimitry Andric   });
6506c3fb27SDimitry Andric }
6606c3fb27SDimitry Andric 
makeNot(const Formula & Val)6706c3fb27SDimitry Andric const Formula &Arena::makeNot(const Formula &Val) {
68*5f757f3fSDimitry Andric   return cached(Nots, &Val, [&] {
69*5f757f3fSDimitry Andric     if (Val.kind() == Formula::Not)
70*5f757f3fSDimitry Andric       return Val.operands()[0];
71*5f757f3fSDimitry Andric     if (Val.kind() == Formula::Literal)
72*5f757f3fSDimitry Andric       return &makeLiteral(!Val.literal());
73*5f757f3fSDimitry Andric 
74*5f757f3fSDimitry Andric     return &Formula::create(Alloc, Formula::Not, {&Val});
75*5f757f3fSDimitry Andric   });
7606c3fb27SDimitry Andric }
7706c3fb27SDimitry Andric 
makeImplies(const Formula & LHS,const Formula & RHS)7806c3fb27SDimitry Andric const Formula &Arena::makeImplies(const Formula &LHS, const Formula &RHS) {
79*5f757f3fSDimitry Andric   return cached(Implies, std::make_pair(&LHS, &RHS), [&] {
8006c3fb27SDimitry Andric     if (&LHS == &RHS)
81*5f757f3fSDimitry Andric       return &makeLiteral(true);
82*5f757f3fSDimitry Andric     if (LHS.kind() == Formula::Literal)
83*5f757f3fSDimitry Andric       return LHS.literal() ? &RHS : &makeLiteral(true);
84*5f757f3fSDimitry Andric     if (RHS.kind() == Formula::Literal)
85*5f757f3fSDimitry Andric       return RHS.literal() ? &RHS : &makeNot(LHS);
8606c3fb27SDimitry Andric 
87*5f757f3fSDimitry Andric     return &Formula::create(Alloc, Formula::Implies, {&LHS, &RHS});
88*5f757f3fSDimitry Andric   });
8906c3fb27SDimitry Andric }
9006c3fb27SDimitry Andric 
makeEquals(const Formula & LHS,const Formula & RHS)9106c3fb27SDimitry Andric const Formula &Arena::makeEquals(const Formula &LHS, const Formula &RHS) {
92*5f757f3fSDimitry Andric   return cached(Equals, canonicalFormulaPair(LHS, RHS), [&] {
9306c3fb27SDimitry Andric     if (&LHS == &RHS)
94*5f757f3fSDimitry Andric       return &makeLiteral(true);
95*5f757f3fSDimitry Andric     if (LHS.kind() == Formula::Literal)
96*5f757f3fSDimitry Andric       return LHS.literal() ? &RHS : &makeNot(RHS);
97*5f757f3fSDimitry Andric     if (RHS.kind() == Formula::Literal)
98*5f757f3fSDimitry Andric       return RHS.literal() ? &LHS : &makeNot(LHS);
9906c3fb27SDimitry Andric 
100*5f757f3fSDimitry Andric     return &Formula::create(Alloc, Formula::Equal, {&LHS, &RHS});
101*5f757f3fSDimitry Andric   });
10206c3fb27SDimitry Andric }
10306c3fb27SDimitry Andric 
makeIntLiteral(llvm::APInt Value)10406c3fb27SDimitry Andric IntegerValue &Arena::makeIntLiteral(llvm::APInt Value) {
10506c3fb27SDimitry Andric   auto [It, Inserted] = IntegerLiterals.try_emplace(Value, nullptr);
10606c3fb27SDimitry Andric 
10706c3fb27SDimitry Andric   if (Inserted)
10806c3fb27SDimitry Andric     It->second = &create<IntegerValue>();
10906c3fb27SDimitry Andric   return *It->second;
11006c3fb27SDimitry Andric }
11106c3fb27SDimitry Andric 
makeBoolValue(const Formula & F)11206c3fb27SDimitry Andric BoolValue &Arena::makeBoolValue(const Formula &F) {
11306c3fb27SDimitry Andric   auto [It, Inserted] = FormulaValues.try_emplace(&F);
11406c3fb27SDimitry Andric   if (Inserted)
11506c3fb27SDimitry Andric     It->second = (F.kind() == Formula::AtomRef)
11606c3fb27SDimitry Andric                      ? (BoolValue *)&create<AtomicBoolValue>(F)
11706c3fb27SDimitry Andric                      : &create<FormulaBoolValue>(F);
11806c3fb27SDimitry Andric   return *It->second;
11906c3fb27SDimitry Andric }
12006c3fb27SDimitry Andric 
121*5f757f3fSDimitry Andric namespace {
parse(Arena & A,llvm::StringRef & In)122*5f757f3fSDimitry Andric const Formula *parse(Arena &A, llvm::StringRef &In) {
123*5f757f3fSDimitry Andric   auto EatSpaces = [&] { In = In.ltrim(' '); };
124*5f757f3fSDimitry Andric   EatSpaces();
125*5f757f3fSDimitry Andric 
126*5f757f3fSDimitry Andric   if (In.consume_front("!")) {
127*5f757f3fSDimitry Andric     if (auto *Arg = parse(A, In))
128*5f757f3fSDimitry Andric       return &A.makeNot(*Arg);
129*5f757f3fSDimitry Andric     return nullptr;
130*5f757f3fSDimitry Andric   }
131*5f757f3fSDimitry Andric 
132*5f757f3fSDimitry Andric   if (In.consume_front("(")) {
133*5f757f3fSDimitry Andric     auto *Arg1 = parse(A, In);
134*5f757f3fSDimitry Andric     if (!Arg1)
135*5f757f3fSDimitry Andric       return nullptr;
136*5f757f3fSDimitry Andric 
137*5f757f3fSDimitry Andric     EatSpaces();
138*5f757f3fSDimitry Andric     decltype(&Arena::makeOr) Op;
139*5f757f3fSDimitry Andric     if (In.consume_front("|"))
140*5f757f3fSDimitry Andric       Op = &Arena::makeOr;
141*5f757f3fSDimitry Andric     else if (In.consume_front("&"))
142*5f757f3fSDimitry Andric       Op = &Arena::makeAnd;
143*5f757f3fSDimitry Andric     else if (In.consume_front("=>"))
144*5f757f3fSDimitry Andric       Op = &Arena::makeImplies;
145*5f757f3fSDimitry Andric     else if (In.consume_front("="))
146*5f757f3fSDimitry Andric       Op = &Arena::makeEquals;
147*5f757f3fSDimitry Andric     else
148*5f757f3fSDimitry Andric       return nullptr;
149*5f757f3fSDimitry Andric 
150*5f757f3fSDimitry Andric     auto *Arg2 = parse(A, In);
151*5f757f3fSDimitry Andric     if (!Arg2)
152*5f757f3fSDimitry Andric       return nullptr;
153*5f757f3fSDimitry Andric 
154*5f757f3fSDimitry Andric     EatSpaces();
155*5f757f3fSDimitry Andric     if (!In.consume_front(")"))
156*5f757f3fSDimitry Andric       return nullptr;
157*5f757f3fSDimitry Andric 
158*5f757f3fSDimitry Andric     return &(A.*Op)(*Arg1, *Arg2);
159*5f757f3fSDimitry Andric   }
160*5f757f3fSDimitry Andric 
161*5f757f3fSDimitry Andric   // For now, only support unnamed variables V0, V1 etc.
162*5f757f3fSDimitry Andric   // FIXME: parse e.g. "X" by allocating an atom and storing a name somewhere.
163*5f757f3fSDimitry Andric   if (In.consume_front("V")) {
164*5f757f3fSDimitry Andric     std::underlying_type_t<Atom> At;
165*5f757f3fSDimitry Andric     if (In.consumeInteger(10, At))
166*5f757f3fSDimitry Andric       return nullptr;
167*5f757f3fSDimitry Andric     return &A.makeAtomRef(static_cast<Atom>(At));
168*5f757f3fSDimitry Andric   }
169*5f757f3fSDimitry Andric 
170*5f757f3fSDimitry Andric   if (In.consume_front("true"))
171*5f757f3fSDimitry Andric     return &A.makeLiteral(true);
172*5f757f3fSDimitry Andric   if (In.consume_front("false"))
173*5f757f3fSDimitry Andric     return &A.makeLiteral(false);
174*5f757f3fSDimitry Andric 
175*5f757f3fSDimitry Andric   return nullptr;
176*5f757f3fSDimitry Andric }
177*5f757f3fSDimitry Andric 
178*5f757f3fSDimitry Andric class FormulaParseError : public llvm::ErrorInfo<FormulaParseError> {
179*5f757f3fSDimitry Andric   std::string Formula;
180*5f757f3fSDimitry Andric   unsigned Offset;
181*5f757f3fSDimitry Andric 
182*5f757f3fSDimitry Andric public:
183*5f757f3fSDimitry Andric   static char ID;
FormulaParseError(llvm::StringRef Formula,unsigned Offset)184*5f757f3fSDimitry Andric   FormulaParseError(llvm::StringRef Formula, unsigned Offset)
185*5f757f3fSDimitry Andric       : Formula(Formula), Offset(Offset) {}
186*5f757f3fSDimitry Andric 
log(raw_ostream & OS) const187*5f757f3fSDimitry Andric   void log(raw_ostream &OS) const override {
188*5f757f3fSDimitry Andric     OS << "bad formula at offset " << Offset << "\n";
189*5f757f3fSDimitry Andric     OS << Formula << "\n";
190*5f757f3fSDimitry Andric     OS.indent(Offset) << "^";
191*5f757f3fSDimitry Andric   }
192*5f757f3fSDimitry Andric 
convertToErrorCode() const193*5f757f3fSDimitry Andric   std::error_code convertToErrorCode() const override {
194*5f757f3fSDimitry Andric     return std::make_error_code(std::errc::invalid_argument);
195*5f757f3fSDimitry Andric   }
196*5f757f3fSDimitry Andric };
197*5f757f3fSDimitry Andric 
198*5f757f3fSDimitry Andric char FormulaParseError::ID = 0;
199*5f757f3fSDimitry Andric 
200*5f757f3fSDimitry Andric } // namespace
201*5f757f3fSDimitry Andric 
parseFormula(llvm::StringRef In)202*5f757f3fSDimitry Andric llvm::Expected<const Formula &> Arena::parseFormula(llvm::StringRef In) {
203*5f757f3fSDimitry Andric   llvm::StringRef Rest = In;
204*5f757f3fSDimitry Andric   auto *Result = parse(*this, Rest);
205*5f757f3fSDimitry Andric   if (!Result) // parse() hit something unparseable
206*5f757f3fSDimitry Andric     return llvm::make_error<FormulaParseError>(In, In.size() - Rest.size());
207*5f757f3fSDimitry Andric   Rest = Rest.ltrim();
208*5f757f3fSDimitry Andric   if (!Rest.empty()) // parse didn't consume all the input
209*5f757f3fSDimitry Andric     return llvm::make_error<FormulaParseError>(In, In.size() - Rest.size());
210*5f757f3fSDimitry Andric   return *Result;
211*5f757f3fSDimitry Andric }
212*5f757f3fSDimitry Andric 
21306c3fb27SDimitry Andric } // namespace clang::dataflow
214