mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	Common: Ported over Dolphin's code for x86 CPU capability detection.
This commit is contained in:
		
							parent
							
								
									3f69c2039d
								
							
						
					
					
						commit
						4d51792285
					
				
					 5 changed files with 286 additions and 19 deletions
				
			
		|  | @ -11,8 +11,8 @@ if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git/hooks/pre-commit) | ||||||
| endif() | endif() | ||||||
| 
 | 
 | ||||||
| if (NOT MSVC) | if (NOT MSVC) | ||||||
|     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-attributes -pthread") |     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-attributes -pthread -msse4.2") | ||||||
|     set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread") |     set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread -msse4.2") | ||||||
| else() | else() | ||||||
|     # Silence "deprecation" warnings |     # Silence "deprecation" warnings | ||||||
|     add_definitions(/D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_DEPRECATE) |     add_definitions(/D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_DEPRECATE) | ||||||
|  | @ -51,6 +51,17 @@ else() | ||||||
|     set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE) |     set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE) | ||||||
| endif() | endif() | ||||||
| 
 | 
 | ||||||
|  | # Platform-agnostic definition to check if we are on x86_64 | ||||||
|  | if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "^x86" OR | ||||||
|  |    ${CMAKE_SYSTEM_PROCESSOR} MATCHES "i.86" OR | ||||||
|  |    ${CMAKE_SYSTEM_PROCESSOR} MATCHES "[aA][mM][dD]64" OR | ||||||
|  |    APPLE) | ||||||
|  |     if(CMAKE_SIZEOF_VOID_P EQUAL 8) | ||||||
|  |         set(ARCHITECTURE_X64 1) | ||||||
|  |         add_definitions(-DARCHITECTURE_X64=1) | ||||||
|  |     endif() | ||||||
|  | endif() | ||||||
|  | 
 | ||||||
