Skip to content

[dataflow] Add global condition to DataflowAnalysisContext #65949

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,15 @@ class DataflowAnalysisContext {
/// A null `PointeeType` can be used for the pointee of `std::nullptr_t`.
PointerValue &getOrCreateNullPointerValue(QualType PointeeType);

/// Adds `Constraint` to current and future flow conditions in this context.
///
/// Invariants must contain only flow-insensitive information, i.e. facts that
/// are true on all paths through the program.
/// Information can be added eagerly (when analysis begins), or lazily (e.g.
/// when values are first used). The analysis must be careful that the same
/// information is added regardless of which order blocks are analyzed in.
void addInvariant(const Formula &Constraint);

/// Adds `Constraint` to the flow condition identified by `Token`.
void addFlowConditionConstraint(Atom Token, const Formula &Constraint);

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

/// Adds all constraints of the flow condition identified by `Token` and all
/// of its transitive dependencies to `Constraints`. `VisitedTokens` is used
/// to track tokens of flow conditions that were already visited by recursive
/// calls.
void addTransitiveFlowConditionConstraints(
Atom Token, llvm::SetVector<const Formula *> &Constraints,
llvm::DenseSet<Atom> &VisitedTokens);
/// of its transitive dependencies to `Constraints`.
void
addTransitiveFlowConditionConstraints(Atom Token,
llvm::SetVector<const Formula *> &Out);


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

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

Expand Down
63 changes: 36 additions & 27 deletions clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@ DataflowAnalysisContext::getOrCreateNullPointerValue(QualType PointeeType) {
return *Res.first->second;
}

void DataflowAnalysisContext::addInvariant(const Formula &Constraint) {
if (Invariant == nullptr)
Invariant = &Constraint;
else
Invariant = &arena().makeAnd(*Invariant, Constraint);
}

void DataflowAnalysisContext::addFlowConditionConstraint(
Atom Token, const Formula &Constraint) {
auto Res = FlowConditionConstraints.try_emplace(Token, &Constraint);
Expand Down Expand Up @@ -149,8 +156,7 @@ bool DataflowAnalysisContext::flowConditionImplies(Atom Token,
llvm::SetVector<const Formula *> Constraints;
Constraints.insert(&arena().makeAtomRef(Token));
Constraints.insert(&arena().makeNot(Val));
llvm::DenseSet<Atom> VisitedTokens;
addTransitiveFlowConditionConstraints(Token, Constraints, VisitedTokens);
addTransitiveFlowConditionConstraints(Token, Constraints);
return isUnsatisfiable(std::move(Constraints));
}

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

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

void DataflowAnalysisContext::addTransitiveFlowConditionConstraints(
Atom Token, llvm::SetVector<const Formula *> &Constraints,
llvm::DenseSet<Atom> &VisitedTokens) {
auto Res = VisitedTokens.insert(Token);
if (!Res.second)
return;

auto ConstraintsIt = FlowConditionConstraints.find(Token);
if (ConstraintsIt == FlowConditionConstraints.end()) {
Constraints.insert(&arena().makeAtomRef(Token));
} else {
// Bind flow condition token via `iff` to its set of constraints:
// FC <=> (C1 ^ C2 ^ ...), where Ci are constraints
Constraints.insert(&arena().makeEquals(arena().makeAtomRef(Token),
*ConstraintsIt->second));
}

auto DepsIt = FlowConditionDeps.find(Token);
if (DepsIt != FlowConditionDeps.end()) {
for (Atom DepToken : DepsIt->second) {
addTransitiveFlowConditionConstraints(DepToken, Constraints,
VisitedTokens);
Atom Token, llvm::SetVector<const Formula *> &Constraints) {
llvm::DenseSet<Atom> AddedTokens;
std::vector<Atom> Remaining = {Token};

if (Invariant)
Constraints.insert(Invariant);
// Define all the flow conditions that might be referenced in constraints.
while (!Remaining.empty()) {
auto Token = Remaining.back();
Remaining.pop_back();
if (!AddedTokens.insert(Token).second)
continue;

auto ConstraintsIt = FlowConditionConstraints.find(Token);
if (ConstraintsIt == FlowConditionConstraints.end()) {
Constraints.insert(&arena().makeAtomRef(Token));
} else {
// Bind flow condition token via `iff` to its set of constraints:
// FC <=> (C1 ^ C2 ^ ...), where Ci are constraints
Constraints.insert(&arena().makeEquals(arena().makeAtomRef(Token),
*ConstraintsIt->second));
}

if (auto DepsIt = FlowConditionDeps.find(Token);
DepsIt != FlowConditionDeps.end())
for (Atom A : DepsIt->second)
Remaining.push_back(A);
}
}

void DataflowAnalysisContext::dumpFlowCondition(Atom Token,
llvm::raw_ostream &OS) {
llvm::SetVector<const Formula *> Constraints;
Constraints.insert(&arena().makeAtomRef(Token));
llvm::DenseSet<Atom> VisitedTokens;
addTransitiveFlowConditionConstraints(Token, Constraints, VisitedTokens);
addTransitiveFlowConditionConstraints(Token, Constraints);

// TODO: have formulas know about true/false directly instead
Atom True = arena().makeLiteral(true).getAtom();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,22 @@ TEST_F(DataflowAnalysisContextTest, AddFlowConditionConstraint) {
EXPECT_TRUE(Context.flowConditionImplies(FC, C));
}

TEST_F(DataflowAnalysisContextTest, AddInvariant) {
Atom FC = A.makeFlowConditionToken();
auto &C = A.makeAtomRef(A.makeAtom());
Context.addInvariant(C);
EXPECT_TRUE(Context.flowConditionImplies(FC, C));
}

TEST_F(DataflowAnalysisContextTest, InvariantAndFCConstraintInteract) {
Atom FC = A.makeFlowConditionToken();
auto &C = A.makeAtomRef(A.makeAtom());
auto &D = A.makeAtomRef(A.makeAtom());
Context.addInvariant(A.makeImplies(C, D));
Context.addFlowConditionConstraint(FC, C);
EXPECT_TRUE(Context.flowConditionImplies(FC, D));
}

TEST_F(DataflowAnalysisContextTest, ForkFlowCondition) {
Atom FC1 = A.makeFlowConditionToken();
auto &C1 = A.makeAtomRef(A.makeAtom());
Expand Down