SWDEV-333033 - add safe and unsafe atomic min and max including gfx940 and add missing nvidia support

Change-Id: I829a0a5fd49c510e77eabbcb92d1a415ef6b5a4c


[ROCm/clr commit: 4375b9f5b9]
Αυτή η υποβολή περιλαμβάνεται σε:
Brian Sumner
2022-07-11 13:57:28 -07:00
υποβλήθηκε από Brian Sumner
γονέας 40fda3cfe9
υποβολή 5a402b9c99
5 αρχεία άλλαξαν με 548 προσθήκες και 52 διαγραφές
@@ -427,20 +427,30 @@ unsigned long long atomicMin_system(unsigned long long* address, unsigned long l
__device__
inline
float atomicMin(float* address, float val) {
unsigned int* uaddr { reinterpret_cast<unsigned int*>(address) };
#if __has_builtin(__hip_atomic_load)
unsigned int tmp {__hip_atomic_load(uaddr, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT)};
#else
unsigned int tmp {__atomic_load_n(uaddr, __ATOMIC_RELAXED)};
#endif
float value = __uint_as_float(tmp);
while (val < value) {
value = atomicCAS(address, value, val);
float atomicMin(float* addr, float val) {
#if defined(__AMDGCN_UNSAFE_FP_ATOMICS__)
return unsafeAtomicMin(address, val);
#else
#if __has_builtin(__hip_atomic_load) && \
__has_builtin(__hip_atomic_compare_exchange_strong)
float value = __hip_atomic_load(addr, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT);
bool done = false;
while (!done && value > val) {
done = __hip_atomic_compare_exchange_strong(addr, &value, val,
__ATOMIC_RELAXED, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT);
}
return value;
#else
unsigned int *uaddr = (unsigned int *)addr;
unsigned int value = __atomic_load_n(uaddr, __ATOMIC_RELAXED);
bool done = false;
while (!done && __uint_as_float(value) > val) {
done = __atomic_compare_exchange_n(uaddr, &value, __float_as_uint(val), false,
__ATOMIC_RELAXED, __ATOMIC_RELAXED);
}
return __uint_as_float(value);
#endif
#endif
}
__device__
@@ -463,20 +473,30 @@ float atomicMin_system(float* address, float val) {
__device__
inline
double atomicMin(double* address, double val) {
unsigned long long* uaddr { reinterpret_cast<unsigned long long*>(address) };
#if __has_builtin(__hip_atomic_load)
unsigned long long tmp {__hip_atomic_load(uaddr, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT)};
#else
unsigned long long tmp {__atomic_load_n(uaddr, __ATOMIC_RELAXED)};
#endif
double value = __longlong_as_double(tmp);
while (val < value) {
value = atomicCAS(address, value, val);
double atomicMin(double* addr, double val) {
#if defined(__AMDGCN_UNSAFE_FP_ATOMICS__)
return unsafeAtomicMin(address, val);
#else
#if __has_builtin(__hip_atomic_load) && \
__has_builtin(__hip_atomic_compare_exchange_strong)
double value = __hip_atomic_load(addr, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT);
bool done = false;
while (!done && value > val) {
done = __hip_atomic_compare_exchange_strong(addr, &value, val,
__ATOMIC_RELAXED, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT);
}
return value;
#else
unsigned long long *uaddr = (unsigned long long *)addr;
unsigned long long value = __atomic_load_n(uaddr, __ATOMIC_RELAXED);
bool done = false;
while (!done && __longlong_as_double(value) > val) {
done = __atomic_compare_exchange_n(uaddr, &value, __double_as_longlong(val), false,
__ATOMIC_RELAXED, __ATOMIC_RELAXED);
}
return __longlong_as_double(value);
#endif
#endif
}
__device__
@@ -547,20 +567,30 @@ unsigned long long atomicMax_system(unsigned long long* address, unsigned long l
__device__
inline
float atomicMax(float* address, float val) {
unsigned int* uaddr { reinterpret_cast<unsigned int*>(address) };
#if __has_builtin(__hip_atomic_load)
unsigned int tmp {__hip_atomic_load(uaddr, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT)};
#else
unsigned int tmp {__atomic_load_n(uaddr, __ATOMIC_RELAXED)};
#endif
float value = __uint_as_float(tmp);
while (value < val) {
value = atomicCAS(address, value, val);
float atomicMax(float* addr, float val) {
#if defined(__AMDGCN_UNSAFE_FP_ATOMICS__)
return unsafeAtomicMax(addr, val);
#else
#if __has_builtin(__hip_atomic_load) && \
__has_builtin(__hip_atomic_compare_exchange_strong)
float value = __hip_atomic_load(addr, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT);
bool done = false;
while (!done && value < val) {
done = __hip_atomic_compare_exchange_strong(addr, &value, val,
__ATOMIC_RELAXED, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT);
}
return value;
#else
unsigned int *uaddr = (unsigned int *)addr;
unsigned int value = __atomic_load_n(uaddr, __ATOMIC_RELAXED);
bool done = false;
while (!done && __uint_as_float(value) < val) {
done = __atomic_compare_exchange_n(uaddr, &value, __float_as_uint(val), false,
__ATOMIC_RELAXED, __ATOMIC_RELAXED);
}
return __uint_as_float(value);
#endif
#endif
}
__device__
@@ -583,20 +613,30 @@ float atomicMax_system(float* address, float val) {
__device__
inline
double atomicMax(double* address, double val) {
unsigned long long* uaddr { reinterpret_cast<unsigned long long*>(address) };
#if __has_builtin(__hip_atomic_load)
unsigned long long tmp {__hip_atomic_load(uaddr, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT)};
#else
unsigned long long tmp {__atomic_load_n(uaddr, __ATOMIC_RELAXED)};
#endif
double value = __longlong_as_double(tmp);
while (value < val) {
value = atomicCAS(address, value, val);
double atomicMax(double* addr, double val) {
#if defined(__AMDGCN_UNSAFE_FP_ATOMICS__)
return unsafeAtomicMax(addr, val);
#else
#if __has_builtin(__hip_atomic_load) && \
__has_builtin(__hip_atomic_compare_exchange_strong)
double value = __hip_atomic_load(addr, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT);
bool done = false;
while (!done && value < val) {
done = __hip_atomic_compare_exchange_strong(addr, &value, val,
__ATOMIC_RELAXED, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT);
}
return value;
#else
unsigned long long *uaddr = (unsigned long long *)addr;
unsigned long long value = __atomic_load_n(uaddr, __ATOMIC_RELAXED);
bool done = false;
while (!done && __longlong_as_double(value) < val) {
done = __atomic_compare_exchange_n(uaddr, &value, __double_as_longlong(val), false,
__ATOMIC_RELAXED, __ATOMIC_RELAXED);
}
return __longlong_as_double(value);
#endif
#endif
}
__device__
@@ -50,7 +50,10 @@ THE SOFTWARE.
* @return Original value contained in \p addr.
*/
__device__ inline float unsafeAtomicAdd(float* addr, float value) {
#if defined(__gfx90a__) && \
#if defined(__gfx940__) && \
__has_builtin(__builtin_amdgcn_flat_atomic_fadd_f32)
return __builtin_amdgcn_flat_atomic_fadd_f32(addr, value);
#elif defined(__gfx90a__) && \
__has_builtin(__builtin_amdgcn_is_shared) && \
__has_builtin(__builtin_amdgcn_is_private) && \
__has_builtin(__builtin_amdgcn_ds_atomic_fadd_f32) && \
@@ -73,6 +76,78 @@ __device__ inline float unsafeAtomicAdd(float* addr, float value) {
#endif
}
/**
* @brief Unsafe floating point rmw atomic max.
*
* Performs a relaxed read-modify-write floating point atomic max with
* device memory scope. The original value at \p addr is returned and
* the value at \p addr is replaced by \p val if greater.
*
* @note This operation is currently identical to that performed by
* atomicMax and is included for completeness.
*
* @param [in,out] addr Pointer to value to be updated
* @param [in] val Value used to update the value at \p addr.
* @return Original value contained in \p addr.
*/
__device__ inline float unsafeAtomicMax(float* addr, float val) {
#if __has_builtin(__hip_atomic_load) && \
__has_builtin(__hip_atomic_compare_exchange_strong)
float value = __hip_atomic_load(addr, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT);
bool done = false;
while (!done && value < val) {
done = __hip_atomic_compare_exchange_strong(addr, &value, val,
__ATOMIC_RELAXED, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT);
}
return value;
#else
unsigned int *uaddr = (unsigned int *)addr;
unsigned int value = __atomic_load_n(uaddr, __ATOMIC_RELAXED);
bool done = false;
while (!done && __uint_as_float(value) < val) {
done = __atomic_compare_exchange_n(uaddr, &value, __float_as_uint(val), false,
__ATOMIC_RELAXED, __ATOMIC_RELAXED);
}
return __uint_as_float(value);
#endif
}
/**
* @brief Unsafe floating point rmw atomic min.
*
* Performs a relaxed read-modify-write floating point atomic min with
* device memory scope. The original value at \p addr is returned and
* the value at \p addr is replaced by \p val if lesser.
*
* @note This operation is currently identical to that performed by
* atomicMin and is included for completeness.
*
* @param [in,out] addr Pointer to value to be updated
* @param [in] val Value used to update the value at \p addr.
* @return Original value contained in \p addr.
*/
__device__ inline float unsafeAtomicMin(float* addr, float val) {
#if __has_builtin(__hip_atomic_load) && \
__has_builtin(__hip_atomic_compare_exchange_strong)
float value = __hip_atomic_load(addr, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT);
bool done = false;
while (!done && value > val) {
done = __hip_atomic_compare_exchange_strong(addr, &value, val,
__ATOMIC_RELAXED, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT);
}
return value;
#else
unsigned int *uaddr = (unsigned int *)addr;
unsigned int value = __atomic_load_n(uaddr, __ATOMIC_RELAXED);
bool done = false;
while (!done && __uint_as_float(value) > val) {
done = __atomic_compare_exchange_n(uaddr, &value, __float_as_uint(val), false,
__ATOMIC_RELAXED, __ATOMIC_RELAXED);
}
return __uint_as_float(value);
#endif
}
/**
* @brief Unsafe double precision rmw atomic add.
*
@@ -95,12 +170,12 @@ __device__ inline float unsafeAtomicAdd(float* addr, float value) {
* Passing in global segment addresses in fine grain allocations will result in
* undefined behavior and are not supported.
*
* @param [in,out] addr Pointer to value to be increment by \p value.
* @param [in,out] addr Pointer to value to be updated.
* @param [in] value Value by \p addr is to be incremented.
* @return Original value contained in \p addr.
*/
__device__ inline double unsafeAtomicAdd(double* addr, double value) {
#if defined(__gfx90a__) && \
#if (defined(__gfx90a__) || defined(__gfx940_)) && \
__has_builtin(__builtin_amdgcn_flat_atomic_fadd_f64)
return __builtin_amdgcn_flat_atomic_fadd_f64(addr, value);
#elif defined (__hip_atomic_fetch_add)
@@ -110,6 +185,112 @@ __device__ inline double unsafeAtomicAdd(double* addr, double value) {
#endif
}
/**
* @brief Unsafe double precision rmw atomic max.
*
* Performs a relaxed read-modify-write double precision atomic max with
* device memory scope. Original value at \p addr is returned and
* the value of \p addr is updated with \p val if greater.
*
* @note This operation currently only performs different operations for
* the gfx90a target. Other devices continue to use safe atomics.
*
* It can be used to generate code that uses fast hardware floating point atomic
* operations which may handle rounding and subnormal values differently than
* non-atomic floating point operations.
*
* The operation is not always safe and can have undefined behavior unless
* following condition are met:
*
* - \p addr is at least 8 byte aligned
* - If \p addr is a global segment address, it is in a coarse grain allocation.
* Passing in global segment addresses in fine grain allocations will result in
* undefined behavior and are not supported.
*
* @param [in,out] addr Pointer to value to be updated.
* @param [in] val Value used to updated the contents at \p addr
* @return Original value contained at \p addr.
*/
__device__ inline double unsafeAtomicMax(double* addr, double val) {
#if (defined(__gfx90a__) || defined(__gfx940__)) && \
__has_builtin(__builtin_amdgcn_flat_atomic_fmax_f64)
return __builtin_amdgcn_flat_atomic_fmax_f64(addr, val);
#else
#if __has_builtin(__hip_atomic_load) && \
__has_builtin(__hip_atomic_compare_exchange_strong)
double value = __hip_atomic_load(addr, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT);
bool done = false;
while (!done && value < val) {
done = __hip_atomic_compare_exchange_strong(addr, &value, val,
__ATOMIC_RELAXED, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT);
}
return value;
#else
unsigned long long *uaddr = (unsigned long long *)addr;
unsigned long long value = __atomic_load_n(uaddr, __ATOMIC_RELAXED);
bool done = false;
while (!done && __longlong_as_double(value) < val) {
done = __atomic_compare_exchange_n(uaddr, &value, __double_as_longlong(val), false,
__ATOMIC_RELAXED, __ATOMIC_RELAXED);
}
return __longlong_as_double(value);
#endif
#endif
}
/**
* @brief Unsafe double precision rmw atomic min.
*
* Performs a relaxed read-modify-write double precision atomic min with
* device memory scope. Original value at \p addr is returned and
* the value of \p addr is updated with \p val if lesser.
*
* @note This operation currently only performs different operations for
* the gfx90a target. Other devices continue to use safe atomics.
*
* It can be used to generate code that uses fast hardware floating point atomic
* operations which may handle rounding and subnormal values differently than
* non-atomic floating point operations.
*
* The operation is not always safe and can have undefined behavior unless
* following condition are met:
*
* - \p addr is at least 8 byte aligned
* - If \p addr is a global segment address, it is in a coarse grain allocation.
* Passing in global segment addresses in fine grain allocations will result in
* undefined behavior and are not supported.
*
* @param [in,out] addr Pointer to value to be updated.
* @param [in] val Value used to updated the contents at \p addr
* @return Original value contained at \p addr.
*/
__device__ inline double unsafeAtomicMin(double* addr, double val) {
#if (defined(__gfx90a__) || defined(__gfx940__)) && \
__has_builtin(__builtin_amdgcn_flat_atomic_fmin_f64)
return __builtin_amdgcn_flat_atomic_fmin_f64(addr, val);
#else
#if __has_builtin(__hip_atomic_load) && \
__has_builtin(__hip_atomic_compare_exchange_strong)
double value = __hip_atomic_load(addr, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT);
bool done = false;
while (!done && value > val) {
done = __hip_atomic_compare_exchange_strong(addr, &value, val,
__ATOMIC_RELAXED, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT);
}
return value;
#else
unsigned long long *uaddr = (unsigned long long *)addr;
unsigned long long value = __atomic_load_n(uaddr, __ATOMIC_RELAXED);
bool done = false;
while (!done && __longlong_as_double(value) > val) {
done = __atomic_compare_exchange_n(uaddr, &value, __double_as_longlong(val), false,
__ATOMIC_RELAXED, __ATOMIC_RELAXED);
}
return __longlong_as_double(value);
#endif
#endif
}
/**
* @brief Safe floating point rmw atomic add.
*
@@ -163,6 +344,78 @@ __device__ inline float safeAtomicAdd(float* addr, float value) {
#endif
}
/**
* @brief Safe floating point rmw atomic max.
*
* Performs a relaxed read-modify-write floating point atomic max with
* device memory scope. The original value at \p addr is returned and
* the value at \p addr is replaced by \p val if greater.
*
* @note This operation ensures that, on all targets, we produce safe atomics.
* This will be the case even when -munsafe-fp-atomics is passed into the compiler.
*
* @param [in,out] addr Pointer to value to be updated
* @param [in] val Value used to update the value at \p addr.
* @return Original value contained in \p addr.
*/
__device__ inline float safeAtomicMax(float* addr, float val) {
#if __has_builtin(__hip_atomic_load) && \
__has_builtin(__hip_atomic_compare_exchange_strong)
float value = __hip_atomic_load(addr, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT);
bool done = false;
while (!done && value < val) {
done = __hip_atomic_compare_exchange_strong(addr, &value, val,
__ATOMIC_RELAXED, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT);
}
return value;
#else
unsigned int *uaddr = (unsigned int *)addr;
unsigned int value = __atomic_load_n(uaddr, __ATOMIC_RELAXED);
bool done = false;
while (!done && __uint_as_float(value) < val) {
done = __atomic_compare_exchange_n(uaddr, &value, __float_as_uint(val), false,
__ATOMIC_RELAXED, __ATOMIC_RELAXED);
}
return __uint_as_float(value);
#endif
}
/**
* @brief Safe floating point rmw atomic min.
*
* Performs a relaxed read-modify-write floating point atomic min with
* device memory scope. The original value at \p addr is returned and
* the value at \p addr is replaced by \p val if lesser.
*
* @note This operation ensures that, on all targets, we produce safe atomics.
* This will be the case even when -munsafe-fp-atomics is passed into the compiler.
*
* @param [in,out] addr Pointer to value to be updated
* @param [in] val Value used to update the value at \p addr.
* @return Original value contained in \p addr.
*/
__device__ inline float safeAtomicMin(float* addr, float val) {
#if __has_builtin(__hip_atomic_load) && \
__has_builtin(__hip_atomic_compare_exchange_strong)
float value = __hip_atomic_load(addr, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT);
bool done = false;
while (!done && value > val) {
done = __hip_atomic_compare_exchange_strong(addr, &value, val,
__ATOMIC_RELAXED, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT);
}
return value;
#else
unsigned int *uaddr = (unsigned int *)addr;
unsigned int value = __atomic_load_n(uaddr, __ATOMIC_RELAXED);
bool done = false;
while (!done && __uint_as_float(value) > val) {
done = __atomic_compare_exchange_n(uaddr, &value, __float_as_uint(val), false,
__ATOMIC_RELAXED, __ATOMIC_RELAXED);
}
return __uint_as_float(value);
#endif
}
/**
* @brief Safe double precision rmw atomic add.
*
@@ -178,7 +431,7 @@ __device__ inline float safeAtomicAdd(float* addr, float value) {
* @return Original value contained in \p addr.
*/
__device__ inline double safeAtomicAdd(double* addr, double value) {
#if defined(__gfx90a__) && \
#if (defined(__gfx90a__) || defined(__gfx940__)) && \
__has_builtin(__hip_atomic_fetch_add)
// On gfx90a, with the __hip_atomic_fetch_add builtin, relaxed system-scope
// atomics will produce safe CAS loops, but are otherwise not different than
@@ -215,4 +468,99 @@ __device__ inline double safeAtomicAdd(double* addr, double value) {
#endif // __has_builtin(__hip_atomic_fetch_add)
#endif
}
/**
* @brief Safe double precision rmw atomic max.
*
* Performs a relaxed read-modify-write double precision atomic max with
* device memory scope. Original value at \p addr is returned and
* the value of \p addr is updated with \p val if greater.
*
* @note This operation ensures that, on all targets, we produce safe atomics.
* This will be the case even when -munsafe-fp-atomics is passed into the compiler.
*
* @param [in,out] addr Pointer to value to be updated.
* @param [in] val Value used to updated the contents at \p addr
* @return Original value contained at \p addr.
*/
__device__ inline double safeAtomicMax(double* addr, double val) {
#if __has_builtin(__builtin_amdgcn_is_private)
if (__builtin_amdgcn_is_private(
(const __attribute__((address_space(0))) void*)addr)) {
double old = *addr;
*addr = __builtin_fmax(old, val);
return old;
} else {
#endif
#if __has_builtin(__hip_atomic_load) && \
__has_builtin(__hip_atomic_compare_exchange_strong)
double value = __hip_atomic_load(addr, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT);
bool done = false;
while (!done && value < val) {
done = __hip_atomic_compare_exchange_strong(addr, &value, val,
__ATOMIC_RELAXED, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT);
}
return value;
#else
unsigned long long *uaddr = (unsigned long long *)addr;
unsigned long long value = __atomic_load_n(uaddr, __ATOMIC_RELAXED);
bool done = false;
while (!done && __longlong_as_double(value) < val) {
done = __atomic_compare_exchange_n(uaddr, &value, __double_as_longlong(val), false,
__ATOMIC_RELAXED, __ATOMIC_RELAXED);
}
return __longlong_as_double(value);
#endif
#if __has_builtin(__builtin_amdgcn_is_private)
}
#endif
}
/**
* @brief Safe double precision rmw atomic min.
*
* Performs a relaxed read-modify-write double precision atomic min with
* device memory scope. Original value at \p addr is returned and
* the value of \p addr is updated with \p val if lesser.
*
* @note This operation ensures that, on all targets, we produce safe atomics.
* This will be the case even when -munsafe-fp-atomics is passed into the compiler.
*
* @param [in,out] addr Pointer to value to be updated.
* @param [in] val Value used to updated the contents at \p addr
* @return Original value contained at \p addr.
*/
__device__ inline double safeAtomicMin(double* addr, double val) {
#if __has_builtin(__builtin_amdgcn_is_private)
if (__builtin_amdgcn_is_private(
(const __attribute__((address_space(0))) void*)addr)) {
double old = *addr;
*addr = __builtin_fmin(old, val);
return old;
} else {
#endif
#if __has_builtin(__hip_atomic_load) && \
__has_builtin(__hip_atomic_compare_exchange_strong)
double value = __hip_atomic_load(addr, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT);
bool done = false;
while (!done && value > val) {
done = __hip_atomic_compare_exchange_strong(addr, &value, val,
__ATOMIC_RELAXED, __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT);
}
return value;
#else
unsigned long long *uaddr = (unsigned long long *)addr;
unsigned long long value = __atomic_load_n(uaddr, __ATOMIC_RELAXED);
bool done = false;
while (!done && __longlong_as_double(value) > val) {
done = __atomic_compare_exchange_n(uaddr, &value, __double_as_longlong(val), false,
__ATOMIC_RELAXED, __ATOMIC_RELAXED);
}
return __longlong_as_double(value);
#endif
#if __has_builtin(__builtin_amdgcn_is_private)
}
#endif
}
#endif
@@ -0,0 +1,75 @@
/*
Copyright (c) 2022 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 HIP_INCLUDE_HIP_NVIDIA_DETAIL_HIP_ATOMICS_H
#define HIP_INCLUDE_HIP_NVIDIA_DETAIL_HIP_ATOMICS_H
__device__ inline float atomicMax(float* addr, float val) {
unsigned int *uaddr = (unsigned int *)addr;
float value = __uint_as_float(*uaddr);
while (value < val) {
value = __uint_as_float(atomicCAS(uaddr, __float_as_uint(value),
__float_as_uint(val)));
}
return value;
}
__device__ inline double atomicMax(double* addr, double val) {
unsigned long long* uaddr = (unsigned long long *)addr;
double value = __longlong_as_double(*uaddr);
while (value < val) {
value = __longlong_as_double(atomicCAS(uaddr,
__double_as_longlong(value),
__double_as_longlong(val)));
}
return value;
}
__device__ inline float atomicMin(float* addr, float val) {
unsigned int *uaddr = (unsigned int *)addr;
float value = __uint_as_float(*uaddr);
while (value > val) {
value = __uint_as_float(atomicCAS(uaddr, __float_as_uint(value),
__float_as_uint(val)));
}
return value;
}
__device__ inline double atomicMin(double* addr, double val) {
unsigned long long* uaddr = (unsigned long long *)addr;
double value = __longlong_as_double(*uaddr);
while (value > val) {
value = __longlong_as_double(atomicCAS(uaddr,
__double_as_longlong(value),
__double_as_longlong(val)));
}
return value;
}
#endif
@@ -76,6 +76,7 @@ typedef int hipLaunchParm;
#ifdef __CUDACC__
#include "nvidia_hip_atomics.h"
#include "nvidia_hip_unsafe_atomics.h"
#define hipThreadIdx_x threadIdx.x
@@ -44,6 +44,22 @@ __device__ inline double unsafeAtomicAdd(double* addr, double value) {
#endif
}
__device__ inline float unsafeAtomicMax(float* addr, float value) {
return atomicMax(addr, value);
}
__device__ inline double unsafeAtomicMax(double* addr, double val) {
return atomicMax(addr, val);
}
__device__ inline float unsafeAtomicMin(float* addr, float value) {
return atomicMin(addr, value);
}
__device__ inline double unsafeAtomicMin(double* addr, double val) {
return atomicMin(addr, val);
}
__device__ inline float safeAtomicAdd(float* addr, float value) {
return atomicAdd(addr, value);
}
@@ -65,4 +81,20 @@ __device__ inline double safeAtomicAdd(double* addr, double value) {
#endif
}
__device__ inline float safeAtomicMax(float* addr, float value) {
return atomicMax(addr, value);
}
__device__ inline double safeAtomicMax(double* addr, double val) {
return atomicMax(addr, val);
}
__device__ inline float safeAtomicMin(float* addr, float value) {
return atomicMin(addr, value);
}
__device__ inline double safeAtomicMin(double* addr, double val) {
return atomicMin(addr, val);
}
#endif