https://gcc.gnu.org/g:66a144b16aa9034350d6294abbbe06559143da9d
commit r17-5183-g66a144b16aa9034350d6294abbbe06559143da9d Author: Jakub Jelinek <[email protected]> Date: Wed Oct 14 11:21:50 2026 +0200 libstdc++: Implement C++29 P3104R6 - Bit permutations The following patch implements the C++29 P3104R6 - Bit permutations paper. bit_repeat comes first in the patch because I'm using it in the bit_reverse fallback. bit_reverse tested both without the https://gcc.gnu.org/pipermail/gcc-patches/2026-September/731814.html patch (where __builtin_bitreverseg isn't used, as gcc doesn't announce it), also tested just the fallback of fallback variant by adding 0 && to the if constexpr condition, and with that patch. bit_compress and bit_expand written according to Hacker's Delight (only clang implements the two builtins), and testcases for those double checked against clang++'s __builtin_elementwise_{pext,pdep}. The implementation of all the functions is fairly optimized but I'm not using any x86 nor aarch64 intrinsics to optimize those even more, I think it is better when we implement builtins for this and handle such details on the compiler side. I've tried to write the tests so that it would eventually work also with _BitInt if voted in and these would be enabled for those too, but haven't actually tested that; __builtin_bitreverseg is known to work fine for _BitInt, the others would likely work too but not efficiently for larger precisions. 2026-10-14 Jakub Jelinek <[email protected]> P3104R6 * include/bits/version.def: Implement C++29 P3104R6 - Bit permutations. (bitops): Change value from 202606 to 202607. * include/bits/version.h: Regenerate. * include/std/bit (std::bit_repeat, std::bit_reverse, std::bit_compress, std::bit_expand): New function templates for C++29. * src/c++23/std.cc.in (std::bit_repeat, std::bit_reverse, std::bit_compress, std::bit_expand): New exports for C++29. * testsuite/26_numerics/bit/bit.permute/bit_reverse.cc: New test. * testsuite/26_numerics/bit/bit.permute/bit_repeat.cc: New test. * testsuite/26_numerics/bit/bit.permute/bit_compress.cc: New test. * testsuite/26_numerics/bit/bit.permute/bit_expand.cc: New test. Reviewed-by: Jonathan Wakely <[email protected]> Diff: --- libstdc++-v3/include/bits/version.def | 5 +- libstdc++-v3/include/bits/version.h | 4 +- libstdc++-v3/include/std/bit | 117 +++++++++++++++++++ libstdc++-v3/src/c++23/std.cc.in | 6 + .../26_numerics/bit/bit.permute/bit_compress.cc | 87 ++++++++++++++ .../26_numerics/bit/bit.permute/bit_expand.cc | 87 ++++++++++++++ .../26_numerics/bit/bit.permute/bit_repeat.cc | 125 +++++++++++++++++++++ .../26_numerics/bit/bit.permute/bit_reverse.cc | 73 ++++++++++++ 8 files changed, 501 insertions(+), 3 deletions(-) diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 9c523581c65a..170309c96999 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -890,10 +890,13 @@ ftms = { ftms = { name = bitops; + // 202606 P3793R2 Better shifting + // 202607 P3104R6 Bit permutations values = { - v = 202606; + v = 202607; cxxmin = 29; }; + // 201907 P0553R4 Bit operations values = { v = 201907; cxxmin = 20; diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index e0003054af68..ce20b41caa2d 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -990,9 +990,9 @@ #if !defined(__cpp_lib_bitops) # if (__cplusplus > 202603L) -# define __glibcxx_bitops 202606L +# define __glibcxx_bitops 202607L # if defined(__glibcxx_want_all) || defined(__glibcxx_want_bitops) -# define __cpp_lib_bitops 202606L +# define __cpp_lib_bitops 202607L # endif # elif (__cplusplus >= 202002L) # define __glibcxx_bitops 201907L diff --git a/libstdc++-v3/include/std/bit b/libstdc++-v3/include/std/bit index 46fa8b65032c..51910fda1c10 100644 --- a/libstdc++-v3/include/std/bit +++ b/libstdc++-v3/include/std/bit @@ -515,6 +515,123 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return std::__bit_width(__x); } #endif // defined (__cpp_lib_int_pow2) +#if __cpp_lib_bitops >= 202607L // >= C++29 + // [bit.permute], permutation + template<__unsigned_integer _Tp> + [[nodiscard]] constexpr _Tp + bit_repeat(_Tp __x, int __l) + { + __glibcxx_assert( __l > 0 ); + constexpr auto _Nd = __gnu_cxx::__int_traits<_Tp>::__digits; + if (__l >= _Nd) + return __x; + __x &= ((_Tp)1 << __l) - 1; + do + { + __x |= __x << __l; + __l <<= 1; + } + while (__l < _Nd); + return __x; + } + + template<__unsigned_integer _Tp> + [[nodiscard]] constexpr _Tp + bit_reverse(_Tp __x) noexcept + { +#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_bitreverseg) + return __builtin_bitreverseg(__x); +#else + constexpr auto _Nd = __gnu_cxx::__int_traits<_Tp>::__digits; + if constexpr (_Nd % __CHAR_BIT__ == 0 && __CHAR_BIT__ == 8) + { + __x = std::byteswap(__x); + constexpr auto __m1 = std::bit_repeat<_Tp>(15, 8); + constexpr auto __m2 = std::bit_repeat<_Tp>(3, 4); + constexpr auto __m3 = std::bit_repeat<_Tp>(1, 2); + __x = ((__x >> 4) & __m1) | ((__x & __m1) << 4); + __x = ((__x >> 2) & __m2) | ((__x & __m2) << 2); + __x = ((__x >> 1) & __m3) | ((__x & __m3) << 1); + return __x; + } + else + { + _Tp __r = 0; + for (int __i = 0; __i < _Nd; ++__i) + { + __r <<= 1; + __r |= (__x & 1); + __x >>= 1; + } + return __r; + } +#endif + } + + template<__unsigned_integer _Tp> + [[nodiscard]] constexpr _Tp + bit_compress(_Tp __x, _Tp __m) noexcept + { +#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_elementwise_pext) + return __builtin_elementwise_pext(__x, __m); +#else + constexpr auto _Nd = __gnu_cxx::__int_traits<_Tp>::__digits; + // Based on "Compress, or Generalized Extract" in Hacker's Delight + __x &= __m; + _Tp __mk = ~__m << 1; + for (int __i = 1; __i < _Nd; __i <<= 1) + { + _Tp __mp = __mk; + for (int __j = 1; __j < _Nd; __j <<= 1) + __mp ^= __mp << __j; + _Tp __mv = __mp & __m; + __m = (__m ^ __mv) | (__mv >> __i); + _Tp __t = __x & __mv; + __x = (__x ^ __t) | (__t >> __i); + __mk = __mk & ~__mp; + } + return __x; +#endif + } + + template<__unsigned_integer _Tp> + [[nodiscard]] constexpr _Tp + bit_expand(_Tp __x, _Tp __m) noexcept + { +#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_elementwise_pdep) + return __builtin_elementwise_pdep(__x, __m); +#else + constexpr auto _Nd = __gnu_cxx::__int_traits<_Tp>::__digits; + if constexpr (_Nd == 1) + return __x & __m; + constexpr auto __log2_Nd + = _Nd == 1 ? 1 : (__gnu_cxx::__int_traits<unsigned>::__digits + - std::__countl_zero<unsigned>(_Nd - 1)); + // Based on "Expand, or Generalized Insert" in Hacker's Delight + _Tp __m0 = __m; + _Tp __mk = ~__m << 1; + _Tp __array[__log2_Nd]; + for (int __i = 0; __i < __log2_Nd; __i++) + { + _Tp __mp = __mk; + for (int __j = 1; __j < _Nd; __j <<= 1) + __mp ^= __mp << __j; + _Tp __mv = __mp & __m; + __array[__i] = __mv; + __m = (__m ^ __mv) | (__mv >> (_Tp(1) << __i)); + __mk = __mk & ~__mp; + } + for (int __i = __log2_Nd - 1; __i >= 0; __i--) + { + _Tp __mv = __array[__i]; + _Tp __t = __x << (_Tp(1) << __i); + __x = (__x & ~__mv) | (__t & __mv); + } + return __x & __m0; +#endif + } +#endif // __cpp_lib_bitops >= 202607L + #ifdef __cpp_lib_endian // C++ >= 20 /// Byte order constants diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/std.cc.in index 928281699db9..d05902466d97 100644 --- a/libstdc++-v3/src/c++23/std.cc.in +++ b/libstdc++-v3/src/c++23/std.cc.in @@ -699,6 +699,12 @@ export namespace std using std::shl; using std::shr; #endif +#if __cpp_lib_bitops >= 202607L // >= C++29 + using std::bit_reverse; + using std::bit_repeat; + using std::bit_compress; + using std::bit_expand; +#endif } // 22.9 <bitset> diff --git a/libstdc++-v3/testsuite/26_numerics/bit/bit.permute/bit_compress.cc b/libstdc++-v3/testsuite/26_numerics/bit/bit.permute/bit_compress.cc new file mode 100644 index 000000000000..130bf6cfea82 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/bit/bit.permute/bit_compress.cc @@ -0,0 +1,87 @@ +// { dg-do compile { target c++29 } } + +#include <bit> +#include <limits> + +template<typename UInt> +constexpr auto +test(UInt x) +-> decltype(std::bit_compress(x, x)) +{ + constexpr unsigned digits = std::numeric_limits<UInt>::digits; + static_assert( std::bit_compress(UInt(0), UInt(0)) == UInt(0) ); + static_assert( std::bit_compress(UInt(0x123456789abcdefull), UInt(0)) == UInt(0) ); + static_assert( std::bit_compress(UInt(0x123456789abcdefull), UInt(~UInt(0))) == UInt(0x123456789abcdefull) ); + static_assert( std::bit_compress(UInt(0), UInt(0x123456789abcdefull)) == UInt(0) ); + static_assert( std::bit_compress(UInt(~UInt(0)), UInt(~UInt(0))) == UInt(~UInt(0)) ); + static_assert( std::bit_compress(UInt(~UInt(0)), UInt(0x123456789abcdefull)) == (UInt(1) << std::popcount(UInt(0x123456789abcdefull))) - 1 ); + static_assert( digits < 16 || digits % 2 != 0 || std::bit_compress(std::bit_repeat(UInt(0x1234), 16), std::bit_repeat(UInt(1), 2)) == std::bit_repeat(UInt(0x46), 8) >> (digits / 2) ); + static_assert( digits < 16 || digits % 2 != 0 || std::bit_compress(std::bit_repeat(UInt(0x1234), 16), std::bit_repeat(UInt(2), 2)) == std::bit_repeat(UInt(0x14), 8) >> (digits / 2) ); + static_assert( digits < 8 || std::bit_compress(UInt(0xff), UInt(0xff)) == UInt(0xff) ); + static_assert( digits < 8 || std::bit_compress(UInt(0xff), UInt(0x0f)) == UInt(0x0f) ); + static_assert( digits < 8 || std::bit_compress(UInt(0xff), UInt(0xf0)) == UInt(0x0f) ); + static_assert( digits % 2 != 0 || std::bit_compress(std::bit_repeat(UInt(2), 2), std::bit_repeat(UInt(0xc), 4)) == std::bit_repeat(UInt(0x2), 2) >> (digits / 2) ); + static_assert( digits % 2 != 0 || std::bit_compress(UInt(~UInt(0)), std::bit_repeat(UInt(2), 2)) == UInt(~UInt(0)) >> (digits / 2) ); + if constexpr (digits >= 8) + { + static_assert( std::bit_compress(UInt(0xd4), UInt(0xbf)) == UInt(0x54) ); + static_assert( std::bit_compress(UInt(0x66), UInt(0x1d)) == UInt(0x02) ); + } + if constexpr (digits >= 16) + { + static_assert( std::bit_compress(UInt(0xebd4u), UInt(0x93bfu)) == UInt(0x05d4) ); + static_assert( std::bit_compress(UInt(0xa166u), UInt(0x001du)) == UInt(2) ); + } + if constexpr (digits >= 32) + { + static_assert( std::bit_compress(UInt(0x9daeebd4u), UInt(0xad8793bfu)) == UInt(0xbf5d4u) ); + static_assert( std::bit_compress(UInt(0x979ba166u), UInt(0x54ca001du)) == UInt(0x3b2u) ); + } + if constexpr (digits >= 64) + { + static_assert( std::bit_compress(UInt(0x5bd30f069daeebd4ull), UInt(0x64f9a89bad8793bfull)) == UInt(0x13522bf5d4ull) ); + static_assert( std::bit_compress(UInt(0xb5672499979ba166ull), UInt(0xd315782354ca001dull)) == UInt(0x2ad0bb2ull) ); + } + return x; +} + +static_assert( test( (unsigned char)123 ) ); +static_assert( test( (unsigned short)12345 ) ); +static_assert( test( (unsigned int)1234567890 ) ); +static_assert( test( 123456789UL ) ); +static_assert( test( ~123456789ULL ) ); + +// std::bit_compress(T) shall not participate in overload resolution +// unless T is an unsigned integer type. +struct X { constexpr bool did_not_match() { return true; } }; +constexpr X test(...) { return X{}; } +static_assert( test( (bool)0 ).did_not_match() ); +static_assert( test( (char)0 ).did_not_match() ); +static_assert( test( (int)0 ).did_not_match() ); +static_assert( test( (char16_t)0 ).did_not_match() ); +static_assert( test( (float)0 ).did_not_match() ); +static_assert( test( (void*)0 ).did_not_match() ); +static_assert( test( X{} ).did_not_match() ); +enum E : unsigned { e }; +static_assert( test( e ).did_not_match() ); + +#if !defined(__STRICT_ANSI__) && defined __SIZEOF_INT128__ +static_assert( test( (unsigned __int128)3 ) ); +static_assert( test( (__int128)0 ).did_not_match() ); +#endif +#if defined(__GLIBCXX_TYPE_INT_N_0) +static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_0)1 ) ); +static_assert( test( (__GLIBCXX_TYPE_INT_N_0)0 ).did_not_match() ); +#endif +#if defined(__GLIBCXX_TYPE_INT_N_1) +static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_1)1 ) ); +static_assert( test( (__GLIBCXX_TYPE_INT_N_1)0 ).did_not_match() ); +#endif +#if defined(__GLIBCXX_TYPE_INT_N_2) +static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_2)1 ) ); +static_assert( test( (__GLIBCXX_TYPE_INT_N_2)0 ).did_not_match() ); +#endif +#if defined(__GLIBCXX_TYPE_INT_N_3) +static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_3)1 ) ); +static_assert( test( (__GLIBCXX_TYPE_INT_N_3)0 ).did_not_match() ); +#endif diff --git a/libstdc++-v3/testsuite/26_numerics/bit/bit.permute/bit_expand.cc b/libstdc++-v3/testsuite/26_numerics/bit/bit.permute/bit_expand.cc new file mode 100644 index 000000000000..0e510dfea99c --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/bit/bit.permute/bit_expand.cc @@ -0,0 +1,87 @@ +// { dg-do compile { target c++29 } } + +#include <bit> +#include <limits> + +template<typename UInt> +constexpr auto +test(UInt x) +-> decltype(std::bit_expand(x, x)) +{ + constexpr unsigned digits = std::numeric_limits<UInt>::digits; + static_assert( std::bit_expand(UInt(0), UInt(0)) == UInt(0) ); + static_assert( std::bit_expand(UInt(0x123456789abcdefull), UInt(0)) == UInt(0) ); + static_assert( std::bit_expand(UInt(0x123456789abcdefull), UInt(~UInt(0))) == UInt(0x123456789abcdefull) ); + static_assert( std::bit_expand(UInt(0), UInt(0x123456789abcdefull)) == UInt(0) ); + static_assert( std::bit_expand(UInt(~UInt(0)), UInt(~UInt(0))) == UInt(~UInt(0)) ); + static_assert( std::bit_expand(UInt((UInt(1) << std::popcount(UInt(0x123456789abcdefull))) - 1), UInt(0x123456789abcdefull)) == UInt(0x123456789abcdefull) ); + static_assert( digits < 16 || std::bit_expand(std::bit_repeat(UInt(0x46), 8), std::bit_repeat(UInt(1), 2)) == std::bit_repeat(UInt(0x1014), 16) ); + static_assert( digits < 16 || std::bit_expand(std::bit_repeat(UInt(0x14), 8), std::bit_repeat(UInt(2), 2)) == std::bit_repeat(UInt(0x0220), 16) ); + static_assert( digits < 8 || std::bit_expand(UInt(0xff), UInt(0xff)) == UInt(0xff) ); + static_assert( digits < 8 || std::bit_expand(UInt(0x0f), UInt(0xff)) == UInt(0x0f) ); + static_assert( digits < 8 || std::bit_expand(UInt(0x0f), UInt(0xf0)) == UInt(0xf0) ); + static_assert( std::bit_expand(std::bit_repeat(UInt(2), 2), std::bit_repeat(UInt(0xc), 4)) == std::bit_repeat(UInt(0x8), 4) ); + static_assert( std::bit_expand(std::bit_repeat(UInt(0x0f), 8), std::bit_repeat(UInt(2), 2)) == std::bit_repeat(UInt(0xaa), 16) ); + if constexpr (digits >= 8) + { + static_assert( std::bit_expand(UInt(0xd4), UInt(0xbf)) == UInt(0x94) ); + static_assert( std::bit_expand(UInt(0x66), UInt(0x1d)) == UInt(0x0c) ); + } + if constexpr (digits >= 16) + { + static_assert( std::bit_expand(UInt(0xebd4u), UInt(0x93bfu)) == UInt(0x1394) ); + static_assert( std::bit_expand(UInt(0xa166u), UInt(0x001du)) == UInt(0xc) ); + } + if constexpr (digits >= 32) + { + static_assert( std::bit_expand(UInt(0x9daeebd4u), UInt(0xad8793bfu)) == UInt(0xa9851394u) ); + static_assert( std::bit_expand(UInt(0x979ba166u), UInt(0x54ca001du)) == UInt(0x448000cu) ); + } + if constexpr (digits >= 64) + { + static_assert( std::bit_expand(UInt(0x5bd30f069daeebd4ull), UInt(0x64f9a89bad8793bfull)) == UInt(0x4a1a092a9851394ull) ); + static_assert( std::bit_expand(UInt(0xb5672499979ba166ull), UInt(0xd315782354ca001dull)) == UInt(0xd01470200448000cull) ); + } + return x; +} + +static_assert( test( (unsigned char)123 ) ); +static_assert( test( (unsigned short)12345 ) ); +static_assert( test( (unsigned int)1234567890 ) ); +static_assert( test( 123456789UL ) ); +static_assert( test( ~123456789ULL ) ); + +// std::bit_expand(T) shall not participate in overload resolution +// unless T is an unsigned integer type. +struct X { constexpr bool did_not_match() { return true; } }; +constexpr X test(...) { return X{}; } +static_assert( test( (bool)0 ).did_not_match() ); +static_assert( test( (char)0 ).did_not_match() ); +static_assert( test( (int)0 ).did_not_match() ); +static_assert( test( (char16_t)0 ).did_not_match() ); +static_assert( test( (float)0 ).did_not_match() ); +static_assert( test( (void*)0 ).did_not_match() ); +static_assert( test( X{} ).did_not_match() ); +enum E : unsigned { e }; +static_assert( test( e ).did_not_match() ); + +#if !defined(__STRICT_ANSI__) && defined __SIZEOF_INT128__ +static_assert( test( (unsigned __int128)3 ) ); +static_assert( test( (__int128)0 ).did_not_match() ); +#endif +#if defined(__GLIBCXX_TYPE_INT_N_0) +static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_0)1 ) ); +static_assert( test( (__GLIBCXX_TYPE_INT_N_0)0 ).did_not_match() ); +#endif +#if defined(__GLIBCXX_TYPE_INT_N_1) +static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_1)1 ) ); +static_assert( test( (__GLIBCXX_TYPE_INT_N_1)0 ).did_not_match() ); +#endif +#if defined(__GLIBCXX_TYPE_INT_N_2) +static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_2)1 ) ); +static_assert( test( (__GLIBCXX_TYPE_INT_N_2)0 ).did_not_match() ); +#endif +#if defined(__GLIBCXX_TYPE_INT_N_3) +static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_3)1 ) ); +static_assert( test( (__GLIBCXX_TYPE_INT_N_3)0 ).did_not_match() ); +#endif diff --git a/libstdc++-v3/testsuite/26_numerics/bit/bit.permute/bit_repeat.cc b/libstdc++-v3/testsuite/26_numerics/bit/bit.permute/bit_repeat.cc new file mode 100644 index 000000000000..c6270f826121 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/bit/bit.permute/bit_repeat.cc @@ -0,0 +1,125 @@ +// { dg-do compile { target c++29 } } + +#include <bit> +#include <limits> + +// Detect whether std::bit_repeat(T(0), N) is a constant expression. +template<typename T, auto N, typename = void> + struct bit_repeat_valid + : std::false_type { }; + +template<typename T, auto N> + struct bit_repeat_valid<T, N, std::void_t<char[(std::bit_repeat(T(0), N) == T(0))]>> + : std::true_type { }; + +template<typename UInt> +constexpr auto +test(UInt x) +-> decltype(std::bit_repeat(x, 1)) +{ + constexpr unsigned digits = std::numeric_limits<UInt>::digits; + static_assert( std::bit_repeat(UInt(0), 1) == UInt(0) ); + static_assert( std::bit_repeat(UInt(0), digits) == UInt(0) ); + static_assert( std::bit_repeat(UInt(0), digits * 6) == UInt(0) ); + static_assert( std::bit_repeat(UInt(~UInt(0)), 1) == UInt(~UInt(0)) ); + static_assert( std::bit_repeat(UInt(~UInt(0)), digits) == UInt(~UInt(0)) ); + static_assert( std::bit_repeat(UInt(~UInt(0)), digits * 6) == UInt(~UInt(0)) ); + static_assert( std::bit_repeat(UInt(1), 1) == UInt(~UInt(0)) ); + static_assert( std::bit_repeat(UInt(~UInt(1)), 1) == UInt(0) ); + static_assert( std::bit_repeat(std::bit_repeat(UInt(0x0123456789abcdefull), 3), 3) + == std::bit_repeat(UInt(0x0123456789abcdefull), 3) ); + if constexpr (std::numeric_limits<UInt>::digits >= 8) + { + static_assert( (std::bit_repeat<UInt>(0x34, 3) & UInt(0xff)) == 0x24u ); + static_assert( (std::bit_repeat<UInt>(0x71, 2) & UInt(0xff)) == 0x55u ); + static_assert( (std::bit_repeat<UInt>(0xf2, 2) & UInt(0xff)) == 0xaau ); + static_assert( (std::bit_repeat<UInt>(0xb3, 5) & UInt(0xff)) == 0x73u ); + static_assert( (std::bit_repeat<UInt>(0xb3, 8) & UInt(0xff)) == 0xb3u ); + } + if constexpr (std::numeric_limits<UInt>::digits >= 16) + { + static_assert( (std::bit_repeat<UInt>(0x1734, 3) & UInt(0xffff)) == 0x4924u ); + static_assert( (std::bit_repeat<UInt>(0x2071, 2) & UInt(0xffff)) == 0x5555u ); + static_assert( (std::bit_repeat<UInt>(0x52f2, 2) & UInt(0xffff)) == 0xaaaau ); + static_assert( (std::bit_repeat<UInt>(0x07b3, 5) & UInt(0xffff)) == 0xce73u ); + static_assert( (std::bit_repeat<UInt>(0x21b3, 8) & UInt(0xffff)) == 0xb3b3u ); + static_assert( (std::bit_repeat<UInt>(0xa2b3u, 16) & UInt(0xffff)) == 0xa2b3u ); + } + if constexpr (std::numeric_limits<UInt>::digits >= 32) + { + static_assert( (std::bit_repeat<UInt>(0x12345634u, 3) & UInt(0xffffffffu)) == 0x24924924u ); + static_assert( (std::bit_repeat<UInt>(0x23456771u, 2) & UInt(0xffffffffu)) == 0x55555555u ); + static_assert( (std::bit_repeat<UInt>(0x345678f2u, 2) & UInt(0xffffffffu)) == 0xaaaaaaaau ); + static_assert( (std::bit_repeat<UInt>(0x456789b3u, 5) & UInt(0xffffffffu)) == 0xe739ce73u ); + static_assert( (std::bit_repeat<UInt>(0x56789ab3u, 8) & UInt(0xffffffffu)) == 0xb3b3b3b3u ); + static_assert( (std::bit_repeat<UInt>(0x6789a2b3u, 16) & UInt(0xffffffffu)) == 0xa2b3a2b3u ); + static_assert( (std::bit_repeat<UInt>(0x2a4dda0eu, 32) & UInt(0xffffffffu)) == 0x2a4dda0eu ); + } + if constexpr (std::numeric_limits<UInt>::digits >= 64) + { + static_assert( (std::bit_repeat<UInt>(0x1122334455667734ull, 3) & UInt(0xffffffffffffffffull)) == 0x4924924924924924ull ); + static_assert( (std::bit_repeat<UInt>(0x2233445566778871ull, 2) & UInt(0xffffffffffffffffull)) == 0x5555555555555555ull ); + static_assert( (std::bit_repeat<UInt>(0x33445566778899f2ull, 2) & UInt(0xffffffffffffffffull)) == 0xaaaaaaaaaaaaaaaaull ); + static_assert( (std::bit_repeat<UInt>(0x445566778899aab3ull, 5) & UInt(0xffffffffffffffffull)) == 0x39ce739ce739ce73ull ); + static_assert( (std::bit_repeat<UInt>(0x5566778899aabbb3ull, 8) & UInt(0xffffffffffffffffull)) == 0xb3b3b3b3b3b3b3b3ull ); + static_assert( (std::bit_repeat<UInt>(0x66778899aabba2b3ull, 16) & UInt(0xffffffffffffffffull)) == 0xa2b3a2b3a2b3a2b3ull ); + static_assert( (std::bit_repeat<UInt>(0x778899aa2a4dda0eull, 32) & UInt(0xffffffffffffffffull)) == 0x2a4dda0e2a4dda0eull ); + } + if constexpr (std::numeric_limits<UInt>::digits == 128) + { + static_assert( std::bit_repeat<UInt>(1, 2) + == (UInt(0x5555555555555555ull) << 64) | 0x5555555555555555ull ); + static_assert( std::bit_repeat<UInt>(2, 2) + == (UInt(0xaaaaaaaaaaaaaaaaull) << 64) | 0xaaaaaaaaaaaaaaaaull ); + } + static_assert( bit_repeat_valid<UInt, 1>() ); + static_assert( bit_repeat_valid<UInt, 17>() ); + static_assert( bit_repeat_valid<UInt, digits>() ); + static_assert( bit_repeat_valid<UInt, digits * 8>() ); + static_assert( !bit_repeat_valid<UInt, 0>() ); + static_assert( !bit_repeat_valid<UInt, -1>() ); + static_assert( !bit_repeat_valid<UInt, -5>() ); + + return x; +} + +static_assert( test( (unsigned char)123 ) ); +static_assert( test( (unsigned short)12345 ) ); +static_assert( test( (unsigned int)1234567890 ) ); +static_assert( test( 123456789UL ) ); +static_assert( test( ~123456789ULL ) ); + +// std::bit_repeat(T, N) shall not participate in overload resolution +// unless T is an unsigned integer type. +struct X { constexpr bool did_not_match() { return true; } }; +constexpr X test(...) { return X{}; } +static_assert( test( (bool)0 ).did_not_match() ); +static_assert( test( (char)0 ).did_not_match() ); +static_assert( test( (int)0 ).did_not_match() ); +static_assert( test( (char16_t)0 ).did_not_match() ); +static_assert( test( (float)0 ).did_not_match() ); +static_assert( test( (void*)0 ).did_not_match() ); +static_assert( test( X{} ).did_not_match() ); +enum E : unsigned { e }; +static_assert( test( e ).did_not_match() ); + +#if !defined(__STRICT_ANSI__) && defined __SIZEOF_INT128__ +static_assert( test( (unsigned __int128)3 ) ); +static_assert( test( (__int128)0 ).did_not_match() ); +#endif +#if defined(__GLIBCXX_TYPE_INT_N_0) +static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_0)1 ) ); +static_assert( test( (__GLIBCXX_TYPE_INT_N_0)0 ).did_not_match() ); +#endif +#if defined(__GLIBCXX_TYPE_INT_N_1) +static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_1)1 ) ); +static_assert( test( (__GLIBCXX_TYPE_INT_N_1)0 ).did_not_match() ); +#endif +#if defined(__GLIBCXX_TYPE_INT_N_2) +static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_2)1 ) ); +static_assert( test( (__GLIBCXX_TYPE_INT_N_2)0 ).did_not_match() ); +#endif +#if defined(__GLIBCXX_TYPE_INT_N_3) +static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_3)1 ) ); +static_assert( test( (__GLIBCXX_TYPE_INT_N_3)0 ).did_not_match() ); +#endif diff --git a/libstdc++-v3/testsuite/26_numerics/bit/bit.permute/bit_reverse.cc b/libstdc++-v3/testsuite/26_numerics/bit/bit.permute/bit_reverse.cc new file mode 100644 index 000000000000..babb848dbe6e --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/bit/bit.permute/bit_reverse.cc @@ -0,0 +1,73 @@ +// { dg-do compile { target c++29 } } + +#include <bit> +#include <limits> + +template<typename UInt> +constexpr auto +test(UInt x) +-> decltype(std::bit_reverse(x)) +{ + constexpr unsigned digits = std::numeric_limits<UInt>::digits; + static_assert( std::bit_reverse(UInt(0)) == UInt(0) ); + static_assert( std::bit_reverse(UInt(~UInt(0))) == UInt(~UInt(0)) ); + static_assert( std::bit_reverse(UInt(1)) == UInt(1) << (digits - 1) ); + static_assert( std::bit_reverse(UInt(UInt(1) << (digits - 1))) == UInt(1) ); + if constexpr (std::numeric_limits<UInt>::digits == 8) + static_assert( std::bit_reverse<UInt>(0x34u) == 0x2cu ); + if constexpr (std::numeric_limits<UInt>::digits == 16) + static_assert( std::bit_reverse<UInt>(0x1234u) == 0x2c48u ); + if constexpr (std::numeric_limits<UInt>::digits == 32) + static_assert( std::bit_reverse<UInt>(0x01234567u) == 0xe6a2c480u ); + if constexpr (std::numeric_limits<UInt>::digits == 64) + static_assert( std::bit_reverse<UInt>(0x0123456789abcdefull) == 0xf7b3d591e6a2c480ull ); + if constexpr (std::numeric_limits<UInt>::digits == 128) + static_assert( std::bit_reverse<UInt>((UInt(0x0123456789abcdefull) << 64) + | 0x2468ace013579bdfull) + == ((UInt(0xfbd9eac807351624ull) << 64) + | 0xf7b3d591e6a2c480ull) ); + if (std::bit_reverse(std::bit_reverse(x)) == x) + return x; + return 0; +} + +static_assert( test( (unsigned char)123 ) ); +static_assert( test( (unsigned short)12345 ) ); +static_assert( test( (unsigned int)1234567890 ) ); +static_assert( test( 123456789UL ) ); +static_assert( test( ~123456789ULL ) ); + +// std::bit_reverse(T) shall not participate in overload resolution +// unless T is an unsigned integer type. +struct X { constexpr bool did_not_match() { return true; } }; +constexpr X test(...) { return X{}; } +static_assert( test( (bool)0 ).did_not_match() ); +static_assert( test( (char)0 ).did_not_match() ); +static_assert( test( (int)0 ).did_not_match() ); +static_assert( test( (char16_t)0 ).did_not_match() ); +static_assert( test( (float)0 ).did_not_match() ); +static_assert( test( (void*)0 ).did_not_match() ); +static_assert( test( X{} ).did_not_match() ); +enum E : unsigned { e }; +static_assert( test( e ).did_not_match() ); + +#if !defined(__STRICT_ANSI__) && defined __SIZEOF_INT128__ +static_assert( test( (unsigned __int128)3 ) ); +static_assert( test( (__int128)0 ).did_not_match() ); +#endif +#if defined(__GLIBCXX_TYPE_INT_N_0) +static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_0)1 ) ); +static_assert( test( (__GLIBCXX_TYPE_INT_N_0)0 ).did_not_match() ); +#endif +#if defined(__GLIBCXX_TYPE_INT_N_1) +static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_1)1 ) ); +static_assert( test( (__GLIBCXX_TYPE_INT_N_1)0 ).did_not_match() ); +#endif +#if defined(__GLIBCXX_TYPE_INT_N_2) +static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_2)1 ) ); +static_assert( test( (__GLIBCXX_TYPE_INT_N_2)0 ).did_not_match() ); +#endif +#if defined(__GLIBCXX_TYPE_INT_N_3) +static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_3)1 ) ); +static_assert( test( (__GLIBCXX_TYPE_INT_N_3)0 ).did_not_match() ); +#endif
