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