diff --git a/src/backend/codegen/include/codegen/utils/codegen_utils.h b/src/backend/codegen/include/codegen/utils/codegen_utils.h index 262e9777e35f695d855f631d66f0d7e9aa0cd020..2ae02e71264c8a822143754c9a3a9e59f60d2a68 100644 --- a/src/backend/codegen/include/codegen/utils/codegen_utils.h +++ b/src/backend/codegen/include/codegen/utils/codegen_utils.h @@ -30,6 +30,7 @@ #include "codegen/utils/macros.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" +#include "llvm/Analysis/CallGraph.h" #include "llvm/ExecutionEngine/ExecutionEngine.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Constants.h" @@ -43,6 +44,7 @@ #include "llvm/IR/Type.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/Value.h" +#include "llvm/Transforms/Utils/Cloning.h" namespace gpcodegen { @@ -479,6 +481,24 @@ class CodegenUtils { void CreateFallback(llvm::Function* regular_function, llvm::Function* generated_function); + /* + * @brief Force inline an LLVM function at the call site. Note that this only + * does one level of inlining. + * + * @param LLVM call Instruction where the called function should be inlined + * @return false if it is not possible to inline. true otherwise. + */ + bool InlineFunction(llvm::CallInst* call_inst) { + // Check that the call instruction belongs to a BasicBlock which is part of + // a valid function + if (!(call_inst && call_inst->getParent() + && call_inst->getParent()->getParent())){ + return false; + } + llvm::InlineFunctionInfo info; + return llvm::InlineFunction(llvm::CallSite(call_inst), info); + } + private: // Give ClangCompiler access to 'context_' add allow it to add compiled C++ // sources to 'auxiliary_modules_'. diff --git a/src/backend/codegen/tests/codegen_utils_unittest.cc b/src/backend/codegen/tests/codegen_utils_unittest.cc index 3c66040b2cb4f6ca827ead1f52ce4d4ad0330ae5..43623fcf91d1ef6acfa793c22553d54f70cef764 100644 --- a/src/backend/codegen/tests/codegen_utils_unittest.cc +++ b/src/backend/codegen/tests/codegen_utils_unittest.cc @@ -41,6 +41,7 @@ #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" +#include "llvm/IR/InstIterator.h" #include "llvm/IR/Module.h" #include "llvm/IR/Type.h" #include "llvm/IR/Value.h" @@ -2736,9 +2737,9 @@ TEST_F(CodegenUtilsTest, CppClassObjectTest) { EXPECT_EQ(-12.75, (*accumulate_test_fn_compiled)(-22.75)); } -// Test GetOrGetOrRegisterExternalFunction to return the right llvm::Function if +// Test GetOrRegisterExternalFunction to return the right llvm::Function if // previously registered or else register it anew -TEST_F(CodegenUtilsTest, GetOrGetOrRegisterExternalFunctionTest) { +TEST_F(CodegenUtilsTest, GetOrRegisterExternalFunctionTest) { // Test previous unregistered function EXPECT_EQ(nullptr, codegen_utils_->module()->getFunction("floor")); llvm::Function* floor_func = codegen_utils_->GetOrRegisterExternalFunction( @@ -2762,6 +2763,56 @@ TEST_F(CodegenUtilsTest, GetOrGetOrRegisterExternalFunctionTest) { EXPECT_EQ(expected_fprintf_func, fprintf_func); } +// Utility method to compute the number of calls in an llvm::Function* +int GetLLVMFunctionCallCount(llvm::Function* F) { + return std::count_if(llvm::inst_begin(F), llvm::inst_end(F), + [] (llvm::Instruction& i)-> bool { + return (llvm::dyn_cast(&i)); + }); +} + +// Test InlineFunction +TEST_F(CodegenUtilsTest, InlineFunctionTest) { + auto irb = codegen_utils_->ir_builder(); + + typedef int (*AddConstToIntFn) (int); + + // Create a simple adds 1 to a number and returns the new value + llvm::Function* add_one_fn = codegen_utils_->CreateFunction("add_one"); + irb->SetInsertPoint(codegen_utils_->CreateBasicBlock("main", add_one_fn)); + irb->CreateRet(irb->CreateAdd(ArgumentByPosition(add_one_fn, 0), + codegen_utils_->GetConstant(1))); + + // Create another simple function add_two which calls add_one twice + llvm::Function* add_two_fn = codegen_utils_->CreateFunction("add_two"); + irb->SetInsertPoint(codegen_utils_->CreateBasicBlock("main", add_two_fn)); + llvm::CallInst* first_call = irb->CreateCall(add_one_fn, {ArgumentByPosition(add_two_fn, 0)}); + llvm::CallInst* second_call = irb->CreateCall(add_one_fn, {first_call}); + irb->CreateRet(second_call); + + + EXPECT_EQ(GetLLVMFunctionCallCount(add_two_fn), 2); + + EXPECT_TRUE(codegen_utils_->InlineFunction(first_call)); + EXPECT_EQ(GetLLVMFunctionCallCount(add_two_fn), 1); + + EXPECT_FALSE(codegen_utils_->InlineFunction(first_call)); + + EXPECT_TRUE(codegen_utils_->InlineFunction(second_call)); + EXPECT_EQ(GetLLVMFunctionCallCount(add_two_fn), 0); + + // Compiled module + EXPECT_TRUE(codegen_utils_->PrepareForExecution( + CodegenUtils::OptimizationLevel::kNone, false)); + AddConstToIntFn compiled_add_two_fn = + codegen_utils_->GetFunctionPointer("add_two"); + + // Test normal functionality, even after inlining + EXPECT_TRUE(nullptr != compiled_add_two_fn); + EXPECT_EQ(compiled_add_two_fn(5), 7); + EXPECT_EQ(compiled_add_two_fn(-5), -3); +} + #ifdef GPCODEGEN_DEBUG