Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement popcount #1883

Merged
merged 2 commits into from
Feb 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions regression/cbmc/gcc_popcount1/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include <assert.h>

#ifndef __GNUC__
int __builtin_popcount(unsigned int);
int __builtin_popcountl(unsigned long);
int __builtin_popcountll(unsigned long long);
#endif

unsigned int __VERIFIER_nondet_unsigned();
unsigned long __VERIFIER_nondet_unsigned_long();
unsigned long long __VERIFIER_nondet_unsigned_long_long();

// Hacker's Delight
// http://www.hackersdelight.org/permissions.htm
int pop4(unsigned long long x)
{
int n = 0;

// variant with additional bounding to make sure symbolic execution always
// terminates without having to specify an unwinding bound
for(int i = 0; x != 0 && i < sizeof(x) * 8; ++i)
{
++n;
x = x & (x - 1);
}

return n;
}

int main()
{
assert(pop4(42) == 3);
assert(__builtin_popcount(42) == 3);
assert(__builtin_popcountl(42) == 3);
assert(__builtin_popcountll(42) == 3);

unsigned int u = __VERIFIER_nondet_unsigned();
assert(pop4(u) == __builtin_popcount(u));

return 0;
}
8 changes: 8 additions & 0 deletions regression/cbmc/gcc_popcount1/test.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CORE
main.c

^EXIT=0$
^SIGNAL=0$
^VERIFICATION SUCCESSFUL$
--
^warning: ignoring
44 changes: 44 additions & 0 deletions regression/cbmc/gcc_popcount2/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include <assert.h>

#ifndef __GNUC__
int __builtin_popcount(unsigned int);
int __builtin_popcountl(unsigned long);
int __builtin_popcountll(unsigned long long);
#endif

unsigned int __VERIFIER_nondet_unsigned();
unsigned long __VERIFIER_nondet_unsigned_long();
unsigned long long __VERIFIER_nondet_unsigned_long_long();

// Hacker's Delight
// http://www.hackersdelight.org/permissions.htm
int pop4(unsigned long long x)
{
int n = 0;

// variant with additional bounding to make sure symbolic execution always
// terminates without having to specify an unwinding bound
for(int i = 0; x != 0 && i < sizeof(x) * 8; ++i)
{
++n;
x = x & (x - 1);
}

return n;
}

int main()
{
unsigned long ul = __VERIFIER_nondet_unsigned_long();
assert(pop4(ul) == __builtin_popcountl(ul));

unsigned long long ull = __VERIFIER_nondet_unsigned_long_long();
assert(pop4(ull) == __builtin_popcountll(ull));

// expected to fail as bits may have been cut off
assert(
sizeof(ull) != sizeof(unsigned int) &&
pop4(ull) == __builtin_popcount(ull));

return 0;
}
10 changes: 10 additions & 0 deletions regression/cbmc/gcc_popcount2/test.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
THOROUGH
main.c

^EXIT=10$
^SIGNAL=0$
^VERIFICATION FAILED$
\[main\.assertion\.1\] assertion sizeof\(ull\) != sizeof\(unsigned int\) && pop4\(ull\) == __builtin_popcount\(ull\): FAILURE$
^\*\* 1 of 3 failed
--
^warning: ignoring
3 changes: 1 addition & 2 deletions src/ansi-c/c_typecheck_expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2355,8 +2355,7 @@ exprt c_typecheck_baset::do_special_functions(
throw 0;
}

exprt popcount_expr(ID_popcount, expr.type());
popcount_expr.operands()=expr.arguments();
popcount_exprt popcount_expr(expr.arguments().front(), expr.type());
popcount_expr.add_source_location()=source_location;

