[flang-commits] [flang] [clang] [flang][driver] Remove Fortain_main static library from linking stages (PR #75816)

Michael Klemm via flang-commits flang-commits at lists.llvm.org
Sat Dec 23 00:56:14 PST 2023


https://github.com/mjklemm updated https://github.com/llvm/llvm-project/pull/75816

>From 511f3a4537267284554bf6b33470a01d747b8a94 Mon Sep 17 00:00:00 2001
From: Michael Klemm <michael.klemm at amd.com>
Date: Sat, 16 Dec 2023 20:15:17 +0100
Subject: [PATCH 1/6] Remove -lFortran_main from the link line when -shared is
 present

---
 clang/lib/Driver/ToolChains/CommonArgs.cpp | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index 45901ee7157f77..5d525d3794ad1d 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -1133,6 +1133,17 @@ static bool isWholeArchivePresent(const ArgList &Args) {
   return WholeArchiveActive;
 }
 
+static bool isSharedLinkage(const ArgList &Args) {
+  bool FoundSharedFlag = false;
+  for (auto *Arg : Args.filtered(options::OPT_shared)) {
+    if (Arg) {
+      FoundSharedFlag = true;
+    }
+  }
+
+  return FoundSharedFlag;
+}
+
 /// Add Fortran runtime libs for MSVC
 static void addFortranRuntimeLibsMSVC(const ArgList &Args,
                                       llvm::opt::ArgStringList &CmdArgs) {
@@ -1164,6 +1175,17 @@ static void addFortranRuntimeLibsMSVC(const ArgList &Args,
 // Add FortranMain runtime lib
 static void addFortranMain(const ToolChain &TC, const ArgList &Args,
                            llvm::opt::ArgStringList &CmdArgs) {
+  // 0. Shared-library linkage
+  // If we are attempting to link a shared library, we should not add
+  // -lFortran_main.a to the link line, as the `main` symbol is not
+  // required for a shared library and should also be provided by one
+  // of the translation units of the code that this shared library
+  // will be linked against eventually.
+  if (isSharedLinkage(Args)) {
+    printf("MK: --> shared linkage, do not add -lFortranMain\n");
+    return;
+  }
+
   // 1. MSVC
   if (TC.getTriple().isKnownWindowsMSVCEnvironment()) {
     addFortranRuntimeLibsMSVC(Args, CmdArgs);

>From 930f2c447daa625d9e6019cd38d82b5750942f5d Mon Sep 17 00:00:00 2001
From: Michael Klemm <michael.klemm at amd.com>
Date: Mon, 18 Dec 2023 11:27:59 +0100
Subject: [PATCH 2/6] Update dynamic_linker.f90 test and clean up a bit

---
 clang/lib/Driver/ToolChains/CommonArgs.cpp | 22 ++++++++++------------
 flang/test/Driver/dynamic-linker.f90       |  8 ++++++--
 2 files changed, 16 insertions(+), 14 deletions(-)

diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index 5d525d3794ad1d..05ebd42829c95d 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -1133,15 +1133,14 @@ static bool isWholeArchivePresent(const ArgList &Args) {
   return WholeArchiveActive;
 }
 
+/// Determine if driver is invoked to create a shared object library (-static)
 static bool isSharedLinkage(const ArgList &Args) {
-  bool FoundSharedFlag = false;
-  for (auto *Arg : Args.filtered(options::OPT_shared)) {
-    if (Arg) {
-      FoundSharedFlag = true;
-    }
-  }
+  return Args.hasArg(options::OPT_shared);
+}
 
-  return FoundSharedFlag;
+/// Determine if driver is invoked to create a static object library (-shared)
+static bool isStaticLinkage(const ArgList &Args) {
+  return Args.hasArg(options::OPT_static);
 }
 
 /// Add Fortran runtime libs for MSVC
@@ -1176,13 +1175,12 @@ static void addFortranRuntimeLibsMSVC(const ArgList &Args,
 static void addFortranMain(const ToolChain &TC, const ArgList &Args,
                            llvm::opt::ArgStringList &CmdArgs) {
   // 0. Shared-library linkage
-  // If we are attempting to link a shared library, we should not add
+  // If we are attempting to link a library, we should not add
   // -lFortran_main.a to the link line, as the `main` symbol is not
-  // required for a shared library and should also be provided by one
-  // of the translation units of the code that this shared library
+  // required for a library and should also be provided by one of
+  // the translation units of the code that this shared library
   // will be linked against eventually.
-  if (isSharedLinkage(Args)) {
-    printf("MK: --> shared linkage, do not add -lFortranMain\n");
+  if (isSharedLinkage(Args) || isStaticLinkage(Args)) {
     return;
   }
 
diff --git a/flang/test/Driver/dynamic-linker.f90 b/flang/test/Driver/dynamic-linker.f90
index df119c22a2ea51..af07e2483f93fa 100644
--- a/flang/test/Driver/dynamic-linker.f90
+++ b/flang/test/Driver/dynamic-linker.f90
@@ -3,18 +3,22 @@
 
 ! RUN: %flang -### --target=x86_64-linux-gnu -rpath /path/to/dir -shared \
 ! RUN:     -static %s 2>&1 | FileCheck \
-! RUN:     --check-prefixes=GNU-LINKER-OPTIONS %s
+! RUN:     --check-prefixes=GNU-LINKER-OPTIONS \
+! RUN:     --implicit-check-not=GNU-LINKER-OPTIONS-NOT %s
 ! RUN: %flang -### --target=x86_64-windows-msvc -rpath /path/to/dir -shared \
 ! RUN:     -static %s 2>&1 | FileCheck \
-! RUN:     --check-prefixes=MSVC-LINKER-OPTIONS %s
+! RUN:     --check-prefixes=MSVC-LINKER-OPTIONS \
+! RUN:     --implicit-check-not=MSVC-LINKER-OPTIONS-NOT %s
 
 ! TODO: Could the linker have an extension or a suffix?
 ! GNU-LINKER-OPTIONS: "{{.*}}ld{{(.exe)?}}"
 ! GNU-LINKER-OPTIONS-SAME: "-shared"
 ! GNU-LINKER-OPTIONS-SAME: "-static"
 ! GNU-LINKER-OPTIONS-SAME: "-rpath" "/path/to/dir"
+! GNU-LINKER-OPTIONS-NOT: "-lFortran_main.a"
 
 ! For MSVC, adding -static does not add any additional linker options.
 ! MSVC-LINKER-OPTIONS: "{{.*}}link{{(.exe)?}}"
 ! MSVC-LINKER-OPTIONS-SAME: "-dll"
 ! MSVC-LINKER-OPTIONS-SAME: "-rpath" "/path/to/dir"
+! MSVC-LINKER-OPTIONS-NOT: "/WHOLEARCHIVE:Fortran_main"

>From c60a3a7dbd038d9e1cac63cda00674462828c5c6 Mon Sep 17 00:00:00 2001
From: Michael Klemm <michael.klemm at amd.com>
Date: Tue, 19 Dec 2023 15:07:14 +0100
Subject: [PATCH 3/6] Write up for Flang creates the link line and the thoughts
 behind that

---
 flang/docs/FlangDriver.md | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/flang/docs/FlangDriver.md b/flang/docs/FlangDriver.md
index 5231e78335f6ad..bdb3c46455601c 100644
--- a/flang/docs/FlangDriver.md
+++ b/flang/docs/FlangDriver.md
@@ -163,6 +163,40 @@ forward compiler options to the frontend driver, `flang-new -fc1`.
 You can read more on the design of `clangDriver` in Clang's [Driver Design &
 Internals](https://clang.llvm.org/docs/DriverInternals.html).
 
+## Linker Driver
+When used as a linker, Flang's frontend driver assembles the command line for an
+external linker command (e.g., LLVM's `lld`) and invokes it to create the final
+executable by linking static and shared libraries together with all the
+translation units supplied as object files.
+
+By default, the Flang linker driver adds several libraries to the linker
+invocation to make sure that all entrypoints for program start
+(Fortran's program unit) and runtime routines can be resolved by the linker.
+The libraries are:
+
+* `Fortran_main`: Provides the main entry point `main` that then invokes
+  `_QQmain` with the Fortran program unit.  This library has a dependency to
+  the `FortranRuntime` library.
+* `FortranRuntime`: Provides most of the Flang runtime library.
+* `FortranDecimal`: Provides operations for decimal numbers.
+
+The default is that, when using Flang as the linker, one of the Fortran
+translation units provides the program unit and therefore it is assumed that
+Fortran is the main code part (calling into C/C++ routines via `BIND
+(C)` interfaces).  When composing the linker commandline, Flang uses
+`--whole-archive` and `--no-whole-archive` (Windows: `/WHOLEARCHIVE:`,
+Darwin: *not implemented yet*) to make sure that all for `Fortran_main` is
+processed by the linker.  This is done to issue a proper error message when
+multiple definitions of `main` occur.  This happens, for instance, when linking
+a code that has a Fortran program unit with a C/C++ code that also defines a
+`main` function.
+
+If the code is C/C++ based and invokes Fortran routines, either use Clang as the
+linker driver (supplying `FortranRuntime` and/or `FortranDecimal` to the linker
+driver) or use Flang with the `-fno-fortran-main` flag.  This flag removes
+`Fortran_main` from the linker stage and hence requires one of the C/C++
+translation units to provide a definition of the `main` function.
+
 ## Frontend Driver
 Flang's frontend driver is the main interface between compiler developers and
 the Flang frontend. The high-level design is similar to Clang's frontend

>From 266687414fd78d8a474d7851cae1009185b68881 Mon Sep 17 00:00:00 2001
From: Michael Klemm <michael.klemm at amd.com>
Date: Tue, 19 Dec 2023 15:16:54 +0100
Subject: [PATCH 4/6] Make small addition about static and shared libraries

---
 flang/docs/FlangDriver.md | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/flang/docs/FlangDriver.md b/flang/docs/FlangDriver.md
index bdb3c46455601c..0f9a571f8e0d61 100644
--- a/flang/docs/FlangDriver.md
+++ b/flang/docs/FlangDriver.md
@@ -197,6 +197,12 @@ driver) or use Flang with the `-fno-fortran-main` flag.  This flag removes
 `Fortran_main` from the linker stage and hence requires one of the C/C++
 translation units to provide a definition of the `main` function.
 
+When creating shared or static libraries, `Fortran_main` is automatically
+removed from the linker stage.  It is assumed that when creating a static or
+shared library, the generated library does not need a `main` function, as a
+final link stage will occur that will provide the `Fortran_main` library when
+creating the final executable.
+
 ## Frontend Driver
 Flang's frontend driver is the main interface between compiler developers and
 the Flang frontend. The high-level design is similar to Clang's frontend

>From 317316eade79f209b139aa42cb65b790fcd13c60 Mon Sep 17 00:00:00 2001
From: Michael Klemm <michael.klemm at amd.com>
Date: Fri, 22 Dec 2023 10:21:05 +0100
Subject: [PATCH 5/6] Try to improve the wording a bit

---
 flang/docs/FlangDriver.md | 38 +++++++++++++++++++++++++++-----------
 1 file changed, 27 insertions(+), 11 deletions(-)

diff --git a/flang/docs/FlangDriver.md b/flang/docs/FlangDriver.md
index 0f9a571f8e0d61..cb78f2fbd16572 100644
--- a/flang/docs/FlangDriver.md
+++ b/flang/docs/FlangDriver.md
@@ -172,7 +172,18 @@ translation units supplied as object files.
 By default, the Flang linker driver adds several libraries to the linker
 invocation to make sure that all entrypoints for program start
 (Fortran's program unit) and runtime routines can be resolved by the linker.
-The libraries are:
+
+An abridged example (only showing the Fortran specific linker flags, omission
+indicated by `[...]`) for such a linker invocation on a Linux system would look
+like this:
+
+```
+$ flang -v -o example example.o
+"/usr/bin/ld" [...] example.o [...] "--whole-archive" "-lFortran_main"
+"--no-whole-archive" "-lFortranRuntime" "-lFortranDecimal" [...]
+```
+
+The automatically added libraries are:
 
 * `Fortran_main`: Provides the main entry point `main` that then invokes
   `_QQmain` with the Fortran program unit.  This library has a dependency to
@@ -182,20 +193,25 @@ The libraries are:
 
 The default is that, when using Flang as the linker, one of the Fortran
 translation units provides the program unit and therefore it is assumed that
-Fortran is the main code part (calling into C/C++ routines via `BIND
-(C)` interfaces).  When composing the linker commandline, Flang uses
+Fortran is the main code part (calling into C/C++ routines via `BIND (C)`
+interfaces).  When composing the linker commandline, Flang uses
 `--whole-archive` and `--no-whole-archive` (Windows: `/WHOLEARCHIVE:`,
-Darwin: *not implemented yet*) to make sure that all for `Fortran_main` is
-processed by the linker.  This is done to issue a proper error message when
+Darwin & AIX: *not implemented yet*) to make sure that all for `Fortran_main`
+is processed by the linker.  This is done to issue a proper error message when
 multiple definitions of `main` occur.  This happens, for instance, when linking
 a code that has a Fortran program unit with a C/C++ code that also defines a
-`main` function.
-
-If the code is C/C++ based and invokes Fortran routines, either use Clang as the
-linker driver (supplying `FortranRuntime` and/or `FortranDecimal` to the linker
-driver) or use Flang with the `-fno-fortran-main` flag.  This flag removes
+`main` function.  A user may be required to explicitly provide the C++ runtime
+libraries at link time (e.g., via `-lstdc++` for STL)
+
+If the code is C/C++ based and invokes Fortran routines, one can either use Clang
+for Flang as the linker driver.  If Clang is used, it will automatically all
+required runtime libraries needed by C++ (e.g., for STL) to the linker invocation.
+In this case, one has to explicitly provide the Fortran runtime libraries
+`FortranRuntime` and/or `FortranDecimal`.  An alternative is to use Flang to link
+and use the `-fno-fortran-main` flag.  This flag removes
 `Fortran_main` from the linker stage and hence requires one of the C/C++
-translation units to provide a definition of the `main` function.
+translation units to provide a definition of the `main` function. In this case,
+it may be required to explicitly supply C++ runtime libraries as mentioned above.
 
 When creating shared or static libraries, `Fortran_main` is automatically
 removed from the linker stage.  It is assumed that when creating a static or

>From 64378894cebdeaa7572e3ecad970693374976a9a Mon Sep 17 00:00:00 2001
From: Michael Klemm <michael.klemm at amd.com>
Date: Sat, 23 Dec 2023 09:56:05 +0100
Subject: [PATCH 6/6] Incorporate change by @kkwli

Co-authored-by: kkwli <kkwli at users.noreply.github.com>
---
 flang/docs/FlangDriver.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/flang/docs/FlangDriver.md b/flang/docs/FlangDriver.md
index cb78f2fbd16572..1d2332d91f3e99 100644
--- a/flang/docs/FlangDriver.md
+++ b/flang/docs/FlangDriver.md
@@ -204,7 +204,7 @@ a code that has a Fortran program unit with a C/C++ code that also defines a
 libraries at link time (e.g., via `-lstdc++` for STL)
 
 If the code is C/C++ based and invokes Fortran routines, one can either use Clang
-for Flang as the linker driver.  If Clang is used, it will automatically all
+or Flang as the linker driver.  If Clang is used, it will automatically all
 required runtime libraries needed by C++ (e.g., for STL) to the linker invocation.
 In this case, one has to explicitly provide the Fortran runtime libraries
 `FortranRuntime` and/or `FortranDecimal`.  An alternative is to use Flang to link



More information about the flang-commits mailing list