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