clang 22.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 <optional>
15#include <system_error>
16#include <utility>
17#include <vector>
18
19#include "clang/AST/ASTDumper.h"
20#include "clang/AST/DeclCXX.h"
22#include "clang/AST/StmtCXX.h"
25#include "clang/Analysis/CFG.h"
33#include "llvm/ADT/ArrayRef.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 {
42class NoopLattice;
43}
44} // namespace clang
45
46namespace llvm {
47// This needs to be exported for ClangAnalysisFlowSensitiveTests so any_cast
48// uses the correct address of Any::TypeId from the clang shared library instead
49// of creating one in the test executable. when building with
50// CLANG_LINK_CLANG_DYLIB
51template struct CLANG_EXPORT_TEMPLATE Any::TypeId<clang::dataflow::NoopLattice>;
52} // namespace llvm
53
54namespace clang {
55namespace dataflow {
56
57/// Returns the index of `Block` in the successors of `Pred`.
58static int blockIndexInPredecessor(const CFGBlock &Pred,
59 const CFGBlock &Block) {
60 auto BlockPos = llvm::find_if(
61 Pred.succs(), [&Block](const CFGBlock::AdjacentBlock &Succ) {
62 return Succ && Succ->getBlockID() == Block.getBlockID();
63 });
64 return BlockPos - Pred.succ_begin();
65}
66
67// A "backedge" node is a block introduced in the CFG exclusively to indicate a
68// loop backedge. They are exactly identified by the presence of a non-null
69// pointer to the entry block of the loop condition. Note that this is not
70// necessarily the block with the loop statement as terminator, because
71// short-circuit operators will result in multiple blocks encoding the loop
72// condition, only one of which will contain the loop statement as terminator.
73static bool isBackedgeNode(const CFGBlock &B) {
74 return B.getLoopTarget() != nullptr;
75}
76
77namespace {
78
79/// Extracts the terminator's condition expression.
80class TerminatorVisitor
82public:
83 TerminatorVisitor() = default;
84 const Expr *VisitIfStmt(const IfStmt *S) { return S->getCond(); }
85 const Expr *VisitWhileStmt(const WhileStmt *S) { return S->getCond(); }
86 const Expr *VisitDoStmt(const DoStmt *S) { return S->getCond(); }
87 const Expr *VisitForStmt(const ForStmt *S) { return S->getCond(); }
88 const Expr *VisitCXXForRangeStmt(const CXXForRangeStmt *) {
89 // Don't do anything special for CXXForRangeStmt, because the condition
90 // (being implicitly generated) isn't visible from the loop body.
91 return nullptr;
92 }
93 const Expr *VisitBinaryOperator(const BinaryOperator *S) {
94 assert(S->getOpcode() == BO_LAnd || S->getOpcode() == BO_LOr);
95 return S->getLHS();
96 }
97 const Expr *VisitConditionalOperator(const ConditionalOperator *S) {
98 return S->getCond();
99 }
100};
101
102/// Holds data structures required for running dataflow analysis.
103struct AnalysisContext {
104 AnalysisContext(const AdornedCFG &ACFG, TypeErasedDataflowAnalysis &Analysis,
105 const Environment &InitEnv,
106 llvm::ArrayRef<std::optional<TypeErasedDataflowAnalysisState>>
107 BlockStates)
108 : ACFG(ACFG), Analysis(Analysis), InitEnv(InitEnv),
109 Log(*InitEnv.getDataflowAnalysisContext().getOptions().Log),
110 BlockStates(BlockStates) {
111 Log.beginAnalysis(ACFG, Analysis);
112 }
113 ~AnalysisContext() { Log.endAnalysis(); }
114
115 /// Contains the CFG being analyzed.
116 const AdornedCFG &ACFG;
117 /// The analysis to be run.
118 TypeErasedDataflowAnalysis &Analysis;
119 /// Initial state to start the analysis.
120 const Environment &InitEnv;
121 Logger &Log;
122 /// Stores the state of a CFG block if it has been evaluated by the analysis.
123 /// The indices correspond to the block IDs.
125};
126
127class PrettyStackTraceAnalysis : public llvm::PrettyStackTraceEntry {
128public:
129 PrettyStackTraceAnalysis(const AdornedCFG &ACFG, const char *Message)
130 : ACFG(ACFG), Message(Message) {}
131
132 void print(raw_ostream &OS) const override {
133 OS << Message << "\n";
134 OS << "Decl:\n";
135 ACFG.getDecl().dump(OS);
136 OS << "CFG:\n";
137 ACFG.getCFG().print(OS, LangOptions(), false);
138 }
139
140private:
141 const AdornedCFG &ACFG;
142 const char *Message;
143};
144
145class PrettyStackTraceCFGElement : public llvm::PrettyStackTraceEntry {
146public:
147 PrettyStackTraceCFGElement(const CFGElement &Element, int BlockIdx,
148 int ElementIdx, const char *Message)
149 : Element(Element), BlockIdx(BlockIdx), ElementIdx(ElementIdx),
150 Message(Message) {}
151
152 void print(raw_ostream &OS) const override {
153 OS << Message << ": Element [B" << BlockIdx << "." << ElementIdx << "]\n";
154 if (auto Stmt = Element.getAs<CFGStmt>()) {
155 OS << "Stmt:\n";
156 ASTDumper Dumper(OS, false);
157 Dumper.Visit(Stmt->getStmt());
158 }
159 }
160
161private:
162 const CFGElement &Element;
163 int BlockIdx;
164 int ElementIdx;
165 const char *Message;
166};
167
168// Builds a joined TypeErasedDataflowAnalysisState from 0 or more sources,
169// each of which may be owned (built as part of the join) or external (a
170// reference to an Environment that will outlive the builder).
171// Avoids unneccesary copies of the environment.
172class JoinedStateBuilder {
173 AnalysisContext &AC;
174 Environment::ExprJoinBehavior JoinBehavior;
175 std::vector<const TypeErasedDataflowAnalysisState *> All;
176 std::deque<TypeErasedDataflowAnalysisState> Owned;
177
178 TypeErasedDataflowAnalysisState
179 join(const TypeErasedDataflowAnalysisState &L,
180 const TypeErasedDataflowAnalysisState &R) {
181 return {AC.Analysis.joinTypeErased(L.Lattice, R.Lattice),
182 Environment::join(L.Env, R.Env, AC.Analysis, JoinBehavior)};
183 }
184
185public:
186 JoinedStateBuilder(AnalysisContext &AC,
187 Environment::ExprJoinBehavior JoinBehavior)
188 : AC(AC), JoinBehavior(JoinBehavior) {}
189
190 void addOwned(TypeErasedDataflowAnalysisState State) {
191 Owned.push_back(std::move(State));
192 All.push_back(&Owned.back());
193 }
194 void addUnowned(const TypeErasedDataflowAnalysisState &State) {
195 All.push_back(&State);
196 }
197 TypeErasedDataflowAnalysisState take() && {
198 if (All.empty())
199 // FIXME: Consider passing `Block` to Analysis.typeErasedInitialElement
200 // to enable building analyses like computation of dominators that
201 // initialize the state of each basic block differently.
202 return {AC.Analysis.typeErasedInitialElement(), AC.InitEnv.fork()};
203 if (All.size() == 1)
204 // Join the environment with itself so that we discard expression state if
205 // desired.
206 // FIXME: We could consider writing special-case code for this that only
207 // does the discarding, but it's not clear if this is worth it.
208 return {All[0]->Lattice, Environment::join(All[0]->Env, All[0]->Env,
209 AC.Analysis, JoinBehavior)};
210
211 auto Result = join(*All[0], *All[1]);
212 for (unsigned I = 2; I < All.size(); ++I)
213 Result = join(Result, *All[I]);
214 return Result;
215 }
216};
217} // namespace
218
219static const Expr *getTerminatorCondition(const Stmt *TerminatorStmt) {
220 return TerminatorStmt == nullptr ? nullptr
221 : TerminatorVisitor().Visit(TerminatorStmt);
222}
223
224/// Computes the input state for a given basic block by joining the output
225/// states of its predecessors.
226///
227/// Requirements:
228///
229/// All predecessors of `Block` except those with loop back edges must have
230/// already been transferred. States in `AC.BlockStates` that are set to
231/// `std::nullopt` represent basic blocks that are not evaluated yet.
232static TypeErasedDataflowAnalysisState
233computeBlockInputState(const CFGBlock &Block, AnalysisContext &AC) {
234 std::vector<const CFGBlock *> Preds(Block.pred_begin(), Block.pred_end());
235 if (Block.getTerminator().isTemporaryDtorsBranch()) {
236 // This handles a special case where the code that produced the CFG includes
237 // a conditional operator with a branch that constructs a temporary and
238 // calls a destructor annotated as noreturn. The CFG models this as follows:
239 //
240 // B1 (contains the condition of the conditional operator) - succs: B2, B3
241 // B2 (contains code that does not call a noreturn destructor) - succs: B4
242 // B3 (contains code that calls a noreturn destructor) - succs: B4
243 // B4 (has temporary destructor terminator) - succs: B5, B6
244 // B5 (noreturn block that is associated with the noreturn destructor call)
245 // B6 (contains code that follows the conditional operator statement)
246 //
247 // The first successor (B5 above) of a basic block with a temporary
248 // destructor terminator (B4 above) is the block that evaluates the
249 // destructor. If that block has a noreturn element then the predecessor
250 // block that constructed the temporary object (B3 above) is effectively a
251 // noreturn block and its state should not be used as input for the state
252 // of the block that has a temporary destructor terminator (B4 above). This
253 // holds regardless of which branch of the ternary operator calls the
254 // noreturn destructor. However, it doesn't cases where a nested ternary
255 // operator includes a branch that contains a noreturn destructor call.
256 //
257 // See `NoreturnDestructorTest` for concrete examples.
258 if (Block.succ_begin()->getReachableBlock() != nullptr &&
259 Block.succ_begin()->getReachableBlock()->hasNoReturnElement()) {
260 const CFGBlock *StmtBlock = nullptr;
261 if (const Stmt *Terminator = Block.getTerminatorStmt())
262 StmtBlock = AC.ACFG.blockForStmt(*Terminator);
263 assert(StmtBlock != nullptr);
264 llvm::erase(Preds, StmtBlock);
265 }
266 }
267
268 // If any of the predecessor blocks contains an expression consumed in a
269 // different block, we need to keep expression state.
270 // Note that in this case, we keep expression state for all predecessors,
271 // rather than only those predecessors that actually contain an expression
272 // consumed in a different block. While this is potentially suboptimal, it's
273 // actually likely, if we have control flow within a full expression, that
274 // all predecessors have expression state consumed in a different block.
276 for (const CFGBlock *Pred : Preds) {
277 if (Pred && AC.ACFG.containsExprConsumedInDifferentBlock(*Pred)) {
278 JoinBehavior = Environment::KeepExprState;
279 break;
280 }
281 }
282
283 JoinedStateBuilder Builder(AC, JoinBehavior);
284 for (const CFGBlock *Pred : Preds) {
285 // Skip if the `Block` is unreachable or control flow cannot get past it.
286 if (!Pred || Pred->hasNoReturnElement())
287 continue;
288
289 // Skip if `Pred` was not evaluated yet. This could happen if `Pred` has a
290 // loop back edge to `Block`.
291 const std::optional<TypeErasedDataflowAnalysisState> &MaybePredState =
292 AC.BlockStates[Pred->getBlockID()];
293 if (!MaybePredState)
294 continue;
295
296 const TypeErasedDataflowAnalysisState &PredState = *MaybePredState;
297 const Expr *Cond = getTerminatorCondition(Pred->getTerminatorStmt());
298 if (Cond == nullptr) {
299 Builder.addUnowned(PredState);
300 continue;
301 }
302
303 bool BranchVal = blockIndexInPredecessor(*Pred, Block) == 0;
304
305 // `transferBranch` may need to mutate the environment to describe the
306 // dynamic effect of the terminator for a given branch. Copy now.
307 TypeErasedDataflowAnalysisState Copy = MaybePredState->fork();
308 if (AC.Analysis.builtinOptions()) {
309 auto *CondVal = Copy.Env.get<BoolValue>(*Cond);
310 // In transferCFGBlock(), we ensure that we always have a `Value`
311 // for the terminator condition, so assert this. We consciously
312 // assert ourselves instead of asserting via `cast()` so that we get
313 // a more meaningful line number if the assertion fails.
314 assert(CondVal != nullptr);
315 BoolValue *AssertedVal =
316 BranchVal ? CondVal : &Copy.Env.makeNot(*CondVal);
317 Copy.Env.assume(AssertedVal->formula());
318 }
319 AC.Analysis.transferBranchTypeErased(BranchVal, Cond, Copy.Lattice,
320 Copy.Env);
321 Builder.addOwned(std::move(Copy));
322 }
323 return std::move(Builder).take();
324}
325
326/// Built-in transfer function for `CFGStmt`.
327static void
328builtinTransferStatement(unsigned CurBlockID, const CFGStmt &Elt,
330 AnalysisContext &AC) {
331 const Stmt *S = Elt.getStmt();
332 assert(S != nullptr);
333 transfer(StmtToEnvMap(AC.ACFG, AC.BlockStates, CurBlockID, InputState), *S,
334 InputState.Env, AC.Analysis);
335}
336
337/// Built-in transfer function for `CFGInitializer`.
338static void
342 assert(Init != nullptr);
343
344 auto &Env = InputState.Env;
345 auto &ThisLoc = *Env.getThisPointeeStorageLocation();
346
347 if (!Init->isAnyMemberInitializer())
348 // FIXME: Handle base initialization
349 return;
350
351 auto *InitExpr = Init->getInit();
352 assert(InitExpr != nullptr);
353
354 const FieldDecl *Member = nullptr;
355 RecordStorageLocation *ParentLoc = &ThisLoc;
356 StorageLocation *MemberLoc = nullptr;
357 if (Init->isMemberInitializer()) {
358 Member = Init->getMember();
359 MemberLoc = ThisLoc.getChild(*Member);
360 } else {
361 IndirectFieldDecl *IndirectField = Init->getIndirectMember();
362 assert(IndirectField != nullptr);
363 MemberLoc = &ThisLoc;
364 for (const auto *I : IndirectField->chain()) {
366 ParentLoc = cast<RecordStorageLocation>(MemberLoc);
367 MemberLoc = ParentLoc->getChild(*Member);
368 }
369 }
370 assert(Member != nullptr);
371
372 // FIXME: Instead of these case distinctions, we would ideally want to be able
373 // to simply use `Environment::createObject()` here, the same way that we do
374 // this in `TransferVisitor::VisitInitListExpr()`. However, this would require
375 // us to be able to build a list of fields that we then use to initialize an
376 // `RecordStorageLocation` -- and the problem is that, when we get here,
377 // the `RecordStorageLocation` already exists. We should explore if there's
378 // anything that we can do to change this.
379 if (Member->getType()->isReferenceType()) {
380 auto *InitExprLoc = Env.getStorageLocation(*InitExpr);
381 if (InitExprLoc == nullptr)
382 return;
383
384 ParentLoc->setChild(*Member, InitExprLoc);
385 // Record-type initializers construct themselves directly into the result
386 // object, so there is no need to handle them here.
387 } else if (!Member->getType()->isRecordType()) {
388 assert(MemberLoc != nullptr);
389 if (auto *InitExprVal = Env.getValue(*InitExpr))
390 Env.setValue(*MemberLoc, *InitExprVal);
391 }
392}
393
394static void builtinTransfer(unsigned CurBlockID, const CFGElement &Elt,
396 AnalysisContext &AC) {
397 switch (Elt.getKind()) {
399 builtinTransferStatement(CurBlockID, Elt.castAs<CFGStmt>(), State, AC);
400 break;
403 break;
405 // Removing declarations when their lifetime ends serves two purposes:
406 // - Eliminate unnecessary clutter from `Environment::DeclToLoc`
407 // - Allow us to assert that, when joining two `Environment`s, the two
408 // `DeclToLoc` maps never contain entries that map the same declaration to
409 // different storage locations.
410 if (const ValueDecl *VD = Elt.castAs<CFGLifetimeEnds>().getVarDecl())
411 State.Env.removeDecl(*VD);
412 break;
413 default:
414 // FIXME: Evaluate other kinds of `CFGElement`
415 break;
416 }
417}
418
419/// Transfers `State` by evaluating each element in the `Block` based on the
420/// `AC.Analysis` specified.
421///
422/// Built-in transfer functions (if the option for `ApplyBuiltinTransfer` is set
423/// by the analysis) will be applied to the element before evaluation by the
424/// user-specified analysis.
425/// `PostVisitCFG` (if provided) will be applied to the element after evaluation
426/// by the user-specified analysis.
427static TypeErasedDataflowAnalysisState
428transferCFGBlock(const CFGBlock &Block, AnalysisContext &AC,
429 const CFGEltCallbacksTypeErased &PostAnalysisCallbacks = {}) {
430 AC.Log.enterBlock(Block, PostAnalysisCallbacks.Before != nullptr ||
431 PostAnalysisCallbacks.After != nullptr);
432 auto State = computeBlockInputState(Block, AC);
433 AC.Log.recordState(State);
434 int ElementIdx = 1;
435 for (const auto &Element : Block) {
436 PrettyStackTraceCFGElement CrashInfo(Element, Block.getBlockID(),
437 ElementIdx++, "transferCFGBlock");
438
439 AC.Log.enterElement(Element);
440
441 if (PostAnalysisCallbacks.Before) {
442 PostAnalysisCallbacks.Before(Element, State);
443 }
444
445 // Built-in analysis
446 if (AC.Analysis.builtinOptions()) {
447 builtinTransfer(Block.getBlockID(), Element, State, AC);
448 }
449
450 // User-provided analysis
451 AC.Analysis.transferTypeErased(Element, State.Lattice, State.Env);
452
453 if (PostAnalysisCallbacks.After) {
454 PostAnalysisCallbacks.After(Element, State);
455 }
456
457 AC.Log.recordState(State);
458 }
459
460 // If we have a terminator, evaluate its condition.
461 // This `Expr` may not appear as a `CFGElement` anywhere else, and it's
462 // important that we evaluate it here (rather than while processing the
463 // terminator) so that we put the corresponding value in the right
464 // environment.
465 if (const Expr *TerminatorCond =
466 dyn_cast_or_null<Expr>(Block.getTerminatorCondition())) {
467 if (State.Env.getValue(*TerminatorCond) == nullptr)
468 // FIXME: This only runs the builtin transfer, not the analysis-specific
469 // transfer. Fixing this isn't trivial, as the analysis-specific transfer
470 // takes a `CFGElement` as input, but some expressions only show up as a
471 // terminator condition, but not as a `CFGElement`. The condition of an if
472 // statement is one such example.
473 transfer(StmtToEnvMap(AC.ACFG, AC.BlockStates, Block.getBlockID(), State),
474 *TerminatorCond, State.Env, AC.Analysis);
475
476 // If the transfer function didn't produce a value, create an atom so that
477 // we have *some* value for the condition expression. This ensures that
478 // when we extend the flow condition, it actually changes.
479 if (State.Env.getValue(*TerminatorCond) == nullptr)
480 State.Env.setValue(*TerminatorCond, State.Env.makeAtomicBoolValue());
481 AC.Log.recordState(State);
482 }
483
484 return State;
485}
486
489 const AdornedCFG &ACFG, TypeErasedDataflowAnalysis &Analysis,
490 const Environment &InitEnv,
491 const CFGEltCallbacksTypeErased &PostAnalysisCallbacks,
492 std::int32_t MaxBlockVisits) {
493 PrettyStackTraceAnalysis CrashInfo(ACFG, "runTypeErasedDataflowAnalysis");
494
495 std::optional<Environment> MaybeStartingEnv;
496 if (InitEnv.callStackSize() == 0) {
497 MaybeStartingEnv = InitEnv.fork();
498 MaybeStartingEnv->initialize();
499 }
500 const Environment &StartingEnv =
501 MaybeStartingEnv ? *MaybeStartingEnv : InitEnv;
502
503 const clang::CFG &CFG = ACFG.getCFG();
504 PostOrderCFGView POV(&CFG);
505 ForwardDataflowWorklist Worklist(CFG, &POV);
506
507 std::vector<std::optional<TypeErasedDataflowAnalysisState>> BlockStates(
508 CFG.size());
509
510 // The entry basic block doesn't contain statements so it can be skipped.
511 const CFGBlock &Entry = CFG.getEntry();
512 BlockStates[Entry.getBlockID()] = {Analysis.typeErasedInitialElement(),
513 StartingEnv.fork()};
514 Worklist.enqueueSuccessors(&Entry);
515
516 AnalysisContext AC(ACFG, Analysis, StartingEnv, BlockStates);
517 std::int32_t BlockVisits = 0;
518 while (const CFGBlock *Block = Worklist.dequeue()) {
519 LLVM_DEBUG(llvm::dbgs()
520 << "Processing Block " << Block->getBlockID() << "\n");
521 if (++BlockVisits > MaxBlockVisits) {
522 return llvm::createStringError(std::errc::timed_out,
523 "maximum number of blocks processed");
524 }
525
526 const std::optional<TypeErasedDataflowAnalysisState> &OldBlockState =
527 BlockStates[Block->getBlockID()];
528 TypeErasedDataflowAnalysisState NewBlockState =
530 LLVM_DEBUG({
531 llvm::errs() << "New Env:\n";
532 NewBlockState.Env.dump();
533 });
534
535 if (OldBlockState) {
536 LLVM_DEBUG({
537 llvm::errs() << "Old Env:\n";
538 OldBlockState->Env.dump();
539 });
540 if (isBackedgeNode(*Block)) {
541 LatticeJoinEffect Effect1 = Analysis.widenTypeErased(
542 NewBlockState.Lattice, OldBlockState->Lattice);
543 LatticeJoinEffect Effect2 =
544 NewBlockState.Env.widen(OldBlockState->Env, Analysis);
545 if (Effect1 == LatticeJoinEffect::Unchanged &&
546 Effect2 == LatticeJoinEffect::Unchanged) {
547 // The state of `Block` didn't change from widening so there's no need
548 // to revisit its successors.
549 AC.Log.blockConverged();
550 continue;
551 }
552 } else if (Analysis.isEqualTypeErased(OldBlockState->Lattice,
553 NewBlockState.Lattice) &&
554 OldBlockState->Env.equivalentTo(NewBlockState.Env, Analysis)) {
555 // The state of `Block` didn't change after transfer so there's no need
556 // to revisit its successors.
557 AC.Log.blockConverged();
558 continue;
559 }
560 }
561
562 BlockStates[Block->getBlockID()] = std::move(NewBlockState);
563
564 // Do not add unreachable successor blocks to `Worklist`.
565 if (Block->hasNoReturnElement())
566 continue;
567
568 Worklist.enqueueSuccessors(Block);
569 }
570 // FIXME: Consider evaluating unreachable basic blocks (those that have a
571 // state set to `std::nullopt` at this point) to also analyze dead code.
572
573 if (PostAnalysisCallbacks.Before || PostAnalysisCallbacks.After) {
574 for (const CFGBlock *Block : ACFG.getCFG()) {
575 // Skip blocks that were not evaluated.
576 if (!BlockStates[Block->getBlockID()])
577 continue;
578 transferCFGBlock(*Block, AC, PostAnalysisCallbacks);
579 }
580 }
581
582 return std::move(BlockStates);
583}
584
585} // namespace dataflow
586} // namespace clang
static const Stmt * getTerminatorCondition(const CFGBlock *B)
A customized wrapper for CFGBlock::getTerminatorCondition() which returns the element for ObjCForColl...
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
static void print(llvm::raw_ostream &OS, const T &V, ASTContext &ASTCtx, QualType Ty)
This class represents a potential adjacent block in the CFG.
Definition CFG.h:825
Represents a single basic block in a source-level CFG.
Definition CFG.h:605
succ_range succs()
Definition CFG.h:1000
succ_iterator succ_begin()
Definition CFG.h:990
const Stmt * getLoopTarget() const
Definition CFG.h:1104
unsigned getBlockID() const
Definition CFG.h:1111
Represents a top-level expression in a basic block.
Definition CFG.h:55
T castAs() const
Convert to the specified CFGElement type, asserting that this CFGElement is of the desired type.
Definition CFG.h:99
Kind getKind() const
Definition CFG.h:118
Represents C++ base or member initializer from constructor's initialization list.
Definition CFG.h:228
CXXCtorInitializer * getInitializer() const
Definition CFG.h:233
Represents the point where the lifetime of an automatic object ends.
Definition CFG.h:293
const VarDecl * getVarDecl() const
Definition CFG.h:298
const Stmt * getStmt() const
Definition CFG.h:139
Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt.
Definition CFG.h:1222
unsigned size() const
Return the total number of CFGBlocks within the CFG This is simply a renaming of the getNumBlockIDs()...
Definition CFG.h:1414
CFGBlock & getEntry()
Definition CFG.h:1330
Represents a C++ base or member initializer.
Definition DeclCXX.h:2369
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
This represents one expression.
Definition Expr.h:112
Represents a member of a struct/union/class.
Definition Decl.h:3157
IfStmt - This represents an if/then/else.
Definition Stmt.h:2259
Expr * getCond()
Definition Stmt.h:2336
Represents a field injected from an anonymous union/struct into the parent scope.
Definition Decl.h:3464
ArrayRef< NamedDecl * > chain() const
Definition Decl.h:3485
Stmt - This represents one statement.
Definition Stmt.h:85
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
Definition Decl.h:711
Holds CFG with additional information derived from it that is needed to perform dataflow analysis.
Definition AdornedCFG.h:47
const CFG & getCFG() const
Returns the CFG that is stored in this context.
Definition AdornedCFG.h:64
const CFGBlock * blockForStmt(const Stmt &S) const
Returns the basic block that contains S, or null if no basic block containing S is found.
Definition AdornedCFG.h:68
bool containsExprConsumedInDifferentBlock(const CFGBlock &B) const
Returns whether B contains an expression that is consumed in a different block than B (i....
Definition AdornedCFG.h:85
Models a boolean.
Definition Value.h:94
const Formula & formula() const
Definition Value.h:107
Holds the state of the program (store and heap) at a given program point.
LatticeEffect widen(const Environment &PrevEnv, Environment::ValueModel &Model)
Widens the environment point-wise, using PrevEnv as needed to inform the approximation.
RecordStorageLocation * getThisPointeeStorageLocation() const
Returns the storage location assigned to the this pointee in the environment or null if the this poin...
LLVM_DUMP_METHOD void dump() const
Environment fork() const
Returns a new environment that is a copy of this one.
ExprJoinBehavior
How to treat expression state (ExprToLoc and ExprToVal) in a join.
size_t callStackSize() const
Returns the size of the call stack, not counting the initial analysis target.
void initialize()
Assigns storage locations and values to all parameters, captures, global variables,...
virtual void enterBlock(const CFGBlock &, bool PostVisit)
Called when we start (re-)processing a block in the CFG.
Definition Logger.h:55
virtual void enterElement(const CFGElement &)
Called when we start processing an element in the current CFG block.
Definition Logger.h:59
virtual void recordState(TypeErasedDataflowAnalysisState &)
Records the analysis state computed for the current program point.
Definition Logger.h:62
virtual void blockConverged()
Records that the analysis state for the current block is now final.
Definition Logger.h:64
A storage location for a record (struct, class, or union).
StorageLocation * getChild(const ValueDecl &D) const
Returns the child storage location for D.
void setChild(const ValueDecl &D, StorageLocation *Loc)
Changes the child storage location for a field D of reference type.
Maps statements to the environments of basic blocks that contain them.
Definition Transfer.h:26
Base class for elements of the local variable store and of the heap.
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 void transferTypeErased(const CFGElement &, TypeErasedLattice &, Environment &)=0
Applies the analysis transfer function for a given control flow graph element and type-erased lattice...
virtual bool isEqualTypeErased(const TypeErasedLattice &, const TypeErasedLattice &)=0
Returns true if and only if the two given type-erased lattice elements are equal.
constexpr XRayInstrMask All
Definition XRayInstr.h:43
void transfer(const StmtToEnvMap &StmtToEnv, const Stmt &S, Environment &Env, Environment::ValueModel &Model)
Evaluates S and updates Env accordingly.
Definition Transfer.cpp:951
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.
static void builtinTransfer(unsigned CurBlockID, const CFGElement &Elt, TypeErasedDataflowAnalysisState &State, AnalysisContext &AC)
static void builtinTransferInitializer(const CFGInitializer &Elt, TypeErasedDataflowAnalysisState &InputState)
Built-in transfer function for CFGInitializer.
static TypeErasedDataflowAnalysisState transferCFGBlock(const CFGBlock &Block, AnalysisContext &AC, const CFGEltCallbacksTypeErased &PostAnalysisCallbacks={})
Transfers State by evaluating each element in the Block based on the AC.Analysis specified.
static int blockIndexInPredecessor(const CFGBlock &Pred, const CFGBlock &Block)
Returns the index of Block in the successors of Pred.
static bool isBackedgeNode(const CFGBlock &B)
llvm::Expected< std::vector< std::optional< TypeErasedDataflowAnalysisState > > > runTypeErasedDataflowAnalysis(const AdornedCFG &ACFG, TypeErasedDataflowAnalysis &Analysis, const Environment &InitEnv, const CFGEltCallbacksTypeErased &PostAnalysisCallbacks, std::int32_t MaxBlockVisits)
Performs dataflow analysis and returns a mapping from basic block IDs to dataflow analysis states tha...
static void builtinTransferStatement(unsigned CurBlockID, const CFGStmt &Elt, TypeErasedDataflowAnalysisState &InputState, AnalysisContext &AC)
Built-in transfer function for CFGStmt.
LatticeEffect LatticeJoinEffect
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
The JSON file list parser is used to communicate input to InstallAPI.
nullptr
This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...
Expr * Cond
};
@ Result
The result type of a method or function.
Definition TypeBase.h:905
U cast(CodeGen::Address addr)
Definition Address.h:327
Diagnostic wrappers for TextAPI types for error reporting.
Definition Dominators.h:30
A worklist implementation for forward dataflow analysis.
void enqueueSuccessors(const CFGBlock *Block)
A pair of callbacks to be called with the state before and after visiting a CFG element.
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).