Skip to content

Commit 537bbb4

Browse files
authored
[clang][dataflow] Process terminator condition within transferCFGBlock(). (#77750)
In particular, it's important that we create the "fallback" atomic at this point (which we produce if the transfer function didn't produce a value for the expression) so that it is placed in the correct environment. Previously, we processed the terminator condition in the `TerminatorVisitor`, which put the fallback atomic in a copy of the environment that is produced as input for the _successor_ block, rather than the environment for the block containing the expression for which we produce the fallback atomic. As a result, we produce different fallback atomics every time we process the successor block, and hence we don't have a consistent representation of the terminator condition in the flow condition. This patch includes a test (authored by ymand@) that fails without the fix.
1 parent dabc901 commit 537bbb4

File tree

3 files changed

+62
-12
lines changed

3 files changed

+62
-12
lines changed

clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -126,19 +126,12 @@ class TerminatorVisitor
126126

127127
private:
128128
TerminatorVisitorRetTy extendFlowCondition(const Expr &Cond) {
129-
// The terminator sub-expression might not be evaluated.
130-
if (Env.getValue(Cond) == nullptr)
131-
transfer(StmtToEnv, Cond, Env);
132-
133129
auto *Val = Env.get<BoolValue>(Cond);
134-
// Value merging depends on flow conditions from different environments
135-
// being mutually exclusive -- that is, they cannot both be true in their
136-
// entirety (even if they may share some clauses). So, we need *some* value
137-
// for the condition expression, even if just an atom.
138-
if (Val == nullptr) {
139-
Val = &Env.makeAtomicBoolValue();
140-
Env.setValue(Cond, *Val);
141-
}
130+
// In transferCFGBlock(), we ensure that we always have a `Value` for the
131+
// terminator condition, so assert this.
132+
// We consciously assert ourselves instead of asserting via `cast()` so
133+
// that we get a more meaningful line number if the assertion fails.
134+
assert(Val != nullptr);
142135

143136
bool ConditionValue = true;
144137
// The condition must be inverted for the successor that encompasses the
@@ -489,6 +482,31 @@ transferCFGBlock(const CFGBlock &Block, AnalysisContext &AC,
489482
}
490483
AC.Log.recordState(State);
491484
}
485+
486+
// If we have a terminator, evaluate its condition.
487+
// This `Expr` may not appear as a `CFGElement` anywhere else, and it's
488+
// important that we evaluate it here (rather than while processing the
489+
// terminator) so that we put the corresponding value in the right
490+
// environment.
491+
if (const Expr *TerminatorCond =
492+
dyn_cast_or_null<Expr>(Block.getTerminatorCondition())) {
493+
if (State.Env.getValue(*TerminatorCond) == nullptr)
494+
// FIXME: This only runs the builtin transfer, not the analysis-specific
495+
// transfer. Fixing this isn't trivial, as the analysis-specific transfer
496+
// takes a `CFGElement` as input, but some expressions only show up as a
497+
// terminator condition, but not as a `CFGElement`. The condition of an if
498+
// statement is one such example.
499+
transfer(StmtToEnvMap(AC.CFCtx, AC.BlockStates), *TerminatorCond,
500+
State.Env);
501+
502+
// If the transfer function didn't produce a value, create an atom so that
503+
// we have *some* value for the condition expression. This ensures that
504+
// when we extend the flow condition, it actually changes.
505+
if (State.Env.getValue(*TerminatorCond) == nullptr)
506+
State.Env.setValue(*TerminatorCond, State.Env.makeAtomicBoolValue());
507+
AC.Log.recordState(State);
508+
}
509+
492510
return State;
493511
}
494512

clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ recordState(Elements=1, Branches=0, Joins=0)
123123
enterElement(b (ImplicitCastExpr, LValueToRValue, _Bool))
124124
transfer()
125125
recordState(Elements=2, Branches=0, Joins=0)
126+
recordState(Elements=2, Branches=0, Joins=0)
126127
127128
enterBlock(3, false)
128129
transferBranch(0)

clang/unittests/Analysis/FlowSensitive/TransferTest.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6408,4 +6408,35 @@ TEST(TransferTest, DifferentReferenceLocInJoin) {
64086408
});
64096409
}
64106410

6411+
// This test verifies correct modeling of a relational dependency that goes
6412+
// through unmodeled functions (the simple `cond()` in this case).
6413+
TEST(TransferTest, ConditionalRelation) {
6414+
std::string Code = R"(
6415+
bool cond();
6416+
void target() {
6417+
bool a = true;
6418+
bool b = true;
6419+
if (cond()) {
6420+
a = false;
6421+
if (cond()) {
6422+
b = false;
6423+
}
6424+
}
6425+
(void)0;
6426+
// [[p]]
6427+
}
6428+
)";
6429+
runDataflow(
6430+
Code,
6431+
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6432+
ASTContext &ASTCtx) {
6433+
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
6434+
auto &A = Env.arena();
6435+
auto &VarA = getValueForDecl<BoolValue>(ASTCtx, Env, "a").formula();
6436+
auto &VarB = getValueForDecl<BoolValue>(ASTCtx, Env, "b").formula();
6437+
6438+
EXPECT_FALSE(Env.allows(A.makeAnd(VarA, A.makeNot(VarB))));
6439+
});
6440+
}
6441+
64116442
} // namespace

0 commit comments

Comments
 (0)