https://github.com/Meinersbur updated 
https://github.com/llvm/llvm-project/pull/218802

>From e10bf991cca54b80710d572b59083b9f2b5d4b47 Mon Sep 17 00:00:00 2001
From: Michael Kruse <[email protected]>
Date: Wed, 26 Aug 2026 00:12:07 +0200
Subject: [PATCH 1/2] [Clang][Driver] Linker options do not trigger linker

---
 clang/include/clang/Driver/Driver.h |  1 +
 clang/include/clang/Driver/Types.h  |  6 ++--
 clang/lib/Driver/Driver.cpp         | 45 ++++++++++++++++++++---------
 clang/lib/Driver/Types.cpp          |  5 ++--
 clang/test/Driver/aix-ld.c          |  2 +-
 clang/test/Driver/pch-inputs.h      | 36 +++++++++++++++++++++++
 6 files changed, 76 insertions(+), 19 deletions(-)
 create mode 100644 clang/test/Driver/pch-inputs.h

diff --git a/clang/include/clang/Driver/Driver.h 
b/clang/include/clang/Driver/Driver.h
index a96d6c535191e..f4f0e20e78f5a 100644
--- a/clang/include/clang/Driver/Driver.h
+++ b/clang/include/clang/Driver/Driver.h
@@ -342,6 +342,7 @@ class Driver {
   //       modes. Fold this functionality into Types::getCompilationPhases and
   //       handleArguments.
   phases::ID getFinalPhase(const llvm::opt::DerivedArgList &DAL,
+                           llvm::ArrayRef<InputTy>,
                            llvm::opt::Arg **FinalPhaseArg = nullptr) const;
 
   llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
diff --git a/clang/include/clang/Driver/Types.h 
b/clang/include/clang/Driver/Types.h
index 9dd89e1904a4f..9ec456773716d 100644
--- a/clang/include/clang/Driver/Types.h
+++ b/clang/include/clang/Driver/Types.h
@@ -115,9 +115,9 @@ namespace types {
   /// done for type 'Id' up until including LastPhase.
   llvm::SmallVector<phases::ID, phases::MaxNumberOfPhases>
   getCompilationPhases(ID Id, phases::ID LastPhase = phases::IfsMerge);
-  llvm::SmallVector<phases::ID, phases::MaxNumberOfPhases>
-  getCompilationPhases(const clang::driver::Driver &Driver,
-                       llvm::opt::DerivedArgList &DAL, ID Id);
+  llvm::SmallVector<phases::ID, phases::MaxNumberOfPhases> 
getCompilationPhases(
+      const clang::driver::Driver &Driver, llvm::opt::DerivedArgList &DAL,
+      llvm::ArrayRef<std::pair<ID, const llvm::opt::Arg *>> Inputs, ID Id);
 
   /// lookupCXXTypeForCType - Lookup CXX input type that corresponds to given
   /// C type (used for clang++ emulation of g++ behaviour)
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 2b79cb9d12c2a..d261a8b126726 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -74,6 +74,7 @@
 #include "clang/ScalableStaticAnalysis/Core/TUSummary/ExtractorRegistry.h"
 #include "clang/ScalableStaticAnalysis/SSAFForceLinker.h" // IWYU pragma: keep
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/BitVector.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/SmallSet.h"
@@ -354,6 +355,7 @@ InputArgList Driver::ParseArgStrings(ArrayRef<const char *> 
ArgStrings,
 // affect the phase, starting with the earliest phases, and record which
 // option we used to determine the final phase.
 phases::ID Driver::getFinalPhase(const DerivedArgList &DAL,
+                                 llvm::ArrayRef<InputTy> Inputs,
                                  Arg **FinalPhaseArg) const {
   Arg *PhaseArg = nullptr;
   phases::ID FinalPhase;
@@ -401,9 +403,25 @@ phases::ID Driver::getFinalPhase(const DerivedArgList &DAL,
   } else if ((PhaseArg = DAL.getLastArg(options::OPT_emit_interface_stubs))) {
     FinalPhase = phases::IfsMerge;
 
-  // Otherwise do everything.
-  } else
-    FinalPhase = phases::Link;
+    // Otherwise, autodetect from last phase triggered by input file
+  } else {
+    llvm::BitVector UsedPhases(phases::MaxNumberOfPhases);
+    for (auto &I : Inputs) {
+      types::ID InputType = I.first;
+      const Arg *InputArg = I.second;
+
+      // Linker options should not trigger more phases
+      if (InputArg->getOption().hasFlag(options::LinkerInput))
+        continue;
+
+      auto PL = types::getCompilationPhases(InputType);
+      for (phases::ID P : PL)
+        UsedPhases.set(P);
+    }
+    FinalPhase = UsedPhases.any()
+                     ? static_cast<phases::ID>(UsedPhases.find_last())
+                     : phases::Link;
+  }
 
   if (FinalPhaseArg)
     *FinalPhaseArg = PhaseArg;
@@ -1835,7 +1853,8 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char 
*> ArgList) {
   BuildInputs(C->getDefaultToolChain(), *TranslatedArgs, Inputs);
   if (HasConfigFileTail && Inputs.size()) {
     Arg *FinalPhaseArg;
-    if (getFinalPhase(*TranslatedArgs, &FinalPhaseArg) == phases::Link) {
+    if (getFinalPhase(*TranslatedArgs, Inputs, &FinalPhaseArg) ==
+        phases::Link) {
       DerivedArgList TranslatedLinkerIns(*CfgOptionsTail);
       for (Arg *A : *CfgOptionsTail)
         TranslatedLinkerIns.append(A);
@@ -3329,7 +3348,7 @@ void Driver::BuildInputs(const ToolChain &TC, 
DerivedArgList &Args,
       A->claim();
     } else if (A->getOption().hasFlag(options::LinkerInput)) {
       // Just treat as object type, we could make a special type for this if
-      // necessary.
+      // necessary. <---
       Inputs.push_back(std::make_pair(types::TY_Object, A));
 
     } else if (A->getOption().matches(options::OPT_x)) {
@@ -4404,7 +4423,7 @@ void Driver::handleArguments(Compilation &C, 
DerivedArgList &Args,
   }
 
   Arg *FinalPhaseArg;
-  phases::ID FinalPhase = getFinalPhase(Args, &FinalPhaseArg);
+  phases::ID FinalPhase = getFinalPhase(Args, Inputs, &FinalPhaseArg);
 
   if (FinalPhase == phases::Link) {
     if (Args.hasArgNoClaim(options::OPT_hipstdpar)) {
@@ -4501,8 +4520,8 @@ void Driver::handleArguments(Compilation &C, 
DerivedArgList &Args,
       else
         Diag(clang::diag::warn_drv_input_file_unused)
             << InputArg->getAsString(Args) << getPhaseName(InitialPhase)
-            << !!FinalPhaseArg
-            << (FinalPhaseArg ? FinalPhaseArg->getOption().getName() : "");
+            << !FinalPhaseArg
+            << (FinalPhaseArg ? FinalPhaseArg->getSpelling() : "");
       continue;
     }
 
@@ -4577,7 +4596,7 @@ void Driver::BuildActions(Compilation &C, DerivedArgList 
&Args,
       UseNewOffloadingDriver && C.isOffloadingHostKind(Action::OFK_HIP) &&
       offloadDeviceOnly() && Args.hasArg(options::OPT_hip_link) &&
       Args.hasFlag(options::OPT_fgpu_rdc, options::OPT_fno_gpu_rdc, false) &&
-      getFinalPhase(Args) == phases::Link &&
+      getFinalPhase(Args, Inputs) == phases::Link &&
       !Args.hasArg(options::OPT_emit_llvm) &&
       Args.hasFlag(options::OPT_gpu_bundle_output,
                    options::OPT_no_gpu_bundle_output, true);
@@ -4597,7 +4616,7 @@ void Driver::BuildActions(Compilation &C, DerivedArgList 
&Args,
     types::ID InputType = I.first;
     const Arg *InputArg = I.second;
 
-    auto PL = types::getCompilationPhases(*this, Args, InputType);
+    auto PL = types::getCompilationPhases(*this, Args, Inputs, InputType);
     if (PL.empty())
       continue;
 
@@ -4723,7 +4742,7 @@ void Driver::BuildActions(Compilation &C, DerivedArgList 
&Args,
 
   if (LinkerInputs.empty()) {
     Arg *FinalPhaseArg;
-    if (getFinalPhase(Args, &FinalPhaseArg) == phases::Link)
+    if (getFinalPhase(Args, Inputs, &FinalPhaseArg) == phases::Link)
       if (!UseNewOffloadingDriver)
         OffloadBuilder->appendDeviceLinkActions(Actions);
   }
@@ -5124,7 +5143,7 @@ Driver::BuildOffloadingActions(Compilation &C, 
llvm::opt::DerivedArgList &Args,
   // Don't build offloading actions if we do not have a compile action. If
   // preprocessing only ignore embedding.
   if (!(isa<CompileJobAction>(HostAction) ||
-        getFinalPhase(Args) == phases::Preprocess))
+        getFinalPhase(Args, {Input}) == phases::Preprocess))
     return HostAction;
 
   bool UsesLLVMOffloading = Args.hasArg(
@@ -5177,7 +5196,7 @@ Driver::BuildOffloadingActions(Compilation &C, 
llvm::opt::DerivedArgList &Args,
             .isOSDarwin())
       HostAction->setCannotBeCollapsedWithNextDependentAction();
 
-    auto PL = types::getCompilationPhases(*this, Args, InputType);
+    auto PL = types::getCompilationPhases(*this, Args, {Input}, InputType);
 
     for (phases::ID Phase : PL) {
       if (Phase == phases::Link) {
diff --git a/clang/lib/Driver/Types.cpp b/clang/lib/Driver/Types.cpp
index 7cf8af1d1af92..c3c65b214907d 100644
--- a/clang/lib/Driver/Types.cpp
+++ b/clang/lib/Driver/Types.cpp
@@ -428,8 +428,9 @@ types::getCompilationPhases(ID Id, phases::ID LastPhase) {
 
 llvm::SmallVector<phases::ID, phases::MaxNumberOfPhases>
 types::getCompilationPhases(const clang::driver::Driver &Driver,
-                            llvm::opt::DerivedArgList &DAL, ID Id) {
-  return types::getCompilationPhases(Id, Driver.getFinalPhase(DAL));
+                            llvm::opt::DerivedArgList &DAL,
+                            llvm::ArrayRef<InputTy> Inputs, ID Id) {
+  return types::getCompilationPhases(Id, Driver.getFinalPhase(DAL, Inputs));
 }
 
 ID types::lookupCXXTypeForCType(ID Id) {
diff --git a/clang/test/Driver/aix-ld.c b/clang/test/Driver/aix-ld.c
index bedd224eeca9c..641908afc8d98 100644
--- a/clang/test/Driver/aix-ld.c
+++ b/clang/test/Driver/aix-ld.c
@@ -1171,4 +1171,4 @@
 // RUN:        -K \
 // RUN:        -c \
 // RUN:   | FileCheck --check-prefixes=CHECK-K-UNUSED %s
-// CHECK-K-UNUSED: clang: warning: -K: 'linker' input unused 
[-Wunused-command-line-argument]
+// CHECK-K-UNUSED: clang: warning: -K: 'linker' input unused when '-c' is 
present [-Wunused-command-line-argument]
diff --git a/clang/test/Driver/pch-inputs.h b/clang/test/Driver/pch-inputs.h
new file mode 100644
index 0000000000000..0d87bd110c42a
--- /dev/null
+++ b/clang/test/Driver/pch-inputs.h
@@ -0,0 +1,36 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+
+// Warn about linker options being ignored when not linking
+// RUN: %clang %s -lfoo -o %t/tmp1.pch -### 2>&1 | FileCheck %s 
--check-prefix=UNUSED-L,SINGLEHEADER
+// UNUSED-L: clang: warning: -lfoo: 'linker' input unused 
[-Wunused-command-line-argument]
+
+// RUN: %clang %s -Wl,--whole-archive -o %t/tmp1.pch -### 2>&1 | FileCheck %s 
--check-prefix=UNUSED-WL,SINGLEHEADER
+// UNUSED-WL: clang: warning: -Wl,--whole-archive: 'linker' input unused 
[-Wunused-command-line-argument]
+
+// RUN: %clang %S/Inputs/header1.h %S/Inputs/header2.h -lfoo -### 2>&1 | 
FileCheck %s --check-prefix=UNUSED-L,MULTIHEADER
+
+
+// Error with single -o when there are multiple output files
+// RUN: not %clang %S/Inputs/header1.h %S/Inputs/header2.h -lfoo -o 
%t/tmp2.pch -### 2>&1 | FileCheck %s --check-prefix=UNUSED-L,MULTIOUTPUT
+// MULTIOUTPUT: clang: error: cannot specify -o when generating multiple 
output files
+
+
+// Normal case: Single header file input compiles to .pch even without 
--precompile
+// RUN: %clang %s -o %t/tmp1.pch -### 2>&1 | FileCheck %s 
--check-prefix=SINGLEHEADER
+// SINGLEHEADER: "-cc1"
+// SINGLEHEADER: "-emit-pch"
+// SINGLEHEADER: "-o"
+// SINGLEHEADER: tmp1.pch
+
+
+// Multiple header files input compiles to one .pch each even without 
--precompile
+// RUN: %clang %S/Inputs/header1.h %S/Inputs/header2.h -### 2>&1 | FileCheck 
%s --check-prefix=MULTIHEADER
+// MULTIHEADER: "-cc1"
+// MULTIHEADER: -emit-pch
+// MULTIHEADER: "-o"
+// MULTIHEADER: header1.h.pch"
+// MULTIHEADER: "-cc1"
+// MULTIHEADER: -emit-pch
+// MULTIHEADER: "-o"
+// MULTIHEADER: header2.h.pch"

>From 77e27a91b96026b4b2143fca93a5cf27d81e64e3 Mon Sep 17 00:00:00 2001
From: Michael Kruse <[email protected]>
Date: Wed, 26 Aug 2026 00:54:39 +0200
Subject: [PATCH 2/2] Add actual linker input test

---
 clang/test/Driver/pch-inputs.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/clang/test/Driver/pch-inputs.h b/clang/test/Driver/pch-inputs.h
index 0d87bd110c42a..eea7aaad57a91 100644
--- a/clang/test/Driver/pch-inputs.h
+++ b/clang/test/Driver/pch-inputs.h
@@ -3,6 +3,7 @@
 
 // Warn about linker options being ignored when not linking
 // RUN: %clang %s -lfoo -o %t/tmp1.pch -### 2>&1 | FileCheck %s 
--check-prefix=UNUSED-L,SINGLEHEADER
+// RUN: %clang %s -x c++-header -lfoo -o %t/tmp1.pch -### 2>&1 | FileCheck %s 
--check-prefix=UNUSED-L,SINGLEHEADER
 // UNUSED-L: clang: warning: -lfoo: 'linker' input unused 
[-Wunused-command-line-argument]
 
 // RUN: %clang %s -Wl,--whole-archive -o %t/tmp1.pch -### 2>&1 | FileCheck %s 
--check-prefix=UNUSED-WL,SINGLEHEADER
@@ -15,6 +16,9 @@
 // RUN: not %clang %S/Inputs/header1.h %S/Inputs/header2.h -lfoo -o 
%t/tmp2.pch -### 2>&1 | FileCheck %s --check-prefix=UNUSED-L,MULTIOUTPUT
 // MULTIOUTPUT: clang: error: cannot specify -o when generating multiple 
output files
 
+// An actual linker input file (object0.o) triggers an error, not a warning
+// RUN: not %clang %s %S/Inputs/object0.o -o %t/tmp3.pch -### 2>&1 | FileCheck 
%s --check-prefix=MULTIOUTPUT
+
 
 // Normal case: Single header file input compiles to .pch even without 
--precompile
 // RUN: %clang %s -o %t/tmp1.pch -### 2>&1 | FileCheck %s 
--check-prefix=SINGLEHEADER

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

Reply via email to