blob: a60e2bf6b29e54a0c422f29421e76f461e548592 [file]
// 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.
#pragma once
#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "arrow/util/macros.h"
#include "gandiva/annotator.h"
#include "gandiva/compiled_expr.h"
#include "gandiva/configuration.h"
#include "gandiva/dex_visitor.h"
#include "gandiva/engine.h"
#include "gandiva/execution_context.h"
#include "gandiva/expr_decomposer.h"
#include "gandiva/expression_cache_key.h"
#include "gandiva/function_registry.h"
#include "gandiva/gandiva_aliases.h"
#include "gandiva/llvm_types.h"
#include "gandiva/lvalue.h"
#include "gandiva/selection_vector.h"
#include "gandiva/value_validity_pair.h"
#include "gandiva/visibility.h"
namespace gandiva {
class FunctionHolder;
/// Builds an LLVM module and generates code for the specified set of expressions.
class GANDIVA_EXPORT LLVMGenerator {
public:
/// \brief Factory method to initialize the generator.
static Result<std::unique_ptr<LLVMGenerator>> Make(
const std::shared_ptr<Configuration>& config, bool cached,
std::optional<std::reference_wrapper<GandivaObjectCache>> object_cache =
std::nullopt);
/// \brief Get the cache to be used for LLVM ObjectCache.
static std::shared_ptr<Cache<ExpressionCacheKey, std::shared_ptr<llvm::MemoryBuffer>>>
GetCache();
/// \brief Set LLVM ObjectCache.
Status SetLLVMObjectCache(GandivaObjectCache& object_cache);
/// \brief Build the code for the expression trees for default mode with a LLVM
/// ObjectCache. Each element in the vector represents an expression tree
Status Build(const ExpressionVector& exprs, SelectionVector::Mode mode);
/// \brief Build the code for the expression trees for default mode. Each
/// element in the vector represents an expression tree
Status Build(const ExpressionVector& exprs);
/// \brief Execute the built expression against the provided arguments for
/// default mode.
Status Execute(const arrow::RecordBatch& record_batch,
const ArrayDataVector& output_vector) const;
/// \brief Execute the built expression against the provided arguments for
/// all modes. Only works on the records specified in the selection_vector.
Status Execute(const arrow::RecordBatch& record_batch,
const SelectionVector* selection_vector,
const ArrayDataVector& output_vector) const;
SelectionVector::Mode selection_vector_mode() { return selection_vector_mode_; }
LLVMTypes* types() { return engine_->types(); }
llvm::Module* module() { return engine_->module(); }
const std::string& ir() { return engine_->ir(); }
private:
explicit LLVMGenerator(bool cached,
std::shared_ptr<FunctionRegistry> function_registry);
FRIEND_TEST(TestLLVMGenerator, VerifyPCFunctions);
FRIEND_TEST(TestLLVMGenerator, TestAdd);
FRIEND_TEST(TestLLVMGenerator, TestNullInternal);
friend class TestLLVMGenerator;
llvm::LLVMContext* context() { return engine_->context(); }
llvm::IRBuilder<>* ir_builder() { return engine_->ir_builder(); }
llvm::Constant* CreateGlobalStringPtr(const std::string& string) {
return engine_->CreateGlobalStringPtr(string);
}
/// Visitor to generate the code for a decomposed expression.
class Visitor : public DexVisitor {
public:
Visitor(LLVMGenerator* generator, llvm::Function* function,
llvm::BasicBlock* entry_block, llvm::Value* arg_addrs,
llvm::Value* arg_local_bitmaps, llvm::Value* arg_holder_ptrs,
std::vector<llvm::Value*> slice_offsets, llvm::Value* arg_context_ptr,
llvm::Value* loop_var);
void Visit(const VectorReadValidityDex& dex) override;
void Visit(const VectorReadFixedLenValueDex& dex) override;
void Visit(const VectorReadVarLenValueDex& dex) override;
void Visit(const LocalBitMapValidityDex& dex) override;
void Visit(const TrueDex& dex) override;
void Visit(const FalseDex& dex) override;
void Visit(const LiteralDex& dex) override;
void Visit(const NonNullableFuncDex& dex) override;
void Visit(const NullableNeverFuncDex& dex) override;
void Visit(const NullableInternalFuncDex& dex) override;
void Visit(const IfDex& dex) override;
void Visit(const BooleanAndDex& dex) override;
void Visit(const BooleanOrDex& dex) override;
void Visit(const InExprDexBase<int32_t>& dex) override;
void Visit(const InExprDexBase<int64_t>& dex) override;
void Visit(const InExprDexBase<float>& dex) override;
void Visit(const InExprDexBase<double>& dex) override;
void Visit(const InExprDexBase<gandiva::DecimalScalar128>& dex) override;
void Visit(const InExprDexBase<std::string>& dex) override;
template <typename Type>
void VisitInExpression(const InExprDexBase<Type>& dex);
LValuePtr result() { return result_; }
bool has_arena_allocs() { return has_arena_allocs_; }
private:
enum BufferType { kBufferTypeValidity = 0, kBufferTypeData, kBufferTypeOffsets };
llvm::IRBuilder<>* ir_builder() { return generator_->ir_builder(); }
llvm::Module* module() { return generator_->module(); }
llvm::Constant* CreateGlobalStringPtr(const std::string& string) {
return generator_->CreateGlobalStringPtr(string);
}
// Generate the code to build the combined validity (bitwise and) from the
// vector of validities.
llvm::Value* BuildCombinedValidity(const DexVector& validities);
// Generate the code to build the validity and the value for the given pair.
LValuePtr BuildValueAndValidity(const ValueValidityPair& pair);
// Generate code to build the params.
std::vector<llvm::Value*> BuildParams(int holder_idx,
const ValueValidityPairVector& args,
bool with_validity, bool with_context);
// Generate code to invoke a function call.
LValuePtr BuildFunctionCall(const NativeFunction* func, DataTypePtr arrow_return_type,
std::vector<llvm::Value*>* params);
// Generate code for an if-else condition.
LValuePtr BuildIfElse(llvm::Value* condition, std::function<LValuePtr()> then_func,
std::function<LValuePtr()> else_func,
DataTypePtr arrow_return_type);
// Switch to the entry_block and get reference of the validity/value/offsets buffer
llvm::Value* GetBufferReference(int idx, BufferType buffer_type, FieldPtr field);
// Get the slice offset of the validity/value/offsets buffer
llvm::Value* GetSliceOffset(int idx);
// Switch to the entry_block and get reference to the local bitmap.
llvm::Value* GetLocalBitMapReference(int idx);
// Clear the bit in the local bitmap, if is_valid is 'false'
void ClearLocalBitMapIfNotValid(int local_bitmap_idx, llvm::Value* is_valid);
LLVMGenerator* generator_;
LValuePtr result_;
llvm::Function* function_;
llvm::BasicBlock* entry_block_;
llvm::Value* arg_addrs_;
llvm::Value* arg_local_bitmaps_;
llvm::Value* arg_holder_ptrs_;
std::vector<llvm::Value*> slice_offsets_;
llvm::Value* arg_context_ptr_;
llvm::Value* loop_var_;
bool has_arena_allocs_;
};
// Generate the code for one expression for default mode, with the output of
// the expression going to 'output'.
Status Add(const ExpressionPtr expr, const FieldDescriptorPtr output);
/// Generate code to load the vector at specified index in the 'arg_addrs' array.
llvm::Value* LoadVectorAtIndex(llvm::Value* arg_addrs, llvm::Type* type, int idx,
const std::string& name);
/// Generate code to load the vector at specified index and cast it as bitmap.
llvm::Value* GetValidityReference(llvm::Value* arg_addrs, int idx, FieldPtr field);
/// Generate code to load the vector at specified index and cast it as data array.
llvm::Value* GetDataReference(llvm::Value* arg_addrs, int idx, FieldPtr field);
/// Generate code to load the vector at specified index and cast it as offsets array.
llvm::Value* GetOffsetsReference(llvm::Value* arg_addrs, int idx, FieldPtr field);
/// Generate code to load the vector at specified index and cast it as buffer pointer.
llvm::Value* GetDataBufferPtrReference(llvm::Value* arg_addrs, int idx, FieldPtr field);
/// Generate code for the value array of one expression.
Status CodeGenExprValue(DexPtr value_expr, int num_buffers, FieldDescriptorPtr output,
int suffix_idx, std::string& fn_name,
SelectionVector::Mode selection_vector_mode);
/// Generate code to load the local bitmap specified index and cast it as bitmap.
llvm::Value* GetLocalBitMapReference(llvm::Value* arg_bitmaps, int idx);
/// Generate code to get the bit value at 'position' in the bitmap.
llvm::Value* GetPackedBitValue(llvm::Value* bitmap, llvm::Value* position);
/// Generate code to get the bit value at 'position' in the validity bitmap.
llvm::Value* GetPackedValidityBitValue(llvm::Value* bitmap, llvm::Value* position);
/// Generate code to set the bit value at 'position' in the bitmap to 'value'.
void SetPackedBitValue(llvm::Value* bitmap, llvm::Value* position, llvm::Value* value);
/// Generate code to clear the bit value at 'position' in the bitmap if 'value'
/// is false.
void ClearPackedBitValueIfFalse(llvm::Value* bitmap, llvm::Value* position,
llvm::Value* value);
// Generate code to build a DecimalLValue with specified value/precision/scale.
std::shared_ptr<DecimalLValue> BuildDecimalLValue(llvm::Value* value,
DataTypePtr arrow_type);
/// Generate code to make a function call (to a pre-compiled IR function) which takes
/// 'args' and has a return type 'ret_type'.
llvm::Value* AddFunctionCall(const std::string& full_name, llvm::Type* ret_type,
const std::vector<llvm::Value*>& args);
/// Compute the result bitmap for the expression.
///
/// \param[in] compiled_expr the compiled expression (includes the bitmap indices to be
/// used for computing the validity bitmap of the result).
/// \param[in] selection_vector the list of selected positions
/// \param[in,out] eval_batch (includes input/output buffer addresses)
void ComputeBitMapsForExpr(const CompiledExpr& compiled_expr,
const SelectionVector* selection_vector,
EvalBatch* eval_batch) const;
/// Replace the %T in the trace msg with the correct type corresponding to 'type'
/// eg. %d for int32, %ld for int64, ..
std::string ReplaceFormatInTrace(const std::string& msg, llvm::Value* value,
std::string* print_fn);
/// Generate the code to print a trace msg with one optional argument (%T)
void AddTrace(const std::string& msg, llvm::Value* value = NULLPTR);
std::unique_ptr<Engine> engine_;
std::vector<std::unique_ptr<CompiledExpr>> compiled_exprs_;
bool cached_;
std::shared_ptr<FunctionRegistry> function_registry_;
Annotator annotator_;
SelectionVector::Mode selection_vector_mode_;
// used for debug
bool enable_ir_traces_;
std::vector<std::string> trace_strings_;
};
} // namespace gandiva