return popcount_expr;
Expand Down
1 change: 1 addition & 0 deletions src/solvers/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ SRC = $(BOOLEFORCE_SRC) \
floatbv/float_bv.cpp \
floatbv/float_utils.cpp \
floatbv/float_approximation.cpp \
lowering/popcount.cpp \
miniBDD/miniBDD.cpp \
prop/aig.cpp \
prop/aig_prop.cpp \
Expand Down
3 changes: 3 additions & 0 deletions src/solvers/flattening/boolbv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Author: Daniel Kroening, [email protected]
#include "boolbv_type.h"

#include "../floatbv/float_utils.h"
#include "../lowering/expr_lowering.h"

bool boolbvt::literal(
const exprt &expr,
Expand Down Expand Up @@ -305,6 +306,8 @@ bvt boolbvt::convert_bitvector(const exprt &expr)
float_utils.debug2(bv0, bv1);
return bv;
}
else if(expr.id() == ID_popcount)
return convert_bv(lower_popcount(to_popcount_expr(expr), ns));

return conversion_failed(expr);
}
Expand Down
23 changes: 23 additions & 0 deletions src/solvers/lowering/expr_lowering.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*******************************************************************\

Module: Lower expressions to arithmetic and logic expressions

Author: Michael Tautschnig

\*******************************************************************/

#ifndef CPROVER_SOLVERS_LOWERING_EXPR_LOWERING_H
#define CPROVER_SOLVERS_LOWERING_EXPR_LOWERING_H

#include <util/expr.h>

class namespacet;
class popcount_exprt;

/// Lower a popcount_exprt to arithmetic and logic expressions
/// \param expr Input expression to be translated
/// \param ns Namespace for type lookups
/// \return Semantically equivalent expression
exprt lower_popcount(const popcount_exprt &expr, const namespacet &ns);

#endif /* CPROVER_SOLVERS_LOWERING_EXPR_LOWERING_H */
59 changes: 59 additions & 0 deletions src/solvers/lowering/popcount.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*******************************************************************\

Module: Lowering of popcount

Author: Michael Tautschnig

\*******************************************************************/

#include "expr_lowering.h"

#include <util/arith_tools.h>
#include <util/invariant.h>
#include <util/pointer_offset_size.h>
#include <util/std_expr.h>

exprt lower_popcount(const popcount_exprt &expr, const namespacet &ns)
{
// Hacker's Delight, variant pop0:
// x = (x & 0x55555555) + ((x >> 1) & 0x55555555);
// x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
// x = (x & 0x0F0F0F0F) + ((x >> 4) & 0x0F0F0F0F);
// x = (x & 0x00FF00FF) + ((x >> 8) & 0x00FF00FF);
// etc.
// return x;
// http://www.hackersdelight.org/permissions.htm

// make sure the operand width is a power of two
exprt x = expr.op();
const mp_integer x_width = pointer_offset_bits(x.type(), ns);
CHECK_RETURN(x_width > 0);
const std::size_t bits = address_bits(x_width);
const std::size_t new_width = integer2size_t(power(2, bits));
const bool need_typecast =
new_width > x_width || x.type().id() != ID_unsignedbv;
if(need_typecast)
x.make_typecast(unsignedbv_typet(new_width));

// repeatedly compute x = (x & bitmask) + ((x >> shift) & bitmask)
for(std::size_t shift = 1; shift < new_width; shift <<= 1)
{
// x >> shift
lshr_exprt shifted_x(
x, from_integer(shift, unsignedbv_typet(address_bits(shift) + 1)));
// bitmask is a string of alternating shift-many bits starting from lsb set
// to 1
std::string bitstring;
bitstring.reserve(new_width);
for(std::size_t i = 0; i < new_width / (2 * shift); ++i)
bitstring += std::string(shift, '0') + std::string(shift, '1');
constant_exprt bitmask(bitstring, x.type());
// build the expression
x = plus_exprt(bitand_exprt(x, bitmask), bitand_exprt(shifted_x, bitmask));
}

// the result is restricted to the result type
x.make_typecast(expr.type());

return x;
}
18 changes: 9 additions & 9 deletions src/util/simplify_expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,28 +136,28 @@ bool simplify_exprt::simplify_sign(exprt &expr)
return true;
}

