1#ifndef TDLS_CORE_GROUP_HPP
2#define TDLS_CORE_GROUP_HPP
127#include <type_traits>
133#if defined(TDLS_DETAIL_COMPILER_NVCXX)
139#if defined(TDLS_DETAIL_COMPILER_HIP_CLANG)
140#include <hip/hip_runtime.h>
154#if defined(TDLS_DETAIL_PASS_NVCXX_STDPAR_OPENACC)
156#define TDLS_DETAIL_GROUP_NONE "The OpenACC backend of nvc++ for the parallel algorithms has none."
157#elif defined(TDLS_DETAIL_PASS_NVCXX_CUDA)
159#define TDLS_DETAIL_GROUP_NVCXX
160#elif defined(TDLS_DETAIL_PASS_NVCXX_OFFLOAD)
162#define TDLS_DETAIL_GROUP_NONE "nvc++ offers it to OpenMP and OpenACC offloading with -cuda."
163#elif defined(TDLS_DETAIL_PASS_AMDGCN)
165#define TDLS_DETAIL_GROUP_AMDGCN
166#elif defined(TDLS_DETAIL_PASS_CUDA)
168#define TDLS_DETAIL_GROUP_CUDA
169#elif defined(TDLS_DETAIL_PASS_NVPTX)
171#define TDLS_DETAIL_GROUP_NVPTX
172#elif defined(TDLS_DETAIL_PASS_GCC_OFFLOAD)
174#define TDLS_DETAIL_GROUP_NONE "GCC offloading has none."
175#elif defined(TDLS_DETAIL_PASS_SPIRV)
177#define TDLS_DETAIL_GROUP_NONE \
178 "On SPIR-V devices, SYCL and OpenMP offloading cannot stop a kernel whose group is " \
179 "incomplete; pass a sub-group, work-group or team barrier."
180#elif defined(TDLS_DETAIL_PASS_ACPP_GENERIC)
182#define TDLS_DETAIL_GROUP_NONE \
183 "The generic mode of AdaptiveCpp has none; its cuda and hip modes do."
186#define TDLS_DETAIL_GROUP_HOST
192#if (defined(TDLS_DETAIL_GROUP_CUDA) || defined(TDLS_DETAIL_GROUP_NVPTX)) && \
193 ((defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 700) || \
194 (defined(__SYCL_CUDA_ARCH__) && __SYCL_CUDA_ARCH__ < 700))
196#define TDLS_DETAIL_GROUP_NONE "NVIDIA GPUs before Volta (sm_70) have none."
197#elif defined(TDLS_DETAIL_GROUP_HOST) && !defined(__cpp_lib_atomic_ref)
199#define TDLS_DETAIL_GROUP_NONE \
200 "On CPU, it needs std::atomic_ref, missing from this standard library (libc++ before 19)."
203#if !defined(TDLS_DETAIL_GROUP_NONE)
205#define TDLS_DETAIL_GROUP_BARRIER
209#define TDLS_DETAIL_GROUP_EXPAND(...) __VA_ARGS__
216#if defined(TDLS_DETAIL_GROUP_NVCXX)
218#define TDLS_DETAIL_GROUP_ON_DEVICE(code) NV_IF_TARGET(NV_IS_DEVICE, code)
220#define TDLS_DETAIL_GROUP_SIDE(device, host) NV_IF_TARGET(NV_IS_DEVICE, device, host)
222#define TDLS_DETAIL_GROUP_LANES TDLS_HOST_DEVICE TDLS_FORCE_INLINE_GPU
223#elif defined(TDLS_DETAIL_GROUP_CUDA)
225#define TDLS_DETAIL_GROUP_ON_DEVICE(code) TDLS_DETAIL_GROUP_EXPAND code
227#define TDLS_DETAIL_GROUP_SIDE(device, host) TDLS_DETAIL_GROUP_EXPAND device
229#define TDLS_DETAIL_GROUP_LANES __device__ TDLS_FORCE_INLINE_GPU
230#elif defined(TDLS_DETAIL_GROUP_AMDGCN) && defined(TDLS_DETAIL_COMPILER_HIP_CLANG)
232#define TDLS_DETAIL_GROUP_SIDE(device, host) TDLS_DETAIL_GROUP_EXPAND device
234#define TDLS_DETAIL_GROUP_LANES __device__ TDLS_FORCE_INLINE_GPU
235#elif defined(TDLS_DETAIL_GROUP_NVPTX) || defined(TDLS_DETAIL_GROUP_AMDGCN)
237#define TDLS_DETAIL_GROUP_SIDE(device, host) TDLS_DETAIL_GROUP_EXPAND device
239#define TDLS_DETAIL_GROUP_LANES inline
240#elif defined(TDLS_DETAIL_GROUP_HOST)
242#define TDLS_DETAIL_GROUP_SIDE(device, host) TDLS_DETAIL_GROUP_EXPAND host
245#define TDLS_DETAIL_GROUP_SIDE(device, host)
248#if defined(TDLS_DETAIL_GROUP_BARRIER)
250#define TDLS_DETAIL_GROUP_DISPATCH(device, host) TDLS_DETAIL_GROUP_SIDE(device, host)
253#define TDLS_DETAIL_GROUP_DISPATCH(device, host)
285template<
typename Sync>
290template<
typename Sync>
300template<
typename Sync,
bool = is_auto_sync<Sync>>
305template<
typename Sync>
306inline constexpr bool nothrow_sync<Sync, true> =
true;
316#if defined(TDLS_DETAIL_GROUP_BARRIER)
320#define TDLS_DETAIL_GROUP_NONE ""
335#if defined(TDLS_DETAIL_GROUP_BARRIER) && \
336 (defined(TDLS_DETAIL_GROUP_CUDA) || defined(TDLS_DETAIL_GROUP_NVPTX) || \
337 defined(TDLS_DETAIL_GROUP_NVCXX))
339#elif defined(TDLS_DETAIL_GROUP_BARRIER) && defined(TDLS_DETAIL_GROUP_AMDGCN)
358#define TDLS_DETAIL_GROUP_INCOMPLETE \
359 "TDLS: a group of threads is incomplete in its warp, or its workspace is private to each " \
360 "thread. The threads of a group must call the solver together, as lanes of one warp or " \
361 "wavefront, with a workspace in shared or global memory.\n"
363#define TDLS_DETAIL_GROUP_NO_SYNC \
364 "TDLS: the runtime solver runs with tdls::NoSync, valid for one thread per system, on a " \
365 "dimension above rows_per_thread. Pass a barrier, or a rows_per_thread reaching the " \
372#if defined(TDLS_DETAIL_GROUP_CUDA) || defined(TDLS_DETAIL_GROUP_NVCXX)
378TDLS_DETAIL_GROUP_LANES
void lanes_fail([[maybe_unused]]
const unsigned long long mask,
379 [[maybe_unused]]
const LanesFailure failure)
noexcept {
381 TDLS_DETAIL_GROUP_ON_DEVICE((
383 asm volatile(
"mov.u32 %0, %%laneid;" :
"=r"(lane));
384 if (lane ==
static_cast<unsigned>(__ffsll(
static_cast<long long>(mask)) - 1)) {
385 if (failure == LanesFailure::incomplete)
396TDLS_DETAIL_GROUP_LANES
unsigned long long lanes_active() noexcept {
397 unsigned long long lanes = 0;
398 TDLS_DETAIL_GROUP_ON_DEVICE((lanes = __activemask();))
402#if defined(TDLS_DETAIL_GROUP_BARRIER)
410TDLS_DETAIL_GROUP_LANES
unsigned long long lanes_join(
const void* key,
const int threads)
noexcept {
411 unsigned long long lanes = 0;
413 TDLS_DETAIL_GROUP_ON_DEVICE((
414 const unsigned mask =
415 __match_any_sync(__activemask(),
reinterpret_cast<unsigned long long>(key));
416 if (__popc(mask) != threads || __isLocal(key))
417 lanes_fail(mask, LanesFailure::incomplete);
425TDLS_DETAIL_GROUP_LANES
void lanes_sync(
const unsigned long long lanes)
noexcept {
426 TDLS_DETAIL_GROUP_ON_DEVICE((__syncwarp(
static_cast<unsigned>(lanes));))
437#elif defined(TDLS_DETAIL_GROUP_NVPTX)
443TDLS_DETAIL_GROUP_LANES
void lanes_fail([[maybe_unused]]
const unsigned long long mask,
444 [[maybe_unused]]
const LanesFailure failure)
noexcept {
445#if !defined(TDLS_DETAIL_LANG_SYCL_DEVICE)
446 if (__nvvm_read_ptx_sreg_laneid() == __builtin_ctzll(mask)) {
447 if (failure == LanesFailure::incomplete)
458TDLS_DETAIL_GROUP_LANES
unsigned long long lanes_active() noexcept {
460 asm volatile(
"activemask.b32 %0;" :
"=r"(active));
464#if defined(TDLS_DETAIL_GROUP_BARRIER)
472TDLS_DETAIL_GROUP_LANES
unsigned long long lanes_join(
const void* key,
const int threads)
noexcept {
473 const unsigned mask = __nvvm_match_any_sync_i64(
474 static_cast<unsigned>(lanes_active()),
475 static_cast<long long>(
reinterpret_cast<unsigned long long>(key)));
476 if (__builtin_popcount(mask) != threads || __nvvm_isspacep_local(key))
477 lanes_fail(mask, LanesFailure::incomplete);
483TDLS_DETAIL_GROUP_LANES
void lanes_sync(
const unsigned long long lanes)
noexcept {
484 __nvvm_bar_warp_sync(
static_cast<unsigned>(lanes));
500#elif defined(TDLS_DETAIL_GROUP_AMDGCN)
503#if __clang_major__ >= 19
504#define TDLS_DETAIL_GROUP_BALLOT(predicate) __builtin_amdgcn_ballot_w64(predicate)
506#define TDLS_DETAIL_GROUP_BALLOT(predicate) \
507 __builtin_amdgcn_uicmp(static_cast<unsigned>(predicate), 0u, 33 )
514TDLS_DETAIL_GROUP_LANES
void lanes_fail([[maybe_unused]]
const unsigned long long mask,
515 [[maybe_unused]]
const LanesFailure failure)
noexcept {
516#if !defined(TDLS_DETAIL_LANG_SYCL_DEVICE)
517 const unsigned lane = __builtin_amdgcn_mbcnt_hi(~0u, __builtin_amdgcn_mbcnt_lo(~0u, 0u));
518 if (lane ==
static_cast<unsigned>(__builtin_ctzll(mask))) {
519 if (failure == LanesFailure::incomplete)
530TDLS_DETAIL_GROUP_LANES
unsigned long long lanes_active() noexcept {
531 return __builtin_amdgcn_read_exec();
540TDLS_DETAIL_GROUP_LANES
unsigned long long lanes_join(
const void* key,
const int threads)
noexcept {
541 const unsigned long long own =
reinterpret_cast<unsigned long long>(key);
542 unsigned long long mask = 0;
544 while (TDLS_DETAIL_GROUP_BALLOT(!done) != 0) {
546 const unsigned low = __builtin_amdgcn_readfirstlane(
static_cast<unsigned>(own));
547 const unsigned high = __builtin_amdgcn_readfirstlane(
static_cast<unsigned>(own >> 32));
548 if (((
static_cast<unsigned long long>(high) << 32) | low) == own) {
549 mask = TDLS_DETAIL_GROUP_BALLOT(
true);
557 const bool isprivate =
558 __builtin_amdgcn_is_private((
const __attribute__((address_space(0)))
void*)key);
559 if (__builtin_popcountll(mask) != threads || isprivate)
560 lanes_fail(mask, LanesFailure::incomplete);
566TDLS_DETAIL_GROUP_LANES
void lanes_sync(
unsigned long long)
noexcept {
567 __builtin_amdgcn_fence(__ATOMIC_RELEASE,
"wavefront");
568 __builtin_amdgcn_wave_barrier();
569 __builtin_amdgcn_fence(__ATOMIC_ACQUIRE,
"wavefront");
581#if defined(TDLS_DETAIL_GROUP_HOST) || defined(TDLS_DETAIL_GROUP_NVCXX)
590#if defined(TDLS_DETAIL_GROUP_BARRIER)
599void host_join(T* slots,
const int threads)
noexcept {
600 static_assert(std::atomic_ref<T>::is_always_lock_free,
601 "TDLS: the deduced CPU barrier needs lock-free atomics on the scalar type; "
602 "pass a barrier (sync argument)");
603 const T count = std::atomic_ref<T>(slots[0]).load(std::memory_order_relaxed);
604 const T flag = std::atomic_ref<T>(slots[1]).load(std::memory_order_relaxed);
605 if (!(count >= T(0) && count < T(threads) && count == T(
static_cast<int>(count)) &&
606 (flag == T(0) || flag == T(1)))) {
608 "TDLS: the workspace of a group of %d CPU threads was not zero before "
620void host_sync(T* slots,
const int threads)
noexcept {
621 std::atomic_ref<T> count(slots[0]);
622 std::atomic_ref<T> flag(slots[1]);
623 const T seen = flag.load(std::memory_order_acquire);
624 if (count.fetch_add(T(1), std::memory_order_acq_rel) == T(threads - 1)) {
625 count.store(T(0), std::memory_order_relaxed);
626 flag.store(seen == T(0) ? T(1) : T(0), std::memory_order_release);
631 static std::atomic<bool> warned{
false};
632 std::chrono::steady_clock::time_point start{};
633 for (
unsigned spins = 0; flag.load(std::memory_order_acquire) == seen; ++spins) {
634 if (spins < 1024)
continue;
635 std::this_thread::yield();
636 if (spins % 1024 != 0)
continue;
637 const auto now = std::chrono::steady_clock::now();
640 else if (now - start > std::chrono::seconds(1) && !warned.exchange(
true))
642 "TDLS: a group of %d CPU threads has waited for 1 s at a barrier. "
643 "The threads of a group must share its workspace and run "
644 "concurrently: not in a worksharing loop, nor in parallel STL or a "
645 "SYCL kernel on the CPU.\n",
654#undef TDLS_DETAIL_GROUP_INCOMPLETE
655#undef TDLS_DETAIL_GROUP_NO_SYNC
656#undef TDLS_DETAIL_GROUP_BALLOT
681 : slots(slots), threads(threads) {
684 (host_join(slots, threads);))
699 unsigned long long lanes = 0;
719template<
int static_threads,
typename Sync,
typename T>
721resolve_sync(Sync& sync, [[maybe_unused]] T* slots, [[maybe_unused]]
const int threads)
noexcept {
722 if constexpr (is_no_sync<Sync>) {
723 static_assert(static_threads <= 1,
"TDLS: a group of several threads cannot run with "
724 "tdls::NoSync (sync argument)");
725 if constexpr (static_threads == 0) {
729 }
else if constexpr (!is_auto_sync<Sync>) {
731 }
else if constexpr (static_threads == 1) {
734 static_assert(group_barrier_available<T>,
735 "TDLS: this compilation target offers no deduced barrier for a group of "
736 "several threads; pass a barrier (sync argument), or tdls::NoSync with one "
739 "TDLS: a group larger than a warp needs a barrier (sync argument)");
752#undef TDLS_DETAIL_GROUP_DISPATCH
753#undef TDLS_DETAIL_GROUP_SIDE
754#undef TDLS_DETAIL_GROUP_LANES
755#undef TDLS_DETAIL_GROUP_ON_DEVICE
756#undef TDLS_DETAIL_GROUP_EXPAND
757#undef TDLS_DETAIL_GROUP_BARRIER
758#undef TDLS_DETAIL_GROUP_CUDA
759#undef TDLS_DETAIL_GROUP_NVPTX
760#undef TDLS_DETAIL_GROUP_AMDGCN
761#undef TDLS_DETAIL_GROUP_NVCXX
762#undef TDLS_DETAIL_GROUP_HOST
763#undef TDLS_DETAIL_GROUP_NONE
The barrier deduced from the compilation target, for a group of several threads. A group of one threa...
Definition group.hpp:671
TDLS_EXEC_CHECK_DISABLE TDLS_HOST_DEVICE TDLS_FORCE_INLINE_GPU constexpr DeducedSync(T *slots, const int threads) noexcept
Joins the group: the GPU check of its lanes, or the CPU check of its workspace.
Definition group.hpp:680
TDLS_EXEC_CHECK_DISABLE TDLS_HOST_DEVICE TDLS_FORCE_INLINE_GPU constexpr void operator()() const noexcept
Barrier of the group.
Definition group.hpp:690
#define TDLS_DETAIL_GROUP_NO_SYNC
Message of tdls::NoSync with a group of several threads.
Definition group.hpp:363
constexpr bool group_barrier_available
Whether the compilation pass offers the deduced barrier to a group of several threads....
Definition group.hpp:323
#define TDLS_DETAIL_GROUP_DISPATCH(device, host)
No deduced barrier: the resolution of the barrier refuses the pass.
Definition group.hpp:253
LanesFailure
Failures that stop a kernel, each with its message.
Definition group.hpp:346
@ no_sync
tdls::NoSync with a group of several threads
@ incomplete
a group incomplete in its warp, or a private workspace
TDLS_HOST_DEVICE TDLS_FORCE_INLINE_GPU constexpr decltype(auto) resolve_sync(Sync &sync, T *slots, const int threads) noexcept
Resolves the barrier argument of an entry point: the caller's barrier as is, or the deduced barrier.
Definition group.hpp:721
void host_refuse_no_sync() noexcept
Stops the program: the runtime solver runs a group of several threads with tdls::NoSync.
Definition group.hpp:585
constexpr int barrier_elements
Elements at the start of the workspace that hold the deduced CPU barrier: a counter and a flag....
Definition group.hpp:330
#define TDLS_DETAIL_GROUP_NONE
No deduced barrier in this pass; the reason, appended to the diagnostic.
Definition group.hpp:199
constexpr int group_lanes
Largest group the warp of the pass can hold, checked at compile time when the size of the group is (0...
Definition group.hpp:342
#define TDLS_DETAIL_GROUP_INCOMPLETE
Message of an incomplete group.
Definition group.hpp:358
constexpr bool is_no_sync
Whether the barrier argument is the absence of barrier.
Definition group.hpp:291
TDLS_HOST_DEVICE TDLS_FORCE_INLINE_GPU void refuse_no_sync() noexcept
Stops the program: the runtime solver runs a group of several threads with tdls::NoSync....
Definition group.hpp:661
constexpr bool is_auto_sync
Whether the barrier argument asks for the deduced barrier.
Definition group.hpp:286
constexpr bool nothrow_sync
Whether a call of the barrier is noexcept, the condition of the noexcept specification of every entry...
Definition group.hpp:301
#define TDLS_DETAIL_GROUP_SIDE(device, host)
Picks the statement list of the side of the pass.
Definition group.hpp:242
Toolchain detection and portability macros.
#define TDLS_FORCE_INLINE_GPU
Inlining forced on GPU only: __forceinline__ under CUDA, the equivalent attribute under HIP (see TDLS...
Definition macros.hpp:77
#define TDLS_EXEC_CHECK_DISABLE
Lifts the execution-space check of nvcc on the next function template; empty elsewhere.
Definition macros.hpp:274
#define TDLS_HOST_DEVICE
__host__ __device__ under CUDA, the equivalent attributes under HIP, empty elsewhere.
Definition macros.hpp:50
Barrier argument of the cooperative solvers that asks them to deduce the barrier from the compilation...
Definition group.hpp:268
Barrier that does nothing, valid only when one thread solves a system. The compile-time solver refuse...
Definition group.hpp:275
TDLS_HOST_DEVICE TDLS_FORCE_INLINE_GPU constexpr void operator()() const noexcept
Does nothing.
Definition group.hpp:277
Detection of the compiler, of the language and of the side of the compilation pass: the one place whe...