Status LLVMGenerator::CodeGenExprValue()

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();
}