12
12
// ===----------------------------------------------------------------------===//
13
13
14
14
#include < cassert>
15
+ #include < cstddef>
15
16
#include < cstdint>
16
- #include < iterator>
17
17
#include < queue>
18
18
#include < vector>
19
19
23
23
#include " llvm/ADT/ArrayRef.h"
24
24
#include " llvm/ADT/DenseMap.h"
25
25
#include " llvm/ADT/DenseSet.h"
26
+ #include " llvm/ADT/SmallVector.h"
26
27
#include " llvm/ADT/STLExtras.h"
27
28
29
+
28
30
namespace clang {
29
31
namespace dataflow {
30
32
@@ -62,6 +64,10 @@ static constexpr Literal NullLit = 0;
62
64
// / Returns the positive literal `V`.
63
65
static constexpr Literal posLit (Variable V) { return 2 * V; }
64
66
67
+ static constexpr bool isPosLit (Literal L) { return 0 == (L & 1 ); }
68
+
69
+ static constexpr bool isNegLit (Literal L) { return 1 == (L & 1 ); }
70
+
65
71
// / Returns the negative literal `!V`.
66
72
static constexpr Literal negLit (Variable V) { return 2 * V + 1 ; }
67
73
@@ -125,43 +131,39 @@ struct CNFFormula {
125
131
// / formula.
126
132
llvm::DenseMap<Variable, Atom> Atomics;
127
133
134
+ // / Indicates that we already know the formula is unsatisfiable.
135
+ // / During construction, we catch simple cases of conflicting unit-clauses.
136
+ bool KnownContradictory;
137
+
128
138
explicit CNFFormula (Variable LargestVar,
129
139
llvm::DenseMap<Variable, Atom> Atomics)
130
- : LargestVar(LargestVar), Atomics(std::move(Atomics)) {
140
+ : LargestVar(LargestVar), Atomics(std::move(Atomics)),
141
+ KnownContradictory(false ) {
131
142
Clauses.push_back (0 );
132
143
ClauseStarts.push_back (0 );
133
144
NextWatched.push_back (0 );
134
145
const size_t NumLiterals = 2 * LargestVar + 1 ;
135
146
WatchedHead.resize (NumLiterals + 1 , 0 );
136
147
}
137
148
138
- // / Adds the `L1 v L2 v L3` clause to the formula. If `L2` or `L3` are
139
- // / `NullLit` they are respectively omitted from the clause.
140
- // /
149
+ // / Adds the `L1 v ... v Ln` clause to the formula.
141
150
// / Requirements:
142
151
// /
143
- // / `L1 ` must not be `NullLit`.
152
+ // / `Li ` must not be `NullLit`.
144
153
// /
145
154
// / All literals in the input that are not `NullLit` must be distinct.
146
- void addClause (Literal L1, Literal L2 = NullLit, Literal L3 = NullLit) {
147
- // The literals are guaranteed to be distinct from properties of Formula
148
- // and the construction in `buildCNF`.
149
- assert (L1 != NullLit && L1 != L2 && L1 != L3 &&
150
- (L2 != L3 || L2 == NullLit));
155
+ void addClause (ArrayRef<Literal> lits) {
156
+ assert (!lits.empty ());
157
+ assert (llvm::all_of (lits, [](Literal L) { return L != NullLit; }));
151
158
152
159
const ClauseID C = ClauseStarts.size ();
153
160
const size_t S = Clauses.size ();
154
161
ClauseStarts.push_back (S);
155
-
156
- Clauses.push_back (L1);
157
- if (L2 != NullLit)
158
- Clauses.push_back (L2);
159
- if (L3 != NullLit)
160
- Clauses.push_back (L3);
162
+ Clauses.insert (Clauses.end (), lits.begin (), lits.end ());
161
163
162
164
// Designate the first literal as the "watched" literal of the clause.
163
- NextWatched.push_back (WatchedHead[L1 ]);
164
- WatchedHead[L1 ] = C;
165
+ NextWatched.push_back (WatchedHead[lits. front () ]);
166
+ WatchedHead[lits. front () ] = C;
165
167
}
166
168
167
169
// / Returns the number of literals in clause `C`.
@@ -176,6 +178,84 @@ struct CNFFormula {
176
178
}
177
179
};
178
180
181
+ // / Applies simplifications while building up a BooleanFormula.
182
+ // / We keep track of unit clauses, which tell us variables that must be
183
+ // / true/false in any model that satisfies the overall formula.
184
+ // / Such variables can be dropped from subsequently-added clauses, which
185
+ // / may in turn yield more unit clauses or even a contradiction.
186
+ // / The total added complexity of this preprocessing is O(N) where we
187
+ // / for every clause, we do a lookup for each unit clauses.
188
+ // / The lookup is O(1) on average. This method won't catch all
189
+ // / contradictory formulas, more passes can in principle catch
190
+ // / more cases but we leave all these and the general case to the
191
+ // / proper SAT solver.
192
+ struct CNFFormulaBuilder {
193
+ // Formula should outlive CNFFormulaBuilder.
194
+ explicit CNFFormulaBuilder (CNFFormula &CNF)
195
+ : Formula(CNF) {}
196
+
197
+ // / Adds the `L1 v ... v Ln` clause to the formula. Applies
198
+ // / simplifications, based on single-literal clauses.
199
+ // /
200
+ // / Requirements:
201
+ // /
202
+ // / `Li` must not be `NullLit`.
203
+ // /
204
+ // / All literals must be distinct.
205
+ void addClause (ArrayRef<Literal> Literals) {
206
+ // We generate clauses with up to 3 literals in this file.
207
+ assert (!Literals.empty () && Literals.size () <= 3 );
208
+ // Contains literals of the simplified clause.
209
+ llvm::SmallVector<Literal> Simplified;
210
+ for (auto L : Literals) {
211
+ assert (L != NullLit &&
212
+ llvm::all_of (Simplified,
213
+ [L](Literal S) { return S != L; }));
214
+ auto X = var (L);
215
+ if (trueVars.contains (X)) { // X must be true
216
+ if (isPosLit (L))
217
+ return ; // Omit clause `(... v X v ...)`, it is `true`.
218
+ else
219
+ continue ; // Omit `!X` from `(... v !X v ...)`.
220
+ }
221
+ if (falseVars.contains (X)) { // X must be false
222
+ if (isNegLit (L))
223
+ return ; // Omit clause `(... v !X v ...)`, it is `true`.
224
+ else
225
+ continue ; // Omit `X` from `(... v X v ...)`.
226
+ }
227
+ Simplified.push_back (L);
228
+ }
229
+ if (Simplified.empty ()) {
230
+ // Simplification made the clause empty, which is equivalent to `false`.
231
+ // We already know that this formula is unsatisfiable.
232
+ Formula.KnownContradictory = true ;
233
+ // We can add any of the input literals to get an unsatisfiable formula.
234
+ Formula.addClause (Literals[0 ]);
235
+ return ;
236
+ }
237
+ if (Simplified.size () == 1 ) {
238
+ // We have new unit clause.
239
+ const Literal lit = Simplified.front ();
240
+ const Variable v = var (lit);
241
+ if (isPosLit (lit))
242
+ trueVars.insert (v);
243
+ else
244
+ falseVars.insert (v);
245
+ }
246
+ Formula.addClause (Simplified);
247
+ }
248
+
249
+ // / Returns true if we observed a contradiction while adding clauses.
250
+ // / In this case then the formula is already known to be unsatisfiable.
251
+ bool isKnownContradictory () { return Formula.KnownContradictory ; }
252
+
253
+ private:
254
+ CNFFormula &Formula;
255
+ llvm::DenseSet<Variable> trueVars;
256
+ llvm::DenseSet<Variable> falseVars;
257
+ };
258
+
179
259
// / Converts the conjunction of `Vals` into a formula in conjunctive normal
180
260
// / form where each clause has at least one and at most three literals.
181
261
CNFFormula buildCNF (const llvm::ArrayRef<const Formula *> &Vals) {
@@ -218,11 +298,12 @@ CNFFormula buildCNF(const llvm::ArrayRef<const Formula *> &Vals) {
218
298
219
299
CNFFormula CNF (NextVar - 1 , std::move (Atomics));
220
300
std::vector<bool > ProcessedSubVals (NextVar, false );
301
+ CNFFormulaBuilder builder (CNF);
221
302
222
- // Add a conjunct for each variable that represents a top-level formula in
223
- // `Vals`.
303
+ // Add a conjunct for each variable that represents a top-level conjunction
304
+ // value in `Vals`.
224
305
for (const Formula *Val : Vals)
225
- CNF .addClause (posLit (GetVar (Val)));
306
+ builder .addClause (posLit (GetVar (Val)));
226
307
227
308
// Add conjuncts that represent the mapping between newly-created variables
228
309
// and their corresponding sub-formulas.
@@ -249,15 +330,15 @@ CNFFormula buildCNF(const llvm::ArrayRef<const Formula *> &Vals) {
249
330
// `X <=> (A ^ A)` is equivalent to `(!X v A) ^ (X v !A)` which is
250
331
// already in conjunctive normal form. Below we add each of the
251
332
// conjuncts of the latter expression to the result.
252
- CNF .addClause (negLit (Var), posLit (LHS));
253
- CNF .addClause (posLit (Var), negLit (LHS));
333
+ builder .addClause ({ negLit (Var), posLit (LHS)} );
334
+ builder .addClause ({ posLit (Var), negLit (LHS)} );
254
335
} else {
255
- // `X <=> (A ^ B)` is equivalent to `(!X v A) ^ (!X v B) ^ (X v !A v !B)`
256
- // which is already in conjunctive normal form. Below we add each of the
257
- // conjuncts of the latter expression to the result.
258
- CNF .addClause (negLit (Var), posLit (LHS));
259
- CNF .addClause (negLit (Var), posLit (RHS));
260
- CNF .addClause (posLit (Var), negLit (LHS), negLit (RHS));
336
+ // `X <=> (A ^ B)` is equivalent to `(!X v A) ^ (!X v B) ^ (X v !A v
337
+ // !B)` which is already in conjunctive normal form. Below we add each
338
+ // of the conjuncts of the latter expression to the result.
339
+ builder .addClause ({ negLit (Var), posLit (LHS)} );
340
+ builder .addClause ({ negLit (Var), posLit (RHS)} );
341
+ builder .addClause ({ posLit (Var), negLit (LHS), negLit (RHS)} );
261
342
}
262
343
break ;
263
344
}
@@ -269,15 +350,15 @@ CNFFormula buildCNF(const llvm::ArrayRef<const Formula *> &Vals) {
269
350
// `X <=> (A v A)` is equivalent to `(!X v A) ^ (X v !A)` which is
270
351
// already in conjunctive normal form. Below we add each of the
271
352
// conjuncts of the latter expression to the result.
272
- CNF .addClause (negLit (Var), posLit (LHS));
273
- CNF .addClause (posLit (Var), negLit (LHS));
353
+ builder .addClause ({ negLit (Var), posLit (LHS)} );
354
+ builder .addClause ({ posLit (Var), negLit (LHS)} );
274
355
} else {
275
356
// `X <=> (A v B)` is equivalent to `(!X v A v B) ^ (X v !A) ^ (X v
276
357
// !B)` which is already in conjunctive normal form. Below we add each
277
358
// of the conjuncts of the latter expression to the result.
278
- CNF .addClause (negLit (Var), posLit (LHS), posLit (RHS));
279
- CNF .addClause (posLit (Var), negLit (LHS));
280
- CNF .addClause (posLit (Var), negLit (RHS));
359
+ builder .addClause ({ negLit (Var), posLit (LHS), posLit (RHS)} );
360
+ builder .addClause ({ posLit (Var), negLit (LHS)} );
361
+ builder .addClause ({ posLit (Var), negLit (RHS)} );
281
362
}
282
363
break ;
283
364
}
@@ -287,8 +368,8 @@ CNFFormula buildCNF(const llvm::ArrayRef<const Formula *> &Vals) {
287
368
// `X <=> !Y` is equivalent to `(!X v !Y) ^ (X v Y)` which is
288
369
// already in conjunctive normal form. Below we add each of the
289
370
// conjuncts of the latter expression to the result.
290
- CNF .addClause (negLit (Var), negLit (Operand));
291
- CNF .addClause (posLit (Var), posLit (Operand));
371
+ builder .addClause ({ negLit (Var), negLit (Operand)} );
372
+ builder .addClause ({ posLit (Var), posLit (Operand)} );
292
373
break ;
293
374
}
294
375
case Formula::Implies: {
@@ -299,20 +380,20 @@ CNFFormula buildCNF(const llvm::ArrayRef<const Formula *> &Vals) {
299
380
// `(X v A) ^ (X v !B) ^ (!X v !A v B)` which is already in
300
381
// conjunctive normal form. Below we add each of the conjuncts of
301
382
// the latter expression to the result.
302
- CNF .addClause (posLit (Var), posLit (LHS));
303
- CNF .addClause (posLit (Var), negLit (RHS));
304
- CNF .addClause (negLit (Var), negLit (LHS), posLit (RHS));
383
+ builder .addClause ({ posLit (Var), posLit (LHS)} );
384
+ builder .addClause ({ posLit (Var), negLit (RHS)} );
385
+ builder .addClause ({ negLit (Var), negLit (LHS), posLit (RHS)} );
305
386
break ;
306
387
}
307
388
case Formula::Equal: {
308
389
const Variable LHS = GetVar (Val->operands ()[0 ]);
309
390
const Variable RHS = GetVar (Val->operands ()[1 ]);
310
391
311
392
if (LHS == RHS) {
312
- // `X <=> (A <=> A)` is equvalent to `X` which is already in
393
+ // `X <=> (A <=> A)` is equivalent to `X` which is already in
313
394
// conjunctive normal form. Below we add each of the conjuncts of the
314
395
// latter expression to the result.
315
- CNF .addClause (posLit (Var));
396
+ builder .addClause (posLit (Var));
316
397
317
398
// No need to visit the sub-values of `Val`.
318
399
continue ;
@@ -321,18 +402,43 @@ CNFFormula buildCNF(const llvm::ArrayRef<const Formula *> &Vals) {
321
402
// `(X v A v B) ^ (X v !A v !B) ^ (!X v A v !B) ^ (!X v !A v B)` which
322
403
// is already in conjunctive normal form. Below we add each of the
323
404
// conjuncts of the latter expression to the result.
324
- CNF .addClause (posLit (Var), posLit (LHS), posLit (RHS));
325
- CNF .addClause (posLit (Var), negLit (LHS), negLit (RHS));
326
- CNF .addClause (negLit (Var), posLit (LHS), negLit (RHS));
327
- CNF .addClause (negLit (Var), negLit (LHS), posLit (RHS));
405
+ builder .addClause ({ posLit (Var), posLit (LHS), posLit (RHS)} );
406
+ builder .addClause ({ posLit (Var), negLit (LHS), negLit (RHS)} );
407
+ builder .addClause ({ negLit (Var), posLit (LHS), negLit (RHS)} );
408
+ builder .addClause ({ negLit (Var), negLit (LHS), posLit (RHS)} );
328
409
break ;
329
410
}
330
411
}
412
+ if (builder.isKnownContradictory ()) {
413
+ return CNF;
414
+ }
331
415
for (const Formula *Child : Val->operands ())
332
416
UnprocessedSubVals.push (Child);
333
417
}
334
418
335
- return CNF;
419
+ // Unit clauses that were added later were not
420
+ // considered for the simplification of earlier clauses. Do a final
421
+ // pass to find more opportunities for simplification.
422
+ CNFFormula FinalCNF (NextVar - 1 , std::move (CNF.Atomics ));
423
+ CNFFormulaBuilder FinalBuilder (FinalCNF);
424
+
425
+ // Collect unit clauses.
426
+ for (ClauseID C = 1 ; C < CNF.ClauseStarts .size (); ++C) {
427
+ if (CNF.clauseSize (C) == 1 ) {
428
+ FinalBuilder.addClause (CNF.clauseLiterals (C)[0 ]);
429
+ }
430
+ }
431
+
432
+ // Add all clauses that were added previously, preserving the order.
433
+ for (ClauseID C = 1 ; C < CNF.ClauseStarts .size (); ++C) {
434
+ FinalBuilder.addClause (CNF.clauseLiterals (C));
435
+ if (FinalBuilder.isKnownContradictory ()) {
436
+ break ;
437
+ }
438
+ }
439
+ // It is possible there were new unit clauses again, but
440
+ // we stop here and leave the rest to the solver algorithm.
441
+ return FinalCNF;
336
442
}
337
443
338
444
class WatchedLiteralsSolverImpl {
@@ -414,6 +520,11 @@ class WatchedLiteralsSolverImpl {
414
520
// Returns the `Result` and the number of iterations "remaining" from
415
521
// `MaxIterations` (that is, `MaxIterations` - iterations in this call).
416
522
std::pair<Solver::Result, std::int64_t > solve (std::int64_t MaxIterations) && {
523
+ if (CNF.KnownContradictory ) {
524
+ // Short-cut the solving process. We already found out at CNF
525
+ // construction time that the formula is unsatisfiable.
526
+ return std::make_pair (Solver::Result::Unsatisfiable (), MaxIterations);
527
+ }
417
528
size_t I = 0 ;
418
529
while (I < ActiveVars.size ()) {
419
530
if (MaxIterations == 0 )
@@ -503,7 +614,8 @@ class WatchedLiteralsSolverImpl {
503
614
++I;
504
615
}
505
616
}
506
- return std::make_pair (Solver::Result::Satisfiable (buildSolution ()), MaxIterations);
617
+ return std::make_pair (Solver::Result::Satisfiable (buildSolution ()),
618
+ MaxIterations);
507
619
}
508
620
509
621
private:
@@ -672,8 +784,7 @@ Solver::Result
672
784
WatchedLiteralsSolver::solve (llvm::ArrayRef<const Formula *> Vals) {
673
785
if (Vals.empty ())
674
786
return Solver::Result::Satisfiable ({{}});
675
- auto [Res, Iterations] =
676
- WatchedLiteralsSolverImpl (Vals).solve (MaxIterations);
787
+ auto [Res, Iterations] = WatchedLiteralsSolverImpl (Vals).solve (MaxIterations);
677
788
MaxIterations = Iterations;
678
789
return Res;
679
790
}
0 commit comments