Skip to content

Commit e7169de

Browse files
committed
Implement __builtin_ctz{,l,ll} via count_trailing_zeros_exprt
__builtin_ctz returns the number of trailing zeros. Just like ffs and clz, introduce a new bitvector expression.
1 parent 66d3773 commit e7169de

File tree

17 files changed

+277
-62
lines changed

17 files changed

+277
-62
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#include <assert.h>
2+
#include <limits.h>
3+
4+
#ifndef __GNUC__
5+
int __builtin_ctz(unsigned int);
6+
int __builtin_ctzl(unsigned long);
7+
int __builtin_ctzll(unsigned long long);
8+
#endif
9+
10+
#ifdef _MSC_VER
11+
# define _Static_assert(x, m) static_assert(x, m)
12+
#endif
13+
14+
unsigned __VERIFIER_nondet_unsigned();
15+
16+
// http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightMultLookup
17+
static const int multiply_de_bruijn_bit_position[32] = {
18+
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
19+
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9};
20+
21+
int main()
22+
{
23+
_Static_assert(__builtin_ctz(1) == 0, "");
24+
_Static_assert(__builtin_ctz(UINT_MAX) == 0, "");
25+
_Static_assert(
26+
__builtin_ctz(1U << (sizeof(unsigned) * 8 - 1)) == sizeof(unsigned) * 8 - 1,
27+
"");
28+
_Static_assert(
29+
__builtin_ctzl(1UL << (sizeof(unsigned) * 8 - 1)) ==
30+
sizeof(unsigned) * 8 - 1,
31+
"");
32+
_Static_assert(
33+
__builtin_ctzll(1ULL << (sizeof(unsigned) * 8 - 1)) ==
34+
sizeof(unsigned) * 8 - 1,
35+
"");
36+
37+
unsigned u = __VERIFIER_nondet_unsigned();
38+
__CPROVER_assume(u != 0);
39+
__CPROVER_assume(sizeof(u) == 4);
40+
__CPROVER_assume(u > INT_MIN);
41+
assert(
42+
multiply_de_bruijn_bit_position
43+
[((unsigned)((u & -u) * 0x077CB531U)) >> 27] == __builtin_ctz(u));
44+
45+
// a failing assertion should be generated as __builtin_ctz is undefined for 0
46+
__builtin_ctz(0U);
47+
48+
return 0;
49+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--bounds-check
4+
^\[main.bit_count.\d+\] line 46 count trailing zeros argument in __builtin_ctz\(0u\): FAILURE$
5+
^\*\* 1 of \d+ failed
6+
^VERIFICATION FAILED$
7+
^EXIT=10$
8+
^SIGNAL=0$
9+
--
10+
^warning: ignoring

scripts/expected_doxygen_warnings.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,20 +82,20 @@
8282
warning: Include graph for 'goto_instrument_parse_options.cpp' not generated, too many nodes (97), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
8383
warning: Included by graph for 'goto_functions.h' not generated, too many nodes (66), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
8484
warning: Included by graph for 'goto_model.h' not generated, too many nodes (110), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
85-
warning: Included by graph for 'arith_tools.h' not generated, too many nodes (182), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
85+
warning: Included by graph for 'arith_tools.h' not generated, too many nodes (183), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
8686
warning: Included by graph for 'c_types.h' not generated, too many nodes (110), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
8787
warning: Included by graph for 'config.h' not generated, too many nodes (87), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
8888
warning: Included by graph for 'exception_utils.h' not generated, too many nodes (61), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
8989
warning: Included by graph for 'expr.h' not generated, too many nodes (87), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
9090
warning: Included by graph for 'expr_util.h' not generated, too many nodes (61), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
91-
warning: Included by graph for 'invariant.h' not generated, too many nodes (188), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
91+
warning: Included by graph for 'invariant.h' not generated, too many nodes (189), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
9292
warning: Included by graph for 'irep.h' not generated, too many nodes (62), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
9393
warning: Included by graph for 'message.h' not generated, too many nodes (117), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
9494
warning: Included by graph for 'namespace.h' not generated, too many nodes (109), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
9595
warning: Included by graph for 'pointer_expr.h' not generated, too many nodes (101), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
9696
warning: Included by graph for 'prefix.h' not generated, too many nodes (86), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
97-
warning: Included by graph for 'simplify_expr.h' not generated, too many nodes (78), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
97+
warning: Included by graph for 'simplify_expr.h' not generated, too many nodes (79), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
9898
warning: Included by graph for 'std_code.h' not generated, too many nodes (78), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
99-
warning: Included by graph for 'std_expr.h' not generated, too many nodes (245), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
99+
warning: Included by graph for 'std_expr.h' not generated, too many nodes (246), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
100100
warning: Included by graph for 'std_types.h' not generated, too many nodes (121), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
101101
warning: Included by graph for 'symbol_table.h' not generated, too many nodes (95), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.

src/analyses/goto_check.cpp

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ class goto_checkt
174174

175175
void bounds_check(const exprt &, const guardt &);
176176
void bounds_check_index(const index_exprt &, const guardt &);
177-
void bounds_check_clz(const count_leading_zeros_exprt &, const guardt &);
177+
void bounds_check_bit_count(const unary_exprt &, const guardt &);
178178
void div_by_zero_check(const div_exprt &, const guardt &);
179179
void mod_by_zero_check(const mod_exprt &, const guardt &);
180180
void mod_overflow_check(const mod_exprt &, const guardt &);
@@ -1326,8 +1326,11 @@ void goto_checkt::bounds_check(const exprt &expr, const guardt &guard)
13261326

13271327
if(expr.id() == ID_index)
13281328
bounds_check_index(to_index_expr(expr), guard);
1329-
else if(expr.id() == ID_count_leading_zeros)
1330-
bounds_check_clz(to_count_leading_zeros_expr(expr), guard);
1329+
else if(
1330+
expr.id() == ID_count_leading_zeros || expr.id() == ID_count_trailing_zeros)
1331+
{
1332+
bounds_check_bit_count(to_unary_expr(expr), guard);
1333+
}
13311334
}
13321335

