cubecl_core/frontend/element/
atomic.rs1use cubecl_ir::{ConstantValue, ExpandValue, dialect::atomic::*, types::AtomicType};
2use cubecl_macros::intrinsic;
3use half::{bf16, f16};
4use pliron::{
5 builtin::op_interfaces::OneResultInterface, context::Context, op::Op, r#type::TypeHandle,
6 value::Value,
7};
8
9use super::{NativeAssign, NativeExpand};
10use crate::{
11 self as cubecl,
12 frontend::{CubePrimitive, CubeType},
13 ir::Scope,
14 prelude::*,
15};
16
17#[derive(Clone, Copy, Hash, PartialEq, Eq)]
21pub struct Atomic<Inner: CubePrimitive> {
22 pub val: Inner,
23}
24
25type AtomicExpand<Inner> = NativeExpand<Atomic<Inner>>;
26
27pub trait AtomicNumeric {
28 fn __expand_fetch_add(scope: &Scope, ptr: ExpandValue, value: ExpandValue) -> ExpandValue;
29 fn __expand_fetch_sub(scope: &Scope, ptr: ExpandValue, value: ExpandValue) -> ExpandValue;
30 fn __expand_fetch_min(scope: &Scope, ptr: ExpandValue, value: ExpandValue) -> ExpandValue;
31 fn __expand_fetch_max(scope: &Scope, ptr: ExpandValue, value: ExpandValue) -> ExpandValue;
32}
33
34macro_rules! atomic_numeric {
35 ($($ty: ty),*; $add: ty, $sub: ty, $min: ty, $max: ty) => {
36 $(impl AtomicNumeric for $ty {
37 fn __expand_fetch_add(scope: &Scope, ptr: ExpandValue, value: ExpandValue) -> ExpandValue {
38 atomic_binary_expand(scope, ptr, value, <$add>::new)
39 }
40 fn __expand_fetch_sub(scope: &Scope, ptr: ExpandValue, value: ExpandValue) -> ExpandValue {
41 atomic_binary_expand(scope, ptr, value, <$sub>::new)
42 }
43 fn __expand_fetch_min(scope: &Scope, ptr: ExpandValue, value: ExpandValue) -> ExpandValue {
44 atomic_binary_expand(scope, ptr, value, <$min>::new)
45 }
46 fn __expand_fetch_max(scope: &Scope, ptr: ExpandValue, value: ExpandValue) -> ExpandValue {
47 atomic_binary_expand(scope, ptr, value, <$max>::new)
48 }
49 })*
50 };
51}
52
53atomic_numeric!(i8, i16, i32, i64, isize; AtomicIAddOp, AtomicISubOp, AtomicSMinOp, AtomicSMaxOp);
54atomic_numeric!(u8, u16, u32, u64, usize; AtomicIAddOp, AtomicISubOp, AtomicUMinOp, AtomicUMaxOp);
55atomic_numeric!(f16, bf16, f32, flex32, tf32, f64; AtomicFAddOp, AtomicFSubOp, AtomicFMinOp, AtomicFMaxOp);
56
57fn atomic_binary_expand<F, O>(
58 scope: &Scope,
59 ptr: ExpandValue,
60 value: ExpandValue,
61 func: F,
62) -> ExpandValue
63where
64 F: Fn(&mut Context, Value, Value) -> O,
65 O: Op + OneResultInterface,
66{
67 let op = func(scope.ctx_mut(), ptr.value(scope), value.read_value(scope));
68 scope.register_with_result(&op).into()
69}
70
71#[cube]
72impl<Inner: CubePrimitive<Scalar: AtomicNumeric>> Atomic<Inner> {
73 pub fn load(&self) -> Inner {
75 intrinsic!(|scope| {
76 let ptr = self.value(scope);
77 let op = AtomicLoadOp::new(scope.ctx_mut(), ptr);
78 scope.register_with_result(&op).into()
79 })
80 }
81
82 pub fn store(&self, value: Inner) {
84 intrinsic!(|scope| {
85 let ptr = self.value(scope);
86 let value = value.read_value(scope);
87 scope.register(&AtomicStoreOp::new(scope.ctx_mut(), ptr, value));
88 })
89 }
90
91 pub fn exchange(&self, value: Inner) -> Inner {
93 intrinsic!(|scope| {
94 let ptr = self.value(scope);
95 let value = value.read_value(scope);
96 let op = AtomicExchangeOp::new(scope.ctx_mut(), ptr, value);
97 scope.register_with_result(&op).into()
98 })
99 }
100
101 pub fn fetch_add(&self, value: Inner) -> Inner {
103 intrinsic!(
104 |scope| Inner::Scalar::__expand_fetch_add(scope, self.expand, value.expand).into()
105 )
106 }
107
108 pub fn fetch_sub(&self, value: Inner) -> Inner {
110 intrinsic!(
111 |scope| Inner::Scalar::__expand_fetch_sub(scope, self.expand, value.expand).into()
112 )
113 }
114
115 pub fn fetch_max(&self, value: Inner) -> Inner {
118 intrinsic!(
119 |scope| Inner::Scalar::__expand_fetch_max(scope, self.expand, value.expand).into()
120 )
121 }
122
123 pub fn fetch_min(&self, value: Inner) -> Inner {
126 intrinsic!(
127 |scope| Inner::Scalar::__expand_fetch_min(scope, self.expand, value.expand).into()
128 )
129 }
130}
131
132#[cube]
133impl<Inner: CubePrimitive<Scalar: Int>> Atomic<Inner> {
134 pub fn compare_exchange_weak(&self, cmp: Inner, value: Inner) -> Inner {
140 intrinsic!(|scope| {
141 let ptr = self.value(scope);
142 let cmp = cmp.read_value(scope);
143 let value = value.read_value(scope);
144 let op = AtomicCompareExchangeWeakOp::new(scope.ctx_mut(), ptr, cmp, value);
145 scope.register_with_result(&op).into()
146 })
147 }
148
149 pub fn fetch_and(&self, value: Inner) -> Inner {
151 intrinsic!(|scope| {
152 let ptr = self.value(scope);
153 let value = value.read_value(scope);
154 let op = AtomicAndOp::new(scope.ctx_mut(), ptr, value);
155 scope.register_with_result(&op).into()
156 })
157 }
158
159 pub fn fetch_or(&self, value: Inner) -> Inner {
161 intrinsic!(|scope| {
162 let ptr = self.value(scope);
163 let value = value.read_value(scope);
164 let op = AtomicOrOp::new(scope.ctx_mut(), ptr, value);
165 scope.register_with_result(&op).into()
166 })
167 }
168
169 pub fn fetch_xor(&self, value: Inner) -> Inner {
171 intrinsic!(|scope| {
172 let ptr = self.value(scope);
173 let value = value.read_value(scope);
174 let op = AtomicXorOp::new(scope.ctx_mut(), ptr, value);
175 scope.register_with_result(&op).into()
176 })
177 }
178}
179
180impl<Inner: CubePrimitive> CubeType for Atomic<Inner> {
181 type ExpandType = NativeExpand<Self>;
182}
183
184impl<Inner: CubePrimitive> CubeDebug for Atomic<Inner> {}
185impl<Inner: CubePrimitive> CubePrimitive for Atomic<Inner> {
186 type Scalar = Inner::Scalar;
187 type Size = Const<1>;
188 type WithScalar<S: Scalar> = Atomic<S>;
189
190 fn __expand_as_type(scope: &Scope) -> TypeHandle {
191 let inner = Inner::__expand_as_type(scope);
192 AtomicType::get(scope.ctx(), inner).into()
193 }
194
195 fn from_expand_elem(elem: ExpandValue) -> Self::ExpandType {
196 NativeExpand::new(elem)
197 }
198
199 fn from_const_value(_value: ConstantValue) -> Self {
200 panic!("Can't have constant atomic");
201 }
202}
203
204impl<Inner: CubePrimitive> NativeAssign for Atomic<Inner> {}