/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuCpuId.cpp Date: 2022-1-25 Author: Reece ***/ #include #include "AuHWInfo.hpp" #include "AuCpuId.hpp" #if defined(AURORA_COMPILER_CLANG) || defined(AURORA_IS_POSIX_DERIVED) #include #endif #include "AuCpuInfo.hpp" namespace Aurora::HWInfo { static AuUInt32 gGuessedCores {}; static AuUInt32 gGuessedThreads {}; union CPUIdContext { struct { AuUInt32 eax; AuUInt32 ebx; AuUInt32 ecx; AuUInt32 edx; }; int regs[4]; }; #if defined(AURORA_ARCH_X64) || defined(AURORA_ARCH_X86) #if defined(AURORA_COMPILER_MSVC) static CPUIdContext cpuid(AuUInt32 a) { CPUIdContext context; __cpuid(context.regs, a); return context; } #elif defined(AURORA_COMPILER_CLANG) || defined(AURORA_COMPILER_GCC) static CPUIdContext cpuid(AuUInt32 a) { CPUIdContext context; __get_cpuid(a, &context.eax, &context.ebx, &context.ecx, &context.edx); return context; } #else static CPUIdContext cpuid(AuUInt32 a) { return {}; } #endif #else static CPUIdContext cpuid(AuUInt32 a) { return {}; } #endif bool CpuId::SSE3() const { return AuTestBit(f_1_ECX, 0); } bool CpuId::PCLMULQDQ() const { return AuTestBit(f_1_ECX, 1); } bool CpuId::MONITOR() const { return AuTestBit(f_1_ECX, 3); } bool CpuId::SSSE3() const { return AuTestBit(f_1_ECX, 9); } bool CpuId::FMA() const { return AuTestBit(f_1_ECX, 12); } bool CpuId::CMPXCHG16B() const { return AuTestBit(f_1_ECX, 13); } bool CpuId::SSE41() const { return AuTestBit(f_1_ECX, 19); } bool CpuId::SSE42() const { return AuTestBit(f_1_ECX, 20); } bool CpuId::MOVBE() const { return AuTestBit(f_1_ECX, 22); } bool CpuId::POPCNT() const { return AuTestBit(f_1_ECX, 23); } bool CpuId::AES() const { return AuTestBit(f_1_ECX, 25); } bool CpuId::XSAVE() const { return AuTestBit(f_1_ECX, 26); } bool CpuId::OSXSAVE() const { return AuTestBit(f_1_ECX, 27); } bool CpuId::AVX() const { return AuTestBit(f_1_ECX, 28); } bool CpuId::F16C() const { return AuTestBit(f_1_ECX, 29); } bool CpuId::RDRAND() const { return AuTestBit(f_1_ECX, 30); } bool CpuId::MSR() const { return AuTestBit(f_1_EDX, 5); } bool CpuId::CX8() const { return AuTestBit(f_1_EDX, 8); } bool CpuId::SEP() const { return AuTestBit(f_1_EDX, 11); } bool CpuId::CMOV() const { return AuTestBit(f_1_EDX, 15); } bool CpuId::CLFSH() const { return AuTestBit(f_1_EDX, 19); } bool CpuId::MMX() const { return AuTestBit(f_1_EDX, 23); } bool CpuId::FXSR() const { return AuTestBit(f_1_EDX, 24); } bool CpuId::SSE() const { return AuTestBit(f_1_EDX, 25); } bool CpuId::SSE2() const { return AuTestBit(f_1_EDX, 26); } bool CpuId::FSGSBASE() const { return AuTestBit(f_7_EBX, 0); } bool CpuId::BMI1() const { return AuTestBit(f_7_EBX, 3); } bool CpuId::HLE() const { return isIntel && AuTestBit(f_7_EBX, 4); } bool CpuId::AVX2() const { return AuTestBit(f_7_EBX, 5); } bool CpuId::BMI2() const { return AuTestBit(f_7_EBX, 8); } bool CpuId::ERMS() const { return AuTestBit(f_7_EBX, 9); } bool CpuId::INVPCID() const { return AuTestBit(f_7_EBX, 10); } bool CpuId::RTM() const { return isIntel && AuTestBit(f_7_EBX, 11); } bool CpuId::AVX512F() const { return AuTestBit(f_7_EBX, 16); } bool CpuId::RDSEED() const { return AuTestBit(f_7_EBX, 18); } bool CpuId::ADX() const { return AuTestBit(f_7_EBX, 19); } bool CpuId::AVX512PF() const { return AuTestBit(f_7_EBX, 26); } bool CpuId::AVX512ER() const { return AuTestBit(f_7_EBX, 27); } bool CpuId::AVX512CD() const { return AuTestBit(f_7_EBX, 28); } bool CpuId::SHA() const { return AuTestBit(f_7_EBX, 29); } bool CpuId::PREFETCHWT1() const { return AuTestBit(f_7_ECX, 0); } bool CpuId::LAHF() const { return AuTestBit(f_81_ECX, 0); } bool CpuId::LZCNT() const { return isIntel && AuTestBit(f_81_ECX, 5); } bool CpuId::ABM() const { return isAMD && AuTestBit(f_81_ECX, 5); } bool CpuId::SSE4a() const { return isAMD && AuTestBit(f_81_ECX, 6); } bool CpuId::XOP() const { return isAMD && AuTestBit(f_81_ECX, 11); } bool CpuId::TBM() const { return isAMD && AuTestBit(f_81_ECX, 21); } bool CpuId::SYSCALL() const { return isIntel && AuTestBit(f_81_EDX, 11); } bool CpuId::MMXEXT() const { return isAMD && AuTestBit(f_81_EDX, 22); } bool CpuId::RDTSCP() const { return isIntel && AuTestBit(f_81_EDX, 27); } bool CpuId::_3DNOWEXT() const { return isAMD && AuTestBit(f_81_EDX, 30); } bool CpuId::_3DNOW() const { return isAMD && AuTestBit(f_81_EDX, 31); } AuString CpuId::ToString() const { return fmt::format( "FMA {}\t\tFSGSBASE {}\t\tCLFSH {}\t\t\tERMS {}{}" "CMPXCHG16B {}\t\tAVX512PF {}\t\tMOVBE {}\t\t\tRTM {}{}" "POPCNT {}\t\tAVX512ER {}\t\tMONITOR {}\t\t\tLAHF {}{}" "SSE {}\t\tAVX512CD {}\t\tF16C {}\t\t\tABM {}{}" "SSE2 {}\t\tSYSCALL {}\t\tRDRAND {}\t\t\tXOP {}{}" "SSE3 {}\t\tAES {}\t\tMSR {}\t\t\tTBM {}{}" "SSSE3 {}\t\tRDTSCP {}\t\tCX8 {}\t\t\tMMXEXT {}{}" "SSE41 {}\t\tXSAVE {}\t\tSEP {}\t\t\tRDSEED {}{}" "SSE42 {}\t\tOSXSAVE {}\t\tCMOV {}\t\t\tPREFETCHWT1 {}{}" "SSE4a {}\t\tSHA {}\t\tFXSR {}\t\t\tPCLMULQDQ {}{}" "AVX {}\t\tAVX512F {}\t\tBMI1 {}\t\t\tINVPCID {}{}" "AVX2 {}\t\tLZCNT {}\t\tHLE {}\t\t\t_3DNOWEXT {}{}" "MMX {}\t\tADX {}\t\tBMI2 {}\t\t\t_3DNOW {}", FMA(), FSGSBASE(), CLFSH(), ERMS(), Aurora::Locale::NewLine(), CMPXCHG16B(), AVX512PF(), MOVBE(), RTM(), Aurora::Locale::NewLine(), POPCNT(), AVX512ER(), MONITOR(), LAHF(), Aurora::Locale::NewLine(), SSE(), AVX512CD(), F16C(), ABM(), Aurora::Locale::NewLine(), SSE2(), SYSCALL(), RDRAND(), XOP(), Aurora::Locale::NewLine(), SSE3(), AES(), MSR(), TBM(), Aurora::Locale::NewLine(), SSSE3(), RDTSCP(), CX8(), MMXEXT(), Aurora::Locale::NewLine(), SSE41(), XSAVE(), SEP(), RDSEED(), Aurora::Locale::NewLine(), SSE42(), OSXSAVE(), CMOV(), PREFETCHWT1(), Aurora::Locale::NewLine(), SSE4a(), SHA(), FXSR(), PCLMULQDQ(), Aurora::Locale::NewLine(), AVX(), AVX512F(), BMI1(), INVPCID(), Aurora::Locale::NewLine(), AVX2(), LZCNT(), HLE(), _3DNOWEXT(), Aurora::Locale::NewLine(), MMX(), ADX(), BMI2(), _3DNOW() ); } AuUInt32 GetCPUIDAPICDCores() { return gGuessedCores; } AuUInt32 GetCPUIDAPICDThreads() { return gGuessedThreads; } void SetCpuId() { // Credit: https://docs.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex?view=msvc-160 #if defined(AURORA_ARCH_X64) || defined(AURORA_ARCH_X86) AuList data; AuList extdata; auto cpuInfo = cpuid(0); auto nIds = cpuInfo.eax; for (AuUInt i = 0; i <= nIds; ++i) { data.push_back(cpuid(i)); } char vendor[0x20]; AuMemset(vendor, 0, sizeof(vendor)); *reinterpret_cast(vendor) = cpuInfo.ebx; *reinterpret_cast(vendor + 4) = cpuInfo.edx; *reinterpret_cast(vendor + 8) = cpuInfo.ecx; gCpuInfo.cpuId.vendor = vendor; { auto regs = cpuid(1); gGuessedThreads = (regs.ebx >> 16) & 0xff; } if (gCpuInfo.cpuId.vendor == "GenuineIntel") { gCpuInfo.cpuId.isIntel = true; auto regs = cpuid(4); gGuessedCores = ((regs.eax >> 26) & 0x3f) + 1; // DCP cache if (!gGuessedThreads) { gGuessedThreads = gGuessedCores; } } else if (gCpuInfo.cpuId.vendor == "AuthenticAMD") { gCpuInfo.cpuId.isAMD = true; if (!gGuessedCores) { auto regs = cpuid(0x80000008); gGuessedCores = (AuUInt32(regs.ecx) & 0xfful) + 1; if (!gGuessedThreads) { gGuessedThreads = gGuessedCores; } } } if (!gGuessedThreads && gGuessedCores) { gGuessedThreads = gGuessedCores; } // load bitset with flags for function 0x00000001 if (nIds >= 1) { gCpuInfo.cpuId.f_1_ECX = data[1].ecx; gCpuInfo.cpuId.f_1_EDX = data[1].edx; } // load bitset with flags for function 0x00000007 if (nIds >= 7) { gCpuInfo.cpuId.f_7_EBX = data[7].ebx; gCpuInfo.cpuId.f_7_ECX = data[7].ecx; } // gets the number of the highest valid extended ID. auto cpui = cpuid(0x80000000); auto nExIds = cpui.eax; char brand[0x40]; AuMemset(brand, 0, sizeof(brand)); for (AuUInt i = 0x80000000u; i <= nExIds; ++i) { extdata.push_back(cpuid(i)); } // load bitset with flags for function 0x80000001 if (nExIds >= 0x80000001) { gCpuInfo.cpuId.f_81_ECX = extdata[1].ecx; gCpuInfo.cpuId.f_81_EDX = extdata[1].edx; } // Interpret CPU brand string if reported if (nExIds >= 0x80000004) { AuMemcpy(brand, &extdata[2], sizeof(cpui)); AuMemcpy(brand + 16, &extdata[3], sizeof(cpui)); AuMemcpy(brand + 32, &extdata[4], sizeof(cpui)); gCpuInfo.cpuId.brand = brand; } #endif } }