Skip to content

Commit a9c9588

Browse files
committed
New expression: count_leading_zeros_exprt
Rather than ad-hoc handling __builtin_clz and __lzcnt (and their variants) in the C front-end, make counting-leading-zeros available across the code base.
1 parent cf88f82 commit a9c9588

File tree

16 files changed

+180
-129
lines changed

16 files changed

+180
-129
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
int main()
2+
{
3+
#ifdef __GNUC__
4+
_Static_assert(
5+
__builtin_clz(0xffU) == 8 * sizeof(unsigned) - 8,
6+
"GCC/Clang compile-time constant");
7+
#endif
8+
9+
return 0;
10+
}

regression/cbmc-library/__builtin_clz-02/main.c

Lines changed: 0 additions & 12 deletions
This file was deleted.

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2689,27 +2689,22 @@ exprt c_typecheck_baset::do_special_functions(
26892689
throw 0;
26902690
}
26912691

2692-
side_effect_expr_function_callt try_constant{expr};
2693-
typecheck_function_call_arguments(try_constant);
2694-
exprt argument = try_constant.arguments().front();
2695-
simplify(argument, *this);
2696-
const auto int_constant = numeric_cast<mp_integer>(argument);
2692+
typecheck_function_call_arguments(expr);
2693+
2694+
exprt clz =
2695+
count_leading_zeros_exprt{expr.arguments().front(),
2696+
has_prefix(id2string(identifier), "__lzcnt"),
2697+
expr.type()};
2698+
clz.add_source_location() = source_location;
26972699

26982700
if(
2699-
!int_constant.has_value() || *int_constant == 0 ||
2700-
argument.type().id() != ID_unsignedbv)
2701+
config.ansi_c.mode == configt::ansi_ct::flavourt::GCC ||
2702+
config.ansi_c.mode == configt::ansi_ct::flavourt::CLANG)
27012703
{
2702-
return nil_exprt{};
2704+
simplify(clz, *this);
27032705
}
27042706

2705-
const std::string binary_value = integer2binary(
2706-
*int_constant, to_unsignedbv_type(argument.type()).get_width());
2707-
std::size_t n_leading_zeros = binary_value.find('1');
2708-
CHECK_RETURN(n_leading_zeros != std::string::npos);
2709-
2710-
return from_integer(
2711-
n_leading_zeros,
2712-
to_code_type(try_constant.function().type()).return_type());
2707+
return clz;
27132708
}
27142709
else if(identifier==CPROVER_PREFIX "equal")
27152710
{

src/ansi-c/library/gcc.c

Lines changed: 0 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -33,64 +33,6 @@ inline void __sync_synchronize(void)
3333
#endif
3434
}
3535

36-
/* FUNCTION: __builtin_clz */
37-
38-
int __builtin_popcount(unsigned int);
39-
40-
inline int __builtin_clz(unsigned int x)
41-
{
42-
__CPROVER_precondition(x != 0, "__builtin_clz(0) is undefined");
43-
44-
x = x | (x >> 1);
45-
x = x | (x >> 2);
46-
x = x | (x >> 4);
47-
x = x | (x >> 8);
48-
if(sizeof(x) >= 4)
49-
x = x | (x >> 16);
50-
51-
return __builtin_popcount(~x);
52-
}
53-
54-
/* FUNCTION: __builtin_clzl */
55-
56-
int __builtin_popcountl(unsigned long int);
57-
58-
inline int __builtin_clzl(unsigned long int x)
59-
{
60-
__CPROVER_precondition(x != 0, "__builtin_clzl(0) is undefined");
61-
62-
x = x | (x >> 1);
63-
x = x | (x >> 2);
64-
x = x | (x >> 4);
65-
x = x | (x >> 8);
66-
if(sizeof(x) >= 4)
67-
x = x | (x >> 16);
68-
if(sizeof(x) >= 8)
69-
x = x | (x >> 32);
70-
71-
return __builtin_popcountl(~x);
72-
}
73-
74-
/* FUNCTION: __builtin_clzll */
75-
76-
int __builtin_popcountll(unsigned long long int);
77-
78-
inline int __builtin_clzll(unsigned long long int x)
79-
{
80-
__CPROVER_precondition(x != 0, "__builtin_clzll(0) is undefined");
81-
82-
x = x | (x >> 1);
83-
x = x | (x >> 2);
84-
x = x | (x >> 4);
85-
x = x | (x >> 8);
86-
if(sizeof(x) >= 4)
87-
x = x | (x >> 16);
88-
if(sizeof(x) >= 8)
89-
x = x | (x >> 32);
90-
91-
return __builtin_popcountll(~x);
92-
}
93-
9436
/* FUNCTION: __builtin_ffs */
9537

9638
int __builtin_clz(unsigned int x);

