cubecl_core/frontend/
harness.rs1use alloc::{boxed::Box, vec::Vec};
5use cubecl_common::bytes::Bytes;
6use cubecl_ir::ContextExt;
7use cubecl_runtime::{kernel::KernelDefinition, server::Handle};
8use variadics_please::all_tuples;
9
10use crate::prelude::*;
11use crate::{self as cubecl};
12
13pub trait HarnessOutput: CubeType + Sized {
25 type Out: LaunchArg;
27
28 type Host;
31
32 fn alloc(
35 client: &Client,
36 lens: &mut impl Iterator<Item = usize>,
37 handles: &mut Vec<Handle>,
38 ) -> <Self::Out as LaunchArg>::RuntimeArg;
39
40 fn __expand_store(
42 scope: &Scope,
43 out: &mut <Self::Out as CubeType>::ExpandType,
44 value: Self::ExpandType,
45 );
46
47 fn read(
49 bytes: &mut impl Iterator<Item = Bytes>,
50 lens: &mut impl Iterator<Item = usize>,
51 ) -> Self::Host;
52}
53
54#[derive(Default)]
57struct HarnessLens(Vec<usize>);
58
59#[doc(hidden)]
61pub fn __expand_store_output<T: HarnessOutput>(
62 scope: &Scope,
63 out: &mut <T::Out as CubeType>::ExpandType,
64 value: T::ExpandType,
65) {
66 scope.ctx_mut().set_aux_ty(HarnessLens::default());
67 T::__expand_store(scope, out, value);
68}
69
70#[doc(hidden)]
72pub fn __output_lens(definition: &KernelDefinition) -> Vec<usize> {
73 definition.body.ctx().aux_ty::<HarnessLens>().0.clone()
74}
75
76#[cube]
77fn store_scalar<T: CubePrimitive>(out: &mut Box<[T]>, value: T) {
78 out[0] = value;
79}
80
81impl<T: CubePrimitive + CubeElement> HarnessOutput for T {
82 type Out = Box<[T]>;
83 type Host = T;
84
85 fn alloc(
86 client: &Client,
87 _lens: &mut impl Iterator<Item = usize>,
88 handles: &mut Vec<Handle>,
89 ) -> BufferArg {
90 let handle = client.empty(core::mem::size_of::<T>());
91 handles.push(handle.clone());
92 unsafe { BufferArg::from_raw_parts(handle, 1) }
94 }
95
96 fn __expand_store(scope: &Scope, out: &mut NativeExpand<Box<[T]>>, value: NativeExpand<T>) {
97 store_scalar::expand::<T>(scope, out, value);
98 }
99
100 fn read(bytes: &mut impl Iterator<Item = Bytes>, _lens: &mut impl Iterator<Item = usize>) -> T {
101 let bytes = bytes.next().expect("a harness buffer for every scalar");
102 T::from_bytes(&bytes)[0]
103 }
104}
105
106#[cube]
107fn store_array<T: CubePrimitive>(out: &mut Box<[T]>, value: Array<T>) {
108 for i in 0..value.len() {
109 out[i] = value[i];
110 }
111}
112
113impl<T: CubePrimitive + CubeElement> HarnessOutput for Array<T> {
115 type Out = Box<[T]>;
116 type Host = Vec<T>;
117
118 fn alloc(
119 client: &Client,
120 lens: &mut impl Iterator<Item = usize>,
121 handles: &mut Vec<Handle>,
122 ) -> BufferArg {
123 let len = lens.next().expect("a harness length for every array");
124 let capacity = len.max(1);
126 let handle = client.empty(capacity * core::mem::size_of::<T>());
127 handles.push(handle.clone());
128 unsafe { BufferArg::from_raw_parts(handle, capacity) }
130 }
131
132 fn __expand_store(
133 scope: &Scope,
134 out: &mut NativeExpand<Box<[T]>>,
135 value: NativeExpand<Array<T>>,
136 ) {
137 let len = Array::<T>::__expand_len(scope, &value);
138 scope.ctx_mut().aux_ty_mut::<HarnessLens>().0.push(len);
139 store_array::expand::<T>(scope, out, value);
140 }
141
142 fn read(
143 bytes: &mut impl Iterator<Item = Bytes>,
144 lens: &mut impl Iterator<Item = usize>,
145 ) -> Vec<T> {
146 let bytes = bytes.next().expect("a harness buffer for every array");
147 let len = lens.next().expect("a harness length for every array");
148 T::from_bytes(&bytes)[..len].to_vec()
149 }
150}
151
152impl HarnessOutput for () {
154 type Out = ();
155 type Host = ();
156
157 fn alloc(
158 _client: &Client,
159 _lens: &mut impl Iterator<Item = usize>,
160 _handles: &mut Vec<Handle>,
161 ) {
162 }
163
164 fn __expand_store(_scope: &Scope, _out: &mut (), _value: ()) {}
165
166 fn read(_bytes: &mut impl Iterator<Item = Bytes>, _lens: &mut impl Iterator<Item = usize>) {}
167}
168
169macro_rules! harness_tuple {
170 ($(($T:ident, $t:ident)),*) => {
171 impl<$($T: HarnessOutput),*> HarnessOutput for ($($T,)*) {
172 type Out = ($($T::Out,)*);
173 type Host = ($($T::Host,)*);
174
175 fn alloc(
176 client: &Client,
177 lens: &mut impl Iterator<Item = usize>,
178 handles: &mut Vec<Handle>,
179 ) -> <Self::Out as LaunchArg>::RuntimeArg {
180 ($($T::alloc(client, lens, handles),)*)
181 }
182
183 #[allow(non_snake_case)]
184 fn __expand_store(
185 scope: &Scope,
186 out: &mut <Self::Out as CubeType>::ExpandType,
187 value: Self::ExpandType,
188 ) {
189 let ($($T,)*) = out;
190 let ($($t,)*) = value;
191 $(<$T as HarnessOutput>::__expand_store(scope, $T, $t);)*
192 }
193
194 fn read(
195 bytes: &mut impl Iterator<Item = Bytes>,
196 lens: &mut impl Iterator<Item = usize>,
197 ) -> Self::Host {
198 ($($T::read(bytes, lens),)*)
199 }
200 }
201 };
202}
203
204all_tuples!(harness_tuple, 1, 12, T, t);