Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 22 additions & 5 deletions src/tl_templates/cuda/atomic.h
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,12 @@ TL_DEVICE void AtomicAddx4Scalar(T *ref, T x, T y, T z, T w) {
atomicAdd(ref + 3, w);
}

template <typename T> TL_DEVICE void CheckAtomicAddDestinationType() {
static_assert(
!std::is_same_v<T, short> && !std::is_same_v<T, unsigned short>,
"CUDA atomic_add does not support int16 or uint16 destinations");
}

TL_DEVICE float2 AtomicAddx2ScalarRet(float *ref, float2 add_val) {
float2 ret;
ret.x = atomicAdd(ref + 0, add_val.x);
Expand Down Expand Up @@ -472,8 +478,11 @@ template <typename T1, typename T2>
TL_DEVICE void AtomicAdd(T1 *address, T2 val,
int memory_order = int(cuda::memory_order_relaxed)) {
using NT1 = typename normalize_atomic_type<T1>::type;
if constexpr (std::is_same_v<NT1, half> ||
std::is_same_v<NT1, __nv_bfloat16>) {
if constexpr (std::is_same_v<NT1, short> ||
std::is_same_v<NT1, unsigned short>) {
tl_atomic_detail::CheckAtomicAddDestinationType<NT1>();
} else if constexpr (std::is_same_v<NT1, half> ||
std::is_same_v<NT1, __nv_bfloat16>) {
if (tl_atomic_detail::IsRelaxedMemoryOrder(memory_order)) {
atomicAdd(reinterpret_cast<NT1 *>(address), static_cast<NT1>(val));
} else {
Expand Down Expand Up @@ -508,16 +517,24 @@ template <typename T1, typename T2>
TL_DEVICE void AtomicAdd(T1 *address, T2 val,
int memory_order = int(cuda::memory_order_relaxed)) {
using NT1 = typename normalize_atomic_type<T1>::type;
(void)memory_order;
atomicAdd(reinterpret_cast<NT1 *>(address), cuda_cast<NT1>(val));
if constexpr (std::is_same_v<NT1, short> ||
std::is_same_v<NT1, unsigned short>) {
tl_atomic_detail::CheckAtomicAddDestinationType<NT1>();
} else {
(void)memory_order;
atomicAdd(reinterpret_cast<NT1 *>(address), cuda_cast<NT1>(val));
}
}
#endif

template <typename T1, typename T2>
TL_DEVICE T1 AtomicAddRet(T1 *address, T2 val,
int memory_order = int(cuda::memory_order_relaxed)) {
using NT1 = typename normalize_atomic_type<T1>::type;
if constexpr (std::is_same_v<NT1, bfloat16_t>) {
if constexpr (std::is_same_v<NT1, short> ||
std::is_same_v<NT1, unsigned short>) {
tl_atomic_detail::CheckAtomicAddDestinationType<NT1>();
} else if constexpr (std::is_same_v<NT1, bfloat16_t>) {
// Pre-SM80 only: cuda::atomic_ref has no fetch_add for bfloat16_t, so use
// the atomicAdd overload above. Memory order is dropped, as in AtomicAdd.
(void)memory_order;
Expand Down
24 changes: 24 additions & 0 deletions testing/python/language/test_tilelang_language_atomic.py
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,30 @@ def test_atomic_add():
run_atomic_add(8, 128, 128, 32, 32)


@tilelang.testing.requires_cuda
@pytest.mark.parametrize("dtype", [T.int16, T.uint16])
def test_atomic_add_rejects_16bit_integer_destination(dtype):
@T.prim_func
def atomic_add_16bit_integer(src: T.Tensor((1,), dtype), dst: T.Tensor((1,), dtype)):
with T.Kernel(1, threads=1):
T.atomic_add(dst[0], src[0])

with pytest.raises(RuntimeError, match="CUDA atomic_add does not support int16 or uint16 destinations"):
tilelang.compile(atomic_add_16bit_integer,>


@tilelang.testing.requires_cuda
@pytest.mark.parametrize("dtype", [T.int16, T.uint16])
def test_atomic_add_return_prev_rejects_16bit_integer_destination(dtype):
@T.prim_func
def atomic_add_16bit_integer(src: T.Tensor((1,), dtype), dst: T.Tensor((1,), dtype), prev: T.Tensor((1,), dtype)):
with T.Kernel(1, threads=1):
prev[0] = T.atomic_add(dst[0], src[0], return_prev=True)

with pytest.raises(RuntimeError, match="CUDA atomic_add does not support int16 or uint16 destinations"):
tilelang.compile(atomic_add_16bit_integer,>


@tilelang.testing.requires_cuda
@tilelang.testing.requires_cuda_compute_version_ge(9, 0)
def test_atomic_add_auto_vectorized():
Expand Down
Loading