Clean up MUtils::CPUFetaures code.

This commit is contained in:
LoRd_MuldeR 2016-12-23 22:14:25 +01:00
parent 1cff20a3ef
commit 37e7f72069
4 changed files with 108 additions and 79 deletions

View File

@ -19,6 +19,13 @@
// http://www.gnu.org/licenses/lgpl-2.1.txt
//////////////////////////////////////////////////////////////////////////////////
/**
* @file
* @brief This file contains function for detecting information about the CPU
*
* Call the MUtils::CPUFetaures::detect() to detect information about the processor, which will be returned in a `MUtils::CPUFetaures::cpu_info_t` struct.
*/
#pragma once
//MUtils
@ -29,33 +36,52 @@
namespace MUtils
{
/**
* \brief This namespace contains functions and constants for detecting CPU information
*
* Call the detect() to detect information about the processor, which will be returned in a `cpu_info_t` struct.
*/
namespace CPUFetaures
{
//CPU flags
static const quint32 FLAG_MMX = 0x01;
static const quint32 FLAG_SSE = 0x02;
static const quint32 FLAG_SSE2 = 0x04;
static const quint32 FLAG_SSE3 = 0x08;
static const quint32 FLAG_SSSE3 = 0x10;
static const quint32 FLAG_SSE4 = 0x20;
static const quint32 FLAG_SSE42 = 0x40;
static const quint32 FLAG_AVX = 0x80;
// CPU vendor flag
static const uint8_t VENDOR_INTEL = 0x01U; ///< \brief CPU vendor flag \details Indicates that the CPU's vendor is *Intel*
static const uint8_t VENDOR_AMD = 0x02U; ///< \brief CPU vendor flag \details Indicates that the CPU's vendor is *AMD*
//CPU features
typedef struct _cpu_info_t
// CPU feature flag
static const quint32 FLAG_CMOV = 0x001U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *CMOV* instruction
static const quint32 FLAG_MMX = 0x002U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *MMX* instruction set extension
static const quint32 FLAG_SSE = 0x004U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *SSE* instruction set extension
static const quint32 FLAG_SSE2 = 0x008U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *SSE2* instruction set extension
static const quint32 FLAG_SSE3 = 0x010U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *SSE3* instruction set extension
static const quint32 FLAG_SSSE3 = 0x020U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *SSSE3* instruction set extension
static const quint32 FLAG_SSE4 = 0x030U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *SSE4.1* instruction set extension
static const quint32 FLAG_SSE42 = 0x080U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *SSE4.2* instruction set extension
static const quint32 FLAG_AVX = 0x100U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *AVX* instruction set extension
/**
* \brief Struct to hold information about the CPU
*/
typedef struct
{
quint32 family;
quint32 model;
quint32 stepping;
quint32 count;
quint32 features;
bool x64;
bool intel;
char vendor[0x40];
char brand[0x40];
quint32 family; ///< CPU *family* indicator, which specifies the processor "generation" to which the CPU belongs
quint32 model; ///< CPU *model* indicator, which is used to distinguish processor "variants" within a generation
quint32 stepping; ///< CPU *stepping* indicator, which is used to distinguish "revisions" of a certain processor model
quint32 count; ///< The number of available (logical) processors
quint32 features; ///< CPU *feature* flags, indicating suppoprt for extended instruction sets; all flags are OR-combined
bool x64; ///< Indicates that the processor and the operating system support 64-Bit (AMD64/EM64T)
uint8_t vendor; ///< CPU *vendor* flag; might be zero, if vendor is unknown
char idstr[13]; ///< CPU *identifier* string, exactly 12 characters (e.g. "GenuineIntel" or "AuthenticAMD")
char brand[48]; ///< CPU *brand* string, up to 48 characters (e.g. "Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz")
}
cpu_info_t;
/**
* \brief Detect processor information
*
* Detects information about the CPU on which the application is running, including CPU vendor, identifier string, feature flags (MMX, SSE, AVX, etc) as well as the CPU core count.
*
* \return The function returns a `cpu_info_t` struct containing the detected information about the CPU.
*/
MUTILS_API cpu_info_t detect(void);
}
}

View File

