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