1 /* 2 * Copyright (C) 2011, Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY 17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 */ 24 25 #ifndef DenormalDisabler_h 26 #define DenormalDisabler_h 27 28 #include "wtf/CPU.h" 29 #include "wtf/MathExtras.h" 30 #include <float.h> 31 32 namespace blink { 33 34 // Deal with denormals. They can very seriously impact performance on x86. 35 36 // Define HAVE_DENORMAL if we support flushing denormals to zero. 37 38 #if OS(WIN) && COMPILER(MSVC) 39 // Windows compiled using MSVC with SSE2 40 #define HAVE_DENORMAL 1 41 #endif 42 43 #if COMPILER(GCC) && (CPU(X86) || CPU(X86_64)) 44 // X86 chips can flush denormals 45 #define HAVE_DENORMAL 1 46 #endif 47 48 #if CPU(ARM) || CPU(ARM64) 49 #define HAVE_DENORMAL 1 50 #endif 51 52 #if HAVE(DENORMAL) 53 class DenormalDisabler { 54 public: DenormalDisabler()55 DenormalDisabler() 56 : m_savedCSR(0) 57 { 58 disableDenormals(); 59 } 60 ~DenormalDisabler()61 ~DenormalDisabler() 62 { 63 restoreState(); 64 } 65 66 // This is a nop if we can flush denormals to zero in hardware. flushDenormalFloatToZero(float f)67 static inline float flushDenormalFloatToZero(float f) 68 { 69 return f; 70 } 71 private: 72 unsigned m_savedCSR; 73 74 #if COMPILER(GCC) && (CPU(X86) || CPU(X86_64)) disableDenormals()75 inline void disableDenormals() 76 { 77 m_savedCSR = getCSR(); 78 setCSR(m_savedCSR | 0x8040); 79 } 80 restoreState()81 inline void restoreState() 82 { 83 setCSR(m_savedCSR); 84 } 85 getCSR()86 inline int getCSR() 87 { 88 int result; 89 asm volatile("stmxcsr %0" : "=m" (result)); 90 return result; 91 } 92 setCSR(int a)93 inline void setCSR(int a) 94 { 95 int temp = a; 96 asm volatile("ldmxcsr %0" : : "m" (temp)); 97 } 98 99 #elif OS(WIN) && COMPILER(MSVC) disableDenormals()100 inline void disableDenormals() 101 { 102 // Save the current state, and set mode to flush denormals. 103 // 104 // http://stackoverflow.com/questions/637175/possible-bug-in-controlfp-s-may-not-restore-control-word-correctly 105 _controlfp_s(&m_savedCSR, 0, 0); 106 unsigned unused; 107 _controlfp_s(&unused, _DN_FLUSH, _MCW_DN); 108 } 109 restoreState()110 inline void restoreState() 111 { 112 unsigned unused; 113 _controlfp_s(&unused, m_savedCSR, _MCW_DN); 114 } 115 #elif CPU(ARM) || CPU(ARM64) disableDenormals()116 inline void disableDenormals() 117 { 118 m_savedCSR = getStatusWord(); 119 // Bit 24 is the flush-to-zero mode control bit. Setting it to 1 flushes denormals to 0. 120 setStatusWord(m_savedCSR | (1 << 24)); 121 } 122 restoreState()123 inline void restoreState() 124 { 125 setStatusWord(m_savedCSR); 126 } 127 getStatusWord()128 inline int getStatusWord() 129 { 130 int result; 131 #if CPU(ARM64) 132 asm volatile("mrs %[result], FPCR" : [result] "=r" (result)); 133 #else 134 asm volatile("vmrs %[result], FPSCR" : [result] "=r" (result)); 135 #endif 136 return result; 137 } 138 setStatusWord(int a)139 inline void setStatusWord(int a) 140 { 141 #if CPU(ARM64) 142 asm volatile("msr FPCR, %[src]" : : [src] "r" (a)); 143 #else 144 asm volatile("vmsr FPSCR, %[src]" : : [src] "r" (a)); 145 #endif 146 } 147 148 #endif 149 150 }; 151 152 #else 153 // FIXME: add implementations for other architectures and compilers 154 class DenormalDisabler { 155 public: 156 DenormalDisabler() { } 157 158 // Assume the worst case that other architectures and compilers 159 // need to flush denormals to zero manually. 160 static inline float flushDenormalFloatToZero(float f) 161 { 162 return (fabs(f) < FLT_MIN) ? 0.0f : f; 163 } 164 }; 165 166 #endif 167 168 } // namespace blink 169 170 #undef HAVE_DENORMAL 171 #endif // DenormalDisabler_h 172