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