@ -80,6 +80,9 @@ class QProcess;
///////////////////////////////////////////////////////////////////////////////
/**
* \brief Global MUtils namespace
*/
namespace MUtils
{
/**

View File

@ -10,6 +10,7 @@
*
* The public API of the *MUtilities* library is defined in the following header files (select file for details):
* - **Global.h** &ndash; miscellaneous useful functions
* - **CPUFeatures.h** &ndash; functions for detection information about the CPU
*
*
* # Example

View File

@ -28,76 +28,73 @@
#include <MUtils/OSSupport.h>
#include "Utils_Win32.h"
#define MY_CPUID(X,Y) __cpuid(((int*)(X)), ((int)(Y)))
#define CHECK_VENDOR(X,Y,Z) (_stricmp((X), (Y)) ? 0U : (Z));
#define CHECK_FLAG(X,Y,Z) (((X) & (Y)) ? (Z) : 0U)
MUtils::CPUFetaures::cpu_info_t MUtils::CPUFetaures::detect(void)
{
const OS::ArgumentMap &args = OS::arguments();
typedef BOOL (WINAPI *IsWow64ProcessFun)(__in HANDLE hProcess, __out PBOOL Wow64Process);
static const quint32 FLAGS_X64 = (FLAG_MMX | FLAG_SSE | FLAG_SSE2);
cpu_info_t features;
cpu_info_t features;
SYSTEM_INFO systemInfo;
int CPUInfo[4] = {-1};
char CPUIdentificationString[0x40];
char CPUBrandString[0x40];
uint32_t cpuInfo[4];
memset(&features, 0, sizeof(cpu_info_t));
//Initialize variables to zero
memset(&features, 0, sizeof(cpu_info_t));
memset(&systemInfo, 0, sizeof(SYSTEM_INFO));
memset(CPUIdentificationString, 0, sizeof(CPUIdentificationString));
memset(CPUBrandString, 0, sizeof(CPUBrandString));
__cpuid(CPUInfo, 0);
memcpy(CPUIdentificationString, &CPUInfo[1], sizeof(int));
memcpy(CPUIdentificationString + 4, &CPUInfo[3], sizeof(int));
memcpy(CPUIdentificationString + 8, &CPUInfo[2], sizeof(int));
features.intel = (_stricmp(CPUIdentificationString, "GenuineIntel") == 0);
strncpy_s(features.vendor, 0x40, CPUIdentificationString, _TRUNCATE);
memset(cpuInfo, 0, sizeof(cpuInfo));
if(CPUInfo[0] >= 1)
//Detect the CPU identifier string
MY_CPUID(&cpuInfo[0], 0);
memcpy(&features.idstr[0U * sizeof(uint32_t)], &cpuInfo[1], sizeof(uint32_t));
memcpy(&features.idstr[1U * sizeof(uint32_t)], &cpuInfo[3], sizeof(uint32_t));
memcpy(&features.idstr[2U * sizeof(uint32_t)], &cpuInfo[2], sizeof(uint32_t));
features.idstr[3U * sizeof(uint32_t)] = '\0';
features.vendor |= CHECK_VENDOR(features.idstr, "GenuineIntel", VENDOR_INTEL);
features.vendor |= CHECK_VENDOR(features.idstr, "AuthenticAMD", VENDOR_AMD);
//Detect the CPU model and feature flags
if(cpuInfo[0] >= 1)
{
__cpuid(CPUInfo, 1);
if(CPUInfo[3] & 0x00800000) features.features |= FLAG_MMX;
if(CPUInfo[3] & 0x02000000) features.features |= FLAG_SSE;
if(CPUInfo[3] & 0x04000000) features.features |= FLAG_SSE2;
if(CPUInfo[2] & 0x00000001) features.features |= FLAG_SSE3;
if(CPUInfo[2] & 0x00000200) features.features |= FLAG_SSSE3;
if(CPUInfo[2] & 0x00080000) features.features |= FLAG_SSE4;
if(CPUInfo[2] & 0x00100000) features.features |= FLAG_SSE42;
if ((CPUInfo[2] & 0x18000000) == 0x18000000)
MY_CPUID(&cpuInfo[0], 1);
features.features |= CHECK_FLAG(cpuInfo[3], 0x00008000, FLAG_CMOV);
features.features |= CHECK_FLAG(cpuInfo[3], 0x00800000, FLAG_MMX);
features.features |= CHECK_FLAG(cpuInfo[3], 0x02000000, FLAG_SSE);
features.features |= CHECK_FLAG(cpuInfo[3], 0x04000000, FLAG_SSE2);
features.features |= CHECK_FLAG(cpuInfo[2], 0x00000001, FLAG_SSE3);
features.features |= CHECK_FLAG(cpuInfo[2], 0x00000200, FLAG_SSSE3);
features.features |= CHECK_FLAG(cpuInfo[2], 0x00080000, FLAG_SSE4);
features.features |= CHECK_FLAG(cpuInfo[2], 0x00100000, FLAG_SSE42);
//Check for AVX
if ((cpuInfo[2] & 0x18000000) == 0x18000000)
{
if((_xgetbv(0) & 0x6ui64) == 0x6ui64) /*AVX requires OS support!*/
{
features.features |= FLAG_AVX;
}
}
features.stepping = CPUInfo[0] & 0xf;
features.model = ((CPUInfo[0] >> 4) & 0xf) + (((CPUInfo[0] >> 16) & 0xf) << 4);
features.family = ((CPUInfo[0] >> 8) & 0xf) + ((CPUInfo[0] >> 20) & 0xff);
//Compute the CPU stepping, model and family
features.stepping = cpuInfo[0] & 0xf;
features.model = ((cpuInfo[0] >> 4) & 0xf) + (((cpuInfo[0] >> 16) & 0xf) << 4);
features.family = ((cpuInfo[0] >> 8) & 0xf) + ((cpuInfo[0] >> 20) & 0xff);
}
__cpuid(CPUInfo, 0x80000000);
int nExIds = qMax<int>(qMin<int>(CPUInfo[0], 0x80000004), 0x80000000);
for(int i = 0x80000002; i <= nExIds; ++i)
//Read the CPU "brand" string
MY_CPUID(&cpuInfo[0], 0x80000000);
const uint32_t nExIds = qBound(0x80000000, cpuInfo[0], 0x80000004);
for(uint32_t i = 0x80000002; i <= nExIds; ++i)
{
__cpuid(CPUInfo, i);
switch(i)
{
case 0x80000002:
memcpy(CPUBrandString, CPUInfo, sizeof(CPUInfo));
break;
case 0x80000003:
memcpy(CPUBrandString + 16, CPUInfo, sizeof(CPUInfo));
break;
case 0x80000004:
memcpy(CPUBrandString + 32, CPUInfo, sizeof(CPUInfo));
break;
}
MY_CPUID(&cpuInfo[0], i);
memcpy(&features.brand[(i - 0x80000002) * sizeof(cpuInfo)], &cpuInfo[0], sizeof(cpuInfo));
}
features.brand[sizeof(features.brand) - 1] = '\0';
strncpy_s(features.brand, 0x40, CPUBrandString, _TRUNCATE);
if(strlen(features.brand) < 1) strncpy_s(features.brand, 0x40, "Unknown", _TRUNCATE);
if(strlen(features.vendor) < 1) strncpy_s(features.vendor, 0x40, "Unknown", _TRUNCATE);
//Detect 64-Bit processors
#if (!(defined(_M_X64) || defined(_M_IA64)))
const IsWow64ProcessFun isWow64ProcessPtr = MUtils::Win32Utils::resolve<IsWow64ProcessFun>(QLatin1String("kernel32"), QLatin1String("IsWow64Process"));
if(isWow64ProcessPtr)
@ -105,24 +102,26 @@ MUtils::CPUFetaures::cpu_info_t MUtils::CPUFetaures::detect(void)
BOOL x64flag = FALSE;
if(isWow64ProcessPtr(GetCurrentProcess(), &x64flag))
{
if(x64flag) features.x64 = true;
if (x64flag)
{
features.x64 = true;
features.features |= FLAGS_X64; /*x86_64 implies SSE2*/
}
}
}
#else
features.x64 = true;
features.features |= FLAGS_X64;
#endif
if (features.x64)
{
features.features |= (FLAG_MMX | FLAG_SSE | FLAG_SSE2); /*x86_64 implies SSE2*/
}
//Make sure that (at least) the MMX flag has been set!
if (!(features.features & FLAG_MMX))
{
qWarning("Warning: CPU does not seem to support MMX. Take care!\n");
features.features = 0;
}
//Count the number of available(!) CPU cores
DWORD_PTR procAffinity, sysAffinity;
if(GetProcessAffinityMask(GetCurrentProcess(), &procAffinity, &sysAffinity))
{
@ -137,11 +136,11 @@ MUtils::CPUFetaures::cpu_info_t MUtils::CPUFetaures::detect(void)
features.count = qBound(1UL, systemInfo.dwNumberOfProcessors, 64UL);
}
//Apply manual CPU overwrites
bool userFlag = false;
if(args.contains("force-cpu-no-64bit")) { userFlag = true; features.x64 = false; }
if(args.contains("force-cpu-no-sse" )) { userFlag = true; features.features &= (~(FLAG_SSE | FLAG_SSE2 | FLAG_SSE3 | FLAG_SSSE3 | FLAG_SSE4 | FLAG_SSE42)); }
if(args.contains("force-cpu-no-intel")) { userFlag = true; features.intel = false; }
if (args.contains(QLatin1String("cpu-no-simd"))) { userFlag = true; features.features = 0U; }
if (args.contains(QLatin1String("cpu-no-vendor"))) { userFlag = true; features.vendor = 0U; }
if (args.contains(QLatin1String("cpu-no-x64"))) { userFlag = true; features.x64 = 0U; }
if(userFlag)
{
qWarning("CPU flags overwritten by user-defined parameters. Take care!\n");