4959 lines
230 KiB
C++
Executable File
4959 lines
230 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.
|
|
* ---------------------------------------------------------------------------------------
|
|
*
|
|
* vechashaggcodegen.cpp
|
|
* Routines to handle vector hashagg nodes. We only focuse on
|
|
* the CPU intensive part of hashagg operation.
|
|
* IDENTIFICATION
|
|
* src/gausskernel/runtime/codegen/vecexecutor/vechashaggcodegen.cpp
|
|
*
|
|
* ---------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "codegen/gscodegen.h"
|
|
#include "codegen/vechashaggcodegen.h"
|
|
#include "codegen/vecsortcodegen.h"
|
|
#include "codegen/codegendebuger.h"
|
|
#include "codegen/builtinscodegen.h"
|
|
#include "codegen/vecexprcodegen.h"
|
|
|
|
#include "catalog/pg_operator.h"
|
|
#include "pgxc/pgxc.h"
|
|
#include "vecexecutor/vecexecutor.h"
|
|
|
|
void WrapAllocHashSlot(HashAggRunner* haRunner, VectorBatch* batch, int idx, int keysimple);
|
|
void WrapSglTblAllocHashSlot(HashAggRunner* haRunner, VectorBatch* batch, int idx, int keysimple);
|
|
void WrapResetExprContext(ExprContext* econtext);
|
|
|
|
/* macro values used for prefetch in batchagg */
|
|
#define PREFETCH_AGGHASHING_DISTANCE1 4
|
|
#define PREFETCH_AGGHASHING_DISTANCE2 2
|
|
#define PREFETCH_BATCHAGGREGATION_DISTANCE 2
|
|
#define AGG_ECONOMY_RATION 0.001
|
|
|
|
namespace dorado {
|
|
llvm::Function* prefetchAggHashingCodeGen();
|
|
llvm::Function* prefetchBatchAggregationCodeGen();
|
|
llvm::Function* prefetchAggSglTblHashingCodeGen();
|
|
|
|
int VecHashAggCodeGen::GetAlignedScale(Expr* node)
|
|
{
|
|
int resscale = 0;
|
|
|
|
/*
|
|
* Get into this function only when fast_aggref is true, which means
|
|
* we could apply fast codegen path for this expression node.
|
|
*/
|
|
switch (nodeTag(node)) {
|
|
case T_TargetEntry: {
|
|
TargetEntry* tentry = (TargetEntry*)node;
|
|
resscale = GetAlignedScale(tentry->expr);
|
|
} break;
|
|
case T_OpExpr: {
|
|
OpExpr* opexpr = (OpExpr*)node;
|
|
Expr* lexpr = (Expr*)linitial(opexpr->args);
|
|
Expr* rexpr = (Expr*)lsecond(opexpr->args);
|
|
|
|
int lscale = GetAlignedScale(lexpr);
|
|
int rscale = GetAlignedScale(rexpr);
|
|
|
|
/*
|
|
* Only when AggRefFastJittable is satisfied, we evaluate the scale
|
|
* of the aggref expression. So, no need to consider NUMERICDIVOID.
|
|
*/
|
|
if (opexpr->opno == NUMERICADDOID || opexpr->opno == NUMERICSUBOID) {
|
|
if (lscale < rscale)
|
|
resscale = rscale;
|
|
else
|
|
resscale = lscale;
|
|
} else if (opexpr->opno == NUMERICMULOID)
|
|
resscale = lscale + rscale;
|
|
else
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Unsupported operation %u in FastAgg.", opexpr->opno)));
|
|
} break;
|
|
case T_Var: {
|
|
Var* var = (Var*)node;
|
|
Assert(var->vartype == NUMERICOID);
|
|
|
|
resscale = (var->vartypmod - VARHDRSZ) & NUMERIC_BI_SCALEMASK;
|
|
} break;
|
|
case T_Const: {
|
|
Const* cst = (Const*)node;
|
|
Assert(cst->consttype == NUMERICOID);
|
|
|
|
/* Extract the real value of this const */
|
|
ScalarValue val = ScalarVector::DatumToScalar(cst->constvalue, cst->consttype, cst->constisnull);
|
|
resscale = NUMERIC_BI_SCALE((Numeric)val);
|
|
} break;
|
|
default:
|
|
Assert(0);
|
|
break;
|
|
}
|
|
|
|
return resscale;
|
|
}
|
|
|
|
bool VecHashAggCodeGen::AggRefJittable(ExprState* state)
|
|
{
|
|
Expr* node = state->expr;
|
|
|
|
switch (nodeTag(node)) {
|
|
/*
|
|
* there always be a targetentry in AggrefExprState, so extract
|
|
* targetentry first.
|
|
*/
|
|
case T_TargetEntry: {
|
|
GenericExprState* gstate = (GenericExprState*)state;
|
|
ExprState* tstate = gstate->arg;
|
|
if (!AggRefJittable(tstate))
|
|
return false;
|
|
} break;
|
|
case T_Var: {
|
|
Var* var = (Var*)state->expr;
|
|
/* If var is in sysattrlist, we do not codegen the var expr. */
|
|
if (var->varattno < 0)
|
|
return false;
|
|
|
|
/* only support int8 and numeric type */
|
|
switch (var->vartype) {
|
|
case INT8OID:
|
|
case NUMERICOID:
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
if (var->vartype == NUMERICOID) {
|
|
/*
|
|
* do not consider numeric data type without precision
|
|
* specfied, since it changed during evaluation.
|
|
*/
|
|
if (var->vartypmod == -1)
|
|
return false;
|
|
}
|
|
|
|
state->exprCodeGen = (exprFakeCodeGenSig)&VecExprCodeGen::VarCodeGen;
|
|
} break;
|
|
case T_Const: {
|
|
Const* con = (Const*)state->expr;
|
|
switch (con->consttype) {
|
|
/* only support int8 and numeric type */
|
|
case INT8OID:
|
|
case NUMERICOID:
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
state->exprCodeGen = (exprFakeCodeGenSig)&VecExprCodeGen::ConstCodeGen;
|
|
} break;
|
|
case T_OpExpr: {
|
|
/* check if have special opexpr with one arg */
|
|
OpExpr* opexpr = (OpExpr*)state->expr;
|
|
FuncExprState* fstate = (FuncExprState*)state;
|
|
if (list_length(opexpr->args) == 1)
|
|
return false;
|
|
|
|
switch (opexpr->opno) {
|
|
/*
|
|
* only support limited math operations
|
|
*/
|
|
case INT8PLOID:
|
|
case INT8MIOID:
|
|
case INT8MULOID:
|
|
case INT8DIVOID:
|
|
case NUMERICADDOID:
|
|
case NUMERICSUBOID:
|
|
case NUMERICMULOID:
|
|
case NUMERICDIVOID:
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* now consider the args of this operation expression.
|
|
*/
|
|
ExprState* lestate = (ExprState*)linitial(fstate->args);
|
|
ExprState* restate = (ExprState*)lsecond(fstate->args);
|
|
|
|
if (!AggRefJittable(lestate) || !AggRefJittable(restate))
|
|
return false;
|
|
|
|
state->exprCodeGen = (exprFakeCodeGenSig)&VecExprCodeGen::OpCodeGen;
|
|
} break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool VecHashAggCodeGen::AggRefFastJittable(ExprState* state)
|
|
{
|
|
Expr* node = state->expr;
|
|
|
|
switch (nodeTag(node)) {
|
|
case T_TargetEntry: {
|
|
GenericExprState* gstate = (GenericExprState*)state;
|
|
ExprState* tstate = (ExprState*)(gstate->arg);
|
|
|
|
if (!AggRefFastJittable(tstate))
|
|
return false;
|
|
} break;
|
|
case T_Var: {
|
|
Var* var = (Var*)node;
|
|
if (var->vartype != NUMERICOID || var->vartypmod == -1)
|
|
return false;
|
|
|
|
/* get the precision of this attribute column */
|
|
int prec = (var->vartypmod >> 16) & 0xFFFF;
|
|
if (prec > 18)
|
|
return false;
|
|
} break;
|
|
case T_Const: {
|
|
Const* cst = (Const*)(state->expr);
|
|
/* only consider not null numeric type const value */
|
|
if (cst->consttype != NUMERICOID || cst->constisnull)
|
|
return false;
|
|
|
|
ScalarValue val = ScalarVector::DatumToScalar(cst->constvalue, cst->consttype, cst->constisnull);
|
|
|
|
if (!cst->constisnull) {
|
|
if ((((NumericData*)val)->choice.n_header & NUMERIC_BI_MASK) != NUMERIC_64)
|
|
return false;
|
|
}
|
|
} break;
|
|
case T_OpExpr: {
|
|
/* check if have special opexpr with one arg */
|
|
OpExpr* opexpr = (OpExpr*)state->expr;
|
|
FuncExprState* fstate = (FuncExprState*)state;
|
|
if (list_length(opexpr->args) == 1)
|
|
return false;
|
|
|
|
/* only support +, -, * operation in numeric expression */
|
|
switch (opexpr->opno) {
|
|
case NUMERICADDOID:
|
|
case NUMERICSUBOID:
|
|
case NUMERICMULOID:
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
ExprState* lestate = (ExprState*)linitial(fstate->args);
|
|
ExprState* restate = (ExprState*)lsecond(fstate->args);
|
|
|
|
if (!AggRefFastJittable(lestate) || !AggRefFastJittable(restate))
|
|
return false;
|
|
} break;
|
|
default:
|
|
Assert(0);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool VecHashAggCodeGen::AgghashingJittable(VecAggState* node)
|
|
{
|
|
VecAgg* vecagg = (VecAgg*)(node->ss.ps.plan);
|
|
|
|
if (!u_sess->attr.attr_sql.enable_codegen || IS_PGXC_COORDINATOR)
|
|
return false;
|
|
|
|
/* Only support hash aggeregation */
|
|
if (vecagg->aggstrategy != AGG_HASHED)
|
|
return false;
|
|
|
|
/*
|
|
* Get the input variable from the outer plan, and we only
|
|
* support char, varchar, text, int4, int8 and timestamp(date).
|
|
*/
|
|
List* tlist = (outerPlan(vecagg))->targetlist;
|
|
int numkeys = vecagg->numCols;
|
|
AttrNumber* keyIdx = vecagg->grpColIdx;
|
|
|
|
for (int i = 0; i < numkeys; i++) {
|
|
AttrNumber key = keyIdx[i] - 1;
|
|
TargetEntry* tle = (TargetEntry*)list_nth(tlist, key);
|
|
|
|
/* only support variable expr */
|
|
switch (nodeTag(tle->expr)) {
|
|
case T_Var: {
|
|
Var* var = (Var*)(tle->expr);
|
|
switch (var->vartype) {
|
|
case BPCHAROID: {
|
|
int len = var->vartypmod - VARHDRSZ;
|
|
|
|
/* no codegeneration for unknown length */
|
|
if (len < 0)
|
|
return false;
|
|
} break;
|
|
case INT4OID:
|
|
case INT8OID:
|
|
case TIMESTAMPOID:
|
|
case DATEOID:
|
|
case TEXTOID:
|
|
case VARCHAROID:
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
} break;
|
|
case T_FuncExpr: {
|
|
FuncExpr* funcexpr = (FuncExpr*)(tle->expr);
|
|
if (funcexpr->funcid != SUBSTRFUNCOID)
|
|
return false;
|
|
|
|
/* Only support ASCII and UTF-8 encoding */
|
|
int current_encoding = GetDatabaseEncoding();
|
|
if (current_encoding != PG_SQL_ASCII && current_encoding != PG_UTF8)
|
|
return false;
|
|
|
|
List* func_args = funcexpr->args;
|
|
Expr* aexpr1 = (Expr*)linitial(func_args);
|
|
Expr* aexpr2 = (Expr*)lsecond(func_args);
|
|
Expr* aexpr3 = (Expr*)lthird(func_args);
|
|
|
|
if (!IsA(aexpr1, RelabelType) && !IsA(aexpr1, Var))
|
|
return false;
|
|
|
|
if (!IsA(aexpr2, Const) || !IsA(aexpr3, Const))
|
|
return false;
|
|
} break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
/* only codegen hashint4, hashint8, hashtext,
|
|
* timestamp_hash, and hashbpchar functions
|
|
*/
|
|
Oid fnoid = node->hashfunctions[i].fn_oid;
|
|
switch (fnoid) {
|
|
case HASHINT4OID:
|
|
case HASHINT8OID:
|
|
case HASHBPCHAROID:
|
|
case HASHTEXTOID:
|
|
break;
|
|
case TIMESTAMPHASHOID: {
|
|
#ifdef HAVE_INT64_TIMESTAMP
|
|
#else
|
|
return false;
|
|
#endif
|
|
} break;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool VecHashAggCodeGen::BatchAggJittable(VecAggState* node, bool isSonic)
|
|
{
|
|
int i = 0;
|
|
VecAgg* vecagg = (VecAgg*)(node->ss.ps.plan);
|
|
AggStatePerAgg peragg = node->peragg;
|
|
|
|
if (!u_sess->attr.attr_sql.enable_codegen || IS_PGXC_COORDINATOR)
|
|
return false;
|
|
|
|
/* only support hashagg */
|
|
if (vecagg->aggstrategy != AGG_HASHED)
|
|
return false;
|
|
|
|
/* if no agg funcs exists, no codegen is needed */
|
|
if (0 == node->numaggs)
|
|
return false;
|
|
|
|
for (i = 0; i < node->numaggs; i++) {
|
|
AggrefExprState* aggexprstate = peragg[i].aggrefstate;
|
|
Aggref* aggref = peragg[i].aggref;
|
|
|
|
/* only support sum/avg */
|
|
switch (aggref->aggfnoid) {
|
|
case INT8AVGFUNCOID:
|
|
case INT8SUMFUNCOID:
|
|
if (isSonic)
|
|
return false;
|
|
break;
|
|
case NUMERICAVGFUNCOID:
|
|
case NUMERICSUMFUNCOID:
|
|
case COUNTOID:
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
/* count(*) has no args */
|
|
if (aggref->aggfnoid == COUNTOID)
|
|
continue;
|
|
|
|
ExprState* estate = (ExprState*)linitial(aggexprstate->args);
|
|
/* We only support simple expression cases */
|
|
if (!AggRefJittable(estate))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void VecHashAggCodeGen::HashAggCodeGen(VecAggState* node)
|
|
{
|
|
/*
|
|
* Codegeneration for hashagg:
|
|
* Since the whole HashAggRunner::BuildAggTbl has been divided into three
|
|
* part, we should do codegeneration separately.
|
|
*/
|
|
Assert(NULL != (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj);
|
|
GsCodeGen* llvmCodeGen = (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj;
|
|
llvm::Function* jitted_vechashing = NULL;
|
|
llvm::Function* jitted_vecsglhashing = NULL;
|
|
llvm::Function* jitted_vecbatchagg = NULL;
|
|
llvm::Function* jitted_SortAggMatchKey = NULL;
|
|
|
|
/*
|
|
* For aggregation, if economy is too small, which means number of distinct
|
|
* values is very large, we meet cachemiss during batch aggregation, so
|
|
* consider prefetch in this case.
|
|
*/
|
|
VecAgg* vagg = (VecAgg*)node->ss.ps.plan;
|
|
Plan* outerplan = outerPlan(vagg);
|
|
bool use_prefetch = vagg->plan.plan_rows / ((Plan*)outerplan)->plan_rows > AGG_ECONOMY_RATION;
|
|
if (use_prefetch) {
|
|
jitted_vechashing = AgghashingWithPrefetchCodeGenorSglTbl<false>(node);
|
|
jitted_vecsglhashing = AgghashingWithPrefetchCodeGenorSglTbl<true>(node);
|
|
} else {
|
|
jitted_vechashing = AgghashingCodeGenorSglTbl<false>(node);
|
|
jitted_vecsglhashing = AgghashingCodeGenorSglTbl<true>(node);
|
|
}
|
|
|
|
if (NULL != jitted_vechashing)
|
|
llvmCodeGen->addFunctionToMCJit(jitted_vechashing, reinterpret_cast<void**>(&(node->jitted_hashing)));
|
|
|
|
if (NULL != jitted_vecsglhashing)
|
|
llvmCodeGen->addFunctionToMCJit(jitted_vecsglhashing, reinterpret_cast<void**>(&(node->jitted_sglhashing)));
|
|
|
|
/* Codegeneration for BatchAggregation in buildAggTbl */
|
|
jitted_vecbatchagg = dorado::VecHashAggCodeGen::BatchAggregationCodeGen(node, use_prefetch);
|
|
if (NULL != jitted_vecbatchagg)
|
|
llvmCodeGen->addFunctionToMCJit(jitted_vecbatchagg, reinterpret_cast<void**>(&(node->jitted_batchagg)));
|
|
|
|
/* Codegeneration for sortagg */
|
|
jitted_SortAggMatchKey = dorado::VecSortCodeGen::SortAggMatchKeyCodeGen(node);
|
|
node->jitted_SortAggMatchKey = NULL;
|
|
if (NULL != jitted_SortAggMatchKey)
|
|
llvmCodeGen->addFunctionToMCJit(
|
|
jitted_SortAggMatchKey, reinterpret_cast<void**>(&(node->jitted_SortAggMatchKey)));
|
|
|
|
/* Codegenration for targetlist of aggregation */
|
|
llvm::Function* jitted_vectarget = NULL;
|
|
jitted_vectarget = dorado::VecExprCodeGen::TargetListCodeGen(node->ss.ps.targetlist, (PlanState*)node);
|
|
if (NULL != jitted_vectarget)
|
|
llvmCodeGen->addFunctionToMCJit(
|
|
jitted_vectarget, reinterpret_cast<void**>(&(node->ss.ps.ps_ProjInfo->jitted_vectarget)));
|
|
}
|
|
|
|
void VecHashAggCodeGen::SonicHashAggCodeGen(VecAggState* node)
|
|
{
|
|
Assert(NULL != (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj);
|
|
GsCodeGen* llvmCodeGen = (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj;
|
|
llvm::Function* jitted_sonicbatchagg = NULL;
|
|
|
|
jitted_sonicbatchagg = SonicBatchAggregationCodeGen(node, false);
|
|
|
|
if (NULL != jitted_sonicbatchagg)
|
|
llvmCodeGen->addFunctionToMCJit(jitted_sonicbatchagg, reinterpret_cast<void**>(&(node->jitted_sonicbatchagg)));
|
|
}
|
|
template <bool isSglTbl>
|
|
llvm::Function* VecHashAggCodeGen::AgghashingCodeGenorSglTbl(VecAggState* node)
|
|
{
|
|
Assert(NULL != (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj);
|
|
GsCodeGen* llvmCodeGen = (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj;
|
|
|
|
/* If the condition can not be satisfied, no need to codegen */
|
|
if (!AgghashingJittable(node))
|
|
return NULL;
|
|
|
|
/* Find and load the IR file from the installaion directory */
|
|
llvmCodeGen->loadIRFile();
|
|
|
|
/* Extract plan information from node */
|
|
int i = 0;
|
|
VecAgg* vecagg = (VecAgg*)(node->ss.ps.plan);
|
|
int numkeys = vecagg->numCols;
|
|
AttrNumber* keyIdx = vecagg->grpColIdx;
|
|
|
|
/* Get LLVM Context and builder */
|
|
llvm::LLVMContext& context = llvmCodeGen->context();
|
|
GsCodeGen::LlvmBuilder builder(context);
|
|
|
|
llvm::Value* hAggRunner = NULL;
|
|
llvm::Value* batch = NULL;
|
|
llvm::Value* tmpval = NULL;
|
|
llvm::Value* cmpval = NULL;
|
|
llvm::Value* idx_next = NULL;
|
|
llvm::Value* llvmargs[2];
|
|
llvm::PHINode* phi_idx = NULL;
|
|
llvm::Function* jitted_agghashing = 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(hashCellPtrType, "struct.hashCell");
|
|
DEFINE_CG_PTRTYPE(vectorBatchPtrType, "class.VectorBatch");
|
|
DEFINE_CG_PTRTYPE(hashAggRunnerPtrType, "class.HashAggRunner");
|
|
|
|
DEFINE_CGVAR_INT32(int32_m1, -1);
|
|
DEFINE_CGVAR_INT32(int32_0, 0);
|
|
DEFINE_CGVAR_INT32(int32_1, 1);
|
|
DEFINE_CGVAR_INT32(int32_pos_hBOper_cols, pos_hBOper_cols);
|
|
DEFINE_CGVAR_INT32(int32_pos_hBOper_cacheLoc, pos_hBOper_cacheLoc);
|
|
DEFINE_CGVAR_INT32(int32_pos_hAggR_hashVal, pos_hAggR_hashVal);
|
|
DEFINE_CGVAR_INT32(int32_pos_hAggR_hSegTbl, pos_hAggR_hSegTbl);
|
|
DEFINE_CGVAR_INT32(int32_pos_hAggR_hsegmax, pos_hAggR_hsegmax); /* for AgghashingCodeGen */
|
|
DEFINE_CGVAR_INT32(int32_pos_hAggR_hashSize, pos_hAggR_hashSize);
|
|
DEFINE_CGVAR_INT32(int32_pos_bAggR_keyIdxInCell, pos_bAggR_keyIdxInCell);
|
|
DEFINE_CGVAR_INT32(int32_pos_bAggR_Loc, pos_bAggR_Loc);
|
|
DEFINE_CGVAR_INT32(int32_pos_bAggR_keySimple, pos_bAggR_keySimple);
|
|
DEFINE_CGVAR_INT32(int32_pos_hcell_mval, pos_hcell_mval);
|
|
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_scalvec_flag, pos_scalvec_flag);
|
|
DEFINE_CGVAR_INT64(Datum_0, 0);
|
|
DEFINE_CGVAR_INT64(Datum_1, 1);
|
|
|
|
/* llvm array values, used to represent the location of some element */
|
|
llvm::Value* Vals[2] = {Datum_0, int32_0};
|
|
llvm::Value* Vals3[3] = {Datum_0, int32_0, int32_0};
|
|
llvm::Value* Vals4[4] = {Datum_0, int32_0, int32_0, int32_0};
|
|
llvm::Value* Vals5[5] = {Datum_0, int32_0, int32_0, int32_0, int32_0};
|
|
|
|
const char* name = NULL;
|
|
if (isSglTbl) {
|
|
name = "JittedSglTblAggHashing";
|
|
} else {
|
|
name = "JittedAggHashing";
|
|
}
|
|
GsCodeGen::FnPrototype fn_prototype(llvmCodeGen, name, voidType);
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("hashAggRunner", hashAggRunnerPtrType));
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("batch", vectorBatchPtrType));
|
|
jitted_agghashing = fn_prototype.generatePrototype(&builder, &llvmargs[0]);
|
|
|
|
/* start the main codegen process for hashagg */
|
|
hAggRunner = llvmargs[0];
|
|
batch = llvmargs[1];
|
|
|
|
/* get the number of rows of this batch : VectorBatch.m_rows */
|
|
tmpval = builder.CreateInBoundsGEP(batch, Vals);
|
|
llvm::Value* nValues = builder.CreateAlignedLoad(tmpval, 4, "m_rows");
|
|
|
|
/* mask = hashAggRunner.m_hashSize - 1 */
|
|
Vals[1] = int32_pos_hAggR_hashSize;
|
|
tmpval = builder.CreateInBoundsGEP(hAggRunner, Vals);
|
|
llvm::Value* maskval = builder.CreateAlignedLoad(tmpval, 8, "m_hashSize");
|
|
maskval = builder.CreateSub(maskval, Datum_1, "mask");
|
|
|
|
/* HashAggRunner.BaseAggRunner.hashBasedOperator.m_cols */
|
|
Vals4[3] = int32_pos_hBOper_cols;
|
|
llvm::Value* m_colsVal = builder.CreateInBoundsGEP(hAggRunner, Vals4);
|
|
|
|
/* define basic block information for batch loop */
|
|
llvm::BasicBlock* entry = &jitted_agghashing->getEntryBlock();
|
|
DEFINE_BLOCK(for_body, jitted_agghashing);
|
|
DEFINE_BLOCK(for_end, jitted_agghashing);
|
|
DEFINE_BLOCK(for_inc, jitted_agghashing);
|
|
|
|
/* define basic block information for key-value matching */
|
|
DEFINE_BLOCK(while_body, jitted_agghashing);
|
|
DEFINE_BLOCK(key_match, jitted_agghashing);
|
|
DEFINE_BLOCK(hashval_eq, jitted_agghashing);
|
|
DEFINE_BLOCK(next_cell, jitted_agghashing);
|
|
DEFINE_BLOCK(alloc_hashslot, jitted_agghashing);
|
|
|
|
/* define vector structures used for store batch info. in LLVM */
|
|
llvm::Value** keyIdxInCell = (llvm::Value**)palloc(sizeof(llvm::Value*) * numkeys);
|
|
llvm::Value** pVector = (llvm::Value**)palloc(sizeof(llvm::Value*) * numkeys);
|
|
llvm::Value** pFlag = (llvm::Value**)palloc(sizeof(llvm::Value*) * numkeys);
|
|
|
|
/* HashAggRunner.BaseAggRunner.m_keyIdxInCell */
|
|
Vals3[0] = Datum_0;
|
|
Vals3[1] = int32_0;
|
|
Vals3[2] = int32_pos_bAggR_keyIdxInCell;
|
|
llvm::Value* cellkeyIdx = builder.CreateInBoundsGEP(hAggRunner, Vals3);
|
|
cellkeyIdx = builder.CreateAlignedLoad(cellkeyIdx, 4, "keyIdxInCellArr");
|
|
for (i = 0; i < numkeys; i++) {
|
|
/* load keyIdx in cell */
|
|
tmpval = llvmCodeGen->getIntConstant(INT4OID, i);
|
|
keyIdxInCell[i] = builder.CreateInBoundsGEP(cellkeyIdx, tmpval);
|
|
keyIdxInCell[i] = builder.CreateAlignedLoad(keyIdxInCell[i], 4, "m_keyIdxInCell");
|
|
}
|
|
|
|
/* load m_arr from batch data */
|
|
Vals[0] = Datum_0;
|
|
Vals[1] = int32_pos_batch_marr;
|
|
tmpval = builder.CreateInBoundsGEP(batch, Vals);
|
|
llvm::Value* tmparr = builder.CreateAlignedLoad(tmpval, 8, "m_arr");
|
|
for (i = 0; i < numkeys; i++) {
|
|
/* load scalarvector from m_arr */
|
|
AttrNumber key = keyIdx[i] - 1;
|
|
Vals[0] = llvmCodeGen->getIntConstant(INT8OID, key);
|
|
Vals[1] = int32_pos_scalvec_vals;
|
|
llvm::Value* pVec = builder.CreateInBoundsGEP(tmparr, Vals);
|
|
pVector[i] = builder.CreateAlignedLoad(pVec, 8, "pVector");
|
|
|
|
/* load flag information from m_arr */
|
|
Vals[1] = int32_pos_scalvec_flag;
|
|
llvm::Value* Flag = builder.CreateInBoundsGEP(tmparr, Vals);
|
|
pFlag[i] = builder.CreateAlignedLoad(Flag, 8, "pFlag");
|
|
}
|
|
|
|
/*
|
|
* Begin to loop the whole batch to evaluation hashval and initialize
|
|
* the hash cell by matching hashval and key
|
|
*/
|
|
builder.SetInsertPoint(entry);
|
|
tmpval = builder.CreateICmpSGT(nValues, int32_0);
|
|
builder.CreateCondBr(tmpval, for_body, for_end);
|
|
|
|
builder.SetInsertPoint(for_body);
|
|
phi_idx = builder.CreatePHI(int64Type, 2);
|
|
|
|
/* add one for every loop to check the index */
|
|
idx_next = builder.CreateAdd(phi_idx, Datum_1);
|
|
phi_idx->addIncoming(Datum_0, entry);
|
|
phi_idx->addIncoming(idx_next, for_inc);
|
|
|
|
/* given the initial hash value */
|
|
llvm::Value* hash_res = int32_m1;
|
|
bool rehash = false;
|
|
|
|
/* evaluation the hash value for phi_idx-th tuple */
|
|
for (i = 0; i < numkeys; i++) {
|
|
if (i > 0)
|
|
rehash = true;
|
|
|
|
llvm::Function* func_hashbatch = HashBatchCodeGen(node, i, rehash);
|
|
if (func_hashbatch == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Failed on generating HashBatchCodeGen!\n")));
|
|
}
|
|
|
|
/* load the phi_idx-th value and flag of the current key */
|
|
llvm::Value* pval = builder.CreateInBoundsGEP(pVector[i], phi_idx);
|
|
pval = builder.CreateAlignedLoad(pval, 8, "pval");
|
|
llvm::Value* pflag = builder.CreateInBoundsGEP(pFlag[i], phi_idx);
|
|
pflag = builder.CreateAlignedLoad(pflag, 1, "pflag");
|
|
|
|
hash_res = builder.CreateCall(func_hashbatch, {pval, pflag, hash_res});
|
|
}
|
|
|
|
/* store the hash value (hash_res) to m_hashVal of hashAggRunner */
|
|
/* hashAggRunner.m_hashVal[phi_idx] */
|
|
Vals3[1] = int32_pos_hAggR_hashVal;
|
|
Vals3[2] = phi_idx;
|
|
llvm::Value* hashValSlot = builder.CreateInBoundsGEP(hAggRunner, Vals3);
|
|
llvm::Value* hash_res64 = builder.CreateZExt(hash_res, int64Type);
|
|
builder.CreateAlignedStore(hash_res64, hashValSlot, 8);
|
|
|
|
/* corresponding to m_cacheLoc[i] = m_hashVal[i] & mask; (uint64) */
|
|
llvm::Value* cacheLocVal = builder.CreateAnd(hash_res64, maskval, "cacheLoc");
|
|
/* store cacheLocVal to hashAggRunner.BaseAggRunner.hashBasedOperator.m_cacheLoc[i] */
|
|
Vals5[3] = int32_pos_hBOper_cacheLoc;
|
|
Vals5[4] = phi_idx;
|
|
llvm::Value* cacheLoc_i = builder.CreateInBoundsGEP(hAggRunner, Vals5);
|
|
builder.CreateAlignedStore(cacheLocVal, cacheLoc_i, 8);
|
|
llvm::Value* segmaxval = NULL;
|
|
llvm::Value* nsegsval = NULL;
|
|
llvm::Value* pos = NULL;
|
|
if (!isSglTbl) {
|
|
/* get m_hashseg_max from hashAggRunner to calculate nsegs and pos */
|
|
Vals[0] = Datum_0;
|
|
Vals[1] = int32_pos_hAggR_hsegmax;
|
|
/* segmaxval, nsegsval and pos are for AgghashingCodeGen */
|
|
segmaxval = builder.CreateInBoundsGEP(hAggRunner, Vals);
|
|
segmaxval = builder.CreateAlignedLoad(segmaxval, 4, "segmax");
|
|
segmaxval = builder.CreateSExt(segmaxval, int64Type);
|
|
/* nsegs = m_cacheLoc[i] / m_hashseg_max */
|
|
nsegsval = builder.CreateExactUDiv(cacheLocVal, segmaxval, "nsegs");
|
|
nsegsval = builder.CreateTrunc(nsegsval, int32Type);
|
|
/* pos = m_cacheLoc[i] % m_hashseg_max */
|
|
pos = builder.CreateSRem(cacheLocVal, segmaxval, "pos");
|
|
}
|
|
/* get m_hashData from hashAggRunner */
|
|
Vals[0] = Datum_0;
|
|
Vals[1] = int32_pos_hAggR_hSegTbl;
|
|
llvm::Value* hashData = builder.CreateInBoundsGEP(hAggRunner, Vals);
|
|
hashData = builder.CreateAlignedLoad(hashData, 8, "m_hashData");
|
|
if (isSglTbl) {
|
|
Vals[0] = int32_0;
|
|
} else {
|
|
Vals[0] = nsegsval;
|
|
}
|
|
Vals[1] = int32_1;
|
|
llvm::Value* tbldata = builder.CreateInBoundsGEP(hashData, Vals);
|
|
tbldata = builder.CreateAlignedLoad(tbldata, 8, "tbl_data");
|
|
if (isSglTbl) {
|
|
tmpval = builder.CreateInBoundsGEP(tbldata, cacheLocVal);
|
|
} else {
|
|
tmpval = builder.CreateInBoundsGEP(tbldata, pos);
|
|
}
|
|
llvm::Value* cellval = builder.CreateAlignedLoad(tmpval, 8, "cell");
|
|
|
|
/* check if cell is NULL or not */
|
|
tmpval = builder.CreatePtrToInt(cellval, int64Type);
|
|
if (!isSglTbl) {
|
|
cellval = builder.CreateIntToPtr(tmpval, hashCellPtrType);
|
|
}
|
|
cmpval = builder.CreateICmpEQ(tmpval, Datum_0);
|
|
builder.CreateCondBr(cmpval, alloc_hashslot, while_body);
|
|
|
|
/* while (cell!= NULL) { compare hash value and do match_key } */
|
|
builder.SetInsertPoint(while_body);
|
|
llvm::PHINode* phi_cell = builder.CreatePHI(hashCellPtrType, 2);
|
|
|
|
/* get next cell : cell = cell->flag.m_next: hashCell.flag.m_next */
|
|
builder.SetInsertPoint(next_cell);
|
|
Vals3[0] = Datum_0;
|
|
Vals3[1] = int32_0;
|
|
Vals3[2] = int32_0;
|
|
llvm::Value* nextcellval = builder.CreateInBoundsGEP(phi_cell, Vals3);
|
|
nextcellval = builder.CreateAlignedLoad(nextcellval, 8, "nextcellval");
|
|
tmpval = builder.CreatePtrToInt(nextcellval, int64Type);
|
|
cmpval = builder.CreateICmpEQ(tmpval, Datum_0);
|
|
builder.CreateCondBr(cmpval, alloc_hashslot, while_body);
|
|
|
|
/* loop over the whole hash cell chain */
|
|
builder.SetInsertPoint(while_body);
|
|
phi_cell->addIncoming(nextcellval, next_cell);
|
|
phi_cell->addIncoming(cellval, for_body);
|
|
|
|
/* get hash value from current cell : cell->m_val[m_cols].val */
|
|
tmpval = builder.CreateAlignedLoad(m_colsVal, 4, "m_cols");
|
|
tmpval = builder.CreateZExt(tmpval, int64Type);
|
|
Vals4[0] = Datum_0;
|
|
Vals4[1] = int32_pos_hcell_mval;
|
|
Vals4[2] = tmpval;
|
|
Vals4[3] = int32_0;
|
|
tmpval = builder.CreateInBoundsGEP(phi_cell, Vals4);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 8, "hashval_cell");
|
|
llvm::Value* cmp_hashval = builder.CreateICmpEQ(hash_res64, tmpval);
|
|
builder.CreateCondBr(cmp_hashval, hashval_eq, next_cell);
|
|
|
|
/*
|
|
* When hash val is equal, we only consider the result of matchkey.
|
|
* Loop over all the keys, once the key is not matched, get the
|
|
* next cell. If all the keys have been compared, find the next cell
|
|
* or keymatched according to the result. The following code is the
|
|
* codegeneration of the following code:
|
|
* if (true && match_key(<simple>)(batch, i, cell))
|
|
* { ...; break; }
|
|
* cell = cell->flag.m_next
|
|
*/
|
|
builder.SetInsertPoint(hashval_eq);
|
|
for (i = 0; i < numkeys; i++) {
|
|
llvm::Function* func_matchonekey = MatchOneKeyCodeGen(node, i);
|
|
if (NULL == func_matchonekey) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Failed on generating MatchOneKey Function!\n")));
|
|
}
|
|
|
|
/* load the phi_idx-th value and flag of the current key */
|
|
llvm::Value* pval = builder.CreateInBoundsGEP(pVector[i], phi_idx);
|
|
pval = builder.CreateAlignedLoad(pval, 8, "pval");
|
|
llvm::Value* pflag = builder.CreateInBoundsGEP(pFlag[i], phi_idx);
|
|
pflag = builder.CreateAlignedLoad(pflag, 1, "pflag");
|
|
llvm::Value* res = builder.CreateCall(func_matchonekey, {pval, pflag, phi_cell, keyIdxInCell[i]});
|
|
|
|
cmpval = builder.CreateICmpEQ(res, Datum_0);
|
|
|
|
if (i == numkeys - 1)
|
|
builder.CreateCondBr(cmpval, next_cell, key_match);
|
|
else {
|
|
DEFINE_BLOCK(next_bb, jitted_agghashing);
|
|
builder.CreateCondBr(cmpval, next_cell, next_bb);
|
|
builder.SetInsertPoint(next_bb);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* when both hash value and all keys are matched, remember this
|
|
* hash cell and go to next tuple.
|
|
*/
|
|
builder.SetInsertPoint(key_match);
|
|
/* get hashAggRunner.BaseAggRunner.m_Loc[phi_idx] */
|
|
Vals4[0] = Datum_0;
|
|
Vals4[1] = int32_0;
|
|
Vals4[2] = int32_pos_bAggR_Loc;
|
|
Vals4[3] = phi_idx;
|
|
tmpval = builder.CreateInBoundsGEP(hAggRunner, Vals4);
|
|
builder.CreateAlignedStore(phi_cell, tmpval, 8);
|
|
builder.CreateBr(for_inc);
|
|
|
|
/* if (foundMatch == false){ allocate hash slot and initilize it } */
|
|
builder.SetInsertPoint(alloc_hashslot);
|
|
/* HashAggRunner.BaseAggRunner.m_keySimple */
|
|
Vals3[0] = Datum_0;
|
|
Vals3[1] = int32_0;
|
|
Vals3[2] = int32_pos_bAggR_keySimple;
|
|
llvm::Value* simple_key = builder.CreateInBoundsGEP(hAggRunner, Vals3);
|
|
simple_key = builder.CreateAlignedLoad(simple_key, 4, "key_simple");
|
|
if (isSglTbl) {
|
|
WarpSglTblAllocHashSlotCodeGen(&builder, hAggRunner, batch, phi_idx, simple_key);
|
|
} else {
|
|
WarpAllocHashSlotCodeGen(&builder, hAggRunner, batch, phi_idx, simple_key);
|
|
}
|
|
builder.CreateBr(for_inc);
|
|
|
|
builder.SetInsertPoint(for_inc);
|
|
tmpval = builder.CreateTrunc(idx_next, int32Type);
|
|
tmpval = builder.CreateICmpEQ(tmpval, nValues);
|
|
builder.CreateCondBr(tmpval, for_end, for_body);
|
|
|
|
/* return nothing after hashing all the tuples */
|
|
builder.SetInsertPoint(for_end);
|
|
builder.CreateRetVoid();
|
|
|
|
llvmCodeGen->FinalizeFunction(jitted_agghashing, vecagg->plan.plan_node_id);
|
|
|
|
return jitted_agghashing;
|
|
}
|
|
|
|
/*
|
|
* AgghashingWithPrefetchCodeGen
|
|
* @Description : Codegeneration for hashing batch and match key in
|
|
* buildAggTbl. To reduce cache miss, we need to prefetch
|
|
* the hash cell. Different from the original function,
|
|
* we use two loops to handle the hashing and match key
|
|
* separately, since cache miss mostly happens during
|
|
* matching key.
|
|
*
|
|
* AggSglTblhashingWithPrefetchCodeGen
|
|
* @Description : Codegeneration for hashing batch and match key in
|
|
* buildAggTbl. To reduce cache miss, we need to prefetch
|
|
* the hash cell. Different from the original function,
|
|
* we use two loops to handle the hashing and match key
|
|
* separately, since cache miss mostly happens during
|
|
* matching key.
|
|
*/
|
|
template <bool isSglTbl>
|
|
llvm::Function* VecHashAggCodeGen::AgghashingWithPrefetchCodeGenorSglTbl(VecAggState* node)
|
|
{
|
|
Assert(NULL != (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj);
|
|
GsCodeGen* llvmCodeGen = (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj;
|
|
|
|
/* If the condition can not be satisfied, no need to codegen */
|
|
if (!AgghashingJittable(node))
|
|
return NULL;
|
|
|
|
/* Find and load the IR file from the installaion directory */
|
|
llvmCodeGen->loadIRFile();
|
|
|
|
/* Extract plan information from node */
|
|
int i = 0;
|
|
VecAgg* vecagg = (VecAgg*)(node->ss.ps.plan);
|
|
int numkeys = vecagg->numCols;
|
|
AttrNumber* keyIdx = vecagg->grpColIdx;
|
|
|
|
/* Get LLVM Context and builder */
|
|
llvm::LLVMContext& context = llvmCodeGen->context();
|
|
GsCodeGen::LlvmBuilder builder(context);
|
|
|
|
llvm::Value* hAggRunner = NULL;
|
|
llvm::Value* batch = NULL;
|
|
llvm::Value* tmpval = NULL;
|
|
llvm::Value* cmpval = NULL;
|
|
llvm::Value* idx_next = NULL;
|
|
llvm::Value* llvmargs[2];
|
|
llvm::PHINode* phi_idx = NULL;
|
|
llvm::Function* jitted_agghashing = 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(hashCellPtrType, "struct.hashCell");
|
|
DEFINE_CG_PTRTYPE(vectorBatchPtrType, "class.VectorBatch");
|
|
DEFINE_CG_PTRTYPE(hashAggRunnerPtrType, "class.HashAggRunner");
|
|
|
|
DEFINE_CGVAR_INT32(int32_m1, -1);
|
|
DEFINE_CGVAR_INT32(int32_0, 0);
|
|
DEFINE_CGVAR_INT32(int32_1, 1);
|
|
DEFINE_CGVAR_INT32(int32_pos_hBOper_cols, pos_hBOper_cols);
|
|
DEFINE_CGVAR_INT32(int32_pos_hBOper_cacheLoc, pos_hBOper_cacheLoc);
|
|
DEFINE_CGVAR_INT32(int32_pos_hAggR_hashVal, pos_hAggR_hashVal);
|
|
DEFINE_CGVAR_INT32(int32_pos_hAggR_hSegTbl, pos_hAggR_hSegTbl);
|
|
DEFINE_CGVAR_INT32(int32_pos_hAggR_hsegmax, pos_hAggR_hsegmax); /* just for AgghashingWithPrefetchCodeGen */
|
|
DEFINE_CGVAR_INT32(int32_pos_hAggR_hashSize, pos_hAggR_hashSize);
|
|
DEFINE_CGVAR_INT32(int32_pos_bAggR_keyIdxInCell, pos_bAggR_keyIdxInCell);
|
|
DEFINE_CGVAR_INT32(int32_pos_bAggR_Loc, pos_bAggR_Loc);
|
|
DEFINE_CGVAR_INT32(int32_pos_bAggR_keySimple, pos_bAggR_keySimple);
|
|
DEFINE_CGVAR_INT32(int32_pos_hcell_mval, pos_hcell_mval);
|
|
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_scalvec_flag, pos_scalvec_flag);
|
|
DEFINE_CGVAR_INT64(Datum_0, 0);
|
|
DEFINE_CGVAR_INT64(Datum_1, 1);
|
|
|
|
/* llvm array values, used to represent the location of some element */
|
|
llvm::Value* Vals[2] = {Datum_0, int32_0};
|
|
llvm::Value* Vals3[3] = {Datum_0, int32_0, int32_0};
|
|
llvm::Value* Vals4[4] = {Datum_0, int32_0, int32_0, int32_0};
|
|
llvm::Value* Vals5[5] = {Datum_0, int32_0, int32_0, int32_0, int32_0};
|
|
const char* name = NULL;
|
|
if (isSglTbl) {
|
|
name = "JittedSglTblAggHashingWithPreFetch";
|
|
} else {
|
|
name = "JittedAggHashingWithPreFetch";
|
|
}
|
|
GsCodeGen::FnPrototype fn_prototype(llvmCodeGen, name, voidType);
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("hashAggRunner", hashAggRunnerPtrType));
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("batch", vectorBatchPtrType));
|
|
jitted_agghashing = fn_prototype.generatePrototype(&builder, &llvmargs[0]);
|
|
|
|
/* start the main codegen process for hashagg */
|
|
hAggRunner = llvmargs[0];
|
|
batch = llvmargs[1];
|
|
|
|
/* get the number of rows of this batch : VectorBatch.m_rows */
|
|
tmpval = builder.CreateInBoundsGEP(batch, Vals);
|
|
llvm::Value* nValues = builder.CreateAlignedLoad(tmpval, 4, "m_rows");
|
|
|
|
/* mask = hashAggRunner.m_hashSize - 1 */
|
|
Vals[1] = int32_pos_hAggR_hashSize;
|
|
tmpval = builder.CreateInBoundsGEP(hAggRunner, Vals);
|
|
llvm::Value* maskval = builder.CreateAlignedLoad(tmpval, 8, "m_hashSize");
|
|
maskval = builder.CreateSub(maskval, Datum_1, "mask");
|
|
|
|
/* HashAggRunner.BaseAggRunner.hashBasedOperator.m_cols */
|
|
Vals4[3] = int32_pos_hBOper_cols;
|
|
llvm::Value* m_colsVal = builder.CreateInBoundsGEP(hAggRunner, Vals4);
|
|
|
|
/* define basic block information for batch loop */
|
|
llvm::BasicBlock* entry = &jitted_agghashing->getEntryBlock();
|
|
DEFINE_BLOCK(hashing_for_body, jitted_agghashing);
|
|
DEFINE_BLOCK(hashing_for_end, jitted_agghashing);
|
|
DEFINE_BLOCK(hashing_for_inc, jitted_agghashing);
|
|
|
|
DEFINE_BLOCK(for_body, jitted_agghashing);
|
|
DEFINE_BLOCK(for_end, jitted_agghashing);
|
|
DEFINE_BLOCK(for_inc, jitted_agghashing);
|
|
|
|
/* define basic block information for key-value matching */
|
|
DEFINE_BLOCK(while_body, jitted_agghashing);
|
|
DEFINE_BLOCK(key_match, jitted_agghashing);
|
|
DEFINE_BLOCK(hashval_eq, jitted_agghashing);
|
|
DEFINE_BLOCK(next_cell, jitted_agghashing);
|
|
DEFINE_BLOCK(alloc_hashslot, jitted_agghashing);
|
|
|
|
/* define vector structures used for store batch info. in LLVM */
|
|
llvm::Value** keyIdxInCell = (llvm::Value**)palloc(sizeof(llvm::Value*) * numkeys);
|
|
llvm::Value** pVector = (llvm::Value**)palloc(sizeof(llvm::Value*) * numkeys);
|
|
llvm::Value** pFlag = (llvm::Value**)palloc(sizeof(llvm::Value*) * numkeys);
|
|
|
|
/* HashAggRunner.BaseAggRunner.m_keyIdxInCell */
|
|
Vals3[0] = Datum_0;
|
|
Vals3[1] = int32_0;
|
|
Vals3[2] = int32_pos_bAggR_keyIdxInCell;
|
|
llvm::Value* cellkeyIdx = builder.CreateInBoundsGEP(hAggRunner, Vals3);
|
|
cellkeyIdx = builder.CreateAlignedLoad(cellkeyIdx, 4, "keyIdxInCellArr");
|
|
for (i = 0; i < numkeys; i++) {
|
|
/* load keyIdx in cell */
|
|
tmpval = llvmCodeGen->getIntConstant(INT4OID, i);
|
|
keyIdxInCell[i] = builder.CreateInBoundsGEP(cellkeyIdx, tmpval);
|
|
keyIdxInCell[i] = builder.CreateAlignedLoad(keyIdxInCell[i], 4, "m_keyIdxInCell");
|
|
}
|
|
|
|
/* load m_arr from batch data */
|
|
Vals[0] = Datum_0;
|
|
Vals[1] = int32_pos_batch_marr;
|
|
tmpval = builder.CreateInBoundsGEP(batch, Vals);
|
|
llvm::Value* tmparr = builder.CreateAlignedLoad(tmpval, 8, "m_arr");
|
|
for (i = 0; i < numkeys; i++) {
|
|
/* load scalarvector from m_arr */
|
|
AttrNumber key = keyIdx[i] - 1;
|
|
Vals[0] = llvmCodeGen->getIntConstant(INT8OID, key);
|
|
Vals[1] = int32_pos_scalvec_vals;
|
|
llvm::Value* pVec = builder.CreateInBoundsGEP(tmparr, Vals);
|
|
pVector[i] = builder.CreateAlignedLoad(pVec, 8, "pVector");
|
|
|
|
/* load flag information from m_arr */
|
|
Vals[1] = int32_pos_scalvec_flag;
|
|
llvm::Value* Flag = builder.CreateInBoundsGEP(tmparr, Vals);
|
|
pFlag[i] = builder.CreateAlignedLoad(Flag, 8, "pFlag");
|
|
}
|
|
|
|
/*
|
|
* Begin to loop the whole batch to evaluation hashval
|
|
*/
|
|
builder.SetInsertPoint(entry);
|
|
tmpval = builder.CreateICmpSGT(nValues, int32_0);
|
|
builder.CreateCondBr(tmpval, hashing_for_body, for_end);
|
|
|
|
builder.SetInsertPoint(hashing_for_body);
|
|
phi_idx = builder.CreatePHI(int64Type, 2);
|
|
|
|
/* add one for every hashing loop to check the index */
|
|
idx_next = builder.CreateAdd(phi_idx, Datum_1);
|
|
phi_idx->addIncoming(Datum_0, entry);
|
|
phi_idx->addIncoming(idx_next, hashing_for_inc);
|
|
|
|
/* given the initial hash value */
|
|
llvm::Value* hash_res = int32_m1;
|
|
bool rehash = false;
|
|
|
|
/* evaluation the hash value for phi_idx-th tuple */
|
|
for (i = 0; i < numkeys; i++) {
|
|
if (i > 0)
|
|
rehash = true;
|
|
|
|
llvm::Function* func_hashbatch = HashBatchCodeGen(node, i, rehash);
|
|
if (func_hashbatch == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Failed on generating HashBatchCodeGen!\n")));
|
|
}
|
|
|
|
/* load the phi_idx-th value and flag of the current key */
|
|
llvm::Value* pval = builder.CreateInBoundsGEP(pVector[i], phi_idx);
|
|
pval = builder.CreateAlignedLoad(pval, 8, "pval");
|
|
llvm::Value* pflag = builder.CreateInBoundsGEP(pFlag[i], phi_idx);
|
|
pflag = builder.CreateAlignedLoad(pflag, 1, "pflag");
|
|
|
|
hash_res = builder.CreateCall(func_hashbatch, {pval, pflag, hash_res});
|
|
}
|
|
|
|
/* store the hash value (hash_res) to m_hashVal of hashAggRunner */
|
|
/* hashAggRunner.m_hashVal[phi_idx] */
|
|
Vals3[1] = int32_pos_hAggR_hashVal;
|
|
Vals3[2] = phi_idx;
|
|
llvm::Value* hashValSlot = builder.CreateInBoundsGEP(hAggRunner, Vals3);
|
|
llvm::Value* hash_res64 = builder.CreateZExt(hash_res, int64Type);
|
|
builder.CreateAlignedStore(hash_res64, hashValSlot, 8);
|
|
builder.CreateBr(hashing_for_inc);
|
|
|
|
builder.SetInsertPoint(hashing_for_inc);
|
|
tmpval = builder.CreateTrunc(idx_next, int32Type);
|
|
tmpval = builder.CreateICmpEQ(tmpval, nValues);
|
|
builder.CreateCondBr(tmpval, hashing_for_end, hashing_for_body);
|
|
|
|
builder.SetInsertPoint(hashing_for_end);
|
|
builder.CreateBr(for_body);
|
|
|
|
/*
|
|
* Ending the hashing loop and starting the loop for match_key.
|
|
* Initializing hash cell according to the match key result.
|
|
*/
|
|
builder.SetInsertPoint(for_body);
|
|
phi_idx = builder.CreatePHI(int64Type, 2);
|
|
|
|
/* add one for every match key loop to check the index */
|
|
idx_next = builder.CreateAdd(phi_idx, Datum_1);
|
|
phi_idx->addIncoming(Datum_0, hashing_for_end);
|
|
phi_idx->addIncoming(idx_next, for_inc);
|
|
|
|
/* Prefetch hashData[m_cacheLoc[i+2]] and &(hashData[m_cacheLoc[i+4]]) */
|
|
if (isSglTbl) {
|
|
llvm::Function* func_sgltblprefetch = llvmCodeGen->module()->getFunction("prefetchAggSglTblHashing");
|
|
if (func_sgltblprefetch == NULL) {
|
|
func_sgltblprefetch = prefetchAggSglTblHashingCodeGen();
|
|
}
|
|
llvm::Value* nrows = builder.CreateZExt(nValues, int64Type);
|
|
builder.CreateCall(func_sgltblprefetch, {hAggRunner, phi_idx, nrows});
|
|
} else {
|
|
llvm::Function* func_prefetch = llvmCodeGen->module()->getFunction("prefetchAggHashing");
|
|
if (func_prefetch == NULL) {
|
|
func_prefetch = prefetchAggHashingCodeGen();
|
|
}
|
|
llvm::Value* nrows = builder.CreateZExt(nValues, int64Type);
|
|
builder.CreateCall(func_prefetch, {hAggRunner, phi_idx, nrows});
|
|
}
|
|
/* load the hash value (hash_res) from m_hashVal of hashAggRunner */
|
|
/* hashAggRunner.m_hashVal[phi_idx] */
|
|
Vals3[1] = int32_pos_hAggR_hashVal;
|
|
Vals3[2] = phi_idx;
|
|
hashValSlot = builder.CreateInBoundsGEP(hAggRunner, Vals3);
|
|
hash_res64 = builder.CreateAlignedLoad(hashValSlot, 8, "m_hashVal");
|
|
hash_res = builder.CreateTrunc(hash_res64, int32Type);
|
|
|
|
/* corresponding to m_cacheLoc[i] = m_hashVal[i] & mask; (uint64) */
|
|
llvm::Value* cacheLocVal = builder.CreateAnd(hash_res64, maskval, "cacheLoc");
|
|
/* store cacheLocVal to hashAggRunner.BaseAggRunner.hashBasedOperator.m_cacheLoc[i] */
|
|
Vals5[3] = int32_pos_hBOper_cacheLoc;
|
|
Vals5[4] = phi_idx;
|
|
llvm::Value* cacheLoc_i = builder.CreateInBoundsGEP(hAggRunner, Vals5);
|
|
builder.CreateAlignedStore(cacheLocVal, cacheLoc_i, 8);
|
|
|
|
/* segmaxval, nsegsval and pos are for AgghashingCodeGen */
|
|
llvm::Value* segmaxval = NULL;
|
|
llvm::Value* nsegsval = NULL;
|
|
llvm::Value* pos = NULL;
|
|
/* get m_hashseg_max from hashAggRunner to calculate nsegs and pos */
|
|
if (!isSglTbl) {
|
|
Vals[0] = Datum_0;
|
|
Vals[1] = int32_pos_hAggR_hsegmax;
|
|
segmaxval = builder.CreateInBoundsGEP(hAggRunner, Vals);
|
|
segmaxval = builder.CreateAlignedLoad(segmaxval, 4, "segmax");
|
|
segmaxval = builder.CreateSExt(segmaxval, int64Type);
|
|
/* nsegs = m_cacheLoc[i] / m_hashseg_max */
|
|
nsegsval = builder.CreateExactUDiv(cacheLocVal, segmaxval, "nsegs");
|
|
nsegsval = builder.CreateTrunc(nsegsval, int32Type);
|
|
/* pos = m_cacheLoc[i] % m_hashseg_max */
|
|
pos = builder.CreateSRem(cacheLocVal, segmaxval, "pos");
|
|
}
|
|
/* get m_hashData from hashAggRunner */
|
|
Vals[0] = Datum_0;
|
|
Vals[1] = int32_pos_hAggR_hSegTbl;
|
|
llvm::Value* hashData = builder.CreateInBoundsGEP(hAggRunner, Vals);
|
|
hashData = builder.CreateAlignedLoad(hashData, 8, "m_hashData");
|
|
if (isSglTbl) {
|
|
Vals[0] = int32_0;
|
|
} else {
|
|
Vals[0] = nsegsval;
|
|
}
|
|
Vals[1] = int32_1;
|
|
llvm::Value* tbldata = builder.CreateInBoundsGEP(hashData, Vals);
|
|
tbldata = builder.CreateAlignedLoad(tbldata, 8, "tbl_data");
|
|
if (isSglTbl) {
|
|
tmpval = builder.CreateInBoundsGEP(tbldata, cacheLocVal);
|
|
} else {
|
|
tmpval = builder.CreateInBoundsGEP(tbldata, pos);
|
|
}
|
|
llvm::Value* cellval = builder.CreateAlignedLoad(tmpval, 8, "cell");
|
|
/* check if cell is NULL or not */
|
|
tmpval = builder.CreatePtrToInt(cellval, int64Type);
|
|
cmpval = builder.CreateICmpEQ(tmpval, Datum_0);
|
|
builder.CreateCondBr(cmpval, alloc_hashslot, while_body);
|
|
|
|
/* while (cell!= NULL) { compare hash value and do match_key } */
|
|
builder.SetInsertPoint(while_body);
|
|
llvm::PHINode* phi_cell = builder.CreatePHI(hashCellPtrType, 2);
|
|
|
|
/* get next cell : cell = cell->flag.m_next: hashCell.flag.m_next */
|
|
builder.SetInsertPoint(next_cell);
|
|
Vals3[0] = Datum_0;
|
|
Vals3[1] = int32_0;
|
|
Vals3[2] = int32_0;
|
|
llvm::Value* nextcellval = builder.CreateInBoundsGEP(phi_cell, Vals3);
|
|
nextcellval = builder.CreateAlignedLoad(nextcellval, 8, "nextcellval");
|
|
tmpval = builder.CreatePtrToInt(nextcellval, int64Type);
|
|
cmpval = builder.CreateICmpEQ(tmpval, Datum_0);
|
|
builder.CreateCondBr(cmpval, alloc_hashslot, while_body);
|
|
|
|
/* loop over the whole hash cell chain */
|
|
builder.SetInsertPoint(while_body);
|
|
phi_cell->addIncoming(nextcellval, next_cell);
|
|
phi_cell->addIncoming(cellval, for_body);
|
|
|
|
/* get hash value from current cell : cell->m_val[m_cols].val */
|
|
tmpval = builder.CreateAlignedLoad(m_colsVal, 4, "m_cols");
|
|
tmpval = builder.CreateZExt(tmpval, int64Type);
|
|
Vals4[0] = Datum_0;
|
|
Vals4[1] = int32_pos_hcell_mval;
|
|
Vals4[2] = tmpval;
|
|
Vals4[3] = int32_0;
|
|
tmpval = builder.CreateInBoundsGEP(phi_cell, Vals4);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 8, "hashval_cell");
|
|
llvm::Value* cmp_hashval = builder.CreateICmpEQ(hash_res64, tmpval);
|
|
builder.CreateCondBr(cmp_hashval, hashval_eq, next_cell);
|
|
|
|
/*
|
|
* When hash val is equal, we only consider the result of matchkey.
|
|
* Loop over all the keys, once the key is not matched, get the
|
|
* next cell. If all the keys have been compared, find the next cell
|
|
* or keymatched according to the result. The following code is the
|
|
* codegeneration of the following code:
|
|
* if (true && match_key(<simple>)(batch, i, cell))
|
|
* { ...; break; }
|
|
* cell = cell->flag.m_next
|
|
*/
|
|
builder.SetInsertPoint(hashval_eq);
|
|
for (i = 0; i < numkeys; i++) {
|
|
llvm::Function* func_matchonekey = MatchOneKeyCodeGen(node, i);
|
|
if (NULL == func_matchonekey) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Failed on generating MatchOneKey Function!\n")));
|
|
}
|
|
|
|
/* load the phi_idx-th value and flag of the current key */
|
|
llvm::Value* pval = builder.CreateInBoundsGEP(pVector[i], phi_idx);
|
|
pval = builder.CreateAlignedLoad(pval, 8, "pval");
|
|
llvm::Value* pflag = builder.CreateInBoundsGEP(pFlag[i], phi_idx);
|
|
pflag = builder.CreateAlignedLoad(pflag, 1, "pflag");
|
|
llvm::Value* res = builder.CreateCall(func_matchonekey, {pval, pflag, phi_cell, keyIdxInCell[i]});
|
|
|
|
cmpval = builder.CreateICmpEQ(res, Datum_0);
|
|
|
|
if (i == numkeys - 1)
|
|
builder.CreateCondBr(cmpval, next_cell, key_match);
|
|
else {
|
|
DEFINE_BLOCK(next_bb, jitted_agghashing);
|
|
builder.CreateCondBr(cmpval, next_cell, next_bb);
|
|
builder.SetInsertPoint(next_bb);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* when both hash value and all keys are matched, remember this
|
|
* hash cell and go to next tuple.
|
|
*/
|
|
builder.SetInsertPoint(key_match);
|
|
/* get hashAggRunner.BaseAggRunner.m_Loc[phi_idx] */
|
|
Vals4[0] = Datum_0;
|
|
Vals4[1] = int32_0;
|
|
Vals4[2] = int32_pos_bAggR_Loc;
|
|
Vals4[3] = phi_idx;
|
|
tmpval = builder.CreateInBoundsGEP(hAggRunner, Vals4);
|
|
builder.CreateAlignedStore(phi_cell, tmpval, 8);
|
|
builder.CreateBr(for_inc);
|
|
|
|
/* if (foundMatch == false){ allocate hash slot and initilize it } */
|
|
builder.SetInsertPoint(alloc_hashslot);
|
|
/* HashAggRunner.BaseAggRunner.m_keySimple */
|
|
Vals3[0] = Datum_0;
|
|
Vals3[1] = int32_0;
|
|
Vals3[2] = int32_pos_bAggR_keySimple;
|
|
llvm::Value* simple_key = builder.CreateInBoundsGEP(hAggRunner, Vals3);
|
|
simple_key = builder.CreateAlignedLoad(simple_key, 4, "key_simple");
|
|
WarpAllocHashSlotCodeGen(&builder, hAggRunner, batch, phi_idx, simple_key);
|
|
builder.CreateBr(for_inc);
|
|
|
|
builder.SetInsertPoint(for_inc);
|
|
tmpval = builder.CreateTrunc(idx_next, int32Type);
|
|
tmpval = builder.CreateICmpEQ(tmpval, nValues);
|
|
builder.CreateCondBr(tmpval, for_end, for_body);
|
|
|
|
/* return nothing after hashing all the tuples */
|
|
builder.SetInsertPoint(for_end);
|
|
builder.CreateRetVoid();
|
|
|
|
llvmCodeGen->FinalizeFunction(jitted_agghashing, vecagg->plan.plan_node_id);
|
|
|
|
return jitted_agghashing;
|
|
}
|
|
|
|
llvm::Function* VecHashAggCodeGen::BatchAggregationCodeGen(VecAggState* node, bool use_prefetch)
|
|
{
|
|
/* First get the basic information of VecAggState */
|
|
int numaggs = node->numaggs;
|
|
AggStatePerAgg peragg = node->peragg;
|
|
|
|
Assert(NULL != (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj);
|
|
GsCodeGen* llvmCodeGen = (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj;
|
|
|
|
if (!BatchAggJittable(node, false))
|
|
return NULL;
|
|
|
|
/* 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();
|
|
|
|
int i;
|
|
int exprscale = 0;
|
|
bool fast_aggref = false;
|
|
ExprState* estate = NULL;
|
|
llvm::Value* nValues = NULL;
|
|
llvm::Value* tmpval = NULL;
|
|
llvm::Value* idx_next = NULL;
|
|
llvm::Value* cell = NULL;
|
|
llvm::Value* result = NULL;
|
|
llvm::Value* expres = NULL;
|
|
llvm::Value* llvmargs[4];
|
|
Aggref* aggref = NULL;
|
|
llvm::Function* jitted_batchagg = NULL;
|
|
|
|
/* Define data types and some llvm consts */
|
|
DEFINE_CG_VOIDTYPE(voidType);
|
|
DEFINE_CG_TYPE(int8Type, CHAROID);
|
|
DEFINE_CG_TYPE(int16Type, INT2OID);
|
|
DEFINE_CG_TYPE(int32Type, INT4OID);
|
|
DEFINE_CG_TYPE(int64Type, INT8OID);
|
|
DEFINE_CG_PTRTYPE(int32PtrType, INT4OID);
|
|
DEFINE_CG_PTRTYPE(int64PtrType, INT8OID);
|
|
DEFINE_CG_PTRTYPE(hashCellPtrType, "struct.hashCell");
|
|
DEFINE_CG_PTRTYPE(ExprContextPtrType, "struct.ExprContext");
|
|
DEFINE_CG_PTRTYPE(vectorBatchPtrType, "class.VectorBatch");
|
|
DEFINE_CG_PTRTYPE(hashAggRunnerPtrType, "class.HashAggRunner");
|
|
DEFINE_CG_PTRTYPE(numericPtrType, "struct.NumericData");
|
|
llvm::Type* hashCellPtrPtrType = llvmCodeGen->getPtrType(hashCellPtrType);
|
|
|
|
/* create LLVM value with {uint16, int64} format type */
|
|
llvm::Type* Elements[] = {int16Type, int64Type};
|
|
llvm::Type* SiNumeric64Type = llvm::StructType::create(context, Elements, "SiNumeric64");
|
|
|
|
DEFINE_CGVAR_INT8(int8_0, 0);
|
|
DEFINE_CGVAR_INT8(int8_1, 1);
|
|
DEFINE_CGVAR_INT16(val_mask, NUMERIC_BI_MASK);
|
|
DEFINE_CGVAR_INT16(val_binum64, NUMERIC_64);
|
|
DEFINE_CGVAR_INT32(int32_0, 0);
|
|
DEFINE_CGVAR_INT32(int32_1, 1);
|
|
DEFINE_CGVAR_INT64(int64_0, 0);
|
|
DEFINE_CGVAR_INT64(int64_1, 1);
|
|
DEFINE_CGVAR_INT64(int64_6, 6);
|
|
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_scalvec_flag, pos_scalvec_flag);
|
|
DEFINE_CGVAR_INT32(int32_pos_ecxt_pertuple, pos_ecxt_pertuple);
|
|
DEFINE_CGVAR_INT32(int32_pos_ecxt_outerbatch, pos_ecxt_outerbatch);
|
|
DEFINE_CGVAR_INT32(int32_pos_hBOper_hcxt, pos_hBOper_hcxt);
|
|
DEFINE_CGVAR_INT32(int32_pos_bAggR_econtext, pos_bAggR_econtext);
|
|
DEFINE_CGVAR_INT32(int32_pos_hcell_mval, pos_hcell_mval);
|
|
|
|
/* 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};
|
|
|
|
llvm::Value** aggIdxList = (llvm::Value**)palloc(sizeof(llvm::Value*) * numaggs);
|
|
llvm::Value** batch_vals = (llvm::Value**)palloc(sizeof(llvm::Value*) * numaggs);
|
|
llvm::Value** batch_flag = (llvm::Value**)palloc(sizeof(llvm::Value*) * numaggs);
|
|
llvm::BasicBlock** agg_bb = (llvm::BasicBlock**)palloc(sizeof(llvm::BasicBlock*) * numaggs);
|
|
llvm::BasicBlock** flag_then = (llvm::BasicBlock**)palloc(sizeof(llvm::BasicBlock*) * numaggs);
|
|
llvm::BasicBlock** flag_else = (llvm::BasicBlock**)palloc(sizeof(llvm::BasicBlock*) * numaggs);
|
|
|
|
GsCodeGen::FnPrototype fn_prototype(llvmCodeGen, "JittedFastBatchAgg", voidType);
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("haRuner", hashAggRunnerPtrType));
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("Loc", hashCellPtrPtrType));
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("batch", vectorBatchPtrType));
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("aggIdx", int32PtrType));
|
|
jitted_batchagg = fn_prototype.generatePrototype(&builder, &llvmargs[0]);
|
|
|
|
llvm::Value* hAggRunner = llvmargs[0];
|
|
llvm::Value* Loc = llvmargs[1];
|
|
llvm::Value* batch = llvmargs[2];
|
|
llvm::Value* aggIdx = llvmargs[3];
|
|
|
|
/* parameter used to mark if this tuple is NULL or not */
|
|
llvm::Value* isNull = builder.CreateAlloca(int8Type);
|
|
|
|
/* HashAggRunner.BaseAggRunner.hashBasedOperator.m_hashContext */
|
|
Vals4[3] = int32_pos_hBOper_hcxt;
|
|
llvm::Value* hcxt = builder.CreateInBoundsGEP(hAggRunner, Vals4);
|
|
hcxt = builder.CreateAlignedLoad(hcxt, 8, "hashContext");
|
|
|
|
/* get the nrows of the batch */
|
|
tmpval = builder.CreateInBoundsGEP(batch, Vals);
|
|
nValues = builder.CreateAlignedLoad(tmpval, 4, "m_rows");
|
|
|
|
/* get vectorBatch.m_arr of the batch */
|
|
Vals[0] = int64_0;
|
|
Vals[1] = int32_pos_batch_marr;
|
|
llvm::Value* argVector = builder.CreateInBoundsGEP(batch, Vals);
|
|
argVector = builder.CreateAlignedLoad(argVector, 8, "m_arr");
|
|
|
|
/* pre-load all the expression context */
|
|
llvm::Value** econtext = (llvm::Value**)palloc(sizeof(llvm::Value*) * numaggs);
|
|
for (i = 0; i < numaggs; i++) {
|
|
econtext[i] = NULL;
|
|
AggStatePerAgg peraggstate = &node->peragg[numaggs - i - 1];
|
|
aggref = (Aggref*)(peraggstate->aggref);
|
|
if (peraggstate->evalproj != NULL && aggref->aggfnoid != COUNTOID) {
|
|
ExprContext* exprcontext = peragg[numaggs - 1 - i].evalproj->pi_exprContext;
|
|
econtext[i] = llvmCodeGen->CastPtrToLlvmPtr(ExprContextPtrType, exprcontext);
|
|
}
|
|
}
|
|
|
|
/* define the basic block needed in the main process */
|
|
llvm::BasicBlock* entry = &jitted_batchagg->getEntryBlock();
|
|
DEFINE_BLOCK(for_body, jitted_batchagg);
|
|
DEFINE_BLOCK(for_inc, jitted_batchagg);
|
|
DEFINE_BLOCK(for_end, jitted_batchagg);
|
|
|
|
/* get all addIdx of agg operators */
|
|
for (i = 0; i < numaggs; i++) {
|
|
llvm::Value* tmpidx = llvmCodeGen->getIntConstant(INT4OID, i);
|
|
tmpval = builder.CreateInBoundsGEP(aggIdx, tmpidx);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 4, "aggIdx");
|
|
aggIdxList[i] = builder.CreateSExt(tmpval, int64Type);
|
|
|
|
agg_bb[i] = llvm::BasicBlock::Create(context, "agg_bb", jitted_batchagg);
|
|
flag_then[i] = llvm::BasicBlock::Create(context, "flag_then", jitted_batchagg);
|
|
flag_else[i] = llvm::BasicBlock::Create(context, "flag_else", jitted_batchagg);
|
|
}
|
|
|
|
/*
|
|
* Start the main process for batchaggregation, which has the following
|
|
* pedudo code:
|
|
* for (j = 0; j < nrows; j++){
|
|
* for (i = 0; i < m_aggNum; i++){
|
|
* peraggstate = peragg[numaggs - 1 - i];
|
|
* pbatch = ExecVecProject (peraggstate->evalproj)
|
|
* AggregationOnScalar(aggInfo[i], &pbatch->m_arr[0], aggidx[i], m_Loc)
|
|
* }
|
|
* }
|
|
*/
|
|
builder.SetInsertPoint(entry);
|
|
/*
|
|
* First get the ecxt_per_tuple_memory, since we need to switch to this
|
|
* memory context.
|
|
*/
|
|
/* HashAggRunner.BaseAggRunner.m_econtext */
|
|
Vals3[2] = int32_pos_bAggR_econtext;
|
|
llvm::Value* mecontext = builder.CreateInBoundsGEP(hAggRunner, Vals3);
|
|
mecontext = builder.CreateAlignedLoad(mecontext, 8, "m_econtext");
|
|
Vals[1] = int32_pos_ecxt_pertuple;
|
|
llvm::Value* agg_expr_context = builder.CreateInBoundsGEP(mecontext, Vals);
|
|
agg_expr_context = builder.CreateAlignedLoad(agg_expr_context, 8, "agg_per_tuple_memory");
|
|
llvm::Value* agg_oldcontext = VecExprCodeGen::MemCxtSwitToCodeGen(&builder, agg_expr_context);
|
|
|
|
/*
|
|
* Load value and flag from batch before the batch loop when we have
|
|
* simple vars in transition level.
|
|
*/
|
|
for (i = 0; i < numaggs; i++) {
|
|
int numSimpleVars = 0;
|
|
AggStatePerAgg peraggstate = &node->peragg[numaggs - i - 1];
|
|
aggref = (Aggref*)(peraggstate->aggref);
|
|
ProjectionInfo* projInfo = (ProjectionInfo*)(peraggstate->evalproj);
|
|
|
|
if (aggref->aggstage == 0 && aggref->aggfnoid != COUNTOID) {
|
|
numSimpleVars = projInfo->pi_numSimpleVars;
|
|
if (numSimpleVars > 0) {
|
|
int* varNumbers = projInfo->pi_varNumbers;
|
|
int varNumber = varNumbers[0] - 1;
|
|
/* m_arr[varNumber].m_vals */
|
|
Vals[0] = llvmCodeGen->getIntConstant(INT8OID, varNumber);
|
|
Vals[1] = int32_pos_scalvec_vals;
|
|
tmpval = builder.CreateInBoundsGEP(argVector, Vals);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 8, "m_vals");
|
|
batch_vals[i] = tmpval;
|
|
|
|
/* m_arr[varNumber].m_flag */
|
|
Vals[1] = int32_pos_scalvec_flag;
|
|
llvm::Value* argFlag = builder.CreateInBoundsGEP(argVector, Vals);
|
|
argFlag = builder.CreateAlignedLoad(argFlag, 1, "m_flag");
|
|
batch_flag[i] = argFlag;
|
|
}
|
|
}
|
|
}
|
|
|
|
tmpval = builder.CreateICmpSGT(nValues, int32_0);
|
|
builder.CreateCondBr(tmpval, for_body, for_end);
|
|
|
|
builder.SetInsertPoint(for_body);
|
|
llvm::PHINode* phi_idx = builder.CreatePHI(int64Type, 2);
|
|
|
|
/* after each loop, index plus one */
|
|
idx_next = builder.CreateAdd(phi_idx, int64_1);
|
|
|
|
phi_idx->addIncoming(int64_0, entry);
|
|
phi_idx->addIncoming(idx_next, for_inc);
|
|
|
|
/* define prefetch function to prefetch loc[i+2] to avoid cache miss */
|
|
if (use_prefetch) {
|
|
llvm::Function* func_prefetch = llvmCodeGen->module()->getFunction("prefetchBatchAggregation");
|
|
if (NULL == func_prefetch) {
|
|
func_prefetch = prefetchBatchAggregationCodeGen();
|
|
}
|
|
llvm::Value* nrows = builder.CreateZExt(nValues, int64Type);
|
|
builder.CreateCall(func_prefetch, {Loc, phi_idx, nrows});
|
|
}
|
|
|
|
/*
|
|
* get the hashcell : cell = Loc[i] (see vnumeric_sum and vint8_sum)
|
|
* and check if it is NULL
|
|
*/
|
|
tmpval = builder.CreateInBoundsGEP(Loc, phi_idx);
|
|
cell = builder.CreateAlignedLoad(tmpval, 8, "hashCell");
|
|
tmpval = builder.CreatePtrToInt(cell, int64Type);
|
|
tmpval = builder.CreateICmpEQ(tmpval, int64_0);
|
|
builder.CreateCondBr(tmpval, for_inc, agg_bb[0]);
|
|
|
|
/* loop over the numaggs */
|
|
int numSimpleVars = 0;
|
|
for (i = 0; i < numaggs; i++) {
|
|
llvm::BasicBlock* bisum_bb = NULL;
|
|
llvm::BasicBlock* numsum_bb = NULL;
|
|
|
|
/* the inverse order */
|
|
AggStatePerAgg peraggstate = &node->peragg[numaggs - i - 1];
|
|
aggref = (Aggref*)(peraggstate->aggref);
|
|
ProjectionInfo* projInfo = (ProjectionInfo*)(peraggstate->evalproj);
|
|
|
|
/* start the codegeneration for each aggregation */
|
|
builder.SetInsertPoint(agg_bb[i]);
|
|
if (aggref->aggstage == 0) {
|
|
if (aggref->aggfnoid != COUNTOID) {
|
|
Assert(peraggstate->evalproj != NULL);
|
|
AggrefExprState* aggexprstate = peraggstate->aggrefstate;
|
|
/* check if current expression can be codegened in fast path or not */
|
|
estate = (ExprState*)linitial(aggexprstate->args);
|
|
fast_aggref = AggRefFastJittable(estate);
|
|
|
|
/*
|
|
* Do not consider collection and finalization level for
|
|
* numeric_avg to avoid deconstruct_array.
|
|
*/
|
|
if (aggref->aggfnoid == NUMERICAVGFUNCOID && aggref->aggstage > 0)
|
|
fast_aggref = false;
|
|
|
|
/* If the current agg expression is just a simple var,
|
|
* load it from the batch directly
|
|
*/
|
|
if (projInfo == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Unexpected NULL project information.")));
|
|
}
|
|
numSimpleVars = projInfo->pi_numSimpleVars;
|
|
if (numSimpleVars > 0) {
|
|
/* m_arr[varNumber].m_vals */
|
|
tmpval = batch_vals[i];
|
|
tmpval = builder.CreateInBoundsGEP(tmpval, phi_idx);
|
|
result = builder.CreateAlignedLoad(tmpval, 8, "val");
|
|
|
|
/* m_arr[varNumber].m_flag */
|
|
tmpval = batch_flag[i];
|
|
tmpval = builder.CreateInBoundsGEP(tmpval, phi_idx);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 1, "flag");
|
|
builder.CreateAlignedStore(tmpval, isNull, 1);
|
|
} else {
|
|
/* set the batch information : econtext->ecxt_outerbatch = batch */
|
|
Vals[0] = int64_0;
|
|
Vals[1] = int32_pos_ecxt_outerbatch;
|
|
llvm::Value* tmp_outerbatch = builder.CreateInBoundsGEP(econtext[i], Vals);
|
|
builder.CreateAlignedStore(batch, tmp_outerbatch, 8);
|
|
|
|
/*
|
|
* If fast_aggref is true, we could try to evaluate the
|
|
* expression value by using BI64 all the way, and turn
|
|
* to original path once meet outofbound.
|
|
*/
|
|
if (fast_aggref) {
|
|
llvm::BasicBlock* bb_last = builder.GetInsertBlock();
|
|
DEFINE_BLOCK(bb_null, jitted_batchagg);
|
|
DEFINE_BLOCK(bb_outofbound, jitted_batchagg);
|
|
|
|
if (NULL == bisum_bb) {
|
|
bisum_bb = llvm::BasicBlock::Create(context, "bisum_bb", jitted_batchagg);
|
|
}
|
|
|
|
if (NULL == numsum_bb) {
|
|
numsum_bb = llvm::BasicBlock::Create(context, "numsum_bb", jitted_batchagg);
|
|
}
|
|
|
|
/* evaluate expression result */
|
|
llvm::Value* tmpexpres = EvalFastExprInBatchAgg(estate,
|
|
builder,
|
|
jitted_batchagg,
|
|
&bb_null,
|
|
&bb_last,
|
|
&bb_outofbound,
|
|
econtext[i],
|
|
argVector,
|
|
phi_idx);
|
|
|
|
/* expres is already in {int16, int64} format */
|
|
builder.SetInsertPoint(bb_last);
|
|
builder.CreateAlignedStore(int8_0, isNull, 1);
|
|
llvm::Value* tmp_scale = builder.CreateExtractValue(tmpexpres, 0);
|
|
llvm::Value* tmp_value = builder.CreateExtractValue(tmpexpres, 1);
|
|
expres = llvm::UndefValue::get(SiNumeric64Type);
|
|
expres = builder.CreateInsertValue(expres, tmp_scale, 0);
|
|
expres = builder.CreateInsertValue(expres, tmp_value, 1);
|
|
builder.CreateBr(bisum_bb);
|
|
|
|
/* construct a null value, and no need to do aggregation */
|
|
builder.SetInsertPoint(bb_null);
|
|
builder.CreateAlignedStore(int8_1, isNull, 1);
|
|
if (i == numaggs - 1)
|
|
builder.CreateBr(for_inc);
|
|
else
|
|
builder.CreateBr(agg_bb[i + 1]);
|
|
|
|
/* if result can not be represented in BI64, turn to
|
|
* the original path
|
|
*/
|
|
builder.SetInsertPoint(bb_outofbound);
|
|
/* Turn to per_tuple_memory to evaluate expression. */
|
|
Vals[0] = int64_0;
|
|
Vals[1] = int32_pos_ecxt_pertuple;
|
|
llvm::Value* curr_Context = builder.CreateInBoundsGEP(econtext[i], Vals);
|
|
curr_Context = builder.CreateAlignedLoad(curr_Context, 8, "per_tuple_memory");
|
|
llvm::Value* cg_oldContext = VecExprCodeGen::MemCxtSwitToCodeGen(&builder, curr_Context);
|
|
result = EvalSimpleExprInBatchAgg(estate, builder, econtext[i], phi_idx, isNull);
|
|
/* return back to the old memory context */
|
|
(void)VecExprCodeGen::MemCxtSwitToCodeGen(&builder, cg_oldContext);
|
|
} else {
|
|
/*
|
|
* corresponding to ExecVecProject(peraggstate->evalproj) :
|
|
* to evaluate expressions, we should turn to per_tuple_memory.
|
|
*/
|
|
Vals[0] = int64_0;
|
|
Vals[1] = int32_pos_ecxt_pertuple;
|
|
llvm::Value* curr_Context = builder.CreateInBoundsGEP(econtext[i], Vals);
|
|
curr_Context = builder.CreateAlignedLoad(curr_Context, 8, "per_tuple_memory");
|
|
llvm::Value* cg_oldContext = VecExprCodeGen::MemCxtSwitToCodeGen(&builder, curr_Context);
|
|
/* corresponding to ExecVecProject(peraggstate->evalproj) */
|
|
result = EvalSimpleExprInBatchAgg(estate, builder, econtext[i], phi_idx, isNull);
|
|
/* return back to the old memory context */
|
|
(void)VecExprCodeGen::MemCxtSwitToCodeGen(&builder, cg_oldContext);
|
|
}
|
|
}
|
|
} else {
|
|
/*
|
|
* When current stage is transaction and aggfnoid is COUNTOID, no need to
|
|
* load any batch information. since we only need to plus one when cell
|
|
* is not null.
|
|
*/
|
|
result = int64_0;
|
|
builder.CreateAlignedStore(int8_0, isNull, 1);
|
|
}
|
|
} else {
|
|
/*
|
|
* When aggref->stage is not transiction, the aggref expr is always
|
|
* be var, so get the value from batch directly (projInfo is not null).
|
|
*/
|
|
if (projInfo != NULL) {
|
|
int* varNumbers = projInfo->pi_varNumbers;
|
|
int varNumber = varNumbers[0] - 1;
|
|
/* m_arr[varNumber].m_vals */
|
|
Vals[0] = llvmCodeGen->getIntConstant(INT8OID, varNumber);
|
|
Vals[1] = int32_pos_scalvec_vals;
|
|
tmpval = builder.CreateInBoundsGEP(argVector, Vals);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 8, "m_vals");
|
|
tmpval = builder.CreateInBoundsGEP(tmpval, phi_idx);
|
|
result = builder.CreateAlignedLoad(tmpval, 8, "val");
|
|
|
|
/* m_arr[varNumber].m_flag */
|
|
Vals[1] = int32_pos_scalvec_flag;
|
|
llvm::Value* argFlag = builder.CreateInBoundsGEP(argVector, Vals);
|
|
argFlag = builder.CreateAlignedLoad(argFlag, 1, "m_flag");
|
|
tmpval = builder.CreateInBoundsGEP(argFlag, phi_idx);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 1, "flag");
|
|
builder.CreateAlignedStore(tmpval, isNull, 1);
|
|
} else {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Unexpected NULL project information.")));
|
|
}
|
|
}
|
|
|
|
/* Compute Aggregation */
|
|
if (aggref->aggfnoid == COUNTOID) {
|
|
flag_then[i]->eraseFromParent();
|
|
flag_else[i]->eraseFromParent();
|
|
|
|
char* Jittedname = NULL;
|
|
if (aggref->aggstage == 0)
|
|
Jittedname = "Jitted_count_0";
|
|
else
|
|
Jittedname = "Jitted_count_1";
|
|
|
|
llvm::Function* func_vcount = llvmCodeGen->module()->getFunction(Jittedname);
|
|
if (NULL == func_vcount) {
|
|
func_vcount = vec_count_codegen(aggref);
|
|
}
|
|
builder.CreateCall(func_vcount, {cell, aggIdxList[i], result});
|
|
|
|
if (i == numaggs - 1)
|
|
builder.CreateBr(for_inc);
|
|
else
|
|
builder.CreateBr(agg_bb[i + 1]);
|
|
} else {
|
|
/*
|
|
* now we already get HashCell cell(cellval) and pVector(result), check
|
|
* the flag and do aggregation.
|
|
*/
|
|
/* see if IS_NULL(flag[phi_idx]) == false */
|
|
llvm::Value* tmpnull = builder.CreateAlignedLoad(isNull, 1, "tmpnull");
|
|
tmpnull = builder.CreateAnd(tmpnull, int8_1);
|
|
llvm::Value* flag_cmp = builder.CreateICmpEQ(tmpnull, int8_0);
|
|
builder.CreateCondBr(flag_cmp, flag_then[i], flag_else[i]);
|
|
|
|
/* only do when not null */
|
|
builder.SetInsertPoint(flag_then[i]);
|
|
switch (aggref->aggfnoid) {
|
|
case INT8SUMFUNCOID: {
|
|
llvm::Function* func_vint8sum = llvmCodeGen->module()->getFunction("Jitted_int8sum");
|
|
if (NULL == func_vint8sum) {
|
|
func_vint8sum = int8_sum_codegen(aggref);
|
|
}
|
|
builder.CreateCall(func_vint8sum, {cell, hcxt, aggIdxList[i], result});
|
|
} break;
|
|
case INT8AVGFUNCOID: {
|
|
llvm::Function* func_vint8avg = llvmCodeGen->module()->getFunction("Jitted_int8avg");
|
|
if (NULL == func_vint8avg) {
|
|
func_vint8avg = int8_avg_codegen(aggref);
|
|
}
|
|
builder.CreateCall(func_vint8avg, {cell, hcxt, aggIdxList[i], result});
|
|
} break;
|
|
case NUMERICSUMFUNCOID: {
|
|
/*
|
|
* If aggref can be evaluated in fast path and just be
|
|
* simple vars, use the result from batch.
|
|
*/
|
|
if (fast_aggref && (numSimpleVars > 0)) {
|
|
DEFINE_BLOCK(agg_then, jitted_batchagg);
|
|
DEFINE_BLOCK(agg_else, jitted_batchagg);
|
|
DEFINE_BLOCK(agg_end, jitted_batchagg);
|
|
DEFINE_BLOCK(normal_bb, jitted_batchagg);
|
|
DEFINE_BLOCK(var_bisum_bb, jitted_batchagg);
|
|
DEFINE_BLOCK(var_numsum_bb, jitted_batchagg);
|
|
|
|
/* get the hash val : hashCell->m_val[aggidx].val */
|
|
Vals4[0] = int64_0;
|
|
Vals4[1] = int32_pos_hcell_mval;
|
|
Vals4[2] = aggIdxList[i];
|
|
Vals4[3] = int32_0;
|
|
llvm::Value* cellval = builder.CreateInBoundsGEP(cell, Vals4);
|
|
|
|
/* get the flag of the hash cell and check if it is NULL */
|
|
Vals4[3] = int32_1;
|
|
llvm::Value* cellflag = builder.CreateInBoundsGEP(cell, Vals4);
|
|
tmpval = builder.CreateAlignedLoad(cellflag, 1, "cellFlag");
|
|
tmpval = builder.CreateAnd(tmpval, int8_1);
|
|
tmpval = builder.CreateICmpEQ(tmpval, int8_0);
|
|
builder.CreateCondBr(tmpval, agg_else, agg_then);
|
|
|
|
/* cell be null, add Variable */
|
|
builder.SetInsertPoint(agg_then);
|
|
/*
|
|
* should make a new context to record the result : the
|
|
* following code corresponding to:
|
|
* 'leftarg = DatumGetBINumeric(pVal[i]);
|
|
* cell->m_val[idx].val = addVariable(context, NumericGetDatum(leftarg));'.
|
|
*/
|
|
tmpval = DatumGetBINumericCodeGen(&builder, result);
|
|
tmpval = builder.CreatePtrToInt(tmpval, int64Type);
|
|
tmpval = WrapaddVariableCodeGen(&builder, hcxt, tmpval);
|
|
|
|
builder.CreateAlignedStore(tmpval, cellval, 8);
|
|
builder.CreateAlignedStore(int8_0, cellflag, 1);
|
|
|
|
/* turn to next basicblock or end this */
|
|
if (i == numaggs - 1)
|
|
builder.CreateBr(for_inc);
|
|
else
|
|
builder.CreateBr(agg_bb[i + 1]);
|
|
|
|
/* cell be not null, do aggregation */
|
|
builder.SetInsertPoint(agg_else);
|
|
|
|
/*
|
|
* When fast_aggref is true and numSimpleVars is greater than zero,
|
|
* the expr is numeric type var. Convert this numeric type data to
|
|
* SiNumeric data to get the value.
|
|
*/
|
|
llvm::Value* bires = DatumGetBINumericCodeGen(&builder, result);
|
|
|
|
/* extract the header of result to check if it is BINumeric */
|
|
Vals4[0] = int64_0;
|
|
Vals4[1] = int32_1;
|
|
Vals4[2] = int32_0;
|
|
Vals4[3] = int32_0;
|
|
tmpval = builder.CreateInBoundsGEP(bires, Vals4);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 2, "biheader");
|
|
llvm::Value* rflag = builder.CreateAnd(tmpval, val_mask);
|
|
|
|
/* extract the header of hashcell to check if it is BINumeric */
|
|
llvm::Value* real_cellval = builder.CreateAlignedLoad(cellval, 8, "cell_val");
|
|
llvm::Value* cellarg = builder.CreateIntToPtr(real_cellval, numericPtrType);
|
|
tmpval = builder.CreateInBoundsGEP(cellarg, Vals4);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 2, "cellheader");
|
|
llvm::Value* lflag = builder.CreateAnd(tmpval, val_mask);
|
|
|
|
/* check if either of them is not BI64 */
|
|
llvm::Value* oparg1 = builder.CreateICmpEQ(lflag, val_binum64);
|
|
llvm::Value* oparg2 = builder.CreateICmpEQ(rflag, val_binum64);
|
|
llvm::Value* bothbi64 = builder.CreateAnd(oparg1, oparg2);
|
|
|
|
/* use fast path only when both args are bi64 */
|
|
builder.CreateCondBr(bothbi64, var_bisum_bb, var_numsum_bb);
|
|
|
|
builder.SetInsertPoint(var_bisum_bb);
|
|
/* extract the actual data of numeric only when cell is not null */
|
|
Vals4[0] = int64_0;
|
|
Vals4[1] = int32_1;
|
|
Vals4[2] = int32_0;
|
|
Vals4[3] = int32_1;
|
|
tmpval = builder.CreateInBoundsGEP(bires, Vals4);
|
|
tmpval = builder.CreateBitCast(tmpval, int64PtrType);
|
|
llvm::Value* resval = builder.CreateAlignedLoad(tmpval, 8, "value");
|
|
|
|
/* locate the restore value in hash cell by position */
|
|
llvm::Value* cell_addr = builder.CreateAdd(real_cellval, int64_6);
|
|
cell_addr = builder.CreateIntToPtr(cell_addr, int64PtrType);
|
|
llvm::Value* mid_cell_val = builder.CreateAlignedLoad(cell_addr, 8);
|
|
|
|
/* check overflow */
|
|
llvm::Type* Intrinsic_Tys[] = {int64Type};
|
|
llvm::Function* func_sadd_overflow =
|
|
llvm::Intrinsic::getDeclaration(mod, llvm::Intrinsic::sadd_with_overflow, Intrinsic_Tys);
|
|
if (func_sadd_overflow == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_LOAD_INTRINSIC_FUNCTION_FAILED),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Cannot get the llvm::Intrinsic::sadd_with_overflow function!\n")));
|
|
}
|
|
llvm::Value* aggres = builder.CreateCall(func_sadd_overflow, {resval, mid_cell_val});
|
|
llvm::Value* oflag = builder.CreateExtractValue(aggres, 1);
|
|
builder.CreateCondBr(oflag, var_numsum_bb, normal_bb);
|
|
|
|
/* if meet overflow during aggregation, turn to original sum function. */
|
|
builder.SetInsertPoint(var_numsum_bb);
|
|
llvm::Function* func_vnumericsum = llvmCodeGen->module()->getFunction("Jitted_numericsum");
|
|
if (NULL == func_vnumericsum) {
|
|
func_vnumericsum = numeric_sum_codegen(aggref);
|
|
}
|
|
builder.CreateCall(func_vnumericsum, {cell, hcxt, aggIdxList[i], result});
|
|
builder.CreateBr(agg_end);
|
|
|
|
builder.SetInsertPoint(normal_bb);
|
|
llvm::Value* sumval = builder.CreateExtractValue(aggres, 0);
|
|
builder.CreateAlignedStore(sumval, cell_addr, 8);
|
|
builder.CreateBr(agg_end);
|
|
|
|
builder.SetInsertPoint(agg_end);
|
|
} else if (fast_aggref) {
|
|
/*
|
|
* If aggref can be evaluated in fast path and be numeric
|
|
* expressions, use the result from fastexpr.
|
|
*/
|
|
Assert(bisum_bb != NULL);
|
|
Assert(numsum_bb != NULL);
|
|
DEFINE_BLOCK(agg_end, jitted_batchagg);
|
|
DEFINE_BLOCK(agg_then, jitted_batchagg);
|
|
DEFINE_BLOCK(agg_else, jitted_batchagg);
|
|
DEFINE_BLOCK(normal_bb, jitted_batchagg);
|
|
DEFINE_BLOCK(expr_bisum_bb, jitted_batchagg);
|
|
DEFINE_BLOCK(bioverflow_bb, jitted_batchagg);
|
|
|
|
/*
|
|
* if the result of expression is outofbound, turn to
|
|
* original numeric path.
|
|
*/
|
|
builder.CreateBr(numsum_bb);
|
|
|
|
/*
|
|
* if the result of expression is BI64, extract it.
|
|
*/
|
|
builder.SetInsertPoint(bisum_bb);
|
|
llvm::Value* resval = builder.CreateExtractValue(expres, 1);
|
|
|
|
/* get the hash val : hashCell->m_val[aggidx].val */
|
|
Vals4[0] = int64_0;
|
|
Vals4[1] = int32_pos_hcell_mval;
|
|
Vals4[2] = aggIdxList[i];
|
|
Vals4[3] = int32_0;
|
|
llvm::Value* cellval = builder.CreateInBoundsGEP(cell, Vals4);
|
|
|
|
/* get the flag of the hash cell and check if it is NULL */
|
|
Vals4[3] = int32_1;
|
|
llvm::Value* cellflag = builder.CreateInBoundsGEP(cell, Vals4);
|
|
tmpval = builder.CreateAlignedLoad(cellflag, 1, "cellFlag");
|
|
tmpval = builder.CreateAnd(tmpval, int8_1);
|
|
tmpval = builder.CreateICmpEQ(tmpval, int8_0);
|
|
builder.CreateCondBr(tmpval, agg_else, agg_then);
|
|
|
|
/* cell be null, add Variable */
|
|
builder.SetInsertPoint(agg_then);
|
|
/* get the aligned scale of this expression */
|
|
exprscale = GetAlignedScale(estate->expr);
|
|
llvm::Value* alignedscale = llvmCodeGen->getIntConstant(CHAROID, exprscale);
|
|
/*
|
|
* should make a new context to record the result : the
|
|
* following code corresponding to:
|
|
* 'leftarg = DatumGetBINumeric(pVal[i]);
|
|
* cell->m_val[idx].val = addVariable(context, NumericGetDatum(leftarg));'.
|
|
*/
|
|
tmpval = WrapmakeNumeric64CodeGen(&builder, resval, alignedscale);
|
|
tmpval = DatumGetBINumericCodeGen(&builder, tmpval);
|
|
tmpval = builder.CreatePtrToInt(tmpval, int64Type);
|
|
tmpval = WrapaddVariableCodeGen(&builder, hcxt, tmpval);
|
|
|
|
builder.CreateAlignedStore(tmpval, cellval, 8);
|
|
builder.CreateAlignedStore(int8_0, cellflag, 1);
|
|
|
|
/* turn to next basicblock or end this */
|
|
if (i == numaggs - 1)
|
|
builder.CreateBr(for_inc);
|
|
else
|
|
builder.CreateBr(agg_bb[i + 1]);
|
|
|
|
/* cell not be null, do aggregation */
|
|
builder.SetInsertPoint(agg_else);
|
|
llvm::Value* real_cellval = builder.CreateAlignedLoad(cellval, 8, "cell_val");
|
|
|
|
/* first make sure the value in cell is BI64 format */
|
|
Vals4[0] = int64_0;
|
|
Vals4[1] = int32_1;
|
|
Vals4[2] = int32_0;
|
|
Vals4[3] = int32_0;
|
|
llvm::Value* cellarg = builder.CreateIntToPtr(real_cellval, numericPtrType);
|
|
tmpval = builder.CreateInBoundsGEP(cellarg, Vals4);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 2, "cellheader");
|
|
llvm::Value* biflag = builder.CreateAnd(tmpval, val_mask);
|
|
llvm::Value* isbi64 = builder.CreateICmpEQ(biflag, val_binum64);
|
|
builder.CreateCondBr(isbi64, expr_bisum_bb, bioverflow_bb);
|
|
|
|
/*
|
|
* do aggregation directly only when both expr value
|
|
* and cell value is bi64.
|
|
*/
|
|
builder.SetInsertPoint(expr_bisum_bb);
|
|
llvm::Value* cell_ptr = builder.CreateAdd(real_cellval, int64_6);
|
|
cell_ptr = builder.CreateIntToPtr(cell_ptr, int64PtrType);
|
|
llvm::Value* mid_cell_val = builder.CreateAlignedLoad(cell_ptr, 8);
|
|
|
|
/* check overflow */
|
|
llvm::Type* Intrinsic_Tys[] = {int64Type};
|
|
llvm::Function* func_sadd_overflow =
|
|
llvm::Intrinsic::getDeclaration(mod, llvm::Intrinsic::sadd_with_overflow, Intrinsic_Tys);
|
|
if (func_sadd_overflow == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_LOAD_INTRINSIC_FUNCTION_FAILED),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Cannot get the llvm::Intrinsic::sadd_with_overflow function!\n")));
|
|
}
|
|
llvm::Value* aggres = builder.CreateCall(func_sadd_overflow, {resval, mid_cell_val});
|
|
llvm::Value* oflag = builder.CreateExtractValue(aggres, 1);
|
|
builder.CreateCondBr(oflag, bioverflow_bb, normal_bb);
|
|
|
|
builder.SetInsertPoint(bioverflow_bb);
|
|
exprscale = GetAlignedScale(estate->expr);
|
|
llvm::Value* ascale = llvmCodeGen->getIntConstant(CHAROID, exprscale);
|
|
llvm::Value* bioverres = WrapmakeNumeric64CodeGen(&builder, resval, ascale);
|
|
builder.CreateBr(numsum_bb);
|
|
|
|
builder.SetInsertPoint(numsum_bb);
|
|
llvm::PHINode* numres = builder.CreatePHI(int64Type, 2);
|
|
numres->addIncoming(result, flag_then[i]);
|
|
numres->addIncoming(bioverres, bioverflow_bb);
|
|
llvm::Value* evalval = (llvm::Value*)numres;
|
|
|
|
llvm::Function* func_vnumericsum = llvmCodeGen->module()->getFunction("Jitted_numericsum");
|
|
if (NULL == func_vnumericsum) {
|
|
func_vnumericsum = numeric_sum_codegen(aggref);
|
|
}
|
|
builder.CreateCall(func_vnumericsum, {cell, hcxt, aggIdxList[i], evalval});
|
|
builder.CreateBr(agg_end);
|
|
|
|
/* if there is no overflow, extract result directly */
|
|
builder.SetInsertPoint(normal_bb);
|
|
llvm::Value* sumval = builder.CreateExtractValue(aggres, 0);
|
|
builder.CreateAlignedStore(sumval, cell_ptr, 8);
|
|
builder.CreateBr(agg_end);
|
|
|
|
builder.SetInsertPoint(agg_end);
|
|
} else {
|
|
llvm::Function* func_vnumericsum = llvmCodeGen->module()->getFunction("Jitted_numericsum");
|
|
if (NULL == func_vnumericsum) {
|
|
func_vnumericsum = numeric_sum_codegen(aggref);
|
|
}
|
|
builder.CreateCall(func_vnumericsum, {cell, hcxt, aggIdxList[i], result});
|
|
}
|
|
} break;
|
|
case NUMERICAVGFUNCOID: {
|
|
/*
|
|
* If aggref can be evaluated in fast path and just be
|
|
* simple vars, use the result from batch.
|
|
*/
|
|
if (fast_aggref && (numSimpleVars > 0)) {
|
|
DEFINE_BLOCK(agg_then, jitted_batchagg);
|
|
DEFINE_BLOCK(agg_else, jitted_batchagg);
|
|
DEFINE_BLOCK(agg_end, jitted_batchagg);
|
|
DEFINE_BLOCK(normal_bb, jitted_batchagg);
|
|
DEFINE_BLOCK(bisum_bblock, jitted_batchagg);
|
|
DEFINE_BLOCK(numsum_bblock, jitted_batchagg);
|
|
|
|
/* get the hash val : hashCell->m_val[aggidx].val */
|
|
Vals4[0] = int64_0;
|
|
Vals4[1] = int32_pos_hcell_mval;
|
|
Vals4[2] = aggIdxList[i];
|
|
Vals4[3] = int32_0;
|
|
llvm::Value* cellval = builder.CreateInBoundsGEP(cell, Vals4);
|
|
|
|
/* get the count of hash val : hashCell->m_val[aggidx + 1].val */
|
|
Vals4[2] = builder.CreateAdd(aggIdxList[i], int64_1, "val_plus");
|
|
llvm::Value* cellval2 = builder.CreateInBoundsGEP(cell, Vals4);
|
|
|
|
/* get the flag of the hash cell and check if it is NULL */
|
|
Vals4[2] = aggIdxList[i];
|
|
Vals4[3] = int32_1;
|
|
llvm::Value* cellflag = builder.CreateInBoundsGEP(cell, Vals4);
|
|
|
|
/* get the flag of cell->m_val[idx + 1].flag */
|
|
Vals4[2] = builder.CreateAdd(aggIdxList[i], int64_1, "flag_plus");
|
|
llvm::Value* cellflag2 = builder.CreateInBoundsGEP(cell, Vals4);
|
|
|
|
/* Now load the cell flag and check it */
|
|
tmpval = builder.CreateAlignedLoad(cellflag, 1, "cellFlag");
|
|
tmpval = builder.CreateAnd(tmpval, int8_1);
|
|
tmpval = builder.CreateICmpEQ(tmpval, int8_0);
|
|
builder.CreateCondBr(tmpval, agg_else, agg_then);
|
|
|
|
/* cell be null, add Variable */
|
|
builder.SetInsertPoint(agg_then);
|
|
/* do leftarg = DatumGetBINumeric(pVal[i]) */
|
|
tmpval = DatumGetBINumericCodeGen(&builder, result);
|
|
tmpval = builder.CreatePtrToInt(tmpval, int64Type);
|
|
/* corresponding to addVariable(context, NumericGetDatum(leftarg)) */
|
|
tmpval = WrapaddVariableCodeGen(&builder, hcxt, tmpval);
|
|
builder.CreateAlignedStore(tmpval, cellval, 8);
|
|
|
|
/* count set to be one */
|
|
builder.CreateAlignedStore(int64_1, cellval2, 8);
|
|
|
|
/* set cell flag */
|
|
builder.CreateAlignedStore(int8_0, cellflag, 1);
|
|
builder.CreateAlignedStore(int8_0, cellflag2, 1);
|
|
|
|
/* turn to next basicblock or end this */
|
|
if (i == numaggs - 1)
|
|
builder.CreateBr(for_inc);
|
|
else
|
|
builder.CreateBr(agg_bb[i + 1]);
|
|
|
|
/* cell be not null, do aggregation */
|
|
builder.SetInsertPoint(agg_else);
|
|
/*
|
|
* When fast_aggref is true and numSimpleVars is greater than zero,
|
|
* the expr is numeric type var. Convert this numeric type data to
|
|
* SiNumeric data to get the value.
|
|
*/
|
|
llvm::Value* bires = DatumGetBINumericCodeGen(&builder, result);
|
|
|
|
/* extract the header of result to check if it is BINumeric */
|
|
Vals4[0] = int64_0;
|
|
Vals4[1] = int32_1;
|
|
Vals4[2] = int32_0;
|
|
Vals4[3] = int32_0;
|
|
tmpval = builder.CreateInBoundsGEP(bires, Vals4);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 2, "biheader");
|
|
llvm::Value* rflag = builder.CreateAnd(tmpval, val_mask);
|
|
|
|
/* extract the header of hashcell to check if it is BINumeric */
|
|
llvm::Value* real_cellval = builder.CreateAlignedLoad(cellval, 8, "cell_val");
|
|
llvm::Value* cellarg = builder.CreateIntToPtr(real_cellval, numericPtrType);
|
|
tmpval = builder.CreateInBoundsGEP(cellarg, Vals4);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 2, "cellheader");
|
|
llvm::Value* lflag = builder.CreateAnd(tmpval, val_mask);
|
|
|
|
/* check if either of them is not BI64 */
|
|
llvm::Value* oparg1 = builder.CreateICmpEQ(lflag, val_binum64);
|
|
llvm::Value* oparg2 = builder.CreateICmpEQ(rflag, val_binum64);
|
|
llvm::Value* bothbi64 = builder.CreateAnd(oparg1, oparg2);
|
|
|
|
/* use fast path only when both args are bi64 */
|
|
builder.CreateCondBr(bothbi64, bisum_bblock, numsum_bblock);
|
|
|
|
builder.SetInsertPoint(bisum_bblock);
|
|
/* extract the actual data of numeric only when value is not null */
|
|
Vals4[0] = int64_0;
|
|
Vals4[1] = int32_1;
|
|
Vals4[2] = int32_0;
|
|
Vals4[3] = int32_1;
|
|
tmpval = builder.CreateInBoundsGEP(bires, Vals4);
|
|
tmpval = builder.CreateBitCast(tmpval, int64PtrType);
|
|
llvm::Value* resval = builder.CreateAlignedLoad(tmpval, 8, "value");
|
|
|
|
llvm::Value* cell_addr = builder.CreateAdd(real_cellval, int64_6);
|
|
cell_addr = builder.CreateIntToPtr(cell_addr, int64PtrType);
|
|
llvm::Value* mid_cell_val = builder.CreateAlignedLoad(cell_addr, 8);
|
|
|
|
/* check overflow */
|
|
llvm::Type* Intrinsic_Tys[] = {int64Type};
|
|
llvm::Function* func_sadd_overflow =
|
|
llvm::Intrinsic::getDeclaration(mod, llvm::Intrinsic::sadd_with_overflow, Intrinsic_Tys);
|
|
if (func_sadd_overflow == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_LOAD_INTRINSIC_FUNCTION_FAILED),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Cannot get the llvm::Intrinsic::sadd_with_overflow function!\n")));
|
|
}
|
|
llvm::Value* aggres = builder.CreateCall(func_sadd_overflow, {resval, mid_cell_val});
|
|
llvm::Value* oflag = builder.CreateExtractValue(aggres, 1);
|
|
builder.CreateCondBr(oflag, numsum_bblock, normal_bb);
|
|
|
|
builder.SetInsertPoint(numsum_bblock);
|
|
llvm::Function* func_vnumericavg = llvmCodeGen->module()->getFunction("Jitted_numericavg");
|
|
if (NULL == func_vnumericavg) {
|
|
func_vnumericavg = numeric_avg_codegen(aggref);
|
|
}
|
|
builder.CreateCall(func_vnumericavg, {cell, hcxt, aggIdxList[i], result});
|
|
builder.CreateBr(agg_end);
|
|
|
|
builder.SetInsertPoint(normal_bb);
|
|
llvm::Value* sumval = builder.CreateExtractValue(aggres, 0);
|
|
builder.CreateAlignedStore(sumval, cell_addr, 8);
|
|
|
|
/* cell->m_val[idx+1].val++ */
|
|
tmpval = builder.CreateAlignedLoad(cellval2, 8, "count");
|
|
tmpval = builder.CreateAdd(tmpval, int64_1);
|
|
builder.CreateAlignedStore(tmpval, cellval2, 8);
|
|
builder.CreateBr(agg_end);
|
|
|
|
builder.SetInsertPoint(agg_end);
|
|
} else if (fast_aggref) {
|
|
/*
|
|
* If aggref can be evaluated in fast path and be numeric
|
|
* expressions, use the result from fastexpr.
|
|
*/
|
|
Assert(bisum_bb != NULL);
|
|
Assert(numsum_bb != NULL);
|
|
DEFINE_BLOCK(agg_end, jitted_batchagg);
|
|
DEFINE_BLOCK(agg_then, jitted_batchagg);
|
|
DEFINE_BLOCK(agg_else, jitted_batchagg);
|
|
DEFINE_BLOCK(normal_bb, jitted_batchagg);
|
|
DEFINE_BLOCK(expr_bisum_bb, jitted_batchagg);
|
|
DEFINE_BLOCK(bioverflow_bb, jitted_batchagg);
|
|
|
|
builder.CreateBr(numsum_bb);
|
|
|
|
builder.SetInsertPoint(bisum_bb);
|
|
llvm::Value* resval = builder.CreateExtractValue(expres, 1);
|
|
|
|
/* get the hash val : hashCell->m_val[aggidx].val */
|
|
Vals4[0] = int64_0;
|
|
Vals4[1] = int32_pos_hcell_mval;
|
|
Vals4[2] = aggIdxList[i];
|
|
Vals4[3] = int32_0;
|
|
llvm::Value* cellval = builder.CreateInBoundsGEP(cell, Vals4);
|
|
|
|
/* get the count of hash val : hashCell->m_val[aggidx + 1].val */
|
|
Vals4[2] = builder.CreateAdd(aggIdxList[i], int64_1, "val_plus");
|
|
llvm::Value* cellval2 = builder.CreateInBoundsGEP(cell, Vals4);
|
|
;
|
|
|
|
/* get the flag of the hash cell and check if it is NULL */
|
|
Vals4[2] = aggIdxList[i];
|
|
Vals4[3] = int32_1;
|
|
llvm::Value* cellflag = builder.CreateInBoundsGEP(cell, Vals4);
|
|
|
|
/* get the flag of cell->m_val[idx + 1].flag */
|
|
Vals4[2] = builder.CreateAdd(aggIdxList[i], int64_1, "flag_plus");
|
|
llvm::Value* cellflag2 = builder.CreateInBoundsGEP(cell, Vals4);
|
|
|
|
tmpval = builder.CreateAlignedLoad(cellflag, 1, "cellFlag");
|
|
tmpval = builder.CreateAnd(tmpval, int8_1);
|
|
tmpval = builder.CreateICmpEQ(tmpval, int8_0);
|
|
builder.CreateCondBr(tmpval, agg_else, agg_then);
|
|
|
|
/* cell be null, add Variable */
|
|
builder.SetInsertPoint(agg_then);
|
|
exprscale = GetAlignedScale(estate->expr);
|
|
llvm::Value* alignedscale = llvmCodeGen->getIntConstant(CHAROID, exprscale);
|
|
/*
|
|
* should make a new context to record the result : the
|
|
* following code corresponding to:
|
|
* 'leftarg = DatumGetBINumeric(pVal[i]);
|
|
* cell->m_val[idx].val = addVariable(context, NumericGetDatum(leftarg));'.
|
|
*/
|
|
tmpval = WrapmakeNumeric64CodeGen(&builder, resval, alignedscale);
|
|
tmpval = DatumGetBINumericCodeGen(&builder, tmpval);
|
|
tmpval = builder.CreatePtrToInt(tmpval, int64Type);
|
|
tmpval = WrapaddVariableCodeGen(&builder, hcxt, tmpval);
|
|
builder.CreateAlignedStore(tmpval, cellval, 8);
|
|
/* count set to be one */
|
|
builder.CreateAlignedStore(int64_1, cellval2, 8);
|
|
/* set the flag of hashcell */
|
|
builder.CreateAlignedStore(int8_0, cellflag, 1);
|
|
builder.CreateAlignedStore(int8_0, cellflag2, 1);
|
|
|
|
/* turn to next basicblock */
|
|
if (i == numaggs - 1)
|
|
builder.CreateBr(for_inc);
|
|
else
|
|
builder.CreateBr(agg_bb[i + 1]);
|
|
|
|
builder.SetInsertPoint(agg_else);
|
|
llvm::Value* real_cellval = builder.CreateAlignedLoad(cellval, 8, "cell_val");
|
|
|
|
/* first make sure the value in cell is BI64 format */
|
|
Vals4[0] = int64_0;
|
|
Vals4[1] = int32_1;
|
|
Vals4[2] = int32_0;
|
|
Vals4[3] = int32_0;
|
|
llvm::Value* cellarg = builder.CreateIntToPtr(real_cellval, numericPtrType);
|
|
tmpval = builder.CreateInBoundsGEP(cellarg, Vals4);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 2, "cellheader");
|
|
llvm::Value* biflag = builder.CreateAnd(tmpval, val_mask);
|
|
llvm::Value* isbi64 = builder.CreateICmpEQ(biflag, val_binum64);
|
|
builder.CreateCondBr(isbi64, expr_bisum_bb, bioverflow_bb);
|
|
|
|
/*
|
|
* do aggregation directly only when both expr value
|
|
* and cell value is bi64.
|
|
*/
|
|
builder.SetInsertPoint(expr_bisum_bb);
|
|
llvm::Value* cell_ptr = builder.CreateAdd(real_cellval, int64_6);
|
|
cell_ptr = builder.CreateIntToPtr(cell_ptr, int64PtrType);
|
|
llvm::Value* mid_cell_val = builder.CreateAlignedLoad(cell_ptr, 8);
|
|
|
|
/* check overflow */
|
|
llvm::Type* Intrinsic_Tys[] = {int64Type};
|
|
llvm::Function* func_sadd_overflow =
|
|
llvm::Intrinsic::getDeclaration(mod, llvm::Intrinsic::sadd_with_overflow, Intrinsic_Tys);
|
|
if (func_sadd_overflow == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_LOAD_INTRINSIC_FUNCTION_FAILED),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Cannot get the llvm::Intrinsic::sadd_with_overflow function!\n")));
|
|
}
|
|
llvm::Value* aggres = builder.CreateCall(func_sadd_overflow, {resval, mid_cell_val});
|
|
llvm::Value* oflag = builder.CreateExtractValue(aggres, 1);
|
|
builder.CreateCondBr(oflag, bioverflow_bb, normal_bb);
|
|
|
|
/* make numeric64 when meet overflow */
|
|
builder.SetInsertPoint(bioverflow_bb);
|
|
exprscale = GetAlignedScale(estate->expr);
|
|
llvm::Value* ascale = llvmCodeGen->getIntConstant(CHAROID, exprscale);
|
|
llvm::Value* bioverres = WrapmakeNumeric64CodeGen(&builder, resval, ascale);
|
|
builder.CreateBr(numsum_bb);
|
|
|
|
builder.SetInsertPoint(numsum_bb);
|
|
llvm::PHINode* numres = builder.CreatePHI(int64Type, 2);
|
|
numres->addIncoming(result, flag_then[i]);
|
|
numres->addIncoming(bioverres, bioverflow_bb);
|
|
llvm::Value* evalval = (llvm::Value*)numres;
|
|
|
|
llvm::Function* func_vnumericavg = llvmCodeGen->module()->getFunction("Jitted_numericavg");
|
|
if (NULL == func_vnumericavg) {
|
|
func_vnumericavg = numeric_avg_codegen(aggref);
|
|
}
|
|
builder.CreateCall(func_vnumericavg, {cell, hcxt, aggIdxList[i], evalval});
|
|
builder.CreateBr(agg_end);
|
|
|
|
/* if there is no overflow, extract result directly */
|
|
builder.SetInsertPoint(normal_bb);
|
|
llvm::Value* sumval = builder.CreateExtractValue(aggres, 0);
|
|
builder.CreateAlignedStore(sumval, cell_ptr, 8);
|
|
|
|
/* cell->m_val[idx+1].val++ */
|
|
tmpval = builder.CreateAlignedLoad(cellval2, 8, "count");
|
|
tmpval = builder.CreateAdd(tmpval, int64_1);
|
|
builder.CreateAlignedStore(tmpval, cellval2, 8);
|
|
builder.CreateBr(agg_end);
|
|
|
|
builder.SetInsertPoint(agg_end);
|
|
} else {
|
|
llvm::Function* func_vnumericavg = llvmCodeGen->module()->getFunction("Jitted_numericavg");
|
|
if (NULL == func_vnumericavg) {
|
|
func_vnumericavg = numeric_avg_codegen(aggref);
|
|
}
|
|
builder.CreateCall(func_vnumericavg, {cell, hcxt, aggIdxList[i], result});
|
|
}
|
|
} break;
|
|
default:
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Unsupported agg function %u!", aggref->aggfnoid)));
|
|
break;
|
|
}
|
|
|
|
if (i == numaggs - 1)
|
|
builder.CreateBr(for_inc);
|
|
else
|
|
builder.CreateBr(agg_bb[i + 1]);
|
|
|
|
/* if the current flag is null, turn to next agg */
|
|
builder.SetInsertPoint(flag_else[i]);
|
|
if (i == numaggs - 1)
|
|
builder.CreateBr(for_inc);
|
|
else
|
|
builder.CreateBr(agg_bb[i + 1]);
|
|
}
|
|
}
|
|
|
|
/* codegen in the for_inc basic block: compare the loop index with nrows */
|
|
builder.SetInsertPoint(for_inc);
|
|
tmpval = builder.CreateTrunc(idx_next, int32Type);
|
|
tmpval = builder.CreateICmpEQ(tmpval, nValues);
|
|
builder.CreateCondBr(tmpval, for_end, for_body);
|
|
|
|
/* codegen in for_end basic block: just return void */
|
|
builder.SetInsertPoint(for_end);
|
|
(void)VecExprCodeGen::MemCxtSwitToCodeGen(&builder, agg_oldcontext);
|
|
WrapResetEContextCodeGen(&builder, mecontext);
|
|
for (i = 0; i < numaggs; i++) {
|
|
if (econtext[i])
|
|
WrapResetEContextCodeGen(&builder, econtext[i]);
|
|
}
|
|
builder.CreateRetVoid();
|
|
|
|
pfree_ext(aggIdxList);
|
|
pfree_ext(agg_bb);
|
|
pfree_ext(flag_then);
|
|
pfree_ext(flag_else);
|
|
pfree_ext(econtext);
|
|
pfree_ext(batch_vals);
|
|
pfree_ext(batch_flag);
|
|
|
|
llvmCodeGen->FinalizeFunction(jitted_batchagg, node->ss.ps.plan->plan_node_id);
|
|
|
|
return jitted_batchagg;
|
|
}
|
|
|
|
llvm::Function* VecHashAggCodeGen::HashBatchCodeGen(VecAggState* node, int idx, bool rehash)
|
|
{
|
|
GsCodeGen* llvmCodeGen = (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj;
|
|
|
|
/* Find and load the IR file from the installaion directory */
|
|
llvmCodeGen->loadIRFile();
|
|
|
|
/* Extract plan information from node */
|
|
VecAgg* vecagg = (VecAgg*)(node->ss.ps.plan);
|
|
List* tlist = (outerPlan(vecagg))->targetlist;
|
|
AttrNumber* keyIdx = vecagg->grpColIdx;
|
|
|
|
AttrNumber key = keyIdx[idx] - 1;
|
|
TargetEntry* tentry = (TargetEntry*)list_nth(tlist, key);
|
|
int bpchar_len = 0;
|
|
|
|
Assert(IsA(tentry->expr, Var) || IsA(tentry->expr, FuncExpr));
|
|
|
|
/* Hash batch value just according to the return type. */
|
|
Oid rettype = InvalidOid;
|
|
switch (nodeTag(tentry->expr)) {
|
|
case T_Var: {
|
|
Var* var = (Var*)(tentry->expr);
|
|
rettype = var->vartype;
|
|
|
|
if (var->vartype == BPCHAROID)
|
|
bpchar_len = var->vartypmod - VARHDRSZ;
|
|
} break;
|
|
case T_FuncExpr: {
|
|
FuncExpr* funcexpr = (FuncExpr*)(tentry->expr);
|
|
rettype = funcexpr->funcresulttype;
|
|
} break;
|
|
default:
|
|
Assert(0);
|
|
break;
|
|
}
|
|
|
|
/* Get LLVM Context and builder */
|
|
llvm::LLVMContext& context = llvmCodeGen->context();
|
|
GsCodeGen::LlvmBuilder builder(context);
|
|
|
|
/* Define data types and some llvm consts */
|
|
DEFINE_CG_TYPE(int8Type, CHAROID);
|
|
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_CGVAR_INT8(int8_0, 0);
|
|
DEFINE_CGVAR_INT8(int8_1, 1);
|
|
DEFINE_CGVAR_INT32(int32_0, 0);
|
|
DEFINE_CGVAR_INT32(int32_2, 2);
|
|
DEFINE_CGVAR_INT32(int32_4, 4);
|
|
DEFINE_CGVAR_INT64(Datum_0, 0);
|
|
|
|
llvm::Function* jitted_hashbatch = NULL;
|
|
llvm::Value* llvmargs[3];
|
|
llvm::Value* hash_res1 = NULL;
|
|
llvm::Value* hash_res2 = NULL;
|
|
llvm::Value* lt0_hash = NULL;
|
|
llvm::BasicBlock* EQ0_bb = NULL;
|
|
|
|
/* Function definition and input parameters */
|
|
GsCodeGen::FnPrototype fn_prototype(llvmCodeGen, "JittedHashBatch", int32Type);
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("value", int64Type));
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("flag", int8Type));
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("hash_val", int32Type));
|
|
jitted_hashbatch = fn_prototype.generatePrototype(&builder, &llvmargs[0]);
|
|
|
|
llvm::Value* pval = llvmargs[0];
|
|
llvm::Value* flag = llvmargs[1];
|
|
llvm::Value* hash_val = llvmargs[2];
|
|
|
|
llvm::BasicBlock* entry = &jitted_hashbatch->getEntryBlock();
|
|
DEFINE_BLOCK(be_not_null, jitted_hashbatch);
|
|
DEFINE_BLOCK(be_null, jitted_hashbatch);
|
|
DEFINE_BLOCK(end_null, jitted_hashbatch);
|
|
|
|
builder.SetInsertPoint(entry);
|
|
/* check the current value is null or not */
|
|
flag = builder.CreateAnd(flag, int8_1);
|
|
llvm::Value* cmp = builder.CreateICmpEQ(flag, int8_0);
|
|
builder.CreateCondBr(cmp, be_not_null, be_null);
|
|
|
|
/*
|
|
* corresponding to likely(NOT_NULL(flag[j])) branch in hashColT function.
|
|
*/
|
|
builder.SetInsertPoint(be_not_null);
|
|
llvm::Module* mod = llvmCodeGen->module();
|
|
switch (rettype) {
|
|
case INT4OID: {
|
|
hash_res1 = hash_val;
|
|
pval = builder.CreateTrunc(pval, int32Type);
|
|
llvm_crc32_32_32(hash_res1, hash_res1, pval);
|
|
} break;
|
|
case INT8OID:
|
|
case DATEOID:
|
|
case TIMESTAMPOID: {
|
|
hash_res1 = hash_val;
|
|
llvm_crc32_32_64(hash_res1, hash_res1, pval);
|
|
} break;
|
|
case BPCHAROID: {
|
|
int len = bpchar_len;
|
|
llvm::Function* func_evalvar = llvmCodeGen->module()->getFunction("JittedEvalVarlena");
|
|
if (func_evalvar == NULL) {
|
|
func_evalvar = VarlenaCvtCodeGen();
|
|
}
|
|
llvm::Value* res = builder.CreateCall(func_evalvar, pval, "func_evalvar");
|
|
llvm::Value* data1 = builder.CreateExtractValue(res, 1);
|
|
data1 = builder.CreatePtrToInt(data1, int64Type);
|
|
data1 = builder.CreateIntToPtr(data1, int8PtrType);
|
|
|
|
hash_res1 = hash_val;
|
|
if (len >= 8) {
|
|
int k = 0;
|
|
llvm::Value* big_data = builder.CreateBitCast(data1, int64PtrType);
|
|
llvm::Value* kidx = NULL;
|
|
while (len >= 8) {
|
|
kidx = llvmCodeGen->getIntConstant(INT4OID, k);
|
|
pval = builder.CreateInBoundsGEP(big_data, kidx);
|
|
pval = builder.CreateAlignedLoad(pval, 8, "bigdat");
|
|
llvm_crc32_32_64(hash_res1, hash_res1, pval);
|
|
len = len - 8;
|
|
k++;
|
|
}
|
|
kidx = llvmCodeGen->getIntConstant(INT4OID, k * 8);
|
|
data1 = builder.CreateInBoundsGEP(data1, kidx);
|
|
}
|
|
|
|
if (len >= 4) {
|
|
llvm::Value* data = builder.CreateBitCast(data1, int32PtrType);
|
|
pval = builder.CreateInBoundsGEP(data, Datum_0);
|
|
pval = builder.CreateAlignedLoad(pval, 4, "intdat");
|
|
llvm_crc32_32_32(hash_res1, hash_res1, pval);
|
|
data1 = builder.CreateInBoundsGEP(data1, int32_4);
|
|
len = len - 4;
|
|
}
|
|
|
|
if (len >= 2) {
|
|
llvm::Value* short_data = builder.CreateBitCast(data1, int16PtrType);
|
|
pval = builder.CreateInBoundsGEP(short_data, Datum_0);
|
|
pval = builder.CreateAlignedLoad(pval, 2, "shortdat");
|
|
llvm_crc32_32_16(hash_res1, hash_res1, pval);
|
|
data1 = builder.CreateInBoundsGEP(data1, int32_2);
|
|
len = len - 2;
|
|
}
|
|
|
|
if (len == 1) {
|
|
pval = builder.CreateAlignedLoad(data1, 1, "val_char");
|
|
llvm_crc32_32_8(hash_res1, hash_res1, pval);
|
|
}
|
|
} break;
|
|
case VARCHAROID:
|
|
case TEXTOID: {
|
|
/*
|
|
* Different from bpchar type, we should create hash table according
|
|
* to the actual length of varchar or text.
|
|
*/
|
|
llvm::Value* cmpval = NULL;
|
|
llvm::Value* data = NULL;
|
|
llvm::Value* nxt_len = NULL;
|
|
llvm::Value* nxt_pos = NULL;
|
|
llvm::Value* nxt_hash = NULL;
|
|
DEFINE_CGVAR_INT32(int32_8, 8);
|
|
DEFINE_CGVAR_INT32(int32_4, 4);
|
|
DEFINE_CGVAR_INT32(int32_2, 2);
|
|
DEFINE_CGVAR_INT32(int32_1, 1);
|
|
|
|
DEFINE_BLOCK(GE8_bb, jitted_hashbatch);
|
|
DEFINE_BLOCK(end_GE8_bb, jitted_hashbatch);
|
|
DEFINE_BLOCK(LT8_bb, jitted_hashbatch);
|
|
DEFINE_BLOCK(GE4_bb, jitted_hashbatch);
|
|
DEFINE_BLOCK(LT4_bb, jitted_hashbatch);
|
|
DEFINE_BLOCK(GE2_bb, jitted_hashbatch);
|
|
DEFINE_BLOCK(LT2_bb, jitted_hashbatch);
|
|
DEFINE_BLOCK(EQ1_bb, jitted_hashbatch);
|
|
|
|
if (NULL == EQ0_bb) {
|
|
EQ0_bb = llvm::BasicBlock::Create(context, "EQ0_bb", jitted_hashbatch);
|
|
}
|
|
|
|
/* get the initial data and true length */
|
|
llvm::Function* func_evalvar = llvmCodeGen->module()->getFunction("JittedEvalVarlena");
|
|
if (func_evalvar == NULL) {
|
|
func_evalvar = VarlenaCvtCodeGen();
|
|
}
|
|
|
|
llvm::Value* res = builder.CreateCall(func_evalvar, pval, "func_evalvar");
|
|
llvm::Value* vlen = builder.CreateExtractValue(res, 0);
|
|
llvm::Value* vdata = builder.CreateExtractValue(res, 1);
|
|
vdata = builder.CreatePtrToInt(vdata, int64Type);
|
|
vdata = builder.CreateIntToPtr(vdata, int8PtrType);
|
|
/* set initial hash value */
|
|
hash_res1 = hash_val;
|
|
llvm::Value* bighash = builder.CreateZExt(hash_res1, int64Type);
|
|
llvm::Value* bigdata = builder.CreateBitCast(vdata, int64PtrType);
|
|
|
|
/* check if the actual length is great than 9 */
|
|
cmpval = builder.CreateICmpSGE(vlen, int32_8, "if_ge8");
|
|
builder.CreateCondBr(cmpval, GE8_bb, LT8_bb);
|
|
|
|
/* loop over the length until it is less than 8 */
|
|
builder.SetInsertPoint(GE8_bb);
|
|
llvm::PHINode* phi_whl_len = builder.CreatePHI(int32Type, 2);
|
|
llvm::PHINode* phi_whl_pos = builder.CreatePHI(int32Type, 2);
|
|
llvm::PHINode* phi_whl_hash = builder.CreatePHI(int64Type, 2);
|
|
|
|
llvm::Value* whl_len = (llvm::Value*)phi_whl_len;
|
|
nxt_len = builder.CreateSub(whl_len, int32_8);
|
|
phi_whl_len->addIncoming(vlen, be_not_null);
|
|
phi_whl_len->addIncoming(nxt_len, GE8_bb);
|
|
|
|
llvm::Value* whl_pos = (llvm::Value*)phi_whl_pos;
|
|
nxt_pos = builder.CreateAdd(whl_pos, int32_1);
|
|
phi_whl_pos->addIncoming(int32_0, be_not_null);
|
|
phi_whl_pos->addIncoming(nxt_pos, GE8_bb);
|
|
|
|
llvm::Value* whl_hash = (llvm::Value*)phi_whl_hash;
|
|
whl_hash = builder.CreateTrunc(whl_hash, int32Type);
|
|
/* compute hash value */
|
|
llvm::Value* whl_data = builder.CreateInBoundsGEP(bigdata, whl_pos);
|
|
whl_data = builder.CreateAlignedLoad(whl_data, 8, "whl_data");
|
|
llvm_crc32_32_64(nxt_hash, whl_hash, whl_data);
|
|
phi_whl_hash->addIncoming(bighash, be_not_null);
|
|
nxt_hash = builder.CreateZExt(nxt_hash, int64Type);
|
|
phi_whl_hash->addIncoming(nxt_hash, GE8_bb);
|
|
|
|
/* increament pos and minimus the length */
|
|
cmpval = builder.CreateICmpSGE(nxt_len, int32_8);
|
|
builder.CreateCondBr(cmpval, GE8_bb, end_GE8_bb);
|
|
|
|
builder.SetInsertPoint(end_GE8_bb);
|
|
llvm::Value* ge8_len = nxt_len;
|
|
llvm::Value* ge8_hash = builder.CreateTrunc(nxt_hash, int32Type);
|
|
llvm::Value* ge8_data = builder.CreateInBoundsGEP(bigdata, nxt_pos);
|
|
ge8_data = builder.CreateBitCast(ge8_data, int8PtrType);
|
|
builder.CreateBr(LT8_bb);
|
|
|
|
builder.SetInsertPoint(LT8_bb);
|
|
llvm::PHINode* phi_lt8_len = builder.CreatePHI(int32Type, 2);
|
|
phi_lt8_len->addIncoming(vlen, be_not_null);
|
|
phi_lt8_len->addIncoming(ge8_len, end_GE8_bb);
|
|
llvm::Value* lt8_len = (llvm::Value*)phi_lt8_len;
|
|
|
|
llvm::PHINode* phi_lt8_hash = builder.CreatePHI(int32Type, 2);
|
|
phi_lt8_hash->addIncoming(hash_res1, be_not_null);
|
|
phi_lt8_hash->addIncoming(ge8_hash, end_GE8_bb);
|
|
llvm::Value* lt8_hash = (llvm::Value*)phi_lt8_hash;
|
|
|
|
llvm::PHINode* phi_lt8_data = builder.CreatePHI(int8PtrType, 2);
|
|
phi_lt8_data->addIncoming(vdata, be_not_null);
|
|
phi_lt8_data->addIncoming(ge8_data, end_GE8_bb);
|
|
llvm::Value* lt8_data = (llvm::Value*)phi_lt8_data;
|
|
|
|
cmpval = builder.CreateICmpSGE(lt8_len, int32_4);
|
|
builder.CreateCondBr(cmpval, GE4_bb, LT4_bb);
|
|
|
|
/* if the actual length is greater than 4 and less than 8 */
|
|
builder.SetInsertPoint(GE4_bb);
|
|
data = builder.CreateBitCast(lt8_data, int32PtrType);
|
|
data = builder.CreateInBoundsGEP(data, Datum_0);
|
|
data = builder.CreateAlignedLoad(data, 4, "intdat");
|
|
llvm::Value* ge4_hash = NULL;
|
|
llvm_crc32_32_32(ge4_hash, lt8_hash, data);
|
|
llvm::Value* ge4_data = builder.CreateInBoundsGEP(lt8_data, int32_4);
|
|
llvm::Value* ge4_len = builder.CreateSub(lt8_len, int32_4);
|
|
builder.CreateBr(LT4_bb);
|
|
|
|
/* if the actual length is less than 4 */
|
|
builder.SetInsertPoint(LT4_bb);
|
|
llvm::PHINode* phi_lt4_len = builder.CreatePHI(int32Type, 2);
|
|
phi_lt4_len->addIncoming(lt8_len, LT8_bb);
|
|
phi_lt4_len->addIncoming(ge4_len, GE4_bb);
|
|
llvm::Value* lt4_len = (llvm::Value*)phi_lt4_len;
|
|
|
|
llvm::PHINode* phi_lt4_hash = builder.CreatePHI(int32Type, 2);
|
|
phi_lt4_hash->addIncoming(lt8_hash, LT8_bb);
|
|
phi_lt4_hash->addIncoming(ge4_hash, GE4_bb);
|
|
llvm::Value* lt4_hash = (llvm::Value*)phi_lt4_hash;
|
|
|
|
llvm::PHINode* phi_lt4_data = builder.CreatePHI(int8PtrType, 2);
|
|
phi_lt4_data->addIncoming(lt8_data, LT8_bb);
|
|
phi_lt4_data->addIncoming(ge4_data, GE4_bb);
|
|
llvm::Value* lt4_data = (llvm::Value*)phi_lt4_data;
|
|
|
|
cmpval = builder.CreateICmpSGE(lt4_len, int32_2);
|
|
builder.CreateCondBr(cmpval, GE2_bb, LT2_bb);
|
|
|
|
/* if the length is greater than 2 and less than 4 */
|
|
builder.SetInsertPoint(GE2_bb);
|
|
data = builder.CreateBitCast(lt4_data, int16PtrType);
|
|
data = builder.CreateInBoundsGEP(data, Datum_0);
|
|
data = builder.CreateAlignedLoad(data, 2, "shortdat");
|
|
llvm::Value* ge2_hash = NULL;
|
|
llvm_crc32_32_16(ge2_hash, lt4_hash, data);
|
|
llvm::Value* ge2_data = builder.CreateInBoundsGEP(lt4_data, int32_2);
|
|
llvm::Value* ge2_len = builder.CreateSub(lt4_len, int32_2);
|
|
builder.CreateBr(LT2_bb);
|
|
|
|
/* if the length is less than 2 */
|
|
builder.SetInsertPoint(LT2_bb);
|
|
llvm::PHINode* phi_lt2_len = builder.CreatePHI(int32Type, 2);
|
|
phi_lt2_len->addIncoming(lt4_len, LT4_bb);
|
|
phi_lt2_len->addIncoming(ge2_len, GE2_bb);
|
|
llvm::Value* lt2_len = (llvm::Value*)phi_lt2_len;
|
|
|
|
llvm::PHINode* phi_lt2_hash = builder.CreatePHI(int32Type, 2);
|
|
phi_lt2_hash->addIncoming(lt4_hash, LT4_bb);
|
|
phi_lt2_hash->addIncoming(ge2_hash, GE2_bb);
|
|
llvm::Value* lt2_hash = (llvm::Value*)phi_lt2_hash;
|
|
|
|
llvm::PHINode* phi_lt2_data = builder.CreatePHI(int8PtrType, 2);
|
|
phi_lt2_data->addIncoming(lt4_data, LT4_bb);
|
|
phi_lt2_data->addIncoming(ge2_data, GE2_bb);
|
|
llvm::Value* lt2_data = (llvm::Value*)phi_lt2_data;
|
|
|
|
cmpval = builder.CreateICmpEQ(lt2_len, int32_1);
|
|
builder.CreateCondBr(cmpval, EQ1_bb, EQ0_bb);
|
|
|
|
builder.SetInsertPoint(EQ1_bb);
|
|
data = builder.CreateAlignedLoad(lt2_data, 1, "val_char");
|
|
llvm::Value* lt1_hash = NULL;
|
|
llvm_crc32_32_8(lt1_hash, lt2_hash, data);
|
|
builder.CreateBr(EQ0_bb);
|
|
|
|
builder.SetInsertPoint(EQ0_bb);
|
|
llvm::PHINode* phi_lt0_hash = builder.CreatePHI(int32Type, 2);
|
|
phi_lt0_hash->addIncoming(lt2_hash, LT2_bb);
|
|
phi_lt0_hash->addIncoming(lt1_hash, EQ1_bb);
|
|
lt0_hash = (llvm::Value*)phi_lt0_hash;
|
|
} break;
|
|
default:
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Type %u is not supported yet in hashBatch", rettype)));
|
|
break;
|
|
}
|
|
builder.CreateBr(end_null);
|
|
|
|
/*
|
|
* corresponding to the else branch:
|
|
* if (!rehash) { hashRes[j] = 0 }
|
|
*/
|
|
builder.SetInsertPoint(be_null);
|
|
if (!rehash)
|
|
hash_res2 = int32_0;
|
|
else
|
|
hash_res2 = hash_val;
|
|
builder.CreateBr(end_null);
|
|
|
|
builder.SetInsertPoint(end_null);
|
|
if (rettype != VARCHAROID && rettype != TEXTOID) {
|
|
llvm::PHINode* Phi_hash = builder.CreatePHI(int32Type, 2);
|
|
Phi_hash->addIncoming(hash_res1, be_not_null);
|
|
Phi_hash->addIncoming(hash_res2, be_null);
|
|
builder.CreateRet(Phi_hash);
|
|
} else {
|
|
llvm::PHINode* Phi_hash = builder.CreatePHI(int32Type, 2);
|
|
Phi_hash->addIncoming(lt0_hash, EQ0_bb);
|
|
Phi_hash->addIncoming(hash_res2, be_null);
|
|
builder.CreateRet(Phi_hash);
|
|
}
|
|
|
|
llvmCodeGen->FinalizeFunction(jitted_hashbatch, node->ss.ps.plan->plan_node_id);
|
|
|
|
return jitted_hashbatch;
|
|
}
|
|
|
|
llvm::Function* VecHashAggCodeGen::MatchOneKeyCodeGen(VecAggState* node, int idx)
|
|
{
|
|
GsCodeGen* llvmCodeGen = (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj;
|
|
|
|
/* Find and load the IR file from the installaion directory */
|
|
llvmCodeGen->loadIRFile();
|
|
|
|
/* Extract plan information from node */
|
|
VecAgg* vecagg = (VecAgg*)(node->ss.ps.plan);
|
|
List* tlist = (outerPlan(vecagg))->targetlist;
|
|
AttrNumber* keyIdx = vecagg->grpColIdx;
|
|
|
|
AttrNumber key = keyIdx[idx] - 1;
|
|
TargetEntry* tentry = (TargetEntry*)list_nth(tlist, key);
|
|
|
|
Assert(IsA(tentry->expr, Var) || IsA(tentry->expr, FuncExpr));
|
|
Oid rettype = InvalidOid;
|
|
int bpchar_len = 0;
|
|
switch (nodeTag(tentry->expr)) {
|
|
case T_Var: {
|
|
Var* var = (Var*)(tentry->expr);
|
|
rettype = var->vartype;
|
|
|
|
if (var->vartype == BPCHAROID)
|
|
bpchar_len = var->vartypmod - VARHDRSZ;
|
|
} break;
|
|
case T_FuncExpr: {
|
|
FuncExpr* funcexpr = (FuncExpr*)(tentry->expr);
|
|
rettype = funcexpr->funcresulttype;
|
|
} break;
|
|
default:
|
|
Assert(0);
|
|
break;
|
|
}
|
|
Var* var = (Var*)(tentry->expr);
|
|
|
|
/* Get LLVM Context and builder */
|
|
llvm::LLVMContext& context = llvmCodeGen->context();
|
|
GsCodeGen::LlvmBuilder builder(context);
|
|
|
|
/* Define data types and some llvm consts */
|
|
DEFINE_CG_TYPE(int8Type, CHAROID);
|
|
DEFINE_CG_TYPE(int32Type, INT4OID);
|
|
DEFINE_CG_TYPE(int64Type, INT8OID);
|
|
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(Datum_0, 0);
|
|
DEFINE_CGVAR_INT64(Datum_1, 1);
|
|
DEFINE_CGVAR_INT32(int32_pos_hcell_mval, pos_hcell_mval);
|
|
|
|
llvm::Function* jitted_matchonekey = NULL;
|
|
llvm::Value* tmpval = NULL;
|
|
llvm::Value* llvmargs[4];
|
|
llvm::Value* Vals4[4] = {Datum_0, int32_0, int32_0, int32_0};
|
|
|
|
GsCodeGen::FnPrototype fn_prototype(llvmCodeGen, "JittedOneMatchKey", int64Type);
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("value", int64Type));
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("flag", int8Type));
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("hashcell", hashCellPtrType));
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("keycell_idx", int32Type));
|
|
jitted_matchonekey = fn_prototype.generatePrototype(&builder, &llvmargs[0]);
|
|
|
|
llvm::Value* pval = llvmargs[0];
|
|
llvm::Value* pflg = llvmargs[1];
|
|
llvm::Value* hashcell = llvmargs[2];
|
|
llvm::Value* keyidxincell = llvmargs[3];
|
|
|
|
DEFINE_BLOCK(bnot_null, jitted_matchonekey);
|
|
DEFINE_BLOCK(may_null, jitted_matchonekey);
|
|
DEFINE_BLOCK(both_null, jitted_matchonekey);
|
|
DEFINE_BLOCK(one_null, jitted_matchonekey);
|
|
DEFINE_BLOCK(check_end, jitted_matchonekey);
|
|
|
|
keyidxincell = builder.CreateSExt(keyidxincell, int64Type);
|
|
/* hashCell.m_val[keyidxincell].val / flag */
|
|
Vals4[0] = Datum_0;
|
|
Vals4[1] = int32_pos_hcell_mval;
|
|
Vals4[2] = keyidxincell;
|
|
Vals4[3] = int32_0;
|
|
tmpval = builder.CreateInBoundsGEP(hashcell, Vals4);
|
|
llvm::Value* keyval = builder.CreateAlignedLoad(tmpval, 8, "keyincell");
|
|
|
|
Vals4[3] = int32_1;
|
|
tmpval = builder.CreateInBoundsGEP(hashcell, Vals4);
|
|
llvm::Value* keyflg = builder.CreateAlignedLoad(tmpval, 1, "flagincell");
|
|
|
|
llvm::Value* cmp1 = NULL;
|
|
llvm::Value* cmp2 = NULL;
|
|
llvm::Value* cmpand = NULL;
|
|
llvm::Value* cmpor = NULL;
|
|
llvm::Value* res1 = NULL;
|
|
llvm::Value* res2 = NULL;
|
|
llvm::Value* res3 = NULL;
|
|
|
|
pflg = builder.CreateAnd(pflg, int8_1);
|
|
cmp1 = builder.CreateICmpEQ(pflg, int8_0);
|
|
keyflg = builder.CreateAnd(keyflg, int8_1);
|
|
cmp2 = builder.CreateICmpEQ(keyflg, int8_0);
|
|
cmpand = builder.CreateAnd(cmp1, cmp2);
|
|
cmpor = builder.CreateOr(cmp1, cmp2);
|
|
|
|
builder.CreateCondBr(cmpand, bnot_null, may_null);
|
|
builder.SetInsertPoint(bnot_null);
|
|
switch (rettype) {
|
|
case INT4OID: {
|
|
/*
|
|
* should first truncate the keyval to make sure we compare the
|
|
* right value
|
|
*/
|
|
keyval = builder.CreateTrunc(keyval, int32Type);
|
|
pval = builder.CreateTrunc(pval, int32Type);
|
|
res1 = builder.CreateICmpEQ(keyval, pval);
|
|
res1 = builder.CreateZExt(res1, int64Type);
|
|
} break;
|
|
case INT8OID:
|
|
case DATEOID:
|
|
case TIMESTAMPOID: {
|
|
res1 = builder.CreateICmpEQ(keyval, pval);
|
|
res1 = builder.CreateZExt(res1, int64Type);
|
|
} break;
|
|
case BPCHAROID: {
|
|
/* first extract the char* value from Datum */
|
|
llvm::Function* func_evalvar = llvmCodeGen->module()->getFunction("JittedEvalVarlena");
|
|
if (func_evalvar == NULL) {
|
|
func_evalvar = dorado::VarlenaCvtCodeGen();
|
|
}
|
|
|
|
llvm::Value* vecval = builder.CreateCall(func_evalvar, pval, "evalbatchvar");
|
|
llvm::Value* vecdata = builder.CreateExtractValue(vecval, 1);
|
|
|
|
llvm::Value* cellval = builder.CreateCall(func_evalvar, keyval, "evalcellvar");
|
|
llvm::Value* celldata = builder.CreateExtractValue(cellval, 1);
|
|
|
|
/* call simple memcmp IR function */
|
|
llvm::Function* evalmemcmp = llvmCodeGen->module()->getFunction("LLVMIRmemcmp");
|
|
if (evalmemcmp == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_LOAD_IR_FUNCTION_FAILED),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Failed on getting IR function : LLVMIRmemcmp!\n")));
|
|
}
|
|
|
|
llvm::Value* lendat = llvmCodeGen->getIntConstant(INT4OID, bpchar_len);
|
|
res1 = builder.CreateCall(evalmemcmp, {celldata, vecdata, lendat}, "memcmp");
|
|
} break;
|
|
case TEXTOID:
|
|
case VARCHAROID: {
|
|
llvm::Function* func_evalvar = llvmCodeGen->module()->getFunction("JittedEvalVarlena");
|
|
if (func_evalvar == NULL) {
|
|
func_evalvar = VarlenaCvtCodeGen();
|
|
}
|
|
|
|
llvm::Value* vecval = builder.CreateCall(func_evalvar, pval, "evalbatchvar");
|
|
llvm::Value* veclen = builder.CreateExtractValue(vecval, 0);
|
|
llvm::Value* vecdata = builder.CreateExtractValue(vecval, 1);
|
|
|
|
llvm::Value* cellval = builder.CreateCall(func_evalvar, keyval, "evalcellvar");
|
|
llvm::Value* celllen = builder.CreateExtractValue(cellval, 0);
|
|
llvm::Value* celldata = builder.CreateExtractValue(cellval, 1);
|
|
|
|
llvm::Function* func_texteq_cc = llvmCodeGen->module()->getFunction("LLVMIRtexteq");
|
|
if (func_texteq_cc == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_LOAD_IR_FUNCTION_FAILED),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Failed on getting IR function : LLVMIRtexteq!\n")));
|
|
}
|
|
res1 = builder.CreateCall(func_texteq_cc, {veclen, vecdata, celllen, celldata}, "texteq");
|
|
} break;
|
|
default:
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmodule(MOD_LLVM),
|
|
(errmsg("Type %u is not supported yet in match_key", var->vartype))));
|
|
break;
|
|
}
|
|
builder.CreateBr(check_end);
|
|
|
|
builder.SetInsertPoint(may_null);
|
|
builder.CreateCondBr(cmpor, one_null, both_null);
|
|
/* both null is equal */
|
|
builder.SetInsertPoint(both_null);
|
|
res2 = Datum_1;
|
|
builder.CreateBr(check_end);
|
|
|
|
/* null not equal to non-null */
|
|
builder.SetInsertPoint(one_null);
|
|
res3 = Datum_0;
|
|
builder.CreateBr(check_end);
|
|
|
|
builder.SetInsertPoint(check_end);
|
|
llvm::PHINode* Phi_ret = builder.CreatePHI(int64Type, 3);
|
|
Phi_ret->addIncoming(res1, bnot_null);
|
|
Phi_ret->addIncoming(res2, both_null);
|
|
Phi_ret->addIncoming(res3, one_null);
|
|
builder.CreateRet(Phi_ret);
|
|
|
|
llvmCodeGen->FinalizeFunction(jitted_matchonekey, node->ss.ps.plan->plan_node_id);
|
|
|
|
return jitted_matchonekey;
|
|
}
|
|
|
|
llvm::Value* VecHashAggCodeGen::EvalFastExprInBatchAgg(ExprState* state, GsCodeGen::LlvmBuilder builder,
|
|
llvm::Function* jitted_func, llvm::BasicBlock** bb_null, llvm::BasicBlock** bb_last,
|
|
llvm::BasicBlock** bb_outofbound, llvm::Value* econtext, llvm::Value* argVector, llvm::Value* phi_idx)
|
|
{
|
|
dorado::GsCodeGen* llvmCodeGen = (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj;
|
|
llvm::Module* mod = llvmCodeGen->module();
|
|
llvm::LLVMContext& context = llvmCodeGen->context();
|
|
|
|
DEFINE_CG_TYPE(int16Type, INT2OID);
|
|
DEFINE_CG_TYPE(int64Type, INT8OID);
|
|
DEFINE_CG_PTRTYPE(int64PtrType, INT8OID);
|
|
|
|
DEFINE_CGVAR_INT8(int8_0, 0);
|
|
DEFINE_CGVAR_INT8(int8_1, 1);
|
|
DEFINE_CGVAR_INT16(int16_0, 0);
|
|
DEFINE_CGVAR_INT16(val_numeric64, NUMERIC_64);
|
|
DEFINE_CGVAR_INT16(val_bimask, NUMERIC_BI_MASK);
|
|
DEFINE_CGVAR_INT16(val_scalemask, NUMERIC_BI_SCALEMASK);
|
|
DEFINE_CGVAR_INT32(int32_pos_scalvec_vals, pos_scalvec_vals);
|
|
DEFINE_CGVAR_INT32(int32_pos_scalvec_flag, pos_scalvec_flag);
|
|
DEFINE_CGVAR_INT32(int32_0, 0);
|
|
DEFINE_CGVAR_INT32(int32_1, 1);
|
|
DEFINE_CGVAR_INT64(int64_0, 0);
|
|
|
|
llvm::Value* tmpval = NULL;
|
|
llvm::Value* cmpval = NULL;
|
|
llvm::Value* phi_val = NULL;
|
|
llvm::Value* result = NULL;
|
|
llvm::Value* multi_bound = NULL;
|
|
llvm::Value* left_scaled1 = NULL;
|
|
llvm::Value* left_scaled2 = NULL;
|
|
llvm::Value* right_scaled1 = NULL;
|
|
llvm::Value* right_scaled2 = NULL;
|
|
llvm::Value* resscale1 = NULL;
|
|
llvm::Value* resscale2 = NULL;
|
|
llvm::Value* res1 = NULL;
|
|
llvm::Value* lval = NULL;
|
|
llvm::Value* rval = NULL;
|
|
llvm::PHINode* left_scaled = NULL;
|
|
llvm::PHINode* right_scaled = NULL;
|
|
llvm::PHINode* resscale = NULL;
|
|
|
|
llvm::Type* Intrinsic_Tys[] = {int64Type};
|
|
llvm::Value* Vals[2] = {int64_0, int32_0};
|
|
llvm::Value* Vals4[4] = {int64_0, int32_0, int32_0, int32_0};
|
|
|
|
/* create LLVM value with {uint16, int64} format */
|
|
llvm::Type* Elements[] = {int16Type, int64Type};
|
|
llvm::Type* SiNumeric64Type = llvm::StructType::create(context, Elements, "SiNumeric64");
|
|
|
|
if (*bb_last)
|
|
builder.SetInsertPoint(*bb_last);
|
|
|
|
switch (nodeTag(state->expr)) {
|
|
case T_TargetEntry: {
|
|
/* TargetEntry information is stored in GenericExprState */
|
|
GenericExprState* gstate = (GenericExprState*)state;
|
|
ExprState* estate = gstate->arg;
|
|
|
|
result = EvalFastExprInBatchAgg(
|
|
estate, builder, jitted_func, bb_null, bb_last, bb_outofbound, econtext, argVector, phi_idx);
|
|
} break;
|
|
case T_OpExpr: {
|
|
/* define the basic block needed here */
|
|
DEFINE_BLOCK(both_bi64, jitted_func);
|
|
DEFINE_BLOCK(not_both_bi64, jitted_func);
|
|
|
|
/* check if have special opexpr with one arg */
|
|
OpExpr* opexpr = (OpExpr*)state->expr;
|
|
FuncExprState* fstate = (FuncExprState*)state;
|
|
List* op_args = fstate->args;
|
|
|
|
ExprState* lestate = (ExprState*)linitial(op_args);
|
|
ExprState* restate = (ExprState*)lsecond(op_args);
|
|
|
|
lval = EvalFastExprInBatchAgg(
|
|
lestate, builder, jitted_func, bb_null, bb_last, bb_outofbound, econtext, argVector, phi_idx);
|
|
rval = EvalFastExprInBatchAgg(
|
|
restate, builder, jitted_func, bb_null, bb_last, bb_outofbound, econtext, argVector, phi_idx);
|
|
|
|
if (*bb_last)
|
|
builder.SetInsertPoint(*bb_last);
|
|
|
|
/* Extract the header value to get the mask and scale */
|
|
llvm::Value* lheader = builder.CreateExtractValue(lval, 0);
|
|
llvm::Value* rheader = builder.CreateExtractValue(rval, 0);
|
|
|
|
llvm::Value* lvalscale = builder.CreateAnd(lheader, val_scalemask);
|
|
llvm::Value* rvalscale = builder.CreateAnd(rheader, val_scalemask);
|
|
|
|
llvm::Value* lvalmask = builder.CreateAnd(lheader, val_bimask);
|
|
llvm::Value* lvalbi64 = builder.CreateICmpEQ(lvalmask, val_numeric64);
|
|
llvm::Value* rvalmask = builder.CreateAnd(rheader, val_bimask);
|
|
llvm::Value* rvalbi64 = builder.CreateICmpEQ(rvalmask, val_numeric64);
|
|
|
|
llvm::Value* bebi64 = builder.CreateAnd(lvalbi64, rvalbi64);
|
|
builder.CreateCondBr(bebi64, both_bi64, not_both_bi64);
|
|
|
|
builder.SetInsertPoint(both_bi64);
|
|
llvm::Value* leftval = builder.CreateExtractValue(lval, 1);
|
|
llvm::Value* rightval = builder.CreateExtractValue(rval, 1);
|
|
|
|
/*
|
|
* adjust the scale and value, first define outofbound block
|
|
*/
|
|
if (opexpr->opno == NUMERICADDOID || opexpr->opno == NUMERICSUBOID) {
|
|
/* Define basic block that needed only by NUMERICADDOID and NUMERICSUBOID*/
|
|
DEFINE_BLOCK(delta_large, jitted_func);
|
|
DEFINE_BLOCK(delta_small, jitted_func);
|
|
DEFINE_BLOCK(adjust_true, jitted_func);
|
|
DEFINE_BLOCK(adjust_overflow_bb, jitted_func);
|
|
DEFINE_BLOCK(adjust_normal_bb, jitted_func);
|
|
|
|
llvm::Value* delta_scale = builder.CreateSub(lvalscale, rvalscale, "delta_scale");
|
|
/* check delta_scale >= 0 or not */
|
|
cmpval = builder.CreateICmpSGE(delta_scale, int16_0, "cmp_delta_scale");
|
|
builder.CreateCondBr(cmpval, delta_large, delta_small);
|
|
|
|
/* corresponding to delta_scale >= 0 */
|
|
builder.SetInsertPoint(delta_large);
|
|
/* tmpval = y < 0 ? y : -y */
|
|
tmpval = builder.CreateSub(int64_0, rightval);
|
|
cmpval = builder.CreateICmpSLT(rightval, int64_0, "negative_cmp");
|
|
phi_val = builder.CreateSelect(cmpval, rightval, tmpval);
|
|
|
|
left_scaled1 = leftval;
|
|
llvm::Value* mulscale = ScaleMultiCodeGen(&builder, delta_scale);
|
|
/* corresponding to y_scaled = y * ScaleMultipler[delta_scale] */
|
|
right_scaled1 = builder.CreateMul(rightval, mulscale);
|
|
|
|
/* result_scale = x_scale */
|
|
resscale1 = lvalscale;
|
|
multi_bound = GetInt64MulOutofBoundCodeGen(&builder, delta_scale);
|
|
cmpval = builder.CreateICmpSGE(phi_val, multi_bound, "bound_check");
|
|
builder.CreateCondBr(cmpval, adjust_true, *bb_outofbound);
|
|
|
|
builder.SetInsertPoint(delta_small);
|
|
/* tmpval = x < 0 ? x : -x */
|
|
tmpval = builder.CreateSub(int64_0, leftval);
|
|
cmpval = builder.CreateICmpSLT(leftval, int64_0, "negative_cmp");
|
|
phi_val = builder.CreateSelect(cmpval, leftval, tmpval);
|
|
|
|
/* corresponding to x_scaled = x * ScaleMultipler[-delta_scale] */
|
|
llvm::Value* mdelta_scale = builder.CreateSub(int16_0, delta_scale);
|
|
llvm::Value* mmulscale = ScaleMultiCodeGen(&builder, mdelta_scale);
|
|
left_scaled2 = builder.CreateMul(leftval, mmulscale);
|
|
right_scaled2 = rightval;
|
|
|
|
/* result_scale = y_scale */
|
|
resscale2 = rvalscale;
|
|
multi_bound = GetInt64MulOutofBoundCodeGen(&builder, mdelta_scale);
|
|
cmpval = builder.CreateICmpSGE(phi_val, multi_bound, "bound_check");
|
|
builder.CreateCondBr(cmpval, adjust_true, *bb_outofbound);
|
|
|
|
builder.SetInsertPoint(adjust_true);
|
|
left_scaled = builder.CreatePHI(int64Type, 2);
|
|
right_scaled = builder.CreatePHI(int64Type, 2);
|
|
resscale = builder.CreatePHI(int16Type, 2);
|
|
|
|
left_scaled->addIncoming(left_scaled1, delta_large);
|
|
left_scaled->addIncoming(left_scaled2, delta_small);
|
|
|
|
right_scaled->addIncoming(right_scaled1, delta_large);
|
|
right_scaled->addIncoming(right_scaled2, delta_small);
|
|
|
|
resscale->addIncoming(resscale1, delta_large);
|
|
resscale->addIncoming(resscale2, delta_small);
|
|
|
|
switch (opexpr->opno) {
|
|
case NUMERICADDOID: {
|
|
/* do bi64 add bi64, need to check if there is any overflow */
|
|
llvm::Function* func_sadd_overflow =
|
|
llvm::Intrinsic::getDeclaration(mod, llvm::Intrinsic::sadd_with_overflow, Intrinsic_Tys);
|
|
if (func_sadd_overflow == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_LOAD_INTRINSIC_FUNCTION_FAILED),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Cannot get the llvm::Intrinsic::sadd_with_overflow function!\n")));
|
|
}
|
|
llvm::Value* res = builder.CreateCall(func_sadd_overflow, {left_scaled, right_scaled}, "sadd");
|
|
llvm::Value* overflow_flag = builder.CreateExtractValue(res, 1);
|
|
|
|
builder.CreateCondBr(overflow_flag, adjust_overflow_bb, adjust_normal_bb);
|
|
|
|
builder.SetInsertPoint(adjust_normal_bb);
|
|
res1 = builder.CreateExtractValue(res, 0);
|
|
|
|
/* create the result of current step */
|
|
result = llvm::UndefValue::get(SiNumeric64Type);
|
|
llvm::Value* tmphead = builder.CreateAdd(val_numeric64, resscale);
|
|
result = builder.CreateInsertValue(result, tmphead, 0);
|
|
result = builder.CreateInsertValue(result, res1, 1);
|
|
*bb_last = adjust_normal_bb;
|
|
|
|
/* adjust true without out_of_bound */
|
|
builder.SetInsertPoint(adjust_overflow_bb);
|
|
builder.CreateBr(*bb_outofbound);
|
|
} break;
|
|
case NUMERICSUBOID: {
|
|
/* do bi64 add bi64, need to check if there is any overflow */
|
|
llvm::Function* func_ssub_overflow =
|
|
llvm::Intrinsic::getDeclaration(mod, llvm::Intrinsic::ssub_with_overflow, Intrinsic_Tys);
|
|
if (func_ssub_overflow == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_LOAD_INTRINSIC_FUNCTION_FAILED),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Cannot get the llvm::Intrinsic::sasub_with_overflow function!\n")));
|
|
}
|
|
llvm::Value* res = builder.CreateCall(func_ssub_overflow, {left_scaled, right_scaled}, "sadd");
|
|
llvm::Value* overflow_flag = builder.CreateExtractValue(res, 1);
|
|
builder.CreateCondBr(overflow_flag, adjust_overflow_bb, adjust_normal_bb);
|
|
|
|
builder.SetInsertPoint(adjust_normal_bb);
|
|
/* if not out of bound, return makeNumeric64(result, resScale) */
|
|
res1 = builder.CreateExtractValue(res, 0);
|
|
|
|
/* create the result of current step */
|
|
result = llvm::UndefValue::get(SiNumeric64Type);
|
|
llvm::Value* tmphead = builder.CreateAdd(val_numeric64, resscale);
|
|
result = builder.CreateInsertValue(result, tmphead, 0);
|
|
result = builder.CreateInsertValue(result, res1, 1);
|
|
*bb_last = adjust_normal_bb;
|
|
|
|
/* if overflow, turn to int128 type first */
|
|
builder.SetInsertPoint(adjust_overflow_bb);
|
|
builder.CreateBr(*bb_outofbound);
|
|
} break;
|
|
default:
|
|
Assert(0);
|
|
break;
|
|
}
|
|
} else if (opexpr->opno == NUMERICMULOID) {
|
|
DEFINE_BLOCK(mul64, jitted_func);
|
|
DEFINE_BLOCK(mul128, jitted_func);
|
|
DEFINE_CGVAR_INT16(maxInt64digitsNum, MAXINT64DIGIT);
|
|
|
|
llvm::Value* res_scale = builder.CreateAdd(lvalscale, rvalscale, "res_scale");
|
|
/* check delta_scale >= MAX DIGITS INT64 NUMBER or not */
|
|
cmpval = builder.CreateICmpSLE(res_scale, maxInt64digitsNum, "cmp_delta_scale");
|
|
|
|
/* do bi64 mul bi64, need to check if there is any overflow */
|
|
llvm::Function* func_smul_overflow =
|
|
llvm::Intrinsic::getDeclaration(mod, llvm::Intrinsic::smul_with_overflow, Intrinsic_Tys);
|
|
if (func_smul_overflow == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_LOAD_INTRINSIC_FUNCTION_FAILED),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Cannot get the llvm::Intrinsic::smul_with_overflow function!")));
|
|
}
|
|
llvm::Value* res = builder.CreateCall(func_smul_overflow, {leftval, rightval}, "smul");
|
|
llvm::Value* overflow_flag = builder.CreateExtractValue(res, 1);
|
|
llvm::Value* res_64 = builder.CreateExtractValue(res, 0);
|
|
cmpval = builder.CreateAnd(cmpval, builder.CreateNot(overflow_flag));
|
|
|
|
builder.CreateCondBr(cmpval, mul64, mul128);
|
|
|
|
builder.SetInsertPoint(mul64);
|
|
res1 = res_64;
|
|
/* create the result of current step */
|
|
result = llvm::UndefValue::get(SiNumeric64Type);
|
|
llvm::Value* tmphead = builder.CreateAdd(val_numeric64, res_scale);
|
|
result = builder.CreateInsertValue(result, tmphead, 0);
|
|
result = builder.CreateInsertValue(result, res_64, 1);
|
|
*bb_last = mul64;
|
|
|
|
builder.SetInsertPoint(mul128);
|
|
builder.CreateBr(*bb_outofbound);
|
|
} else {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Unexpected operation %u!", opexpr->opno)));
|
|
}
|
|
|
|
/*
|
|
* when one of the argument is bi128 : note that at most
|
|
* one of the argument is bi128.
|
|
*/
|
|
builder.SetInsertPoint(not_both_bi64);
|
|
builder.CreateBr(*bb_outofbound);
|
|
} break;
|
|
case T_Var: {
|
|
/* Extract the variable from the batch */
|
|
Var* var = (Var*)(state->expr);
|
|
llvm::Value* m_attno = llvmCodeGen->getIntConstant(INT8OID, var->varattno - 1);
|
|
|
|
Vals[0] = m_attno;
|
|
Vals[1] = int32_pos_scalvec_vals;
|
|
tmpval = builder.CreateInBoundsGEP(argVector, Vals);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 8, "m_vals");
|
|
tmpval = builder.CreateInBoundsGEP(tmpval, phi_idx);
|
|
llvm::Value* res = builder.CreateAlignedLoad(tmpval, 8, "val");
|
|
|
|
Vals[1] = int32_pos_scalvec_flag;
|
|
tmpval = builder.CreateInBoundsGEP(argVector, Vals);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 8, "m_flag");
|
|
tmpval = builder.CreateInBoundsGEP(tmpval, phi_idx);
|
|
llvm::Value* flg = builder.CreateAlignedLoad(tmpval, 1, "flag");
|
|
|
|
/* make sure the value is null or not */
|
|
llvm::Value* tmpNull = builder.CreateAnd(flg, int8_1);
|
|
tmpval = builder.CreateICmpEQ(tmpNull, int8_0);
|
|
DEFINE_BLOCK(var_bb, jitted_func);
|
|
builder.CreateCondBr(tmpval, var_bb, *bb_null);
|
|
|
|
builder.SetInsertPoint(var_bb);
|
|
Assert(NUMERICOID == var->vartype);
|
|
/* Since var is numeric type, turn numeric data to BInumeric */
|
|
result = llvm::UndefValue::get(SiNumeric64Type);
|
|
llvm::Value* bires = DatumGetBINumericCodeGen(&builder, res);
|
|
/* Extract the header data */
|
|
Vals4[0] = int64_0;
|
|
Vals4[1] = int32_1;
|
|
Vals4[2] = int32_0;
|
|
Vals4[3] = int32_0;
|
|
tmpval = builder.CreateInBoundsGEP(bires, Vals4);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 2, "header");
|
|
result = builder.CreateInsertValue(result, tmpval, 0);
|
|
|
|
Vals4[3] = int32_1;
|
|
tmpval = builder.CreateInBoundsGEP(bires, Vals4);
|
|
tmpval = builder.CreateBitCast(tmpval, int64PtrType);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 8, "value");
|
|
result = builder.CreateInsertValue(result, tmpval, 1);
|
|
*bb_last = var_bb;
|
|
|
|
return result;
|
|
} break;
|
|
case T_Const: {
|
|
Const* cst = (Const*)(state->expr);
|
|
ScalarValue val = ScalarVector::DatumToScalar(cst->constvalue, cst->consttype, cst->constisnull);
|
|
|
|
Assert(NUMERICOID == cst->consttype);
|
|
/* The const value will not be NULL */
|
|
uint16 header;
|
|
Datum numval;
|
|
/* turn to {uint16, int64} format(Simple Numeric Type) */
|
|
/* First store mark bits and scale of big integer:
|
|
* first 4 bits to distinguish bi64 and bi128, next
|
|
* 4 bits are not used, the last 8 bits store the scale of bit integer
|
|
*/
|
|
Numeric arg = DatumGetBINumeric(val);
|
|
header = arg->choice.n_header;
|
|
numval = *(int64*)(arg->choice.n_bi.n_data);
|
|
|
|
result = llvm::UndefValue::get(SiNumeric64Type);
|
|
llvm::Value* hed = llvmCodeGen->getIntConstant(INT2OID, header);
|
|
llvm::Value* res = llvmCodeGen->getIntConstant(INT8OID, numval);
|
|
result = builder.CreateInsertValue(result, hed, 0);
|
|
result = builder.CreateInsertValue(result, res, 1);
|
|
return result;
|
|
} break;
|
|
default:
|
|
Assert(0);
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
llvm::Value* VecHashAggCodeGen::EvalSimpleExprInBatchAgg(
|
|
ExprState* state, GsCodeGen::LlvmBuilder builder, llvm::Value* econtext, llvm::Value* phi_idx, llvm::Value* isNull)
|
|
{
|
|
llvm::Value* result = NULL;
|
|
switch (nodeTag(state->expr)) {
|
|
case T_TargetEntry: {
|
|
/* TargetEntry information is stored in GenericExprState */
|
|
GenericExprState* gstate = (GenericExprState*)state;
|
|
ExprState* estate = gstate->arg;
|
|
result = EvalSimpleExprInBatchAgg(estate, builder, econtext, phi_idx, isNull);
|
|
} break;
|
|
case T_Var:
|
|
case T_Const:
|
|
case T_OpExpr: {
|
|
/* define llvmargs */
|
|
llvm::Value* llvmargs[3];
|
|
llvmargs[0] = econtext;
|
|
llvmargs[1] = isNull;
|
|
llvmargs[2] = phi_idx;
|
|
|
|
/*
|
|
* Prepare the parameters that needed by OpCodeGen.
|
|
*/
|
|
ExprCodeGenArgs args;
|
|
args.exprstate = state;
|
|
args.parent = NULL;
|
|
args.builder = &builder;
|
|
args.llvm_args = &llvmargs[0];
|
|
result = dorado::VecExprCodeGen::CodeGen(&args);
|
|
} break;
|
|
default:
|
|
Assert(0);
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
llvm::Function* VecHashAggCodeGen::SonicBatchAggregationCodeGen(VecAggState* node, bool use_prefetch)
|
|
{
|
|
/* First get the basic information of VecAggState */
|
|
int numaggs = node->numaggs;
|
|
|
|
Assert(NULL != (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj);
|
|
GsCodeGen* llvmCodeGen = (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj;
|
|
|
|
if (!BatchAggJittable(node, true))
|
|
return NULL;
|
|
|
|
/* 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();
|
|
|
|
int i;
|
|
int exprscale = 0;
|
|
bool fast_aggref = false;
|
|
ExprState* estate = NULL;
|
|
llvm::Value* nValues = NULL;
|
|
llvm::Value* tmpval = NULL;
|
|
llvm::Value* idx_next = NULL;
|
|
llvm::Value* locidx = NULL;
|
|
llvm::Value* result = NULL;
|
|
llvm::Value* expres = NULL;
|
|
llvm::Value* nbit = NULL;
|
|
llvm::Value* arrIdx = NULL;
|
|
llvm::Value* atomIdx = NULL;
|
|
llvm::Value* atom = NULL;
|
|
llvm::Value* cntatom = NULL;
|
|
llvm::Value* atomsize = NULL;
|
|
llvm::Value* data = NULL;
|
|
llvm::Value* cntdata = NULL;
|
|
llvm::Value* scount = NULL;
|
|
llvm::Value* llvmargs[3];
|
|
Aggref* aggref = NULL;
|
|
llvm::Function* jitted_sonicbatchagg = NULL;
|
|
|
|
/* Define data types and some llvm consts */
|
|
DEFINE_CG_VOIDTYPE(voidType);
|
|
DEFINE_CG_TYPE(int8Type, CHAROID);
|
|
DEFINE_CG_TYPE(int16Type, INT2OID);
|
|
DEFINE_CG_TYPE(int32Type, INT4OID);
|
|
DEFINE_CG_TYPE(int64Type, INT8OID);
|
|
DEFINE_CG_PTRTYPE(int16PtrType, INT2OID);
|
|
DEFINE_CG_PTRTYPE(int64PtrType, INT8OID);
|
|
DEFINE_CG_PTRTYPE(ExprContextPtrType, "struct.ExprContext");
|
|
DEFINE_CG_PTRTYPE(vectorBatchPtrType, "class.VectorBatch");
|
|
DEFINE_CG_PTRTYPE(sonicEncodingDatumArrayPtrType, "class.SonicEncodingDatumArray");
|
|
DEFINE_CG_PTRTYPE(numericPtrType, "struct.NumericData");
|
|
DEFINE_CG_PTRTYPE(sonicHashAggPtrType, "class.SonicHashAgg");
|
|
|
|
/* create LLVM value with {uint16, int64} format type */
|
|
llvm::Type* Elements[] = {int16Type, int64Type};
|
|
llvm::Type* SiNumeric64Type = llvm::StructType::create(context, Elements, "SiNumeric64");
|
|
|
|
DEFINE_CGVAR_INT8(int8_0, 0);
|
|
DEFINE_CGVAR_INT8(int8_1, 1);
|
|
DEFINE_CGVAR_INT16(val_mask, NUMERIC_BI_MASK);
|
|
DEFINE_CGVAR_INT16(val_binum64, NUMERIC_64);
|
|
DEFINE_CGVAR_INT32(int32_0, 0);
|
|
DEFINE_CGVAR_INT32(int32_1, 1);
|
|
DEFINE_CGVAR_INT64(int64_0, 0);
|
|
DEFINE_CGVAR_INT64(int64_1, 1);
|
|
DEFINE_CGVAR_INT64(int64_6, 6);
|
|
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_scalvec_flag, pos_scalvec_flag);
|
|
DEFINE_CGVAR_INT32(int32_pos_ecxt_pertuple, pos_ecxt_pertuple);
|
|
DEFINE_CGVAR_INT32(int32_pos_ecxt_outerbatch, pos_ecxt_outerbatch);
|
|
DEFINE_CGVAR_INT32(int32_pos_shash_data, pos_shash_data);
|
|
DEFINE_CGVAR_INT32(int32_pos_shash_sonichmemctl, pos_shash_sonichmemctl);
|
|
DEFINE_CGVAR_INT32(int32_pos_shash_loc, pos_shash_loc);
|
|
DEFINE_CGVAR_INT32(int32_pos_sonichmemctl_hcxt, pos_sonichmemctl_hcxt);
|
|
DEFINE_CGVAR_INT32(int32_pos_shashagg_ecxt, pos_shashagg_ecxt);
|
|
DEFINE_CGVAR_INT32(int32_pos_sdarray_nbit, pos_sdarray_nbit);
|
|
DEFINE_CGVAR_INT32(int32_pos_sdarray_atomsize, pos_sdarray_atomsize);
|
|
DEFINE_CGVAR_INT32(int32_pos_sdarray_arr, pos_sdarray_arr);
|
|
DEFINE_CGVAR_INT32(int32_pos_atom_data, pos_atom_data);
|
|
DEFINE_CGVAR_INT32(int32_pos_atom_nullflag, pos_atom_nullflag);
|
|
|
|
/* 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};
|
|
|
|
llvm::Value** aggIdxList = (llvm::Value**)palloc(sizeof(llvm::Value*) * numaggs);
|
|
llvm::Value** batch_vals = (llvm::Value**)palloc(sizeof(llvm::Value*) * numaggs);
|
|
llvm::Value** batch_flag = (llvm::Value**)palloc(sizeof(llvm::Value*) * numaggs);
|
|
llvm::Value** sdata = (llvm::Value**)palloc(sizeof(llvm::Value*) * numaggs);
|
|
llvm::BasicBlock** agg_bb = (llvm::BasicBlock**)palloc(sizeof(llvm::BasicBlock*) * numaggs);
|
|
llvm::BasicBlock** flag_then = (llvm::BasicBlock**)palloc(sizeof(llvm::BasicBlock*) * numaggs);
|
|
llvm::BasicBlock** flag_else = (llvm::BasicBlock**)palloc(sizeof(llvm::BasicBlock*) * numaggs);
|
|
|
|
GsCodeGen::FnPrototype fn_prototype(llvmCodeGen, "JittedSonicFastBatchAgg", voidType);
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("sonicHashAgg", sonicHashAggPtrType));
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("batch", vectorBatchPtrType));
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("aggIdx", int16PtrType));
|
|
jitted_sonicbatchagg = fn_prototype.generatePrototype(&builder, &llvmargs[0]);
|
|
|
|
llvm::Value* sonicHashAgg = llvmargs[0];
|
|
llvm::Value* batch = llvmargs[1];
|
|
llvm::Value* aggIdx = llvmargs[2];
|
|
|
|
/* parameter used to mark if this tuple is NULL or not */
|
|
llvm::Value* isNull = builder.CreateAlloca(int8Type);
|
|
|
|
/* SonicHashAgg.SonicHash.SonicHashMemoryControl.hashContext */
|
|
Vals4[2] = int32_pos_shash_sonichmemctl;
|
|
Vals4[3] = int32_pos_sonichmemctl_hcxt;
|
|
llvm::Value* hcxt = builder.CreateInBoundsGEP(sonicHashAgg, Vals4);
|
|
hcxt = builder.CreateAlignedLoad(hcxt, 8, "hashContext");
|
|
|
|
/* get the nrows of the batch */
|
|
tmpval = builder.CreateInBoundsGEP(batch, Vals);
|
|
nValues = builder.CreateAlignedLoad(tmpval, 4, "m_rows");
|
|
|
|
/* get vectorBatch.m_arr of the batch */
|
|
Vals[0] = int64_0;
|
|
Vals[1] = int32_pos_batch_marr;
|
|
llvm::Value* argVector = builder.CreateInBoundsGEP(batch, Vals);
|
|
argVector = builder.CreateAlignedLoad(argVector, 8, "m_arr");
|
|
|
|
/* pre-load all the expression context : node->ss.ps.ps_ExprContext*/
|
|
ExprContext* exprcontext = node->ss.ps.ps_ExprContext;
|
|
llvm::Value* econtext = llvmCodeGen->CastPtrToLlvmPtr(ExprContextPtrType, exprcontext);
|
|
|
|
/* get sonic data array structure : SonicHashAgg.SonicHash.SonicDatumArray** */
|
|
Vals3[2] = int32_pos_shash_data;
|
|
llvm::Value* sdataptr = builder.CreateInBoundsGEP(sonicHashAgg, Vals3);
|
|
sdataptr = builder.CreateAlignedLoad(sdataptr, 8, "sonicdatumarray");
|
|
|
|
/* get sonic data array structure: SonicHashAgg.SonicHash.m_loc */
|
|
Vals4[2] = int32_pos_shash_loc;
|
|
Vals4[3] = int64_0;
|
|
llvm::Value* loc = builder.CreateInBoundsGEP(sonicHashAgg, Vals4);
|
|
|
|
/* define the basic block needed in the main process */
|
|
llvm::BasicBlock* entry = &jitted_sonicbatchagg->getEntryBlock();
|
|
DEFINE_BLOCK(for_body, jitted_sonicbatchagg);
|
|
DEFINE_BLOCK(for_inc, jitted_sonicbatchagg);
|
|
DEFINE_BLOCK(for_end, jitted_sonicbatchagg);
|
|
|
|
/* get all addIdx of agg operators */
|
|
for (i = 0; i < numaggs; i++) {
|
|
llvm::Value* tmpidx = llvmCodeGen->getIntConstant(INT4OID, i);
|
|
tmpval = builder.CreateInBoundsGEP(aggIdx, tmpidx);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 4, "aggIdx");
|
|
aggIdxList[i] = builder.CreateSExt(tmpval, int64Type);
|
|
|
|
tmpval = builder.CreateInBoundsGEP(sdataptr, aggIdxList[i]);
|
|
sdata[i] = builder.CreateAlignedLoad(tmpval, 8, "sonicdata");
|
|
|
|
agg_bb[i] = llvm::BasicBlock::Create(context, "agg_bb", jitted_sonicbatchagg);
|
|
flag_then[i] = llvm::BasicBlock::Create(context, "flag_then", jitted_sonicbatchagg);
|
|
flag_else[i] = llvm::BasicBlock::Create(context, "flag_else", jitted_sonicbatchagg);
|
|
}
|
|
|
|
/*
|
|
* Start the main process for SonicHashAgg::batchaggregation, which has the following
|
|
* pedudo code:
|
|
* for (j = 0; j < nrows; j++){
|
|
* for (i = 0; i < m_aggNum; i++){
|
|
* peraggstate = peragg[numaggs - 1 - i];
|
|
* pbatch = ExecVecProject (peraggstate->evalproj)
|
|
* AggregationOnScalar(aggInfo[i], &pbatch->m_arr[0], aggidx[i], m_Loc)
|
|
* }
|
|
* }
|
|
*/
|
|
builder.SetInsertPoint(entry);
|
|
/*
|
|
* First get the ecxt_per_tuple_memory, since we need to switch to this
|
|
* memory context.
|
|
*/
|
|
/* SonicHashAgg.m_econtext */
|
|
Vals[1] = int32_pos_shashagg_ecxt;
|
|
llvm::Value* mecontext = builder.CreateInBoundsGEP(sonicHashAgg, Vals);
|
|
mecontext = builder.CreateAlignedLoad(mecontext, 8, "m_econtext");
|
|
Vals[1] = int32_pos_ecxt_pertuple;
|
|
llvm::Value* agg_expr_context = builder.CreateInBoundsGEP(mecontext, Vals);
|
|
agg_expr_context = builder.CreateAlignedLoad(agg_expr_context, 8, "agg_per_tuple_memory");
|
|
llvm::Value* agg_oldcontext = VecExprCodeGen::MemCxtSwitToCodeGen(&builder, agg_expr_context);
|
|
|
|
/*
|
|
* Load value and flag from batch before the batch loop when we have
|
|
* simple vars in transition level.
|
|
*/
|
|
for (i = 0; i < numaggs; i++) {
|
|
int numSimpleVars = 0;
|
|
AggStatePerAgg peraggstate = &node->peragg[numaggs - i - 1];
|
|
aggref = (Aggref*)(peraggstate->aggref);
|
|
ProjectionInfo* projInfo = (ProjectionInfo*)(peraggstate->evalproj);
|
|
|
|
if (aggref->aggstage == 0 && aggref->aggfnoid != COUNTOID) {
|
|
numSimpleVars = projInfo->pi_numSimpleVars;
|
|
if (numSimpleVars > 0) {
|
|
int* varNumbers = projInfo->pi_varNumbers;
|
|
int varNumber = varNumbers[0] - 1;
|
|
/* m_arr[varNumber].m_vals */
|
|
Vals[0] = llvmCodeGen->getIntConstant(INT8OID, varNumber);
|
|
Vals[1] = int32_pos_scalvec_vals;
|
|
tmpval = builder.CreateInBoundsGEP(argVector, Vals);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 8, "m_vals");
|
|
batch_vals[i] = tmpval;
|
|
|
|
/* m_arr[varNumber].m_flag */
|
|
Vals[1] = int32_pos_scalvec_flag;
|
|
llvm::Value* argFlag = builder.CreateInBoundsGEP(argVector, Vals);
|
|
argFlag = builder.CreateAlignedLoad(argFlag, 1, "m_flag");
|
|
batch_flag[i] = argFlag;
|
|
} else {
|
|
batch_vals[i] = NULL;
|
|
batch_flag[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
tmpval = builder.CreateICmpSGT(nValues, int32_0);
|
|
builder.CreateCondBr(tmpval, for_body, for_end);
|
|
|
|
builder.SetInsertPoint(for_body);
|
|
llvm::PHINode* phi_idx = builder.CreatePHI(int64Type, 2);
|
|
|
|
/* after each loop, index plus one */
|
|
idx_next = builder.CreateAdd(phi_idx, int64_1);
|
|
|
|
phi_idx->addIncoming(int64_0, entry);
|
|
phi_idx->addIncoming(idx_next, for_inc);
|
|
|
|
/* get location : loc[i] (see vsnumeric_sum and vsint8_sum) and check if it is zero */
|
|
tmpval = builder.CreateInBoundsGEP(loc, phi_idx);
|
|
locidx = builder.CreateAlignedLoad(tmpval, 4, "loc_i");
|
|
tmpval = builder.CreateICmpEQ(locidx, int32_0);
|
|
builder.CreateCondBr(tmpval, for_inc, agg_bb[0]);
|
|
|
|
/* loop over the numaggs */
|
|
int numSimpleVars = 0;
|
|
for (i = 0; i < numaggs; i++) {
|
|
llvm::BasicBlock* bisum_bb = NULL;
|
|
llvm::BasicBlock* numsum_bb = NULL;
|
|
|
|
/* the inverse order */
|
|
AggStatePerAgg peraggstate = &node->peragg[numaggs - i - 1];
|
|
aggref = (Aggref*)(peraggstate->aggref);
|
|
ProjectionInfo* projInfo = (ProjectionInfo*)(peraggstate->evalproj);
|
|
|
|
/* start the codegeneration for each aggregation */
|
|
builder.SetInsertPoint(agg_bb[i]);
|
|
|
|
if (aggref->aggfnoid == NUMERICAVGFUNCOID) {
|
|
/* get scount = sdata[aggidx + 1] */
|
|
llvm::Value* aggplus = builder.CreateAdd(aggIdxList[i], int64_1);
|
|
tmpval = builder.CreateInBoundsGEP(sdataptr, aggplus);
|
|
scount = builder.CreateAlignedLoad(tmpval, 8, "scount");
|
|
}
|
|
|
|
if (aggref->aggstage == 0) {
|
|
if (aggref->aggfnoid != COUNTOID) {
|
|
Assert(peraggstate->evalproj != NULL);
|
|
AggrefExprState* aggexprstate = peraggstate->aggrefstate;
|
|
/* check if current expression can be codegened in fast path or not */
|
|
estate = (ExprState*)linitial(aggexprstate->args);
|
|
fast_aggref = AggRefFastJittable(estate);
|
|
|
|
/*
|
|
* Do not consider collection and finalization level for
|
|
* numeric_avg to avoid deconstruct_array.
|
|
*/
|
|
if (aggref->aggfnoid == NUMERICAVGFUNCOID && aggref->aggstage > 0)
|
|
fast_aggref = false;
|
|
|
|
/* If the current agg expression is just a simple var,
|
|
* load it from the batch directly
|
|
*/
|
|
if (projInfo == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Unexpected NULL project information.")));
|
|
}
|
|
numSimpleVars = projInfo->pi_numSimpleVars;
|
|
if (numSimpleVars > 0) {
|
|
/* m_arr[varNumber].m_vals */
|
|
tmpval = batch_vals[i];
|
|
tmpval = builder.CreateInBoundsGEP(tmpval, phi_idx);
|
|
result = builder.CreateAlignedLoad(tmpval, 8, "val");
|
|
|
|
/* m_arr[varNumber].m_flag */
|
|
tmpval = batch_flag[i];
|
|
tmpval = builder.CreateInBoundsGEP(tmpval, phi_idx);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 1, "flag");
|
|
builder.CreateAlignedStore(tmpval, isNull, 1);
|
|
} else {
|
|
/* set the batch information : econtext->ecxt_outerbatch = batch */
|
|
Vals[0] = int64_0;
|
|
Vals[1] = int32_pos_ecxt_outerbatch;
|
|
llvm::Value* tmp_outerbatch = builder.CreateInBoundsGEP(econtext, Vals);
|
|
builder.CreateAlignedStore(batch, tmp_outerbatch, 8);
|
|
|
|
/*
|
|
* If fast_aggref is true, we could try to evaluate the
|
|
* expression value by using BI64 all the way, and turn
|
|
* to original path once meet outofbound.
|
|
*/
|
|
if (fast_aggref) {
|
|
llvm::BasicBlock* bb_last = builder.GetInsertBlock();
|
|
DEFINE_BLOCK(bb_null, jitted_sonicbatchagg);
|
|
DEFINE_BLOCK(bb_outofbound, jitted_sonicbatchagg);
|
|
|
|
if (NULL == bisum_bb) {
|
|
bisum_bb = llvm::BasicBlock::Create(context, "bisum_bb", jitted_sonicbatchagg);
|
|
}
|
|
|
|
if (NULL == numsum_bb) {
|
|
numsum_bb = llvm::BasicBlock::Create(context, "numsum_bb", jitted_sonicbatchagg);
|
|
}
|
|
|
|
/* evaluate expression result */
|
|
llvm::Value* tmpexpres = EvalFastExprInBatchAgg(estate,
|
|
builder,
|
|
jitted_sonicbatchagg,
|
|
&bb_null,
|
|
&bb_last,
|
|
&bb_outofbound,
|
|
econtext,
|
|
argVector,
|
|
phi_idx);
|
|
|
|
/* expres is already in {int16, int64} format */
|
|
builder.SetInsertPoint(bb_last);
|
|
builder.CreateAlignedStore(int8_0, isNull, 1);
|
|
llvm::Value* tmp_scale = builder.CreateExtractValue(tmpexpres, 0);
|
|
llvm::Value* tmp_value = builder.CreateExtractValue(tmpexpres, 1);
|
|
expres = llvm::UndefValue::get(SiNumeric64Type);
|
|
expres = builder.CreateInsertValue(expres, tmp_scale, 0);
|
|
expres = builder.CreateInsertValue(expres, tmp_value, 1);
|
|
builder.CreateBr(bisum_bb);
|
|
|
|
/* construct a null value, and no need to do aggregation */
|
|
builder.SetInsertPoint(bb_null);
|
|
builder.CreateAlignedStore(int8_1, isNull, 1);
|
|
if (i == numaggs - 1)
|
|
builder.CreateBr(for_inc);
|
|
else
|
|
builder.CreateBr(agg_bb[i + 1]);
|
|
|
|
/* if result can not be represented in BI64, turn to
|
|
* the original path
|
|
*/
|
|
builder.SetInsertPoint(bb_outofbound);
|
|
/* Turn to per_tuple_memory to evaluate expression. */
|
|
Vals[0] = int64_0;
|
|
Vals[1] = int32_pos_ecxt_pertuple;
|
|
llvm::Value* curr_Context = builder.CreateInBoundsGEP(econtext, Vals);
|
|
curr_Context = builder.CreateAlignedLoad(curr_Context, 8, "per_tuple_memory");
|
|
llvm::Value* cg_oldContext = VecExprCodeGen::MemCxtSwitToCodeGen(&builder, curr_Context);
|
|
result = EvalSimpleExprInBatchAgg(estate, builder, econtext, phi_idx, isNull);
|
|
/* return back to the old memory context */
|
|
(void)VecExprCodeGen::MemCxtSwitToCodeGen(&builder, cg_oldContext);
|
|
} else {
|
|
/*
|
|
* corresponding to ExecVecProject(peraggstate->evalproj) :
|
|
* to evaluate expressions, we should turn to per_tuple_memory.
|
|
*/
|
|
Vals[0] = int64_0;
|
|
Vals[1] = int32_pos_ecxt_pertuple;
|
|
llvm::Value* curr_Context = builder.CreateInBoundsGEP(econtext, Vals);
|
|
curr_Context = builder.CreateAlignedLoad(curr_Context, 8, "per_tuple_memory");
|
|
llvm::Value* cg_oldContext = VecExprCodeGen::MemCxtSwitToCodeGen(&builder, curr_Context);
|
|
/* corresponding to ExecVecProject(peraggstate->evalproj) */
|
|
result = EvalSimpleExprInBatchAgg(estate, builder, econtext, phi_idx, isNull);
|
|
/* return back to the old memory context */
|
|
(void)VecExprCodeGen::MemCxtSwitToCodeGen(&builder, cg_oldContext);
|
|
}
|
|
}
|
|
} else {
|
|
/*
|
|
* When current stage is transaction and aggfnoid is COUNTOID, no need to
|
|
* load any batch information. since we only need to plus one when cell
|
|
* is not null.
|
|
*/
|
|
result = int64_0;
|
|
builder.CreateAlignedStore(int8_0, isNull, 1);
|
|
}
|
|
} else {
|
|
/*
|
|
* When aggref->stage is not transiction, the aggref expr is always
|
|
* be var, so get the value from batch directly (projInfo is not null).
|
|
*/
|
|
if (projInfo != NULL) {
|
|
int* varNumbers = projInfo->pi_varNumbers;
|
|
int varNumber = varNumbers[0] - 1;
|
|
/* m_arr[varNumber].m_vals */
|
|
Vals[0] = llvmCodeGen->getIntConstant(INT8OID, varNumber);
|
|
Vals[1] = int32_pos_scalvec_vals;
|
|
tmpval = builder.CreateInBoundsGEP(argVector, Vals);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 8, "m_vals");
|
|
tmpval = builder.CreateInBoundsGEP(tmpval, phi_idx);
|
|
result = builder.CreateAlignedLoad(tmpval, 8, "val");
|
|
|
|
/* m_arr[varNumber].m_flag */
|
|
Vals[1] = int32_pos_scalvec_flag;
|
|
llvm::Value* argFlag = builder.CreateInBoundsGEP(argVector, Vals);
|
|
argFlag = builder.CreateAlignedLoad(argFlag, 1, "m_flag");
|
|
tmpval = builder.CreateInBoundsGEP(argFlag, phi_idx);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 1, "flag");
|
|
builder.CreateAlignedStore(tmpval, isNull, 1);
|
|
} else {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Unexpected NULL project information.")));
|
|
}
|
|
}
|
|
|
|
/* Compute Aggregation */
|
|
if (aggref->aggfnoid == COUNTOID) {
|
|
flag_then[i]->eraseFromParent();
|
|
flag_else[i]->eraseFromParent();
|
|
|
|
char* Jittedname = NULL;
|
|
if (aggref->aggstage == 0)
|
|
Jittedname = "Jitted_scount_0";
|
|
else
|
|
Jittedname = "Jitted_scount_1";
|
|
|
|
llvm::Function* func_vscount = llvmCodeGen->module()->getFunction(Jittedname);
|
|
if (NULL == func_vscount) {
|
|
func_vscount = vsonic_count_codegen(aggref);
|
|
}
|
|
builder.CreateCall(func_vscount, {sdata[i], locidx, result});
|
|
|
|
if (i == numaggs - 1)
|
|
builder.CreateBr(for_inc);
|
|
else
|
|
builder.CreateBr(agg_bb[i + 1]);
|
|
} else {
|
|
/*
|
|
* now we already get sonic datum (data and locidx) and pVector(result), check
|
|
* the flag and do aggregation.
|
|
*/
|
|
/* see if IS_NULL(flag[phi_idx]) == false */
|
|
llvm::Value* tmpnull = builder.CreateAlignedLoad(isNull, 1, "tmpnull");
|
|
tmpnull = builder.CreateAnd(tmpnull, int8_1);
|
|
llvm::Value* flag_cmp = builder.CreateICmpEQ(tmpnull, int8_0);
|
|
builder.CreateCondBr(flag_cmp, flag_then[i], flag_else[i]);
|
|
|
|
/* only do when not null */
|
|
builder.SetInsertPoint(flag_then[i]);
|
|
switch (aggref->aggfnoid) {
|
|
case NUMERICSUMFUNCOID: {
|
|
/*
|
|
* If aggref can be evaluated in fast path and just be
|
|
* simple vars, use the result from batch.
|
|
*/
|
|
if (fast_aggref && (numSimpleVars > 0)) {
|
|
DEFINE_BLOCK(agg_then, jitted_sonicbatchagg);
|
|
DEFINE_BLOCK(agg_else, jitted_sonicbatchagg);
|
|
DEFINE_BLOCK(agg_end, jitted_sonicbatchagg);
|
|
DEFINE_BLOCK(normal_bb, jitted_sonicbatchagg);
|
|
DEFINE_BLOCK(var_bisum_bb, jitted_sonicbatchagg);
|
|
DEFINE_BLOCK(var_numsum_bb, jitted_sonicbatchagg);
|
|
|
|
/* get sonic data according to sonic datum array and locidx */
|
|
/* calculate arrIdx and atomIdx */
|
|
Vals[0] = int64_0;
|
|
Vals[1] = int32_pos_sdarray_nbit;
|
|
nbit = builder.CreateInBoundsGEP(sdata[i], Vals);
|
|
nbit = builder.CreateAlignedLoad(nbit, 4, "arridx");
|
|
arrIdx = builder.CreateLShr(locidx, nbit);
|
|
arrIdx = builder.CreateSExt(arrIdx, int64Type);
|
|
|
|
Vals[1] = int32_pos_sdarray_atomsize;
|
|
atomsize = builder.CreateInBoundsGEP(sdata[i], Vals);
|
|
atomsize = builder.CreateAlignedLoad(atomsize, 4, "atomsize");
|
|
atomsize = builder.CreateSub(atomsize, int32_1);
|
|
atomIdx = builder.CreateAnd(atomsize, locidx);
|
|
atomIdx = builder.CreateSExt(atomIdx, int64Type);
|
|
|
|
/* get sdata[i]->m_arr[arrIdx] */
|
|
Vals[1] = int32_pos_sdarray_arr;
|
|
atom = builder.CreateInBoundsGEP(sdata[i], Vals);
|
|
atom = builder.CreateAlignedLoad(atom, 8, "atomptr");
|
|
atom = builder.CreateInBoundsGEP(atom, arrIdx);
|
|
atom = builder.CreateAlignedLoad(atom, 8, "atom");
|
|
|
|
/* get address of sdata->m_arr[arrIdx]->data[atomIdx] */
|
|
Vals[1] = int32_pos_atom_data;
|
|
data = builder.CreateInBoundsGEP(atom, Vals);
|
|
data = builder.CreateAlignedLoad(data, 8, "data");
|
|
data = builder.CreateBitCast(data, int64PtrType);
|
|
llvm::Value* dataptr = builder.CreateInBoundsGEP(data, atomIdx);
|
|
|
|
/* get sdata->m_arr[arrIdx]->nullFlag[atomIdx] and check if it is NULL */
|
|
Vals[1] = int32_pos_atom_nullflag;
|
|
llvm::Value* nullflag = builder.CreateInBoundsGEP(atom, Vals);
|
|
nullflag = builder.CreateAlignedLoad(nullflag, 8, "nullflagptr");
|
|
nullflag = builder.CreateInBoundsGEP(nullflag, atomIdx);
|
|
|
|
/* load nullflag */
|
|
tmpval = builder.CreateAlignedLoad(nullflag, 1, "nullFlag");
|
|
tmpval = builder.CreateAnd(tmpval, int8_1);
|
|
tmpval = builder.CreateICmpEQ(tmpval, int8_0);
|
|
builder.CreateCondBr(tmpval, agg_else, agg_then);
|
|
|
|
/* sonic data is null, set value */
|
|
builder.SetInsertPoint(agg_then);
|
|
|
|
/*
|
|
* should make a new context to record the result : the
|
|
* following code corresponding to:
|
|
* 'leftarg = DatumGetBINumeric(pVal[i]);
|
|
* data->setValue(NumericGetDatum(leftarg, false, arrIndx, atomIdx))'.
|
|
*/
|
|
tmpval = DatumGetBINumericCodeGen(&builder, result);
|
|
tmpval = builder.CreatePtrToInt(tmpval, int64Type);
|
|
tmpval = WrapaddVariableCodeGen(&builder, hcxt, tmpval);
|
|
|
|
builder.CreateAlignedStore(tmpval, dataptr, 8);
|
|
builder.CreateAlignedStore(int8_0, nullflag, 1);
|
|
|
|
/* turn to next basicblock or end this */
|
|
if (i == numaggs - 1)
|
|
builder.CreateBr(for_inc);
|
|
else
|
|
builder.CreateBr(agg_bb[i + 1]);
|
|
|
|
/* cell be not null, do aggregation */
|
|
builder.SetInsertPoint(agg_else);
|
|
|
|
/*
|
|
* When fast_aggref is true and numSimpleVars is greater than zero,
|
|
* the expr is numeric type var. Convert this numeric type data to
|
|
* SiNumeric data to get the value.
|
|
*/
|
|
llvm::Value* bires = DatumGetBINumericCodeGen(&builder, result);
|
|
|
|
/* extract the header of result to check if it is BINumeric */
|
|
Vals4[0] = int64_0;
|
|
Vals4[1] = int32_1;
|
|
Vals4[2] = int32_0;
|
|
Vals4[3] = int32_0;
|
|
tmpval = builder.CreateInBoundsGEP(bires, Vals4);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 2, "biheader");
|
|
llvm::Value* rflag = builder.CreateAnd(tmpval, val_mask);
|
|
|
|
/* get leftdata[0] and convert to BINumeric */
|
|
llvm::Value* realdata = builder.CreateAlignedLoad(dataptr, 8, "dataval");
|
|
llvm::Value* leftarg = DatumGetBINumericCodeGen(&builder, realdata);
|
|
tmpval = builder.CreateInBoundsGEP(leftarg, Vals4);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 2, "sonicheader");
|
|
llvm::Value* lflag = builder.CreateAnd(tmpval, val_mask);
|
|
|
|
/* check if either of them is not BI64 */
|
|
llvm::Value* oparg1 = builder.CreateICmpEQ(lflag, val_binum64);
|
|
llvm::Value* oparg2 = builder.CreateICmpEQ(rflag, val_binum64);
|
|
llvm::Value* bothbi64 = builder.CreateAnd(oparg1, oparg2);
|
|
|
|
/* use fast path only when both args are bi64 */
|
|
builder.CreateCondBr(bothbi64, var_bisum_bb, var_numsum_bb);
|
|
|
|
builder.SetInsertPoint(var_bisum_bb);
|
|
/* extract the actual data of numeric only when sonic data is not null */
|
|
Vals4[0] = int64_0;
|
|
Vals4[1] = int32_1;
|
|
Vals4[2] = int32_0;
|
|
Vals4[3] = int32_1;
|
|
tmpval = builder.CreateInBoundsGEP(bires, Vals4);
|
|
tmpval = builder.CreateBitCast(tmpval, int64PtrType);
|
|
llvm::Value* resval = builder.CreateAlignedLoad(tmpval, 8, "bivalue");
|
|
|
|
/* extrac the actual data of leftdata and do sum */
|
|
llvm::Value* sonicptr = builder.CreateAdd(realdata, int64_6);
|
|
sonicptr = builder.CreateIntToPtr(sonicptr, int64PtrType);
|
|
llvm::Value* midval = builder.CreateAlignedLoad(sonicptr, 8, "intvalue");
|
|
|
|
/* check overflow */
|
|
llvm::Type* Intrinsic_Tys[] = {int64Type};
|
|
llvm::Function* func_sonicadd_overflow =
|
|
llvm::Intrinsic::getDeclaration(mod, llvm::Intrinsic::sadd_with_overflow, Intrinsic_Tys);
|
|
if (func_sonicadd_overflow == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_LOAD_INTRINSIC_FUNCTION_FAILED),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Cannot get the llvm::Intrinsic::sadd_with_overflow function!\n")));
|
|
}
|
|
llvm::Value* aggres = builder.CreateCall(func_sonicadd_overflow, {resval, midval});
|
|
llvm::Value* oflag = builder.CreateExtractValue(aggres, 1);
|
|
builder.CreateCondBr(oflag, var_numsum_bb, normal_bb);
|
|
|
|
/* if meet overflow during aggregation, turn to original sum function. */
|
|
builder.SetInsertPoint(var_numsum_bb);
|
|
llvm::Function* func_vsnumericsum =
|
|
llvmCodeGen->module()->getFunction("Jitted_sonic_numericsum");
|
|
if (NULL == func_vsnumericsum) {
|
|
func_vsnumericsum = vsnumeric_sum_codegen(aggref);
|
|
}
|
|
sdata[i] = builder.CreateBitCast(sdata[i], sonicEncodingDatumArrayPtrType);
|
|
builder.CreateCall(func_vsnumericsum, {sdata[i], locidx, result});
|
|
builder.CreateBr(agg_end);
|
|
|
|
builder.SetInsertPoint(normal_bb);
|
|
llvm::Value* sumval = builder.CreateExtractValue(aggres, 0);
|
|
builder.CreateAlignedStore(sumval, sonicptr, 8);
|
|
builder.CreateBr(agg_end);
|
|
|
|
builder.SetInsertPoint(agg_end);
|
|
} else if (fast_aggref) {
|
|
/*
|
|
* If aggref can be evaluated in fast path and be numeric
|
|
* expressions, use the result from fastexpr.
|
|
*/
|
|
Assert(bisum_bb != NULL);
|
|
Assert(numsum_bb != NULL);
|
|
DEFINE_BLOCK(agg_end, jitted_sonicbatchagg);
|
|
DEFINE_BLOCK(agg_then, jitted_sonicbatchagg);
|
|
DEFINE_BLOCK(agg_else, jitted_sonicbatchagg);
|
|
DEFINE_BLOCK(normal_bb, jitted_sonicbatchagg);
|
|
DEFINE_BLOCK(expr_bisum_bb, jitted_sonicbatchagg);
|
|
DEFINE_BLOCK(bioverflow_bb, jitted_sonicbatchagg);
|
|
|
|
/*
|
|
* if the result of expression is outofbound, turn to
|
|
* original numeric path.
|
|
*/
|
|
builder.CreateBr(numsum_bb);
|
|
|
|
/*
|
|
* if the result of expression is BI64, extract it.
|
|
*/
|
|
builder.SetInsertPoint(bisum_bb);
|
|
llvm::Value* resval = builder.CreateExtractValue(expres, 1);
|
|
|
|
/* get sonic data according to sonic datum array and locidx */
|
|
/* calculate arrIdx and atomIdx */
|
|
Vals[1] = int32_pos_sdarray_nbit;
|
|
nbit = builder.CreateInBoundsGEP(sdata[i], Vals);
|
|
nbit = builder.CreateAlignedLoad(nbit, 4, "arridx");
|
|
arrIdx = builder.CreateLShr(locidx, nbit);
|
|
arrIdx = builder.CreateSExt(arrIdx, int64Type);
|
|
|
|
Vals[1] = int32_pos_sdarray_atomsize;
|
|
atomsize = builder.CreateInBoundsGEP(sdata[i], Vals);
|
|
atomsize = builder.CreateAlignedLoad(atomsize, 4, "atomsize");
|
|
atomsize = builder.CreateSub(atomsize, int32_1);
|
|
atomIdx = builder.CreateAnd(atomsize, locidx);
|
|
atomIdx = builder.CreateSExt(atomIdx, int64Type);
|
|
|
|
/* get sdata[aggidx]->m_arr[arrIdx] */
|
|
Vals[1] = int32_pos_sdarray_arr;
|
|
atom = builder.CreateInBoundsGEP(sdata[i], Vals);
|
|
atom = builder.CreateAlignedLoad(atom, 8, "atomptr");
|
|
atom = builder.CreateInBoundsGEP(atom, arrIdx);
|
|
atom = builder.CreateAlignedLoad(atom, 8, "atom");
|
|
|
|
/* get address of sdata[aggidx]->m_arr[arrIdx]->data[atomIdx] */
|
|
Vals[1] = int32_pos_atom_data;
|
|
data = builder.CreateInBoundsGEP(atom, Vals);
|
|
data = builder.CreateAlignedLoad(data, 8, "data");
|
|
data = builder.CreateBitCast(data, int64PtrType);
|
|
llvm::Value* dataptr = builder.CreateInBoundsGEP(data, atomIdx);
|
|
|
|
/* get sdata[aggidx]->m_arr[arrIdx]->nullFlag[atomIdx] and check if it is NULL */
|
|
Vals[1] = int32_pos_atom_nullflag;
|
|
llvm::Value* nullflag = builder.CreateInBoundsGEP(atom, Vals);
|
|
nullflag = builder.CreateAlignedLoad(nullflag, 8, "nullflagptr");
|
|
nullflag = builder.CreateInBoundsGEP(nullflag, atomIdx);
|
|
|
|
/* load nullflag */
|
|
tmpval = builder.CreateAlignedLoad(nullflag, 1, "nullFlag");
|
|
tmpval = builder.CreateAnd(tmpval, int8_1);
|
|
tmpval = builder.CreateICmpEQ(tmpval, int8_0);
|
|
builder.CreateCondBr(tmpval, agg_else, agg_then);
|
|
|
|
/* sonic data is null, set value */
|
|
builder.SetInsertPoint(agg_then);
|
|
|
|
/* get the aligned scale of this expression */
|
|
exprscale = GetAlignedScale(estate->expr);
|
|
llvm::Value* alignedscale = llvmCodeGen->getIntConstant(CHAROID, exprscale);
|
|
|
|
/*
|
|
* should make a new context to record the result : the
|
|
* following code corresponding to:
|
|
* 'leftarg = DatumGetBINumeric(pVal[i]);
|
|
* cell->m_val[idx].val = addVariable(context, NumericGetDatum(leftarg));'.
|
|
*/
|
|
tmpval = WrapmakeNumeric64CodeGen(&builder, resval, alignedscale);
|
|
tmpval = DatumGetBINumericCodeGen(&builder, tmpval);
|
|
tmpval = builder.CreatePtrToInt(tmpval, int64Type);
|
|
tmpval = WrapaddVariableCodeGen(&builder, hcxt, tmpval);
|
|
|
|
builder.CreateAlignedStore(tmpval, dataptr, 8);
|
|
builder.CreateAlignedStore(int8_0, nullflag, 1);
|
|
|
|
/* turn to next basicblock or end this */
|
|
if (i == numaggs - 1)
|
|
builder.CreateBr(for_inc);
|
|
else
|
|
builder.CreateBr(agg_bb[i + 1]);
|
|
|
|
/* cell not be null, do aggregation */
|
|
builder.SetInsertPoint(agg_else);
|
|
llvm::Value* realdata = builder.CreateAlignedLoad(dataptr, 8, "dataval");
|
|
|
|
/* first make sure the value in sonic datum array is BI64 format */
|
|
llvm::Value* leftarg = DatumGetBINumericCodeGen(&builder, realdata);
|
|
Vals4[0] = int64_0;
|
|
Vals4[1] = int32_1;
|
|
Vals4[2] = int32_0;
|
|
Vals4[3] = int32_0;
|
|
tmpval = builder.CreateInBoundsGEP(leftarg, Vals4);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 2, "leftheader");
|
|
llvm::Value* biflag = builder.CreateAnd(tmpval, val_mask);
|
|
llvm::Value* isbi64 = builder.CreateICmpEQ(biflag, val_binum64);
|
|
builder.CreateCondBr(isbi64, expr_bisum_bb, bioverflow_bb);
|
|
|
|
/*
|
|
* do aggregation directly only when both expr value
|
|
* and cell value is bi64.
|
|
*/
|
|
builder.SetInsertPoint(expr_bisum_bb);
|
|
llvm::Value* sonicptr = builder.CreateAdd(realdata, int64_6);
|
|
sonicptr = builder.CreateIntToPtr(sonicptr, int64PtrType);
|
|
llvm::Value* midval = builder.CreateAlignedLoad(sonicptr, 8, "intvalue");
|
|
|
|
/* check overflow */
|
|
llvm::Type* Intrinsic_Tys[] = {int64Type};
|
|
llvm::Function* func_sonicadd_overflow =
|
|
llvm::Intrinsic::getDeclaration(mod, llvm::Intrinsic::sadd_with_overflow, Intrinsic_Tys);
|
|
if (func_sonicadd_overflow == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_LOAD_INTRINSIC_FUNCTION_FAILED),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Cannot get the llvm::Intrinsic::sadd_with_overflow function!")));
|
|
}
|
|
llvm::Value* aggres = builder.CreateCall(func_sonicadd_overflow, {resval, midval});
|
|
llvm::Value* oflag = builder.CreateExtractValue(aggres, 1);
|
|
builder.CreateCondBr(oflag, bioverflow_bb, normal_bb);
|
|
|
|
builder.SetInsertPoint(bioverflow_bb);
|
|
exprscale = GetAlignedScale(estate->expr);
|
|
llvm::Value* ascale = llvmCodeGen->getIntConstant(CHAROID, exprscale);
|
|
llvm::Value* bioverres = WrapmakeNumeric64CodeGen(&builder, resval, ascale);
|
|
builder.CreateBr(numsum_bb);
|
|
|
|
builder.SetInsertPoint(numsum_bb);
|
|
llvm::PHINode* numres = builder.CreatePHI(int64Type, 2);
|
|
numres->addIncoming(result, flag_then[i]);
|
|
numres->addIncoming(bioverres, bioverflow_bb);
|
|
llvm::Value* evalval = (llvm::Value*)numres;
|
|
|
|
llvm::Function* func_vsnumericsum =
|
|
llvmCodeGen->module()->getFunction("Jitted_sonic_numericsum");
|
|
if (NULL == func_vsnumericsum) {
|
|
func_vsnumericsum = vsnumeric_sum_codegen(aggref);
|
|
}
|
|
sdata[i] = builder.CreateBitCast(sdata[i], sonicEncodingDatumArrayPtrType);
|
|
builder.CreateCall(func_vsnumericsum, {sdata[i], locidx, evalval});
|
|
builder.CreateBr(agg_end);
|
|
|
|
/* if there is no overflow, extract result directly */
|
|
builder.SetInsertPoint(normal_bb);
|
|
llvm::Value* sumval = builder.CreateExtractValue(aggres, 0);
|
|
builder.CreateAlignedStore(sumval, sonicptr, 8);
|
|
builder.CreateBr(agg_end);
|
|
|
|
builder.SetInsertPoint(agg_end);
|
|
} else {
|
|
llvm::Function* func_vsnumericsum =
|
|
llvmCodeGen->module()->getFunction("Jitted_sonic_numericsum");
|
|
if (NULL == func_vsnumericsum) {
|
|
func_vsnumericsum = vsnumeric_sum_codegen(aggref);
|
|
}
|
|
sdata[i] = builder.CreateBitCast(sdata[i], sonicEncodingDatumArrayPtrType);
|
|
builder.CreateCall(func_vsnumericsum, {sdata[i], locidx, result});
|
|
}
|
|
} break;
|
|
case NUMERICAVGFUNCOID: {
|
|
/*
|
|
* If aggref can be evaluated in fast path and just be
|
|
* simple vars, use the result from batch.
|
|
*/
|
|
if (fast_aggref && (numSimpleVars > 0)) {
|
|
DEFINE_BLOCK(agg_then, jitted_sonicbatchagg);
|
|
DEFINE_BLOCK(agg_else, jitted_sonicbatchagg);
|
|
DEFINE_BLOCK(agg_end, jitted_sonicbatchagg);
|
|
DEFINE_BLOCK(normal_bb, jitted_sonicbatchagg);
|
|
DEFINE_BLOCK(bisum_bblock, jitted_sonicbatchagg);
|
|
DEFINE_BLOCK(numsum_bblock, jitted_sonicbatchagg);
|
|
|
|
/* get sonic data according to sonic datum array and locidx */
|
|
/* calculate arrIdx and atomIdx */
|
|
Vals[0] = int64_0;
|
|
Vals[1] = int32_pos_sdarray_nbit;
|
|
nbit = builder.CreateInBoundsGEP(sdata[i], Vals);
|
|
nbit = builder.CreateAlignedLoad(nbit, 4, "arridx");
|
|
arrIdx = builder.CreateLShr(locidx, nbit);
|
|
arrIdx = builder.CreateSExt(arrIdx, int64Type);
|
|
|
|
Vals[1] = int32_pos_sdarray_atomsize;
|
|
atomsize = builder.CreateInBoundsGEP(sdata[i], Vals);
|
|
atomsize = builder.CreateAlignedLoad(atomsize, 4, "atomsize");
|
|
atomsize = builder.CreateSub(atomsize, int32_1);
|
|
atomIdx = builder.CreateAnd(atomsize, locidx);
|
|
atomIdx = builder.CreateSExt(atomIdx, int64Type);
|
|
|
|
/* get sdata[aggidx]->m_arr[arrIdx] */
|
|
Vals[1] = int32_pos_sdarray_arr;
|
|
atom = builder.CreateInBoundsGEP(sdata[i], Vals);
|
|
atom = builder.CreateAlignedLoad(atom, 8, "atomptr");
|
|
atom = builder.CreateInBoundsGEP(atom, arrIdx);
|
|
atom = builder.CreateAlignedLoad(atom, 8, "atom");
|
|
|
|
/* get address of sdata->m_arr[arrIdx]->data[atomIdx] */
|
|
Vals[1] = int32_pos_atom_data;
|
|
data = builder.CreateInBoundsGEP(atom, Vals);
|
|
data = builder.CreateAlignedLoad(data, 8, "data");
|
|
data = builder.CreateBitCast(data, int64PtrType);
|
|
llvm::Value* dataptr = builder.CreateInBoundsGEP(data, atomIdx);
|
|
|
|
/* get scount->m_arr[arrIdx] */
|
|
Vals[1] = int32_pos_sdarray_arr;
|
|
cntatom = builder.CreateInBoundsGEP(scount, Vals);
|
|
cntatom = builder.CreateAlignedLoad(cntatom, 8, "cntatomptr");
|
|
cntatom = builder.CreateInBoundsGEP(cntatom, arrIdx);
|
|
cntatom = builder.CreateAlignedLoad(cntatom, 8, "cntatom");
|
|
|
|
/* get address of scount->m_arr[arrIdx]->data[atomIdx] */
|
|
Vals[1] = int32_pos_atom_data;
|
|
cntdata = builder.CreateInBoundsGEP(cntatom, Vals);
|
|
cntdata = builder.CreateAlignedLoad(cntdata, 8, "cntdata");
|
|
cntdata = builder.CreateBitCast(cntdata, int64PtrType);
|
|
llvm::Value* cntptr = builder.CreateInBoundsGEP(cntdata, atomIdx);
|
|
|
|
/* get sdata[aggidx]->m_arr[arrIdx]->nullFlag[atomIdx] */
|
|
Vals[1] = int32_pos_atom_nullflag;
|
|
llvm::Value* dataflag = builder.CreateInBoundsGEP(atom, Vals);
|
|
dataflag = builder.CreateAlignedLoad(dataflag, 8, "dataflagptr");
|
|
dataflag = builder.CreateInBoundsGEP(dataflag, atomIdx);
|
|
|
|
/* get scount>m_arr[arrIdx]->nullFlag[atomIdx] */
|
|
Vals[1] = int32_pos_atom_nullflag;
|
|
llvm::Value* cntflag = builder.CreateInBoundsGEP(cntatom, Vals);
|
|
cntflag = builder.CreateAlignedLoad(cntflag, 8, "cntflagptr");
|
|
cntflag = builder.CreateInBoundsGEP(cntflag, atomIdx);
|
|
|
|
/* Now load the sonic data flag and check it */
|
|
tmpval = builder.CreateAlignedLoad(dataflag, 1, "dataFlag");
|
|
tmpval = builder.CreateAnd(tmpval, int8_1);
|
|
tmpval = builder.CreateICmpEQ(tmpval, int8_0);
|
|
builder.CreateCondBr(tmpval, agg_else, agg_then);
|
|
|
|
/* sonic data is null, store the value in data and count */
|
|
builder.SetInsertPoint(agg_then);
|
|
/* do leftarg = DatumGetBINumeric(pVal[i]) */
|
|
tmpval = DatumGetBINumericCodeGen(&builder, result);
|
|
tmpval = builder.CreatePtrToInt(tmpval, int64Type);
|
|
/* corresponding to addVariable(context, NumericGetDatum(leftarg)) */
|
|
tmpval = WrapaddVariableCodeGen(&builder, hcxt, tmpval);
|
|
builder.CreateAlignedStore(tmpval, dataptr, 8);
|
|
|
|
/* count set to be one */
|
|
builder.CreateAlignedStore(int64_1, cntptr, 8);
|
|
|
|
/* set cell flag */
|
|
builder.CreateAlignedStore(int8_0, dataflag, 1);
|
|
builder.CreateAlignedStore(int8_0, cntflag, 1);
|
|
|
|
/* turn to next basicblock or end this */
|
|
if (i == numaggs - 1)
|
|
builder.CreateBr(for_inc);
|
|
else
|
|
builder.CreateBr(agg_bb[i + 1]);
|
|
|
|
/* cell be not null, do aggregation */
|
|
builder.SetInsertPoint(agg_else);
|
|
/*
|
|
* When fast_aggref is true and numSimpleVars is greater than zero,
|
|
* the expr is numeric type var. Convert this numeric type data to
|
|
* SiNumeric data to get the value.
|
|
*/
|
|
llvm::Value* bires = DatumGetBINumericCodeGen(&builder, result);
|
|
|
|
/* extract the header of result to check if it is BINumeric */
|
|
Vals4[0] = int64_0;
|
|
Vals4[1] = int32_1;
|
|
Vals4[2] = int32_0;
|
|
Vals4[3] = int32_0;
|
|
tmpval = builder.CreateInBoundsGEP(bires, Vals4);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 2, "biheader");
|
|
llvm::Value* rflag = builder.CreateAnd(tmpval, val_mask);
|
|
|
|
/* get leftdata[0] and convert to Numeric to get lflag */
|
|
llvm::Value* realdata = builder.CreateAlignedLoad(dataptr, 8, "dataval");
|
|
llvm::Value* leftarg = builder.CreateIntToPtr(realdata, numericPtrType);
|
|
tmpval = builder.CreateInBoundsGEP(leftarg, Vals4);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 2, "sonicheader");
|
|
llvm::Value* lflag = builder.CreateAnd(tmpval, val_mask);
|
|
|
|
/* check if either of them is not BI64 */
|
|
llvm::Value* oparg1 = builder.CreateICmpEQ(lflag, val_binum64);
|
|
llvm::Value* oparg2 = builder.CreateICmpEQ(rflag, val_binum64);
|
|
llvm::Value* bothbi64 = builder.CreateAnd(oparg1, oparg2);
|
|
|
|
/* use fast path only when both args are bi64 */
|
|
builder.CreateCondBr(bothbi64, bisum_bblock, numsum_bblock);
|
|
|
|
builder.SetInsertPoint(bisum_bblock);
|
|
/* extract the actual data of numeric only when value is not null */
|
|
Vals4[0] = int64_0;
|
|
Vals4[1] = int32_1;
|
|
Vals4[2] = int32_0;
|
|
Vals4[3] = int32_1;
|
|
tmpval = builder.CreateInBoundsGEP(bires, Vals4);
|
|
tmpval = builder.CreateBitCast(tmpval, int64PtrType);
|
|
llvm::Value* resval = builder.CreateAlignedLoad(tmpval, 8, "value");
|
|
|
|
llvm::Value* sonicptr = builder.CreateAdd(realdata, int64_6);
|
|
sonicptr = builder.CreateIntToPtr(sonicptr, int64PtrType);
|
|
llvm::Value* midval = builder.CreateAlignedLoad(sonicptr, 8);
|
|
|
|
/* check overflow */
|
|
llvm::Type* Intrinsic_Tys[] = {int64Type};
|
|
llvm::Function* func_sonicadd_overflow =
|
|
llvm::Intrinsic::getDeclaration(mod, llvm::Intrinsic::sadd_with_overflow, Intrinsic_Tys);
|
|
if (func_sonicadd_overflow == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_LOAD_INTRINSIC_FUNCTION_FAILED),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Cannot get the llvm::Intrinsic::sadd_with_overflow function!\n")));
|
|
}
|
|
llvm::Value* aggres = builder.CreateCall(func_sonicadd_overflow, {resval, midval});
|
|
llvm::Value* oflag = builder.CreateExtractValue(aggres, 1);
|
|
builder.CreateCondBr(oflag, numsum_bblock, normal_bb);
|
|
|
|
builder.SetInsertPoint(numsum_bblock);
|
|
llvm::Function* func_vsnumericavg =
|
|
llvmCodeGen->module()->getFunction("Jitted_sonic_numericavg");
|
|
if (NULL == func_vsnumericavg) {
|
|
func_vsnumericavg = vsnumeric_avg_codegen(aggref);
|
|
}
|
|
sdata[i] = builder.CreateBitCast(sdata[i], sonicEncodingDatumArrayPtrType);
|
|
builder.CreateCall(func_vsnumericavg, {sdata[i], scount, locidx, result});
|
|
builder.CreateBr(agg_end);
|
|
|
|
builder.SetInsertPoint(normal_bb);
|
|
llvm::Value* sumval = builder.CreateExtractValue(aggres, 0);
|
|
builder.CreateAlignedStore(sumval, sonicptr, 8);
|
|
|
|
/* cell->m_val[idx+1].val++ */
|
|
tmpval = builder.CreateAlignedLoad(cntptr, 8, "count");
|
|
tmpval = builder.CreateAdd(tmpval, int64_1);
|
|
builder.CreateAlignedStore(tmpval, cntptr, 8);
|
|
builder.CreateBr(agg_end);
|
|
|
|
builder.SetInsertPoint(agg_end);
|
|
} else if (fast_aggref) {
|
|
/*
|
|
* If aggref can be evaluated in fast path and be numeric
|
|
* expressions, use the result from fastexpr.
|
|
*/
|
|
Assert(bisum_bb != NULL);
|
|
Assert(numsum_bb != NULL);
|
|
DEFINE_BLOCK(agg_end, jitted_sonicbatchagg);
|
|
DEFINE_BLOCK(agg_then, jitted_sonicbatchagg);
|
|
DEFINE_BLOCK(agg_else, jitted_sonicbatchagg);
|
|
DEFINE_BLOCK(normal_bb, jitted_sonicbatchagg);
|
|
DEFINE_BLOCK(expr_bisum_bb, jitted_sonicbatchagg);
|
|
DEFINE_BLOCK(bioverflow_bb, jitted_sonicbatchagg);
|
|
|
|
builder.CreateBr(numsum_bb);
|
|
|
|
builder.SetInsertPoint(bisum_bb);
|
|
llvm::Value* resval = builder.CreateExtractValue(expres, 1);
|
|
|
|
/* get sonic data according to sonic datum array and locidx */
|
|
/* calculate arrIdx and atomIdx */
|
|
Vals[0] = int64_0;
|
|
Vals[1] = int32_pos_sdarray_nbit;
|
|
nbit = builder.CreateInBoundsGEP(sdata[i], Vals);
|
|
nbit = builder.CreateAlignedLoad(nbit, 4, "arridx");
|
|
arrIdx = builder.CreateLShr(locidx, nbit);
|
|
arrIdx = builder.CreateSExt(arrIdx, int64Type);
|
|
|
|
Vals[1] = int32_pos_sdarray_atomsize;
|
|
atomsize = builder.CreateInBoundsGEP(sdata[i], Vals);
|
|
atomsize = builder.CreateAlignedLoad(atomsize, 4, "atomsize");
|
|
atomsize = builder.CreateSub(atomsize, int32_1);
|
|
atomIdx = builder.CreateAnd(atomsize, locidx);
|
|
atomIdx = builder.CreateSExt(atomIdx, int64Type);
|
|
|
|
/* get sdata[aggidx]->m_arr[arrIdx] */
|
|
Vals[1] = int32_pos_sdarray_arr;
|
|
atom = builder.CreateInBoundsGEP(sdata[i], Vals);
|
|
atom = builder.CreateAlignedLoad(atom, 8, "atomptr");
|
|
atom = builder.CreateInBoundsGEP(atom, arrIdx);
|
|
atom = builder.CreateAlignedLoad(atom, 8, "atom");
|
|
|
|
/* get address of sdata->m_arr[arrIdx]->data[atomIdx] */
|
|
Vals[1] = int32_pos_atom_data;
|
|
data = builder.CreateInBoundsGEP(atom, Vals);
|
|
data = builder.CreateAlignedLoad(data, 8, "data");
|
|
data = builder.CreateBitCast(data, int64PtrType);
|
|
llvm::Value* dataptr = builder.CreateInBoundsGEP(data, atomIdx);
|
|
|
|
/* get scount->m_arr[arrIdx] */
|
|
Vals[1] = int32_pos_sdarray_arr;
|
|
cntatom = builder.CreateInBoundsGEP(scount, Vals);
|
|
cntatom = builder.CreateAlignedLoad(cntatom, 8, "cntatomptr");
|
|
cntatom = builder.CreateInBoundsGEP(cntatom, arrIdx);
|
|
cntatom = builder.CreateAlignedLoad(cntatom, 8, "cntatom");
|
|
|
|
/* get address of scount->m_arr[arrIdx]->data[atomIdx] */
|
|
Vals[1] = int32_pos_atom_data;
|
|
cntdata = builder.CreateInBoundsGEP(cntatom, Vals);
|
|
cntdata = builder.CreateAlignedLoad(cntdata, 8, "cntdata");
|
|
cntdata = builder.CreateBitCast(cntdata, int64PtrType);
|
|
llvm::Value* cntptr = builder.CreateInBoundsGEP(cntdata, atomIdx);
|
|
|
|
/* get sdata[aggidx]->m_arr[arrIdx]->nullFlag[atomIdx] */
|
|
Vals[1] = int32_pos_atom_nullflag;
|
|
llvm::Value* dataflag = builder.CreateInBoundsGEP(atom, Vals);
|
|
dataflag = builder.CreateAlignedLoad(dataflag, 8, "dataflagptr");
|
|
dataflag = builder.CreateInBoundsGEP(dataflag, atomIdx);
|
|
|
|
/* get scount>m_arr[arrIdx]->nullFlag[atomIdx] */
|
|
Vals[1] = int32_pos_atom_nullflag;
|
|
llvm::Value* cntflag = builder.CreateInBoundsGEP(cntatom, Vals);
|
|
cntflag = builder.CreateAlignedLoad(cntflag, 8, "cntflagptr");
|
|
cntflag = builder.CreateInBoundsGEP(cntflag, atomIdx);
|
|
|
|
/* Now load the sonic data flag and check it */
|
|
tmpval = builder.CreateAlignedLoad(dataflag, 1, "dataFlag");
|
|
tmpval = builder.CreateAnd(tmpval, int8_1);
|
|
tmpval = builder.CreateICmpEQ(tmpval, int8_0);
|
|
builder.CreateCondBr(tmpval, agg_else, agg_then);
|
|
|
|
/* cell be null, add Variable */
|
|
builder.SetInsertPoint(agg_then);
|
|
exprscale = GetAlignedScale(estate->expr);
|
|
llvm::Value* alignedscale = llvmCodeGen->getIntConstant(CHAROID, exprscale);
|
|
/*
|
|
* should make a new context to record the result : the
|
|
* following code corresponding to:
|
|
* 'leftarg = DatumGetBINumeric(pVal[i]);
|
|
* data->setValue(NumericGetDatum(leftarg), false, arrIdx, atomIdx);'.
|
|
*/
|
|
tmpval = WrapmakeNumeric64CodeGen(&builder, resval, alignedscale);
|
|
tmpval = DatumGetBINumericCodeGen(&builder, tmpval);
|
|
tmpval = builder.CreatePtrToInt(tmpval, int64Type);
|
|
tmpval = WrapaddVariableCodeGen(&builder, hcxt, tmpval);
|
|
builder.CreateAlignedStore(tmpval, dataptr, 8);
|
|
/* count set to be one */
|
|
builder.CreateAlignedStore(int64_1, cntptr, 8);
|
|
/* set the flag of hashcell */
|
|
builder.CreateAlignedStore(int8_0, dataflag, 1);
|
|
builder.CreateAlignedStore(int8_0, cntflag, 1);
|
|
|
|
/* turn to next basicblock */
|
|
if (i == numaggs - 1)
|
|
builder.CreateBr(for_inc);
|
|
else
|
|
builder.CreateBr(agg_bb[i + 1]);
|
|
|
|
builder.SetInsertPoint(agg_else);
|
|
llvm::Value* realdata = builder.CreateAlignedLoad(dataptr, 8, "cell_val");
|
|
|
|
/* first make sure the value in cell is BI64 format */
|
|
Vals4[0] = int64_0;
|
|
Vals4[1] = int32_1;
|
|
Vals4[2] = int32_0;
|
|
Vals4[3] = int32_0;
|
|
llvm::Value* leftflag = builder.CreateIntToPtr(realdata, numericPtrType);
|
|
tmpval = builder.CreateInBoundsGEP(leftflag, Vals4);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 2, "leftheader");
|
|
llvm::Value* biflag = builder.CreateAnd(tmpval, val_mask);
|
|
llvm::Value* isbi64 = builder.CreateICmpEQ(biflag, val_binum64);
|
|
builder.CreateCondBr(isbi64, expr_bisum_bb, bioverflow_bb);
|
|
|
|
/*
|
|
* do aggregation directly only when both expr value
|
|
* and cell value is bi64.
|
|
*/
|
|
builder.SetInsertPoint(expr_bisum_bb);
|
|
llvm::Value* sonicptr = builder.CreateAdd(realdata, int64_6);
|
|
sonicptr = builder.CreateIntToPtr(sonicptr, int64PtrType);
|
|
llvm::Value* midval = builder.CreateAlignedLoad(sonicptr, 8);
|
|
|
|
/* check overflow */
|
|
llvm::Type* Intrinsic_Tys[] = {int64Type};
|
|
llvm::Function* func_sonicadd_overflow =
|
|
llvm::Intrinsic::getDeclaration(mod, llvm::Intrinsic::sadd_with_overflow, Intrinsic_Tys);
|
|
if (func_sonicadd_overflow == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_LOAD_INTRINSIC_FUNCTION_FAILED),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Cannot get the llvm::Intrinsic::sadd_with_overflow function!\n")));
|
|
}
|
|
llvm::Value* aggres = builder.CreateCall(func_sonicadd_overflow, {resval, midval});
|
|
llvm::Value* oflag = builder.CreateExtractValue(aggres, 1);
|
|
builder.CreateCondBr(oflag, bioverflow_bb, normal_bb);
|
|
|
|
/* make numeric64 when meet overflow */
|
|
builder.SetInsertPoint(bioverflow_bb);
|
|
exprscale = GetAlignedScale(estate->expr);
|
|
llvm::Value* ascale = llvmCodeGen->getIntConstant(CHAROID, exprscale);
|
|
llvm::Value* bioverres = WrapmakeNumeric64CodeGen(&builder, resval, ascale);
|
|
builder.CreateBr(numsum_bb);
|
|
|
|
builder.SetInsertPoint(numsum_bb);
|
|
llvm::PHINode* numres = builder.CreatePHI(int64Type, 2);
|
|
numres->addIncoming(result, flag_then[i]);
|
|
numres->addIncoming(bioverres, bioverflow_bb);
|
|
llvm::Value* evalval = (llvm::Value*)numres;
|
|
|
|
llvm::Function* func_vsnumericavg =
|
|
llvmCodeGen->module()->getFunction("Jitted_sonic_numericavg");
|
|
if (NULL == func_vsnumericavg) {
|
|
func_vsnumericavg = vsnumeric_avg_codegen(aggref);
|
|
}
|
|
sdata[i] = builder.CreateBitCast(sdata[i], sonicEncodingDatumArrayPtrType);
|
|
builder.CreateCall(func_vsnumericavg, {sdata[i], scount, locidx, evalval});
|
|
builder.CreateBr(agg_end);
|
|
|
|
/* if there is no overflow, extract result directly */
|
|
builder.SetInsertPoint(normal_bb);
|
|
llvm::Value* sumval = builder.CreateExtractValue(aggres, 0);
|
|
builder.CreateAlignedStore(sumval, sonicptr, 8);
|
|
|
|
/* cell->m_val[idx+1].val++ */
|
|
tmpval = builder.CreateAlignedLoad(cntptr, 8, "count");
|
|
tmpval = builder.CreateAdd(tmpval, int64_1);
|
|
builder.CreateAlignedStore(tmpval, cntptr, 8);
|
|
builder.CreateBr(agg_end);
|
|
|
|
builder.SetInsertPoint(agg_end);
|
|
} else {
|
|
llvm::Function* func_vsnumericavg =
|
|
llvmCodeGen->module()->getFunction("Jitted_sonic_numericavg");
|
|
if (NULL == func_vsnumericavg) {
|
|
func_vsnumericavg = vsnumeric_avg_codegen(aggref);
|
|
}
|
|
sdata[i] = builder.CreateBitCast(sdata[i], sonicEncodingDatumArrayPtrType);
|
|
builder.CreateCall(func_vsnumericavg, {sdata[i], scount, locidx, result});
|
|
}
|
|
} break;
|
|
default:
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Unsupported agg function %u!", aggref->aggfnoid)));
|
|
break;
|
|
}
|
|
|
|
if (i == numaggs - 1)
|
|
builder.CreateBr(for_inc);
|
|
else
|
|
builder.CreateBr(agg_bb[i + 1]);
|
|
|
|
/* if the current flag is null, turn to next agg */
|
|
builder.SetInsertPoint(flag_else[i]);
|
|
if (i == numaggs - 1)
|
|
builder.CreateBr(for_inc);
|
|
else
|
|
builder.CreateBr(agg_bb[i + 1]);
|
|
}
|
|
}
|
|
|
|
/* codegen in the for_inc basic block: compare the loop index with nrows */
|
|
builder.SetInsertPoint(for_inc);
|
|
tmpval = builder.CreateTrunc(idx_next, int32Type);
|
|
tmpval = builder.CreateICmpEQ(tmpval, nValues);
|
|
builder.CreateCondBr(tmpval, for_end, for_body);
|
|
|
|
/* codegen in for_end basic block: just return void */
|
|
builder.SetInsertPoint(for_end);
|
|
(void)VecExprCodeGen::MemCxtSwitToCodeGen(&builder, agg_oldcontext);
|
|
WrapResetEContextCodeGen(&builder, mecontext);
|
|
WrapResetEContextCodeGen(&builder, econtext);
|
|
builder.CreateRetVoid();
|
|
|
|
pfree_ext(aggIdxList);
|
|
pfree_ext(sdata);
|
|
pfree_ext(agg_bb);
|
|
pfree_ext(flag_then);
|
|
pfree_ext(flag_else);
|
|
pfree_ext(batch_vals);
|
|
pfree_ext(batch_flag);
|
|
|
|
llvmCodeGen->FinalizeFunction(jitted_sonicbatchagg, node->ss.ps.plan->plan_node_id);
|
|
|
|
return jitted_sonicbatchagg;
|
|
}
|
|
|
|
void VecHashAggCodeGen::WarpAllocHashSlotCodeGen(GsCodeGen::LlvmBuilder* ptrbuilder, llvm::Value* hAggRunner,
|
|
llvm::Value* batch, llvm::Value* idx, llvm::Value* keysimple)
|
|
{
|
|
GsCodeGen* llvmCodeGen = (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj;
|
|
|
|
/* Define data types and some llvm consts */
|
|
DEFINE_CG_VOIDTYPE(voidType);
|
|
DEFINE_CG_TYPE(int32Type, INT4OID);
|
|
DEFINE_CG_PTRTYPE(vectorBatchPtrType, "class.VectorBatch");
|
|
DEFINE_CG_PTRTYPE(hashAggRunnerPtrType, "class.HashAggRunner");
|
|
|
|
llvm::Function* func_allochashslot = llvmCodeGen->module()->getFunction("IRAllocHashSlot");
|
|
if (NULL == func_allochashslot) {
|
|
GsCodeGen::FnPrototype fn_prototype(llvmCodeGen, "IRAllocHashSlot", voidType);
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("hAggRunner", hashAggRunnerPtrType));
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("batch", vectorBatchPtrType));
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("idx", int32Type));
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("keysimple", int32Type));
|
|
func_allochashslot = fn_prototype.generatePrototype(NULL, NULL);
|
|
llvm::sys::DynamicLibrary::AddSymbol("IRAllocHashSlot", (void*)WrapAllocHashSlot);
|
|
}
|
|
|
|
idx = ptrbuilder->CreateTrunc(idx, int32Type);
|
|
ptrbuilder->CreateCall(func_allochashslot, {hAggRunner, batch, idx, keysimple});
|
|
|
|
return;
|
|
}
|
|
|
|
void VecHashAggCodeGen::WarpSglTblAllocHashSlotCodeGen(GsCodeGen::LlvmBuilder* ptrbuilder, llvm::Value* hAggRunner,
|
|
llvm::Value* batch, llvm::Value* idx, llvm::Value* keysimple)
|
|
{
|
|
GsCodeGen* llvmCodeGen = (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj;
|
|
|
|
/* Define data types and some llvm consts */
|
|
DEFINE_CG_VOIDTYPE(voidType);
|
|
DEFINE_CG_TYPE(int32Type, INT4OID);
|
|
DEFINE_CG_PTRTYPE(vectorBatchPtrType, "class.VectorBatch");
|
|
DEFINE_CG_PTRTYPE(hashAggRunnerPtrType, "class.HashAggRunner");
|
|
|
|
llvm::Function* func_sgltblallochashslot = llvmCodeGen->module()->getFunction("IRSglTblAllocHashSlot");
|
|
if (NULL == func_sgltblallochashslot) {
|
|
GsCodeGen::FnPrototype fn_prototype(llvmCodeGen, "IRSglTblAllocHashSlot", voidType);
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("hAggRunner", hashAggRunnerPtrType));
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("batch", vectorBatchPtrType));
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("idx", int32Type));
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("keysimple", int32Type));
|
|
func_sgltblallochashslot = fn_prototype.generatePrototype(NULL, NULL);
|
|
llvm::sys::DynamicLibrary::AddSymbol("IRSglTblAllocHashSlot", (void*)WrapSglTblAllocHashSlot);
|
|
}
|
|
|
|
idx = ptrbuilder->CreateTrunc(idx, int32Type);
|
|
ptrbuilder->CreateCall(func_sgltblallochashslot, {hAggRunner, batch, idx, keysimple});
|
|
|
|
return;
|
|
}
|
|
|
|
void VecHashAggCodeGen::WrapResetEContextCodeGen(GsCodeGen::LlvmBuilder* ptrbuilder, llvm::Value* econtext)
|
|
{
|
|
GsCodeGen* llvmCodeGen = (GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj;
|
|
|
|
/* Define data types and some llvm consts */
|
|
DEFINE_CG_VOIDTYPE(voidType);
|
|
DEFINE_CG_PTRTYPE(ExprContextPtrType, "struct.ExprContext");
|
|
|
|
/* Associate with ResetExprContext */
|
|
llvm::Function* func_resetec = llvmCodeGen->module()->getFunction("IRResetExprContext");
|
|
if (NULL == func_resetec) {
|
|
GsCodeGen::FnPrototype fn_prototype(llvmCodeGen, "IRResetExprContext", voidType);
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("econtext", ExprContextPtrType));
|
|
func_resetec = fn_prototype.generatePrototype(NULL, NULL);
|
|
llvm::sys::DynamicLibrary::AddSymbol("IRResetExprContext", (void*)WrapResetExprContext);
|
|
}
|
|
|
|
ptrbuilder->CreateCall(func_resetec, econtext);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* @Description : Generate the function to prefetch the hash cell ahead
|
|
* of 2, which means hint fetch loc[i+2]. This function
|
|
* is called by BatchAggregation.
|
|
* @return : return llvm prefetch function for batchaggregation.
|
|
*/
|
|
llvm::Function* prefetchBatchAggregationCodeGen()
|
|
{
|
|
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::Value* tmpval = NULL;
|
|
llvm::Value* llvmargs[3];
|
|
llvm::Function* jitted_batchagg_prefetch = NULL;
|
|
|
|
/* Define data types and some llvm consts */
|
|
DEFINE_CG_VOIDTYPE(voidType);
|
|
DEFINE_CG_TYPE(int64Type, INT8OID);
|
|
DEFINE_CG_PTRTYPE(int8PtrType, CHAROID);
|
|
DEFINE_CG_PTRTYPE(hashCellPtrType, "struct.hashCell");
|
|
llvm::Type* hashCellPtrPtrType = llvmCodeGen->getPtrType(hashCellPtrType);
|
|
|
|
DEFINE_CGVAR_INT32(int32_0, 0);
|
|
DEFINE_CGVAR_INT32(int32_1, 1);
|
|
DEFINE_CGVAR_INT32(int32_3, 3);
|
|
DEFINE_CGVAR_INT64(int64_distance, PREFETCH_BATCHAGGREGATION_DISTANCE);
|
|
|
|
GsCodeGen::FnPrototype fn_prototype(llvmCodeGen, "prefetchBatchAggregation", voidType);
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("Loc", hashCellPtrPtrType));
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("Idx", int64Type));
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("nrows", int64Type));
|
|
jitted_batchagg_prefetch = fn_prototype.generatePrototype(&builder, &llvmargs[0]);
|
|
|
|
llvm::Value* Loc = llvmargs[0];
|
|
llvm::Value* idx = llvmargs[1];
|
|
llvm::Value* nrows = llvmargs[2];
|
|
|
|
DEFINE_BLOCK(prefetch_bb, jitted_batchagg_prefetch);
|
|
DEFINE_BLOCK(ret_bb, jitted_batchagg_prefetch);
|
|
|
|
llvm::Value* prefetchIdx = builder.CreateAdd(idx, int64_distance);
|
|
tmpval = builder.CreateICmpSLT(prefetchIdx, nrows);
|
|
builder.CreateCondBr(tmpval, prefetch_bb, ret_bb);
|
|
|
|
builder.SetInsertPoint(prefetch_bb);
|
|
llvm::Function* fn_prefetch = llvm::Intrinsic::getDeclaration(llvmCodeGen->module(), llvm::Intrinsic::prefetch);
|
|
if (fn_prefetch == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_LOAD_INTRINSIC_FUNCTION_FAILED),
|
|
errmsg("Failed to get llvm function Intrinsic::prefetch!\n")));
|
|
}
|
|
tmpval = builder.CreateInBoundsGEP(Loc, prefetchIdx);
|
|
tmpval = builder.CreateAlignedLoad(tmpval, 8, "hashCell");
|
|
tmpval = builder.CreateBitCast(tmpval, int8PtrType);
|
|
builder.CreateCall(fn_prefetch, {tmpval, int32_0, int32_3, int32_1});
|
|
builder.CreateBr(ret_bb);
|
|
|
|
builder.SetInsertPoint(ret_bb);
|
|
builder.CreateRetVoid();
|
|
|
|
llvmCodeGen->FinalizeFunction(jitted_batchagg_prefetch);
|
|
|
|
return jitted_batchagg_prefetch;
|
|
}
|
|
|
|
/*
|
|
* @Description : Generate the function to prefetch the hash cell ahead
|
|
* of 2, which means hint fetch loc[i+2], and prefetch the
|
|
* address of m_data ahead of 4, which means hint fetch
|
|
* m_data[m_cacheLoc[i+4]]. This function is called in the
|
|
* loop of the matching key.
|
|
* @return : return llvm function for building hash table in batchagg.
|
|
*/
|
|
llvm::Function* prefetchAggHashingCodeGen()
|
|
{
|
|
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::Value* tmpval = NULL;
|
|
llvm::Value* llvmargs[3];
|
|
llvm::Function* jitted_hashing_prefetch = 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(hashAggRunnerPtrType, "class.HashAggRunner");
|
|
|
|
DEFINE_CGVAR_INT32(int32_0, 0);
|
|
DEFINE_CGVAR_INT32(int32_1, 1);
|
|
DEFINE_CGVAR_INT32(int32_3, 3);
|
|
DEFINE_CGVAR_INT32(int32_pos_hAggR_hashVal, pos_hAggR_hashVal);
|
|
DEFINE_CGVAR_INT32(int32_pos_hAggR_hSegTbl, pos_hAggR_hSegTbl);
|
|
DEFINE_CGVAR_INT32(int32_pos_hAggR_hsegmax, pos_hAggR_hsegmax);
|
|
DEFINE_CGVAR_INT32(int32_pos_hAggR_hashSize, pos_hAggR_hashSize);
|
|
DEFINE_CGVAR_INT64(int64_0, 0);
|
|
DEFINE_CGVAR_INT64(int64_1, 1);
|
|
DEFINE_CGVAR_INT64(int64_distance1, PREFETCH_AGGHASHING_DISTANCE1);
|
|
DEFINE_CGVAR_INT64(int64_distance2, PREFETCH_AGGHASHING_DISTANCE2);
|
|
|
|
llvm::Value* Vals2[2] = {int64_0, int32_0};
|
|
llvm::Value* Vals3[3] = {int64_0, int32_0, int32_0};
|
|
|
|
GsCodeGen::FnPrototype fn_prototype(llvmCodeGen, "prefetchAggHashing", voidType);
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("hAggRunner", hashAggRunnerPtrType));
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("Idx", int64Type));
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("nrows", int64Type));
|
|
jitted_hashing_prefetch = fn_prototype.generatePrototype(&builder, &llvmargs[0]);
|
|
|
|
llvm::Value* hAggRunner = llvmargs[0];
|
|
llvm::Value* idx = llvmargs[1];
|
|
llvm::Value* nrows = llvmargs[2];
|
|
|
|
DEFINE_BLOCK(prefetch_hashData_bb, jitted_hashing_prefetch);
|
|
DEFINE_BLOCK(prefetch_cell_bb, jitted_hashing_prefetch);
|
|
DEFINE_BLOCK(ret_bb, jitted_hashing_prefetch);
|
|
|
|
/*
|
|
* try to prefetch hashData[m_cacheLoc[i+4]] : only do this when we have
|
|
* enough rows.
|
|
*/
|
|
llvm::Value* prefetchIdx = builder.CreateAdd(idx, int64_distance1);
|
|
tmpval = builder.CreateICmpSLT(prefetchIdx, nrows);
|
|
builder.CreateCondBr(tmpval, prefetch_hashData_bb, ret_bb);
|
|
|
|
builder.SetInsertPoint(prefetch_hashData_bb);
|
|
/* mask = hashAggRunner.m_hashSize - 1 */
|
|
Vals2[1] = int32_pos_hAggR_hashSize;
|
|
tmpval = builder.CreateInBoundsGEP(hAggRunner, Vals2);
|
|
llvm::Value* maskval = builder.CreateAlignedLoad(tmpval, 8, "m_hashSize");
|
|
maskval = builder.CreateSub(maskval, int64_1, "mask");
|
|
|
|
/* calculate m_cacheLoc[i+4] = m_hashVal[i+4] & mask */
|
|
llvm::Value* next_hashData_idx = builder.CreateAdd(idx, int64_distance1);
|
|
Vals3[1] = int32_pos_hAggR_hashVal;
|
|
Vals3[2] = next_hashData_idx;
|
|
llvm::Value* hashValSlot = builder.CreateInBoundsGEP(hAggRunner, Vals3);
|
|
llvm::Value* hashVal64 = builder.CreateAlignedLoad(hashValSlot, 8, "hashVal");
|
|
llvm::Value* cacheLocVal = builder.CreateAnd(hashVal64, maskval);
|
|
|
|
/* get hashData = hashAggRunner->m_hashData.tbl_data */
|
|
/* calculate nsegs and pos according to m_cacheLoc[i+4] */
|
|
Vals2[0] = int64_0;
|
|
Vals2[1] = int32_pos_hAggR_hsegmax;
|
|
llvm::Value* segmaxval = builder.CreateInBoundsGEP(hAggRunner, Vals2);
|
|
segmaxval = builder.CreateAlignedLoad(segmaxval, 4, "segmax");
|
|
segmaxval = builder.CreateSExt(segmaxval, int64Type);
|
|
/* nsegs = m_cacheLoc[i+4] / m_hashseg_max */
|
|
llvm::Value* nsegsval = builder.CreateExactUDiv(cacheLocVal, segmaxval, "nsegs");
|
|
nsegsval = builder.CreateTrunc(nsegsval, int32Type);
|
|
/* pos = m_cacheLoc[i+4] % m_hashseg_max */
|
|
llvm::Value* pos = builder.CreateSRem(cacheLocVal, segmaxval, "pos");
|
|
/* get m_hashData from hashAggRunner */
|
|
Vals2[0] = int64_0;
|
|
Vals2[1] = int32_pos_hAggR_hSegTbl;
|
|
llvm::Value* hashData = builder.CreateInBoundsGEP(hAggRunner, Vals2);
|
|
hashData = builder.CreateAlignedLoad(hashData, 8, "m_hashData");
|
|
Vals2[0] = nsegsval;
|
|
Vals2[1] = int32_1;
|
|
llvm::Value* tbldata = builder.CreateInBoundsGEP(hashData, Vals2);
|
|
tbldata = builder.CreateAlignedLoad(tbldata, 8, "tbl_data");
|
|
llvm::Value* next_hashData_addr = builder.CreateInBoundsGEP(tbldata, pos);
|
|
|
|
llvm::Function* fn_prefetch = llvm::Intrinsic::getDeclaration(llvmCodeGen->module(), llvm::Intrinsic::prefetch);
|
|
if (fn_prefetch == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_LOAD_INTRINSIC_FUNCTION_FAILED),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Failed to get function Intrinsic::prefetch!\n")));
|
|
}
|
|
tmpval = builder.CreateBitCast(next_hashData_addr, int8PtrType);
|
|
builder.CreateCall(fn_prefetch, {tmpval, int32_0, int32_3, int32_1});
|
|
|
|
/* calculate m_cacheLoc[i+2] = m_hashVal[i+2] & mask */
|
|
llvm::Value* next_cell_idx = builder.CreateAdd(idx, int64_distance2);
|
|
Vals3[1] = int32_pos_hAggR_hashVal;
|
|
Vals3[2] = next_cell_idx;
|
|
hashValSlot = builder.CreateInBoundsGEP(hAggRunner, Vals3);
|
|
hashVal64 = builder.CreateAlignedLoad(hashValSlot, 8, "hashVal");
|
|
cacheLocVal = builder.CreateAnd(hashVal64, maskval);
|
|
|
|
/* nsegs = m_cacheLoc[i+2] / m_hashseg_max */
|
|
llvm::Value* nsegsval2 = builder.CreateExactUDiv(cacheLocVal, segmaxval, "nsegs");
|
|
nsegsval2 = builder.CreateTrunc(nsegsval2, int32Type);
|
|
/* pos = m_cacheLoc[i+2] % m_hashseg_max */
|
|
llvm::Value* pos2 = builder.CreateSRem(cacheLocVal, segmaxval, "pos");
|
|
Vals2[0] = nsegsval2;
|
|
Vals2[1] = int32_1;
|
|
llvm::Value* tbldata2 = builder.CreateInBoundsGEP(hashData, Vals2);
|
|
tbldata2 = builder.CreateAlignedLoad(tbldata2, 8, "tbl_data");
|
|
tmpval = builder.CreateInBoundsGEP(tbldata2, pos2);
|
|
llvm::Value* next_cell = builder.CreateAlignedLoad(tmpval, 8, "cell");
|
|
|
|
tmpval = builder.CreatePtrToInt(next_cell, int64Type);
|
|
tmpval = builder.CreateICmpEQ(tmpval, int64_0);
|
|
builder.CreateCondBr(tmpval, ret_bb, prefetch_cell_bb);
|
|
|
|
builder.SetInsertPoint(prefetch_cell_bb);
|
|
next_cell = builder.CreateBitCast(next_cell, int8PtrType);
|
|
builder.CreateCall(fn_prefetch, {next_cell, int32_0, int32_3, int32_1});
|
|
builder.CreateBr(ret_bb);
|
|
|
|
builder.SetInsertPoint(ret_bb);
|
|
builder.CreateRetVoid();
|
|
|
|
llvmCodeGen->FinalizeFunction(jitted_hashing_prefetch);
|
|
|
|
return jitted_hashing_prefetch;
|
|
}
|
|
|
|
/*
|
|
* @Description : Generate the function to prefetch the hash cell ahead
|
|
* of 2, which means hint fetch loc[i+2], and prefetch the
|
|
* address of m_data ahead of 4, which means hint fetch
|
|
* m_data[m_cacheLoc[i+4]]. This function is called in the
|
|
* loop of the matching key.
|
|
* @return : return llvm function for building hash table in batchagg.
|
|
*/
|
|
llvm::Function* prefetchAggSglTblHashingCodeGen()
|
|
{
|
|
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::Value* tmpval = NULL;
|
|
llvm::Value* llvmargs[3];
|
|
llvm::Function* jitted_hashing_prefetch = NULL;
|
|
|
|
/* Define data types and some llvm consts */
|
|
DEFINE_CG_VOIDTYPE(voidType);
|
|
DEFINE_CG_TYPE(int64Type, INT8OID);
|
|
DEFINE_CG_PTRTYPE(int8PtrType, CHAROID);
|
|
DEFINE_CG_PTRTYPE(hashAggRunnerPtrType, "class.HashAggRunner");
|
|
|
|
DEFINE_CGVAR_INT32(int32_0, 0);
|
|
DEFINE_CGVAR_INT32(int32_1, 1);
|
|
DEFINE_CGVAR_INT32(int32_3, 3);
|
|
DEFINE_CGVAR_INT32(int32_pos_hAggR_hashVal, pos_hAggR_hashVal);
|
|
DEFINE_CGVAR_INT32(int32_pos_hAggR_hSegTbl, pos_hAggR_hSegTbl);
|
|
DEFINE_CGVAR_INT32(int32_pos_hAggR_hashSize, pos_hAggR_hashSize);
|
|
DEFINE_CGVAR_INT64(int64_0, 0);
|
|
DEFINE_CGVAR_INT64(int64_1, 1);
|
|
DEFINE_CGVAR_INT64(int64_distance1, PREFETCH_AGGHASHING_DISTANCE1);
|
|
DEFINE_CGVAR_INT64(int64_distance2, PREFETCH_AGGHASHING_DISTANCE2);
|
|
|
|
llvm::Value* Vals2[2] = {int64_0, int32_0};
|
|
llvm::Value* Vals3[3] = {int64_0, int32_0, int32_0};
|
|
|
|
GsCodeGen::FnPrototype fn_prototype(llvmCodeGen, "prefetchAggSglTblHashing", voidType);
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("hAggRunner", hashAggRunnerPtrType));
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("Idx", int64Type));
|
|
fn_prototype.addArgument(GsCodeGen::NamedVariable("nrows", int64Type));
|
|
jitted_hashing_prefetch = fn_prototype.generatePrototype(&builder, &llvmargs[0]);
|
|
|
|
llvm::Value* hAggRunner = llvmargs[0];
|
|
llvm::Value* idx = llvmargs[1];
|
|
llvm::Value* nrows = llvmargs[2];
|
|
|
|
DEFINE_BLOCK(prefetch_hashData_bb, jitted_hashing_prefetch);
|
|
DEFINE_BLOCK(prefetch_cell_bb, jitted_hashing_prefetch);
|
|
DEFINE_BLOCK(ret_bb, jitted_hashing_prefetch);
|
|
|
|
/*
|
|
* try to prefetch hashData[m_cacheLoc[i+4]] : only do this when we have
|
|
* enough rows.
|
|
*/
|
|
llvm::Value* prefetchIdx = builder.CreateAdd(idx, int64_distance1);
|
|
tmpval = builder.CreateICmpSLT(prefetchIdx, nrows);
|
|
builder.CreateCondBr(tmpval, prefetch_hashData_bb, ret_bb);
|
|
|
|
builder.SetInsertPoint(prefetch_hashData_bb);
|
|
/* mask = hashAggRunner.m_hashSize - 1 */
|
|
Vals2[1] = int32_pos_hAggR_hashSize;
|
|
tmpval = builder.CreateInBoundsGEP(hAggRunner, Vals2);
|
|
llvm::Value* maskval = builder.CreateAlignedLoad(tmpval, 8, "m_hashSize");
|
|
maskval = builder.CreateSub(maskval, int64_1, "mask");
|
|
|
|
/* calculate m_cacheLoc[i+4] = m_hashVal[i+4] & mask */
|
|
llvm::Value* next_hashData_idx = builder.CreateAdd(idx, int64_distance1);
|
|
Vals3[1] = int32_pos_hAggR_hashVal;
|
|
Vals3[2] = next_hashData_idx;
|
|
llvm::Value* hashValSlot = builder.CreateInBoundsGEP(hAggRunner, Vals3);
|
|
llvm::Value* hashVal64 = builder.CreateAlignedLoad(hashValSlot, 8, "hashVal");
|
|
llvm::Value* cacheLocVal = builder.CreateAnd(hashVal64, maskval);
|
|
|
|
/* get m_hashData from hashAggRunner */
|
|
Vals2[0] = int64_0;
|
|
Vals2[1] = int32_pos_hAggR_hSegTbl;
|
|
llvm::Value* hashData = builder.CreateInBoundsGEP(hAggRunner, Vals2);
|
|
hashData = builder.CreateAlignedLoad(hashData, 8, "m_hashData");
|
|
|
|
/* calculate address of m_hashData[0].tbl_data[m_cacheLoc[i+4]] */
|
|
Vals2[0] = int64_0;
|
|
Vals2[1] = int32_1;
|
|
llvm::Value* tbldata = builder.CreateInBoundsGEP(hashData, Vals2);
|
|
tbldata = builder.CreateAlignedLoad(tbldata, 8, "tbl_data");
|
|
llvm::Value* next_hashData_addr = builder.CreateInBoundsGEP(tbldata, cacheLocVal);
|
|
|
|
llvm::Function* fn_prefetch = llvm::Intrinsic::getDeclaration(llvmCodeGen->module(), llvm::Intrinsic::prefetch);
|
|
if (fn_prefetch == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_LOAD_INTRINSIC_FUNCTION_FAILED),
|
|
errmodule(MOD_LLVM),
|
|
errmsg("Failed to get function Intrinsic::prefetch!\n")));
|
|
}
|
|
tmpval = builder.CreateBitCast(next_hashData_addr, int8PtrType);
|
|
builder.CreateCall(fn_prefetch, {tmpval, int32_0, int32_3, int32_1});
|
|
|
|
/* calculate m_cacheLoc[i+2] = m_hashVal[i+2] & mask */
|
|
llvm::Value* next_cell_idx = builder.CreateAdd(idx, int64_distance2);
|
|
Vals3[1] = int32_pos_hAggR_hashVal;
|
|
Vals3[2] = next_cell_idx;
|
|
hashValSlot = builder.CreateInBoundsGEP(hAggRunner, Vals3);
|
|
hashVal64 = builder.CreateAlignedLoad(hashValSlot, 8, "hashVal");
|
|
cacheLocVal = builder.CreateAnd(hashVal64, maskval);
|
|
|
|
llvm::Value* next_cell = builder.CreateInBoundsGEP(tbldata, cacheLocVal);
|
|
next_cell = builder.CreateAlignedLoad(next_cell, 8, "cell");
|
|
|
|
tmpval = builder.CreatePtrToInt(next_cell, int64Type);
|
|
tmpval = builder.CreateICmpEQ(tmpval, int64_0);
|
|
builder.CreateCondBr(tmpval, ret_bb, prefetch_cell_bb);
|
|
|
|
builder.SetInsertPoint(prefetch_cell_bb);
|
|
next_cell = builder.CreateBitCast(next_cell, int8PtrType);
|
|
builder.CreateCall(fn_prefetch, {next_cell, int32_0, int32_3, int32_1});
|
|
builder.CreateBr(ret_bb);
|
|
|
|
builder.SetInsertPoint(ret_bb);
|
|
builder.CreateRetVoid();
|
|
|
|
llvmCodeGen->FinalizeFunction(jitted_hashing_prefetch);
|
|
|
|
return jitted_hashing_prefetch;
|
|
}
|
|
} // namespace dorado
|
|
|
|
/**
|
|
* @Description : If current hashcell is not matched, allocate a new hash cell and
|
|
* initialize the hash value.
|
|
* @in haRunner : HashAggRunner data structure.
|
|
* @in batch : Batch value used to initialize the hash cell.
|
|
* @in idx : The row index of current tuple in batch.
|
|
* @in keysimple : the match key is simple when true.
|
|
*/
|
|
void WrapAllocHashSlot(HashAggRunner* haRunner, VectorBatch* batch, int idx, int keysimple)
|
|
{
|
|
if (keysimple)
|
|
haRunner->AllocHashSlot<true, false>(batch, idx);
|
|
else
|
|
haRunner->AllocHashSlot<false, false>(batch, idx);
|
|
}
|
|
|
|
/**
|
|
* @Description : If current hashcell is not matched, allocate a new hash cell and
|
|
* initialize the hash value with only one segment hash table case.
|
|
* @in haRunner : HashAggRunner data structure.
|
|
* @in batch : Batch value used to initialize the hash cell.
|
|
* @in idx : The row index of current tuple in batch.
|
|
* @in keysimple : the match key is simple when true.
|
|
*/
|
|
void WrapSglTblAllocHashSlot(HashAggRunner* haRunner, VectorBatch* batch, int idx, int keysimple)
|
|
{
|
|
if (keysimple)
|
|
haRunner->AllocHashSlot<true, true>(batch, idx);
|
|
else
|
|
haRunner->AllocHashSlot<false, true>(batch, idx);
|
|
}
|
|
|
|
/**
|
|
* @Description : Reset current expr context.
|
|
* @econtext : Current expr context.
|
|
*/
|
|
void WrapResetExprContext(ExprContext* econtext)
|
|
{
|
|
if (econtext != NULL)
|
|
ResetExprContext(econtext);
|
|
}
|