1 /* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
4 * This file is part of GROMACS.
7 * Written by the Gromacs development team under coordination of
8 * David van der Spoel, Berk Hess, and Erik Lindahl.
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
15 * To help us fund GROMACS development, we humbly ask that you cite
16 * the research papers on the package. Check out http://www.gromacs.org
19 * Gnomes, ROck Monsters And Chili Sauce
30 /* MSVC definition for __cpuid() */
35 #include "gmx_detectcpu.h"
39 gmx_detectcpu_vendorid_string[GMX_DETECTCPU_NVENDORS] =
47 gmx_detectcpu_feature_string[GMX_DETECTCPU_NFEATURES] =
64 gmx_detectcpu_acceleration_string[GMX_DETECTCPU_NACCELERATIONS] =
77 /* What type of acceleration was compiled in, if any?
78 * This is set from Cmake. Note that the SSE2 and SSE4_1 macros are set for
79 * AVX too, so it is important that they appear last in the list.
81 #ifdef GMX_X86_AVX_256
83 gmx_detectcpu_acceleration_t
84 compiled_acc = GMX_DETECTCPU_ACCELERATION_X86_AVX_256;
85 #elif defined GMX_X86_AVX_128_FMA
87 gmx_detectcpu_acceleration_t
88 compiled_acc = GMX_DETECTCPU_ACCELERATION_X86_AVX_128_FMA;
89 #elif defined GMX_X86_SSE4_1
91 gmx_detectcpu_acceleration_t
92 compiled_acc = GMX_DETECTCPU_ACCELERATION_X86_SSE4_1;
93 #elif defined GMX_X86_SSE2
95 gmx_detectcpu_acceleration_t
96 compiled_acc = GMX_DETECTCPU_ACCELERATION_X86_SSE2;
99 gmx_detectcpu_acceleration_t
100 compiled_acc = GMX_DETECTCPU_ACCELERATION_NONE;
103 /* Execute CPUID on x86 class CPUs. level sets function to exec, and the
104 * contents of register output is returned. See Intel/AMD docs for details.
106 #if defined (__i386__) || defined (__x86_64__) || defined (_M_IX86) || defined (_M_X64)
107 /* Currently CPUID is only supported (1) if we can use an instruction on MSVC, or (2)
108 * if the compiler handles GNU-style inline assembly.
110 #if (defined GMX_X86_GCC_INLINE_ASM || defined _MSC_VER)
111 #define GMX_X86_HAVE_CPUID
113 execute_cpuid_x86(unsigned int level,
119 unsigned int _eax,_ebx,_ecx,_edx;
126 __cpuid(CPUInfo,level);
136 /* for now this means GMX_X86_GCC_INLINE_ASM should be defined,
137 * but there might be more options added in the future.
139 /* tested on 32 & 64 GCC, and Intel icc. */
140 #if defined (__x86_64__) || defined (_M_X64)
141 __asm__("push %%rbx \n\t"
143 "movl %%ebx, %1 \n\t"
145 : "=a"(_eax), "=r"(_ebx), "=c"(_ecx), "=d"(_edx) : "0"(level));
147 __asm__("push %%ebx \n\t"
149 "movl %%ebx, %1 \n\t"
151 : "=a"(_eax), "=r"(_ebx), "=c"(_ecx), "=d"(_edx) : "0"(level));
156 /* If you end up having a compiler that really doesn't understand this and
157 * you can't fix it, create a separate ifdef and set the results to:
159 * _eax=_ebx=_ecx=_edx=0;
162 * However, this will lose you ALL Gromacs x86 acceleration, so you want to
163 * try really hard before giving up!
173 #endif /* GMX_X86_GCC_INLINE_ASM or _MSC_VER */
174 #endif /* architecture is x86 */
177 /* Identify CPU features common to Intel & AMD - mainly brand string,
178 * version and some features. Vendor has already been detected outside this.
181 detectcpu_common_x86(gmx_detectcpu_t * data)
183 int fn,max_stdfn,max_extfn;
184 unsigned int eax,ebx,ecx,edx;
185 char str[GMX_DETECTCPU_STRLEN];
188 #ifdef GMX_X86_HAVE_CPUID
189 /* Find largest standard/extended function input value */
190 execute_cpuid_x86(0x0,&eax,&ebx,&ecx,&edx);
192 execute_cpuid_x86(0x80000000,&eax,&ebx,&ecx,&edx);
196 if(max_extfn>=0x80000005)
198 /* Get CPU brand string */
199 for(fn=0x80000002;fn<0x80000005;fn++)
201 execute_cpuid_x86(fn,&eax,&ebx,&ecx,&edx);
210 /* Remove empty initial space */
221 strncpy(data->brand,p,GMX_DETECTCPU_STRLEN);
223 /* Find basic CPU properties */
226 execute_cpuid_x86(1,&eax,&ebx,&ecx,&edx);
228 data->family = ((eax & 0x0FF00000) >> 20) + ((eax & 0x00000F00) >> 8);
229 /* Note that extended model should be shifted left 4, so only shift right 12 iso 16. */
230 data->model = ((eax & 0x000F0000) >> 12) + ((eax & 0x000000F0) >> 4);
231 data->stepping = (eax & 0x0000000F);
233 /* Feature flags common to AMD and intel */
234 data->feature[GMX_DETECTCPU_FEATURE_X86_FMA] = (ecx & (1 << 12)) != 0;
235 data->feature[GMX_DETECTCPU_FEATURE_X86_SSE4_1] = (ecx & (1 << 19)) != 0;
236 data->feature[GMX_DETECTCPU_FEATURE_X86_AES] = (ecx & (1 << 25)) != 0;
237 data->feature[GMX_DETECTCPU_FEATURE_X86_AVX] = (ecx & (1 << 28)) != 0;
238 data->feature[GMX_DETECTCPU_FEATURE_X86_RDRAND] = (ecx & (1 << 30)) != 0;
240 data->feature[GMX_DETECTCPU_FEATURE_X86_SSE2] = (edx & (1 << 26)) != 0;
241 data->feature[GMX_DETECTCPU_FEATURE_X86_HTT] = (edx & (1 << 28)) != 0;
244 if(max_extfn>=0x80000001)
246 execute_cpuid_x86(0x80000001,&eax,&ebx,&ecx,&edx);
247 data->feature[GMX_DETECTCPU_FEATURE_X86_RDTSCP] = (edx & (1 << 27)) != 0;
251 /* No CPUID present */
252 strncpy(data->brand,"Unknown CPU brand",GMX_DETECTCPU_STRLEN);
261 /* Detection of AMD-specific CPU features */
263 detectcpu_amd(gmx_detectcpu_t * data)
265 int max_stdfn,max_extfn;
266 unsigned int eax,ebx,ecx,edx;
268 detectcpu_common_x86(data);
270 #ifdef GMX_X86_HAVE_CPUID
271 execute_cpuid_x86(0x0,&eax,&ebx,&ecx,&edx);
274 execute_cpuid_x86(0x80000000,&eax,&ebx,&ecx,&edx);
277 if(max_extfn>=0x80000001)
279 execute_cpuid_x86(0x80000001,&eax,&ebx,&ecx,&edx);
281 data->feature[GMX_DETECTCPU_FEATURE_X86_XOP] = (ecx & (1 << 11)) != 0;
282 data->feature[GMX_DETECTCPU_FEATURE_X86_FMA4] = (ecx & (1 << 16)) != 0;
289 /* Detection of Intel-specific CPU features */
291 detectcpu_intel(gmx_detectcpu_t * data)
294 unsigned int eax,ebx,ecx,edx;
296 detectcpu_common_x86(data);
298 #ifdef GMX_X86_HAVE_CPUID
299 execute_cpuid_x86(0x0,&eax,&ebx,&ecx,&edx);
304 execute_cpuid_x86(0x7,&eax,&ebx,&ecx,&edx);
305 data->feature[GMX_DETECTCPU_FEATURE_X86_AVX2] = (ebx & (1 << 5)) != 0;
313 /* Try to find the vendor of the current CPU, so we know what specific
314 * detection routine to call.
316 static gmx_detectcpu_vendorid_t
317 detectcpu_vendor(void)
319 gmx_detectcpu_vendorid_t i,vendor;
320 /* Register data used on x86 */
321 unsigned int eax,ebx,ecx,edx;
322 char vendorstring[13];
324 /* Set default first */
325 vendor = GMX_DETECTCPU_VENDOR_UNKNOWN;
327 #ifdef GMX_X86_HAVE_CPUID
328 execute_cpuid_x86(0,&eax,&ebx,&ecx,&edx);
330 memcpy(vendorstring,&ebx,4);
331 memcpy(vendorstring+4,&edx,4);
332 memcpy(vendorstring+8,&ecx,4);
334 vendorstring[12]='\0';
336 for(i=GMX_DETECTCPU_VENDOR_UNKNOWN;i<GMX_DETECTCPU_NVENDORS;i++)
338 if(!strncmp(vendorstring,gmx_detectcpu_vendorid_string[i],12))
349 gmx_detectcpu (gmx_detectcpu_t * data)
353 for(i=0;i<GMX_DETECTCPU_NFEATURES;i++)
358 data->vendorid = detectcpu_vendor();
360 switch(data->vendorid)
362 case GMX_DETECTCPU_VENDOR_INTEL:
363 detectcpu_intel(data);
365 case GMX_DETECTCPU_VENDOR_AMD:
369 /* Could not find vendor */
370 strncpy(data->brand,"Unknown CPU brand",GMX_DETECTCPU_STRLEN);
375 for(i=0;i<GMX_DETECTCPU_NFEATURES;i++)
379 data->feature[GMX_DETECTCPU_FEATURE_CANNOTDETECT] = 1;
390 gmx_detectcpu_formatstring (gmx_detectcpu_t data,
401 "Family: %2d Model: %2d Stepping: %2d\n"
403 gmx_detectcpu_vendorid_string[data.vendorid],
405 data.family,data.model,data.stepping);
410 "Family: %2d Model: %2d Stepping: %2d\n"
412 gmx_detectcpu_vendorid_string[data.vendorid],
414 data.family,data.model,data.stepping);
422 for(i=0;i<GMX_DETECTCPU_NFEATURES;i++)
424 if(data.feature[i]==1)
427 _snprintf(str,n," %s",gmx_detectcpu_feature_string[i]);
429 snprintf(str,n," %s",gmx_detectcpu_feature_string[i]);
438 _snprintf(str,n,"\n");
440 snprintf(str,n,"\n");
450 gmx_detectcpu_suggest_acceleration (gmx_detectcpu_t data,
451 gmx_detectcpu_acceleration_t * acc)
453 gmx_detectcpu_acceleration_t tmpacc;
455 tmpacc = GMX_DETECTCPU_ACCELERATION_NONE;
457 if(data.vendorid==GMX_DETECTCPU_VENDOR_INTEL)
459 if(data.feature[GMX_DETECTCPU_FEATURE_X86_AVX]==1)
461 tmpacc = GMX_DETECTCPU_ACCELERATION_X86_AVX_256;
463 else if(data.feature[GMX_DETECTCPU_FEATURE_X86_SSE4_1]==1)
465 tmpacc = GMX_DETECTCPU_ACCELERATION_X86_SSE4_1;
467 else if(data.feature[GMX_DETECTCPU_FEATURE_X86_SSE2]==1)
469 tmpacc = GMX_DETECTCPU_ACCELERATION_X86_SSE2;
472 else if(data.vendorid==GMX_DETECTCPU_VENDOR_AMD)
474 if(data.feature[GMX_DETECTCPU_FEATURE_X86_AVX]==1)
476 tmpacc = GMX_DETECTCPU_ACCELERATION_X86_AVX_128_FMA;
478 else if(data.feature[GMX_DETECTCPU_FEATURE_X86_SSE4_1]==1)
480 tmpacc = GMX_DETECTCPU_ACCELERATION_X86_SSE4_1;
482 else if(data.feature[GMX_DETECTCPU_FEATURE_X86_SSE2]==1)
484 tmpacc = GMX_DETECTCPU_ACCELERATION_X86_SSE2;
496 gmx_detectcpu_check_acceleration(gmx_detectcpu_t data,
501 gmx_detectcpu_acceleration_t acc;
503 gmx_detectcpu_suggest_acceleration(data,&acc);
504 rc = (acc != compiled_acc);
506 gmx_detectcpu_formatstring(data,str,1023);
512 "Detecting CPU-specific acceleration. Present hardware specification:\n"
514 "Acceleration most likely to fit this hardware: %s\n"
515 "Acceleration selected at Gromacs compile time: %s\n\n",
517 gmx_detectcpu_acceleration_string[acc],
518 gmx_detectcpu_acceleration_string[compiled_acc]);
525 fprintf(log,"WARNING! Binary not matching hardware - you are likely losing performance.\n\n");
527 printf("\nWARNING! Binary not matching hardware - you are likely losing performance.\n"
528 "Acceleration most likely to fit this hardware: %s\n"
529 "Acceleration selected at Gromacs compile time: %s\n\n",
530 gmx_detectcpu_acceleration_string[acc],
531 gmx_detectcpu_acceleration_string[compiled_acc]);
540 #ifdef GMX_DETECTCPU_STANDALONE
541 /* Stand-alone program to enable queries of CPU features from Cmake.
542 * Note that you need to check inline ASM capabilities before compling and set
543 * -DGMX_X86_GCC_INLINE_ASM for the cpuid instruction to work...
546 main(int argc, char **argv)
548 gmx_detectcpu_t data;
549 gmx_detectcpu_acceleration_t acc;
555 "Usage:\n\n%s [flags]\n\n"
557 "-vendor Print CPU vendor.\n"
558 "-brand Print CPU brand string.\n"
559 "-family Print CPU family version.\n"
560 "-model Print CPU model version.\n"
561 "-stepping Print CPU stepping version.\n"
562 "-features Print CPU feature flags.\n"
563 "-acceleration Print suggested Gromacs acceleration.\n"
568 gmx_detectcpu(&data);
570 if(!strncmp(argv[1],"-vendor",3))
572 printf("%s\n",gmx_detectcpu_vendorid_string[data.vendorid]);
574 else if(!strncmp(argv[1],"-brand",3))
576 printf("%s\n",data.brand);
578 else if(!strncmp(argv[1],"-family",3))
580 printf("%d\n",data.family);
582 else if(!strncmp(argv[1],"-model",3))
584 printf("%d\n",data.model);
586 else if(!strncmp(argv[1],"-stepping",3))
588 printf("%d\n",data.stepping);
590 else if(!strncmp(argv[1],"-features",3))
593 for(i=0;i<GMX_DETECTCPU_NFEATURES;i++)
595 if(data.feature[i]==1)
601 printf("%s",gmx_detectcpu_feature_string[i]);
606 else if(!strncmp(argv[1],"-acceleration",3))
608 gmx_detectcpu_suggest_acceleration(data,&acc);
609 fprintf(stdout,"%s\n",gmx_detectcpu_acceleration_string[acc]);