Make PCHReader cope with PCH files containing more than one predefines buffer.

llvm-svn: 108340
This commit is contained in:
Sebastian Redl 2010-07-14 17:49:11 +00:00
parent a90af1ba38
commit 75fbb3b5e5
5 changed files with 137 additions and 49 deletions

View File

@ -65,6 +65,15 @@ class SwitchCase;
class PCHReader; class PCHReader;
struct HeaderFileInfo; struct HeaderFileInfo;
struct PCHPredefinesBlock {
/// \brief The file ID for this predefines buffer in a PCH file.
FileID BufferID;
/// \brief This predefines buffer in a PCH file.
llvm::StringRef Data;
};
typedef llvm::SmallVector<PCHPredefinesBlock, 2> PCHPredefinesBlocks;
/// \brief Abstract interface for callback invocations by the PCHReader. /// \brief Abstract interface for callback invocations by the PCHReader.
/// ///
/// While reading a PCH file, the PCHReader will call the methods of the /// While reading a PCH file, the PCHReader will call the methods of the
@ -103,8 +112,7 @@ public:
/// here. /// here.
/// ///
/// \returns true to indicate the predefines are invalid or false otherwise. /// \returns true to indicate the predefines are invalid or false otherwise.
virtual bool ReadPredefinesBuffer(llvm::StringRef PCHPredef, virtual bool ReadPredefinesBuffer(const PCHPredefinesBlocks &Buffers,
FileID PCHBufferID,
llvm::StringRef OriginalFileName, llvm::StringRef OriginalFileName,
std::string &SuggestedPredefines) { std::string &SuggestedPredefines) {
return false; return false;
@ -131,8 +139,7 @@ public:
virtual bool ReadLanguageOptions(const LangOptions &LangOpts); virtual bool ReadLanguageOptions(const LangOptions &LangOpts);
virtual bool ReadTargetTriple(llvm::StringRef Triple); virtual bool ReadTargetTriple(llvm::StringRef Triple);
virtual bool ReadPredefinesBuffer(llvm::StringRef PCHPredef, virtual bool ReadPredefinesBuffer(const PCHPredefinesBlocks &Buffers,
FileID PCHBufferID,
llvm::StringRef OriginalFileName, llvm::StringRef OriginalFileName,
std::string &SuggestedPredefines); std::string &SuggestedPredefines);
virtual void ReadHeaderFileInfo(const HeaderFileInfo &HFI, unsigned ID); virtual void ReadHeaderFileInfo(const HeaderFileInfo &HFI, unsigned ID);
@ -483,15 +490,9 @@ private:
~ReadingKindTracker() { Reader.ReadingKind = PrevKind; } ~ReadingKindTracker() { Reader.ReadingKind = PrevKind; }
}; };
/// \brief The file ID for the predefines buffer in the PCH file. /// \brief All predefines buffers in all PCH files, to be treated as if
FileID PCHPredefinesBufferID; /// concatenated.
PCHPredefinesBlocks PCHPredefinesBuffers;
/// \brief Pointer to the beginning of the predefines buffer in the
/// PCH file.
const char *PCHPredefines;
/// \brief Length of the predefines buffer in the PCH file.
unsigned PCHPredefinesLen;
/// \brief Suggested contents of the predefines buffer, after this /// \brief Suggested contents of the predefines buffer, after this
/// PCH file has been processed. /// PCH file has been processed.
@ -509,7 +510,7 @@ private:
void MaybeAddSystemRootToFilename(std::string &Filename); void MaybeAddSystemRootToFilename(std::string &Filename);
PCHReadResult ReadPCHBlock(); PCHReadResult ReadPCHBlock();
bool CheckPredefinesBuffer(llvm::StringRef PCHPredef, FileID PCHBufferID); bool CheckPredefinesBuffers();
bool ParseLineTable(llvm::SmallVectorImpl<uint64_t> &Record); bool ParseLineTable(llvm::SmallVectorImpl<uint64_t> &Record);
PCHReadResult ReadSourceManagerBlock(); PCHReadResult ReadSourceManagerBlock();
PCHReadResult ReadSLocEntryRecord(unsigned ID); PCHReadResult ReadSLocEntryRecord(unsigned ID);

View File

