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

Michael Klemm via cfe-commits cfe-commits at lists.llvm.org
Mon Dec 25 08:36:02 PST 2023


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

>From b3fa63a02f63e6fe0dacc25d584eba2012a5a8c5 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/7] 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 6eb0ed8f3fed9a..939a719c7c7e29 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 a480e16ac94451c635fc6d47df727c47ff3f1b21 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/7] Update dynamic_linker.f90 test and clean up a bit

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

diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index 939a719c7c7e29..3b29e1bc75850f 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 1cbd407d21ce09..069b51870c91ff 100644
--- a/flang/test/Driver/dynamic-linker.f90
+++ b/flang/test/Driver/dynamic-linker.f90
@@ -3,10 +3,12 @@
 
 ! 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:     --implicit-check-not=MSVC-LINKER-OPTIONS-NOT %s
 ! RUN: %flang -### --target=aarch64-linux-none -rdynamic %s 2>&1 | FileCheck --check-prefixes=RDYNAMIC-LINKER-OPTION %s
 
 ! TODO: Could the linker have an extension or a suffix?
@@ -14,6 +16,7 @@
 ! 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"
 
 ! RDYNAMIC-LINKER-OPTION: "{{.*}}ld"
 ! RDYNAMIC-LINKER-OPTION-SAME: "-export-dynamic"
@@ -22,3 +25,4 @@
 ! 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 bdd5d9b13655f2691fe90a862c09f2bf05f0ca26 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/7] 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 a2e41cafe2c5c9a66a335bf19cdb8e34d470c69f 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/7] 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 c9a9bb1e873aa613071b31abd2a36423e727d6d3 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/7] 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 b31e9908ea04176c0a2ebd99999d0418929eb381 Mon Sep 17 00:00:00 2001
From: Michael Klemm <michael.klemm at amd.com>
Date: Sat, 23 Dec 2023 09:59:36 +0100
Subject: [PATCH 6/7] Apply suggested edit by @kkwli

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

diff --git a/flang/docs/FlangDriver.md b/flang/docs/FlangDriver.md
index cb78f2fbd16572..9a30c78790fb36 100644
--- a/flang/docs/FlangDriver.md
+++ b/flang/docs/FlangDriver.md
@@ -213,11 +213,12 @@ and use the `-fno-fortran-main` flag.  This flag removes
 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
-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.
+When creating shared or static libraries using Flang with -shared or -static
+flag, Fortran_main is automatically removed from the linker stage (i.e.,
+`-fno-fortran-main` is on by default).  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

>From b29a7fdb5f69e400862fca0c717e080d5f7bffe2 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 7/7] Incorporate change by @kkwli

Co-authored-by: kkwli <kkwli at users.noreply.github.com>
---
 flang/docs/FlangDriver.md            | 2 +-
 flang/test/Driver/dynamic-linker.f90 | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/flang/docs/FlangDriver.md b/flang/docs/FlangDriver.md
index 9a30c78790fb36..27383974920861 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
diff --git a/flang/test/Driver/dynamic-linker.f90 b/flang/test/Driver/dynamic-linker.f90
index 069b51870c91ff..7c3f1b5a53fe48 100644
--- a/flang/test/Driver/dynamic-linker.f90
+++ b/flang/test/Driver/dynamic-linker.f90
@@ -7,7 +7,7 @@
 ! 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
 ! RUN: %flang -### --target=aarch64-linux-none -rdynamic %s 2>&1 | FileCheck --check-prefixes=RDYNAMIC-LINKER-OPTION %s
 



More information about the cfe-commits mailing list