summaryrefslogtreecommitdiff
path: root/lib/Frontend/PrecompiledPreamble.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2017-12-18 20:11:37 +0000
committerDimitry Andric <dim@FreeBSD.org>2017-12-18 20:11:37 +0000
commit461a67fa15370a9ec88f8f8a240bf7c123bb2029 (patch)
tree6942083d7d56bba40ec790a453ca58ad3baf6832 /lib/Frontend/PrecompiledPreamble.cpp
parent75c3240472ba6ac2669ee72ca67eb72d4e2851fc (diff)
Diffstat (limited to 'lib/Frontend/PrecompiledPreamble.cpp')
-rw-r--r--lib/Frontend/PrecompiledPreamble.cpp261
1 files changed, 213 insertions, 48 deletions
diff --git a/lib/Frontend/PrecompiledPreamble.cpp b/lib/Frontend/PrecompiledPreamble.cpp
index 15b24cbed4841..f6964d02b2373 100644
--- a/lib/Frontend/PrecompiledPreamble.cpp
+++ b/lib/Frontend/PrecompiledPreamble.cpp
@@ -24,15 +24,45 @@
#include "clang/Serialization/ASTWriter.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSet.h"
+#include "llvm/Config/llvm-config.h"
#include "llvm/Support/CrashRecoveryContext.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Mutex.h"
#include "llvm/Support/MutexGuard.h"
+#include "llvm/Support/Process.h"
+
+#include <utility>
using namespace clang;
namespace {
+StringRef getInMemoryPreamblePath() {
+#if defined(LLVM_ON_UNIX)
+ return "/__clang_tmp/___clang_inmemory_preamble___";
+#elif defined(LLVM_ON_WIN32)
+ return "C:\\__clang_tmp\\___clang_inmemory_preamble___";
+#else
+#warning "Unknown platform. Defaulting to UNIX-style paths for in-memory PCHs"
+ return "/__clang_tmp/___clang_inmemory_preamble___";
+#endif
+}
+
+IntrusiveRefCntPtr<vfs::FileSystem>
+createVFSOverlayForPreamblePCH(StringRef PCHFilename,
+ std::unique_ptr<llvm::MemoryBuffer> PCHBuffer,
+ IntrusiveRefCntPtr<vfs::FileSystem> VFS) {
+ // We want only the PCH file from the real filesystem to be available,
+ // so we create an in-memory VFS with just that and overlay it on top.
+ IntrusiveRefCntPtr<vfs::InMemoryFileSystem> PCHFS(
+ new vfs::InMemoryFileSystem());
+ PCHFS->addFile(PCHFilename, 0, std::move(PCHBuffer));
+ IntrusiveRefCntPtr<vfs::OverlayFileSystem> Overlay(
+ new vfs::OverlayFileSystem(VFS));
+ Overlay->pushOverlay(PCHFS);
+ return Overlay;
+}
+
/// Keeps a track of files to be deleted in destructor.
class TemporaryFiles {
public:
@@ -85,23 +115,11 @@ void TemporaryFiles::removeFile(StringRef File) {
llvm::sys::fs::remove(File);
}
-class PreambleMacroCallbacks : public PPCallbacks {
-public:
- PreambleMacroCallbacks(PreambleCallbacks &Callbacks) : Callbacks(Callbacks) {}
-
- void MacroDefined(const Token &MacroNameTok,
- const MacroDirective *MD) override {
- Callbacks.HandleMacroDefined(MacroNameTok, MD);
- }
-
-private:
- PreambleCallbacks &Callbacks;
-};
-
class PrecompilePreambleAction : public ASTFrontendAction {
public:
- PrecompilePreambleAction(PreambleCallbacks &Callbacks)
- : Callbacks(Callbacks) {}
+ PrecompilePreambleAction(std::string *InMemStorage,
+ PreambleCallbacks &Callbacks)
+ : InMemStorage(InMemStorage), Callbacks(Callbacks) {}
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
StringRef InFile) override;
@@ -122,6 +140,7 @@ private:
friend class PrecompilePreambleConsumer;
bool HasEmittedPreamblePCH = false;
+ std::string *InMemStorage;
PreambleCallbacks &Callbacks;
};
@@ -163,21 +182,24 @@ private:
std::unique_ptr<ASTConsumer>
PrecompilePreambleAction::CreateASTConsumer(CompilerInstance &CI,
-
StringRef InFile) {
std::string Sysroot;
- std::string OutputFile;
- std::unique_ptr<raw_ostream> OS =
- GeneratePCHAction::ComputeASTConsumerArguments(CI, InFile, Sysroot,
- OutputFile);
+ if (!GeneratePCHAction::ComputeASTConsumerArguments(CI, Sysroot))
+ return nullptr;
+
+ std::unique_ptr<llvm::raw_ostream> OS;
+ if (InMemStorage) {
+ OS = llvm::make_unique<llvm::raw_string_ostream>(*InMemStorage);
+ } else {
+ std::string OutputFile;
+ OS = GeneratePCHAction::CreateOutputFile(CI, InFile, OutputFile);
+ }
if (!OS)
return nullptr;
if (!CI.getFrontendOpts().RelocatablePCH)
Sysroot.clear();
- CI.getPreprocessor().addPPCallbacks(
- llvm::make_unique<PreambleMacroCallbacks>(Callbacks));
return llvm::make_unique<PrecompilePreambleConsumer>(
*this, CI.getPreprocessor(), Sysroot, std::move(OS));
}
@@ -194,15 +216,14 @@ template <class T> bool moveOnNoError(llvm::ErrorOr<T> Val, T &Output) {
PreambleBounds clang::ComputePreambleBounds(const LangOptions &LangOpts,
llvm::MemoryBuffer *Buffer,
unsigned MaxLines) {
- auto Pre = Lexer::ComputePreamble(Buffer->getBuffer(), LangOpts, MaxLines);
- return PreambleBounds(Pre.first, Pre.second);
+ return Lexer::ComputePreamble(Buffer->getBuffer(), LangOpts, MaxLines);
}
llvm::ErrorOr<PrecompiledPreamble> PrecompiledPreamble::Build(
const CompilerInvocation &Invocation,
const llvm::MemoryBuffer *MainFileBuffer, PreambleBounds Bounds,
DiagnosticsEngine &Diagnostics, IntrusiveRefCntPtr<vfs::FileSystem> VFS,
- std::shared_ptr<PCHContainerOperations> PCHContainerOps,
+ std::shared_ptr<PCHContainerOperations> PCHContainerOps, bool StoreInMemory,
PreambleCallbacks &Callbacks) {
assert(VFS && "VFS is null");
@@ -214,12 +235,19 @@ llvm::ErrorOr<PrecompiledPreamble> PrecompiledPreamble::Build(
PreprocessorOptions &PreprocessorOpts =
PreambleInvocation->getPreprocessorOpts();
- // Create a temporary file for the precompiled preamble. In rare
- // circumstances, this can fail.
- llvm::ErrorOr<PrecompiledPreamble::TempPCHFile> PreamblePCHFile =
- PrecompiledPreamble::TempPCHFile::CreateNewPreamblePCHFile();
- if (!PreamblePCHFile)
- return BuildPreambleError::CouldntCreateTempFile;
+ llvm::Optional<TempPCHFile> TempFile;
+ if (!StoreInMemory) {
+ // Create a temporary file for the precompiled preamble. In rare
+ // circumstances, this can fail.
+ llvm::ErrorOr<PrecompiledPreamble::TempPCHFile> PreamblePCHFile =
+ PrecompiledPreamble::TempPCHFile::CreateNewPreamblePCHFile();
+ if (!PreamblePCHFile)
+ return BuildPreambleError::CouldntCreateTempFile;
+ TempFile = std::move(*PreamblePCHFile);
+ }
+
+ PCHStorage Storage = StoreInMemory ? PCHStorage(InMemoryPreamble())
+ : PCHStorage(std::move(*TempFile));
// Save the preamble text for later; we'll need to compare against it for
// subsequent reparses.
@@ -230,10 +258,12 @@ llvm::ErrorOr<PrecompiledPreamble> PrecompiledPreamble::Build(
// Tell the compiler invocation to generate a temporary precompiled header.
FrontendOpts.ProgramAction = frontend::GeneratePCH;
- // FIXME: Generate the precompiled header into memory?
- FrontendOpts.OutputFile = PreamblePCHFile->getFilePath();
+ FrontendOpts.OutputFile = StoreInMemory ? getInMemoryPreamblePath()
+ : Storage.asFile().getFilePath();
PreprocessorOpts.PrecompiledPreambleBytes.first = 0;
PreprocessorOpts.PrecompiledPreambleBytes.second = false;
+ // Inform preprocessor to record conditional stack when building the preamble.
+ PreprocessorOpts.GeneratePreamble = true;
// Create the compiler instance to use for building the precompiled preamble.
std::unique_ptr<CompilerInstance> Clang(
@@ -301,10 +331,16 @@ llvm::ErrorOr<PrecompiledPreamble> PrecompiledPreamble::Build(
}
std::unique_ptr<PrecompilePreambleAction> Act;
- Act.reset(new PrecompilePreambleAction(Callbacks));
+ Act.reset(new PrecompilePreambleAction(
+ StoreInMemory ? &Storage.asMemory().Data : nullptr, Callbacks));
if (!Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0]))
return BuildPreambleError::BeginSourceFileFailed;
+ std::unique_ptr<PPCallbacks> DelegatedPPCallbacks =
+ Callbacks.createPPCallbacks();
+ if (DelegatedPPCallbacks)
+ Clang->getPreprocessor().addPPCallbacks(std::move(DelegatedPPCallbacks));
+
Act->Execute();
// Run the callbacks.
@@ -335,9 +371,9 @@ llvm::ErrorOr<PrecompiledPreamble> PrecompiledPreamble::Build(
}
}
- return PrecompiledPreamble(
- std::move(*PreamblePCHFile), std::move(PreambleBytes),
- PreambleEndsAtStartOfLine, std::move(FilesInPreamble));
+ return PrecompiledPreamble(std::move(Storage), std::move(PreambleBytes),
+ PreambleEndsAtStartOfLine,
+ std::move(FilesInPreamble));
}
PreambleBounds PrecompiledPreamble::getBounds() const {
@@ -425,27 +461,33 @@ bool PrecompiledPreamble::CanReuse(const CompilerInvocation &Invocation,
}
void PrecompiledPreamble::AddImplicitPreamble(
- CompilerInvocation &CI, llvm::MemoryBuffer *MainFileBuffer) const {
+ CompilerInvocation &CI, IntrusiveRefCntPtr<vfs::FileSystem> &VFS,
+ llvm::MemoryBuffer *MainFileBuffer) const {
+ assert(VFS && "VFS must not be null");
+
auto &PreprocessorOpts = CI.getPreprocessorOpts();
+ // Remap main file to point to MainFileBuffer.
+ auto MainFilePath = CI.getFrontendOpts().Inputs[0].getFile();
+ PreprocessorOpts.addRemappedFile(MainFilePath, MainFileBuffer);
+
// Configure ImpicitPCHInclude.
PreprocessorOpts.PrecompiledPreambleBytes.first = PreambleBytes.size();
PreprocessorOpts.PrecompiledPreambleBytes.second = PreambleEndsAtStartOfLine;
- PreprocessorOpts.ImplicitPCHInclude = PCHFile.getFilePath();
PreprocessorOpts.DisablePCHValidation = true;
- // Remap main file to point to MainFileBuffer.
- auto MainFilePath = CI.getFrontendOpts().Inputs[0].getFile();
- PreprocessorOpts.addRemappedFile(MainFilePath, MainFileBuffer);
+ setupPreambleStorage(Storage, PreprocessorOpts, VFS);
}
PrecompiledPreamble::PrecompiledPreamble(
- TempPCHFile PCHFile, std::vector<char> PreambleBytes,
+ PCHStorage Storage, std::vector<char> PreambleBytes,
bool PreambleEndsAtStartOfLine,
llvm::StringMap<PreambleFileHash> FilesInPreamble)
- : PCHFile(std::move(PCHFile)), FilesInPreamble(FilesInPreamble),
+ : Storage(std::move(Storage)), FilesInPreamble(std::move(FilesInPreamble)),
PreambleBytes(std::move(PreambleBytes)),
- PreambleEndsAtStartOfLine(PreambleEndsAtStartOfLine) {}
+ PreambleEndsAtStartOfLine(PreambleEndsAtStartOfLine) {
+ assert(this->Storage.getKind() != PCHStorage::Kind::Empty);
+}
llvm::ErrorOr<PrecompiledPreamble::TempPCHFile>
PrecompiledPreamble::TempPCHFile::CreateNewPreamblePCHFile() {
@@ -462,9 +504,15 @@ llvm::ErrorOr<PrecompiledPreamble::TempPCHFile>
PrecompiledPreamble::TempPCHFile::createInSystemTempDir(const Twine &Prefix,
StringRef Suffix) {
llvm::SmallString<64> File;
- auto EC = llvm::sys::fs::createTemporaryFile(Prefix, Suffix, /*ref*/ File);
+ // Using a version of createTemporaryFile with a file descriptor guarantees
+ // that we would never get a race condition in a multi-threaded setting (i.e.,
+ // multiple threads getting the same temporary path).
+ int FD;
+ auto EC = llvm::sys::fs::createTemporaryFile(Prefix, Suffix, FD, File);
if (EC)
return EC;
+ // We only needed to make sure the file exists, close the file right away.
+ llvm::sys::Process::SafelyCloseFileDescriptor(FD);
return TempPCHFile(std::move(File).str());
}
@@ -506,6 +554,87 @@ llvm::StringRef PrecompiledPreamble::TempPCHFile::getFilePath() const {
return *FilePath;
}
+PrecompiledPreamble::PCHStorage::PCHStorage(TempPCHFile File)
+ : StorageKind(Kind::TempFile) {
+ new (&asFile()) TempPCHFile(std::move(File));
+}
+
+PrecompiledPreamble::PCHStorage::PCHStorage(InMemoryPreamble Memory)
+ : StorageKind(Kind::InMemory) {
+ new (&asMemory()) InMemoryPreamble(std::move(Memory));
+}
+
+PrecompiledPreamble::PCHStorage::PCHStorage(PCHStorage &&Other) : PCHStorage() {
+ *this = std::move(Other);
+}
+
+PrecompiledPreamble::PCHStorage &PrecompiledPreamble::PCHStorage::
+operator=(PCHStorage &&Other) {
+ destroy();
+
+ StorageKind = Other.StorageKind;
+ switch (StorageKind) {
+ case Kind::Empty:
+ // do nothing;
+ break;
+ case Kind::TempFile:
+ new (&asFile()) TempPCHFile(std::move(Other.asFile()));
+ break;
+ case Kind::InMemory:
+ new (&asMemory()) InMemoryPreamble(std::move(Other.asMemory()));
+ break;
+ }
+
+ Other.setEmpty();
+ return *this;
+}
+
+PrecompiledPreamble::PCHStorage::~PCHStorage() { destroy(); }
+
+PrecompiledPreamble::PCHStorage::Kind
+PrecompiledPreamble::PCHStorage::getKind() const {
+ return StorageKind;
+}
+
+PrecompiledPreamble::TempPCHFile &PrecompiledPreamble::PCHStorage::asFile() {
+ assert(getKind() == Kind::TempFile);
+ return *reinterpret_cast<TempPCHFile *>(Storage.buffer);
+}
+
+const PrecompiledPreamble::TempPCHFile &
+PrecompiledPreamble::PCHStorage::asFile() const {
+ return const_cast<PCHStorage *>(this)->asFile();
+}
+
+PrecompiledPreamble::InMemoryPreamble &
+PrecompiledPreamble::PCHStorage::asMemory() {
+ assert(getKind() == Kind::InMemory);
+ return *reinterpret_cast<InMemoryPreamble *>(Storage.buffer);
+}
+
+const PrecompiledPreamble::InMemoryPreamble &
+PrecompiledPreamble::PCHStorage::asMemory() const {
+ return const_cast<PCHStorage *>(this)->asMemory();
+}
+
+void PrecompiledPreamble::PCHStorage::destroy() {
+ switch (StorageKind) {
+ case Kind::Empty:
+ return;
+ case Kind::TempFile:
+ asFile().~TempPCHFile();
+ return;
+ case Kind::InMemory:
+ asMemory().~InMemoryPreamble();
+ return;
+ }
+}
+
+void PrecompiledPreamble::PCHStorage::setEmpty() {
+ destroy();
+ StorageKind = Kind::Empty;
+}
+
PrecompiledPreamble::PreambleFileHash
PrecompiledPreamble::PreambleFileHash::createForFile(off_t Size,
time_t ModTime) {
@@ -530,11 +659,47 @@ PrecompiledPreamble::PreambleFileHash::createForMemoryBuffer(
return Result;
}
+void PrecompiledPreamble::setupPreambleStorage(
+ const PCHStorage &Storage, PreprocessorOptions &PreprocessorOpts,
+ IntrusiveRefCntPtr<vfs::FileSystem> &VFS) {
+ if (Storage.getKind() == PCHStorage::Kind::TempFile) {
+ const TempPCHFile &PCHFile = Storage.asFile();
+ PreprocessorOpts.ImplicitPCHInclude = PCHFile.getFilePath();
+
+ // Make sure we can access the PCH file even if we're using a VFS
+ IntrusiveRefCntPtr<vfs::FileSystem> RealFS = vfs::getRealFileSystem();
+ auto PCHPath = PCHFile.getFilePath();
+ if (VFS == RealFS || VFS->exists(PCHPath))
+ return;
+ auto Buf = RealFS->getBufferForFile(PCHPath);
+ if (!Buf) {
+ // We can't read the file even from RealFS, this is clearly an error,
+ // but we'll just leave the current VFS as is and let clang's code
+ // figure out what to do with missing PCH.
+ return;
+ }
+
+ // We have a slight inconsistency here -- we're using the VFS to
+ // read files, but the PCH was generated in the real file system.
+ VFS = createVFSOverlayForPreamblePCH(PCHPath, std::move(*Buf), VFS);
+ } else {
+ assert(Storage.getKind() == PCHStorage::Kind::InMemory);
+ // For in-memory preamble, we have to provide a VFS overlay that makes it
+ // accessible.
+ StringRef PCHPath = getInMemoryPreamblePath();
+ PreprocessorOpts.ImplicitPCHInclude = PCHPath;
+
+ auto Buf = llvm::MemoryBuffer::getMemBuffer(Storage.asMemory().Data);
+ VFS = createVFSOverlayForPreamblePCH(PCHPath, std::move(Buf), VFS);
+ }
+}
+
void PreambleCallbacks::AfterExecute(CompilerInstance &CI) {}
void PreambleCallbacks::AfterPCHEmitted(ASTWriter &Writer) {}
void PreambleCallbacks::HandleTopLevelDecl(DeclGroupRef DG) {}
-void PreambleCallbacks::HandleMacroDefined(const Token &MacroNameTok,
- const MacroDirective *MD) {}
+std::unique_ptr<PPCallbacks> PreambleCallbacks::createPPCallbacks() {
+ return nullptr;
+}
std::error_code clang::make_error_code(BuildPreambleError Error) {
return std::error_code(static_cast<int>(Error), BuildPreambleErrorCategory());