|
11 | 11 | // |
12 | 12 | //===----------------------------------------------------------------------===// |
13 | 13 |
|
14 | | -#include <algorithm> |
15 | 14 | #include <optional> |
16 | 15 | #include <system_error> |
17 | 16 | #include <utility> |
|
33 | 32 | #include "clang/Analysis/FlowSensitive/Value.h" |
34 | 33 | #include "llvm/ADT/ArrayRef.h" |
35 | 34 | #include "llvm/ADT/STLExtras.h" |
36 | | -#include "llvm/ADT/SmallBitVector.h" |
37 | 35 | #include "llvm/Support/Debug.h" |
38 | 36 | #include "llvm/Support/Error.h" |
39 | 37 |
|
@@ -64,88 +62,27 @@ static bool isBackedgeNode(const CFGBlock &B) { |
64 | 62 |
|
65 | 63 | namespace { |
66 | 64 |
|
67 | | -// The return type of the visit functions in TerminatorVisitor. The first |
68 | | -// element represents the terminator expression (that is the conditional |
69 | | -// expression in case of a path split in the CFG). The second element |
70 | | -// represents whether the condition was true or false. |
71 | | -using TerminatorVisitorRetTy = std::pair<const Expr *, bool>; |
72 | | - |
73 | | -/// Extends the flow condition of an environment based on a terminator |
74 | | -/// statement. |
| 65 | +/// Extracts the terminator's condition expression. |
75 | 66 | class TerminatorVisitor |
76 | | - : public ConstStmtVisitor<TerminatorVisitor, TerminatorVisitorRetTy> { |
| 67 | + : public ConstStmtVisitor<TerminatorVisitor, const Expr *> { |
77 | 68 | public: |
78 | | - TerminatorVisitor(Environment &Env, int BlockSuccIdx) |
79 | | - : Env(Env), BlockSuccIdx(BlockSuccIdx) {} |
80 | | - |
81 | | - TerminatorVisitorRetTy VisitIfStmt(const IfStmt *S) { |
82 | | - auto *Cond = S->getCond(); |
83 | | - assert(Cond != nullptr); |
84 | | - return extendFlowCondition(*Cond); |
85 | | - } |
86 | | - |
87 | | - TerminatorVisitorRetTy VisitWhileStmt(const WhileStmt *S) { |
88 | | - auto *Cond = S->getCond(); |
89 | | - assert(Cond != nullptr); |
90 | | - return extendFlowCondition(*Cond); |
91 | | - } |
92 | | - |
93 | | - TerminatorVisitorRetTy VisitDoStmt(const DoStmt *S) { |
94 | | - auto *Cond = S->getCond(); |
95 | | - assert(Cond != nullptr); |
96 | | - return extendFlowCondition(*Cond); |
97 | | - } |
98 | | - |
99 | | - TerminatorVisitorRetTy VisitForStmt(const ForStmt *S) { |
100 | | - auto *Cond = S->getCond(); |
101 | | - if (Cond != nullptr) |
102 | | - return extendFlowCondition(*Cond); |
103 | | - return {nullptr, false}; |
104 | | - } |
105 | | - |
106 | | - TerminatorVisitorRetTy VisitCXXForRangeStmt(const CXXForRangeStmt *) { |
| 69 | + TerminatorVisitor() = default; |
| 70 | + const Expr *VisitIfStmt(const IfStmt *S) { return S->getCond(); } |
| 71 | + const Expr *VisitWhileStmt(const WhileStmt *S) { return S->getCond(); } |
| 72 | + const Expr *VisitDoStmt(const DoStmt *S) { return S->getCond(); } |
| 73 | + const Expr *VisitForStmt(const ForStmt *S) { return S->getCond(); } |
| 74 | + const Expr *VisitCXXForRangeStmt(const CXXForRangeStmt *) { |
107 | 75 | // Don't do anything special for CXXForRangeStmt, because the condition |
108 | 76 | // (being implicitly generated) isn't visible from the loop body. |
109 | | - return {nullptr, false}; |
| 77 | + return nullptr; |
110 | 78 | } |
111 | | - |
112 | | - TerminatorVisitorRetTy VisitBinaryOperator(const BinaryOperator *S) { |
| 79 | + const Expr *VisitBinaryOperator(const BinaryOperator *S) { |
113 | 80 | assert(S->getOpcode() == BO_LAnd || S->getOpcode() == BO_LOr); |
114 | | - auto *LHS = S->getLHS(); |
115 | | - assert(LHS != nullptr); |
116 | | - return extendFlowCondition(*LHS); |
| 81 | + return S->getLHS(); |
117 | 82 | } |
118 | | - |
119 | | - TerminatorVisitorRetTy |
120 | | - VisitConditionalOperator(const ConditionalOperator *S) { |
121 | | - auto *Cond = S->getCond(); |
122 | | - assert(Cond != nullptr); |
123 | | - return extendFlowCondition(*Cond); |
| 83 | + const Expr *VisitConditionalOperator(const ConditionalOperator *S) { |
| 84 | + return S->getCond(); |
124 | 85 | } |
125 | | - |
126 | | -private: |
127 | | - TerminatorVisitorRetTy extendFlowCondition(const Expr &Cond) { |
128 | | - auto *Val = Env.get<BoolValue>(Cond); |
129 | | - // In transferCFGBlock(), we ensure that we always have a `Value` for the |
130 | | - // terminator condition, so assert this. |
131 | | - // We consciously assert ourselves instead of asserting via `cast()` so |
132 | | - // that we get a more meaningful line number if the assertion fails. |
133 | | - assert(Val != nullptr); |
134 | | - |
135 | | - bool ConditionValue = true; |
136 | | - // The condition must be inverted for the successor that encompasses the |
137 | | - // "else" branch, if such exists. |
138 | | - if (BlockSuccIdx == 1) { |
139 | | - Val = &Env.makeNot(*Val); |
140 | | - ConditionValue = false; |
141 | | - } |
142 | | - |
143 | | - Env.assume(Val->formula()); |
144 | | - return {&Cond, ConditionValue}; |
145 | | - } |
146 | | - |
147 | | - Environment &Env; |
148 | | - int BlockSuccIdx; |
149 | 86 | }; |
150 | 87 |
|
151 | 88 | /// Holds data structures required for running dataflow analysis. |
@@ -263,9 +200,13 @@ class JoinedStateBuilder { |
263 | 200 | return Result; |
264 | 201 | } |
265 | 202 | }; |
266 | | - |
267 | 203 | } // namespace |
268 | 204 |
|
| 205 | +static const Expr *getTerminatorCondition(const Stmt *TerminatorStmt) { |
| 206 | + return TerminatorStmt == nullptr ? nullptr |
| 207 | + : TerminatorVisitor().Visit(TerminatorStmt); |
| 208 | +} |
| 209 | + |
269 | 210 | /// Computes the input state for a given basic block by joining the output |
270 | 211 | /// states of its predecessors. |
271 | 212 | /// |
@@ -337,25 +278,32 @@ computeBlockInputState(const CFGBlock &Block, AnalysisContext &AC) { |
337 | 278 | if (!MaybePredState) |
338 | 279 | continue; |
339 | 280 |
|
| 281 | + const TypeErasedDataflowAnalysisState &PredState = *MaybePredState; |
| 282 | + const Expr *Cond = getTerminatorCondition(Pred->getTerminatorStmt()); |
| 283 | + if (Cond == nullptr) { |
| 284 | + Builder.addUnowned(PredState); |
| 285 | + continue; |
| 286 | + } |
| 287 | + |
| 288 | + bool BranchVal = blockIndexInPredecessor(*Pred, Block) == 0; |
| 289 | + |
| 290 | + // `transferBranch` may need to mutate the environment to describe the |
| 291 | + // dynamic effect of the terminator for a given branch. Copy now. |
| 292 | + TypeErasedDataflowAnalysisState Copy = MaybePredState->fork(); |
340 | 293 | if (AC.Analysis.builtinOptions()) { |
341 | | - if (const Stmt *PredTerminatorStmt = Pred->getTerminatorStmt()) { |
342 | | - // We have a terminator: we need to mutate an environment to describe |
343 | | - // when the terminator is taken. Copy now. |
344 | | - TypeErasedDataflowAnalysisState Copy = MaybePredState->fork(); |
345 | | - |
346 | | - auto [Cond, CondValue] = |
347 | | - TerminatorVisitor(Copy.Env, blockIndexInPredecessor(*Pred, Block)) |
348 | | - .Visit(PredTerminatorStmt); |
349 | | - if (Cond != nullptr) |
350 | | - // FIXME: Call transferBranchTypeErased even if BuiltinTransferOpts |
351 | | - // are not set. |
352 | | - AC.Analysis.transferBranchTypeErased(CondValue, Cond, Copy.Lattice, |
353 | | - Copy.Env); |
354 | | - Builder.addOwned(std::move(Copy)); |
355 | | - continue; |
356 | | - } |
| 294 | + auto *CondVal = Copy.Env.get<BoolValue>(*Cond); |
| 295 | + // In transferCFGBlock(), we ensure that we always have a `Value` |
| 296 | + // for the terminator condition, so assert this. We consciously |
| 297 | + // assert ourselves instead of asserting via `cast()` so that we get |
| 298 | + // a more meaningful line number if the assertion fails. |
| 299 | + assert(CondVal != nullptr); |
| 300 | + BoolValue *AssertedVal = |
| 301 | + BranchVal ? CondVal : &Copy.Env.makeNot(*CondVal); |
| 302 | + Copy.Env.assume(AssertedVal->formula()); |
357 | 303 | } |
358 | | - Builder.addUnowned(*MaybePredState); |
| 304 | + AC.Analysis.transferBranchTypeErased(BranchVal, Cond, Copy.Lattice, |
| 305 | + Copy.Env); |
| 306 | + Builder.addOwned(std::move(Copy)); |
359 | 307 | } |
360 | 308 | return std::move(Builder).take(); |
361 | 309 | } |
|
0 commit comments