[clang-cl, PCH] Implement support for MS-style PCH through headers
Implement support for MS-style PCH through headers.
This enables support for /Yc and /Yu where the through header is either
on the command line or included in the source. It replaces the current
support the requires the header also be specified with /FI.
This change adds a -cc1 option -pch-through-header that is used to either
start or stop compilation during PCH create or use.
When creating a PCH, the compilation ends after compilation of the through
header.
When using a PCH, tokens are skipped until after the through header is seen.
Patch By: mikerice
Differential Revision: https://reviews.llvm.org/D46652
llvm-svn: 336379
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 7b890d8..1fd7a6d 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -2991,22 +2991,6 @@
Args.eraseArg(options::OPT__SLASH_Yu);
YcArg = YuArg = nullptr;
}
- if (YcArg || YuArg) {
- StringRef Val = YcArg ? YcArg->getValue() : YuArg->getValue();
- bool FoundMatchingInclude = false;
- for (const Arg *Inc : Args.filtered(options::OPT_include)) {
- // FIXME: Do case-insensitive matching and consider / and \ as equal.
- if (Inc->getValue() == Val)
- FoundMatchingInclude = true;
- }
- if (!FoundMatchingInclude) {
- Diag(clang::diag::warn_drv_ycyu_no_fi_arg_clang_cl)
- << (YcArg ? YcArg : YuArg)->getSpelling();
- Args.eraseArg(options::OPT__SLASH_Yc);
- Args.eraseArg(options::OPT__SLASH_Yu);
- YcArg = YuArg = nullptr;
- }
- }
if (YcArg && Inputs.size() > 1) {
Diag(clang::diag::warn_drv_yc_multiple_inputs_clang_cl);
Args.eraseArg(options::OPT__SLASH_Yc);
@@ -3076,11 +3060,9 @@
const types::ID HeaderType = lookupHeaderTypeForSourceType(InputType);
llvm::SmallVector<phases::ID, phases::MaxNumberOfPhases> PCHPL;
types::getCompilationPhases(HeaderType, PCHPL);
- Arg *PchInputArg = MakeInputArg(Args, *Opts, YcArg->getValue());
-
// Build the pipeline for the pch file.
Action *ClangClPch =
- C.MakeAction<InputAction>(*PchInputArg, HeaderType);
+ C.MakeAction<InputAction>(*InputArg, HeaderType);
for (phases::ID Phase : PCHPL)
ClangClPch = ConstructPhaseAction(C, Args, Phase, ClangClPch);
assert(ClangClPch);
@@ -4269,6 +4251,9 @@
// extension of .pch is assumed. "
if (!llvm::sys::path::has_extension(Output))
Output += ".pch";
+ } else if (Arg *YcArg = C.getArgs().getLastArg(options::OPT__SLASH_Yc)) {
+ Output = YcArg->getValue();
+ llvm::sys::path::replace_extension(Output, ".pch");
} else {
Output = BaseName;
llvm::sys::path::replace_extension(Output, ".pch");
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 5999d0b..d709280 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -1060,77 +1060,28 @@
// wonky, but we include looking for .gch so we can support seamless
// replacement into a build system already set up to be generating
// .gch files.
- int YcIndex = -1, YuIndex = -1;
- {
- int AI = -1;
+
+ if (getToolChain().getDriver().IsCLMode()) {
const Arg *YcArg = Args.getLastArg(options::OPT__SLASH_Yc);
const Arg *YuArg = Args.getLastArg(options::OPT__SLASH_Yu);
- for (const Arg *A : Args.filtered(options::OPT_clang_i_Group)) {
- // Walk the whole i_Group and skip non "-include" flags so that the index
- // here matches the index in the next loop below.
- ++AI;
- if (!A->getOption().matches(options::OPT_include))
- continue;
- if (YcArg && strcmp(A->getValue(), YcArg->getValue()) == 0)
- YcIndex = AI;
- if (YuArg && strcmp(A->getValue(), YuArg->getValue()) == 0)
- YuIndex = AI;
+ if (YcArg && JA.getKind() >= Action::PrecompileJobClass &&
+ JA.getKind() <= Action::AssembleJobClass) {
+ CmdArgs.push_back(Args.MakeArgString("-building-pch-with-obj"));
}
- }
- if (isa<PrecompileJobAction>(JA) && YcIndex != -1) {
- Driver::InputList Inputs;
- D.BuildInputs(getToolChain(), C.getArgs(), Inputs);
- assert(Inputs.size() == 1 && "Need one input when building pch");
- CmdArgs.push_back(Args.MakeArgString(Twine("-find-pch-source=") +
- Inputs[0].second->getValue()));
- }
- if (YcIndex != -1 && JA.getKind() >= Action::PrecompileJobClass &&
- JA.getKind() <= Action::AssembleJobClass) {
- CmdArgs.push_back(Args.MakeArgString("-building-pch-with-obj"));
+ if (YcArg || YuArg) {
+ StringRef ThroughHeader = YcArg ? YcArg->getValue() : YuArg->getValue();
+ if (!isa<PrecompileJobAction>(JA)) {
+ CmdArgs.push_back("-include-pch");
+ CmdArgs.push_back(Args.MakeArgString(D.GetClPchPath(C, ThroughHeader)));
+ }
+ CmdArgs.push_back(
+ Args.MakeArgString(Twine("-pch-through-header=") + ThroughHeader));
+ }
}
bool RenderedImplicitInclude = false;
- int AI = -1;
for (const Arg *A : Args.filtered(options::OPT_clang_i_Group)) {
- ++AI;
-
- if (getToolChain().getDriver().IsCLMode() &&
- A->getOption().matches(options::OPT_include)) {
- // In clang-cl mode, /Ycfoo.h means that all code up to a foo.h
- // include is compiled into foo.h, and everything after goes into
- // the .obj file. /Yufoo.h means that all includes prior to and including
- // foo.h are completely skipped and replaced with a use of the pch file
- // for foo.h. (Each flag can have at most one value, multiple /Yc flags
- // just mean that the last one wins.) If /Yc and /Yu are both present
- // and refer to the same file, /Yc wins.
- // Note that OPT__SLASH_FI gets mapped to OPT_include.
- // FIXME: The code here assumes that /Yc and /Yu refer to the same file.
- // cl.exe seems to support both flags with different values, but that
- // seems strange (which flag does /Fp now refer to?), so don't implement
- // that until someone needs it.
- int PchIndex = YcIndex != -1 ? YcIndex : YuIndex;
- if (PchIndex != -1) {
- if (isa<PrecompileJobAction>(JA)) {
- // When building the pch, skip all includes after the pch.
- assert(YcIndex != -1 && PchIndex == YcIndex);
- if (AI >= YcIndex)
- continue;
- } else {
- // When using the pch, skip all includes prior to the pch.
- if (AI < PchIndex) {
- A->claim();
- continue;
- }
- if (AI == PchIndex) {
- A->claim();
- CmdArgs.push_back("-include-pch");
- CmdArgs.push_back(
- Args.MakeArgString(D.GetClPchPath(C, A->getValue())));
- continue;
- }
- }
- }
- } else if (A->getOption().matches(options::OPT_include)) {
+ if (A->getOption().matches(options::OPT_include)) {
// Handling of gcc-style gch precompiled headers.
bool IsFirstImplicitInclude = !RenderedImplicitInclude;
RenderedImplicitInclude = true;