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

Conversation

vzakhari
Copy link
Contributor

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.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' flang:driver flang:runtime flang Flang issues not falling into any other category flang:fir-hlfir labels Feb 16, 2024
@llvmbot
Copy link
Member

llvmbot commented Feb 16, 2024

@llvm/pr-subscribers-clang
@llvm/pr-subscribers-flang-driver

@llvm/pr-subscribers-flang-runtime

Author: Slava Zakharin (vzakhari)

Changes

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.


Patch is 27.69 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/81971.diff

13 Files Affected:

  • (modified) clang/include/clang/Driver/Driver.h (+10)
  • (modified) clang/lib/Driver/ToolChains/CommonArgs.cpp (+8)
  • (modified) flang/CMakeLists.txt (+17)
  • (modified) flang/include/flang/Optimizer/Builder/IntrinsicCall.h (+10-9)
  • (modified) flang/lib/Optimizer/Builder/IntrinsicCall.cpp (+71-30)
  • (modified) flang/runtime/CMakeLists.txt (+20)
  • (added) flang/runtime/Float128Math/CMakeLists.txt (+56)
  • (added) flang/runtime/Float128Math/cabs.cpp (+24)
  • (added) flang/runtime/Float128Math/math-entries.h (+77)
  • (added) flang/runtime/Float128Math/sin.cpp (+22)
  • (added) flang/runtime/Float128Math/sqrt.cpp (+22)
  • (modified) flang/test/Lower/Intrinsics/missing-math-runtime.f90 (+5-1)
  • (modified) flang/tools/flang-driver/driver.cpp (+3)
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_TO...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Feb 16, 2024

@llvm/pr-subscribers-flang-fir-hlfir

Author: Slava Zakharin (vzakhari)

Changes

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.


Patch is 27.69 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/81971.diff

13 Files Affected:

  • (modified) clang/include/clang/Driver/Driver.h (+10)
  • (modified) clang/lib/Driver/ToolChains/CommonArgs.cpp (+8)
  • (modified) flang/CMakeLists.txt (+17)
  • (modified) flang/include/flang/Optimizer/Builder/IntrinsicCall.h (+10-9)
  • (modified) flang/lib/Optimizer/Builder/IntrinsicCall.cpp (+71-30)
  • (modified) flang/runtime/CMakeLists.txt (+20)
  • (added) flang/runtime/Float128Math/CMakeLists.txt (+56)
  • (added) flang/runtime/Float128Math/cabs.cpp (+24)
  • (added) flang/runtime/Float128Math/math-entries.h (+77)
  • (added) flang/runtime/Float128Math/sin.cpp (+22)
  • (added) flang/runtime/Float128Math/sqrt.cpp (+22)
  • (modified) flang/test/Lower/Intrinsics/missing-math-runtime.f90 (+5-1)
  • (modified) flang/tools/flang-driver/driver.cpp (+3)
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_TO...
[truncated]

Copy link
Contributor

@jeanPerier jeanPerier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for plumbing-in the quadmath runtime and offering ways to customize the builds here. Looks great me!

Copy link
Contributor

@kiranchandramohan kiranchandramohan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Slava.

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.
@vzakhari
Copy link
Contributor Author

I have been doing some renaming and restructuring before uploading this for review, and I did not test the final version with libquadmath. The latest commit makes the -DFLANG_RUNTIME_F128_MATH_LIB=libquadmath compiler fully functional (the end-to-end test is working).

Thank you all for the reviews! I am going to merge this as soon as CI completes.

I decided to keep the compiler is built without support for message as is, because emitting NYI might be confusing to the compiler developers. For users it will look like a compiler error, so I guess it does not matter for them if it has the NYI prefix or not.

@vzakhari vzakhari merged commit a468d02 into llvm:main Feb 20, 2024
vzakhari added a commit to vzakhari/llvm-project that referenced this pull request Feb 23, 2024
Follow-up for llvm#81971 to fix the disabled LIT test and add
LIT tests for lowering of the added math intrinsics.
vzakhari added a commit to vzakhari/llvm-project that referenced this pull request Feb 23, 2024
Follow-up for llvm#81971 to fix the disabled LIT test and add
LIT tests for lowering of the added math intrinsics.
vzakhari added a commit to vzakhari/llvm-project that referenced this pull request Feb 24, 2024
Follow-up for llvm#81971 to fix the disabled LIT test and add
LIT tests for lowering of the added math intrinsics.
vzakhari added a commit to vzakhari/llvm-project that referenced this pull request Feb 24, 2024
Follow-up for llvm#81971 to fix the disabled LIT test and add
LIT tests for lowering of the added math intrinsics.
vzakhari added a commit that referenced this pull request Feb 26, 2024
…82832)

Follow-up for #81971 to fix the disabled LIT test and add
LIT tests for lowering of the added math intrinsics.
@dpalermo
Copy link
Contributor

I am seeing build errors on systems running cmake versions 3.22.1 and 3.24.3:

FAILED: tools/flang/unittests/Evaluate/CMakeFiles/uint128.test.dir/uint128.cpp.o
/usr/bin/c++ -D-DFLANG_RUNTIME_F128_MATH_LIB=\"libquadmath\" -DFLANG_INCLUDE_TESTS=1 -DFLANG_LITTLE_ENDIAN=1 -D_DEBUG -D_GLIBCXX_ASSERTIONS -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS ...
...
<command-line>: error: macro names must be identifiers

Systems running 3.28.1 build without problem.

The problem can be resolved by removing the stray '-D' in the call to add_compile_definitions:

diff --git a/flang/CMakeLists.txt b/flang/CMakeLists.txt
index c8e75024823f..af34366b3652 100644
--- a/flang/CMakeLists.txt
+++ b/flang/CMakeLists.txt
@@ -336,7 +336,7 @@ endif()

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

dpalermo added a commit to dpalermo/llvm-project that referenced this pull request May 22, 2024
Remove an extra '-D' in the call to add_compile_definitions.
The extra -D causes -D-DFLANG_RUNTIME_F128_MATH_LIB=libquadmath to
appear in compile options which results in a compilation failure.
This occurs only on systems using cmake versions 3.22.1 or 3.24.3.
Using cmake 3.28.1 seems to automatically remove the duplicate -D.

Fixes llvm#81971
dpalermo added a commit that referenced this pull request May 22, 2024
Remove an extra '-D' in the call to add_compile_definitions. The extra
-D causes -D-DFLANG_RUNTIME_F128_MATH_LIB=libquadmath to appear in
compile options which results in a compilation failure. This occurs only
on systems using cmake versions 3.22.1 or 3.24.3. Using cmake 3.28.1
seems to automatically remove the duplicate -D.

Fixes #81971
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang Clang issues not falling into any other category flang:driver flang:fir-hlfir flang:runtime flang Flang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants