• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <cmath>
14 #include <limits>
15 #include <vector>
16 
17 #include "rtc_base/checks.h"
18 #include "test/gtest.h"
19 
20 namespace webrtc {
21 namespace {
22 
23 constexpr float kSmallest = std::numeric_limits<float>::min();
24 
25 // Float values such that, if used as divisors of `kSmallest`, the division
26 // produces a denormal or zero depending on whether denormals are enabled.
27 constexpr float kDenormalDivisors[] = {123.125f, 97.0f, 32.0f, 5.0f, 1.5f};
28 
29 // Returns true if the result of `dividend` / `divisor` is a denormal.
30 // `dividend` and `divisor` must not be denormals.
DivisionIsDenormal(float dividend,float divisor)31 bool DivisionIsDenormal(float dividend, float divisor) {
32   RTC_DCHECK_GE(std::fabsf(dividend), kSmallest);
33   RTC_DCHECK_GE(std::fabsf(divisor), kSmallest);
34   volatile float division = dividend / divisor;
35   return division != 0.0f && std::fabsf(division) < kSmallest;
36 }
37 
38 }  // namespace
39 
40 class DenormalDisablerParametrization : public ::testing::TestWithParam<bool> {
41 };
42 
43 // Checks that +inf and -inf are not zeroed regardless of whether
44 // architecture and compiler are supported.
TEST_P(DenormalDisablerParametrization,InfNotZeroed)45 TEST_P(DenormalDisablerParametrization, InfNotZeroed) {
46   DenormalDisabler denormal_disabler(/*enabled=*/GetParam());
47   constexpr float kMax = std::numeric_limits<float>::max();
48   for (float x : {-2.0f, 2.0f}) {
49     SCOPED_TRACE(x);
50     volatile float multiplication = kMax * x;
51     EXPECT_TRUE(std::isinf(multiplication));
52   }
53 }
54 
55 // Checks that a NaN is not zeroed regardless of whether architecture and
56 // compiler are supported.
TEST_P(DenormalDisablerParametrization,NanNotZeroed)57 TEST_P(DenormalDisablerParametrization, NanNotZeroed) {
58   DenormalDisabler denormal_disabler(/*enabled=*/GetParam());
59   volatile float kNan = std::sqrt(-1.0f);
60   EXPECT_TRUE(std::isnan(kNan));
61 }
62 
63 INSTANTIATE_TEST_SUITE_P(DenormalDisabler,
64                          DenormalDisablerParametrization,
65                          ::testing::Values(false, true),
__anonc2cc9fec0202(const ::testing::TestParamInfo<bool>& info) 66                          [](const ::testing::TestParamInfo<bool>& info) {
67                            return info.param ? "enabled" : "disabled";
68                          });
69 
70 // Checks that denormals are not zeroed if `DenormalDisabler` is disabled and
71 // architecture and compiler are supported.
TEST(DenormalDisabler,DoNotZeroDenormalsIfDisabled)72 TEST(DenormalDisabler, DoNotZeroDenormalsIfDisabled) {
73   if (!DenormalDisabler::IsSupported()) {
74     GTEST_SKIP() << "Unsupported platform.";
75   }
76   ASSERT_TRUE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]))
77       << "Precondition not met: denormals must be enabled.";
78   DenormalDisabler denormal_disabler(/*enabled=*/false);
79   for (float x : kDenormalDivisors) {
80     SCOPED_TRACE(x);
81     EXPECT_TRUE(DivisionIsDenormal(-kSmallest, x));
82     EXPECT_TRUE(DivisionIsDenormal(kSmallest, x));
83   }
84 }
85 
86 // Checks that denormals are zeroed if `DenormalDisabler` is enabled if
87 // architecture and compiler are supported.
TEST(DenormalDisabler,ZeroDenormals)88 TEST(DenormalDisabler, ZeroDenormals) {
89   if (!DenormalDisabler::IsSupported()) {
90     GTEST_SKIP() << "Unsupported platform.";
91   }
92   DenormalDisabler denormal_disabler(/*enabled=*/true);
93   for (float x : kDenormalDivisors) {
94     SCOPED_TRACE(x);
95     EXPECT_FALSE(DivisionIsDenormal(-kSmallest, x));
96     EXPECT_FALSE(DivisionIsDenormal(kSmallest, x));
97   }
98 }
99 
100 // Checks that the `DenormalDisabler` dtor re-enables denormals if previously
101 // enabled and architecture and compiler are supported.
TEST(DenormalDisabler,RestoreDenormalsEnabled)102 TEST(DenormalDisabler, RestoreDenormalsEnabled) {
103   if (!DenormalDisabler::IsSupported()) {
104     GTEST_SKIP() << "Unsupported platform.";
105   }
106   ASSERT_TRUE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]))
107       << "Precondition not met: denormals must be enabled.";
108   {
109     DenormalDisabler denormal_disabler(/*enabled=*/true);
110     ASSERT_FALSE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]));
111   }
112   EXPECT_TRUE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]));
113 }
114 
115 // Checks that the `DenormalDisabler` dtor keeps denormals disabled if
116 // architecture and compiler are supported and if previously disabled - i.e.,
117 // nested usage is supported.
TEST(DenormalDisabler,ZeroDenormalsNested)118 TEST(DenormalDisabler, ZeroDenormalsNested) {
119   if (!DenormalDisabler::IsSupported()) {
120     GTEST_SKIP() << "Unsupported platform.";
121   }
122   DenormalDisabler d1(/*enabled=*/true);
123   ASSERT_FALSE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]));
124   {
125     DenormalDisabler d2(/*enabled=*/true);
126     ASSERT_FALSE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]));
127   }
128   EXPECT_FALSE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]));
129 }
130 
131 // Checks that `DenormalDisabler` does not zero denormals if architecture and
132 // compiler are not supported.
TEST(DenormalDisabler,DoNotZeroDenormalsIfUnsupported)133 TEST(DenormalDisabler, DoNotZeroDenormalsIfUnsupported) {
134   if (DenormalDisabler::IsSupported()) {
135     GTEST_SKIP() << "This test should only run on platforms without support "
136                     "for DenormalDisabler.";
137   }
138   DenormalDisabler denormal_disabler(/*enabled=*/true);
139   for (float x : kDenormalDivisors) {
140     SCOPED_TRACE(x);
141     EXPECT_TRUE(DivisionIsDenormal(-kSmallest, x));
142     EXPECT_TRUE(DivisionIsDenormal(kSmallest, x));
143   }
144 }
145 
146 }  // namespace webrtc
147