Working on L4, now on swiss. FMA_TILE is re arranged.

This commit is contained in:
Youmu Liu
2026-03-03 18:02:31 +08:00
parent ae1a49d6fb
commit 91e136f81f
12 changed files with 1083 additions and 477 deletions
+240 -153
View File
@@ -6,7 +6,7 @@ static SIMDID global_simdid = { 0 };
#if SN_IS_ARCH(SN_ARCH_X86_64)
#include "sn_intrincontext.x86.h"
#include "sn_intrinsic.context.x86.h"
#include <immintrin.h>
#include <string.h>
@@ -57,15 +57,44 @@ static int xgetbv_has_avx512_state(void)
return ((xcr0 & mask) == mask);
}
static void decode_family_model(int cpuid1_eax, int *family, int *model)
{
int family_value = 0;
int model_value = 0;
const int base_fam = (cpuid1_eax >> 8) & 0xF;
const int ext_fam = (cpuid1_eax >> 20) & 0xFF;
const int base_model = (cpuid1_eax >> 4) & 0xF;
const int ext_model = (cpuid1_eax >> 16) & 0xF;
if (base_fam == 0xF) {
family_value = base_fam + ext_fam;
} else {
family_value = base_fam;
}
if ((base_fam == 0x6) || (base_fam == 0xF)) {
model_value = (ext_model << 4) | base_model;
} else {
model_value = base_model;
}
*family = family_value;
*model = model_value;
}
/* AVX-512 execution-width classification for AMD, by CPU family.
* Zen4 (0x19): single 256-bit ALU, 512-bit op = two 256-bit micro-ops (dual-pump) → DUAL.
* Zen5+ (>=0x1A): native 512-bit execution units → NATIVE.
* Other/future AMD families → UNKNOWN (conservative). */
static uint64_t amd_avx512_impl(int family)
{
if (family == 0x19) return SIMDID_AVX512_IMPL_DUAL;
if (family >= 0x1A) return SIMDID_AVX512_IMPL_NATIVE;
return SIMDID_AVX512_IMPL_UNKNOWN;
if (family == 0x19) {
return SIMDID_AVX512_IMPL_DUAL;
} else if (family >= 0x1A) {
return SIMDID_AVX512_IMPL_NATIVE;
} else {
return SIMDID_AVX512_IMPL_UNKNOWN;
}
}
/* Returns 1 if this AMD/Hygon CPU uses split-lane YMM (two fused 128-bit execution units).
@@ -74,194 +103,241 @@ static uint64_t amd_avx512_impl(int family)
* - AMD Zen2+ (family 0x17 model >= 0x30, family 0x19+) have native 256-bit execution. */
static int is_amd_glue_ymm(const char *vendor, int cpuid1_eax)
{
if (strcmp(vendor, "HygonGenuine") == 0)
return 1; /* Hygon is Zen1-based; always split YMM */
if (strcmp(vendor, "AuthenticAMD") != 0)
int family = 0;
int model = 0;
if (strcmp(vendor, "HygonGenuine") == 0) {
return 1;
}
if (strcmp(vendor, "AuthenticAMD") != 0) {
return 0;
int base_fam = (cpuid1_eax >> 8) & 0xF;
int ext_fam = (cpuid1_eax >> 20) & 0xFF;
int disp_fam = (base_fam < 0xF) ? base_fam : (base_fam + ext_fam);
int base_mdl = (cpuid1_eax >> 4) & 0xF;
int ext_mdl = (cpuid1_eax >> 16) & 0xF;
int disp_mdl = (ext_mdl << 4) | base_mdl;
return (disp_fam == 0x17) && (disp_mdl < 0x30); /* Zen1/Zen+ */
}
decode_family_model(cpuid1_eax, &family, &model);
return (family == 0x17) && (model < 0x30);
}
SN_API SIMDID SN_DECL sn_get_simd_level_local()
{
SIMDID _simdid = { 0 };
SIMDID simdid = { 0 };
int eax = 0, ebx = 0, ecx = 0, edx = 0;
int eax = 0;
int ebx = 0;
int ecx = 0;
int edx = 0;
int family = 0;
int model = 0;
/* Vendor */
get_cpuid(0, 0, &eax, &ebx, &ecx, &edx);
const int max_basic_leaf = eax;
char vendor[13];
((int *)vendor)[0] = ebx;
((int *)vendor)[1] = edx;
((int *)vendor)[2] = ecx;
memcpy(vendor + 0, &ebx, 4);
memcpy(vendor + 4, &edx, 4);
memcpy(vendor + 8, &ecx, 4);
vendor[12] = '\0';
const int is_intel = (strcmp(vendor, "GenuineIntel") == 0);
const int is_amd = (strcmp(vendor, "AuthenticAMD") == 0);
const int is_hygon = (strcmp(vendor, "HygonGenuine") == 0); /* 海光: separate from AMD */
const int is_via = (strcmp(vendor, "CentaurHauls") == 0) ||
(memcmp(vendor, " Shanghai ", 12) == 0); /* VIA / Zhaoxin */
const int is_amd = (strcmp(vendor, "AuthenticAMD") == 0);
const int is_hygon = (strcmp(vendor, "HygonGenuine") == 0);
const int is_centaur = (strcmp(vendor, "CentaurHauls") == 0);
const int is_zhaoxin = (memcmp(vendor, " Shanghai ", 12) == 0);
const int is_via = is_centaur || is_zhaoxin;
/* CPUID.1 */
get_cpuid(1, 0, &eax, &ebx, &ecx, &edx);
int cpuid1_eax = eax; /* saved for Zen1/Zen+ glue-YMM detection */
int family = ((eax >> 8) & 0xF) + ((eax >> 20) & 0xFF0);
int model = ((eax >> 4) & 0xF) | ((eax >> 12) & 0xF0);
const int cpuid1_eax = eax;
decode_family_model(cpuid1_eax, &family, &model);
(void)model;
int has_sse42 = (ecx & (1 << 20)) != 0;
int has_avx = (ecx & (1 << 28)) != 0;
int has_osxsave = (ecx & (1 << 27)) != 0;
int has_fma = (ecx & (1 << 12)) != 0;
int has_f16c = (ecx & (1 << 29)) != 0;
const int has_sse42 = (ecx & (1 << 20)) != 0;
const int has_avx = (ecx & (1 << 28)) != 0;
const int has_osxsave = (ecx & (1 << 27)) != 0;
const int has_fma = (ecx & (1 << 12)) != 0;
const int has_f16c = (ecx & (1 << 29)) != 0;
const uint64_t root = SIMDID_PACK_ROOT(SIMDID_ROOT_X86_64);
uint64_t manu_code = SIMDID_MANU_UNKNOWN;
if (is_intel) {
manu_code = SIMDID_MANU_INTEL;
} else if (is_amd) {
manu_code = SIMDID_MANU_AMD;
} else if (is_hygon) {
manu_code = SIMDID_MANU_HYGON;
} else if (is_via) {
manu_code = SIMDID_MANU_VIA;
}
const uint64_t manu = SIMDID_PACK_MANU(manu_code);
uint64_t root = SIMDID_PACK_ROOT(SIMDID_ROOT_X86_64);
uint64_t manu = SIMDID_PACK_MANU(is_intel ? SIMDID_MANU_INTEL :
is_amd ? SIMDID_MANU_AMD :
is_hygon ? SIMDID_MANU_HYGON :
is_via ? SIMDID_MANU_VIA :
SIMDID_MANU_UNKNOWN);
uint64_t main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_NONE);
uint64_t avx_bits = 0;
uint64_t avx512_bits = 0;
uint64_t amx_bits = 0;
uint64_t avx_bits = 0;
uint64_t avx10_bits = 0;
int has_hybrid = 0; /* filled from CPUID.7.0 EDX[15] when AVX present */
uint64_t uarch_bits = 0;
int has_hybrid = 0;
/* Baseline main level */
if (has_sse42)
if (has_sse42) {
main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_SSE);
}
if (has_avx && has_osxsave && xgetbv_has_xmm_ymm()) {
avx_bits |= SIMDID_AVX;
/* Glued/split-lane YMM: early AMD Zen1/Zen+, Hygon, and VIA/Zhaoxin */
if (is_amd_glue_ymm(vendor, cpuid1_eax) || is_via)
if (is_amd_glue_ymm(vendor, cpuid1_eax) || is_via) {
avx_bits |= SIMDID_AVX_DUAL;
/* CPUID.7.0 */
get_cpuid(7, 0, &eax, &ebx, &ecx, &edx);
const int has_bmi1 = (ebx & (1 << 3)) != 0;
const int has_bmi2 = (ebx & (1 << 8)) != 0;
const int has_avx2 = (ebx & (1 << 5)) != 0;
const int has_avx512f = (ebx & (1 << 16)) != 0;
const int has_avx512dq = (ebx & (1 << 17)) != 0;
const int has_avx512bw = (ebx & (1 << 30)) != 0;
const int has_avx512vl = (ebx & (1u << 31)) != 0;
const int has_avx512_vnni = (ebx & (1 << 11)) != 0;
const int has_vbmi = (ecx & (1 << 1)) != 0;
const int has_vbmi2 = (ecx & (1 << 6)) != 0;
/* AMX in leaf 7 subleaf 0 EDX bits 24/25/26 — capture before
overwriting regs with subleaf 1 */
const int has_amx_tile = (edx & (1 << 24)) != 0;
const int has_amx_int8 = (edx & (1 << 25)) != 0;
const int has_amx_bf16 = (edx & (1 << 22)) != 0;
has_hybrid = (edx & (1 << 15)) != 0; /* Intel hybrid core topology */
/* leaf 7, subleaf 1 for BF16/FP16/AVX10 convergence */
get_cpuid(7, 1, &eax, &ebx, &ecx, &edx);
const int has_bf16 = (eax & (1 << 5)) != 0; /* AVX512_BF16 */
const int has_fp16 = (eax & (1 << 23)) != 0; /* AVX512_FP16 */
const int has_avx10 = (edx & (1 << 19)) != 0; /* AVX10 convergence */
if (has_avx2) {
avx_bits |= SIMDID_AVX2;
if (has_fma)
avx_bits |= SIMDID_AVX_FMA;
if (has_f16c)
avx_bits |= SIMDID_AVX_F16C;
main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX2);
}
if (has_avx512f && xgetbv_has_avx512_state()) {
/* Ensure base subset present */
if (has_avx512bw && has_avx512vl && has_avx512dq) {
avx512_bits |= SIMDID_AVX512_BASE;
}
if (has_avx512_vnni)
avx512_bits |= SIMDID_AVX512_VNNI;
if (has_bf16)
avx512_bits |= SIMDID_AVX512_BF16;
if (has_fp16)
avx512_bits |= SIMDID_AVX512_FP16;
if (has_vbmi)
avx512_bits |= SIMDID_AVX512_VBMI;
if (has_vbmi2)
avx512_bits |= SIMDID_AVX512_VBMI2;
if (max_basic_leaf >= 7) {
get_cpuid(7, 0, &eax, &ebx, &ecx, &edx);
/* Execution-width classification:
* Intel all → NATIVE (genuine 512-bit ALU; SKX dual-port is native)
* AMD Zen4 → DUAL (dual-pump 256-bit, Family 0x19)
* AMD Zen5+ → NATIVE (native 512-bit, Family >= 0x1A)
* Hygon/VIA/unknown → UNKNOWN (conservative) */
if (is_intel)
avx512_bits |= SIMDID_AVX512_IMPL_NATIVE;
else if (is_amd)
avx512_bits |= amd_avx512_impl(family);
else
avx512_bits |= SIMDID_AVX512_IMPL_UNKNOWN;
const int has_avx2 = (ebx & (1 << 5)) != 0;
const int has_avx512f = (ebx & (1 << 16)) != 0;
const int has_avx512dq = (ebx & (1 << 17)) != 0;
const int has_avx512bw = (ebx & (1 << 30)) != 0;
const int has_avx512vl = (ebx & (1u << 31)) != 0;
const int has_avx512_vnni = (ecx & (1 << 11)) != 0;
const int has_vbmi = (ecx & (1 << 1)) != 0;
const int has_vbmi2 = (ecx & (1 << 6)) != 0;
/* main level for AVX512 */
main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX512);
const int has_amx_tile = (edx & (1 << 24)) != 0;
const int has_amx_int8 = (edx & (1 << 25)) != 0;
const int has_amx_bf16 = (edx & (1 << 22)) != 0;
has_hybrid = (edx & (1 << 15)) != 0;
/* AMX promotion if present */
if (has_amx_tile) {
amx_bits |= SIMDID_AMX_TILE;
if (has_amx_int8)
amx_bits |= SIMDID_AMX_INT8;
if (has_amx_bf16)
amx_bits |= SIMDID_AMX_BF16;
main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AMX);
int has_bf16 = 0;
int has_fp16 = 0;
int has_avx10 = 0;
if (eax >= 1) {
get_cpuid(7, 1, &eax, &ebx, &ecx, &edx);
has_bf16 = (eax & (1 << 5)) != 0;
has_fp16 = (eax & (1 << 23)) != 0;
has_avx10 = (edx & (1 << 19)) != 0;
}
/* AVX10 detection — requires AVX10 convergence bit
(leaf 7 subleaf 1 EDX[19]) and leaf 0x24 */
if (has_avx10) {
int a10_eax = 0, a10_ebx = 0, a10_ecx = 0, a10_edx = 0;
get_cpuid(0x24, 0, &a10_eax, &a10_ebx, &a10_ecx, &a10_edx);
const int avx10_ver = a10_ebx & 0xFF;
if (avx10_ver >= 1) {
avx10_bits |= SIMDID_AVX10 | SIMDID_AVX10_V1;
if (avx10_ver >= 2)
avx10_bits |= SIMDID_AVX10_V2;
if (a10_ebx & (1 << 17))
avx10_bits |= SIMDID_AVX10_256W;
if (a10_ebx & (1 << 18))
avx10_bits |= SIMDID_AVX10_512W;
main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX10);
if (has_avx2) {
avx_bits |= SIMDID_AVX2;
if (has_fma) {
avx_bits |= SIMDID_AVX_FMA;
}
if (has_f16c) {
avx_bits |= SIMDID_AVX_F16C;
}
main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX2);
}
if (has_avx512f && xgetbv_has_avx512_state()) {
if (has_avx512bw && has_avx512vl && has_avx512dq) {
avx512_bits |= SIMDID_AVX512_BASE;
}
if (has_avx512_vnni) {
avx512_bits |= SIMDID_AVX512_VNNI;
}
if (has_bf16) {
avx512_bits |= SIMDID_AVX512_BF16;
}
if (has_fp16) {
avx512_bits |= SIMDID_AVX512_FP16;
}
if (has_vbmi) {
avx512_bits |= SIMDID_AVX512_VBMI;
}
if (has_vbmi2) {
avx512_bits |= SIMDID_AVX512_VBMI2;
}
if (is_intel) {
avx512_bits |= SIMDID_AVX512_IMPL_NATIVE;
} else if (is_amd) {
avx512_bits |= amd_avx512_impl(family);
} else {
avx512_bits |= SIMDID_AVX512_IMPL_UNKNOWN;
}
main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX512);
if (has_amx_tile) {
amx_bits |= SIMDID_AMX_TILE;
if (has_amx_int8) {
amx_bits |= SIMDID_AMX_INT8;
}
if (has_amx_bf16) {
amx_bits |= SIMDID_AMX_BF16;
}
main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AMX);
}
if (has_avx10 && (max_basic_leaf >= 0x24)) {
int a10_eax = 0;
int a10_ebx = 0;
int a10_ecx = 0;
int a10_edx = 0;
int avx10_ver = 0;
int has_256w = 0;
int has_512w = 0;
get_cpuid(0x24, 0, &a10_eax, &a10_ebx, &a10_ecx, &a10_edx);
avx10_ver = a10_ebx & 0xFF;
if (avx10_ver >= 1) {
avx10_bits |= SIMDID_AVX10 | SIMDID_AVX10_V1;
if (avx10_ver >= 2) {
avx10_bits |= SIMDID_AVX10_V2;
}
has_256w = (a10_ebx & (1 << 17)) != 0;
has_512w = (a10_ebx & (1 << 18)) != 0;
if (has_256w) {
avx10_bits |= SIMDID_AVX10_256W;
}
if (has_512w) {
avx10_bits |= SIMDID_AVX10_512W;
}
main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX10);
}
}
}
}
}
/* uArchHi: hybrid core topology and SIMD-dual summary */
uint64_t uarch_bits = 0;
if (has_hybrid) {
int h_eax = 0, h_ebx = 0, h_ecx = 0, h_edx = 0;
if (has_hybrid && (max_basic_leaf >= 0x1A)) {
int h_eax = 0;
int h_ebx = 0;
int h_ecx = 0;
int h_edx = 0;
get_cpuid(0x1A, 0, &h_eax, &h_ebx, &h_ecx, &h_edx);
unsigned core_type = (unsigned)(h_eax >> 24) & 0xFFU;
if (core_type == 0x40) /* Intel Core / P-core → 10 */
uarch_bits |= SIMDID_UARCH_HYBRID_P;
else if (core_type == 0x20) /* Intel Atom / E-core → 01 */
uarch_bits |= SIMDID_UARCH_HYBRID_E;
/* else unknown hybrid core type → topology stays 00 */
{
const unsigned core_type = (unsigned)(h_eax >> 24) & 0xFFU;
if (core_type == 0x40U) {
uarch_bits |= SIMDID_UARCH_HYBRID_P;
} else if (core_type == 0x20U) {
uarch_bits |= SIMDID_UARCH_HYBRID_E;
}
}
} else {
/* Confirmed non-hybrid (unified) → set both bits = 11 (numerically strongest) */
uarch_bits |= SIMDID_UARCH_HYBRID_P | SIMDID_UARCH_HYBRID_E;
}
if ((avx_bits & SIMDID_AVX_DUAL) ||
((avx512_bits & SIMDID_AVX512_IMPL_MASK) == SIMDID_AVX512_IMPL_DUAL))
uarch_bits |= SIMDID_UARCH_SIMD_DUAL;
_simdid.mask = root | manu | main_lvl | SIMDID_PACK_AVX512(avx512_bits) |
SIMDID_PACK_AMX(amx_bits) | SIMDID_PACK_AVX(avx_bits) |
SIMDID_PACK_AVX10(avx10_bits) | SIMDID_PACK_UARCH(uarch_bits);
return _simdid;
{
const int has_avx_dual = (avx_bits & SIMDID_AVX_DUAL) != 0;
const uint64_t avx512_impl = avx512_bits & SIMDID_AVX512_IMPL_MASK;
const int has_avx512_dual = avx512_impl == SIMDID_AVX512_IMPL_DUAL;
if (has_avx_dual || has_avx512_dual) {
uarch_bits |= SIMDID_UARCH_SIMD_DUAL;
}
}
{
uint64_t final_mask = root;
final_mask |= manu;
final_mask |= main_lvl;
final_mask |= SIMDID_PACK_AVX512(avx512_bits);
final_mask |= SIMDID_PACK_AMX(amx_bits);
final_mask |= SIMDID_PACK_AVX(avx_bits);
final_mask |= SIMDID_PACK_AVX10(avx10_bits);
final_mask |= SIMDID_PACK_UARCH(uarch_bits);
simdid.mask = final_mask;
}
return simdid;
}
SN_API SIMDID SN_DECL sn_get_simd_level_global()
@@ -269,17 +345,28 @@ SN_API SIMDID SN_DECL sn_get_simd_level_global()
if (global_simdid.mask) {
return global_simdid;
}
global_simdid = sn_get_simd_level_local();
return global_simdid;
}
#else
SIMDID sn_get_simd_level()
SN_API SIMDID SN_DECL sn_get_simd_level_local()
{
SIMDID simd_id;
simd_id.mask = 0;
return simd_id; /* non-x86: no SIMD level probing */
SIMDID simdid;
simdid.mask = 0;
return simdid;
}
SN_API SIMDID SN_DECL sn_get_simd_level_global()
{
if (global_simdid.mask) {
return global_simdid;
}
global_simdid = sn_get_simd_level_local();
return global_simdid;
}
#endif