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