[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/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index 949dbf1..4ea0f48 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -887,6 +887,22 @@
bool save;
};
+/// Process a directive while looking for the through header.
+/// Only #include (to check if it is the through header) and #define (to warn
+/// about macros that don't match the PCH) are handled. All other directives
+/// are completely discarded.
+void Preprocessor::HandleSkippedThroughHeaderDirective(Token &Result,
+ SourceLocation HashLoc) {
+ if (const IdentifierInfo *II = Result.getIdentifierInfo()) {
+ if (II->getPPKeywordID() == tok::pp_include)
+ return HandleIncludeDirective(HashLoc, Result);
+ if (II->getPPKeywordID() == tok::pp_define)
+ return HandleDefineDirective(Result,
+ /*ImmediatelyAfterHeaderGuard=*/false);
+ }
+ DiscardUntilEndOfDirective();
+}
+
/// HandleDirective - This callback is invoked when the lexer sees a # token
/// at the start of a line. This consumes the directive, modifies the
/// lexer/preprocessor state, and advances the lexer(s) so that the next token
@@ -948,6 +964,9 @@
// and reset to previous state when returning from this function.
ResetMacroExpansionHelper helper(this);
+ if (SkippingUntilPCHThroughHeader)
+ return HandleSkippedThroughHeaderDirective(Result, SavedHash.getLocation());
+
switch (Result.getKind()) {
case tok::eod:
return; // null directive.
@@ -1862,6 +1881,12 @@
}
}
+ if (usingPCHWithThroughHeader() && SkippingUntilPCHThroughHeader) {
+ if (isPCHThroughHeader(File))
+ SkippingUntilPCHThroughHeader = false;
+ return;
+ }
+
// Should we enter the source file? Set to false if either the source file is
// known to have no effect beyond its effect on module visibility -- that is,
// if it's got an include guard that is already defined or is a modular header
@@ -2587,7 +2612,15 @@
}
}
-
+ // When skipping just warn about macros that do not match.
+ if (SkippingUntilPCHThroughHeader) {
+ const MacroInfo *OtherMI = getMacroInfo(MacroNameTok.getIdentifierInfo());
+ if (!OtherMI || !MI->isIdenticalTo(*OtherMI, *this,
+ /*Syntactic=*/LangOpts.MicrosoftExt))
+ Diag(MI->getDefinitionLoc(), diag::warn_pp_macro_def_mismatch_with_pch)
+ << MacroNameTok.getIdentifierInfo();
+ return;
+ }
// Finally, if this identifier already had a macro defined for it, verify that
// the macro bodies are identical, and issue diagnostics if they are not.
diff --git a/clang/lib/Lex/PPLexerChange.cpp b/clang/lib/Lex/PPLexerChange.cpp
index c7e0d2c..352814d 100644
--- a/clang/lib/Lex/PPLexerChange.cpp
+++ b/clang/lib/Lex/PPLexerChange.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "clang/Lex/Preprocessor.h"
+#include "clang/Lex/PreprocessorOptions.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/HeaderSearch.h"
@@ -425,6 +426,8 @@
PragmaAssumeNonNullLoc = SourceLocation();
}
+ bool LeavingPCHThroughHeader = false;
+
// If this is a #include'd file, pop it off the include stack and continue
// lexing the #includer file.
if (!IncludeMacroStack.empty()) {
@@ -481,6 +484,12 @@
Result.setAnnotationValue(M);
}
+ bool FoundPCHThroughHeader = false;
+ if (CurPPLexer && creatingPCHWithThroughHeader() &&
+ isPCHThroughHeader(
+ SourceMgr.getFileEntryForID(CurPPLexer->getFileID())))
+ FoundPCHThroughHeader = true;
+
// We're done with the #included file.
RemoveTopOfLexerStack();
@@ -500,8 +509,16 @@
if (ExitedFromPredefinesFile)
replayPreambleConditionalStack();
- // Client should lex another token unless we generated an EOM.
- return LeavingSubmodule;
+ if (!isEndOfMacro && CurPPLexer && FoundPCHThroughHeader &&
+ (isInPrimaryFile() ||
+ CurPPLexer->getFileID() == getPredefinesFileID())) {
+ // Leaving the through header. Continue directly to end of main file
+ // processing.
+ LeavingPCHThroughHeader = true;
+ } else {
+ // Client should lex another token unless we generated an EOM.
+ return LeavingSubmodule;
+ }
}
// If this is the end of the main file, form an EOF token.
@@ -522,6 +539,12 @@
Result.setLocation(Result.getLocation().getLocWithOffset(-1));
}
+ if (creatingPCHWithThroughHeader() && !LeavingPCHThroughHeader) {
+ // Reached the end of the compilation without finding the through header.
+ Diag(CurLexer->getFileLoc(), diag::err_pp_through_header_not_seen)
+ << PPOpts->PCHThroughHeader << 0;
+ }
+
if (!isIncrementalProcessingEnabled())
// We're done with lexing.
CurLexer.reset();
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 358103d..0217a2e 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -149,6 +149,11 @@
Ident_AbnormalTermination = nullptr;
}
+ // If using a PCH with a through header, start skipping tokens.
+ if (!this->PPOpts->PCHThroughHeader.empty() &&
+ !this->PPOpts->ImplicitPCHInclude.empty())
+ SkippingUntilPCHThroughHeader = true;
+
if (this->PPOpts->GeneratePreamble)
PreambleConditionalStack.startRecording();
}
@@ -551,6 +556,72 @@
// Start parsing the predefines.
EnterSourceFile(FID, nullptr, SourceLocation());
+
+ if (!PPOpts->PCHThroughHeader.empty()) {
+ // Lookup and save the FileID for the through header. If it isn't found
+ // in the search path, it's a fatal error.
+ const DirectoryLookup *CurDir;
+ const FileEntry *File = LookupFile(
+ SourceLocation(), PPOpts->PCHThroughHeader,
+ /*isAngled=*/false, /*FromDir=*/nullptr, /*FromFile=*/nullptr, CurDir,
+ /*SearchPath=*/nullptr, /*RelativePath=*/nullptr,
+ /*SuggestedModule=*/nullptr, /*IsMapped=*/nullptr);
+ if (!File) {
+ Diag(SourceLocation(), diag::err_pp_through_header_not_found)
+ << PPOpts->PCHThroughHeader;
+ return;
+ }
+ setPCHThroughHeaderFileID(
+ SourceMgr.createFileID(File, SourceLocation(), SrcMgr::C_User));
+ }
+
+ // Skip tokens from the Predefines and if needed the main file.
+ if (usingPCHWithThroughHeader() && SkippingUntilPCHThroughHeader)
+ SkipTokensUntilPCHThroughHeader();
+}
+
+void Preprocessor::setPCHThroughHeaderFileID(FileID FID) {
+ assert(PCHThroughHeaderFileID.isInvalid() &&
+ "PCHThroughHeaderFileID already set!");
+ PCHThroughHeaderFileID = FID;
+}
+
+bool Preprocessor::isPCHThroughHeader(const FileEntry *FE) {
+ assert(PCHThroughHeaderFileID.isValid() &&
+ "Invalid PCH through header FileID");
+ return FE == SourceMgr.getFileEntryForID(PCHThroughHeaderFileID);
+}
+
+bool Preprocessor::creatingPCHWithThroughHeader() {
+ return TUKind == TU_Prefix && !PPOpts->PCHThroughHeader.empty() &&
+ PCHThroughHeaderFileID.isValid();
+}
+
+bool Preprocessor::usingPCHWithThroughHeader() {
+ return TUKind != TU_Prefix && !PPOpts->PCHThroughHeader.empty() &&
+ PCHThroughHeaderFileID.isValid();
+}
+
+/// Skip tokens until after the #include of the through header.
+/// Tokens in the predefines file and the main file may be skipped. If the end
+/// of the predefines file is reached, skipping continues into the main file.
+/// If the end of the main file is reached, it's a fatal error.
+void Preprocessor::SkipTokensUntilPCHThroughHeader() {
+ bool ReachedMainFileEOF = false;
+ Token Tok;
+ while (true) {
+ bool InPredefines = (CurLexer->getFileID() == getPredefinesFileID());
+ CurLexer->Lex(Tok);
+ if (Tok.is(tok::eof) && !InPredefines) {
+ ReachedMainFileEOF = true;
+ break;
+ }
+ if (!SkippingUntilPCHThroughHeader)
+ break;
+ }
+ if (ReachedMainFileEOF)
+ Diag(SourceLocation(), diag::err_pp_through_header_not_seen)
+ << PPOpts->PCHThroughHeader << 1;
}
void Preprocessor::replayPreambleConditionalStack() {