Skip to content

Commit ae909f8

Browse files
authored
Merge branch 'main' into prefer_alt_opc_vec
2 parents b370e1d + d6e0922 commit ae909f8

40 files changed

+1230
-493
lines changed

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,18 @@ struct ContextSensitiveOptions {
4242
unsigned Depth = 2;
4343
};
4444

45+
/// A simple representation of essential elements of the logical context used in
46+
/// environments. Designed for import/export for applications requiring
47+
/// serialization support.
48+
struct SimpleLogicalContext {
49+
// Global invariant that applies for all definitions in the context.
50+
const Formula *Invariant;
51+
// Flow-condition tokens in the context.
52+
llvm::DenseMap<Atom, const Formula *> TokenDefs;
53+
// Dependencies between flow-condition definitions.
54+
llvm::DenseMap<Atom, llvm::DenseSet<Atom>> TokenDeps;
55+
};
56+
4557
/// Owns objects that encompass the state of a program and stores context that
4658
/// is used during dataflow analysis.
4759
class DataflowAnalysisContext {
@@ -140,6 +152,15 @@ class DataflowAnalysisContext {
140152
/// Adds `Constraint` to the flow condition identified by `Token`.
141153
void addFlowConditionConstraint(Atom Token, const Formula &Constraint);
142154

155+
/// Adds `Deps` to the dependencies of the flow condition identified by
156+
/// `Token`. Intended for use in deserializing contexts. The formula alone
157+
/// doesn't have enough information to indicate its deps.
158+
void addFlowConditionDeps(Atom Token, const llvm::DenseSet<Atom> &Deps) {
159+
// Avoid creating an entry for `Token` with an empty set.
160+
if (!Deps.empty())
161+
FlowConditionDeps[Token].insert(Deps.begin(), Deps.end());
162+
}
163+
143164
/// Creates a new flow condition with the same constraints as the flow
144165
/// condition identified by `Token` and returns its token.
145166
Atom forkFlowCondition(Atom Token);
@@ -207,6 +228,14 @@ class DataflowAnalysisContext {
207228
return {};
208229
}
209230

231+
/// Export the logical-context portions of `AC`, limited to the given target
232+
/// flow-condition tokens.
233+
SimpleLogicalContext
234+
exportLogicalContext(llvm::DenseSet<dataflow::Atom> TargetTokens) const;
235+
236+
/// Initializes this context's "logical" components with `LC`.
237+
void initLogicalContext(SimpleLogicalContext LC);
238+
210239
private:
211240
friend class Environment;
212241

@@ -228,6 +257,11 @@ class DataflowAnalysisContext {
228257
DataflowAnalysisContext(Solver &S, std::unique_ptr<Solver> &&OwnedSolver,
229258
Options Opts);
230259

260+
/// Computes the transitive closure of dependencies of (flow-condition)
261+
/// `Tokens`. That is, the set of flow-condition tokens reachable from
262+
/// `Tokens` in the dependency graph.
263+
llvm::DenseSet<Atom> collectDependencies(llvm::DenseSet<Atom> Tokens) const;
264+
231265
// Extends the set of modeled field declarations.
232266
void addModeledFields(const FieldSet &Fields);
233267

clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,18 @@ class Environment {
157157
};
158158

159159
/// Creates an environment that uses `DACtx` to store objects that encompass
160-
/// the state of a program.
160+
/// the state of a program. `FlowConditionToken` sets the flow condition
161+
/// associated with the environment. Generally, new environments should be
162+
/// initialized with a fresh token, by using one of the other
163+
/// constructors. This constructor is for specialized use, including
164+
/// deserialization and delegation from other constructors.
165+
Environment(DataflowAnalysisContext &DACtx, Atom FlowConditionToken)
166+
: DACtx(&DACtx), FlowConditionToken(FlowConditionToken) {}
167+
168+
/// Creates an environment that uses `DACtx` to store objects that encompass
169+
/// the state of a program. Populates a fresh atom as flow condition token.
161170
explicit Environment(DataflowAnalysisContext &DACtx)
162-
: DACtx(&DACtx),
163-
FlowConditionToken(DACtx.arena().makeFlowConditionToken()) {}
171+
: Environment(DACtx, DACtx.arena().makeFlowConditionToken()) {}
164172

165173
/// Creates an environment that uses `DACtx` to store objects that encompass
166174
/// the state of a program, with `S` as the statement to analyze.

clang/include/clang/Analysis/FlowSensitive/Formula.h

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -85,21 +85,17 @@ class alignas(const Formula *) Formula {
8585
}
8686

8787
using AtomNames = llvm::DenseMap<Atom, std::string>;
88-
// Produce a stable human-readable representation of this formula.
89-
// For example: (V3 | !(V1 & V2))
90-
// If AtomNames is provided, these override the default V0, V1... names.
88+
/// Produces a stable human-readable representation of this formula.
89+
/// For example: (V3 | !(V1 & V2))
90+
/// If AtomNames is provided, these override the default V0, V1... names.
9191
void print(llvm::raw_ostream &OS, const AtomNames * = nullptr) const;
9292

93-
// Allocate Formulas using Arena rather than calling this function directly.
93+
/// Allocates Formulas using Arena rather than calling this function directly.
9494
static const Formula &create(llvm::BumpPtrAllocator &Alloc, Kind K,
9595
ArrayRef<const Formula *> Operands,
9696
unsigned Value = 0);
9797

98-
private:
99-
Formula() = default;
100-
Formula(const Formula &) = delete;
101-
Formula &operator=(const Formula &) = delete;
102-
98+
/// Count of operands (sub-formulas) associated with Formulas of kind `K`.
10399
static unsigned numOperands(Kind K) {
104100
switch (K) {
105101
case AtomRef:
@@ -116,6 +112,11 @@ class alignas(const Formula *) Formula {
116112
llvm_unreachable("Unhandled Formula::Kind enum");
117113
}
118114

115+
private:
116+
Formula() = default;
117+
Formula(const Formula &) = delete;
118+
Formula &operator=(const Formula &) = delete;
119+
119120
Kind FormulaKind;
120121
// Some kinds of formula have scalar values, e.g. AtomRef's atom number.
121122
unsigned Value;
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//=== FormulaSerialization.h - Formula De/Serialization support -*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_FORMULA_SERIALIZATION_H
10+
#define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_FORMULA_SERIALIZATION_H
11+
12+
#include "clang/Analysis/FlowSensitive/Arena.h"
13+
#include "clang/Analysis/FlowSensitive/Formula.h"
14+
#include "clang/Basic/LLVM.h"
15+
#include "llvm/ADT/ArrayRef.h"
16+
#include "llvm/ADT/DenseMap.h"
17+
#include "llvm/ADT/DenseMapInfo.h"
18+
#include "llvm/Support/Allocator.h"
19+
#include "llvm/Support/raw_ostream.h"
20+
#include <cassert>
21+
#include <string>
22+
23+
namespace clang::dataflow {
24+
25+
/// Prints `F` to `OS` in a compact format, optimized for easy parsing
26+
/// (deserialization) rather than human use.
27+
void serializeFormula(const Formula &F, llvm::raw_ostream &OS);
28+
29+
/// Parses `Str` to build a serialized Formula.
30+
/// @returns error on parse failure or if parsing does not fully consume `Str`.
31+
/// @param A used to construct the formula components.
32+
/// @param AtomMap maps serialized Atom identifiers (unsigned ints) to Atoms.
33+
/// This map is provided by the caller to enable consistency across
34+
/// multiple formulas in a single file.
35+
llvm::Expected<const Formula *>
36+
parseFormula(llvm::StringRef Str, Arena &A,
37+
llvm::DenseMap<unsigned, Atom> &AtomMap);
38+
39+
} // namespace clang::dataflow
40+
#endif

clang/lib/Analysis/FlowSensitive/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ add_clang_library(clangAnalysisFlowSensitive
66
DataflowAnalysisContext.cpp
77
DataflowEnvironment.cpp
88
Formula.cpp
9+
FormulaSerialization.cpp
910
HTMLLogger.cpp
1011
Logger.cpp
1112
RecordOps.cpp

clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,24 @@ bool DataflowAnalysisContext::equivalentFormulas(const Formula &Val1,
208208
return isUnsatisfiable(std::move(Constraints));
209209
}
210210

211+
llvm::DenseSet<Atom> DataflowAnalysisContext::collectDependencies(
212+
llvm::DenseSet<Atom> Tokens) const {
213+
// Use a worklist algorithm, with `Remaining` holding the worklist and
214+
// `Tokens` tracking which atoms have already been added to the worklist.
215+
std::vector<Atom> Remaining(Tokens.begin(), Tokens.end());
216+
while (!Remaining.empty()) {
217+
Atom CurrentToken = Remaining.back();
218+
Remaining.pop_back();
219+
if (auto DepsIt = FlowConditionDeps.find(CurrentToken);
220+
DepsIt != FlowConditionDeps.end())
221+
for (Atom A : DepsIt->second)
222+
if (Tokens.insert(A).second)
223+
Remaining.push_back(A);
224+
}
225+
226+
return Tokens;
227+
}
228+
211229
void DataflowAnalysisContext::addTransitiveFlowConditionConstraints(
212230
Atom Token, llvm::SetVector<const Formula *> &Constraints) {
213231
llvm::DenseSet<Atom> AddedTokens;
@@ -224,6 +242,8 @@ void DataflowAnalysisContext::addTransitiveFlowConditionConstraints(
224242

225243
auto ConstraintsIt = FlowConditionConstraints.find(Token);
226244
if (ConstraintsIt == FlowConditionConstraints.end()) {
245+
// The flow condition is unconstrained. Just add the atom directly, which
246+
// is equivalent to asserting it is true.
227247
Constraints.insert(&arena().makeAtomRef(Token));
228248
} else {
229249
// Bind flow condition token via `iff` to its set of constraints:
@@ -239,6 +259,65 @@ void DataflowAnalysisContext::addTransitiveFlowConditionConstraints(
239259
}
240260
}
241261

262+
static void getReferencedAtoms(const Formula &F,
263+
llvm::DenseSet<dataflow::Atom> &Refs) {
264+
switch (F.kind()) {
265+
case Formula::AtomRef:
266+
Refs.insert(F.getAtom());
267+
break;
268+
case Formula::Literal:
269+
break;
270+
case Formula::Not:
271+
getReferencedAtoms(*F.operands()[0], Refs);
272+
break;
273+
case Formula::And:
274+
case Formula::Or:
275+
case Formula::Implies:
276+
case Formula::Equal:
277+
ArrayRef<const Formula *> Operands = F.operands();
278+
getReferencedAtoms(*Operands[0], Refs);
279+
getReferencedAtoms(*Operands[1], Refs);
280+
break;
281+
}
282+
}
283+
284+
SimpleLogicalContext DataflowAnalysisContext::exportLogicalContext(
285+
llvm::DenseSet<dataflow::Atom> TargetTokens) const {
286+
SimpleLogicalContext LC;
287+
288+
if (Invariant != nullptr) {
289+
LC.Invariant = Invariant;
290+
getReferencedAtoms(*Invariant, TargetTokens);
291+
}
292+
293+
llvm::DenseSet<dataflow::Atom> Dependencies =
294+
collectDependencies(std::move(TargetTokens));
295+
296+
for (dataflow::Atom Token : Dependencies) {
297+
// Only process the token if it is constrained. Unconstrained tokens don't
298+
// have dependencies.
299+
const Formula *Constraints = FlowConditionConstraints.lookup(Token);
300+
if (Constraints == nullptr)
301+
continue;
302+
LC.TokenDefs[Token] = Constraints;
303+
304+
if (auto DepsIt = FlowConditionDeps.find(Token);
305+
DepsIt != FlowConditionDeps.end())
306+
LC.TokenDeps[Token] = DepsIt->second;
307+
}
308+
309+
return LC;
310+
}
311+
312+
void DataflowAnalysisContext::initLogicalContext(SimpleLogicalContext LC) {
313+
Invariant = LC.Invariant;
314+
FlowConditionConstraints = std::move(LC.TokenDefs);
315+
// TODO: The dependencies in `LC.TokenDeps` can be reconstructed from
316+
// `LC.TokenDefs`. Give the caller the option to reconstruct, rather than
317+
// providing them directly, to save caller space (memory/disk).
318+
FlowConditionDeps = std::move(LC.TokenDeps);
319+
}
320+
242321
static void printAtomList(const llvm::SmallVector<Atom> &Atoms,
243322
llvm::raw_ostream &OS) {
244323
OS << "(";

0 commit comments

Comments
 (0)