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