-
Notifications
You must be signed in to change notification settings - Fork 14.2k
[APFloat] Add exp function for APFloat::IEEESsingle using expf implementation from LLVM libc. #143959
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-libc @llvm/pr-subscribers-llvm-adt Author: None (lntue) Changes
Full diff: https://github.com/llvm/llvm-project/pull/143959.diff 7 Files Affected:
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
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given sparse checkouts, I'm wondering if we should check if lib/
exists in the tree and use this by default if so.
llvm/lib/Support/APFloat.cpp
Outdated
int current_rounding_mode = fegetround(); | ||
switch (rounding_mode) { | ||
case APFloat::rmNearestTiesToEven: | ||
fesetround(FE_TONEAREST); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're not compiling LLVM with strictfp (which also barely works, on one target). Can we instead just call a fixed rounding mode implementation in libc?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main problem the preventing linking LLVM libc directly to LLVM / APFloat is that ideally, the libc project belongs to the LLVM_ENABLE_RUNTIMES
list which will be built after LLVM and clang in the LLVM_ENABLE_PROJECTS
list. To break that build dependency cycle, and simplify build systems for both projects, I think letting LLVM directly include and build libc's math functions is the simplest way.
On the other hand, I do see 2 problems that might arise:
- we might need to build these libc math routines wrapped inside
#pragma STDC FENV_ACCESS ON
to make sure that the floating point environment accesses are not shuffled around. At the moment it looks like they are built correctly, but it would be safer to put them under the pragma. - as more and more math routines are included directly, it might be slow to build
APFloat.cpp
, so we might need to refactor them into separate TUs to speed up the build.
What do you think about refactor this into something like APFloatMath.cpp
or APFloatExpLog.cpp
, put them under #pragma STDC FENV_ACCESS ON
, and then add them to LLVMSupport
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the suggestion is to do LIBC_NAMESPACE::shared::expf(X.convertToFloat(), rounding_mode)
and not mess with global state. It would be much cleaner if that were possible
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, at the moment, LLVM libc math functions are rounded according to the rounding mode of the floating point environment. So in order to have a specific rounding mode, they need to be wrapped with fesetround()
calls, and LLVM libc won't have access to APFloat::rm*
. So to hide the fegetround/fesetround
calls, we could add either LIBC_NAMESPACE::shared::expf(float, int)
to take the fenv.h
rounding mode on the libc side, but the switch / translation from APFloat::rm*
to FE_*
still needs to be in APFloat.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So to hide the fegetround/fesetround calls, we could add either LIBC_NAMESPACE::shared::expf(float, int) to take the fenv.h rounding mode on the libc side, but the switch / translation from APFloat::rm* to FE_* still needs to be in APFloat.
This would be fine - we are mostly concerned about the global state. having to map an enum to another is perfectly reasonable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've moved the rounding mode adjustment into libc.
llvm/unittests/ADT/APFloatTest.cpp
Outdated
1.0f, | ||
llvm::exp(APFloat(0.0f), APFloat::rmNearestTiesToEven).convertToFloat()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Compare bit-equal? Can you also test the edge case values, nan, inf, denormals, -0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added more tests for those values. And for floating point values that are not +-0 or NaN, the equal (==) comparison is bit-equal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not true when denormals are flushed, and x87 unnormals
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, or those odd cases. But in here we have float and normal values, and I hope people don't alias float
with x87 80-bit fp type
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't address that the denormal values will compare equal to 0 depending on the host FP environment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm also concerned about having a function declaration but not definition if LLVM_INTEGRATE_LIBC is off.
llvm/include/llvm/ADT/APFloat.h
Outdated
@@ -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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I'm following correctly, we have two separate declarations of this function...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. And added test for using the default rounding mode.
llvm/lib/Support/APFloat.cpp
Outdated
} | ||
|
||
APFloat exp(const APFloat &X, | ||
RoundingMode rounding_mode = APFloat::rmNearestTiesToEven) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... and the default parameter is only in the definition body that no user code sees.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. And added test for using the default rounding modes.
…nding mode declaration.
I wrapped the declarations inside |
@@ -109,6 +109,18 @@ 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(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the pragma to enable fenv_access?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Discourse RFC: https://discourse.llvm.org/t/rfc-make-clang-builtin-math-functions-constexpr-with-llvm-libc-to-support-c-23-constexpr-math-functions/86450