xref: /llvm-project/llvm/unittests/Analysis/PluginInlineOrderAnalysisTest.cpp (revision ab4253f6dff194a1e09448c8628809d21f148df9)
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