diff --git a/config/compilers/compiler_clang.h.in b/config/compilers/compiler_clang.h.in index 91f8398ef4..f62824ef0e 100644 --- a/config/compilers/compiler_clang.h.in +++ b/config/compilers/compiler_clang.h.in @@ -62,3 +62,15 @@ #undef DUK_USE_GCC_PRAGMAS #define DUK_USE_PACK_CLANG_ATTR + +#if defined(__clang__) && defined(__has_builtin) +#if __has_builtin(__builtin_bswap64) +#define DUK_BSWAP64(x) ((duk_uint64_t) __builtin_bswap64((duk_uint64_t) (x))) +#endif +#if __has_builtin(__builtin_bswap32) +#define DUK_BSWAP32(x) ((duk_uint32_t) __builtin_bswap32((duk_uint32_t) (x))) +#endif +#if __has_builtin(__builtin_bswap16) +#define DUK_BSWAP16(x) ((duk_uint16_t) __builtin_bswap16((duk_uint16_t) (x))) +#endif +#endif diff --git a/config/compilers/compiler_gcc.h.in b/config/compilers/compiler_gcc.h.in index 0a9b7d7af6..9e7f62048a 100644 --- a/config/compilers/compiler_gcc.h.in +++ b/config/compilers/compiler_gcc.h.in @@ -82,3 +82,13 @@ #endif #define DUK_USE_PACK_GCC_ATTR + +/* Availability varies based on platform (between GCC 4.4 and 4.8), and there + * are apparently some bugs in GCC 4.x. Check for GCC 5.0 before enabling + * these to be safe. + */ +#if defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 50000L) +#define DUK_BSWAP64(x) ((duk_uint64_t) __builtin_bswap64((duk_uint64_t) (x))) +#define DUK_BSWAP32(x) ((duk_uint32_t) __builtin_bswap32((duk_uint32_t) (x))) +#define DUK_BSWAP16(x) ((duk_uint16_t) __builtin_bswap16((duk_uint16_t) (x))) +#endif diff --git a/config/header-snippets/compiler_fillins.h.in b/config/header-snippets/compiler_fillins.h.in index 706b539633..431a87981e 100644 --- a/config/header-snippets/compiler_fillins.h.in +++ b/config/header-snippets/compiler_fillins.h.in @@ -1,3 +1,10 @@ +#if !defined(DUK_U64_CONSTANT) +#define DUK_U64_CONSTANT(x) x##ULL +#endif +#if !defined(DUK_I64_CONSTANT) +#define DUK_I64_CONSTANT(x) x##LL +#endif + #if !defined(DUK_VA_COPY) /* We need va_copy() which is defined in C99 / C++11, so an awkward * replacement is needed for pre-C99 / pre-C++11 environments. This @@ -148,17 +155,30 @@ #endif #endif +#if defined(DUK_F_HAVE_64BIT) +#if !defined(DUK_BSWAP64) +#define DUK_BSWAP64(x) \ + ((((duk_uint64_t) (x)) >> 56U) | \ + ((((duk_uint64_t) (x)) >> 40U) & DUK_U64_CONSTANT(0xff00)) | \ + ((((duk_uint64_t) (x)) >> 24U) & DUK_U64_CONSTANT(0xff0000)) | \ + ((((duk_uint64_t) (x)) >> 8U) & DUK_U64_CONSTANT(0xff000000)) | \ + ((((duk_uint64_t) (x)) << 8U) & DUK_U64_CONSTANT(0xff00000000)) | \ + ((((duk_uint64_t) (x)) << 24U) & DUK_U64_CONSTANT(0xff0000000000)) | \ + ((((duk_uint64_t) (x)) << 40U) & DUK_U64_CONSTANT(0xff000000000000)) | \ + (((duk_uint64_t) (x)) << 56U)) +#endif +#endif #if !defined(DUK_BSWAP32) #define DUK_BSWAP32(x) \ - ((((duk_uint32_t) (x)) >> 24) | \ - ((((duk_uint32_t) (x)) >> 8) & 0xff00UL) | \ - ((((duk_uint32_t) (x)) << 8) & 0xff0000UL) | \ - (((duk_uint32_t) (x)) << 24)) + ((((duk_uint32_t) (x)) >> 24U) | \ + ((((duk_uint32_t) (x)) >> 8U) & 0xff00UL) | \ + ((((duk_uint32_t) (x)) << 8U) & 0xff0000UL) | \ + (((duk_uint32_t) (x)) << 24U)) #endif #if !defined(DUK_BSWAP16) #define DUK_BSWAP16(x) \ - ((duk_uint16_t) (x) >> 8) | \ - ((duk_uint16_t) (x) << 8) + ((duk_uint16_t) (x) >> 8U) | \ + ((duk_uint16_t) (x) << 8U) #endif /* DUK_USE_VARIADIC_MACROS: required from compilers, so no fill-in. */ @@ -180,10 +200,3 @@ #if 0 /* not defined by default */ #undef DUK_USE_GCC_PRAGMAS #endif - -#if !defined(DUK_U64_CONSTANT) -#define DUK_U64_CONSTANT(x) x##ULL -#endif -#if !defined(DUK_I64_CONSTANT) -#define DUK_I64_CONSTANT(x) x##LL -#endif diff --git a/src-input/duk_selftest.c b/src-input/duk_selftest.c index 5c759464a6..db3665761d 100644 --- a/src-input/duk_selftest.c +++ b/src-input/duk_selftest.c @@ -187,10 +187,28 @@ DUK_LOCAL duk_uint_t duk__selftest_byte_order(void) { DUK_LOCAL duk_uint_t duk__selftest_bswap_macros(void) { duk_uint_t error_count = 0; + volatile duk_uint32_t x32_input, x32_output; duk_uint32_t x32; + volatile duk_uint16_t x16_input, x16_output; duk_uint16_t x16; duk_double_union du; duk_double_t du_diff; +#if defined(DUK_BSWAP64) + volatile duk_uint64_t x64_input, x64_output; + duk_uint64_t x64; +#endif + + /* Cover both compile time and runtime bswap operations, as these + * may have different bugs. + */ + + x16_input = 0xbeefUL; + x16 = x16_input; + x16 = DUK_BSWAP16(x16); + x16_output = x16; + if (x16_output != (duk_uint16_t) 0xefbeUL) { + DUK__FAILED("DUK_BSWAP16"); + } x16 = 0xbeefUL; x16 = DUK_BSWAP16(x16); @@ -198,12 +216,36 @@ DUK_LOCAL duk_uint_t duk__selftest_bswap_macros(void) { DUK__FAILED("DUK_BSWAP16"); } + x32_input = 0xdeadbeefUL; + x32 = x32_input; + x32 = DUK_BSWAP32(x32); + x32_output = x32; + if (x32_output != (duk_uint32_t) 0xefbeaddeUL) { + DUK__FAILED("DUK_BSWAP32"); + } + x32 = 0xdeadbeefUL; x32 = DUK_BSWAP32(x32); if (x32 != (duk_uint32_t) 0xefbeaddeUL) { DUK__FAILED("DUK_BSWAP32"); } +#if defined(DUK_BSWAP64) + x64_input = DUK_U64_CONSTANT(0x8899aabbccddeeff); + x64 = x64_input; + x64 = DUK_BSWAP64(x64); + x64_output = x64; + if (x64_output != (duk_uint64_t) DUK_U64_CONSTANT(0xffeeddccbbaa9988)) { + DUK__FAILED("DUK_BSWAP64"); + } + + x64 = DUK_U64_CONSTANT(0x8899aabbccddeeff); + x64 = DUK_BSWAP64(x64); + if (x64 != (duk_uint64_t) DUK_U64_CONSTANT(0xffeeddccbbaa9988)) { + DUK__FAILED("DUK_BSWAP64"); + } +#endif + /* >>> struct.unpack('>d', '4000112233445566'.decode('hex')) * (2.008366013071895,) */