diff options
Diffstat (limited to 'src/common/x64/cpu_detect.cpp')
| -rw-r--r-- | src/common/x64/cpu_detect.cpp | 123 |
1 files changed, 67 insertions, 56 deletions
diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp index fbeacc7e2..f5296b32a 100644 --- a/src/common/x64/cpu_detect.cpp +++ b/src/common/x64/cpu_detect.cpp | |||
| @@ -1,8 +1,11 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project / 2022 Yuzu Emulator |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Project Licensed under GPLv2 or any later version Refer to the license.txt file included. |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 4 | #include <array> | ||
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | #include <iterator> | ||
| 7 | #include <string_view> | ||
| 8 | #include "common/bit_util.h" | ||
| 6 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 7 | #include "common/x64/cpu_detect.h" | 10 | #include "common/x64/cpu_detect.h" |
| 8 | 11 | ||
| @@ -17,7 +20,7 @@ | |||
| 17 | // clang-format on | 20 | // clang-format on |
| 18 | #endif | 21 | #endif |
| 19 | 22 | ||
| 20 | static inline void __cpuidex(int info[4], int function_id, int subfunction_id) { | 23 | static inline void __cpuidex(int info[4], u32 function_id, u32 subfunction_id) { |
| 21 | #if defined(__DragonFly__) || defined(__FreeBSD__) | 24 | #if defined(__DragonFly__) || defined(__FreeBSD__) |
| 22 | // Despite the name, this is just do_cpuid() with ECX as second input. | 25 | // Despite the name, this is just do_cpuid() with ECX as second input. |
| 23 | cpuid_count((u_int)function_id, (u_int)subfunction_id, (u_int*)info); | 26 | cpuid_count((u_int)function_id, (u_int)subfunction_id, (u_int*)info); |
| @@ -30,7 +33,7 @@ static inline void __cpuidex(int info[4], int function_id, int subfunction_id) { | |||
| 30 | #endif | 33 | #endif |
| 31 | } | 34 | } |
| 32 | 35 | ||
| 33 | static inline void __cpuid(int info[4], int function_id) { | 36 | static inline void __cpuid(int info[4], u32 function_id) { |
| 34 | return __cpuidex(info, function_id, 0); | 37 | return __cpuidex(info, function_id, 0); |
| 35 | } | 38 | } |
| 36 | 39 | ||
| @@ -45,6 +48,17 @@ static inline u64 _xgetbv(u32 index) { | |||
| 45 | 48 | ||
| 46 | namespace Common { | 49 | namespace Common { |
| 47 | 50 | ||
| 51 | CPUCaps::Manufacturer CPUCaps::ParseManufacturer(std::string_view brand_string) { | ||
| 52 | if (brand_string == "GenuineIntel") { | ||
| 53 | return Manufacturer::Intel; | ||
| 54 | } else if (brand_string == "AuthenticAMD") { | ||
| 55 | return Manufacturer::AMD; | ||
| 56 | } else if (brand_string == "HygonGenuine") { | ||
| 57 | return Manufacturer::Hygon; | ||
| 58 | } | ||
| 59 | return Manufacturer::Unknown; | ||
| 60 | } | ||
| 61 | |||
| 48 | // Detects the various CPU features | 62 | // Detects the various CPU features |
| 49 | static CPUCaps Detect() { | 63 | static CPUCaps Detect() { |
| 50 | CPUCaps caps = {}; | 64 | CPUCaps caps = {}; |
| @@ -53,75 +67,74 @@ static CPUCaps Detect() { | |||
| 53 | // yuzu at all anyway | 67 | // yuzu at all anyway |
| 54 | 68 | ||
| 55 | int cpu_id[4]; | 69 | int cpu_id[4]; |
| 56 | memset(caps.brand_string, 0, sizeof(caps.brand_string)); | ||
| 57 | 70 | ||
| 58 | // Detect CPU's CPUID capabilities and grab CPU string | 71 | // Detect CPU's CPUID capabilities and grab manufacturer string |
| 59 | __cpuid(cpu_id, 0x00000000); | 72 | __cpuid(cpu_id, 0x00000000); |
| 60 | u32 max_std_fn = cpu_id[0]; // EAX | 73 | const u32 max_std_fn = cpu_id[0]; // EAX |
| 61 | |||
| 62 | std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(int)); | ||
| 63 | std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(int)); | ||
| 64 | std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(int)); | ||
| 65 | if (cpu_id[1] == 0x756e6547 && cpu_id[2] == 0x6c65746e && cpu_id[3] == 0x49656e69) | ||
| 66 | caps.manufacturer = Manufacturer::Intel; | ||
| 67 | else if (cpu_id[1] == 0x68747541 && cpu_id[2] == 0x444d4163 && cpu_id[3] == 0x69746e65) | ||
| 68 | caps.manufacturer = Manufacturer::AMD; | ||
| 69 | else if (cpu_id[1] == 0x6f677948 && cpu_id[2] == 0x656e6975 && cpu_id[3] == 0x6e65476e) | ||
| 70 | caps.manufacturer = Manufacturer::Hygon; | ||
| 71 | else | ||
| 72 | caps.manufacturer = Manufacturer::Unknown; | ||
| 73 | 74 | ||
| 74 | __cpuid(cpu_id, 0x80000000); | 75 | std::memset(caps.brand_string, 0, std::size(caps.brand_string)); |
| 76 | std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(u32)); | ||
| 77 | std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(u32)); | ||
| 78 | std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(u32)); | ||
| 79 | |||
| 80 | caps.manufacturer = CPUCaps::ParseManufacturer(caps.brand_string); | ||
| 75 | 81 | ||
| 76 | u32 max_ex_fn = cpu_id[0]; | 82 | // Set reasonable default cpu string even if brand string not available |
| 83 | std::strncpy(caps.cpu_string, caps.brand_string, std::size(caps.brand_string)); | ||
| 77 | 84 | ||
| 78 | // Set reasonable default brand string even if brand string not available | 85 | __cpuid(cpu_id, 0x80000000); |
| 79 | strcpy(caps.cpu_string, caps.brand_string); | 86 | |
| 87 | const u32 max_ex_fn = cpu_id[0]; | ||
| 80 | 88 | ||
| 81 | // Detect family and other miscellaneous features | 89 | // Detect family and other miscellaneous features |
| 82 | if (max_std_fn >= 1) { | 90 | if (max_std_fn >= 1) { |
| 83 | __cpuid(cpu_id, 0x00000001); | 91 | __cpuid(cpu_id, 0x00000001); |
| 84 | if ((cpu_id[3] >> 25) & 1) | 92 | caps.sse = Common::Bit<25>(cpu_id[3]); |
| 85 | caps.sse = true; | 93 | caps.sse2 = Common::Bit<26>(cpu_id[3]); |
| 86 | if ((cpu_id[3] >> 26) & 1) | 94 | caps.sse3 = Common::Bit<0>(cpu_id[2]); |
| 87 | caps.sse2 = true; | 95 | caps.pclmulqdq = Common::Bit<1>(cpu_id[2]); |
| 88 | if ((cpu_id[2]) & 1) | 96 | caps.ssse3 = Common::Bit<9>(cpu_id[2]); |
| 89 | caps.sse3 = true; | 97 | caps.sse4_1 = Common::Bit<19>(cpu_id[2]); |
| 90 | if ((cpu_id[2] >> 9) & 1) | 98 | caps.sse4_2 = Common::Bit<20>(cpu_id[2]); |
| 91 | caps.ssse3 = true; | 99 | caps.movbe = Common::Bit<22>(cpu_id[2]); |
| 92 | if ((cpu_id[2] >> 19) & 1) | 100 | caps.popcnt = Common::Bit<23>(cpu_id[2]); |
| 93 | caps.sse4_1 = true; | 101 | caps.aes = Common::Bit<25>(cpu_id[2]); |
| 94 | if ((cpu_id[2] >> 20) & 1) | 102 | caps.f16c = Common::Bit<29>(cpu_id[2]); |
| 95 | caps.sse4_2 = true; | ||
| 96 | if ((cpu_id[2] >> 25) & 1) | ||
| 97 | caps.aes = true; | ||
| 98 | 103 | ||
| 99 | // AVX support requires 3 separate checks: | 104 | // AVX support requires 3 separate checks: |
| 100 | // - Is the AVX bit set in CPUID? | 105 | // - Is the AVX bit set in CPUID? |
| 101 | // - Is the XSAVE bit set in CPUID? | 106 | // - Is the XSAVE bit set in CPUID? |
| 102 | // - XGETBV result has the XCR bit set. | 107 | // - XGETBV result has the XCR bit set. |
| 103 | if (((cpu_id[2] >> 28) & 1) && ((cpu_id[2] >> 27) & 1)) { | 108 | if (Common::Bit<28>(cpu_id[2]) && Common::Bit<27>(cpu_id[2])) { |
| 104 | if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) { | 109 | if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) { |
| 105 | caps.avx = true; | 110 | caps.avx = true; |
| 106 | if ((cpu_id[2] >> 12) & 1) | 111 | if (Common::Bit<12>(cpu_id[2])) |
| 107 | caps.fma = true; | 112 | caps.fma = true; |
| 108 | } | 113 | } |
| 109 | } | 114 | } |
| 110 | 115 | ||
| 111 | if (max_std_fn >= 7) { | 116 | if (max_std_fn >= 7) { |
| 112 | __cpuidex(cpu_id, 0x00000007, 0x00000000); | 117 | __cpuidex(cpu_id, 0x00000007, 0x00000000); |
| 113 | // Can't enable AVX2 unless the XSAVE/XGETBV checks above passed | 118 | // Can't enable AVX{2,512} unless the XSAVE/XGETBV checks above passed |
| 114 | if ((cpu_id[1] >> 5) & 1) | 119 | if (caps.avx) { |
| 115 | caps.avx2 = caps.avx; | 120 | caps.avx2 = Common::Bit<5>(cpu_id[1]); |
| 116 | if ((cpu_id[1] >> 3) & 1) | 121 | caps.avx512f = Common::Bit<16>(cpu_id[1]); |
| 117 | caps.bmi1 = true; | 122 | caps.avx512dq = Common::Bit<17>(cpu_id[1]); |
| 118 | if ((cpu_id[1] >> 8) & 1) | 123 | caps.avx512cd = Common::Bit<28>(cpu_id[1]); |
| 119 | caps.bmi2 = true; | 124 | caps.avx512bw = Common::Bit<30>(cpu_id[1]); |
| 120 | // Checks for AVX512F, AVX512CD, AVX512VL, AVX512DQ, AVX512BW (Intel Skylake-X/SP) | 125 | caps.avx512vl = Common::Bit<31>(cpu_id[1]); |
| 121 | if ((cpu_id[1] >> 16) & 1 && (cpu_id[1] >> 28) & 1 && (cpu_id[1] >> 31) & 1 && | 126 | caps.avx512vbmi = Common::Bit<1>(cpu_id[2]); |
| 122 | (cpu_id[1] >> 17) & 1 && (cpu_id[1] >> 30) & 1) { | 127 | caps.avx512bitalg = Common::Bit<12>(cpu_id[2]); |
| 123 | caps.avx512 = caps.avx2; | ||
| 124 | } | 128 | } |
| 129 | |||
| 130 | caps.bmi1 = Common::Bit<3>(cpu_id[1]); | ||
| 131 | caps.bmi2 = Common::Bit<8>(cpu_id[1]); | ||
| 132 | caps.sha = Common::Bit<29>(cpu_id[1]); | ||
| 133 | |||
| 134 | caps.gfni = Common::Bit<8>(cpu_id[2]); | ||
| 135 | |||
| 136 | __cpuidex(cpu_id, 0x00000007, 0x00000001); | ||
| 137 | caps.avx_vnni = caps.avx && Common::Bit<4>(cpu_id[0]); | ||
| 125 | } | 138 | } |
| 126 | } | 139 | } |
| 127 | 140 | ||
| @@ -138,15 +151,13 @@ static CPUCaps Detect() { | |||
| 138 | if (max_ex_fn >= 0x80000001) { | 151 | if (max_ex_fn >= 0x80000001) { |
| 139 | // Check for more features | 152 | // Check for more features |
| 140 | __cpuid(cpu_id, 0x80000001); | 153 | __cpuid(cpu_id, 0x80000001); |
| 141 | if ((cpu_id[2] >> 16) & 1) | 154 | caps.lzcnt = Common::Bit<5>(cpu_id[2]); |
| 142 | caps.fma4 = true; | 155 | caps.fma4 = Common::Bit<16>(cpu_id[2]); |
| 143 | } | 156 | } |
| 144 | 157 | ||
| 145 | if (max_ex_fn >= 0x80000007) { | 158 | if (max_ex_fn >= 0x80000007) { |
| 146 | __cpuid(cpu_id, 0x80000007); | 159 | __cpuid(cpu_id, 0x80000007); |
| 147 | if (cpu_id[3] & (1 << 8)) { | 160 | caps.invariant_tsc = Common::Bit<8>(cpu_id[3]); |
| 148 | caps.invariant_tsc = true; | ||
| 149 | } | ||
| 150 | } | 161 | } |
| 151 | 162 | ||
| 152 | if (max_std_fn >= 0x16) { | 163 | if (max_std_fn >= 0x16) { |