1 //===- WatchedLiteralsSolver.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 // This file defines a SAT solver implementation that can be used by dataflow 10 // analyses. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include <cassert> 15 #include <vector> 16 17 #include "clang/Analysis/FlowSensitive/CNFFormula.h" 18 #include "clang/Analysis/FlowSensitive/Formula.h" 19 #include "clang/Analysis/FlowSensitive/Solver.h" 20 #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h" 21 #include "llvm/ADT/ArrayRef.h" 22 #include "llvm/ADT/DenseMap.h" 23 #include "llvm/ADT/DenseSet.h" 24 #include "llvm/ADT/STLExtras.h" 25 26 27 namespace clang { 28 namespace dataflow { 29 30 namespace { 31 32 class WatchedLiteralsSolverImpl { 33 /// Stores the variable identifier and Atom for atomic booleans in the 34 /// formula. 35 llvm::DenseMap<Variable, Atom> Atomics; 36 37 /// A boolean formula in conjunctive normal form that the solver will attempt 38 /// to prove satisfiable. The formula will be modified in the process. 39 CNFFormula CNF; 40 41 /// Maps literals (indices of the vector) to clause identifiers (elements of 42 /// the vector) that watch the respective literals. 43 /// 44 /// For a given clause, its watched literal is always its first literal in 45 /// `Clauses`. This invariant is maintained when watched literals change. 46 std::vector<ClauseID> WatchedHead; 47 48 /// Maps clause identifiers (elements of the vector) to identifiers of other 49 /// clauses that watch the same literals, forming a set of linked lists. 50 /// 51 /// The element at index 0 stands for the identifier of the clause that 52 /// follows the null clause. It is set to 0 and isn't used. Identifiers of 53 /// clauses in the formula start from the element at index 1. 54 std::vector<ClauseID> NextWatched; 55 56 /// The search for a satisfying assignment of the variables in `Formula` will 57 /// proceed in levels, starting from 1 and going up to `Formula.LargestVar` 58 /// (inclusive). The current level is stored in `Level`. At each level the 59 /// solver will assign a value to an unassigned variable. If this leads to a 60 /// consistent partial assignment, `Level` will be incremented. Otherwise, if 61 /// it results in a conflict, the solver will backtrack by decrementing 62 /// `Level` until it reaches the most recent level where a decision was made. 63 size_t Level = 0; 64 65 /// Maps levels (indices of the vector) to variables (elements of the vector) 66 /// that are assigned values at the respective levels. 67 /// 68 /// The element at index 0 isn't used. Variables start from the element at 69 /// index 1. 70 std::vector<Variable> LevelVars; 71 72 /// State of the solver at a particular level. 73 enum class State : uint8_t { 74 /// Indicates that the solver made a decision. 75 Decision = 0, 76 77 /// Indicates that the solver made a forced move. 78 Forced = 1, 79 }; 80 81 /// State of the solver at a particular level. It keeps track of previous 82 /// decisions that the solver can refer to when backtracking. 83 /// 84 /// The element at index 0 isn't used. States start from the element at index 85 /// 1. 86 std::vector<State> LevelStates; 87 88 enum class Assignment : int8_t { 89 Unassigned = -1, 90 AssignedFalse = 0, 91 AssignedTrue = 1 92 }; 93 94 /// Maps variables (indices of the vector) to their assignments (elements of 95 /// the vector). 96 /// 97 /// The element at index 0 isn't used. Variable assignments start from the 98 /// element at index 1. 99 std::vector<Assignment> VarAssignments; 100 101 /// A set of unassigned variables that appear in watched literals in 102 /// `Formula`. The vector is guaranteed to contain unique elements. 103 std::vector<Variable> ActiveVars; 104 105 public: 106 explicit WatchedLiteralsSolverImpl( 107 const llvm::ArrayRef<const Formula *> &Vals) 108 // `Atomics` needs to be initialized first so that we can use it as an 109 // output argument of `buildCNF()`. 110 : Atomics(), CNF(buildCNF(Vals, Atomics)), 111 LevelVars(CNF.largestVar() + 1), LevelStates(CNF.largestVar() + 1) { 112 assert(!Vals.empty()); 113 114 // Skip initialization if the formula is known to be contradictory. 115 if (CNF.knownContradictory()) 116 return; 117 118 // Initialize `NextWatched` and `WatchedHead`. 119 NextWatched.push_back(0); 120 const size_t NumLiterals = 2 * CNF.largestVar() + 1; 121 WatchedHead.resize(NumLiterals + 1, 0); 122 for (ClauseID C = 1; C <= CNF.numClauses(); ++C) { 123 // Designate the first literal as the "watched" literal of the clause. 124 Literal FirstLit = CNF.clauseLiterals(C).front(); 125 NextWatched.push_back(WatchedHead[FirstLit]); 126 WatchedHead[FirstLit] = C; 127 } 128 129 // Initialize the state at the root level to a decision so that in 130 // `reverseForcedMoves` we don't have to check that `Level >= 0` on each 131 // iteration. 132 LevelStates[0] = State::Decision; 133 134 // Initialize all variables as unassigned. 135 VarAssignments.resize(CNF.largestVar() + 1, Assignment::Unassigned); 136 137 // Initialize the active variables. 138 for (Variable Var = CNF.largestVar(); Var != NullVar; --Var) { 139 if (isWatched(posLit(Var)) || isWatched(negLit(Var))) 140 ActiveVars.push_back(Var); 141 } 142 } 143 144 // Returns the `Result` and the number of iterations "remaining" from 145 // `MaxIterations` (that is, `MaxIterations` - iterations in this call). 146 std::pair<Solver::Result, std::int64_t> solve(std::int64_t MaxIterations) && { 147 if (CNF.knownContradictory()) { 148 // Short-cut the solving process. We already found out at CNF 149 // construction time that the formula is unsatisfiable. 150 return std::make_pair(Solver::Result::Unsatisfiable(), MaxIterations); 151 } 152 size_t I = 0; 153 while (I < ActiveVars.size()) { 154 if (MaxIterations == 0) 155 return std::make_pair(Solver::Result::TimedOut(), 0); 156 --MaxIterations; 157 158 // Assert that the following invariants hold: 159 // 1. All active variables are unassigned. 160 // 2. All active variables form watched literals. 161 // 3. Unassigned variables that form watched literals are active. 162 // FIXME: Consider replacing these with test cases that fail if the any 163 // of the invariants is broken. That might not be easy due to the 164 // transformations performed by `buildCNF`. 165 assert(activeVarsAreUnassigned()); 166 assert(activeVarsFormWatchedLiterals()); 167 assert(unassignedVarsFormingWatchedLiteralsAreActive()); 168 169 const Variable ActiveVar = ActiveVars[I]; 170 171 // Look for unit clauses that contain the active variable. 172 const bool unitPosLit = watchedByUnitClause(posLit(ActiveVar)); 173 const bool unitNegLit = watchedByUnitClause(negLit(ActiveVar)); 174 if (unitPosLit && unitNegLit) { 175 // We found a conflict! 176 177 // Backtrack and rewind the `Level` until the most recent non-forced 178 // assignment. 179 reverseForcedMoves(); 180 181 // If the root level is reached, then all possible assignments lead to 182 // a conflict. 183 if (Level == 0) 184 return std::make_pair(Solver::Result::Unsatisfiable(), MaxIterations); 185 186 // Otherwise, take the other branch at the most recent level where a 187 // decision was made. 188 LevelStates[Level] = State::Forced; 189 const Variable Var = LevelVars[Level]; 190 VarAssignments[Var] = VarAssignments[Var] == Assignment::AssignedTrue 191 ? Assignment::AssignedFalse 192 : Assignment::AssignedTrue; 193 194 updateWatchedLiterals(); 195 } else if (unitPosLit || unitNegLit) { 196 // We found a unit clause! The value of its unassigned variable is 197 // forced. 198 ++Level; 199 200 LevelVars[Level] = ActiveVar; 201 LevelStates[Level] = State::Forced; 202 VarAssignments[ActiveVar] = 203 unitPosLit ? Assignment::AssignedTrue : Assignment::AssignedFalse; 204 205 // Remove the variable that was just assigned from the set of active 206 // variables. 207 if (I + 1 < ActiveVars.size()) { 208 // Replace the variable that was just assigned with the last active 209 // variable for efficient removal. 210 ActiveVars[I] = ActiveVars.back(); 211 } else { 212 // This was the last active variable. Repeat the process from the 213 // beginning. 214 I = 0; 215 } 216 ActiveVars.pop_back(); 217 218 updateWatchedLiterals(); 219 } else if (I + 1 == ActiveVars.size()) { 220 // There are no remaining unit clauses in the formula! Make a decision 221 // for one of the active variables at the current level. 222 ++Level; 223 224 LevelVars[Level] = ActiveVar; 225 LevelStates[Level] = State::Decision; 226 VarAssignments[ActiveVar] = decideAssignment(ActiveVar); 227 228 // Remove the variable that was just assigned from the set of active 229 // variables. 230 ActiveVars.pop_back(); 231 232 updateWatchedLiterals(); 233 234 // This was the last active variable. Repeat the process from the 235 // beginning. 236 I = 0; 237 } else { 238 ++I; 239 } 240 } 241 return std::make_pair(Solver::Result::Satisfiable(buildSolution()), 242 MaxIterations); 243 } 244 245 private: 246 /// Returns a satisfying truth assignment to the atoms in the boolean formula. 247 llvm::DenseMap<Atom, Solver::Result::Assignment> buildSolution() { 248 llvm::DenseMap<Atom, Solver::Result::Assignment> Solution; 249 for (auto &Atomic : Atomics) { 250 // A variable may have a definite true/false assignment, or it may be 251 // unassigned indicating its truth value does not affect the result of 252 // the formula. Unassigned variables are assigned to true as a default. 253 Solution[Atomic.second] = 254 VarAssignments[Atomic.first] == Assignment::AssignedFalse 255 ? Solver::Result::Assignment::AssignedFalse 256 : Solver::Result::Assignment::AssignedTrue; 257 } 258 return Solution; 259 } 260 261 /// Reverses forced moves until the most recent level where a decision was 262 /// made on the assignment of a variable. 263 void reverseForcedMoves() { 264 for (; LevelStates[Level] == State::Forced; --Level) { 265 const Variable Var = LevelVars[Level]; 266 267 VarAssignments[Var] = Assignment::Unassigned; 268 269 // If the variable that we pass through is watched then we add it to the 270 // active variables. 271 if (isWatched(posLit(Var)) || isWatched(negLit(Var))) 272 ActiveVars.push_back(Var); 273 } 274 } 275 276 /// Updates watched literals that are affected by a variable assignment. 277 void updateWatchedLiterals() { 278 const Variable Var = LevelVars[Level]; 279 280 // Update the watched literals of clauses that currently watch the literal 281 // that falsifies `Var`. 282 const Literal FalseLit = VarAssignments[Var] == Assignment::AssignedTrue 283 ? negLit(Var) 284 : posLit(Var); 285 ClauseID FalseLitWatcher = WatchedHead[FalseLit]; 286 WatchedHead[FalseLit] = NullClause; 287 while (FalseLitWatcher != NullClause) { 288 const ClauseID NextFalseLitWatcher = NextWatched[FalseLitWatcher]; 289 290 // Pick the first non-false literal as the new watched literal. 291 const CNFFormula::Iterator FalseLitWatcherStart = 292 CNF.startOfClause(FalseLitWatcher); 293 CNFFormula::Iterator NewWatchedLitIter = FalseLitWatcherStart.next(); 294 while (isCurrentlyFalse(*NewWatchedLitIter)) 295 ++NewWatchedLitIter; 296 const Literal NewWatchedLit = *NewWatchedLitIter; 297 const Variable NewWatchedLitVar = var(NewWatchedLit); 298 299 // Swap the old watched literal for the new one in `FalseLitWatcher` to 300 // maintain the invariant that the watched literal is at the beginning of 301 // the clause. 302 *NewWatchedLitIter = FalseLit; 303 *FalseLitWatcherStart = NewWatchedLit; 304 305 // If the new watched literal isn't watched by any other clause and its 306 // variable isn't assigned we need to add it to the active variables. 307 if (!isWatched(NewWatchedLit) && !isWatched(notLit(NewWatchedLit)) && 308 VarAssignments[NewWatchedLitVar] == Assignment::Unassigned) 309 ActiveVars.push_back(NewWatchedLitVar); 310 311 NextWatched[FalseLitWatcher] = WatchedHead[NewWatchedLit]; 312 WatchedHead[NewWatchedLit] = FalseLitWatcher; 313 314 // Go to the next clause that watches `FalseLit`. 315 FalseLitWatcher = NextFalseLitWatcher; 316 } 317 } 318 319 /// Returns true if and only if one of the clauses that watch `Lit` is a unit 320 /// clause. 321 bool watchedByUnitClause(Literal Lit) const { 322 for (ClauseID LitWatcher = WatchedHead[Lit]; LitWatcher != NullClause; 323 LitWatcher = NextWatched[LitWatcher]) { 324 llvm::ArrayRef<Literal> Clause = CNF.clauseLiterals(LitWatcher); 325 326 // Assert the invariant that the watched literal is always the first one 327 // in the clause. 328 // FIXME: Consider replacing this with a test case that fails if the 329 // invariant is broken by `updateWatchedLiterals`. That might not be easy 330 // due to the transformations performed by `buildCNF`. 331 assert(Clause.front() == Lit); 332 333 if (isUnit(Clause)) 334 return true; 335 } 336 return false; 337 } 338 339 /// Returns true if and only if `Clause` is a unit clause. 340 bool isUnit(llvm::ArrayRef<Literal> Clause) const { 341 return llvm::all_of(Clause.drop_front(), 342 [this](Literal L) { return isCurrentlyFalse(L); }); 343 } 344 345 /// Returns true if and only if `Lit` evaluates to `false` in the current 346 /// partial assignment. 347 bool isCurrentlyFalse(Literal Lit) const { 348 return static_cast<int8_t>(VarAssignments[var(Lit)]) == 349 static_cast<int8_t>(Lit & 1); 350 } 351 352 /// Returns true if and only if `Lit` is watched by a clause in `Formula`. 353 bool isWatched(Literal Lit) const { return WatchedHead[Lit] != NullClause; } 354 355 /// Returns an assignment for an unassigned variable. 356 Assignment decideAssignment(Variable Var) const { 357 return !isWatched(posLit(Var)) || isWatched(negLit(Var)) 358 ? Assignment::AssignedFalse 359 : Assignment::AssignedTrue; 360 } 361 362 /// Returns a set of all watched literals. 363 llvm::DenseSet<Literal> watchedLiterals() const { 364 llvm::DenseSet<Literal> WatchedLiterals; 365 for (Literal Lit = 2; Lit < WatchedHead.size(); Lit++) { 366 if (WatchedHead[Lit] == NullClause) 367 continue; 368 WatchedLiterals.insert(Lit); 369 } 370 return WatchedLiterals; 371 } 372 373 /// Returns true if and only if all active variables are unassigned. 374 bool activeVarsAreUnassigned() const { 375 return llvm::all_of(ActiveVars, [this](Variable Var) { 376 return VarAssignments[Var] == Assignment::Unassigned; 377 }); 378 } 379 380 /// Returns true if and only if all active variables form watched literals. 381 bool activeVarsFormWatchedLiterals() const { 382 const llvm::DenseSet<Literal> WatchedLiterals = watchedLiterals(); 383 return llvm::all_of(ActiveVars, [&WatchedLiterals](Variable Var) { 384 return WatchedLiterals.contains(posLit(Var)) || 385 WatchedLiterals.contains(negLit(Var)); 386 }); 387 } 388 389 /// Returns true if and only if all unassigned variables that are forming 390 /// watched literals are active. 391 bool unassignedVarsFormingWatchedLiteralsAreActive() const { 392 const llvm::DenseSet<Variable> ActiveVarsSet(ActiveVars.begin(), 393 ActiveVars.end()); 394 for (Literal Lit : watchedLiterals()) { 395 const Variable Var = var(Lit); 396 if (VarAssignments[Var] != Assignment::Unassigned) 397 continue; 398 if (ActiveVarsSet.contains(Var)) 399 continue; 400 return false; 401 } 402 return true; 403 } 404 }; 405 406 } // namespace 407 408 Solver::Result 409 WatchedLiteralsSolver::solve(llvm::ArrayRef<const Formula *> Vals) { 410 if (Vals.empty()) 411 return Solver::Result::Satisfiable({{}}); 412 auto [Res, Iterations] = WatchedLiteralsSolverImpl(Vals).solve(MaxIterations); 413 MaxIterations = Iterations; 414 return Res; 415 } 416 417 } // namespace dataflow 418 } // namespace clang 419