Project
Loading...
Searching...
No Matches
GPUCommonMath.h
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
14
15#ifndef GPUCOMMONMATH_H
16#define GPUCOMMONMATH_H
17
18#include "GPUCommonDef.h"
19
20#if defined(__CUDACC__) && !defined(__clang__) && !defined(GPUCA_GPUCODE_COMPILEKERNELS) && !defined(GPUCA_GPUCODE_HOSTONLY)
21#include <sm_20_atomic_functions.h>
22#endif
23
24#if !defined(GPUCA_GPUCODE_DEVICE)
25#include <cmath>
26#include <algorithm>
27#include <atomic>
28#include <limits>
29#include <cstring>
30#endif
31
32#if !defined(GPUCA_GPUCODE_COMPILEKERNELS) && (!defined(GPUCA_GPUCODE_DEVICE) || defined(__CUDACC__) || defined(__HIPCC__))
33#include <cstdint>
34#endif
35
36// GPUCA_CHOICE Syntax: GPUCA_CHOICE(Host, CUDA&HIP, OpenCL&Metal)
37#if defined(GPUCA_GPUCODE_DEVICE) && (defined(__CUDACC__) || defined(__HIPCC__)) // clang-format off
38 #define GPUCA_CHOICE(c1, c2, c3) (c2) // Select second option for CUDA and HIP
39#elif defined(GPUCA_GPUCODE_DEVICE) && (defined(__OPENCL__) || defined(__METAL__))
40 #define GPUCA_CHOICE(c1, c2, c3) (c3) // Select third option for OpenCL and Metal
41#else
42 #define GPUCA_CHOICE(c1, c2, c3) (c1) // Select first option for Host
43#endif // clang-format on
44
45namespace o2::gpu
46{
47
49{
50 public:
51 GPUd() static float2 MakeFloat2(float x, float y); // TODO: Find better appraoch that is constexpr
52
53 template <class T>
54 GPUhd() constexpr static T Min(const T x, const T y)
55 {
56 return GPUCA_CHOICE(std::min(x, y), min(x, y), min(x, y));
57 }
58 template <class T>
59 GPUhd() constexpr static T Max(const T x, const T y)
60 {
61 return GPUCA_CHOICE(std::max(x, y), max(x, y), max(x, y));
62 }
63 template <class T, class S, class R>
64 GPUd() static T MinWithRef(T x, T y, S refX, S refY, R& r);
65 template <class T, class S, class R>
66 GPUd() static T MaxWithRef(T x, T y, S refX, S refY, R& r);
67 template <class T, class S, class R>
68 GPUd() static T MaxWithRef(T x, T y, T z, T w, S refX, S refY, S refZ, S refW, R& r);
69 template <class T>
70 GPUdi() constexpr static T Clamp(const T v, const T lo, const T hi)
71 {
72 return Max(lo, Min(v, hi));
73 }
74 GPUhdni() constexpr static float Sqrt(float x);
75 GPUd() constexpr static double Sqrtd(double x);
76 GPUd() static float InvSqrt(float x);
77 template <class T>
78 GPUdi() constexpr static T Square(T x)
79 {
80 return x * x;
81 }
82 template <class T>
83 GPUhd() constexpr static T Abs(T x);
84 GPUd() constexpr static float ASin(float x);
85 GPUd() constexpr static double ASind(double x);
86 GPUd() constexpr static float ACos(float x);
87 GPUd() constexpr static double ACosd(double x);
88 GPUd() constexpr static float ATan(float x);
89 GPUd() constexpr static double ATand(double x);
90 GPUhd() constexpr static float ATan2(float y, float x);
91 GPUd() constexpr static double ATan2d(double y, double x);
92 GPUd() constexpr static float Sin(float x);
93 GPUd() constexpr static double Sind(double x);
94 GPUd() constexpr static float Cos(float x);
95 GPUd() constexpr static double Cosd(double x);
96 GPUhdni() static void SinCos(float x, float& s, float& c);
97 GPUhdni() static void SinCosd(double x, double& s, double& c);
98 GPUd() constexpr static float Tan(float x);
99 GPUd() constexpr static double Tand(double x);
100 GPUd() constexpr static float Pow(float x, float y);
101 GPUd() constexpr static float Log(float x);
102 GPUd() constexpr static double Logd(double x);
103 GPUd() constexpr static float Exp(float x);
104 GPUd() constexpr static double Expd(double x);
105 GPUhdni() constexpr static float Copysign(float x, float y) { return GPUCA_CHOICE(std::copysignf(x, y), copysignf(x, y), copysign(x, y)); }
106 GPUd() constexpr static double Copysignd(double x, double y);
107 GPUd() constexpr static float TwoPi() { return 6.2831853f; }
108 GPUd() constexpr static float Pi() { return 3.1415927f; }
109 GPUd() constexpr static float Round(float x);
110 GPUd() constexpr static float Floor(float x) { return GPUCA_CHOICE(floorf(x), floorf(x), floor(x)); }
111 GPUd() static uint32_t Float2UIntReint(const float& x);
112 GPUd() constexpr static uint32_t Float2UIntRn(float x) { return (uint32_t)(int32_t)(x + 0.5f); }
113 GPUd() constexpr static int32_t Float2IntRn(float x);
114 GPUd() constexpr static float Modf(float x, float y);
115 GPUhdi() static float Remainderf(float x, float y);
116 GPUd() constexpr static bool Finite(float x);
117 GPUd() constexpr static bool IsNaN(float x);
118#ifndef __FAST_MATH__
119 GPUd() constexpr static float QuietNaN() { return GPUCA_CHOICE(std::numeric_limits<float>::quiet_NaN(), __builtin_nanf(""), NAN); }
120#endif
121 GPUd() constexpr static uint32_t Clz(uint32_t val);
122 GPUd() constexpr static uint32_t Ctz(uint32_t val);
123 GPUd() constexpr static uint32_t Popcount(uint32_t val);
124 GPUd() constexpr static uint32_t Clz64(uint64_t val);
125 GPUd() constexpr static uint32_t Ctz64(uint64_t val);
126 GPUd() constexpr static uint32_t Popcount64(uint64_t val);
127
128 GPUd() static void memcpy(void* dst, const void* src, size_t size);
129
130 GPUhdi() constexpr static float Hypot(float x, float y) { return Sqrt(x * x + y * y); }
131 GPUhdi() constexpr static float Hypot(float x, float y, float z) { return Sqrt(x * x + y * y + z * z); }
132 GPUhdi() constexpr static float Hypot(float x, float y, float z, float w) { return Sqrt(x * x + y * y + z * z + w * w); }
133
134 template <typename T>
135 GPUhd() constexpr static void Swap(T& a, T& b);
136
137 template <class T>
138 GPUdi() static T AtomicExch(GPUglobalref() GPUgeneric() GPUAtomic(T) * addr, T val)
139 {
140 return GPUCommonMath::AtomicExchInternal(addr, val);
141 }
142
143 template <class T>
144 GPUdi() static bool AtomicCAS(GPUglobalref() GPUgeneric() GPUAtomic(T) * addr, T cmp, T val)
145 {
146 return GPUCommonMath::AtomicCASInternal(addr, cmp, val);
147 }
148
149 template <class T>
150 GPUdi() static T AtomicAdd(GPUglobalref() GPUgeneric() GPUAtomic(T) * addr, T val)
151 {
152 return GPUCommonMath::AtomicAddInternal(addr, val);
153 }
154 template <class T>
155 GPUdi() static void AtomicMax(GPUglobalref() GPUgeneric() GPUAtomic(T) * addr, T val)
156 {
157 GPUCommonMath::AtomicMaxInternal(addr, val);
158 }
159 template <class T>
160 GPUdi() static void AtomicMin(GPUglobalref() GPUgeneric() GPUAtomic(T) * addr, T val)
161 {
162 GPUCommonMath::AtomicMinInternal(addr, val);
163 }
164 template <class T>
165 GPUdi() static T AtomicLoad(GPUglobalref() GPUgeneric() GPUAtomic(T) * addr)
166 {
167 return GPUCommonMath::AtomicLoadInternal<T>(addr);
168 }
169 template <class T>
170 GPUdi() static void AtomicStore(GPUglobalref() GPUgeneric() GPUAtomic(T) * addr, T val)
171 {
172 GPUCommonMath::AtomicStoreInternal(addr, val);
173 }
174 template <class T>
175 GPUdi() static T AtomicExchShared(GPUsharedref() GPUgeneric() GPUAtomic(T) * addr, T val)
176 {
177 return GPUCommonMath::AtomicExchInternal(addr, val);
178 }
179 template <class T>
180 GPUdi() static T AtomicAddShared(GPUsharedref() GPUgeneric() GPUAtomic(T) * addr, T val)
181 {
182 return GPUCommonMath::AtomicAddInternal(addr, val);
183 }
184 template <class T>
185 GPUdi() static void AtomicMaxShared(GPUsharedref() GPUgeneric() GPUAtomic(T) * addr, T val)
186 {
187 GPUCommonMath::AtomicMaxInternal(addr, val);
188 }
189 template <class T>
190 GPUdi() static void AtomicMinShared(GPUsharedref() GPUgeneric() GPUAtomic(T) * addr, T val)
191 {
192 GPUCommonMath::AtomicMinInternal(addr, val);
193 }
194 GPUd() constexpr static int32_t Mul24(int32_t a, int32_t b);
195 GPUd() constexpr static float FMulRZ(float a, float b);
196
197 template <int32_t I, class T>
198 GPUd() constexpr static T nextMultipleOf(T val);
199
200 template <typename... Args>
201 GPUhdni() constexpr static float Sum2(float w, Args... args);
202
203 private:
204 template <class S, class T>
205 GPUd() static uint32_t AtomicExchInternal(S* addr, T val);
206 template <class S, class T>
207 GPUd() static bool AtomicCASInternal(S* addr, T cmp, T val);
208 template <class S, class T>
209 GPUd() static uint32_t AtomicAddInternal(S* addr, T val);
210 template <class S, class T>
211 GPUd() static void AtomicMaxInternal(S* addr, T val);
212 template <class S, class T>
213 GPUd() static void AtomicMinInternal(S* addr, T val);
214 template <class T, class S>
215 GPUd() static T AtomicLoadInternal(S* addr);
216 template <class S, class T>
217 GPUd() static void AtomicStoreInternal(S* addr, T val);
218};
219
221
222template <typename... Args>
223GPUhdi() constexpr float GPUCommonMath::Sum2(float w, Args... args)
224{
225 if constexpr (sizeof...(Args) == 0) {
226 return w * w;
227 } else {
228 return w * w + Sum2(args...);
229 }
230 return 0;
231}
232
233GPUdi() void GPUCommonMath::memcpy(void* dst, const void* src, size_t size)
234{
235#ifndef GPUCA_GPUCODE_DEVICE
236 std::memcpy(dst, src, size);
237#elif defined(__CUDACC__) || defined(__HIPCC__)
238 ::memcpy(dst, src, size);
239#elif defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
240 __builtin_memcpy(dst, src, size);
241#else
242 char* d = (char*)dst;
243 const char* s = (const char*)src;
244 for (size_t i = 0; i < size; i++) {
245 d[i] = s[i];
246 }
247#endif
248}
249
250template <int32_t I, class T>
251GPUdi() constexpr T GPUCommonMath::nextMultipleOf(T val)
252{
253 if constexpr (I & (I - 1)) {
254 T tmp = val % I;
255 if (tmp) {
256 val += I - tmp;
257 }
258 return val;
259 } else {
260 return (val + I - 1) & ~(T)(I - 1);
261 }
262 return 0; // BUG: Cuda complains about missing return value with constexpr if
263}
264
265GPUdi() float2 GPUCommonMath::MakeFloat2(float x, float y)
266{
267#if !defined(GPUCA_GPUCODE) || defined(__OPENCL__) || defined(__OPENCL_HOST__) || defined(__METAL__) || defined(__METAL_HOST__) || defined(__CPUDEVICE__)
268 float2 ret = {x, y};
269 return ret;
270#else
271 return make_float2(x, y);
272#endif // GPUCA_GPUCODE
273}
274
275GPUdi() constexpr float GPUCommonMath::Modf(float x, float y) { return GPUCA_CHOICE(fmodf(x, y), fmodf(x, y), fmod(x, y)); }
276GPUhdi() float GPUCommonMath::Remainderf(float x, float y)
277{
278#ifdef __METAL__ // MSL has no remainder(), so reduce with fmod, which is exact
279 const float r = fmod(x, y), h = 0.5f * fabs(y), a = fabs(r);
280 if (a > h) {
281 return r - copysign(y, r);
282 }
283 if (a == h && fmod((x - r) / y, 2.f) != 0.f) { // the quotient of a tie is rounded to even
284 return r - copysign(y, r);
285 }
286 return r;
287#else
288 return GPUCA_CHOICE(std::remainderf(x, y), remainderf(x, y), remainder(x, y));
289#endif
290}
291
292GPUdi() uint32_t GPUCommonMath::Float2UIntReint(const float& x)
293{
294#if defined(GPUCA_GPUCODE_DEVICE) && (defined(__CUDACC__) || defined(__HIPCC__))
295 return __float_as_uint(x);
296#elif defined(GPUCA_GPUCODE_DEVICE) && defined(__OPENCL__)
297 return as_uint(x);
298#elif !defined(GPUCA_GPUCODE_DEVICE)
299 return __builtin_bit_cast(uint32_t, x);
300#else
301 return reinterpret_cast<const uint32_t&>(x);
302#endif
303}
304
305GPUCA_DETERMINISTIC_CODE( // clang-format off
306GPUdi() constexpr float GPUCommonMath::Round(float x) { return GPUCA_CHOICE(roundf(x), roundf(x), round(x)); }
307GPUdi() constexpr int32_t GPUCommonMath::Float2IntRn(float x) { return (int32_t)Round(x); }
308GPUhdi() constexpr float GPUCommonMath::Sqrt(float x) { return GPUCA_CHOICE(sqrtf(x), (float)sqrt((double)x), sqrt(x)); }
309GPUdi() constexpr float GPUCommonMath::ATan(float x) { return GPUCA_CHOICE((float)atan((double)x), (float)atan((double)x), atan(x)); }
310GPUhdi() constexpr float GPUCommonMath::ATan2(float y, float x) { return GPUCA_CHOICE((float)atan2((double)y, (double)x), (float)atan2((double)y, (double)x), atan2(y, x)); }
311GPUdi() constexpr float GPUCommonMath::Sin(float x) { return GPUCA_CHOICE((float)sin((double)x), (float)sin((double)x), sin(x)); }
312GPUdi() constexpr float GPUCommonMath::Cos(float x) { return GPUCA_CHOICE((float)cos((double)x), (float)cos((double)x), cos(x)); }
313GPUdi() constexpr float GPUCommonMath::Tan(float x) { return GPUCA_CHOICE((float)tanf((double)x), (float)tanf((double)x), tan(x)); }
314GPUdi() constexpr float GPUCommonMath::Pow(float x, float y) { return GPUCA_CHOICE((float)pow((double)x, (double)y), pow((double)x, (double)y), pow(x, y)); }
315GPUdi() constexpr float GPUCommonMath::ASin(float x) { return GPUCA_CHOICE((float)asin((double)x), (float)asin((double)x), asin(x)); }
316GPUdi() constexpr float GPUCommonMath::ACos(float x) { return GPUCA_CHOICE((float)acos((double)x), (float)acos((double)x), acos(x)); }
317GPUdi() constexpr float GPUCommonMath::Log(float x) { return GPUCA_CHOICE((float)log((double)x), (float)log((double)x), log(x)); }
318GPUdi() constexpr float GPUCommonMath::Exp(float x) { return GPUCA_CHOICE((float)exp((double)x), (float)exp((double)x), exp(x)); }
319GPUdi() constexpr bool GPUCommonMath::Finite(float x) { return GPUCA_CHOICE(std::isfinite(x), isfinite(x), isfinite(x)); }
320GPUdi() constexpr bool GPUCommonMath::IsNaN(float x) { return GPUCA_CHOICE(std::isnan(x), isnan(x), isnan(x)); }
321, // !GPUCA_DETERMINISTIC_CODE
322GPUdi() constexpr float GPUCommonMath::Round(float x) { return GPUCA_CHOICE(roundf(x), rintf(x), rint(x)); }
323GPUdi() constexpr int32_t GPUCommonMath::Float2IntRn(float x) { return GPUCA_CHOICE((int32_t)Round(x), __float2int_rn(x), (int32_t)Round(x)); }
324GPUhdi() constexpr float GPUCommonMath::Sqrt(float x) { return GPUCA_CHOICE(sqrtf(x), sqrtf(x), sqrt(x)); }
325GPUdi() constexpr float GPUCommonMath::ATan(float x) { return GPUCA_CHOICE(atanf(x), atanf(x), atan(x)); }
326GPUhdi() constexpr float GPUCommonMath::ATan2(float y, float x) { return GPUCA_CHOICE(atan2f(y, x), atan2f(y, x), atan2(y, x)); }
327GPUdi() constexpr float GPUCommonMath::Sin(float x) { return GPUCA_CHOICE(sinf(x), sinf(x), sin(x)); }
328GPUdi() constexpr float GPUCommonMath::Cos(float x) { return GPUCA_CHOICE(cosf(x), cosf(x), cos(x)); }
329GPUdi() constexpr float GPUCommonMath::Tan(float x) { return GPUCA_CHOICE(tanf(x), tanf(x), tan(x)); }
330GPUdi() constexpr float GPUCommonMath::Pow(float x, float y) { return GPUCA_CHOICE(powf(x, y), powf(x, y), pow(x, y)); }
331GPUdi() constexpr float GPUCommonMath::ASin(float x) { return GPUCA_CHOICE(asinf(x), asinf(x), asin(x)); }
332GPUdi() constexpr float GPUCommonMath::ACos(float x) { return GPUCA_CHOICE(acosf(x), acosf(x), acos(x)); }
333GPUdi() constexpr float GPUCommonMath::Log(float x) { return GPUCA_CHOICE(logf(x), logf(x), log(x)); }
334GPUdi() constexpr float GPUCommonMath::Exp(float x) { return GPUCA_CHOICE(expf(x), expf(x), exp(x)); }
335GPUdi() constexpr bool GPUCommonMath::Finite(float x) { return true; }
336GPUdi() constexpr bool GPUCommonMath::IsNaN(float x) { return false; }
337) // clang-format on
338
339GPUhdi() void GPUCommonMath::SinCos(float x, float& s, float& c)
340{
341 GPUCA_DETERMINISTIC_CODE( // clang-format off
342 s = sin((double)x);
343 c = cos((double)x);
344 , // !GPUCA_DETERMINISTIC_CODE
345#if !defined(GPUCA_GPUCODE_DEVICE) && defined(__APPLE__)
346 __sincosf(x, &s, &c);
347#elif !defined(GPUCA_GPUCODE_DEVICE) && (defined(__GNU_SOURCE__) || defined(_GNU_SOURCE) || defined(GPUCA_GPUCODE))
348 sincosf(x, &s, &c);
349#elif defined(__METAL__) // MSL's sincos returns sin and takes cos by thread
350 // reference, so it cannot write through a generic one
351 float metalCos;
352 s = sincos(x, metalCos);
353 c = metalCos;
354#else
355 GPUCA_CHOICE((void)((s = sinf(x)) + (c = cosf(x))), sincosf(x, &s, &c), s = sincos(x, &c));
356#endif
357 ) // clang-format on
358}
359
360#ifndef __METAL__ // MSL has no double; math_utils::sincosd is excluded there, as it is for OpenCL
361GPUhdi() void GPUCommonMath::SinCosd(double x, double& s, double& c)
362{
363#if !defined(GPUCA_GPUCODE_DEVICE) && defined(__APPLE__)
364 __sincos(x, &s, &c);
365#elif !defined(GPUCA_GPUCODE_DEVICE) && (defined(__GNU_SOURCE__) || defined(_GNU_SOURCE) || defined(GPUCA_GPUCODE))
366 sincos(x, &s, &c);
367#else
368 GPUCA_CHOICE((void)((s = sin(x)) + (c = cos(x))), sincos(x, &s, &c), s = sincos(x, &c));
369#endif
370}
371
372GPUdi() constexpr double GPUCommonMath::Sqrtd(double x) { return GPUCA_CHOICE(std::sqrt(x), sqrt(x), sqrt(x)); }
373GPUdi() constexpr double GPUCommonMath::ASind(double x) { return GPUCA_CHOICE(std::asin(x), asin(x), asin(x)); }
374GPUdi() constexpr double GPUCommonMath::ACosd(double x) { return GPUCA_CHOICE(std::acos(x), acos(x), acos(x)); }
375GPUdi() constexpr double GPUCommonMath::ATand(double x) { return GPUCA_CHOICE(std::atan(x), atan(x), atan(x)); }
376GPUdi() constexpr double GPUCommonMath::ATan2d(double y, double x) { return GPUCA_CHOICE(std::atan2(y, x), atan2(y, x), atan2(y, x)); }
377GPUdi() constexpr double GPUCommonMath::Sind(double x) { return GPUCA_CHOICE(std::sin(x), sin(x), sin(x)); }
378GPUdi() constexpr double GPUCommonMath::Cosd(double x) { return GPUCA_CHOICE(std::cos(x), cos(x), cos(x)); }
379GPUdi() constexpr double GPUCommonMath::Tand(double x) { return GPUCA_CHOICE(std::tan(x), tan(x), tan(x)); }
380GPUdi() constexpr double GPUCommonMath::Logd(double x) { return GPUCA_CHOICE(std::log(x), log(x), log(x)); }
381GPUdi() constexpr double GPUCommonMath::Expd(double x) { return GPUCA_CHOICE(std::exp(x), exp(x), exp(x)); }
382GPUdi() constexpr double GPUCommonMath::Copysignd(double x, double y) { return GPUCA_CHOICE(std::copysign(x, y), copysign(x, y), copysign(x, y)); }
383#endif
384
385GPUdi() constexpr uint32_t GPUCommonMath::Clz(uint32_t x)
386{
387#if (defined(__GNUC__) || defined(__clang__) || defined(__CUDACC__) || defined(__HIPCC__))
388 return x == 0 ? 32 : GPUCA_CHOICE(__builtin_clz(x), __clz(x), __builtin_clz(x)); // use builtin if available
389#else
390 for (int32_t i = 31; i >= 0; i--) {
391 if (x & (1u << i)) {
392 return (31 - i);
393 }
394 }
395 return 32;
396#endif
397}
398
399GPUdi() constexpr uint32_t GPUCommonMath::Ctz(uint32_t x)
400{
401#if (defined(__GNUC__) || defined(__clang__) || defined(__CUDACC__) || defined(__HIPCC__))
402 return x == 0 ? 32 : GPUCA_CHOICE(__builtin_ctz(x), __ffs(x) - 1, __builtin_ctz(x));
403#else
404 for (uint32_t i = 0; i < 32; ++i) {
405 if (x & (1u << i)) {
406 return i;
407 }
408 }
409 return 32;
410#endif
411}
412
413GPUdi() constexpr uint32_t GPUCommonMath::Popcount(uint32_t x)
414{
415#if (defined(__GNUC__) || defined(__clang__) || defined(__CUDACC__) || defined(__HIPCC__)) && !defined(__OPENCL__) // TODO: remove OPENCL when reported SPIR-V bug is fixed
416 // use builtin if available
417 return GPUCA_CHOICE(__builtin_popcount(x), __popc(x), __builtin_popcount(x));
418#else
419 x = x - ((x >> 1) & 0x55555555);
420 x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
421 return (((x + (x >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
422#endif
423}
424
425GPUdi() constexpr uint32_t GPUCommonMath::Clz64(uint64_t x)
426{
427#if (defined(__GNUC__) || defined(__clang__) || defined(__CUDACC__) || defined(__HIPCC__))
428 return x == 0 ? 64 : GPUCA_CHOICE(__builtin_clzll(x), __clzll(x), __builtin_clzll(x)); // use builtin if available
429#else
430 for (int32_t i = 63; i >= 0; i--) {
431 if (x & (1ull << i)) {
432 return (63 - i);
433 }
434 }
435 return 64;
436#endif
437}
438
439GPUdi() constexpr uint32_t GPUCommonMath::Ctz64(uint64_t x)
440{
441#if (defined(__GNUC__) || defined(__clang__) || defined(__CUDACC__) || defined(__HIPCC__))
442 return x == 0 ? 64 : GPUCA_CHOICE(__builtin_ctzll(x), __ffsll((long long)x) - 1, __builtin_ctzll(x));
443#else
444 for (uint32_t i = 0; i < 64; ++i) {
445 if (x & (1ull << i)) {
446 return i;
447 }
448 }
449 return 64;
450#endif
451}
452
453GPUdi() constexpr uint32_t GPUCommonMath::Popcount64(uint64_t x)
454{
455#if (defined(__GNUC__) || defined(__clang__) || defined(__CUDACC__) || defined(__HIPCC__)) && !defined(__OPENCL__) // TODO: remove OPENCL when reported SPIR-V bug is fixed
456 // use builtin if available
457 return GPUCA_CHOICE(__builtin_popcountll(x), __popcll(x), __builtin_popcountll(x));
458#else
459 return Popcount((uint32_t)x) + Popcount((uint32_t)(x >> 32));
460#endif
461}
462
463template <typename T>
464GPUhdi() constexpr void GPUCommonMath::Swap(T& a, T& b)
465{
466#ifndef GPUCA_GPUCODE_DEVICE
467 std::swap(a, b);
468#else
469 T tmp = a;
470 a = b;
471 b = tmp;
472#endif
473}
474
475template <class T, class S, class R>
476GPUdi() T GPUCommonMath::MinWithRef(T x, T y, S refX, S refY, R& r)
477{
478 if (x < y) {
479 r = refX;
480 return x;
481 }
482 r = refY;
483 return y;
484}
485
486template <class T, class S, class R>
487GPUdi() T GPUCommonMath::MaxWithRef(T x, T y, S refX, S refY, R& r)
488{
489 if (x > y) {
490 r = refX;
491 return x;
492 }
493 r = refY;
494 return y;
495}
496
497template <class T, class S, class R>
498GPUdi() T GPUCommonMath::MaxWithRef(T x, T y, T z, T w, S refX, S refY, S refZ, S refW, R& r)
499{
500 T retVal = x;
501 S retRef = refX;
502 if (y > retVal) {
503 retVal = y;
504 retRef = refY;
505 }
506 if (z > retVal) {
507 retVal = z;
508 retRef = refZ;
509 }
510 if (w > retVal) {
511 retVal = w;
512 retRef = refW;
513 }
514 r = retRef;
515 return retVal;
516}
517
518GPUdi() float GPUCommonMath::InvSqrt(float _x)
519{
520 GPUCA_DETERMINISTIC_CODE( // clang-format off
521 return 1.f / Sqrt(_x);
522 , // !GPUCA_DETERMINISTIC_CODE
523#if defined(__CUDACC__) || defined(__HIPCC__)
524 return __frsqrt_rn(_x);
525#elif (defined(__OPENCL__) || defined(__METAL__)) && defined(__clang__)
526 return 1.f / sqrt(_x);
527#elif !defined(__OPENCL__) && (defined(__FAST_MATH__) || defined(__clang__))
528 return 1.f / sqrtf(_x);
529#else
530 union {
531 float f;
532 int32_t i;
533 } x = {_x};
534 const float xhalf = 0.5f * x.f;
535 x.i = 0x5f3759df - (x.i >> 1);
536 x.f = x.f * (1.5f - xhalf * x.f * x.f);
537 return x.f;
538#endif
539 ) // clang-format on
540}
541
542template <>
543GPUhdi() constexpr float GPUCommonMath::Abs<float>(float x)
544{
545 return GPUCA_CHOICE(fabsf(x), fabsf(x), fabs(x));
546}
547
548#ifdef __METAL__ // MSL has no double, so the keyword names the emulated binary64 and fabs does not apply
549template <>
550GPUhdi() constexpr double GPUCommonMath::Abs<double>(double x)
551{
552 return GPUdoubleBinary64::fromBits(x.bits() & ~GPUCA_B64_SIGN);
553}
554// metal::fabs is not constant-evaluable, so this also fails to compile if the
555// specialisation above is ever dropped and the call falls back to it in float
556static_assert(GPUCommonMath::Abs<double>(GPUdoubleBinary64::fromBits(0xBFF0000000000001ULL)).bits() == 0x3FF0000000000001ULL,
557 "Abs on the emulated double must clear the sign bit and keep every other one");
558#else
559template <>
560GPUhdi() constexpr double GPUCommonMath::Abs<double>(double x)
561{
562 return GPUCA_CHOICE(fabs(x), fabs(x), fabs(x));
563}
564#endif
565
566template <>
567GPUhdi() constexpr int32_t GPUCommonMath::Abs<int32_t>(int32_t x)
568{
569 return GPUCA_CHOICE(abs(x), abs(x), abs(x));
570}
571
572#ifdef __METAL__
573// The counters these operate on are plain integers in the transferred structures,
574// as they are for CUDA and HIP. MSL's atomic operations want metal::atomic, which
575// has the same size and alignment. The overload is picked by address space, and a
576// threadgroup pointer has to keep its own: casting one to a device atomic is an
577// error while the qualifier is visible, but a generic pointer resolves here to
578// device and the cast then goes through silently.
579template <class T>
580GPUdi() threadgroup metal::atomic<T>* GPUCommonMathMetalAtomic(threadgroup T* p)
581{
582 return reinterpret_cast<threadgroup metal::atomic<T>*>(p);
583}
584template <class T>
585GPUdi() device metal::atomic<T>* GPUCommonMathMetalAtomic(T* p)
586{
587 return (device metal::atomic<T>*)p;
588}
589#endif
590
591template <class S, class T>
592GPUdi() uint32_t GPUCommonMath::AtomicExchInternal(S* addr, T val)
593{
594#if defined(GPUCA_GPUCODE) && defined(__OPENCL__) && (!defined(__clang__) || defined(GPUCA_OPENCL_CLANG_C11_ATOMICS))
595 return ::atomic_exchange(addr, val);
596#elif defined(GPUCA_GPUCODE) && defined(__OPENCL__)
597 return ::atomic_xchg(addr, val);
598#elif defined(GPUCA_GPUCODE) && (defined(__CUDACC__) || defined(__HIPCC__))
599 return ::atomicExch(addr, val);
600#elif defined(GPUCA_GPUCODE) && defined(__METAL__)
601 return atomic_exchange_explicit(GPUCommonMathMetalAtomic(addr), val, memory_order_relaxed);
602#elif defined(WITH_OPENMP)
603 uint32_t old;
604 __atomic_exchange(addr, &val, &old, __ATOMIC_SEQ_CST);
605 return old;
606#else
607 return reinterpret_cast<std::atomic<T>*>(addr)->exchange(val);
608#endif
609}
610
611template <class S, class T>
612GPUdi() bool GPUCommonMath::AtomicCASInternal(S* addr, T cmp, T val)
613{
614#if defined(GPUCA_GPUCODE) && defined(__OPENCL__) && (!defined(__clang__) || defined(GPUCA_OPENCL_CLANG_C11_ATOMICS))
615 return ::atomic_compare_exchange(addr, cmp, val) == cmp;
616#elif defined(GPUCA_GPUCODE) && defined(__OPENCL__)
617 return ::atomic_cmpxchg(addr, cmp, val) == cmp;
618#elif defined(GPUCA_GPUCODE) && (defined(__CUDACC__) || defined(__HIPCC__))
619 return ::atomicCAS(addr, cmp, val) == cmp;
620#elif defined(GPUCA_GPUCODE) && defined(__METAL__)
621 return atomic_compare_exchange_weak_explicit(GPUCommonMathMetalAtomic(addr), &cmp, val, memory_order_relaxed, memory_order_relaxed);
622#elif defined(WITH_OPENMP)
623 return __atomic_compare_exchange(addr, &cmp, &val, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
624#else
625 return reinterpret_cast<std::atomic<T>*>(addr)->compare_exchange_strong(cmp, val);
626#endif
627}
628
629template <class S, class T>
630GPUdi() uint32_t GPUCommonMath::AtomicAddInternal(S* addr, T val)
631{
632#if defined(GPUCA_GPUCODE) && defined(__OPENCL__) && (!defined(__clang__) || defined(GPUCA_OPENCL_CLANG_C11_ATOMICS))
633 return ::atomic_fetch_add(addr, val);
634#elif defined(GPUCA_GPUCODE) && defined(__OPENCL__)
635 return ::atomic_add(addr, val);
636#elif defined(GPUCA_GPUCODE) && (defined(__CUDACC__) || defined(__HIPCC__))
637 return ::atomicAdd(addr, val);
638#elif defined(GPUCA_GPUCODE) && defined(__METAL__)
639 return atomic_fetch_add_explicit(GPUCommonMathMetalAtomic(addr), val, memory_order_relaxed);
640#elif defined(WITH_OPENMP)
641 return __atomic_add_fetch(addr, val, __ATOMIC_SEQ_CST) - val;
642#else
643 return reinterpret_cast<std::atomic<T>*>(addr)->fetch_add(val);
644#endif
645}
646
647template <class S, class T>
648GPUdi() void GPUCommonMath::AtomicMaxInternal(S* addr, T val)
649{
650#if defined(GPUCA_GPUCODE) && defined(__OPENCL__) && (!defined(__clang__) || defined(GPUCA_OPENCL_CLANG_C11_ATOMICS))
651 ::atomic_fetch_max(addr, val);
652#elif defined(GPUCA_GPUCODE) && defined(__OPENCL__)
653 ::atomic_max(addr, val);
654#elif defined(GPUCA_GPUCODE) && (defined(__CUDACC__) || defined(__HIPCC__))
655 ::atomicMax(addr, val);
656#elif defined(GPUCA_GPUCODE) && defined(__METAL__)
657 atomic_fetch_max_explicit(GPUCommonMathMetalAtomic(addr), val, memory_order_relaxed);
658#else
659 S current;
660 while ((current = *(volatile S*)addr) < val && !AtomicCASInternal(addr, current, val)) {
661 }
662#endif // GPUCA_GPUCODE
663}
664
665template <class S, class T>
666GPUdi() void GPUCommonMath::AtomicMinInternal(S* addr, T val)
667{
668#if defined(GPUCA_GPUCODE) && defined(__OPENCL__) && (!defined(__clang__) || defined(GPUCA_OPENCL_CLANG_C11_ATOMICS))
669 ::atomic_fetch_min(addr, val);
670#elif defined(GPUCA_GPUCODE) && defined(__OPENCL__)
671 ::atomic_min(addr, val);
672#elif defined(GPUCA_GPUCODE) && (defined(__CUDACC__) || defined(__HIPCC__))
673 ::atomicMin(addr, val);
674#elif defined(GPUCA_GPUCODE) && defined(__METAL__)
675 atomic_fetch_min_explicit(GPUCommonMathMetalAtomic(addr), val, memory_order_relaxed);
676#else
677 S current;
678 while ((current = *(volatile S*)addr) > val && !AtomicCASInternal(addr, current, val)) {
679 }
680#endif // GPUCA_GPUCODE
681}
682
683// Relaxed, as the device atomics above
684template <class T, class S>
685GPUdi() T GPUCommonMath::AtomicLoadInternal(S* addr)
686{
687#if defined(GPUCA_GPUCODE) && defined(__OPENCL__) && (!defined(__clang__) || defined(GPUCA_OPENCL_CLANG_C11_ATOMICS))
688 return ::atomic_load_explicit(addr, memory_order_relaxed);
689#elif defined(GPUCA_GPUCODE) && (defined(__OPENCL__) || defined(__CUDACC__) || defined(__HIPCC__))
690 return *(volatile S*)addr;
691#elif defined(GPUCA_GPUCODE) && defined(__METAL__)
692 return atomic_load_explicit(GPUCommonMathMetalAtomic(addr), memory_order_relaxed);
693#elif defined(WITH_OPENMP)
694 T ret;
695 __atomic_load(addr, &ret, __ATOMIC_RELAXED);
696 return ret;
697#else
698 return reinterpret_cast<std::atomic<T>*>(addr)->load(std::memory_order_relaxed);
699#endif
700}
701
702template <class S, class T>
703GPUdi() void GPUCommonMath::AtomicStoreInternal(S* addr, T val)
704{
705#if defined(GPUCA_GPUCODE) && defined(__OPENCL__) && (!defined(__clang__) || defined(GPUCA_OPENCL_CLANG_C11_ATOMICS))
706 ::atomic_store_explicit(addr, val, memory_order_relaxed);
707#elif defined(GPUCA_GPUCODE) && (defined(__OPENCL__) || defined(__CUDACC__) || defined(__HIPCC__))
708 *(volatile S*)addr = val;
709#elif defined(GPUCA_GPUCODE) && defined(__METAL__)
710 atomic_store_explicit(GPUCommonMathMetalAtomic(addr), val, memory_order_relaxed);
711#elif defined(WITH_OPENMP)
712 __atomic_store(addr, &val, __ATOMIC_RELAXED);
713#else
714 reinterpret_cast<std::atomic<T>*>(addr)->store(val, std::memory_order_relaxed);
715#endif
716}
717
718#if (defined(__CUDACC__) || defined(__HIPCC__)) && !defined(G__ROOT) && !defined(__CLING__)
719#define GPUCA_HAVE_ATOMIC_MINMAX_FLOAT
720template <>
721GPUdii() void GPUCommonMath::AtomicMaxInternal(GPUglobalref() GPUgeneric() GPUAtomic(float) * addr, float val)
722{
723 if (val == -0.f) {
724 val = 0.f;
725 }
726 if (val >= 0) {
727 AtomicMaxInternal((GPUAtomic(int32_t)*)addr, __float_as_int(val));
728 } else {
729 AtomicMinInternal((GPUAtomic(uint32_t)*)addr, __float_as_uint(val));
730 }
731}
732template <>
733GPUdii() void GPUCommonMath::AtomicMinInternal(GPUglobalref() GPUgeneric() GPUAtomic(float) * addr, float val)
734{
735 if (val == -0.f) {
736 val = 0.f;
737 }
738 if (val >= 0) {
739 AtomicMinInternal((GPUAtomic(int32_t)*)addr, __float_as_int(val));
740 } else {
741 AtomicMaxInternal((GPUAtomic(uint32_t)*)addr, __float_as_uint(val));
742 }
743}
744#endif
745
746#undef GPUCA_CHOICE
747
748} // namespace o2::gpu
749
750#endif // GPUCOMMONMATH_H
uint64_t exp(uint64_t base, uint8_t exp) noexcept
int32_t i
#define GPUsharedref()
#define GPUdii()
#define GPUAtomic(type)
#define GPUgeneric()
#define GPUglobalref()
#define GPUCA_DETERMINISTIC_CODE(det, indet)
#define GPUCA_B64_SIGN
#define GPUCA_CHOICE(c1, c2, c3)
int32_t retVal
Class for time synchronization of RawReader instances.
GPUd() const expr static float Pi()
GPUdi() static void AtomicStore(GPUglobalref() GPUgeneric() GPUAtomic(T) *addr
GPUhd() const expr static T Min(const T x
GPUd() const expr static float QuietNaN()
GPUdi() static void AtomicMax(GPUglobalref() GPUgeneric() GPUAtomic(T) *addr
GPUd() const expr static int32_t Float2IntRn(float x)
GPUd() const expr static float Round(float x)
GPUhdni() const expr static float Sqrt(float x)
GPUdi() static void AtomicMin(GPUglobalref() GPUgeneric() GPUAtomic(T) *addr
GPUdi() const expr static T Clamp(const T v
GPUd() static float2 MakeFloat2(float x
GPUhd() const expr static T Max(const T x
GPUdi() static T AtomicAddShared(GPUsharedref() GPUgeneric() GPUAtomic(T) *addr
GPUhd() const expr static T Abs(T x)
GPUdi() static T AtomicAdd(GPUglobalref() GPUgeneric() GPUAtomic(T) *addr
const void size_t size
GPUdi() static T AtomicExchShared(GPUsharedref() GPUgeneric() GPUAtomic(T) *addr
GPUd() static uint32_t Float2UIntReint(const float &x)
GPUd() static T MinWithRef(T x
GPUdi() static void AtomicMinShared(GPUsharedref() GPUgeneric() GPUAtomic(T) *addr
GPUhdi() static float Remainderf(float x
GPUdi() static T AtomicLoad(GPUglobalref() GPUgeneric() GPUAtomic(T) *addr)
GPUhdi() const expr static float Hypot(float x
GPUd() const expr static double Copysignd(double x
GPUd() const expr static uint32_t Clz(uint32_t val)
GPUdi() static void AtomicMaxShared(GPUsharedref() GPUgeneric() GPUAtomic(T) *addr
GLint GLenum GLint x
Definition glcorearb.h:403
GLenum src
Definition glcorearb.h:1767
GLsizeiptr size
Definition glcorearb.h:659
const GLdouble * v
Definition glcorearb.h:832
GLdouble f
Definition glcorearb.h:310
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLint y
Definition glcorearb.h:270
GLenum GLenum dst
Definition glcorearb.h:1767
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLuint GLfloat * val
Definition glcorearb.h:1582
GLboolean r
Definition glcorearb.h:1233
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLubyte GLubyte GLubyte GLubyte w
Definition glcorearb.h:852
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
bool isnan(float f)
constexpr size_t min
constexpr size_t max