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