Skip to content

Commit 5eff777

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 abba3d7 commit 5eff777

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
@@ -2727,6 +2727,24 @@ exprt c_typecheck_baset::do_special_functions(
27272727

27282728
return std::move(clz);
27292729
}
2730+
else if(
2731+
identifier == "__builtin_ffs" || identifier == "__builtin_ffsl" ||
2732+
identifier == "__builtin_ffsll")
2733+
{
2734+
if(expr.arguments().size() != 1)
2735+
{
2736+
error().source_location = f_op.source_location();
2737+
error() << identifier << " expects one operand" << eom;
2738+
throw 0;
2739+
}
2740+
2741+
typecheck_function_call_arguments(expr);
2742+
2743+
find_first_set_exprt ffs{expr.arguments().front(), expr.type()};
2744+
ffs.add_source_location() = source_location;
2745+
2746+
return std::move(ffs);
2747+
}
27302748
else if(identifier==CPROVER_PREFIX "equal")
27312749
{
27322750
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
@@ -2006,6 +2006,10 @@ void smt2_convt::convert_expr(const exprt &expr)
20062006
{
20072007
convert_expr(simplify_expr(to_count_leading_zeros_expr(expr).lower(), ns));
20082008
}
2009+
else if(expr.id() == ID_find_first_set)
2010+
{
2011+
convert_expr(simplify_expr(to_find_first_set_expr(expr).lower(), ns));
2012+
}
20092013
else
20102014
INVARIANT_WITH_DIAGNOSTICS(
20112015
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

src/util/irep_ids.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,7 @@ IREP_ID_TWO(vector_le, vector-<=)
850850
IREP_ID_TWO(vector_gt, vector->)
851851
IREP_ID_TWO(vector_lt, vector-<)
852852
IREP_ID_ONE(shuffle_vector)
853+
IREP_ID_ONE(find_first_set)
853854

854855
// Projects depending on this code base that wish to extend the list of
855856
// available ids should provide a file local_irep_ids.def in their source tree

src/util/simplify_expr.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,25 @@ simplify_exprt::simplify_clz(const count_leading_zeros_exprt &expr)
172172
return from_integer(n_leading_zeros, expr.type());
173173
}
174174

175+
simplify_exprt::resultt<>
176+
simplify_exprt::simplify_ffs(const find_first_set_exprt &expr)
177+
{
178+
const auto const_bits_opt = expr2bits(
179+
expr.op(), byte_extract_id() == ID_byte_extract_little_endian, ns);
180+
181+
if(!const_bits_opt.has_value())
182+
return unchanged(expr);
183+
184+
// expr2bits generates a bit string starting with the least-significant bit
185+
std::size_t first_one_bit = const_bits_opt->find('1');
186+
if(first_one_bit == std::string::npos)
187+
first_one_bit = 0;
188+
else
189+
++first_one_bit;
190+
191+
return from_integer(first_one_bit, expr.type());
192+
}
193+
175194
/// Simplify String.endsWith function when arguments are constant
176195
/// \param expr: the expression to simplify
177196
/// \param ns: namespace
@@ -2421,6 +2440,10 @@ simplify_exprt::resultt<> simplify_exprt::simplify_node(exprt node)
24212440
{
24222441
r = simplify_clz(to_count_leading_zeros_expr(expr));
24232442
}
2443+
else if(expr.id() == ID_find_first_set)
2444+
{
2445+
r = simplify_ffs(to_find_first_set_expr(expr));
2446+
}
24242447
else if(expr.id() == ID_function_application)
24252448
{
24262449
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
@@ -43,6 +43,7 @@ class div_exprt;
4343
class exprt;
4444
class extractbit_exprt;
4545
class extractbits_exprt;
46+
class find_first_set_exprt;
4647
class floatbv_typecast_exprt;
4748
class function_application_exprt;
4849
class ieee_float_op_exprt;
@@ -206,6 +207,9 @@ class simplify_exprt
206207
/// Try to simplify count-leading-zeros to a constant expression.
207208
NODISCARD resultt<> simplify_clz(const count_leading_zeros_exprt &);
208209

210+
/// Try to simplify find-first-set to a constant expression.
211+
NODISCARD resultt<> simplify_ffs(const find_first_set_exprt &);
212+
209213
// auxiliary
210214
bool simplify_if_implies(
211215
exprt &expr, const exprt &cond, bool truth, bool &new_truth);

0 commit comments

Comments
 (0)