use alloc::{boxed::Box, vec::Vec};
use cubecl_common::bytes::Bytes;
use cubecl_ir::ContextExt;
use cubecl_runtime::{kernel::KernelDefinition, server::Handle};
use variadics_please::all_tuples;
use crate::prelude::*;
use crate::{self as cubecl};
pub trait HarnessOutput: CubeType + Sized {
type Out: LaunchArg;
type Host;
fn alloc(
client: &Client,
lens: &mut impl Iterator<Item = usize>,
handles: &mut Vec<Handle>,
) -> <Self::Out as LaunchArg>::RuntimeArg;
fn __expand_store(
scope: &Scope,
out: &mut <Self::Out as CubeType>::ExpandType,
value: Self::ExpandType,
);
fn read(
bytes: &mut impl Iterator<Item = Bytes>,
lens: &mut impl Iterator<Item = usize>,
) -> Self::Host;
}
#[derive(Default)]
struct HarnessLens(Vec<usize>);
#[doc(hidden)]
pub fn __expand_store_output<T: HarnessOutput>(
scope: &Scope,
out: &mut <T::Out as CubeType>::ExpandType,
value: T::ExpandType,
) {
scope.ctx_mut().set_aux_ty(HarnessLens::default());
T::__expand_store(scope, out, value);
}
#[doc(hidden)]
pub fn __output_lens(definition: &KernelDefinition) -> Vec<usize> {
definition.body.ctx().aux_ty::<HarnessLens>().0.clone()
}
#[cube]
fn store_scalar<T: CubePrimitive>(out: &mut Box<[T]>, value: T) {
out[0] = value;
}
impl<T: CubePrimitive + CubeElement> HarnessOutput for T {
type Out = Box<[T]>;
type Host = T;
fn alloc(
client: &Client,
_lens: &mut impl Iterator<Item = usize>,
handles: &mut Vec<Handle>,
) -> BufferArg {
let handle = client.empty(core::mem::size_of::<T>());
handles.push(handle.clone());
unsafe { BufferArg::from_raw_parts(handle, 1) }
}
fn __expand_store(scope: &Scope, out: &mut NativeExpand<Box<[T]>>, value: NativeExpand<T>) {
store_scalar::expand::<T>(scope, out, value);
}
fn read(bytes: &mut impl Iterator<Item = Bytes>, _lens: &mut impl Iterator<Item = usize>) -> T {
let bytes = bytes.next().expect("a harness buffer for every scalar");
T::from_bytes(&bytes)[0]
}
}
#[cube]
fn store_array<T: CubePrimitive>(out: &mut Box<[T]>, value: Array<T>) {
for i in 0..value.len() {
out[i] = value[i];
}
}
impl<T: CubePrimitive + CubeElement> HarnessOutput for Array<T> {
type Out = Box<[T]>;
type Host = Vec<T>;
fn alloc(
client: &Client,
lens: &mut impl Iterator<Item = usize>,
handles: &mut Vec<Handle>,
) -> BufferArg {
let len = lens.next().expect("a harness length for every array");
let capacity = len.max(1);
let handle = client.empty(capacity * core::mem::size_of::<T>());
handles.push(handle.clone());
unsafe { BufferArg::from_raw_parts(handle, capacity) }
}
fn __expand_store(
scope: &Scope,
out: &mut NativeExpand<Box<[T]>>,
value: NativeExpand<Array<T>>,
) {
let len = Array::<T>::__expand_len(scope, &value);
scope.ctx_mut().aux_ty_mut::<HarnessLens>().0.push(len);
store_array::expand::<T>(scope, out, value);
}
fn read(
bytes: &mut impl Iterator<Item = Bytes>,
lens: &mut impl Iterator<Item = usize>,
) -> Vec<T> {
let bytes = bytes.next().expect("a harness buffer for every array");
let len = lens.next().expect("a harness length for every array");
T::from_bytes(&bytes)[..len].to_vec()
}
}
impl HarnessOutput for () {
type Out = ();
type Host = ();
fn alloc(
_client: &Client,
_lens: &mut impl Iterator<Item = usize>,
_handles: &mut Vec<Handle>,
) {
}
fn __expand_store(_scope: &Scope, _out: &mut (), _value: ()) {}
fn read(_bytes: &mut impl Iterator<Item = Bytes>, _lens: &mut impl Iterator<Item = usize>) {}
}
macro_rules! harness_tuple {
($(($T:ident, $t:ident)),*) => {
impl<$($T: HarnessOutput),*> HarnessOutput for ($($T,)*) {
type Out = ($($T::Out,)*);
type Host = ($($T::Host,)*);
fn alloc(
client: &Client,
lens: &mut impl Iterator<Item = usize>,
handles: &mut Vec<Handle>,
) -> <Self::Out as LaunchArg>::RuntimeArg {
($($T::alloc(client, lens, handles),)*)
}
#[allow(non_snake_case)]
fn __expand_store(
scope: &Scope,
out: &mut <Self::Out as CubeType>::ExpandType,
value: Self::ExpandType,
) {
let ($($T,)*) = out;
let ($($t,)*) = value;
$(<$T as HarnessOutput>::__expand_store(scope, $T, $t);)*
}
fn read(
bytes: &mut impl Iterator<Item = Bytes>,
lens: &mut impl Iterator<Item = usize>,
) -> Self::Host {
($($T::read(bytes, lens),)*)
}
}
};
}
all_tuples!(harness_tuple, 1, 12, T, t);