From 1b041963149642d208b7ac5a652137760449476f Mon Sep 17 00:00:00 2001 From: Youmu Liu <105042599+Nitload-NSI@users.noreply.github.com> Date: Wed, 25 Feb 2026 18:02:56 +0800 Subject: [PATCH] many versions of simdid --- .../Numeric/MatrixParallel.cs | 23 +++++-- .../Numeric/MatrixParallelThread.cs | 66 ++++++++++++------- StgSharp.Native/SIMDID.x86.md | 54 +++++++++++---- .../sn-head/sn_intrincontext.x86.h | 14 ++++ StgSharp.Native/sn-src/simdid.x86.c | 50 +++++++++++++- StgSharp.Native/sn-src/simdid_compare.x86.c | 32 ++++++--- 6 files changed, 188 insertions(+), 51 deletions(-) diff --git a/StgSharp.Mathematics/Numeric/MatrixParallel.cs b/StgSharp.Mathematics/Numeric/MatrixParallel.cs index c4acd29f..7ff08f23 100644 --- a/StgSharp.Mathematics/Numeric/MatrixParallel.cs +++ b/StgSharp.Mathematics/Numeric/MatrixParallel.cs @@ -2,8 +2,8 @@ // ----------------------------------------------------------------------- // file="MatrixParallel" // Project: StgSharp -// AuthorGroup: Nitload Space -// Copyright (c) Nitload Space. All rights reserved. +// AuthorGroup: Nitload +// Copyright (c) Nitload. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -57,7 +57,9 @@ namespace StgSharp.Mathematics.Numeric private static bool IsInitialized { get; set; } = false; - internal static unsafe IntrinsicContext* GetContext(SIMDID simd) + internal static unsafe IntrinsicContext* GetContext( + SIMDID simd + ) { if (!IsParallelSupported) { return null; @@ -92,7 +94,10 @@ namespace StgSharp.Mathematics.Numeric { MatrixParallelThread thread = new MatrixParallelThread(i); _threads.Push(thread); + SpinWait.SpinUntil(() => thread.State == MatrixThreadState.Ready); } + + // TODO 构建全局的保底上下文 if (contextMap.Keys.Count == 1) { for (int i = 0; i < 4; i++) @@ -110,7 +115,17 @@ namespace StgSharp.Mathematics.Numeric [LibraryImport(Native.LibName, EntryPoint = "load_intrinsic_function")] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] - private static unsafe partial void LoadIntrinsicFunction(IntrinsicContext* context, ulong id); + private static unsafe partial ulong CompareSIMDID( + IntrinsicContext* context, + ulong id + ); + + [LibraryImport(Native.LibName, EntryPoint = "load_intrinsic_function")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + private static unsafe partial void LoadIntrinsicFunction( + IntrinsicContext* context, + ulong id + ); } } diff --git a/StgSharp.Mathematics/Numeric/MatrixParallelThread.cs b/StgSharp.Mathematics/Numeric/MatrixParallelThread.cs index 6ef881ec..1cc1bde8 100644 --- a/StgSharp.Mathematics/Numeric/MatrixParallelThread.cs +++ b/StgSharp.Mathematics/Numeric/MatrixParallelThread.cs @@ -2,8 +2,8 @@ // ----------------------------------------------------------------------- // file="MatrixParallelThread" // Project: StgSharp -// AuthorGroup: Nitload Space -// Copyright (c) Nitload Space. All rights reserved. +// AuthorGroup: Nitload +// Copyright (c) Nitload. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -35,9 +35,11 @@ namespace StgSharp.Mathematics.Numeric internal enum MatrixThreadState : int { - Ready = 0, - Running = 1, - Idle = 2, + Born = 0, + Ready = 1, + Running = 2, + Idle = 4, + Retired = 8, } @@ -45,11 +47,13 @@ namespace StgSharp.Mathematics.Numeric { private IntPtr* _context; - private bool disposedValue; + private volatile bool _disposed, _isRetireRequested = false; private readonly ManualResetEventSlim _resetEvent; private readonly Thread _managedThread; - internal MatrixParallelThread(int poolId) + internal MatrixParallelThread( + int poolId + ) { _resetEvent = new ManualResetEventSlim(false); _managedThread = new Thread(MatrixParallelWorkLoad); @@ -69,7 +73,7 @@ namespace StgSharp.Mathematics.Numeric internal MatrixParallelWrap BindedWrap { get; set; } - internal MatrixThreadState state { get; set; } + internal MatrixThreadState State { get; set; } internal SIMDID Simd { get; set; } @@ -80,7 +84,9 @@ namespace StgSharp.Mathematics.Numeric } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void ExecuteTaskPackage(MatrixParallelQueue taskQueue) + public static unsafe void ExecuteTaskPackage( + MatrixParallelQueue taskQueue + ) { MatrixParallelTaskPackage* p = taskQueue.Package; switch (p->ComputeMode.OperationStyle) @@ -121,44 +127,54 @@ namespace StgSharp.Mathematics.Numeric _resetEvent.Set(); } - [DllImport( - dllName: Native.LibName, - EntryPoint = "sn_get_simd_level_local", - CallingConvention = CallingConvention.Cdecl)] - internal static extern ulong GetSimdId(); + [LibraryImport(Native.LibName, EntryPoint = "sn_get_simd_level_local")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + internal static partial ulong GetSimdId(); - [DllImport( - dllName:Native.LibName, - EntryPoint = "sn_set_thread_affinity", - CallingConvention = CallingConvention.Cdecl)] - internal static extern void SetThreadAffinity(int coreId); + [LibraryImport(Native.LibName, EntryPoint = "sn_set_thread_affinity")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + internal static partial void SetThreadAffinity( + int coreId + ); - protected virtual void Dispose(bool disposing) + protected virtual void Dispose( + bool disposing + ) { - if (!disposedValue) + if (!_disposed) { if (disposing) { - _resetEvent.Dispose(); + _isRetireRequested = true; + _resetEvent.Set(); _managedThread.Join(); + _resetEvent.Dispose(); } - disposedValue = true; + _disposed = true; } } private unsafe void MatrixParallelWorkLoad() { + State = MatrixThreadState.Born; + // Bind OS-thread affinity once when the worker starts running SetThreadAffinity(PoolId); - Thread.Sleep(1); + Thread.Yield(); SIMDID feature = new(); feature.Mask = GetSimdId(); _context = (nint*)MatrixParallel.GetContext(feature); + State = MatrixThreadState.Ready; while (true) { // Thread self = Thread.CurrentThread; _resetEvent.Wait(); _resetEvent.Reset(); + if (_isRetireRequested) + { + break; + } + State = MatrixThreadState.Running; AfterWork = BindedWrap.AfterWork; _managedThread.Priority = ThreadPriority.Highest; if (IsSingle) @@ -170,7 +186,9 @@ namespace StgSharp.Mathematics.Numeric BindedWrap.ReportThreadAccomplished(); } _managedThread.Priority = AfterWork; + State = MatrixThreadState.Idle; } + State = MatrixThreadState.Retired; } } diff --git a/StgSharp.Native/SIMDID.x86.md b/StgSharp.Native/SIMDID.x86.md index 08050769..795ac0d7 100644 --- a/StgSharp.Native/SIMDID.x86.md +++ b/StgSharp.Native/SIMDID.x86.md @@ -134,14 +134,15 @@ AVX Feature (bits 47..40) | bit | 47 | 46 | 45 | 44 | 43 | 42 | 41 | 40 | |-----|----|----|----|----|----|----|----|----| -| use | R | R | R | R | F16C | FMA | AVX2 | AVX | +| use | R | R | R | DUAL | F16C | FMA | AVX2 | AVX | Rules: - **AVX (bit 40)**: AVX present. - **AVX2 (bit 41)**: AVX2 present. For the AVX feature byte to be considered valid, both AVX and AVX2 must be set. - **FMA (bit 42)**: FMA present. - **F16C (bit 43)**: F16C present. -- **[44..47]**: Reserved for other AVX-family extensions (e.g., BMI/BMI2, IFMA, XOP) as needed. +- **DUAL (bit 44)**: YMM execution is **glued/split-lane** (two fused 128-bit units rather than a native 256-bit path). Detection: AMD Zen1/Zen+ (Family 0x17, model < 0x30), Hygon (all, Zen1-based), VIA/Zhaoxin (all). When set, code using YMM operations may incur split-register overhead; prefer 128-bit SSE paths on these targets. +- **[45..47]**: Reserved for other AVX-family extensions (e.g., IFMA, XOP) as needed. --- @@ -207,17 +208,36 @@ When `MainLevel < AMX`, this byte can be all zeros. ## 11) uArchHi ([63..56], 8 bits) -Purpose: optional high-byte strategy field to address issues like early Intel/AMD "glue" AVX512 that are not expressible purely via CPUID subsets. Defaults to 0. +Purpose: **bitmask** byte encoding core topology classification and SIMD execution-width policy for the detected core. All bits default to 0. -Suggested minimal use: -- `0`: unused -- `1`: ForceDual (treat as AVX512-DUAL even if impl is unknown) -- `2`: ForceNative (treat as AVX512-NATIVE even if impl is unknown) -- `3..`: Implementation-maintained uArch enumeration (e.g., SKX/ICL/SPR/ZEN4/ZEN5…) +uArchHi bitmask layout: -Practical strategy for a single maintainer: -- AMD: detection side sets `AVX512_IMPL = DUAL` directly (no uArch needed) -- Intel: enumerate only selected models of interest; otherwise Unknown follows default policy +| bit (relative to [63..56]) | Name | Note | +|---:|------|------| +| 0 | HYBRID_E | E-core (efficiency/small core) in a hybrid-topology processor (e.g., Intel Alder Lake+ E-core) | +| 1 | HYBRID_P | P-core (performance/big core) in a hybrid-topology processor (e.g., Intel Alder Lake+ P-core) | +| 2 | SIMD_DUAL | SIMD execution is glued/dual (not full-width). Summary flag: set when `AVX_DUAL=1` **or** `AVX512_IMPL=DUAL`. | +| 3..7 | Reserved | | + +Core topology encoding (bits [1..0], numerically comparable — higher = stronger): + +| bits [1..0] | Value | Meaning | +|:-----------:|:-----:|----------| +| 00 | 0 | Unknown — undetected hybrid type or pre-CPUID.1A hardware | +| 01 | 1 | **Hybrid-E** — efficiency/small core (a3) | +| 10 | 2 | **Hybrid-P** — performance/big core (a2) | +| 11 | 3 | **Unified** — confirmed non-hybrid; full-topology core (a1) | + +The 2-bit field is directly comparable as an unsigned integer: `unified(3) > P-core(2) > E-core(1) > unknown(0)`. + +Detection: +- **Non-hybrid (unified)**: if `CPUID.7.0 EDX[15]` (HYBRID) is **not** set, the CPU is confirmed non-hybrid → set both `HYBRID_P` and `HYBRID_E` (topology = 11). +- **Hybrid P-core**: HYBRID flag set and `CPUID.0x1A.0 EAX[31:24] == 0x40` → set only `HYBRID_P` (topology = 10). +- **Hybrid E-core**: HYBRID flag set and `CPUID.0x1A.0 EAX[31:24] == 0x20` → set only `HYBRID_E` (topology = 01). +- **Unknown hybrid** (flag set but core type unrecognised): leave topology at 00. +- **SIMD_DUAL**: set when any of the following: `AVX Feature DUAL=1` (glued YMM), or `AVX512_IMPL=DUAL`. + +`sn_get_unite_simd` computes topology bits [1..0] with bitwise **AND** (conservative intersection — weaker topology wins) and SIMD_DUAL with bitwise **OR** (dual if either core is dual). `sn_compare_simd` compares topology value numerically as step 6 and SIMD_DUAL as the final (step 7) tie-break. --- @@ -232,7 +252,7 @@ Practical strategy for a single maintainer: - Intel: Decide DUAL/NATIVE based on family/model or policy table; if uncertain, use Unknown - When VNNI/BF16/FP16/VBMI/VBMI2 are detected, set the corresponding bits. 4) If `MainLevel >= AMX`: fill the AMX byte. -5) When AVX/AVX2 are present, populate the AVX Feature byte: set AVX and AVX2; set FMA and F16C when detected; leave reserved bits for future extensions. +5) When AVX/AVX2 are present, populate the AVX Feature byte: set `AVX` and `AVX2`; set `FMA` and `F16C` when detected; set `DUAL` when the YMM execution path is split/glued (AMD Zen1/Zen+ Family 0x17 model < 0x30, Hygon all models, VIA/Zhaoxin all models). 6) **AVX10 detection** (requires `CPUID leaf 7 subleaf 1, EDX[19] == 1` as the AVX10 convergence bit): - If AVX10 is indicated, query `CPUID leaf 0x24, subleaf 0`: - Read `EBX[7:0]` for version number; set `AVX10`, `V1`, and optionally `V2`. @@ -240,10 +260,16 @@ Practical strategy for a single maintainer: - Read `EBX[18]` for 512-bit support → set `512W`. - Promote `MainLevel` to `AVX10` (value 7). - The `AVX512 Feature` field should still be filled for backward compatibility. -7) Use uArchHi only when additional policy is needed; otherwise leave as 0. +7) Populate uArchHi: + - If `CPUID.7.0 EDX[15]` (HYBRID) **not** set: set `HYBRID_P | HYBRID_E` (unified = 11). + - If HYBRID is set, read `CPUID.0x1A.0 EAX[31:24]`: + - `0x40` (P-core) → set only `HYBRID_P` (topology = 10). + - `0x20` (E-core) → set only `HYBRID_E` (topology = 01). + - Otherwise leave topology at 00 (unknown hybrid). + - If `AVX Feature DUAL=1` or `AVX512_IMPL=DUAL`, set `SIMD_DUAL`. --- ## 13) Choice for High Bits Reservation -`[31..24]` is reserved for AVX512 extensions/AI. `[47..44]` is reserved for AVX-family extensions beside the AVX byte. `[55..53]` is reserved for future AVX10 versions. uArchHi `[63..56]` remains available for minimal policy hints. If IFMA or other extensions are more important, repurpose the reserved bits while keeping `BASE`, `Impl`, and `AVX10` core bits unchanged. \ No newline at end of file +`[31..24]` is reserved for AVX512 extensions/AI. `[47..45]` is reserved for AVX-family extensions (bit 44 is now `DUAL`). `[55..53]` is reserved for future AVX10 versions. uArchHi `[63..56]` bits [3..7] are reserved; bits [0..2] encode hybrid topology and SIMD-dual policy. \ No newline at end of file diff --git a/StgSharp.Native/sn-head/sn_intrincontext.x86.h b/StgSharp.Native/sn-head/sn_intrincontext.x86.h index 2801609d..7203aa26 100644 --- a/StgSharp.Native/sn-head/sn_intrincontext.x86.h +++ b/StgSharp.Native/sn-head/sn_intrincontext.x86.h @@ -75,6 +75,7 @@ #define SIMDID_AVX2 (1U << 1) #define SIMDID_AVX_FMA (1U << 2) #define SIMDID_AVX_F16C (1U << 3) +#define SIMDID_AVX_DUAL (1U << 4) /* glued/split-lane YMM (two 128-bit units; Zen1, Hygon, VIA/Zhaoxin) */ #endif #ifndef SIMDID_AMX_TILE @@ -91,6 +92,19 @@ #define SIMDID_AVX10_512W (1U << 4) #endif +/* uArchHi ([63..56]) bitmask — core topology and SIMD-width execution policy */ +#ifndef SIMDID_UARCH_HYBRID_P +/* Topology 2-bit field (bits [1..0] of uArchHi byte): + * 00 = unknown (undetected hybrid type or pre-CPUID.1A hardware) + * 01 = E-core (efficiency/small core in a hybrid CPU) + * 10 = P-core (performance/big core in a hybrid CPU) + * 11 = unified (confirmed non-hybrid; numerically strongest) + * Higher numeric value == stronger / higher-capability topology. */ +#define SIMDID_UARCH_HYBRID_E (1U << 0) /* E-core (efficiency/small) in a hybrid topology */ +#define SIMDID_UARCH_HYBRID_P (1U << 1) /* P-core (performance/big) in a hybrid topology */ +#define SIMDID_UARCH_SIMD_DUAL (1U << 2) /* SIMD is glued/dual (summary: AVX_DUAL or AVX512_IMPL=DUAL) */ +#endif + // Pack helpers (define locally to avoid missing macros when included standalone) #ifndef SIMDID_PACK_ROOT #define SIMDID_PACK_ROOT(root) (((uint64_t)(root) & 0xFULL) << SIMDID_SHIFT_ROOT) diff --git a/StgSharp.Native/sn-src/simdid.x86.c b/StgSharp.Native/sn-src/simdid.x86.c index 5d8fde2a..7b9c39fd 100644 --- a/StgSharp.Native/sn-src/simdid.x86.c +++ b/StgSharp.Native/sn-src/simdid.x86.c @@ -77,6 +77,25 @@ static int is_intel_native_avx512(int family, int model) } } +/* Returns 1 if this AMD/Hygon CPU uses split-lane YMM (two fused 128-bit execution units). + * - AMD Zen1 (family 0x17, model < 0x30) and Zen+ (0x18-0x2F) use a split/glued path. + * - Hygon Dhyana is architecturally Zen1-based; always split. + * - 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) + 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+ */ +} + SN_API SIMDID SN_DECL sn_get_simd_level_local() { SIMDID _simdid = { 0 }; @@ -93,9 +112,12 @@ SN_API SIMDID SN_DECL sn_get_simd_level_local() const int is_intel = (strcmp(vendor, "GenuineIntel") == 0); const int is_amd = (strcmp(vendor, "AuthenticAMD") == 0) || (strcmp(vendor, "HygonGenuine") == 0); + const int is_via = (strcmp(vendor, "CentaurHauls") == 0) || + (memcmp(vendor, " Shanghai ", 12) == 0); /* VIA / 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); @@ -108,12 +130,14 @@ SN_API SIMDID SN_DECL sn_get_simd_level_local() 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_via ? SIMDID_MANU_VIA : SIMDID_MANU_UNKNOWN); uint64_t main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_NONE); 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 */ /* Baseline main level */ if (has_sse42) @@ -122,6 +146,10 @@ SN_API SIMDID SN_DECL sn_get_simd_level_local() 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) + avx_bits |= SIMDID_AVX_DUAL; + /* CPUID.7.0 */ get_cpuid(7, 0, &eax, &ebx, &ecx, &edx); const int has_bmi1 = (ebx & (1 << 3)) != 0; @@ -140,6 +168,7 @@ SN_API SIMDID SN_DECL sn_get_simd_level_local() 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); @@ -217,9 +246,28 @@ SN_API SIMDID SN_DECL sn_get_simd_level_local() } } + /* 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; + 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 */ + } 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_AVX10(avx10_bits) | SIMDID_PACK_UARCH(uarch_bits); return _simdid; } diff --git a/StgSharp.Native/sn-src/simdid_compare.x86.c b/StgSharp.Native/sn-src/simdid_compare.x86.c index 0f3608c2..40a4f4bd 100644 --- a/StgSharp.Native/sn-src/simdid_compare.x86.c +++ b/StgSharp.Native/sn-src/simdid_compare.x86.c @@ -61,7 +61,7 @@ static int impl_strength(uint16_t avx512) * - AVX features : bitwise AND * - AVX10 features : bitwise AND * - Root / Manu : keep left (must be same arch in practice) - * - uArchHi : min (most conservative policy) + * - 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) { @@ -102,10 +102,14 @@ SN_API SIMDID SN_DECL sn_get_unite_simd(SIMDID left, SIMDID right) /* AVX10 (byte 6): AND */ out.make_byte[6] = left.make_byte[6] & right.make_byte[6]; - /* uArchHi (byte 7): min */ + /* 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); - out.make_byte[7] = (char)(lu < ru ? lu : ru); + 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; } @@ -142,8 +146,12 @@ SN_API SIMDID SN_DECL sn_get_unite_simd(SIMDID left, SIMDID right) * DUAL(weakest) < UNKNOWN < NATIVE(strongest). * This is not ISA capability but execution-width risk. * - * 6. uArchHi (final tie-break) - * Higher value = more specific / stronger policy hint. + * 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) { @@ -186,11 +194,19 @@ SN_API int SN_DECL sn_compare_simd(SIMDID left, SIMDID right) return ls > rs ? 1 : -1; } - /* 6. uArchHi (final tie-break) */ + /* 6. uArchHi topology bits [1..0]: unified(3) > P(2) > E(1) > unknown(0) */ unsigned lu = FIELD_UARCH(left); unsigned ru = FIELD_UARCH(right); - if (lu != ru) - return lu > ru ? 1 : -1; + 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; }