Skip to content

Commit 24ab9b5

Browse files
author
serge_sans_paille
committed
Generalize the pass registration mechanism used by Polly to any third-party tool
There's quite a lot of references to Polly in the LLVM CMake codebase. However the registration pattern used by Polly could be useful to other external projects: thanks to that mechanism it would be possible to develop LLVM extension without touching the LLVM code base. This patch has two effects: 1. Remove all code specific to Polly in the llvm/clang codebase, replaicing it with a generic mechanism 2. Provide a generic mechanism to register compiler extensions. A compiler extension is similar to a pass plugin, with the notable difference that the compiler extension can be configured to be built dynamically (like plugins) or statically (like regular passes). As a result, people willing to add extra passes to clang/opt can do it using a separate code repo, but still have their pass be linked in clang/opt as built-in passes. Differential Revision: https://reviews.llvm.org/D61446
1 parent 8d7ecc1 commit 24ab9b5

35 files changed

+364
-157
lines changed

clang/lib/CodeGen/BackendUtil.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@
7575
using namespace clang;
7676
using namespace llvm;
7777

78+
#define HANDLE_EXTENSION(Ext) \
79+
llvm::PassPluginLibraryInfo get##Ext##PluginInfo();
80+
#include "llvm/Support/Extension.def"
81+
7882
namespace {
7983

8084
// Default filename used for profile generation.
@@ -1076,6 +1080,9 @@ void EmitAssemblyHelper::EmitAssemblyWithNewPassManager(
10761080
<< PluginFN << toString(PassPlugin.takeError());
10771081
}
10781082
}
1083+
#define HANDLE_EXTENSION(Ext) \
1084+
get##Ext##PluginInfo().RegisterPassBuilderCallbacks(PB);
1085+
#include "llvm/Support/Extension.def"
10791086

10801087
LoopAnalysisManager LAM(CodeGenOpts.DebugPassManager);
10811088
FunctionAnalysisManager FAM(CodeGenOpts.DebugPassManager);

