Skip to content

Commit a468d02

Browse files
authored
[flang][runtime] Add FortranFloat128Math wrapper library. (#81971)
Implemented few entry points for REAL(16) math in FortranF128Math static library. It is a thin wrapper around GNU libquadmath. Flang driver can always link it, and the dependencies will be brought in as needed. The final Fortran program/library that uses any of the entry points will depend on the underlying third-party library - this dependency has to be resolved somehow. I added FLANG_RUNTIME_F128_MATH_LIB CMake control so that the compiler driver and the runtime library can be built using the same third-party library: this way the linker knows which dependency to link in (under --as-needed). The compiler distribution should specify which third-party library is required for linking/running the apps that use REAL(16). The compiler package may provide a version of the third-party library or at least a stub library that can be used for linking, but the final program execution will still require the actual library.
1 parent cd4e246 commit a468d02

File tree

13 files changed

+345
-40
lines changed

13 files changed

+345
-40
lines changed

clang/include/clang/Driver/Driver.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,11 @@ class Driver {
251251
/// from non-system headers are emitted.
252252
HeaderIncludeFilteringKind CCPrintHeadersFiltering = HIFIL_None;
253253

254+
/// Name of the library that provides implementations of
255+
/// IEEE-754 128-bit float math functions used by Fortran F128
256+
/// runtime library. It should be linked as needed by the linker job.
257+
std::string FlangF128MathLibrary;
258+
254259
/// Set CC_LOG_DIAGNOSTICS mode, which causes the frontend to log diagnostics
255260
/// to CCLogDiagnosticsFilename or to stderr, in a stable machine readable
256261
/// format.
@@ -440,6 +445,11 @@ class Driver {
440445
bool offloadHostOnly() const { return Offload == OffloadHost; }
441446
bool offloadDeviceOnly() const { return Offload == OffloadDevice; }
442447

448+
void setFlangF128MathLibrary(std::string name) {
449+
FlangF128MathLibrary = std::move(name);
450+
}
451+
StringRef getFlangF128MathLibrary() const { return FlangF128MathLibrary; }
452+
443453
/// Compute the desired OpenMP runtime from the flags provided.
444454
OpenMPRuntimeKind getOpenMPRuntime(const llvm::opt::ArgList &Args) const;
445455

clang/lib/Driver/ToolChains/CommonArgs.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,6 +1285,14 @@ void tools::addFortranRuntimeLibs(const ToolChain &TC, const ArgList &Args,
12851285
// add the correct libraries to link against as dependents in the object
12861286
// file.
12871287
if (!TC.getTriple().isKnownWindowsMSVCEnvironment()) {
1288+
StringRef f128LibName = TC.getDriver().getFlangF128MathLibrary();
1289+
f128LibName.consume_front_insensitive("lib");
1290+
if (!f128LibName.empty()) {
1291+
CmdArgs.push_back("-lFortranFloat128Math");
1292+
addAsNeededOption(TC, Args, CmdArgs, /*as_needed=*/true);
1293+
CmdArgs.push_back(Args.MakeArgString("-l" + f128LibName));
1294+
addAsNeededOption(TC, Args, CmdArgs, /*as_needed=*/false);
1295+
}
12881296
CmdArgs.push_back("-lFortranRuntime");
12891297
CmdArgs.push_back("-lFortranDecimal");
12901298
}

flang/CMakeLists.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,17 @@ endif()
3333

3434
option(FLANG_ENABLE_WERROR "Fail and stop building flang if a warning is triggered." OFF)
3535

36+
# The out of tree builds of the compiler and the Fortran runtime
37+
# must use the same setting of FLANG_RUNTIME_F128_MATH_LIB
38+
# to be composable. Failure to synchronize this setting may result
39+
# in linking errors or fatal failures in F128 runtime functions.
40+
set(FLANG_RUNTIME_F128_MATH_LIB "" CACHE STRING
41+
"Specifies the target library used for implementing IEEE-754 128-bit float \
42+
math in F18 runtime, e.g. it might be libquadmath for targets where \
43+
REAL(16) is mapped to __float128, or libm for targets where REAL(16) \
44+
is mapped to long double, etc."
45+
)
46+
3647
# Check for a standalone build and configure as appropriate from
3748
# there.
3849
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
@@ -321,6 +332,12 @@ if (FLANG_REPOSITORY_STRING)
321332
add_definitions(-DFLANG_REPOSITORY_STRING="${FLANG_REPOSITORY_STRING}")
322333
endif()
323334

335+
if (FLANG_RUNTIME_F128_MATH_LIB)
336+
add_compile_definitions(
337+
-DFLANG_RUNTIME_F128_MATH_LIB="${FLANG_RUNTIME_F128_MATH_LIB}"
338+
)
339+
endif()
340+
324341
include(TestBigEndian)
325342
test_big_endian(IS_BIGENDIAN)
326343
if (IS_BIGENDIAN)

flang/include/flang/Optimizer/Builder/IntrinsicCall.h

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -494,12 +494,13 @@ struct RuntimeFunction {
494494
fir::runtime::FuncTypeBuilderFunc typeGenerator;
495495
};
496496

497-
/// Callback type for generating lowering for a math operation.
498-
using MathGeneratorTy = mlir::Value (*)(fir::FirOpBuilder &, mlir::Location,
499-
llvm::StringRef, mlir::FunctionType,
500-
llvm::ArrayRef<mlir::Value>);
501-
502497
struct MathOperation {
498+
// Callback type for generating lowering for a math operation.
499+
using MathGeneratorTy = mlir::Value (*)(fir::FirOpBuilder &, mlir::Location,
500+
const MathOperation &,
501+
mlir::FunctionType,
502+
llvm::ArrayRef<mlir::Value>);
503+
503504
// Overrides fir::runtime::FuncTypeBuilderFunc to add FirOpBuilder argument.
504505
using FuncTypeBuilderFunc = mlir::FunctionType (*)(mlir::MLIRContext *,
505506
fir::FirOpBuilder &);
@@ -681,25 +682,25 @@ getTypesForArgs(llvm::ArrayRef<mlir::Value> args) {
681682
}
682683

683684
mlir::Value genLibCall(fir::FirOpBuilder &builder, mlir::Location loc,
684-
llvm::StringRef libFuncName,
685+
const MathOperation &mathOp,
685686
mlir::FunctionType libFuncType,
686687
llvm::ArrayRef<mlir::Value> args);
687688

688689
template <typename T>
689690
mlir::Value genMathOp(fir::FirOpBuilder &builder, mlir::Location loc,
690-
llvm::StringRef mathLibFuncName,
691+
const MathOperation &mathOp,
691692
mlir::FunctionType mathLibFuncType,
692693
llvm::ArrayRef<mlir::Value> args);
693694

694695
template <typename T>
695696
mlir::Value genComplexMathOp(fir::FirOpBuilder &builder, mlir::Location loc,
696-
llvm::StringRef mathLibFuncName,
697+
const MathOperation &mathOp,
697698
mlir::FunctionType mathLibFuncType,
698699
llvm::ArrayRef<mlir::Value> args);
699700

700701
mlir::Value genLibSplitComplexArgsCall(fir::FirOpBuilder &builder,
701702
mlir::Location loc,
702-
llvm::StringRef libFuncName,
703+
const MathOperation &mathOp,
703704
mlir::FunctionType libFuncType,
704705
llvm::ArrayRef<mlir::Value> args);
705706

flang/lib/Optimizer/Builder/IntrinsicCall.cpp

Lines changed: 71 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -657,10 +657,61 @@ static llvm::cl::opt<bool>
657657
"instead of libm complex operations"),
658658
llvm::cl::init(false));
659659

660+
/// Return a string containing the given Fortran intrinsic name
661+
/// with the type of its arguments specified in funcType
662+
/// surrounded by the given prefix/suffix.
663+
static std::string
664+
prettyPrintIntrinsicName(fir::FirOpBuilder &builder, mlir::Location loc,
665+
llvm::StringRef prefix, llvm::StringRef name,
666+
llvm::StringRef suffix, mlir::FunctionType funcType) {
667+
std::string output = prefix.str();
668+
llvm::raw_string_ostream sstream(output);
669+
if (name == "pow") {
670+
assert(funcType.getNumInputs() == 2 && "power operator has two arguments");
671+
std::string displayName{" ** "};
672+
sstream << numericMlirTypeToFortran(builder, funcType.getInput(0), loc,
673+
displayName)
674+
<< displayName
675+
<< numericMlirTypeToFortran(builder, funcType.getInput(1), loc,
676+
displayName);
677+
} else {
678+
sstream << name.upper() << "(";
679+
if (funcType.getNumInputs() > 0)
680+
sstream << numericMlirTypeToFortran(builder, funcType.getInput(0), loc,
681+
name);
682+
for (mlir::Type argType : funcType.getInputs().drop_front()) {
683+
sstream << ", " << numericMlirTypeToFortran(builder, argType, loc, name);
684+
}
685+
sstream << ")";
686+
}
687+
sstream << suffix;
688+
return output;
689+
}
690+
691+
// Generate a call to the Fortran runtime library providing
692+
// support for 128-bit float math via a third-party library.
693+
// If the compiler is built without FLANG_RUNTIME_F128_MATH_LIB,
694+
// this function will report an error.
695+
static mlir::Value genLibF128Call(fir::FirOpBuilder &builder,
696+
mlir::Location loc,
697+
const MathOperation &mathOp,
698+
mlir::FunctionType libFuncType,
699+
llvm::ArrayRef<mlir::Value> args) {
700+
#ifndef FLANG_RUNTIME_F128_MATH_LIB
701+
std::string message = prettyPrintIntrinsicName(
702+
builder, loc, "compiler is built without support for '", mathOp.key, "'",
703+
libFuncType);
704+
fir::emitFatalError(loc, message, /*genCrashDiag=*/false);
705+
#else // FLANG_RUNTIME_F128_MATH_LIB
706+
return genLibCall(builder, loc, mathOp, libFuncType, args);
707+
#endif // FLANG_RUNTIME_F128_MATH_LIB
708+
}
709+
660710
mlir::Value genLibCall(fir::FirOpBuilder &builder, mlir::Location loc,
661-
llvm::StringRef libFuncName,
711+
const MathOperation &mathOp,
662712
mlir::FunctionType libFuncType,
663713
llvm::ArrayRef<mlir::Value> args) {
714+
llvm::StringRef libFuncName = mathOp.runtimeFunc;
664715
LLVM_DEBUG(llvm::dbgs() << "Generating '" << libFuncName
665716
<< "' call with type ";
666717
libFuncType.dump(); llvm::dbgs() << "\n");
@@ -718,7 +769,7 @@ mlir::Value genLibCall(fir::FirOpBuilder &builder, mlir::Location loc,
718769

719770
mlir::Value genLibSplitComplexArgsCall(fir::FirOpBuilder &builder,
720771
mlir::Location loc,
721-
llvm::StringRef libFuncName,
772+
const MathOperation &mathOp,
722773
mlir::FunctionType libFuncType,
723774
llvm::ArrayRef<mlir::Value> args) {
724775
assert(args.size() == 2 && "Incorrect #args to genLibSplitComplexArgsCall");
@@ -762,13 +813,12 @@ mlir::Value genLibSplitComplexArgsCall(fir::FirOpBuilder &builder,
762813
cplx2, /*isImagPart=*/true);
763814
splitArgs.push_back(imag2);
764815

765-
return genLibCall(builder, loc, libFuncName, getSplitComplexArgsType(),
766-
splitArgs);
816+
return genLibCall(builder, loc, mathOp, getSplitComplexArgsType(), splitArgs);
767817
}
768818

769819
template <typename T>
770820
mlir::Value genMathOp(fir::FirOpBuilder &builder, mlir::Location loc,
771-
llvm::StringRef mathLibFuncName,
821+
const MathOperation &mathOp,
772822
mlir::FunctionType mathLibFuncType,
773823
llvm::ArrayRef<mlir::Value> args) {
774824
// TODO: we have to annotate the math operations with flags
@@ -791,13 +841,14 @@ mlir::Value genMathOp(fir::FirOpBuilder &builder, mlir::Location loc,
791841
// can be also lowered to libm calls for "fast" and "relaxed"
792842
// modes.
793843
mlir::Value result;
844+
llvm::StringRef mathLibFuncName = mathOp.runtimeFunc;
794845
if (mathRuntimeVersion == preciseVersion &&
795846
// Some operations do not have to be lowered as conservative
796847
// calls, since they do not affect strict FP behavior.
797848
// For example, purely integer operations like exponentiation
798849
// with integer operands fall into this class.
799850
!mathLibFuncName.empty()) {
800-
result = genLibCall(builder, loc, mathLibFuncName, mathLibFuncType, args);
851+
result = genLibCall(builder, loc, mathOp, mathLibFuncType, args);
801852
} else {
802853
LLVM_DEBUG(llvm::dbgs() << "Generating '" << mathLibFuncName
803854
<< "' operation with type ";
@@ -810,7 +861,7 @@ mlir::Value genMathOp(fir::FirOpBuilder &builder, mlir::Location loc,
810861

811862
template <typename T>
812863
mlir::Value genComplexMathOp(fir::FirOpBuilder &builder, mlir::Location loc,
813-
llvm::StringRef mathLibFuncName,
864+
const MathOperation &mathOp,
814865
mlir::FunctionType mathLibFuncType,
815866
llvm::ArrayRef<mlir::Value> args) {
816867
mlir::Value result;
@@ -819,11 +870,12 @@ mlir::Value genComplexMathOp(fir::FirOpBuilder &builder, mlir::Location loc,
819870

820871
// If we have libm functions, we can attempt to generate the more precise
821872
// version of the complex math operation.
873+
llvm::StringRef mathLibFuncName = mathOp.runtimeFunc;
822874
if (!mathLibFuncName.empty()) {
823875
// If we enabled MLIR complex or can use approximate operations, we should
824876
// NOT use libm.
825877
if (!forceMlirComplex && !canUseApprox) {
826-
result = genLibCall(builder, loc, mathLibFuncName, mathLibFuncType, args);
878+
result = genLibCall(builder, loc, mathOp, mathLibFuncType, args);
827879
LLVM_DEBUG(result.dump(); llvm::dbgs() << "\n");
828880
return result;
829881
}
@@ -863,6 +915,10 @@ mlir::Value genComplexMathOp(fir::FirOpBuilder &builder, mlir::Location loc,
863915
/// TODO: support remaining Fortran math intrinsics.
864916
/// See https://gcc.gnu.org/onlinedocs/gcc-12.1.0/gfortran/\
865917
/// Intrinsic-Procedures.html for a reference.
918+
constexpr auto FuncTypeReal16Real16 = genFuncType<Ty::Real<16>, Ty::Real<16>>;
919+
constexpr auto FuncTypeReal16Complex16 =
920+
genFuncType<Ty::Real<16>, Ty::Complex<16>>;
921+
866922
static constexpr MathOperation mathOperations[] = {
867923
{"abs", "fabsf", genFuncType<Ty::Real<4>, Ty::Real<4>>,
868924
genMathOp<mlir::math::AbsFOp>},
@@ -874,6 +930,7 @@ static constexpr MathOperation mathOperations[] = {
874930
genComplexMathOp<mlir::complex::AbsOp>},
875931
{"abs", "cabs", genFuncType<Ty::Real<8>, Ty::Complex<8>>,
876932
genComplexMathOp<mlir::complex::AbsOp>},
933+
{"abs", RTNAME_STRING(CAbsF128), FuncTypeReal16Complex16, genLibF128Call},
877934
{"acos", "acosf", genFuncType<Ty::Real<4>, Ty::Real<4>>, genLibCall},
878935
{"acos", "acos", genFuncType<Ty::Real<8>, Ty::Real<8>>, genLibCall},
879936
{"acos", "cacosf", genFuncType<Ty::Complex<4>, Ty::Complex<4>>, genLibCall},
@@ -1110,6 +1167,7 @@ static constexpr MathOperation mathOperations[] = {
11101167
genMathOp<mlir::math::SinOp>},
11111168
{"sin", "sin", genFuncType<Ty::Real<8>, Ty::Real<8>>,
11121169
genMathOp<mlir::math::SinOp>},
1170+
{"sin", RTNAME_STRING(SinF128), FuncTypeReal16Real16, genLibF128Call},
11131171
{"sin", "csinf", genFuncType<Ty::Complex<4>, Ty::Complex<4>>,
11141172
genComplexMathOp<mlir::complex::SinOp>},
11151173
{"sin", "csin", genFuncType<Ty::Complex<8>, Ty::Complex<8>>,
@@ -1122,6 +1180,7 @@ static constexpr MathOperation mathOperations[] = {
11221180
genMathOp<mlir::math::SqrtOp>},
11231181
{"sqrt", "sqrt", genFuncType<Ty::Real<8>, Ty::Real<8>>,
11241182
genMathOp<mlir::math::SqrtOp>},
1183+
{"sqrt", RTNAME_STRING(SqrtF128), FuncTypeReal16Real16, genLibF128Call},
11251184
{"sqrt", "csqrtf", genFuncType<Ty::Complex<4>, Ty::Complex<4>>,
11261185
genComplexMathOp<mlir::complex::SqrtOp>},
11271186
{"sqrt", "csqrt", genFuncType<Ty::Complex<8>, Ty::Complex<8>>,
@@ -1345,27 +1404,9 @@ static void checkPrecisionLoss(llvm::StringRef name,
13451404
// lowering and could be used here. Emit an error and continue
13461405
// generating the code with the narrowing cast so that the user
13471406
// can get a complete list of the problematic intrinsic calls.
1348-
std::string message("not yet implemented: no math runtime available for '");
1349-
llvm::raw_string_ostream sstream(message);
1350-
if (name == "pow") {
1351-
assert(funcType.getNumInputs() == 2 && "power operator has two arguments");
1352-
std::string displayName{" ** "};
1353-
sstream << numericMlirTypeToFortran(builder, funcType.getInput(0), loc,
1354-
displayName)
1355-
<< displayName
1356-
<< numericMlirTypeToFortran(builder, funcType.getInput(1), loc,
1357-
displayName);
1358-
} else {
1359-
sstream << name.upper() << "(";
1360-
if (funcType.getNumInputs() > 0)
1361-
sstream << numericMlirTypeToFortran(builder, funcType.getInput(0), loc,
1362-
name);
1363-
for (mlir::Type argType : funcType.getInputs().drop_front()) {
1364-
sstream << ", " << numericMlirTypeToFortran(builder, argType, loc, name);
1365-
}
1366-
sstream << ")";
1367-
}
1368-
sstream << "'";
1407+
std::string message = prettyPrintIntrinsicName(
1408+
builder, loc, "not yet implemented: no math runtime available for '",
1409+
name, "'", funcType);
13691410
mlir::emitError(loc, message);
13701411
}
13711412

@@ -1887,7 +1928,7 @@ IntrinsicLibrary::getRuntimeCallGenerator(llvm::StringRef name,
18871928
for (auto [fst, snd] : llvm::zip(actualFuncType.getInputs(), args))
18881929
convertedArguments.push_back(builder.createConvert(loc, fst, snd));
18891930
mlir::Value result = mathOp->funcGenerator(
1890-
builder, loc, mathOp->runtimeFunc, actualFuncType, convertedArguments);
1931+
builder, loc, *mathOp, actualFuncType, convertedArguments);
18911932
mlir::Type soughtType = soughtFuncType.getResult(0);
18921933
return builder.createConvert(loc, soughtType, result);
18931934
};

flang/runtime/CMakeLists.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,23 @@ if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
4646
endif ()
4747
include_directories(BEFORE
4848
${FLANG_SOURCE_DIR}/include)
49+
50+
# The out of tree builds of the compiler and the Fortran runtime
51+
# must use the same setting of FLANG_RUNTIME_F128_MATH_LIB
52+
# to be composable. Failure to synchronize this setting may result
53+
# in linking errors or fatal failures in F128 runtime functions.
54+
set(FLANG_RUNTIME_F128_MATH_LIB "" CACHE STRING
55+
"Specifies the target library used for implementing IEEE-754 128-bit float \
56+
math in F18 runtime, e.g. it might be libquadmath for targets where \
57+
REAL(16) is mapped to __float128, or libm for targets where REAL(16) \
58+
is mapped to long double, etc."
59+
)
60+
61+
if (NOT FLANG_RUNTIME_F128_MATH_LIB STREQUAL "")
62+
add_compile_definitions(
63+
-DFLANG_RUNTIME_F128_MATH_LIB="${FLANG_RUNTIME_F128_MATH_LIB}"
64+
)
65+
endif()
4966
endif()
5067

5168
include(CheckCXXSymbolExists)
@@ -83,6 +100,9 @@ add_definitions(-U_GLIBCXX_ASSERTIONS)
83100
add_definitions(-U_LIBCPP_ENABLE_ASSERTIONS)
84101

85102
add_subdirectory(FortranMain)
103+
if (NOT ${FLANG_RUNTIME_F128_MATH_LIB} STREQUAL "")
104+
add_subdirectory(Float128Math)
105+
endif()
86106

87107
set(sources
88108
ISO_Fortran_binding.cpp

0 commit comments

Comments
 (0)