src/ansi-c/library/windows.c

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -51,46 +51,3 @@ inline HANDLE CreateThread(
5151
return handle;
5252
}
5353
#endif
54-
55-
/* FUNCTION: __lzcnt16 */
56-
57-
#ifdef _MSC_VER
58-
int __builtin_clz(unsigned int x);
59-
60-
unsigned short __lzcnt16(unsigned short value)
61-
{
62-
if(value == 0)
63-
return 16;
64-
65-
return __builtin_clz(value) -
66-
(sizeof(unsigned int) - sizeof(unsigned short)) * 8;
67-
}
68-
#endif
69-
70-
/* FUNCTION: __lzcnt */
71-
72-
#ifdef _MSC_VER
73-
int __builtin_clz(unsigned int x);
74-
75-
unsigned int __lzcnt(unsigned int value)
76-
{
77-
if(value == 0)
78-
return 32;
79-
80-
return __builtin_clz(value);
81-
}
82-
#endif
83-
84-
/* FUNCTION: __lzcnt64 */
85-
86-
#ifdef _MSC_VER
87-
int __builtin_clzll(unsigned long long x);
88-
89-
unsigned __int64 __lzcnt64(unsigned __int64 value)
90-
{
91-
if(value == 0)
92-
return 64;
93-
94-
return __builtin_clzll(value);
95-
}
96-
#endif

src/goto-programs/goto_clean_expr.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ Author: Daniel Kroening, [email protected]
1111

1212
#include "goto_convert_class.h"
1313

14+
#include <util/arith_tools.h>
15+
#include <util/bitvector_expr.h>
1416
#include <util/cprover_prefix.h>
1517
#include <util/exception_utils.h>
1618
#include <util/expr_util.h>
@@ -100,6 +102,13 @@ bool goto_convertt::needs_cleaning(const exprt &expr)
100102
if(expr.id()==ID_forall || expr.id()==ID_exists)
101103
return false;
102104

105+
if(
106+
expr.id() == ID_count_leading_zeros &&
107+
!to_count_leading_zeros_expr(expr).zero_permitted())
108+
{
109+
return true;
110+
}
111+
103112
forall_operands(it, expr)
104113
if(needs_cleaning(*it))
105114
return true;
@@ -429,6 +438,22 @@ void goto_convertt::clean_expr(
429438
expr.operands().size() == 1, "ID_compound_literal has a single operand");
430439
expr = to_unary_expr(expr).op();
431440
}
441+
else if(expr.id() == ID_count_leading_zeros)
442+
{
443+
count_leading_zeros_exprt &clz = to_count_leading_zeros_expr(expr);
444+
if(!clz.zero_permitted())
445+
{
446+
clz.zero_permitted(true);
447+
const source_locationt source_location = clz.source_location();
448+
449+
exprt nondet = side_effect_expr_nondett{expr.type(), source_location};
450+
remove_side_effect(to_side_effect_expr(nondet), dest, mode, true, false);
451+
452+
expr = if_exprt{
453+
equal_exprt{clz.op(), from_integer(0, clz.op().type())}, nondet, clz};
454+
expr.add_source_location() = source_location;
455+
}
456+
}
432457
}
433458

434459
void goto_convertt::clean_expr_address_of(

src/solvers/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ SRC = $(BOOLEFORCE_SRC) \
140140
floatbv/float_utils.cpp \
141141
floatbv/float_approximation.cpp \
142142
lowering/byte_operators.cpp \
143+
lowering/count_leading_zeros.cpp \
143144
lowering/functions.cpp \
144145
lowering/popcount.cpp \
145146
bdd/miniBDD/miniBDD.cpp \

src/solvers/flattening/boolbv.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,8 @@ bvt boolbvt::convert_bitvector(const exprt &expr)
228228
return convert_power(to_binary_expr(expr));
229229
else if(expr.id() == ID_popcount)
230230
return convert_bv(lower_popcount(to_popcount_expr(expr), ns));
231+
else if(expr.id() == ID_count_leading_zeros)
232+
return convert_bv(lower_clz(to_count_leading_zeros_expr(expr), ns));
231233

232234
return conversion_failed(expr);
233235
}

src/solvers/lowering/expr_lowering.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Author: Michael Tautschnig
1313

1414
class byte_extract_exprt;
1515
class byte_update_exprt;
16+
class count_leading_zeros_exprt;
1617
class namespacet;
1718
class popcount_exprt;
1819

@@ -50,4 +51,10 @@ bool has_byte_operator(const exprt &src);
5051
/// \return Semantically equivalent expression
5152
exprt lower_popcount(const popcount_exprt &expr, const namespacet &ns);
5253

