Skip to content

Commit

Permalink
New expression: count_leading_zeros_exprt
Browse files Browse the repository at this point in the history
Rather than ad-hoc handling __builtin_clz and __lzcnt (and their
variants) in the C front-end, make counting-leading-zeros available
across the code base.
  • Loading branch information
tautschnig committed Mar 10, 2021
1 parent 2f560d0 commit 7409997
Show file tree
Hide file tree
Showing 17 changed files with 239 additions and 137 deletions.
10 changes: 10 additions & 0 deletions regression/ansi-c/__builtin_clz-02/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
int main()
{
#ifdef __GNUC__
_Static_assert(
__builtin_clz(0xffU) == 8 * sizeof(unsigned) - 8,
"GCC/Clang compile-time constant");
#endif

return 0;
}
12 changes: 0 additions & 12 deletions regression/cbmc-library/__builtin_clz-02/main.c

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,9 @@ int main()
__CPROVER_assume(u != 0);
assert(nlz2a(u) == __builtin_clz(u));

#undef __builtin_clz
// a failing assertion should be generated as __builtin_clz is undefined for 0
__builtin_clz(0U);

return 0;
}
10 changes: 10 additions & 0 deletions regression/cbmc/__builtin_clz-01/test.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
CORE
main.c
--bounds-check
^\[main.bit_count.\d+\] line 61 count leading zeros argument in __builtin_clz\(0u\): FAILURE$
^\*\* 1 of \d+ failed
^VERIFICATION FAILED$
^EXIT=10$
^SIGNAL=0$
--
^warning: ignoring
37 changes: 32 additions & 5 deletions src/analyses/goto_check.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,9 @@ class goto_checkt

using conditionst = std::list<conditiont>;

void bounds_check(const index_exprt &, const guardt &);
void bounds_check(const exprt &, const guardt &);
void bounds_check_index(const index_exprt &, const guardt &);
void bounds_check_clz(const count_leading_zeros_exprt &, const guardt &);
void div_by_zero_check(const div_exprt &, const guardt &);
void mod_by_zero_check(const mod_exprt &, const guardt &);
void mod_overflow_check(const mod_exprt &, const guardt &);
Expand Down Expand Up @@ -1321,9 +1323,7 @@ std::string goto_checkt::array_name(const exprt &expr)
return ::array_name(ns, expr);
}

void goto_checkt::bounds_check(
const index_exprt &expr,
const guardt &guard)
void goto_checkt::bounds_check(const exprt &expr, const guardt &guard)
{
if(!enable_bounds_check)
return;
Expand All @@ -1335,6 +1335,16 @@ void goto_checkt::bounds_check(
return;
}

if(expr.id() == ID_index)
bounds_check_index(to_index_expr(expr), guard);
else if(expr.id() == ID_count_leading_zeros)
bounds_check_clz(to_count_leading_zeros_expr(expr), guard);
}

void goto_checkt::bounds_check_index(
const index_exprt &expr,
const guardt &guard)
{
typet array_type = expr.array().type();

if(array_type.id()==ID_pointer)
Expand Down Expand Up @@ -1510,6 +1520,19 @@ void goto_checkt::bounds_check(
}
}

void goto_checkt::bounds_check_clz(
const count_leading_zeros_exprt &expr,
const guardt &guard)
{
add_guarded_property(
notequal_exprt{expr.op(), from_integer(0, expr.op().type())},
"count leading zeros argument",
"bit count",
expr.find_source_location(),
expr,
guard);
}

void goto_checkt::add_guarded_property(
const exprt &asserted_expr,
const std::string &comment,
Expand Down Expand Up @@ -1729,7 +1752,7 @@ void goto_checkt::check_rec(const exprt &expr, guardt &guard)

if(expr.id()==ID_index)
{
bounds_check(to_index_expr(expr), guard);
bounds_check(expr, guard);
}
else if(expr.id()==ID_div)
{
Expand Down Expand Up @@ -1766,6 +1789,10 @@ void goto_checkt::check_rec(const exprt &expr, guardt &guard)
{
pointer_primitive_check(expr, guard);
}
else if(expr.id() == ID_count_leading_zeros)
{
bounds_check(expr, guard);
}
}

void goto_checkt::check(const exprt &expr)
Expand Down
25 changes: 6 additions & 19 deletions src/ansi-c/c_typecheck_expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2689,27 +2689,14 @@ exprt c_typecheck_baset::do_special_functions(
throw 0;
}

side_effect_expr_function_callt try_constant{expr};
typecheck_function_call_arguments(try_constant);
exprt argument = try_constant.arguments().front();
simplify(argument, *this);
const auto int_constant = numeric_cast<mp_integer>(argument);

if(
!int_constant.has_value() || *int_constant == 0 ||
argument.type().id() != ID_unsignedbv)
{
return nil_exprt{};
}
typecheck_function_call_arguments(expr);

const std::string binary_value = integer2binary(
*int_constant, to_unsignedbv_type(argument.type()).get_width());
std::size_t n_leading_zeros = binary_value.find('1');
CHECK_RETURN(n_leading_zeros != std::string::npos);
count_leading_zeros_exprt clz{expr.arguments().front(),
has_prefix(id2string(identifier), "__lzcnt"),
expr.type()};
clz.add_source_location() = source_location;

return from_integer(
n_leading_zeros,
to_code_type(try_constant.function().type()).return_type());
return std::move(clz);
}
else if(identifier==CPROVER_PREFIX "equal")
{
Expand Down
3 changes: 3 additions & 0 deletions src/ansi-c/expr2c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3896,6 +3896,9 @@ std::string expr2ct::convert_with_precedence(
else if(src.id()==ID_type)
return convert(src.type());

else if(src.id() == ID_count_leading_zeros)
return convert_function(src, "__builtin_clz");

// no C language expression for internal representation
return convert_norep(src, precedence);
}
Expand Down
58 changes: 0 additions & 58 deletions src/ansi-c/library/gcc.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,64 +33,6 @@ inline void __sync_synchronize(void)
#endif
}

