experiment with constexpr
diff --git a/libdivide.h b/libdivide.h index 4421888..2824be1 100644 --- a/libdivide.h +++ b/libdivide.h
@@ -89,6 +89,14 @@ #define LIBDIVIDE_INLINE inline #endif +#if defined(__cplusplus) && __cplusplus >= 201703L +#define LIBDIVIDE_CONSTEXPR constexpr +#else +#define LIBDIVIDE_CONSTEXPR +#endif + +#define LIBDIVIDE_CONSTEXPR_INLINE LIBDIVIDE_CONSTEXPR LIBDIVIDE_INLINE + #if defined(__AVR__) #define LIBDIVIDE_ERROR(msg) #else @@ -123,33 +131,33 @@ #pragma pack(push, 1) struct libdivide_u16_t { - uint16_t magic; - uint8_t more; + uint16_t magic = 0; + uint8_t more = 0; }; struct libdivide_s16_t { - int16_t magic; - uint8_t more; + int16_t magic = 0; + uint8_t more = 0; }; struct libdivide_u32_t { - uint32_t magic; - uint8_t more; + uint32_t magic = 0; + uint8_t more = 0; }; struct libdivide_s32_t { - int32_t magic; - uint8_t more; + int32_t magic = 0; + uint8_t more = 0; }; struct libdivide_u64_t { - uint64_t magic; - uint8_t more; + uint64_t magic = 0; + uint8_t more = 0; }; struct libdivide_s64_t { - int64_t magic; - uint8_t more; + int64_t magic = 0; + uint8_t more = 0; }; struct libdivide_u16_branchfree_t { @@ -224,19 +232,25 @@ LIBDIVIDE_NEGATIVE_DIVISOR = 0x80 }; -static LIBDIVIDE_INLINE struct libdivide_s16_t libdivide_s16_gen(int16_t d); -static LIBDIVIDE_INLINE struct libdivide_u16_t libdivide_u16_gen(uint16_t d); -static LIBDIVIDE_INLINE struct libdivide_s32_t libdivide_s32_gen(int32_t d); -static LIBDIVIDE_INLINE struct libdivide_u32_t libdivide_u32_gen(uint32_t d); -static LIBDIVIDE_INLINE struct libdivide_s64_t libdivide_s64_gen(int64_t d); -static LIBDIVIDE_INLINE struct libdivide_u64_t libdivide_u64_gen(uint64_t d); +static LIBDIVIDE_CONSTEXPR_INLINE struct libdivide_s16_t libdivide_s16_gen(int16_t d); +static LIBDIVIDE_CONSTEXPR_INLINE struct libdivide_u16_t libdivide_u16_gen(uint16_t d); +static LIBDIVIDE_CONSTEXPR_INLINE struct libdivide_s32_t libdivide_s32_gen(int32_t d); +static LIBDIVIDE_CONSTEXPR_INLINE struct libdivide_u32_t libdivide_u32_gen(uint32_t d); +static LIBDIVIDE_CONSTEXPR_INLINE struct libdivide_s64_t libdivide_s64_gen(int64_t d); +static LIBDIVIDE_CONSTEXPR_INLINE struct libdivide_u64_t libdivide_u64_gen(uint64_t d); -static LIBDIVIDE_INLINE struct libdivide_s16_branchfree_t libdivide_s16_branchfree_gen(int16_t d); -static LIBDIVIDE_INLINE struct libdivide_u16_branchfree_t libdivide_u16_branchfree_gen(uint16_t d); -static LIBDIVIDE_INLINE struct libdivide_s32_branchfree_t libdivide_s32_branchfree_gen(int32_t d); -static LIBDIVIDE_INLINE struct libdivide_u32_branchfree_t libdivide_u32_branchfree_gen(uint32_t d); -static LIBDIVIDE_INLINE struct libdivide_s64_branchfree_t libdivide_s64_branchfree_gen(int64_t d); -static LIBDIVIDE_INLINE struct libdivide_u64_branchfree_t libdivide_u64_branchfree_gen(uint64_t d); +static LIBDIVIDE_CONSTEXPR_INLINE struct libdivide_s16_branchfree_t libdivide_s16_branchfree_gen( + int16_t d); +static LIBDIVIDE_CONSTEXPR_INLINE struct libdivide_u16_branchfree_t libdivide_u16_branchfree_gen( + uint16_t d); +static LIBDIVIDE_CONSTEXPR_INLINE struct libdivide_s32_branchfree_t libdivide_s32_branchfree_gen( + int32_t d); +static LIBDIVIDE_CONSTEXPR_INLINE struct libdivide_u32_branchfree_t libdivide_u32_branchfree_gen( + uint32_t d); +static LIBDIVIDE_CONSTEXPR_INLINE struct libdivide_s64_branchfree_t libdivide_s64_branchfree_gen( + int64_t d); +static LIBDIVIDE_CONSTEXPR_INLINE struct libdivide_u64_branchfree_t libdivide_u64_branchfree_gen( + uint64_t d); static LIBDIVIDE_INLINE int16_t libdivide_s16_do_raw(int16_t numer, int16_t magic, uint8_t more); static LIBDIVIDE_INLINE int16_t libdivide_s16_do( @@ -362,7 +376,7 @@ #endif } -static LIBDIVIDE_INLINE int16_t libdivide_count_leading_zeros16(uint16_t val) { +static LIBDIVIDE_CONSTEXPR_INLINE int16_t libdivide_count_leading_zeros16(uint16_t val) { #if defined(__AVR__) // Fast way to count leading zeros // On the AVR 8-bit architecture __builtin_clz() works on a int16_t. @@ -392,7 +406,7 @@ #endif } -static LIBDIVIDE_INLINE int32_t libdivide_count_leading_zeros32(uint32_t val) { +static LIBDIVIDE_CONSTEXPR_INLINE int32_t libdivide_count_leading_zeros32(uint32_t val) { #if defined(__AVR__) // Fast way to count leading zeros return __builtin_clzl(val); @@ -421,7 +435,7 @@ #endif } -static LIBDIVIDE_INLINE int32_t libdivide_count_leading_zeros64(uint64_t val) { +static LIBDIVIDE_CONSTEXPR_INLINE int32_t libdivide_count_leading_zeros64(uint64_t val) { #if defined(__GNUC__) || __has_builtin(__builtin_clzll) // Fast way to count leading zeros return __builtin_clzll(val); @@ -442,7 +456,7 @@ // libdivide_32_div_16_to_16: divides a 32-bit uint {u1, u0} by a 16-bit // uint {v}. The result must fit in 16 bits. // Returns the quotient directly and the remainder in *r -static LIBDIVIDE_INLINE uint16_t libdivide_32_div_16_to_16( +static LIBDIVIDE_CONSTEXPR_INLINE uint16_t libdivide_32_div_16_to_16( uint16_t u1, uint16_t u0, uint16_t v, uint16_t *r) { uint32_t n = ((uint32_t)u1 << 16) | u0; uint16_t result = (uint16_t)(n / v); @@ -453,7 +467,7 @@ // libdivide_64_div_32_to_32: divides a 64-bit uint {u1, u0} by a 32-bit // uint {v}. The result must fit in 32 bits. // Returns the quotient directly and the remainder in *r -static LIBDIVIDE_INLINE uint32_t libdivide_64_div_32_to_32( +static LIBDIVIDE_CONSTEXPR_INLINE uint32_t libdivide_64_div_32_to_32( uint32_t u1, uint32_t u0, uint32_t v, uint32_t *r) { #if (defined(LIBDIVIDE_i386) || defined(LIBDIVIDE_X86_64)) && defined(LIBDIVIDE_GCC_STYLE_ASM) uint32_t result; @@ -469,7 +483,7 @@ // libdivide_128_div_64_to_64: divides a 128-bit uint {numhi, numlo} by a 64-bit uint {den}. The // result must fit in 64 bits. Returns the quotient directly and the remainder in *r -static LIBDIVIDE_INLINE uint64_t libdivide_128_div_64_to_64( +static LIBDIVIDE_CONSTEXPR_INLINE uint64_t libdivide_128_div_64_to_64( uint64_t numhi, uint64_t numlo, uint64_t den, uint64_t *r) { // N.B. resist the temptation to use __uint128_t here. // In LLVM compiler-rt, it performs a 128/128 -> 128 division which is many times slower than @@ -564,8 +578,7 @@ #endif } -#if !(defined(HAS_INT128_T) && \ - defined(HAS_INT128_DIV)) +#if !(defined(HAS_INT128_T) && defined(HAS_INT128_DIV)) // Bitshift a u128 in place, left (signed_shift > 0) or right (signed_shift < 0) static LIBDIVIDE_INLINE void libdivide_u128_shift( @@ -685,7 +698,7 @@ ////////// UINT16 -static LIBDIVIDE_INLINE struct libdivide_u16_t libdivide_internal_u16_gen( +static LIBDIVIDE_CONSTEXPR_INLINE struct libdivide_u16_t libdivide_internal_u16_gen( uint16_t d, int branchfree) { if (d == 0) { LIBDIVIDE_ERROR("divider must be != 0"); @@ -703,9 +716,9 @@ result.magic = 0; result.more = (uint8_t)(floor_log_2_d - (branchfree != 0)); } else { - uint8_t more; - uint16_t rem, proposed_m; - proposed_m = libdivide_32_div_16_to_16((uint16_t)1 << floor_log_2_d, 0, d, &rem); + uint8_t more = 0; + uint16_t rem = 0; + uint16_t proposed_m = libdivide_32_div_16_to_16((uint16_t)1 << floor_log_2_d, 0, d, &rem); LIBDIVIDE_ASSERT(rem > 0 && rem < d); const uint16_t e = d - rem; @@ -736,11 +749,11 @@ return result; } -struct libdivide_u16_t libdivide_u16_gen(uint16_t d) { +LIBDIVIDE_CONSTEXPR struct libdivide_u16_t libdivide_u16_gen(uint16_t d) { return libdivide_internal_u16_gen(d, 0); } -struct libdivide_u16_branchfree_t libdivide_u16_branchfree_gen(uint16_t d) { +LIBDIVIDE_CONSTEXPR struct libdivide_u16_branchfree_t libdivide_u16_branchfree_gen(uint16_t d) { if (d == 1) { LIBDIVIDE_ERROR("branchfree divider must be != 1"); } @@ -849,7 +862,7 @@ ////////// UINT32 -static LIBDIVIDE_INLINE struct libdivide_u32_t libdivide_internal_u32_gen( +static LIBDIVIDE_CONSTEXPR_INLINE struct libdivide_u32_t libdivide_internal_u32_gen( uint32_t d, int branchfree) { if (d == 0) { LIBDIVIDE_ERROR("divider must be != 0"); @@ -867,9 +880,9 @@ result.magic = 0; result.more = (uint8_t)(floor_log_2_d - (branchfree != 0)); } else { - uint8_t more; - uint32_t rem, proposed_m; - proposed_m = libdivide_64_div_32_to_32((uint32_t)1 << floor_log_2_d, 0, d, &rem); + uint8_t more = 0; + uint32_t rem = 0; + uint32_t proposed_m = libdivide_64_div_32_to_32((uint32_t)1 << floor_log_2_d, 0, d, &rem); LIBDIVIDE_ASSERT(rem > 0 && rem < d); const uint32_t e = d - rem; @@ -900,11 +913,11 @@ return result; } -struct libdivide_u32_t libdivide_u32_gen(uint32_t d) { +LIBDIVIDE_CONSTEXPR struct libdivide_u32_t libdivide_u32_gen(uint32_t d) { return libdivide_internal_u32_gen(d, 0); } -struct libdivide_u32_branchfree_t libdivide_u32_branchfree_gen(uint32_t d) { +LIBDIVIDE_CONSTEXPR struct libdivide_u32_branchfree_t libdivide_u32_branchfree_gen(uint32_t d) { if (d == 1) { LIBDIVIDE_ERROR("branchfree divider must be != 1"); } @@ -1007,7 +1020,7 @@ /////////// UINT64 -static LIBDIVIDE_INLINE struct libdivide_u64_t libdivide_internal_u64_gen( +static LIBDIVIDE_CONSTEXPR_INLINE struct libdivide_u64_t libdivide_internal_u64_gen( uint64_t d, int branchfree) { if (d == 0) { LIBDIVIDE_ERROR("divider must be != 0"); @@ -1025,10 +1038,10 @@ result.magic = 0; result.more = (uint8_t)(floor_log_2_d - (branchfree != 0)); } else { - uint64_t proposed_m, rem; - uint8_t more; + uint64_t rem = 0; + uint8_t more = 0; // (1 << (64 + floor_log_2_d)) / d - proposed_m = libdivide_128_div_64_to_64((uint64_t)1 << floor_log_2_d, 0, d, &rem); + uint64_t proposed_m = libdivide_128_div_64_to_64((uint64_t)1 << floor_log_2_d, 0, d, &rem); LIBDIVIDE_ASSERT(rem > 0 && rem < d); const uint64_t e = d - rem; @@ -1060,11 +1073,11 @@ return result; } -struct libdivide_u64_t libdivide_u64_gen(uint64_t d) { +LIBDIVIDE_CONSTEXPR struct libdivide_u64_t libdivide_u64_gen(uint64_t d) { return libdivide_internal_u64_gen(d, 0); } -struct libdivide_u64_branchfree_t libdivide_u64_branchfree_gen(uint64_t d) { +LIBDIVIDE_CONSTEXPR struct libdivide_u64_branchfree_t libdivide_u64_branchfree_gen(uint64_t d) { if (d == 1) { LIBDIVIDE_ERROR("branchfree divider must be != 1"); } @@ -1179,7 +1192,7 @@ /////////// SINT16 -static LIBDIVIDE_INLINE struct libdivide_s16_t libdivide_internal_s16_gen( +static LIBDIVIDE_CONSTEXPR_INLINE struct libdivide_s16_t libdivide_internal_s16_gen( int16_t d, int branchfree) { if (d == 0) { LIBDIVIDE_ERROR("divider must be != 0"); @@ -1205,11 +1218,12 @@ } else { LIBDIVIDE_ASSERT(floor_log_2_d >= 1); - uint8_t more; + uint8_t more = 0; // the dividend here is 2**(floor_log_2_d + 31), so the low 16 bit word // is 0 and the high word is floor_log_2_d - 1 - uint16_t rem, proposed_m; - proposed_m = libdivide_32_div_16_to_16((uint16_t)1 << (floor_log_2_d - 1), 0, absD, &rem); + uint16_t rem = 0; + uint16_t proposed_m = + libdivide_32_div_16_to_16((uint16_t)1 << (floor_log_2_d - 1), 0, absD, &rem); const uint16_t e = absD - rem; // We are going to start with a power of floor_log_2_d - 1. @@ -1245,11 +1259,11 @@ return result; } -struct libdivide_s16_t libdivide_s16_gen(int16_t d) { +LIBDIVIDE_CONSTEXPR struct libdivide_s16_t libdivide_s16_gen(int16_t d) { return libdivide_internal_s16_gen(d, 0); } -struct libdivide_s16_branchfree_t libdivide_s16_branchfree_gen(int16_t d) { +LIBDIVIDE_CONSTEXPR struct libdivide_s16_branchfree_t libdivide_s16_branchfree_gen(int16_t d) { struct libdivide_s16_t tmp = libdivide_internal_s16_gen(d, 1); struct libdivide_s16_branchfree_t result = {tmp.magic, tmp.more}; return result; @@ -1354,7 +1368,7 @@ /////////// SINT32 -static LIBDIVIDE_INLINE struct libdivide_s32_t libdivide_internal_s32_gen( +static LIBDIVIDE_CONSTEXPR_INLINE struct libdivide_s32_t libdivide_internal_s32_gen( int32_t d, int branchfree) { if (d == 0) { LIBDIVIDE_ERROR("divider must be != 0"); @@ -1380,11 +1394,12 @@ } else { LIBDIVIDE_ASSERT(floor_log_2_d >= 1); - uint8_t more; + uint8_t more = 0; // the dividend here is 2**(floor_log_2_d + 31), so the low 32 bit word // is 0 and the high word is floor_log_2_d - 1 - uint32_t rem, proposed_m; - proposed_m = libdivide_64_div_32_to_32((uint32_t)1 << (floor_log_2_d - 1), 0, absD, &rem); + uint32_t rem = 0; + uint32_t proposed_m = + libdivide_64_div_32_to_32((uint32_t)1 << (floor_log_2_d - 1), 0, absD, &rem); const uint32_t e = absD - rem; // We are going to start with a power of floor_log_2_d - 1. @@ -1420,11 +1435,11 @@ return result; } -struct libdivide_s32_t libdivide_s32_gen(int32_t d) { +LIBDIVIDE_CONSTEXPR struct libdivide_s32_t libdivide_s32_gen(int32_t d) { return libdivide_internal_s32_gen(d, 0); } -struct libdivide_s32_branchfree_t libdivide_s32_branchfree_gen(int32_t d) { +LIBDIVIDE_CONSTEXPR struct libdivide_s32_branchfree_t libdivide_s32_branchfree_gen(int32_t d) { struct libdivide_s32_t tmp = libdivide_internal_s32_gen(d, 1); struct libdivide_s32_branchfree_t result = {tmp.magic, tmp.more}; return result; @@ -1523,7 +1538,7 @@ ///////////// SINT64 -static LIBDIVIDE_INLINE struct libdivide_s64_t libdivide_internal_s64_gen( +static LIBDIVIDE_CONSTEXPR_INLINE struct libdivide_s64_t libdivide_internal_s64_gen( int64_t d, int branchfree) { if (d == 0) { LIBDIVIDE_ERROR("divider must be != 0"); @@ -1549,8 +1564,8 @@ } else { // the dividend here is 2**(floor_log_2_d + 63), so the low 64 bit word // is 0 and the high word is floor_log_2_d - 1 - uint8_t more; - uint64_t rem, proposed_m; + uint8_t more = 0; + uint64_t rem = 0, proposed_m = 0; proposed_m = libdivide_128_div_64_to_64((uint64_t)1 << (floor_log_2_d - 1), 0, absD, &rem); const uint64_t e = absD - rem; @@ -1589,11 +1604,11 @@ return result; } -struct libdivide_s64_t libdivide_s64_gen(int64_t d) { +LIBDIVIDE_CONSTEXPR struct libdivide_s64_t libdivide_s64_gen(int64_t d) { return libdivide_internal_s64_gen(d, 0); } -struct libdivide_s64_branchfree_t libdivide_s64_branchfree_gen(int64_t d) { +LIBDIVIDE_CONSTEXPR struct libdivide_s64_branchfree_t libdivide_s64_branchfree_gen(int64_t d) { struct libdivide_s64_t tmp = libdivide_internal_s64_gen(d, 1); struct libdivide_s64_branchfree_t ret = {tmp.magic, tmp.more}; return ret; @@ -2966,18 +2981,21 @@ #ifdef __cplusplus -//for constexpr zero initialization, -//c++11 might handle things ok, -//but just limit to at least c++14 to ensure -//we don't break anyone's code: +// for constexpr zero initialization, +// c++11 might handle things ok, +// but just limit to at least c++14 to ensure +// we don't break anyone's code: // for gcc and clang, use https://en.cppreference.com/w/cpp/feature_test#cpp_constexpr #if (defined(__GNUC__) || defined(__clang__)) && (__cpp_constexpr >= 201304L) #define LIBDIVIDE_CONSTEXPR constexpr -// supposedly, MSVC might not implement feature test macros right (https://stackoverflow.com/questions/49316752/feature-test-macros-not-working-properly-in-visual-c) -// so check that _MSVC_LANG corresponds to at least c++14, and _MSC_VER corresponds to at least VS 2017 15.0 (for extended constexpr support https://learn.microsoft.com/en-us/cpp/overview/visual-cpp-language-conformance?view=msvc-170) -#elif defined(_MSC_VER) && _MSC_VER >= 1910 && defined(_MSVC_LANG) && _MSVC_LANG >=201402L +// supposedly, MSVC might not implement feature test macros right +// (https://stackoverflow.com/questions/49316752/feature-test-macros-not-working-properly-in-visual-c) +// so check that _MSVC_LANG corresponds to at least c++14, and _MSC_VER corresponds to at least VS +// 2017 15.0 (for extended constexpr support +// https://learn.microsoft.com/en-us/cpp/overview/visual-cpp-language-conformance?view=msvc-170) +#elif defined(_MSC_VER) && _MSC_VER >= 1910 && defined(_MSVC_LANG) && _MSVC_LANG >= 201402L #define LIBDIVIDE_CONSTEXPR constexpr // in case some other obscure compiler has the right __cpp_constexpr : @@ -3081,7 +3099,7 @@ #define DISPATCHER_GEN(T, ALGO) \ libdivide_##ALGO##_t denom; \ LIBDIVIDE_INLINE dispatcher() {} \ - explicit LIBDIVIDE_CONSTEXPR dispatcher(decltype(nullptr)) : denom{} {} \ + explicit LIBDIVIDE_CONSTEXPR dispatcher(decltype(nullptr)) : denom{} {} \ LIBDIVIDE_INLINE dispatcher(T d) : denom(libdivide_##ALGO##_gen(d)) {} \ LIBDIVIDE_INLINE T divide(T n) const { return libdivide_##ALGO##_do(n, &denom); } \ LIBDIVIDE_INLINE T recover() const { return libdivide_##ALGO##_recover(&denom); } \