Skip to content

Commit 21ab252

Browse files
authored
[dataflow] Add global invariant condition to DataflowAnalysisContext (#65949)
This records facts that are not sensitive to the current flow condition, and should apply to all environments. The motivating case is recording information about where a Value originated, such as nullability: - we may see the same Value for multiple expressions (e.g. reads of the same field) in multiple environments (multiple blocks or iterations) - we want to record information only when we first see the Value (e.g. Nullability annotations on fields only add information if we don't know where the value came from) - this information should be expressible as a SAT condition - we must add this SAT condition to every environment where the Value may appear We solve this by recording the information in the global condition. This doesn't seem particularly elegant, but solves the problem and is a fairly small and natural extension of the Environment. Alternatives considered: - store the constraint directly as a property on the Value. But it's more composable for such properties to always be variables (AtomicBoolValue), and constrain them with SAT conditions. - add a hook whenever values are created, giving the analysis the chance to populate them. However the framework relies on/provides the ability to construct values in arbitrary places without providing the context such a hook would need, this would be a very invasive change.
1 parent 2f377c5 commit 21ab252

File tree

3 files changed

+67
-33
lines changed

3 files changed

+67
-33
lines changed

clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,15 @@ class DataflowAnalysisContext {
108108
/// A null `PointeeType` can be used for the pointee of `std::nullptr_t`.
109109
PointerValue &getOrCreateNullPointerValue(QualType PointeeType);
110110

111+
/// Adds `Constraint` to current and future flow conditions in this context.
112+
///
113+
/// Invariants must contain only flow-insensitive information, i.e. facts that
114+
/// are true on all paths through the program.
115+
/// Information can be added eagerly (when analysis begins), or lazily (e.g.
116+
/// when values are first used). The analysis must be careful that the same
117+
/// information is added regardless of which order blocks are analyzed in.
118+
void addInvariant(const Formula &Constraint);
119+
111120
/// Adds `Constraint` to the flow condition identified by `Token`.
112121
void addFlowConditionConstraint(Atom Token, const Formula &Constraint);
113122

@@ -174,12 +183,11 @@ class DataflowAnalysisContext {
174183
void addModeledFields(const FieldSet &Fields);
175184

176185
/// Adds all constraints of the flow condition identified by `Token` and all
177-
/// of its transitive dependencies to `Constraints`. `VisitedTokens` is used
178-
/// to track tokens of flow conditions that were already visited by recursive
179-
/// calls.
180-
void addTransitiveFlowConditionConstraints(
181-
Atom Token, llvm::SetVector<const Formula *> &Constraints,
182-
llvm::DenseSet<Atom> &VisitedTokens);
186+
/// of its transitive dependencies to `Constraints`.
187+
void
188+
addTransitiveFlowConditionConstraints(Atom Token,
189+
llvm::SetVector<const Formula *> &Out);
190+
183191

184192
/// Returns true if the solver is able to prove that there is no satisfying
185193
/// assignment for `Constraints`
@@ -224,6 +232,7 @@ class DataflowAnalysisContext {
224232
// dependencies is stored in the `FlowConditionDeps` map.
225233
llvm::DenseMap<Atom, llvm::DenseSet<Atom>> FlowConditionDeps;
226234
llvm::DenseMap<Atom, const Formula *> FlowConditionConstraints;
235+
const Formula *Invariant = nullptr;
227236

228237
llvm::DenseMap<const FunctionDecl *, ControlFlowContext> FunctionContexts;
229238

clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,13 @@ DataflowAnalysisContext::getOrCreateNullPointerValue(QualType PointeeType) {
104104
return *Res.first->second;
105105
}
106106

107+
void DataflowAnalysisContext::addInvariant(const Formula &Constraint) {
108+
if (Invariant == nullptr)
109+
Invariant = &Constraint;
110+
else
111+
Invariant = &arena().makeAnd(*Invariant, Constraint);
112+
}
113+
107114
void DataflowAnalysisContext::addFlowConditionConstraint(
108115
Atom Token, const Formula &Constraint) {
109116
auto Res = FlowConditionConstraints.try_emplace(Token, &Constraint);
@@ -149,8 +156,7 @@ bool DataflowAnalysisContext::flowConditionImplies(Atom Token,
149156
llvm::SetVector<const Formula *> Constraints;
150157
Constraints.insert(&arena().makeAtomRef(Token));
151158
Constraints.insert(&arena().makeNot(Val));
152-
llvm::DenseSet<Atom> VisitedTokens;
153-
addTransitiveFlowConditionConstraints(Token, Constraints, VisitedTokens);
159+
addTransitiveFlowConditionConstraints(Token, Constraints);
154160
return isUnsatisfiable(std::move(Constraints));
155161
}
156162

@@ -159,8 +165,7 @@ bool DataflowAnalysisContext::flowConditionIsTautology(Atom Token) {
159165
// ever be false.
160166
llvm::SetVector<const Formula *> Constraints;
161167
Constraints.insert(&arena().makeNot(arena().makeAtomRef(Token)));
162-
llvm::DenseSet<Atom> VisitedTokens;
163-
addTransitiveFlowConditionConstraints(Token, Constraints, VisitedTokens);
168+
addTransitiveFlowConditionConstraints(Token, Constraints);
164169
return isUnsatisfiable(std::move(Constraints));
165170
}
166171

@@ -172,37 +177,41 @@ bool DataflowAnalysisContext::equivalentFormulas(const Formula &Val1,
172177
}
173178

174179
void DataflowAnalysisContext::addTransitiveFlowConditionConstraints(
175-
Atom Token, llvm::SetVector<const Formula *> &Constraints,
176-
llvm::DenseSet<Atom> &VisitedTokens) {
177-
auto Res = VisitedTokens.insert(Token);
178-
if (!Res.second)
179-
return;
180-
181-
auto ConstraintsIt = FlowConditionConstraints.find(Token);
182-
if (ConstraintsIt == FlowConditionConstraints.end()) {
183-
Constraints.insert(&arena().makeAtomRef(Token));
184-
} else {
185-
// Bind flow condition token via `iff` to its set of constraints:
186-
// FC <=> (C1 ^ C2 ^ ...), where Ci are constraints
187-
Constraints.insert(&arena().makeEquals(arena().makeAtomRef(Token),
188-
*ConstraintsIt->second));
189-
}
190-
191-
auto DepsIt = FlowConditionDeps.find(Token);
192-
if (DepsIt != FlowConditionDeps.end()) {
193-
for (Atom DepToken : DepsIt->second) {
194-
addTransitiveFlowConditionConstraints(DepToken, Constraints,
195-
VisitedTokens);
180+
Atom Token, llvm::SetVector<const Formula *> &Constraints) {
181+
llvm::DenseSet<Atom> AddedTokens;
182+
std::vector<Atom> Remaining = {Token};
183+
184+
if (Invariant)
185+
Constraints.insert(Invariant);
186+
// Define all the flow conditions that might be referenced in constraints.
187+
while (!Remaining.empty()) {
188+
auto Token = Remaining.back();
189+
Remaining.pop_back();
190+
if (!AddedTokens.insert(Token).second)
191+
continue;
192+
193+
auto ConstraintsIt = FlowConditionConstraints.find(Token);
194+
if (ConstraintsIt == FlowConditionConstraints.end()) {
195+
Constraints.insert(&arena().makeAtomRef(Token));
196+
} else {
197+
// Bind flow condition token via `iff` to its set of constraints:
198+
// FC <=> (C1 ^ C2 ^ ...), where Ci are constraints
199+
Constraints.insert(&arena().makeEquals(arena().makeAtomRef(Token),
200+
*ConstraintsIt->second));
196201
}
202+
203+
if (auto DepsIt = FlowConditionDeps.find(Token);
204+
DepsIt != FlowConditionDeps.end())
205+
for (Atom A : DepsIt->second)
206+
Remaining.push_back(A);
197207
}
198208
}
199209

200210
void DataflowAnalysisContext::dumpFlowCondition(Atom Token,
201211
llvm::raw_ostream &OS) {
202212
llvm::SetVector<const Formula *> Constraints;
203213
Constraints.insert(&arena().makeAtomRef(Token));
204-
llvm::DenseSet<Atom> VisitedTokens;
205-
addTransitiveFlowConditionConstraints(Token, Constraints, VisitedTokens);
214+
addTransitiveFlowConditionConstraints(Token, Constraints);
206215

207216
// TODO: have formulas know about true/false directly instead
208217
Atom True = arena().makeLiteral(true).getAtom();

clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,22 @@ TEST_F(DataflowAnalysisContextTest, AddFlowConditionConstraint) {
4646
EXPECT_TRUE(Context.flowConditionImplies(FC, C));
4747
}
4848

49+
TEST_F(DataflowAnalysisContextTest, AddInvariant) {
50+
Atom FC = A.makeFlowConditionToken();
51+
auto &C = A.makeAtomRef(A.makeAtom());
52+
Context.addInvariant(C);
53+
EXPECT_TRUE(Context.flowConditionImplies(FC, C));
54+
}
55+
56+
TEST_F(DataflowAnalysisContextTest, InvariantAndFCConstraintInteract) {
57+
Atom FC = A.makeFlowConditionToken();
58+
auto &C = A.makeAtomRef(A.makeAtom());
59+
auto &D = A.makeAtomRef(A.makeAtom());
60+
Context.addInvariant(A.makeImplies(C, D));
61+
Context.addFlowConditionConstraint(FC, C);
62+
EXPECT_TRUE(Context.flowConditionImplies(FC, D));
63+
}
64+
4965
TEST_F(DataflowAnalysisContextTest, ForkFlowCondition) {
5066
Atom FC1 = A.makeFlowConditionToken();
5167
auto &C1 = A.makeAtomRef(A.makeAtom());

0 commit comments

Comments
 (0)