Skip to content

Commit

Permalink
[flang][runtime] Add FortranFloat128Math wrapper library. (#81971)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
vzakhari committed Feb 20, 2024
1 parent cd4e246 commit a468d02
Show file tree
Hide file tree
Showing 13 changed files with 345 additions and 40 deletions.
10 changes: 10 additions & 0 deletions clang/include/clang/Driver/Driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,11 @@ class Driver {
/// from non-system headers are emitted.
HeaderIncludeFilteringKind CCPrintHeadersFiltering = HIFIL_None;

/// Name of the library that provides implementations of
/// IEEE-754 128-bit float math functions used by Fortran F128
/// runtime library. It should be linked as needed by the linker job.
std::string FlangF128MathLibrary;

/// Set CC_LOG_DIAGNOSTICS mode, which causes the frontend to log diagnostics
/// to CCLogDiagnosticsFilename or to stderr, in a stable machine readable
/// format.
Expand Down Expand Up @@ -440,6 +445,11 @@ class Driver {
bool offloadHostOnly() const { return Offload == OffloadHost; }
bool offloadDeviceOnly() const { return Offload == OffloadDevice; }

void setFlangF128MathLibrary(std::string name) {
FlangF128MathLibrary = std::move(name);
}
StringRef getFlangF128MathLibrary() const { return FlangF128MathLibrary; }

/// Compute the desired OpenMP runtime from the flags provided.
OpenMPRuntimeKind getOpenMPRuntime(const llvm::opt::ArgList &Args) const;

Expand Down
8 changes: 8 additions & 0 deletions clang/lib/Driver/ToolChains/CommonArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1285,6 +1285,14 @@ void tools::addFortranRuntimeLibs(const ToolChain &TC, const ArgList &Args,
// add the correct libraries to link against as dependents in the object
// file.
if (!TC.getTriple().isKnownWindowsMSVCEnvironment()) {
StringRef f128LibName = TC.getDriver().getFlangF128MathLibrary();
f128LibName.consume_front_insensitive("lib");
if (!f128LibName.empty()) {
CmdArgs.push_back("-lFortranFloat128Math");
addAsNeededOption(TC, Args, CmdArgs, /*as_needed=*/true);
CmdArgs.push_back(Args.MakeArgString("-l" + f128LibName));
addAsNeededOption(TC, Args, CmdArgs, /*as_needed=*/false);
}
CmdArgs.push_back("-lFortranRuntime");
CmdArgs.push_back("-lFortranDecimal");
}
Expand Down
17 changes: 17 additions & 0 deletions flang/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@ endif()

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

# The out of tree builds of the compiler and the Fortran runtime
# must use the same setting of FLANG_RUNTIME_F128_MATH_LIB
# to be composable. Failure to synchronize this setting may result
# in linking errors or fatal failures in F128 runtime functions.
set(FLANG_RUNTIME_F128_MATH_LIB "" CACHE STRING
"Specifies the target library used for implementing IEEE-754 128-bit float \
math in F18 runtime, e.g. it might be libquadmath for targets where \
REAL(16) is mapped to __float128, or libm for targets where REAL(16) \
is mapped to long double, etc."
)

# Check for a standalone build and configure as appropriate from
# there.
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
Expand Down Expand Up @@ -321,6 +332,12 @@ if (FLANG_REPOSITORY_STRING)
add_definitions(-DFLANG_REPOSITORY_STRING="${FLANG_REPOSITORY_STRING}")
endif()

if (FLANG_RUNTIME_F128_MATH_LIB)
add_compile_definitions(
-DFLANG_RUNTIME_F128_MATH_LIB="${FLANG_RUNTIME_F128_MATH_LIB}"
)
endif()

include(TestBigEndian)
test_big_endian(IS_BIGENDIAN)
if (IS_BIGENDIAN)
Expand Down
19 changes: 10 additions & 9 deletions flang/include/flang/Optimizer/Builder/IntrinsicCall.h
Original file line number Diff line number Diff line change
Expand Up @@ -494,12 +494,13 @@ struct RuntimeFunction {
fir::runtime::FuncTypeBuilderFunc typeGenerator;
};

/// Callback type for generating lowering for a math operation.
using MathGeneratorTy = mlir::Value (*)(fir::FirOpBuilder &, mlir::Location,
llvm::StringRef, mlir::FunctionType,
llvm::ArrayRef<mlir::Value>);

struct MathOperation {
// Callback type for generating lowering for a math operation.
using MathGeneratorTy = mlir::Value (*)(fir::FirOpBuilder &, mlir::Location,
const MathOperation &,
mlir::FunctionType,
llvm::ArrayRef<mlir::Value>);

// Overrides fir::runtime::FuncTypeBuilderFunc to add FirOpBuilder argument.
using FuncTypeBuilderFunc = mlir::FunctionType (*)(mlir::MLIRContext *,
fir::FirOpBuilder &);
Expand Down Expand Up @@ -681,25 +682,25 @@ getTypesForArgs(llvm::ArrayRef<mlir::Value> args) {
}

mlir::Value genLibCall(fir::FirOpBuilder &builder, mlir::Location loc,
llvm::StringRef libFuncName,
const MathOperation &mathOp,
mlir::FunctionType libFuncType,
llvm::ArrayRef<mlir::Value> args);

template <typename T>
mlir::Value genMathOp(fir::FirOpBuilder &builder, mlir::Location loc,
llvm::StringRef mathLibFuncName,
const MathOperation &mathOp,
mlir::FunctionType mathLibFuncType,
llvm::ArrayRef<mlir::Value> args);

template <typename T>
mlir::Value genComplexMathOp(fir::FirOpBuilder &builder, mlir::Location loc,
llvm::StringRef mathLibFuncName,
const MathOperation &mathOp,
mlir::FunctionType mathLibFuncType,
llvm::ArrayRef<mlir::Value> args);

mlir::Value genLibSplitComplexArgsCall(fir::FirOpBuilder &builder,
mlir::Location loc,
llvm::StringRef libFuncName,
const MathOperation &mathOp,
mlir::FunctionType libFuncType,
llvm::ArrayRef<mlir::Value> args);

Expand Down
101 changes: 71 additions & 30 deletions flang/lib/Optimizer/Builder/IntrinsicCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -657,10 +657,61 @@ static llvm::cl::opt<bool>
"instead of libm complex operations"),
llvm::cl::init(false));

/// Return a string containing the given Fortran intrinsic name
/// with the type of its arguments specified in funcType
/// surrounded by the given prefix/suffix.
static std::string
prettyPrintIntrinsicName(fir::FirOpBuilder &builder, mlir::Location loc,
llvm::StringRef prefix, llvm::StringRef name,
llvm::StringRef suffix, mlir::FunctionType funcType) {
std::string output = prefix.str();
llvm::raw_string_ostream sstream(output);
if (name == "pow") {
assert(funcType.getNumInputs() == 2 && "power operator has two arguments");
std::string displayName{" ** "};
sstream << numericMlirTypeToFortran(builder, funcType.getInput(0), loc,
displayName)
<< displayName
<< numericMlirTypeToFortran(builder, funcType.getInput(1), loc,
displayName);
} else {
sstream << name.upper() << "(";
if (funcType.getNumInputs() > 0)
sstream << numericMlirTypeToFortran(builder, funcType.getInput(0), loc,
name);
for (mlir::Type argType : funcType.getInputs().drop_front()) {
sstream << ", " << numericMlirTypeToFortran(builder, argType, loc, name);
}
sstream << ")";
}
sstream << suffix;
return output;
}

// Generate a call to the Fortran runtime library providing
// support for 128-bit float math via a third-party library.
// If the compiler is built without FLANG_RUNTIME_F128_MATH_LIB,
// this function will report an error.
static mlir::Value genLibF128Call(fir::FirOpBuilder &builder,
mlir::Location loc,
const MathOperation &mathOp,
mlir::FunctionType libFuncType,
llvm::ArrayRef<mlir::Value> args) {
#ifndef FLANG_RUNTIME_F128_MATH_LIB
std::string message = prettyPrintIntrinsicName(
builder, loc, "compiler is built without support for '", mathOp.key, "'",
libFuncType);
fir::emitFatalError(loc, message, /*genCrashDiag=*/false);
#else // FLANG_RUNTIME_F128_MATH_LIB
return genLibCall(builder, loc, mathOp, libFuncType, args);
#endif // FLANG_RUNTIME_F128_MATH_LIB
}

mlir::Value genLibCall(fir::FirOpBuilder &builder, mlir::Location loc,
llvm::StringRef libFuncName,
const MathOperation &mathOp,
mlir::FunctionType libFuncType,
llvm::ArrayRef<mlir::Value> args) {
llvm::StringRef libFuncName = mathOp.runtimeFunc;
LLVM_DEBUG(llvm::dbgs() << "Generating '" << libFuncName
<< "' call with type ";
libFuncType.dump(); llvm::dbgs() << "\n");
Expand Down Expand Up @@ -718,7 +769,7 @@ mlir::Value genLibCall(fir::FirOpBuilder &builder, mlir::Location loc,

mlir::Value genLibSplitComplexArgsCall(fir::FirOpBuilder &builder,
mlir::Location loc,
llvm::StringRef libFuncName,
const MathOperation &mathOp,
mlir::FunctionType libFuncType,
llvm::ArrayRef<mlir::Value> args) {
assert(args.size() == 2 && "Incorrect #args to genLibSplitComplexArgsCall");
Expand Down Expand Up @@ -762,13 +813,12 @@ mlir::Value genLibSplitComplexArgsCall(fir::FirOpBuilder &builder,
cplx2, /*isImagPart=*/true);
splitArgs.push_back(imag2);

return genLibCall(builder, loc, libFuncName, getSplitComplexArgsType(),
splitArgs);
return genLibCall(builder, loc, mathOp, getSplitComplexArgsType(), splitArgs);
}

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

template <typename T>
mlir::Value genComplexMathOp(fir::FirOpBuilder &builder, mlir::Location loc,
llvm::StringRef mathLibFuncName,
const MathOperation &mathOp,
mlir::FunctionType mathLibFuncType,
llvm::ArrayRef<mlir::Value> args) {
mlir::Value result;
Expand All @@ -819,11 +870,12 @@ mlir::Value genComplexMathOp(fir::FirOpBuilder &builder, mlir::Location loc,

// If we have libm functions, we can attempt to generate the more precise
// version of the complex math operation.
llvm::StringRef mathLibFuncName = mathOp.runtimeFunc;
if (!mathLibFuncName.empty()) {
// If we enabled MLIR complex or can use approximate operations, we should
// NOT use libm.
if (!forceMlirComplex && !canUseApprox) {
result = genLibCall(builder, loc, mathLibFuncName, mathLibFuncType, args);
result = genLibCall(builder, loc, mathOp, mathLibFuncType, args);
LLVM_DEBUG(result.dump(); llvm::dbgs() << "\n");
return result;
}
Expand Down Expand Up @@ -863,6 +915,10 @@ mlir::Value genComplexMathOp(fir::FirOpBuilder &builder, mlir::Location loc,
/// TODO: support remaining Fortran math intrinsics.
/// See https://gcc.gnu.org/onlinedocs/gcc-12.1.0/gfortran/\
/// Intrinsic-Procedures.html for a reference.
constexpr auto FuncTypeReal16Real16 = genFuncType<Ty::Real<16>, Ty::Real<16>>;
constexpr auto FuncTypeReal16Complex16 =
genFuncType<Ty::Real<16>, Ty::Complex<16>>;

static constexpr MathOperation mathOperations[] = {
{"abs", "fabsf", genFuncType<Ty::Real<4>, Ty::Real<4>>,
genMathOp<mlir::math::AbsFOp>},
Expand All @@ -874,6 +930,7 @@ static constexpr MathOperation mathOperations[] = {
genComplexMathOp<mlir::complex::AbsOp>},
{"abs", "cabs", genFuncType<Ty::Real<8>, Ty::Complex<8>>,
genComplexMathOp<mlir::complex::AbsOp>},
{"abs", RTNAME_STRING(CAbsF128), FuncTypeReal16Complex16, genLibF128Call},
{"acos", "acosf", genFuncType<Ty::Real<4>, Ty::Real<4>>, genLibCall},
{"acos", "acos", genFuncType<Ty::Real<8>, Ty::Real<8>>, genLibCall},
{"acos", "cacosf", genFuncType<Ty::Complex<4>, Ty::Complex<4>>, genLibCall},
Expand Down Expand Up @@ -1110,6 +1167,7 @@ static constexpr MathOperation mathOperations[] = {
genMathOp<mlir::math::SinOp>},
{"sin", "sin", genFuncType<Ty::Real<8>, Ty::Real<8>>,
genMathOp<mlir::math::SinOp>},
{"sin", RTNAME_STRING(SinF128), FuncTypeReal16Real16, genLibF128Call},
{"sin", "csinf", genFuncType<Ty::Complex<4>, Ty::Complex<4>>,
genComplexMathOp<mlir::complex::SinOp>},
{"sin", "csin", genFuncType<Ty::Complex<8>, Ty::Complex<8>>,
Expand All @@ -1122,6 +1180,7 @@ static constexpr MathOperation mathOperations[] = {
genMathOp<mlir::math::SqrtOp>},
{"sqrt", "sqrt", genFuncType<Ty::Real<8>, Ty::Real<8>>,
genMathOp<mlir::math::SqrtOp>},
{"sqrt", RTNAME_STRING(SqrtF128), FuncTypeReal16Real16, genLibF128Call},
{"sqrt", "csqrtf", genFuncType<Ty::Complex<4>, Ty::Complex<4>>,
genComplexMathOp<mlir::complex::SqrtOp>},
{"sqrt", "csqrt", genFuncType<Ty::Complex<8>, Ty::Complex<8>>,
Expand Down Expand Up @@ -1345,27 +1404,9 @@ static void checkPrecisionLoss(llvm::StringRef name,
// lowering and could be used here. Emit an error and continue
// generating the code with the narrowing cast so that the user
// can get a complete list of the problematic intrinsic calls.
std::string message("not yet implemented: no math runtime available for '");
llvm::raw_string_ostream sstream(message);
if (name == "pow") {
assert(funcType.getNumInputs() == 2 && "power operator has two arguments");
std::string displayName{" ** "};
sstream << numericMlirTypeToFortran(builder, funcType.getInput(0), loc,
displayName)
<< displayName
<< numericMlirTypeToFortran(builder, funcType.getInput(1), loc,
displayName);
} else {
sstream << name.upper() << "(";
if (funcType.getNumInputs() > 0)
sstream << numericMlirTypeToFortran(builder, funcType.getInput(0), loc,
name);
for (mlir::Type argType : funcType.getInputs().drop_front()) {
sstream << ", " << numericMlirTypeToFortran(builder, argType, loc, name);
}
sstream << ")";
}
sstream << "'";
std::string message = prettyPrintIntrinsicName(
builder, loc, "not yet implemented: no math runtime available for '",
name, "'", funcType);
mlir::emitError(loc, message);
}

Expand Down Expand Up @@ -1887,7 +1928,7 @@ IntrinsicLibrary::getRuntimeCallGenerator(llvm::StringRef name,
for (auto [fst, snd] : llvm::zip(actualFuncType.getInputs(), args))
convertedArguments.push_back(builder.createConvert(loc, fst, snd));
mlir::Value result = mathOp->funcGenerator(
builder, loc, mathOp->runtimeFunc, actualFuncType, convertedArguments);
builder, loc, *mathOp, actualFuncType, convertedArguments);
mlir::Type soughtType = soughtFuncType.getResult(0);
return builder.createConvert(loc, soughtType, result);
};
Expand Down
20 changes: 20 additions & 0 deletions flang/runtime/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,23 @@ if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
endif ()
include_directories(BEFORE
${FLANG_SOURCE_DIR}/include)

# The out of tree builds of the compiler and the Fortran runtime
# must use the same setting of FLANG_RUNTIME_F128_MATH_LIB
# to be composable. Failure to synchronize this setting may result
# in linking errors or fatal failures in F128 runtime functions.
set(FLANG_RUNTIME_F128_MATH_LIB "" CACHE STRING
"Specifies the target library used for implementing IEEE-754 128-bit float \
math in F18 runtime, e.g. it might be libquadmath for targets where \
REAL(16) is mapped to __float128, or libm for targets where REAL(16) \
is mapped to long double, etc."
)

if (NOT FLANG_RUNTIME_F128_MATH_LIB STREQUAL "")
add_compile_definitions(
-DFLANG_RUNTIME_F128_MATH_LIB="${FLANG_RUNTIME_F128_MATH_LIB}"
)
endif()
endif()

include(CheckCXXSymbolExists)
Expand Down Expand Up @@ -83,6 +100,9 @@ add_definitions(-U_GLIBCXX_ASSERTIONS)
add_definitions(-U_LIBCPP_ENABLE_ASSERTIONS)

add_subdirectory(FortranMain)
if (NOT ${FLANG_RUNTIME_F128_MATH_LIB} STREQUAL "")
add_subdirectory(Float128Math)
endif()

set(sources
ISO_Fortran_binding.cpp
Expand Down
Loading

0 comments on commit a468d02

Please sign in to comment.