Skip to main content

cubecl_core/frontend/container/
shared_memory.rs

1use core::ops::{Deref, DerefMut};
2
3use crate::{
4    self as cubecl,
5    frontend::container::slice,
6    prelude::{Vectorized, VectorizedExpand},
7    unexpanded,
8};
9use cubecl_ir::{
10    AggregateKind, BoundsCheckMetadata, Marker, MetadataKind, SliceMetadata, VectorSize,
11};
12use cubecl_macros::{cube, intrinsic};
13
14use crate::{
15    frontend::{CubePrimitive, CubeType, IntoMut, NativeExpand},
16    ir::Scope,
17    prelude::*,
18};
19
20pub type SharedExpand<T> = NativeExpand<Shared<T>>;
21
22pub struct Shared<E: NativeCubeType + ?Sized> {
23    _val: *mut E,
24}
25
26// Treat it as a shared smart pointer
27impl<E: NativeCubeType + ?Sized> Clone for Shared<E> {
28    fn clone(&self) -> Self {
29        Self { _val: self._val }
30    }
31}
32
33impl<T: NativeCubeType + ?Sized> IntoMut for NativeExpand<Shared<T>> {
34    fn into_mut(self, _scope: &Scope) -> Self {
35        self
36    }
37}
38
39impl<T: NativeCubeType + ?Sized> CubeType for Shared<T> {
40    type ExpandType = NativeExpand<Shared<T>>;
41}
42
43impl<T: NativeCubeType + ?Sized> AsMutExpand for SharedExpand<T> {
44    fn __expand_ref_mut_method(&mut self, _: &Scope) -> &mut Self {
45        self
46    }
47}
48
49#[cube]
50impl<T: CubePrimitive> Shared<[T]> {
51    /// Create a new shared slice.
52    ///
53    /// # Safety
54    /// Shared memory is always uninitialized by default. Reading uninitialized shared values is
55    /// undefined behavior.
56    pub fn new_slice(#[comptime] len: usize) -> Self {
57        intrinsic!(|scope| {
58            let ty = Type::array(T::__expand_as_type(scope), len);
59            let buffer = scope.create_shared(ty, None);
60            let slice = slice::from_raw_parts::<T>(
61                scope,
62                buffer,
63                0usize.into_expand(scope),
64                len.into_expand(scope),
65            );
66            slice.expand.into()
67        })
68    }
69
70    /// Create a new shared slice with a specified minimum alignment.
71    ///
72    /// # Safety
73    /// Shared memory is always uninitialized by default. Reading uninitialized shared values is
74    /// undefined behavior.
75    #[allow(unused_variables)]
76    pub fn new_aligned_slice(#[comptime] len: usize, #[comptime] alignment: usize) -> Self {
77        intrinsic!(|scope| {
78            let ty = Type::array(T::__expand_as_type(scope), len);
79            let buffer = scope.create_shared(ty, Some(alignment));
80            let slice = slice::from_raw_parts::<T>(
81                scope,
82                buffer,
83                0usize.into_expand(scope),
84                len.into_expand(scope),
85            );
86            slice.expand.into()
87        })
88    }
89
90    #[allow(clippy::len_without_is_empty)]
91    pub fn len(&self) -> usize {
92        intrinsic!(|_| len_static(&self))
93    }
94}
95
96impl<T: NativeCubeType + ?Sized> Shared<T> {
97    pub fn map<U: NativeCubeType + ?Sized>(self, _map: impl FnOnce(&T) -> &U) -> Shared<U> {
98        unexpanded!()
99    }
100}
101
102impl<T: NativeCubeType + ?Sized> SharedExpand<T> {
103    pub fn __expand_map_method<U: NativeCubeType + ?Sized>(
104        self,
105        scope: &Scope,
106        map: impl for<'a> FnOnce(&Scope, &'a NativeExpand<T>) -> &'a NativeExpand<U>,
107    ) -> SharedExpand<U> {
108        let out = map(scope, self.__expand_ref_method(scope));
109        out.expand.into()
110    }
111}
112
113#[cube]
114impl<T: CubePrimitive> Shared<T> {
115    /// Create a new shared object.
116    ///
117    /// # Safety
118    /// Shared memory is always uninitialized by default. Reading uninitialized shared values is
119    /// undefined behavior.
120    pub fn new() -> Self {
121        intrinsic!(|scope| {
122            let val = scope.create_shared(T::__expand_as_type(scope), None);
123            NativeExpand::new(val)
124        })
125    }
126}
127
128#[cube]
129impl<T: NativeCubeType + ?Sized> Shared<T> {
130    pub fn inner_ref(&self) -> &T {
131        intrinsic!(|scope| { unsafe { self.as_type_ref_unchecked() } })
132    }
133
134    pub fn inner_mut(&mut self) -> &mut T {
135        intrinsic!(|scope| { unsafe { self.as_type_mut_unchecked() } })
136    }
137}
138
139impl<T: CubePrimitive> Default for Shared<T> {
140    fn default() -> Self {
141        Self::new()
142    }
143}
144impl<T: CubePrimitive> Shared<T> {
145    pub fn __expand_default(scope: &Scope) -> <Self as CubeType>::ExpandType {
146        Self::__expand_new(scope)
147    }
148}
149
150#[cube]
151impl<T: NativeCubeType + ?Sized> Shared<T> {
152    /// Frees the shared memory for reuse, if possible on the target runtime.
153    ///
154    /// # Safety
155    /// *Must* be used in uniform control flow
156    /// *Must not* have any dangling references to this shared memory
157    pub unsafe fn free(&self) {
158        intrinsic!(|scope| {
159            let val = match self.expand.ty {
160                Type::Aggregate(aggregate_kind) => match aggregate_kind {
161                    AggregateKind::Ptr {
162                        meta: MetadataKind::Slice,
163                        ..
164                    } => scope.extract_field(self.expand, self.expand.ty, SliceMetadata::LIST),
165                    AggregateKind::Ptr {
166                        meta: MetadataKind::BoundsCheck,
167                        ..
168                    } => scope.extract_field(
169                        self.expand,
170                        self.expand.ty,
171                        BoundsCheckMetadata::POINTER,
172                    ),
173                },
174                _ => self.expand,
175            };
176            scope.register(Marker::Free(val))
177        })
178    }
179}
180
181fn len_static<T: CubePrimitive>(shared: &NativeExpand<Shared<[T]>>) -> NativeExpand<usize> {
182    let Type::Array(_, length) = shared.expand.ty else {
183        unreachable!("Kind of shared memory is always shared memory")
184    };
185    length.into()
186}
187
188impl<T: CubePrimitive> List<T> for Shared<[T]> {}
189impl<T: CubePrimitive> ListExpand<T> for NativeExpand<Shared<[T]>> {
190    fn __expand_len_method(&self, scope: &Scope) -> NativeExpand<usize> {
191        Self::__expand_len_method(self, scope)
192    }
193}
194
195impl<T: CubePrimitive> Vectorized for Shared<[T]> {}
196impl<T: CubePrimitive> VectorizedExpand for NativeExpand<Shared<[T]>> {
197    fn vector_size(&self) -> VectorSize {
198        self.expand.ty.vector_size()
199    }
200}
201
202impl<T: NativeCubeType + ?Sized> Deref for Shared<T> {
203    type Target = T;
204
205    fn deref(&self) -> &Self::Target {
206        unexpanded!()
207    }
208}
209impl<T: NativeCubeType + ?Sized> DerefMut for Shared<T> {
210    fn deref_mut(&mut self) -> &mut Self::Target {
211        unexpanded!()
212    }
213}
214
215impl<T: NativeCubeType + ?Sized> Deref for SharedExpand<T> {
216    type Target = NativeExpand<T>;
217
218    fn deref(&self) -> &Self::Target {
219        unsafe { self.as_type_ref_unchecked() }
220    }
221}
222impl<T: NativeCubeType + ?Sized> DerefMut for SharedExpand<T> {
223    fn deref_mut(&mut self) -> &mut Self::Target {
224        unsafe { self.as_type_mut_unchecked() }
225    }
226}
227
228impl<'a, T: NativeCubeType + ?Sized> From<&'a SharedExpand<T>> for &'a NativeExpand<T> {
229    fn from(value: &'a SharedExpand<T>) -> Self {
230        value
231    }
232}
233
234impl<'a, T: NativeCubeType + ?Sized> From<&'a mut SharedExpand<T>> for &'a mut NativeExpand<T> {
235    fn from(value: &'a mut SharedExpand<T>) -> Self {
236        value
237    }
238}
239
240impl<T: NativeCubeType + ?Sized> AsDerefExpand for SharedExpand<T> {
241    type Target = NativeExpand<T>;
242
243    fn __expand_as_deref_method(&self, _: &Scope) -> &Self::Target {
244        unsafe { self.as_type_ref_unchecked::<T>() }
245    }
246}
247impl<T: NativeCubeType + ?Sized> AsDerefMutExpand for SharedExpand<T> {
248    fn __expand_as_deref_mut_method(&mut self, _: &Scope) -> &mut Self::Target {
249        unsafe { self.as_type_mut_unchecked::<T>() }
250    }
251}
252
253impl<T: CubePrimitive> Assign<NativeExpand<T>> for SharedExpand<T> {
254    fn __expand_assign_method(&mut self, scope: &Scope, value: NativeExpand<T>) {
255        let value = read_value(scope, value.expand);
256        assign::expand_element(scope, value, self.expand);
257    }
258}
259
260impl<T: CubePrimitive> RuntimeAssign<NativeExpand<T>> for SharedExpand<T> {
261    fn init_mut(&self, scope: &Scope) -> Self::Expand {
262        Shared::<T>::__expand_new(scope)
263    }
264}