Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -2647,7 +2647,7 @@ MetaVarName<"-">, HelpText<"Pass to plugin ">; def fpass_plugin_EQ : Joined<["-"], "fpass-plugin=">, - Group, Flags<[CC1Option]>, MetaVarName<"">, + Group, Flags<[CC1Option,FlangOption,FC1Option]>, MetaVarName<"">, HelpText<"Load pass plugin from a dynamic shared object file (only with new pass manager).">, MarshallingInfoStringVector>; defm preserve_as_comments : BoolFOption<"preserve-as-comments", Index: clang/lib/Driver/ToolChains/Flang.cpp =================================================================== --- clang/lib/Driver/ToolChains/Flang.cpp +++ clang/lib/Driver/ToolChains/Flang.cpp @@ -55,7 +55,7 @@ Args.AddAllArgs(CmdArgs, {options::OPT_module_dir, options::OPT_fdebug_module_writer, options::OPT_fintrinsic_modules_path, options::OPT_pedantic, - options::OPT_std_EQ, options::OPT_W_Joined}); + options::OPT_std_EQ, options::OPT_W_Joined, options::OPT_fpass_plugin_EQ}); } void Flang::ConstructJob(Compilation &C, const JobAction &JA, Index: flang/examples/CMakeLists.txt =================================================================== --- flang/examples/CMakeLists.txt +++ flang/examples/CMakeLists.txt @@ -13,3 +13,4 @@ add_subdirectory(PrintFlangFunctionNames) add_subdirectory(FlangOmpReport) +add_subdirectory(PrintFunctionNamesLLVMPass) Index: flang/examples/PrintFunctionNamesLLVMPass/CMakeLists.txt =================================================================== --- /dev/null +++ flang/examples/PrintFunctionNamesLLVMPass/CMakeLists.txt @@ -0,0 +1,4 @@ +add_llvm_library(flangPrintFunctionNamesLLVMPass + MODULE + PrintFunctionNamesLLVMPass.cpp +) Index: flang/examples/PrintFunctionNamesLLVMPass/PrintFunctionNamesLLVMPass.cpp =================================================================== --- /dev/null +++ flang/examples/PrintFunctionNamesLLVMPass/PrintFunctionNamesLLVMPass.cpp @@ -0,0 +1,59 @@ +//===-- PrintFunctionNamesLLVMPass.cpp ------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Small example an LLVM pass to count and print the names of defined functions +// and external (library) functions in the LLVM IR. The intention is simply to +// demonstrate how to create an out-of-tree LLVM pass and then load it using +// -fpass-plugin. This will also allow the -fpass-plugin option to be tested in +// flang. +// +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include +#include + +using namespace llvm; + +class PrintFunctionNamesPass : public PassInfoMixin { +public: + PreservedAnalyses run(Module &M, ModuleAnalysisManager &) { + unsigned defined = 0, declared = 0; + for (Function &f : M.functions()) + if (f.size()) + ++defined; + else + ++declared; + + outs() << " ==== Defined: " << defined << " ====\n"; + for (Function &f : M.functions()) + if (f.size()) + outs() << f.getName() << "\n"; + outs() << "\n"; + + outs() << " ==== Declared: " << declared << " ====\n"; + for (Function &f : M.functions()) + if (not f.size()) + outs() << f.getName() << "\n"; + + return PreservedAnalyses::all(); + } +}; + +static void registerPass(ModulePassManager &mpm, OptimizationLevel) { + mpm.addPass(PrintFunctionNamesPass()); +} + +extern "C" PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK llvmGetPassPluginInfo() { + return {LLVM_PLUGIN_API_VERSION, "PrintFunctionNamesPass", + LLVM_VERSION_STRING, [](PassBuilder &PB) { + PB.registerPipelineStartEPCallback(registerPass); + }}; +} Index: flang/include/flang/Frontend/CodeGenOptions.h =================================================================== --- flang/include/flang/Frontend/CodeGenOptions.h +++ flang/include/flang/Frontend/CodeGenOptions.h @@ -43,6 +43,9 @@ /// to the LLVM backend. class CodeGenOptions : public CodeGenOptionsBase { +public: + std::vector LLVMPassPlugins; + public: CodeGenOptions(); }; Index: flang/lib/Frontend/CompilerInvocation.cpp =================================================================== --- flang/lib/Frontend/CompilerInvocation.cpp +++ flang/lib/Frontend/CompilerInvocation.cpp @@ -107,6 +107,9 @@ if (args.hasFlag(clang::driver::options::OPT_fdebug_pass_manager, clang::driver::options::OPT_fno_debug_pass_manager, false)) opts.DebugPassManager = 1; + + for (auto *a : args.filtered(clang::driver::options::OPT_fpass_plugin_EQ)) + opts.LLVMPassPlugins.push_back(a->getValue()); } /// Parses all target input arguments and populates the target Index: flang/lib/Frontend/FrontendActions.cpp =================================================================== --- flang/lib/Frontend/FrontendActions.cpp +++ flang/lib/Frontend/FrontendActions.cpp @@ -46,6 +46,7 @@ #include "llvm/IRReader/IRReader.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Passes/PassBuilder.h" +#include "llvm/Passes/PassPlugin.h" #include "llvm/Passes/StandardInstrumentations.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/SourceMgr.h" @@ -647,6 +648,7 @@ void CodeGenAction::runOptimizationPipeline(llvm::raw_pwrite_stream &os) { auto opts = getInstance().getInvocation().getCodeGenOpts(); + auto &diags = getInstance().getDiagnostics(); llvm::OptimizationLevel level = mapToLevel(opts); // Create the analysis managers. @@ -663,6 +665,17 @@ si.registerCallbacks(pic, &fam); llvm::PassBuilder pb(tm.get(), pto, pgoOpt, &pic); + // Attempt to load pass plugins and register their callbacks with PB. + for (auto &pluginFile : opts.LLVMPassPlugins) { + auto passPlugin = llvm::PassPlugin::Load(pluginFile); + if (passPlugin) { + passPlugin->registerPassBuilderCallbacks(pb); + } else { + diags.Report(clang::diag::err_fe_unable_to_load_plugin) + << pluginFile << passPlugin.takeError(); + } + } + // Register all the basic analyses with the managers. pb.registerModuleAnalyses(mam); pb.registerCGSCCAnalyses(cgam); Index: flang/test/Driver/driver-help-hidden.f90 =================================================================== --- flang/test/Driver/driver-help-hidden.f90 +++ flang/test/Driver/driver-help-hidden.f90 @@ -44,6 +44,7 @@ ! CHECK-NEXT: -fno-integrated-as Disable the integrated assembler ! CHECK-NEXT: -fopenacc Enable OpenACC ! CHECK-NEXT: -fopenmp Parse OpenMP pragmas and generate parallel code. +! CHECK-NEXT: -fpass-plugin= Load pass plugin from a dynamic shared object file (only with new pass manager). ! CHECK-NEXT: -fxor-operator Enable .XOR. as a synonym of .NEQV. ! CHECK-NEXT: -help Display available options ! CHECK-NEXT: -I Add directory to the end of the list of include search paths Index: flang/test/Driver/driver-help.f90 =================================================================== --- flang/test/Driver/driver-help.f90 +++ flang/test/Driver/driver-help.f90 @@ -42,6 +42,7 @@ ! HELP-NEXT: -fno-integrated-as Disable the integrated assembler ! HELP-NEXT: -fopenacc Enable OpenACC ! HELP-NEXT: -fopenmp Parse OpenMP pragmas and generate parallel code. +! HELP-NEXT: -fpass-plugin= Load pass plugin from a dynamic shared object file (only with new pass manager). ! HELP-NEXT: -fxor-operator Enable .XOR. as a synonym of .NEQV. ! HELP-NEXT: -help Display available options ! HELP-NEXT: -I Add directory to the end of the list of include search paths @@ -119,6 +120,7 @@ ! HELP-FC1-NEXT: -fno-reformat Dump the cooked character stream in -E mode ! HELP-FC1-NEXT: -fopenacc Enable OpenACC ! HELP-FC1-NEXT: -fopenmp Parse OpenMP pragmas and generate parallel code. +! HELP-FC1-NEXT: -fpass-plugin= Load pass plugin from a dynamic shared object file (only with new pass manager). ! HELP-FC1-NEXT: -fxor-operator Enable .XOR. as a synonym of .NEQV. ! HELP-FC1-NEXT: -help Display available options ! HELP-FC1-NEXT: -init-only Only execute frontend initialization Index: flang/test/Driver/pass-plugin-not-found.f90 =================================================================== --- /dev/null +++ flang/test/Driver/pass-plugin-not-found.f90 @@ -0,0 +1,8 @@ +! Check the correct error diagnostic is reported when a pass plugin shared object isn't found + +! REQUIRES: plugins, shell + +! RUN: not %flang -fpass-plugin=X.Y %s 2>&1 | FileCheck %s --check-prefix=ERROR +! RUN: not %flang_fc1 -emit-llvm -o /dev/null -fpass-plugin=X.Y %s 2>&1 | FileCheck %s --check-prefix=ERROR + +! ERROR: error: unable to load plugin 'X.Y': 'Could not load library 'X.Y': X.Y: cannot open shared object file: No such file or directory' Index: flang/test/Examples/print-fns-definitions.f90 =================================================================== --- flang/test/Examples/print-fns-definitions.f90 +++ flang/test/Examples/print-fns-definitions.f90 @@ -1,19 +1,47 @@ +! --------------------------- +! FLANG plugin +! --------------------------- + ! Check the Flang Print Function Names example plugin prints and counts function/subroutine definitions ! This includes internal and external Function/Subroutines, but not Statement Functions ! This requires that the examples are built (FLANG_BUILD_EXAMPLES=ON) to access flangPrintFunctionNames.so ! REQUIRES: plugins, examples, shell -! RUN: %flang_fc1 -load %llvmshlibdir/flangPrintFunctionNames%pluginext -plugin print-fns %s 2>&1 | FileCheck %s +! RUN: %flang_fc1 -load %llvmshlibdir/flangPrintFunctionNames%pluginext -plugin print-fns %s 2>&1 | FileCheck %s --check-prefix=PLUGIN + +! PLUGIN: Function: external_func1 +! PLUGIN-NEXT: Function: external_func2 +! PLUGIN-NEXT: Subroutine: external_subr +! PLUGIN-NEXT: Function: internal_func +! PLUGIN-NEXT: Subroutine: internal_subr +! PLUGIN-EMPTY: +! PLUGIN-NEXT: ==== Functions: 3 ==== +! PLUGIN-NEXT: ==== Subroutines: 2 ==== + +! --------------------------- +! FLANG LLVM pass plugin +! --------------------------- + +! Check the Print Function Names example LLVM pass plugin that prints and counts defined/declared functions in LLVM-IR. +! This requires that the examples are built (FLANG_BUILD_EXAMPLES=ON) to access flangPrintFunctionNamesLLVMPass.so + +! REQUIRES: plugins, examples, shell + +! RUN: %flang_fc1 -emit-llvm -o /dev/null -fpass-plugin=%llvmshlibdir/flangPrintFunctionNamesLLVMPass%pluginext %s 2>&1 | FileCheck %s --check-prefix=PASS + +! PASS: ==== Defined: 6 ==== +! PASS-NEXT: external_func1 +! PASS-NEXT: external_func2 +! PASS-NEXT: external_subr +! PASS-NEXT: _QQmain +! PASS-NEXT: _QFPinternal_func +! PASS-NEXT: _QFPinternal_subr +! PASS-EMPTY: +! PASS-NEXT: ==== Declared: 2 ==== +! PASS-NEXT: malloc +! PASS-NEXT: free -! CHECK: Function: external_func1 -! CHECK-NEXT: Function: external_func2 -! CHECK-NEXT: Subroutine: external_subr -! CHECK-NEXT: Function: internal_func -! CHECK-NEXT: Subroutine: internal_subr -! CHECK-EMPTY: -! CHECK-NEXT: ==== Functions: 3 ==== -! CHECK-NEXT: ==== Subroutines: 2 ==== function external_func1() end function