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