1use cubecl::prelude::*;
7
8use cubecl_std::tensor::{
9 AsView, AsViewExpand, AsViewMut, AsViewMutExpand, ErasedTensor, View, ViewMut, WriteOnly,
10 layout::{Coordinates, Coords1d, Coords2d, CoordsDyn},
11};
12
13use crate::*;
14
15#[cube]
16impl Tile {
17 pub fn view<T: Numeric, W: Size>(&self) -> View<'_, Vector<T, W>, CoordsDyn> {
20 let g = self.mem("view");
21 if comptime!(g.store.packing != Packing::Plain) {
22 panic!(
23 "Tile::view: a packed tile only serves values its read unpacks (Tile::copy_from, \
24 Tile::matrix_packed)"
25 )
26 }
27 g.window_view::<T, W>(comptime!(Guard::Checked))
28 }
29
30 #[allow(dead_code)] pub(crate) fn fetched_scalars(&self) -> comptime_type!(usize) {
33 self.mem("fetched_scalars").fetched_scalars()
34 }
35
36 pub(crate) fn free_stage(&self) {
38 self.mem("free_stage").store.free();
39 self.free_scales();
40 }
41
42 #[allow(dead_code)] pub(crate) fn fetch_from<T: Numeric, W: Size>(
45 &self,
46 src: &Tile,
47 fetched: &mut Array<Vector<T, W>>,
48 ) {
49 let space = comptime!(self.place.space.clone());
50 self.mem("fetch_from")
51 .fetch_straight(src.mem("fetch_from"), space, fetched);
52 }
53
54 #[allow(dead_code)] pub(crate) fn store_fetched<T: Numeric, W: Size>(&mut self, fetched: &Array<Vector<T, W>>) {
57 self.mem_mut("store_fetched").store_fetched(fetched);
58 }
59
60 pub fn view_mut<T: Numeric, W: Size>(&mut self) -> ViewMut<'_, Vector<T, W>, CoordsDyn> {
61 let g = self.mem_mut("view_mut");
62 g.window_view_mut::<T, W>(comptime!(Guard::Checked))
63 }
64}
65
66#[cube]
67impl Memory {
68 pub(crate) fn set_init_from(&mut self, #[comptime] init_from: InitFrom) {
70 comptime!({
71 self.init_from = init_from;
72 });
73 }
74
75 pub(crate) fn set_contraction(&mut self, #[comptime] contraction: Contraction) {
77 comptime!({
78 self.contraction = Some(contraction);
79 });
80 }
81
82 pub(crate) fn zero<T: Numeric>(&mut self) {
84 self.init::<T>(T::from_int(0));
85 }
86
87 pub(crate) fn init<T: Numeric>(&mut self, val: T) {
89 let size!(W) = comptime!(self.store.vector_size);
90 let mut d = self.flat_mut::<T, W>();
91 let total = d.shape();
92 for i in 0..total {
93 d.write(i, Vector::<T, W>::cast_from(val));
94 }
95 }
96
97 pub(crate) fn stage_element(&self) -> comptime_type!(StageElement) {
102 comptime!(match self.store.packing {
103 Packing::Plain => StageElement::Served,
104 Packing::Packed { field } if field.per_word() == 1 => StageElement::Decoded,
105 Packing::Packed { .. } => StageElement::Stored,
106 })
107 }
108
109 pub(crate) fn packing(&self) -> comptime_type!(Packing) {
111 comptime!(self.store.packing)
112 }
113
114 pub(crate) fn size_bytes<T: Numeric>(&self) -> u32 {
116 let wp = comptime!(self.store.packing.physical(self.store.vector_size) as u32);
117 match comptime!(self.store.packing) {
118 Packing::Plain => {
119 self.store.buffer::<T>().len() as u32 * T::size().comptime() as u32 * wp
120 }
121 Packing::Packed { field: _ } => {
122 self.store.buffer::<u32>().len() as u32 * u32::size().comptime() as u32 * wp
123 }
124 }
125 }
126
127 pub(crate) fn base(&self) -> BufferLayout {
129 self.layout.clone()
130 }
131
132 fn window(&self) -> Window {
133 self.window.clone()
134 }
135
136 fn extent(&self) -> Coords<u32> {
138 self.window.extent.clone()
139 }
140
141 fn lines<T: Numeric, W: Size>(&self) -> &[Vector<T, W>] {
143 self.store
144 .buffer::<T>()
145 .as_vectorized()
146 .with_vector_size::<W>()
147 }
148
149 fn lines_mut<T: Numeric, W: Size>(&mut self) -> &mut [Vector<T, W>] {
151 self.store
152 .buffer_mut::<T>()
153 .as_vectorized_mut()
154 .with_vector_size_mut::<W>()
155 }
156
157 fn write_view<T: Numeric, W: Size>(
159 &mut self,
160 layout: BufferLayout,
161 ) -> ViewMut<'_, Vector<T, W>, CoordsDyn> {
162 comptime!(assert!(
164 layout.paging.is_none(),
165 "Memory::write_view: a pool of pages is read through its table, never written \
166 through it; write the pool where it lies"
167 ));
168 match &mut self.store.backing {
169 Backing::Buffer(buffer) => buffer
170 .at_mut::<T>()
171 .as_vectorized_mut()
172 .with_vector_size_mut::<W>()
173 .view_mut(layout),
174 Backing::WriteCall(destination) => ViewMut::new::<
175 &mut ErasedTensor<T, WriteOnly>,
176 Coords1d,
177 >(destination.call_mut::<T>(), layout),
178 Backing::ReadCall(_) => panic!(
179 "Memory::write_view: this tile's backing is read through a call, which is \
180 read-only"
181 ),
182 }
183 }
184
185 pub(crate) fn read_view<T: Numeric, W: Size>(
187 &self,
188 layout: BufferLayout,
189 ) -> View<'_, Vector<T, W>, CoordsDyn> {
190 match &self.store.backing {
191 Backing::Buffer(buffer) => buffer
192 .at::<T>()
193 .as_vectorized()
194 .with_vector_size::<W>()
195 .view(layout),
196 Backing::ReadCall(producer) => {
197 View::new::<&ErasedTensor<T, ReadOnly>, Coords1d>(producer.call::<T>(), layout)
198 }
199 Backing::WriteCall(_) => panic!(
200 "Memory::read_view: this tile's backing is written through a call, and is never \
201 read"
202 ),
203 }
204 }
205
206 fn window_view<T: Numeric, W: Size>(
208 &self,
209 #[comptime] guard: Guard,
210 ) -> View<'_, Vector<T, W>, CoordsDyn> {
211 match comptime!(self.access.storage) {
212 WindowStorage::Contiguous => {
213 let start = self.window_start.cast::<usize>();
214 let all = self.lines::<T, W>();
215 all.slice(start, all.len()).view(self.contiguous_layout())
216 }
217 WindowStorage::Stored(_) => WindowedView::new(
218 self.read_view::<T, W>(self.base()),
219 self.window().with_guard(guard),
220 )
221 .view(),
222 }
223 }
224
225 pub(crate) fn window_view_storage<I: Numeric, WP: Size>(
227 &self,
228 #[comptime] guard: Guard,
229 ) -> View<'_, Vector<I, WP>, CoordsDyn> {
230 match comptime!(self.access.storage) {
231 WindowStorage::Contiguous => {
232 let start = self.window_start.cast::<usize>();
233 let all = self.lines_storage::<I, WP>();
234 all.slice(start, all.len()).view(self.contiguous_layout())
235 }
236 WindowStorage::Stored(_) => WindowedView::new(
237 self.lines_storage::<I, WP>().view(self.base()),
238 self.window().with_guard(guard),
239 )
240 .view(),
241 }
242 }
243
244 fn window_view_mut<T: Numeric, W: Size>(
246 &mut self,
247 #[comptime] guard: Guard,
248 ) -> ViewMut<'_, Vector<T, W>, CoordsDyn> {
249 match comptime!(self.access.storage) {
250 WindowStorage::Contiguous => {
251 let start = self.window_start.cast::<usize>();
252 let layout = self.contiguous_layout();
253 let all = self.lines_mut::<T, W>();
254 let len = all.len();
255 all.slice_mut(start, len).view_mut(layout)
256 }
257 WindowStorage::Stored(_) => {
258 let base = self.base();
259 let window = self.window().with_guard(guard);
260 WindowedViewMut::new(self.write_view::<T, W>(base), window).view_mut()
261 }
262 }
263 }
264
265 fn contiguous_layout(&self) -> BufferLayout {
267 comptime!(assert!(
268 !self.access.overhang.masks(),
269 "Memory: a window inside one storage tile reads unmasked; a storage-tiled tensor is \
270 padded to whole storage tiles"
271 ));
272 comptime!(self.layout.rows.assert_in_order(
273 "Memory::contiguous_layout",
274 "a window inside one storage tile is addressed as one run"
275 ));
276 let positional = comptime!(self.layout.projection.clone());
277 let rank = comptime!(positional.coordinate_rank());
278 let mut strides = Coords::<u32>::new();
279 #[unroll]
280 for c in 0..rank {
281 let axis = comptime!(positional.logical_axes()[c]);
282 let inner = comptime!(*positional.carriers(axis).last().unwrap());
283 strides.push(self.layout.physical_strides.at(inner));
284 }
285 BufferLayout {
286 physical_shape: self.window.extent.clone(),
287 physical_strides: strides,
288 projection: comptime!(Projection::direct(positional.logical_axes())),
289 rows: RowArrangement::InOrder,
290 paging: ComptimeOption::new_None(),
291 }
292 }
293
294 fn lines_storage<I: Numeric, W: Size>(&self) -> &[Vector<I, W>] {
296 self.store
297 .buffer::<I>()
298 .as_vectorized()
299 .with_vector_size::<W>()
300 }
301
302 pub(crate) fn lines_storage_mut<I: Numeric, W: Size>(&mut self) -> &mut [Vector<I, W>] {
304 self.store
305 .buffer_mut::<I>()
306 .as_vectorized_mut()
307 .with_vector_size_mut::<W>()
308 }
309
310 pub(crate) fn window_slice<T: Numeric>(&self) -> &[T] {
312 let offset = self.window_offset();
313 self.store
314 .buffer::<T>()
315 .slice(offset, self.store.buffer::<T>().len())
316 }
317
318 pub(crate) fn window_slice_mut<T: Numeric>(&mut self) -> &mut [T] {
320 let offset = self.window_offset();
321 let end = self.store.buffer::<T>().len();
322 self.store.buffer_mut::<T>().slice_mut(offset, end)
323 }
324
325 fn window_offset(&self) -> usize {
327 comptime!(assert!(
330 self.layout.paging.is_none(),
331 "Memory::window_offset: a pool of pages is read through its table one line at a \
332 time, never as one raw run; stage it"
333 ));
334 comptime!(assert!(
335 !self.access.overhang.masks(),
336 "Memory::window_offset: cmma cannot mask an overhang"
337 ));
338 match comptime!(self.access.storage) {
340 WindowStorage::Stored(Storage::Strided) => {}
341 WindowStorage::Contiguous => {}
342 WindowStorage::Stored(Storage::Tiled(Some(level))) => panic!(
343 "Memory::window_offset: this window sits above its storage tile (the tile of \
344 level {level}), spanning several, so it is not one contiguous region; descend \
345 through that level first, or read the operand through its layout"
346 ),
347 WindowStorage::Stored(Storage::Tiled(None)) => panic!(
348 "Memory::window_offset: this operand's storage tile is the tile of no level of \
349 the kernel's nest, so no window is known to lie inside one; read it through its \
350 layout, or stage it"
351 ),
352 }
353 comptime!(
355 self.layout
356 .rows
357 .assert_rows_are_runs("Memory::window_slice")
358 );
359 if comptime!(self.store.packing != Packing::Plain) {
361 panic!(
362 "Memory::window_slice: a packed store has no raw element window; a fragment \
363 load reads it through Tile::matrix_packed"
364 )
365 }
366 self.window_start.cast::<usize>()
367 }
368
369 pub(crate) fn row_stride_at(&self, #[comptime] row: usize) -> u32 {
372 let rank = comptime!(self.layout.projection.physical_rank());
373 let row = comptime!(
374 if self.projection.is_direct() && !self.layout.projection.is_tiled() {
375 row
376 } else {
377 rank - 2
378 }
379 );
380 self.layout
381 .physical_strides
382 .at(row)
383 .times(comptime!(self.store.vector_size as u32).runtime())
384 }
385
386 pub(crate) fn masked<T: Numeric, W: Size, C: Coordinates, L: TileLayout<C>>(
388 &self,
389 layout: L,
390 #[comptime] guard: Guard,
391 ) -> Masked<'_, Vector<T, W>, C> {
392 if comptime!(self.store.packing != Packing::Plain) {
393 panic!(
394 "Tile::matrix: a packed tile only serves values its read unpacks \
395 (Tile::matrix_packed)"
396 )
397 }
398 Masked::new(
399 self.window_view::<T, W>(guard).view(layout),
400 comptime!(guard.checks() && self.access.overhang.masks()),
401 )
402 }
403
404 fn write_check(&self) -> comptime_type!(bool) {
406 comptime!(assert!(
408 !self.window.boundaries.contains(&Some(Boundary::Clamp))
409 || !self.access.overhang.masks(),
410 "Memory: a Boundary::Clamp operand is read-only, a clamped write aliases the edge cell"
411 ));
412 comptime!(self.access.overhang.masks())
413 }
414
415 pub(crate) fn masked_mut<T: Numeric, W: Size, C: Coordinates, L: TileLayout<C>>(
417 &mut self,
418 layout: L,
419 ) -> MaskedMut<'_, Vector<T, W>, C> {
420 if comptime!(self.store.packing != Packing::Plain) {
421 panic!("Tile::matrix_mut: writing a packed tile requires repacking")
422 }
423 let check = self.write_check();
424 MaskedMut::new(
425 self.window_view_mut::<T, W>(comptime!(Guard::Checked))
426 .view_mut(layout),
427 check,
428 )
429 }
430
431 pub(crate) fn flat<T: Numeric, W: Size>(&self) -> FlatView<'_, Vector<T, W>> {
433 FlatView::new(
434 self.window_view::<T, W>(comptime!(Guard::Checked))
435 .view(FlatLayout::new(self.window.extent.clone())),
436 comptime!(self.access.overhang.masks()),
437 )
438 }
439
440 pub(crate) fn flat_unpacked<T: Numeric, WP: Size, W: Size>(
442 &self,
443 ) -> FlatView<'_, Vector<T, W>> {
444 self.unpacked::<T, WP, W, Coords1d, FlatLayout>(
445 FlatLayout::new(self.window.extent.clone()),
446 comptime!(Guard::Checked),
447 )
448 }
449
450 pub(crate) fn unpacked<
453 T: Numeric,
454 WP: Size,
455 W: Size,
456 C: Coordinates + 'static,
457 L: TileLayout<C>,
458 >(
459 &self,
460 layout: L,
461 #[comptime] guard: Guard,
462 ) -> Masked<'_, Vector<T, W>, C> {
463 let packing = self.packing();
464 match comptime!(packing) {
465 Packing::Plain => self.masked::<T, W, C, L>(layout, guard),
466 Packing::Packed { field } => {
467 let words = self
468 .window_view_storage::<u32, WP>(comptime!(Guard::Checked))
469 .view(layout);
470 let values = PackedView::<WP, T, W, C>::new(words, comptime!(field));
471 Masked::new(
472 values.view(),
473 comptime!(guard.checks() && self.access.overhang.masks()),
474 )
475 }
476 }
477 }
478
479 pub(crate) fn packed<T: Numeric, W: Size, C: Coordinates + 'static, L: TileLayout<C>>(
481 &self,
482 layout: L,
483 #[comptime] guard: Guard,
484 ) -> Masked<'_, Vector<T, W>, C> {
485 let packing = self.packing();
486 let physical = comptime!(packing.physical(self.store.vector_size));
487 match comptime!(packing) {
489 Packing::Plain => {
490 let size!(WP) = physical;
491 self.unpacked::<T, WP, W, C, L>(layout, guard)
492 }
493 Packing::Packed { field: _ } => {
494 let size!(WP) = physical;
495 self.unpacked::<T, WP, W, C, L>(layout, guard)
496 }
497 }
498 }
499
500 pub(crate) fn nd_words<WP: Size>(
502 &self,
503 layout: ProjectionInKernel,
504 #[comptime] guard: Guard,
505 ) -> Masked<'_, Vector<u32, WP>, CoordsDyn> {
506 comptime!(assert!(
507 matches!(self.store.packing, Packing::Packed { .. }),
508 "Memory::nd_words: only a packed store holds words"
509 ));
510 let words = self.window_view_storage::<u32, WP>(guard).view(layout);
511 Masked::new(
512 words,
513 comptime!(guard.checks() && self.access.overhang.masks()),
514 )
515 }
516
517 pub(crate) fn nd_words_mut<WP: Size>(
520 &mut self,
521 layout: ProjectionInKernel,
522 ) -> MaskedMut<'_, Vector<u32, WP>, CoordsDyn> {
523 comptime!(assert!(
524 matches!(self.store.packing, Packing::Packed { .. }),
525 "Memory::nd_words_mut: only a packed store holds words"
526 ));
527 let check = self.write_check();
528 MaskedMut::new(
529 self.window_view_mut::<u32, WP>(comptime!(Guard::Checked))
530 .view_mut(layout),
531 check,
532 )
533 }
534
535 pub(crate) fn physical_box(&self) -> CompactionStep {
538 let rank = comptime!(self.projection.physical_rank());
539 CompactionStep::new(self.window.extent.clone(), comptime!(vec![1; rank]))
540 }
541
542 pub(crate) fn batch_matrix(
544 &self,
545 #[comptime] space: Space,
546 #[comptime] axes: MatrixAxes,
547 i: usize,
548 ) -> ProjectedMatrix {
549 let bound = self.extent();
550 let load = self.vector_tile(&space);
551 ProjectedMatrix::new(
552 TileMatrix::batch(
553 &bound,
554 comptime!(&space),
555 comptime!(self.projection.composition()),
556 comptime!(&load),
557 axes,
558 i,
559 ),
560 self.axis_projection(comptime!(space.clone())),
561 )
562 }
563
564 pub(crate) fn whole_matrix(
566 &self,
567 #[comptime] space: Space,
568 #[comptime] rows: usize,
569 #[comptime] cols: usize,
570 ) -> ProjectedMatrix {
571 let vector_size = comptime!(self.store.vector_size);
572 ProjectedMatrix::new(
573 TileMatrix::whole(comptime!(&space), vector_size, rows, cols),
574 axis_projection(
575 comptime!(space),
576 comptime!(self.projection.clone()),
577 self.map.clone(),
578 vector_size,
579 ),
580 )
581 }
582
583 pub(crate) fn axis_projection(&self, #[comptime] space: Space) -> ProjectionInKernel {
586 let load = self.vector_tile(&space);
587 ProjectionInKernel::new(
588 Coords::constant(comptime!(load.counts(&space))),
589 self.map.clone(),
590 comptime!(space.clone()),
591 comptime!(self.projection.clone()),
592 comptime!(load.values()),
593 )
594 }
595
596 pub(crate) fn flat_mut<T: Numeric, W: Size>(&mut self) -> FlatViewMut<'_, Vector<T, W>> {
598 if comptime!(self.store.packing != Packing::Plain) {
599 panic!("Tile::flat_mut: writing a packed tile requires repacking")
600 }
601 let extent = self.window.extent.clone();
602 let check = self.write_check();
603 FlatViewMut::new(
604 self.window_view_mut::<T, W>(comptime!(Guard::Checked))
605 .view_mut(FlatLayout::new(extent)),
606 check,
607 )
608 }
609
610 pub(crate) fn matrix_mut<T: Numeric, W: Size>(
612 &mut self,
613 i: usize,
614 #[comptime] axes: MatrixAxes,
615 #[comptime] space: Space,
616 ) -> MatrixViewMut<'_, Vector<T, W>> {
617 comptime!(assert!(
619 self.projection.composition() != Composition::Overlapping,
620 "Memory::matrix_mut: an overlapping operand aliases under a write"
621 ));
622 let layout = self.batch_matrix(space, axes, i);
623 self.masked_mut::<T, W, Coords2d, ProjectedMatrix>(layout)
624 }
625
626 pub(crate) fn matrix_accumulate<T: Numeric, W: Size>(
628 &mut self,
629 i: usize,
630 #[comptime] axes: MatrixAxes,
631 #[comptime] space: Space,
632 #[comptime] monoid: Monoid,
633 ) -> AccumulateView<'_, T, W> {
634 let unit_share = comptime!(self.unit_share);
635 let split_share = comptime!(self.split_share);
636 let write = comptime!(self.access.write);
637 let init_from = comptime!(self.init_from);
638 AccumulateView::new(
639 self.matrix_mut::<T, W>(i, axes, space),
640 unit_share,
641 split_share,
642 write,
643 monoid,
644 init_from,
645 )
646 }
647
648 pub(crate) fn flat_accumulate<T: Numeric, W: Size>(
650 &mut self,
651 #[comptime] monoid: Monoid,
652 ) -> AccumulateView<'_, T, W, Coords1d> {
653 comptime!(assert!(
655 !self.layout.projection.is_tiled(),
656 "Memory::flat_accumulate: a storage-tiled window has no flat logical accumulator view"
657 ));
658 comptime!(assert!(
659 self.projection.is_direct(),
660 "Memory::flat_accumulate: a gathered window has no flat logical accumulator view"
661 ));
662 let unit_share = comptime!(self.unit_share);
663 let split_share = comptime!(self.split_share);
664 let write = comptime!(self.access.write);
665 let init_from = comptime!(self.init_from);
666 AccumulateView::new(
667 self.flat_mut::<T, W>(),
668 unit_share,
669 split_share,
670 write,
671 monoid,
672 init_from,
673 )
674 }
675
676 pub(crate) fn at(&self, step: &Step, #[comptime] space: Space) -> Memory {
678 let rank = comptime!(self.projection.physical_rank());
679 let (origin, extent, advances, map) = if comptime!(self.projection.is_direct()) {
680 self.direct_descent(step, comptime!(space.clone()))
681 } else {
682 self.gathered_descent(step)
683 };
684 let start = self
685 .window_start
686 .plus(advances.sum(comptime!((0..rank).collect::<Vec<_>>())));
687 let edges = comptime!(match self.projection.is_direct() {
690 true => {
691 let width = self.store.vector_size;
692 let load = VectorTile::new(
693 &self.store.stored_tiles,
694 space.axis_at(space.rank() - 1),
695 width,
696 )
697 .unwrap_or_else(|why| panic!("Memory: {width} values a load: {why}"));
698 Some(
699 (0..rank)
700 .map(|p| {
701 let axis = space.axis_at(p);
702 match step.level.extent_in(&space, axis) {
703 Extent::Dynamic => None,
704 edge => {
705 Some(load.loads_in(axis, edge.get(), space.extent_raw(axis)))
706 }
707 }
708 })
709 .collect::<Vec<_>>(),
710 )
711 }
712 false => None,
713 });
714 let cut = comptime!(match edges {
715 None => {
716 assert!(
717 self.layout.paging.is_none(),
718 "Memory::at: a pool of pages is read through a direct mapping only"
719 );
720 WindowCut::Shifted
722 }
723 Some(edges) => {
724 let nested = self.layout.paging.is_some()
727 && (0..rank).all(|p| {
728 let axis = space.axis_at(p);
729 edges[p].is_none()
730 || (matches!(space.extent_raw(axis), Extent::Static(_))
731 && !step.level.overhangs(&space, axis))
732 });
733 WindowCut::Level { edges, nested }
734 }
735 });
736
737 self.moved_to(
738 Window::new(
739 origin,
740 extent,
741 self.window.bound.clone(),
742 comptime!(self.window.signed),
743 comptime!(self.window.boundaries.clone()),
744 ),
745 comptime!(cut),
746 start,
747 map,
748 comptime!(Access {
750 whole: false,
751 overhang: self.access.overhang,
752 write: self.access.write,
753 fill: self.access.fill,
754 storage: self.access.storage.below(step.depth, &step.level, &space),
755 delivery: self.access.delivery,
756 }),
757 comptime!(UnitShare::new(&step.level, &space).under(self.unit_share)),
758 comptime!(SplitShare::new(&step.level, &step.space, &space).under(self.split_share)),
760 )
761 }
762
763 fn direct_descent(
765 &self,
766 step: &Step,
767 #[comptime] space: Space,
768 ) -> (Coords<i32>, Coords<u32>, Coords<u32>, RuntimeMap) {
769 let mut origin = Coords::<i32>::new();
770 let mut extent = Coords::<u32>::new();
771 let mut advances = Coords::<u32>::new();
772 let rank = comptime!(self.projection.physical_rank());
773 let load = self.vector_tile(&space);
774
775 #[unroll]
776 for p in 0..rank {
777 let axis = space.axis_at(p);
778 if comptime!(matches!(
780 step.level.extent_in(&space, axis),
781 Extent::Dynamic
782 )) {
783 origin.push(self.window.origin.at(p));
784 extent.push(self.window.extent.at(p));
785 advances.push(0u32);
786 } else {
787 let edge = comptime!(load.loads_in(
789 axis,
790 step.level.extent_in(&space, axis).get(),
791 space.extent_raw(axis)
792 ));
793 let index = step.coord(axis);
794 let from = self.window.origin.at(p);
795 let moved = index.times(edge).cast::<u32>();
796
797 origin.push(from.plus(moved.cast::<i32>()));
798 extent.push(comptime!(edge as u32).runtime());
799 let projection = comptime!(self.layout.projection.clone());
800 let coordinate = comptime!(Axis(p as u8));
801 let shape = &self.layout.physical_shape;
802 let strides = &self.layout.physical_strides;
803 let advance = if comptime!(projection.carriers(coordinate).len() > 1) {
807 let from = from.cast::<u32>();
808 axis_offset(
809 comptime!(projection.clone()),
810 coordinate,
811 from.plus(moved),
812 shape,
813 strides,
814 )
815 .minus(axis_offset(projection, coordinate, from, shape, strides))
816 } else {
817 index.cast::<u32>().times(axis_offset(
818 projection,
819 coordinate,
820 comptime!(edge as u32).runtime(),
821 shape,
822 strides,
823 ))
824 };
825 advances.push(advance);
826 }
827 }
828 (origin, extent, advances, RuntimeMap::integral(rank))
829 }
830
831 fn gathered_descent(&self, step: &Step) -> (Coords<i32>, Coords<u32>, Coords<u32>, RuntimeMap) {
833 let mut origin = Coords::<i32>::new();
834 let mut extent = Coords::<u32>::new();
835 let mut advances = Coords::<u32>::new();
836 let mut residues = Coords::<u32>::new();
837 let rank = comptime!(self.projection.physical_rank());
838 let w = comptime!(self.store.vector_size);
839
840 #[unroll]
841 for pa in 0..rank {
842 let (moved, residue, span) =
843 gathered_axis_descent(comptime!(self.projection.clone()), step, &self.map, w, pa);
844 origin.push(self.window.origin.at(pa).plus(moved.cast::<i32>()));
845 residues.push(residue);
846 extent.push(span);
847 advances.push(moved.times(self.layout.physical_strides.at(pa)));
849 }
850 let map = RuntimeMap {
851 coefficients: self.map.coefficients.clone(),
852 residues,
853 };
854 (origin, extent, advances, map)
855 }
856
857 #[allow(clippy::too_many_arguments)]
860 fn moved_to(
861 &self,
862 window: Window,
863 #[comptime] cut: WindowCut,
864 window_start: u32,
865 map: RuntimeMap,
866 #[comptime] access: Access,
867 #[comptime] unit_share: UnitShare,
868 #[comptime] split_share: SplitShare,
869 ) -> Memory {
870 let layout = self.layout.pinned_at(&window.origin, cut);
871 self.relaid(
872 layout,
873 window,
874 window_start,
875 map,
876 access,
877 unit_share,
878 split_share,
879 )
880 }
881
882 pub(crate) fn with_page_entry(&self, entry: &PageEntry) -> Memory {
885 self.relaid(
886 self.layout.with_page_entry(entry),
887 self.window.clone(),
888 self.window_start,
889 self.map.clone(),
890 comptime!(self.access.clone()),
891 comptime!(self.unit_share),
892 comptime!(self.split_share),
893 )
894 }
895
896 #[allow(clippy::too_many_arguments)]
898 fn relaid(
899 &self,
900 layout: BufferLayout,
901 window: Window,
902 window_start: u32,
903 map: RuntimeMap,
904 #[comptime] access: Access,
905 #[comptime] unit_share: UnitShare,
906 #[comptime] split_share: SplitShare,
907 ) -> Memory {
908 Memory {
909 address: comptime!(self.address),
910 store: self.store.clone(),
911 layout,
912 window,
913 projection: comptime!(self.projection.clone()),
914 source_window: self.source_window.clone(),
915 map,
916 offsets: self.offsets.clone(),
917 window_start,
918 access,
919 unit_share,
920 split_share,
921 init_from: comptime!(self.init_from),
922 contraction: comptime!(self.contraction),
923 codebook: self.codebook.clone(),
924 index: self.index.clone(),
925 }
926 }
927
928 pub(crate) fn filled_by(&self, #[comptime] holder: ComputeScope) -> Memory {
931 let fill = comptime!(self.access.fill.held_by(holder));
932 self.moved_to(
933 self.window.clone(),
934 comptime!(WindowCut::Unmoved),
935 self.window_start,
936 self.map.clone(),
937 comptime!(Access {
938 fill,
939 ..self.access
940 }),
941 comptime!(self.unit_share),
942 comptime!(self.split_share),
943 )
944 }
945
946 #[allow(dead_code)] pub(crate) fn placed_at(&self, #[comptime] axis: Axis, coord: u32) -> Memory {
950 let proj = comptime!(self.projection.clone());
951 let rank = comptime!(proj.physical_rank());
952 let at = comptime!(
953 placed_dim(&proj, axis)
954 .filter(|_| !self.layout.projection.is_tiled())
955 .unwrap_or_else(|| panic!("{}", Refusal::IndexOfAStatedLayout))
956 );
957
958 let mut origin = Coords::<i32>::new();
959 let mut extent = Coords::<u32>::new();
960 #[unroll]
961 for p in 0..rank {
962 if comptime!(p == at) {
963 origin.push(self.window.origin.at(p).plus(coord.cast::<i32>()));
964 extent.push(1u32.runtime());
965 } else {
966 origin.push(self.window.origin.at(p));
967 extent.push(self.window.extent.at(p));
968 }
969 }
970
971 let start = self.window_start.plus(coord.times(axis_offset(
972 comptime!(self.layout.projection.clone()),
973 comptime!(Axis(at as u8)),
974 1u32.runtime(),
975 &self.layout.physical_shape,
976 &self.layout.physical_strides,
977 )));
978
979 self.moved_to(
980 Window::new(
981 origin,
982 extent,
983 self.window.bound.clone(),
984 comptime!(self.window.signed),
985 comptime!(self.window.boundaries.clone()),
986 ),
987 comptime!(WindowCut::Shifted),
988 start,
989 self.map.clone(),
990 comptime!(Access {
991 whole: false,
992 ..self.access
993 }),
994 comptime!(self.unit_share),
995 comptime!(self.split_share),
996 )
997 }
998
999 pub(crate) fn within(&self, #[comptime] axis: Axis, from: usize, until: usize) -> Memory {
1002 let first = from.constant();
1003 let moved = comptime!(first != Some(0));
1004 let proj = comptime!(self.projection.clone());
1005 comptime!(assert!(
1006 proj.untiled().is_direct() && !proj.is_tiled(),
1007 "Memory::within: placing a window at an element needs a direct, untiled mapping"
1008 ));
1009 let rank = comptime!(proj.physical_rank());
1010 let at = comptime!(proj.position(axis));
1011
1012 let mut origin = Coords::<i32>::new();
1013 let mut bound = Coords::<u32>::new();
1014 #[unroll]
1015 for p in 0..rank {
1016 if comptime!(p == at) {
1017 origin.push(self.window.origin.at(p).plus(from.cast::<i32>()));
1018 bound.push(self.window.bound.at(p).min_with(until.cast::<u32>()));
1019 } else {
1020 origin.push(self.window.origin.at(p));
1021 bound.push(self.window.bound.at(p));
1022 }
1023 }
1024
1025 let start = self.window_start.plus(from.cast::<u32>().times(axis_offset(
1026 comptime!(self.layout.projection.clone()),
1027 comptime!(Axis(at as u8)),
1028 1u32.runtime(),
1029 &self.layout.physical_shape,
1030 &self.layout.physical_strides,
1031 )));
1032
1033 self.moved_to(
1034 Window::new(
1035 origin,
1036 self.window.extent.clone(),
1037 bound,
1038 comptime!(self.window.signed),
1039 comptime!({
1040 let mut boundaries = self.window.boundaries.clone();
1041 if boundaries.is_empty() {
1042 boundaries = (0..rank).map(|_| None).collect();
1043 }
1044 boundaries[at] = Some(Boundary::Zero);
1045 boundaries
1046 }),
1047 ),
1048 comptime!(match moved {
1049 true => WindowCut::Shifted,
1050 false => WindowCut::Unmoved,
1051 }),
1052 start,
1053 self.map.clone(),
1054 comptime!(Access {
1057 whole: false,
1058 overhang: match self.access.overhang {
1059 Overhang::Never | Overhang::Fits => Overhang::Run,
1060 overhang => overhang,
1061 },
1062 write: self.access.write,
1063 fill: self.access.fill,
1064 storage: self.access.storage,
1065 delivery: self.access.delivery,
1066 }),
1067 comptime!(self.unit_share),
1068 comptime!(self.split_share),
1069 )
1070 }
1071}
1072
1073#[cube]
1075fn gathered_axis_descent(
1076 #[comptime] projection: Projection,
1077 cut: &Step,
1078 map: &RuntimeMap,
1079 #[comptime] vector_size: usize,
1080 #[comptime] pa: usize,
1081) -> (u32, u32, u32) {
1082 let axis_map = comptime!(projection.physical_axis(pa));
1083 let n = comptime!(axis_map.terms().len());
1084 let picks = comptime!((0..n).collect::<Vec<_>>());
1085 let lined = comptime!(pa == projection.physical_rank() - 1);
1086
1087 let mut terms = Coords::<u32>::new();
1088 let mut spans = Coords::<u32>::new();
1090 #[unroll]
1091 for t in 0..n {
1092 let term = comptime!(axis_map.terms()[t]);
1093 let edge = comptime!(cut.level.extent_in(&cut.space, term.axis).get());
1094 match comptime!(term.scale) {
1095 Scale::Static(s) => {
1096 let step = comptime!(if lined {
1097 assert!(
1098 (edge * s).is_multiple_of(vector_size)
1099 || matches!(cut.space.extent_raw(term.axis), Extent::Static(x) if x == edge),
1100 "Memory::at: the innermost edge {edge} of {:?} is neither a whole number \
1101 of {vector_size}-wide lines nor the axis's whole extent, so a step would \
1102 start mid-line",
1103 term.axis
1104 );
1105 edge * s / vector_size
1106 } else {
1107 edge * s
1108 });
1109 terms.push(cut.coord(term.axis).times(step).cast::<u32>());
1110 spans.push(comptime!(((edge - 1) * s) as u32).runtime());
1111 }
1112 Scale::Dynamic { .. } => {
1114 let coefficient = map
1115 .coefficients
1116 .at(comptime!(projection.dynamic_scale_index(pa, t).unwrap()));
1117 terms.push(
1118 cut.coord(term.axis)
1119 .cast::<u32>()
1120 .times(comptime!(edge as u32).runtime())
1121 .times(coefficient),
1122 );
1123 spans.push(comptime!((edge - 1) as u32).runtime().times(coefficient));
1124 }
1125 }
1126 }
1127 let advance = terms.sum(comptime!(picks.clone()));
1128
1129 if comptime!(!axis_map.is_rational()) {
1130 let span = if comptime!(!axis_map.has_dynamic_scale()) {
1131 comptime!({
1132 let s = projection.span(pa, |a| cut.level.extent_in(&cut.space, a).get());
1133 (if lined { s / vector_size } else { s }) as u32
1134 })
1135 .runtime()
1136 } else {
1137 spans.sum(comptime!(picks.clone())).plus(1)
1138 };
1139 (advance, 0u32, span)
1140 } else {
1141 let numerator = advance.plus(map.residues.at(pa));
1143 let field = spans.sum(comptime!(picks.clone()));
1144 match comptime!(axis_map.divisor()) {
1145 Divisor::Static(d) => {
1146 let d = comptime!(d as u32);
1147 let residue = numerator.remainder(d);
1148 (
1149 numerator.divided_by(d),
1150 residue,
1151 field.plus(residue).divided_by(d).plus(1),
1152 )
1153 }
1154 Divisor::Dynamic { .. } => {
1155 let d = map
1156 .coefficients
1157 .at(comptime!(projection.dynamic_divisor_index(pa).unwrap()));
1158 let residue = numerator.remainder(d);
1159 (
1160 numerator.divided_by(d),
1161 residue,
1162 field.plus(residue).divided_by(d).plus(1),
1163 )
1164 }
1165 }
1166 }
1167}