bool simplify_exprt::simplify_popcount(exprt &expr)
bool simplify_exprt::simplify_popcount(popcount_exprt &expr)
{
if(expr.operands().size()!=1)
return true;
const exprt &op = expr.op();

if(expr.op0().is_constant())
if(op.is_constant())
{
const typet &type=ns.follow(expr.op0().type());
const typet &type=ns.follow(op.type());

if(type.id()==ID_signedbv ||
type.id()==ID_unsignedbv)
{
mp_integer value;
if(!to_integer(expr.op0(), value))
if(!to_integer(op, value))
{
std::size_t result;

for(result=0; value!=0; value=value>>1)
if(value.is_odd())
result++;

expr=from_integer(result, expr.type());
exprt simp_result = from_integer(result, expr.type());
expr.swap(simp_result);

return false;
}
Expand Down Expand Up @@ -2334,8 +2334,8 @@ bool simplify_exprt::simplify_node(exprt &expr)
result=simplify_abs(expr) && result;
else if(expr.id()==ID_sign)
result=simplify_sign(expr) && result;
else if(expr.id()==ID_popcount)
result=simplify_popcount(expr) && result;
else if(expr.id() == ID_popcount)
result = simplify_popcount(to_popcount_expr(expr)) && result;

#ifdef DEBUGX
if(!result
Expand Down
3 changes: 2 additions & 1 deletion src/util/simplify_expr_class.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class if_exprt;
class index_exprt;
class member_exprt;
class namespacet;
class popcount_exprt;
class tvt;

#define forall_value_list(it, value_list) \
Expand Down Expand Up @@ -107,7 +108,7 @@ class simplify_exprt
bool simplify_isnormal(exprt &expr);
bool simplify_abs(exprt &expr);
bool simplify_sign(exprt &expr);
bool simplify_popcount(exprt &expr);
bool simplify_popcount(popcount_exprt &expr);

// auxiliary
bool simplify_if_implies(
Expand Down
57 changes: 57 additions & 0 deletions src/util/std_expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -4807,4 +4807,61 @@ class exists_exprt:public binary_exprt
}
};

/*! \brief The popcount (counting the number of bits set to 1) expression
*/
class popcount_exprt: public unary_exprt
{
public:
popcount_exprt(): unary_exprt(ID_popcount)
{
}

popcount_exprt(const exprt &_op, const typet &_type)
: unary_exprt(ID_popcount, _op, _type)
{
}

explicit popcount_exprt(const exprt &_op)
: unary_exprt(ID_popcount, _op, _op.type())
{
}
};

/*! \brief Cast a generic exprt to a \ref popcount_exprt
*
* This is an unchecked conversion. \a expr must be known to be \ref
* popcount_exprt.
*
* \param expr Source expression
* \return Object of type \ref popcount_exprt
*
* \ingroup gr_std_expr
*/
inline const popcount_exprt &to_popcount_expr(const exprt &expr)
{
PRECONDITION(expr.id() == ID_popcount);
DATA_INVARIANT(expr.operands().size() == 1, "popcount must have one operand");
return static_cast<const popcount_exprt &>(expr);
}

/*! \copydoc to_popcount_expr(const exprt &)
* \ingroup gr_std_expr
*/
inline popcount_exprt &to_popcount_expr(exprt &expr)
{
PRECONDITION(expr.id() == ID_popcount);
DATA_INVARIANT(expr.operands().size() == 1, "popcount must have one operand");
return static_cast<popcount_exprt &>(expr);
}

template <>
inline bool can_cast_expr<popcount_exprt>(const exprt &base)
{
return base.id() == ID_popcount;
}
inline void validate_expr(const popcount_exprt &value)
{
validate_operands(value, 1, "popcount must have one operand");
}

#endif // CPROVER_UTIL_STD_EXPR_H