[clang] 97a32d3 - [flang][driver] Add support for generating executables

Andrzej Warzynski via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 25 05:02:03 PDT 2022

Author: Andrzej Warzynski
Date: 2022-04-25T12:00:23Z
New Revision: 97a32d3e43fec35ea424e77c2940ee7ffffe801a

URL: https://github.com/llvm/llvm-project/commit/97a32d3e43fec35ea424e77c2940ee7ffffe801a
DIFF: https://github.com/llvm/llvm-project/commit/97a32d3e43fec35ea424e77c2940ee7ffffe801a.diff

LOG: [flang][driver] Add support for generating executables

This patch adds 2 missing items required for `flang-new` to be able to
generate executables:

1. The Fortran_main runtime library, which implements the main entry
   point into Fortran's `PROGRAM` in Flang,

2. Extra linker flags to include Fortran runtime libraries (e.g.

Fortran_main is the bridge between object files generated by Flang and
the C runtime that takes care of program set-up at system-level. For
every Fortran `PROGRAM`, Flang generates the `_QQmain` function.
Fortran_main implements the C `main` function that simply calls

Additionally, "<driver-path>/../lib" directory is added to the list of
search directories for libraries. This is where the required runtime
libraries are currently located. Note that this the case for the build
directory. We haven't considered installation directories/targets yet.

With this change, you can generate an executable that will print `hello,
world!` as follows:

$ cat hello.f95
  write(*, *) "hello, world!"
$ flang-new -flang-experimental-exec hello.f95
hello, world!

NOTE 1: Fortran_main has to be a static library at all times. It invokes
`_QQmain`, which is the main entry point generated by Flang for the
given input file (you can check this with `flang-new -S hello.f95 -o - |
grep "Qmain"`). This means that Fortran_main has an unresolved
dependency at build time. The linker will allow this for a static
library. However, if Fortran_main was a shared object, then the linker
will produce an error: `undefined symbol: `_QQmain`.

NOTE 2: When Fortran runtime libraries are generated as shared libraries
(excluding Fortran_main, which is always static), you will need to
tell the dynamic linker (by e.g. tweaking LD_LIBRARY_PATH) where to look
for them when invoking the executables. For example:
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:<flang-build-dir>/lib/ ./a.out

NOTE 3: This feature is considered experimental and currently guarded
with a flag: `-flang-experimental-exec`.

Differential Revision: https://reviews.llvm.org/D122008

[1] https://github.com/flang-compiler/f18-llvm-project

CREDITS: Fortran_main was originally written by Eric Schweitz, Jean
Perier, Peter Klausler and Steve Scalpone in the fir-dev` branch in [1].

Co-authored-by: Eric Schweitz <eschweitz at nvidia.com>
Co-authored-by: Peter Klausler <pklausler at nvidia.com>
Co-authored-by: Jean Perier <jperier at nvidia.com>
Co-authored-by: Steve Scalpone <sscalpone at nvidia.com




diff  --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 8e3611cff2d63..780e814bef396 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -4712,6 +4712,14 @@ def fsycl : Flag<["-"], "fsycl">, Flags<[NoXarchOption, CoreOption]>,
 def fno_sycl : Flag<["-"], "fno-sycl">, Flags<[NoXarchOption, CoreOption]>,
   Group<sycl_Group>, HelpText<"Disables SYCL kernels compilation for device">;
+// FLangOption + NoXarchOption
+def flang_experimental_exec : Flag<["-"], "flang-experimental-exec">,
+  Flags<[FlangOption, FlangOnlyOption, NoXarchOption, HelpHidden]>,
+  HelpText<"Enable support for generating executables (experimental)">;
 // FLangOption + CoreOption + NoXarchOption

diff  --git a/clang/lib/Driver/ToolChains/Gnu.cpp b/clang/lib/Driver/ToolChains/Gnu.cpp
index 5816b91994102..e0fcabc3e250a 100644
--- a/clang/lib/Driver/ToolChains/Gnu.cpp
+++ b/clang/lib/Driver/ToolChains/Gnu.cpp
@@ -382,6 +382,28 @@ void tools::gnutools::StaticLibTool::ConstructJob(
                                          Exec, CmdArgs, Inputs, Output));
