use std::sync::OnceLock;
use tract_data::internal::*;
use crate::WeightType;
use crate::frame::mmm::{
EagerPackedInput, MMMInputFormat, MMMInputValue, PackedExoticFact, PackedMatrixStorage,
};
fn cpu_has_amx_bf16() -> bool {
if !std::is_x86_feature_detected!("avx512f") {
return false;
}
#[allow(unused_unsafe)]
let r = unsafe { std::arch::x86_64::__cpuid_count(7, 0) };
const AMX_BF16: u32 = 1 << 22;
const AMX_TILE: u32 = 1 << 24;
(r.edx & AMX_BF16) != 0 && (r.edx & AMX_TILE) != 0
}
pub fn has_amx_bf16() -> bool {
static GATE: OnceLock<bool> = OnceLock::new();
*GATE.get_or_init(|| cpu_has_amx_bf16() && super::amx::request_amx_tile_xcomp_perm())
}
#[inline]
pub fn f32_to_bf16_rne(x: f32) -> u16 {
let bits = x.to_bits();
if (bits & 0x7F80_0000) == 0x7F80_0000 && (bits & 0x007F_FFFF) != 0 {
((bits >> 16) as u16) | 0x0040
} else {
let lsb = (bits >> 16) & 1;
let rounding = 0x0000_7FFF + lsb;
(bits.wrapping_add(rounding) >> 16) as u16
}
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct PackedAmxBf16A {
pub r: usize,
pub align: usize,
}
impl PackedAmxBf16A {
pub fn new(r: usize) -> Self {
PackedAmxBf16A { r, align: 64 }
}
fn k_padded(&self, k: usize) -> usize {
k.div_ceil(32) * 32
}
fn panel(&self, k: usize) -> usize {
self.k_padded(k) * self.r * 2
}
pub fn single_panel_len(&self, k: usize) -> usize {
self.panel(k)
}
pub fn len(&self, k: usize, mn: usize) -> usize {
mn.div_ceil(self.r) * self.panel(k)
}
pub fn alignment(&self) -> usize {
self.align
}
pub fn pack_view(
&self,
t: &TensorView,
k_axis: usize,
mn_axis: usize,
) -> TractResult<Box<dyn MMMInputValue>> {
let k = t.shape()[k_axis];
let mn = t.shape()[mn_axis];
let kp = self.k_padded(k);
let pl = kp * self.r * 2; let panels = mn.div_ceil(self.r);
let st = t.strides();
let (ks, ms) = (st[k_axis], st[mn_axis]);
let mut blob = unsafe { Blob::new_for_size_and_align(panels * pl, self.align) };
blob.as_bytes_mut().fill(0);
unsafe {
let src = t.as_ptr_unchecked::<f32>();
let dst = blob.as_mut_ptr() as *mut u16;
for p in 0..panels {
let pw = self.r.min(mn - p * self.r);
let panel = dst.add(p * (kp * self.r)); let mn0 = (p * self.r) as isize;
for lm in 0..pw {
let drow = panel.add(lm * kp);
let srow_base = src.offset((mn0 + lm as isize) * ms);
for kk in 0..k {
let v = *srow_base.offset(kk as isize * ks);
*drow.add(kk) = f32_to_bf16_rne(v);
}
}
}
}
Ok(Box::new(EagerPackedInput {
fact: PackedExoticFact { format: Box::new(self.clone()), mn: mn.to_dim(), k },
packed: blob.into(),
panel_bytes: pl,
mn,
}))
}
}
impl std::fmt::Display for PackedAmxBf16A {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "AmxBf16A[{}]", self.r)
}
}
impl MMMInputFormat for PackedAmxBf16A {
fn prepare_tensor(&self, t: &Tensor, k_axis: usize, mn_axis: usize) -> TractResult<Tensor> {
Ok(PackedMatrixStorage::new(self.prepare_one(t, k_axis, mn_axis)?)
.into_tensor(t.datum_type()))
}
fn prepare_one(
&self,
t: &Tensor,
k_axis: usize,
mn_axis: usize,
) -> TractResult<Box<dyn MMMInputValue>> {
self.pack_view(&t.view(), k_axis, mn_axis)
}
fn k_alignment(&self) -> usize {
32
}
fn r(&self) -> usize {
self.r
}
fn precursor(&self) -> WeightType {
WeightType::Plain(f32::datum_type())
}
fn merge_with<'o, 'a: 'o, 'b: 'o>(
&'a self,
o: &'b dyn MMMInputFormat,
) -> Option<&'o dyn MMMInputFormat> {
o.downcast_ref::<PackedAmxBf16A>().filter(|x| x.r == self.r).map(|_| self as _)
}
fn mem_size(&self, k: TDim, mn: TDim) -> TDim {
mn.divceil(self.r) * self.panel(k.to_usize().unwrap_or(0))
}
fn extract_at_mn_f16(&self, _: &EagerPackedInput, _: usize, _: &mut [f16]) -> TractResult<()> {
bail!("no f16 extract")
}
fn extract_at_mn_f32(&self, _: &EagerPackedInput, _: usize, _: &mut [f32]) -> TractResult<()> {
bail!("no f32 extract")
}
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct PackedBf16K2 {
pub r: usize,
pub align: usize,
}
impl PackedBf16K2 {
pub fn new(r: usize) -> Self {
PackedBf16K2 { r, align: 64 }
}
fn k_padded(&self, k: usize) -> usize {
k.div_ceil(2) * 2
}
fn panel(&self, k: usize) -> usize {
self.k_padded(k) * self.r * 2
}
pub fn single_panel_len(&self, k: usize) -> usize {
self.panel(k)
}
pub fn len(&self, k: usize, mn: usize) -> usize {
mn.div_ceil(self.r) * self.panel(k)
}
pub fn alignment(&self) -> usize {
self.align
}
pub fn pack_view(
&self,
t: &TensorView,
k_axis: usize,
mn_axis: usize,
) -> TractResult<Box<dyn MMMInputValue>> {
let k = t.shape()[k_axis];
let mn = t.shape()[mn_axis];
let kp = self.k_padded(k);
let pl = kp * self.r * 2; let panels = mn.div_ceil(self.r);
let st = t.strides();
let mut blob = unsafe { Blob::new_for_size_and_align(panels * pl, self.align) };
blob.as_bytes_mut().fill(0);
let (ks, ms) = (st[k_axis], st[mn_axis]);
let kblocks = kp / 2;
unsafe {
let src = t.as_ptr_unchecked::<f32>();
let dst = blob.as_mut_ptr() as *mut u16;
for p in 0..panels {
let pw = self.r.min(mn - p * self.r);
let panel = dst.add(p * (kp * self.r));
let mn0 = (p * self.r) as isize;
for kb in 0..kblocks {
for ki in 0..2 {
let kk = kb * 2 + ki;
if kk >= k {
break;
}
let srow = src.offset(kk as isize * ks + mn0 * ms);
let dblock = panel.add(kb * self.r * 2 + ki);
for lm in 0..pw {
let v = *srow.offset(lm as isize * ms);
*dblock.add(lm * 2) = f32_to_bf16_rne(v);
}
}
}
}
}
Ok(Box::new(EagerPackedInput {
fact: PackedExoticFact { format: Box::new(self.clone()), mn: mn.to_dim(), k },
packed: blob.into(),
panel_bytes: pl,
mn,
}))
}
}
impl std::fmt::Display for PackedBf16K2 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Bf16K2[{}]", self.r)
}
}
impl MMMInputFormat for PackedBf16K2 {
fn prepare_tensor(&self, t: &Tensor, k_axis: usize, mn_axis: usize) -> TractResult<Tensor> {
Ok(PackedMatrixStorage::new(self.prepare_one(t, k_axis, mn_axis)?)
.into_tensor(t.datum_type()))
}
fn prepare_one(
&self,
t: &Tensor,
k_axis: usize,
mn_axis: usize,
) -> TractResult<Box<dyn MMMInputValue>> {
self.pack_view(&t.view(), k_axis, mn_axis)
}
fn k_alignment(&self) -> usize {
2
}
fn r(&self) -> usize {
self.r
}
fn precursor(&self) -> WeightType {
WeightType::Plain(f32::datum_type())
}
fn merge_with<'o, 'a: 'o, 'b: 'o>(
&'a self,
o: &'b dyn MMMInputFormat,
) -> Option<&'o dyn MMMInputFormat> {
o.downcast_ref::<PackedBf16K2>().filter(|x| x.r == self.r).map(|_| self as _)
}
fn mem_size(&self, k: TDim, mn: TDim) -> TDim {
mn.divceil(self.r) * self.panel(k.to_usize().unwrap_or(0))
}
fn extract_at_mn_f16(&self, _: &EagerPackedInput, _: usize, _: &mut [f16]) -> TractResult<()> {
bail!("no f16 extract")
}
fn extract_at_mn_f32(&self, _: &EagerPackedInput, _: usize, _: &mut [f32]) -> TractResult<()> {
bail!("no f32 extract")
}
}