Skip to content

Commit 79caa06

Browse files
authored
[libc++][bit] Improves rotate functions. (#98032)
Investigating #96612 shows our implementation was different from the Standard and could cause UB. Testing the codegen showed quite a bit of assembly generated for these functions. The functions have been written differently which allows Clang to optimize the code to use simple CPU rotate instructions. Fixes: #96612
1 parent 879640c commit 79caa06

File tree

3 files changed

+33
-12
lines changed

3 files changed

+33
-12
lines changed

libcxx/include/__bit/rotate.h

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,24 +20,37 @@
2020

2121
_LIBCPP_BEGIN_NAMESPACE_STD
2222

23+
// Writing two full functions for rotl and rotr makes it easier for the compiler
24+
// to optimize the code. On x86 this function becomes the ROL instruction and
25+
// the rotr function becomes the ROR instruction.
2326
template <class _Tp>
24-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp __rotr(_Tp __t, int __cnt) _NOEXCEPT {
25-
static_assert(__libcpp_is_unsigned_integer<_Tp>::value, "__rotr requires an unsigned integer type");
26-
const unsigned int __dig = numeric_limits<_Tp>::digits;
27-
if ((__cnt % __dig) == 0)
28-
return __t;
27+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp __rotl(_Tp __x, int __s) _NOEXCEPT {
28+
static_assert(__libcpp_is_unsigned_integer<_Tp>::value, "__rotl requires an unsigned integer type");
29+
const int __N = numeric_limits<_Tp>::digits;
30+
int __r = __s % __N;
31+
32+
if (__r == 0)
33+
return __x;
2934

30-
if (__cnt < 0) {
31-
__cnt *= -1;
32-
return (__t << (__cnt % __dig)) | (__t >> (__dig - (__cnt % __dig))); // rotr with negative __cnt is similar to rotl
33-
}
35+
if (__r > 0)
36+
return (__x << __r) | (__x >> (__N - __r));
3437

35-
return (__t >> (__cnt % __dig)) | (__t << (__dig - (__cnt % __dig)));
38+
return (__x >> -__r) | (__x << (__N + __r));
3639
}
3740

3841
template <class _Tp>
39-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp __rotl(_Tp __t, int __cnt) _NOEXCEPT {
40-
return std::__rotr(__t, -__cnt);
42+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp __rotr(_Tp __x, int __s) _NOEXCEPT {
43+
static_assert(__libcpp_is_unsigned_integer<_Tp>::value, "__rotr requires an unsigned integer type");
44+
const int __N = numeric_limits<_Tp>::digits;
45+
int __r = __s % __N;
46+
47+
if (__r == 0)
48+
return __x;
49+
50+
if (__r > 0)
51+
return (__x >> __r) | (__x << (__N - __r));
52+
53+
return (__x << -__r) | (__x >> (__N + __r));
4154
}
4255

4356
#if _LIBCPP_STD_VER >= 20

libcxx/test/std/numerics/bit/bitops.rot/rotl.pass.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ constexpr bool test()
4141
assert(std::rotl(T(max - 1), 5) == T(max - 32));
4242
assert(std::rotl(T(max - 1), 6) == T(max - 64));
4343
assert(std::rotl(T(max - 1), 7) == T(max - 128));
44+
assert(std::rotl(T(max - 1), std::numeric_limits<int>::max()) ==
45+
std::rotl(T(max - 1), std::numeric_limits<int>::max() % std::numeric_limits<T>::digits));
4446

4547
assert(std::rotl(T(max - 1), -1) == T(max - highbit));
4648
assert(std::rotl(T(max - 1), -2) == T(max - (highbit >> 1)));
@@ -49,6 +51,8 @@ constexpr bool test()
4951
assert(std::rotl(T(max - 1), -5) == T(max - (highbit >> 4)));
5052
assert(std::rotl(T(max - 1), -6) == T(max - (highbit >> 5)));
5153
assert(std::rotl(T(max - 1), -7) == T(max - (highbit >> 6)));
54+
assert(std::rotl(T(max - 1), std::numeric_limits<int>::min()) ==
55+
std::rotl(T(max - 1), std::numeric_limits<int>::min() % std::numeric_limits<T>::digits));
5256

5357
assert(std::rotl(T(1), 0) == T(1));
5458
assert(std::rotl(T(1), 1) == T(2));

libcxx/test/std/numerics/bit/bitops.rot/rotr.pass.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ constexpr bool test()
4141
assert(std::rotr(T(max - 1), 5) == T(max - (highbit >> 4)));
4242
assert(std::rotr(T(max - 1), 6) == T(max - (highbit >> 5)));
4343
assert(std::rotr(T(max - 1), 7) == T(max - (highbit >> 6)));
44+
assert(std::rotr(T(max - 1), std::numeric_limits<int>::max()) ==
45+
std::rotr(T(max - 1), std::numeric_limits<int>::max() % std::numeric_limits<T>::digits));
4446

4547
assert(std::rotr(T(max - 1), -1) == T(max - 2));
4648
assert(std::rotr(T(max - 1), -2) == T(max - 4));
@@ -49,6 +51,8 @@ constexpr bool test()
4951
assert(std::rotr(T(max - 1), -5) == T(max - 32));
5052
assert(std::rotr(T(max - 1), -6) == T(max - 64));
5153
assert(std::rotr(T(max - 1), -7) == T(max - 128));
54+
assert(std::rotr(T(max - 1), std::numeric_limits<int>::min()) ==
55+
std::rotr(T(max - 1), std::numeric_limits<int>::min() % std::numeric_limits<T>::digits));
5256

5357
assert(std::rotr(T(128), 0) == T(128));
5458
assert(std::rotr(T(128), 1) == T(64));

0 commit comments

Comments
 (0)