+static void addFortranRuntimeLibraryPath(const ToolChain &TC,
+                                         const ArgList &Args,
+                                         ArgStringList &CmdArgs) {
+  // Default to the <driver-path>/../lib directory. This works fine on the
+  // platforms that we have tested so far. We will probably have to re-fine
+  // this in the future. In particular:
+  //    * on some platforms, we may need to use lib64 instead of lib
+  //    * this logic should also work on other similar platforms too, so we
+  //    should move it to one of Gnu's parent tool{chain} classes
+  SmallString<256> DefaultLibPath =
+      llvm::sys::path::parent_path(TC.getDriver().Dir);
+  llvm::sys::path::append(DefaultLibPath, "lib");
+  CmdArgs.push_back(Args.MakeArgString("-L" + DefaultLibPath));
+static void addFortranLinkerFlags(ArgStringList &CmdArgs) {
+  CmdArgs.push_back("-lFortran_main");
+  CmdArgs.push_back("-lFortranRuntime");
+  CmdArgs.push_back("-lFortranDecimal");
+  CmdArgs.push_back("-lm");
 void tools::gnutools::Linker::ConstructJob(Compilation &C, const JobAction &JA,
                                            const InputInfo &Output,
                                            const InputInfoList &Inputs,
@@ -586,6 +608,19 @@ void tools::gnutools::Linker::ConstructJob(Compilation &C, const JobAction &JA,
   // Silence warnings when linking C code with a C++ '-stdlib' argument.
+  // Additional linker set-up and flags for Fortran. This is required in order
+  // to generate executables. As Fortran runtime depends on the C runtime,
+  // these dependencies need to be listed before the C runtime below (i.e.
+  // AddRuntTimeLibs).
+  //
+  // NOTE: Generating executables by Flang is considered an "experimental"
+  // feature and hence this is guarded with a command line option.
+  // TODO: Make this work unconditionally once Flang is mature enough.
+  if (D.IsFlangMode() && Args.hasArg(options::OPT_flang_experimental_exec)) {
+    addFortranRuntimeLibraryPath(ToolChain, Args, CmdArgs);
+    addFortranLinkerFlags(CmdArgs);
+  }
   if (!Args.hasArg(options::OPT_nostdlib, options::OPT_r)) {
     if (!Args.hasArg(options::OPT_nodefaultlibs)) {
       if (IsStatic || IsStaticPIE)

diff  --git a/flang/include/flang/Runtime/stop.h b/flang/include/flang/Runtime/stop.h
index cf5ee7bcf4095..f7c4ffe7403e8 100644
--- a/flang/include/flang/Runtime/stop.h
+++ b/flang/include/flang/Runtime/stop.h
@@ -27,7 +27,7 @@ NORETURN void RTNAME(FailImageStatement)(NO_ARGUMENTS);
 NORETURN void RTNAME(ProgramEndStatement)(NO_ARGUMENTS);
 // Extensions
-NORETURN void RTNAME(Exit)(int status = EXIT_SUCCESS);
 // Crash with an error message when the program dynamically violates a Fortran

diff  --git a/flang/runtime/CMakeLists.txt b/flang/runtime/CMakeLists.txt
index 62f251f7dbbb4..ce8a615642341 100644
--- a/flang/runtime/CMakeLists.txt
+++ b/flang/runtime/CMakeLists.txt
@@ -30,6 +30,8 @@ configure_file(config.h.cmake config.h)
 # with 
diff erent names
 include_directories(AFTER ${CMAKE_CURRENT_BINARY_DIR})

diff  --git a/flang/runtime/FortranMain/CMakeLists.txt b/flang/runtime/FortranMain/CMakeLists.txt
new file mode 100644
index 0000000000000..aa214cee31ff7
--- /dev/null
+++ b/flang/runtime/FortranMain/CMakeLists.txt
@@ -0,0 +1,3 @@
+llvm_add_library(Fortran_main STATIC
+  Fortran_main.c

diff  --git a/flang/runtime/FortranMain/Fortran_main.c b/flang/runtime/FortranMain/Fortran_main.c
new file mode 100644
index 0000000000000..6a095ee5411d4
--- /dev/null
+++ b/flang/runtime/FortranMain/Fortran_main.c
@@ -0,0 +1,21 @@
+//===-- runtime/FortranMain/Fortran_main.c --------------------------------===//
+// 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 "flang/Runtime/main.h"
+#include "flang/Runtime/stop.h"
+/* main entry into PROGRAM */
+void _QQmain();
+/* C main stub */
+int main(int argc, const char *argv[], const char *envp[]) {
+  RTNAME(ProgramStart)(argc, argv, envp);
+  _QQmain();
+  RTNAME(ProgramEndStatement)();
+  return 0;

diff  --git a/flang/test/CMakeLists.txt b/flang/test/CMakeLists.txt
index 850b545db748e..d8dca531d9398 100644
--- a/flang/test/CMakeLists.txt
+++ b/flang/test/CMakeLists.txt
@@ -58,6 +58,9 @@ set(FLANG_TEST_DEPENDS
+  FortranRuntime
+  Fortran_main
+  FortranDecimal

diff  --git a/flang/test/Driver/driver-help-hidden.f90 b/flang/test/Driver/driver-help-hidden.f90
index 6b38d0a87ceaa..5bc044deffcec 100644
--- a/flang/test/Driver/driver-help-hidden.f90
+++ b/flang/test/Driver/driver-help-hidden.f90
@@ -38,6 +38,8 @@
 ! CHECK-NEXT: -finput-charset=<value> Specify the default character set for source files
 ! CHECK-NEXT: -fintrinsic-modules-path <dir>
 ! CHECK-NEXT:                        Specify where to find the compiled intrinsic modules
+! CHECK-NEXT: -flang-experimental-exec
+! CHECK-NEXT:                        Enable support for generating executables (experimental)
 ! CHECK-NEXT: -flarge-sizes          Use INTEGER(KIND=8) for the result type in size-related intrinsics
 ! CHECK-NEXT: -flogical-abbreviations Enable logical abbreviations
 ! CHECK-NEXT: -fno-automatic         Implies the SAVE attribute for non-automatic local objects in subprograms unless RECURSIVE

diff  --git a/flang/test/Driver/linker-flags.f90 b/flang/test/Driver/linker-flags.f90
new file mode 100644
index 0000000000000..66257452e84ab
--- /dev/null
+++ b/flang/test/Driver/linker-flags.f90
@@ -0,0 +1,31 @@
+! Verify that the Fortran runtime libraries are present in the linker
+! invocation. These libraries are added on top of other standard runtime
+! libraries that the Clang driver will include.
+! NOTE: The additional linker flags tested here are currently specified in
+! clang/lib/Driver/Toolchains/Gnu.cpp. This makes the current implementation GNU
+! (Linux) specific. The following line will make sure that this test is skipped
+! on Windows. Ideally we should find a more robust way of testing this.
+! REQUIRES: shell
+! UNSUPPORTED: darwin, macos, system-windows
+! Use `--ld-path` so that the linker location (used in the LABEL below) is deterministic.
+! RUN: %flang -### -flang-experimental-exec --ld-path=/usr/bin/ld %S/Inputs/hello.f90 2>&1 | FileCheck %s
+! Compiler invocation to generate the object file
+! CHECK-LABEL: {{.*}} "-emit-obj"
+! CHECK-SAME:  "-o" "[[object_file:.*]]" {{.*}}Inputs/hello.f90
+! Linker invocation to generate the executable
+! CHECK-LABEL:  "/usr/bin/ld"
+! CHECK-SAME: "[[object_file]]"
+! CHECK-SAME: -lFortran_main
+! CHECK-SAME: -lFortranRuntime
+! CHECK-SAME: -lFortranDecimal
+! CHECK-SAME: -lm

diff  --git a/flang/tools/flang-driver/CMakeLists.txt b/flang/tools/flang-driver/CMakeLists.txt
index b3e90746e786d..94c8ce6d58f19 100644
--- a/flang/tools/flang-driver/CMakeLists.txt
+++ b/flang/tools/flang-driver/CMakeLists.txt
@@ -13,6 +13,14 @@ set( LLVM_LINK_COMPONENTS
+  # These libraries are used in the linker invocation generated by the driver
+  # (i.e. when constructing the linker job). Without them the driver would be
+  # unable to generate executables.
+  FortranRuntime
+  FortranDecimal
+  Fortran_main


More information about the cfe-commits mailing list