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 56 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 77 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 if (sign) 85 out_bits += values[i].rnd_downward_offset; 86 else 87 out_bits += values[i].rnd_upward_offset; 88 break; 89 case FE_DOWNWARD: 90 // Use conditionals instead of ternary operator to work around gcc's 91 // -Wconversion false positive bug: 92 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101537 93 if (sign) 94 out_bits += values[i].rnd_upward_offset; 95 else 96 out_bits += values[i].rnd_downward_offset; 97 break; 98 case FE_TONEAREST: 99 out_bits += values[i].rnd_tonearest_offset; 100 break; 101 } 102 T result = FPBits<T>(out_bits).get_val(); 103 if (sign) 104 result = -result; 105 106 return result; 107 } 108 } 109 return cpp::nullopt; 110 } 111 }; 112 113 // Helper functions to set results for exceptional cases. 114 template <typename T> LIBC_INLINE T round_result_slightly_down(T value_rn) { 115 volatile T tmp = value_rn; 116 tmp -= FPBits<T>::min_normal().get_val(); 117 return tmp; 118 } 119 120 template <typename T> LIBC_INLINE T round_result_slightly_up(T value_rn) { 121 volatile T tmp = value_rn; 122 tmp += FPBits<T>::min_normal().get_val(); 123 return tmp; 124 } 125 126 #if defined(LIBC_TYPES_HAS_FLOAT16) && \ 127 !defined(LIBC_TARGET_CPU_HAS_FAST_FLOAT16_OPS) 128 template <> LIBC_INLINE float16 round_result_slightly_down(float16 value_rn) { 129 volatile float tmp = value_rn; 130 tmp -= FPBits<float16>::min_normal().get_val(); 131 return cast<float16>(tmp); 132 } 133 134 template <> LIBC_INLINE float16 round_result_slightly_up(float16 value_rn) { 135 volatile float tmp = value_rn; 136 tmp += FPBits<float16>::min_normal().get_val(); 137 return cast<float16>(tmp); 138 } 139 #endif 140 141 } // namespace fputil 142 143 } // namespace LIBC_NAMESPACE_DECL 144 145 #endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_EXCEPT_VALUE_UTILS_H 146