16#include <system_error>
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"
45#define DEBUG_TYPE "clang-dataflow"
58template struct CLANG_EXPORT_TEMPLATE Any::TypeId<clang::dataflow::NoopLattice>;
67 auto BlockPos = llvm::find_if(
69 return Succ && Succ->getBlockID() == Block.getBlockID();
77class TerminatorVisitor
80 TerminatorVisitor() =
default;
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 *) {
90 const Expr *VisitBinaryOperator(
const BinaryOperator *S) {
91 assert(S->getOpcode() == BO_LAnd || S->getOpcode() == BO_LOr);
94 const Expr *VisitConditionalOperator(
const ConditionalOperator *S) {
100struct AnalysisContext {
101 AnalysisContext(
const AdornedCFG &ACFG, TypeErasedDataflowAnalysis &Analysis,
102 const Environment &InitEnv,
105 : ACFG(ACFG), Analysis(Analysis), InitEnv(InitEnv),
106 Log(*InitEnv.getDataflowAnalysisContext().getOptions().Log),
107 BlockStates(BlockStates) {
108 Log.beginAnalysis(ACFG, Analysis);
110 ~AnalysisContext() { Log.endAnalysis(); }
113 const AdornedCFG &ACFG;
115 TypeErasedDataflowAnalysis &Analysis;
117 const Environment &InitEnv;
124class PrettyStackTraceAnalysis :
public llvm::PrettyStackTraceEntry {
126 PrettyStackTraceAnalysis(
const AdornedCFG &ACFG,
const char *Message)
129 void print(raw_ostream &OS)
const override {
132 ACFG.getDecl().dump(OS);
134 ACFG.getCFG().print(OS, LangOptions(),
false);
138 const AdornedCFG &ACFG;
142class PrettyStackTraceCFGElement :
public llvm::PrettyStackTraceEntry {
144 PrettyStackTraceCFGElement(
const CFGElement &Element,
int BlockIdx,
145 int ElementIdx,
const char *Message)
146 : Element(Element), BlockIdx(BlockIdx), ElementIdx(ElementIdx),
149 void print(raw_ostream &OS)
const override {
150 OS <<
Message <<
": Element [B" << BlockIdx <<
"." << ElementIdx <<
"]\n";
151 if (
auto Stmt = Element.getAs<CFGStmt>()) {
153 ASTDumper Dumper(OS,
false);
154 Dumper.Visit(Stmt->getStmt());
159 const CFGElement ∈
169class JoinedStateBuilder {
171 Environment::ExprJoinBehavior JoinBehavior;
172 std::vector<const TypeErasedDataflowAnalysisState *>
All;
173 std::deque<TypeErasedDataflowAnalysisState> Owned;
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)};
183 JoinedStateBuilder(AnalysisContext &AC,
184 Environment::ExprJoinBehavior JoinBehavior)
185 : AC(AC), JoinBehavior(JoinBehavior) {}
187 void addOwned(TypeErasedDataflowAnalysisState State) {
188 Owned.push_back(std::move(State));
189 All.push_back(&Owned.back());
191 void addUnowned(
const TypeErasedDataflowAnalysisState &State) {
192 All.push_back(&State);
194 TypeErasedDataflowAnalysisState take() && {
199 return {AC.Analysis.typeErasedInitialElement(), AC.InitEnv.fork()};
205 return {
All[0]->Lattice, Environment::join(All[0]->Env, All[0]->Env,
206 AC.Analysis, JoinBehavior)};
208 auto Result = join(*All[0], *All[1]);
209 for (
unsigned I = 2; I <
All.size(); ++I)
210 Result = join(Result, *All[I]);
217 return TerminatorStmt ==
nullptr ?
nullptr
218 : TerminatorVisitor().Visit(TerminatorStmt);
229static TypeErasedDataflowAnalysisState
231 std::vector<const CFGBlock *> Preds(
Block.pred_begin(),
Block.pred_end());
232 if (
Block.getTerminator().isTemporaryDtorsBranch()) {
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())
260 assert(StmtBlock !=
nullptr);
261 llvm::erase(Preds, StmtBlock);
273 for (
const CFGBlock *Pred : Preds) {
280 JoinedStateBuilder Builder(AC, JoinBehavior);
281 for (
const CFGBlock *Pred : Preds) {
283 if (!Pred || Pred->hasNoReturnElement())
288 const std::optional<TypeErasedDataflowAnalysisState> &MaybePredState =
289 AC.BlockStates[Pred->getBlockID()];
295 if (
Cond ==
nullptr) {
296 Builder.addUnowned(PredState);
311 assert(CondVal !=
nullptr);
313 BranchVal ? CondVal : &
Copy.Env.makeNot(*CondVal);
318 Builder.addOwned(std::move(
Copy));
320 return std::move(Builder).take();
327 AnalysisContext &AC) {
329 assert(S !=
nullptr);
331 InputState.
Env, AC.Analysis);
339 assert(
Init !=
nullptr);
341 auto &Env = InputState.
Env;
344 if (!
Init->isAnyMemberInitializer())
348 auto *InitExpr =
Init->getInit();
349 assert(InitExpr !=
nullptr);
354 if (
Init->isMemberInitializer()) {
356 MemberLoc = ThisLoc.getChild(*
Member);
359 assert(IndirectField !=
nullptr);
360 MemberLoc = &ThisLoc;
361 for (
const auto *I : IndirectField->
chain()) {
367 assert(
Member !=
nullptr);
376 if (
Member->getType()->isReferenceType()) {
377 auto *InitExprLoc = Env.getStorageLocation(*InitExpr);
378 if (InitExprLoc ==
nullptr)
384 }
else if (!
Member->getType()->isRecordType()) {
385 assert(MemberLoc !=
nullptr);
386 if (
auto *InitExprVal = Env.getValue(*InitExpr))
387 Env.setValue(*MemberLoc, *InitExprVal);
393 AnalysisContext &AC) {
408 State.Env.removeDecl(*VD);
424static TypeErasedDataflowAnalysisState
428 PostAnalysisCallbacks.After !=
nullptr);
429 auto State = computeBlockInputState(
Block, AC);
432 for (
const auto &Element :
Block) {
433 PrettyStackTraceCFGElement CrashInfo(Element,
Block.getBlockID(),
434 ElementIdx++,
"transferCFGBlock");
438 if (PostAnalysisCallbacks.Before) {
439 PostAnalysisCallbacks.Before(Element, State);
444 builtinTransfer(Block.getBlockID(), Element, State, AC);
450 if (PostAnalysisCallbacks.After) {
451 PostAnalysisCallbacks.After(Element, State);
462 if (
const Expr *TerminatorCond =
463 dyn_cast_or_null<Expr>(Block.getTerminatorCondition())) {
464 if (State.Env.getValue(*TerminatorCond) ==
nullptr)
470 transfer(StmtToEnvMap(AC.ACFG, AC.BlockStates, Block.getBlockID(), State),
471 *TerminatorCond, State.Env, AC.Analysis);
476 if (State.Env.getValue(*TerminatorCond) ==
nullptr)
477 State.Env.setValue(*TerminatorCond, State.Env.makeAtomicBoolValue());
490 llvm::BitVector VisitedBlocks(
CFG.
size());
494 if (VisitedBlocks[
Block->getBlockID()])
496 VisitedBlocks[
Block->getBlockID()] =
true;
498 if (
Block->hasNoReturnElement())
502 return VisitedBlocks.count();
510 std::int32_t MaxBlockVisits) {
511 PrettyStackTraceAnalysis CrashInfo(ACFG,
"runTypeErasedDataflowAnalysis");
513 std::optional<Environment> MaybeStartingEnv;
515 MaybeStartingEnv = InitEnv.
fork();
519 MaybeStartingEnv ? *MaybeStartingEnv : InitEnv;
528 if (
CFG.
size() >
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");
537 llvm::SmallDenseSet<const CFGBlock *> NonStructLoopBackedgeNodes =
540 std::vector<std::optional<TypeErasedDataflowAnalysisState>> BlockStates(
549 AnalysisContext AC(ACFG, Analysis, StartingEnv, BlockStates);
550 std::int32_t BlockVisits = 0;
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");
559 const std::optional<TypeErasedDataflowAnalysisState> &OldBlockState =
560 BlockStates[
Block->getBlockID()];
564 llvm::errs() <<
"New Env:\n";
570 llvm::errs() <<
"Old Env:\n";
571 OldBlockState->Env.dump();
575 NewBlockState.
Lattice, OldBlockState->Lattice);
577 NewBlockState.
Env.
widen(OldBlockState->Env, Analysis);
587 OldBlockState->Env.equivalentTo(NewBlockState.
Env, Analysis)) {
595 BlockStates[
Block->getBlockID()] = std::move(NewBlockState);
598 if (
Block->hasNoReturnElement())
606 if (PostAnalysisCallbacks.
Before || PostAnalysisCallbacks.
After) {
609 if (!BlockStates[
Block->getBlockID()])
615 return std::move(BlockStates);
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.
Represents a single basic block in a source-level CFG.
succ_iterator succ_begin()
unsigned getBlockID() const
Represents a top-level expression in a basic block.
T castAs() const
Convert to the specified CFGElement type, asserting that this CFGElement is of the desired type.
Represents C++ base or member initializer from constructor's initialization list.
const CXXCtorInitializer * getInitializer() const
Represents the point where the lifetime of an automatic object ends.
const VarDecl * getVarDecl() const
const Stmt * getStmt() const
Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt.
unsigned size() const
Return the total number of CFGBlocks within the CFG This is simply a renaming of the getNumBlockIDs()...
Represents a C++ base or member initializer.
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
const CFGBlock * dequeue()
This represents one expression.
Represents a member of a struct/union/class.
IfStmt - This represents an if/then/else.
Represents a field injected from an anonymous union/struct into the parent scope.
ArrayRef< NamedDecl * > chain() const
Stmt - This represents one statement.
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
Holds CFG with additional information derived from it that is needed to perform dataflow analysis.
const CFG & getCFG() const
Returns the CFG that is stored in this context.
const CFGBlock * blockForStmt(const Stmt &S) const
Returns the basic block that contains S, or null if no basic block containing S is found.
bool containsExprConsumedInDifferentBlock(const CFGBlock &B) const
Returns whether B contains an expression that is consumed in a different block than B (i....
const Formula & formula() const
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.
virtual void enterElement(const CFGElement &)
Called when we start processing an element in the current CFG block.
virtual void recordState(TypeErasedDataflowAnalysisState &)
Records the analysis state computed for the current program point.
virtual void blockConverged()
Records that the analysis state for the current block is now final.
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.
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
void transfer(const StmtToEnvMap &StmtToEnv, const Stmt &S, Environment &Env, Environment::ValueModel &Model)
Evaluates S and updates Env accordingly.
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',...
@ Result
The result type of a method or function.
U cast(CodeGen::Address addr)
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.
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.
CFGEltCallbackTypeErased Before
CFGEltCallbackTypeErased After
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).