Skip to main content

cubecl_tile/launch/spec/
base.rs

1//! The comptime half of an operand.
2
3use cubecl::zspace::SmallVec;
4
5use crate::Delivery;
6use crate::{Axis, Boundary, BoundaryPolicy, Field, Packing, Paging, Projection, Space, Storage};
7
8/// The comptime half of an operand: its axis mapping, edge checks, packing, storage and delivery.
9#[derive(Clone, PartialEq, Eq, Hash, Debug)]
10pub struct TileSpec {
11    /// How this operand's logical axes address its buffer's physical ones.
12    pub projection: Projection,
13    /// Out-of-bounds handling per coordinate axis; `None` or an empty list is unchecked.
14    pub boundaries: SmallVec<[Option<Boundary>; Space::MAX_RANK]>,
15    /// The launch's units per cube, `0` when unknown.
16    pub units: usize,
17    /// How this operand's values sit in its binding.
18    pub(crate) packing: Packing,
19    /// What this operand's storage tiles are to the windows it is read through.
20    pub storage: Storage,
21    /// How the buffer is stored: its stated storage tiles, finest first, as its
22    /// [`StoragePartitioning`](crate::StoragePartitioning) reads back. Empty for a plain buffer.
23    /// What a load of it covers is the kernel's to pick
24    /// ([`VectorTile::new`](crate::VectorTile::new)).
25    pub stored_tiles: Vec<(Axis, usize)>,
26    /// Who moves this operand into a stage ([`delivery`](Self::delivery)).
27    pub delivery: Delivery,
28    /// The axes a page table resolves where the buffer is a pool of pages read as sequences
29    /// ([`TileArgLaunch::indexed`](crate::TileArgLaunch::indexed)); `None` for a buffer read where
30    /// it lies.
31    pub paging: Option<Paging>,
32}
33
34impl TileSpec {
35    /// An operand's spec from its mapping, unchecked, plain, untiled, cube size unknown.
36    pub fn new(projection: Projection) -> Self {
37        TileSpec {
38            projection,
39            boundaries: SmallVec::new(),
40            units: 0,
41            packing: Packing::Plain,
42            storage: Storage::Strided,
43            stored_tiles: Vec::new(),
44            delivery: Delivery::SyncPerUnit,
45            paging: None,
46        }
47    }
48
49    /// [`new`](Self::new) over the [`direct`](Projection::direct) mapping.
50    pub fn direct(axes: &[Axis]) -> Self {
51        Self::new(Projection::direct(axes))
52    }
53
54    /// The axes this operand spans, in its logical order.
55    pub fn axes(&self) -> &[Axis] {
56        self.projection.logical_axes()
57    }
58
59    /// This spec with one boundary mode per coordinate axis; panics on any other count.
60    pub fn boundaries(mut self, boundaries: &[Option<Boundary>]) -> Self {
61        let coord_rank = self.projection.coordinate_rank();
62        assert!(
63            boundaries.len() == coord_rank,
64            "TileSpec::boundaries: {} modes stated but the operand has {coord_rank} coordinate \
65             axes",
66            boundaries.len()
67        );
68        self.boundaries = match boundaries.iter().any(Option::is_some) {
69            true => SmallVec::from_slice(boundaries),
70            false => SmallVec::new(),
71        };
72        self
73    }
74
75    /// This spec checked as `policy` says.
76    pub fn boundary(self, policy: BoundaryPolicy) -> Self {
77        let coord_rank = self.projection.coordinate_rank();
78        match policy {
79            BoundaryPolicy::Unchecked => self.boundaries(&vec![None; coord_rank]),
80            // A spec has no launch to say which axes some extent could overhang: every one.
81            BoundaryPolicy::Every(boundary) | BoundaryPolicy::AnyExtent(boundary) => {
82                self.boundaries(&vec![Some(boundary); coord_rank])
83            }
84        }
85    }
86
87    /// This spec with its binding holding `u32` words of `field`-wide values.
88    pub fn packed(mut self, field: impl Into<Field>) -> Self {
89        self.packing = Packing::Packed {
90            field: field.into(),
91        };
92        self
93    }
94
95    /// This spec with its stages moved by `delivery`: each unit its own lines, landed when the fill
96    /// returns or after it, or one unit the whole stage in bulk. The kernel does not change; the
97    /// fill does.
98    ///
99    /// # Panics
100    ///
101    /// [`Tma`](Delivery::Tma), which moves a tensor map rather than a tensor, and
102    /// [`Procedural`](Delivery::Procedural), which moves nothing.
103    pub fn delivery(mut self, delivery: Delivery) -> Self {
104        assert!(
105            matches!(
106                delivery,
107                Delivery::SyncPerUnit | Delivery::AsyncPerUnit | Delivery::AsyncBulk
108            ),
109            "TileSpec::delivery: a tensor is moved per unit or in bulk; {delivery:?} is a tensor \
110             map's or a coordinate tile's"
111        );
112        self.delivery = delivery;
113        self
114    }
115
116    /// Whether this operand has bounds checking enabled.
117    pub fn is_checked(&self) -> bool {
118        self.boundaries.iter().any(Option::is_some)
119    }
120}