Files
stgsharp/StgSharp.Native/sn-src/simdid_compare.x86.c
T

215 lines
8.3 KiB
C

#include "sn_target.h"
#if SN_IS_ARCH(SN_ARCH_X86_64)
#include "StgSharpNative.h"
#include "sn_intrinsic.h"
#include "sn_intrinsic.context.x86.h"
/* -----------------------------------------------------------------------
* Field extraction helpers.
*
* SIMDID layout (64 bits, little-endian byte order in make_byte[]):
* [3..0] Root ISA make_byte[0] low nibble
* [7..4] Manufacture make_byte[0] high nibble
* [15..8] MainLevel make_byte[1]
* [31..16] AVX512 Feature make_byte[2..3] (16 bits)
* [39..32] AMX Feature make_byte[4]
* [47..40] AVX Feature make_byte[5]
* [55..48] AVX10 Feature make_byte[6]
* [63..56] uArchHi make_byte[7]
* ----------------------------------------------------------------------- */
#define FIELD_MAIN(id) ((unsigned char)(id).make_byte[1])
#define FIELD_AVX512(id) \
((uint16_t)((unsigned char)(id).make_byte[2] | ((unsigned char)(id).make_byte[3] << 8)))
#define FIELD_AMX(id) ((unsigned char)(id).make_byte[4])
#define FIELD_AVX(id) ((unsigned char)(id).make_byte[5])
#define FIELD_AVX10(id) ((unsigned char)(id).make_byte[6])
#define FIELD_UARCH(id) ((unsigned char)(id).make_byte[7])
/* Portable popcount for a 16-bit value. */
static int popcount16(uint16_t v)
{
int c = 0;
while (v) {
v &= v - 1;
c++;
}
return c;
}
/* Map AVX512_IMPL 2-bit field to a strength ordinal for tie-breaking.
* Index = impl value (0=UNKNOWN, 1=DUAL, 2=NATIVE, 3=reserved).
* Strength: DUAL(0) < UNKNOWN(1) < NATIVE(2). */
static const int impl_strength_table[4] = { 1, 0, 2, 1 };
static int impl_strength(uint16_t avx512)
{
unsigned impl = (avx512 >> SIMDID_AVX512_IMPL_SHIFT) & 0x3u;
return impl_strength_table[impl];
}
/* -----------------------------------------------------------------------
* sn_get_unite_simd
*
* Produce the conservative intersection of two SIMDIDs — the strongest
* capability set that is safe to use on BOTH cores:
* - MainLevel : min (only assume what both cores support)
* - AVX512 features : bitwise AND (IMPL field: pick the weaker one)
* - AMX features : bitwise AND
* - AVX features : bitwise AND
* - AVX10 features : bitwise AND
* - Root / Manu : keep left (must be same arch in practice)
* - uArchHi : bitwise OR (HYBRID_P/E union; SIMD_DUAL set if either core is dual)
* ----------------------------------------------------------------------- */
SN_API SIMDID SN_DECL sn_get_unite_simd(SIMDID left, SIMDID right)
{
SIMDID out;
out.mask = 0;
/* Root + Manufacture (byte 0): prefer left (same arch assumed) */
unsigned char lb0 = (unsigned char)left.make_byte[0];
unsigned char rb0 = (unsigned char)right.make_byte[0];
out.make_byte[0] = (char)(lb0 ? lb0 : rb0);
/* MainLevel (byte 1): min */
unsigned char lm = FIELD_MAIN(left);
unsigned char rm = FIELD_MAIN(right);
out.make_byte[1] = (char)(lm < rm ? lm : rm);
/* AVX512 features (bytes 2-3): AND all bits, then fix IMPL to weaker */
uint16_t l512 = FIELD_AVX512(left);
uint16_t r512 = FIELD_AVX512(right);
uint16_t merged = l512 & r512;
/* Clear the IMPL field, then set the weaker one */
int ls = impl_strength(l512);
int rs = impl_strength(r512);
int s = (ls <= rs) ? l512 : r512; /* pick the one with weaker strength ordinal */
unsigned weak_impl = (s >> SIMDID_AVX512_IMPL_SHIFT) & 0x3u;
merged &= ~(uint16_t)SIMDID_AVX512_IMPL_MASK;
merged |= (uint16_t)(weak_impl << SIMDID_AVX512_IMPL_SHIFT);
out.make_byte[2] = (char)(merged & 0xFF);
out.make_byte[3] = (char)((merged >> 8) & 0xFF);
/* AMX (byte 4): AND */
out.make_byte[4] = left.make_byte[4] & right.make_byte[4];
/* AVX (byte 5): AND */
out.make_byte[5] = left.make_byte[5] & right.make_byte[5];
/* AVX10 (byte 6): AND */
out.make_byte[6] = left.make_byte[6] & right.make_byte[6];
/* uArchHi (byte 7):
* topology bits [1..0]: AND — conservative intersection (weaker topology wins)
* SIMD_DUAL bit [2]: OR — dual if either core is dual */
unsigned char lu = FIELD_UARCH(left);
unsigned char ru = FIELD_UARCH(right);
unsigned char topo = (lu & 0x3u) & (ru & 0x3u);
unsigned char dual = (lu | ru) & (unsigned char)SIMDID_UARCH_SIMD_DUAL;
out.make_byte[7] = (char)(topo | dual);
return out;
}
/* -----------------------------------------------------------------------
* sn_compare_simd
*
* Compare two SIMDIDs by ISA strength. Returns:
* +1 if left is strictly stronger than right
* -1 if left is strictly weaker than right
* 0 if equal
*
* Comparison priority (most significant first):
*
* 1. MainLevel ([15..8])
* Higher value = stronger. This is the primary discriminator:
* NONE < SSE < AVX2 < AVX512 < AMX < AVX10
*
* 2. AVX10 vector width ([55..48])
* Only meaningful when both MainLevel == AVX10.
* 512W > 256W-only. Compared by the full AVX10 byte value since
* width bits are in higher positions than version bits.
*
* 3. AVX512 BASE bit (bit 4 of [31..16])
* Only meaningful when both MainLevel >= AVX512.
* BASE=1 (F+CD+VL+DQ+BW all present) > BASE=0.
*
* 4. AVX512 extension popcount
* Count of set extension bits (VNNI/BF16/FP16/VBMI/VBMI2/AI_RESERVED).
* The IMPL and BASE bits are masked out before counting so they don't
* inflate the extension count.
*
* 5. AVX512 IMPL strength (tie-break, performance hint)
* DUAL(weakest) < UNKNOWN < NATIVE(strongest).
* This is not ISA capability but execution-width risk.
*
* 6. uArchHi topology bits [1..0]
* Numeric value: unified(11=3) > P-core(10=2) > E-core(01=1) > unknown(00=0).
* Higher value = stronger / higher-capability core form.
*
* 7. uArchHi SIMD_DUAL (final tie-break)
* Non-dual (SIMD_DUAL=0) > dual (SIMD_DUAL=1).
* ----------------------------------------------------------------------- */
SN_API int SN_DECL sn_compare_simd(SIMDID left, SIMDID right)
{
/* 1. MainLevel */
unsigned lm = FIELD_MAIN(left);
unsigned rm = FIELD_MAIN(right);
if (lm != rm)
return lm > rm ? 1 : -1;
/* 2. AVX10 width (only when MainLevel == AVX10) */
if (lm >= SIMDID_MAIN_LVL_AVX10) {
unsigned la = FIELD_AVX10(left);
unsigned ra = FIELD_AVX10(right);
if (la != ra)
return la > ra ? 1 : -1;
}
/* 3. AVX512 BASE (only when MainLevel >= AVX512) */
if (lm >= SIMDID_MAIN_LVL_AVX512) {
uint16_t l512 = FIELD_AVX512(left);
uint16_t r512 = FIELD_AVX512(right);
int lb = (l512 & SIMDID_AVX512_BASE) != 0;
int rb = (r512 & SIMDID_AVX512_BASE) != 0;
if (lb != rb)
return lb > rb ? 1 : -1;
// 4. Extension popcount (mask out BASE + IMPL + reserved low nibble)
int drop = ~(SIMDID_AVX512_BASE | SIMDID_AVX512_IMPL_MASK | 0x000Fu);
uint16_t ext_mask = drop & (l512 | r512); /* only consider bits set in either */
int lp = popcount16(l512 & ext_mask);
int rp = popcount16(r512 & ext_mask);
if (lp != rp)
return lp > rp ? 1 : -1;
/* 5. IMPL strength */
int ls = impl_strength(l512);
int rs = impl_strength(r512);
if (ls != rs)
return ls > rs ? 1 : -1;
}
/* 6. uArchHi topology bits [1..0]: unified(3) > P(2) > E(1) > unknown(0) */
unsigned lu = FIELD_UARCH(left);
unsigned ru = FIELD_UARCH(right);
unsigned lt = lu & 0x3u;
unsigned rt = ru & 0x3u;
if (lt != rt)
return lt > rt ? 1 : -1;
/* 7. uArchHi SIMD_DUAL: non-dual (SIMD_DUAL=0) > dual (SIMD_DUAL=1) */
int ld = (lu & SIMDID_UARCH_SIMD_DUAL) != 0;
int rd = (ru & SIMDID_UARCH_SIMD_DUAL) != 0;
if (ld != rd)
return ld ? -1 : 1; /* non-dual is stronger */
return 0;
}
#endif