[flang-commits] [flang] [llvm] [flang] Support REAL(16) through glibc's *f128 entry points (PR #219698)

Dmitry Mikushin via flang-commits flang-commits at lists.llvm.org
Sat Aug 29 11:57:18 PDT 2026


https://github.com/dmikushin updated https://github.com/llvm/llvm-project/pull/219698

>From 4f95f089c352c0ac05994286fccedb858af2e6c3 Mon Sep 17 00:00:00 2001
From: Dmitry Mikushin <dmitry at kernelgen.org>
Date: Fri, 28 Aug 2026 15:49:17 +0200
Subject: [PATCH 1/7] [flang-rt] Bring up the glibc *f128 route for REAL(16)

glibc 2.26 and later export the binary128 math functions from libm as *f128.
flang-rt already detected them - check_library_exists(m sinf128 ...) setting
HAS_LIBMF128 - but the target_sources line was commented out with "Enable this,
when math-entries.h and complex-math.h is ready", and both headers ended in
#error "Float128Math build with glibc>=2.26 is unsupported yet". So the only
way to get REAL(16) was libquadmath.

This turns that route on. It matters beyond convenience: libm is already linked
into every binary, so quad precision stops requiring a third-party library at
all.

The alias tables are derived rather than typed. Intersecting the names
flang-rt declares fallbacks for with what libm actually exports gives 42 scalar
and 17 complex entry points, with nothing left uncovered - including the six
Bessel functions, which were assumed missing.

Three names are not libm calls in any width: Isinf, Isnan and Qnan. They still
need aliases. "Not a libm call" is not "needs no implementation" - the fallback
for an unaliased name is a runtime Crash, and leaving these unaliased made
SPACING, RRSPACING, SET_EXPONENT, MOD and MODULO abort at run time while the
build and the link stayed green. They now go to __builtin_isinf/__builtin_isnan
and numeric_limits.

__STDC_WANT_IEC_60559_TYPES_EXT__ is set on the target, not in a header. A
header cannot guarantee it is seen before <cmath> or <complex.h> in every
translation unit, and when it is not the prototypes simply do not appear.
Measured on glibc 2.43: <cmath> exposes sqrtf128 even without the macro, while
<complex.h> does not expose cabsf128 - so a header-based definition would look
correct on the scalar half and fail on the complex half of the same build.

Each branch carries a compile-time canary taking the address of sqrtf128 and
cabsf128. That is not decoration: on the first build the macro reached only the
auto-detected path and not the explicit one, and the canary turned what would
have been a silent absence of prototypes into an error on the line that owns
the assumption.

The complex half uses CFloat128ComplexType from flang/Common/float128.h, the
same type the libquadmath branch passes to the *q functions. The C23 spelling
_Complex _Float128 is rejected by clang, and the GNU spelling
__complex__ _Float128 is accepted only with a warning that it is being read as
_Complex double - which would halve the precision in silence.

Verified without libquadmath present: selected_real_kind(33) = 16, the archive
has 55 _FortranA*F128 definitions and zero unresolved *q symbols, all 56 cases
of a differential matrix against gfortran agree, and ERFC_SCALED at REAL(16) is
byte-identical to the libquadmath route on every sample point - the same
trait-based implementation resolving through expq/erfcq/fmaq on one route and
expf128/erfcf128/fmaf128 on the other. Complex results are within 0.54 epsilons
of mpmath.
---
 flang-rt/lib/quadmath/CMakeLists.txt | 31 +++++++++-
 flang-rt/lib/quadmath/complex-math.h | 45 ++++++++++++--
 flang-rt/lib/quadmath/math-entries.h | 87 ++++++++++++++++++++++++++--
 3 files changed, 153 insertions(+), 10 deletions(-)

diff --git a/flang-rt/lib/quadmath/CMakeLists.txt b/flang-rt/lib/quadmath/CMakeLists.txt
index 353c72def24de..4c050eb66f810 100644
--- a/flang-rt/lib/quadmath/CMakeLists.txt
+++ b/flang-rt/lib/quadmath/CMakeLists.txt
@@ -86,6 +86,26 @@ if (FLANG_RUNTIME_F128_MATH_LIB)
         "to be available: ${FLANG_RUNTIME_F128_MATH_LIB}"
         )
     endif()
