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: 181 188 96.3 %
Date: 2026-04-30 10:45:04 Functions: 13 13 100.0 %
Branches: 118 169 69.8 %

           Branch data     Line data    Source code
       1                 :            : /******************************************************************************
       2                 :            :  * This file is part of the cvc5 project.
       3                 :            :  *
       4                 :            :  * Copyright (c) 2009-2026 by the authors listed in the file AUTHORS
       5                 :            :  * in the top-level source directory and their institutional affiliations.
       6                 :            :  * All rights reserved.  See the file COPYING in the top-level source
       7                 :            :  * directory for licensing information.
       8                 :            :  * ****************************************************************************
       9                 :            :  *
      10                 :            :  * The BoolToBV preprocessing pass.
      11                 :            :  *
      12                 :            :  */
      13                 :            : 
      14                 :            : #include "preprocessing/passes/bool_to_bv.h"
      15                 :            : 
      16                 :            : #include <string>
      17                 :            : 
      18                 :            : #include "base/map_util.h"
      19                 :            : #include "expr/node.h"
      20                 :            : #include "options/bv_options.h"
      21                 :            : #include "preprocessing/assertion_pipeline.h"
      22                 :            : #include "preprocessing/preprocessing_pass_context.h"
      23                 :            : #include "theory/bv/theory_bv_utils.h"
      24                 :            : #include "theory/rewriter.h"
      25                 :            : #include "theory/theory.h"
      26                 :            : 
      27                 :            : namespace cvc5::internal {
      28                 :            : namespace preprocessing {
      29                 :            : namespace passes {
      30                 :            : using namespace cvc5::internal::theory;
      31                 :            : 
      32                 :      51756 : BoolToBV::BoolToBV(PreprocessingPassContext* preprocContext)
      33                 :            :     : PreprocessingPass(preprocContext, "bool-to-bv"),
      34                 :      51756 :       d_statistics(statisticsRegistry())
      35                 :            : {
      36                 :      51756 :   d_boolToBVMode = options().bv.boolToBitvector;
      37                 :      51756 : };
      38                 :            : 
      39                 :         12 : PreprocessingPassResult BoolToBV::applyInternal(
      40                 :            :     AssertionPipeline* assertionsToPreprocess)
      41                 :            : {
      42                 :         12 :   d_preprocContext->spendResource(Resource::PreprocessStep);
      43                 :            : 
      44                 :         12 :   size_t size = assertionsToPreprocess->size();
      45 [ +  + ][ +  - ]:         12 :   Assert(d_boolToBVMode == options::BoolToBVMode::ALL
         [ -  + ][ -  + ]
                 [ -  - ]
      46                 :            :          || d_boolToBVMode == options::BoolToBVMode::ITE);
      47         [ +  + ]:        100 :   for (size_t i = 0; i < size; ++i)
      48                 :            :   {
      49                 :         88 :     Node newAssertion;
      50         [ +  + ]:         88 :     if (d_boolToBVMode == options::BoolToBVMode::ALL)
      51                 :            :     {
      52                 :         68 :       newAssertion = lowerAssertion((*assertionsToPreprocess)[i], true);
      53                 :            :     }
      54                 :            :     else
      55                 :            :     {
      56                 :         20 :       newAssertion = lowerIte((*assertionsToPreprocess)[i]);
      57                 :            :     }
      58                 :         88 :     assertionsToPreprocess->replace(
      59                 :            :         i, newAssertion, nullptr, TrustId::PREPROCESS_BOOL_TO_BV);
      60                 :         88 :     assertionsToPreprocess->ensureRewritten(i);
      61         [ -  + ]:         88 :     if (assertionsToPreprocess->isInConflict())
      62                 :            :     {
      63                 :          0 :       return PreprocessingPassResult::CONFLICT;
      64                 :            :     }
      65         [ +  - ]:         88 :   }
      66                 :         12 :   return PreprocessingPassResult::NO_CONFLICT;
      67                 :            : }
      68                 :            : 
      69                 :        156 : void BoolToBV::updateCache(TNode n, TNode rebuilt)
      70                 :            : {
      71                 :            :   // check more likely case first
      72                 :        156 :   if ((n.getKind() != Kind::ITE) || !n[1].getType().isBitVector())
      73                 :            :   {
      74                 :        152 :     d_lowerCache[n] = rebuilt;
      75                 :            :   }
      76                 :            :   else
      77                 :            :   {
      78                 :          4 :     d_iteBVLowerCache[n] = rebuilt;
      79                 :            :   }
      80                 :        156 : }
      81                 :            : 
      82                 :       1288 : Node BoolToBV::fromCache(TNode n) const
      83                 :            : {
      84                 :            :   // check more likely case first
      85         [ +  + ]:       1288 :   if (n.getKind() != Kind::ITE)
      86                 :            :   {
      87         [ +  + ]:       1272 :     if (d_lowerCache.find(n) != d_lowerCache.end())
      88                 :            :     {
      89                 :       1360 :       return d_lowerCache.at(n);
      90                 :            :     }
      91                 :            :   }
      92                 :            :   else
      93                 :            :   {
      94         [ +  + ]:         16 :     if (d_iteBVLowerCache.find(n) != d_iteBVLowerCache.end())
      95                 :            :     {
      96                 :         16 :       return d_iteBVLowerCache.at(n);
      97                 :            :     }
      98                 :            :   }
      99                 :        600 :   return n;
     100                 :            : }
     101                 :            : 
     102                 :        220 : inline bool BoolToBV::inCache(const Node& n) const
     103                 :            : {
     104 [ +  - ][ +  + ]:        220 :   return (ContainsKey(d_lowerCache, n) || ContainsKey(d_iteBVLowerCache, n));
     105                 :            : }
     106                 :            : 
     107                 :        532 : bool BoolToBV::needToRebuild(TNode n) const
     108                 :            : {
     109                 :            :   // check if any children were rebuilt
     110         [ +  + ]:        748 :   for (const Node& nn : n)
     111                 :            :   {
     112         [ +  + ]:        220 :     if (inCache(nn))
     113                 :            :     {
     114                 :          4 :       return true;
     115                 :            :     }
     116         [ +  + ]:        220 :   }
     117                 :        528 :   return false;
     118                 :            : }
     119                 :            : 
     120                 :         68 : Node BoolToBV::lowerAssertion(const TNode& assertion, bool allowIteIntroduction)
     121                 :            : {
     122                 :            :   // first try to lower all the children
     123         [ +  + ]:        166 :   for (const Node& c : assertion)
     124                 :            :   {
     125                 :         98 :     lowerNode(c, allowIteIntroduction);
     126                 :         98 :   }
     127                 :            : 
     128                 :            :   // now try lowering the assertion, but don't force it with an ITE (even in
     129                 :            :   // mode all)
     130                 :         68 :   lowerNode(assertion, false);
     131                 :         68 :   Node newAssertion = fromCache(assertion);
     132                 :         68 :   TypeNode newAssertionType = newAssertion.getType();
     133         [ +  + ]:         68 :   if (newAssertionType.isBitVector())
     134                 :            :   {
     135 [ -  + ][ -  + ]:         64 :     Assert(newAssertionType.getBitVectorSize() == 1);
                 [ -  - ]
     136                 :         64 :     NodeManager* nm = nodeManager();
     137                 :            :     newAssertion =
     138                 :         64 :         nm->mkNode(Kind::EQUAL, newAssertion, bv::utils::mkOne(nm, 1));
     139                 :         64 :     newAssertionType = newAssertion.getType();
     140                 :            :   }
     141 [ -  + ][ -  + ]:         68 :   Assert(newAssertionType.isBoolean());
                 [ -  - ]
     142                 :        136 :   return newAssertion;
     143                 :         68 : }
     144                 :            : 
     145                 :        170 : Node BoolToBV::lowerNode(const TNode& node, bool allowIteIntroduction)
     146                 :            : {
     147                 :        170 :   std::vector<TNode> to_visit;
     148                 :        170 :   to_visit.push_back(node);
     149                 :        170 :   std::unordered_set<TNode> visited;
     150                 :            : 
     151         [ +  + ]:       1342 :   while (!to_visit.empty())
     152                 :            :   {
     153                 :       1172 :     TNode n = to_visit.back();
     154                 :       1172 :     to_visit.pop_back();
     155                 :            : 
     156         [ +  - ]:       2344 :     Trace("bool-to-bv") << "BoolToBV::lowerNode: Post-order traversal with "
     157                 :       1172 :                         << n << " and visited = " << ContainsKey(visited, n)
     158                 :       1172 :                         << std::endl;
     159                 :            : 
     160                 :            :     // Mark as visited
     161         [ +  + ]:       1172 :     if (ContainsKey(visited, n))
     162                 :            :     {
     163                 :        608 :       visit(n, allowIteIntroduction);
     164                 :            :     }
     165                 :            :     else
     166                 :            :     {
     167                 :        564 :       visited.insert(n);
     168                 :        564 :       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                 :        564 :       size_t numChildren = n.getNumChildren();
     174         [ +  + ]:       1002 :       for (size_t i = numChildren - 1; i < numChildren; --i)
     175                 :            :       {
     176                 :        438 :         to_visit.push_back(n[i]);
     177                 :            :       }
     178                 :            :     }
     179                 :       1172 :   }
     180                 :            : 
     181                 :        340 :   return fromCache(node);
     182                 :        170 : }
     183                 :            : 
     184                 :        608 : void BoolToBV::visit(const TNode& n, bool allowIteIntroduction)
     185                 :            : {
     186                 :        608 :   Kind k = n.getKind();
     187                 :            : 
     188                 :        608 :   NodeManager* nm = nodeManager();
     189                 :            :   // easy case -- just replace boolean constant
     190         [ +  + ]:        608 :   if (k == Kind::CONST_BOOLEAN)
     191                 :            :   {
     192                 :         10 :     updateCache(n,
     193         [ +  - ]:         20 :                 (n == bv::utils::mkTrue(nm)) ? bv::utils::mkOne(nm, 1)
     194                 :            :                                              : bv::utils::mkZero(nm, 1));
     195                 :        120 :     return;
     196                 :            :   }
     197                 :            : 
     198                 :        598 :   Kind new_kind = k;
     199 [ +  + ][ +  + ]:        598 :   switch (k)
         [ +  + ][ +  + ]
            [ +  - ][ + ]
     200                 :            :   {
     201                 :         38 :     case Kind::EQUAL: new_kind = Kind::BITVECTOR_COMP; break;
     202                 :          8 :     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                 :          0 :     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                 :          0 :       Unreachable();
     218                 :        464 :     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                 :        598 :   bool safe_to_lower =
     227                 :        598 :       (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                 :        598 :   bool safe_to_rebuild = true;
     232                 :            : 
     233         [ +  + ]:        988 :   for (const Node& nn : n)
     234                 :            :   {
     235                 :        430 :     safe_to_lower = safe_to_lower && fromCache(nn).getType().isBitVector();
     236                 :        430 :     safe_to_rebuild =
     237 [ +  + ][ +  + ]:       1510 :         safe_to_rebuild && (CVC5_EQUAL(fromCache(nn).getType(), nn.getType()));
     238                 :            : 
     239                 :            :     // if it's already not safe to do either, stop checking
     240 [ +  + ][ +  + ]:        430 :     if (!safe_to_lower && !safe_to_rebuild)
     241                 :            :     {
     242                 :         40 :       break;
     243                 :            :     }
     244         [ +  + ]:        430 :   }
     245         [ +  - ]:       1196 :   Trace("bool-to-bv") << "safe_to_lower = " << safe_to_lower
     246                 :        598 :                       << ", safe_to_rebuild = " << safe_to_rebuild << std::endl;
     247                 :            : 
     248 [ +  + ][ +  + ]:        598 :   if (new_kind != k && safe_to_lower)
     249                 :            :   {
     250                 :            :     // lower to BV
     251                 :        102 :     rebuildNode(n, new_kind);
     252                 :        102 :     return;
     253                 :            :   }
     254         [ +  + ]:         32 :   else if (new_kind != k && allowIteIntroduction
     255                 :        528 :            && 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(
     266                 :            :         n,
     267 [ +  + ][ -  - ]:         56 :         nm->mkNode(
     268                 :            :             Kind::ITE,
     269                 :         32 :             {fromCache(n), bv::utils::mkOne(nm, 1), bv::utils::mkZero(nm, 1)}));
     270         [ +  - ]:         16 :     Trace("bool-to-bv") << "BoolToBV::visit forcing " << n << " =>\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 [ +  + ][ -  + ]:        488 :   else if (safe_to_rebuild && needToRebuild(n))
         [ +  + ][ -  + ]
                 [ -  - ]
     280                 :            :   {
     281                 :            :     // rebuild to incorporate changes to children
     282                 :          0 :     rebuildNode(n, k);
     283                 :            :   }
     284                 :        488 :   else if (allowIteIntroduction && fromCache(n).getType().isBoolean())
     285                 :            :   {
     286                 :            :     // force booleans (which haven't already been converted) to bit-vector
     287                 :            :     // needed to maintain the invariant that all boolean children
     288                 :            :     // have been converted (even constants and variables) when forcing
     289                 :            :     // with ITE introductions
     290                 :         32 :     updateCache(
     291                 :            :         n,
     292 [ +  + ][ -  - ]:        224 :         nm->mkNode(Kind::ITE,
     293                 :         96 :                    {n, bv::utils::mkOne(nm, 1), bv::utils::mkZero(nm, 1)}));
     294         [ +  - ]:         64 :     Trace("bool-to-bv") << "BoolToBV::visit forcing " << n << " =>\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         [ +  - ]:        456 :     Trace("bool-to-bv") << "BoolToBV::visit skipping: " << n << std::endl;
     306                 :            :   }
     307                 :            : }
     308                 :            : 
     309                 :         20 : Node BoolToBV::lowerIte(const TNode& node)
     310                 :            : {
     311                 :         20 :   std::vector<TNode> visit;
     312                 :         20 :   visit.push_back(node);
     313                 :         20 :   std::unordered_set<TNode> visited;
     314                 :            : 
     315         [ +  + ]:        168 :   while (!visit.empty())
     316                 :            :   {
     317                 :        148 :     TNode n = visit.back();
     318                 :        148 :     visit.pop_back();
     319                 :            : 
     320         [ +  - ]:        296 :     Trace("bool-to-bv") << "BoolToBV::lowerIte: Post-order traversal with " << n
     321                 :        148 :                         << " and visited = " << ContainsKey(visited, n)
     322                 :        148 :                         << std::endl;
     323                 :            : 
     324                 :            :     // Look for ITEs and mark visited
     325         [ +  + ]:        148 :     if (!ContainsKey(visited, n))
     326                 :            :     {
     327                 :         72 :       if ((n.getKind() == Kind::ITE) && n[1].getType().isBitVector())
     328                 :            :       {
     329 [ +  - ][ -  + ]:          8 :         Trace("bool-to-bv") << "BoolToBV::lowerIte: adding " << n[0]
                 [ -  - ]
     330                 :          4 :                             << " to set of ite conditions" << std::endl;
     331                 :            :         // don't force in this case -- forcing only introduces more ITEs
     332                 :          4 :         Node loweredNode = lowerNode(n, false);
     333                 :            :         // some of the lowered nodes might appear elsewhere but not in an ITE
     334                 :            :         // reset the cache to prevent lowering them
     335                 :            :         // the bit-vector ITEs are still tracked in d_iteBVLowerCache though
     336                 :          4 :         d_lowerCache.clear();
     337                 :          4 :       }
     338                 :            :       else
     339                 :            :       {
     340                 :         68 :         visit.push_back(n);
     341                 :         68 :         visited.insert(n);
     342                 :            :         // insert in reverse order so that they're processed in order
     343         [ +  + ]:        128 :         for (int i = n.getNumChildren() - 1; i >= 0; --i)
     344                 :            :         {
     345                 :         60 :           visit.push_back(n[i]);
     346                 :            :         }
     347                 :            :       }
     348                 :            :     }
     349         [ +  + ]:         76 :     else if (needToRebuild(n))
     350                 :            :     {
     351                 :            :       // Note: it's always safe to rebuild here, because we've only lowered
     352                 :            :       //       ITEs of type bit-vector to BITVECTOR_ITE
     353                 :          4 :       rebuildNode(n, n.getKind());
     354                 :            :     }
     355                 :            :     else
     356                 :            :     {
     357         [ +  - ]:        144 :       Trace("bool-to-bv")
     358                 :          0 :           << "BoolToBV::lowerIte Skipping because don't need to rebuild: " << n
     359                 :         72 :           << std::endl;
     360                 :            :     }
     361                 :        148 :   }
     362                 :         40 :   return fromCache(node);
     363                 :         20 : }
     364                 :            : 
     365                 :        106 : void BoolToBV::rebuildNode(const TNode& n, Kind new_kind)
     366                 :            : {
     367                 :        106 :   Kind k = n.getKind();
     368                 :        106 :   NodeManager* nm = nodeManager();
     369                 :        106 :   NodeBuilder builder(nm, new_kind);
     370                 :            : 
     371         [ +  - ]:        212 :   Trace("bool-to-bv") << "BoolToBV::rebuildNode with " << n
     372 [ -  + ][ -  - ]:        106 :                       << " and new_kind = " << kindToString(new_kind)
     373                 :        106 :                       << std::endl;
     374                 :            : 
     375 [ +  + ][ +  - ]:        106 :   if ((d_boolToBVMode == options::BoolToBVMode::ALL) && (new_kind != k))
     376                 :            :   {
     377                 :            :     // this statistic only makes sense for ALL mode
     378                 :         94 :     ++(d_statistics.d_numTermsLowered);
     379                 :            :   }
     380                 :            : 
     381         [ -  + ]:        106 :   if (n.getMetaKind() == kind::metakind::PARAMETERIZED)
     382                 :            :   {
     383                 :          0 :     builder << n.getOperator();
     384                 :            :   }
     385                 :            :   // special case IMPLIES because needs to be rewritten
     386 [ +  + ][ +  - ]:        106 :   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         [ +  + ]:        244 :     for (const Node& nn : n)
     394                 :            :     {
     395                 :        158 :       builder << fromCache(nn);
     396                 :        158 :     }
     397                 :            :   }
     398                 :            : 
     399         [ +  - ]:        212 :   Trace("bool-to-bv") << "BoolToBV::rebuildNode " << n << " =>\n"
     400                 :        106 :                       << builder << std::endl;
     401                 :            : 
     402                 :        106 :   updateCache(n, builder.constructNode());
     403                 :        106 : }
     404                 :            : 
     405                 :      51756 : BoolToBV::Statistics::Statistics(StatisticsRegistry& reg)
     406                 :            :     : d_numIteToBvite(
     407                 :      51756 :           reg.registerInt("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                 :            :       d_numTermsLowered(
     412                 :      51756 :           reg.registerInt("preprocessing::passes:BoolToBV::NumTermsLowered")),
     413                 :      51756 :       d_numIntroducedItes(reg.registerInt(
     414                 :            :           "preprocessing::passes::BoolToBV::NumTermsForcedLowered"))
     415                 :            : {
     416                 :      51756 : }
     417                 :            : 
     418                 :            : }  // namespace passes
     419                 :            : }  // namespace preprocessing
     420                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14