15#include <system_error>
36#include "llvm/ADT/ArrayRef.h"
37#include "llvm/ADT/DenseMap.h"
38#include "llvm/ADT/DenseSet.h"
39#include "llvm/ADT/STLExtras.h"
40#include "llvm/Support/Debug.h"
41#include "llvm/Support/Error.h"
43#define DEBUG_TYPE "clang-dataflow"
56template struct CLANG_EXPORT_TEMPLATE Any::TypeId<clang::dataflow::NoopLattice>;
65 auto BlockPos = llvm::find_if(
67 return Succ && Succ->getBlockID() == Block.getBlockID();
75class TerminatorVisitor
78 TerminatorVisitor() =
default;
80 const Expr *VisitWhileStmt(
const WhileStmt *S) {
return S->getCond(); }
81 const Expr *VisitDoStmt(
const DoStmt *S) {
return S->getCond(); }
82 const Expr *VisitForStmt(
const ForStmt *S) {
return S->getCond(); }
83 const Expr *VisitCXXForRangeStmt(
const CXXForRangeStmt *) {
88 const Expr *VisitBinaryOperator(
const BinaryOperator *S) {
89 assert(S->getOpcode() == BO_LAnd || S->getOpcode() == BO_LOr);
92 const Expr *VisitConditionalOperator(
const ConditionalOperator *S) {
98struct AnalysisContext {
99 AnalysisContext(
const AdornedCFG &ACFG, TypeErasedDataflowAnalysis &Analysis,
100 const Environment &InitEnv,
103 : ACFG(ACFG), Analysis(Analysis), InitEnv(InitEnv),
104 Log(*InitEnv.getDataflowAnalysisContext().getOptions().Log),
105 BlockStates(BlockStates) {
106 Log.beginAnalysis(ACFG, Analysis);
108 ~AnalysisContext() { Log.endAnalysis(); }
111 const AdornedCFG &ACFG;
113 TypeErasedDataflowAnalysis &Analysis;
115 const Environment &InitEnv;
122class PrettyStackTraceAnalysis :
public llvm::PrettyStackTraceEntry {
124 PrettyStackTraceAnalysis(
const AdornedCFG &ACFG,
const char *Message)
127 void print(raw_ostream &OS)
const override {
130 ACFG.getDecl().dump(OS);
132 ACFG.getCFG().print(OS, LangOptions(),
false);
136 const AdornedCFG &ACFG;
140class PrettyStackTraceCFGElement :
public llvm::PrettyStackTraceEntry {
142 PrettyStackTraceCFGElement(
const CFGElement &Element,
int BlockIdx,
143 int ElementIdx,
const char *Message)
144 : Element(Element), BlockIdx(BlockIdx), ElementIdx(ElementIdx),
147 void print(raw_ostream &OS)
const override {
148 OS <<
Message <<
": Element [B" << BlockIdx <<
"." << ElementIdx <<
"]\n";
149 if (
auto Stmt = Element.getAs<CFGStmt>()) {
151 ASTDumper Dumper(OS,
false);
152 Dumper.Visit(Stmt->getStmt());
157 const CFGElement ∈
167class JoinedStateBuilder {
169 Environment::ExprJoinBehavior JoinBehavior;
170 std::vector<const TypeErasedDataflowAnalysisState *>
All;
171 std::deque<TypeErasedDataflowAnalysisState> Owned;
173 TypeErasedDataflowAnalysisState
174 join(
const TypeErasedDataflowAnalysisState &L,
175 const TypeErasedDataflowAnalysisState &R) {
176 return {AC.Analysis.joinTypeErased(L.Lattice,
R.Lattice),
177 Environment::join(L.Env,
R.Env, AC.Analysis, JoinBehavior)};
181 JoinedStateBuilder(AnalysisContext &AC,
182 Environment::ExprJoinBehavior JoinBehavior)
183 : AC(AC), JoinBehavior(JoinBehavior) {}
185 void addOwned(TypeErasedDataflowAnalysisState State) {
186 Owned.push_back(std::move(State));
187 All.push_back(&Owned.back());
189 void addUnowned(
const TypeErasedDataflowAnalysisState &State) {
190 All.push_back(&State);
192 TypeErasedDataflowAnalysisState take() && {
197 return {AC.Analysis.typeErasedInitialElement(), AC.InitEnv.fork()};
203 return {
All[0]->Lattice, Environment::join(All[0]->Env, All[0]->Env,
204 AC.Analysis, JoinBehavior)};
206 auto Result = join(*All[0], *All[1]);
207 for (
unsigned I = 2; I <
All.size(); ++I)
208 Result = join(Result, *All[I]);
215 return TerminatorStmt ==
nullptr ?
nullptr
216 : TerminatorVisitor().Visit(TerminatorStmt);
227static TypeErasedDataflowAnalysisState
229 std::vector<const CFGBlock *> Preds(
Block.pred_begin(),
Block.pred_end());
230 if (
Block.getTerminator().isTemporaryDtorsBranch()) {
253 if (
Block.succ_begin()->getReachableBlock() !=
nullptr &&
254 Block.succ_begin()->getReachableBlock()->hasNoReturnElement()) {
255 const CFGBlock *StmtBlock =
nullptr;
256 if (
const Stmt *Terminator =
Block.getTerminatorStmt())
258 assert(StmtBlock !=
nullptr);
259 llvm::erase(Preds, StmtBlock);
271 for (
const CFGBlock *Pred : Preds) {
278 JoinedStateBuilder Builder(AC, JoinBehavior);
279 for (
const CFGBlock *Pred : Preds) {
281 if (!Pred || Pred->hasNoReturnElement())
286 const std::optional<TypeErasedDataflowAnalysisState> &MaybePredState =
287 AC.BlockStates[Pred->getBlockID()];
293 if (
Cond ==
nullptr) {
294 Builder.addUnowned(PredState);
309 assert(CondVal !=
nullptr);
311 BranchVal ? CondVal : &
Copy.Env.makeNot(*CondVal);
316 Builder.addOwned(std::move(
Copy));
318 return std::move(Builder).take();
325 AnalysisContext &AC) {
327 assert(S !=
nullptr);
329 InputState.
Env, AC.Analysis);
337 assert(
Init !=
nullptr);
339 auto &Env = InputState.
Env;
342 if (!
Init->isAnyMemberInitializer())
346 auto *InitExpr =
Init->getInit();
347 assert(InitExpr !=
nullptr);
352 if (
Init->isMemberInitializer()) {
354 MemberLoc = ThisLoc.getChild(*
Member);
357 assert(IndirectField !=
nullptr);
358 MemberLoc = &ThisLoc;
359 for (
const auto *I : IndirectField->
chain()) {
365 assert(
Member !=
nullptr);
374 if (
Member->getType()->isReferenceType()) {
375 auto *InitExprLoc = Env.getStorageLocation(*InitExpr);
376 if (InitExprLoc ==
nullptr)
382 }
else if (!
Member->getType()->isRecordType()) {
383 assert(MemberLoc !=
nullptr);
384 if (
auto *InitExprVal = Env.getValue(*InitExpr))
385 Env.setValue(*MemberLoc, *InitExprVal);
391 AnalysisContext &AC) {
406 State.Env.removeDecl(*VD);
422static TypeErasedDataflowAnalysisState
426 PostAnalysisCallbacks.After !=
nullptr);
427 auto State = computeBlockInputState(
Block, AC);
430 for (
const auto &Element :
Block) {
431 PrettyStackTraceCFGElement CrashInfo(Element,
Block.getBlockID(),
432 ElementIdx++,
"transferCFGBlock");
436 if (PostAnalysisCallbacks.Before) {
437 PostAnalysisCallbacks.Before(Element, State);
442 builtinTransfer(Block.getBlockID(), Element, State, AC);
448 if (PostAnalysisCallbacks.After) {
449 PostAnalysisCallbacks.After(Element, State);
460 if (
const Expr *TerminatorCond =
461 dyn_cast_or_null<Expr>(Block.getTerminatorCondition())) {
462 if (State.Env.getValue(*TerminatorCond) ==
nullptr)
468 transfer(StmtToEnvMap(AC.ACFG, AC.BlockStates, Block.getBlockID(), State),
469 *TerminatorCond, State.Env, AC.Analysis);
474 if (State.Env.getValue(*TerminatorCond) ==
nullptr)
475 State.Env.setValue(*TerminatorCond, State.Env.makeAtomicBoolValue());
487 std::int32_t MaxBlockVisits) {
488 PrettyStackTraceAnalysis CrashInfo(ACFG,
"runTypeErasedDataflowAnalysis");
490 std::optional<Environment> MaybeStartingEnv;
492 MaybeStartingEnv = InitEnv.
fork();
496 MaybeStartingEnv ? *MaybeStartingEnv : InitEnv;
501 llvm::SmallDenseSet<const CFGBlock *> NonStructLoopBackedgeNodes =
504 std::vector<std::optional<TypeErasedDataflowAnalysisState>> BlockStates(
513 AnalysisContext AC(ACFG, Analysis, StartingEnv, BlockStates);
514 std::int32_t BlockVisits = 0;
516 LLVM_DEBUG(llvm::dbgs()
517 <<
"Processing Block " <<
Block->getBlockID() <<
"\n");
518 if (++BlockVisits > MaxBlockVisits) {
519 return llvm::createStringError(std::errc::timed_out,
520 "maximum number of blocks processed");
523 const std::optional<TypeErasedDataflowAnalysisState> &OldBlockState =
524 BlockStates[
Block->getBlockID()];
528 llvm::errs() <<
"New Env:\n";
534 llvm::errs() <<
"Old Env:\n";
535 OldBlockState->Env.dump();
539 NewBlockState.
Lattice, OldBlockState->Lattice);
541 NewBlockState.
Env.
widen(OldBlockState->Env, Analysis);
551 OldBlockState->Env.equivalentTo(NewBlockState.
Env, Analysis)) {
559 BlockStates[
Block->getBlockID()] = std::move(NewBlockState);
562 if (
Block->hasNoReturnElement())
570 if (PostAnalysisCallbacks.
Before || PostAnalysisCallbacks.
After) {
573 if (!BlockStates[
Block->getBlockID()])
579 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.
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).