1use cubecl::frontend::branch::{if_else_expand, if_expand};
5use cubecl::ir::Scope;
6use cubecl::prelude::*;
7use cubecl::unexpanded;
8
9use super::payload::base::{Payload, PayloadExpand};
10use crate::*;
11
12pub trait StagesFill {
14 fn has_fixed(&self, scope: &Scope) -> bool;
16 fn fill_fixed(&mut self, scope: &Scope, slot: usize, region: &RegionExpand);
18 fn fill_streamed(&mut self, scope: &Scope, slot: usize, region: &RegionExpand);
20 fn fill_inside_run(&mut self, scope: &Scope, slot: usize, region: &RegionExpand);
23 fn publish(&mut self, scope: &Scope, slot: usize);
25}
26
27impl<T: CubeType> Stages<T> {
28 pub fn pipelined<F>(&mut self, _walk: Walk, _compute: F)
31 where
32 F: FnMut(&mut Slot<T>, &Region),
33 {
34 unexpanded!()
35 }
36}
37
38impl<T: CubeType> StagesExpand<T>
39where
40 StagesExpand<T>: StagesFill,
41{
42 pub fn __expand_pipelined_method<F>(&mut self, scope: &Scope, walk: WalkExpand, compute: F)
43 where
44 F: FnMut(&Scope, &mut SlotExpand<T>, &RegionExpand),
45 {
46 shared::run(
47 scope,
48 walk,
49 self,
50 |scope, stages, slot, region| stages.fill_streamed(scope, slot, region),
51 compute,
52 )
53 }
54}
55
56#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
58pub enum Prefetch {
59 InSlots,
61 InRegisters,
64}
65
66impl Prefetch {
67 pub fn scalars(elements: usize, units: usize, width: usize) -> usize {
69 UnitLines::new(elements.div_ceil(width), units).scalars(width)
70 }
71
72 pub fn fits(scalars: usize) -> bool {
74 scalars <= MOST_FETCHED_SCALARS
75 }
76}
77
78impl Stages<OperandPair> {
79 pub fn walk<F>(&mut self, _walk: Walk, _prefetch: Prefetch, _compute: F)
81 where
82 F: FnMut(&mut Slot<OperandPair>, &Region),
83 {
84 unexpanded!()
85 }
86}
87
88impl StagesExpand<OperandPair> {
89 pub fn __expand_walk_method<F>(
90 &mut self,
91 scope: &Scope,
92 walk: WalkExpand,
93 prefetch: Prefetch,
94 compute: F,
95 ) where
96 F: FnMut(&Scope, &mut SlotExpand<OperandPair>, &RegionExpand),
97 {
98 match prefetch {
99 Prefetch::InSlots => self.__expand_pipelined_method(scope, walk, compute),
100 Prefetch::InRegisters => self.__expand_prefetched_method(scope, walk, compute),
101 }
102 }
103}
104
105impl Stages<OperandPair> {
106 pub fn prefetched<F>(&mut self, _walk: Walk, _compute: F)
113 where
114 F: FnMut(&mut Slot<OperandPair>, &Region),
115 {
116 unexpanded!()
117 }
118}
119
120impl StagesExpand<OperandPair> {
121 pub fn __expand_prefetched_method<F>(&mut self, scope: &Scope, walk: WalkExpand, compute: F)
122 where
123 F: FnMut(&Scope, &mut SlotExpand<OperandPair>, &RegionExpand),
124 {
125 let scales_beside = self.sources.lhs.__expand_scales_beside_method(scope)
128 || self.sources.rhs.__expand_scales_beside_method(scope);
129 if self.has_fixed(scope) || scales_beside {
130 return self.__expand_pipelined_method(scope, walk, compute);
131 }
132 self.__expand_assert_copied_by_every_unit_method(scope);
133 assert!(
134 self.depth <= 2,
135 "Stages::prefetched: {} slots hold one region ahead, so the slots past a second \
136 would sit idle; build them with a depth of 1 or 2",
137 self.depth
138 );
139 let total = walk.__expand_total_method(scope);
140 self.prime(scope, &walk, &total);
141 self.prefetching_laps(scope, walk, total, compute);
142 }
143
144 fn prime(&mut self, scope: &Scope, walk: &WalkExpand, total: &NativeExpand<usize>) {
146 let any = 0usize.into_expand(scope).__expand_lt_method(scope, total);
147 if_expand(scope, any, |scope| {
148 let first = walk.__expand_region_method(scope, FIRST_SLOT.into_expand(scope));
149 self.fill_streamed(scope, FIRST_SLOT, &first);
150 self.publish(scope, FIRST_SLOT);
151 });
152 }
153
154 fn steady_prefetching_laps<Lhs: Numeric, Rhs: Numeric, F>(
158 &mut self,
159 scope: &Scope,
160 walk: &WalkExpand,
161 total: NativeExpand<usize>,
162 lhs: &mut <Array<Vector<Lhs, super::base::FL>> as CubeType>::ExpandType,
163 rhs: &mut <Array<Vector<Rhs, super::base::FR>> as CubeType>::ExpandType,
164 compute: &mut F,
165 ) -> NativeExpand<usize>
166 where
167 F: FnMut(&Scope, &mut SlotExpand<OperandPair>, &RegionExpand),
168 {
169 let depth = self.depth;
173 let (held_back, sources) = match walk.run {
174 None => (0, self.sources.clone()),
175 Some(_) => (1, self.sources.__expand_inside_runs_method(scope)),
176 };
177 let steady = steady_laps(scope, total, 1 + held_back, depth);
178 let mut steady_body = |scope: &Scope, lap: NativeExpand<usize>| {
179 for j in 0..depth {
180 let target = (j + 1) % depth;
181 let region_idx = lap
182 .__expand_times_method(scope, depth.into_expand(scope))
183 .__expand_plus_method(scope, j.into_expand(scope));
184 let next = region_idx.__expand_plus_method(scope, 1usize.into_expand(scope));
185 let upcoming = walk.__expand_region_method(scope, next);
186 self.__expand_fetch_method(scope, target, &sources, &upcoming, lhs, rhs);
187 let region = walk.__expand_region_method(scope, region_idx);
188 let slot = self.__expand_slot_mut_method(scope, j);
189 compute(scope, slot, ®ion);
190 if depth == 1 {
192 self.publish(scope, FIRST_SLOT);
193 }
194 self.__expand_store_method(scope, target, lhs, rhs);
195 self.publish(scope, target);
196 }
197 };
198 run_laps(
199 scope,
200 0usize.into_expand(scope),
201 steady,
202 walk.unroll,
203 &mut steady_body,
204 );
205 steady
206 }
207
208 fn prefetching_laps<F>(
210 &mut self,
211 scope: &Scope,
212 walk: WalkExpand,
213 total: NativeExpand<usize>,
214 mut compute: F,
215 ) where
216 F: FnMut(&Scope, &mut SlotExpand<OperandPair>, &RegionExpand),
217 {
218 let depth = self.depth;
219 define_scalar!(Lhs);
221 define_scalar!(Rhs);
222 let _lhs = scope.define_type::<Lhs>(self.sources.lhs.element);
223 let _rhs = scope.define_type::<Rhs>(self.sources.rhs.element);
224 let (mut lhs, mut rhs) = self.__expand_fetch_buffers_method::<Lhs, Rhs>(scope);
225 let steady =
226 self.steady_prefetching_laps(scope, &walk, total, &mut lhs, &mut rhs, &mut compute);
227 let mut body = |scope: &Scope, lap: NativeExpand<usize>| {
229 for j in 0..depth {
230 let target = (j + 1) % depth;
231 let region_idx = lap
232 .__expand_times_method(scope, depth.into_expand(scope))
233 .__expand_plus_method(scope, j.into_expand(scope));
234 let next = region_idx.__expand_plus_method(scope, 1usize.into_expand(scope));
235 let in_walk = region_idx.__expand_lt_method(scope, &total);
236 if_expand(scope, in_walk, |scope| {
237 let prefetching = next.__expand_lt_method(scope, &total);
238 if_expand(scope, prefetching, |scope| {
239 let upcoming = walk.__expand_region_method(scope, next);
240 let sources = self.sources.clone();
241 self.__expand_fetch_method(
242 scope, target, &sources, &upcoming, &mut lhs, &mut rhs,
243 );
244 });
245 let region = walk.__expand_region_method(scope, region_idx);
246 let slot = self.__expand_slot_mut_method(scope, j);
247 compute(scope, slot, ®ion);
248 if_expand(scope, prefetching, |scope| {
249 if depth == 1 {
251 self.publish(scope, FIRST_SLOT);
252 }
253 self.__expand_store_method(scope, target, &lhs, &rhs);
254 self.publish(scope, target);
255 });
256 });
257 }
258 };
259 match walk.run {
264 None => body(scope, steady),
265 Some(_) => {
266 let laps = total
267 .__expand_plus_method(scope, (depth - 1).into_expand(scope))
268 .__expand_divided_by_method(scope, depth.into_expand(scope));
269 run_laps(scope, steady, laps, walk.unroll, &mut body);
270 }
271 }
272 }
273}
274
275fn steady_laps(
279 scope: &Scope,
280 total: NativeExpand<usize>,
281 ahead: usize,
282 depth: usize,
283) -> NativeExpand<usize> {
284 total
285 .__expand_max_with_method(scope, ahead.into_expand(scope))
286 .__expand_minus_method(scope, ahead.into_expand(scope))
287 .__expand_divided_by_method(scope, depth.into_expand(scope))
288}
289
290fn run_laps(
292 scope: &Scope,
293 from: NativeExpand<usize>,
294 to: NativeExpand<usize>,
295 unroll: bool,
296 body: &mut dyn FnMut(&Scope, NativeExpand<usize>),
297) {
298 let range = RangeExpand::new(from, to);
299 if unroll {
300 range.expand_unroll(scope, body);
301 } else {
302 range.expand(scope, body);
303 }
304}
305
306impl<P: Payload<P> + CubeType<ExpandType: Clone>> StagesFill for StagesExpand<P> {
307 fn has_fixed(&self, scope: &Scope) -> bool {
308 self.slots
309 .__expand_index_method(scope, FIRST_SLOT.into_expand(scope))
310 .__expand_has_fixed_method(scope)
311 }
312
313 fn fill_fixed(&mut self, scope: &Scope, slot: usize, region: &RegionExpand) {
314 let sources = self.sources.clone();
315 self.__expand_slot_mut_method(scope, slot)
316 .__expand_refill_method(scope, &sources, region, Refill::Once);
317 }
318
319 fn fill_streamed(&mut self, scope: &Scope, slot: usize, region: &RegionExpand) {
320 let sources = self.sources.clone();
321 self.__expand_slot_mut_method(scope, slot)
322 .__expand_refill_method(scope, &sources, region, Refill::EveryRegion);
323 }
324
325 fn fill_inside_run(&mut self, scope: &Scope, slot: usize, region: &RegionExpand) {
326 let sources = self.sources.__expand_inside_runs_method(scope);
327 self.__expand_slot_mut_method(scope, slot)
328 .__expand_refill_method(scope, &sources, region, Refill::EveryRegion);
329 }
330
331 fn publish(&mut self, scope: &Scope, slot: usize) {
332 self.__expand_slot_mut_method(scope, slot)
333 .__expand_publish_method(scope);
334 }
335}
336
337mod shared {
339 use super::*;
340
341 pub(super) fn run<T: CubeType, Fill, F>(
342 scope: &Scope,
343 walk: WalkExpand,
344 stages: &mut StagesExpand<T>,
345 mut fill: Fill,
346 mut compute: F,
347 ) where
348 StagesExpand<T>: StagesFill,
349 Fill: FnMut(&Scope, &mut StagesExpand<T>, usize, &RegionExpand),
350 F: FnMut(&Scope, &mut SlotExpand<T>, &RegionExpand),
351 {
352 let depth = stages.depth;
353 let unroll = walk.unroll;
354 let total = walk.__expand_total_method(scope);
355
356 if stages.has_fixed(scope) {
357 let first = walk.__expand_region_method(scope, FIRST_SLOT.into_expand(scope));
358 for slot in 0..depth {
359 stages.fill_fixed(scope, slot, &first);
360 }
361 }
362
363 for slot in 0..depth - 1 {
364 let index = slot.into_expand(scope);
365 let cond = index.__expand_lt_method(scope, &total);
366 if_expand(scope, cond, |scope| {
367 let region = walk.__expand_region_method(scope, slot.into_expand(scope));
368 fill(scope, stages, slot, ®ion);
369 });
370 }
371
372 let laps = total
373 .__expand_plus_method(scope, (depth - 1).into_expand(scope))
374 .__expand_divided_by_method(scope, depth.into_expand(scope));
375 let steady = match walk.run {
378 None => steady_ahead_laps(scope, &walk, stages, total, 0, &mut fill, &mut compute),
379 Some(_) => steady_ahead_laps(
380 scope,
381 &walk,
382 stages,
383 total,
384 1,
385 &mut |scope, stages: &mut StagesExpand<T>, slot, region: &RegionExpand| {
386 stages.fill_inside_run(scope, slot, region)
387 },
388 &mut compute,
389 ),
390 };
391 let mut body = |scope: &Scope, lap: NativeExpand<usize>| {
392 for j in 0..depth {
393 let region_idx = lap
394 .__expand_times_method(scope, depth.into_expand(scope))
395 .__expand_plus_method(scope, j.into_expand(scope));
396 if depth == 1 {
397 let region = walk.__expand_region_method(scope, region_idx);
398 fill(scope, stages, FIRST_SLOT, ®ion);
399 stages.publish(scope, FIRST_SLOT);
400 let slot = stages.__expand_slot_mut_method(scope, FIRST_SLOT);
401 compute(scope, slot, ®ion);
402 } else {
403 let ahead =
404 region_idx.__expand_plus_method(scope, (depth - 1).into_expand(scope));
405 let prefetching = ahead.__expand_lt_method(scope, &total);
406 let in_walk = region_idx.__expand_lt_method(scope, &total);
407 if_else_expand(scope, prefetching, |scope| {
409 let prefetch = walk.__expand_region_method(scope, ahead);
410 fill(scope, stages, (j + depth - 1) % depth, &prefetch);
411 })
412 .or_else(scope, |scope| {
413 if_expand(scope, in_walk, |scope| {
415 stages.publish(scope, j);
416 });
417 });
418 if_expand(scope, in_walk, |scope| {
419 let region = walk.__expand_region_method(scope, region_idx);
420 let slot = stages.__expand_slot_mut_method(scope, j);
421 compute(scope, slot, ®ion);
422 });
423 }
424 }
425 };
426 run_laps(scope, steady, laps, unroll, &mut body);
427 }
428
429 fn steady_ahead_laps<T: CubeType, Fill, F>(
435 scope: &Scope,
436 walk: &WalkExpand,
437 stages: &mut StagesExpand<T>,
438 total: NativeExpand<usize>,
439 held_back: usize,
440 fill: &mut Fill,
441 compute: &mut F,
442 ) -> NativeExpand<usize>
443 where
444 StagesExpand<T>: StagesFill,
445 Fill: FnMut(&Scope, &mut StagesExpand<T>, usize, &RegionExpand),
446 F: FnMut(&Scope, &mut SlotExpand<T>, &RegionExpand),
447 {
448 let depth = stages.depth;
449 let ahead = depth - 1 + held_back;
450 if ahead == 0 {
451 return 0usize.into_expand(scope);
452 }
453 let steady = steady_laps(scope, total, ahead, depth);
454 let mut steady_body = |scope: &Scope, lap: NativeExpand<usize>| {
455 for j in 0..depth {
456 let region_idx = lap
457 .__expand_times_method(scope, depth.into_expand(scope))
458 .__expand_plus_method(scope, j.into_expand(scope));
459 let ahead = region_idx.__expand_plus_method(scope, (depth - 1).into_expand(scope));
460 let prefetch = walk.__expand_region_method(scope, ahead);
461 fill(scope, stages, (j + depth - 1) % depth, &prefetch);
462 if depth == 1 {
464 stages.publish(scope, FIRST_SLOT);
465 }
466 let region = walk.__expand_region_method(scope, region_idx);
467 let slot = stages.__expand_slot_mut_method(scope, j);
468 compute(scope, slot, ®ion);
469 }
470 };
471 run_laps(
472 scope,
473 0usize.into_expand(scope),
474 steady,
475 walk.unroll,
476 &mut steady_body,
477 );
478 steady
479 }
480}