Skip to content

Commit c43b619

Browse files
committed
Add support for __builtin_bit_cast
This is supported by Clang and GCC (though in C++-mode only) and now required for SIMD1 on macOS.
1 parent a9701a1 commit c43b619

File tree

8 files changed

+109
-0
lines changed

8 files changed

+109
-0
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#include <assert.h>
2+
#include <inttypes.h>
3+
4+
int main()
5+
{
6+
#if defined(__clang__) || defined(_GNUC_)
7+
// GCC actually only supports __builtin_bit_cast in C++ mode, but we do it for
8+
// C as well.
9+
float f = 1.5;
10+
assert((int32_t)f == 1);
11+
int32_t i = __builtin_bit_cast(int32_t, f);
12+
assert(i != 1);
13+
assert(__builtin_bit_cast(float, i) == f);
14+
#endif
15+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
4+
^VERIFICATION SUCCESSFUL$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
^warning: ignoring

src/ansi-c/c_expr.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,8 @@ exprt enum_is_in_range_exprt::lower(const namespacet &ns) const
8383

8484
return simplify_expr(disjunction(disjuncts), ns);
8585
}
86+
87+
byte_extract_exprt bit_cast_exprt::lower() const
88+
{
89+
return make_byte_extract(op(), from_integer(0, c_index_type()), type());
90+
}

src/ansi-c/c_expr.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Author: Daniel Kroening, [email protected]
1212
/// \file ansi-c/c_expr.h
1313
/// API to expression classes that are internal to the C frontend
1414

15+
#include <util/byte_operators.h>
1516
#include <util/std_code.h>
1617

1718
/// \brief Shuffle elements of one or two vectors, modelled after Clang's
@@ -370,4 +371,51 @@ inline enum_is_in_range_exprt &to_enum_is_in_range_expr(exprt &expr)
370371
return ret;
371372
}
372373

374+
/// \brief Reinterpret the bits of an expression of type `S` as an expression of
375+
/// type `T` where `S` and `T` have the same size.
376+
class bit_cast_exprt : public unary_exprt
377+
{
378+
public:
379+
bit_cast_exprt(exprt expr, typet type)
380+
: unary_exprt(ID_bit_cast, std::move(expr), std::move(type))
381+
{
382+
}
383+
384+
byte_extract_exprt lower() const;
385+
};
386+
387+
template <>
388+
inline bool can_cast_expr<bit_cast_exprt>(const exprt &base)
389+
{
390+
return base.id() == ID_bit_cast;
391+
}
392+
393+
inline void validate_expr(const bit_cast_exprt &value)
394+
{
395+
validate_operands(value, 1, "bit_cast must have one operand");
396+
}
397+
398+
/// \brief Cast an exprt to a \ref bit_cast_exprt
399+
///
400+
/// \a expr must be known to be \ref bit_cast_exprt.
401+
///
402+
/// \param expr: Source expression
403+
/// \return Object of type \ref bit_cast_exprt
404+
inline const bit_cast_exprt &to_bit_cast_expr(const exprt &expr)
405+
{
406+
PRECONDITION(expr.id() == ID_bit_cast);
407+
const bit_cast_exprt &ret = static_cast<const bit_cast_exprt &>(expr);
408+
validate_expr(ret);
409+
return ret;
410+
}
411+
412+
/// \copydoc to_bit_cast_expr(const exprt &)
413+
inline bit_cast_exprt &to_bit_cast_expr(exprt &expr)
414+
{
415+
PRECONDITION(expr.id() == ID_bit_cast);
416+
bit_cast_exprt &ret = static_cast<bit_cast_exprt &>(expr);
417+
validate_expr(ret);
418+
return ret;
419+
}
420+
373421
#endif // CPROVER_ANSI_C_C_EXPR_H

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,24 @@ void c_typecheck_baset::typecheck_expr_main(exprt &expr)
497497
{
498498
// already type checked
499499
}
500+
else if(auto bit_cast_expr = expr_try_dynamic_cast<bit_cast_exprt>(expr))
501+
{
502+
typecheck_type(expr.type());
503+
if(
504+
pointer_offset_bits(bit_cast_expr->type(), *this) ==
505+
pointer_offset_bits(bit_cast_expr->op().type(), *this))
506+
{
507+
exprt tmp = bit_cast_expr->lower();
508+
expr.swap(tmp);
509+
}
510+
else
511+
{
512+
error().source_location = expr.source_location();
513+
error() << "bit cast from '" << to_string(bit_cast_expr->op().type())
514+
<< "' to '" << to_string(expr.type()) << "' not permitted" << eom;
515+
throw 0;
516+
}
517+
}
500518
else
501519
{
502520
error().source_location = expr.source_location();

src/ansi-c/parser.y

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ int yyansi_cerror(const std::string &error);
236236
%token TOK_THREAD_LOCAL "_Thread_local"
237237
%token TOK_NULLPTR "nullptr"
238238
%token TOK_CONSTEXPR "constexpr"
239+
%token TOK_BIT_CAST "__builtin_bit_cast"
239240

240241
/*** special scanner reports ***/
241242

@@ -715,6 +716,12 @@ unary_expression:
715716
parser_stack($$).id(ID_alignof);
716717
parser_stack($$).add(ID_type_arg).swap(parser_stack($3));
717718
}
719+
| TOK_BIT_CAST '(' type_name ',' unary_expression ')'
720+
{ $$=$1;
721+
set($$, ID_bit_cast);
722+
mto($$, $5);
723+
parser_stack($$).type().swap(parser_stack($3));
724+
}
718725
;
719726

720727
cast_expression:

src/ansi-c/scanner.l

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,6 +1305,13 @@ __decltype { if(PARSER.cpp98 &&
13051305
return make_identifier();
13061306
}
13071307

1308+
"__builtin_bit_cast" { if(PARSER.mode==configt::ansi_ct::flavourt::GCC ||
1309+
PARSER.mode==configt::ansi_ct::flavourt::CLANG)
1310+
{ loc(); return TOK_BIT_CAST; }
1311+
else
1312+
return make_identifier();
1313+
}
1314+
13081315
{CPROVER_PREFIX}"atomic" { loc(); return TOK_CPROVER_ATOMIC; }
13091316
{CPROVER_PREFIX}"forall" { loc(); return TOK_FORALL; }
13101317
{CPROVER_PREFIX}"exists" { loc(); return TOK_EXISTS; }

src/util/irep_ids.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -914,6 +914,7 @@ IREP_ID_ONE(prophecy_rw_ok)
914914
IREP_ID_ONE(prophecy_r_ok)
915915
IREP_ID_ONE(prophecy_w_ok)
916916
IREP_ID_ONE(prophecy_pointer_in_range)
917+
IREP_ID_ONE(bit_cast)
917918

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

0 commit comments

Comments
 (0)