explain MXCSR shenanigans in libs/pbd/fpu.cc
[ardour.git] / libs / pbd / fpu.cc
1 #include "libpbd-config.h"
2
3 #define _XOPEN_SOURCE 600
4 #include <cstring> // for memset
5 #include <cstdlib>
6 #include <stdint.h>
7
8 #include "pbd/fpu.h"
9 #include "pbd/error.h"
10
11 #include "i18n.h"
12
13 using namespace PBD;
14 using namespace std;
15
16 FPU::FPU ()
17 {
18         unsigned long cpuflags = 0;
19
20         _flags = Flags (0);
21
22 #if !( (defined __x86_64__) || (defined __i386__) ) // !ARCH_X86
23         return;
24 #else
25
26         
27 #ifndef _LP64 //USE_X86_64_ASM
28         asm volatile (
29                 "mov $1, %%eax\n"
30                 "pushl %%ebx\n"
31                 "cpuid\n"
32                 "movl %%edx, %0\n"
33                 "popl %%ebx\n"
34                 : "=r" (cpuflags)
35                 : 
36                 : "%eax", "%ecx", "%edx"
37                 );
38         
39 #else
40         
41         /* asm notes: although we explicitly save&restore rbx, we must tell
42            gcc that ebx,rbx is clobbered so that it doesn't try to use it as an intermediate
43            register when storing rbx. gcc 4.3 didn't make this "mistake", but gcc 4.4
44            does, at least on x86_64.
45         */
46
47         asm volatile (
48                 "pushq %%rbx\n"
49                 "movq $1, %%rax\n"
50                 "cpuid\n"
51                 "movq %%rdx, %0\n"
52                 "popq %%rbx\n"
53                 : "=r" (cpuflags)
54                 : 
55                 : "%rax", "%rbx", "%rcx", "%rdx"
56                 );
57
58 #endif /* USE_X86_64_ASM */
59
60         if (cpuflags & (1<<25)) {
61                 _flags = Flags (_flags | (HasSSE|HasFlushToZero));
62         }
63
64         if (cpuflags & (1<<26)) {
65                 _flags = Flags (_flags | HasSSE2);
66         }
67
68         if (cpuflags & (1 << 24)) {
69                 
70                 char* fxbuf = 0;
71                 
72                 /* DAZ wasn't available in the first version of SSE. Since
73                    setting a reserved bit in MXCSR causes a general protection
74                    fault, we need to be able to check the availability of this
75                    feature without causing problems. To do this, one needs to
76                    set up a 512-byte area of memory to save the SSE state to,
77                    using fxsave, and then one needs to inspect bytes 28 through
78                    31 for the MXCSR_MASK value. If bit 6 is set, DAZ is
79                    supported, otherwise, it isn't.
80                 */
81                 
82 #ifdef NO_POSIX_MEMALIGN
83                 if ((fxbuf = (char *) malloc(512)) == 0)
84 #else
85                 if (posix_memalign ((void**)&fxbuf, 16, 512)) 
86 #endif                  
87                 {
88                         error << _("cannot allocate 16 byte aligned buffer for h/w feature detection") << endmsg;
89                 } else {
90                         
91                         memset (fxbuf, 0, 512);
92
93                         asm volatile (
94                                 "fxsave (%0)"
95                                 :
96                                 : "r" (fxbuf)
97                                 : "memory"
98                                 );
99                         
100                         uint32_t mxcsr_mask = *((uint32_t*) &fxbuf[28]);
101                         
102                         /* if the mask is zero, set its default value (from intel specs) */
103                         
104                         if (mxcsr_mask == 0) {
105                                 mxcsr_mask = 0xffbf;
106                         }
107                         
108                         if (mxcsr_mask & (1<<6)) {
109                                 _flags = Flags (_flags | HasDenormalsAreZero);
110                         } 
111
112                         free (fxbuf);
113                 }
114         }
115 #endif
116 }                       
117
118 FPU::~FPU ()
119 {
120 }