expression eval: Reduce number of steps for agg transition invocations.

Do so by combining the various steps that are part of aggregate
transition function invocation into one larger step. As some of the
current steps are only necessary for some aggregates, have one variant
of the aggregate transition step for each possible combination.

To avoid further manual copies of code in the different transition
step implementations, move most of the code into helper functions
marked as "always inline".

The benefit of this change is an increase in performance when
aggregating lots of rows. This comes in part due to the reduced number
of indirect jumps due to the reduced number of steps, and in part by
reducing redundant setup code across steps. This mainly benefits
interpreted execution, but the code generated by JIT is also improved
a bit.

As a nice side-effect it also ends up making the code a bit simpler.

A small additional optimization is removing the need to set
aggstate->curaggcontext before calling ExecAggInitGroup, choosing to
instead passign curaggcontext as an argument. It was, in contrast to
other aggregate related functions, only needed to fetch a memory
context to copy the transition value into.

Author: Andres Freund
Discussion:
   https://postgr.es/m/20191023163849.sosqbfs5yenocez3@alap3.anarazel.de
   https://postgr.es/m/5c371df7cee903e8cd4c685f90c6c72086d3a2dc.camel@j-davis.com
This commit is contained in:
Andres Freund 2020-02-24 14:39:22 -08:00
parent 7d672b76bf
commit 2742c45080
5 changed files with 351 additions and 367 deletions

View File

