[clang] 876df74 - [flang][driver] Allow main program to be in an archive
Shao-Ce SUN via cfe-commits
cfe-commits at lists.llvm.org
Thu Apr 27 18:53:35 PDT 2023
Author: Shao-Ce SUN
Date: 2023-04-28T09:53:25+08:00
New Revision: 876df74dd47196a9ca3b4fff21ffb5441491a0a0
URL: https://github.com/llvm/llvm-project/commit/876df74dd47196a9ca3b4fff21ffb5441491a0a0
DIFF: https://github.com/llvm/llvm-project/commit/876df74dd47196a9ca3b4fff21ffb5441491a0a0.diff
LOG: [flang][driver] Allow main program to be in an archive
Add --undefined=_QQmain to the link line, so that a Fortran main program
will be included in the link job even if it is in an archive (unless we
are building a shared object). For now, this is only applied to the Gnu
toolchain.
We also add a section on the linker invocation to docs/FlangDriver.md.
The new tests require llvm-ar to construct an archive we can include in
the link job. This is a new dependency for flang/test (which already
depends on similar tools such as llvm-objdump).
See discussions in
https://github.com/llvm/llvm-project/issues/54787
which this patch fixes.
Reviewed By: awarzynski
Differential Revision: https://reviews.llvm.org/D134821
Added:
flang/test/Driver/link-c-main.c
flang/test/Driver/link-f90-main.f90
Modified:
clang/lib/Driver/ToolChains/Gnu.cpp
flang/docs/FlangDriver.md
flang/test/CMakeLists.txt
flang/test/Driver/linker-flags.f90
Removed:
################################################################################
diff --git a/clang/lib/Driver/ToolChains/Gnu.cpp b/clang/lib/Driver/ToolChains/Gnu.cpp
index b0716322bc141..c4a276126b653 100644
--- a/clang/lib/Driver/ToolChains/Gnu.cpp
+++ b/clang/lib/Driver/ToolChains/Gnu.cpp
@@ -598,6 +598,14 @@ void tools::gnutools::Linker::ConstructJob(Compilation &C, const JobAction &JA,
// these dependencies need to be listed before the C runtime below (i.e.
// AddRuntTimeLibs).
if (D.IsFlangMode()) {
+ // A Fortran main program will be lowered to a function named _QQmain. Make
+ // _QQmain an undefined symbol, so that it's correctly resolved even when
+ // creating executable from archives. This is a workaround for how and where
+ // Flang's `main` is defined. For more context, see:
+ // * https://github.com/llvm/llvm-project/issues/54787
+ if (!Args.hasArg(options::OPT_shared))
+ CmdArgs.push_back("--undefined=_QQmain");
+
addFortranRuntimeLibraryPath(ToolChain, Args, CmdArgs);
addFortranRuntimeLibs(ToolChain, CmdArgs);
CmdArgs.push_back("-lm");
diff --git a/flang/docs/FlangDriver.md b/flang/docs/FlangDriver.md
index 6c2a473820634..9522e223b17b5 100644
--- a/flang/docs/FlangDriver.md
+++ b/flang/docs/FlangDriver.md
@@ -149,13 +149,6 @@ flang-new -ccc-print-phases -c file.f
+- 3: backend, {2}, assembler
4: assembler, {3}, object
```
-Note that currently Flang does not support code-generation and `flang-new` will
-fail during the second step above with the following error:
-```bash
-error: code-generation is not available yet
-```
-The other phases are printed nonetheless when using `-ccc-print-phases`, as
-that reflects what `clangDriver`, the library, will try to create and run.
For actions specific to the frontend (e.g. preprocessing or code generation), a
command to call the frontend driver is generated (more specifically, an
@@ -205,6 +198,41 @@ is `ParseSyntaxOnlyAction`, which corresponds to `-fsyntax-only`. In other
words, `flang-new -fc1 <input-file>` is equivalent to `flang-new -fc1 -fsyntax-only
<input-file>`.
+## Linker invocation
+> **_NOTE:_** Linker invocation through the `flang-new` driver is so far
+> experimental. This section describes the currently intended design, and not
+> necessarily what is implemented.
+
+Calling
+```bash
+flang-new -flang-experimental-exec prog.f90
+```
+will, on a high level, do two things:
+* call the frontend driver, `flang-new -fc1`, to build the object file `prog.o`
+* call the system linker to build the executable `a.out`.
+
+In both invocations, `flang-new` will add default options to the frontend driver
+and the linker invocations. To see the exact invocations on your system, you can
+call
+```bash
+flang-new -### prog.f90
+```
+The link line will contain a fair number of options, which will depend on your
+system. Compared to when linking a C program with `clang`, the main additions
+are (on GNU/linux),
+* `--undefined=_QQmain`: A Fortran main program will appear in the corresponding
+ object file as a function called `_QQmain`. This flag makes sure that an
+ object file containing a Fortran main program (i.e., a symbol `_QQmain`) be
+ included in the linking also when it is bundled in an archive.
+* `-lFortran_main`: The Fortran_main archive is part of Flang's runtime. It
+ exports the symbol `main`, i.e., a c main function, which will make some
+ initial configuration before calling `_QQmain`, and clean up before returning.
+* `-lFortranRuntime`: Flang's Fortran runtime, containing, for example,
+ implementations of intrinsic functions.
+* `-lFortranDecimal`: Part of Flang's runtime, containing routines for parsing
+ and formatting decimal numbers.
+* `-lm`: Link with the math library, on which Flang's runtime depends.
+
## The `flang-to-external-fc` script
The `flang-to-external-fc` wrapper script for `flang-new` was introduced as a
development tool and to facilitate testing. The `flang-to-external-fc` wrapper
diff --git a/flang/test/CMakeLists.txt b/flang/test/CMakeLists.txt
index 7d96a72e5f36d..dbb02574ec183 100644
--- a/flang/test/CMakeLists.txt
+++ b/flang/test/CMakeLists.txt
@@ -57,6 +57,7 @@ set(FLANG_TEST_DEPENDS
fir-opt
tco
bbc
+ llvm-ar
llvm-dis
llvm-objdump
llvm-readobj
diff --git a/flang/test/Driver/link-c-main.c b/flang/test/Driver/link-c-main.c
new file mode 100644
index 0000000000000..e6814259a33d2
--- /dev/null
+++ b/flang/test/Driver/link-c-main.c
@@ -0,0 +1,28 @@
+/*
+Test that an object file with a C main function can be linked to an executable
+by Flang.
+
+For now, this test only covers the Gnu toolchain on Linux.
+
+REQUIRES: x86-registered-target || aarch64-registered-target || riscv64-registered-target
+REQUIRES: system-linux, c-compiler
+
+RUN: %cc -c %s -o %t.o
+RUN: %flang -target x86_64-unknown-linux-gnu %t.o -o %t.out -flang-experimental-exec
+RUN: llvm-objdump --syms %t.out | FileCheck %s --implicit-check-not Fortran
+
+Test that it also works if the c-main is bundled in an archive.
+
+RUN: llvm-ar -r %t.a %t.o
+RUN: %flang -target x86_64-unknown-linux-gnu %t.a -o %ta.out -flang-experimental-exec
+RUN: llvm-objdump --syms %ta.out | FileCheck %s --implicit-check-not Fortran
+*/
+
+int main(void) {
+ return 0;
+}
+
+/*
+CHECK-DAG: F .text {{[a-f0-9]+}} main
+CHECK-DAG: *UND* {{[a-f0-9]+}} _QQmain
+*/
diff --git a/flang/test/Driver/link-f90-main.f90 b/flang/test/Driver/link-f90-main.f90
new file mode 100644
index 0000000000000..01f77f40d0300
--- /dev/null
+++ b/flang/test/Driver/link-f90-main.f90
@@ -0,0 +1,23 @@
+! Test that a fortran main program can be linked to an executable
+! by flang.
+!
+! For now, this test only covers the Gnu toolchain on linux.
+
+!REQUIRES: x86-registered-target || aarch64-registered-target || riscv64-registered-target
+!REQUIRES: system-linux
+
+! RUN: %flang_fc1 -emit-obj %s -o %t.o
+! RUN: %flang -target x86_64-unknown-linux-gnu %t.o -o %t.out -flang-experimental-exec
+! RUN: llvm-objdump --syms %t.out | FileCheck %s
+
+! Test that it also works if the program is bundled in an archive.
+
+! RUN: llvm-ar -r %t.a %t.o
+! RUN: %flang -target x86_64-unknown-linux-gnu %t.a -o %ta.out -flang-experimental-exec
+! RUN: llvm-objdump --syms %ta.out | FileCheck %s
+
+end program
+
+! CHECK-DAG: F .text {{[a-f0-9]+}} main
+! CHECK-DAG: F .text {{[a-f0-9]+}} _QQmain
+! CHECK-DAG: _FortranAProgramStart
diff --git a/flang/test/Driver/linker-flags.f90 b/flang/test/Driver/linker-flags.f90
index e1350b74ae30a..f776d76129698 100644
--- a/flang/test/Driver/linker-flags.f90
+++ b/flang/test/Driver/linker-flags.f90
@@ -12,6 +12,14 @@
! Make sure they're not added.
! RUN: %flang -### -flang-experimental-exec -target aarch64-windows-msvc %S/Inputs/hello.f90 2>&1 | FileCheck %s --check-prefixes=CHECK,MSVC --implicit-check-not libcmt --implicit-check-not oldnames
+! Check linker invocation to generate shared object (only GNU toolchain for now)
+! Output should not contain any undefined reference to _QQmain since it is not
+! considered a valid entry point for shared objects, which are usually specified
+! using the bind attribute.
+! RUN: %flang -### -flang-experimental-exec -shared -target x86_64-linux-gnu %S/Inputs/hello.f90 2>&1 | FileCheck %s --check-prefixes=CHECK,GNU-SHARED --implicit-check-not _QQmain
+! RUN: %flang -### -flang-experimental-exec -shared -target aarch64-linux-gnu %S/Inputs/hello.f90 2>&1 | FileCheck %s --check-prefixes=CHECK,GNU-SHARED --implicit-check-not _QQmain
+! RUN: %flang -### -flang-experimental-exec -shared -target riscv64-linux-gnu %S/Inputs/hello.f90 2>&1 | FileCheck %s --check-prefixes=CHECK,GNU-SHARED --implicit-check-not _QQmain
+
! Compiler invocation to generate the object file
! CHECK-LABEL: {{.*}} "-emit-obj"
! CHECK-SAME: "-o" "[[object_file:.*\.o]]" {{.*}}Inputs/hello.f90
@@ -23,6 +31,7 @@
! executable and may find the GNU linker from MinGW or Cygwin.
! GNU-LABEL: "{{.*}}ld{{(\.exe)?}}"
! GNU-SAME: "[[object_file]]"
+! GNU-SAME: --undefined=_QQmain
! GNU-SAME: -lFortran_main
! GNU-SAME: -lFortranRuntime
! GNU-SAME: -lFortranDecimal
@@ -50,3 +59,9 @@
! MSVC-SAME: FortranDecimal.lib
! MSVC-SAME: /subsystem:console
! MSVC-SAME: "[[object_file]]"
+
+! Linker invocation to generate a shared object
+! GNU-SHARED-LABEL: "{{.*}}ld"
+! GNU-SHARED-SAME: "[[object_file]]"
+! GNU-SHARED-SAME: -lFortranRuntime
+! GNU-SHARED-SAME: -lFortranDecimal
More information about the cfe-commits
mailing list