Skip to content

Commit 3c244a3

Browse files
committed
Evaluate __builtin_ffs{,l,ll} over constants at compile time
GCC evaluates this built-in at compile time, and the Linux kernel assumes this is the case.
1 parent 9bb29e6 commit 3c244a3

File tree

2 files changed

+48
-10
lines changed

2 files changed

+48
-10
lines changed

regression/cbmc-library/__builtin_ffs-01/main.c

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ int __builtin_ffsl(long);
77
int __builtin_ffsll(long long);
88
#endif
99

10+
#ifdef _MSC_VER
11+
# define _Static_assert(x, m) static_assert(x, m)
12+
#endif
13+
1014
int __VERIFIER_nondet_int();
11-
long __VERIFIER_nondet_long();
12-
long long __VERIFIER_nondet_long_long();
1315

1416
// http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightMultLookup
1517
static const int multiply_de_bruijn_bit_position[32] = {
@@ -18,14 +20,14 @@ static const int multiply_de_bruijn_bit_position[32] = {
1820

1921
int main()
2022
{
21-
assert(__builtin_ffs(0) == 0);
22-
assert(__builtin_ffs(-1) == 1);
23-
assert(__builtin_ffs(INT_MIN) == sizeof(int) * 8);
24-
assert(__builtin_ffsl(INT_MIN) == sizeof(int) * 8);
25-
assert(__builtin_ffsl(LONG_MIN) == sizeof(long) * 8);
26-
assert(__builtin_ffsll(INT_MIN) == sizeof(int) * 8);
27-
assert(__builtin_ffsll(LLONG_MIN) == sizeof(long long) * 8);
28-
assert(__builtin_ffs(INT_MAX) == 1);
23+
_Static_assert(__builtin_ffs(0) == 0, "");
24+
_Static_assert(__builtin_ffs(-1) == 1, "");
25+
_Static_assert(__builtin_ffs(INT_MIN) == sizeof(int) * 8, "");
26+
_Static_assert(__builtin_ffsl(INT_MIN) == sizeof(int) * 8, "");
27+
_Static_assert(__builtin_ffsl(LONG_MIN) == sizeof(long) * 8, "");
28+
_Static_assert(__builtin_ffsll(INT_MIN) == sizeof(int) * 8, "");
29+
_Static_assert(__builtin_ffsll(LLONG_MIN) == sizeof(long long) * 8, "");
30+
_Static_assert(__builtin_ffs(INT_MAX) == 1, "");
2931

3032
int i = __VERIFIER_nondet_int();
3133
__CPROVER_assume(i != 0);

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2759,6 +2759,42 @@ exprt c_typecheck_baset::do_special_functions(
27592759
n_leading_zeros,
27602760
to_code_type(try_constant.function().type()).return_type());
27612761
}
2762+
else if(
2763+
identifier == "__builtin_ffs" || identifier == "__builtin_ffsl" ||
2764+
identifier == "__builtin_ffsll")
2765+
{
2766+
if(expr.arguments().size() != 1)
2767+
{
2768+
error().source_location = f_op.source_location();
2769+
error() << identifier << " expects one operand" << eom;
2770+
throw 0;
2771+
}
2772+
2773+
side_effect_expr_function_callt try_constant{expr};
2774+
typecheck_function_call_arguments(try_constant);
2775+
exprt argument = try_constant.arguments().front();
2776+
simplify(argument, *this);
2777+
const auto int_constant = numeric_cast<mp_integer>(argument);
2778+
2779+
if(!int_constant.has_value() || argument.type().id() != ID_signedbv)
2780+
{
2781+
return nil_exprt{};
2782+
}
2783+
else if(*int_constant == 0)
2784+
{
2785+
return from_integer(
2786+
0, to_code_type(try_constant.function().type()).return_type());
2787+
}
2788+
2789+
const std::size_t bit_width = to_signedbv_type(argument.type()).get_width();
2790+
const std::string binary_value = integer2binary(*int_constant, bit_width);
2791+
std::size_t last_one_bit = binary_value.rfind('1');
2792+
CHECK_RETURN(last_one_bit != std::string::npos);
2793+
2794+
return from_integer(
2795+
bit_width - last_one_bit,
2796+
to_code_type(try_constant.function().type()).return_type());
2797+
}
27622798
else if(identifier==CPROVER_PREFIX "equal")
27632799
{
27642800
if(expr.arguments().size()!=2)

0 commit comments

Comments
 (0)