diff --git a/flang/include/flang/Evaluate/pgmath.h.inc b/flang/include/flang/Evaluate/pgmath.h.inc --- a/flang/include/flang/Evaluate/pgmath.h.inc +++ b/flang/include/flang/Evaluate/pgmath.h.inc @@ -22,6 +22,7 @@ // 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 @@ -33,6 +34,8 @@ #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 @@ -56,14 +59,53 @@ #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 +// 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)) \ @@ -71,8 +113,17 @@ 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 @@ -82,8 +133,10 @@ #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) @@ -102,8 +155,10 @@ #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) @@ -122,8 +177,10 @@ #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) @@ -139,16 +196,28 @@ 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) \ @@ -207,12 +276,19 @@ #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) \ @@ -237,6 +313,7 @@ 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) @@ -250,3 +327,5 @@ #undef PGMATH_USE_Z #undef PGMATH_USE_OTHER #undef PGMATH_USE_ALL_TYPES +#undef PGMATH_LINKING +#undef DEFINE_C_COMPLEX_TYPES diff --git a/flang/lib/Evaluate/CMakeLists.txt b/flang/lib/Evaluate/CMakeLists.txt --- a/flang/lib/Evaluate/CMakeLists.txt +++ b/flang/lib/Evaluate/CMakeLists.txt @@ -2,8 +2,18 @@ # If pgmath library is found, it can be used for constant folding. find_library(LIBPGMATH pgmath PATHS ${LIBPGMATH_DIR}) if(LIBPGMATH) - add_compile_definitions(LINK_WITH_LIBPGMATH) - message(STATUS "Found libpgmath: ${LIBPGMATH}") + # pgmath uses _Complex, so only enable linking pgmath with flang in environments + # that support it (MSVC is OK, pgmath uses _Fcomplex/_Dcomplex there). + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU|MSVC") + check_cxx_compiler_flag("-Werror -Wc99-extensions" HAS_WC99_EXTENSIONS_FLAG) + if (HAS_WC99_EXTENSIONS_FLAG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-c99-extensions") + endif() + add_compile_definitions(LINK_WITH_LIBPGMATH) + message(STATUS "Found libpgmath: ${LIBPGMATH}") + else() + message(STATUS "Libpgmath will not be used because C99 complex is not supported.") + endif() else() message(STATUS "Libpgmath not found in: ${LIBPGMATH_DIR}") endif() diff --git a/flang/lib/Lower/RTBuilder.h b/flang/lib/Lower/RTBuilder.h --- a/flang/lib/Lower/RTBuilder.h +++ b/flang/lib/Lower/RTBuilder.h @@ -27,6 +27,13 @@ // List the runtime headers we want to be able to dissect #include "flang/Runtime/io-api.h" +// Incomplete type indicating C99 complex ABI in interfaces. Beware, _Complex +// and std::complex are layout compatible, but not compatible in all ABI call +// interface (e.g. X86 32 bits). _Complex is not standard C++, so do not use +// it here. +struct c_float_complex_t; +struct c_double_complex_t; + namespace Fortran::lower { using TypeBuilderFunc = mlir::Type (*)(mlir::MLIRContext *); @@ -156,7 +163,18 @@ return fir::ReferenceType::get(f(context)); }; } - +template <> +constexpr TypeBuilderFunc getModel() { + return [](mlir::MLIRContext *context) -> mlir::Type { + return fir::ComplexType::get(context, sizeof(float)); + }; +} +template <> +constexpr TypeBuilderFunc getModel() { + return [](mlir::MLIRContext *context) -> mlir::Type { + return fir::ComplexType::get(context, sizeof(double)); + }; +} template <> constexpr TypeBuilderFunc getModel() { return [](mlir::MLIRContext *context) -> mlir::Type { diff --git a/flang/unittests/CMakeLists.txt b/flang/unittests/CMakeLists.txt --- a/flang/unittests/CMakeLists.txt +++ b/flang/unittests/CMakeLists.txt @@ -39,5 +39,6 @@ add_subdirectory(Optimizer) add_subdirectory(Decimal) add_subdirectory(Evaluate) +add_subdirectory(Lower) add_subdirectory(Runtime) add_subdirectory(Frontend) diff --git a/flang/unittests/Lower/CMakeLists.txt b/flang/unittests/Lower/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/flang/unittests/Lower/CMakeLists.txt @@ -0,0 +1,15 @@ +get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) + +set(LIBS + FIROptimizer + MLIRLLVMIR + ${dialect_libs} +) + +add_flang_unittest(FlangLoweringTests + RTBuilder.cpp +) + +target_link_libraries(FlangLoweringTests + PRIVATE + ${LIBS}) diff --git a/flang/unittests/Lower/RTBuilder.cpp b/flang/unittests/Lower/RTBuilder.cpp new file mode 100644 --- /dev/null +++ b/flang/unittests/Lower/RTBuilder.cpp @@ -0,0 +1,36 @@ +//===- 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::getTypeModel()( + &ctx)}; + auto c99_cacosf_funcTy = c99_cacosf_signature.cast(); + 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); +}