Skip to content

Commit e13e360

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 8be381c commit e13e360

File tree

13 files changed

+163
-61
lines changed

13 files changed

+163
-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
@@ -3124,6 +3124,24 @@ exprt c_typecheck_baset::do_special_functions(
31243124

31253125
return std::move(ctz);
31263126
}
3127+
else if(
3128+
identifier == "__builtin_ffs" || identifier == "__builtin_ffsl" ||
3129+
identifier == "__builtin_ffsll")
3130+
{
3131+
if(expr.arguments().size() != 1)
3132+
{
3133+
error().source_location = f_op.source_location();
3134+
error() << identifier << " expects one operand" << eom;
3135+
throw 0;
3136+
}
3137+
3138+
typecheck_function_call_arguments(expr);
3139+
3140+
find_first_set_exprt ffs{expr.arguments().front(), expr.type()};
3141+
ffs.add_source_location() = source_location;
3142+
3143+
return std::move(ffs);
3144+
}
31273145
else if(identifier==CPROVER_PREFIX "equal")
31283146
{
31293147
if(expr.arguments().size()!=2)

src/ansi-c/expr2c.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3987,6 +3987,7 @@ optionalt<std::string> expr2ct::convert_function(const exprt &src)
39873987
{ID_dynamic_object, "DYNAMIC_OBJECT"},
39883988
{ID_live_object, "LIVE_OBJECT"},
39893989
{ID_writeable_object, "WRITEABLE_OBJECT"},
3990+
{ID_find_first_set, "__builtin_ffs"},
39903991
{ID_floatbv_div, "FLOAT/"},
39913992
{ID_floatbv_minus, "FLOAT-"},
39923993
{ID_floatbv_mult, "FLOAT*"},

src/ansi-c/library/gcc.c

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -45,57 +45,6 @@ void __sync_synchronize(void)
4545
#endif
4646
}
4747

48-
/* FUNCTION: __builtin_ffs */
49-
50-
int __builtin_clz(unsigned int x);
51-
52-
int __builtin_ffs(int x)
53-
{
54-
if(x == 0)
55-
return 0;
56-
57-
#pragma CPROVER check push
58-
#pragma CPROVER check disable "conversion"
59-
unsigned int u = (unsigned int)x;
60-
#pragma CPROVER check pop
61-
62-
return sizeof(int) * 8 - __builtin_clz(u & ~(u - 1));
63-
}
64-
65-
/* FUNCTION: __builtin_ffsl */
66-
67-
int __builtin_clzl(unsigned long x);
68-
69-
int __builtin_ffsl(long x)
70-
{
71-
if(x == 0)
72-
return 0;
73-
74-
#pragma CPROVER check push
75-
#pragma CPROVER check disable "conversion"
76-
unsigned long u = (unsigned long)x;
77-
#pragma CPROVER check pop
78-
79-
return sizeof(long) * 8 - __builtin_clzl(u & ~(u - 1));
80-
}
81-
82-
/* FUNCTION: __builtin_ffsll */
83-
84-
int __builtin_clzll(unsigned long long x);
85-
86-
int __builtin_ffsll(long long x)
87-
{
88-
if(x == 0)
89-
return 0;
90-
91-
#pragma CPROVER check push
92-
#pragma CPROVER check disable "conversion"
93-
unsigned long long u = (unsigned long long)x;
94-
#pragma CPROVER check pop
95-
96-
return sizeof(long long) * 8 - __builtin_clzll(u & ~(u - 1));
97-
}
98-
9948
/* FUNCTION: __atomic_test_and_set */
10049

10150
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
@@ -240,6 +240,8 @@ bvt boolbvt::convert_bitvector(const exprt &expr)
240240
{
241241
return convert_overflow_result(*overflow_with_result);
242242
}
243+
else if(expr.id() == ID_find_first_set)
244+
return convert_bv(simplify_expr(to_find_first_set_expr(expr).lower(), ns));
243245

244246
return conversion_failed(expr);
245247
}