| add_definitions(-DSINGLETHREADED) | add_definitions(-DSINGLETHREADED) | ||||||
| # CMake seems to only define _DEBUG on Windows | # CMake seems to only define _DEBUG on Windows | ||||||
| set_property(DIRECTORY APPEND PROPERTY | set_property(DIRECTORY APPEND PROPERTY | ||||||
|  |  | ||||||
|  | @ -56,6 +56,14 @@ set(HEADERS | ||||||
|             vector_math.h |             vector_math.h | ||||||
|             ) |             ) | ||||||
| 
 | 
 | ||||||
|  | if(_M_X86) | ||||||
|  |     set(SRCS ${SRCS} | ||||||
|  |              cpu_detect_x86.cpp) | ||||||
|  | else() | ||||||
|  |     set(SRCS ${SRCS} | ||||||
|  |              cpu_detect_generic.cpp) | ||||||
|  | endif() | ||||||
|  | 
 | ||||||
| create_directory_groups(${SRCS} ${HEADERS}) | create_directory_groups(${SRCS} ${HEADERS}) | ||||||
| 
 | 
 | ||||||
| add_library(common STATIC ${SRCS} ${HEADERS}) | add_library(common STATIC ${SRCS} ${HEADERS}) | ||||||
|  |  | ||||||
|  | @ -3,11 +3,13 @@ | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| // Detect the cpu, so we'll know which optimizations to use
 | // Detect the CPU, so we'll know which optimizations to use
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <string> | #include <string> | ||||||
| 
 | 
 | ||||||
|  | namespace Common { | ||||||
|  | 
 | ||||||
| enum CPUVendor | enum CPUVendor | ||||||
| { | { | ||||||
|     VENDOR_INTEL = 0, |     VENDOR_INTEL = 0, | ||||||
|  | @ -40,29 +42,28 @@ struct CPUInfo | ||||||
|     bool bLZCNT; |     bool bLZCNT; | ||||||
|     bool bSSE4A; |     bool bSSE4A; | ||||||
|     bool bAVX; |     bool bAVX; | ||||||
|  |     bool bAVX2; | ||||||
|  |     bool bBMI1; | ||||||
|  |     bool bBMI2; | ||||||
|  |     bool bFMA; | ||||||
|  |     bool bFMA4; | ||||||
|     bool bAES; |     bool bAES; | ||||||
|  |     // FXSAVE/FXRSTOR
 | ||||||
|  |     bool bFXSR; | ||||||
|  |     bool bMOVBE; | ||||||
|  |     // This flag indicates that the hardware supports some mode
 | ||||||
|  |     // in which denormal inputs _and_ outputs are automatically set to (signed) zero.
 | ||||||
|  |     bool bFlushToZero; | ||||||
|     bool bLAHFSAHF64; |     bool bLAHFSAHF64; | ||||||
|     bool bLongMode; |     bool bLongMode; | ||||||
| 
 |     bool bAtom; | ||||||
|     // ARM specific CPUInfo
 |  | ||||||
|     bool bSwp; |  | ||||||
|     bool bHalf; |  | ||||||
|     bool bThumb; |  | ||||||
|     bool bFastMult; |  | ||||||
|     bool bVFP; |  | ||||||
|     bool bEDSP; |  | ||||||
|     bool bThumbEE; |  | ||||||
|     bool bNEON; |  | ||||||
|     bool bVFPv3; |  | ||||||
|     bool bTLS; |  | ||||||
|     bool bVFPv4; |  | ||||||
|     bool bIDIVa; |  | ||||||
|     bool bIDIVt; |  | ||||||
|     bool bArmV7;  // enable MOVT, MOVW etc
 |  | ||||||
| 
 | 
 | ||||||
|     // ARMv8 specific
 |     // ARMv8 specific
 | ||||||
|     bool bFP; |     bool bFP; | ||||||
|     bool bASIMD; |     bool bASIMD; | ||||||
|  |     bool bCRC32; | ||||||
|  |     bool bSHA1; | ||||||
|  |     bool bSHA2; | ||||||
| 
 | 
 | ||||||
|     // Call Detect()
 |     // Call Detect()
 | ||||||
|     explicit CPUInfo(); |     explicit CPUInfo(); | ||||||
|  | @ -76,3 +77,5 @@ private: | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| extern CPUInfo cpu_info; | extern CPUInfo cpu_info; | ||||||
|  | 
 | ||||||
|  | } // namespace Common
 | ||||||
|  |  | ||||||
							
								
								
									
										17
									
								
								src/common/cpu_detect_generic.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/common/cpu_detect_generic.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | ||||||
|  | // Copyright 2014 Dolphin Emulator Project
 | ||||||
|  | // Licensed under GPLv2+
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include "cpu_detect.h" | ||||||
|  | 
 | ||||||
|  | namespace Common { | ||||||
|  | 
 | ||||||
|  | CPUInfo cpu_info; | ||||||
|  | 
 | ||||||
|  | CPUInfo::CPUInfo() { } | ||||||
|  | 
 | ||||||
|  | std::string CPUInfo::Summarize() { | ||||||
|  |     return "Generic"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace Common
 | ||||||
							
								
								
									
										228
									
								
								src/common/cpu_detect_x86.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										228
									
								
								src/common/cpu_detect_x86.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,228 @@ | ||||||
|  | // Copyright 2008 Dolphin Emulator Project
 | ||||||
|  | // Licensed under GPLv2+
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include <cstring> | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | #include "common_types.h" | ||||||
|  | #include "cpu_detect.h" | ||||||
|  | 
 | ||||||
|  | #ifndef _WIN32 | ||||||
|  | 
 | ||||||
|  | #ifdef __FreeBSD__ | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <machine/cpufunc.h> | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | static inline void __cpuidex(int info[4], int function_id, int subfunction_id) | ||||||
|  | { | ||||||
|  | #ifdef __FreeBSD__ | ||||||
|  |     // Despite the name, this is just do_cpuid() with ECX as second input.
 | ||||||
|  |     cpuid_count((u_int)function_id, (u_int)subfunction_id, (u_int*)info); | ||||||
|  | #else | ||||||
|  |     info[0] = function_id;    // eax
 | ||||||
|  |     info[2] = subfunction_id; // ecx
 | ||||||
|  |     __asm__( | ||||||
|  |         "cpuid" | ||||||
|  |         : "=a" (info[0]), | ||||||
|  |         "=b" (info[1]), | ||||||
|  |         "=c" (info[2]), | ||||||
|  |         "=d" (info[3]) | ||||||
|  |         : "a" (function_id), | ||||||
|  |         "c" (subfunction_id) | ||||||
|  |         ); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void __cpuid(int info[4], int function_id) | ||||||
|  | { | ||||||
|  |     return __cpuidex(info, function_id, 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #define _XCR_XFEATURE_ENABLED_MASK 0 | ||||||
|  | static u64 _xgetbv(u32 index) | ||||||
|  | { | ||||||
|  |     u32 eax, edx; | ||||||
|  |     __asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index)); | ||||||
|  |     return ((u64)edx << 32) | eax; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif // ifndef _WIN32
 | ||||||
|  | 
 | ||||||
|  | namespace Common { | ||||||
|  | 
 | ||||||
|  | CPUInfo cpu_info; | ||||||
|  | 
 | ||||||
|  | CPUInfo::CPUInfo() { | ||||||
|  |     Detect(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Detects the various CPU features
 | ||||||
|  | void CPUInfo::Detect() { | ||||||
|  |     memset(this, 0, sizeof(*this)); | ||||||
|  | #ifdef _M_X86_64 | ||||||
|  |     Mode64bit = true; | ||||||
|  |     OS64bit = true; | ||||||
|  | #endif | ||||||
|  |     num_cores = 1; | ||||||
|  | 
 | ||||||
|  |     // Set obvious defaults, for extra safety
 | ||||||
|  |     if (Mode64bit) { | ||||||
|  |         bSSE = true; | ||||||
|  |         bSSE2 = true; | ||||||
|  |         bLongMode = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Assume CPU supports the CPUID instruction. Those that don't can barely
 | ||||||
|  |     // boot modern OS:es anyway.
 | ||||||
|  |     int cpu_id[4]; | ||||||
|  |     memset(brand_string, 0, sizeof(brand_string)); | ||||||
|  | 
 | ||||||
|  |     // Detect CPU's CPUID capabilities, and grab CPU string
 | ||||||
|  |     __cpuid(cpu_id, 0x00000000); | ||||||
|  |     u32 max_std_fn = cpu_id[0];  // EAX
 | ||||||
|  |     *((int *)brand_string) = cpu_id[1]; | ||||||
|  |     *((int *)(brand_string + 4)) = cpu_id[3]; | ||||||
|  |     *((int *)(brand_string + 8)) = cpu_id[2]; | ||||||
|  |     __cpuid(cpu_id, 0x80000000); | ||||||
|  |     u32 max_ex_fn = cpu_id[0]; | ||||||
|  |     if (!strcmp(brand_string, "GenuineIntel")) | ||||||
|  |         vendor = VENDOR_INTEL; | ||||||
|  |     else if (!strcmp(brand_string, "AuthenticAMD")) | ||||||
|  |         vendor = VENDOR_AMD; | ||||||
|  |     else | ||||||
|  |         vendor = VENDOR_OTHER; | ||||||
|  | 
 | ||||||
|  |     // Set reasonable default brand string even if brand string not available.
 | ||||||
|  |     strcpy(cpu_string, brand_string); | ||||||
|  | 
 | ||||||
|  |     // Detect family and other misc stuff.
 | ||||||
|  |     bool ht = false; | ||||||
|  |     HTT = ht; | ||||||
|  |     logical_cpu_count = 1; | ||||||
|  |     if (max_std_fn >= 1) { | ||||||
|  |         __cpuid(cpu_id, 0x00000001); | ||||||
|  |         int family = ((cpu_id[0] >> 8) & 0xf) + ((cpu_id[0] >> 20) & 0xff); | ||||||
|  |         int model = ((cpu_id[0] >> 4) & 0xf) + ((cpu_id[0] >> 12) & 0xf0); | ||||||
|  |         // Detect people unfortunate enough to be running Dolphin on an Atom
 | ||||||
|  |         if (family == 6 && (model == 0x1C || model == 0x26 || model == 0x27 || model == 0x35 || model == 0x36 || | ||||||
|  |             model == 0x37 || model == 0x4A || model == 0x4D || model == 0x5A || model == 0x5D)) | ||||||
|  |             bAtom = true; | ||||||
|  |         logical_cpu_count = (cpu_id[1] >> 16) & 0xFF; | ||||||
|  |         ht = (cpu_id[3] >> 28) & 1; | ||||||
|  | 
 | ||||||
|  |         if ((cpu_id[3] >> 25) & 1) bSSE = true; | ||||||
|  |         if ((cpu_id[3] >> 26) & 1) bSSE2 = true; | ||||||
|  |         if ((cpu_id[2]) & 1) bSSE3 = true; | ||||||
|  |         if ((cpu_id[2] >> 9) & 1) bSSSE3 = true; | ||||||
|  |         if ((cpu_id[2] >> 19) & 1) bSSE4_1 = true; | ||||||
|  |         if ((cpu_id[2] >> 20) & 1) bSSE4_2 = true; | ||||||
|  |         if ((cpu_id[2] >> 22) & 1) bMOVBE = true; | ||||||
|  |         if ((cpu_id[2] >> 25) & 1) bAES = true; | ||||||
|  | 
 | ||||||
|  |         if ((cpu_id[3] >> 24) & 1) | ||||||
|  |         { | ||||||
|  |             // We can use FXSAVE.
 | ||||||
|  |             bFXSR = true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // AVX support requires 3 separate checks:
 | ||||||
|  |         //  - Is the AVX bit set in CPUID?
 | ||||||
|  |         //  - Is the XSAVE bit set in CPUID?
 | ||||||
|  |         //  - XGETBV result has the XCR bit set.
 | ||||||
|  |         if (((cpu_id[2] >> 28) & 1) && ((cpu_id[2] >> 27) & 1)) { | ||||||
|  |             if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) { | ||||||
|  |                 bAVX = true; | ||||||
|  |                 if ((cpu_id[2] >> 12) & 1) | ||||||
|  |                     bFMA = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (max_std_fn >= 7) { | ||||||
|  |             __cpuidex(cpu_id, 0x00000007, 0x00000000); | ||||||
|  |             // careful; we can't enable AVX2 unless the XSAVE/XGETBV checks above passed
 | ||||||
|  |             if ((cpu_id[1] >> 5) & 1) | ||||||
|  |                 bAVX2 = bAVX; | ||||||
|  |             if ((cpu_id[1] >> 3) & 1) | ||||||
|  |                 bBMI1 = true; | ||||||
|  |             if ((cpu_id[1] >> 8) & 1) | ||||||
|  |                 bBMI2 = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bFlushToZero = bSSE; | ||||||
|  | 
 | ||||||
|  |     if (max_ex_fn >= 0x80000004) { | ||||||
|  |         // Extract CPU model string
 | ||||||
|  |         __cpuid(cpu_id, 0x80000002); | ||||||
|  |         memcpy(cpu_string, cpu_id, sizeof(cpu_id)); | ||||||
|  |         __cpuid(cpu_id, 0x80000003); | ||||||
|  |         memcpy(cpu_string + 16, cpu_id, sizeof(cpu_id)); | ||||||
|  |         __cpuid(cpu_id, 0x80000004); | ||||||
|  |         memcpy(cpu_string + 32, cpu_id, sizeof(cpu_id)); | ||||||
|  |     } | ||||||
|  |     if (max_ex_fn >= 0x80000001) { | ||||||
|  |         // Check for more features.
 | ||||||
|  |         __cpuid(cpu_id, 0x80000001); | ||||||
|  |         if (cpu_id[2] & 1) bLAHFSAHF64 = true; | ||||||
|  |         if ((cpu_id[2] >> 5) & 1) bLZCNT = true; | ||||||
|  |         if ((cpu_id[2] >> 16) & 1) bFMA4 = true; | ||||||
|  |         if ((cpu_id[3] >> 29) & 1) bLongMode = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     num_cores = (logical_cpu_count == 0) ? 1 : logical_cpu_count; | ||||||
|  | 
 | ||||||
|  |     if (max_ex_fn >= 0x80000008) { | ||||||
|  |         // Get number of cores. This is a bit complicated. Following AMD manual here.
 | ||||||
|  |         __cpuid(cpu_id, 0x80000008); | ||||||
|  |         int apic_id_core_id_size = (cpu_id[2] >> 12) & 0xF; | ||||||
|  |         if (apic_id_core_id_size == 0) { | ||||||
|  |             if (ht) { | ||||||
|  |                 // New mechanism for modern Intel CPUs.
 | ||||||
|  |                 if (vendor == VENDOR_INTEL) { | ||||||
|  |                     __cpuidex(cpu_id, 0x00000004, 0x00000000); | ||||||
|  |                     int cores_x_package = ((cpu_id[0] >> 26) & 0x3F) + 1; | ||||||
|  |                     HTT = (cores_x_package < logical_cpu_count); | ||||||
|  |                     cores_x_package = ((logical_cpu_count % cores_x_package) == 0) ? cores_x_package : 1; | ||||||
|  |                     num_cores = (cores_x_package > 1) ? cores_x_package : num_cores; | ||||||
|  |                     logical_cpu_count /= cores_x_package; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             // Use AMD's new method.
 | ||||||
|  |             num_cores = (cpu_id[2] & 0xFF) + 1; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Turn the CPU info into a string we can show
 | ||||||
|  | std::string CPUInfo::Summarize() { | ||||||
|  |     std::string sum(cpu_string); | ||||||
|  |     sum += " ("; | ||||||
|  |     sum += brand_string; | ||||||
|  |     sum += ")"; | ||||||
|  | 
 | ||||||
|  |     if (bSSE) sum += ", SSE"; | ||||||
|  |     if (bSSE2) { | ||||||
|  |         sum += ", SSE2"; | ||||||
|  |         if (!bFlushToZero) | ||||||
|  |             sum += " (but not DAZ!)"; | ||||||
|  |     } | ||||||
|  |     if (bSSE3) sum += ", SSE3"; | ||||||
|  |     if (bSSSE3) sum += ", SSSE3"; | ||||||
|  |     if (bSSE4_1) sum += ", SSE4.1"; | ||||||
|  |     if (bSSE4_2) sum += ", SSE4.2"; | ||||||
|  |     if (HTT) sum += ", HTT"; | ||||||
|  |     if (bAVX) sum += ", AVX"; | ||||||
|  |     if (bAVX2) sum += ", AVX2"; | ||||||
|  |     if (bBMI1) sum += ", BMI1"; | ||||||
|  |     if (bBMI2) sum += ", BMI2"; | ||||||
|  |     if (bFMA) sum += ", FMA"; | ||||||
|  |     if (bAES) sum += ", AES"; | ||||||
|  |     if (bMOVBE) sum += ", MOVBE"; | ||||||
|  |     if (bLongMode) sum += ", 64-bit support"; | ||||||
|  |     return sum; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace Common
 | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue