#[cfg(feature = "std")]
use std::sync::OnceLock;
use super::isa::{ForcedIsa, forced_isa};
use super::orient_swap;
use crate::driver;
use crate::kernel::IntGemm;
#[cfg(feature = "epilogue")]
use crate::kernel::IntGemmQ;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
use crate::kernel::IntGemmVnni;
#[cfg(all(feature = "epilogue", any(target_arch = "x86", target_arch = "x86_64")))]
use crate::kernel::IntGemmVnniQ;
use crate::kernel::KernelFamily;
#[cfg(feature = "epilogue")]
use crate::kernel::epilogue::{BiasDim, BiasSpec, Epilogue, KRequantize, QuantOut, ScaleSpec};
use crate::parallel::Parallelism;
#[cfg(feature = "epilogue")]
use crate::parallel::Ptr;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
use crate::simd::Avx512Vnni;
use crate::simd::KernelSimd;
#[cfg(target_arch = "aarch64")]
use crate::simd::Neon;
use crate::simd::ScalarTok;
#[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
use crate::simd::Simd128;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
use crate::simd::{Avx512F, Fma};
use crate::special::{small_k, small_mn};
use crate::tuning;
use crate::workspace::Workspace;
#[cfg(feature = "int8")]
#[derive(Copy, Clone)]
pub(crate) struct IntTask {
pub m: usize,
pub k: usize,
pub n: usize,
pub alpha: i32,
pub a: *const i8,
pub rsa: isize,
pub csa: isize,
pub b: *const i8,
pub rsb: isize,
pub csb: isize,
pub beta: i32,
pub c: *mut i32,
pub rsc: isize,
pub csc: isize,
}
#[cfg(feature = "int8")]
pub(crate) struct IntPackedConsume {
pub m: usize,
pub k: usize,
pub n: usize,
pub alpha: i32,
pub a: *const i8,
pub rsa: isize,
pub csa: isize,
pub packed: *const i8,
pub nr: usize,
pub kc: usize,
pub nc: usize,
pub beta: i32,
pub c: *mut i32,
pub rsc: isize,
pub csc: isize,
}
#[cfg(feature = "int8")]
#[inline]
fn pick_int_kernel<F: Copy>(
par: Parallelism,
mnk: usize,
run: F,
small_par_fallback: Option<F>,
) -> F {
match small_par_fallback {
Some(fallback)
if matches!(par, Parallelism::Rayon(n) if n != 1)
&& mnk < tuning::i8_vnni_min_par_mnk() =>
{
fallback
}
_ => run,
}
}
#[cfg(feature = "int8")]
pub(crate) unsafe fn execute_int(t: IntTask, par: Parallelism, ws: &mut Workspace) {
unsafe {
if t.m == 0 || t.n == 0 {
return;
}
if t.k == 0 || t.alpha == 0 {
scale_c_int(t.beta, t.c, t.m, t.n, t.rsc, t.csc);
return;
}
let d = dispatched_i8();
let mnk = t.m.saturating_mul(t.n).saturating_mul(t.k);
let run = pick_int_kernel(par, mnk, d.run, d.small_par_fallback);
run(t, par, ws);
}
}
#[cfg(feature = "int8")]
unsafe fn scale_c_int(beta: i32, c: *mut i32, m: usize, n: usize, rsc: isize, csc: isize) {
unsafe {
for j in 0..n {
for i in 0..m {
let p = c.offset(i as isize * rsc + j as isize * csc);
if beta == 0 {
*p = 0;
} else if beta != 1 {
*p = beta.wrapping_mul(*p);
}
}
}
}
}
#[cfg(feature = "int8")]
#[inline]
unsafe fn run_typed_int<Fam, S, const MR_REG: usize, const NR: usize>(
simd: S,
mut t: IntTask,
par: Parallelism,
ws: &mut Workspace,
) where
Fam: KernelFamily<Lhs = i8, Rhs = i8, Acc = i32, Out = i32>,
S: KernelSimd<i8, i8, i32, i32>,
{
unsafe {
orient_swap(
&mut t.m, &mut t.n, &mut t.a, &mut t.rsa, &mut t.csa, &mut t.b, &mut t.rsb, &mut t.csb,
&mut t.rsc, &mut t.csc,
);
if super::small_mn_eligible_dims(t.m, t.n, t.k, t.csa, t.rsb)
|| super::small_mn_pack_eligible_dims(t.m, t.n, t.k, t.csa, t.rsb)
{
small_mn::run_int::<S>(
simd, t.m, t.k, t.n, par, ws, t.alpha, t.a, t.rsa, t.csa, t.b, t.rsb, t.csb,
t.beta, t.c, t.rsc, t.csc,
);
return;
}
if t.k <= tuning::small_k_threshold() {
small_k::run::<IntGemm, S, MR_REG, NR>(
simd, t.m, t.k, t.n, t.alpha, t.a, t.rsa, t.csa, t.b, t.rsb, t.csb, t.beta, t.c,
t.rsc, t.csc, par, ws,
);
return;
}
driver::run::<Fam, S, MR_REG, NR>(
simd, t.m, t.k, t.n, t.alpha, t.a, t.rsa, t.csa, t.b, t.rsb, t.csb, t.beta, t.c, t.rsc,
t.csc, par, ws,
);
}
}
#[cfg(feature = "int8")]
unsafe fn gemm_i8_scalar(t: IntTask, par: Parallelism, ws: &mut Workspace) {
unsafe { run_typed_int::<IntGemm, ScalarTok, 4, 4>(ScalarTok, t, par, ws) }
}
#[cfg(all(feature = "int8", any(target_arch = "x86", target_arch = "x86_64")))]
unsafe fn gemm_i8_fma(t: IntTask, par: Parallelism, ws: &mut Workspace) {
unsafe { run_typed_int::<IntGemm, Fma, 2, 6>(Fma, t, par, ws) }
}
#[cfg(all(feature = "int8", any(target_arch = "x86", target_arch = "x86_64")))]
unsafe fn gemm_i8_avx512f(t: IntTask, par: Parallelism, ws: &mut Workspace) {
unsafe { run_typed_int::<IntGemm, Avx512F, 2, 12>(Avx512F, t, par, ws) }
}
#[cfg(all(feature = "int8", any(target_arch = "x86", target_arch = "x86_64")))]
unsafe fn gemm_i8_avx512vnni(t: IntTask, par: Parallelism, ws: &mut Workspace) {
unsafe { run_typed_int::<IntGemmVnni, Avx512Vnni, 2, 12>(Avx512Vnni, t, par, ws) }
}
#[cfg(all(feature = "int8", target_arch = "aarch64"))]
unsafe fn gemm_i8_neon(t: IntTask, par: Parallelism, ws: &mut Workspace) {
unsafe { run_typed_int::<IntGemm, Neon, 4, 4>(Neon, t, par, ws) }
}
#[cfg(all(feature = "int8", target_arch = "wasm32", target_feature = "simd128"))]
unsafe fn gemm_i8_simd128(t: IntTask, par: Parallelism, ws: &mut Workspace) {
unsafe { run_typed_int::<IntGemm, Simd128, 2, 4>(Simd128, t, par, ws) }
}
#[cfg(feature = "int8")]
#[inline]
unsafe fn run_packed_typed_int<Fam, S, const MR_REG: usize, const NR: usize>(
simd: S,
req: IntPackedConsume,
par: Parallelism,
ws: &mut Workspace,
) where
Fam: KernelFamily<Lhs = i8, Rhs = i8, Acc = i32, Out = i32>,
S: KernelSimd<i8, i8, i32, i32>,
{
unsafe {
debug_assert_eq!(NR, req.nr, "prepacked RHS panel width != kernel NR");
driver::run_packed_rhs::<Fam, S, MR_REG, NR>(
simd, req.m, req.k, req.n, req.alpha, req.a, req.rsa, req.csa, req.packed, req.kc,
req.nc, req.beta, req.c, req.rsc, req.csc, par, ws,
);
}
}
#[cfg(feature = "int8")]
unsafe fn gemm_i8_scalar_packed(r: IntPackedConsume, par: Parallelism, ws: &mut Workspace) {
unsafe { run_packed_typed_int::<IntGemm, ScalarTok, 4, 4>(ScalarTok, r, par, ws) }
}
#[cfg(all(feature = "int8", any(target_arch = "x86", target_arch = "x86_64")))]
unsafe fn gemm_i8_fma_packed(r: IntPackedConsume, par: Parallelism, ws: &mut Workspace) {
unsafe { run_packed_typed_int::<IntGemm, Fma, 2, 6>(Fma, r, par, ws) }
}
#[cfg(all(feature = "int8", any(target_arch = "x86", target_arch = "x86_64")))]
unsafe fn gemm_i8_avx512f_packed(r: IntPackedConsume, par: Parallelism, ws: &mut Workspace) {
unsafe { run_packed_typed_int::<IntGemm, Avx512F, 2, 12>(Avx512F, r, par, ws) }
}
#[cfg(all(feature = "int8", any(target_arch = "x86", target_arch = "x86_64")))]
unsafe fn gemm_i8_avx512vnni_packed(r: IntPackedConsume, par: Parallelism, ws: &mut Workspace) {
unsafe { run_packed_typed_int::<IntGemmVnni, Avx512Vnni, 2, 12>(Avx512Vnni, r, par, ws) }
}
#[cfg(all(feature = "int8", target_arch = "aarch64"))]
unsafe fn gemm_i8_neon_packed(r: IntPackedConsume, par: Parallelism, ws: &mut Workspace) {
unsafe { run_packed_typed_int::<IntGemm, Neon, 4, 4>(Neon, r, par, ws) }
}
#[cfg(all(feature = "int8", target_arch = "wasm32", target_feature = "simd128"))]
unsafe fn gemm_i8_simd128_packed(r: IntPackedConsume, par: Parallelism, ws: &mut Workspace) {
unsafe { run_packed_typed_int::<IntGemm, Simd128, 2, 4>(Simd128, r, par, ws) }
}
#[cfg(feature = "int8")]
#[allow(clippy::too_many_arguments)]
unsafe fn pack_rhs_i8_widen(
dst: *mut i8,
b: *const i8,
rsb: isize,
csb: isize,
k: usize,
n: usize,
kc: usize,
nc: usize,
nr: usize,
) {
unsafe { driver::pack_rhs_full::<IntGemm>(dst, b, rsb, csb, k, n, kc, nc, nr) }
}
#[cfg(all(feature = "int8", any(target_arch = "x86", target_arch = "x86_64")))]
#[allow(clippy::too_many_arguments)]
unsafe fn pack_rhs_i8_vnni(
dst: *mut i8,
b: *const i8,
rsb: isize,
csb: isize,
k: usize,
n: usize,
kc: usize,
nc: usize,
nr: usize,
) {
unsafe { driver::pack_rhs_full::<IntGemmVnni>(dst, b, rsb, csb, k, n, kc, nc, nr) }
}
#[cfg(feature = "int8")]
type IntFn = unsafe fn(IntTask, Parallelism, &mut Workspace);
#[cfg(feature = "int8")]
type IntPackedFn = unsafe fn(IntPackedConsume, Parallelism, &mut Workspace);
#[cfg(feature = "int8")]
type IntPackFn = unsafe fn(*mut i8, *const i8, isize, isize, usize, usize, usize, usize, usize);
#[cfg(feature = "int8")]
#[derive(Copy, Clone)]
struct IntDispatched {
run: IntFn,
small_par_fallback: Option<IntFn>,
run_packed: IntPackedFn,
pack_rhs: IntPackFn,
mr: usize,
nr: usize,
depth_multiple: usize,
}
#[cfg(feature = "int8")]
const DISP_I8_SCALAR: IntDispatched = IntDispatched {
run: gemm_i8_scalar,
small_par_fallback: None,
run_packed: gemm_i8_scalar_packed,
pack_rhs: pack_rhs_i8_widen,
mr: 4,
nr: 4,
depth_multiple: 1,
};
#[cfg(all(feature = "int8", any(target_arch = "x86", target_arch = "x86_64")))]
const DISP_I8_FMA: IntDispatched = IntDispatched {
run: gemm_i8_fma,
small_par_fallback: None,
run_packed: gemm_i8_fma_packed,
pack_rhs: pack_rhs_i8_widen,
mr: 16,
nr: 6,
depth_multiple: 1,
};
#[cfg(all(feature = "int8", any(target_arch = "x86", target_arch = "x86_64")))]
const DISP_I8_AVX512F: IntDispatched = IntDispatched {
run: gemm_i8_avx512f,
small_par_fallback: None,
run_packed: gemm_i8_avx512f_packed,
pack_rhs: pack_rhs_i8_widen,
mr: 32,
nr: 12,
depth_multiple: 1,
};
#[cfg(all(feature = "int8", any(target_arch = "x86", target_arch = "x86_64")))]
const DISP_I8_AVX512VNNI: IntDispatched = IntDispatched {
run: gemm_i8_avx512vnni,
small_par_fallback: None,
run_packed: gemm_i8_avx512vnni_packed,
pack_rhs: pack_rhs_i8_vnni,
mr: 32,
nr: 12,
depth_multiple: 4,
};
#[cfg(all(feature = "int8", target_arch = "aarch64"))]
const DISP_I8_NEON: IntDispatched = IntDispatched {
run: gemm_i8_neon,
small_par_fallback: None,
run_packed: gemm_i8_neon_packed,
pack_rhs: pack_rhs_i8_widen,
mr: 16,
nr: 4,
depth_multiple: 1,
};
#[cfg(all(feature = "int8", target_arch = "wasm32", target_feature = "simd128"))]
const DISP_I8_SIMD128: IntDispatched = IntDispatched {
run: gemm_i8_simd128,
small_par_fallback: None,
run_packed: gemm_i8_simd128_packed,
pack_rhs: pack_rhs_i8_widen,
mr: 8,
nr: 4,
depth_multiple: 1,
};
#[cfg(feature = "int8")]
fn select_i8() -> IntDispatched {
match forced_isa() {
ForcedIsa::Scalar => return DISP_I8_SCALAR,
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
ForcedIsa::Fma => {
assert!(
x86_isa_detected!("avx2") && x86_isa_detected!("fma"),
"GEMMKIT_REQUIRE_ISA=fma, but this CPU/emulator does not report avx2+fma"
);
return DISP_I8_FMA;
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
ForcedIsa::Avx512F | ForcedIsa::Avx512Bf16 => {
assert!(
x86_isa_detected!("avx512f"),
"GEMMKIT_REQUIRE_ISA=avx512f, but this CPU/emulator does not report avx512f"
);
return DISP_I8_AVX512F;
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
ForcedIsa::Avx512Vnni => {
assert!(
x86_isa_detected!("avx512vnni")
&& x86_isa_detected!("avx512bw")
&& x86_isa_detected!("avx512f"),
"GEMMKIT_REQUIRE_ISA=avx512vnni, but this CPU/emulator does not report avx512f+bw+vnni"
);
return DISP_I8_AVX512VNNI;
}
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
ForcedIsa::Fma | ForcedIsa::Avx512F | ForcedIsa::Avx512Vnni | ForcedIsa::Avx512Bf16 => {
panic!("GEMMKIT_REQUIRE_ISA: requested SIMD ISA is unavailable on this target")
}
#[cfg(target_arch = "aarch64")]
ForcedIsa::Neon => return DISP_I8_NEON,
#[cfg(not(target_arch = "aarch64"))]
ForcedIsa::Neon => panic!("GEMMKIT_REQUIRE_ISA=neon, but this target is not aarch64"),
#[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
ForcedIsa::Simd128 => return DISP_I8_SIMD128,
#[cfg(not(all(target_arch = "wasm32", target_feature = "simd128")))]
ForcedIsa::Simd128 => panic!(
"GEMMKIT_REQUIRE_ISA=simd128, but this build is not wasm32 with -C target-feature=+simd128"
),
ForcedIsa::Auto => {}
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{
if x86_isa_detected!("avx512vnni")
&& x86_isa_detected!("avx512bw")
&& x86_isa_detected!("avx512f")
{
return IntDispatched {
small_par_fallback: Some(gemm_i8_avx512f),
..DISP_I8_AVX512VNNI
};
}
if x86_isa_detected!("avx512f") {
return DISP_I8_AVX512F;
}
if x86_isa_detected!("avx2") && x86_isa_detected!("fma") {
return DISP_I8_FMA;
}
}
#[cfg(target_arch = "aarch64")]
{
DISP_I8_NEON
}
#[cfg(target_arch = "wasm32")]
{
#[cfg(target_feature = "simd128")]
{
DISP_I8_SIMD128
}
#[cfg(not(target_feature = "simd128"))]
{
DISP_I8_SCALAR
}
}
#[cfg(not(any(target_arch = "aarch64", target_arch = "wasm32")))]
{
DISP_I8_SCALAR
}
}
memoized_select!(
GEMM_I8,
dispatched_i8,
IntDispatched,
select_i8,
"The memoized integer dispatch descriptor (selection runs once).",
"int8"
);
#[cfg(feature = "int8")]
pub(crate) fn i8_rhs_tile() -> (usize, usize) {
let d = dispatched_i8();
(d.mr, d.nr)
}
#[cfg(feature = "int8")]
pub(crate) fn i8_rhs_depth_multiple() -> usize {
dispatched_i8().depth_multiple
}
#[cfg(feature = "int8")]
#[allow(clippy::too_many_arguments)]
pub(crate) unsafe fn pack_rhs_full_i8(
dst: *mut i8,
b: *const i8,
rsb: isize,
csb: isize,
k: usize,
n: usize,
kc: usize,
nc: usize,
nr: usize,
) {
unsafe { (dispatched_i8().pack_rhs)(dst, b, rsb, csb, k, n, kc, nc, nr) }
}
#[cfg(feature = "int8")]
pub(crate) unsafe fn execute_int_packed(
req: IntPackedConsume,
par: Parallelism,
ws: &mut Workspace,
) {
unsafe {
if req.m == 0 || req.n == 0 {
return;
}
if req.k == 0 || req.alpha == 0 {
scale_c_int(req.beta, req.c, req.m, req.n, req.rsc, req.csc);
return;
}
(dispatched_i8().run_packed)(req, par, ws);
}
}
#[cfg(all(feature = "int8", feature = "epilogue"))]
#[derive(Copy, Clone)]
pub(crate) struct RequantTask<O> {
pub m: usize,
pub k: usize,
pub n: usize,
pub a: *const i8,
pub rsa: isize,
pub csa: isize,
pub b: *const i8,
pub rsb: isize,
pub csb: isize,
pub c: *mut O,
pub rsc: isize,
pub csc: isize,
pub scale: f32,
pub row_scales: *const f32,
pub has_row_scales: bool,
pub zp: i32,
pub bias: *const i32,
pub has_bias: bool,
pub bias_dim: BiasDim,
}
#[cfg(all(feature = "int8", feature = "epilogue"))]
pub(crate) unsafe fn execute_int_requant<O: RequantOut>(
t: RequantTask<O>,
par: Parallelism,
ws: &mut Workspace,
) {
unsafe {
if t.m == 0 || t.n == 0 {
return;
}
if t.k == 0 {
requant_degenerate(&t);
return;
}
let d = O::dispatched();
let mnk = t.m.saturating_mul(t.n).saturating_mul(t.k);
let run = pick_int_kernel(par, mnk, d.run, d.small_par_fallback);
run(t, par, ws);
}
}
#[cfg(all(feature = "int8", feature = "epilogue"))]
#[inline]
fn requant_bias_spec<O>(t: &RequantTask<O>) -> BiasSpec<i32> {
if t.has_bias {
let p = Ptr(t.bias as *mut i32);
match t.bias_dim {
BiasDim::PerRow => BiasSpec::Row(p),
BiasDim::PerCol => BiasSpec::Col(p),
}
} else {
BiasSpec::None
}
}
#[cfg(all(feature = "int8", feature = "epilogue"))]
#[inline]
fn requant_scale_spec<O>(t: &RequantTask<O>) -> ScaleSpec {
if t.has_row_scales {
let p = Ptr(t.row_scales as *mut f32);
match t.bias_dim {
BiasDim::PerRow => ScaleSpec::Row(p),
BiasDim::PerCol => ScaleSpec::Col(p),
}
} else {
ScaleSpec::Tensor(t.scale)
}
}
#[cfg(all(feature = "int8", feature = "epilogue"))]
unsafe fn requant_degenerate<O: QuantOut>(t: &RequantTask<O>) {
let epi = KRequantize {
scale: requant_scale_spec(t),
zp: t.zp,
bias: requant_bias_spec(t),
};
unsafe {
for j in 0..t.n {
for i in 0..t.m {
let out = <KRequantize as Epilogue<IntGemmQ<O>>>::apply(&epi, 0, i, j);
*t.c.offset(i as isize * t.rsc + j as isize * t.csc) = out;
}
}
}
}
#[cfg(all(feature = "int8", feature = "epilogue"))]
#[inline]
unsafe fn run_typed_int_requant<Fam, S, O, const MR_REG: usize, const NR: usize>(
simd: S,
mut t: RequantTask<O>,
par: Parallelism,
ws: &mut Workspace,
) where
O: QuantOut,
Fam: KernelFamily<Lhs = i8, Rhs = i8, Acc = i32, Out = O>,
S: KernelSimd<i8, i8, i32, O>,
{
unsafe {
let swap = orient_swap(
&mut t.m, &mut t.n, &mut t.a, &mut t.rsa, &mut t.csa, &mut t.b, &mut t.rsb, &mut t.csb,
&mut t.rsc, &mut t.csc,
);
if swap {
t.bias_dim = match t.bias_dim {
BiasDim::PerRow => BiasDim::PerCol,
BiasDim::PerCol => BiasDim::PerRow,
};
}
let epi = KRequantize {
scale: requant_scale_spec(&t),
zp: t.zp,
bias: requant_bias_spec(&t),
};
driver::run_epilogue::<Fam, S, KRequantize, MR_REG, NR>(
simd, t.m, t.k, t.n, 1, t.a, t.rsa, t.csa, t.b, t.rsb, t.csb, 0, t.c, t.rsc, t.csc,
&epi, par, ws,
);
}
}
#[cfg(all(feature = "int8", feature = "epilogue"))]
type RequantFn<O> = unsafe fn(RequantTask<O>, Parallelism, &mut Workspace);
#[cfg(all(feature = "int8", feature = "epilogue"))]
#[derive(Copy, Clone)]
pub(crate) struct IntRequantDispatched<O> {
run: RequantFn<O>,
small_par_fallback: Option<RequantFn<O>>,
}
#[cfg(all(feature = "int8", feature = "epilogue"))]
macro_rules! requant_dispatch {
(
$O:ty,
$w_scalar:ident, $w_fma:ident, $w_avx512f:ident, $w_vnni:ident, $w_neon:ident,
$w_simd128:ident,
$d_scalar:ident, $d_fma:ident, $d_avx512f:ident, $d_vnni:ident, $d_neon:ident,
$d_simd128:ident,
$select:ident, $slot:ident, $accessor:ident, $doc:literal
) => {
#[cfg(feature = "int8")]
unsafe fn $w_scalar(t: RequantTask<$O>, par: Parallelism, ws: &mut Workspace) {
unsafe { run_typed_int_requant::<IntGemmQ<$O>, ScalarTok, $O, 4, 4>(ScalarTok, t, par, ws) }
}
#[cfg(all(feature = "int8", any(target_arch = "x86", target_arch = "x86_64")))]
unsafe fn $w_fma(t: RequantTask<$O>, par: Parallelism, ws: &mut Workspace) {
unsafe { run_typed_int_requant::<IntGemmQ<$O>, Fma, $O, 2, 6>(Fma, t, par, ws) }
}
#[cfg(all(feature = "int8", any(target_arch = "x86", target_arch = "x86_64")))]
unsafe fn $w_avx512f(t: RequantTask<$O>, par: Parallelism, ws: &mut Workspace) {
unsafe { run_typed_int_requant::<IntGemmQ<$O>, Avx512F, $O, 2, 12>(Avx512F, t, par, ws) }
}
#[cfg(all(feature = "int8", any(target_arch = "x86", target_arch = "x86_64")))]
unsafe fn $w_vnni(t: RequantTask<$O>, par: Parallelism, ws: &mut Workspace) {
unsafe {
run_typed_int_requant::<IntGemmVnniQ<$O>, Avx512Vnni, $O, 2, 12>(
Avx512Vnni,
t,
par,
ws,
)
}
}
#[cfg(all(feature = "int8", target_arch = "aarch64"))]
unsafe fn $w_neon(t: RequantTask<$O>, par: Parallelism, ws: &mut Workspace) {
unsafe { run_typed_int_requant::<IntGemmQ<$O>, Neon, $O, 4, 4>(Neon, t, par, ws) }
}
#[cfg(all(feature = "int8", target_arch = "wasm32", target_feature = "simd128"))]
unsafe fn $w_simd128(t: RequantTask<$O>, par: Parallelism, ws: &mut Workspace) {
unsafe { run_typed_int_requant::<IntGemmQ<$O>, Simd128, $O, 2, 4>(Simd128, t, par, ws) }
}
#[cfg(feature = "int8")]
const $d_scalar: IntRequantDispatched<$O> = IntRequantDispatched {
run: $w_scalar,
small_par_fallback: None,
};
#[cfg(all(feature = "int8", any(target_arch = "x86", target_arch = "x86_64")))]
const $d_fma: IntRequantDispatched<$O> = IntRequantDispatched {
run: $w_fma,
small_par_fallback: None,
};
#[cfg(all(feature = "int8", any(target_arch = "x86", target_arch = "x86_64")))]
const $d_avx512f: IntRequantDispatched<$O> = IntRequantDispatched {
run: $w_avx512f,
small_par_fallback: None,
};
#[cfg(all(feature = "int8", any(target_arch = "x86", target_arch = "x86_64")))]
const $d_vnni: IntRequantDispatched<$O> = IntRequantDispatched {
run: $w_vnni,
small_par_fallback: None,
};
#[cfg(all(feature = "int8", target_arch = "aarch64"))]
const $d_neon: IntRequantDispatched<$O> = IntRequantDispatched {
run: $w_neon,
small_par_fallback: None,
};
#[cfg(all(feature = "int8", target_arch = "wasm32", target_feature = "simd128"))]
const $d_simd128: IntRequantDispatched<$O> = IntRequantDispatched {
run: $w_simd128,
small_par_fallback: None,
};
#[cfg(feature = "int8")]
fn $select() -> IntRequantDispatched<$O> {
match forced_isa() {
ForcedIsa::Scalar => return $d_scalar,
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
ForcedIsa::Fma => {
assert!(
x86_isa_detected!("avx2") && x86_isa_detected!("fma"),
"GEMMKIT_REQUIRE_ISA=fma, but this CPU/emulator does not report avx2+fma"
);
return $d_fma;
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
ForcedIsa::Avx512F | ForcedIsa::Avx512Bf16 => {
assert!(
x86_isa_detected!("avx512f"),
"GEMMKIT_REQUIRE_ISA=avx512f, but this CPU/emulator does not report avx512f"
);
return $d_avx512f;
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
ForcedIsa::Avx512Vnni => {
assert!(
x86_isa_detected!("avx512vnni")
&& x86_isa_detected!("avx512bw")
&& x86_isa_detected!("avx512f"),
"GEMMKIT_REQUIRE_ISA=avx512vnni, but this CPU/emulator does not report avx512f+bw+vnni"
);
return $d_vnni;
}
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
ForcedIsa::Fma
| ForcedIsa::Avx512F
| ForcedIsa::Avx512Vnni
| ForcedIsa::Avx512Bf16 => {
panic!("GEMMKIT_REQUIRE_ISA: requested SIMD ISA is unavailable on this target")
}
#[cfg(target_arch = "aarch64")]
ForcedIsa::Neon => return $d_neon,
#[cfg(not(target_arch = "aarch64"))]
ForcedIsa::Neon => {
panic!("GEMMKIT_REQUIRE_ISA=neon, but this target is not aarch64")
}
#[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
ForcedIsa::Simd128 => return $d_simd128,
#[cfg(not(all(target_arch = "wasm32", target_feature = "simd128")))]
ForcedIsa::Simd128 => panic!(
"GEMMKIT_REQUIRE_ISA=simd128, but this build is not wasm32 with -C target-feature=+simd128"
),
ForcedIsa::Auto => {}
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{
if x86_isa_detected!("avx512vnni")
&& x86_isa_detected!("avx512bw")
&& x86_isa_detected!("avx512f")
{
return IntRequantDispatched {
small_par_fallback: Some($w_avx512f),
..$d_vnni
};
}
if x86_isa_detected!("avx512f") {
return $d_avx512f;
}
if x86_isa_detected!("avx2") && x86_isa_detected!("fma") {
return $d_fma;
}
}
#[cfg(target_arch = "aarch64")]
{
$d_neon
}
#[cfg(target_arch = "wasm32")]
{
#[cfg(target_feature = "simd128")]
{
$d_simd128
}
#[cfg(not(target_feature = "simd128"))]
{
$d_scalar
}
}
#[cfg(not(any(target_arch = "aarch64", target_arch = "wasm32")))]
{
$d_scalar
}
}
memoized_select!($slot, $accessor, IntRequantDispatched<$O>, $select, $doc, "int8");
};
}
#[cfg(all(feature = "int8", feature = "epilogue"))]
requant_dispatch!(
i8,
requant_i8_scalar,
requant_i8_fma,
requant_i8_avx512f,
requant_i8_vnni,
requant_i8_neon,
requant_i8_simd128,
RDISP_I8_SCALAR,
RDISP_I8_FMA,
RDISP_I8_AVX512F,
RDISP_I8_VNNI,
RDISP_I8_NEON,
RDISP_I8_SIMD128,
select_requant_i8,
GEMM_REQUANT_I8,
dispatched_requant_i8,
"The memoized `i8`-output requantizing dispatch descriptor (selection runs once)."
);
#[cfg(all(feature = "int8", feature = "epilogue"))]
requant_dispatch!(
u8,
requant_u8_scalar,
requant_u8_fma,
requant_u8_avx512f,
requant_u8_vnni,
requant_u8_neon,
requant_u8_simd128,
RDISP_U8_SCALAR,
RDISP_U8_FMA,
RDISP_U8_AVX512F,
RDISP_U8_VNNI,
RDISP_U8_NEON,
RDISP_U8_SIMD128,
select_requant_u8,
GEMM_REQUANT_U8,
dispatched_requant_u8,
"The memoized `u8`-output requantizing dispatch descriptor (selection runs once)."
);
#[cfg(all(feature = "int8", feature = "epilogue"))]
pub(crate) trait RequantOut: QuantOut {
fn dispatched() -> IntRequantDispatched<Self>
where
Self: Sized;
}
#[cfg(all(feature = "int8", feature = "epilogue"))]
impl RequantOut for i8 {
#[inline]
fn dispatched() -> IntRequantDispatched<i8> {
dispatched_requant_i8()
}
}
#[cfg(all(feature = "int8", feature = "epilogue"))]
impl RequantOut for u8 {
#[inline]
fn dispatched() -> IntRequantDispatched<u8> {
dispatched_requant_u8()
}
}