src/solvers/smt2/smt2_conv.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2301,6 +2301,10 @@ void smt2_convt::convert_expr(const exprt &expr)
23012301
{
23022302
convert_expr(simplify_expr(to_count_trailing_zeros_expr(expr).lower(), ns));
23032303
}
2304+
else if(expr.id() == ID_find_first_set)
2305+
{
2306+
convert_expr(simplify_expr(to_find_first_set_expr(expr).lower(), ns));
2307+
}
23042308
else if(expr.id() == ID_empty_union)
23052309
{
23062310
out << "()";

src/util/bitvector_expr.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,3 +209,20 @@ exprt mult_overflow_exprt::lower() const
209209
typecast_exprt{typecast_exprt{exact_result, lhs().type()}, ssize_type},
210210
exact_result};
211211
}
212+
213+
exprt find_first_set_exprt::lower() const
214+
{
215+
exprt x = op();
216+
const auto int_width = to_bitvector_type(x.type()).get_width();
217+
CHECK_RETURN(int_width >= 1);
218+
219+
// bitwidth(x) - clz(x & ~((unsigned)x - 1));
220+
const unsignedbv_typet ut{int_width};
221+
minus_exprt minus_one{
222+
typecast_exprt::conditional_cast(x, ut), from_integer(1, ut)};
223+
count_leading_zeros_exprt clz{bitand_exprt{
224+
x, bitnot_exprt{typecast_exprt::conditional_cast(minus_one, x.type())}}};
225+
minus_exprt result{from_integer(int_width, x.type()), clz.lower()};
226+
227+
return typecast_exprt::conditional_cast(result, type());
228+
}

src/util/bitvector_expr.h

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1427,4 +1427,81 @@ inline overflow_result_exprt &to_overflow_result_expr(exprt &expr)
14271427
return ret;
14281428
}
14291429

1430+
/// \brief Returns one plus the index of the least-significant one bit, or zero
1431+
/// if the operand is zero.
1432+
class find_first_set_exprt : public unary_exprt
1433+
{
1434+
public:
1435+
find_first_set_exprt(exprt _op, typet _type)
1436+
: unary_exprt(ID_find_first_set, std::move(_op), std::move(_type))
1437+
{
1438+
}
1439+
1440+
explicit find_first_set_exprt(const exprt &_op)
1441+
: find_first_set_exprt(_op, _op.type())
1442+
{
1443+
}
1444+
1445+
static void check(
1446+
const exprt &expr,
1447+
const validation_modet vm = validation_modet::INVARIANT)
1448+
{
1449+
DATA_CHECK(
1450+
vm,
1451+
expr.operands().size() == 1,
1452+
"unary expression must have a single operand");
1453+
DATA_CHECK(
1454+
vm,
1455+
can_cast_type<bitvector_typet>(to_unary_expr(expr).op().type()),
1456+
"operand must be of bitvector type");
1457+
}
1458+
1459+
static void validate(
1460+
const exprt &expr,
1461+
const namespacet &,
1462+
const validation_modet vm = validation_modet::INVARIANT)
1463+
{
1464+
check(expr, vm);
1465+
}
1466+
1467+
/// Lower a find_first_set_exprt to arithmetic and logic expressions.
1468+
/// \return Semantically equivalent expression
1469+
exprt lower() const;
1470+
};
1471+
1472+
template <>
1473+
inline bool can_cast_expr<find_first_set_exprt>(const exprt &base)
1474+
{
1475+
return base.id() == ID_find_first_set;
1476+
}
1477+
1478+
inline void validate_expr(const find_first_set_exprt &value)
1479+
{
1480+
validate_operands(value, 1, "find_first_set must have one operand");
1481+
}
1482+
1483+
/// \brief Cast an exprt to a \ref find_first_set_exprt
1484+
///
1485+
/// \a expr must be known to be \ref find_first_set_exprt.
1486+
///
1487+
/// \param expr: Source expression
1488+
/// \return Object of type \ref find_first_set_exprt
1489+
inline const find_first_set_exprt &to_find_first_set_expr(const exprt &expr)
1490+
{
1491+
PRECONDITION(expr.id() == ID_find_first_set);
1492+
const find_first_set_exprt &ret =
1493+
static_cast<const find_first_set_exprt &>(expr);
1494+
validate_expr(ret);
1495+
return ret;
1496+
}
1497+
1498+
/// \copydoc to_find_first_set_expr(const exprt &)
1499+
inline find_first_set_exprt &to_find_first_set_expr(exprt &expr)
1500+
{
1501+
PRECONDITION(expr.id() == ID_find_first_set);
1502+
find_first_set_exprt &ret = static_cast<find_first_set_exprt &>(expr);
1503+
validate_expr(ret);
1504+
return ret;
1505+
}
1506+
14301507
#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
@@ -150,6 +150,8 @@ static std::ostream &format_rec(std::ostream &os, const unary_exprt &src)
150150
os << "clz";
151151
else if(src.id() == ID_count_trailing_zeros)
152152
os << "ctz";
153+
else if(src.id() == ID_find_first_set)
154+
os << "ffs";
153155
else
154156
return os << src.pretty();
155157

