Skip to content

Commit cf294be

Browse files
committed
[CodeGen] Implement post-optimization linking option for builtin bitcodes
In this patch, we create a new ModulePass that mimics the LinkInModules API from CodeGenAction.cpp, and a new command line option to enable the pass. With this new pass, we can now re-link bitcodes supplied via the -mlink-built-in bitcodes as part of the RunOptimizationPipeline. With the re-linking pass, we now handle cases where new device library functions are introduced as part of the optimization pipeline. Previously, these newly introduced functions (for example a fused sincos call) would result in a linking error due to a missing function defintion. This new pass can be initiated via: -mllvm -relink-builtin-bitcode-postop Also note we intentionally exclude bitcodes supplied via the -mlink-bitcode-file option from the second linking step
1 parent c8df891 commit cf294be

File tree

7 files changed

+138
-35
lines changed

7 files changed

+138
-35
lines changed

clang/include/clang/CodeGen/BackendUtil.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ namespace clang {
3030
class CodeGenOptions;
3131
class TargetOptions;
3232
class LangOptions;
33+
class BackendConsumer;
3334

3435
enum BackendAction {
3536
Backend_EmitAssembly, ///< Emit native assembly files
@@ -45,7 +46,8 @@ namespace clang {
4546
const TargetOptions &TOpts, const LangOptions &LOpts,
4647
StringRef TDesc, llvm::Module *M, BackendAction Action,
4748
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
48-
std::unique_ptr<raw_pwrite_stream> OS);
49+
std::unique_ptr<raw_pwrite_stream> OS,
50+
BackendConsumer *BC = nullptr);
4951

5052
void EmbedBitcode(llvm::Module *M, const CodeGenOptions &CGOpts,
5153
llvm::MemoryBufferRef Buf);

clang/lib/CodeGen/BackendConsumer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ class BackendConsumer : public ASTConsumer {
113113

114114

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

118118
/// Get the best possible source location to represent a diagnostic that
119119
/// may have associated debug info.

clang/lib/CodeGen/BackendUtil.cpp

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "clang/CodeGen/BackendUtil.h"
10+
#include "BackendConsumer.h"
11+
#include "LinkInModulesPass.h"
1012
#include "clang/Basic/CodeGenOptions.h"
1113
#include "clang/Basic/Diagnostic.h"
1214
#include "clang/Basic/LangOptions.h"
@@ -98,6 +100,11 @@ extern cl::opt<bool> PrintPipelinePasses;
98100
static cl::opt<bool> ClSanitizeOnOptimizerEarlyEP(
99101
"sanitizer-early-opt-ep", cl::Optional,
100102
cl::desc("Insert sanitizers on OptimizerEarlyEP."), cl::init(false));
103+
104+
// Re-link builtin bitcodes after optimization
105+
static cl::opt<bool> ClRelinkBuiltinBitcodePostop(
106+
"relink-builtin-bitcode-postop", cl::Optional,
107+
cl::desc("Re-link builtin bitcodes after optimization."), cl::init(false));
101108
}
102109

103110
namespace {
@@ -156,10 +163,9 @@ class EmitAssemblyHelper {
156163
return F;
157164
}
158165

159-
void
160-
RunOptimizationPipeline(BackendAction Action,
161-
std::unique_ptr<raw_pwrite_stream> &OS,
162-
std::unique_ptr<llvm::ToolOutputFile> &ThinLinkOS);
166+
void RunOptimizationPipeline(
167+
BackendAction Action, std::unique_ptr<raw_pwrite_stream> &OS,
168+
std::unique_ptr<llvm::ToolOutputFile> &ThinLinkOS, BackendConsumer *BC);
163169
void RunCodegenPipeline(BackendAction Action,
164170
std::unique_ptr<raw_pwrite_stream> &OS,
165171
std::unique_ptr<llvm::ToolOutputFile> &DwoOS);
@@ -194,8 +200,8 @@ class EmitAssemblyHelper {
194200
std::unique_ptr<TargetMachine> TM;
195201

196202
// Emit output using the new pass manager for the optimization pipeline.
197-
void EmitAssembly(BackendAction Action,
198-
std::unique_ptr<raw_pwrite_stream> OS);
203+
void EmitAssembly(BackendAction Action, std::unique_ptr<raw_pwrite_stream> OS,
204+
BackendConsumer *BC);
199205
};
200206
}
201207

@@ -764,7 +770,7 @@ static void addSanitizers(const Triple &TargetTriple,
764770

765771
void EmitAssemblyHelper::RunOptimizationPipeline(
766772
BackendAction Action, std::unique_ptr<raw_pwrite_stream> &OS,
767-
std::unique_ptr<llvm::ToolOutputFile> &ThinLinkOS) {
773+
std::unique_ptr<llvm::ToolOutputFile> &ThinLinkOS, BackendConsumer *BC) {
768774
std::optional<PGOOptions> PGOOpt;
769775

770776
if (CodeGenOpts.hasProfileIRInstr())
@@ -1040,6 +1046,13 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
10401046
}
10411047
}
10421048

1049+
// Re-link against any bitcodes supplied via the -mlink-builtin-bitcode option
1050+
// Some optimizations may generate new function calls that would not have
1051+
// been linked pre-optimization (i.e. fused sincos calls generated by
1052+
// AMDGPULibCalls::fold_sincos.)
1053+
if (ClRelinkBuiltinBitcodePostop)
1054+
MPM.addPass(LinkInModulesPass(BC, false));
1055+
10431056
// Add a verifier pass if requested. We don't have to do this if the action
10441057
// requires code generation because there will already be a verifier pass in
10451058
// the code-generation pipeline.
@@ -1169,7 +1182,8 @@ void EmitAssemblyHelper::RunCodegenPipeline(
11691182
}
11701183

11711184
void EmitAssemblyHelper::EmitAssembly(BackendAction Action,
1172-
std::unique_ptr<raw_pwrite_stream> OS) {
1185+
std::unique_ptr<raw_pwrite_stream> OS,
1186+
BackendConsumer *BC) {
11731187
TimeRegion Region(CodeGenOpts.TimePasses ? &CodeGenerationTime : nullptr);
11741188
setCommandLineOpts(CodeGenOpts);
11751189

@@ -1185,7 +1199,7 @@ void EmitAssemblyHelper::EmitAssembly(BackendAction Action,
11851199
cl::PrintOptionValues();
11861200

11871201
std::unique_ptr<llvm::ToolOutputFile> ThinLinkOS, DwoOS;
1188-
RunOptimizationPipeline(Action, OS, ThinLinkOS);
1202+
RunOptimizationPipeline(Action, OS, ThinLinkOS, BC);
11891203
RunCodegenPipeline(Action, OS, DwoOS);
11901204

11911205
if (ThinLinkOS)
@@ -1299,14 +1313,12 @@ static void runThinLTOBackend(
12991313
}
13001314
}
13011315

1302-
void clang::EmitBackendOutput(DiagnosticsEngine &Diags,
1303-
const HeaderSearchOptions &HeaderOpts,
1304-
const CodeGenOptions &CGOpts,
1305-
const clang::TargetOptions &TOpts,
1306-
const LangOptions &LOpts, StringRef TDesc,
1307-
llvm::Module *M, BackendAction Action,
1308-
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
1309-
std::unique_ptr<raw_pwrite_stream> OS) {
1316+
void clang::EmitBackendOutput(
1317+
DiagnosticsEngine &Diags, const HeaderSearchOptions &HeaderOpts,
1318+
const CodeGenOptions &CGOpts, const clang::TargetOptions &TOpts,
1319+
const LangOptions &LOpts, StringRef TDesc, llvm::Module *M,
1320+
BackendAction Action, IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
1321+
std::unique_ptr<raw_pwrite_stream> OS, BackendConsumer *BC) {
13101322

13111323
llvm::TimeTraceScope TimeScope("Backend");
13121324

@@ -1349,7 +1361,7 @@ void clang::EmitBackendOutput(DiagnosticsEngine &Diags,
13491361
}
13501362

13511363
EmitAssemblyHelper AsmHelper(Diags, HeaderOpts, CGOpts, TOpts, LOpts, M, VFS);
1352-
AsmHelper.EmitAssembly(Action, std::move(OS));
1364+
AsmHelper.EmitAssembly(Action, std::move(OS), BC);
13531365

13541366
// Verify clang's TargetInfo DataLayout against the LLVM TargetMachine's
13551367
// DataLayout.

clang/lib/CodeGen/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ add_clang_library(clangCodeGen
103103
ConstantInitBuilder.cpp
104104
CoverageMappingGen.cpp
105105
ItaniumCXXABI.cpp
106+
LinkInModulesPass.cpp
106107
MacroPPCallbacks.cpp
107108
MicrosoftCXXABI.cpp
108109
ModuleBuilder.cpp

clang/lib/CodeGen/CodeGenAction.cpp

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
#include "llvm/Support/ToolOutputFile.h"
5050
#include "llvm/Support/YAMLTraits.h"
5151
#include "llvm/Transforms/IPO/Internalize.h"
52+
#include "llvm/Transforms/Utils/Cloning.h"
5253

5354
#include <optional>
5455
using namespace clang;
@@ -229,9 +230,16 @@ void BackendConsumer::HandleInterestingDecl(DeclGroupRef D) {
229230
}
230231

231232
// Links each entry in LinkModules into our module. Returns true on error.
232-
bool BackendConsumer::LinkInModules(llvm::Module *M) {
233+
bool BackendConsumer::LinkInModules(llvm::Module *M, bool ShouldLinkFiles) {
234+
233235
for (auto &LM : LinkModules) {
234236
assert(LM.Module && "LinkModule does not actually have a module");
237+
238+
// If ShouldLinkFiles is not set, skip files added via the
239+
// -mlink-bitcode-files, only linking -mlink-builtin-bitcode
240+
if (!LM.Internalize && !ShouldLinkFiles)
241+
continue;
242+
235243
if (LM.PropagateAttrs)
236244
for (Function &F : *LM.Module) {
237245
// Skip intrinsics. Keep consistent with how intrinsics are created
@@ -244,24 +252,33 @@ bool BackendConsumer::LinkInModules(llvm::Module *M) {
244252

245253
CurLinkModule = LM.Module.get();
246254

255+
// TODO: If CloneModule() is updated to support cloning of unmaterialized
256+
// modules, we can remove this
247257
bool Err;
258+
if (Error E = CurLinkModule->materializeAll())
259+
return false;
260+
261+
// Create a Clone to move to the linker, which preserves the original
262+
// linking modules, allowing them to be linked again in the future
263+
// TODO: Add a ShouldCleanup option to make Cloning optional. When
264+
// set, we can pass the original modules to the linker for cleanup
265+
std::unique_ptr<llvm::Module> Clone = llvm::CloneModule(*LM.Module);
266+
248267
if (LM.Internalize) {
249268
Err = Linker::linkModules(
250-
*M, std::move(LM.Module), LM.LinkFlags,
251-
[](llvm::Module &M, const llvm::StringSet<> &GVS) {
252-
internalizeModule(M, [&GVS](const llvm::GlobalValue &GV) {
253-
return !GV.hasName() ||
254-
(GVS.count(GV.getName()) == 0);
255-
});
256-
});
257-
} else {
258-
Err = Linker::linkModules(*M, std::move(LM.Module), LM.LinkFlags);
259-
}
269+
*M, std::move(Clone), LM.LinkFlags,
270+
[](llvm::Module &M, const llvm::StringSet<> &GVS) {
271+
internalizeModule(M, [&GVS](const llvm::GlobalValue &GV) {
272+
return !GV.hasName() || (GVS.count(GV.getName()) == 0);
273+
});
274+
});
275+
} else
276+
Err = Linker::linkModules(*M, std::move(Clone), LM.LinkFlags);
260277

261278
if (Err)
262279
return true;
263280
}
264-
LinkModules.clear();
281+
265282
return false; // success
266283
}
267284

@@ -350,9 +367,9 @@ void BackendConsumer::HandleTranslationUnit(ASTContext &C) {
350367

351368
EmbedBitcode(getModule(), CodeGenOpts, llvm::MemoryBufferRef());
352369

353-
EmitBackendOutput(Diags, HeaderSearchOpts, CodeGenOpts, TargetOpts,
354-
LangOpts, C.getTargetInfo().getDataLayoutString(),
355-
getModule(), Action, FS, std::move(AsmOutStream));
370+
EmitBackendOutput(Diags, HeaderSearchOpts, CodeGenOpts, TargetOpts, LangOpts,
371+
C.getTargetInfo().getDataLayoutString(), getModule(),
372+
Action, FS, std::move(AsmOutStream), this);
356373

357374
Ctx.setDiagnosticHandler(std::move(OldDiagnosticHandler));
358375

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//===-- LinkInModulesPass.cpp - Module Linking pass --------------- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
/// \file
9+
///
10+
/// LinkInModulesPass implementation.
11+
///
12+
//===----------------------------------------------------------------------===//
13+
14+
#include "LinkInModulesPass.h"
15+
#include "BackendConsumer.h"
16+
17+
using namespace llvm;
18+
19+
LinkInModulesPass::LinkInModulesPass(clang::BackendConsumer *BC,
20+
bool ShouldLinkFiles)
21+
: BC(BC), ShouldLinkFiles(ShouldLinkFiles) {}
22+
23+
PreservedAnalyses LinkInModulesPass::run(Module &M, ModuleAnalysisManager &AM) {
24+
25+
if (BC && BC->LinkInModules(&M, ShouldLinkFiles))
26+
report_fatal_error("Bitcode module linking failed, compilation aborted!");
27+
28+
return PreservedAnalyses::all();
29+
}

clang/lib/CodeGen/LinkInModulesPass.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//===-- LinkInModulesPass.h - Module Linking pass ----------------- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
/// \file
9+
///
10+
/// This file provides a pass to link in Modules from a provided
11+
/// BackendConsumer.
12+
///
13+
//===----------------------------------------------------------------------===//
14+
15+
#ifndef LLVM_BITCODE_LINKINMODULESPASS_H
16+
#define LLVM_BITCODE_LINKINMODULESPASS_H
17+
18+
#include "BackendConsumer.h"
19+
#include "llvm/IR/PassManager.h"
20+
21+
namespace llvm {
22+
class Module;
23+
class ModulePass;
24+
class Pass;
25+
26+
/// Create and return a pass that links in Moduels from a provided
27+
/// BackendConsumer to a given primary Module. Note that this pass is designed
28+
/// for use with the legacy pass manager.
29+
class LinkInModulesPass : public PassInfoMixin<LinkInModulesPass> {
30+
clang::BackendConsumer *BC;
31+
bool ShouldLinkFiles;
32+
33+
public:
34+
LinkInModulesPass(clang::BackendConsumer *BC, bool ShouldLinkFiles = true);
35+
36+
PreservedAnalyses run(Module &M, AnalysisManager<Module> &);
37+
static bool isRequired() { return true; }
38+
};
39+
40+
} // namespace llvm
41+
42+
#endif

0 commit comments

Comments
 (0)