[RFC] Building Flang's builtin .mod files

Currently, the system module files needed when compiling a Fortran source in /flang/module are built when compiling flang itself. The instructions for this are in /flang/tools/f18[1]. This has some issues:

  1. Some of the .f90 files are target-dependent. While AFAIU .mod files are in principle target-triple-independent, some contain #ifdefs for specific targets. This includes: iso_c_binding.f90 (C ABI is obviously target-dependent), __fortran_ieee_exceptions.f90, and ieee_arithmetic.f90 (floating point support varies between targets).

  2. __ppc_intrinsics.f90 and mma.f90 are only compiled if the LLVM PowerPC backend is enabled, since otherwise Flang will reject it.

  3. Module dependency resolution is done manually in the CMakeLists.txt using a if-elseif cascade.

  4. Does not work when cross-compiling.

  5. For building omp_lib.mod. requires LLVM_ENABLE_PROJECTS=openmp. This is because it looks for omp_var.F90 in openmp’s build directory because it is the output of openmp’s configure phase. With LLVM_ENABLE_RUNTIMES=openmp, openmp’s build dir is somewhere else, and might not even be present if building openmp standalone (in contrast to a bootstrapping build). LLVM_ENABLE_PROJECTS=openmp will eventually be deprecated and we currently have no other way to build omp_lib.mod. Also note that omp_var.F90 imports iso_c_binding.

Require LLVM_RUNTIME_TARGETS=default

Low effort solution where we can assume to find omp_lib.F90 at ${CMAKE_BUILD_DIR}/runtimes/runtimes-bins/openmp/runtime/src.

  • :white_check_mark: Solves 5: Can deprecate LLVM_ENABLE_PROJECTS=openmp
  • :x: Solves only 5.
  • :x: Only works with bootstrapping-builds, not standalone-runtime builds.
  • :x: Must build runtime default target, even if only interested in cross-compilation

What’s currently upstream in main

There are additional sets of build instructions at
openmp/runtime/src/CMakeLists.txt#L378-L383
and
openmp/runtime/src/CMakeLists.txt#L400-L406

The first is activated with custom code in llvm/runtimes/CMakeLists.txt. With LLVM_RUNTIMES_TARGETS=default, the omp_lib.mod is emitted to runtimes/runtimes-bins/openmp/runtime/src/omp_lib.mod. Because the flang-driver needs to find omp_lib.mod when running tests, that path is added using -J. The actual path is hardcoded.

This wasn’t working for me because check-flang does not depend on building the runtimes. So just running ninja check-flang in a newly configured build directory would fail a lot of OpenMP tests. The CMake output is also “Not building omp_lib.mod, no OpenMP runtime in LLVM_ENABLED_PROJECTS”.

  • :white_check_mark: Solves 5: Can deprecate LLVM_ENABLE_PROJECTS=openmp
  • :x: Solves only 5.
  • :x: Only works with bootstrapping-builds, not standalone-runtime builds.
  • :x: Must build runtime default target, even if only interested in cross-compilation
  • :x: When building another LLVM_RUNTIME_TARGETS, on ninja install the omp_lib.mod files for each target will overwrite each other.

Remove the configure-time dependency to openmp

Some placeholders in the file omp_lib.F90.var are replaced with values from the build configuration:

  • Version of the OpenMP runtime library (libomp.so): kmp_version_major, kmp_version_minor, kmp_version_build, kmp_build_date.
  • Version of the OpenMP standard supported: openmp_version

I think what version libomp.so is does not even belong into the header file. Its version may be different from when an OpenMP program is compiled and when it is executed. For instance, an OS update may update libomp.so but the application will not notice since it the header value is fixed. Should be replaced by a reference to a global variable defined in libomp.so using e.g. bind(C).

According to the OpenMP spec, openmp_version is the equivalent to the macro _OPENMP in C, i.e. it is defined by version of OpenMP the compiler supports, not the runtime library or its header. It still might be regarded acceptable since both of them together are the OpenMP implementation. However, to avoid the configure-dependency, could be replaced by a built-in like __builtin_openmp_version that the compiler replaces when compiling to the .mod file.