/* FUNCTION: __builtin_clz */

int __builtin_popcount(unsigned int);

inline int __builtin_clz(unsigned int x)
{
__CPROVER_precondition(x != 0, "__builtin_clz(0) is undefined");

x = x | (x >> 1);
x = x | (x >> 2);
x = x | (x >> 4);
x = x | (x >> 8);
if(sizeof(x) >= 4)
x = x | (x >> 16);

return __builtin_popcount(~x);
}

/* FUNCTION: __builtin_clzl */

int __builtin_popcountl(unsigned long int);

inline int __builtin_clzl(unsigned long int x)
{
__CPROVER_precondition(x != 0, "__builtin_clzl(0) is undefined");

x = x | (x >> 1);
x = x | (x >> 2);
x = x | (x >> 4);
x = x | (x >> 8);
if(sizeof(x) >= 4)
x = x | (x >> 16);
if(sizeof(x) >= 8)
x = x | (x >> 32);

return __builtin_popcountl(~x);
}

/* FUNCTION: __builtin_clzll */

int __builtin_popcountll(unsigned long long int);

inline int __builtin_clzll(unsigned long long int x)
{
__CPROVER_precondition(x != 0, "__builtin_clzll(0) is undefined");

x = x | (x >> 1);
x = x | (x >> 2);
x = x | (x >> 4);
x = x | (x >> 8);
if(sizeof(x) >= 4)
x = x | (x >> 16);
if(sizeof(x) >= 8)
x = x | (x >> 32);

return __builtin_popcountll(~x);
}

/* FUNCTION: __builtin_ffs */

int __builtin_clz(unsigned int x);
Expand Down
43 changes: 0 additions & 43 deletions src/ansi-c/library/windows.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,46 +51,3 @@ inline HANDLE CreateThread(
return handle;
}
#endif

/* FUNCTION: __lzcnt16 */

#ifdef _MSC_VER
int __builtin_clz(unsigned int x);

unsigned short __lzcnt16(unsigned short value)
{
if(value == 0)
return 16;

return __builtin_clz(value) -
(sizeof(unsigned int) - sizeof(unsigned short)) * 8;
}
#endif

/* FUNCTION: __lzcnt */

#ifdef _MSC_VER
int __builtin_clz(unsigned int x);

unsigned int __lzcnt(unsigned int value)
{
if(value == 0)
return 32;

return __builtin_clz(value);
}
#endif

/* FUNCTION: __lzcnt64 */

#ifdef _MSC_VER
int __builtin_clzll(unsigned long long x);

unsigned __int64 __lzcnt64(unsigned __int64 value)
{
if(value == 0)
return 64;

return __builtin_clzll(value);
}
#endif
5 changes: 5 additions & 0 deletions src/solvers/flattening/boolbv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,11 @@ bvt boolbvt::convert_bitvector(const exprt &expr)
return convert_power(to_binary_expr(expr));
else if(expr.id() == ID_popcount)
return convert_bv(simplify_expr(to_popcount_expr(expr).lower(), ns));
else if(expr.id() == ID_count_leading_zeros)
{
return convert_bv(
simplify_expr(to_count_leading_zeros_expr(expr).lower(), ns));
}

return conversion_failed(expr);
}
Expand Down
4 changes: 4 additions & 0 deletions src/solvers/smt2/smt2_conv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1987,6 +1987,10 @@ void smt2_convt::convert_expr(const exprt &expr)
{
convert_expr(simplify_expr(to_popcount_expr(expr).lower(), ns));
}
else if(expr.id() == ID_count_leading_zeros)
{
convert_expr(simplify_expr(to_count_leading_zeros_expr(expr).lower(), ns));
}
else
INVARIANT_WITH_DIAGNOSTICS(
false,
Expand Down
38 changes: 38 additions & 0 deletions src/util/bitvector_expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,41 @@ exprt popcount_exprt::lower() const
// the result is restricted to the result type
return typecast_exprt::conditional_cast(x, type());
}

exprt count_leading_zeros_exprt::lower() const
{
// x = x | (x >> 1);
// x = x | (x >> 2);
// x = x | (x >> 4);
// x = x | (x >> 8);
// etc.
// return popcount(~x);

// make sure the operand width is a power of two
exprt x = op();
const auto x_width = to_bitvector_type(x.type()).get_width();
CHECK_RETURN(x_width >= 1);
const std::size_t bits = address_bits(x_width);
const std::size_t new_width = numeric_cast_v<std::size_t>(power(2, bits));

const bool need_typecast =
new_width > x_width || x.type().id() != ID_unsignedbv;

if(need_typecast)
x = typecast_exprt(x, unsignedbv_typet(new_width));

// repeatedly compute x = x | (x >> shift)
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)));
// build the expression
x = bitor_exprt{x, shifted_x};
}

// the result is restricted to the result type
return popcount_exprt{
bitnot_exprt{typecast_exprt::conditional_cast(x, op().type())}, type()}
.lower();
}
Loading

0 comments on commit 7409997

Please sign in to comment.