clang 17.0.0git
DataflowAnalysisContext.cpp
Go to the documentation of this file.
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
16#include "clang/AST/ExprCXX.h"
20#include "llvm/ADT/SetOperations.h"
21#include "llvm/Support/CommandLine.h"
22#include "llvm/Support/Debug.h"
23#include "llvm/Support/FileSystem.h"
24#include "llvm/Support/Path.h"
25#include <cassert>
26#include <memory>
27#include <utility>
28
29static llvm::cl::opt<std::string> DataflowLog(
30 "dataflow-log", llvm::cl::Hidden, llvm::cl::ValueOptional,
31 llvm::cl::desc("Emit log of dataflow analysis. With no arg, writes textual "
32 "log to stderr. With an arg, writes HTML logs under the "
33 "specified directory (one per analyzed function)."));
34
35namespace clang {
36namespace dataflow {
37
38void DataflowAnalysisContext::addModeledFields(
40 llvm::set_union(ModeledFields, Fields);
41}
42
44DataflowAnalysisContext::getReferencedFields(QualType Type) {
46 llvm::set_intersect(Fields, ModeledFields);
47 return Fields;
48}
49
51 if (!Type.isNull() && Type->isRecordType()) {
52 llvm::DenseMap<const ValueDecl *, StorageLocation *> FieldLocs;
53 // During context-sensitive analysis, a struct may be allocated in one
54 // function, but its field accessed in a function lower in the stack than
55 // the allocation. Since we only collect fields used in the function where
56 // the allocation occurs, we can't apply that filter when performing
57 // context-sensitive analysis. But, this only applies to storage locations,
58 // since field access it not allowed to fail. In contrast, field *values*
59 // don't need this allowance, since the API allows for uninitialized fields.
60 auto Fields = Opts.ContextSensitiveOpts ? getObjectFields(Type)
61 : getReferencedFields(Type);
62 for (const FieldDecl *Field : Fields)
63 FieldLocs.insert({Field, &createStorageLocation(Field->getType())});
64 return arena().create<AggregateStorageLocation>(Type, std::move(FieldLocs));
65 }
67}
68
71 if (auto *Loc = getStorageLocation(D))
72 return *Loc;
73 auto &Loc = createStorageLocation(D.getType());
74 setStorageLocation(D, Loc);
75 return Loc;
76}
77
80 if (auto *Loc = getStorageLocation(E))
81 return *Loc;
82 auto &Loc = createStorageLocation(E.getType());
83 setStorageLocation(E, Loc);
84 return Loc;
85}
86
89 auto CanonicalPointeeType =
90 PointeeType.isNull() ? PointeeType : PointeeType.getCanonicalType();
91 auto Res = NullPointerVals.try_emplace(CanonicalPointeeType, nullptr);
92 if (Res.second) {
93 auto &PointeeLoc = createStorageLocation(CanonicalPointeeType);
94 Res.first->second = &arena().create<PointerValue>(PointeeLoc);
95 }
96 return *Res.first->second;
97}
98
100 AtomicBoolValue &Token, BoolValue &Constraint) {
101 auto Res = FlowConditionConstraints.try_emplace(&Token, &Constraint);
102 if (!Res.second) {
103 Res.first->second =
104 &arena().makeAnd(*Res.first->second, Constraint);
105 }
106}
107
110 auto &ForkToken = arena().makeFlowConditionToken();
111 FlowConditionDeps[&ForkToken].insert(&Token);
113 return ForkToken;
114}
115
118 AtomicBoolValue &SecondToken) {
120 FlowConditionDeps[&Token].insert(&FirstToken);
121 FlowConditionDeps[&Token].insert(&SecondToken);
123 Token, arena().makeOr(FirstToken, SecondToken));
124 return Token;
125}
126
128DataflowAnalysisContext::querySolver(llvm::DenseSet<BoolValue *> Constraints) {
129 Constraints.insert(&arena().makeLiteral(true));
130 Constraints.insert(
131 &arena().makeNot(arena().makeLiteral(false)));
132 return S->solve(std::move(Constraints));
133}
134
136 BoolValue &Val) {
137 // Returns true if and only if truth assignment of the flow condition implies
138 // that `Val` is also true. We prove whether or not this property holds by
139 // reducing the problem to satisfiability checking. In other words, we attempt
140 // to show that assuming `Val` is false makes the constraints induced by the
141 // flow condition unsatisfiable.
142 llvm::DenseSet<BoolValue *> Constraints = {&Token,
143 &arena().makeNot(Val)};
145 addTransitiveFlowConditionConstraints(Token, Constraints, VisitedTokens);
146 return isUnsatisfiable(std::move(Constraints));
147}
148
150 // Returns true if and only if we cannot prove that the flow condition can
151 // ever be false.
152 llvm::DenseSet<BoolValue *> Constraints = {
153 &arena().makeNot(Token)};
155 addTransitiveFlowConditionConstraints(Token, Constraints, VisitedTokens);
156 return isUnsatisfiable(std::move(Constraints));
157}
158
160 BoolValue &Val2) {
161 llvm::DenseSet<BoolValue *> Constraints = {
162 &arena().makeNot(arena().makeEquals(Val1, Val2))};
163 return isUnsatisfiable(Constraints);
164}
165
166void DataflowAnalysisContext::addTransitiveFlowConditionConstraints(
168 llvm::DenseSet<AtomicBoolValue *> &VisitedTokens) {
169 auto Res = VisitedTokens.insert(&Token);
170 if (!Res.second)
171 return;
172
173 auto ConstraintsIt = FlowConditionConstraints.find(&Token);
174 if (ConstraintsIt == FlowConditionConstraints.end()) {
175 Constraints.insert(&Token);
176 } else {
177 // Bind flow condition token via `iff` to its set of constraints:
178 // FC <=> (C1 ^ C2 ^ ...), where Ci are constraints
179 Constraints.insert(&arena().makeEquals(Token, *ConstraintsIt->second));
180 }
181
182 auto DepsIt = FlowConditionDeps.find(&Token);
183 if (DepsIt != FlowConditionDeps.end()) {
184 for (AtomicBoolValue *DepToken : DepsIt->second) {
185 addTransitiveFlowConditionConstraints(*DepToken, Constraints,
186 VisitedTokens);
187 }
188 }
189}
190
192 llvm::raw_ostream &OS) {
193 llvm::DenseSet<BoolValue *> Constraints = {&Token};
195 addTransitiveFlowConditionConstraints(Token, Constraints, VisitedTokens);
196
197 llvm::DenseMap<const AtomicBoolValue *, std::string> AtomNames = {
198 {&arena().makeLiteral(false), "False"},
199 {&arena().makeLiteral(true), "True"}};
200 OS << debugString(Constraints, AtomNames);
201}
202
203const ControlFlowContext *
205 // Canonicalize the key:
206 F = F->getDefinition();
207 if (F == nullptr)
208 return nullptr;
209 auto It = FunctionContexts.find(F);
210 if (It != FunctionContexts.end())
211 return &It->second;
212
213 if (F->hasBody()) {
215 // FIXME: Handle errors.
216 assert(CFCtx);
217 auto Result = FunctionContexts.insert({F, std::move(*CFCtx)});
218 return &Result.first->second;
219 }
220
221 return nullptr;
222}
223
224static std::unique_ptr<Logger> makeLoggerFromCommandLine() {
225 if (DataflowLog.empty())
226 return Logger::textual(llvm::errs());
227
228 llvm::StringRef Dir = DataflowLog;
229 if (auto EC = llvm::sys::fs::create_directories(Dir))
230 llvm::errs() << "Failed to create log dir: " << EC.message() << "\n";
231 // All analysis runs within a process will log to the same directory.
232 // Share a counter so they don't all overwrite each other's 0.html.
233 // (Don't share a logger, it's not threadsafe).
234 static std::atomic<unsigned> Counter = {0};
235 auto StreamFactory =
236 [Dir(Dir.str())]() mutable -> std::unique_ptr<llvm::raw_ostream> {
238 llvm::sys::path::append(File,
239 std::to_string(Counter.fetch_add(1)) + ".html");
240 std::error_code EC;
241 auto OS = std::make_unique<llvm::raw_fd_ostream>(File, EC);
242 if (EC) {
243 llvm::errs() << "Failed to create log " << File << ": " << EC.message()
244 << "\n";
245 return std::make_unique<llvm::raw_null_ostream>();
246 }
247 return OS;
248 };
249 return Logger::html(std::move(StreamFactory));
250}
251
253 Options Opts)
254 : S(std::move(S)), A(std::make_unique<Arena>()), Opts(Opts) {
255 assert(this->S != nullptr);
256 // If the -dataflow-log command-line flag was set, synthesize a logger.
257 // This is ugly but provides a uniform method for ad-hoc debugging dataflow-
258 // based tools.
259 if (Opts.Log == nullptr) {
260 if (DataflowLog.getNumOccurrences() > 0) {
261 LogOwner = makeLoggerFromCommandLine();
262 this->Opts.Log = LogOwner.get();
263 // FIXME: if the flag is given a value, write an HTML log to a file.
264 } else {
265 this->Opts.Log = &Logger::null();
266 }
267 }
268}
269
271
272} // namespace dataflow
273} // namespace clang
274
275using namespace clang;
276
278 const Expr *Current = &E;
279 if (auto *EWC = dyn_cast<ExprWithCleanups>(Current)) {
280 Current = EWC->getSubExpr();
281 assert(Current != nullptr);
282 }
283 Current = Current->IgnoreParens();
284 assert(Current != nullptr);
285 return *Current;
286}
287
289 if (auto *E = dyn_cast<Expr>(&S))
290 return ignoreCFGOmittedNodes(*E);
291 return S;
292}
293
294// FIXME: Does not precisely handle non-virtual diamond inheritance. A single
295// field decl will be modeled for all instances of the inherited field.
296static void
300 !Type->isRecordType())
301 return;
302
303 for (const FieldDecl *Field : Type->getAsRecordDecl()->fields())
304 Fields.insert(Field);
305 if (auto *CXXRecord = Type->getAsCXXRecordDecl())
306 for (const CXXBaseSpecifier &Base : CXXRecord->bases())
307 getFieldsFromClassHierarchy(Base.getType(), Fields);
308}
309
310/// Gets the set of all fields in the type.
315 return Fields;
316}
static llvm::cl::opt< std::string > DataflowLog("dataflow-log", llvm::cl::Hidden, llvm::cl::ValueOptional, llvm::cl::desc("Emit log of dataflow analysis. With no arg, writes textual " "log to stderr. With an arg, writes HTML logs under the " "specified directory (one per analyzed function)."))
static void getFieldsFromClassHierarchy(QualType Type, llvm::DenseSet< const FieldDecl * > &Fields)
Defines the clang::Expr interface and subclasses for C++ expressions.
const ControlFlowContext & CFCtx
Contains the CFG being analyzed.
Represents a base class of a C++ class.
Definition: DeclCXX.h:146
This represents one expression.
Definition: Expr.h:110
QualType getType() const
Definition: Expr.h:142
Represents a member of a struct/union/class.
Definition: Decl.h:2945
Represents a function declaration or definition.
Definition: Decl.h:1917
FunctionDecl * getDefinition()
Get the definition for this declaration.
Definition: Decl.h:2184
bool hasBody(const FunctionDecl *&Definition) const
Returns true if the function has a body.
Definition: Decl.cpp:3058
A (possibly-)qualified type.
Definition: Type.h:736
bool isNull() const
Return true if this QualType doesn't point to a type yet.
Definition: Type.h:803
QualType getCanonicalType() const
Definition: Type.h:6716
field_range fields() const
Definition: Decl.h:4239
Stmt - This represents one statement.
Definition: Stmt.h:72
Token - This structure provides full information about a lexed token.
Definition: Token.h:35
The base class of the type hierarchy.
Definition: Type.h:1568
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
Definition: Type.cpp:1799
bool isDependentType() const
Whether this type is a dependent type, meaning that its definition somehow depends on a template para...
Definition: Type.h:2329
bool isIncompleteType(NamedDecl **Def=nullptr) const
Types are partitioned into 3 broad categories (C99 6.2.5p1): object types, function types,...
Definition: Type.cpp:2279
bool isRecordType() const
Definition: Type.h:7006
RecordDecl * getAsRecordDecl() const
Retrieves the RecordDecl this type refers to.
Definition: Type.cpp:1803
QualType getType() const
Definition: Decl.h:712
Represents a variable declaration or definition.
Definition: Decl.h:913
A storage location which is subdivided into smaller storage locations that can be traced independentl...
The Arena owns the objects that model data within an analysis.
Definition: Arena.h:19
AtomicBoolValue & makeFlowConditionToken()
Creates a fresh flow condition and returns a token that identifies it.
Definition: Arena.h:97
AtomicBoolValue & makeLiteral(bool Value) const
Returns a symbolic boolean value that models a boolean literal equal to Value.
Definition: Arena.h:89
BoolValue & makeNot(BoolValue &Val)
Returns a boolean value that represents the negation of Val.
Definition: Arena.cpp:43
std::enable_if_t< std::is_base_of< StorageLocation, T >::value, T & > create(Args &&...args)
Creates a T (some subclass of StorageLocation), forwarding args to the constructor,...
Definition: Arena.h:34
BoolValue & makeAnd(BoolValue &LHS, BoolValue &RHS)
Returns a boolean value that represents the conjunction of LHS and RHS.
Definition: Arena.cpp:21
Models an atomic boolean.
Definition: Value.h:125
Models a boolean.
Definition: Value.h:98
Holds CFG and other derived context that is needed to perform dataflow analysis.
static llvm::Expected< ControlFlowContext > build(const FunctionDecl &Func)
Builds a ControlFlowContext from a FunctionDecl.
StorageLocation & getStableStorageLocation(const VarDecl &D)
Returns a stable storage location for D.
StorageLocation * getStorageLocation(const ValueDecl &D) const
Returns the storage location assigned to D or null if D has no assigned storage location.
DataflowAnalysisContext(std::unique_ptr< Solver > S, Options Opts=Options{ std::nullopt, nullptr})
Constructs a dataflow analysis context.
bool equivalentBoolValues(BoolValue &Val1, BoolValue &Val2)
Returns true if Val1 is equivalent to Val2.
bool flowConditionImplies(AtomicBoolValue &Token, BoolValue &Val)
Returns true if and only if the constraints of the flow condition identified by Token imply that Val ...
void setStorageLocation(const ValueDecl &D, StorageLocation &Loc)
Assigns Loc as the storage location of D.
bool flowConditionIsTautology(AtomicBoolValue &Token)
Returns true if and only if the constraints of the flow condition identified by Token are always true...
void addFlowConditionConstraint(AtomicBoolValue &Token, BoolValue &Constraint)
Adds Constraint to the flow condition identified by Token.
PointerValue & getOrCreateNullPointerValue(QualType PointeeType)
Returns a pointer value that represents a null pointer.
StorageLocation & createStorageLocation(QualType Type)
Returns a new storage location appropriate for Type.
AtomicBoolValue & joinFlowConditions(AtomicBoolValue &FirstToken, AtomicBoolValue &SecondToken)
Creates a new flow condition that represents the disjunction of the flow conditions identified by Fir...
LLVM_DUMP_METHOD void dumpFlowCondition(AtomicBoolValue &Token, llvm::raw_ostream &OS=llvm::dbgs())
AtomicBoolValue & forkFlowCondition(AtomicBoolValue &Token)
Creates a new flow condition with the same constraints as the flow condition identified by Token and ...
const ControlFlowContext * getControlFlowContext(const FunctionDecl *F)
Returns the ControlFlowContext registered for F, if any.
static std::unique_ptr< Logger > textual(llvm::raw_ostream &)
A logger that simply writes messages to the specified ostream in real time.
Definition: Logger.cpp:104
static std::unique_ptr< Logger > html(std::function< std::unique_ptr< llvm::raw_ostream >()>)
A logger that builds an HTML UI to inspect the analysis results.
Definition: HTMLLogger.cpp:554
static Logger & null()
Returns a dummy logger that does nothing.
Definition: Logger.cpp:16
Models a symbolic pointer. Specifically, any value of type T*.
Definition: Value.h:274
A storage location that is not subdivided further for the purposes of abstract interpretation.
Base class for elements of the local variable store and of the heap.
llvm::StringRef debugString(Value::Kind Kind)
Returns a string representation of a value kind.
llvm::DenseSet< const FieldDecl * > getObjectFields(QualType Type)
Returns the set of all fields in the type.
const Expr & ignoreCFGOmittedNodes(const Expr &E)
Skip past nodes that the CFG does not emit.
static std::unique_ptr< Logger > makeLoggerFromCommandLine()
@ Result
The result type of a method or function.
Definition: Format.h:4756
Logger * Log
If provided, analysis details will be recorded here.
std::optional< ContextSensitiveOptions > ContextSensitiveOpts
Options for analyzing function bodies when present in the translation unit, or empty to disable conte...