src/util/irep_ids.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,7 @@ IREP_ID_TWO(vector_gt, vector->)
868868
IREP_ID_TWO(vector_lt, vector-<)
869869
IREP_ID_ONE(shuffle_vector)
870870
IREP_ID_ONE(count_trailing_zeros)
871+
IREP_ID_ONE(find_first_set)
871872
IREP_ID_ONE(empty_union)
872873
IREP_ID_ONE(bitreverse)
873874
IREP_ID_ONE(saturating_minus)

src/util/simplify_expr.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,27 @@ simplify_exprt::simplify_ctz(const count_trailing_zeros_exprt &expr)
197197
return from_integer(n_trailing_zeros, expr.type());
198198
}
199199

200+
simplify_exprt::resultt<>
201+
simplify_exprt::simplify_ffs(const find_first_set_exprt &expr)
202+
{
203+
const auto const_bits_opt = expr2bits(
204+
expr.op(),
205+
config.ansi_c.endianness == configt::ansi_ct::endiannesst::IS_LITTLE_ENDIAN,
206+
ns);
207+
208+
if(!const_bits_opt.has_value())
209+
return unchanged(expr);
210+
211+
// expr2bits generates a bit string starting with the least-significant bit
212+
std::size_t first_one_bit = const_bits_opt->find('1');
213+
if(first_one_bit == std::string::npos)
214+
first_one_bit = 0;
215+
else
216+
++first_one_bit;
217+
218+
return from_integer(first_one_bit, expr.type());
219+
}
220+
200221
/// Simplify String.endsWith function when arguments are constant
201222
/// \param expr: the expression to simplify
202223
/// \param ns: namespace
@@ -2758,6 +2779,10 @@ simplify_exprt::resultt<> simplify_exprt::simplify_node(exprt node)
27582779
{
27592780
r = simplify_ctz(to_count_trailing_zeros_expr(expr));
27602781
}
2782+
else if(expr.id() == ID_find_first_set)
2783+
{
2784+
r = simplify_ffs(to_find_first_set_expr(expr));
2785+
}
27612786
else if(expr.id() == ID_function_application)
27622787
{
27632788
r = simplify_function_application(to_function_application_expr(expr));

src/util/simplify_expr_class.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class div_exprt;
4545
class exprt;
4646
class extractbit_exprt;
4747
class extractbits_exprt;
48+
class find_first_set_exprt;
4849
class floatbv_typecast_exprt;
4950
class function_application_exprt;
5051
class ieee_float_op_exprt;
@@ -224,6 +225,9 @@ class simplify_exprt
224225
/// Try to simplify bit-reversing to a constant expression.
225226
NODISCARD resultt<> simplify_bitreverse(const bitreverse_exprt &);
226227

228+
/// Try to simplify find-first-set to a constant expression.
229+
NODISCARD resultt<> simplify_ffs(const find_first_set_exprt &);
230+
227231
// auxiliary
228232
bool simplify_if_implies(
229233
exprt &expr, const exprt &cond, bool truth, bool &new_truth);

0 commit comments

Comments
 (0)