};
std::unique_ptr<InlineOrder<std::pair<CallBase *, int>>>
-getInlineOrder(FunctionAnalysisManager &FAM, const InlineParams &Params);
+getDefaultInlineOrder(FunctionAnalysisManager &FAM, const InlineParams &Params,
+ ModuleAnalysisManager &MAM, Module &M);
+
+std::unique_ptr<InlineOrder<std::pair<CallBase *, int>>>
+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<PluginInlineOrderAnalysis> {
+public:
+ static AnalysisKey Key;
+
+ typedef std::unique_ptr<InlineOrder<std::pair<CallBase *, int>>> (
+ *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
"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<int> ModuleInlinerTopPriorityThreshold(
"moudle-inliner-top-priority-threshold", cl::Hidden, cl::init(0),
} // namespace
+AnalysisKey llvm::PluginInlineOrderAnalysis::Key;
+bool llvm::PluginInlineOrderAnalysis::HasBeenRegistered;
+
std::unique_ptr<InlineOrder<std::pair<CallBase *, int>>>
-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");
case InlinePriorityMode::CostBenefit:
LLVM_DEBUG(
dbgs() << " Current used priority: cost-benefit priority ---- \n");
- return std::make_unique<PriorityInlineOrder<CostBenefitPriority>>(FAM, Params);
+ return std::make_unique<PriorityInlineOrder<CostBenefitPriority>>(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<PriorityInlineOrder<MLPriority>>(FAM, Params);
}
return nullptr;
}
+
+std::unique_ptr<InlineOrder<std::pair<CallBase *, int>>>
+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<PluginInlineOrderAnalysis>(M).Factory(FAM, Params, MAM,
+ M);
+ }
+ return getDefaultInlineOrder(FAM, Params, MAM, M);
+}
\ No newline at end of file
//
// 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.
MLModelRunnerTest.cpp
PhiValuesTest.cpp
PluginInlineAdvisorAnalysisTest.cpp
+ PluginInlineOrderAnalysisTest.cpp
ProfileSummaryInfoTest.cpp
ScalarEvolutionTest.cpp
VectorFunctionABITest.cpp
endif()
add_subdirectory(InlineAdvisorPlugin)
+add_subdirectory(InlineOrderPlugin)
--- /dev/null
+# 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(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()
--- /dev/null
+#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<std::pair<CallBase *, int>> {
+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<CallBase *, int> &Elt) override {
+ // We ignore calles named "foo"
+ if (Elt.first->getCalledFunction()->getName() == "foo") {
+ DefaultInlineOrder->push(Elt);
+ }
+ }
+ std::pair<CallBase *, int> pop() override {
+ return DefaultInlineOrder->pop();
+ }
+ void erase_if(function_ref<bool(std::pair<CallBase *, int>)> Pred) override {
+ DefaultInlineOrder->erase_if(Pred);
+ }
+
+private:
+ std::unique_ptr<InlineOrder<std::pair<CallBase *, int>>> DefaultInlineOrder;
+};
+
+std::unique_ptr<InlineOrder<std::pair<CallBase *, int>>>
+NoFooInlineOrderFactory(FunctionAnalysisManager &FAM,
+ const InlineParams &Params, ModuleAnalysisManager &MAM,
+ Module &M) {
+ return std::make_unique<NoFooInlineOrder>(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();
+}
namespace llvm {
+namespace {
+
void anchor() {}
static std::string libPath(const std::string Name = "InlineAdvisorPlugin") {
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<Module> outputM;
}
)"};
+} // namespace
+
// check that loading a plugin works
// the plugin being loaded acts identically to the default inliner
TEST(PluginInlineAdvisorTest, PluginLoad) {
--- /dev/null
+#include "llvm/Analysis/CallGraph.h"
+#include "llvm/AsmParser/Parser.h"
+#include "llvm/Config/config.h"
+#include "llvm/Passes/PassBuilder.h"
+#include "llvm/Passes/PassPlugin.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gtest/gtest.h"
+
+#include "llvm/Analysis/InlineOrder.h"
+
+namespace llvm {
+
+namespace {
+
+void anchor() {}
+
+std::string libPath(const std::string Name = "InlineOrderPlugin") {
+ const auto &Argvs = testing::internal::GetArgvs();
+ const char *Argv0 =
+ 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)};
+ sys::path::append(Buf, (Name + LLVM_PLUGIN_EXT).c_str());
+ return std::string(Buf.str());
+}
+
+struct CompilerInstance {
+ LLVMContext Ctx;
+ ModulePassManager MPM;
+ InlineParams IP;
+
+ PassBuilder PB;
+ LoopAnalysisManager LAM;
+ FunctionAnalysisManager FAM;
+ CGSCCAnalysisManager CGAM;
+ ModuleAnalysisManager MAM;
+
+ SMDiagnostic Error;
+
+ // Connect the plugin to our compiler instance.
+ void setupPlugin() {
+ auto PluginPath = libPath();
+ ASSERT_NE("", PluginPath);
+ Expected<PassPlugin> Plugin = PassPlugin::Load(PluginPath);
+ ASSERT_TRUE(!!Plugin) << "Plugin path: " << PluginPath;
+ Plugin->registerPassBuilderCallbacks(PB);
+ }
+
+ CompilerInstance() {
+ IP = getInlineParams(3, 0);
+ PB.registerModuleAnalyses(MAM);
+ PB.registerCGSCCAnalyses(CGAM);
+ PB.registerFunctionAnalyses(FAM);
+ PB.registerLoopAnalyses(LAM);
+ PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
+ MPM.addPass(ModuleInlinerPass(IP, InliningAdvisorMode::Default,
+ 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.
+ PluginInlineOrderAnalysis::unregister();
+ }
+
+ std::string Output;
+ std::unique_ptr<Module> 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.
+ R"(
+define void @f1() {
+ call void @foo()
+ ret void
+}
+define void @foo() {
+ call void @f3()
+ ret void
+}
+define void @f3() {
+ ret void
+}
+ )",
+ // Test that has 5 functions of which 2 are recursive.
+ R"(
+define void @f1() {
+ call void @foo()
+ ret void
+}
+define void @f2() {
+ call void @foo()
+ ret void
+}
+define void @foo() {
+ call void @f4()
+ call void @f5()
+ ret void
+}
+define void @f4() {
+ ret void
+}
+define void @f5() {
+ call void @foo()
+ ret void
+}
+ )",
+ // 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 @foo()
+ ret void
+}
+define void @foo() {
+ call void @f1()
+ ret void
+}
+define void @f4() {
+ br label %loop
+loop:
+ call void @f5()
+ br label %loop
+}
+define void @f5() {
+ ret void
+}
+ )",
+ // 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
+ %last = alloca i32
+ %i = alloca i32
+ store i32 1, i32* %curr
+ store i32 1, i32* %last
+ store i32 2, i32* %i
+ br label %loop_cond
+ loop_cond:
+ %i_val = load i32, i32* %i
+ %cmp = icmp slt i32 %i_val, %n
+ br i1 %cmp, label %loop_body, label %loop_end
+ loop_body:
+ %curr_val = load i32, i32* %curr
+ %last_val = load i32, i32* %last
+ %add = add i32 %curr_val, %last_val
+ store i32 %add, i32* %last
+ store i32 %curr_val, i32* %curr
+ %i_val2 = load i32, i32* %i
+ %add2 = add i32 %i_val2, 1
+ store i32 %add2, i32* %i
+ br label %loop_cond
+ loop_end:
+ %curr_val3 = load i32, i32* %curr
+ ret i32 %curr_val3
+}
+
+define i32 @foo(i32 %n){
+ %cmp = icmp eq i32 %n, 0
+ %cmp2 = icmp eq i32 %n, 1
+ %or = or i1 %cmp, %cmp2
+ br i1 %or, label %if_true, label %if_false
+ if_true:
+ ret i32 1
+ if_false:
+ %sub = sub i32 %n, 1
+ %call = call i32 @foo(i32 %sub)
+ %sub2 = sub i32 %n, 2
+ %call2 = call i32 @foo(i32 %sub2)
+ %add = add i32 %call, %call2
+ ret i32 %add
+}
+
+define i32 @fib_check(){
+ %correct = alloca i32
+ %i = alloca i32
+ store i32 1, i32* %correct
+ store i32 0, i32* %i
+ br label %loop_cond
+ loop_cond:
+ %i_val = load i32, i32* %i
+ %cmp = icmp slt i32 %i_val, 10
+ br i1 %cmp, label %loop_body, label %loop_end
+ loop_body:
+ %i_val2 = load i32, i32* %i
+ %call = call i32 @fib_loop(i32 %i_val2)
+ %i_val3 = load i32, i32* %i
+ %call2 = call i32 @foo(i32 %i_val3)
+ %cmp2 = icmp ne i32 %call, %call2
+ br i1 %cmp2, label %if_true, label %if_false
+ if_true:
+ store i32 0, i32* %correct
+ br label %if_end
+ if_false:
+ br label %if_end
+ if_end:
+ %i_val4 = load i32, i32* %i
+ %add = add i32 %i_val4, 1
+ store i32 %add, i32* %i
+ br label %loop_cond
+ loop_end:
+ %correct_val = load i32, i32* %correct
+ ret i32 %correct_val
+}
+ )"};
+
+} // 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();
+#endif
+ CompilerInstance CI{};
+ CI.setupPlugin();
+
+ for (StringRef IR : TestIRS) {
+ 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);
+ }
+}
+
+} // namespace llvm