Skip to content

Commit bb47a84

Browse files
author
Daniel Kroening
authored
Merge pull request #1883 from tautschnig/implement-popcount
Implement popcount
2 parents 90beed4 + 716103e commit bb47a84

File tree

12 files changed

+258
-12
lines changed

12 files changed

+258
-12
lines changed

regression/cbmc/gcc_popcount1/main.c

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#include <assert.h>
2+
3+
#ifndef __GNUC__
4+
int __builtin_popcount(unsigned int);
5+
int __builtin_popcountl(unsigned long);
6+
int __builtin_popcountll(unsigned long long);
7+
#endif
8+
9+
unsigned int __VERIFIER_nondet_unsigned();
10+
unsigned long __VERIFIER_nondet_unsigned_long();
11+
unsigned long long __VERIFIER_nondet_unsigned_long_long();
12+
13+
// Hacker's Delight
14+
// http://www.hackersdelight.org/permissions.htm
15+
int pop4(unsigned long long x)
16+
{
17+
int n = 0;
18+
19+
// variant with additional bounding to make sure symbolic execution always
20+
// terminates without having to specify an unwinding bound
21+
for(int i = 0; x != 0 && i < sizeof(x) * 8; ++i)
22+
{
23+
++n;
24+
x = x & (x - 1);
25+
}
26+
27+
return n;
28+
}
29+
30+
int main()
31+
{
32+
assert(pop4(42) == 3);
33+
assert(__builtin_popcount(42) == 3);
34+
assert(__builtin_popcountl(42) == 3);
35+
assert(__builtin_popcountll(42) == 3);
36+
37+
unsigned int u = __VERIFIER_nondet_unsigned();
38+
assert(pop4(u) == __builtin_popcount(u));
39+
40+
return 0;
41+
}
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring

regression/cbmc/gcc_popcount2/main.c

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#include <assert.h>
2+
3+
#ifndef __GNUC__
4+
int __builtin_popcount(unsigned int);
5+
int __builtin_popcountl(unsigned long);
6+
int __builtin_popcountll(unsigned long long);
7+
#endif
8+
9+
unsigned int __VERIFIER_nondet_unsigned();
10+
unsigned long __VERIFIER_nondet_unsigned_long();
11+
unsigned long long __VERIFIER_nondet_unsigned_long_long();
12+
13+
// Hacker's Delight
14+
// http://www.hackersdelight.org/permissions.htm
15+
int pop4(unsigned long long x)
16+
{
17+
int n = 0;
18+
19+
// variant with additional bounding to make sure symbolic execution always
20+
// terminates without having to specify an unwinding bound
21+
for(int i = 0; x != 0 && i < sizeof(x) * 8; ++i)
22+
{
23+
++n;
24+
x = x & (x - 1);
25+
}
26+
27+
return n;
28+
}
29+
30+
int main()
31+
{
32+
unsigned long ul = __VERIFIER_nondet_unsigned_long();
33+
assert(pop4(ul) == __builtin_popcountl(ul));
34+
35+
unsigned long long ull = __VERIFIER_nondet_unsigned_long_long();
36+
assert(pop4(ull) == __builtin_popcountll(ull));
37+
38+
// expected to fail as bits may have been cut off
39+
assert(
40+
sizeof(ull) != sizeof(unsigned int) &&
41+
pop4(ull) == __builtin_popcount(ull));
42+
43+
return 0;
44+
}
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
THOROUGH
2+
main.c
3+
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
\[main\.assertion\.1\] assertion sizeof\(ull\) != sizeof\(unsigned int\) && pop4\(ull\) == __builtin_popcount\(ull\): FAILURE$
8+
^\*\* 1 of 3 failed
9+
--
10+
^warning: ignoring

src/ansi-c/c_typecheck_expr.cpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -2355,8 +2355,7 @@ exprt c_typecheck_baset::do_special_functions(
23552355
throw 0;
23562356
}
23572357

2358-
exprt popcount_expr(ID_popcount, expr.type());
2359-
popcount_expr.operands()=expr.arguments();
2358+
popcount_exprt popcount_expr(expr.arguments().front(), expr.type());
23602359
popcount_expr.add_source_location()=source_location;
23612360

23622361
return popcount_expr;

src/solvers/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ SRC = $(BOOLEFORCE_SRC) \
157157
floatbv/float_bv.cpp \
158158
floatbv/float_utils.cpp \
159159
floatbv/float_approximation.cpp \
160+
lowering/popcount.cpp \
160161
miniBDD/miniBDD.cpp \
161162
prop/aig.cpp \
162163
prop/aig_prop.cpp \

