Skip to content

Commit 71b283d

Browse files
committed
New expression: find_first_set_exprt
Rather than ad-hoc handling __builtin_ffs (and its variants) in the C front-end, make find-first-set available across the code base.
1 parent be4e768 commit 71b283d

File tree

13 files changed

+161
-61
lines changed

13 files changed

+161
-61
lines changed

regression/cbmc-library/__builtin_ffs-01/main.c renamed to regression/cbmc/__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: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2707,6 +2707,24 @@ exprt c_typecheck_baset::do_special_functions(
27072707

27082708
return std::move(clz);
27092709
}
2710+
else if(
2711+
identifier == "__builtin_ffs" || identifier == "__builtin_ffsl" ||
2712+
identifier == "__builtin_ffsll")
2713+
{
2714+
if(expr.arguments().size() != 1)
2715+
{
2716+
error().source_location = f_op.source_location();
2717+
error() << identifier << " expects one operand" << eom;
2718+
throw 0;
2719+
}
2720+
2721+
typecheck_function_call_arguments(expr);
2722+
2723+
find_first_set_exprt ffs{expr.arguments().front(), expr.type()};
2724+
ffs.add_source_location() = source_location;
2725+
2726+
return std::move(ffs);
2727+
}
27102728
else if(identifier==CPROVER_PREFIX "equal")
27112729
{
27122730
if(expr.arguments().size()!=2)

src/ansi-c/expr2c.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3823,6 +3823,7 @@ optionalt<std::string> expr2ct::convert_function(const exprt &src)
38233823
{ID_concatenation, "CONCATENATION"},
38243824
{ID_count_leading_zeros, "__builtin_clz"},
38253825
{ID_dynamic_object, "DYNAMIC_OBJECT"},
3826+
{ID_find_first_set, "__builtin_ffs"},
38263827
{ID_floatbv_div, "FLOAT/"},
38273828
{ID_floatbv_minus, "FLOAT-"},
38283829
{ID_floatbv_mult, "FLOAT*"},

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/flattening/boolbv.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,8 @@ bvt boolbvt::convert_bitvector(const exprt &expr)
234234
return convert_bv(
235235
simplify_expr(to_count_leading_zeros_expr(expr).lower(), ns));
236236
}
237+
else if(expr.id() == ID_find_first_set)
238+
return convert_bv(simplify_expr(to_find_first_set_expr(expr).lower(), ns));
237239

238240
return conversion_failed(expr);
239241
}

src/solvers/smt2/smt2_conv.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1991,6 +1991,10 @@ void smt2_convt::convert_expr(const exprt &expr)
19911991
{
19921992
convert_expr(simplify_expr(to_count_leading_zeros_expr(expr).lower(), ns));
19931993
}
1994+
else if(expr.id() == ID_find_first_set)
1995+
{
1996+
convert_expr(simplify_expr(to_find_first_set_expr(expr).lower(), ns));
1997+
}
19941998
else
19951999
INVARIANT_WITH_DIAGNOSTICS(
19962000
false,

src/util/bitvector_expr.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,20 @@ exprt count_leading_zeros_exprt::lower() const
125125
bitnot_exprt{typecast_exprt::conditional_cast(x, op().type())}, type()}
126126
.lower();
127127
}
128+
129+
exprt find_first_set_exprt::lower() const
130+
{
131+
exprt x = op();
132+
const auto int_width = to_bitvector_type(x.type()).get_width();
133+
CHECK_RETURN(int_width >= 1);
134+
135+
// bitwidth(x) - clz(x & ~((unsigned)x - 1));
136+
const unsignedbv_typet ut{int_width};
137+
minus_exprt minus_one{typecast_exprt::conditional_cast(x, ut),
138+
from_integer(1, ut)};
139+
count_leading_zeros_exprt clz{bitand_exprt{
140+
x, bitnot_exprt{typecast_exprt::conditional_cast(minus_one, x.type())}}};
141+
minus_exprt result{from_integer(int_width, x.type()), clz.lower()};
142+
143+
return typecast_exprt::conditional_cast(result, type());
144+
}

src/util/bitvector_expr.h

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -915,4 +915,81 @@ inline count_leading_zeros_exprt &to_count_leading_zeros_expr(exprt &expr)
915915
return ret;
916916
}
917917

918+
/// \brief Returns one plus the index of the least-significant one bit, or zero
919+
/// if the operand is zero.
920+
class find_first_set_exprt : public unary_exprt
921+
{
922+
public:
923+
find_first_set_exprt(exprt _op, typet _type)
924+
: unary_exprt(ID_find_first_set, std::move(_op), std::move(_type))
925+
{
926+
}
927+
928+
explicit find_first_set_exprt(const exprt &_op)
929+
: find_first_set_exprt(_op, _op.type())
930+
{
931+
}
932+
933+
static void check(
934+
const exprt &expr,
935+
const validation_modet vm = validation_modet::INVARIANT)
936+
{
937+
DATA_CHECK(
938+
vm,
939+
expr.operands().size() == 1,
940+
"unary expression must have a single operand");
941+
DATA_CHECK(
942+
vm,
943+
can_cast_type<bitvector_typet>(to_unary_expr(expr).op().type()),
944+
"operand must be of bitvector type");
945+
}
946+
947+
static void validate(
948+
const exprt &expr,
949+
const namespacet &,
950+
const validation_modet vm = validation_modet::INVARIANT)
951+
{
952+
check(expr, vm);
953+
}
954+
955+
/// Lower a find_first_set_exprt to arithmetic and logic expressions.
956+
/// \return Semantically equivalent expression
957+
exprt lower() const;
958+
};
959+
960+
template <>
961+
inline bool can_cast_expr<find_first_set_exprt>(const exprt &base)
962+
{
963+
return base.id() == ID_find_first_set;
964+
}
965+
966+
inline void validate_expr(const find_first_set_exprt &value)
967+
{
968+
validate_operands(value, 1, "find_first_set must have one operand");
969+
}
970+
971+
/// \brief Cast an exprt to a \ref find_first_set_exprt
972+
///
973+
/// \a expr must be known to be \ref find_first_set_exprt.
974+
///
975+
/// \param expr: Source expression
976+
/// \return Object of type \ref find_first_set_exprt
977+
inline const find_first_set_exprt &to_find_first_set_expr(const exprt &expr)
978+
{
979+
PRECONDITION(expr.id() == ID_find_first_set);
980+
const find_first_set_exprt &ret =
981+
static_cast<const find_first_set_exprt &>(expr);
982+
validate_expr(ret);
983+
return ret;
984+
}
985+
986+
/// \copydoc to_find_first_set_expr(const exprt &)
987+
inline find_first_set_exprt &to_find_first_set_expr(exprt &expr)
988+
{
989+
PRECONDITION(expr.id() == ID_find_first_set);
990+
find_first_set_exprt &ret = static_cast<find_first_set_exprt &>(expr);
991+
validate_expr(ret);
992+
return ret;
993+
}
994+
918995
#endif // CPROVER_UTIL_BITVECTOR_EXPR_H

src/util/format_expr.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ static std::ostream &format_rec(std::ostream &os, const unary_exprt &src)
153153
os << '-';
154154
else if(src.id() == ID_count_leading_zeros)
155155
os << "clz";
156+
else if(src.id() == ID_find_first_set)
157+
os << "ffs";
156158
else
157159
return os << src.pretty();
158160

0 commit comments

Comments
 (0)