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