| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| #include "gandiva/llvm_generator.h" |
| |
| #include <memory> |
| #include <vector> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| #include "gandiva/configuration.h" |
| #include "gandiva/dex.h" |
| #include "gandiva/expression.h" |
| #include "gandiva/func_descriptor.h" |
| #include "gandiva/function_registry.h" |
| #include "gandiva/tests/test_util.h" |
| |
| namespace gandiva { |
| |
| typedef int64_t (*add_vector_func_t)(int64_t* elements, int nelements); |
| |
| class TestLLVMGenerator : public ::testing::Test { |
| protected: |
| std::shared_ptr<FunctionRegistry> registry_ = default_function_registry(); |
| |
| public: |
| // create a Configuration with the given registry and verify that the given function |
| // exists in the module. |
| static void VerifyFunctionMapping( |
| const std::string& function_name, |
| const std::function<std::shared_ptr<Configuration>( |
| std::shared_ptr<FunctionRegistry>)>& config_factory) { |
| auto external_registry = std::make_shared<FunctionRegistry>(); |
| auto config = config_factory(std::move(external_registry)); |
| |
| ASSERT_OK_AND_ASSIGN(auto generator, LLVMGenerator::Make(config, false)); |
| |
| auto module = generator->module(); |
| ASSERT_OK(generator->engine_->LoadFunctionIRs()); |
| EXPECT_NE(module->getFunction(function_name), nullptr); |
| } |
| }; |
| |
| // Verify that a valid pc function exists for every function in the registry. |
| TEST_F(TestLLVMGenerator, VerifyPCFunctions) { |
| ASSERT_OK_AND_ASSIGN(auto generator, LLVMGenerator::Make(TestConfiguration(), false)); |
| |
| llvm::Module* module = generator->module(); |
| ASSERT_OK(generator->engine_->LoadFunctionIRs()); |
| for (auto& iter : *registry_) { |
| EXPECT_NE(module->getFunction(iter.pc_name()), nullptr); |
| } |
| } |
| |
| TEST_F(TestLLVMGenerator, TestAdd) { |
| // Setup LLVM generator to do an arithmetic add of two vectors |
| ASSERT_OK_AND_ASSIGN(auto generator, |
| LLVMGenerator::Make(TestConfigWithIrDumping(), false)); |
| Annotator annotator; |
| |
| auto field0 = std::make_shared<arrow::Field>("f0", arrow::int32()); |
| auto desc0 = annotator.CheckAndAddInputFieldDescriptor(field0); |
| auto validity_dex0 = std::make_shared<VectorReadValidityDex>(desc0); |
| auto value_dex0 = std::make_shared<VectorReadFixedLenValueDex>(desc0); |
| auto pair0 = std::make_shared<ValueValidityPair>(validity_dex0, value_dex0); |
| |
| auto field1 = std::make_shared<arrow::Field>("f1", arrow::int32()); |
| auto desc1 = annotator.CheckAndAddInputFieldDescriptor(field1); |
| auto validity_dex1 = std::make_shared<VectorReadValidityDex>(desc1); |
| auto value_dex1 = std::make_shared<VectorReadFixedLenValueDex>(desc1); |
| auto pair1 = std::make_shared<ValueValidityPair>(validity_dex1, value_dex1); |
| |
| DataTypeVector params{arrow::int32(), arrow::int32()}; |
| auto func_desc = std::make_shared<FuncDescriptor>("add", params, arrow::int32()); |
| FunctionSignature signature(func_desc->name(), func_desc->params(), |
| func_desc->return_type()); |
| const NativeFunction* native_func = |
| generator->function_registry_->LookupSignature(signature); |
| |
| std::vector<ValueValidityPairPtr> pairs{pair0, pair1}; |
| auto func_dex = std::make_shared<NonNullableFuncDex>( |
| func_desc, native_func, FunctionHolderPtr(nullptr), -1, pairs); |
| |
| auto field_sum = std::make_shared<arrow::Field>("out", arrow::int32()); |
| auto desc_sum = annotator.CheckAndAddInputFieldDescriptor(field_sum); |
| |
| // LLVM 10 doesn't like the expr function name to be the same as the module name when |
| // LLJIT is used |
| std::string fn_name = "llvm_gen_test_add_expr"; |
| |
| ASSERT_OK(generator->engine_->LoadFunctionIRs()); |
| ASSERT_OK(generator->CodeGenExprValue(func_dex, 4, desc_sum, 0, fn_name, |
| SelectionVector::MODE_NONE)); |
| |
| ASSERT_OK(generator->engine_->FinalizeModule()); |
| const auto& ir = generator->engine_->ir(); |
| EXPECT_THAT(ir, testing::HasSubstr("vector.body")); |
| |
| ASSERT_OK_AND_ASSIGN(auto fn_ptr, generator->engine_->CompiledFunction(fn_name)); |
| ASSERT_TRUE(fn_ptr); |
| |
| auto eval_func = reinterpret_cast<EvalFunc>(fn_ptr); |
| constexpr size_t kNumRecords = 4; |
| std::array<uint32_t, kNumRecords> a0{1, 2, 3, 4}; |
| std::array<uint32_t, kNumRecords> a1{5, 6, 7, 8}; |
| uint64_t in_bitmap = 0xffffffffffffffffull; |
| |
| std::array<uint32_t, kNumRecords> out{0, 0, 0, 0}; |
| uint64_t out_bitmap = 0; |
| |
| std::array<uint8_t*, 6> addrs{ |
| reinterpret_cast<uint8_t*>(a0.data()), reinterpret_cast<uint8_t*>(&in_bitmap), |
| reinterpret_cast<uint8_t*>(a1.data()), reinterpret_cast<uint8_t*>(&in_bitmap), |
| reinterpret_cast<uint8_t*>(out.data()), reinterpret_cast<uint8_t*>(&out_bitmap), |
| }; |
| std::array<int64_t, 6> addr_offsets{0, 0, 0, 0, 0, 0}; |
| |
| eval_func(addrs.data(), addr_offsets.data(), nullptr, nullptr, nullptr, |
| 0 /* dummy context ptr */, kNumRecords); |
| |
| EXPECT_THAT(out, testing::ElementsAre(6, 8, 10, 12)); |
| EXPECT_EQ(out_bitmap, 0ULL); |
| } |
| |
| TEST_F(TestLLVMGenerator, VerifyExtendedPCFunctions) { |
| VerifyFunctionMapping("multiply_by_two_int32", [](auto registry) { |
| return TestConfigWithFunctionRegistry(std::move(registry)); |
| }); |
| } |
| |
| TEST_F(TestLLVMGenerator, VerifyExtendedCFunctions) { |
| VerifyFunctionMapping("multiply_by_three_int32", [](auto registry) { |
| return TestConfigWithCFunction(std::move(registry)); |
| }); |
| |
| VerifyFunctionMapping("multiply_by_n_int32_int32", [](auto registry) { |
| return TestConfigWithHolderFunction(std::move(registry)); |
| }); |
| } |
| |
| } // namespace gandiva |