+  elseif (${FLANG_RUNTIME_F128_MATH_LIB} STREQUAL "libm")
+    # glibc 2.26 and later export the *f128 entry points from libm itself, so
+    # this route pulls in no third-party library at all.
+    check_library_exists(m sinf128 "" FOUND_LIBMF128_EXPLICIT)
+    if (FOUND_LIBMF128_EXPLICIT)
+      add_compile_definitions(HAS_LIBMF128)
+      # glibc gates the *f128 prototypes behind this, and it has to be on the
+      # command line rather than in a header: no header can guarantee it is
+      # seen before <cmath> or <complex.h> in every translation unit, and when
+      # it is not, the prototypes silently do not appear. Measured on glibc
+      # 2.43: <cmath> exposes sqrtf128 without it but <complex.h> does not
+      # expose cabsf128, so a header-based definition looks correct on the
+      # scalar half and fails on the complex half of the same build.
+      add_compile_definitions(__STDC_WANT_IEC_60559_TYPES_EXT__=1)
+    else()
+      message(FATAL_ERROR
+        "FLANG_RUNTIME_F128_MATH_LIB=libm requires a libm that exports the "
+        "*f128 entry points (glibc 2.26 or later)"
+        )
+    endif()
   else()
     message(FATAL_ERROR
       "Unsupported third-party library for Fortran F128 math runtime: "
@@ -125,11 +145,18 @@ else()
   if (FOUND_LIBMF128)
     target_compile_definitions(FortranFloat128MathILib INTERFACE
       HAS_LIBMF128
+      # glibc gates the *f128 prototypes behind this. It must be on the command
+      # line, not in a header: a header cannot guarantee it is seen before
+      # <cmath> or <complex.h> in every translation unit, and when it is not,
+      # the prototypes silently do not appear. Measured on glibc 2.43: <cmath>
+      # exposes sqrtf128 without it, <complex.h> does not expose cabsf128 - so
+      # a header-based definition would look correct on the scalar half and
+      # fail on the complex half of the same build.
+      __STDC_WANT_IEC_60559_TYPES_EXT__=1
       )
     target_include_directories(FortranFloat128MathILib INTERFACE
       "${FLANG_RT_SOURCE_DIR}/lib/flang_rt"
       )
-    # Enable this, when math-entries.h and complex-math.h is ready.
-    # target_sources(FortranFloat128MathILib INTERFACE ${sources})
+    target_sources(FortranFloat128MathILib INTERFACE ${sources})
   endif()
 endif()
diff --git a/flang-rt/lib/quadmath/complex-math.h b/flang-rt/lib/quadmath/complex-math.h
index b76bf03732938..878e824eaf87c 100644
--- a/flang-rt/lib/quadmath/complex-math.h
+++ b/flang-rt/lib/quadmath/complex-math.h
@@ -53,10 +53,47 @@
 #define CTan(x) ctanl(x)
 #define CTanh(x) ctanhl(x)
 #elif HAS_LIBMF128
-/* We can use __float128 versions of libm functions.
- * __STDC_WANT_IEC_60559_TYPES_EXT__ needs to be defined
- * before including math.h to enable the *f128 prototypes. */
-#error "Float128Math build with glibc>=2.26 is unsupported yet"
+/* glibc 2.26 and later export the complex *f128 entry points from libm, so
+ * this route needs no third-party library.
+ *
+ * __STDC_WANT_IEC_60559_TYPES_EXT__ must be defined before <complex.h> and is
+ * set on the target in this directory's CMakeLists.txt, not here: a header
+ * cannot guarantee it is included first, and the failure is silent. Unlike the
+ * scalar half, <complex.h> on glibc 2.43 really does hide cabsf128 without the
+ * macro, so this is the half that would break.
+ *
+ * The argument type is CFloat128ComplexType from flang/Common/float128.h,
+ * which is _Complex float __attribute__((mode(TC))). That is what the
+ * libquadmath branch already passes to the *q functions, and it is ABI
+ * compatible with the _Float128 _Complex these take: verified with the build
+ * clang under -Wall, sizeof 32, cabs(2+i) correct to nine places. The C23
+ * spelling _Complex _Float128 is not accepted by this clang, and the GNU
+ * spelling __complex__ _Float128 is accepted only with a warning that it is
+ * being read as _Complex double - which would silently halve the precision. */
+#include <complex.h>
+
+/* Compile-time canary: see the note in math-entries.h. If the prototypes are
+ * not visible, this fails here rather than at link time. */
+__attribute__((unused)) static CFloat128Type (*const c_f128_prototype_canary)(
+    CFloat128ComplexType) = &cabsf128;
+
+#define CAbs(x) cabsf128(x)
+#define CAcos(x) cacosf128(x)
+#define CAcosh(x) cacoshf128(x)
+#define CAsin(x) casinf128(x)
+#define CAsinh(x) casinhf128(x)
+#define CAtan(x) catanf128(x)
+#define CAtanh(x) catanhf128(x)
+#define CCos(x) ccosf128(x)
+#define CCosh(x) ccoshf128(x)
+#define CExp(x) cexpf128(x)
+#define CLog(x) clogf128(x)
+#define CSin(x) csinf128(x)
+#define CSinh(x) csinhf128(x)
+#define CSqrt(x) csqrtf128(x)
+#define CTan(x) ctanf128(x)
+#define CTanh(x) ctanhf128(x)
+#define CPow(x, p) cpowf128(x, p)
 #endif
 
 #endif /* FLANG_RT_QUADMATH_COMPLEX_MATH_H_ */
diff --git a/flang-rt/lib/quadmath/math-entries.h b/flang-rt/lib/quadmath/math-entries.h
index 91ad80c8197f4..28faa202d4dcf 100644
--- a/flang-rt/lib/quadmath/math-entries.h
+++ b/flang-rt/lib/quadmath/math-entries.h
@@ -224,10 +224,89 @@ DEFINE_SIMPLE_ALIAS(Yn, ynl)
 #define F128_RT_QNAN \
   (std::numeric_limits<CppTypeFor<TypeCategory::Real, 16>>::quiet_NaN())
 #elif HAS_LIBMF128
-// We can use __float128 versions of libm functions.
-// __STDC_WANT_IEC_60559_TYPES_EXT__ needs to be defined
-// before including cmath to enable the *f128 prototypes.
-#error "Float128Math build with glibc>=2.26 is unsupported yet"
+// glibc 2.26 and later export *f128 entry points from libm. They are the same
+// IEEE-754 binary128 functions libquadmath provides as *q, so this route needs
+// no third-party library: libm is already linked into everything.
+//
+// __STDC_WANT_IEC_60559_TYPES_EXT__ must be defined before any header pulls in
+// <cmath>, and it is set on the target in this directory's CMakeLists.txt
+// rather than here. In a header it would depend on this file being included
+// before <cmath> in every translation unit, which no header can guarantee -
+// and the failure is silent, because the prototypes simply do not appear.
+//
+// Measured on glibc 2.43 with clang: <cmath> exposes sqrtf128 even without the
+// macro, while <complex.h> does not expose cabsf128 without it. Relying on that
+// asymmetry would give a scalar half that appears to work beside a complex half
+// that does not compile, in the same build.
+#include <cmath>
+#include <limits>
+
+// Compile-time canary. If the *f128 prototypes are not visible here - a macro
+// lost, an include reordered, a future libc spelling them differently - the
+// aliases below would resolve to nothing and the failure would surface at link
+// time or in the field. This makes it surface at the line that owns the
+// assumption.
+[[maybe_unused]] static CppTypeFor<TypeCategory::Real, 16> (
+    *const f128_prototype_canary)(CppTypeFor<TypeCategory::Real, 16>) =
+    &sqrtf128;
+
+DEFINE_SIMPLE_ALIAS(Abs, fabsf128)
+DEFINE_SIMPLE_ALIAS(Acos, acosf128)
+DEFINE_SIMPLE_ALIAS(Acosh, acoshf128)
+DEFINE_SIMPLE_ALIAS(Asin, asinf128)
+DEFINE_SIMPLE_ALIAS(Asinh, asinhf128)
+DEFINE_SIMPLE_ALIAS(Atan, atanf128)
+DEFINE_SIMPLE_ALIAS(Atan2, atan2f128)
+DEFINE_SIMPLE_ALIAS(Atanh, atanhf128)
+DEFINE_SIMPLE_ALIAS(Ceil, ceilf128)
+DEFINE_SIMPLE_ALIAS(Cos, cosf128)
+DEFINE_SIMPLE_ALIAS(Cosh, coshf128)
+DEFINE_SIMPLE_ALIAS(Erf, erff128)
+DEFINE_SIMPLE_ALIAS(Erfc, erfcf128)
+DEFINE_SIMPLE_ALIAS(Exp, expf128)
+DEFINE_SIMPLE_ALIAS(Floor, floorf128)
+DEFINE_SIMPLE_ALIAS(Fma, fmaf128)
+DEFINE_SIMPLE_ALIAS(Frexp, frexpf128)
+DEFINE_SIMPLE_ALIAS(Hypot, hypotf128)
+DEFINE_SIMPLE_ALIAS(Ilogb, ilogbf128)
+DEFINE_SIMPLE_ALIAS(J0, j0f128)
+DEFINE_SIMPLE_ALIAS(J1, j1f128)
+DEFINE_SIMPLE_ALIAS(Jn, jnf128)
+DEFINE_SIMPLE_ALIAS(Ldexp, ldexpf128)
+DEFINE_SIMPLE_ALIAS(Lgamma, lgammaf128)
+DEFINE_SIMPLE_ALIAS(Log, logf128)
+DEFINE_SIMPLE_ALIAS(Log10, log10f128)
+DEFINE_SIMPLE_ALIAS(Lround, lroundf128)
+DEFINE_SIMPLE_ALIAS(Nearbyint, nearbyintf128)
+DEFINE_SIMPLE_ALIAS(Nextafter, nextafterf128)
+DEFINE_SIMPLE_ALIAS(Pow, powf128)
+DEFINE_SIMPLE_ALIAS(Remainder, remainderf128)
+DEFINE_SIMPLE_ALIAS(Round, roundf128)
+DEFINE_SIMPLE_ALIAS(Sin, sinf128)
+DEFINE_SIMPLE_ALIAS(Sinh, sinhf128)
+DEFINE_SIMPLE_ALIAS(Sqrt, sqrtf128)
+DEFINE_SIMPLE_ALIAS(Tan, tanf128)
+DEFINE_SIMPLE_ALIAS(Tanh, tanhf128)
+DEFINE_SIMPLE_ALIAS(Tgamma, tgammaf128)
+DEFINE_SIMPLE_ALIAS(Trunc, truncf128)
+DEFINE_SIMPLE_ALIAS(Y0, y0f128)
+DEFINE_SIMPLE_ALIAS(Y1, y1f128)
+DEFINE_SIMPLE_ALIAS(Yn, ynf128)
+
+// Isinf, Isnan and the quiet NaN are not libm entry points in any width, but
+// they still need implementations: the fallback for an unaliased name is a
+// runtime Crash, not a default. The builtins are type-generic and correct for
+// binary128; the libquadmath branch reaches the same place through isinfq and
+// isnanq.
+DEFINE_SIMPLE_ALIAS(Isinf, __builtin_isinf)
+DEFINE_SIMPLE_ALIAS(Isnan, __builtin_isnan)
+
+// Not libm calls: isinf and isnan are type-generic macros, and the quiet NaN
+// comes from numeric_limits rather than from a function.
+#define F128_RT_INFINITY \
+  (std::numeric_limits<CppTypeFor<TypeCategory::Real, 16>>::infinity())
+#define F128_RT_QNAN \
+  (std::numeric_limits<CppTypeFor<TypeCategory::Real, 16>>::quiet_NaN())
 #endif
 
 } // namespace Fortran::runtime

