1#![allow(non_snake_case)]
18
19use core::ops::BitAnd;
20
21use teeny_core::dtype::Num;
22use teeny_macros::kernel;
23use teeny_triton::triton::{
24 types::{AddOffsets, Comparison, Tensor},
25 *,
26};
27
28#[kernel]
44pub fn conv2d_forward<
45 T: Triton,
46 D: Num,
47 const KH: i32,
48 const KW: i32,
49 const STRIDE_H: i32,
50 const STRIDE_W: i32,
51 const PAD_H: i32,
52 const PAD_W: i32,
53 const G: i32,
54 const BLOCK_OW: i32,
55>(
56 x_ptr: T::Pointer<D>,
57 w_ptr: T::Pointer<D>,
58 y_ptr: T::Pointer<D>,
59 _B: i32,
60 C_IN: i32,
61 C_OUT: i32,
62 H: i32,
63 W: i32,
64 OH: i32,
65 OW: i32,
66) where
67 T::I32Tensor: Tensor<i32, 1>,
68 T::I32Tensor: Comparison<i32, BoolTensor = T::BoolTensor>,
69 T::BoolTensor: BitAnd<Output = T::BoolTensor>,
70 T::Pointer<D>: AddOffsets<i32, 1, T::I32Tensor, Output = T::Tensor<T::Pointer<D>>>,
71{
72 let pid = T::program_id(Axis::X);
73 let num_ow_tiles = T::cdiv(OW, BLOCK_OW);
74
75 let ow_tile = pid % num_ow_tiles;
77 let bco = pid / num_ow_tiles;
78 let oh = bco % OH;
79 let bc = bco / OH;
80 let c_out = bc % C_OUT;
81 let b = bc / C_OUT;
82
83 let ow_start = ow_tile * BLOCK_OW;
84 let ow_range = T::arange(0, BLOCK_OW) + ow_start;
85 let ow_mask = ow_range.lt(OW);
86
87 let out_bc_base = (b * C_OUT + c_out) * OH * OW;
88
89 let c_in_per_group = C_IN / G;
91 let g_idx = c_out / (C_OUT / G);
92 let c_in_start = g_idx * c_in_per_group;
93
94 let mut acc = T::zeros::<D>(&[BLOCK_OW]);
95
96 let loop_bound = c_in_per_group * KH * KW;
98 for idx in 0..loop_bound {
99 let kw = idx % KW;
100 let kh_cin = idx / KW;
101 let kh = kh_cin % KH;
102 let c_in_local = kh_cin / KH; let c_in = c_in_start + c_in_local; let ih = oh * STRIDE_H + kh - PAD_H;
107 let iw_range = ow_range * STRIDE_W + kw - PAD_W;
108
109 #[allow(clippy::erasing_op)]
112 let ih_t = ow_range * 0 + ih;
113 let h_in_bounds = ih_t.ge(0) & ih_t.lt(H);
114 let w_in_bounds = iw_range.ge(0) & iw_range.lt(W);
115 let load_mask = ow_mask & h_in_bounds & w_in_bounds;
116
117 let x_offsets = iw_range + ((b * C_IN + c_in) * H * W + ih * W);
118 let x_tile = T::load(
119 x_ptr.add_offsets(x_offsets),
120 Some(load_mask),
121 Some(T::zeros::<D>(&[BLOCK_OW])),
122 &[],
123 None,
124 None,
125 None,
126 false,
127 );
128
129 let w_idx = ((c_out * c_in_per_group + c_in_local) * KH + kh) * KW + kw;
131 let w_off = T::arange(0, 1) + w_idx;
132 let w_1 = T::load(
133 w_ptr.add_offsets(w_off),
134 None,
135 None,
136 &[],
137 None,
138 None,
139 None,
140 false,
141 );
142 let w_tile = T::broadcast_to(w_1, &[BLOCK_OW]);
143
144 acc = acc + x_tile * w_tile;
145 }
146
147 let out_offsets = ow_range + (out_bc_base + oh * OW);
148 T::store(
149 y_ptr.add_offsets(out_offsets),
150 acc,
151 Some(ow_mask),
152 &[],
153 None,
154 None,
155 );
156}
157
158#[kernel]
172pub fn conv2d_backward_dx<
173 T: Triton,
174 D: Num,
175 const KH: i32,
176 const KW: i32,
177 const STRIDE_H: i32,
178 const STRIDE_W: i32,
179 const PAD_H: i32,
180 const PAD_W: i32,
181 const G: i32,
182 const BLOCK_OW: i32,
183>(
184 dy_ptr: T::Pointer<D>,
185 w_ptr: T::Pointer<D>,
186 dx_ptr: T::Pointer<D>,
187 _B: i32,
188 C_IN: i32,
189 C_OUT: i32,
190 H: i32,
191 W: i32,
192 OH: i32,
193 OW: i32,
194) where
195 T::I32Tensor: Tensor<i32, 1>,
196 T::I32Tensor: Comparison<i32, BoolTensor = T::BoolTensor>,
197 T::BoolTensor: BitAnd<Output = T::BoolTensor>,
198 T::Pointer<D>: AddOffsets<i32, 1, T::I32Tensor, Output = T::Tensor<T::Pointer<D>>>,
199{
200 let pid = T::program_id(Axis::X);
201 let num_ow_tiles = T::cdiv(OW, BLOCK_OW);
202
203 let ow_tile = pid % num_ow_tiles;
204 let bco = pid / num_ow_tiles;
205 let oh = bco % OH;
206 let bc = bco / OH;
207 let c_out = bc % C_OUT;
208 let b = bc / C_OUT;
209
210 let ow_start = ow_tile * BLOCK_OW;
211 let ow_range = T::arange(0, BLOCK_OW) + ow_start;
212 let ow_mask = ow_range.lt(OW);
213
214 let dy_offsets = ow_range + ((b * C_OUT + c_out) * OH * OW + oh * OW);
216 let dy_tile = T::load(
217 dy_ptr.add_offsets(dy_offsets),
218 Some(ow_mask),
219 Some(T::zeros::<D>(&[BLOCK_OW])),
220 &[],
221 None,
222 None,
223 None,
224 false,
225 );
226
227 let c_in_per_group = C_IN / G;
229 let g_idx = c_out / (C_OUT / G);
230 let c_in_start = g_idx * c_in_per_group;
231
232 let loop_bound = c_in_per_group * KH * KW;
234 for idx in 0..loop_bound {
235 let kw = idx % KW;
236 let kh_cin = idx / KW;
237 let kh = kh_cin % KH;
238 let c_in_local = kh_cin / KH;
239 let c_in = c_in_start + c_in_local;
240
241 let w_idx = ((c_out * c_in_per_group + c_in_local) * KH + kh) * KW + kw;
242 let w_off = T::arange(0, 1) + w_idx;
243 let w_1 = T::load(
244 w_ptr.add_offsets(w_off),
245 None,
246 None,
247 &[],
248 None,
249 None,
250 None,
251 false,
252 );
253 let w_tile = T::broadcast_to(w_1, &[BLOCK_OW]);
254
255 let grad_tile = dy_tile * w_tile;
256
257 let ih = oh * STRIDE_H + kh - PAD_H;
258 let iw_range = ow_range * STRIDE_W + kw - PAD_W;
259
260 #[allow(clippy::erasing_op)]
261 let ih_t = ow_range * 0 + ih;
262 let h_in_bounds = ih_t.ge(0) & ih_t.lt(H);
263 let w_in_bounds = iw_range.ge(0) & iw_range.lt(W);
264
265 let dx_offsets = iw_range + ((b * C_IN + c_in) * H * W + ih * W);
266 T::atomic_add(
267 dx_ptr.add_offsets(dx_offsets),
268 grad_tile,
269 Some(ow_mask & h_in_bounds & w_in_bounds),
270 None,
271 None,
272 );
273 }
274}
275
276#[kernel]
287pub fn conv2d_backward_dw<
288 T: Triton,
289 D: Num,
290 const KH: i32,
291 const KW: i32,
292 const STRIDE_H: i32,
293 const STRIDE_W: i32,
294 const PAD_H: i32,
295 const PAD_W: i32,
296 const G: i32,
297 const BLOCK_OW: i32,
298>(
299 dy_ptr: T::Pointer<D>,
300 x_ptr: T::Pointer<D>,
301 dw_ptr: T::Pointer<D>,
302 _B: i32,
303 C_IN: i32,
304 C_OUT: i32,
305 H: i32,
306 W: i32,
307 OH: i32,
308 OW: i32,
309) where
310 T::I32Tensor: Tensor<i32, 1>,
311 T::I32Tensor: Comparison<i32, BoolTensor = T::BoolTensor>,
312 T::BoolTensor: BitAnd<Output = T::BoolTensor>,
313 T::Pointer<D>: AddOffsets<i32, 1, T::I32Tensor, Output = T::Tensor<T::Pointer<D>>>,
314{
315 let pid = T::program_id(Axis::X);
316 let num_ow_tiles = T::cdiv(OW, BLOCK_OW);
317
318 let ow_tile = pid % num_ow_tiles;
319 let bco = pid / num_ow_tiles;
320 let oh = bco % OH;
321 let bc = bco / OH;
322 let c_out = bc % C_OUT;
323 let b = bc / C_OUT;
324
325 let ow_start = ow_tile * BLOCK_OW;
326 let ow_range = T::arange(0, BLOCK_OW) + ow_start;
327 let ow_mask = ow_range.lt(OW);
328
329 let dy_offsets = ow_range + ((b * C_OUT + c_out) * OH * OW + oh * OW);
331 let dy_tile = T::load(
332 dy_ptr.add_offsets(dy_offsets),
333 Some(ow_mask),
334 Some(T::zeros::<D>(&[BLOCK_OW])),
335 &[],
336 None,
337 None,
338 None,
339 false,
340 );
341
342 let ih_base = oh * STRIDE_H;
343
344 let c_in_per_group = C_IN / G;
346 let g_idx = c_out / (C_OUT / G);
347 let c_in_start = g_idx * c_in_per_group;
348
349 let loop_bound = c_in_per_group * KH * KW;
351 for idx in 0..loop_bound {
352 let kw = idx % KW;
353 let kh_cin = idx / KW;
354 let kh = kh_cin % KH;
355 let c_in_local = kh_cin / KH;
356 let c_in = c_in_start + c_in_local;
357
358 let ih = ih_base + kh - PAD_H;
359 let iw_range = ow_range * STRIDE_W + kw - PAD_W;
360
361 #[allow(clippy::erasing_op)]
362 let ih_t = ow_range * 0 + ih;
363 let h_in_bounds = ih_t.ge(0) & ih_t.lt(H);
364 let w_in_bounds = iw_range.ge(0) & iw_range.lt(W);
365 let load_mask = ow_mask & h_in_bounds & w_in_bounds;
366
367 let x_offsets = iw_range + ((b * C_IN + c_in) * H * W + ih * W);
368 let x_tile = T::load(
369 x_ptr.add_offsets(x_offsets),
370 Some(load_mask),
371 Some(T::zeros::<D>(&[BLOCK_OW])),
372 &[],
373 None,
374 None,
375 None,
376 false,
377 );
378
379 let partial = T::sum(dy_tile * x_tile, Some(0), false);
381 let partial_1 = T::expand_dims(partial, 0);
382
383 let w_idx = ((c_out * c_in_per_group + c_in_local) * KH + kh) * KW + kw;
384 let dw_off = T::arange(0, 1) + w_idx;
385 T::atomic_add(dw_ptr.add_offsets(dw_off), partial_1, None, None, None);
386 }
387}
388
389#[kernel]
400pub fn conv2d_backward<
401 T: Triton,
402 D: Num,
403 const KH: i32,
404 const KW: i32,
405 const STRIDE_H: i32,
406 const STRIDE_W: i32,
407 const PAD_H: i32,
408 const PAD_W: i32,
409 const G: i32,
410 const BLOCK_OW: i32,
411>(
412 dy_ptr: T::Pointer<D>,
413 x_ptr: T::Pointer<D>,
414 w_ptr: T::Pointer<D>,
415 dx_ptr: T::Pointer<D>,
416 dw_ptr: T::Pointer<D>,
417 _B: i32,
418 C_IN: i32,
419 C_OUT: i32,
420 H: i32,
421 W: i32,
422 OH: i32,
423 OW: i32,
424) where
425 T::I32Tensor: Tensor<i32, 1>,
426 T::I32Tensor: Comparison<i32, BoolTensor = T::BoolTensor>,
427 T::BoolTensor: BitAnd<Output = T::BoolTensor>,
428 T::Pointer<D>: AddOffsets<i32, 1, T::I32Tensor, Output = T::Tensor<T::Pointer<D>>>,
429{
430 let pid = T::program_id(Axis::X);
431 let num_ow_tiles = T::cdiv(OW, BLOCK_OW);
432
433 let ow_tile = pid % num_ow_tiles;
434 let bco = pid / num_ow_tiles;
435 let oh = bco % OH;
436 let bc = bco / OH;
437 let c_out = bc % C_OUT;
438 let b = bc / C_OUT;
439
440 let ow_start = ow_tile * BLOCK_OW;
441 let ow_range = T::arange(0, BLOCK_OW) + ow_start;
442 let ow_mask = ow_range.lt(OW);
443
444 let dy_offsets = ow_range + ((b * C_OUT + c_out) * OH * OW + oh * OW);
446 let dy_tile = T::load(
447 dy_ptr.add_offsets(dy_offsets),
448 Some(ow_mask),
449 Some(T::zeros::<D>(&[BLOCK_OW])),
450 &[],
451 None,
452 None,
453 None,
454 false,
455 );
456
457 let c_in_per_group = C_IN / G;
458 let g_idx = c_out / (C_OUT / G);
459 let c_in_start = g_idx * c_in_per_group;
460
461 let loop_bound = c_in_per_group * KH * KW;
462 for idx in 0..loop_bound {
463 let kw = idx % KW;
464 let kh_cin = idx / KW;
465 let kh = kh_cin % KH;
466 let c_in_local = kh_cin / KH;
467 let c_in = c_in_start + c_in_local;
468
469 let w_idx = ((c_out * c_in_per_group + c_in_local) * KH + kh) * KW + kw;
471 let w_off = T::arange(0, 1) + w_idx;
472 let w_1 = T::load(
473 w_ptr.add_offsets(w_off),
474 None,
475 None,
476 &[],
477 None,
478 None,
479 None,
480 false,
481 );
482 let w_tile = T::broadcast_to(w_1, &[BLOCK_OW]);
483
484 let ih = oh * STRIDE_H + kh - PAD_H;
485 let iw_range = ow_range * STRIDE_W + kw - PAD_W;
486
487 #[allow(clippy::erasing_op)]
488 let ih_t = ow_range * 0 + ih;
489 let h_in_bounds = ih_t.ge(0) & ih_t.lt(H);
490 let w_in_bounds = iw_range.ge(0) & iw_range.lt(W);
491 let in_mask = ow_mask & h_in_bounds & w_in_bounds;
492
493 let dx_offsets = iw_range + ((b * C_IN + c_in) * H * W + ih * W);
494
495 let grad_tile = dy_tile * w_tile;
497 T::atomic_add(
498 dx_ptr.add_offsets(dx_offsets),
499 grad_tile,
500 Some(in_mask),
501 None,
502 None,
503 );
504
505 let x_tile = T::load(
507 x_ptr.add_offsets(dx_offsets),
508 Some(in_mask),
509 Some(T::zeros::<D>(&[BLOCK_OW])),
510 &[],
511 None,
512 None,
513 None,
514 false,
515 );
516 let partial = T::sum(dy_tile * x_tile, Some(0), false);
517 let partial_1 = T::expand_dims(partial, 0);
518 let dw_off = T::arange(0, 1) + w_idx;
519 T::atomic_add(dw_ptr.add_offsets(dw_off), partial_1, None, None, None);
520 }
521}
522
523impl<D: Num + Send + Sync + 'static> teeny_core::model::RuntimeOp for Conv2dForward<D> {
524 fn n_activation_inputs(&self) -> usize {
525 1
526 }
527
528 fn param_shapes(&self, input_shapes: &[&[usize]], output_shape: &[usize]) -> Vec<Vec<usize>> {
529 let c_in = input_shapes[0][1];
530 let c_out = output_shape[1];
531 vec![vec![
532 c_out,
533 c_in / self.g as usize,
534 self.kh as usize,
535 self.kw as usize,
536 ]]
537 }
538
539 fn param_names(&self) -> &'static [&'static str] {
540 &["weight"]
541 }
542
543 fn pack_args(
544 &self,
545 inputs: &[(teeny_core::model::RawPtr, &[usize])],
546 params: &[teeny_core::model::RawPtr],
547 output: teeny_core::model::RawPtr,
548 output_shape: &[usize],
549 _output_row_stride: i32,
550 visitor: &mut dyn teeny_core::device::program::ArgVisitor,
551 ) {
552 let input_shape = inputs[0].1;
553 visitor.visit_ptr(inputs[0].0);
554 visitor.visit_ptr(params[0]);
555 visitor.visit_ptr(output);
556 visitor.visit_i32(input_shape[0] as i32);
557 visitor.visit_i32(input_shape[1] as i32);
558 visitor.visit_i32(output_shape[1] as i32);
559 visitor.visit_i32(input_shape[2] as i32);
560 visitor.visit_i32(input_shape[3] as i32);
561 visitor.visit_i32(output_shape[2] as i32);
562 visitor.visit_i32(output_shape[3] as i32);
563 }
564
565 fn block(&self) -> [u32; 3] {
566 [128, 1, 1]
567 }
568
569 fn grid(&self, output_shape: &[usize]) -> [u32; 3] {
570 let num_ow_tiles = output_shape[3].div_ceil(self.block_ow as usize);
571 [
572 (output_shape[0] * output_shape[1] * output_shape[2] * num_ow_tiles) as u32,
573 1,
574 1,
575 ]
576 }
577
578 #[cfg(feature = "training")]
579 fn has_backward(&self) -> bool {
580 true
581 }
582
583 #[cfg(feature = "training")]
585 fn pack_backward_args(
586 &self,
587 inputs: &[(teeny_core::model::RawPtr, &[usize])],
588 params: &[teeny_core::model::RawPtr],
589 _output: teeny_core::model::RawPtr,
590 output_shape: &[usize],
591 grad_output: teeny_core::model::RawPtr,
592 _grad_output_row_stride: i32,
593 grad_inputs: &[teeny_core::model::RawPtr],
594 grad_params: &[teeny_core::model::RawPtr],
595 visitor: &mut dyn teeny_core::device::program::ArgVisitor,
596 ) {
597 let in_shape = inputs[0].1; visitor.visit_ptr(grad_output); visitor.visit_ptr(inputs[0].0); visitor.visit_ptr(params[0]); visitor.visit_ptr(grad_inputs[0]); visitor.visit_ptr(grad_params[0]); visitor.visit_i32(in_shape[0] as i32); visitor.visit_i32(in_shape[1] as i32); visitor.visit_i32(output_shape[1] as i32); visitor.visit_i32(in_shape[2] as i32); visitor.visit_i32(in_shape[3] as i32); visitor.visit_i32(output_shape[2] as i32); visitor.visit_i32(output_shape[3] as i32); }
611
612 #[cfg(feature = "training")]
613 fn backward_block(&self) -> [u32; 3] {
614 [128, 1, 1]
615 }
616
617 #[cfg(feature = "training")]
619 fn backward_grid(&self, _input_shapes: &[&[usize]], output_shape: &[usize]) -> [u32; 3] {
620 let num_ow_tiles = output_shape[3].div_ceil(self.block_ow as usize);
621 [
622 (output_shape[0] * output_shape[1] * output_shape[2] * num_ow_tiles) as u32,
623 1,
624 1,
625 ]
626 }
627}
628
629pub struct Conv2dOp<'a, T: Num> {
630 pub forward: Conv2dForward<T>,
631 pub backward_dx: Conv2dBackwardDx<T>,
632 pub backward_dw: Conv2dBackwardDw<T>,
633 _marker: core::marker::PhantomData<&'a ()>,
634}
635
636#[kernel]
653pub fn conv2d_bias_forward<
654 T: Triton,
655 D: Num,
656 const KH: i32,
657 const KW: i32,
658 const STRIDE_H: i32,
659 const STRIDE_W: i32,
660 const PAD_H: i32,
661 const PAD_W: i32,
662 const G: i32,
663 const BLOCK_OW: i32,
664>(
665 x_ptr: T::Pointer<D>,
666 w_ptr: T::Pointer<D>,
667 bias_ptr: T::Pointer<D>,
668 y_ptr: T::Pointer<D>,
669 _B: i32,
670 C_IN: i32,
671 C_OUT: i32,
672 H: i32,
673 W: i32,
674 OH: i32,
675 OW: i32,
676) where
677 T::I32Tensor: Tensor<i32, 1>,
678 T::I32Tensor: Comparison<i32, BoolTensor = T::BoolTensor>,
679 T::BoolTensor: BitAnd<Output = T::BoolTensor>,
680 T::Pointer<D>: AddOffsets<i32, 1, T::I32Tensor, Output = T::Tensor<T::Pointer<D>>>,
681{
682 let pid = T::program_id(Axis::X);
683 let num_ow_tiles = T::cdiv(OW, BLOCK_OW);
684
685 let ow_tile = pid % num_ow_tiles;
686 let bco = pid / num_ow_tiles;
687 let oh = bco % OH;
688 let bc = bco / OH;
689 let c_out = bc % C_OUT;
690 let b = bc / C_OUT;
691
692 let ow_start = ow_tile * BLOCK_OW;
693 let ow_range = T::arange(0, BLOCK_OW) + ow_start;
694 let ow_mask = ow_range.lt(OW);
695
696 let out_bc_base = (b * C_OUT + c_out) * OH * OW;
697
698 let c_in_per_group = C_IN / G;
699 let g_idx = c_out / (C_OUT / G);
700 let c_in_start = g_idx * c_in_per_group;
701
702 let mut acc = T::zeros::<D>(&[BLOCK_OW]);
703
704 let loop_bound = c_in_per_group * KH * KW;
705 for idx in 0..loop_bound {
706 let kw = idx % KW;
707 let kh_cin = idx / KW;
708 let kh = kh_cin % KH;
709 let c_in_local = kh_cin / KH;
710 let c_in = c_in_start + c_in_local;
711
712 let ih = oh * STRIDE_H + kh - PAD_H;
713 let iw_range = ow_range * STRIDE_W + kw - PAD_W;
714
715 #[allow(clippy::erasing_op)]
716 let ih_t = ow_range * 0 + ih;
717 let h_in_bounds = ih_t.ge(0) & ih_t.lt(H);
718 let w_in_bounds = iw_range.ge(0) & iw_range.lt(W);
719 let load_mask = ow_mask & h_in_bounds & w_in_bounds;
720
721 let x_offsets = iw_range + ((b * C_IN + c_in) * H * W + ih * W);
722 let x_tile = T::load(
723 x_ptr.add_offsets(x_offsets),
724 Some(load_mask),
725 Some(T::zeros::<D>(&[BLOCK_OW])),
726 &[],
727 None,
728 None,
729 None,
730 false,
731 );
732
733 let w_idx = ((c_out * c_in_per_group + c_in_local) * KH + kh) * KW + kw;
734 let w_off = T::arange(0, 1) + w_idx;
735 let w_1 = T::load(
736 w_ptr.add_offsets(w_off),
737 None,
738 None,
739 &[],
740 None,
741 None,
742 None,
743 false,
744 );
745 let w_tile = T::broadcast_to(w_1, &[BLOCK_OW]);
746
747 acc = acc + x_tile * w_tile;
748 }
749
750 let bias_off = T::arange(0, 1) + c_out;
752 let bias_1 = T::load(
753 bias_ptr.add_offsets(bias_off),
754 None,
755 None,
756 &[],
757 None,
758 None,
759 None,
760 false,
761 );
762 let bias_tile = T::broadcast_to(bias_1, &[BLOCK_OW]);
763 acc = acc + bias_tile;
764
765 let out_offsets = ow_range + (out_bc_base + oh * OW);
766 T::store(
767 y_ptr.add_offsets(out_offsets),
768 acc,
769 Some(ow_mask),
770 &[],
771 None,
772 None,
773 );
774}
775
776impl<D: Num + Send + Sync + 'static> teeny_core::model::RuntimeOp for Conv2dBiasForward<D> {
777 fn n_activation_inputs(&self) -> usize {
778 1
779 }
780
781 fn param_shapes(&self, input_shapes: &[&[usize]], output_shape: &[usize]) -> Vec<Vec<usize>> {
782 let c_in = input_shapes[0][1];
783 let c_out = output_shape[1];
784 vec![
785 vec![c_out, c_in / self.g as usize, self.kh as usize, self.kw as usize],
786 vec![c_out],
787 ]
788 }
789
790 fn param_names(&self) -> &'static [&'static str] {
791 &["weight", "bias"]
792 }
793
794 fn pack_args(
795 &self,
796 inputs: &[(teeny_core::model::RawPtr, &[usize])],
797 params: &[teeny_core::model::RawPtr],
798 output: teeny_core::model::RawPtr,
799 output_shape: &[usize],
800 _output_row_stride: i32,
801 visitor: &mut dyn teeny_core::device::program::ArgVisitor,
802 ) {
803 let input_shape = inputs[0].1;
804 visitor.visit_ptr(inputs[0].0); visitor.visit_ptr(params[0]); visitor.visit_ptr(params[1]); visitor.visit_ptr(output); visitor.visit_i32(input_shape[0] as i32); visitor.visit_i32(input_shape[1] as i32); visitor.visit_i32(output_shape[1] as i32); visitor.visit_i32(input_shape[2] as i32); visitor.visit_i32(input_shape[3] as i32); visitor.visit_i32(output_shape[2] as i32); visitor.visit_i32(output_shape[3] as i32); }
816
817 fn block(&self) -> [u32; 3] {
818 [128, 1, 1]
819 }
820
821 fn grid(&self, output_shape: &[usize]) -> [u32; 3] {
822 let num_ow_tiles = output_shape[3].div_ceil(self.block_ow as usize);
823 [
824 (output_shape[0] * output_shape[1] * output_shape[2] * num_ow_tiles) as u32,
825 1,
826 1,
827 ]
828 }
829}