Skip to content

Commit 4190d57

Browse files
authored
[APFloat] Add exp function for APFloat::IEEESsingle using expf implementation from LLVM libc. (#143959)
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 - The implementation in LLVM libc is header-only. - expf implementation in LLVM libc is correctly rounded for all rounding modes. - LLVM libc implementation will round to the floating point environment's rounding mode. - No cmake build dependency between LLVM and LLVM libc, only requires LLVM libc source presents in llvm-project/libc folder.
1 parent 491e001 commit 4190d57

File tree

8 files changed

+162
-1
lines changed

8 files changed

+162
-1
lines changed

libc/shared/math/expf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include "shared/libc_common.h"
1313
#include "src/__support/math/expf.h"
14+
#include "src/__support/math/expf_static_rounding.h"
1415

1516
namespace LIBC_NAMESPACE_DECL {
1617
namespace shared {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//===-- Implementation header for expf --------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_EXPF_STATIC_ROUNDING_H
10+
#define LLVM_LIBC_SRC___SUPPORT_MATH_EXPF_STATIC_ROUNDING_H
11+
12+
#include "expf.h"
13+
#include "src/__support/FPUtil/FEnvImpl.h"
14+
#include "src/__support/common.h"
15+
#include "src/__support/macros/config.h"
16+
17+
namespace LIBC_NAMESPACE_DECL {
18+
19+
namespace math {
20+
21+
// Remark: "#pragma STDC FENV_ACCESS" might generate unsupported warnings on
22+
// certain platforms.
23+
#pragma STDC FENV_ACCESS ON
24+
25+
// Directional rounding version of expf.
26+
LIBC_INLINE static float expf(float x, int rounding_mode) {
27+
int current_rounding_mode = fputil::get_round();
28+
if (rounding_mode == current_rounding_mode)
29+
return expf(x);
30+
31+
fputil::set_round(rounding_mode);
32+
float result = expf(x);
33+
fputil::set_round(current_rounding_mode);
34+
return result;
35+
}
36+
37+
#pragma STDC FENV_ACCESS DEFAULT
38+
39+
} // namespace math
40+
41+
} // namespace LIBC_NAMESPACE_DECL
42+
43+
#endif // LLVM_LIBC_SRC___SUPPORT_MATH_EXPF_STATIC_ROUNDING_H

llvm/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,11 @@ endif()
657657

658658
set(LLVM_ENABLE_Z3_SOLVER_DEFAULT "${Z3_FOUND}")
659659

660+
include(FindLibcCommonUtils)
661+
if(NOT TARGET llvm-libc-common-utilities)
662+
message(FATAL_ERROR "LLVM libc is not found at ${libc_path}.")
663+
endif()
664+
660665

661666
if( LLVM_TARGETS_TO_BUILD STREQUAL "all" )
662667
set( LLVM_TARGETS_TO_BUILD ${LLVM_ALL_TARGETS} )

llvm/include/llvm/ADT/APFloat.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1493,6 +1493,8 @@ class APFloat : public APFloatBase {
14931493
friend APFloat frexp(const APFloat &X, int &Exp, roundingMode RM);
14941494
friend IEEEFloat;
14951495
friend DoubleAPFloat;
1496+
1497+
friend APFloat exp(const APFloat &X, roundingMode RM);
14961498
};
14971499

14981500
static_assert(sizeof(APFloat) == sizeof(detail::IEEEFloat),
@@ -1645,6 +1647,10 @@ inline APFloat maximumnum(const APFloat &A, const APFloat &B) {
16451647
return A < B ? B : A;
16461648
}
16471649

1650+
/// Implement IEEE 754-2019 exp functions.
1651+
LLVM_READONLY
1652+
APFloat exp(const APFloat &X, RoundingMode RM = APFloat::rmNearestTiesToEven);
1653+
16481654
inline raw_ostream &operator<<(raw_ostream &OS, const APFloat &V) {
16491655
V.print(OS);
16501656
return OS;

llvm/lib/Support/APFloat.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
#include <cstring>
2929
#include <limits.h>
3030

31+
// Shared headers from LLVM libc
32+
#include "shared/math.h"
33+
3134
#define APFLOAT_DISPATCH_ON_SEMANTICS(METHOD_CALL) \
3235
do { \
3336
if (usesLayout<IEEEFloat>(getSemantics())) \
@@ -6155,6 +6158,29 @@ float APFloat::convertToFloat() const {
61556158
return Temp.getIEEE().convertToFloat();
61566159
}
61576160

6161+
static constexpr int getFEnvRoundingMode(llvm::RoundingMode rm) {
6162+
switch (rm) {
6163+
case APFloat::rmTowardPositive:
6164+
return FE_UPWARD;
6165+
case APFloat::rmTowardNegative:
6166+
return FE_DOWNWARD;
6167+
case APFloat::rmTowardZero:
6168+
return FE_TOWARDZERO;
6169+
default:
6170+
// TODO: fix rmNearestTiesToAway for platform without FE_TONEARESTFROMZERO.
6171+
return FE_TONEAREST;
6172+
};
6173+
}
6174+
6175+
APFloat exp(const APFloat &X, RoundingMode rounding_mode) {
6176+
if (&X.getSemantics() == &APFloat::IEEEsingle()) {
6177+
float result = LIBC_NAMESPACE::shared::expf(
6178+
X.convertToFloat(), getFEnvRoundingMode(rounding_mode));
6179+
return APFloat(result);
6180+
}
6181+
llvm_unreachable("Unsupported floating-point semantics");
6182+
}
6183+
61586184
APFloat::Storage::~Storage() {
61596185
if (usesLayout<IEEEFloat>(*semantics)) {
61606186
IEEE.~IEEEFloat();

llvm/lib/Support/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,3 +395,9 @@ target_include_directories(LLVMSupport
395395
PRIVATE
396396
${LLVM_THIRD_PARTY_DIR}/siphash/include
397397
)
398+
399+
# Integrating LLVM libc's math functions
400+
target_include_directories(LLVMSupport PRIVATE "${LLVM_INCLUDE_DIR}/../../libc")
401+
if(NOT MSVC)
402+
target_compile_options(LLVMSupport PRIVATE "-Wno-c99-extensions") # _Complex warnings.
403+
endif()

llvm/lib/Target/AMDGPU/AMDGPULibCalls.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1531,7 +1531,7 @@ bool AMDGPULibCalls::evaluateScalarMathFunc(const FuncInfo &FInfo, double &Res0,
15311531
return true;
15321532

15331533
case AMDGPULibFunc::EI_EXP:
1534-
Res0 = exp(opr0);
1534+
Res0 = std::exp(opr0);
15351535
return true;
15361536

15371537
case AMDGPULibFunc::EI_EXP2:

llvm/unittests/ADT/APFloatTest.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10182,4 +10182,78 @@ TEST(APFloatTest, FrexpQuietSNaN) {
1018210182
EXPECT_FALSE(Result.isSignaling());
1018310183
}
1018410184

10185+
TEST(APFloatTest, expf) {
10186+
std::array<llvm::RoundingMode, 4> allRoundingModes = {
10187+
APFloat::rmNearestTiesToEven, APFloat::rmTowardPositive,
10188+
APFloat::rmTowardNegative, APFloat::rmTowardZero};
10189+
for (auto rm : allRoundingModes) {
10190+
// exp(+-0) = 1 for all rounding modes.
10191+
EXPECT_EQ(1.0f, llvm::exp(APFloat(0.0f), rm).convertToFloat());
10192+
EXPECT_EQ(1.0f, llvm::exp(APFloat(-0.0f), rm).convertToFloat());
10193+
// exp(+Inf) = +Inf for all rounding modes.
10194+
EXPECT_EQ(std::numeric_limits<float>::infinity(),
10195+
llvm::exp(APFloat::getInf(APFloat::IEEEsingle(), false), rm)
10196+
.convertToFloat());
10197+
// exp(-Inf) = 0 for all rounding modes.
10198+
EXPECT_EQ(0.0f, llvm::exp(APFloat::getInf(APFloat::IEEEsingle(), true), rm)
10199+
.convertToFloat());
10200+
// exp(NaN) = NaN for all rounding modes.
10201+
EXPECT_TRUE(llvm::exp(APFloat::getNaN(APFloat::IEEEsingle()), rm).isNaN());
10202+
}
10203+
// exp(1)
10204+
EXPECT_EQ(
10205+
0x1.5bf0a8p1f,
10206+
llvm::exp(APFloat(1.0f), APFloat::rmNearestTiesToEven).convertToFloat());
10207+
EXPECT_EQ(
10208+
0x1.5bf0aap1f,
10209+
llvm::exp(APFloat(1.0f), APFloat::rmTowardPositive).convertToFloat());
10210+
EXPECT_EQ(
10211+
0x1.5bf0a8p1f,
10212+
llvm::exp(APFloat(1.0f), APFloat::rmTowardNegative).convertToFloat());
10213+
EXPECT_EQ(0x1.5bf0a8p1f,
10214+
llvm::exp(APFloat(1.0f), APFloat::rmTowardZero).convertToFloat());
10215+
// exp(float max)
10216+
EXPECT_EQ(std::numeric_limits<float>::infinity(),
10217+
llvm::exp(APFloat::getLargest(APFloat::IEEEsingle(), false),
10218+
APFloat::rmNearestTiesToEven)
10219+
.convertToFloat());
10220+
EXPECT_EQ(std::numeric_limits<float>::infinity(),
10221+
llvm::exp(APFloat::getLargest(APFloat::IEEEsingle(), false),
10222+
APFloat::rmTowardPositive)
10223+
.convertToFloat());
10224+
EXPECT_EQ(std::numeric_limits<float>::max(),
10225+
llvm::exp(APFloat::getLargest(APFloat::IEEEsingle(), false),
10226+
APFloat::rmTowardNegative)
10227+
.convertToFloat());
10228+
EXPECT_EQ(std::numeric_limits<float>::max(),
10229+
llvm::exp(APFloat::getLargest(APFloat::IEEEsingle(), false),
10230+
APFloat::rmTowardZero)
10231+
.convertToFloat());
10232+
// exp(min_denormal)
10233+
EXPECT_EQ(1.0f, llvm::exp(APFloat::getSmallest(APFloat::IEEEsingle(), false),
10234+
APFloat::rmNearestTiesToEven)
10235+
.convertToFloat());
10236+
EXPECT_EQ(0x1.000002p0f,
10237+
llvm::exp(APFloat::getSmallest(APFloat::IEEEsingle(), false),
10238+
APFloat::rmTowardPositive)
10239+
.convertToFloat());
10240+
EXPECT_EQ(1.0f, llvm::exp(APFloat::getSmallest(APFloat::IEEEsingle(), false),
10241+
APFloat::rmTowardNegative)
10242+
.convertToFloat());
10243+
EXPECT_EQ(1.0f, llvm::exp(APFloat::getSmallest(APFloat::IEEEsingle(), false),
10244+
APFloat::rmTowardZero)
10245+
.convertToFloat());
10246+
// Default rounding mode.
10247+
// exp(-1)
10248+
EXPECT_EQ(0x1.78b564p-2f, llvm::exp(APFloat(-1.0f)).convertToFloat());
10249+
EXPECT_EQ(
10250+
0x1.78b564p-2f,
10251+
llvm::exp(APFloat(-1.0f), APFloat::rmTowardPositive).convertToFloat());
10252+
EXPECT_EQ(
10253+
0x1.78b562p-2f,
10254+
llvm::exp(APFloat(-1.0f), APFloat::rmTowardNegative).convertToFloat());
10255+
EXPECT_EQ(0x1.78b562p-2f,
10256+
llvm::exp(APFloat(-1.0f), APFloat::rmTowardZero).convertToFloat());
10257+
}
10258+
1018510259
} // namespace

0 commit comments

Comments
 (0)