Skip to content

Commit 6d4ffbd

Browse files
authored
[clang][CodeGen] Shift relink option implementation away from module cloning (#81693)
We recently implemented a new option allowing relinking of bitcode modules via the "-mllvm -relink-builtin-bitcode-postop" option. This implementation relied on llvm::CloneModule() in order to pass copies to modules and preserve the original modules for later relinking. However, cloning modules has been found to be prohibitively expensive, significantly increasing compilation time for large bitcode libraries. In this patch, we shift the relink option implementation to instead link the original modules initially, and reload modules from the file system if relinking is requested. This approach results in significantly reduced overhead. We accomplish this by creating a new ReloadModules() routine that can be called from a BackendConsumer class, to mimic the behavior of ASTConsumer's loadLinkModules(), but without access to the CompilerInstance. Because loading the bitcodes from the filesystem requires access to the FileManager class, we also forward a reference to the CompilerInstance class to the BackendConsumer. This mirrors what is already done for several CompilerInstance members, such as TargetOptions and CodeGenOptions. Finally, we needed to add a const specifier to the FileManager::getBufferForFile() routine to allow it to be called using the const reference returned from CompilerInstance::getFileManager()
1 parent 16e7d68 commit 6d4ffbd

File tree

5 files changed

+109
-88
lines changed

5 files changed

+109
-88
lines changed

clang/include/clang/Basic/FileManager.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,15 +283,15 @@ class FileManager : public RefCountedBase<FileManager> {
283283
bool RequiresNullTerminator = true);
284284
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
285285
getBufferForFile(StringRef Filename, bool isVolatile = false,
286-
bool RequiresNullTerminator = true) {
286+
bool RequiresNullTerminator = true) const {
287287
return getBufferForFileImpl(Filename, /*FileSize=*/-1, isVolatile,
288288
RequiresNullTerminator);
289289
}
290290

291291
private:
292292
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
293293
getBufferForFileImpl(StringRef Filename, int64_t FileSize, bool isVolatile,
294-
bool RequiresNullTerminator);
294+
bool RequiresNullTerminator) const;
295295

296296
public:
297297
/// Get the 'stat' information for the given \p Path.

