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