Index: lld/ELF/Config.h =================================================================== --- lld/ELF/Config.h +++ lld/ELF/Config.h @@ -94,6 +94,7 @@ llvm::StringRef ltoAAPipeline; llvm::StringRef ltoCSProfileFile; llvm::StringRef ltoNewPmPasses; + std::vector ltoNewPmPlugins; llvm::StringRef ltoObjPath; llvm::StringRef ltoSampleProfile; llvm::StringRef mapFile; Index: lld/ELF/Driver.cpp =================================================================== --- lld/ELF/Driver.cpp +++ lld/ELF/Driver.cpp @@ -857,6 +857,7 @@ config->ltoDebugPassManager = args.hasArg(OPT_lto_debug_pass_manager); config->ltoNewPassManager = args.hasArg(OPT_lto_new_pass_manager); config->ltoNewPmPasses = args.getLastArgValue(OPT_lto_newpm_passes); + config->ltoNewPmPlugins = args::getStrings(args, OPT_lto_newpm_load_pass_plugins); config->ltoo = args::getInteger(args, OPT_lto_O, 2); config->ltoObjPath = args.getLastArgValue(OPT_plugin_opt_obj_path_eq); config->ltoPartitions = args::getInteger(args, OPT_lto_partitions, 1); Index: lld/ELF/LTO.cpp =================================================================== --- lld/ELF/LTO.cpp +++ lld/ELF/LTO.cpp @@ -93,6 +93,10 @@ c.MAttrs = getMAttrs(); c.CGOptLevel = args::getCGOptLevel(config->ltoo); + for (StringRef s : config->ltoNewPmPlugins) { + c.OptPlugins.push_back(s); + } + // Set up a custom pipeline if we've been asked to. c.OptPipeline = config->ltoNewPmPasses; c.AAPipeline = config->ltoAAPipeline; Index: lld/ELF/Options.td =================================================================== --- lld/ELF/Options.td +++ lld/ELF/Options.td @@ -473,6 +473,8 @@ HelpText<"Use new pass manager">; def lto_newpm_passes: J<"lto-newpm-passes=">, HelpText<"Passes to run during LTO">; +def lto_newpm_load_pass_plugins: J<"lto-newpm-load-pass-plugin=">, + HelpText<"New style pass plugins to load">; def lto_O: J<"lto-O">, MetaVarName<"">, HelpText<"Optimization level for LTO">; def lto_partitions: J<"lto-partitions=">, Index: lld/test/ELF/lto/ltopasses-load-plugin.ll =================================================================== --- /dev/null +++ lld/test/ELF/lto/ltopasses-load-plugin.ll @@ -0,0 +1,13 @@ +; REQUIRES: x86 +; RUN: llvm-as %s -o %t.o +; RUN: ld.lld %t.o -o %t%shlibext -save-temps --lto-newpm-passes=hello-new-pm \ +; RUN: --lto-newpm-load-pass-plugin=%llvmshlibdir/LLVMHelloNewPM%shlibext 2>&1 | grep Hello + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@junk = global i32 0 + +define i32* @somefunk() { + ret i32* @junk +} Index: lld/test/lit.cfg.py =================================================================== --- lld/test/lit.cfg.py +++ lld/test/lit.cfg.py @@ -45,6 +45,11 @@ llvm_config.add_tool_substitutions(tool_patterns) +# Add site-specific substitutions +config.substitutions.append(('%llvmshlibdir', config.llvm_shlib_dir)) +config.substitutions.append(('%shlibext', config.llvm_shlib_ext)) +config.substitutions.append(('%exeext', config.llvm_exe_ext)) + # LLD tests tend to be flaky on NetBSD, so add some retries. # We don't do this on other platforms because it's slower. if platform.system() in ['NetBSD']: Index: lld/test/lit.site.cfg.py.in =================================================================== --- lld/test/lit.site.cfg.py.in +++ lld/test/lit.site.cfg.py.in @@ -8,6 +8,9 @@ config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" config.llvm_libs_dir = "@LLVM_LIBS_DIR@" config.llvm_libxml2_enabled = @LLVM_LIBXML2_ENABLED@ +config.llvm_shlib_dir = "@SHLIBDIR@" +config.llvm_shlib_ext = "@SHLIBEXT@" +config.llvm_exe_ext = "@EXEEXT@" config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@" config.lld_obj_root = "@LLD_BINARY_DIR@" config.lld_libs_dir = "@LLVM_LIBRARY_OUTPUT_INTDIR@" Index: lld/tools/lld/CMakeLists.txt =================================================================== --- lld/tools/lld/CMakeLists.txt +++ lld/tools/lld/CMakeLists.txt @@ -5,6 +5,7 @@ add_lld_tool(lld lld.cpp ) +export_executable_symbols(lld) target_link_libraries(lld PRIVATE Index: llvm/include/llvm/LTO/Config.h =================================================================== --- llvm/include/llvm/LTO/Config.h +++ llvm/include/llvm/LTO/Config.h @@ -58,6 +58,11 @@ /// Run PGO context sensitive IR instrumentation. bool RunCSIRInstr = false; + /// A list of optional pass plugins to load into the pass manager. + /// These will only be used if a custom OptPipeline has been set, + /// since otherwise no newly defined passes are going to be used anyway. + std::vector OptPlugins; + /// If this field is set, the set of passes run in the middle-end optimizer /// will be the one specified by the string. Only works with the new pass /// manager as the old one doesn't have this ability. @@ -231,7 +236,7 @@ DiagnosticHandlerFunction DiagHandler; }; -} -} +} // namespace lto +} // namespace llvm #endif Index: llvm/lib/LTO/LTOBackend.cpp =================================================================== --- llvm/lib/LTO/LTOBackend.cpp +++ llvm/lib/LTO/LTOBackend.cpp @@ -28,6 +28,7 @@ #include "llvm/MC/SubtargetFeature.h" #include "llvm/Object/ModuleSymbolTable.h" #include "llvm/Passes/PassBuilder.h" +#include "llvm/Passes/PassPlugin.h" #include "llvm/Passes/StandardInstrumentations.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" @@ -169,7 +170,7 @@ PassInstrumentationCallbacks PIC; StandardInstrumentations SI; SI.registerCallbacks(PIC); - PassBuilder PB(TM, PipelineTuningOptions(),PGOOpt, &PIC); + PassBuilder PB(TM, PipelineTuningOptions(), PGOOpt, &PIC); AAManager AA; // Parse a custom AA pipeline if asked to. @@ -224,6 +225,7 @@ } static void runNewPMCustomPasses(Module &Mod, TargetMachine *TM, + std::vector &PassPlugins, std::string PipelineDesc, std::string AAPipelineDesc, bool DisableVerify) { @@ -256,6 +258,19 @@ // Always verify the input. MPM.addPass(VerifierPass()); + // Load requested pass plugins and let them register pass builder callbacks + for (auto &PluginFN : PassPlugins) { + auto PassPlugin = PassPlugin::Load(PluginFN); + if (!PassPlugin) { + errs() << "Failed to load passes from '" << PluginFN + << "'. Request ignored.\n"; + errs() << PassPlugin.takeError() << '\n'; + continue; + } + + PassPlugin->registerPassBuilderCallbacks(PB); + } + // Now, add all the passes we've been requested to. if (auto Err = PB.parsePassPipeline(MPM, PipelineDesc)) report_fatal_error("unable to parse pass pipeline description '" + @@ -302,8 +317,8 @@ const ModuleSummaryIndex *ImportSummary) { // FIXME: Plumb the combined index into the new pass manager. if (!Conf.OptPipeline.empty()) - runNewPMCustomPasses(Mod, TM, Conf.OptPipeline, Conf.AAPipeline, - Conf.DisableVerify); + runNewPMCustomPasses(Mod, TM, Conf.OptPlugins, Conf.OptPipeline, + Conf.AAPipeline, Conf.DisableVerify); else if (Conf.UseNewPM) runNewPMPasses(Conf, Mod, TM, Conf.OptLevel, IsThinLTO, ExportSummary, ImportSummary); @@ -411,7 +426,7 @@ return T; } -} +} // namespace static Error finalizeOptimizationRemarks(std::unique_ptr DiagOutputFile) { @@ -459,7 +474,7 @@ static void dropDeadSymbols(Module &Mod, const GVSummaryMapTy &DefinedGlobals, const ModuleSummaryIndex &Index) { - std::vector DeadGVs; + std::vector DeadGVs; for (auto &GV : Mod.global_values()) if (GlobalValueSummary *GVS = DefinedGlobals.lookup(GV.getGUID())) if (!Index.isGlobalValueLive(GVS)) { Index: llvm/lib/Transforms/CMakeLists.txt =================================================================== --- llvm/lib/Transforms/CMakeLists.txt +++ llvm/lib/Transforms/CMakeLists.txt @@ -6,5 +6,6 @@ add_subdirectory(IPO) add_subdirectory(Vectorize) add_subdirectory(Hello) +add_subdirectory(HelloNewPM) add_subdirectory(ObjCARC) add_subdirectory(Coroutines) Index: llvm/lib/Transforms/HelloNewPM/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/lib/Transforms/HelloNewPM/CMakeLists.txt @@ -0,0 +1,12 @@ +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Core Support) +endif() + +add_llvm_library( LLVMHelloNewPM MODULE BUILDTREE_ONLY + HelloNewPM.cpp + + DEPENDS + intrinsics_gen + PLUGIN_TOOL + opt + ) Index: llvm/lib/Transforms/HelloNewPM/HelloNewPM.cpp =================================================================== --- /dev/null +++ llvm/lib/Transforms/HelloNewPM/HelloNewPM.cpp @@ -0,0 +1,34 @@ +#include "llvm/IR/PassManager.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Passes/PassPlugin.h" +#include "llvm/Support/raw_ostream.h" +using namespace llvm; + +namespace { + +struct HelloNewPMPass : public PassInfoMixin { + PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) { + if (F.hasName()) { + errs() << "Hello " << F.getName() << '\n'; + } + return PreservedAnalyses::all(); + } +}; + +} // namespace + +extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK +llvmGetPassPluginInfo() { + return {LLVM_PLUGIN_API_VERSION, "HelloNewPMPass", "v0.1", + [](PassBuilder &PB) { + PB.registerPipelineParsingCallback( + [](StringRef Name, FunctionPassManager &FPM, + ArrayRef) { + if (Name == "hello-new-pm") { + FPM.addPass(HelloNewPMPass()); + return true; + } + return false; + }); + }}; +} Index: llvm/test/Feature/load_module_newpm.ll =================================================================== --- /dev/null +++ llvm/test/Feature/load_module_newpm.ll @@ -0,0 +1,11 @@ +; RUN: opt < %s -load-pass-plugin=%llvmshlibdir/LLVMHelloNewPM%shlibext --passes=hello-new-pm \ +; RUN: -disable-output 2>&1 | grep Hello +; REQUIRES: plugins +; FIXME: On Cygming, it might fail without building LLVMHello manually. + +@junk = global i32 0 + +define i32* @somefunk() { + ret i32* @junk +} +