Status FilterContext::CodegenInsert()

in be/src/exec/filter-context.cc [372:590]


Status FilterContext::CodegenInsert(LlvmCodeGen* codegen, ScalarExpr* filter_expr,
    const TRuntimeFilterDesc& filter_desc, llvm::Function** fn) {
  llvm::LLVMContext& context = codegen->context();
  LlvmBuilder builder(context);

  *fn = nullptr;
  llvm::PointerType* this_type = codegen->GetStructPtrType<FilterContext>();
  llvm::PointerType* tuple_row_ptr_type = codegen->GetStructPtrType<TupleRow>();
  LlvmCodeGen::FnPrototype prototype(
      codegen, "FilterContextInsert", codegen->void_type());
  prototype.AddArgument(LlvmCodeGen::NamedVariable("this", this_type));
  prototype.AddArgument(LlvmCodeGen::NamedVariable("row", tuple_row_ptr_type));

  llvm::Value* args[2];
  llvm::Function* insert_filter_fn = prototype.GeneratePrototype(&builder, args);
  llvm::Value* this_arg = args[0];
  llvm::Value* row_arg = args[1];

  llvm::Value* local_filter_arg;
  // The function for inserting into the in-list filter.
  llvm::Function* insert_in_list_filter_fn = nullptr;
  if (filter_desc.type == TRuntimeFilterType::BLOOM) {
    // Load 'local_bloom_filter' from 'this_arg' FilterContext object.
    llvm::Value* local_bloom_filter_ptr =
        builder.CreateStructGEP(nullptr, this_arg, 3, "local_bloom_filter_ptr");
    local_filter_arg =
        builder.CreateLoad(local_bloom_filter_ptr, "local_bloom_filter_arg");
  } else if (filter_desc.type == TRuntimeFilterType::MIN_MAX) {
    // Load 'local_min_max_filter' from 'this_arg' FilterContext object.
    llvm::Value* local_min_max_filter_ptr =
        builder.CreateStructGEP(nullptr, this_arg, 4, "local_min_max_filter_ptr");
    llvm::PointerType* min_max_filter_type =
        codegen->GetNamedPtrType(MinMaxFilter::GetLlvmClassName(
        filter_expr->type().type))->getPointerTo();
    local_min_max_filter_ptr = builder.CreatePointerCast(
        local_min_max_filter_ptr, min_max_filter_type, "cast_min_max_filter_ptr");
    local_filter_arg =
        builder.CreateLoad(local_min_max_filter_ptr, "local_min_max_filter_arg");
  } else {
    DCHECK(filter_desc.type == TRuntimeFilterType::IN_LIST);
    // Load 'local_in_list_filter' from 'this_arg' FilterContext object.
    llvm::Value* local_in_list_filter_ptr =
        builder.CreateStructGEP(nullptr, this_arg, 5, "local_in_list_filter_ptr");
    switch (filter_expr->type().type) {
      case TYPE_TINYINT:
        insert_in_list_filter_fn = codegen->GetFunction(
            IRFunction::TINYINT_IN_LIST_FILTER_INSERT, false);
        break;
      case TYPE_SMALLINT:
        insert_in_list_filter_fn = codegen->GetFunction(
            IRFunction::SMALLINT_IN_LIST_FILTER_INSERT, false);
        break;
      case TYPE_INT:
        insert_in_list_filter_fn = codegen->GetFunction(
            IRFunction::INT_IN_LIST_FILTER_INSERT, false);
        break;
      case TYPE_BIGINT:
        insert_in_list_filter_fn = codegen->GetFunction(
            IRFunction::BIGINT_IN_LIST_FILTER_INSERT, false);
        break;
      case TYPE_DATE:
        insert_in_list_filter_fn = codegen->GetFunction(
            IRFunction::DATE_IN_LIST_FILTER_INSERT, false);
        break;
      case TYPE_STRING:
        insert_in_list_filter_fn = codegen->GetFunction(
            IRFunction::STRING_IN_LIST_FILTER_INSERT, false);
        break;
      case TYPE_CHAR:
        insert_in_list_filter_fn = codegen->GetFunction(
            IRFunction::CHAR_IN_LIST_FILTER_INSERT, false);
        break;
      case TYPE_VARCHAR:
        insert_in_list_filter_fn = codegen->GetFunction(
            IRFunction::VARCHAR_IN_LIST_FILTER_INSERT, false);
        break;
      default:
        DCHECK(false);
        break;
    }
    // Get type of the InListFilterImpl class from the first arg of the Insert() method.
    // We can't hardcode the class name since it's a template class. The class name will
    // be something like "class.impala::InListFilterImpl.1408". The last number is a
    // unique id appended by LLVM at runtime.
    llvm::Type* filter_impl_type = insert_in_list_filter_fn->arg_begin()->getType();
    llvm::PointerType* in_list_filter_type = codegen->GetPtrType(filter_impl_type);
    local_in_list_filter_ptr = builder.CreatePointerCast(
        local_in_list_filter_ptr, in_list_filter_type, "cast_in_list_filter_ptr");
    local_filter_arg =
        builder.CreateLoad(local_in_list_filter_ptr, "local_in_list_filter_arg");
  }

  // Check if 'local_bloom_filter', 'local_min_max_filter' or 'local_in_list_filter' are
  // NULL (depending on filter desc) and return if so.
  llvm::Value* filter_null = builder.CreateIsNull(local_filter_arg, "filter_is_null");
  llvm::BasicBlock* filter_not_null_block =
      llvm::BasicBlock::Create(context, "filters_not_null", insert_filter_fn);
  llvm::BasicBlock* filter_null_block =
      llvm::BasicBlock::Create(context, "filters_null", insert_filter_fn);
  llvm::BasicBlock* check_val_block =
      llvm::BasicBlock::Create(context, "check_val_block", insert_filter_fn);
  builder.CreateCondBr(filter_null, filter_null_block, filter_not_null_block);
  builder.SetInsertPoint(filter_null_block);
  builder.CreateRetVoid();
  builder.SetInsertPoint(filter_not_null_block);

  // Test whether 'local_min_max_filter->AlwaysTrue()' is true and return if so.
  if (filter_desc.type == TRuntimeFilterType::MIN_MAX) {
    // Get the function for boolean <Type>MinMaxFilter::AlwaysTrue().
    llvm::Function* always_true_member_fn = codegen->GetFunction(
        MinMaxFilter::GetAlwaysTrueIRFunctionType(filter_expr->type()), false);
    DCHECK(always_true_member_fn != nullptr);

    llvm::Value* always_true_result =
        builder.CreateCall(always_true_member_fn, {local_filter_arg});

    llvm::BasicBlock* always_true_true_block =
        llvm::BasicBlock::Create(context, "always_true_true_block", insert_filter_fn);
    llvm::BasicBlock* always_true_false_block =
        llvm::BasicBlock::Create(context, "always_true_false_block", insert_filter_fn);

    builder.CreateCondBr(
        always_true_result, always_true_true_block, always_true_false_block);

    builder.SetInsertPoint(always_true_true_block);
    builder.CreateRetVoid();
    builder.SetInsertPoint(always_true_false_block);
    builder.CreateBr(check_val_block);
  } else {
    builder.CreateBr(check_val_block);
  }
  builder.SetInsertPoint(check_val_block);


  llvm::Function* compute_fn;
  RETURN_IF_ERROR(filter_expr->GetCodegendComputeFn(codegen, false, &compute_fn));
  DCHECK(compute_fn != nullptr);

  // Load 'expr_eval' from 'this_arg' FilterContext object.
  llvm::Value* expr_eval_ptr =
      builder.CreateStructGEP(nullptr, this_arg, 0, "expr_eval_ptr");
  llvm::Value* expr_eval_arg = builder.CreateLoad(expr_eval_ptr, "expr_eval_arg");

  // Evaluate the row against the filter's expression.
  llvm::Value* compute_fn_args[] = {expr_eval_arg, row_arg};
  CodegenAnyVal result = CodegenAnyVal::CreateCallWrapped(
      codegen, &builder, filter_expr->type(), compute_fn, compute_fn_args, "result");

  CodegenAnyValReadWriteInfo rwi = result.ToReadWriteInfo();
  rwi.entry_block().BranchTo(&builder);

  llvm::BasicBlock* insert_filter_block =
      llvm::BasicBlock::Create(context, "insert_filter", insert_filter_fn);

  // Set the pointer to NULL in case it evaluates to NULL.
  builder.SetInsertPoint(rwi.null_block());
  llvm::Value* null_ptr = codegen->null_ptr_value();
  builder.CreateBr(insert_filter_block);

  // Saves 'result' on the stack and passes a pointer to it to Insert().
  builder.SetInsertPoint(rwi.non_null_block());
  llvm::Value* native_ptr = SlotDescriptor::CodegenStoreNonNullAnyValToNewAlloca(rwi);
  native_ptr = builder.CreatePointerCast(native_ptr, codegen->ptr_type(), "native_ptr");
  builder.CreateBr(insert_filter_block);

  // Get the arguments in place to call Insert().
  builder.SetInsertPoint(insert_filter_block);
  llvm::PHINode* val_ptr_phi = rwi.CodegenNullPhiNode(native_ptr, null_ptr,
      "val_ptr_phi");

  // Insert into the bloom filter.
  if (filter_desc.type == TRuntimeFilterType::BLOOM) {
    // Create a global constant of the filter expression's ColumnType. It needs to be a
    // constant for constant propagation and dead code elimination in 'get_hash_value_fn'.
    llvm::Type* col_type = codegen->GetStructType<ColumnType>();
    llvm::Constant* expr_type_arg = codegen->ConstantToGVPtr(
        col_type, filter_expr->type().ToIR(codegen), "expr_type_arg");

    // Call RawValue::GetHashValue() on the result of the filter's expression.
    llvm::Value* seed_arg =
        codegen->GetI32Constant(RuntimeFilterBank::DefaultHashSeed());
    llvm::Value* get_hash_value_args[] = {val_ptr_phi, expr_type_arg, seed_arg};
    llvm::Function* get_hash_value_fn =
        codegen->GetFunction(IRFunction::RAW_VALUE_GET_HASH_VALUE_FAST_HASH32, false);
    DCHECK(get_hash_value_fn != nullptr);
    llvm::Value* hash_value =
        builder.CreateCall(get_hash_value_fn, get_hash_value_args, "hash_value");

    // Call Insert() on the bloom filter.
    llvm::Function* insert_bloom_filter_fn =
        codegen->GetFunction(IRFunction::BLOOM_FILTER_INSERT, false);

    DCHECK(insert_bloom_filter_fn != nullptr);

    llvm::Value* insert_args[] = {local_filter_arg, hash_value};
    builder.CreateCall(insert_bloom_filter_fn, insert_args);
  } else if (filter_desc.type == TRuntimeFilterType::MIN_MAX) {
    // The function for inserting into the min-max filter.
    llvm::Function* min_max_insert_fn = codegen->GetFunction(
        MinMaxFilter::GetInsertIRFunctionType(filter_expr->type()), false);
    DCHECK(min_max_insert_fn != nullptr);

    llvm::Value* insert_filter_args[] = {local_filter_arg, val_ptr_phi};
    builder.CreateCall(min_max_insert_fn, insert_filter_args);
  } else {
    DCHECK(filter_desc.type == TRuntimeFilterType::IN_LIST);
    DCHECK(insert_in_list_filter_fn != nullptr);
    llvm::Value* insert_filter_args[] = {local_filter_arg, val_ptr_phi};
    builder.CreateCall(insert_in_list_filter_fn, insert_filter_args);
  }

  builder.CreateRetVoid();

  *fn = codegen->FinalizeFunction(insert_filter_fn);
  if (*fn == nullptr) {
    return Status("Codegen'ed FilterContext::Insert() fails verification, see log");
  }
  return Status::OK();
}