[clang] [flang] [RFC][flang][runtime] Add FortranFloat128Math wrapper library. (PR #81971)

Slava Zakharin via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 20 09:47:02 PST 2024


https://github.com/vzakhari updated https://github.com/llvm/llvm-project/pull/81971

>From c798a2b74df57a1375882fb13a88ccf946f4bfbf Mon Sep 17 00:00:00 2001
From: Slava Zakharin <szakharin at nvidia.com>
Date: Thu, 15 Feb 2024 20:01:35 -0800
Subject: [PATCH 1/3] [RFC][flang][runtime] Add FortranFloat128Math wrapper
 library.

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.
---
 clang/include/clang/Driver/Driver.h           |  10 ++
 clang/lib/Driver/ToolChains/CommonArgs.cpp    |   8 ++
 flang/CMakeLists.txt                          |  17 +++
 .../flang/Optimizer/Builder/IntrinsicCall.h   |  19 ++--
 flang/lib/Optimizer/Builder/IntrinsicCall.cpp | 101 ++++++++++++------
 flang/runtime/CMakeLists.txt                  |  20 ++++
 flang/runtime/Float128Math/CMakeLists.txt     |  56 ++++++++++
 flang/runtime/Float128Math/cabs.cpp           |  24 +++++
 flang/runtime/Float128Math/math-entries.h     |  77 +++++++++++++
 flang/runtime/Float128Math/sin.cpp            |  22 ++++
 flang/runtime/Float128Math/sqrt.cpp           |  22 ++++
 .../Lower/Intrinsics/missing-math-runtime.f90 |   6 +-
 flang/tools/flang-driver/driver.cpp           |   3 +
 13 files changed, 345 insertions(+), 40 deletions(-)
 create mode 100644 flang/runtime/Float128Math/CMakeLists.txt
 create mode 100644 flang/runtime/Float128Math/cabs.cpp
 create mode 100644 flang/runtime/Float128Math/math-entries.h
 create mode 100644 flang/runtime/Float128Math/sin.cpp
 create mode 100644 flang/runtime/Float128Math/sqrt.cpp

diff --git a/clang/include/clang/Driver/Driver.h b/clang/include/clang/Driver/Driver.h
index 908bc87c14b1ca..a5ca637853a6ae 100644
--- a/clang/include/clang/Driver/Driver.h
+++ b/clang/include/clang/Driver/Driver.h
@@ -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.
@@ -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;
 
diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index 0fd7b8424eb4ba..63d8e2f68f389f 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -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("-lFortranFloat128");
+      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");
   }
diff --git a/flang/CMakeLists.txt b/flang/CMakeLists.txt
index f8ad39ba712f8c..21617aeea0215e 100644
--- a/flang/CMakeLists.txt
+++ b/flang/CMakeLists.txt
@@ -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)
@@ -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)
diff --git a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
index 3f1e22ecca4ccc..7cb99d61a686ed 100644
--- a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
+++ b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
@@ -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 &);
@@ -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);
 
diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
index a3536895ca3b7c..bba53bb57bee51 100644
--- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
+++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
@@ -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, libFuncName, 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");
@@ -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");
@@ -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
@@ -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 ";
@@ -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;
@@ -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;
     }
@@ -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>},
@@ -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},
@@ -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>>,
@@ -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>>,
@@ -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);
 }
 
@@ -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);
   };
diff --git a/flang/runtime/CMakeLists.txt b/flang/runtime/CMakeLists.txt
index dfa9da502db0a8..ac89184a7cbffc 100644
--- a/flang/runtime/CMakeLists.txt
+++ b/flang/runtime/CMakeLists.txt
@@ -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)
@@ -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
diff --git a/flang/runtime/Float128Math/CMakeLists.txt b/flang/runtime/Float128Math/CMakeLists.txt
new file mode 100644
index 00000000000000..f8da4d7ca1a9fe
--- /dev/null
+++ b/flang/runtime/Float128Math/CMakeLists.txt
@@ -0,0 +1,56 @@
+#===-- runtime/Float128Math/CMakeLists.txt ---------------------------------===#
+#
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+#===------------------------------------------------------------------------===#
+
+# FortranFloat128 implements IEEE-754 128-bit float math functions.
+# It is a thin wapper and it currently relies on third-party
+# libraries available for the target.
+# It is distributed as a static library only.
+# Fortran programs/libraries that end up linking any of the provided
+# will have a dependency on the third-party library that is being
+# used for building this FortranFloat128Math library.
+
+if (${FLANG_RUNTIME_F128_MATH_LIB} STREQUAL "libquadmath" OR
+    ${FLANG_RUNTIME_F128_MATH_LIB} STREQUAL "quadmath")
+  check_include_file(quadmath.h FOUND_QUADMATH_HEADER)
+  if(FOUND_QUADMATH_HEADER)
+    add_compile_definitions(HAS_QUADMATHLIB)
+  else()
+    message(FATAL_ERROR
+      "FLANG_RUNTIME_F128_MATH_LIB setting requires quadmath.h "
+      "to be available: ${FLANG_RUNTIME_F128_MATH_LIB}"
+      )
+  endif()
+else()
+  message(FATAL_ERROR
+    "Unsupported third-party library for Fortran F128 math runtime: "
+    "${FLANG_RUNTIME_F128_MATH_LIB}"
+    )
+endif()
+
+set(sources
+  cabs.cpp
+  sin.cpp
+  sqrt.cpp
+  )
+
+include_directories(AFTER "${CMAKE_CURRENT_SOURCE_DIR}/..")
+add_flang_library(FortranFloat128Math STATIC INSTALL_WITH_TOOLCHAIN ${sources})
+
+if (DEFINED MSVC)
+  set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded)
+  add_flang_library(FortranFloat128Math.static STATIC INSTALL_WITH_TOOLCHAIN
+    ${sources}
+    )
+  set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreadedDebug)
+  add_flang_library(FortranFloat128Math.static_dbg STATIC INSTALL_WITH_TOOLCHAIN
+    ${sources}
+    )
+  add_dependencies(FortranFloat128Math FortranFloat128Math.static
+    FortranFloat128Math.static_dbg
+    )
+endif()
diff --git a/flang/runtime/Float128Math/cabs.cpp b/flang/runtime/Float128Math/cabs.cpp
new file mode 100644
index 00000000000000..63f2bdf8e177ae
--- /dev/null
+++ b/flang/runtime/Float128Math/cabs.cpp
@@ -0,0 +1,24 @@
+//===-- runtime/Float128Math/cabs.cpp -------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "math-entries.h"
+
+namespace Fortran::runtime {
+extern "C" {
+
+#if LDBL_MANT_DIG == 113 || HAS_FLOAT128
+// FIXME: the argument should be CppTypeFor<TypeCategory::Complex, 16>,
+// and it should be translated into the underlying library's
+// corresponding complex128 type.
+CppTypeFor<TypeCategory::Real, 16> RTDEF(CAbsF128)(ComplexF128 x) {
+  return CAbs<RTNAME(CAbsF128)>::invoke(x);
+}
+#endif
+
+} // extern "C"
+} // namespace Fortran::runtime
diff --git a/flang/runtime/Float128Math/math-entries.h b/flang/runtime/Float128Math/math-entries.h
new file mode 100644
index 00000000000000..91c14b008b5768
--- /dev/null
+++ b/flang/runtime/Float128Math/math-entries.h
@@ -0,0 +1,77 @@
+//===-- runtime/Float128Math/math-entries.h ---------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_RUNTIME_FLOAT128MATH_MATH_ENTRIES_H_
+#define FORTRAN_RUNTIME_FLOAT128MATH_MATH_ENTRIES_H_
+#include "terminator.h"
+#include "tools.h"
+#include "flang/Common/float128.h"
+#include "flang/Runtime/entry-names.h"
+#include <type_traits>
+
+namespace Fortran::runtime {
+
+// Define a class template to gracefully fail, when
+// there is no specialized template that implements
+// the required function via using the third-party
+// implementation.
+#define DEFINE_FALLBACK(caller) \
+  template <auto F> struct caller { \
+    template <typename... ATs> \
+    [[noreturn]] static std::invoke_result_t<decltype(F), ATs...> invoke( \
+        ATs... args) { \
+      Terminator terminator{__FILE__, __LINE__}; \
+      terminator.Crash("Float128 variant of '%s' is unsupported", #caller); \
+    } \
+  };
+
+// Define template specialization that is calling the third-party
+// implementation. The template is specialized by a function pointer
+// that is the FortranFloat128Math entry point. The signatures
+// of the caller and the callee must match.
+//
+// Defining the specialization for any target library requires
+// adding the generic template via DEFINE_FALLBACK, so that
+// a build with another target library that does not define
+// the same alias can gracefully fail in runtime.
+#define DEFINE_SIMPLE_ALIAS(caller, callee) \
+  template <typename RT, typename... ATs, RT (*p)(ATs...)> struct caller<p> { \
+    static RT invoke(ATs... args) { \
+      static_assert(std::is_invocable_r_v<RT, decltype(callee), ATs...>); \
+      if constexpr (std::is_same_v<RT, void>) { \
+        callee(args...); \
+      } else { \
+        return callee(args...); \
+      } \
+    } \
+  };
+
+// Define fallback callers.
+DEFINE_FALLBACK(CAbs)
+DEFINE_FALLBACK(Sin)
+DEFINE_FALLBACK(Sqrt)
+
+// Define ComplexF128 type that is compatible with
+// the type of results/arguments of libquadmath.
+// TODO: this may need more work for other libraries/compilers.
+#if !defined(_ARCH_PPC) || defined(__LONG_DOUBLE_IEEE128__)
+typedef _Complex float __attribute__((mode(TC))) ComplexF128;
+#else
+typedef _Complex float __attribute__((mode(KC))) ComplexF128;
+#endif
+
+#if HAS_QUADMATHLIB
+// Define wrapper callers for libquadmath.
+#include "quadmath.h"
+DEFINE_SIMPLE_ALIAS(CAbs, cabsq)
+DEFINE_SIMPLE_ALIAS(Sin, sinq)
+DEFINE_SIMPLE_ALIAS(Sqrt, sqrtq)
+#endif
+} // namespace Fortran::runtime
+
+#endif // FORTRAN_RUNTIME_FLOAT128MATH_MATH_ENTRIES_H_
diff --git a/flang/runtime/Float128Math/sin.cpp b/flang/runtime/Float128Math/sin.cpp
new file mode 100644
index 00000000000000..013eb9d119a6a3
--- /dev/null
+++ b/flang/runtime/Float128Math/sin.cpp
@@ -0,0 +1,22 @@
+//===-- runtime/Float128Math/sin.cpp --------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "math-entries.h"
+
+namespace Fortran::runtime {
+extern "C" {
+
+#if LDBL_MANT_DIG == 113 || HAS_FLOAT128
+CppTypeFor<TypeCategory::Real, 16> RTDEF(SinF128)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Sin<RTNAME(SinF128)>::invoke(x);
+}
+#endif
+
+} // extern "C"
+} // namespace Fortran::runtime
diff --git a/flang/runtime/Float128Math/sqrt.cpp b/flang/runtime/Float128Math/sqrt.cpp
new file mode 100644
index 00000000000000..aafbd850ca973a
--- /dev/null
+++ b/flang/runtime/Float128Math/sqrt.cpp
@@ -0,0 +1,22 @@
+//===-- runtime/Float128Math/sqrt.cpp -------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "math-entries.h"
+
+namespace Fortran::runtime {
+extern "C" {
+
+#if LDBL_MANT_DIG == 113 || HAS_FLOAT128
+CppTypeFor<TypeCategory::Real, 16> RTDEF(SqrtF128)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Sqrt<RTNAME(SqrtF128)>::invoke(x);
+}
+#endif
+
+} // extern "C"
+} // namespace Fortran::runtime
diff --git a/flang/test/Lower/Intrinsics/missing-math-runtime.f90 b/flang/test/Lower/Intrinsics/missing-math-runtime.f90
index 98d3abb17f3a8f..ff767ba18faaec 100644
--- a/flang/test/Lower/Intrinsics/missing-math-runtime.f90
+++ b/flang/test/Lower/Intrinsics/missing-math-runtime.f90
@@ -1,10 +1,14 @@
 ! There is no quad math runtime available in lowering
 ! for now. Test that the TODO are emitted correctly.
+! FIXME: the lit config has to flip a feature flag so that
+! the tests can use different checks depending on whether
+! REAL(16) math support is enabled or not.
+! XFAIL: *
 ! RUN: bbc -emit-fir %s -o /dev/null 2>&1 | FileCheck %s
 
  complex(16) :: a
  real(16) :: b
-! CHECK: not yet implemented: no math runtime available for 'ABS(COMPLEX(KIND=16))'
+! CHECK: compiler is built without support for 'ABS(COMPLEX(KIND=16))'
  b = abs(a)
 end
 
diff --git a/flang/tools/flang-driver/driver.cpp b/flang/tools/flang-driver/driver.cpp
index c4e56a862c8613..52136df10c0b02 100644
--- a/flang/tools/flang-driver/driver.cpp
+++ b/flang/tools/flang-driver/driver.cpp
@@ -130,6 +130,9 @@ int main(int argc, const char **argv) {
                                   llvm::sys::getDefaultTargetTriple(), diags,
                                   "flang LLVM compiler");
   theDriver.setTargetAndMode(targetandMode);
+#ifdef FLANG_RUNTIME_F128_MATH_LIB
+  theDriver.setFlangF128MathLibrary(FLANG_RUNTIME_F128_MATH_LIB);
+#endif
   std::unique_ptr<clang::driver::Compilation> c(
       theDriver.BuildCompilation(args));
   llvm::SmallVector<std::pair<int, const clang::driver::Command *>, 4>

