LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/preprocessing/passes - bool_to_bv.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 182 191 95.3 %
Date: 2026-02-21 11:58:00 Functions: 12 12 100.0 %
Branches: 108 155 69.7 %

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

Generated by: LCOV version 1.14