merge with master
[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 #ifndef  COMPILER_MSVC
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 #include "pbd/fpu.h"
29 #include "pbd/error.h"
30
31 #include "i18n.h"
32
33 using namespace PBD;
34 using namespace std;
35
36 FPU::FPU ()
37 {
38         unsigned long cpuflags = 0;
39
40         _flags = Flags (0);
41
42 #if defined(__MINGW64__) // Vkamyshniy: under __MINGW64__ the assembler code below is not compiled
43         return;
44 #endif
45
46 #if !( (defined __x86_64__) || (defined __i386__) ) // !ARCH_X86
47         return;
48 #else
49
50 #ifndef _LP64 //USE_X86_64_ASM
51         asm volatile (
52                 "mov $1, %%eax\n"
53                 "pushl %%ebx\n"
54                 "cpuid\n"
55                 "movl %%edx, %0\n"
56                 "popl %%ebx\n"
57                 : "=r" (cpuflags)
58                 : 
59                 : "%eax", "%ecx", "%edx"
60                 );
61         
62 #else
63         
64         /* asm notes: although we explicitly save&restore rbx, we must tell
65            gcc that ebx,rbx is clobbered so that it doesn't try to use it as an intermediate
66            register when storing rbx. gcc 4.3 didn't make this "mistake", but gcc 4.4
67            does, at least on x86_64.
68         */
69
70         asm volatile (
71                 "pushq %%rbx\n"
72                 "movq $1, %%rax\n"
73                 "cpuid\n"
74                 "movq %%rdx, %0\n"
75                 "popq %%rbx\n"
76                 : "=r" (cpuflags)
77                 : 
78                 : "%rax", "%rbx", "%rcx", "%rdx"
79                 );
80
81 #endif /* USE_X86_64_ASM */
82
83         if (cpuflags & (1<<25)) {
84                 _flags = Flags (_flags | (HasSSE|HasFlushToZero));
85         }
86
87         if (cpuflags & (1<<26)) {
88                 _flags = Flags (_flags | HasSSE2);
89         }
90
91         if (cpuflags & (1 << 24)) {
92                 
93                 char** fxbuf = 0;
94                 
95                 /* DAZ wasn't available in the first version of SSE. Since
96                    setting a reserved bit in MXCSR causes a general protection
97                    fault, we need to be able to check the availability of this
98                    feature without causing problems. To do this, one needs to
99                    set up a 512-byte area of memory to save the SSE state to,
100                    using fxsave, and then one needs to inspect bytes 28 through
101                    31 for the MXCSR_MASK value. If bit 6 is set, DAZ is
102                    supported, otherwise, it isn't.
103                 */
104                 
105 #ifndef HAVE_POSIX_MEMALIGN
106                 fxbuf = (char **) malloc (sizeof (char *));
107                 assert (fxbuf);
108                 *fxbuf = (char *) malloc (512);
109                 assert (*fxbuf);
110 #else
111                 (void) posix_memalign ((void **) &fxbuf, 16, sizeof (char *));
112                 assert (fxbuf);
113                 (void) posix_memalign ((void **) fxbuf, 16, 512);
114                 assert (*fxbuf);
115 #endif                  
116                 
117                 memset (*fxbuf, 0, 512);
118                 
119                 asm volatile (
120                         "fxsave (%0)"
121                         :
122                         : "r" (*fxbuf)
123                         : "memory"
124                         );
125                 
126                 uint32_t mxcsr_mask = *((uint32_t*) &((*fxbuf)[28]));
127                 
128                 /* if the mask is zero, set its default value (from intel specs) */
129                 
130                 if (mxcsr_mask == 0) {
131                         mxcsr_mask = 0xffbf;
132                 }
133                 
134                 if (mxcsr_mask & (1<<6)) {
135                         _flags = Flags (_flags | HasDenormalsAreZero);
136                 } 
137                 
138                 free (*fxbuf);
139                 free (fxbuf);
140         }
141 #endif
142 }                       
143
144 FPU::~FPU ()
145 {
146 }
147
148 #else  // COMPILER_MSVC
149         const char* pbd_fpu = "pbd/msvc/fpu.cc takes precedence over this file";
150 #endif // COMPILER_MSVC