src/solvers/flattening/boolbv.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Author: Daniel Kroening, [email protected]
2727
#include "boolbv_type.h"
2828

2929
#include "../floatbv/float_utils.h"
30+
#include "../lowering/expr_lowering.h"
3031

3132
bool boolbvt::literal(
3233
const exprt &expr,
@@ -305,6 +306,8 @@ bvt boolbvt::convert_bitvector(const exprt &expr)
305306
float_utils.debug2(bv0, bv1);
306307
return bv;
307308
}
309+
else if(expr.id() == ID_popcount)
310+
return convert_bv(lower_popcount(to_popcount_expr(expr), ns));
308311

309312
return conversion_failed(expr);
310313
}

src/solvers/lowering/expr_lowering.h

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*******************************************************************\
2+
3+
Module: Lower expressions to arithmetic and logic expressions
4+
5+
Author: Michael Tautschnig
6+
7+
\*******************************************************************/
8+
9+
#ifndef CPROVER_SOLVERS_LOWERING_EXPR_LOWERING_H
10+
#define CPROVER_SOLVERS_LOWERING_EXPR_LOWERING_H
11+
12+
#include <util/expr.h>
13+
14+
class namespacet;
15+
class popcount_exprt;
16+
17+
/// Lower a popcount_exprt to arithmetic and logic expressions
18+
/// \param expr Input expression to be translated
19+
/// \param ns Namespace for type lookups
20+
/// \return Semantically equivalent expression
21+
exprt lower_popcount(const popcount_exprt &expr, const namespacet &ns);
22+
23+
#endif /* CPROVER_SOLVERS_LOWERING_EXPR_LOWERING_H */

src/solvers/lowering/popcount.cpp

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*******************************************************************\
2+
3+
Module: Lowering of popcount
4+
5+
Author: Michael Tautschnig
6+
7+
\*******************************************************************/
8+
9+
#include "expr_lowering.h"
10+
11+
#include <util/arith_tools.h>
12+
#include <util/invariant.h>
13+
#include <util/pointer_offset_size.h>
14+
#include <util/std_expr.h>
15+
16+
exprt lower_popcount(const popcount_exprt &expr, const namespacet &ns)
17+
{
18+
// Hacker's Delight, variant pop0:
19+
// x = (x & 0x55555555) + ((x >> 1) & 0x55555555);
20+
// x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
21+
// x = (x & 0x0F0F0F0F) + ((x >> 4) & 0x0F0F0F0F);
22+
// x = (x & 0x00FF00FF) + ((x >> 8) & 0x00FF00FF);
23+
// etc.
24+
// return x;
25+
// http://www.hackersdelight.org/permissions.htm
26+
27+
// make sure the operand width is a power of two
28+
exprt x = expr.op();
29+
const mp_integer x_width = pointer_offset_bits(x.type(), ns);
30+
CHECK_RETURN(x_width > 0);
31+
const std::size_t bits = address_bits(x_width);
32+
const std::size_t new_width = integer2size_t(power(2, bits));
33+
const bool need_typecast =
34+
new_width > x_width || x.type().id() != ID_unsignedbv;
35+
if(need_typecast)
36+
x.make_typecast(unsignedbv_typet(new_width));
37+
38+
// repeatedly compute x = (x & bitmask) + ((x >> shift) & bitmask)
39+
for(std::size_t shift = 1; shift < new_width; shift <<= 1)
40+
{
41+
// x >> shift
42+
lshr_exprt shifted_x(
43+
x, from_integer(shift, unsignedbv_typet(address_bits(shift) + 1)));
44+
// bitmask is a string of alternating shift-many bits starting from lsb set
45+
// to 1
46+
std::string bitstring;
47+
bitstring.reserve(new_width);
48+
for(std::size_t i = 0; i < new_width / (2 * shift); ++i)
49+
bitstring += std::string(shift, '0') + std::string(shift, '1');
50+
constant_exprt bitmask(bitstring, x.type());
51+
// build the expression
52+
x = plus_exprt(bitand_exprt(x, bitmask), bitand_exprt(shifted_x, bitmask));
53+
}
54+
55+
// the result is restricted to the result type
56+
x.make_typecast(expr.type());
57+
58+
return x;
59+
}

src/util/simplify_expr.cpp

+9-9
Original file line numberDiff line numberDiff line change
@@ -136,28 +136,28 @@ bool simplify_exprt::simplify_sign(exprt &expr)
136136
return true;
137137
}
138138

