clang 23.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 <cstddef>
15#include <optional>
16#include <system_error>
17#include <utility>
18#include <vector>
19
20#include "clang/AST/ASTDumper.h"
21#include "clang/AST/DeclCXX.h"
23#include "clang/AST/Stmt.h"
24#include "clang/AST/StmtCXX.h"
27#include "clang/Analysis/CFG.h"
35#include "clang/Basic/LLVM.h"
37#include "llvm/ADT/ArrayRef.h"
38#include "llvm/ADT/BitVector.h"
39#include "llvm/ADT/DenseMap.h"
40#include "llvm/ADT/DenseSet.h"
41#include "llvm/ADT/STLExtras.h"
42#include "llvm/Support/Debug.h"
43#include "llvm/Support/Error.h"
44
45#define DEBUG_TYPE "clang-dataflow"
46
47namespace clang {
48namespace dataflow {
49class NoopLattice;
50}
51} // namespace clang
52
53namespace llvm {
54// This needs to be exported for ClangAnalysisFlowSensitiveTests so any_cast
55// uses the correct address of Any::TypeId from the clang shared library instead
56// of creating one in the test executable. when building with
57// CLANG_LINK_CLANG_DYLIB
58template struct CLANG_EXPORT_TEMPLATE Any::TypeId<clang::dataflow::NoopLattice>;
59} // namespace llvm
60
61namespace clang {
62namespace dataflow {
63
64/// Returns the index of `Block` in the successors of `Pred`.
65static int blockIndexInPredecessor(const CFGBlock &Pred,
66 const CFGBlock &Block) {
67 auto BlockPos = llvm::find_if(
68 Pred.succs(), [&Block](const CFGBlock::AdjacentBlock &Succ) {
69 return Succ && Succ->getBlockID() == Block.getBlockID();
70 });
71 return BlockPos - Pred.succ_begin();
72}
73
74namespace {
75
76/// Extracts the terminator's condition expression.
77class TerminatorVisitor
79public:
80 TerminatorVisitor() = default;
81 const Expr *VisitIfStmt(const IfStmt *S) { return S->getCond(); }
82 const Expr *VisitWhileStmt(const WhileStmt *S) { return S->getCond(); }
83 const Expr *VisitDoStmt(const DoStmt *S) { return S->getCond(); }
84 const Expr *VisitForStmt(const ForStmt *S) { return S->getCond(); }
85 const Expr *VisitCXXForRangeStmt(const CXXForRangeStmt *) {
86 // Don't do anything special for CXXForRangeStmt, because the condition
87 // (being implicitly generated) isn't visible from the loop body.
88 return nullptr;
89 }
90 const Expr *VisitBinaryOperator(const BinaryOperator *S) {
91 assert(S->getOpcode() == BO_LAnd || S->getOpcode() == BO_LOr);
92 return S->getLHS();
93 }
94 const Expr *VisitConditionalOperator(const ConditionalOperator *S) {
95 return S->getCond();
96 }
97};
98
99/// Holds data structures required for running dataflow analysis.
100struct AnalysisContext {
101 AnalysisContext(const AdornedCFG &ACFG, TypeErasedDataflowAnalysis &Analysis,
102 const Environment &InitEnv,
103 llvm::ArrayRef<std::optional<TypeErasedDataflowAnalysisState>>
104 BlockStates)
105 : ACFG(ACFG), Analysis(Analysis), InitEnv(InitEnv),
106 Log(*InitEnv.getDataflowAnalysisContext().getOptions().Log),
107 BlockStates(BlockStates) {
108 Log.beginAnalysis(ACFG, Analysis);
109 }
110 ~AnalysisContext() { Log.endAnalysis(); }
111
112 /// Contains the CFG being analyzed.
113 const AdornedCFG &ACFG;
114 /// The analysis to be run.
115 TypeErasedDataflowAnalysis &Analysis;
116 /// Initial state to start the analysis.
117 const Environment &InitEnv;
118 Logger &Log;
119 /// Stores the state of a CFG block if it has been evaluated by the analysis.
120 /// The indices correspond to the block IDs.
122};
123
124class PrettyStackTraceAnalysis : public llvm::PrettyStackTraceEntry {
125public:
126 PrettyStackTraceAnalysis(const AdornedCFG &ACFG, const char *Message)
127 : ACFG(ACFG), Message(Message) {}
128
129 void print(raw_ostream &OS) const override {
130 OS << Message << "\n";
131 OS << "Decl:\n";
132 ACFG.getDecl().dump(OS);
133 OS << "CFG:\n";
134 ACFG.getCFG().print(OS, LangOptions(), false);
135 }
136
137private:
138 const AdornedCFG &ACFG;
139 const char *Message;
140};
141
142class PrettyStackTraceCFGElement : public llvm::PrettyStackTraceEntry {
143public:
144 PrettyStackTraceCFGElement(const CFGElement &Element, int BlockIdx,
145 int ElementIdx, const char *Message)
146 : Element(Element), BlockIdx(BlockIdx), ElementIdx(ElementIdx),
147 Message(Message) {}
148
149 void print(raw_ostream &OS) const override {
150 OS << Message << ": Element [B" << BlockIdx << "." << ElementIdx << "]\n";
151 if (auto Stmt = Element.getAs<CFGStmt>()) {
152 OS << "Stmt:\n";
153 ASTDumper Dumper(OS, false);
154 Dumper.Visit(Stmt->getStmt());
155 }
156 }
157
158private:
159 const CFGElement &Element;
160 int BlockIdx;
161 int ElementIdx;
162 const char *Message;
163};
164
165// Builds a joined TypeErasedDataflowAnalysisState from 0 or more sources,
166// each of which may be owned (built as part of the join) or external (a
167// reference to an Environment that will outlive the builder).
168// Avoids unneccesary copies of the environment.
169class JoinedStateBuilder {
170 AnalysisContext &AC;
171 Environment::ExprJoinBehavior JoinBehavior;
172 std::vector<const TypeErasedDataflowAnalysisState *> All;
173 std::deque<TypeErasedDataflowAnalysisState> Owned;
174
175 TypeErasedDataflowAnalysisState
176 join(const TypeErasedDataflowAnalysisState &L,
177 const TypeErasedDataflowAnalysisState &R) {
178 return {AC.Analysis.joinTypeErased(L.Lattice, R.Lattice),
179 Environment::join(L.Env, R.Env, AC.Analysis, JoinBehavior)};
180 }
181
182public:
183 JoinedStateBuilder(AnalysisContext &AC,
184 Environment::ExprJoinBehavior JoinBehavior)
185 : AC(AC), JoinBehavior(JoinBehavior) {}
186
187 void addOwned(TypeErasedDataflowAnalysisState State) {
188 Owned.push_back(std::move(State));
189 All.push_back(&Owned.back());
190 }
191 void addUnowned(const TypeErasedDataflowAnalysisState &State) {
192 All.push_back(&State);
193 }
194 TypeErasedDataflowAnalysisState take() && {
195 if (All.empty())
196 // FIXME: Consider passing `Block` to Analysis.typeErasedInitialElement
197 // to enable building analyses like computation of dominators that
198 // initialize the state of each basic block differently.
199 return {AC.Analysis.typeErasedInitialElement(), AC.InitEnv.fork()};
200 if (All.size() == 1)
201 // Join the environment with itself so that we discard expression state if
202 // desired.
203 // FIXME: We could consider writing special-case code for this that only
204 // does the discarding, but it's not clear if this is worth it.
205 return {All[0]->Lattice, Environment::join(All[0]->Env, All[0]->Env,
206 AC.Analysis, JoinBehavior)};
207
208 auto Result = join(*All[0], *All[1]);
209 for (unsigned I = 2; I < All.size(); ++I)
210 Result = join(Result, *All[I]);
211 return Result;
212 }
213};
214} // namespace
215
216static const Expr *getTerminatorCondition(const Stmt *TerminatorStmt) {
217 return TerminatorStmt == nullptr ? nullptr
218 : TerminatorVisitor().Visit(TerminatorStmt);
219}
220
221/// Computes the input state for a given basic block by joining the output
222/// states of its predecessors.
223///
224/// Requirements:
225///
226/// All predecessors of `Block` except those with loop back edges must have
227/// already been transferred. States in `AC.BlockStates` that are set to
228/// `std::nullopt` represent basic blocks that are not evaluated yet.
229static TypeErasedDataflowAnalysisState
230computeBlockInputState(const CFGBlock &Block, AnalysisContext &AC) {
231 std::vector<const CFGBlock *> Preds(Block.pred_begin(), Block.pred_end());
232 if (Block.getTerminator().isTemporaryDtorsBranch()) {
233 // This handles a special case where the code that produced the CFG includes
234 // a conditional operator with a branch that constructs a temporary and
235 // calls a destructor annotated as noreturn. The CFG models this as follows:
236 //
237 // B1 (contains the condition of the conditional operator) - succs: B2, B3
238 // B2 (contains code that does not call a noreturn destructor) - succs: B4
239 // B3 (contains code that calls a noreturn destructor) - succs: B4
240 // B4 (has temporary destructor terminator) - succs: B5, B6
241 // B5 (noreturn block that is associated with the noreturn destructor call)
242 // B6 (contains code that follows the conditional operator statement)
243 //
244 // The first successor (B5 above) of a basic block with a temporary
245 // destructor terminator (B4 above) is the block that evaluates the
246 // destructor. If that block has a noreturn element then the predecessor
247 // block that constructed the temporary object (B3 above) is effectively a
248 // noreturn block and its state should not be used as input for the state
249 // of the block that has a temporary destructor terminator (B4 above). This
250 // holds regardless of which branch of the ternary operator calls the
251 // noreturn destructor. However, it doesn't cases where a nested ternary
252 // operator includes a branch that contains a noreturn destructor call.
253 //
254 // See `NoreturnDestructorTest` for concrete examples.
255 if (Block.succ_begin()->getReachableBlock() != nullptr &&
256 Block.succ_begin()->getReachableBlock()->hasNoReturnElement()) {
257 const CFGBlock *StmtBlock = nullptr;
258 if (const Stmt *Terminator = Block.getTerminatorStmt())
259 StmtBlock = AC.ACFG.blockForStmt(*Terminator);
260 assert(StmtBlock != nullptr);
261 llvm::erase(Preds, StmtBlock);
262 }
263 }
264
265 // If any of the predecessor blocks contains an expression consumed in a
266 // different block, we need to keep expression state.
267 // Note that in this case, we keep expression state for all predecessors,
268 // rather than only those predecessors that actually contain an expression
269 // consumed in a different block. While this is potentially suboptimal, it's
270 // actually likely, if we have control flow within a full expression, that
271 // all predecessors have expression state consumed in a different block.
273 for (const CFGBlock *Pred : Preds) {
274 if (Pred && AC.ACFG.containsExprConsumedInDifferentBlock(*Pred)) {
275 JoinBehavior = Environment::KeepExprState;
276 break;
277 }
278 }
279
280 JoinedStateBuilder Builder(AC, JoinBehavior);
281 for (const CFGBlock *Pred : Preds) {
282 // Skip if the `Block` is unreachable or control flow cannot get past it.
283 if (!Pred || Pred->hasNoReturnElement())
284 continue;
285
286 // Skip if `Pred` was not evaluated yet. This could happen if `Pred` has a
287 // loop back edge to `Block`.
288 const std::optional<TypeErasedDataflowAnalysisState> &MaybePredState =
289 AC.BlockStates[Pred->getBlockID()];
290 if (!MaybePredState)
291 continue;
292
293 const TypeErasedDataflowAnalysisState &PredState = *MaybePredState;
294 const Expr *Cond = getTerminatorCondition(Pred->getTerminatorStmt());
295 if (Cond == nullptr) {
296 Builder.addUnowned(PredState);
297 continue;
298 }
299
300 bool BranchVal = blockIndexInPredecessor(*Pred, Block) == 0;
301
302 // `transferBranch` may need to mutate the environment to describe the
303 // dynamic effect of the terminator for a given branch. Copy now.
304 TypeErasedDataflowAnalysisState Copy = MaybePredState->fork();
305 if (AC.Analysis.builtinOptions()) {
306 auto *CondVal = Copy.Env.get<BoolValue>(*Cond);
307 // In transferCFGBlock(), we ensure that we always have a `Value`
308 // for the terminator condition, so assert this. We consciously
309 // assert ourselves instead of asserting via `cast()` so that we get
310 // a more meaningful line number if the assertion fails.
311 assert(CondVal != nullptr);
312 BoolValue *AssertedVal =
313 BranchVal ? CondVal : &Copy.Env.makeNot(*CondVal);
314 Copy.Env.assume(AssertedVal->formula());
315 }
316 AC.Analysis.transferBranchTypeErased(BranchVal, Cond, Copy.Lattice,
317 Copy.Env);
318 Builder.addOwned(std::move(Copy));
319 }
320 return std::move(Builder).take();
321}
322
323/// Built-in transfer function for `CFGStmt`.
324static void
325builtinTransferStatement(unsigned CurBlockID, const CFGStmt &Elt,
327 AnalysisContext &AC) {
328 const Stmt *S = Elt.getStmt();
329 assert(S != nullptr);
330 transfer(StmtToEnvMap(AC.ACFG, AC.BlockStates, CurBlockID, InputState), *S,
331 InputState.Env, AC.Analysis);
332}
333
334/// Built-in transfer function for `CFGInitializer`.
335static void
339 assert(Init != nullptr);
340
341 auto &Env = InputState.Env;
342 auto &ThisLoc = *Env.getThisPointeeStorageLocation();
343
344 if (!Init->isAnyMemberInitializer())
345 // FIXME: Handle base initialization
346 return;
347
348 auto *InitExpr = Init->getInit();
349 assert(InitExpr != nullptr);
350
351 const FieldDecl *Member = nullptr;
352 RecordStorageLocation *ParentLoc = &ThisLoc;
353 StorageLocation *MemberLoc = nullptr;
354 if (Init->isMemberInitializer()) {
355 Member = Init->getMember();
356 MemberLoc = ThisLoc.getChild(*Member);
357 } else {
358 IndirectFieldDecl *IndirectField = Init->getIndirectMember();
359 assert(IndirectField != nullptr);
360 MemberLoc = &ThisLoc;
361 for (const auto *I : IndirectField->chain()) {
363 ParentLoc = cast<RecordStorageLocation>(MemberLoc);
364 MemberLoc = ParentLoc->getChild(*Member);
365 }
366 }
367 assert(Member != nullptr);
368
369 // FIXME: Instead of these case distinctions, we would ideally want to be able
370 // to simply use `Environment::createObject()` here, the same way that we do
371 // this in `TransferVisitor::VisitInitListExpr()`. However, this would require
372 // us to be able to build a list of fields that we then use to initialize an
373 // `RecordStorageLocation` -- and the problem is that, when we get here,
374 // the `RecordStorageLocation` already exists. We should explore if there's
375 // anything that we can do to change this.
376 if (Member->getType()->isReferenceType()) {
377 auto *InitExprLoc = Env.getStorageLocation(*InitExpr);
378 if (InitExprLoc == nullptr)
379 return;
380
381 ParentLoc->setChild(*Member, InitExprLoc);
382 // Record-type initializers construct themselves directly into the result
383 // object, so there is no need to handle them here.
384 } else if (!Member->getType()->isRecordType()) {
385 assert(MemberLoc != nullptr);
386 if (auto *InitExprVal = Env.getValue(*InitExpr))
387 Env.setValue(*MemberLoc, *InitExprVal);
388 }
389}
390
391static void builtinTransfer(unsigned CurBlockID, const CFGElement &Elt,
393 AnalysisContext &AC) {
394 switch (Elt.getKind()) {
396 builtinTransferStatement(CurBlockID, Elt.castAs<CFGStmt>(), State, AC);
397 break;
400 break;
402 // Removing declarations when their lifetime ends serves two purposes:
403 // - Eliminate unnecessary clutter from `Environment::DeclToLoc`
404 // - Allow us to assert that, when joining two `Environment`s, the two
405 // `DeclToLoc` maps never contain entries that map the same declaration to
406 // different storage locations.
407 if (const ValueDecl *VD = Elt.castAs<CFGLifetimeEnds>().getVarDecl())
408 State.Env.removeDecl(*VD);
409 break;
410 default:
411 // FIXME: Evaluate other kinds of `CFGElement`
412 break;
413 }
414}
415
416/// Transfers `State` by evaluating each element in the `Block` based on the
417/// `AC.Analysis` specified.
418///
419/// Built-in transfer functions (if the option for `ApplyBuiltinTransfer` is set
420/// by the analysis) will be applied to the element before evaluation by the
421/// user-specified analysis.
422/// `PostVisitCFG` (if provided) will be applied to the element after evaluation
423/// by the user-specified analysis.
424static TypeErasedDataflowAnalysisState
425transferCFGBlock(const CFGBlock &Block, AnalysisContext &AC,
426 const CFGEltCallbacksTypeErased &PostAnalysisCallbacks = {}) {
427 AC.Log.enterBlock(Block, PostAnalysisCallbacks.Before != nullptr ||
428 PostAnalysisCallbacks.After != nullptr);
429 auto State = computeBlockInputState(Block, AC);
430 AC.Log.recordState(State);
431 int ElementIdx = 1;
432 for (const auto &Element : Block) {
433 PrettyStackTraceCFGElement CrashInfo(Element, Block.getBlockID(),
434 ElementIdx++, "transferCFGBlock");
435
436 AC.Log.enterElement(Element);
437
438 if (PostAnalysisCallbacks.Before) {
439 PostAnalysisCallbacks.Before(Element, State);
440 }
441
442 // Built-in analysis
443 if (AC.Analysis.builtinOptions()) {
444 builtinTransfer(Block.getBlockID(), Element, State, AC);
445 }
446
447 // User-provided analysis
448 AC.Analysis.transferTypeErased(Element, State.Lattice, State.Env);
449
450 if (PostAnalysisCallbacks.After) {
451 PostAnalysisCallbacks.After(Element, State);
452 }
453
454 AC.Log.recordState(State);
455 }
456
457 // If we have a terminator, evaluate its condition.
458 // This `Expr` may not appear as a `CFGElement` anywhere else, and it's
459 // important that we evaluate it here (rather than while processing the
460 // terminator) so that we put the corresponding value in the right
461 // environment.
462 if (const Expr *TerminatorCond =
463 dyn_cast_or_null<Expr>(Block.getTerminatorCondition())) {
464 if (State.Env.getValue(*TerminatorCond) == nullptr)
465 // FIXME: This only runs the builtin transfer, not the analysis-specific
466 // transfer. Fixing this isn't trivial, as the analysis-specific transfer
467 // takes a `CFGElement` as input, but some expressions only show up as a
468 // terminator condition, but not as a `CFGElement`. The condition of an if
469 // statement is one such example.
470 transfer(StmtToEnvMap(AC.ACFG, AC.BlockStates, Block.getBlockID(), State),
471 *TerminatorCond, State.Env, AC.Analysis);
472
473 // If the transfer function didn't produce a value, create an atom so that
474 // we have *some* value for the condition expression. This ensures that
475 // when we extend the flow condition, it actually changes.
476 if (State.Env.getValue(*TerminatorCond) == nullptr)
477 State.Env.setValue(*TerminatorCond, State.Env.makeAtomicBoolValue());
478 AC.Log.recordState(State);
479 }
480
481 return State;
482}
483
484// Returns the number of reachable blocks (would be visited if we only visit
485// each reachable block once). This is a light version of the main fixpoint loop
486// (keep in sync).
487size_t NumReachableBlocks(const CFG &CFG) {
488 PostOrderCFGView POV(&CFG);
489 ForwardDataflowWorklist Worklist(CFG, &POV);
490 llvm::BitVector VisitedBlocks(CFG.size());
491 const CFGBlock &Entry = CFG.getEntry();
492 Worklist.enqueueSuccessors(&Entry);
493 while (const CFGBlock *Block = Worklist.dequeue()) {
494 if (VisitedBlocks[Block->getBlockID()])
495 continue;
496 VisitedBlocks[Block->getBlockID()] = true;
497 // Do not add unreachable successor blocks to `Worklist`.
498 if (Block->hasNoReturnElement())
499 continue;
500 Worklist.enqueueSuccessors(Block);
501 }
502 return VisitedBlocks.count();
503}
504
507 const AdornedCFG &ACFG, TypeErasedDataflowAnalysis &Analysis,
508 const Environment &InitEnv,
509 const CFGEltCallbacksTypeErased &PostAnalysisCallbacks,
510 std::int32_t MaxBlockVisits) {
511 PrettyStackTraceAnalysis CrashInfo(ACFG, "runTypeErasedDataflowAnalysis");
512
513 std::optional<Environment> MaybeStartingEnv;
514 if (InitEnv.callStackSize() == 0) {
515 MaybeStartingEnv = InitEnv.fork();
516 MaybeStartingEnv->initialize();
517 }
518 const Environment &StartingEnv =
519 MaybeStartingEnv ? *MaybeStartingEnv : InitEnv;
520
521 const clang::CFG &CFG = ACFG.getCFG();
522
523 // Bail out if the number of reachable blocks is already beyond the maximum
524 // number of block visits to save time and avoid running out of memory for
525 // massive CFGs. Note: CFG.size() could have unreachable blocks,
526 // but it is a faster to compare that to MaxBlockVisits first before doing the
527 // reachable block count.
528 if (CFG.size() > static_cast<size_t>(MaxBlockVisits) &&
529 NumReachableBlocks(CFG) > static_cast<size_t>(MaxBlockVisits)) {
530 return llvm::createStringError(std::errc::timed_out,
531 "number of blocks in cfg will lead to "
532 "exceeding maximum number of block visits");
533 }
534
535 PostOrderCFGView POV(&CFG);
536 ForwardDataflowWorklist Worklist(CFG, &POV);
537 llvm::SmallDenseSet<const CFGBlock *> NonStructLoopBackedgeNodes =
539
540 std::vector<std::optional<TypeErasedDataflowAnalysisState>> BlockStates(
541 CFG.size());
542
543 // The entry basic block doesn't contain statements so it can be skipped.
544 const CFGBlock &Entry = CFG.getEntry();
545 BlockStates[Entry.getBlockID()] = {Analysis.typeErasedInitialElement(),
546 StartingEnv.fork()};
547 Worklist.enqueueSuccessors(&Entry);
548
549 AnalysisContext AC(ACFG, Analysis, StartingEnv, BlockStates);
550 std::int32_t BlockVisits = 0;
551 while (const CFGBlock *Block = Worklist.dequeue()) {
552 LLVM_DEBUG(llvm::dbgs()
553 << "Processing Block " << Block->getBlockID() << "\n");
554 if (++BlockVisits > MaxBlockVisits) {
555 return llvm::createStringError(std::errc::timed_out,
556 "maximum number of blocks processed");
557 }
558
559 const std::optional<TypeErasedDataflowAnalysisState> &OldBlockState =
560 BlockStates[Block->getBlockID()];
561 TypeErasedDataflowAnalysisState NewBlockState =
563 LLVM_DEBUG({
564 llvm::errs() << "New Env:\n";
565 NewBlockState.Env.dump();
566 });
567
568 if (OldBlockState) {
569 LLVM_DEBUG({
570 llvm::errs() << "Old Env:\n";
571 OldBlockState->Env.dump();
572 });
573 if (isBackedgeCFGNode(*Block, NonStructLoopBackedgeNodes)) {
574 LatticeJoinEffect Effect1 = Analysis.widenTypeErased(
575 NewBlockState.Lattice, OldBlockState->Lattice);
576 LatticeJoinEffect Effect2 =
577 NewBlockState.Env.widen(OldBlockState->Env, Analysis);
578 if (Effect1 == LatticeJoinEffect::Unchanged &&
579 Effect2 == LatticeJoinEffect::Unchanged) {
580 // The state of `Block` didn't change from widening so there's no need
581 // to revisit its successors.
582 AC.Log.blockConverged();
583 continue;
584 }
585 } else if (Analysis.isEqualTypeErased(OldBlockState->Lattice,
586 NewBlockState.Lattice) &&
587 OldBlockState->Env.equivalentTo(NewBlockState.Env, Analysis)) {
588 // The state of `Block` didn't change after transfer so there's no need
589 // to revisit its successors.
590 AC.Log.blockConverged();
591 continue;
592 }
593 }
594
595 BlockStates[Block->getBlockID()] = std::move(NewBlockState);
596
597 // Do not add unreachable successor blocks to `Worklist`.
598 if (Block->hasNoReturnElement())
599 continue;
600
601 Worklist.enqueueSuccessors(Block);
602 }
603 // FIXME: Consider evaluating unreachable basic blocks (those that have a
604 // state set to `std::nullopt` at this point) to also analyze dead code.
605
606 if (PostAnalysisCallbacks.Before || PostAnalysisCallbacks.After) {
607 for (const CFGBlock *Block : ACFG.getCFG()) {
608 // Skip blocks that were not evaluated.
609 if (!BlockStates[Block->getBlockID()])
610 continue;
611 transferCFGBlock(*Block, AC, PostAnalysisCallbacks);
612 }
613 }
614
615 return std::move(BlockStates);
616}
617
618} // namespace dataflow
619} // 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)
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
This class represents a potential adjacent block in the CFG.
Definition CFG.h:852
Represents a single basic block in a source-level CFG.
Definition CFG.h:632
succ_range succs()
Definition CFG.h:1027
succ_iterator succ_begin()
Definition CFG.h:1017
unsigned getBlockID() const
Definition CFG.h:1134
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:100
Kind getKind() const
Definition CFG.h:119
Represents C++ base or member initializer from constructor's initialization list.
Definition CFG.h:229
const CXXCtorInitializer * getInitializer() const
Definition CFG.h:234
Represents the point where the lifetime of an automatic object ends.
Definition CFG.h:294
const VarDecl * getVarDecl() const
Definition CFG.h:299
const Stmt * getStmt() const
Definition CFG.h:140
Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt.
Definition CFG.h:1250
unsigned size() const
Return the total number of CFGBlocks within the CFG This is simply a renaming of the getNumBlockIDs()...
Definition CFG.h:1448
CFGBlock & getEntry()
Definition CFG.h:1364
Represents a C++ base or member initializer.
Definition DeclCXX.h:2389
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:3175
IfStmt - This represents an if/then/else.
Definition Stmt.h:2260
Expr * getCond()
Definition Stmt.h:2337
Represents a field injected from an anonymous union/struct into the parent scope.
Definition Decl.h:3482
ArrayRef< NamedDecl * > chain() const
Definition Decl.h:3503
Stmt - This represents one statement.
Definition Stmt.h:86
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
Definition Decl.h:712
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:986
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.
size_t NumReachableBlocks(const CFG &CFG)
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.
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.
bool isBackedgeCFGNode(const CFGBlock &B, const llvm::SmallDenseSet< const CFGBlock * > &NonStructLoopBackedgeNodes)
Given a backedge from B1 to B2, B1 is a "backedge node" in a CFG.
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
llvm::SmallDenseSet< const CFGBlock * > findNonStructuredLoopBackedgeNodes(const CFG &CFG)
Returns a set of CFG blocks that is the source of a backedge and is not tracked as part of a structur...
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).