clang/lib/CodeGen/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ add_clang_library(clangCodeGen
9696
TargetInfo.cpp
9797
VarBypassDetector.cpp
9898

99+
ENABLE_PLUGINS
100+
99101
DEPENDS
100102
${codegen_deps}
101103

clang/tools/driver/CMakeLists.txt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,3 @@ if(CLANG_ORDER_FILE AND
122122
set_target_properties(clang PROPERTIES LINK_DEPENDS ${CLANG_ORDER_FILE})
123123
endif()
124124
endif()
125-
126-
if(WITH_POLLY AND LINK_POLLY_INTO_TOOLS)
127-
target_link_libraries(clang PRIVATE Polly)
128-
endif(WITH_POLLY AND LINK_POLLY_INTO_TOOLS)

clang/tools/driver/cc1_main.cpp

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,6 @@ static void LLVMErrorHandler(void *UserData, const std::string &Message,
7272
exit(GenCrashDiag ? 70 : 1);
7373
}
7474

75-
#ifdef LINK_POLLY_INTO_TOOLS
76-
namespace polly {
77-
void initializePollyPasses(llvm::PassRegistry &Registry);
78-
}
79-
#endif
80-
8175
#ifdef CLANG_HAVE_RLIMITS
8276
#if defined(__linux__) && defined(__PIE__)
8377
static size_t getCurrentStackAllocation() {
@@ -203,11 +197,6 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
203197
llvm::InitializeAllAsmPrinters();
204198
llvm::InitializeAllAsmParsers();
205199

206-
#ifdef LINK_POLLY_INTO_TOOLS
207-
llvm::PassRegistry &Registry = *llvm::PassRegistry::getPassRegistry();
208-
polly::initializePollyPasses(Registry);
209-
#endif
210-
211200
// Buffer diagnostics from argument parsing so that we can output them using a
212201
// well formed diagnostic object.
213202
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();

llvm/CMakeLists.txt

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -465,29 +465,6 @@ set(LLVM_LIB_FUZZING_ENGINE "" CACHE PATH
465465
option(LLVM_USE_SPLIT_DWARF
466466
"Use -gsplit-dwarf when compiling llvm." OFF)
467467

468-
option(LLVM_POLLY_LINK_INTO_TOOLS "Statically link Polly into tools (if available)" ON)
469-
option(LLVM_POLLY_BUILD "Build LLVM with Polly" ON)
470-
471-
if (EXISTS ${LLVM_MAIN_SRC_DIR}/tools/polly/CMakeLists.txt)
472-
set(POLLY_IN_TREE TRUE)
473-
elseif(LLVM_EXTERNAL_POLLY_SOURCE_DIR)
474-
set(POLLY_IN_TREE TRUE)
475-
else()
476-
set(POLLY_IN_TREE FALSE)
477-
endif()
478-
479-
if (LLVM_POLLY_BUILD AND POLLY_IN_TREE)
480-
set(WITH_POLLY ON)
481-
else()
482-
set(WITH_POLLY OFF)
483-
endif()
484-
485-
if (LLVM_POLLY_LINK_INTO_TOOLS AND WITH_POLLY)
486-
set(LINK_POLLY_INTO_TOOLS ON)
487-
else()
488-
set(LINK_POLLY_INTO_TOOLS OFF)
489-
endif()
490-
491468
# Define an option controlling whether we should build for 32-bit on 64-bit
492469
# platforms, where supported.
493470
if( CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT WIN32 )
@@ -1112,3 +1089,5 @@ endif()
11121089
if (LLVM_INCLUDE_UTILS AND LLVM_INCLUDE_TOOLS)
11131090
add_subdirectory(utils/llvm-locstats)
11141091
endif()
1092+
1093+
process_llvm_pass_plugins()

llvm/cmake/modules/AddLLVM.cmake

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ endfunction(set_windows_version_resource_properties)
404404
# )
405405
function(llvm_add_library name)
406406
cmake_parse_arguments(ARG
407-
"MODULE;SHARED;STATIC;OBJECT;DISABLE_LLVM_LINK_LLVM_DYLIB;SONAME;NO_INSTALL_RPATH;COMPONENT_LIB"
407+
"MODULE;SHARED;STATIC;OBJECT;DISABLE_LLVM_LINK_LLVM_DYLIB;SONAME;NO_INSTALL_RPATH;COMPONENT_LIB;ENABLE_PLUGINS"
408408
"OUTPUT_NAME;PLUGIN_TOOL;ENTITLEMENTS;BUNDLE_PATH"
409409
"ADDITIONAL_HEADERS;DEPENDS;LINK_COMPONENTS;LINK_LIBS;OBJLIBS"
410410
${ARGN})
@@ -418,6 +418,9 @@ function(llvm_add_library name)
418418
else()
419419
llvm_process_sources(ALL_FILES ${ARG_UNPARSED_ARGUMENTS} ${ARG_ADDITIONAL_HEADERS})
420420
endif()
421+
if(ARG_ENABLE_PLUGINS)
422+
set_property(GLOBAL APPEND PROPERTY LLVM_PLUGIN_TARGETS ${name})
423+
endif()
421424

422425
if(ARG_MODULE)
423426
if(ARG_SHARED OR ARG_STATIC)
@@ -745,7 +748,7 @@ endmacro(add_llvm_library name)
745748

746749
macro(add_llvm_executable name)
747750
cmake_parse_arguments(ARG
748-
"DISABLE_LLVM_LINK_LLVM_DYLIB;IGNORE_EXTERNALIZE_DEBUGINFO;NO_INSTALL_RPATH;SUPPORT_PLUGINS"
751+
"DISABLE_LLVM_LINK_LLVM_DYLIB;IGNORE_EXTERNALIZE_DEBUGINFO;NO_INSTALL_RPATH;SUPPORT_PLUGINS;ENABLE_PLUGINS"
749752
"ENTITLEMENTS;BUNDLE_PATH"
750753
"DEPENDS"
751754
${ARGN})
@@ -832,10 +835,78 @@ macro(add_llvm_executable name)
832835
# API for all shared libaries loaded by this executable.
833836
target_link_libraries(${name} PRIVATE ${LLVM_PTHREAD_LIB})
834837
endif()
838+
if(ARG_ENABLE_PLUGINS)
839+
set_property(GLOBAL APPEND PROPERTY LLVM_PLUGIN_TARGETS ${name})
840+
endif()
835841

836842
llvm_codesign(${name} ENTITLEMENTS ${ARG_ENTITLEMENTS} BUNDLE_PATH ${ARG_BUNDLE_PATH})
837843
endmacro(add_llvm_executable name)
838844

845+
# add_llvm_pass_plugin(name)
846+
# Add ${name} as an llvm plugin.
847+
# If option LLVM_${name_upper}_LINK_INTO_TOOLS is set to ON, the plugin is registered statically.
848+
# Otherwise a pluggable shared library is registered.
849+
function(add_llvm_pass_plugin name)
850+
851+
string(TOUPPER ${name} name_upper)
852+
853+
option(LLVM_${name_upper}_LINK_INTO_TOOLS "Statically link ${name} into tools (if available)" OFF)
854+
855+
# process_llvm_pass_plugins takes care of the actual linking, just create an
856+
# object library as of now
857+
add_llvm_library(${name} OBJECT ${ARGN})
858+
859+
if(LLVM_${name_upper}_LINK_INTO_TOOLS)
860+
target_compile_definitions(${name} PRIVATE LLVM_${name_upper}_LINK_INTO_TOOLS)
861+
set_property(TARGET ${name} APPEND PROPERTY COMPILE_DEFINITIONS LLVM_LINK_INTO_TOOLS)
862+
if (TARGET intrinsics_gen)
863+
add_dependencies(obj.${name} intrinsics_gen)
864+
endif()
865+
endif()
866+
867+
message(STATUS "Registering ${name} as a pass plugin (static build: ${LLVM_${name_upper}_LINK_INTO_TOOLS})")
868+
if(LLVM_${name_upper}_LINK_INTO_TOOLS)
869+
set_property(GLOBAL APPEND PROPERTY LLVM_COMPILE_EXTENSIONS ${name})
870+
endif()
871+
endfunction(add_llvm_pass_plugin)
872+
873+
# Generate X Macro file for extension handling. It provides a
874+
# HANDLE_EXTENSION(extension_namespace, ExtensionProject) call for each extension
875+
# allowing client code to define HANDLE_EXTENSION to have a specific code be run for
876+
# each extension.
877+
#
878+
# Also correctly set lib dependencies between plugins and tools.
879+
function(process_llvm_pass_plugins)
880+
get_property(LLVM_EXTENSIONS GLOBAL PROPERTY LLVM_COMPILE_EXTENSIONS)
881+
file(WRITE "${CMAKE_BINARY_DIR}/include/llvm/Support/Extension.def.tmp" "//extension handlers\n")
882+
foreach(llvm_extension ${LLVM_EXTENSIONS})
883+
string(TOLOWER ${llvm_extension} llvm_extension_lower)
884+
885+
string(TOUPPER ${llvm_extension} llvm_extension_upper)
886+
string(SUBSTRING ${llvm_extension_upper} 0 1 llvm_extension_upper_first)
887+
string(SUBSTRING ${llvm_extension_lower} 1 -1 llvm_extension_lower_tail)
888+
string(CONCAT llvm_extension_project ${llvm_extension_upper_first} ${llvm_extension_lower_tail})
889+
890+
if(LLVM_${llvm_extension_upper}_LINK_INTO_TOOLS)
891+
file(APPEND "${CMAKE_BINARY_DIR}/include/llvm/Support/Extension.def.tmp" "HANDLE_EXTENSION(${llvm_extension_project})\n")
892+
893+
get_property(llvm_plugin_targets GLOBAL PROPERTY LLVM_PLUGIN_TARGETS)
894+
foreach(llvm_plugin_target ${llvm_plugin_targets})
895+
set_property(TARGET ${llvm_plugin_target} APPEND PROPERTY LINK_LIBRARIES ${llvm_extension})
896+
set_property(TARGET ${llvm_plugin_target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${llvm_extension})
897+
endforeach()
898+
else()
899+
add_llvm_library(${llvm_extension_lower} MODULE obj.${llvm_extension_lower})
900+
endif()
901+
902+
endforeach()
903+
file(APPEND "${CMAKE_BINARY_DIR}/include/llvm/Support/Extension.def.tmp" "#undef HANDLE_EXTENSION\n")
904+
905+
# only replace if there's an actual change
906+
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_BINARY_DIR}/include/llvm/Support/Extension.def.tmp" "${CMAKE_BINARY_DIR}/include/llvm/Support/Extension.def")
907+
file(REMOVE "${CMAKE_BINARY_DIR}/include/llvm/Support/Extension.def.tmp")
908+
endfunction()
909+
839910
function(export_executable_symbols target)
840911
if (LLVM_EXPORTED_SYMBOL_FILE)
841912
# The symbol file should contain the symbols we want the executable to

llvm/docs/WritingAnLLVMPass.rst

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,6 +1175,51 @@ implement ``releaseMemory`` to, well, release the memory allocated to maintain
11751175
this internal state. This method is called after the ``run*`` method for the
11761176
class, before the next call of ``run*`` in your pass.
11771177

1178+
Building pass plugins
1179+
=====================
1180+
1181+
As an alternative to using ``PLUGIN_TOOL``, LLVM provides a mechanism to
1182+
automatically register pass plugins within ``clang``, ``opt`` and ``bugpoint``.
1183+
One first needs to create an independent project and add it to either ``tools/``
1184+
or, using the MonoRepo layout, at the root of the repo alongside other projects.
1185+
This project must contain the following minimal ``CMakeLists.txt``:
1186+
1187+
.. code-block:: cmake
1188+
1189+
add_llvm_pass_plugin(Name source0.cpp)
1190+
1191+
The pass must provide two entry points for the new pass manager, one for static
1192+
registration and one for dynamically loaded plugins:
1193+
1194+
- ``llvm::PassPluginLibraryInfo get##Name##PluginInfo();``
1195+
- ``extern "C" ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() LLVM_ATTRIBUTE_WEAK;``
1196+
1197+
Pass plugins are compiled and link dynamically by default, but it's
1198+
possible to set the following variables to change this behavior:
1199+
1200+
- ``LLVM_${NAME}_LINK_INTO_TOOLS``, when set to ``ON``, turns the project into
1201+
a statically linked extension
1202+
1203+
1204+
When building a tool that uses the new pass manager, one can use the following snippet to
1205+
include statically linked pass plugins:
1206+
1207+
.. code-block:: c++
1208+
1209+
// fetch the declaration
1210+
#define HANDLE_EXTENSION(Ext) llvm::PassPluginLibraryInfo get##Ext##PluginInfo();
1211+
#include "llvm/Support/Extension.def"
1212+
1213+
[...]
1214+
1215+
// use them, PB is an llvm::PassBuilder instance
1216+
#define HANDLE_EXTENSION(Ext) get##Ext##PluginInfo().RegisterPassBuilderCallbacks(PB);
1217+
#include "llvm/Support/Extension.def"
1218+
1219+
1220+
1221+
1222+
11781223
Registering dynamically loaded passes
11791224
=====================================
11801225

llvm/examples/Bye/Bye.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#include "llvm/IR/Function.h"
2+
#include "llvm/IR/LegacyPassManager.h"
3+
#include "llvm/Pass.h"
4+
#include "llvm/Passes/PassBuilder.h"
5+
#include "llvm/Passes/PassPlugin.h"
6+
#include "llvm/Support/raw_ostream.h"
7+
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
8+
9+
using namespace llvm;
10+
11+
static cl::opt<bool> Wave("wave-goodbye", cl::init(false),
12+
cl::desc("wave good bye"));
13+
14+
namespace {
15+
16+
bool runBye(Function &F) {
17+
if (Wave) {
18+
errs() << "Bye: ";
19+
errs().write_escaped(F.getName()) << '\n';
20+
}
21+
return false;
22+
}
23+
24+
struct LegacyBye : public FunctionPass {
25+
static char ID;
26+
LegacyBye() : FunctionPass(ID) {}
27+
bool runOnFunction(Function &F) override { return runBye(F); }
28+
};
29+
30+
struct Bye : PassInfoMixin<Bye> {
31+
PreservedAnalyses run(Function &F, FunctionAnalysisManager &) {
32+
if (!runBye(F))
33+
return PreservedAnalyses::all();
34+
return PreservedAnalyses::none();
35+
}
36+
};
37+
38+
} // namespace
39+
40+
char LegacyBye::ID = 0;
41+
42+
static RegisterPass<LegacyBye> X("goodbye", "Good Bye World Pass",
43+
false /* Only looks at CFG */,
44+
false /* Analysis Pass */);
45+
46+
/* Legacy PM Registration */
47+
static llvm::RegisterStandardPasses RegisterBye(
48+
llvm::PassManagerBuilder::EP_EarlyAsPossible,
49+
[](const llvm::PassManagerBuilder &Builder,
50+
llvm::legacy::PassManagerBase &PM) { PM.add(new LegacyBye()); });
51+
52+
/* New PM Registration */
53+
llvm::PassPluginLibraryInfo getByePluginInfo() {
54+
return {LLVM_PLUGIN_API_VERSION, "Bye", LLVM_VERSION_STRING,
55+
[](PassBuilder &PB) {
56+
PB.registerVectorizerStartEPCallback(
57+
[](llvm::FunctionPassManager &PM,
58+
llvm::PassBuilder::OptimizationLevel Level) {
59+
PM.addPass(Bye());
60+
});
61+
}};
62+
}
63+
64+
#ifndef LLVM_BYE_LINK_INTO_TOOLS
65+
extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
66+
llvmGetPassPluginInfo() {
67+
return getByePluginInfo();
68+
}
69+
#endif

llvm/examples/Bye/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
add_llvm_pass_plugin(Bye Bye.cpp)
2+
if (LLVM_LINK_LLVM_DYLIB)
3+
target_link_libraries(Bye PUBLIC LLVM)
4+
else()
5+
target_link_libraries(Bye
6+
PUBLIC
7+
LLVMSupport
8+
LLVMCore
9+
LLVMipo
10+
LLVMPasses
11+
)
12+
endif()
13+

llvm/examples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ add_subdirectory(LLJITExamples)
66
add_subdirectory(Kaleidoscope)
77
add_subdirectory(ModuleMaker)
88
add_subdirectory(SpeculativeJIT)
9+
add_subdirectory(Bye)
910

1011
if(LLVM_ENABLE_EH AND (NOT WIN32) AND (NOT "${LLVM_NATIVE_ARCH}" STREQUAL "ARM"))
1112
add_subdirectory(ExceptionDemo)

llvm/include/llvm/Config/llvm-config.h.cmake

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@
1717
/* Define if LLVM_ENABLE_DUMP is enabled */
1818
#cmakedefine LLVM_ENABLE_DUMP
1919

20-
/* Define if we link Polly to the tools */
21-
#cmakedefine LINK_POLLY_INTO_TOOLS
22-
2320
/* Target triple LLVM will generate code for by default */
2421
#cmakedefine LLVM_DEFAULT_TARGET_TRIPLE "${LLVM_DEFAULT_TARGET_TRIPLE}"
2522

llvm/test/Feature/load_extension.ll

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
; RUN: opt %s %loadbye -goodbye -wave-goodbye -disable-output | FileCheck %s
2+
; REQUIRES: plugins, examples
3+
; CHECK: Bye
4+
5+
@junk = global i32 0
6+
7+
define i32* @somefunk() {
8+
ret i32* @junk
9+
}
10+

0 commit comments

Comments
 (0)