139-
bool simplify_exprt::simplify_popcount(exprt &expr)
139+
bool simplify_exprt::simplify_popcount(popcount_exprt &expr)
140140
{
141-
if(expr.operands().size()!=1)
142-
return true;
141+
const exprt &op = expr.op();
143142

144-
if(expr.op0().is_constant())
143+
if(op.is_constant())
145144
{
146-
const typet &type=ns.follow(expr.op0().type());
145+
const typet &type=ns.follow(op.type());
147146

148147
if(type.id()==ID_signedbv ||
149148
type.id()==ID_unsignedbv)
150149
{
151150
mp_integer value;
152-
if(!to_integer(expr.op0(), value))
151+
if(!to_integer(op, value))
153152
{
154153
std::size_t result;
155154

156155
for(result=0; value!=0; value=value>>1)
157156
if(value.is_odd())
158157
result++;
159158

160-
expr=from_integer(result, expr.type());
159+
exprt simp_result = from_integer(result, expr.type());
160+
expr.swap(simp_result);
161161

162162
return false;
163163
}
@@ -2334,8 +2334,8 @@ bool simplify_exprt::simplify_node(exprt &expr)
23342334
result=simplify_abs(expr) && result;
23352335
else if(expr.id()==ID_sign)
23362336
result=simplify_sign(expr) && result;
2337-
else if(expr.id()==ID_popcount)
2338-
result=simplify_popcount(expr) && result;
2337+
else if(expr.id() == ID_popcount)
2338+
result = simplify_popcount(to_popcount_expr(expr)) && result;
23392339

23402340
#ifdef DEBUGX
23412341
if(!result

src/util/simplify_expr_class.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class if_exprt;
2929
class index_exprt;
3030
class member_exprt;
3131
class namespacet;
32+
class popcount_exprt;
3233
class tvt;
3334

3435
#define forall_value_list(it, value_list) \
@@ -107,7 +108,7 @@ class simplify_exprt
107108
bool simplify_isnormal(exprt &expr);
108109
bool simplify_abs(exprt &expr);
109110
bool simplify_sign(exprt &expr);
110-
bool simplify_popcount(exprt &expr);
111+
bool simplify_popcount(popcount_exprt &expr);
111112

112113
// auxiliary
113114
bool simplify_if_implies(

src/util/std_expr.h

+57
Original file line numberDiff line numberDiff line change
@@ -4807,4 +4807,61 @@ class exists_exprt:public binary_exprt
48074807
}
48084808
};
48094809

4810+
/*! \brief The popcount (counting the number of bits set to 1) expression
4811+
*/
4812+
class popcount_exprt: public unary_exprt
4813+
{
4814+
public:
4815+
popcount_exprt(): unary_exprt(ID_popcount)
4816+
{
4817+
}
4818+
4819+
popcount_exprt(const exprt &_op, const typet &_type)
4820+
: unary_exprt(ID_popcount, _op, _type)
4821+
{
4822+
}
4823+
4824+
explicit popcount_exprt(const exprt &_op)
4825+
: unary_exprt(ID_popcount, _op, _op.type())
4826+
{
4827+
}
4828+
};
4829+
4830+
/*! \brief Cast a generic exprt to a \ref popcount_exprt
4831+
*
4832+
* This is an unchecked conversion. \a expr must be known to be \ref
4833+
* popcount_exprt.
4834+
*
4835+
* \param expr Source expression
4836+
* \return Object of type \ref popcount_exprt
4837+
*
4838+
* \ingroup gr_std_expr
4839+
*/
4840+
inline const popcount_exprt &to_popcount_expr(const exprt &expr)
4841+
{
4842+
PRECONDITION(expr.id() == ID_popcount);
4843+
DATA_INVARIANT(expr.operands().size() == 1, "popcount must have one operand");
4844+
return static_cast<const popcount_exprt &>(expr);
4845+
}
4846+
4847+
/*! \copydoc to_popcount_expr(const exprt &)
4848+
* \ingroup gr_std_expr
4849+
*/
4850+
inline popcount_exprt &to_popcount_expr(exprt &expr)
4851+
{
4852+
PRECONDITION(expr.id() == ID_popcount);
4853+
DATA_INVARIANT(expr.operands().size() == 1, "popcount must have one operand");
4854+
return static_cast<popcount_exprt &>(expr);
4855+
}
4856+
4857+
template <>
4858+
inline bool can_cast_expr<popcount_exprt>(const exprt &base)
4859+
{
4860+
return base.id() == ID_popcount;
4861+
}
4862+
inline void validate_expr(const popcount_exprt &value)
4863+
{
4864+
validate_operands(value, 1, "popcount must have one operand");
4865+
}
4866+
48104867
#endif // CPROVER_UTIL_STD_EXPR_H

0 commit comments

Comments
 (0)