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/6] [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/6] 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 >From 9fd1a7b7b6d9e9ae73451b91f90c52e944f0b80a Mon Sep 17 00:00:00 2001 From: Michael Kruse <[email protected]> Date: Wed, 26 Aug 2026 01:01:50 +0200 Subject: [PATCH 3/6] Add test object file --- clang/test/Driver/Inputs/object0.o | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 clang/test/Driver/Inputs/object0.o diff --git a/clang/test/Driver/Inputs/object0.o b/clang/test/Driver/Inputs/object0.o new file mode 100644 index 0000000000000..e69de29bb2d1d >From c7b3a459f2bc6a2c6585b949c43df2088aab7bcf Mon Sep 17 00:00:00 2001 From: Michael Kruse <[email protected]> Date: Wed, 26 Aug 2026 01:12:54 +0200 Subject: [PATCH 4/6] Consistent quoting in test --- clang/test/Driver/pch-inputs.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/test/Driver/pch-inputs.h b/clang/test/Driver/pch-inputs.h index eea7aaad57a91..fea3d7c40770a 100644 --- a/clang/test/Driver/pch-inputs.h +++ b/clang/test/Driver/pch-inputs.h @@ -25,16 +25,16 @@ // SINGLEHEADER: "-cc1" // SINGLEHEADER: "-emit-pch" // SINGLEHEADER: "-o" -// SINGLEHEADER: tmp1.pch +// 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: "-emit-pch" // MULTIHEADER: "-o" // MULTIHEADER: header1.h.pch" // MULTIHEADER: "-cc1" -// MULTIHEADER: -emit-pch +// MULTIHEADER: "-emit-pch" // MULTIHEADER: "-o" // MULTIHEADER: header2.h.pch" >From 623d152b63d5c208c406f602eead70aff5a0c9be Mon Sep 17 00:00:00 2001 From: Michael Kruse <[email protected]> Date: Wed, 26 Aug 2026 09:45:16 +0200 Subject: [PATCH 5/6] Make use of phase list being ordered --- clang/lib/Driver/Driver.cpp | 30 ++++++++++++++++++++---------- clang/test/Driver/pch-inputs.h | 8 ++++++++ 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index d261a8b126726..28ff1ba92019f 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -74,7 +74,6 @@ #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" @@ -353,7 +352,8 @@ InputArgList Driver::ParseArgStrings(ArrayRef<const char *> ArgStrings, // Determine which compilation mode we are in. We look for options which // affect the phase, starting with the earliest phases, and record which -// option we used to determine the final phase. +// option we used to determine the final phase. In absence of any explicit +// action command line option, derive the compilation mode from the inputs. phases::ID Driver::getFinalPhase(const DerivedArgList &DAL, llvm::ArrayRef<InputTy> Inputs, Arg **FinalPhaseArg) const { @@ -403,24 +403,34 @@ phases::ID Driver::getFinalPhase(const DerivedArgList &DAL, } else if ((PhaseArg = DAL.getLastArg(options::OPT_emit_interface_stubs))) { FinalPhase = phases::IfsMerge; - // Otherwise, autodetect from last phase triggered by input file + // Otherwise autodetect from last phase triggered by input file. } else { - llvm::BitVector UsedPhases(phases::MaxNumberOfPhases); + FinalPhase = phases::Preprocess; + bool AnyPhase = false; for (auto &I : Inputs) { types::ID InputType = I.first; const Arg *InputArg = I.second; - // Linker options should not trigger more phases + // Linker options should not trigger more phases. if (InputArg->getOption().hasFlag(options::LinkerInput)) continue; + // Relies on the compilation phases being ordered. auto PL = types::getCompilationPhases(InputType); - for (phases::ID P : PL) - UsedPhases.set(P); + if (PL.empty()) + continue; + + phases::ID LastPL = PL.back(); + if (LastPL > FinalPhase || !AnyPhase) { + PhaseArg = const_cast<Arg *>(InputArg); + FinalPhase = LastPL; + } + AnyPhase = true; } - FinalPhase = UsedPhases.any() - ? static_cast<phases::ID>(UsedPhases.find_last()) - : phases::Link; + + // Fall back to "do everything" when consistency check fails. + if (!AnyPhase || FinalPhase > phases::Link) + FinalPhase = phases::Link; } if (FinalPhaseArg) diff --git a/clang/test/Driver/pch-inputs.h b/clang/test/Driver/pch-inputs.h index fea3d7c40770a..fa6f94f996915 100644 --- a/clang/test/Driver/pch-inputs.h +++ b/clang/test/Driver/pch-inputs.h @@ -19,6 +19,12 @@ // 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 +// Other input types that do not link +// RUN: %clang -x cl-header %s -Xlinker -somelinkerflag -### 2>&1 | FileCheck %s --check-prefix=UNUSED-XLINKER +// RUN: %clang -x objective-c++-header %s -Xlinker -somelinkerflag -### 2>&1 | FileCheck %s --check-prefix=UNUSED-XLINKER +// RUN: %clang -x hlsl %s -Xlinker -somelinkerflag -### 2>&1 | FileCheck %s --check-prefix=UNUSED-XLINKER +// UNUSED-XLINKER: clang: warning: -Xlinker -somelinkerflag: 'linker' input unused [-Wunused-command-line-argument] + // 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 @@ -38,3 +44,5 @@ // MULTIHEADER: "-emit-pch" // MULTIHEADER: "-o" // MULTIHEADER: header2.h.pch" + + >From 9c737b0fec422988af2bfffdc9038226d2430de3 Mon Sep 17 00:00:00 2001 From: Michael Kruse <[email protected]> Date: Wed, 26 Aug 2026 13:30:57 +0200 Subject: [PATCH 6/6] Avoid misleading 'when pch-inputs.h is present' suffix --- clang/lib/Driver/Driver.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index 28ff1ba92019f..06417764d1344 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -421,10 +421,8 @@ phases::ID Driver::getFinalPhase(const DerivedArgList &DAL, continue; phases::ID LastPL = PL.back(); - if (LastPL > FinalPhase || !AnyPhase) { - PhaseArg = const_cast<Arg *>(InputArg); + if (LastPL > FinalPhase) FinalPhase = LastPL; - } AnyPhase = true; } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