13331336
void goto_checkt::bounds_check_index(
@@ -1509,13 +1512,22 @@ void goto_checkt::bounds_check_index(
15091512
}
15101513
}
15111514

1512-
void goto_checkt::bounds_check_clz(
1513-
const count_leading_zeros_exprt &expr,
1515+
void goto_checkt::bounds_check_bit_count(
1516+
const unary_exprt &expr,
15141517
const guardt &guard)
15151518
{
1519+
std::string name;
1520+
1521+
if(expr.id() == ID_count_leading_zeros)
1522+
name = "leading";
1523+
else if(expr.id() == ID_count_trailing_zeros)
1524+
name = "trailing";
1525+
else
1526+
UNREACHABLE;
1527+
15161528
add_guarded_property(
15171529
notequal_exprt{expr.op(), from_integer(0, expr.op().type())},
1518-
"count leading zeros argument",
1530+
"count " + name + " zeros argument",
15191531
"bit count",
15201532
expr.find_source_location(),
15211533
expr,
@@ -1778,7 +1790,8 @@ void goto_checkt::check_rec(const exprt &expr, guardt &guard)
17781790
{
17791791
pointer_primitive_check(expr, guard);
17801792
}
1781-
else if(expr.id() == ID_count_leading_zeros)
1793+
else if(
1794+
expr.id() == ID_count_leading_zeros || expr.id() == ID_count_trailing_zeros)
17821795
{
17831796
bounds_check(expr, guard);
17841797
}

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2698,6 +2698,25 @@ exprt c_typecheck_baset::do_special_functions(
26982698

26992699
return std::move(clz);
27002700
}
2701+
else if(
2702+
identifier == "__builtin_ctz" || identifier == "__builtin_ctzl" ||
2703+
identifier == "__builtin_ctzll")
2704+
{
2705+
if(expr.arguments().size() != 1)
2706+
{
2707+
error().source_location = f_op.source_location();
2708+
error() << identifier << " expects one operand" << eom;
2709+
throw 0;
2710+
}
2711+
2712+
typecheck_function_call_arguments(expr);
2713+
2714+
count_trailing_zeros_exprt ctz{
2715+
expr.arguments().front(), false, expr.type()};
2716+
ctz.add_source_location() = source_location;
2717+
2718+
return std::move(ctz);
2719+
}
27012720
else if(identifier==CPROVER_PREFIX "equal")
27022721
{
27032722
if(expr.arguments().size()!=2)

src/ansi-c/expr2c.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3899,6 +3899,9 @@ std::string expr2ct::convert_with_precedence(
38993899
else if(src.id() == ID_count_leading_zeros)
39003900
return convert_function(src, "__builtin_clz");
39013901

3902+
else if(src.id() == ID_count_trailing_zeros)
3903+
return convert_function(src, "__builtin_ctz");
3904+
39023905
// no C language expression for internal representation
39033906
return convert_norep(src, precedence);
39043907
}

src/ansi-c/library/gcc.c

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -33,57 +33,6 @@ inline void __sync_synchronize(void)
3333
#endif
3434
}
3535

36-
/* FUNCTION: __builtin_ffs */
37-
38-
int __builtin_clz(unsigned int x);
39-
40-
inline int __builtin_ffs(int x)
41-
{
42-
if(x == 0)
43-
return 0;
44-
45-
#pragma CPROVER check push
46-
#pragma CPROVER check disable "conversion"
47-
unsigned int u = (unsigned int)x;
48-
#pragma CPROVER check pop
49-
50-
return sizeof(int) * 8 - __builtin_clz(u & ~(u - 1));
51-
}
52-
53-
/* FUNCTION: __builtin_ffsl */
54-
55-
int __builtin_clzl(unsigned long x);
56-
57-
inline int __builtin_ffsl(long x)
58-
{
59-
if(x == 0)
60-
return 0;
61-
62-
#pragma CPROVER check push
63-
#pragma CPROVER check disable "conversion"
64-
unsigned long u = (unsigned long)x;
65-
#pragma CPROVER check pop
66-
67-
return sizeof(long) * 8 - __builtin_clzl(u & ~(u - 1));
68-
}
69-
70-
/* FUNCTION: __builtin_ffsll */
71-
72-
int __builtin_clzll(unsigned long long x);
73-
74-
inline int __builtin_ffsll(long long x)
75-
{
76-
if(x == 0)
77-
return 0;
78-
79-
#pragma CPROVER check push
80-
#pragma CPROVER check disable "conversion"
81-
unsigned long long u = (unsigned long long)x;
82-
#pragma CPROVER check pop
83-
84-
return sizeof(long long) * 8 - __builtin_clzll(u & ~(u - 1));
85-
}
86-
8736
/* FUNCTION: __atomic_test_and_set */
8837

8938
void __atomic_thread_fence(int memorder);

src/solvers/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ SRC = $(BOOLEFORCE_SRC) \
141141
floatbv/float_approximation.cpp \
142142
lowering/byte_operators.cpp \
143143
lowering/count_leading_zeros.cpp \
144+
lowering/count_trailing_zeros.cpp \
144145
lowering/functions.cpp \
145146
lowering/popcount.cpp \
146147
bdd/miniBDD/miniBDD.cpp \

src/solvers/flattening/boolbv.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,8 @@ bvt boolbvt::convert_bitvector(const exprt &expr)
230230
return convert_bv(lower_popcount(to_popcount_expr(expr), ns));
231231
else if(expr.id() == ID_count_leading_zeros)
232232
return convert_bv(lower_clz(to_count_leading_zeros_expr(expr), ns));
233+
else if(expr.id() == ID_count_trailing_zeros)
234+
return convert_bv(lower_ctz(to_count_trailing_zeros_expr(expr), ns));
233235

234236
return conversion_failed(expr);
235237
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*******************************************************************\
2+
3+
Module: Lowering of count_trailing_zeros
4+
5+
Author: Michael Tautschnig
6+
7+
\*******************************************************************/
8+
9+
#include "expr_lowering.h"
10+
11+
#include <util/arith_tools.h>
12+
#include <util/bitvector_expr.h>
13+
#include <util/invariant.h>
14+
#include <util/pointer_offset_size.h>
15+
#include <util/simplify_expr.h>
16+
#include <util/std_expr.h>
17+
18+
exprt lower_ctz(const count_trailing_zeros_exprt &expr, const namespacet &ns)
19+
{
20+
exprt x = expr.op();
21+
const auto x_width = pointer_offset_bits(x.type(), ns);
22+
CHECK_RETURN(x_width.has_value() && *x_width >= 1);
23+
const std::size_t int_width = numeric_cast_v<std::size_t>(*x_width);
24+
25+
// popcount(x ^ ((unsigned)x - 1)) - 1
26+
const unsignedbv_typet ut{int_width};
27+
minus_exprt minus_one{typecast_exprt::conditional_cast(x, ut),
28+
from_integer(1, ut)};
29+
popcount_exprt popcount{
30+
bitxor_exprt{x, typecast_exprt::conditional_cast(minus_one, x.type())}};
31+
minus_exprt result{lower_popcount(popcount, ns), from_integer(1, x.type())};
32+
33+
return simplify_expr(
34+
typecast_exprt::conditional_cast(result, expr.type()), ns);
35+
}

src/solvers/lowering/expr_lowering.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Author: Michael Tautschnig
1414
class byte_extract_exprt;
1515
class byte_update_exprt;
1616
class count_leading_zeros_exprt;
17+
class count_trailing_zeros_exprt;
1718
class namespacet;
1819
class popcount_exprt;
1920

@@ -57,4 +58,10 @@ exprt lower_popcount(const popcount_exprt &expr, const namespacet &ns);
5758
/// \return Semantically equivalent expression
5859
exprt lower_clz(const count_leading_zeros_exprt &expr, const namespacet &ns);
5960

61+
/// Lower a count_trailing_zeros_exprt to arithmetic and logic expressions.
62+
/// \param expr: Input expression to be translated
63+
/// \param ns: Namespace for type lookups
64+
/// \return Semantically equivalent expression
65+
exprt lower_ctz(const count_trailing_zeros_exprt &expr, const namespacet &ns);
66+
6067
#endif /* CPROVER_SOLVERS_LOWERING_EXPR_LOWERING_H */

src/solvers/smt2/smt2_conv.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1992,6 +1992,11 @@ void smt2_convt::convert_expr(const exprt &expr)
19921992
exprt lowered = lower_clz(to_count_leading_zeros_expr(expr), ns);
19931993
convert_expr(lowered);
19941994
}
1995+
else if(expr.id() == ID_count_trailing_zeros)
1996+
{
1997+
exprt lowered = lower_ctz(to_count_trailing_zeros_expr(expr), ns);
1998+
convert_expr(lowered);
1999+
}
19952000
else
19962001
INVARIANT_WITH_DIAGNOSTICS(
19972002
false,

0 commit comments

Comments
 (0)