1 //===-- DataflowAnalysisContext.cpp -----------------------------*- C++ -*-===//
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 //  This file defines a DataflowAnalysisContext class that owns objects that
10 //  encompass the state of a program and stores context that is used during
11 //  dataflow analysis.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
16 #include "clang/AST/ExprCXX.h"
17 #include "clang/Analysis/FlowSensitive/Value.h"
18 #include <cassert>
19 #include <memory>
20 #include <utility>
21 
22 namespace clang {
23 namespace dataflow {
24 
25 StorageLocation &
26 DataflowAnalysisContext::getStableStorageLocation(QualType Type) {
27   assert(!Type.isNull());
28   if (Type->isStructureOrClassType() || Type->isUnionType()) {
29     // FIXME: Explore options to avoid eager initialization of fields as some of
30     // them might not be needed for a particular analysis.
31     llvm::DenseMap<const ValueDecl *, StorageLocation *> FieldLocs;
32     for (const FieldDecl *Field : getObjectFields(Type))
33       FieldLocs.insert({Field, &getStableStorageLocation(Field->getType())});
34     return takeOwnership(
35         std::make_unique<AggregateStorageLocation>(Type, std::move(FieldLocs)));
36   }
37   return takeOwnership(std::make_unique<ScalarStorageLocation>(Type));
38 }
39 
40 StorageLocation &
41 DataflowAnalysisContext::getStableStorageLocation(const VarDecl &D) {
42   if (auto *Loc = getStorageLocation(D))
43     return *Loc;
44   auto &Loc = getStableStorageLocation(D.getType());
45   setStorageLocation(D, Loc);
46   return Loc;
47 }
48 
49 StorageLocation &
50 DataflowAnalysisContext::getStableStorageLocation(const Expr &E) {
51   if (auto *Loc = getStorageLocation(E))
52     return *Loc;
53   auto &Loc = getStableStorageLocation(E.getType());
54   setStorageLocation(E, Loc);
55   return Loc;
56 }
57 
58 PointerValue &
59 DataflowAnalysisContext::getOrCreateNullPointerValue(QualType PointeeType) {
60   assert(!PointeeType.isNull());
61   auto CanonicalPointeeType = PointeeType.getCanonicalType();
62   auto Res = NullPointerVals.try_emplace(CanonicalPointeeType, nullptr);
63   if (Res.second) {
64     auto &PointeeLoc = getStableStorageLocation(CanonicalPointeeType);
65     Res.first->second =
66         &takeOwnership(std::make_unique<PointerValue>(PointeeLoc));
67   }
68   return *Res.first->second;
69 }
70 
71 static std::pair<BoolValue *, BoolValue *>
72 makeCanonicalBoolValuePair(BoolValue &LHS, BoolValue &RHS) {
73   auto Res = std::make_pair(&LHS, &RHS);
74   if (&RHS < &LHS)
75     std::swap(Res.first, Res.second);
76   return Res;
77 }
78 
79 BoolValue &DataflowAnalysisContext::getOrCreateConjunction(BoolValue &LHS,
80                                                            BoolValue &RHS) {
81   if (&LHS == &RHS)
82     return LHS;
83 
84   auto Res = ConjunctionVals.try_emplace(makeCanonicalBoolValuePair(LHS, RHS),
85                                          nullptr);
86   if (Res.second)
87     Res.first->second =
88         &takeOwnership(std::make_unique<ConjunctionValue>(LHS, RHS));
89   return *Res.first->second;
90 }
91 
92 BoolValue &DataflowAnalysisContext::getOrCreateDisjunction(BoolValue &LHS,
93                                                            BoolValue &RHS) {
94   if (&LHS == &RHS)
95     return LHS;
96 
97   auto Res = DisjunctionVals.try_emplace(makeCanonicalBoolValuePair(LHS, RHS),
98                                          nullptr);
99   if (Res.second)
100     Res.first->second =
101         &takeOwnership(std::make_unique<DisjunctionValue>(LHS, RHS));
102   return *Res.first->second;
103 }
104 
105 BoolValue &DataflowAnalysisContext::getOrCreateNegation(BoolValue &Val) {
106   auto Res = NegationVals.try_emplace(&Val, nullptr);
107   if (Res.second)
108     Res.first->second = &takeOwnership(std::make_unique<NegationValue>(Val));
109   return *Res.first->second;
110 }
111 
112 BoolValue &DataflowAnalysisContext::getOrCreateImplication(BoolValue &LHS,
113                                                            BoolValue &RHS) {
114   return &LHS == &RHS ? getBoolLiteralValue(true)
115                       : getOrCreateDisjunction(getOrCreateNegation(LHS), RHS);
116 }
117 
118 BoolValue &DataflowAnalysisContext::getOrCreateIff(BoolValue &LHS,
119                                                    BoolValue &RHS) {
120   return &LHS == &RHS
121              ? getBoolLiteralValue(true)
122              : getOrCreateConjunction(getOrCreateImplication(LHS, RHS),
123                                       getOrCreateImplication(RHS, LHS));
124 }
125 
126 AtomicBoolValue &DataflowAnalysisContext::makeFlowConditionToken() {
127   return createAtomicBoolValue();
128 }
129 
130 void DataflowAnalysisContext::addFlowConditionConstraint(
131     AtomicBoolValue &Token, BoolValue &Constraint) {
132   auto Res = FlowConditionConstraints.try_emplace(&Token, &Constraint);
133   if (!Res.second) {
134     Res.first->second = &getOrCreateConjunction(*Res.first->second, Constraint);
135   }
136 }
137 
138 AtomicBoolValue &
139 DataflowAnalysisContext::forkFlowCondition(AtomicBoolValue &Token) {
140   auto &ForkToken = makeFlowConditionToken();
141   FlowConditionDeps[&ForkToken].insert(&Token);
142   addFlowConditionConstraint(ForkToken, Token);
143   return ForkToken;
144 }
145 
146 AtomicBoolValue &
147 DataflowAnalysisContext::joinFlowConditions(AtomicBoolValue &FirstToken,
148                                             AtomicBoolValue &SecondToken) {
149   auto &Token = makeFlowConditionToken();
150   FlowConditionDeps[&Token].insert(&FirstToken);
151   FlowConditionDeps[&Token].insert(&SecondToken);
152   addFlowConditionConstraint(Token,
153                              getOrCreateDisjunction(FirstToken, SecondToken));
154   return Token;
155 }
156 
157 Solver::Result
158 DataflowAnalysisContext::querySolver(llvm::DenseSet<BoolValue *> Constraints) {
159   Constraints.insert(&getBoolLiteralValue(true));
160   Constraints.insert(&getOrCreateNegation(getBoolLiteralValue(false)));
161   return S->solve(std::move(Constraints));
162 }
163 
164 bool DataflowAnalysisContext::flowConditionImplies(AtomicBoolValue &Token,
165                                                    BoolValue &Val) {
166   // Returns true if and only if truth assignment of the flow condition implies
167   // that `Val` is also true. We prove whether or not this property holds by
168   // reducing the problem to satisfiability checking. In other words, we attempt
169   // to show that assuming `Val` is false makes the constraints induced by the
170   // flow condition unsatisfiable.
171   llvm::DenseSet<BoolValue *> Constraints = {&Token, &getOrCreateNegation(Val)};
172   llvm::DenseSet<AtomicBoolValue *> VisitedTokens;
173   addTransitiveFlowConditionConstraints(Token, Constraints, VisitedTokens);
174   return isUnsatisfiable(std::move(Constraints));
175 }
176 
177 bool DataflowAnalysisContext::flowConditionIsTautology(AtomicBoolValue &Token) {
178   // Returns true if and only if we cannot prove that the flow condition can
179   // ever be false.
180   llvm::DenseSet<BoolValue *> Constraints = {&getOrCreateNegation(Token)};
181   llvm::DenseSet<AtomicBoolValue *> VisitedTokens;
182   addTransitiveFlowConditionConstraints(Token, Constraints, VisitedTokens);
183   return isUnsatisfiable(std::move(Constraints));
184 }
185 
186 bool DataflowAnalysisContext::equivalentBoolValues(BoolValue &Val1,
187                                                    BoolValue &Val2) {
188   llvm::DenseSet<BoolValue *> Constraints = {
189       &getOrCreateNegation(getOrCreateIff(Val1, Val2))};
190   return isUnsatisfiable(Constraints);
191 }
192 
193 void DataflowAnalysisContext::addTransitiveFlowConditionConstraints(
194     AtomicBoolValue &Token, llvm::DenseSet<BoolValue *> &Constraints,
195     llvm::DenseSet<AtomicBoolValue *> &VisitedTokens) {
196   auto Res = VisitedTokens.insert(&Token);
197   if (!Res.second)
198     return;
199 
200   auto ConstraintsIT = FlowConditionConstraints.find(&Token);
201   if (ConstraintsIT == FlowConditionConstraints.end()) {
202     Constraints.insert(&Token);
203   } else {
204     // Bind flow condition token via `iff` to its set of constraints:
205     // FC <=> (C1 ^ C2 ^ ...), where Ci are constraints
206     Constraints.insert(&getOrCreateIff(Token, *ConstraintsIT->second));
207   }
208 
209   auto DepsIT = FlowConditionDeps.find(&Token);
210   if (DepsIT != FlowConditionDeps.end()) {
211     for (AtomicBoolValue *DepToken : DepsIT->second) {
212       addTransitiveFlowConditionConstraints(*DepToken, Constraints,
213                                             VisitedTokens);
214     }
215   }
216 }
217 
218 BoolValue &DataflowAnalysisContext::substituteBoolValue(
219     BoolValue &Val,
220     llvm::DenseMap<BoolValue *, BoolValue *> &SubstitutionsCache) {
221   auto IT = SubstitutionsCache.find(&Val);
222   if (IT != SubstitutionsCache.end()) {
223     return *IT->second;
224   }
225   BoolValue *Result;
226   switch (Val.getKind()) {
227   case Value::Kind::AtomicBool: {
228     Result = &Val;
229     break;
230   }
231   case Value::Kind::Negation: {
232     auto &Negation = *cast<NegationValue>(&Val);
233     auto &Sub = substituteBoolValue(Negation.getSubVal(), SubstitutionsCache);
234     Result = &getOrCreateNegation(Sub);
235     break;
236   }
237   case Value::Kind::Disjunction: {
238     auto &Disjunct = *cast<DisjunctionValue>(&Val);
239     auto &LeftSub =
240         substituteBoolValue(Disjunct.getLeftSubValue(), SubstitutionsCache);
241     auto &RightSub =
242         substituteBoolValue(Disjunct.getRightSubValue(), SubstitutionsCache);
243     Result = &getOrCreateDisjunction(LeftSub, RightSub);
244     break;
245   }
246   case Value::Kind::Conjunction: {
247     auto &Conjunct = *cast<ConjunctionValue>(&Val);
248     auto &LeftSub =
249         substituteBoolValue(Conjunct.getLeftSubValue(), SubstitutionsCache);
250     auto &RightSub =
251         substituteBoolValue(Conjunct.getRightSubValue(), SubstitutionsCache);
252     Result = &getOrCreateConjunction(LeftSub, RightSub);
253     break;
254   }
255   default:
256     llvm_unreachable("Unhandled Value Kind");
257   }
258   SubstitutionsCache[&Val] = Result;
259   return *Result;
260 }
261 
262 BoolValue &DataflowAnalysisContext::buildAndSubstituteFlowCondition(
263     AtomicBoolValue &Token,
264     llvm::DenseMap<AtomicBoolValue *, BoolValue *> Substitutions) {
265   llvm::DenseMap<BoolValue *, BoolValue *> SubstitutionsCache(
266       Substitutions.begin(), Substitutions.end());
267   return buildAndSubstituteFlowConditionWithCache(Token, SubstitutionsCache);
268 }
269 
270 BoolValue &DataflowAnalysisContext::buildAndSubstituteFlowConditionWithCache(
271     AtomicBoolValue &Token,
272     llvm::DenseMap<BoolValue *, BoolValue *> &SubstitutionsCache) {
273   auto ConstraintsIT = FlowConditionConstraints.find(&Token);
274   if (ConstraintsIT == FlowConditionConstraints.end()) {
275     return getBoolLiteralValue(true);
276   }
277   auto DepsIT = FlowConditionDeps.find(&Token);
278   if (DepsIT != FlowConditionDeps.end()) {
279     for (AtomicBoolValue *DepToken : DepsIT->second) {
280       auto &NewDep = buildAndSubstituteFlowConditionWithCache(
281           *DepToken, SubstitutionsCache);
282       SubstitutionsCache[DepToken] = &NewDep;
283     }
284   }
285   return substituteBoolValue(*ConstraintsIT->second, SubstitutionsCache);
286 }
287 
288 } // namespace dataflow
289 } // namespace clang
290 
291 using namespace clang;
292 
293 const Expr &clang::dataflow::ignoreCFGOmittedNodes(const Expr &E) {
294   const Expr *Current = &E;
295   if (auto *EWC = dyn_cast<ExprWithCleanups>(Current)) {
296     Current = EWC->getSubExpr();
297     assert(Current != nullptr);
298   }
299   Current = Current->IgnoreParens();
300   assert(Current != nullptr);
301   return *Current;
302 }
303 
304 const Stmt &clang::dataflow::ignoreCFGOmittedNodes(const Stmt &S) {
305   if (auto *E = dyn_cast<Expr>(&S))
306     return ignoreCFGOmittedNodes(*E);
307   return S;
308 }
309 
310 // FIXME: Does not precisely handle non-virtual diamond inheritance. A single
311 // field decl will be modeled for all instances of the inherited field.
312 static void
313 getFieldsFromClassHierarchy(QualType Type,
314                             llvm::DenseSet<const FieldDecl *> &Fields) {
315   if (Type->isIncompleteType() || Type->isDependentType() ||
316       !Type->isRecordType())
317     return;
318 
319   for (const FieldDecl *Field : Type->getAsRecordDecl()->fields())
320     Fields.insert(Field);
321   if (auto *CXXRecord = Type->getAsCXXRecordDecl())
322     for (const CXXBaseSpecifier &Base : CXXRecord->bases())
323       getFieldsFromClassHierarchy(Base.getType(), Fields);
324 }
325 
326 /// Gets the set of all fields in the type.
327 llvm::DenseSet<const FieldDecl *>
328 clang::dataflow::getObjectFields(QualType Type) {
329   llvm::DenseSet<const FieldDecl *> Fields;
330   getFieldsFromClassHierarchy(Type, Fields);
331   return Fields;
332 }
333