[clang] [clang][deps] Optimize command line generation (PR #65412)

Jan Svoboda via cfe-commits cfe-commits at lists.llvm.org
Wed Sep 6 10:39:31 PDT 2023


https://github.com/jansvoboda11 updated https://github.com/llvm/llvm-project/pull/65412:

>From e41d54104cff710a9f6744bfcda9962386558b17 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Fri, 1 Sep 2023 15:07:23 -0700
Subject: [PATCH 1/2] [llvm][ADT] Implement `IntrusiveRefCntPtr::useCount()`

This function has the same semantics as `std::shared_ptr<T>::use_count()`, and enables implementing copy-on-write semantics.
---
 llvm/include/llvm/ADT/IntrusiveRefCntPtr.h    |  9 +++++++++
 llvm/unittests/ADT/IntrusiveRefCntPtrTest.cpp | 13 +++++++++++++
 2 files changed, 22 insertions(+)

diff --git a/llvm/include/llvm/ADT/IntrusiveRefCntPtr.h b/llvm/include/llvm/ADT/IntrusiveRefCntPtr.h
index e41eb0639ce30eb..90349e02014dd66 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:
+  unsigned UseCount() const { return RefCount; }
+
   void Retain() const { ++RefCount; }
 
   void Release() const {
@@ -124,6 +126,8 @@ template <class Derived> class ThreadSafeRefCountedBase {
 #endif
 
 public:
+  unsigned UseCount() const { return RefCount.load(std::memory_order_relaxed); }
+
   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 unsigned useCount(const T *obj) { return obj->UseCount(); }
   static void retain(T *obj) { obj->Retain(); }
   static void release(T *obj) { obj->Release(); }
 };
@@ -213,6 +218,10 @@ template <typename T> class IntrusiveRefCntPtr {
 
   void resetWithoutRelease() { Obj = nullptr; }
 
+  unsigned useCount() const {
+    return Obj ? IntrusiveRefCntPtrInfo<T>::useCount(Obj) : 0;
+  }
+
 private:
   void retain() {
     if (Obj)
diff --git a/llvm/unittests/ADT/IntrusiveRefCntPtrTest.cpp b/llvm/unittests/ADT/IntrusiveRefCntPtrTest.cpp
index 45b8028aef20089..af2c3760bc99af2 100644
--- a/llvm/unittests/ADT/IntrusiveRefCntPtrTest.cpp
+++ b/llvm/unittests/ADT/IntrusiveRefCntPtrTest.cpp
@@ -140,4 +140,17 @@ TEST(IntrusiveRefCntPtr, InteropsWithConvertible) {
   EXPECT_EQ(P4, X4.get());
 }
 
+TEST(IntrusiveRefCntPtrTest, Unique) {
+  IntrusiveRefCntPtr<X> X1;
+  EXPECT_EQ(X1.useCount(), 0);
+  X1 = new X();
+  EXPECT_EQ(X1.useCount(), 1);
+  {
+    IntrusiveRefCntPtr<X> X2 = X1;
+    EXPECT_EQ(X1.useCount(), 2);
+    EXPECT_EQ(X2.useCount(), 2);
+  }
+  EXPECT_EQ(X1.useCount(), 1);
+}
+
 } // end namespace llvm

>From 16be7a0df5f853d19fb2472e24c4b6ddd9f5a445 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Thu, 31 Aug 2023 11:39:32 -0700
Subject: [PATCH 2/2] [clang] 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       | 317 ++++++++++++------
 clang/lib/Frontend/CompilerInvocation.cpp     | 208 +++++++-----
 5 files changed, 346 insertions(+), 182 deletions(-)

diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index 14fc94fe27f9958..c6d7db32f2d2669 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 0f3120859ecef6c..9fb84415e22ead2 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 2adf4751444726b..aa649e675d9024d 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 5dc55bb7abdbab7..81f3107b4b4df4e 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,16 +66,18 @@ 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
 /// 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:
+/// This is a separate base class so that the client-facing sub-classes can
+/// simply declare the desired copy semantics without managing the internals
+/// themselves (for the most parts).
+class RefBase {
+protected:
   /// Options controlling the language variant.
   std::shared_ptr<LangOptions> LangOpts;
 
@@ -94,47 +96,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 +146,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 +234,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 +328,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 +352,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 11ffb3d6630d1fb..3fc354d6cc621d6 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.use_count() > 1)
+    Storage = std::make_shared<T>(*Storage);
+  return *Storage;
+}
+
+template <typename T>
+T &ensure_owned(llvm::IntrusiveRefCntPtr<T> &Storage) {
+  if (Storage.useCount() > 1)
+    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()); });



More information about the cfe-commits mailing list