[libc] [llvm] [APFloat] Add exp function for APFloat::IEEESsingle using expf implementation from LLVM libc. (PR #143959)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Jun 13 07:19:12 PDT 2025
https://github.com/lntue updated https://github.com/llvm/llvm-project/pull/143959
>From aeaac932cde03103ba4831dfd54d943098fad716 Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Thu, 12 Jun 2025 19:53:28 +0000
Subject: [PATCH 1/3] [APFloat] Add exp function for APFloat::IEEESsingle using
expf implementation from LLVM libc.
---
llvm/CMakeLists.txt | 11 +++++++
llvm/include/llvm/ADT/APFloat.h | 5 +++
llvm/include/llvm/Config/llvm-config.h.cmake | 3 ++
llvm/lib/Support/APFloat.cpp | 34 ++++++++++++++++++++
llvm/lib/Support/CMakeLists.txt | 6 ++++
llvm/lib/Target/AMDGPU/AMDGPULibCalls.cpp | 2 +-
llvm/unittests/ADT/APFloatTest.cpp | 19 +++++++++++
7 files changed, 79 insertions(+), 1 deletion(-)
diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt
index 0849bec26d56a..4128cceaf33fa 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 codes 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..6f6dd3c014584 100644
--- a/llvm/include/llvm/ADT/APFloat.h
+++ b/llvm/include/llvm/ADT/APFloat.h
@@ -1522,6 +1522,7 @@ class APFloat : public APFloatBase {
friend int ilogb(const APFloat &Arg) { return ilogb(Arg.getIEEE()); }
friend APFloat scalbn(APFloat X, int Exp, roundingMode RM);
friend APFloat frexp(const APFloat &X, int &Exp, roundingMode RM);
+ friend APFloat exp(const APFloat &X, roundingMode RM);
friend IEEEFloat;
friend DoubleAPFloat;
};
@@ -1657,6 +1658,10 @@ inline APFloat maximumnum(const APFloat &A, const APFloat &B) {
return A < B ? B : A;
}
+/// Implement IEEE 754-2019 exp functions.
+LLVM_READONLY
+APFloat exp(const APFloat &X, RoundingMode RM);
+
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..150853743540d 100644
--- a/llvm/lib/Support/APFloat.cpp
+++ b/llvm/lib/Support/APFloat.cpp
@@ -28,6 +28,11 @@
#include <cstring>
#include <limits.h>
+#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<IEEEFloat>(getSemantics())) \
@@ -5601,6 +5606,35 @@ float APFloat::convertToFloat() const {
return Temp.getIEEE().convertToFloat();
}
+#ifdef LLVM_INTEGRATE_LIBC
+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) {
+ int current_rounding_mode = fegetround();
+ switch (rounding_mode) {
+ case APFloat::rmNearestTiesToEven:
+ fesetround(FE_TONEAREST);
+ break;
+ case APFloat::rmTowardPositive:
+ fesetround(FE_UPWARD);
+ break;
+ case APFloat::rmTowardNegative:
+ fesetround(FE_DOWNWARD);
+ break;
+ case APFloat::rmTowardZero:
+ fesetround(FE_TOWARDZERO);
+ break;
+ default:
+ }
+ float result = LIBC_NAMESPACE::shared::expf(X.convertToFloat());
+ fesetround(current_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..c56cd8898d17a 100644
--- a/llvm/unittests/ADT/APFloatTest.cpp
+++ b/llvm/unittests/ADT/APFloatTest.cpp
@@ -8356,4 +8356,23 @@ TEST(APFloatTest, hasSignBitInMSB) {
EXPECT_FALSE(APFloat::hasSignBitInMSB(APFloat::Float8E8M0FNU()));
}
+#ifdef LLVM_INTEGRATE_LIBC
+TEST(APFloatTest, expf) {
+ EXPECT_EQ(
+ 1.0f,
+ llvm::exp(APFloat(0.0f), APFloat::rmNearestTiesToEven).convertToFloat());
+ 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());
+}
+#endif // LLVM_INTEGRATE_LIBC
+
} // namespace
>From 32f6609a86ba8164407cd6099c7ab60cf7fe8438 Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Fri, 13 Jun 2025 04:09:15 +0000
Subject: [PATCH 2/3] Fix typo and add more tests.
---
llvm/CMakeLists.txt | 2 +-
llvm/unittests/ADT/APFloatTest.cpp | 53 ++++++++++++++++++++++++++++--
2 files changed, 51 insertions(+), 4 deletions(-)
diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt
index 4128cceaf33fa..f1a1b39a60652 100644
--- a/llvm/CMakeLists.txt
+++ b/llvm/CMakeLists.txt
@@ -644,7 +644,7 @@ endif()
set(LLVM_ENABLE_Z3_SOLVER_DEFAULT "${Z3_FOUND}")
-set(LLVM_INTEGRATE_LIBC "OFF" CACHE STRING "Use LLVM libc codes directly if available.")
+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}")
diff --git a/llvm/unittests/ADT/APFloatTest.cpp b/llvm/unittests/ADT/APFloatTest.cpp
index c56cd8898d17a..f7e5a64ba9306 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 <cmath>
+#include <limits>
#include <ostream>
#include <string>
#include <tuple>
@@ -8358,9 +8359,24 @@ TEST(APFloatTest, hasSignBitInMSB) {
#ifdef LLVM_INTEGRATE_LIBC
TEST(APFloatTest, expf) {
- EXPECT_EQ(
- 1.0f,
- llvm::exp(APFloat(0.0f), APFloat::rmNearestTiesToEven).convertToFloat());
+ std::array<llvm::RoundingMode, 4> 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<float>::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());
@@ -8372,6 +8388,37 @@ TEST(APFloatTest, expf) {
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<float>::infinity(),
+ llvm::exp(APFloat::getLargest(APFloat::IEEEsingle(), false),
+ APFloat::rmNearestTiesToEven)
+ .convertToFloat());
+ EXPECT_EQ(std::numeric_limits<float>::infinity(),
+ llvm::exp(APFloat::getLargest(APFloat::IEEEsingle(), false),
+ APFloat::rmTowardPositive)
+ .convertToFloat());
+ EXPECT_EQ(std::numeric_limits<float>::max(),
+ llvm::exp(APFloat::getLargest(APFloat::IEEEsingle(), false),
+ APFloat::rmTowardNegative)
+ .convertToFloat());
+ EXPECT_EQ(std::numeric_limits<float>::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.000002p0,
+ 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());
}
#endif // LLVM_INTEGRATE_LIBC
>From d43491bf2f30923dd4b03d2fe8355947709c240a Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Fri, 13 Jun 2025 14:16:20 +0000
Subject: [PATCH 3/3] Add directional rounding version to LLVM libc and use
that in APFloat.
---
libc/src/__support/math/expf.h | 9 +++++++++
llvm/lib/Support/APFloat.cpp | 37 +++++++++++++++++-----------------
2 files changed, 27 insertions(+), 19 deletions(-)
diff --git a/libc/src/__support/math/expf.h b/libc/src/__support/math/expf.h
index 88c151492a041..d401993a75fc9 100644
--- a/libc/src/__support/math/expf.h
+++ b/libc/src/__support/math/expf.h
@@ -109,6 +109,15 @@ static constexpr float expf(float x) {
return static_cast<float>(exp_hi * exp_mid * exp_lo);
}
+// Directional rounding version of expf.
+LIBC_INLINE static float expf(float x, int rounding_mode) {
+ int current_rounding_mode = fputil::get_round();
+ fputil::set_round(rounding_mode);
+ float result = expf(x);
+ fputil::set_round(current_rounding_mode);
+ return result;
+}
+
} // namespace math
} // namespace LIBC_NAMESPACE_DECL
diff --git a/llvm/lib/Support/APFloat.cpp b/llvm/lib/Support/APFloat.cpp
index 150853743540d..574d0c233c425 100644
--- a/llvm/lib/Support/APFloat.cpp
+++ b/llvm/lib/Support/APFloat.cpp
@@ -5607,28 +5607,27 @@ float APFloat::convertToFloat() const {
}
#ifdef LLVM_INTEGRATE_LIBC
-APFloat exp(const APFloat &X, RoundingMode rounding_mode) {
+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 = APFloat::rmNearestTiesToEven) {
assert((&X.getSemantics() == (const llvm::fltSemantics *)&semIEEEsingle) &&
"Float semantics is not IEEEsingle");
if (&X.getSemantics() == (const llvm::fltSemantics *)&semIEEEsingle) {
- int current_rounding_mode = fegetround();
- switch (rounding_mode) {
- case APFloat::rmNearestTiesToEven:
- fesetround(FE_TONEAREST);
- break;
- case APFloat::rmTowardPositive:
- fesetround(FE_UPWARD);
- break;
- case APFloat::rmTowardNegative:
- fesetround(FE_DOWNWARD);
- break;
- case APFloat::rmTowardZero:
- fesetround(FE_TOWARDZERO);
- break;
- default:
- }
- float result = LIBC_NAMESPACE::shared::expf(X.convertToFloat());
- fesetround(current_rounding_mode);
+ float result = LIBC_NAMESPACE::shared::expf(
+ X.convertToFloat(), getFEnvRoundingMode(rounding_mode));
return APFloat(result);
}
llvm_unreachable("Unexpected semantics");
More information about the llvm-commits
mailing list