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 }