Merge branch release-4-6 into release-5-0
[alexxy/gromacs.git] / src / gromacs / gmxlib / gmx_cpuid.c
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012,2013,2014, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source 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 #  ifndef _GNU_SOURCE
41 #    define _GNU_SOURCE 1
42 #  endif
43 #  include <sched.h>
44 #endif
45
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <ctype.h>
50 #ifdef _MSC_VER
51 /* MSVC definition for __cpuid() */
52 #include <intrin.h>
53 /* sysinfo functions */
54 #include <windows.h>
55 #endif
56 #ifdef HAVE_UNISTD_H
57 /* sysconf() definition */
58 #include <unistd.h>
59 #endif
60
61 #include "gmx_cpuid.h"
62
63
64
65 /* For convenience, and to enable configure-time invocation, we keep all architectures
66  * in a single file, but to avoid repeated ifdefs we set the overall architecture here.
67  */
68 #ifdef GMX_TARGET_X86
69 /* OK, it is x86, but can we execute cpuid? */
70 #if defined(GMX_X86_GCC_INLINE_ASM) || ( defined(_MSC_VER) && ( (_MSC_VER > 1500) || (_MSC_VER == 1500 & _MSC_FULL_VER >= 150030729)))
71 #    define GMX_CPUID_X86
72 #endif
73 #endif
74
75 /* Global constant character strings corresponding to our enumerated types */
76 const char *
77 gmx_cpuid_vendor_string[GMX_CPUID_NVENDORS] =
78 {
79     "CannotDetect",
80     "Unknown",
81     "GenuineIntel",
82     "AuthenticAMD",
83     "Fujitsu",
84     "IBM"
85 };
86
87 const char *
88 gmx_cpuid_vendor_string_alternative[GMX_CPUID_NVENDORS] =
89 {
90     "CannotDetect",
91     "Unknown",
92     "GenuineIntel",
93     "AuthenticAMD",
94     "Fujitsu",
95     "ibm" /* Used on BlueGene/Q */
96 };
97
98 const char *
99 gmx_cpuid_feature_string[GMX_CPUID_NFEATURES] =
100 {
101     "CannotDetect",
102     "aes",
103     "apic",
104     "avx",
105     "avx2",
106     "clfsh",
107     "cmov",
108     "cx8",
109     "cx16",
110     "f16c",
111     "fma",
112     "fma4",
113     "htt",
114     "lahf_lm",
115     "misalignsse",
116     "mmx",
117     "msr",
118     "nonstop_tsc",
119     "pcid",
120     "pclmuldq",
121     "pdcm",
122     "pdpe1gb",
123     "popcnt",
124     "pse",
125     "rdrnd",
126     "rdtscp",
127     "sse2",
128     "sse3",
129     "sse4a",
130     "sse4.1",
131     "sse4.2",
132     "ssse3",
133     "tdt",
134     "x2apic",
135     "xop"
136 };
137
138 const char *
139 gmx_cpuid_simd_string[GMX_CPUID_NSIMD] =
140 {
141     "CannotDetect",
142     "None",
143     "Reference",
144     "SSE2",
145     "SSE4.1",
146     "AVX_128_FMA",
147     "AVX_256",
148     "AVX2_256",
149     "Sparc64 HPC-ACE",
150     "IBM_QPX"
151 };
152
153 /* Max length of brand string */
154 #define GMX_CPUID_BRAND_MAXLEN 256
155
156
157 /* Contents of the abstract datatype */
158 struct gmx_cpuid
159 {
160     enum gmx_cpuid_vendor      vendor;
161     char                       brand[GMX_CPUID_BRAND_MAXLEN];
162     int                        family;
163     int                        model;
164     int                        stepping;
165     /* Not using gmx_bool here, since this file must be possible to compile without simple.h */
166     char                       feature[GMX_CPUID_NFEATURES];
167
168     /* Basic CPU topology information. For x86 this is a bit complicated since the topology differs between
169      * operating systems and sometimes even settings. For most other architectures you can likely just check
170      * the documentation and then write static information to these arrays rather than detecting on-the-fly.
171      */
172     int                        have_cpu_topology;
173     int                        nproc;               /* total number of logical processors from OS */
174     int                        npackages;
175     int                        ncores_per_package;
176     int                        nhwthreads_per_core;
177     int *                      package_id;
178     int *                      core_id;             /* Local core id in each package */
179     int *                      hwthread_id;         /* Local hwthread id in each core */
180     int *                      locality_order;      /* Processor indices sorted in locality order */
181 };
182
183
184 /* Simple routines to access the data structure. The initialization routine is
185  * further down since that needs to call other static routines in this file.
186  */
187 enum gmx_cpuid_vendor
188 gmx_cpuid_vendor            (gmx_cpuid_t                cpuid)
189 {
190     return cpuid->vendor;
191 }
192
193
194 const char *
195 gmx_cpuid_brand             (gmx_cpuid_t                cpuid)
196 {
197     return cpuid->brand;
198 }
199
200 int
201 gmx_cpuid_family            (gmx_cpuid_t                cpuid)
202 {
203     return cpuid->family;
204 }
205
206 int
207 gmx_cpuid_model             (gmx_cpuid_t                cpuid)
208 {
209     return cpuid->model;
210 }
211
212 int
213 gmx_cpuid_stepping          (gmx_cpuid_t                cpuid)
214 {
215     return cpuid->stepping;
216 }
217
218 int
219 gmx_cpuid_feature           (gmx_cpuid_t                cpuid,
220                              enum gmx_cpuid_feature     feature)
221 {
222     return (cpuid->feature[feature] != 0);
223 }
224
225
226
227
228 /* What type of SIMD was compiled in, if any? */
229 #ifdef GMX_SIMD_X86_AVX2_256
230 static const enum gmx_cpuid_simd compiled_simd = GMX_CPUID_SIMD_X86_AVX2_256;
231 #elif defined GMX_SIMD_X86_AVX_256
232 static const enum gmx_cpuid_simd compiled_simd = GMX_CPUID_SIMD_X86_AVX_256;
233 #elif defined GMX_SIMD_X86_AVX_128_FMA
234 static const enum gmx_cpuid_simd compiled_simd = GMX_CPUID_SIMD_X86_AVX_128_FMA;
235 #elif defined GMX_SIMD_X86_SSE4_1
236 static const enum gmx_cpuid_simd compiled_simd = GMX_CPUID_SIMD_X86_SSE4_1;
237 #elif defined GMX_SIMD_X86_SSE2
238 static const enum gmx_cpuid_simd compiled_simd = GMX_CPUID_SIMD_X86_SSE2;
239 #elif defined GMX_SIMD_SPARC64_HPC_ACE
240 static const enum gmx_cpuid_simd compiled_simd = GMX_CPUID_SIMD_SPARC64_HPC_ACE;
241 #elif defined GMX_SIMD_IBM_QPX
242 static const enum gmx_cpuid_simd compiled_simd = GMX_CPUID_SIMD_IBM_QPX;
243 #elif defined GMX_SIMD_REFERENCE
244 static const enum gmx_cpuid_simd compiled_simd = GMX_CPUID_SIMD_REFERENCE;
245 #else
246 static const enum gmx_cpuid_simd compiled_simd = GMX_CPUID_SIMD_NONE;
247 #endif
248
249
250 #ifdef GMX_CPUID_X86
251
252 /* Execute CPUID on x86 class CPUs. level sets function to exec, and the
253  * contents of register output is returned. See Intel/AMD docs for details.
254  *
255  * This version supports extended information where we can also have an input
256  * value in the ecx register. This is ignored for most levels, but some of them
257  * (e.g. level 0xB on Intel) use it.
258  */
259 static int
260 execute_x86cpuid(unsigned int   level,
261                  unsigned int   ecxval,
262                  unsigned int * eax,
263                  unsigned int * ebx,
264                  unsigned int * ecx,
265                  unsigned int * edx)
266 {
267     int rc = 0;
268
269     /* Currently CPUID is only supported (1) if we can use an instruction on MSVC, or (2)
270      * if the compiler handles GNU-style inline assembly.
271      */
272
273 #if (defined _MSC_VER)
274     int CPUInfo[4];
275
276 #if (_MSC_VER > 1500) || (_MSC_VER == 1500 & _MSC_FULL_VER >= 150030729)
277     /* MSVC 9.0 SP1 or later */
278     __cpuidex(CPUInfo, level, ecxval);
279     rc = 0;
280 #else
281     __cpuid(CPUInfo, level);
282     /* Set an error code if the user wanted a non-zero ecxval, since we did not have cpuidex */
283     rc = (ecxval > 0) ? -1 : 0;
284 #endif
285     *eax = CPUInfo[0];
286     *ebx = CPUInfo[1];
287     *ecx = CPUInfo[2];
288     *edx = CPUInfo[3];
289
290 #elif (defined GMX_X86_GCC_INLINE_ASM)
291     /* for now this means GMX_X86_GCC_INLINE_ASM should be defined,
292      * but there might be more options added in the future.
293      */
294     *eax = level;
295     *ecx = ecxval;
296     *ebx = 0;
297     *edx = 0;
298 #if defined(__i386__) && defined(__PIC__)
299     /* Avoid clobbering the global offset table in 32-bit pic code (ebx register) */
300     __asm__ __volatile__ ("xchgl %%ebx, %1  \n\t"
301                           "cpuid            \n\t"
302                           "xchgl %%ebx, %1  \n\t"
303                           : "+a" (*eax), "+r" (*ebx), "+c" (*ecx), "+d" (*edx));
304 #else
305     /* i386 without PIC, or x86-64. Things are easy and we can clobber any reg we want :-) */
306     __asm__ __volatile__ ("cpuid            \n\t"
307                           : "+a" (*eax), "+b" (*ebx), "+c" (*ecx), "+d" (*edx));
308 #endif
309     rc = 0;
310 #else
311     /* Death and horror!
312      * Apparently this is an x86 platform where we don't know how to call cpuid.
313      *
314      * This is REALLY bad, since we will lose all Gromacs SIMD support.
315      */
316     *eax = 0;
317     *ebx = 0;
318     *ecx = 0;
319     *edx = 0;
320
321     rc = -1;
322 #endif
323     return rc;
324 }
325
326
327 /* Identify CPU features common to Intel & AMD - mainly brand string,
328  * version and some features. Vendor has already been detected outside this.
329  */
330 static int
331 cpuid_check_common_x86(gmx_cpuid_t                cpuid)
332 {
333     int                       fn, max_stdfn, max_extfn;
334     unsigned int              eax, ebx, ecx, edx;
335     char                      str[GMX_CPUID_BRAND_MAXLEN];
336     char *                    p;
337
338     /* Find largest standard/extended function input value */
339     execute_x86cpuid(0x0, 0, &eax, &ebx, &ecx, &edx);
340     max_stdfn = eax;
341     execute_x86cpuid(0x80000000, 0, &eax, &ebx, &ecx, &edx);
342     max_extfn = eax;
343
344     p = str;
345     if (max_extfn >= 0x80000005)
346     {
347         /* Get CPU brand string */
348         for (fn = 0x80000002; fn < 0x80000005; fn++)
349         {
350             execute_x86cpuid(fn, 0, &eax, &ebx, &ecx, &edx);
351             memcpy(p, &eax, 4);
352             memcpy(p+4, &ebx, 4);
353             memcpy(p+8, &ecx, 4);
354             memcpy(p+12, &edx, 4);
355             p += 16;
356         }
357         *p = '\0';
358
359         /* Remove empty initial space */
360         p = str;
361         while (isspace(*(p)))
362         {
363             p++;
364         }
365         strncpy(cpuid->brand, p, GMX_CPUID_BRAND_MAXLEN);
366     }
367     else
368     {
369         strncpy(cpuid->brand, "Unknown CPU brand", GMX_CPUID_BRAND_MAXLEN);
370     }
371
372     /* Find basic CPU properties */
373     if (max_stdfn >= 1)
374     {
375         execute_x86cpuid(0x1, 0, &eax, &ebx, &ecx, &edx);
376
377         cpuid->family   = ((eax & 0x0FF00000) >> 20) + ((eax & 0x00000F00) >> 8);
378         /* Note that extended model should be shifted left 4, so only shift right 12 iso 16. */
379         cpuid->model    = ((eax & 0x000F0000) >> 12) + ((eax & 0x000000F0) >> 4);
380         cpuid->stepping = (eax & 0x0000000F);
381
382         /* Feature flags common to AMD and intel */
383         cpuid->feature[GMX_CPUID_FEATURE_X86_SSE3]     = (ecx & (1 << 0))  != 0;
384         cpuid->feature[GMX_CPUID_FEATURE_X86_PCLMULDQ] = (ecx & (1 << 1))  != 0;
385         cpuid->feature[GMX_CPUID_FEATURE_X86_SSSE3]    = (ecx & (1 << 9))  != 0;
386         cpuid->feature[GMX_CPUID_FEATURE_X86_FMA]      = (ecx & (1 << 12)) != 0;
387         cpuid->feature[GMX_CPUID_FEATURE_X86_CX16]     = (ecx & (1 << 13)) != 0;
388         cpuid->feature[GMX_CPUID_FEATURE_X86_SSE4_1]   = (ecx & (1 << 19)) != 0;
389         cpuid->feature[GMX_CPUID_FEATURE_X86_SSE4_2]   = (ecx & (1 << 20)) != 0;
390         cpuid->feature[GMX_CPUID_FEATURE_X86_POPCNT]   = (ecx & (1 << 23)) != 0;
391         cpuid->feature[GMX_CPUID_FEATURE_X86_AES]      = (ecx & (1 << 25)) != 0;
392         cpuid->feature[GMX_CPUID_FEATURE_X86_AVX]      = (ecx & (1 << 28)) != 0;
393         cpuid->feature[GMX_CPUID_FEATURE_X86_F16C]     = (ecx & (1 << 29)) != 0;
394         cpuid->feature[GMX_CPUID_FEATURE_X86_RDRND]    = (ecx & (1 << 30)) != 0;
395
396         cpuid->feature[GMX_CPUID_FEATURE_X86_PSE]      = (edx & (1 << 3))  != 0;
397         cpuid->feature[GMX_CPUID_FEATURE_X86_MSR]      = (edx & (1 << 5))  != 0;
398         cpuid->feature[GMX_CPUID_FEATURE_X86_CX8]      = (edx & (1 << 8))  != 0;
399         cpuid->feature[GMX_CPUID_FEATURE_X86_APIC]     = (edx & (1 << 9))  != 0;
400         cpuid->feature[GMX_CPUID_FEATURE_X86_CMOV]     = (edx & (1 << 15)) != 0;
401         cpuid->feature[GMX_CPUID_FEATURE_X86_CLFSH]    = (edx & (1 << 19)) != 0;
402         cpuid->feature[GMX_CPUID_FEATURE_X86_MMX]      = (edx & (1 << 23)) != 0;
403         cpuid->feature[GMX_CPUID_FEATURE_X86_SSE2]     = (edx & (1 << 26)) != 0;
404         cpuid->feature[GMX_CPUID_FEATURE_X86_HTT]      = (edx & (1 << 28)) != 0;
405     }
406     else
407     {
408         cpuid->family   = -1;
409         cpuid->model    = -1;
410         cpuid->stepping = -1;
411     }
412
413     if (max_extfn >= 0x80000001)
414     {
415         execute_x86cpuid(0x80000001, 0, &eax, &ebx, &ecx, &edx);
416         cpuid->feature[GMX_CPUID_FEATURE_X86_LAHF_LM] = (ecx & (1 << 0))  != 0;
417         cpuid->feature[GMX_CPUID_FEATURE_X86_PDPE1GB] = (edx & (1 << 26)) != 0;
418         cpuid->feature[GMX_CPUID_FEATURE_X86_RDTSCP]  = (edx & (1 << 27)) != 0;
419     }
420
421     if (max_extfn >= 0x80000007)
422     {
423         execute_x86cpuid(0x80000007, 0, &eax, &ebx, &ecx, &edx);
424         cpuid->feature[GMX_CPUID_FEATURE_X86_NONSTOP_TSC]  = (edx & (1 << 8))  != 0;
425     }
426     return 0;
427 }
428
429 /* This routine returns the number of unique different elements found in the array,
430  * and renumbers these starting from 0. For example, the array {0,1,2,8,9,10,8,9,10,0,1,2}
431  * will be rewritten to {0,1,2,3,4,5,3,4,5,0,1,2}, and it returns 6 for the
432  * number of unique elements.
433  */
434 static int
435 cpuid_renumber_elements(int *data, int n)
436 {
437     int *unique;
438     int  i, j, nunique, found;
439
440     unique = malloc(sizeof(int)*n);
441
442     nunique = 0;
443     for (i = 0; i < n; i++)
444     {
445         for (j = 0, found = 0; j < nunique && !found; j++)
446         {
447             found = (data[i] == unique[j]);
448         }
449         if (!found)
450         {
451             /* Insert in sorted order! */
452             for (j = nunique++; j > 0 && unique[j-1] > data[i]; j--)
453             {
454                 unique[j] = unique[j-1];
455             }
456             unique[j] = data[i];
457         }
458     }
459     /* renumber */
460     for (i = 0; i < n; i++)
461     {
462         for (j = 0; j < nunique; j++)
463         {
464             if (data[i] == unique[j])
465             {
466                 data[i] = j;
467             }
468         }
469     }
470     return nunique;
471 }
472
473 /* APIC IDs, or everything you wanted to know about your x86 cores but were afraid to ask...
474  *
475  * Raw APIC IDs are unfortunately somewhat dirty. For technical reasons they are assigned
476  * in power-of-2 chunks, and even then there are no guarantees about specific numbers - all
477  * we know is that the part for each thread/core/package is unique, and how many bits are
478  * reserved for that part.
479  * This routine does internal renumbering so we get continuous indices, and also
480  * decodes the actual number of packages,cores-per-package and hwthreads-per-core.
481  * Returns: 0 on success, non-zero on failure.
482  */
483 static int
484 cpuid_x86_decode_apic_id(gmx_cpuid_t cpuid, int *apic_id, int core_bits, int hwthread_bits)
485 {
486     int i, idx;
487     int hwthread_mask, core_mask_after_shift;
488
489     cpuid->hwthread_id     = malloc(sizeof(int)*cpuid->nproc);
490     cpuid->core_id         = malloc(sizeof(int)*cpuid->nproc);
491     cpuid->package_id      = malloc(sizeof(int)*cpuid->nproc);
492     cpuid->locality_order  = malloc(sizeof(int)*cpuid->nproc);
493
494     hwthread_mask         = (1 << hwthread_bits) - 1;
495     core_mask_after_shift = (1 << core_bits) - 1;
496
497     for (i = 0; i < cpuid->nproc; i++)
498     {
499         cpuid->hwthread_id[i] = apic_id[i] & hwthread_mask;
500         cpuid->core_id[i]     = (apic_id[i] >> hwthread_bits) & core_mask_after_shift;
501         cpuid->package_id[i]  = apic_id[i] >> (core_bits + hwthread_bits);
502     }
503
504     cpuid->npackages            = cpuid_renumber_elements(cpuid->package_id, cpuid->nproc);
505     cpuid->ncores_per_package   = cpuid_renumber_elements(cpuid->core_id, cpuid->nproc);
506     cpuid->nhwthreads_per_core  = cpuid_renumber_elements(cpuid->hwthread_id, cpuid->nproc);
507
508     /* now check for consistency */
509     if ( (cpuid->npackages * cpuid->ncores_per_package *
510           cpuid->nhwthreads_per_core) != cpuid->nproc)
511     {
512         /* the packages/cores-per-package/hwthreads-per-core counts are
513            inconsistent. */
514         return -1;
515     }
516
517     /* Create a locality order array, i.e. first all resources in package0, which in turn
518      * are sorted so we first have all resources in core0, where threads are sorted in order, etc.
519      */
520
521     for (i = 0; i < cpuid->nproc; i++)
522     {
523         idx = (cpuid->package_id[i]*cpuid->ncores_per_package + cpuid->core_id[i])*cpuid->nhwthreads_per_core + cpuid->hwthread_id[i];
524         cpuid->locality_order[idx] = i;
525     }
526     return 0;
527 }
528
529
530 /* Detection of AMD-specific CPU features */
531 static int
532 cpuid_check_amd_x86(gmx_cpuid_t                cpuid)
533 {
534     int                       max_stdfn, max_extfn, ret;
535     unsigned int              eax, ebx, ecx, edx;
536     int                       hwthread_bits, core_bits;
537     int *                     apic_id;
538
539     cpuid_check_common_x86(cpuid);
540
541     execute_x86cpuid(0x0, 0, &eax, &ebx, &ecx, &edx);
542     max_stdfn = eax;
543
544     execute_x86cpuid(0x80000000, 0, &eax, &ebx, &ecx, &edx);
545     max_extfn = eax;
546
547     if (max_extfn >= 0x80000001)
548     {
549         execute_x86cpuid(0x80000001, 0, &eax, &ebx, &ecx, &edx);
550
551         cpuid->feature[GMX_CPUID_FEATURE_X86_SSE4A]       = (ecx & (1 << 6))  != 0;
552         cpuid->feature[GMX_CPUID_FEATURE_X86_MISALIGNSSE] = (ecx & (1 << 7))  != 0;
553         cpuid->feature[GMX_CPUID_FEATURE_X86_XOP]         = (ecx & (1 << 11)) != 0;
554         cpuid->feature[GMX_CPUID_FEATURE_X86_FMA4]        = (ecx & (1 << 16)) != 0;
555     }
556
557     /* Query APIC information on AMD */
558     if (max_extfn >= 0x80000008)
559     {
560 #if (defined HAVE_SCHED_AFFINITY && defined HAVE_SYSCONF && defined __linux__)
561         /* Linux */
562         unsigned int   i;
563         cpu_set_t      cpuset, save_cpuset;
564         cpuid->nproc = sysconf(_SC_NPROCESSORS_ONLN);
565         apic_id      = malloc(sizeof(int)*cpuid->nproc);
566         sched_getaffinity(0, sizeof(cpu_set_t), &save_cpuset);
567         /* Get APIC id from each core */
568         CPU_ZERO(&cpuset);
569         for (i = 0; i < cpuid->nproc; i++)
570         {
571             CPU_SET(i, &cpuset);
572             sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);
573             execute_x86cpuid(0x1, 0, &eax, &ebx, &ecx, &edx);
574             apic_id[i] = ebx >> 24;
575             CPU_CLR(i, &cpuset);
576         }
577         /* Reset affinity to the value it had when calling this routine */
578         sched_setaffinity(0, sizeof(cpu_set_t), &save_cpuset);
579 #define CPUID_HAVE_APIC
580 #elif defined GMX_NATIVE_WINDOWS
581         /* Windows */
582         DWORD_PTR     i;
583         SYSTEM_INFO   sysinfo;
584         unsigned int  save_affinity, affinity;
585         GetSystemInfo( &sysinfo );
586         cpuid->nproc  = sysinfo.dwNumberOfProcessors;
587         apic_id       = malloc(sizeof(int)*cpuid->nproc);
588         /* Get previous affinity mask */
589         save_affinity = SetThreadAffinityMask(GetCurrentThread(), 1);
590         for (i = 0; i < cpuid->nproc; i++)
591         {
592             SetThreadAffinityMask(GetCurrentThread(), (((DWORD_PTR)1)<<i));
593             Sleep(0);
594             execute_x86cpuid(0x1, 0, &eax, &ebx, &ecx, &edx);
595             apic_id[i] = ebx >> 24;
596         }
597         SetThreadAffinityMask(GetCurrentThread(), save_affinity);
598 #define CPUID_HAVE_APIC
599 #endif
600 #ifdef CPUID_HAVE_APIC
601         /* AMD does not support SMT yet - there are no hwthread bits in apic ID */
602         hwthread_bits = 0;
603         /* Get number of core bits in apic ID - try modern extended method first */
604         execute_x86cpuid(0x80000008, 0, &eax, &ebx, &ecx, &edx);
605         core_bits = (ecx >> 12) & 0xf;
606         if (core_bits == 0)
607         {
608             /* Legacy method for old single/dual core AMD CPUs */
609             int i = ecx & 0xF;
610             for (core_bits = 0; (i>>core_bits) > 0; core_bits++)
611             {
612                 ;
613             }
614         }
615         ret = cpuid_x86_decode_apic_id(cpuid, apic_id, core_bits,
616                                        hwthread_bits);
617         cpuid->have_cpu_topology = (ret == 0);
618 #endif
619     }
620     return 0;
621 }
622
623 /* Detection of Intel-specific CPU features */
624 static int
625 cpuid_check_intel_x86(gmx_cpuid_t                cpuid)
626 {
627     unsigned int              max_stdfn, max_extfn, ret;
628     unsigned int              eax, ebx, ecx, edx;
629     unsigned int              max_logical_cores, max_physical_cores;
630     int                       hwthread_bits, core_bits;
631     int *                     apic_id;
632
633     cpuid_check_common_x86(cpuid);
634
635     execute_x86cpuid(0x0, 0, &eax, &ebx, &ecx, &edx);
636     max_stdfn = eax;
637
638     execute_x86cpuid(0x80000000, 0, &eax, &ebx, &ecx, &edx);
639     max_extfn = eax;
640
641     if (max_stdfn >= 1)
642     {
643         execute_x86cpuid(0x1, 0, &eax, &ebx, &ecx, &edx);
644         cpuid->feature[GMX_CPUID_FEATURE_X86_PDCM]    = (ecx & (1 << 15)) != 0;
645         cpuid->feature[GMX_CPUID_FEATURE_X86_PCID]    = (ecx & (1 << 17)) != 0;
646         cpuid->feature[GMX_CPUID_FEATURE_X86_X2APIC]  = (ecx & (1 << 21)) != 0;
647         cpuid->feature[GMX_CPUID_FEATURE_X86_TDT]     = (ecx & (1 << 24)) != 0;
648     }
649
650     if (max_stdfn >= 7)
651     {
652         execute_x86cpuid(0x7, 0, &eax, &ebx, &ecx, &edx);
653         cpuid->feature[GMX_CPUID_FEATURE_X86_AVX2]    = (ebx & (1 << 5))  != 0;
654     }
655
656     /* Check whether Hyper-Threading is enabled, not only supported */
657     if (cpuid->feature[GMX_CPUID_FEATURE_X86_HTT] && max_stdfn >= 4)
658     {
659         execute_x86cpuid(0x1, 0, &eax, &ebx, &ecx, &edx);
660         max_logical_cores  = (ebx >> 16) & 0x0FF;
661         execute_x86cpuid(0x4, 0, &eax, &ebx, &ecx, &edx);
662         max_physical_cores = ((eax >> 26) & 0x3F) + 1;
663
664         /* Clear HTT flag if we only have 1 logical core per physical */
665         if (max_logical_cores/max_physical_cores < 2)
666         {
667             cpuid->feature[GMX_CPUID_FEATURE_X86_HTT] = 0;
668         }
669     }
670
671     if (max_stdfn >= 0xB)
672     {
673         /* Query x2 APIC information from cores */
674 #if (defined HAVE_SCHED_AFFINITY && defined HAVE_SYSCONF && defined __linux__)
675         /* Linux */
676         unsigned int   i;
677         cpu_set_t      cpuset, save_cpuset;
678         cpuid->nproc = sysconf(_SC_NPROCESSORS_ONLN);
679         apic_id      = malloc(sizeof(int)*cpuid->nproc);
680         sched_getaffinity(0, sizeof(cpu_set_t), &save_cpuset);
681         /* Get x2APIC ID from each hardware thread */
682         CPU_ZERO(&cpuset);
683         for (i = 0; i < cpuid->nproc; i++)
684         {
685             CPU_SET(i, &cpuset);
686             sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);
687             execute_x86cpuid(0xB, 0, &eax, &ebx, &ecx, &edx);
688             apic_id[i] = edx;
689             CPU_CLR(i, &cpuset);
690         }
691         /* Reset affinity to the value it had when calling this routine */
692         sched_setaffinity(0, sizeof(cpu_set_t), &save_cpuset);
693 #define CPUID_HAVE_APIC
694 #elif defined GMX_NATIVE_WINDOWS
695         /* Windows */
696         DWORD_PTR     i;
697         SYSTEM_INFO   sysinfo;
698         unsigned int  save_affinity, affinity;
699         GetSystemInfo( &sysinfo );
700         cpuid->nproc  = sysinfo.dwNumberOfProcessors;
701         apic_id       = malloc(sizeof(int)*cpuid->nproc);
702         /* Get previous affinity mask */
703         save_affinity = SetThreadAffinityMask(GetCurrentThread(), 1);
704         for (i = 0; i < cpuid->nproc; i++)
705         {
706             SetThreadAffinityMask(GetCurrentThread(), (((DWORD_PTR)1)<<i));
707             Sleep(0);
708             execute_x86cpuid(0xB, 0, &eax, &ebx, &ecx, &edx);
709             apic_id[i] = edx;
710         }
711         SetThreadAffinityMask(GetCurrentThread(), save_affinity);
712 #define CPUID_HAVE_APIC
713 #endif
714 #ifdef CPUID_HAVE_APIC
715         execute_x86cpuid(0xB, 0, &eax, &ebx, &ecx, &edx);
716         hwthread_bits    = eax & 0x1F;
717         execute_x86cpuid(0xB, 1, &eax, &ebx, &ecx, &edx);
718         core_bits        = (eax & 0x1F) - hwthread_bits;
719         ret              = cpuid_x86_decode_apic_id(cpuid, apic_id, core_bits,
720                                                     hwthread_bits);
721         cpuid->have_cpu_topology = (ret == 0);
722 #endif
723     }
724     return 0;
725 }
726 #endif /* GMX_CPUID_X86 */
727
728
729
730
731 static void
732 chomp_substring_before_colon(const char *in, char *s, int maxlength)
733 {
734     char *p;
735     strncpy(s, in, maxlength);
736     p = strchr(s, ':');
737     if (p != NULL)
738     {
739         *p = '\0';
740         while (isspace(*(--p)) && (p >= s))
741         {
742             *p = '\0';
743         }
744     }
745     else
746     {
747         *s = '\0';
748     }
749 }
750
751 static void
752 chomp_substring_after_colon(const char *in, char *s, int maxlength)
753 {
754     char *p;
755     if ( (p = strchr(in, ':')) != NULL)
756     {
757         p++;
758         while (isspace(*p))
759         {
760             p++;
761         }
762         strncpy(s, p, maxlength);
763         p = s+strlen(s);
764         while (isspace(*(--p)) && (p >= s))
765         {
766             *p = '\0';
767         }
768     }
769     else
770     {
771         *s = '\0';
772     }
773 }
774
775 /* Try to find the vendor of the current CPU, so we know what specific
776  * detection routine to call.
777  */
778 static enum gmx_cpuid_vendor
779 cpuid_check_vendor(void)
780 {
781     enum gmx_cpuid_vendor      i, vendor;
782     /* Register data used on x86 */
783     unsigned int               eax, ebx, ecx, edx;
784     char                       vendorstring[13];
785     FILE *                     fp;
786     char                       buffer[255], before_colon[255], after_colon[255];
787
788     /* Set default first */
789     vendor = GMX_CPUID_VENDOR_UNKNOWN;
790
791 #ifdef GMX_CPUID_X86
792     execute_x86cpuid(0x0, 0, &eax, &ebx, &ecx, &edx);
793
794     memcpy(vendorstring, &ebx, 4);
795     memcpy(vendorstring+4, &edx, 4);
796     memcpy(vendorstring+8, &ecx, 4);
797
798     vendorstring[12] = '\0';
799
800     for (i = GMX_CPUID_VENDOR_UNKNOWN; i < GMX_CPUID_NVENDORS; i++)
801     {
802         if (!strncmp(vendorstring, gmx_cpuid_vendor_string[i], 12))
803         {
804             vendor = i;
805         }
806     }
807 #elif defined(__linux__) || defined(__linux)
808     /* General Linux. Try to get CPU vendor from /proc/cpuinfo */
809     if ( (fp = fopen("/proc/cpuinfo", "r")) != NULL)
810     {
811         while ( (vendor == GMX_CPUID_VENDOR_UNKNOWN) && (fgets(buffer, sizeof(buffer), fp) != NULL))
812         {
813             chomp_substring_before_colon(buffer, before_colon, sizeof(before_colon));
814             /* Intel/AMD use "vendor_id", IBM "vendor"(?) or "model". Fujitsu "manufacture". Add others if you have them! */
815             if (!strcmp(before_colon, "vendor_id")
816                 || !strcmp(before_colon, "vendor")
817                 || !strcmp(before_colon, "manufacture")
818                 || !strcmp(before_colon, "model"))
819             {
820                 chomp_substring_after_colon(buffer, after_colon, sizeof(after_colon));
821                 for (i = GMX_CPUID_VENDOR_UNKNOWN; i < GMX_CPUID_NVENDORS; i++)
822                 {
823                     /* Be liberal and accept if we find the vendor
824                      * string (or alternative string) anywhere. Using
825                      * strcasestr() would be non-portable. */
826                     if (strstr(after_colon, gmx_cpuid_vendor_string[i])
827                         || strstr(after_colon, gmx_cpuid_vendor_string_alternative[i]))
828                     {
829                         vendor = i;
830                     }
831                 }
832             }
833         }
834     }
835     fclose(fp);
836 #endif
837
838     return vendor;
839 }
840
841
842
843 int
844 gmx_cpuid_topology(gmx_cpuid_t        cpuid,
845                    int *              nprocessors,
846                    int *              npackages,
847                    int *              ncores_per_package,
848                    int *              nhwthreads_per_core,
849                    const int **       package_id,
850                    const int **       core_id,
851                    const int **       hwthread_id,
852                    const int **       locality_order)
853 {
854     int rc;
855
856     if (cpuid->have_cpu_topology)
857     {
858         *nprocessors          = cpuid->nproc;
859         *npackages            = cpuid->npackages;
860         *ncores_per_package   = cpuid->ncores_per_package;
861         *nhwthreads_per_core  = cpuid->nhwthreads_per_core;
862         *package_id           = cpuid->package_id;
863         *core_id              = cpuid->core_id;
864         *hwthread_id          = cpuid->hwthread_id;
865         *locality_order       = cpuid->locality_order;
866         rc                    = 0;
867     }
868     else
869     {
870         rc = -1;
871     }
872     return rc;
873 }
874
875
876 enum gmx_cpuid_x86_smt
877 gmx_cpuid_x86_smt(gmx_cpuid_t cpuid)
878 {
879     enum gmx_cpuid_x86_smt rc;
880
881     if (cpuid->have_cpu_topology)
882     {
883         rc = (cpuid->nhwthreads_per_core > 1) ? GMX_CPUID_X86_SMT_ENABLED : GMX_CPUID_X86_SMT_DISABLED;
884     }
885     else if (cpuid->vendor == GMX_CPUID_VENDOR_AMD || gmx_cpuid_feature(cpuid, GMX_CPUID_FEATURE_X86_HTT) == 0)
886     {
887         rc = GMX_CPUID_X86_SMT_DISABLED;
888     }
889     else
890     {
891         rc = GMX_CPUID_X86_SMT_CANNOTDETECT;
892     }
893     return rc;
894 }
895
896
897 int
898 gmx_cpuid_init               (gmx_cpuid_t *              pcpuid)
899 {
900     gmx_cpuid_t cpuid;
901     int         i;
902     FILE *      fp;
903     char        buffer[255], buffer2[255];
904     int         found_brand;
905
906     cpuid = malloc(sizeof(*cpuid));
907
908     *pcpuid = cpuid;
909
910     for (i = 0; i < GMX_CPUID_NFEATURES; i++)
911     {
912         cpuid->feature[i] = 0;
913     }
914
915     cpuid->have_cpu_topology   = 0;
916     cpuid->nproc               = 0;
917     cpuid->npackages           = 0;
918     cpuid->ncores_per_package  = 0;
919     cpuid->nhwthreads_per_core = 0;
920     cpuid->package_id          = NULL;
921     cpuid->core_id             = NULL;
922     cpuid->hwthread_id         = NULL;
923     cpuid->locality_order      = NULL;
924
925     cpuid->vendor = cpuid_check_vendor();
926
927     switch (cpuid->vendor)
928     {
929 #ifdef GMX_CPUID_X86
930         case GMX_CPUID_VENDOR_INTEL:
931             cpuid_check_intel_x86(cpuid);
932             break;
933         case GMX_CPUID_VENDOR_AMD:
934             cpuid_check_amd_x86(cpuid);
935             break;
936 #endif
937         default:
938             /* Default value */
939             strncpy(cpuid->brand, "Unknown CPU brand", GMX_CPUID_BRAND_MAXLEN);
940 #if defined(__linux__) || defined(__linux)
941             /* General Linux. Try to get CPU type from /proc/cpuinfo */
942             if ( (fp = fopen("/proc/cpuinfo", "r")) != NULL)
943             {
944                 found_brand = 0;
945                 while ( (found_brand == 0) && (fgets(buffer, sizeof(buffer), fp) != NULL))
946                 {
947                     chomp_substring_before_colon(buffer, buffer2, sizeof(buffer2));
948                     /* Intel uses "model name", Fujitsu and IBM "cpu". */
949                     if (!strcmp(buffer2, "model name") || !strcmp(buffer2, "cpu"))
950                     {
951                         chomp_substring_after_colon(buffer, cpuid->brand, GMX_CPUID_BRAND_MAXLEN);
952                         found_brand = 1;
953                     }
954                 }
955             }
956             fclose(fp);
957 #endif
958             cpuid->family         = 0;
959             cpuid->model          = 0;
960             cpuid->stepping       = 0;
961
962             for (i = 0; i < GMX_CPUID_NFEATURES; i++)
963             {
964                 cpuid->feature[i] = 0;
965             }
966             cpuid->feature[GMX_CPUID_FEATURE_CANNOTDETECT] = 1;
967             break;
968     }
969     return 0;
970 }
971
972
973
974 void
975 gmx_cpuid_done               (gmx_cpuid_t              cpuid)
976 {
977     free(cpuid);
978 }
979
980
981 int
982 gmx_cpuid_formatstring       (gmx_cpuid_t              cpuid,
983                               char *                   str,
984                               int                      n)
985 {
986     int                     c;
987     int                     i;
988     enum gmx_cpuid_feature  feature;
989
990 #ifdef _MSC_VER
991     _snprintf(str, n,
992               "Vendor: %s\n"
993               "Brand:  %s\n"
994               "Family: %2d  Model: %2d  Stepping: %2d\n"
995               "Features:",
996               gmx_cpuid_vendor_string[gmx_cpuid_vendor(cpuid)],
997               gmx_cpuid_brand(cpuid),
998               gmx_cpuid_family(cpuid), gmx_cpuid_model(cpuid), gmx_cpuid_stepping(cpuid));
999 #else
1000     snprintf(str, n,
1001              "Vendor: %s\n"
1002              "Brand:  %s\n"
1003              "Family: %2d  Model: %2d  Stepping: %2d\n"
1004              "Features:",
1005              gmx_cpuid_vendor_string[gmx_cpuid_vendor(cpuid)],
1006              gmx_cpuid_brand(cpuid),
1007              gmx_cpuid_family(cpuid), gmx_cpuid_model(cpuid), gmx_cpuid_stepping(cpuid));
1008 #endif
1009
1010     str[n-1] = '\0';
1011     c        = strlen(str);
1012     n       -= c;
1013     str     += c;
1014
1015     for (feature = GMX_CPUID_FEATURE_CANNOTDETECT; feature < GMX_CPUID_NFEATURES; feature++)
1016     {
1017         if (gmx_cpuid_feature(cpuid, feature) == 1)
1018         {
1019 #ifdef _MSC_VER
1020             _snprintf(str, n, " %s", gmx_cpuid_feature_string[feature]);
1021 #else
1022             snprintf(str, n, " %s", gmx_cpuid_feature_string[feature]);
1023 #endif
1024             str[n-1] = '\0';
1025             c        = strlen(str);
1026             n       -= c;
1027             str     += c;
1028         }
1029     }
1030 #ifdef _MSC_VER
1031     _snprintf(str, n, "\n");
1032 #else
1033     snprintf(str, n, "\n");
1034 #endif
1035     str[n-1] = '\0';
1036
1037     return 0;
1038 }
1039
1040
1041
1042 enum gmx_cpuid_simd
1043 gmx_cpuid_simd_suggest  (gmx_cpuid_t                 cpuid)
1044 {
1045     enum gmx_cpuid_simd  tmpsimd;
1046
1047     tmpsimd = GMX_CPUID_SIMD_NONE;
1048
1049     if (gmx_cpuid_vendor(cpuid) == GMX_CPUID_VENDOR_INTEL)
1050     {
1051         if (gmx_cpuid_feature(cpuid, GMX_CPUID_FEATURE_X86_AVX2))
1052         {
1053             tmpsimd = GMX_CPUID_SIMD_X86_AVX2_256;
1054         }
1055         else if (gmx_cpuid_feature(cpuid, GMX_CPUID_FEATURE_X86_AVX))
1056         {
1057             tmpsimd = GMX_CPUID_SIMD_X86_AVX_256;
1058         }
1059         else if (gmx_cpuid_feature(cpuid, GMX_CPUID_FEATURE_X86_SSE4_1))
1060         {
1061             tmpsimd = GMX_CPUID_SIMD_X86_SSE4_1;
1062         }
1063         else if (gmx_cpuid_feature(cpuid, GMX_CPUID_FEATURE_X86_SSE2))
1064         {
1065             tmpsimd = GMX_CPUID_SIMD_X86_SSE2;
1066         }
1067     }
1068     else if (gmx_cpuid_vendor(cpuid) == GMX_CPUID_VENDOR_AMD)
1069     {
1070         if (gmx_cpuid_feature(cpuid, GMX_CPUID_FEATURE_X86_AVX))
1071         {
1072             tmpsimd = GMX_CPUID_SIMD_X86_AVX_128_FMA;
1073         }
1074         else if (gmx_cpuid_feature(cpuid, GMX_CPUID_FEATURE_X86_SSE4_1))
1075         {
1076             tmpsimd = GMX_CPUID_SIMD_X86_SSE4_1;
1077         }
1078         else if (gmx_cpuid_feature(cpuid, GMX_CPUID_FEATURE_X86_SSE2))
1079         {
1080             tmpsimd = GMX_CPUID_SIMD_X86_SSE2;
1081         }
1082     }
1083     else if (gmx_cpuid_vendor(cpuid) == GMX_CPUID_VENDOR_FUJITSU)
1084     {
1085         if (strstr(gmx_cpuid_brand(cpuid), "SPARC64"))
1086         {
1087             tmpsimd = GMX_CPUID_SIMD_SPARC64_HPC_ACE;
1088         }
1089     }
1090     else if (gmx_cpuid_vendor(cpuid) == GMX_CPUID_VENDOR_IBM)
1091     {
1092         if (strstr(gmx_cpuid_brand(cpuid), "A2"))
1093         {
1094             tmpsimd = GMX_CPUID_SIMD_IBM_QPX;
1095         }
1096     }
1097     return tmpsimd;
1098 }
1099
1100
1101
1102 int
1103 gmx_cpuid_simd_check(gmx_cpuid_t   cpuid,
1104                      FILE *        log,
1105                      int           print_to_stderr)
1106 {
1107     int                           rc;
1108     char                          str[1024];
1109     enum gmx_cpuid_simd           simd;
1110
1111     simd = gmx_cpuid_simd_suggest(cpuid);
1112
1113     rc = (simd != compiled_simd);
1114
1115     gmx_cpuid_formatstring(cpuid, str, 1023);
1116     str[1023] = '\0';
1117
1118     if (log != NULL)
1119     {
1120         fprintf(log,
1121                 "\nDetecting CPU SIMD instructions.\nPresent hardware specification:\n"
1122                 "%s"
1123                 "SIMD instructions most likely to fit this hardware: %s\n"
1124                 "SIMD instructions selected at GROMACS compile time: %s\n\n",
1125                 str,
1126                 gmx_cpuid_simd_string[simd],
1127                 gmx_cpuid_simd_string[compiled_simd]);
1128     }
1129
1130     if (rc != 0)
1131     {
1132         if (log != NULL)
1133         {
1134             fprintf(log, "\nBinary not matching hardware - you might be losing performance.\n"
1135                     "SIMD instructions most likely to fit this hardware: %s\n"
1136                     "SIMD instructions selected at GROMACS compile time: %s\n\n",
1137                     gmx_cpuid_simd_string[simd],
1138                     gmx_cpuid_simd_string[compiled_simd]);
1139         }
1140         if (print_to_stderr)
1141         {
1142             fprintf(stderr, "Compiled SIMD instructions: %s (Gromacs could use %s on this machine, which is better)\n",
1143                     gmx_cpuid_simd_string[compiled_simd],
1144                     gmx_cpuid_simd_string[simd]);
1145         }
1146     }
1147     return rc;
1148 }
1149
1150
1151 #ifdef GMX_CPUID_STANDALONE
1152 /* Stand-alone program to enable queries of CPU features from Cmake.
1153  * Note that you need to check inline ASM capabilities before compiling and set
1154  * -DGMX_X86_GCC_INLINE_ASM for the cpuid instruction to work...
1155  */
1156 int
1157 main(int argc, char **argv)
1158 {
1159     gmx_cpuid_t                   cpuid;
1160     enum gmx_cpuid_simd           simd;
1161     int                           i, cnt;
1162
1163     if (argc < 2)
1164     {
1165         fprintf(stdout,
1166                 "Usage:\n\n%s [flags]\n\n"
1167                 "Available flags:\n"
1168                 "-vendor        Print CPU vendor.\n"
1169                 "-brand         Print CPU brand string.\n"
1170                 "-family        Print CPU family version.\n"
1171                 "-model         Print CPU model version.\n"
1172                 "-stepping      Print CPU stepping version.\n"
1173                 "-features      Print CPU feature flags.\n"
1174                 "-simd          Print suggested GROMACS SIMD instructions.\n",
1175                 argv[0]);
1176         exit(0);
1177     }
1178
1179     gmx_cpuid_init(&cpuid);
1180
1181     if (!strncmp(argv[1], "-vendor", 3))
1182     {
1183         printf("%s\n", gmx_cpuid_vendor_string[cpuid->vendor]);
1184     }
1185     else if (!strncmp(argv[1], "-brand", 3))
1186     {
1187         printf("%s\n", cpuid->brand);
1188     }
1189     else if (!strncmp(argv[1], "-family", 3))
1190     {
1191         printf("%d\n", cpuid->family);
1192     }
1193     else if (!strncmp(argv[1], "-model", 3))
1194     {
1195         printf("%d\n", cpuid->model);
1196     }
1197     else if (!strncmp(argv[1], "-stepping", 3))
1198     {
1199         printf("%d\n", cpuid->stepping);
1200     }
1201     else if (!strncmp(argv[1], "-features", 3))
1202     {
1203         cnt = 0;
1204         for (i = 0; i < GMX_CPUID_NFEATURES; i++)
1205         {
1206             if (cpuid->feature[i] == 1)
1207             {
1208                 if (cnt++ > 0)
1209                 {
1210                     printf(" ");
1211                 }
1212                 printf("%s", gmx_cpuid_feature_string[i]);
1213             }
1214         }
1215         printf("\n");
1216     }
1217     else if (!strncmp(argv[1], "-simd", 3))
1218     {
1219         simd = gmx_cpuid_simd_suggest(cpuid);
1220         fprintf(stdout, "%s\n", gmx_cpuid_simd_string[simd]);
1221     }
1222
1223     gmx_cpuid_done(cpuid);
1224
1225
1226     return 0;
1227 }
1228
1229 #endif