@ -140,8 +140,86 @@ bool PCHValidator::ReadTargetTriple(llvm::StringRef Triple) {
return true; return true;
} }
bool PCHValidator::ReadPredefinesBuffer(llvm::StringRef PCHPredef, struct EmptyStringRef {
FileID PCHBufferID, bool operator ()(const llvm::StringRef &r) const { return r.empty(); }
};
struct EmptyBlock {
bool operator ()(const PCHPredefinesBlock &r) const { return r.Data.empty(); }
};
static bool EqualConcatenations(llvm::SmallVector<llvm::StringRef, 2> L,
PCHPredefinesBlocks R) {
// First, sum up the lengths.
unsigned LL = 0, RL = 0;
for (unsigned I = 0, N = L.size(); I != N; ++I) {
LL += L[I].size();
}
for (unsigned I = 0, N = R.size(); I != N; ++I) {
RL += R[I].Data.size();
}
if (LL != RL)
return false;
if (LL == 0 && RL == 0)
return true;
// Kick out empty parts, they confuse the algorithm below.
L.erase(std::remove_if(L.begin(), L.end(), EmptyStringRef()), L.end());
R.erase(std::remove_if(R.begin(), R.end(), EmptyBlock()), R.end());
// Do it the hard way. At this point, both vectors must be non-empty.
llvm::StringRef LR = L[0], RR = R[0].Data;
unsigned LI = 0, RI = 0, LN = L.size(), RN = R.size();
for (;;) {
// Compare the current pieces.
if (LR.size() == RR.size()) {
// If they're the same length, it's pretty easy.
if (LR != RR)
return false;
// Both pieces are done, advance.
++LI;
++RI;
// If either string is done, they're both done, since they're the same
// length.
if (LI == LN) {
assert(RI == RN && "Strings not the same length after all?");
return true;
}
LR = L[LI];
RR = R[RI].Data;
} else if (LR.size() < RR.size()) {
// Right piece is longer.
if (!RR.startswith(LR))
return false;
++LI;
assert(LI != LN && "Strings not the same length after all?");
RR = RR.substr(LR.size());
LR = L[LI];
} else {
// Left piece is longer.
if (!LR.startswith(RR))
return false;
++RI;
assert(RI != RN && "Strings not the same length after all?");
LR = LR.substr(RR.size());
RR = R[RI].Data;
}
}
}
static std::pair<FileID, llvm::StringRef::size_type>
FindMacro(const PCHPredefinesBlocks &Buffers, llvm::StringRef MacroDef) {
std::pair<FileID, llvm::StringRef::size_type> Res;
for (unsigned I = 0, N = Buffers.size(); I != N; ++I) {
Res.second = Buffers[I].Data.find(MacroDef);
if (Res.second != llvm::StringRef::npos) {
Res.first = Buffers[I].BufferID;
break;
}
}
return Res;
}
bool PCHValidator::ReadPredefinesBuffer(const PCHPredefinesBlocks &Buffers,
llvm::StringRef OriginalFileName, llvm::StringRef OriginalFileName,
std::string &SuggestedPredefines) { std::string &SuggestedPredefines) {
// We are in the context of an implicit include, so the predefines buffer will // We are in the context of an implicit include, so the predefines buffer will
@ -160,9 +238,15 @@ bool PCHValidator::ReadPredefinesBuffer(llvm::StringRef PCHPredef,
return true; return true;
} }
// If the predefines is equal to the joined left and right halves, we're done! // If the concatenation of all the PCH buffers is equal to the adjusted
if (Left.size() + Right.size() == PCHPredef.size() && // command line, we're done.
PCHPredef.startswith(Left) && PCHPredef.endswith(Right)) // We build a SmallVector of the command line here, because we'll eventually
// need to support an arbitrary amount of pieces anyway (when we have chained
// PCH reading).
llvm::SmallVector<llvm::StringRef, 2> CommandLine;
CommandLine.push_back(Left);
CommandLine.push_back(Right);
if (EqualConcatenations(CommandLine, Buffers))
return false; return false;
SourceManager &SourceMgr = PP.getSourceManager(); SourceManager &SourceMgr = PP.getSourceManager();
@ -170,7 +254,8 @@ bool PCHValidator::ReadPredefinesBuffer(llvm::StringRef PCHPredef,
// The predefines buffers are different. Determine what the differences are, // The predefines buffers are different. Determine what the differences are,
// and whether they require us to reject the PCH file. // and whether they require us to reject the PCH file.
llvm::SmallVector<llvm::StringRef, 8> PCHLines; llvm::SmallVector<llvm::StringRef, 8> PCHLines;
PCHPredef.split(PCHLines, "\n", /*MaxSplit=*/-1, /*KeepEmpty=*/false); for (unsigned I = 0, N = Buffers.size(); I != N; ++I)
Buffers[I].Data.split(PCHLines, "\n", /*MaxSplit=*/-1, /*KeepEmpty=*/false);
llvm::SmallVector<llvm::StringRef, 8> CmdLineLines; llvm::SmallVector<llvm::StringRef, 8> CmdLineLines;
Left.split(CmdLineLines, "\n", /*MaxSplit=*/-1, /*KeepEmpty=*/false); Left.split(CmdLineLines, "\n", /*MaxSplit=*/-1, /*KeepEmpty=*/false);
@ -235,10 +320,12 @@ bool PCHValidator::ReadPredefinesBuffer(llvm::StringRef PCHPredef,
<< MacroName; << MacroName;
// Show the definition of this macro within the PCH file. // Show the definition of this macro within the PCH file.
llvm::StringRef::size_type Offset = PCHPredef.find(Missing); std::pair<FileID, llvm::StringRef::size_type> MacroLoc =
assert(Offset != llvm::StringRef::npos && "Unable to find macro!"); FindMacro(Buffers, Missing);
SourceLocation PCHMissingLoc = SourceMgr.getLocForStartOfFile(PCHBufferID) assert(MacroLoc.second!=llvm::StringRef::npos && "Unable to find macro!");
.getFileLocWithOffset(Offset); SourceLocation PCHMissingLoc =
SourceMgr.getLocForStartOfFile(MacroLoc.first)
.getFileLocWithOffset(MacroLoc.second);
Reader.Diag(PCHMissingLoc, diag::note_pch_macro_defined_as) << MacroName; Reader.Diag(PCHMissingLoc, diag::note_pch_macro_defined_as) << MacroName;
ConflictingDefines = true; ConflictingDefines = true;
@ -256,10 +343,12 @@ bool PCHValidator::ReadPredefinesBuffer(llvm::StringRef PCHPredef,
} }
// Show the definition of this macro within the PCH file. // Show the definition of this macro within the PCH file.
llvm::StringRef::size_type Offset = PCHPredef.find(Missing); std::pair<FileID, llvm::StringRef::size_type> MacroLoc =
assert(Offset != llvm::StringRef::npos && "Unable to find macro!"); FindMacro(Buffers, Missing);
SourceLocation PCHMissingLoc = SourceMgr.getLocForStartOfFile(PCHBufferID) assert(MacroLoc.second!=llvm::StringRef::npos && "Unable to find macro!");
.getFileLocWithOffset(Offset); SourceLocation PCHMissingLoc =
SourceMgr.getLocForStartOfFile(MacroLoc.first)
.getFileLocWithOffset(MacroLoc.second);
Reader.Diag(PCHMissingLoc, diag::note_using_macro_def_from_pch); Reader.Diag(PCHMissingLoc, diag::note_using_macro_def_from_pch);
} }
@ -609,27 +698,18 @@ void PCHReader::Error(const char *Msg) {
Diag(diag::err_fe_pch_malformed) << Msg; Diag(diag::err_fe_pch_malformed) << Msg;
} }
/// \brief Check the contents of the predefines buffer against the /// \brief Check the contents of the concatenation of all predefines buffers in
/// contents of the predefines buffer used to build the PCH file. /// the PCH chain against the contents of the predefines buffer of the current
/// compiler invocation.
/// ///
/// The contents of the two predefines buffers should be the same. If /// The contents should be the same. If not, then some command-line option
/// not, then some command-line option changed the preprocessor state /// changed the preprocessor state and we must probably reject the PCH file.
/// and we must reject the PCH file.
///
/// \param PCHPredef The start of the predefines buffer in the PCH
/// file.
///
/// \param PCHPredefLen The length of the predefines buffer in the PCH
/// file.
///
/// \param PCHBufferID The FileID for the PCH predefines buffer.
/// ///
/// \returns true if there was a mismatch (in which case the PCH file /// \returns true if there was a mismatch (in which case the PCH file
/// should be ignored), or false otherwise. /// should be ignored), or false otherwise.
bool PCHReader::CheckPredefinesBuffer(llvm::StringRef PCHPredef, bool PCHReader::CheckPredefinesBuffers() {
FileID PCHBufferID) {
if (Listener) if (Listener)
return Listener->ReadPredefinesBuffer(PCHPredef, PCHBufferID, return Listener->ReadPredefinesBuffer(PCHPredefinesBuffers,
ActualOriginalFileName, ActualOriginalFileName,
SuggestedPredefines); SuggestedPredefines);
return false; return false;
@ -958,9 +1038,11 @@ PCHReader::PCHReadResult PCHReader::ReadSLocEntryRecord(unsigned ID) {
FileID BufferID = SourceMgr.createFileIDForMemBuffer(Buffer, ID, Offset); FileID BufferID = SourceMgr.createFileIDForMemBuffer(Buffer, ID, Offset);
if (strcmp(Name, "<built-in>") == 0) { if (strcmp(Name, "<built-in>") == 0) {
PCHPredefinesBufferID = BufferID; PCHPredefinesBlock Block = {
PCHPredefines = BlobStart; BufferID,
PCHPredefinesLen = BlobLen - 1; llvm::StringRef(BlobStart, BlobLen - 1)
};
PCHPredefinesBuffers.push_back(Block);
} }
break; break;
@ -1636,8 +1718,7 @@ PCHReader::PCHReadResult PCHReader::ReadPCH(const std::string &FileName) {
} }
// Check the predefines buffer. // Check the predefines buffer.
if (CheckPredefinesBuffer(llvm::StringRef(PCHPredefines, PCHPredefinesLen), if (CheckPredefinesBuffers())
PCHPredefinesBufferID))
return IgnorePCH; return IgnorePCH;
if (PP) { if (PP) {

6
clang/test/PCH/pchpch.c Normal file
View File

@ -0,0 +1,6 @@
// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-pch -o %t1 %S/pchpch1.h
// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-pch -o %t2 %S/pchpch2.h -include-pch %t1
// RUN: %clang_cc1 -triple i386-unknown-unknown -fsyntax-only %s -include-pch %t2
// The purpose of this test is to make sure that a PCH created while including
// an existing PCH can be loaded.

0
clang/test/PCH/pchpch1.h Normal file
View File

0
clang/test/PCH/pchpch2.h Normal file
View File