std::simd overloads for bit permutations

Document number:
P3772R1
Date:
2025-11-13
Audience:
LEWG
Project:
ISO/IEC 14882 Programming Languages — C++, ISO/IEC JTC1/SC22/WG21
Reply-to:
Jan Schultke <janschultkegmail.com>
GitHub Issue:
wg21.link/P3772/github
Source:
github.com/Eisenwave/cpp-proposals/blob/master/src/bit-permutations-simd.cow

[P3104R4] has been been design-approved by LEWG for C++29. This proposal adds std::simd overloads to add consistency with [P2933R4].

Contents

1

Revision history

2

Introduction

3

Design

4

Implementation experience

5

Wording

6

References

1. Revision history

2. Introduction

[P3104R4] has been been design-approved by LEWG for C++29. During the discussion of the paper, it was not mentioned that [P2933R4] adds std::simd overloads for almost all functions in the <bit> header.

For the purpose of consistency, this proposal adds these overloads.

This proposal has an extremely minimal motivation and design discussion. That is because I see it as an obvious extensions of the existing design, and possibly a bug fix.

Should the need arise, these topics could be explored more deeply.

3. Design

Some of the design in this proposal has direct equivalents:

The signatures of the the new and respective existing functions are identical. std::bit_compress and std::bit_expand are binary operations between two unsigned integers of the same type, so naturally, std::simd::bit_compress and std::simd::bit_expand are binary operations between two std::simd::vecs of the same type.

All added function are noexcept, except the simd::bit_repeat overloads. That is because std::bit_repeat is not noexcept either; it has a narrow contract.

4. Implementation experience

None.

5. Wording

In [version.syn], bump the feature-test macro:

#define __cpp_lib_simd 202506L 20XXXXL // also in <simd>

In [simd.syn], change the synopsis as follows:

namespace std::simd { […] // [simd.bit], bit manipulation template<simd-vec-type V> constexpr V byteswap(const V& v) noexcept; template<simd-vec-type V> constexpr V bit_reverse(const V& v) noexcept; template<simd-vec-type V> constexpr V bit_ceil(const V& v) noexcept; template<simd-vec-type V> constexpr V bit_floor(const V& v) noexcept; template<simd-vec-type V> constexpr typename V::mask_type has_single_bit(const V& v) noexcept; template<simd-vec-type V0, simd-vec-type V1> constexpr V0 rotl(const V0& v, const V1& s) noexcept; template<simd-vec-type V> constexpr V rotl(const V& v, int s) noexcept; template<simd-vec-type V0, simd-vec-type V1> constexpr V0 rotr(const V0& v, const V1& s) noexcept; template<simd-vec-type V> constexpr V rotr(const V& v, int s) noexcept; template<simd-vec-type V0, simd-vec-type V1> constexpr V0 bit_repeat(const V0& v, const V1& l); template<simd-vec-type V> constexpr V bit_repeat(const V& v, int l); template<simd-vec-type V> constexpr rebind_t<make_signed_t<typename V::value_type>, V> bit_width(const V& v) noexcept; template<simd-vec-type V> constexpr rebind_t<make_signed_t<typename V::value_type>, V> countl_zero(const V& v) noexcept; template<simd-vec-type V> constexpr rebind_t<make_signed_t<typename V::value_type>, V> countl_one(const V& v) noexcept; template<simd-vec-type V> constexpr rebind_t<make_signed_t<typename V::value_type>, V> countr_zero(const V& v) noexcept; template<simd-vec-type V> constexpr rebind_t<make_signed_t<typename V::value_type>, V> countr_one(const V& v) noexcept; template<simd-vec-type V> constexpr rebind_t<make_signed_t<typename V::value_type>, V> popcount(const V& v) noexcept; template<simd-vec-type V> constexpr V bit_compress(const V& v, const V& m) noexcept; template<simd-vec-type V> constexpr V bit_expand(const V& v, const V& m) noexcept; template<simd-vec-type V> constexpr V bit_compress(const V& v, typename V::value_type m) noexcept; template<simd-vec-type V> constexpr V bit_expand(const V& v, typename V::value_type m) noexcept; […] } namespace std { […] // [simd.bit], bit manipulation using simd::byteswap; using simd::bit_ceil; using simd::bit_floor; using simd::bit_reverse; using simd::has_single_bit; using simd::rotl; using simd::rotr; using simd::bit_repeat; using simd::bit_width; using simd::countl_zero; using simd::countl_one; using simd::countr_zero; using simd::countr_one; using simd::popcount; using simd::bit_compress; using simd::bit_expand; […] }

Change [simd.bit] as follows:

Bit manipulation [simd.bit]

template<simd-vec-type V> constexpr V byteswap(const V& v) noexcept;

1 Constraints: The type V::value_type models integral.

2 Returns: A basic_vec object where the ith element is initialized to the result of std::byteswap(v[i]) for all i in the range [0, V::size()).

template<simd-vec-type V> constexpr V bit_reverse(const V& v) noexcept;

Constraints: The type V::value_type is an unsigned integer type ([basic.fundamental]).

Returns: A basic_vec object where the ith element is initialized to the result of std::bit_reverse(v[i]) for all i in the range [0, V::size()).

template<simd-vec-type V> constexpr V bit_ceil(const V& v) noexcept;

3 Constraints: The type V::value_type is an unsigned integer type ([basic.fundamental]).

4 Preconditions: For every i in the range [0, V::size()), the smallest power of 2 greater than or equal to v[i] is representable as a value of type V::value_type.

5 Returns: A basic_vec object where the ith element is initialized to the result of std::bit_ceil(v[i]) for all i in the range [0, V::size()).

6 Remarks: A function call expression that violates the precondition in the Preconditions: element is not a core constant expression ([expr.const]).

template<simd-vec-type V> constexpr V bit_floor(const V& v) noexcept;

7 Constraints: The type V::value_type is an unsigned integer type ([basic.fundamental]).

8 Returns: A basic_vec object where the ith element is initialized to the result of std::bit_floor(v[i]) for all i in the range [0, V::size()).

template<simd-vec-type V> constexpr typename V::mask_type has_single_bit(const V& v) noexcept;

9 Constraints: The type V::value_type is an unsigned integer type ([basic.fundamental]).

10 Returns: A basic_mask object where the ith element is initialized to the result of std::has_single_bit(v[i]) for all i in the range [0, V::size()).

template<simd-vec-type V0, simd-vec-type V1> constexpr V0 rotl(const V0& v0, const V1& v1) noexcept; template<simd-vec-type V0, simd-vec-type V1> constexpr V0 rotr(const V0& v0, const V1& v1) noexcept; template<simd-vec-type V0, simd-vec-type V1> constexpr V0 bit_repeat(const V0& v0, const V1& v1);

11 Constraints:

