https://github.com/jhuber6 updated 
https://github.com/llvm/llvm-project/pull/201155

>From 315551340fd71979068d4339c6923f515fa6f2d6 Mon Sep 17 00:00:00 2001
From: Joseph Huber <[email protected]>
Date: Tue, 2 Jun 2026 11:15:24 -0500
Subject: [PATCH 1/3] [Clang] Rework LTO mode selection to be a Toolchain
 property

Summary:
Currently, the LTO mode is a property of the Driver, which makes sense
because it is used to set up phases. However, we currently have `-flto`
and `-foffload-lto`, which is a split that doesn't fully work with the
full context of a heterogenous compilation as it is 'all-or-nothing'.

This PR seeks to be mostly NFC for now, just moving the queries to a
per-toolchain interface rather than the static driver mode setting we
have right now. The *single* use of this before ToolChains are created
is for the Webassembly toolchain to set an include path. This is now
just a direct check on the flag, which is consistent. In the future they
could shift to fat LTO objects as well.

The main goal for the PR is to allow the GPU / Offloading toolchains to
specify their "real" LTO behavior. Right now SPIR-V and AMDGCN both
default to LTO, but rather than re-use the LTO handling we hack through
the driver phases to override it. Allowing this split would let us
heavily simplify this logic.

Co-authored-by: Cursor <[email protected]>
---
 clang/include/clang/Driver/Driver.h          |  32 +----
 clang/include/clang/Driver/ToolChain.h       |  12 ++
 clang/lib/Driver/Driver.cpp                  | 118 +++++++------------
 clang/lib/Driver/SanitizerArgs.cpp           |   2 +-
 clang/lib/Driver/ToolChain.cpp               |  52 ++++++++
 clang/lib/Driver/ToolChains/AIX.cpp          |   5 +-
 clang/lib/Driver/ToolChains/AMDGPU.cpp       |   6 +-
 clang/lib/Driver/ToolChains/AMDGPUOpenMP.cpp |   2 +-
 clang/lib/Driver/ToolChains/AVR.cpp          |   5 +-
 clang/lib/Driver/ToolChains/BareMetal.cpp    |   5 +-
 clang/lib/Driver/ToolChains/Clang.cpp        |  24 ++--
 clang/lib/Driver/ToolChains/Cuda.cpp         |   8 +-
 clang/lib/Driver/ToolChains/Darwin.cpp       |   7 +-
 clang/lib/Driver/ToolChains/Flang.cpp        |   3 +-
 clang/lib/Driver/ToolChains/FreeBSD.cpp      |   5 +-
 clang/lib/Driver/ToolChains/Fuchsia.cpp      |   5 +-
 clang/lib/Driver/ToolChains/Gnu.cpp          |   5 +-
 clang/lib/Driver/ToolChains/HIPAMD.cpp       |   2 +-
 clang/lib/Driver/ToolChains/Haiku.cpp        |   5 +-
 clang/lib/Driver/ToolChains/Hexagon.cpp      |  10 +-
 clang/lib/Driver/ToolChains/MSVC.cpp         |   4 +-
 clang/lib/Driver/ToolChains/MinGW.cpp        |   5 +-
 clang/lib/Driver/ToolChains/OpenBSD.cpp      |   5 +-
 clang/lib/Driver/ToolChains/PS4CPU.cpp       |   8 +-
 clang/lib/Driver/ToolChains/SPIRV.cpp        |   4 +-
 clang/lib/Driver/ToolChains/Serenity.cpp     |   5 +-
 clang/lib/Driver/ToolChains/WebAssembly.cpp  |  12 +-
 27 files changed, 176 insertions(+), 180 deletions(-)

diff --git a/clang/include/clang/Driver/Driver.h 
b/clang/include/clang/Driver/Driver.h
index 5490e06b6f3f3..7281e04ac323e 100644
--- a/clang/include/clang/Driver/Driver.h
+++ b/clang/include/clang/Driver/Driver.h
@@ -55,12 +55,7 @@ class JobAction;
 class ToolChain;
 
 /// Describes the kind of LTO mode selected via -f(no-)?lto(=.*)? options.
-enum LTOKind {
-  LTOK_None,
-  LTOK_Full,
-  LTOK_Thin,
-  LTOK_Unknown
-};
+enum LTOKind : int { LTOK_None, LTOK_Full, LTOK_Thin, LTOK_Unknown };
 
 /// Whether headers used to construct C++20 module units should be looked
 /// up by the path supplied on the command line, or in the user or system
