Skip to content

Commit cd806d7

Browse files
authored
[LLVM] Add plugin hook for back-ends
Add a mechanism to permit plugins running code between optimizations and the back-end passes. Implement this through the LLVM plug-in mechanism to make permit plugins to be written independently of the front-end. The primary motivation for this point is TPDE-LLVM, which substitutes the LLVM back-end (optionally falling back to it for unsupported IR). We have been distributing a Clang patch; but requiring a custom-build toolchain is impracticable for many users. Front-end adjustments will follow as separate patches. Pull Request: llvm#170846
1 parent 720b003 commit cd806d7

File tree

5 files changed

+115
-30
lines changed

5 files changed

+115
-30
lines changed

llvm/examples/Bye/Bye.cpp

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ using namespace llvm;
1111
static cl::opt<bool> Wave("wave-goodbye", cl::init(false),
1212
cl::desc("wave good bye"));
1313

14+
static cl::opt<bool> LastWords("last-words", cl::init(false),
15+
cl::desc("say last words (suppress codegen)"));
16+
1417
namespace {
1518

1619
bool runBye(Function &F) {
@@ -35,6 +38,37 @@ struct Bye : PassInfoMixin<Bye> {
3538
}
3639
};
3740

41+
void registerPassBuilderCallbacks(PassBuilder &PB) {
42+
PB.registerVectorizerStartEPCallback(
43+
[](llvm::FunctionPassManager &PM, OptimizationLevel Level) {
44+
PM.addPass(Bye());
45+
});
46+
PB.registerPipelineParsingCallback(
47+
[](StringRef Name, llvm::FunctionPassManager &PM,
48+
ArrayRef<llvm::PassBuilder::PipelineElement>) {
49+
if (Name == "goodbye") {
50+
PM.addPass(Bye());
51+
return true;
52+
}
53+
return false;
54+
});
55+
}
56+
57+
bool preCodeGenCallback(Module &M, TargetMachine &, CodeGenFileType CGFT,
58+
raw_pwrite_stream &OS) {
59+
if (LastWords) {
60+
if (CGFT != CodeGenFileType::AssemblyFile) {
61+
// Test error emission.
62+
M.getContext().emitError("last words unsupported for binary output");
63+
return false;
64+
}
65+
OS << "CodeGen Bye\n";
66+
return true; // Suppress remaining compilation pipeline.
67+
}
68+
// Do nothing.
69+
return false;
70+
}
71+
3872
} // namespace
3973

4074
char LegacyBye::ID = 0;
@@ -46,21 +80,7 @@ static RegisterPass<LegacyBye> X("goodbye", "Good Bye World Pass",
4680
/* New PM Registration */
4781
llvm::PassPluginLibraryInfo getByePluginInfo() {
4882
return {LLVM_PLUGIN_API_VERSION, "Bye", LLVM_VERSION_STRING,
49-
[](PassBuilder &PB) {
50-
PB.registerVectorizerStartEPCallback(
51-
[](llvm::FunctionPassManager &PM, OptimizationLevel Level) {
52-
PM.addPass(Bye());
53-
});
54-
PB.registerPipelineParsingCallback(
55-
[](StringRef Name, llvm::FunctionPassManager &PM,
56-
ArrayRef<llvm::PassBuilder::PipelineElement>) {
57-
if (Name == "goodbye") {
58-
PM.addPass(Bye());
59-
return true;
60-
}
61-
return false;
62-
});
63-
}};
83+
registerPassBuilderCallbacks, preCodeGenCallback};
6484
}
6585

6686
#ifndef LLVM_BYE_LINK_INTO_TOOLS

llvm/include/llvm/Passes/PassPlugin.h

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,17 @@
1414
#define LLVM_PASSES_PASSPLUGIN_H
1515

1616
#include "llvm/ADT/StringRef.h"
17+
#include "llvm/Support/CodeGen.h"
1718
#include "llvm/Support/Compiler.h"
1819
#include "llvm/Support/DynamicLibrary.h"
1920
#include "llvm/Support/Error.h"
2021
#include <cstdint>
2122
#include <string>
2223

