1 /**
2 Save/Restore floating-point FPU/SSE state for every plug-in callback.
3  
4 Copyright: Guillaume Piolat 2015-2016.
5 License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
6 */
7 module dplug.core.fpcontrol;
8 
9 version(X86)
10     version = isX86;
11 version(X86_64)
12     version = isX86;
13 
14 import inteli.xmmintrin;
15 
16 /// This struct ensures that floating point is save/restored and set consistently in plugin callbacks.
17 struct FPControl
18 {
19     void initialize() nothrow @nogc
20     {
21         // Get current SSE control word (emulated on ARM)
22         storedMXCSR = _mm_getcsr();
23 
24         // Set current SSE control word (emulated on ARM)
25         _mm_setcsr(0x9fff); // Flush denormals to zero + Denormals Are Zeros + all exception masked
26 
27         version(isX86)
28         {
29             // store FPU control word
30             fpuState = getFPUControlState();
31 
32             // masks all floating-point exceptions, sets rounding to nearest, and sets the x87 FPU precision to 64 bits
33             ushort control = 0x037f;
34 
35             // Looking for problems? Unmask all errors.
36             //control = 0x0340;
37 
38             // Looking for denormals only? This unmasks denormal creation and denormal use exceptions.
39             //control = 0x036d;
40 
41             setFPUControlState(control);
42         }
43     }
44 
45     ~this() nothrow @nogc
46     {
47         _mm_setcsr(storedMXCSR);
48 
49         version(isX86)
50         {        
51             // restore FPU control word
52             setFPUControlState(fpuState);
53         }
54     }
55 
56     version(isX86)
57     {
58         ushort fpuState;        
59     }
60     uint storedMXCSR;
61 }
62 
63 
64 version(isX86)
65 {
66     version(D_InlineAsm_X86)
67         version = InlineX86Asm;
68     else version(D_InlineAsm_X86_64)
69         version = InlineX86Asm;
70 
71     /// Gets FPU control register
72     ushort getFPUControlState() nothrow @nogc
73     {
74         version (InlineX86Asm)
75         {
76             short cont;
77             asm nothrow @nogc
78             {
79                 xor EAX, EAX;
80                 fstcw cont;
81             }
82             return cont;
83         }
84         else
85             static assert(0, "Unsupported");
86     }
87 
88     /// Sets FPU control register
89     void setFPUControlState(ushort newState) nothrow @nogc
90     {
91         // MAYDO: report that the naked version in Phobos is buggy on OSX
92         // it fills the control word with a random word which can create
93         // FP exceptions.
94         version (InlineX86Asm)
95         {
96             asm nothrow @nogc
97             {
98                 fclex;
99                 fldcw newState;
100             }
101         }
102         else
103             static assert(0, "Unsupported");
104     }
105 }
106 
107 unittest
108 {
109     // TEST FOR DENORMAL FLUSH TO ZERO
110 
111     FPControl control;
112     control.initialize();
113    
114    // Doesn't work since constant folder may use "real" precision.
115    /*
116 
117     // Trying to see if FTZ is working, 1e-37 is a very small normalized number
118     float denormal = 1e-37f * 0.1f;
119 
120     version(DigitalMars)
121     {
122         version(X86)
123         {
124             // DMD x86 32-bit may use FPU operations, hence not suppressing denormals         
125         }
126         else
127             assert(denormal == 0);
128     }
129     else
130         assert(denormal == 0);
131 
132     */
133 }