https://github.com/jansvoboda11 updated https://github.com/llvm/llvm-project/pull/65412:
>From 968fc04bc9bdcc33bb3f9401073343d2f69369f3 Mon Sep 17 00:00:00 2001 From: Jan Svoboda <jan_svob...@apple.com> Date: Fri, 1 Sep 2023 15:07:23 -0700 Subject: [PATCH 1/5] [llvm][adt] Implement `IntrusiveRefCntPtr::unique()` --- llvm/include/llvm/ADT/IntrusiveRefCntPtr.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/llvm/include/llvm/ADT/IntrusiveRefCntPtr.h b/llvm/include/llvm/ADT/IntrusiveRefCntPtr.h index e41eb0639ce30e..5a7f93f4a99363 100644 --- a/llvm/include/llvm/ADT/IntrusiveRefCntPtr.h +++ b/llvm/include/llvm/ADT/IntrusiveRefCntPtr.h @@ -93,6 +93,8 @@ template <class Derived> class RefCountedBase { #endif public: + bool Unique() const { return RefCount == 1; } + void Retain() const { ++RefCount; } void Release() const { @@ -124,6 +126,8 @@ template <class Derived> class ThreadSafeRefCountedBase { #endif public: + bool Unique() const { return RefCount.load() == 1; } + void Retain() const { RefCount.fetch_add(1, std::memory_order_relaxed); } void Release() const { @@ -155,6 +159,7 @@ template <class Derived> class ThreadSafeRefCountedBase { /// Bar.h could use IntrusiveRefCntPtr<Foo>, although it still couldn't call any /// functions on Foo itself, because Foo would be an incomplete type. template <typename T> struct IntrusiveRefCntPtrInfo { + static bool unique(T *obj) { return obj->Unique(); } static void retain(T *obj) { obj->Retain(); } static void release(T *obj) { obj->Release(); } }; @@ -213,6 +218,8 @@ template <typename T> class IntrusiveRefCntPtr { void resetWithoutRelease() { Obj = nullptr; } + bool unique() { return Obj && IntrusiveRefCntPtrInfo<T>::unique(Obj); } + private: void retain() { if (Obj) >From d02f3e8d5626c45a8cab1080e7c72f414bb53870 Mon Sep 17 00:00:00 2001 From: Jan Svoboda <jan_svob...@apple.com> Date: Thu, 31 Aug 2023 11:39:32 -0700 Subject: [PATCH 2/5] [clang][ci] Introduce copy-on-write `CompilerInvocation` --- clang/include/clang/Basic/CodeGenOptions.h | 1 + clang/include/clang/Basic/DiagnosticOptions.h | 1 + clang/include/clang/Basic/LangOptions.h | 1 + .../clang/Frontend/CompilerInvocation.h | 312 ++++++++++++------ clang/lib/Frontend/CompilerInvocation.cpp | 208 +++++++----- 5 files changed, 343 insertions(+), 180 deletions(-) diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h index 14fc94fe27f995..c6d7db32f2d266 100644 --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -32,6 +32,7 @@ namespace clang { /// that this large collection of bitfields is a trivial class type. class CodeGenOptionsBase { friend class CompilerInvocation; + friend struct CompilerInvocationBase; public: #define CODEGENOPT(Name, Bits, Default) unsigned Name : Bits; diff --git a/clang/include/clang/Basic/DiagnosticOptions.h b/clang/include/clang/Basic/DiagnosticOptions.h index 0f3120859ecef6..9fb84415e22ead 100644 --- a/clang/include/clang/Basic/DiagnosticOptions.h +++ b/clang/include/clang/Basic/DiagnosticOptions.h @@ -72,6 +72,7 @@ class DiagnosticOptions : public RefCountedBase<DiagnosticOptions>{ clang::DiagnosticsEngine *, bool); friend class CompilerInvocation; + friend struct CompilerInvocationBase; public: enum TextDiagnosticFormat { Clang, MSVC, Vi, SARIF }; diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index 2adf4751444726..aa649e675d9024 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -34,6 +34,7 @@ namespace clang { /// this large collection of bitfields is a trivial class type. class LangOptionsBase { friend class CompilerInvocation; + friend struct CompilerInvocationBase; public: // Define simple language options (with no accessors). diff --git a/clang/include/clang/Frontend/CompilerInvocation.h b/clang/include/clang/Frontend/CompilerInvocation.h index 5dc55bb7abdbab..e57844333eaceb 100644 --- a/clang/include/clang/Frontend/CompilerInvocation.h +++ b/clang/include/clang/Frontend/CompilerInvocation.h @@ -20,8 +20,8 @@ #include "clang/Frontend/MigratorOptions.h" #include "clang/Frontend/PreprocessorOutputOptions.h" #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" -#include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" #include <memory> #include <string> @@ -66,6 +66,7 @@ bool ParseDiagnosticArgs(DiagnosticOptions &Opts, llvm::opt::ArgList &Args, DiagnosticsEngine *Diags = nullptr, bool DefaultDiagColor = true); +namespace CompilerInvocationDetail { /// The base class of CompilerInvocation with reference semantics. /// /// This class stores option objects behind reference-counted pointers. This is @@ -74,8 +75,8 @@ bool ParseDiagnosticArgs(DiagnosticOptions &Opts, llvm::opt::ArgList &Args, /// /// 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: +class RefBase { +protected: /// Options controlling the language variant. std::shared_ptr<LangOptions> LangOpts; @@ -94,47 +95,37 @@ class CompilerInvocationRefBase { /// Options controlling the static analyzer. AnalyzerOptionsRef AnalyzerOpts; - CompilerInvocationRefBase(); - CompilerInvocationRefBase(const CompilerInvocationRefBase &X); - CompilerInvocationRefBase(CompilerInvocationRefBase &&X); - CompilerInvocationRefBase &operator=(CompilerInvocationRefBase X); - CompilerInvocationRefBase &operator=(CompilerInvocationRefBase &&X); - ~CompilerInvocationRefBase(); + struct ShallowCopy {}; + struct DeepCopy {}; - LangOptions &getLangOpts() { return *LangOpts; } - const LangOptions &getLangOpts() const { return *LangOpts; } + RefBase(); - TargetOptions &getTargetOpts() { return *TargetOpts.get(); } - const TargetOptions &getTargetOpts() const { return *TargetOpts.get(); } + RefBase(const RefBase &X, DeepCopy); + RefBase(const RefBase &X, ShallowCopy); + RefBase(const RefBase &) = delete; - DiagnosticOptions &getDiagnosticOpts() const { return *DiagnosticOpts; } + RefBase &assign(const RefBase &X, DeepCopy); + RefBase &assign(const RefBase &X, ShallowCopy); + RefBase &operator=(const RefBase &) = delete; - HeaderSearchOptions &getHeaderSearchOpts() { return *HeaderSearchOpts; } + RefBase(RefBase &&); + RefBase &operator=(RefBase &&); - const HeaderSearchOptions &getHeaderSearchOpts() const { - return *HeaderSearchOpts; - } + ~RefBase(); - 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; } +public: + // clang-format off + const LangOptions &getLangOpts() const { return *LangOpts; } + const TargetOptions &getTargetOpts() const { return *TargetOpts; } + const DiagnosticOptions &getDiagnosticOpts() const { return *DiagnosticOpts; } + const HeaderSearchOptions &getHeaderSearchOpts() const { return *HeaderSearchOpts; } + const PreprocessorOptions &getPreprocessorOpts() const { return *PreprocessorOpts; } const AnalyzerOptions &getAnalyzerOpts() const { return *AnalyzerOpts; } + // clang-format on }; /// The base class of CompilerInvocation with value semantics. -class CompilerInvocationValueBase { +class ValBase { protected: MigratorOptions MigratorOpts; @@ -154,36 +145,87 @@ class CompilerInvocationValueBase { PreprocessorOutputOptions PreprocessorOutputOpts; public: - MigratorOptions &getMigratorOpts() { return MigratorOpts; } + // clang-format off const MigratorOptions &getMigratorOpts() const { return MigratorOpts; } - - CodeGenOptions &getCodeGenOpts() { return CodeGenOpts; } const CodeGenOptions &getCodeGenOpts() const { return CodeGenOpts; } + const DependencyOutputOptions &getDependencyOutputOpts() const { return DependencyOutputOpts; } + const FileSystemOptions &getFileSystemOpts() const { return FileSystemOpts; } + const FrontendOptions &getFrontendOpts() const { return FrontendOpts; } + const PreprocessorOutputOptions &getPreprocessorOutputOpts() const { return PreprocessorOutputOpts; } + // clang-format on +}; +} // namespace CompilerInvocationDetail - DependencyOutputOptions &getDependencyOutputOpts() { - return DependencyOutputOpts; - } +struct CompilerInvocationBase : CompilerInvocationDetail::RefBase, + CompilerInvocationDetail::ValBase { + CompilerInvocationBase() = default; - const DependencyOutputOptions &getDependencyOutputOpts() const { - return DependencyOutputOpts; - } + CompilerInvocationBase(const CompilerInvocationBase &X, DeepCopy) + : RefBase(X, DeepCopy{}), ValBase(X) {} - FileSystemOptions &getFileSystemOpts() { return FileSystemOpts; } + CompilerInvocationBase(const CompilerInvocationBase &X, ShallowCopy) + : RefBase(X, ShallowCopy{}), ValBase(X) {} - const FileSystemOptions &getFileSystemOpts() const { - return FileSystemOpts; + CompilerInvocationBase &assign(const CompilerInvocationBase &X, DeepCopy) { + RefBase::assign(X, DeepCopy{}); + ValBase::operator=(X); + return *this; } - FrontendOptions &getFrontendOpts() { return FrontendOpts; } - const FrontendOptions &getFrontendOpts() const { return FrontendOpts; } - - PreprocessorOutputOptions &getPreprocessorOutputOpts() { - return PreprocessorOutputOpts; + CompilerInvocationBase &assign(const CompilerInvocationBase &X, ShallowCopy) { + RefBase::assign(X, ShallowCopy{}); + ValBase::operator=(X); + return *this; } - const PreprocessorOutputOptions &getPreprocessorOutputOpts() const { - return PreprocessorOutputOpts; + using StringAllocator = llvm::function_ref<const char *(const Twine &)>; + /// Generate cc1-compatible command line arguments from this instance. + /// + /// \param [out] Args - The generated arguments. Note that the caller is + /// responsible for inserting the path to the clang executable and "-cc1" if + /// desired. + /// \param SA - A function that given a Twine can allocate storage for a given + /// command line argument and return a pointer to the newly allocated string. + /// The returned pointer is what gets appended to Args. + void generateCC1CommandLine(llvm::SmallVectorImpl<const char *> &Args, + StringAllocator SA) const { + generateCC1CommandLine([&](const Twine &Arg) { + // No need to allocate static string literals. + Args.push_back(Arg.isSingleStringLiteral() + ? Arg.getSingleStringRef().data() + : SA(Arg)); + }); } + + using ArgumentConsumer = llvm::function_ref<void(const Twine &)>; + /// Generate cc1-compatible command line arguments from this instance. + /// + /// \param Consumer - Callback that gets invoked for every single generated + /// command line argument. + void generateCC1CommandLine(ArgumentConsumer Consumer) const; + + /// Generate cc1-compatible command line arguments from this instance, + /// wrapping the result as a std::vector<std::string>. + /// + /// This is a (less-efficient) wrapper over generateCC1CommandLine(). + std::vector<std::string> getCC1CommandLine() const; + + /// Generate command line options from DiagnosticOptions. + static void GenerateDiagnosticArgs(const DiagnosticOptions &Opts, + ArgumentConsumer Consumer, + bool DefaultDiagColor); + + /// Generate command line options from LangOptions. + static void GenerateLangArgs(const LangOptions &Opts, + ArgumentConsumer Consumer, const llvm::Triple &T, + InputKind IK); + + // Generate command line options from CodeGenOptions. + static void GenerateCodeGenArgs(const CodeGenOptions &Opts, + ArgumentConsumer Consumer, + const llvm::Triple &T, + const std::string &OutputFile, + const LangOptions *LangOpts); }; /// Helper class for holding the data necessary to invoke the compiler. @@ -191,9 +233,70 @@ 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: + CompilerInvocation() = default; + + CompilerInvocation(const CompilerInvocation &X) + : CompilerInvocationBase(X, DeepCopy{}) {} + + CompilerInvocation &operator=(const CompilerInvocation &X) { + CompilerInvocationBase::assign(X, DeepCopy{}); + return *this; + } + + // Mutable RefBase accessors. + + LangOptions &getLangOpts() { return *LangOpts; } + TargetOptions &getTargetOpts() { return *TargetOpts; } + DiagnosticOptions &getDiagnosticOpts() { return *DiagnosticOpts; } + HeaderSearchOptions &getHeaderSearchOpts() { return *HeaderSearchOpts; } + PreprocessorOptions &getPreprocessorOpts() { return *PreprocessorOpts; } + AnalyzerOptions &getAnalyzerOpts() { return *AnalyzerOpts; } + + // Const RefBase accessors. + + using RefBase::getLangOpts; + using RefBase::getTargetOpts; + using RefBase::getDiagnosticOpts; + using RefBase::getHeaderSearchOpts; + using RefBase::getPreprocessorOpts; + using RefBase::getAnalyzerOpts; + + // Mutable ValBase accessors. + + // clang-format off + MigratorOptions &getMigratorOpts() { return MigratorOpts; } + CodeGenOptions &getCodeGenOpts() { return CodeGenOpts; } + DependencyOutputOptions &getDependencyOutputOpts() { return DependencyOutputOpts; } + FileSystemOptions &getFileSystemOpts() { return FileSystemOpts; } + FrontendOptions &getFrontendOpts() { return FrontendOpts; } + PreprocessorOutputOptions &getPreprocessorOutputOpts() { return PreprocessorOutputOpts; } + // clang-format on + + // Const ValBase accessors. + + using ValBase::getMigratorOpts; + using ValBase::getCodeGenOpts; + using ValBase::getDependencyOutputOpts; + using ValBase::getFileSystemOpts; + using ValBase::getFrontendOpts; + using ValBase::getPreprocessorOutputOpts; + + /// RefBase innards + + std::shared_ptr<HeaderSearchOptions> getHeaderSearchOptsPtr() const { + return HeaderSearchOpts; + } + + std::shared_ptr<PreprocessorOptions> getPreprocessorOptsPtr() const { + return PreprocessorOpts; + } + + using CompilerInvocationBase::DiagnosticOpts; + using CompilerInvocationBase::TargetOpts; + using CompilerInvocationBase::LangOpts; + /// Create a compiler invocation from a list of input options. /// \returns true on success. /// @@ -224,38 +327,6 @@ class CompilerInvocation : public CompilerInvocationRefBase, /// identifying the conditions under which the module was built. std::string getModuleHash() const; - using StringAllocator = llvm::function_ref<const char *(const Twine &)>; - /// Generate cc1-compatible command line arguments from this instance. - /// - /// \param [out] Args - The generated arguments. Note that the caller is - /// responsible for inserting the path to the clang executable and "-cc1" if - /// desired. - /// \param SA - A function that given a Twine can allocate storage for a given - /// command line argument and return a pointer to the newly allocated string. - /// The returned pointer is what gets appended to Args. - void generateCC1CommandLine(llvm::SmallVectorImpl<const char *> &Args, - StringAllocator SA) const { - generateCC1CommandLine([&](const Twine &Arg) { - // No need to allocate static string literals. - Args.push_back(Arg.isSingleStringLiteral() - ? Arg.getSingleStringRef().data() - : SA(Arg)); - }); - } - - using ArgumentConsumer = llvm::function_ref<void(const Twine &)>; - /// Generate cc1-compatible command line arguments from this instance. - /// - /// \param Consumer - Callback that gets invoked for every single generated - /// command line argument. - void generateCC1CommandLine(ArgumentConsumer Consumer) const; - - /// Generate cc1-compatible command line arguments from this instance, - /// wrapping the result as a std::vector<std::string>. - /// - /// This is a (less-efficient) wrapper over generateCC1CommandLine(). - std::vector<std::string> getCC1CommandLine() const; - /// Check that \p Args can be parsed and re-serialized without change, /// emiting diagnostics for any differences. /// @@ -280,35 +351,70 @@ class CompilerInvocation : public CompilerInvocationRefBase, ArrayRef<const char *> CommandLineArgs, DiagnosticsEngine &Diags, const char *Argv0); - /// Generate command line options from DiagnosticOptions. - static void GenerateDiagnosticArgs(const DiagnosticOptions &Opts, - ArgumentConsumer Consumer, - bool DefaultDiagColor); - /// Parse command line options that map to LangOptions. static bool ParseLangArgs(LangOptions &Opts, llvm::opt::ArgList &Args, InputKind IK, const llvm::Triple &T, std::vector<std::string> &Includes, DiagnosticsEngine &Diags); - /// Generate command line options from LangOptions. - static void GenerateLangArgs(const LangOptions &Opts, - ArgumentConsumer Consumer, const llvm::Triple &T, - InputKind IK); - /// Parse command line options that map to CodeGenOptions. static bool ParseCodeGenArgs(CodeGenOptions &Opts, llvm::opt::ArgList &Args, InputKind IK, DiagnosticsEngine &Diags, const llvm::Triple &T, const std::string &OutputFile, const LangOptions &LangOptsRef); +}; - // Generate command line options from CodeGenOptions. - static void GenerateCodeGenArgs(const CodeGenOptions &Opts, - ArgumentConsumer Consumer, - const llvm::Triple &T, - const std::string &OutputFile, - const LangOptions *LangOpts); +/// Same as CompilerInvocation, but copies are on-demand and per-opts-object. +struct CowCompilerInvocation : CompilerInvocationBase { + CowCompilerInvocation(const CompilerInvocation &X) + : CompilerInvocationBase(X, DeepCopy{}) {} + + CowCompilerInvocation(const CowCompilerInvocation &X) + : CompilerInvocationBase(X, ShallowCopy{}) {} + + CowCompilerInvocation &operator=(const CowCompilerInvocation &X) { + CompilerInvocationBase::assign(X, ShallowCopy{}); + return *this; + } + + // Mutable RefBase accessors. + + LangOptions &getMutLangOpts(); + TargetOptions &getMutTargetOpts(); + DiagnosticOptions &getMutDiagnosticOpts(); + HeaderSearchOptions &getMutHeaderSearchOpts(); + PreprocessorOptions &getMutPreprocessorOpts(); + AnalyzerOptions &getMutAnalyzerOpts(); + + // Const RefBase accessors. + + using RefBase::getLangOpts; + using RefBase::getTargetOpts; + using RefBase::getDiagnosticOpts; + using RefBase::getHeaderSearchOpts; + using RefBase::getPreprocessorOpts; + using RefBase::getAnalyzerOpts; + + // Mutable ValBase accessors. + + // clang-format off + MigratorOptions &getMutMigratorOpts() { return MigratorOpts; } + CodeGenOptions &getMutCodeGenOpts() { return CodeGenOpts; } + DependencyOutputOptions &getMutDependencyOutputOpts() { return DependencyOutputOpts; } + FileSystemOptions &getMutFileSystemOpts() { return FileSystemOpts; } + FrontendOptions &getMutFrontendOpts() { return FrontendOpts; } + PreprocessorOutputOptions &getMutPreprocessorOutputOpts() { return PreprocessorOutputOpts; } + // clang-format on + + // Const ValBase accessors. + + using ValBase::getMigratorOpts; + using ValBase::getCodeGenOpts; + using ValBase::getDependencyOutputOpts; + using ValBase::getFileSystemOpts; + using ValBase::getFrontendOpts; + using ValBase::getPreprocessorOutputOpts; }; IntrusiveRefCntPtr<llvm::vfs::FileSystem> diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 11ffb3d6630d1f..41972c7b05f896 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -123,49 +123,101 @@ static Expected<std::optional<uint32_t>> parseToleranceOption(StringRef Arg) { } //===----------------------------------------------------------------------===// -// Initialization. +// Storage details. //===----------------------------------------------------------------------===// -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); +namespace clang::CompilerInvocationDetail { +namespace { +template <class T> std::shared_ptr<T> make_shared(const T &X) { + return std::make_shared<T>(X); +} + +template <class T> llvm::IntrusiveRefCntPtr<T> makeIntrusiveRefCnt(const T &X) { + return llvm::makeIntrusiveRefCnt<T>(X); +} +} // namespace + +RefBase::RefBase() + : LangOpts(std::make_shared<LangOptions>()), + TargetOpts(std::make_shared<TargetOptions>()), + DiagnosticOpts(llvm::makeIntrusiveRefCnt<DiagnosticOptions>()), + HeaderSearchOpts(std::make_shared<HeaderSearchOptions>()), + PreprocessorOpts(std::make_shared<PreprocessorOptions>()), + AnalyzerOpts(llvm::makeIntrusiveRefCnt<AnalyzerOptions>()) {} + +RefBase::RefBase(const RefBase &X, DeepCopy) + : LangOpts(make_shared(*X.LangOpts)), + TargetOpts(make_shared(*X.TargetOpts)), + DiagnosticOpts(makeIntrusiveRefCnt(*X.DiagnosticOpts)), + HeaderSearchOpts(make_shared(*X.HeaderSearchOpts)), + PreprocessorOpts(make_shared(*X.PreprocessorOpts)), + AnalyzerOpts(makeIntrusiveRefCnt(*X.AnalyzerOpts)) {} + +RefBase::RefBase(const RefBase &X, ShallowCopy) + : LangOpts(X.LangOpts), // + TargetOpts(X.TargetOpts), // + DiagnosticOpts(X.DiagnosticOpts), // + HeaderSearchOpts(X.HeaderSearchOpts), // + PreprocessorOpts(X.PreprocessorOpts), // + AnalyzerOpts(X.AnalyzerOpts) {} + +RefBase &RefBase::assign(const RefBase &X, DeepCopy) { + LangOpts = make_shared(*X.LangOpts); + TargetOpts = make_shared(*X.TargetOpts); + DiagnosticOpts = makeIntrusiveRefCnt(*X.DiagnosticOpts); + HeaderSearchOpts = make_shared(*X.HeaderSearchOpts); + PreprocessorOpts = make_shared(*X.PreprocessorOpts); + AnalyzerOpts = makeIntrusiveRefCnt(*X.AnalyzerOpts); return *this; } -CompilerInvocationRefBase & -CompilerInvocationRefBase::operator=(CompilerInvocationRefBase &&X) = default; +RefBase &RefBase::assign(const RefBase &X, ShallowCopy) { + LangOpts = X.LangOpts; + TargetOpts = X.TargetOpts; + DiagnosticOpts = X.DiagnosticOpts; + HeaderSearchOpts = X.HeaderSearchOpts; + PreprocessorOpts = X.PreprocessorOpts; + AnalyzerOpts = X.AnalyzerOpts; + return *this; +} + +RefBase::RefBase(RefBase &&) = default; +RefBase &RefBase::operator=(RefBase &&) = default; + +RefBase::~RefBase() = default; +} // namespace clang::CompilerInvocationDetail + +namespace { +template <typename T> +T &ensure_owned(std::shared_ptr<T> &Storage) { + if (!Storage.unique()) + Storage = std::make_shared<T>(*Storage); + return *Storage; +} + +template <typename T> +T &ensure_owned(llvm::IntrusiveRefCntPtr<T> &Storage) { + if (!Storage.unique()) + Storage = llvm::makeIntrusiveRefCnt<T>(*Storage); + return *Storage; +} +} // namespace -CompilerInvocationRefBase::~CompilerInvocationRefBase() = default; +// clang-format off +LangOptions &CowCompilerInvocation::getMutLangOpts() { return ensure_owned(LangOpts); } +TargetOptions &CowCompilerInvocation::getMutTargetOpts() { return ensure_owned(TargetOpts); } +DiagnosticOptions &CowCompilerInvocation::getMutDiagnosticOpts() { return ensure_owned(DiagnosticOpts); } +HeaderSearchOptions &CowCompilerInvocation::getMutHeaderSearchOpts() { return ensure_owned(HeaderSearchOpts); } +PreprocessorOptions &CowCompilerInvocation::getMutPreprocessorOpts() { return ensure_owned(PreprocessorOpts); } +AnalyzerOptions &CowCompilerInvocation::getMutAnalyzerOpts() { return ensure_owned(AnalyzerOpts); } +// clang-format on //===----------------------------------------------------------------------===// // Normalizers //===----------------------------------------------------------------------===// -using ArgumentConsumer = CompilerInvocation::ArgumentConsumer; +using ArgumentConsumer = CompilerInvocationBase::ArgumentConsumer; +using StringAllocator = CompilerInvocationBase::StringAllocator; #define SIMPLE_ENUM_VALUE_TABLE #include "clang/Driver/Options.inc" @@ -633,8 +685,7 @@ using ParseFn = // Generate command line arguments from CompilerInvocation. using GenerateFn = llvm::function_ref<void( - CompilerInvocation &, SmallVectorImpl<const char *> &, - CompilerInvocation::StringAllocator)>; + CompilerInvocation &, SmallVectorImpl<const char *> &, StringAllocator)>; /// May perform round-trip of command line arguments. By default, the round-trip /// is enabled in assert builds. This can be overwritten at run-time via the @@ -838,7 +889,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; @@ -1346,11 +1397,11 @@ static void setPGOUseInstrumentor(CodeGenOptions &Opts, Opts.setProfileUse(CodeGenOptions::ProfileClangInstr); } -void CompilerInvocation::GenerateCodeGenArgs(const CodeGenOptions &Opts, - ArgumentConsumer Consumer, - const llvm::Triple &T, - const std::string &OutputFile, - const LangOptions *LangOpts) { +void CompilerInvocationBase::GenerateCodeGenArgs(const CodeGenOptions &Opts, + ArgumentConsumer Consumer, + const llvm::Triple &T, + const std::string &OutputFile, + const LangOptions *LangOpts) { const CodeGenOptions &CodeGenOpts = Opts; if (Opts.OptimizationLevel == 0) @@ -2254,9 +2305,9 @@ static bool ParseMigratorArgs(MigratorOptions &Opts, const ArgList &Args, return Diags.getNumErrors() == NumErrorsBefore; } -void CompilerInvocation::GenerateDiagnosticArgs(const DiagnosticOptions &Opts, - ArgumentConsumer Consumer, - bool DefaultDiagColor) { +void CompilerInvocationBase::GenerateDiagnosticArgs( + const DiagnosticOptions &Opts, ArgumentConsumer Consumer, + bool DefaultDiagColor) { const DiagnosticOptions *DiagnosticOpts = &Opts; #define DIAG_OPTION_WITH_MARSHALLING(...) \ GENERATE_OPTION_WITH_MARSHALLING(Consumer, __VA_ARGS__) @@ -2917,7 +2968,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(...) \ @@ -3247,9 +3298,10 @@ static StringRef GetInputKindName(InputKind IK) { llvm_unreachable("unknown input language"); } -void CompilerInvocation::GenerateLangArgs(const LangOptions &Opts, - ArgumentConsumer Consumer, - const llvm::Triple &T, InputKind IK) { +void CompilerInvocationBase::GenerateLangArgs(const LangOptions &Opts, + ArgumentConsumer Consumer, + const llvm::Triple &T, + InputKind IK) { if (IK.getFormat() == InputKind::Precompiled || IK.getLanguage() == Language::LLVM_IR) { if (Opts.ObjCAutoRefCount) @@ -4103,12 +4155,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__) @@ -4507,22 +4559,23 @@ std::string CompilerInvocation::getModuleHash() const { HBuilder.add(serialization::VERSION_MAJOR, serialization::VERSION_MINOR); // Extend the signature with the language options -#define LANGOPT(Name, Bits, Default, Description) HBuilder.add(LangOpts->Name); +#define LANGOPT(Name, Bits, Default, Description) \ + HBuilder.add(getLangOpts().Name); #define ENUM_LANGOPT(Name, Type, Bits, Default, Description) \ - HBuilder.add(static_cast<unsigned>(LangOpts->get##Name())); + HBuilder.add(static_cast<unsigned>(getLangOpts().get##Name())); #define BENIGN_LANGOPT(Name, Bits, Default, Description) #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(); @@ -4577,7 +4630,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); @@ -4588,28 +4641,29 @@ std::string CompilerInvocation::getModuleHash() const { return toString(llvm::APInt(64, Hash), 36, /*Signed=*/false); } -void CompilerInvocation::generateCC1CommandLine( +void CompilerInvocationBase::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, 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 { +std::vector<std::string> CompilerInvocationBase::getCC1CommandLine() const { std::vector<std::string> Args{"-cc1"}; generateCC1CommandLine( [&Args](const Twine &Arg) { Args.push_back(Arg.str()); }); >From d574bbe3b3125b1c2d7bf22fab7b06b74e3d652f Mon Sep 17 00:00:00 2001 From: Jan Svoboda <jan_svob...@apple.com> Date: Thu, 24 Aug 2023 17:33:38 -0700 Subject: [PATCH 3/5] [clang][deps] Store common, partially-formed invocation --- .../DependencyScanning/ModuleDepCollector.h | 17 +-- .../DependencyScanning/ModuleDepCollector.cpp | 102 +++++++++++------- 2 files changed, 72 insertions(+), 47 deletions(-) diff --git a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h index 914d55eadefe85..1f76314101c20a 100644 --- a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h +++ b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h @@ -241,8 +241,10 @@ class ModuleDepCollector final : public DependencyCollector { llvm::SetVector<const Module *> DirectModularDeps; /// Options that control the dependency output generation. std::unique_ptr<DependencyOutputOptions> Opts; - /// The original Clang invocation passed to dependency scanner. - CompilerInvocation OriginalInvocation; + /// Original Clang invocation passed to the dependency scanner that has been + /// partially massaged into one that can perform explicit build of a module. + /// This still needs to be finalized for each discovered modular dependency. + CowCompilerInvocation CommonInvocation; /// Whether to optimize the modules' command-line arguments. bool OptimizeArgs; /// Whether to set up command-lines to load PCM files eagerly. @@ -265,9 +267,9 @@ class ModuleDepCollector final : public DependencyCollector { /// Constructs a CompilerInvocation that can be used to build the given /// module, excluding paths to discovered modular dependencies that are yet to /// be built. - CompilerInvocation makeInvocationForModuleBuildWithoutOutputs( + CowCompilerInvocation makeInvocationForModuleBuildWithoutOutputs( const ModuleDeps &Deps, - llvm::function_ref<void(CompilerInvocation &)> Optimize) const; + llvm::function_ref<void(CowCompilerInvocation &)> Optimize) const; /// Collect module map files for given modules. llvm::DenseSet<const FileEntry *> @@ -279,13 +281,16 @@ class ModuleDepCollector final : public DependencyCollector { /// Add module files (pcm) to the invocation, if needed. void addModuleFiles(CompilerInvocation &CI, ArrayRef<ModuleID> ClangModuleDeps) const; + void addModuleFiles(CowCompilerInvocation &CI, + ArrayRef<ModuleID> ClangModuleDeps) const; /// Add paths that require looking up outputs to the given dependencies. - void addOutputPaths(CompilerInvocation &CI, ModuleDeps &Deps); + void addOutputPaths(CowCompilerInvocation &CI, ModuleDeps &Deps); /// Compute the context hash for \p Deps, and create the mapping /// \c ModuleDepsByID[Deps.ID] = &Deps. - void associateWithContextHash(const CompilerInvocation &CI, ModuleDeps &Deps); + void associateWithContextHash(const CompilerInvocationBase &CI, + ModuleDeps &Deps); }; } // end namespace dependencies diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp index e13f7c74e9b92e..e363a0ba502778 100644 --- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp +++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp @@ -54,18 +54,18 @@ static std::vector<std::string> splitString(std::string S, char Separator) { return Result; } -void ModuleDepCollector::addOutputPaths(CompilerInvocation &CI, +void ModuleDepCollector::addOutputPaths(CowCompilerInvocation &CI, ModuleDeps &Deps) { - CI.getFrontendOpts().OutputFile = + CI.getMutFrontendOpts().OutputFile = Controller.lookupModuleOutput(Deps.ID, ModuleOutputKind::ModuleFile); if (!CI.getDiagnosticOpts().DiagnosticSerializationFile.empty()) - CI.getDiagnosticOpts().DiagnosticSerializationFile = + CI.getMutDiagnosticOpts().DiagnosticSerializationFile = Controller.lookupModuleOutput( Deps.ID, ModuleOutputKind::DiagnosticSerializationFile); if (!CI.getDependencyOutputOpts().OutputFile.empty()) { - CI.getDependencyOutputOpts().OutputFile = Controller.lookupModuleOutput( + CI.getMutDependencyOutputOpts().OutputFile = Controller.lookupModuleOutput( Deps.ID, ModuleOutputKind::DependencyFile); - CI.getDependencyOutputOpts().Targets = + CI.getMutDependencyOutputOpts().Targets = splitString(Controller.lookupModuleOutput( Deps.ID, ModuleOutputKind::DependencyTargets), '\0'); @@ -74,18 +74,13 @@ void ModuleDepCollector::addOutputPaths(CompilerInvocation &CI, // Fallback to -o as dependency target, as in the driver. SmallString<128> Target; quoteMakeTarget(CI.getFrontendOpts().OutputFile, Target); - CI.getDependencyOutputOpts().Targets.push_back(std::string(Target)); + CI.getMutDependencyOutputOpts().Targets.push_back(std::string(Target)); } } } -CompilerInvocation -ModuleDepCollector::makeInvocationForModuleBuildWithoutOutputs( - const ModuleDeps &Deps, - llvm::function_ref<void(CompilerInvocation &)> Optimize) const { - // Make a deep copy of the original Clang invocation. - CompilerInvocation CI(OriginalInvocation); - +static CowCompilerInvocation +makeCommonInvocationForModuleBuild(CompilerInvocation CI) { CI.resetNonModularOptions(); CI.clearImplicitModuleBuildOptions(); @@ -117,14 +112,37 @@ ModuleDepCollector::makeInvocationForModuleBuildWithoutOutputs( CI.getFrontendOpts().ARCMTAction = FrontendOptions::ARCMT_None; CI.getFrontendOpts().ObjCMTAction = FrontendOptions::ObjCMT_None; CI.getFrontendOpts().MTMigrateDir.clear(); - CI.getLangOpts().ModuleName = Deps.ID.ModuleName; - CI.getFrontendOpts().IsSystemModule = Deps.IsSystem; + + // Remove any macro definitions that are explicitly ignored. + if (!CI.getHeaderSearchOpts().ModulesIgnoreMacros.empty()) { + llvm::erase_if( + CI.getPreprocessorOpts().Macros, + [&CI](const std::pair<std::string, bool> &Def) { + StringRef MacroDef = Def.first; + return CI.getHeaderSearchOpts().ModulesIgnoreMacros.contains( + llvm::CachedHashString(MacroDef.split('=').first)); + }); + // Remove the now unused option. + CI.getHeaderSearchOpts().ModulesIgnoreMacros.clear(); + } + + return CI; +} + +CowCompilerInvocation +ModuleDepCollector::makeInvocationForModuleBuildWithoutOutputs( + const ModuleDeps &Deps, + llvm::function_ref<void(CowCompilerInvocation &)> Optimize) const { + CowCompilerInvocation CI = CommonInvocation; + + CI.getMutLangOpts().ModuleName = Deps.ID.ModuleName; + CI.getMutFrontendOpts().IsSystemModule = Deps.IsSystem; // Inputs InputKind ModuleMapInputKind(CI.getFrontendOpts().DashX.getLanguage(), InputKind::Format::ModuleMap); - CI.getFrontendOpts().Inputs.emplace_back(Deps.ClangModuleMapFile, - ModuleMapInputKind); + CI.getMutFrontendOpts().Inputs.emplace_back(Deps.ClangModuleMapFile, + ModuleMapInputKind); auto CurrentModuleMapEntry = ScanInstance.getFileManager().getFile(Deps.ClangModuleMapFile); @@ -150,35 +168,22 @@ ModuleDepCollector::makeInvocationForModuleBuildWithoutOutputs( !DepModuleMapFiles.contains(*ModuleMapEntry)) continue; - CI.getFrontendOpts().ModuleMapFiles.emplace_back(ModuleMapFile); + CI.getMutFrontendOpts().ModuleMapFiles.emplace_back(ModuleMapFile); } // Report the prebuilt modules this module uses. for (const auto &PrebuiltModule : Deps.PrebuiltModuleDeps) - CI.getFrontendOpts().ModuleFiles.push_back(PrebuiltModule.PCMFile); + CI.getMutFrontendOpts().ModuleFiles.push_back(PrebuiltModule.PCMFile); // Add module file inputs from dependencies. addModuleFiles(CI, Deps.ClangModuleDeps); - // Remove any macro definitions that are explicitly ignored. - if (!CI.getHeaderSearchOpts().ModulesIgnoreMacros.empty()) { - llvm::erase_if( - CI.getPreprocessorOpts().Macros, - [&CI](const std::pair<std::string, bool> &Def) { - StringRef MacroDef = Def.first; - return CI.getHeaderSearchOpts().ModulesIgnoreMacros.contains( - llvm::CachedHashString(MacroDef.split('=').first)); - }); - // Remove the now unused option. - CI.getHeaderSearchOpts().ModulesIgnoreMacros.clear(); - } - // Apply -Wsystem-headers-in-module for the current module. if (llvm::is_contained(CI.getDiagnosticOpts().SystemHeaderWarningsModules, Deps.ID.ModuleName)) - CI.getDiagnosticOpts().Warnings.push_back("system-headers"); + CI.getMutDiagnosticOpts().Warnings.push_back("system-headers"); // Remove the now unused option(s). - CI.getDiagnosticOpts().SystemHeaderWarningsModules.clear(); + CI.getMutDiagnosticOpts().SystemHeaderWarningsModules.clear(); Optimize(CI); @@ -224,6 +229,19 @@ void ModuleDepCollector::addModuleFiles( } } +void ModuleDepCollector::addModuleFiles( + CowCompilerInvocation &CI, ArrayRef<ModuleID> ClangModuleDeps) const { + for (const ModuleID &MID : ClangModuleDeps) { + std::string PCMPath = + Controller.lookupModuleOutput(MID, ModuleOutputKind::ModuleFile); + if (EagerLoadModules) + CI.getMutFrontendOpts().ModuleFiles.push_back(std::move(PCMPath)); + else + CI.getMutHeaderSearchOpts().PrebuiltModuleFiles.insert( + {MID.ModuleName, std::move(PCMPath)}); + } +} + static bool needsModules(FrontendInputFile FIF) { switch (FIF.getKind().getLanguage()) { case Language::Unknown: @@ -264,7 +282,7 @@ void ModuleDepCollector::applyDiscoveredDependencies(CompilerInvocation &CI) { } static std::string getModuleContextHash(const ModuleDeps &MD, - const CompilerInvocation &CI, + const CompilerInvocationBase &CI, bool EagerLoadModules) { llvm::HashBuilder<llvm::TruncatedBLAKE3<16>, llvm::support::endianness::native> @@ -304,8 +322,8 @@ static std::string getModuleContextHash(const ModuleDeps &MD, return toString(llvm::APInt(sizeof(Words) * 8, Words), 36, /*Signed=*/false); } -void ModuleDepCollector::associateWithContextHash(const CompilerInvocation &CI, - ModuleDeps &Deps) { +void ModuleDepCollector::associateWithContextHash( + const CompilerInvocationBase &CI, ModuleDeps &Deps) { Deps.ID.ContextHash = getModuleContextHash(Deps, CI, EagerLoadModules); bool Inserted = ModuleDepsByID.insert({Deps.ID, &Deps}).second; (void)Inserted; @@ -498,10 +516,10 @@ ModuleDepCollectorPP::handleTopLevelModule(const Module *M) { MD.ModuleMapFileDeps.emplace_back(IFI.FilenameAsRequested); }); - CompilerInvocation CI = MDC.makeInvocationForModuleBuildWithoutOutputs( - MD, [&](CompilerInvocation &BuildInvocation) { + CowCompilerInvocation CI = MDC.makeInvocationForModuleBuildWithoutOutputs( + MD, [&](CowCompilerInvocation &BuildInvocation) { if (MDC.OptimizeArgs) - optimizeHeaderSearchOpts(BuildInvocation.getHeaderSearchOpts(), + optimizeHeaderSearchOpts(BuildInvocation.getMutHeaderSearchOpts(), *MDC.ScanInstance.getASTReader(), *MF); }); @@ -601,7 +619,9 @@ ModuleDepCollector::ModuleDepCollector( DependencyActionController &Controller, CompilerInvocation OriginalCI, bool OptimizeArgs, bool EagerLoadModules, bool IsStdModuleP1689Format) : ScanInstance(ScanInstance), Consumer(C), Controller(Controller), - Opts(std::move(Opts)), OriginalInvocation(std::move(OriginalCI)), + Opts(std::move(Opts)), + CommonInvocation( + makeCommonInvocationForModuleBuild(std::move(OriginalCI))), OptimizeArgs(OptimizeArgs), EagerLoadModules(EagerLoadModules), IsStdModuleP1689Format(IsStdModuleP1689Format) {} >From 370f9710c69d9edc78ce5044e2fc08354fd77500 Mon Sep 17 00:00:00 2001 From: Jan Svoboda <jan_svob...@apple.com> Date: Fri, 1 Sep 2023 13:01:24 -0700 Subject: [PATCH 4/5] [clang][deps] Generate command-lines lazily --- .../DependencyScanningTool.h | 2 +- .../DependencyScanning/ModuleDepCollector.h | 23 ++++++++++---- .../DependencyScanning/ModuleDepCollector.cpp | 29 ++++++++--------- clang/tools/clang-scan-deps/ClangScanDeps.cpp | 31 +++++++++++++------ 4 files changed, 54 insertions(+), 31 deletions(-) diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h index cb9476d1550df3..b2cfa621e183c8 100644 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h @@ -168,7 +168,7 @@ class FullDependencyConsumer : public DependencyConsumer { } void handleModuleDependency(ModuleDeps MD) override { - ClangModuleDeps[MD.ID] = std::move(MD); + ClangModuleDeps.insert({MD.ID, std::move(MD)}); } void handleDirectModuleDependency(ModuleID ID) override { diff --git a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h index 1f76314101c20a..5ed76de665d2c9 100644 --- a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h +++ b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h @@ -20,9 +20,11 @@ #include "llvm/ADT/Hashing.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/raw_ostream.h" +#include <memory> #include <optional> #include <string> #include <unordered_map> +#include <variant> namespace clang { namespace tooling { @@ -104,7 +106,16 @@ enum class ModuleOutputKind { DiagnosticSerializationFile, }; -struct ModuleDeps { +class ModuleDeps { + std::variant<std::shared_ptr<CowCompilerInvocation>, std::vector<std::string>> + BuildInvocationOrArguments; + + explicit ModuleDeps(std::shared_ptr<CowCompilerInvocation> CI) + : BuildInvocationOrArguments(std::move(CI)) {} + + friend class ModuleDepCollectorPP; + +public: /// The identifier of the module. ModuleID ID; @@ -136,9 +147,9 @@ struct ModuleDeps { /// determined that the differences are benign for this compilation. std::vector<ModuleID> ClangModuleDeps; - /// Compiler invocation that can be used to build this module. Does not - /// include argv[0]. - std::vector<std::string> BuildArguments; + /// Get (or compute) the compiler invocation that can be used to build this + /// module. Does not include argv[0]. + const std::vector<std::string> &getBuildArguments(); }; class ModuleDepCollector; @@ -267,8 +278,8 @@ class ModuleDepCollector final : public DependencyCollector { /// Constructs a CompilerInvocation that can be used to build the given /// module, excluding paths to discovered modular dependencies that are yet to /// be built. - CowCompilerInvocation makeInvocationForModuleBuildWithoutOutputs( - const ModuleDeps &Deps, + void makeInvocationForModuleBuildWithoutOutputs( + CowCompilerInvocation &CI, const ModuleDeps &Deps, llvm::function_ref<void(CowCompilerInvocation &)> Optimize) const; /// Collect module map files for given modules. diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp index e363a0ba502778..2908eaebce06ac 100644 --- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp +++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp @@ -21,6 +21,13 @@ using namespace clang; using namespace tooling; using namespace dependencies; +const std::vector<std::string> &ModuleDeps::getBuildArguments() { + if (auto *CI = std::get_if<std::shared_ptr<CowCompilerInvocation>>( + &BuildInvocationOrArguments)) + BuildInvocationOrArguments = (*CI)->getCC1CommandLine(); + return std::get<std::vector<std::string>>(BuildInvocationOrArguments); +} + static void optimizeHeaderSearchOpts(HeaderSearchOptions &Opts, ASTReader &Reader, const serialization::ModuleFile &MF) { @@ -129,12 +136,9 @@ makeCommonInvocationForModuleBuild(CompilerInvocation CI) { return CI; } -CowCompilerInvocation -ModuleDepCollector::makeInvocationForModuleBuildWithoutOutputs( - const ModuleDeps &Deps, +void ModuleDepCollector::makeInvocationForModuleBuildWithoutOutputs( + CowCompilerInvocation &CI, const ModuleDeps &Deps, llvm::function_ref<void(CowCompilerInvocation &)> Optimize) const { - CowCompilerInvocation CI = CommonInvocation; - CI.getMutLangOpts().ModuleName = Deps.ID.ModuleName; CI.getMutFrontendOpts().IsSystemModule = Deps.IsSystem; @@ -186,8 +190,6 @@ ModuleDepCollector::makeInvocationForModuleBuildWithoutOutputs( CI.getMutDiagnosticOpts().SystemHeaderWarningsModules.clear(); Optimize(CI); - - return CI; } llvm::DenseSet<const FileEntry *> ModuleDepCollector::collectModuleMapFiles( @@ -465,7 +467,8 @@ ModuleDepCollectorPP::handleTopLevelModule(const Module *M) { if (!ModI.second) return ModI.first->second->ID; - ModI.first->second = std::make_unique<ModuleDeps>(); + auto CI = std::make_shared<CowCompilerInvocation>(MDC.CommonInvocation); + ModI.first->second.reset(new ModuleDeps(CI)); ModuleDeps &MD = *ModI.first->second; MD.ID.ModuleName = M->getFullModuleName(); @@ -516,19 +519,17 @@ ModuleDepCollectorPP::handleTopLevelModule(const Module *M) { MD.ModuleMapFileDeps.emplace_back(IFI.FilenameAsRequested); }); - CowCompilerInvocation CI = MDC.makeInvocationForModuleBuildWithoutOutputs( - MD, [&](CowCompilerInvocation &BuildInvocation) { + MDC.makeInvocationForModuleBuildWithoutOutputs( + *CI, MD, [&](CowCompilerInvocation &BuildInvocation) { if (MDC.OptimizeArgs) optimizeHeaderSearchOpts(BuildInvocation.getMutHeaderSearchOpts(), *MDC.ScanInstance.getASTReader(), *MF); }); - MDC.associateWithContextHash(CI, MD); + MDC.associateWithContextHash(*CI, MD); // Finish the compiler invocation. Requires dependencies and the context hash. - MDC.addOutputPaths(CI, MD); - - MD.BuildArguments = CI.getCC1CommandLine(); + MDC.addOutputPaths(*CI, MD); return MD.ID; } diff --git a/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/clang/tools/clang-scan-deps/ClangScanDeps.cpp index dab3de42b7fa1a..7491d6c284fe12 100644 --- a/clang/tools/clang-scan-deps/ClangScanDeps.cpp +++ b/clang/tools/clang-scan-deps/ClangScanDeps.cpp @@ -351,14 +351,23 @@ class FullDeps { } void mergeDeps(ModuleDepsGraph Graph, size_t InputIndex) { - std::unique_lock<std::mutex> ul(Lock); - for (const ModuleDeps &MD : Graph) { - auto I = Modules.find({MD.ID, 0}); - if (I != Modules.end()) { - I->first.InputIndex = std::min(I->first.InputIndex, InputIndex); - continue; + std::vector<ModuleDeps *> NewMDs; + { + std::unique_lock<std::mutex> ul(Lock); + for (const ModuleDeps &MD : Graph) { + auto I = Modules.find({MD.ID, 0}); + if (I != Modules.end()) { + I->first.InputIndex = std::min(I->first.InputIndex, InputIndex); + continue; + } + auto Res = Modules.insert(I, {{MD.ID, InputIndex}, std::move(MD)}); + NewMDs.push_back(&Res->second); } - Modules.insert(I, {{MD.ID, InputIndex}, std::move(MD)}); + // First call to \c getBuildArguments is somewhat expensive. Let's call it + // on the current thread (instead of the main one), and outside the + // critical section. + for (ModuleDeps *MD : NewMDs) + MD->getBuildArguments(); } } @@ -382,7 +391,7 @@ class FullDeps { /*ShouldOwnClient=*/false); for (auto &&M : Modules) - if (roundTripCommand(M.second.BuildArguments, *Diags)) + if (roundTripCommand(M.second.getBuildArguments(), *Diags)) return true; for (auto &&I : Inputs) @@ -404,14 +413,16 @@ class FullDeps { Array OutModules; for (auto &&ModID : ModuleIDs) { - auto &MD = Modules[ModID]; + auto It = Modules.find(ModID); + assert(It != Modules.end()); + auto &MD = It->second; Object O{ {"name", MD.ID.ModuleName}, {"context-hash", MD.ID.ContextHash}, {"file-deps", toJSONSorted(MD.FileDeps)}, {"clang-module-deps", toJSONSorted(MD.ClangModuleDeps)}, {"clang-modulemap-file", MD.ClangModuleMapFile}, - {"command-line", MD.BuildArguments}, + {"command-line", MD.getBuildArguments()}, }; OutModules.push_back(std::move(O)); } >From 7c0af252876a2c8de0bcb2438cf7834256049183 Mon Sep 17 00:00:00 2001 From: Jan Svoboda <jan_svob...@apple.com> Date: Fri, 1 Sep 2023 14:45:48 -0700 Subject: [PATCH 5/5] [clang][cli][deps] Make CodeGenOpts copy-on-write --- .../include/clang/Frontend/CompilerInvocation.h | 16 ++++++++-------- clang/lib/Frontend/CompilerInvocation.cpp | 6 ++++++ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/clang/include/clang/Frontend/CompilerInvocation.h b/clang/include/clang/Frontend/CompilerInvocation.h index e57844333eaceb..03df3879e1d86f 100644 --- a/clang/include/clang/Frontend/CompilerInvocation.h +++ b/clang/include/clang/Frontend/CompilerInvocation.h @@ -92,6 +92,9 @@ class RefBase { /// Options controlling the preprocessor (aside from \#include handling). std::shared_ptr<PreprocessorOptions> PreprocessorOpts; + /// Options controlling IRgen and the backend. + std::shared_ptr<CodeGenOptions> CodeGenOpts; + /// Options controlling the static analyzer. AnalyzerOptionsRef AnalyzerOpts; @@ -120,6 +123,7 @@ class RefBase { const DiagnosticOptions &getDiagnosticOpts() const { return *DiagnosticOpts; } const HeaderSearchOptions &getHeaderSearchOpts() const { return *HeaderSearchOpts; } const PreprocessorOptions &getPreprocessorOpts() const { return *PreprocessorOpts; } + const CodeGenOptions &getCodeGenOpts() const { return *CodeGenOpts; } const AnalyzerOptions &getAnalyzerOpts() const { return *AnalyzerOpts; } // clang-format on }; @@ -129,9 +133,6 @@ class ValBase { protected: MigratorOptions MigratorOpts; - /// Options controlling IRgen and the backend. - CodeGenOptions CodeGenOpts; - /// Options controlling dependency output. DependencyOutputOptions DependencyOutputOpts; @@ -147,7 +148,6 @@ class ValBase { public: // clang-format off const MigratorOptions &getMigratorOpts() const { return MigratorOpts; } - const CodeGenOptions &getCodeGenOpts() const { return CodeGenOpts; } const DependencyOutputOptions &getDependencyOutputOpts() const { return DependencyOutputOpts; } const FileSystemOptions &getFileSystemOpts() const { return FileSystemOpts; } const FrontendOptions &getFrontendOpts() const { return FrontendOpts; } @@ -252,6 +252,7 @@ class CompilerInvocation : public CompilerInvocationBase { DiagnosticOptions &getDiagnosticOpts() { return *DiagnosticOpts; } HeaderSearchOptions &getHeaderSearchOpts() { return *HeaderSearchOpts; } PreprocessorOptions &getPreprocessorOpts() { return *PreprocessorOpts; } + CodeGenOptions &getCodeGenOpts() { return *CodeGenOpts; } AnalyzerOptions &getAnalyzerOpts() { return *AnalyzerOpts; } // Const RefBase accessors. @@ -261,13 +262,13 @@ class CompilerInvocation : public CompilerInvocationBase { using RefBase::getDiagnosticOpts; using RefBase::getHeaderSearchOpts; using RefBase::getPreprocessorOpts; + using RefBase::getCodeGenOpts; using RefBase::getAnalyzerOpts; // Mutable ValBase accessors. // clang-format off MigratorOptions &getMigratorOpts() { return MigratorOpts; } - CodeGenOptions &getCodeGenOpts() { return CodeGenOpts; } DependencyOutputOptions &getDependencyOutputOpts() { return DependencyOutputOpts; } FileSystemOptions &getFileSystemOpts() { return FileSystemOpts; } FrontendOptions &getFrontendOpts() { return FrontendOpts; } @@ -277,7 +278,6 @@ class CompilerInvocation : public CompilerInvocationBase { // Const ValBase accessors. using ValBase::getMigratorOpts; - using ValBase::getCodeGenOpts; using ValBase::getDependencyOutputOpts; using ValBase::getFileSystemOpts; using ValBase::getFrontendOpts; @@ -385,6 +385,7 @@ struct CowCompilerInvocation : CompilerInvocationBase { DiagnosticOptions &getMutDiagnosticOpts(); HeaderSearchOptions &getMutHeaderSearchOpts(); PreprocessorOptions &getMutPreprocessorOpts(); + CodeGenOptions &getMutCodeGenOpts(); AnalyzerOptions &getMutAnalyzerOpts(); // Const RefBase accessors. @@ -394,13 +395,13 @@ struct CowCompilerInvocation : CompilerInvocationBase { using RefBase::getDiagnosticOpts; using RefBase::getHeaderSearchOpts; using RefBase::getPreprocessorOpts; + using RefBase::getCodeGenOpts; using RefBase::getAnalyzerOpts; // Mutable ValBase accessors. // clang-format off MigratorOptions &getMutMigratorOpts() { return MigratorOpts; } - CodeGenOptions &getMutCodeGenOpts() { return CodeGenOpts; } DependencyOutputOptions &getMutDependencyOutputOpts() { return DependencyOutputOpts; } FileSystemOptions &getMutFileSystemOpts() { return FileSystemOpts; } FrontendOptions &getMutFrontendOpts() { return FrontendOpts; } @@ -410,7 +411,6 @@ struct CowCompilerInvocation : CompilerInvocationBase { // Const ValBase accessors. using ValBase::getMigratorOpts; - using ValBase::getCodeGenOpts; using ValBase::getDependencyOutputOpts; using ValBase::getFileSystemOpts; using ValBase::getFrontendOpts; diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 41972c7b05f896..ba586fcac6bd29 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -143,6 +143,7 @@ RefBase::RefBase() DiagnosticOpts(llvm::makeIntrusiveRefCnt<DiagnosticOptions>()), HeaderSearchOpts(std::make_shared<HeaderSearchOptions>()), PreprocessorOpts(std::make_shared<PreprocessorOptions>()), + CodeGenOpts(std::make_shared<CodeGenOptions>()), AnalyzerOpts(llvm::makeIntrusiveRefCnt<AnalyzerOptions>()) {} RefBase::RefBase(const RefBase &X, DeepCopy) @@ -151,6 +152,7 @@ RefBase::RefBase(const RefBase &X, DeepCopy) DiagnosticOpts(makeIntrusiveRefCnt(*X.DiagnosticOpts)), HeaderSearchOpts(make_shared(*X.HeaderSearchOpts)), PreprocessorOpts(make_shared(*X.PreprocessorOpts)), + CodeGenOpts(make_shared(*X.CodeGenOpts)), AnalyzerOpts(makeIntrusiveRefCnt(*X.AnalyzerOpts)) {} RefBase::RefBase(const RefBase &X, ShallowCopy) @@ -159,6 +161,7 @@ RefBase::RefBase(const RefBase &X, ShallowCopy) DiagnosticOpts(X.DiagnosticOpts), // HeaderSearchOpts(X.HeaderSearchOpts), // PreprocessorOpts(X.PreprocessorOpts), // + CodeGenOpts(X.CodeGenOpts), // AnalyzerOpts(X.AnalyzerOpts) {} RefBase &RefBase::assign(const RefBase &X, DeepCopy) { @@ -168,6 +171,7 @@ RefBase &RefBase::assign(const RefBase &X, DeepCopy) { HeaderSearchOpts = make_shared(*X.HeaderSearchOpts); PreprocessorOpts = make_shared(*X.PreprocessorOpts); AnalyzerOpts = makeIntrusiveRefCnt(*X.AnalyzerOpts); + CodeGenOpts = make_shared(*X.CodeGenOpts); return *this; } @@ -177,6 +181,7 @@ RefBase &RefBase::assign(const RefBase &X, ShallowCopy) { DiagnosticOpts = X.DiagnosticOpts; HeaderSearchOpts = X.HeaderSearchOpts; PreprocessorOpts = X.PreprocessorOpts; + CodeGenOpts = X.CodeGenOpts; AnalyzerOpts = X.AnalyzerOpts; return *this; } @@ -209,6 +214,7 @@ TargetOptions &CowCompilerInvocation::getMutTargetOpts() { return ensure_owned(T DiagnosticOptions &CowCompilerInvocation::getMutDiagnosticOpts() { return ensure_owned(DiagnosticOpts); } HeaderSearchOptions &CowCompilerInvocation::getMutHeaderSearchOpts() { return ensure_owned(HeaderSearchOpts); } PreprocessorOptions &CowCompilerInvocation::getMutPreprocessorOpts() { return ensure_owned(PreprocessorOpts); } +CodeGenOptions &CowCompilerInvocation::getMutCodeGenOpts() { return ensure_owned(CodeGenOpts); } AnalyzerOptions &CowCompilerInvocation::getMutAnalyzerOpts() { return ensure_owned(AnalyzerOpts); } // clang-format on _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits