From b64c19365deab788753d29c9bc881253c3f16f37 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 12 May 2009 01:31:05 +0000 Subject: [PATCH] Make precompiled headers work with -E. When we're only preprocessing (with -E), we turn the PCH include into an implicit include of the file from which the PCH file was generated. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@71534 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Frontend/PCHBitCodes.h | 6 +- include/clang/Frontend/PCHReader.h | 14 +++++ include/clang/Frontend/PCHWriter.h | 2 +- lib/Frontend/PCHReader.cpp | 85 ++++++++++++++++++++++++++++ lib/Frontend/PCHWriter.cpp | 51 +++++++++++++---- test/PCH/preprocess.c | 5 ++ test/PCH/preprocess.h | 7 +++ tools/clang-cc/clang-cc.cpp | 19 ++++++- 8 files changed, 173 insertions(+), 16 deletions(-) create mode 100644 test/PCH/preprocess.c create mode 100644 test/PCH/preprocess.h diff --git a/include/clang/Frontend/PCHBitCodes.h b/include/clang/Frontend/PCHBitCodes.h index 29f408316b..0862cd6390 100644 --- a/include/clang/Frontend/PCHBitCodes.h +++ b/include/clang/Frontend/PCHBitCodes.h @@ -214,7 +214,11 @@ namespace clang { /// \brief Record code for the set of Objective-C category /// implementations. - OBJC_CATEGORY_IMPLEMENTATIONS = 19 + OBJC_CATEGORY_IMPLEMENTATIONS = 19, + + /// \brief Record code for the original file that was used to + /// generate the precompiled header. + ORIGINAL_FILE_NAME = 20 }; /// \brief Record types used within a source manager block. diff --git a/include/clang/Frontend/PCHReader.h b/include/clang/Frontend/PCHReader.h index e5591fe631..1e00ae3413 100644 --- a/include/clang/Frontend/PCHReader.h +++ b/include/clang/Frontend/PCHReader.h @@ -216,6 +216,10 @@ private: /// the PCH file. llvm::SmallVector ObjCCategoryImpls; + /// \brief The original file name that was used to build the PCH + /// file. + std::string OriginalFileName; + /// \brief Mapping from switch-case IDs in the PCH file to /// switch-case statements. std::map SwitchCaseStmts; @@ -327,8 +331,18 @@ public: explicit PCHReader(Preprocessor &PP, ASTContext *Context); ~PCHReader(); + /// \brief Load the precompiled header designated by the given file + /// name. PCHReadResult ReadPCH(const std::string &FileName); + /// \brief Retrieve the name of the original source file name + const std::string &getOriginalSourceFile() { return OriginalFileName; } + + /// \brief Retrieve the name of the original source file name + /// directly from the PCH file, without actually loading the PCH + /// file. + static std::string getOriginalSourceFile(const std::string &PCHFileName); + /// \brief Returns the suggested contents of the predefines buffer, /// which contains a (typically-empty) subset of the predefines /// build prior to including the precompiled header. diff --git a/include/clang/Frontend/PCHWriter.h b/include/clang/Frontend/PCHWriter.h index 3c2d5ff1af..5cb939547f 100644 --- a/include/clang/Frontend/PCHWriter.h +++ b/include/clang/Frontend/PCHWriter.h @@ -160,7 +160,7 @@ private: unsigned NumVisibleDeclContexts; void WriteBlockInfoBlock(); - void WriteMetadata(const TargetInfo &Target); + void WriteMetadata(ASTContext &Context); void WriteLanguageOptions(const LangOptions &LangOpts); void WriteStatCache(MemorizeStatCalls &StatCalls); void WriteSourceManagerBlock(SourceManager &SourceMgr, diff --git a/lib/Frontend/PCHReader.cpp b/lib/Frontend/PCHReader.cpp index 759865c7eb..ab7ad04e9d 100644 --- a/lib/Frontend/PCHReader.cpp +++ b/lib/Frontend/PCHReader.cpp @@ -1218,6 +1218,10 @@ PCHReader::ReadPCHBlock() { } ObjCCategoryImpls.swap(Record); break; + + case pch::ORIGINAL_FILE_NAME: + OriginalFileName.assign(BlobStart, BlobLen); + break; } } Error("premature end of bitstream in PCH file"); @@ -1364,6 +1368,87 @@ PCHReader::PCHReadResult PCHReader::ReadPCH(const std::string &FileName) { return Success; } +/// \brief Retrieve the name of the original source file name +/// directly from the PCH file, without actually loading the PCH +/// file. +std::string PCHReader::getOriginalSourceFile(const std::string &PCHFileName) { + // Open the PCH file. + std::string ErrStr; + llvm::OwningPtr Buffer; + Buffer.reset(llvm::MemoryBuffer::getFile(PCHFileName.c_str(), &ErrStr)); + if (!Buffer) { + fprintf(stderr, "error: %s\n", ErrStr.c_str()); + return std::string(); + } + + // Initialize the stream + llvm::BitstreamReader StreamFile; + llvm::BitstreamCursor Stream; + StreamFile.init((const unsigned char *)Buffer->getBufferStart(), + (const unsigned char *)Buffer->getBufferEnd()); + Stream.init(StreamFile); + + // Sniff for the signature. + if (Stream.Read(8) != 'C' || + Stream.Read(8) != 'P' || + Stream.Read(8) != 'C' || + Stream.Read(8) != 'H') { + fprintf(stderr, + "error: '%s' does not appear to be a precompiled header file\n", + PCHFileName.c_str()); + return std::string(); + } + + RecordData Record; + while (!Stream.AtEndOfStream()) { + unsigned Code = Stream.ReadCode(); + + if (Code == llvm::bitc::ENTER_SUBBLOCK) { + unsigned BlockID = Stream.ReadSubBlockID(); + + // We only know the PCH subblock ID. + switch (BlockID) { + case pch::PCH_BLOCK_ID: + if (Stream.EnterSubBlock(pch::PCH_BLOCK_ID)) { + fprintf(stderr, "error: malformed block record in PCH file\n"); + return std::string(); + } + break; + + default: + if (Stream.SkipBlock()) { + fprintf(stderr, "error: malformed block record in PCH file\n"); + return std::string(); + } + break; + } + continue; + } + + if (Code == llvm::bitc::END_BLOCK) { + if (Stream.ReadBlockEnd()) { + fprintf(stderr, "error: error at end of module block in PCH file\n"); + return std::string(); + } + continue; + } + + if (Code == llvm::bitc::DEFINE_ABBREV) { + Stream.ReadAbbrevRecord(); + continue; + } + + Record.clear(); + const char *BlobStart = 0; + unsigned BlobLen = 0; + if (Stream.ReadRecord(Code, Record, &BlobStart, &BlobLen) + == pch::ORIGINAL_FILE_NAME) + return std::string(BlobStart, BlobLen); + } + + return std::string(); +} + /// \brief Parse the record that corresponds to a LangOptions data /// structure. /// diff --git a/lib/Frontend/PCHWriter.cpp b/lib/Frontend/PCHWriter.cpp index fc6aa4709e..dbe0e536e5 100644 --- a/lib/Frontend/PCHWriter.cpp +++ b/lib/Frontend/PCHWriter.cpp @@ -33,6 +33,7 @@ #include "llvm/Bitcode/BitstreamWriter.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/System/Path.h" #include using namespace clang; @@ -442,16 +443,44 @@ void PCHWriter::WriteBlockInfoBlock() { /// \brief Write the PCH metadata (e.g., i686-apple-darwin9). -void PCHWriter::WriteMetadata(const TargetInfo &Target) { +void PCHWriter::WriteMetadata(ASTContext &Context) { using namespace llvm; - BitCodeAbbrev *Abbrev = new BitCodeAbbrev(); - Abbrev->Add(BitCodeAbbrevOp(pch::METADATA)); - Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // PCH major - Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // PCH minor - Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Clang major - Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Clang minor - Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Target triple - unsigned AbbrevCode = Stream.EmitAbbrev(Abbrev); + + // Original file name + SourceManager &SM = Context.getSourceManager(); + if (const FileEntry *MainFile = SM.getFileEntryForID(SM.getMainFileID())) { + BitCodeAbbrev *FileAbbrev = new BitCodeAbbrev(); + FileAbbrev->Add(BitCodeAbbrevOp(pch::ORIGINAL_FILE_NAME)); + FileAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name + unsigned FileAbbrevCode = Stream.EmitAbbrev(FileAbbrev); + + llvm::sys::Path MainFilePath(MainFile->getName()); + std::string MainFileName; + + if (!MainFilePath.isAbsolute()) { + llvm::sys::Path P = llvm::sys::Path::GetCurrentDirectory(); + P.appendComponent(MainFilePath.toString()); + MainFileName = P.toString(); + } else { + MainFileName = MainFilePath.toString(); + } + + RecordData Record; + Record.push_back(pch::ORIGINAL_FILE_NAME); + Stream.EmitRecordWithBlob(FileAbbrevCode, Record, MainFileName.c_str(), + MainFileName.size()); + } + + // Metadata + const TargetInfo &Target = Context.Target; + BitCodeAbbrev *MetaAbbrev = new BitCodeAbbrev(); + MetaAbbrev->Add(BitCodeAbbrevOp(pch::METADATA)); + MetaAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // PCH major + MetaAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // PCH minor + MetaAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Clang major + MetaAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Clang minor + MetaAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Target triple + unsigned MetaAbbrevCode = Stream.EmitAbbrev(MetaAbbrev); RecordData Record; Record.push_back(pch::METADATA); @@ -460,7 +489,7 @@ void PCHWriter::WriteMetadata(const TargetInfo &Target) { Record.push_back(CLANG_VERSION_MAJOR); Record.push_back(CLANG_VERSION_MINOR); const char *Triple = Target.getTargetTriple(); - Stream.EmitRecordWithBlob(AbbrevCode, Record, Triple, strlen(Triple)); + Stream.EmitRecordWithBlob(MetaAbbrevCode, Record, Triple, strlen(Triple)); } /// \brief Write the LangOptions structure. @@ -1672,7 +1701,7 @@ void PCHWriter::WritePCH(Sema &SemaRef, MemorizeStatCalls *StatCalls) { // Write the remaining PCH contents. RecordData Record; Stream.EnterSubblock(pch::PCH_BLOCK_ID, 4); - WriteMetadata(Context.Target); + WriteMetadata(Context); WriteLanguageOptions(Context.getLangOptions()); if (StatCalls) WriteStatCache(*StatCalls); diff --git a/test/PCH/preprocess.c b/test/PCH/preprocess.c new file mode 100644 index 0000000000..128cc0acb9 --- /dev/null +++ b/test/PCH/preprocess.c @@ -0,0 +1,5 @@ +// RUN: clang-cc -emit-pch -o %t %S/preprocess.h && +// RUN: clang-cc -include-pch %t -E -o - %s | grep -c "a_typedef" | count 1 +#include "preprocess.h" + +int a_value; diff --git a/test/PCH/preprocess.h b/test/PCH/preprocess.h new file mode 100644 index 0000000000..39fa006181 --- /dev/null +++ b/test/PCH/preprocess.h @@ -0,0 +1,7 @@ +// Helper header for preprocess.c PCH test +#ifndef PREPROCESS_H +#define PREPROCESS_H + +typedef int a_typedef; + +#endif // PREPROCESS_H diff --git a/tools/clang-cc/clang-cc.cpp b/tools/clang-cc/clang-cc.cpp index ca8d6e5117..a948f843e4 100644 --- a/tools/clang-cc/clang-cc.cpp +++ b/tools/clang-cc/clang-cc.cpp @@ -1267,7 +1267,8 @@ void InitializePreprocessorInitOptions(PreprocessorInitOptions &InitOpts) for (unsigned i = 0, e = ImplicitMacroIncludes.size(); i != e; ++i) InitOpts.addMacroInclude(ImplicitMacroIncludes[i]); - if (!ImplicitIncludePTH.empty() || !ImplicitIncludes.empty()) { + if (!ImplicitIncludePTH.empty() || !ImplicitIncludes.empty() || + (!ImplicitIncludePCH.empty() && ProgAction == PrintPreprocessedInput)) { // We want to add these paths to the predefines buffer in order, make a // temporary vector to sort by their occurrence. llvm::SmallVector, 8> OrderedPaths; @@ -1275,6 +1276,9 @@ void InitializePreprocessorInitOptions(PreprocessorInitOptions &InitOpts) if (!ImplicitIncludePTH.empty()) OrderedPaths.push_back(std::make_pair(ImplicitIncludePTH.getPosition(), &ImplicitIncludePTH)); + if (!ImplicitIncludePCH.empty() && ProgAction == PrintPreprocessedInput) + OrderedPaths.push_back(std::make_pair(ImplicitIncludePCH.getPosition(), + &ImplicitIncludePCH)); for (unsigned i = 0, e = ImplicitIncludes.size(); i != e; ++i) OrderedPaths.push_back(std::make_pair(ImplicitIncludes.getPosition(i), &ImplicitIncludes[i])); @@ -1288,9 +1292,18 @@ void InitializePreprocessorInitOptions(PreprocessorInitOptions &InitOpts) Ptr >= &ImplicitIncludes[0] && Ptr <= &ImplicitIncludes[ImplicitIncludes.size()-1]) { InitOpts.addInclude(*Ptr, false); - } else { - assert(Ptr == &ImplicitIncludePTH); + } else if (Ptr == &ImplicitIncludePTH) { InitOpts.addInclude(*Ptr, true); + } else { + // We end up here when we're producing preprocessed output and + // we loaded a PCH file. In this case, just include the header + // file that was used to build the precompiled header. + assert(Ptr == &ImplicitIncludePCH); + std::string OriginalFile = PCHReader::getOriginalSourceFile(*Ptr); + if (!OriginalFile.empty()) { + InitOpts.addInclude(OriginalFile, false); + ImplicitIncludePCH.clear(); + } } } } -- 2.40.0