[runtimes][CMake] Move Fortran support code from flang-rt (#171610)

Common CMake code to be used by flang-rt and openmp to emit Flang module
files. Most of the code is not yet used within this PR.

Extracted out of #171515 for review by @petrhosek.
This commit is contained in:
Michael Kruse
2026-04-21 11:58:43 +01:00
committed by GitHub
parent af5fb3870a
commit 8e132f78bf
4 changed files with 290 additions and 0 deletions

View File

@@ -49,6 +49,17 @@ function (get_toolchain_fortran_module_subdir outvar)
endfunction () endfunction ()
# Corresponds to Flang's ToolChain::getDefaultIntrinsicModuleDir().
function (get_toolchain_module_subdir outvar)
set(outval "finclude/flang")
get_toolchain_target_dirname(arch_dirname)
set(outval "${outval}/${arch_dirname}")
set(${outvar} "${outval}" PARENT_SCOPE)
endfunction ()
# Corresponds to Clang's ToolChain::getOSLibName(). Adapted from Compiler-RT. # Corresponds to Clang's ToolChain::getOSLibName(). Adapted from Compiler-RT.
function (get_toolchain_os_dirname outvar) function (get_toolchain_os_dirname outvar)
if (ANDROID) if (ANDROID)

View File

@@ -345,6 +345,20 @@ function (add_flangrt_library name)
# directory. Otherwise it is part of testing and is not installed at all. # directory. Otherwise it is part of testing and is not installed at all.
# TODO: Consider multi-configuration builds (MSVC_IDE, "Ninja Multi-Config") # TODO: Consider multi-configuration builds (MSVC_IDE, "Ninja Multi-Config")
if (ARG_INSTALL_WITH_TOOLCHAIN) if (ARG_INSTALL_WITH_TOOLCHAIN)
# FIXME: RUNTIMES_OUTPUT_RESOURCE_LIB_DIR is not a good location for
# shared libraries because it is not a ld.so default search path.
# Also, the machine where the executable is eventually executed may
# not be one where the compiler is installed, so even RPATH/RUNPATH
# will not help. The most appropriate location for shared libraries
# is /usr/lib/<triple>/lib<name>.so, like e.g. libgcc_s.so.
# Flang-RT also would require a library versioning scheme so
# executables compiled with different versions of Flang either use
# matching versions of Flang-RT, or use a newer backward-compatible
# version. Currently, Flang-RT has no ABI backwards-compatibility
# policy.
# Currently, we just emit it into RUNTIMES_OUTPUT_RESOURCE_LIB_DIR
# like the static library, which is already in the driver's and
# linker's search path.
set_target_properties(${tgtname} set_target_properties(${tgtname}
PROPERTIES PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${RUNTIMES_OUTPUT_RESOURCE_LIB_DIR}" ARCHIVE_OUTPUT_DIRECTORY "${RUNTIMES_OUTPUT_RESOURCE_LIB_DIR}"

View File

@@ -270,6 +270,21 @@ cmake_path(NORMAL_PATH RUNTIMES_OUTPUT_RESOURCE_LIB_DIR)
cmake_path(NORMAL_PATH RUNTIMES_INSTALL_RESOURCE_LIB_PATH) cmake_path(NORMAL_PATH RUNTIMES_INSTALL_RESOURCE_LIB_PATH)
# Code for Fortran support.
#
# Currently, only flang-rt and openmp contain Fortran sources. We assume that
# none of the public CMake variables defined in config-Fortran are used unless
# we enable one of these runtimes. Other runtimes would make used of undefined
# variables.
#
# config-Fortran.cmake makes use of
# RUNTIMES_OUTPUT_RESOURCE_DIR, RUNTIMES_INSTALL_RESOURCE_PATH,
# hence must be included after those variables are defined.
if ("flang-rt" IN_LIST LLVM_ENABLE_RUNTIMES OR "openmp" IN_LIST LLVM_ENABLE_RUNTIMES)
include(config-Fortran)
endif ()
option(LLVM_INCLUDE_TESTS "Generate build targets for the runtimes unit tests." ON) option(LLVM_INCLUDE_TESTS "Generate build targets for the runtimes unit tests." ON)
option(LLVM_INCLUDE_DOCS "Generate build targets for the runtimes documentation." ON) option(LLVM_INCLUDE_DOCS "Generate build targets for the runtimes documentation." ON)
option(LLVM_ENABLE_SPHINX "Use Sphinx to generate the runtimes documentation." OFF) option(LLVM_ENABLE_SPHINX "Use Sphinx to generate the runtimes documentation." OFF)

View File

@@ -0,0 +1,250 @@
# This file sets the following public CMake variables:
#
# RUNTIMES_ENABLE_FORTRAN - Whether support for Fortran code is available and
# enabled. This is currently not intended to be a user-configuration but
# derived from CMAKE_Fortran_COMPILER. Can also be OFF when Fortran support is
# not needed or is insufficient, e.g. if intrinsic modules are missing and
# cannot be compiled on-the-fly.
#
# RUNTIMES_FORTRAN_BUILD_DEPS - If RUNTIMES_ENABLE_FORTRAN is true, this is a
# list of dependencies that must be built before any Fortran source can be
# compiled. Contains the build targets for intrinsic modules, if necessary.
# Otherweise, it is empty.
#
# RUNTIMES_ENABLE_FLANG_MODULES - Whether to build Flang modules and emit them
# into Flang's search path. This is a CMake CACHE option defined in
# config-Fortran.cmake and default to ON iff the Fortran compiler is detected
# for be a (compatible) version of Flang. In the OFF setting, modules are still
# built, but not installed or emitted into a default path.
#
# RUNTIMES_OUTPUT_RESOURCE_MOD_DIR - Where to emit intrinsic module files in
# the build directory. Most relevant when RUNTIMES_ENABLE_FLANG_MODULES is ON.
#
# RUNTIMES_INSTALL_RESOURCE_MOD_PATH - Where to install intrinsic module files
# in the install prefix. Relative to CMAKE_INSTALL_PREFIX. Only used when
# RUNTIMES_ENABLE_FLANG_MODULES is ON.
# Check whether the Fortran compiler already has access to builtin modules. Sets
# HAVE_FORTRAN_INTRINSIC_MODS when returning.
#
# This must be wrapped in a function because
# cmake_push_check_state/cmake_pop_check_state is insufficient to isolate
# a compiler introspection environment, see
# https://gitlab.kitware.com/cmake/cmake/-/issues/27419
function (check_fortran_builtins_available)
if (CMAKE_Fortran_COMPILER_FORCED AND CMAKE_Fortran_COMPILER_ID STREQUAL "LLVMFlang")
# CMake's check_fortran_source_compiles/try_compile does not take a
# user-defined CMAKE_Fortran_PREPROCESS_SOURCE into account. Instead of
# test-compiling, ask Flang directly for the builtin module files.
# CMAKE_Fortran_PREPROCESS_SOURCE is defined for CMake < 3.24 because it
# does not natively recognize Flang (see below). Once we bump the required
# CMake version, and because setting CMAKE_Fortran_PREPROCESS_SOURCE has
# been deprecated by CMake, this workaround can be removed.
if (NOT DEFINED FORTRAN_HAS_ISO_C_BINDING_MOD)
message(STATUS "Performing Test ISO_C_BINDING_PATH")
execute_process(
COMMAND ${CMAKE_Fortran_COMPILER} ${CMAKE_Fortran_FLAGS} "-print-file-name=iso_c_binding.mod"
OUTPUT_VARIABLE ISO_C_BINDING_PATH
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
set(FORTRAN_HAS_ISO_C_BINDING_MOD "")
if (EXISTS "${ISO_C_BINDING_PATH}")
message(STATUS "Performing Test ISO_C_BINDING_PATH -- Success")
set(FORTRAN_HAS_ISO_C_BINDING_MOD TRUE CACHE INTERNAL "Existence result of ${CMAKE_Fortran_COMPILER} -print-file-name=iso_c_binding.mod")
else ()
message(STATUS "Performing Test ISO_C_BINDING_PATH -- Failed")
set(FORTRAN_HAS_ISO_C_BINDING_MOD FALSE CACHE INTERNAL "Existence result of ${CMAKE_Fortran_COMPILER} -print-file-name=iso_c_binding.mod")
endif ()
endif ()
else ()
cmake_push_check_state(RESET)
set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY")
check_fortran_source_compiles("
subroutine testroutine
use iso_c_binding
end subroutine
" FORTRAN_HAS_ISO_C_BINDING_MOD SRC_EXT F90)
cmake_pop_check_state()
endif ()
set(HAVE_FORTRAN_INTRINSIC_MODS "${FORTRAN_HAS_ISO_C_BINDING_MOD}" PARENT_SCOPE)
endfunction ()
# Workarounds for older versions of CMake not recognizing FLang. Hence, we
# cannot use CMAKE_Fortran_COMPILER_ID.
cmake_path(GET CMAKE_Fortran_COMPILER STEM _Fortran_COMPILER_STEM)
if (_Fortran_COMPILER_STEM STREQUAL "flang-new" OR _Fortran_COMPILER_STEM STREQUAL "flang")
# CMake 3.24 is the first version of CMake that directly recognizes Flang.
# LLVM's requirement is only CMake 3.20, teach CMake 3.20-3.23 how to use Flang, if used.
if (CMAKE_VERSION VERSION_LESS "3.24")
include(CMakeForceCompiler)
CMAKE_FORCE_Fortran_COMPILER("${CMAKE_Fortran_COMPILER}" "LLVMFlang")
set(CMAKE_Fortran_COMPILER_ID "LLVMFlang")
set(CMAKE_Fortran_COMPILER_VERSION "${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}")
set(CMAKE_Fortran_SUBMODULE_SEP "-")
set(CMAKE_Fortran_SUBMODULE_EXT ".mod")
set(CMAKE_Fortran_PREPROCESS_SOURCE
"<CMAKE_Fortran_COMPILER> -cpp <DEFINES> <INCLUDES> <FLAGS> -E <SOURCE> > <PREPROCESSED_SOURCE>")
set(CMAKE_Fortran_FORMAT_FIXED_FLAG "-ffixed-form")
set(CMAKE_Fortran_FORMAT_FREE_FLAG "-ffree-form")
set(CMAKE_Fortran_MODDIR_FLAG "-J")
set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON "-cpp")
set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_OFF "-nocpp")
set(CMAKE_Fortran_POSTPROCESS_FLAG "-ffixed-line-length-72")
set(CMAKE_Fortran_LINKER_WRAPPER_FLAG "-Wl,")
set(CMAKE_Fortran_LINKER_WRAPPER_FLAG_SEP ",")
set(CMAKE_Fortran_VERBOSE_FLAG "-v")
set(CMAKE_Fortran_LINK_MODE DRIVER)
endif ()
# Optimization flags are only passed after CMake 3.27.4
# https://gitlab.kitware.com/cmake/cmake/-/commit/1140087adea98bd8d8974e4c18979f4949b52c34
if (CMAKE_VERSION VERSION_LESS "3.27.4")
string(APPEND CMAKE_Fortran_FLAGS_DEBUG_INIT " -O0 -g")
string(APPEND CMAKE_Fortran_FLAGS_RELWITHDEBINFO_INIT " -O2 -g")
string(APPEND CMAKE_Fortran_FLAGS_RELEASE_INIT " -O3")
endif ()
# Only CMake 3.28+ pass --target= to Flang. But for cross-compiling, including
# to nvptx amd amdgpu targets, passing the target triple is essential.
# https://gitlab.kitware.com/cmake/cmake/-/commit/e9af7b968756e72553296ecdcde6f36606a0babf
if (CMAKE_VERSION VERSION_LESS "3.28")
set(CMAKE_Fortran_COMPILE_OPTIONS_TARGET "--target=")
endif ()
endif ()
set(RUNTIMES_ENABLE_FORTRAN OFF)
# Insert at least one element for
#
# add_dependencies(target ${RUNTIMES_FORTRAN_BUILD_DEPS})
#
# to not fail
add_custom_target(fortran-dummy-dep)
set(RUNTIMES_FORTRAN_BUILD_DEPS fortran-dummy-dep)
include(CheckLanguage)
check_language(Fortran)
if (CMAKE_Fortran_COMPILER)
enable_language(Fortran)
include(CheckFortranSourceCompiles)
if (CMAKE_Fortran_COMPILER_ID STREQUAL "LLVMFlang" AND "flang-rt" IN_LIST LLVM_ENABLE_RUNTIMES)
# In a bootstrapping build (or any runtimes-build that includes flang-rt),
# the intrinsic modules are not built yet. Targets can depend on
# flang-rt-mod to ensure that flang-rt's modules are built first.
list(APPEND RUNTIMES_FORTRAN_BUILD_DEPS flang-rt-mod)
set(RUNTIMES_ENABLE_FORTRAN ON)
message(STATUS "Fortran support enabled using just-built Flang-RT builtin modules")
else ()
# Check whether building modules works, avoid causing the entire build to
# fail because of Fortran. The primary situation we want to support here
# is Flang, or its intrinsic modules were built separately in a
# non-bootstrapping build.
check_fortran_builtins_available()
if (HAVE_FORTRAN_INTRINSIC_MODS)
set(RUNTIMES_ENABLE_FORTRAN ON)
message(STATUS "Fortran support enabled using compiler's own modules")
else ()
message(STATUS "Fortran support disabled: Not passing smoke check")
endif ()
endif ()
else ()
message(STATUS "Fortran support disabled: not enabled in CMake; Use CMAKE_Fortran_COMPILER_WORKS=yes if the issues is missing builtin modules")
endif ()
# Check whether modules files are compatible with our version of Flang.
set(RUNTIMES_ENABLE_FLANG_MODULES_default OFF)
if (CMAKE_Fortran_COMPILER_ID STREQUAL "LLVMFlang")
set(RUNTIMES_ENABLE_FLANG_MODULES_default ON)
else ()
set(RUNTIMES_ENABLE_FLANG_MODULES_default OFF)
endif ()
option(RUNTIMES_ENABLE_FLANG_MODULES "Make Fortran .mod files available to Flang; should only be enabled if compiling with a matching version of Flang" "${RUNTIMES_ENABLE_FLANG_MODULES_default}")
# Determine the paths for Fortran .mod files.
if (RUNTIMES_ENABLE_FLANG_MODULES)
# Flang expects its builtin modules in Clang's resource directory.
get_toolchain_module_subdir(toolchain_mod_subdir)
extend_path(RUNTIMES_OUTPUT_RESOURCE_MOD_DIR "${RUNTIMES_OUTPUT_RESOURCE_DIR}" "${toolchain_mod_subdir}")
extend_path(RUNTIMES_INSTALL_RESOURCE_MOD_PATH "${RUNTIMES_INSTALL_RESOURCE_PATH}" "${toolchain_mod_subdir}")
cmake_path(NORMAL_PATH RUNTIMES_INSTALL_RESOURCE_MOD_PATH)
# No way to find out which mod files are built by a target, so install the
# entire output directory. flang_module_target() will copy (only) the PUBLIC
# .mod file into the output directory.
# https://stackoverflow.com/questions/52712416/cmake-fortran-module-directory-to-be-used-with-add-library
set(destination "${RUNTIMES_INSTALL_RESOURCE_MOD_PATH}/..")
cmake_path(NORMAL_PATH destination)
install(DIRECTORY "${RUNTIMES_OUTPUT_RESOURCE_MOD_DIR}"
DESTINATION "${destination}"
)
else ()
# If Flang modules are disabled (e.g. because the compiler is not Flang), avoid the risk of Flang accidentally picking them up.
extend_path(RUNTIMES_OUTPUT_RESOURCE_MOD_DIR "${CMAKE_CURRENT_BINARY_DIR}" "finclude-${CMAKE_Fortran_COMPILER_ID}")
# We don't know how to install modules for other compilers. Do not install them at all.
set(RUNTIMES_INSTALL_RESOURCE_MOD_PATH "")
endif ()
cmake_path(NORMAL_PATH RUNTIMES_OUTPUT_RESOURCE_MOD_DIR)
# Set options to compile Fortran module files. Assumes the code above has run.
#
# Usage:
#
# flang_module_target(name
# PUBLIC
# Modules files are to be used by other Fortran sources. If a library is
# compiled multiple times (e.g. static/shared, or msvcrt variants), only
# one of those can be public module files; non-public modules are still
# generated but to be forgotten inside the build directory to not
# conflict with each other.
# Also, installs the module with the toolchain.
# )
function (flang_module_target tgtname)
set(options PUBLIC)
cmake_parse_arguments(ARG
"${options}"
""
""
${ARGN}
)
# Let all modules find the public module files
target_compile_options(${tgtname} PRIVATE
"$<$<COMPILE_LANGUAGE:Fortran>:-fintrinsic-modules-path=${RUNTIMES_OUTPUT_RESOURCE_MOD_DIR}>"
)
if (CMAKE_Fortran_COMPILER_ID MATCHES "LLVM")
target_compile_options(${tgtname} PRIVATE
# Flang bug workaround: Reformating of cooked token buffer causes
# identifier to be split between lines
"$<$<COMPILE_LANGUAGE:Fortran>:SHELL:-Xflang;SHELL:-fno-reformat>"
)
endif ()
if (ARG_PUBLIC)
set_target_properties(${tgtname}
PROPERTIES
Fortran_MODULE_DIRECTORY "${RUNTIMES_OUTPUT_RESOURCE_MOD_DIR}"
)
else ()
# Keep non-public modules where CMake would put them normally;
# Modules of different target must not overwrite each other.
endif ()
endfunction ()