From 4916a606c6c3fe861be864b5f744f5847a6732b6 Mon Sep 17 00:00:00 2001 From: Shreedhar Hardikar Date: Sat, 23 Apr 2016 17:54:28 -0700 Subject: [PATCH] Add support for registering vararg external functions with codegen utils. --- .../include/codegen/utils/codegen_utils.h | 36 +++++++--- .../codegen/tests/codegen_utils_unittest.cc | 67 +++++++++++++++++++ 2 files changed, 95 insertions(+), 8 deletions(-) diff --git a/src/backend/codegen/include/codegen/utils/codegen_utils.h b/src/backend/codegen/include/codegen/utils/codegen_utils.h index 6d61d0ce4f..05df90c81b 100644 --- a/src/backend/codegen/include/codegen/utils/codegen_utils.h +++ b/src/backend/codegen/include/codegen/utils/codegen_utils.h @@ -162,11 +162,12 @@ class CodegenUtils { * * @tparam ReturnType The function's return type. * @tparam ArgumentTypes The types of any number of arguments to the function. + * @param is_var_arg Whether the function has trailing variable arguments list * @return A pointer to the complete function type-signature's equivalent as * an LLVM FunctionType in this CodegenUtils's context. **/ template - llvm::FunctionType* GetFunctionType(); + llvm::FunctionType* GetFunctionType(const bool is_var_arg = false); /** * @brief Get an LLVM constant based on a C++ value. @@ -231,6 +232,7 @@ class CodegenUtils { * @tparam FuncType FunctionType. e.g ReturnType (*)(ArgumenTypes) * * @param name The function's name. + * @param is_var_arg Whether the function has trailing variable arguments list * @param linkage The linkage visibility of the function. Defaults to * ExternalLinkage, which makes the function visible and callable from * anywhere. @@ -240,6 +242,7 @@ class CodegenUtils { template llvm::Function* CreateFunction( const llvm::Twine& name, + const bool is_var_arg = false, const llvm::GlobalValue::LinkageTypes linkage = llvm::GlobalValue::ExternalLinkage); @@ -294,12 +297,14 @@ class CodegenUtils { * so that the registered function will also be callable by its name * in C++ source code compiled by ClangCompiler (see * ClangCompiler::GenerateExternalFunctionDeclarations()). + * @param is_var_arg Whether the function has trailing variable arguments list * @return A callable LLVM function. **/ template llvm::Function* RegisterExternalFunction( ReturnType (*external_function)(ArgumentTypes...), - const std::string& name = "") { + const std::string& name = "", + const bool is_var_arg = false) { external_functions_.emplace_back( name.empty() ? GenerateExternalFunctionName() : name, @@ -310,9 +315,20 @@ class CodegenUtils { } return CreateFunctionImpl( - external_functions_.back().first); + external_functions_.back().first, is_var_arg); } + template + llvm::Function* RegisterExternalFunction( + ReturnType (*external_function)(ArgumentTypes..., ...), + const std::string& name = "") { + return RegisterExternalFunction( + reinterpret_cast(external_function), + name, + true); + } + + /** * @brief Optimize the code in the module managed by this CodegenUtils before * execution. @@ -412,6 +428,7 @@ class CodegenUtils { template llvm::Function* CreateFunctionImpl( const llvm::Twine& name, + const bool is_var_arg = false, const llvm::GlobalValue::LinkageTypes linkage = llvm::GlobalValue::ExternalLinkage); @@ -781,12 +798,12 @@ class TypeVectorBuilder { } // namespace codegen_utils_detail template -llvm::FunctionType* CodegenUtils::GetFunctionType() { +llvm::FunctionType* CodegenUtils::GetFunctionType(const bool is_var_arg) { std::vector argument_types; codegen_utils_detail::TypeVectorBuilder::AppendTypes( this, &argument_types); - return llvm::FunctionType::get(GetType(), argument_types, false); + return llvm::FunctionType::get(GetType(), argument_types, is_var_arg); } // ---------------------------------------------------------------------------- @@ -985,9 +1002,10 @@ class FunctionTypeUnpacker { static llvm::Function* CreateFunctionImpl( CodegenUtils* codegen_utils, const llvm::Twine& name, + const bool is_var_arg, const llvm::GlobalValue::LinkageTypes linkage) { return codegen_utils->CreateFunctionImpl( - name, linkage); + name, is_var_arg, linkage); } static auto GetFunctionPointerImpl(gpcodegen::CodegenUtils* codegen_utils, @@ -1002,17 +1020,19 @@ class FunctionTypeUnpacker { template llvm::Function* CodegenUtils::CreateFunction( const llvm::Twine& name, + const bool is_var_arg, const llvm::GlobalValue::LinkageTypes linkage) { return codegen_utils_detail::FunctionTypeUnpacker:: - CreateFunctionImpl(this, name, linkage); + CreateFunctionImpl(this, name, is_var_arg, linkage); } template llvm::Function* CodegenUtils::CreateFunctionImpl( const llvm::Twine& name, + const bool is_var_arg, const llvm::GlobalValue::LinkageTypes linkage) { return llvm::Function::Create( - GetFunctionType(), + GetFunctionType(is_var_arg), linkage, name, module_.get()); diff --git a/src/backend/codegen/tests/codegen_utils_unittest.cc b/src/backend/codegen/tests/codegen_utils_unittest.cc index f03bf23fe0..1a77839d4b 100644 --- a/src/backend/codegen/tests/codegen_utils_unittest.cc +++ b/src/backend/codegen/tests/codegen_utils_unittest.cc @@ -1776,6 +1776,73 @@ TEST_F(CodegenUtilsTest, ExternalFunctionTest) { EXPECT_EQ(42, StaticIntWrapper::Get()); } +TEST_F(CodegenUtilsTest, VariadicExternalFunctionTest) { + // Test a function with overloads an external function that contains + // variable length arguments : printf and sprintf + + // Register printf with takes variable arguments past char* + llvm::Function* llvm_printf_function = + codegen_utils_->RegisterExternalFunction(printf); + ASSERT_NE(llvm_printf_function, nullptr); + ASSERT_EQ( + (codegen_utils_->GetFunctionType(true)->getPointerTo()), + llvm_printf_function->getType()); + + // Register sprintf with takes variable arguments past char*, char* + llvm::Function* llvm_sprintf_function = + codegen_utils_->RegisterExternalFunction(sprintf); + ASSERT_NE(llvm_sprintf_function, nullptr); + ASSERT_EQ( + (codegen_utils_->GetFunctionType(true)->getPointerTo()), + llvm_sprintf_function->getType()); + + char sprintf_with_three_args_buffer[16], sprintf_with_four_args_buffer[16]; + + // Create a simple function that calls sprintf with 3 and 4 arguments and uses + // the buffers allocated above + typedef void (*SprintfTestType)(); + llvm::Function* sprintf_test = + codegen_utils_->CreateFunction("sprintf_test"); + llvm::BasicBlock* main_block = + codegen_utils_->CreateBasicBlock( "main", sprintf_test); + + codegen_utils_->ir_builder()->SetInsertPoint(main_block); + codegen_utils_->ir_builder()->CreateCall( + llvm_printf_function, { + codegen_utils_->GetConstant("%s %d"), + codegen_utils_->GetConstant("Zero is another way of saying "), + codegen_utils_->GetConstant(0) + }); + codegen_utils_->ir_builder()->CreateCall( + llvm_sprintf_function, { + codegen_utils_->GetConstant(sprintf_with_three_args_buffer), + codegen_utils_->GetConstant("%d"), + codegen_utils_->GetConstant(42) + }); + codegen_utils_->ir_builder()->CreateCall( + llvm_sprintf_function, { + codegen_utils_->GetConstant(sprintf_with_four_args_buffer), + codegen_utils_->GetConstant("%d %s"), + codegen_utils_->GetConstant(51), + codegen_utils_->GetConstant("fifty-one") + }); + codegen_utils_->ir_builder()->CreateRetVoid(); + + // Check that the module is well-formed and prepare the generated wrapper + // functions for execution. + EXPECT_FALSE(llvm::verifyModule(*codegen_utils_->module())); + EXPECT_TRUE(codegen_utils_->PrepareForExecution( + CodegenUtils::OptimizationLevel::kNone, + true)); + + SprintfTestType llvm_sprintf_test = + codegen_utils_->GetFunctionPointer("sprintf_test"); + llvm_sprintf_test(); + + ASSERT_EQ(strcmp(sprintf_with_three_args_buffer, "42"), 0); + ASSERT_EQ(strcmp(sprintf_with_four_args_buffer, "51 fifty-one"), 0); +} + TEST_F(CodegenUtilsTest, RecursionTest) { // Test a version of the factorial function that works by recursion. typedef unsigned (*FactorialRecursiveFn) (unsigned); -- GitLab