cubecl_core/frontend/
tensor_layout.rs1use alloc::boxed::Box;
2
3use cubecl_ir::{
4 dialect::spirv::{CreateLayoutOp, CreateViewOp, SliceOp},
5 pliron::{builtin::op_interfaces::OneResultInterface, r#type::Typed},
6 types::spirv::{ClampMode, TensorLayoutType, TensorViewType},
7};
8use pliron::r#type::TypeHandle;
9
10use crate::{self as cubecl, unexpanded};
11
12use crate::prelude::*;
13
14#[derive_cube_comptime]
15pub enum TensorClampMode {
16 Undefined,
17 Constant(u32),
18 ClampToEdge,
19 Repeat,
20 RepeatMirrored,
21}
22
23impl From<TensorClampMode> for ClampMode {
24 fn from(value: TensorClampMode) -> Self {
25 match value {
26 TensorClampMode::Undefined => ClampMode::Undefined,
27 TensorClampMode::Constant(val) => ClampMode::Constant(val),
28 TensorClampMode::ClampToEdge => ClampMode::ClampToEdge,
29 TensorClampMode::Repeat => ClampMode::Repeat,
30 TensorClampMode::RepeatMirrored => ClampMode::RepeatMirrored,
31 }
32 }
33}
34
35#[derive(CubeType, Clone)]
37pub struct TensorView<T: CubePrimitive> {
38 #[allow(unused)]
39 pub(crate) buffer: Box<[T]>,
40 #[allow(unused)]
41 pub(crate) layout: TensorLayout,
42 #[allow(unused)]
43 pub(crate) view: ComptimeOption<TensorReinterpret>,
44}
45
46#[derive_cube_comptime]
47pub struct TensorLayout;
48
49#[derive_cube_comptime]
50pub struct TensorReinterpret;
51
52impl CubeType for TensorLayout {
53 type ExpandType = NativeExpand<TensorLayout>;
54}
55
56impl CubeDebug for TensorLayout {}
57impl CubePrimitive for TensorLayout {
58 type Scalar = u32;
59 type Size = Const<1>;
60 type WithScalar<S: Scalar> = S;
61
62 fn from_const_value(_: cubecl_ir::ConstantValue) -> Self {
63 panic!("Can't construct tensor layout from constant")
64 }
65}
66impl ConstantTyped for TensorLayout {
67 fn __expand_as_type(_scope: &Scope) -> TypeHandle {
68 unimplemented!()
69 }
70}
71
72impl NativeAssign for TensorLayout {}
73
74impl CubeType for TensorReinterpret {
75 type ExpandType = NativeExpand<TensorReinterpret>;
76}
77
78impl CubeDebug for TensorReinterpret {}
79impl CubePrimitive for TensorReinterpret {
80 type Scalar = u32;
81 type Size = Const<1>;
82 type WithScalar<S: Scalar> = S;
83
84 fn from_const_value(_: cubecl_ir::ConstantValue) -> Self {
85 panic!("Can't construct tensor layout from constant")
86 }
87}
88impl ConstantTyped for TensorReinterpret {
89 fn __expand_as_type(_scope: &Scope) -> TypeHandle {
90 unimplemented!()
91 }
92}
93
94impl NativeAssign for TensorReinterpret {}
95
96#[derive(CubeType, CubeLaunch)]
97pub struct TensorViewBuilder<T: CubePrimitive> {
98 #[allow(unused)]
99 buffer: Box<[T]>,
100 #[allow(unused)]
101 shape: Sequence<u32>,
102 strides: ComptimeOption<Sequence<u32>>,
104 #[cube(comptime)]
105 clamp_mode: TensorClampMode,
106}
107
108#[cube]
109impl<T: CubePrimitive> TensorView<T> {
110 #[allow(clippy::new_ret_no_self)]
111 pub fn new(buffer: &[T], shape: Sequence<u32>) -> TensorViewBuilder<T> {
112 TensorViewBuilder::<T> {
113 buffer: unsafe { buffer.as_boxed_unchecked() },
114 shape,
115 strides: ComptimeOption::new_None(),
116 clamp_mode: comptime![TensorClampMode::Constant(0)],
117 }
118 }
119
120 #[allow(unused)]
121 pub fn slice(&self, offs: Sequence<u32>, shape: Sequence<u32>) -> TensorView<T> {
122 intrinsic!(|scope| {
123 assert_eq!(
124 offs.len(),
125 self.layout.rank(scope),
126 "Offsets and view rank must match"
127 );
128 assert_eq!(
129 offs.len(),
130 shape.len(),
131 "Offsets and shape must have same rank"
132 );
133 let layout = self.layout.read_value(scope);
134 let offs = offs.iter_cloned().map(|it| it.read_value(scope)).collect();
135 let shape = shape.iter_cloned().map(|it| it.read_value(scope)).collect();
136 let slice_op = SliceOp::new(scope.ctx_mut(), layout, offs, shape);
137 scope.register(&slice_op);
138 let new_layout = slice_op.get_result(scope.ctx());
139 TensorViewExpand {
140 buffer: self.buffer.clone(),
141 layout: new_layout.into(),
142 view: self.view.clone(),
143 }
144 })
145 }
146}
147
148impl NativeExpand<TensorLayout> {
149 fn rank(&self, scope: &Scope) -> usize {
150 let ty = self.read_value(scope).get_type(scope.ctx());
151 let ctx = scope.ctx();
152 let ty = ty.deref(ctx);
153 let TensorLayoutType { rank, .. } = ty.downcast_ref().unwrap();
154 *rank
155 }
156}
157
158impl<T: CubePrimitive> TensorView<T> {
159 pub fn permuted(&self, _permutation: Sequence<usize>) -> TensorView<T> {
160 unexpanded!()
161 }
162}
163
164impl<T: CubePrimitive> TensorViewExpand<T> {
165 pub fn __expand_permuted_method(
166 self,
167 scope: &Scope,
168 permutation: SequenceExpand<usize>,
169 ) -> TensorViewExpand<T> {
170 let dims = permutation.len();
171 assert!(dims <= 5, "Max 5 dims allowed");
172 let permutation = permutation
173 .iter_cloned()
174 .map(|it| {
175 it.constant()
176 .expect("permutation must be constant")
177 .as_usize()
178 })
179 .collect::<alloc::vec::Vec<_>>();
180 let ty = TensorViewType::get(scope.ctx(), permutation.len(), false, permutation);
181 let op = CreateViewOp::new(scope.ctx_mut(), ty.into());
182 scope.register(&op);
183 let view = op.get_result(scope.ctx());
184
185 TensorViewExpand {
186 buffer: self.buffer,
187 layout: self.layout,
188 view: ComptimeOptionExpand::Some(view.into()),
189 }
190 }
191}
192
193impl<T: CubePrimitive> TensorViewBuilder<T> {
194 pub fn with_strides(mut self, strides: Sequence<u32>) -> Self {
195 self.strides = ComptimeOption::Some(strides);
196 self
197 }
198
199 pub fn with_clamp_mode(mut self, clamp_mode: TensorClampMode) -> Self {
200 self.clamp_mode = clamp_mode;
201 self
202 }
203
204 pub fn finish(self) -> TensorView<T> {
205 unexpanded!()
206 }
207}
208
209impl<T: CubePrimitive> TensorViewBuilderExpand<T> {
210 pub fn __expand_with_strides_method(
211 mut self,
212 _scope: &Scope,
213 strides: SequenceExpand<u32>,
214 ) -> Self {
215 self.strides = ComptimeOptionExpand::Some(strides);
216 self
217 }
218
219 pub fn __expand_with_clamp_mode_method(
220 mut self,
221 _scope: &Scope,
222 clamp_mode: TensorClampMode,
223 ) -> Self {
224 self.clamp_mode = clamp_mode;
225 self
226 }
227
228 pub fn __expand_finish_method(self, scope: &Scope) -> TensorViewExpand<T> {
229 let shape = self.shape.into_iter().map(|it| it.read_value(scope));
230 let strides = match self.strides {
231 ComptimeOptionExpand::None => None,
232 ComptimeOptionExpand::Some(strides) => {
233 Some(strides.into_iter().map(|it| it.read_value(scope)).collect())
234 }
235 };
236 let clamp_mode = ClampMode::from(self.clamp_mode);
237
238 let op = CreateLayoutOp::new(scope.ctx_mut(), shape.collect(), strides, clamp_mode);
239 scope.register(&op);
240 let layout = op.get_result(scope.ctx());
241
242 TensorViewExpand {
243 buffer: self.buffer,
244 layout: layout.into(),
245 view: ComptimeOptionExpand::None,
246 }
247 }
248}
249
250impl<T: CubePrimitive> LaunchArg for TensorView<T> {
251 type RuntimeArg = TensorViewBuilderLaunch<T>;
252 type CompilationArg = TensorViewBuilderCompilationArg<T>;
253
254 fn register(arg: Self::RuntimeArg, launcher: &mut KernelLauncher) -> Self::CompilationArg {
255 TensorViewBuilder::<T>::register(arg, launcher)
256 }
257
258 fn expand(
259 arg: &Self::CompilationArg,
260 builder: &mut KernelBuilder,
261 ) -> <Self as CubeType>::ExpandType {
262 let build = TensorViewBuilder::<T>::expand(arg, builder);
263 build.__expand_finish_method(&builder.scope)
264 }
265}