Skip to content

Commit 3c41387

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 0312102 commit 3c41387

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
@@ -2625,6 +2625,42 @@ exprt c_typecheck_baset::do_special_functions(
26252625
n_leading_zeros,
26262626
to_code_type(try_constant.function().type()).return_type());
26272627
}
2628+
else if(
2629+
identifier == "__builtin_ffs" || identifier == "__builtin_ffsl" ||
2630+
identifier == "__builtin_ffsll")
2631+
{
2632+
if(expr.arguments().size() != 1)
2633+
{
2634+
error().source_location = f_op.source_location();
2635+
error() << identifier << " expects one operand" << eom;
2636+
throw 0;
2637+
}
2638+
2639+
side_effect_expr_function_callt try_constant{expr};
2640+
typecheck_function_call_arguments(try_constant);
2641+
exprt argument = try_constant.arguments().front();
2642+
simplify(argument, *this);
2643+
const auto int_constant = numeric_cast<mp_integer>(argument);
2644+
2645+
if(!int_constant.has_value() || argument.type().id() != ID_signedbv)
2646+
{
2647+
return nil_exprt{};
2648+
}
2649+
else if(*int_constant == 0)
2650+
{
2651+
return from_integer(
2652+
0, to_code_type(try_constant.function().type()).return_type());
2653+
}
2654+
2655+
const std::size_t bit_width = to_signedbv_type(argument.type()).get_width();
2656+
const std::string binary_value = integer2binary(*int_constant, bit_width);
2657+
std::size_t last_one_bit = binary_value.rfind('1');
2658+
CHECK_RETURN(last_one_bit != std::string::npos);
2659+
2660+
return from_integer(
2661+
bit_width - last_one_bit,
2662+
to_code_type(try_constant.function().type()).return_type());
2663+
}
26282664
else if(identifier==CPROVER_PREFIX "equal")
26292665
{
26302666
if(expr.arguments().size()!=2)

0 commit comments

Comments
 (0)