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