>From efba48e54fd1fb12a4ca0ee3c2f4273e69cc5cae Mon Sep 17 00:00:00 2001
From: Dmitry Mikushin <dmitry at kernelgen.org>
Date: Fri, 28 Aug 2026 15:50:43 +0200
Subject: [PATCH 2/7] [flang] Accept libm for REAL(16), and stop documenting
 what the code refused

FLANG_RUNTIME_F128_MATH_LIB's cache description offered "libm for targets where
REAL(16) is mapped to long double", while the code rejected every value except
libquadmath with a FATAL_ERROR. Now that libm is a real option the description
can be true: it lists the two accepted values and says what an empty setting
means, which is that REAL(16) is disabled in the compiler as well as the
runtime.

TargetSetup.h needed no change to enable the type - it already turns on
f128Support for any non-empty value - but its comments were wrong in two ways:
they attributed the setting to libquadmath specifically, and they pointed at
flang/runtime/Float128Math/math-entries.h, a path that has since become
flang-rt/lib/quadmath/.

The TODO about deriving this from TargetInfo::getLongDoubleFormat is left
alone. It is a real defect - the decision is made from the host the compiler
was built on rather than the target it compiles for - but it is separate work
and cannot be demonstrated on a build configured for the native target only.
---
 flang/cmake/modules/FlangCommon.cmake   | 9 +++++----
 flang/include/flang/Tools/TargetSetup.h | 7 ++++---
 2 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/flang/cmake/modules/FlangCommon.cmake b/flang/cmake/modules/FlangCommon.cmake
index a1e80f737e543..39b9c7a8e7b4d 100644
--- a/flang/cmake/modules/FlangCommon.cmake
+++ b/flang/cmake/modules/FlangCommon.cmake
@@ -19,10 +19,11 @@ include(CMakePushCheckState)
 # 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."
+  "Library implementing IEEE-754 binary128 math in the Fortran runtime. \
+  Accepted values: libquadmath, for GCC's __float128 support; or libm, on a \
+  glibc 2.26 or later that exports the *f128 entry points, which needs no \
+  third-party library. Leave empty to build without REAL(16) support - the \
+  type is then disabled in the compiler as well as the runtime."
   )
 if (FLANG_RUNTIME_F128_MATH_LIB)
   add_compile_definitions(FLANG_RUNTIME_F128_MATH_LIB="${FLANG_RUNTIME_F128_MATH_LIB}")
diff --git a/flang/include/flang/Tools/TargetSetup.h b/flang/include/flang/Tools/TargetSetup.h
index 47f886141b002..d462de6443567 100644
--- a/flang/include/flang/Tools/TargetSetup.h
+++ b/flang/include/flang/Tools/TargetSetup.h
@@ -57,12 +57,13 @@ namespace Fortran::tools {
     break;
   }
 
-  // Check for kind=16 support. See flang/runtime/Float128Math/math-entries.h.
+  // Check for kind=16 support. See flang-rt/lib/quadmath/math-entries.h.
   // TODO: Take this from TargetInfo::getLongDoubleFormat for cross compilation.
 #ifdef FLANG_RUNTIME_F128_MATH_LIB
