[Frontend] Simplify PrecompiledPreamble::PCHStorage. NFC

- Remove fiddly union, preambles are heavyweight
- Remove fiddly move constructors in TempPCHFile and PCHStorage, use unique_ptr
- Remove unneccesary accessors on PCHStorage
- Remove trivial InMemoryStorage
- Move implementation details into cpp file

This is a prefactoring, followup change will change the in-memory PCHStorage to
avoid extra string copies while creating it.

Differential Revision: https://reviews.llvm.org/D124177
This commit is contained in:
Sam McCall 2022-04-21 16:37:01 +02:00
parent 889847922d
commit af3fb07154
2 changed files with 116 additions and 237 deletions

View File

@ -17,7 +17,6 @@
#include "clang/Lex/Preprocessor.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/AlignOf.h"
#include "llvm/Support/MD5.h"
#include <cstddef>
#include <memory>
@ -86,8 +85,9 @@ public:
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
bool StoreInMemory, PreambleCallbacks &Callbacks);
PrecompiledPreamble(PrecompiledPreamble &&) = default;
PrecompiledPreamble &operator=(PrecompiledPreamble &&) = default;
PrecompiledPreamble(PrecompiledPreamble &&);
PrecompiledPreamble &operator=(PrecompiledPreamble &&);
~PrecompiledPreamble();
/// PreambleBounds used to build the preamble.
PreambleBounds getBounds() const;
@ -128,79 +128,12 @@ public:
llvm::MemoryBuffer *MainFileBuffer) const;
private:
PrecompiledPreamble(PCHStorage Storage, std::vector<char> PreambleBytes,
PrecompiledPreamble(std::unique_ptr<PCHStorage> Storage,
std::vector<char> PreambleBytes,
bool PreambleEndsAtStartOfLine,
llvm::StringMap<PreambleFileHash> FilesInPreamble,
llvm::StringSet<> MissingFiles);
/// A temp file that would be deleted on destructor call. If destructor is not
/// called for any reason, the file will be deleted at static objects'
/// destruction.
/// An assertion will fire if two TempPCHFiles are created with the same name,
/// so it's not intended to be used outside preamble-handling.
class TempPCHFile {
public:
// A main method used to construct TempPCHFile.
static llvm::ErrorOr<TempPCHFile> CreateNewPreamblePCHFile();
private:
TempPCHFile(std::string FilePath);
public:
TempPCHFile(TempPCHFile &&Other);
TempPCHFile &operator=(TempPCHFile &&Other);
TempPCHFile(const TempPCHFile &) = delete;
~TempPCHFile();
/// A path where temporary file is stored.
llvm::StringRef getFilePath() const;
private:
void RemoveFileIfPresent();
private:
llvm::Optional<std::string> FilePath;
};
class InMemoryPreamble {
public:
std::string Data;
};
class PCHStorage {
public:
enum class Kind { Empty, InMemory, TempFile };
PCHStorage() = default;
PCHStorage(TempPCHFile File);
PCHStorage(InMemoryPreamble Memory);
PCHStorage(const PCHStorage &) = delete;
PCHStorage &operator=(const PCHStorage &) = delete;
PCHStorage(PCHStorage &&Other);
PCHStorage &operator=(PCHStorage &&Other);
~PCHStorage();
Kind getKind() const;
TempPCHFile &asFile();
const TempPCHFile &asFile() const;
InMemoryPreamble &asMemory();
const InMemoryPreamble &asMemory() const;
private:
void destroy();
void setEmpty();
private:
Kind StorageKind = Kind::Empty;
llvm::AlignedCharArrayUnion<TempPCHFile, InMemoryPreamble> Storage = {};
};
/// Data used to determine if a file used in the preamble has been changed.
struct PreambleFileHash {
/// All files have size set.
@ -245,7 +178,7 @@ private:
IntrusiveRefCntPtr<llvm::vfs::FileSystem> &VFS);
/// Manages the memory buffer or temporary file that stores the PCH.
PCHStorage Storage;
std::unique_ptr<PCHStorage> Storage;
/// Keeps track of the files that were used when computing the
/// preamble, with both their buffer size and their modification time.
///

View File

@ -11,10 +11,8 @@
//===----------------------------------------------------------------------===//
#include "clang/Frontend/PrecompiledPreamble.h"
#include "clang/AST/DeclObjC.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/LangStandard.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/FrontendActions.h"
@ -25,7 +23,6 @@
#include "clang/Lex/PreprocessorOptions.h"
#include "clang/Serialization/ASTWriter.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Config/llvm-config.h"
@ -192,6 +189,49 @@ void TemporaryFiles::removeFile(StringRef File) {
llvm::sys::fs::remove(File);
}
// A temp file that would be deleted on destructor call. If destructor is not
// called for any reason, the file will be deleted at static objects'
// destruction.
// An assertion will fire if two TempPCHFiles are created with the same name,
// so it's not intended to be used outside preamble-handling.
class TempPCHFile {
public:
// A main method used to construct TempPCHFile.
static std::unique_ptr<TempPCHFile> create() {
// FIXME: This is a hack so that we can override the preamble file during
// crash-recovery testing, which is the only case where the preamble files
// are not necessarily cleaned up.
if (const char *TmpFile = ::getenv("CINDEXTEST_PREAMBLE_FILE"))
return std::unique_ptr<TempPCHFile>(new TempPCHFile(TmpFile));
llvm::SmallString<64> 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;
if (auto EC =
llvm::sys::fs::createTemporaryFile("preamble", "pch", FD, File))
return nullptr;
// We only needed to make sure the file exists, close the file right away.
llvm::sys::Process::SafelyCloseFileDescriptor(FD);
return std::unique_ptr<TempPCHFile>(new TempPCHFile(File.str().str()));
}
TempPCHFile &operator=(const TempPCHFile &) = delete;
TempPCHFile(const TempPCHFile &) = delete;
~TempPCHFile() { TemporaryFiles::getInstance().removeFile(FilePath); };
/// A path where temporary file is stored.
llvm::StringRef getFilePath() const { return FilePath; };
private:
TempPCHFile(std::string FilePath) : FilePath(std::move(FilePath)) {
TemporaryFiles::getInstance().addFile(this->FilePath);
}
std::string FilePath;
};
class PrecompilePreambleAction : public ASTFrontendAction {
public:
PrecompilePreambleAction(std::string *InMemStorage,
@ -308,6 +348,55 @@ PreambleBounds clang::ComputePreambleBounds(const LangOptions &LangOpts,
return Lexer::ComputePreamble(Buffer.getBuffer(), LangOpts, MaxLines);
}
class PrecompiledPreamble::PCHStorage {
public:
static std::unique_ptr<PCHStorage> file(std::unique_ptr<TempPCHFile> File) {
assert(File);
std::unique_ptr<PCHStorage> S(new PCHStorage());
S->File = std::move(File);
return S;
}
static std::unique_ptr<PCHStorage> inMemory() {
std::unique_ptr<PCHStorage> S(new PCHStorage());
S->Memory.emplace();
return S;
}
enum class Kind { InMemory, TempFile };
Kind getKind() const {
if (Memory)
return Kind::InMemory;
if (File)
return Kind::TempFile;
llvm_unreachable("Neither Memory nor File?");
}
llvm::StringRef filePath() const {
assert(getKind() == Kind::TempFile);
return File->getFilePath();
}
llvm::StringRef memoryContents() const {
assert(getKind() == Kind::InMemory);
return *Memory;
}
std::string &memoryBufferForWrite() {
assert(getKind() == Kind::InMemory);
return *Memory;
}
private:
PCHStorage() = default;
PCHStorage(const PCHStorage &) = delete;
PCHStorage &operator=(const PCHStorage &) = delete;
llvm::Optional<std::string> Memory;
std::unique_ptr<TempPCHFile> File;
};
PrecompiledPreamble::~PrecompiledPreamble() = default;
PrecompiledPreamble::PrecompiledPreamble(PrecompiledPreamble &&) = default;
PrecompiledPreamble &
PrecompiledPreamble::operator=(PrecompiledPreamble &&) = default;
llvm::ErrorOr<PrecompiledPreamble> PrecompiledPreamble::Build(
const CompilerInvocation &Invocation,
const llvm::MemoryBuffer *MainFileBuffer, PreambleBounds Bounds,
@ -322,20 +411,18 @@ llvm::ErrorOr<PrecompiledPreamble> PrecompiledPreamble::Build(
PreprocessorOptions &PreprocessorOpts =
PreambleInvocation->getPreprocessorOpts();
llvm::Optional<TempPCHFile> TempFile;
if (!StoreInMemory) {
std::unique_ptr<PCHStorage> Storage;
if (StoreInMemory) {
Storage = PCHStorage::inMemory();
} else {
// Create a temporary file for the precompiled preamble. In rare
// circumstances, this can fail.
llvm::ErrorOr<PrecompiledPreamble::TempPCHFile> PreamblePCHFile =
PrecompiledPreamble::TempPCHFile::CreateNewPreamblePCHFile();
std::unique_ptr<TempPCHFile> PreamblePCHFile = TempPCHFile::create();
if (!PreamblePCHFile)
return BuildPreambleError::CouldntCreateTempFile;
TempFile = std::move(*PreamblePCHFile);
Storage = PCHStorage::file(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.
std::vector<char> PreambleBytes(MainFileBuffer->getBufferStart(),
@ -345,9 +432,8 @@ llvm::ErrorOr<PrecompiledPreamble> PrecompiledPreamble::Build(
// Tell the compiler invocation to generate a temporary precompiled header.
FrontendOpts.ProgramAction = frontend::GeneratePCH;
FrontendOpts.OutputFile =
std::string(StoreInMemory ? getInMemoryPreamblePath()
: Storage.asFile().getFilePath());
FrontendOpts.OutputFile = std::string(
StoreInMemory ? getInMemoryPreamblePath() : Storage->filePath());
PreprocessorOpts.PrecompiledPreambleBytes.first = 0;
PreprocessorOpts.PrecompiledPreambleBytes.second = false;
// Inform preprocessor to record conditional stack when building the preamble.
@ -411,7 +497,7 @@ llvm::ErrorOr<PrecompiledPreamble> PrecompiledPreamble::Build(
std::unique_ptr<PrecompilePreambleAction> Act;
Act.reset(new PrecompilePreambleAction(
StoreInMemory ? &Storage.asMemory().Data : nullptr, Callbacks));
StoreInMemory ? &Storage->memoryBufferForWrite() : nullptr, Callbacks));
if (!Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0]))
return BuildPreambleError::BeginSourceFileFailed;
@ -475,16 +561,12 @@ PreambleBounds PrecompiledPreamble::getBounds() const {
}
std::size_t PrecompiledPreamble::getSize() const {
switch (Storage.getKind()) {
case PCHStorage::Kind::Empty:
assert(false && "Calling getSize() on invalid PrecompiledPreamble. "
"Was it std::moved?");
return 0;
switch (Storage->getKind()) {
case PCHStorage::Kind::InMemory:
return Storage.asMemory().Data.size();
return Storage->memoryContents().size();
case PCHStorage::Kind::TempFile: {
uint64_t Result;
if (llvm::sys::fs::file_size(Storage.asFile().getFilePath(), Result))
if (llvm::sys::fs::file_size(Storage->filePath(), Result))
return 0;
assert(Result <= std::numeric_limits<std::size_t>::max() &&
@ -621,7 +703,7 @@ void PrecompiledPreamble::OverridePreamble(
}
PrecompiledPreamble::PrecompiledPreamble(
PCHStorage Storage, std::vector<char> PreambleBytes,
std::unique_ptr<PCHStorage> Storage, std::vector<char> PreambleBytes,
bool PreambleEndsAtStartOfLine,
llvm::StringMap<PreambleFileHash> FilesInPreamble,
llvm::StringSet<> MissingFiles)
@ -629,142 +711,7 @@ PrecompiledPreamble::PrecompiledPreamble(
MissingFiles(std::move(MissingFiles)),
PreambleBytes(std::move(PreambleBytes)),
PreambleEndsAtStartOfLine(PreambleEndsAtStartOfLine) {
assert(this->Storage.getKind() != PCHStorage::Kind::Empty);
}
llvm::ErrorOr<PrecompiledPreamble::TempPCHFile>
PrecompiledPreamble::TempPCHFile::CreateNewPreamblePCHFile() {
// FIXME: This is a hack so that we can override the preamble file during
// crash-recovery testing, which is the only case where the preamble files
// are not necessarily cleaned up.
if (const char *TmpFile = ::getenv("CINDEXTEST_PREAMBLE_FILE"))
return TempPCHFile(TmpFile);
llvm::SmallString<64> 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("preamble", "pch", 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::string(std::move(File).str()));
}
PrecompiledPreamble::TempPCHFile::TempPCHFile(std::string FilePath)
: FilePath(std::move(FilePath)) {
TemporaryFiles::getInstance().addFile(*this->FilePath);
}
PrecompiledPreamble::TempPCHFile::TempPCHFile(TempPCHFile &&Other) {
FilePath = std::move(Other.FilePath);
Other.FilePath = None;
}
PrecompiledPreamble::TempPCHFile &PrecompiledPreamble::TempPCHFile::
operator=(TempPCHFile &&Other) {
RemoveFileIfPresent();
FilePath = std::move(Other.FilePath);
Other.FilePath = None;
return *this;
}
PrecompiledPreamble::TempPCHFile::~TempPCHFile() { RemoveFileIfPresent(); }
void PrecompiledPreamble::TempPCHFile::RemoveFileIfPresent() {
if (FilePath) {
TemporaryFiles::getInstance().removeFile(*FilePath);
FilePath = None;
}
}
llvm::StringRef PrecompiledPreamble::TempPCHFile::getFilePath() const {
assert(FilePath && "TempPCHFile doesn't have a FilePath. Had it been moved?");
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);
}
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);
}
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;
assert(this->Storage != nullptr);
}
PrecompiledPreamble::PreambleFileHash
@ -810,20 +757,19 @@ void PrecompiledPreamble::configurePreamble(
PreprocessorOpts.DisablePCHOrModuleValidation =
DisableValidationForModuleKind::PCH;
setupPreambleStorage(Storage, PreprocessorOpts, VFS);
setupPreambleStorage(*Storage, PreprocessorOpts, VFS);
}
void PrecompiledPreamble::setupPreambleStorage(
const PCHStorage &Storage, PreprocessorOptions &PreprocessorOpts,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> &VFS) {
if (Storage.getKind() == PCHStorage::Kind::TempFile) {
const TempPCHFile &PCHFile = Storage.asFile();
PreprocessorOpts.ImplicitPCHInclude = std::string(PCHFile.getFilePath());
llvm::StringRef PCHPath = Storage.filePath();
PreprocessorOpts.ImplicitPCHInclude = PCHPath.str();
// Make sure we can access the PCH file even if we're using a VFS
IntrusiveRefCntPtr<llvm::vfs::FileSystem> RealFS =
llvm::vfs::getRealFileSystem();
auto PCHPath = PCHFile.getFilePath();
if (VFS == RealFS || VFS->exists(PCHPath))
return;
auto Buf = RealFS->getBufferForFile(PCHPath);
@ -844,7 +790,7 @@ void PrecompiledPreamble::setupPreambleStorage(
StringRef PCHPath = getInMemoryPreamblePath();
PreprocessorOpts.ImplicitPCHInclude = std::string(PCHPath);
auto Buf = llvm::MemoryBuffer::getMemBuffer(Storage.asMemory().Data);
auto Buf = llvm::MemoryBuffer::getMemBuffer(Storage.memoryContents());
VFS = createVFSOverlayForPreamblePCH(PCHPath, std::move(Buf), VFS);
}
}