blob: d5ece7e00cd422750dc7aee18a99659c43d0830c [file] [log] [blame]
/*
* Copyright (c) 2023, Google and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_UTILITIES_BYTESWAP_HPP
#define SHARE_UTILITIES_BYTESWAP_HPP
#include "metaprogramming/enableIf.hpp"
#include "utilities/globalDefinitions.hpp"
#include <cstddef>
#include <cstdint>
#include <type_traits>
template <typename T, size_t N = sizeof(T)>
struct ByteswapImpl;
// T byteswap<T>(T)
//
// Reverses the bytes for the value of the integer type T. Partially compatible with std::byteswap
// introduced in C++23.
template <typename T, ENABLE_IF(std::is_integral<T>::value)>
inline T byteswap(T x) {
using U = std::make_unsigned_t<T>;
return static_cast<T>(ByteswapImpl<U>{}(static_cast<U>(x)));
}
// We support 8-bit integer types to be compatible with C++23's std::byteswap.
template <typename T>
struct ByteswapImpl<T, 1> {
inline constexpr T operator()(T x) const {
return x;
}
};
/*****************************************************************************
* Fallback
*****************************************************************************/
template <typename T, size_t N = sizeof(T)>
struct ByteswapFallbackImpl;
template <typename T>
struct ByteswapFallbackImpl<T, 2> {
inline constexpr uint16_t operator()(uint16_t x) const {
return (((x & UINT16_C(0x00ff)) << 8) | ((x & UINT16_C(0xff00)) >> 8));
}
};
template <typename T>
struct ByteswapFallbackImpl<T, 4> {
inline constexpr uint32_t operator()(uint32_t x) const {
return (((x & UINT32_C(0x000000ff)) << 24) | ((x & UINT32_C(0x0000ff00)) << 8) |
((x & UINT32_C(0x00ff0000)) >> 8) | ((x & UINT32_C(0xff000000)) >> 24));
}
};
template <typename T>
struct ByteswapFallbackImpl<T, 8> {
inline constexpr uint64_t operator()(uint64_t x) const {
return (((x & UINT64_C(0x00000000000000ff)) << 56) | ((x & UINT64_C(0x000000000000ff00)) << 40) |
((x & UINT64_C(0x0000000000ff0000)) << 24) | ((x & UINT64_C(0x00000000ff000000)) << 8) |
((x & UINT64_C(0x000000ff00000000)) >> 8) | ((x & UINT64_C(0x0000ff0000000000)) >> 24) |
((x & UINT64_C(0x00ff000000000000)) >> 40) | ((x & UINT64_C(0xff00000000000000)) >> 56));
}
};
/*****************************************************************************
* GCC and compatible (including Clang)
*****************************************************************************/
#if defined(TARGET_COMPILER_gcc) || defined(TARGET_COMPILER_xlc)
#if defined(__clang__) || defined(ASSERT)
// Unlike GCC, Clang is willing to inline the generic implementation of __builtin_bswap when
// architecture support is unavailable in -O2. This ensures we avoid the function call to libgcc.
// Clang is able to recognize the fallback implementation as byteswapping, but not on every
// architecture unlike GCC. This suggests the optimization pass for GCC that recognizes byteswapping
// is architecture agnostic, while for Clang it is not.
template <typename T>
struct ByteswapImpl<T, 2> {
inline constexpr uint16_t operator()(uint16_t x) const {
return __builtin_bswap16(x);
}
};
template <typename T>
struct ByteswapImpl<T, 4> {
inline constexpr uint32_t operator()(uint32_t x) const {
return __builtin_bswap32(x);
}
};
template <typename T>
struct ByteswapImpl<T, 8> {
inline constexpr uint64_t operator()(uint64_t x) const {
return __builtin_bswap64(x);
}
};
#else
// We do not use __builtin_bswap and friends for GCC in release builds. Unfortunately on
// architectures that do not have a byteswap instruction (i.e. RISC-V), GCC emits a function call to
// libgcc regardless of optimization options, even when the generic implementation is, for example,
// less than 20 instructions. GCC is however able to recognize the fallback as byteswapping
// regardless of architecture and appropriately replaces the code in -O2 with the appropriate
// architecture-specific byteswap instruction, if available. If it is not available, GCC emits the
// exact same implementation that underpins its __builtin_bswap in libgcc as there is really only
// one way to implement it, as we have in fallback.
template <typename T, size_t N>
struct ByteswapImpl : public ByteswapFallbackImpl<T, N> {};
#endif
/*****************************************************************************
* Microsoft Visual Studio
*****************************************************************************/
#elif defined(TARGET_COMPILER_visCPP)
#include <cstdlib>
#pragma intrinsic(_byteswap_ushort)
#pragma intrinsic(_byteswap_ulong)
#pragma intrinsic(_byteswap_uint64)
template <typename T>
struct ByteswapImpl<T, 2> {
inline unsigned short operator()(unsigned short x) const {
return _byteswap_ushort(x);
}
};
template <typename T>
struct ByteswapImpl<T, 4> {
inline unsigned long operator()(unsigned long x) const {
return _byteswap_ulong(x);
}
};
template <typename T>
struct ByteswapImpl<T, 8> {
inline unsigned __int64 operator()(unsigned __int64 x) const {
return _byteswap_uint64(x);
}
};
/*****************************************************************************
* Unknown toolchain
*****************************************************************************/
#else
#error Unknown toolchain.
#endif
#endif // SHARE_UTILITIES_BYTESWAP_HPP