From 97a32d3e43fec35ea424e77c2940ee7ffffe801a Mon Sep 17 00:00:00 2001 From: Andrzej Warzynski Date: Thu, 17 Mar 2022 16:24:54 +0000 Subject: [PATCH] [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, "/../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:/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 Co-authored-by: Peter Klausler Co-authored-by: Jean Perier Co-authored-by: Steve Scalpone , Flags<[NoXarchOption, CoreOption]>, 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 5816b91..e0fcabc 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 /../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 cf5ee7b..f7c4ffe 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 62f251f..ce8a615 100644 --- a/flang/runtime/CMakeLists.txt +++ b/flang/runtime/CMakeLists.txt @@ -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 diff --git a/flang/runtime/FortranMain/CMakeLists.txt b/flang/runtime/FortranMain/CMakeLists.txt new file mode 100644 index 0000000..aa214ce --- /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 0000000..6a095ee --- /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 850b545..d8dca53 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 6b38d0a..5bc044d 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= Specify the default character set for source files ! CHECK-NEXT: -fintrinsic-modules-path ! 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 0000000..6625745 --- /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 b3e9074..94c8ce6 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 -- 2.7.4