use cubecl_ir::{AtomicBinaryOperands, AtomicOp, ConstantValue, StoreOperands, Value};
use cubecl_macros::intrinsic;
use super::{NativeAssign, NativeExpand, Numeric};
use crate::{
self as cubecl,
frontend::{CubePrimitive, CubeType},
ir::{CompareAndSwapOperands, Instruction, Scope, Type},
prelude::*,
};
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub struct Atomic<Inner: CubePrimitive> {
pub val: Inner,
}
type AtomicExpand<Inner> = NativeExpand<Atomic<Inner>>;
#[cube]
impl<Inner: CubePrimitive<Scalar: Numeric>> Atomic<Inner> {
pub fn load(&self) -> Inner {
intrinsic!(|scope| {
let pointer: Value = self.clone().into();
let new_val = scope.create_value(Inner::__expand_as_type(scope));
scope.register(Instruction::new(AtomicOp::Load(pointer), new_val));
new_val.into()
})
}
pub fn store(&self, value: Inner) {
intrinsic!(|scope| {
let ptr: Value = self.clone().into();
let value: Value = value.into();
scope.register(Instruction::no_out(AtomicOp::Store(StoreOperands {
ptr,
value,
})));
})
}
pub fn swap(&self, value: Inner) -> Inner {
intrinsic!(|scope| {
let ptr: Value = self.clone().into();
let value: Value = value.into();
let new_val = scope.create_value(Inner::__expand_as_type(scope));
scope.register(Instruction::new(
AtomicOp::Swap(AtomicBinaryOperands { ptr, value }),
new_val,
));
new_val.into()
})
}
pub fn fetch_add(&self, value: Inner) -> Inner {
intrinsic!(|scope| {
let ptr: Value = self.clone().into();
let value: Value = value.into();
let new_val = scope.create_value(Inner::__expand_as_type(scope));
scope.register(Instruction::new(
AtomicOp::Add(AtomicBinaryOperands { ptr, value }),
new_val,
));
new_val.into()
})
}
pub fn fetch_sub(&self, value: Inner) -> Inner {
intrinsic!(|scope| {
let ptr: Value = self.clone().into();
let value: Value = value.into();
let new_val = scope.create_value(Inner::__expand_as_type(scope));
scope.register(Instruction::new(
AtomicOp::Sub(AtomicBinaryOperands { ptr, value }),
new_val,
));
new_val.into()
})
}
pub fn fetch_max(&self, value: Inner) -> Inner {
intrinsic!(|scope| {
let ptr: Value = self.clone().into();
let value: Value = value.into();
let new_val = scope.create_value(Inner::__expand_as_type(scope));
scope.register(Instruction::new(
AtomicOp::Max(AtomicBinaryOperands { ptr, value }),
new_val,
));
new_val.into()
})
}
pub fn fetch_min(&self, value: Inner) -> Inner {
intrinsic!(|scope| {
let ptr: Value = self.clone().into();
let value: Value = value.into();
let new_val = scope.create_value(Inner::__expand_as_type(scope));
scope.register(Instruction::new(
AtomicOp::Min(AtomicBinaryOperands { ptr, value }),
new_val,
));
new_val.into()
})
}
}
#[cube]
impl<Inner: CubePrimitive<Scalar: Int>> Atomic<Inner> {
pub fn compare_exchange_weak(&self, cmp: Inner, value: Inner) -> Inner {
intrinsic!(|scope| {
let pointer: Value = self.clone().into();
let cmp: Value = cmp.into();
let value: Value = value.into();
let new_val = scope.create_value(Inner::__expand_as_type(scope));
scope.register(Instruction::new(
AtomicOp::CompareAndSwap(CompareAndSwapOperands {
ptr: pointer,
cmp: cmp,
val: value,
}),
new_val,
));
new_val.into()
})
}
pub fn fetch_and(&self, value: Inner) -> Inner {
intrinsic!(|scope| {
let ptr: Value = self.clone().into();
let value: Value = value.into();
let new_val = scope.create_value(Inner::__expand_as_type(scope));
scope.register(Instruction::new(
AtomicOp::And(AtomicBinaryOperands { ptr, value }),
new_val,
));
new_val.into()
})
}
pub fn fetch_or(&self, value: Inner) -> Inner {
intrinsic!(|scope| {
let ptr: Value = self.clone().into();
let value: Value = value.into();
let new_val = scope.create_value(Inner::__expand_as_type(scope));
scope.register(Instruction::new(
AtomicOp::Or(AtomicBinaryOperands { ptr, value }),
new_val,
));
new_val.into()
})
}
pub fn fetch_xor(&self, value: Inner) -> Inner {
intrinsic!(|scope| {
let ptr: Value = self.clone().into();
let value: Value = value.into();
let new_val = scope.create_value(Inner::__expand_as_type(scope));
scope.register(Instruction::new(
AtomicOp::Xor(AtomicBinaryOperands { ptr, value }),
new_val,
));
new_val.into()
})
}
}
impl<Inner: CubePrimitive> CubeType for Atomic<Inner> {
type ExpandType = NativeExpand<Self>;
}
impl<Inner: CubePrimitive> CubeDebug for Atomic<Inner> {}
impl<Inner: CubePrimitive> CubePrimitive for Atomic<Inner> {
type Scalar = Inner::Scalar;
type Size = Const<1>;
type WithScalar<S: Scalar> = Atomic<S>;
fn as_type_native() -> Option<Type> {
Inner::as_type_native().map(Type::atomic)
}
fn __expand_as_type(scope: &Scope) -> Type {
Type::atomic(Inner::__expand_as_type(scope))
}
fn as_type_native_unchecked() -> Type {
Type::atomic(Inner::as_type_native_unchecked())
}
fn size() -> Option<usize> {
Inner::size()
}
fn from_expand_elem(elem: Value) -> Self::ExpandType {
NativeExpand::new(elem)
}
fn from_const_value(_value: ConstantValue) -> Self {
panic!("Can't have constant atomic");
}
}
impl<Inner: CubePrimitive> NativeAssign for Atomic<Inner> {}