diff --git a/llvm/include/llvm/Analysis/InlineAdvisor.h b/llvm/include/llvm/Analysis/InlineAdvisor.h --- a/llvm/include/llvm/Analysis/InlineAdvisor.h +++ b/llvm/include/llvm/Analysis/InlineAdvisor.h @@ -302,6 +302,11 @@ "The plugin advisor factory should not be a null pointer."); } + // This is needed for unit tests where plugins may be deregistered. + // Otherwise the static varaible will stay true and if createAdvisor + // is called it will try to load a non-registered advisor and crash. + ~PluginInlineAdvisorAnalysis() { HasBeenRegistered = false; } + struct Result { AdvisorFactory Factory; }; diff --git a/llvm/include/llvm/Analysis/InlineOrder.h b/llvm/include/llvm/Analysis/InlineOrder.h --- a/llvm/include/llvm/Analysis/InlineOrder.h +++ b/llvm/include/llvm/Analysis/InlineOrder.h @@ -32,7 +32,91 @@ }; std::unique_ptr>> -getInlineOrder(FunctionAnalysisManager &FAM, const InlineParams &Params); +getInlineOrder(FunctionAnalysisManager &FAM, const InlineParams &Params, + ModuleAnalysisManager &MAM, Module &M); + +/// Used for dynamically registering InlineOrder as plugins +/// +/// An inline order plugin adds a new inline order at runtime by registering +/// an instance of PluginInlineOrderAnalysis in the current +/// ModuleAnalysisManager. For example, the following code dynamically registers +/// the default inline order: +/// +/// namespace { +/// +/// std::unique_ptr>> +/// DefaultInlineOrderFactory(FunctionAnalysisManager &FAM, +/// const InlineParams &Params, +/// ModuleAnalysisManager &MAM, Module &M) { +/// llvm::PluginInlineOrderAnalysis::HasBeenRegistered = false; +/// auto DefaultInlineOrder = +/// std::unique_ptr>>( +/// getInlineOrder(FAM, Params, MAM, M)); +/// llvm::PluginInlineOrderAnalysis::HasBeenRegistered = true; +/// return DefaultInlineOrder; +/// } +/// +/// struct DefaultDynamicInlineOrder : PassInfoMixin +/// { +/// PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM) { +/// PluginInlineOrderAnalysis DA(DefaultInlineOrderFactory); +/// MAM.registerPass([&] { return DA; }); +/// return PreservedAnalyses::all(); +/// } +/// }; +/// +/// } // namespace +/// +/// extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo +/// llvmGetPassPluginInfo() { +/// return {LLVM_PLUGIN_API_VERSION, "DefaultDynamicInlineOrder", +/// LLVM_VERSION_STRING, +/// [](PassBuilder &PB) { +/// PB.registerPipelineStartEPCallback( +/// [](ModulePassManager &MPM, OptimizationLevel Level) { +/// MPM.addPass(DefaultDynamicInlineOrder()); +/// }); +/// }}; +/// } +/// +/// A plugin must implement an InlineOrderFactory and register it with a +/// PluginInlineOrderAnalysis to the provided ModuleanAlysisManager. +/// +/// If such a plugin has been registered +/// llvm::getInlineOrder will return the dynamically loaded inliner order. +/// +class PluginInlineOrderAnalysis + : public AnalysisInfoMixin { +public: + static AnalysisKey Key; + static bool HasBeenRegistered; + + typedef std::unique_ptr>> ( + *InlineOrderFactory)(FunctionAnalysisManager &FAM, + const InlineParams &Params, + ModuleAnalysisManager &MAM, Module &M); + + PluginInlineOrderAnalysis(InlineOrderFactory Factory) : Factory(Factory) { + HasBeenRegistered = true; + assert(Factory != nullptr && + "The plugin inline order factory should not be a null pointer."); + } + + // This is needed for unit tests where plugins may be deregistered. + // Otherwise the static varaible will stay true and if getInlineOrder + // is called it will try to load a non-registered advisor and crash. + ~PluginInlineOrderAnalysis() { HasBeenRegistered = false; } + + struct Result { + InlineOrderFactory Factory; + }; + + Result run(Module &, ModuleAnalysisManager &) { return {Factory}; } + Result getResult() { return {Factory}; } + +private: + InlineOrderFactory Factory; +}; } // namespace llvm #endif // LLVM_ANALYSIS_INLINEORDER_H diff --git a/llvm/lib/Analysis/InlineOrder.cpp b/llvm/lib/Analysis/InlineOrder.cpp --- a/llvm/lib/Analysis/InlineOrder.cpp +++ b/llvm/lib/Analysis/InlineOrder.cpp @@ -17,6 +17,7 @@ #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/PluginLoader.h" using namespace llvm; @@ -33,8 +34,7 @@ "Use inline cost priority."), clEnumValN(InlinePriorityMode::CostBenefit, "cost-benefit", "Use cost-benefit ratio."), - clEnumValN(InlinePriorityMode::ML, "ml", - "Use ML."))); + clEnumValN(InlinePriorityMode::ML, "ml", "Use ML."))); static cl::opt ModuleInlinerTopPriorityThreshold( "moudle-inliner-top-priority-threshold", cl::Hidden, cl::init(0), @@ -281,8 +281,17 @@ } // namespace +AnalysisKey llvm::PluginInlineOrderAnalysis::Key; +bool llvm::PluginInlineOrderAnalysis::HasBeenRegistered; + std::unique_ptr>> -llvm::getInlineOrder(FunctionAnalysisManager &FAM, const InlineParams &Params) { +llvm::getInlineOrder(FunctionAnalysisManager &FAM, const InlineParams &Params, + ModuleAnalysisManager &MAM, Module &M) { + if (llvm::PluginInlineOrderAnalysis::HasBeenRegistered) { + LLVM_DEBUG(dbgs() << " Current used priority: plugin ---- \n"); + return MAM.getResult(M).Factory(FAM, Params, MAM, M); + } + switch (UseInlinePriority) { case InlinePriorityMode::Size: LLVM_DEBUG(dbgs() << " Current used priority: Size priority ---- \n"); @@ -295,10 +304,10 @@ case InlinePriorityMode::CostBenefit: LLVM_DEBUG( dbgs() << " Current used priority: cost-benefit priority ---- \n"); - return std::make_unique>(FAM, Params); + return std::make_unique>(FAM, + Params); case InlinePriorityMode::ML: - LLVM_DEBUG( - dbgs() << " Current used priority: ML priority ---- \n"); + LLVM_DEBUG(dbgs() << " Current used priority: ML priority ---- \n"); return std::make_unique>(FAM, Params); } return nullptr; diff --git a/llvm/lib/Transforms/IPO/ModuleInliner.cpp b/llvm/lib/Transforms/IPO/ModuleInliner.cpp --- a/llvm/lib/Transforms/IPO/ModuleInliner.cpp +++ b/llvm/lib/Transforms/IPO/ModuleInliner.cpp @@ -138,7 +138,7 @@ // // TODO: Here is a huge amount duplicate code between the module inliner and // the SCC inliner, which need some refactoring. - auto Calls = getInlineOrder(FAM, Params); + auto Calls = getInlineOrder(FAM, Params, MAM, M); assert(Calls != nullptr && "Expected an initialized InlineOrder"); // Populate the initial list of calls in this module. diff --git a/llvm/unittests/Analysis/CMakeLists.txt b/llvm/unittests/Analysis/CMakeLists.txt --- a/llvm/unittests/Analysis/CMakeLists.txt +++ b/llvm/unittests/Analysis/CMakeLists.txt @@ -39,6 +39,7 @@ MLModelRunnerTest.cpp PhiValuesTest.cpp PluginInlineAdvisorAnalysisTest.cpp + PluginInlineOrderAnalysisTest.cpp ProfileSummaryInfoTest.cpp ScalarEvolutionTest.cpp VectorFunctionABITest.cpp @@ -66,3 +67,4 @@ target_link_libraries(AnalysisTests PRIVATE LLVMTestingSupport) add_subdirectory(InlineAdvisorPlugin) +add_subdirectory(InlineOrderPlugin) diff --git a/llvm/unittests/Analysis/InlineOrderPlugin/CMakeLists.txt b/llvm/unittests/Analysis/InlineOrderPlugin/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/unittests/Analysis/InlineOrderPlugin/CMakeLists.txt @@ -0,0 +1,27 @@ +# The order plugin expects to not link against the Analysis, Support and Core +# libraries, but expects them to exist in the process loading the plugin. This +# doesn't work with DLLs on Windows (where a shared library can't have undefined +# references), so just skip this testcase on Windows. +if (NOT WIN32) + # On AIX, enable run-time linking to allow symbols from the plugins shared + # objects to be properly bound. + if(CMAKE_SYSTEM_NAME STREQUAL "AIX") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-brtl") + endif() + + unset(LLVM_LINK_COMPONENTS) + add_llvm_library(InlineOrderPlugin MODULE BUILDTREE_ONLY InlineOrderPlugin.cpp) + # Put PLUGIN next to the unit test executable. + set_output_directory(InlineOrderPlugin + BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/../ + LIBRARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/../ + ) + set_target_properties(InlineOrderPlugin PROPERTIES FOLDER "Tests") + + export_executable_symbols_for_plugins(AnalysisTests) + # The plugin depends on some of the output files of intrinsics_gen, so make sure + # it is built before the plugin. + add_dependencies(InlineOrderPlugin intrinsics_gen) + add_dependencies(AnalysisTests InlineOrderPlugin) + set_property(TARGET InlineOrderPlugin PROPERTY FOLDER "Tests/UnitTests/AnalysisTests") +endif() \ No newline at end of file diff --git a/llvm/unittests/Analysis/InlineOrderPlugin/InlineOrderPlugin.cpp b/llvm/unittests/Analysis/InlineOrderPlugin/InlineOrderPlugin.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/Analysis/InlineOrderPlugin/InlineOrderPlugin.cpp @@ -0,0 +1,83 @@ +#include "llvm/IR/Function.h" +#include "llvm/IR/Module.h" +#include "llvm/Pass.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Passes/PassPlugin.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/raw_ostream.h" + +#include "llvm/Analysis/InlineOrder.h" + +using namespace llvm; + +namespace { + +class NoFooInlineOrder : public InlineOrder> { +public: + NoFooInlineOrder(FunctionAnalysisManager &FAM, const InlineParams &Params, + ModuleAnalysisManager &MAM, Module &M) { + // we temporarily set the PluginInlineOrderAnalysis::HasBeenRegistered + // as false to disable the plugin loading itself and renable it later + llvm::PluginInlineOrderAnalysis::HasBeenRegistered = false; + DefaultInlineOrder = getInlineOrder(FAM, Params, MAM, M); + llvm::PluginInlineOrderAnalysis::HasBeenRegistered = true; + } + size_t size() override { return DefaultInlineOrder->size(); } + void push(const std::pair &Elt) override { + // we ignore call that include foo in the name + if (Elt.first->getCalledFunction()->getName() == "foo") { + DefaultInlineOrder->push(Elt); + } + } + std::pair pop() override { + return DefaultInlineOrder->pop(); + } + void erase_if(function_ref)> Pred) override { + DefaultInlineOrder->erase_if(Pred); + } + +private: + std::unique_ptr>> DefaultInlineOrder; +}; + +std::unique_ptr>> +DefaultInlineOrderFactory(FunctionAnalysisManager &FAM, + const InlineParams &Params, + ModuleAnalysisManager &MAM, Module &M) { + return std::make_unique(FAM, Params, MAM, M); +} + +struct DefaultDynamicInlineOrder : PassInfoMixin { + PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM) { + PluginInlineOrderAnalysis DA(DefaultInlineOrderFactory); + MAM.registerPass([&] { return DA; }); + return PreservedAnalyses::all(); + } +}; + +} // namespace + +/* New PM Registration */ +llvm::PassPluginLibraryInfo getDefaultDynamicInlineOrderPluginInfo() { + return {LLVM_PLUGIN_API_VERSION, "DynamicDefaultInlineOrder", + LLVM_VERSION_STRING, [](PassBuilder &PB) { + PB.registerPipelineStartEPCallback( + [](ModulePassManager &MPM, OptimizationLevel Level) { + MPM.addPass(DefaultDynamicInlineOrder()); + }); + PB.registerPipelineParsingCallback( + [](StringRef Name, ModulePassManager &PM, + ArrayRef InnerPipeline) { + if (Name == "dynamic-inline-order") { + PM.addPass(DefaultDynamicInlineOrder()); + return true; + } + return false; + }); + }}; +} + +extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo +llvmGetPassPluginInfo() { + return getDefaultDynamicInlineOrderPluginInfo(); +} diff --git a/llvm/unittests/Analysis/PluginInlineAdvisorAnalysisTest.cpp b/llvm/unittests/Analysis/PluginInlineAdvisorAnalysisTest.cpp --- a/llvm/unittests/Analysis/PluginInlineAdvisorAnalysisTest.cpp +++ b/llvm/unittests/Analysis/PluginInlineAdvisorAnalysisTest.cpp @@ -10,6 +10,8 @@ namespace llvm { +namespace { + void anchor() {} static std::string libPath(const std::string Name = "InlineAdvisorPlugin") { @@ -256,6 +258,8 @@ } )"}; +} // namespace + // check that loading a plugin works // the plugin being loaded acts identically to the default inliner TEST(PluginInlineAdvisorTest, PluginLoad) { diff --git a/llvm/unittests/Analysis/PluginInlineAdvisorAnalysisTest.cpp b/llvm/unittests/Analysis/PluginInlineOrderAnalysisTest.cpp copy from llvm/unittests/Analysis/PluginInlineAdvisorAnalysisTest.cpp copy to llvm/unittests/Analysis/PluginInlineOrderAnalysisTest.cpp --- a/llvm/unittests/Analysis/PluginInlineAdvisorAnalysisTest.cpp +++ b/llvm/unittests/Analysis/PluginInlineOrderAnalysisTest.cpp @@ -4,18 +4,23 @@ #include "llvm/Passes/PassBuilder.h" #include "llvm/Passes/PassPlugin.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/RandomNumberGenerator.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Testing/Support/Error.h" #include "gtest/gtest.h" +#include "llvm/Analysis/InlineOrder.h" + namespace llvm { -void anchor() {} +namespace { + +static void anchor() {} -static std::string libPath(const std::string Name = "InlineAdvisorPlugin") { +std::string libPath(const std::string Name = "InlineOrderPlugin") { const auto &Argvs = testing::internal::GetArgvs(); const char *Argv0 = - Argvs.size() > 0 ? Argvs[0].c_str() : "PluginInlineAdvisorAnalysisTest"; + Argvs.size() > 0 ? Argvs[0].c_str() : "PluginInlineOrderAnalysisTest"; void *Ptr = (void *)(intptr_t)anchor; std::string Path = sys::fs::getMainExecutable(Argv0, Ptr); llvm::SmallString<256> Buf{sys::path::parent_path(Path)}; @@ -23,26 +28,6 @@ return std::string(Buf.str()); } -// Example of a custom InlineAdvisor that only inlines calls to functions called -// "foo". -class FooOnlyInlineAdvisor : public InlineAdvisor { -public: - FooOnlyInlineAdvisor(Module &M, FunctionAnalysisManager &FAM, - InlineParams Params, InlineContext IC) - : InlineAdvisor(M, FAM, IC) {} - - std::unique_ptr getAdviceImpl(CallBase &CB) override { - if (CB.getCalledFunction()->getName() == "foo") - return std::make_unique(this, CB, getCallerORE(CB), true); - return std::make_unique(this, CB, getCallerORE(CB), false); - } -}; - -static InlineAdvisor *fooOnlyFactory(Module &M, FunctionAnalysisManager &FAM, - InlineParams Params, InlineContext IC) { - return new FooOnlyInlineAdvisor(M, FAM, Params, IC); -} - struct CompilerInstance { LLVMContext Ctx; ModulePassManager MPM; @@ -63,16 +48,10 @@ Expected Plugin = PassPlugin::Load(PluginPath); ASSERT_TRUE(!!Plugin) << "Plugin path: " << PluginPath; Plugin->registerPassBuilderCallbacks(PB); - ASSERT_THAT_ERROR(PB.parsePassPipeline(MPM, "dynamic-inline-advisor"), + ASSERT_THAT_ERROR(PB.parsePassPipeline(MPM, "dynamic-inline-order"), Succeeded()); } - // connect the FooOnlyInlineAdvisor to our compiler instance - void setupFooOnly() { - MAM.registerPass( - [&] { return PluginInlineAdvisorAnalysis(fooOnlyFactory); }); - } - CompilerInstance() { IP = getInlineParams(3, 0); PB.registerModuleAnalyses(MAM); @@ -87,24 +66,8 @@ std::string output; std::unique_ptr outputM; - // run with the default inliner - auto run_default(StringRef IR) { - PluginInlineAdvisorAnalysis::HasBeenRegistered = false; - outputM = parseAssemblyString(IR, Error, Ctx); - MPM.run(*outputM, MAM); - ASSERT_TRUE(outputM); - output.clear(); - raw_string_ostream o_stream{output}; - outputM->print(o_stream, nullptr); - ASSERT_TRUE(true); - } - - // run with the dnamic inliner - auto run_dynamic(StringRef IR) { - // note typically the constructor for the DynamicInlineAdvisorAnalysis - // will automatically set this to true, we controll it here only to - // altenate between the default and dynamic inliner in our test - PluginInlineAdvisorAnalysis::HasBeenRegistered = true; + // run with the dynamic inline order + auto run(StringRef IR) { outputM = parseAssemblyString(IR, Error, Ctx); MPM.run(*outputM, MAM); ASSERT_TRUE(outputM); @@ -160,10 +123,10 @@ ret void } define void @f2() { - call void @f3() + call void @foo() ret void } -define void @f3() { +define void @foo() { call void @f1() ret void } @@ -207,7 +170,7 @@ ret i32 %curr_val3 } -define i32 @fib_rec(i32 %n){ +define i32 @foo(i32 %n){ %cmp = icmp eq i32 %n, 0 %cmp2 = icmp eq i32 %n, 1 %or = or i1 %cmp, %cmp2 @@ -216,9 +179,9 @@ ret i32 1 if_false: %sub = sub i32 %n, 1 - %call = call i32 @fib_rec(i32 %sub) + %call = call i32 @foo(i32 %sub) %sub2 = sub i32 %n, 2 - %call2 = call i32 @fib_rec(i32 %sub2) + %call2 = call i32 @foo(i32 %sub2) %add = add i32 %call, %call2 ret i32 %add } @@ -237,7 +200,7 @@ %i_val2 = load i32, i32* %i %call = call i32 @fib_loop(i32 %i_val2) %i_val3 = load i32, i32* %i - %call2 = call i32 @fib_rec(i32 %i_val3) + %call2 = call i32 @foo(i32 %i_val3) %cmp2 = icmp ne i32 %call, %call2 br i1 %cmp2, label %if_true, label %if_false if_true: @@ -256,42 +219,25 @@ } )"}; -// check that loading a plugin works -// the plugin being loaded acts identically to the default inliner -TEST(PluginInlineAdvisorTest, PluginLoad) { -#if !defined(LLVM_ENABLE_PLUGINS) - // Disable the test if plugins are disabled. - return; -#endif - CompilerInstance CI{}; - CI.setupPlugin(); +} // namespace - for (StringRef IR : TestIRS) { - CI.run_default(IR); - std::string default_output = CI.output; - CI.run_dynamic(IR); - std::string dynamic_output = CI.output; - ASSERT_EQ(default_output, dynamic_output); - } -} - -// check that the behaviour of a custom inliner is correct -// the custom inliner inlines all functions that are not named "foo" -// this testdoes not require plugins to be enabled -TEST(PluginInlineAdvisorTest, CustomAdvisor) { +// check that the behaviour of a custom inline order is correct +// the custom order drops any functions called "foo" so all tests +// should contain at least one function called foo +TEST(PluginInlineOrderTest, NoInlineFoo) { CompilerInstance CI{}; - CI.setupFooOnly(); + CI.setupPlugin(); for (StringRef IR : TestIRS) { - CI.run_dynamic(IR); + bool found_foo = false; + CI.run(IR); CallGraph CGraph = CallGraph(*CI.outputM); for (auto &node : CGraph) { for (auto &edge : *node.second) { - if (!edge.first) - continue; - ASSERT_NE(edge.second->getFunction()->getName(), "foo"); + found_foo |= edge.second->getFunction()->getName() == "foo"; } } + ASSERT_TRUE(found_foo); } }