Skip to content

Commit e30fcc8

Browse files
committed
Implement __builtin_ctz{,l,ll} including constant evaluation
__builtin_ctz returns the number of trailing zeros. Just like ffs and clz, evaluate application of the built-in to constants in the front-end.
1 parent 1300adb commit e30fcc8

File tree

4 files changed

+121
-0
lines changed

4 files changed

+121
-0
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#include <assert.h>
2+
#include <limits.h>
3+
4+
#ifndef __GNUC__
5+
int __builtin_ctz(int);
6+
int __builtin_ctzl(long);
7+
int __builtin_ctzll(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+
return 0;
46+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
--pointer-check --bounds-check
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2768,6 +2768,40 @@ exprt c_typecheck_baset::do_special_functions(
27682768
n_leading_zeros,
27692769
to_code_type(try_constant.function().type()).return_type());
27702770
}
2771+
else if(
2772+
identifier == "__builtin_ctz" || identifier == "__builtin_ctzl" ||
2773+
identifier == "__builtin_ctzll")
2774+
{
2775+
if(expr.arguments().size() != 1)
2776+
{
2777+
error().source_location = f_op.source_location();
2778+
error() << identifier << " expects one operand" << eom;
2779+
throw 0;
2780+
}
2781+
2782+
side_effect_expr_function_callt try_constant{expr};
2783+
typecheck_function_call_arguments(try_constant);
2784+
exprt argument = try_constant.arguments().front();
2785+
simplify(argument, *this);
2786+
const auto int_constant = numeric_cast<mp_integer>(argument);
2787+
2788+
if(
2789+
!int_constant.has_value() || *int_constant == 0 ||
2790+
argument.type().id() != ID_unsignedbv)
2791+
{
2792+
return nil_exprt{};
2793+
}
2794+
2795+
const std::size_t bit_width =
2796+
to_unsignedbv_type(argument.type()).get_width();
2797+
const std::string binary_value = integer2binary(*int_constant, bit_width);
2798+
std::size_t last_one_bit = binary_value.rfind('1');
2799+
CHECK_RETURN(last_one_bit != std::string::npos);
2800+
2801+
return from_integer(
2802+
bit_width - last_one_bit - 1,
2803+
to_code_type(try_constant.function().type()).return_type());
2804+
}
27712805
else if(
27722806
identifier == "__builtin_ffs" || identifier == "__builtin_ffsl" ||
27732807
identifier == "__builtin_ffsll")

src/ansi-c/library/gcc.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,39 @@ inline int __builtin_clzll(unsigned long long int x)
9191
return __builtin_popcountll(~x);
9292
}
9393

94+
/* FUNCTION: __builtin_ctz */
95+
96+
int __builtin_popcount(unsigned int);
97+
98+
inline int __builtin_ctz(unsigned int x)
99+
{
100+
__CPROVER_precondition(x != 0, "__builtin_ctz(0) is undefined");
101+
102+
return __builtin_popcount(x ^ (x - 1)) - 1;
103+
}
104+
105+
/* FUNCTION: __builtin_ctzl */
106+
107+
int __builtin_popcountl(unsigned long int);
108+
109+
inline int __builtin_ctzl(unsigned long int x)
110+
{
111+
__CPROVER_precondition(x != 0, "__builtin_ctzl(0) is undefined");
112+
113+
return __builtin_popcountl(x ^ (x - 1)) - 1;
114+
}
115+
116+
/* FUNCTION: __builtin_ctzll */
117+
118+
int __builtin_popcountll(unsigned long long int);
119+
120+
inline int __builtin_ctzll(unsigned long long int x)
121+
{
122+
__CPROVER_precondition(x != 0, "__builtin_ctzll(0) is undefined");
123+
124+
return __builtin_popcountll(x ^ (x - 1)) - 1;
125+
}
126+
94127
/* FUNCTION: __builtin_ffs */
95128

96129
int __builtin_clz(unsigned int x);

0 commit comments

Comments
 (0)