cpu_detect.cpp
1 // Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project 2 // Licensed under GPLv2 or any later version 3 // Refer to the license.txt file included. 4 5 #include "common/arch.h" 6 #if CITRA_ARCH(x86_64) 7 8 #include <cstring> 9 #include "common/common_types.h" 10 #include "common/x64/cpu_detect.h" 11 12 #ifdef _MSC_VER 13 #include <intrin.h> 14 #else 15 16 #if defined(__DragonFly__) || defined(__FreeBSD__) 17 // clang-format off 18 #include <sys/types.h> 19 #include <machine/cpufunc.h> 20 // clang-format on 21 #endif 22 23 static inline void __cpuidex(int info[4], int function_id, int subfunction_id) { 24 #if defined(__DragonFly__) || defined(__FreeBSD__) 25 // Despite the name, this is just do_cpuid() with ECX as second input. 26 cpuid_count((u_int)function_id, (u_int)subfunction_id, (u_int*)info); 27 #else 28 info[0] = function_id; // eax 29 info[2] = subfunction_id; // ecx 30 __asm__("cpuid" 31 : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) 32 : "a"(function_id), "c"(subfunction_id)); 33 #endif 34 } 35 36 static inline void __cpuid(int info[4], int function_id) { 37 return __cpuidex(info, function_id, 0); 38 } 39 40 #define _XCR_XFEATURE_ENABLED_MASK 0 41 static inline u64 _xgetbv(u32 index) { 42 u32 eax, edx; 43 __asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index)); 44 return ((u64)edx << 32) | eax; 45 } 46 47 #endif // _MSC_VER 48 49 namespace Common { 50 51 // Detects the various CPU features 52 static CPUCaps Detect() { 53 CPUCaps caps = {}; 54 55 // Assumes the CPU supports the CPUID instruction. Those that don't would likely not support 56 // Citra at all anyway 57 58 int cpu_id[4]; 59 std::memset(caps.brand_string, 0, sizeof(caps.brand_string)); 60 61 // Detect CPU's CPUID capabilities and grab CPU string 62 __cpuid(cpu_id, 0x00000000); 63 u32 max_std_fn = cpu_id[0]; // EAX 64 65 std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(int)); 66 std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(int)); 67 std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(int)); 68 69 __cpuid(cpu_id, 0x80000000); 70 71 u32 max_ex_fn = cpu_id[0]; 72 73 // Set reasonable default brand string even if brand string not available 74 strcpy(caps.cpu_string, caps.brand_string); 75 76 // Detect family and other miscellaneous features 77 if (max_std_fn >= 1) { 78 __cpuid(cpu_id, 0x00000001); 79 80 if ((cpu_id[3] >> 25) & 1) 81 caps.sse = true; 82 if ((cpu_id[3] >> 26) & 1) 83 caps.sse2 = true; 84 if ((cpu_id[2]) & 1) 85 caps.sse3 = true; 86 if ((cpu_id[2] >> 9) & 1) 87 caps.ssse3 = true; 88 if ((cpu_id[2] >> 19) & 1) 89 caps.sse4_1 = true; 90 if ((cpu_id[2] >> 20) & 1) 91 caps.sse4_2 = true; 92 if ((cpu_id[2] >> 25) & 1) 93 caps.aes = true; 94 95 // AVX support requires 3 separate checks: 96 // - Is the AVX bit set in CPUID? 97 // - Is the XSAVE bit set in CPUID? 98 // - XGETBV result has the XCR bit set. 99 if (((cpu_id[2] >> 28) & 1) && ((cpu_id[2] >> 27) & 1)) { 100 if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) { 101 caps.avx = true; 102 if ((cpu_id[2] >> 12) & 1) 103 caps.fma = true; 104 } 105 } 106 107 if (max_std_fn >= 7) { 108 __cpuidex(cpu_id, 0x00000007, 0x00000000); 109 // Can't enable AVX2 unless the XSAVE/XGETBV checks above passed 110 if ((cpu_id[1] >> 5) & 1) 111 caps.avx2 = caps.avx; 112 if ((cpu_id[1] >> 3) & 1) 113 caps.bmi1 = true; 114 if ((cpu_id[1] >> 8) & 1) 115 caps.bmi2 = true; 116 // Checks for AVX512F, AVX512CD, AVX512VL, AVX512DQ, AVX512BW (Intel Skylake-X/SP) 117 if ((cpu_id[1] >> 16) & 1 && (cpu_id[1] >> 28) & 1 && (cpu_id[1] >> 31) & 1 && 118 (cpu_id[1] >> 17) & 1 && (cpu_id[1] >> 30) & 1) { 119 caps.avx512 = caps.avx2; 120 } 121 } 122 } 123 124 if (max_ex_fn >= 0x80000004) { 125 // Extract CPU model string 126 __cpuid(cpu_id, 0x80000002); 127 std::memcpy(caps.cpu_string, cpu_id, sizeof(cpu_id)); 128 __cpuid(cpu_id, 0x80000003); 129 std::memcpy(caps.cpu_string + 16, cpu_id, sizeof(cpu_id)); 130 __cpuid(cpu_id, 0x80000004); 131 std::memcpy(caps.cpu_string + 32, cpu_id, sizeof(cpu_id)); 132 } 133 134 if (max_ex_fn >= 0x80000001) { 135 // Check for more features 136 __cpuid(cpu_id, 0x80000001); 137 if ((cpu_id[2] >> 16) & 1) 138 caps.fma4 = true; 139 } 140 141 return caps; 142 } 143 144 const CPUCaps& GetCPUCaps() { 145 static CPUCaps caps = Detect(); 146 return caps; 147 } 148 149 } // namespace Common 150 151 #endif // CITRA_ARCH(x86_64)