-  constexpr bool f128Support = true; // use libquadmath wrappers
+  // libquadmath, or libm on a glibc that exports the *f128 entry points.
+  constexpr bool f128Support = true;
 #elif HAS_LDBL128
-  constexpr bool f128Support = true; // use libm wrappers
+  constexpr bool f128Support = true; // long double is itself binary128
 #else
   constexpr bool f128Support = false;
 #endif

>From bff2ec3f245ab58ce6bf683e6db00e2dd8248086 Mon Sep 17 00:00:00 2001
From: Dmitry Mikushin <dmitry at kernelgen.org>
Date: Fri, 28 Aug 2026 16:10:15 +0200
Subject: [PATCH 3/7] [flang][test] Add the libm check prefixes to the linker
 test

linker-flags.f90 builds its check prefix from the configured F128 library:
--check-prefixes=CHECK,UNIX,UNIX-F128%f128-lib. That machinery is not new, and
neither is the driver's handling of the libm route - it already emits
"-lflang_rt.quadmath" "--as-needed" "-lm" "--no-as-needed". What was missing
were the check strings for the value, because until now the build refused to
accept it: the test enumerated only NONE and LIBQUADMATH, so configuring with
FLANG_RUNTIME_F128_MATH_LIB=libm produced

  error: no check strings found with prefix 'UNIX-F128LIBM:'

The six new lines are derived from the observed link line rather than written
from expectation: the libm route links the same runtime archive against -lm
where the libquadmath route links -lquadmath, and nothing else about the
command differs.

This is worth noting beyond the fix. The driver half of the libm route was
already in the tree - the substitution, the RUN lines and the link logic - while
the runtime half was behind #error "unsupported yet". The route was not
unstarted; it was broken off half way, with the front end waiting for a runtime
that refused to build.

Verified on both configurations: the test passes under libquadmath and under
libm, and on the libm build a deliberate run with the LIBQUADMATH prefix fails
for the right reason - the driver no longer emits -lquadmath.
---
 flang/test/Driver/linker-flags.f90 | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/flang/test/Driver/linker-flags.f90 b/flang/test/Driver/linker-flags.f90
index c4e0012b5c224..668c554085ace 100644
--- a/flang/test/Driver/linker-flags.f90
+++ b/flang/test/Driver/linker-flags.f90
@@ -44,7 +44,9 @@
 ! UNIX-F128NONE-NOT: lang_rt.quadmath
 ! SOLARIS-F128NONE-NOT: flang_rt.quadmath
 ! UNIX-F128LIBQUADMATH-SAME: "-lflang_rt.quadmath" "--as-needed" "-lquadmath" "--no-as-needed"
+! UNIX-F128LIBM-SAME: "-lflang_rt.quadmath" "--as-needed" "-lm" "--no-as-needed"
 ! SOLARIS-F128LIBQUADMATH-SAME: "-lflang_rt.quadmath" "-z" "ignore" "-lquadmath" "-z" "record"
+! SOLARIS-F128LIBM-SAME: "-lflang_rt.quadmath" "-z" "ignore" "-lm" "-z" "record"
 ! UNIX-SAME: "-lflang_rt.runtime" "-lm"
 ! COMPILER-RT: "{{.*}}{{\\|/}}libclang_rt.builtins.a"
 ! UNIX-STATIC-FLANGRT:   "{{.*}}{{\\|/}}libflang_rt.runtime.a"
@@ -53,6 +55,7 @@
 ! BSD-SAME: "[[object_file]]"
 ! BSD-F128NONE-NOT: flang_rt.quadmath
 ! BSD-F128LIBQUADMATH-SAME: "-lflang_rt.quadmath" "--as-needed" "-lquadmath" "--no-as-needed"
+! BSD-F128LIBM-SAME: "-lflang_rt.quadmath" "--as-needed" "-lm" "--no-as-needed"
 ! BSD-SAME: -lflang_rt.runtime
 ! BSD-SAME: -lexecinfo
 ! BSD-STATIC-FLANGRT: "{{.*}}{{\\|/}}libflang_rt.runtime.a"
@@ -61,6 +64,7 @@
 ! DARWIN-SAME: "[[object_file]]"
 ! DARWIN-F128NONE-NOT: libflang_rt.quadmath
 ! DARWIN-F128LIBQUADMATH-SAME: "-lflang_rt.quadmath" "--as-needed" "-lquadmath" "--no-as-needed"
+! DARWIN-F128LIBM-SAME: "-lflang_rt.quadmath" "--as-needed" "-lm" "--no-as-needed"
 ! DARWIN-SAME: -lflang_rt.runtime
 ! DARWIN-STATIC-FLANGRT: "{{.*}}{{\\|/}}libclang_rt.runtime_osx.a"
 
@@ -68,6 +72,7 @@
 ! HAIKU-SAME: "[[object_file]]"
 ! HAIKU-F128NONE-NOT: libflang_rt.quadmath
 ! HAIKU-F128LIBQUADMATH-SAME: "-lflang_rt.quadmath" "--as-needed" "-lquadmath" "--no-as-needed"
+! HAIKU-F128LIBM-SAME: "-lflang_rt.quadmath" "--as-needed" "-lm" "--no-as-needed"
 ! HAIKU-SAME: "-lflang_rt.runtime"
 ! HAIKU-STATIC-FLANGRT: "{{.*}}{{\\|/}}libflang_rt.runtime.a"
 
@@ -75,6 +80,7 @@
 ! MINGW-SAME: "[[object_file]]"
 ! MINGW-F128NONE-NOT: libflang_rt.quadmath
 ! MINGW-F128LIBQUADMATH-SAME: "-lflang_rt.quadmath" "--as-needed" "-lquadmath" "--no-as-needed"
+! MINGW-F128LIBM-SAME: "-lflang_rt.quadmath" "--as-needed" "-lm" "--no-as-needed"
 ! MINGW-SAME: -lflang_rt.runtime
 ! MINGW-STATIC-FLANGRT: "{{.*}}{{\\|/}}libflang_rt.runtime.a"
 

>From f268cc6eb3b13892c4e54b17192f5b3f20109742 Mon Sep 17 00:00:00 2001
From: Dmitry Mikushin <dmitry at kernelgen.org>
Date: Fri, 28 Aug 2026 18:29:58 +0200
Subject: [PATCH 4/7] [flang][Evaluate] Fold REAL(16) constants through libm on
 the libm route

