Skip to content

Instantly share code, notes, and snippets.

@SARDONYX-sard
Last active August 3, 2022 20:24
Show Gist options
  • Save SARDONYX-sard/76d43ecee88aa1f291b743f41bcdef3b to your computer and use it in GitHub Desktop.
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.
$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))
"@
@SARDONYX-sard
Copy link
Author

SARDONYX-sard commented Jun 28, 2022

2022-06-29 (13)

@SARDONYX-sard
Copy link
Author

2022-06-29 (6)

@SARDONYX-sard
Copy link
Author

SARDONYX-sard commented Jun 29, 2022

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