From d0fe4dde1ae31c7c79e08e3fed0bb948f78bcf22 Mon Sep 17 00:00:00 2001 From: Youmu Liu <105042599+Nitload-NSI@users.noreply.github.com> Date: Sun, 4 Jan 2026 18:13:50 +0800 Subject: [PATCH] add simd id --- StgSharp.Native/SIMDID.x86.md | 182 ++++++++++++++ StgSharp.Native/StgSharp.Native.vcxproj | 18 +- .../StgSharp.Native.vcxproj.filters | 25 +- StgSharp.Native/sn-head/sn_intrinsic.h | 191 +++++++++------ StgSharp.Native/sn-head/sn_target.h | 49 ++-- StgSharp.Native/sn-src/cpuid.c | 226 ++++++++++++++++++ .../sn-src/{k_basicop.c => m.basic.x86.c} | 6 + .../sn-src/{k_bufop.c => m.bufop.x86.c} | 4 +- .../sn-src/{k_fma.c => m.fma.x86.c} | 6 + .../sn-src/{k_panel.c => m.panel.x86.c} | 8 +- .../sn-src/{k_pivot.c => m.pivot.x86.c} | 6 + .../{str_factorial.c => s.factorial.x86.c} | 6 + .../sn-src/{str_hash.c => s.hash.x86.c} | 6 + StgSharp.Native/sn-src/str_string.c | 6 + 14 files changed, 622 insertions(+), 117 deletions(-) create mode 100644 StgSharp.Native/SIMDID.x86.md create mode 100644 StgSharp.Native/sn-src/cpuid.c rename StgSharp.Native/sn-src/{k_basicop.c => m.basic.x86.c} (97%) rename StgSharp.Native/sn-src/{k_bufop.c => m.bufop.x86.c} (99%) rename StgSharp.Native/sn-src/{k_fma.c => m.fma.x86.c} (99%) rename StgSharp.Native/sn-src/{k_panel.c => m.panel.x86.c} (99%) rename StgSharp.Native/sn-src/{k_pivot.c => m.pivot.x86.c} (97%) rename StgSharp.Native/sn-src/{str_factorial.c => s.factorial.x86.c} (93%) rename StgSharp.Native/sn-src/{str_hash.c => s.hash.x86.c} (96%) diff --git a/StgSharp.Native/SIMDID.x86.md b/StgSharp.Native/SIMDID.x86.md new file mode 100644 index 00000000..31b2f213 --- /dev/null +++ b/StgSharp.Native/SIMDID.x86.md @@ -0,0 +1,182 @@ +# SIMDID(x86-64)64-bit 规范(按你给定的固定布局补全) + +本文按你指定的 **64-bit SIMDID** 位布局进行补全,重点给出 **AVX512 Feature 区段([31..16])** 的设计,使其既能覆盖 AVX-512 混乱的子集,又能保持分支逻辑尽量精简。 + +> 约定:bit0 为最低位(LSB)。本文只定义 x86-64 解释器下的语义;Header 的 Root ISA 允许扩展到其他架构。 + +--- + +## 1) ASCII 位布局(最终) + +``` +SIMDID (64 bits) [63 ........................................................ 0] ++--------------------------+-----------+----------------+----------------+----------------+----------------+ +| [63..40] uArch/Policy | [39..32] | [31..16] | [15..8] | [7..4] | [3..0] | +| (24 bits, optional) | AMX Future| AVX512 Feature | MainLevel | Manufacture | Root ISA | ++--------------------------+-----------+----------------+----------------+----------------+----------------+ +``` + +- **[3..0] Root ISA**:根指令集类型(用于选择解释器) +- **[7..4] Manufacture**:制造商信息(对 x86 来说 Intel/AMD 等) +- **[15..8] MainLevel**:主 ISA 等级(递增、共享比特语义) +- **[31..16] AVX512 Feature**:AVX-512 特征/子集/实现形态(重点) +- **[39..32] AMX Future**:AMX 特征(目前可只放少量位) +- **[63..40] uArch/Policy**:可选策略/微架构提示(默认 0,不影响功能) + +--- + +## 2) Root ISA([3..0]) + +建议编码(你列出的架构较多,这里给一个可用的最小集合;不够可继续扩): + +| 值 | Root ISA | +|---:|----------| +| 0 | Unknown | +| 1 | x86-64 | +| 2 | AArch64 | +| 3 | PPC64LE | +| 4 | Apple Silicon(如果你想把它当“制造商+ARM”以外的 note,也可放在 Manufacture;但你要求 Root ISA 里有它,那就给它一个值) | +| 5 | RISC-V | +| 6 | LoongArch | +| 7..15 | Reserved | + +> 注:从体系结构角度 Apple Silicon 属于 AArch64;但你既然希望在 Root ISA 层面区分,那就这样定义即可(解释器由你决定)。 + +--- + +## 3) Manufacture([7..4]) + +在 **x86-64** 下建议: + +| 值 | Manufacture | +|---:|-------------| +| 0 | Unknown/Other | +| 1 | Intel | +| 2 | AMD | +| 3 | VIA/Zhaoxin | +| 4..15 | Reserved | + +在其它 Root ISA 下,该 nibble 由各自解释器自行定义(例如 ARM 下可放 Apple/Qualcomm/MediaTek 等)。 + +--- + +## 4) MainLevel([15..8],递增) + +你要求“共用比特且递增”,也就是说数值越大能力越强(或至少在你的库调度中越靠后)。建议定为: + +| 值 | MainLevel | +|---:|-----------| +| 0 | NONE | +| 1 | SSE(完整 SSE 家族:SSE1..SSE4.2) | +| 2 | AVX2 | +| 3 | AVX2_FMA | +| 4 | AVX2_FMA_F16C(从该级开始可处理 16-bit 类型) | +| 5 | AVX512(需要查询 [31..16]) | +| 6 | AMX | +| 7 | AVX10 | +| 8..255 | Reserved | + +> 建议调度:`MainLevel` 是主 switch;只有当 `MainLevel>=AVX512` 才解析 `AVX512 Feature`;当 `MainLevel>=AMX` 才解析 `AMX Future`。 + +--- + +## 5) AVX512 Feature([31..16])设计目标(精简版) + +目标改为: + +1) **基础 AVX512 一次性判定**:用 1 bit 表示“F+CD+VL+DQ+BW”这一捆绑基础包,避免多次子集判断。 +2) **胶水 vs 原生**:用实现字段直接区分 `AVX512-DUAL`(胶水/降频风险)与 `AVX512-NATIVE`(完整 512 位 ALU)。 +3) **小众/AI 扩展单独留位**:VNNI、BF16/FP16 等稀疏出现的扩展单独占位,基础路径不受干扰。 + +--- + +## 6) AVX512 Feature 位布局([31..16] = 16 bits,精简) + + +AVX512 Feature (bits 31..16) + +| **bit** | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | +|-----------|-----|-----|------|------|------|-------|------|------|-------|-------|----|------|----|----|----|----| +| **Usage** | R | R | FP16 | BF16 | VNNI | VBMI2 | VBMI | R/AI | Impl1 | Impl0 | R | BASE | R | R | R | R | + + +说明: + +- **BASE(bit 20)**:基础 AVX512 一揽子,表示同时具备 `F + CD + VL + DQ + BW`。这是 AVX512 最低可用门槛,探测端若设为 1,应保证上述子集都成立。 +- **AVX512_IMPL([23..22],2-bit)**:实现形态(宽度策略)。 + +| 值 | 含义 | +|---:|------| +| 0 | Unknown(需结合 Manufacture/uArch 决定策略) | +| 1 | AVX512-DUAL(胶水/降频风险,建议默认 256 宽路径) | +| 2 | AVX512-NATIVE(完整 512 位 ALU,可走 512 宽路径) | +| 3 | Reserved | + +- **VBMI/VBMI2/VNNI/BF16/FP16([29..26])**:稀疏但常用的小类型/AI 扩展,按需设置。 +- **R/AI(bit 24)**:预留给未来 AI 或其他非密码学的自选扩展。 +- **[31..30]、[21]、[19..16]**:Reserved,按需再分配。 + +--- + +## 7) “AVX512BASE” 判定(一次性检查) + +- **规则**:`BASE=1` 即视为具备 `F+CD+VL+DQ+BW`,无需逐项再查。探测端必须保持一致性,若无法保证全部子集齐备则应置 0。 +- **运行时分支**: + - 若 `MainLevel < AVX512`:直接降级到 AVX2/FMA 路径。 + - 否则: + - 检查 `BASE`;若为 0 则降级到 AVX2/FMA(或视为探测错误)。 + - 查看 `AVX512_IMPL`:`DUAL` 走 256 宽/降频安全路径,`NATIVE` 走 512 宽路径。 + - 需要小类型/AI 能力时,按位查看 `VNNI/BF16/FP16/VBMI/VBMI2`。 + +--- + +## 8) AMX Future([39..32],8 bits) + +你已经留了 1 个字节,建议先只定义 3 个最常见位,其余保留: + +| bit(相对 [39..32]) | 名称 | 说明 | +|---:|------|------| +| 0 | AMX_TILE | tile 基础 | +| 1 | AMX_INT8 | INT8 tile | +| 2 | AMX_BF16 | BF16 tile | +| 3..7 | Reserved | | + +当 `MainLevel < AMX` 时,这个字节可全为 0。 + +--- + +## 9) uArch/Policy([63..40],24 bits) + +用途:**你不想浪费比特,但又需要解决“Intel 早期/AMD 胶水”这类非 CPUID 纯子集能解决的问题**。 +把它留作可选策略字段,默认 0;你需要时再启用。 + +建议最小化用法: +- `0`:未使用 +- `1`:ForceDual(即使 impl unknown 也当 AVX512-DUAL) +- `2`:ForceNative(即使 impl unknown 也当 AVX512-NATIVE) +- `3..`:你自己维护的 uArch 枚举(例如 SKX/ICL/SPR/ZEN4/ZEN5…) + +> 由于你是单人维护,最现实的策略是: +> - AMD:探测端直接把 `AVX512_IMPL=DUAL`(无需 uArch) +> - Intel:仅对你关心的少数型号做 uArch 枚举,否则 unknown 走默认策略 + +--- + +## 10) 建议的最简探测端填充规则(x86-64) + +1) 先填 `RootISA=x86-64`、`Manufacture=Intel/AMD` +2) 计算 `MainLevel`(按你递增规则) +3) 若 `MainLevel >= AVX512`: + - 若探测到 `F+CD+VL+DQ+BW` 全部成立,则置 `BASE=1`,否则 `BASE=0` + - 填 `AVX512_IMPL`: + - AMD:DUAL(按你的定义) + - Intel:可按 family/model 或 policy 表决定 DUAL/NATIVE;不确定就 Unknown + - 需要 VNNI/BF16/FP16/VBMI/VBMI2 时,按位填写对应扩展 +4) 若 `MainLevel >= AMX`:填 AMX byte + +--- + +## 11) 你需要做的一个选择(高位保留位如何分配) + +目前 `[31..24]` 留作扩展/AI:示例定义为 FP16/BF16/VNNI/VBMI2/VBMI + 1 个预留位。 +如果你更需要 IFMA 或其他非密码学扩展,请替换 bit26~29 的定义;`BASE` 与 `Impl` 保持不变即可。 \ No newline at end of file diff --git a/StgSharp.Native/StgSharp.Native.vcxproj b/StgSharp.Native/StgSharp.Native.vcxproj index e16d55be..b3f14864 100644 --- a/StgSharp.Native/StgSharp.Native.vcxproj +++ b/StgSharp.Native/StgSharp.Native.vcxproj @@ -154,18 +154,19 @@ - - + + - - - - - + + + + + CCode + @@ -189,6 +190,9 @@ + + + diff --git a/StgSharp.Native/StgSharp.Native.vcxproj.filters b/StgSharp.Native/StgSharp.Native.vcxproj.filters index 8754294f..c282bd74 100644 --- a/StgSharp.Native/StgSharp.Native.vcxproj.filters +++ b/StgSharp.Native/StgSharp.Native.vcxproj.filters @@ -13,15 +13,18 @@ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + {fbd74b53-5314-4d9d-a788-07bd1b413f74} + 源文件 - + 源文件 - + 源文件 @@ -39,22 +42,25 @@ 源文件 - + 源文件 - + 源文件 源文件 - + 源文件 - + 源文件 - + + 源文件 + + 源文件 @@ -104,4 +110,9 @@ 资源文件 + + + Document + + \ No newline at end of file diff --git a/StgSharp.Native/sn-head/sn_intrinsic.h b/StgSharp.Native/sn-head/sn_intrinsic.h index a04779a0..c02e43ce 100644 --- a/StgSharp.Native/sn-head/sn_intrinsic.h +++ b/StgSharp.Native/sn-head/sn_intrinsic.h @@ -1,4 +1,4 @@ -#ifndef SSC_INTRIN +#ifndef SSC_INTRIN #define SSC_INTRIN #define SSC_INTRIN_HEAD @@ -22,7 +22,8 @@ (LASTTWO(source - 1) << 6) | (LASTTWO(target - 1) << 4) | (LAST(reset3) << 3) | \ (LAST(reset2) << 2) | (LAST(reset1) << 1) | LAST(reset0) #define SSE_INSERT_SIMPLE(source, target) SSE_INSERT(source, target, 0, 0, 0, 0) -#define M256_PERMUTE(s3, s2, s1, s0) (LASTTWO(s3) << 6) | (LASTTWO(s2) << 4) | (LASTTWO(s1) << 2) | (LASTTWO(s0) +#define M256_PERMUTE(s3, s2, s1, s0) \ + ((LASTTWO(s3) << 6) | (LASTTWO(s2) << 4) | (LASTTWO(s1) << 2) | (LASTTWO(s0))) #define BIT_CVRT(pack, index, T) *(T *)(pack->data + index) #define zero_vec _mm_setzero_ps() #define zero_vec_256 _mm256_setzero_ps() @@ -92,8 +93,8 @@ typedef struct matrix_task matrix_task; #if defined(__cplusplus) static_assert(sizeof(matrix_task) == 128, "matrix_parallel_task_package size mismatch"); -#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L -_Static_assert(sizeof(MAT_TASK(float)) == 128, "matrix_parallel_task_package size mismatch"); +#elif defined(__STDC_VERSION__) + static_assert(sizeof(MAT_TASK(float)) == 128, "matrix_parallel_task_package size mismatch"); #endif #pragma region matrix buffer operation function type @@ -197,6 +198,114 @@ typedef void(SN_DECL *VECTORNORMALIZEPROC)(__m128 *source, __m128 *target); typedef void(SN_DECL *DOTPROC)(void *transpose, __m128 *vector, __m128 *ans); typedef uint64_t(SN_DECL *FACTORIALROC)(int n); +#pragma region string + +typedef int(SN_DECL *HASH)(byte const *str, int length); +typedef int(SN_DECL *INDEX_PAIR)(short const *stram, uint32_t target, int length); + +#pragma endregion + +typedef struct mat_intrinsic { + BUF_PROC_LRA buffer_add; + BUF_PROC_AS buffer_fill; + BUF_PROC_RAS buffer_scalar_mul; + BUF_PROC_LRA buffer_sub; + KER_PROC_RA buffer_transpose; + PANEL_PROC build_panel; + PANEL_PROC clear_panel; + PNL_PROC_LRA panel_fma; + PIVOTPROC pivot; + PANEL_PROC store_panel; + +} mat_intrinsic; + +// Mirrors C# IntrinsicContext field order so managed/unmanaged layouts stay in sync. +typedef struct sn_intrinsic { + HASH city_hash_simplify; + FACTORIALROC factorial_simd; + INDEX_PAIR index_pair; + VECTORNORMALIZEPROC vec_f32_normalize_3; + mat_intrinsic mat[4]; +} sn_intrinsic; + +typedef enum elment_type { F32 = 0, F64 = 1, I32 = 2, I64 = 3 } element_type; + +typedef enum most_advanced_instruction { +#ifdef _MSC_VER + SSE = __IA_SUPPORT_SSE42, + AVX256 = __IA_SUPPORT_VECTOR256, + AVX512 = __IA_SUPPORT_VECTOR512, + NEON +#else + SSE, + AVX256, + AVX512, + NEON +#endif +} most_advanced_instruction; + +#include "sn_target.h" + +#if SN_IS_ARCH(SN_ARCH_X86_64) + +// ----------------------------------------------------------------------------- +// SIMDID layout helpers (see SIMDID.simd.md) +// bits [3..0] : Root ISA +// bits [7..4] : Manufacture (ISA-specific) +// bits [15..8] : MainLevel (monotonic capability) +// bits [31..16]: AVX512 Feature +// bits [39..32]: AMX Feature +// bits [63..40]: uArch / Policy (optional) +// ----------------------------------------------------------------------------- +#define SIMDID_SHIFT_ROOT 0 +#define SIMDID_SHIFT_MANU 4 +#define SIMDID_SHIFT_MAIN 8 +#define SIMDID_SHIFT_AVX512 16 +#define SIMDID_SHIFT_AMX 32 +#define SIMDID_SHIFT_UARCH 40 + +#define SIMDID_MASK_ROOT 0xFULL +#define SIMDID_MASK_MANU (0xFULL << SIMDID_SHIFT_MANU) +#define SIMDID_MASK_MAIN (0xFFULL << SIMDID_SHIFT_MAIN) +#define SIMDID_MASK_AVX512 (0xFFFFULL << SIMDID_SHIFT_AVX512) +#define SIMDID_MASK_AMX (0xFFULL << SIMDID_SHIFT_AMX) +#define SIMDID_MASK_UARCH (0xFFFFFFULL << SIMDID_SHIFT_UARCH) + +// Root ISA (low nibble) +#define SIMDID_ROOT_UNKNOWN 0x0 +#define SIMDID_ROOT_X86_64 0x1 +#define SIMDID_ROOT_AARCH64 0x2 +#define SIMDID_ROOT_PPC64LE 0x3 +#define SIMDID_ROOT_APPLESILICON 0x4 +#define SIMDID_ROOT_RISCV64 0x5 +#define SIMDID_ROOT_LOONGARCH 0x6 + +// Manufacture (x86-64 mapping; other ISAs may reinterpret) +#define SIMDID_MANU_UNKNOWN 0x0 +#define SIMDID_MANU_INTEL 0x1 +#define SIMDID_MANU_AMD 0x2 +#define SIMDID_MANU_VIA 0x3 + +// MainLevel (ordinal, shared bits 8..15; not bit-flags) +#define SIMDID_MAIN_LVL_NONE 0x00u +#define SIMDID_MAIN_LVL_SSE 0x01u /* SSE1..SSE4.2 */ +#define SIMDID_MAIN_LVL_AVX2 0x02u +#define SIMDID_MAIN_LVL_AVX2_FMA 0x03u +#define SIMDID_MAIN_LVL_AVX2_FMA_F16C 0x04u /* 16-bit scalar/vector ready */ +#define SIMDID_MAIN_LVL_AVX512 0x05u +#define SIMDID_MAIN_LVL_AMX 0x06u +#define SIMDID_MAIN_LVL_AVX10 0x07u + +/* Pre-shifted helpers for convenience when constructing SIMDID */ +#define SIMDID_MAIN_ENC_NONE SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_NONE) +#define SIMDID_MAIN_ENC_SSE SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_SSE) +#define SIMDID_MAIN_ENC_AVX2 SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX2) +#define SIMDID_MAIN_ENC_AVX2_FMA SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX2_FMA) +#define SIMDID_MAIN_ENC_AVX2_FMA_F16C SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX2_FMA_F16C) +#define SIMDID_MAIN_ENC_AVX512 SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX512) +#define SIMDID_MAIN_ENC_AMX SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AMX) +#define SIMDID_MAIN_ENC_AVX10 SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX10) + #pragma region matix function INTERNAL void f32_normalize(VEC(float) * source, VEC(float) * target); @@ -281,85 +390,15 @@ INTERNAL void SN_DECL dot_42_avx(MAT_KERNEL(float) const *transpose, __m128 cons #pragma region string -typedef int(SN_DECL *HASH)(byte const *str, int length); -typedef int(SN_DECL *INDEX_PAIR)(short const *stram, uint32_t target, int length); - INTERNAL int SN_DECL city_hash_simplify_sse(byte const *str, int length); -INTERNAL int SN_DECL index_pair_sse(short const *str, uint32_t target, int length); +INTERNAL int SN_DECL index_pair_sse(short const *stram, uint32_t target, int length); #pragma endregion -typedef struct mat_intrinsic { - BUF_PROC_LRA buffer_add; - BUF_PROC_AS buffer_fill; - BUF_PROC_RAS buffer_scalar_mul; - BUF_PROC_LRA buffer_sub; - KER_PROC_RA buffer_transpose; - PANEL_PROC build_panel; - PANEL_PROC clear_panel; - PNL_PROC_LRA panel_fma; - PIVOTPROC pivot; - PANEL_PROC store_panel; - -} mat_intrinsic; - -// Mirrors C# IntrinsicContext field order so managed/unmanaged layouts stay in sync. -typedef struct sn_intrinsic { - HASH city_hash_simplify; - FACTORIALROC factorial_simd; - INDEX_PAIR index_pair; - VECTORNORMALIZEPROC vec_f32_normalize_3; - mat_intrinsic mat[4]; -} sn_intrinsic; - -typedef enum elment_type { F32 = 0, F64 = 1, I32 = 2, I64 = 3 } element_type; - -typedef enum most_advanced_instruction { -#ifdef _MSC_VER - SSE = __IA_SUPPORT_SSE42, - AVX256 = __IA_SUPPORT_VECTOR256, - AVX512 = __IA_SUPPORT_VECTOR512, - NEON #else - SSE, - AVX256, - AVX512, - NEON -#endif -} most_advanced_instruction; -#include "sn_target.h" +typedef enum intrinsic_level { SIMD0 = 0 } intrinsic_level; -#if SN_IS_ARCH(SN_ARCH_X86_64) - -typedef enum intrinsic_level { - // No SIMD support, will cause error if used in matrix operations - SIMD0 = 0, - // SSE4.2 support - SIMD1 = 1, - // AVX2 support - SIMD2 = 2, - // AVX2 and AVX2-FMA support - SIMD3 = 3, - // AVX2-16C support based on SIMD3 - SIMD4 = 4, - // AVX-512-dual support - SIMD5 = 5, - // AVX-512-native support - SIMD6 = 6, - // AVX-512 with 8/16-wide support - SIMD7 = 7, - // AMX support - SIMD8 = 8, - // AVX-10 support - SIMD9 = 9, - // future use - SIMD_MAX = 10 -} intrinsic_level; - -#else -typedef enum intrinsic_level { -} intrinsic_level; #endif #endif diff --git a/StgSharp.Native/sn-head/sn_target.h b/StgSharp.Native/sn-head/sn_target.h index 5724ec84..e5d6ea5c 100644 --- a/StgSharp.Native/sn-head/sn_target.h +++ b/StgSharp.Native/sn-head/sn_target.h @@ -1,21 +1,20 @@ -#ifndef SN_TARGET +#ifndef SN_TARGET #define SN_TARGET -typedef enum SN_OS_T { - SN_OS_WINDOWS, - SN_OS_LINUX, - SN_OS_MACOS, - SN_OS_IOS, - SN_OS_ANDROID, - SN_OS_UNKNOWN -} SN_OS_T; +#include "sn_internal.h" +#include -typedef enum SN_ARCH_T { - SN_ARCH_X86_64, - SN_ARCH_ARM64, - SN_ARCH_RISCV64, - SN_ARCH_PPC64le, -} SN_ARCH_T; +#define SN_OS_WINDOWS 0 +#define SN_OS_LINUX 1 +#define SN_OS_MACOS 2 +#define SN_OS_IOS 3 +#define SN_OS_ANDROID 4 +#define SN_OS_UNKNOWN 5 + +#define SN_ARCH_X86_64 0 +#define SN_ARCH_ARM64 1 +#define SN_ARCH_RISCV64 2 +#define SN_ARCH_PPC64le 3 /* * Target selection policy: @@ -38,24 +37,28 @@ typedef enum SN_ARCH_T { #endif /* Validate externally supplied values are within known enum constants. */ -#if (SN_OS_TARGET != SN_OS_WINDOWS) && (SN_OS_TARGET != SN_OS_LINUX) && (SN_OS_TARGET != SN_OS_MACOS) && \ - (SN_OS_TARGET != SN_OS_IOS) && (SN_OS_TARGET != SN_OS_ANDROID) && (SN_OS_TARGET != SN_OS_UNKNOWN) +#if (SN_OS_TARGET != SN_OS_WINDOWS) && (SN_OS_TARGET != SN_OS_LINUX) && \ + (SN_OS_TARGET != SN_OS_MACOS) && (SN_OS_TARGET != SN_OS_IOS) && \ + (SN_OS_TARGET != SN_OS_ANDROID) && (SN_OS_TARGET != SN_OS_UNKNOWN) #error "SN_OS has an invalid value. Use SN_OS_<...> enum constants." #endif -#if (SN_ARCH_TARGET != SN_ARCH_X86_64) && (SN_ARCH_TARGET != SN_ARCH_ARM64) && (SN_ARCH_TARGET != SN_ARCH_RISCV64) && \ - (SN_ARCH_TARGET != SN_ARCH_PPC64le) +#if (SN_ARCH_TARGET != SN_ARCH_X86_64) && (SN_ARCH_TARGET != SN_ARCH_ARM64) && \ + (SN_ARCH_TARGET != SN_ARCH_RISCV64) && (SN_ARCH_TARGET != SN_ARCH_PPC64le) #error "SN_ARCH has an invalid value. Use SN_ARCH_<...> enum constants." #endif #endif -#ifndef SN_IS_OS #define SN_IS_OS(os) ((SN_OS_TARGET) == (os)) -#endif -#ifndef SN_IS_ARCH #define SN_IS_ARCH(arch) ((SN_ARCH_TARGET) == (arch)) -#endif + +typedef union SIMDID { + uint64_t mask; + char make_byte[]; +} SIMDID; + +SN_API SIMDID SN_DECL sn_get_simd_level(); #endif // SN_TARGET diff --git a/StgSharp.Native/sn-src/cpuid.c b/StgSharp.Native/sn-src/cpuid.c new file mode 100644 index 00000000..947d66f3 --- /dev/null +++ b/StgSharp.Native/sn-src/cpuid.c @@ -0,0 +1,226 @@ +#include "sn_target.h" +#include "sn_intrinsic.h" + +#if SN_IS_ARCH(SN_ARCH_X86_64) + +#ifndef SIMDID_AVX512_BASE +#define SIMDID_AVX512_FP16 (1U << 29) +#define SIMDID_AVX512_BF16 (1U << 28) +#define SIMDID_AVX512_VNNI (1U << 27) +#define SIMDID_AVX512_VBMI2 (1U << 26) +#define SIMDID_AVX512_VBMI (1U << 25) +#define SIMDID_AVX512_AI_RESERVED (1U << 24) +#define SIMDID_AVX512_BASE (1U << 20) +#define SIMDID_AVX512_IMPL_SHIFT 22 +#define SIMDID_AVX512_IMPL_MASK (0x3U << SIMDID_AVX512_IMPL_SHIFT) +#define SIMDID_AVX512_IMPL_UNKNOWN (0x0U << SIMDID_AVX512_IMPL_SHIFT) +#define SIMDID_AVX512_IMPL_DUAL (0x1U << SIMDID_AVX512_IMPL_SHIFT) +#define SIMDID_AVX512_IMPL_NATIVE (0x2U << SIMDID_AVX512_IMPL_SHIFT) +#endif + +#ifndef SIMDID_AMX_TILE +#define SIMDID_AMX_TILE (1ULL << 32) +#define SIMDID_AMX_INT8 (1ULL << 33) +#define SIMDID_AMX_BF16 (1ULL << 34) +#endif + +#include +#include +#if defined(_MSC_VER) +#include +#else +#include +#endif + +static void get_cpuid(int leaf, int subleaf, int *eax, int *ebx, int *ecx, int *edx) +{ +#if defined(_MSC_VER) + int regs[4]; + __cpuidex(regs, leaf, subleaf); + *eax = regs[0]; + *ebx = regs[1]; + *ecx = regs[2]; + *edx = regs[3]; +#else + unsigned int a, b, c, d; + __cpuid_count((unsigned int)leaf, (unsigned int)subleaf, a, b, c, d); + *eax = (int)a; + *ebx = (int)b; + *ecx = (int)c; + *edx = (int)d; +#endif +} + +static int xgetbv_has_xmm_ymm(void) +{ +#if defined(_MSC_VER) + unsigned __int64 xcr0 = _xgetbv(0); +#else + unsigned long long xcr0 = __builtin_ia32_xgetbv(0); +#endif + return ((xcr0 & 0x6) == 0x6); +} + +static int xgetbv_has_avx512_state(void) +{ + const unsigned long long mask = (1ull << 0) | (1ull << 1) | (1ull << 2) | (1ull << 5) | + (1ull << 6); +#if defined(_MSC_VER) + unsigned __int64 xcr0 = _xgetbv(0); +#else + unsigned long long xcr0 = __builtin_ia32_xgetbv(0); +#endif + return ((xcr0 & mask) == mask); +} + +static int is_intel_glue_avx512(int family, int model) +{ + return (family == 6) && (model == 0x55); /* SKX/CLX/CPX */ +} + +static int is_intel_native_avx512(int family, int model) +{ + if (family != 6) + return 0; + switch (model) { + case 0x6A: /* ICX */ + case 0x6C: /* ICX stepping */ + case 0x8F: /* SPR/EMR */ + case 0xCF: /* GNR */ + return 1; + default: + return 0; + } +} + +SN_API SIMDID SN_DECL sn_get_simd_level() +{ + SIMDID simd_id; + simd_id.mask = 0; + + int eax = 0, ebx = 0, ecx = 0, edx = 0; + + /* Vendor */ + get_cpuid(0, 0, &eax, &ebx, &ecx, &edx); + char vendor[13]; + ((int *)vendor)[0] = ebx; + ((int *)vendor)[1] = edx; + ((int *)vendor)[2] = ecx; + vendor[12] = '\0'; + const int is_intel = (strcmp(vendor, "GenuineIntel") == 0); + const int is_amd = (strcmp(vendor, "AuthenticAMD") == 0) || + (strcmp(vendor, "HygonGenuine") == 0); + + /* CPUID.1 */ + get_cpuid(1, 0, &eax, &ebx, &ecx, &edx); + int family = ((eax >> 8) & 0xF) + ((eax >> 20) & 0xFF0); + int model = ((eax >> 4) & 0xF) | ((eax >> 12) & 0xF0); + + 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; + + 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 : + SIMDID_MANU_UNKNOWN); + uint64_t main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_NONE); + uint64_t avx512_bits = 0; + uint64_t amx_bits = 0; + + /* Baseline main level */ + if (has_sse42) + main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_SSE); + + if (has_avx && has_osxsave && xgetbv_has_xmm_ymm()) { + /* 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; + + /* leaf 7, subleaf 1 for BF16/FP16 */ + 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 */ + + /* AMX in leaf 7 subleaf 0 EDX bits 24/25/26 */ + 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 << 26)) != 0; + + if (has_avx2) { + if (has_fma) { + main_lvl = SIMDID_PACK_MAIN((has_bmi1 && has_bmi2) ? + SIMDID_MAIN_LVL_AVX2_FMA_F16C : + SIMDID_MAIN_LVL_AVX2_FMA); + } else { + 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; + + /* impl classification */ + const int glue = is_amd || is_intel_glue_avx512(family, model); + const int native512 = is_intel_native_avx512(family, model); + if (glue) { + avx512_bits |= SIMDID_AVX512_IMPL_DUAL; + } else if (native512) { + avx512_bits |= SIMDID_AVX512_IMPL_NATIVE; + } else { + avx512_bits |= SIMDID_AVX512_IMPL_UNKNOWN; + } + + /* main level for AVX512 */ + main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX512); + + /* 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); + } + } + } + + simd_id.mask = root | manu | main_lvl | SIMDID_PACK_AVX512(avx512_bits) | + SIMDID_PACK_AMX(amx_bits); + return simd_id; +} + +#else + +SIMDID sn_get_simd_level() +{ + SIMDID simd_id; + simd_id.mask = 0; + return simd_id; /* non-x86: no SIMD level probing */ +} + +#endif diff --git a/StgSharp.Native/sn-src/k_basicop.c b/StgSharp.Native/sn-src/m.basic.x86.c similarity index 97% rename from StgSharp.Native/sn-src/k_basicop.c rename to StgSharp.Native/sn-src/m.basic.x86.c index 9240b2fa..ea76418f 100644 --- a/StgSharp.Native/sn-src/k_basicop.c +++ b/StgSharp.Native/sn-src/m.basic.x86.c @@ -1,3 +1,7 @@ +#include "sn_target.h" + +#if SN_IS_ARCH(SN_ARCH_X86_64) + #include "sn_intrinsic.h" DECLARE_PNL_PROC_ANS(float, sse, clear) @@ -91,3 +95,5 @@ DECLARE_KER_PROC_RIGHT_ANS(float, 512, transpose) ans->f32_z[0] = _mm512_permutexvar_ps(transpose_indices, matrix); } + +#endif diff --git a/StgSharp.Native/sn-src/k_bufop.c b/StgSharp.Native/sn-src/m.bufop.x86.c similarity index 99% rename from StgSharp.Native/sn-src/k_bufop.c rename to StgSharp.Native/sn-src/m.bufop.x86.c index 452ba474..d143d57f 100644 --- a/StgSharp.Native/sn-src/k_bufop.c +++ b/StgSharp.Native/sn-src/m.bufop.x86.c @@ -1,4 +1,4 @@ -#include "sn_target.h" +#include "sn_target.h" #if SN_IS_ARCH(SN_ARCH_X86_64) @@ -320,4 +320,4 @@ DECLARE_BUF_PROC_RIGHT_ANS_SCALAR(double, 512, scalar_mul) } } -#endif \ No newline at end of file +#endif diff --git a/StgSharp.Native/sn-src/k_fma.c b/StgSharp.Native/sn-src/m.fma.x86.c similarity index 99% rename from StgSharp.Native/sn-src/k_fma.c rename to StgSharp.Native/sn-src/m.fma.x86.c index 9da92a74..e38c90a8 100644 --- a/StgSharp.Native/sn-src/k_fma.c +++ b/StgSharp.Native/sn-src/m.fma.x86.c @@ -1,3 +1,7 @@ +#include "sn_target.h" + +#if SN_IS_ARCH(SN_ARCH_X86_64) + #include "sn_intrinsic.h" #define _FMA_SELECT(i) _MM_SHUFFLE(i, i, i, i) @@ -379,3 +383,5 @@ DECLARE_PNL_PROC_LEFT_RIGHT_ANS(double, 512, fma) _mm512_store_pd((double *)&ans->vec[col][0], ccol); } } + +#endif diff --git a/StgSharp.Native/sn-src/k_panel.c b/StgSharp.Native/sn-src/m.panel.x86.c similarity index 99% rename from StgSharp.Native/sn-src/k_panel.c rename to StgSharp.Native/sn-src/m.panel.x86.c index 79c751fe..6e477fc1 100644 --- a/StgSharp.Native/sn-src/k_panel.c +++ b/StgSharp.Native/sn-src/m.panel.x86.c @@ -1,3 +1,7 @@ +#include "sn_target.h" + +#if SN_IS_ARCH(SN_ARCH_X86_64) + #include "sn_matkernel.h" DECLARE_BUILD_PANEL(avx, float) @@ -41,7 +45,6 @@ DECLARE_BUILD_PANEL(avx, float) } } - DECLARE_BUILD_PANEL(512, float) { const MAT_KERNEL(float) *const zero = (const MAT_KERNEL(float) *)&SN_ZERO_KERNEL; @@ -77,7 +80,6 @@ DECLARE_BUILD_PANEL(512, float) } } - DECLARE_BUILD_PANEL(512, double) { const int Q = 8; /* max(4, lanes) for double with ZMM -> 8 columns */ @@ -295,3 +297,5 @@ DECLARE_STORE_PANEL(512, double) bot_dst->f64_y[kc_col] = bot256; } } + +#endif diff --git a/StgSharp.Native/sn-src/k_pivot.c b/StgSharp.Native/sn-src/m.pivot.x86.c similarity index 97% rename from StgSharp.Native/sn-src/k_pivot.c rename to StgSharp.Native/sn-src/m.pivot.x86.c index cc6a1733..8d720f35 100644 --- a/StgSharp.Native/sn-src/k_pivot.c +++ b/StgSharp.Native/sn-src/m.pivot.x86.c @@ -1,3 +1,7 @@ +#include "sn_target.h" + +#if SN_IS_ARCH(SN_ARCH_X86_64) + #include "sn_intrinsic.h" // Hybrid implementation: SIMD for Abs, Scalar for Max @@ -62,3 +66,5 @@ INTERNAL void SN_DECL pivot_float(MAT_KERNEL(float) const *source, size_t col_be } } } + +#endif diff --git a/StgSharp.Native/sn-src/str_factorial.c b/StgSharp.Native/sn-src/s.factorial.x86.c similarity index 93% rename from StgSharp.Native/sn-src/str_factorial.c rename to StgSharp.Native/sn-src/s.factorial.x86.c index 450bdbc5..39b7c140 100644 --- a/StgSharp.Native/sn-src/str_factorial.c +++ b/StgSharp.Native/sn-src/s.factorial.x86.c @@ -1,3 +1,7 @@ +#include "sn_target.h" + +#if SN_IS_ARCH(SN_ARCH_X86_64) + #include "sn_internal.h" #include "sn_intrinsic.h" @@ -21,3 +25,5 @@ INTERNAL uint64_t SN_DECL factorial_simd_sse(int n) } return result; } + +#endif diff --git a/StgSharp.Native/sn-src/str_hash.c b/StgSharp.Native/sn-src/s.hash.x86.c similarity index 96% rename from StgSharp.Native/sn-src/str_hash.c rename to StgSharp.Native/sn-src/s.hash.x86.c index 8662dab9..295b192d 100644 --- a/StgSharp.Native/sn-src/str_hash.c +++ b/StgSharp.Native/sn-src/s.hash.x86.c @@ -1,3 +1,7 @@ +#include "sn_target.h" + +#if SN_IS_ARCH(SN_ARCH_X86_64) + #include "sn_intrinsic.h" #include "StgSharpNative.h" @@ -43,3 +47,5 @@ INTERNAL int SN_DECL city_hash_simplify_sse(byte const *str, int const length) hash ^= hash >> 16; return hash; } + +#endif diff --git a/StgSharp.Native/sn-src/str_string.c b/StgSharp.Native/sn-src/str_string.c index 37f208e7..e67d142c 100644 --- a/StgSharp.Native/sn-src/str_string.c +++ b/StgSharp.Native/sn-src/str_string.c @@ -1,3 +1,7 @@ +#include "sn_target.h" + +#if SN_IS_ARCH(SN_ARCH_X86_64) + #include "sn_intrinsic.h" INTERNAL int SN_DECL index_pair_sse(short const *str, uint32_t target, int length) @@ -46,3 +50,5 @@ INTERNAL int SN_DECL index_pair_sse(short const *str, uint32_t target, int lengt } return -1; } + +#endif