blob: bb9c05643c12e0c4c75a4c720f588b99e5101caf [file] [log] [blame]
/*
* 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