Skip to content

Commit

Permalink
[clang] Make the entire CompilerInvocation ref-counted (#65647)
Browse files Browse the repository at this point in the history
This enables making the whole `CompilerInvocation` more efficient
through copy-on-write.
  • Loading branch information
jansvoboda11 committed Sep 7, 2023
1 parent 7fda1b7 commit 62e576b
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 147 deletions.
150 changes: 63 additions & 87 deletions clang/include/clang/Frontend/CompilerInvocation.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,12 @@ bool ParseDiagnosticArgs(DiagnosticOptions &Opts, llvm::opt::ArgList &Args,
DiagnosticsEngine *Diags = nullptr,
bool DefaultDiagColor = true);

/// The base class of CompilerInvocation with reference semantics.
///
/// This class stores option objects behind reference-counted pointers. This is
/// useful for clients that want to keep some option object around even after
/// CompilerInvocation gets destroyed, without making a copy.
///
/// This is a separate class so that we can implement the copy constructor and
/// assignment here and leave them defaulted in the rest of CompilerInvocation.
class CompilerInvocationRefBase {
public:
/// The base class of CompilerInvocation. It keeps individual option objects
/// behind reference-counted pointers, which is useful for clients that want to
/// keep select option objects alive (even after CompilerInvocation gets
/// destroyed) without making a copy.
class CompilerInvocationBase {
protected:
/// Options controlling the language variant.
std::shared_ptr<LangOptions> LangOpts;

Expand All @@ -86,103 +82,71 @@ class CompilerInvocationRefBase {
IntrusiveRefCntPtr<DiagnosticOptions> DiagnosticOpts;

/// Options controlling the \#include directive.
std::shared_ptr<HeaderSearchOptions> HeaderSearchOpts;
std::shared_ptr<HeaderSearchOptions> HSOpts;

/// Options controlling the preprocessor (aside from \#include handling).
std::shared_ptr<PreprocessorOptions> PreprocessorOpts;
std::shared_ptr<PreprocessorOptions> PPOpts;

/// Options controlling the static analyzer.
AnalyzerOptionsRef AnalyzerOpts;

CompilerInvocationRefBase();
CompilerInvocationRefBase(const CompilerInvocationRefBase &X);
CompilerInvocationRefBase(CompilerInvocationRefBase &&X);
CompilerInvocationRefBase &operator=(CompilerInvocationRefBase X);
CompilerInvocationRefBase &operator=(CompilerInvocationRefBase &&X);
~CompilerInvocationRefBase();

LangOptions &getLangOpts() { return *LangOpts; }
const LangOptions &getLangOpts() const { return *LangOpts; }

TargetOptions &getTargetOpts() { return *TargetOpts.get(); }
const TargetOptions &getTargetOpts() const { return *TargetOpts.get(); }

DiagnosticOptions &getDiagnosticOpts() const { return *DiagnosticOpts; }

HeaderSearchOptions &getHeaderSearchOpts() { return *HeaderSearchOpts; }

const HeaderSearchOptions &getHeaderSearchOpts() const {
return *HeaderSearchOpts;
}

std::shared_ptr<HeaderSearchOptions> getHeaderSearchOptsPtr() const {
return HeaderSearchOpts;
}

std::shared_ptr<PreprocessorOptions> getPreprocessorOptsPtr() {
return PreprocessorOpts;
}

PreprocessorOptions &getPreprocessorOpts() { return *PreprocessorOpts; }

const PreprocessorOptions &getPreprocessorOpts() const {
return *PreprocessorOpts;
}

AnalyzerOptions &getAnalyzerOpts() { return *AnalyzerOpts; }
const AnalyzerOptions &getAnalyzerOpts() const { return *AnalyzerOpts; }
};

/// The base class of CompilerInvocation with value semantics.
class CompilerInvocationValueBase {
protected:
MigratorOptions MigratorOpts;
std::shared_ptr<MigratorOptions> MigratorOpts;

/// Options controlling IRgen and the backend.
CodeGenOptions CodeGenOpts;

/// Options controlling dependency output.
DependencyOutputOptions DependencyOutputOpts;
std::shared_ptr<CodeGenOptions> CodeGenOpts;

/// Options controlling file system operations.
FileSystemOptions FileSystemOpts;
std::shared_ptr<FileSystemOptions> FSOpts;

/// Options controlling the frontend itself.
FrontendOptions FrontendOpts;
std::shared_ptr<FrontendOptions> FrontendOpts;

/// Options controlling dependency output.
std::shared_ptr<DependencyOutputOptions> DependencyOutputOpts;

/// Options controlling preprocessed output.
PreprocessorOutputOptions PreprocessorOutputOpts;
std::shared_ptr<PreprocessorOutputOptions> PreprocessorOutputOpts;

public:
MigratorOptions &getMigratorOpts() { return MigratorOpts; }
const MigratorOptions &getMigratorOpts() const { return MigratorOpts; }

CodeGenOptions &getCodeGenOpts() { return CodeGenOpts; }
const CodeGenOptions &getCodeGenOpts() const { return CodeGenOpts; }

DependencyOutputOptions &getDependencyOutputOpts() {
return DependencyOutputOpts;
}
CompilerInvocationBase();
CompilerInvocationBase(const CompilerInvocationBase &X) { operator=(X); }
CompilerInvocationBase(CompilerInvocationBase &&X) = default;
CompilerInvocationBase &operator=(const CompilerInvocationBase &X);
CompilerInvocationBase &operator=(CompilerInvocationBase &&X) = default;
~CompilerInvocationBase() = default;

const LangOptions &getLangOpts() const { return *LangOpts; }
const TargetOptions &getTargetOpts() const { return *TargetOpts; }
const DiagnosticOptions &getDiagnosticOpts() const { return *DiagnosticOpts; }
const HeaderSearchOptions &getHeaderSearchOpts() const { return *HSOpts; }
const PreprocessorOptions &getPreprocessorOpts() const { return *PPOpts; }
const AnalyzerOptions &getAnalyzerOpts() const { return *AnalyzerOpts; }
const MigratorOptions &getMigratorOpts() const { return *MigratorOpts; }
const CodeGenOptions &getCodeGenOpts() const { return *CodeGenOpts; }
const FileSystemOptions &getFileSystemOpts() const { return *FSOpts; }
const FrontendOptions &getFrontendOpts() const { return *FrontendOpts; }
const DependencyOutputOptions &getDependencyOutputOpts() const {
return DependencyOutputOpts;
return *DependencyOutputOpts;
}

FileSystemOptions &getFileSystemOpts() { return FileSystemOpts; }

const FileSystemOptions &getFileSystemOpts() const {
return FileSystemOpts;
const PreprocessorOutputOptions &getPreprocessorOutputOpts() const {
return *PreprocessorOutputOpts;
}

FrontendOptions &getFrontendOpts() { return FrontendOpts; }
const FrontendOptions &getFrontendOpts() const { return FrontendOpts; }

PreprocessorOutputOptions &getPreprocessorOutputOpts() {
return PreprocessorOutputOpts;
LangOptions &getLangOpts() { return *LangOpts; }
TargetOptions &getTargetOpts() { return *TargetOpts; }
DiagnosticOptions &getDiagnosticOpts() { return *DiagnosticOpts; }
HeaderSearchOptions &getHeaderSearchOpts() { return *HSOpts; }
PreprocessorOptions &getPreprocessorOpts() { return *PPOpts; }
AnalyzerOptions &getAnalyzerOpts() { return *AnalyzerOpts; }
MigratorOptions &getMigratorOpts() { return *MigratorOpts; }
CodeGenOptions &getCodeGenOpts() { return *CodeGenOpts; }
FileSystemOptions &getFileSystemOpts() { return *FSOpts; }
FrontendOptions &getFrontendOpts() { return *FrontendOpts; }
DependencyOutputOptions &getDependencyOutputOpts() {
return *DependencyOutputOpts;
}

const PreprocessorOutputOptions &getPreprocessorOutputOpts() const {
return PreprocessorOutputOpts;
PreprocessorOutputOptions &getPreprocessorOutputOpts() {
return *PreprocessorOutputOpts;
}
};

Expand All @@ -191,9 +155,21 @@ class CompilerInvocationValueBase {
/// This class is designed to represent an abstract "invocation" of the
/// compiler, including data such as the include paths, the code generation
/// options, the warning flags, and so on.
class CompilerInvocation : public CompilerInvocationRefBase,
public CompilerInvocationValueBase {
class CompilerInvocation : public CompilerInvocationBase {
public:
/// Base class internals.
/// @{
using CompilerInvocationBase::LangOpts;
using CompilerInvocationBase::TargetOpts;
using CompilerInvocationBase::DiagnosticOpts;
std::shared_ptr<HeaderSearchOptions> getHeaderSearchOptsPtr() {
return HSOpts;
}
std::shared_ptr<PreprocessorOptions> getPreprocessorOptsPtr() {
return PPOpts;
}
/// @}

/// Create a compiler invocation from a list of input options.
/// \returns true on success.
///
Expand Down
130 changes: 70 additions & 60 deletions clang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,40 +126,49 @@ static Expected<std::optional<uint32_t>> parseToleranceOption(StringRef Arg) {
// Initialization.
//===----------------------------------------------------------------------===//

CompilerInvocationRefBase::CompilerInvocationRefBase()
: LangOpts(new LangOptions()), TargetOpts(new TargetOptions()),
DiagnosticOpts(new DiagnosticOptions()),
HeaderSearchOpts(new HeaderSearchOptions()),
PreprocessorOpts(new PreprocessorOptions()),
AnalyzerOpts(new AnalyzerOptions()) {}

CompilerInvocationRefBase::CompilerInvocationRefBase(
const CompilerInvocationRefBase &X)
: LangOpts(new LangOptions(X.getLangOpts())),
TargetOpts(new TargetOptions(X.getTargetOpts())),
DiagnosticOpts(new DiagnosticOptions(X.getDiagnosticOpts())),
HeaderSearchOpts(new HeaderSearchOptions(X.getHeaderSearchOpts())),
PreprocessorOpts(new PreprocessorOptions(X.getPreprocessorOpts())),
AnalyzerOpts(new AnalyzerOptions(X.getAnalyzerOpts())) {}

CompilerInvocationRefBase::CompilerInvocationRefBase(
CompilerInvocationRefBase &&X) = default;

CompilerInvocationRefBase &
CompilerInvocationRefBase::operator=(CompilerInvocationRefBase X) {
LangOpts.swap(X.LangOpts);
TargetOpts.swap(X.TargetOpts);
DiagnosticOpts.swap(X.DiagnosticOpts);
HeaderSearchOpts.swap(X.HeaderSearchOpts);
PreprocessorOpts.swap(X.PreprocessorOpts);
AnalyzerOpts.swap(X.AnalyzerOpts);
return *this;
namespace {
template <class T> std::shared_ptr<T> make_shared_copy(const T &X) {
return std::make_shared<T>(X);
}

CompilerInvocationRefBase &
CompilerInvocationRefBase::operator=(CompilerInvocationRefBase &&X) = default;

CompilerInvocationRefBase::~CompilerInvocationRefBase() = default;
template <class T>
llvm::IntrusiveRefCntPtr<T> makeIntrusiveRefCntCopy(const T &X) {
return llvm::makeIntrusiveRefCnt<T>(X);
}
} // namespace

CompilerInvocationBase::CompilerInvocationBase()
: LangOpts(std::make_shared<LangOptions>()),
TargetOpts(std::make_shared<TargetOptions>()),
DiagnosticOpts(llvm::makeIntrusiveRefCnt<DiagnosticOptions>()),
HSOpts(std::make_shared<HeaderSearchOptions>()),
PPOpts(std::make_shared<PreprocessorOptions>()),
AnalyzerOpts(llvm::makeIntrusiveRefCnt<AnalyzerOptions>()),
MigratorOpts(std::make_shared<MigratorOptions>()),
CodeGenOpts(std::make_shared<CodeGenOptions>()),
FSOpts(std::make_shared<FileSystemOptions>()),
FrontendOpts(std::make_shared<FrontendOptions>()),
DependencyOutputOpts(std::make_shared<DependencyOutputOptions>()),
PreprocessorOutputOpts(std::make_shared<PreprocessorOutputOptions>()) {}

CompilerInvocationBase &
CompilerInvocationBase::operator=(const CompilerInvocationBase &X) {
if (this != &X) {
LangOpts = make_shared_copy(X.getLangOpts());
TargetOpts = make_shared_copy(X.getTargetOpts());
DiagnosticOpts = makeIntrusiveRefCntCopy(X.getDiagnosticOpts());
HSOpts = make_shared_copy(X.getHeaderSearchOpts());
PPOpts = make_shared_copy(X.getPreprocessorOpts());
AnalyzerOpts = makeIntrusiveRefCntCopy(X.getAnalyzerOpts());
MigratorOpts = make_shared_copy(X.getMigratorOpts());
CodeGenOpts = make_shared_copy(X.getCodeGenOpts());
FSOpts = make_shared_copy(X.getFileSystemOpts());
FrontendOpts = make_shared_copy(X.getFrontendOpts());
DependencyOutputOpts = make_shared_copy(X.getDependencyOutputOpts());
PreprocessorOutputOpts = make_shared_copy(X.getPreprocessorOutputOpts());
}
return *this;
}

//===----------------------------------------------------------------------===//
// Normalizers
Expand Down Expand Up @@ -838,7 +847,7 @@ static void getAllNoBuiltinFuncValues(ArgList &Args,
Funcs.insert(Funcs.end(), Values.begin(), BuiltinEnd);
}

static void GenerateAnalyzerArgs(AnalyzerOptions &Opts,
static void GenerateAnalyzerArgs(const AnalyzerOptions &Opts,
ArgumentConsumer Consumer) {
const AnalyzerOptions *AnalyzerOpts = &Opts;

Expand Down Expand Up @@ -2917,7 +2926,7 @@ std::string CompilerInvocation::GetResourcesPath(const char *Argv0,
return Driver::GetResourcesPath(ClangExecutable, CLANG_RESOURCE_DIR);
}

static void GenerateHeaderSearchArgs(HeaderSearchOptions &Opts,
static void GenerateHeaderSearchArgs(const HeaderSearchOptions &Opts,
ArgumentConsumer Consumer) {
const HeaderSearchOptions *HeaderSearchOpts = &Opts;
#define HEADER_SEARCH_OPTION_WITH_MARSHALLING(...) \
Expand Down Expand Up @@ -4103,12 +4112,12 @@ static bool isStrictlyPreprocessorAction(frontend::ActionKind Action) {
llvm_unreachable("invalid frontend action");
}

static void GeneratePreprocessorArgs(PreprocessorOptions &Opts,
static void GeneratePreprocessorArgs(const PreprocessorOptions &Opts,
ArgumentConsumer Consumer,
const LangOptions &LangOpts,
const FrontendOptions &FrontendOpts,
const CodeGenOptions &CodeGenOpts) {
PreprocessorOptions *PreprocessorOpts = &Opts;
const PreprocessorOptions *PreprocessorOpts = &Opts;

#define PREPROCESSOR_OPTION_WITH_MARSHALLING(...) \
GENERATE_OPTION_WITH_MARSHALLING(Consumer, __VA_ARGS__)
Expand Down Expand Up @@ -4514,15 +4523,15 @@ std::string CompilerInvocation::getModuleHash() const {
#define BENIGN_ENUM_LANGOPT(Name, Type, Bits, Default, Description)
#include "clang/Basic/LangOptions.def"

HBuilder.addRange(LangOpts->ModuleFeatures);
HBuilder.addRange(getLangOpts().ModuleFeatures);

HBuilder.add(LangOpts->ObjCRuntime);
HBuilder.addRange(LangOpts->CommentOpts.BlockCommandNames);
HBuilder.add(getLangOpts().ObjCRuntime);
HBuilder.addRange(getLangOpts().CommentOpts.BlockCommandNames);

// Extend the signature with the target options.
HBuilder.add(TargetOpts->Triple, TargetOpts->CPU, TargetOpts->TuneCPU,
TargetOpts->ABI);
HBuilder.addRange(TargetOpts->FeaturesAsWritten);
HBuilder.add(getTargetOpts().Triple, getTargetOpts().CPU,
getTargetOpts().TuneCPU, getTargetOpts().ABI);
HBuilder.addRange(getTargetOpts().FeaturesAsWritten);

// Extend the signature with preprocessor options.
const PreprocessorOptions &ppOpts = getPreprocessorOpts();
Expand Down Expand Up @@ -4577,7 +4586,7 @@ std::string CompilerInvocation::getModuleHash() const {

// Extend the signature with the enabled sanitizers, if at least one is
// enabled. Sanitizers which cannot affect AST generation aren't hashed.
SanitizerSet SanHash = LangOpts->Sanitize;
SanitizerSet SanHash = getLangOpts().Sanitize;
SanHash.clear(getPPTransparentSanitizers());
if (!SanHash.empty())
HBuilder.add(SanHash.Mask);
Expand All @@ -4590,23 +4599,24 @@ std::string CompilerInvocation::getModuleHash() const {

void CompilerInvocation::generateCC1CommandLine(
ArgumentConsumer Consumer) const {
llvm::Triple T(TargetOpts->Triple);

GenerateFileSystemArgs(FileSystemOpts, Consumer);
GenerateMigratorArgs(MigratorOpts, Consumer);
GenerateAnalyzerArgs(*AnalyzerOpts, Consumer);
GenerateDiagnosticArgs(*DiagnosticOpts, Consumer, false);
GenerateFrontendArgs(FrontendOpts, Consumer, LangOpts->IsHeaderFile);
GenerateTargetArgs(*TargetOpts, Consumer);
GenerateHeaderSearchArgs(*HeaderSearchOpts, Consumer);
GenerateLangArgs(*LangOpts, Consumer, T, FrontendOpts.DashX);
GenerateCodeGenArgs(CodeGenOpts, Consumer, T, FrontendOpts.OutputFile,
&*LangOpts);
GeneratePreprocessorArgs(*PreprocessorOpts, Consumer, *LangOpts, FrontendOpts,
CodeGenOpts);
GeneratePreprocessorOutputArgs(PreprocessorOutputOpts, Consumer,
FrontendOpts.ProgramAction);
GenerateDependencyOutputArgs(DependencyOutputOpts, Consumer);
llvm::Triple T(getTargetOpts().Triple);

GenerateFileSystemArgs(getFileSystemOpts(), Consumer);
GenerateMigratorArgs(getMigratorOpts(), Consumer);
GenerateAnalyzerArgs(getAnalyzerOpts(), Consumer);
GenerateDiagnosticArgs(getDiagnosticOpts(), Consumer,
/*DefaultDiagColor=*/false);
GenerateFrontendArgs(getFrontendOpts(), Consumer, getLangOpts().IsHeaderFile);
GenerateTargetArgs(getTargetOpts(), Consumer);
GenerateHeaderSearchArgs(getHeaderSearchOpts(), Consumer);
GenerateLangArgs(getLangOpts(), Consumer, T, getFrontendOpts().DashX);
GenerateCodeGenArgs(getCodeGenOpts(), Consumer, T,
getFrontendOpts().OutputFile, &getLangOpts());
GeneratePreprocessorArgs(getPreprocessorOpts(), Consumer, getLangOpts(),
getFrontendOpts(), getCodeGenOpts());
GeneratePreprocessorOutputArgs(getPreprocessorOutputOpts(), Consumer,
getFrontendOpts().ProgramAction);
GenerateDependencyOutputArgs(getDependencyOutputOpts(), Consumer);
}

std::vector<std::string> CompilerInvocation::getCC1CommandLine() const {
Expand Down

0 comments on commit 62e576b

Please sign in to comment.