Skip to content

Commit ed26c5a

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 5a5471a commit ed26c5a

File tree

5 files changed

+102
-1
lines changed

5 files changed

+102
-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(
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 zero_extension(bv_utils.popcount(op));
20+
}

src/solvers/flattening/bv_utils.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3205,3 +3205,77 @@ bvt bv_utilst::verilog_bv_normal_bits(const bvt &src)
32053205

32063206
return even_bits;
32073207
}
3208+
3209+
/// Symbolic implementation of popcount (count of 1 bits in a bit vector)
3210+
/// Based on the pop0 algorithm from Hacker's Delight
3211+
/// \param bv: The bit vector to count 1s in
3212+
/// \return A bit vector representing the count
3213+
bvt bv_utilst::popcount(const bvt &bv)
3214+
{
3215+
if(bv.empty())
3216+
return bvt();
3217+
3218+
// Determine the result width: log2(bv.size()) + 1
3219+
std::size_t result_width = address_bits(bv.size());
3220+
if(result_width == 0)
3221+
result_width = 1; // At least one bit for the result
3222+
3223+
// Start with the original bit vector
3224+
bvt x = bv;
3225+
3226+
// Apply the parallel bit counting algorithm from Hacker's Delight
3227+
// The algorithm works by summing adjacent bit groups of increasing sizes
3228+
3229+
// Iterate through the stages of the algorithm, doubling the field size each time
3230+
for(std::size_t stage = 0; stage < address_bits(x.size()); stage++)
3231+
{
3232+
std::size_t shift_amount = 1 << stage; // 1, 2, 4, 8, 16, ...
3233+
std::size_t field_size = 2 * shift_amount; // 2, 4, 8, 16, 32, ...
3234+
3235+
// Skip if the bit vector is smaller than the field size
3236+
if(x.size() < field_size)
3237+
break;
3238+
3239+
// Shift the bit vector
3240+
bvt x_shifted = shift(x, shiftt::SHIFT_LRIGHT, shift_amount);
3241+
3242+
// Create a mask with 'shift_amount' ones followed by 'shift_amount' zeros, repeated
3243+
bvt mask;
3244+
mask.reserve(x.size());
3245+
for(std::size_t i = 0; i < x.size(); i++)
3246+
{
3247+
if((i % field_size) < shift_amount)
3248+
mask.push_back(const_literal(true));
3249+
else
3250+
mask.push_back(const_literal(false));
3251+
}
3252+
3253+
// Apply the mask to both the original and shifted bit vectors
3254+
bvt masked_x = zeros(x.size());
3255+
bvt masked_shifted = zeros(x.size());
3256+
3257+
for(std::size_t i = 0; i < x.size(); i++)
3258+
{
3259+
masked_x[i] = prop.land(x[i], mask[i]);
3260+
masked_shifted[i] = prop.land(x_shifted[i], mask[i]);
3261+
}
3262+
3263+
// Add the masked vectors
3264+
x = add(masked_x, masked_shifted);
3265+
}
3266+
3267+
// Extract the result with the appropriate width
3268+
// The result is in the least significant bits of x
3269+
bvt result;
3270+
result.reserve(result_width);
3271+
3272+
for(std::size_t i = 0; i < result_width; i++)
3273+
{
3274+
if(i < x.size())
3275+
result.push_back(x[i]);
3276+
else
3277+
result.push_back(const_literal(false));
3278+
}
3279+
3280+
return result;
3281+
}

src/solvers/flattening/bv_utils.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,12 @@ class bv_utilst
222222
literalt verilog_bv_has_x_or_z(const bvt &);
223223
static bvt verilog_bv_normal_bits(const bvt &);
224224

225+
/// Symbolic implementation of popcount (count of 1 bits in a bit vector)
226+
/// Based on the pop0 algorithm from Hacker's Delight
227+
/// \param bv: The bit vector to count 1s in
228+
/// \return A bit vector representing the count
229+
bvt popcount(const bvt &bv);
230+
225231
protected:
226232
propt &prop;
227233

0 commit comments

Comments
 (0)