//===----------------------------------------------------------------------===// // // Part of libcu++, the C++ Standard Library for your entire system, // under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPO__RATION & AFFILIATES. // //===----------------------------------------------------------------------===// // MIT License // // Modifications Copyright (C) 2025 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #ifndef _CUDA___WARP_WARP_SHUFFLE_H #define _CUDA___WARP_WARP_SHUFFLE_H #include #if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC) # pragma GCC system_header #elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG) # pragma clang system_header #elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC) # pragma system_header #endif // no system header #include #include #include #include #include #include #include #include #include #include #if __cccl_ptx_isa >= 600 #include #include _LIBCUDACXX_BEGIN_NAMESPACE_CUDA_DEVICE template struct warp_shuffle_result { _Tp data; bool pred; template _CCCL_NODISCARD _CCCL_HIDE_FROM_ABI _CCCL_DEVICE operator cuda::std::enable_if_t, _Up>() const { return data; } }; template > _CCCL_NODISCARD _CCCL_HIDE_FROM_ABI _CCCL_DEVICE warp_shuffle_result<_Up> warp_shuffle_idx( const _Tp& __data, int __src_lane, uint32_t __lane_mask = 0xFFFFFFFF, _CUDA_VSTD::integral_constant = {}) { constexpr auto __warp_size = 32u; constexpr bool __is_void_ptr = _CUDA_VSTD::is_same_v<_Up, void*> || _CUDA_VSTD::is_same_v<_Up, const void*>; static_assert(!_CUDA_VSTD::is_pointer_v<_Up> || __is_void_ptr, "non-void pointers are not allowed to prevent bug-prone code"); static_assert(_CUDA_VSTD::has_single_bit(uint32_t{_Width}) && _Width >= 1 && _Width <= __warp_size, "_Width must be a power of 2 and less or equal to the warp size"); if constexpr (_Width == 1) { return warp_shuffle_result<_Up>{__data, true}; } else { constexpr int __ratio = ::cuda::ceil_div(sizeof(_Up), sizeof(uint32_t)); auto __clamp_segmask = (_Width - 1u) | ((__warp_size - _Width) << 8); bool __pred; uint32_t __array[__ratio]; _CUDA_VSTD::memcpy( static_cast(__array), static_cast(_CUDA_VSTD::addressof(__data)), sizeof(_Up)); _CCCL_PRAGMA_UNROLL_FULL() for (int i = 0; i < __ratio; ++i) { __array[i] = _CUDA_VPTX::shfl_sync_idx(__array[i], __pred, __src_lane, __clamp_segmask, __lane_mask); } warp_shuffle_result<_Up> __result; __result.pred = __pred; _CUDA_VSTD::memcpy( static_cast(_CUDA_VSTD::addressof(__result.data)), static_cast(__array), sizeof(_Up)); return __result; } } template > _CCCL_NODISCARD _CCCL_HIDE_FROM_ABI _CCCL_DEVICE warp_shuffle_result<_Up> warp_shuffle_idx(const _Tp& __data, int __src_lane, _CUDA_VSTD::integral_constant __width) { return ::cuda::device::warp_shuffle_idx(__data, __src_lane, 0xFFFFFFFF, __width); } template > _CCCL_NODISCARD _CCCL_HIDE_FROM_ABI _CCCL_DEVICE warp_shuffle_result<_Tp> warp_shuffle_up( const _Tp& __data, int __delta, uint32_t __lane_mask = 0xFFFFFFFF, _CUDA_VSTD::integral_constant = {}) { constexpr auto __warp_size = 32u; constexpr bool __is_void_ptr = _CUDA_VSTD::is_same_v<_Up, void*> || _CUDA_VSTD::is_same_v<_Up, const void*>; static_assert(!_CUDA_VSTD::is_pointer_v<_Up> || __is_void_ptr, "non-void pointers are not allowed to prevent bug-prone code"); static_assert(_CUDA_VSTD::has_single_bit(uint32_t{_Width}) && _Width >= 1 && _Width <= __warp_size, "_Width must be a power of 2 and less or equal to the warp size"); # if __CUDA_ARCH__ >= 700 [[maybe_unused]] int __pred1; _CCCL_ASSERT(__match_all_sync(__activemask(), __delta, &__pred1), "all active lanes must have the same delta"); # endif if constexpr (_Width == 1) { _CCCL_ASSERT(__delta == 0, "delta must be 0 when Width == 1"); return warp_shuffle_result<_Up>{__data, true}; } else { _CCCL_ASSERT(__delta >= 1 && __delta < _Width, "delta must be in the range [1, _Width)"); constexpr int __ratio = ::cuda::ceil_div(sizeof(_Up), sizeof(uint32_t)); auto __clamp_segmask = (__warp_size - _Width) << 8; bool __pred; uint32_t __array[__ratio]; _CUDA_VSTD::memcpy( static_cast(__array), static_cast(_CUDA_VSTD::addressof(__data)), sizeof(_Up)); _CCCL_PRAGMA_UNROLL_FULL() for (int i = 0; i < __ratio; ++i) { __array[i] = _CUDA_VPTX::shfl_sync_up(__array[i], __pred, __delta, __clamp_segmask, __lane_mask); } warp_shuffle_result<_Up> __result; __result.pred = __pred; _CUDA_VSTD::memcpy( static_cast(_CUDA_VSTD::addressof(__result.data)), static_cast(__array), sizeof(_Up)); return __result; } } template > _CCCL_NODISCARD _CCCL_HIDE_FROM_ABI _CCCL_DEVICE warp_shuffle_result<_Up> warp_shuffle_up(const _Tp& __data, int __src_lane, _CUDA_VSTD::integral_constant __width) { return ::cuda::device::warp_shuffle_up(__data, __src_lane, 0xFFFFFFFF, __width); } template > _CCCL_NODISCARD _CCCL_HIDE_FROM_ABI _CCCL_DEVICE warp_shuffle_result<_Up> warp_shuffle_down( const _Tp& __data, int __delta, uint32_t __lane_mask = 0xFFFFFFFF, _CUDA_VSTD::integral_constant = {}) { constexpr auto __warp_size = 32u; constexpr bool __is_void_ptr = _CUDA_VSTD::is_same_v<_Up, void*> || _CUDA_VSTD::is_same_v<_Up, const void*>; static_assert(!_CUDA_VSTD::is_pointer_v<_Up> || __is_void_ptr, "non-void pointers are not allowed to prevent bug-prone code"); static_assert(_CUDA_VSTD::has_single_bit(uint32_t{_Width}) && _Width >= 1 && _Width <= __warp_size, "_Width must be a power of 2 and less or equal to the warp size"); # if __CUDA_ARCH__ >= 700 [[maybe_unused]] int __pred1; _CCCL_ASSERT(__match_all_sync(__activemask(), __delta, &__pred1), "all active lanes must have the same delta"); # endif if constexpr (_Width == 1) { _CCCL_ASSERT(__delta == 0, "delta must be 0 when Width == 1"); return warp_shuffle_result<_Up>{__data, true}; } else { _CCCL_ASSERT(__delta >= 1 && __delta < _Width, "delta must be in the range [1, _Width)"); constexpr int __ratio = ::cuda::ceil_div(sizeof(_Up), sizeof(uint32_t)); auto __clamp_segmask = (_Width - 1u) | ((__warp_size - _Width) << 8); bool __pred; uint32_t __array[__ratio]; _CUDA_VSTD::memcpy( static_cast(__array), static_cast(_CUDA_VSTD::addressof(__data)), sizeof(_Up)); _CCCL_PRAGMA_UNROLL_FULL() for (int i = 0; i < __ratio; ++i) { __array[i] = _CUDA_VPTX::shfl_sync_down(__array[i], __pred, __delta, __clamp_segmask, __lane_mask); } warp_shuffle_result<_Up> __result; __result.pred = __pred; _CUDA_VSTD::memcpy( static_cast(_CUDA_VSTD::addressof(__result.data)), static_cast(__array), sizeof(_Up)); return __result; } } template > _CCCL_NODISCARD _CCCL_HIDE_FROM_ABI _CCCL_DEVICE warp_shuffle_result<_Tp> warp_shuffle_down(const _Tp& __data, int __src_lane, _CUDA_VSTD::integral_constant __width) { return ::cuda::device::warp_shuffle_down(__data, __src_lane, 0xFFFFFFFF, __width); } template > _CCCL_NODISCARD _CCCL_HIDE_FROM_ABI _CCCL_DEVICE warp_shuffle_result<_Up> warp_shuffle_xor( const _Tp& __data, int __xor_mask, uint32_t __lane_mask = 0xFFFFFFFF, _CUDA_VSTD::integral_constant = {}) { constexpr auto __warp_size = 32u; constexpr bool __is_void_ptr = _CUDA_VSTD::is_same_v<_Up, void*> || _CUDA_VSTD::is_same_v<_Up, const void*>; static_assert(!_CUDA_VSTD::is_pointer_v<_Up> || __is_void_ptr, "non-void pointers are not allowed to prevent bug-prone code"); static_assert(_CUDA_VSTD::has_single_bit(uint32_t{_Width}) && _Width >= 1 && _Width <= __warp_size, "_Width must be a power of 2 and less or equal to the warp size"); # if __CUDA_ARCH__ >= 700 [[maybe_unused]] int __pred1; _CCCL_ASSERT(__match_all_sync(__activemask(), __xor_mask, &__pred1), "all active lanes must have the same delta"); # endif if constexpr (_Width == 1) { _CCCL_ASSERT(__xor_mask == 0, "delta must be 0 when Width == 1"); return warp_shuffle_result<_Up>{__data, true}; } else { _CCCL_ASSERT(__xor_mask >= 1 && __xor_mask < _Width, "delta must be in the range [1, _Width)"); constexpr int __ratio = ::cuda::ceil_div(sizeof(_Up), sizeof(uint32_t)); auto __clamp_segmask = (_Width - 1u) | ((__warp_size - _Width) << 8); bool __pred; uint32_t __array[__ratio]; _CUDA_VSTD::memcpy( static_cast(__array), static_cast(_CUDA_VSTD::addressof(__data)), sizeof(_Up)); _CCCL_PRAGMA_UNROLL_FULL() for (int i = 0; i < __ratio; ++i) { __array[i] = _CUDA_VPTX::shfl_sync_bfly(__array[i], __pred, __xor_mask, __clamp_segmask, __lane_mask); } warp_shuffle_result<_Up> __result; __result.pred = __pred; _CUDA_VSTD::memcpy( static_cast(_CUDA_VSTD::addressof(__result.data)), static_cast(__array), sizeof(_Up)); return __result; } } template > _CCCL_NODISCARD _CCCL_HIDE_FROM_ABI _CCCL_DEVICE warp_shuffle_result<_Up> warp_shuffle_xor(const _Tp& __data, int __src_lane, _CUDA_VSTD::integral_constant __width) { return ::cuda::device::warp_shuffle_xor(__data, __src_lane, 0xFFFFFFFF, __width); } _LIBCUDACXX_END_NAMESPACE_CUDA_DEVICE #endif // __cccl_ptx_isa >= 600 #endif // _CUDA___WARP_WARP_SHUFFLE_H