blob: 1006c67c3ca9766a7bf79488c8a98d8e316cc189 [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 <cmath>
#include <limits>
#include <vector>
#include "rtc_base/checks.h"
#include "test/gtest.h"
namespace webrtc {
namespace {
constexpr float kSmallest = std::numeric_limits<float>::min();
// Float values such that, if used as divisors of `kSmallest`, the division
// produces a denormal or zero depending on whether denormals are enabled.
constexpr float kDenormalDivisors[] = {123.125f, 97.0f, 32.0f, 5.0f, 1.5f};
// Returns true if the result of `dividend` / `divisor` is a denormal.
// `dividend` and `divisor` must not be denormals.
bool DivisionIsDenormal(float dividend, float divisor) {
RTC_DCHECK_GE(std::fabsf(dividend), kSmallest);
RTC_DCHECK_GE(std::fabsf(divisor), kSmallest);
volatile float division = dividend / divisor;
return division != 0.0f && std::fabsf(division) < kSmallest;
}
} // namespace
class DenormalDisablerParametrization : public ::testing::TestWithParam<bool> {
};
// Checks that +inf and -inf are not zeroed regardless of whether
// architecture and compiler are supported.
TEST_P(DenormalDisablerParametrization, InfNotZeroed) {
DenormalDisabler denormal_disabler(/*enabled=*/GetParam());
constexpr float kMax = std::numeric_limits<float>::max();
for (float x : {-2.0f, 2.0f}) {
SCOPED_TRACE(x);
volatile float multiplication = kMax * x;
EXPECT_TRUE(std::isinf(multiplication));
}
}
// Checks that a NaN is not zeroed regardless of whether architecture and
// compiler are supported.
TEST_P(DenormalDisablerParametrization, NanNotZeroed) {
DenormalDisabler denormal_disabler(/*enabled=*/GetParam());
volatile float kNan = std::sqrt(-1.0f);
EXPECT_TRUE(std::isnan(kNan));
}
INSTANTIATE_TEST_SUITE_P(DenormalDisabler,
DenormalDisablerParametrization,
::testing::Values(false, true),
[](const ::testing::TestParamInfo<bool>& info) {
return info.param ? "enabled" : "disabled";
});
// Checks that denormals are not zeroed if `DenormalDisabler` is disabled and
// architecture and compiler are supported.
TEST(DenormalDisabler, DoNotZeroDenormalsIfDisabled) {
if (!DenormalDisabler::IsSupported()) {
GTEST_SKIP() << "Unsupported platform.";
}
ASSERT_TRUE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]))
<< "Precondition not met: denormals must be enabled.";
DenormalDisabler denormal_disabler(/*enabled=*/false);
for (float x : kDenormalDivisors) {
SCOPED_TRACE(x);
EXPECT_TRUE(DivisionIsDenormal(-kSmallest, x));
EXPECT_TRUE(DivisionIsDenormal(kSmallest, x));
}
}
// Checks that denormals are zeroed if `DenormalDisabler` is enabled if
// architecture and compiler are supported.
TEST(DenormalDisabler, ZeroDenormals) {
if (!DenormalDisabler::IsSupported()) {
GTEST_SKIP() << "Unsupported platform.";
}
DenormalDisabler denormal_disabler(/*enabled=*/true);
for (float x : kDenormalDivisors) {
SCOPED_TRACE(x);
EXPECT_FALSE(DivisionIsDenormal(-kSmallest, x));
EXPECT_FALSE(DivisionIsDenormal(kSmallest, x));
}
}
// Checks that the `DenormalDisabler` dtor re-enables denormals if previously
// enabled and architecture and compiler are supported.
TEST(DenormalDisabler, RestoreDenormalsEnabled) {
if (!DenormalDisabler::IsSupported()) {
GTEST_SKIP() << "Unsupported platform.";
}
ASSERT_TRUE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]))
<< "Precondition not met: denormals must be enabled.";
{
DenormalDisabler denormal_disabler(/*enabled=*/true);
ASSERT_FALSE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]));
}
EXPECT_TRUE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]));
}
// Checks that the `DenormalDisabler` dtor keeps denormals disabled if
// architecture and compiler are supported and if previously disabled - i.e.,
// nested usage is supported.
TEST(DenormalDisabler, ZeroDenormalsNested) {
if (!DenormalDisabler::IsSupported()) {
GTEST_SKIP() << "Unsupported platform.";
}
DenormalDisabler d1(/*enabled=*/true);
ASSERT_FALSE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]));
{
DenormalDisabler d2(/*enabled=*/true);
ASSERT_FALSE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]));
}
EXPECT_FALSE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]));
}
// Checks that `DenormalDisabler` does not zero denormals if architecture and
// compiler are not supported.
TEST(DenormalDisabler, DoNotZeroDenormalsIfUnsupported) {
if (DenormalDisabler::IsSupported()) {
GTEST_SKIP() << "This test should only run on platforms without support "
"for DenormalDisabler.";
}
DenormalDisabler denormal_disabler(/*enabled=*/true);
for (float x : kDenormalDivisors) {
SCOPED_TRACE(x);
EXPECT_TRUE(DivisionIsDenormal(-kSmallest, x));
EXPECT_TRUE(DivisionIsDenormal(kSmallest, x));
}
}
} // namespace webrtc