1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-14 08:21:07 +03:00

Replace EEOP_DONE with special steps for return/no return

Knowing when the side-effects of an expression is the intended result
of the execution, rather than the returnvalue, is important for being
able generate more efficient JITed code. This replaces EEOP_DONE with
two new steps: EEOP_DONE_RETURN and EEOP_DONE_NO_RETURN.  Expressions
which return a value should use the former step; expressions used for
their side-effects which don't return value should use the latter.

Author: Andres Freund <andres@anarazel.de>
Co-authored-by: Daniel Gustafsson <daniel@yesql.se>
Reviewed-by: Andreas Karlsson <andreas@proxel.se>
Discussion: https://postgr.es/m/415721CE-7D2E-4B74-B5D9-1950083BA03E@yesql.se
Discussion: https://postgr.es/m/20191023163849.sosqbfs5yenocez3@alap3.anarazel.de
This commit is contained in:
Daniel Gustafsson
2025-03-11 12:02:38 +01:00
parent dabccf4513
commit 8dd7c7cd0a
7 changed files with 98 additions and 34 deletions

View File

@ -133,9 +133,14 @@ is used by the function evaluation step, thus avoiding extra work to copy
the result values around. the result values around.
The last entry in a completed ExprState->steps array is always an The last entry in a completed ExprState->steps array is always an
EEOP_DONE step; this removes the need to test for end-of-array while EEOP_DONE_RETURN or EEOP_DONE_NO_RETURN step; this removes the need to
iterating. Also, if the expression contains any variable references (to test for end-of-array while iterating. The former is used when the
user columns of the ExprContext's INNER, OUTER, or SCAN tuples), the steps expression returns a value directly, the latter when side-effects of
expression initialization are the goal (e.g. for projection or
aggregate transition value computation).
Also, if the expression contains any variable references (to user
columns of the ExprContext's INNER, OUTER, or SCAN tuples), the steps
array begins with EEOP_*_FETCHSOME steps that ensure that the relevant array begins with EEOP_*_FETCHSOME steps that ensure that the relevant
tuples have been deconstructed to make the required columns directly tuples have been deconstructed to make the required columns directly
available (cf. slot_getsomeattrs()). This allows individual Var-fetching available (cf. slot_getsomeattrs()). This allows individual Var-fetching

View File

@ -8,7 +8,7 @@
* using ExecInitExpr() et al. This converts the tree into a flat array * using ExecInitExpr() et al. This converts the tree into a flat array
* of ExprEvalSteps, which may be thought of as instructions in a program. * of ExprEvalSteps, which may be thought of as instructions in a program.
* At runtime, we'll execute steps, starting with the first, until we reach * At runtime, we'll execute steps, starting with the first, until we reach
* an EEOP_DONE opcode. * an EEOP_DONE_{RETURN|NO_RETURN} opcode.
* *
* This file contains the "compilation" logic. It is independent of the * This file contains the "compilation" logic. It is independent of the
* specific execution technology we use (switch statement, computed goto, * specific execution technology we use (switch statement, computed goto,
@ -162,7 +162,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
ExecInitExprRec(node, state, &state->resvalue, &state->resnull); ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
/* Finally, append a DONE step */ /* Finally, append a DONE step */
scratch.opcode = EEOP_DONE; scratch.opcode = EEOP_DONE_RETURN;
ExprEvalPushStep(state, &scratch); ExprEvalPushStep(state, &scratch);
ExecReadyExpr(state); ExecReadyExpr(state);
@ -199,7 +199,7 @@ ExecInitExprWithParams(Expr *node, ParamListInfo ext_params)
ExecInitExprRec(node, state, &state->resvalue, &state->resnull); ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
/* Finally, append a DONE step */ /* Finally, append a DONE step */
scratch.opcode = EEOP_DONE; scratch.opcode = EEOP_DONE_RETURN;
ExprEvalPushStep(state, &scratch); ExprEvalPushStep(state, &scratch);
ExecReadyExpr(state); ExecReadyExpr(state);
@ -291,7 +291,7 @@ ExecInitQual(List *qual, PlanState *parent)
* have yielded TRUE, and since its result is stored in the desired output * have yielded TRUE, and since its result is stored in the desired output
* location, we're done. * location, we're done.
*/ */
scratch.opcode = EEOP_DONE; scratch.opcode = EEOP_DONE_RETURN;
ExprEvalPushStep(state, &scratch); ExprEvalPushStep(state, &scratch);
ExecReadyExpr(state); ExecReadyExpr(state);
@ -503,7 +503,7 @@ ExecBuildProjectionInfo(List *targetList,
} }
} }
scratch.opcode = EEOP_DONE; scratch.opcode = EEOP_DONE_NO_RETURN;
ExprEvalPushStep(state, &scratch); ExprEvalPushStep(state, &scratch);
ExecReadyExpr(state); ExecReadyExpr(state);
@ -742,7 +742,7 @@ ExecBuildUpdateProjection(List *targetList,
} }
} }
scratch.opcode = EEOP_DONE; scratch.opcode = EEOP_DONE_NO_RETURN;
ExprEvalPushStep(state, &scratch); ExprEvalPushStep(state, &scratch);
ExecReadyExpr(state); ExecReadyExpr(state);
@ -1714,7 +1714,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
else else
{ {
/* Not trivial, so append a DONE step */ /* Not trivial, so append a DONE step */
scratch.opcode = EEOP_DONE; scratch.opcode = EEOP_DONE_RETURN;
ExprEvalPushStep(elemstate, &scratch); ExprEvalPushStep(elemstate, &scratch);
/* and ready the subexpression */ /* and ready the subexpression */
ExecReadyExpr(elemstate); ExecReadyExpr(elemstate);
@ -3991,7 +3991,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
scratch.resvalue = NULL; scratch.resvalue = NULL;
scratch.resnull = NULL; scratch.resnull = NULL;
scratch.opcode = EEOP_DONE; scratch.opcode = EEOP_DONE_NO_RETURN;
ExprEvalPushStep(state, &scratch); ExprEvalPushStep(state, &scratch);
ExecReadyExpr(state); ExecReadyExpr(state);
@ -4258,7 +4258,7 @@ ExecBuildHash32FromAttrs(TupleDesc desc, const TupleTableSlotOps *ops,
scratch.resvalue = NULL; scratch.resvalue = NULL;
scratch.resnull = NULL; scratch.resnull = NULL;
scratch.opcode = EEOP_DONE; scratch.opcode = EEOP_DONE_RETURN;
ExprEvalPushStep(state, &scratch); ExprEvalPushStep(state, &scratch);
ExecReadyExpr(state); ExecReadyExpr(state);
@ -4431,7 +4431,7 @@ ExecBuildHash32Expr(TupleDesc desc, const TupleTableSlotOps *ops,
scratch.resvalue = NULL; scratch.resvalue = NULL;
scratch.resnull = NULL; scratch.resnull = NULL;
scratch.opcode = EEOP_DONE; scratch.opcode = EEOP_DONE_RETURN;
ExprEvalPushStep(state, &scratch); ExprEvalPushStep(state, &scratch);
ExecReadyExpr(state); ExecReadyExpr(state);
@ -4586,7 +4586,7 @@ ExecBuildGroupingEqual(TupleDesc ldesc, TupleDesc rdesc,
scratch.resvalue = NULL; scratch.resvalue = NULL;
scratch.resnull = NULL; scratch.resnull = NULL;
scratch.opcode = EEOP_DONE; scratch.opcode = EEOP_DONE_RETURN;
ExprEvalPushStep(state, &scratch); ExprEvalPushStep(state, &scratch);
ExecReadyExpr(state); ExecReadyExpr(state);
@ -4722,7 +4722,7 @@ ExecBuildParamSetEqual(TupleDesc desc,
scratch.resvalue = NULL; scratch.resvalue = NULL;
scratch.resnull = NULL; scratch.resnull = NULL;
scratch.opcode = EEOP_DONE; scratch.opcode = EEOP_DONE_RETURN;
ExprEvalPushStep(state, &scratch); ExprEvalPushStep(state, &scratch);
ExecReadyExpr(state); ExecReadyExpr(state);

View File

@ -246,7 +246,8 @@ ExecReadyInterpretedExpr(ExprState *state)
/* Simple validity checks on expression */ /* Simple validity checks on expression */
Assert(state->steps_len >= 1); Assert(state->steps_len >= 1);
Assert(state->steps[state->steps_len - 1].opcode == EEOP_DONE); Assert(state->steps[state->steps_len - 1].opcode == EEOP_DONE_RETURN ||
state->steps[state->steps_len - 1].opcode == EEOP_DONE_NO_RETURN);
/* /*
* Don't perform redundant initialization. This is unreachable in current * Don't perform redundant initialization. This is unreachable in current
@ -469,7 +470,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
*/ */
#if defined(EEO_USE_COMPUTED_GOTO) #if defined(EEO_USE_COMPUTED_GOTO)
static const void *const dispatch_table[] = { static const void *const dispatch_table[] = {
&&CASE_EEOP_DONE, &&CASE_EEOP_DONE_RETURN,
&&CASE_EEOP_DONE_NO_RETURN,
&&CASE_EEOP_INNER_FETCHSOME, &&CASE_EEOP_INNER_FETCHSOME,
&&CASE_EEOP_OUTER_FETCHSOME, &&CASE_EEOP_OUTER_FETCHSOME,
&&CASE_EEOP_SCAN_FETCHSOME, &&CASE_EEOP_SCAN_FETCHSOME,
@ -612,9 +614,16 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_SWITCH() EEO_SWITCH()
{ {
EEO_CASE(EEOP_DONE) EEO_CASE(EEOP_DONE_RETURN)
{ {
goto out; *isnull = state->resnull;
return state->resvalue;
}
EEO_CASE(EEOP_DONE_NO_RETURN)
{
Assert(isnull == NULL);
return (Datum) 0;
} }
EEO_CASE(EEOP_INNER_FETCHSOME) EEO_CASE(EEOP_INNER_FETCHSOME)
@ -2188,13 +2197,13 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
{ {
/* unreachable */ /* unreachable */
Assert(false); Assert(false);
goto out; goto out_error;
} }
} }
out: out_error:
*isnull = state->resnull; pg_unreachable();
return state->resvalue; return (Datum) 0;
} }
/* /*

View File

@ -816,11 +816,8 @@ advance_transition_function(AggState *aggstate,
static void static void
advance_aggregates(AggState *aggstate) advance_aggregates(AggState *aggstate)
{ {
bool dummynull; ExecEvalExprNoReturnSwitchContext(aggstate->phase->evaltrans,
aggstate->tmpcontext);
ExecEvalExprSwitchContext(aggstate->phase->evaltrans,
aggstate->tmpcontext,
&dummynull);
} }
/* /*

View File

@ -321,7 +321,7 @@ llvm_compile_expr(ExprState *state)
switch (opcode) switch (opcode)
{ {
case EEOP_DONE: case EEOP_DONE_RETURN:
{ {
LLVMValueRef v_tmpisnull; LLVMValueRef v_tmpisnull;
LLVMValueRef v_tmpvalue; LLVMValueRef v_tmpvalue;
@ -335,6 +335,10 @@ llvm_compile_expr(ExprState *state)
break; break;
} }
case EEOP_DONE_NO_RETURN:
LLVMBuildRet(b, l_sizet_const(0));
break;
case EEOP_INNER_FETCHSOME: case EEOP_INNER_FETCHSOME:
case EEOP_OUTER_FETCHSOME: case EEOP_OUTER_FETCHSOME:
case EEOP_SCAN_FETCHSOME: case EEOP_SCAN_FETCHSOME:

View File

@ -65,8 +65,11 @@ typedef struct ExprEvalRowtypeCache
*/ */
typedef enum ExprEvalOp typedef enum ExprEvalOp
{ {
/* entire expression has been evaluated completely, return */ /* entire expression has been evaluated, return value */
EEOP_DONE, EEOP_DONE_RETURN,
/* entire expression has been evaluated, no return value */
EEOP_DONE_NO_RETURN,
/* apply slot_getsomeattrs on corresponding tuple slot */ /* apply slot_getsomeattrs on corresponding tuple slot */
EEOP_INNER_FETCHSOME, EEOP_INNER_FETCHSOME,

View File

@ -379,6 +379,34 @@ ExecEvalExpr(ExprState *state,
} }
#endif #endif
/*
* ExecEvalExprNoReturn
*
* Like ExecEvalExpr(), but for cases where no return value is expected,
* because the side-effects of expression evaluation are what's desired. This
* is e.g. used for projection and aggregate transition computation.
* Evaluate expression identified by "state" in the execution context
* given by "econtext".
*
* The caller should already have switched into the temporary memory context
* econtext->ecxt_per_tuple_memory. The convenience entry point
* ExecEvalExprNoReturnSwitchContext() is provided for callers who don't
* prefer to do the switch in an outer loop.
*/
#ifndef FRONTEND
static inline void
ExecEvalExprNoReturn(ExprState *state,
ExprContext *econtext)
{
PG_USED_FOR_ASSERTS_ONLY Datum retDatum;
retDatum = state->evalfunc(state, econtext, NULL);
Assert(retDatum == (Datum) 0);
}
#endif
/* /*
* ExecEvalExprSwitchContext * ExecEvalExprSwitchContext
* *
@ -400,6 +428,25 @@ ExecEvalExprSwitchContext(ExprState *state,
} }
#endif #endif
/*
* ExecEvalExprNoReturnSwitchContext
*
* Same as ExecEvalExprNoReturn, but get into the right allocation context
* explicitly.
*/
#ifndef FRONTEND
static inline void
ExecEvalExprNoReturnSwitchContext(ExprState *state,
ExprContext *econtext)
{
MemoryContext oldContext;
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
ExecEvalExprNoReturn(state, econtext);
MemoryContextSwitchTo(oldContext);
}
#endif
/* /*
* ExecProject * ExecProject
* *
@ -419,7 +466,6 @@ ExecProject(ProjectionInfo *projInfo)
ExprContext *econtext = projInfo->pi_exprContext; ExprContext *econtext = projInfo->pi_exprContext;
ExprState *state = &projInfo->pi_state; ExprState *state = &projInfo->pi_state;
TupleTableSlot *slot = state->resultslot; TupleTableSlot *slot = state->resultslot;
bool isnull;
/* /*
* Clear any former contents of the result slot. This makes it safe for * Clear any former contents of the result slot. This makes it safe for
@ -427,8 +473,8 @@ ExecProject(ProjectionInfo *projInfo)
*/ */
ExecClearTuple(slot); ExecClearTuple(slot);
/* Run the expression, discarding scalar result from the last column. */ /* Run the expression */
(void) ExecEvalExprSwitchContext(state, econtext, &isnull); ExecEvalExprNoReturnSwitchContext(state, econtext);
/* /*
* Successfully formed a result row. Mark the result slot as containing a * Successfully formed a result row. Mark the result slot as containing a