15# include <sycl/sycl.hpp>
18# include <type_traits>
20namespace alpaka::detail
22 template<
typename T_Scope>
23 struct SyclMemoryScope
28 struct SyclMemoryScope<
alpaka::onAcc::scope::System>
30 static constexpr auto value = sycl::memory_scope::system;
34 struct SyclMemoryScope<
alpaka::onAcc::scope::Device>
36 static constexpr auto value = sycl::memory_scope::device;
40 struct SyclMemoryScope<
alpaka::onAcc::scope::Block>
42 static constexpr auto value = sycl::memory_scope::work_group;
45 template<
typename T,
typename T_Scope>
46 using sycl_atomic_ref = sycl::atomic_ref<T, sycl::memory_order::relaxed, SyclMemoryScope<T_Scope>::value>;
48 template<
typename T_Scope,
typename T,
typename TOp>
49 inline auto callAtomicOp(T*
const addr, TOp&& op)
51 auto ref = sycl_atomic_ref<T, T_Scope>{*addr};
55 template<
typename TRef,
typename T,
typename TEval>
56 inline auto casWithCondition(T*
const addr, TEval&& eval)
58 auto ref = TRef{*addr};
59 auto old_val = ref.load();
62 while(!ref.compare_exchange_weak(old_val, eval(old_val)))
70namespace alpaka::onAcc::internalCompute
74 template<
typename T,
typename T_Scope>
75 struct Atomic::Op<
alpaka::operation::Add, onAcc::internal::SyclAtomic, T, T_Scope>
77 static_assert(std::is_integral_v<T> || std::is_floating_point_v<T>,
"SYCL atomics do not support this type");
79 static auto atomicOp(onAcc::internal::SyclAtomic
const&, T*
const addr, T
const& value) -> T
81 return alpaka::detail::callAtomicOp<T_Scope>(addr, [&value](
auto& ref) {
return ref.fetch_add(value); });
87 template<
typename T,
typename T_Scope>
88 struct Atomic::Op<
alpaka::operation::Sub, onAcc::internal::SyclAtomic, T, T_Scope>
90 static_assert(std::is_integral_v<T> || std::is_floating_point_v<T>,
"SYCL atomics do not support this type");
92 static auto atomicOp(onAcc::internal::SyclAtomic
const&, T*
const addr, T
const& value) -> T
94 return alpaka::detail::callAtomicOp<T_Scope>(addr, [&value](
auto& ref) {
return ref.fetch_sub(value); });
100 template<
typename T,
typename T_Scope>
101 struct Atomic::Op<
alpaka::operation::Min, onAcc::internal::SyclAtomic, T, T_Scope>
103 static_assert(std::is_integral_v<T> || std::is_floating_point_v<T>,
"SYCL atomics do not support this type");
105 static auto atomicOp(onAcc::internal::SyclAtomic
const&, T*
const addr, T
const& value) -> T
107 return alpaka::detail::callAtomicOp<T_Scope>(addr, [&value](
auto& ref) {
return ref.fetch_min(value); });
113 template<
typename T,
typename T_Scope>
114 struct Atomic::Op<
alpaka::operation::Max, onAcc::internal::SyclAtomic, T, T_Scope>
116 static_assert(std::is_integral_v<T> || std::is_floating_point_v<T>,
"SYCL atomics do not support this type");
118 static auto atomicOp(onAcc::internal::SyclAtomic
const&, T*
const addr, T
const& value) -> T
120 return alpaka::detail::callAtomicOp<T_Scope>(addr, [&value](
auto& ref) {
return ref.fetch_max(value); });
126 template<
typename T,
typename T_Scope>
127 struct Atomic::Op<
alpaka::operation::Exch, onAcc::internal::SyclAtomic, T, T_Scope>
130 (std::is_integral_v<T> || std::is_floating_point_v<T>) and (
sizeof(T) == 4 ||
sizeof(T) == 8),
131 "SYCL atomics do not support this type");
133 static auto atomicOp(onAcc::internal::SyclAtomic
const&, T*
const addr, T
const& value) -> T
135 return alpaka::detail::callAtomicOp<T_Scope>(addr, [&value](
auto& ref) {
return ref.exchange(value); });
141 template<
typename T,
typename T_Scope>
142 struct Atomic::Op<
alpaka::operation::Inc, onAcc::internal::SyclAtomic, T, T_Scope>
145 std::is_unsigned_v<T> && (
sizeof(T) == 4 ||
sizeof(T) == 8),
146 "SYCL atomics support only 32- and 64-bits unsigned integral types");
148 static auto atomicOp(onAcc::internal::SyclAtomic
const&, T*
const addr, T
const& value) -> T
150 auto inc = [&value](
auto old_val)
151 {
return (old_val >= value) ?
static_cast<T
>(0) : (old_val + static_cast<T>(1)); };
152 return alpaka::detail::casWithCondition<alpaka::detail::sycl_atomic_ref<T, T_Scope>>(addr, inc);
158 template<
typename T,
typename T_Scope>
159 struct Atomic::Op<
alpaka::operation::Dec, onAcc::internal::SyclAtomic, T, T_Scope>
162 std::is_unsigned_v<T> && (
sizeof(T) == 4 ||
sizeof(T) == 8),
163 "SYCL atomics support only 32- and 64-bits unsigned integral types");
165 static auto atomicOp(onAcc::internal::SyclAtomic
const&, T*
const addr, T
const& value) -> T
167 auto dec = [&value](
auto& old_val)
168 {
return ((old_val == 0) || (old_val > value)) ? value : (old_val -
static_cast<T
>(1)); };
169 return alpaka::detail::casWithCondition<alpaka::detail::sycl_atomic_ref<T, T_Scope>>(addr, dec);
175 template<
typename T,
typename T_Scope>
176 struct Atomic::Op<
alpaka::operation::And, onAcc::internal::SyclAtomic, T, T_Scope>
178 static_assert(std::is_integral_v<T>,
"Bitwise operations only supported for integral types.");
180 static auto atomicOp(onAcc::internal::SyclAtomic
const&, T*
const addr, T
const& value) -> T
182 return alpaka::detail::callAtomicOp<T_Scope>(addr, [&value](
auto& ref) {
return ref.fetch_and(value); });
188 template<
typename T,
typename T_Scope>
189 struct Atomic::Op<
alpaka::operation::Or, onAcc::internal::SyclAtomic, T, T_Scope>
191 static_assert(std::is_integral_v<T>,
"Bitwise operations only supported for integral types.");
193 static auto atomicOp(onAcc::internal::SyclAtomic
const&, T*
const addr, T
const& value) -> T
195 return alpaka::detail::callAtomicOp<T_Scope>(addr, [&value](
auto& ref) {
return ref.fetch_or(value); });
201 template<
typename T,
typename T_Scope>
202 struct Atomic::Op<
alpaka::operation::Xor, onAcc::internal::SyclAtomic, T, T_Scope>
204 static_assert(std::is_integral_v<T>,
"Bitwise operations only supported for integral types.");
206 static auto atomicOp(onAcc::internal::SyclAtomic
const&, T*
const addr, T
const& value) -> T
208 return alpaka::detail::callAtomicOp<T_Scope>(addr, [&value](
auto& ref) {
return ref.fetch_xor(value); });
214 template<
typename T,
typename T_Scope>
215 struct Atomic::Op<
alpaka::operation::Cas, onAcc::internal::SyclAtomic, T, T_Scope>
217 static_assert(std::is_integral_v<T> || std::is_floating_point_v<T>,
"SYCL atomics do not support this type");
219 static auto atomicOp(onAcc::internal::SyclAtomic
const&, T*
const addr, T
const& expected, T
const& desired)
222 auto cas = [&expected, &desired](
auto& ref)
224 auto expected_ = expected;
230 ref.compare_exchange_strong(expected_, desired);
237 return alpaka::detail::callAtomicOp<T_Scope>(addr, cas);
constexpr auto atomicOp(auto const &acc, T *const addr, T const &value, T_Scope const scope=T_Scope()) -> T
Executes the given operation atomically.