The compile-time folder for REAL(16) was built only on libquadmath's *q
functions. On the libm route that left the two halves of the language
disagreeing about who computes: `parameter :: v = sin(x)` folded through sinq
while `y = sin(x)` ran through sinf128. They agree on every point measured, but
nothing makes them agree in the last place, and a named constant that differs
from its own runtime value is a defect waiting for an unlucky argument.

This adds the libm tables beside the libquadmath ones and dispatches on the
configured route. Both are derived from the existing tables rather than typed:
27 scalar entries and 16 complex ones, every one of which has an f128
counterpart in glibc, so nothing stops folding.

The complex prototypes are declared by hand. glibc exposes the complex *f128
functions to C but hides them from C++, where Evaluate lives, while the symbols
are in libm and the ABI is the one mode(TC) produces. The signatures are fixed
by TS 18661-3, and no separate canary is needed because the table takes each
function's address - a change in visibility or signature is a build error at
that line, not a silent drift back to libquadmath.

Two traps were caught by guards rather than by reasoning, and both are worth
recording.

The first: `using HostComplex128 = _Complex float __attribute__((mode(TC)))`
silently drops the attribute and yields an 8-byte _Complex float. The
static_assert on sizeof caught it on the first build. The C-style typedef, which
is how flang/Common/float128.h already spells this type, applies the attribute.
That is the same class of failure as clang reading __complex__ _Float128 as
_Complex double - a plausible type of the wrong width.

The second: the tables compiled and were never reached, because
GetHostRuntimeMapVersion still guarded its dispatch on HAS_QUADMATHLIB alone.
The symptom looked like a macro that had not reached the build graph, and
measuring the graph - 30 occurrences of the new macro in build.ninja - is what
redirected the search to the dispatcher.

Measured after: on the libm route the compiler links no libquadmath at all, and
six folded constants equal their runtime values bit for bit. On the libquadmath
route nothing changes, checked the same way rather than by inspection.
---
 flang/lib/Evaluate/host.h                 |  25 +++++-
 flang/lib/Evaluate/intrinsics-library.cpp | 101 +++++++++++++++++++++-
 2 files changed, 122 insertions(+), 4 deletions(-)

diff --git a/flang/lib/Evaluate/host.h b/flang/lib/Evaluate/host.h
index 7f6bf76bb5c53..62d2dc22ea16c 100644
--- a/flang/lib/Evaluate/host.h
+++ b/flang/lib/Evaluate/host.h
@@ -20,6 +20,8 @@
 #if HAS_QUADMATHLIB
 #include "quadmath_wrapper.h"
 #include "flang/Common/float128.h"
+#elif HAS_LIBMF128_HOST
+#include "flang/Common/float128.h"
 #endif
 #include "flang/Evaluate/type.h"
 #include <cfenv>
@@ -159,9 +161,13 @@ struct HostTypeHelper<
       long double, UnsupportedType>;
 };
 
-#if HAS_QUADMATHLIB
+#if HAS_QUADMATHLIB || HAS_LIBMF128_HOST
 template <> struct HostTypeHelper<Type<TypeCategory::Real, 16>> {
-  // IEEE 754 128bits
+  // IEEE 754 128bits.
+  //
+  // Both routes use the same host type; only the library the folding table
+  // calls differs - libquadmath's *q or glibc's *f128. Keeping the type common
+  // is what lets the two share everything above this line.
   using Type = __float128;
 };
 #else
@@ -185,6 +191,21 @@ template <> struct HostTypeHelper<Type<TypeCategory::Complex, 16>> {
   using RealT = Fortran::evaluate::Type<TypeCategory::Real, 16>;
   using Type = __complex128;
 };