  • The type V0::value_type is an unsigned integer type ([basic.fundamental]),
  • the type V1::value_type models integral,
  • V0::size() == V1::size() is true, and
  • sizeof(typename V0::value_type) == sizeof(typename V1::value_type) is true.

Preconditions: For bit_repeat, v1[i] > 0 is true for all i in the range [0, V0::size()).

12 Returns: A basic_vec object where the ith element is initialized to the result of bit-func(v0[i], static_cast<int>(v1[i])) for all i in the range [0, V0::size()), where bit-func is the corresponding scalar function from <bit>.

template<simd-vec-type V> constexpr V rotl(const V& v, int s) noexcept; template<simd-vec-type V> constexpr V rotr(const V& v, int s) noexcept; template<simd-vec-type V> constexpr V bit_repeat(const V& v, int l);

13 Constraints: The type V::value_type is an unsigned integer type ([basic.fundamental]).

Preconditions: For bit_repeat, l > 0 is true.

14 Returns: A basic_vec object where the ith element is initialized to the result of bit-func(v[i], s) for all i in the range [0, V::size()), where bit-func is the corresponding scalar function from <bit>.

template<simd-vec-type V> constexpr rebind_t<make_signed_t<typename V::value_type>, V> bit_width(const V& v) noexcept; template<simd-vec-type V> constexpr rebind_t<make_signed_t<typename V::value_type>, V> countl_zero(const V& v) noexcept; template<simd-vec-type V> constexpr rebind_t<make_signed_t<typename V::value_type>, V> countl_one(const V& v) noexcept; template<simd-vec-type V> constexpr rebind_t<make_signed_t<typename V::value_type>, V> countr_zero(const V& v) noexcept; template<simd-vec-type V> constexpr rebind_t<make_signed_t<typename V::value_type>, V> countr_one(const V& v) noexcept; template<simd-vec-type V> constexpr rebind_t<make_signed_t<typename V::value_type>, V> popcount(const V& v) noexcept;

15 Constraints: The type V::value_type is an unsigned integer type ([basic.fundamental]).

16 Returns: A basic_vec object where the ith element is initialized to the result of bit-func(v[i]) for all i in the range [0, V::size()), where bit-func is the corresponding scalar function from <bit>.

template<simd-vec-type V> constexpr V bit_compress(const V& v, const V& m) noexcept; template<simd-vec-type V> constexpr V bit_expand(const V& v, const V& m) noexcept;

Constraints: The type V::value_type is an unsigned integer type ([basic.fundamental]).

Returns: A basic_vec object where the ith element is initialized to the result of bit-func(v[i], m[i]) for all i in the range [0, V::size()), where bit-func is the corresponding scalar function from <bit>.

template<simd-vec-type V> constexpr V bit_compress(const V& v, typename V::value_type m) noexcept; template<simd-vec-type V> constexpr V bit_expand(const V& v, typename V::value_type m) noexcept;

Constraints: The type V::value_type is an unsigned integer type ([basic.fundamental]).

Returns: A basic_vec object where the ith element is initialized to the result of bit-func(v[i], m) for all i in the range [0, V::size()), where bit-func is the corresponding scalar function from <bit>.

6. References

[N5014] Thomas Köppe. Working Draft, Programming Languages — C++ 2025-08-05 https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/n5014.pdf
[P2933R4] Daniel Towner, Ruslan Arutyunyan. Extend <bit> header function with overloads for std::simd 2025-02-13 https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2933r4.html