1use super::Thunk;
30use super::ops::{exec_gather_elements, exec_gather_nd, exec_scatter_elements, exec_scatter_nd};
31use rlx_ir::{Graph, NodeId, Op};
32
33fn shape_u32s(shape: &rlx_ir::Shape) -> Vec<u32> {
34 (0..shape.rank())
35 .map(|i| shape.dim(i).unwrap_static() as u32)
36 .collect()
37}
38
39pub fn indexing_thunk_from_node(
41 graph: &Graph,
42 node: &rlx_ir::Node,
43 mut byte_off: impl FnMut(NodeId) -> usize,
44) -> Thunk {
45 match &node.op {
46 Op::ScatterNd { reduction } => {
47 let data_shape = &graph.node(node.inputs[0]).shape;
48 let indices_shape = &graph.node(node.inputs[1]).shape;
49 let updates_shape = &graph.node(node.inputs[2]).shape;
50 Thunk::ScatterNd {
51 data: byte_off(node.inputs[0]),
52 indices: byte_off(node.inputs[1]),
53 updates: byte_off(node.inputs[2]),
54 dst: byte_off(node.id),
55 data_shape: shape_u32s(data_shape),
56 indices_shape: shape_u32s(indices_shape),
57 data_len: data_shape.num_elements().unwrap_or(0) as u32,
58 updates_len: updates_shape.num_elements().unwrap_or(0) as u32,
59 indices_len: indices_shape.num_elements().unwrap_or(0) as u32,
60 indices_i64: u8::from(indices_shape.dtype() == rlx_ir::DType::I64),
61 reduction: *reduction,
62 }
63 }
64 Op::ScatterElements { axis, reduction } => {
65 let data_shape = &graph.node(node.inputs[0]).shape;
66 let indices_shape = &graph.node(node.inputs[1]).shape;
67 let updates_shape = &graph.node(node.inputs[2]).shape;
68 Thunk::ScatterElements {
69 data: byte_off(node.inputs[0]),
70 indices: byte_off(node.inputs[1]),
71 updates: byte_off(node.inputs[2]),
72 dst: byte_off(node.id),
73 data_shape: shape_u32s(data_shape),
74 data_len: data_shape.num_elements().unwrap_or(0) as u32,
75 updates_len: updates_shape.num_elements().unwrap_or(0) as u32,
76 indices_len: indices_shape.num_elements().unwrap_or(0) as u32,
77 indices_i64: u8::from(indices_shape.dtype() == rlx_ir::DType::I64),
78 axis: *axis,
79 reduction: *reduction,
80 }
81 }
82 Op::GatherNd { batch_dims } => {
83 let data_shape = &graph.node(node.inputs[0]).shape;
84 let indices_shape = &graph.node(node.inputs[1]).shape;
85 Thunk::GatherNd {
86 data: byte_off(node.inputs[0]),
87 indices: byte_off(node.inputs[1]),
88 dst: byte_off(node.id),
89 data_shape: shape_u32s(data_shape),
90 indices_shape: shape_u32s(indices_shape),
91 data_len: data_shape.num_elements().unwrap_or(0) as u32,
92 indices_len: indices_shape.num_elements().unwrap_or(0) as u32,
93 out_len: node.shape.num_elements().unwrap_or(0) as u32,
94 indices_i64: u8::from(indices_shape.dtype() == rlx_ir::DType::I64),
95 batch_dims: *batch_dims,
96 }
97 }
98 Op::GatherElements { axis } => {
99 let data_shape = &graph.node(node.inputs[0]).shape;
100 let indices_shape = &graph.node(node.inputs[1]).shape;
101 Thunk::GatherElements {
102 data: byte_off(node.inputs[0]),
103 indices: byte_off(node.inputs[1]),
104 dst: byte_off(node.id),
105 data_shape: shape_u32s(data_shape),
106 indices_shape: shape_u32s(indices_shape),
107 data_len: data_shape.num_elements().unwrap_or(0) as u32,
108 indices_len: indices_shape.num_elements().unwrap_or(0) as u32,
109 out_len: node.shape.num_elements().unwrap_or(0) as u32,
110 indices_i64: u8::from(indices_shape.dtype() == rlx_ir::DType::I64),
111 axis: *axis,
112 }
113 }
114 _ => panic!(
115 "indexing_thunk_from_node: expected ScatterNd/ScatterElements/GatherNd/GatherElements"
116 ),
117 }
118}
119
120fn idx_nbytes(len: u32, indices_i64: u8) -> usize {
121 if indices_i64 != 0 {
122 len as usize * 8
123 } else {
124 len as usize * 4
125 }
126}
127
128pub fn indexing_thunk_regions(thunk: &Thunk) -> Vec<(usize, usize)> {
130 match thunk {
131 Thunk::ScatterNd {
132 data,
133 indices,
134 updates,
135 dst,
136 data_len,
137 updates_len,
138 indices_len,
139 indices_i64,
140 ..
141 } => vec![
142 (*data, *data_len as usize * 4),
143 (*indices, idx_nbytes(*indices_len, *indices_i64)),
144 (*updates, *updates_len as usize * 4),
145 (*dst, *data_len as usize * 4),
146 ],
147 Thunk::ScatterElements {
148 data,
149 indices,
150 updates,
151 dst,
152 data_len,
153 updates_len,
154 indices_len,
155 indices_i64,
156 ..
157 } => vec![
158 (*data, *data_len as usize * 4),
159 (*indices, idx_nbytes(*indices_len, *indices_i64)),
160 (*updates, *updates_len as usize * 4),
161 (*dst, *data_len as usize * 4),
162 ],
163 Thunk::GatherNd {
164 data,
165 indices,
166 dst,
167 data_len,
168 indices_len,
169 out_len,
170 indices_i64,
171 ..
172 } => vec![
173 (*data, *data_len as usize * 4),
174 (*indices, idx_nbytes(*indices_len, *indices_i64)),
175 (*dst, *out_len as usize * 4),
176 ],
177 Thunk::GatherElements {
178 data,
179 indices,
180 dst,
181 data_len,
182 indices_len,
183 out_len,
184 indices_i64,
185 ..
186 } => vec![
187 (*data, *data_len as usize * 4),
188 (*indices, idx_nbytes(*indices_len, *indices_i64)),
189 (*dst, *out_len as usize * 4),
190 ],
191 _ => panic!("indexing_thunk_regions: not an indexing thunk"),
192 }
193}
194
195fn rebase_off(off: usize, lo: usize) -> usize {
196 off.checked_sub(lo)
197 .expect("indexing host span: offset below span lo")
198}
199
200fn rebase_indexing_thunk(thunk: Thunk, lo: usize) -> Thunk {
201 match thunk {
202 Thunk::ScatterNd {
203 data,
204 indices,
205 updates,
206 dst,
207 data_shape,
208 indices_shape,
209 data_len,
210 updates_len,
211 indices_len,
212 indices_i64,
213 reduction,
214 } => Thunk::ScatterNd {
215 data: rebase_off(data, lo),
216 indices: rebase_off(indices, lo),
217 updates: rebase_off(updates, lo),
218 dst: rebase_off(dst, lo),
219 data_shape,
220 indices_shape,
221 data_len,
222 updates_len,
223 indices_len,
224 indices_i64,
225 reduction,
226 },
227 Thunk::ScatterElements {
228 data,
229 indices,
230 updates,
231 dst,
232 data_shape,
233 data_len,
234 updates_len,
235 indices_len,
236 indices_i64,
237 axis,
238 reduction,
239 } => Thunk::ScatterElements {
240 data: rebase_off(data, lo),
241 indices: rebase_off(indices, lo),
242 updates: rebase_off(updates, lo),
243 dst: rebase_off(dst, lo),
244 data_shape,
245 data_len,
246 updates_len,
247 indices_len,
248 indices_i64,
249 axis,
250 reduction,
251 },
252 Thunk::GatherNd {
253 data,
254 indices,
255 dst,
256 data_shape,
257 indices_shape,
258 data_len,
259 indices_len,
260 out_len,
261 indices_i64,
262 batch_dims,
263 } => Thunk::GatherNd {
264 data: rebase_off(data, lo),
265 indices: rebase_off(indices, lo),
266 dst: rebase_off(dst, lo),
267 data_shape,
268 indices_shape,
269 data_len,
270 indices_len,
271 out_len,
272 indices_i64,
273 batch_dims,
274 },
275 Thunk::GatherElements {
276 data,
277 indices,
278 dst,
279 data_shape,
280 indices_shape,
281 data_len,
282 indices_len,
283 out_len,
284 indices_i64,
285 axis,
286 } => Thunk::GatherElements {
287 data: rebase_off(data, lo),
288 indices: rebase_off(indices, lo),
289 dst: rebase_off(dst, lo),
290 data_shape,
291 indices_shape,
292 data_len,
293 indices_len,
294 out_len,
295 indices_i64,
296 axis,
297 },
298 _ => panic!("rebase_indexing_thunk: not an indexing thunk"),
299 }
300}
301
302#[derive(Clone)]
306pub struct IndexingHostSpan {
307 pub lo: usize,
308 pub hi: usize,
309 pub thunk: Thunk,
310}
311
312impl IndexingHostSpan {
313 pub fn from_thunk(thunk: Thunk) -> Self {
314 let regions = indexing_thunk_regions(&thunk);
315 let mut lo = regions[0].0;
316 let mut hi = regions[0].0 + regions[0].1;
317 for &(off, n) in ®ions[1..] {
318 lo = lo.min(off);
319 hi = hi.max(off + n);
320 }
321 Self {
322 lo,
323 hi,
324 thunk: rebase_indexing_thunk(thunk, lo),
325 }
326 }
327
328 pub fn len(&self) -> usize {
329 self.hi - self.lo
330 }
331
332 pub fn is_empty(&self) -> bool {
333 self.len() == 0
334 }
335}
336
337pub fn indexing_thunk_dst_region(thunk: &Thunk) -> (usize, usize) {
339 match thunk {
340 Thunk::ScatterNd { dst, data_len, .. } | Thunk::ScatterElements { dst, data_len, .. } => {
341 (*dst, *data_len as usize * 4)
342 }
343 Thunk::GatherNd { dst, out_len, .. } | Thunk::GatherElements { dst, out_len, .. } => {
344 (*dst, *out_len as usize * 4)
345 }
346 _ => panic!("indexing_thunk_dst_region: not an indexing thunk"),
347 }
348}
349
350pub fn remap_indexing_thunk_offsets(thunk: Thunk, mut map: impl FnMut(usize) -> usize) -> Thunk {
352 match thunk {
353 Thunk::ScatterNd {
354 data,
355 indices,
356 updates,
357 dst,
358 data_shape,
359 indices_shape,
360 data_len,
361 updates_len,
362 indices_len,
363 indices_i64,
364 reduction,
365 } => Thunk::ScatterNd {
366 data: map(data),
367 indices: map(indices),
368 updates: map(updates),
369 dst: map(dst),
370 data_shape,
371 indices_shape,
372 data_len,
373 updates_len,
374 indices_len,
375 indices_i64,
376 reduction,
377 },
378 Thunk::ScatterElements {
379 data,
380 indices,
381 updates,
382 dst,
383 data_shape,
384 data_len,
385 updates_len,
386 indices_len,
387 indices_i64,
388 axis,
389 reduction,
390 } => Thunk::ScatterElements {
391 data: map(data),
392 indices: map(indices),
393 updates: map(updates),
394 dst: map(dst),
395 data_shape,
396 data_len,
397 updates_len,
398 indices_len,
399 indices_i64,
400 axis,
401 reduction,
402 },
403 Thunk::GatherNd {
404 data,
405 indices,
406 dst,
407 data_shape,
408 indices_shape,
409 data_len,
410 indices_len,
411 out_len,
412 indices_i64,
413 batch_dims,
414 } => Thunk::GatherNd {
415 data: map(data),
416 indices: map(indices),
417 dst: map(dst),
418 data_shape,
419 indices_shape,
420 data_len,
421 indices_len,
422 out_len,
423 indices_i64,
424 batch_dims,
425 },
426 Thunk::GatherElements {
427 data,
428 indices,
429 dst,
430 data_shape,
431 indices_shape,
432 data_len,
433 indices_len,
434 out_len,
435 indices_i64,
436 axis,
437 } => Thunk::GatherElements {
438 data: map(data),
439 indices: map(indices),
440 dst: map(dst),
441 data_shape,
442 indices_shape,
443 data_len,
444 indices_len,
445 out_len,
446 indices_i64,
447 axis,
448 },
449 _ => panic!("remap_indexing_thunk_offsets: not an indexing thunk"),
450 }
451}
452
453pub const INDEXING_CONTIGUOUS_SPAN_CAP: usize = 512 * 1024 * 1024;
459
460pub unsafe fn execute_indexing_thunk_on_bytes(base: *mut u8, thunk: &Thunk) {
466 match thunk {
467 Thunk::ScatterNd { .. } => exec_scatter_nd(thunk, base),
468 Thunk::ScatterElements { .. } => exec_scatter_elements(thunk, base),
469 Thunk::GatherNd { .. } => exec_gather_nd(thunk, base),
470 Thunk::GatherElements { .. } => exec_gather_elements(thunk, base),
471 _ => panic!("execute_indexing_thunk_on_bytes: not an indexing thunk"),
472 }
473}
474
475#[derive(Clone)]
478pub struct IndexingThunk(pub Thunk);
479
480impl std::fmt::Debug for IndexingThunk {
481 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
482 f.write_str("IndexingThunk(..)")
483 }
484}
485
486impl IndexingThunk {
487 pub fn from_node(
488 graph: &Graph,
489 node: &rlx_ir::Node,
490 byte_off: impl FnMut(NodeId) -> usize,
491 ) -> Self {
492 Self(indexing_thunk_from_node(graph, node, byte_off))
493 }
494
495 pub fn inner(&self) -> &Thunk {
496 &self.0
497 }
498
499 pub fn force_indices_f32(mut self) -> Self {
503 self.0 = force_indexing_indices_f32(self.0);
504 self
505 }
506}
507
508pub fn force_indexing_indices_f32(thunk: Thunk) -> Thunk {
510 match thunk {
511 Thunk::ScatterNd {
512 data,
513 indices,
514 updates,
515 dst,
516 data_shape,
517 indices_shape,
518 data_len,
519 updates_len,
520 indices_len,
521 reduction,
522 ..
523 } => Thunk::ScatterNd {
524 data,
525 indices,
526 updates,
527 dst,
528 data_shape,
529 indices_shape,
530 data_len,
531 updates_len,
532 indices_len,
533 indices_i64: 0,
534 reduction,
535 },
536 Thunk::ScatterElements {
537 data,
538 indices,
539 updates,
540 dst,
541 data_shape,
542 data_len,
543 updates_len,
544 indices_len,
545 axis,
546 reduction,
547 ..
548 } => Thunk::ScatterElements {
549 data,
550 indices,
551 updates,
552 dst,
553 data_shape,
554 data_len,
555 updates_len,
556 indices_len,
557 indices_i64: 0,
558 axis,
559 reduction,
560 },
561 Thunk::GatherNd {
562 data,
563 indices,
564 dst,
565 data_shape,
566 indices_shape,
567 data_len,
568 indices_len,
569 out_len,
570 batch_dims,
571 ..
572 } => Thunk::GatherNd {
573 data,
574 indices,
575 dst,
576 data_shape,
577 indices_shape,
578 data_len,
579 indices_len,
580 out_len,
581 indices_i64: 0,
582 batch_dims,
583 },
584 Thunk::GatherElements {
585 data,
586 indices,
587 dst,
588 data_shape,
589 indices_shape,
590 data_len,
591 indices_len,
592 out_len,
593 axis,
594 ..
595 } => Thunk::GatherElements {
596 data,
597 indices,
598 dst,
599 data_shape,
600 indices_shape,
601 data_len,
602 indices_len,
603 out_len,
604 indices_i64: 0,
605 axis,
606 },
607 other => other,
608 }
609}
610
611#[macro_export]
613macro_rules! rlx_indexing_thunk {
614 ($graph:expr, $node:expr, $byte_off:expr) => {
615 $crate::thunk::IndexingThunk::from_node(&$graph, $node, $byte_off)
616 };
617}
618
619#[macro_export]
621macro_rules! rlx_execute_indexing_on_bytes {
622 ($base:expr, $thunk:expr) => {
623 $crate::thunk::execute_indexing_thunk_on_bytes($base, $thunk)
624 };
625}
626
627#[macro_export]
632macro_rules! rlx_indexing_stage_d2h {
633 (
634 arena_size_bytes = $nbytes:expr,
635 thunk = $thunk:expr,
636 sync = $sync:block,
637 dtoh = |$host:ident| $dtoh:block,
638 htod = |$host_back:ident| $htod:block $(,)?
639 ) => {
640 $crate::rlx_arena_stage_d2h! {
641 arena_size_bytes = $nbytes,
642 sync = $sync,
643 dtoh = |$host| $dtoh,
644 on_host = |host_mut| {
645 unsafe {
646 $crate::thunk::execute_indexing_thunk_on_bytes(
647 host_mut.as_mut_ptr() as *mut u8,
648 $thunk,
649 );
650 }
651 },
652 htod = |$host_back| $htod,
653 }
654 };
655}