>From ca8b5e868225cd2cd5be5a5bc3c65234899eb576 Mon Sep 17 00:00:00 2001
From: Slava Zakharin <szakharin at nvidia.com>
Date: Mon, 19 Feb 2024 20:47:35 -0800
Subject: [PATCH 2/3] Fixed FLANG_RUNTIME_F128_MATH_LIB build.

---
 flang/lib/Optimizer/Builder/IntrinsicCall.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
index bba53bb57bee51..3a82be895d37c4 100644
--- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
+++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
@@ -703,7 +703,7 @@ static mlir::Value genLibF128Call(fir::FirOpBuilder &builder,
       libFuncType);
   fir::emitFatalError(loc, message, /*genCrashDiag=*/false);
 #else  // FLANG_RUNTIME_F128_MATH_LIB
-  return genLibCall(builder, loc, libFuncName, libFuncType, args);
+  return genLibCall(builder, loc, mathOp, libFuncType, args);
 #endif // FLANG_RUNTIME_F128_MATH_LIB
 }
 

>From aa34f08f760d0ba95886ad315b74cb52418e473e Mon Sep 17 00:00:00 2001
From: Slava Zakharin <szakharin at nvidia.com>
Date: Tue, 20 Feb 2024 09:44:43 -0800
Subject: [PATCH 3/3] Fixed the linked library name in clang driver.

---
 clang/lib/Driver/ToolChains/CommonArgs.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index 63d8e2f68f389f..e5196bd8b5ae9e 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -1288,7 +1288,7 @@ void tools::addFortranRuntimeLibs(const ToolChain &TC, const ArgList &Args,
     StringRef f128LibName = TC.getDriver().getFlangF128MathLibrary();
     f128LibName.consume_front_insensitive("lib");
     if (!f128LibName.empty()) {
-      CmdArgs.push_back("-lFortranFloat128");
+      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);



More information about the cfe-commits mailing list