Skip to content

Commit 7469ae8

Browse files
committed
Implement popcount in flattening back-end
We can avoid lowering, and eventually re-use this as part of other algorithms.
1 parent 46f9d54 commit 7469ae8

File tree

6 files changed

+94
-1
lines changed

6 files changed

+94
-1
lines changed

src/solvers/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ SRC = $(BOOLEFORCE_SRC) \
118118
flattening/boolbv_onehot.cpp \
119119
flattening/boolbv_overflow.cpp \
120120
flattening/boolbv_power.cpp \
121+
flattening/boolbv_popcount.cpp \
121122
flattening/boolbv_quantifier.cpp \
122123
flattening/boolbv_reduction.cpp \
123124
flattening/boolbv_replication.cpp \

src/solvers/flattening/boolbv.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ bvt boolbvt::convert_bitvector(const exprt &expr)
235235
else if(expr.id()==ID_power)
236236
return convert_power(to_binary_expr(expr));
237237
else if(expr.id() == ID_popcount)
238-
return convert_bv(simplify_expr(to_popcount_expr(expr).lower(), ns));
238+
return convert_popcount(to_popcount_expr(expr));
239239
else if(expr.id() == ID_count_leading_zeros)
240240
{
241241
return convert_bv(

src/solvers/flattening/boolbv.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class floatbv_round_to_integral_exprt;
3737
class floatbv_typecast_exprt;
3838
class ieee_float_op_exprt;
3939
class overflow_result_exprt;
40+
class popcount_exprt;
4041
class replication_exprt;
4142
class unary_overflow_exprt;
4243
class union_typet;
@@ -203,6 +204,7 @@ class boolbvt:public arrayst
203204
virtual bvt convert_bitreverse(const bitreverse_exprt &expr);
204205
virtual bvt convert_saturating_add_sub(const binary_exprt &expr);
205206
virtual bvt convert_overflow_result(const overflow_result_exprt &expr);
207+
virtual bvt convert_popcount(const popcount_exprt &expr);
206208

207209
bvt convert_update_bits(bvt src, const exprt &index, const bvt &new_value);
208210

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*******************************************************************\
2+
3+
Module:
4+
5+
Author: Michael Tautschnig
6+
7+
\*******************************************************************/
8+
9+
#include <util/bitvector_expr.h>
10+
11+
#include "boolbv.h"
12+
13+
bvt boolbvt::convert_popcount(const popcount_exprt &expr)
14+
{
15+
const std::size_t width = boolbv_width(expr.type());
16+
17+
bvt op = convert_bv(expr.op());
18+
19+
return bv_utils.zero_extension(bv_utils.popcount(op), width);
20+
}

src/solvers/flattening/bv_utils.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Author: Daniel Kroening, [email protected]
88

99
#include "bv_utils.h"
1010

11+
#include <util/arith_tools.h>
12+
1113
#include <list>
1214
#include <utility>
1315

@@ -1644,3 +1646,65 @@ bvt bv_utilst::verilog_bv_normal_bits(const bvt &src)
16441646

16451647
return even_bits;
16461648
}
1649+
1650+
/// Symbolic implementation of popcount (count of 1 bits in a bit vector)
1651+
/// Based on the pop0 algorithm from Hacker's Delight
1652+
/// \param bv: The bit vector to count 1s in
1653+
/// \return A bit vector representing the count
1654+
bvt bv_utilst::popcount(const bvt &bv)
1655+
{
1656+
PRECONDITION(!bv.empty());
1657+
1658+
// Determine the result width: log2(bv.size()) + 1
1659+
std::size_t log2 = address_bits(bv.size());
1660+
CHECK_RETURN(log2 >= 1);
1661+
1662+
// Start with the original bit vector
1663+
bvt x = bv;
1664+
1665+
// Apply the parallel bit counting algorithm from Hacker's Delight (pop0).
1666+
// The algorithm works by summing adjacent bit groups of increasing sizes.
1667+
1668+
// Iterate through the stages of the algorithm, doubling the field size each
1669+
// time
1670+
for(std::size_t stage = 0; stage < log2; ++stage)
1671+
{
1672+
std::size_t shift_amount = 1 << stage; // 1, 2, 4, 8, 16, ...
1673+
std::size_t field_size = 2 * shift_amount; // 2, 4, 8, 16, 32, ...
1674+
1675+
// Skip if the bit vector is smaller than the field size
1676+
if(x.size() <= shift_amount)
1677+
break;
1678+
1679+
// Shift the bit vector
1680+
bvt x_shifted = shift(x, shiftt::SHIFT_LRIGHT, shift_amount);
1681+
1682+
// Create a mask with 'shift_amount' ones followed by 'shift_amount' zeros,
1683+
// repeated
1684+
bvt mask;
1685+
mask.reserve(x.size());
1686+
for(std::size_t i = 0; i < x.size(); i++)
1687+
{
1688+
if((i % field_size) < shift_amount)
1689+
mask.push_back(const_literal(true));
1690+
else
1691+
mask.push_back(const_literal(false));
1692+
}
1693+
1694+
// Apply the mask to both the original and shifted bit vectors
1695+
bvt masked_x, masked_shifted;
1696+
masked_x.reserve(x.size());
1697+
masked_shifted.reserve(x.size());
1698+
1699+
for(std::size_t i = 0; i < x.size(); i++)
1700+
{
1701+
masked_x.push_back(prop.land(x[i], mask[i]));
1702+
masked_shifted.push_back(prop.land(x_shifted[i], mask[i]));
1703+
}
1704+
1705+
// Add the masked vectors
1706+
x = add(masked_x, masked_shifted);
1707+
}
1708+
1709+
return x;
1710+
}

src/solvers/flattening/bv_utils.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,12 @@ class bv_utilst
218218
literalt verilog_bv_has_x_or_z(const bvt &);
219219
static bvt verilog_bv_normal_bits(const bvt &);
220220

221+
/// Symbolic implementation of popcount (count of 1 bits in a bit vector)
222+
/// Based on the pop0 algorithm from Hacker's Delight
223+
/// \param bv: The bit vector to count 1s in
224+
/// \return A bit vector representing the count
225+
bvt popcount(const bvt &bv);
226+
221227
protected:
222228
propt &prop;
223229

0 commit comments

Comments
 (0)