54+
/// Lower a count_leading_zeros_exprt to arithmetic and logic expressions.
55+
/// \param expr: Input expression to be translated
56+
/// \param ns: Namespace for type lookups
57+
/// \return Semantically equivalent expression
58+
exprt lower_clz(const count_leading_zeros_exprt &expr, const namespacet &ns);
59+
5360
#endif /* CPROVER_SOLVERS_LOWERING_EXPR_LOWERING_H */

src/solvers/smt2/smt2_conv.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1987,6 +1987,11 @@ void smt2_convt::convert_expr(const exprt &expr)
19871987
exprt lowered = lower_popcount(to_popcount_expr(expr), ns);
19881988
convert_expr(lowered);
19891989
}
1990+
else if(expr.id() == ID_count_leading_zeros)
1991+
{
1992+
exprt lowered = lower_clz(to_count_leading_zeros_expr(expr), ns);
1993+
convert_expr(lowered);
1994+
}
19901995
else
19911996
INVARIANT_WITH_DIAGNOSTICS(
19921997
false,

src/util/bitvector_expr.h

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,4 +677,92 @@ inline popcount_exprt &to_popcount_expr(exprt &expr)
677677
return ret;
678678
}
679679

680+
/// \brief The count leading zeros (counting the number of zero bits starting
681+
/// from the most-significant bit) expression. When \c zero_permitted is set to
682+
/// false, the result is non-deterministic when the operand is evaluates to
683+
/// zero.
684+
class count_leading_zeros_exprt : public unary_exprt
685+
{
686+
public:
687+
count_leading_zeros_exprt(exprt _op, bool _zero_permitted, typet _type)
688+
: unary_exprt(ID_count_leading_zeros, std::move(_op), std::move(_type))
689+
{
690+
zero_permitted(_zero_permitted);
691+
}
692+
693+
explicit count_leading_zeros_exprt(const exprt &_op)
694+
: count_leading_zeros_exprt(_op, true, _op.type())
695+
{
696+
}
697+
698+
bool zero_permitted() const
699+
{
700+
return get_bool(ID_zero_permitted);
701+
}
702+
703+
void zero_permitted(bool value)
704+
{
705+
set(ID_zero_permitted, value);
706+
}
707+
708+
static void check(
709+
const exprt &expr,
710+
const validation_modet vm = validation_modet::INVARIANT)
711+
{
712+
DATA_CHECK(
713+
vm,
714+
expr.operands().size() == 1,
715+
"unary expression must have a single operand");
716+
DATA_CHECK(
717+
vm,
718+
can_cast_type<bitvector_typet>(to_unary_expr(expr).op().type()),
719+
"operand must be of bitvector type");
720+
}
721+
722+
static void validate(
723+
const exprt &expr,
724+
const namespacet &,
725+
const validation_modet vm = validation_modet::INVARIANT)
726+
{
727+
check(expr, vm);
728+
}
729+
};
730+
731+
template <>
732+
inline bool can_cast_expr<count_leading_zeros_exprt>(const exprt &base)
733+
{
734+
return base.id() == ID_count_leading_zeros;
735+
}
736+
737+
inline void validate_expr(const count_leading_zeros_exprt &value)
738+
{
739+
validate_operands(value, 1, "count_leading_zeros must have one operand");
740+
}
741+
742+
/// \brief Cast an exprt to a \ref count_leading_zeros_exprt
743+
///
744+
/// \a expr must be known to be \ref count_leading_zeros_exprt.
745+
///
746+
/// \param expr: Source expression
747+
/// \return Object of type \ref count_leading_zeros_exprt
748+
inline const count_leading_zeros_exprt &
749+
to_count_leading_zeros_expr(const exprt &expr)
750+
{
751+
PRECONDITION(expr.id() == ID_count_leading_zeros);
752+
const count_leading_zeros_exprt &ret =
753+
static_cast<const count_leading_zeros_exprt &>(expr);
754+
validate_expr(ret);
755+
return ret;
756+
}
757+
758+
/// \copydoc to_count_leading_zeros_expr(const exprt &)
759+
inline count_leading_zeros_exprt &to_count_leading_zeros_expr(exprt &expr)
760+
{
761+
PRECONDITION(expr.id() == ID_count_leading_zeros);
762+
count_leading_zeros_exprt &ret =
763+
static_cast<count_leading_zeros_exprt &>(expr);
764+
validate_expr(ret);
765+
return ret;
766+
}
767+
680768
#endif // CPROVER_UTIL_BITVECTOR_EXPR_H

src/util/irep_ids.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,8 @@ IREP_ID_ONE(statement_list_instructions)
841841
IREP_ID_ONE(max)
842842
IREP_ID_ONE(min)
843843
IREP_ID_ONE(constant_interval)
844+
IREP_ID_ONE(count_leading_zeros)
845+
IREP_ID_ONE(zero_permitted)
844846

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

0 commit comments

Comments
 (0)