|
1 /* |
|
2 * cpuflags |
|
3 * |
|
4 * Simple tool which detects CPU capabilities |
|
5 * and outputs appropriate compiler flags. |
|
6 * |
|
7 * Usage: |
|
8 * cpuflags [gcc version] [intelc version] |
|
9 * |
|
10 * Returns: |
|
11 * [arch] |
|
12 * [gcc flags] |
|
13 * [intelc flags] |
|
14 * |
|
15 * The gcc/intelc version must be passed as floating point value, |
|
16 * e.g. 4.23 |
|
17 * |
|
18 * Copyright (C) 2008 Radek Brich <radek@brich.org> |
|
19 * |
|
20 * Based on x86cpucaps |
|
21 * by Osamu Kayasono <jacobi@jcom.home.ne.jp> |
|
22 * http://members.jcom.home.ne.jp/jacobi/linux/softwares.html |
|
23 * |
|
24 * This program is free software; you can redistribute it and/or |
|
25 * modify it under the terms of the GNU General Public License |
|
26 * as published by the Free Software Foundation; either version 2 |
|
27 * of the License, or (at your option) any later version. |
|
28 * |
|
29 * This program is distributed in the hope that it will be useful, |
|
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
32 * GNU General Public License for more details. |
|
33 * |
|
34 * You should have received a copy of the GNU General Public License |
|
35 * along with this program; if not, write to the Free Software |
|
36 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
37 */ |
|
38 |
|
39 #include <stdio.h> |
|
40 #include <stdlib.h> |
|
41 #include <string.h> |
|
42 |
|
43 #define LEN_VENDORNAME 13 |
|
44 |
|
45 #define VENDOR_INTEL 1 |
|
46 #define VENDOR_AMD 2 |
|
47 #define VENDOR_CYRIX 3 |
|
48 #define VENDOR_CENTAUR 4 |
|
49 #define VENDOR_TRANSMETA 5 |
|
50 #define VENDOR_OTHERS 0 |
|
51 |
|
52 struct simdcaps |
|
53 { |
|
54 unsigned int has_mmx; |
|
55 unsigned int has_sse; |
|
56 unsigned int has_sse2; |
|
57 unsigned int has_sse3; |
|
58 unsigned int has_ssse3; |
|
59 unsigned int has_sse41; |
|
60 unsigned int has_sse42; |
|
61 unsigned int has_mmxext; |
|
62 unsigned int has_3dnowext; |
|
63 unsigned int has_3dnow; |
|
64 }; |
|
65 |
|
66 /* CPU caps */ |
|
67 #define FLAG_MMX (1<<23) |
|
68 #define FLAG_SSE (1<<25) |
|
69 #define FLAG_SSE2 (1<<26) |
|
70 |
|
71 /* CPU caps 2 */ |
|
72 #define FLAG_SSE3 (1<<0) |
|
73 #define FLAG_SSSE3 (1<<9) |
|
74 #define FLAG_SSE41 (1<<19) |
|
75 #define FLAG_SSE42 (1<<20) |
|
76 |
|
77 /* AMD CPU caps */ |
|
78 #define FLAG_MMXEXT (1<<22) |
|
79 #define FLAG_3DNOWEXT (1<<30) |
|
80 #define FLAG_3DNOW (1<<31) |
|
81 |
|
82 |
|
83 /* cpuid, from kernel source ( linux/include/asm-i386/processor.h ) */ |
|
84 inline void cpuid(int op, int *eax, int *ebx, int *ecx, int *edx) |
|
85 { |
|
86 __asm__("cpuid" : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx) : "a" (op)); |
|
87 } |
|
88 |
|
89 /* |
|
90 * check SIMD capabilities |
|
91 */ |
|
92 int x86cpucaps_simd(struct simdcaps *simd) |
|
93 { |
|
94 int ex[4]; |
|
95 |
|
96 memset(&(*simd),0,sizeof(struct simdcaps)); |
|
97 |
|
98 /* check CPU has CPUID */ |
|
99 cpuid(0,&ex[0],&ex[1],&ex[2],&ex[3]); |
|
100 if ( ex[0] < 1) return 1; |
|
101 |
|
102 cpuid(0x80000001,&ex[0],&ex[1],&ex[2],&ex[3]); |
|
103 if ( (ex[3] & FLAG_MMXEXT ) == FLAG_MMXEXT) simd->has_mmxext = 1; |
|
104 if ( (ex[3] & FLAG_3DNOW ) == FLAG_3DNOW) simd->has_3dnow = 1; |
|
105 if ( (ex[3] & FLAG_3DNOWEXT ) == FLAG_3DNOWEXT) simd->has_3dnowext = 1; |
|
106 |
|
107 cpuid(1,&ex[0],&ex[1],&ex[2],&ex[3]); |
|
108 if ( (ex[3] & FLAG_MMX ) == FLAG_MMX ) simd->has_mmx = 1; |
|
109 if ( (ex[3] & FLAG_SSE ) == FLAG_SSE ) simd->has_sse = 1; |
|
110 if ( (ex[3] & FLAG_SSE2 ) == FLAG_SSE2) simd->has_sse2 = 1; |
|
111 if ( (ex[2] & FLAG_SSE3 ) == FLAG_SSE3 ) simd->has_sse3 = 1; |
|
112 if ( (ex[2] & FLAG_SSSE3 ) == FLAG_SSSE3 ) simd->has_ssse3 = 1; |
|
113 if ( (ex[2] & FLAG_SSE41 ) == FLAG_SSE41 ) simd->has_sse41 = 1; |
|
114 if ( (ex[2] & FLAG_SSE42 ) == FLAG_SSE42 ) simd->has_sse42 = 1; |
|
115 |
|
116 /* SSE CPU supports mmxext too */ |
|
117 if (simd->has_sse == 1) simd->has_mmxext = 1; |
|
118 |
|
119 return 0; |
|
120 } |
|
121 |
|
122 /* |
|
123 * check CPU Family-Model-Stepping |
|
124 */ |
|
125 int x86cpucaps_cpumodel() |
|
126 { |
|
127 int ex[4]; |
|
128 int f = 0; |
|
129 |
|
130 /* check CPU has CPUID */ |
|
131 cpuid(0,&ex[0],&ex[1],&ex[2],&ex[3]); |
|
132 if ( ex[0] < 1) return f; |
|
133 |
|
134 cpuid(1,&ex[0],&ex[1],&ex[2],&ex[3]); |
|
135 f = ex[0] & 0x0fff; |
|
136 |
|
137 return f; |
|
138 } |
|
139 |
|
140 /* |
|
141 * check CPU Vendor |
|
142 */ |
|
143 int x86cpucaps_vendor(char *vendorname) |
|
144 { |
|
145 int ex[4]; |
|
146 int f = 0; |
|
147 char vendorstr[LEN_VENDORNAME]; |
|
148 |
|
149 /* check CPU has CPUID */ |
|
150 cpuid(0,&ex[0],&ex[1],&ex[2],&ex[3]); |
|
151 if ( ex[0] < 1) return f; |
|
152 |
|
153 /* read Vendor Strings */ |
|
154 vendorstr[0] = ex[1] & 0xff; |
|
155 vendorstr[1] = (ex[1] >> 8) & 0xff; |
|
156 vendorstr[2] = (ex[1] >> 16) & 0xff; |
|
157 vendorstr[3] = (ex[1] >> 24) & 0xff; |
|
158 vendorstr[4] = ex[3] & 0xff; |
|
159 vendorstr[5] = (ex[3] >> 8) & 0xff; |
|
160 vendorstr[6] = (ex[3] >> 16) & 0xff; |
|
161 vendorstr[7] = (ex[3] >> 24) & 0xff; |
|
162 vendorstr[8] = ex[2] & 0xff; |
|
163 vendorstr[9] = (ex[2] >> 8) & 0xff; |
|
164 vendorstr[10]= (ex[2] >> 16) & 0xff; |
|
165 vendorstr[11]= (ex[2] >> 24) & 0xff; |
|
166 vendorstr[12]= '\0'; |
|
167 |
|
168 if ( strcmp(vendorstr, "GenuineIntel") == 0 ) |
|
169 f = VENDOR_INTEL; |
|
170 else if ( strcmp(vendorstr, "AuthenticAMD") == 0 ) |
|
171 f = VENDOR_AMD; |
|
172 else if ( strcmp(vendorstr, "CyrixInstead") == 0 ) |
|
173 f = VENDOR_CYRIX; |
|
174 else if ( strcmp(vendorstr, "CentaurHauls") == 0 ) |
|
175 f = VENDOR_CENTAUR; |
|
176 else if ( strcmp(vendorstr, "GenuineTMx86") == 0 ) |
|
177 f = VENDOR_TRANSMETA; |
|
178 |
|
179 strncpy(vendorname, vendorstr, LEN_VENDORNAME); |
|
180 |
|
181 return f; |
|
182 } |
|
183 |
|
184 |
|
185 int main(int argc, char **argv) |
|
186 { |
|
187 int family, model, stepping; |
|
188 char *arch, *gccarch, *gccsimd, *iccarch, *icctune, *iccsimd; |
|
189 |
|
190 int cpu_id = x86cpucaps_cpumodel(); |
|
191 char vendorname[LEN_VENDORNAME]; |
|
192 int vendor_id = x86cpucaps_vendor(vendorname); |
|
193 struct simdcaps simd; |
|
194 |
|
195 float gccver = 999.; |
|
196 float iccver = 999.; |
|
197 |
|
198 if (argc > 1) |
|
199 gccver = atof(argv[1]); |
|
200 if (argc > 2) |
|
201 iccver = atof(argv[2]); |
|
202 |
|
203 family = (cpu_id & 0xf00) >> 8; |
|
204 model = (cpu_id & 0x0f0) >> 4; |
|
205 stepping = cpu_id & 0x00f; |
|
206 |
|
207 switch (vendor_id) |
|
208 { |
|
209 |
|
210 case VENDOR_INTEL: |
|
211 if (family == 4) |
|
212 { |
|
213 arch = "i486"; |
|
214 } |
|
215 else if (family == 5) |
|
216 { |
|
217 if (model < 4) arch = "pentium"; |
|
218 else arch = "pentium-mmx"; |
|
219 } |
|
220 else if (family == 6) |
|
221 { |
|
222 if (model <= 1) arch = "pentiumpro"; |
|
223 else if (model < 7) arch = "pentium2"; |
|
224 else if (model == 7) arch = "pentium3"; |
|
225 else if (model < 15) arch = "pentium-m"; |
|
226 else if (model == 15) |
|
227 { |
|
228 if (stepping < 6) arch = "core"; |
|
229 else arch = "core2"; |
|
230 } |
|
231 |
|
232 } |
|
233 else if (family > 6) |
|
234 { /* family == 15 */ |
|
235 arch = "pentium4"; |
|
236 } |
|
237 else |
|
238 { |
|
239 arch = "i386"; |
|
240 } |
|
241 break; |
|
242 |
|
243 case VENDOR_AMD: |
|
244 if (family == 4) |
|
245 { |
|
246 if (model <= 9) arch = "i486"; |
|
247 else arch = "i586"; |
|
248 } |
|
249 else if (family == 5) |
|
250 { |
|
251 if (model <= 3) arch = "i586"; |
|
252 else if (model <= 7) arch = "k6"; |
|
253 else if (model == 8) arch = "k6-2"; |
|
254 else arch = "k6-3"; |
|
255 } |
|
256 else if (family == 6) |
|
257 { |
|
258 if (model <= 3) arch = "athlon"; |
|
259 else if (model == 4) arch = "athlon-tbird"; |
|
260 else arch = "athlon-xp"; |
|
261 } |
|
262 else if (family > 6) |
|
263 { |
|
264 arch = "k8"; |
|
265 } |
|
266 else |
|
267 { |
|
268 arch = "unknown"; |
|
269 } |
|
270 break; |
|
271 |
|
272 case VENDOR_CYRIX: |
|
273 if (family == 4) arch = "i586"; |
|
274 else if (family == 5) arch = "i586"; |
|
275 else if (family == 6) arch = "i686"; |
|
276 else arch = "unknown"; |
|
277 break; |
|
278 |
|
279 case VENDOR_CENTAUR: |
|
280 if (family == 5) arch = "i586"; |
|
281 else arch = "unknown"; |
|
282 break; |
|
283 |
|
284 case VENDOR_TRANSMETA: |
|
285 arch = "i686"; |
|
286 break; |
|
287 |
|
288 default: |
|
289 arch = "unknown"; |
|
290 break; |
|
291 |
|
292 } |
|
293 |
|
294 /* some targets not supported by older gcc */ |
|
295 gccarch = arch; |
|
296 if (gccver < (float)4.3) |
|
297 { |
|
298 if (!strcmp(gccarch, "core2")) gccarch = "pentium3"; |
|
299 } |
|
300 if (gccver < (float)3.4) |
|
301 { |
|
302 if (!strcmp(gccarch, "k8")) gccarch = "athlon-xp"; |
|
303 } |
|
304 if (gccver < (float)3.1) |
|
305 { |
|
306 if (strstr(gccarch, "athlon-") != NULL) |
|
307 gccarch = "athlon"; |
|
308 else if (strstr(gccarch, "k6-") != NULL) |
|
309 gccarch = "k6"; |
|
310 else if (!strcmp(gccarch, "pentium-mmx")) |
|
311 gccarch = "pentium"; |
|
312 else if (!strcmp(gccarch, "pentium2") |
|
313 || !strcmp(gccarch, "pentium3") |
|
314 || !strcmp(gccarch, "pentium4")) |
|
315 gccarch = "pentiumpro"; |
|
316 } |
|
317 |
|
318 if (gccver < (float)3.0) |
|
319 { |
|
320 if (!strcmp(gccarch, "athlon")) |
|
321 gccarch = "pentiumpro"; |
|
322 else if (!strcmp(gccarch, "k6")) |
|
323 gccarch = "pentium"; |
|
324 } |
|
325 |
|
326 if (gccver < (float)2.9) |
|
327 { |
|
328 if (!strcmp(gccarch, "pentiumpro")) |
|
329 gccarch = "i686"; |
|
330 else if (!strcmp(gccarch, "pentium")) |
|
331 gccarch = "i586"; |
|
332 } |
|
333 |
|
334 /* SIMD options */ |
|
335 x86cpucaps_simd(&simd); |
|
336 gccsimd = ""; |
|
337 if (gccver >= 3.1) { |
|
338 if ( simd.has_3dnow || simd.has_3dnowext ) |
|
339 gccsimd = "-m3dnow"; |
|
340 else |
|
341 { |
|
342 if (gccver >= 4.3) |
|
343 { |
|
344 if (simd.has_sse41 || simd.has_sse42) gccsimd = "-msse4 -mfpmath=sse"; |
|
345 else if (simd.has_ssse3) gccsimd = "-mssse3 -mfpmath=sse"; |
|
346 } |
|
347 else if ( gccver >= 3.4 && simd.has_sse3 ) gccsimd = "-msse3 -mfpmath=sse"; |
|
348 else if ( simd.has_sse2 ) gccsimd = "-msse2 -mfpmath=sse"; |
|
349 else if ( simd.has_sse ) gccsimd = "-msse"; |
|
350 else if ( simd.has_mmx ) gccsimd = "-mmmx"; |
|
351 } |
|
352 } |
|
353 |
|
354 /* intelc options */ |
|
355 iccarch = arch; |
|
356 icctune = arch; |
|
357 iccsimd = ""; |
|
358 if (simd.has_sse41 || simd.has_sse42) iccsimd = "-xS"; |
|
359 else if (simd.has_ssse3) iccsimd = "-xT"; |
|
360 else if (simd.has_sse3) iccsimd = "-msse3 -xP"; |
|
361 else if (simd.has_sse2) iccsimd = "-msse2"; |
|
362 |
|
363 printf("%s\n", arch); |
|
364 if (gccver >= 4.2) gccarch = "native"; |
|
365 printf("-march=%s -mtune=%s %s\n", gccarch, gccarch, gccsimd); |
|
366 printf("-march=%s -mtune=%s %s\n", iccarch, icctune, iccsimd); |
|
367 return 0; |
|
368 } |