Files
openGauss-server/src/gausskernel/runtime/codegen/vecexecutor/vechashjoincodegen.cpp
2020-06-30 17:38:27 +08:00

3029 lines
123 KiB
C++
Executable File

/*
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* ---------------------------------------------------------------------------------------
*
* vechashjoincodegen.cpp
* Routines to handle vector hashjoin nodes. We only focuse on
* the CPU intensive part of hashjoin operation.
* IDENTIFICATION
* src/gausskernel/runtime/codegen/vecexecutor/vechashjoincodegen.cpp
*
* ---------------------------------------------------------------------------------------
*/
#include "codegen/gscodegen.h"
#include "codegen/vechashjoincodegen.h"
#include "codegen/codegendebuger.h"
#include "codegen/builtinscodegen.h"
#include "access/transam.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
#include "utils/lsyscache.h"
#include "vecexecutor/vecexecutor.h"
#include "pgxc/pgxc.h"
char* Wrappalloc(int32 size);
bool NULL_possible(PlanState* node);
void WrapposRepalloc(filter::BloomFilterImpl<int64>* bfi);
int64 Idx_cellCache_hashBasedOperator;
int64 Idx_keyMatch_hashBasedOperator;
namespace dorado {
Var* VecHashJoinCodeGen::GetSimpHashCondExpr(
VecHashJoinState* node, ExprState* estate, bool* is_simple_var, int* enable_fast_keyMatch)
{
Var* var = NULL;
Assert(true == *is_simple_var);
/*
* Since we only support simple hash condition case, make sure the
* expr is 'Var' or 'Relabel' type.
*/
if (IsA(estate->expr, Var)) {
var = (Var*)estate->expr;
} else if (IsA(estate->expr, RelabelType)) {
RelabelType* reltype = (RelabelType*)estate->expr;
if (IsA(reltype->arg, Var) && ((Var*)reltype->arg)->varattno > 0) {
var = (Var*)reltype->arg;
} else {
*is_simple_var = false;
return var;
}
} else {
*is_simple_var = false;
return var;
}
/* Without considering codegen if hash key contains system columns */
Assert(var != NULL);
if (var->varoattno < 0) {
*is_simple_var = false;
return var;
}
/* Find the base relation according to the varnoold of var. */
Oid relid = 0;
int count = 0;
Relation base_relation;
FormData_pg_attribute* attr = NULL;
if (*enable_fast_keyMatch) {
if (var->varnoold > 0) {
ListCell* lc = NULL;
int base_pos = var->varnoold;
foreach (lc, node->js.ps.state->es_range_table) {
if ((++count) == base_pos) {
RangeTblEntry* rte = (RangeTblEntry*)lfirst(lc);
relid = rte->relid;
break;
}
}
/* If not found, do not use fast match key path. */
if (relid > 0) {
base_relation = RelationIdGetRelation(relid);
Oid namespaceOid = get_rel_namespace(relid);
if (RelationIsValid(base_relation) && namespaceOid > FirstNormalObjectId) {
if (var->varoattno <= base_relation->rd_att->natts) {
attr = base_relation->rd_att->attrs[var->varoattno - 1];
if (attr == NULL || !attr->attnotnull)
*enable_fast_keyMatch = 0;
} else
*enable_fast_keyMatch = 0;
RelationClose(base_relation);
} else {
*enable_fast_keyMatch = 0;
if (RelationIsValid(base_relation))
RelationClose(base_relation);
}
} else
*enable_fast_keyMatch = 0;
} else
*enable_fast_keyMatch = 0;
}
return var;
}
bool VecHashJoinCodeGen::JittableHashJoin(VecHashJoinState* node)
{
int enable_fast_keyMatch = 1;
if (!u_sess->attr.attr_sql.enable_codegen || IS_PGXC_COORDINATOR)
return false;
/*
* Only support inner hash join.
*/
if (node->js.jointype != JOIN_INNER)
return false;
/*
* Set enable_fast_keyMatch to be 1 when hash keys are NOT_NULL columns
* and their data types are integer.
*/
ListCell* lc_inner = NULL;
ListCell* lc_outer = NULL;
Var* var_inner = NULL;
Var* var_outer = NULL;
ExprState* exprState = NULL;
bool is_simple_var = false;
forboth(lc_inner, node->hj_InnerHashKeys, lc_outer, node->hj_OuterHashKeys)
{
/*
* Only support hash join with simple join key. Check inner hash key
* and outer hash key separately
*/
exprState = (ExprState*)lfirst(lc_inner);
is_simple_var = true;
var_inner = GetSimpHashCondExpr(node, exprState, &is_simple_var, &enable_fast_keyMatch);
if (!is_simple_var)
return false;
/* Reset the flag */
is_simple_var = true;
exprState = (ExprState*)lfirst(lc_outer);
var_outer = GetSimpHashCondExpr(node, exprState, &is_simple_var, &enable_fast_keyMatch);
if (!is_simple_var)
return false;
/* Inner var and outer var should be the same data type */
if (var_inner->vartype != var_outer->vartype)
return false;
/*
* Only supported hash claus with int, float, numeric, bpchar type.
*/
switch (var_inner->vartype) {
case INT4OID:
case INT8OID:
break;
case BPCHAROID: {
enable_fast_keyMatch = 0;
/*
* Make sure the inner key and the outer key has the same length.
*/
if (var_inner->vartypmod != var_outer->vartypmod)
return false;
} break;
default:
return false;
}
}
/*
* If we can make sure the column of the base relation is masked as
* not null, we could codegen the process without checking NULL.
*/
if (enable_fast_keyMatch != 0 && !NULL_possible((PlanState*)node))
enable_fast_keyMatch = 2;
else if (list_length(node->hj_InnerHashKeys) == 1)
enable_fast_keyMatch = 1;
else
enable_fast_keyMatch = 0;
node->enable_fast_keyMatch = enable_fast_keyMatch;
return true;
}
/*
* @Description : Check if codegeneration is allowed for buildHashTable
* and probeHashTable function.
* @Notes : Do not support complicated hash condition and the hash
* key should be int4/int8/bpchar type.
*/
bool VecHashJoinCodeGen::JittableHashJoin_buildandprobe(VecHashJoinState* node)
{
if (!u_sess->attr.attr_sql.enable_codegen || IS_PGXC_COORDINATOR)
return false;
/* loop over all the hash keys */
ListCell* lc_inner = NULL;
ListCell* lc_outer = NULL;
Var* var_inner = NULL;
Var* var_outer = NULL;
ExprState* exprState = NULL;
forboth(lc_inner, node->hj_InnerHashKeys, lc_outer, node->hj_OuterHashKeys)
{
exprState = (ExprState*)lfirst(lc_inner);
if (IsA(exprState->expr, Var))
var_inner = (Var*)exprState->expr;
else if (IsA(exprState->expr, RelabelType)) {
RelabelType* reltype = (RelabelType*)exprState->expr;
if (IsA(reltype->arg, Var) && ((Var*)reltype->arg)->varattno > 0)
var_inner = (Var*)((RelabelType*)exprState->expr)->arg;
else
return false;
} else
return false;
Assert(var_inner != NULL);
/* without doing codegen for system column */
if (var_inner->varoattno < 0)
return false;
exprState = (ExprState*)lfirst(lc_outer);
if (IsA(exprState->expr, Var))
var_outer = (Var*)exprState->expr;
else if (IsA(exprState->expr, RelabelType)) {
RelabelType* reltype = (RelabelType*)exprState->expr;
if (IsA(reltype->arg, Var) && ((Var*)reltype->arg)->varattno > 0)
var_outer = (Var*)((RelabelType*)exprState->expr)->arg;
else
return false;
} else
return false;
Assert(var_outer != NULL);
/* without doing codegen for system column */
if (var_outer->varoattno < 0)
return false;
/* 2. Same data type */
if (var_inner->vartype != var_outer->vartype)
return false;
/* 3. Supported data type */
switch (var_inner->vartype) {
case INT4OID:
case INT8OID:
break;
case BPCHAROID: {
/*
* Make sure the inner key and the outer key has the same length.
*/
if (var_inner->vartypmod != var_outer->vartypmod)
return false;
} break;
default:
return false;
}
}
return true;
}
bool VecHashJoinCodeGen::JittableHashJoin_bloomfilter(VecHashJoinState* node)
{
if (!u_sess->attr.attr_sql.enable_codegen || IS_PGXC_COORDINATOR)
return false;
List* bfVarList = node->bf_runtime.bf_var_list;
/* Without considering codegen when there is no bfVarList */
if (bfVarList == NULL || list_length(bfVarList) == 0)
return false;
/*
* If one of the hash key type is in (int2, int4, int8), allow codegeneration
* in this function.
*/
int int_count = 0;
for (int i = 0; i < list_length(bfVarList); i++) {
Var* var = (Var*)list_nth(bfVarList, i);
Oid dataType = var->vartype;
switch (dataType) {
case INT2OID:
case INT4OID:
case INT8OID:
int_count++;
break;
default:
break;
}
}
if (int_count == 0)
return false;
return true;
}
/* @Description : Codegen keyMatch of hash join, the jitted function will be
* called by innerJoinT.
* int8
* jitted_keyMatch(%struct.hashCell *mcellCache, bool flag_batch,
* int64 vals_batch, int64 keyIdx, bool *mkeyMatch)
* {
* if (mkeyMatch == false)
* result = int8_0;
* else
* {
* flag_cell = mcellCache->m_val[keyIdx].flag
* and_flag = flag_cell && flag_batch;
* if (and_flag)
* {
* if (nulleqnull)
* result = int8_1;
* else
* result = int8_0;
* }
* else
* {
* or_flag = flag_cell || flag_batch;
* if (or_flag)
* result = int8_0;
* else
* {
* val_cell = mcellCache->m_val[keyIdx].val;
* // for integer keys
* result = (val_cell == vals_batch);
* }
* }
* }
* *mkeyMatch = result; //if node->enable_fast_keyMatch == 0
* return result;
* }
*/
llvm::Function* VecHashJoinCodeGen::KeyMatchCodeGen(VecHashJoinState* node, Var* variable)
{
bool nulleqnull = (node->js.nulleqqual != NIL);
Assert(NULL != (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj);
GsCodeGen* llvmCodeGen = (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj;
/* Get LLVM Context and builder */
llvm::LLVMContext& context = llvmCodeGen->context();
GsCodeGen::LlvmBuilder builder(context);
llvm::Function* jitted_keyMatch = NULL;
llvm::Value* andTrue_result = NULL;
llvm::Value* orFalse_result = NULL;
llvm::PHINode* Phi_result = NULL;
llvm::Value* llvmargs[5];
/* Define data types and some llvm consts */
DEFINE_CG_TYPE(int1Type, BITOID);
DEFINE_CG_TYPE(int8Type, CHAROID);
DEFINE_CG_TYPE(int32Type, INT4OID);
DEFINE_CG_TYPE(int64Type, INT8OID);
DEFINE_CG_PTRTYPE(int8PtrType, CHAROID);
DEFINE_CG_PTRTYPE(hashCellPtrType, "struct.hashCell");
DEFINE_CGVAR_INT8(int8_0, 0);
DEFINE_CGVAR_INT8(int8_1, 1);
DEFINE_CGVAR_INT32(int32_0, 0);
DEFINE_CGVAR_INT32(int32_1, 1);
DEFINE_CGVAR_INT64(int64_0, 0);
/* llvm array values, used to represent the location of some element */
llvm::Value* Vals4[4] = {int64_0, int32_0, int32_0, int32_0};
GsCodeGen::FnPrototype fn_prototype(llvmCodeGen, "JittedkeyMatch", int8Type);
fn_prototype.addArgument(GsCodeGen::NamedVariable("cellCache", hashCellPtrType));
fn_prototype.addArgument(GsCodeGen::NamedVariable("batch_mflag", int8Type));
fn_prototype.addArgument(GsCodeGen::NamedVariable("batch_mvals", int64Type));
fn_prototype.addArgument(GsCodeGen::NamedVariable("key_idx", int64Type));
if (node->enable_fast_keyMatch == 0)
fn_prototype.addArgument(GsCodeGen::NamedVariable("keyMatch", int8PtrType));
jitted_keyMatch = fn_prototype.generatePrototype(&builder, &llvmargs[0]);
/* define basic blocks */
llvm::BasicBlock* bb_entry = &jitted_keyMatch->getEntryBlock();
DEFINE_BLOCK(bb_checkNull, jitted_keyMatch);
DEFINE_BLOCK(bb_andTrue, jitted_keyMatch);
DEFINE_BLOCK(bb_andFalse, jitted_keyMatch);
DEFINE_BLOCK(bb_orTrue, jitted_keyMatch);
DEFINE_BLOCK(bb_orFalse, jitted_keyMatch);
DEFINE_BLOCK(bb_ret, jitted_keyMatch);
/* start the main codegen process for hash inner join */
llvm::Value* mcellCache = llvmargs[0];
llvm::Value* flag_batch = llvmargs[1];
llvm::Value* vals_batch = llvmargs[2];
llvm::Value* keyIdx = llvmargs[3];
llvm::Value* mkeyMatch = NULL;
if (node->enable_fast_keyMatch == 0) {
mkeyMatch = llvmargs[4];
}
/* if node->enable_fast_keyMatch == 1, there is just one hash clause,
* so no need to check the previous m_keyMatch[looxIdx]
*/
if (node->enable_fast_keyMatch == 0) {
/* get HashJoinTbl.hashBasedOperator.m_keyMatch[loop_idx] */
llvm::Value* keyMatch_val = builder.CreateAlignedLoad(mkeyMatch, 1, "m_keyMatch_i");
keyMatch_val = builder.CreateTrunc(keyMatch_val, int1Type);
builder.CreateCondBr(keyMatch_val, bb_checkNull, bb_ret);
} else {
builder.CreateBr(bb_checkNull);
}
/* if m_keyMatch[i] = TRUE, start the keyMatching, first check the NULL flag */
builder.SetInsertPoint(bb_checkNull);
/* Get flag from m_cellCache[keyIdx] */
Vals4[0] = int64_0;
Vals4[1] = int32_1;
Vals4[2] = keyIdx;
Vals4[3] = int32_1;
llvm::Value* flag_cell = builder.CreateInBoundsGEP(mcellCache, Vals4);
flag_cell = builder.CreateAlignedLoad(flag_cell, 1, "flag");
flag_cell = builder.CreateTrunc(flag_cell, int1Type);
flag_batch = builder.CreateTrunc(flag_batch, int1Type);
llvm::Value* and_flag = builder.CreateAnd(flag_cell, flag_batch);
builder.CreateCondBr(and_flag, bb_andTrue, bb_andFalse);
/* both flags are TRUE */
builder.SetInsertPoint(bb_andTrue);
if (nulleqnull)
andTrue_result = int8_1;
else
andTrue_result = int8_0;
builder.CreateBr(bb_ret);
/* at least one flag is FALSE */
builder.SetInsertPoint(bb_andFalse);
llvm::Value* or_flag = builder.CreateOr(flag_cell, flag_batch);
builder.CreateCondBr(or_flag, bb_orTrue, bb_orFalse);
/* one flag is TRUE, and the other is FALSE */
builder.SetInsertPoint(bb_orTrue);
builder.CreateBr(bb_ret);
/* both flags are FALSE, so compare the values from batch and cellCache */
builder.SetInsertPoint(bb_orFalse);
/* m_cellCache->m_val[keyIdx].val */
Vals4[1] = int32_1;
Vals4[2] = keyIdx;
Vals4[3] = int32_0;
llvm::Value* val_cell = builder.CreateInBoundsGEP(mcellCache, Vals4);
val_cell = builder.CreateAlignedLoad(val_cell, 8, "val");
/* According to the data type to do comparison */
switch (variable->vartype) {
case INT4OID: {
val_cell = builder.CreateTrunc(val_cell, int32Type);
vals_batch = builder.CreateTrunc(vals_batch, int32Type);
orFalse_result = builder.CreateICmpEQ(val_cell, vals_batch, "key_int4eq");
} break;
case INT8OID:
orFalse_result = builder.CreateICmpEQ(val_cell, vals_batch, "key_int8eq");
break;
case BPCHAROID: {
DEFINE_CGVAR_INT32(int32_len, variable->vartypmod - VARHDRSZ);
/* first extract the char* value from Datum */
llvm::Function* evalvar = llvmCodeGen->module()->getFunction("JittedEvalVarlena");
if (evalvar == NULL) {
evalvar = VarlenaCvtCodeGen();
}
val_cell = builder.CreateCall(evalvar, val_cell, "evalbatchvar");
val_cell = builder.CreateExtractValue(val_cell, 1);
vals_batch = builder.CreateCall(evalvar, vals_batch, "evalbatchvar");
vals_batch = builder.CreateExtractValue(vals_batch, 1);
llvm::Function* func_memcmp = llvmCodeGen->module()->getFunction("LLVMIRmemcmp");
if (func_memcmp == NULL) {
ereport(ERROR,
(errcode(ERRCODE_LOAD_IR_FUNCTION_FAILED),
errmodule(MOD_LLVM),
errmsg("Failed on getting IR function : LLVMIRmemcmp!\n")));
}
orFalse_result = builder.CreateCall(func_memcmp, {val_cell, vals_batch, int32_len}, "memcmp");
orFalse_result = builder.CreateTrunc(orFalse_result, int1Type);
} break;
default:
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmodule(MOD_LLVM),
errmsg("Codegen keyMatch failed: unsupported data type!\n")));
}
orFalse_result = builder.CreateZExt(orFalse_result, int8Type);
builder.CreateBr(bb_ret);
/* return result according to different cases */
builder.SetInsertPoint(bb_ret);
if (node->enable_fast_keyMatch == 0) {
Phi_result = builder.CreatePHI(int8Type, 4);
Phi_result->addIncoming(int8_0, bb_entry);
Phi_result->addIncoming(andTrue_result, bb_andTrue);
Phi_result->addIncoming(int8_0, bb_orTrue);
Phi_result->addIncoming(orFalse_result, bb_orFalse);
builder.CreateAlignedStore(Phi_result, mkeyMatch, 1);
} else {
Phi_result = builder.CreatePHI(int8Type, 3);
Phi_result->addIncoming(andTrue_result, bb_andTrue);
Phi_result->addIncoming(int8_0, bb_orTrue);
Phi_result->addIncoming(orFalse_result, bb_orFalse);
}
builder.CreateRet(Phi_result);
/* finalize the jitted function */
llvmCodeGen->FinalizeFunction(jitted_keyMatch, node->js.ps.plan->plan_node_id);
return jitted_keyMatch;
}
/*
* @Description : Consider codegeneration on hot functions of hash join node,
* including innerJoinT, buildHashTable, probeHashTable and
* bloom filter function.
* @Notes : Different hot functions have different constraints.
*/
void VecHashJoinCodeGen::HashJoinCodeGen(VecHashJoinState* node)
{
Assert(NULL != (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj);
GsCodeGen* llvmCodeGen = (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj;
llvm::Function* jitted_innerjoin = NULL;
/*
* only consider simple key-match cases. If the condition
* is not satisfied, no need to codegen.
*/
if (JittableHashJoin(node)) {
if (node->enable_fast_keyMatch == 2)
jitted_innerjoin = VecHashJoinCodeGen::HashJoinCodeGen_fastpath(node);
else
jitted_innerjoin = VecHashJoinCodeGen::HashJoinCodeGen_normal(node);
if (jitted_innerjoin != NULL) {
llvmCodeGen->addFunctionToMCJit(jitted_innerjoin, reinterpret_cast<void**>(&(node->jitted_innerjoin)));
}
}
/* buildHashTable and probeHashTable should use the same hash function */
if (JittableHashJoin_buildandprobe(node)) {
llvm::Function* jitted_buildHashTable = VecHashJoinCodeGen::HashJoinCodeGen_buildHashTable(node);
llvm::Function* jitted_buildHashTable_NeedCopy =
VecHashJoinCodeGen::HashJoinCodeGen_buildHashTable_NeedCopy(node);
llvm::Function* jitted_probeHashTable = VecHashJoinCodeGen::HashJoinCodeGen_probeHashTable(node);
if (jitted_buildHashTable != NULL && jitted_probeHashTable != NULL) {
llvmCodeGen->addFunctionToMCJit(
jitted_buildHashTable, reinterpret_cast<void**>(&(node->jitted_buildHashTable)));
llvmCodeGen->addFunctionToMCJit(
jitted_buildHashTable_NeedCopy, reinterpret_cast<void**>(&(node->jitted_buildHashTable_NeedCopy)));
llvmCodeGen->addFunctionToMCJit(
jitted_probeHashTable, reinterpret_cast<void**>(&(node->jitted_probeHashTable)));
}
}
/*
* Since jitted_innerjoin IR function does not support compalicate key
* cases, we should codegen this part independently when jitted_innerjoin
* is null.
*/
if (jitted_innerjoin == NULL) {
llvm::Function* vhj_hashclauses = NULL;
if (node->js.nulleqqual == NIL) {
vhj_hashclauses = dorado::VecExprCodeGen::QualCodeGen(node->hashclauses, (PlanState*)node);
if (vhj_hashclauses != NULL)
llvmCodeGen->addFunctionToMCJit(vhj_hashclauses, reinterpret_cast<void**>(&(node->jitted_hashclause)));
}
}
/* check codegeneration for hash join qual */
{
llvm::Function* vhj_joinqual = NULL;
vhj_joinqual = dorado::VecExprCodeGen::QualCodeGen(node->js.joinqual, (PlanState*)node);
if (vhj_joinqual != NULL)
llvmCodeGen->addFunctionToMCJit(vhj_joinqual, reinterpret_cast<void**>(&(node->jitted_joinqual)));
}
/* consider codegeneration for bloom_filter in hashjoin node */
if (JittableHashJoin_bloomfilter(node)) {
llvm::Function* jitted_bf_addLong = dorado::VecHashJoinCodeGen::HashJoinCodeGen_bf_addLong(node);
if (jitted_bf_addLong != NULL)
llvmCodeGen->addFunctionToMCJit(
jitted_bf_addLong, reinterpret_cast<void**>(&(node->jitted_hashjoin_bfaddLong)));
llvm::Function* jitted_bf_incLong = dorado::VecHashJoinCodeGen::HashJoinCodeGen_bf_includeLong(node);
if (jitted_bf_incLong != NULL)
llvmCodeGen->addFunctionToMCJit(
jitted_bf_incLong, reinterpret_cast<void**>(&(node->jitted_hashjoin_bfincLong)));
}
}
llvm::Function* VecHashJoinCodeGen::HashJoinCodeGen_normal(VecHashJoinState* node)
{
Assert(NULL != (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj);
GsCodeGen* llvmCodeGen = (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj;
/* Find and load the IR file from the installaion directory */
llvmCodeGen->loadIRFile();
/* Get LLVM Context and builder */
llvm::LLVMContext& context = llvmCodeGen->context();
GsCodeGen::LlvmBuilder builder(context);
llvm::Value* hashJoinTbl = NULL;
llvm::Value* batch = NULL;
llvm::Function* jitted_hashjoin = NULL;
llvm::Value* tmpval = NULL;
llvm::Value* llvmargs[2];
int mcols, j;
llvm::Value* val = NULL;
llvm::Value* flag = NULL;
llvm::Value* mvals = NULL;
llvm::Value* mflag = NULL;
/* Define data types and some llvm consts */
DEFINE_CG_TYPE(int1Type, BITOID);
DEFINE_CG_TYPE(int32Type, INT4OID);
DEFINE_CG_TYPE(int64Type, INT8OID);
DEFINE_CG_PTRTYPE(VectorBatchPtrType, "class.VectorBatch");
DEFINE_CG_PTRTYPE(HashJoinTblPtrType, "class.HashJoinTbl");
DEFINE_CG_PTRTYPE(hashCellPtrType, "struct.hashCell");
DEFINE_CGVAR_INT8(int8_0, 0);
DEFINE_CGVAR_INT8(int8_1, 1);
DEFINE_CGVAR_INT32(int32_0, 0);
DEFINE_CGVAR_INT32(int32_1, 1);
DEFINE_CGVAR_INT32(int32_2, 2);
DEFINE_CGVAR_INT32(int32_BatchMaxSize, BatchMaxSize);
DEFINE_CGVAR_INT32(int32_pos_hjointbl_StateLog, pos_hjointbl_StateLog);
DEFINE_CGVAR_INT32(int32_pos_batch_marr, pos_batch_marr);
DEFINE_CGVAR_INT32(int32_pos_hjointbl_inbatch, pos_hjointbl_inbatch);
DEFINE_CGVAR_INT32(int32_pos_hjointbl_outbatch, pos_hjointbl_outbatch);
DEFINE_CGVAR_INT32(int32_pos_scalvec_vals, pos_scalvec_vals);
DEFINE_CGVAR_INT32(int32_pos_scalvec_flag, pos_scalvec_flag);
DEFINE_CGVAR_INT64(int64_0, 0);
DEFINE_CGVAR_INT64(int64_1, 1);
DEFINE_CGVAR_INT32(int32_6, 6);
/* llvm array values, used to represent the location of some element */
llvm::Value* Vals[2] = {int64_0, int32_0};
llvm::Value* Vals3[3] = {int64_0, int32_0, int32_0};
llvm::Value* Vals4[4] = {int64_0, int32_0, int32_0, int32_0};
/* the prototype of the jitted function for innerHashJoinT.
* Two parameters: (1) hashJoinTbl (this of innerHashJoinT)
* (2) batch of probing data
*/
GsCodeGen::FnPrototype fn_prototype(llvmCodeGen, "JittedHashInnerJoin", int32Type);
fn_prototype.addArgument(GsCodeGen::NamedVariable("hashJoinTbl", HashJoinTblPtrType));
fn_prototype.addArgument(GsCodeGen::NamedVariable("batch", VectorBatchPtrType));
jitted_hashjoin = fn_prototype.generatePrototype(&builder, &llvmargs[0]);
hashJoinTbl = llvmargs[0];
batch = llvmargs[1];
/* define basic block information for batch loop */
llvm::BasicBlock* entry = &jitted_hashjoin->getEntryBlock();
DEFINE_BLOCK(bb_restoreTrue, jitted_hashjoin);
DEFINE_BLOCK(bb_restoreFalse, jitted_hashjoin);
DEFINE_BLOCK(bb_pre_loopEntry, jitted_hashjoin);
DEFINE_BLOCK(bb_ret, jitted_hashjoin);
DEFINE_BLOCK(bb_loopEntry, jitted_hashjoin);
DEFINE_BLOCK(bb_crit_edge, jitted_hashjoin);
/* define basic block information for key-value matching */
DEFINE_BLOCK(bb_pre_keyMatch, jitted_hashjoin);
DEFINE_BLOCK(bb_keyMatch, jitted_hashjoin);
DEFINE_BLOCK(bb_Matched, jitted_hashjoin);
DEFINE_BLOCK(bb_NoMatch, jitted_hashjoin);
DEFINE_BLOCK(bb_BatchFull, jitted_hashjoin);
DEFINE_BLOCK(bb_TryNextCell, jitted_hashjoin);
/* start the main codegen process for hash inner join */
/* get the number of rows of this batch */
tmpval = builder.CreateInBoundsGEP(batch, Vals);
llvm::Value* nRows = builder.CreateAlignedLoad(tmpval, 4, "m_rows");
/* get HashJoinTbl.m_joinStateLog.lastBuildIdx */
Vals3[0] = int64_0;
Vals3[1] = int32_pos_hjointbl_StateLog;
Vals3[2] = int32_0;
llvm::Value* lastBuildIdx_addr = builder.CreateInBoundsGEP(hashJoinTbl, Vals3);
/* get HashJoinTbl.m_joinStateLog.lastCell */
Vals3[2] = int32_1;
llvm::Value* lastCell_addr = builder.CreateInBoundsGEP(hashJoinTbl, Vals3);
/* get HashJoinTbl.m_joinStateLog.restore */
Vals3[2] = int32_2;
llvm::Value* joinStateLog_restore = builder.CreateInBoundsGEP(hashJoinTbl, Vals3);
tmpval = builder.CreateAlignedLoad(joinStateLog_restore, 1, "restore");
tmpval = builder.CreateICmpEQ(tmpval, int8_0);
builder.CreateCondBr(tmpval, bb_restoreFalse, bb_restoreTrue);
/* if restore == true, get the loop start index and cellCache from
* m_joinStateLog, and reset the restore to false.
* bb_restoreTrue:
*/
builder.SetInsertPoint(bb_restoreTrue);
/* corresponding to : m_joinStateLog.restore = false; */
builder.CreateAlignedStore(int8_0, joinStateLog_restore, 1);
/* get m_joinStateLog.lastBuildIdx; */
llvm::Value* lastBuildIdx = builder.CreateAlignedLoad(lastBuildIdx_addr, 4, "lastBuildIdx");
/* get m_joinStateLog.lastCell */
llvm::Value* lastCell = builder.CreateAlignedLoad(lastCell_addr, 8, "lastCell");
/* get HashJoinTbl.hashBasedOperator.m_cellCache[lastBuildIdx] */
DEFINE_CGVAR_INT64(int64_8032, 8032);
DEFINE_CGVAR_INT64(int64_8, 8);
tmpval = builder.CreatePtrToInt(hashJoinTbl, int64Type);
tmpval = builder.CreateAdd(tmpval, int64_8032);
llvm::Value* tmpval2 = builder.CreateSExt(lastBuildIdx, int64Type);
tmpval2 = builder.CreateMul(tmpval2, int64_8);
tmpval = builder.CreateAdd(tmpval, tmpval2);
DEFINE_CG_PTRTYPE(hashCellPPtrType, hashCellPtrType);
llvm::Value* mcellCache = builder.CreateIntToPtr(tmpval, hashCellPPtrType);
/* m_cellCache[lastBuildIdx] = m_joinStateLog.lastCell */
builder.CreateAlignedStore(lastCell, mcellCache, 8);
builder.CreateBr(bb_restoreFalse);
/* restore is false, so start the loop from 0
* bb_restoreFalse:
*/
builder.SetInsertPoint(bb_restoreFalse);
llvm::PHINode* Phi = builder.CreatePHI(int32Type, 2);
Phi->addIncoming(int32_0, entry);
Phi->addIncoming(lastBuildIdx, bb_restoreTrue);
tmpval = builder.CreateICmpSLT(Phi, nRows);
builder.CreateCondBr(tmpval, bb_pre_loopEntry, bb_ret);
/*
* bb_pre_loopEntry:
* br label %bb_loopEntry
* This basic block is the one prior to the batch loop.
* Load all necessary ScalarVectors (batch loop constants) here.
* innerBatch, outerBatch, inner_arr, outer_arr, inner_vals/inner_flag,
* outer_vals/outer_flag, batch_vals/batch_flag, ...
*/
builder.SetInsertPoint(bb_pre_loopEntry);
/* get m_arr from batch */
Vals[1] = int32_pos_batch_marr;
llvm::Value* arr = builder.CreateInBoundsGEP(batch, Vals);
llvm::Value* batch_arr = builder.CreateAlignedLoad(arr, 8, "batch_arr");
/* HashJoinTbl.m_innerBatch */
Vals[1] = int32_pos_hjointbl_inbatch;
llvm::Value* innerBatch = builder.CreateInBoundsGEP(hashJoinTbl, Vals);
/* HashJoinTbl.m_outerBatch */
Vals[1] = int32_pos_hjointbl_outbatch;
llvm::Value* outerBatch = builder.CreateInBoundsGEP(hashJoinTbl, Vals);
innerBatch = builder.CreateAlignedLoad(innerBatch, 8, "innerBatch");
outerBatch = builder.CreateAlignedLoad(outerBatch, 8, "outerBatch");
/* Get m_arr from innerBatch and outerBatch */
Vals[1] = int32_pos_batch_marr;
llvm::Value* inner_arr = builder.CreateInBoundsGEP(innerBatch, Vals);
inner_arr = builder.CreateAlignedLoad(inner_arr, 8, "inner_arr");
llvm::Value* outer_arr = builder.CreateInBoundsGEP(outerBatch, Vals);
outer_arr = builder.CreateAlignedLoad(outer_arr, 8, "outer_arr");
/* Extract vals and flags from m_innerBatch */
mcols = innerPlanState(node)->ps_ResultTupleSlot->tts_tupleDescriptor->natts;
llvm::Value** inner_vals_array = (llvm::Value**)palloc(sizeof(llvm::Value*) * mcols);
llvm::Value** inner_flag_array = (llvm::Value**)palloc(sizeof(llvm::Value*) * mcols);
for (j = 0; j < mcols; j++) {
DEFINE_CGVAR_INT64(int64_jIdx, (long long)j);
/* Get m_vals from pVector */
Vals[0] = int64_jIdx;
Vals[1] = int32_pos_scalvec_vals;
tmpval = builder.CreateInBoundsGEP(inner_arr, Vals);
tmpval = builder.CreateAlignedLoad(tmpval, 8, "inner_vals");
inner_vals_array[j] = tmpval;
/* Get m_flag from pVector */
Vals[1] = int32_pos_scalvec_flag;
tmpval = builder.CreateInBoundsGEP(inner_arr, Vals);
tmpval = builder.CreateAlignedLoad(tmpval, 8, "inner_flag");
inner_flag_array[j] = tmpval;
}
/* Extract vals and flags from m_outerBatch */
mcols = outerPlanState(node)->ps_ResultTupleSlot->tts_tupleDescriptor->natts;
llvm::Value** outer_vals_array = (llvm::Value**)palloc(sizeof(llvm::Value*) * mcols);
llvm::Value** outer_flag_array = (llvm::Value**)palloc(sizeof(llvm::Value*) * mcols);
llvm::Value** batch_vals_array = (llvm::Value**)palloc(sizeof(llvm::Value*) * mcols);
llvm::Value** batch_flag_array = (llvm::Value**)palloc(sizeof(llvm::Value*) * mcols);
for (j = 0; j < mcols; j++) {
DEFINE_CGVAR_INT64(int64_jIdx, (long long)j);
Vals[0] = int64_jIdx;
/* Get m_vals from pVector */
Vals[1] = int32_pos_scalvec_vals;
tmpval = builder.CreateInBoundsGEP(outer_arr, Vals);
tmpval = builder.CreateAlignedLoad(tmpval, 8, "outer_vals");
outer_vals_array[j] = tmpval;
tmpval = builder.CreateInBoundsGEP(batch_arr, Vals);
tmpval = builder.CreateAlignedLoad(tmpval, 8, "batch_val");
batch_vals_array[j] = tmpval;
/* Get m_flag from pVector */
Vals[1] = int32_pos_scalvec_flag;
tmpval = builder.CreateInBoundsGEP(outer_arr, Vals);
tmpval = builder.CreateAlignedLoad(tmpval, 8, "outer_flag");
outer_flag_array[j] = tmpval;
tmpval = builder.CreateInBoundsGEP(batch_arr, Vals);
tmpval = builder.CreateAlignedLoad(tmpval, 1, "batch_flag");
batch_flag_array[j] = tmpval;
}
ListCell* lc_inner = NULL;
ListCell* lc_outer = NULL;
int keyIdx_var = 0;
int outkeyIdx_var = 0;
int num_hashkey = list_length(node->hj_InnerHashKeys);
Var* variable = NULL;
ExprState* exprState = NULL;
llvm::Value** outer_keys_flag_array = (llvm::Value**)palloc(sizeof(llvm::Value*) * num_hashkey);
llvm::Value** outer_keys_vals_array = (llvm::Value**)palloc(sizeof(llvm::Value*) * num_hashkey);
llvm::Value** inner_keyIdx_array = (llvm::Value**)palloc(sizeof(llvm::Value*) * num_hashkey);
Var** inner_var = (Var**)palloc(sizeof(Var*) * num_hashkey);
j = 0;
forboth(lc_inner, node->hj_InnerHashKeys, lc_outer, node->hj_OuterHashKeys)
{
/* get inner keyIdx */
variable = NULL;
exprState = (ExprState*)lfirst(lc_inner);
if (IsA(exprState->expr, Var))
variable = (Var*)exprState->expr;
else if (IsA(exprState->expr, RelabelType)) {
RelabelType* reltype = (RelabelType*)exprState->expr;
if (IsA(reltype->arg, Var) && ((Var*)reltype->arg)->varattno > 0)
variable = (Var*)((RelabelType*)exprState->expr)->arg;
}
if (variable == NULL) {
ereport(ERROR,
(errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmodule(MOD_LLVM), errmsg("Failed to get inner hash key!")));
}
keyIdx_var = variable->varattno - 1;
inner_var[j] = variable;
/* get outer keyIdx */
variable = NULL;
exprState = (ExprState*)lfirst(lc_outer);
if (IsA(exprState->expr, Var))
variable = (Var*)exprState->expr;
else if (IsA(exprState->expr, RelabelType)) {
RelabelType* reltype = (RelabelType*)exprState->expr;
if (IsA(reltype->arg, Var) && ((Var*)reltype->arg)->varattno > 0)
variable = (Var*)((RelabelType*)exprState->expr)->arg;
}
if (variable == NULL) {
ereport(ERROR,
(errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmodule(MOD_LLVM), errmsg("Failed to get outer hash key!")));
}
outkeyIdx_var = variable->varattno - 1;
/* get the ScalarVector = m_arr[outkeyIdx_var] */
DEFINE_CGVAR_INT64(int64_outkeyIdx, (long long)outkeyIdx_var);
DEFINE_CGVAR_INT64(int64_keyIdx, (long long)keyIdx_var);
tmpval = builder.CreateInBoundsGEP(batch_arr, int64_outkeyIdx); // address of ScalarVector
Vals[0] = int64_0;
Vals[1] = int32_pos_scalvec_vals;
tmpval = builder.CreateInBoundsGEP(tmpval, Vals);
tmpval = builder.CreateAlignedLoad(tmpval, 8, "outer_key_vals");
outer_keys_vals_array[j] = tmpval;
Vals[1] = int32_pos_scalvec_flag;
tmpval = builder.CreateInBoundsGEP(batch_arr, int64_outkeyIdx); // address of ScalarVector
tmpval = builder.CreateInBoundsGEP(tmpval, Vals);
tmpval = builder.CreateAlignedLoad(tmpval, 8, "outer_key_flag");
outer_keys_flag_array[j] = tmpval;
inner_keyIdx_array[j] = int64_keyIdx;
j++;
}
llvm::Value* loopIdx1 = builder.CreateSExt(Phi, int64Type);
builder.CreateBr(bb_loopEntry);
/* bb_loopEntry */
/* loop entry of the batch loop */
builder.SetInsertPoint(bb_loopEntry);
llvm::PHINode* Phi_Idx = builder.CreatePHI(int64Type, 2);
Phi_Idx->addIncoming(loopIdx1, bb_pre_loopEntry);
llvm::Value* loopIdx2 = NULL;
llvm::PHINode* Phi_resultRow_06 = builder.CreatePHI(int32Type, 2); // resultRow: number of result rows
Phi_resultRow_06->addIncoming(int32_0, bb_pre_loopEntry);
llvm::PHINode* Phi_resultRow_01 = NULL;
/* get HashJoinTbl.hashBasedOperator.m_cellCache */
tmpval = builder.CreatePtrToInt(hashJoinTbl, int64Type);
tmpval = builder.CreateAdd(tmpval, int64_8032);
tmpval2 = builder.CreateMul(Phi_Idx, int64_8);
tmpval = builder.CreateAdd(tmpval, tmpval2);
mcellCache = builder.CreateIntToPtr(tmpval, hashCellPPtrType);
llvm::Value* cellCache_loopEntry = builder.CreateAlignedLoad(mcellCache, 8, "cellCache");
tmpval = builder.CreatePtrToInt(cellCache_loopEntry, int64Type);
tmpval = builder.CreateICmpEQ(tmpval, int64_0);
builder.CreateCondBr(tmpval, bb_crit_edge, bb_pre_keyMatch);
/*
* bb_pre_keyMatch: ; preds = %bb_loopEntry
*/
llvm::Value* flag_batch = NULL;
llvm::Value* vals_batch = NULL;
builder.SetInsertPoint(bb_pre_keyMatch);
/* hashJoinTbl.hashBasedOperator.m_keyMatch[Phi_idx] */
llvm::Value* mkeyMatch = NULL;
if (node->enable_fast_keyMatch == 0) {
Vals4[0] = int64_0;
Vals4[1] = int32_0;
Vals4[2] = int32_6;
Vals4[3] = Phi_Idx;
mkeyMatch = builder.CreateInBoundsGEP(hashJoinTbl, Vals4);
} else {
/* just one hash clause */
tmpval = builder.CreateInBoundsGEP(outer_keys_flag_array[0], Phi_Idx);
flag_batch = builder.CreateAlignedLoad(tmpval, 1, "flag_outer_key");
tmpval = builder.CreateInBoundsGEP(outer_keys_vals_array[0], Phi_Idx);
vals_batch = builder.CreateAlignedLoad(tmpval, 1, "vals_outer_key");
}
builder.CreateBr(bb_keyMatch);
/*
* bb_keyMatch: ; preds = %bb_pre_keyMatch, %TryNextCell
*/
builder.SetInsertPoint(bb_keyMatch);
llvm::PHINode* Phi_resultRow_keyMatch = builder.CreatePHI(int32Type, 2);
Phi_resultRow_keyMatch->addIncoming(Phi_resultRow_06, bb_pre_keyMatch);
llvm::PHINode* Phi_resultRow_02 = NULL;
llvm::PHINode* Phi_cellCache_keyMatch = NULL;
Phi_cellCache_keyMatch = builder.CreatePHI(hashCellPtrType, 2);
Phi_cellCache_keyMatch->addIncoming(cellCache_loopEntry, bb_pre_keyMatch);
/* if there are multiple hash clauses, initialize m_keyMatch[i] to true
* and the match key result will be saved in m_keyMatch[i]
*/
if (node->enable_fast_keyMatch == 0) {
/* m_keyMatch[i] = 1 */
builder.CreateAlignedStore(int8_1, mkeyMatch, 1);
}
/* for each key, call the matchKey function
* matchKey(HashJoinTbl *this, ScalarVector *col, int64 loopIdx, int64 keyIdx)
*/
llvm::Value* keyMatch_result = NULL;
llvm::Function* jitted_matchKey = NULL;
for (j = 0; j < num_hashkey; j++) {
jitted_matchKey = VecHashJoinCodeGen::KeyMatchCodeGen(node, inner_var[j]);
if (jitted_matchKey == NULL) {
ereport(ERROR,
(errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
errmodule(MOD_LLVM),
errmsg("Codegen keyMatch failed for %d-th clause of hash join!\n", j)));
}
/* Have multiple hash clauses */
if (node->enable_fast_keyMatch == 0) {
tmpval = builder.CreateInBoundsGEP(outer_keys_flag_array[j], Phi_Idx);
flag_batch = builder.CreateAlignedLoad(tmpval, 1, "flag_outer_key");
tmpval = builder.CreateInBoundsGEP(outer_keys_vals_array[j], Phi_Idx);
vals_batch = builder.CreateAlignedLoad(tmpval, 1, "vals_outer_key");
}
if (node->enable_fast_keyMatch == 0)
keyMatch_result = builder.CreateCall(
jitted_matchKey, {Phi_cellCache_keyMatch, flag_batch, vals_batch, inner_keyIdx_array[j], mkeyMatch});
else
keyMatch_result = builder.CreateCall(
jitted_matchKey, {Phi_cellCache_keyMatch, flag_batch, vals_batch, inner_keyIdx_array[j]});
}
if (node->enable_fast_keyMatch == 0) {
tmpval = builder.CreateAlignedLoad(mkeyMatch, 1, "keyMatch");
tmpval = builder.CreateICmpEQ(tmpval, int8_0);
builder.CreateCondBr(tmpval, bb_NoMatch, bb_Matched);
} else {
/* when there is only one hash clause */
keyMatch_result = builder.CreateTrunc(keyMatch_result, int1Type);
builder.CreateCondBr(keyMatch_result, bb_Matched, bb_NoMatch);
}
/* Generate the code to copy data from batch/hashCell to m_outerBatch/m_innerBatch
* if(m_keyMatch[i])
* {
* val = m_cellCache[i]->m_val;
*
* for(j = 0 ; j < m_innerBatch->m_cols; j++)
* {
* pVector = &m_innerBatch->m_arr[j];
*
* pVector->m_vals[resultRow] = val[j].val;
* pVector->m_flag[resultRow] = val[j].flag;
* }
*
* for(j = 0 ; j < m_outerBatch->m_cols; j++)
* {
* pVector = &m_outerBatch->m_arr[j];
* pVector->m_vals[resultRow] = batch->m_arr[j].m_vals[i];
* pVector->m_flag[resultRow] = batch->m_arr[j].m_flag[i];
* }
* resultRow++;
* }
*/
builder.SetInsertPoint(bb_Matched);
llvm::Value* cellCache = Phi_cellCache_keyMatch;
mcols = innerPlanState(node)->ps_ResultTupleSlot->tts_tupleDescriptor->natts;
llvm::Value* resultRow = builder.CreateSExt(Phi_resultRow_keyMatch, int64Type);
/* copy data from hash cell to innerBatch */
llvm::Value* resultRow_i64 = builder.CreateSExt(resultRow, int64Type);
for (j = 0; j < mcols; j++) {
mvals = builder.CreateInBoundsGEP(inner_vals_array[j], resultRow_i64);
mflag = builder.CreateInBoundsGEP(inner_flag_array[j], resultRow_i64);
/* get val from cellCache */
DEFINE_CGVAR_INT64(int64_jIdx, (long long)j);
Vals4[1] = int32_1;
Vals4[2] = int64_jIdx;
Vals4[3] = int32_0;
val = builder.CreateInBoundsGEP(cellCache, Vals4);
val = builder.CreateAlignedLoad(val, 8, "cell_val");
/* get flag from cellCache */
Vals4[3] = int32_1;
flag = builder.CreateInBoundsGEP(cellCache, Vals4);
flag = builder.CreateAlignedLoad(flag, 1, "cell_flag");
/* Assign the cell val to mvals and cell flag to mflag */
builder.CreateAlignedStore(val, mvals, 8);
builder.CreateAlignedStore(flag, mflag, 1);
}
/* copy data from batch to outerBatch */
mcols = outerPlanState(node)->ps_ResultTupleSlot->tts_tupleDescriptor->natts;
for (j = 0; j < mcols; j++) {
/* Get the address from outerBatch*/
mvals = builder.CreateInBoundsGEP(outer_vals_array[j], resultRow_i64);
mflag = builder.CreateInBoundsGEP(outer_flag_array[j], resultRow_i64);
/* Get the val and flag from batch */
val = builder.CreateInBoundsGEP(batch_vals_array[j], Phi_Idx);
val = builder.CreateAlignedLoad(val, 8, "batch_val");
flag = builder.CreateInBoundsGEP(batch_flag_array[j], Phi_Idx);
flag = builder.CreateAlignedLoad(flag, 1, "batch_flag");
/* Assign the cell val to mvals and cell flag to mflag */
builder.CreateAlignedStore(val, mvals, 8);
builder.CreateAlignedStore(flag, mflag, 1);
}
/* resultRow++ */
llvm::Value* resultRow_Matched = builder.CreateAdd(Phi_resultRow_keyMatch, int32_1);
tmpval = builder.CreateICmpEQ(resultRow_Matched, int32_BatchMaxSize);
builder.CreateCondBr(tmpval, bb_BatchFull, bb_TryNextCell);
builder.SetInsertPoint(bb_NoMatch);
builder.CreateBr(bb_TryNextCell);
/*
* corresponding to the following code:
* if(resultRow == BatchMaxSize)
* {
* m_joinStateLog.lastBuildIdx = i;
* m_joinStateLog.lastCell = m_cellCache[i]->flag.m_next;
* m_joinStateLog.restore = true;
* return batch;
* }
*/
builder.SetInsertPoint(bb_BatchFull);
llvm::Value* idx_int32 = builder.CreateTrunc(Phi_Idx, int32Type);
builder.CreateAlignedStore(idx_int32, lastBuildIdx_addr, 4);
Vals3[0] = int64_0;
Vals3[1] = int32_0;
Vals3[2] = int32_0;
tmpval = builder.CreateInBoundsGEP(cellCache, Vals3);
llvm::Value* nextCell = builder.CreateAlignedLoad(tmpval, 8, "nextCell");
builder.CreateAlignedStore(nextCell, lastCell_addr, 8);
builder.CreateAlignedStore(int8_1, joinStateLog_restore, 1);
builder.CreateBr(bb_ret);
/* try next cell of m_cellCache[i] and update m_cellCache[i] if next cell exists */
builder.SetInsertPoint(bb_TryNextCell);
Phi_resultRow_02 = builder.CreatePHI(int32Type, 2);
Phi_resultRow_02->addIncoming(resultRow_Matched, bb_Matched);
Phi_resultRow_02->addIncoming(Phi_resultRow_keyMatch, bb_NoMatch);
/* Phi_resultRow_keyMatch- is in bb_keyMatch */
Phi_resultRow_keyMatch->addIncoming(Phi_resultRow_02, bb_TryNextCell);
/* m_cellCache[i]->flag.m_next */
Vals3[1] = int32_0;
Vals3[2] = int32_0;
tmpval = builder.CreateInBoundsGEP(cellCache, Vals3);
nextCell = builder.CreateAlignedLoad(tmpval, 8, "nextCell");
Phi_cellCache_keyMatch->addIncoming(nextCell, bb_TryNextCell);
tmpval = builder.CreatePtrToInt(nextCell, int64Type);
tmpval = builder.CreateICmpEQ(tmpval, int64_0);
builder.CreateCondBr(tmpval, bb_crit_edge, bb_keyMatch);
/* loop_index++ */
/* Phi resultRow (loopEntry, TryNextCell)
* if (loop_index < nRows) goto bb_loopEntry
* else goto bb_ret
*/
builder.SetInsertPoint(bb_crit_edge);
Phi_resultRow_01 = builder.CreatePHI(int32Type, 2);
Phi_resultRow_01->addIncoming(Phi_resultRow_06, bb_loopEntry);
Phi_resultRow_01->addIncoming(Phi_resultRow_02, bb_TryNextCell);
// Phi_resultRow_06- is in bb_loopEntry
Phi_resultRow_06->addIncoming(Phi_resultRow_01, bb_crit_edge);
loopIdx2 = builder.CreateAdd(Phi_Idx, int64_1);
Phi_Idx->addIncoming(loopIdx2, bb_crit_edge);
tmpval = builder.CreateTrunc(loopIdx2, int32Type);
tmpval = builder.CreateICmpSLT(tmpval, nRows);
builder.CreateCondBr(tmpval, bb_loopEntry, bb_ret);
/* return resultRow */
builder.SetInsertPoint(bb_ret);
llvm::PHINode* Phi_resultRow = builder.CreatePHI(int32Type, 3);
Phi_resultRow->addIncoming(int32_BatchMaxSize, bb_BatchFull);
llvm::Value* resultRow_crit_edge = builder.CreateTrunc(Phi_resultRow_01, int32Type);
Phi_resultRow->addIncoming(resultRow_crit_edge, bb_crit_edge);
Phi_resultRow->addIncoming(int32_0, bb_restoreFalse);
builder.CreateRet(Phi_resultRow);
/* free all temperatory memory */
pfree_ext(inner_vals_array);
pfree_ext(inner_flag_array);
pfree_ext(outer_vals_array);
pfree_ext(outer_flag_array);
pfree_ext(batch_vals_array);
pfree_ext(batch_flag_array);
pfree_ext(outer_keys_flag_array);
pfree_ext(outer_keys_vals_array);
pfree_ext(inner_keyIdx_array);
pfree_ext(inner_var);
/* finalize the jitted function */
llvmCodeGen->FinalizeFunction(jitted_hashjoin, node->js.ps.plan->plan_node_id);
return jitted_hashjoin;
}
/* Codegen fast path of innerJoinT with inlined match key function.
* Condition: (1) All hash keys are NOT NULL columns from base tables
* (2) All hash key data types are int4 or int8
*/
llvm::Function* VecHashJoinCodeGen::HashJoinCodeGen_fastpath(VecHashJoinState* node)
{
Assert(NULL != (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj);
GsCodeGen* llvmCodeGen = (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj;
/* Find and load the IR file from the installaion directory */
llvmCodeGen->loadIRFile();
/* Get LLVM Context and builder */
llvm::LLVMContext& context = llvmCodeGen->context();
GsCodeGen::LlvmBuilder builder(context);
llvm::Value* hashJoinTbl = NULL;
llvm::Value* batch = NULL;
llvm::Function* jitted_hashjoin = NULL;
llvm::Value* tmpval = NULL;
llvm::Value* llvmargs[2];
int mcols, j;
llvm::Value* val = NULL;
llvm::Value* flag = NULL;
llvm::Value* mvals = NULL;
llvm::Value* mflag = NULL;
/* Define data types and some llvm consts */
DEFINE_CG_TYPE(int32Type, INT4OID);
DEFINE_CG_TYPE(int64Type, INT8OID);
DEFINE_CG_PTRTYPE(VectorBatchPtrType, "class.VectorBatch");
DEFINE_CG_PTRTYPE(HashJoinTblPtrType, "class.HashJoinTbl");
DEFINE_CG_PTRTYPE(hashCellPtrType, "struct.hashCell");
DEFINE_CGVAR_INT8(int8_0, 0);
DEFINE_CGVAR_INT8(int8_1, 1);
DEFINE_CGVAR_INT32(int32_0, 0);
DEFINE_CGVAR_INT32(int32_1, 1);
DEFINE_CGVAR_INT32(int32_2, 2);
DEFINE_CGVAR_INT64(int64_0, 0);
DEFINE_CGVAR_INT64(int64_1, 1);
DEFINE_CGVAR_INT32(int32_BatchMaxSize, BatchMaxSize);
DEFINE_CGVAR_INT32(int32_pos_hjointbl_StateLog, pos_hjointbl_StateLog);
DEFINE_CGVAR_INT32(int32_pos_batch_marr, pos_batch_marr);
DEFINE_CGVAR_INT32(int32_pos_hjointbl_inbatch, pos_hjointbl_inbatch);
DEFINE_CGVAR_INT32(int32_pos_hjointbl_outbatch, pos_hjointbl_outbatch);
DEFINE_CGVAR_INT32(int32_pos_scalvec_vals, pos_scalvec_vals);
DEFINE_CGVAR_INT32(int32_pos_scalvec_flag, pos_scalvec_flag);
/* llvm array values, used to represent the location of some element */
llvm::Value* Vals[2] = {int64_0, int32_0};
llvm::Value* Vals3[3] = {int64_0, int32_0, int32_0};
llvm::Value* Vals4[4] = {int64_0, int32_0, int32_0, int32_0};
GsCodeGen::FnPrototype fn_prototype(llvmCodeGen, "JittedHashInnerJoin_fastpath", int32Type);
fn_prototype.addArgument(GsCodeGen::NamedVariable("hashJoinTbl", HashJoinTblPtrType));
fn_prototype.addArgument(GsCodeGen::NamedVariable("batch", VectorBatchPtrType));
jitted_hashjoin = fn_prototype.generatePrototype(&builder, &llvmargs[0]);
/* start the main codegen process for hash inner join */
hashJoinTbl = llvmargs[0];
batch = llvmargs[1];
/* define basic block information for batch loop */
llvm::BasicBlock* entry = &jitted_hashjoin->getEntryBlock();
DEFINE_BLOCK(bb_restoreTrue, jitted_hashjoin);
DEFINE_BLOCK(bb_restoreFalse, jitted_hashjoin);
DEFINE_BLOCK(bb_pre_loopEntry, jitted_hashjoin);
DEFINE_BLOCK(bb_ret, jitted_hashjoin);
DEFINE_BLOCK(bb_loopEntry, jitted_hashjoin);
DEFINE_BLOCK(bb_crit_edge, jitted_hashjoin);
/* define basic block information for key-value matching */
DEFINE_BLOCK(bb_keyMatch, jitted_hashjoin);
DEFINE_BLOCK(bb_Matched, jitted_hashjoin);
DEFINE_BLOCK(bb_NoMatch, jitted_hashjoin);
DEFINE_BLOCK(bb_BatchFull, jitted_hashjoin);
DEFINE_BLOCK(bb_TryNextCell, jitted_hashjoin);
/* entry basic block */
/* get the number of rows of this batch */
tmpval = builder.CreateInBoundsGEP(batch, Vals);
llvm::Value* nRows = builder.CreateAlignedLoad(tmpval, 4, "m_rows");
/* get HashJoinTbl.m_joinStateLog.lastBuildIdx */
Vals3[0] = int64_0;
Vals3[1] = int32_pos_hjointbl_StateLog;
Vals3[2] = int32_0;
llvm::Value* lastBuildIdx_addr = builder.CreateInBoundsGEP(hashJoinTbl, Vals3);
/* get HashJoinTbl.m_joinStateLog.lastCell */
Vals3[2] = int32_1;
llvm::Value* lastCell_addr = builder.CreateInBoundsGEP(hashJoinTbl, Vals3);
/* get HashJoinTbl.m_joinStateLog.restore */
Vals3[2] = int32_2;
llvm::Value* joinStateLog_restore = builder.CreateInBoundsGEP(hashJoinTbl, Vals3);
tmpval = builder.CreateAlignedLoad(joinStateLog_restore, 1, "restore");
tmpval = builder.CreateICmpEQ(tmpval, int8_0);
builder.CreateCondBr(tmpval, bb_restoreFalse, bb_restoreTrue);
/* if restore == true, get the loop start index and cellCache from
* m_joinStateLog, and reset the restore to false.
*/
builder.SetInsertPoint(bb_restoreTrue);
builder.CreateAlignedStore(int8_0, joinStateLog_restore, 1);
/* get lastBuildIdx from m_joinStateLog */
llvm::Value* lastBuildIdx = builder.CreateAlignedLoad(lastBuildIdx_addr, 4, "lastBuildIdx");
/* get m_joinStateLog.lastCell */
llvm::Value* lastCell = builder.CreateAlignedLoad(lastCell_addr, 8, "lastCell");
/* get hashJoinTbl.vechashtable.m_cellCache[lastBuildIdx] */
DEFINE_CGVAR_INT64(int64_8032, 8032);
DEFINE_CGVAR_INT64(int64_8, 8);
tmpval = builder.CreatePtrToInt(hashJoinTbl, int64Type);
tmpval = builder.CreateAdd(tmpval, int64_8032);
llvm::Value* tmpval2 = builder.CreateSExt(lastBuildIdx, int64Type);
tmpval2 = builder.CreateMul(tmpval2, int64_8);
tmpval = builder.CreateAdd(tmpval, tmpval2);
DEFINE_CG_PTRTYPE(hashCellPPtrType, hashCellPtrType);
llvm::Value* mcellCache = builder.CreateIntToPtr(tmpval, hashCellPPtrType);
/* m_cellCache[lastBuildIdx] = m_joinStateLog.lastCell */
builder.CreateAlignedStore(lastCell, mcellCache, 8);
builder.CreateBr(bb_restoreFalse);
/* restore is false, so start the loop from 0
* bb_restoreFalse:
*/
builder.SetInsertPoint(bb_restoreFalse);
llvm::PHINode* Phi = builder.CreatePHI(int32Type, 2);
Phi->addIncoming(int32_0, entry);
Phi->addIncoming(lastBuildIdx, bb_restoreTrue);
tmpval = builder.CreateICmpSLT(Phi, nRows);
builder.CreateCondBr(tmpval, bb_pre_loopEntry, bb_ret);
/*
* bb_pre_loopEntry:
* br label %bb_loopEntry
* This basic block is the one prior to the batch loop.
* Load all necessary ScalarVectors (batch loop constants) here.
* innerBatch, outerBatch, inner_arr, outer_arr, inner_vals/inner_flag,
* outer_vals/outer_flag, batch_vals/batch_flag, ...
*/
builder.SetInsertPoint(bb_pre_loopEntry);
/* Get m_arr from batch */
Vals[1] = int32_pos_batch_marr;
llvm::Value* arr = builder.CreateInBoundsGEP(batch, Vals);
llvm::Value* batch_arr = builder.CreateAlignedLoad(arr, 8, "batch_arr");
/* hashJoinTbl.m_innerBatch */
Vals[1] = int32_pos_hjointbl_inbatch;
llvm::Value* innerBatch = builder.CreateInBoundsGEP(hashJoinTbl, Vals);
/* hashJoinTbl.m_outerBatch */
Vals[1] = int32_pos_hjointbl_outbatch;
llvm::Value* outerBatch = builder.CreateInBoundsGEP(hashJoinTbl, Vals);
innerBatch = builder.CreateAlignedLoad(innerBatch, 8, "innerBatch");
outerBatch = builder.CreateAlignedLoad(outerBatch, 8, "outerBatch");
Vals[1] = int32_pos_batch_marr;
llvm::Value* inner_arr = builder.CreateInBoundsGEP(innerBatch, Vals);
inner_arr = builder.CreateAlignedLoad(inner_arr, 8, "inner_arr");
llvm::Value* outer_arr = builder.CreateInBoundsGEP(outerBatch, Vals);
outer_arr = builder.CreateAlignedLoad(outer_arr, 8, "outer_arr");
mcols = innerPlanState(node)->ps_ResultTupleSlot->tts_tupleDescriptor->natts;
llvm::Value** inner_vals_array = (llvm::Value**)palloc(sizeof(llvm::Value*) * mcols);
llvm::Value** inner_flag_array = (llvm::Value**)palloc(sizeof(llvm::Value*) * mcols);
for (j = 0; j < mcols; j++) {
DEFINE_CGVAR_INT64(int64_jIdx, (long long)j);
/* Get m_vals from pVector */
Vals[0] = int64_jIdx;
Vals[1] = int32_pos_scalvec_vals;
tmpval = builder.CreateInBoundsGEP(inner_arr, Vals);
tmpval = builder.CreateAlignedLoad(tmpval, 8, "inner_vals");
inner_vals_array[j] = tmpval;
/* Get m_flag from pVector */
Vals[1] = int32_pos_scalvec_flag;
tmpval = builder.CreateInBoundsGEP(inner_arr, Vals);
tmpval = builder.CreateAlignedLoad(tmpval, 8, "inner_flag");
inner_flag_array[j] = tmpval;
}
mcols = outerPlanState(node)->ps_ResultTupleSlot->tts_tupleDescriptor->natts;
llvm::Value** outer_vals_array = (llvm::Value**)palloc(sizeof(llvm::Value*) * mcols);
llvm::Value** outer_flag_array = (llvm::Value**)palloc(sizeof(llvm::Value*) * mcols);
llvm::Value** batch_vals_array = (llvm::Value**)palloc(sizeof(llvm::Value*) * mcols);
llvm::Value** batch_flag_array = (llvm::Value**)palloc(sizeof(llvm::Value*) * mcols);
for (j = 0; j < mcols; j++) {
DEFINE_CGVAR_INT64(int64_jIdx, (long long)j);
Vals[0] = int64_jIdx;
/* Get m_vals from pVector */
Vals[1] = int32_pos_scalvec_vals;
tmpval = builder.CreateInBoundsGEP(outer_arr, Vals);
tmpval = builder.CreateAlignedLoad(tmpval, 8, "outer_vals");
outer_vals_array[j] = tmpval;
tmpval = builder.CreateInBoundsGEP(batch_arr, Vals);
tmpval = builder.CreateAlignedLoad(tmpval, 8, "batch_val");
batch_vals_array[j] = tmpval;
/* Get m_flag from pVector */
Vals[1] = int32_pos_scalvec_flag;
tmpval = builder.CreateInBoundsGEP(outer_arr, Vals);
tmpval = builder.CreateAlignedLoad(tmpval, 8, "outer_flag");
outer_flag_array[j] = tmpval;
tmpval = builder.CreateInBoundsGEP(batch_arr, Vals);
tmpval = builder.CreateAlignedLoad(tmpval, 1, "batch_flag");
batch_flag_array[j] = tmpval;
}
ListCell* lc_inner = NULL;
ListCell* lc_outer = NULL;
int keyIdx_var = 0;
int outkeyIdx_var = 0;
int num_hashkey = list_length(node->hj_InnerHashKeys);
Var* variable = NULL;
ExprState* exprState = NULL;
llvm::Value** outer_key_mvals_array = (llvm::Value**)palloc(sizeof(llvm::Value*) * num_hashkey);
llvm::Value** inner_keyIdx_array = (llvm::Value**)palloc(sizeof(llvm::Value*) * num_hashkey);
Var** inner_var = (Var**)palloc(sizeof(Var*) * num_hashkey);
j = 0;
forboth(lc_inner, node->hj_InnerHashKeys, lc_outer, node->hj_OuterHashKeys)
{
/* get inner keyIdx */
variable = NULL;
exprState = (ExprState*)lfirst(lc_inner);
if (IsA(exprState->expr, Var))
variable = (Var*)exprState->expr;
else if (IsA(exprState->expr, RelabelType)) {
RelabelType* reltype = (RelabelType*)exprState->expr;
if (IsA(reltype->arg, Var) && ((Var*)reltype->arg)->varattno > 0)
variable = (Var*)((RelabelType*)exprState->expr)->arg;
}
if (variable == NULL) {
ereport(ERROR,
(errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmodule(MOD_LLVM), errmsg("Failed to get inner hash key!")));
}
keyIdx_var = variable->varattno - 1;
inner_var[j] = variable;
/* get outer keyIdx */
variable = NULL;
exprState = (ExprState*)lfirst(lc_outer);
if (IsA(exprState->expr, Var))
variable = (Var*)exprState->expr;
else if (IsA(exprState->expr, RelabelType)) {
RelabelType* reltype = (RelabelType*)exprState->expr;
if (IsA(reltype->arg, Var) && ((Var*)reltype->arg)->varattno > 0)
variable = (Var*)((RelabelType*)exprState->expr)->arg;
}
if (variable == NULL) {
ereport(ERROR,
(errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmodule(MOD_LLVM), errmsg("Failed to get outer hash key!")));
}
outkeyIdx_var = variable->varattno - 1;
/* get the ScalarVector = m_arr[outkeyIdx_var] */
DEFINE_CGVAR_INT64(int64_outkeyIdx, (long long)outkeyIdx_var);
DEFINE_CGVAR_INT64(int64_keyIdx, (long long)keyIdx_var);
tmpval = builder.CreateInBoundsGEP(batch_arr, int64_outkeyIdx);
/* get val from batch->m_arr[outkeyIdx].m_vals[i] */
Vals[0] = int64_0;
Vals[1] = int32_pos_scalvec_vals;
tmpval = builder.CreateInBoundsGEP(tmpval, Vals);
tmpval = builder.CreateAlignedLoad(tmpval, 8, "batch_vals");
inner_keyIdx_array[j] = int64_keyIdx;
outer_key_mvals_array[j] = tmpval;
j++;
}
llvm::Value* loopIdx1 = builder.CreateSExt(Phi, int64Type);
builder.CreateBr(bb_loopEntry);
/* bb_loopEntry */
builder.SetInsertPoint(bb_loopEntry);
llvm::PHINode* Phi_Idx = builder.CreatePHI(int64Type, 2);
Phi_Idx->addIncoming(loopIdx1, bb_pre_loopEntry);
llvm::Value* loopIdx2 = NULL;
/* Phi_Idx->addIncoming(loopIdx2, bb_crit_edge); do this in bb_crit_edge*/
llvm::PHINode* Phi_resultRow_06 = builder.CreatePHI(int32Type, 2);
Phi_resultRow_06->addIncoming(int32_0, bb_pre_loopEntry);
llvm::PHINode* Phi_resultRow_01 = NULL;
/* get HashJoinTbl.hashBasedOperator.m_cellCache */
tmpval = builder.CreatePtrToInt(hashJoinTbl, int64Type);
tmpval = builder.CreateAdd(tmpval, int64_8032);
tmpval2 = builder.CreateMul(Phi_Idx, int64_8);
tmpval = builder.CreateAdd(tmpval, tmpval2);
mcellCache = builder.CreateIntToPtr(tmpval, hashCellPPtrType);
llvm::Value* cellCache_loopEntry = builder.CreateAlignedLoad(mcellCache, 8, "cellCache");
tmpval = builder.CreatePtrToInt(cellCache_loopEntry, int64Type);
tmpval = builder.CreateICmpEQ(tmpval, int64_0);
builder.CreateCondBr(tmpval, bb_crit_edge, bb_keyMatch);
/* bb_keyMatch */
builder.SetInsertPoint(bb_keyMatch);
llvm::PHINode* Phi_resultRow_keyMatch = builder.CreatePHI(int32Type, 2);
Phi_resultRow_keyMatch->addIncoming(Phi_resultRow_06, bb_loopEntry);
llvm::PHINode* Phi_resultRow_02 = NULL;
llvm::PHINode* Phi_cellCache_keyMatch = NULL;
Phi_cellCache_keyMatch = builder.CreatePHI(hashCellPtrType, 2);
Phi_cellCache_keyMatch->addIncoming(cellCache_loopEntry, bb_loopEntry);
Vals3[0] = int64_0;
Vals3[1] = int32_0;
Vals3[2] = int32_0;
tmpval = builder.CreateInBoundsGEP(Phi_cellCache_keyMatch, Vals3);
llvm::Value* nextCell = builder.CreateAlignedLoad(tmpval, 8, "nextCell");
Phi_cellCache_keyMatch->addIncoming(nextCell, bb_TryNextCell);
llvm::Value* fastPath_result = NULL;
llvm::Value* oneMatch_result = NULL;
/* fast path keyMatch
* condition: (1) NOT NULL for both hash keys
* (2) integer hash keys
*/
llvm::Value* cellCache = NULL;
llvm::Value* val_cell = NULL;
llvm::Value* val_batch = NULL;
cellCache = Phi_cellCache_keyMatch;
for (j = 0; j < num_hashkey; j++) {
Vals4[0] = int64_0;
Vals4[1] = int32_1; // Get val from cellCache
Vals4[2] = inner_keyIdx_array[j];
Vals4[3] = int32_0;
val_cell = builder.CreateInBoundsGEP(cellCache, Vals4);
val_cell = builder.CreateAlignedLoad(val_cell, 8, "cell_val");
val_batch = builder.CreateInBoundsGEP(outer_key_mvals_array[j], Phi_Idx);
val_batch = builder.CreateAlignedLoad(val_batch, 8, "m_vals");
switch (variable->vartype) {
case INT4OID: {
val_cell = builder.CreateTrunc(val_cell, int32Type);
val_batch = builder.CreateTrunc(val_batch, int32Type);
oneMatch_result = builder.CreateICmpEQ(val_cell, val_batch);
} break;
case INT8OID:
oneMatch_result = builder.CreateICmpEQ(val_cell, val_batch);
break;
default:
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmodule(MOD_LLVM),
errmsg("Codegen fast keyMatch failed: unsupported data type!\n")));
}
if (fastPath_result == NULL)
fastPath_result = oneMatch_result;
else {
fastPath_result = builder.CreateAnd(fastPath_result, oneMatch_result);
}
}
builder.CreateCondBr(fastPath_result, bb_Matched, bb_NoMatch);
/* Generate the code to copy data from batch/hashCell to m_outerBatch/m_innerBatch
* if(m_keyMatch[i])
* {
* val = m_cellCache[i]->m_val;
*
* for(j = 0 ; j < m_innerBatch->m_cols; j++)
* {
* pVector = &m_innerBatch->m_arr[j];
*
* pVector->m_vals[resultRow] = val[j].val;
* pVector->m_flag[resultRow] = val[j].flag;
* }
*
* for(j = 0 ; j < m_outerBatch->m_cols; j++)
* {
* pVector = &m_outerBatch->m_arr[j];
* pVector->m_vals[resultRow] = batch->m_arr[j].m_vals[i];
* pVector->m_flag[resultRow] = batch->m_arr[j].m_flag[i];
* }
* resultRow++;
* }
*/
builder.SetInsertPoint(bb_Matched);
cellCache = Phi_cellCache_keyMatch;
mcols = innerPlanState(node)->ps_ResultTupleSlot->tts_tupleDescriptor->natts;
llvm::Value* resultRow = builder.CreateSExt(Phi_resultRow_keyMatch, int64Type);
/* copy data from hash cell to innerBatch */
llvm::Value* resultRow_i64 = builder.CreateSExt(resultRow, int64Type);
for (j = 0; j < mcols; j++) {
mvals = builder.CreateInBoundsGEP(inner_vals_array[j], resultRow_i64);
mflag = builder.CreateInBoundsGEP(inner_flag_array[j], resultRow_i64);
DEFINE_CGVAR_INT64(int64_jIdx, (long long)j);
/* Get val from cellCache */
Vals4[1] = int32_1;
Vals4[2] = int64_jIdx;
Vals4[3] = int32_0;
val = builder.CreateInBoundsGEP(cellCache, Vals4);
val = builder.CreateAlignedLoad(val, 8, "cell_val");
/* get flag from cellCache */
Vals4[3] = int32_1;
flag = builder.CreateInBoundsGEP(cellCache, Vals4);
flag = builder.CreateAlignedLoad(flag, 1, "cell_flag");
/* Assign the cell val to mvals and cell flag to mflag */
builder.CreateAlignedStore(val, mvals, 8);
builder.CreateAlignedStore(flag, mflag, 1);
}
/* copy data from batch to outerBatch */
mcols = outerPlanState(node)->ps_ResultTupleSlot->tts_tupleDescriptor->natts;
for (j = 0; j < mcols; j++) {
/* Get the address from outerBatch*/
mvals = builder.CreateInBoundsGEP(outer_vals_array[j], resultRow_i64);
mflag = builder.CreateInBoundsGEP(outer_flag_array[j], resultRow_i64);
/* Get the val and flag from batch */
val = builder.CreateInBoundsGEP(batch_vals_array[j], Phi_Idx);
val = builder.CreateAlignedLoad(val, 8, "batch_val");
flag = builder.CreateInBoundsGEP(batch_flag_array[j], Phi_Idx);
flag = builder.CreateAlignedLoad(flag, 1, "batch_flag");
/* Assign the cell val to mvals and cell flag to mflag */
builder.CreateAlignedStore(val, mvals, 8);
builder.CreateAlignedStore(flag, mflag, 1);
}
/* resultRow++ */
llvm::Value* resultRow_Matched = builder.CreateAdd(Phi_resultRow_keyMatch, int32_1);
tmpval = builder.CreateICmpEQ(resultRow_Matched, int32_BatchMaxSize);
builder.CreateCondBr(tmpval, bb_BatchFull, bb_TryNextCell);
builder.SetInsertPoint(bb_NoMatch);
builder.CreateBr(bb_TryNextCell);
/*
* corresponding to the following code:
* if(resultRow == BatchMaxSize)
* {
* m_joinStateLog.lastBuildIdx = i;
* m_joinStateLog.lastCell = m_cellCache[i]->flag.m_next;
* m_joinStateLog.restore = true;
* return batch;
* }
*/
builder.SetInsertPoint(bb_BatchFull);
llvm::Value* idx_int32 = builder.CreateTrunc(Phi_Idx, int32Type);
builder.CreateAlignedStore(idx_int32, lastBuildIdx_addr, 4);
/* Save m_cellCache[i]->flag.m_next to joinStateLog */
builder.CreateAlignedStore(nextCell, lastCell_addr, 8);
builder.CreateAlignedStore(int8_1, joinStateLog_restore, 1);
builder.CreateBr(bb_ret);
/* try next cell of m_cellCache[i] and update m_cellCache[i] if next cell exists */
builder.SetInsertPoint(bb_TryNextCell);
Phi_resultRow_02 = builder.CreatePHI(int32Type, 2);
Phi_resultRow_02->addIncoming(resultRow_Matched, bb_Matched);
Phi_resultRow_02->addIncoming(Phi_resultRow_keyMatch, bb_NoMatch);
/* Phi_resultRow_keyMatch- is in bb_keyMatch */
Phi_resultRow_keyMatch->addIncoming(Phi_resultRow_02, bb_TryNextCell);
tmpval = builder.CreatePtrToInt(nextCell, int64Type);
tmpval = builder.CreateICmpEQ(tmpval, int64_0);
builder.CreateCondBr(tmpval, bb_crit_edge, bb_keyMatch);
/* loop_index++ */
/* Phi resultRow (loopEntry, TryNextCell)
* if (loop_index < nRows) goto bb_loopEntry
* else goto bb_ret
*/
builder.SetInsertPoint(bb_crit_edge);
Phi_resultRow_01 = builder.CreatePHI(int32Type, 2);
Phi_resultRow_01->addIncoming(Phi_resultRow_06, bb_loopEntry);
Phi_resultRow_01->addIncoming(Phi_resultRow_02, bb_TryNextCell);
/* Phi_resultRow_06- is in bb_loopEntry */
Phi_resultRow_06->addIncoming(Phi_resultRow_01, bb_crit_edge);
loopIdx2 = builder.CreateAdd(Phi_Idx, int64_1); // Phi_Idx is in bb_loopEntry
Phi_Idx->addIncoming(loopIdx2, bb_crit_edge);
tmpval = builder.CreateTrunc(loopIdx2, int32Type);
tmpval = builder.CreateICmpSLT(tmpval, nRows);
builder.CreateCondBr(tmpval, bb_loopEntry, bb_ret);
/* return resultRow */
builder.SetInsertPoint(bb_ret);
llvm::PHINode* Phi_resultRow = builder.CreatePHI(int32Type, 3);
Phi_resultRow->addIncoming(int32_BatchMaxSize, bb_BatchFull);
llvm::Value* resultRow_crit_edge = builder.CreateTrunc(Phi_resultRow_01, int32Type);
Phi_resultRow->addIncoming(resultRow_crit_edge, bb_crit_edge);
Phi_resultRow->addIncoming(int32_0, bb_restoreFalse);
builder.CreateRet(Phi_resultRow);
pfree_ext(inner_vals_array);
pfree_ext(inner_flag_array);
pfree_ext(outer_vals_array);
pfree_ext(outer_flag_array);
pfree_ext(batch_vals_array);
pfree_ext(batch_flag_array);
pfree_ext(outer_key_mvals_array);
pfree_ext(inner_keyIdx_array);
pfree_ext(inner_var);
/* finalize the jitted function */
llvmCodeGen->FinalizeFunction(jitted_hashjoin, node->js.ps.plan->plan_node_id);
return jitted_hashjoin;
}
/*
* @Description : Codegen buildHashTable for hash join. Replace the hash
* function(hashint4 + hash_uint32) by CRC32 hardware instruction.
* @Notes : Only Support the case with condition:
* (1) int4, int8, or bpchar hash keys
* (2) NeedCopy is false
* (3) m_complicateJoinKey is false
*/
llvm::Function* VecHashJoinCodeGen::HashJoinCodeGen_buildHashTable(VecHashJoinState* node)
{
Assert(NULL != (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj);
GsCodeGen* llvmCodeGen = (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj;
/* Find and load the IR file from the installaion directory */
llvmCodeGen->loadIRFile();
/* Get LLVM Context and builder */
llvm::LLVMContext& context = llvmCodeGen->context();
GsCodeGen::LlvmBuilder builder(context);
llvm::Module* mod = llvmCodeGen->module();
llvm::Function* jitted_buildHashTable = NULL;
llvm::Value* tmpval = NULL;
llvm::Value* llvmargs[2];
llvm::Value* cellHead = NULL;
llvm::Value* hashTbl = NULL;
int j;
llvm::Value* val = NULL;
llvm::Value* flag = NULL;
llvm::Value* mask = NULL;
llvm::Value* hash_result = NULL;
/* Define data types and some llvm consts */
DEFINE_CG_VOIDTYPE(voidType);
DEFINE_CG_TYPE(int32Type, INT4OID);
DEFINE_CG_TYPE(int64Type, INT8OID);
DEFINE_CG_PTRTYPE(int8PtrType, CHAROID);
DEFINE_CG_PTRTYPE(int16PtrType, INT2OID);
DEFINE_CG_PTRTYPE(int32PtrType, INT4OID);
DEFINE_CG_PTRTYPE(int64PtrType, INT8OID);
DEFINE_CG_PTRTYPE(hashCellPtrType, "struct.hashCell");
DEFINE_CG_PTRTYPE(vechashtablePtrType, "class.vechashtable");
DEFINE_CGVAR_INT8(int8_0, 0);
DEFINE_CGVAR_INT8(int8_1, 1);
DEFINE_CGVAR_INT32(int32_0, 0);
DEFINE_CGVAR_INT32(int32_1, 1);
DEFINE_CGVAR_INT64(int64_0, 0);
DEFINE_CGVAR_INT64(int64_2, 2);
DEFINE_CGVAR_INT64(int64_4, 4);
DEFINE_CGVAR_INT64(int64_8, 8);
/* llvm array values, used to represent the location of some element */
llvm::Value* Vals2[2] = {int64_0, int32_0};
llvm::Value* Vals3[3] = {int64_0, int32_0, int32_0};
llvm::Value* Vals4[4] = {int64_0, int32_0, int32_0, int32_0};
GsCodeGen::FnPrototype fn_prototype(llvmCodeGen, "Jitted_buildHashTable", voidType);
fn_prototype.addArgument(GsCodeGen::NamedVariable("cellHead", hashCellPtrType));
fn_prototype.addArgument(GsCodeGen::NamedVariable("hashTbl", vechashtablePtrType));
jitted_buildHashTable = fn_prototype.generatePrototype(&builder, &llvmargs[0]);
/* start the main codegen process for buildHashTable in hash join */
cellHead = llvmargs[0];
hashTbl = llvmargs[1];
VecHashJoin* plannode = (VecHashJoin*)node->js.ps.plan;
Plan* innerPlan = innerPlan(plannode);
/* add 1 column to flag match or not */
int numCols = 0;
if (plannode->join.jointype == JOIN_RIGHT || plannode->join.jointype == JOIN_RIGHT_ANTI_FULL ||
plannode->join.jointype == JOIN_RIGHT_ANTI || plannode->join.jointype == JOIN_RIGHT_SEMI)
numCols = list_length(innerPlan->targetlist) + 1;
else
numCols = list_length(innerPlan->targetlist);
/*
* Since m_complicateJoinKey is false, m_cellSize equals to
* sizeof(hashCell) + (m_cols - 1) * sizeof(hashVal);
*/
int cellSize_value = offsetof(hashCell, m_val) + numCols * sizeof(hashVal);
llvm::Value* int64_cellsize = llvmCodeGen->getIntConstant(INT8OID, cellSize_value);
/* m_key equals to list_length(node->hj_InnerHashKeys) */
int num_hashkey = list_length(node->hj_InnerHashKeys);
/* define basic block information for the function */
llvm::BasicBlock** bb_checkFlag = (llvm::BasicBlock**)palloc(sizeof(llvm::BasicBlock*) * num_hashkey);
llvm::BasicBlock** bb_hashing = (llvm::BasicBlock**)palloc(sizeof(llvm::BasicBlock*) * num_hashkey);
for (j = 0; j < num_hashkey; j++) {
bb_checkFlag[j] = llvm::BasicBlock::Create(context, "bb_checkFlag", jitted_buildHashTable);
bb_hashing[j] = llvm::BasicBlock::Create(context, "bb_hashing", jitted_buildHashTable);
}
DEFINE_BLOCK(bb_pre_loop, jitted_buildHashTable);
DEFINE_BLOCK(bb_loopEnd, jitted_buildHashTable);
DEFINE_BLOCK(bb_ret, jitted_buildHashTable);
/* entry basic block */
/* get the number of rows of this batch : nrows = cellHead->flag.m_rows; */
tmpval = builder.CreateBitCast(cellHead, int32PtrType);
llvm::Value* nRows = builder.CreateAlignedLoad(tmpval, 4, "nrows");
tmpval = builder.CreateICmpSGT(nRows, int32_0);
builder.CreateCondBr(tmpval, bb_pre_loop, bb_ret);
/*
* bb_pre_loopEntry:
* Get all m_keyIdx constants.
*/
builder.SetInsertPoint(bb_pre_loop);
/* m_hashTbl is constructed by hashSize, mask = hashSize - 1 */
Vals2[1] = int32_0;
mask = builder.CreateInBoundsGEP(hashTbl, Vals2);
mask = builder.CreateAlignedLoad(mask, 4, "m_size");
mask = builder.CreateSub(mask, int32_1);
ListCell* lc_inner = NULL;
int keyIdx_var = 0;
Var* variable = NULL;
ExprState* exprState = NULL;
Var** inner_var = (Var**)palloc(sizeof(Var*) * num_hashkey);
llvm::Value** inner_keyIdx_array = (llvm::Value**)palloc(sizeof(llvm::Value*) * num_hashkey);
/* loop over all the right hashkeys to preload all the variables */
j = 0;
foreach (lc_inner, node->hj_InnerHashKeys) {
/* get inner keyIdx */
variable = NULL;
exprState = (ExprState*)lfirst(lc_inner);
if (IsA(exprState->expr, Var)) {
variable = (Var*)exprState->expr;
} else if (IsA(exprState->expr, RelabelType)) {
RelabelType* reltype = (RelabelType*)exprState->expr;
if (IsA(reltype->arg, Var) && ((Var*)reltype->arg)->varattno > 0)
variable = (Var*)((RelabelType*)exprState->expr)->arg;
}
Assert(variable != NULL);
keyIdx_var = variable->varattno - 1;
inner_var[j] = variable;
DEFINE_CGVAR_INT64(int64_keyIdx, (long long)keyIdx_var);
/* m_keyIdx[j] = variable->varattno - 1 */
inner_keyIdx_array[j] = int64_keyIdx;
j++;
}
builder.CreateBr(bb_checkFlag[0]);
/* Beginning of the loop */
builder.SetInsertPoint(bb_checkFlag[0]);
llvm::PHINode* Phi_cell = builder.CreatePHI(hashCellPtrType, 2);
llvm::PHINode* Phi_Idx = builder.CreatePHI(int32Type, 2);
Phi_cell->addIncoming(cellHead, bb_pre_loop);
Phi_Idx->addIncoming(int32_0, bb_pre_loop);
/* defined for multiple hash keys */
llvm::PHINode* Phi_hash_result = NULL;
llvm::Value* prev_hash_value = NULL;
for (j = 0; j < num_hashkey; j++) {
/* bb_checkFlag */
builder.SetInsertPoint(bb_checkFlag[j]);
if (j > 0) {
Phi_hash_result = builder.CreatePHI(int32Type, 2);
Phi_hash_result->addIncoming(prev_hash_value, bb_checkFlag[j - 1]);
Phi_hash_result->addIncoming(hash_result, bb_hashing[j - 1]);
prev_hash_value = Phi_hash_result;
} else
prev_hash_value = int32_0;
/* get the flag of hash key : cell->m_val[keyIdx].flag */
Vals4[1] = int32_1;
Vals4[2] = inner_keyIdx_array[j];
Vals4[3] = int32_1;
flag = builder.CreateInBoundsGEP(Phi_cell, Vals4);
flag = builder.CreateAlignedLoad(flag, 1, "flag");
flag = builder.CreateAnd(flag, int8_1);
tmpval = builder.CreateICmpEQ(flag, int8_0);
if (j == num_hashkey - 1)
builder.CreateCondBr(tmpval, bb_hashing[j], bb_loopEnd);
else
builder.CreateCondBr(tmpval, bb_hashing[j], bb_checkFlag[j + 1]);
/* when val is not null */
builder.SetInsertPoint(bb_hashing[j]);
/* cell->m_val[keyIdx].val */
Vals4[3] = int32_0;
val = builder.CreateInBoundsGEP(Phi_cell, Vals4);
val = builder.CreateAlignedLoad(val, 8, "val");
/* get the variable type according to the preloaded variable */
variable = inner_var[j];
switch (variable->vartype) {
case INT4OID: {
val = builder.CreateTrunc(val, int32Type);
llvm_crc32_32_32(hash_result, prev_hash_value, val);
} break;
case INT8OID: {
llvm_crc32_32_64(hash_result, prev_hash_value, val);
} break;
case BPCHAROID: {
int len = variable->vartypmod - VARHDRSZ;
llvm::Function* func_evalvar = llvmCodeGen->module()->getFunction("JittedEvalVarlena");
if (func_evalvar == NULL) {
func_evalvar = VarlenaCvtCodeGen();
}
llvm::Value* res = builder.CreateCall(func_evalvar, val, "func_evalvar");
llvm::Value* data1 = builder.CreateExtractValue(res, 1);
llvm::Value* data1_int64 = builder.CreatePtrToInt(data1, int64Type);
llvm::Value* pval = NULL;
llvm::Value* hash_res1 = prev_hash_value;
if (len >= 8) {
while (len >= 8) {
pval = builder.CreateIntToPtr(data1_int64, int64PtrType);
pval = builder.CreateAlignedLoad(pval, 8, "int64data");
llvm_crc32_32_64(hash_res1, hash_res1, pval);
data1_int64 = builder.CreateAdd(data1_int64, int64_8);
len = len - 8;
}
}
if (len >= 4) {
pval = builder.CreateIntToPtr(data1_int64, int32PtrType);
pval = builder.CreateAlignedLoad(pval, 4, "int32data");
llvm_crc32_32_32(hash_res1, hash_res1, pval);
data1_int64 = builder.CreateAdd(data1_int64, int64_4);
len = len - 4;
}
if (len >= 2) {
pval = builder.CreateIntToPtr(data1_int64, int16PtrType);
pval = builder.CreateAlignedLoad(pval, 2, "int16data");
llvm_crc32_32_16(hash_res1, hash_res1, pval);
data1_int64 = builder.CreateAdd(data1_int64, int64_2);
len = len - 2;
}
if (len == 1) {
pval = builder.CreateIntToPtr(data1_int64, int8PtrType);
pval = builder.CreateAlignedLoad(pval, 1, "int8data");
llvm_crc32_32_8(hash_res1, hash_res1, pval);
}
hash_result = hash_res1;
} break;
default: {
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmodule(MOD_LLVM),
errmsg("Codegen buildHashTable failed: unsupported data type!\n")));
}
}
if (j == num_hashkey - 1)
builder.CreateBr(bb_loopEnd);
else
builder.CreateBr(bb_checkFlag[j + 1]);
}
builder.SetInsertPoint(bb_loopEnd);
llvm::PHINode* Phi_result = builder.CreatePHI(int32Type, 2);
Phi_result->addIncoming(hash_result, bb_hashing[num_hashkey - 1]);
Phi_result->addIncoming(prev_hash_value, bb_checkFlag[num_hashkey - 1]);
/* location = hashVal & mask */
tmpval = builder.CreateAnd(Phi_result, mask);
llvm::Value* bucketIdx = builder.CreateZExt(tmpval, int64Type);
/* Get m_data from vechashtable */
Vals2[1] = int32_1;
llvm::Value* m_data = builder.CreateInBoundsGEP(hashTbl, Vals2);
m_data = builder.CreateAlignedLoad(m_data, 8, "m_data");
/* lastCell = m_hashTbl->m_data[location] */
llvm::Value* m_data_idx = builder.CreateInBoundsGEP(m_data, bucketIdx);
llvm::Value* lastCell = builder.CreateAlignedLoad(m_data_idx, 8, "lastCell");
/* m_hashTbl->m_data[location] = cell */
builder.CreateAlignedStore(Phi_cell, m_data_idx, 8);
/* get cell->flag.m_next */
Vals3[1] = int32_0;
Vals3[2] = int32_0;
llvm::Value* next_addr = builder.CreateInBoundsGEP(Phi_cell, Vals3);
/* cell->flag.m_next = lastCell */
builder.CreateAlignedStore(lastCell, next_addr, 8);
tmpval = builder.CreateBitCast(Phi_cell, int8PtrType);
tmpval = builder.CreateInBoundsGEP(tmpval, int64_cellsize);
llvm::Value* nextCell = builder.CreateBitCast(tmpval, hashCellPtrType);
llvm::Value* nextIdx = builder.CreateAdd(Phi_Idx, int32_1);
Phi_cell->addIncoming(nextCell, bb_loopEnd);
Phi_Idx->addIncoming(nextIdx, bb_loopEnd);
tmpval = builder.CreateICmpEQ(nextIdx, nRows);
builder.CreateCondBr(tmpval, bb_ret, bb_checkFlag[0]);
builder.SetInsertPoint(bb_ret);
builder.CreateRetVoid();
pfree_ext(inner_keyIdx_array);
pfree_ext(inner_var);
pfree_ext(bb_checkFlag);
pfree_ext(bb_hashing);
/* finalize the jitted function */
llvmCodeGen->FinalizeFunction(jitted_buildHashTable, node->js.ps.plan->plan_node_id);
return jitted_buildHashTable;
}
/* @Description : Codegen buildHashTable for hash join when NeedCopy is
* required. Replace the hash function (hashint4 + hash_uint32)
* by CRC32 hardware instruction.
* @Notes : Only Support the case with condition:
* (1) int4, int8, or bpchar hash keys
* (2) NeedCopy is true
* (3) m_complicateJoinKey is false
*/
llvm::Function* VecHashJoinCodeGen::HashJoinCodeGen_buildHashTable_NeedCopy(VecHashJoinState* node)
{
Assert(NULL != (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj);
GsCodeGen* llvmCodeGen = (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj;
/* Find and load the IR file from the installaion directory */
llvmCodeGen->loadIRFile();
/* Get LLVM Context and builder */
llvm::LLVMContext& context = llvmCodeGen->context();
GsCodeGen::LlvmBuilder builder(context);
llvm::Module* mod = llvmCodeGen->module();
llvm::Function* jitted_buildHashTable = NULL;
llvm::Value* tmpval = NULL;
llvm::Value* llvmargs[2];
llvm::Value* cellHead = NULL;
llvm::Value* hashTbl = NULL;
int j;
llvm::Value* val = NULL;
llvm::Value* flag = NULL;
llvm::Value* mask = NULL;
llvm::Value* hash_result = NULL;
/* Define data types and some llvm consts */
DEFINE_CG_VOIDTYPE(voidType);
DEFINE_CG_TYPE(int32Type, INT4OID);
DEFINE_CG_TYPE(int64Type, INT8OID);
DEFINE_CG_PTRTYPE(int8PtrType, CHAROID);
DEFINE_CG_PTRTYPE(int16PtrType, INT2OID);
DEFINE_CG_PTRTYPE(int32PtrType, INT4OID);
DEFINE_CG_PTRTYPE(int64PtrType, INT8OID);
DEFINE_CG_PTRTYPE(hashCellPtrType, "struct.hashCell");
DEFINE_CG_PTRTYPE(vechashtablePtrType, "class.vechashtable");
DEFINE_CGVAR_INT8(int8_0, 0);
DEFINE_CGVAR_INT8(int8_1, 1);
DEFINE_CGVAR_INT32(int32_0, 0);
DEFINE_CGVAR_INT32(int32_1, 1);
DEFINE_CGVAR_INT64(int64_0, 0);
DEFINE_CGVAR_INT64(int64_2, 2);
DEFINE_CGVAR_INT64(int64_4, 4);
DEFINE_CGVAR_INT64(int64_8, 8);
/* llvm array values, used to represent the location of some element */
llvm::Value* Vals2[2] = {int64_0, int32_0};
llvm::Value* Vals3[3] = {int64_0, int32_0, int32_0};
llvm::Value* Vals4[4] = {int64_0, int32_0, int32_0, int32_0};
GsCodeGen::FnPrototype fn_prototype(llvmCodeGen, "Jitted_buildHashTable_NeedCopy", voidType);
fn_prototype.addArgument(GsCodeGen::NamedVariable("cellHead", hashCellPtrType));
fn_prototype.addArgument(GsCodeGen::NamedVariable("hashTbl", vechashtablePtrType));
jitted_buildHashTable = fn_prototype.generatePrototype(&builder, &llvmargs[0]);
/* start the main codegen process for buildHashTable in hash join */
cellHead = llvmargs[0];
hashTbl = llvmargs[1];
VecHashJoin* plannode = (VecHashJoin*)node->js.ps.plan;
Plan* innerPlan = innerPlan(plannode);
/* add 1 column to flag match or not */
int numCols = 0;
if (plannode->join.jointype == JOIN_RIGHT || plannode->join.jointype == JOIN_RIGHT_ANTI_FULL ||
plannode->join.jointype == JOIN_RIGHT_ANTI || plannode->join.jointype == JOIN_RIGHT_SEMI)
numCols = list_length(innerPlan->targetlist) + 1;
else
numCols = list_length(innerPlan->targetlist);
/*
* Since m_complicateJoinKey is false, m_cellSize equals to
* sizeof(hashCell) + (m_cols - 1) * sizeof(hashVal);
*/
int cellSize_value = offsetof(hashCell, m_val) + numCols * sizeof(hashVal);
llvm::Value* int64_cellsize = llvmCodeGen->getIntConstant(INT8OID, cellSize_value);
/* m_key equals to list_length(node->hj_InnerHashKeys) */
int num_hashkey = list_length(node->hj_InnerHashKeys);
/* define basic block information for the function */
llvm::BasicBlock** bb_checkFlag = (llvm::BasicBlock**)palloc(sizeof(llvm::BasicBlock*) * num_hashkey);
llvm::BasicBlock** bb_hashing = (llvm::BasicBlock**)palloc(sizeof(llvm::BasicBlock*) * num_hashkey);
for (j = 0; j < num_hashkey; j++) {
bb_checkFlag[j] = llvm::BasicBlock::Create(context, "bb_checkFlag", jitted_buildHashTable);
bb_hashing[j] = llvm::BasicBlock::Create(context, "bb_hashing", jitted_buildHashTable);
}
DEFINE_BLOCK(bb_pre_loop, jitted_buildHashTable);
DEFINE_BLOCK(bb_loopEnd, jitted_buildHashTable);
DEFINE_BLOCK(bb_ret, jitted_buildHashTable);
/* entry basic block */
/* get the number of rows of this batch : nrows = cellHead->flag.m_rows; */
tmpval = builder.CreateBitCast(cellHead, int32PtrType);
llvm::Value* nRows = builder.CreateAlignedLoad(tmpval, 4, "nrows");
tmpval = builder.CreateICmpSGT(nRows, int32_0);
builder.CreateCondBr(tmpval, bb_pre_loop, bb_ret);
/*
* bb_pre_loopEntry:
* Get all m_keyIdx constants.
*/
builder.SetInsertPoint(bb_pre_loop);
/* since needcopy is true, do allocate for copyCellArray
* copyCellArray = (hashCell*)palloc0(nrows * m_cellSize);
*/
llvm::Function* func_palloc = llvmCodeGen->module()->getFunction("Wrappalloc");
if (NULL == func_palloc) {
GsCodeGen::FnPrototype func_prototype(llvmCodeGen, "Wrappalloc", int8PtrType);
func_prototype.addArgument(GsCodeGen::NamedVariable("size", int32Type));
func_palloc = func_prototype.generatePrototype(NULL, NULL);
llvm::sys::DynamicLibrary::AddSymbol("Wrappalloc", (void*)Wrappalloc);
}
llvm::Value* int32_cellsize = llvmCodeGen->getIntConstant(INT4OID, cellSize_value);
llvm::Value* alloc_size = builder.CreateMul(nRows, int32_cellsize);
llvm::Value* copyCellArray = builder.CreateCall(func_palloc, alloc_size);
copyCellArray = builder.CreateBitCast(copyCellArray, hashCellPtrType);
/* m_hashTbl is constructed by hashSize, mask = hashSize - 1 */
Vals2[1] = int32_0;
mask = builder.CreateInBoundsGEP(hashTbl, Vals2);
mask = builder.CreateAlignedLoad(mask, 4, "m_size");
mask = builder.CreateSub(mask, int32_1);
ListCell* lc_inner = NULL;
int keyIdx_var = 0;
Var* variable = NULL;
ExprState* exprState = NULL;
Var** inner_var = (Var**)palloc(sizeof(Var*) * num_hashkey);
llvm::Value** inner_keyIdx_array = (llvm::Value**)palloc(sizeof(llvm::Value*) * num_hashkey);
/* loop over all the right hashkeys to preload all the variables */
j = 0;
foreach (lc_inner, node->hj_InnerHashKeys) {
/* get inner keyIdx */
variable = NULL;
exprState = (ExprState*)lfirst(lc_inner);
if (IsA(exprState->expr, Var)) {
variable = (Var*)exprState->expr;
} else if (IsA(exprState->expr, RelabelType)) {
RelabelType* reltype = (RelabelType*)exprState->expr;
if (IsA(reltype->arg, Var) && ((Var*)reltype->arg)->varattno > 0)
variable = (Var*)((RelabelType*)exprState->expr)->arg;
}
Assert(variable != NULL);
keyIdx_var = variable->varattno - 1;
inner_var[j] = variable;
DEFINE_CGVAR_INT64(int64_keyIdx, (long long)keyIdx_var);
/* m_keyIdx[j] = variable->varattno - 1 */
inner_keyIdx_array[j] = int64_keyIdx;
j++;
}
builder.CreateBr(bb_checkFlag[0]);
/* Beginning of the loop */
builder.SetInsertPoint(bb_checkFlag[0]);
llvm::PHINode* Phi_cell = builder.CreatePHI(hashCellPtrType, 2);
llvm::PHINode* Phi_copyCell = builder.CreatePHI(hashCellPtrType, 2);
llvm::PHINode* Phi_Idx = builder.CreatePHI(int32Type, 2);
Phi_cell->addIncoming(cellHead, bb_pre_loop);
Phi_copyCell->addIncoming(copyCellArray, bb_pre_loop);
Phi_Idx->addIncoming(int32_0, bb_pre_loop);
/* defined for multiple hash keys */
llvm::PHINode* Phi_hash_result = NULL;
llvm::Value* prev_hash_value = NULL;
for (j = 0; j < num_hashkey; j++) {
/* bb_checkFlag */
builder.SetInsertPoint(bb_checkFlag[j]);
if (j > 0) {
Phi_hash_result = builder.CreatePHI(int32Type, 2);
Phi_hash_result->addIncoming(prev_hash_value, bb_checkFlag[j - 1]);
Phi_hash_result->addIncoming(hash_result, bb_hashing[j - 1]);
prev_hash_value = Phi_hash_result;
} else
prev_hash_value = int32_0;
/* get the flag of hash key : cell->m_val[keyIdx].flag */
Vals4[1] = int32_1;
Vals4[2] = inner_keyIdx_array[j];
Vals4[3] = int32_1;
flag = builder.CreateInBoundsGEP(Phi_cell, Vals4);
flag = builder.CreateAlignedLoad(flag, 1, "flag");
flag = builder.CreateAnd(flag, int8_1);
tmpval = builder.CreateICmpEQ(flag, int8_0);
if (j == num_hashkey - 1)
builder.CreateCondBr(tmpval, bb_hashing[j], bb_loopEnd);
else
builder.CreateCondBr(tmpval, bb_hashing[j], bb_checkFlag[j + 1]);
/* when val is not null, do hashing */
builder.SetInsertPoint(bb_hashing[j]);
Vals4[3] = int32_0;
val = builder.CreateInBoundsGEP(Phi_cell, Vals4);
val = builder.CreateAlignedLoad(val, 8, "val");
variable = inner_var[j];
switch (variable->vartype) {
case INT4OID: {
val = builder.CreateTrunc(val, int32Type);
llvm_crc32_32_32(hash_result, prev_hash_value, val);
} break;
case INT8OID: {
llvm_crc32_32_64(hash_result, prev_hash_value, val);
} break;
case BPCHAROID: {
int len = variable->vartypmod - VARHDRSZ;
llvm::Function* func_evalvar = llvmCodeGen->module()->getFunction("JittedEvalVarlena");
if (func_evalvar == NULL) {
func_evalvar = VarlenaCvtCodeGen();
}
llvm::Value* res = builder.CreateCall(func_evalvar, val, "func_evalvar");
llvm::Value* data1 = builder.CreateExtractValue(res, 1);
llvm::Value* data1_int64 = builder.CreatePtrToInt(data1, int64Type);
llvm::Value* pval = NULL;
llvm::Value* hash_res1 = prev_hash_value;
if (len >= 8) {
while (len >= 8) {
pval = builder.CreateIntToPtr(data1_int64, int64PtrType);
pval = builder.CreateAlignedLoad(pval, 8, "int64data");
llvm_crc32_32_64(hash_res1, hash_res1, pval);
data1_int64 = builder.CreateAdd(data1_int64, int64_8);
len = len - 8;
}
}
if (len >= 4) {
pval = builder.CreateIntToPtr(data1_int64, int32PtrType);
pval = builder.CreateAlignedLoad(pval, 4, "int32data");
llvm_crc32_32_32(hash_res1, hash_res1, pval);
data1_int64 = builder.CreateAdd(data1_int64, int64_4);
len = len - 4;
}
if (len >= 2) {
pval = builder.CreateIntToPtr(data1_int64, int16PtrType);
pval = builder.CreateAlignedLoad(pval, 2, "int16data");
llvm_crc32_32_16(hash_res1, hash_res1, pval);
data1_int64 = builder.CreateAdd(data1_int64, int64_2);
len = len - 2;
}
if (len == 1) {
pval = builder.CreateIntToPtr(data1_int64, int8PtrType);
pval = builder.CreateAlignedLoad(pval, 1, "int8data");
llvm_crc32_32_8(hash_res1, hash_res1, pval);
}
hash_result = hash_res1;
} break;
default: {
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmodule(MOD_LLVM),
errmsg("Codegen buildHashTable failed: unsupported data type!\n")));
}
}
if (j == num_hashkey - 1)
builder.CreateBr(bb_loopEnd);
else
builder.CreateBr(bb_checkFlag[j + 1]);
}
builder.SetInsertPoint(bb_loopEnd);
llvm::PHINode* Phi_result = builder.CreatePHI(int32Type, 2);
Phi_result->addIncoming(hash_result, bb_hashing[num_hashkey - 1]);
Phi_result->addIncoming(prev_hash_value, bb_checkFlag[num_hashkey - 1]);
/* location = hashVal & mask */
tmpval = builder.CreateAnd(Phi_result, mask);
llvm::Value* bucketIdx = builder.CreateZExt(tmpval, int64Type);
/* Get m_data from vechashtable */
Vals2[1] = int32_1;
llvm::Value* m_data = builder.CreateInBoundsGEP(hashTbl, Vals2);
m_data = builder.CreateAlignedLoad(m_data, 8, "m_data");
/* lastCell = m_hashTbl->m_data[location] */
llvm::Value* m_data_idx = builder.CreateInBoundsGEP(m_data, bucketIdx);
llvm::Value* lastCell = builder.CreateAlignedLoad(m_data_idx, 8, "lastCell");
/* needcopy is true, need the following functionality :
* Phi_copyCell = GET_NTH_CELL(copyCellArray, i);
* memcpy_s(Phi_copyCell, m_cellSize, Phi_cell, m_cellSize);
*/
llvm::Value* target = builder.CreateBitCast(Phi_copyCell, int8PtrType);
llvm::Value* source = builder.CreateBitCast(Phi_cell, int8PtrType);
builder.CreateMemCpy(target, 1, source, 1, int32_cellsize);
/* m_hashTbl->m_data[location] = copyCell */
builder.CreateAlignedStore(Phi_copyCell, m_data_idx, 8);
/* get copyCell->flag.m_next */
Vals3[1] = int32_0;
Vals3[2] = int32_0;
llvm::Value* next_addr = builder.CreateInBoundsGEP(Phi_copyCell, Vals3);
/* copyCell->flag.m_next = lastCell */
builder.CreateAlignedStore(lastCell, next_addr, 8);
/* Get next cell and next copyCell : GET_NTH_CELL(hashcell, i) */
tmpval = builder.CreateBitCast(Phi_cell, int8PtrType);
tmpval = builder.CreateInBoundsGEP(tmpval, int64_cellsize);
llvm::Value* nextCell = builder.CreateBitCast(tmpval, hashCellPtrType);
llvm::Value* nextIdx = builder.CreateAdd(Phi_Idx, int32_1);
Phi_cell->addIncoming(nextCell, bb_loopEnd);
tmpval = builder.CreateBitCast(Phi_copyCell, int8PtrType);
tmpval = builder.CreateInBoundsGEP(tmpval, int64_cellsize);
llvm::Value* nextCopyCell = builder.CreateBitCast(tmpval, hashCellPtrType);
Phi_copyCell->addIncoming(nextCopyCell, bb_loopEnd);
/* next idx */
Phi_Idx->addIncoming(nextIdx, bb_loopEnd);
tmpval = builder.CreateICmpEQ(nextIdx, nRows);
builder.CreateCondBr(tmpval, bb_ret, bb_checkFlag[0]);
builder.SetInsertPoint(bb_ret);
builder.CreateRetVoid();
pfree_ext(inner_keyIdx_array);
pfree_ext(inner_var);
pfree_ext(bb_checkFlag);
pfree_ext(bb_hashing);
/* finalize the jitted function */
llvmCodeGen->FinalizeFunction(jitted_buildHashTable, node->js.ps.plan->plan_node_id);
return jitted_buildHashTable;
}
/*
* @Description : Codegen probeHashTable for hash join. Replace the hash
* function(hashint4 + hash_uint32) by CRC32 hardware instruction.
* @Notes : Only Support the case with condition:
* (1) int4, int8, or bpchar hash keys
* (2) m_complicateJoinKey is false
*/
llvm::Function* VecHashJoinCodeGen::HashJoinCodeGen_probeHashTable(VecHashJoinState* node)
{
Assert(NULL != (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj);
GsCodeGen* llvmCodeGen = (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj;
/* Find and load the IR file from the installaion directory */
llvmCodeGen->loadIRFile();
/* Get LLVM Context and builder */
llvm::LLVMContext& context = llvmCodeGen->context();
GsCodeGen::LlvmBuilder builder(context);
llvm::Module* mod = llvmCodeGen->module();
llvm::Function* jitted_probeHashTable = NULL;
llvm::Value* tmpval = NULL;
llvm::Value* llvmargs[2];
llvm::Value* hashJoinTbl = NULL;
llvm::Value* batch = NULL;
int j;
llvm::Value* val = NULL;
llvm::Value* flag = NULL;
llvm::Value* mask = NULL;
llvm::Value* hash_result = NULL;
/* Define data types and some llvm consts */
DEFINE_CG_VOIDTYPE(voidType);
DEFINE_CG_TYPE(int32Type, INT4OID);
DEFINE_CG_TYPE(int64Type, INT8OID);
DEFINE_CG_PTRTYPE(int8PtrType, CHAROID);
DEFINE_CG_PTRTYPE(int16PtrType, INT2OID);
DEFINE_CG_PTRTYPE(int32PtrType, INT4OID);
DEFINE_CG_PTRTYPE(int64PtrType, INT8OID);
DEFINE_CG_PTRTYPE(VectorBatchPtrType, "class.VectorBatch");
DEFINE_CG_PTRTYPE(HashJoinTblPtrType, "class.HashJoinTbl");
DEFINE_CG_PTRTYPE(hashBasedOperatorPtrType, "class.hashBasedOperator.base");
DEFINE_CGVAR_INT8(int8_0, 0);
DEFINE_CGVAR_INT8(int8_1, 1);
DEFINE_CGVAR_INT32(int32_0, 0);
DEFINE_CGVAR_INT32(int32_1, 1);
DEFINE_CGVAR_INT32(int32_pos_hashtbl_data, pos_hashtbl_data);
DEFINE_CGVAR_INT32(int32_pos_hashtbl_size, pos_hashtbl_size);
DEFINE_CGVAR_INT32(int32_pos_hBOper_htbl, pos_hBOper_htbl);
DEFINE_CGVAR_INT32(int32_pos_scalvec_flag, pos_scalvec_flag);
DEFINE_CGVAR_INT32(int32_pos_batch_marr, pos_batch_marr);
DEFINE_CGVAR_INT32(int32_pos_scalvec_vals, pos_scalvec_vals);
DEFINE_CGVAR_INT32(int32_pos_hBOper_keyMatch, pos_hBOper_keyMatch);
DEFINE_CGVAR_INT32(int32_pos_hjointbl_match, pos_hjointbl_match);
DEFINE_CGVAR_INT32(int32_pos_hBOper_cellCache, pos_hBOper_cellCache);
DEFINE_CGVAR_INT32(int32_pos_hBOper_cacheLoc, pos_hBOper_cacheLoc);
DEFINE_CGVAR_INT64(int64_0, 0);
DEFINE_CGVAR_INT64(int64_2, 2);
DEFINE_CGVAR_INT64(int64_4, 4);
DEFINE_CGVAR_INT64(int64_8, 8);
/* llvm array values, used to represent the location of some element */
llvm::Value* Vals2[2] = {int64_0, int32_0};
llvm::Value* Vals3[3] = {int64_0, int32_0, int32_0};
GsCodeGen::FnPrototype fn_prototype(llvmCodeGen, "Jitted_probeHashTable", voidType);
fn_prototype.addArgument(GsCodeGen::NamedVariable("hashJoinTbl", HashJoinTblPtrType));
fn_prototype.addArgument(GsCodeGen::NamedVariable("batch", VectorBatchPtrType));
jitted_probeHashTable = fn_prototype.generatePrototype(&builder, &llvmargs[0]);
/* start the main codegen process for probeHashTable in hash join */
hashJoinTbl = llvmargs[0];
batch = llvmargs[1];
/* m_key equals to list_length(node->hj_InnerHashKeys) */
int num_hashkey = list_length(node->hj_InnerHashKeys);
/* define basic block information for the function */
llvm::BasicBlock** bb_checkFlag = (llvm::BasicBlock**)palloc(sizeof(llvm::BasicBlock*) * num_hashkey);
llvm::BasicBlock** bb_hashing = (llvm::BasicBlock**)palloc(sizeof(llvm::BasicBlock*) * num_hashkey);
for (j = 0; j < num_hashkey; j++) {
bb_checkFlag[j] = llvm::BasicBlock::Create(context, "bb_checkFlag", jitted_probeHashTable);
bb_hashing[j] = llvm::BasicBlock::Create(context, "bb_hashing", jitted_probeHashTable);
}
DEFINE_BLOCK(bb_pre_loop, jitted_probeHashTable);
DEFINE_BLOCK(bb_loopEnd, jitted_probeHashTable);
DEFINE_BLOCK(bb_ret, jitted_probeHashTable);
/* entry basic block */
/* get the number of rows of this batch : nrows = batch->m_rows */
tmpval = builder.CreateInBoundsGEP(batch, Vals2);
llvm::Value* nRows = builder.CreateAlignedLoad(tmpval, 4, "nrows");
tmpval = builder.CreateICmpSGT(nRows, int32_0);
builder.CreateCondBr(tmpval, bb_pre_loop, bb_ret);
/* enter into bb_pre_loopEntry to get all the needed info. */
builder.SetInsertPoint(bb_pre_loop);
/* get hashBasedOperator from HashJoinTbl */
Vals2[1] = int32_0;
llvm::Value* hashBasedOperator = builder.CreateInBoundsGEP(hashJoinTbl, Vals2);
hashBasedOperator = builder.CreateBitCast(hashBasedOperator, hashBasedOperatorPtrType);
/* get hashBasedOperator.m_hashTbl */
Vals2[1] = int32_pos_hBOper_htbl;
llvm::Value* hashTbl = builder.CreateInBoundsGEP(hashBasedOperator, Vals2);
hashTbl = builder.CreateAlignedLoad(hashTbl, 8, "m_hashTbl");
/* get m_hashTbl->m_size */
Vals2[1] = int32_pos_hashtbl_size;
mask = builder.CreateInBoundsGEP(hashTbl, Vals2);
mask = builder.CreateAlignedLoad(mask, 4, "m_size");
/* mask = m_hashTbl->m_size -1 */
mask = builder.CreateSub(mask, int32_1);
/* get m_arr from the batch */
Vals2[1] = int32_pos_batch_marr;
llvm::Value* m_arr = builder.CreateInBoundsGEP(batch, Vals2);
m_arr = builder.CreateAlignedLoad(m_arr, 8, "m_arr");
/* get HashJoinTbl.m_match */
Vals2[1] = int32_pos_hjointbl_match;
llvm::Value* m_match_addr = builder.CreateInBoundsGEP(hashJoinTbl, Vals2, "m_match");
llvm::Value* int64_nRows = builder.CreateZExt(nRows, int64Type);
/* set m_match[i] = false */
builder.CreateMemSet(m_match_addr, int8_0, int64_nRows, 1);
/* get hashBasedOperator.m_keyMatch */
Vals2[0] = int64_0;
Vals2[1] = int32_pos_hBOper_keyMatch;
llvm::Value* m_keyMatch_addr = builder.CreateInBoundsGEP(hashBasedOperator, Vals2, "m_keyMatch");
/* set m_keyMatch[i] = true */
builder.CreateMemSet(m_keyMatch_addr, int8_1, int64_nRows, 1);
ListCell* lc_outer = NULL;
int keyIdx_var = 0;
Var* variable = NULL;
ExprState* exprState = NULL;
Var** outer_var = (Var**)palloc(sizeof(Var*) * num_hashkey);
llvm::Value** outer_keyIdx_array = (llvm::Value**)palloc(sizeof(llvm::Value*) * num_hashkey);
/* loop over all the left hashkeys to preload all the variables */
j = 0;
foreach (lc_outer, node->hj_OuterHashKeys) {
/* get outer keyIdx */
variable = NULL;
exprState = (ExprState*)lfirst(lc_outer);
if (IsA(exprState->expr, Var)) {
variable = (Var*)exprState->expr;
} else if (IsA(exprState->expr, RelabelType)) {
RelabelType* reltype = (RelabelType*)exprState->expr;
if (IsA(reltype->arg, Var) && ((Var*)reltype->arg)->varattno > 0)
variable = (Var*)((RelabelType*)exprState->expr)->arg;
}
Assert(variable != NULL);
keyIdx_var = variable->varattno - 1;
outer_var[j] = variable;
DEFINE_CGVAR_INT64(int64_keyIdx, (long long)keyIdx_var);
/* m_outkeyIdx[j] = variable->varattno - 1 */
outer_keyIdx_array[j] = int64_keyIdx;
j++;
}
builder.CreateBr(bb_checkFlag[0]);
/* Beginning of the loop */
builder.SetInsertPoint(bb_checkFlag[0]);
llvm::PHINode* Phi_Idx = builder.CreatePHI(int32Type, 2);
Phi_Idx->addIncoming(int32_0, bb_pre_loop);
/* defined for multiple hash keys */
llvm::PHINode* Phi_hash_result = NULL;
llvm::Value* prev_hash_value = NULL;
for (j = 0; j < num_hashkey; j++) {
/* bb_checkFlag */
builder.SetInsertPoint(bb_checkFlag[j]);
if (j > 0) {
Phi_hash_result = builder.CreatePHI(int32Type, 2);
Phi_hash_result->addIncoming(prev_hash_value, bb_checkFlag[j - 1]);
Phi_hash_result->addIncoming(hash_result, bb_hashing[j - 1]);
prev_hash_value = Phi_hash_result;
} else
prev_hash_value = int32_0;
/* get the flag of hash key from the batch : pVector[m_outkeyIdx].flag */
Vals2[0] = outer_keyIdx_array[j];
Vals2[1] = int32_pos_scalvec_flag;
flag = builder.CreateInBoundsGEP(m_arr, Vals2);
flag = builder.CreateAlignedLoad(flag, 8, "flag");
flag = builder.CreateInBoundsGEP(flag, Phi_Idx);
flag = builder.CreateAlignedLoad(flag, 1, "flag_i");
flag = builder.CreateAnd(flag, int8_1);
tmpval = builder.CreateICmpEQ(flag, int8_0);
if (j == num_hashkey - 1)
builder.CreateCondBr(tmpval, bb_hashing[j], bb_loopEnd);
else
builder.CreateCondBr(tmpval, bb_hashing[j], bb_checkFlag[j + 1]);
/* value is not null, do hashing */
builder.SetInsertPoint(bb_hashing[j]);
/* get the value of hash key from the batch : pVector[m_outkeyIdx].val */
Vals2[1] = int32_pos_scalvec_vals;
val = builder.CreateInBoundsGEP(m_arr, Vals2);
val = builder.CreateAlignedLoad(val, 8, "val");
val = builder.CreateInBoundsGEP(val, Phi_Idx);
val = builder.CreateAlignedLoad(val, 8, "val_i");
/* do hashing according to the hashkey type */
variable = outer_var[j];
switch (variable->vartype) {
case INT4OID: {
val = builder.CreateTrunc(val, int32Type);
llvm_crc32_32_32(hash_result, prev_hash_value, val);
} break;
case INT8OID: {
llvm_crc32_32_64(hash_result, prev_hash_value, val);
} break;
case BPCHAROID: {
int len = variable->vartypmod - 4;
llvm::Function* func_evalvar = llvmCodeGen->module()->getFunction("JittedEvalVarlena");
if (func_evalvar == NULL) {
func_evalvar = VarlenaCvtCodeGen();
}
llvm::Value* res = builder.CreateCall(func_evalvar, val, "func_evalvar");
llvm::Value* data1 = builder.CreateExtractValue(res, 1);
llvm::Value* data1_int64 = builder.CreatePtrToInt(data1, int64Type);
llvm::Value* pval = NULL;
llvm::Value* hash_res1 = prev_hash_value;
if (len >= 8) {
while (len >= 8) {
pval = builder.CreateIntToPtr(data1_int64, int64PtrType);
pval = builder.CreateAlignedLoad(pval, 8, "int64data");
llvm_crc32_32_64(hash_res1, hash_res1, pval);
data1_int64 = builder.CreateAdd(data1_int64, int64_8);
len = len - 8;
}
}
if (len >= 4) {
pval = builder.CreateIntToPtr(data1_int64, int32PtrType);
pval = builder.CreateAlignedLoad(pval, 4, "int32data");
llvm_crc32_32_32(hash_res1, hash_res1, pval);
data1_int64 = builder.CreateAdd(data1_int64, int64_4);
len = len - 4;
}
if (len >= 2) {
pval = builder.CreateIntToPtr(data1_int64, int16PtrType);
pval = builder.CreateAlignedLoad(pval, 2, "int16data");
llvm_crc32_32_16(hash_res1, hash_res1, pval);
data1_int64 = builder.CreateAdd(data1_int64, int64_2);
len = len - 2;
}
if (len == 1) {
pval = builder.CreateIntToPtr(data1_int64, int8PtrType);
pval = builder.CreateAlignedLoad(pval, 1, "int8data");
llvm_crc32_32_8(hash_res1, hash_res1, pval);
}
hash_result = hash_res1;
} break;
default: {
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmodule(MOD_LLVM),
errmsg("Codegen probeHashTable failed: unsupported data type!\n")));
}
}
if (j == num_hashkey - 1)
builder.CreateBr(bb_loopEnd);
else
builder.CreateBr(bb_checkFlag[j + 1]);
}
builder.SetInsertPoint(bb_loopEnd);
llvm::PHINode* Phi_result = builder.CreatePHI(int32Type, 2);
Phi_result->addIncoming(hash_result, bb_hashing[num_hashkey - 1]);
Phi_result->addIncoming(prev_hash_value, bb_checkFlag[num_hashkey - 1]);
/* compute bucketIdx = hashVal & mask */
tmpval = builder.CreateAnd(Phi_result, mask);
llvm::Value* bucketIdx = builder.CreateZExt(tmpval, int64Type);
/* get hashBasedOperator.m_cacheLoc[i] */
Vals3[0] = int64_0;
Vals3[1] = int32_pos_hBOper_cacheLoc;
Vals3[2] = Phi_Idx;
llvm::Value* m_cacheLoc_idx = builder.CreateInBoundsGEP(hashBasedOperator, Vals3);
/* m_cacheLoc[i] = bucketIdx = hashVal & mask */
builder.CreateAlignedStore(bucketIdx, m_cacheLoc_idx, 8);
/* get vechashtable.m_data */
Vals2[0] = int64_0;
Vals2[1] = int32_pos_hashtbl_data;
llvm::Value* m_data = builder.CreateInBoundsGEP(hashTbl, Vals2);
m_data = builder.CreateAlignedLoad(m_data, 8, "m_data");
/* m_hashTbl->m_data[m_cacheLoc[i]] */
llvm::Value* m_data_idx = builder.CreateInBoundsGEP(m_data, bucketIdx);
llvm::Value* lastCell = builder.CreateAlignedLoad(m_data_idx, 8, "lastCell");
/* get hashBasedOperator.m_cellCache[i] */
Vals3[0] = int64_0;
Vals3[1] = int32_pos_hBOper_cellCache;
Vals3[2] = Phi_Idx;
llvm::Value* m_cellCache_idx = builder.CreateInBoundsGEP(hashBasedOperator, Vals3);
/* m_cellCache[i] = m_hashTbl->m_data[m_cacheLoc[i]] */
builder.CreateAlignedStore(lastCell, m_cellCache_idx, 8);
/* increase idx */
llvm::Value* nextIdx = builder.CreateAdd(Phi_Idx, int32_1);
Phi_Idx->addIncoming(nextIdx, bb_loopEnd);
tmpval = builder.CreateICmpEQ(nextIdx, nRows);
builder.CreateCondBr(tmpval, bb_ret, bb_checkFlag[0]);
builder.SetInsertPoint(bb_ret);
builder.CreateRetVoid();
pfree_ext(outer_keyIdx_array);
pfree_ext(outer_var);
pfree_ext(bb_checkFlag);
pfree_ext(bb_hashing);
/* finalize the jitted function */
llvmCodeGen->FinalizeFunction(jitted_probeHashTable, node->js.ps.plan->plan_node_id);
return jitted_probeHashTable;
}
llvm::Function* VecHashJoinCodeGen::HashJoinCodeGen_bf_includeLong(VecHashJoinState* node)
{
Assert(NULL != (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj);
GsCodeGen* llvmCodeGen = (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj;
/* Find and load the IR file from the installaion directory */
llvmCodeGen->loadIRFile();
/* Get LLVM Context and builder */
llvm::LLVMContext& context = llvmCodeGen->context();
GsCodeGen::LlvmBuilder builder(context);
llvm::Module* mod = llvmCodeGen->module();
DEFINE_CG_TYPE(int1Type, BITOID);
DEFINE_CG_TYPE(int8Type, CHAROID);
DEFINE_CG_TYPE(int32Type, INT4OID);
DEFINE_CG_TYPE(int64Type, INT8OID);
DEFINE_CG_PTRTYPE(bFImplPtrType, "class.filter::BloomFilterImpl");
DEFINE_CGVAR_INT8(int8_0, 0);
DEFINE_CGVAR_INT8(int8_1, 1);
DEFINE_CGVAR_INT32(int32_0, 0);
DEFINE_CGVAR_INT32(int32_1, 1);
DEFINE_CGVAR_INT32(int32_2, 2);
DEFINE_CGVAR_INT32(int32_3, 3);
DEFINE_CGVAR_INT32(int32_4, 4);
DEFINE_CGVAR_INT32(int32_8, 8);
DEFINE_CGVAR_INT32(int32_10, 10);
DEFINE_CGVAR_INT64(int64_0, 0);
DEFINE_CGVAR_INT64(int64_1, 1);
llvm::Function* jitted_bf_inlLong = NULL;
llvm::Value* llvmargs[2];
llvm::Value* tmpval = NULL;
llvm::Value* idx_next = NULL;
llvm::Value* pos = int32_0;
/* llvm array values, used to represent the location of some element */
llvm::Value* Vals[2] = {int64_0, int32_0};
GsCodeGen::FnPrototype fn_prototype(llvmCodeGen, "JittedBloomFilterIncLong", int8Type);
fn_prototype.addArgument(GsCodeGen::NamedVariable("BFImpl", bFImplPtrType));
fn_prototype.addArgument(GsCodeGen::NamedVariable("value", int64Type));
jitted_bf_inlLong = fn_prototype.generatePrototype(&builder, &llvmargs[0]);
llvm::Value* bfimpl = llvmargs[0];
llvm::Value* inputval = llvmargs[1];
/* define basic block information for batch loop */
llvm::BasicBlock* entry = &jitted_bf_inlLong->getEntryBlock();
DEFINE_BLOCK(nohash_bb, jitted_bf_inlLong);
DEFINE_BLOCK(evalhash_bb, jitted_bf_inlLong);
DEFINE_BLOCK(for_inc, jitted_bf_inlLong);
DEFINE_BLOCK(end_bb, jitted_bf_inlLong);
builder.SetInsertPoint(entry);
/* Get all the parameters that we needed */
Vals[1] = int32_2;
llvm::Value* numBits = builder.CreateInBoundsGEP(bfimpl, Vals);
numBits = builder.CreateAlignedLoad(numBits, 8, "numBits");
Vals[1] = int32_3;
llvm::Value* numHashFunction = builder.CreateInBoundsGEP(bfimpl, Vals);
numHashFunction = builder.CreateAlignedLoad(numHashFunction, 8, "numHashFunc");
Vals[1] = int32_4;
llvm::Value* numValues = builder.CreateInBoundsGEP(bfimpl, Vals);
numValues = builder.CreateAlignedLoad(numValues, 8, "numValues");
Vals[1] = int32_8;
llvm::Value* minValue = builder.CreateInBoundsGEP(bfimpl, Vals);
minValue = builder.CreateAlignedLoad(minValue, 8, "minValue");
Vals[1] = int32_10;
llvm::Value* addMinMax = builder.CreateInBoundsGEP(bfimpl, Vals);
addMinMax = builder.CreateAlignedLoad(addMinMax, 1, "addMinMax");
/* Compute addMinMax && (1 == numValues) */
addMinMax = builder.CreateTrunc(addMinMax, int1Type);
tmpval = builder.CreateICmpEQ(numValues, int64_1);
tmpval = builder.CreateAnd(tmpval, addMinMax);
builder.CreateCondBr(tmpval, nohash_bb, evalhash_bb);
/* Corresponding to ret = (value == minValue) */
builder.SetInsertPoint(nohash_bb);
llvm::Value* res1 = builder.CreateICmpEQ(inputval, minValue);
res1 = builder.CreateZExt(res1, int8Type);
builder.CreateBr(end_bb);
/*
* Begin to compute includeHashInternal(getLongHash(value)),
* defferent from the original function, we use CRC32 in LLVM
* to replace the original Thomas Wang's integer hash function.
*/
builder.SetInsertPoint(evalhash_bb);
llvm::PHINode* phi_idx = builder.CreatePHI(int32Type, 2);
idx_next = builder.CreateAdd(phi_idx, int32_1);
phi_idx->addIncoming(int32_1, entry);
phi_idx->addIncoming(idx_next, for_inc);
/*
* Compute hashval and combinedHash = hv1 + hv2, then get the pos:
* pos = combinedHash % numBits;
*/
llvm::Value* hashval1 = NULL;
llvm_crc32_32_64(hashval1, phi_idx, inputval);
llvm::Value* hashval2 = NULL;
llvm_crc32_32_64(hashval2, hashval1, inputval);
llvm::Value* combinedHash = builder.CreateAdd(hashval1, hashval2);
combinedHash = builder.CreateSExt(combinedHash, int64Type);
pos = builder.CreateURem(combinedHash, numBits, "pos");
/* corresponding to bitSet->get(pos) */
Vals[1] = int32_1;
llvm::Value* bitset = builder.CreateInBoundsGEP(bfimpl, Vals);
bitset = builder.CreateAlignedLoad(bitset, 8, "bitset");
llvm::Value* data = builder.CreateInBoundsGEP(bitset, Vals);
data = builder.CreateAlignedLoad(data, 8, "data");
llvm::Value* index1 = builder.CreateAShr(pos, 6);
data = builder.CreateInBoundsGEP(data, index1);
data = builder.CreateAlignedLoad(data, 8, "fdata");
llvm::Value* index2 = builder.CreateShl(int64_1, pos);
tmpval = builder.CreateAnd(data, index2);
tmpval = builder.CreateICmpNE(tmpval, int64_0);
builder.CreateCondBr(tmpval, for_inc, end_bb);
builder.SetInsertPoint(for_inc);
numHashFunction = builder.CreateTrunc(numHashFunction, int32Type);
tmpval = builder.CreateICmpSGT(idx_next, numHashFunction);
builder.CreateCondBr(tmpval, end_bb, evalhash_bb);
builder.SetInsertPoint(end_bb);
llvm::PHINode* phi_ret = builder.CreatePHI(int8Type, 3);
phi_ret->addIncoming(res1, nohash_bb);
phi_ret->addIncoming(int8_0, evalhash_bb);
phi_ret->addIncoming(int8_1, for_inc);
builder.CreateRet(phi_ret);
llvmCodeGen->FinalizeFunction(jitted_bf_inlLong, node->js.ps.plan->plan_node_id);
return jitted_bf_inlLong;
}
llvm::Function* VecHashJoinCodeGen::HashJoinCodeGen_bf_addLong(VecHashJoinState* node)
{
Assert(NULL != (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj);
GsCodeGen* llvmCodeGen = (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj;
/* Find and load the IR file from the installaion directory */
llvmCodeGen->loadIRFile();
/* Get LLVM Context and builder */
llvm::LLVMContext& context = llvmCodeGen->context();
GsCodeGen::LlvmBuilder builder(context);
llvm::Module* mod = llvmCodeGen->module();
DEFINE_CG_VOIDTYPE(voidType);
DEFINE_CG_TYPE(int16Type, INT2OID);
DEFINE_CG_TYPE(int32Type, INT4OID);
DEFINE_CG_TYPE(int64Type, INT8OID);
DEFINE_CG_PTRTYPE(bFImplPtrType, "class.filter::BloomFilterImpl");
DEFINE_CGVAR_INT32(int32_0, 0);
DEFINE_CGVAR_INT32(int32_1, 1);
DEFINE_CGVAR_INT32(int32_2, 2);
DEFINE_CGVAR_INT32(int32_3, 3);
DEFINE_CGVAR_INT32(int32_4, 4);
DEFINE_CGVAR_INT32(int32_5, 5);
DEFINE_CGVAR_INT32(int32_6, 6);
DEFINE_CGVAR_INT32(int32_7, 7);
DEFINE_CGVAR_INT64(int64_0, 0);
DEFINE_CGVAR_INT64(int64_1, 1);
llvm::Function* jitted_bf_addLong = NULL;
llvm::Value* llvmargs[2];
llvm::Value* tmpval = NULL;
llvm::Value* idx_next = NULL;
llvm::Value* pos = int32_0;
/* llvm array values, used to represent the location of some element */
llvm::Value* Vals[2] = {int64_0, int32_0};
llvm::Value* Vals3[3] = {int64_0, int32_0, int32_0};
GsCodeGen::FnPrototype fn_prototype(llvmCodeGen, "JittedBloomFilterAddLong", voidType);
fn_prototype.addArgument(GsCodeGen::NamedVariable("BFImpl", bFImplPtrType));
fn_prototype.addArgument(GsCodeGen::NamedVariable("value", int64Type));
jitted_bf_addLong = fn_prototype.generatePrototype(&builder, &llvmargs[0]);
llvm::Value* bfimpl = llvmargs[0];
llvm::Value* inputval = llvmargs[1];
/* define basic block information for batch loop */
llvm::BasicBlock* entry = &jitted_bf_addLong->getEntryBlock();
DEFINE_BLOCK(do_error, jitted_bf_addLong);
DEFINE_BLOCK(chk_realloc, jitted_bf_addLong);
DEFINE_BLOCK(do_realloc, jitted_bf_addLong);
DEFINE_BLOCK(for_body, jitted_bf_addLong);
DEFINE_BLOCK(for_inc, jitted_bf_addLong);
DEFINE_BLOCK(for_end, jitted_bf_addLong);
builder.SetInsertPoint(entry);
/* Get all the parameters that we needed */
Vals[1] = int32_2;
llvm::Value* numBits = builder.CreateInBoundsGEP(bfimpl, Vals);
numBits = builder.CreateAlignedLoad(numBits, 8, "numBits");
Vals[1] = int32_3;
llvm::Value* num_HashFunction = builder.CreateInBoundsGEP(bfimpl, Vals);
num_HashFunction = builder.CreateAlignedLoad(num_HashFunction, 8, "numHashFunc");
num_HashFunction = builder.CreateTrunc(num_HashFunction, int32Type);
Vals[1] = int32_4;
llvm::Value* ptrnumValues = builder.CreateInBoundsGEP(bfimpl, Vals);
llvm::Value* numValues = builder.CreateAlignedLoad(ptrnumValues, 8, "numValues");
Vals[1] = int32_5;
llvm::Value* val_numMaxValues = builder.CreateInBoundsGEP(bfimpl, Vals);
val_numMaxValues = builder.CreateAlignedLoad(val_numMaxValues, 8, "maxNumValues");
Vals[1] = int32_6;
llvm::Value* ptrvalstartup = builder.CreateInBoundsGEP(bfimpl, Vals);
llvm::Value* val_startup = builder.CreateAlignedLoad(ptrvalstartup, 8, "startupEntries");
Vals[1] = int32_7;
llvm::Value* valpos = builder.CreateInBoundsGEP(bfimpl, Vals);
valpos = builder.CreateAlignedLoad(valpos, 8, "valuePositions");
/* Report error when numValues >= maxNumValues */
tmpval = builder.CreateICmpSGE(numValues, val_numMaxValues);
builder.CreateCondBr(tmpval, do_error, chk_realloc);
builder.SetInsertPoint(do_error);
const char* errorstr = "Add too many values to the bloom filter.";
DebugerCodeGen::CodeGenElogInfo(&builder, ERROR, errorstr);
builder.CreateBr(chk_realloc);
/* Check if numValues >= startupEntries, repalloc valuePositions when true */
builder.SetInsertPoint(chk_realloc);
tmpval = builder.CreateICmpSGE(numValues, val_startup);
builder.CreateCondBr(tmpval, do_realloc, for_body);
builder.SetInsertPoint(do_realloc);
llvm::Function* func_pos_repalloc = llvmCodeGen->module()->getFunction("WrapvalPosRepalloc");
if (NULL == func_pos_repalloc) {
GsCodeGen::FnPrototype func_prototype(llvmCodeGen, "WrapvalPosRepalloc", voidType);
func_prototype.addArgument(GsCodeGen::NamedVariable("bfimpl", bFImplPtrType));
func_pos_repalloc = func_prototype.generatePrototype(NULL, NULL);
llvm::sys::DynamicLibrary::AddSymbol("WrapvalPosRepalloc", (void*)WrapposRepalloc);
}
builder.CreateCall(func_pos_repalloc, bfimpl);
builder.CreateBr(for_body);
/*
* Defferent from the original function, we use CRC32 in LLVM
* to replace the original Thomas Wang's integer hash function.
*/
builder.SetInsertPoint(for_body);
llvm::PHINode* phi_idx = builder.CreatePHI(int32Type, 3);
idx_next = builder.CreateAdd(phi_idx, int32_1);
phi_idx->addIncoming(int32_1, chk_realloc);
phi_idx->addIncoming(int32_1, do_realloc);
phi_idx->addIncoming(idx_next, for_inc);
/*
* Compute hashval and combinedHash = hv1 + hv2, then get the pos:
* pos = combinedHash % numBits;
*/
llvm::Value* hashval1 = NULL;
llvm_crc32_32_64(hashval1, phi_idx, inputval);
llvm::Value* hashval2 = NULL;
llvm_crc32_32_64(hashval2, hashval1, inputval);
llvm::Value* combinedHash = builder.CreateAdd(hashval1, hashval2);
combinedHash = builder.CreateSExt(combinedHash, int64Type);
pos = builder.CreateURem(combinedHash, numBits, "pos");
/* corresponding to bitSet->set(pos) */
Vals[1] = int32_1;
llvm::Value* bitset = builder.CreateInBoundsGEP(bfimpl, Vals);
bitset = builder.CreateAlignedLoad(bitset, 8, "bitset");
llvm::Value* data = builder.CreateInBoundsGEP(bitset, Vals);
data = builder.CreateAlignedLoad(data, 8, "data");
llvm::Value* index1 = builder.CreateAShr(pos, 6);
llvm::Value* pdata = builder.CreateInBoundsGEP(data, index1);
llvm::Value* realdata = builder.CreateAlignedLoad(pdata, 8, "pdata");
llvm::Value* index2 = builder.CreateShl(int64_1, pos);
realdata = builder.CreateOr(realdata, index2);
builder.CreateAlignedStore(realdata, pdata, 8);
/* corresponding to valuePositions[numValues].position[i - 1] = pos */
Vals3[0] = numValues;
Vals3[1] = int32_0;
Vals3[2] = builder.CreateSub(phi_idx, int32_1);
llvm::Value* position = builder.CreateInBoundsGEP(valpos, Vals3);
llvm::Value* idx = builder.CreateTrunc(pos, int16Type);
builder.CreateAlignedStore(idx, position, 2);
builder.CreateBr(for_inc);
builder.SetInsertPoint(for_inc);
tmpval = builder.CreateICmpSGT(idx_next, num_HashFunction);
builder.CreateCondBr(tmpval, for_end, for_body);
builder.SetInsertPoint(for_end);
numValues = builder.CreateAdd(numValues, int64_1);
builder.CreateAlignedStore(numValues, ptrnumValues, 8);
builder.CreateRetVoid();
llvmCodeGen->FinalizeFunction(jitted_bf_addLong, node->js.ps.plan->plan_node_id);
return jitted_bf_addLong;
}
} // namespace dorado
/*
* @Description : We could load notnull information from base relation, while if the
* table is not located at the subplan of inner join, we could not
* promise it.
*
* @in node : Input node state.
* @return : The output variable cound be null when ture.
*/
bool NULL_possible(PlanState* node)
{
bool result = false;
if (IsA(node, VecHashJoinState) || IsA(node, VecNestLoopState) || IsA(node, VecMergeJoinState)) {
if (((VecHashJoinState*)node)->js.jointype != JOIN_INNER)
return true;
}
PlanState* inner_node = innerPlanState(node);
PlanState* outer_node = outerPlanState(node);
if (inner_node != NULL)
result = NULL_possible(inner_node);
if (result == false && outer_node != NULL)
result = NULL_possible(outer_node);
return result;
}
/* @Description : Wraper function for palloc, when we do codegen on buildHashTable, if
* NeedCopy is true, we need to palloc copyCellArray.
* @in size : The size of memory that we need.
* @return : The pointer point to the alloced context.
*/
char* Wrappalloc(int32 size)
{
return (char*)palloc0(size);
}
/*
* @Description : Realloc valuepos when numValues >= startupEntries.
* @in bfi : The BloomFilter info struct that we used in HashJoin
*/
void WrapposRepalloc(filter::BloomFilterImpl<int64>* bfi)
{
AutoContextSwitch memGuard(bfi->context);
bfi->startupEntries = Min(bfi->startupEntries * 2, bfi->maxNumValues);
bfi->valuePositions = (ValueBit*)repalloc(bfi->valuePositions, bfi->startupEntries * sizeof(ValueBit));
}