Author: Michael Kruse Date: 2026-09-07T13:34:39Z New Revision: 7987830e776411a0de84c0c75e9f1059acadceb5
URL: https://github.com/llvm/llvm-project/commit/7987830e776411a0de84c0c75e9f1059acadceb5 DIFF: https://github.com/llvm/llvm-project/commit/7987830e776411a0de84c0c75e9f1059acadceb5.diff LOG: [Clang][Driver] Final phase also determined by input (#218802) Before this PR, `getFinalPhase` assumed that when no option is give then the final phase will a linking step. This is objectively wrong: For instance passing a header file as input (`-x c-header` or `-x c++-header`) triggers precompilation, not linking. Other inputs that do not have a link phase as specified by `Types.def` are `.dxo`, `.ifs`, and `.hlsl` files. Instead of defaulting to link in the absence of any action flag, this PR uses the last phase that `Types.def` specifies for the given input files. The effect of this is relevant when handling linking options which are categorized as "input" like any other file. Usually these should be filtered out if there is no linking step. But, since `getFinalPhase` says there is a linking flag, those are forwarded to the precompile handling which gets confused with such "inputs". An accurate determination of the final phase filters out those non-applicable linker options. Note that the second condition of `clang::diag::warn_drv_input_file_unused` was inverted: It would print > clang: warning: -lfoo: 'linker' input unused when '' is present [-Wunused-command-line-argument] when passing a linker option to a non-link command that is not the result of an action command flag. Inversely, when a non-link action flag was used, it wasn't mentioned in the diagnostic message. Added: clang/test/Driver/Inputs/object0.o clang/test/Driver/pch-inputs.h Modified: clang/include/clang/Driver/Driver.h clang/include/clang/Driver/Types.h clang/lib/Driver/Driver.cpp clang/lib/Driver/Types.cpp clang/test/Driver/aix-ld.c Removed: ################################################################################ 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 de5cb3faa69bb..7a742e404bf5c 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -352,8 +352,10 @@ 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 { Arg *PhaseArg = nullptr; phases::ID FinalPhase; @@ -401,9 +403,33 @@ 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 { + 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. + if (InputArg->getOption().hasFlag(options::LinkerInput)) + continue; + + // Relies on the compilation phases being ordered. + auto PL = types::getCompilationPhases(InputType); + if (PL.empty()) + continue; + + phases::ID LastPL = PL.back(); + if (LastPL > FinalPhase) + FinalPhase = LastPL; + AnyPhase = true; + } + + // Fall back to "do everything" when consistency check fails. + if (!AnyPhase || FinalPhase > phases::Link) + FinalPhase = phases::Link; + } if (FinalPhaseArg) *FinalPhaseArg = PhaseArg; @@ -1849,7 +1875,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); @@ -3434,7 +3461,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)) { @@ -3531,8 +3558,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; } @@ -3613,7 +3640,7 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args, 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); @@ -3627,7 +3654,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; @@ -4125,7 +4152,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( @@ -4178,7 +4205,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 4cca8acd515d7..618fb402af728 100644 --- a/clang/lib/Driver/Types.cpp +++ b/clang/lib/Driver/Types.cpp @@ -429,8 +429,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/Inputs/object0.o b/clang/test/Driver/Inputs/object0.o new file mode 100644 index 0000000000000..e69de29bb2d1d 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..fa6f94f996915 --- /dev/null +++ b/clang/test/Driver/pch-inputs.h @@ -0,0 +1,48 @@ +// 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 +// 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 +// 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 + +// 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 +// 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" + + _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