@ -3229,8 +3229,6 @@ ExecBuildAggTransCall(ExprState *state, AggState *aggstate,
FunctionCallInfo fcinfo, AggStatePerTrans pertrans,
int transno, int setno, int setoff, bool ishash)
{
int adjust_init_jumpnull = -1;
int adjust_strict_jumpnull = -1;
ExprContext *aggcontext;
if (ishash)
@ -3239,52 +3237,61 @@ ExecBuildAggTransCall(ExprState *state, AggState *aggstate,
aggcontext = aggstate->aggcontexts[setno];
/*
* Determine appropriate transition implementation.
*
* For non-ordered aggregates:
*
* If the initial value for the transition state doesn't exist in the
* pg_aggregate table then we will let the first non-NULL value returned
* from the outer procNode become the initial value. (This is useful for
* aggregates like max() and min().) The noTransValue flag signals that we
* still need to do this.
* need to do so. If true, generate a
* EEOP_AGG_INIT_STRICT_PLAIN_TRANS{,_BYVAL} step. This step also needs to
* do the work described next:
*
* If the function is strict, but does have an initial value, choose
* EEOP_AGG_STRICT_PLAIN_TRANS{,_BYVAL}, which skips the transition
* function if the transition value has become NULL (because a previous
* transition function returned NULL). This step also needs to do the work
* described next:
*
* Otherwise we call EEOP_AGG_PLAIN_TRANS{,_BYVAL}, which does not have to
* perform either of the above checks.
*
* Having steps with overlapping responsibilities is not nice, but
* aggregations are very performance sensitive, making this worthwhile.
*
* For ordered aggregates:
*
* Only need to choose between the faster path for a single orderred
* column, and the one between multiple columns. Checking strictness etc
* is done when finalizing the aggregate. See
* process_ordered_aggregate_{single, multi} and
* advance_transition_function.
*/
if (pertrans->numSortCols == 0 &&
fcinfo->flinfo->fn_strict &&
pertrans->initValueIsNull)
if (pertrans->numSortCols == 0)
{
scratch->opcode = EEOP_AGG_INIT_TRANS;
scratch->d.agg_init_trans.pertrans = pertrans;
scratch->d.agg_init_trans.setno = setno;
scratch->d.agg_init_trans.setoff = setoff;
scratch->d.agg_init_trans.transno = transno;
scratch->d.agg_init_trans.aggcontext = aggcontext;
scratch->d.agg_init_trans.jumpnull = -1; /* adjust later */
ExprEvalPushStep(state, scratch);
/* see comment about jumping out below */
adjust_init_jumpnull = state->steps_len - 1;
if (pertrans->transtypeByVal)
{
if (fcinfo->flinfo->fn_strict &&
pertrans->initValueIsNull)
scratch->opcode = EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL;
else if (fcinfo->flinfo->fn_strict)
scratch->opcode = EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL;
else
scratch->opcode = EEOP_AGG_PLAIN_TRANS_BYVAL;
}
else
{
if (fcinfo->flinfo->fn_strict &&
pertrans->initValueIsNull)
scratch->opcode = EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF;
else if (fcinfo->flinfo->fn_strict)
scratch->opcode = EEOP_AGG_PLAIN_TRANS_STRICT_BYREF;
else
scratch->opcode = EEOP_AGG_PLAIN_TRANS_BYREF;
}
}
if (pertrans->numSortCols == 0 &&
fcinfo->flinfo->fn_strict)
{
scratch->opcode = EEOP_AGG_STRICT_TRANS_CHECK;
scratch->d.agg_strict_trans_check.setno = setno;
scratch->d.agg_strict_trans_check.setoff = setoff;
scratch->d.agg_strict_trans_check.transno = transno;
scratch->d.agg_strict_trans_check.jumpnull = -1; /* adjust later */
ExprEvalPushStep(state, scratch);
/*
* Note, we don't push into adjust_bailout here - those jump to the
* end of all transition value computations. Here a single transition
* value is NULL, so just skip processing the individual value.
*/
adjust_strict_jumpnull = state->steps_len - 1;
}
/* invoke appropriate transition implementation */
if (pertrans->numSortCols == 0 && pertrans->transtypeByVal)
scratch->opcode = EEOP_AGG_PLAIN_TRANS_BYVAL;
else if (pertrans->numSortCols == 0)
scratch->opcode = EEOP_AGG_PLAIN_TRANS;
else if (pertrans->numInputs == 1)
scratch->opcode = EEOP_AGG_ORDERED_TRANS_DATUM;
else
@ -3296,22 +3303,6 @@ ExecBuildAggTransCall(ExprState *state, AggState *aggstate,
scratch->d.agg_trans.transno = transno;
scratch->d.agg_trans.aggcontext = aggcontext;
ExprEvalPushStep(state, scratch);
/* adjust jumps so they jump till after transition invocation */
if (adjust_init_jumpnull != -1)
{
ExprEvalStep *as = &state->steps[adjust_init_jumpnull];
Assert(as->d.agg_init_trans.jumpnull == -1);
as->d.agg_init_trans.jumpnull = state->steps_len;
}
if (adjust_strict_jumpnull != -1)
{
ExprEvalStep *as = &state->steps[adjust_strict_jumpnull];
Assert(as->d.agg_strict_trans_check.jumpnull == -1);
as->d.agg_strict_trans_check.jumpnull = state->steps_len;
}
}
/*

View File

@ -166,6 +166,16 @@ static Datum ExecJustAssignInnerVarVirt(ExprState *state, ExprContext *econtext,
static Datum ExecJustAssignOuterVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustAssignScanVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
/* execution helper functions */
static pg_attribute_always_inline void
ExecAggPlainTransByVal(AggState *aggstate, AggStatePerTrans pertrans,
AggStatePerGroup pergroup,
ExprContext *aggcontext, int setno);
static pg_attribute_always_inline void
ExecAggPlainTransByRef(AggState *aggstate, AggStatePerTrans pertrans,
AggStatePerGroup pergroup,
ExprContext *aggcontext, int setno);
/*
* Prepare ExprState for interpreted execution.
@ -425,10 +435,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_AGG_DESERIALIZE,
&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_NULLS,
&&CASE_EEOP_AGG_INIT_TRANS,
&&CASE_EEOP_AGG_STRICT_TRANS_CHECK,
&&CASE_EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL,
&&CASE_EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL,
&&CASE_EEOP_AGG_PLAIN_TRANS_BYVAL,
&&CASE_EEOP_AGG_PLAIN_TRANS,
&&CASE_EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF,
&&CASE_EEOP_AGG_PLAIN_TRANS_STRICT_BYREF,
&&CASE_EEOP_AGG_PLAIN_TRANS_BYREF,
&&CASE_EEOP_AGG_ORDERED_TRANS_DATUM,
&&CASE_EEOP_AGG_ORDERED_TRANS_TUPLE,
&&CASE_EEOP_LAST
@ -1592,167 +1604,136 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
}
/*
* Initialize an aggregate's first value if necessary.
* Different types of aggregate transition functions are implemented
* as different types of steps, to avoid incurring unnecessary
* overhead. There's a step type for each valid combination of having
* a by value / by reference transition type, [not] needing to the
* initialize the transition value for the first row in a group from
* input, and [not] strict transition function.
*
* Could optimize further by splitting off by-reference for
* fixed-length types, but currently that doesn't seem worth it.
*/
EEO_CASE(EEOP_AGG_INIT_TRANS)
EEO_CASE(EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL)
{
AggState *aggstate = castNode(AggState, state->parent);
AggStatePerGroup pergroup;
pergroup = &aggstate->all_pergroups
[op->d.agg_init_trans.setoff]
[op->d.agg_init_trans.transno];
/* If transValue has not yet been initialized, do so now. */
if (pergroup->noTransValue)
{
AggStatePerTrans pertrans = op->d.agg_init_trans.pertrans;
aggstate->curaggcontext = op->d.agg_init_trans.aggcontext;
aggstate->current_set = op->d.agg_init_trans.setno;
ExecAggInitGroup(aggstate, pertrans, pergroup);
/* copied trans value from input, done this round */
EEO_JUMP(op->d.agg_init_trans.jumpnull);
}
EEO_NEXT();
}
/* check that a strict aggregate's input isn't NULL */
EEO_CASE(EEOP_AGG_STRICT_TRANS_CHECK)
{
AggState *aggstate = castNode(AggState, state->parent);
AggStatePerGroup pergroup;
pergroup = &aggstate->all_pergroups
[op->d.agg_strict_trans_check.setoff]
[op->d.agg_strict_trans_check.transno];
if (unlikely(pergroup->transValueIsNull))
EEO_JUMP(op->d.agg_strict_trans_check.jumpnull);
EEO_NEXT();
}
/*
* Evaluate aggregate transition / combine function that has a
* by-value transition type. That's a separate case from the
* by-reference implementation because it's a bit simpler.
*/
EEO_CASE(EEOP_AGG_PLAIN_TRANS_BYVAL)
{
AggState *aggstate = castNode(AggState, state->parent);
AggStatePerTrans pertrans;
AggStatePerGroup pergroup;
FunctionCallInfo fcinfo;
MemoryContext oldContext;
Datum newVal;
pertrans = op->d.agg_trans.pertrans;
pergroup = &aggstate->all_pergroups
AggStatePerTrans pertrans = op->d.agg_trans.pertrans;
AggStatePerGroup pergroup = &aggstate->all_pergroups
[op->d.agg_trans.setoff]
[op->d.agg_trans.transno];
Assert(pertrans->transtypeByVal);
fcinfo = pertrans->transfn_fcinfo;
/* cf. select_current_set() */
aggstate->curaggcontext = op->d.agg_trans.aggcontext;
aggstate->current_set = op->d.agg_trans.setno;
/* set up aggstate->curpertrans for AggGetAggref() */
aggstate->curpertrans = pertrans;
/* invoke transition function in per-tuple context */
oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory);
fcinfo->args[0].value = pergroup->transValue;
fcinfo->args[0].isnull = pergroup->transValueIsNull;
fcinfo->isnull = false; /* just in case transfn doesn't set it */
newVal = FunctionCallInvoke(fcinfo);
pergroup->transValue = newVal;
pergroup->transValueIsNull = fcinfo->isnull;
MemoryContextSwitchTo(oldContext);
if (pergroup->noTransValue)
{
/* If transValue has not yet been initialized, do so now. */
ExecAggInitGroup(aggstate, pertrans, pergroup,
op->d.agg_trans.aggcontext);
/* copied trans value from input, done this round */
}
else if (likely(!pergroup->transValueIsNull))
{
/* invoke transition function, unless prevented by strictness */
ExecAggPlainTransByVal(aggstate, pertrans, pergroup,
op->d.agg_trans.aggcontext,
op->d.agg_trans.setno);
}
EEO_NEXT();
}
/*
* Evaluate aggregate transition / combine function that has a
* by-reference transition type.
*
* Could optimize a bit further by splitting off by-reference
* fixed-length types, but currently that doesn't seem worth it.
*/
EEO_CASE(EEOP_AGG_PLAIN_TRANS)
/* see comments above EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL */
EEO_CASE(EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL)
{
AggState *aggstate = castNode(AggState, state->parent);
AggStatePerTrans pertrans;
AggStatePerGroup pergroup;
FunctionCallInfo fcinfo;
MemoryContext oldContext;
Datum newVal;
AggStatePerTrans pertrans = op->d.agg_trans.pertrans;
AggStatePerGroup pergroup = &aggstate->all_pergroups
[op->d.agg_trans.setoff]
[op->d.agg_trans.transno];
pertrans = op->d.agg_trans.pertrans;
Assert(pertrans->transtypeByVal);
pergroup = &aggstate->all_pergroups
if (likely(!pergroup->transValueIsNull))
ExecAggPlainTransByVal(aggstate, pertrans, pergroup,
op->d.agg_trans.aggcontext,
op->d.agg_trans.setno);
EEO_NEXT();
}
/* see comments above EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL */
EEO_CASE(EEOP_AGG_PLAIN_TRANS_BYVAL)
{
AggState *aggstate = castNode(AggState, state->parent);
AggStatePerTrans pertrans = op->d.agg_trans.pertrans;
AggStatePerGroup pergroup = &aggstate->all_pergroups
[op->d.agg_trans.setoff]
[op->d.agg_trans.transno];
Assert(pertrans->transtypeByVal);
ExecAggPlainTransByVal(aggstate, pertrans, pergroup,
op->d.agg_trans.aggcontext,
op->d.agg_trans.setno);
EEO_NEXT();
}
/* see comments above EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL */
EEO_CASE(EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF)
{
AggState *aggstate = castNode(AggState, state->parent);
AggStatePerTrans pertrans = op->d.agg_trans.pertrans;
AggStatePerGroup pergroup = &aggstate->all_pergroups
[op->d.agg_trans.setoff]
[op->d.agg_trans.transno];
Assert(!pertrans->transtypeByVal);
fcinfo = pertrans->transfn_fcinfo;
if (pergroup->noTransValue)
ExecAggInitGroup(aggstate, pertrans, pergroup,
op->d.agg_trans.aggcontext);
else if (likely(!pergroup->transValueIsNull))
ExecAggPlainTransByRef(aggstate, pertrans, pergroup,
op->d.agg_trans.aggcontext,
op->d.agg_trans.setno);
/* cf. select_current_set() */
aggstate->curaggcontext = op->d.agg_trans.aggcontext;
aggstate->current_set = op->d.agg_trans.setno;
EEO_NEXT();
}
/* set up aggstate->curpertrans for AggGetAggref() */
aggstate->curpertrans = pertrans;
/* see comments above EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL */
EEO_CASE(EEOP_AGG_PLAIN_TRANS_STRICT_BYREF)
{
AggState *aggstate = castNode(AggState, state->parent);
AggStatePerTrans pertrans = op->d.agg_trans.pertrans;
AggStatePerGroup pergroup = &aggstate->all_pergroups
[op->d.agg_trans.setoff]
[op->d.agg_trans.transno];
/* invoke transition function in per-tuple context */
oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory);
Assert(!pertrans->transtypeByVal);
fcinfo->args[0].value = pergroup->transValue;
fcinfo->args[0].isnull = pergroup->transValueIsNull;
fcinfo->isnull = false; /* just in case transfn doesn't set it */
if (likely(!pergroup->transValueIsNull))
ExecAggPlainTransByRef(aggstate, pertrans, pergroup,
op->d.agg_trans.aggcontext,
op->d.agg_trans.setno);
EEO_NEXT();
}
newVal = FunctionCallInvoke(fcinfo);
/* see comments above EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL */
EEO_CASE(EEOP_AGG_PLAIN_TRANS_BYREF)
{
AggState *aggstate = castNode(AggState, state->parent);
AggStatePerTrans pertrans = op->d.agg_trans.pertrans;
AggStatePerGroup pergroup = &aggstate->all_pergroups
[op->d.agg_trans.setoff]
[op->d.agg_trans.transno];
/*
* For pass-by-ref datatype, must copy the new value into
* aggcontext and free the prior transValue. But if transfn
* returned a pointer to its first input, we don't need to do
* anything. Also, if transfn returned a pointer to a R/W
* expanded object that is already a child of the aggcontext,
* assume we can adopt that value without copying it.
*
* It's safe to compare newVal with pergroup->transValue without
* regard for either being NULL, because ExecAggTransReparent()
* takes care to set transValue to 0 when NULL. Otherwise we could
* end up accidentally not reparenting, when the transValue has
* the same numerical value as newValue, despite being NULL. This
* is a somewhat hot path, making it undesirable to instead solve
* this with another branch for the common case of the transition
* function returning its (modified) input argument.
*/
if (DatumGetPointer(newVal) != DatumGetPointer(pergroup->transValue))
newVal = ExecAggTransReparent(aggstate, pertrans,
newVal, fcinfo->isnull,
pergroup->transValue,
pergroup->transValueIsNull);
Assert(!pertrans->transtypeByVal);
pergroup->transValue = newVal;
pergroup->transValueIsNull = fcinfo->isnull;
MemoryContextSwitchTo(oldContext);
ExecAggPlainTransByRef(aggstate, pertrans, pergroup,
op->d.agg_trans.aggcontext,
op->d.agg_trans.setno);
EEO_NEXT();
}
@ -4145,7 +4126,8 @@ ExecEvalSysVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext,
* value for a group. We use it as the initial value for transValue.
*/
void
ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup pergroup)
ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup pergroup,
ExprContext *aggcontext)
{
FunctionCallInfo fcinfo = pertrans->transfn_fcinfo;
MemoryContext oldContext;
@ -4156,7 +4138,7 @@ ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup
* that the agg's input type is binary-compatible with its transtype, so
* straight copy here is OK.)
*/
oldContext = MemoryContextSwitchTo(aggstate->curaggcontext->ecxt_per_tuple_memory);
oldContext = MemoryContextSwitchTo(aggcontext->ecxt_per_tuple_memory);
pergroup->transValue = datumCopy(fcinfo->args[1].value,
pertrans->transtypeByVal,
pertrans->transtypeLen);
@ -4243,3 +4225,90 @@ ExecEvalAggOrderedTransTuple(ExprState *state, ExprEvalStep *op,
ExecStoreVirtualTuple(pertrans->sortslot);
tuplesort_puttupleslot(pertrans->sortstates[setno], pertrans->sortslot);
}
/* implementation of transition function invocation for byval types */
static pg_attribute_always_inline void
ExecAggPlainTransByVal(AggState *aggstate, AggStatePerTrans pertrans,
AggStatePerGroup pergroup,
ExprContext *aggcontext, int setno)
{
FunctionCallInfo fcinfo = pertrans->transfn_fcinfo;
MemoryContext oldContext;
Datum newVal;
/* cf. select_current_set() */
aggstate->curaggcontext = aggcontext;
aggstate->current_set = setno;
/* set up aggstate->curpertrans for AggGetAggref() */
aggstate->curpertrans = pertrans;
/* invoke transition function in per-tuple context */
oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory);
fcinfo->args[0].value = pergroup->transValue;
fcinfo->args[0].isnull = pergroup->transValueIsNull;
fcinfo->isnull = false; /* just in case transfn doesn't set it */
newVal = FunctionCallInvoke(fcinfo);
pergroup->transValue = newVal;
pergroup->transValueIsNull = fcinfo->isnull;
MemoryContextSwitchTo(oldContext);
}
/* implementation of transition function invocation for byref types */
static pg_attribute_always_inline void
ExecAggPlainTransByRef(AggState *aggstate, AggStatePerTrans pertrans,
AggStatePerGroup pergroup,
ExprContext *aggcontext, int setno)
{
FunctionCallInfo fcinfo = pertrans->transfn_fcinfo;
MemoryContext oldContext;
Datum newVal;
/* cf. select_current_set() */
aggstate->curaggcontext = aggcontext;
aggstate->current_set = setno;
/* set up aggstate->curpertrans for AggGetAggref() */
aggstate->curpertrans = pertrans;
/* invoke transition function in per-tuple context */
oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory);
fcinfo->args[0].value = pergroup->transValue;
fcinfo->args[0].isnull = pergroup->transValueIsNull;
fcinfo->isnull = false; /* just in case transfn doesn't set it */
newVal = FunctionCallInvoke(fcinfo);
/*
* For pass-by-ref datatype, must copy the new value into
* aggcontext and free the prior transValue. But if transfn
* returned a pointer to its first input, we don't need to do
* anything. Also, if transfn returned a pointer to a R/W
* expanded object that is already a child of the aggcontext,
* assume we can adopt that value without copying it.
*
* It's safe to compare newVal with pergroup->transValue without
* regard for either being NULL, because ExecAggTransReparent()
* takes care to set transValue to 0 when NULL. Otherwise we could
* end up accidentally not reparenting, when the transValue has
* the same numerical value as newValue, despite being NULL. This
* is a somewhat hot path, making it undesirable to instead solve
* this with another branch for the common case of the transition
* function returning its (modified) input argument.
*/
if (DatumGetPointer(newVal) != DatumGetPointer(pergroup->transValue))
newVal = ExecAggTransReparent(aggstate, pertrans,
newVal, fcinfo->isnull,
pergroup->transValue,
pergroup->transValueIsNull);
pergroup->transValue = newVal;
pergroup->transValueIsNull = fcinfo->isnull;
MemoryContextSwitchTo(oldContext);
}

