1 //===- CNFFormula.cpp -------------------------------------------*- 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 // A representation of a boolean formula in 3-CNF. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "clang/Analysis/FlowSensitive/CNFFormula.h" 14 #include "llvm/ADT/DenseSet.h" 15 16 #include <queue> 17 18 namespace clang { 19 namespace dataflow { 20 21 namespace { 22 23 /// Applies simplifications while building up a BooleanFormula. 24 /// We keep track of unit clauses, which tell us variables that must be 25 /// true/false in any model that satisfies the overall formula. 26 /// Such variables can be dropped from subsequently-added clauses, which 27 /// may in turn yield more unit clauses or even a contradiction. 28 /// The total added complexity of this preprocessing is O(N) where we 29 /// for every clause, we do a lookup for each unit clauses. 30 /// The lookup is O(1) on average. This method won't catch all 31 /// contradictory formulas, more passes can in principle catch 32 /// more cases but we leave all these and the general case to the 33 /// proper SAT solver. 34 struct CNFFormulaBuilder { 35 // Formula should outlive CNFFormulaBuilder. 36 explicit CNFFormulaBuilder(CNFFormula &CNF) : Formula(CNF) {} 37 38 /// Adds the `L1 v ... v Ln` clause to the formula. Applies 39 /// simplifications, based on single-literal clauses. 40 /// 41 /// Requirements: 42 /// 43 /// `Li` must not be `NullLit`. 44 /// 45 /// All literals must be distinct. 46 void addClause(ArrayRef<Literal> Literals) { 47 // We generate clauses with up to 3 literals in this file. 48 assert(!Literals.empty() && Literals.size() <= 3); 49 // Contains literals of the simplified clause. 50 llvm::SmallVector<Literal> Simplified; 51 for (auto L : Literals) { 52 assert(L != NullLit && 53 llvm::all_of(Simplified, [L](Literal S) { return S != L; })); 54 auto X = var(L); 55 if (trueVars.contains(X)) { // X must be true 56 if (isPosLit(L)) 57 return; // Omit clause `(... v X v ...)`, it is `true`. 58 else 59 continue; // Omit `!X` from `(... v !X v ...)`. 60 } 61 if (falseVars.contains(X)) { // X must be false 62 if (isNegLit(L)) 63 return; // Omit clause `(... v !X v ...)`, it is `true`. 64 else 65 continue; // Omit `X` from `(... v X v ...)`. 66 } 67 Simplified.push_back(L); 68 } 69 if (Simplified.empty()) { 70 // Simplification made the clause empty, which is equivalent to `false`. 71 // We already know that this formula is unsatisfiable. 72 Formula.addClause(Simplified); 73 return; 74 } 75 if (Simplified.size() == 1) { 76 // We have new unit clause. 77 const Literal lit = Simplified.front(); 78 const Variable v = var(lit); 79 if (isPosLit(lit)) 80 trueVars.insert(v); 81 else 82 falseVars.insert(v); 83 } 84 Formula.addClause(Simplified); 85 } 86 87 /// Returns true if we observed a contradiction while adding clauses. 88 /// In this case then the formula is already known to be unsatisfiable. 89 bool isKnownContradictory() { return Formula.knownContradictory(); } 90 91 private: 92 CNFFormula &Formula; 93 llvm::DenseSet<Variable> trueVars; 94 llvm::DenseSet<Variable> falseVars; 95 }; 96 97 } // namespace 98 99 CNFFormula::CNFFormula(Variable LargestVar) 100 : LargestVar(LargestVar), KnownContradictory(false) { 101 Clauses.push_back(0); 102 ClauseStarts.push_back(0); 103 } 104 105 void CNFFormula::addClause(ArrayRef<Literal> lits) { 106 assert(llvm::all_of(lits, [](Literal L) { return L != NullLit; })); 107 108 if (lits.empty()) 109 KnownContradictory = true; 110 111 const size_t S = Clauses.size(); 112 ClauseStarts.push_back(S); 113 Clauses.insert(Clauses.end(), lits.begin(), lits.end()); 114 } 115 116 CNFFormula buildCNF(const llvm::ArrayRef<const Formula *> &Formulas, 117 llvm::DenseMap<Variable, Atom> &Atomics) { 118 // The general strategy of the algorithm implemented below is to map each 119 // of the sub-values in `Vals` to a unique variable and use these variables in 120 // the resulting CNF expression to avoid exponential blow up. The number of 121 // literals in the resulting formula is guaranteed to be linear in the number 122 // of sub-formulas in `Vals`. 123 124 // Map each sub-formula in `Vals` to a unique variable. 125 llvm::DenseMap<const Formula *, Variable> FormulaToVar; 126 // Store variable identifiers and Atom of atomic booleans. 127 Variable NextVar = 1; 128 { 129 std::queue<const Formula *> UnprocessedFormulas; 130 for (const Formula *F : Formulas) 131 UnprocessedFormulas.push(F); 132 while (!UnprocessedFormulas.empty()) { 133 Variable Var = NextVar; 134 const Formula *F = UnprocessedFormulas.front(); 135 UnprocessedFormulas.pop(); 136 137 if (!FormulaToVar.try_emplace(F, Var).second) 138 continue; 139 ++NextVar; 140 141 for (const Formula *Op : F->operands()) 142 UnprocessedFormulas.push(Op); 143 if (F->kind() == Formula::AtomRef) 144 Atomics[Var] = F->getAtom(); 145 } 146 } 147 148 auto GetVar = [&FormulaToVar](const Formula *F) { 149 auto ValIt = FormulaToVar.find(F); 150 assert(ValIt != FormulaToVar.end()); 151 return ValIt->second; 152 }; 153 154 CNFFormula CNF(NextVar - 1); 155 std::vector<bool> ProcessedSubVals(NextVar, false); 156 CNFFormulaBuilder builder(CNF); 157 158 // Add a conjunct for each variable that represents a top-level conjunction 159 // value in `Vals`. 160 for (const Formula *F : Formulas) 161 builder.addClause(posLit(GetVar(F))); 162 163 // Add conjuncts that represent the mapping between newly-created variables 164 // and their corresponding sub-formulas. 165 std::queue<const Formula *> UnprocessedFormulas; 166 for (const Formula *F : Formulas) 167 UnprocessedFormulas.push(F); 168 while (!UnprocessedFormulas.empty()) { 169 const Formula *F = UnprocessedFormulas.front(); 170 UnprocessedFormulas.pop(); 171 const Variable Var = GetVar(F); 172 173 if (ProcessedSubVals[Var]) 174 continue; 175 ProcessedSubVals[Var] = true; 176 177 switch (F->kind()) { 178 case Formula::AtomRef: 179 break; 180 case Formula::Literal: 181 CNF.addClause(F->literal() ? posLit(Var) : negLit(Var)); 182 break; 183 case Formula::And: { 184 const Variable LHS = GetVar(F->operands()[0]); 185 const Variable RHS = GetVar(F->operands()[1]); 186 187 if (LHS == RHS) { 188 // `X <=> (A ^ A)` is equivalent to `(!X v A) ^ (X v !A)` which is 189 // already in conjunctive normal form. Below we add each of the 190 // conjuncts of the latter expression to the result. 191 builder.addClause({negLit(Var), posLit(LHS)}); 192 builder.addClause({posLit(Var), negLit(LHS)}); 193 } else { 194 // `X <=> (A ^ B)` is equivalent to `(!X v A) ^ (!X v B) ^ (X v !A v 195 // !B)` which is already in conjunctive normal form. Below we add each 196 // of the conjuncts of the latter expression to the result. 197 builder.addClause({negLit(Var), posLit(LHS)}); 198 builder.addClause({negLit(Var), posLit(RHS)}); 199 builder.addClause({posLit(Var), negLit(LHS), negLit(RHS)}); 200 } 201 break; 202 } 203 case Formula::Or: { 204 const Variable LHS = GetVar(F->operands()[0]); 205 const Variable RHS = GetVar(F->operands()[1]); 206 207 if (LHS == RHS) { 208 // `X <=> (A v A)` is equivalent to `(!X v A) ^ (X v !A)` which is 209 // already in conjunctive normal form. Below we add each of the 210 // conjuncts of the latter expression to the result. 211 builder.addClause({negLit(Var), posLit(LHS)}); 212 builder.addClause({posLit(Var), negLit(LHS)}); 213 } else { 214 // `X <=> (A v B)` is equivalent to `(!X v A v B) ^ (X v !A) ^ (X v 215 // !B)` which is already in conjunctive normal form. Below we add each 216 // of the conjuncts of the latter expression to the result. 217 builder.addClause({negLit(Var), posLit(LHS), posLit(RHS)}); 218 builder.addClause({posLit(Var), negLit(LHS)}); 219 builder.addClause({posLit(Var), negLit(RHS)}); 220 } 221 break; 222 } 223 case Formula::Not: { 224 const Variable Operand = GetVar(F->operands()[0]); 225 226 // `X <=> !Y` is equivalent to `(!X v !Y) ^ (X v Y)` which is 227 // already in conjunctive normal form. Below we add each of the 228 // conjuncts of the latter expression to the result. 229 builder.addClause({negLit(Var), negLit(Operand)}); 230 builder.addClause({posLit(Var), posLit(Operand)}); 231 break; 232 } 233 case Formula::Implies: { 234 const Variable LHS = GetVar(F->operands()[0]); 235 const Variable RHS = GetVar(F->operands()[1]); 236 237 // `X <=> (A => B)` is equivalent to 238 // `(X v A) ^ (X v !B) ^ (!X v !A v B)` which is already in 239 // conjunctive normal form. Below we add each of the conjuncts of 240 // the latter expression to the result. 241 builder.addClause({posLit(Var), posLit(LHS)}); 242 builder.addClause({posLit(Var), negLit(RHS)}); 243 builder.addClause({negLit(Var), negLit(LHS), posLit(RHS)}); 244 break; 245 } 246 case Formula::Equal: { 247 const Variable LHS = GetVar(F->operands()[0]); 248 const Variable RHS = GetVar(F->operands()[1]); 249 250 if (LHS == RHS) { 251 // `X <=> (A <=> A)` is equivalent to `X` which is already in 252 // conjunctive normal form. Below we add each of the conjuncts of the 253 // latter expression to the result. 254 builder.addClause(posLit(Var)); 255 256 // No need to visit the sub-values of `Val`. 257 continue; 258 } 259 // `X <=> (A <=> B)` is equivalent to 260 // `(X v A v B) ^ (X v !A v !B) ^ (!X v A v !B) ^ (!X v !A v B)` which 261 // is already in conjunctive normal form. Below we add each of the 262 // conjuncts of the latter expression to the result. 263 builder.addClause({posLit(Var), posLit(LHS), posLit(RHS)}); 264 builder.addClause({posLit(Var), negLit(LHS), negLit(RHS)}); 265 builder.addClause({negLit(Var), posLit(LHS), negLit(RHS)}); 266 builder.addClause({negLit(Var), negLit(LHS), posLit(RHS)}); 267 break; 268 } 269 } 270 if (builder.isKnownContradictory()) { 271 return CNF; 272 } 273 for (const Formula *Child : F->operands()) 274 UnprocessedFormulas.push(Child); 275 } 276 277 // Unit clauses that were added later were not 278 // considered for the simplification of earlier clauses. Do a final 279 // pass to find more opportunities for simplification. 280 CNFFormula FinalCNF(NextVar - 1); 281 CNFFormulaBuilder FinalBuilder(FinalCNF); 282 283 // Collect unit clauses. 284 for (ClauseID C = 1; C <= CNF.numClauses(); ++C) { 285 if (CNF.clauseSize(C) == 1) { 286 FinalBuilder.addClause(CNF.clauseLiterals(C)[0]); 287 } 288 } 289 290 // Add all clauses that were added previously, preserving the order. 291 for (ClauseID C = 1; C <= CNF.numClauses(); ++C) { 292 FinalBuilder.addClause(CNF.clauseLiterals(C)); 293 if (FinalBuilder.isKnownContradictory()) { 294 break; 295 } 296 } 297 // It is possible there were new unit clauses again, but 298 // we stop here and leave the rest to the solver algorithm. 299 return FinalCNF; 300 } 301 302 } // namespace dataflow 303 } // namespace clang 304