| #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 |