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/DebugSupport.h"
18 #include "clang/Analysis/FlowSensitive/Value.h"
19 #include "llvm/ADT/SetOperations.h"
20 #include "llvm/Support/Debug.h"
21 #include <cassert>
22 #include <memory>
23 #include <utility>
24
25 namespace clang {
26 namespace dataflow {
27
addModeledFields(const llvm::DenseSet<const FieldDecl * > & Fields)28 void DataflowAnalysisContext::addModeledFields(
29 const llvm::DenseSet<const FieldDecl *> &Fields) {
30 llvm::set_union(ModeledFields, Fields);
31 }
32
33 llvm::DenseSet<const FieldDecl *>
getReferencedFields(QualType Type)34 DataflowAnalysisContext::getReferencedFields(QualType Type) {
35 llvm::DenseSet<const FieldDecl *> Fields = getObjectFields(Type);
36 llvm::set_intersect(Fields, ModeledFields);
37 return Fields;
38 }
39
createStorageLocation(QualType Type)40 StorageLocation &DataflowAnalysisContext::createStorageLocation(QualType Type) {
41 if (!Type.isNull() &&
42 (Type->isStructureOrClassType() || Type->isUnionType())) {
43 llvm::DenseMap<const ValueDecl *, StorageLocation *> FieldLocs;
44 // During context-sensitive analysis, a struct may be allocated in one
45 // function, but its field accessed in a function lower in the stack than
46 // the allocation. Since we only collect fields used in the function where
47 // the allocation occurs, we can't apply that filter when performing
48 // context-sensitive analysis. But, this only applies to storage locations,
49 // since field access it not allowed to fail. In contrast, field *values*
50 // don't need this allowance, since the API allows for uninitialized fields.
51 auto Fields = Opts.ContextSensitiveOpts ? getObjectFields(Type)
52 : getReferencedFields(Type);
53 for (const FieldDecl *Field : Fields)
54 FieldLocs.insert({Field, &createStorageLocation(Field->getType())});
55 return takeOwnership(
56 std::make_unique<AggregateStorageLocation>(Type, std::move(FieldLocs)));
57 }
58 return takeOwnership(std::make_unique<ScalarStorageLocation>(Type));
59 }
60
61 StorageLocation &
getStableStorageLocation(const VarDecl & D)62 DataflowAnalysisContext::getStableStorageLocation(const VarDecl &D) {
63 if (auto *Loc = getStorageLocation(D))
64 return *Loc;
65 auto &Loc = createStorageLocation(D.getType());
66 setStorageLocation(D, Loc);
67 return Loc;
68 }
69
70 StorageLocation &
getStableStorageLocation(const Expr & E)71 DataflowAnalysisContext::getStableStorageLocation(const Expr &E) {
72 if (auto *Loc = getStorageLocation(E))
73 return *Loc;
74 auto &Loc = createStorageLocation(E.getType());
75 setStorageLocation(E, Loc);
76 return Loc;
77 }
78
79 PointerValue &
getOrCreateNullPointerValue(QualType PointeeType)80 DataflowAnalysisContext::getOrCreateNullPointerValue(QualType PointeeType) {
81 auto CanonicalPointeeType =
82 PointeeType.isNull() ? PointeeType : PointeeType.getCanonicalType();
83 auto Res = NullPointerVals.try_emplace(CanonicalPointeeType, nullptr);
84 if (Res.second) {
85 auto &PointeeLoc = createStorageLocation(CanonicalPointeeType);
86 Res.first->second =
87 &takeOwnership(std::make_unique<PointerValue>(PointeeLoc));
88 }
89 return *Res.first->second;
90 }
91
92 static std::pair<BoolValue *, BoolValue *>
makeCanonicalBoolValuePair(BoolValue & LHS,BoolValue & RHS)93 makeCanonicalBoolValuePair(BoolValue &LHS, BoolValue &RHS) {
94 auto Res = std::make_pair(&LHS, &RHS);
95 if (&RHS < &LHS)
96 std::swap(Res.first, Res.second);
97 return Res;
98 }
99
getOrCreateConjunction(BoolValue & LHS,BoolValue & RHS)100 BoolValue &DataflowAnalysisContext::getOrCreateConjunction(BoolValue &LHS,
101 BoolValue &RHS) {
102 if (&LHS == &RHS)
103 return LHS;
104
105 auto Res = ConjunctionVals.try_emplace(makeCanonicalBoolValuePair(LHS, RHS),
106 nullptr);
107 if (Res.second)
108 Res.first->second =
109 &takeOwnership(std::make_unique<ConjunctionValue>(LHS, RHS));
110 return *Res.first->second;
111 }
112
getOrCreateDisjunction(BoolValue & LHS,BoolValue & RHS)113 BoolValue &DataflowAnalysisContext::getOrCreateDisjunction(BoolValue &LHS,
114 BoolValue &RHS) {
115 if (&LHS == &RHS)
116 return LHS;
117
118 auto Res = DisjunctionVals.try_emplace(makeCanonicalBoolValuePair(LHS, RHS),
119 nullptr);
120 if (Res.second)
121 Res.first->second =
122 &takeOwnership(std::make_unique<DisjunctionValue>(LHS, RHS));
123 return *Res.first->second;
124 }
125
getOrCreateNegation(BoolValue & Val)126 BoolValue &DataflowAnalysisContext::getOrCreateNegation(BoolValue &Val) {
127 auto Res = NegationVals.try_emplace(&Val, nullptr);
128 if (Res.second)
129 Res.first->second = &takeOwnership(std::make_unique<NegationValue>(Val));
130 return *Res.first->second;
131 }
132
getOrCreateImplication(BoolValue & LHS,BoolValue & RHS)133 BoolValue &DataflowAnalysisContext::getOrCreateImplication(BoolValue &LHS,
134 BoolValue &RHS) {
135 if (&LHS == &RHS)
136 return getBoolLiteralValue(true);
137
138 auto Res = ImplicationVals.try_emplace(std::make_pair(&LHS, &RHS), nullptr);
139 if (Res.second)
140 Res.first->second =
141 &takeOwnership(std::make_unique<ImplicationValue>(LHS, RHS));
142 return *Res.first->second;
143 }
144
getOrCreateIff(BoolValue & LHS,BoolValue & RHS)145 BoolValue &DataflowAnalysisContext::getOrCreateIff(BoolValue &LHS,
146 BoolValue &RHS) {
147 if (&LHS == &RHS)
148 return getBoolLiteralValue(true);
149
150 auto Res = BiconditionalVals.try_emplace(makeCanonicalBoolValuePair(LHS, RHS),
151 nullptr);
152 if (Res.second)
153 Res.first->second =
154 &takeOwnership(std::make_unique<BiconditionalValue>(LHS, RHS));
155 return *Res.first->second;
156 }
157
makeFlowConditionToken()158 AtomicBoolValue &DataflowAnalysisContext::makeFlowConditionToken() {
159 return createAtomicBoolValue();
160 }
161
addFlowConditionConstraint(AtomicBoolValue & Token,BoolValue & Constraint)162 void DataflowAnalysisContext::addFlowConditionConstraint(
163 AtomicBoolValue &Token, BoolValue &Constraint) {
164 auto Res = FlowConditionConstraints.try_emplace(&Token, &Constraint);
165 if (!Res.second) {
166 Res.first->second = &getOrCreateConjunction(*Res.first->second, Constraint);
167 }
168 }
169
170 AtomicBoolValue &
forkFlowCondition(AtomicBoolValue & Token)171 DataflowAnalysisContext::forkFlowCondition(AtomicBoolValue &Token) {
172 auto &ForkToken = makeFlowConditionToken();
173 FlowConditionDeps[&ForkToken].insert(&Token);
174 addFlowConditionConstraint(ForkToken, Token);
175 return ForkToken;
176 }
177
178 AtomicBoolValue &
joinFlowConditions(AtomicBoolValue & FirstToken,AtomicBoolValue & SecondToken)179 DataflowAnalysisContext::joinFlowConditions(AtomicBoolValue &FirstToken,
180 AtomicBoolValue &SecondToken) {
181 auto &Token = makeFlowConditionToken();
182 FlowConditionDeps[&Token].insert(&FirstToken);
183 FlowConditionDeps[&Token].insert(&SecondToken);
184 addFlowConditionConstraint(Token,
185 getOrCreateDisjunction(FirstToken, SecondToken));
186 return Token;
187 }
188
189 Solver::Result
querySolver(llvm::DenseSet<BoolValue * > Constraints)190 DataflowAnalysisContext::querySolver(llvm::DenseSet<BoolValue *> Constraints) {
191 Constraints.insert(&getBoolLiteralValue(true));
192 Constraints.insert(&getOrCreateNegation(getBoolLiteralValue(false)));
193 return S->solve(std::move(Constraints));
194 }
195
flowConditionImplies(AtomicBoolValue & Token,BoolValue & Val)196 bool DataflowAnalysisContext::flowConditionImplies(AtomicBoolValue &Token,
197 BoolValue &Val) {
198 // Returns true if and only if truth assignment of the flow condition implies
199 // that `Val` is also true. We prove whether or not this property holds by
200 // reducing the problem to satisfiability checking. In other words, we attempt
201 // to show that assuming `Val` is false makes the constraints induced by the
202 // flow condition unsatisfiable.
203 llvm::DenseSet<BoolValue *> Constraints = {&Token, &getOrCreateNegation(Val)};
204 llvm::DenseSet<AtomicBoolValue *> VisitedTokens;
205 addTransitiveFlowConditionConstraints(Token, Constraints, VisitedTokens);
206 return isUnsatisfiable(std::move(Constraints));
207 }
208
flowConditionIsTautology(AtomicBoolValue & Token)209 bool DataflowAnalysisContext::flowConditionIsTautology(AtomicBoolValue &Token) {
210 // Returns true if and only if we cannot prove that the flow condition can
211 // ever be false.
212 llvm::DenseSet<BoolValue *> Constraints = {&getOrCreateNegation(Token)};
213 llvm::DenseSet<AtomicBoolValue *> VisitedTokens;
214 addTransitiveFlowConditionConstraints(Token, Constraints, VisitedTokens);
215 return isUnsatisfiable(std::move(Constraints));
216 }
217
equivalentBoolValues(BoolValue & Val1,BoolValue & Val2)218 bool DataflowAnalysisContext::equivalentBoolValues(BoolValue &Val1,
219 BoolValue &Val2) {
220 llvm::DenseSet<BoolValue *> Constraints = {
221 &getOrCreateNegation(getOrCreateIff(Val1, Val2))};
222 return isUnsatisfiable(Constraints);
223 }
224
addTransitiveFlowConditionConstraints(AtomicBoolValue & Token,llvm::DenseSet<BoolValue * > & Constraints,llvm::DenseSet<AtomicBoolValue * > & VisitedTokens)225 void DataflowAnalysisContext::addTransitiveFlowConditionConstraints(
226 AtomicBoolValue &Token, llvm::DenseSet<BoolValue *> &Constraints,
227 llvm::DenseSet<AtomicBoolValue *> &VisitedTokens) {
228 auto Res = VisitedTokens.insert(&Token);
229 if (!Res.second)
230 return;
231
232 auto ConstraintsIt = FlowConditionConstraints.find(&Token);
233 if (ConstraintsIt == FlowConditionConstraints.end()) {
234 Constraints.insert(&Token);
235 } else {
236 // Bind flow condition token via `iff` to its set of constraints:
237 // FC <=> (C1 ^ C2 ^ ...), where Ci are constraints
238 Constraints.insert(&getOrCreateIff(Token, *ConstraintsIt->second));
239 }
240
241 auto DepsIt = FlowConditionDeps.find(&Token);
242 if (DepsIt != FlowConditionDeps.end()) {
243 for (AtomicBoolValue *DepToken : DepsIt->second) {
244 addTransitiveFlowConditionConstraints(*DepToken, Constraints,
245 VisitedTokens);
246 }
247 }
248 }
249
substituteBoolValue(BoolValue & Val,llvm::DenseMap<BoolValue *,BoolValue * > & SubstitutionsCache)250 BoolValue &DataflowAnalysisContext::substituteBoolValue(
251 BoolValue &Val,
252 llvm::DenseMap<BoolValue *, BoolValue *> &SubstitutionsCache) {
253 auto It = SubstitutionsCache.find(&Val);
254 if (It != SubstitutionsCache.end()) {
255 // Return memoized result of substituting this boolean value.
256 return *It->second;
257 }
258
259 // Handle substitution on the boolean value (and its subvalues), saving the
260 // result into `SubstitutionsCache`.
261 BoolValue *Result;
262 switch (Val.getKind()) {
263 case Value::Kind::AtomicBool: {
264 Result = &Val;
265 break;
266 }
267 case Value::Kind::Negation: {
268 auto &Negation = *cast<NegationValue>(&Val);
269 auto &Sub = substituteBoolValue(Negation.getSubVal(), SubstitutionsCache);
270 Result = &getOrCreateNegation(Sub);
271 break;
272 }
273 case Value::Kind::Disjunction: {
274 auto &Disjunct = *cast<DisjunctionValue>(&Val);
275 auto &LeftSub =
276 substituteBoolValue(Disjunct.getLeftSubValue(), SubstitutionsCache);
277 auto &RightSub =
278 substituteBoolValue(Disjunct.getRightSubValue(), SubstitutionsCache);
279 Result = &getOrCreateDisjunction(LeftSub, RightSub);
280 break;
281 }
282 case Value::Kind::Conjunction: {
283 auto &Conjunct = *cast<ConjunctionValue>(&Val);
284 auto &LeftSub =
285 substituteBoolValue(Conjunct.getLeftSubValue(), SubstitutionsCache);
286 auto &RightSub =
287 substituteBoolValue(Conjunct.getRightSubValue(), SubstitutionsCache);
288 Result = &getOrCreateConjunction(LeftSub, RightSub);
289 break;
290 }
291 case Value::Kind::Implication: {
292 auto &IV = *cast<ImplicationValue>(&Val);
293 auto &LeftSub =
294 substituteBoolValue(IV.getLeftSubValue(), SubstitutionsCache);
295 auto &RightSub =
296 substituteBoolValue(IV.getRightSubValue(), SubstitutionsCache);
297 Result = &getOrCreateImplication(LeftSub, RightSub);
298 break;
299 }
300 case Value::Kind::Biconditional: {
301 auto &BV = *cast<BiconditionalValue>(&Val);
302 auto &LeftSub =
303 substituteBoolValue(BV.getLeftSubValue(), SubstitutionsCache);
304 auto &RightSub =
305 substituteBoolValue(BV.getRightSubValue(), SubstitutionsCache);
306 Result = &getOrCreateIff(LeftSub, RightSub);
307 break;
308 }
309 default:
310 llvm_unreachable("Unhandled Value Kind");
311 }
312 SubstitutionsCache[&Val] = Result;
313 return *Result;
314 }
315
buildAndSubstituteFlowCondition(AtomicBoolValue & Token,llvm::DenseMap<AtomicBoolValue *,BoolValue * > Substitutions)316 BoolValue &DataflowAnalysisContext::buildAndSubstituteFlowCondition(
317 AtomicBoolValue &Token,
318 llvm::DenseMap<AtomicBoolValue *, BoolValue *> Substitutions) {
319 assert(
320 Substitutions.find(&getBoolLiteralValue(true)) == Substitutions.end() &&
321 Substitutions.find(&getBoolLiteralValue(false)) == Substitutions.end() &&
322 "Do not substitute true/false boolean literals");
323 llvm::DenseMap<BoolValue *, BoolValue *> SubstitutionsCache(
324 Substitutions.begin(), Substitutions.end());
325 return buildAndSubstituteFlowConditionWithCache(Token, SubstitutionsCache);
326 }
327
buildAndSubstituteFlowConditionWithCache(AtomicBoolValue & Token,llvm::DenseMap<BoolValue *,BoolValue * > & SubstitutionsCache)328 BoolValue &DataflowAnalysisContext::buildAndSubstituteFlowConditionWithCache(
329 AtomicBoolValue &Token,
330 llvm::DenseMap<BoolValue *, BoolValue *> &SubstitutionsCache) {
331 auto ConstraintsIt = FlowConditionConstraints.find(&Token);
332 if (ConstraintsIt == FlowConditionConstraints.end()) {
333 return getBoolLiteralValue(true);
334 }
335 auto DepsIt = FlowConditionDeps.find(&Token);
336 if (DepsIt != FlowConditionDeps.end()) {
337 for (AtomicBoolValue *DepToken : DepsIt->second) {
338 auto &NewDep = buildAndSubstituteFlowConditionWithCache(
339 *DepToken, SubstitutionsCache);
340 SubstitutionsCache[DepToken] = &NewDep;
341 }
342 }
343 return substituteBoolValue(*ConstraintsIt->second, SubstitutionsCache);
344 }
345
dumpFlowCondition(AtomicBoolValue & Token)346 void DataflowAnalysisContext::dumpFlowCondition(AtomicBoolValue &Token) {
347 llvm::DenseSet<BoolValue *> Constraints = {&Token};
348 llvm::DenseSet<AtomicBoolValue *> VisitedTokens;
349 addTransitiveFlowConditionConstraints(Token, Constraints, VisitedTokens);
350
351 llvm::DenseMap<const AtomicBoolValue *, std::string> AtomNames = {
352 {&getBoolLiteralValue(false), "False"},
353 {&getBoolLiteralValue(true), "True"}};
354 llvm::dbgs() << debugString(Constraints, AtomNames);
355 }
356
357 const ControlFlowContext *
getControlFlowContext(const FunctionDecl * F)358 DataflowAnalysisContext::getControlFlowContext(const FunctionDecl *F) {
359 // Canonicalize the key:
360 F = F->getDefinition();
361 if (F == nullptr)
362 return nullptr;
363 auto It = FunctionContexts.find(F);
364 if (It != FunctionContexts.end())
365 return &It->second;
366
367 if (Stmt *Body = F->getBody()) {
368 auto CFCtx = ControlFlowContext::build(F, *Body, F->getASTContext());
369 // FIXME: Handle errors.
370 assert(CFCtx);
371 auto Result = FunctionContexts.insert({F, std::move(*CFCtx)});
372 return &Result.first->second;
373 }
374
375 return nullptr;
376 }
377
378 } // namespace dataflow
379 } // namespace clang
380
381 using namespace clang;
382
ignoreCFGOmittedNodes(const Expr & E)383 const Expr &clang::dataflow::ignoreCFGOmittedNodes(const Expr &E) {
384 const Expr *Current = &E;
385 if (auto *EWC = dyn_cast<ExprWithCleanups>(Current)) {
386 Current = EWC->getSubExpr();
387 assert(Current != nullptr);
388 }
389 Current = Current->IgnoreParens();
390 assert(Current != nullptr);
391 return *Current;
392 }
393
ignoreCFGOmittedNodes(const Stmt & S)394 const Stmt &clang::dataflow::ignoreCFGOmittedNodes(const Stmt &S) {
395 if (auto *E = dyn_cast<Expr>(&S))
396 return ignoreCFGOmittedNodes(*E);
397 return S;
398 }
399
400 // FIXME: Does not precisely handle non-virtual diamond inheritance. A single
401 // field decl will be modeled for all instances of the inherited field.
402 static void
getFieldsFromClassHierarchy(QualType Type,llvm::DenseSet<const FieldDecl * > & Fields)403 getFieldsFromClassHierarchy(QualType Type,
404 llvm::DenseSet<const FieldDecl *> &Fields) {
405 if (Type->isIncompleteType() || Type->isDependentType() ||
406 !Type->isRecordType())
407 return;
408
409 for (const FieldDecl *Field : Type->getAsRecordDecl()->fields())
410 Fields.insert(Field);
411 if (auto *CXXRecord = Type->getAsCXXRecordDecl())
412 for (const CXXBaseSpecifier &Base : CXXRecord->bases())
413 getFieldsFromClassHierarchy(Base.getType(), Fields);
414 }
415
416 /// Gets the set of all fields in the type.
417 llvm::DenseSet<const FieldDecl *>
getObjectFields(QualType Type)418 clang::dataflow::getObjectFields(QualType Type) {
419 llvm::DenseSet<const FieldDecl *> Fields;
420 getFieldsFromClassHierarchy(Type, Fields);
421 return Fields;
422 }
423