Skip to content

bitreverse_exprt: Expression to reverse the order of bits #6581

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions regression/cbmc/clang_builtins/bitreverse-typeconflict.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
unsigned char __builtin_bitreverse8(char, char);

int main()
{
return __builtin_bitreverse8(1, 2);
}
7 changes: 7 additions & 0 deletions regression/cbmc/clang_builtins/bitreverse-typeconflict.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CORE
bitreverse-typeconflict.c
file bitreverse-typeconflict.c line 5 function main: error: __builtin_bitreverse8 expects one operand
^EXIT=6$
^SIGNAL=0$
--
^warning: ignoring
64 changes: 64 additions & 0 deletions regression/cbmc/clang_builtins/bitreverse.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#include <assert.h>
#include <stdint.h>

#define test_bit_reverse(W) \
uint##W##_t test_bit_reverse##W(uint##W##_t v) \
{ \
uint##W##_t result = 0; \
int i; \
for(i = 0; i < W; i++) \
{ \
if(v & (1ULL << i)) \
result |= 1ULL << (W - i - 1); \
} \
return result; \
} \
int dummy_for_semicolon##W

test_bit_reverse(8);
test_bit_reverse(16);
test_bit_reverse(32);
test_bit_reverse(64);

#ifndef __clang__
unsigned char __builtin_bitreverse8(unsigned char);
unsigned short __builtin_bitreverse16(unsigned short);
unsigned int __builtin_bitreverse32(unsigned int);
unsigned long long __builtin_bitreverse64(unsigned long long);
#endif

void check_8(void)
{
uint8_t op;
assert(__builtin_bitreverse8(op) == test_bit_reverse8(op));
assert(__builtin_bitreverse8(1) == 0x80);
}

void check_16(void)
{
uint16_t op;
assert(__builtin_bitreverse16(op) == test_bit_reverse16(op));
assert(__builtin_bitreverse16(1) == 0x8000);
}

void check_32(void)
{
uint32_t op;
assert(__builtin_bitreverse32(op) == test_bit_reverse32(op));
assert(__builtin_bitreverse32(1) == 0x80000000);
}

void check_64(void)
{
uint64_t op;
assert(__builtin_bitreverse64(op) == test_bit_reverse64(op));
assert(__builtin_bitreverse64(1) == 0x8000000000000000ULL);
}

int main(void)
{
check_8();
check_16();
check_32();
check_64();
}
8 changes: 8 additions & 0 deletions regression/cbmc/clang_builtins/bitreverse.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CORE thorough-paths
bitreverse.c

^VERIFICATION SUCCESSFUL$
^EXIT=0$
^SIGNAL=0$
--
^warning: ignoring
22 changes: 22 additions & 0 deletions src/ansi-c/c_typecheck_expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3204,6 +3204,28 @@ exprt c_typecheck_baset::do_special_functions(
{
return typecheck_builtin_overflow(expr, ID_mult);
}
else if(
identifier == "__builtin_bitreverse8" ||
identifier == "__builtin_bitreverse16" ||
identifier == "__builtin_bitreverse32" ||
identifier == "__builtin_bitreverse64")
{
// clang only
if(expr.arguments().size() != 1)
{
std::ostringstream error_message;
error_message << expr.source_location().as_string()
<< ": error: " << identifier << " expects one operand";
throw invalid_source_file_exceptiont{error_message.str()};
}

typecheck_function_call_arguments(expr);

bitreverse_exprt bitreverse{expr.arguments()[0]};
bitreverse.add_source_location() = source_location;

return std::move(bitreverse);
}
else
return nil_exprt();
// NOLINTNEXTLINE(readability/fn_size)
Expand Down
5 changes: 5 additions & 0 deletions src/ansi-c/clang_builtin_headers.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ void __builtin_nontemporal_load();

int __builtin_flt_rounds(void);

unsigned char __builtin_bitreverse8(unsigned char);
unsigned short __builtin_bitreverse16(unsigned short);
unsigned int __builtin_bitreverse32(unsigned int);
unsigned long long __builtin_bitreverse64(unsigned long long);

unsigned char __builtin_rotateleft8(unsigned char, unsigned char);
unsigned short __builtin_rotateleft16(unsigned short, unsigned short);
unsigned int __builtin_rotateleft32(unsigned int, unsigned int);
Expand Down
19 changes: 19 additions & 0 deletions src/ansi-c/expr2c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3496,6 +3496,22 @@ std::string expr2ct::convert_conditional_target_group(const exprt &src)
return dest;
}

