165f7ebe7Sibricchi #include "llvm/Analysis/CallGraph.h" 265f7ebe7Sibricchi #include "llvm/AsmParser/Parser.h" 365f7ebe7Sibricchi #include "llvm/Config/config.h" 4*74deadf1SNikita Popov #include "llvm/IR/Module.h" 565f7ebe7Sibricchi #include "llvm/Passes/PassBuilder.h" 665f7ebe7Sibricchi #include "llvm/Passes/PassPlugin.h" 765f7ebe7Sibricchi #include "llvm/Support/CommandLine.h" 865f7ebe7Sibricchi #include "llvm/Support/raw_ostream.h" 965f7ebe7Sibricchi #include "llvm/Testing/Support/Error.h" 1065f7ebe7Sibricchi #include "gtest/gtest.h" 1165f7ebe7Sibricchi 1265f7ebe7Sibricchi #include "llvm/Analysis/InlineOrder.h" 1365f7ebe7Sibricchi 1465f7ebe7Sibricchi namespace llvm { 1565f7ebe7Sibricchi 1665f7ebe7Sibricchi namespace { 1765f7ebe7Sibricchi 1865f7ebe7Sibricchi void anchor() {} 1965f7ebe7Sibricchi 2065f7ebe7Sibricchi std::string libPath(const std::string Name = "InlineOrderPlugin") { 2165f7ebe7Sibricchi const auto &Argvs = testing::internal::GetArgvs(); 2265f7ebe7Sibricchi const char *Argv0 = 2365f7ebe7Sibricchi Argvs.size() > 0 ? Argvs[0].c_str() : "PluginInlineOrderAnalysisTest"; 2465f7ebe7Sibricchi void *Ptr = (void *)(intptr_t)anchor; 2565f7ebe7Sibricchi std::string Path = sys::fs::getMainExecutable(Argv0, Ptr); 2665f7ebe7Sibricchi llvm::SmallString<256> Buf{sys::path::parent_path(Path)}; 2765f7ebe7Sibricchi sys::path::append(Buf, (Name + LLVM_PLUGIN_EXT).c_str()); 2865f7ebe7Sibricchi return std::string(Buf.str()); 2965f7ebe7Sibricchi } 3065f7ebe7Sibricchi 3165f7ebe7Sibricchi struct CompilerInstance { 3265f7ebe7Sibricchi LLVMContext Ctx; 3365f7ebe7Sibricchi ModulePassManager MPM; 3465f7ebe7Sibricchi InlineParams IP; 3565f7ebe7Sibricchi 3665f7ebe7Sibricchi PassBuilder PB; 3765f7ebe7Sibricchi LoopAnalysisManager LAM; 3865f7ebe7Sibricchi FunctionAnalysisManager FAM; 3965f7ebe7Sibricchi CGSCCAnalysisManager CGAM; 4065f7ebe7Sibricchi ModuleAnalysisManager MAM; 4165f7ebe7Sibricchi 4265f7ebe7Sibricchi SMDiagnostic Error; 4365f7ebe7Sibricchi 4465f7ebe7Sibricchi // Connect the plugin to our compiler instance. 4565f7ebe7Sibricchi void setupPlugin() { 4665f7ebe7Sibricchi auto PluginPath = libPath(); 4765f7ebe7Sibricchi ASSERT_NE("", PluginPath); 4865f7ebe7Sibricchi Expected<PassPlugin> Plugin = PassPlugin::Load(PluginPath); 4965f7ebe7Sibricchi ASSERT_TRUE(!!Plugin) << "Plugin path: " << PluginPath; 5065f7ebe7Sibricchi Plugin->registerPassBuilderCallbacks(PB); 5165f7ebe7Sibricchi } 5265f7ebe7Sibricchi 5365f7ebe7Sibricchi CompilerInstance() { 5465f7ebe7Sibricchi IP = getInlineParams(3, 0); 5565f7ebe7Sibricchi PB.registerModuleAnalyses(MAM); 5665f7ebe7Sibricchi PB.registerCGSCCAnalyses(CGAM); 5765f7ebe7Sibricchi PB.registerFunctionAnalyses(FAM); 5865f7ebe7Sibricchi PB.registerLoopAnalyses(LAM); 5965f7ebe7Sibricchi PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); 6065f7ebe7Sibricchi MPM.addPass(ModuleInlinerPass(IP, InliningAdvisorMode::Default, 6165f7ebe7Sibricchi ThinOrFullLTOPhase::None)); 6265f7ebe7Sibricchi } 6365f7ebe7Sibricchi 6465f7ebe7Sibricchi std::string Output; 6565f7ebe7Sibricchi std::unique_ptr<Module> OutputM; 6665f7ebe7Sibricchi 6765f7ebe7Sibricchi // Run with the dynamic inline order. 6865f7ebe7Sibricchi auto run(StringRef IR) { 6965f7ebe7Sibricchi OutputM = parseAssemblyString(IR, Error, Ctx); 7065f7ebe7Sibricchi MPM.run(*OutputM, MAM); 7165f7ebe7Sibricchi ASSERT_TRUE(OutputM); 7265f7ebe7Sibricchi Output.clear(); 7365f7ebe7Sibricchi raw_string_ostream OStream{Output}; 7465f7ebe7Sibricchi OutputM->print(OStream, nullptr); 7565f7ebe7Sibricchi ASSERT_TRUE(true); 7665f7ebe7Sibricchi } 7765f7ebe7Sibricchi }; 7865f7ebe7Sibricchi 7965f7ebe7Sibricchi StringRef TestIRS[] = { 8065f7ebe7Sibricchi // Simple 3 function inline case. 8165f7ebe7Sibricchi R"( 8265f7ebe7Sibricchi define void @f1() { 8365f7ebe7Sibricchi call void @foo() 8465f7ebe7Sibricchi ret void 8565f7ebe7Sibricchi } 8665f7ebe7Sibricchi define void @foo() { 8765f7ebe7Sibricchi call void @f3() 8865f7ebe7Sibricchi ret void 8965f7ebe7Sibricchi } 9065f7ebe7Sibricchi define void @f3() { 9165f7ebe7Sibricchi ret void 9265f7ebe7Sibricchi } 9365f7ebe7Sibricchi )", 9465f7ebe7Sibricchi // Test that has 5 functions of which 2 are recursive. 9565f7ebe7Sibricchi R"( 9665f7ebe7Sibricchi define void @f1() { 9765f7ebe7Sibricchi call void @foo() 9865f7ebe7Sibricchi ret void 9965f7ebe7Sibricchi } 10065f7ebe7Sibricchi define void @f2() { 10165f7ebe7Sibricchi call void @foo() 10265f7ebe7Sibricchi ret void 10365f7ebe7Sibricchi } 10465f7ebe7Sibricchi define void @foo() { 10565f7ebe7Sibricchi call void @f4() 10665f7ebe7Sibricchi call void @f5() 10765f7ebe7Sibricchi ret void 10865f7ebe7Sibricchi } 10965f7ebe7Sibricchi define void @f4() { 11065f7ebe7Sibricchi ret void 11165f7ebe7Sibricchi } 11265f7ebe7Sibricchi define void @f5() { 11365f7ebe7Sibricchi call void @foo() 11465f7ebe7Sibricchi ret void 11565f7ebe7Sibricchi } 11665f7ebe7Sibricchi )", 11765f7ebe7Sibricchi // Test with 2 mutually recursive functions and 1 function with a loop. 11865f7ebe7Sibricchi R"( 11965f7ebe7Sibricchi define void @f1() { 12065f7ebe7Sibricchi call void @f2() 12165f7ebe7Sibricchi ret void 12265f7ebe7Sibricchi } 12365f7ebe7Sibricchi define void @f2() { 12465f7ebe7Sibricchi call void @foo() 12565f7ebe7Sibricchi ret void 12665f7ebe7Sibricchi } 12765f7ebe7Sibricchi define void @foo() { 12865f7ebe7Sibricchi call void @f1() 12965f7ebe7Sibricchi ret void 13065f7ebe7Sibricchi } 13165f7ebe7Sibricchi define void @f4() { 13265f7ebe7Sibricchi br label %loop 13365f7ebe7Sibricchi loop: 13465f7ebe7Sibricchi call void @f5() 13565f7ebe7Sibricchi br label %loop 13665f7ebe7Sibricchi } 13765f7ebe7Sibricchi define void @f5() { 13865f7ebe7Sibricchi ret void 13965f7ebe7Sibricchi } 14065f7ebe7Sibricchi )", 14165f7ebe7Sibricchi // Test that has a function that computes fibonacci in a loop, one in a 14265f7ebe7Sibricchi // recursive manner, and one that calls both and compares them. 14365f7ebe7Sibricchi R"( 14465f7ebe7Sibricchi define i32 @fib_loop(i32 %n){ 14565f7ebe7Sibricchi %curr = alloca i32 14665f7ebe7Sibricchi %last = alloca i32 14765f7ebe7Sibricchi %i = alloca i32 14865f7ebe7Sibricchi store i32 1, i32* %curr 14965f7ebe7Sibricchi store i32 1, i32* %last 15065f7ebe7Sibricchi store i32 2, i32* %i 15165f7ebe7Sibricchi br label %loop_cond 15265f7ebe7Sibricchi loop_cond: 15365f7ebe7Sibricchi %i_val = load i32, i32* %i 15465f7ebe7Sibricchi %cmp = icmp slt i32 %i_val, %n 15565f7ebe7Sibricchi br i1 %cmp, label %loop_body, label %loop_end 15665f7ebe7Sibricchi loop_body: 15765f7ebe7Sibricchi %curr_val = load i32, i32* %curr 15865f7ebe7Sibricchi %last_val = load i32, i32* %last 15965f7ebe7Sibricchi %add = add i32 %curr_val, %last_val 16065f7ebe7Sibricchi store i32 %add, i32* %last 16165f7ebe7Sibricchi store i32 %curr_val, i32* %curr 16265f7ebe7Sibricchi %i_val2 = load i32, i32* %i 16365f7ebe7Sibricchi %add2 = add i32 %i_val2, 1 16465f7ebe7Sibricchi store i32 %add2, i32* %i 16565f7ebe7Sibricchi br label %loop_cond 16665f7ebe7Sibricchi loop_end: 16765f7ebe7Sibricchi %curr_val3 = load i32, i32* %curr 16865f7ebe7Sibricchi ret i32 %curr_val3 16965f7ebe7Sibricchi } 17065f7ebe7Sibricchi 17165f7ebe7Sibricchi define i32 @foo(i32 %n){ 17265f7ebe7Sibricchi %cmp = icmp eq i32 %n, 0 17365f7ebe7Sibricchi %cmp2 = icmp eq i32 %n, 1 17465f7ebe7Sibricchi %or = or i1 %cmp, %cmp2 17565f7ebe7Sibricchi br i1 %or, label %if_true, label %if_false 17665f7ebe7Sibricchi if_true: 17765f7ebe7Sibricchi ret i32 1 17865f7ebe7Sibricchi if_false: 17965f7ebe7Sibricchi %sub = sub i32 %n, 1 18065f7ebe7Sibricchi %call = call i32 @foo(i32 %sub) 18165f7ebe7Sibricchi %sub2 = sub i32 %n, 2 18265f7ebe7Sibricchi %call2 = call i32 @foo(i32 %sub2) 18365f7ebe7Sibricchi %add = add i32 %call, %call2 18465f7ebe7Sibricchi ret i32 %add 18565f7ebe7Sibricchi } 18665f7ebe7Sibricchi 18765f7ebe7Sibricchi define i32 @fib_check(){ 18865f7ebe7Sibricchi %correct = alloca i32 18965f7ebe7Sibricchi %i = alloca i32 19065f7ebe7Sibricchi store i32 1, i32* %correct 19165f7ebe7Sibricchi store i32 0, i32* %i 19265f7ebe7Sibricchi br label %loop_cond 19365f7ebe7Sibricchi loop_cond: 19465f7ebe7Sibricchi %i_val = load i32, i32* %i 19565f7ebe7Sibricchi %cmp = icmp slt i32 %i_val, 10 19665f7ebe7Sibricchi br i1 %cmp, label %loop_body, label %loop_end 19765f7ebe7Sibricchi loop_body: 19865f7ebe7Sibricchi %i_val2 = load i32, i32* %i 19965f7ebe7Sibricchi %call = call i32 @fib_loop(i32 %i_val2) 20065f7ebe7Sibricchi %i_val3 = load i32, i32* %i 20165f7ebe7Sibricchi %call2 = call i32 @foo(i32 %i_val3) 20265f7ebe7Sibricchi %cmp2 = icmp ne i32 %call, %call2 20365f7ebe7Sibricchi br i1 %cmp2, label %if_true, label %if_false 20465f7ebe7Sibricchi if_true: 20565f7ebe7Sibricchi store i32 0, i32* %correct 20665f7ebe7Sibricchi br label %if_end 20765f7ebe7Sibricchi if_false: 20865f7ebe7Sibricchi br label %if_end 20965f7ebe7Sibricchi if_end: 21065f7ebe7Sibricchi %i_val4 = load i32, i32* %i 21165f7ebe7Sibricchi %add = add i32 %i_val4, 1 21265f7ebe7Sibricchi store i32 %add, i32* %i 21365f7ebe7Sibricchi br label %loop_cond 21465f7ebe7Sibricchi loop_end: 21565f7ebe7Sibricchi %correct_val = load i32, i32* %correct 21665f7ebe7Sibricchi ret i32 %correct_val 21765f7ebe7Sibricchi } 21865f7ebe7Sibricchi )"}; 21965f7ebe7Sibricchi 22065f7ebe7Sibricchi } // namespace 22165f7ebe7Sibricchi 22265f7ebe7Sibricchi // Check that the behaviour of a custom inline order is correct. 22365f7ebe7Sibricchi // The custom order drops any functions named "foo" so all tests 22465f7ebe7Sibricchi // should contain at least one function named foo. 22565f7ebe7Sibricchi TEST(PluginInlineOrderTest, NoInlineFoo) { 22665f7ebe7Sibricchi #if !defined(LLVM_ENABLE_PLUGINS) 22765f7ebe7Sibricchi // Skip the test if plugins are disabled. 22865f7ebe7Sibricchi GTEST_SKIP(); 22965f7ebe7Sibricchi #endif 23065f7ebe7Sibricchi CompilerInstance CI{}; 23165f7ebe7Sibricchi CI.setupPlugin(); 23265f7ebe7Sibricchi 23365f7ebe7Sibricchi for (StringRef IR : TestIRS) { 23465f7ebe7Sibricchi bool FoundFoo = false; 23565f7ebe7Sibricchi CI.run(IR); 23665f7ebe7Sibricchi CallGraph CGraph = CallGraph(*CI.OutputM); 23765f7ebe7Sibricchi for (auto &Node : CGraph) { 23865f7ebe7Sibricchi for (auto &Edge : *Node.second) { 23965f7ebe7Sibricchi FoundFoo |= Edge.second->getFunction()->getName() == "foo"; 24065f7ebe7Sibricchi } 24165f7ebe7Sibricchi } 24265f7ebe7Sibricchi ASSERT_TRUE(FoundFoo); 24365f7ebe7Sibricchi } 24465f7ebe7Sibricchi } 24565f7ebe7Sibricchi 24665f7ebe7Sibricchi } // namespace llvm 247