1 /*********************                                                        */
2 /*! \file bool_to_bv.cpp
3  ** \verbatim
4  ** Top contributors (to current version):
5  **   Makai Mann, Yoni Zohar, Clark Barrett
6  ** This file is part of the CVC4 project.
7  ** Copyright (c) 2009-2019 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.\endverbatim
11  **
12  ** \brief The BoolToBV preprocessing pass
13  **
14  **/
15 
16 #include "preprocessing/passes/bool_to_bv.h"
17 
18 #include <string>
19 
20 #include "base/map_util.h"
21 #include "expr/node.h"
22 #include "options/bv_options.h"
23 #include "smt/smt_statistics_registry.h"
24 #include "theory/rewriter.h"
25 #include "theory/theory.h"
26 
27 namespace CVC4 {
28 namespace preprocessing {
29 namespace passes {
30 using namespace CVC4::theory;
31 
BoolToBV(PreprocessingPassContext * preprocContext)32 BoolToBV::BoolToBV(PreprocessingPassContext* preprocContext)
33     : PreprocessingPass(preprocContext, "bool-to-bv"), d_statistics(){};
34 
applyInternal(AssertionPipeline * assertionsToPreprocess)35 PreprocessingPassResult BoolToBV::applyInternal(
36     AssertionPipeline* assertionsToPreprocess)
37 {
38   NodeManager::currentResourceManager()->spendResource(
39       options::preprocessStep());
40 
41   unsigned size = assertionsToPreprocess->size();
42   for (unsigned i = 0; i < size; ++i)
43   {
44     assertionsToPreprocess->replace(
45         i, Rewriter::rewrite(lowerAssertion((*assertionsToPreprocess)[i])));
46   }
47 
48   return PreprocessingPassResult::NO_CONFLICT;
49 }
50 
fromCache(TNode n) const51 Node BoolToBV::fromCache(TNode n) const
52 {
53   if (d_lowerCache.find(n) != d_lowerCache.end())
54   {
55     return d_lowerCache.find(n)->second;
56   }
57   return n;
58 }
59 
needToRebuild(TNode n) const60 bool BoolToBV::needToRebuild(TNode n) const
61 {
62   // check if any children were rebuilt
63   for (const Node& nn : n)
64   {
65     if (ContainsKey(d_lowerCache, nn))
66     {
67       return true;
68     }
69   }
70   return false;
71 }
72 
lowerAssertion(const TNode & a)73 Node BoolToBV::lowerAssertion(const TNode& a)
74 {
75   bool optionITE = options::boolToBitvector() == BOOL_TO_BV_ITE;
76   NodeManager* nm = NodeManager::currentNM();
77   std::vector<TNode> visit;
78   visit.push_back(a);
79   std::unordered_set<TNode, TNodeHashFunction> visited;
80   // for ite mode, keeps track of whether you're in an ite condition
81   // for all mode, unused
82   std::unordered_set<TNode, TNodeHashFunction> ite_cond;
83 
84   while (!visit.empty())
85   {
86     TNode n = visit.back();
87     visit.pop_back();
88 
89     int numChildren = n.getNumChildren();
90     Kind k = n.getKind();
91     Debug("bool-to-bv") << "BoolToBV::lowerAssertion Post-traversal with " << n
92                         << " and visited = " << ContainsKey(visited, n)
93                         << std::endl;
94 
95     // Mark as visited
96     /* Optimization: if it's a leaf, don't need to wait to do the work */
97     if (!ContainsKey(visited, n) && (numChildren > 0))
98     {
99       visited.insert(n);
100       visit.push_back(n);
101 
102       // insert children in reverse order so that they're processed in order
103       //     important for rewriting which sorts by node id
104       for (int i = numChildren - 1; i >= 0; --i)
105       {
106         visit.push_back(n[i]);
107       }
108 
109       if (optionITE)
110       {
111         // check for ite-conditions
112         if (k == kind::ITE)
113         {
114           ite_cond.insert(n[0]);
115         }
116         else if (ContainsKey(ite_cond, n))
117         {
118           // being part of an ite condition is inherited from the parent
119           ite_cond.insert(n.begin(), n.end());
120         }
121       }
122     }
123     /* Optimization for ite mode */
124     else if (optionITE && !ContainsKey(ite_cond, n) && !needToRebuild(n))
125     {
126       Debug("bool-to-bv")
127           << "BoolToBV::lowerAssertion Skipping because don't need to rebuild: "
128           << n << std::endl;
129       // in ite mode, if you've already visited the node but it's not
130       // in an ite condition and doesn't need to be rebuilt, then
131       // don't need to do anything
132       continue;
133     }
134     else
135     {
136       lowerNode(n);
137     }
138   }
139 
140   if (fromCache(a).getType().isBitVector())
141   {
142     return nm->mkNode(kind::EQUAL, fromCache(a), bv::utils::mkOne(1));
143   }
144   else
145   {
146     Assert(a == fromCache(a));
147     return a;
148   }
149 }
150 
lowerNode(const TNode & n)151 void BoolToBV::lowerNode(const TNode& n)
152 {
153   NodeManager* nm = NodeManager::currentNM();
154   Kind k = n.getKind();
155 
156   bool all_bv = true;
157   // check if it was able to convert all children to bitvectors
158   for (const Node& nn : n)
159   {
160     all_bv = all_bv && fromCache(nn).getType().isBitVector();
161     if (!all_bv)
162     {
163       break;
164     }
165   }
166 
167   if (!all_bv || (n.getNumChildren() == 0))
168   {
169     if ((options::boolToBitvector() == BOOL_TO_BV_ALL)
170         && n.getType().isBoolean())
171     {
172       if (k == kind::CONST_BOOLEAN)
173       {
174         d_lowerCache[n] = (n == bv::utils::mkTrue()) ? bv::utils::mkOne(1)
175                                                      : bv::utils::mkZero(1);
176       }
177       else
178       {
179         d_lowerCache[n] =
180             nm->mkNode(kind::ITE, n, bv::utils::mkOne(1), bv::utils::mkZero(1));
181       }
182 
183       Debug("bool-to-bv") << "BoolToBV::lowerNode " << n << " =>\n"
184                           << fromCache(n) << std::endl;
185       ++(d_statistics.d_numTermsForcedLowered);
186       return;
187     }
188     else
189     {
190       // invariant
191       // either one of the children is not a bit-vector or bool
192       //   i.e. something that can't be 'forced' to a bitvector
193       // or it's in 'ite' mode which will give up on bools that
194       //   can't be converted easily
195 
196       Debug("bool-to-bv") << "BoolToBV::lowerNode skipping: " << n << std::endl;
197       return;
198     }
199   }
200 
201   Kind new_kind = k;
202   switch (k)
203   {
204     case kind::EQUAL: new_kind = kind::BITVECTOR_COMP; break;
205     case kind::AND: new_kind = kind::BITVECTOR_AND; break;
206     case kind::OR: new_kind = kind::BITVECTOR_OR; break;
207     case kind::NOT: new_kind = kind::BITVECTOR_NOT; break;
208     case kind::XOR: new_kind = kind::BITVECTOR_XOR; break;
209     case kind::IMPLIES: new_kind = kind::BITVECTOR_OR; break;
210     case kind::ITE: new_kind = kind::BITVECTOR_ITE; break;
211     case kind::BITVECTOR_ULT: new_kind = kind::BITVECTOR_ULTBV; break;
212     case kind::BITVECTOR_SLT: new_kind = kind::BITVECTOR_SLTBV; break;
213     case kind::BITVECTOR_ULE:
214     case kind::BITVECTOR_UGT:
215     case kind::BITVECTOR_UGE:
216     case kind::BITVECTOR_SLE:
217     case kind::BITVECTOR_SGT:
218     case kind::BITVECTOR_SGE:
219       // Should have been removed by rewriting.
220       Unreachable();
221     default: break;
222   }
223 
224   NodeBuilder<> builder(new_kind);
225   if ((options::boolToBitvector() == BOOL_TO_BV_ALL) && (new_kind != k))
226   {
227     ++(d_statistics.d_numTermsLowered);
228   }
229 
230   if (n.getMetaKind() == kind::metakind::PARAMETERIZED)
231   {
232     builder << n.getOperator();
233   }
234 
235   // special case IMPLIES because needs to be rewritten
236   if (k == kind::IMPLIES)
237   {
238     builder << nm->mkNode(kind::BITVECTOR_NOT, fromCache(n[0]));
239     builder << fromCache(n[1]);
240   }
241   else
242   {
243     for (const Node& nn : n)
244     {
245       builder << fromCache(nn);
246     }
247   }
248 
249   Debug("bool-to-bv") << "BoolToBV::lowerNode " << n << " =>\n"
250                       << builder << std::endl;
251 
252   d_lowerCache[n] = builder.constructNode();
253 }
254 
Statistics()255 BoolToBV::Statistics::Statistics()
256     : d_numIteToBvite("preprocessing::passes::BoolToBV::NumIteToBvite", 0),
257       d_numTermsLowered("preprocessing::passes:BoolToBV::NumTermsLowered", 0),
258       d_numTermsForcedLowered(
259           "preprocessing::passes::BoolToBV::NumTermsForcedLowered", 0)
260 {
261   smtStatisticsRegistry()->registerStat(&d_numIteToBvite);
262   if (options::boolToBitvector() == BOOL_TO_BV_ALL)
263   {
264     // these statistics wouldn't be correct in the ITE mode,
265     // because it might discard rebuilt nodes if it fails to
266     // convert a bool to width-one bit-vector (never forces)
267     smtStatisticsRegistry()->registerStat(&d_numTermsLowered);
268     smtStatisticsRegistry()->registerStat(&d_numTermsForcedLowered);
269   }
270 }
271 
~Statistics()272 BoolToBV::Statistics::~Statistics()
273 {
274   smtStatisticsRegistry()->unregisterStat(&d_numIteToBvite);
275   if (options::boolToBitvector() == BOOL_TO_BV_ALL)
276   {
277     smtStatisticsRegistry()->unregisterStat(&d_numTermsLowered);
278     smtStatisticsRegistry()->unregisterStat(&d_numTermsForcedLowered);
279   }
280 }
281 
282 
283 }  // namespace passes
284 }  // namespace preprocessing
285 }  // namespace CVC4
286