alpaka
Abstraction Library for Parallel Kernel Acceleration
Loading...
Searching...
No Matches
WarpGenericSycl.hpp
Go to the documentation of this file.
1/* Copyright 2026 Jan Stephan, Luca Ferragina, Andrea Bocci, Aurora Perego, Simone Balducci
2 * SPDX-License-Identifier: MPL-2.0
3 *
4 * The implementations of Shfl::shfl(), ShflUp::shfl_up(), ShflDown::shfl_down() and ShflXor::shfl_xor() are derived
5 * from Intel DPCT.
6 * Copyright (C) Intel Corporation.
7 * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8 * See https://llvm.org/LICENSE.txt for license information.
9 */
10
11#pragma once
12
13#include "alpaka/acc/Tag.hpp"
17
18#include <cstdint>
19#include <type_traits>
20
21#ifdef ALPAKA_ACC_SYCL_ENABLED
22
23# include <sycl/sycl.hpp>
24
25namespace alpaka::warp
26{
27 //! The SYCL warp.
28 template<typename TDim>
29 class WarpGenericSycl : public interface::Implements<alpaka::warp::ConceptWarp, WarpGenericSycl<TDim>>
30 {
31 public:
32 using mask_type = std::uint32_t;
33
34 WarpGenericSycl(sycl::nd_item<TDim::value> my_item) : m_item_warp{my_item}
35 {
36 }
37
38 sycl::nd_item<TDim::value> m_item_warp;
39 };
40} // namespace alpaka::warp
41
42// The SYCL warp operations are implemented with the SYCL group functions (group_ballot(), the group algorithms,
43// get_opportunistic_group(), select_from_group(), ...), which work only on some backends and with some oneAPI
44// versions:
45// - Intel/Altera FPGAs: not supported (the FPGA backend is available only up to oneAPI 2025.0, where they do not
46// work);
47// - AMD GPUs: not supported (the Codeplay plugin, available up to oneAPI 2025.2, does not implement the SPIR-V group
48// operations they use);
49// - Intel CPUs and GPUs: supported starting from oneAPI 2025.2. Up to oneAPI 2026.1 they give wrong results when
50// some work-items have returned early from the kernel, and the code calling them is not inlined, for example
51// when compiling with -O0, -fno-inline or -fno-inline-functions: the work-items that have returned are still
52// counted as members of the group. Clang-based compilers, like icpx, define __NO_INLINE__ in these cases.
53// Define ALPAKA_SYCL_DISABLE_WARP_INLINE_CHECK to disable this check, for example if the kernels using the warp
54// operations never return early;
55// - NVIDIA GPUs: supported starting from oneAPI 2025.0.
56// Using the warp operations where they are not supported fails at compile time, with one of these messages.
57# define ALPAKA_SYCL_WARP_UNSUPPORTED_FPGA "The alpaka warp operations are not supported on Intel/Altera FPGAs."
58# define ALPAKA_SYCL_WARP_UNSUPPORTED_AMD \
59 "The alpaka warp operations are not supported by the SYCL backend on AMD GPUs."
60# if ALPAKA_COMP_ICPX < ALPAKA_VERSION_NUMBER(2025, 2, 0)
61# define ALPAKA_SYCL_WARP_UNSUPPORTED_INTEL \
62 "The alpaka warp operations require oneAPI 2025.2 or newer on Intel CPUs and GPUs."
63# elif ALPAKA_COMP_ICPX < ALPAKA_VERSION_NUMBER(2026, 2, 0) && defined(__NO_INLINE__) \
64 && !defined(ALPAKA_SYCL_DISABLE_WARP_INLINE_CHECK)
65# define ALPAKA_SYCL_WARP_UNSUPPORTED_INTEL \
66 "The alpaka warp operations give wrong results on Intel CPUs and GPUs with oneAPI 2026.1 and older, " \
67 "when the SYCL code is compiled without inlining (e.g. -O0, -fno-inline or -fno-inline-functions). " \
68 "Enable optimisations (-O1 or higher) and inlining, or define ALPAKA_SYCL_DISABLE_WARP_INLINE_CHECK if " \
69 "the kernels never return early."
70# endif
71# if ALPAKA_COMP_ICPX < ALPAKA_VERSION_NUMBER(2025, 0, 0)
72# define ALPAKA_SYCL_WARP_UNSUPPORTED_NVIDIA \
73 "The alpaka warp operations require oneAPI 2025.0 or newer on NVIDIA GPUs."
74# endif
75
76// Each SYCL kernel is compiled for all the targets, so the check is done in the device compilation for each target.
77# ifdef __SYCL_DEVICE_ONLY__
78# if defined(__NVPTX__)
79# ifdef ALPAKA_SYCL_WARP_UNSUPPORTED_NVIDIA
80# define ALPAKA_SYCL_WARP_UNSUPPORTED ALPAKA_SYCL_WARP_UNSUPPORTED_NVIDIA
81# endif
82# elif defined(__AMDGCN__)
83# define ALPAKA_SYCL_WARP_UNSUPPORTED ALPAKA_SYCL_WARP_UNSUPPORTED_AMD
84# elif defined(ALPAKA_SYCL_ONEAPI_FPGA) && !defined(ALPAKA_SYCL_ONEAPI_GPU) \
85 && !(defined(__SYCL_TARGET_INTEL_X86_64__) && __SYCL_TARGET_INTEL_X86_64__)
86// The FPGA target does not define a specific macro. The SYCL headers define all the __SYCL_TARGET_*__ macros, with a
87// value of 1 for the current target and 0 for the others.
88# define ALPAKA_SYCL_WARP_UNSUPPORTED ALPAKA_SYCL_WARP_UNSUPPORTED_FPGA
89# elif defined(ALPAKA_SYCL_WARP_UNSUPPORTED_INTEL)
90// Intel CPUs and GPUs, and generic SPIR-V targets
91# define ALPAKA_SYCL_WARP_UNSUPPORTED ALPAKA_SYCL_WARP_UNSUPPORTED_INTEL
92# endif
93# endif
94
95namespace alpaka::warp::detail
96{
97 //! Return why the SYCL warp operations are not supported for the accelerator tag TTag, or nullptr if they are
98 //! supported.
99 template<typename TTag>
100 constexpr auto syclWarpUnsupportedReason() -> char const*
101 {
102 if constexpr(std::is_same_v<TTag, TagFpgaSyclIntel>)
103 {
104 return ALPAKA_SYCL_WARP_UNSUPPORTED_FPGA;
105 }
106 else if constexpr(std::is_same_v<TTag, TagGpuSyclAmd>)
107 {
108 return ALPAKA_SYCL_WARP_UNSUPPORTED_AMD;
109 }
110 else if constexpr(std::is_same_v<TTag, TagGpuSyclNvidia>)
111 {
112# ifdef ALPAKA_SYCL_WARP_UNSUPPORTED_NVIDIA
113 return ALPAKA_SYCL_WARP_UNSUPPORTED_NVIDIA;
114# else
115 return nullptr;
116# endif
117 }
118 else
119 {
120 // Intel CPUs and GPUs, and generic SYCL targets
121# ifdef ALPAKA_SYCL_WARP_UNSUPPORTED_INTEL
122 return ALPAKA_SYCL_WARP_UNSUPPORTED_INTEL;
123# else
124 return nullptr;
125# endif
126 }
127 }
128
129 // Dependent on the template parameter, so that the static_assert is only evaluated when a warp operation is used.
130 template<typename TDim>
131 inline constexpr bool syclWarpDependentFalse = false;
132} // namespace alpaka::warp::detail
133
134# ifdef ALPAKA_SYCL_WARP_UNSUPPORTED
135# define ALPAKA_SYCL_WARP_CHECK \
136 static_assert(alpaka::warp::detail::syclWarpDependentFalse<TDim>, ALPAKA_SYCL_WARP_UNSUPPORTED)
137# else
138# define ALPAKA_SYCL_WARP_CHECK static_assert(true)
139# endif
140
141namespace alpaka::warp::trait
142{
143 // oneAPI up to 2025.3 uses sycl::ext::oneapi::experimental::this_kernel::get_opportunistic_group(),
144 // while oneAPI 2026.0 uses sycl::ext::oneapi::experimental::this_work_item::get_opportunistic_group()
145# if ALPAKA_COMP_ICPX >= ALPAKA_VERSION_NUMBER(2026, 0, 0)
146 using sycl::ext::oneapi::experimental::this_work_item::get_opportunistic_group;
147# else
148 using sycl::ext::oneapi::experimental::this_kernel::get_opportunistic_group;
149# endif
150
151 template<typename TDim>
152 struct GetSize<warp::WarpGenericSycl<TDim>>
153 {
154 static auto getSize(warp::WarpGenericSycl<TDim> const& warp) -> std::int32_t
155 {
156 auto const sub_group = warp.m_item_warp.get_sub_group();
157 // SYCL sub-groups are always 1D
158 return static_cast<std::int32_t>(sub_group.get_max_local_range()[0]);
159 }
160 };
161
162 template<typename TDim>
163 struct GetSizeCompileTime<warp::WarpGenericSycl<TDim>>
164 {
165 static constexpr auto getSizeCompileTime() -> std::int32_t
166 {
167 // SYCL sub-groups size is usually not known at compile time
168 return 0;
169 }
170 };
171
172 template<typename TDim>
173 struct GetSizeUpperLimit<warp::WarpGenericSycl<TDim>>
174 {
175 static constexpr auto getSizeUpperLimit() -> std::int32_t
176 {
177 // See include/alpaka/kernel/SyclSubgroupSize.hpp for possible sub-group sizes.
178 return 64;
179 }
180 };
181
182 template<typename TDim>
183 struct Activemask<warp::WarpGenericSycl<TDim>>
184 {
185 // FIXME This should be std::uint64_t on AMD GCN architectures and on CPU,
186 // but the former is not targeted in alpaka and CPU case is not supported in SYCL yet.
187 // Restrict to warpSize <= 32 for now.
188 static auto activemask(warp::WarpGenericSycl<TDim> const& /*warp*/) -> warp::WarpGenericSycl<TDim>::mask_type
189 {
190 ALPAKA_SYCL_WARP_CHECK;
191
192 sycl::sub_group sg = sycl::ext::oneapi::this_work_item::get_sub_group();
193 auto const mask = sycl::ext::oneapi::group_ballot(sg, true);
194 std::uint32_t bits = 0;
195 mask.extract_bits(bits);
196 return bits;
197 }
198 };
199
200 template<typename TDim>
201 struct All<warp::WarpGenericSycl<TDim>>
202 {
203 static auto all(warp::WarpGenericSycl<TDim> const& /*warp*/, std::int32_t predicate) -> std::int32_t
204 {
205 ALPAKA_SYCL_WARP_CHECK;
206
207 auto activegroup = get_opportunistic_group();
208 return static_cast<std::int32_t>(sycl::all_of_group(activegroup, static_cast<bool>(predicate)));
209 }
210 };
211
212 template<typename TDim>
213 struct Any<warp::WarpGenericSycl<TDim>>
214 {
215 static auto any(warp::WarpGenericSycl<TDim> const& /*warp*/, std::int32_t predicate) -> std::int32_t
216 {
217 ALPAKA_SYCL_WARP_CHECK;
218
219 auto activegroup = get_opportunistic_group();
220 return static_cast<std::int32_t>(sycl::any_of_group(activegroup, static_cast<bool>(predicate)));
221 }
222 };
223
224 template<typename TDim>
225 struct Ballot<warp::WarpGenericSycl<TDim>>
226 {
227 // FIXME This should be std::uint64_t on AMD GCN architectures and on CPU,
228 // but the former is not targeted in alpaka and CPU case is not supported in SYCL yet.
229 // Restrict to warpSize <= 32 for now.
230 static auto ballot(warp::WarpGenericSycl<TDim> const& /*warp*/, std::int32_t predicate)
231 -> warp::WarpGenericSycl<TDim>::mask_type
232 {
233 ALPAKA_SYCL_WARP_CHECK;
234
235 auto sub_group = sycl::ext::oneapi::this_work_item::get_sub_group();
236 auto const mask = sycl::ext::oneapi::group_ballot(sub_group, static_cast<bool>(predicate));
237 // FIXME This should be std::uint64_t on AMD GCN architectures and on CPU,
238 // but the former is not targeted in alpaka and CPU case is not supported in SYCL yet.
239 // Restrict to warpSize <= 32 for now.
240 std::uint32_t bits = 0;
241 mask.extract_bits(bits);
242 return bits;
243 }
244 };
245
246 template<typename TDim>
247 struct Shfl<warp::WarpGenericSycl<TDim>>
248 {
249 template<typename T>
250 static auto shfl(
251 warp::WarpGenericSycl<TDim> const& /*warp*/,
252 T value,
253 std::int32_t srcLane,
254 std::int32_t width)
255 {
256 ALPAKA_SYCL_WARP_CHECK;
257
258 ALPAKA_ASSERT_ACC(width > 0);
259 ALPAKA_ASSERT_ACC(srcLane >= 0);
260
261 /* If width < srcLane the sub-group needs to be split into assumed subdivisions. The first item of each
262 subdivision has the assumed index 0. The srcLane index is relative to the subdivisions.
263
264 Example: If we assume a sub-group size of 32 and a width of 16 we will receive two subdivisions:
265 The first starts at sub-group index 0 and the second at sub-group index 16. For srcLane = 4 the
266 first subdivision will access the value at sub-group index 4 and the second at sub-group index 20. */
267 auto actual_group = get_opportunistic_group();
268 std::uint32_t const w = static_cast<std::uint32_t>(width);
269 std::uint32_t const start_index = actual_group.get_local_linear_id() / w * w;
270 return sycl::select_from_group(actual_group, value, start_index + static_cast<std::uint32_t>(srcLane) % w);
271 }
272 };
273
274 template<typename TDim>
275 struct ShflUp<warp::WarpGenericSycl<TDim>>
276 {
277 template<typename T>
278 static auto shfl_up(
279 warp::WarpGenericSycl<TDim> const& /*warp*/,
280 T value,
281 std::uint32_t offset, /* must be the same for all work-items in the group */
282 std::int32_t width)
283 {
284 ALPAKA_SYCL_WARP_CHECK;
285
286 auto actual_group = get_opportunistic_group();
287 std::uint32_t const w = static_cast<std::uint32_t>(width);
288 std::uint32_t const id = actual_group.get_local_linear_id();
289 std::uint32_t const start_index = id / w * w;
290 T result = sycl::shift_group_right(actual_group, value, offset);
291 if((id - start_index) < offset)
292 {
293 result = value;
294 }
295 return result;
296 }
297 };
298
299 template<typename TDim>
300 struct ShflDown<warp::WarpGenericSycl<TDim>>
301 {
302 template<typename T>
303 static auto shfl_down(
304 warp::WarpGenericSycl<TDim> const& /*warp*/,
305 T value,
306 std::uint32_t offset,
307 std::int32_t width)
308 {
309 ALPAKA_SYCL_WARP_CHECK;
310
311 auto actual_group = get_opportunistic_group();
312 std::uint32_t const w = static_cast<std::uint32_t>(width);
313 std::uint32_t const id = actual_group.get_local_linear_id();
314 std::uint32_t const end_index = (id / w + 1) * w;
315 T result = sycl::shift_group_left(actual_group, value, offset);
316 if((id + offset) >= end_index)
317 {
318 result = value;
319 }
320 return result;
321 }
322 };
323
324 template<typename TDim>
325 struct ShflXor<warp::WarpGenericSycl<TDim>>
326 {
327 template<typename T>
328 static auto shfl_xor(
329 warp::WarpGenericSycl<TDim> const& /*warp*/,
330 T value,
331 std::int32_t mask,
332 std::int32_t width)
333 {
334 ALPAKA_SYCL_WARP_CHECK;
335
336 auto actual_group = get_opportunistic_group();
337 std::uint32_t const w = static_cast<std::uint32_t>(width);
338 std::uint32_t const id = actual_group.get_local_linear_id();
339 std::uint32_t const start_index = id / w * w;
340 std::uint32_t const target_offset = (id % w) ^ static_cast<std::uint32_t>(mask);
341 return sycl::select_from_group(actual_group, value, target_offset < w ? start_index + target_offset : id);
342 }
343 };
344} // namespace alpaka::warp::trait
345
346# undef ALPAKA_SYCL_WARP_CHECK
347# undef ALPAKA_SYCL_WARP_UNSUPPORTED
348# undef ALPAKA_SYCL_WARP_UNSUPPORTED_FPGA
349# undef ALPAKA_SYCL_WARP_UNSUPPORTED_AMD
350# undef ALPAKA_SYCL_WARP_UNSUPPORTED_INTEL
351# undef ALPAKA_SYCL_WARP_UNSUPPORTED_NVIDIA
352
353#endif
#define ALPAKA_ASSERT_ACC(...)
ALPAKA_ASSERT_ACC is an assert-like macro.
Definition Assert.hpp:52
The warp traits.
Definition Traits.hpp:21
ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_ACC auto ballot(TWarp const &warp, std::int32_t predicate) -> typename TWarp::mask_type
Evaluates predicate for all non-exited threads in a warp and returns a 32- or 64-bit unsigned integer...
Definition Traits.hpp:194
ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_ACC auto all(TWarp const &warp, std::int32_t predicate) -> std::int32_t
Evaluates predicate for all active threads of the warp and returns non-zero if and only if predicate ...
Definition Traits.hpp:144
ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_ACC auto shfl_up(TWarp const &warp, T value, std::uint32_t offset, std::int32_t width=0)
Exchange data between threads within a warp. It copies from a lane with lower ID relative to caller....
Definition Traits.hpp:266
ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_ACC constexpr auto getSizeUpperLimit() -> std::int32_t
If the warp size is available as a compile-time constant returns its value; otherwise returns an uppe...
Definition Traits.hpp:96
ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_ACC auto any(TWarp const &warp, std::int32_t predicate) -> std::int32_t
Evaluates predicate for all active threads of the warp and returns non-zero if and only if predicate ...
Definition Traits.hpp:167
ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_ACC auto shfl_down(TWarp const &warp, T value, std::uint32_t offset, std::int32_t width=0)
Exchange data between threads within a warp. It copies from a lane with higher ID relative to caller....
Definition Traits.hpp:304
ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_ACC auto getSize(TWarp const &warp) -> std::int32_t
Returns warp size.
Definition Traits.hpp:73
ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_ACC auto shfl_xor(TWarp const &warp, T value, std::int32_t mask, std::int32_t width=0)
Exchange data between threads within a warp. It copies from a lane based on bitwise XOR of own lane I...
Definition Traits.hpp:342
ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_ACC auto activemask(TWarp const &warp) -> typename TWarp::mask_type
Returns a 32- or 64-bit unsigned integer (depending on the accelerator) whose Nth bit is set if and o...
Definition Traits.hpp:121
ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_ACC constexpr auto getSizeCompileTime() -> std::int32_t
If the warp size is available as a compile-time constant returns its value; otherwise returns 0.
Definition Traits.hpp:84
ALPAKA_NO_HOST_ACC_WARNING ALPAKA_FN_ACC auto shfl(TWarp const &warp, T value, std::int32_t srcLane, std::int32_t width=0)
Exchange data between threads within a warp.
Definition Traits.hpp:228