Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 23 additions & 10 deletions clang/lib/CodeGen/BackendUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ class EmitAssemblyHelper {
const LangOptions &LangOpts;
llvm::Module *TheModule;
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS;
llvm::SmallVector<llvm::PassPlugin> Plugins;

std::unique_ptr<raw_pwrite_stream> OS;

Expand Down Expand Up @@ -973,16 +974,9 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
}
#endif
}
// Attempt to load pass plugins and register their callbacks with PB.
for (auto &PluginFN : CodeGenOpts.PassPlugins) {
auto PassPlugin = PassPlugin::Load(PluginFN);
if (PassPlugin) {
PassPlugin->registerPassBuilderCallbacks(PB);
} else {
Diags.Report(diag::err_fe_unable_to_load_plugin)
<< PluginFN << toString(PassPlugin.takeError());
}
}
// Register plugin callbacks with PB.
for (auto &Plugin : Plugins)
Plugin.registerPassBuilderCallbacks(PB);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about the other places in-tree that load plugins: Flang, clang-linker-wrapper, libLTO, opt and lld? Do they need adjustment?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need no, they continue to work just fine.

  • LTO: yes, might be good to have at some point in the future.
  • Flang: likewise.
  • clang-linker-wrapper: if there's interest, this can be added. While it currently seems to load plugins, it doesn't seem to call any methods on them.
  • MLIR ModuleToObject::translateToISA also only seems to be used for GPUs and MLIR doesn't do anything related to plugins right now.

opt doesn't call and lld doesn't directly call back-ends.

for (const auto &PassCallback : CodeGenOpts.PassBuilderCallbacks)
PassCallback(PB);
#define HANDLE_EXTENSION(Ext) \
Expand Down Expand Up @@ -1211,6 +1205,14 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
void EmitAssemblyHelper::RunCodegenPipeline(
BackendAction Action, std::unique_ptr<raw_pwrite_stream> &OS,
std::unique_ptr<llvm::ToolOutputFile> &DwoOS) {
// Invoke pre-codegen callback from plugin, which might want to take over the
// entire code generation itself.
for (auto &Plugin : Plugins) {
CodeGenFileType CGFT = getCodeGenFileType(Action);
if (Plugin.invokePreCodeGenCallback(*TheModule, *TM, CGFT, *OS))
return;
}

// We still use the legacy PM to run the codegen pipeline since the new PM
// does not work with the codegen pipeline.
// FIXME: make the new PM work with the codegen pipeline.
Expand Down Expand Up @@ -1274,6 +1276,17 @@ void EmitAssemblyHelper::emitAssembly(BackendAction Action,
// Before executing passes, print the final values of the LLVM options.
cl::PrintOptionValues();

// Attempt to load pass plugins.
for (auto &PluginFN : CodeGenOpts.PassPlugins) {
auto PassPlugin = PassPlugin::Load(PluginFN);
if (PassPlugin) {
Plugins.push_back(std::move(*PassPlugin));
} else {
Diags.Report(diag::err_fe_unable_to_load_plugin)
<< PluginFN << toString(PassPlugin.takeError());
}
}

std::unique_ptr<llvm::ToolOutputFile> ThinLinkOS, DwoOS;
RunOptimizationPipeline(Action, OS, ThinLinkOS, BC);
RunCodegenPipeline(Action, OS, DwoOS);
Expand Down
31 changes: 26 additions & 5 deletions llvm/include/llvm/Passes/PassPlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,17 @@
#define LLVM_PASSES_PASSPLUGIN_H

#include "llvm/ADT/StringRef.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/Error.h"
#include <cstdint>
#include <string>

namespace llvm {
class Module;
class PassBuilder;
class TargetMachine;

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

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

/// The callback for registering plugin passes with a \c PassBuilder
/// instance
void (*RegisterPassBuilderCallbacks)(PassBuilder &);
void (*RegisterPassBuilderCallbacks)(PassBuilder &) = nullptr;

/// Callback called before running the back-end passes on the module. The
/// callback can generate code itself by writing the expected output to OS and
/// returning true to prevent the default pipeline and further plugin
/// callbacks from running.
bool (*PreCodeGenCallback)(Module &, TargetMachine &, CodeGenFileType,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea to have pre/post opt/codegen callbacks. If we ever get the new pass manager in the Codegen pipeline, that would help plugins to handle the pipeline change. Could we generalize this into something like registerPipelinePhaseCallbacks()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would consider this to be out-of-scope for now. Back-end pipelines are, currently, target-specific and any support for plugins modifying these pipelines is likely to need a lot of further discussion (esp. as many back-end passes not really self-contained). There was some discussion on CodeGenPassBuilder to add callbacks for targets earlier this year, but it seems work on that has stalled. Additionally, the proposed plugin API already permits building and running a custom pass manager, so right now there would be no benefit of speculatively building a more generic API.

If there's a desire to add external hooks for CodeGenPassBuilder, I think this should be a separate function similar to the RegisterPassBuilderCallbacks that exists so far.

raw_pwrite_stream &OS) = nullptr;
};
}

Expand Down Expand Up @@ -80,7 +91,17 @@ class PassPlugin {

/// Invoke the PassBuilder callback registration
void registerPassBuilderCallbacks(PassBuilder &PB) const {
Info.RegisterPassBuilderCallbacks(PB);
if (Info.RegisterPassBuilderCallbacks)
Info.RegisterPassBuilderCallbacks(PB);
}

/// Invoke the pre-codegen callback.
bool invokePreCodeGenCallback(Module &M, TargetMachine &TM,
CodeGenFileType CGFT,
raw_pwrite_stream &OS) const {
if (Info.PreCodeGenCallback)
return Info.PreCodeGenCallback(M, TM, CGFT, OS);
return false;
}

private:
Expand Down
5 changes: 0 additions & 5 deletions llvm/lib/Passes/PassPlugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,5 @@ Expected<PassPlugin> PassPlugin::Load(const std::string &Filename) {
Twine(LLVM_PLUGIN_API_VERSION) + ".",
inconvertibleErrorCode());

if (!P.Info.RegisterPassBuilderCallbacks)
return make_error<StringError>(Twine("Empty entry callback in plugin '") +
Filename + "'.'",
inconvertibleErrorCode());

return P;
}
Loading