1use alloc::string::{String, ToString};
2
3use crate as cubecl;
4use cubecl::prelude::*;
5use cubecl_ir::{Instruction, Operation, Value};
6
7define_scalar!(ElemA);
8define_size!(SizeA);
9
10#[cube]
12pub fn read_masked<C: CubePrimitive>(mask: bool, list: &[C], index: usize, value: C) -> C {
13 let index = index * usize::cast_from(mask);
14 let input = unsafe { *list.get_unchecked(index) };
15
16 select(mask, input, value)
17}
18
19#[cube]
21pub fn read_checked<C: CubePrimitive>(list: &[C], index: usize) -> C {
22 let fallback = comptime![C::Scalar::default()].runtime();
23 let clamped = index.min(list.len() - 1);
24 let input = unsafe { *list.get_unchecked(clamped) };
25
26 select(index == clamped, input, C::cast_from(fallback))
27}
28
29#[cube]
31pub fn write_checked<C: CubePrimitive>(list: &mut [C], index: usize, value: C) {
32 if index < list.len() {
33 unsafe { *list.get_unchecked_mut(index) = value };
34 }
35}
36
37#[cube]
39pub fn checked_index<E: Scalar, N: Size>(
40 index: usize,
41 buffer_len: usize,
42 #[comptime] unroll_factor: usize,
43) -> usize {
44 let len = buffer_len * unroll_factor;
45 index.min(len - 1)
46}
47
48#[cube]
50pub fn validate_index<E: Scalar, N: Size>(
51 tensor: &[Vector<E, N>],
52 index: usize,
53 buffer_len: usize,
54 #[comptime] unroll_factor: usize,
55 #[comptime] kernel_name: String,
56) -> usize {
57 let len = buffer_len * unroll_factor;
58 let in_bounds = index < len;
59 if !in_bounds {
60 print_oob::<[Vector<E, N>]>(kernel_name, index, len, tensor);
61 }
62
63 index.min(len)
64}
65
66#[cube]
67#[allow(unused)]
68fn print_oob<Out: CubeType<ExpandType: Into<Value>> + ?Sized>(
69 #[comptime] kernel_name: String,
70 index: usize,
71 len: usize,
72 buffer: &Out,
73) {
74 intrinsic!(|scope| {
75 let value: Value = buffer.clone_unchecked().into();
76 let name = value.address_space();
77 __expand_debug_print!(
78 scope,
79 alloc::format!(
80 "[VALIDATION {kernel_name}]: Encountered OOB index in {name} at %u, length is %u\n"
81 ),
82 index,
83 len
84 );
85 })
86}
87
88#[allow(missing_docs)]
89pub fn expand_checked_index(
90 scope: &Scope,
91 list: Value,
92 index: Value,
93 out: Value,
94 unroll_factor: usize,
95) {
96 scope.register_type::<ElemA>(list.ty.storage_type());
97 scope.register_size::<SizeA>(list.ty.vector_size());
98 let len = expand_buffer_length_native(scope, list);
99 let index =
100 checked_index::expand::<ElemA, SizeA>(scope, index.into(), len.into(), unroll_factor);
101 let ptr = index_expand(scope, list, index.expand, false);
102 scope.register(Instruction::new(Operation::Copy(ptr), out));
103}
104
105#[allow(missing_docs)]
106pub fn expand_validate_index(
107 scope: &Scope,
108 list: Value,
109 index: Value,
110 out: Value,
111 unroll_factor: usize,
112 kernel_name: &str,
113) {
114 scope.register_type::<ElemA>(list.ty.storage_type());
115 scope.register_size::<SizeA>(list.ty.vector_size());
116 let len = expand_buffer_length_native(scope, list);
117 let tensor = list.into();
118 let index = validate_index::expand::<ElemA, SizeA>(
119 scope,
120 &tensor,
121 index.into(),
122 len.into(),
123 unroll_factor,
124 kernel_name.to_string(),
125 );
126 let ptr = index_expand(scope, list, index.expand, false);
127 scope.register(Instruction::new(Operation::Copy(ptr), out));
128}