use std::marker::PhantomData;
use std::ops::Range;
pub(crate) use super::super::lstm::layout::{
GATE_COLUMNS, GATES, HIDDEN, KERNEL, ONNX_GATE, Schedule, TILE_ROWS, pack_bias, pack_directions,
};
pub(crate) mod exchange;
pub(crate) use exchange::{PAD as STATE_PAD, TILE as STATE_TILE};
pub(crate) mod tiled_exchange;
const _: () = assert!(exchange::bulk_word(TILE_ROWS * HIDDEN - 1) < exchange::SLOT);
const _: () = assert!(
exchange::single_copy(
exchange::single_base(HIDDEN - 1),
exchange::COPIES - 1,
u32::MAX
) < exchange::SLOT
);
const _: () = assert!(tiled_exchange::LINE == HIDDEN / TILED_GROUPS);
const _: () = assert!(tiled_exchange::word(TILED_ROWS - 1, HIDDEN - 1) < tiled_exchange::SLOT);
const UNITS: usize = 8;
pub(crate) const THREADS: u32 = 32 * UNITS as u32;
pub(crate) const GROUPS: usize = HIDDEN / UNITS;
pub(crate) const TILED_KERNEL: &str = "spk_lstm_recurrence_tiled";
pub(crate) const TILED_GROUPS: usize = 8;
pub(crate) const TILED_THREADS: u32 = 256;
pub(crate) const TILED_ROWS: usize = tiled_exchange::ROWS;
pub(crate) const RESIDENT_KERNEL: &str = "spk_lstm_recurrence_resident";
pub(crate) const RESIDENT_THREADS: u32 = 256;
pub(crate) const RESIDENT_SHARED_BYTES: u32 = (GATE_COLUMNS * HIDDEN * size_of::<f32>() / 2) as u32;
pub(crate) const RESIDENT_STATIC_SHARED_BYTES: u32 = (2 * HIDDEN * size_of::<f32>()) as u32;
pub(crate) const TENSOR_SHARED_BYTES: u32 = 61_440;
#[derive(Debug, Clone)]
pub(crate) struct AlignedSpan<T>(Range<usize>, PhantomData<T>);
impl<T> AlignedSpan<T> {
const ALIGN_BYTES: usize = 2 * 1024 * 1024;
pub(crate) const fn allocation_len(len: usize) -> usize {
const { assert!(size_of::<T>() > 0 && Self::ALIGN_BYTES % size_of::<T>() == 0) };
len + Self::ALIGN_BYTES / size_of::<T>() - 1
}
pub(crate) fn new(address: u64, len: usize) -> Self {
const { assert!(size_of::<T>() > 0 && Self::ALIGN_BYTES % size_of::<T>() == 0) };
let remainder = address as usize % Self::ALIGN_BYTES;
let offset = (Self::ALIGN_BYTES - remainder) % Self::ALIGN_BYTES / size_of::<T>();
Self(offset..offset + len, PhantomData)
}
pub(crate) fn range(&self) -> Range<usize> {
self.0.clone()
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct ExchangeLayout(usize);
impl ExchangeLayout {
pub(crate) const fn new(tiles: usize, tile_words: usize) -> Self {
Self(tiles * tile_words + STATE_PAD)
}
pub(crate) const fn words(self) -> usize {
2 * self.0
}
pub(crate) const fn direction(self, direction: usize) -> usize {
direction * self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ProjectionLayout {
Simt,
Tensor,
}
pub(crate) const fn padded_input(columns: usize) -> usize {
columns.div_ceil(16) * 16
}
pub(crate) fn pack_input_directions(
source: &[f32],
columns: usize,
layout: ProjectionLayout,
) -> Vec<f32> {
let padded = padded_input(columns);
let direction_len = GATE_COLUMNS * padded;
let mut packed = vec![0.0; 2 * direction_len];
for direction in 0..2 {
for unit in 0..HIDDEN {
for (gate, onnx_gate) in ONNX_GATE.into_iter().enumerate() {
let column = unit * GATES + gate;
let source_row = (direction * GATE_COLUMNS + onnx_gate * HIDDEN + unit) * columns;
for k in 0..columns {
let value = source[source_row + k];
let (offset, value) = match layout {
ProjectionLayout::Simt => (k * GATE_COLUMNS + column, value),
ProjectionLayout::Tensor => (column * padded + k, round_tf32(value)),
};
packed[direction * direction_len + offset] = value;
}
}
}
}
packed
}
pub(crate) fn round_tf32(value: f32) -> f32 {
let bits = value.to_bits();
if bits & 0x7f80_0000 == 0x7f80_0000 {
return value;
}
f32::from_bits((bits + 0x0fff + ((bits >> 13) & 1)) & 0xffff_e000)
}