xref: /llvm-project/libc/src/__support/FPUtil/except_value_utils.h (revision ed199c8d7668c14e9e9e8e4bfa59793e3f7f0266)
1d883a4adSTue Ly //===-- Common header for helpers to set exceptional values -----*- C++ -*-===//
2d883a4adSTue Ly //
3d883a4adSTue Ly // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4d883a4adSTue Ly // See https://llvm.org/LICENSE.txt for license information.
5d883a4adSTue Ly // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6d883a4adSTue Ly //
7d883a4adSTue Ly //===----------------------------------------------------------------------===//
8d883a4adSTue Ly 
9270547f3SGuillaume Chatelet #ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_EXCEPT_VALUE_UTILS_H
10270547f3SGuillaume Chatelet #define LLVM_LIBC_SRC___SUPPORT_FPUTIL_EXCEPT_VALUE_UTILS_H
11d883a4adSTue Ly 
12d883a4adSTue Ly #include "FEnvImpl.h"
13d883a4adSTue Ly #include "FPBits.h"
14127349fcSOverMighty #include "cast.h"
15a9824312STue Ly #include "rounding_mode.h"
16a4d48e3bSTue Ly #include "src/__support/CPP/optional.h"
175ff3ff33SPetr Hosek #include "src/__support/macros/config.h"
18737e1cd1SGuillaume Chatelet #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
19127349fcSOverMighty #include "src/__support/macros/properties/cpu_features.h"
20127349fcSOverMighty #include "src/__support/macros/properties/types.h"
21d883a4adSTue Ly 
225ff3ff33SPetr Hosek namespace LIBC_NAMESPACE_DECL {
23d883a4adSTue Ly 
24d883a4adSTue Ly namespace fputil {
25d883a4adSTue Ly 
26a4d48e3bSTue Ly // This file contains utility functions and classes to manage exceptional values
27a4d48e3bSTue Ly // when there are many of them.
28a4d48e3bSTue Ly //
29a4d48e3bSTue Ly // Example usage:
30a4d48e3bSTue Ly //
31a4d48e3bSTue Ly // Define list of exceptional inputs and outputs:
32a4d48e3bSTue Ly //   static constexpr int N = ...;  // Number of exceptional values.
333546f4daSGuillaume Chatelet //   static constexpr fputil::ExceptValues<StorageType, N> Excepts {
34a4d48e3bSTue Ly //     <list of input bits, output bits and offsets>
35a4d48e3bSTue Ly //   };
36a4d48e3bSTue Ly //
37a4d48e3bSTue Ly // Check for exceptional inputs:
3829f8e076SGuillaume Chatelet //   if (auto r = Excepts.lookup(x_bits); LIBC_UNLIKELY(r.has_value()))
39a4d48e3bSTue Ly //     return r.value();
40a4d48e3bSTue Ly 
41a4d48e3bSTue Ly template <typename T, size_t N> struct ExceptValues {
42a4d48e3bSTue Ly   static_assert(cpp::is_floating_point_v<T>, "Must be a floating point type.");
43a4d48e3bSTue Ly 
443546f4daSGuillaume Chatelet   using StorageType = typename FPBits<T>::StorageType;
45a4d48e3bSTue Ly 
46a4d48e3bSTue Ly   struct Mapping {
473546f4daSGuillaume Chatelet     StorageType input;
483546f4daSGuillaume Chatelet     StorageType rnd_towardzero_result;
493546f4daSGuillaume Chatelet     StorageType rnd_upward_offset;
503546f4daSGuillaume Chatelet     StorageType rnd_downward_offset;
513546f4daSGuillaume Chatelet     StorageType rnd_tonearest_offset;
52d883a4adSTue Ly   };
53d883a4adSTue Ly 
54a4d48e3bSTue Ly   Mapping values[N];
55d883a4adSTue Ly 
563546f4daSGuillaume Chatelet   LIBC_INLINE constexpr cpp::optional<T> lookup(StorageType x_bits) const {
57a4d48e3bSTue Ly     for (size_t i = 0; i < N; ++i) {
5829f8e076SGuillaume Chatelet       if (LIBC_UNLIKELY(x_bits == values[i].input)) {
593546f4daSGuillaume Chatelet         StorageType out_bits = values[i].rnd_towardzero_result;
60a9824312STue Ly         switch (fputil::quick_get_round()) {
61d883a4adSTue Ly         case FE_UPWARD:
62a4d48e3bSTue Ly           out_bits += values[i].rnd_upward_offset;
63d883a4adSTue Ly           break;
64d883a4adSTue Ly         case FE_DOWNWARD:
65a4d48e3bSTue Ly           out_bits += values[i].rnd_downward_offset;
66d883a4adSTue Ly           break;
67d883a4adSTue Ly         case FE_TONEAREST:
68a4d48e3bSTue Ly           out_bits += values[i].rnd_tonearest_offset;
69d883a4adSTue Ly           break;
70d883a4adSTue Ly         }
71a4d48e3bSTue Ly         return FPBits<T>(out_bits).get_val();
72a4d48e3bSTue Ly       }
73a4d48e3bSTue Ly     }
74a4d48e3bSTue Ly     return cpp::nullopt;
75a4d48e3bSTue Ly   }
76a4d48e3bSTue Ly 
773546f4daSGuillaume Chatelet   LIBC_INLINE constexpr cpp::optional<T> lookup_odd(StorageType x_abs,
78ae2d8b49STue Ly                                                     bool sign) const {
79a4d48e3bSTue Ly     for (size_t i = 0; i < N; ++i) {
8029f8e076SGuillaume Chatelet       if (LIBC_UNLIKELY(x_abs == values[i].input)) {
813546f4daSGuillaume Chatelet         StorageType out_bits = values[i].rnd_towardzero_result;
82a9824312STue Ly         switch (fputil::quick_get_round()) {
83a4d48e3bSTue Ly         case FE_UPWARD:
84*ed199c8dSlntue           if (sign)
85*ed199c8dSlntue             out_bits += values[i].rnd_downward_offset;
86*ed199c8dSlntue           else
87*ed199c8dSlntue             out_bits += values[i].rnd_upward_offset;
88a4d48e3bSTue Ly           break;
89a4d48e3bSTue Ly         case FE_DOWNWARD:
90*ed199c8dSlntue           // Use conditionals instead of ternary operator to work around gcc's
91*ed199c8dSlntue           // -Wconversion false positive bug:
92*ed199c8dSlntue           // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101537
93*ed199c8dSlntue           if (sign)
94*ed199c8dSlntue             out_bits += values[i].rnd_upward_offset;
95*ed199c8dSlntue           else
96*ed199c8dSlntue             out_bits += values[i].rnd_downward_offset;
97a4d48e3bSTue Ly           break;
98a4d48e3bSTue Ly         case FE_TONEAREST:
99a4d48e3bSTue Ly           out_bits += values[i].rnd_tonearest_offset;
100a4d48e3bSTue Ly           break;
101a4d48e3bSTue Ly         }
102a4d48e3bSTue Ly         T result = FPBits<T>(out_bits).get_val();
103d883a4adSTue Ly         if (sign)
104d883a4adSTue Ly           result = -result;
105d883a4adSTue Ly 
106a4d48e3bSTue Ly         return result;
107d883a4adSTue Ly       }
108d883a4adSTue Ly     }
109a4d48e3bSTue Ly     return cpp::nullopt;
110d883a4adSTue Ly   }
111d883a4adSTue Ly };
112d883a4adSTue Ly 
113ae2d8b49STue Ly // Helper functions to set results for exceptional cases.
114da28593dSlntue template <typename T> LIBC_INLINE T round_result_slightly_down(T value_rn) {
115da28593dSlntue   volatile T tmp = value_rn;
1166b02d2f8SGuillaume Chatelet   tmp -= FPBits<T>::min_normal().get_val();
117ae2d8b49STue Ly   return tmp;
118ae2d8b49STue Ly }
119ae2d8b49STue Ly 
120da28593dSlntue template <typename T> LIBC_INLINE T round_result_slightly_up(T value_rn) {
121da28593dSlntue   volatile T tmp = value_rn;
1226b02d2f8SGuillaume Chatelet   tmp += FPBits<T>::min_normal().get_val();
123ae2d8b49STue Ly   return tmp;
124ae2d8b49STue Ly }
125ae2d8b49STue Ly 
126127349fcSOverMighty #if defined(LIBC_TYPES_HAS_FLOAT16) &&                                         \
127127349fcSOverMighty     !defined(LIBC_TARGET_CPU_HAS_FAST_FLOAT16_OPS)
128127349fcSOverMighty template <> LIBC_INLINE float16 round_result_slightly_down(float16 value_rn) {
129127349fcSOverMighty   volatile float tmp = value_rn;
130127349fcSOverMighty   tmp -= FPBits<float16>::min_normal().get_val();
131127349fcSOverMighty   return cast<float16>(tmp);
132127349fcSOverMighty }
133127349fcSOverMighty 
134127349fcSOverMighty template <> LIBC_INLINE float16 round_result_slightly_up(float16 value_rn) {
135127349fcSOverMighty   volatile float tmp = value_rn;
136127349fcSOverMighty   tmp += FPBits<float16>::min_normal().get_val();
137127349fcSOverMighty   return cast<float16>(tmp);
138127349fcSOverMighty }
139127349fcSOverMighty #endif
140127349fcSOverMighty 
141d883a4adSTue Ly } // namespace fputil
142d883a4adSTue Ly 
1435ff3ff33SPetr Hosek } // namespace LIBC_NAMESPACE_DECL
144d883a4adSTue Ly 
145270547f3SGuillaume Chatelet #endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_EXCEPT_VALUE_UTILS_H
146