Skip to content

Commit c1328db

Browse files
authored
[clang][dataflow] Refactor processing of terminator element (#84499)
This patch vastly simplifies the code handling terminators, without changing any behavior. Additionally, the simplification unblocks our ability to address a (simple) FIXME in the code to invoke `transferBranch`, even when builtin options are disabled.
1 parent 5b59b3a commit c1328db

File tree

1 file changed

+42
-94
lines changed

1 file changed

+42
-94
lines changed

clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp

Lines changed: 42 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
//
1212
//===----------------------------------------------------------------------===//
1313

14-
#include <algorithm>
1514
#include <optional>
1615
#include <system_error>
1716
#include <utility>
@@ -33,7 +32,6 @@
3332
#include "clang/Analysis/FlowSensitive/Value.h"
3433
#include "llvm/ADT/ArrayRef.h"
3534
#include "llvm/ADT/STLExtras.h"
36-
#include "llvm/ADT/SmallBitVector.h"
3735
#include "llvm/Support/Debug.h"
3836
#include "llvm/Support/Error.h"
3937

@@ -64,88 +62,27 @@ static bool isBackedgeNode(const CFGBlock &B) {
6462

6563
namespace {
6664

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.
7566
class TerminatorVisitor
76-
: public ConstStmtVisitor<TerminatorVisitor, TerminatorVisitorRetTy> {
67+
: public ConstStmtVisitor<TerminatorVisitor, const Expr *> {
7768
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 *) {
10775
// Don't do anything special for CXXForRangeStmt, because the condition
10876
// (being implicitly generated) isn't visible from the loop body.
109-
return {nullptr, false};
77+
return nullptr;
11078
}
111-
112-
TerminatorVisitorRetTy VisitBinaryOperator(const BinaryOperator *S) {
79+
const Expr *VisitBinaryOperator(const BinaryOperator *S) {
11380
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();
11782
}
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();
12485
}
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;
14986
};
15087

15188
/// Holds data structures required for running dataflow analysis.
@@ -263,9 +200,13 @@ class JoinedStateBuilder {
263200
return Result;
264201
}
265202
};
266-
267203
} // namespace
268204

205+
static const Expr *getTerminatorCondition(const Stmt *TerminatorStmt) {
206+
return TerminatorStmt == nullptr ? nullptr
207+
: TerminatorVisitor().Visit(TerminatorStmt);
208+
}
209+
269210
/// Computes the input state for a given basic block by joining the output
270211
/// states of its predecessors.
271212
///
@@ -337,25 +278,32 @@ computeBlockInputState(const CFGBlock &Block, AnalysisContext &AC) {
337278
if (!MaybePredState)
338279
continue;
339280

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();
340293
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());
357303
}
358-
Builder.addUnowned(*MaybePredState);
304+
AC.Analysis.transferBranchTypeErased(BranchVal, Cond, Copy.Lattice,
305+
Copy.Env);
306+
Builder.addOwned(std::move(Copy));
359307
}
360308
return std::move(Builder).take();
361309
}

0 commit comments

Comments
 (0)