xref: /aosp_15_r20/external/llvm-libc/src/__support/FPUtil/except_value_utils.h (revision 71db0c75aadcf003ffe3238005f61d7618a3fead)
1 //===-- Common header for helpers to set exceptional values -----*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_EXCEPT_VALUE_UTILS_H
10 #define LLVM_LIBC_SRC___SUPPORT_FPUTIL_EXCEPT_VALUE_UTILS_H
11 
12 #include "FEnvImpl.h"
13 #include "FPBits.h"
14 #include "cast.h"
15 #include "rounding_mode.h"
16 #include "src/__support/CPP/optional.h"
17 #include "src/__support/macros/config.h"
18 #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
19 #include "src/__support/macros/properties/cpu_features.h"
20 #include "src/__support/macros/properties/types.h"
21 
22 namespace LIBC_NAMESPACE_DECL {
23 
24 namespace fputil {
25 
26 // This file contains utility functions and classes to manage exceptional values
27 // when there are many of them.
28 //
29 // Example usage:
30 //
31 // Define list of exceptional inputs and outputs:
32 //   static constexpr int N = ...;  // Number of exceptional values.
33 //   static constexpr fputil::ExceptValues<StorageType, N> Excepts {
34 //     <list of input bits, output bits and offsets>
35 //   };
36 //
37 // Check for exceptional inputs:
38 //   if (auto r = Excepts.lookup(x_bits); LIBC_UNLIKELY(r.has_value()))
39 //     return r.value();
40 
41 template <typename T, size_t N> struct ExceptValues {
42   static_assert(cpp::is_floating_point_v<T>, "Must be a floating point type.");
43 
44   using StorageType = typename FPBits<T>::StorageType;
45 
46   struct Mapping {
47     StorageType input;
48     StorageType rnd_towardzero_result;
49     StorageType rnd_upward_offset;
50     StorageType rnd_downward_offset;
51     StorageType rnd_tonearest_offset;
52   };
53 
54   Mapping values[N];
55 
lookupExceptValues56   LIBC_INLINE constexpr cpp::optional<T> lookup(StorageType x_bits) const {
57     for (size_t i = 0; i < N; ++i) {
58       if (LIBC_UNLIKELY(x_bits == values[i].input)) {
59         StorageType out_bits = values[i].rnd_towardzero_result;
60         switch (fputil::quick_get_round()) {
61         case FE_UPWARD:
62           out_bits += values[i].rnd_upward_offset;
63           break;
64         case FE_DOWNWARD:
65           out_bits += values[i].rnd_downward_offset;
66           break;
67         case FE_TONEAREST:
68           out_bits += values[i].rnd_tonearest_offset;
69           break;
70         }
71         return FPBits<T>(out_bits).get_val();
72       }
73     }
74     return cpp::nullopt;
75   }
76 
lookup_oddExceptValues77   LIBC_INLINE constexpr cpp::optional<T> lookup_odd(StorageType x_abs,
78                                                     bool sign) const {
79     for (size_t i = 0; i < N; ++i) {
80       if (LIBC_UNLIKELY(x_abs == values[i].input)) {
81         StorageType out_bits = values[i].rnd_towardzero_result;
82         switch (fputil::quick_get_round()) {
83         case FE_UPWARD:
84           out_bits += sign ? values[i].rnd_downward_offset
85                            : values[i].rnd_upward_offset;
86           break;
87         case FE_DOWNWARD:
88           out_bits += sign ? values[i].rnd_upward_offset
89                            : values[i].rnd_downward_offset;
90           break;
91         case FE_TONEAREST:
92           out_bits += values[i].rnd_tonearest_offset;
93           break;
94         }
95         T result = FPBits<T>(out_bits).get_val();
96         if (sign)
97           result = -result;
98 
99         return result;
100       }
101     }
102     return cpp::nullopt;
103   }
104 };
105 
106 // Helper functions to set results for exceptional cases.
round_result_slightly_down(T value_rn)107 template <typename T> LIBC_INLINE T round_result_slightly_down(T value_rn) {
108   volatile T tmp = value_rn;
109   tmp -= FPBits<T>::min_normal().get_val();
110   return tmp;
111 }
112 
round_result_slightly_up(T value_rn)113 template <typename T> LIBC_INLINE T round_result_slightly_up(T value_rn) {
114   volatile T tmp = value_rn;
115   tmp += FPBits<T>::min_normal().get_val();
116   return tmp;
117 }
118 
119 #if defined(LIBC_TYPES_HAS_FLOAT16) &&                                         \
120     !defined(LIBC_TARGET_CPU_HAS_FAST_FLOAT16_OPS)
round_result_slightly_down(float16 value_rn)121 template <> LIBC_INLINE float16 round_result_slightly_down(float16 value_rn) {
122   volatile float tmp = value_rn;
123   tmp -= FPBits<float16>::min_normal().get_val();
124   return cast<float16>(tmp);
125 }
126 
round_result_slightly_up(float16 value_rn)127 template <> LIBC_INLINE float16 round_result_slightly_up(float16 value_rn) {
128   volatile float tmp = value_rn;
129   tmp += FPBits<float16>::min_normal().get_val();
130   return cast<float16>(tmp);
131 }
132 #endif
133 
134 } // namespace fputil
135 
136 } // namespace LIBC_NAMESPACE_DECL
137 
138 #endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_EXCEPT_VALUE_UTILS_H
139