Skip to content

Commit d1bedf3

Browse files
committed
[clang][dataflow] Refactor processing of terminator element
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 cb6f657 commit d1bedf3

File tree

1 file changed

+36
-92
lines changed

1 file changed

+36
-92
lines changed

clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp

Lines changed: 36 additions & 92 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 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);
117-
}
118-
119-
TerminatorVisitorRetTy
120-
VisitConditionalOperator(const ConditionalOperator *S) {
121-
auto *Cond = S->getCond();
122-
assert(Cond != nullptr);
123-
return extendFlowCondition(*Cond);
81+
return S->getLHS();
12482
}
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};
83+
const Expr *VisitConditionalOperator(const ConditionalOperator *S) {
84+
return S->getCond();
14585
}
146-
147-
Environment &Env;
148-
int BlockSuccIdx;
14986
};
15087

15188
/// Holds data structures required for running dataflow analysis.
@@ -337,26 +274,33 @@ computeBlockInputState(const CFGBlock &Block, AnalysisContext &AC) {
337274
AC.BlockStates[Pred->getBlockID()];
338275
if (!MaybePredState)
339276
continue;
340-
341-
if (AC.Analysis.builtinOptions()) {
342-
if (const Stmt *PredTerminatorStmt = Pred->getTerminatorStmt()) {
343-
// We have a terminator: we need to mutate an environment to describe
344-
// when the terminator is taken. Copy now.
277+
const TypeErasedDataflowAnalysisState &PredState = *MaybePredState;
278+
279+
if (const Stmt *PredTerminatorStmt = Pred->getTerminatorStmt()) {
280+
bool BranchVal = blockIndexInPredecessor(*Pred, Block) == 0;
281+
const Expr *Cond = TerminatorVisitor().Visit(PredTerminatorStmt);
282+
if (Cond != nullptr) {
283+
// `transferBranch` may need to mutate the environment to describe the
284+
// dynamic effect of the terminator for a given branch. Copy now.
345285
TypeErasedDataflowAnalysisState Copy = MaybePredState->fork();
346-
347-
auto [Cond, CondValue] =
348-
TerminatorVisitor(Copy.Env, blockIndexInPredecessor(*Pred, Block))
349-
.Visit(PredTerminatorStmt);
350-
if (Cond != nullptr)
351-
// FIXME: Call transferBranchTypeErased even if BuiltinTransferOpts
352-
// are not set.
353-
AC.Analysis.transferBranchTypeErased(CondValue, Cond, Copy.Lattice,
354-
Copy.Env);
286+
if (AC.Analysis.builtinOptions()) {
287+
auto *CondVal = Copy.Env.get<BoolValue>(*Cond);
288+
// In transferCFGBlock(), we ensure that we always have a `Value`
289+
// for the terminator condition, so assert this. We consciously
290+
// assert ourselves instead of asserting via `cast()` so that we get
291+
// a more meaningful line number if the assertion fails.
292+
assert(CondVal != nullptr);
293+
BoolValue *AssertedVal =
294+
BranchVal ? CondVal : &Copy.Env.makeNot(*CondVal);
295+
Copy.Env.assume(AssertedVal->formula());
296+
}
297+
AC.Analysis.transferBranchTypeErased(BranchVal, Cond, Copy.Lattice,
298+
Copy.Env);
355299
Builder.addOwned(std::move(Copy));
356300
continue;
357301
}
358-
}
359-
Builder.addUnowned(*MaybePredState);
302+
}
303+
Builder.addUnowned(PredState);
360304
}
361305
return std::move(Builder).take();
362306
}

0 commit comments

Comments
 (0)