[clang] [clang] Introduce `ModuleCache::write()` (PR #188877)
Jan Svoboda via cfe-commits
cfe-commits at lists.llvm.org
Fri Mar 27 09:51:04 PDT 2026
https://github.com/jansvoboda11 updated https://github.com/llvm/llvm-project/pull/188877
>From 196117b66a6bf6cf282394673c63d5ebd8ef2c82 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Fri, 20 Mar 2026 20:54:01 -0700
Subject: [PATCH 1/3] [clang] Introduce `ModuleCache::write()`
---
.../clang/Basic/DiagnosticCommonKinds.td | 1 +
.../include/clang/Frontend/CompilerInstance.h | 10 +--
.../include/clang/Frontend/FrontendActions.h | 12 ++++
.../include/clang/Serialization/ModuleCache.h | 10 ++-
.../InProcessModuleCache.cpp | 6 ++
clang/lib/Frontend/CompilerInstance.cpp | 61 ++++++++++++++-----
clang/lib/Frontend/FrontendActions.cpp | 3 +-
clang/lib/Serialization/ModuleCache.cpp | 40 ++++++++++++
8 files changed, 121 insertions(+), 22 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td
index cb267e3ee05c1..b5f99606789fe 100644
--- a/clang/include/clang/Basic/DiagnosticCommonKinds.td
+++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td
@@ -103,6 +103,7 @@ def err_deleted_non_function : Error<
"only functions can have deleted definitions">;
def err_module_not_found : Error<"module '%0' not found">, DefaultFatal;
def err_module_not_built : Error<"could not build module '%0'">, DefaultFatal;
+def err_module_not_written : Error<"could not write module file for '%0' to '%1': %2">, DefaultFatal;
def err_module_build_disabled: Error<
"module '%0' is needed but has not been provided, and implicit use of module "
"files is disabled">, DefaultFatal;
diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h
index f206d012eacc9..be44817aa5a1b 100644
--- a/clang/include/clang/Frontend/CompilerInstance.h
+++ b/clang/include/clang/Frontend/CompilerInstance.h
@@ -934,12 +934,14 @@ class CompilerInstance : public ModuleLoader {
std::optional<ThreadSafeCloneConfig> ThreadSafeConfig = std::nullopt);
/// 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.
+ /// provided by the importing compiler instance. Returns the PCM file in
+ /// a buffer.
// 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);
+ std::unique_ptr<llvm::MemoryBuffer> compileModule(SourceLocation ImportLoc,
+ StringRef ModuleName,
+ StringRef ModuleFileName,
+ CompilerInstance &Instance);
ModuleLoadResult loadModule(SourceLocation ImportLoc, ModuleIdPath Path,
Module::NameVisibilityKind Visibility,
diff --git a/clang/include/clang/Frontend/FrontendActions.h b/clang/include/clang/Frontend/FrontendActions.h
index 87a9f0d4cb06c..c5aff7ae1a713 100644
--- a/clang/include/clang/Frontend/FrontendActions.h
+++ b/clang/include/clang/Frontend/FrontendActions.h
@@ -114,6 +114,15 @@ class GeneratePCHAction : public ASTFrontendAction {
};
class GenerateModuleAction : public ASTFrontendAction {
+public:
+ /// When \c OS is non-null, uses it for outputting the PCM file instead of
+ /// automatically creating an output file.
+ explicit GenerateModuleAction(std::unique_ptr<raw_pwrite_stream> OS = nullptr)
+ : OS(std::move(OS)) {}
+
+private:
+ std::unique_ptr<raw_pwrite_stream> OS;
+
virtual std::unique_ptr<raw_pwrite_stream>
CreateOutputFile(CompilerInstance &CI, StringRef InFile) = 0;
@@ -145,6 +154,9 @@ class GenerateInterfaceStubsAction : public ASTFrontendAction {
};
class GenerateModuleFromModuleMapAction : public GenerateModuleAction {
+public:
+ using GenerateModuleAction::GenerateModuleAction;
+
private:
bool BeginSourceFileAction(CompilerInstance &CI) override;
diff --git a/clang/include/clang/Serialization/ModuleCache.h b/clang/include/clang/Serialization/ModuleCache.h
index c6795c5dc358a..4fced900bbdcb 100644
--- a/clang/include/clang/Serialization/ModuleCache.h
+++ b/clang/include/clang/Serialization/ModuleCache.h
@@ -14,6 +14,7 @@
#include <ctime>
namespace llvm {
+class MemoryBufferRef;
class AdvisoryLock;
} // namespace llvm
@@ -52,7 +53,11 @@ class ModuleCache {
virtual InMemoryModuleCache &getInMemoryModuleCache() = 0;
virtual const InMemoryModuleCache &getInMemoryModuleCache() const = 0;
- // TODO: Virtualize writing/reading PCM files, etc.
+ /// Write the PCM contents to the given path in the module cache.
+ virtual std::error_code write(StringRef Path,
+ llvm::MemoryBufferRef Buffer) = 0;
+
+ // TODO: Virtualize reading PCM files, etc.
virtual ~ModuleCache() = default;
};
@@ -65,6 +70,9 @@ std::shared_ptr<ModuleCache> createCrossProcessModuleCache();
/// Shared implementation of `ModuleCache::maybePrune()`.
void maybePruneImpl(StringRef Path, time_t PruneInterval, time_t PruneAfter);
+
+/// Shared implementation of `ModuleCache::write()`.
+std::error_code writeImpl(StringRef Path, llvm::MemoryBufferRef Buffer);
} // namespace clang
#endif
diff --git a/clang/lib/DependencyScanning/InProcessModuleCache.cpp b/clang/lib/DependencyScanning/InProcessModuleCache.cpp
index cd7385c8f38c2..7bdfae8f3e567 100644
--- a/clang/lib/DependencyScanning/InProcessModuleCache.cpp
+++ b/clang/lib/DependencyScanning/InProcessModuleCache.cpp
@@ -127,6 +127,12 @@ class InProcessModuleCache : public ModuleCache {
maybePruneImpl(Path, PruneInterval, PruneAfter);
}
+ std::error_code write(StringRef Path, llvm::MemoryBufferRef Buffer) override {
+ // FIXME: This could use an in-memory cache to avoid IO, and only write to
+ // disk at the end of the scan.
+ return writeImpl(Path, Buffer);
+ }
+
InMemoryModuleCache &getInMemoryModuleCache() override { return InMemory; }
const InMemoryModuleCache &getInMemoryModuleCache() const override {
return InMemory;
diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index 1f1b6701c38df..262bf3484e6a0 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -56,6 +56,7 @@
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Signals.h"
+#include "llvm/Support/SmallVectorMemoryBuffer.h"
#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/VirtualFileSystem.h"
@@ -1238,10 +1239,10 @@ class PrettyStackTraceBuildModule : public llvm::PrettyStackTraceEntry {
};
} // namespace
-bool CompilerInstance::compileModule(SourceLocation ImportLoc,
- StringRef ModuleName,
- StringRef ModuleFileName,
- CompilerInstance &Instance) {
+std::unique_ptr<llvm::MemoryBuffer>
+CompilerInstance::compileModule(SourceLocation ImportLoc, StringRef ModuleName,
+ StringRef ModuleFileName,
+ CompilerInstance &Instance) {
PrettyStackTraceBuildModule CrashInfo(ModuleName, ModuleFileName);
llvm::TimeTraceScope TimeScope("Module Compile", ModuleName);
@@ -1250,18 +1251,22 @@ bool CompilerInstance::compileModule(SourceLocation ImportLoc,
if (getModuleCache().getInMemoryModuleCache().isPCMFinal(ModuleFileName)) {
getDiagnostics().Report(ImportLoc, diag::err_module_rebuild_finalized)
<< ModuleName;
- return false;
+ return nullptr;
}
getDiagnostics().Report(ImportLoc, diag::remark_module_build)
<< ModuleName << ModuleFileName;
+ SmallString<0> Buffer;
+
// Execute the action to actually build the module in-place. Use a separate
// thread so that we get a stack large enough.
bool Crashed = !llvm::CrashRecoveryContext().RunSafelyOnNewStack(
[&]() {
+ auto OS = std::make_unique<llvm::raw_svector_ostream>(Buffer);
+
std::unique_ptr<FrontendAction> Action =
- std::make_unique<GenerateModuleFromModuleMapAction>();
+ std::make_unique<GenerateModuleFromModuleMapAction>(std::move(OS));
if (auto WrapGenModuleAction = Instance.getGenModuleActionWrapper())
Action = WrapGenModuleAction(Instance.getFrontendOpts(),
@@ -1297,10 +1302,17 @@ bool CompilerInstance::compileModule(SourceLocation ImportLoc,
setBuildGlobalModuleIndex(true);
}
- // If \p AllowPCMWithCompilerErrors is set return 'success' even if errors
+ if (Crashed)
+ return nullptr;
+
+ // Unless \p AllowPCMWithCompilerErrors is set, return 'failure' if errors
// occurred.
- return !Instance.getDiagnostics().hasErrorOccurred() ||
- Instance.getFrontendOpts().AllowPCMWithCompilerErrors;
+ if (Instance.getDiagnostics().hasErrorOccurred() &&
+ !Instance.getFrontendOpts().AllowPCMWithCompilerErrors)
+ return nullptr;
+
+ return std::make_unique<llvm::SmallVectorMemoryBuffer>(
+ std::move(Buffer), Instance.getFrontendOpts().OutputFile);
}
static OptionalFileEntryRef getPublicModuleMap(FileEntryRef File,
@@ -1442,13 +1454,17 @@ static bool compileModuleImpl(CompilerInstance &ImportingInstance,
SourceLocation ImportLoc,
SourceLocation ModuleNameLoc, Module *Module,
ModuleFileName ModuleFileName) {
+ std::unique_ptr<llvm::MemoryBuffer> Buffer;
+
{
auto Instance = ImportingInstance.cloneForModuleCompile(
ModuleNameLoc, Module, ModuleFileName);
- if (!ImportingInstance.compileModule(ModuleNameLoc,
- Module->getTopLevelModuleName(),
- ModuleFileName, *Instance)) {
+ Buffer = ImportingInstance.compileModule(ModuleNameLoc,
+ Module->getTopLevelModuleName(),
+ ModuleFileName, *Instance);
+
+ if (!Buffer) {
ImportingInstance.getDiagnostics().Report(ModuleNameLoc,
diag::err_module_not_built)
<< Module->Name << SourceRange(ImportLoc, ModuleNameLoc);
@@ -1456,6 +1472,16 @@ static bool compileModuleImpl(CompilerInstance &ImportingInstance,
}
}
+ std::error_code EC =
+ ImportingInstance.getModuleCache().write(ModuleFileName, *Buffer);
+ if (EC) {
+ ImportingInstance.getDiagnostics().Report(ModuleNameLoc,
+ diag::err_module_not_written)
+ << Module->Name << ModuleFileName << EC.message()
+ << SourceRange(ImportLoc, ModuleNameLoc);
+ return false;
+ }
+
// The module is built successfully, we can update its timestamp now.
if (ImportingInstance.getPreprocessor()
.getHeaderSearchInfo()
@@ -2196,8 +2222,9 @@ void CompilerInstance::createModuleFromSource(SourceLocation ImportLoc,
// output is nondeterministic (as .pcm files refer to each other by name).
// Can this affect the output in any way?
SmallString<128> ModuleFileName;
+ int FD;
if (std::error_code EC = llvm::sys::fs::createTemporaryFile(
- CleanModuleName, "pcm", ModuleFileName)) {
+ CleanModuleName, "pcm", FD, ModuleFileName)) {
getDiagnostics().Report(ImportLoc, diag::err_fe_unable_to_open_output)
<< ModuleFileName << EC.message();
return;
@@ -2225,12 +2252,14 @@ void CompilerInstance::createModuleFromSource(SourceLocation ImportLoc,
Other->DeleteBuiltModules = false;
// Build the module, inheriting any modules that we've built locally.
- bool Success = compileModule(ImportLoc, ModuleName, ModuleFileName, *Other);
-
+ std::unique_ptr<llvm::MemoryBuffer> Buffer =
+ compileModule(ImportLoc, ModuleName, ModuleFileName, *Other);
BuiltModules = std::move(Other->BuiltModules);
- if (Success) {
+ if (Buffer) {
+ llvm::raw_fd_ostream OS(FD, /*shouldClose=*/true);
BuiltModules[std::string(ModuleName)] = std::string(ModuleFileName);
+ OS << Buffer->getBuffer();
llvm::sys::RemoveFileOnSignal(ModuleFileName);
}
}
diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp
index e5eaab0da7adb..42f1ae3d83ed3 100644
--- a/clang/lib/Frontend/FrontendActions.cpp
+++ b/clang/lib/Frontend/FrontendActions.cpp
@@ -188,7 +188,8 @@ bool GeneratePCHAction::BeginSourceFileAction(CompilerInstance &CI) {
std::vector<std::unique_ptr<ASTConsumer>>
GenerateModuleAction::CreateMultiplexConsumer(CompilerInstance &CI,
StringRef InFile) {
- std::unique_ptr<raw_pwrite_stream> OS = CreateOutputFile(CI, InFile);
+ if (!OS)
+ OS = CreateOutputFile(CI, InFile);
if (!OS)
return {};
diff --git a/clang/lib/Serialization/ModuleCache.cpp b/clang/lib/Serialization/ModuleCache.cpp
index 658da6e3b7145..6a1fe5e635cd8 100644
--- a/clang/lib/Serialization/ModuleCache.cpp
+++ b/clang/lib/Serialization/ModuleCache.cpp
@@ -101,6 +101,39 @@ void clang::maybePruneImpl(StringRef Path, time_t PruneInterval,
}
}
+std::error_code clang::writeImpl(StringRef Path, llvm::MemoryBufferRef Buffer) {
+ StringRef Extension = llvm::sys::path::extension(Path);
+ SmallString<128> ModelPath = StringRef(Path).drop_back(Extension.size());
+ ModelPath += "-%%%%%%%%";
+ ModelPath += Extension;
+ ModelPath += ".tmp";
+
+ std::error_code EC;
+ int FD;
+ SmallString<128> TmpPath;
+ if ((EC = llvm::sys::fs::createUniqueFile(ModelPath, FD, TmpPath))) {
+ if (EC != std::errc::no_such_file_or_directory)
+ return EC;
+
+ StringRef Dir = llvm::sys::path::parent_path(Path);
+ if (std::error_code InnerEC = llvm::sys::fs::create_directories(Dir))
+ return InnerEC;
+
+ if ((EC = llvm::sys::fs::createUniqueFile(ModelPath, FD, TmpPath)))
+ return EC;
+ }
+
+ {
+ llvm::raw_fd_ostream OS(FD, /*shouldClose=*/true);
+ OS << Buffer.getBuffer();
+ }
+
+ if ((EC = llvm::sys::fs::rename(TmpPath, Path)))
+ return EC;
+
+ return {};
+}
+
namespace {
class CrossProcessModuleCache : public ModuleCache {
InMemoryModuleCache InMemory;
@@ -157,6 +190,13 @@ class CrossProcessModuleCache : public ModuleCache {
maybePruneImpl(Path, PruneInterval, PruneAfter);
}
+ std::error_code write(StringRef Path, llvm::MemoryBufferRef Buffer) override {
+ // This is a compiler-internal input/output, let's bypass the sandbox.
+ auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
+
+ return writeImpl(Path, Buffer);
+ }
+
InMemoryModuleCache &getInMemoryModuleCache() override { return InMemory; }
const InMemoryModuleCache &getInMemoryModuleCache() const override {
return InMemory;
>From eaa8b5e9260c7e4303500865935e7604f03751b0 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Fri, 27 Mar 2026 09:48:29 -0700
Subject: [PATCH 2/3] Add missing include
---
clang/include/clang/Serialization/ModuleCache.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/clang/include/clang/Serialization/ModuleCache.h b/clang/include/clang/Serialization/ModuleCache.h
index 4fced900bbdcb..9ea4d84380660 100644
--- a/clang/include/clang/Serialization/ModuleCache.h
+++ b/clang/include/clang/Serialization/ModuleCache.h
@@ -12,6 +12,7 @@
#include "clang/Basic/LLVM.h"
#include <ctime>
+#include <system_error>
namespace llvm {
class MemoryBufferRef;
>From ae3fab117c51725a63b5479711659f140b8df280 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Fri, 27 Mar 2026 09:50:50 -0700
Subject: [PATCH 3/3] Use `llvm::sys::fs::file_t`
---
clang/lib/Serialization/ModuleCache.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Serialization/ModuleCache.cpp b/clang/lib/Serialization/ModuleCache.cpp
index 6a1fe5e635cd8..26fb3ddb0aef4 100644
--- a/clang/lib/Serialization/ModuleCache.cpp
+++ b/clang/lib/Serialization/ModuleCache.cpp
@@ -109,7 +109,7 @@ std::error_code clang::writeImpl(StringRef Path, llvm::MemoryBufferRef Buffer) {
ModelPath += ".tmp";
std::error_code EC;
- int FD;
+ llvm::sys::fs::file_t FD;
SmallString<128> TmpPath;
if ((EC = llvm::sys::fs::createUniqueFile(ModelPath, FD, TmpPath))) {
if (EC != std::errc::no_such_file_or_directory)
More information about the cfe-commits
mailing list