1use core::marker::PhantomData;
4
5use cubecl::zspace::{SmallVec, Tiling};
6
7use cubecl::ir::ElemType;
8use cubecl::prelude::*;
9use cubecl::server::BufferBinding;
10
11use super::{boundaries::Boundaries, derivation::Derivation, labels::Labels};
12use crate::BoundaryPolicy;
13use crate::{
14 Axis, Delivery, Extent, Field, Geometry, Launcher, LineMisfit, Packing, Projection, Refusal,
15 Space, Storage, StoragePartitioning, TileArgLaunch, TileSpec, VectorTile,
16};
17
18pub struct Unlabelled;
20pub struct Labelled;
22
23pub(crate) struct ArgData<'a> {
25 pub(crate) launch: &'a Launcher,
26 pub(crate) binding: Option<(TensorBinding, ElemType)>,
29 pub(crate) geometry: Geometry,
30 pub(crate) axes: &'a [Axis],
31 pub(crate) batches: &'a [Axis],
32 pub(crate) projection: Option<Projection>,
34 pub(crate) width: usize,
35 pub(crate) boundary: Option<BoundaryPolicy>,
37 pub(crate) packing: Packing,
38 pub(crate) delivery: Delivery,
40 pub(crate) in_stride_order: bool,
42}
43
44pub struct Arg<'a, S> {
46 data: ArgData<'a>,
47 _state: PhantomData<S>,
48}
49
50impl<'a> Arg<'a, Unlabelled> {
51 pub(crate) fn bound(launch: &'a Launcher, binding: TensorBinding, element: ElemType) -> Self {
52 let geometry = Geometry::from(&binding);
53 Self::over(launch, Some((binding, element)), geometry)
54 }
55
56 pub(crate) fn unbound(launch: &'a Launcher, geometry: &Geometry) -> Self {
57 Self::over(launch, None, geometry.clone())
58 }
59
60 fn over(
61 launch: &'a Launcher,
62 binding: Option<(TensorBinding, ElemType)>,
63 geometry: Geometry,
64 ) -> Self {
65 Arg {
66 data: ArgData {
67 launch,
68 binding,
69 geometry,
70 axes: &[],
71 batches: &[],
72 projection: None,
73 width: 1,
74 boundary: None,
75 packing: Packing::Plain,
76 delivery: Delivery::SyncPerUnit,
77 in_stride_order: false,
78 },
79 _state: PhantomData,
80 }
81 }
82
83 pub fn axes(mut self, axes: &'a [Axis]) -> Arg<'a, Labelled> {
85 self.data.axes = axes;
86 self.labelled()
87 }
88
89 pub fn gathered(mut self, projection: Projection) -> Arg<'a, Labelled> {
92 self.data.projection = Some(projection);
93 self.labelled()
94 }
95
96 fn labelled(self) -> Arg<'a, Labelled> {
97 Arg {
98 data: self.data,
99 _state: PhantomData,
100 }
101 }
102}
103
104impl<'a> Arg<'a, Labelled> {
105 pub fn batches(mut self, axes: &'a [Axis]) -> Self {
107 self.data.batches = axes;
108 self
109 }
110
111 pub fn in_stride_order(mut self) -> Self {
113 self.data.in_stride_order = true;
114 self
115 }
116
117 pub fn vectorize(mut self, width: usize) -> Self {
119 self.data.width = width;
120 self
121 }
122
123 pub fn boundary(mut self, policy: BoundaryPolicy) -> Self {
125 self.data.boundary = Some(policy);
126 self
127 }
128
129 pub fn packed(mut self, field: impl Into<Field>) -> Self {
131 self.data.packing = Packing::Packed {
132 field: field.into(),
133 };
134 self
135 }
136
137 pub fn delivery(mut self, delivery: Delivery) -> Self {
140 self.data.delivery = delivery;
141 self
142 }
143
144 pub fn build(self) -> Result<Bound, Refusal> {
146 self.realize().map(|(bound, _)| bound)
147 }
148
149 pub fn build_spec(self) -> Result<Unbound, Refusal> {
151 let (bound, geometry) = self.realize()?;
152 Ok(Unbound {
153 spec: bound.spec,
154 vector_size: bound.vector_size,
155 geometry,
156 })
157 }
158
159 fn realize(self) -> Result<(Bound, Geometry), Refusal> {
162 let packing = self.data.packing.read_on(self.data.launch.client());
163 let stored = self
164 .data
165 .binding
166 .as_ref()
167 .map(|(b, _)| b.tiling)
168 .unwrap_or_default();
169 let derived = Derivation::view(&self.data, packing, stored)
170 .derived(|| self.derive(packing, stored))?;
171 let tensor = self.data.binding.map(|(mut binding, element)| {
172 binding.shape = derived.geometry.shape().into();
174 binding.strides = derived.geometry.strides().into();
175 binding.tiling = derived.geometry.tiling();
176 (binding.into_tensor_arg(), element)
177 });
178 let bound = Bound {
179 tensor,
180 vector_size: self.data.width,
181 spec: derived.spec,
182 };
183 Ok((bound, derived.geometry))
184 }
185
186 fn derive(&self, packing: Packing, stored: Tiling) -> Result<Derived, Refusal> {
189 let data = &self.data;
190 let launch = data.launch;
191 let width = data.width;
192 let stated = data.projection.is_some() || stored.is_tiled();
193 let (geometry, axes) = match data.in_stride_order {
194 true if stated => return Err(Refusal::StrideOrderOfAStatedLayout),
195 true => data.geometry.clone().in_stride_order(data.axes),
196 false => (data.geometry.clone(), data.axes.to_vec()),
197 };
198 let (storage, stored_tiles) = self.storage(&geometry, &axes);
199 let labels = match data.projection.clone() {
200 Some(projection) => {
201 Labels::stated(&geometry, launch, projection, &axes, data.batches, stored)?
202 }
203 None => Labels::new(&geometry, &axes, data.batches)?,
204 };
205 let Labels {
206 geometry,
207 projection,
208 addressed,
209 } = labels;
210 projection.validate(width)?;
211 let labels = projection.dense_labels();
214 let served = match labels.last() {
215 Some(&axis) => VectorTile::new(&stored_tiles, axis, width)
216 .map_err(LineMisfit::Tile)
217 .and_then(|tile| geometry.serves(tile.extents(), &labels)),
218 None if width == 1 => Ok(()),
219 None => Err(LineMisfit::NoDims),
220 };
221 served.map_err(|why| Refusal::WidthNotServed { width, why })?;
222 let may_overhang;
223 let overhangs = match data.boundary {
224 Some(BoundaryPolicy::AnyExtent(_)) => {
225 may_overhang = launch.may_overhang();
226 &may_overhang
227 }
228 _ => launch.overhangs(),
229 };
230 let boundaries = Boundaries::new(
231 data.boundary,
232 &projection,
233 &addressed,
234 launch.space(),
235 overhangs,
236 width,
237 )?;
238 let spec = TileSpec {
239 projection,
240 boundaries: boundaries.modes,
241 units: launch.cube_dim().num_elems() as usize,
242 packing,
243 storage,
244 stored_tiles,
245 delivery: Delivery::SyncPerUnit,
246 paging: None,
247 }
248 .delivery(data.delivery);
249 Ok(Derived { spec, geometry })
250 }
251
252 fn storage(&self, geometry: &Geometry, axes: &[Axis]) -> (Storage, Vec<(Axis, usize)>) {
256 let launch = self.data.launch;
257 if !(self.data.projection.is_none() && geometry.tiling().is_tiled()) {
258 return (Storage::Strided, Vec::new());
259 }
260 let labels = geometry.labels(axes);
261 let partitioning = StoragePartitioning::new(geometry, &labels);
262 let stored_tiles = partitioning
263 .as_ref()
264 .map_or_else(Vec::new, |storage| storage.tiles().to_vec());
265 let tiles = partitioning
266 .map(|storage| storage.contiguous_tiles(&geometry.extents(&labels)))
267 .unwrap_or_default();
268 let (space, kernel) = (launch.space(), launch.partitioning().space());
272 let mut window: LevelWindow = axes
273 .iter()
274 .map(|&axis| (space.extent_raw(axis), kernel.is_dynamic(axis)))
275 .collect();
276 let windows: SmallVec<[LevelWindow; 8]> = launch
277 .partitioning()
278 .levels()
279 .iter()
280 .map(|level| {
281 for (&axis, cut) in axes.iter().zip(window.iter_mut()) {
282 if let Some(tile) = level.tile(axis) {
283 *cut = (Extent::Static(tile), false);
284 }
285 }
286 window.clone()
287 })
288 .collect();
289 let cuts_to = |window: &[(Extent, bool)], tile: &[(Axis, usize)]| {
292 axes.iter().zip(window).all(|(&axis, &(extent, dynamic))| {
293 let stored = tile
294 .iter()
295 .find(|&&(a, _)| a == axis)
296 .map_or(1, |&(_, e)| e);
297 !dynamic && extent == Extent::Static(stored)
298 })
299 };
300 let level = tiles
301 .iter()
302 .rev()
303 .find_map(|tile| windows.iter().position(|window| cuts_to(window, tile)));
304 (Storage::Tiled(level), stored_tiles)
305 }
306}
307
308#[derive(Clone)]
311pub(crate) struct Derived {
312 pub(crate) spec: TileSpec,
313 pub(crate) geometry: Geometry,
314}
315
316pub struct Bound {
319 tensor: Option<(TensorArg, ElemType)>,
320 pub vector_size: usize,
322 pub spec: TileSpec,
323}
324
325impl Bound {
326 pub fn rebound(&self, buffers: &mut impl Iterator<Item = BufferBinding>) -> Option<Self> {
332 let tensor = match &self.tensor {
333 Some((tensor, element)) => Some((rebound_tensor(tensor, buffers.next()?)?, *element)),
334 None => None,
335 };
336 Some(Self {
337 tensor,
338 vector_size: self.vector_size,
339 spec: self.spec.clone(),
340 })
341 }
342
343 pub fn arg(self) -> TileArgLaunch {
345 let width = self.bound_width();
346 let (tensor, element) = self
347 .tensor
348 .expect("Bound::arg: this operand was built over geometry alone and has no tensor");
349 TileArgLaunch::new(tensor, element, width, self.spec)
350 }
351
352 pub fn tensor(self) -> TensorArg {
354 self.tensor
355 .expect("Bound::tensor: this operand was built over geometry alone and has no tensor")
356 .0
357 }
358
359 pub fn bound_width(&self) -> usize {
362 self.spec.packing.physical(self.vector_size).max(1)
363 }
364}
365
366#[derive(Clone)]
368pub struct Unbound {
369 pub spec: TileSpec,
370 pub vector_size: usize,
372 pub geometry: Geometry,
373}
374
375type LevelWindow = SmallVec<[(Extent, bool); Space::MAX_RANK]>;
378
379pub(crate) fn rebound_tensor(tensor: &TensorArg, buffer: BufferBinding) -> Option<TensorArg> {
382 match tensor {
383 TensorArg::Handle { handle } => Some(TensorArg::Handle {
384 handle: TensorBinding {
385 handle: buffer,
386 strides: handle.strides.clone(),
387 shape: handle.shape.clone(),
388 tiling: handle.tiling,
389 },
390 }),
391 TensorArg::Alias { .. } => None,
392 }
393}