cubecl_tile/tile/kind/memory/view/
decoded.rs1use std::marker::PhantomData;
5
6use cubecl::prelude::*;
7
8use crate::*;
9
10#[derive(CubeType, Clone)]
16pub struct Decoded<O: Float, W: Size> {
17 window: Tile,
18 #[cube(comptime)]
19 line: PhantomData<(O, W)>,
20}
21
22#[cube]
23impl<O: Float, W: Size> Decoded<O, W> {
24 pub fn read(&self, row: usize, line: usize) -> Vector<O, W> {
26 self.read_under(row, line, comptime!(Guard::Checked))
27 }
28
29 pub fn read_unchecked(&self, row: usize, line: usize) -> Vector<O, W> {
38 let provable = self.window.guard_provable();
39 comptime!(assert!(
40 provable,
41 "Decoded::read_unchecked: a clamped window's reads are clamped, never untested"
42 ));
43 self.read_under(row, line, comptime!(Guard::Proved))
44 }
45
46 fn read_under(&self, row: usize, line: usize, #[comptime] guard: Guard) -> Vector<O, W> {
47 let packing = self.window.packing();
48 match comptime!(packing) {
49 Packing::Plain => read_plain_line::<O, W>(&self.window, row, line, guard),
50 Packing::Packed { .. } => read_packed_line::<O, W>(&self.window, row, line, guard),
51 }
52 }
53}
54
55#[cube]
56impl Tile {
57 pub fn decoded<O: Float, W: Size>(&self) -> Decoded<O, W> {
71 let (packing, scaled) = (self.packing(), self.factor.scaled());
72 comptime!(assert!(
73 packing != Packing::Plain || !scaled,
74 "Tile::decoded: plain values under scales are decoded by a copy (`dequantize_from`)"
75 ));
76 Decoded::<O, W> {
77 window: self.clone(),
78 line: PhantomData,
79 }
80 }
81}
82
83#[cube]
85fn read_plain_line<O: Float, W: Size>(
86 window: &Tile,
87 row: usize,
88 line: usize,
89 #[comptime] guard: Guard,
90) -> Vector<O, W> {
91 let space = comptime!(window.place.space.clone());
92 let lines = comptime!(space.extent_at(space.rank() - 1)) / W::value();
93 let values = window.flat::<O, W>();
94 match comptime!(guard) {
95 Guard::Checked => values.read(row * lines + line),
96 Guard::Proved => values.read_unchecked(row * lines + line),
97 }
98}
99
100#[cube]
105fn read_packed_line<O: Float, W: Size>(
106 window: &Tile,
107 row: usize,
108 line: usize,
109 #[comptime] guard: Guard,
110) -> Vector<O, W> {
111 let space = comptime!(window.place.space.clone());
112 let rank = comptime!(space.rank());
113 let along = comptime!(space.axis_at(rank - 1));
114 let packing = window.packing();
115 let field = comptime!(match packing {
116 Packing::Packed { field } => field,
117 Packing::Plain => unreachable!("Tile::decoded serves plain values as they lie"),
118 });
119 let (bits, per_word) = comptime!((field.size_bits() as u32, field.per_word()));
120 let width = W::value();
121 let load = window.vector_tile();
122 comptime!(assert!(
123 load.extents() == [(along, per_word)],
124 "Tile::decoded: packed values are read a word a load, along {along:?}; this tile loads \
125 {:?}. Bind it one word wide",
126 load.extents()
127 ));
128 let varies = window.factor.varies_along(comptime!(along));
129 comptime!(assert!(
130 width == 1 || !varies,
131 "Tile::decoded: a {width}-wide line runs along {along:?}, which the scales vary along, so \
132 one line lies under several scales"
133 ));
134 let fields = comptime!(core::cmp::min(width, per_word));
136 let words_per_line = comptime!(width / fields);
137 let size!(NF) = fields;
138 let mem = window.mem("Tile::decoded");
139 let looks_up = mem.codebook.present();
140 let scaled = window.factor.scaled();
141 let words = window.nd_words::<Const<1>>(guard);
142 let rows = Coords::constant(comptime!(
144 (0..rank - 1)
145 .map(|p| space.extent_at(p))
146 .collect::<Vec<_>>()
147 ));
148 let mut first = rows.unravel(row as u32);
149 let along_first = (line * width) as u32;
150 first.push(along_first);
151 let shift = (along_first % comptime!(per_word as u32)) * bits;
153
154 let mut values = Vector::<f32, W>::empty();
155 #[unroll]
156 for w in 0..words_per_line {
157 let mut at = Coords::<u32>::new();
158 #[unroll]
159 for p in 0..rank {
160 if comptime!(p == rank - 1) {
161 at.push(first.at(p) + comptime!((w * per_word) as u32));
162 } else {
163 at.push(first.at(p));
164 }
165 }
166 let mut word = words.read(load.index(&at, &space)).extract(0usize);
167 if comptime!(fields < per_word) {
168 word >>= shift;
169 }
170 let word = Vector::<u32, Const<1>>::new(word);
171 if comptime!(looks_up) {
172 let indices = unpack_line::<u32, Const<1>, NF>(word, comptime!(field));
173 #[unroll]
174 for j in 0..fields {
175 values.insert(w * fields + j, mem.codebook.entry(indices.extract(j)));
176 }
177 } else {
178 let fields_of_word = unpack_line::<f32, Const<1>, NF>(word, comptime!(field));
179 #[unroll]
180 for j in 0..fields {
181 values.insert(w * fields + j, fields_of_word.extract(j));
182 }
183 }
184 }
185 if comptime!(scaled) {
186 let scale = window
187 .factor
188 .at_coords(&first, comptime!(space.clone()), guard);
189 values *= Vector::<f32, W>::cast_from(scale);
190 }
191 Vector::<O, W>::cast_from(values)
192}