std::string expr2ct::convert_bitreverse(const bitreverse_exprt &src)
{
if(auto type_ptr = type_try_dynamic_cast<unsignedbv_typet>(src.type()))
{
const std::size_t width = type_ptr->get_width();
if(width == 8 || width == 16 || width == 32 || width == 64)
{
return convert_function(
src, "__builtin_bitreverse" + std::to_string(width));
}
}

unsigned precedence;
return convert_norep(src, precedence);
}

std::string expr2ct::convert_with_precedence(
const exprt &src,
unsigned &precedence)
Expand Down Expand Up @@ -3904,6 +3920,9 @@ std::string expr2ct::convert_with_precedence(
return convert_conditional_target_group(src);
}

else if(src.id() == ID_bitreverse)
return convert_bitreverse(to_bitreverse_expr(src));

auto function_string_opt = convert_function(src);
if(function_string_opt.has_value())
return *function_string_opt;
Expand Down
1 change: 1 addition & 0 deletions src/ansi-c/expr2c_class.h
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ class expr2ct
bool include_padding_components);

std::string convert_conditional_target_group(const exprt &src);
std::string convert_bitreverse(const bitreverse_exprt &src);
};

#endif // CPROVER_ANSI_C_EXPR2C_CLASS_H
1 change: 1 addition & 0 deletions src/solvers/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ SRC = $(BOOLEFORCE_SRC) \
flattening/boolbv_add_sub.cpp \
flattening/boolbv_array.cpp \
flattening/boolbv_array_of.cpp \
flattening/boolbv_bitreverse.cpp \
flattening/boolbv_bitwise.cpp \
flattening/boolbv_bswap.cpp \
flattening/boolbv_bv_rel.cpp \
Expand Down
2 changes: 2 additions & 0 deletions src/solvers/flattening/boolbv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ bvt boolbvt::convert_bitvector(const exprt &expr)
return convert_bv(
simplify_expr(to_count_trailing_zeros_expr(expr).lower(), ns));
}
else if(expr.id() == ID_bitreverse)
return convert_bitreverse(to_bitreverse_expr(expr));

return conversion_failed(expr);
}
Expand Down
2 changes: 2 additions & 0 deletions src/solvers/flattening/boolbv.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Author: Daniel Kroening, [email protected]
#include "arrays.h"

class array_comprehension_exprt;
class bitreverse_exprt;
class bswap_exprt;
class byte_extract_exprt;
class byte_update_exprt;
Expand Down Expand Up @@ -192,6 +193,7 @@ class boolbvt:public arrayst
virtual bvt convert_power(const binary_exprt &expr);
virtual bvt convert_function_application(
const function_application_exprt &expr);
virtual bvt convert_bitreverse(const bitreverse_exprt &expr);

virtual exprt make_bv_expr(const typet &type, const bvt &bv);
virtual exprt make_free_bv_expr(const typet &type);
Expand Down
24 changes: 24 additions & 0 deletions src/solvers/flattening/boolbv_bitreverse.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*******************************************************************\

Module:

Author: Michael Tautschnig

\*******************************************************************/

#include "boolbv.h"

#include <util/bitvector_expr.h>

bvt boolbvt::convert_bitreverse(const bitreverse_exprt &expr)
{
const std::size_t width = boolbv_width(expr.type());
if(width == 0)
return conversion_failed(expr);

bvt bv = convert_bv(expr.op(), width);

std::reverse(bv.begin(), bv.end());

return bv;
}
4 changes: 4 additions & 0 deletions src/solvers/smt2/smt2_conv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2134,6 +2134,10 @@ void smt2_convt::convert_expr(const exprt &expr)
{
out << "()";
}
else if(expr.id() == ID_bitreverse)
{
convert_expr(simplify_expr(to_bitreverse_expr(expr).lower(), ns));
}
else
INVARIANT_WITH_DIAGNOSTICS(
false,
Expand Down
14 changes: 14 additions & 0 deletions src/util/bitvector_expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,17 @@ exprt count_trailing_zeros_exprt::lower() const

return typecast_exprt::conditional_cast(result, type());
}

exprt bitreverse_exprt::lower() const
{
const std::size_t int_width = to_bitvector_type(type()).get_width();

exprt::operandst result_bits;
result_bits.reserve(int_width);

const symbol_exprt to_reverse("to_reverse", op().type());
for(std::size_t i = 0; i < int_width; ++i)
result_bits.push_back(extractbit_exprt{to_reverse, i});

return let_exprt{to_reverse, op(), concatenation_exprt{result_bits, type()}};
}
48 changes: 48 additions & 0 deletions src/util/bitvector_expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -1008,4 +1008,52 @@ inline count_trailing_zeros_exprt &to_count_trailing_zeros_expr(exprt &expr)
return ret;
}

