[clang] 4a1105b - [clang][modules] Unlock before reading just-built PCM (#183787)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 11 07:11:23 PDT 2026
Author: Jan Svoboda
Date: 2026-03-11T07:11:18-07:00
New Revision: 4a1105be78248f068f1e4822b6f076682734380c
URL: https://github.com/llvm/llvm-project/commit/4a1105be78248f068f1e4822b6f076682734380c
DIFF: https://github.com/llvm/llvm-project/commit/4a1105be78248f068f1e4822b6f076682734380c.diff
LOG: [clang][modules] Unlock before reading just-built PCM (#183787)
Implicitly-built modules are stored in the in-memory cache of the
`CompilerInstance` responsible for building it. This means it's safe to
release the lock right after building it, and read it outside of the
critical section from the in-memory module cache. This speeds up
dependency scanning in a statistically significant way, somewhere
between 0.5% and 1.0%.
Added:
Modified:
clang/lib/Frontend/CompilerInstance.cpp
Removed:
################################################################################
diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index 60914d9b2cbc7..9873db5a4228b 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -1406,6 +1406,7 @@ std::unique_ptr<CompilerInstance> CompilerInstance::cloneForModuleCompile(
}
/// Read the AST right after compiling the module.
+/// Returns true on success, false on failure.
static bool readASTAfterCompileModule(CompilerInstance &ImportingInstance,
SourceLocation ImportLoc,
SourceLocation ModuleNameLoc,
@@ -1445,13 +1446,12 @@ static bool readASTAfterCompileModule(CompilerInstance &ImportingInstance,
return false;
}
-/// Compile a module in a separate compiler instance and read the AST,
-/// returning true if the module compiles without errors.
-static bool compileModuleAndReadASTImpl(CompilerInstance &ImportingInstance,
- SourceLocation ImportLoc,
- SourceLocation ModuleNameLoc,
- Module *Module,
- StringRef ModuleFileName) {
+/// Compile a module in a separate compiler instance.
+/// Returns true on success, false on failure.
+static bool compileModuleImpl(CompilerInstance &ImportingInstance,
+ SourceLocation ImportLoc,
+ SourceLocation ModuleNameLoc, Module *Module,
+ StringRef ModuleFileName) {
{
auto Instance = ImportingInstance.cloneForModuleCompile(
ModuleNameLoc, Module, ModuleFileName);
@@ -1474,20 +1474,25 @@ static bool compileModuleAndReadASTImpl(CompilerInstance &ImportingInstance,
ImportingInstance.getModuleCache().updateModuleTimestamp(ModuleFileName);
}
- return readASTAfterCompileModule(ImportingInstance, ImportLoc, ModuleNameLoc,
- Module, ModuleFileName,
- /*OutOfDate=*/nullptr, /*Missing=*/nullptr);
+ return true;
}
-/// Compile a module in a separate compiler instance and read the AST,
-/// returning true if the module compiles without errors, using a lock manager
-/// to avoid building the same module in multiple compiler instances.
-///
-/// Uses a lock file manager and exponential backoff to reduce the chances that
-/// multiple instances will compete to create the same module. On timeout,
-/// deletes the lock file in order to avoid deadlock from crashing processes or
-/// bugs in the lock file manager.
-static bool compileModuleAndReadASTBehindLock(
+/// The result of `compileModuleBehindLockOrRead()`.
+enum class CompileOrReadResult : uint8_t {
+ /// We failed to compile the module.
+ FailedToCompile,
+ /// We successfully compiled the module and we still need to read it.
+ Compiled,
+ /// We failed to read the module file compiled by another instance.
+ FailedToRead,
+ /// We read a module file compiled by another instance.
+ Read,
+};
+
+/// Attempt to compile the module in a separate compiler instance behind a lock
+/// (to avoid building the same module in multiple compiler instances), or read
+/// the AST produced by another compiler instance.
+static CompileOrReadResult compileModuleBehindLockOrRead(
CompilerInstance &ImportingInstance, SourceLocation ImportLoc,
SourceLocation ModuleNameLoc, Module *Module, StringRef ModuleFileName) {
DiagnosticsEngine &Diags = ImportingInstance.getDiagnostics();
@@ -1507,13 +1512,17 @@ static bool compileModuleAndReadASTBehindLock(
// related errors.
Diags.Report(ModuleNameLoc, diag::remark_module_lock_failure)
<< Module->Name << toString(std::move(Err));
- return compileModuleAndReadASTImpl(ImportingInstance, ImportLoc,
- ModuleNameLoc, Module, ModuleFileName);
+ if (!compileModuleImpl(ImportingInstance, ImportLoc, ModuleNameLoc,
+ Module, ModuleFileName))
+ return CompileOrReadResult::FailedToCompile;
+ return CompileOrReadResult::Compiled;
}
if (Owned) {
// We're responsible for building the module ourselves.
- return compileModuleAndReadASTImpl(ImportingInstance, ImportLoc,
- ModuleNameLoc, Module, ModuleFileName);
+ if (!compileModuleImpl(ImportingInstance, ImportLoc, ModuleNameLoc,
+ Module, ModuleFileName))
+ return CompileOrReadResult::FailedToCompile;
+ return CompileOrReadResult::Compiled;
}
// Someone else is responsible for building the module. Wait for them to
@@ -1541,9 +1550,9 @@ static bool compileModuleAndReadASTBehindLock(
bool Missing = false;
if (readASTAfterCompileModule(ImportingInstance, ImportLoc, ModuleNameLoc,
Module, ModuleFileName, &OutOfDate, &Missing))
- return true;
+ return CompileOrReadResult::Read;
if (!OutOfDate && !Missing)
- return false;
+ return CompileOrReadResult::FailedToRead;
// The module may be missing or out of date in the presence of file system
// races. It may also be out of date if one of its imports depends on header
@@ -1560,15 +1569,30 @@ static bool compileModuleAndReadAST(CompilerInstance &ImportingInstance,
SourceLocation ImportLoc,
SourceLocation ModuleNameLoc,
Module *Module, StringRef ModuleFileName) {
- return ImportingInstance.getInvocation()
- .getFrontendOpts()
- .BuildingImplicitModuleUsesLock
- ? compileModuleAndReadASTBehindLock(ImportingInstance, ImportLoc,
- ModuleNameLoc, Module,
- ModuleFileName)
- : compileModuleAndReadASTImpl(ImportingInstance, ImportLoc,
- ModuleNameLoc, Module,
- ModuleFileName);
+ if (ImportingInstance.getInvocation()
+ .getFrontendOpts()
+ .BuildingImplicitModuleUsesLock) {
+ switch (compileModuleBehindLockOrRead(
+ ImportingInstance, ImportLoc, ModuleNameLoc, Module, ModuleFileName)) {
+ case CompileOrReadResult::FailedToRead:
+ case CompileOrReadResult::FailedToCompile:
+ return false;
+ case CompileOrReadResult::Read:
+ return true;
+ case CompileOrReadResult::Compiled:
+ // We successfully compiled the module under a lock. Let's read it from
+ // the in-memory module cache now.
+ break;
+ }
+ } else {
+ if (!compileModuleImpl(ImportingInstance, ImportLoc, ModuleNameLoc, Module,
+ ModuleFileName))
+ return false;
+ }
+
+ return readASTAfterCompileModule(ImportingInstance, ImportLoc, ModuleNameLoc,
+ Module, ModuleFileName,
+ /*OutOfDate=*/nullptr, /*Missing=*/nullptr);
}
/// Diagnose
diff erences between the current definition of the given
More information about the cfe-commits
mailing list