Author: Jan Svoboda
Date: 2025-04-14T20:01:06-07:00
New Revision: 1cf9f764ac41fb3492e10c78640dd50e616388db

URL: 
https://github.com/llvm/llvm-project/commit/1cf9f764ac41fb3492e10c78640dd50e616388db
DIFF: 
https://github.com/llvm/llvm-project/commit/1cf9f764ac41fb3492e10c78640dd50e616388db.diff

LOG: [clang][frontend] Make `CompilerInstance::FailedModules` thread-safe 
(#135473)

This PR makes some progress towards making it possible to create clones
of `CompilerInstance` that are independent of each other and can be used
in a multi-threaded setting. This PR tackles
`CompilerInstance::FailedModules`, makes it a value-type instead of a
mutable shared pointer, and adds explicit copies & moves where
appropriate.

Besides that change, this PR also turns two previously free functions
with internal linkage into member functions of `CompilerInstance`, which
makes it possible to reduce the public API of that class that relates to
`FailedModules`. This reduces some API churn that was necessary for each
new member of `CompilerInstance` that needs to be cloned.

Added: 
    

Modified: 
    clang/include/clang/Frontend/CompilerInstance.h
    clang/lib/Frontend/CompilerInstance.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Frontend/CompilerInstance.h 
b/clang/include/clang/Frontend/CompilerInstance.h
index 66f56932c51a3..41dc7f1fef461 100644
--- a/clang/include/clang/Frontend/CompilerInstance.h
+++ b/clang/include/clang/Frontend/CompilerInstance.h
@@ -134,23 +134,13 @@ class CompilerInstance : public ModuleLoader {
 
   std::vector<std::shared_ptr<DependencyCollector>> DependencyCollectors;
 
-  /// Records the set of modules
-  class FailedModulesSet {
-    llvm::StringSet<> Failed;
-
-  public:
-    bool hasAlreadyFailed(StringRef module) { return Failed.count(module) > 0; 
}
-
-    void addFailed(StringRef module) { Failed.insert(module); }
-  };
-
   /// The set of modules that failed to build.
   ///
-  /// This pointer will be shared among all of the compiler instances created
+  /// This value will be passed among all of the compiler instances created
   /// to (re)build modules, so that once a module fails to build anywhere,
   /// other instances will see that the module has failed and won't try to
   /// build it again.
-  std::shared_ptr<FailedModulesSet> FailedModules;
+  llvm::StringSet<> FailedModules;
 
   /// The set of top-level modules that has already been built on the
   /// fly as part of this overall compilation action.
@@ -637,24 +627,6 @@ class CompilerInstance : public ModuleLoader {
     return *FrontendTimer;
   }
 
-  /// @}
-  /// @name Failed modules set
-  /// @{
-
-  bool hasFailedModulesSet() const { return (bool)FailedModules; }
-
-  void createFailedModulesSet() {
-    FailedModules = std::make_shared<FailedModulesSet>();
-  }
-
-  std::shared_ptr<FailedModulesSet> getFailedModulesSetPtr() const {
-    return FailedModules;
-  }
-
-  void setFailedModulesSet(std::shared_ptr<FailedModulesSet> FMS) {
-    FailedModules = FMS;
-  }
-
   /// }
   /// @name Output Files
   /// @{
@@ -870,6 +842,13 @@ class CompilerInstance : public ModuleLoader {
                                                  SourceLocation ModuleNameLoc,
                                                  bool IsInclusionDirective);
 
+  /// Creates a \c CompilerInstance for compiling a module.
+  ///
+  /// This expects a properly initialized \c FrontendInputFile.
+  std::unique_ptr<CompilerInstance> cloneForModuleCompileImpl(
+      SourceLocation ImportLoc, StringRef ModuleName, FrontendInputFile Input,
+      StringRef OriginalModuleMapFile, StringRef ModuleFileName);
+
 public:
   /// Creates a new \c CompilerInstance for compiling a module.
   ///
@@ -879,6 +858,14 @@ class CompilerInstance : public ModuleLoader {
   cloneForModuleCompile(SourceLocation ImportLoc, Module *Module,
                         StringRef ModuleFileName);
 
+  /// Compile a module file for the given module, using the options
+  /// provided by the importing compiler instance. Returns true if the module
+  /// was built without errors.
+  // FIXME: This should be private, but it's called from static non-member
+  // functions in the implementation file.
+  bool compileModule(SourceLocation ImportLoc, StringRef ModuleName,
+                     StringRef ModuleFileName, CompilerInstance &Instance);
+
   ModuleLoadResult loadModule(SourceLocation ImportLoc, ModuleIdPath Path,
                               Module::NameVisibilityKind Visibility,
                               bool IsInclusionDirective) override;

diff  --git a/clang/lib/Frontend/CompilerInstance.cpp 
b/clang/lib/Frontend/CompilerInstance.cpp
index eb138de9d20cc..243e0a3c15b05 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -1150,19 +1150,11 @@ static Language getLanguageFromOptions(const 
LangOptions &LangOpts) {
   return LangOpts.CPlusPlus ? Language::CXX : Language::C;
 }
 
-/// Creates a \c CompilerInstance for compiling a module.
-///
-/// This expects a properly initialized \c FrontendInputFile.
-static std::unique_ptr<CompilerInstance>
-createCompilerInstanceForModuleCompileImpl(CompilerInstance &ImportingInstance,
-                                           SourceLocation ImportLoc,
-                                           StringRef ModuleName,
-                                           FrontendInputFile Input,
-                                           StringRef OriginalModuleMapFile,
-                                           StringRef ModuleFileName) {
+std::unique_ptr<CompilerInstance> CompilerInstance::cloneForModuleCompileImpl(
+    SourceLocation ImportLoc, StringRef ModuleName, FrontendInputFile Input,
+    StringRef OriginalModuleMapFile, StringRef ModuleFileName) {
   // Construct a compiler invocation for creating this module.
-  auto Invocation =
-      std::make_shared<CompilerInvocation>(ImportingInstance.getInvocation());
+  auto Invocation = std::make_shared<CompilerInvocation>(getInvocation());
 
   PreprocessorOptions &PPOpts = Invocation->getPreprocessorOpts();
 
@@ -1182,7 +1174,7 @@ 
createCompilerInstanceForModuleCompileImpl(CompilerInstance &ImportingInstance,
 
   // If the original compiler invocation had -fmodule-name, pass it through.
   Invocation->getLangOpts().ModuleName =
-      ImportingInstance.getInvocation().getLangOpts().ModuleName;
+      getInvocation().getLangOpts().ModuleName;
 
   // Note the name of the module we're building.
   Invocation->getLangOpts().CurrentModule = std::string(ModuleName);
@@ -1206,82 +1198,70 @@ 
createCompilerInstanceForModuleCompileImpl(CompilerInstance &ImportingInstance,
   DiagnosticOptions &DiagOpts = Invocation->getDiagnosticOpts();
 
   DiagOpts.VerifyDiagnostics = 0;
-  assert(ImportingInstance.getInvocation().getModuleHash() ==
-         Invocation->getModuleHash() && "Module hash mismatch!");
+  assert(getInvocation().getModuleHash() == Invocation->getModuleHash() &&
+         "Module hash mismatch!");
 
   // Construct a compiler instance that will be used to actually create the
   // module.  Since we're sharing an in-memory module cache,
   // CompilerInstance::CompilerInstance is responsible for finalizing the
   // buffers to prevent use-after-frees.
   auto InstancePtr = std::make_unique<CompilerInstance>(
-      ImportingInstance.getPCHContainerOperations(),
-      &ImportingInstance.getModuleCache());
+      getPCHContainerOperations(), &getModuleCache());
   auto &Instance = *InstancePtr;
 
   auto &Inv = *Invocation;
   Instance.setInvocation(std::move(Invocation));
 
   Instance.createDiagnostics(
-      ImportingInstance.getVirtualFileSystem(),
-      new 
ForwardingDiagnosticConsumer(ImportingInstance.getDiagnosticClient()),
+      getVirtualFileSystem(),
+      new ForwardingDiagnosticConsumer(getDiagnosticClient()),
       /*ShouldOwnClient=*/true);
 
   if (llvm::is_contained(DiagOpts.SystemHeaderWarningsModules, ModuleName))
     Instance.getDiagnostics().setSuppressSystemWarnings(false);
 
   if (FrontendOpts.ModulesShareFileManager) {
-    Instance.setFileManager(&ImportingInstance.getFileManager());
+    Instance.setFileManager(&getFileManager());
   } else {
-    Instance.createFileManager(&ImportingInstance.getVirtualFileSystem());
+    Instance.createFileManager(&getVirtualFileSystem());
   }
   Instance.createSourceManager(Instance.getFileManager());
   SourceManager &SourceMgr = Instance.getSourceManager();
 
   // Note that this module is part of the module build stack, so that we
   // can detect cycles in the module graph.
-  SourceMgr.setModuleBuildStack(
-    ImportingInstance.getSourceManager().getModuleBuildStack());
+  SourceMgr.setModuleBuildStack(getSourceManager().getModuleBuildStack());
   SourceMgr.pushModuleBuildStack(ModuleName,
-    FullSourceLoc(ImportLoc, ImportingInstance.getSourceManager()));
+                                 FullSourceLoc(ImportLoc, getSourceManager()));
 
-  // Make sure that the failed-module structure has been allocated in
-  // the importing instance, and propagate the pointer to the newly-created
-  // instance.
-  if (!ImportingInstance.hasFailedModulesSet())
-    ImportingInstance.createFailedModulesSet();
-  Instance.setFailedModulesSet(ImportingInstance.getFailedModulesSetPtr());
+  // Make a copy for the new instance.
+  Instance.FailedModules = FailedModules;
 
   // If we're collecting module dependencies, we need to share a collector
   // between all of the module CompilerInstances. Other than that, we don't
   // want to produce any dependency output from the module build.
-  Instance.setModuleDepCollector(ImportingInstance.getModuleDepCollector());
+  Instance.setModuleDepCollector(getModuleDepCollector());
   Inv.getDependencyOutputOpts() = DependencyOutputOptions();
 
   return InstancePtr;
 }
 
-/// Compile a module file for the given module, using the options
-/// provided by the importing compiler instance. Returns true if the module
-/// was built without errors.
-static bool compileModule(CompilerInstance &ImportingInstance,
-                          SourceLocation ImportLoc, StringRef ModuleName,
-                          StringRef ModuleFileName,
-                          CompilerInstance &Instance) {
+bool CompilerInstance::compileModule(SourceLocation ImportLoc,
+                                     StringRef ModuleName,
+                                     StringRef ModuleFileName,
+                                     CompilerInstance &Instance) {
   llvm::TimeTraceScope TimeScope("Module Compile", ModuleName);
 
   // Never compile a module that's already finalized - this would cause the
   // existing module to be freed, causing crashes if it is later referenced
-  if (ImportingInstance.getModuleCache().getInMemoryModuleCache().isPCMFinal(
-          ModuleFileName)) {
-    ImportingInstance.getDiagnostics().Report(
-        ImportLoc, diag::err_module_rebuild_finalized)
+  if (getModuleCache().getInMemoryModuleCache().isPCMFinal(ModuleFileName)) {
+    getDiagnostics().Report(ImportLoc, diag::err_module_rebuild_finalized)
         << ModuleName;
     return false;
   }
 
-  ImportingInstance.getDiagnostics().Report(ImportLoc,
-                                            diag::remark_module_build)
-    << ModuleName << ModuleFileName;
+  getDiagnostics().Report(ImportLoc, diag::remark_module_build)
+      << ModuleName << ModuleFileName;
 
   // Execute the action to actually build the module in-place. Use a separate
   // thread so that we get a stack large enough.
@@ -1292,13 +1272,15 @@ static bool compileModule(CompilerInstance 
&ImportingInstance,
       },
       DesiredStackSize);
 
-  ImportingInstance.getDiagnostics().Report(ImportLoc,
-                                            diag::remark_module_build_done)
-    << ModuleName;
+  getDiagnostics().Report(ImportLoc, diag::remark_module_build_done)
+      << ModuleName;
 
   // Propagate the statistics to the parent FileManager.
-  if (!ImportingInstance.getFrontendOpts().ModulesShareFileManager)
-    ImportingInstance.getFileManager().AddStats(Instance.getFileManager());
+  if (!getFrontendOpts().ModulesShareFileManager)
+    getFileManager().AddStats(Instance.getFileManager());
+
+  // Propagate the failed modules to the parent instance.
+  FailedModules = std::move(Instance.FailedModules);
 
   if (Crashed) {
     // Clear the ASTConsumer if it hasn't been already, in case it owns streams
@@ -1312,8 +1294,8 @@ static bool compileModule(CompilerInstance 
&ImportingInstance,
 
   // We've rebuilt a module. If we're allowed to generate or update the global
   // module index, record that fact in the importing compiler instance.
-  if (ImportingInstance.getFrontendOpts().GenerateGlobalModuleIndex) {
-    ImportingInstance.setBuildGlobalModuleIndex(true);
+  if (getFrontendOpts().GenerateGlobalModuleIndex) {
+    setBuildGlobalModuleIndex(true);
   }
 
   // If \p AllowPCMWithCompilerErrors is set return 'success' even if errors
@@ -1378,8 +1360,8 @@ std::unique_ptr<CompilerInstance> 
CompilerInstance::cloneForModuleCompile(
     bool IsSystem = isSystem(SLoc.getFile().getFileCharacteristic());
 
     // Use the module map where this module resides.
-    return createCompilerInstanceForModuleCompileImpl(
-        *this, ImportLoc, ModuleName,
+    return cloneForModuleCompileImpl(
+        ImportLoc, ModuleName,
         FrontendInputFile(ModuleMapFilePath, IK, IsSystem),
         ModMap.getModuleMapFileForUniquing(Module)->getName(), ModuleFileName);
   }
@@ -1395,8 +1377,8 @@ std::unique_ptr<CompilerInstance> 
CompilerInstance::cloneForModuleCompile(
   llvm::raw_string_ostream OS(InferredModuleMapContent);
   Module->print(OS);
 
-  auto Instance = createCompilerInstanceForModuleCompileImpl(
-      *this, ImportLoc, ModuleName,
+  auto Instance = cloneForModuleCompileImpl(
+      ImportLoc, ModuleName,
       FrontendInputFile(FakeModuleMapFile, IK, +Module->IsSystem),
       ModMap.getModuleMapFileForUniquing(Module)->getName(), ModuleFileName);
 
@@ -1460,9 +1442,9 @@ static bool compileModuleAndReadASTImpl(CompilerInstance 
&ImportingInstance,
   auto Instance = ImportingInstance.cloneForModuleCompile(ModuleNameLoc, 
Module,
                                                           ModuleFileName);
 
-  if (!compileModule(ImportingInstance, ModuleNameLoc,
-                     Module->getTopLevelModuleName(), ModuleFileName,
-                     *Instance)) {
+  if (!ImportingInstance.compileModule(ModuleNameLoc,
+                                       Module->getTopLevelModuleName(),
+                                       ModuleFileName, *Instance)) {
     ImportingInstance.getDiagnostics().Report(ModuleNameLoc,
                                               diag::err_module_not_built)
         << Module->Name << SourceRange(ImportLoc, ModuleNameLoc);
@@ -2002,7 +1984,7 @@ ModuleLoadResult 
CompilerInstance::findOrCompileModuleAndReadAST(
   }
 
   // Check whether we have already attempted to build this module (but failed).
-  if (FailedModules && FailedModules->hasAlreadyFailed(ModuleName)) {
+  if (FailedModules.contains(ModuleName)) {
     getDiagnostics().Report(ModuleNameLoc, diag::err_module_not_built)
         << ModuleName << SourceRange(ImportLoc, ModuleNameLoc);
     return nullptr;
@@ -2013,8 +1995,7 @@ ModuleLoadResult 
CompilerInstance::findOrCompileModuleAndReadAST(
                                ModuleFilename)) {
     assert(getDiagnostics().hasErrorOccurred() &&
            "undiagnosed error in compileModuleAndReadAST");
-    if (FailedModules)
-      FailedModules->addFailed(ModuleName);
+    FailedModules.insert(ModuleName);
     return nullptr;
   }
 
@@ -2238,8 +2219,8 @@ void 
CompilerInstance::createModuleFromSource(SourceLocation ImportLoc,
 
   std::string NullTerminatedSource(Source.str());
 
-  auto Other = createCompilerInstanceForModuleCompileImpl(
-      *this, ImportLoc, ModuleName, Input, StringRef(), ModuleFileName);
+  auto Other = cloneForModuleCompileImpl(ImportLoc, ModuleName, Input,
+                                         StringRef(), ModuleFileName);
 
   // Create a virtual file containing our desired source.
   // FIXME: We shouldn't need to do this.
@@ -2252,8 +2233,7 @@ void 
CompilerInstance::createModuleFromSource(SourceLocation ImportLoc,
   Other->DeleteBuiltModules = false;
 
   // Build the module, inheriting any modules that we've built locally.
-  bool Success =
-      compileModule(*this, ImportLoc, ModuleName, ModuleFileName, *Other);
+  bool Success = compileModule(ImportLoc, ModuleName, ModuleFileName, *Other);
 
   BuiltModules = std::move(Other->BuiltModules);
 


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to