// Control Macros
#ifdef PGMATH_DECLARE
#undef PGMATH_DECLARE
+#define DEFINE_C_COMPLEX_TYPES
#define PGMATH_DECLARE(x) extern "C" x;
#define PGMATH_FAST
#define PGMATH_PRECISE
#ifdef PGMATH_USE_ALL_TYPES
#define PGMATH_USE_S(name, func) PGMATH_USE_ALL_TYPES(name, func)
#define PGMATH_USE_D(name, func) PGMATH_USE_ALL_TYPES(name, func)
+#define PGMATH_USE_C(name, func) PGMATH_USE_ALL_TYPES(name, func)
+#define PGMATH_USE_Z(name, func) PGMATH_USE_ALL_TYPES(name, func)
#define PGMATH_USE_OTHER(name, func) PGMATH_USE_ALL_TYPES(name, func)
#endif
#define PGMATH_USE_OTHER(name, x)
#endif
+// Handle the C99 _Complex vs C++ std::complex call interface issue.
+// _Complex and std::complex are layout compatible (they are the same when
+// in memory), but they are not guaranteed to be compatible in call interface
+// (they may be passed/returned differently). For instance on X86 32 bits,
+// float _complex is returned in a pair of register, but std::complex<float>
+// is returned in memory.
+// Pgmath is defined in C using _Complex (and windows _Fcomplex/_DComplex
+// equivalents). Since this file defines the call interface with the runtime
+// for both folding and code generation (through template introspection), it
+// is crucial to make a distinction between std::complex and _Complex here.
+// Unfortunately, _Complex support is not standard in C++.
+// Reserve pgmath usage at compile time (folding) when _Complex is available
+// (cmake is responsible to detect this).
+// For code generation, define type c_float_complex_t that can be used in
+// introspection to indicate that the C99 _Complex ABI has to be used for the
+// related value.
+#ifdef DEFINE_C_COMPLEX_TYPES
+#ifdef PGMATH_LINKING
+#ifdef _WIN32
+using c_float_complex_t = _Fcomplex;
+using c_double_complex_t = _Dcomplex;
+#else
+using c_float_complex_t = float _Complex;
+using c_double_complex_t = double _Complex;
+#endif
+#else
+struct c_float_complex_t {};
+struct c_double_complex_t {};
+#endif
+#endif
+
#define PGMATH_REAL_IMPL(impl, func) \
PGMATH_DECLARE(float __##impl##s_##func##_1(float)) \
PGMATH_DECLARE(double __##impl##d_##func##_1(double)) \
PGMATH_USE_S(func, __##impl##s_##func##_1) \
PGMATH_USE_D(func, __##impl##d_##func##_1)
+#define PGMATH_COMPLEX_IMPL(impl, func) \
+ PGMATH_DECLARE(c_float_complex_t __##impl##c_##func##_1(c_float_complex_t)) \
+ PGMATH_DECLARE( \
+ c_double_complex_t __##impl##z_##func##_1(c_double_complex_t)) \
+ PGMATH_USE_C(func, __##impl##c_##func##_1) \
+ PGMATH_USE_Z(func, __##impl##z_##func##_1)
+
#define PGMATH_ALL_FP_IMPL(impl, func) \
PGMATH_REAL_IMPL(impl, func) \
+ PGMATH_FAST_COMPLEX_IMPL(impl, func)
#define PGMATH_REAL2_IMPL(impl, func) \
PGMATH_DECLARE(float __##impl##s_##func##_1(float, float)) \
PGMATH_USE_S(func, __##impl##s_##func##_1) \
PGMATH_USE_D(func, __##impl##d_##func##_1)
+#define PGMATH_COMPLEX2_IMPL(impl, func) \
+ PGMATH_DECLARE(c_float_complex_t __##impl##c_##func##_1( \
+ c_float_complex_t, c_float_complex_t)) \
+ PGMATH_DECLARE(c_double_complex_t __##impl##z_##func##_1( \
+ c_double_complex_t, c_double_complex_t)) \
+ PGMATH_USE_C(func, __##impl##c_##func##_1) \
+ PGMATH_USE_Z(func, __##impl##z_##func##_1)
+
#define PGMATH_ALL_FP2_IMPL(impl, func) \
PGMATH_REAL2_IMPL(func) \
+ PGMATH_COMPLEX2_IMPL(func)
#undef PGMATH_FAST_REAL
#undef PGMATH_FAST_COMPLEX
#undef PGMATH_FAST_ALL_FP2
#ifdef PGMATH_FAST
#define PGMATH_FAST_REAL(func) PGMATH_REAL_IMPL(f, func)
+#define PGMATH_FAST_COMPLEX(func) PGMATH_COMPLEX_IMPL(f, func)
#define PGMATH_FAST_ALL_FP(func) PGMATH_ALL_IMPL(f, func)
#define PGMATH_FAST_REAL2(func) PGMATH_REAL2_IMPL(f, func)
+#define PGMATH_FAST_COMPLEX2(func) PGMATH_COMPLEX2_IMPL(f, func)
#define PGMATH_FAST_ALL_FP2(func) PGMATH_ALL_FP2_IMPL(f, func)
#else
#define PGMATH_FAST_REAL(func)
#undef PGMATH_RELAXED_ALL_FP2
#ifdef PGMATH_RELAXED
#define PGMATH_RELAXED_REAL(func) PGMATH_REAL_IMPL(r, func)
+#define PGMATH_RELAXED_COMPLEX(func) PGMATH_COMPLEX_IMPL(r, func)
#define PGMATH_RELAXED_ALL_FP(func) PGMATH_ALL_IMPL(r, func)
#define PGMATH_RELAXED_REAL2(func) PGMATH_REAL2_IMPL(r, func)
+#define PGMATH_RELAXED_COMPLEX2(func) PGMATH_COMPLEX2_IMPL(r, func)
#define PGMATH_RELAXED_ALL_FP2(func) PGMATH_ALL_FP2_IMPL(r, func)
#else
#define PGMATH_RELAXED_REAL(func)
#undef PGMATH_PRECISE_ALL_FP2
#ifdef PGMATH_PRECISE
#define PGMATH_PRECISE_REAL(func) PGMATH_REAL_IMPL(p, func)
+#define PGMATH_PRECISE_COMPLEX(func) PGMATH_COMPLEX_IMPL(p, func)
#define PGMATH_PRECISE_ALL_FP(func) PGMATH_ALL_IMPL(p, func)
#define PGMATH_PRECISE_REAL2(func) PGMATH_REAL2_IMPL(p, func)
+#define PGMATH_PRECISE_COMPLEX2(func) PGMATH_COMPLEX2_IMPL(p, func)
#define PGMATH_PRECISE_ALL_FP2(func) PGMATH_ALL_FP2_IMPL(p, func)
#else
#define PGMATH_PRECISE_REAL(func)
PGMATH_PRECISE_REAL(func) \
PGMATH_RELAXED_REAL(func)
+#define PGMATH_COMPLEX(func) \
+ PGMATH_FAST_COMPLEX(func) \
+ PGMATH_PRECISE_COMPLEX(func) \
+ PGMATH_RELAXED_COMPLEX(func)
+
#define PGMATH_ALL(func) \
PGMATH_REAL(func) \
+ PGMATH_COMPLEX(func)
#define PGMATH_REAL2(func) \
PGMATH_FAST_REAL2(func) \
PGMATH_PRECISE_REAL2(func) \
PGMATH_RELAXED_REAL2(func)
+#define PGMATH_COMPLEX2(func) \
+ PGMATH_FAST_COMPLEX2(func) \
+ PGMATH_PRECISE_COMPLEX2(func) \
+ PGMATH_RELAXED_COMPLEX2(func)
+
#define PGMATH_ALL2(func) \
PGMATH_REAL2(func) \
+ PGMATH_COMPLEX2(func)
// Marcos to declare __mth_i libpgmath variants
#define PGMATH_MTH_VERSION_REAL(func) \
#define PGMATH_DELCARE_POW(impl) \
PGMATH_DECLARE(float __##impl##s_powi_1(float, int)) \
PGMATH_DECLARE(double __##impl##d_powi_1(double, int)) \
+ PGMATH_DECLARE(c_float_complex_t __##impl##c_powi_1(c_float_complex_t, int)) \
+ PGMATH_DECLARE( \
+ c_double_complex_t __##impl##z_powi_1(c_double_complex_t, int)) \
PGMATH_USE_S(pow, __##impl##s_powi_1) \
PGMATH_USE_D(pow, __##impl##d_powi_1) \
PGMATH_USE_C(pow, __##impl##c_powi_1) \
PGMATH_USE_Z(pow, __##impl##z_powi_1) \
PGMATH_DECLARE(float __##impl##s_powk_1(float, int64_t)) \
PGMATH_DECLARE(double __##impl##d_powk_1(double, int64_t)) \
+ PGMATH_DECLARE( \
+ c_float_complex_t __##impl##c_powk_1(c_float_complex_t, int64_t)) \
+ PGMATH_DECLARE( \
+ c_double_complex_t __##impl##z_powk_1(c_double_complex_t, int64_t)) \
PGMATH_USE_S(pow, __##impl##s_powk_1) \
PGMATH_USE_D(pow, __##impl##d_powk_1) \
PGMATH_USE_C(pow, __##impl##c_powk_1) \
PGMATH_ALL(sin)
PGMATH_ALL(sinh)
PGMATH_MTH_VERSION_REAL(sqrt)
+PGMATH_COMPLEX(sqrt) // real versions are __mth_i...
PGMATH_ALL(tan)
PGMATH_ALL(tanh)
#undef PGMATH_USE_Z
#undef PGMATH_USE_OTHER
#undef PGMATH_USE_ALL_TYPES
+#undef PGMATH_LINKING
+#undef DEFINE_C_COMPLEX_TYPES
--- /dev/null
+//===- RTBuilder.cpp -- Runtime Interface unit tests ----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "../../lib/Lower/RTBuilder.h"
+#include "gtest/gtest.h"
+#include "flang/Optimizer/Support/InitFIR.h"
+
+// Check that it is possible to make a difference between complex runtime
+// function using C99 complex and C++ std::complex. This is important since
+// they are layout compatible but not link time compatible (returned differently
+// in X86 32 ABI for instance). At high level fir, we need to convey that the
+// signature are different regardless of the target ABI.
+
+// Fake runtime header to be introspected.
+c_float_complex_t c99_cacosf(c_float_complex_t);
+
+TEST(RTBuilderTest, ComplexRuntimeInterface) {
+ mlir::DialectRegistry registry;
+ fir::support::registerDialects(registry);
+ mlir::MLIRContext ctx(registry);
+ fir::support::loadDialects(ctx);
+ mlir::Type c99_cacosf_signature{
+ Fortran::lower::RuntimeTableKey<decltype(c99_cacosf)>::getTypeModel()(
+ &ctx)};
+ auto c99_cacosf_funcTy = c99_cacosf_signature.cast<mlir::FunctionType>();
+ EXPECT_EQ(c99_cacosf_funcTy.getNumInputs(), 1u);
+ EXPECT_EQ(c99_cacosf_funcTy.getNumResults(), 1u);
+ auto cplx_ty = fir::ComplexType::get(&ctx, 4);
+ EXPECT_EQ(c99_cacosf_funcTy.getInput(0), cplx_ty);
+ EXPECT_EQ(c99_cacosf_funcTy.getResult(0), cplx_ty);
+}