+#elif HAS_LIBMF128_HOST
+// __complex128 is libquadmath's typedef and is not available here. The mode
+// attribute is how flang already spells this type in flang/Common/float128.h
+// for the runtime, and glibc's complex *f128 functions take the same ABI.
+// The C-style typedef, not `using`: with an alias declaration the mode
+// attribute is silently dropped and the type degrades to _Complex float. The
+// static_assert below caught exactly that during development.
+typedef _Complex float __attribute__((mode(TC))) HostComplex128;
+static_assert(sizeof(HostComplex128) == 32,
+    "mode(TC) must be a pair of binary128 values; if it is not, the complex "
+    "folding table below is calling libm with the wrong ABI");
+template <> struct HostTypeHelper<Type<TypeCategory::Complex, 16>> {
+  using RealT = Fortran::evaluate::Type<TypeCategory::Real, 16>;
+  using Type = HostComplex128;
+};
 #endif
 
 template <int KIND> struct HostTypeHelper<Type<TypeCategory::Logical, KIND>> {
diff --git a/flang/lib/Evaluate/intrinsics-library.cpp b/flang/lib/Evaluate/intrinsics-library.cpp
index f2c1a7bfaf50b..2572d361dbe91 100644
--- a/flang/lib/Evaluate/intrinsics-library.cpp
+++ b/flang/lib/Evaluate/intrinsics-library.cpp
@@ -576,7 +576,95 @@ template <> struct HostRuntimeLibrary<__complex128, LibraryVersion::Libm> {
   static constexpr HostRuntimeMap map{table};
   static_assert(map.Verify(), "map must be sorted");
 };
-#endif // HAS_QUADMATHLIB
+
+#elif HAS_LIBMF128_HOST
+template <> struct HostRuntimeLibrary<__float128, LibraryVersion::Libm> {
+  using F = FuncPointer<__float128, __float128>;
+  using F2 = FuncPointer<__float128, __float128, __float128>;
+  using FN = FuncPointer<__float128, int, __float128>;
+  static constexpr HostRuntimeFunction table[]{
+      FolderFactory<F, F{::acosf128}>::Create("acos"),
+      FolderFactory<F, F{::acoshf128}>::Create("acosh"),
+      FolderFactory<F, F{::asinf128}>::Create("asin"),
+      FolderFactory<F, F{::asinhf128}>::Create("asinh"),
+      FolderFactory<F, F{::atanf128}>::Create("atan"),
+      FolderFactory<F2, F2{::atan2f128}>::Create("atan2"),
+      FolderFactory<F, F{::atanhf128}>::Create("atanh"),
+      FolderFactory<F, F{::j0f128}>::Create("bessel_j0"),
+      FolderFactory<F, F{::j1f128}>::Create("bessel_j1"),
+      FolderFactory<FN, FN{::jnf128}>::Create("bessel_jn"),
+      FolderFactory<F, F{::y0f128}>::Create("bessel_y0"),
+      FolderFactory<F, F{::y1f128}>::Create("bessel_y1"),
+      FolderFactory<FN, FN{::ynf128}>::Create("bessel_yn"),
+      FolderFactory<F, F{cosf128}>::Create("cos"),
+      FolderFactory<F, F{coshf128}>::Create("cosh"),
+      FolderFactory<F, F{::erff128}>::Create("erf"),
+      FolderFactory<F, F{::erfcf128}>::Create("erfc"),
+      FolderFactory<F, F{::expf128}>::Create("exp"),
+      FolderFactory<F, F{::tgammaf128}>::Create("gamma"),
+      FolderFactory<F, F{::logf128}>::Create("log"),
+      FolderFactory<F, F{::log10f128}>::Create("log10"),
+      FolderFactory<F, F{::lgammaf128}>::Create("log_gamma"),
+      FolderFactory<F2, F2{::powf128}>::Create("pow"),
+      FolderFactory<F, F{::sinf128}>::Create("sin"),
+      FolderFactory<F, F{::sinhf128}>::Create("sinh"),
+      FolderFactory<F, F{::tanf128}>::Create("tan"),
+      FolderFactory<F, F{::tanhf128}>::Create("tanh"),
+  };
+  static constexpr HostRuntimeMap map{table};
+  static_assert(map.Verify(), "map must be sorted");
+};
+
+// glibc declares the complex *f128 entry points in <complex.h> for C only; in
+// C++ they are hidden, though the symbols are in libm and the ABI is the one
+// mode(TC) produces. Declaring them here is deliberate. The signatures are
+// fixed by TS 18661-3 and the glibc ABI, and no separate canary is needed: the
+// table below takes each function's address, so a change in visibility or
+// signature is a build error at that line rather than a silent drift back to
+// libquadmath. Do not delete this block expecting the headers to provide them.
+extern "C" {
+host::HostComplex128 cacosf128(host::HostComplex128);
+host::HostComplex128 cacoshf128(host::HostComplex128);
+host::HostComplex128 casinf128(host::HostComplex128);
+host::HostComplex128 casinhf128(host::HostComplex128);
+host::HostComplex128 catanf128(host::HostComplex128);
+host::HostComplex128 catanhf128(host::HostComplex128);
+host::HostComplex128 ccosf128(host::HostComplex128);
+host::HostComplex128 ccoshf128(host::HostComplex128);
+host::HostComplex128 cexpf128(host::HostComplex128);
+host::HostComplex128 clogf128(host::HostComplex128);
+host::HostComplex128 cpowf128(host::HostComplex128, host::HostComplex128);
+host::HostComplex128 csinf128(host::HostComplex128);
+host::HostComplex128 csinhf128(host::HostComplex128);
+host::HostComplex128 csqrtf128(host::HostComplex128);
+host::HostComplex128 ctanf128(host::HostComplex128);
+host::HostComplex128 ctanhf128(host::HostComplex128);
+}
+template <> struct HostRuntimeLibrary<host::HostComplex128, LibraryVersion::Libm> {
+  using F = FuncPointer<host::HostComplex128, host::HostComplex128>;
+  using F2 = FuncPointer<host::HostComplex128, host::HostComplex128, host::HostComplex128>;
+  static constexpr HostRuntimeFunction table[]{
+      FolderFactory<F, F{cacosf128}>::Create("acos"),
+      FolderFactory<F, F{cacoshf128}>::Create("acosh"),
+      FolderFactory<F, F{casinf128}>::Create("asin"),
+      FolderFactory<F, F{casinhf128}>::Create("asinh"),
+      FolderFactory<F, F{catanf128}>::Create("atan"),
+      FolderFactory<F, F{catanhf128}>::Create("atanh"),
+      FolderFactory<F, F{ccosf128}>::Create("cos"),
+      FolderFactory<F, F{ccoshf128}>::Create("cosh"),
+      FolderFactory<F, F{cexpf128}>::Create("exp"),
+      FolderFactory<F, F{clogf128}>::Create("log"),
+      FolderFactory<F2, F2{cpowf128}>::Create("pow"),
+      FolderFactory<F, F{csinf128}>::Create("sin"),
+      FolderFactory<F, F{csinhf128}>::Create("sinh"),
+      FolderFactory<F, F{csqrtf128}>::Create("sqrt"),
+      FolderFactory<F, F{ctanf128}>::Create("tan"),
+      FolderFactory<F, F{ctanhf128}>::Create("tanh"),
+  };
+  static constexpr HostRuntimeMap map{table};
+  static_assert(map.Verify(), "map must be sorted");
+};
+#endif // HAS_QUADMATHLIB || HAS_LIBMF128_HOST
 
 /// Define pgmath description
 #if LINK_WITH_LIBPGMATH
@@ -686,7 +774,11 @@ static const HostRuntimeMap *GetHostRuntimeMapVersion(DynamicType resultType) {
             GetHostRuntimeMapHelper<long double, version>(resultType)}) {
       return map;
     }
-#if HAS_QUADMATHLIB
+#if HAS_QUADMATHLIB || HAS_LIBMF128_HOST
+    // Both routes present __float128 here; only the library behind the table
+    // differs. Guarding this on HAS_QUADMATHLIB alone compiled the libm tables
+    // and then never reached them, so REAL(16) folding failed on a build that
+    // had everything it needed.
     if (const auto *map{
             GetHostRuntimeMapHelper<__float128, version>(resultType)}) {
       return map;
@@ -712,6 +804,11 @@ static const HostRuntimeMap *GetHostRuntimeMapVersion(DynamicType resultType) {
             GetHostRuntimeMapHelper<__complex128, version>(resultType)}) {
       return map;
     }
+#elif HAS_LIBMF128_HOST
+    if (const auto *map{GetHostRuntimeMapHelper<host::HostComplex128, version>(
+            resultType)}) {
+      return map;
+    }
 #endif
   }
   return nullptr;

>From b0893f73b20eb1354603c184c9f708080d942f91 Mon Sep 17 00:00:00 2001
From: Dmitry Mikushin <dmitry at kernelgen.org>
Date: Fri, 28 Aug 2026 18:30:54 +0200
Subject: [PATCH 5/7] [flang] Tie the host folding library to the configured
 F128 route

HAS_QUADMATHLIB was set from whether libquadmath exists on the build machine,
not from FLANG_RUNTIME_F128_MATH_LIB. Measured consequence: every flang binary
here linked libquadmath - the one built for the libm route, where the runtime
deliberately does not, and the one built with REAL(16) disabled entirely, where
the folding table is unreachable and the 43 symbols are dead weight.

