1 | /* |
2 | SPDX-FileCopyrightText: 2010 Kevin Ottens <ervin@kde.org> |
3 | SPDX-FileCopyrightText: 2013 Patrick Spendrin <ps_ml@gmx.de> |
4 | |
5 | SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL |
6 | */ |
7 | |
8 | // clang-format off |
9 | |
10 | #include "cpufeatures.h" |
11 | |
12 | #ifndef _MSC_VER |
13 | //for cpuFeatures |
14 | #include <csignal> |
15 | #include <csetjmp> |
16 | #else |
17 | #include <intrin.h> |
18 | #endif |
19 | #include <config-processor.h> |
20 | |
21 | #if defined(__GNUC__) || defined(__INTEL_COMPILER) |
22 | # define HAVE_GNU_INLINE_ASM |
23 | #endif |
24 | |
25 | namespace Solid |
26 | { |
27 | namespace Backends |
28 | { |
29 | namespace Shared |
30 | { |
31 | |
32 | #ifndef _MSC_VER |
33 | typedef void (*kde_sighandler_t)(int); |
34 | |
35 | #if defined( __i386__ ) || defined( __x86_64__ ) |
36 | static jmp_buf env; |
37 | |
38 | #if HAVE_X86_SSE |
39 | // Sighandler for the SSE OS support check |
40 | static void sighandler(int) |
41 | { |
42 | std::longjmp(env: env, val: 1); |
43 | } |
44 | #endif |
45 | #endif |
46 | |
47 | #ifdef __i386__ |
48 | #define ASM_REG(reg) "%e" reg |
49 | #define ASM_POP(reg) "popl %%e" reg " \n\t" |
50 | #define ASM_PUSH(reg) "pushl %%e" reg " \n\t" |
51 | #define ASM_XOR_REG(reg1, reg2) "xorl %%e" reg1 ", %%e" reg2 " \n\t" |
52 | #define ASM_XOR_VAR(var, reg) "xorl " var ", %%e" reg " \n\t" |
53 | #define ASM_CMP_REG(reg1, reg2) "cmpl %%e" reg1 ", %%e" reg2 " \n\t" |
54 | #define ASM_MOV_REG(reg1, reg2) "movl %%e" reg1 ", %%e" reg2 " \n\t" |
55 | #define ASM_MOV_VAR(var, reg) "movl " var ", %%e" reg " \n\t" |
56 | #elif defined(__x86_64__) |
57 | #define ASM_REG(reg) "%r" reg |
58 | #define ASM_POP(reg) "popq %%r" reg " \n\t" |
59 | #define ASM_PUSH(reg) "pushq %%r" reg " \n\t" |
60 | #define ASM_XOR_REG(reg1, reg2) "xorq %%r" reg1 ", %%r" reg2 " \n\t" |
61 | #define ASM_XOR_VAR(var, reg) "xorq " var ", %%r" reg " \n\t" |
62 | #define ASM_CMP_REG(reg1, reg2) "cmpq %%r" reg1 ", %%r" reg2 " \n\t" |
63 | #define ASM_MOV_REG(reg1, reg2) "movq %%r" reg1 ", %%r" reg2 " \n\t" |
64 | #define ASM_MOV_VAR(var, reg) "movq " var ", %%r" reg " \n\t" |
65 | #endif |
66 | #endif |
67 | |
68 | #ifdef __PPC__ |
69 | static sigjmp_buf jmpbuf; |
70 | static sig_atomic_t canjump = 0; |
71 | |
72 | static void sigill_handler(int sig) |
73 | { |
74 | if (!canjump) { |
75 | signal(sig, SIG_DFL); |
76 | raise(sig); |
77 | } |
78 | canjump = 0; |
79 | siglongjmp(jmpbuf, 1); |
80 | } |
81 | #endif |
82 | |
83 | Solid::Processor::InstructionSets cpuFeatures() |
84 | { |
85 | volatile unsigned int features = 0; |
86 | |
87 | #if defined( HAVE_GNU_INLINE_ASM ) |
88 | #if defined( __i386__ ) || defined( __x86_64__ ) |
89 | bool haveCPUID = false; |
90 | unsigned int result = 0; |
91 | unsigned int result2 = 0; |
92 | |
93 | // First check if the CPU supports the CPUID instruction |
94 | __asm__ __volatile__( |
95 | // Try to toggle the CPUID bit in the EFLAGS register |
96 | "pushf \n\t" // Push the EFLAGS register onto the stack |
97 | ASM_POP("cx" ) // Pop the value into ECX |
98 | ASM_MOV_REG("cx" , "dx" ) // Copy ECX to EDX |
99 | ASM_XOR_VAR("$0x00200000" , "cx" ) // Toggle bit 21 (CPUID) in ECX |
100 | ASM_PUSH("cx" ) // Push the modified value onto the stack |
101 | "popf \n\t" // Pop it back into EFLAGS |
102 | |
103 | // Check if the CPUID bit was successfully toggled |
104 | "pushf \n\t" // Push EFLAGS back onto the stack |
105 | ASM_POP("cx" ) // Pop the value into ECX |
106 | ASM_XOR_REG("ax" , "ax" ) // Zero out the EAX register |
107 | ASM_CMP_REG("cx" , "dx" ) // Compare ECX with EDX |
108 | "je .Lno_cpuid_support%= \n\t" // Jump if they're identical |
109 | ASM_MOV_VAR("$1" , "ax" ) // Set EAX to true |
110 | ".Lno_cpuid_support%=: \n\t" |
111 | : "=a" (haveCPUID) : : ASM_REG("cx" ), ASM_REG("dx" )); |
112 | |
113 | // If we don't have CPUID we won't have the other extensions either |
114 | if (haveCPUID) { |
115 | // Execute CPUID with the feature request bit set |
116 | __asm__ __volatile__( |
117 | ASM_PUSH("bx" ) // Save EBX |
118 | ASM_MOV_VAR("$1" , "ax" ) // Set EAX to 1 (features request) |
119 | "cpuid \n\t" // Call CPUID |
120 | ASM_POP("bx" ) // Restore EBX |
121 | : "=d" (result), "=c" (result2) : : ASM_REG("ax" )); |
122 | |
123 | features = result & 0x06800000; //copy the mmx and sse & sse2 bits to features |
124 | features |= result2 & 0x00180101; //copy the ssse3, sse3 and sse4.1, sse4.2 bits to features |
125 | |
126 | __asm__ __volatile__( |
127 | ASM_PUSH("bx" ) |
128 | ASM_PUSH("dx" ) |
129 | ASM_MOV_VAR("$0x80000000" , "ax" ) |
130 | ASM_MOV_VAR("$0x80000000" , "dx" ) |
131 | "cpuid \n\t" |
132 | ASM_CMP_REG("dx" , "ax" ) |
133 | "jbe .Lno_extended%= \n\t" |
134 | ASM_MOV_VAR("$0x80000001" , "ax" ) |
135 | "cpuid \n\t" |
136 | ".Lno_extended%=: \n\t" |
137 | ASM_POP("dx" ) |
138 | ASM_POP("bx" ) // Restore EBX |
139 | : "=d" (result) : : ASM_REG("ax" ), ASM_REG("cx" )); |
140 | |
141 | if (result & 0x80000000) { |
142 | features |= 0x80000000; |
143 | } |
144 | |
145 | #if HAVE_X86_SSE |
146 | // Test bit 25 (SSE support) |
147 | if (features & 0x02000000) { |
148 | // OS support test for SSE. |
149 | // Install our own sighandler for SIGILL. |
150 | kde_sighandler_t oldhandler = std::signal(SIGILL, handler: sighandler); |
151 | |
152 | // Try executing an SSE insn to see if we get a SIGILL |
153 | if (setjmp(env)) { |
154 | features &= ~0x06080001; // The OS support test failed |
155 | } else { |
156 | __asm__ __volatile__("xorps %xmm0, %xmm0" ); |
157 | } |
158 | |
159 | // Restore the default sighandler |
160 | std::signal(SIGILL, handler: oldhandler); |
161 | |
162 | // Note: The OS requirements for SSE2 are the same as for SSE |
163 | // so we don't have to do any additional tests for that. |
164 | } |
165 | #endif // HAVE_X86_SSE |
166 | } |
167 | #elif defined __PPC__ && HAVE_PPC_ALTIVEC |
168 | signal(SIGILL, sigill_handler); |
169 | if (sigsetjmp(jmpbuf, 1)) { |
170 | signal(SIGILL, SIG_DFL); |
171 | } else { |
172 | canjump = 1; |
173 | __asm__ __volatile__("mtspr 256, %0\n\t" |
174 | "vand %%v0, %%v0, %%v0" |
175 | : /* none */ |
176 | : "r" (-1)); |
177 | signal(SIGILL, SIG_DFL); |
178 | features = 0x2; |
179 | } |
180 | #endif // __i386__ || __x86_64__ |
181 | #elif defined(_MSC_VER) |
182 | int array[4], ft = 1; |
183 | __cpuid(array, ft); |
184 | |
185 | features = array[3] & 0x06800000; //copy the mmx and sse & sse2 bits to features |
186 | features |= array[2] & 0x00180101; //copy the ssse3, sse3 and sse4.1, sse4.2 bits to features |
187 | |
188 | if (array[3] & 0x80000000) { |
189 | features |= 0x80000000; |
190 | } |
191 | #endif //HAVE_GNU_INLINE_ASM |
192 | Solid::Processor::InstructionSets featureflags; |
193 | |
194 | if (features & 0x80000000) { |
195 | featureflags |= Solid::Processor::Amd3DNow; |
196 | } |
197 | if (features & 0x00800000) { |
198 | featureflags |= Solid::Processor::IntelMmx; |
199 | } |
200 | if (features & 0x02000000) { |
201 | featureflags |= Solid::Processor::IntelSse; |
202 | } |
203 | if (features & 0x04000000) { |
204 | featureflags |= Solid::Processor::IntelSse2; |
205 | } |
206 | if (features & 0x00000001) { |
207 | featureflags |= Solid::Processor::IntelSse3; |
208 | } |
209 | if (features & 0x00000100) { |
210 | featureflags |= Solid::Processor::IntelSsse3; |
211 | } |
212 | if (features & 0x00080000) { |
213 | featureflags |= Solid::Processor::IntelSse41; |
214 | } |
215 | if (features & 0x00100000) { |
216 | featureflags |= Solid::Processor::IntelSse42; |
217 | } |
218 | |
219 | if (features & 0x2) { |
220 | featureflags |= Solid::Processor::AltiVec; |
221 | } |
222 | |
223 | return featureflags; |
224 | } |
225 | |
226 | } |
227 | } |
228 | } |
229 | |