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