[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:16:56 PDT 2025


https://github.com/lntue updated https://github.com/llvm/llvm-project/pull/143959

>From ce5e4f90957ed6143d1d46e4dea80e49138a130c 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 cfb67472aa71e..ff2f4ff4ea298 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 dbc882937b4f4..8ec9a30dc85d1 100644
--- a/llvm/include/llvm/Config/llvm-config.h.cmake
+++ b/llvm/include/llvm/Config/llvm-config.h.cmake
@@ -133,4 +133,7 @@
    and to 0 otherwise. */
 #cmakedefine01 LLVM_ENABLE_DEBUGLOC_COVERAGE_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 4adec802975004fbe0bed0f94ff9307059834f53 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 ff2f4ff4ea298..fedede482e22f 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 b4df8221477375faa0385c3fc547c5a85f5fbfd1 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