clang/lib/Basic/FileManager.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,7 @@ FileManager::getBufferForFile(FileEntryRef FE, bool isVolatile,
547547
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
548548
FileManager::getBufferForFileImpl(StringRef Filename, int64_t FileSize,
549549
bool isVolatile,
550-
bool RequiresNullTerminator) {
550+
bool RequiresNullTerminator) const {
551551
if (FileSystemOpts.WorkingDir.empty())
552552
return FS->getBufferForFile(Filename, FileSize, RequiresNullTerminator,
553553
isVolatile);

clang/lib/CodeGen/BackendConsumer.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class BackendConsumer : public ASTConsumer {
3434
const CodeGenOptions &CodeGenOpts;
3535
const TargetOptions &TargetOpts;
3636
const LangOptions &LangOpts;
37+
const FileManager &FileMgr;
3738
std::unique_ptr<raw_pwrite_stream> AsmOutStream;
3839
ASTContext *Context;
3940
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS;
@@ -74,8 +75,8 @@ class BackendConsumer : public ASTConsumer {
7475
const HeaderSearchOptions &HeaderSearchOpts,
7576
const PreprocessorOptions &PPOpts,
7677
const CodeGenOptions &CodeGenOpts,
77-
const TargetOptions &TargetOpts,
78-
const LangOptions &LangOpts, const std::string &InFile,
78+
const TargetOptions &TargetOpts, const LangOptions &LangOpts,
79+
const FileManager &FileMgr, const std::string &InFile,
7980
SmallVector<LinkModule, 4> LinkModules,
8081
std::unique_ptr<raw_pwrite_stream> OS, llvm::LLVMContext &C,
8182
CoverageSourceInfo *CoverageInfo = nullptr);
@@ -88,8 +89,8 @@ class BackendConsumer : public ASTConsumer {
8889
const HeaderSearchOptions &HeaderSearchOpts,
8990
const PreprocessorOptions &PPOpts,
9091
const CodeGenOptions &CodeGenOpts,
91-
const TargetOptions &TargetOpts,
92-
const LangOptions &LangOpts, llvm::Module *Module,
92+
const TargetOptions &TargetOpts, const LangOptions &LangOpts,
93+
const FileManager &FileMgr, llvm::Module *Module,
9394
SmallVector<LinkModule, 4> LinkModules, llvm::LLVMContext &C,
9495
CoverageSourceInfo *CoverageInfo = nullptr);
9596

@@ -111,10 +112,13 @@ class BackendConsumer : public ASTConsumer {
111112
void AssignInheritanceModel(CXXRecordDecl *RD) override;
112113
void HandleVTable(CXXRecordDecl *RD) override;
113114

114-
115-
// Links each entry in LinkModules into our module. Returns true on error.
115+
// Links each entry in LinkModules into our module. Returns true on error.
116116
bool LinkInModules(llvm::Module *M, bool ShouldLinkFiles = true);
117117

118+
// Load a bitcode module from -mlink-builtin-bitcode option using
119+
// methods from a BackendConsumer instead of CompilerInstance
120+
bool ReloadModules(llvm::Module *M);
121+
118122
/// Get the best possible source location to represent a diagnostic that
119123
/// may have associated debug info.
120124
const FullSourceLoc getBestLocationFromDebugLoc(

clang/lib/CodeGen/CodeGenAction.cpp

Lines changed: 84 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -109,56 +109,52 @@ static void reportOptRecordError(Error E, DiagnosticsEngine &Diags,
109109
});
110110
}
111111

112-
BackendConsumer::BackendConsumer(BackendAction Action, DiagnosticsEngine &Diags,
113-
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
114-
const HeaderSearchOptions &HeaderSearchOpts,
115-
const PreprocessorOptions &PPOpts,
116-
const CodeGenOptions &CodeGenOpts,
117-
const TargetOptions &TargetOpts,
118-
const LangOptions &LangOpts,
119-
const std::string &InFile,
120-
SmallVector<LinkModule, 4> LinkModules,
121-
std::unique_ptr<raw_pwrite_stream> OS,
122-
LLVMContext &C,
123-
CoverageSourceInfo *CoverageInfo)
124-
: Diags(Diags), Action(Action), HeaderSearchOpts(HeaderSearchOpts),
125-
CodeGenOpts(CodeGenOpts), TargetOpts(TargetOpts), LangOpts(LangOpts),
126-
AsmOutStream(std::move(OS)), Context(nullptr), FS(VFS),
127-
LLVMIRGeneration("irgen", "LLVM IR Generation Time"),
128-
LLVMIRGenerationRefCount(0),
129-
Gen(CreateLLVMCodeGen(Diags, InFile, std::move(VFS), HeaderSearchOpts,
130-
PPOpts, CodeGenOpts, C, CoverageInfo)),
131-
LinkModules(std::move(LinkModules)) {
132-
TimerIsEnabled = CodeGenOpts.TimePasses;
133-
llvm::TimePassesIsEnabled = CodeGenOpts.TimePasses;
134-
llvm::TimePassesPerRun = CodeGenOpts.TimePassesPerRun;
112+
BackendConsumer::BackendConsumer(
113+
BackendAction Action, DiagnosticsEngine &Diags,
114+
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
115+
const HeaderSearchOptions &HeaderSearchOpts,
116+
const PreprocessorOptions &PPOpts, const CodeGenOptions &CodeGenOpts,
117+
const TargetOptions &TargetOpts, const LangOptions &LangOpts,
118+
const FileManager &FileMgr, const std::string &InFile,
119+
SmallVector<LinkModule, 4> LinkModules,
120+
std::unique_ptr<raw_pwrite_stream> OS, LLVMContext &C,
121+
CoverageSourceInfo *CoverageInfo)
122+
: Diags(Diags), Action(Action), HeaderSearchOpts(HeaderSearchOpts),
123+
CodeGenOpts(CodeGenOpts), TargetOpts(TargetOpts), LangOpts(LangOpts),
124+
FileMgr(FileMgr), AsmOutStream(std::move(OS)), Context(nullptr), FS(VFS),
125+
LLVMIRGeneration("irgen", "LLVM IR Generation Time"),
126+
LLVMIRGenerationRefCount(0),
127+
Gen(CreateLLVMCodeGen(Diags, InFile, std::move(VFS), HeaderSearchOpts,
128+
PPOpts, CodeGenOpts, C, CoverageInfo)),
129+
LinkModules(std::move(LinkModules)) {
130+
TimerIsEnabled = CodeGenOpts.TimePasses;
131+
llvm::TimePassesIsEnabled = CodeGenOpts.TimePasses;
132+
llvm::TimePassesPerRun = CodeGenOpts.TimePassesPerRun;
135133
}
136134

137135
// This constructor is used in installing an empty BackendConsumer
138136
// to use the clang diagnostic handler for IR input files. It avoids
139137
// initializing the OS field.
140-
BackendConsumer::BackendConsumer(BackendAction Action, DiagnosticsEngine &Diags,
141-
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
142-
const HeaderSearchOptions &HeaderSearchOpts,
143-
const PreprocessorOptions &PPOpts,
144-
const CodeGenOptions &CodeGenOpts,
145-
const TargetOptions &TargetOpts,
146-
const LangOptions &LangOpts,
147-
llvm::Module *Module,
148-
SmallVector<LinkModule, 4> LinkModules,
149-
LLVMContext &C,
150-
CoverageSourceInfo *CoverageInfo)
151-
: Diags(Diags), Action(Action), HeaderSearchOpts(HeaderSearchOpts),
152-
CodeGenOpts(CodeGenOpts), TargetOpts(TargetOpts), LangOpts(LangOpts),
153-
Context(nullptr), FS(VFS),
154-
LLVMIRGeneration("irgen", "LLVM IR Generation Time"),
155-
LLVMIRGenerationRefCount(0),
156-
Gen(CreateLLVMCodeGen(Diags, "", std::move(VFS), HeaderSearchOpts,
157-
PPOpts, CodeGenOpts, C, CoverageInfo)),
158-
LinkModules(std::move(LinkModules)), CurLinkModule(Module) {
159-
TimerIsEnabled = CodeGenOpts.TimePasses;
160-
llvm::TimePassesIsEnabled = CodeGenOpts.TimePasses;
161-
llvm::TimePassesPerRun = CodeGenOpts.TimePassesPerRun;
138+
BackendConsumer::BackendConsumer(
139+
BackendAction Action, DiagnosticsEngine &Diags,
140+
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
141+
const HeaderSearchOptions &HeaderSearchOpts,
142+
const PreprocessorOptions &PPOpts, const CodeGenOptions &CodeGenOpts,
143+
const TargetOptions &TargetOpts, const LangOptions &LangOpts,
144+
const FileManager &FileMgr, llvm::Module *Module,
145+
SmallVector<LinkModule, 4> LinkModules, LLVMContext &C,
146+
CoverageSourceInfo *CoverageInfo)
147+
: Diags(Diags), Action(Action), HeaderSearchOpts(HeaderSearchOpts),
148+
CodeGenOpts(CodeGenOpts), TargetOpts(TargetOpts), LangOpts(LangOpts),
149+
FileMgr(FileMgr), Context(nullptr), FS(VFS),
150+
LLVMIRGeneration("irgen", "LLVM IR Generation Time"),
151+
LLVMIRGenerationRefCount(0),
152+
Gen(CreateLLVMCodeGen(Diags, "", std::move(VFS), HeaderSearchOpts, PPOpts,
153+
CodeGenOpts, C, CoverageInfo)),
154+
LinkModules(std::move(LinkModules)), CurLinkModule(Module) {
155+
TimerIsEnabled = CodeGenOpts.TimePasses;
156+
llvm::TimePassesIsEnabled = CodeGenOpts.TimePasses;
157+
llvm::TimePassesPerRun = CodeGenOpts.TimePassesPerRun;
162158
}
163159

164160
llvm::Module* BackendConsumer::getModule() const {
@@ -233,9 +229,37 @@ void BackendConsumer::HandleInterestingDecl(DeclGroupRef D) {
233229
HandleTopLevelDecl(D);
234230
}
235231

232+
bool BackendConsumer::ReloadModules(llvm::Module *M) {
233+
for (const CodeGenOptions::BitcodeFileToLink &F :
234+
CodeGenOpts.LinkBitcodeFiles) {
235+
auto BCBuf = FileMgr.getBufferForFile(F.Filename);
236+
if (!BCBuf) {
237+
Diags.Report(diag::err_cannot_open_file)
238+
<< F.Filename << BCBuf.getError().message();
239+
LinkModules.clear();
240+
return true;
241+
}
242+
243+
LLVMContext &Ctx = getModule()->getContext();
244+
Expected<std::unique_ptr<llvm::Module>> ModuleOrErr =
245+
getOwningLazyBitcodeModule(std::move(*BCBuf), Ctx);
246+
247+
if (!ModuleOrErr) {
248+
handleAllErrors(ModuleOrErr.takeError(), [&](ErrorInfoBase &EIB) {
249+
Diags.Report(diag::err_cannot_open_file) << F.Filename << EIB.message();
250+
});
251+
LinkModules.clear();
252+
return true;
253+
}
254+
LinkModules.push_back({std::move(ModuleOrErr.get()), F.PropagateAttrs,
255+
F.Internalize, F.LinkFlags});
256+
}
257+
258+
return false; // success
259+
}
260+
236261
// Links each entry in LinkModules into our module. Returns true on error.
237262
bool BackendConsumer::LinkInModules(llvm::Module *M, bool ShouldLinkFiles) {
238-
239263
for (auto &LM : LinkModules) {
240264
assert(LM.Module && "LinkModule does not actually have a module");
241265

@@ -257,37 +281,19 @@ bool BackendConsumer::LinkInModules(llvm::Module *M, bool ShouldLinkFiles) {
257281
CurLinkModule = LM.Module.get();
258282
bool Err;
259283

260-
auto DoLink = [&](auto &Mod) {
261-
if (LM.Internalize) {
262-
Err = Linker::linkModules(
263-
*M, std::move(Mod), LM.LinkFlags,
264-
[](llvm::Module &M, const llvm::StringSet<> &GVS) {
265-
internalizeModule(M, [&GVS](const llvm::GlobalValue &GV) {
266-
return !GV.hasName() || (GVS.count(GV.getName()) == 0);
267-
});
284+
if (LM.Internalize) {
285+
Err = Linker::linkModules(
286+
*M, std::move(LM.Module), LM.LinkFlags,
287+
[](llvm::Module &M, const llvm::StringSet<> &GVS) {
288+
internalizeModule(M, [&GVS](const llvm::GlobalValue &GV) {
289+
return !GV.hasName() || (GVS.count(GV.getName()) == 0);
268290
});
269-
} else
270-
Err = Linker::linkModules(*M, std::move(Mod), LM.LinkFlags);
271-
};
272-
273-
// Create a Clone to move to the linker, which preserves the original
274-
// linking modules, allowing them to be linked again in the future
275-
if (ClRelinkBuiltinBitcodePostop) {
276-
// TODO: If CloneModule() is updated to support cloning of unmaterialized
277-
// modules, we can remove this
278-
if (Error E = CurLinkModule->materializeAll())
279-
return false;
280-
281-
std::unique_ptr<llvm::Module> Clone = llvm::CloneModule(*LM.Module);
282-
283-
DoLink(Clone);
284-
}
285-
// Otherwise we can link (and clean up) the original modules
286-
else {
287-
DoLink(LM.Module);
288-
}
291+
});
292+
} else
293+
Err = Linker::linkModules(*M, std::move(LM.Module), LM.LinkFlags);
289294
}
290295

296+
LinkModules.clear();
291297
return false; // success
292298
}
293299

@@ -1037,8 +1043,9 @@ CodeGenAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
10371043
std::unique_ptr<BackendConsumer> Result(new BackendConsumer(
10381044
BA, CI.getDiagnostics(), &CI.getVirtualFileSystem(),
10391045
CI.getHeaderSearchOpts(), CI.getPreprocessorOpts(), CI.getCodeGenOpts(),
1040-
CI.getTargetOpts(), CI.getLangOpts(), std::string(InFile),
1041-
std::move(LinkModules), std::move(OS), *VMContext, CoverageInfo));
1046+
CI.getTargetOpts(), CI.getLangOpts(), CI.getFileManager(),
1047+
std::string(InFile), std::move(LinkModules), std::move(OS), *VMContext,
1048+
CoverageInfo));
10421049
BEConsumer = Result.get();
10431050

10441051
// Enable generating macro debug info only when debug info is not disabled and
@@ -1199,7 +1206,7 @@ void CodeGenAction::ExecuteAction() {
11991206
BackendConsumer Result(BA, CI.getDiagnostics(), &CI.getVirtualFileSystem(),
12001207
CI.getHeaderSearchOpts(), CI.getPreprocessorOpts(),
12011208
CI.getCodeGenOpts(), CI.getTargetOpts(),
1202-
CI.getLangOpts(), TheModule.get(),
1209+
CI.getLangOpts(), CI.getFileManager(), TheModule.get(),
12031210
std::move(LinkModules), *VMContext, nullptr);
12041211

12051212
// Link in each pending link module.

clang/lib/CodeGen/LinkInModulesPass.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,26 @@
1414
#include "LinkInModulesPass.h"
1515
#include "BackendConsumer.h"
1616

17+
#include "clang/Basic/CodeGenOptions.h"
18+
#include "clang/Basic/FileManager.h"
19+
#include "clang/Basic/SourceManager.h"
20+
1721
using namespace llvm;
1822

1923
LinkInModulesPass::LinkInModulesPass(clang::BackendConsumer *BC,
2024
bool ShouldLinkFiles)
2125
: BC(BC), ShouldLinkFiles(ShouldLinkFiles) {}
2226

2327
PreservedAnalyses LinkInModulesPass::run(Module &M, ModuleAnalysisManager &AM) {
28+
if (!BC)
29+
return PreservedAnalyses::all();
30+
31+
// Re-load bitcode modules from files
32+
if (BC->ReloadModules(&M))
33+
report_fatal_error("Bitcode module re-loading failed, aborted!");
2434

25-
if (BC && BC->LinkInModules(&M, ShouldLinkFiles))
26-
report_fatal_error("Bitcode module linking failed, compilation aborted!");
35+
if (BC->LinkInModules(&M, ShouldLinkFiles))
36+
report_fatal_error("Bitcode module re-linking failed, aborted!");
2737

2838
return PreservedAnalyses::all();
2939
}

0 commit comments

Comments
 (0)