[cfe-commits] r109202 - in /cfe/trunk: include/clang-c/Index.h include/clang/Frontend/ASTUnit.h include/clang/Frontend/PreprocessorOptions.h lib/Frontend/ASTUnit.cpp lib/Frontend/InitPreprocessor.cpp tools/c-index-test/c-index-test.c tools/libclang/CIndex.cpp
Douglas Gregor
dgregor at apple.com
Thu Jul 22 17:33:23 PDT 2010
Author: dgregor
Date: Thu Jul 22 19:33:23 2010
New Revision: 109202
URL: http://llvm.org/viewvc/llvm-project?rev=109202&view=rev
Log:
Basic plumbing for generating a precompiled preamble for an
ASTUnit/CXTranslationUnit. We can't actually use this preamble yet,
however.
Modified:
cfe/trunk/include/clang-c/Index.h
cfe/trunk/include/clang/Frontend/ASTUnit.h
cfe/trunk/include/clang/Frontend/PreprocessorOptions.h
cfe/trunk/lib/Frontend/ASTUnit.cpp
cfe/trunk/lib/Frontend/InitPreprocessor.cpp
cfe/trunk/tools/c-index-test/c-index-test.c
cfe/trunk/tools/libclang/CIndex.cpp
Modified: cfe/trunk/include/clang-c/Index.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang-c/Index.h?rev=109202&r1=109201&r2=109202&view=diff
==============================================================================
--- cfe/trunk/include/clang-c/Index.h (original)
+++ cfe/trunk/include/clang-c/Index.h Thu Jul 22 19:33:23 2010
@@ -634,13 +634,13 @@
CINDEX_LINKAGE CXTranslationUnit clang_createTranslationUnit(CXIndex,
const char *ast_filename);
- /**
- * \brief Flags that control the creation of translation units.
- *
- * The enumerators in this enumeration type are meant to be bitwise
- * ORed together to specify which options should be used when
- * constructing the translation unit.
- */
+/**
+ * \brief Flags that control the creation of translation units.
+ *
+ * The enumerators in this enumeration type are meant to be bitwise
+ * ORed together to specify which options should be used when
+ * constructing the translation unit.
+ */
enum CXTranslationUnit_Flags {
/**
* \brief Used to indicate that no special translation-unit options are
@@ -658,7 +658,35 @@
* applications that require more detailed information about the
* behavior of the preprocessor.
*/
- CXTranslationUnit_DetailedPreprocessingRecord = 0x01
+ CXTranslationUnit_DetailedPreprocessingRecord = 0x01,
+
+ /**
+ * \brief A flag that indicates that the intent of parsing the
+ * given translation unit is for live editing of the file.
+ *
+ * This flag is essentially a meta-flag that callers can use to indicate
+ * that the translation unit is being edited and, therefore, is likely to
+ * be reparsed many times. It enables an unspecified set of optimizations
+ * (e.g., the precompiled preamble) geared toward improving the performance
+ * of \c clang_reparseTranslationUnit().
+ */
+ CXTranslationUnit_Editing = 0x02,
+
+ /**
+ * \brief Used to indicate that the translation unit should be built with an
+ * implicit precompiled header for the preamble.
+ *
+ * An implicit precompiled header is used as an optimization when a
+ * particular translation unit is likely to be reparsed many times
+ * when the sources aren't changing that often. In this case, an
+ * implicit precompiled header will be built containing all of the
+ * initial includes at the top of the main file (what we refer to as
+ * the "preamble" of the file). In subsequent parses, if the
+ * preamble or the files in it have not changed, \c
+ * clang_reparseTranslationUnit() will re-use the implicit
+ * precompiled header to improve parsing performance.
+ */
+ CXTranslationUnit_PrecompiledPreamble = 0x04
};
/**
Modified: cfe/trunk/include/clang/Frontend/ASTUnit.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/ASTUnit.h?rev=109202&r1=109201&r2=109202&view=diff
==============================================================================
--- cfe/trunk/include/clang/Frontend/ASTUnit.h (original)
+++ cfe/trunk/include/clang/Frontend/ASTUnit.h Thu Jul 22 19:33:23 2010
@@ -46,6 +46,14 @@
using namespace idx;
+class PrecompiledPreamble {
+ llvm::sys::Path PreambleFile;
+
+public:
+ ~PrecompiledPreamble();
+
+};
+
/// \brief Utility class for loading a ASTContext from a PCH file.
///
class ASTUnit {
@@ -125,6 +133,7 @@
void CleanTemporaryFiles();
bool Parse();
+ void BuildPrecompiledPreamble();
public:
class ConcurrencyCheck {
@@ -241,7 +250,8 @@
static ASTUnit *LoadFromCompilerInvocation(CompilerInvocation *CI,
llvm::IntrusiveRefCntPtr<Diagnostic> Diags,
bool OnlyLocalDecls = false,
- bool CaptureDiagnostics = false);
+ bool CaptureDiagnostics = false,
+ bool PrecompilePreamble = false);
/// LoadFromCommandLine - Create an ASTUnit from a vector of command line
/// arguments, which must specify exactly one source file.
@@ -264,7 +274,8 @@
bool OnlyLocalDecls = false,
RemappedFile *RemappedFiles = 0,
unsigned NumRemappedFiles = 0,
- bool CaptureDiagnostics = false);
+ bool CaptureDiagnostics = false,
+ bool PrecompilePreamble = false);
/// \brief Reparse the source files using the same command-line options that
/// were originally used to produce this translation unit.
Modified: cfe/trunk/include/clang/Frontend/PreprocessorOptions.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/PreprocessorOptions.h?rev=109202&r1=109201&r2=109202&view=diff
==============================================================================
--- cfe/trunk/include/clang/Frontend/PreprocessorOptions.h (original)
+++ cfe/trunk/include/clang/Frontend/PreprocessorOptions.h Thu Jul 22 19:33:23 2010
@@ -62,21 +62,37 @@
std::vector<std::pair<std::string, const llvm::MemoryBuffer *> >
RemappedFileBuffers;
- typedef std::vector<std::pair<std::string, std::string> >::const_iterator
+ typedef std::vector<std::pair<std::string, std::string> >::iterator
remapped_file_iterator;
- remapped_file_iterator remapped_file_begin() const {
+ typedef std::vector<std::pair<std::string, std::string> >::const_iterator
+ const_remapped_file_iterator;
+ remapped_file_iterator remapped_file_begin() {
return RemappedFiles.begin();
}
- remapped_file_iterator remapped_file_end() const {
+ const_remapped_file_iterator remapped_file_begin() const {
+ return RemappedFiles.begin();
+ }
+ remapped_file_iterator remapped_file_end() {
+ return RemappedFiles.end();
+ }
+ const_remapped_file_iterator remapped_file_end() const {
return RemappedFiles.end();
}
typedef std::vector<std::pair<std::string, const llvm::MemoryBuffer *> >::
- const_iterator remapped_file_buffer_iterator;
- remapped_file_buffer_iterator remapped_file_buffer_begin() const {
+ iterator remapped_file_buffer_iterator;
+ typedef std::vector<std::pair<std::string, const llvm::MemoryBuffer *> >::
+ const_iterator const_remapped_file_buffer_iterator;
+ remapped_file_buffer_iterator remapped_file_buffer_begin() {
+ return RemappedFileBuffers.begin();
+ }
+ const_remapped_file_buffer_iterator remapped_file_buffer_begin() const {
return RemappedFileBuffers.begin();
}
- remapped_file_buffer_iterator remapped_file_buffer_end() const {
+ remapped_file_buffer_iterator remapped_file_buffer_end() {
+ return RemappedFileBuffers.end();
+ }
+ const_remapped_file_buffer_iterator remapped_file_buffer_end() const {
return RemappedFileBuffers.end();
}
@@ -92,9 +108,20 @@
void addRemappedFile(llvm::StringRef From, llvm::StringRef To) {
RemappedFiles.push_back(std::make_pair(From, To));
}
+
+ remapped_file_iterator eraseRemappedFile(remapped_file_iterator Remapped) {
+ return RemappedFiles.erase(Remapped);
+ }
+
void addRemappedFile(llvm::StringRef From, const llvm::MemoryBuffer * To) {
RemappedFileBuffers.push_back(std::make_pair(From, To));
}
+
+ remapped_file_buffer_iterator
+ eraseRemappedFile(remapped_file_buffer_iterator Remapped) {
+ return RemappedFileBuffers.erase(Remapped);
+ }
+
void clearRemappedFiles() {
RemappedFiles.clear();
RemappedFileBuffers.clear();
Modified: cfe/trunk/lib/Frontend/ASTUnit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/ASTUnit.cpp?rev=109202&r1=109201&r2=109202&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/ASTUnit.cpp (original)
+++ cfe/trunk/lib/Frontend/ASTUnit.cpp Thu Jul 22 19:33:23 2010
@@ -33,8 +33,13 @@
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/System/Host.h"
#include "llvm/System/Path.h"
+#include <cstdlib>
using namespace clang;
+PrecompiledPreamble::~PrecompiledPreamble() {
+ PreambleFile.eraseFromDisk();
+}
+
ASTUnit::ASTUnit(bool _MainFileIsAST)
: CaptureDiagnostics(false), MainFileIsAST(_MainFileIsAST),
ConcurrencyCheckValue(CheckUnlocked) { }
@@ -399,11 +404,195 @@
return true;
}
+/// \brief Simple function to retrieve a path for a preamble precompiled header.
+static std::string GetPreamblePCHPath() {
+ // FIXME: This is lame; sys::Path should provide this function (in particular,
+ // it should know how to find the temporary files dir).
+ // FIXME: This is really lame. I copied this code from the Driver!
+ std::string Error;
+ const char *TmpDir = ::getenv("TMPDIR");
+ if (!TmpDir)
+ TmpDir = ::getenv("TEMP");
+ if (!TmpDir)
+ TmpDir = ::getenv("TMP");
+ if (!TmpDir)
+ TmpDir = "/tmp";
+ llvm::sys::Path P(TmpDir);
+ P.appendComponent("preamble");
+ if (P.createTemporaryFileOnDisk())
+ return std::string();
+
+ P.appendSuffix("pch");
+ return P.str();
+}
+
+void ASTUnit::BuildPrecompiledPreamble() {
+ CompilerInvocation PreambleInvocation(*Invocation);
+ FrontendOptions &FrontendOpts = PreambleInvocation.getFrontendOpts();
+ PreprocessorOptions &PreprocessorOpts
+ = PreambleInvocation.getPreprocessorOpts();
+
+ // Try to determine if the main file has been remapped, either from the
+ // command line (to another file) or directly through the compiler invocation
+ // (to a memory buffer).
+ llvm::MemoryBuffer *Buffer = 0;
+ llvm::sys::PathWithStatus MainFilePath(FrontendOpts.Inputs[0].second);
+ if (const llvm::sys::FileStatus *MainFileStatus = MainFilePath.getFileStatus()) {
+ // Check whether there is a file-file remapping of the main file
+ for (PreprocessorOptions::remapped_file_iterator
+ M = PreprocessorOpts.remapped_file_begin(),
+ E = PreprocessorOpts.remapped_file_end();
+ M != E;
+ ++M) {
+ llvm::sys::PathWithStatus MPath(M->first);
+ if (const llvm::sys::FileStatus *MStatus = MPath.getFileStatus()) {
+ if (MainFileStatus->uniqueID == MStatus->uniqueID) {
+ // We found a remapping. Try to load the resulting, remapped source.
+ if (Buffer)
+ delete Buffer;
+ Buffer = llvm::MemoryBuffer::getFile(M->second);
+ if (!Buffer)
+ return;
+
+ // Remove the file-file remapping.
+ M = PreprocessorOpts.eraseRemappedFile(M);
+ E = PreprocessorOpts.remapped_file_end();
+ }
+ }
+ }
+
+ // Check whether there is a file-buffer remapping. It supercedes the
+ // file-file remapping.
+ for (PreprocessorOptions::remapped_file_buffer_iterator
+ M = PreprocessorOpts.remapped_file_buffer_begin(),
+ E = PreprocessorOpts.remapped_file_buffer_end();
+ M != E;
+ ++M) {
+ llvm::sys::PathWithStatus MPath(M->first);
+ if (const llvm::sys::FileStatus *MStatus = MPath.getFileStatus()) {
+ if (MainFileStatus->uniqueID == MStatus->uniqueID) {
+ // We found a remapping.
+ if (Buffer)
+ delete Buffer;
+ Buffer = const_cast<llvm::MemoryBuffer *>(M->second);
+
+ // Remove the file-buffer remapping.
+ M = PreprocessorOpts.eraseRemappedFile(M);
+ E = PreprocessorOpts.remapped_file_buffer_end();
+ }
+ }
+ }
+ }
+
+ // If the main source file was not remapped, load it now.
+ if (!Buffer) {
+ Buffer = llvm::MemoryBuffer::getFile(FrontendOpts.Inputs[0].second);
+ if (!Buffer)
+ return;
+ }
+
+ // Try to compute the preamble.
+ unsigned PreambleLength = Lexer::ComputePreamble(Buffer);
+ if (PreambleLength == 0)
+ return;
+
+ // Create a new buffer that stores the preamble. The buffer also contains
+ // extra space for the original contents of the file (which will be present
+ // when we actually parse the file) along with more room in case the file
+ // grows.
+ unsigned PreambleBufferSize = Buffer->getBufferSize();
+ if (PreambleBufferSize < 4096)
+ PreambleBufferSize = 8192;
+ else
+ PreambleBufferSize *= 2;
+
+ llvm::MemoryBuffer *PreambleBuffer
+ = llvm::MemoryBuffer::getNewUninitMemBuffer(PreambleBufferSize,
+ FrontendOpts.Inputs[0].second);
+ memcpy(const_cast<char*>(PreambleBuffer->getBufferStart()),
+ Buffer->getBufferStart(), PreambleLength);
+ memset(const_cast<char*>(PreambleBuffer->getBufferStart()) + PreambleLength,
+ ' ', PreambleBufferSize - PreambleLength - 1);
+ const_cast<char*>(PreambleBuffer->getBufferEnd())[-1] = 0;
+ delete Buffer;
+
+ // Remap the main source file to the preamble buffer.
+ PreprocessorOpts.addRemappedFile(MainFilePath.str(), PreambleBuffer);
+
+ // Tell the compiler invocation to generate a temporary precompiled header.
+ FrontendOpts.ProgramAction = frontend::GeneratePCH;
+ // FIXME: Set ChainedPCH, once it is ready.
+ // FIXME: Generate the precompiled header into memory?
+ FrontendOpts.OutputFile = GetPreamblePCHPath();
+
+ // Create the compiler instance to use for building the precompiled preamble.
+ CompilerInstance Clang;
+ Clang.setInvocation(&PreambleInvocation);
+ OriginalSourceFile = Clang.getFrontendOpts().Inputs[0].second;
+
+ // Set up diagnostics.
+ Clang.setDiagnostics(&getDiagnostics());
+ Clang.setDiagnosticClient(getDiagnostics().getClient());
+
+ // Create the target instance.
+ Clang.setTarget(TargetInfo::CreateTargetInfo(Clang.getDiagnostics(),
+ Clang.getTargetOpts()));
+ if (!Clang.hasTarget()) {
+ Clang.takeDiagnosticClient();
+ return;
+ }
+
+ // Inform the target of the language options.
+ //
+ // FIXME: We shouldn't need to do this, the target should be immutable once
+ // created. This complexity should be lifted elsewhere.
+ Clang.getTarget().setForcedLangOptions(Clang.getLangOpts());
+
+ assert(Clang.getFrontendOpts().Inputs.size() == 1 &&
+ "Invocation must have exactly one source file!");
+ assert(Clang.getFrontendOpts().Inputs[0].first != IK_AST &&
+ "FIXME: AST inputs not yet supported here!");
+ assert(Clang.getFrontendOpts().Inputs[0].first != IK_LLVM_IR &&
+ "IR inputs not support here!");
+
+ // Clear out old caches and data.
+ StoredDiagnostics.clear();
+
+ // Capture any diagnostics that would otherwise be dropped.
+ CaptureDroppedDiagnostics Capture(CaptureDiagnostics,
+ Clang.getDiagnostics(),
+ StoredDiagnostics);
+
+ // Create a file manager object to provide access to and cache the filesystem.
+ Clang.setFileManager(new FileManager);
+
+ // Create the source manager.
+ Clang.setSourceManager(new SourceManager(getDiagnostics()));
+
+ // FIXME: Eventually, we'll have to track top-level declarations here, too.
+ llvm::OwningPtr<GeneratePCHAction> Act;
+ Act.reset(new GeneratePCHAction);
+ if (!Act->BeginSourceFile(Clang, Clang.getFrontendOpts().Inputs[0].second,
+ Clang.getFrontendOpts().Inputs[0].first)) {
+ Clang.takeDiagnosticClient();
+ Clang.takeInvocation();
+ return;
+ }
+
+ Act->Execute();
+ Act->EndSourceFile();
+ Clang.takeDiagnosticClient();
+ Clang.takeInvocation();
+
+ // FIXME: Keep track of the actual preamble header we created!
+ fprintf(stderr, "Preamble PCH: %s\n", FrontendOpts.OutputFile.c_str());
+}
ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI,
llvm::IntrusiveRefCntPtr<Diagnostic> Diags,
bool OnlyLocalDecls,
- bool CaptureDiagnostics) {
+ bool CaptureDiagnostics,
+ bool PrecompilePreamble) {
if (!Diags.getPtr()) {
// No diagnostics engine was provided, so create our own diagnostics object
// with the default options.
@@ -419,6 +608,9 @@
AST->OnlyLocalDecls = OnlyLocalDecls;
AST->Invocation.reset(CI);
+ if (PrecompilePreamble)
+ AST->BuildPrecompiledPreamble();
+
if (!AST->Parse())
return AST.take();
@@ -432,7 +624,8 @@
bool OnlyLocalDecls,
RemappedFile *RemappedFiles,
unsigned NumRemappedFiles,
- bool CaptureDiagnostics) {
+ bool CaptureDiagnostics,
+ bool PrecompilePreamble) {
if (!Diags.getPtr()) {
// No diagnostics engine was provided, so create our own diagnostics object
// with the default options.
@@ -480,7 +673,7 @@
CompilerInvocation::CreateFromArgs(*CI,
const_cast<const char **>(CCArgs.data()),
const_cast<const char **>(CCArgs.data()) +
- CCArgs.size(),
+ CCArgs.size(),
*Diags);
// Override any files that need remapping
@@ -493,7 +686,7 @@
CI->getFrontendOpts().DisableFree = true;
return LoadFromCompilerInvocation(CI.take(), Diags, OnlyLocalDecls,
- CaptureDiagnostics);
+ CaptureDiagnostics, PrecompilePreamble);
}
bool ASTUnit::Reparse(RemappedFile *RemappedFiles, unsigned NumRemappedFiles) {
Modified: cfe/trunk/lib/Frontend/InitPreprocessor.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/InitPreprocessor.cpp?rev=109202&r1=109201&r2=109202&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/InitPreprocessor.cpp (original)
+++ cfe/trunk/lib/Frontend/InitPreprocessor.cpp Thu Jul 22 19:33:23 2010
@@ -477,7 +477,7 @@
FileManager &FileMgr,
const PreprocessorOptions &InitOpts) {
// Remap files in the source manager (with buffers).
- for (PreprocessorOptions::remapped_file_buffer_iterator
+ for (PreprocessorOptions::const_remapped_file_buffer_iterator
Remap = InitOpts.remapped_file_buffer_begin(),
RemapEnd = InitOpts.remapped_file_buffer_end();
Remap != RemapEnd;
@@ -499,9 +499,9 @@
}
// Remap files in the source manager (with other files).
- for (PreprocessorOptions::remapped_file_iterator
- Remap = InitOpts.remapped_file_begin(),
- RemapEnd = InitOpts.remapped_file_end();
+ for (PreprocessorOptions::const_remapped_file_iterator
+ Remap = InitOpts.remapped_file_begin(),
+ RemapEnd = InitOpts.remapped_file_end();
Remap != RemapEnd;
++Remap) {
// Find the file that we're mapping to.
Modified: cfe/trunk/tools/c-index-test/c-index-test.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/c-index-test/c-index-test.c?rev=109202&r1=109201&r2=109202&view=diff
==============================================================================
--- cfe/trunk/tools/c-index-test/c-index-test.c (original)
+++ cfe/trunk/tools/c-index-test/c-index-test.c Thu Jul 22 19:33:23 2010
@@ -28,6 +28,16 @@
extern char *basename(const char *);
#endif
+/// \brief Return the default parsing options.
+static unsigned getDefaultParsingOptions() {
+ unsigned options = CXTranslationUnit_DetailedPreprocessingRecord;
+
+ if (getenv("CINDEXTEST_EDITING"))
+ options |= CXTranslationUnit_Editing;
+
+ return options;
+}
+
static void PrintExtent(FILE *out, unsigned begin_line, unsigned begin_column,
unsigned end_line, unsigned end_column) {
fprintf(out, "[%d:%d - %d:%d]", begin_line, begin_column,
@@ -613,11 +623,12 @@
return -1;
}
- TU = clang_createTranslationUnitFromSourceFile(Idx, 0,
- argc - num_unsaved_files,
- argv + num_unsaved_files,
- num_unsaved_files,
- unsaved_files);
+ TU = clang_parseTranslationUnit(Idx, 0,
+ argv + num_unsaved_files,
+ argc - num_unsaved_files,
+ unsaved_files,
+ num_unsaved_files,
+ getDefaultParsingOptions());
if (!TU) {
fprintf(stderr, "Unable to load translation unit!\n");
free_remapped_files(unsaved_files, num_unsaved_files);
Modified: cfe/trunk/tools/libclang/CIndex.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/libclang/CIndex.cpp?rev=109202&r1=109201&r2=109202&view=diff
==============================================================================
--- cfe/trunk/tools/libclang/CIndex.cpp (original)
+++ cfe/trunk/tools/libclang/CIndex.cpp Thu Jul 22 19:33:23 2010
@@ -1176,6 +1176,11 @@
CIndexer *CXXIdx = static_cast<CIndexer *>(CIdx);
+ // The "editing" option implies other options.
+ if (options & CXTranslationUnit_Editing)
+ options |= CXTranslationUnit_PrecompiledPreamble;
+ bool PrecompilePreamble = options & CXTranslationUnit_PrecompiledPreamble;
+
// Configure the diagnostics.
DiagnosticOptions DiagOpts;
llvm::IntrusiveRefCntPtr<Diagnostic> Diags;
@@ -1210,10 +1215,12 @@
Args.insert(Args.end(), command_line_args,
command_line_args + num_command_line_args);
+ // Do we need the detailed preprocessing record?
if (options & CXTranslationUnit_DetailedPreprocessingRecord) {
Args.push_back("-Xclang");
Args.push_back("-detailed-preprocessing-record");
}
+
unsigned NumErrors = Diags->getNumErrors();
#ifdef USE_CRASHTRACER
@@ -1227,7 +1234,8 @@
CXXIdx->getOnlyLocalDecls(),
RemappedFiles.data(),
RemappedFiles.size(),
- /*CaptureDiagnostics=*/true));
+ /*CaptureDiagnostics=*/true,
+ PrecompilePreamble));
if (NumErrors != Diags->getNumErrors()) {
// Make sure to check that 'Unit' is non-NULL.
@@ -1317,9 +1325,12 @@
TemporaryFiles.push_back(DiagnosticsFile);
argv.push_back("-fdiagnostics-binary");
- argv.push_back("-Xclang");
- argv.push_back("-detailed-preprocessing-record");
-
+ // Do we need the detailed preprocessing record?
+ if (options & CXTranslationUnit_DetailedPreprocessingRecord) {
+ argv.push_back("-Xclang");
+ argv.push_back("-detailed-preprocessing-record");
+ }
+
// Add the null terminator.
argv.push_back(NULL);
More information about the cfe-commits
mailing list