use crate::hptt::micro_kernel::{MicroKernel, ScalarKernel};
#[inline]
pub unsafe fn macro_kernel_f64(
src: *const f64,
lda: isize,
block_a: usize,
dst: *mut f64,
ldb: isize,
block_b: usize,
) {
const MICRO: usize = <ScalarKernel as MicroKernel<f64>>::MICRO;
let full_a = block_a / MICRO;
let rem_a = block_a % MICRO;
let full_b = block_b / MICRO;
let rem_b = block_b % MICRO;
for jb in 0..full_b {
let j = (jb * MICRO) as isize;
for ia in 0..full_a {
let i = (ia * MICRO) as isize;
ScalarKernel::transpose_micro(
src.offset(i + j * lda),
lda,
dst.offset(j + i * ldb),
ldb,
);
}
if rem_a > 0 {
let i = (full_a * MICRO) as isize;
for jj in 0..MICRO as isize {
for ii in 0..rem_a as isize {
*dst.offset((j + jj) + (i + ii) * ldb) = *src.offset((i + ii) + (j + jj) * lda);
}
}
}
}
if rem_b > 0 {
let j = (full_b * MICRO) as isize;
for ia in 0..full_a {
let i = (ia * MICRO) as isize;
for jj in 0..rem_b as isize {
for ii in 0..MICRO as isize {
*dst.offset((j + jj) + (i + ii) * ldb) = *src.offset((i + ii) + (j + jj) * lda);
}
}
}
if rem_a > 0 {
let i = (full_a * MICRO) as isize;
for jj in 0..rem_b as isize {
for ii in 0..rem_a as isize {
*dst.offset((j + jj) + (i + ii) * ldb) = *src.offset((i + ii) + (j + jj) * lda);
}
}
}
}
}
#[inline]
pub unsafe fn macro_kernel_f32(
src: *const f32,
lda: isize,
block_a: usize,
dst: *mut f32,
ldb: isize,
block_b: usize,
) {
const MICRO: usize = <ScalarKernel as MicroKernel<f32>>::MICRO;
let full_a = block_a / MICRO;
let rem_a = block_a % MICRO;
let full_b = block_b / MICRO;
let rem_b = block_b % MICRO;
for jb in 0..full_b {
let j = (jb * MICRO) as isize;
for ia in 0..full_a {
let i = (ia * MICRO) as isize;
ScalarKernel::transpose_micro(
src.offset(i + j * lda),
lda,
dst.offset(j + i * ldb),
ldb,
);
}
if rem_a > 0 {
let i = (full_a * MICRO) as isize;
for jj in 0..MICRO as isize {
for ii in 0..rem_a as isize {
*dst.offset((j + jj) + (i + ii) * ldb) = *src.offset((i + ii) + (j + jj) * lda);
}
}
}
}
if rem_b > 0 {
let j = (full_b * MICRO) as isize;
for ia in 0..full_a {
let i = (ia * MICRO) as isize;
for jj in 0..rem_b as isize {
for ii in 0..MICRO as isize {
*dst.offset((j + jj) + (i + ii) * ldb) = *src.offset((i + ii) + (j + jj) * lda);
}
}
}
if rem_a > 0 {
let i = (full_a * MICRO) as isize;
for jj in 0..rem_b as isize {
for ii in 0..rem_a as isize {
*dst.offset((j + jj) + (i + ii) * ldb) = *src.offset((i + ii) + (j + jj) * lda);
}
}
}
}
}
#[inline(always)]
pub unsafe fn const_stride1_copy<T: Copy>(
src: *const T,
dst: *mut T,
count: usize,
src_stride: isize,
dst_stride: isize,
) {
if src_stride == 1 && dst_stride == 1 {
std::ptr::copy_nonoverlapping(src, dst, count);
} else if dst_stride == 1 {
let mut s = src;
let mut d = dst;
for _ in 0..count {
*d = *s;
s = s.offset(src_stride);
d = d.add(1);
}
} else if src_stride == 1 {
let mut s = src;
let mut d = dst;
for _ in 0..count {
*d = *s;
s = s.add(1);
d = d.offset(dst_stride);
}
} else {
let mut s = src;
let mut d = dst;
for _ in 0..count {
*d = *s;
s = s.offset(src_stride);
d = d.offset(dst_stride);
}
}
}
#[inline]
pub unsafe fn macro_kernel_fallback<T: Copy>(
src: *const T,
lda: isize,
block_a: usize,
dst: *mut T,
ldb: isize,
block_b: usize,
) {
for j in 0..block_b as isize {
for i in 0..block_a as isize {
*dst.offset(j + i * ldb) = *src.offset(i + j * lda);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_macro_kernel_f64_full_block() {
let n = 16;
let src: Vec<f64> = (0..n * n).map(|i| i as f64).collect();
let mut dst = vec![0.0f64; n * n];
unsafe {
macro_kernel_f64(src.as_ptr(), n as isize, n, dst.as_mut_ptr(), n as isize, n);
}
for j in 0..n {
for i in 0..n {
assert_eq!(dst[j + i * n], src[i + j * n], "mismatch at i={i}, j={j}");
}
}
}
#[test]
fn test_macro_kernel_f64_with_remainder() {
let block_a = 15;
let block_b = 17;
let lda = 20isize; let ldb = 18isize;
let src: Vec<f64> = (0..(block_a as isize * 1 + (block_b - 1) as isize * lda + 1) as usize)
.map(|i| i as f64)
.collect();
let mut dst =
vec![0.0f64; ((block_b - 1) as isize * 1 + (block_a - 1) as isize * ldb + 1) as usize];
unsafe {
macro_kernel_f64(src.as_ptr(), lda, block_a, dst.as_mut_ptr(), ldb, block_b);
}
for j in 0..block_b {
for i in 0..block_a {
let s = src[(i as isize + j as isize * lda) as usize];
let d = dst[(j as isize + i as isize * ldb) as usize];
assert_eq!(d, s, "mismatch at i={i}, j={j}");
}
}
}
#[test]
fn test_macro_kernel_f64_small() {
let block_a = 3;
let block_b = 5;
let lda = 8isize;
let ldb = 6isize;
let src_len = ((block_a - 1) as isize + (block_b - 1) as isize * lda + 1) as usize;
let dst_len = ((block_b - 1) as isize + (block_a - 1) as isize * ldb + 1) as usize;
let src: Vec<f64> = (0..src_len).map(|i| i as f64).collect();
let mut dst = vec![0.0f64; dst_len];
unsafe {
macro_kernel_f64(src.as_ptr(), lda, block_a, dst.as_mut_ptr(), ldb, block_b);
}
for j in 0..block_b {
for i in 0..block_a {
let s = src[(i as isize + j as isize * lda) as usize];
let d = dst[(j as isize + i as isize * ldb) as usize];
assert_eq!(d, s, "mismatch at i={i}, j={j}");
}
}
}
#[test]
fn test_const_stride1_copy_contiguous() {
let src = vec![1.0f64, 2.0, 3.0, 4.0, 5.0];
let mut dst = vec![0.0f64; 5];
unsafe {
const_stride1_copy(src.as_ptr(), dst.as_mut_ptr(), 5, 1, 1);
}
assert_eq!(dst, src);
}
#[test]
fn test_const_stride1_copy_strided() {
let src = vec![1.0f64, 0.0, 2.0, 0.0, 3.0];
let mut dst = vec![0.0f64; 5];
unsafe {
const_stride1_copy(src.as_ptr(), dst.as_mut_ptr(), 3, 2, 1);
}
assert_eq!(dst[0], 1.0);
assert_eq!(dst[1], 2.0);
assert_eq!(dst[2], 3.0);
}
}