Skip to main content

cubecl_core/frontend/container/tensor/
launch.rs

1use core::marker::PhantomData;
2
3use cubecl_ir::AddressType;
4use cubecl_runtime::{runtime::Runtime, server::CopyDescriptor};
5use cubecl_zspace::{Shape, Strides};
6
7use crate::{
8    self as cubecl,
9    compute::{KernelBuilder, KernelLauncher},
10    frontend::container::{buffer_len::expand_buffer_length_native, slice},
11    prelude::*,
12};
13
14use super::Tensor;
15
16#[derive(CubeType, CubeLaunch, Clone, Copy)]
17#[expand(derive(Clone, Copy))]
18pub struct TensorMeta {
19    pub len: usize,
20    pub rank: usize,
21}
22
23/// Argument to be used for [tensors](Tensor) passed as arguments to kernels.
24#[derive(Debug)]
25pub enum TensorArg<R: Runtime> {
26    /// The tensor is passed with a tensor handle.
27    Handle {
28        /// The tensor handle.
29        handle: TensorBinding<R>,
30    },
31    /// The tensor is aliasing another input tensor.
32    Alias {
33        /// The position of the input tensor.
34        input_pos: usize,
35        strides: Strides,
36        shape: Shape,
37    },
38}
39
40/// Tensor representation with a reference to the [server handle](cubecl_runtime::server::Handle),
41/// the strides and the shape.
42pub struct TensorBinding<R: Runtime> {
43    pub handle: cubecl_runtime::server::BufferBinding,
44    pub strides: Strides,
45    pub shape: Shape,
46    pub runtime: PhantomData<R>,
47}
48
49impl<R: Runtime> Clone for TensorBinding<R> {
50    fn clone(&self) -> Self {
51        Self {
52            handle: self.handle.clone(),
53            strides: self.strides.clone(),
54            shape: self.shape.clone(),
55            runtime: PhantomData,
56        }
57    }
58}
59
60impl<R: Runtime> TensorBinding<R> {
61    pub fn size(&self) -> usize {
62        self.shape.iter().product()
63    }
64
65    /// Address type required to fully index this tensor handle, assuming scalar access.
66    pub fn required_address_type(&self, elem_size: usize) -> AddressType {
67        AddressType::from_len(self.handle.size() as usize / elem_size)
68    }
69}
70
71impl<R: Runtime> core::fmt::Debug for TensorBinding<R> {
72    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
73        writeln!(
74            f,
75            "TensorHandleRef {{ strides: {:?}, shape: {:?} }}",
76            self.strides, self.shape
77        )
78    }
79}
80
81/// Compilation argument for a [tensor](Tensor).
82#[derive(Clone, PartialEq, Eq, Hash, Debug)]
83pub struct TensorCompilationArg {
84    pub meta: TensorMetaCompilationArg,
85    pub buffer: BufferCompilationArg,
86}
87
88impl<C: CubePrimitive> LaunchArg for Tensor<C> {
89    type RuntimeArg<R: Runtime> = TensorArg<R>;
90    type CompilationArg = TensorCompilationArg;
91
92    fn register<R: Runtime>(
93        arg: Self::RuntimeArg<R>,
94        launcher: &mut KernelLauncher<R>,
95    ) -> Self::CompilationArg {
96        let elem_size = launcher.with_scope(|scope| C::__expand_size(scope));
97        let vector_size = launcher.with_scope(|scope| C::__expand_vector_size(scope));
98        let len = arg.size() / vector_size;
99        let meta_arg = TensorMetaLaunch::new(len, arg.shape().len());
100        let buffer = match &arg {
101            TensorArg::Handle { .. } => BufferCompilationArg { inplace: None },
102            TensorArg::Alias { input_pos, .. } => BufferCompilationArg {
103                inplace: Some(*input_pos),
104            },
105        };
106        launcher.register_tensor(arg, elem_size);
107        let meta = TensorMeta::register(meta_arg, launcher);
108        TensorCompilationArg { meta, buffer }
109    }
110
111    fn expand(arg: &Self::CompilationArg, builder: &mut KernelBuilder) -> TensorExpand<C> {
112        let buffer = match arg.buffer.inplace {
113            Some(id) => builder.inplace(id),
114            None => builder.tensor(C::__expand_as_type(&builder.scope)),
115        };
116        let meta = TensorMeta::expand(&arg.meta, builder);
117        let scope = &builder.scope;
118        let len = expand_buffer_length_native(scope, buffer);
119        let buffer =
120            slice::from_raw_parts::<C>(scope, buffer, 0usize.into_expand(scope), len.into());
121        TensorExpand { meta, buffer }
122    }
123}
124
125impl<C: CubePrimitive> LaunchArg for OwnedTensor<C> {
126    type RuntimeArg<R: Runtime> = TensorArg<R>;
127    type CompilationArg = TensorCompilationArg;
128
129    fn register<R: Runtime>(
130        arg: Self::RuntimeArg<R>,
131        launcher: &mut KernelLauncher<R>,
132    ) -> Self::CompilationArg {
133        Tensor::<C>::register(arg, launcher)
134    }
135
136    fn expand(arg: &Self::CompilationArg, builder: &mut KernelBuilder) -> OwnedTensorExpand<C> {
137        let tensor = Tensor::<C>::expand(arg, builder);
138        OwnedTensorExpand {
139            meta: tensor.meta,
140            buffer: tensor.buffer.expand.into(),
141        }
142    }
143}
144
145impl<R: Runtime> TensorArg<R> {
146    /// Create a new tensor argument specified with its vectorization factor.
147    ///
148    /// # Safety
149    ///
150    /// If you provide wrong strides or shapes, it might create undefined behavior caused by
151    /// out-of-bound reads and writes.
152    pub unsafe fn from_raw_parts(
153        handle: cubecl_runtime::server::Handle,
154        strides: Strides,
155        shape: Shape,
156    ) -> Self {
157        unsafe { Self::from_raw_parts_binding(handle.binding(), strides, shape) }
158    }
159
160    pub(crate) unsafe fn from_raw_parts_binding(
161        handle: cubecl_runtime::server::BufferBinding,
162        strides: Strides,
163        shape: Shape,
164    ) -> Self {
165        unsafe {
166            Self::Handle {
167                handle: TensorBinding::from_raw_parts_binding(handle, strides, shape),
168            }
169        }
170    }
171
172    /// Create an alias argument.
173    pub fn into_alias(self, position: usize) -> Self {
174        match self {
175            TensorArg::Handle { handle } => handle.into_alias(position),
176            alias @ TensorArg::Alias { .. } => alias,
177        }
178    }
179
180    pub fn size(&self) -> usize {
181        match self {
182            TensorArg::Handle { handle } => handle.size(),
183            TensorArg::Alias { shape, .. } => shape.iter().product(),
184        }
185    }
186
187    pub fn shape(&self) -> &[usize] {
188        match self {
189            TensorArg::Handle { handle } => &handle.shape,
190            TensorArg::Alias { shape, .. } => shape,
191        }
192    }
193
194    pub fn strides(&self) -> &[usize] {
195        match self {
196            TensorArg::Handle { handle } => &handle.strides,
197            TensorArg::Alias { strides, .. } => strides,
198        }
199    }
200}
201
202impl<R: Runtime> TensorArg<R> {
203    pub fn into_buffer_arg(self) -> BufferArg<R> {
204        match self {
205            TensorArg::Handle { handle } => {
206                let handle = unsafe {
207                    let size = handle.size();
208                    BufferBinding::from_raw_parts_binding(handle.handle, size)
209                };
210                BufferArg::Handle { handle }
211            }
212            TensorArg::Alias {
213                input_pos, shape, ..
214            } => BufferArg::Alias {
215                input_pos,
216                length: [shape.iter().product()],
217            },
218        }
219    }
220}
221
222impl<R: Runtime> TensorBinding<R> {
223    /// Convert the handle into a [tensor argument](TensorArg).
224    pub fn into_tensor_arg(self) -> TensorArg<R> {
225        TensorArg::Handle { handle: self }
226    }
227    /// Convert the handle into a [tensor argument](TensorArg).
228    pub fn into_alias(self, index: usize) -> TensorArg<R> {
229        TensorArg::Alias {
230            input_pos: index,
231            strides: self.strides,
232            shape: self.shape,
233        }
234    }
235    /// Convert the handle into a [tensor argument](TensorArg).
236    pub fn as_alias(&self, index: usize) -> TensorArg<R> {
237        TensorArg::Alias {
238            input_pos: index,
239            strides: self.strides.clone(),
240            shape: self.shape.clone(),
241        }
242    }
243    /// Convert the handle into a [buffer argument](BufferArg).
244    pub fn into_buffer_arg(self) -> BufferArg<R> {
245        unsafe { BufferArg::from_raw_parts_binding(self.handle, self.shape.iter().product()) }
246    }
247
248    /// Create a handle from raw parts.
249    ///
250    /// # Safety
251    ///
252    /// If you provide wrong strides or shapes, it might create undefined behavior caused by
253    /// out-of-bounds reads and writes.
254    pub unsafe fn from_raw_parts(
255        handle: cubecl_runtime::server::Handle,
256        strides: Strides,
257        shape: Shape,
258    ) -> Self {
259        unsafe { Self::from_raw_parts_binding(handle.binding(), strides, shape) }
260    }
261
262    /// Create a handle from raw parts.
263    ///
264    /// # Safety
265    ///
266    /// If you provide wrong strides or shapes, it might create undefined behavior caused by
267    /// out-of-bounds reads and writes.
268    pub unsafe fn from_raw_parts_binding(
269        handle: cubecl_runtime::server::BufferBinding,
270        strides: Strides,
271        shape: Shape,
272    ) -> Self {
273        Self {
274            handle,
275            strides,
276            shape,
277            runtime: PhantomData,
278        }
279    }
280
281    pub fn into_copy_descriptor(self, elem_size: usize) -> CopyDescriptor {
282        CopyDescriptor {
283            handle: self.handle,
284            shape: self.shape,
285            strides: self.strides,
286            elem_size,
287        }
288    }
289}