Correct -ntmpi auto for intel nehalem and older
[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,2015, 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 #ifdef HAVE_CONFIG_H
36 #include <config.h>
37 #endif
38
39 #ifdef HAVE_SCHED_H
40 #  ifndef _GNU_SOURCE
41 #    define _GNU_SOURCE 1
42 #  endif
43 #  include <sched.h>
44 #endif
45
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <ctype.h>
50 #ifdef GMX_NATIVE_WINDOWS
51 /* MSVC definition for __cpuid() */
52     #ifdef _MSC_VER
53         #include <intrin.h>
54     #endif
55 /* sysinfo functions */
56     #include <windows.h>
57 #endif
58 #ifdef HAVE_UNISTD_H
59 /* sysconf() definition */
60     #include <unistd.h>
61 #endif
62
63 #include "gmx_cpuid.h"
64
65
66
67 /* For convenience, and to enable configure-time invocation, we keep all architectures
68  * in a single file, but to avoid repeated ifdefs we set the overall architecture here.
69  */
70 #ifdef GMX_TARGET_X86
71 /* OK, it is x86, but can we execute cpuid? */
72 #if defined(GMX_X86_GCC_INLINE_ASM) || ( defined(_MSC_VER) && ( (_MSC_VER > 1500) || (_MSC_VER == 1500 & _MSC_FULL_VER >= 150030729)))
73 #    define GMX_CPUID_X86
74 #endif
75 #endif
76
77 /* Global constant character strings corresponding to our enumerated types */
78 const char *
79 gmx_cpuid_vendor_string[GMX_CPUID_NVENDORS] =
80 {
81     "CannotDetect",
82     "Unknown",
83     "GenuineIntel",
84     "AuthenticAMD",
85     "Fujitsu",
86     "IBM"
87 };
88
89 const char *
90 gmx_cpuid_vendor_string_alternative[GMX_CPUID_NVENDORS] =
91 {
92     "CannotDetect",
93     "Unknown",
94     "GenuineIntel",
95     "AuthenticAMD",
96     "Fujitsu",
97     "ibm" /* Used on BlueGene/Q */
98 };
99
100 const char *
101 gmx_cpuid_feature_string[GMX_CPUID_NFEATURES] =
102 {
103     "CannotDetect",
104     "aes",
105     "apic",
106     "avx",
107     "avx2",
108     "clfsh",
109     "cmov",
110     "cx8",
111     "cx16",
112     "f16c",
113     "fma",
114     "fma4",
115     "htt",
116     "lahf_lm",
117     "misalignsse",
118     "mmx",
119     "msr",
120     "nonstop_tsc",
121     "pcid",
122     "pclmuldq",
123     "pdcm",
124     "pdpe1gb",
125     "popcnt",
126     "pse",
127     "rdrnd",
128     "rdtscp",
129     "sse2",
130     "sse3",
131     "sse4a",
132     "sse4.1",
133     "sse4.2",
134     "ssse3",
135     "tdt",
136     "x2apic",
137     "xop"
138 };
139
140 const char *
141 gmx_cpuid_simd_string[GMX_CPUID_NSIMD] =
142 {
143     "CannotDetect",
144     "None",
145     "Reference",
146     "SSE2",
147     "SSE4.1",
148     "AVX_128_FMA",
149     "AVX_256",
150     "AVX2_256",
151     "Sparc64 HPC-ACE",
152     "IBM_QPX"
153 };
154
155 /* Max length of brand string */
156 #define GMX_CPUID_BRAND_MAXLEN 256
157
158
159 /* Contents of the abstract datatype */
160 struct gmx_cpuid
161 {
162     enum gmx_cpuid_vendor      vendor;
163     char                       brand[GMX_CPUID_BRAND_MAXLEN];
164     int                        family;
165     int                        model;
166     int                        stepping;
167     /* Not using gmx_bool here, since this file must be possible to compile without simple.h */
168     char                       feature[GMX_CPUID_NFEATURES];
169
170     /* Basic CPU topology information. For x86 this is a bit complicated since the topology differs between
171      * operating systems and sometimes even settings. For most other architectures you can likely just check
172      * the documentation and then write static information to these arrays rather than detecting on-the-fly.
173      */
174     int                        have_cpu_topology;
175     int                        nproc;               /* total number of logical processors from OS */
176     int                        npackages;
177     int                        ncores_per_package;
178     int                        nhwthreads_per_core;
179     int *                      package_id;
180     int *                      core_id;             /* Local core id in each package */
181     int *                      hwthread_id;         /* Local hwthread id in each core */
182     int *                      locality_order;      /* Processor indices sorted in locality order */
183 };
184
185
186 /* Simple routines to access the data structure. The initialization routine is
187  * further down since that needs to call other static routines in this file.
188  */
189 enum gmx_cpuid_vendor
190 gmx_cpuid_vendor            (gmx_cpuid_t                cpuid)
191 {
192     return cpuid->vendor;
193 }
194
195
196 const char *
197 gmx_cpuid_brand             (gmx_cpuid_t                cpuid)
198 {
199     return cpuid->brand;
200 }
201
202 int
203 gmx_cpuid_family            (gmx_cpuid_t                cpuid)
204 {
205     return cpuid->family;
206 }
207
208 int
209 gmx_cpuid_model             (gmx_cpuid_t                cpuid)
210 {
211     return cpuid->model;
212 }
213
214 int
215 gmx_cpuid_stepping          (gmx_cpuid_t                cpuid)
216 {
217     return cpuid->stepping;
218 }
219
220 int
221 gmx_cpuid_feature           (gmx_cpuid_t                cpuid,
222                              enum gmx_cpuid_feature     feature)
223 {
224     return (cpuid->feature[feature] != 0);
225 }
226
227
228 int
229 gmx_cpuid_is_intel_nehalem  (const gmx_cpuid_t          cpuid)
230 {
231     return (cpuid->vendor == GMX_CPUID_VENDOR_INTEL &&
232             cpuid->family == 6 &&
233             (cpuid->model == 0x2E ||
234              cpuid->model == 0x1A ||
235              cpuid->model == 0x1E ||
236              cpuid->model == 0x2F ||
237              cpuid->model == 0x2C ||
238              cpuid->model == 0x25));
239 }
240
241
242 /* What type of SIMD was compiled in, if any? */
243 #ifdef GMX_SIMD_X86_AVX2_256
244 static const enum gmx_cpuid_simd compiled_simd = GMX_CPUID_SIMD_X86_AVX2_256;
245 #elif defined GMX_SIMD_X86_AVX_256
246 static const enum gmx_cpuid_simd compiled_simd = GMX_CPUID_SIMD_X86_AVX_256;
247 #elif defined GMX_SIMD_X86_AVX_128_FMA
248 static const enum gmx_cpuid_simd compiled_simd = GMX_CPUID_SIMD_X86_AVX_128_FMA;
249 #elif defined GMX_SIMD_X86_SSE4_1
250 static const enum gmx_cpuid_simd compiled_simd = GMX_CPUID_SIMD_X86_SSE4_1;
251 #elif defined GMX_SIMD_X86_SSE2
252 static const enum gmx_cpuid_simd compiled_simd = GMX_CPUID_SIMD_X86_SSE2;
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_BRAND_MAXLEN];
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_BRAND_MAXLEN);
380     }
381     else
382     {
383         strncpy(cpuid->brand, "Unknown CPU brand", GMX_CPUID_BRAND_MAXLEN);
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     return nunique;
485 }
486
487 /* APIC IDs, or everything you wanted to know about your x86 cores but were afraid to ask...
488  *
489  * Raw APIC IDs are unfortunately somewhat dirty. For technical reasons they are assigned
490  * in power-of-2 chunks, and even then there are no guarantees about specific numbers - all
491  * we know is that the part for each thread/core/package is unique, and how many bits are
492  * reserved for that part.
493  * This routine does internal renumbering so we get continuous indices, and also
494  * decodes the actual number of packages,cores-per-package and hwthreads-per-core.
495  * Returns: 0 on success, non-zero on failure.
496  */
497 static int
498 cpuid_x86_decode_apic_id(gmx_cpuid_t cpuid, int *apic_id, int core_bits, int hwthread_bits)
499 {
500     int i, idx;
501     int hwthread_mask, core_mask_after_shift;
502
503     cpuid->hwthread_id     = malloc(sizeof(int)*cpuid->nproc);
504     cpuid->core_id         = malloc(sizeof(int)*cpuid->nproc);
505     cpuid->package_id      = malloc(sizeof(int)*cpuid->nproc);
506     cpuid->locality_order  = malloc(sizeof(int)*cpuid->nproc);
507
508     hwthread_mask         = (1 << hwthread_bits) - 1;
509     core_mask_after_shift = (1 << core_bits) - 1;
510
511     for (i = 0; i < cpuid->nproc; i++)
512     {
513         cpuid->hwthread_id[i] = apic_id[i] & hwthread_mask;
514         cpuid->core_id[i]     = (apic_id[i] >> hwthread_bits) & core_mask_after_shift;
515         cpuid->package_id[i]  = apic_id[i] >> (core_bits + hwthread_bits);
516     }
517
518     cpuid->npackages            = cpuid_renumber_elements(cpuid->package_id, cpuid->nproc);
519     cpuid->ncores_per_package   = cpuid_renumber_elements(cpuid->core_id, cpuid->nproc);
520     cpuid->nhwthreads_per_core  = cpuid_renumber_elements(cpuid->hwthread_id, cpuid->nproc);
521
522     /* now check for consistency */
523     if ( (cpuid->npackages * cpuid->ncores_per_package *
524           cpuid->nhwthreads_per_core) != cpuid->nproc)
525     {
526         /* the packages/cores-per-package/hwthreads-per-core counts are
527            inconsistent. */
528         return -1;
529     }
530
531     /* Create a locality order array, i.e. first all resources in package0, which in turn
532      * are sorted so we first have all resources in core0, where threads are sorted in order, etc.
533      */
534
535     for (i = 0; i < cpuid->nproc; i++)
536     {
537         idx = (cpuid->package_id[i]*cpuid->ncores_per_package + cpuid->core_id[i])*cpuid->nhwthreads_per_core + cpuid->hwthread_id[i];
538         cpuid->locality_order[idx] = i;
539     }
540     return 0;
541 }
542
543
544 /* Detection of AMD-specific CPU features */
545 static int
546 cpuid_check_amd_x86(gmx_cpuid_t                cpuid)
547 {
548     int                       max_stdfn, max_extfn, ret;
549     unsigned int              eax, ebx, ecx, edx;
550     int                       hwthread_bits, core_bits;
551     int *                     apic_id;
552
553     cpuid_check_common_x86(cpuid);
554
555     execute_x86cpuid(0x0, 0, &eax, &ebx, &ecx, &edx);
556     max_stdfn = eax;
557
558     execute_x86cpuid(0x80000000, 0, &eax, &ebx, &ecx, &edx);
559     max_extfn = eax;
560
561     if (max_extfn >= 0x80000001)
562     {
563         execute_x86cpuid(0x80000001, 0, &eax, &ebx, &ecx, &edx);
564
565         cpuid->feature[GMX_CPUID_FEATURE_X86_SSE4A]       = (ecx & (1 << 6))  != 0;
566         cpuid->feature[GMX_CPUID_FEATURE_X86_MISALIGNSSE] = (ecx & (1 << 7))  != 0;
567         cpuid->feature[GMX_CPUID_FEATURE_X86_XOP]         = (ecx & (1 << 11)) != 0;
568         cpuid->feature[GMX_CPUID_FEATURE_X86_FMA4]        = (ecx & (1 << 16)) != 0;
569     }
570
571     /* Query APIC information on AMD */
572     if (max_extfn >= 0x80000008)
573     {
574 #if (defined HAVE_SCHED_AFFINITY && defined HAVE_SYSCONF && defined __linux__)
575         /* Linux */
576         unsigned int   i;
577         cpu_set_t      cpuset, save_cpuset;
578         cpuid->nproc = sysconf(_SC_NPROCESSORS_ONLN);
579         apic_id      = malloc(sizeof(int)*cpuid->nproc);
580         sched_getaffinity(0, sizeof(cpu_set_t), &save_cpuset);
581         /* Get APIC id from each core */
582         CPU_ZERO(&cpuset);
583         for (i = 0; i < cpuid->nproc; i++)
584         {
585             CPU_SET(i, &cpuset);
586             sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);
587             execute_x86cpuid(0x1, 0, &eax, &ebx, &ecx, &edx);
588             apic_id[i] = ebx >> 24;
589             CPU_CLR(i, &cpuset);
590         }
591         /* Reset affinity to the value it had when calling this routine */
592         sched_setaffinity(0, sizeof(cpu_set_t), &save_cpuset);
593 #define CPUID_HAVE_APIC
594 #elif defined GMX_NATIVE_WINDOWS
595         /* Windows */
596         DWORD_PTR     i;
597         SYSTEM_INFO   sysinfo;
598         unsigned int  save_affinity, affinity;
599         GetSystemInfo( &sysinfo );
600         cpuid->nproc  = sysinfo.dwNumberOfProcessors;
601         apic_id       = malloc(sizeof(int)*cpuid->nproc);
602         /* Get previous affinity mask */
603         save_affinity = SetThreadAffinityMask(GetCurrentThread(), 1);
604         for (i = 0; i < cpuid->nproc; i++)
605         {
606             SetThreadAffinityMask(GetCurrentThread(), (((DWORD_PTR)1)<<i));
607             Sleep(0);
608             execute_x86cpuid(0x1, 0, &eax, &ebx, &ecx, &edx);
609             apic_id[i] = ebx >> 24;
610         }
611         SetThreadAffinityMask(GetCurrentThread(), save_affinity);
612 #define CPUID_HAVE_APIC
613 #endif
614 #ifdef CPUID_HAVE_APIC
615         /* AMD does not support SMT yet - there are no hwthread bits in apic ID */
616         hwthread_bits = 0;
617         /* Get number of core bits in apic ID - try modern extended method first */
618         execute_x86cpuid(0x80000008, 0, &eax, &ebx, &ecx, &edx);
619         core_bits = (ecx >> 12) & 0xf;
620         if (core_bits == 0)
621         {
622             /* Legacy method for old single/dual core AMD CPUs */
623             int i = ecx & 0xF;
624             for (core_bits = 0; (i>>core_bits) > 0; core_bits++)
625             {
626                 ;
627             }
628         }
629         ret = cpuid_x86_decode_apic_id(cpuid, apic_id, core_bits,
630                                        hwthread_bits);
631         cpuid->have_cpu_topology = (ret == 0);
632 #endif
633     }
634     return 0;
635 }
636
637 /* Detection of Intel-specific CPU features */
638 static int
639 cpuid_check_intel_x86(gmx_cpuid_t                cpuid)
640 {
641     unsigned int              max_stdfn, max_extfn, ret;
642     unsigned int              eax, ebx, ecx, edx;
643     unsigned int              max_logical_cores, max_physical_cores;
644     int                       hwthread_bits, core_bits;
645     int *                     apic_id;
646
647     cpuid_check_common_x86(cpuid);
648
649     execute_x86cpuid(0x0, 0, &eax, &ebx, &ecx, &edx);
650     max_stdfn = eax;
651
652     execute_x86cpuid(0x80000000, 0, &eax, &ebx, &ecx, &edx);
653     max_extfn = eax;
654
655     if (max_stdfn >= 1)
656     {
657         execute_x86cpuid(0x1, 0, &eax, &ebx, &ecx, &edx);
658         cpuid->feature[GMX_CPUID_FEATURE_X86_PDCM]    = (ecx & (1 << 15)) != 0;
659         cpuid->feature[GMX_CPUID_FEATURE_X86_PCID]    = (ecx & (1 << 17)) != 0;
660         cpuid->feature[GMX_CPUID_FEATURE_X86_X2APIC]  = (ecx & (1 << 21)) != 0;
661         cpuid->feature[GMX_CPUID_FEATURE_X86_TDT]     = (ecx & (1 << 24)) != 0;
662     }
663
664     if (max_stdfn >= 7)
665     {
666         execute_x86cpuid(0x7, 0, &eax, &ebx, &ecx, &edx);
667         cpuid->feature[GMX_CPUID_FEATURE_X86_AVX2]    = (ebx & (1 << 5))  != 0;
668     }
669
670     /* Check whether Hyper-Threading is enabled, not only supported */
671     if (cpuid->feature[GMX_CPUID_FEATURE_X86_HTT] && max_stdfn >= 4)
672     {
673         execute_x86cpuid(0x1, 0, &eax, &ebx, &ecx, &edx);
674         max_logical_cores  = (ebx >> 16) & 0x0FF;
675         execute_x86cpuid(0x4, 0, &eax, &ebx, &ecx, &edx);
676         max_physical_cores = ((eax >> 26) & 0x3F) + 1;
677
678         /* Clear HTT flag if we only have 1 logical core per physical */
679         if (max_logical_cores/max_physical_cores < 2)
680         {
681             cpuid->feature[GMX_CPUID_FEATURE_X86_HTT] = 0;
682         }
683     }
684
685     if (max_stdfn >= 0xB)
686     {
687         /* Query x2 APIC information from cores */
688 #if (defined HAVE_SCHED_AFFINITY && defined HAVE_SYSCONF && defined __linux__)
689         /* Linux */
690         unsigned int   i;
691         cpu_set_t      cpuset, save_cpuset;
692         cpuid->nproc = sysconf(_SC_NPROCESSORS_ONLN);
693         apic_id      = malloc(sizeof(int)*cpuid->nproc);
694         sched_getaffinity(0, sizeof(cpu_set_t), &save_cpuset);
695         /* Get x2APIC ID from each hardware thread */
696         CPU_ZERO(&cpuset);
697         for (i = 0; i < cpuid->nproc; i++)
698         {
699             CPU_SET(i, &cpuset);
700             sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);
701             execute_x86cpuid(0xB, 0, &eax, &ebx, &ecx, &edx);
702             apic_id[i] = edx;
703             CPU_CLR(i, &cpuset);
704         }
705         /* Reset affinity to the value it had when calling this routine */
706         sched_setaffinity(0, sizeof(cpu_set_t), &save_cpuset);
707 #define CPUID_HAVE_APIC
708 #elif defined GMX_NATIVE_WINDOWS
709         /* Windows */
710         DWORD_PTR     i;
711         SYSTEM_INFO   sysinfo;
712         unsigned int  save_affinity, affinity;
713         GetSystemInfo( &sysinfo );
714         cpuid->nproc  = sysinfo.dwNumberOfProcessors;
715         apic_id       = malloc(sizeof(int)*cpuid->nproc);
716         /* Get previous affinity mask */
717         save_affinity = SetThreadAffinityMask(GetCurrentThread(), 1);
718         for (i = 0; i < cpuid->nproc; i++)
719         {
720             SetThreadAffinityMask(GetCurrentThread(), (((DWORD_PTR)1)<<i));
721             Sleep(0);
722             execute_x86cpuid(0xB, 0, &eax, &ebx, &ecx, &edx);
723             apic_id[i] = edx;
724         }
725         SetThreadAffinityMask(GetCurrentThread(), save_affinity);
726 #define CPUID_HAVE_APIC
727 #endif
728 #ifdef CPUID_HAVE_APIC
729         execute_x86cpuid(0xB, 0, &eax, &ebx, &ecx, &edx);
730         hwthread_bits    = eax & 0x1F;
731         execute_x86cpuid(0xB, 1, &eax, &ebx, &ecx, &edx);
732         core_bits        = (eax & 0x1F) - hwthread_bits;
733         ret              = cpuid_x86_decode_apic_id(cpuid, apic_id, core_bits,
734                                                     hwthread_bits);
735         cpuid->have_cpu_topology = (ret == 0);
736 #endif
737     }
738     return 0;
739 }
740 #endif /* GMX_CPUID_X86 */
741
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 /* Try to find the vendor of the current CPU, so we know what specific
790  * detection routine to call.
791  */
792 static enum gmx_cpuid_vendor
793 cpuid_check_vendor(void)
794 {
795     enum gmx_cpuid_vendor      i, vendor;
796     /* Register data used on x86 */
797     unsigned int               eax, ebx, ecx, edx;
798     char                       vendorstring[13];
799     FILE *                     fp;
800     char                       buffer[255], before_colon[255], after_colon[255];
801
802     /* Set default first */
803     vendor = GMX_CPUID_VENDOR_UNKNOWN;
804
805 #ifdef GMX_CPUID_X86
806     execute_x86cpuid(0x0, 0, &eax, &ebx, &ecx, &edx);
807
808     memcpy(vendorstring, &ebx, 4);
809     memcpy(vendorstring+4, &edx, 4);
810     memcpy(vendorstring+8, &ecx, 4);
811
812     vendorstring[12] = '\0';
813
814     for (i = GMX_CPUID_VENDOR_UNKNOWN; i < GMX_CPUID_NVENDORS; i++)
815     {
816         if (!strncmp(vendorstring, gmx_cpuid_vendor_string[i], 12))
817         {
818             vendor = i;
819         }
820     }
821 #elif defined(__linux__) || defined(__linux)
822     /* General Linux. Try to get CPU vendor from /proc/cpuinfo */
823     if ( (fp = fopen("/proc/cpuinfo", "r")) != NULL)
824     {
825         while ( (vendor == GMX_CPUID_VENDOR_UNKNOWN) && (fgets(buffer, sizeof(buffer), fp) != NULL))
826         {
827             chomp_substring_before_colon(buffer, before_colon, sizeof(before_colon));
828             /* Intel/AMD use "vendor_id", IBM "vendor"(?) or "model". Fujitsu "manufacture". Add others if you have them! */
829             if (!strcmp(before_colon, "vendor_id")
830                 || !strcmp(before_colon, "vendor")
831                 || !strcmp(before_colon, "manufacture")
832                 || !strcmp(before_colon, "model"))
833             {
834                 chomp_substring_after_colon(buffer, after_colon, sizeof(after_colon));
835                 for (i = GMX_CPUID_VENDOR_UNKNOWN; i < GMX_CPUID_NVENDORS; i++)
836                 {
837                     /* Be liberal and accept if we find the vendor
838                      * string (or alternative string) anywhere. Using
839                      * strcasestr() would be non-portable. */
840                     if (strstr(after_colon, gmx_cpuid_vendor_string[i])
841                         || strstr(after_colon, gmx_cpuid_vendor_string_alternative[i]))
842                     {
843                         vendor = i;
844                     }
845                 }
846             }
847         }
848     }
849     fclose(fp);
850 #endif
851
852     return vendor;
853 }
854
855
856
857 int
858 gmx_cpuid_topology(gmx_cpuid_t        cpuid,
859                    int *              nprocessors,
860                    int *              npackages,
861                    int *              ncores_per_package,
862                    int *              nhwthreads_per_core,
863                    const int **       package_id,
864                    const int **       core_id,
865                    const int **       hwthread_id,
866                    const int **       locality_order)
867 {
868     int rc;
869
870     if (cpuid->have_cpu_topology)
871     {
872         *nprocessors          = cpuid->nproc;
873         *npackages            = cpuid->npackages;
874         *ncores_per_package   = cpuid->ncores_per_package;
875         *nhwthreads_per_core  = cpuid->nhwthreads_per_core;
876         *package_id           = cpuid->package_id;
877         *core_id              = cpuid->core_id;
878         *hwthread_id          = cpuid->hwthread_id;
879         *locality_order       = cpuid->locality_order;
880         rc                    = 0;
881     }
882     else
883     {
884         rc = -1;
885     }
886     return rc;
887 }
888
889
890 enum gmx_cpuid_x86_smt
891 gmx_cpuid_x86_smt(gmx_cpuid_t cpuid)
892 {
893     enum gmx_cpuid_x86_smt rc;
894
895     if (cpuid->have_cpu_topology)
896     {
897         rc = (cpuid->nhwthreads_per_core > 1) ? GMX_CPUID_X86_SMT_ENABLED : GMX_CPUID_X86_SMT_DISABLED;
898     }
899     else if (cpuid->vendor == GMX_CPUID_VENDOR_AMD || gmx_cpuid_feature(cpuid, GMX_CPUID_FEATURE_X86_HTT) == 0)
900     {
901         rc = GMX_CPUID_X86_SMT_DISABLED;
902     }
903     else
904     {
905         rc = GMX_CPUID_X86_SMT_CANNOTDETECT;
906     }
907     return rc;
908 }
909
910
911 int
912 gmx_cpuid_init               (gmx_cpuid_t *              pcpuid)
913 {
914     gmx_cpuid_t cpuid;
915     int         i;
916     FILE *      fp;
917     char        buffer[255], buffer2[255];
918     int         found_brand;
919
920     cpuid = malloc(sizeof(*cpuid));
921
922     *pcpuid = cpuid;
923
924     for (i = 0; i < GMX_CPUID_NFEATURES; i++)
925     {
926         cpuid->feature[i] = 0;
927     }
928
929     cpuid->have_cpu_topology   = 0;
930     cpuid->nproc               = 0;
931     cpuid->npackages           = 0;
932     cpuid->ncores_per_package  = 0;
933     cpuid->nhwthreads_per_core = 0;
934     cpuid->package_id          = NULL;
935     cpuid->core_id             = NULL;
936     cpuid->hwthread_id         = NULL;
937     cpuid->locality_order      = NULL;
938
939     cpuid->vendor = cpuid_check_vendor();
940
941     switch (cpuid->vendor)
942     {
943 #ifdef GMX_CPUID_X86
944         case GMX_CPUID_VENDOR_INTEL:
945             cpuid_check_intel_x86(cpuid);
946             break;
947         case GMX_CPUID_VENDOR_AMD:
948             cpuid_check_amd_x86(cpuid);
949             break;
950 #endif
951         default:
952             /* Default value */
953             strncpy(cpuid->brand, "Unknown CPU brand", GMX_CPUID_BRAND_MAXLEN);
954 #if defined(__linux__) || defined(__linux)
955             /* General Linux. Try to get CPU type from /proc/cpuinfo */
956             if ( (fp = fopen("/proc/cpuinfo", "r")) != NULL)
957             {
958                 found_brand = 0;
959                 while ( (found_brand == 0) && (fgets(buffer, sizeof(buffer), fp) != NULL))
960                 {
961                     chomp_substring_before_colon(buffer, buffer2, sizeof(buffer2));
962                     /* Intel uses "model name", Fujitsu and IBM "cpu". */
963                     if (!strcmp(buffer2, "model name") || !strcmp(buffer2, "cpu"))
964                     {
965                         chomp_substring_after_colon(buffer, cpuid->brand, GMX_CPUID_BRAND_MAXLEN);
966                         found_brand = 1;
967                     }
968                 }
969             }
970             fclose(fp);
971 #endif
972             cpuid->family         = 0;
973             cpuid->model          = 0;
974             cpuid->stepping       = 0;
975
976             for (i = 0; i < GMX_CPUID_NFEATURES; i++)
977             {
978                 cpuid->feature[i] = 0;
979             }
980             cpuid->feature[GMX_CPUID_FEATURE_CANNOTDETECT] = 1;
981             break;
982     }
983     return 0;
984 }
985
986
987
988 void
989 gmx_cpuid_done               (gmx_cpuid_t              cpuid)
990 {
991     free(cpuid);
992 }
993
994
995 int
996 gmx_cpuid_formatstring       (gmx_cpuid_t              cpuid,
997                               char *                   str,
998                               int                      n)
999 {
1000     int                     c;
1001     int                     i;
1002     enum gmx_cpuid_feature  feature;
1003
1004 #ifdef _MSC_VER
1005     _snprintf(str, n,
1006               "Vendor: %s\n"
1007               "Brand:  %s\n"
1008               "Family: %2d  Model: %2d  Stepping: %2d\n"
1009               "Features:",
1010               gmx_cpuid_vendor_string[gmx_cpuid_vendor(cpuid)],
1011               gmx_cpuid_brand(cpuid),
1012               gmx_cpuid_family(cpuid), gmx_cpuid_model(cpuid), gmx_cpuid_stepping(cpuid));
1013 #else
1014     snprintf(str, n,
1015              "Vendor: %s\n"
1016              "Brand:  %s\n"
1017              "Family: %2d  Model: %2d  Stepping: %2d\n"
1018              "Features:",
1019              gmx_cpuid_vendor_string[gmx_cpuid_vendor(cpuid)],
1020              gmx_cpuid_brand(cpuid),
1021              gmx_cpuid_family(cpuid), gmx_cpuid_model(cpuid), gmx_cpuid_stepping(cpuid));
1022 #endif
1023
1024     str[n-1] = '\0';
1025     c        = strlen(str);
1026     n       -= c;
1027     str     += c;
1028
1029     for (feature = GMX_CPUID_FEATURE_CANNOTDETECT; feature < GMX_CPUID_NFEATURES; feature++)
1030     {
1031         if (gmx_cpuid_feature(cpuid, feature) == 1)
1032         {
1033 #ifdef _MSC_VER
1034             _snprintf(str, n, " %s", gmx_cpuid_feature_string[feature]);
1035 #else
1036             snprintf(str, n, " %s", gmx_cpuid_feature_string[feature]);
1037 #endif
1038             str[n-1] = '\0';
1039             c        = strlen(str);
1040             n       -= c;
1041             str     += c;
1042         }
1043     }
1044 #ifdef _MSC_VER
1045     _snprintf(str, n, "\n");
1046 #else
1047     snprintf(str, n, "\n");
1048 #endif
1049     str[n-1] = '\0';
1050
1051     return 0;
1052 }
1053
1054
1055
1056 enum gmx_cpuid_simd
1057 gmx_cpuid_simd_suggest  (gmx_cpuid_t                 cpuid)
1058 {
1059     enum gmx_cpuid_simd  tmpsimd;
1060
1061     tmpsimd = GMX_CPUID_SIMD_NONE;
1062
1063     if (gmx_cpuid_vendor(cpuid) == GMX_CPUID_VENDOR_INTEL)
1064     {
1065         if (gmx_cpuid_feature(cpuid, GMX_CPUID_FEATURE_X86_AVX2))
1066         {
1067             tmpsimd = GMX_CPUID_SIMD_X86_AVX2_256;
1068         }
1069         else if (gmx_cpuid_feature(cpuid, GMX_CPUID_FEATURE_X86_AVX))
1070         {
1071             tmpsimd = GMX_CPUID_SIMD_X86_AVX_256;
1072         }
1073         else if (gmx_cpuid_feature(cpuid, GMX_CPUID_FEATURE_X86_SSE4_1))
1074         {
1075             tmpsimd = GMX_CPUID_SIMD_X86_SSE4_1;
1076         }
1077         else if (gmx_cpuid_feature(cpuid, GMX_CPUID_FEATURE_X86_SSE2))
1078         {
1079             tmpsimd = GMX_CPUID_SIMD_X86_SSE2;
1080         }
1081     }
1082     else if (gmx_cpuid_vendor(cpuid) == GMX_CPUID_VENDOR_AMD)
1083     {
1084         if (gmx_cpuid_feature(cpuid, GMX_CPUID_FEATURE_X86_AVX))
1085         {
1086             tmpsimd = GMX_CPUID_SIMD_X86_AVX_128_FMA;
1087         }
1088         else if (gmx_cpuid_feature(cpuid, GMX_CPUID_FEATURE_X86_SSE4_1))
1089         {
1090             tmpsimd = GMX_CPUID_SIMD_X86_SSE4_1;
1091         }
1092         else if (gmx_cpuid_feature(cpuid, GMX_CPUID_FEATURE_X86_SSE2))
1093         {
1094             tmpsimd = GMX_CPUID_SIMD_X86_SSE2;
1095         }
1096     }
1097     else if (gmx_cpuid_vendor(cpuid) == GMX_CPUID_VENDOR_FUJITSU)
1098     {
1099         if (strstr(gmx_cpuid_brand(cpuid), "SPARC64"))
1100         {
1101             tmpsimd = GMX_CPUID_SIMD_SPARC64_HPC_ACE;
1102         }
1103     }
1104     else if (gmx_cpuid_vendor(cpuid) == GMX_CPUID_VENDOR_IBM)
1105     {
1106         if (strstr(gmx_cpuid_brand(cpuid), "A2"))
1107         {
1108             tmpsimd = GMX_CPUID_SIMD_IBM_QPX;
1109         }
1110     }
1111     return tmpsimd;
1112 }
1113
1114
1115
1116 int
1117 gmx_cpuid_simd_check(gmx_cpuid_t   cpuid,
1118                      FILE *        log,
1119                      int           print_to_stderr)
1120 {
1121     int                           rc;
1122     char                          str[1024];
1123     enum gmx_cpuid_simd           simd;
1124
1125     simd = gmx_cpuid_simd_suggest(cpuid);
1126
1127     rc = (simd != compiled_simd);
1128
1129     gmx_cpuid_formatstring(cpuid, str, 1023);
1130     str[1023] = '\0';
1131
1132     if (log != NULL)
1133     {
1134         fprintf(log,
1135                 "\nDetecting CPU SIMD instructions.\nPresent hardware specification:\n"
1136                 "%s"
1137                 "SIMD instructions most likely to fit this hardware: %s\n"
1138                 "SIMD instructions selected at GROMACS compile time: %s\n\n",
1139                 str,
1140                 gmx_cpuid_simd_string[simd],
1141                 gmx_cpuid_simd_string[compiled_simd]);
1142     }
1143
1144     if (rc != 0)
1145     {
1146         if (log != NULL)
1147         {
1148             fprintf(log, "\nBinary not matching hardware - you might be losing performance.\n"
1149                     "SIMD instructions most likely to fit this hardware: %s\n"
1150                     "SIMD instructions selected at GROMACS compile time: %s\n\n",
1151                     gmx_cpuid_simd_string[simd],
1152                     gmx_cpuid_simd_string[compiled_simd]);
1153         }
1154         if (print_to_stderr)
1155         {
1156             fprintf(stderr, "Compiled SIMD instructions: %s (Gromacs could use %s on this machine, which is better)\n",
1157                     gmx_cpuid_simd_string[compiled_simd],
1158                     gmx_cpuid_simd_string[simd]);
1159         }
1160     }
1161     return rc;
1162 }
1163
1164
1165 #ifdef GMX_CPUID_STANDALONE
1166 /* Stand-alone program to enable queries of CPU features from Cmake.
1167  * Note that you need to check inline ASM capabilities before compiling and set
1168  * -DGMX_X86_GCC_INLINE_ASM for the cpuid instruction to work...
1169  */
1170 int
1171 main(int argc, char **argv)
1172 {
1173     gmx_cpuid_t                   cpuid;
1174     enum gmx_cpuid_simd           simd;
1175     int                           i, cnt;
1176
1177     if (argc < 2)
1178     {
1179         fprintf(stdout,
1180                 "Usage:\n\n%s [flags]\n\n"
1181                 "Available flags:\n"
1182                 "-vendor        Print CPU vendor.\n"
1183                 "-brand         Print CPU brand string.\n"
1184                 "-family        Print CPU family version.\n"
1185                 "-model         Print CPU model version.\n"
1186                 "-stepping      Print CPU stepping version.\n"
1187                 "-features      Print CPU feature flags.\n"
1188                 "-simd          Print suggested GROMACS SIMD instructions.\n",
1189                 argv[0]);
1190         exit(0);
1191     }
1192
1193     gmx_cpuid_init(&cpuid);
1194
1195     if (!strncmp(argv[1], "-vendor", 3))
1196     {
1197         printf("%s\n", gmx_cpuid_vendor_string[cpuid->vendor]);
1198     }
1199     else if (!strncmp(argv[1], "-brand", 3))
1200     {
1201         printf("%s\n", cpuid->brand);
1202     }
1203     else if (!strncmp(argv[1], "-family", 3))
1204     {
1205         printf("%d\n", cpuid->family);
1206     }
1207     else if (!strncmp(argv[1], "-model", 3))
1208     {
1209         printf("%d\n", cpuid->model);
1210     }
1211     else if (!strncmp(argv[1], "-stepping", 3))
1212     {
1213         printf("%d\n", cpuid->stepping);
1214     }
1215     else if (!strncmp(argv[1], "-features", 3))
1216     {
1217         cnt = 0;
1218         for (i = 0; i < GMX_CPUID_NFEATURES; i++)
1219         {
1220             if (cpuid->feature[i] == 1)
1221             {
1222                 if (cnt++ > 0)
1223                 {
1224                     printf(" ");
1225                 }
1226                 printf("%s", gmx_cpuid_feature_string[i]);
1227             }
1228         }
1229         printf("\n");
1230     }
1231     else if (!strncmp(argv[1], "-simd", 3))
1232     {
1233         simd = gmx_cpuid_simd_suggest(cpuid);
1234         fprintf(stdout, "%s\n", gmx_cpuid_simd_string[simd]);
1235     }
1236
1237     gmx_cpuid_done(cpuid);
1238
1239
1240     return 0;
1241 }
1242
1243 #endif