Index: include/clang/Lex/Preprocessor.h =================================================================== --- include/clang/Lex/Preprocessor.h +++ include/clang/Lex/Preprocessor.h @@ -277,6 +277,58 @@ /// This is used when loading a precompiled preamble. std::pair SkipMainFilePreamble; + class PreambleConditionalStackStore { + public: + enum State { + Off = 0, + Recording = 1, + Replaying = 2, + Done = 3 + }; + typedef SmallVector Stack; + + PreambleConditionalStackStore() + : ConditionalStack(nullptr, Off) + {} + ~PreambleConditionalStackStore() + { delete ConditionalStack.getPointer(); } + + void setState(State s) { + ConditionalStack.setInt(s); + } + + void startRecording() { ConditionalStack.setInt(Recording); } + bool isRecording() const { return ConditionalStack.getInt() == Recording; } + bool isReplaying() const { return ConditionalStack.getInt() == Replaying; } + + const Stack &getStack() const { + assert(ConditionalStack.getPointer()); + return *ConditionalStack.getPointer(); + } + + void doneReplaying() { + assert(ConditionalStack.getPointer()); + delete ConditionalStack.getPointer(); + ConditionalStack.setPointer(nullptr); + setState(Done); + } + + void setStack(const Stack &s) + { + if (!isRecording() && !isReplaying()) + return; + if (auto ptr = ConditionalStack.getPointer()) + *ptr = s; + else + ConditionalStack.setPointer(new Stack(s)); + } + + bool HasRecordedPreamble() const { return ConditionalStack.getPointer(); } + + private: + llvm::PointerIntPair ConditionalStack; + } PreambleConditionalStack; + /// \brief The current top of the stack that we're lexing from if /// not expanding a macro and we are lexing directly from source code. /// @@ -1904,6 +1956,28 @@ const FileEntry *getModuleHeaderToIncludeForDiagnostics(SourceLocation IncLoc, SourceLocation MLoc); + bool IsRecordingPreamble() const { + return PreambleConditionalStack.isRecording(); + } + + bool HasRecordedPreamble() const { + return PreambleConditionalStack.HasRecordedPreamble(); + } + + const SmallVector &getPreambleConditionalStack() const + { return PreambleConditionalStack.getStack(); } + + void setRecordedPreambleConditionalStack( + const SmallVector &s) { + PreambleConditionalStack.setStack(s); + } + + void setReplayablePreambleConditionalStack( + const SmallVector &s) { + PreambleConditionalStack.setState(PreambleConditionalStackStore::Replaying); + PreambleConditionalStack.setStack(s); + } + private: // Macro handling. void HandleDefineDirective(Token &Tok, bool ImmediatelyAfterTopLevelIfndef); Index: include/clang/Lex/PreprocessorLexer.h =================================================================== --- include/clang/Lex/PreprocessorLexer.h +++ include/clang/Lex/PreprocessorLexer.h @@ -176,6 +176,9 @@ conditional_iterator conditional_end() const { return ConditionalStack.end(); } + + void setConditionalLevels(const SmallVector &CL) + { ConditionalStack = CL; } }; } // end namespace clang Index: include/clang/Lex/PreprocessorOptions.h =================================================================== --- include/clang/Lex/PreprocessorOptions.h +++ include/clang/Lex/PreprocessorOptions.h @@ -81,6 +81,8 @@ /// The boolean indicates whether the preamble ends at the start of a new /// line. std::pair PrecompiledPreambleBytes; + + bool PreambleGeneration = false; /// The implicit PTH input included at the start of the translation unit, or /// empty. Index: include/clang/Serialization/ASTBitCodes.h =================================================================== --- include/clang/Serialization/ASTBitCodes.h +++ include/clang/Serialization/ASTBitCodes.h @@ -580,7 +580,9 @@ MSSTRUCT_PRAGMA_OPTIONS = 55, /// \brief Record code for \#pragma ms_struct options. - POINTERS_TO_MEMBERS_PRAGMA_OPTIONS = 56 + POINTERS_TO_MEMBERS_PRAGMA_OPTIONS = 56, + + PP_CONDITIONAL_STACK = 57, }; /// \brief Record types used within a source manager block. Index: lib/Frontend/ASTUnit.cpp =================================================================== --- lib/Frontend/ASTUnit.cpp +++ lib/Frontend/ASTUnit.cpp @@ -1972,6 +1972,7 @@ PreprocessorOptions &PPOpts = CI->getPreprocessorOpts(); PPOpts.RemappedFilesKeepOriginalName = RemappedFilesKeepOriginalName; PPOpts.AllowPCHWithCompilerErrors = AllowPCHWithCompilerErrors; + PPOpts.PreambleGeneration = PrecompilePreambleAfterNParses != 0; // Override the resources path. CI->getHeaderSearchOpts().ResourceDir = ResourceFilesPath; Index: lib/Lex/Lexer.cpp =================================================================== --- lib/Lex/Lexer.cpp +++ lib/Lex/Lexer.cpp @@ -682,9 +682,7 @@ } while (true); SourceLocation End; - if (IfCount) - End = IfStartTok.getLocation(); - else if (ActiveCommentLoc.isValid()) + if (ActiveCommentLoc.isValid()) End = ActiveCommentLoc; // don't truncate a decl comment. else End = TheTok.getLocation(); @@ -2528,6 +2526,11 @@ return true; } + if (PP->IsRecordingPreamble()) { + PP->setRecordedPreambleConditionalStack(ConditionalStack); + ConditionalStack.clear(); + } + // Issue diagnostics for unterminated #if and missing newline. // If we are in a #if directive, emit an error. Index: lib/Lex/PPLexerChange.cpp =================================================================== --- lib/Lex/PPLexerChange.cpp +++ lib/Lex/PPLexerChange.cpp @@ -130,6 +130,11 @@ Callbacks->FileChanged(CurLexer->getFileLoc(), PPCallbacks::EnterFile, FileType); } + + if (PreambleConditionalStack.isReplaying()) { + CurPPLexer->setConditionalLevels(PreambleConditionalStack.getStack()); + PreambleConditionalStack.doneReplaying(); + } } /// EnterSourceFileWithPTH - Add a source file to the top of the include stack Index: lib/Lex/Preprocessor.cpp =================================================================== --- lib/Lex/Preprocessor.cpp +++ lib/Lex/Preprocessor.cpp @@ -140,6 +140,9 @@ Ident_GetExceptionInfo = Ident_GetExceptionCode = nullptr; Ident_AbnormalTermination = nullptr; } + + if (this->PPOpts->PreambleGeneration) + PreambleConditionalStack.startRecording(); } Preprocessor::~Preprocessor() { Index: lib/Serialization/ASTReader.cpp =================================================================== --- lib/Serialization/ASTReader.cpp +++ lib/Serialization/ASTReader.cpp @@ -2803,6 +2803,20 @@ } break; + case PP_CONDITIONAL_STACK: + if (!Record.empty()) { + SmallVector ConditionalStack; + for (unsigned Idx = 0, N = Record.size() - 1; Idx < N; /* in loop */) { + auto loc = ReadSourceLocation(F, Record, Idx); + bool WasSkipping = Record[Idx++]; + bool FoundNonSkip = Record[Idx++]; + bool FoundElse = Record[Idx++]; + ConditionalStack.push_back({ loc, WasSkipping, FoundNonSkip, FoundElse }); + } + PP.setReplayablePreambleConditionalStack(ConditionalStack); + } + break; + case PP_COUNTER_VALUE: if (!Record.empty() && Listener) Listener->ReadCounter(F, Record[0]); Index: lib/Serialization/ASTWriter.cpp =================================================================== --- lib/Serialization/ASTWriter.cpp +++ lib/Serialization/ASTWriter.cpp @@ -983,6 +983,7 @@ RECORD(POINTERS_TO_MEMBERS_PRAGMA_OPTIONS); RECORD(UNUSED_LOCAL_TYPEDEF_NAME_CANDIDATES); RECORD(DELETE_EXPRS_TO_ANALYZE); + RECORD(PP_CONDITIONAL_STACK); // SourceManager Block. BLOCK(SOURCE_MANAGER_BLOCK); @@ -2140,6 +2141,17 @@ Stream.EmitRecord(PP_COUNTER_VALUE, Record); } + if (PP.IsRecordingPreamble() && PP.HasRecordedPreamble()) { + for (const auto &Cond : PP.getPreambleConditionalStack()) { + AddSourceLocation(Cond.IfLoc, Record); + Record.push_back(Cond.WasSkipping); + Record.push_back(Cond.FoundNonSkip); + Record.push_back(Cond.FoundElse); + } + Stream.EmitRecord(PP_CONDITIONAL_STACK, Record); + Record.clear(); + } + // Enter the preprocessor block. Stream.EnterSubblock(PREPROCESSOR_BLOCK_ID, 3); Index: test/Lexer/preamble.c =================================================================== --- test/Lexer/preamble.c +++ test/Lexer/preamble.c @@ -9,15 +9,12 @@ #pragma unknown #endif #ifdef WIBBLE -#include "honk" -#else -int foo(); +#include "foo" +int bar; #endif // This test checks for detection of the preamble of a file, which -// includes all of the starting comments and #includes. Note that any -// changes to the preamble part of this file must be mirrored in -// Inputs/preamble.txt, since we diff against it. +// includes all of the starting comments and #includes. // RUN: %clang_cc1 -print-preamble %s > %t // RUN: echo END. >> %t @@ -33,4 +30,6 @@ // CHECK-NEXT: #endif // CHECK-NEXT: #pragma unknown // CHECK-NEXT: #endif +// CHECK-NEXT: #ifdef WIBBLE +// CHECK-NEXT: #include "foo" // CHECK-NEXT: END. Index: test/Lexer/preamble2.c =================================================================== --- /dev/null +++ test/Lexer/preamble2.c @@ -0,0 +1,19 @@ +// Preamble detection test: header with an include guard. +#ifndef HEADER_H +#define HEADER_H +#include "foo" +int bar; +#endif + +// This test checks for detection of the preamble of a file, which +// includes all of the starting comments and #includes. + +// RUN: %clang_cc1 -print-preamble %s > %t +// RUN: echo END. >> %t +// RUN: FileCheck < %t %s + +// CHECK: // Preamble detection test: header with an include guard. +// CHECK-NEXT: #ifndef HEADER_H +// CHECK-NEXT: #define HEADER_H +// CHECK-NEXT: #include "foo" +// CHECK-NEXT: END.