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