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