#![allow(unsafe_op_in_unsafe_fn, clippy::missing_safety_doc, clippy::needless_range_loop)]
use core::arch::asm;
use half::{bf16, f16};
#[repr(C)]
struct TileCfg {
palette: u8,
start_row: u8,
reserved: [u8; 14],
colsb: [u16; 16],
rows: [u8; 16],
}
impl TileCfg {
#[inline]
fn new() -> Self {
Self {
palette: 1,
start_row: 0,
reserved: [0; 14],
colsb: [0; 16],
rows: [0; 16],
}
}
#[inline]
fn set(&mut self, tile: usize, rows: u8, colsb: u16) {
self.rows[tile] = rows;
self.colsb[tile] = colsb;
}
}
macro_rules! tile_mma {
($(#[$doc:meta])* $name:ident, $ty:ty, $dot:literal) => {
$(#[$doc])*
#[inline]
pub unsafe fn $name<const M: usize, const N: usize, const K: usize>(
a: *const $ty,
lda: usize,
b: *const $ty,
ldb: usize,
c: *mut f32,
ldc: usize,
) {
let keven = K.next_multiple_of(2);
let pairs = keven / 2;
let mut apack = [[<$ty>::ZERO; 32]; 16];
for i in 0..M {
let row = a.add(i * lda);
for k in 0..K {
apack[i][k] = *row.add(k);
}
}
let mut bpack = [[<$ty>::ZERO; 32]; 16];
for p in 0..pairs {
let (k0, k1) = (2 * p, 2 * p + 1);
for n in 0..N {
bpack[p][2 * n] = *b.add(k0 * ldb + n);
bpack[p][2 * n + 1] = if k1 < K { *b.add(k1 * ldb + n) } else { <$ty>::ZERO };
}
}
let mut cfg = TileCfg::new();
cfg.set(0, M as u8, (N * 4) as u16); cfg.set(1, M as u8, (keven * 2) as u16); cfg.set(2, pairs as u8, (N * 4) as u16);
asm!(
"ldtilecfg [{cfg}]",
"tileloadd tmm0, [{c} + {ldc_b} * 1]",
"tileloadd tmm1, [{a} + {lda_b} * 1]",
"tileloadd tmm2, [{b} + {ldb_b} * 1]",
concat!($dot, " tmm0, tmm1, tmm2"), "tilestored [{c} + {ldc_b} * 1], tmm0",
"tilerelease",
cfg = in(reg) &cfg as *const TileCfg,
c = in(reg) c,
ldc_b = in(reg) ldc * 4,
a = in(reg) apack.as_ptr(),
lda_b = in(reg) keven * 2,
b = in(reg) bpack.as_ptr(),
ldb_b = in(reg) N * 4,
options(nostack),
);
}
};
}
tile_mma! {
mma_bf16, bf16, "tdpbf16ps"
}
tile_mma! {
mma_f16, f16, "tdpfp16ps"
}
#[cfg(feature = "std")]
pub fn is_supported() -> bool {
use std::sync::OnceLock;
static OK: OnceLock<bool> = OnceLock::new();
*OK.get_or_init(|| cpuid_amx_bf16() && request_xtiledata_perm())
}
#[cfg(feature = "std")]
pub fn is_supported_f16() -> bool {
use std::sync::OnceLock;
static OK: OnceLock<bool> = OnceLock::new();
*OK.get_or_init(|| cpuid_amx_fp16() && request_xtiledata_perm())
}
fn cpuid_amx_bf16() -> bool {
#[cfg(target_arch = "x86")]
use core::arch::x86::__cpuid_count;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::__cpuid_count;
let leaf7 = __cpuid_count(7, 0);
let amx_tile = leaf7.edx & (1 << 24) != 0;
let amx_bf16 = leaf7.edx & (1 << 22) != 0;
amx_tile && amx_bf16
}
fn cpuid_amx_fp16() -> bool {
#[cfg(target_arch = "x86")]
use core::arch::x86::__cpuid_count;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::__cpuid_count;
let amx_tile = __cpuid_count(7, 0).edx & (1 << 24) != 0;
let amx_fp16 = __cpuid_count(7, 1).eax & (1 << 21) != 0;
amx_tile && amx_fp16
}
#[cfg(all(feature = "std", target_os = "linux"))]
fn request_xtiledata_perm() -> bool {
use core::ffi::c_long;
const SYS_ARCH_PRCTL: c_long = 158;
const ARCH_REQ_XCOMP_PERM: c_long = 0x1023;
const XFEATURE_XTILEDATA: c_long = 18;
unsafe extern "C" {
fn syscall(num: c_long, ...) -> c_long;
}
unsafe { syscall(SYS_ARCH_PRCTL, ARCH_REQ_XCOMP_PERM, XFEATURE_XTILEDATA) == 0 }
}
#[cfg(all(feature = "std", not(target_os = "linux")))]
fn request_xtiledata_perm() -> bool {
false
}