in cpp/src/gandiva/llvm_generator.cc [276:443]
Status LLVMGenerator::CodeGenExprValue(DexPtr value_expr, int buffer_count,
FieldDescriptorPtr output, int suffix_idx,
std::string& fn_name,
SelectionVector::Mode selection_vector_mode) {
llvm::IRBuilder<>* builder = ir_builder();
const auto output_type_id = output->Type()->id();
// Create fn prototype :
// int expr_1 (long **addrs, long *offsets, long **bitmaps,
// long *context_ptr, long nrec)
std::vector<llvm::Type*> arguments;
arguments.push_back(types()->i64_ptr_type()); // addrs
arguments.push_back(types()->i64_ptr_type()); // offsets
arguments.push_back(types()->i64_ptr_type()); // bitmaps
arguments.push_back(types()->i64_ptr_type()); // holders
llvm::Type* selection_vector_type = nullptr;
switch (selection_vector_mode) {
case SelectionVector::MODE_NONE:
case SelectionVector::MODE_UINT16:
arguments.push_back(types()->ptr_type(types()->i16_type()));
selection_vector_type = types()->i16_type();
break;
case SelectionVector::MODE_UINT32:
arguments.push_back(types()->i32_ptr_type());
selection_vector_type = types()->i32_type();
break;
case SelectionVector::MODE_UINT64:
arguments.push_back(types()->i64_ptr_type());
selection_vector_type = types()->i64_type();
break;
}
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*/);
// Create fn
engine_->AddFunctionToCompile(fn_name);
llvm::Function* fn = llvm::Function::Create(
prototype, llvm::GlobalValue::ExternalLinkage, fn_name, module());
ARROW_RETURN_IF((fn == nullptr), Status::CodeGenError("Error creating function."));
// Name the arguments
llvm::Function::arg_iterator args = (fn)->arg_begin();
llvm::Value* arg_addrs = &*args;
arg_addrs->setName("inputs_addr");
++args;
llvm::Value* arg_addr_offsets = &*args;
arg_addr_offsets->setName("inputs_addr_offsets");
++args;
llvm::Value* arg_local_bitmaps = &*args;
arg_local_bitmaps->setName("local_bitmaps");
++args;
llvm::Value* arg_holder_ptrs = &*args;
arg_holder_ptrs->setName("holder_ptrs");
++args;
llvm::Value* arg_selection_vector = &*args;
arg_selection_vector->setName("selection_vector");
++args;
llvm::Value* arg_context_ptr = &*args;
arg_context_ptr->setName("context_ptr");
++args;
llvm::Value* arg_nrecords = &*args;
arg_nrecords->setName("nrecords");
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);
// Add reference to output vector (in entry block)
builder->SetInsertPoint(loop_entry);
llvm::Value* output_ref =
GetDataReference(arg_addrs, output->data_idx(), output->field());
llvm::Value* output_buffer_ptr_ref =
arrow::is_binary_like(output_type_id)
? GetDataBufferPtrReference(arg_addrs, output->data_buffer_ptr_idx(),
output->field())
: nullptr;
llvm::Value* output_offset_ref =
arrow::is_binary_like(output_type_id)
? GetOffsetsReference(arg_addrs, output->offsets_idx(), output->field())
: nullptr;
std::vector<llvm::Value*> slice_offsets;
for (int idx = 0; idx < buffer_count; idx++) {
auto offsetAddr = builder->CreateGEP(types()->i64_type(), arg_addr_offsets,
types()->i32_constant(idx));
auto offset = builder->CreateLoad(types()->i64_type(), offsetAddr);
slice_offsets.push_back(offset);
}
// Loop body
builder->SetInsertPoint(loop_body);
// define loop_var : start with 0, +1 after each iter
llvm::PHINode* loop_var = builder->CreatePHI(types()->i64_type(), 2, "loop_var");
llvm::Value* position_var = loop_var;
if (selection_vector_mode != SelectionVector::MODE_NONE) {
auto selection_vector_addr =
builder->CreateGEP(selection_vector_type, arg_selection_vector, loop_var);
position_var = builder->CreateIntCast(
builder->CreateLoad(selection_vector_type, selection_vector_addr,
"uncasted_position_var"),
types()->i64_type(), true, "position_var");
}
// The visitor can add code to both the entry/loop blocks.
Visitor visitor(this, fn, loop_entry, arg_addrs, arg_local_bitmaps, arg_holder_ptrs,
slice_offsets, arg_context_ptr, position_var);
value_expr->Accept(visitor);
LValuePtr output_value = visitor.result();
// The "current" block may have changed due to code generation in the visitor.
llvm::BasicBlock* loop_body_tail = builder->GetInsertBlock();
// add jump to "loop block" at the end of the "setup block".
builder->SetInsertPoint(loop_entry);
builder->CreateBr(loop_body);
// save the value in the output vector.
builder->SetInsertPoint(loop_body_tail);
if (output_type_id == arrow::Type::BOOL) {
SetPackedBitValue(output_ref, loop_var, output_value->data());
} else if (arrow::is_primitive(output_type_id) ||
output_type_id == arrow::Type::DECIMAL) {
auto slot_offset =
builder->CreateGEP(types()->IRType(output_type_id), output_ref, loop_var);
builder->CreateStore(output_value->data(), slot_offset);
} else if (arrow::is_binary_like(output_type_id)) {
// Var-len output. Make a function call to populate the data.
// if there is an error, the fn sets it in the context. And, will be returned at the
// end of this row batch.
AddFunctionCall("gdv_fn_populate_varlen_vector", types()->i32_type(),
{arg_context_ptr, output_buffer_ptr_ref, output_offset_ref, loop_var,
output_value->data(), output_value->length()});
} else {
return Status::NotImplemented("output type ", output->Type()->ToString(),
" not supported");
}
ADD_TRACE("saving result " + output->Name() + " value %T", output_value->data());
if (visitor.has_arena_allocs()) {
// Reset allocations to avoid excessive memory usage. Once the result is copied to
// the output vector (store instruction above), any memory allocations in this
// iteration of the loop are no longer needed.
std::vector<llvm::Value*> reset_args;
reset_args.push_back(arg_context_ptr);
AddFunctionCall("gdv_fn_context_arena_reset", types()->void_type(), reset_args);
}
// check loop_var
loop_var->addIncoming(types()->i64_constant(0), loop_entry);
llvm::Value* loop_update =
builder->CreateAdd(loop_var, types()->i64_constant(1), "loop_var+1");
loop_var->addIncoming(loop_update, loop_body_tail);
llvm::Value* loop_var_check =
builder->CreateICmpSLT(loop_update, arg_nrecords, "loop_var < nrec");
builder->CreateCondBr(loop_var_check, loop_body, loop_exit);
// Loop exit
builder->SetInsertPoint(loop_exit);
builder->CreateRet(types()->i32_constant(0));
return Status::OK();
}