diff --git a/cpp/src/gandiva/arrow.h b/cpp/src/gandiva/arrow.h index cc2bd9a10294b..294445105cb99 100644 --- a/cpp/src/gandiva/arrow.h +++ b/cpp/src/gandiva/arrow.h @@ -51,6 +51,9 @@ using ArrayDataVector = std::vector; using Status = arrow::Status; using StatusCode = arrow::StatusCode; +template +using Result = arrow::Result; + static inline bool is_decimal_128(DataTypePtr type) { if (type->id() == arrow::Type::DECIMAL) { auto decimal_type = arrow::internal::checked_cast(type.get()); diff --git a/cpp/src/gandiva/configuration.h b/cpp/src/gandiva/configuration.h index 7fb0d63cf57d9..ca8922dee5dbe 100644 --- a/cpp/src/gandiva/configuration.h +++ b/cpp/src/gandiva/configuration.h @@ -38,6 +38,12 @@ class GANDIVA_EXPORT Configuration { std::size_t Hash() const; bool operator==(const Configuration& other) const; bool operator!=(const Configuration& other) const; + + bool optimize() const { return optimize_; } + void set_optimize(bool optimize) { optimize_ = optimize; } + + private: + bool optimize_ = true; }; /// \brief configuration builder for gandiva diff --git a/cpp/src/gandiva/engine.cc b/cpp/src/gandiva/engine.cc index 9ce0dc919c22d..d1e1affff1fdc 100644 --- a/cpp/src/gandiva/engine.cc +++ b/cpp/src/gandiva/engine.cc @@ -24,6 +24,8 @@ #include "gandiva/engine.h" #include +#include +#include #include #include #include @@ -41,8 +43,11 @@ #include #include #include +#include #include #include +#include +#include #include #include #include @@ -61,72 +66,87 @@ #pragma warning(pop) #endif +#include "gandiva/configuration.h" #include "gandiva/decimal_ir.h" #include "gandiva/exported_funcs_registry.h" +#include "arrow/util/make_unique.h" + namespace gandiva { extern const unsigned char kPrecompiledBitcode[]; extern const size_t kPrecompiledBitcodeSize; -std::once_flag init_once_flag; - -bool Engine::init_once_done_ = false; -std::set Engine::loaded_libs_ = {}; -std::mutex Engine::mtx_; +std::once_flag llvm_init_once_flag; +static bool llvm_init = false; -// One-time initializations. void Engine::InitOnce() { - DCHECK_EQ(init_once_done_, false); + DCHECK_EQ(llvm_init, false); llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetAsmPrinter(); llvm::InitializeNativeTargetAsmParser(); llvm::InitializeNativeTargetDisassembler(); - llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr); - init_once_done_ = true; + llvm_init = true; } -/// factory method to construct the engine. -Status Engine::Make(std::shared_ptr config, - std::unique_ptr* engine) { - static auto host_cpu_name = llvm::sys::getHostCPUName(); - std::unique_ptr engine_obj(new Engine()); - - std::call_once(init_once_flag, [&engine_obj] { engine_obj->InitOnce(); }); - engine_obj->context_.reset(new llvm::LLVMContext()); - engine_obj->ir_builder_.reset(new llvm::IRBuilder<>(*(engine_obj->context()))); - engine_obj->types_.reset(new LLVMTypes(*(engine_obj->context()))); - - // Create the execution engine - std::unique_ptr cg_module( - new llvm::Module("codegen", *(engine_obj->context()))); - engine_obj->module_ = cg_module.get(); - - llvm::EngineBuilder engineBuilder(std::move(cg_module)); - engineBuilder.setMCPU(host_cpu_name); - engineBuilder.setEngineKind(llvm::EngineKind::JIT); - engineBuilder.setOptLevel(llvm::CodeGenOpt::Aggressive); - engineBuilder.setErrorStr(&(engine_obj->llvm_error_)); - engine_obj->execution_engine_.reset(engineBuilder.create()); - if (engine_obj->execution_engine_ == NULL) { - engine_obj->module_ = NULL; - return Status::CodeGenError(engine_obj->llvm_error_); - } - +Engine::Engine(const std::shared_ptr& conf, + std::unique_ptr ctx, + std::unique_ptr engine, llvm::Module* module) + : context_(std::move(ctx)), + execution_engine_(std::move(engine)), + ir_builder_(arrow::internal::make_unique>(*context_)), + module_(module), + types_(*context_), + optimize_(conf->optimize()) {} + +Status Engine::Init() { // Add mappings for functions that can be accessed from LLVM/IR module. - engine_obj->AddGlobalMappings(); + AddGlobalMappings(); - auto status = engine_obj->LoadPreCompiledIR(); - ARROW_RETURN_NOT_OK(status); + ARROW_RETURN_NOT_OK(LoadPreCompiledIR()); + ARROW_RETURN_NOT_OK(DecimalIR::AddFunctions(this)); - // Add decimal functions - status = DecimalIR::AddFunctions(engine_obj.get()); - ARROW_RETURN_NOT_OK(status); + return Status::OK(); +} - *engine = std::move(engine_obj); +/// factory method to construct the engine. +Status Engine::Make(const std::shared_ptr& conf, + std::unique_ptr* out) { + std::call_once(llvm_init_once_flag, InitOnce); + + auto ctx = arrow::internal::make_unique(); + auto module = arrow::internal::make_unique("codegen", *ctx); + + // Capture before moving, ExceutionEngine does not allow retrieving the + // original Module. + auto module_ptr = module.get(); + + auto opt_level = + conf->optimize() ? llvm::CodeGenOpt::Aggressive : llvm::CodeGenOpt::None; + // Note that the lifetime of the error string is not captured by the + // ExecutionEngine but only for the lifetime of the builder. Found by + // inspecting LLVM sources. + std::string builder_error; + std::unique_ptr exec_engine{ + llvm::EngineBuilder(std::move(module)) + .setMCPU(llvm::sys::getHostCPUName()) + .setEngineKind(llvm::EngineKind::JIT) + .setOptLevel(opt_level) + .setErrorStr(&builder_error) + .create()}; + + if (exec_engine == nullptr) { + return Status::CodeGenError("Could not instantiate llvm::ExecutionEngine: ", + builder_error); + } + + std::unique_ptr engine{ + new Engine(conf, std::move(ctx), std::move(exec_engine), module_ptr)}; + ARROW_RETURN_NOT_OK(engine->Init()); + *out = std::move(engine); return Status::OK(); } @@ -191,15 +211,10 @@ Status Engine::RemoveUnusedFunctions() { } // Optimise and compile the module. -Status Engine::FinalizeModule(bool optimise_ir, bool dump_ir, std::string* final_ir) { - auto status = RemoveUnusedFunctions(); - ARROW_RETURN_NOT_OK(status); - - if (dump_ir) { - DumpIR("Before optimise"); - } +Status Engine::FinalizeModule() { + ARROW_RETURN_NOT_OK(RemoveUnusedFunctions()); - if (optimise_ir) { + if (optimize_) { // misc passes to allow for inlining, vectorization, .. std::unique_ptr pass_manager( new llvm::legacy::PassManager()); @@ -222,15 +237,8 @@ Status Engine::FinalizeModule(bool optimise_ir, bool dump_ir, std::string* final pass_builder.OptLevel = 3; pass_builder.populateModulePassManager(*pass_manager); pass_manager->run(*module_); - - if (dump_ir) { - DumpIR("After optimise"); - } - } - if (final_ir != nullptr) { - llvm::raw_string_ostream stream(*final_ir); - module_->print(stream, nullptr); } + ARROW_RETURN_IF(llvm::verifyModule(*module_, &llvm::errs()), Status::CodeGenError("Module verification failed after optimizer")); @@ -249,20 +257,20 @@ void* Engine::CompiledFunction(llvm::Function* irFunction) { void Engine::AddGlobalMappingForFunc(const std::string& name, llvm::Type* ret_type, const std::vector& args, void* function_ptr) { - auto prototype = llvm::FunctionType::get(ret_type, args, false /*isVarArg*/); - auto fn = llvm::Function::Create(prototype, llvm::GlobalValue::ExternalLinkage, name, - module()); + constexpr bool is_var_arg = false; + auto prototype = llvm::FunctionType::get(ret_type, args, is_var_arg); + constexpr auto linkage = llvm::GlobalValue::ExternalLinkage; + auto fn = llvm::Function::Create(prototype, linkage, name, module()); execution_engine_->addGlobalMapping(fn, function_ptr); } void Engine::AddGlobalMappings() { ExportedFuncsRegistry::AddMappings(this); } -void Engine::DumpIR(std::string prefix) { - std::string str; - - llvm::raw_string_ostream stream(str); +std::string Engine::DumpIR() { + std::string ir; + llvm::raw_string_ostream stream(ir); module_->print(stream, nullptr); - std::cout << "====" << prefix << "===" << str << "\n"; + return ir; } } // namespace gandiva diff --git a/cpp/src/gandiva/engine.h b/cpp/src/gandiva/engine.h index dbe33277b87e8..9bda72853395f 100644 --- a/cpp/src/gandiva/engine.h +++ b/cpp/src/gandiva/engine.h @@ -23,7 +23,6 @@ #include #include -#include "arrow/status.h" #include "arrow/util/macros.h" #include "gandiva/configuration.h" @@ -34,21 +33,19 @@ namespace gandiva { -class FunctionIRBuilder; - /// \brief LLVM Execution engine wrapper. class GANDIVA_EXPORT Engine { public: llvm::LLVMContext* context() { return context_.get(); } llvm::IRBuilder<>* ir_builder() { return ir_builder_.get(); } - LLVMTypes* types() { return types_.get(); } + LLVMTypes* types() { return &types_; } llvm::Module* module() { return module_; } /// Factory method to create and initialize the engine object. /// /// \param[in] config the engine configuration /// \param[out] engine the created engine - static Status Make(std::shared_ptr config, + static Status Make(const std::shared_ptr& config, std::unique_ptr* engine); /// Add the function to the list of IR functions that need to be compiled. @@ -59,7 +56,7 @@ class GANDIVA_EXPORT Engine { } /// Optimise and compile the module. - Status FinalizeModule(bool optimise_ir, bool dump_ir, std::string* final_ir = NULLPTR); + Status FinalizeModule(); /// Get the compiled function corresponding to the irfunction. void* CompiledFunction(llvm::Function* irFunction); @@ -68,16 +65,20 @@ class GANDIVA_EXPORT Engine { void AddGlobalMappingForFunc(const std::string& name, llvm::Type* ret_type, const std::vector& args, void* func); + /// Return the generated IR for the module. + std::string DumpIR(); + private: - /// private constructor to ensure engine is created - /// only through the factory. - Engine() : module_finalized_(false) {} + Engine(const std::shared_ptr& conf, + std::unique_ptr ctx, + std::unique_ptr engine, llvm::Module* module); + + // Post construction init. This _must_ be called after the constructor. + Status Init(); - /// do one time inits. static void InitOnce(); - static bool init_once_done_; - llvm::ExecutionEngine& execution_engine() { return *execution_engine_.get(); } + llvm::ExecutionEngine& execution_engine() { return *execution_engine_; } /// load pre-compiled IR modules from precompiled_bitcode.cc and merge them into /// the main module. @@ -89,23 +90,16 @@ class GANDIVA_EXPORT Engine { // Remove unused functions to reduce compile time. Status RemoveUnusedFunctions(); - /// dump the IR code to stdout with the prefix string. - void DumpIR(std::string prefix); - std::unique_ptr context_; std::unique_ptr execution_engine_; - std::unique_ptr types_; std::unique_ptr> ir_builder_; - llvm::Module* module_; // This is owned by the execution_engine_, so doesn't need to be - // explicitly deleted. + llvm::Module* module_; + LLVMTypes types_; std::vector functions_to_compile_; - bool module_finalized_; - std::string llvm_error_; - - static std::set loaded_libs_; - static std::mutex mtx_; + bool optimize_ = true; + bool module_finalized_ = false; }; } // namespace gandiva diff --git a/cpp/src/gandiva/engine_llvm_test.cc b/cpp/src/gandiva/engine_llvm_test.cc index 85c262c8d0799..d5a6c04b3bb3b 100644 --- a/cpp/src/gandiva/engine_llvm_test.cc +++ b/cpp/src/gandiva/engine_llvm_test.cc @@ -18,113 +18,111 @@ #include "gandiva/engine.h" #include +#include #include "gandiva/llvm_types.h" #include "gandiva/tests/test_util.h" namespace gandiva { -typedef int64_t (*add_vector_func_t)(int64_t* elements, int nelements); +typedef int64_t (*add_vector_func_t)(int64_t* data, int n); class TestEngine : public ::testing::Test { protected: - llvm::Function* BuildVecAdd(Engine* engine, LLVMTypes* types); -}; + llvm::Function* BuildVecAdd(Engine* engine) { + auto types = engine->types(); + llvm::IRBuilder<>* builder = engine->ir_builder(); + llvm::LLVMContext* context = engine->context(); + + // Create fn prototype : + // int64_t add_longs(int64_t *elements, int32_t nelements) + std::vector arguments; + arguments.push_back(types->i64_ptr_type()); + arguments.push_back(types->i32_type()); + llvm::FunctionType* prototype = + llvm::FunctionType::get(types->i64_type(), arguments, false /*isVarArg*/); + + // Create fn + std::string func_name = "add_longs"; + engine->AddFunctionToCompile(func_name); + llvm::Function* fn = llvm::Function::Create( + prototype, llvm::GlobalValue::ExternalLinkage, func_name, engine->module()); + assert(fn != nullptr); + + // Name the arguments + llvm::Function::arg_iterator args = fn->arg_begin(); + llvm::Value* arg_elements = &*args; + arg_elements->setName("elements"); + ++args; + llvm::Value* arg_nelements = &*args; + arg_nelements->setName("nelements"); + ++args; + + llvm::BasicBlock* loop_entry = llvm::BasicBlock::Create(*context, "entry", fn); + llvm::BasicBlock* loop_body = llvm::BasicBlock::Create(*context, "loop", fn); + llvm::BasicBlock* loop_exit = llvm::BasicBlock::Create(*context, "exit", fn); + + // Loop entry + builder->SetInsertPoint(loop_entry); + builder->CreateBr(loop_body); + + // Loop body + builder->SetInsertPoint(loop_body); + + llvm::PHINode* loop_var = builder->CreatePHI(types->i32_type(), 2, "loop_var"); + llvm::PHINode* sum = builder->CreatePHI(types->i64_type(), 2, "sum"); + + loop_var->addIncoming(types->i32_constant(0), loop_entry); + sum->addIncoming(types->i64_constant(0), loop_entry); + + // setup loop PHI + llvm::Value* loop_update = + builder->CreateAdd(loop_var, types->i32_constant(1), "loop_var+1"); + loop_var->addIncoming(loop_update, loop_body); + + // get the current value + llvm::Value* offset = builder->CreateGEP(arg_elements, loop_var, "offset"); + llvm::Value* current_value = builder->CreateLoad(offset, "value"); + + // setup sum PHI + llvm::Value* sum_update = builder->CreateAdd(sum, current_value, "sum+ith"); + sum->addIncoming(sum_update, loop_body); + + // check loop_var + llvm::Value* loop_var_check = + builder->CreateICmpSLT(loop_update, arg_nelements, "loop_var < nrec"); + builder->CreateCondBr(loop_var_check, loop_body, loop_exit); + + // Loop exit + builder->SetInsertPoint(loop_exit); + builder->CreateRet(sum_update); + return fn; + } + + void BuildEngine() { ASSERT_OK(Engine::Make(TestConfiguration(), &engine)); } -llvm::Function* TestEngine::BuildVecAdd(Engine* engine, LLVMTypes* types) { - llvm::IRBuilder<>* builder = engine->ir_builder(); - llvm::LLVMContext* context = engine->context(); - - // Create fn prototype : - // int64_t add_longs(int64_t *elements, int32_t nelements) - std::vector arguments; - arguments.push_back(types->i64_ptr_type()); - arguments.push_back(types->i32_type()); - llvm::FunctionType* prototype = - llvm::FunctionType::get(types->i64_type(), arguments, false /*isVarArg*/); - - // Create fn - std::string func_name = "add_longs"; - engine->AddFunctionToCompile(func_name); - llvm::Function* fn = llvm::Function::Create( - prototype, llvm::GlobalValue::ExternalLinkage, func_name, engine->module()); - assert(fn != NULL); - - // Name the arguments - llvm::Function::arg_iterator args = fn->arg_begin(); - llvm::Value* arg_elements = &*args; - arg_elements->setName("elements"); - ++args; - llvm::Value* arg_nelements = &*args; - arg_nelements->setName("nelements"); - ++args; - - llvm::BasicBlock* loop_entry = llvm::BasicBlock::Create(*context, "entry", fn); - llvm::BasicBlock* loop_body = llvm::BasicBlock::Create(*context, "loop", fn); - llvm::BasicBlock* loop_exit = llvm::BasicBlock::Create(*context, "exit", fn); - - // Loop entry - builder->SetInsertPoint(loop_entry); - builder->CreateBr(loop_body); - - // Loop body - builder->SetInsertPoint(loop_body); - - llvm::PHINode* loop_var = builder->CreatePHI(types->i32_type(), 2, "loop_var"); - llvm::PHINode* sum = builder->CreatePHI(types->i64_type(), 2, "sum"); - - loop_var->addIncoming(types->i32_constant(0), loop_entry); - sum->addIncoming(types->i64_constant(0), loop_entry); - - // setup loop PHI - llvm::Value* loop_update = - builder->CreateAdd(loop_var, types->i32_constant(1), "loop_var+1"); - loop_var->addIncoming(loop_update, loop_body); - - // get the current value - llvm::Value* offset = builder->CreateGEP(arg_elements, loop_var, "offset"); - llvm::Value* current_value = builder->CreateLoad(offset, "value"); - - // setup sum PHI - llvm::Value* sum_update = builder->CreateAdd(sum, current_value, "sum+ith"); - sum->addIncoming(sum_update, loop_body); - - // check loop_var - llvm::Value* loop_var_check = - builder->CreateICmpSLT(loop_update, arg_nelements, "loop_var < nrec"); - builder->CreateCondBr(loop_var_check, loop_body, loop_exit); - - // Loop exit - builder->SetInsertPoint(loop_exit); - builder->CreateRet(sum_update); - return fn; -} + std::unique_ptr engine; + std::shared_ptr configuration = TestConfiguration(); +}; TEST_F(TestEngine, TestAddUnoptimised) { - std::unique_ptr engine; - auto status = Engine::Make(TestConfiguration(), &engine); - EXPECT_TRUE(status.ok()) << status.message(); - LLVMTypes types(*engine->context()); - llvm::Function* ir_func = BuildVecAdd(engine.get(), &types); - status = engine->FinalizeModule(false, false); - EXPECT_TRUE(status.ok()) << status.message(); - add_vector_func_t add_func = - reinterpret_cast(engine->CompiledFunction(ir_func)); + configuration->set_optimize(false); + BuildEngine(); + + llvm::Function* ir_func = BuildVecAdd(engine.get()); + ASSERT_OK(engine->FinalizeModule()); + auto add_func = reinterpret_cast(engine->CompiledFunction(ir_func)); int64_t my_array[] = {1, 3, -5, 8, 10}; EXPECT_EQ(add_func(my_array, 5), 17); } TEST_F(TestEngine, TestAddOptimised) { - std::unique_ptr engine; - auto status = Engine::Make(TestConfiguration(), &engine); - EXPECT_TRUE(status.ok()) << status.message(); - LLVMTypes types(*engine->context()); - llvm::Function* ir_func = BuildVecAdd(engine.get(), &types); - status = engine->FinalizeModule(true, false); - EXPECT_TRUE(status.ok()) << status.message(); - - add_vector_func_t add_func = - reinterpret_cast(engine->CompiledFunction(ir_func)); + configuration->set_optimize(true); + BuildEngine(); + + llvm::Function* ir_func = BuildVecAdd(engine.get()); + ASSERT_OK(engine->FinalizeModule()); + auto add_func = reinterpret_cast(engine->CompiledFunction(ir_func)); int64_t my_array[] = {1, 3, -5, 8, 10}; EXPECT_EQ(add_func(my_array, 5), 17); diff --git a/cpp/src/gandiva/filter.cc b/cpp/src/gandiva/filter.cc index 3d3e5d04e273f..7efe2c2a1f8e0 100644 --- a/cpp/src/gandiva/filter.cc +++ b/cpp/src/gandiva/filter.cc @@ -103,4 +103,6 @@ Status Filter::Evaluate(const arrow::RecordBatch& batch, return out_selection->PopulateFromBitMap(result, bitmap_size, num_rows - 1); } +std::string Filter::DumpIR() { return llvm_generator_->DumpIR(); } + } // namespace gandiva diff --git a/cpp/src/gandiva/filter.h b/cpp/src/gandiva/filter.h index 4fbda806e0af9..4f6f8f7151797 100644 --- a/cpp/src/gandiva/filter.h +++ b/cpp/src/gandiva/filter.h @@ -76,10 +76,12 @@ class GANDIVA_EXPORT Filter { Status Evaluate(const arrow::RecordBatch& batch, std::shared_ptr out_selection); + std::string DumpIR(); + private: - const std::unique_ptr llvm_generator_; - const SchemaPtr schema_; - const std::shared_ptr configuration_; + std::unique_ptr llvm_generator_; + SchemaPtr schema_; + std::shared_ptr configuration_; }; } // namespace gandiva diff --git a/cpp/src/gandiva/llvm_generator.cc b/cpp/src/gandiva/llvm_generator.cc index f0f1c7c42cd3c..ee156107c1aa1 100644 --- a/cpp/src/gandiva/llvm_generator.cc +++ b/cpp/src/gandiva/llvm_generator.cc @@ -39,8 +39,7 @@ namespace gandiva { AddTrace(__VA_ARGS__); \ } -LLVMGenerator::LLVMGenerator() - : dump_ir_(false), optimise_ir_(true), enable_ir_traces_(false) {} +LLVMGenerator::LLVMGenerator() : enable_ir_traces_(false) {} Status LLVMGenerator::Make(std::shared_ptr config, std::unique_ptr* llvm_generator) { @@ -77,16 +76,17 @@ Status LLVMGenerator::Build(const ExpressionVector& exprs, SelectionVector::Mode auto output = annotator_.AddOutputFieldDescriptor(expr->result()); ARROW_RETURN_NOT_OK(Add(expr, output)); } - // optimise, compile and finalize the module - ARROW_RETURN_NOT_OK(engine_->FinalizeModule(optimise_ir_, dump_ir_)); + + // Compile and inject into the process' memory the generated function. + ARROW_RETURN_NOT_OK(engine_->FinalizeModule()); // setup the jit functions for each expression. for (auto& compiled_expr : compiled_exprs_) { - auto ir_function = compiled_expr->GetIRFunction(mode); - auto jit_function = - reinterpret_cast(engine_->CompiledFunction(ir_function)); - compiled_expr->SetJITFunction(selection_vector_mode_, jit_function); + auto ir_fn = compiled_expr->GetIRFunction(mode); + auto jit_fn = reinterpret_cast(engine_->CompiledFunction(ir_fn)); + compiled_expr->SetJITFunction(selection_vector_mode_, jit_fn); } + return Status::OK(); } @@ -143,10 +143,9 @@ Status LLVMGenerator::Execute(const arrow::RecordBatch& record_batch, llvm::Value* LLVMGenerator::LoadVectorAtIndex(llvm::Value* arg_addrs, int idx, const std::string& name) { - llvm::IRBuilder<>* builder = ir_builder(); - llvm::Value* offset = - builder->CreateGEP(arg_addrs, types()->i32_constant(idx), name + "_mem_addr"); - return builder->CreateLoad(offset, name + "_mem"); + auto* idx_val = types()->i32_constant(idx); + auto* offset = ir_builder()->CreateGEP(arg_addrs, idx_val, name + "_mem_addr"); + return ir_builder()->CreateLoad(offset, name + "_mem"); } /// Get reference to validity array at specified index in the args list. @@ -268,7 +267,7 @@ Status LLVMGenerator::CodeGenExprValue(DexPtr value_expr, int buffer_count, case SelectionVector::MODE_UINT64: arguments.push_back(types()->i64_ptr_type()); } - arguments.push_back(types()->i64_type()); // ctxt_ptr + arguments.push_back(types()->i64_type()); // ctx_ptr arguments.push_back(types()->i64_type()); // nrec llvm::FunctionType* prototype = llvm::FunctionType::get(types()->i32_type(), arguments, false /*isVarArg*/); @@ -284,10 +283,10 @@ Status LLVMGenerator::CodeGenExprValue(DexPtr value_expr, int buffer_count, // Name the arguments llvm::Function::arg_iterator args = (*fn)->arg_begin(); llvm::Value* arg_addrs = &*args; - arg_addrs->setName("args"); + arg_addrs->setName("inputs_addr"); ++args; llvm::Value* arg_addr_offsets = &*args; - arg_addr_offsets->setName("arg_addr_offsets"); + arg_addr_offsets->setName("inputs_addr_offsets"); ++args; llvm::Value* arg_local_bitmaps = &*args; arg_local_bitmaps->setName("local_bitmaps"); diff --git a/cpp/src/gandiva/llvm_generator.h b/cpp/src/gandiva/llvm_generator.h index be7f6667dcbd2..3ed414fec0cb2 100644 --- a/cpp/src/gandiva/llvm_generator.h +++ b/cpp/src/gandiva/llvm_generator.h @@ -74,6 +74,7 @@ class GANDIVA_EXPORT LLVMGenerator { SelectionVector::Mode selection_vector_mode() { return selection_vector_mode_; } LLVMTypes* types() { return engine_->types(); } llvm::Module* module() { return engine_->module(); } + std::string DumpIR() { return engine_->DumpIR(); } private: LLVMGenerator(); @@ -243,8 +244,6 @@ class GANDIVA_EXPORT LLVMGenerator { SelectionVector::Mode selection_vector_mode_; // used for debug - bool dump_ir_; - bool optimise_ir_; bool enable_ir_traces_; std::vector trace_strings_; }; diff --git a/cpp/src/gandiva/llvm_generator_test.cc b/cpp/src/gandiva/llvm_generator_test.cc index 7983005cf37f3..bdc3b0051691f 100644 --- a/cpp/src/gandiva/llvm_generator_test.cc +++ b/cpp/src/gandiva/llvm_generator_test.cc @@ -20,6 +20,7 @@ #include #include +#include #include #include "gandiva/configuration.h" #include "gandiva/dex.h" @@ -40,22 +41,18 @@ class TestLLVMGenerator : public ::testing::Test { // Verify that a valid pc function exists for every function in the registry. TEST_F(TestLLVMGenerator, VerifyPCFunctions) { std::unique_ptr generator; - auto status = LLVMGenerator::Make(TestConfiguration(), &generator); - EXPECT_TRUE(status.ok()) << status.message(); + ASSERT_OK(LLVMGenerator::Make(TestConfiguration(), &generator)); llvm::Module* module = generator->module(); for (auto& iter : registry_) { - llvm::Function* fn = module->getFunction(iter.pc_name()); - EXPECT_NE(fn, nullptr) << "function " << iter.pc_name() - << " missing in precompiled module\n"; + EXPECT_NE(module->getFunction(iter.pc_name()), nullptr); } } TEST_F(TestLLVMGenerator, TestAdd) { // Setup LLVM generator to do an arithmetic add of two vectors std::unique_ptr generator; - auto status = LLVMGenerator::Make(TestConfiguration(), &generator); - EXPECT_TRUE(status.ok()); + ASSERT_OK(LLVMGenerator::Make(TestConfiguration(), &generator)); Annotator annotator; auto field0 = std::make_shared("f0", arrow::int32()); @@ -86,39 +83,34 @@ TEST_F(TestLLVMGenerator, TestAdd) { llvm::Function* ir_func = nullptr; - status = generator->CodeGenExprValue(func_dex, 4, desc_sum, 0, &ir_func, - SelectionVector::MODE_NONE); - EXPECT_TRUE(status.ok()) << status.message(); + ASSERT_OK(generator->CodeGenExprValue(func_dex, 4, desc_sum, 0, &ir_func, + SelectionVector::MODE_NONE)); - std::string final_ir; - status = generator->engine_->FinalizeModule(true, false, &final_ir); - EXPECT_TRUE(status.ok()) << status.message(); - EXPECT_TRUE(final_ir.find("vector.body") != std::string::npos) - << "missing vectorization: " << final_ir; + ASSERT_OK(generator->engine_->FinalizeModule()); + auto ir = generator->engine_->DumpIR(); + EXPECT_THAT(ir, testing::HasSubstr("vector.body")); EvalFunc eval_func = (EvalFunc)generator->engine_->CompiledFunction(ir_func); - int num_records = 4; - uint32_t a0[] = {1, 2, 3, 4}; - uint32_t a1[] = {5, 6, 7, 8}; + constexpr size_t kNumRecords = 4; + std::array a0{1, 2, 3, 4}; + std::array a1{5, 6, 7, 8}; uint64_t in_bitmap = 0xffffffffffffffffull; - uint32_t out[] = {0, 0, 0, 0}; + std::array out{0, 0, 0, 0}; uint64_t out_bitmap = 0; - uint8_t* addrs[] = { - reinterpret_cast(a0), reinterpret_cast(&in_bitmap), - reinterpret_cast(a1), reinterpret_cast(&in_bitmap), - reinterpret_cast(out), reinterpret_cast(&out_bitmap), + std::array addrs{ + reinterpret_cast(a0.data()), reinterpret_cast(&in_bitmap), + reinterpret_cast(a1.data()), reinterpret_cast(&in_bitmap), + reinterpret_cast(out.data()), reinterpret_cast(&out_bitmap), }; - int64_t addr_offsets[] = {0, 0, 0, 0, 0, 0}; - eval_func(addrs, addr_offsets, nullptr, nullptr, 0 /* dummy context ptr */, - num_records); + std::array addr_offsets{0, 0, 0, 0, 0, 0}; + eval_func(addrs.data(), addr_offsets.data(), nullptr, nullptr, + 0 /* dummy context ptr */, kNumRecords); - uint32_t expected[] = {6, 8, 10, 12}; - for (int i = 0; i < num_records; i++) { - EXPECT_EQ(expected[i], out[i]); - } + EXPECT_THAT(out, testing::ElementsAre(6, 8, 10, 12)); + EXPECT_EQ(out_bitmap, 0ULL); } } // namespace gandiva diff --git a/cpp/src/gandiva/llvm_types.h b/cpp/src/gandiva/llvm_types.h index 2629d326c3590..b2d325b1a38b9 100644 --- a/cpp/src/gandiva/llvm_types.h +++ b/cpp/src/gandiva/llvm_types.h @@ -33,6 +33,8 @@ class GANDIVA_EXPORT LLVMTypes { public: explicit LLVMTypes(llvm::LLVMContext& context); + llvm::Type* void_type() { return llvm::Type::getVoidTy(context_); } + llvm::Type* i1_type() { return llvm::Type::getInt1Ty(context_); } llvm::Type* i8_type() { return llvm::Type::getInt8Ty(context_); } @@ -45,68 +47,42 @@ class GANDIVA_EXPORT LLVMTypes { llvm::Type* i128_type() { return llvm::Type::getInt128Ty(context_); } - llvm::Type* float_type() { return llvm::Type::getFloatTy(context_); } - - llvm::Type* double_type() { return llvm::Type::getDoubleTy(context_); } - - llvm::PointerType* i8_ptr_type() { return llvm::PointerType::get(i8_type(), 0); } - - llvm::PointerType* i32_ptr_type() { return llvm::PointerType::get(i32_type(), 0); } - - llvm::PointerType* i64_ptr_type() { return llvm::PointerType::get(i64_type(), 0); } - - llvm::PointerType* i128_ptr_type() { return llvm::PointerType::get(i128_type(), 0); } - llvm::StructType* i128_split_type() { // struct with high/low bits (see decimal_ops.cc:DecimalSplit) return llvm::StructType::get(context_, {i64_type(), i64_type()}, false); } - llvm::Type* void_type() { return llvm::Type::getVoidTy(context_); } - - llvm::PointerType* ptr_type(llvm::Type* base_type) { - return llvm::PointerType::get(base_type, 0); - } + llvm::Type* float_type() { return llvm::Type::getFloatTy(context_); } - llvm::Constant* true_constant() { - return llvm::ConstantInt::get(context_, llvm::APInt(1, 1)); - } + llvm::Type* double_type() { return llvm::Type::getDoubleTy(context_); } - llvm::Constant* false_constant() { - return llvm::ConstantInt::get(context_, llvm::APInt(1, 0)); - } + llvm::PointerType* ptr_type(llvm::Type* type) { return type->getPointerTo(); } - llvm::Constant* i1_constant(bool val) { - return llvm::ConstantInt::get(context_, llvm::APInt(1, val)); - } + llvm::PointerType* i8_ptr_type() { return ptr_type(i8_type()); } - llvm::Constant* i8_constant(bool val) { - return llvm::ConstantInt::get(context_, llvm::APInt(8, val)); - } + llvm::PointerType* i32_ptr_type() { return ptr_type(i32_type()); } - llvm::Constant* i16_constant(bool val) { - return llvm::ConstantInt::get(context_, llvm::APInt(16, val)); - } + llvm::PointerType* i64_ptr_type() { return ptr_type(i64_type()); } - llvm::Constant* i32_constant(int32_t val) { - return llvm::ConstantInt::get(context_, llvm::APInt(32, val)); - } + llvm::PointerType* i128_ptr_type() { return ptr_type(i128_type()); } - llvm::Constant* i64_constant(int64_t val) { - return llvm::ConstantInt::get(context_, llvm::APInt(64, val)); + template + llvm::Constant* int_constant(ctype val) { + return llvm::ConstantInt::get(context_, llvm::APInt(N, val)); } - llvm::Constant* i128_constant(int64_t val) { - return llvm::ConstantInt::get(context_, llvm::APInt(128, val)); - } + llvm::Constant* i1_constant(bool val) { return int_constant(val); } + llvm::Constant* i8_constant(int8_t val) { return int_constant(val); } + llvm::Constant* i16_constant(int16_t val) { return int_constant(val); } + llvm::Constant* i32_constant(int32_t val) { return int_constant(val); } + llvm::Constant* i64_constant(int64_t val) { return int_constant(val); } + llvm::Constant* i128_constant(int64_t val) { return int_constant(val); } - llvm::Constant* i128_zero() { - return llvm::ConstantInt::get(context_, llvm::APInt(128, 0)); - } + llvm::Constant* true_constant() { return i1_constant(true); } + llvm::Constant* false_constant() { return i1_constant(false); } - llvm::Constant* i128_one() { - return llvm::ConstantInt::get(context_, llvm::APInt(128, 1)); - } + llvm::Constant* i128_zero() { return i128_constant(0); } + llvm::Constant* i128_one() { return i128_constant(1); } llvm::Constant* float_constant(float val) { return llvm::ConstantFP::get(float_type(), val); diff --git a/cpp/src/gandiva/projector.cc b/cpp/src/gandiva/projector.cc index 86a6e33a64718..295a8ede9c33a 100644 --- a/cpp/src/gandiva/projector.cc +++ b/cpp/src/gandiva/projector.cc @@ -267,4 +267,6 @@ Status Projector::ValidateArrayDataCapacity(const arrow::ArrayData& array_data, return Status::OK(); } +std::string Projector::DumpIR() { return llvm_generator_->DumpIR(); } + } // namespace gandiva diff --git a/cpp/src/gandiva/projector.h b/cpp/src/gandiva/projector.h index dbbcf5914aa12..a5a5a37d65528 100644 --- a/cpp/src/gandiva/projector.h +++ b/cpp/src/gandiva/projector.h @@ -117,6 +117,8 @@ class GANDIVA_EXPORT Projector { Status Evaluate(const arrow::RecordBatch& batch, const SelectionVector* selection_vector, const ArrayDataVector& output); + std::string DumpIR(); + private: Projector(std::unique_ptr llvm_generator, SchemaPtr schema, const FieldVector& output_fields, std::shared_ptr); @@ -132,10 +134,10 @@ class GANDIVA_EXPORT Projector { /// Validate the common args for Evaluate() APIs. Status ValidateEvaluateArgsCommon(const arrow::RecordBatch& batch); - const std::unique_ptr llvm_generator_; - const SchemaPtr schema_; - const FieldVector output_fields_; - const std::shared_ptr configuration_; + std::unique_ptr llvm_generator_; + SchemaPtr schema_; + FieldVector output_fields_; + std::shared_ptr configuration_; }; } // namespace gandiva diff --git a/python/pyarrow/gandiva.pyx b/python/pyarrow/gandiva.pyx index 3904a8a8297ae..380fa98454e97 100644 --- a/python/pyarrow/gandiva.pyx +++ b/python/pyarrow/gandiva.pyx @@ -147,6 +147,10 @@ cdef class Projector: self.pool = pool return self + @property + def llvm_ir(self): + return self.projector.get().DumpIR().decode() + def evaluate(self, RecordBatch batch): cdef vector[shared_ptr[CArray]] results check_status(self.projector.get().Evaluate( @@ -172,6 +176,10 @@ cdef class Filter: self.filter = filter return self + @property + def llvm_ir(self): + return self.filter.get().DumpIR().decode() + def evaluate(self, RecordBatch batch, MemoryPool pool, dtype='int32'): cdef: DataType type = ensure_type(dtype) diff --git a/python/pyarrow/includes/libgandiva.pxd b/python/pyarrow/includes/libgandiva.pxd index fc73872128685..ce1aa4639d852 100644 --- a/python/pyarrow/includes/libgandiva.pxd +++ b/python/pyarrow/includes/libgandiva.pxd @@ -172,11 +172,6 @@ cdef extern from "gandiva/tree_expr_builder.h" namespace "gandiva" nogil: "gandiva::TreeExprBuilder::MakeInExpressionBinary"( shared_ptr[CNode] node, const c_unordered_set[c_string]& values) - cdef CStatus Projector_Make \ - "gandiva::Projector::Make"( - shared_ptr[CSchema] schema, const CExpressionVector& children, - shared_ptr[CProjector]* projector) - cdef extern from "gandiva/projector.h" namespace "gandiva" nogil: cdef cppclass CProjector" gandiva::Projector": @@ -185,6 +180,13 @@ cdef extern from "gandiva/projector.h" namespace "gandiva" nogil: const CRecordBatch& batch, CMemoryPool* pool, const CArrayVector* output) + c_string DumpIR() + + cdef CStatus Projector_Make \ + "gandiva::Projector::Make"( + shared_ptr[CSchema] schema, const CExpressionVector& children, + shared_ptr[CProjector]* projector) + cdef extern from "gandiva/filter.h" namespace "gandiva" nogil: cdef cppclass CFilter" gandiva::Filter": @@ -193,6 +195,8 @@ cdef extern from "gandiva/filter.h" namespace "gandiva" nogil: const CRecordBatch& batch, shared_ptr[CSelectionVector] out_selection) + c_string DumpIR() + cdef CStatus Filter_Make \ "gandiva::Filter::Make"( shared_ptr[CSchema] schema, shared_ptr[CCondition] condition, diff --git a/python/pyarrow/tests/test_gandiva.py b/python/pyarrow/tests/test_gandiva.py index 62636634b365c..2deb2a0df87d5 100644 --- a/python/pyarrow/tests/test_gandiva.py +++ b/python/pyarrow/tests/test_gandiva.py @@ -46,6 +46,9 @@ def test_tree_exp_builder(): projector = gandiva.make_projector( schema, [expr], pa.default_memory_pool()) + # Gandiva generates compute kernel function named `@expr_X` + assert projector.llvm_ir.find("@expr_") != -1 + a = pa.array([10, 12, -20, 5], type=pa.int32()) b = pa.array([5, 15, 15, 17], type=pa.int32()) e = pa.array([10, 15, 15, 17], type=pa.int32()) @@ -96,6 +99,9 @@ def test_filter(): condition = builder.make_condition(cond) filter = gandiva.make_filter(table.schema, condition) + # Gandiva generates compute kernel function named `@expr_X` + assert filter.llvm_ir.find("@expr_") != -1 + result = filter.evaluate(table.to_batches()[0], pa.default_memory_pool()) assert result.to_array().equals(pa.array(range(1000), type=pa.uint32()))