It now follows the route. libquadmath selects the *q tables and errors out if
the library or header is missing rather than silently disabling folding; libm
selects the *f128 tables and defines the macro that reveals glibc's prototypes;
an empty setting selects neither, which is correct because REAL(16) is disabled
in that configuration and the tables could never be reached.

__STDC_WANT_IEC_60559_TYPES_EXT__ goes on the target for the same reason as in
flang-rt: no header can guarantee it is seen before <cmath> in every
translation unit, and when it is not the prototypes simply do not appear.

No configuration loses anything. The empty route loses only the dead linkage;
selected_real_kind(33) is -1 there and semantics rejects the type before
folding is ever consulted.
---
 flang/lib/Evaluate/CMakeLists.txt | 35 ++++++++++++++++++++++++++-----
 1 file changed, 30 insertions(+), 5 deletions(-)

diff --git a/flang/lib/Evaluate/CMakeLists.txt b/flang/lib/Evaluate/CMakeLists.txt
index 472ecb6d8d079..af25209b5cd62 100644
--- a/flang/lib/Evaluate/CMakeLists.txt
+++ b/flang/lib/Evaluate/CMakeLists.txt
@@ -21,11 +21,36 @@ if (LIBPGMATH_DIR)
   endif()
 endif()
 
-check_library_exists(quadmath sinq "" FOUND_QUADMATH_LIB)
-if (FLANG_INCLUDE_QUADMATH_H AND FOUND_QUADMATH_LIB)
-  configure_file("${FLANG_SOURCE_DIR}/cmake/quadmath_wrapper.h.in" "${CMAKE_CURRENT_BINARY_DIR}/quadmath_wrapper.h")
-  add_compile_definitions(HAS_QUADMATHLIB)
-  set(QUADMATHLIB quadmath)
+# Compile-time folding of REAL(16) constants needs a binary128 math library on
+# the host. Which one is not a separate question from which one the runtime
+# uses: before this was tied to FLANG_RUNTIME_F128_MATH_LIB, the folder was
+# enabled purely by libquadmath being installed on the build machine, so every
+# flang binary linked it - including one configured for the libm route, and one
+# configured with REAL(16) disabled entirely, where the table was unreachable
+# dead weight.
+#
+# Worse, on the libm route the two halves then disagreed about who computes:
+# `parameter :: v = sin(x)` folded through sinq while `y = sin(x)` ran through
+# sinf128. They agree on every point measured, but nothing makes them agree in
+# the last place.
+if (FLANG_RUNTIME_F128_MATH_LIB STREQUAL "libquadmath")
+  check_library_exists(quadmath sinq "" FOUND_QUADMATH_LIB)
+  if (FLANG_INCLUDE_QUADMATH_H AND FOUND_QUADMATH_LIB)
+    configure_file("${FLANG_SOURCE_DIR}/cmake/quadmath_wrapper.h.in" "${CMAKE_CURRENT_BINARY_DIR}/quadmath_wrapper.h")
+    add_compile_definitions(HAS_QUADMATHLIB)
+    set(QUADMATHLIB quadmath)
+  else()
+    message(FATAL_ERROR
+      "FLANG_RUNTIME_F128_MATH_LIB=libquadmath but quadmath.h or libquadmath "
+      "was not found; the compiler could not fold REAL(16) constants")
+  endif()
+elseif (FLANG_RUNTIME_F128_MATH_LIB STREQUAL "libm")
+  # glibc 2.26 and later carry the same functions as *f128 in libm, which is
+  # linked anyway. The macro that reveals their prototypes has to be on the
+  # command line for the same reason as in flang-rt: no header can guarantee it
+  # is seen before <cmath> in every translation unit.
+  add_compile_definitions(HAS_LIBMF128_HOST)
+  add_compile_definitions(__STDC_WANT_IEC_60559_TYPES_EXT__=1)
 endif ()
 
 add_flang_library(FortranEvaluate

>From 8c3ad5c62ae728fb8d888b8fc0a0b26dd1889197 Mon Sep 17 00:00:00 2001
From: Dmitry Mikushin <dmitry at kernelgen.org>
Date: Sat, 29 Aug 2026 19:42:04 +0200
Subject: [PATCH 6/7] [flang-rt] Test for the *f128 declarations, not just the
 symbols

Premerge CI failed on the default configuration with cabsf128 undeclared,
which this change caused in two ways.

First, the configure test asked the wrong question. check_library_exists(m
sinf128) looks for a symbol in libm, but the code needs a declaration in a
header, and on the premerge Linux runner those come apart: sinf128 is in
libm while <complex.h> declares no cabsf128. The test passed and every
complex entry point then failed to compile. It now compiles a program that
takes the addresses of both a scalar and a complex entry point, which needs
declarations and cannot be satisfied by the linker finding the symbols
anyway. Both halves are checked because they are gated separately and it is
the complex half that goes missing.

Second, and the reason CI saw it at all, the autodetected branch - the one
that runs when FLANG_RUNTIME_F128_MATH_LIB is unset, which is how CI builds
- had its target_sources enabled. That line was commented out upstream with
a note saying to enable it when math-entries.h and complex-math.h were
ready. Enabling it changes every default build, which is a different change
with a different blast radius from adding an opt-in route, and it is not
what this PR is for. That branch is restored byte for byte; the diff against
main is now two purely additive hunks in the explicit route.
---
 flang-rt/lib/quadmath/CMakeLists.txt | 44 ++++++++++++++++++++--------
 1 file changed, 31 insertions(+), 13 deletions(-)

diff --git a/flang-rt/lib/quadmath/CMakeLists.txt b/flang-rt/lib/quadmath/CMakeLists.txt
index 4c050eb66f810..0769cabcdc040 100644
--- a/flang-rt/lib/quadmath/CMakeLists.txt
+++ b/flang-rt/lib/quadmath/CMakeLists.txt
@@ -16,6 +16,8 @@
 
 include(CheckLibraryExists)
 include(CheckIncludeFile)
+include(CheckCSourceCompiles)
+include(CMakePushCheckState)
 
 set(sources
   acos.cpp
@@ -89,8 +91,29 @@ if (FLANG_RUNTIME_F128_MATH_LIB)
   elseif (${FLANG_RUNTIME_F128_MATH_LIB} STREQUAL "libm")
     # glibc 2.26 and later export the *f128 entry points from libm itself, so
     # this route pulls in no third-party library at all.
-    check_library_exists(m sinf128 "" FOUND_LIBMF128_EXPLICIT)
-    if (FOUND_LIBMF128_EXPLICIT)
+    #
+    # This compiles rather than looking for a symbol, because a symbol in libm
+    # is not the same thing as a declaration in a header and the two really do
+    # come apart. On the LLVM premerge Linux runner sinf128 is present in libm
+    # while <complex.h> declares no cabsf128: a check_library_exists test
+    # passes there and every complex entry point then fails to compile.
+    #
+    # Both halves are tested because they are gated separately and it is the
+    # complex half that goes missing. Taking the addresses is the point - that
+    # needs a declaration and nothing else, so the check cannot be satisfied by
+    # an implicit declaration or by the linker finding the symbol anyway.
+    cmake_push_check_state()
+    set(CMAKE_REQUIRED_DEFINITIONS -D__STDC_WANT_IEC_60559_TYPES_EXT__=1)
+    set(CMAKE_REQUIRED_LIBRARIES m)
+    check_c_source_compiles("
+#include <complex.h>
+#include <math.h>
+void *scalar = (void *)&sinf128;
+void *complex_ = (void *)&cabsf128;
+int main(void) { return scalar == complex_; }
+" HAVE_LIBM_F128_DECLARATIONS)
+    cmake_pop_check_state()
+    if (HAVE_LIBM_F128_DECLARATIONS)
       add_compile_definitions(HAS_LIBMF128)
       # glibc gates the *f128 prototypes behind this, and it has to be on the
       # command line rather than in a header: no header can guarantee it is
@@ -102,8 +125,10 @@ if (FLANG_RUNTIME_F128_MATH_LIB)
       add_compile_definitions(__STDC_WANT_IEC_60559_TYPES_EXT__=1)
     else()
       message(FATAL_ERROR
-        "FLANG_RUNTIME_F128_MATH_LIB=libm requires a libm that exports the "
-        "*f128 entry points (glibc 2.26 or later)"
+        "FLANG_RUNTIME_F128_MATH_LIB=libm requires a libm whose headers "
+        "declare both the scalar and the complex *f128 entry points "
+        "(glibc 2.26 or later, with a compiler it recognises as supporting "
+        "_Float128). The symbols being present in libm is not enough."
         )
     endif()
   else()
@@ -145,18 +170,11 @@ else()
   if (FOUND_LIBMF128)
     target_compile_definitions(FortranFloat128MathILib INTERFACE
       HAS_LIBMF128
-      # glibc gates the *f128 prototypes behind this. It must be on the command
-      # line, not in a header: a header cannot guarantee it is seen before
-      # <cmath> or <complex.h> in every translation unit, and when it is not,
-      # the prototypes silently do not appear. Measured on glibc 2.43: <cmath>
-      # exposes sqrtf128 without it, <complex.h> does not expose cabsf128 - so
-      # a header-based definition would look correct on the scalar half and
-      # fail on the complex half of the same build.
-      __STDC_WANT_IEC_60559_TYPES_EXT__=1
       )
     target_include_directories(FortranFloat128MathILib INTERFACE
       "${FLANG_RT_SOURCE_DIR}/lib/flang_rt"
       )
-    target_sources(FortranFloat128MathILib INTERFACE ${sources})
+    # Enable this, when math-entries.h and complex-math.h is ready.
+    # target_sources(FortranFloat128MathILib INTERFACE ${sources})
   endif()
 endif()

>From 698e308a3e508b67930f29a3f1c602c521f03def Mon Sep 17 00:00:00 2001
From: Dmitry Mikushin <dmitry at kernelgen.org>
Date: Sat, 29 Aug 2026 19:42:25 +0200
Subject: [PATCH 7/7] [flang] clang-format the lines this branch touches

---
 flang-rt/lib/quadmath/math-entries.h      | 5 ++---
 flang/lib/Evaluate/intrinsics-library.cpp | 6 ++++--
 2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/flang-rt/lib/quadmath/math-entries.h b/flang-rt/lib/quadmath/math-entries.h
index 28faa202d4dcf..f4d98ffb6b902 100644
--- a/flang-rt/lib/quadmath/math-entries.h
+++ b/flang-rt/lib/quadmath/math-entries.h
@@ -246,9 +246,8 @@ DEFINE_SIMPLE_ALIAS(Yn, ynl)
 // aliases below would resolve to nothing and the failure would surface at link
 // time or in the field. This makes it surface at the line that owns the
 // assumption.
-[[maybe_unused]] static CppTypeFor<TypeCategory::Real, 16> (
-    *const f128_prototype_canary)(CppTypeFor<TypeCategory::Real, 16>) =
-    &sqrtf128;
+[[maybe_unused]] static CppTypeFor<TypeCategory::Real, 16> (*const
+        f128_prototype_canary)(CppTypeFor<TypeCategory::Real, 16>) = &sqrtf128;
 
 DEFINE_SIMPLE_ALIAS(Abs, fabsf128)
 DEFINE_SIMPLE_ALIAS(Acos, acosf128)
diff --git a/flang/lib/Evaluate/intrinsics-library.cpp b/flang/lib/Evaluate/intrinsics-library.cpp
index 2572d361dbe91..919e2da3d01ec 100644
--- a/flang/lib/Evaluate/intrinsics-library.cpp
+++ b/flang/lib/Evaluate/intrinsics-library.cpp
@@ -640,9 +640,11 @@ host::HostComplex128 csqrtf128(host::HostComplex128);
 host::HostComplex128 ctanf128(host::HostComplex128);
 host::HostComplex128 ctanhf128(host::HostComplex128);
 }
-template <> struct HostRuntimeLibrary<host::HostComplex128, LibraryVersion::Libm> {
+template <>
+struct HostRuntimeLibrary<host::HostComplex128, LibraryVersion::Libm> {
   using F = FuncPointer<host::HostComplex128, host::HostComplex128>;
-  using F2 = FuncPointer<host::HostComplex128, host::HostComplex128, host::HostComplex128>;
+  using F2 = FuncPointer<host::HostComplex128, host::HostComplex128,
+      host::HostComplex128>;
   static constexpr HostRuntimeFunction table[]{
       FolderFactory<F, F{cacosf128}>::Create("acos"),
       FolderFactory<F, F{cacoshf128}>::Create("acosh"),



More information about the flang-commits mailing list