With these changes, flang could instead compile openmp/runtime/include/omp_lib.F90 from the source tree directly.

  • :white_check_mark: Solves 5: Can deprecate LLVM_ENABLE_PROJECTS=openmp
  • :white_check_mark: “more correct” than current approach
  • :x: Solves only 5.

Build the .mod files in the runtimes, not by LLVM_ENABLE_PROJECTS=flang

Instead of the flang build looking for the .f90 files, make the runtimes look for the flang compiler to build their .mod files. That is, LLVM_ENABLE_RUNTIMES=openmp would build omp_lib.mod, and LLVM_ENABLE_RUNTIMES=flang-rt builds the .mod files from flang/module. It already builds the .o file from iso_fortran_env_impl.f90. Therefore I would move the files from /flang/module to Flang-RT.

The target-dependent modules also need to be install into target-triple-dependent paths or they would overwrite each other. That is, the driver needs to be changed to look for modules files in per-target directory. For gfortran, this is already the case. On my Ubuntu system, gfortran’s .mod files are located at /usr/lib/gcc/x86_64-linux-gnu/13/finclude/.

The necessary infrastructure is largely already created in #110217. That is, openmp and flang-rt can instruct the LLVM_ENABLE_RUNTIMES system to make flang available as Fortran compiler.

  • :white_check_mark: Solves 1: Different module files for each target
  • :white_check_mark: Solves 2: PowerPC module files only included on PowerPC target
  • :white_check_mark: Solves 3: Dependency resolution done by CMake
  • :white_check_mark: Solves 4: Cross-compilation done by LLVM_ENABLE_RUNTIMES bootstrapping build
  • :white_check_mark: Solves 5: Can deprecate LLVM_ENABLE_PROJECTS=openmp
  • :x: Redundant non-target dependent module files
  • :x: Modules only become available when compiling openmp/flang-rt
  • :x: Does not yet work without changes to Flang

This is what I already tried in the first drafts of #110217, but removed this part because I encountered some problems:

  • CMake by default passes pre-processed sources (which are already available because CMake uses it for its dependency analysis), but Flang cannot always compile its own preprocessor output [sic!]. Fortunately, CMake has a workaround, or just adding some line breaks into will solve this issue.

  • Compiling modules requires builtin modules, even if those are the builtin modules themselves. Fortunately, CMake already has a solution: Fortran_BUILDING_INSTRINSIC_MODULES. This option was actually added for Classic Flang.

  • CMake assumes that the output of the compilation step is a .o file, but with -fsyntax-only[2] no object file is being emitted. Unmitigated, CMake/ninja will rebuild the module files every time. We could just emit an empty object files for module-only sources, but unfortunately crashes on one some of the builtin modules, e.g. iso_fortran_env.f90 because the value of __builtin_numeric_storage_size() cannot be emitted into an object file (it is not a constant but depends on compiler switches passed to Flang). This is also the reason why iso_fortran_env_impl.f90 exists.

  • Flang crashes while compiling __fortran_builtins.mod with the error

error: loc(".../flang/module/__fortran_builtins.f90":203:5): runtime derived type info descriptor was not generated

There is already is a flag that allows emit the object file: -mllvm -ignore-missing-type-desc


  1. Even though there is no f18 executable anymore. The dump.cpp file is unused. ↩︎

  2. -fsyntax-only still creates the .mod file, hence the switch is a misnomer ↩︎

It might be apparent that I am heavily tending towards the last approach. If Flang should some day be able to cross-compile, it is needed at least for some builtin modules. I would like the implications to be discussed beforehand.

The last approach sounds the most reasonable, clang requires the openmp project to be enabled to get omp.h installed in its resource directory so it’s not unheard of to handle this externally. We could probably do the first as the easy fix to the OpenMP problem and then work on refining it for cross-compiling later.

I missed that we already have a solution for building omp_lib.mod when LLVM_ENABLE_RUNTIMES=openmp, but Flang’s OpenMP tests were still failing for me. I amended the original post.