Skip to content

Commit 3c2444d

Browse files
committed
WIP/POC: Constant Fold Logf128 calls
This is a proof of concept/work in progress patch. This patch enables ConstantFolding of log FP128 calls. This is achieved by querying with CMake if the host system has the logf128 symbol available. If so, replace the runtime call with the compile time constant returned from logf128. This approach could be considered controversial as cross-compiled llvm executables using shared objects may not have the logf128 symbol available at runtime. The implementation of logf128 may also yield different results on different targets, such as x86 using fp80 precision instead of the full fp128 range on other targets. This approach relies on unit tests, as more commonplace Clang/C tests and opt/llc/IR tests are not applicable since they are ignorant to the result of the compile time CMake check.
1 parent 8cfb716 commit 3c2444d

File tree

9 files changed

+175
-4
lines changed

9 files changed

+175
-4
lines changed

llvm/include/llvm/ADT/APFloat.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ class IEEEFloat final : public APFloatBase {
299299
IEEEFloat(const fltSemantics &, integerPart);
300300
IEEEFloat(const fltSemantics &, uninitializedTag);
301301
IEEEFloat(const fltSemantics &, const APInt &);
302+
explicit IEEEFloat(long double ld);
302303
explicit IEEEFloat(double d);
303304
explicit IEEEFloat(float f);
304305
IEEEFloat(const IEEEFloat &);
@@ -354,6 +355,7 @@ class IEEEFloat final : public APFloatBase {
354355
Expected<opStatus> convertFromString(StringRef, roundingMode);
355356
APInt bitcastToAPInt() const;
356357
double convertToDouble() const;
358+
long double convertToQuad() const;
357359
float convertToFloat() const;
358360

359361
/// @}
@@ -942,6 +944,7 @@ class APFloat : public APFloatBase {
942944
APFloat(const fltSemantics &Semantics, uninitializedTag)
943945
: U(Semantics, uninitialized) {}
944946
APFloat(const fltSemantics &Semantics, const APInt &I) : U(Semantics, I) {}
947+
explicit APFloat(long double ld) : U(IEEEFloat(ld), IEEEquad()) {}
945948
explicit APFloat(double d) : U(IEEEFloat(d), IEEEdouble()) {}
946949
explicit APFloat(float f) : U(IEEEFloat(f), IEEEsingle()) {}
947950
APFloat(const APFloat &RHS) = default;
@@ -1218,6 +1221,13 @@ class APFloat : public APFloatBase {
12181221
/// shorter semantics, like IEEEsingle and others.
12191222
double convertToDouble() const;
12201223

1224+
/// Converts this APFloat to host float value.
1225+
///
1226+
/// \pre The APFloat must be built using semantics, that can be represented by
1227+
/// the host float type without loss of precision. It can be IEEEquad and
1228+
/// shorter semantics, like IEEEdouble and others.
1229+
long double convertToQuad() const;
1230+
12211231
/// Converts this APFloat to host float value.
12221232
///
12231233
/// \pre The APFloat must be built using semantics, that can be represented by

llvm/include/llvm/ADT/APInt.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1663,6 +1663,11 @@ class [[nodiscard]] APInt {
16631663
/// any bit width. Exactly 64 bits will be translated.
16641664
double bitsToDouble() const { return llvm::bit_cast<double>(getWord(0)); }
16651665

1666+
long double bitsToQuad() const {
1667+
__uint128_t ul = ((__uint128_t)U.pVal[1] << 64) + U.pVal[0];
1668+
return llvm::bit_cast<long double>(ul);
1669+
}
1670+
16661671
/// Converts APInt bits to a float
16671672
///
16681673
/// The conversion does not do a translation from integer to float, it just
@@ -1688,6 +1693,16 @@ class [[nodiscard]] APInt {
16881693
return APInt(sizeof(float) * CHAR_BIT, llvm::bit_cast<uint32_t>(V));
16891694
}
16901695

1696+
static APInt longDoubleToBits(long double V) {
1697+
assert(sizeof(long double) == 16 && "Expected 16 byte long double");
1698+
1699+
const uint64_t Words[2] = {
1700+
static_cast<uint64_t>(V),
1701+
static_cast<uint64_t>(llvm::bit_cast<__uint128_t>(V) >> 64),
1702+
};
1703+
return APInt(sizeof(long double) * CHAR_BIT, 2, Words);
1704+
}
1705+
16911706
/// @}
16921707
/// \name Mathematics Operations
16931708
/// @{

llvm/include/llvm/IR/Constants.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,8 @@ class ConstantFP final : public ConstantData {
289289
/// host double and as the target format.
290290
static Constant *get(Type *Ty, double V);
291291

292+
static Constant *get128(Type *Ty, long double V);
293+
292294
/// If Ty is a vector type, return a Constant with a splat of the given
293295
/// value. Otherwise return a ConstantFP for the given value.
294296
static Constant *get(Type *Ty, const APFloat &V);

llvm/lib/Analysis/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,9 @@ add_llvm_component_library(LLVMAnalysis
161161
Support
162162
TargetParser
163163
)
164+
165+
include(CheckCXXSymbolExists)
166+
check_cxx_symbol_exists(logf128 math.h HAS_LOGF128)
167+
if(HAS_LOGF128)
168+
target_compile_definitions(LLVMAnalysis PRIVATE HAS_LOGF128)
169+
endif()

llvm/lib/Analysis/ConstantFolding.cpp

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1678,9 +1678,9 @@ bool llvm::canConstantFoldCallTo(const CallBase *Call, const Function *F) {
16781678
Name == "floor" || Name == "floorf" ||
16791679
Name == "fmod" || Name == "fmodf";
16801680
case 'l':
1681-
return Name == "log" || Name == "logf" ||
1682-
Name == "log2" || Name == "log2f" ||
1683-
Name == "log10" || Name == "log10f";
1681+
return Name == "log" || Name == "logf" || Name == "log2" ||
1682+
Name == "log2f" || Name == "log10" || Name == "log10f" ||
1683+
Name == "logl";
16841684
case 'n':
16851685
return Name == "nearbyint" || Name == "nearbyintf";
16861686
case 'p':
@@ -1763,6 +1763,15 @@ inline bool llvm_fenv_testexcept() {
17631763
return false;
17641764
}
17651765

1766+
Constant *ConstantFoldLogf128(const APFloat &V, Type *Ty) {
1767+
#ifdef HAS_LOGF128
1768+
long double l = logf128(V.convertToQuad());
1769+
return ConstantFP::get128(Ty, l);
1770+
#else
1771+
return nullptr;
1772+
#endif
1773+
}
1774+
17661775
Constant *ConstantFoldFP(double (*NativeFP)(double), const APFloat &V,
17671776
Type *Ty) {
17681777
llvm_fenv_clearexcept();
@@ -2094,7 +2103,8 @@ static Constant *ConstantFoldScalarCall1(StringRef Name,
20942103
if (IntrinsicID == Intrinsic::canonicalize)
20952104
return constantFoldCanonicalize(Ty, Call, U);
20962105

2097-
if (!Ty->isHalfTy() && !Ty->isFloatTy() && !Ty->isDoubleTy())
2106+
if (!Ty->isHalfTy() && !Ty->isFloatTy() && !Ty->isDoubleTy() &&
2107+
!Ty->isFP128Ty())
20982108
return nullptr;
20992109

21002110
// Use internal versions of these intrinsics.
@@ -2209,6 +2219,8 @@ static Constant *ConstantFoldScalarCall1(StringRef Name,
22092219
switch (IntrinsicID) {
22102220
default: break;
22112221
case Intrinsic::log:
2222+
if (Ty->isFP128Ty())
2223+
return ConstantFoldLogf128(APF, Ty);
22122224
return ConstantFoldFP(log, APF, Ty);
22132225
case Intrinsic::log2:
22142226
// TODO: What about hosts that lack a C99 library?
@@ -2338,6 +2350,11 @@ static Constant *ConstantFoldScalarCall1(StringRef Name,
23382350
if (!APF.isNegative() && !APF.isZero() && TLI->has(Func))
23392351
return ConstantFoldFP(log, APF, Ty);
23402352
break;
2353+
case LibFunc_logl:
2354+
if (!APF.isNegative() && !APF.isZero() && TLI->has(Func) &&
2355+
Ty->isFP128Ty())
2356+
return ConstantFoldLogf128(APF, Ty);
2357+
break;
23412358
case LibFunc_log2:
23422359
case LibFunc_log2f:
23432360
case LibFunc_log2_finite:

llvm/lib/IR/Constants.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,22 @@ Constant *ConstantFP::get(Type *Ty, double V) {
976976
return C;
977977
}
978978

979+
Constant *ConstantFP::get128(Type *Ty, long double V) {
980+
LLVMContext &Context = Ty->getContext();
981+
982+
APFloat FV(V);
983+
bool ignored;
984+
FV.convert(Ty->getScalarType()->getFltSemantics(),
985+
APFloat::rmNearestTiesToEven, &ignored);
986+
Constant *C = get(Context, FV);
987+
988+
// For vectors, broadcast the value.
989+
if (VectorType *VTy = dyn_cast<VectorType>(Ty))
990+
return ConstantVector::getSplat(VTy->getElementCount(), C);
991+
992+
return C;
993+
}
994+
979995
Constant *ConstantFP::get(Type *Ty, const APFloat &V) {
980996
ConstantFP *C = get(Ty->getContext(), V);
981997
assert(C->getType() == Ty->getScalarType() &&

llvm/lib/Support/APFloat.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3670,6 +3670,13 @@ double IEEEFloat::convertToDouble() const {
36703670
return api.bitsToDouble();
36713671
}
36723672

3673+
long double IEEEFloat::convertToQuad() const {
3674+
assert(semantics == (const llvm::fltSemantics *)&semIEEEquad &&
3675+
"Float semantics are not IEEEquads");
3676+
APInt api = bitcastToAPInt();
3677+
return api.bitsToQuad();
3678+
}
3679+
36733680
/// Integer bit is explicit in this format. Intel hardware (387 and later)
36743681
/// does not support these bit patterns:
36753682
/// exponent = all 1's, integer bit 0, significand 0 ("pseudoinfinity")
@@ -3958,6 +3965,10 @@ IEEEFloat::IEEEFloat(double d) {
39583965
initFromAPInt(&semIEEEdouble, APInt::doubleToBits(d));
39593966
}
39603967

3968+
IEEEFloat::IEEEFloat(long double ld) {
3969+
initFromAPInt(&semIEEEquad, APInt::longDoubleToBits(ld));
3970+
}
3971+
39613972
namespace {
39623973
void append(SmallVectorImpl<char> &Buffer, StringRef Str) {
39633974
Buffer.append(Str.begin(), Str.end());
@@ -5265,6 +5276,19 @@ double APFloat::convertToDouble() const {
52655276
return Temp.getIEEE().convertToDouble();
52665277
}
52675278

5279+
long double APFloat::convertToQuad() const {
5280+
if (&getSemantics() == (const llvm::fltSemantics *)&semIEEEquad)
5281+
return getIEEE().convertToQuad();
5282+
assert(getSemantics().isRepresentableBy(semIEEEquad) &&
5283+
"Float semantics is not representable by IEEEquad");
5284+
APFloat Temp = *this;
5285+
bool LosesInfo;
5286+
opStatus St = Temp.convert(semIEEEquad, rmNearestTiesToEven, &LosesInfo);
5287+
assert(!(St & opInexact) && !LosesInfo && "Unexpected imprecision");
5288+
(void)St;
5289+
return Temp.getIEEE().convertToQuad();
5290+
}
5291+
52685292
float APFloat::convertToFloat() const {
52695293
if (&getSemantics() == (const llvm::fltSemantics *)&semIEEEsingle)
52705294
return getIEEE().convertToFloat();

llvm/unittests/Analysis/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ set(ANALYSIS_TEST_SOURCES
5151
ValueLatticeTest.cpp
5252
ValueTrackingTest.cpp
5353
VectorUtilsTest.cpp
54+
ConstantLogf128.cpp
5455
)
5556

5657
set(MLGO_TESTS TFUtilsTest.cpp)
@@ -80,5 +81,11 @@ if(NOT WIN32)
8081
export_executable_symbols_for_plugins(AnalysisTests)
8182
endif()
8283

84+
include(CheckCXXSymbolExists)
85+
check_cxx_symbol_exists(logf128 math.h HAS_LOGF128)
86+
if(HAS_LOGF128)
87+
target_compile_definitions(AnalysisTests PRIVATE HAS_LOGF128)
88+
endif()
89+
8390
add_subdirectory(InlineAdvisorPlugin)
8491
add_subdirectory(InlineOrderPlugin)
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//===- unittests/CodeGen/BufferSourceTest.cpp - MemoryBuffer source tests -===//
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+
#include "llvm/Analysis/ConstantFolding.h"
10+
#include "llvm/Analysis/TargetLibraryInfo.h"
11+
#include "llvm/CodeGen/GlobalISel/CallLowering.h"
12+
#include "llvm/IR/IRBuilder.h"
13+
#include "llvm/IR/InstrTypes.h"
14+
#include "gtest/gtest.h"
15+
16+
using namespace llvm;
17+
18+
namespace {
19+
20+
class ConstantFoldLogf128Fixture
21+
: public ::testing ::TestWithParam<std::string> {
22+
protected:
23+
std::string FuncName;
24+
};
25+
26+
TEST_P(ConstantFoldLogf128Fixture, ConstantFoldLogf128) {
27+
LLVMContext Context;
28+
IRBuilder<> Builder(Context);
29+
Module MainModule("Logf128TestModule", Context);
30+
MainModule.setTargetTriple("aarch64-unknown-linux");
31+
32+
Type *FP128Ty = Type::getFP128Ty(Context);
33+
FunctionType *FP128Prototype = FunctionType::get(FP128Ty, false);
34+
Function *Logf128TestFunction = Function::Create(
35+
FP128Prototype, Function::ExternalLinkage, "logf128test", MainModule);
36+
BasicBlock *EntryBlock =
37+
BasicBlock::Create(Context, "entry", Logf128TestFunction);
38+
Builder.SetInsertPoint(EntryBlock);
39+
40+
FunctionType *FP128FP128Prototype =
41+
FunctionType::get(FP128Ty, {FP128Ty}, false);
42+
Constant *Constant2L = ConstantFP::get128(FP128Ty, 2.0L);
43+
44+
std::string FunctionName = GetParam();
45+
Function *Logl = Function::Create(
46+
FP128FP128Prototype, Function::ExternalLinkage, FunctionName, MainModule);
47+
CallInst *LoglCall = Builder.CreateCall(Logl, Constant2L);
48+
49+
TargetLibraryInfoImpl TLII(Triple(MainModule.getTargetTriple()));
50+
TargetLibraryInfo TLI(TLII, Logf128TestFunction);
51+
Constant *FoldResult = ConstantFoldCall(LoglCall, Logl, Constant2L, &TLI);
52+
53+
#ifndef HAS_LOGF128
54+
ASSERT_TRUE(FoldResult == nullptr);
55+
#else
56+
auto ConstantLog = dyn_cast<ConstantFP>(FoldResult);
57+
ASSERT_TRUE(ConstantLog);
58+
59+
APFloat APF = ConstantLog->getValueAPF();
60+
char LongDoubleHexString[0xFF];
61+
unsigned Size =
62+
APF.convertToHexString(LongDoubleHexString, 32, true,
63+
APFloatBase::roundingMode::NearestTiesToAway);
64+
EXPECT_GT(Size, 0U);
65+
66+
ASSERT_STREQ(LongDoubleHexString,
67+
std::string("0X1.62E42FEFA39E0000000000000000000P-1").c_str());
68+
#endif
69+
}
70+
71+
INSTANTIATE_TEST_SUITE_P(ConstantFoldLogf128, ConstantFoldLogf128Fixture,
72+
::testing::Values("logl", "llvm.log.f128"));
73+
74+
} // end anonymous namespace

0 commit comments

Comments
 (0)