Use precompiled preambles for in-process code completion.

llvm-svn: 110596
This commit is contained in:
Douglas Gregor 2010-08-09 20:45:32 +00:00
parent f4804c696d
commit 028d3e4d0f
9 changed files with 188 additions and 39 deletions

View File

@ -686,7 +686,19 @@ enum CXTranslationUnit_Flags {
* clang_reparseTranslationUnit() will re-use the implicit
* precompiled header to improve parsing performance.
*/
CXTranslationUnit_PrecompiledPreamble = 0x04
CXTranslationUnit_PrecompiledPreamble = 0x04,
/**
* \brief Used to indicate that the translation unit is incomplete.
*
* When a translation unit is considered "incomplete", semantic
* analysis that is typically performed at the end of the
* translation unit will be suppressed. For example, this suppresses
* the completion of tentative declarations in C and of
* instantiation of implicitly-instantiation function templates in
* C++. This option is typically used when parsing a header with the
* intent of producing a precompiled header.
*/
CXTranslationUnit_Incomplete = 0x08
};
/**

View File

@ -78,9 +78,12 @@ private:
/// \brief Whether to capture any diagnostics produced.
bool CaptureDiagnostics;
/// Track whether the main file was loaded from an AST or not.
/// \brief Track whether the main file was loaded from an AST or not.
bool MainFileIsAST;
/// \brief Whether this AST represents a complete translation unit.
bool CompleteTranslationUnit;
/// Track the top-level decls which appeared in an ASTUnit which was loaded
/// from a source file.
//
@ -199,9 +202,12 @@ private:
bool Parse(llvm::MemoryBuffer *OverrideMainBuffer);
std::pair<llvm::MemoryBuffer *, std::pair<unsigned, bool> >
ComputePreamble(CompilerInvocation &Invocation, bool &CreatedBuffer);
ComputePreamble(CompilerInvocation &Invocation,
unsigned MaxLines, bool &CreatedBuffer);
llvm::MemoryBuffer *BuildPrecompiledPreamble();
llvm::MemoryBuffer *getMainBufferWithPrecompiledPreamble(
bool AllowRebuild = true,
unsigned MaxLines = 0);
void RealizeTopLevelDeclsFromPreamble();
public:
@ -318,6 +324,12 @@ public:
return StoredDiagnostics;
}
/// \brief Whether this AST represents a complete translation unit.
///
/// If false, this AST is only a partial translation unit, e.g., one
/// that might still be used as a precompiled header or preamble.
bool isCompleteTranslationUnit() const { return CompleteTranslationUnit; }
/// \brief A mapping from a file name to the memory buffer that stores the
/// remapped contents of that file.
typedef std::pair<std::string, const llvm::MemoryBuffer *> RemappedFile;
@ -352,7 +364,8 @@ public:
llvm::IntrusiveRefCntPtr<Diagnostic> Diags,
bool OnlyLocalDecls = false,
bool CaptureDiagnostics = false,
bool PrecompilePreamble = false);
bool PrecompilePreamble = false,
bool CompleteTranslationUnit = true);
/// LoadFromCommandLine - Create an ASTUnit from a vector of command line
/// arguments, which must specify exactly one source file.
@ -376,7 +389,8 @@ public:
RemappedFile *RemappedFiles = 0,
unsigned NumRemappedFiles = 0,
bool CaptureDiagnostics = false,
bool PrecompilePreamble = false);
bool PrecompilePreamble = false,
bool CompleteTranslationUnit = true);
/// \brief Reparse the source files using the same command-line options that
/// were originally used to produce this translation unit.

View File

@ -237,11 +237,14 @@ public:
///
/// \param Buffer The memory buffer containing the file's contents.
///
/// \param MaxLines If non-zero, restrict the length of the preamble
/// to fewer than this number of lines.
///
/// \returns The offset into the file where the preamble ends and the rest
/// of the file begins along with a boolean value indicating whether
/// the preamble ends at the beginning of a new line.
static std::pair<unsigned, bool>
ComputePreamble(const llvm::MemoryBuffer *Buffer);
ComputePreamble(const llvm::MemoryBuffer *Buffer, unsigned MaxLines = 0);
//===--------------------------------------------------------------------===//
// Internal implementation interfaces.

View File

@ -34,6 +34,7 @@
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/System/Host.h"
#include "llvm/System/Path.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Timer.h"
#include <cstdlib>
#include <cstdio>
@ -48,8 +49,8 @@ const unsigned DefaultPreambleRebuildInterval = 5;
ASTUnit::ASTUnit(bool _MainFileIsAST)
: CaptureDiagnostics(false), MainFileIsAST(_MainFileIsAST),
ConcurrencyCheckValue(CheckUnlocked), PreambleRebuildCounter(0),
SavedMainFileBuffer(0) {
CompleteTranslationUnit(true), ConcurrencyCheckValue(CheckUnlocked),
PreambleRebuildCounter(0), SavedMainFileBuffer(0) {
}
ASTUnit::~ASTUnit() {
@ -334,6 +335,9 @@ public:
TopLevelDeclTrackerAction(ASTUnit &_Unit) : Unit(_Unit) {}
virtual bool hasCodeCompletionSupport() const { return false; }
virtual bool usesCompleteTranslationUnit() {
return Unit.isCompleteTranslationUnit();
}
};
class PrecompilePreambleConsumer : public PCHGenerator {
@ -396,6 +400,7 @@ public:
virtual bool hasCodeCompletionSupport() const { return false; }
virtual bool hasASTFileSupport() const { return false; }
virtual bool usesCompleteTranslationUnit() { return false; }
};
}
@ -567,7 +572,8 @@ static std::string GetPreamblePCHPath() {
/// that corresponds to the main file along with a pair (bytes, start-of-line)
/// that describes the preamble.
std::pair<llvm::MemoryBuffer *, std::pair<unsigned, bool> >
ASTUnit::ComputePreamble(CompilerInvocation &Invocation, bool &CreatedBuffer) {
ASTUnit::ComputePreamble(CompilerInvocation &Invocation,
unsigned MaxLines, bool &CreatedBuffer) {
FrontendOptions &FrontendOpts = Invocation.getFrontendOpts();
PreprocessorOptions &PreprocessorOpts
= Invocation.getPreprocessorOpts();
@ -642,7 +648,7 @@ ASTUnit::ComputePreamble(CompilerInvocation &Invocation, bool &CreatedBuffer) {
CreatedBuffer = true;
}
return std::make_pair(Buffer, Lexer::ComputePreamble(Buffer));
return std::make_pair(Buffer, Lexer::ComputePreamble(Buffer, MaxLines));
}
static llvm::MemoryBuffer *CreatePaddedMainFileBuffer(llvm::MemoryBuffer *Old,
@ -673,10 +679,19 @@ static llvm::MemoryBuffer *CreatePaddedMainFileBuffer(llvm::MemoryBuffer *Old,
/// this routine will determine if it is still valid and, if so, avoid
/// rebuilding the precompiled preamble.
///
/// \param AllowRebuild When true (the default), this routine is
/// allowed to rebuild the precompiled preamble if it is found to be
/// out-of-date.
///
/// \param MaxLines When non-zero, the maximum number of lines that
/// can occur within the preamble.
///
/// \returns If the precompiled preamble can be used, returns a newly-allocated
/// buffer that should be used in place of the main file when doing so.
/// Otherwise, returns a NULL pointer.
llvm::MemoryBuffer *ASTUnit::BuildPrecompiledPreamble() {
llvm::MemoryBuffer *ASTUnit::getMainBufferWithPrecompiledPreamble(
bool AllowRebuild,
unsigned MaxLines) {
CompilerInvocation PreambleInvocation(*Invocation);
FrontendOptions &FrontendOpts = PreambleInvocation.getFrontendOpts();
PreprocessorOptions &PreprocessorOpts
@ -684,7 +699,7 @@ llvm::MemoryBuffer *ASTUnit::BuildPrecompiledPreamble() {
bool CreatedPreambleBuffer = false;
std::pair<llvm::MemoryBuffer *, std::pair<unsigned, bool> > NewPreamble
= ComputePreamble(PreambleInvocation, CreatedPreambleBuffer);
= ComputePreamble(PreambleInvocation, MaxLines, CreatedPreambleBuffer);
if (!NewPreamble.second.first) {
// We couldn't find a preamble in the main source. Clear out the current
@ -793,12 +808,21 @@ llvm::MemoryBuffer *ASTUnit::BuildPrecompiledPreamble() {
FrontendOpts.Inputs[0].second);
}
}
// If we aren't allowed to rebuild the precompiled preamble, just
// return now.
if (!AllowRebuild)
return 0;
// We can't reuse the previously-computed preamble. Build a new one.
Preamble.clear();
llvm::sys::Path(PreambleFile).eraseFromDisk();
PreambleRebuildCounter = 1;
}
} else if (!AllowRebuild) {
// We aren't allowed to rebuild the precompiled preamble; just
// return now.
return 0;
}
// If the preamble rebuild counter > 1, it's because we previously
// failed to build a preamble and we're not yet ready to try
@ -1004,7 +1028,8 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI,
llvm::IntrusiveRefCntPtr<Diagnostic> Diags,
bool OnlyLocalDecls,
bool CaptureDiagnostics,
bool PrecompilePreamble) {
bool PrecompilePreamble,
bool CompleteTranslationUnit) {
if (!Diags.getPtr()) {
// No diagnostics engine was provided, so create our own diagnostics object
// with the default options.
@ -1018,6 +1043,7 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI,
AST->Diagnostics = Diags;
AST->CaptureDiagnostics = CaptureDiagnostics;
AST->OnlyLocalDecls = OnlyLocalDecls;
AST->CompleteTranslationUnit = CompleteTranslationUnit;
AST->Invocation.reset(CI);
CI->getPreprocessorOpts().RetainRemappedFileBuffers = true;
@ -1030,7 +1056,7 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI,
// FIXME: When C++ PCH is ready, allow use of it for a precompiled preamble.
if (PrecompilePreamble && !CI->getLangOpts().CPlusPlus) {
AST->PreambleRebuildCounter = 1;
OverrideMainBuffer = AST->BuildPrecompiledPreamble();
OverrideMainBuffer = AST->getMainBufferWithPrecompiledPreamble();
}
llvm::Timer *ParsingTimer = 0;
@ -1055,7 +1081,8 @@ ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin,
RemappedFile *RemappedFiles,
unsigned NumRemappedFiles,
bool CaptureDiagnostics,
bool PrecompilePreamble) {
bool PrecompilePreamble,
bool CompleteTranslationUnit) {
if (!Diags.getPtr()) {
// No diagnostics engine was provided, so create our own diagnostics object
// with the default options.
@ -1116,7 +1143,8 @@ ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin,
CI->getFrontendOpts().DisableFree = true;
return LoadFromCompilerInvocation(CI.take(), Diags, OnlyLocalDecls,
CaptureDiagnostics, PrecompilePreamble);
CaptureDiagnostics, PrecompilePreamble,
CompleteTranslationUnit);
}
bool ASTUnit::Reparse(RemappedFile *RemappedFiles, unsigned NumRemappedFiles) {
@ -1140,7 +1168,7 @@ bool ASTUnit::Reparse(RemappedFile *RemappedFiles, unsigned NumRemappedFiles) {
// build a precompiled preamble, do so now.
llvm::MemoryBuffer *OverrideMainBuffer = 0;
if (!PreambleFile.empty() || PreambleRebuildCounter > 0)
OverrideMainBuffer = BuildPrecompiledPreamble();
OverrideMainBuffer = getMainBufferWithPrecompiledPreamble();
// Clear out the diagnostics state.
if (!OverrideMainBuffer)
@ -1165,6 +1193,17 @@ void ASTUnit::CodeComplete(llvm::StringRef File, unsigned Line, unsigned Column,
if (!Invocation.get())
return;
llvm::Timer *CompletionTimer = 0;
if (TimerGroup.get()) {
llvm::SmallString<128> TimerName;
llvm::raw_svector_ostream TimerNameOut(TimerName);
TimerNameOut << "Code completion @ " << File << ":" << Line << ":"
<< Column;
CompletionTimer = new llvm::Timer(TimerNameOut.str(), *TimerGroup);
CompletionTimer->startTimer();
Timers.push_back(CompletionTimer);
}
CompilerInvocation CCInvocation(*Invocation);
FrontendOptions &FrontendOpts = CCInvocation.getFrontendOpts();
PreprocessorOptions &PreprocessorOpts = CCInvocation.getPreprocessorOpts();
@ -1230,6 +1269,42 @@ void ASTUnit::CodeComplete(llvm::StringRef File, unsigned Line, unsigned Column,
// Use the code completion consumer we were given.
Clang.setCodeCompletionConsumer(&Consumer);
// If we have a precompiled preamble, try to use it. We only allow
// the use of the precompiled preamble if we're if the completion
// point is within the main file, after the end of the precompiled
// preamble.
llvm::MemoryBuffer *OverrideMainBuffer = 0;
if (!PreambleFile.empty()) {
using llvm::sys::FileStatus;
llvm::sys::PathWithStatus CompleteFilePath(File);
llvm::sys::PathWithStatus MainPath(OriginalSourceFile);
if (const FileStatus *CompleteFileStatus = CompleteFilePath.getFileStatus())
if (const FileStatus *MainStatus = MainPath.getFileStatus())
if (CompleteFileStatus->getUniqueID() == MainStatus->getUniqueID())
OverrideMainBuffer = getMainBufferWithPrecompiledPreamble(false,
Line);
}
// If the main file has been overridden due to the use of a preamble,
// make that override happen and introduce the preamble.
if (OverrideMainBuffer) {
PreprocessorOpts.addRemappedFile(OriginalSourceFile, OverrideMainBuffer);
PreprocessorOpts.PrecompiledPreambleBytes.first = Preamble.size();
PreprocessorOpts.PrecompiledPreambleBytes.second
= PreambleEndsAtStartOfLine;
PreprocessorOpts.ImplicitPCHInclude = PreambleFile;
PreprocessorOpts.DisablePCHValidation = true;
// The stored diagnostics have the old source manager. Copy them
// to our output set of stored diagnostics, updating the source
// manager to the one we were given.
for (unsigned I = 0, N = this->StoredDiagnostics.size(); I != N; ++I) {
StoredDiagnostics.push_back(this->StoredDiagnostics[I]);
FullSourceLoc Loc(StoredDiagnostics[I].getLocation(), SourceMgr);
StoredDiagnostics[I].setLocation(Loc);
}
}
llvm::OwningPtr<SyntaxOnlyAction> Act;
Act.reset(new SyntaxOnlyAction);
if (Act->BeginSourceFile(Clang, Clang.getFrontendOpts().Inputs[0].second,
@ -1237,8 +1312,12 @@ void ASTUnit::CodeComplete(llvm::StringRef File, unsigned Line, unsigned Column,
Act->Execute();
Act->EndSourceFile();
}
if (CompletionTimer)
CompletionTimer->stopTimer();
// Steal back our resources.
delete OverrideMainBuffer;
Clang.takeFileManager();
Clang.takeSourceManager();
Clang.takeInvocation();

View File

@ -311,7 +311,7 @@ namespace {
}
std::pair<unsigned, bool>
Lexer::ComputePreamble(const llvm::MemoryBuffer *Buffer) {
Lexer::ComputePreamble(const llvm::MemoryBuffer *Buffer, unsigned MaxLines) {
// Create a lexer starting at the beginning of the file. Note that we use a
// "fake" file source location at offset 1 so that the lexer will track our
// position within the file.
@ -325,6 +325,8 @@ Lexer::ComputePreamble(const llvm::MemoryBuffer *Buffer) {
Token TheTok;
Token IfStartTok;
unsigned IfCount = 0;
unsigned Line = 0;
do {
TheLexer.LexFromRawLexer(TheTok);
@ -345,6 +347,16 @@ Lexer::ComputePreamble(const llvm::MemoryBuffer *Buffer) {
InPreprocessorDirective = false;
}
// Keep track of the # of lines in the preamble.
if (TheTok.isAtStartOfLine()) {
++Line;
// If we were asked to limit the number of lines in the preamble,
// and we're about to exceed that limit, we're done.
if (MaxLines && Line >= MaxLines)
break;
}
// Comments are okay; skip over them.
if (TheTok.getKind() == tok::comment)
continue;
@ -418,7 +430,9 @@ Lexer::ComputePreamble(const llvm::MemoryBuffer *Buffer) {
TheTok = HashTok;
}
// We hit a token
// We hit a token that we don't recognize as being in the
// "preprocessing only" part of the file, so we're no longer in
// the preamble.
break;
} while (true);

View File

@ -2,6 +2,9 @@
#include "preamble.h"
int wibble(int);
void f(int x) {
}
// RUN: %clang -x c-header -o %t.pch %S/Inputs/prefix.h
// RUN: env CINDEXTEST_EDITING=1 c-index-test -test-load-source-reparse 5 local -I %S/Inputs -include %t %s 2> %t.stderr.txt | FileCheck %s
// RUN: FileCheck -check-prefix CHECK-DIAG %s < %t.stderr.txt
@ -18,3 +21,8 @@ int wibble(int);
// CHECK: preamble.c:3:5: FunctionDecl=wibble:3:5 Extent=[3:5 - 3:16]
// CHECK: preamble.c:3:15: ParmDecl=:3:15 (Definition) Extent=[3:12 - 3:16]
// CHECK-DIAG: preamble.h:4:7:{4:9-4:13}: warning: incompatible pointer types assigning to 'int *' from 'float *'
// RUN: env CINDEXTEST_EDITING=1 c-index-test -code-completion-at=%s:6:1 -I %S/Inputs -include %t %s 2> %t.stderr.txt | FileCheck -check-prefix CHECK-CC %s
// CHECK-CC: FunctionDecl:{ResultType int}{TypedText bar}{LeftParen (}{Placeholder int i}{RightParen )} (50)
// CHECK-CC: FunctionDecl:{ResultType void}{TypedText f}{LeftParen (}{Placeholder int x}{RightParen )} (50)
// CHECK-CC: FunctionDecl:{ResultType int}{TypedText foo}{LeftParen (}{Placeholder int}{RightParen )} (50)
// CHECK-CC: FunctionDecl:{ResultType int}{TypedText wibble}{LeftParen (}{Placeholder int}{RightParen )} (50)

View File

@ -874,7 +874,8 @@ int perform_code_completion(int argc, const char **argv, int timing_only) {
struct CXUnsavedFile *unsaved_files = 0;
int num_unsaved_files = 0;
CXCodeCompleteResults *results = 0;
CXTranslationUnit *TU = 0;
if (timing_only)
input += strlen("-code-completion-timing=");
else
@ -889,15 +890,20 @@ int perform_code_completion(int argc, const char **argv, int timing_only) {
CIdx = clang_createIndex(0, 1);
if (getenv("CINDEXTEST_EDITING")) {
CXTranslationUnit *TU = clang_parseTranslationUnit(CIdx, 0,
argv + num_unsaved_files + 2,
argc - num_unsaved_files - 2,
unsaved_files,
num_unsaved_files,
getDefaultParsingOptions());
results = clang_codeCompleteAt(TU, filename, line, column,
unsaved_files, num_unsaved_files,
clang_defaultCodeCompleteOptions());
TU = clang_parseTranslationUnit(CIdx, 0,
argv + num_unsaved_files + 2,
argc - num_unsaved_files - 2,
unsaved_files,
num_unsaved_files,
getDefaultParsingOptions());
unsigned I, Repeats = 5;
for (I = 0; I != Repeats; ++I) {
results = clang_codeCompleteAt(TU, filename, line, column,
unsaved_files, num_unsaved_files,
clang_defaultCodeCompleteOptions());
if (I != Repeats-1)
clang_disposeCodeCompleteResults(results);
}
} else
results = clang_codeComplete(CIdx,
argv[argc - 1], argc - num_unsaved_files - 3,
@ -918,7 +924,7 @@ int perform_code_completion(int argc, const char **argv, int timing_only) {
}
clang_disposeCodeCompleteResults(results);
}
clang_disposeTranslationUnit(TU);
clang_disposeIndex(CIdx);
free(filename);

View File

@ -1195,7 +1195,9 @@ CXTranslationUnit clang_parseTranslationUnit(CXIndex CIdx,
if (options & CXTranslationUnit_Editing)
options |= CXTranslationUnit_PrecompiledPreamble;
bool PrecompilePreamble = options & CXTranslationUnit_PrecompiledPreamble;
bool CompleteTranslationUnit
= ((options & CXTranslationUnit_Incomplete) == 0);
// Configure the diagnostics.
DiagnosticOptions DiagOpts;
llvm::IntrusiveRefCntPtr<Diagnostic> Diags;
@ -1250,7 +1252,8 @@ CXTranslationUnit clang_parseTranslationUnit(CXIndex CIdx,
RemappedFiles.data(),
RemappedFiles.size(),
/*CaptureDiagnostics=*/true,
PrecompilePreamble));
PrecompilePreamble,
CompleteTranslationUnit));
if (NumErrors != Diags->getNumErrors()) {
// Make sure to check that 'Unit' is non-NULL.
@ -1451,7 +1454,7 @@ int clang_reparseTranslationUnit(CXTranslationUnit TU,
return static_cast<ASTUnit *>(TU)->Reparse(RemappedFiles.data(),
RemappedFiles.size())? 1 : 0;
}
CXString clang_getTranslationUnitSpelling(CXTranslationUnit CTUnit) {
if (!CTUnit)
return createCXString("");

View File

@ -20,17 +20,18 @@
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Sema/CodeCompleteConsumer.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/System/Program.h"
#include <cstdlib>
#include <cstdio>
#ifdef UDP_CODE_COMPLETION_LOGGER
#include "clang/Basic/Version.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h"
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
@ -277,7 +278,16 @@ CXCodeCompleteResults *clang_codeComplete(CXIndex CIdx,
#endif
bool EnableLogging = getenv("LIBCLANG_CODE_COMPLETION_LOGGING") != 0;
llvm::OwningPtr<llvm::NamedRegionTimer> CCTimer;
if (getenv("LIBCLANG_TIMING")) {
llvm::SmallString<128> TimerName;
llvm::raw_svector_ostream TimerNameOut(TimerName);
TimerNameOut << "Code completion @ " << complete_filename << ":"
<< complete_line << ":" << complete_column;
CCTimer.reset(new llvm::NamedRegionTimer(TimerNameOut.str()));
}
// The indexer, which is mainly used to determine where diagnostics go.
CIndexer *CXXIdx = static_cast<CIndexer *>(CIdx);