15#include <system_error>
33#include "llvm/ADT/ArrayRef.h"
34#include "llvm/ADT/STLExtras.h"
35#include "llvm/Support/Debug.h"
36#include "llvm/Support/Error.h"
38#define DEBUG_TYPE "clang-dataflow"
51template struct CLANG_EXPORT_TEMPLATE Any::TypeId<clang::dataflow::NoopLattice>;
60 auto BlockPos = llvm::find_if(
62 return Succ && Succ->getBlockID() == Block.getBlockID();
80class TerminatorVisitor
83 TerminatorVisitor() =
default;
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 *) {
93 const Expr *VisitBinaryOperator(
const BinaryOperator *S) {
94 assert(S->getOpcode() == BO_LAnd || S->getOpcode() == BO_LOr);
97 const Expr *VisitConditionalOperator(
const ConditionalOperator *S) {
103struct AnalysisContext {
104 AnalysisContext(
const AdornedCFG &ACFG, TypeErasedDataflowAnalysis &Analysis,
105 const Environment &InitEnv,
108 : ACFG(ACFG), Analysis(Analysis), InitEnv(InitEnv),
109 Log(*InitEnv.getDataflowAnalysisContext().getOptions().Log),
110 BlockStates(BlockStates) {
111 Log.beginAnalysis(ACFG, Analysis);
113 ~AnalysisContext() { Log.endAnalysis(); }
116 const AdornedCFG &ACFG;
118 TypeErasedDataflowAnalysis &Analysis;
120 const Environment &InitEnv;
127class PrettyStackTraceAnalysis :
public llvm::PrettyStackTraceEntry {
129 PrettyStackTraceAnalysis(
const AdornedCFG &ACFG,
const char *Message)
132 void print(raw_ostream &OS)
const override {
135 ACFG.getDecl().dump(OS);
137 ACFG.getCFG().print(OS, LangOptions(),
false);
141 const AdornedCFG &ACFG;
145class PrettyStackTraceCFGElement :
public llvm::PrettyStackTraceEntry {
147 PrettyStackTraceCFGElement(
const CFGElement &Element,
int BlockIdx,
148 int ElementIdx,
const char *Message)
149 : Element(Element), BlockIdx(BlockIdx), ElementIdx(ElementIdx),
152 void print(raw_ostream &OS)
const override {
153 OS <<
Message <<
": Element [B" << BlockIdx <<
"." << ElementIdx <<
"]\n";
154 if (
auto Stmt = Element.getAs<CFGStmt>()) {
156 ASTDumper Dumper(OS,
false);
157 Dumper.Visit(Stmt->getStmt());
162 const CFGElement ∈
172class JoinedStateBuilder {
174 Environment::ExprJoinBehavior JoinBehavior;
175 std::vector<const TypeErasedDataflowAnalysisState *>
All;
176 std::deque<TypeErasedDataflowAnalysisState> Owned;
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)};
186 JoinedStateBuilder(AnalysisContext &AC,
187 Environment::ExprJoinBehavior JoinBehavior)
188 : AC(AC), JoinBehavior(JoinBehavior) {}
190 void addOwned(TypeErasedDataflowAnalysisState State) {
191 Owned.push_back(std::move(State));
192 All.push_back(&Owned.back());
194 void addUnowned(
const TypeErasedDataflowAnalysisState &State) {
195 All.push_back(&State);
197 TypeErasedDataflowAnalysisState take() && {
202 return {AC.Analysis.typeErasedInitialElement(), AC.InitEnv.fork()};
208 return {
All[0]->Lattice, Environment::join(All[0]->Env, All[0]->Env,
209 AC.Analysis, JoinBehavior)};
211 auto Result = join(*All[0], *All[1]);
212 for (
unsigned I = 2; I <
All.size(); ++I)
213 Result = join(Result, *All[I]);
220 return TerminatorStmt ==
nullptr ?
nullptr
221 : TerminatorVisitor().Visit(TerminatorStmt);
232static TypeErasedDataflowAnalysisState
234 std::vector<const CFGBlock *> Preds(
Block.pred_begin(),
Block.pred_end());
235 if (
Block.getTerminator().isTemporaryDtorsBranch()) {
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())
263 assert(StmtBlock !=
nullptr);
264 llvm::erase(Preds, StmtBlock);
276 for (
const CFGBlock *Pred : Preds) {
283 JoinedStateBuilder Builder(AC, JoinBehavior);
284 for (
const CFGBlock *Pred : Preds) {
286 if (!Pred || Pred->hasNoReturnElement())
291 const std::optional<TypeErasedDataflowAnalysisState> &MaybePredState =
292 AC.BlockStates[Pred->getBlockID()];
298 if (
Cond ==
nullptr) {
299 Builder.addUnowned(PredState);
314 assert(CondVal !=
nullptr);
316 BranchVal ? CondVal : &
Copy.Env.makeNot(*CondVal);
321 Builder.addOwned(std::move(
Copy));
323 return std::move(Builder).take();
330 AnalysisContext &AC) {
332 assert(S !=
nullptr);
334 InputState.
Env, AC.Analysis);
342 assert(
Init !=
nullptr);
344 auto &Env = InputState.
Env;
347 if (!
Init->isAnyMemberInitializer())
351 auto *InitExpr =
Init->getInit();
352 assert(InitExpr !=
nullptr);
357 if (
Init->isMemberInitializer()) {
359 MemberLoc = ThisLoc.getChild(*
Member);
362 assert(IndirectField !=
nullptr);
363 MemberLoc = &ThisLoc;
364 for (
const auto *I : IndirectField->
chain()) {
370 assert(
Member !=
nullptr);
379 if (
Member->getType()->isReferenceType()) {
380 auto *InitExprLoc = Env.getStorageLocation(*InitExpr);
381 if (InitExprLoc ==
nullptr)
387 }
else if (!
Member->getType()->isRecordType()) {
388 assert(MemberLoc !=
nullptr);
389 if (
auto *InitExprVal = Env.getValue(*InitExpr))
390 Env.setValue(*MemberLoc, *InitExprVal);
396 AnalysisContext &AC) {
411 State.Env.removeDecl(*VD);
427static TypeErasedDataflowAnalysisState
431 PostAnalysisCallbacks.After !=
nullptr);
432 auto State = computeBlockInputState(
Block, AC);
435 for (
const auto &Element :
Block) {
436 PrettyStackTraceCFGElement CrashInfo(Element,
Block.getBlockID(),
437 ElementIdx++,
"transferCFGBlock");
441 if (PostAnalysisCallbacks.Before) {
442 PostAnalysisCallbacks.Before(Element, State);
447 builtinTransfer(Block.getBlockID(), Element, State, AC);
453 if (PostAnalysisCallbacks.After) {
454 PostAnalysisCallbacks.After(Element, State);
465 if (
const Expr *TerminatorCond =
466 dyn_cast_or_null<Expr>(Block.getTerminatorCondition())) {
467 if (State.Env.getValue(*TerminatorCond) ==
nullptr)
473 transfer(StmtToEnvMap(AC.ACFG, AC.BlockStates, Block.getBlockID(), State),
474 *TerminatorCond, State.Env, AC.Analysis);
479 if (State.Env.getValue(*TerminatorCond) ==
nullptr)
480 State.Env.setValue(*TerminatorCond, State.Env.makeAtomicBoolValue());
492 std::int32_t MaxBlockVisits) {
493 PrettyStackTraceAnalysis CrashInfo(ACFG,
"runTypeErasedDataflowAnalysis");
495 std::optional<Environment> MaybeStartingEnv;
497 MaybeStartingEnv = InitEnv.
fork();
501 MaybeStartingEnv ? *MaybeStartingEnv : InitEnv;
507 std::vector<std::optional<TypeErasedDataflowAnalysisState>> BlockStates(
516 AnalysisContext AC(ACFG, Analysis, StartingEnv, BlockStates);
517 std::int32_t BlockVisits = 0;
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");
526 const std::optional<TypeErasedDataflowAnalysisState> &OldBlockState =
527 BlockStates[
Block->getBlockID()];
531 llvm::errs() <<
"New Env:\n";
537 llvm::errs() <<
"Old Env:\n";
538 OldBlockState->Env.dump();
542 NewBlockState.
Lattice, OldBlockState->Lattice);
544 NewBlockState.
Env.
widen(OldBlockState->Env, Analysis);
554 OldBlockState->Env.equivalentTo(NewBlockState.
Env, Analysis)) {
562 BlockStates[
Block->getBlockID()] = std::move(NewBlockState);
565 if (
Block->hasNoReturnElement())
573 if (PostAnalysisCallbacks.
Before || PostAnalysisCallbacks.
After) {
576 if (!BlockStates[
Block->getBlockID()])
582 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)
This class represents a potential adjacent block in the CFG.
Represents a single basic block in a source-level CFG.
succ_iterator succ_begin()
const Stmt * getLoopTarget() const
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.
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.
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',...
@ Result
The result type of a method or function.
U cast(CodeGen::Address addr)
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).