251 lines
6 KiB
C++
251 lines
6 KiB
C++
#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
|