| /* |
| * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #include "system_wrappers/include/denormal_disabler.h" |
| |
| #include "rtc_base/checks.h" |
| |
| namespace webrtc { |
| namespace { |
| |
| #if defined(WEBRTC_ARCH_X86_FAMILY) && defined(__clang__) |
| #define WEBRTC_DENORMAL_DISABLER_X86_SUPPORTED |
| #endif |
| |
| #if defined(WEBRTC_DENORMAL_DISABLER_X86_SUPPORTED) || \ |
| defined(WEBRTC_ARCH_ARM_FAMILY) |
| #define WEBRTC_DENORMAL_DISABLER_SUPPORTED |
| #endif |
| |
| constexpr int kUnspecifiedStatusWord = -1; |
| |
| #if defined(WEBRTC_DENORMAL_DISABLER_SUPPORTED) |
| |
| // Control register bit mask to disable denormals on the hardware. |
| #if defined(WEBRTC_DENORMAL_DISABLER_X86_SUPPORTED) |
| // On x86 two bits are used: flush-to-zero (FTZ) and denormals-are-zero (DAZ). |
| constexpr int kDenormalBitMask = 0x8040; |
| #elif defined(WEBRTC_ARCH_ARM_FAMILY) |
| // On ARM one bit is used: flush-to-zero (FTZ). |
| constexpr int kDenormalBitMask = 1 << 24; |
| #endif |
| |
| // Reads the relevant CPU control register and returns its value for supported |
| // architectures and compilers. Otherwise returns `kUnspecifiedStatusWord`. |
| int ReadStatusWord() { |
| int result = kUnspecifiedStatusWord; |
| #if defined(WEBRTC_DENORMAL_DISABLER_X86_SUPPORTED) |
| asm volatile("stmxcsr %0" : "=m"(result)); |
| #elif defined(WEBRTC_ARCH_ARM_FAMILY) && defined(WEBRTC_ARCH_32_BITS) |
| asm volatile("vmrs %[result], FPSCR" : [result] "=r"(result)); |
| #elif defined(WEBRTC_ARCH_ARM_FAMILY) && defined(WEBRTC_ARCH_64_BITS) |
| asm volatile("mrs %x[result], FPCR" : [result] "=r"(result)); |
| #endif |
| return result; |
| } |
| |
| // Writes `status_word` in the relevant CPU control register if the architecture |
| // and the compiler are supported. |
| void SetStatusWord(int status_word) { |
| #if defined(WEBRTC_DENORMAL_DISABLER_X86_SUPPORTED) |
| asm volatile("ldmxcsr %0" : : "m"(status_word)); |
| #elif defined(WEBRTC_ARCH_ARM_FAMILY) && defined(WEBRTC_ARCH_32_BITS) |
| asm volatile("vmsr FPSCR, %[src]" : : [src] "r"(status_word)); |
| #elif defined(WEBRTC_ARCH_ARM_FAMILY) && defined(WEBRTC_ARCH_64_BITS) |
| asm volatile("msr FPCR, %x[src]" : : [src] "r"(status_word)); |
| #endif |
| } |
| |
| // Returns true if the status word indicates that denormals are enabled. |
| constexpr bool DenormalsEnabled(int status_word) { |
| return (status_word & kDenormalBitMask) != kDenormalBitMask; |
| } |
| |
| #endif // defined(WEBRTC_DENORMAL_DISABLER_SUPPORTED) |
| |
| } // namespace |
| |
| #if defined(WEBRTC_DENORMAL_DISABLER_SUPPORTED) |
| DenormalDisabler::DenormalDisabler() : DenormalDisabler(/*enabled=*/true) {} |
| |
| DenormalDisabler::DenormalDisabler(bool enabled) |
| : status_word_(enabled ? ReadStatusWord() : kUnspecifiedStatusWord), |
| disabling_activated_(enabled && DenormalsEnabled(status_word_)) { |
| if (disabling_activated_) { |
| RTC_DCHECK_NE(status_word_, kUnspecifiedStatusWord); |
| SetStatusWord(status_word_ | kDenormalBitMask); |
| RTC_DCHECK(!DenormalsEnabled(ReadStatusWord())); |
| } |
| } |
| |
| bool DenormalDisabler::IsSupported() { |
| return true; |
| } |
| |
| DenormalDisabler::~DenormalDisabler() { |
| if (disabling_activated_) { |
| RTC_DCHECK_NE(status_word_, kUnspecifiedStatusWord); |
| SetStatusWord(status_word_); |
| } |
| } |
| #else |
| DenormalDisabler::DenormalDisabler() : DenormalDisabler(/*enabled=*/false) {} |
| |
| DenormalDisabler::DenormalDisabler(bool enabled) |
| : status_word_(kUnspecifiedStatusWord), disabling_activated_(false) {} |
| |
| bool DenormalDisabler::IsSupported() { |
| return false; |
| } |
| |
| DenormalDisabler::~DenormalDisabler() = default; |
| #endif |
| |
| } // namespace webrtc |