add new debug bit (DebugTimestamps) that adds timestamps to all debug messages
[ardour.git] / libs / pbd / fpu.cc
1 /*
2  * Copyright (C) 2007-2016 Paul Davis <paul@linuxaudiosystems.com>
3  * Copyright (C) 2009-2012 David Robillard <d@drobilla.net>
4  * Copyright (C) 2013-2015 John Emmas <john@creativepost.co.uk>
5  * Copyright (C) 2015-2019 Robin Gareus <robin@gareus.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "libpbd-config.h"
23
24 #define _XOPEN_SOURCE 600
25 #include <cstring> // for memset
26 #include <cstdlib>
27 #include <stdint.h>
28 #include <assert.h>
29
30 #ifdef PLATFORM_WINDOWS
31 #include <intrin.h>
32 #endif
33
34 #include "pbd/compose.h"
35 #include "pbd/fpu.h"
36 #include "pbd/error.h"
37
38 #include "pbd/i18n.h"
39
40 using namespace PBD;
41 using namespace std;
42
43 FPU* FPU::_instance (0);
44
45 #if ( (defined __x86_64__) || (defined __i386__) || (defined _M_X64) || (defined _M_IX86) ) // ARCH_X86
46 #ifndef PLATFORM_WINDOWS
47
48 /* use __cpuid() as the name to match the MSVC/mingw intrinsic */
49
50 static void
51 __cpuid(int regs[4], int cpuid_leaf)
52 {
53         asm volatile (
54 #if defined(__i386__)
55                         "pushl %%ebx;\n\t"
56 #endif
57                         "cpuid;\n\t"
58                         "movl %%eax, (%1);\n\t"
59                         "movl %%ebx, 4(%1);\n\t"
60                         "movl %%ecx, 8(%1);\n\t"
61                         "movl %%edx, 12(%1);\n\t"
62 #if defined(__i386__)
63                         "popl %%ebx;\n\t"
64 #endif
65                         :"=a" (cpuid_leaf) /* %eax clobbered by CPUID */
66                         :"S" (regs), "a" (cpuid_leaf)
67                         :
68 #if !defined(__i386__)
69                         "%ebx",
70 #endif
71                         "%ecx", "%edx", "memory");
72 }
73
74 #endif /* !PLATFORM_WINDOWS */
75
76 #ifndef HAVE_XGETBV // Allow definition by build system
77         #if defined(__MINGW32__) && defined(__MINGW64_VERSION_MAJOR) && __MINGW64_VERSION_MAJOR >= 5
78                 #define HAVE_XGETBV
79         #elif defined(_MSC_VER) && _MSC_VER >= 1600
80                 // '_xgetbv()' was only available from VC10 onwards
81                 #define HAVE_XGETBV
82         #endif
83 #endif
84
85 #ifndef HAVE_XGETBV
86
87 #ifdef COMPILER_MSVC
88
89 // '_xgetbv()' was only available from VC10 onwards
90 __declspec(noinline) static uint64_t
91 _xgetbv (uint32_t xcr)
92 {
93         return 0;
94
95         // N.B.  The following would probably work for a pre-VC10 build,
96         // although it might suffer from optimization issues.  We'd need
97         // to place this function into its own (unoptimized) source file.
98         __asm {
99                          mov ecx, [xcr]
100                          __asm _emit 0x0f __asm _emit 0x01 __asm _emit 0xd0 /*xgetbv*/
101         }
102 }
103
104 #else
105
106 static uint64_t
107 _xgetbv (uint32_t xcr)
108 {
109 #ifdef __APPLE__
110         /* it would be nice to make this work on OS X but as long we use veclib,
111            we don't really need to know about SSE/AVX on that platform.
112         */
113         return 0;
114 #else
115         uint32_t eax, edx;
116         __asm__ volatile ("xgetbv" : "=a" (eax), "=d" (edx) : "c" (xcr));
117         return (static_cast<uint64_t>(edx) << 32) | eax;
118 #endif
119 }
120
121 #endif /* !COMPILER_MSVC */
122 #endif /* !HAVE_XGETBV */
123 #endif /* ARCH_X86 */
124
125 #ifndef _XCR_XFEATURE_ENABLED_MASK
126 #define _XCR_XFEATURE_ENABLED_MASK 0
127 #endif
128
129 FPU*
130 FPU::instance()
131 {
132         if (!_instance) {
133                 _instance = new FPU;
134         }
135
136         return _instance;
137 }
138
139 void
140 FPU::destroy ()
141 {
142         delete _instance;
143         _instance = 0;
144 }
145
146 FPU::FPU ()
147         : _flags ((Flags) 0)
148 {
149         if (_instance) {
150                 error << _("FPU object instantiated more than once") << endmsg;
151         }
152
153         if (getenv("ARDOUR_FPU_FLAGS")) {
154                 _flags = Flags (atoi (getenv("ARDOUR_FPU_FLAGS")));
155                 return;
156         }
157
158 #if !( (defined __x86_64__) || (defined __i386__) || (defined _M_X64) || (defined _M_IX86) ) // !ARCH_X86
159         /* Non-Intel architecture, nothing to do here */
160         return;
161 #else
162
163         /* Get the CPU vendor just for kicks
164          *
165          * __cpuid with an InfoType argument of 0 returns the number of
166          * valid Ids in CPUInfo[0] and the CPU identification string in
167          * the other three array elements. The CPU identification string is
168          * not in linear order. The code below arranges the information
169          * in a human readable form. The human readable order is CPUInfo[1] |
170          * CPUInfo[3] | CPUInfo[2]. CPUInfo[2] and CPUInfo[3] are swapped
171          * before using memcpy to copy these three array elements to cpu_string.
172          */
173
174         int cpu_info[4];
175         char cpu_string[48];
176         string cpu_vendor;
177
178         __cpuid (cpu_info, 0);
179
180         int num_ids = cpu_info[0];
181         std::swap(cpu_info[2], cpu_info[3]);
182         memcpy(cpu_string, &cpu_info[1], 3 * sizeof(cpu_info[1]));
183         cpu_vendor.assign(cpu_string, 3 * sizeof(cpu_info[1]));
184
185         info << string_compose (_("CPU vendor: %1"), cpu_vendor) << endmsg;
186
187         if (num_ids > 0) {
188
189                 /* Now get CPU/FPU flags */
190
191                 __cpuid (cpu_info, 1);
192
193                 if ((cpu_info[2] & (1<<27)) /* OSXSAVE */ &&
194                     (cpu_info[2] & (1<<28) /* AVX */) &&
195                     ((_xgetbv (_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6)) { /* OS really supports XSAVE */
196                         info << _("AVX-capable processor") << endmsg;
197                         _flags = Flags (_flags | (HasAVX) );
198                 }
199
200                 if (cpu_info[3] & (1<<25)) {
201                         _flags = Flags (_flags | (HasSSE|HasFlushToZero));
202                 }
203
204                 if (cpu_info[3] & (1<<26)) {
205                         _flags = Flags (_flags | HasSSE2);
206                 }
207
208                 /* Figure out CPU/FPU denormal handling capabilities */
209
210                 if (cpu_info[3] & (1 << 24)) {
211
212                         char** fxbuf = 0;
213
214                         /* DAZ wasn't available in the first version of SSE. Since
215                            setting a reserved bit in MXCSR causes a general protection
216                            fault, we need to be able to check the availability of this
217                            feature without causing problems. To do this, one needs to
218                            set up a 512-byte area of memory to save the SSE state to,
219                            using fxsave, and then one needs to inspect bytes 28 through
220                            31 for the MXCSR_MASK value. If bit 6 is set, DAZ is
221                            supported, otherwise, it isn't.
222                         */
223
224 #ifndef HAVE_POSIX_MEMALIGN
225 #  ifdef PLATFORM_WINDOWS
226                         fxbuf = (char **) _aligned_malloc (sizeof (char *), 16);
227                         assert (fxbuf);
228                         *fxbuf = (char *) _aligned_malloc (512, 16);
229                         assert (*fxbuf);
230 #  else
231 #  warning using default malloc for aligned memory
232                         fxbuf = (char **) malloc (sizeof (char *));
233                         assert (fxbuf);
234                         *fxbuf = (char *) malloc (512);
235                         assert (*fxbuf);
236 #  endif
237 #else
238                         (void) posix_memalign ((void **) &fxbuf, 16, sizeof (char *));
239                         assert (fxbuf);
240                         (void) posix_memalign ((void **) fxbuf, 16, 512);
241                         assert (*fxbuf);
242 #endif
243
244                         memset (*fxbuf, 0, 512);
245
246 #ifdef COMPILER_MSVC
247                         char *buf = *fxbuf;
248                         __asm {
249                                 mov eax, buf
250                                         fxsave   [eax]
251                                         };
252 #else
253                         asm volatile (
254                                 "fxsave (%0)"
255                                 :
256                                 : "r" (*fxbuf)
257                                 : "memory"
258                                 );
259 #endif
260
261                         uint32_t mxcsr_mask = *((uint32_t*) &((*fxbuf)[28]));
262
263                         /* if the mask is zero, set its default value (from intel specs) */
264
265                         if (mxcsr_mask == 0) {
266                                 mxcsr_mask = 0xffbf;
267                         }
268
269                         if (mxcsr_mask & (1<<6)) {
270                                 _flags = Flags (_flags | HasDenormalsAreZero);
271                         }
272
273 #if !defined HAVE_POSIX_MEMALIGN && defined PLATFORM_WINDOWS
274                         _aligned_free (*fxbuf);
275                         _aligned_free (fxbuf);
276 #else
277                         free (*fxbuf);
278                         free (fxbuf);
279 #endif
280                 }
281
282                 /* finally get the CPU brand */
283
284                 __cpuid (cpu_info, 0x80000000);
285
286                 const int parameter_end = 0x80000004;
287                 string cpu_brand;
288
289                 if (cpu_info[0] >= parameter_end) {
290                         char* cpu_string_ptr = cpu_string;
291
292                         for (int parameter = 0x80000002; parameter <= parameter_end &&
293                                      cpu_string_ptr < &cpu_string[sizeof(cpu_string)]; parameter++) {
294                                 __cpuid(cpu_info, parameter);
295                                 memcpy(cpu_string_ptr, cpu_info, sizeof(cpu_info));
296                                 cpu_string_ptr += sizeof(cpu_info);
297                         }
298                         cpu_brand.assign(cpu_string, cpu_string_ptr - cpu_string);
299                         info << string_compose (_("CPU brand: %1"), cpu_brand) << endmsg;
300                 }
301         }
302 #endif /* !ARCH_X86 */
303 }
304
305 FPU::~FPU ()
306 {
307 }