Skip to main content

cubecl_std/tensor/layout/
linear.rs

1use cubecl::prelude::*;
2use cubecl_core::{self as cubecl, ir::UIntKind, zspace::Shape};
3
4use crate::tensor::{
5    View, ViewMut, is_contiguous, is_contiguous_pitched,
6    launch::{ConcreteLayout, ConcreteLayoutLaunch, MemoryArg, ViewArg, ViewLayoutLaunchArg},
7    layout::{
8        Coords1d, Layout, LayoutExpand, VirtualLayoutOperationsExpand,
9        permuted::{PermutedLayout, PermutedLayoutCompilationArg, PermutedLayoutLaunch},
10        plain::PlainLayout,
11        strided::{StridedLayout, StridedLayoutCompilationArg},
12    },
13};
14
15/// Maps a linear index based on vector count to a potentially strided tensor. Only applies the
16/// necessary level of striding, either none, only the last dim (for freshly allocated strided
17/// tensors), or all dimensions.
18///
19/// Treats indices as the vector index, with the shape being adjusted for vector size.
20///
21/// `Layout` version of [`crate::tensor::contiguous::index_offset_contiguous()`]
22#[derive(CubeType, Clone)]
23pub enum LinearViewLayout {
24    /// Input is contiguous, no mapping
25    Plain(PlainLayout),
26    /// Strided tensor, i.e. freshly allocated but not permuted
27    Strided(StridedLayout),
28    /// Permuted layout, tracks the entire shape/strides and not just the last dim
29    Permuted(PermutedLayout),
30}
31
32impl LinearViewLayoutExpand {
33    fn __expand_inner_method(
34        &self,
35        _scope: &Scope,
36    ) -> &dyn VirtualLayoutOperationsExpand<Coords1d, Coords1d> {
37        match self {
38            LinearViewLayoutExpand::Plain(layout) => layout,
39            LinearViewLayoutExpand::Strided(layout) => layout,
40            LinearViewLayoutExpand::Permuted(layout) => layout,
41        }
42    }
43}
44
45#[derive(Default)]
46pub struct LinearViewLayoutLaunch {
47    reference_shape: Option<Shape>,
48}
49
50impl ViewLayoutLaunchArg for LinearViewLayout {
51    type RuntimeArg<R: Runtime> = LinearViewLayoutLaunch;
52    type CompilationArg = LinearLayoutCompilationArg;
53
54    fn register<R: Runtime, B: MemoryArg>(
55        runtime_arg: Self::RuntimeArg<R>,
56        buffer: &B,
57        ty: Type,
58        launcher: &mut KernelLauncher<R>,
59    ) -> Self::CompilationArg {
60        let shape = buffer.shape();
61        match runtime_arg.reference_shape {
62            Some(reference_shape) if reference_shape.as_slice() != shape => {
63                let arg = PermutedLayoutLaunch::from_reference_shape(reference_shape);
64                let comp_arg = PermutedLayout::register(arg, buffer, ty, launcher);
65                LinearLayoutCompilationArg::Permuted(comp_arg)
66            }
67            _ => {
68                let strides = buffer.strides();
69                if is_contiguous(shape, strides) {
70                    PlainLayout::register((), buffer, ty, launcher);
71                    LinearLayoutCompilationArg::Plain
72                } else if is_contiguous_pitched(shape, strides) {
73                    let comp_arg = StridedLayout::register((), buffer, ty, launcher);
74                    LinearLayoutCompilationArg::Strided(comp_arg)
75                } else {
76                    let comp_arg =
77                        PermutedLayout::register(Default::default(), buffer, ty, launcher);
78                    LinearLayoutCompilationArg::Permuted(comp_arg)
79                }
80            }
81        }
82    }
83    fn expand(
84        compilation_arg: &Self::CompilationArg,
85        ty: Type,
86        builder: &mut cubecl::prelude::KernelBuilder,
87    ) -> <Self as cubecl::prelude::CubeType>::ExpandType {
88        match compilation_arg {
89            LinearLayoutCompilationArg::Plain => {
90                LinearViewLayoutExpand::Plain(PlainLayout::expand(&(), ty, builder))
91            }
92            LinearLayoutCompilationArg::Strided(arg) => {
93                LinearViewLayoutExpand::Strided(StridedLayout::expand(arg, ty, builder))
94            }
95            LinearLayoutCompilationArg::Permuted(arg) => {
96                LinearViewLayoutExpand::Permuted(PermutedLayout::expand(arg, ty, builder))
97            }
98        }
99    }
100}
101
102#[derive(Debug, Hash, PartialEq, Eq, Clone)]
103pub enum LinearLayoutCompilationArg {
104    Plain,
105    Strided(StridedLayoutCompilationArg),
106    Permuted(PermutedLayoutCompilationArg),
107}
108
109impl LinearViewLayoutLaunch {
110    /// Construct a linear layout from shapes, strides and vector size of the tensor
111    pub fn new() -> Self {
112        Self::default()
113    }
114
115    /// Construct a possibly broadcast linear layout from shapes/strides and a reference shape
116    pub fn from_reference_shape(reference_shape: Shape) -> Self {
117        Self {
118            reference_shape: Some(reference_shape),
119        }
120    }
121
122    /// Construct a possibly broadcast linear layout from a tensor handle and reference handle
123    pub fn from_reference_handle<R: Runtime>(reference: TensorBinding<R>) -> Self {
124        Self::from_reference_shape(reference.shape)
125    }
126}
127
128#[cube]
129impl Layout for LinearViewLayout {
130    type Coordinates = Coords1d;
131    type SourceCoordinates = Coords1d;
132
133    #[allow(unused)]
134    fn to_source_pos(&self, pos: Self::Coordinates) -> usize {
135        intrinsic!(|scope| {
136            let inner = self.__expand_inner_method(scope);
137            inner.__expand_to_source_pos_virt_method(scope, pos)
138        })
139    }
140
141    fn to_source_pos_checked(&self, pos: Self::Coordinates) -> (usize, bool) {
142        (self.to_source_pos(pos), self.is_in_bounds(pos))
143    }
144
145    fn shape(&self) -> Self::Coordinates {
146        intrinsic!(|scope| {
147            let inner = self.__expand_inner_method(scope);
148            inner.__expand_shape_virt_method(scope)
149        })
150    }
151
152    #[allow(unused)]
153    fn is_in_bounds(&self, pos: Self::Coordinates) -> bool {
154        intrinsic!(|scope| {
155            let inner = self.__expand_inner_method(scope);
156            inner.__expand_is_in_bounds_virt_method(scope, pos)
157        })
158    }
159}
160
161/// Concrete version of the layout, so it can be launched on its own
162pub type LinearLayout = ConcreteLayout<LinearViewLayout>;
163pub type LinearLayoutLaunch<R> = ConcreteLayoutLaunch<LinearViewLayout, R>;
164
165/// [`View`] with a linear layout inferred from the shape/strides at launch.
166/// Useful for elementwise kernels.
167pub type LinearView<'a, E> = View<'a, E, Coords1d>;
168pub type LinearViewMut<'a, E> = ViewMut<'a, E, Coords1d>;
169/// Launch type for [`LinearView`].
170pub type LinearViewLaunch<R> = ViewArg<Coords1d, R>;
171
172/// Create a linear layout from a handle and vector size
173pub fn linear_layout<R: Runtime>(
174    handle: &TensorBinding<R>,
175    vector_size: VectorSize,
176) -> LinearLayoutLaunch<R> {
177    LinearLayoutLaunch::from_handle(
178        handle,
179        // Don't care about type size, only vector size
180        Type::new(UIntKind::U32.into()).with_vector_size(vector_size),
181        LinearViewLayoutLaunch::new(),
182    )
183}
184
185/// Create a linear tensor view from a handle
186pub fn linear_view<R: Runtime>(handle: TensorBinding<R>) -> LinearViewLaunch<R> {
187    let layout = LinearViewLayoutLaunch::new();
188    LinearViewLaunch::new_tensor::<LinearViewLayout>(handle.into_tensor_arg(), layout)
189}
190
191/// Create a possibly broadcast linear tensor view from a handle and reference handle
192pub fn linear_view_with_reference<R: Runtime>(
193    handle: TensorBinding<R>,
194    reference: TensorBinding<R>,
195) -> LinearViewLaunch<R> {
196    let layout = LinearViewLayoutLaunch::from_reference_handle(reference);
197    LinearViewLaunch::new_tensor::<LinearViewLayout>(handle.into_tensor_arg(), layout)
198}
199
200pub fn linear_view_alias<R: Runtime>(handle: &TensorBinding<R>, pos: usize) -> LinearViewLaunch<R> {
201    let layout = LinearViewLayoutLaunch::new();
202    LinearViewLaunch::new_tensor::<LinearViewLayout>(handle.as_alias(pos), layout)
203}