[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).

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
`_QQmain`.

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:

```bash
$ cat hello.f95
PROGRAM HELLO
  write(*, *) "hello, world!"
END PROGRAM HELLO
$ flang-new -flang-experimental-exec hello.f95
./a.out
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:
```bash
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

Added: 
    flang/runtime/FortranMain/CMakeLists.txt
    flang/runtime/FortranMain/Fortran_main.c
    flang/test/Driver/linker-flags.f90

Modified: 
    clang/include/clang/Driver/Options.td
    clang/lib/Driver/ToolChains/Gnu.cpp
    flang/include/flang/Runtime/stop.h
    flang/runtime/CMakeLists.txt
    flang/test/CMakeLists.txt
    flang/test/Driver/driver-help-hidden.f90
    flang/tools/flang-driver/CMakeLists.txt

Removed: 
    


################################################################################
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.
   Args.ClaimAllArgs(options::OPT_stdlib_EQ);
 
+  // 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);
+NORETURN void RTNAME(Exit)(int status DEFAULT_VALUE(EXIT_SUCCESS));
 NORETURN void RTNAME(Abort)(NO_ARGUMENTS);
 
 // 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})
 
+add_subdirectory(FortranMain)
+
 add_flang_library(FortranRuntime
   ISO_Fortran_binding.cpp
   allocatable.cpp

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
   llvm-dis
   llvm-objdump
   split-file
+  FortranRuntime
+  Fortran_main
+  FortranDecimal
 )
 
 if (FLANG_INCLUDE_TESTS)

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
+
+!------------
+! RUN COMMAND
+!------------
+! 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
+
+!----------------
+! EXPECTED OUTPUT
+!----------------
+! 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
 add_flang_tool(flang-new
   driver.cpp
   fc1_main.cpp
+
+  DEPENDS
+  # 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
 )
 
 target_link_libraries(flang-new


        


More information about the cfe-commits mailing list