GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/preprocessing/passes/bool_to_bv.cpp Lines: 176 182 96.7 %
Date: 2021-08-14 Branches: 408 783 52.1 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Makai Mann, Yoni Zohar, Clark Barrett
4
 *
5
 * This file is part of the cvc5 project.
6
 *
7
 * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS
8
 * in the top-level source directory and their institutional affiliations.
9
 * All rights reserved.  See the file COPYING in the top-level source
10
 * directory for licensing information.
11
 * ****************************************************************************
12
 *
13
 * The BoolToBV preprocessing pass.
14
 *
15
 */
16
17
#include "preprocessing/passes/bool_to_bv.h"
18
19
#include <string>
20
21
#include "base/map_util.h"
22
#include "expr/node.h"
23
#include "preprocessing/assertion_pipeline.h"
24
#include "preprocessing/preprocessing_pass_context.h"
25
#include "smt/smt_statistics_registry.h"
26
#include "theory/bv/theory_bv_utils.h"
27
#include "theory/rewriter.h"
28
#include "theory/theory.h"
29
30
namespace cvc5 {
31
namespace preprocessing {
32
namespace passes {
33
using namespace cvc5::theory;
34
35
9853
BoolToBV::BoolToBV(PreprocessingPassContext* preprocContext)
36
9853
    : PreprocessingPass(preprocContext, "bool-to-bv"), d_statistics()
37
{
38
9853
  d_boolToBVMode = options::boolToBitvector();
39
9853
};
40
41
12
PreprocessingPassResult BoolToBV::applyInternal(
42
    AssertionPipeline* assertionsToPreprocess)
43
{
44
12
  d_preprocContext->spendResource(Resource::PreprocessStep);
45
46
12
  size_t size = assertionsToPreprocess->size();
47
48
12
  if (d_boolToBVMode == options::BoolToBVMode::ALL)
49
  {
50
74
    for (size_t i = 0; i < size; ++i)
51
    {
52
132
      Node newAssertion = lowerAssertion((*assertionsToPreprocess)[i], true);
53
66
      assertionsToPreprocess->replace(i, Rewriter::rewrite(newAssertion));
54
    }
55
  }
56
  else
57
  {
58
4
    Assert(d_boolToBVMode == options::BoolToBVMode::ITE);
59
24
    for (size_t i = 0; i < size; ++i)
60
    {
61
20
      assertionsToPreprocess->replace(
62
40
          i, Rewriter::rewrite(lowerIte((*assertionsToPreprocess)[i])));
63
    }
64
  }
65
66
12
  return PreprocessingPassResult::NO_CONFLICT;
67
}
68
69
158
void BoolToBV::updateCache(TNode n, TNode rebuilt)
70
{
71
  // check more likely case first
72
158
  if ((n.getKind() != kind::ITE) || !n[1].getType().isBitVector())
73
  {
74
154
    d_lowerCache[n] = rebuilt;
75
  }
76
  else
77
  {
78
4
    d_iteBVLowerCache[n] = rebuilt;
79
  }
80
158
}
81
82
1342
Node BoolToBV::fromCache(TNode n) const
83
{
84
  // check more likely case first
85
1342
  if (n.getKind() != kind::ITE)
86
  {
87
1326
    if (d_lowerCache.find(n) != d_lowerCache.end())
88
    {
89
734
      return d_lowerCache.at(n);
90
    }
91
  }
92
  else
93
  {
94
16
    if (d_iteBVLowerCache.find(n) != d_iteBVLowerCache.end())
95
    {
96
8
      return d_iteBVLowerCache.at(n);
97
    }
98
  }
99
600
  return n;
100
}
101
102
216
inline bool BoolToBV::inCache(const Node& n) const
103
{
104
216
  return (ContainsKey(d_lowerCache, n) || ContainsKey(d_iteBVLowerCache, n));
105
}
106
107
536
bool BoolToBV::needToRebuild(TNode n) const
108
{
109
  // check if any children were rebuilt
110
748
  for (const Node& nn : n)
111
  {
112
216
    if (inCache(nn))
113
    {
114
4
      return true;
115
    }
116
  }
117
532
  return false;
118
}
119
120
66
Node BoolToBV::lowerAssertion(const TNode& assertion, bool allowIteIntroduction)
121
{
122
  // first try to lower all the children
123
168
  for (const Node& c : assertion)
124
  {
125
102
    lowerNode(c, allowIteIntroduction);
126
  }
127
128
  // now try lowering the assertion, but don't force it with an ITE (even in mode all)
129
66
  lowerNode(assertion, false);
130
66
  Node newAssertion = fromCache(assertion);
131
132
  TypeNode newAssertionType = newAssertion.getType();
132
66
  if (newAssertionType.isBitVector())
133
  {
134
62
    Assert(newAssertionType.getBitVectorSize() == 1);
135
124
    newAssertion = NodeManager::currentNM()->mkNode(
136
124
        kind::EQUAL, newAssertion, bv::utils::mkOne(1));
137
62
    newAssertionType = newAssertion.getType();
138
  }
139
66
  Assert(newAssertionType.isBoolean());
140
132
  return newAssertion;
141
}
142
143
172
Node BoolToBV::lowerNode(const TNode& node, bool allowIteIntroduction)
144
{
145
344
  std::vector<TNode> to_visit;
146
172
  to_visit.push_back(node);
147
344
  std::unordered_set<TNode> visited;
148
149
2540
  while (!to_visit.empty())
150
  {
151
2368
    TNode n = to_visit.back();
152
1184
    to_visit.pop_back();
153
154
2368
    Debug("bool-to-bv") << "BoolToBV::lowerNode: Post-order traversal with "
155
2368
                        << n << " and visited = " << ContainsKey(visited, n)
156
1184
                        << std::endl;
157
158
    // Mark as visited
159
1184
    if (ContainsKey(visited, n))
160
    {
161
614
      visit(n, allowIteIntroduction);
162
    }
163
    else
164
    {
165
570
      visited.insert(n);
166
570
      to_visit.push_back(n);
167
168
      // insert children in reverse order so that they're processed in order
169
      //    important for rewriting which sorts by node id
170
      // NOTE: size_t is unsigned, so using underflow for termination condition
171
570
      size_t numChildren = n.getNumChildren();
172
1012
      for (size_t i = numChildren - 1; i < numChildren; --i)
173
      {
174
442
        to_visit.push_back(n[i]);
175
      }
176
    }
177
  }
178
179
344
  return fromCache(node);
180
}
181
182
614
void BoolToBV::visit(const TNode& n, bool allowIteIntroduction)
183
{
184
614
  Kind k = n.getKind();
185
186
  // easy case -- just replace boolean constant
187
614
  if (k == kind::CONST_BOOLEAN)
188
  {
189
10
    updateCache(n,
190
20
                (n == bv::utils::mkTrue()) ? bv::utils::mkOne(1)
191
                                           : bv::utils::mkZero(1));
192
132
    return;
193
  }
194
195
604
  NodeManager* nm = NodeManager::currentNM();
196
604
  Kind new_kind = k;
197
604
  switch (k)
198
  {
199
38
    case kind::EQUAL: new_kind = kind::BITVECTOR_COMP; break;
200
10
    case kind::AND: new_kind = kind::BITVECTOR_AND; break;
201
4
    case kind::OR: new_kind = kind::BITVECTOR_OR; break;
202
26
    case kind::NOT: new_kind = kind::BITVECTOR_NOT; break;
203
6
    case kind::XOR: new_kind = kind::BITVECTOR_XOR; break;
204
20
    case kind::IMPLIES: new_kind = kind::BITVECTOR_OR; break;
205
12
    case kind::ITE: new_kind = kind::BITVECTOR_ITE; break;
206
16
    case kind::BITVECTOR_ULT: new_kind = kind::BITVECTOR_ULTBV; break;
207
4
    case kind::BITVECTOR_SLT: new_kind = kind::BITVECTOR_SLTBV; break;
208
    case kind::BITVECTOR_ULE:
209
    case kind::BITVECTOR_UGT:
210
    case kind::BITVECTOR_UGE:
211
    case kind::BITVECTOR_SLE:
212
    case kind::BITVECTOR_SGT:
213
    case kind::BITVECTOR_SGE:
214
      // Should have been removed by rewriting.
215
      Unreachable();
216
468
    default: break;
217
  }
218
219
  // check if it's safe to lower or rebuild the node
220
  // Note: might have to rebuild to keep changes to children, even if this node
221
  // isn't being lowered
222
223
  // it's safe to lower if all the children are bit-vectors
224
604
  bool safe_to_lower =
225
604
      (new_kind != k);  // don't need to lower at all if kind hasn't changed
226
227
  // it's safe to rebuild if rebuilding doesn't change any of the types of the
228
  // children
229
604
  bool safe_to_rebuild = true;
230
231
998
  for (const Node& nn : n)
232
  {
233
434
    safe_to_lower = safe_to_lower && fromCache(nn).getType().isBitVector();
234
434
    safe_to_rebuild = safe_to_rebuild && (fromCache(nn).getType() == nn.getType());
235
236
    // if it's already not safe to do either, stop checking
237
434
    if (!safe_to_lower && !safe_to_rebuild)
238
    {
239
40
      break;
240
    }
241
  }
242
243
1208
  Debug("bool-to-bv") << "safe_to_lower = " << safe_to_lower
244
604
                      << ", safe_to_rebuild = " << safe_to_rebuild << std::endl;
245
246
604
  if (new_kind != k && safe_to_lower)
247
  {
248
    // lower to BV
249
104
    rebuildNode(n, new_kind);
250
104
    return;
251
  }
252
500
  else if (new_kind != k && allowIteIntroduction && fromCache(n).getType().isBoolean())
253
  {
254
    // lower to BV using an ITE
255
256
8
    if (safe_to_rebuild && needToRebuild(n))
257
    {
258
      // need to rebuild to keep changes made to descendants
259
      rebuildNode(n, k);
260
    }
261
262
8
    updateCache(n,
263
48
                nm->mkNode(kind::ITE,
264
16
                           fromCache(n),
265
16
                           bv::utils::mkOne(1),
266
16
                           bv::utils::mkZero(1)));
267
16
    Debug("bool-to-bv") << "BoolToBV::visit forcing " << n
268
8
                        << " =>\n"
269
8
                        << fromCache(n) << std::endl;
270
8
    if (d_boolToBVMode == options::BoolToBVMode::ALL)
271
    {
272
      // this statistic only makes sense for ALL mode
273
8
      ++(d_statistics.d_numIntroducedItes);
274
    }
275
8
    return;
276
  }
277
492
  else if (safe_to_rebuild && needToRebuild(n))
278
  {
279
    // rebuild to incorporate changes to children
280
    Assert(k == new_kind);
281
    rebuildNode(n, k);
282
  }
283
492
  else if (allowIteIntroduction && fromCache(n).getType().isBoolean())
284
  {
285
    // force booleans (which haven't already been converted) to bit-vector
286
    // needed to maintain the invariant that all boolean children
287
    // have been converted (even constants and variables) when forcing
288
    // with ITE introductions
289
32
    updateCache(
290
64
        n, nm->mkNode(kind::ITE, n, bv::utils::mkOne(1), bv::utils::mkZero(1)));
291
64
    Debug("bool-to-bv") << "BoolToBV::visit forcing " << n
292
32
                        << " =>\n"
293
32
                        << fromCache(n) << std::endl;
294
32
    if (d_boolToBVMode == options::BoolToBVMode::ALL)
295
    {
296
      // this statistic only makes sense for ALL mode
297
32
      ++(d_statistics.d_numIntroducedItes);
298
    }
299
  }
300
  else
301
  {
302
    // do nothing
303
920
    Debug("bool-to-bv") << "BoolToBV::visit skipping: " << n
304
460
                        << std::endl;
305
  }
306
}
307
308
20
Node BoolToBV::lowerIte(const TNode& node)
309
{
310
40
  std::vector<TNode> visit;
311
20
  visit.push_back(node);
312
40
  std::unordered_set<TNode> visited;
313
314
316
  while (!visit.empty())
315
  {
316
296
    TNode n = visit.back();
317
148
    visit.pop_back();
318
319
296
    Debug("bool-to-bv") << "BoolToBV::lowerIte: Post-order traversal with " << n
320
296
                        << " and visited = " << ContainsKey(visited, n)
321
148
                        << std::endl;
322
323
    // Look for ITEs and mark visited
324
148
    if (!ContainsKey(visited, n))
325
    {
326
72
      if ((n.getKind() == kind::ITE) && n[1].getType().isBitVector())
327
      {
328
8
        Debug("bool-to-bv") << "BoolToBV::lowerIte: adding " << n[0]
329
4
                            << " to set of ite conditions" << std::endl;
330
        // don't force in this case -- forcing only introduces more ITEs
331
8
        Node loweredNode = lowerNode(n, false);
332
        // some of the lowered nodes might appear elsewhere but not in an ITE
333
        // reset the cache to prevent lowering them
334
        // the bit-vector ITEs are still tracked in d_iteBVLowerCache though
335
4
        d_lowerCache.clear();
336
      }
337
      else
338
      {
339
68
        visit.push_back(n);
340
68
        visited.insert(n);
341
        // insert in reverse order so that they're processed in order
342
128
        for (int i = n.getNumChildren() - 1; i >= 0; --i)
343
        {
344
60
          visit.push_back(n[i]);
345
        }
346
      }
347
    }
348
76
    else if (needToRebuild(n))
349
    {
350
      // Note: it's always safe to rebuild here, because we've only lowered
351
      //       ITEs of type bit-vector to BITVECTOR_ITE
352
4
      rebuildNode(n, n.getKind());
353
    }
354
    else
355
    {
356
144
      Debug("bool-to-bv")
357
72
          << "BoolToBV::lowerIte Skipping because don't need to rebuild: " << n
358
72
          << std::endl;
359
    }
360
  }
361
40
  return fromCache(node);
362
}
363
364
108
void BoolToBV::rebuildNode(const TNode& n, Kind new_kind)
365
{
366
108
  Kind k = n.getKind();
367
108
  NodeManager* nm = NodeManager::currentNM();
368
216
  NodeBuilder builder(new_kind);
369
370
216
  Debug("bool-to-bv") << "BoolToBV::rebuildNode with " << n
371
216
                      << " and new_kind = " << kindToString(new_kind)
372
108
                      << std::endl;
373
374
108
  if ((d_boolToBVMode == options::BoolToBVMode::ALL) && (new_kind != k))
375
  {
376
    // this statistic only makes sense for ALL mode
377
96
    ++(d_statistics.d_numTermsLowered);
378
  }
379
380
108
  if (n.getMetaKind() == kind::metakind::PARAMETERIZED)
381
  {
382
    builder << n.getOperator();
383
  }
384
385
  // special case IMPLIES because needs to be rewritten
386
108
  if ((k == kind::IMPLIES) && (new_kind != k))
387
  {
388
20
    builder << nm->mkNode(kind::BITVECTOR_NOT, fromCache(n[0]));
389
20
    builder << fromCache(n[1]);
390
  }
391
  else
392
  {
393
250
    for (const Node& nn : n)
394
    {
395
162
      builder << fromCache(nn);
396
    }
397
  }
398
399
216
  Debug("bool-to-bv") << "BoolToBV::rebuildNode " << n << " =>\n"
400
108
                      << builder << std::endl;
401
402
108
  updateCache(n, builder.constructNode());
403
108
}
404
405
9853
BoolToBV::Statistics::Statistics()
406
9853
    : d_numIteToBvite(smtStatisticsRegistry().registerInt(
407
19706
        "preprocessing::passes::BoolToBV::NumIteToBvite")),
408
      // the following two statistics are not correct in the ITE mode, because
409
      // we might discard rebuilt nodes if we fails to convert a bool to
410
      // width-one bit-vector (never forces)
411
9853
      d_numTermsLowered(smtStatisticsRegistry().registerInt(
412
19706
          "preprocessing::passes:BoolToBV::NumTermsLowered")),
413
9853
      d_numIntroducedItes(smtStatisticsRegistry().registerInt(
414
29559
          "preprocessing::passes::BoolToBV::NumTermsForcedLowered"))
415
{
416
9853
}
417
418
}  // namespace passes
419
}  // namespace preprocessing
420
29340
}  // namespace cvc5