[clang] [clang][deps] Parallelize module compilations (PR #180047)

Jan Svoboda via cfe-commits cfe-commits at lists.llvm.org
Thu Feb 12 08:47:45 PST 2026


================
@@ -546,6 +553,127 @@ dependencies::initializeScanInstanceDependencyCollector(
   return MDC;
 }
 
+struct SingleModuleWithAsyncModuleCompiles : PreprocessOnlyAction {
+  DependencyScanningService &Service;
+
+  SingleModuleWithAsyncModuleCompiles(DependencyScanningService &Service)
+      : Service(Service) {}
+
+  bool BeginSourceFileAction(CompilerInstance &CI) override;
+};
+
+/// The preprocessor callback that takes care of initiating an asynchronous
+/// module compilation if needed.
+struct AsyncModuleCompile : PPCallbacks {
+  CompilerInstance &CI;
+  DependencyScanningService &Service;
+
+  AsyncModuleCompile(CompilerInstance &CI, DependencyScanningService &Service)
+      : CI(CI), Service(Service) {}
+
+  void moduleLoadSkipped(Module *M) override {
+    M = M->getTopLevelModule();
+
+    HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo();
+    ModuleCache &ModCache = CI.getModuleCache();
+    std::string ModuleFileName = HS.getCachedModuleFileName(M);
+
+    uint64_t Timestamp = ModCache.getModuleTimestamp(ModuleFileName);
+    // Someone else already built/validated the PCM.
+    if (Timestamp > CI.getHeaderSearchOpts().BuildSessionTimestamp)
+      return;
+
+    if (!CI.getASTReader())
+      CI.createASTReader();
+    SmallVector<ASTReader::ImportedModule, 0> Imported;
+    // Only calling ReadASTCore() to avoid the expensive eager deserialization
+    // of the clang::Module objects in ReadAST().
+    // FIXME: Consider doing this in the new thread depending on how expensive
+    // the read turns out to be.
+    switch (CI.getASTReader()->ReadASTCore(
+        ModuleFileName, serialization::MK_ImplicitModule, SourceLocation(),
+        nullptr, Imported, {}, {}, {},
+        ASTReader::ARR_OutOfDate | ASTReader::ARR_Missing |
+            ASTReader::ARR_TreatModuleWithErrorsAsOutOfDate)) {
+    case ASTReader::Success:
+      // We successfully read a valid, up-to-date PCM.
+      // FIXME: This could update the timestamp. Regular calls to
+      // ASTReader::ReadAST() would do so unless they encountered corrupted
+      // AST block, corrupted extension block, or did not read the expected
+      // top-level module.
+      return;
+    case ASTReader::OutOfDate:
+    case ASTReader::Missing:
+      // The most interesting case.
+      break;
+    default:
+      // Let the regular scan diagnose this.
+      return;
+    }
+
+    ModCache.prepareForGetLock(ModuleFileName);
+    auto Lock = ModCache.getLock(ModuleFileName);
+    bool Owned;
+    llvm::Error LockErr = Lock->tryLock().moveInto(Owned);
+    // Someone else is building the PCM right now.
+    if (!LockErr && !Owned)
+      return;
+    // We should build the PCM.
+    // FIXME: Pass the correct BaseFS to the worker FS.
+    auto VFS = llvm::makeIntrusiveRefCnt<DependencyScanningWorkerFilesystem>(
+        Service.getSharedCache(), llvm::vfs::getRealFileSystem());
+    auto DC = std::make_unique<DiagnosticConsumer>();
+    auto MC = makeInProcessModuleCache(Service.getModuleCacheEntries());
+    CompilerInstance::ThreadSafeCloneConfig CloneConfig(std::move(VFS), *DC,
+                                                        std::move(MC));
+    auto ModCI1 = CI.cloneForModuleCompile(SourceLocation(), M, ModuleFileName,
+                                           CloneConfig);
+    auto ModCI2 = CI.cloneForModuleCompile(SourceLocation(), M, ModuleFileName,
+                                           CloneConfig);
+
+    // FIXME: Have the service own a thread pool and use that instead.
+    // FIXME: This lock belongs to a module cache that might not outlive the
+    // thread. (This should work for now, because the in-process lock only
+    // refers to an object managed by the service, which should outlive this
+    // thread.)
+    std::thread([Lock = std::move(Lock), ModCI1 = std::move(ModCI1),
+                 ModCI2 = std::move(ModCI2), DC = std::move(DC),
+                 Service = &Service] {
+      // Quickly discovers and compiles modules for the real scan below.
+      SingleModuleWithAsyncModuleCompiles Action1(*Service);
+      (void)ModCI1->ExecuteAction(Action1);
+      // The real scan below.
+      ModCI2->getPreprocessorOpts().SingleModuleParseMode = false;
+      GenerateModuleFromModuleMapAction Action2;
+      (void)ModCI2->ExecuteAction(Action2);
----------------
jansvoboda11 wrote:

Done.

https://github.com/llvm/llvm-project/pull/180047


More information about the cfe-commits mailing list