View File

@ -304,7 +304,10 @@ static int find_compatible_pertrans(AggState *aggstate, Aggref *newagg,
static void
select_current_set(AggState *aggstate, int setno, bool is_hash)
{
/* when changing this, also adapt ExecInterpExpr() and friends */
/*
* When changing this, also adapt ExecAggPlainTransByVal() and
* ExecAggPlainTransByRef().
*/
if (is_hash)
aggstate->curaggcontext = aggstate->hashcontext;
else

View File

@ -2046,152 +2046,12 @@ llvm_compile_expr(ExprState *state)
break;
}
case EEOP_AGG_INIT_TRANS:
{
AggStatePerTrans pertrans;
LLVMValueRef v_aggstatep;
LLVMValueRef v_pertransp;
LLVMValueRef v_allpergroupsp;
LLVMValueRef v_pergroupp;
LLVMValueRef v_setoff,
v_transno;
LLVMValueRef v_notransvalue;
LLVMBasicBlockRef b_init;
pertrans = op->d.agg_init_trans.pertrans;
v_aggstatep =
LLVMBuildBitCast(b, v_parent, l_ptr(StructAggState), "");
v_pertransp = l_ptr_const(pertrans,
l_ptr(StructAggStatePerTransData));
/*
* pergroup = &aggstate->all_pergroups
* [op->d.agg_init_trans_check.setoff]
* [op->d.agg_init_trans_check.transno];
*/
v_allpergroupsp =
l_load_struct_gep(b, v_aggstatep,
FIELDNO_AGGSTATE_ALL_PERGROUPS,
"aggstate.all_pergroups");
v_setoff = l_int32_const(op->d.agg_init_trans.setoff);
v_transno = l_int32_const(op->d.agg_init_trans.transno);
v_pergroupp =
LLVMBuildGEP(b,
l_load_gep1(b, v_allpergroupsp, v_setoff, ""),
&v_transno, 1, "");
v_notransvalue =
l_load_struct_gep(b, v_pergroupp,
FIELDNO_AGGSTATEPERGROUPDATA_NOTRANSVALUE,
"notransvalue");
b_init = l_bb_before_v(opblocks[opno + 1],
"op.%d.inittrans", opno);
LLVMBuildCondBr(b,
LLVMBuildICmp(b, LLVMIntEQ, v_notransvalue,
l_sbool_const(1), ""),
b_init,
opblocks[opno + 1]);
LLVMPositionBuilderAtEnd(b, b_init);
{
LLVMValueRef params[3];
LLVMValueRef v_curaggcontext;
LLVMValueRef v_current_set;
LLVMValueRef v_aggcontext;
v_aggcontext = l_ptr_const(op->d.agg_init_trans.aggcontext,
l_ptr(StructExprContext));
v_current_set =
LLVMBuildStructGEP(b,
v_aggstatep,
FIELDNO_AGGSTATE_CURRENT_SET,
"aggstate.current_set");
v_curaggcontext =
LLVMBuildStructGEP(b,
v_aggstatep,
FIELDNO_AGGSTATE_CURAGGCONTEXT,
"aggstate.curaggcontext");
LLVMBuildStore(b, l_int32_const(op->d.agg_init_trans.setno),
v_current_set);
LLVMBuildStore(b, v_aggcontext,
v_curaggcontext);
params[0] = v_aggstatep;
params[1] = v_pertransp;
params[2] = v_pergroupp;
LLVMBuildCall(b,
llvm_pg_func(mod, "ExecAggInitGroup"),
params, lengthof(params),
"");
}
LLVMBuildBr(b, opblocks[op->d.agg_init_trans.jumpnull]);
break;
}
case EEOP_AGG_STRICT_TRANS_CHECK:
{
LLVMValueRef v_setoff,
v_transno;
LLVMValueRef v_aggstatep;
LLVMValueRef v_allpergroupsp;
LLVMValueRef v_transnull;
LLVMValueRef v_pergroupp;
int jumpnull = op->d.agg_strict_trans_check.jumpnull;
v_aggstatep =
LLVMBuildBitCast(b, v_parent, l_ptr(StructAggState), "");
/*
* pergroup = &aggstate->all_pergroups
* [op->d.agg_strict_trans_check.setoff]
* [op->d.agg_init_trans_check.transno];
*/
v_allpergroupsp =
l_load_struct_gep(b, v_aggstatep,
FIELDNO_AGGSTATE_ALL_PERGROUPS,
"aggstate.all_pergroups");
v_setoff =
l_int32_const(op->d.agg_strict_trans_check.setoff);
v_transno =
l_int32_const(op->d.agg_strict_trans_check.transno);
v_pergroupp =
LLVMBuildGEP(b,
l_load_gep1(b, v_allpergroupsp, v_setoff, ""),
&v_transno, 1, "");
v_transnull =
l_load_struct_gep(b, v_pergroupp,
FIELDNO_AGGSTATEPERGROUPDATA_TRANSVALUEISNULL,
"transnull");
LLVMBuildCondBr(b,
LLVMBuildICmp(b, LLVMIntEQ, v_transnull,
l_sbool_const(1), ""),
opblocks[jumpnull],
opblocks[opno + 1]);
break;
}
case EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL:
case EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL:
case EEOP_AGG_PLAIN_TRANS_BYVAL:
case EEOP_AGG_PLAIN_TRANS:
case EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF:
case EEOP_AGG_PLAIN_TRANS_STRICT_BYREF:
case EEOP_AGG_PLAIN_TRANS_BYREF:
{
AggState *aggstate;
AggStatePerTrans pertrans;
@ -2249,6 +2109,81 @@ llvm_compile_expr(ExprState *state)
l_load_gep1(b, v_allpergroupsp, v_setoff, ""),
&v_transno, 1, "");
if (opcode == EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL ||
opcode == EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF)
{
LLVMValueRef v_notransvalue;
LLVMBasicBlockRef b_init;
LLVMBasicBlockRef b_no_init;
v_notransvalue =
l_load_struct_gep(b, v_pergroupp,
FIELDNO_AGGSTATEPERGROUPDATA_NOTRANSVALUE,
"notransvalue");
b_init = l_bb_before_v(opblocks[opno + 1],
"op.%d.inittrans", opno);
b_no_init = l_bb_before_v(opblocks[opno + 1],
"op.%d.no_inittrans", opno);
LLVMBuildCondBr(b,
LLVMBuildICmp(b, LLVMIntEQ, v_notransvalue,
l_sbool_const(1), ""),
b_init,
b_no_init);
/* block to init the transition value if necessary */
{
LLVMValueRef params[4];
LLVMPositionBuilderAtEnd(b, b_init);
v_aggcontext = l_ptr_const(op->d.agg_trans.aggcontext,
l_ptr(StructExprContext));
params[0] = v_aggstatep;
params[1] = v_pertransp;
params[2] = v_pergroupp;
params[3] = v_aggcontext;
LLVMBuildCall(b,
llvm_pg_func(mod, "ExecAggInitGroup"),
params, lengthof(params),
"");
LLVMBuildBr(b, opblocks[opno + 1]);
}
LLVMPositionBuilderAtEnd(b, b_no_init);
}
if (opcode == EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL ||
opcode == EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF ||
opcode == EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL ||
opcode == EEOP_AGG_PLAIN_TRANS_STRICT_BYREF)
{
LLVMValueRef v_transnull;
LLVMBasicBlockRef b_strictpass;
b_strictpass = l_bb_before_v(opblocks[opno + 1],
"op.%d.strictpass", opno);
v_transnull =
l_load_struct_gep(b, v_pergroupp,
FIELDNO_AGGSTATEPERGROUPDATA_TRANSVALUEISNULL,
"transnull");
LLVMBuildCondBr(b,
LLVMBuildICmp(b, LLVMIntEQ, v_transnull,
l_sbool_const(1), ""),
opblocks[opno + 1],
b_strictpass);
LLVMPositionBuilderAtEnd(b, b_strictpass);
}
v_fcinfo = l_ptr_const(fcinfo,
l_ptr(StructFunctionCallInfoData));
v_aggcontext = l_ptr_const(op->d.agg_trans.aggcontext,
@ -2312,7 +2247,9 @@ llvm_compile_expr(ExprState *state)
* child of the aggcontext, assume we can adopt that value
* without copying it.
*/
if (opcode == EEOP_AGG_PLAIN_TRANS)
if (opcode == EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF ||
opcode == EEOP_AGG_PLAIN_TRANS_STRICT_BYREF ||
opcode == EEOP_AGG_PLAIN_TRANS_BYREF)
{
LLVMBasicBlockRef b_call;
LLVMBasicBlockRef b_nocall;

View File

@ -225,10 +225,12 @@ typedef enum ExprEvalOp
EEOP_AGG_DESERIALIZE,
EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
EEOP_AGG_STRICT_INPUT_CHECK_NULLS,
EEOP_AGG_INIT_TRANS,
EEOP_AGG_STRICT_TRANS_CHECK,
EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL,
EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL,
EEOP_AGG_PLAIN_TRANS_BYVAL,
EEOP_AGG_PLAIN_TRANS,
EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF,
EEOP_AGG_PLAIN_TRANS_STRICT_BYREF,
EEOP_AGG_PLAIN_TRANS_BYREF,
EEOP_AGG_ORDERED_TRANS_DATUM,
EEOP_AGG_ORDERED_TRANS_TUPLE,
@ -620,27 +622,8 @@ typedef struct ExprEvalStep
int jumpnull;
} agg_strict_input_check;
/* for EEOP_AGG_INIT_TRANS */
struct
{
AggStatePerTrans pertrans;
ExprContext *aggcontext;
int setno;
int transno;
int setoff;
int jumpnull;
} agg_init_trans;
/* for EEOP_AGG_STRICT_TRANS_CHECK */
struct
{
int setno;
int transno;
int setoff;
int jumpnull;
} agg_strict_trans_check;
/* for EEOP_AGG_{PLAIN,ORDERED}_TRANS* */
/* for EEOP_AGG_PLAIN_TRANS_[INIT_][STRICT_]{BYVAL,BYREF} */
/* for EEOP_AGG_ORDERED_TRANS_{DATUM,TUPLE} */
struct
{
AggStatePerTrans pertrans;
@ -750,7 +733,8 @@ extern void ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op,
extern void ExecEvalSysVar(ExprState *state, ExprEvalStep *op,
ExprContext *econtext, TupleTableSlot *slot);
extern void ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup pergroup);
extern void ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup pergroup,
ExprContext *aggcontext);
extern Datum ExecAggTransReparent(AggState *aggstate, AggStatePerTrans pertrans,
Datum newValue, bool newValueIsNull,
Datum oldValue, bool oldValueIsNull);