[flang-commits] [clang] [flang] [flang][Driver] Enables lto-partitions and fat-lto-object. (PR #158125)

Anchu Rajendran S via flang-commits flang-commits at lists.llvm.org
Wed Sep 17 11:06:30 PDT 2025


https://github.com/anchuraj updated https://github.com/llvm/llvm-project/pull/158125

>From 78068dcea27ebdfd84743d69a8815f77b8bfcd3d Mon Sep 17 00:00:00 2001
From: Anchu Rajendran <asudhaku at amd.com>
Date: Tue, 2 Sep 2025 12:34:06 -0500
Subject: [PATCH 1/2] [flang][Driver] Enables lto-partitions and
 fat-lto-object. lto-partition helps in performing parallel lto and
 fat-lto-objects allows bit code to be embedded in object files generated.

---
 clang/include/clang/Driver/Options.td         |  5 +--
 clang/lib/Driver/ToolChains/Flang.cpp         | 36 ++++++++++++-------
 clang/lib/Driver/ToolChains/Flang.h           |  8 +++++
 .../include/flang/Frontend/CodeGenOptions.def |  1 +
 flang/lib/Frontend/CompilerInvocation.cpp     |  4 +++
 flang/lib/Frontend/FrontendActions.cpp        |  4 ++-
 flang/test/Driver/lto-fatlto.f90              | 20 +++++++++++
 flang/test/Driver/lto-lld-flags.f90           | 19 ++++++++++
 8 files changed, 82 insertions(+), 15 deletions(-)
 create mode 100644 flang/test/Driver/lto-fatlto.f90
 create mode 100644 flang/test/Driver/lto-lld-flags.f90

diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 902a28d60b349..1940f6fec9a40 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -3171,10 +3171,11 @@ def fthin_link_bitcode_EQ : Joined<["-"], "fthin-link-bitcode=">,
   MarshallingInfoString<CodeGenOpts<"ThinLinkBitcodeFile">>;
 defm fat_lto_objects : BoolFOption<"fat-lto-objects",
   CodeGenOpts<"FatLTO">, DefaultFalse,
-  PosFlag<SetTrue, [], [ClangOption, CC1Option], "Enable">,
-  NegFlag<SetFalse, [], [ClangOption, CC1Option], "Disable">,
+  PosFlag<SetTrue, [], [ClangOption, CC1Option, FlangOption, FC1Option], "Enable">,
+  NegFlag<SetFalse, [], [ClangOption, CC1Option, FlangOption, FC1Option], "Disable">,
   BothFlags<[], [ClangOption, CC1Option], " fat LTO object support">>;
 def flto_partitions_EQ : Joined<["-"], "flto-partitions=">, Group<f_Group>,
+  Visibility<[ClangOption, FlangOption]>,
   HelpText<"Number of partitions to use for parallel full LTO codegen, ld.lld only.">;
 def fmacro_backtrace_limit_EQ : Joined<["-"], "fmacro-backtrace-limit=">,
   Group<f_Group>, Visibility<[ClangOption, CC1Option, CLOption]>,
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp
index 1535f4cebf436..f2fef91f7d4f6 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -182,6 +182,29 @@ void Flang::addCodegenOptions(const ArgList &Args,
     CmdArgs.push_back("-fcoarray");
 }
 
+void Flang::addLTOOptions(const ArgList &Args, ArgStringList &CmdArgs) const {
+  const auto &TC = getToolChain();
+  const Driver &D = TC.getDriver();
+  DiagnosticsEngine &Diags = D.getDiags();
+  LTOKind LTOMode = D.getLTOMode();
+  // LTO mode is parsed by the Clang driver library.
+  assert(LTOMode != LTOK_Unknown && "Unknown LTO mode.");
+  if (LTOMode == LTOK_Full)
+    CmdArgs.push_back("-flto=full");
+  else if (LTOMode == LTOK_Thin) {
+    Diags.Report(
+        Diags.getCustomDiagID(DiagnosticsEngine::Warning,
+                              "the option '-flto=thin' is a work in progress"));
+    CmdArgs.push_back("-flto=thin");
+  }
+  if (Args.hasArg(options::OPT_flto_partitions_EQ)) {
+    StringRef A = Args.getLastArgValue(options::OPT_flto_partitions_EQ, "8");
+    CmdArgs.push_back(Args.MakeArgString("-flto-partitions=" + A));
+  }
+  Args.addAllArgs(CmdArgs, {options::OPT_ffat_lto_objects,
+                            options::OPT_fno_fat_lto_objects});
+}
+
 void Flang::addPicOptions(const ArgList &Args, ArgStringList &CmdArgs) const {
   // ParsePICArgs parses -fPIC/-fPIE and their variants and returns a tuple of
   // (RelocationModel, PICLevel, IsPIE).
@@ -821,7 +844,6 @@ void Flang::ConstructJob(Compilation &C, const JobAction &JA,
 
   const Driver &D = TC.getDriver();
   ArgStringList CmdArgs;
-  DiagnosticsEngine &Diags = D.getDiags();
 
   // Invoke ourselves in -fc1 mode.
   CmdArgs.push_back("-fc1");
@@ -884,17 +906,7 @@ void Flang::ConstructJob(Compilation &C, const JobAction &JA,
 
   handleColorDiagnosticsArgs(D, Args, CmdArgs);
 
-  // LTO mode is parsed by the Clang driver library.
-  LTOKind LTOMode = D.getLTOMode();
-  assert(LTOMode != LTOK_Unknown && "Unknown LTO mode.");
-  if (LTOMode == LTOK_Full)
-    CmdArgs.push_back("-flto=full");
-  else if (LTOMode == LTOK_Thin) {
-    Diags.Report(
-        Diags.getCustomDiagID(DiagnosticsEngine::Warning,
-                              "the option '-flto=thin' is a work in progress"));
-    CmdArgs.push_back("-flto=thin");
-  }
+  addLTOOptions(Args, CmdArgs);
 
   // -fPIC and related options.
   addPicOptions(Args, CmdArgs);
diff --git a/clang/lib/Driver/ToolChains/Flang.h b/clang/lib/Driver/ToolChains/Flang.h
index 7c24a623af393..98167e1b75e15 100644
--- a/clang/lib/Driver/ToolChains/Flang.h
+++ b/clang/lib/Driver/ToolChains/Flang.h
@@ -40,6 +40,14 @@ class LLVM_LIBRARY_VISIBILITY Flang : public Tool {
   void addPreprocessingOptions(const llvm::opt::ArgList &Args,
                                llvm::opt::ArgStringList &CmdArgs) const;
 
+  /// Extract LTO options from the driver arguments and add them to
+  /// the command arguments.
+  ///
+  /// \param [in] Args The list of input driver arguments
+  /// \param [out] CmdArgs The list of output command arguments
+  void addLTOOptions(const llvm::opt::ArgList &Args,
+                     llvm::opt::ArgStringList &CmdArgs) const;
+
   /// Extract PIC options from the driver arguments and add them to
   /// the command arguments.
   ///
diff --git a/flang/include/flang/Frontend/CodeGenOptions.def b/flang/include/flang/Frontend/CodeGenOptions.def
index cdeea93c9aecb..fa29b8ed79532 100644
--- a/flang/include/flang/Frontend/CodeGenOptions.def
+++ b/flang/include/flang/Frontend/CodeGenOptions.def
@@ -35,6 +35,7 @@ CODEGENOPT(InstrumentFunctions, 1, 0) ///< Set when -finstrument_functions is
 
 CODEGENOPT(IsPIE, 1, 0) ///< PIE level is the same as PIC Level.
 CODEGENOPT(PICLevel, 2, 0) ///< PIC level of the LLVM module.
+CODEGENOPT(PrepareForFatLTO , 1, 0) ///<  Set when -ffat-lto-objects is enabled.
 CODEGENOPT(PrepareForFullLTO , 1, 0) ///< Set when -flto is enabled on the
                                      ///< compile step.
 CODEGENOPT(PrepareForThinLTO , 1, 0) ///< Set when -flto=thin is enabled on the
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index fb3a132cae30e..d0adc517fcc5d 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -325,6 +325,10 @@ static void parseCodeGenArgs(Fortran::frontend::CodeGenOptions &opts,
   if (args.hasArg(clang::driver::options::OPT_finstrument_functions))
     opts.InstrumentFunctions = 1;
 
+  opts.PrepareForFatLTO =
+      args.hasFlag(clang::driver::options::OPT_ffat_lto_objects,
+                   clang::driver::options::OPT_fno_fat_lto_objects, false);
+
   // -flto=full/thin option.
   if (const llvm::opt::Arg *a =
           args.getLastArg(clang::driver::options::OPT_flto_EQ)) {
diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp
index 3bef6b1c31825..bcce9f6ede62e 100644
--- a/flang/lib/Frontend/FrontendActions.cpp
+++ b/flang/lib/Frontend/FrontendActions.cpp
@@ -995,7 +995,9 @@ void CodeGenAction::runOptimizationPipeline(llvm::raw_pwrite_stream &os) {
 
   // Create the pass manager.
   llvm::ModulePassManager mpm;
-  if (opts.PrepareForFullLTO)
+  if (opts.PrepareForFatLTO)
+    mpm = pb.buildFatLTODefaultPipeline(level, opts.PrepareForThinLTO, true);
+  else if (opts.PrepareForFullLTO)
     mpm = pb.buildLTOPreLinkDefaultPipeline(level);
   else if (opts.PrepareForThinLTO)
     mpm = pb.buildThinLTOPreLinkDefaultPipeline(level);
diff --git a/flang/test/Driver/lto-fatlto.f90 b/flang/test/Driver/lto-fatlto.f90
new file mode 100644
index 0000000000000..4602cf8f38890
--- /dev/null
+++ b/flang/test/Driver/lto-fatlto.f90
@@ -0,0 +1,20 @@
+! REQUIRES: x86-registered-target
+! checks fatlto objects: that valid bitcode is included in the object file generated. 
+
+! RUN: %flang -fc1 -triple x86_64-unknown-linux-gnu -flto -ffat-lto-objects -emit-obj %s -o %t.o
+! RUN: llvm-readelf -S %t.o | FileCheck %s --check-prefixes=ELF
+! RUN: llvm-objcopy --dump-section=.llvm.lto=%t.bc %t.o
+! RUN: llvm-dis %t.bc -o - | FileCheck %s --check-prefixes=DIS
+
+! ELF: .llvm.lto
+! DIS: define void @_QQmain()
+! DIS-NEXT:  ret void
+! DIS-NEXT: }
+
+! RUN: %flang -fc1 -triple x86_64-unknown-linux-gnu -flto -ffat-lto-objects -S %s -o - | FileCheck %s --check-prefixes=ASM
+
+!      ASM: .section        .llvm.lto,"e", at llvm_lto
+! ASM-NEXT: .Lllvm.embedded.object:
+! ASM-NEXT:        .asciz  "BC
+! ASM-NEXT: .size   .Lllvm.embedded.object
+end program
diff --git a/flang/test/Driver/lto-lld-flags.f90 b/flang/test/Driver/lto-lld-flags.f90
new file mode 100644
index 0000000000000..8100d7a9f7cf0
--- /dev/null
+++ b/flang/test/Driver/lto-lld-flags.f90
@@ -0,0 +1,19 @@
+! UNSUPPORTED: system-windows
+! check flto-partitions is passed to lld, fc1
+! RUN: %flang -### -fuse-ld=lld -flto=full -flto-partitions=16 %s 2>&1 | FileCheck %s --check-prefixes=LLD-PART,FC1-PART
+
+! FC1-PART: "-fc1"
+! FC1-PART-SAME: "-flto=full"
+! FC1-PART-SAME: "-flto-partitions=16"
+! LLD-PART: ld.lld
+! LLD-PART-SAME: "--lto-partitions=16"
+
+! check fat-lto-objects is passed to lld, fc1
+! RUN: %flang -### -fuse-ld=lld -flto -ffat-lto-objects %s 2>&1 | FileCheck %s --check-prefixes=LLD-FAT,FC1-FAT
+
+! FC1-FAT: "-fc1"
+! FC1-FAT-SAME: "-flto=full"
+! FC1-FAT-SAME: "-ffat-lto-objects"
+! LLD-FAT: ld.lld
+! LLD-FAT-SAME: "--fat-lto-objects"
+end program
\ No newline at end of file

>From 3375fc4409b72de7b67d4150aeb0e497e8465932 Mon Sep 17 00:00:00 2001
From: Anchu Rajendran <asudhaku at amd.com>
Date: Tue, 16 Sep 2025 14:07:34 -0500
Subject: [PATCH 2/2] R2: Addressing review comments

---
 clang/include/clang/Driver/CommonArgs.h    |  3 ++
 clang/lib/Driver/ToolChains/CommonArgs.cpp |  3 +-
 clang/lib/Driver/ToolChains/Flang.cpp      |  5 ++-
 flang/lib/Frontend/CompilerInvocation.cpp  | 45 ++++++++++++++--------
 flang/lib/Frontend/FrontendActions.cpp     | 11 ++++--
 flang/test/Driver/fatlto-err.f90           |  7 ++++
 flang/test/Driver/lto-lld-flags.f90        |  3 +-
 7 files changed, 54 insertions(+), 23 deletions(-)
 create mode 100644 flang/test/Driver/fatlto-err.f90

diff --git a/clang/include/clang/Driver/CommonArgs.h b/clang/include/clang/Driver/CommonArgs.h
index 1464ce4e1b31b..4526c8f0599cd 100644
--- a/clang/include/clang/Driver/CommonArgs.h
+++ b/clang/include/clang/Driver/CommonArgs.h
@@ -24,6 +24,9 @@ namespace clang {
 namespace driver {
 namespace tools {
 
+// Default value for -flto_partitions. It is shared between clang and flang.
+static llvm::StringRef lto_partition_default = "8";
+
 void addPathIfExists(const Driver &D, const Twine &Path,
                      ToolChain::path_list &Paths);
 
diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index 299422328aecf..1d54eda78672a 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -1003,7 +1003,8 @@ void tools::addLTOOptions(const ToolChain &ToolChain, const ArgList &Args,
 
     if (Args.hasArg(options::OPT_flto_partitions_EQ)) {
       int Value = 0;
-      StringRef A = Args.getLastArgValue(options::OPT_flto_partitions_EQ, "8");
+      StringRef A = Args.getLastArgValue(options::OPT_flto_partitions_EQ,
+                                         lto_partition_default);
       if (A.getAsInteger(10, Value) || (Value < 1)) {
         Arg *Arg = Args.getLastArg(options::OPT_flto_partitions_EQ);
         D.Diag(diag::err_drv_invalid_int_value)
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp
index f2fef91f7d4f6..67e0b364bcb02 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -183,7 +183,7 @@ void Flang::addCodegenOptions(const ArgList &Args,
 }
 
 void Flang::addLTOOptions(const ArgList &Args, ArgStringList &CmdArgs) const {
-  const auto &TC = getToolChain();
+  const ToolChain &TC = getToolChain();
   const Driver &D = TC.getDriver();
   DiagnosticsEngine &Diags = D.getDiags();
   LTOKind LTOMode = D.getLTOMode();
@@ -198,7 +198,8 @@ void Flang::addLTOOptions(const ArgList &Args, ArgStringList &CmdArgs) const {
     CmdArgs.push_back("-flto=thin");
   }
   if (Args.hasArg(options::OPT_flto_partitions_EQ)) {
-    StringRef A = Args.getLastArgValue(options::OPT_flto_partitions_EQ, "8");
+    StringRef A = Args.getLastArgValue(options::OPT_flto_partitions_EQ,
+                                       lto_partition_default);
     CmdArgs.push_back(Args.MakeArgString("-flto-partitions=" + A));
   }
   Args.addAllArgs(CmdArgs, {options::OPT_ffat_lto_objects,
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index d0adc517fcc5d..e8a39b67f4f82 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -325,21 +325,6 @@ static void parseCodeGenArgs(Fortran::frontend::CodeGenOptions &opts,
   if (args.hasArg(clang::driver::options::OPT_finstrument_functions))
     opts.InstrumentFunctions = 1;
 
-  opts.PrepareForFatLTO =
-      args.hasFlag(clang::driver::options::OPT_ffat_lto_objects,
-                   clang::driver::options::OPT_fno_fat_lto_objects, false);
-
-  // -flto=full/thin option.
-  if (const llvm::opt::Arg *a =
-          args.getLastArg(clang::driver::options::OPT_flto_EQ)) {
-    llvm::StringRef s = a->getValue();
-    assert((s == "full" || s == "thin") && "Unknown LTO mode.");
-    if (s == "full")
-      opts.PrepareForFullLTO = true;
-    else
-      opts.PrepareForThinLTO = true;
-  }
-
   if (const llvm::opt::Arg *a = args.getLastArg(
           clang::driver::options::OPT_mcode_object_version_EQ)) {
     llvm::StringRef s = a->getValue();
@@ -1486,6 +1471,7 @@ static bool parseLinkerOptionsArgs(CompilerInvocation &invoc,
                                    llvm::opt::ArgList &args,
                                    clang::DiagnosticsEngine &diags) {
   llvm::Triple triple = llvm::Triple(invoc.getTargetOpts().triple);
+  CodeGenOptions &opts = invoc.getCodeGenOpts();
 
   // TODO: support --dependent-lib on other platforms when MLIR supports
   //       !llvm.dependent.lib
@@ -1498,8 +1484,35 @@ static bool parseLinkerOptionsArgs(CompilerInvocation &invoc,
     return false;
   }
 
-  invoc.getCodeGenOpts().DependentLibs =
+  opts.DependentLibs =
       args.getAllArgValues(clang::driver::options::OPT_dependent_lib);
+
+  // -flto=full/thin option.
+  if (const llvm::opt::Arg *a =
+          args.getLastArg(clang::driver::options::OPT_flto_EQ)) {
+    llvm::StringRef s = a->getValue();
+    assert((s == "full" || s == "thin") && "Unknown LTO mode.");
+    if (s == "full")
+      opts.PrepareForFullLTO = true;
+    else
+      opts.PrepareForThinLTO = true;
+  }
+
+  // -ffat-lto-objects
+  if (const llvm::opt::Arg *arg =
+          args.getLastArg(clang::driver::options::OPT_ffat_lto_objects,
+                          clang::driver::options::OPT_fno_fat_lto_objects)) {
+    opts.PrepareForFatLTO =
+        arg->getOption().matches(clang::driver::options::OPT_ffat_lto_objects);
+    if (opts.PrepareForFatLTO) {
+      assert((opts.PrepareForFullLTO || opts.PrepareForThinLTO) &&
+             "Unknown LTO mode");
+
+      if (!triple.isOSBinFormatELF())
+        diags.Report(clang::diag::err_drv_unsupported_opt_for_target)
+            << arg->getAsString(args) << triple.getTriple();
+    }
+  }
   return true;
 }
 
diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp
index bcce9f6ede62e..6e5aa203d84a4 100644
--- a/flang/lib/Frontend/FrontendActions.cpp
+++ b/flang/lib/Frontend/FrontendActions.cpp
@@ -995,9 +995,14 @@ void CodeGenAction::runOptimizationPipeline(llvm::raw_pwrite_stream &os) {
 
   // Create the pass manager.
   llvm::ModulePassManager mpm;
-  if (opts.PrepareForFatLTO)
-    mpm = pb.buildFatLTODefaultPipeline(level, opts.PrepareForThinLTO, true);
-  else if (opts.PrepareForFullLTO)
+  if (opts.PrepareForFatLTO) {
+    // The module summary should be emitted by default for regular LTO
+    // except for ld64 targets.
+    bool emitSummary = opts.PrepareForThinLTO || opts.PrepareForFullLTO ||
+                       triple.getVendor() != llvm::Triple::Apple;
+    mpm = pb.buildFatLTODefaultPipeline(level, opts.PrepareForThinLTO,
+                                        emitSummary);
+  } else if (opts.PrepareForFullLTO)
     mpm = pb.buildLTOPreLinkDefaultPipeline(level);
   else if (opts.PrepareForThinLTO)
     mpm = pb.buildThinLTOPreLinkDefaultPipeline(level);
diff --git a/flang/test/Driver/fatlto-err.f90 b/flang/test/Driver/fatlto-err.f90
new file mode 100644
index 0000000000000..8b83f751c3c14
--- /dev/null
+++ b/flang/test/Driver/fatlto-err.f90
@@ -0,0 +1,7 @@
+
+! RUN: not %flang_fc1 -triple x86_64-apple-macos10.13 -flto -ffat-lto-objects -emit-llvm-bc %s 2>&1 | FileCheck %s --check-prefix=ERROR
+! ERROR: error: unsupported option '-ffat-lto-objects' for target 'x86_64-apple-macos10.13'
+
+parameter(i=1)
+integer :: j
+end program
diff --git a/flang/test/Driver/lto-lld-flags.f90 b/flang/test/Driver/lto-lld-flags.f90
index 8100d7a9f7cf0..963787afed6ea 100644
--- a/flang/test/Driver/lto-lld-flags.f90
+++ b/flang/test/Driver/lto-lld-flags.f90
@@ -16,4 +16,5 @@
 ! FC1-FAT-SAME: "-ffat-lto-objects"
 ! LLD-FAT: ld.lld
 ! LLD-FAT-SAME: "--fat-lto-objects"
-end program
\ No newline at end of file
+program test
+end program



More information about the flang-commits mailing list