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