summaryrefslogtreecommitdiff
path: root/src/common/cpu_detect_x86.cpp
diff options
context:
space:
mode:
authorGravatar bunnei2015-07-21 19:49:33 -0400
committerGravatar bunnei2015-08-15 17:33:44 -0400
commit4d517922856791decfed475cdd536aedcaa25c30 (patch)
treee6ac9daf302ea079411052bb0e78428fb71f1440 /src/common/cpu_detect_x86.cpp
parentShader: Define a common interface for running vertex shader programs. (diff)
downloadyuzu-4d517922856791decfed475cdd536aedcaa25c30.tar.gz
yuzu-4d517922856791decfed475cdd536aedcaa25c30.tar.xz
yuzu-4d517922856791decfed475cdd536aedcaa25c30.zip
Common: Ported over Dolphin's code for x86 CPU capability detection.
Diffstat (limited to 'src/common/cpu_detect_x86.cpp')
-rw-r--r--src/common/cpu_detect_x86.cpp228
1 files changed, 228 insertions, 0 deletions
diff --git a/src/common/cpu_detect_x86.cpp b/src/common/cpu_detect_x86.cpp
new file mode 100644
index 000000000..2dff69b94
--- /dev/null
+++ b/src/common/cpu_detect_x86.cpp
@@ -0,0 +1,228 @@
1// Copyright 2008 Dolphin Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include <string>
7
8#include "common_types.h"
9#include "cpu_detect.h"
10
11#ifndef _WIN32
12
13#ifdef __FreeBSD__
14#include <sys/types.h>
15#include <machine/cpufunc.h>
16#endif
17
18static inline void __cpuidex(int info[4], int function_id, int subfunction_id)
19{
20#ifdef __FreeBSD__
21 // Despite the name, this is just do_cpuid() with ECX as second input.
22 cpuid_count((u_int)function_id, (u_int)subfunction_id, (u_int*)info);
23#else
24 info[0] = function_id; // eax
25 info[2] = subfunction_id; // ecx
26 __asm__(
27 "cpuid"
28 : "=a" (info[0]),
29 "=b" (info[1]),
30 "=c" (info[2]),
31 "=d" (info[3])
32 : "a" (function_id),
33 "c" (subfunction_id)
34 );
35#endif
36}
37
38static inline void __cpuid(int info[4], int function_id)
39{
40 return __cpuidex(info, function_id, 0);
41}
42
43#define _XCR_XFEATURE_ENABLED_MASK 0
44static u64 _xgetbv(u32 index)
45{
46 u32 eax, edx;
47 __asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index));
48 return ((u64)edx << 32) | eax;
49}
50
51#endif // ifndef _WIN32
52
53namespace Common {
54
55CPUInfo cpu_info;
56
57CPUInfo::CPUInfo() {
58 Detect();
59}
60
61// Detects the various CPU features
62void CPUInfo::Detect() {
63 memset(this, 0, sizeof(*this));
64#ifdef _M_X86_64
65 Mode64bit = true;
66 OS64bit = true;
67#endif
68 num_cores = 1;
69
70 // Set obvious defaults, for extra safety
71 if (Mode64bit) {
72 bSSE = true;
73 bSSE2 = true;
74 bLongMode = true;
75 }
76
77 // Assume CPU supports the CPUID instruction. Those that don't can barely
78 // boot modern OS:es anyway.
79 int cpu_id[4];
80 memset(brand_string, 0, sizeof(brand_string));
81
82 // Detect CPU's CPUID capabilities, and grab CPU string
83 __cpuid(cpu_id, 0x00000000);
84 u32 max_std_fn = cpu_id[0]; // EAX
85 *((int *)brand_string) = cpu_id[1];
86 *((int *)(brand_string + 4)) = cpu_id[3];
87 *((int *)(brand_string + 8)) = cpu_id[2];
88 __cpuid(cpu_id, 0x80000000);
89 u32 max_ex_fn = cpu_id[0];
90 if (!strcmp(brand_string, "GenuineIntel"))
91 vendor = VENDOR_INTEL;
92 else if (!strcmp(brand_string, "AuthenticAMD"))
93 vendor = VENDOR_AMD;
94 else
95 vendor = VENDOR_OTHER;
96
97 // Set reasonable default brand string even if brand string not available.
98 strcpy(cpu_string, brand_string);
99
100 // Detect family and other misc stuff.
101 bool ht = false;
102 HTT = ht;
103 logical_cpu_count = 1;
104 if (max_std_fn >= 1) {
105 __cpuid(cpu_id, 0x00000001);
106 int family = ((cpu_id[0] >> 8) & 0xf) + ((cpu_id[0] >> 20) & 0xff);
107 int model = ((cpu_id[0] >> 4) & 0xf) + ((cpu_id[0] >> 12) & 0xf0);
108 // Detect people unfortunate enough to be running Dolphin on an Atom
109 if (family == 6 && (model == 0x1C || model == 0x26 || model == 0x27 || model == 0x35 || model == 0x36 ||
110 model == 0x37 || model == 0x4A || model == 0x4D || model == 0x5A || model == 0x5D))
111 bAtom = true;
112 logical_cpu_count = (cpu_id[1] >> 16) & 0xFF;
113 ht = (cpu_id[3] >> 28) & 1;
114
115 if ((cpu_id[3] >> 25) & 1) bSSE = true;
116 if ((cpu_id[3] >> 26) & 1) bSSE2 = true;
117 if ((cpu_id[2]) & 1) bSSE3 = true;
118 if ((cpu_id[2] >> 9) & 1) bSSSE3 = true;
119 if ((cpu_id[2] >> 19) & 1) bSSE4_1 = true;
120 if ((cpu_id[2] >> 20) & 1) bSSE4_2 = true;
121 if ((cpu_id[2] >> 22) & 1) bMOVBE = true;
122 if ((cpu_id[2] >> 25) & 1) bAES = true;
123
124 if ((cpu_id[3] >> 24) & 1)
125 {
126 // We can use FXSAVE.
127 bFXSR = true;
128 }
129
130 // AVX support requires 3 separate checks:
131 // - Is the AVX bit set in CPUID?
132 // - Is the XSAVE bit set in CPUID?
133 // - XGETBV result has the XCR bit set.
134 if (((cpu_id[2] >> 28) & 1) && ((cpu_id[2] >> 27) & 1)) {
135 if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {
136 bAVX = true;
137 if ((cpu_id[2] >> 12) & 1)
138 bFMA = true;
139 }
140 }
141
142 if (max_std_fn >= 7) {
143 __cpuidex(cpu_id, 0x00000007, 0x00000000);
144 // careful; we can't enable AVX2 unless the XSAVE/XGETBV checks above passed
145 if ((cpu_id[1] >> 5) & 1)
146 bAVX2 = bAVX;
147 if ((cpu_id[1] >> 3) & 1)
148 bBMI1 = true;
149 if ((cpu_id[1] >> 8) & 1)
150 bBMI2 = true;
151 }
152 }
153
154 bFlushToZero = bSSE;
155
156 if (max_ex_fn >= 0x80000004) {
157 // Extract CPU model string
158 __cpuid(cpu_id, 0x80000002);
159 memcpy(cpu_string, cpu_id, sizeof(cpu_id));
160 __cpuid(cpu_id, 0x80000003);
161 memcpy(cpu_string + 16, cpu_id, sizeof(cpu_id));
162 __cpuid(cpu_id, 0x80000004);
163 memcpy(cpu_string + 32, cpu_id, sizeof(cpu_id));
164 }
165 if (max_ex_fn >= 0x80000001) {
166 // Check for more features.
167 __cpuid(cpu_id, 0x80000001);
168 if (cpu_id[2] & 1) bLAHFSAHF64 = true;
169 if ((cpu_id[2] >> 5) & 1) bLZCNT = true;
170 if ((cpu_id[2] >> 16) & 1) bFMA4 = true;
171 if ((cpu_id[3] >> 29) & 1) bLongMode = true;
172 }
173
174 num_cores = (logical_cpu_count == 0) ? 1 : logical_cpu_count;
175
176 if (max_ex_fn >= 0x80000008) {
177 // Get number of cores. This is a bit complicated. Following AMD manual here.
178 __cpuid(cpu_id, 0x80000008);
179 int apic_id_core_id_size = (cpu_id[2] >> 12) & 0xF;
180 if (apic_id_core_id_size == 0) {
181 if (ht) {
182 // New mechanism for modern Intel CPUs.
183 if (vendor == VENDOR_INTEL) {
184 __cpuidex(cpu_id, 0x00000004, 0x00000000);
185 int cores_x_package = ((cpu_id[0] >> 26) & 0x3F) + 1;
186 HTT = (cores_x_package < logical_cpu_count);
187 cores_x_package = ((logical_cpu_count % cores_x_package) == 0) ? cores_x_package : 1;
188 num_cores = (cores_x_package > 1) ? cores_x_package : num_cores;
189 logical_cpu_count /= cores_x_package;
190 }
191 }
192 } else {
193 // Use AMD's new method.
194 num_cores = (cpu_id[2] & 0xFF) + 1;
195 }
196 }
197}
198
199// Turn the CPU info into a string we can show
200std::string CPUInfo::Summarize() {
201 std::string sum(cpu_string);
202 sum += " (";
203 sum += brand_string;
204 sum += ")";
205
206 if (bSSE) sum += ", SSE";
207 if (bSSE2) {
208 sum += ", SSE2";
209 if (!bFlushToZero)
210 sum += " (but not DAZ!)";
211 }
212 if (bSSE3) sum += ", SSE3";
213 if (bSSSE3) sum += ", SSSE3";
214 if (bSSE4_1) sum += ", SSE4.1";
215 if (bSSE4_2) sum += ", SSE4.2";
216 if (HTT) sum += ", HTT";
217 if (bAVX) sum += ", AVX";
218 if (bAVX2) sum += ", AVX2";
219 if (bBMI1) sum += ", BMI1";
220 if (bBMI2) sum += ", BMI2";
221 if (bFMA) sum += ", FMA";
222 if (bAES) sum += ", AES";
223 if (bMOVBE) sum += ", MOVBE";
224 if (bLongMode) sum += ", 64-bit support";
225 return sum;
226}
227
228} // namespace Common