bf7c1302a2a17b9a14649174699b90aa1c72f945
[alexxy/gromacs.git] / src / gmxlib / gmx_cpuid.c
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012, by the GROMACS development team, led by
5  * David van der Spoel, Berk Hess, Erik Lindahl, and including many
6  * others, as listed in the AUTHORS file in the top-level source
7  * directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 #ifdef HAVE_CONFIG_H
36 #include <config.h>
37 #endif
38
39 #ifdef HAVE_SCHED_H
40 #define _GNU_SOURCE
41 #include <sched.h>
42 #endif
43
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <ctype.h>
48 #ifdef _MSC_VER
49 /* MSVC definition for __cpuid() */
50 #include <intrin.h>
51 #endif
52 #ifdef HAVE_UNISTD_H
53 /* sysconf() definition */
54 #include <unistd.h>
55 #endif
56
57 #include "gmx_cpuid.h"
58
59
60
61 /* For convenience, and to enable configure-time invocation, we keep all architectures
62  * in a single file, but to avoid repeated ifdefs we set the overall architecture here.
63  */
64 #if defined (__i386__) || defined (__x86_64__) || defined (_M_IX86) || defined (_M_X64)
65 #    define GMX_CPUID_X86
66 #endif
67
68 /* Global constant character strings corresponding to our enumerated types */
69 const char *
70 gmx_cpuid_vendor_string[GMX_CPUID_NVENDORS] =
71 {
72     "CannotDetect",
73     "Unknown",
74     "GenuineIntel",
75     "AuthenticAMD"
76 };
77
78 const char *
79 gmx_cpuid_feature_string[GMX_CPUID_NFEATURES] =
80 {
81     "CannotDetect",
82     "aes",
83     "apic",
84     "avx",
85     "avx2",
86     "clfsh",
87     "cmov",
88     "cx8",
89     "cx16",
90     "f16c",
91     "fma",
92     "fma4",
93     "htt",
94     "lahf_lm",
95     "misalignsse",
96     "mmx",
97     "msr",
98     "nonstop_tsc",
99     "pcid",
100     "pclmuldq",
101     "pdcm",
102     "pdpe1gb",
103     "popcnt",
104     "pse",
105     "rdrnd",
106     "rdtscp",
107     "sse2",
108     "sse3",
109     "sse4a",
110     "sse4.1",
111     "sse4.2",
112     "ssse3",
113     "tdt",
114     "x2apic",
115     "xop"
116 };
117
118 const char *
119 gmx_cpuid_acceleration_string[GMX_CPUID_NACCELERATIONS] =
120 {
121     "CannotDetect",
122     "None",
123     "SSE2",
124     "SSE4.1",
125     "AVX_128_FMA",
126     "AVX_256"
127 };
128
129 /* Max length of brand string */
130 #define GMX_CPUID_BRAND_MAXLEN 256
131
132
133 /* Contents of the abstract datatype */
134 struct gmx_cpuid
135 {
136     enum gmx_cpuid_vendor      vendor;
137     char                       brand[GMX_CPUID_BRAND_MAXLEN];
138     int                        family;
139     int                        model;
140     int                        stepping;
141     /* Not using gmx_bool here, since this file must be possible to compile without simple.h */
142     char                       feature[GMX_CPUID_NFEATURES];
143 };
144
145
146 /* Simple routines to access the data structure. The initialization routine is
147  * further down since that needs to call other static routines in this file.
148  */
149 enum gmx_cpuid_vendor
150 gmx_cpuid_vendor            (gmx_cpuid_t                cpuid)
151 {
152     return cpuid->vendor;
153 }
154
155
156 const char *
157 gmx_cpuid_brand             (gmx_cpuid_t                cpuid)
158 {
159     return cpuid->brand;
160 }
161
162 int
163 gmx_cpuid_family            (gmx_cpuid_t                cpuid)
164 {
165     return cpuid->family;
166 }
167
168 int
169 gmx_cpuid_model             (gmx_cpuid_t                cpuid)
170 {
171     return cpuid->model;
172 }
173
174 int
175 gmx_cpuid_stepping          (gmx_cpuid_t                cpuid)
176 {
177     return cpuid->stepping;
178 }
179
180 int
181 gmx_cpuid_feature           (gmx_cpuid_t                cpuid,
182                              enum gmx_cpuid_feature     feature)
183 {
184     return (cpuid->feature[feature]!=0);
185 }
186
187
188
189
190 /* What type of acceleration was compiled in, if any?
191  * This is set from Cmake. Note that the SSE2 and SSE4_1 macros are set for
192  * AVX too, so it is important that they appear last in the list.
193  */
194 #ifdef GMX_X86_AVX_256
195 static const
196 enum gmx_cpuid_acceleration
197 compiled_acc = GMX_CPUID_ACCELERATION_X86_AVX_256;
198 #elif defined GMX_X86_AVX_128_FMA
199 static const
200 enum gmx_cpuid_acceleration
201 compiled_acc = GMX_CPUID_ACCELERATION_X86_AVX_128_FMA;
202 #elif defined GMX_X86_SSE4_1
203 static const
204 enum gmx_cpuid_acceleration
205 compiled_acc = GMX_CPUID_ACCELERATION_X86_SSE4_1;
206 #elif defined GMX_X86_SSE2
207 static const
208 enum gmx_cpuid_acceleration
209 compiled_acc = GMX_CPUID_ACCELERATION_X86_SSE2;
210 #else
211 static const
212 enum gmx_cpuid_acceleration
213 compiled_acc = GMX_CPUID_ACCELERATION_NONE;
214 #endif
215
216
217 #ifdef GMX_CPUID_X86
218
219 /* Execute CPUID on x86 class CPUs. level sets function to exec, and the
220  * contents of register output is returned. See Intel/AMD docs for details.
221  *
222  * This version supports extended information where we can also have an input
223  * value in the ecx register. This is ignored for most levels, but some of them
224  * (e.g. level 0xB on Intel) use it.
225  */
226 static int
227 execute_x86cpuid(unsigned int   level,
228                  unsigned int   ecxval,
229                  unsigned int * eax,
230                  unsigned int * ebx,
231                  unsigned int * ecx,
232                  unsigned int * edx)
233 {
234     int rc = 0;
235
236     /* Currently CPUID is only supported (1) if we can use an instruction on MSVC, or (2)
237      * if the compiler handles GNU-style inline assembly.
238      */
239
240 #if (defined _MSC_VER)
241     int CPUInfo[4];
242
243 #if (_MSC_VER > 1500) || (_MSC_VER==1500 & _MSC_FULL_VER >= 150030729)
244     /* MSVC 9.0 SP1 or later */
245     __cpuidex(CPUInfo,level,ecxval);
246     rc = 0;
247 #else
248     __cpuid(CPUInfo,level);
249     /* Set an error code if the user wanted a non-zero ecxval, since we did not have cpuidex */
250     rc = (ecxval>0) ? -1 : 0;
251 #endif
252     *eax=CPUInfo[0];
253     *ebx=CPUInfo[1];
254     *ecx=CPUInfo[2];
255     *edx=CPUInfo[3];
256
257 #elif (defined GMX_X86_GCC_INLINE_ASM)
258     /* for now this means GMX_X86_GCC_INLINE_ASM should be defined,
259      * but there might be more options added in the future.
260      */
261     *eax = level;
262     *ecx = ecxval;
263     *ebx = 0;
264     *edx = 0;
265 #if defined(__i386__) && defined(__PIC__)
266     /* Avoid clobbering the global offset table in 32-bit pic code (ebx register) */
267     __asm__ __volatile__ ("xchgl %%ebx, %1  \n\t"
268                           "cpuid            \n\t"
269                           "xchgl %%ebx, %1  \n\t"
270                           : "+a"(*eax), "+r"(*ebx), "+c"(*ecx), "+d"(*edx));
271 #else
272     /* i386 without PIC, or x86-64. Things are easy and we can clobber any reg we want :-) */
273     __asm__ __volatile__ ("cpuid            \n\t"
274                           : "+a"(*eax), "+b"(*ebx), "+c"(*ecx), "+d"(*edx));
275 #endif
276     rc = 0;
277 #else
278     /* Death and horror!
279      * Apparently this is an x86 platform where we don't know how to call cpuid.
280      *
281      * This is REALLY bad, since we will lose all Gromacs acceleration.
282      */
283     *eax = 0;
284     *ebx = 0;
285     *ecx = 0;
286     *edx = 0;
287
288     rc = -1;
289 #endif
290     return rc;
291 }
292
293
294 /* Identify CPU features common to Intel & AMD - mainly brand string,
295  * version and some features. Vendor has already been detected outside this.
296  */
297 static int
298 cpuid_check_common_x86(gmx_cpuid_t                cpuid)
299 {
300     int                       fn,max_stdfn,max_extfn;
301     unsigned int              eax,ebx,ecx,edx;
302     char                      str[GMX_CPUID_BRAND_MAXLEN];
303     char *                    p;
304
305     /* Find largest standard/extended function input value */
306     execute_x86cpuid(0x0,0,&eax,&ebx,&ecx,&edx);
307     max_stdfn = eax;
308     execute_x86cpuid(0x80000000,0,&eax,&ebx,&ecx,&edx);
309     max_extfn = eax;
310
311     p = str;
312     if(max_extfn>=0x80000005)
313     {
314         /* Get CPU brand string */
315         for(fn=0x80000002;fn<0x80000005;fn++)
316         {
317             execute_x86cpuid(fn,0,&eax,&ebx,&ecx,&edx);
318             memcpy(p,&eax,4);
319             memcpy(p+4,&ebx,4);
320             memcpy(p+8,&ecx,4);
321             memcpy(p+12,&edx,4);
322             p+=16;
323         }
324         *p='\0';
325
326         /* Remove empty initial space */
327         p = str;
328         while(isspace(*(p)))
329         {
330             p++;
331         }
332         strncpy(cpuid->brand,p,GMX_CPUID_BRAND_MAXLEN);
333     }
334     else
335     {
336         strncpy(cpuid->brand,"Unknown CPU brand",GMX_CPUID_BRAND_MAXLEN);
337     }
338
339     /* Find basic CPU properties */
340     if(max_stdfn>=1)
341     {
342         execute_x86cpuid(0x1,0,&eax,&ebx,&ecx,&edx);
343
344         cpuid->family   = ((eax & 0x0FF00000) >> 20) + ((eax & 0x00000F00) >> 8);
345         /* Note that extended model should be shifted left 4, so only shift right 12 iso 16. */
346         cpuid->model    = ((eax & 0x000F0000) >> 12) + ((eax & 0x000000F0) >> 4);
347         cpuid->stepping = (eax & 0x0000000F);
348
349         /* Feature flags common to AMD and intel */
350         cpuid->feature[GMX_CPUID_FEATURE_X86_SSE3]     = (ecx & (1 << 0))  != 0;
351         cpuid->feature[GMX_CPUID_FEATURE_X86_PCLMULDQ] = (ecx & (1 << 1))  != 0;
352         cpuid->feature[GMX_CPUID_FEATURE_X86_SSSE3]    = (ecx & (1 << 9))  != 0;
353         cpuid->feature[GMX_CPUID_FEATURE_X86_FMA]      = (ecx & (1 << 12)) != 0;
354         cpuid->feature[GMX_CPUID_FEATURE_X86_CX16]     = (ecx & (1 << 13)) != 0;
355         cpuid->feature[GMX_CPUID_FEATURE_X86_SSE4_1]   = (ecx & (1 << 19)) != 0;
356         cpuid->feature[GMX_CPUID_FEATURE_X86_SSE4_2]   = (ecx & (1 << 20)) != 0;
357         cpuid->feature[GMX_CPUID_FEATURE_X86_POPCNT]   = (ecx & (1 << 23)) != 0;
358         cpuid->feature[GMX_CPUID_FEATURE_X86_AES]      = (ecx & (1 << 25)) != 0;
359         cpuid->feature[GMX_CPUID_FEATURE_X86_AVX]      = (ecx & (1 << 28)) != 0;
360         cpuid->feature[GMX_CPUID_FEATURE_X86_F16C]     = (ecx & (1 << 29)) != 0;
361         cpuid->feature[GMX_CPUID_FEATURE_X86_RDRND]    = (ecx & (1 << 30)) != 0;
362
363         cpuid->feature[GMX_CPUID_FEATURE_X86_PSE]      = (edx & (1 << 3))  != 0;
364         cpuid->feature[GMX_CPUID_FEATURE_X86_MSR]      = (edx & (1 << 5))  != 0;
365         cpuid->feature[GMX_CPUID_FEATURE_X86_CX8]      = (edx & (1 << 8))  != 0;
366         cpuid->feature[GMX_CPUID_FEATURE_X86_APIC]     = (edx & (1 << 9))  != 0;
367         cpuid->feature[GMX_CPUID_FEATURE_X86_CMOV]     = (edx & (1 << 15)) != 0;
368         cpuid->feature[GMX_CPUID_FEATURE_X86_CLFSH]    = (edx & (1 << 19)) != 0;
369         cpuid->feature[GMX_CPUID_FEATURE_X86_MMX]      = (edx & (1 << 23)) != 0;
370         cpuid->feature[GMX_CPUID_FEATURE_X86_SSE2]     = (edx & (1 << 26)) != 0;
371         cpuid->feature[GMX_CPUID_FEATURE_X86_HTT]      = (edx & (1 << 28)) != 0;
372     }
373     else
374     {
375         cpuid->family   = -1;
376         cpuid->model    = -1;
377         cpuid->stepping = -1;
378     }
379
380     if(max_extfn>=0x80000001)
381     {
382         execute_x86cpuid(0x80000001,0,&eax,&ebx,&ecx,&edx);
383         cpuid->feature[GMX_CPUID_FEATURE_X86_LAHF_LM] = (ecx & (1 << 0))  != 0;
384         cpuid->feature[GMX_CPUID_FEATURE_X86_PDPE1GB] = (edx & (1 << 26)) != 0;
385         cpuid->feature[GMX_CPUID_FEATURE_X86_RDTSCP]  = (edx & (1 << 27)) != 0;
386     }
387
388     if(max_extfn>=0x80000007)
389     {
390         execute_x86cpuid(0x80000007,0,&eax,&ebx,&ecx,&edx);
391         cpuid->feature[GMX_CPUID_FEATURE_X86_NONSTOP_TSC]  = (edx & (1 << 8))  != 0;
392     }
393
394     return 0;
395 }
396
397 /* Detection of AMD-specific CPU features */
398 static int
399 cpuid_check_amd_x86(gmx_cpuid_t                cpuid)
400 {
401     int                       max_stdfn,max_extfn;
402     unsigned int              eax,ebx,ecx,edx;
403
404     cpuid_check_common_x86(cpuid);
405
406     execute_x86cpuid(0x0,0,&eax,&ebx,&ecx,&edx);
407     max_stdfn = eax;
408
409     execute_x86cpuid(0x80000000,0,&eax,&ebx,&ecx,&edx);
410     max_extfn = eax;
411
412     if(max_extfn>=0x80000001)
413     {
414         execute_x86cpuid(0x80000001,0,&eax,&ebx,&ecx,&edx);
415
416         cpuid->feature[GMX_CPUID_FEATURE_X86_SSE4A]       = (ecx & (1 << 6))  != 0;
417         cpuid->feature[GMX_CPUID_FEATURE_X86_MISALIGNSSE] = (ecx & (1 << 7))  != 0;
418         cpuid->feature[GMX_CPUID_FEATURE_X86_XOP]         = (ecx & (1 << 11)) != 0;
419         cpuid->feature[GMX_CPUID_FEATURE_X86_FMA4]        = (ecx & (1 << 16)) != 0;
420     }
421
422     return 0;
423 }
424
425 /* Detection of Intel-specific CPU features */
426 static int
427 cpuid_check_intel_x86(gmx_cpuid_t                cpuid)
428 {
429     unsigned int              max_stdfn,max_extfn;
430     unsigned int              eax,ebx,ecx,edx;
431     unsigned int              i;
432     unsigned int              max_logical_cores,max_physical_cores;
433
434     cpuid_check_common_x86(cpuid);
435
436     execute_x86cpuid(0x0,0,&eax,&ebx,&ecx,&edx);
437     max_stdfn = eax;
438
439     execute_x86cpuid(0x80000000,0,&eax,&ebx,&ecx,&edx);
440     max_extfn = eax;
441
442     if(max_stdfn>=1)
443     {
444         execute_x86cpuid(0x1,0,&eax,&ebx,&ecx,&edx);
445         cpuid->feature[GMX_CPUID_FEATURE_X86_PDCM]    = (ecx & (1 << 15)) != 0;
446         cpuid->feature[GMX_CPUID_FEATURE_X86_PCID]    = (ecx & (1 << 17)) != 0;
447         cpuid->feature[GMX_CPUID_FEATURE_X86_X2APIC]  = (ecx & (1 << 21)) != 0;
448         cpuid->feature[GMX_CPUID_FEATURE_X86_TDT]     = (ecx & (1 << 24)) != 0;
449     }
450
451     if(max_stdfn>=7)
452     {
453         execute_x86cpuid(0x7,0,&eax,&ebx,&ecx,&edx);
454         cpuid->feature[GMX_CPUID_FEATURE_X86_AVX2]    = (ebx & (1 << 5))  != 0;
455     }
456
457     /* Check whether Hyper-Threading is enabled, not only supported */
458     if(cpuid->feature[GMX_CPUID_FEATURE_X86_HTT] && max_stdfn>=4)
459     {
460         execute_x86cpuid(0x1,0,&eax,&ebx,&ecx,&edx);
461         max_logical_cores  = (ebx >> 16) & 0x0FF;
462         execute_x86cpuid(0x4,0,&eax,&ebx,&ecx,&edx);
463         max_physical_cores = ((eax >> 26) & 0x3F) + 1;
464
465         /* Clear HTT flag if we only have 1 logical core per physical */
466         if(max_logical_cores/max_physical_cores < 2)
467         {
468             cpuid->feature[GMX_CPUID_FEATURE_X86_HTT] = 0;
469         }
470     }
471     return 0;
472 }
473 #endif /* GMX_CPUID_X86 */
474
475
476
477 /* Try to find the vendor of the current CPU, so we know what specific
478  * detection routine to call.
479  */
480 static enum gmx_cpuid_vendor
481 cpuid_check_vendor(void)
482 {
483     enum gmx_cpuid_vendor      i,vendor;
484     /* Register data used on x86 */
485     unsigned int               eax,ebx,ecx,edx;
486     char                       vendorstring[13];
487
488     /* Set default first */
489     vendor = GMX_CPUID_VENDOR_UNKNOWN;
490
491 #ifdef GMX_CPUID_X86
492     execute_x86cpuid(0x0,0,&eax,&ebx,&ecx,&edx);
493
494     memcpy(vendorstring,&ebx,4);
495     memcpy(vendorstring+4,&edx,4);
496     memcpy(vendorstring+8,&ecx,4);
497
498     vendorstring[12]='\0';
499
500     for(i=GMX_CPUID_VENDOR_UNKNOWN;i<GMX_CPUID_NVENDORS;i++)
501     {
502         if(!strncmp(vendorstring,gmx_cpuid_vendor_string[i],12))
503         {
504             vendor = i;
505         }
506     }
507 #else
508     vendor = GMX_CPUID_VENDOR_UNKNOWN;
509 #endif
510     
511     return vendor;
512 }
513
514
515
516
517 int
518 gmx_cpuid_init               (gmx_cpuid_t *              pcpuid)
519 {
520     gmx_cpuid_t cpuid;
521     int i;
522
523     cpuid = malloc(sizeof(*cpuid));
524
525     *pcpuid = cpuid;
526
527     for(i=0;i<GMX_CPUID_NFEATURES;i++)
528     {
529         cpuid->feature[i]=0;
530     }
531
532     cpuid->vendor = cpuid_check_vendor();
533
534     switch(cpuid->vendor)
535     {
536 #ifdef GMX_CPUID_X86
537         case GMX_CPUID_VENDOR_INTEL:
538             cpuid_check_intel_x86(cpuid);
539             break;
540         case GMX_CPUID_VENDOR_AMD:
541             cpuid_check_amd_x86(cpuid);
542             break;
543 #endif
544         default:
545             /* Could not find vendor */
546             strncpy(cpuid->brand,"Unknown CPU brand",GMX_CPUID_BRAND_MAXLEN);
547             cpuid->family         = 0;
548             cpuid->model          = 0;
549             cpuid->stepping       = 0;
550
551             for(i=0;i<GMX_CPUID_NFEATURES;i++)
552             {
553                 cpuid->feature[i]=0;
554             }
555             cpuid->feature[GMX_CPUID_FEATURE_CANNOTDETECT] = 1;
556             break;
557     }
558
559     return 0;
560 }
561
562
563
564 void
565 gmx_cpuid_done               (gmx_cpuid_t              cpuid)
566 {
567     free(cpuid);
568 }
569
570
571 int
572 gmx_cpuid_formatstring       (gmx_cpuid_t              cpuid,
573                               char *                   str,
574                               int                      n)
575 {
576     int c;
577     int i;
578     enum gmx_cpuid_feature  feature;
579
580 #ifdef _MSC_VER
581     _snprintf(str,n,
582               "Vendor: %s\n"
583               "Brand:  %s\n"
584               "Family: %2d  Model: %2d  Stepping: %2d\n"
585               "Features:",
586               gmx_cpuid_vendor_string[gmx_cpuid_vendor(cpuid)],
587               gmx_cpuid_brand(cpuid),
588               gmx_cpuid_family(cpuid),gmx_cpuid_model(cpuid),gmx_cpuid_stepping(cpuid));
589 #else
590     snprintf(str,n,
591              "Vendor: %s\n"
592              "Brand:  %s\n"
593              "Family: %2d  Model: %2d  Stepping: %2d\n"
594              "Features:",
595              gmx_cpuid_vendor_string[gmx_cpuid_vendor(cpuid)],
596              gmx_cpuid_brand(cpuid),
597              gmx_cpuid_family(cpuid),gmx_cpuid_model(cpuid),gmx_cpuid_stepping(cpuid));
598 #endif
599
600     str[n-1] = '\0';
601     c = strlen(str);
602     n   -= c;
603     str += c;
604
605     for(feature=GMX_CPUID_FEATURE_CANNOTDETECT;feature<GMX_CPUID_NFEATURES;feature++)
606     {
607         if(gmx_cpuid_feature(cpuid,feature)==1)
608         {
609 #ifdef _MSC_VER
610             _snprintf(str,n," %s",gmx_cpuid_feature_string[feature]);
611 #else
612             snprintf(str,n," %s",gmx_cpuid_feature_string[feature]);
613 #endif
614             str[n-1] = '\0';
615             c = strlen(str);
616             n   -= c;
617             str += c;
618         }
619     }
620 #ifdef _MSC_VER
621     _snprintf(str,n,"\n");
622 #else
623     snprintf(str,n,"\n");
624 #endif
625     str[n-1] = '\0';
626
627     return 0;
628 }
629
630
631
632 enum gmx_cpuid_acceleration
633 gmx_cpuid_acceleration_suggest  (gmx_cpuid_t                 cpuid)
634 {
635     enum gmx_cpuid_acceleration  tmpacc;
636
637     tmpacc = GMX_CPUID_ACCELERATION_NONE;
638
639     if(gmx_cpuid_vendor(cpuid)==GMX_CPUID_VENDOR_INTEL)
640     {
641         if(gmx_cpuid_feature(cpuid,GMX_CPUID_FEATURE_X86_AVX))
642         {
643             tmpacc = GMX_CPUID_ACCELERATION_X86_AVX_256;
644         }
645         else if(gmx_cpuid_feature(cpuid,GMX_CPUID_FEATURE_X86_SSE4_1))
646         {
647             tmpacc = GMX_CPUID_ACCELERATION_X86_SSE4_1;
648         }
649         else if(gmx_cpuid_feature(cpuid,GMX_CPUID_FEATURE_X86_SSE2))
650         {
651             tmpacc = GMX_CPUID_ACCELERATION_X86_SSE2;
652         }
653     }
654     else if(gmx_cpuid_vendor(cpuid)==GMX_CPUID_VENDOR_AMD)
655     {
656         if(gmx_cpuid_feature(cpuid,GMX_CPUID_FEATURE_X86_AVX))
657         {
658             tmpacc = GMX_CPUID_ACCELERATION_X86_AVX_128_FMA;
659         }
660         else if(gmx_cpuid_feature(cpuid,GMX_CPUID_FEATURE_X86_SSE4_1))
661         {
662             tmpacc = GMX_CPUID_ACCELERATION_X86_SSE4_1;
663         }
664         else if(gmx_cpuid_feature(cpuid,GMX_CPUID_FEATURE_X86_SSE2))
665         {
666             tmpacc = GMX_CPUID_ACCELERATION_X86_SSE2;
667         }
668     }
669
670     return tmpacc;
671 }
672
673
674
675 int
676 gmx_cpuid_acceleration_check(gmx_cpuid_t   cpuid,
677                              FILE *        log)
678 {
679     int                           rc;
680     char                          str[1024];
681     enum gmx_cpuid_acceleration   acc;
682
683     acc = gmx_cpuid_acceleration_suggest(cpuid);
684
685     rc = (acc != compiled_acc);
686
687     gmx_cpuid_formatstring(cpuid,str,1023);
688     str[1023] = '\0';
689
690     if(log!=NULL)
691     {
692         fprintf(log,
693                 "\nDetecting CPU-specific acceleration.\nPresent hardware specification:\n"
694                 "%s"
695                 "Acceleration most likely to fit this hardware: %s\n"
696                 "Acceleration selected at GROMACS compile time: %s\n\n",
697                 str,
698                 gmx_cpuid_acceleration_string[acc],
699                 gmx_cpuid_acceleration_string[compiled_acc]);
700     }
701
702     if(rc!=0)
703     {
704         if(log!=NULL)
705         {
706         fprintf(log,"\nBinary not matching hardware - you might be losing performance.\n"
707                 "Acceleration most likely to fit this hardware: %s\n"
708                 "Acceleration selected at GROMACS compile time: %s\n\n",
709                 gmx_cpuid_acceleration_string[acc],
710                 gmx_cpuid_acceleration_string[compiled_acc]);
711         }
712         printf("Compiled acceleration: %s (Gromacs could use %s on this machine, which is better)\n",
713                gmx_cpuid_acceleration_string[compiled_acc],
714                gmx_cpuid_acceleration_string[acc]);
715     }
716     return rc;
717 }
718
719
720 enum gmx_cpuid_x86_smt
721 gmx_cpuid_x86_smt(gmx_cpuid_t cpuid)
722 {
723 #ifdef GMX_CPUID_X86
724 #if (defined HAVE_SCHED_H && defined HAVE_SCHED_SETAFFINITY && defined HAVE_SYSCONF && defined __linux__)
725     int            i;
726     int            nproc;
727     cpu_set_t      cpuset,save_cpuset;
728     int *          apic_id;
729     unsigned int   eax,ebx,ecx,edx;
730     int            core_shift_bits;
731     int            smt_found;
732
733     if( gmx_cpuid_vendor(cpuid)!=GMX_CPUID_VENDOR_INTEL ||
734        gmx_cpuid_feature(cpuid,GMX_CPUID_FEATURE_X86_HTT)==0)
735     {
736         return GMX_CPUID_X86_SMT_DISABLED;
737     }
738
739     /* Check cpuid max standard function */
740     execute_x86cpuid(0x0,0,&eax,&ebx,&ecx,&edx);
741
742     /* Early CPUs that do not support function 11 do not support SMT either */
743     if(eax<0xB)
744     {
745         return GMX_CPUID_X86_SMT_DISABLED;
746     }
747
748     /* If we got here, it is a modern Intel CPU that supports detection, as does our OS */
749
750     /* How many processors? */
751     nproc = sysconf(_SC_NPROCESSORS_ONLN);
752
753     apic_id      = malloc(sizeof(int)*nproc);
754
755     sched_getaffinity(0,sizeof(cpu_set_t),&save_cpuset);
756
757     /* Get x2APIC ID from each hardware thread */
758     CPU_ZERO(&cpuset);
759     for(i=0;i<nproc;i++)
760     {
761         CPU_SET(i,&cpuset);
762         sched_setaffinity(0,sizeof(cpu_set_t),&cpuset);
763         execute_x86cpuid(0xB,0,&eax,&ebx,&ecx,&edx);
764         apic_id[i]=edx;
765         CPU_CLR(i,&cpuset);
766     }
767     /* Reset affinity to the value it had when calling this routine */
768     sched_setaffinity(0,sizeof(cpu_set_t),&save_cpuset);
769
770     core_shift_bits = eax & 0x1F;
771
772     /* Check if there is any other APIC id that is identical to [0], apart from
773      * the hardware thread bit.
774      */
775     smt_found  = 0;
776     for(i=1;i<nproc && smt_found==0;i++)
777     {
778         smt_found = (apic_id[i]>>core_shift_bits == apic_id[0] >> core_shift_bits);
779     }
780
781     free(apic_id);
782
783     if(smt_found==1)
784     {
785         return GMX_CPUID_X86_SMT_ENABLED;
786     }
787     else
788     {
789         return GMX_CPUID_X86_SMT_DISABLED;
790     }
791 #else
792     /* Do the trivial stuff first. If Hyper-Threading isn't even supported it
793      * cannot be enabled, no matter what OS detection we use!
794      */
795     if(0==gmx_cpuid_feature(cpuid,GMX_CPUID_FEATURE_X86_HTT))
796     {
797         return GMX_CPUID_X86_SMT_DISABLED;
798     }
799     else
800     {
801         return GMX_CPUID_X86_SMT_CANNOTDETECT;
802     }
803 #endif
804 #else 
805     /* not x86 */
806     return GMX_CPUID_X86_SMT_CANNOTDETECT;
807 #endif
808 }
809
810
811
812
813 #ifdef GMX_CPUID_STANDALONE
814 /* Stand-alone program to enable queries of CPU features from Cmake.
815  * Note that you need to check inline ASM capabilities before compiling and set
816  * -DGMX_X86_GCC_INLINE_ASM for the cpuid instruction to work...
817  */
818 int
819 main(int argc, char **argv)
820 {
821     gmx_cpuid_t                   cpuid;
822     enum gmx_cpuid_acceleration   acc;
823     int                           i,cnt;
824
825     if(argc<2)
826     {
827         fprintf(stdout,
828                 "Usage:\n\n%s [flags]\n\n"
829                 "Available flags:\n"
830                 "-vendor        Print CPU vendor.\n"
831                 "-brand         Print CPU brand string.\n"
832                 "-family        Print CPU family version.\n"
833                 "-model         Print CPU model version.\n"
834                 "-stepping      Print CPU stepping version.\n"
835                 "-features      Print CPU feature flags.\n"
836                 "-acceleration  Print suggested GROMACS acceleration.\n"
837                 ,argv[0]);
838         exit(0);
839     }
840
841     gmx_cpuid_init(&cpuid);
842
843     if(!strncmp(argv[1],"-vendor",3))
844     {
845         printf("%s\n",gmx_cpuid_vendor_string[cpuid->vendor]);
846     }
847     else if(!strncmp(argv[1],"-brand",3))
848     {
849         printf("%s\n",cpuid->brand);
850     }
851     else if(!strncmp(argv[1],"-family",3))
852     {
853         printf("%d\n",cpuid->family);
854     }
855     else if(!strncmp(argv[1],"-model",3))
856     {
857         printf("%d\n",cpuid->model);
858     }
859     else if(!strncmp(argv[1],"-stepping",3))
860     {
861         printf("%d\n",cpuid->stepping);
862     }
863     else if(!strncmp(argv[1],"-features",3))
864     {
865         cnt = 0;
866         for(i=0;i<GMX_CPUID_NFEATURES;i++)
867         {
868             if(cpuid->feature[i]==1)
869             {
870                 if(cnt++ > 0)
871                 {
872                     printf(" ");
873                 }
874                 printf("%s",gmx_cpuid_feature_string[i]);
875             }
876         }
877         printf("\n");
878     }
879     else if(!strncmp(argv[1],"-acceleration",3))
880     {
881         acc = gmx_cpuid_acceleration_suggest(cpuid);
882         fprintf(stdout,"%s\n",gmx_cpuid_acceleration_string[acc]);
883     }
884
885     gmx_cpuid_done(cpuid);
886
887
888     return 0;
889 }
890
891 #endif