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,52 @@ }; std::unique_ptr>> -getInlineOrder(FunctionAnalysisManager &FAM, const InlineParams &Params); +getDefaultInlineOrder(FunctionAnalysisManager &FAM, const InlineParams &Params, + ModuleAnalysisManager &MAM, Module &M); + +std::unique_ptr>> +getInlineOrder(FunctionAnalysisManager &FAM, const InlineParams &Params, + ModuleAnalysisManager &MAM, Module &M); + +/// Used for dynamically loading instances of InlineOrder as plugins +/// +/// Plugins must implement an InlineOrderFactory, for an example refer to: +/// llvm/unittests/Analysis/InlineOrderPlugin/InlineOrderPlugin.cpp +/// +/// If a PluginInlineOrderAnalysis has been registered with the +/// current ModuleAnalysisManager, llvm::getInlineOrder returns an +/// InlineOrder created by the PluginInlineOrderAnalysis' Factory. +/// +class PluginInlineOrderAnalysis + : public AnalysisInfoMixin { +public: + static AnalysisKey Key; + + 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."); + } + + struct Result { + InlineOrderFactory Factory; + }; + + Result run(Module &, ModuleAnalysisManager &) { return {Factory}; } + Result getResult() { return {Factory}; } + + static bool isRegistered() { return HasBeenRegistered; } + static void unregister() { HasBeenRegistered = false; } + +private: + static bool HasBeenRegistered; + 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 @@ -33,8 +33,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 +280,13 @@ } // namespace +AnalysisKey llvm::PluginInlineOrderAnalysis::Key; +bool llvm::PluginInlineOrderAnalysis::HasBeenRegistered; + std::unique_ptr>> -llvm::getInlineOrder(FunctionAnalysisManager &FAM, const InlineParams &Params) { +llvm::getDefaultInlineOrder(FunctionAnalysisManager &FAM, + const InlineParams &Params, + ModuleAnalysisManager &MAM, Module &M) { switch (UseInlinePriority) { case InlinePriorityMode::Size: LLVM_DEBUG(dbgs() << " Current used priority: Size priority ---- \n"); @@ -295,11 +299,22 @@ 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; } + +std::unique_ptr>> +llvm::getInlineOrder(FunctionAnalysisManager &FAM, const InlineParams &Params, + ModuleAnalysisManager &MAM, Module &M) { + if (llvm::PluginInlineOrderAnalysis::isRegistered()) { + LLVM_DEBUG(dbgs() << " Current used priority: plugin ---- \n"); + return MAM.getResult(M).Factory(FAM, Params, MAM, + M); + } + return getDefaultInlineOrder(FAM, Params, MAM, M); +} \ No newline at end of file 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 @@ -72,4 +73,10 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-brtl") endif() +# Export symbols from the plugins shared objects. +if(NOT WIN32) + export_executable_symbols_for_plugins(AnalysisTests) +endif() + add_subdirectory(InlineAdvisorPlugin) +add_subdirectory(InlineOrderPlugin) diff --git a/llvm/unittests/Analysis/InlineAdvisorPlugin/CMakeLists.txt b/llvm/unittests/Analysis/InlineAdvisorPlugin/CMakeLists.txt --- a/llvm/unittests/Analysis/InlineAdvisorPlugin/CMakeLists.txt +++ b/llvm/unittests/Analysis/InlineAdvisorPlugin/CMakeLists.txt @@ -14,7 +14,6 @@ ) set_target_properties(InlineAdvisorPlugin 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(InlineAdvisorPlugin intrinsics_gen) diff --git a/llvm/unittests/Analysis/InlineAdvisorPlugin/CMakeLists.txt b/llvm/unittests/Analysis/InlineOrderPlugin/CMakeLists.txt copy from llvm/unittests/Analysis/InlineAdvisorPlugin/CMakeLists.txt copy to llvm/unittests/Analysis/InlineOrderPlugin/CMakeLists.txt --- a/llvm/unittests/Analysis/InlineAdvisorPlugin/CMakeLists.txt +++ b/llvm/unittests/Analysis/InlineOrderPlugin/CMakeLists.txt @@ -1,23 +1,20 @@ -# The advisor plugin expects to not link against the Analysis, Support and Core +# 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) unset(LLVM_LINK_COMPONENTS) - add_llvm_library(InlineAdvisorPlugin MODULE BUILDTREE_ONLY - InlineAdvisorPlugin.cpp - ) + add_llvm_library(InlineOrderPlugin MODULE BUILDTREE_ONLY InlineOrderPlugin.cpp) # Put PLUGIN next to the unit test executable. - set_output_directory(InlineAdvisorPlugin + 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(InlineAdvisorPlugin PROPERTIES FOLDER "Tests") + 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(InlineAdvisorPlugin intrinsics_gen) - add_dependencies(AnalysisTests InlineAdvisorPlugin) - set_property(TARGET InlineAdvisorPlugin PROPERTY FOLDER "Tests/UnitTests/AnalysisTests") + add_dependencies(InlineOrderPlugin intrinsics_gen) + add_dependencies(AnalysisTests InlineOrderPlugin) + set_property(TARGET InlineOrderPlugin PROPERTY FOLDER "Tests/UnitTests/AnalysisTests") endif() 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,70 @@ +#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) { + DefaultInlineOrder = getDefaultInlineOrder(FAM, Params, MAM, M); + } + size_t size() override { return DefaultInlineOrder->size(); } + void push(const std::pair &Elt) override { + // We ignore calles named "foo" + 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>> +NoFooInlineOrderFactory(FunctionAnalysisManager &FAM, + const InlineParams &Params, ModuleAnalysisManager &MAM, + Module &M) { + return std::make_unique(FAM, Params, MAM, M); +} + +} // namespace + +/* New PM Registration */ +llvm::PassPluginLibraryInfo getDefaultDynamicInlineOrderPluginInfo() { + return {LLVM_PLUGIN_API_VERSION, "DynamicDefaultInlineOrder", + LLVM_VERSION_STRING, [](PassBuilder &PB) { + // We use the PassBuilder's callback mechanism + // to register our Analysis: this will register + // our PluginInlineOrderAnalysis instance with + // the ModuleAnalysisManager + PB.registerAnalysisRegistrationCallback( + [](ModuleAnalysisManager &MAM) { + MAM.registerPass([] { + // defaultInlineOrderFactory will be + // used to create an InlineOrder + return PluginInlineOrderAnalysis(NoFooInlineOrderFactory); + }); + }); + }}; +} + +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") { @@ -84,6 +86,12 @@ ThinOrFullLTOPhase::None)); } + ~CompilerInstance() { + // Reset the static variable that tracks if the plugin has been registered. + // This is needed to allow the test to run multiple times. + PluginInlineAdvisorAnalysis::HasBeenRegistered = false; + } + std::string output; std::unique_ptr outputM; @@ -256,6 +264,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 @@ -8,14 +8,18 @@ #include "llvm/Testing/Support/Error.h" #include "gtest/gtest.h" +#include "llvm/Analysis/InlineOrder.h" + namespace llvm { +namespace { + 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 +27,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; @@ -56,21 +40,13 @@ SMDiagnostic Error; - // connect the plugin to our compiler instance + // Connect the plugin to our compiler instance. void setupPlugin() { auto PluginPath = libPath(); ASSERT_NE("", PluginPath); Expected Plugin = PassPlugin::Load(PluginPath); ASSERT_TRUE(!!Plugin) << "Plugin path: " << PluginPath; Plugin->registerPassBuilderCallbacks(PB); - ASSERT_THAT_ERROR(PB.parsePassPipeline(MPM, "dynamic-inline-advisor"), - Succeeded()); - } - - // connect the FooOnlyInlineAdvisor to our compiler instance - void setupFooOnly() { - MAM.registerPass( - [&] { return PluginInlineAdvisorAnalysis(fooOnlyFactory); }); } CompilerInstance() { @@ -84,39 +60,29 @@ ThinOrFullLTOPhase::None)); } - 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); + ~CompilerInstance() { + // Reset the static variable that tracks if the plugin has been registered. + // This is needed to allow the test to run multiple times. + PluginInlineOrderAnalysis::unregister(); } - // 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; - 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); + std::string Output; + std::unique_ptr OutputM; + + // Run with the dynamic inline order. + auto run(StringRef IR) { + OutputM = parseAssemblyString(IR, Error, Ctx); + MPM.run(*OutputM, MAM); + ASSERT_TRUE(OutputM); + Output.clear(); + raw_string_ostream OStream{Output}; + OutputM->print(OStream, nullptr); ASSERT_TRUE(true); } }; StringRef TestIRS[] = { - // Simple 3 function inline case + // Simple 3 function inline case. R"( define void @f1() { call void @foo() @@ -130,7 +96,7 @@ ret void } )", - // Test that has 5 functions of which 2 are recursive + // Test that has 5 functions of which 2 are recursive. R"( define void @f1() { call void @foo() @@ -153,17 +119,17 @@ ret void } )", - // test with 2 mutually recursive functions and 1 function with a loop + // Test with 2 mutually recursive functions and 1 function with a loop. R"( define void @f1() { call void @f2() ret void } define void @f2() { - call void @f3() + call void @foo() ret void } -define void @f3() { +define void @foo() { call void @f1() ret void } @@ -177,8 +143,8 @@ ret void } )", - // test that has a function that computes fibonacci in a loop, one in a - // recurisve manner, and one that calls both and compares them + // Test that has a function that computes fibonacci in a loop, one in a + // recursive manner, and one that calls both and compares them. R"( define i32 @fib_loop(i32 %n){ %curr = alloca i32 @@ -207,7 +173,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 +182,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 +203,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,9 +222,12 @@ } )"}; -// check that loading a plugin works -// the plugin being loaded acts identically to the default inliner -TEST(PluginInlineAdvisorTest, PluginLoad) { +} // namespace + +// Check that the behaviour of a custom inline order is correct. +// The custom order drops any functions named "foo" so all tests +// should contain at least one function named foo. +TEST(PluginInlineOrderTest, NoInlineFoo) { #if !defined(LLVM_ENABLE_PLUGINS) // Skip the test if plugins are disabled. GTEST_SKIP(); @@ -267,31 +236,15 @@ CI.setupPlugin(); 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) { - CompilerInstance CI{}; - CI.setupFooOnly(); - - for (StringRef IR : TestIRS) { - CI.run_dynamic(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"); + bool FoundFoo = false; + CI.run(IR); + CallGraph CGraph = CallGraph(*CI.OutputM); + for (auto &Node : CGraph) { + for (auto &Edge : *Node.second) { + FoundFoo |= Edge.second->getFunction()->getName() == "foo"; } } + ASSERT_TRUE(FoundFoo); } }