/// \brief Reverse the order of bits in a bit-vector.
class bitreverse_exprt : public unary_exprt
{
public:
explicit bitreverse_exprt(exprt op)
: unary_exprt(ID_bitreverse, std::move(op))
{
}

/// Lower a bitreverse_exprt to arithmetic and logic expressions.
/// \return Semantically equivalent expression
exprt lower() const;
};

template <>
inline bool can_cast_expr<bitreverse_exprt>(const exprt &base)
{
return base.id() == ID_bitreverse;
}

inline void validate_expr(const bitreverse_exprt &value)
{
validate_operands(value, 1, "Bit-wise reverse must have one operand");
}

/// \brief Cast an exprt to a \ref bitreverse_exprt
///
/// \a expr must be known to be \ref bitreverse_exprt.
///
/// \param expr: Source expression
/// \return Object of type \ref bitreverse_exprt
inline const bitreverse_exprt &to_bitreverse_expr(const exprt &expr)
{
PRECONDITION(expr.id() == ID_bitreverse);
const bitreverse_exprt &ret = static_cast<const bitreverse_exprt &>(expr);
validate_expr(ret);
return ret;
}

/// \copydoc to_bitreverse_expr(const exprt &)
inline bitreverse_exprt &to_bitreverse_expr(exprt &expr)
{
PRECONDITION(expr.id() == ID_bitreverse);
bitreverse_exprt &ret = static_cast<bitreverse_exprt &>(expr);
validate_expr(ret);
return ret;
}

#endif // CPROVER_UTIL_BITVECTOR_EXPR_H
1 change: 1 addition & 0 deletions src/util/irep_ids.def
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,7 @@ IREP_ID_TWO(vector_lt, vector-<)
IREP_ID_ONE(shuffle_vector)
IREP_ID_ONE(count_trailing_zeros)
IREP_ID_ONE(empty_union)
IREP_ID_ONE(bitreverse)

// Projects depending on this code base that wish to extend the list of
// available ids should provide a file local_irep_ids.def in their source tree
Expand Down
4 changes: 4 additions & 0 deletions src/util/simplify_expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2542,6 +2542,10 @@ simplify_exprt::resultt<> simplify_exprt::simplify_node(exprt node)
{
r = simplify_overflow_unary(to_unary_overflow_expr(expr));
}
else if(expr.id() == ID_bitreverse)
{
r = simplify_bitreverse(to_bitreverse_expr(expr));
}

if(!no_change_join_operands)
r = changed(r);
Expand Down
4 changes: 4 additions & 0 deletions src/util/simplify_expr_class.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class binary_exprt;
class binary_overflow_exprt;
class binary_relation_exprt;
class bitnot_exprt;
class bitreverse_exprt;
class bswap_exprt;
class byte_extract_exprt;
class byte_update_exprt;
Expand Down Expand Up @@ -210,6 +211,9 @@ class simplify_exprt
/// Try to simplify count-trailing-zeros to a constant expression.
NODISCARD resultt<> simplify_ctz(const count_trailing_zeros_exprt &);

/// Try to simplify bit-reversing to a constant expression.
NODISCARD resultt<> simplify_bitreverse(const bitreverse_exprt &);

// auxiliary
bool simplify_if_implies(
exprt &expr, const exprt &cond, bool truth, bool &new_truth);
Expand Down
24 changes: 24 additions & 0 deletions src/util/simplify_expr_int.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1822,3 +1822,27 @@ simplify_exprt::resultt<> simplify_exprt::simplify_inequality_rhs_is_constant(
#endif
return unchanged(expr);
}

simplify_exprt::resultt<>
simplify_exprt::simplify_bitreverse(const bitreverse_exprt &expr)
{
auto const_bits_opt = expr2bits(
expr.op(),
config.ansi_c.endianness == configt::ansi_ct::endiannesst::IS_LITTLE_ENDIAN,
ns);

if(!const_bits_opt.has_value())
return unchanged(expr);

std::reverse(const_bits_opt->begin(), const_bits_opt->end());

auto result = bits2expr(
*const_bits_opt,
expr.type(),
config.ansi_c.endianness == configt::ansi_ct::endiannesst::IS_LITTLE_ENDIAN,
ns);
if(!result.has_value())
return unchanged(expr);

return std::move(*result);
}