Last active
August 3, 2022 20:24
-
-
Save SARDONYX-sard/76d43ecee88aa1f291b743f41bcdef3b to your computer and use it in GitHub Desktop.
Check SIMD extension support in PowerShell on Windows 10 or 11 immediately after a clean install.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$Source = @" | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Reflection; | |
using System.Runtime.InteropServices; | |
public class CpuID : IDisposable | |
{ | |
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)] | |
private delegate void CpuIDDelegate(int level, IntPtr ptr); | |
[StructLayout(LayoutKind.Sequential, Size = 16)] | |
public struct CpuIdResult | |
{ | |
public int Eax; | |
public int Ebx; | |
public int Ecx; | |
public int Edx; | |
public int Xcr; // Extended control register | |
} | |
// https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc | |
[DllImport("kernel32.dll", SetLastError = true)] | |
private static extern IntPtr VirtualAlloc( | |
IntPtr lpAddress, | |
UIntPtr dwSize, | |
AllocationType flAllocationType, | |
MemoryProtection flProtect | |
); | |
[DllImport("kernel32")] | |
private static extern bool VirtualFree( | |
IntPtr lpAddress, | |
UInt32 dwSize, | |
UInt32 dwFreeType | |
); | |
[Flags()] | |
public enum AllocationType : uint | |
{ | |
COMMIT = 0x1000, | |
RESERVE = 0x2000, | |
RELEASE = 0x8000, | |
RESET = 0x80000, | |
LARGE_PAGES = 0x20000000, | |
PHYSICAL = 0x400000, | |
TOP_DOWN = 0x100000, | |
WRITE_WATCH = 0x200000 | |
} | |
[Flags()] | |
public enum MemoryProtection : uint | |
{ | |
EXECUTE = 0x10, | |
EXECUTE_READ = 0x20, | |
EXECUTE_READWRITE = 0x40, | |
EXECUTE_WRITECOPY = 0x80, | |
NOACCESS = 0x01, | |
READONLY = 0x02, | |
READWRITE = 0x04, | |
WRITECOPY = 0x08, | |
GUARD_Modifierflag = 0x100, | |
NOCACHE_Modifierflag = 0x200, | |
WRITECOMBINE_Modifierflag = 0x400 | |
} | |
private CpuIDDelegate cpuIdDelg; | |
private IntPtr codePointer; | |
// void x86CpuId(int level, byte* buffer) | |
// { | |
// eax = level | |
// cpuid | |
// buffer[0] = eax | |
// buffer[4] = ebx | |
// buffer[8] = ecx | |
// buffer[12] = edx | |
// } | |
private byte[] x86CodeBytes = | |
{ | |
0x55, // push ebp | |
0x8B, 0xEC, // mov ebp,esp | |
0x53, // push ebx | |
0x57, // push edi | |
0x8B, 0x45, 0x08, // mov eax, dword ptr [ebp+8] (move level into eax) | |
0x0F, 0xA2, // cpuid | |
0x8B, 0x7D, 0x0C, // mov edi, dword ptr [ebp+12] (move address of buffer into edi) | |
0x89, 0x07, // mov dword ptr [edi+0], eax (write eax, ... to buffer) | |
0x89, 0x5F, 0x04, // mov dword ptr [edi+4], ebx | |
0x89, 0x4F, 0x08, // mov dword ptr [edi+8], ecx | |
0x89, 0x57, 0x0C, // mov dword ptr [edi+12],edx | |
0x5F, // pop edi | |
0x5B, // pop ebx | |
0x8B, 0xE5, // mov esp,ebp | |
0x5D, // pop ebp | |
0xc3 // ret | |
}; | |
private byte[] x64CodeBytes = | |
{ | |
0x53, // push rbx this gets clobbered by cpuid | |
// rcx is level | |
// rdx is buffer. | |
// Need to save buffer elsewhere, cpuid overwrites rdx | |
// Put buffer in r8, use r8 to reference buffer later. | |
// Save rdx (buffer addy) to r8 | |
0x49, 0x89, 0xd0, // mov r8, rdx | |
// Move ecx (level) to eax to call cpuid, call cpuid | |
0x89, 0xc8, // mov eax, ecx | |
0x31, 0xC9, // xor ecx, ecx | |
0x0F, 0xA2, // cpuid | |
// Write eax et al to buffer | |
0x41, 0x89, 0x40, 0x00, // mov dword ptr [r8+0], eax | |
0x41, 0x89, 0x58, 0x04, // mov dword ptr [r8+4], ebx | |
0x41, 0x89, 0x48, 0x08, // mov dword ptr [r8+8], ecx | |
0x41, 0x89, 0x50, 0x0c, // mov dword ptr [r8+12], edx | |
// Call xgetbv to confirm that the OS supports AVX (only 64bit) | |
// | |
// See more: https://www.felixcloutier.com/x86/xgetbv | |
// unsigned __int64 __cdecl _xgetbv(unsigned int); | |
// | |
// EDX:EAX <- ECR(extended control register) xgetbv | |
0x31, 0xC9, // xor ecx, ecx | |
0x0f, 0x01, 0xd0, // xgetbv | |
0x48, 0xc1, 0xe2, 0x20, // shl rdx, 32 | |
0x48, 0x09, 0xd0, // or rax, rdx | |
0x49, 0x89, 0x40, 0x10, // mov qword ptr [r8+16], rax | |
0x5b, // pop rbx | |
0xc3 // ret | |
}; | |
public CpuID() | |
{ | |
Compile(); | |
} | |
~CpuID() | |
{ | |
Dispose(false); | |
} | |
private void Compile() | |
{ | |
byte[] codeBytes; | |
if (IntPtr.Size == 4) | |
{ | |
codeBytes = x86CodeBytes; | |
} | |
else | |
{ | |
codeBytes = x64CodeBytes; | |
} | |
this.codePointer = VirtualAlloc( | |
IntPtr.Zero, | |
new UIntPtr((uint)codeBytes.Length), | |
AllocationType.COMMIT | AllocationType.RESERVE, | |
MemoryProtection.EXECUTE_READWRITE | |
); | |
Marshal.Copy(codeBytes, 0, this.codePointer, codeBytes.Length); | |
this.cpuIdDelg = (CpuIDDelegate)Marshal.GetDelegateForFunctionPointer(this.codePointer, typeof(CpuIDDelegate)); | |
} | |
public CpuIdResult Invoke(int level) | |
{ | |
CpuIdResult result; | |
IntPtr buffer = Marshal.AllocHGlobal(24); | |
try | |
{ | |
this.cpuIdDelg(level, buffer); | |
result = (CpuIdResult)Marshal.PtrToStructure(buffer, typeof(CpuIdResult)); | |
} | |
finally | |
{ | |
Marshal.FreeHGlobal(buffer); | |
} | |
return result; | |
} | |
public void Dispose() | |
{ | |
Dispose(true); | |
} | |
public void Dispose(bool disposing) | |
{ | |
if (this.codePointer != IntPtr.Zero) | |
{ | |
// 0x8000 is release | |
VirtualFree(this.codePointer, 0, 0x8000); | |
this.codePointer = IntPtr.Zero; | |
} | |
} | |
} | |
"@ | |
Add-Type -TypeDefinition $Source | |
# Misc. | |
[bool]$HW_MMX = $False | |
[bool]$HW_x64 = $False | |
[bool]$HW_ABM = $False # Advanced Bit Manipulation | |
[bool]$HW_RDRAND = $False | |
[bool]$HW_BMI1 = $False | |
[bool]$HW_BMI2 = $False | |
[bool]$HW_ADX = $False | |
[bool]$HW_PREFETCHWT1 = $False | |
# SIMD: 128-bit | |
[bool]$HW_SSE = $False | |
[bool]$HW_SSE2 = $False | |
[bool]$HW_SSE3 = $False | |
[bool]$HW_SSSE3 = $False | |
[bool]$HW_SSE41 = $False | |
[bool]$HW_SSE42 = $False | |
[bool]$HW_SSE4a = $False | |
[bool]$HW_AES = $False | |
[bool]$HW_SHA = $False | |
[bool]$AvxSupported = $False | |
# SIMD: 256-bit | |
[bool]$HW_AVX = $False | |
[bool]$HW_XOP = $False | |
[bool]$HW_FMA3 = $False | |
[bool]$HW_FMA4 = $False | |
[bool]$HW_AVX2 = $False | |
# SIMD: 512-bit | |
[bool]$HW_AVX512F = $False # AVX512 Foundation | |
[bool]$HW_AVX512CD = $False # AVX512 Conflict Detection | |
[bool]$HW_AVX512PF = $False # AVX512 Prefetch | |
[bool]$HW_AVX512ER = $False # AVX512 Exponential + Reciprocal | |
[bool]$HW_AVX512VL = $False # AVX512 Vector Length Extensions | |
[bool]$HW_AVX512BW = $False # AVX512 Byte + Word | |
[bool]$HW_AVX512DQ = $False # AVX512 Doubleword + Quadword | |
[bool]$HW_AVX512IFMA = $False # AVX512 Integer 52-bit Fused Multiply-Add | |
[bool]$HW_AVX512VBMI = $False # AVX512 Vector Byte Manipulation Instructions | |
$cpuid = New-Object -TypeName CpuID | |
$info = $cpuid.Invoke(0) | |
$nIds = $info.Eax | |
# https://en.wikipedia.org/wiki/CPUID | |
# https://www.intel.co.jp/content/dam/www/public/ijkk/jp/ja/documents/developer/Processor_Identification_071405_i.pdf | |
# CPUID 命令の出力によるとEAXに0x80000000以上を指定すると拡張プロセッサ情報を返す | |
$info = $cpuid.Invoke(0x80000000) | |
# 最大値を超える入力パラメータを指定してCPUIDを実行した場合や、 | |
# 拡張機能がサポートされていない場合は、EAX レジスタのビット 31 がクリアされる | |
$nExIds = $info.EaX | |
# Detect Features | |
# 31bitがクリアされて、eaxが1以下になっていないか? | |
# つまり拡張機能がサポートされているか? | |
if ($nIds -ge 0x00000001) { | |
$info = $cpuid.Invoke(0x00000001) | |
# $info.Edxなどに入っているbit配列はFlagとなっており、 | |
# 各項目が有効ならば1、そうでなければ0になっている。 | |
# このbit演算ではそれを確かめている。 | |
$HW_MMX = ($info.Edx -band (1 -shl 23)) -ne 0 | |
$HW_SSE = ($info.Edx -band (1 -shl 25)) -ne 0 | |
$HW_SSE2 = ($info.Edx -band (1 -shl 26)) -ne 0 | |
$HW_SSE3 = $info.Ecx -band (1 -shl 0) -ne 0 | |
$HW_SSSE3 = ($info.Ecx -band (1 -shl 9)) -ne 0 | |
$HW_SSE41 = ($info.Ecx -band (1 -shl 19)) -ne 0 | |
$HW_SSE42 = ($info.Ecx -band (1 -shl 20)) -ne 0 | |
$HW_AES = ($info.Ecx -band (1 -shl 25)) -ne 0 | |
$HW_AVX = ($info.Ecx -band (1 -shl 28)) -ne 0 | |
$HW_FMA3 = ($info.Ecx -band (1 -shl 12)) -ne 0 | |
$HW_RDRAND = ($info.Ecx -band (1 -shl 30)) -ne 0 | |
# https://insufficientlycomplicated.wordpress.com/2011/11/07/detecting-intel-advanced-vector-extensions-avx-in-visual-studio/ | |
# AVXのチェックには3つのことが必要。 | |
# 1) CPUIDは、OSがXSAVEとXRSTORE命令を使用することを示す。 | |
# (コンテキストスイッチでYMMレジスタを保存できる) | |
# 2) CPUIDがAVXをサポートしていることを示す。 | |
# 3) XGETBV は AVX レジスタが保存され、コンテキストスイッチで復元されることを示す。 | |
# XGETBVは686以降のCPUでのみ利用可能であるため、 | |
# 条件付きで実行する必要があることに注意。 | |
$osUsesXSAVE_XRSTORE = $info.Ecx -band (1 -shl 27) -ne 0 | |
if ($osUsesXSAVE_XRSTORE -and $HW_AVX) { | |
$xcr0 = $info.Xcr | |
$AvxSupported = ($xcr0 -band 6) -eq 6 # Check if the OS will save the YMM registers | |
} | |
} | |
if ($nIds -ge 0x00000007) { | |
$info = $cpuid.Invoke(0x00000007) | |
$HW_AVX2 = ($info.Ebx -band (1 -shl 5)) -ne 0 | |
$HW_BMI1 = ($info.Ebx -band (1 -shl 3)) -ne 0 | |
$HW_BMI2 = ($info.Ebx -band (1 -shl 8)) -ne 0 | |
$HW_ADX = ($info.Ebx -band (1 -shl 19)) -ne 0 | |
$HW_SHA = ($info.Ebx -band (1 -shl 29)) -ne 0 | |
$HW_PREFETCHWT1 = ($info.Ecx -band (1 -shl 0)) -ne 0 | |
$HW_AVX512F = ($info.Ebx -band (1 -shl 16)) -ne 0 | |
$HW_AVX512CD = ($info.Ebx -band (1 -shl 28)) -ne 0 | |
$HW_AVX512PF = ($info.Ebx -band (1 -shl 26)) -ne 0 | |
$HW_AVX512ER = ($info.Ebx -band (1 -shl 27)) -ne 0 | |
$HW_AVX512VL = ($info.Ebx -band (1 -shl 31)) -ne 0 | |
$HW_AVX512BW = ($info.Ebx -band (1 -shl 30)) -ne 0 | |
$HW_AVX512DQ = ($info.Ebx -band (1 -shl 17)) -ne 0 | |
$HW_AVX512IFMA = ($info.Ebx -band (1 -shl 21)) -ne 0 | |
$HW_AVX512VBMI = ($info.Ecx -band (1 -shl 1)) -ne 0 | |
} | |
if ($nExIds -ge 0x80000001) { | |
$info = $cpuid.Invoke(0x80000001) | |
$osUsesXSAVE_XRSTORE = $info.Ecx -band (1 -shl 27) -ne 0 | |
$HW_x64 = ($info.Edx -band (1 -shl 29)) -ne 0 | |
$HW_ABM = ($info.Ecx -band (1 -shl 5)) -ne 0 | |
$HW_SSE4a = ($info.Ecx -band (1 -shl 6)) -ne 0 | |
$HW_FMA4 = ($info.Ecx -band (1 -shl 16)) -ne 0 | |
$HW_XOP = ($info.Ecx -band (1 -shl 11)) -ne 0 | |
} | |
function message($isSupported, $isUnknown) { | |
$CSI = "$([char] 27)[" | |
if ($isUnknown) { | |
return "$($CSI)95mUnknown $($CSI)0m" | |
} | |
if ($isSupported) { | |
return "$($CSI)92mSupported $($CSI)0m" | |
} | |
else { | |
return "$($CSI)91mNot Supported $($CSI)0m" | |
} | |
} | |
Write-Output @" | |
Your CPU: $(Get-WmiObject -Class Win32_Processor -ComputerName. | Select-Object -Property Name | ForEach-Object {$_.Name}) | |
Do the OS & CPU's support AVX instructions?: $(message($AvxSupported, $HW_x64)) | |
# Misc. | |
MMX: $(message($HW_MMX)) | |
x64: $(message($HW_x64)) | |
Advanced Bit Manipulation: $(message($HW_ABM)) | |
#RDRAND: $(message($HW_RDRAND)) | |
BMI1: $(message($HW_BMI1)) | |
BMI2: $(message($HW_BMI2)) | |
# SIMD: 128-bit | |
SSE: $(message($HW_SSE)) | |
SSE2: $(message($HW_SSE2)) | |
SSE3: $(message($HW_SSE3)) | |
SSSE3: $(message($HW_SSSE3)) | |
SSE4.1: $(message($HW_SSE41)) | |
SSE4.2: $(message($HW_SSE42)) | |
SSE4a: $(message($HW_SSE4a)) | |
AES: $(message($HW_AES)) | |
SHA: $(message($HW_SHA)) | |
# SIMD: 256-bit | |
AVX: $(message($HW_AVX)) | |
XOP: $(message($HW_XOP)) | |
FMA3: $(message($HW_FMA3)) | |
FMA4: $(message($HW_FMA4)) | |
AVX2: $(message($HW_AVX2)) | |
# SIMD: 512-bit | |
AVX512 Foundation: $(message($HW_AVX512F)) | |
AVX512 Conflict Detection: $(message($HW_AVX512CD)) | |
AVX512 Prefetch: $(message($HW_AVX512PF)) | |
AVX512 Exponential + Reciprocal: $(message($HW_AVX512ER)) | |
AVX512 Vector Length Extensions: $(message($HW_AVX512VL)) | |
AVX512 Byte + Word: $(message($HW_AVX512BW)) | |
AVX512 Doubleword + Quadword: $(message($HW_AVX512DQ)) | |
AVX512 Integer 52-bit Fused Multiply-Add: $(message($HW_AVX512IFMA)) | |
AVX512 Vector Byte Manipulation Instructions: $(message($HW_AVX512VBMI)) | |
"@ |
PowerShell Core Version
$Source = @"
using System.Text;
using System.Runtime.Intrinsics.X86;
public class SimdInfo
{
public static string GetCpuInfo()
{
StringBuilder sb = new StringBuilder();
sb.Append("Does this CPU support each SIMD extension?" + "\n");
sb.Append("true: Yes\nfalse: No" + "\n\n");
sb.Append(" SSE: " + Sse.IsSupported.ToString() + "\n");
sb.Append(" SSE2: " + Sse2.IsSupported.ToString() + "\n");
sb.Append(" SSE3: " + Sse3.IsSupported.ToString() + "\n");
sb.Append(" SSSE3: " + Ssse3.IsSupported.ToString() + "\n");
sb.Append("SSE4.1: " + Sse41.IsSupported.ToString() + "\n");
sb.Append("SSE4.2: " + Sse42.IsSupported.ToString() + "\n");
sb.Append(" AVX: " + Avx.IsSupported.ToString() + "\n");
sb.Append(" AVX2: " + Avx2.IsSupported.ToString() + "\n");
sb.Append("AVX512: " + AvxVnni.IsSupported.ToString() + "\n"); // This is preview
return sb.ToString();
}
}
"@
Add-Type -TypeDefinition $Source
[SimdInfo]::GetCpuInfo()
Read-Host("Press any key.")
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
ref: