Skip to content

[RFC][flang][runtime] Add FortranFloat128Math wrapper library. #81971

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

Merged
merged 3 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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