#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/Support/MemoryBuffer.h"
+#include <map>
#include <vector>
#include <cassert>
class FileEntry;
class LineTableInfo;
class LangOptions;
+class ASTWriter;
+class ASTReader;
/// SrcMgr - Public enums and private classes that are part of the
/// SourceManager implementation.
/// if SourceLineCache is non-null.
unsigned NumLines;
+ /// \brief Lazily computed map of macro argument chunks to their expanded
+ /// source location.
+ typedef std::map<unsigned, SourceLocation> MacroArgsMap;
+ MacroArgsMap *MacroArgsCache;
+
/// getBuffer - Returns the memory buffer for the associated content.
///
/// \param Diag Object through which diagnostics will be emitted if the
ContentCache(const FileEntry *Ent = 0)
: Buffer(0, false), OrigEntry(Ent), ContentsEntry(Ent),
- SourceLineCache(0), NumLines(0) {}
+ SourceLineCache(0), NumLines(0), MacroArgsCache(0) {}
ContentCache(const FileEntry *Ent, const FileEntry *contentEnt)
: Buffer(0, false), OrigEntry(Ent), ContentsEntry(contentEnt),
- SourceLineCache(0), NumLines(0) {}
+ SourceLineCache(0), NumLines(0), MacroArgsCache(0) {}
~ContentCache();
/// a non-NULL Buffer or SourceLineCache. Ownership of allocated memory
/// is not transferred, so this is a logical error.
ContentCache(const ContentCache &RHS)
- : Buffer(0, false), SourceLineCache(0)
+ : Buffer(0, false), SourceLineCache(0), MacroArgsCache(0)
{
OrigEntry = RHS.OrigEntry;
ContentsEntry = RHS.ContentsEntry;
- assert (RHS.Buffer.getPointer() == 0 && RHS.SourceLineCache == 0
+ assert (RHS.Buffer.getPointer() == 0 && RHS.SourceLineCache == 0 &&
+ RHS.MacroArgsCache == 0
&& "Passed ContentCache object cannot own a buffer.");
NumLines = RHS.NumLines;
/// This is an invalid SLOC for the main file (top of the #include chain).
unsigned IncludeLoc; // Really a SourceLocation
+ /// \brief Number of FileIDs (files and macros) that were created during
+ /// preprocessing of this #include, including this SLocEntry.
+ /// Zero means the preprocessor didn't provide such info for this SLocEntry.
+ unsigned NumCreatedFIDs;
+
/// Data - This contains the ContentCache* and the bits indicating the
/// characteristic of the file and whether it has #line info, all bitmangled
/// together.
uintptr_t Data;
+
+ friend class SourceManager;
+ friend class ASTWriter;
+ friend class ASTReader;
public:
/// get - Return a FileInfo object.
static FileInfo get(SourceLocation IL, const ContentCache *Con,
CharacteristicKind FileCharacter) {
FileInfo X;
X.IncludeLoc = IL.getRawEncoding();
+ X.NumCreatedFIDs = 0;
X.Data = (uintptr_t)Con;
assert((X.Data & 7) == 0 &&"ContentCache pointer insufficiently aligned");
assert((unsigned)FileCharacter < 4 && "invalid file character");
/// \param Invalid If non-NULL, will be set true if an error occurred.
StringRef getBufferData(FileID FID, bool *Invalid = 0) const;
+ /// \brief Get the number of FileIDs (files and macros) that were created
+ /// during preprocessing of \arg FID, including it.
+ unsigned getNumCreatedFIDsForFileID(FileID FID) const {
+ bool Invalid = false;
+ const SrcMgr::SLocEntry &Entry = getSLocEntry(FID, &Invalid);
+ if (Invalid || !Entry.isFile())
+ return 0;
+
+ return Entry.getFile().NumCreatedFIDs;
+ }
+
+ /// \brief Set the number of FileIDs (files and macros) that were created
+ /// during preprocessing of \arg FID, including it.
+ void setNumCreatedFIDsForFileID(FileID FID, unsigned NumFIDs) const {
+ bool Invalid = false;
+ const SrcMgr::SLocEntry &Entry = getSLocEntry(FID, &Invalid);
+ if (Invalid || !Entry.isFile())
+ return;
+
+ assert(Entry.getFile().NumCreatedFIDs == 0 && "Already set!");
+ const_cast<SrcMgr::FileInfo &>(Entry.getFile()).NumCreatedFIDs = NumFIDs;
+ }
//===--------------------------------------------------------------------===//
// SourceLocation manipulation methods.
return SourceLocation::getFileLoc(FileOffset);
}
+ /// \brief Returns the include location if \arg FID is a #include'd file
+ /// otherwise it returns an invalid location.
+ SourceLocation getIncludeLoc(FileID FID) const {
+ bool Invalid = false;
+ const SrcMgr::SLocEntry &Entry = getSLocEntry(FID, &Invalid);
+ if (Invalid || !Entry.isFile())
+ return SourceLocation();
+
+ return Entry.getFile().getIncludeLoc();
+ }
+
/// getExpansionLoc - Given a SourceLocation object, return the expansion
/// location referenced by the ID.
SourceLocation getExpansionLoc(SourceLocation Loc) const {
std::pair<FileID, unsigned>
getDecomposedSpellingLocSlowCase(const SrcMgr::SLocEntry *E,
unsigned Offset) const;
+ void computeMacroArgsCache(SrcMgr::ContentCache *Content, FileID FID);
};
/// The SourceManager FileID corresponding to the file being lexed.
const FileID FID;
+ /// \brief Number of SLocEntries before lexing the file.
+ unsigned InitialNumSLocEntries;
+
//===--------------------------------------------------------------------===//
// Context-specific lexing flags set by the preprocessor.
//===--------------------------------------------------------------------===//
void operator=(const PreprocessorLexer&); // DO NOT IMPLEMENT
friend class Preprocessor;
- PreprocessorLexer(Preprocessor *pp, FileID fid)
- : PP(pp), FID(fid), ParsingPreprocessorDirective(false),
- ParsingFilename(false), LexingRawMode(false) {}
+ PreprocessorLexer(Preprocessor *pp, FileID fid);
PreprocessorLexer()
- : PP(0),
+ : PP(0), InitialNumSLocEntries(0),
ParsingPreprocessorDirective(false),
ParsingFilename(false),
LexingRawMode(false) {}
return FID;
}
+ /// \brief Number of SLocEntries before lexing the file.
+ unsigned getInitialNumSLocEntries() const {
+ return InitialNumSLocEntries;
+ }
+
/// getFileEntry - Return the FileEntry corresponding to this FileID. Like
/// getFileID(), this only works for lexers with attached preprocessors.
const FileEntry *getFileEntry() const;
#include "clang/Basic/FileManager.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
ContentCache::~ContentCache() {
if (shouldFreeBuffer())
delete Buffer.getPointer();
+ delete MacroArgsCache;
}
/// getSizeBytesMapped - Returns the number of bytes actually mapped for this
return getLocForStartOfFile(FirstFID).getFileLocWithOffset(FilePos + Col - 1);
}
-/// \brief If \arg Loc points inside a function macro argument, the returned
-/// location will be the macro location in which the argument was expanded.
-/// If a macro argument is used multiple times, the expanded location will
-/// be at the first expansion of the argument.
-/// e.g.
-/// MY_MACRO(foo);
-/// ^
-/// Passing a file location pointing at 'foo', will yield a macro location
-/// where 'foo' was expanded into.
-SourceLocation SourceManager::getMacroArgExpandedLocation(SourceLocation Loc) {
- if (Loc.isInvalid())
- return Loc;
-
- FileID FID = getFileID(Loc);
- if (FID.isInvalid())
- return Loc;
+/// \brief Compute a map of macro argument chunks to their expanded source
+/// location. Chunks that are not part of a macro argument will map to an
+/// invalid source location. e.g. if a file contains one macro argument at
+/// offset 100 with length 10, this is how the map will be formed:
+/// 0 -> SourceLocation()
+/// 100 -> Expanded macro arg location
+/// 110 -> SourceLocation()
+void SourceManager::computeMacroArgsCache(ContentCache *Content, FileID FID) {
+ assert(!Content->MacroArgsCache);
+ assert(!FID.isInvalid());
+
+ Content->MacroArgsCache = new ContentCache::MacroArgsMap();
+ ContentCache::MacroArgsMap &MacroArgsCache = *Content->MacroArgsCache;
+ // Initially no macro argument chunk is present.
+ MacroArgsCache.insert(std::make_pair(0, SourceLocation()));
int ID = FID.ID;
while (1) {
// Stop if there are no more FileIDs to check.
if (ID > 0) {
if (unsigned(ID) >= local_sloc_entry_size())
- return Loc;
+ return;
} else if (ID == -1) {
- return Loc;
+ return;
}
const SrcMgr::SLocEntry &Entry = getSLocEntryByID(ID);
if (Entry.isFile()) {
- if (Entry.getFile().getIncludeLoc().isValid() &&
- !isBeforeInTranslationUnit(Entry.getFile().getIncludeLoc(), Loc))
- return Loc;
+ SourceLocation IncludeLoc = Entry.getFile().getIncludeLoc();
+ if (IncludeLoc.isInvalid())
+ continue;
+ if (!isInFileID(IncludeLoc, FID))
+ return; // No more files/macros that may be "contained" in this file.
+
+ // Skip the files/macros of the #include'd file, we only care about macros
+ // that lexed macro arguments from our file.
+ if (Entry.getFile().NumCreatedFIDs)
+ ID += Entry.getFile().NumCreatedFIDs - 1/*because of next ++ID*/;
continue;
}
-
- if (isBeforeInTranslationUnit(Loc,
- Entry.getExpansion().getExpansionLocStart()))
- return Loc;
+
if (!Entry.getExpansion().isMacroArgExpansion())
continue;
-
- // This is a macro argument expansion. See if Loc points in the argument
- // that was lexed.
-
- SourceLocation SpellLoc = Entry.getExpansion().getSpellingLoc();
- unsigned BeginOffs = SpellLoc.getOffset();
+
+ SourceLocation SpellLoc =
+ getSpellingLoc(Entry.getExpansion().getSpellingLoc());
+ unsigned BeginOffs;
+ if (!isInFileID(SpellLoc, FID, &BeginOffs))
+ return; // No more files/macros that may be "contained" in this file.
unsigned EndOffs = BeginOffs + getFileIDSize(FileID::get(ID));
- if (BeginOffs <= Loc.getOffset() && Loc.getOffset() < EndOffs) {
- SourceLocation ExpandLoc = SourceLocation::getMacroLoc(Entry.getOffset());
- // Replace current Loc with the expanded location and continue.
- // The expanded argument may end up being passed to another function macro
- // and relexed again.
- Loc = ExpandLoc.getFileLocWithOffset(Loc.getOffset()-BeginOffs);
- }
+
+ // Add a new chunk for this macro argument. A previous macro argument chunk
+ // may have been lexed again, so e.g. if the map is
+ // 0 -> SourceLocation()
+ // 100 -> Expanded loc #1
+ // 110 -> SourceLocation()
+ // and we found a new macro FileID that lexed from offet 105 with length 3,
+ // the new map will be:
+ // 0 -> SourceLocation()
+ // 100 -> Expanded loc #1
+ // 105 -> Expanded loc #2
+ // 108 -> Expanded loc #1
+ // 110 -> SourceLocation()
+ //
+ // Since re-lexed macro chunks will always be the same size or less of
+ // previous chunks, we only need to find where the ending of the new macro
+ // chunk is mapped to and update the map with new begin/end mappings.
+
+ ContentCache::MacroArgsMap::iterator I= MacroArgsCache.upper_bound(EndOffs);
+ --I;
+ SourceLocation EndOffsMappedLoc = I->second;
+ MacroArgsCache[BeginOffs] = SourceLocation::getMacroLoc(Entry.getOffset());
+ MacroArgsCache[EndOffs] = EndOffsMappedLoc;
}
}
+/// \brief If \arg Loc points inside a function macro argument, the returned
+/// location will be the macro location in which the argument was expanded.
+/// If a macro argument is used multiple times, the expanded location will
+/// be at the first expansion of the argument.
+/// e.g.
+/// MY_MACRO(foo);
+/// ^
+/// Passing a file location pointing at 'foo', will yield a macro location
+/// where 'foo' was expanded into.
+SourceLocation SourceManager::getMacroArgExpandedLocation(SourceLocation Loc) {
+ if (Loc.isInvalid() || !Loc.isFileID())
+ return Loc;
+
+ FileID FID;
+ unsigned Offset;
+ llvm::tie(FID, Offset) = getDecomposedLoc(Loc);
+ if (FID.isInvalid())
+ return Loc;
+
+ ContentCache *Content
+ = const_cast<ContentCache *>(getSLocEntry(FID).getFile().getContentCache());
+ if (!Content->MacroArgsCache)
+ computeMacroArgsCache(Content, FID);
+
+ assert(Content->MacroArgsCache);
+ assert(!Content->MacroArgsCache->empty());
+ ContentCache::MacroArgsMap::iterator
+ I = Content->MacroArgsCache->upper_bound(Offset);
+ --I;
+
+ unsigned MacroArgBeginOffs = I->first;
+ SourceLocation MacroArgExpandedLoc = I->second;
+ if (MacroArgExpandedLoc.isValid())
+ return MacroArgExpandedLoc.getFileLocWithOffset(Offset - MacroArgBeginOffs);
+
+ return Loc;
+}
+
/// Given a decomposed source location, move it up the include/expansion stack
/// to the parent source location. If this is possible, return the decomposed
/// version of the parent in Loc and return false. If Loc is the top-level
<< "B of Sloc address space used.\n";
unsigned NumLineNumsComputed = 0;
+ unsigned NumMacroArgsComputed = 0;
unsigned NumFileBytesMapped = 0;
for (fileinfo_iterator I = fileinfo_begin(), E = fileinfo_end(); I != E; ++I){
NumLineNumsComputed += I->second->SourceLineCache != 0;
+ NumMacroArgsComputed += I->second->MacroArgsCache != 0;
NumFileBytesMapped += I->second->getSizeBytesMapped();
}
llvm::errs() << NumFileBytesMapped << " bytes of files mapped, "
- << NumLineNumsComputed << " files with line #'s computed.\n";
+ << NumLineNumsComputed << " files with line #'s computed, "
+ << NumMacroArgsComputed << " files with macro args computed.\n";
llvm::errs() << "FileID scans: " << NumLinearScans << " linear, "
<< NumBinaryProbes << " binary.\n";
}
// If this is a #include'd file, pop it off the include stack and continue
// lexing the #includer file.
if (!IncludeMacroStack.empty()) {
+ if (!isEndOfMacro && CurPPLexer &&
+ SourceMgr.getIncludeLoc(CurPPLexer->getFileID()).isValid()) {
+ // Notify SourceManager to record the number of FileIDs that were created
+ // during lexing of the #include'd file.
+ unsigned NumFIDs =
+ SourceMgr.local_sloc_entry_size() -
+ CurPPLexer->getInitialNumSLocEntries() + 1/*#include'd file*/;
+ SourceMgr.setNumCreatedFIDsForFileID(CurPPLexer->getFileID(), NumFIDs);
+ }
+
// We're done with the #included file.
RemoveTopOfLexerStack();
#include "clang/Basic/SourceManager.h"
using namespace clang;
+PreprocessorLexer::PreprocessorLexer(Preprocessor *pp, FileID fid)
+ : PP(pp), FID(fid), InitialNumSLocEntries(0),
+ ParsingPreprocessorDirective(false),
+ ParsingFilename(false), LexingRawMode(false) {
+ if (pp)
+ InitialNumSLocEntries = pp->getSourceManager().local_sloc_entry_size();
+}
+
/// LexIncludeFilename - After the preprocessor has parsed a #include, lex and
/// (potentially) macro expand the filename.
void PreprocessorLexer::LexIncludeFilename(Token &FilenameTok) {
FileID FID = SourceMgr.createFileID(File, IncludeLoc,
(SrcMgr::CharacteristicKind)Record[2],
ID, BaseOffset + Record[0]);
+ SrcMgr::FileInfo &FileInfo =
+ const_cast<SrcMgr::FileInfo&>(SourceMgr.getSLocEntry(FID).getFile());
+ FileInfo.NumCreatedFIDs = Record[6];
if (Record[3])
- const_cast<SrcMgr::FileInfo&>(SourceMgr.getSLocEntry(FID).getFile())
- .setHasLineDirectives();
+ FileInfo.setHasLineDirectives();
break;
}
// FileEntry fields.
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 12)); // Size
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 32)); // Modification time
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // NumCreatedFIDs
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name
return Stream.EmitAbbrev(Abbrev);
}
Record.push_back(Content->OrigEntry->getSize());
Record.push_back(Content->OrigEntry->getModificationTime());
+ Record.push_back(File.NumCreatedFIDs);
+
// Turn the file name into an absolute path, if it isn't already.
const char *Filename = Content->OrigEntry->getName();
llvm::SmallString<128> FilePath(Filename);