clang 17.0.0git
TypeErasedDataflowAnalysis.cpp
Go to the documentation of this file.
1//===- TypeErasedDataflowAnalysis.cpp -------------------------------------===//
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 type-erased base types and functions for building dataflow
10// analyses that run over Control-Flow Graphs (CFGs).
11//
12//===----------------------------------------------------------------------===//
13
14#include <algorithm>
15#include <memory>
16#include <optional>
17#include <system_error>
18#include <utility>
19#include <vector>
20
21#include "clang/AST/DeclCXX.h"
25#include "clang/Analysis/CFG.h"
32#include "llvm/ADT/ArrayRef.h"
33#include "llvm/ADT/DenseSet.h"
34#include "llvm/ADT/STLExtras.h"
35#include "llvm/Support/Debug.h"
36#include "llvm/Support/Error.h"
37
38#define DEBUG_TYPE "clang-dataflow"
39
40namespace clang {
41namespace dataflow {
42
43/// Returns the index of `Block` in the successors of `Pred`.
44static int blockIndexInPredecessor(const CFGBlock &Pred,
45 const CFGBlock &Block) {
46 auto BlockPos = llvm::find_if(
47 Pred.succs(), [&Block](const CFGBlock::AdjacentBlock &Succ) {
48 return Succ && Succ->getBlockID() == Block.getBlockID();
49 });
50 return BlockPos - Pred.succ_begin();
51}
52
53static bool isLoopHead(const CFGBlock &B) {
54 if (const auto *T = B.getTerminatorStmt())
55 switch (T->getStmtClass()) {
56 case Stmt::WhileStmtClass:
57 case Stmt::DoStmtClass:
58 case Stmt::ForStmtClass:
59 return true;
60 default:
61 return false;
62 }
63
64 return false;
65}
66
67namespace {
68
69// The return type of the visit functions in TerminatorVisitor. The first
70// element represents the terminator expression (that is the conditional
71// expression in case of a path split in the CFG). The second element
72// represents whether the condition was true or false.
73using TerminatorVisitorRetTy = std::pair<const Expr *, bool>;
74
75/// Extends the flow condition of an environment based on a terminator
76/// statement.
77class TerminatorVisitor
78 : public ConstStmtVisitor<TerminatorVisitor, TerminatorVisitorRetTy> {
79public:
80 TerminatorVisitor(const StmtToEnvMap &StmtToEnv, Environment &Env,
81 int BlockSuccIdx)
82 : StmtToEnv(StmtToEnv), Env(Env), BlockSuccIdx(BlockSuccIdx) {}
83
84 TerminatorVisitorRetTy VisitIfStmt(const IfStmt *S) {
85 auto *Cond = S->getCond();
86 assert(Cond != nullptr);
87 return extendFlowCondition(*Cond);
88 }
89
90 TerminatorVisitorRetTy VisitWhileStmt(const WhileStmt *S) {
91 auto *Cond = S->getCond();
92 assert(Cond != nullptr);
93 return extendFlowCondition(*Cond);
94 }
95
96 TerminatorVisitorRetTy VisitDoStmt(const DoStmt *S) {
97 auto *Cond = S->getCond();
98 assert(Cond != nullptr);
99 return extendFlowCondition(*Cond);
100 }
101
102 TerminatorVisitorRetTy VisitForStmt(const ForStmt *S) {
103 auto *Cond = S->getCond();
104 if (Cond != nullptr)
105 return extendFlowCondition(*Cond);
106 return {nullptr, false};
107 }
108
109 TerminatorVisitorRetTy VisitBinaryOperator(const BinaryOperator *S) {
110 assert(S->getOpcode() == BO_LAnd || S->getOpcode() == BO_LOr);
111 auto *LHS = S->getLHS();
112 assert(LHS != nullptr);
113 return extendFlowCondition(*LHS);
114 }
115
116 TerminatorVisitorRetTy
117 VisitConditionalOperator(const ConditionalOperator *S) {
118 auto *Cond = S->getCond();
119 assert(Cond != nullptr);
120 return extendFlowCondition(*Cond);
121 }
122
123private:
124 TerminatorVisitorRetTy extendFlowCondition(const Expr &Cond) {
125 // The terminator sub-expression might not be evaluated.
126 if (Env.getValueStrict(Cond) == nullptr)
127 transfer(StmtToEnv, Cond, Env);
128
129 auto *Val = cast_or_null<BoolValue>(Env.getValueStrict(Cond));
130 // Value merging depends on flow conditions from different environments
131 // being mutually exclusive -- that is, they cannot both be true in their
132 // entirety (even if they may share some clauses). So, we need *some* value
133 // for the condition expression, even if just an atom.
134 if (Val == nullptr) {
135 Val = &Env.makeAtomicBoolValue();
136 Env.setValueStrict(Cond, *Val);
137 }
138
139 bool ConditionValue = true;
140 // The condition must be inverted for the successor that encompasses the
141 // "else" branch, if such exists.
142 if (BlockSuccIdx == 1) {
143 Val = &Env.makeNot(*Val);
144 ConditionValue = false;
145 }
146
147 Env.addToFlowCondition(*Val);
148 return {&Cond, ConditionValue};
149 }
150
151 const StmtToEnvMap &StmtToEnv;
152 Environment &Env;
153 int BlockSuccIdx;
154};
155
156/// Holds data structures required for running dataflow analysis.
157struct AnalysisContext {
158 AnalysisContext(const ControlFlowContext &CFCtx,
159 TypeErasedDataflowAnalysis &Analysis,
160 const Environment &InitEnv,
161 llvm::ArrayRef<std::optional<TypeErasedDataflowAnalysisState>>
162 BlockStates)
164 Log(*InitEnv.getDataflowAnalysisContext().getOptions().Log),
166 Log.beginAnalysis(CFCtx, Analysis);
167 }
168 ~AnalysisContext() { Log.endAnalysis(); }
169
170 /// Contains the CFG being analyzed.
171 const ControlFlowContext &CFCtx;
172 /// The analysis to be run.
173 TypeErasedDataflowAnalysis &Analysis;
174 /// Initial state to start the analysis.
175 const Environment &InitEnv;
176 Logger &Log;
177 /// Stores the state of a CFG block if it has been evaluated by the analysis.
178 /// The indices correspond to the block IDs.
180};
181
182} // namespace
183
184/// Computes the input state for a given basic block by joining the output
185/// states of its predecessors.
186///
187/// Requirements:
188///
189/// All predecessors of `Block` except those with loop back edges must have
190/// already been transferred. States in `AC.BlockStates` that are set to
191/// `std::nullopt` represent basic blocks that are not evaluated yet.
192static TypeErasedDataflowAnalysisState
193computeBlockInputState(const CFGBlock &Block, AnalysisContext &AC) {
195 Preds.insert(Block.pred_begin(), Block.pred_end());
196 if (Block.getTerminator().isTemporaryDtorsBranch()) {
197 // This handles a special case where the code that produced the CFG includes
198 // a conditional operator with a branch that constructs a temporary and
199 // calls a destructor annotated as noreturn. The CFG models this as follows:
200 //
201 // B1 (contains the condition of the conditional operator) - succs: B2, B3
202 // B2 (contains code that does not call a noreturn destructor) - succs: B4
203 // B3 (contains code that calls a noreturn destructor) - succs: B4
204 // B4 (has temporary destructor terminator) - succs: B5, B6
205 // B5 (noreturn block that is associated with the noreturn destructor call)
206 // B6 (contains code that follows the conditional operator statement)
207 //
208 // The first successor (B5 above) of a basic block with a temporary
209 // destructor terminator (B4 above) is the block that evaluates the
210 // destructor. If that block has a noreturn element then the predecessor
211 // block that constructed the temporary object (B3 above) is effectively a
212 // noreturn block and its state should not be used as input for the state
213 // of the block that has a temporary destructor terminator (B4 above). This
214 // holds regardless of which branch of the ternary operator calls the
215 // noreturn destructor. However, it doesn't cases where a nested ternary
216 // operator includes a branch that contains a noreturn destructor call.
217 //
218 // See `NoreturnDestructorTest` for concrete examples.
219 if (Block.succ_begin()->getReachableBlock() != nullptr &&
220 Block.succ_begin()->getReachableBlock()->hasNoReturnElement()) {
221 auto &StmtToBlock = AC.CFCtx.getStmtToBlock();
222 auto StmtBlock = StmtToBlock.find(Block.getTerminatorStmt());
223 assert(StmtBlock != StmtToBlock.end());
224 Preds.erase(StmtBlock->getSecond());
225 }
226 }
227
228 std::optional<TypeErasedDataflowAnalysisState> MaybeState;
229
230 auto &Analysis = AC.Analysis;
231 for (const CFGBlock *Pred : Preds) {
232 // Skip if the `Block` is unreachable or control flow cannot get past it.
233 if (!Pred || Pred->hasNoReturnElement())
234 continue;
235
236 // Skip if `Pred` was not evaluated yet. This could happen if `Pred` has a
237 // loop back edge to `Block`.
238 const std::optional<TypeErasedDataflowAnalysisState> &MaybePredState =
239 AC.BlockStates[Pred->getBlockID()];
240 if (!MaybePredState)
241 continue;
242
243 TypeErasedDataflowAnalysisState PredState = *MaybePredState;
244 if (Analysis.builtinOptions()) {
245 if (const Stmt *PredTerminatorStmt = Pred->getTerminatorStmt()) {
246 const StmtToEnvMap StmtToEnv(AC.CFCtx, AC.BlockStates);
247 auto [Cond, CondValue] =
248 TerminatorVisitor(StmtToEnv, PredState.Env,
250 .Visit(PredTerminatorStmt);
251 if (Cond != nullptr)
252 // FIXME: Call transferBranchTypeErased even if BuiltinTransferOpts
253 // are not set.
254 Analysis.transferBranchTypeErased(CondValue, Cond, PredState.Lattice,
255 PredState.Env);
256 }
257 }
258
259 if (MaybeState) {
260 Analysis.joinTypeErased(MaybeState->Lattice, PredState.Lattice);
261 MaybeState->Env.join(PredState.Env, Analysis);
262 } else {
263 MaybeState = std::move(PredState);
264 }
265 }
266 if (!MaybeState) {
267 // FIXME: Consider passing `Block` to `Analysis.typeErasedInitialElement()`
268 // to enable building analyses like computation of dominators that
269 // initialize the state of each basic block differently.
270 MaybeState.emplace(Analysis.typeErasedInitialElement(), AC.InitEnv);
271 }
272 return *MaybeState;
273}
274
275/// Built-in transfer function for `CFGStmt`.
276static void
279 AnalysisContext &AC) {
280 const Stmt *S = Elt.getStmt();
281 assert(S != nullptr);
282 transfer(StmtToEnvMap(AC.CFCtx, AC.BlockStates), *S, InputState.Env);
283}
284
285/// Built-in transfer function for `CFGInitializer`.
286static void
289 const CXXCtorInitializer *Init = Elt.getInitializer();
290 assert(Init != nullptr);
291
292 auto &Env = InputState.Env;
293 const auto &ThisLoc =
294 *cast<AggregateStorageLocation>(Env.getThisPointeeStorageLocation());
295
296 const FieldDecl *Member = Init->getMember();
297 if (Member == nullptr)
298 // Not a field initializer.
299 return;
300
301 auto *InitStmt = Init->getInit();
302 assert(InitStmt != nullptr);
303
304 if (Member->getType()->isReferenceType()) {
305 auto *InitStmtLoc = Env.getStorageLocationStrict(*InitStmt);
306 if (InitStmtLoc == nullptr)
307 return;
308
309 auto &MemberLoc = ThisLoc.getChild(*Member);
310 Env.setValue(MemberLoc, Env.create<ReferenceValue>(*InitStmtLoc));
311 } else if (auto *InitStmtVal = Env.getValueStrict(*InitStmt)) {
312 auto &MemberLoc = ThisLoc.getChild(*Member);
313 Env.setValue(MemberLoc, *InitStmtVal);
314 }
315}
316
317static void builtinTransfer(const CFGElement &Elt,
319 AnalysisContext &AC) {
320 switch (Elt.getKind()) {
322 builtinTransferStatement(Elt.castAs<CFGStmt>(), State, AC);
323 break;
326 break;
327 default:
328 // FIXME: Evaluate other kinds of `CFGElement`, including:
329 // - When encountering `CFGLifetimeEnds`, remove the declaration from
330 // `Environment::DeclToLoc`. This would serve two purposes:
331 // a) Eliminate unnecessary clutter from `Environment::DeclToLoc`
332 // b) Allow us to implement an assertion that, when joining two
333 // `Environments`, the two `DeclToLoc` maps never contain entries that
334 // map the same declaration to different storage locations.
335 // Unfortunately, however, we can't currently process `CFGLifetimeEnds`
336 // because the corresponding CFG option `AddLifetime` is incompatible with
337 // the option 'AddImplicitDtors`, which we already use. We will first
338 // need to modify the CFG implementation to make these two options
339 // compatible before we can process `CFGLifetimeEnds`.
340 break;
341 }
342}
343
344/// Transfers `State` by evaluating each element in the `Block` based on the
345/// `AC.Analysis` specified.
346///
347/// Built-in transfer functions (if the option for `ApplyBuiltinTransfer` is set
348/// by the analysis) will be applied to the element before evaluation by the
349/// user-specified analysis.
350/// `PostVisitCFG` (if provided) will be applied to the element after evaluation
351/// by the user-specified analysis.
352static TypeErasedDataflowAnalysisState
353transferCFGBlock(const CFGBlock &Block, AnalysisContext &AC,
354 std::function<void(const CFGElement &,
356 PostVisitCFG = nullptr) {
357 AC.Log.enterBlock(Block);
358 auto State = computeBlockInputState(Block, AC);
359 AC.Log.recordState(State);
360 for (const auto &Element : Block) {
361 AC.Log.enterElement(Element);
362 // Built-in analysis
363 if (AC.Analysis.builtinOptions()) {
364 builtinTransfer(Element, State, AC);
365 }
366
367 // User-provided analysis
368 AC.Analysis.transferTypeErased(Element, State.Lattice, State.Env);
369
370 // Post processing
371 if (PostVisitCFG) {
372 PostVisitCFG(Element, State);
373 }
374 AC.Log.recordState(State);
375 }
376 return State;
377}
378
381 llvm::ArrayRef<std::optional<TypeErasedDataflowAnalysisState>> BlockStates,
382 const CFGBlock &Block, const Environment &InitEnv,
384 std::function<void(const CFGElement &,
386 PostVisitCFG) {
387 AnalysisContext AC(CFCtx, Analysis, InitEnv, BlockStates);
388 return transferCFGBlock(Block, AC, PostVisitCFG);
389}
390
394 const Environment &InitEnv,
395 std::function<void(const CFGElement &,
397 PostVisitCFG) {
399 ForwardDataflowWorklist Worklist(CFCtx.getCFG(), &POV);
400
401 std::vector<std::optional<TypeErasedDataflowAnalysisState>> BlockStates(
402 CFCtx.getCFG().size(), std::nullopt);
403
404 // The entry basic block doesn't contain statements so it can be skipped.
405 const CFGBlock &Entry = CFCtx.getCFG().getEntry();
407 InitEnv};
408 Worklist.enqueueSuccessors(&Entry);
409
410 AnalysisContext AC(CFCtx, Analysis, InitEnv, BlockStates);
411
412 // Bugs in lattices and transfer functions can prevent the analysis from
413 // converging. To limit the damage (infinite loops) that these bugs can cause,
414 // limit the number of iterations.
415 // FIXME: Consider making the maximum number of iterations configurable.
416 // FIXME: Consider restricting the number of backedges followed, rather than
417 // iterations.
418 // FIXME: Set up statistics (see llvm/ADT/Statistic.h) to count average number
419 // of iterations, number of functions that time out, etc.
420 static constexpr uint32_t MaxAverageVisitsPerBlock = 4;
421 static constexpr uint32_t AbsoluteMaxIterations = 1 << 16;
422 const uint32_t RelativeMaxIterations =
423 MaxAverageVisitsPerBlock * BlockStates.size();
424 const uint32_t MaxIterations =
425 std::min(RelativeMaxIterations, AbsoluteMaxIterations);
426 uint32_t Iterations = 0;
427 while (const CFGBlock *Block = Worklist.dequeue()) {
428 LLVM_DEBUG(llvm::dbgs()
429 << "Processing Block " << Block->getBlockID() << "\n");
430 if (++Iterations > MaxIterations) {
431 return llvm::createStringError(std::errc::timed_out,
432 "maximum number of iterations reached");
433 }
434
435 const std::optional<TypeErasedDataflowAnalysisState> &OldBlockState =
436 BlockStates[Block->getBlockID()];
437 TypeErasedDataflowAnalysisState NewBlockState =
439 LLVM_DEBUG({
440 llvm::errs() << "New Env:\n";
441 NewBlockState.Env.dump();
442 });
443
444 if (OldBlockState) {
445 LLVM_DEBUG({
446 llvm::errs() << "Old Env:\n";
447 OldBlockState->Env.dump();
448 });
449 if (isLoopHead(*Block)) {
451 NewBlockState.Lattice, OldBlockState->Lattice);
452 LatticeJoinEffect Effect2 =
453 NewBlockState.Env.widen(OldBlockState->Env, Analysis);
454 if (Effect1 == LatticeJoinEffect::Unchanged &&
455 Effect2 == LatticeJoinEffect::Unchanged) {
456 // The state of `Block` didn't change from widening so there's no need
457 // to revisit its successors.
458 AC.Log.blockConverged();
459 continue;
460 }
461 } else if (Analysis.isEqualTypeErased(OldBlockState->Lattice,
462 NewBlockState.Lattice) &&
463 OldBlockState->Env.equivalentTo(NewBlockState.Env, Analysis)) {
464 // The state of `Block` didn't change after transfer so there's no need
465 // to revisit its successors.
466 AC.Log.blockConverged();
467 continue;
468 }
469 }
470
471 BlockStates[Block->getBlockID()] = std::move(NewBlockState);
472
473 // Do not add unreachable successor blocks to `Worklist`.
474 if (Block->hasNoReturnElement())
475 continue;
476
477 Worklist.enqueueSuccessors(Block);
478 }
479 // FIXME: Consider evaluating unreachable basic blocks (those that have a
480 // state set to `std::nullopt` at this point) to also analyze dead code.
481
482 if (PostVisitCFG) {
483 for (const CFGBlock *Block : CFCtx.getCFG()) {
484 // Skip blocks that were not evaluated.
485 if (!BlockStates[Block->getBlockID()])
486 continue;
487 transferCFGBlock(*Block, AC, PostVisitCFG);
488 }
489 }
490
491 return BlockStates;
492}
493
494} // namespace dataflow
495} // namespace clang
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
const Environment & Env
Definition: HTMLLogger.cpp:170
llvm::ArrayRef< std::optional< TypeErasedDataflowAnalysisState > > BlockStates
Stores the state of a CFG block if it has been evaluated by the analysis.
const ControlFlowContext & CFCtx
Contains the CFG being analyzed.
const Environment & InitEnv
Initial state to start the analysis.
TypeErasedDataflowAnalysis & Analysis
The analysis to be run.
This class represents a potential adjacent block in the CFG.
Definition: CFG.h:791
Represents a single basic block in a source-level CFG.
Definition: CFG.h:576
succ_range succs()
Definition: CFG.h:965
succ_iterator succ_begin()
Definition: CFG.h:955
Stmt * getTerminatorStmt()
Definition: CFG.h:1050
unsigned getBlockID() const
Definition: CFG.h:1074
Represents a top-level expression in a basic block.
Definition: CFG.h:54
T castAs() const
Convert to the specified CFGElement type, asserting that this CFGElement is of the desired type.
Definition: CFG.h:97
Kind getKind() const
Definition: CFG.h:116
Represents C++ base or member initializer from constructor's initialization list.
Definition: CFG.h:225
CXXCtorInitializer * getInitializer() const
Definition: CFG.h:230
const Stmt * getStmt() const
Definition: CFG.h:136
unsigned size() const
Return the total number of CFGBlocks within the CFG This is simply a renaming of the getNumBlockIDs()...
Definition: CFG.h:1416
CFGBlock & getEntry()
Definition: CFG.h:1331
Represents a C++ base or member initializer.
Definition: DeclCXX.h:2259
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
Definition: StmtVisitor.h:194
const CFGBlock * dequeue()
Represents a member of a struct/union/class.
Definition: Decl.h:2945
Stmt - This represents one statement.
Definition: Stmt.h:72
Holds CFG and other derived context that is needed to perform dataflow analysis.
const CFG & getCFG() const
Returns the CFG that is stored in this context.
Holds the state of the program (store and heap) at a given program point.
LatticeJoinEffect widen(const Environment &PrevEnv, Environment::ValueModel &Model)
Widens the environment point-wise, using PrevEnv as needed to inform the approximation.
LLVM_DUMP_METHOD void dump() const
StorageLocation * getThisPointeeStorageLocation() const
Returns the storage location assigned to the this pointee in the environment or null if the this poin...
StorageLocation * getStorageLocationStrict(const Expr &E) const
Returns the storage location assigned to the glvalue E in the environment, or null if E isn't assigne...
Value * getValueStrict(const Expr &E) const
Returns the Value assigned to the prvalue E in the environment, or null if E isn't assigned a value i...
void setValue(const StorageLocation &Loc, Value &Val)
Assigns Val as the value of Loc in the environment.
std::enable_if_t< std::is_base_of< Value, T >::value, T & > create(Args &&...args)
Creates a T (some subclass of Value), forwarding args to the constructor, and returns a reference to ...
Models a dereferenced pointer.
Definition: Value.h:258
Maps statements to the environments of basic blocks that contain them.
Definition: Transfer.h:26
Type-erased base class for dataflow analyses built on a single lattice type.
const std::optional< DataflowAnalysisContext::Options > & builtinOptions() const
If the built-in model is enabled, returns the options to be passed to them.
virtual void transferBranchTypeErased(bool Branch, const Stmt *, TypeErasedLattice &, Environment &)=0
Applies the analysis transfer function for a given edge from a CFG block of a conditional statement.
virtual LatticeJoinEffect widenTypeErased(TypeErasedLattice &Current, const TypeErasedLattice &Previous)=0
Chooses a lattice element that approximates the current element at a program point,...
virtual TypeErasedLattice typeErasedInitialElement()=0
Returns a type-erased lattice element that models the initial state of a basic block.
virtual LatticeJoinEffect joinTypeErased(TypeErasedLattice &, const TypeErasedLattice &)=0
Joins two type-erased lattice elements by computing their least upper bound.
virtual bool isEqualTypeErased(const TypeErasedLattice &, const TypeErasedLattice &)=0
Returns true if and only if the two given type-erased lattice elements are equal.
llvm::Expected< std::vector< std::optional< TypeErasedDataflowAnalysisState > > > runTypeErasedDataflowAnalysis(const ControlFlowContext &CFCtx, TypeErasedDataflowAnalysis &Analysis, const Environment &InitEnv, std::function< void(const CFGElement &, const TypeErasedDataflowAnalysisState &)> PostVisitCFG=nullptr)
Performs dataflow analysis and returns a mapping from basic block IDs to dataflow analysis states tha...
static bool isLoopHead(const CFGBlock &B)
static TypeErasedDataflowAnalysisState computeBlockInputState(const CFGBlock &Block, AnalysisContext &AC)
Computes the input state for a given basic block by joining the output states of its predecessors.
TypeErasedDataflowAnalysisState transferBlock(const ControlFlowContext &CFCtx, llvm::ArrayRef< std::optional< TypeErasedDataflowAnalysisState > > BlockStates, const CFGBlock &Block, const Environment &InitEnv, TypeErasedDataflowAnalysis &Analysis, std::function< void(const CFGElement &, const TypeErasedDataflowAnalysisState &)> PostVisitCFG=nullptr)
Transfers the state of a basic block by evaluating each of its elements in the context of Analysis an...
static void builtinTransferInitializer(const CFGInitializer &Elt, TypeErasedDataflowAnalysisState &InputState)
Built-in transfer function for CFGInitializer.
static void builtinTransfer(const CFGElement &Elt, TypeErasedDataflowAnalysisState &State, AnalysisContext &AC)
static TypeErasedDataflowAnalysisState transferCFGBlock(const CFGBlock &Block, AnalysisContext &AC, std::function< void(const CFGElement &, const TypeErasedDataflowAnalysisState &)> PostVisitCFG=nullptr)
Transfers State by evaluating each element in the Block based on the AC.Analysis specified.
void transfer(const StmtToEnvMap &StmtToEnv, const Stmt &S, Environment &Env)
Evaluates S and updates Env accordingly.
Definition: Transfer.cpp:869
static int blockIndexInPredecessor(const CFGBlock &Pred, const CFGBlock &Block)
Returns the index of Block in the successors of Pred.
LatticeJoinEffect
Effect indicating whether a lattice join operation resulted in a new value.
static void builtinTransferStatement(const CFGStmt &Elt, TypeErasedDataflowAnalysisState &InputState, AnalysisContext &AC)
Built-in transfer function for CFGStmt.
A worklist implementation for forward dataflow analysis.
void enqueueSuccessors(const CFGBlock *Block)
Type-erased model of the program at a given program point.
TypeErasedLattice Lattice
Type-erased model of a program property.
Environment Env
Model of the state of the program (store and heap).