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