Skip to content

Commit 724f2f7

Browse files
authored
Merge pull request #8717 from tautschnig/popcount-flattening
Implement popcount in flattening back-end
2 parents 558ef2f + c2d47e1 commit 724f2f7

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

@@ -1693,3 +1695,65 @@ bvt bv_utilst::verilog_bv_normal_bits(const bvt &src)
16931695

16941696
return even_bits;
16951697
}
1698+
1699+
/// Symbolic implementation of popcount (count of 1 bits in a bit vector)
1700+
/// Based on the pop0 algorithm from Hacker's Delight
1701+
/// \param bv: The bit vector to count 1s in
1702+
/// \return A bit vector representing the count
1703+
bvt bv_utilst::popcount(const bvt &bv)
1704+
{
1705+
PRECONDITION(!bv.empty());
1706+
1707+
// Determine the result width: log2(bv.size()) + 1
1708+
std::size_t log2 = address_bits(bv.size());
1709+
CHECK_RETURN(log2 >= 1);
1710+
1711+
// Start with the original bit vector
1712+
bvt x = bv;
1713+
1714+
// Apply the parallel bit counting algorithm from Hacker's Delight (pop0).
1715+
// The algorithm works by summing adjacent bit groups of increasing sizes.
1716+
1717+
// Iterate through the stages of the algorithm, doubling the field size each
1718+
// time
1719+
for(std::size_t stage = 0; stage < log2; ++stage)
1720+
{
1721+
std::size_t shift_amount = 1 << stage; // 1, 2, 4, 8, 16, ...
1722+
std::size_t field_size = 2 * shift_amount; // 2, 4, 8, 16, 32, ...
1723+
1724+
// Skip if the bit vector is smaller than the field size
1725+
if(x.size() <= shift_amount)
1726+
break;
1727+
1728+
// Shift the bit vector
1729+
bvt x_shifted = shift(x, shiftt::SHIFT_LRIGHT, shift_amount);
1730+
1731+
// Create a mask with 'shift_amount' ones followed by 'shift_amount' zeros,
1732+
// repeated
1733+
bvt mask;
1734+
mask.reserve(x.size());
1735+
for(std::size_t i = 0; i < x.size(); i++)
1736+
{
1737+
if((i % field_size) < shift_amount)
1738+
mask.push_back(const_literal(true));
1739+
else
1740+
mask.push_back(const_literal(false));
1741+
}
1742+
1743+
// Apply the mask to both the original and shifted bit vectors
1744+
bvt masked_x, masked_shifted;
1745+
masked_x.reserve(x.size());
1746+
masked_shifted.reserve(x.size());
1747+
1748+
for(std::size_t i = 0; i < x.size(); i++)
1749+
{
1750+
masked_x.push_back(prop.land(x[i], mask[i]));
1751+
masked_shifted.push_back(prop.land(x_shifted[i], mask[i]));
1752+
}
1753+
1754+
// Add the masked vectors
1755+
x = add(masked_x, masked_shifted);
1756+
}
1757+
1758+
return x;
1759+
}

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)