Skip to content

Commit

Permalink
[flang][driver] Add support for generating executables
Browse files Browse the repository at this point in the history
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@nvidia.com>
Co-authored-by: Peter Klausler <pklausler@nvidia.com>
Co-authored-by: Jean Perier <jperier@nvidia.com>
Co-authored-by: Steve Scalpone <sscalpone@nvidia.com
  • Loading branch information
4 people committed Apr 25, 2022
1 parent 2fee8dd commit 97a32d3
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 1 deletion.
8 changes: 8 additions & 0 deletions clang/include/clang/Driver/Options.td
Expand Up @@ -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
//===----------------------------------------------------------------------===//
Expand Down
35 changes: 35 additions & 0 deletions clang/lib/Driver/ToolChains/Gnu.cpp
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion flang/include/flang/Runtime/stop.h
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions flang/runtime/CMakeLists.txt
Expand Up @@ -30,6 +30,8 @@ configure_file(config.h.cmake config.h)
# with different names
include_directories(AFTER ${CMAKE_CURRENT_BINARY_DIR})

add_subdirectory(FortranMain)

add_flang_library(FortranRuntime
ISO_Fortran_binding.cpp
allocatable.cpp
Expand Down
3 changes: 3 additions & 0 deletions flang/runtime/FortranMain/CMakeLists.txt
@@ -0,0 +1,3 @@
llvm_add_library(Fortran_main STATIC
Fortran_main.c
)
21 changes: 21 additions & 0 deletions 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;
}
3 changes: 3 additions & 0 deletions flang/test/CMakeLists.txt
Expand Up @@ -58,6 +58,9 @@ set(FLANG_TEST_DEPENDS
llvm-dis
llvm-objdump
split-file
FortranRuntime
Fortran_main
FortranDecimal
)

if (FLANG_INCLUDE_TESTS)
Expand Down
2 changes: 2 additions & 0 deletions flang/test/Driver/driver-help-hidden.f90
Expand Up @@ -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
Expand Down
31 changes: 31 additions & 0 deletions 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
8 changes: 8 additions & 0 deletions flang/tools/flang-driver/CMakeLists.txt
Expand Up @@ -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
Expand Down

0 comments on commit 97a32d3

Please sign in to comment.