@@ -135,12 +130,6 @@ class Driver {
   /// interpretation.
   bool ModulesModeCXX20;
 
-  /// LTO mode selected via -f(no-)?lto(=.*)? options.
-  LTOKind LTOMode;
-
-  /// LTO mode selected via -f(no-offload-)?lto(=.*)? options.
-  LTOKind OffloadLTOMode;
-
   /// Options for CUID
   CUIDOptions CUIDOpts;
 
@@ -664,7 +653,8 @@ class Driver {
   Action *ConstructPhaseAction(
       Compilation &C, const llvm::opt::ArgList &Args, phases::ID Phase,
       Action *Input,
-      Action::OffloadKind TargetDeviceOffloadKind = Action::OFK_None) const;
+      Action::OffloadKind TargetDeviceOffloadKind = Action::OFK_None,
+      LTOKind TargetLTOMode = LTOK_None) const;
 
   /// BuildJobsForAction - Construct the jobs to perform for the action \p A 
and
   /// return an InputInfo for the result of running \p A.  Will only construct
@@ -740,18 +730,6 @@ class Driver {
   /// Get the mode for handling headers as set by fmodule-header{=}.
   ModuleHeaderMode getModuleHeaderMode() const { return CXX20HeaderType; }
 
-  /// Returns true if we are performing any kind of LTO.
-  bool isUsingLTO() const { return getLTOMode() != LTOK_None; }
-
-  /// Get the specific kind of LTO being performed.
-  LTOKind getLTOMode() const { return LTOMode; }
-
-  /// Returns true if we are performing any kind of offload LTO.
-  bool isUsingOffloadLTO() const { return getOffloadLTOMode() != LTOK_None; }
-
-  /// Get the specific kind of offload LTO being performed.
-  LTOKind getOffloadLTOMode() const { return OffloadLTOMode; }
-
   /// Get the CUID option.
   const CUIDOptions &getCUIDOpts() const { return CUIDOpts; }
 
@@ -784,10 +762,6 @@ class Driver {
   /// option.
   void setDriverMode(StringRef DriverModeValue);
 
-  /// Parse the \p Args list for LTO options and record the type of LTO
-  /// compilation based on which -f(no-)?lto(=.*)? option occurs last.
-  void setLTOMode(const llvm::opt::ArgList &Args);
-
   /// Retrieves a ToolChain for a particular \p Target triple.
   ///
   /// Will cache ToolChains for the life of the driver object, and create them
diff --git a/clang/include/clang/Driver/ToolChain.h 
b/clang/include/clang/Driver/ToolChain.h
index 684ef52d8532b..d700457c68591 100644
--- a/clang/include/clang/Driver/ToolChain.h
+++ b/clang/include/clang/Driver/ToolChain.h
@@ -61,6 +61,8 @@ class SanitizerArgs;
 class Tool;
 class XRayArgs;
 
+enum LTOKind : int;
+
 /// Helper structure used to pass information extracted from clang executable
 /// name such as `i686-linux-android-g++`.
 struct ParsedClangName {
@@ -462,6 +464,16 @@ class ToolChain {
   /// native LLVM support.
   virtual bool HasNativeLLVMSupport() const;
 
+  /// Returns the default LTO mode for this toolchain.
+  virtual LTOKind getDefaultLTOMode() const;
+
+  /// Resolve the requested LTO mode for this toolchain.
+  LTOKind getLTOMode(const llvm::opt::ArgList &Args,
+                     bool IsOffload = false) const;
+
+  /// Returns true if LTO is active for this toolchain given the args.
+  bool isUsingLTO(const llvm::opt::ArgList &Args, bool IsOffload = false) 
const;
+
   /// LookupTypeForExtension - Return the default language type to use for the
   /// given extension.
   virtual types::ID LookupTypeForExtension(StringRef Ext) const;
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 82e8977648b92..636a39b3690e7 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -181,10 +181,9 @@ Driver::Driver(StringRef ClangExecutable, StringRef 
TargetTriple,
     : Diags(Diags), VFS(std::move(VFS)), Mode(GCCMode),
       SaveTemps(SaveTempsNone), BitcodeEmbed(EmbedNone),
       Offload(OffloadHostDevice), CXX20HeaderType(HeaderMode_None),
-      ModulesModeCXX20(false), LTOMode(LTOK_None),
-      ClangExecutable(ClangExecutable), SysRoot(DEFAULT_SYSROOT),
-      DriverTitle(Title), CCCPrintBindings(false), CCPrintOptions(false),
-      CCLogDiagnostics(false), CCGenDiagnostics(false),
+      ModulesModeCXX20(false), ClangExecutable(ClangExecutable),
+      SysRoot(DEFAULT_SYSROOT), DriverTitle(Title), CCCPrintBindings(false),
+      CCPrintOptions(false), CCLogDiagnostics(false), CCGenDiagnostics(false),
       CCPrintProcessStats(false), CCPrintInternalStats(false),
       TargetTriple(TargetTriple), Saver(Alloc), PrependArg(nullptr),
       PreferredLinker(CLANG_DEFAULT_LINKER), CheckInputsExist(true),
@@ -840,50 +839,6 @@ static llvm::Triple computeTargetTriple(const Driver &D,
   return Target;
 }
 
-// Parse the LTO options and record the type of LTO compilation
-// based on which -f(no-)?lto(=.*)? or -f(no-)?offload-lto(=.*)?
-// option occurs last.
-static driver::LTOKind parseLTOMode(Driver &D, const llvm::opt::ArgList &Args,
-                                    OptSpecifier OptEq, OptSpecifier OptNeg) {
-  if (!Args.hasFlag(OptEq, OptNeg, false))
-    return LTOK_None;
-
-  const Arg *A = Args.getLastArg(OptEq);
-  StringRef LTOName = A->getValue();
-
-  driver::LTOKind LTOMode = llvm::StringSwitch<LTOKind>(LTOName)
-                                .Case("full", LTOK_Full)
-                                .Case("thin", LTOK_Thin)
-                                .Default(LTOK_Unknown);
-
-  if (LTOMode == LTOK_Unknown) {
-    D.Diag(diag::err_drv_unsupported_option_argument)
-        << A->getSpelling() << A->getValue();
-    return LTOK_None;
-  }
-  return LTOMode;
-}
-
-// Parse the LTO options.
-void Driver::setLTOMode(const llvm::opt::ArgList &Args) {
-  LTOMode =
-      parseLTOMode(*this, Args, options::OPT_flto_EQ, options::OPT_fno_lto);
-
-  OffloadLTOMode = parseLTOMode(*this, Args, options::OPT_foffload_lto_EQ,
-                                options::OPT_fno_offload_lto);
-
-  // Try to enable `-foffload-lto=full` if `-fopenmp-target-jit` is on.
-  if (Args.hasFlag(options::OPT_fopenmp_target_jit,
-                   options::OPT_fno_openmp_target_jit, false)) {
-    if (Arg *A = Args.getLastArg(options::OPT_foffload_lto_EQ,
-                                 options::OPT_fno_offload_lto))
-      if (OffloadLTOMode != LTOK_Full)
-        Diag(diag::err_drv_incompatible_options)
-            << A->getSpelling() << "-fopenmp-target-jit";
-    OffloadLTOMode = LTOK_Full;
-  }
-}
-
 /// Compute the desired OpenMP runtime from the flags provided.
 Driver::OpenMPRuntimeKind Driver::getOpenMPRuntime(const ArgList &Args) const {
   StringRef RuntimeName(CLANG_DEFAULT_OPENMP_RUNTIME);
@@ -1677,8 +1632,6 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char 
*> ArgList) {
       Offload = OffloadHostDevice;
   }
 
-  setLTOMode(Args);
-
   // Process -fembed-bitcode= flags.
   if (Arg *A = Args.getLastArg(options::OPT_fembed_bitcode_EQ)) {
     StringRef Name = A->getValue();
@@ -3648,7 +3601,9 @@ class OffloadingActionBuilder final {
               break;
 
             CudaDeviceActions[I] = C.getDriver().ConstructPhaseAction(
-                C, Args, Ph, CudaDeviceActions[I], Action::OFK_Cuda);
+                C, Args, Ph, CudaDeviceActions[I], Action::OFK_Cuda,
+                ToolChains[I]->getLTOMode(Args,
+                                          /*IsOffload=*/true));
 
             if (Ph == phases::Assemble)
               break;
@@ -3799,7 +3754,8 @@ class OffloadingActionBuilder final {
         // a fat binary containing all the code objects for different GPU's.
         // The fat binary is then an input to the host action.
         for (unsigned I = 0, E = GpuArchList.size(); I != E; ++I) {
-          if (C.getDriver().isUsingOffloadLTO()) {
+          if (ToolChains[I]->isUsingLTO(Args,
+                                        /*IsOffload=*/true)) {
             // When LTO is enabled, skip the backend and assemble phases and
             // use lld to link the bitcode.
             ActionList AL;
@@ -3824,10 +3780,12 @@ class OffloadingActionBuilder final {
                                      : types::TY_LLVM_BC;
               BackendAction =
                   C.MakeAction<BackendJobAction>(CudaDeviceActions[I], Output);
-            } else
+            } else {
+              auto DevLTO = ToolChains[I]->getLTOMode(Args, 
/*IsOffload=*/true);
               BackendAction = C.getDriver().ConstructPhaseAction(
                   C, Args, phases::Backend, CudaDeviceActions[I],
-                  AssociatedOffloadKind);
+                  AssociatedOffloadKind, DevLTO);
+            }
             auto AssembleAction = C.getDriver().ConstructPhaseAction(
                 C, Args, phases::Assemble, BackendAction,
                 AssociatedOffloadKind);
@@ -3892,9 +3850,11 @@ class OffloadingActionBuilder final {
       }
 
       // By default, we produce an action for each device arch.
-      for (Action *&A : CudaDeviceActions)
-        A = C.getDriver().ConstructPhaseAction(C, Args, CurPhase, A,
-                                               AssociatedOffloadKind);
+      for (unsigned I = 0, E = CudaDeviceActions.size(); I != E; ++I)
+        CudaDeviceActions[I] = C.getDriver().ConstructPhaseAction(
+            C, Args, CurPhase, CudaDeviceActions[I], AssociatedOffloadKind,
+            ToolChains[I]->getLTOMode(Args,
+                                      /*IsOffload=*/true));
 
       if (CompileDeviceOnly && CurPhase == FinalPhase && BundleOutput &&
           *BundleOutput) {
@@ -4352,7 +4312,7 @@ void Driver::handleArguments(Compilation &C, 
DerivedArgList &Args,
         !C.getDefaultToolChain().getTriple().isSPIRV())
       Diag(clang::diag::err_drv_emit_llvm_link);
     if (C.getDefaultToolChain().getTriple().isWindowsMSVCEnvironment() &&
-        LTOMode != LTOK_None &&
+        C.getDefaultToolChain().isUsingLTO(Args) &&
         !Args.getLastArgValue(options::OPT_fuse_ld_EQ)
              .starts_with_insensitive("lld"))
       Diag(clang::diag::err_drv_lto_without_lld);
@@ -4445,8 +4405,10 @@ void Driver::handleArguments(Compilation &C, 
DerivedArgList &Args,
         const types::ID HeaderType = lookupHeaderTypeForSourceType(InputType);
         // Build the pipeline for the pch file.
         Action *ClangClPch = C.MakeAction<InputAction>(*InputArg, HeaderType);
+        auto HostLTO = C.getDefaultToolChain().getLTOMode(Args);
         for (phases::ID Phase : types::getCompilationPhases(HeaderType))
-          ClangClPch = ConstructPhaseAction(C, Args, Phase, ClangClPch);
+          ClangClPch = ConstructPhaseAction(C, Args, Phase, ClangClPch,
+                                            Action::OFK_None, HostLTO);
         assert(ClangClPch);
         Actions.push_back(ClangClPch);
         // The driver currently exits after the first failed command.  This
@@ -4586,7 +4548,9 @@ void Driver::BuildActions(Compilation &C, DerivedArgList 
&Args,
       // later actions in the same command line?
 
       // Otherwise construct the appropriate action.
-      Action *NewCurrent = ConstructPhaseAction(C, Args, Phase, Current);
+      Action *NewCurrent =
+          ConstructPhaseAction(C, Args, Phase, Current, Action::OFK_None,
+                               C.getDefaultToolChain().getLTOMode(Args));
 
       // We didn't create a new action, so we will just move to the next phase.
       if (NewCurrent == Current)
@@ -5073,7 +5037,10 @@ Driver::BuildOffloadingActions(Compilation &C, 
llvm::opt::DerivedArgList &Args,
         // Propagate the ToolChain so we can use it in ConstructPhaseAction.
         A->propagateDeviceOffloadInfo(Kind, TCAndArch->second.data(),
                                       TCAndArch->first);
-        A = ConstructPhaseAction(C, Args, Phase, A, Kind);
+        A = ConstructPhaseAction(
+            C, Args, Phase, A, Kind,
+            TCAndArch->first->getLTOMode(Args,
+                                         /*IsOffload=*/true));
 
         if (isa<CompileJobAction>(A) && isa<CompileJobAction>(HostAction) &&
             Kind == Action::OFK_OpenMP &&
@@ -5226,7 +5193,7 @@ Driver::BuildOffloadingActions(Compilation &C, 
llvm::opt::DerivedArgList &Args,
 
 Action *Driver::ConstructPhaseAction(
     Compilation &C, const ArgList &Args, phases::ID Phase, Action *Input,
-    Action::OffloadKind TargetDeviceOffloadKind) const {
+    Action::OffloadKind TargetDeviceOffloadKind, LTOKind TargetLTOMode) const {
   llvm::PrettyStackTraceString CrashInfo("Constructing phase actions");
 
   // Some types skip the assembler phase (e.g., llvm-bc), but we can't
@@ -5350,18 +5317,19 @@ Action *Driver::ConstructPhaseAction(
         !(Args.hasArg(options::OPT_S) && !Args.hasArg(options::OPT_emit_llvm)))
       return Input;
 
-    if (isUsingLTO() && TargetDeviceOffloadKind == Action::OFK_None) {
-      types::ID Output;
-      if (Args.hasArg(options::OPT_ffat_lto_objects) &&
-          !Args.hasArg(options::OPT_emit_llvm))
-        Output = types::TY_PP_Asm;
-      else if (Args.hasArg(options::OPT_S))
-        Output = types::TY_LTO_IR;
-      else
-        Output = types::TY_LTO_BC;
-      return C.MakeAction<BackendJobAction>(Input, Output);
-    }
-    if (isUsingOffloadLTO() && TargetDeviceOffloadKind != Action::OFK_None) {
+    if (TargetLTOMode != LTOK_None) {
+      bool IsDeviceOffload = TargetDeviceOffloadKind != Action::OFK_None;
+      if (!IsDeviceOffload) {
+        types::ID Output;
+        if (Args.hasArg(options::OPT_ffat_lto_objects) &&
+            !Args.hasArg(options::OPT_emit_llvm))
+          Output = types::TY_PP_Asm;
+        else if (Args.hasArg(options::OPT_S))
+          Output = types::TY_LTO_IR;
+        else
+          Output = types::TY_LTO_BC;
+        return C.MakeAction<BackendJobAction>(Input, Output);
+      }
       types::ID Output =
           Args.hasArg(options::OPT_S) ? types::TY_LTO_IR : types::TY_LTO_BC;
       return C.MakeAction<BackendJobAction>(Input, Output);
@@ -6203,7 +6171,7 @@ InputInfoList Driver::BuildJobsForActionNoCache(
   ActionList CollapsedOffloadActions;
 
   ToolSelector TS(JA, *TC, C, isSaveTempsEnabled(),
-                  embedBitcodeInObject() && !isUsingLTO());
+                  embedBitcodeInObject() && !TC->isUsingLTO(C.getArgs()));
   const Tool *T = TS.getTool(Inputs, CollapsedOffloadActions);
 
   if (!T)
diff --git a/clang/lib/Driver/SanitizerArgs.cpp 
b/clang/lib/Driver/SanitizerArgs.cpp
index 31660dd29407c..74ebd0bf375d3 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -771,7 +771,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
   }
 
   // Check that LTO is enabled if we need it.
-  if ((Kinds & NeedsLTO) && !D.isUsingLTO() && DiagnoseErrors) {
+  if ((Kinds & NeedsLTO) && !TC.isUsingLTO(Args) && DiagnoseErrors) {
     D.Diag(diag::err_drv_argument_only_allowed_with)
         << lastArgumentForMask(D, Args, Kinds & NeedsLTO) << "-flto";
   }
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index 511eb3757456b..dd73c889570b9 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -1328,6 +1328,58 @@ bool ToolChain::HasNativeLLVMSupport() const {
   return false;
 }
 
+LTOKind ToolChain::getDefaultLTOMode() const { return LTOK_None; }
+
+bool ToolChain::isUsingLTO(const llvm::opt::ArgList &Args,
+                           bool IsOffload) const {
+  return getLTOMode(Args, IsOffload) != LTOK_None;
+}
+
+static LTOKind parseLTOMode(const llvm::opt::ArgList &Args,
+                            llvm::opt::OptSpecifier OptEq,
+                            llvm::opt::OptSpecifier OptNeg) {
+  if (!Args.hasFlag(OptEq, OptNeg, false))
+    return LTOK_None;
+
+  const Arg *A = Args.getLastArg(OptEq);
+  StringRef LTOName = A->getValue();
+
+  return llvm::StringSwitch<LTOKind>(LTOName)
+      .Case("full", LTOK_Full)
+      .Case("thin", LTOK_Thin)
+      .Default(LTOK_Unknown);
+}
+
+LTOKind ToolChain::getLTOMode(const llvm::opt::ArgList &Args,
+                              bool IsOffload) const {
+  auto OptEq = IsOffload ? options::OPT_foffload_lto_EQ : options::OPT_flto_EQ;
+  auto OptNeg = IsOffload ? options::OPT_fno_offload_lto : 
options::OPT_fno_lto;
+
+  // -fopenmp-target-jit implies -foffload-lto=full for device compilations,
+  // overriding any explicit -fno-offload-lto.
+  if (IsOffload && Args.hasFlag(options::OPT_fopenmp_target_jit,
+                                options::OPT_fno_openmp_target_jit, false)) {
+    if (Arg *A = Args.getLastArg(OptEq, OptNeg))
+      if (parseLTOMode(Args, OptEq, OptNeg) != LTOK_Full)
+        getDriver().Diag(diag::err_drv_incompatible_options)
+            << A->getSpelling() << "-fopenmp-target-jit";
+    return LTOK_Full;
+  }
+
+  if (!Args.hasArg(OptEq, OptNeg))
+    return getDefaultLTOMode();
+
+  LTOKind Mode = parseLTOMode(Args, OptEq, OptNeg);
+
+  if (Mode == LTOK_Unknown) {
+    const Arg *A = Args.getLastArg(OptEq);
+    getDriver().Diag(diag::err_drv_unsupported_option_argument)
+        << A->getSpelling() << A->getValue();
+    return LTOK_None;
+  }
+  return Mode;
+}
+
 bool ToolChain::isCrossCompiling() const {
   llvm::Triple HostTriple(LLVM_HOST_TRIPLE);
   switch (HostTriple.getArch()) {
diff --git a/clang/lib/Driver/ToolChains/AIX.cpp 
b/clang/lib/Driver/ToolChains/AIX.cpp
index 01b2c8ec18a35..b783e8596a872 100644
--- a/clang/lib/Driver/ToolChains/AIX.cpp
+++ b/clang/lib/Driver/ToolChains/AIX.cpp
@@ -243,9 +243,8 @@ void aix::Linker::ConstructJob(Compilation &C, const 
JobAction &JA,
   // Specify linker input file(s).
   AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA);
 
-  if (D.isUsingLTO())
-    addLTOOptions(ToolChain, Args, CmdArgs, Output, Inputs,
-                  D.getLTOMode() == LTOK_Thin);
+  if (auto LTO = ToolChain.getLTOMode(Args); LTO != LTOK_None)
+    addLTOOptions(ToolChain, Args, CmdArgs, Output, Inputs, LTO == LTOK_Thin);
 
   if (Args.hasArg(options::OPT_shared) && !hasExportListLinkerOpts(CmdArgs)) {
 
diff --git a/clang/lib/Driver/ToolChains/AMDGPU.cpp 
b/clang/lib/Driver/ToolChains/AMDGPU.cpp
index 55c94823376b0..db671e9e59ca2 100644
--- a/clang/lib/Driver/ToolChains/AMDGPU.cpp
+++ b/clang/lib/Driver/ToolChains/AMDGPU.cpp
@@ -609,9 +609,9 @@ void amdgpu::Linker::ConstructJob(Compilation &C, const 
JobAction &JA,
     CmdArgs.push_back("-shared");
   }
 
-  if (C.getDriver().isUsingLTO()) {
-    const bool ThinLTO = (C.getDriver().getLTOMode() == LTOK_Thin);
-    addLTOOptions(getToolChain(), Args, CmdArgs, Output, Inputs, ThinLTO);
+  if (auto LTO = getToolChain().getLTOMode(Args); LTO != LTOK_None) {
+    addLTOOptions(getToolChain(), Args, CmdArgs, Output, Inputs,
+                  LTO == LTOK_Thin);
   } else if (Args.hasArg(options::OPT_mcpu_EQ)) {
     CmdArgs.push_back(Args.MakeArgString(
         "-plugin-opt=mcpu=" +
diff --git a/clang/lib/Driver/ToolChains/AMDGPUOpenMP.cpp 
b/clang/lib/Driver/ToolChains/AMDGPUOpenMP.cpp
index aecec6c4c419b..a03ac8227a8c3 100644
--- a/clang/lib/Driver/ToolChains/AMDGPUOpenMP.cpp
+++ b/clang/lib/Driver/ToolChains/AMDGPUOpenMP.cpp
@@ -49,7 +49,7 @@ void AMDGPUOpenMPToolChain::addClangTargetOptions(
   }
 
   // Link the bitcode library late if we're using device LTO.
-  if (getDriver().isUsingOffloadLTO())
+  if (isUsingLTO(DriverArgs, /*IsOffload=*/true))
     return;
 }
 
diff --git a/clang/lib/Driver/ToolChains/AVR.cpp 
b/clang/lib/Driver/ToolChains/AVR.cpp
index 588255dc5a0cd..16bfa84d86dcf 100644
--- a/clang/lib/Driver/ToolChains/AVR.cpp
+++ b/clang/lib/Driver/ToolChains/AVR.cpp
@@ -587,9 +587,8 @@ void AVR::Linker::ConstructJob(Compilation &C, const 
JobAction &JA,
     }
   }
 
-  if (D.isUsingLTO())
-    addLTOOptions(TC, Args, CmdArgs, Output, Inputs,
-                  D.getLTOMode() == LTOK_Thin);
+  if (auto LTO = TC.getLTOMode(Args); LTO != LTOK_None)
+    addLTOOptions(TC, Args, CmdArgs, Output, Inputs, LTO == LTOK_Thin);
 
   // If the family name is known, we can link with the device-specific libgcc.
   // Without it, libgcc will simply not be linked. This matches avr-gcc
diff --git a/clang/lib/Driver/ToolChains/BareMetal.cpp 
b/clang/lib/Driver/ToolChains/BareMetal.cpp
index 4293295f20541..1bd34f7777226 100644
--- a/clang/lib/Driver/ToolChains/BareMetal.cpp
+++ b/clang/lib/Driver/ToolChains/BareMetal.cpp
@@ -600,9 +600,8 @@ void baremetal::Linker::ConstructJob(Compilation &C, const 
JobAction &JA,
   for (const auto &LibPath : TC.getLibraryPaths())
     CmdArgs.push_back(Args.MakeArgString(llvm::Twine("-L", LibPath)));
 
-  if (D.isUsingLTO())
-    addLTOOptions(TC, Args, CmdArgs, Output, Inputs,
-                  D.getLTOMode() == LTOK_Thin);
+  if (auto LTO = TC.getLTOMode(Args); LTO != LTOK_None)
+    addLTOOptions(TC, Args, CmdArgs, Output, Inputs, LTO == LTOK_Thin);
 
   AddLinkerInputs(TC, Inputs, Args, CmdArgs, JA);
   TC.addProfileRTLibs(Args, CmdArgs);
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index 7657afb14f077..57348dbeeb32c 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -5028,7 +5028,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction 
&JA,
   bool IsRDCMode =
       Args.hasFlag(options::OPT_fgpu_rdc, options::OPT_fno_gpu_rdc, false);
 
-  auto LTOMode = IsDeviceOffloadAction ? D.getOffloadLTOMode() : 
D.getLTOMode();
+  auto LTOMode = TC.getLTOMode(Args, IsDeviceOffloadAction);
   bool IsUsingLTO = LTOMode != LTOK_None;
 
   // Extract API doesn't have a main input file, so invent a fake one as a
@@ -5405,8 +5405,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction 
&JA,
             Twine("-flto=") + (LTOMode == LTOK_Thin ? "thin" : "full")));
         // PS4 uses the legacy LTO API, which does not support some of the
         // features enabled by -flto-unit.
-        if (!RawTriple.isPS4() ||
-            (D.getLTOMode() == LTOK_Full) || !UnifiedLTO)
+        if (!RawTriple.isPS4() || (LTOMode == LTOK_Full) || !UnifiedLTO)
           CmdArgs.push_back("-flto-unit");
       }
     }
@@ -8239,12 +8238,12 @@ void Clang::ConstructJob(Compilation &C, const 
JobAction &JA,
     // Check if we are using PS4 in regular LTO mode.
     // Otherwise, issue an error.
 
-    auto OtherLTOMode =
-        IsDeviceOffloadAction ? D.getLTOMode() : D.getOffloadLTOMode();
+    auto OtherLTOMode = TC.getLTOMode(Args, !IsDeviceOffloadAction);
     auto OtherIsUsingLTO = OtherLTOMode != LTOK_None;
 
     if ((!IsUsingLTO && !OtherIsUsingLTO) ||
-        (IsPS4 && !UnifiedLTO && (D.getLTOMode() != LTOK_Full)))
+        (IsPS4 && !UnifiedLTO &&
+         (TC.getLTOMode(Args, /*IsOffload=*/false) != LTOK_Full)))
       D.Diag(diag::err_drv_argument_only_allowed_with)
           << "-fwhole-program-vtables"
           << ((IsPS4 && !UnifiedLTO) ? "-flto=full" : "-flto");
@@ -9508,7 +9507,7 @@ void OffloadPackager::ConstructJob(Compilation &C, const 
JobAction &JA,
         "kind=" + Kind.str(),
     };
 
-    if (TC->getDriver().isUsingOffloadLTO())
+    if (TC->isUsingLTO(TCArgs, /*IsOffload=*/true))
       for (StringRef Feature : FeatureArgs)
         Parts.emplace_back("feature=" + Feature.str());
 
@@ -9682,8 +9681,8 @@ void LinkerWrapper::ConstructJob(Compilation &C, const 
JobAction &JA,
       // flags. SYCL uses clang-sycl-linker instead of spirv-link, so skip it.
       if (TC->getTriple().isSPIRV() &&
           TC->getTriple().getVendor() != llvm::Triple::VendorType::AMD &&
-          Kind != Action::OFK_SYCL && !C.getDriver().isUsingLTO() &&
-          !C.getDriver().isUsingOffloadLTO()) {
+          Kind != Action::OFK_SYCL &&
+          !TC->isUsingLTO(ToolChainArgs, /*IsOffload=*/true)) {
         // For SPIR-V some functions will be defined by the runtime so allow
         // unresolved symbols in `spirv-link`.
         LinkerArgs.emplace_back("--allow-partial-linkage");
@@ -9699,11 +9698,12 @@ void LinkerWrapper::ConstructJob(Compilation &C, const 
JobAction &JA,
         CmdArgs.push_back(Args.MakeArgString(
             "--device-linker=" + TC->getTripleString() + "=" + Arg));
 
-      // Forward the LTO mode relying on the Driver's parsing.
-      if (C.getDriver().getOffloadLTOMode() == LTOK_Full)
+      // Forward the LTO mode for this toolchain.
+      auto DeviceLTOMode = TC->getLTOMode(ToolChainArgs, /*IsOffload=*/true);
+      if (DeviceLTOMode == LTOK_Full)
         CmdArgs.push_back(Args.MakeArgString(
             "--device-compiler=" + TC->getTripleString() + "=-flto=full"));
-      else if (C.getDriver().getOffloadLTOMode() == LTOK_Thin) {
+      else if (DeviceLTOMode == LTOK_Thin) {
         CmdArgs.push_back(Args.MakeArgString(
             "--device-compiler=" + TC->getTripleString() + "=-flto=thin"));
         if (TC->getTriple().isAMDGPU()) {
diff --git a/clang/lib/Driver/ToolChains/Cuda.cpp 
b/clang/lib/Driver/ToolChains/Cuda.cpp
index cddd96a7e762d..a25f00e96b2f1 100644
--- a/clang/lib/Driver/ToolChains/Cuda.cpp
+++ b/clang/lib/Driver/ToolChains/Cuda.cpp
@@ -603,7 +603,7 @@ void NVPTX::Linker::ConstructJob(Compilation &C, const 
JobAction &JA,
     CmdArgs.push_back("-v");
 
   StringRef GPUArch = Args.getLastArgValue(options::OPT_march_EQ);
-  if (GPUArch.empty() && !C.getDriver().isUsingLTO()) {
+  if (GPUArch.empty() && !getToolChain().isUsingLTO(Args)) {
     C.getDriver().Diag(diag::err_drv_offload_missing_gpu_arch)
         << getToolChain().getArchName() << getShortName();
     return;
@@ -633,9 +633,9 @@ void NVPTX::Linker::ConstructJob(Compilation &C, const 
JobAction &JA,
   getToolChain().AddFilePathLibArgs(Args, CmdArgs);
   AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs, JA);
 
-  if (C.getDriver().isUsingLTO())
+  if (auto LTO = getToolChain().getLTOMode(Args); LTO != LTOK_None)
     addLTOOptions(getToolChain(), Args, CmdArgs, Output, Inputs,
-                  C.getDriver().getLTOMode() == LTOK_Thin);
+                  LTO == LTOK_Thin);
 
   // Forward the PTX features if the nvlink-wrapper needs it.
   std::vector<StringRef> Features;
@@ -939,7 +939,7 @@ void CudaToolChain::addClangTargetOptions(
     }
 
     // Link the bitcode library late if we're using device LTO.
-    if (getDriver().isUsingOffloadLTO())
+    if (isUsingLTO(DriverArgs, /*IsOffload=*/true))
       return;
 
     addOpenMPDeviceRTL(getDriver(), DriverArgs, CC1Args, GpuArch.str(),
diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp 
b/clang/lib/Driver/ToolChains/Darwin.cpp
index 103bc4ec7613f..3219b2f1fec45 100644
--- a/clang/lib/Driver/ToolChains/Darwin.cpp
+++ b/clang/lib/Driver/ToolChains/Darwin.cpp
@@ -250,16 +250,17 @@ void darwin::Linker::AddLinkArgs(Compilation &C, const 
ArgList &Args,
                    options::OPT_fno_application_extension, false))
     CmdArgs.push_back("-application_extension");
 
-  if (D.isUsingLTO() && (Version >= VersionTuple(116) || LinkerIsLLD) &&
+  if (auto LTO = getToolChain().getLTOMode(Args);
+      LTO != LTOK_None && (Version >= VersionTuple(116) || LinkerIsLLD) &&
       NeedsTempPath(Inputs)) {
     std::string TmpPathName;
-    if (D.getLTOMode() == LTOK_Full) {
+    if (LTO == LTOK_Full) {
       // If we are using full LTO, then automatically create a temporary file
       // path for the linker to use, so that it's lifetime will extend past a
       // possible dsymutil step.
       TmpPathName =
           D.GetTemporaryPath("cc", types::getTypeTempSuffix(types::TY_Object));
-    } else if (D.getLTOMode() == LTOK_Thin)
+    } else if (LTO == LTOK_Thin)
       // If we are using thin LTO, then create a directory instead.
       TmpPathName = D.GetTemporaryDirectory("thinlto");
 
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp 
b/clang/lib/Driver/ToolChains/Flang.cpp
index 892a455167205..b479ced418725 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -268,8 +268,7 @@ void Flang::addCodegenOptions(const ArgList &Args,
 
 void Flang::addLTOOptions(const ArgList &Args, ArgStringList &CmdArgs) const {
   const ToolChain &TC = getToolChain();
-  const Driver &D = TC.getDriver();
-  LTOKind LTOMode = D.getLTOMode();
+  LTOKind LTOMode = TC.getLTOMode(Args);
   // LTO mode is parsed by the Clang driver library.
   assert(LTOMode != LTOK_Unknown && "Unknown LTO mode.");
   if (LTOMode == LTOK_Full)
diff --git a/clang/lib/Driver/ToolChains/FreeBSD.cpp 
b/clang/lib/Driver/ToolChains/FreeBSD.cpp
index e3189d02eca21..1218868ac3bda 100644
--- a/clang/lib/Driver/ToolChains/FreeBSD.cpp
+++ b/clang/lib/Driver/ToolChains/FreeBSD.cpp
@@ -272,9 +272,8 @@ void freebsd::Linker::ConstructJob(Compilation &C, const 
JobAction &JA,
   Args.addAllArgs(CmdArgs,
                   {options::OPT_T_Group, options::OPT_s, options::OPT_t});
 
-  if (D.isUsingLTO())
-    addLTOOptions(ToolChain, Args, CmdArgs, Output, Inputs,
-                  D.getLTOMode() == LTOK_Thin);
+  if (auto LTO = ToolChain.getLTOMode(Args); LTO != LTOK_None)
+    addLTOOptions(ToolChain, Args, CmdArgs, Output, Inputs, LTO == LTOK_Thin);
 
   bool NeedsSanitizerDeps = addSanitizerRuntimes(ToolChain, Args, CmdArgs);
   bool NeedsXRayDeps = addXRayRuntime(ToolChain, Args, CmdArgs);
diff --git a/clang/lib/Driver/ToolChains/Fuchsia.cpp 
b/clang/lib/Driver/ToolChains/Fuchsia.cpp
index e534cb4a99b29..e037e29eb7796 100644
--- a/clang/lib/Driver/ToolChains/Fuchsia.cpp
+++ b/clang/lib/Driver/ToolChains/Fuchsia.cpp
@@ -139,9 +139,8 @@ void fuchsia::Linker::ConstructJob(Compilation &C, const 
JobAction &JA,
 
   ToolChain.AddFilePathLibArgs(Args, CmdArgs);
 
-  if (D.isUsingLTO())
-    addLTOOptions(ToolChain, Args, CmdArgs, Output, Inputs,
-                  D.getLTOMode() == LTOK_Thin);
+  if (auto LTO = ToolChain.getLTOMode(Args); LTO != LTOK_None)
+    addLTOOptions(ToolChain, Args, CmdArgs, Output, Inputs, LTO == LTOK_Thin);
 
   addLinkerCompressDebugSectionsOption(ToolChain, Args, CmdArgs);
   AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA);
diff --git a/clang/lib/Driver/ToolChains/Gnu.cpp 
b/clang/lib/Driver/ToolChains/Gnu.cpp
index 131dd725c7289..9aa0ec38a1191 100644
--- a/clang/lib/Driver/ToolChains/Gnu.cpp
+++ b/clang/lib/Driver/ToolChains/Gnu.cpp
@@ -438,9 +438,8 @@ void tools::gnutools::Linker::ConstructJob(Compilation &C, 
const JobAction &JA,
 
   ToolChain.AddFilePathLibArgs(Args, CmdArgs);
 
-  if (D.isUsingLTO())
-    addLTOOptions(ToolChain, Args, CmdArgs, Output, Inputs,
-                  D.getLTOMode() == LTOK_Thin);
+  if (auto LTO = ToolChain.getLTOMode(Args); LTO != LTOK_None)
+    addLTOOptions(ToolChain, Args, CmdArgs, Output, Inputs, LTO == LTOK_Thin);
 
   if (Args.hasArg(options::OPT_Z_Xlinker__no_demangle))
     CmdArgs.push_back("--no-demangle");
diff --git a/clang/lib/Driver/ToolChains/HIPAMD.cpp 
b/clang/lib/Driver/ToolChains/HIPAMD.cpp
index 98be4861276f8..ea2efedd6251b 100644
--- a/clang/lib/Driver/ToolChains/HIPAMD.cpp
+++ b/clang/lib/Driver/ToolChains/HIPAMD.cpp
@@ -70,7 +70,7 @@ void AMDGCN::Linker::constructLldCommand(Compilation &C, 
const JobAction &JA,
 
   auto &TC = getToolChain();
   auto &D = TC.getDriver();
-  bool IsThinLTO = D.getOffloadLTOMode() == LTOK_Thin;
+  bool IsThinLTO = TC.getLTOMode(Args, /*IsOffload=*/true) == LTOK_Thin;
   addLTOOptions(TC, Args, LldArgs, Output, Inputs, IsThinLTO);
 
   // Extract all the -m options
diff --git a/clang/lib/Driver/ToolChains/Haiku.cpp 
b/clang/lib/Driver/ToolChains/Haiku.cpp
index 6feb632f64bdb..083769f2c7954 100644
--- a/clang/lib/Driver/ToolChains/Haiku.cpp
+++ b/clang/lib/Driver/ToolChains/Haiku.cpp
@@ -87,9 +87,8 @@ void haiku::Linker::ConstructJob(Compilation &C, const 
JobAction &JA,
                             options::OPT_s, options::OPT_t});
   ToolChain.AddFilePathLibArgs(Args, CmdArgs);
 
-  if (D.isUsingLTO())
-    addLTOOptions(ToolChain, Args, CmdArgs, Output, Inputs,
-                  D.getLTOMode() == LTOK_Thin);
+  if (auto LTO = ToolChain.getLTOMode(Args); LTO != LTOK_None)
+    addLTOOptions(ToolChain, Args, CmdArgs, Output, Inputs, LTO == LTOK_Thin);
 
   bool NeedsSanitizerDeps = addSanitizerRuntimes(ToolChain, Args, CmdArgs);
   addLinkerCompressDebugSectionsOption(ToolChain, Args, CmdArgs);
diff --git a/clang/lib/Driver/ToolChains/Hexagon.cpp 
b/clang/lib/Driver/ToolChains/Hexagon.cpp
index ce3fd5110953a..0e7055797a1f0 100644
--- a/clang/lib/Driver/ToolChains/Hexagon.cpp
+++ b/clang/lib/Driver/ToolChains/Hexagon.cpp
@@ -358,9 +358,8 @@ constructHexagonLinkArgs(Compilation &C, const JobAction 
&JA,
                               options::OPT_t, options::OPT_u_Group});
     AddLinkerInputs(HTC, Inputs, Args, CmdArgs, JA);
 
-    if (D.isUsingLTO())
-      addLTOOptions(HTC, Args, CmdArgs, Output, Inputs,
-                    D.getLTOMode() == LTOK_Thin);
+    if (auto LTO = HTC.getLTOMode(Args); LTO != LTOK_None)
+      addLTOOptions(HTC, Args, CmdArgs, Output, Inputs, LTO == LTOK_Thin);
 
     ToolChain::UnwindLibType UNW = HTC.GetUnwindLibType(Args);
 
@@ -456,9 +455,8 @@ constructHexagonLinkArgs(Compilation &C, const JobAction 
&JA,
 
   AddLinkerInputs(HTC, Inputs, Args, CmdArgs, JA);
 
-  if (D.isUsingLTO())
-    addLTOOptions(HTC, Args, CmdArgs, Output, Inputs,
-                  D.getLTOMode() == LTOK_Thin);
+  if (auto LTO = HTC.getLTOMode(Args); LTO != LTOK_None)
+    addLTOOptions(HTC, Args, CmdArgs, Output, Inputs, LTO == LTOK_Thin);
 
   
//----------------------------------------------------------------------------
   // Libraries
diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp 
b/clang/lib/Driver/ToolChains/MSVC.cpp
index f0cb01058e9c9..6bc58699fb007 100644
--- a/clang/lib/Driver/ToolChains/MSVC.cpp
+++ b/clang/lib/Driver/ToolChains/MSVC.cpp
@@ -282,7 +282,7 @@ void visualstudio::Linker::ConstructJob(Compilation &C, 
const JobAction &JA,
     }
   }
 
-  if (C.getDriver().isUsingLTO()) {
+  if (TC.isUsingLTO(Args)) {
     if (Arg *A = tools::getLastProfileSampleUseArg(Args))
       CmdArgs.push_back(Args.MakeArgString(std::string("-lto-sample-profile:") 
+
                                            A->getValue()));
@@ -357,7 +357,7 @@ void visualstudio::Linker::ConstructJob(Compilation &C, 
const JobAction &JA,
       CmdArgs.push_back(
           Args.MakeArgString(std::string("/vfsoverlay:") + A->getValue()));
 
-    if (C.getDriver().isUsingLTO() &&
+    if (TC.isUsingLTO(Args) &&
         Args.hasFlag(options::OPT_gsplit_dwarf, options::OPT_gno_split_dwarf,
                      false))
       CmdArgs.push_back(Args.MakeArgString(Twine("/dwodir:") +
diff --git a/clang/lib/Driver/ToolChains/MinGW.cpp 
b/clang/lib/Driver/ToolChains/MinGW.cpp
index 78b15feee37ef..e3f8cb8292fc8 100644
--- a/clang/lib/Driver/ToolChains/MinGW.cpp
+++ b/clang/lib/Driver/ToolChains/MinGW.cpp
@@ -263,9 +263,8 @@ void tools::MinGW::Linker::ConstructJob(Compilation &C, 
const JobAction &JA,
 
   AddLinkerInputs(TC, Inputs, Args, CmdArgs, JA);
 
-  if (D.isUsingLTO())
-    addLTOOptions(TC, Args, CmdArgs, Output, Inputs,
-                  D.getLTOMode() == LTOK_Thin);
+  if (auto LTO = TC.getLTOMode(Args); LTO != LTOK_None)
+    addLTOOptions(TC, Args, CmdArgs, Output, Inputs, LTO == LTOK_Thin);
 
   if (C.getDriver().IsFlangMode() &&
       !Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
diff --git a/clang/lib/Driver/ToolChains/OpenBSD.cpp 
b/clang/lib/Driver/ToolChains/OpenBSD.cpp
index 9c1e47f4b61fe..a8ac97ac5f1b9 100644
--- a/clang/lib/Driver/ToolChains/OpenBSD.cpp
+++ b/clang/lib/Driver/ToolChains/OpenBSD.cpp
@@ -199,9 +199,8 @@ void openbsd::Linker::ConstructJob(Compilation &C, const 
JobAction &JA,
   Args.addAllArgs(CmdArgs,
                   {options::OPT_T_Group, options::OPT_s, options::OPT_t});
 
-  if (D.isUsingLTO())
-    addLTOOptions(ToolChain, Args, CmdArgs, Output, Inputs,
-                  D.getLTOMode() == LTOK_Thin);
+  if (auto LTO = ToolChain.getLTOMode(Args); LTO != LTOK_None)
+    addLTOOptions(ToolChain, Args, CmdArgs, Output, Inputs, LTO == LTOK_Thin);
 
   bool NeedsSanitizerDeps = addSanitizerRuntimes(ToolChain, Args, CmdArgs);
   bool NeedsXRayDeps = addXRayRuntime(ToolChain, Args, CmdArgs);
diff --git a/clang/lib/Driver/ToolChains/PS4CPU.cpp 
b/clang/lib/Driver/ToolChains/PS4CPU.cpp
index 8c281f60ae90a..e40127bc2baeb 100644
--- a/clang/lib/Driver/ToolChains/PS4CPU.cpp
+++ b/clang/lib/Driver/ToolChains/PS4CPU.cpp
@@ -167,8 +167,8 @@ void tools::PS4cpu::Linker::ConstructJob(Compilation &C, 
const JobAction &JA,
   // LTO indeed occurs.
   if (Args.hasFlag(options::OPT_funified_lto, options::OPT_fno_unified_lto,
                    true))
-    CmdArgs.push_back(D.getLTOMode() == LTOK_Thin ? "--lto=thin"
-                                                  : "--lto=full");
+    CmdArgs.push_back(TC.getLTOMode(Args) == LTOK_Thin ? "--lto=thin"
+                                                       : "--lto=full");
   if (UseJMC)
     AddLTOFlag("-enable-jmc-instrument");
 
@@ -348,8 +348,8 @@ void tools::PS5cpu::Linker::ConstructJob(Compilation &C, 
const JobAction &JA,
 
   if (Args.hasFlag(options::OPT_funified_lto, options::OPT_fno_unified_lto,
                    true))
-    CmdArgs.push_back(D.getLTOMode() == LTOK_Thin ? "--lto=thin"
-                                                  : "--lto=full");
+    CmdArgs.push_back(TC.getLTOMode(Args) == LTOK_Thin ? "--lto=thin"
+                                                       : "--lto=full");
 
   if (Args.hasFlag(options::OPT_ffat_lto_objects,
                    options::OPT_fno_fat_lto_objects, false))
diff --git a/clang/lib/Driver/ToolChains/SPIRV.cpp 
b/clang/lib/Driver/ToolChains/SPIRV.cpp
index 479696d523e90..5cc1eec74c1cc 100644
--- a/clang/lib/Driver/ToolChains/SPIRV.cpp
+++ b/clang/lib/Driver/ToolChains/SPIRV.cpp
@@ -167,7 +167,7 @@ void SPIRV::Linker::ConstructJob(Compilation &C, const 
JobAction &JA,
   CmdArgs.push_back(Output.getFilename());
 
   // TODO: Consider moving SPIR-V linking to a separate tool.
-  if (C.getDriver().isUsingLTO()) {
+  if (ToolChain.isUsingLTO(Args)) {
     // Implement limited LTO support through llvm-lto.
     if (Args.hasArg(options::OPT_sycl_link)) {
       // For unsupported cases, throw the same error as when LTO isn't 
supported
@@ -201,7 +201,7 @@ SPIRVToolChain::SPIRVToolChain(const Driver &D, const 
llvm::Triple &Triple,
     : ToolChain(D, Triple, Args) {
   // TODO: Revisit need/use of --sycl-link option once SYCL toolchain is
   // available and SYCL linking support is moved there.
-  NativeLLVMSupport = Args.hasArg(options::OPT_sycl_link) || D.isUsingLTO();
+  NativeLLVMSupport = Args.hasArg(options::OPT_sycl_link) || isUsingLTO(Args);
 
   // Lookup binaries into the driver directory.
   getProgramPaths().push_back(getDriver().Dir);
diff --git a/clang/lib/Driver/ToolChains/Serenity.cpp 
b/clang/lib/Driver/ToolChains/Serenity.cpp
index 001765e45f73d..60a966fac39e9 100644
--- a/clang/lib/Driver/ToolChains/Serenity.cpp
+++ b/clang/lib/Driver/ToolChains/Serenity.cpp
@@ -107,9 +107,8 @@ void tools::serenity::Linker::ConstructJob(Compilation &C, 
const JobAction &JA,
 
   TC.AddFilePathLibArgs(Args, CmdArgs);
 
-  if (D.isUsingLTO())
-    addLTOOptions(TC, Args, CmdArgs, Output, Inputs,
-                  D.getLTOMode() == LTOK_Thin);
+  if (auto LTO = TC.getLTOMode(Args); LTO != LTOK_None)
+    addLTOOptions(TC, Args, CmdArgs, Output, Inputs, LTO == LTOK_Thin);
 
   Args.addAllArgs(CmdArgs, {options::OPT_T_Group, options::OPT_s,
                             options::OPT_t, options::OPT_r});
diff --git a/clang/lib/Driver/ToolChains/WebAssembly.cpp 
b/clang/lib/Driver/ToolChains/WebAssembly.cpp
index 4c1cd937e81aa..b90f1ddf596fc 100644
--- a/clang/lib/Driver/ToolChains/WebAssembly.cpp
+++ b/clang/lib/Driver/ToolChains/WebAssembly.cpp
@@ -248,10 +248,12 @@ void wasm::Linker::ConstructJob(Compilation &C, const 
JobAction &JA,
 }
 
 /// Append `Dir` to `Paths`, but also include the LTO directories before that 
if
-/// LTO is eanbled.
-static void AppendLibDirAndLTODir(ToolChain::path_list &Paths, const Driver &D,
+/// LTO is enabled.
+static void AppendLibDirAndLTODir(ToolChain::path_list &Paths,
+                                  const ToolChain &TC,
+                                  const llvm::opt::ArgList &Args,
                                   const std::string &Dir) {
-  if (D.isUsingLTO()) {
+  if (TC.isUsingLTO(Args)) {
     // The version allows the path to be keyed to the specific version of
     // LLVM in used, as the bitcode format is not stable.
     Paths.push_back(Dir + "/llvm-lto/" LLVM_VERSION_STRING);
@@ -283,9 +285,9 @@ WebAssembly::WebAssembly(const Driver &D, const 
llvm::Triple &Triple,
     // sysroots that contain libraries that are capable of producing binaries
     // entirely without exception-handling instructions but also with if
     // exceptions are enabled, for example.
-    AppendLibDirAndLTODir(getFilePaths(), D,
+    AppendLibDirAndLTODir(getFilePaths(), *this, Args,
                           TripleLibDir + "/" + GetCXXExceptionsDir(Args));
-    AppendLibDirAndLTODir(getFilePaths(), D, TripleLibDir);
+    AppendLibDirAndLTODir(getFilePaths(), *this, Args, TripleLibDir);
   }
 
   if (getTriple().getOS() == llvm::Triple::WASI) {

>From 9f0f05e18af7cbc38eddec613dbe8eeab2700e6a Mon Sep 17 00:00:00 2001
From: Joseph Huber <[email protected]>
Date: Tue, 2 Jun 2026 14:02:39 -0500
Subject: [PATCH 2/3] Comments

---
 clang/include/clang/Driver/ToolChain.h       | 3 ++-
 clang/lib/Driver/Driver.cpp                  | 3 +--
 clang/lib/Driver/ToolChain.cpp               | 4 ++--
 clang/lib/Driver/ToolChains/AMDGPUOpenMP.cpp | 2 +-
 clang/lib/Driver/ToolChains/Clang.cpp        | 5 ++---
 clang/lib/Driver/ToolChains/Cuda.cpp         | 2 +-
 6 files changed, 9 insertions(+), 10 deletions(-)

diff --git a/clang/include/clang/Driver/ToolChain.h 
b/clang/include/clang/Driver/ToolChain.h
index d700457c68591..2ce3f38ebe6f3 100644
--- a/clang/include/clang/Driver/ToolChain.h
+++ b/clang/include/clang/Driver/ToolChain.h
@@ -472,7 +472,8 @@ class ToolChain {
                      bool IsOffload = false) const;
 
   /// Returns true if LTO is active for this toolchain given the args.
-  bool isUsingLTO(const llvm::opt::ArgList &Args, bool IsOffload = false) 
const;
+  bool isUsingLTO(const llvm::opt::ArgList &Args,
+                  Action::OffloadKind Kind = Action::OFK_None) const;
 
   /// LookupTypeForExtension - Return the default language type to use for the
   /// given extension.
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 636a39b3690e7..f40183fc9317b 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -3754,8 +3754,7 @@ class OffloadingActionBuilder final {
         // a fat binary containing all the code objects for different GPU's.
         // The fat binary is then an input to the host action.
         for (unsigned I = 0, E = GpuArchList.size(); I != E; ++I) {
-          if (ToolChains[I]->isUsingLTO(Args,
-                                        /*IsOffload=*/true)) {
+          if (ToolChains[I]->isUsingLTO(Args, AssociatedOffloadKind)) {
             // When LTO is enabled, skip the backend and assemble phases and
             // use lld to link the bitcode.
             ActionList AL;
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index dd73c889570b9..032b3bbaa0483 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -1331,8 +1331,8 @@ bool ToolChain::HasNativeLLVMSupport() const {
 LTOKind ToolChain::getDefaultLTOMode() const { return LTOK_None; }
 
 bool ToolChain::isUsingLTO(const llvm::opt::ArgList &Args,
-                           bool IsOffload) const {
-  return getLTOMode(Args, IsOffload) != LTOK_None;
+                           Action::OffloadKind Kind) const {
+  return getLTOMode(Args, Kind != Action::OFK_None) != LTOK_None;
 }
 
 static LTOKind parseLTOMode(const llvm::opt::ArgList &Args,
diff --git a/clang/lib/Driver/ToolChains/AMDGPUOpenMP.cpp 
b/clang/lib/Driver/ToolChains/AMDGPUOpenMP.cpp
index a03ac8227a8c3..40c85bae0b9db 100644
--- a/clang/lib/Driver/ToolChains/AMDGPUOpenMP.cpp
+++ b/clang/lib/Driver/ToolChains/AMDGPUOpenMP.cpp
@@ -49,7 +49,7 @@ void AMDGPUOpenMPToolChain::addClangTargetOptions(
   }
 
   // Link the bitcode library late if we're using device LTO.
-  if (isUsingLTO(DriverArgs, /*IsOffload=*/true))
+  if (isUsingLTO(DriverArgs, DeviceOffloadingKind))
     return;
 }
 
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index 57348dbeeb32c..8117058e1a228 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -9507,7 +9507,7 @@ void OffloadPackager::ConstructJob(Compilation &C, const 
JobAction &JA,
         "kind=" + Kind.str(),
     };
 
-    if (TC->isUsingLTO(TCArgs, /*IsOffload=*/true))
+    if (TC->isUsingLTO(TCArgs, OffloadAction->getOffloadingDeviceKind()))
       for (StringRef Feature : FeatureArgs)
         Parts.emplace_back("feature=" + Feature.str());
 
@@ -9681,8 +9681,7 @@ void LinkerWrapper::ConstructJob(Compilation &C, const 
JobAction &JA,
       // flags. SYCL uses clang-sycl-linker instead of spirv-link, so skip it.
       if (TC->getTriple().isSPIRV() &&
           TC->getTriple().getVendor() != llvm::Triple::VendorType::AMD &&
-          Kind != Action::OFK_SYCL &&
-          !TC->isUsingLTO(ToolChainArgs, /*IsOffload=*/true)) {
+          Kind != Action::OFK_SYCL && !TC->isUsingLTO(ToolChainArgs, Kind)) {
         // For SPIR-V some functions will be defined by the runtime so allow
         // unresolved symbols in `spirv-link`.
         LinkerArgs.emplace_back("--allow-partial-linkage");
diff --git a/clang/lib/Driver/ToolChains/Cuda.cpp 
b/clang/lib/Driver/ToolChains/Cuda.cpp
index a25f00e96b2f1..2e47d3e33ed50 100644
--- a/clang/lib/Driver/ToolChains/Cuda.cpp
+++ b/clang/lib/Driver/ToolChains/Cuda.cpp
@@ -939,7 +939,7 @@ void CudaToolChain::addClangTargetOptions(
     }
 
     // Link the bitcode library late if we're using device LTO.
-    if (isUsingLTO(DriverArgs, /*IsOffload=*/true))
+    if (isUsingLTO(DriverArgs, DeviceOffloadingKind))
       return;
 
     addOpenMPDeviceRTL(getDriver(), DriverArgs, CC1Args, GpuArch.str(),

>From aebaae42db8ea8f5e7ea43b106de836bde2d1b47 Mon Sep 17 00:00:00 2001
From: Joseph Huber <[email protected]>
Date: Tue, 2 Jun 2026 14:38:04 -0500
Subject: [PATCH 3/3] comments 2

---
 clang/include/clang/Driver/ToolChain.h |  2 +-
 clang/lib/Driver/Driver.cpp            | 15 ++++++---------
 clang/lib/Driver/ToolChain.cpp         |  5 +++--
 clang/lib/Driver/ToolChains/Clang.cpp  | 13 ++++++++-----
 clang/lib/Driver/ToolChains/HIPAMD.cpp |  2 +-
 5 files changed, 19 insertions(+), 18 deletions(-)

diff --git a/clang/include/clang/Driver/ToolChain.h 
b/clang/include/clang/Driver/ToolChain.h
index 2ce3f38ebe6f3..c9051d17850ad 100644
--- a/clang/include/clang/Driver/ToolChain.h
+++ b/clang/include/clang/Driver/ToolChain.h
@@ -469,7 +469,7 @@ class ToolChain {
 
   /// Resolve the requested LTO mode for this toolchain.
   LTOKind getLTOMode(const llvm::opt::ArgList &Args,
-                     bool IsOffload = false) const;
+                     Action::OffloadKind Kind = Action::OFK_None) const;
 
   /// Returns true if LTO is active for this toolchain given the args.
   bool isUsingLTO(const llvm::opt::ArgList &Args,
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index f40183fc9317b..d1170400f58c2 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -3602,8 +3602,7 @@ class OffloadingActionBuilder final {
 
             CudaDeviceActions[I] = C.getDriver().ConstructPhaseAction(
                 C, Args, Ph, CudaDeviceActions[I], Action::OFK_Cuda,
-                ToolChains[I]->getLTOMode(Args,
-                                          /*IsOffload=*/true));
+                ToolChains[I]->getLTOMode(Args, Action::OFK_Cuda));
 
             if (Ph == phases::Assemble)
               break;
@@ -3780,7 +3779,8 @@ class OffloadingActionBuilder final {
               BackendAction =
                   C.MakeAction<BackendJobAction>(CudaDeviceActions[I], Output);
             } else {
-              auto DevLTO = ToolChains[I]->getLTOMode(Args, 
/*IsOffload=*/true);
+              auto DevLTO =
+                  ToolChains[I]->getLTOMode(Args, AssociatedOffloadKind);
               BackendAction = C.getDriver().ConstructPhaseAction(
                   C, Args, phases::Backend, CudaDeviceActions[I],
                   AssociatedOffloadKind, DevLTO);
@@ -3852,8 +3852,7 @@ class OffloadingActionBuilder final {
       for (unsigned I = 0, E = CudaDeviceActions.size(); I != E; ++I)
         CudaDeviceActions[I] = C.getDriver().ConstructPhaseAction(
             C, Args, CurPhase, CudaDeviceActions[I], AssociatedOffloadKind,
-            ToolChains[I]->getLTOMode(Args,
-                                      /*IsOffload=*/true));
+            ToolChains[I]->getLTOMode(Args, AssociatedOffloadKind));
 
       if (CompileDeviceOnly && CurPhase == FinalPhase && BundleOutput &&
           *BundleOutput) {
@@ -5036,10 +5035,8 @@ Driver::BuildOffloadingActions(Compilation &C, 
llvm::opt::DerivedArgList &Args,
         // Propagate the ToolChain so we can use it in ConstructPhaseAction.
         A->propagateDeviceOffloadInfo(Kind, TCAndArch->second.data(),
                                       TCAndArch->first);
-        A = ConstructPhaseAction(
-            C, Args, Phase, A, Kind,
-            TCAndArch->first->getLTOMode(Args,
-                                         /*IsOffload=*/true));
+        A = ConstructPhaseAction(C, Args, Phase, A, Kind,
+                                 TCAndArch->first->getLTOMode(Args, Kind));
 
         if (isa<CompileJobAction>(A) && isa<CompileJobAction>(HostAction) &&
             Kind == Action::OFK_OpenMP &&
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index 032b3bbaa0483..82c4a7a34e47f 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -1332,7 +1332,7 @@ LTOKind ToolChain::getDefaultLTOMode() const { return 
LTOK_None; }
 
 bool ToolChain::isUsingLTO(const llvm::opt::ArgList &Args,
                            Action::OffloadKind Kind) const {
-  return getLTOMode(Args, Kind != Action::OFK_None) != LTOK_None;
+  return getLTOMode(Args, Kind) != LTOK_None;
 }
 
 static LTOKind parseLTOMode(const llvm::opt::ArgList &Args,
@@ -1351,7 +1351,8 @@ static LTOKind parseLTOMode(const llvm::opt::ArgList 
&Args,
 }
 
 LTOKind ToolChain::getLTOMode(const llvm::opt::ArgList &Args,
-                              bool IsOffload) const {
+                              Action::OffloadKind Kind) const {
+  bool IsOffload = Kind != Action::OFK_None;
   auto OptEq = IsOffload ? options::OPT_foffload_lto_EQ : options::OPT_flto_EQ;
   auto OptNeg = IsOffload ? options::OPT_fno_offload_lto : 
options::OPT_fno_lto;
 
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index 8117058e1a228..d6fbf53a2d345 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -5028,7 +5028,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction 
&JA,
   bool IsRDCMode =
       Args.hasFlag(options::OPT_fgpu_rdc, options::OPT_fno_gpu_rdc, false);
 
-  auto LTOMode = TC.getLTOMode(Args, IsDeviceOffloadAction);
+  auto LTOMode = TC.getLTOMode(Args, JA.getOffloadingDeviceKind());
   bool IsUsingLTO = LTOMode != LTOK_None;
 
   // Extract API doesn't have a main input file, so invent a fake one as a
@@ -8238,12 +8238,15 @@ void Clang::ConstructJob(Compilation &C, const 
JobAction &JA,
     // Check if we are using PS4 in regular LTO mode.
     // Otherwise, issue an error.
 
-    auto OtherLTOMode = TC.getLTOMode(Args, !IsDeviceOffloadAction);
+    auto OtherLTOMode = TC.getLTOMode(
+        Args, IsDeviceOffloadAction
+                  ? Action::OFK_None
+                  : static_cast<Action::OffloadKind>(
+                        C.getActiveOffloadKinds()));
     auto OtherIsUsingLTO = OtherLTOMode != LTOK_None;
 
     if ((!IsUsingLTO && !OtherIsUsingLTO) ||
-        (IsPS4 && !UnifiedLTO &&
-         (TC.getLTOMode(Args, /*IsOffload=*/false) != LTOK_Full)))
+        (IsPS4 && !UnifiedLTO && (TC.getLTOMode(Args) != LTOK_Full)))
       D.Diag(diag::err_drv_argument_only_allowed_with)
           << "-fwhole-program-vtables"
           << ((IsPS4 && !UnifiedLTO) ? "-flto=full" : "-flto");
@@ -9698,7 +9701,7 @@ void LinkerWrapper::ConstructJob(Compilation &C, const 
JobAction &JA,
             "--device-linker=" + TC->getTripleString() + "=" + Arg));
 
       // Forward the LTO mode for this toolchain.
-      auto DeviceLTOMode = TC->getLTOMode(ToolChainArgs, /*IsOffload=*/true);
+      auto DeviceLTOMode = TC->getLTOMode(ToolChainArgs, Kind);
       if (DeviceLTOMode == LTOK_Full)
         CmdArgs.push_back(Args.MakeArgString(
             "--device-compiler=" + TC->getTripleString() + "=-flto=full"));
diff --git a/clang/lib/Driver/ToolChains/HIPAMD.cpp 
b/clang/lib/Driver/ToolChains/HIPAMD.cpp
index ea2efedd6251b..5b88398c747d1 100644
--- a/clang/lib/Driver/ToolChains/HIPAMD.cpp
+++ b/clang/lib/Driver/ToolChains/HIPAMD.cpp
@@ -70,7 +70,7 @@ void AMDGCN::Linker::constructLldCommand(Compilation &C, 
const JobAction &JA,
 
   auto &TC = getToolChain();
   auto &D = TC.getDriver();
-  bool IsThinLTO = TC.getLTOMode(Args, /*IsOffload=*/true) == LTOK_Thin;
+  bool IsThinLTO = TC.getLTOMode(Args, Action::OFK_HIP) == LTOK_Thin;
   addLTOOptions(TC, Args, LldArgs, Output, Inputs, IsThinLTO);
 
   // Extract all the -m options

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to