diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h index 685bbe486b54e..4698f0616e66e 100644 --- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h @@ -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); @@ -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 &Constraints, - llvm::DenseSet &VisitedTokens); + /// of its transitive dependencies to `Constraints`. + void + addTransitiveFlowConditionConstraints(Atom Token, + llvm::SetVector &Out); + /// Returns true if the solver is able to prove that there is no satisfying /// assignment for `Constraints` @@ -224,6 +232,7 @@ class DataflowAnalysisContext { // dependencies is stored in the `FlowConditionDeps` map. llvm::DenseMap> FlowConditionDeps; llvm::DenseMap FlowConditionConstraints; + const Formula *Invariant = nullptr; llvm::DenseMap FunctionContexts; diff --git a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp index 47a994f4bbdb6..e81048ce92338 100644 --- a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp @@ -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); @@ -149,8 +156,7 @@ bool DataflowAnalysisContext::flowConditionImplies(Atom Token, llvm::SetVector Constraints; Constraints.insert(&arena().makeAtomRef(Token)); Constraints.insert(&arena().makeNot(Val)); - llvm::DenseSet VisitedTokens; - addTransitiveFlowConditionConstraints(Token, Constraints, VisitedTokens); + addTransitiveFlowConditionConstraints(Token, Constraints); return isUnsatisfiable(std::move(Constraints)); } @@ -159,8 +165,7 @@ bool DataflowAnalysisContext::flowConditionIsTautology(Atom Token) { // ever be false. llvm::SetVector Constraints; Constraints.insert(&arena().makeNot(arena().makeAtomRef(Token))); - llvm::DenseSet VisitedTokens; - addTransitiveFlowConditionConstraints(Token, Constraints, VisitedTokens); + addTransitiveFlowConditionConstraints(Token, Constraints); return isUnsatisfiable(std::move(Constraints)); } @@ -172,28 +177,33 @@ bool DataflowAnalysisContext::equivalentFormulas(const Formula &Val1, } void DataflowAnalysisContext::addTransitiveFlowConditionConstraints( - Atom Token, llvm::SetVector &Constraints, - llvm::DenseSet &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 &Constraints) { + llvm::DenseSet AddedTokens; + std::vector 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); } } @@ -201,8 +211,7 @@ void DataflowAnalysisContext::dumpFlowCondition(Atom Token, llvm::raw_ostream &OS) { llvm::SetVector Constraints; Constraints.insert(&arena().makeAtomRef(Token)); - llvm::DenseSet VisitedTokens; - addTransitiveFlowConditionConstraints(Token, Constraints, VisitedTokens); + addTransitiveFlowConditionConstraints(Token, Constraints); // TODO: have formulas know about true/false directly instead Atom True = arena().makeLiteral(true).getAtom(); diff --git a/clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp b/clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp index f6e8b30d898e8..fb7642c131e31 100644 --- a/clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp @@ -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());