1 /* 2 * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "system_wrappers/include/denormal_disabler.h" 12 13 #include "rtc_base/checks.h" 14 15 namespace webrtc { 16 namespace { 17 18 #if defined(WEBRTC_ARCH_X86_FAMILY) && defined(__clang__) 19 #define WEBRTC_DENORMAL_DISABLER_X86_SUPPORTED 20 #endif 21 22 #if defined(WEBRTC_DENORMAL_DISABLER_X86_SUPPORTED) || \ 23 defined(WEBRTC_ARCH_ARM_FAMILY) 24 #define WEBRTC_DENORMAL_DISABLER_SUPPORTED 25 #endif 26 27 constexpr int kUnspecifiedStatusWord = -1; 28 29 #if defined(WEBRTC_DENORMAL_DISABLER_SUPPORTED) 30 31 // Control register bit mask to disable denormals on the hardware. 32 #if defined(WEBRTC_DENORMAL_DISABLER_X86_SUPPORTED) 33 // On x86 two bits are used: flush-to-zero (FTZ) and denormals-are-zero (DAZ). 34 constexpr int kDenormalBitMask = 0x8040; 35 #elif defined(WEBRTC_ARCH_ARM_FAMILY) 36 // On ARM one bit is used: flush-to-zero (FTZ). 37 constexpr int kDenormalBitMask = 1 << 24; 38 #endif 39 40 // Reads the relevant CPU control register and returns its value for supported 41 // architectures and compilers. Otherwise returns `kUnspecifiedStatusWord`. ReadStatusWord()42int ReadStatusWord() { 43 int result = kUnspecifiedStatusWord; 44 #if defined(WEBRTC_DENORMAL_DISABLER_X86_SUPPORTED) 45 asm volatile("stmxcsr %0" : "=m"(result)); 46 #elif defined(WEBRTC_ARCH_ARM_FAMILY) && defined(WEBRTC_ARCH_32_BITS) 47 asm volatile("vmrs %[result], FPSCR" : [result] "=r"(result)); 48 #elif defined(WEBRTC_ARCH_ARM_FAMILY) && defined(WEBRTC_ARCH_64_BITS) 49 asm volatile("mrs %x[result], FPCR" : [result] "=r"(result)); 50 #endif 51 return result; 52 } 53 54 // Writes `status_word` in the relevant CPU control register if the architecture 55 // and the compiler are supported. SetStatusWord(int status_word)56void SetStatusWord(int status_word) { 57 #if defined(WEBRTC_DENORMAL_DISABLER_X86_SUPPORTED) 58 asm volatile("ldmxcsr %0" : : "m"(status_word)); 59 #elif defined(WEBRTC_ARCH_ARM_FAMILY) && defined(WEBRTC_ARCH_32_BITS) 60 asm volatile("vmsr FPSCR, %[src]" : : [src] "r"(status_word)); 61 #elif defined(WEBRTC_ARCH_ARM_FAMILY) && defined(WEBRTC_ARCH_64_BITS) 62 asm volatile("msr FPCR, %x[src]" : : [src] "r"(status_word)); 63 #endif 64 } 65 66 // Returns true if the status word indicates that denormals are enabled. DenormalsEnabled(int status_word)67constexpr bool DenormalsEnabled(int status_word) { 68 return (status_word & kDenormalBitMask) != kDenormalBitMask; 69 } 70 71 #endif // defined(WEBRTC_DENORMAL_DISABLER_SUPPORTED) 72 73 } // namespace 74 75 #if defined(WEBRTC_DENORMAL_DISABLER_SUPPORTED) DenormalDisabler(bool enabled)76DenormalDisabler::DenormalDisabler(bool enabled) 77 : status_word_(enabled ? ReadStatusWord() : kUnspecifiedStatusWord), 78 disabling_activated_(enabled && DenormalsEnabled(status_word_)) { 79 if (disabling_activated_) { 80 RTC_DCHECK_NE(status_word_, kUnspecifiedStatusWord); 81 SetStatusWord(status_word_ | kDenormalBitMask); 82 RTC_DCHECK(!DenormalsEnabled(ReadStatusWord())); 83 } 84 } 85 IsSupported()86bool DenormalDisabler::IsSupported() { 87 return true; 88 } 89 ~DenormalDisabler()90DenormalDisabler::~DenormalDisabler() { 91 if (disabling_activated_) { 92 RTC_DCHECK_NE(status_word_, kUnspecifiedStatusWord); 93 SetStatusWord(status_word_); 94 } 95 } 96 #else DenormalDisabler(bool enabled)97DenormalDisabler::DenormalDisabler(bool enabled) 98 : status_word_(kUnspecifiedStatusWord), disabling_activated_(false) {} 99 IsSupported()100bool DenormalDisabler::IsSupported() { 101 return false; 102 } 103 104 DenormalDisabler::~DenormalDisabler() = default; 105 #endif 106 107 } // namespace webrtc 108