1use std::mem::MaybeUninit;
2use std::ops::Range;
3
4use rten_base::byte_cast::{AsBytes, cast_uninit_mut_slice};
5use rten_simd::ops::{BitOps, MaskOps, NumOps};
6use rten_simd::{Isa, Mask, Simd};
7use rten_tensor::{NdTensorView, Storage};
8
9use super::packing::int8::{PackedBMeta, shift_cast_i8_u8};
10
11pub struct RowOffsets {
16 pub chan: Vec<i32>,
18
19 pub y: Vec<i32>,
21
22 pub x: Vec<i32>,
24}
25
26pub struct ColOffsets {
31 pub y: Vec<i32>,
34
35 pub x: Vec<i32>,
38}
39
40pub struct Im2Col<'a, T> {
57 pub image: NdTensorView<'a, T, 3>,
58
59 pub row_offsets: RowOffsets,
66
67 pub col_offsets: ColOffsets,
74
75 pub n_cols: usize,
77
78 pub n_rows: usize,
80
81 pub max_y_offset: i32,
84
85 pub max_x_offset: i32,
88}
89
90impl<T: Copy + Default> Im2Col<'_, T> {
91 pub fn rows(&self) -> usize {
93 self.n_rows
94 }
95
96 pub fn cols(&self) -> usize {
98 self.n_cols
99 }
100
101 #[inline(always)]
110 pub(super) fn pack_block<I: Isa, const NR_REGS: usize>(
111 &self,
112 isa: I,
113 out: &mut [MaybeUninit<T>],
114 panel_width: usize,
115 rows: Range<usize>,
116 cols: Range<usize>,
117 ) {
118 let ops = isa.i32();
119 let mask_ops = isa.m32();
120
121 assert_eq!(panel_width, ops.len() * NR_REGS);
122
123 let col_range = cols.start..cols.end.next_multiple_of(panel_width);
124 let used_size = rows.len() * col_range.len();
125 assert_eq!(out.len(), used_size);
126
127 let col_y_offsets = &self.col_offsets.y[col_range.clone()];
128 let col_x_offsets = &self.col_offsets.x[col_range.clone()];
129 let row_chan_offsets = &self.row_offsets.chan[rows.clone()];
130 let row_y_offsets = &self.row_offsets.y[rows.clone()];
131 let row_x_offsets = &self.row_offsets.x[rows.clone()];
132
133 let img_data = self.image.storage();
134
135 let img_len = self.image.storage().len();
138 assert!(img_len > 0 && img_len <= i32::MAX as usize);
139 let max_img_offset = ops.splat(img_len as i32 - 1);
140
141 let mut out_offset = 0;
144
145 for start_col in (0..col_y_offsets.len()).step_by(ops.len() * NR_REGS) {
146 let col_y_offset: [I::I32; NR_REGS] =
147 std::array::from_fn(|i| ops.load(&col_y_offsets[start_col + ops.len() * i..]));
148 let col_x_offset: [I::I32; NR_REGS] =
149 std::array::from_fn(|i| ops.load(&col_x_offsets[start_col + ops.len() * i..]));
150 let max_x_offset = ops.splat(self.max_x_offset);
151 let max_y_offset = ops.splat(self.max_y_offset);
152
153 for ((&row_chan_offset, &row_y_offset), &row_x_offset) in row_chan_offsets
154 .iter()
155 .zip(row_y_offsets.iter())
156 .zip(row_x_offsets.iter())
157 {
158 let row_chan_offset = ops.splat(row_chan_offset);
159 let row_y_offset = ops.splat(row_y_offset);
160 let row_x_offset = ops.splat(row_x_offset);
161
162 for i in 0..NR_REGS {
163 let y_offset = ops.add(col_y_offset[i], row_y_offset);
164 let x_offset = ops.add(col_x_offset[i], row_x_offset);
165
166 let offsets = ops.add(ops.add(row_chan_offset, y_offset), x_offset);
167
168 let offsets = ops.min(ops.max(offsets, ops.zero()), max_img_offset);
171
172 let zero = ops.zero();
175
176 let y_valid =
177 mask_ops.and(ops.ge(y_offset, zero), ops.le(y_offset, max_y_offset));
178 let x_valid =
179 mask_ops.and(ops.ge(x_offset, zero), ops.le(x_offset, max_x_offset));
180 let pad_mask = mask_ops.and(y_valid, x_valid);
181
182 let offsets_array = ops.select(offsets, zero, pad_mask).to_array();
185 let pad_mask_array = pad_mask.to_array();
186
187 for idx in 0..ops.len() {
189 let src_elem =
191 unsafe { *img_data.get_unchecked(offsets_array[idx] as usize) };
192
193 let elem = if pad_mask_array[idx] {
195 src_elem
196 } else {
197 T::default()
198 };
199
200 let out_el = unsafe { out.get_unchecked_mut(out_offset + idx) };
202 out_el.write(elem);
203 }
204
205 out_offset += ops.len();
206 }
207 }
208 }
209
210 assert_eq!(out_offset, used_size);
212 }
213}
214
215impl Im2Col<'_, i8> {
216 #[inline(always)]
225 #[allow(unused)] pub(super) fn pack_block_i8_dot<
227 I: Isa,
228 const NR: usize,
229 const NR_REGS: usize,
230 const K_TILE: usize,
231 >(
232 &self,
233 isa: I,
234 out: &mut [MaybeUninit<i8>],
235 rows: Range<usize>,
236 cols: Range<usize>,
237 zero_point: i8,
238 ) {
239 self.pack_block_int8::<_, NR, NR_REGS, K_TILE, false>(isa, out, rows, cols, zero_point);
240 }
241
242 #[inline(always)]
245 #[allow(unused)] pub(super) fn pack_block_i8_dot_cast_u8<
247 I: Isa,
248 const NR: usize,
249 const NR_REGS: usize,
250 const K_TILE: usize,
251 >(
252 &self,
253 isa: I,
254 out: &mut [MaybeUninit<u8>],
255 rows: Range<usize>,
256 cols: Range<usize>,
257 zero_point: i8,
258 ) {
259 let out = cast_uninit_mut_slice(out).unwrap();
260 self.pack_block_int8::<_, NR, NR_REGS, K_TILE, true>(isa, out, rows, cols, zero_point);
261 }
262
263 #[inline(always)]
264 fn pack_block_int8<
265 I: Isa,
266 const NR: usize,
267 const NR_REGS: usize,
268 const K_TILE: usize,
269 const CAST_B_U8: bool,
270 >(
271 &self,
272 isa: I,
273 out: &mut [MaybeUninit<i8>],
274 rows: Range<usize>,
275 cols: Range<usize>,
276 zero_point: i8,
277 ) {
278 let ops = isa.i32();
279 assert_eq!(ops.len() * NR_REGS, NR);
280
281 let mask_ops = isa.m32();
282
283 debug_assert!(rows.end <= self.rows());
284 debug_assert!(cols.end <= self.cols());
285
286 let max_x_offset = ops.splat(self.max_x_offset);
287 let max_y_offset = ops.splat(self.max_y_offset);
288
289 let col_x_offsets = &self.col_offsets.x;
290 debug_assert_eq!(col_x_offsets.len() % ops.len(), 0);
291
292 let col_y_offsets = &self.col_offsets.y;
293 debug_assert_eq!(col_y_offsets.len() % ops.len(), 0);
294
295 let row_x_offsets = &self.row_offsets.x;
296 debug_assert_eq!(row_x_offsets.len() % K_TILE, 0);
297
298 let row_y_offsets = &self.row_offsets.y;
299 debug_assert_eq!(row_y_offsets.len() % K_TILE, 0);
300
301 let row_chan_offsets = &self.row_offsets.chan;
302 debug_assert_eq!(row_chan_offsets.len() % K_TILE, 0);
303
304 let img_data = self.image.storage();
305
306 let mut out_offset = 0;
307
308 for start_col in cols.step_by(ops.len() * NR_REGS) {
309 let col_y_offset: [I::I32; NR_REGS] =
310 std::array::from_fn(|i| ops.load(&col_y_offsets[start_col + i * ops.len()..]));
311 let col_x_offset: [I::I32; NR_REGS] =
312 std::array::from_fn(|i| ops.load(&col_x_offsets[start_col + i * ops.len()..]));
313 let zero = ops.zero();
314
315 let mut col_sums = [ops.zero().to_array(); NR_REGS];
316
317 for start_row in rows.clone().step_by(K_TILE) {
318 for i in 0..K_TILE {
319 let k = start_row + i;
320 let row_x_offset = ops.splat(unsafe { *row_x_offsets.get_unchecked(k) });
321 let row_y_offset = ops.splat(unsafe { *row_y_offsets.get_unchecked(k) });
322 let row_chan_offset = ops.splat(unsafe { *row_chan_offsets.get_unchecked(k) });
323
324 for c_block in 0..NR_REGS {
325 let x_offsets = ops.add(row_x_offset, col_x_offset[c_block]);
326 let y_offsets = ops.add(row_y_offset, col_y_offset[c_block]);
327 let offsets = ops.add(ops.add(x_offsets, y_offsets), row_chan_offset);
328
329 let y_valid =
330 mask_ops.and(ops.ge(y_offsets, zero), ops.le(y_offsets, max_y_offset));
331 let x_valid =
332 mask_ops.and(ops.ge(x_offsets, zero), ops.le(x_offsets, max_x_offset));
333 let pad_mask = mask_ops.and(y_valid, x_valid);
334 let pad_mask_array = pad_mask.to_array();
335
336 let offsets_array = ops.select(offsets, zero, pad_mask).to_array();
339
340 for idx in 0..ops.len() {
341 let out_elem = unsafe {
342 out.get_unchecked_mut(
343 out_offset + (c_block * ops.len() + idx) * K_TILE + i,
344 )
345 };
346 let src_elem =
347 unsafe { *img_data.get_unchecked(offsets_array[idx] as usize) };
348
349 if CAST_B_U8 {
350 let src_elem = shift_cast_i8_u8(src_elem);
351 let elem = if pad_mask_array[idx] { src_elem } else { 0 };
352 col_sums[c_block][idx] += elem as i32;
353 out_elem.write(elem as i8);
354 } else {
355 let elem = if pad_mask_array[idx] { src_elem } else { 0 };
356 col_sums[c_block][idx] += elem as i32;
357 out_elem.write(elem);
358 }
359 }
360 }
361 }
362 out_offset += ops.len() * NR_REGS * K_TILE;
363 }
364
365 let meta = PackedBMeta::<NR> {
367 col_sums: *unsafe {
370 std::mem::transmute::<&[<I::I32 as Simd>::Array; NR_REGS], &[i32; NR]>(
371 &col_sums,
372 )
373 },
374 zero_points: if CAST_B_U8 {
375 [shift_cast_i8_u8(zero_point) as i32; NR]
376 } else {
377 [zero_point as i32; NR]
378 },
379 };
380 let meta_bytes = meta.as_bytes();
381 for (i, byte) in meta_bytes.iter().enumerate() {
382 out[out_offset + i].write(*byte as i8);
383 }
384 out_offset += meta_bytes.len();
385 }
386
387 assert_eq!(out_offset, out.len());
389 }
390}