2324
namespace llvm {
25+
class Module;
2426
class PassBuilder;
27+
class TargetMachine;
2528

2629
/// \macro LLVM_PLUGIN_API_VERSION
2730
/// Identifies the API version understood by this plugin.
@@ -30,14 +33,15 @@ class PassBuilder;
3033
/// against that of the plugin. A mismatch is an error. The supported version
3134
/// will be incremented for ABI-breaking changes to the \c PassPluginLibraryInfo
3235
/// struct, i.e. when callbacks are added, removed, or reordered.
33-
#define LLVM_PLUGIN_API_VERSION 1
36+
#define LLVM_PLUGIN_API_VERSION 2
3437

3538
extern "C" {
3639
/// Information about the plugin required to load its passes
3740
///
3841
/// This struct defines the core interface for pass plugins and is supposed to
39-
/// be filled out by plugin implementors. LLVM-side users of a plugin are
40-
/// expected to use the \c PassPlugin class below to interface with it.
42+
/// be filled out by plugin implementors. Unused function pointers can be set to
43+
/// nullptr. LLVM-side users of a plugin are expected to use the \c PassPlugin
44+
/// class below to interface with it.
4145
struct PassPluginLibraryInfo {
4246
/// The API version understood by this plugin, usually \c
4347
/// LLVM_PLUGIN_API_VERSION
@@ -49,7 +53,14 @@ struct PassPluginLibraryInfo {
4953

5054
/// The callback for registering plugin passes with a \c PassBuilder
5155
/// instance
52-
void (*RegisterPassBuilderCallbacks)(PassBuilder &);
56+
void (*RegisterPassBuilderCallbacks)(PassBuilder &) = nullptr;
57+
58+
/// Callback called before running the back-end passes on the module. The
59+
/// callback can generate code itself by writing the expected output to OS and
60+
/// returning true to prevent the default pipeline and further plugin
61+
/// callbacks from running.
62+
bool (*PreCodeGenCallback)(Module &, TargetMachine &, CodeGenFileType,
63+
raw_pwrite_stream &OS) = nullptr;
5364
};
5465
}
5566

@@ -80,7 +91,17 @@ class PassPlugin {
8091

8192
/// Invoke the PassBuilder callback registration
8293
void registerPassBuilderCallbacks(PassBuilder &PB) const {
83-
Info.RegisterPassBuilderCallbacks(PB);
94+
if (Info.RegisterPassBuilderCallbacks)
95+
Info.RegisterPassBuilderCallbacks(PB);
96+
}
97+
98+
/// Invoke the pre-codegen callback.
99+
bool invokePreCodeGenCallback(Module &M, TargetMachine &TM,
100+
CodeGenFileType CGFT,
101+
raw_pwrite_stream &OS) const {
102+
if (Info.PreCodeGenCallback)
103+
return Info.PreCodeGenCallback(M, TM, CGFT, OS);
104+
return false;
84105
}
85106

86107
private:
@@ -93,6 +114,11 @@ class PassPlugin {
93114
};
94115
}
95116

117+
// The function returns a struct with default initializers.
118+
#ifdef __clang__
119+
#pragma clang diagnostic push
120+
#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
121+
#endif
96122
/// The public entry point for a pass plugin.
97123
///
98124
/// When a plugin is loaded by the driver, it will call this entry point to
@@ -109,5 +135,8 @@ class PassPlugin {
109135
/// ```
110136
extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
111137
llvmGetPassPluginInfo();
138+
#ifdef __clang__
139+
#pragma clang diagnostic pop
140+
#endif
112141

113142
#endif /* LLVM_PASSES_PASSPLUGIN_H */

llvm/lib/Passes/PassPlugin.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,5 @@ Expected<PassPlugin> PassPlugin::Load(const std::string &Filename) {
4545
Twine(LLVM_PLUGIN_API_VERSION) + ".",
4646
inconvertibleErrorCode());
4747

48-
if (!P.Info.RegisterPassBuilderCallbacks)
49-
return make_error<StringError>(Twine("Empty entry callback in plugin '") +
50-
Filename + "'.'",
51-
inconvertibleErrorCode());
52-
5348
return P;
5449
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
; REQUIRES: x86-registered-target
2+
; RUN: llc < %s %loadnewpmbye | FileCheck %s --check-prefix=CHECK-ASM
3+
; RUN: llc < %s %loadnewpmbye -last-words | FileCheck %s --check-prefix=CHECK-ACTIVE
4+
; RUN: not llc %s %loadnewpmbye -last-words -filetype=obj 2>&1 | FileCheck %s --check-prefix=CHECK-ERR
5+
; REQUIRES: plugins, examples
6+
; UNSUPPORTED: target={{.*windows.*}}
7+
; CHECK-ASM: somefunk:
8+
; CHECK-ACTIVE: CodeGen Bye
9+
; CHECK-ERR: error: last words unsupported for binary output
10+
11+
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
12+
target triple = "x86_64-unknown-linux-gnu"
13+
@junk = global i32 0
14+
15+
define ptr @somefunk() {
16+
ret ptr @junk
17+
}
18+

llvm/tools/llc/llc.cpp

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "llvm/MC/MCTargetOptionsCommandFlags.h"
4141
#include "llvm/MC/TargetRegistry.h"
4242
#include "llvm/Pass.h"
43+
#include "llvm/Passes/PassPlugin.h"
4344
#include "llvm/Remarks/HotnessThresholdParser.h"
4445
#include "llvm/Support/CommandLine.h"
4546
#include "llvm/Support/Debug.h"
@@ -213,6 +214,9 @@ static cl::opt<std::string> RemarksFormat(
213214
cl::desc("The format used for serializing remarks (default: YAML)"),
214215
cl::value_desc("format"), cl::init("yaml"));
215216

217+
static cl::list<std::string> PassPlugins("load-pass-plugin",
218+
cl::desc("Load plugin library"));
219+
216220
static cl::opt<bool> EnableNewPassManager(
217221
"enable-new-pm", cl::desc("Enable the new pass manager"), cl::init(false));
218222

@@ -286,8 +290,8 @@ static void setPGOOptions(TargetMachine &TM) {
286290
TM.setPGOOption(PGOOpt);
287291
}
288292

289-
static int compileModule(char **argv, LLVMContext &Context,
290-
std::string &OutputFilename);
293+
static int compileModule(char **argv, SmallVectorImpl<PassPlugin> &,
294+
LLVMContext &Context, std::string &OutputFilename);
291295

292296
[[noreturn]] static void reportError(Twine Msg, StringRef Filename = "") {
293297
SmallString<256> Prefix;
@@ -396,6 +400,14 @@ int main(int argc, char **argv) {
396400
// Initialize debugging passes.
397401
initializeScavengerTestPass(*Registry);
398402

403+
SmallVector<PassPlugin, 1> PluginList;
404+
PassPlugins.setCallback([&](const std::string &PluginPath) {
405+
auto Plugin = PassPlugin::Load(PluginPath);
406+
if (!Plugin)
407+
reportFatalUsageError(Plugin.takeError());
408+
PluginList.emplace_back(Plugin.get());
409+
});
410+
399411
// Register the Target and CPU printer for --version.
400412
cl::AddExtraVersionPrinter(sys::printDefaultTargetAndDetectedCPU);
401413
// Register the target printer for --version.
@@ -447,7 +459,7 @@ int main(int argc, char **argv) {
447459
// Compile the module TimeCompilations times to give better compile time
448460
// metrics.
449461
for (unsigned I = TimeCompilations; I; --I)
450-
if (int RetVal = compileModule(argv, Context, OutputFilename))
462+
if (int RetVal = compileModule(argv, PluginList, Context, OutputFilename))
451463
return RetVal;
452464

453465
if (RemarksFile)
@@ -485,8 +497,8 @@ static bool addPass(PassManagerBase &PM, const char *argv0, StringRef PassName,
485497
return false;
486498
}
487499

488-
static int compileModule(char **argv, LLVMContext &Context,
489-
std::string &OutputFilename) {
500+
static int compileModule(char **argv, SmallVectorImpl<PassPlugin> &PluginList,
501+
LLVMContext &Context, std::string &OutputFilename) {
490502
// Load the module to be compiled...
491503
SMDiagnostic Err;
492504
std::unique_ptr<Module> M;
@@ -707,6 +719,17 @@ static int compileModule(char **argv, LLVMContext &Context,
707719
// flags.
708720
codegen::setFunctionAttributes(CPUStr, FeaturesStr, *M);
709721

722+
for (auto &Plugin : PluginList) {
723+
CodeGenFileType CGFT = codegen::getFileType();
724+
if (Plugin.invokePreCodeGenCallback(*M, *Target, CGFT, Out->os())) {
725+
// TODO: Deduplicate code with below and the NewPMDriver.
726+
if (Context.getDiagHandlerPtr()->HasErrors)
727+
exit(1);
728+
Out->keep();
729+
return 0;
730+
}
731+
}
732+
710733
if (mc::getExplicitRelaxAll() &&
711734
codegen::getFileType() != CodeGenFileType::ObjectFile)
712735
WithColor::warning(errs(), argv[0])

0 commit comments

Comments
 (0)