diff --git a/libc/src/__support/math/expf.h b/libc/src/__support/math/expf.h index 88c151492a041..f3accce54885f 100644 --- a/libc/src/__support/math/expf.h +++ b/libc/src/__support/math/expf.h @@ -109,6 +109,22 @@ static constexpr float expf(float x) { return static_cast(exp_hi * exp_mid * exp_lo); } +#pragma STDC FENV_ACCESS ON + +// Directional rounding version of expf. +LIBC_INLINE static float expf(float x, int rounding_mode) { + int current_rounding_mode = fputil::get_round(); + if (rounding_mode == current_rounding_mode) + return expf(x); + + fputil::set_round(rounding_mode); + float result = expf(x); + fputil::set_round(current_rounding_mode); + return result; +} + +#pragma STDC FENV_ACCESS DEFAULT + } // namespace math } // namespace LIBC_NAMESPACE_DECL diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt index 0849bec26d56a..f1a1b39a60652 100644 --- a/llvm/CMakeLists.txt +++ b/llvm/CMakeLists.txt @@ -644,6 +644,17 @@ endif() set(LLVM_ENABLE_Z3_SOLVER_DEFAULT "${Z3_FOUND}") +set(LLVM_INTEGRATE_LIBC "OFF" CACHE STRING "Use LLVM libc code directly if available.") + +if(LLVM_INTEGRATE_LIBC) + message(STATUS "LLVM_INTEGRATE_LIBC is ${LLVM_INTEGRATE_LIBC}") + include(FindLibcCommonUtils) + if(NOT TARGET llvm-libc-common-utilities) + message(STATUS "LLVM_INTEGRATE_LIBC is set but cannot find LLVM libc at ${libc_path}.") + set(LLVM_INTEGRATE_LIBC OFF) + endif() +endif() + if( LLVM_TARGETS_TO_BUILD STREQUAL "all" ) set( LLVM_TARGETS_TO_BUILD ${LLVM_ALL_TARGETS} ) diff --git a/llvm/include/llvm/ADT/APFloat.h b/llvm/include/llvm/ADT/APFloat.h index 13df838da3dad..b2bbd096a802c 100644 --- a/llvm/include/llvm/ADT/APFloat.h +++ b/llvm/include/llvm/ADT/APFloat.h @@ -1524,6 +1524,10 @@ class APFloat : public APFloatBase { friend APFloat frexp(const APFloat &X, int &Exp, roundingMode RM); friend IEEEFloat; friend DoubleAPFloat; + +#ifdef LLVM_INTEGRATE_LIBC + friend APFloat exp(const APFloat &X, roundingMode RM); +#endif // LLVM_INTEGRATE_LIBC }; static_assert(sizeof(APFloat) == sizeof(detail::IEEEFloat), @@ -1657,6 +1661,12 @@ inline APFloat maximumnum(const APFloat &A, const APFloat &B) { return A < B ? B : A; } +#ifdef LLVM_INTEGRATE_LIBC +/// Implement IEEE 754-2019 exp functions. +LLVM_READONLY +APFloat exp(const APFloat &X, RoundingMode RM = APFloat::rmNearestTiesToEven); +#endif // LLVM_INTEGRATE_LIBC + inline raw_ostream &operator<<(raw_ostream &OS, const APFloat &V) { V.print(OS); return OS; diff --git a/llvm/include/llvm/Config/llvm-config.h.cmake b/llvm/include/llvm/Config/llvm-config.h.cmake index 6d3c37cc8b194..30c4ed6fffc74 100644 --- a/llvm/include/llvm/Config/llvm-config.h.cmake +++ b/llvm/include/llvm/Config/llvm-config.h.cmake @@ -137,4 +137,7 @@ coverage bugs, and to 0 otherwise. */ #cmakedefine01 LLVM_ENABLE_DEBUGLOC_ORIGIN_TRACKING +/* Define if LLVM and clang uses LLVM libc for math computations. */ +#cmakedefine LLVM_INTEGRATE_LIBC + #endif diff --git a/llvm/lib/Support/APFloat.cpp b/llvm/lib/Support/APFloat.cpp index 5e0b29ffb2590..d2948d37a5cf0 100644 --- a/llvm/lib/Support/APFloat.cpp +++ b/llvm/lib/Support/APFloat.cpp @@ -28,6 +28,11 @@ #include #include +#ifdef LLVM_INTEGRATE_LIBC +// Shared headers from LLVM libc +#include "shared/math.h" +#endif // LLVM_INTEGRATE_LIBC + #define APFLOAT_DISPATCH_ON_SEMANTICS(METHOD_CALL) \ do { \ if (usesLayout(getSemantics())) \ @@ -5601,6 +5606,33 @@ float APFloat::convertToFloat() const { return Temp.getIEEE().convertToFloat(); } +#ifdef LLVM_INTEGRATE_LIBC +static constexpr int getFEnvRoundingMode(llvm::RoundingMode rm) { + switch (rm) { + case APFloat::rmTowardPositive: + return FE_UPWARD; + case APFloat::rmTowardNegative: + return FE_DOWNWARD; + case APFloat::rmTowardZero: + return FE_TOWARDZERO; + default: + // TODO: fix rmNearestTiesToAway for platform without FE_TONEARESTFROMZERO. + return FE_TONEAREST; + }; +} + +APFloat exp(const APFloat &X, RoundingMode rounding_mode) { + assert((&X.getSemantics() == (const llvm::fltSemantics *)&semIEEEsingle) && + "Float semantics is not IEEEsingle"); + if (&X.getSemantics() == (const llvm::fltSemantics *)&semIEEEsingle) { + float result = LIBC_NAMESPACE::shared::expf( + X.convertToFloat(), getFEnvRoundingMode(rounding_mode)); + return APFloat(result); + } + llvm_unreachable("Unexpected semantics"); +} +#endif // LLVM_INTEGRATE_LIBC + } // namespace llvm #undef APFLOAT_DISPATCH_ON_SEMANTICS diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt index 45d961e994a1a..aeeba93819227 100644 --- a/llvm/lib/Support/CMakeLists.txt +++ b/llvm/lib/Support/CMakeLists.txt @@ -379,3 +379,9 @@ if(LLVM_WITH_Z3) ${Z3_INCLUDE_DIR} ) endif() + +if(LLVM_INTEGRATE_LIBC) + set_property(TARGET LLVMSupport PROPERTY CXX_STANDARD 17) + target_include_directories(LLVMSupport PRIVATE "${LLVM_INCLUDE_DIR}/../../libc") + target_compile_options(LLVMSupport PRIVATE "-Wno-c99-extensions") # _Complex warnings. +endif() diff --git a/llvm/lib/Target/AMDGPU/AMDGPULibCalls.cpp b/llvm/lib/Target/AMDGPU/AMDGPULibCalls.cpp index 8767208d20ec9..1e2aa2839fc8f 100644 --- a/llvm/lib/Target/AMDGPU/AMDGPULibCalls.cpp +++ b/llvm/lib/Target/AMDGPU/AMDGPULibCalls.cpp @@ -1540,7 +1540,7 @@ bool AMDGPULibCalls::evaluateScalarMathFunc(const FuncInfo &FInfo, double &Res0, return true; case AMDGPULibFunc::EI_EXP: - Res0 = exp(opr0); + Res0 = std::exp(opr0); return true; case AMDGPULibFunc::EI_EXP2: diff --git a/llvm/unittests/ADT/APFloatTest.cpp b/llvm/unittests/ADT/APFloatTest.cpp index 7a5fd83cd9581..3eef9287b8abf 100644 --- a/llvm/unittests/ADT/APFloatTest.cpp +++ b/llvm/unittests/ADT/APFloatTest.cpp @@ -16,6 +16,7 @@ #include "llvm/Support/FormatVariadic.h" #include "gtest/gtest.h" #include +#include #include #include #include @@ -8356,4 +8357,80 @@ TEST(APFloatTest, hasSignBitInMSB) { EXPECT_FALSE(APFloat::hasSignBitInMSB(APFloat::Float8E8M0FNU())); } +#ifdef LLVM_INTEGRATE_LIBC +TEST(APFloatTest, expf) { + std::array allRoundingModes = { + APFloat::rmNearestTiesToEven, APFloat::rmTowardPositive, + APFloat::rmTowardNegative, APFloat::rmTowardZero}; + for (auto rm : allRoundingModes) { + // exp(+-0) = 1 for all rounding modes. + EXPECT_EQ(1.0f, llvm::exp(APFloat(0.0f), rm).convertToFloat()); + EXPECT_EQ(1.0f, llvm::exp(APFloat(-0.0f), rm).convertToFloat()); + // exp(+Inf) = +Inf for all rounding modes. + EXPECT_EQ(std::numeric_limits::infinity(), + llvm::exp(APFloat::getInf(APFloat::IEEEsingle(), false), rm) + .convertToFloat()); + // exp(-Inf) = 0 for all rounding modes. + EXPECT_EQ(0.0f, llvm::exp(APFloat::getInf(APFloat::IEEEsingle(), true), rm) + .convertToFloat()); + // exp(NaN) = NaN for all rounding modes. + EXPECT_TRUE(llvm::exp(APFloat::getNaN(APFloat::IEEEsingle()), rm).isNaN()); + } + // exp(1) + EXPECT_EQ( + 0x1.5bf0a8p1f, + llvm::exp(APFloat(1.0f), APFloat::rmNearestTiesToEven).convertToFloat()); + EXPECT_EQ( + 0x1.5bf0aap1f, + llvm::exp(APFloat(1.0f), APFloat::rmTowardPositive).convertToFloat()); + EXPECT_EQ( + 0x1.5bf0a8p1f, + llvm::exp(APFloat(1.0f), APFloat::rmTowardNegative).convertToFloat()); + EXPECT_EQ(0x1.5bf0a8p1f, + llvm::exp(APFloat(1.0f), APFloat::rmTowardZero).convertToFloat()); + // exp(float max) + EXPECT_EQ(std::numeric_limits::infinity(), + llvm::exp(APFloat::getLargest(APFloat::IEEEsingle(), false), + APFloat::rmNearestTiesToEven) + .convertToFloat()); + EXPECT_EQ(std::numeric_limits::infinity(), + llvm::exp(APFloat::getLargest(APFloat::IEEEsingle(), false), + APFloat::rmTowardPositive) + .convertToFloat()); + EXPECT_EQ(std::numeric_limits::max(), + llvm::exp(APFloat::getLargest(APFloat::IEEEsingle(), false), + APFloat::rmTowardNegative) + .convertToFloat()); + EXPECT_EQ(std::numeric_limits::max(), + llvm::exp(APFloat::getLargest(APFloat::IEEEsingle(), false), + APFloat::rmTowardZero) + .convertToFloat()); + // exp(min_denormal) + EXPECT_EQ(1.0f, llvm::exp(APFloat::getSmallest(APFloat::IEEEsingle(), false), + APFloat::rmNearestTiesToEven) + .convertToFloat()); + EXPECT_EQ(0x1.000002p0f, + llvm::exp(APFloat::getSmallest(APFloat::IEEEsingle(), false), + APFloat::rmTowardPositive) + .convertToFloat()); + EXPECT_EQ(1.0f, llvm::exp(APFloat::getSmallest(APFloat::IEEEsingle(), false), + APFloat::rmTowardNegative) + .convertToFloat()); + EXPECT_EQ(1.0f, llvm::exp(APFloat::getSmallest(APFloat::IEEEsingle(), false), + APFloat::rmTowardZero) + .convertToFloat()); + // Default rounding mode. + // exp(-1) + EXPECT_EQ(0x1.78b564p-2f, llvm::exp(APFloat(-1.0f)).convertToFloat()); + EXPECT_EQ( + 0x1.78b564p-2f, + llvm::exp(APFloat(-1.0f), APFloat::rmTowardPositive).convertToFloat()); + EXPECT_EQ( + 0x1.78b562p-2f, + llvm::exp(APFloat(-1.0f), APFloat::rmTowardNegative).convertToFloat()); + EXPECT_EQ(0x1.78b562p-2f, + llvm::exp(APFloat(-1.0f), APFloat::rmTowardZero).convertToFloat()); +} +#endif // LLVM_INTEGRATE_LIBC + } // namespace