|
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