Add 32-bit ARM Neon SIMD support
[alexxy/gromacs.git] / src / gromacs / gmxlib / gmx_cpuid.c
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012,2013,2014, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 #include "gmxpre.h"
36
37 #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     "arm"
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 };
144
145 const char *
146 gmx_cpuid_simd_string[GMX_CPUID_NSIMD] =
147 {
148     "CannotDetect",
149     "None",
150     "Reference",
151     "SSE2",
152     "SSE4.1",
153     "AVX_128_FMA",
154     "AVX_256",
155     "AVX2_256",
156     "Sparc64 HPC-ACE",
157     "IBM_QPX",
158     "ARM_NEON"
159 };
160
161 /* Max length of brand string */
162 #define GMX_CPUID_STRLEN 256
163
164
165 /* Contents of the abstract datatype */
166 struct gmx_cpuid
167 {
168     enum gmx_cpuid_vendor      vendor;
169     char                       brand[GMX_CPUID_STRLEN];
170     int                        family;
171     int                        model;
172     int                        stepping;
173     /* Not using gmx_bool here, since this file must be possible to compile without simple.h */
174     char                       feature[GMX_CPUID_NFEATURES];
175
176     /* Basic CPU topology information. For x86 this is a bit complicated since the topology differs between
177      * operating systems and sometimes even settings. For most other architectures you can likely just check
178      * the documentation and then write static information to these arrays rather than detecting on-the-fly.
179      */
180     int                        have_cpu_topology;
181     int                        nproc;               /* total number of logical processors from OS */
182     int                        npackages;
183     int                        ncores_per_package;
184     int                        nhwthreads_per_core;
185     int *                      package_id;
186     int *                      core_id;             /* Local core id in each package */
187     int *                      hwthread_id;         /* Local hwthread id in each core */
188     int *                      locality_order;      /* Processor indices sorted in locality order */
189 };
190
191
192 /* Simple routines to access the data structure. The initialization routine is
193  * further down since that needs to call other static routines in this file.
194  */
195 enum gmx_cpuid_vendor
196 gmx_cpuid_vendor            (gmx_cpuid_t                cpuid)
197 {
198     return cpuid->vendor;
199 }
200
201
202 const char *
203 gmx_cpuid_brand             (gmx_cpuid_t                cpuid)
204 {
205     return cpuid->brand;
206 }
207
208 int
209 gmx_cpuid_family            (gmx_cpuid_t                cpuid)
210 {
211     return cpuid->family;
212 }
213
214 int
215 gmx_cpuid_model             (gmx_cpuid_t                cpuid)
216 {
217     return cpuid->model;
218 }
219
220 int
221 gmx_cpuid_stepping          (gmx_cpuid_t                cpuid)
222 {
223     return cpuid->stepping;
224 }
225
226 int
227 gmx_cpuid_feature           (gmx_cpuid_t                cpuid,
228                              enum gmx_cpuid_feature     feature)
229 {
230     return (cpuid->feature[feature] != 0);
231 }
232
233
234
235
236 /* What type of SIMD was compiled in, if any? */
237 #ifdef GMX_SIMD_X86_AVX2_256
238 static const enum gmx_cpuid_simd compiled_simd = GMX_CPUID_SIMD_X86_AVX2_256;
239 #elif defined GMX_SIMD_X86_AVX_256
240 static const enum gmx_cpuid_simd compiled_simd = GMX_CPUID_SIMD_X86_AVX_256;
241 #elif defined GMX_SIMD_X86_AVX_128_FMA
242 static const enum gmx_cpuid_simd compiled_simd = GMX_CPUID_SIMD_X86_AVX_128_FMA;
243 #elif defined GMX_SIMD_X86_SSE4_1
244 static const enum gmx_cpuid_simd compiled_simd = GMX_CPUID_SIMD_X86_SSE4_1;
245 #elif defined GMX_SIMD_X86_SSE2
246 static const enum gmx_cpuid_simd compiled_simd = GMX_CPUID_SIMD_X86_SSE2;
247 #elif defined GMX_SIMD_ARM_NEON
248 static const enum gmx_cpuid_simd compiled_simd = GMX_CPUID_SIMD_ARM_NEON;
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             }
807             else if (!strcmp(buffer2, "CPU part"))
808             {
809                 cpuid->model = strtol(buffer3, NULL, 16);
810             }
811             else if (!strcmp(buffer2, "CPU revision"))
812             {
813                 cpuid->stepping = strtol(buffer3, NULL, 10);
814             }
815             else if (!strcmp(buffer2, "Features") && strstr(buffer3, "neon"))
816             {
817                 cpuid->feature[GMX_CPUID_FEATURE_ARM_NEON] = 1;
818             }
819         }
820     }
821     fclose(fp);
822 #else
823     /* Strange non-linux platform. We cannot assume that neon is present. */
824     cpuid->feature[GMX_CPUID_FEATURE_ARM_NEON] = 0;
825 #endif
826     return 0;
827 }
828
829 /* Try to find the vendor of the current CPU, so we know what specific
830  * detection routine to call.
831  */
832 static enum gmx_cpuid_vendor
833 cpuid_check_vendor(void)
834 {
835     enum gmx_cpuid_vendor      i, vendor;
836     /* Register data used on x86 */
837     unsigned int               eax, ebx, ecx, edx;
838     char                       vendorstring[13];
839     FILE *                     fp;
840     char                       buffer[GMX_CPUID_STRLEN];
841     char                       before_colon[GMX_CPUID_STRLEN];
842     char                       after_colon[GMX_CPUID_STRLEN];
843
844     /* Set default first */
845     vendor = GMX_CPUID_VENDOR_UNKNOWN;
846
847 #ifdef GMX_CPUID_X86
848     execute_x86cpuid(0x0, 0, &eax, &ebx, &ecx, &edx);
849
850     memcpy(vendorstring, &ebx, 4);
851     memcpy(vendorstring+4, &edx, 4);
852     memcpy(vendorstring+8, &ecx, 4);
853
854     vendorstring[12] = '\0';
855
856     for (i = GMX_CPUID_VENDOR_UNKNOWN; i < GMX_CPUID_NVENDORS; i++)
857     {
858         if (!strncmp(vendorstring, gmx_cpuid_vendor_string[i], 12))
859         {
860             vendor = i;
861         }
862     }
863 #elif defined(__linux__) || defined(__linux)
864     /* General Linux. Try to get CPU vendor from /proc/cpuinfo */
865     if ( (fp = fopen("/proc/cpuinfo", "r")) != NULL)
866     {
867         while ( (vendor == GMX_CPUID_VENDOR_UNKNOWN) && (fgets(buffer, sizeof(buffer), fp) != NULL))
868         {
869             chomp_substring_before_colon(buffer, before_colon, sizeof(before_colon));
870             /* Intel/AMD use "vendor_id", IBM "vendor"(?) or "model". Fujitsu "manufacture".
871              * On ARM there does not seem to be a vendor, but ARM is listed in the Processor string.
872              * Add others if you have them!
873              */
874             if (!strcmp(before_colon, "vendor_id")
875                 || !strcmp(before_colon, "vendor")
876                 || !strcmp(before_colon, "manufacture")
877                 || !strcmp(before_colon, "model")
878                 || !strcmp(before_colon, "Processor"))
879             {
880                 chomp_substring_after_colon(buffer, after_colon, sizeof(after_colon));
881                 for (i = GMX_CPUID_VENDOR_UNKNOWN; i < GMX_CPUID_NVENDORS; i++)
882                 {
883                     /* Be liberal and accept if we find the vendor
884                      * string (or alternative string) anywhere. Using
885                      * strcasestr() would be non-portable. */
886                     if (strstr(after_colon, gmx_cpuid_vendor_string[i])
887                         || strstr(after_colon, gmx_cpuid_vendor_string_alternative[i]))
888                     {
889                         vendor = i;
890                     }
891                 }
892             }
893         }
894     }
895     fclose(fp);
896 #elif defined(__arm__) || defined (__arm)
897     /* If we are using ARM on something that is not linux we have to trust the compiler,
898      * and we cannot get the extra info that might be present in /proc/cpuinfo.
899      * This path will not trigger 64-bit arm, which is identified by __aarch64__ instead.
900      */
901     vendor = GMX_CPUID_VENDOR_ARM;
902 #endif
903
904     return vendor;
905 }
906
907
908
909 int
910 gmx_cpuid_topology(gmx_cpuid_t        cpuid,
911                    int *              nprocessors,
912                    int *              npackages,
913                    int *              ncores_per_package,
914                    int *              nhwthreads_per_core,
915                    const int **       package_id,
916                    const int **       core_id,
917                    const int **       hwthread_id,
918                    const int **       locality_order)
919 {
920     int rc;
921
922     if (cpuid->have_cpu_topology)
923     {
924         *nprocessors          = cpuid->nproc;
925         *npackages            = cpuid->npackages;
926         *ncores_per_package   = cpuid->ncores_per_package;
927         *nhwthreads_per_core  = cpuid->nhwthreads_per_core;
928         *package_id           = cpuid->package_id;
929         *core_id              = cpuid->core_id;
930         *hwthread_id          = cpuid->hwthread_id;
931         *locality_order       = cpuid->locality_order;
932         rc                    = 0;
933     }
934     else
935     {
936         rc = -1;
937     }
938     return rc;
939 }
940
941
942 enum gmx_cpuid_x86_smt
943 gmx_cpuid_x86_smt(gmx_cpuid_t cpuid)
944 {
945     enum gmx_cpuid_x86_smt rc;
946
947     if (cpuid->have_cpu_topology)
948     {
949         rc = (cpuid->nhwthreads_per_core > 1) ? GMX_CPUID_X86_SMT_ENABLED : GMX_CPUID_X86_SMT_DISABLED;
950     }
951     else if (cpuid->vendor == GMX_CPUID_VENDOR_AMD || gmx_cpuid_feature(cpuid, GMX_CPUID_FEATURE_X86_HTT) == 0)
952     {
953         rc = GMX_CPUID_X86_SMT_DISABLED;
954     }
955     else
956     {
957         rc = GMX_CPUID_X86_SMT_CANNOTDETECT;
958     }
959     return rc;
960 }
961
962
963 int
964 gmx_cpuid_init               (gmx_cpuid_t *              pcpuid)
965 {
966     gmx_cpuid_t cpuid;
967     int         i;
968     FILE *      fp;
969     char        buffer[GMX_CPUID_STRLEN], buffer2[GMX_CPUID_STRLEN];
970     int         found_brand;
971
972     cpuid = malloc(sizeof(*cpuid));
973
974     *pcpuid = cpuid;
975
976     for (i = 0; i < GMX_CPUID_NFEATURES; i++)
977     {
978         cpuid->feature[i] = 0;
979     }
980
981     cpuid->have_cpu_topology   = 0;
982     cpuid->nproc               = 0;
983     cpuid->npackages           = 0;
984     cpuid->ncores_per_package  = 0;
985     cpuid->nhwthreads_per_core = 0;
986     cpuid->package_id          = NULL;
987     cpuid->core_id             = NULL;
988     cpuid->hwthread_id         = NULL;
989     cpuid->locality_order      = NULL;
990
991     cpuid->vendor = cpuid_check_vendor();
992
993     switch (cpuid->vendor)
994     {
995 #ifdef GMX_CPUID_X86
996         case GMX_CPUID_VENDOR_INTEL:
997             cpuid_check_intel_x86(cpuid);
998             break;
999         case GMX_CPUID_VENDOR_AMD:
1000             cpuid_check_amd_x86(cpuid);
1001             break;
1002 #endif
1003         case GMX_CPUID_VENDOR_ARM:
1004             cpuid_check_arm(cpuid);
1005             break;
1006         default:
1007             /* Default value */
1008             strncpy(cpuid->brand, "Unknown CPU brand", GMX_CPUID_STRLEN);
1009 #if defined(__linux__) || defined(__linux)
1010             /* General Linux. Try to get CPU type from /proc/cpuinfo */
1011             if ( (fp = fopen("/proc/cpuinfo", "r")) != NULL)
1012             {
1013                 found_brand = 0;
1014                 while ( (found_brand == 0) && (fgets(buffer, sizeof(buffer), fp) != NULL))
1015                 {
1016                     chomp_substring_before_colon(buffer, buffer2, sizeof(buffer2));
1017                     /* Intel uses "model name", Fujitsu and IBM "cpu". */
1018                     if (!strcmp(buffer2, "model name") || !strcmp(buffer2, "cpu"))
1019                     {
1020                         chomp_substring_after_colon(buffer, cpuid->brand, GMX_CPUID_STRLEN);
1021                         found_brand = 1;
1022                     }
1023                 }
1024             }
1025             fclose(fp);
1026 #endif
1027             cpuid->family         = 0;
1028             cpuid->model          = 0;
1029             cpuid->stepping       = 0;
1030
1031             for (i = 0; i < GMX_CPUID_NFEATURES; i++)
1032             {
1033                 cpuid->feature[i] = 0;
1034             }
1035             cpuid->feature[GMX_CPUID_FEATURE_CANNOTDETECT] = 1;
1036             break;
1037     }
1038     return 0;
1039 }
1040
1041
1042
1043 void
1044 gmx_cpuid_done               (gmx_cpuid_t              cpuid)
1045 {
1046     free(cpuid);
1047 }
1048
1049
1050 int
1051 gmx_cpuid_formatstring       (gmx_cpuid_t              cpuid,
1052                               char *                   str,
1053                               int                      n)
1054 {
1055     int                     c;
1056     int                     i;
1057     enum gmx_cpuid_feature  feature;
1058
1059 #ifdef _MSC_VER
1060     _snprintf(str, n,
1061               "Vendor: %s\n"
1062               "Brand:  %s\n"
1063               "Family: %2d  Model: %2d  Stepping: %2d\n"
1064               "Features:",
1065               gmx_cpuid_vendor_string[gmx_cpuid_vendor(cpuid)],
1066               gmx_cpuid_brand(cpuid),
1067               gmx_cpuid_family(cpuid), gmx_cpuid_model(cpuid), gmx_cpuid_stepping(cpuid));
1068 #else
1069     snprintf(str, n,
1070              "Vendor: %s\n"
1071              "Brand:  %s\n"
1072              "Family: %2d  Model: %2d  Stepping: %2d\n"
1073              "Features:",
1074              gmx_cpuid_vendor_string[gmx_cpuid_vendor(cpuid)],
1075              gmx_cpuid_brand(cpuid),
1076              gmx_cpuid_family(cpuid), gmx_cpuid_model(cpuid), gmx_cpuid_stepping(cpuid));
1077 #endif
1078
1079     str[n-1] = '\0';
1080     c        = strlen(str);
1081     n       -= c;
1082     str     += c;
1083
1084     for (feature = GMX_CPUID_FEATURE_CANNOTDETECT; feature < GMX_CPUID_NFEATURES; feature++)
1085     {
1086         if (gmx_cpuid_feature(cpuid, feature) == 1)
1087         {
1088 #ifdef _MSC_VER
1089             _snprintf(str, n, " %s", gmx_cpuid_feature_string[feature]);
1090 #else
1091             snprintf(str, n, " %s", gmx_cpuid_feature_string[feature]);
1092 #endif
1093             str[n-1] = '\0';
1094             c        = strlen(str);
1095             n       -= c;
1096             str     += c;
1097         }
1098     }
1099 #ifdef _MSC_VER
1100     _snprintf(str, n, "\n");
1101 #else
1102     snprintf(str, n, "\n");
1103 #endif
1104     str[n-1] = '\0';
1105
1106     return 0;
1107 }
1108
1109
1110
1111 enum gmx_cpuid_simd
1112 gmx_cpuid_simd_suggest  (gmx_cpuid_t                 cpuid)
1113 {
1114     enum gmx_cpuid_simd  tmpsimd;
1115
1116     tmpsimd = GMX_CPUID_SIMD_NONE;
1117
1118     if (gmx_cpuid_vendor(cpuid) == GMX_CPUID_VENDOR_INTEL)
1119     {
1120         if (gmx_cpuid_feature(cpuid, GMX_CPUID_FEATURE_X86_AVX2))
1121         {
1122             tmpsimd = GMX_CPUID_SIMD_X86_AVX2_256;
1123         }
1124         else if (gmx_cpuid_feature(cpuid, GMX_CPUID_FEATURE_X86_AVX))
1125         {
1126             tmpsimd = GMX_CPUID_SIMD_X86_AVX_256;
1127         }
1128         else if (gmx_cpuid_feature(cpuid, GMX_CPUID_FEATURE_X86_SSE4_1))
1129         {
1130             tmpsimd = GMX_CPUID_SIMD_X86_SSE4_1;
1131         }
1132         else if (gmx_cpuid_feature(cpuid, GMX_CPUID_FEATURE_X86_SSE2))
1133         {
1134             tmpsimd = GMX_CPUID_SIMD_X86_SSE2;
1135         }
1136     }
1137     else if (gmx_cpuid_vendor(cpuid) == GMX_CPUID_VENDOR_AMD)
1138     {
1139         if (gmx_cpuid_feature(cpuid, GMX_CPUID_FEATURE_X86_AVX))
1140         {
1141             tmpsimd = GMX_CPUID_SIMD_X86_AVX_128_FMA;
1142         }
1143         else if (gmx_cpuid_feature(cpuid, GMX_CPUID_FEATURE_X86_SSE4_1))
1144         {
1145             tmpsimd = GMX_CPUID_SIMD_X86_SSE4_1;
1146         }
1147         else if (gmx_cpuid_feature(cpuid, GMX_CPUID_FEATURE_X86_SSE2))
1148         {
1149             tmpsimd = GMX_CPUID_SIMD_X86_SSE2;
1150         }
1151     }
1152     else if (gmx_cpuid_vendor(cpuid) == GMX_CPUID_VENDOR_FUJITSU)
1153     {
1154         if (strstr(gmx_cpuid_brand(cpuid), "SPARC64"))
1155         {
1156             tmpsimd = GMX_CPUID_SIMD_SPARC64_HPC_ACE;
1157         }
1158     }
1159     else if (gmx_cpuid_vendor(cpuid) == GMX_CPUID_VENDOR_IBM)
1160     {
1161         if (strstr(gmx_cpuid_brand(cpuid), "A2"))
1162         {
1163             tmpsimd = GMX_CPUID_SIMD_IBM_QPX;
1164         }
1165     }
1166     else if (gmx_cpuid_vendor(cpuid) == GMX_CPUID_VENDOR_ARM)
1167     {
1168         if (gmx_cpuid_feature(cpuid, GMX_CPUID_FEATURE_ARM_NEON))
1169         {
1170             tmpsimd = GMX_CPUID_SIMD_ARM_NEON;
1171         }
1172     }
1173     return tmpsimd;
1174 }
1175
1176
1177
1178 int
1179 gmx_cpuid_simd_check(gmx_cpuid_t   cpuid,
1180                      FILE *        log,
1181                      int           print_to_stderr)
1182 {
1183     int                           rc;
1184     char                          str[1024];
1185     enum gmx_cpuid_simd           simd;
1186
1187     simd = gmx_cpuid_simd_suggest(cpuid);
1188
1189     rc = (simd != compiled_simd);
1190
1191     gmx_cpuid_formatstring(cpuid, str, 1023);
1192     str[1023] = '\0';
1193
1194     if (log != NULL)
1195     {
1196         fprintf(log,
1197                 "\nDetecting CPU SIMD instructions.\nPresent hardware specification:\n"
1198                 "%s"
1199                 "SIMD instructions most likely to fit this hardware: %s\n"
1200                 "SIMD instructions selected at GROMACS compile time: %s\n\n",
1201                 str,
1202                 gmx_cpuid_simd_string[simd],
1203                 gmx_cpuid_simd_string[compiled_simd]);
1204     }
1205
1206     if (rc != 0)
1207     {
1208         if (log != NULL)
1209         {
1210             fprintf(log, "\nBinary not matching hardware - you might be losing performance.\n"
1211                     "SIMD instructions most likely to fit this hardware: %s\n"
1212                     "SIMD instructions selected at GROMACS compile time: %s\n\n",
1213                     gmx_cpuid_simd_string[simd],
1214                     gmx_cpuid_simd_string[compiled_simd]);
1215         }
1216         if (print_to_stderr)
1217         {
1218             fprintf(stderr, "Compiled SIMD instructions: %s (Gromacs could use %s on this machine, which is better)\n",
1219                     gmx_cpuid_simd_string[compiled_simd],
1220                     gmx_cpuid_simd_string[simd]);
1221         }
1222     }
1223     return rc;
1224 }
1225
1226
1227 #ifdef GMX_CPUID_STANDALONE
1228 /* Stand-alone program to enable queries of CPU features from Cmake.
1229  * Note that you need to check inline ASM capabilities before compiling and set
1230  * -DGMX_X86_GCC_INLINE_ASM for the cpuid instruction to work...
1231  */
1232 int
1233 main(int argc, char **argv)
1234 {
1235     gmx_cpuid_t                   cpuid;
1236     enum gmx_cpuid_simd           simd;
1237     int                           i, cnt;
1238
1239     if (argc < 2)
1240     {
1241         fprintf(stdout,
1242                 "Usage:\n\n%s [flags]\n\n"
1243                 "Available flags:\n"
1244                 "-vendor        Print CPU vendor.\n"
1245                 "-brand         Print CPU brand string.\n"
1246                 "-family        Print CPU family version.\n"
1247                 "-model         Print CPU model version.\n"
1248                 "-stepping      Print CPU stepping version.\n"
1249                 "-features      Print CPU feature flags.\n"
1250                 "-simd          Print suggested GROMACS SIMD instructions.\n",
1251                 argv[0]);
1252         exit(0);
1253     }
1254
1255     gmx_cpuid_init(&cpuid);
1256
1257     if (!strncmp(argv[1], "-vendor", 3))
1258     {
1259         printf("%s\n", gmx_cpuid_vendor_string[cpuid->vendor]);
1260     }
1261     else if (!strncmp(argv[1], "-brand", 3))
1262     {
1263         printf("%s\n", cpuid->brand);
1264     }
1265     else if (!strncmp(argv[1], "-family", 3))
1266     {
1267         printf("%d\n", cpuid->family);
1268     }
1269     else if (!strncmp(argv[1], "-model", 3))
1270     {
1271         printf("%d\n", cpuid->model);
1272     }
1273     else if (!strncmp(argv[1], "-stepping", 3))
1274     {
1275         printf("%d\n", cpuid->stepping);
1276     }
1277     else if (!strncmp(argv[1], "-features", 3))
1278     {
1279         cnt = 0;
1280         for (i = 0; i < GMX_CPUID_NFEATURES; i++)
1281         {
1282             if (cpuid->feature[i] == 1)
1283             {
1284                 if (cnt++ > 0)
1285                 {
1286                     printf(" ");
1287                 }
1288                 printf("%s", gmx_cpuid_feature_string[i]);
1289             }
1290         }
1291         printf("\n");
1292     }
1293     else if (!strncmp(argv[1], "-simd", 3))
1294     {
1295         simd = gmx_cpuid_simd_suggest(cpuid);
1296         fprintf(stdout, "%s\n", gmx_cpuid_simd_string[simd]);
1297     }
1298
1299     gmx_cpuid_done(cpuid);
1300
1301
1302     return 0;
1303 }
1304
1305 #endif