($INBOX_DIR/description missing)
help / color / mirror / Atom feed[PATCH v2 1/2] Variable length FunctionCallInfoData.
30+ messages / 8 participants
[nested] [flat]
* [PATCH v2 1/2] Variable length FunctionCallInfoData.
@ 2018-06-05 17:24 Andres Freund <[email protected]>
0 siblings, 0 replies; 30+ messages in thread
From: Andres Freund @ 2018-06-05 17:24 UTC (permalink / raw)
Author: Andres Freund
Reviewed-By:
Discussion: https://postgr.es/m/
Backpatch:
---
src/backend/commands/event_trigger.c | 8 +-
src/backend/commands/functioncmds.c | 14 +-
src/backend/commands/tablecmds.c | 8 +-
src/backend/commands/trigger.c | 12 +-
src/backend/executor/execExpr.c | 81 +--
src/backend/executor/execExprInterp.c | 127 ++--
src/backend/executor/execSRF.c | 39 +-
src/backend/executor/functions.c | 4 +-
src/backend/executor/nodeAgg.c | 74 +--
src/backend/executor/nodeWindowAgg.c | 65 +-
src/backend/jit/llvm/llvmjit_expr.c | 20 +-
src/backend/tcop/fastpath.c | 43 +-
src/backend/utils/adt/arrayfuncs.c | 123 ++--
src/backend/utils/adt/int.c | 20 +-
src/backend/utils/adt/oid.c | 20 +-
src/backend/utils/adt/rowtypes.c | 32 +-
src/backend/utils/fmgr/fmgr.c | 848 ++++++++++----------------
src/backend/utils/sort/sortsupport.c | 13 +-
src/include/executor/execExpr.h | 6 +-
src/include/executor/nodeAgg.h | 6 +-
src/include/fmgr.h | 30 +-
src/include/nodes/execnodes.h | 2 +-
src/include/postgres.h | 7 +
src/pl/plperl/plperl.c | 20 +-
src/pl/plpgsql/src/pl_exec.c | 8 +-
src/pl/plpython/plpy_exec.c | 4 +-
src/pl/tcl/pltcl.c | 17 +-
27 files changed, 764 insertions(+), 887 deletions(-)
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 9a702e4097e..6564cd7fe11 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1050,7 +1050,7 @@ EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
{
Oid fnoid = lfirst_oid(lc);
FmgrInfo flinfo;
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 0);
PgStat_FunctionCallUsage fcusage;
elog(DEBUG1, "EventTriggerInvoke %u", fnoid);
@@ -1070,10 +1070,10 @@ EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
fmgr_info(fnoid, &flinfo);
/* Call the function, passing no arguments but setting a context. */
- InitFunctionCallInfoData(fcinfo, &flinfo, 0,
+ InitFunctionCallInfoData(*fcinfo, &flinfo, 0,
InvalidOid, (Node *) trigdata, NULL);
- pgstat_init_function_usage(&fcinfo, &fcusage);
- FunctionCallInvoke(&fcinfo);
+ pgstat_init_function_usage(fcinfo, &fcusage);
+ FunctionCallInvoke(fcinfo);
pgstat_end_function_usage(&fcusage, true);
/* Reclaim memory. */
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 3fd73a69f04..1eaf8011a96 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -2215,7 +2215,7 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver
int i;
AclResult aclresult;
FmgrInfo flinfo;
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 3);
CallContext *callcontext;
EState *estate;
ExprContext *econtext;
@@ -2277,7 +2277,7 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver
InvokeFunctionExecuteHook(fexpr->funcid);
fmgr_info(fexpr->funcid, &flinfo);
fmgr_info_set_expr((Node *) fexpr, &flinfo);
- InitFunctionCallInfoData(fcinfo, &flinfo, nargs, fexpr->inputcollid, (Node *) callcontext, NULL);
+ InitFunctionCallInfoData(*fcinfo, &flinfo, nargs, fexpr->inputcollid, (Node *) callcontext, NULL);
/*
* Evaluate procedure arguments inside a suitable execution context. Note
@@ -2298,14 +2298,14 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver
val = ExecEvalExprSwitchContext(exprstate, econtext, &isnull);
- fcinfo.arg[i] = val;
- fcinfo.argnull[i] = isnull;
+ fcinfo->args[i].datum = val;
+ fcinfo->args[i].isnull = isnull;
i++;
}
- pgstat_init_function_usage(&fcinfo, &fcusage);
- retval = FunctionCallInvoke(&fcinfo);
+ pgstat_init_function_usage(fcinfo, &fcusage);
+ retval = FunctionCallInvoke(fcinfo);
pgstat_end_function_usage(&fcusage, true);
if (fexpr->funcresulttype == VOIDOID)
@@ -2326,7 +2326,7 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver
TupOutputState *tstate;
TupleTableSlot *slot;
- if (fcinfo.isnull)
+ if (fcinfo->isnull)
elog(ERROR, "procedure returned null record");
td = DatumGetHeapTupleHeader(retval);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index e10d3dbf3dd..9a1a3895522 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8600,7 +8600,7 @@ validateForeignKeyConstraint(char *conname,
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 0);
TriggerData trigdata;
/*
@@ -8608,7 +8608,7 @@ validateForeignKeyConstraint(char *conname,
*
* No parameters are passed, but we do set a context
*/
- MemSet(&fcinfo, 0, sizeof(fcinfo));
+ MemSet(&fcinfodata, 0, sizeof(fcinfodata));
/*
* We assume RI_FKey_check_ins won't look at flinfo...
@@ -8622,9 +8622,9 @@ validateForeignKeyConstraint(char *conname,
trigdata.tg_trigtuplebuf = scan->rs_cbuf;
trigdata.tg_newtuplebuf = InvalidBuffer;
- fcinfo.context = (Node *) &trigdata;
+ fcinfo->context = (Node *) &trigdata;
- RI_FKey_check_ins(&fcinfo);
+ RI_FKey_check_ins(fcinfo);
}
heap_endscan(scan);
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 240e85e3910..19fcd543caf 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2358,7 +2358,7 @@ ExecCallTriggerFunc(TriggerData *trigdata,
Instrumentation *instr,
MemoryContext per_tuple_context)
{
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 0);
PgStat_FunctionCallUsage fcusage;
Datum result;
MemoryContext oldContext;
@@ -2403,15 +2403,15 @@ ExecCallTriggerFunc(TriggerData *trigdata,
/*
* Call the function, passing no arguments but setting a context.
*/
- InitFunctionCallInfoData(fcinfo, finfo, 0,
+ InitFunctionCallInfoData(*fcinfo, finfo, 0,
InvalidOid, (Node *) trigdata, NULL);
- pgstat_init_function_usage(&fcinfo, &fcusage);
+ pgstat_init_function_usage(fcinfo, &fcusage);
MyTriggerDepth++;
PG_TRY();
{
- result = FunctionCallInvoke(&fcinfo);
+ result = FunctionCallInvoke(fcinfo);
}
PG_CATCH();
{
@@ -2429,11 +2429,11 @@ ExecCallTriggerFunc(TriggerData *trigdata,
* Trigger protocol allows function to return a null pointer, but NOT to
* set the isnull result flag.
*/
- if (fcinfo.isnull)
+ if (fcinfo->isnull)
ereport(ERROR,
(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
errmsg("trigger function %u returned null value",
- fcinfo.flinfo->fn_oid)));
+ fcinfo->flinfo->fn_oid)));
/*
* If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index e284fd71d75..a39c3eabf35 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -965,7 +965,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
/* Set up the primary fmgr lookup information */
finfo = palloc0(sizeof(FmgrInfo));
- fcinfo = palloc0(sizeof(FunctionCallInfoData));
+ fcinfo = palloc0(SizeForFunctionCallInfoData(2));
fmgr_info(opexpr->opfuncid, finfo);
fmgr_info_set_expr((Node *) node, finfo);
InitFunctionCallInfoData(*fcinfo, finfo, 2,
@@ -973,7 +973,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
/* Evaluate scalar directly into left function argument */
ExecInitExprRec(scalararg, state,
- &fcinfo->arg[0], &fcinfo->argnull[0]);
+ &fcinfo->args[0].datum, &fcinfo->args[0].isnull);
/*
* Evaluate array argument into our return value. There's no
@@ -1262,7 +1262,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
/* lookup the source type's output function */
scratch.d.iocoerce.finfo_out = palloc0(sizeof(FmgrInfo));
- scratch.d.iocoerce.fcinfo_data_out = palloc0(sizeof(FunctionCallInfoData));
+ scratch.d.iocoerce.fcinfo_data_out = palloc0(SizeForFunctionCallInfoData(1));
getTypeOutputInfo(exprType((Node *) iocoerce->arg),
&iofunc, &typisvarlena);
@@ -1274,7 +1274,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
/* lookup the result type's input function */
scratch.d.iocoerce.finfo_in = palloc0(sizeof(FmgrInfo));
- scratch.d.iocoerce.fcinfo_data_in = palloc0(sizeof(FunctionCallInfoData));
+ scratch.d.iocoerce.fcinfo_data_in = palloc0(SizeForFunctionCallInfoData(3));
getTypeInputInfo(iocoerce->resulttype,
&iofunc, &typioparam);
@@ -1289,10 +1289,10 @@ ExecInitExprRec(Expr *node, ExprState *state,
* function, since they're constants.
*/
fcinfo_in = scratch.d.iocoerce.fcinfo_data_in;
- fcinfo_in->arg[1] = ObjectIdGetDatum(typioparam);
- fcinfo_in->argnull[1] = false;
- fcinfo_in->arg[2] = Int32GetDatum(-1);
- fcinfo_in->argnull[2] = false;
+ fcinfo_in->args[1].datum = ObjectIdGetDatum(typioparam);
+ fcinfo_in->args[1].isnull = false;
+ fcinfo_in->args[2].datum = Int32GetDatum(-1);
+ fcinfo_in->args[2].isnull = false;
ExprEvalPushStep(state, &scratch);
break;
@@ -1733,7 +1733,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
/* Set up the primary fmgr lookup information */
finfo = palloc0(sizeof(FmgrInfo));
- fcinfo = palloc0(sizeof(FunctionCallInfoData));
+ fcinfo = palloc0(SizeForFunctionCallInfoData(2));
fmgr_info(proc, finfo);
fmgr_info_set_expr((Node *) node, finfo);
InitFunctionCallInfoData(*fcinfo, finfo, 2,
@@ -1748,9 +1748,9 @@ ExecInitExprRec(Expr *node, ExprState *state,
/* evaluate left and right args directly into fcinfo */
ExecInitExprRec(left_expr, state,
- &fcinfo->arg[0], &fcinfo->argnull[0]);
+ &fcinfo->args[0].datum, &fcinfo->args[0].isnull);
ExecInitExprRec(right_expr, state,
- &fcinfo->arg[1], &fcinfo->argnull[1]);
+ &fcinfo->args[1].datum, &fcinfo->args[1].isnull);
scratch.opcode = EEOP_ROWCOMPARE_STEP;
scratch.d.rowcompare_step.finfo = finfo;
@@ -1876,7 +1876,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
/* Perform function lookup */
finfo = palloc0(sizeof(FmgrInfo));
- fcinfo = palloc0(sizeof(FunctionCallInfoData));
+ fcinfo = palloc0(SizeForFunctionCallInfoData(2));
fmgr_info(typentry->cmp_proc, finfo);
fmgr_info_set_expr((Node *) node, finfo);
InitFunctionCallInfoData(*fcinfo, finfo, 2,
@@ -2185,7 +2185,7 @@ ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
/* Allocate function lookup data and parameter workspace for this call */
scratch->d.func.finfo = palloc0(sizeof(FmgrInfo));
- scratch->d.func.fcinfo_data = palloc0(sizeof(FunctionCallInfoData));
+ scratch->d.func.fcinfo_data = palloc0(SizeForFunctionCallInfoData(nargs));
flinfo = scratch->d.func.finfo;
fcinfo = scratch->d.func.fcinfo_data;
@@ -2224,13 +2224,14 @@ ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
*/
Const *con = (Const *) arg;
- fcinfo->arg[argno] = con->constvalue;
- fcinfo->argnull[argno] = con->constisnull;
+ fcinfo->args[argno].datum = con->constvalue;
+ fcinfo->args[argno].isnull = con->constisnull;
}
else
{
ExecInitExprRec(arg, state,
- &fcinfo->arg[argno], &fcinfo->argnull[argno]);
+ &fcinfo->args[argno].datum,
+ &fcinfo->args[argno].isnull);
}
argno++;
}
@@ -2863,10 +2864,11 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
int numInputs = pertrans->numInputs;
int argno;
int setno;
- FunctionCallInfo trans_fcinfo = &pertrans->transfn_fcinfo;
+ FunctionCallInfo trans_fcinfo = pertrans->transfn_fcinfo;
ListCell *arg;
ListCell *bail;
List *adjust_bailout = NIL;
+ NullableDatum *strictargs = NULL;
bool *strictnulls = NULL;
/*
@@ -2904,7 +2906,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
Assert(pertrans->numSortCols == 0);
Assert(list_length(pertrans->aggref->args) == 1);
- strictnulls = trans_fcinfo->argnull + 1;
+ strictargs = trans_fcinfo->args + 1;
source_tle = (TargetEntry *) linitial(pertrans->aggref->args);
/*
@@ -2918,21 +2920,21 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
* value
*/
ExecInitExprRec(source_tle->expr, state,
- &trans_fcinfo->arg[argno + 1],
- &trans_fcinfo->argnull[argno + 1]);
+ &trans_fcinfo->args[argno + 1].datum,
+ &trans_fcinfo->args[argno + 1].isnull);
}
else
{
- FunctionCallInfo ds_fcinfo = &pertrans->deserialfn_fcinfo;
+ FunctionCallInfo ds_fcinfo = pertrans->deserialfn_fcinfo;
/* evaluate argument */
ExecInitExprRec(source_tle->expr, state,
- &ds_fcinfo->arg[0],
- &ds_fcinfo->argnull[0]);
+ &ds_fcinfo->args[0].datum,
+ &ds_fcinfo->args[0].isnull);
/* Dummy second argument for type-safety reasons */
- ds_fcinfo->arg[1] = PointerGetDatum(NULL);
- ds_fcinfo->argnull[1] = false;
+ ds_fcinfo->args[1].datum = PointerGetDatum(NULL);
+ ds_fcinfo->args[1].isnull = false;
/*
* Don't call a strict deserialization function with NULL
@@ -2946,8 +2948,8 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
scratch.d.agg_deserialize.aggstate = aggstate;
scratch.d.agg_deserialize.fcinfo_data = ds_fcinfo;
scratch.d.agg_deserialize.jumpnull = -1; /* adjust later */
- scratch.resvalue = &trans_fcinfo->arg[argno + 1];
- scratch.resnull = &trans_fcinfo->argnull[argno + 1];
+ scratch.resvalue = &trans_fcinfo->args[argno + 1].datum;
+ scratch.resnull = &trans_fcinfo->args[argno + 1].isnull;
ExprEvalPushStep(state, &scratch);
adjust_bailout = lappend_int(adjust_bailout,
@@ -2964,7 +2966,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
/*
* Normal transition function without ORDER BY / DISTINCT.
*/
- strictnulls = trans_fcinfo->argnull + 1;
+ strictargs = trans_fcinfo->args + 1;
foreach(arg, pertrans->aggref->args)
{
@@ -2975,8 +2977,8 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
* value
*/
ExecInitExprRec(source_tle->expr, state,
- &trans_fcinfo->arg[argno + 1],
- &trans_fcinfo->argnull[argno + 1]);
+ &trans_fcinfo->args[argno + 1].datum,
+ &trans_fcinfo->args[argno + 1].isnull);
argno++;
}
}
@@ -3024,8 +3026,12 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
*/
if (trans_fcinfo->flinfo->fn_strict && numInputs > 0)
{
- scratch.opcode = EEOP_AGG_STRICT_INPUT_CHECK;
+ if (strictnulls)
+ scratch.opcode = EEOP_AGG_STRICT_INPUT_CHECK_NULLS;
+ else
+ scratch.opcode = EEOP_AGG_STRICT_INPUT_CHECK_ARGS;
scratch.d.agg_strict_input_check.nulls = strictnulls;
+ scratch.d.agg_strict_input_check.args = strictargs;
scratch.d.agg_strict_input_check.jumpnull = -1; /* adjust later */
scratch.d.agg_strict_input_check.nargs = numInputs;
ExprEvalPushStep(state, &scratch);
@@ -3079,7 +3085,8 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
Assert(as->d.jump.jumpdone == -1);
as->d.jump.jumpdone = state->steps_len;
}
- else if (as->opcode == EEOP_AGG_STRICT_INPUT_CHECK)
+ else if (as->opcode == EEOP_AGG_STRICT_INPUT_CHECK_ARGS ||
+ as->opcode == EEOP_AGG_STRICT_INPUT_CHECK_NULLS)
{
Assert(as->d.agg_strict_input_check.jumpnull == -1);
as->d.agg_strict_input_check.jumpnull = state->steps_len;
@@ -3284,7 +3291,7 @@ ExecBuildGroupingEqual(TupleDesc ldesc, TupleDesc rdesc,
/* Set up the primary fmgr lookup information */
finfo = palloc0(sizeof(FmgrInfo));
- fcinfo = palloc0(sizeof(FunctionCallInfoData));
+ fcinfo = palloc0(SizeForFunctionCallInfoData(2));
fmgr_info(foid, finfo);
fmgr_info_set_expr(NULL, finfo);
InitFunctionCallInfoData(*fcinfo, finfo, 2,
@@ -3294,16 +3301,16 @@ ExecBuildGroupingEqual(TupleDesc ldesc, TupleDesc rdesc,
scratch.opcode = EEOP_INNER_VAR;
scratch.d.var.attnum = attno - 1;
scratch.d.var.vartype = latt->atttypid;
- scratch.resvalue = &fcinfo->arg[0];
- scratch.resnull = &fcinfo->argnull[0];
+ scratch.resvalue = &fcinfo->args[0].datum;
+ scratch.resnull = &fcinfo->args[0].isnull;
ExprEvalPushStep(state, &scratch);
/* right arg */
scratch.opcode = EEOP_OUTER_VAR;
scratch.d.var.attnum = attno - 1;
scratch.d.var.vartype = ratt->atttypid;
- scratch.resvalue = &fcinfo->arg[1];
- scratch.resnull = &fcinfo->argnull[1];
+ scratch.resvalue = &fcinfo->args[1].datum;
+ scratch.resnull = &fcinfo->args[1].isnull;
ExprEvalPushStep(state, &scratch);
/* evaluate distinctness */
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 1b0946b02f2..3716585901e 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -386,7 +386,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_ALTERNATIVE_SUBPLAN,
&&CASE_EEOP_AGG_STRICT_DESERIALIZE,
&&CASE_EEOP_AGG_DESERIALIZE,
- &&CASE_EEOP_AGG_STRICT_INPUT_CHECK,
+ &&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_BYVAL,
@@ -661,14 +662,14 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_CASE(EEOP_FUNCEXPR_STRICT)
{
FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
- bool *argnull = fcinfo->argnull;
+ NullableDatum *args = fcinfo->args;
int argno;
Datum d;
/* strict function, so check for NULL args */
for (argno = 0; argno < op->d.func.nargs; argno++)
{
- if (argnull[argno])
+ if (args[argno].isnull)
{
*op->resnull = true;
goto strictfail;
@@ -1084,8 +1085,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
FunctionCallInfo fcinfo_out;
fcinfo_out = op->d.iocoerce.fcinfo_data_out;
- fcinfo_out->arg[0] = *op->resvalue;
- fcinfo_out->argnull[0] = false;
+ fcinfo_out->args[0].datum = *op->resvalue;
+ fcinfo_out->args[0].isnull = false;
fcinfo_out->isnull = false;
str = DatumGetCString(FunctionCallInvoke(fcinfo_out));
@@ -1101,8 +1102,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
Datum d;
fcinfo_in = op->d.iocoerce.fcinfo_data_in;
- fcinfo_in->arg[0] = PointerGetDatum(str);
- fcinfo_in->argnull[0] = *op->resnull;
+ fcinfo_in->args[0].datum = PointerGetDatum(str);
+ fcinfo_in->args[0].isnull = *op->resnull;
/* second and third arguments are already set up */
fcinfo_in->isnull = false;
@@ -1139,13 +1140,13 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
/* check function arguments for NULLness */
- if (fcinfo->argnull[0] && fcinfo->argnull[1])
+ if (fcinfo->args[0].isnull && fcinfo->args[1].isnull)
{
/* Both NULL? Then is not distinct... */
*op->resvalue = BoolGetDatum(false);
*op->resnull = false;
}
- else if (fcinfo->argnull[0] || fcinfo->argnull[1])
+ else if (fcinfo->args[0].isnull || fcinfo->args[1].isnull)
{
/* Only one is NULL? Then is distinct... */
*op->resvalue = BoolGetDatum(true);
@@ -1171,12 +1172,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
{
FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
- if (fcinfo->argnull[0] && fcinfo->argnull[1])
+ if (fcinfo->args[0].isnull && fcinfo->args[1].isnull)
{
*op->resvalue = BoolGetDatum(true);
*op->resnull = false;
}
- else if (fcinfo->argnull[0] || fcinfo->argnull[1])
+ else if (fcinfo->args[0].isnull || fcinfo->args[1].isnull)
{
*op->resvalue = BoolGetDatum(false);
*op->resnull = false;
@@ -1202,7 +1203,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
/* if either argument is NULL they can't be equal */
- if (!fcinfo->argnull[0] && !fcinfo->argnull[1])
+ if (!fcinfo->args[0].isnull && !fcinfo->args[1].isnull)
{
Datum result;
@@ -1220,8 +1221,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
}
/* Arguments aren't equal, so return the first one */
- *op->resvalue = fcinfo->arg[0];
- *op->resnull = fcinfo->argnull[0];
+ *op->resvalue = fcinfo->args[0].datum;
+ *op->resnull = fcinfo->args[0].isnull;
EEO_NEXT();
}
@@ -1287,7 +1288,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
/* force NULL result if strict fn and NULL input */
if (op->d.rowcompare_step.finfo->fn_strict &&
- (fcinfo->argnull[0] || fcinfo->argnull[1]))
+ (fcinfo->args[0].isnull || fcinfo->args[1].isnull))
{
*op->resnull = true;
EEO_JUMP(op->d.rowcompare_step.jumpnull);
@@ -1526,10 +1527,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
/* evaluate a strict aggregate deserialization function */
EEO_CASE(EEOP_AGG_STRICT_DESERIALIZE)
{
- bool *argnull = op->d.agg_deserialize.fcinfo_data->argnull;
-
/* Don't call a strict deserialization function with NULL input */
- if (argnull[0])
+ if (op->d.agg_deserialize.fcinfo_data->args[0].isnull)
EEO_JUMP(op->d.agg_deserialize.jumpnull);
/* fallthrough */
@@ -1559,15 +1558,29 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
* Check that a strict aggregate transition / combination function's
* input is not NULL.
*/
- EEO_CASE(EEOP_AGG_STRICT_INPUT_CHECK)
+ EEO_CASE(EEOP_AGG_STRICT_INPUT_CHECK_ARGS)
{
int argno;
- bool *nulls = op->d.agg_strict_input_check.nulls;
+ NullableDatum *args = op->d.agg_strict_input_check.args;
int nargs = op->d.agg_strict_input_check.nargs;
for (argno = 0; argno < nargs; argno++)
{
- if (nulls[argno])
+ if (args[argno].isnull)
+ EEO_JUMP(op->d.agg_strict_input_check.jumpnull);
+ }
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_AGG_STRICT_INPUT_CHECK_NULLS)
+ {
+ int argno;
+ bool *isnull = op->d.agg_strict_input_check.nulls;
+ int nargs = op->d.agg_strict_input_check.nargs;
+
+ for (argno = 0; argno < nargs; argno++)
+ {
+ if (isnull[argno])
EEO_JUMP(op->d.agg_strict_input_check.jumpnull);
}
EEO_NEXT();
@@ -1643,7 +1656,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
Assert(pertrans->transtypeByVal);
- fcinfo = &pertrans->transfn_fcinfo;
+ fcinfo = pertrans->transfn_fcinfo;
/* cf. select_current_set() */
aggstate->curaggcontext = op->d.agg_trans.aggcontext;
@@ -1655,8 +1668,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
/* invoke transition function in per-tuple context */
oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory);
- fcinfo->arg[0] = pergroup->transValue;
- fcinfo->argnull[0] = pergroup->transValueIsNull;
+ fcinfo->args[0].datum = pergroup->transValue;
+ fcinfo->args[0].isnull = pergroup->transValueIsNull;
fcinfo->isnull = false; /* just in case transfn doesn't set it */
newVal = FunctionCallInvoke(fcinfo);
@@ -1694,7 +1707,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
Assert(!pertrans->transtypeByVal);
- fcinfo = &pertrans->transfn_fcinfo;
+ fcinfo = pertrans->transfn_fcinfo;
/* cf. select_current_set() */
aggstate->curaggcontext = op->d.agg_trans.aggcontext;
@@ -1706,8 +1719,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
/* invoke transition function in per-tuple context */
oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory);
- fcinfo->arg[0] = pergroup->transValue;
- fcinfo->argnull[0] = pergroup->transValueIsNull;
+ fcinfo->args[0].datum = pergroup->transValue;
+ fcinfo->args[0].isnull = pergroup->transValueIsNull;
fcinfo->isnull = false; /* just in case transfn doesn't set it */
newVal = FunctionCallInvoke(fcinfo);
@@ -2060,7 +2073,7 @@ ExecJustApplyFuncToCase(ExprState *state, ExprContext *econtext, bool *isnull)
{
ExprEvalStep *op = &state->steps[0];
FunctionCallInfo fcinfo;
- bool *argnull;
+ NullableDatum *args;
int argno;
Datum d;
@@ -2074,12 +2087,12 @@ ExecJustApplyFuncToCase(ExprState *state, ExprContext *econtext, bool *isnull)
op++;
fcinfo = op->d.func.fcinfo_data;
- argnull = fcinfo->argnull;
+ args = fcinfo->args;
/* strict function, so check for NULL args */
for (argno = 0; argno < op->d.func.nargs; argno++)
{
- if (argnull[argno])
+ if (args[argno].isnull)
{
*isnull = true;
return (Datum) 0;
@@ -2205,14 +2218,14 @@ ExecEvalFuncExprStrictFusage(ExprState *state, ExprEvalStep *op,
FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
PgStat_FunctionCallUsage fcusage;
- bool *argnull = fcinfo->argnull;
+ NullableDatum *args = fcinfo->args;
int argno;
Datum d;
/* strict function, so check for NULL args */
for (argno = 0; argno < op->d.func.nargs; argno++)
{
- if (argnull[argno])
+ if (args[argno].isnull)
{
*op->resnull = true;
return;
@@ -2303,7 +2316,7 @@ void
ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op)
{
SQLValueFunction *svf = op->d.sqlvaluefunction.svf;
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 0);
*op->resnull = false;
@@ -2335,24 +2348,24 @@ ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op)
case SVFOP_CURRENT_ROLE:
case SVFOP_CURRENT_USER:
case SVFOP_USER:
- InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
- *op->resvalue = current_user(&fcinfo);
- *op->resnull = fcinfo.isnull;
+ InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+ *op->resvalue = current_user(fcinfo);
+ *op->resnull = fcinfo->isnull;
break;
case SVFOP_SESSION_USER:
- InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
- *op->resvalue = session_user(&fcinfo);
- *op->resnull = fcinfo.isnull;
+ InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+ *op->resvalue = session_user(fcinfo);
+ *op->resnull = fcinfo->isnull;
break;
case SVFOP_CURRENT_CATALOG:
- InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
- *op->resvalue = current_database(&fcinfo);
- *op->resnull = fcinfo.isnull;
+ InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+ *op->resvalue = current_database(fcinfo);
+ *op->resnull = fcinfo->isnull;
break;
case SVFOP_CURRENT_SCHEMA:
- InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
- *op->resvalue = current_schema(&fcinfo);
- *op->resnull = fcinfo.isnull;
+ InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+ *op->resvalue = current_schema(fcinfo);
+ *op->resnull = fcinfo->isnull;
break;
}
}
@@ -2785,8 +2798,8 @@ ExecEvalMinMax(ExprState *state, ExprEvalStep *op)
int off;
/* set at initialization */
- Assert(fcinfo->argnull[0] == false);
- Assert(fcinfo->argnull[1] == false);
+ Assert(fcinfo->args[0].isnull == false);
+ Assert(fcinfo->args[1].isnull == false);
/* default to null result */
*op->resnull = true;
@@ -2808,8 +2821,8 @@ ExecEvalMinMax(ExprState *state, ExprEvalStep *op)
int cmpresult;
/* apply comparison function */
- fcinfo->arg[0] = *op->resvalue;
- fcinfo->arg[1] = values[off];
+ fcinfo->args[0].datum = *op->resvalue;
+ fcinfo->args[1].datum = values[off];
fcinfo->isnull = false;
cmpresult = DatumGetInt32(FunctionCallInvoke(fcinfo));
@@ -3361,7 +3374,7 @@ ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
* If the scalar is NULL, and the function is strict, return NULL; no
* point in iterating the loop.
*/
- if (fcinfo->argnull[0] && strictfunc)
+ if (fcinfo->args[0].isnull && strictfunc)
{
*op->resnull = true;
return;
@@ -3401,20 +3414,20 @@ ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
/* Get array element, checking for NULL */
if (bitmap && (*bitmap & bitmask) == 0)
{
- fcinfo->arg[1] = (Datum) 0;
- fcinfo->argnull[1] = true;
+ fcinfo->args[1].datum = (Datum) 0;
+ fcinfo->args[1].isnull = true;
}
else
{
elt = fetch_att(s, typbyval, typlen);
s = att_addlength_pointer(s, typlen, s);
s = (char *) att_align_nominal(s, typalign);
- fcinfo->arg[1] = elt;
- fcinfo->argnull[1] = false;
+ fcinfo->args[1].datum = elt;
+ fcinfo->args[1].isnull = false;
}
/* Call comparison function */
- if (fcinfo->argnull[1] && strictfunc)
+ if (fcinfo->args[1].isnull && strictfunc)
{
fcinfo->isnull = true;
thisresult = (Datum) 0;
@@ -4013,7 +4026,7 @@ ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
void
ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup pergroup)
{
- FunctionCallInfo fcinfo = &pertrans->transfn_fcinfo;
+ FunctionCallInfo fcinfo = pertrans->transfn_fcinfo;
MemoryContext oldContext;
/*
@@ -4024,7 +4037,7 @@ ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup
*/
oldContext = MemoryContextSwitchTo(
aggstate->curaggcontext->ecxt_per_tuple_memory);
- pergroup->transValue = datumCopy(fcinfo->arg[1],
+ pergroup->transValue = datumCopy(fcinfo->args[1].datum,
pertrans->transtypeByVal,
pertrans->transtypeLen);
pergroup->transValueIsNull = false;
diff --git a/src/backend/executor/execSRF.c b/src/backend/executor/execSRF.c
index b97b8d797ec..a50670f537a 100644
--- a/src/backend/executor/execSRF.c
+++ b/src/backend/executor/execSRF.c
@@ -109,7 +109,7 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
Oid funcrettype;
bool returnsTuple;
bool returnsSet = false;
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 3);
PgStat_FunctionCallUsage fcusage;
ReturnSetInfo rsinfo;
HeapTupleData tmptup;
@@ -157,9 +157,9 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
* This path is similar to ExecMakeFunctionResultSet.
*/
returnsSet = setexpr->funcReturnsSet;
- InitFunctionCallInfoData(fcinfo, &(setexpr->func),
+ InitFunctionCallInfoData(*fcinfo, &(setexpr->func),
list_length(setexpr->args),
- setexpr->fcinfo_data.fncollation,
+ setexpr->fcinfo->fncollation,
NULL, (Node *) &rsinfo);
/*
@@ -174,7 +174,7 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
*/
MemoryContextReset(argContext);
oldcontext = MemoryContextSwitchTo(argContext);
- ExecEvalFuncArgs(&fcinfo, setexpr->args, econtext);
+ ExecEvalFuncArgs(fcinfo, setexpr->args, econtext);
MemoryContextSwitchTo(oldcontext);
/*
@@ -186,9 +186,9 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
{
int i;
- for (i = 0; i < fcinfo.nargs; i++)
+ for (i = 0; i < fcinfo->nargs; i++)
{
- if (fcinfo.argnull[i])
+ if (fcinfo->args[i].isnull)
goto no_function_result;
}
}
@@ -196,7 +196,7 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
else
{
/* Treat setexpr as a generic expression */
- InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+ InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
}
/*
@@ -224,11 +224,11 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
/* Call the function or expression one time */
if (!setexpr->elidedFuncState)
{
- pgstat_init_function_usage(&fcinfo, &fcusage);
+ pgstat_init_function_usage(fcinfo, &fcusage);
- fcinfo.isnull = false;
+ fcinfo->isnull = false;
rsinfo.isDone = ExprSingleResult;
- result = FunctionCallInvoke(&fcinfo);
+ result = FunctionCallInvoke(fcinfo);
pgstat_end_function_usage(&fcusage,
rsinfo.isDone != ExprMultipleResult);
@@ -236,7 +236,7 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
else
{
result =
- ExecEvalExpr(setexpr->elidedFuncState, econtext, &fcinfo.isnull);
+ ExecEvalExpr(setexpr->elidedFuncState, econtext, &fcinfo->isnull);
rsinfo.isDone = ExprSingleResult;
}
@@ -277,7 +277,7 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
*/
if (returnsTuple)
{
- if (!fcinfo.isnull)
+ if (!fcinfo->isnull)
{
HeapTupleHeader td = DatumGetHeapTupleHeader(result);
@@ -338,7 +338,7 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
else
{
/* Scalar-type case: just store the function result */
- tuplestore_putvalues(tupstore, tupdesc, &result, &fcinfo.isnull);
+ tuplestore_putvalues(tupstore, tupdesc, &result, &fcinfo->isnull);
}
/*
@@ -547,7 +547,7 @@ restart:
* rows from this SRF have been returned, otherwise ValuePerCall SRFs
* would reference freed memory after the first returned row.
*/
- fcinfo = &fcache->fcinfo_data;
+ fcinfo = fcache->fcinfo;
arguments = fcache->args;
if (!fcache->setArgsValid)
{
@@ -587,7 +587,7 @@ restart:
{
for (i = 0; i < fcinfo->nargs; i++)
{
- if (fcinfo->argnull[i])
+ if (fcinfo->args[i].isnull)
{
callit = false;
break;
@@ -704,7 +704,8 @@ init_sexpr(Oid foid, Oid input_collation, Expr *node,
fmgr_info_set_expr((Node *) sexpr->expr, &(sexpr->func));
/* Initialize the function call parameter struct as well */
- InitFunctionCallInfoData(sexpr->fcinfo_data, &(sexpr->func),
+ sexpr->fcinfo = (FunctionCallInfo) palloc(SizeForFunctionCallInfoData(list_length(sexpr->args)));
+ InitFunctionCallInfoData(*sexpr->fcinfo, &(sexpr->func),
list_length(sexpr->args),
input_collation, NULL, NULL);
@@ -820,9 +821,9 @@ ExecEvalFuncArgs(FunctionCallInfo fcinfo,
{
ExprState *argstate = (ExprState *) lfirst(arg);
- fcinfo->arg[i] = ExecEvalExpr(argstate,
- econtext,
- &fcinfo->argnull[i]);
+ fcinfo->args[i].datum = ExecEvalExpr(argstate,
+ econtext,
+ &fcinfo->args[i].isnull);
i++;
}
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 23545896d4d..672dcd981cd 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -933,8 +933,8 @@ postquel_sub_params(SQLFunctionCachePtr fcache,
{
ParamExternData *prm = ¶mLI->params[i];
- prm->value = fcinfo->arg[i];
- prm->isnull = fcinfo->argnull[i];
+ prm->value = fcinfo->args[i].datum;
+ prm->isnull = fcinfo->args[i].isnull;
prm->pflags = 0;
prm->ptype = fcache->pinfo->argtypes[i];
}
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 98d8483b720..e9bea4a46a2 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -553,7 +553,7 @@ advance_transition_function(AggState *aggstate,
AggStatePerTrans pertrans,
AggStatePerGroup pergroupstate)
{
- FunctionCallInfo fcinfo = &pertrans->transfn_fcinfo;
+ FunctionCallInfo fcinfo = pertrans->transfn_fcinfo;
MemoryContext oldContext;
Datum newVal;
@@ -568,7 +568,7 @@ advance_transition_function(AggState *aggstate,
for (i = 1; i <= numTransInputs; i++)
{
- if (fcinfo->argnull[i])
+ if (fcinfo->args[i].isnull)
return;
}
if (pergroupstate->noTransValue)
@@ -584,7 +584,7 @@ advance_transition_function(AggState *aggstate,
*/
oldContext = MemoryContextSwitchTo(
aggstate->curaggcontext->ecxt_per_tuple_memory);
- pergroupstate->transValue = datumCopy(fcinfo->arg[1],
+ pergroupstate->transValue = datumCopy(fcinfo->args[1].datum,
pertrans->transtypeByVal,
pertrans->transtypeLen);
pergroupstate->transValueIsNull = false;
@@ -613,8 +613,8 @@ advance_transition_function(AggState *aggstate,
/*
* OK to call the transition function
*/
- fcinfo->arg[0] = pergroupstate->transValue;
- fcinfo->argnull[0] = pergroupstate->transValueIsNull;
+ fcinfo->args[0].datum = pergroupstate->transValue;
+ fcinfo->args[0].isnull = pergroupstate->transValueIsNull;
fcinfo->isnull = false; /* just in case transfn doesn't set it */
newVal = FunctionCallInvoke(fcinfo);
@@ -717,7 +717,7 @@ process_ordered_aggregate_single(AggState *aggstate,
bool isDistinct = (pertrans->numDistinctCols > 0);
Datum newAbbrevVal = (Datum) 0;
Datum oldAbbrevVal = (Datum) 0;
- FunctionCallInfo fcinfo = &pertrans->transfn_fcinfo;
+ FunctionCallInfo fcinfo = pertrans->transfn_fcinfo;
Datum *newVal;
bool *isNull;
@@ -726,8 +726,8 @@ process_ordered_aggregate_single(AggState *aggstate,
tuplesort_performsort(pertrans->sortstates[aggstate->current_set]);
/* Load the column into argument 1 (arg 0 will be transition value) */
- newVal = fcinfo->arg + 1;
- isNull = fcinfo->argnull + 1;
+ newVal = &fcinfo->args[1].datum;
+ isNull = &fcinfo->args[1].isnull;
/*
* Note: if input type is pass-by-ref, the datums returned by the sort are
@@ -803,7 +803,7 @@ process_ordered_aggregate_multi(AggState *aggstate,
AggStatePerGroup pergroupstate)
{
ExprContext *tmpcontext = aggstate->tmpcontext;
- FunctionCallInfo fcinfo = &pertrans->transfn_fcinfo;
+ FunctionCallInfo fcinfo = pertrans->transfn_fcinfo;
TupleTableSlot *slot1 = pertrans->sortslot;
TupleTableSlot *slot2 = pertrans->uniqslot;
int numTransInputs = pertrans->numTransInputs;
@@ -843,8 +843,8 @@ process_ordered_aggregate_multi(AggState *aggstate,
/* Start from 1, since the 0th arg will be the transition value */
for (i = 0; i < numTransInputs; i++)
{
- fcinfo->arg[i + 1] = slot1->tts_values[i];
- fcinfo->argnull[i + 1] = slot1->tts_isnull[i];
+ fcinfo->args[i + 1].datum = slot1->tts_values[i];
+ fcinfo->args[i + 1].isnull = slot1->tts_isnull[i];
}
advance_transition_function(aggstate, pertrans, pergroupstate);
@@ -897,7 +897,7 @@ finalize_aggregate(AggState *aggstate,
AggStatePerGroup pergroupstate,
Datum *resultVal, bool *resultIsNull)
{
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 3);
bool anynull = false;
MemoryContext oldContext;
int i;
@@ -917,10 +917,10 @@ finalize_aggregate(AggState *aggstate,
{
ExprState *expr = (ExprState *) lfirst(lc);
- fcinfo.arg[i] = ExecEvalExpr(expr,
- aggstate->ss.ps.ps_ExprContext,
- &fcinfo.argnull[i]);
- anynull |= fcinfo.argnull[i];
+ fcinfo->args[i].datum = ExecEvalExpr(expr,
+ aggstate->ss.ps.ps_ExprContext,
+ &fcinfo->args[i].isnull);
+ anynull |= fcinfo->args[i].isnull;
i++;
}
@@ -934,27 +934,27 @@ finalize_aggregate(AggState *aggstate,
/* set up aggstate->curperagg for AggGetAggref() */
aggstate->curperagg = peragg;
- InitFunctionCallInfoData(fcinfo, &peragg->finalfn,
+ InitFunctionCallInfoData(*fcinfo, &peragg->finalfn,
numFinalArgs,
pertrans->aggCollation,
(void *) aggstate, NULL);
/* Fill in the transition state value */
- fcinfo.arg[0] = MakeExpandedObjectReadOnly(pergroupstate->transValue,
- pergroupstate->transValueIsNull,
- pertrans->transtypeLen);
- fcinfo.argnull[0] = pergroupstate->transValueIsNull;
+ fcinfo->args[0].datum = MakeExpandedObjectReadOnly(pergroupstate->transValue,
+ pergroupstate->transValueIsNull,
+ pertrans->transtypeLen);
+ fcinfo->args[0].isnull = pergroupstate->transValueIsNull;
anynull |= pergroupstate->transValueIsNull;
/* Fill any remaining argument positions with nulls */
for (; i < numFinalArgs; i++)
{
- fcinfo.arg[i] = (Datum) 0;
- fcinfo.argnull[i] = true;
+ fcinfo->args[i].datum = (Datum) 0;
+ fcinfo->args[i].isnull = true;
anynull = true;
}
- if (fcinfo.flinfo->fn_strict && anynull)
+ if (fcinfo->flinfo->fn_strict && anynull)
{
/* don't call a strict function with NULL inputs */
*resultVal = (Datum) 0;
@@ -962,8 +962,8 @@ finalize_aggregate(AggState *aggstate,
}
else
{
- *resultVal = FunctionCallInvoke(&fcinfo);
- *resultIsNull = fcinfo.isnull;
+ *resultVal = FunctionCallInvoke(fcinfo);
+ *resultIsNull = fcinfo->isnull;
}
aggstate->curperagg = NULL;
}
@@ -1018,12 +1018,12 @@ finalize_partialaggregate(AggState *aggstate,
}
else
{
- FunctionCallInfo fcinfo = &pertrans->serialfn_fcinfo;
+ FunctionCallInfo fcinfo = pertrans->serialfn_fcinfo;
- fcinfo->arg[0] = MakeExpandedObjectReadOnly(pergroupstate->transValue,
- pergroupstate->transValueIsNull,
- pertrans->transtypeLen);
- fcinfo->argnull[0] = pergroupstate->transValueIsNull;
+ fcinfo->args[0].datum = MakeExpandedObjectReadOnly(pergroupstate->transValue,
+ pergroupstate->transValueIsNull,
+ pertrans->transtypeLen);
+ fcinfo->args[0].isnull = pergroupstate->transValueIsNull;
*resultVal = FunctionCallInvoke(fcinfo);
*resultIsNull = fcinfo->isnull;
@@ -2925,7 +2925,8 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
fmgr_info(aggtransfn, &pertrans->transfn);
fmgr_info_set_expr((Node *) combinefnexpr, &pertrans->transfn);
- InitFunctionCallInfoData(pertrans->transfn_fcinfo,
+ pertrans->transfn_fcinfo = (FunctionCallInfo) palloc(SizeForFunctionCallInfoData(2));
+ InitFunctionCallInfoData(*pertrans->transfn_fcinfo,
&pertrans->transfn,
2,
pertrans->aggCollation,
@@ -2963,7 +2964,8 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
fmgr_info(aggtransfn, &pertrans->transfn);
fmgr_info_set_expr((Node *) transfnexpr, &pertrans->transfn);
- InitFunctionCallInfoData(pertrans->transfn_fcinfo,
+ pertrans->transfn_fcinfo = (FunctionCallInfo) palloc(SizeForFunctionCallInfoData(pertrans->numTransInputs + 1));
+ InitFunctionCallInfoData(*pertrans->transfn_fcinfo,
&pertrans->transfn,
pertrans->numTransInputs + 1,
pertrans->aggCollation,
@@ -3001,7 +3003,8 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
fmgr_info(aggserialfn, &pertrans->serialfn);
fmgr_info_set_expr((Node *) serialfnexpr, &pertrans->serialfn);
- InitFunctionCallInfoData(pertrans->serialfn_fcinfo,
+ pertrans->serialfn_fcinfo = (FunctionCallInfo) palloc(SizeForFunctionCallInfoData(1));
+ InitFunctionCallInfoData(*pertrans->serialfn_fcinfo,
&pertrans->serialfn,
1,
InvalidOid,
@@ -3015,7 +3018,8 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
fmgr_info(aggdeserialfn, &pertrans->deserialfn);
fmgr_info_set_expr((Node *) deserialfnexpr, &pertrans->deserialfn);
- InitFunctionCallInfoData(pertrans->deserialfn_fcinfo,
+ pertrans->deserialfn_fcinfo = (FunctionCallInfo) palloc(SizeForFunctionCallInfoData(2));
+ InitFunctionCallInfoData(*pertrans->deserialfn_fcinfo,
&pertrans->deserialfn,
2,
InvalidOid,
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 729c376d86b..032598bd5bb 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -243,8 +243,7 @@ advance_windowaggregate(WindowAggState *winstate,
{
WindowFuncExprState *wfuncstate = perfuncstate->wfuncstate;
int numArguments = perfuncstate->numArguments;
- FunctionCallInfoData fcinfodata;
- FunctionCallInfo fcinfo = &fcinfodata;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 3);
Datum newVal;
ListCell *arg;
int i;
@@ -273,8 +272,8 @@ advance_windowaggregate(WindowAggState *winstate,
{
ExprState *argstate = (ExprState *) lfirst(arg);
- fcinfo->arg[i] = ExecEvalExpr(argstate, econtext,
- &fcinfo->argnull[i]);
+ fcinfo->args[i].datum = ExecEvalExpr(argstate, econtext,
+ &fcinfo->args[i].isnull);
i++;
}
@@ -287,7 +286,7 @@ advance_windowaggregate(WindowAggState *winstate,
*/
for (i = 1; i <= numArguments; i++)
{
- if (fcinfo->argnull[i])
+ if (fcinfo->args[i].isnull)
{
MemoryContextSwitchTo(oldContext);
return;
@@ -306,7 +305,7 @@ advance_windowaggregate(WindowAggState *winstate,
if (peraggstate->transValueCount == 0 && peraggstate->transValueIsNull)
{
MemoryContextSwitchTo(peraggstate->aggcontext);
- peraggstate->transValue = datumCopy(fcinfo->arg[1],
+ peraggstate->transValue = datumCopy(fcinfo->args[1].datum,
peraggstate->transtypeByVal,
peraggstate->transtypeLen);
peraggstate->transValueIsNull = false;
@@ -339,8 +338,8 @@ advance_windowaggregate(WindowAggState *winstate,
numArguments + 1,
perfuncstate->winCollation,
(void *) winstate, NULL);
- fcinfo->arg[0] = peraggstate->transValue;
- fcinfo->argnull[0] = peraggstate->transValueIsNull;
+ fcinfo->args[0].datum = peraggstate->transValue;
+ fcinfo->args[0].isnull = peraggstate->transValueIsNull;
winstate->curaggcontext = peraggstate->aggcontext;
newVal = FunctionCallInvoke(fcinfo);
winstate->curaggcontext = NULL;
@@ -420,8 +419,7 @@ advance_windowaggregate_base(WindowAggState *winstate,
{
WindowFuncExprState *wfuncstate = perfuncstate->wfuncstate;
int numArguments = perfuncstate->numArguments;
- FunctionCallInfoData fcinfodata;
- FunctionCallInfo fcinfo = &fcinfodata;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 3);
Datum newVal;
ListCell *arg;
int i;
@@ -450,8 +448,8 @@ advance_windowaggregate_base(WindowAggState *winstate,
{
ExprState *argstate = (ExprState *) lfirst(arg);
- fcinfo->arg[i] = ExecEvalExpr(argstate, econtext,
- &fcinfo->argnull[i]);
+ fcinfo->args[i].datum = ExecEvalExpr(argstate, econtext,
+ &fcinfo->args[i].isnull);
i++;
}
@@ -464,7 +462,7 @@ advance_windowaggregate_base(WindowAggState *winstate,
*/
for (i = 1; i <= numArguments; i++)
{
- if (fcinfo->argnull[i])
+ if (fcinfo->args[i].isnull)
{
MemoryContextSwitchTo(oldContext);
return true;
@@ -510,8 +508,8 @@ advance_windowaggregate_base(WindowAggState *winstate,
numArguments + 1,
perfuncstate->winCollation,
(void *) winstate, NULL);
- fcinfo->arg[0] = peraggstate->transValue;
- fcinfo->argnull[0] = peraggstate->transValueIsNull;
+ fcinfo->args[0].datum = peraggstate->transValue;
+ fcinfo->args[0].isnull = peraggstate->transValueIsNull;
winstate->curaggcontext = peraggstate->aggcontext;
newVal = FunctionCallInvoke(fcinfo);
winstate->curaggcontext = NULL;
@@ -592,29 +590,29 @@ finalize_windowaggregate(WindowAggState *winstate,
if (OidIsValid(peraggstate->finalfn_oid))
{
int numFinalArgs = peraggstate->numFinalArgs;
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 3);
bool anynull;
int i;
- InitFunctionCallInfoData(fcinfo, &(peraggstate->finalfn),
+ InitFunctionCallInfoData(fcinfodata.fcinfo, &(peraggstate->finalfn),
numFinalArgs,
perfuncstate->winCollation,
(void *) winstate, NULL);
- fcinfo.arg[0] = MakeExpandedObjectReadOnly(peraggstate->transValue,
- peraggstate->transValueIsNull,
- peraggstate->transtypeLen);
- fcinfo.argnull[0] = peraggstate->transValueIsNull;
+ fcinfo->args[0].datum = MakeExpandedObjectReadOnly(peraggstate->transValue,
+ peraggstate->transValueIsNull,
+ peraggstate->transtypeLen);
+ fcinfo->args[0].isnull = peraggstate->transValueIsNull;
anynull = peraggstate->transValueIsNull;
/* Fill any remaining argument positions with nulls */
for (i = 1; i < numFinalArgs; i++)
{
- fcinfo.arg[i] = (Datum) 0;
- fcinfo.argnull[i] = true;
+ fcinfo->args[i].datum = (Datum) 0;
+ fcinfo->args[i].isnull = true;
anynull = true;
}
- if (fcinfo.flinfo->fn_strict && anynull)
+ if (fcinfo->flinfo->fn_strict && anynull)
{
/* don't call a strict function with NULL inputs */
*result = (Datum) 0;
@@ -623,9 +621,9 @@ finalize_windowaggregate(WindowAggState *winstate,
else
{
winstate->curaggcontext = peraggstate->aggcontext;
- *result = FunctionCallInvoke(&fcinfo);
+ *result = FunctionCallInvoke(fcinfo);
winstate->curaggcontext = NULL;
- *isnull = fcinfo.isnull;
+ *isnull = fcinfo->isnull;
}
}
else
@@ -1032,8 +1030,9 @@ static void
eval_windowfunction(WindowAggState *winstate, WindowStatePerFunc perfuncstate,
Datum *result, bool *isnull)
{
- FunctionCallInfoData fcinfo;
+ FunctionCallInfo fcinfo;
MemoryContext oldContext;
+ int argno;
oldContext = MemoryContextSwitchTo(winstate->ss.ps.ps_ExprContext->ecxt_per_tuple_memory);
@@ -1043,24 +1042,26 @@ eval_windowfunction(WindowAggState *winstate, WindowStatePerFunc perfuncstate,
* implementations to support varying numbers of arguments. The real info
* goes through the WindowObject, which is passed via fcinfo->context.
*/
- InitFunctionCallInfoData(fcinfo, &(perfuncstate->flinfo),
+ fcinfo = palloc(SizeForFunctionCallInfoData(perfuncstate->numArguments));
+ InitFunctionCallInfoData(*fcinfo, &(perfuncstate->flinfo),
perfuncstate->numArguments,
perfuncstate->winCollation,
(void *) perfuncstate->winobj, NULL);
/* Just in case, make all the regular argument slots be null */
- memset(fcinfo.argnull, true, perfuncstate->numArguments);
+ for (argno = 0; argno < perfuncstate->numArguments; argno++)
+ fcinfo->args[argno].isnull = true;
/* Window functions don't have a current aggregate context, either */
winstate->curaggcontext = NULL;
- *result = FunctionCallInvoke(&fcinfo);
- *isnull = fcinfo.isnull;
+ *result = FunctionCallInvoke(fcinfo);
+ *isnull = fcinfo->isnull;
/*
* Make sure pass-by-ref data is allocated in the appropriate context. (We
* need this in case the function returns a pointer into some short-lived
* tuple, as is entirely possible.)
*/
- if (!perfuncstate->resulttypeByVal && !fcinfo.isnull &&
+ if (!perfuncstate->resulttypeByVal && !fcinfo->isnull &&
!MemoryContextContains(CurrentMemoryContext,
DatumGetPointer(*result)))
*result = datumCopy(*result,
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index 99e0cee157f..13aedc93016 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -2117,15 +2117,16 @@ llvm_compile_expr(ExprState *state)
case EEOP_AGG_STRICT_INPUT_CHECK:
{
int nargs = op->d.agg_strict_input_check.nargs;
- bool *nulls = op->d.agg_strict_input_check.nulls;
+ NullableDatum *args = op->d.agg_strict_input_check.args;
int jumpnull;
int argno;
- LLVMValueRef v_nullp;
+ LLVMValueRef v_argp;
LLVMBasicBlockRef *b_checknulls;
jumpnull = op->d.agg_strict_input_check.jumpnull;
- v_nullp = l_ptr_const(nulls, l_ptr(TypeStorageBool));
+ /* FIXME: This isn't correct, it's a NullableDatum now */
+ v_argp = l_ptr_const(args, l_ptr(TypeStorageBool));
/* create blocks for checking args */
b_checknulls = palloc(sizeof(LLVMBasicBlockRef *) * nargs);
@@ -2153,7 +2154,8 @@ llvm_compile_expr(ExprState *state)
else
b_argnotnull = b_checknulls[argno + 1];
- v_argisnull = l_load_gep1(b, v_nullp, v_argno, "");
+ /* FIXME: This isn't correct */
+ v_argisnull = l_load_gep1(b, v_argp, v_argno, "");
LLVMBuildCondBr(b,
LLVMBuildICmp(b,
@@ -2352,7 +2354,7 @@ llvm_compile_expr(ExprState *state)
aggstate = op->d.agg_trans.aggstate;
pertrans = op->d.agg_trans.pertrans;
- fcinfo = &pertrans->transfn_fcinfo;
+ fcinfo = pertrans->transfn_fcinfo;
v_aggstatep = l_ptr_const(aggstate,
l_ptr(StructAggState));
@@ -2622,12 +2624,8 @@ BuildV1Call(LLVMJitContext *context, LLVMBuilderRef b,
LLVMValueRef v_lifetime = create_LifetimeEnd(mod);
LLVMValueRef params[2];
- params[0] = l_int64_const(sizeof(fcinfo->arg));
- params[1] = l_ptr_const(fcinfo->arg, l_ptr(LLVMInt8Type()));
- LLVMBuildCall(b, v_lifetime, params, lengthof(params), "");
-
- params[0] = l_int64_const(sizeof(fcinfo->argnull));
- params[1] = l_ptr_const(fcinfo->argnull, l_ptr(LLVMInt8Type()));
+ params[0] = l_int64_const(sizeof(NullableDatum) * fcinfo->nargs);
+ params[1] = l_ptr_const(fcinfo->args, l_ptr(LLVMInt8Type()));
LLVMBuildCall(b, v_lifetime, params, lengthof(params), "");
params[0] = l_int64_const(sizeof(fcinfo->isnull));
diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c
index d16ba5ec927..4d2b687fb1f 100644
--- a/src/backend/tcop/fastpath.c
+++ b/src/backend/tcop/fastpath.c
@@ -260,7 +260,7 @@ HandleFunctionRequest(StringInfo msgBuf)
{
Oid fid;
AclResult aclresult;
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 3);
int16 rformat;
Datum retval;
struct fp_info my_fp;
@@ -332,12 +332,12 @@ HandleFunctionRequest(StringInfo msgBuf)
* functions can't be called this way. Perhaps we should pass
* DEFAULT_COLLATION_OID, instead?
*/
- InitFunctionCallInfoData(fcinfo, &fip->flinfo, 0, InvalidOid, NULL, NULL);
+ InitFunctionCallInfoData(*fcinfo, &fip->flinfo, 0, InvalidOid, NULL, NULL);
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
- rformat = parse_fcall_arguments(msgBuf, fip, &fcinfo);
+ rformat = parse_fcall_arguments(msgBuf, fip, fcinfo);
else
- rformat = parse_fcall_arguments_20(msgBuf, fip, &fcinfo);
+ rformat = parse_fcall_arguments_20(msgBuf, fip, fcinfo);
/* Verify we reached the end of the message where expected. */
pq_getmsgend(msgBuf);
@@ -350,9 +350,9 @@ HandleFunctionRequest(StringInfo msgBuf)
{
int i;
- for (i = 0; i < fcinfo.nargs; i++)
+ for (i = 0; i < fcinfo->nargs; i++)
{
- if (fcinfo.argnull[i])
+ if (fcinfo->args[i].isnull)
{
callit = false;
break;
@@ -363,18 +363,18 @@ HandleFunctionRequest(StringInfo msgBuf)
if (callit)
{
/* Okay, do it ... */
- retval = FunctionCallInvoke(&fcinfo);
+ retval = FunctionCallInvoke(fcinfo);
}
else
{
- fcinfo.isnull = true;
+ fcinfo->isnull = true;
retval = (Datum) 0;
}
/* ensure we do at least one CHECK_FOR_INTERRUPTS per function call */
CHECK_FOR_INTERRUPTS();
- SendFunctionResult(retval, fcinfo.isnull, fip->rettype, rformat);
+ SendFunctionResult(retval, fcinfo->isnull, fip->rettype, rformat);
/* We no longer need the snapshot */
PopActiveSnapshot();
@@ -450,11 +450,11 @@ parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
argsize = pq_getmsgint(msgBuf, 4);
if (argsize == -1)
{
- fcinfo->argnull[i] = true;
+ fcinfo->args[i].isnull = true;
}
else
{
- fcinfo->argnull[i] = false;
+ fcinfo->args[i].isnull = false;
if (argsize < 0)
ereport(ERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
@@ -494,8 +494,8 @@ parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
else
pstring = pg_client_to_server(abuf.data, argsize);
- fcinfo->arg[i] = OidInputFunctionCall(typinput, pstring,
- typioparam, -1);
+ fcinfo->args[i].datum = OidInputFunctionCall(typinput, pstring,
+ typioparam, -1);
/* Free result of encoding conversion, if any */
if (pstring && pstring != abuf.data)
pfree(pstring);
@@ -514,8 +514,8 @@ parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
else
bufptr = &abuf;
- fcinfo->arg[i] = OidReceiveFunctionCall(typreceive, bufptr,
- typioparam, -1);
+ fcinfo->args[i].datum = OidReceiveFunctionCall(typreceive, bufptr,
+ typioparam, -1);
/* Trouble if it didn't eat the whole buffer */
if (argsize != -1 && abuf.cursor != abuf.len)
@@ -579,12 +579,13 @@ parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
argsize = pq_getmsgint(msgBuf, 4);
if (argsize == -1)
{
- fcinfo->argnull[i] = true;
- fcinfo->arg[i] = OidReceiveFunctionCall(typreceive, NULL,
- typioparam, -1);
+ /* FIXME: huh? */
+ fcinfo->args[i].isnull = true;
+ fcinfo->args[i].datum = OidReceiveFunctionCall(typreceive, NULL,
+ typioparam, -1);
continue;
}
- fcinfo->argnull[i] = false;
+ fcinfo->args[i].isnull = false;
if (argsize < 0)
ereport(ERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
@@ -597,8 +598,8 @@ parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
pq_getmsgbytes(msgBuf, argsize),
argsize);
- fcinfo->arg[i] = OidReceiveFunctionCall(typreceive, &abuf,
- typioparam, -1);
+ fcinfo->args[i].datum = OidReceiveFunctionCall(typreceive, &abuf,
+ typioparam, -1);
/* Trouble if it didn't eat the whole buffer */
if (abuf.cursor != abuf.len)
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index ce1ded888f1..577e8637768 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -3590,7 +3590,7 @@ array_eq(PG_FUNCTION_ARGS)
array_iter it1;
array_iter it2;
int i;
- FunctionCallInfoData locfcinfo;
+ STACK_FCINFO_FOR_ARGS(locfcinfo, 3);
if (element_type != AARR_ELEMTYPE(array2))
ereport(ERROR,
@@ -3630,7 +3630,7 @@ array_eq(PG_FUNCTION_ARGS)
/*
* apply the operator to each pair of array elements.
*/
- InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+ InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
collation, NULL, NULL);
/* Loop over source data */
@@ -3666,12 +3666,12 @@ array_eq(PG_FUNCTION_ARGS)
/*
* Apply the operator to the element pair
*/
- locfcinfo.arg[0] = elt1;
- locfcinfo.arg[1] = elt2;
- locfcinfo.argnull[0] = false;
- locfcinfo.argnull[1] = false;
- locfcinfo.isnull = false;
- oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+ locfcinfo->args[0].datum = elt1;
+ locfcinfo->args[0].isnull = false;
+ locfcinfo->args[1].datum = elt2;
+ locfcinfo->args[1].isnull = false;
+ locfcinfo->isnull = false;
+ oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
if (!oprresult)
{
result = false;
@@ -3761,7 +3761,7 @@ array_cmp(FunctionCallInfo fcinfo)
array_iter it1;
array_iter it2;
int i;
- FunctionCallInfoData locfcinfo;
+ STACK_FCINFO_FOR_ARGS(locfcinfo, 2);
if (element_type != AARR_ELEMTYPE(array2))
ereport(ERROR,
@@ -3794,7 +3794,7 @@ array_cmp(FunctionCallInfo fcinfo)
/*
* apply the operator to each pair of array elements.
*/
- InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
+ InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
collation, NULL, NULL);
/* Loop over source data */
@@ -3833,12 +3833,12 @@ array_cmp(FunctionCallInfo fcinfo)
}
/* Compare the pair of elements */
- locfcinfo.arg[0] = elt1;
- locfcinfo.arg[1] = elt2;
- locfcinfo.argnull[0] = false;
- locfcinfo.argnull[1] = false;
- locfcinfo.isnull = false;
- cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
+ locfcinfo->args[0].datum = elt1;
+ locfcinfo->args[0].isnull = false;
+ locfcinfo->args[1].datum = elt2;
+ locfcinfo->args[1].isnull = false;
+ locfcinfo->isnull = false;
+ cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
if (cmpresult == 0)
continue; /* equal */
@@ -3925,7 +3925,7 @@ hash_array(PG_FUNCTION_ARGS)
char typalign;
int i;
array_iter iter;
- FunctionCallInfoData locfcinfo;
+ STACK_FCINFO_FOR_ARGS(locfcinfo, 1);
/*
* We arrange to look up the hash function only once per series of calls,
@@ -3953,7 +3953,7 @@ hash_array(PG_FUNCTION_ARGS)
/*
* apply the hash function to each array element.
*/
- InitFunctionCallInfoData(locfcinfo, &typentry->hash_proc_finfo, 1,
+ InitFunctionCallInfoData(*locfcinfo, &typentry->hash_proc_finfo, 1,
InvalidOid, NULL, NULL);
/* Loop over source data */
@@ -3977,10 +3977,10 @@ hash_array(PG_FUNCTION_ARGS)
else
{
/* Apply the hash function */
- locfcinfo.arg[0] = elt;
- locfcinfo.argnull[0] = false;
- locfcinfo.isnull = false;
- elthash = DatumGetUInt32(FunctionCallInvoke(&locfcinfo));
+ locfcinfo->args[0].datum = elt;
+ locfcinfo->args[0].isnull = false;
+ locfcinfo->isnull = false;
+ elthash = DatumGetUInt32(FunctionCallInvoke(locfcinfo));
}
/*
@@ -4023,7 +4023,7 @@ hash_array_extended(PG_FUNCTION_ARGS)
char typalign;
int i;
array_iter iter;
- FunctionCallInfoData locfcinfo;
+ STACK_FCINFO_FOR_ARGS(locfcinfo, 2);
typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
if (typentry == NULL ||
@@ -4042,7 +4042,7 @@ hash_array_extended(PG_FUNCTION_ARGS)
typbyval = typentry->typbyval;
typalign = typentry->typalign;
- InitFunctionCallInfoData(locfcinfo, &typentry->hash_extended_proc_finfo, 2,
+ InitFunctionCallInfoData(*locfcinfo, &typentry->hash_extended_proc_finfo, 2,
InvalidOid, NULL, NULL);
/* Loop over source data */
@@ -4065,12 +4065,11 @@ hash_array_extended(PG_FUNCTION_ARGS)
else
{
/* Apply the hash function */
- locfcinfo.arg[0] = elt;
- locfcinfo.arg[1] = Int64GetDatum(seed);
- locfcinfo.argnull[0] = false;
- locfcinfo.argnull[1] = false;
- locfcinfo.isnull = false;
- elthash = DatumGetUInt64(FunctionCallInvoke(&locfcinfo));
+ locfcinfo->args[0].datum = elt;
+ locfcinfo->args[0].isnull = false;
+ locfcinfo->args[1].datum = Int64GetDatum(seed);
+ locfcinfo->args[1].isnull = false;
+ elthash = DatumGetUInt64(FunctionCallInvoke(locfcinfo));
}
result = (result << 5) - result + elthash;
@@ -4113,7 +4112,7 @@ array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation,
int i;
int j;
array_iter it1;
- FunctionCallInfoData locfcinfo;
+ STACK_FCINFO_FOR_ARGS(locfcinfo, 2);
if (element_type != AARR_ELEMTYPE(array2))
ereport(ERROR,
@@ -4164,7 +4163,7 @@ array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation,
/*
* Apply the comparison operator to each pair of array elements.
*/
- InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+ InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
collation, NULL, NULL);
/* Loop over source data */
@@ -4206,12 +4205,12 @@ array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation,
/*
* Apply the operator to the element pair
*/
- locfcinfo.arg[0] = elt1;
- locfcinfo.arg[1] = elt2;
- locfcinfo.argnull[0] = false;
- locfcinfo.argnull[1] = false;
- locfcinfo.isnull = false;
- oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+ locfcinfo->args[0].datum = elt1;
+ locfcinfo->args[0].isnull = false;
+ locfcinfo->args[1].datum = elt2;
+ locfcinfo->args[1].isnull = false;
+ locfcinfo->isnull = false;
+ oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
if (oprresult)
break;
}
@@ -6062,7 +6061,7 @@ array_replace_internal(ArrayType *array,
int bitmask;
bool changed = false;
TypeCacheEntry *typentry;
- FunctionCallInfoData locfcinfo;
+ STACK_FCINFO_FOR_ARGS(locfcinfo, 2);
element_type = ARR_ELEMTYPE(array);
ndim = ARR_NDIM(array);
@@ -6117,7 +6116,7 @@ array_replace_internal(ArrayType *array,
}
/* Prepare to apply the comparison operator */
- InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+ InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
collation, NULL, NULL);
/* Allocate temporary arrays for new values */
@@ -6175,12 +6174,12 @@ array_replace_internal(ArrayType *array,
/*
* Apply the operator to the element pair
*/
- locfcinfo.arg[0] = elt;
- locfcinfo.arg[1] = search;
- locfcinfo.argnull[0] = false;
- locfcinfo.argnull[1] = false;
- locfcinfo.isnull = false;
- oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+ locfcinfo->args[0].datum = elt;
+ locfcinfo->args[0].isnull = false;
+ locfcinfo->args[1].datum = search;
+ locfcinfo->args[1].isnull = false;
+ locfcinfo->isnull = false;
+ oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
if (!oprresult)
{
/* no match, keep element */
@@ -6460,7 +6459,8 @@ width_bucket_array_fixed(Datum operand,
char *thresholds_data;
int typlen = typentry->typlen;
bool typbyval = typentry->typbyval;
- FunctionCallInfoData locfcinfo;
+ STACK_FCINFO_FOR_ARGS(locfcinfo, 2);
+
int left;
int right;
@@ -6470,7 +6470,7 @@ width_bucket_array_fixed(Datum operand,
*/
thresholds_data = (char *) ARR_DATA_PTR(thresholds);
- InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
+ InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
collation, NULL, NULL);
/* Find the bucket */
@@ -6484,13 +6484,13 @@ width_bucket_array_fixed(Datum operand,
ptr = thresholds_data + mid * typlen;
- locfcinfo.arg[0] = operand;
- locfcinfo.arg[1] = fetch_att(ptr, typbyval, typlen);
- locfcinfo.argnull[0] = false;
- locfcinfo.argnull[1] = false;
- locfcinfo.isnull = false;
+ locfcinfo->args[0].datum = operand;
+ locfcinfo->args[0].isnull = false;
+ locfcinfo->args[1].datum = fetch_att(ptr, typbyval, typlen);
+ locfcinfo->args[1].isnull = false;
+ locfcinfo->isnull = false;
- cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
+ cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
if (cmpresult < 0)
right = mid;
@@ -6514,13 +6514,13 @@ width_bucket_array_variable(Datum operand,
int typlen = typentry->typlen;
bool typbyval = typentry->typbyval;
char typalign = typentry->typalign;
- FunctionCallInfoData locfcinfo;
+ STACK_FCINFO_FOR_ARGS(locfcinfo, 2);
int left;
int right;
thresholds_data = (char *) ARR_DATA_PTR(thresholds);
- InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
+ InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
collation, NULL, NULL);
/* Find the bucket */
@@ -6541,13 +6541,12 @@ width_bucket_array_variable(Datum operand,
ptr = (char *) att_align_nominal(ptr, typalign);
}
- locfcinfo.arg[0] = operand;
- locfcinfo.arg[1] = fetch_att(ptr, typbyval, typlen);
- locfcinfo.argnull[0] = false;
- locfcinfo.argnull[1] = false;
- locfcinfo.isnull = false;
+ locfcinfo->args[0].datum = operand;
+ locfcinfo->args[0].isnull = false;
+ locfcinfo->args[1].datum = fetch_att(ptr, typbyval, typlen);
+ locfcinfo->args[1].isnull = false;
- cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
+ cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
if (cmpresult < 0)
right = mid;
diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c
index 8149dc1369b..cb92b0f98bb 100644
--- a/src/backend/utils/adt/int.c
+++ b/src/backend/utils/adt/int.c
@@ -202,7 +202,7 @@ Datum
int2vectorrecv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
- FunctionCallInfoData locfcinfo;
+ STACK_FCINFO_FOR_ARGS(locfcinfo, 3);
int2vector *result;
/*
@@ -211,19 +211,19 @@ int2vectorrecv(PG_FUNCTION_ARGS)
* fcinfo->flinfo->fn_extra. So we need to pass it our own flinfo
* parameter.
*/
- InitFunctionCallInfoData(locfcinfo, fcinfo->flinfo, 3,
+ InitFunctionCallInfoData(*locfcinfo, fcinfo->flinfo, 3,
InvalidOid, NULL, NULL);
- locfcinfo.arg[0] = PointerGetDatum(buf);
- locfcinfo.arg[1] = ObjectIdGetDatum(INT2OID);
- locfcinfo.arg[2] = Int32GetDatum(-1);
- locfcinfo.argnull[0] = false;
- locfcinfo.argnull[1] = false;
- locfcinfo.argnull[2] = false;
+ locfcinfo->args[0].datum = PointerGetDatum(buf);
+ locfcinfo->args[0].isnull = false;
+ locfcinfo->args[1].datum = ObjectIdGetDatum(INT2OID);
+ locfcinfo->args[1].isnull = false;
+ locfcinfo->args[2].datum = Int32GetDatum(-1);
+ locfcinfo->args[2].isnull = false;
- result = (int2vector *) DatumGetPointer(array_recv(&locfcinfo));
+ result = (int2vector *) DatumGetPointer(array_recv(locfcinfo));
- Assert(!locfcinfo.isnull);
+ Assert(!locfcinfo->isnull);
/* sanity checks: int2vector must be 1-D, 0-based, no nulls */
if (ARR_NDIM(result) != 1 ||
diff --git a/src/backend/utils/adt/oid.c b/src/backend/utils/adt/oid.c
index b0670e0d9f7..cf50f843115 100644
--- a/src/backend/utils/adt/oid.c
+++ b/src/backend/utils/adt/oid.c
@@ -257,7 +257,7 @@ Datum
oidvectorrecv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
- FunctionCallInfoData locfcinfo;
+ STACK_FCINFO_FOR_ARGS(locfcinfo, 3);
oidvector *result;
/*
@@ -266,19 +266,19 @@ oidvectorrecv(PG_FUNCTION_ARGS)
* fcinfo->flinfo->fn_extra. So we need to pass it our own flinfo
* parameter.
*/
- InitFunctionCallInfoData(locfcinfo, fcinfo->flinfo, 3,
+ InitFunctionCallInfoData(*locfcinfo, fcinfo->flinfo, 3,
InvalidOid, NULL, NULL);
- locfcinfo.arg[0] = PointerGetDatum(buf);
- locfcinfo.arg[1] = ObjectIdGetDatum(OIDOID);
- locfcinfo.arg[2] = Int32GetDatum(-1);
- locfcinfo.argnull[0] = false;
- locfcinfo.argnull[1] = false;
- locfcinfo.argnull[2] = false;
+ locfcinfo->args[0].datum = PointerGetDatum(buf);
+ locfcinfo->args[0].isnull = false;
+ locfcinfo->args[1].datum = ObjectIdGetDatum(OIDOID);
+ locfcinfo->args[1].isnull = false;
+ locfcinfo->args[2].datum = Int32GetDatum(-1);
+ locfcinfo->args[2].isnull = false;
- result = (oidvector *) DatumGetPointer(array_recv(&locfcinfo));
+ result = (oidvector *) DatumGetPointer(array_recv(locfcinfo));
- Assert(!locfcinfo.isnull);
+ Assert(!locfcinfo->isnull);
/* sanity checks: oidvector must be 1-D, 0-based, no nulls */
if (ARR_NDIM(result) != 1 ||
diff --git a/src/backend/utils/adt/rowtypes.c b/src/backend/utils/adt/rowtypes.c
index 5f729342f8d..de2bd560eea 100644
--- a/src/backend/utils/adt/rowtypes.c
+++ b/src/backend/utils/adt/rowtypes.c
@@ -942,7 +942,7 @@ record_cmp(FunctionCallInfo fcinfo)
*/
if (!nulls1[i1] || !nulls2[i2])
{
- FunctionCallInfoData locfcinfo;
+ STACK_FCINFO_FOR_ARGS(locfcinfo, 3);
int32 cmpresult;
if (nulls1[i1])
@@ -959,14 +959,14 @@ record_cmp(FunctionCallInfo fcinfo)
}
/* Compare the pair of elements */
- InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
+ InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
collation, NULL, NULL);
- locfcinfo.arg[0] = values1[i1];
- locfcinfo.arg[1] = values2[i2];
- locfcinfo.argnull[0] = false;
- locfcinfo.argnull[1] = false;
- locfcinfo.isnull = false;
- cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
+ locfcinfo->args[0].datum = values1[i1];
+ locfcinfo->args[0].isnull = false;
+ locfcinfo->args[1].datum = values2[i2];
+ locfcinfo->args[1].isnull = false;
+ locfcinfo->isnull = false;
+ cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
if (cmpresult < 0)
{
@@ -1123,7 +1123,7 @@ record_eq(PG_FUNCTION_ARGS)
Form_pg_attribute att2;
TypeCacheEntry *typentry;
Oid collation;
- FunctionCallInfoData locfcinfo;
+ STACK_FCINFO_FOR_ARGS(locfcinfo, 3);
bool oprresult;
/*
@@ -1193,14 +1193,14 @@ record_eq(PG_FUNCTION_ARGS)
}
/* Compare the pair of elements */
- InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+ InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
collation, NULL, NULL);
- locfcinfo.arg[0] = values1[i1];
- locfcinfo.arg[1] = values2[i2];
- locfcinfo.argnull[0] = false;
- locfcinfo.argnull[1] = false;
- locfcinfo.isnull = false;
- oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+ locfcinfo->args[0].datum = values1[i1];
+ locfcinfo->args[0].isnull = false;
+ locfcinfo->args[1].datum = values2[i2];
+ locfcinfo->args[1].isnull = false;
+ locfcinfo->isnull = false;
+ oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
if (!oprresult)
{
result = false;
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 6cbbd5b78b0..86d5cef8108 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -782,7 +782,6 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
* Support routines for callers of fmgr-compatible functions
*-------------------------------------------------------------------------
*/
-
/*
* These are for invocation of a specifically named function with a
* directly-computed parameter list. Note that neither arguments nor result
@@ -792,18 +791,18 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
Datum
DirectFunctionCall1Coll(PGFunction func, Oid collation, Datum arg1)
{
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 1);
Datum result;
- InitFunctionCallInfoData(fcinfo, NULL, 1, collation, NULL, NULL);
+ InitFunctionCallInfoData(*fcinfo, NULL, 1, collation, NULL, NULL);
- fcinfo.arg[0] = arg1;
- fcinfo.argnull[0] = false;
+ fcinfo->args[0].datum = arg1;
+ fcinfo->args[0].isnull = false;
- result = (*func) (&fcinfo);
+ result = (*func) (fcinfo);
/* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
+ if (fcinfo->isnull)
elog(ERROR, "function %p returned NULL", (void *) func);
return result;
@@ -812,20 +811,20 @@ DirectFunctionCall1Coll(PGFunction func, Oid collation, Datum arg1)
Datum
DirectFunctionCall2Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2)
{
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 2);
Datum result;
- InitFunctionCallInfoData(fcinfo, NULL, 2, collation, NULL, NULL);
+ InitFunctionCallInfoData(*fcinfo, NULL, 2, collation, NULL, NULL);
- fcinfo.arg[0] = arg1;
- fcinfo.arg[1] = arg2;
- fcinfo.argnull[0] = false;
- fcinfo.argnull[1] = false;
+ fcinfo->args[0].datum = arg1;
+ fcinfo->args[0].isnull = false;
+ fcinfo->args[1].datum = arg2;
+ fcinfo->args[1].isnull = false;
- result = (*func) (&fcinfo);
+ result = (*func) (fcinfo);
/* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
+ if (fcinfo->isnull)
elog(ERROR, "function %p returned NULL", (void *) func);
return result;
@@ -835,22 +834,22 @@ Datum
DirectFunctionCall3Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
Datum arg3)
{
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 3);
Datum result;
- InitFunctionCallInfoData(fcinfo, NULL, 3, collation, NULL, NULL);
+ InitFunctionCallInfoData(*fcinfo, NULL, 3, collation, NULL, NULL);
- fcinfo.arg[0] = arg1;
- fcinfo.arg[1] = arg2;
- fcinfo.arg[2] = arg3;
- fcinfo.argnull[0] = false;
- fcinfo.argnull[1] = false;
- fcinfo.argnull[2] = false;
+ fcinfo->args[0].datum = arg1;
+ fcinfo->args[0].isnull = false;
+ fcinfo->args[1].datum = arg2;
+ fcinfo->args[1].isnull = false;
+ fcinfo->args[2].datum = arg3;
+ fcinfo->args[2].isnull = false;
- result = (*func) (&fcinfo);
+ result = (*func) (fcinfo);
/* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
+ if (fcinfo->isnull)
elog(ERROR, "function %p returned NULL", (void *) func);
return result;
@@ -860,24 +859,24 @@ Datum
DirectFunctionCall4Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4)
{
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 4);
Datum result;
- InitFunctionCallInfoData(fcinfo, NULL, 4, collation, NULL, NULL);
+ InitFunctionCallInfoData(*fcinfo, NULL, 4, collation, NULL, NULL);
- fcinfo.arg[0] = arg1;
- fcinfo.arg[1] = arg2;
- fcinfo.arg[2] = arg3;
- fcinfo.arg[3] = arg4;
- fcinfo.argnull[0] = false;
- fcinfo.argnull[1] = false;
- fcinfo.argnull[2] = false;
- fcinfo.argnull[3] = false;
+ fcinfo->args[0].datum = arg1;
+ fcinfo->args[0].isnull = false;
+ fcinfo->args[1].datum = arg2;
+ fcinfo->args[1].isnull = false;
+ fcinfo->args[2].datum = arg3;
+ fcinfo->args[2].isnull = false;
+ fcinfo->args[3].datum = arg4;
+ fcinfo->args[3].isnull = false;
- result = (*func) (&fcinfo);
+ result = (*func) (fcinfo);
/* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
+ if (fcinfo->isnull)
elog(ERROR, "function %p returned NULL", (void *) func);
return result;
@@ -887,26 +886,26 @@ Datum
DirectFunctionCall5Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4, Datum arg5)
{
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 5);
Datum result;
- InitFunctionCallInfoData(fcinfo, NULL, 5, collation, NULL, NULL);
+ InitFunctionCallInfoData(*fcinfo, NULL, 5, collation, NULL, NULL);
- fcinfo.arg[0] = arg1;
- fcinfo.arg[1] = arg2;
- fcinfo.arg[2] = arg3;
- fcinfo.arg[3] = arg4;
- fcinfo.arg[4] = arg5;
- fcinfo.argnull[0] = false;
- fcinfo.argnull[1] = false;
- fcinfo.argnull[2] = false;
- fcinfo.argnull[3] = false;
- fcinfo.argnull[4] = false;
+ fcinfo->args[0].datum = arg1;
+ fcinfo->args[0].isnull = false;
+ fcinfo->args[1].datum = arg2;
+ fcinfo->args[1].isnull = false;
+ fcinfo->args[2].datum = arg3;
+ fcinfo->args[2].isnull = false;
+ fcinfo->args[3].datum = arg4;
+ fcinfo->args[3].isnull = false;
+ fcinfo->args[4].datum = arg5;
+ fcinfo->args[4].isnull = false;
- result = (*func) (&fcinfo);
+ result = (*func) (fcinfo);
/* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
+ if (fcinfo->isnull)
elog(ERROR, "function %p returned NULL", (void *) func);
return result;
@@ -917,28 +916,28 @@ DirectFunctionCall6Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4, Datum arg5,
Datum arg6)
{
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 6);
Datum result;
- InitFunctionCallInfoData(fcinfo, NULL, 6, collation, NULL, NULL);
+ InitFunctionCallInfoData(*fcinfo, NULL, 6, collation, NULL, NULL);
- fcinfo.arg[0] = arg1;
- fcinfo.arg[1] = arg2;
- fcinfo.arg[2] = arg3;
- fcinfo.arg[3] = arg4;
- fcinfo.arg[4] = arg5;
- fcinfo.arg[5] = arg6;
- fcinfo.argnull[0] = false;
- fcinfo.argnull[1] = false;
- fcinfo.argnull[2] = false;
- fcinfo.argnull[3] = false;
- fcinfo.argnull[4] = false;
- fcinfo.argnull[5] = false;
+ fcinfo->args[0].datum = arg1;
+ fcinfo->args[0].isnull = false;
+ fcinfo->args[1].datum = arg2;
+ fcinfo->args[1].isnull = false;
+ fcinfo->args[2].datum = arg3;
+ fcinfo->args[2].isnull = false;
+ fcinfo->args[3].datum = arg4;
+ fcinfo->args[3].isnull = false;
+ fcinfo->args[4].datum = arg5;
+ fcinfo->args[4].isnull = false;
+ fcinfo->args[5].datum = arg6;
+ fcinfo->args[5].isnull = false;
- result = (*func) (&fcinfo);
+ result = (*func) (fcinfo);
/* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
+ if (fcinfo->isnull)
elog(ERROR, "function %p returned NULL", (void *) func);
return result;
@@ -949,30 +948,30 @@ DirectFunctionCall7Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4, Datum arg5,
Datum arg6, Datum arg7)
{
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 7);
Datum result;
- InitFunctionCallInfoData(fcinfo, NULL, 7, collation, NULL, NULL);
+ InitFunctionCallInfoData(*fcinfo, NULL, 7, collation, NULL, NULL);
- fcinfo.arg[0] = arg1;
- fcinfo.arg[1] = arg2;
- fcinfo.arg[2] = arg3;
- fcinfo.arg[3] = arg4;
- fcinfo.arg[4] = arg5;
- fcinfo.arg[5] = arg6;
- fcinfo.arg[6] = arg7;
- fcinfo.argnull[0] = false;
- fcinfo.argnull[1] = false;
- fcinfo.argnull[2] = false;
- fcinfo.argnull[3] = false;
- fcinfo.argnull[4] = false;
- fcinfo.argnull[5] = false;
- fcinfo.argnull[6] = false;
+ fcinfo->args[0].datum = arg1;
+ fcinfo->args[0].isnull = false;
+ fcinfo->args[1].datum = arg2;
+ fcinfo->args[1].isnull = false;
+ fcinfo->args[2].datum = arg3;
+ fcinfo->args[2].isnull = false;
+ fcinfo->args[3].datum = arg4;
+ fcinfo->args[3].isnull = false;
+ fcinfo->args[4].datum = arg5;
+ fcinfo->args[4].isnull = false;
+ fcinfo->args[5].datum = arg6;
+ fcinfo->args[5].isnull = false;
+ fcinfo->args[6].datum = arg7;
+ fcinfo->args[6].isnull = false;
- result = (*func) (&fcinfo);
+ result = (*func) (fcinfo);
/* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
+ if (fcinfo->isnull)
elog(ERROR, "function %p returned NULL", (void *) func);
return result;
@@ -983,32 +982,32 @@ DirectFunctionCall8Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4, Datum arg5,
Datum arg6, Datum arg7, Datum arg8)
{
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 8);
Datum result;
- InitFunctionCallInfoData(fcinfo, NULL, 8, collation, NULL, NULL);
+ InitFunctionCallInfoData(*fcinfo, NULL, 8, collation, NULL, NULL);
- fcinfo.arg[0] = arg1;
- fcinfo.arg[1] = arg2;
- fcinfo.arg[2] = arg3;
- fcinfo.arg[3] = arg4;
- fcinfo.arg[4] = arg5;
- fcinfo.arg[5] = arg6;
- fcinfo.arg[6] = arg7;
- fcinfo.arg[7] = arg8;
- fcinfo.argnull[0] = false;
- fcinfo.argnull[1] = false;
- fcinfo.argnull[2] = false;
- fcinfo.argnull[3] = false;
- fcinfo.argnull[4] = false;
- fcinfo.argnull[5] = false;
- fcinfo.argnull[6] = false;
- fcinfo.argnull[7] = false;
+ fcinfo->args[0].datum = arg1;
+ fcinfo->args[0].isnull = false;
+ fcinfo->args[1].datum = arg2;
+ fcinfo->args[1].isnull = false;
+ fcinfo->args[2].datum = arg3;
+ fcinfo->args[2].isnull = false;
+ fcinfo->args[3].datum = arg4;
+ fcinfo->args[3].isnull = false;
+ fcinfo->args[4].datum = arg5;
+ fcinfo->args[4].isnull = false;
+ fcinfo->args[5].datum = arg6;
+ fcinfo->args[5].isnull = false;
+ fcinfo->args[6].datum = arg7;
+ fcinfo->args[6].isnull = false;
+ fcinfo->args[7].datum = arg8;
+ fcinfo->args[7].isnull = false;
- result = (*func) (&fcinfo);
+ result = (*func) (fcinfo);
/* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
+ if (fcinfo->isnull)
elog(ERROR, "function %p returned NULL", (void *) func);
return result;
@@ -1020,34 +1019,34 @@ DirectFunctionCall9Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
Datum arg6, Datum arg7, Datum arg8,
Datum arg9)
{
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 9);
Datum result;
- InitFunctionCallInfoData(fcinfo, NULL, 9, collation, NULL, NULL);
+ InitFunctionCallInfoData(*fcinfo, NULL, 9, collation, NULL, NULL);
- fcinfo.arg[0] = arg1;
- fcinfo.arg[1] = arg2;
- fcinfo.arg[2] = arg3;
- fcinfo.arg[3] = arg4;
- fcinfo.arg[4] = arg5;
- fcinfo.arg[5] = arg6;
- fcinfo.arg[6] = arg7;
- fcinfo.arg[7] = arg8;
- fcinfo.arg[8] = arg9;
- fcinfo.argnull[0] = false;
- fcinfo.argnull[1] = false;
- fcinfo.argnull[2] = false;
- fcinfo.argnull[3] = false;
- fcinfo.argnull[4] = false;
- fcinfo.argnull[5] = false;
- fcinfo.argnull[6] = false;
- fcinfo.argnull[7] = false;
- fcinfo.argnull[8] = false;
+ fcinfo->args[0].datum = arg1;
+ fcinfo->args[0].isnull = false;
+ fcinfo->args[1].datum = arg2;
+ fcinfo->args[1].isnull = false;
+ fcinfo->args[2].datum = arg3;
+ fcinfo->args[2].isnull = false;
+ fcinfo->args[3].datum = arg4;
+ fcinfo->args[3].isnull = false;
+ fcinfo->args[4].datum = arg5;
+ fcinfo->args[4].isnull = false;
+ fcinfo->args[5].datum = arg6;
+ fcinfo->args[5].isnull = false;
+ fcinfo->args[6].datum = arg7;
+ fcinfo->args[6].isnull = false;
+ fcinfo->args[7].datum = arg8;
+ fcinfo->args[7].isnull = false;
+ fcinfo->args[8].datum = arg9;
+ fcinfo->args[8].isnull = false;
- result = (*func) (&fcinfo);
+ result = (*func) (fcinfo);
/* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
+ if (fcinfo->isnull)
elog(ERROR, "function %p returned NULL", (void *) func);
return result;
@@ -1065,18 +1064,18 @@ DirectFunctionCall9Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
Datum
CallerFInfoFunctionCall1(PGFunction func, FmgrInfo *flinfo, Oid collation, Datum arg1)
{
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 1);
Datum result;
- InitFunctionCallInfoData(fcinfo, flinfo, 1, collation, NULL, NULL);
+ InitFunctionCallInfoData(*fcinfo, flinfo, 1, collation, NULL, NULL);
- fcinfo.arg[0] = arg1;
- fcinfo.argnull[0] = false;
+ fcinfo->args[0].datum = arg1;
+ fcinfo->args[0].isnull = false;
- result = (*func) (&fcinfo);
+ result = (*func) (fcinfo);
/* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
+ if (fcinfo->isnull)
elog(ERROR, "function %p returned NULL", (void *) func);
return result;
@@ -1085,25 +1084,47 @@ CallerFInfoFunctionCall1(PGFunction func, FmgrInfo *flinfo, Oid collation, Datum
Datum
CallerFInfoFunctionCall2(PGFunction func, FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
{
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 2);
Datum result;
- InitFunctionCallInfoData(fcinfo, flinfo, 2, collation, NULL, NULL);
+ InitFunctionCallInfoData(*fcinfo, flinfo, 2, collation, NULL, NULL);
- fcinfo.arg[0] = arg1;
- fcinfo.arg[1] = arg2;
- fcinfo.argnull[0] = false;
- fcinfo.argnull[1] = false;
+ fcinfo->args[0].datum = arg1;
+ fcinfo->args[0].isnull = false;
+ fcinfo->args[1].datum = arg2;
+ fcinfo->args[1].isnull = false;
- result = (*func) (&fcinfo);
+ result = (*func) (fcinfo);
/* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
+ if (fcinfo->isnull)
elog(ERROR, "function %p returned NULL", (void *) func);
return result;
}
+/*
+ * These are for invocation of a previously-looked-up function with a
+ * directly-computed parameter list. Note that neither arguments nor result
+ * are allowed to be NULL.
+ */
+Datum
+FunctionCall0Coll(FmgrInfo *flinfo, Oid collation)
+{
+ STACK_FCINFO_FOR_ARGS(fcinfo, 0);
+ Datum result;
+
+ InitFunctionCallInfoData(*fcinfo, flinfo, 0, collation, NULL, NULL);
+
+ result = FunctionCallInvoke(fcinfo);
+
+ /* Check for null result, since caller is clearly not expecting one */
+ if (fcinfo->isnull)
+ elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
+
+ return result;
+}
+
/*
* These are for invocation of a previously-looked-up function with a
* directly-computed parameter list. Note that neither arguments nor result
@@ -1112,19 +1133,19 @@ CallerFInfoFunctionCall2(PGFunction func, FmgrInfo *flinfo, Oid collation, Datum
Datum
FunctionCall1Coll(FmgrInfo *flinfo, Oid collation, Datum arg1)
{
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 1);
Datum result;
- InitFunctionCallInfoData(fcinfo, flinfo, 1, collation, NULL, NULL);
+ InitFunctionCallInfoData(*fcinfo, flinfo, 1, collation, NULL, NULL);
- fcinfo.arg[0] = arg1;
- fcinfo.argnull[0] = false;
+ fcinfo->args[0].datum = arg1;
+ fcinfo->args[0].isnull = false;
- result = FunctionCallInvoke(&fcinfo);
+ result = FunctionCallInvoke(fcinfo);
/* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
- elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+ if (fcinfo->isnull)
+ elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
return result;
}
@@ -1132,21 +1153,21 @@ FunctionCall1Coll(FmgrInfo *flinfo, Oid collation, Datum arg1)
Datum
FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
{
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 2);
Datum result;
- InitFunctionCallInfoData(fcinfo, flinfo, 2, collation, NULL, NULL);
+ InitFunctionCallInfoData(*fcinfo, flinfo, 2, collation, NULL, NULL);
- fcinfo.arg[0] = arg1;
- fcinfo.arg[1] = arg2;
- fcinfo.argnull[0] = false;
- fcinfo.argnull[1] = false;
+ fcinfo->args[0].datum = arg1;
+ fcinfo->args[0].isnull = false;
+ fcinfo->args[1].datum = arg2;
+ fcinfo->args[1].isnull = false;
- result = FunctionCallInvoke(&fcinfo);
+ result = FunctionCallInvoke(fcinfo);
/* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
- elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+ if (fcinfo->isnull)
+ elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
return result;
}
@@ -1155,23 +1176,23 @@ Datum
FunctionCall3Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
Datum arg3)
{
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 3);
Datum result;
- InitFunctionCallInfoData(fcinfo, flinfo, 3, collation, NULL, NULL);
+ InitFunctionCallInfoData(*fcinfo, flinfo, 3, collation, NULL, NULL);
- fcinfo.arg[0] = arg1;
- fcinfo.arg[1] = arg2;
- fcinfo.arg[2] = arg3;
- fcinfo.argnull[0] = false;
- fcinfo.argnull[1] = false;
- fcinfo.argnull[2] = false;
+ fcinfo->args[0].datum = arg1;
+ fcinfo->args[0].isnull = false;
+ fcinfo->args[1].datum = arg2;
+ fcinfo->args[1].isnull = false;
+ fcinfo->args[2].datum = arg3;
+ fcinfo->args[2].isnull = false;
- result = FunctionCallInvoke(&fcinfo);
+ result = FunctionCallInvoke(fcinfo);
/* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
- elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+ if (fcinfo->isnull)
+ elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
return result;
}
@@ -1180,25 +1201,25 @@ Datum
FunctionCall4Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4)
{
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 4);
Datum result;
- InitFunctionCallInfoData(fcinfo, flinfo, 4, collation, NULL, NULL);
+ InitFunctionCallInfoData(*fcinfo, flinfo, 4, collation, NULL, NULL);
- fcinfo.arg[0] = arg1;
- fcinfo.arg[1] = arg2;
- fcinfo.arg[2] = arg3;
- fcinfo.arg[3] = arg4;
- fcinfo.argnull[0] = false;
- fcinfo.argnull[1] = false;
- fcinfo.argnull[2] = false;
- fcinfo.argnull[3] = false;
+ fcinfo->args[0].datum = arg1;
+ fcinfo->args[0].isnull = false;
+ fcinfo->args[1].datum = arg2;
+ fcinfo->args[1].isnull = false;
+ fcinfo->args[2].datum = arg3;
+ fcinfo->args[2].isnull = false;
+ fcinfo->args[3].datum = arg4;
+ fcinfo->args[3].isnull = false;
- result = FunctionCallInvoke(&fcinfo);
+ result = FunctionCallInvoke(fcinfo);
/* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
- elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+ if (fcinfo->isnull)
+ elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
return result;
}
@@ -1207,27 +1228,27 @@ Datum
FunctionCall5Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4, Datum arg5)
{
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 5);
Datum result;
- InitFunctionCallInfoData(fcinfo, flinfo, 5, collation, NULL, NULL);
+ InitFunctionCallInfoData(*fcinfo, flinfo, 5, collation, NULL, NULL);
- fcinfo.arg[0] = arg1;
- fcinfo.arg[1] = arg2;
- fcinfo.arg[2] = arg3;
- fcinfo.arg[3] = arg4;
- fcinfo.arg[4] = arg5;
- fcinfo.argnull[0] = false;
- fcinfo.argnull[1] = false;
- fcinfo.argnull[2] = false;
- fcinfo.argnull[3] = false;
- fcinfo.argnull[4] = false;
+ fcinfo->args[0].datum = arg1;
+ fcinfo->args[0].isnull = false;
+ fcinfo->args[1].datum = arg2;
+ fcinfo->args[1].isnull = false;
+ fcinfo->args[2].datum = arg3;
+ fcinfo->args[2].isnull = false;
+ fcinfo->args[3].datum = arg4;
+ fcinfo->args[3].isnull = false;
+ fcinfo->args[4].datum = arg5;
+ fcinfo->args[4].isnull = false;
- result = FunctionCallInvoke(&fcinfo);
+ result = FunctionCallInvoke(fcinfo);
/* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
- elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+ if (fcinfo->isnull)
+ elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
return result;
}
@@ -1237,29 +1258,29 @@ FunctionCall6Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4, Datum arg5,
Datum arg6)
{
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 6);
Datum result;
- InitFunctionCallInfoData(fcinfo, flinfo, 6, collation, NULL, NULL);
+ InitFunctionCallInfoData(*fcinfo, flinfo, 6, collation, NULL, NULL);
- fcinfo.arg[0] = arg1;
- fcinfo.arg[1] = arg2;
- fcinfo.arg[2] = arg3;
- fcinfo.arg[3] = arg4;
- fcinfo.arg[4] = arg5;
- fcinfo.arg[5] = arg6;
- fcinfo.argnull[0] = false;
- fcinfo.argnull[1] = false;
- fcinfo.argnull[2] = false;
- fcinfo.argnull[3] = false;
- fcinfo.argnull[4] = false;
- fcinfo.argnull[5] = false;
+ fcinfo->args[0].datum = arg1;
+ fcinfo->args[0].isnull = false;
+ fcinfo->args[1].datum = arg2;
+ fcinfo->args[1].isnull = false;
+ fcinfo->args[2].datum = arg3;
+ fcinfo->args[2].isnull = false;
+ fcinfo->args[3].datum = arg4;
+ fcinfo->args[3].isnull = false;
+ fcinfo->args[4].datum = arg5;
+ fcinfo->args[4].isnull = false;
+ fcinfo->args[5].datum = arg6;
+ fcinfo->args[5].isnull = false;
- result = FunctionCallInvoke(&fcinfo);
+ result = FunctionCallInvoke(fcinfo);
/* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
- elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+ if (fcinfo->isnull)
+ elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
return result;
}
@@ -1269,31 +1290,31 @@ FunctionCall7Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4, Datum arg5,
Datum arg6, Datum arg7)
{
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 7);
Datum result;
- InitFunctionCallInfoData(fcinfo, flinfo, 7, collation, NULL, NULL);
+ InitFunctionCallInfoData(*fcinfo, flinfo, 7, collation, NULL, NULL);
- fcinfo.arg[0] = arg1;
- fcinfo.arg[1] = arg2;
- fcinfo.arg[2] = arg3;
- fcinfo.arg[3] = arg4;
- fcinfo.arg[4] = arg5;
- fcinfo.arg[5] = arg6;
- fcinfo.arg[6] = arg7;
- fcinfo.argnull[0] = false;
- fcinfo.argnull[1] = false;
- fcinfo.argnull[2] = false;
- fcinfo.argnull[3] = false;
- fcinfo.argnull[4] = false;
- fcinfo.argnull[5] = false;
- fcinfo.argnull[6] = false;
+ fcinfo->args[0].datum = arg1;
+ fcinfo->args[0].isnull = false;
+ fcinfo->args[1].datum = arg2;
+ fcinfo->args[1].isnull = false;
+ fcinfo->args[2].datum = arg3;
+ fcinfo->args[2].isnull = false;
+ fcinfo->args[3].datum = arg4;
+ fcinfo->args[3].isnull = false;
+ fcinfo->args[4].datum = arg5;
+ fcinfo->args[4].isnull = false;
+ fcinfo->args[5].datum = arg6;
+ fcinfo->args[5].isnull = false;
+ fcinfo->args[6].datum = arg7;
+ fcinfo->args[6].isnull = false;
- result = FunctionCallInvoke(&fcinfo);
+ result = FunctionCallInvoke(fcinfo);
/* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
- elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+ if (fcinfo->isnull)
+ elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
return result;
}
@@ -1303,33 +1324,33 @@ FunctionCall8Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4, Datum arg5,
Datum arg6, Datum arg7, Datum arg8)
{
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 8);
Datum result;
- InitFunctionCallInfoData(fcinfo, flinfo, 8, collation, NULL, NULL);
+ InitFunctionCallInfoData(*fcinfo, flinfo, 8, collation, NULL, NULL);
- fcinfo.arg[0] = arg1;
- fcinfo.arg[1] = arg2;
- fcinfo.arg[2] = arg3;
- fcinfo.arg[3] = arg4;
- fcinfo.arg[4] = arg5;
- fcinfo.arg[5] = arg6;
- fcinfo.arg[6] = arg7;
- fcinfo.arg[7] = arg8;
- fcinfo.argnull[0] = false;
- fcinfo.argnull[1] = false;
- fcinfo.argnull[2] = false;
- fcinfo.argnull[3] = false;
- fcinfo.argnull[4] = false;
- fcinfo.argnull[5] = false;
- fcinfo.argnull[6] = false;
- fcinfo.argnull[7] = false;
+ fcinfo->args[0].datum = arg1;
+ fcinfo->args[0].isnull = false;
+ fcinfo->args[1].datum = arg2;
+ fcinfo->args[1].isnull = false;
+ fcinfo->args[2].datum = arg3;
+ fcinfo->args[2].isnull = false;
+ fcinfo->args[3].datum = arg4;
+ fcinfo->args[3].isnull = false;
+ fcinfo->args[4].datum = arg5;
+ fcinfo->args[4].isnull = false;
+ fcinfo->args[5].datum = arg6;
+ fcinfo->args[5].isnull = false;
+ fcinfo->args[6].datum = arg7;
+ fcinfo->args[6].isnull = false;
+ fcinfo->args[7].datum = arg8;
+ fcinfo->args[7].isnull = false;
- result = FunctionCallInvoke(&fcinfo);
+ result = FunctionCallInvoke(fcinfo);
/* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
- elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+ if (fcinfo->isnull)
+ elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
return result;
}
@@ -1340,35 +1361,35 @@ FunctionCall9Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
Datum arg6, Datum arg7, Datum arg8,
Datum arg9)
{
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 9);
Datum result;
- InitFunctionCallInfoData(fcinfo, flinfo, 9, collation, NULL, NULL);
+ InitFunctionCallInfoData(*fcinfo, flinfo, 9, collation, NULL, NULL);
- fcinfo.arg[0] = arg1;
- fcinfo.arg[1] = arg2;
- fcinfo.arg[2] = arg3;
- fcinfo.arg[3] = arg4;
- fcinfo.arg[4] = arg5;
- fcinfo.arg[5] = arg6;
- fcinfo.arg[6] = arg7;
- fcinfo.arg[7] = arg8;
- fcinfo.arg[8] = arg9;
- fcinfo.argnull[0] = false;
- fcinfo.argnull[1] = false;
- fcinfo.argnull[2] = false;
- fcinfo.argnull[3] = false;
- fcinfo.argnull[4] = false;
- fcinfo.argnull[5] = false;
- fcinfo.argnull[6] = false;
- fcinfo.argnull[7] = false;
- fcinfo.argnull[8] = false;
+ fcinfo->args[0].datum = arg1;
+ fcinfo->args[0].isnull = false;
+ fcinfo->args[1].datum = arg2;
+ fcinfo->args[1].isnull = false;
+ fcinfo->args[2].datum = arg3;
+ fcinfo->args[2].isnull = false;
+ fcinfo->args[3].datum = arg4;
+ fcinfo->args[3].isnull = false;
+ fcinfo->args[4].datum = arg5;
+ fcinfo->args[4].isnull = false;
+ fcinfo->args[5].datum = arg6;
+ fcinfo->args[5].isnull = false;
+ fcinfo->args[6].datum = arg7;
+ fcinfo->args[6].isnull = false;
+ fcinfo->args[7].datum = arg8;
+ fcinfo->args[7].isnull = false;
+ fcinfo->args[8].datum = arg9;
+ fcinfo->args[8].isnull = false;
- result = FunctionCallInvoke(&fcinfo);
+ result = FunctionCallInvoke(fcinfo);
/* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
- elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+ if (fcinfo->isnull)
+ elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
return result;
}
@@ -1385,68 +1406,30 @@ Datum
OidFunctionCall0Coll(Oid functionId, Oid collation)
{
FmgrInfo flinfo;
- FunctionCallInfoData fcinfo;
- Datum result;
fmgr_info(functionId, &flinfo);
- InitFunctionCallInfoData(fcinfo, &flinfo, 0, collation, NULL, NULL);
-
- result = FunctionCallInvoke(&fcinfo);
-
- /* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
- elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
- return result;
+ return FunctionCall0Coll(&flinfo, collation);
}
Datum
OidFunctionCall1Coll(Oid functionId, Oid collation, Datum arg1)
{
FmgrInfo flinfo;
- FunctionCallInfoData fcinfo;
- Datum result;
fmgr_info(functionId, &flinfo);
- InitFunctionCallInfoData(fcinfo, &flinfo, 1, collation, NULL, NULL);
-
- fcinfo.arg[0] = arg1;
- fcinfo.argnull[0] = false;
-
- result = FunctionCallInvoke(&fcinfo);
-
- /* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
- elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
- return result;
+ return FunctionCall1Coll(&flinfo, collation, arg1);
}
Datum
OidFunctionCall2Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2)
{
FmgrInfo flinfo;
- FunctionCallInfoData fcinfo;
- Datum result;
fmgr_info(functionId, &flinfo);
- InitFunctionCallInfoData(fcinfo, &flinfo, 2, collation, NULL, NULL);
-
- fcinfo.arg[0] = arg1;
- fcinfo.arg[1] = arg2;
- fcinfo.argnull[0] = false;
- fcinfo.argnull[1] = false;
-
- result = FunctionCallInvoke(&fcinfo);
-
- /* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
- elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
- return result;
+ return FunctionCall2Coll(&flinfo, collation, arg1, arg2);
}
Datum
@@ -1454,27 +1437,10 @@ OidFunctionCall3Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
Datum arg3)
{
FmgrInfo flinfo;
- FunctionCallInfoData fcinfo;
- Datum result;
fmgr_info(functionId, &flinfo);
- InitFunctionCallInfoData(fcinfo, &flinfo, 3, collation, NULL, NULL);
-
- fcinfo.arg[0] = arg1;
- fcinfo.arg[1] = arg2;
- fcinfo.arg[2] = arg3;
- fcinfo.argnull[0] = false;
- fcinfo.argnull[1] = false;
- fcinfo.argnull[2] = false;
-
- result = FunctionCallInvoke(&fcinfo);
-
- /* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
- elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
- return result;
+ return FunctionCall3Coll(&flinfo, collation, arg1, arg2, arg3);
}
Datum
@@ -1482,29 +1448,10 @@ OidFunctionCall4Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4)
{
FmgrInfo flinfo;
- FunctionCallInfoData fcinfo;
- Datum result;
fmgr_info(functionId, &flinfo);
- InitFunctionCallInfoData(fcinfo, &flinfo, 4, collation, NULL, NULL);
-
- fcinfo.arg[0] = arg1;
- fcinfo.arg[1] = arg2;
- fcinfo.arg[2] = arg3;
- fcinfo.arg[3] = arg4;
- fcinfo.argnull[0] = false;
- fcinfo.argnull[1] = false;
- fcinfo.argnull[2] = false;
- fcinfo.argnull[3] = false;
-
- result = FunctionCallInvoke(&fcinfo);
-
- /* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
- elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
- return result;
+ return FunctionCall4Coll(&flinfo, collation, arg1, arg2, arg3, arg4);
}
Datum
@@ -1512,31 +1459,10 @@ OidFunctionCall5Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4, Datum arg5)
{
FmgrInfo flinfo;
- FunctionCallInfoData fcinfo;
- Datum result;
fmgr_info(functionId, &flinfo);
- InitFunctionCallInfoData(fcinfo, &flinfo, 5, collation, NULL, NULL);
-
- fcinfo.arg[0] = arg1;
- fcinfo.arg[1] = arg2;
- fcinfo.arg[2] = arg3;
- fcinfo.arg[3] = arg4;
- fcinfo.arg[4] = arg5;
- fcinfo.argnull[0] = false;
- fcinfo.argnull[1] = false;
- fcinfo.argnull[2] = false;
- fcinfo.argnull[3] = false;
- fcinfo.argnull[4] = false;
-
- result = FunctionCallInvoke(&fcinfo);
-
- /* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
- elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
- return result;
+ return FunctionCall5Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5);
}
Datum
@@ -1545,33 +1471,11 @@ OidFunctionCall6Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
Datum arg6)
{
FmgrInfo flinfo;
- FunctionCallInfoData fcinfo;
- Datum result;
fmgr_info(functionId, &flinfo);
- InitFunctionCallInfoData(fcinfo, &flinfo, 6, collation, NULL, NULL);
-
- fcinfo.arg[0] = arg1;
- fcinfo.arg[1] = arg2;
- fcinfo.arg[2] = arg3;
- fcinfo.arg[3] = arg4;
- fcinfo.arg[4] = arg5;
- fcinfo.arg[5] = arg6;
- fcinfo.argnull[0] = false;
- fcinfo.argnull[1] = false;
- fcinfo.argnull[2] = false;
- fcinfo.argnull[3] = false;
- fcinfo.argnull[4] = false;
- fcinfo.argnull[5] = false;
-
- result = FunctionCallInvoke(&fcinfo);
-
- /* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
- elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
- return result;
+ return FunctionCall6Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5,
+ arg6);
}
Datum
@@ -1580,35 +1484,11 @@ OidFunctionCall7Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
Datum arg6, Datum arg7)
{
FmgrInfo flinfo;
- FunctionCallInfoData fcinfo;
- Datum result;
fmgr_info(functionId, &flinfo);
- InitFunctionCallInfoData(fcinfo, &flinfo, 7, collation, NULL, NULL);
-
- fcinfo.arg[0] = arg1;
- fcinfo.arg[1] = arg2;
- fcinfo.arg[2] = arg3;
- fcinfo.arg[3] = arg4;
- fcinfo.arg[4] = arg5;
- fcinfo.arg[5] = arg6;
- fcinfo.arg[6] = arg7;
- fcinfo.argnull[0] = false;
- fcinfo.argnull[1] = false;
- fcinfo.argnull[2] = false;
- fcinfo.argnull[3] = false;
- fcinfo.argnull[4] = false;
- fcinfo.argnull[5] = false;
- fcinfo.argnull[6] = false;
-
- result = FunctionCallInvoke(&fcinfo);
-
- /* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
- elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
- return result;
+ return FunctionCall7Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7);
}
Datum
@@ -1617,37 +1497,11 @@ OidFunctionCall8Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
Datum arg6, Datum arg7, Datum arg8)
{
FmgrInfo flinfo;
- FunctionCallInfoData fcinfo;
- Datum result;
fmgr_info(functionId, &flinfo);
- InitFunctionCallInfoData(fcinfo, &flinfo, 8, collation, NULL, NULL);
-
- fcinfo.arg[0] = arg1;
- fcinfo.arg[1] = arg2;
- fcinfo.arg[2] = arg3;
- fcinfo.arg[3] = arg4;
- fcinfo.arg[4] = arg5;
- fcinfo.arg[5] = arg6;
- fcinfo.arg[6] = arg7;
- fcinfo.arg[7] = arg8;
- fcinfo.argnull[0] = false;
- fcinfo.argnull[1] = false;
- fcinfo.argnull[2] = false;
- fcinfo.argnull[3] = false;
- fcinfo.argnull[4] = false;
- fcinfo.argnull[5] = false;
- fcinfo.argnull[6] = false;
- fcinfo.argnull[7] = false;
-
- result = FunctionCallInvoke(&fcinfo);
-
- /* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
- elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
- return result;
+ return FunctionCall8Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7, arg8);
}
Datum
@@ -1657,39 +1511,11 @@ OidFunctionCall9Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
Datum arg9)
{
FmgrInfo flinfo;
- FunctionCallInfoData fcinfo;
- Datum result;
fmgr_info(functionId, &flinfo);
- InitFunctionCallInfoData(fcinfo, &flinfo, 9, collation, NULL, NULL);
-
- fcinfo.arg[0] = arg1;
- fcinfo.arg[1] = arg2;
- fcinfo.arg[2] = arg3;
- fcinfo.arg[3] = arg4;
- fcinfo.arg[4] = arg5;
- fcinfo.arg[5] = arg6;
- fcinfo.arg[6] = arg7;
- fcinfo.arg[7] = arg8;
- fcinfo.arg[8] = arg9;
- fcinfo.argnull[0] = false;
- fcinfo.argnull[1] = false;
- fcinfo.argnull[2] = false;
- fcinfo.argnull[3] = false;
- fcinfo.argnull[4] = false;
- fcinfo.argnull[5] = false;
- fcinfo.argnull[6] = false;
- fcinfo.argnull[7] = false;
- fcinfo.argnull[8] = false;
-
- result = FunctionCallInvoke(&fcinfo);
-
- /* Check for null result, since caller is clearly not expecting one */
- if (fcinfo.isnull)
- elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
-
- return result;
+ return FunctionCall9Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7, arg8, arg9);
}
@@ -1708,35 +1534,35 @@ OidFunctionCall9Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
Datum
InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
{
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 3);
Datum result;
if (str == NULL && flinfo->fn_strict)
return (Datum) 0; /* just return null result */
- InitFunctionCallInfoData(fcinfo, flinfo, 3, InvalidOid, NULL, NULL);
+ InitFunctionCallInfoData(*fcinfo, flinfo, 3, InvalidOid, NULL, NULL);
- fcinfo.arg[0] = CStringGetDatum(str);
- fcinfo.arg[1] = ObjectIdGetDatum(typioparam);
- fcinfo.arg[2] = Int32GetDatum(typmod);
- fcinfo.argnull[0] = (str == NULL);
- fcinfo.argnull[1] = false;
- fcinfo.argnull[2] = false;
+ fcinfo->args[0].datum = CStringGetDatum(str);
+ fcinfo->args[0].isnull = false;
+ fcinfo->args[1].datum = ObjectIdGetDatum(typioparam);
+ fcinfo->args[1].isnull = false;
+ fcinfo->args[2].datum = Int32GetDatum(typmod);
+ fcinfo->args[2].isnull = false;
- result = FunctionCallInvoke(&fcinfo);
+ result = FunctionCallInvoke(fcinfo);
/* Should get null result if and only if str is NULL */
if (str == NULL)
{
- if (!fcinfo.isnull)
+ if (!fcinfo->isnull)
elog(ERROR, "input function %u returned non-NULL",
- fcinfo.flinfo->fn_oid);
+ flinfo->fn_oid);
}
else
{
- if (fcinfo.isnull)
+ if (fcinfo->isnull)
elog(ERROR, "input function %u returned NULL",
- fcinfo.flinfo->fn_oid);
+ flinfo->fn_oid);
}
return result;
@@ -1767,35 +1593,35 @@ Datum
ReceiveFunctionCall(FmgrInfo *flinfo, StringInfo buf,
Oid typioparam, int32 typmod)
{
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 3);
Datum result;
if (buf == NULL && flinfo->fn_strict)
return (Datum) 0; /* just return null result */
- InitFunctionCallInfoData(fcinfo, flinfo, 3, InvalidOid, NULL, NULL);
+ InitFunctionCallInfoData(*fcinfo, flinfo, 3, InvalidOid, NULL, NULL);
- fcinfo.arg[0] = PointerGetDatum(buf);
- fcinfo.arg[1] = ObjectIdGetDatum(typioparam);
- fcinfo.arg[2] = Int32GetDatum(typmod);
- fcinfo.argnull[0] = (buf == NULL);
- fcinfo.argnull[1] = false;
- fcinfo.argnull[2] = false;
+ fcinfo->args[0].datum = PointerGetDatum(buf);
+ fcinfo->args[0].isnull = false;
+ fcinfo->args[1].datum = ObjectIdGetDatum(typioparam);
+ fcinfo->args[1].isnull = false;
+ fcinfo->args[2].datum = Int32GetDatum(typmod);
+ fcinfo->args[2].isnull = false;
- result = FunctionCallInvoke(&fcinfo);
+ result = FunctionCallInvoke(fcinfo);
/* Should get null result if and only if buf is NULL */
if (buf == NULL)
{
- if (!fcinfo.isnull)
+ if (!fcinfo->isnull)
elog(ERROR, "receive function %u returned non-NULL",
- fcinfo.flinfo->fn_oid);
+ flinfo->fn_oid);
}
else
{
- if (fcinfo.isnull)
+ if (fcinfo->isnull)
elog(ERROR, "receive function %u returned NULL",
- fcinfo.flinfo->fn_oid);
+ flinfo->fn_oid);
}
return result;
diff --git a/src/backend/utils/sort/sortsupport.c b/src/backend/utils/sort/sortsupport.c
index 8c09fe4a430..31aa4100d67 100644
--- a/src/backend/utils/sort/sortsupport.c
+++ b/src/backend/utils/sort/sortsupport.c
@@ -26,10 +26,11 @@
/* Info needed to use an old-style comparison function as a sort comparator */
typedef struct
{
- FunctionCallInfoData fcinfo; /* reusable callinfo structure */
FmgrInfo flinfo; /* lookup data for comparison function */
+ FunctionCallInfoData fcinfo; /* reusable callinfo structure */
} SortShimExtra;
+#define SizeForSortShimExtra(nargs) (offsetof(SortShimExtra, fcinfo) + SizeForFunctionCallInfoData(nargs))
/*
* Shim function for calling an old-style comparator
@@ -44,8 +45,8 @@ comparison_shim(Datum x, Datum y, SortSupport ssup)
SortShimExtra *extra = (SortShimExtra *) ssup->ssup_extra;
Datum result;
- extra->fcinfo.arg[0] = x;
- extra->fcinfo.arg[1] = y;
+ extra->fcinfo.args[0].datum = x;
+ extra->fcinfo.args[1].datum = y;
/* just for paranoia's sake, we reset isnull each time */
extra->fcinfo.isnull = false;
@@ -69,7 +70,7 @@ PrepareSortSupportComparisonShim(Oid cmpFunc, SortSupport ssup)
SortShimExtra *extra;
extra = (SortShimExtra *) MemoryContextAlloc(ssup->ssup_cxt,
- sizeof(SortShimExtra));
+ SizeForSortShimExtra(2));
/* Lookup the comparison function */
fmgr_info_cxt(cmpFunc, &extra->flinfo, ssup->ssup_cxt);
@@ -77,8 +78,8 @@ PrepareSortSupportComparisonShim(Oid cmpFunc, SortSupport ssup)
/* We can initialize the callinfo just once and re-use it */
InitFunctionCallInfoData(extra->fcinfo, &extra->flinfo, 2,
ssup->ssup_collation, NULL, NULL);
- extra->fcinfo.argnull[0] = false;
- extra->fcinfo.argnull[1] = false;
+ extra->fcinfo.args[0].isnull = false;
+ extra->fcinfo.args[1].isnull = false;
ssup->ssup_extra = extra;
ssup->comparator = comparison_shim;
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 02af68f2c25..8233a465189 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -223,7 +223,8 @@ typedef enum ExprEvalOp
/* aggregation related nodes */
EEOP_AGG_STRICT_DESERIALIZE,
EEOP_AGG_DESERIALIZE,
- EEOP_AGG_STRICT_INPUT_CHECK,
+ 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_BYVAL,
@@ -599,7 +600,8 @@ typedef struct ExprEvalStep
/* for EEOP_AGG_STRICT_INPUT_CHECK */
struct
{
- bool *nulls;
+ NullableDatum *args;
+ bool *nulls;
int nargs;
int jumpnull;
} agg_strict_input_check;
diff --git a/src/include/executor/nodeAgg.h b/src/include/executor/nodeAgg.h
index 8fb8c8fe802..51aec33fd8d 100644
--- a/src/include/executor/nodeAgg.h
+++ b/src/include/executor/nodeAgg.h
@@ -158,12 +158,12 @@ typedef struct AggStatePerTransData
* re-initializing the unchanging fields; which isn't much, but it seems
* worth the extra space consumption.
*/
- FunctionCallInfoData transfn_fcinfo;
+ FunctionCallInfo transfn_fcinfo;
/* Likewise for serialization and deserialization functions */
- FunctionCallInfoData serialfn_fcinfo;
+ FunctionCallInfo serialfn_fcinfo;
- FunctionCallInfoData deserialfn_fcinfo;
+ FunctionCallInfo deserialfn_fcinfo;
} AggStatePerTransData;
/*
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 101f513ba67..ee1dbd5fcbb 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -83,12 +83,28 @@ typedef struct FunctionCallInfoData
#define FIELDNO_FUNCTIONCALLINFODATA_ISNULL 4
bool isnull; /* function must set true if result is NULL */
short nargs; /* # arguments actually passed */
-#define FIELDNO_FUNCTIONCALLINFODATA_ARG 6
- Datum arg[FUNC_MAX_ARGS]; /* Arguments passed to function */
-#define FIELDNO_FUNCTIONCALLINFODATA_ARGNULL 7
- bool argnull[FUNC_MAX_ARGS]; /* T if arg[i] is actually NULL */
+#define FIELDNO_FUNCTIONCALLINFODATA_ARGS 6
+
+ NullableDatum args[FLEXIBLE_ARRAY_MEMBER];
+
} FunctionCallInfoData;
+#define SizeForFunctionCallInfoData(nargs) (offsetof(FunctionCallInfoData, args) + sizeof(NullableDatum) * (nargs))
+
+/*
+ * This macro ensures that `name` points to a stack-allocated
+ * FunctionCallInfoData struct with sufficient space for `nargs` arguments.
+ */
+#define STACK_FCINFO_FOR_ARGS(name, nargs) \
+ union \
+ { \
+ FunctionCallInfoData fcinfo; \
+ /* ensure enough space for nargs args is available */ \
+ char *fcinfo_data[SizeForFunctionCallInfoData(nargs)]; \
+ } name##data; \
+ FunctionCallInfo name = &name##data.fcinfo
+
+
/*
* This routine fills a FmgrInfo struct, given the OID
* of the function to be called.
@@ -141,7 +157,6 @@ extern void fmgr_symbol(Oid functionId, char **mod, char **fn);
*/
#define FunctionCallInvoke(fcinfo) ((* (fcinfo)->flinfo->fn_addr) (fcinfo))
-
/*-------------------------------------------------------------------------
* Support macros to ease writing fmgr-compatible functions
*
@@ -176,7 +191,7 @@ extern void fmgr_symbol(Oid functionId, char **mod, char **fn);
* If function is not marked "proisstrict" in pg_proc, it must check for
* null arguments using this macro. Do not try to GETARG a null argument!
*/
-#define PG_ARGISNULL(n) (fcinfo->argnull[n])
+#define PG_ARGISNULL(n) (fcinfo->args[n].isnull)
/*
* Support for fetching detoasted copies of toastable datatypes (all of
@@ -235,7 +250,7 @@ extern struct varlena *pg_detoast_datum_packed(struct varlena *datum);
/* Macros for fetching arguments of standard types */
-#define PG_GETARG_DATUM(n) (fcinfo->arg[n])
+#define PG_GETARG_DATUM(n) (fcinfo->args[n].datum)
#define PG_GETARG_INT32(n) DatumGetInt32(PG_GETARG_DATUM(n))
#define PG_GETARG_UINT32(n) DatumGetUInt32(PG_GETARG_DATUM(n))
#define PG_GETARG_INT16(n) DatumGetInt16(PG_GETARG_DATUM(n))
@@ -514,6 +529,7 @@ extern Datum CallerFInfoFunctionCall2(PGFunction func, FmgrInfo *flinfo,
* directly-computed parameter list. Note that neither arguments nor result
* are allowed to be NULL.
*/
+extern Datum FunctionCall0Coll(FmgrInfo *flinfo, Oid collation);
extern Datum FunctionCall1Coll(FmgrInfo *flinfo, Oid collation,
Datum arg1);
extern Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation,
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 834708944bf..e04573f11ae 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -828,7 +828,7 @@ typedef struct SetExprState
* (by InitFunctionCallInfoData) if func.fn_oid is valid. It also saves
* argument values between calls, when setArgsValid is true.
*/
- FunctionCallInfoData fcinfo_data;
+ FunctionCallInfo fcinfo;
} SetExprState;
/* ----------------
diff --git a/src/include/postgres.h b/src/include/postgres.h
index b596fcb513e..b3ff25dc6e2 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -366,6 +366,13 @@ typedef struct
typedef uintptr_t Datum;
+typedef struct NullableDatum
+{
+ Datum datum;
+ bool isnull;
+ /* FIXME: space for flags? */
+} NullableDatum;
+
#define SIZEOF_DATUM SIZEOF_VOID_P
/*
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 4cfc5062531..3d4350a16c9 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -1877,7 +1877,7 @@ Datum
plperl_inline_handler(PG_FUNCTION_ARGS)
{
InlineCodeBlock *codeblock = (InlineCodeBlock *) PG_GETARG_POINTER(0);
- FunctionCallInfoData fake_fcinfo;
+ STACK_FCINFO_FOR_ARGS(fake_fcinfo, 0);
FmgrInfo flinfo;
plperl_proc_desc desc;
plperl_call_data *volatile save_call_data = current_call_data;
@@ -1899,10 +1899,10 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
* plperl_call_perl_func(). In particular note that this sets things up
* with no arguments passed, and a result type of VOID.
*/
- MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo));
+ MemSet(&fake_fcinfodata, 0, sizeof(fake_fcinfodata));
MemSet(&flinfo, 0, sizeof(flinfo));
MemSet(&desc, 0, sizeof(desc));
- fake_fcinfo.flinfo = &flinfo;
+ fake_fcinfo->flinfo = &flinfo;
flinfo.fn_oid = InvalidOid;
flinfo.fn_mcxt = CurrentMemoryContext;
@@ -1920,7 +1920,7 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
desc.nargs = 0;
desc.reference = NULL;
- this_call_data.fcinfo = &fake_fcinfo;
+ this_call_data.fcinfo = fake_fcinfo;
this_call_data.prodesc = &desc;
/* we do not bother with refcounting the fake prodesc */
@@ -1940,7 +1940,7 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
if (!desc.reference) /* can this happen? */
elog(ERROR, "could not create internal procedure for anonymous code block");
- perlret = plperl_call_perl_func(&desc, &fake_fcinfo);
+ perlret = plperl_call_perl_func(&desc, fake_fcinfo);
SvREFCNT_dec_current(perlret);
@@ -2194,11 +2194,11 @@ plperl_call_perl_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo)
for (i = 0; i < desc->nargs; i++)
{
- if (fcinfo->argnull[i])
+ if (fcinfo->args[i].isnull)
PUSHs(&PL_sv_undef);
else if (desc->arg_is_rowtype[i])
{
- SV *sv = plperl_hash_from_datum(fcinfo->arg[i]);
+ SV *sv = plperl_hash_from_datum(fcinfo->args[i].datum);
PUSHs(sv_2mortal(sv));
}
@@ -2208,15 +2208,15 @@ plperl_call_perl_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo)
Oid funcid;
if (OidIsValid(desc->arg_arraytype[i]))
- sv = plperl_ref_from_pg_array(fcinfo->arg[i], desc->arg_arraytype[i]);
+ sv = plperl_ref_from_pg_array(fcinfo->args[i].datum, desc->arg_arraytype[i]);
else if ((funcid = get_transform_fromsql(argtypes[i], current_call_data->prodesc->lang_oid, current_call_data->prodesc->trftypes)))
- sv = (SV *) DatumGetPointer(OidFunctionCall1(funcid, fcinfo->arg[i]));
+ sv = (SV *) DatumGetPointer(OidFunctionCall1(funcid, fcinfo->args[i].datum));
else
{
char *tmp;
tmp = OutputFunctionCall(&(desc->arg_out_func[i]),
- fcinfo->arg[i]);
+ fcinfo->args[i].datum);
sv = cstr2sv(tmp);
pfree(tmp);
}
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 45526383f25..665118cfe82 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -489,8 +489,8 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo,
PLpgSQL_var *var = (PLpgSQL_var *) estate.datums[n];
assign_simple_var(&estate, var,
- fcinfo->arg[i],
- fcinfo->argnull[i],
+ fcinfo->args[i].datum,
+ fcinfo->args[i].isnull,
false);
/*
@@ -541,12 +541,12 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo,
{
PLpgSQL_rec *rec = (PLpgSQL_rec *) estate.datums[n];
- if (!fcinfo->argnull[i])
+ if (!fcinfo->args[i].isnull)
{
/* Assign row value from composite datum */
exec_move_row_from_datum(&estate,
(PLpgSQL_variable *) rec,
- fcinfo->arg[i]);
+ fcinfo->args[i].datum);
}
else
{
diff --git a/src/pl/plpython/plpy_exec.c b/src/pl/plpython/plpy_exec.c
index 47ed95dcc60..d1a8c395ffd 100644
--- a/src/pl/plpython/plpy_exec.c
+++ b/src/pl/plpython/plpy_exec.c
@@ -436,10 +436,10 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc)
{
PLyDatumToOb *arginfo = &proc->args[i];
- if (fcinfo->argnull[i])
+ if (fcinfo->args[i].isnull)
arg = NULL;
else
- arg = PLy_input_convert(arginfo, fcinfo->arg[i]);
+ arg = PLy_input_convert(arginfo, fcinfo->args[i].datum);
if (arg == NULL)
{
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index e2fa43b890d..3ee2a7fd4a2 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -601,7 +601,8 @@ call_pltcl_start_proc(Oid prolang, bool pltrusted)
Form_pg_proc procStruct;
AclResult aclresult;
FmgrInfo finfo;
- FunctionCallInfoData fcinfo;
+ STACK_FCINFO_FOR_ARGS(fcinfo, 3);
+
PgStat_FunctionCallUsage fcusage;
/* select appropriate GUC */
@@ -662,11 +663,11 @@ call_pltcl_start_proc(Oid prolang, bool pltrusted)
*/
InvokeFunctionExecuteHook(procOid);
fmgr_info(procOid, &finfo);
- InitFunctionCallInfoData(fcinfo, &finfo,
+ InitFunctionCallInfoData(*fcinfo, &finfo,
0,
InvalidOid, NULL, NULL);
- pgstat_init_function_usage(&fcinfo, &fcusage);
- (void) FunctionCallInvoke(&fcinfo);
+ pgstat_init_function_usage(fcinfo, &fcusage);
+ (void) FunctionCallInvoke(fcinfo);
pgstat_end_function_usage(&fcusage, true);
/* Pop the error context stack */
@@ -873,7 +874,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
/**************************************************
* For tuple values, add a list for 'array set ...'
**************************************************/
- if (fcinfo->argnull[i])
+ if (fcinfo->args[i].isnull)
Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
else
{
@@ -884,7 +885,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
HeapTupleData tmptup;
Tcl_Obj *list_tmp;
- td = DatumGetHeapTupleHeader(fcinfo->arg[i]);
+ td = DatumGetHeapTupleHeader(fcinfo->args[i].datum);
/* Extract rowtype info and find a tupdesc */
tupType = HeapTupleHeaderGetTypeId(td);
tupTypmod = HeapTupleHeaderGetTypMod(td);
@@ -905,14 +906,14 @@ pltcl_func_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
* Single values are added as string element
* of their external representation
**************************************************/
- if (fcinfo->argnull[i])
+ if (fcinfo->args[i].isnull)
Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
else
{
char *tmp;
tmp = OutputFunctionCall(&prodesc->arg_out_func[i],
- fcinfo->arg[i]);
+ fcinfo->args[i].datum);
UTF_BEGIN;
Tcl_ListObjAppendElement(NULL, tcl_cmd,
Tcl_NewStringObj(UTF_E2U(tmp), -1));
--
2.18.0.rc2.dirty
--kkvoa4kudrvr5owc
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v2-0002-Replace-fmgr.-ch-duplication-with-macro-magic.patch"
^ permalink raw reply [nested|flat] 30+ messages in thread
* [PATCH v6 7/7] Update comment obsolete since 69c3936a
@ 2020-02-15 21:53 Justin Pryzby <[email protected]>
0 siblings, 0 replies; 30+ messages in thread
From: Justin Pryzby @ 2020-02-15 21:53 UTC (permalink / raw)
---
src/backend/executor/nodeAgg.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 3d60f7a314..567d65bb6c 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1538,8 +1538,7 @@ lookup_hash_entry(AggState *aggstate, uint32 hash)
}
/*
- * Look up hash entries for the current tuple in all hashed grouping sets,
- * returning an array of pergroup pointers suitable for advance_aggregates.
+ * Look up hash entries for the current tuple in all hashed grouping sets.
*
* Be aware that lookup_hash_entry can reset the tmpcontext.
*/
--
2.17.0
--xHbokkKX1kTiQeDC--
^ permalink raw reply [nested|flat] 30+ messages in thread
* [PATCH v8 8/8] Update comment obsolete since 69c3936a
@ 2020-02-15 21:53 Justin Pryzby <[email protected]>
0 siblings, 0 replies; 30+ messages in thread
From: Justin Pryzby @ 2020-02-15 21:53 UTC (permalink / raw)
---
src/backend/executor/nodeAgg.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 4cd09e2291..68fe4d3591 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -2025,8 +2025,7 @@ lookup_hash_entry(AggState *aggstate, uint32 hash)
}
/*
- * Look up hash entries for the current tuple in all hashed grouping sets,
- * returning an array of pergroup pointers suitable for advance_aggregates.
+ * Look up hash entries for the current tuple in all hashed grouping sets.
*
* Be aware that lookup_hash_entry can reset the tmpcontext.
*
--
2.17.0
--ZljC5FVPx7rxDQQ8--
^ permalink raw reply [nested|flat] 30+ messages in thread
* [PATCH v7 7/9] Update comment obsolete since 69c3936a
@ 2020-02-15 21:53 Justin Pryzby <[email protected]>
0 siblings, 0 replies; 30+ messages in thread
From: Justin Pryzby @ 2020-02-15 21:53 UTC (permalink / raw)
---
src/backend/executor/nodeAgg.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 867da6eebf..9aa2941457 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1538,8 +1538,7 @@ lookup_hash_entry(AggState *aggstate, uint32 hash)
}
/*
- * Look up hash entries for the current tuple in all hashed grouping sets,
- * returning an array of pergroup pointers suitable for advance_aggregates.
+ * Look up hash entries for the current tuple in all hashed grouping sets.
*
* Be aware that lookup_hash_entry can reset the tmpcontext.
*/
--
2.17.0
--UfEAyuTBtIjiZzX6
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment; filename="v7-0008-Add-explain-MACHINE.patch"
^ permalink raw reply [nested|flat] 30+ messages in thread
* [PATCH v3] Update information_schema definition for not-null constraints
@ 2023-09-04 16:05 Alvaro Herrera <[email protected]>
0 siblings, 0 replies; 30+ messages in thread
From: Alvaro Herrera @ 2023-09-04 16:05 UTC (permalink / raw)
Now that we have catalogued not-null constraints, our information_schema
definition must be updated to grab those rather than fabricate synthetic
definitions.
Note that we still don't have catalog rows for not-null constraints on
domains, but we've never had not-null constraints listed in
information_schema, so that's a problem to be solved separately.
Co-authored-by: Peter Eisentraut <[email protected]>
Co-authored-by: Álvaro Herrera <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/backend/catalog/information_schema.sql | 74 ++++++++--------------
src/include/catalog/catversion.h | 2 +-
2 files changed, 28 insertions(+), 48 deletions(-)
diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql
index a06ec7a0a8..c402cca7f4 100644
--- a/src/backend/catalog/information_schema.sql
+++ b/src/backend/catalog/information_schema.sql
@@ -444,22 +444,19 @@ CREATE VIEW check_constraints AS
WHERE pg_has_role(coalesce(c.relowner, t.typowner), 'USAGE')
AND con.contype = 'c'
- UNION
+ UNION ALL
-- not-null constraints
-
- SELECT CAST(current_database() AS sql_identifier) AS constraint_catalog,
- CAST(n.nspname AS sql_identifier) AS constraint_schema,
- CAST(CAST(n.oid AS text) || '_' || CAST(r.oid AS text) || '_' || CAST(a.attnum AS text) || '_not_null' AS sql_identifier) AS constraint_name, -- XXX
- CAST(a.attname || ' IS NOT NULL' AS character_data)
- AS check_clause
- FROM pg_namespace n, pg_class r, pg_attribute a
- WHERE n.oid = r.relnamespace
- AND r.oid = a.attrelid
- AND a.attnum > 0
- AND NOT a.attisdropped
- AND a.attnotnull
- AND r.relkind IN ('r', 'p')
- AND pg_has_role(r.relowner, 'USAGE');
+ SELECT current_database()::information_schema.sql_identifier AS constraint_catalog,
+ rs.nspname::information_schema.sql_identifier AS constraint_schema,
+ con.conname::information_schema.sql_identifier AS constraint_name,
+ format('CHECK (%s IS NOT NULL)', at.attname)::information_schema.character_data AS check_clause
+ FROM pg_constraint con
+ LEFT JOIN pg_namespace rs ON rs.oid = con.connamespace
+ LEFT JOIN pg_class c ON c.oid = con.conrelid
+ LEFT JOIN pg_type t ON t.oid = con.contypid
+ LEFT JOIN pg_attribute at ON (con.conrelid = at.attrelid AND con.conkey[1] = at.attnum)
+ WHERE pg_has_role(coalesce(c.relowner, t.typowner), 'USAGE'::text)
+ AND con.contype = 'n';
GRANT SELECT ON check_constraints TO PUBLIC;
@@ -826,6 +823,20 @@ CREATE VIEW constraint_column_usage AS
AND r.relkind IN ('r', 'p')
AND NOT a.attisdropped
+ UNION ALL
+
+ /* not-null constraints */
+ SELECT DISTINCT nr.nspname, r.relname, r.relowner, a.attname, nc.nspname, c.conname
+ FROM pg_namespace nr, pg_class r, pg_attribute a, pg_namespace nc, pg_constraint c
+ WHERE nr.oid = r.relnamespace
+ AND r.oid = a.attrelid
+ AND r.oid = c.conrelid
+ AND a.attnum = c.conkey[1]
+ AND c.connamespace = nc.oid
+ AND c.contype = 'n'
+ AND r.relkind in ('r', 'p')
+ AND not a.attisdropped
+
UNION ALL
/* unique/primary key/foreign key constraints */
@@ -1828,6 +1839,7 @@ CREATE VIEW table_constraints AS
CAST(r.relname AS sql_identifier) AS table_name,
CAST(
CASE c.contype WHEN 'c' THEN 'CHECK'
+ WHEN 'n' THEN 'CHECK'
WHEN 'f' THEN 'FOREIGN KEY'
WHEN 'p' THEN 'PRIMARY KEY'
WHEN 'u' THEN 'UNIQUE' END
@@ -1852,38 +1864,6 @@ CREATE VIEW table_constraints AS
AND c.contype NOT IN ('t', 'x') -- ignore nonstandard constraints
AND r.relkind IN ('r', 'p')
AND (NOT pg_is_other_temp_schema(nr.oid))
- AND (pg_has_role(r.relowner, 'USAGE')
- -- SELECT privilege omitted, per SQL standard
- OR has_table_privilege(r.oid, 'INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER')
- OR has_any_column_privilege(r.oid, 'INSERT, UPDATE, REFERENCES') )
-
- UNION ALL
-
- -- not-null constraints
-
- SELECT CAST(current_database() AS sql_identifier) AS constraint_catalog,
- CAST(nr.nspname AS sql_identifier) AS constraint_schema,
- CAST(CAST(nr.oid AS text) || '_' || CAST(r.oid AS text) || '_' || CAST(a.attnum AS text) || '_not_null' AS sql_identifier) AS constraint_name, -- XXX
- CAST(current_database() AS sql_identifier) AS table_catalog,
- CAST(nr.nspname AS sql_identifier) AS table_schema,
- CAST(r.relname AS sql_identifier) AS table_name,
- CAST('CHECK' AS character_data) AS constraint_type,
- CAST('NO' AS yes_or_no) AS is_deferrable,
- CAST('NO' AS yes_or_no) AS initially_deferred,
- CAST('YES' AS yes_or_no) AS enforced,
- CAST(NULL AS yes_or_no) AS nulls_distinct
-
- FROM pg_namespace nr,
- pg_class r,
- pg_attribute a
-
- WHERE nr.oid = r.relnamespace
- AND r.oid = a.attrelid
- AND a.attnotnull
- AND a.attnum > 0
- AND NOT a.attisdropped
- AND r.relkind IN ('r', 'p')
- AND (NOT pg_is_other_temp_schema(nr.oid))
AND (pg_has_role(r.relowner, 'USAGE')
-- SELECT privilege omitted, per SQL standard
OR has_table_privilege(r.oid, 'INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER')
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index ab9a7ac1f7..4eaef54d0c 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -57,6 +57,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 202308251
+#define CATALOG_VERSION_NO 202309061
#endif
--
2.30.2
--ljgpw53wkr76ezax--
^ permalink raw reply [nested|flat] 30+ messages in thread
* Re: [PATCH] Hex-coding optimizations using SVE on ARM.
@ 2025-01-15 11:06 ` Ranier Vilela <[email protected]>
1 sibling, 0 replies; 30+ messages in thread
From: Ranier Vilela @ 2025-01-15 11:06 UTC (permalink / raw)
To: John Naylor <[email protected]>; +Cc: Tom Lane <[email protected]>; Nathan Bossart <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; pgsql-hackers; [email protected] <[email protected]>
Hi.
Em qua., 15 de jan. de 2025 às 07:57, John Naylor <[email protected]>
escreveu:
> On Wed, Jan 15, 2025 at 2:14 PM Tom Lane <[email protected]> wrote:
>
> > Couple of thoughts:
> >
> > 1. I was actually hoping for a comment on the constant's definition,
> > perhaps along the lines of
> >
> > /*
> > * The hex expansion of each possible byte value (two chars per value).
> > */
>
> Works for me. With that, did you mean we then wouldn't need a comment
> in the code?
>
> > 2. Since "src" is defined as "const char *", I'm pretty sure that
> > pickier compilers will complain that
> >
> > + unsigned char usrc = *((unsigned char *) src);
> >
> > results in casting away const. Recommend
> >
> > + unsigned char usrc = *((const unsigned char *) src);
>
> Thanks for the reminder!
>
> > 3. I really wonder if
> >
> > + memcpy(dst, &hextbl[2 * usrc], 2);
> >
> > is faster than copying the two bytes manually, along the lines of
> >
> > + *dst++ = hextbl[2 * usrc];
> > + *dst++ = hextbl[2 * usrc + 1];
> >
> > Compilers that inline memcpy() may arrive at the same machine code,
> > but why rely on the compiler to make that optimization? If the
> > compiler fails to do so, an out-of-line memcpy() call will surely
> > be a loser.
>
> See measurements at the end. As for compilers, gcc 3.4.6 and clang
> 3.0.0 can inline the memcpy. The manual copy above only gets combined
> to a single word starting with gcc 12 and clang 15, and latest MSVC
> still can't do it (4A in the godbolt link below). Are there any
> buildfarm animals around that may not inline memcpy for word-sized
> input?
>
> > A variant could be
> >
> > + const char *hexptr = &hextbl[2 * usrc];
> > + *dst++ = hexptr[0];
> > + *dst++ = hexptr[1];
> >
> > but this supposes that the compiler fails to see the common
> > subexpression in the other formulation, which I believe
> > most modern compilers will see.
>
> This combines to a single word starting with clang 5, but does not
> work on gcc 14.2 or gcc trunk (4B below). I have gcc 14.2 handy, and
> on my machine bytewise load/stores are somewhere in the middle:
>
> master 1158.969 ms
> v3 776.791 ms
> variant 4A 775.777 ms
> variant 4B 969.945 ms
>
> https://godbolt.org/z/ajToordKq
Your example from godbolt, has a
have an important difference, which modifies the assembler result.
-static const char hextbl[] =
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"
;
+static const char hextbl[512] =
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"
;
best regards,
Ranier Vilela
^ permalink raw reply [nested|flat] 30+ messages in thread
* Re: [PATCH] Hex-coding optimizations using SVE on ARM.
@ 2025-06-04 13:47 ` Nathan Bossart <[email protected]>
2025-06-09 09:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
1 sibling, 1 reply; 30+ messages in thread
From: Nathan Bossart @ 2025-06-04 13:47 UTC (permalink / raw)
To: [email protected] <[email protected]>; +Cc: Tom Lane <[email protected]>; David Rowley <[email protected]>; John Naylor <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>
I have marked the commitfest entry for this [0] as waiting-on-author
because the patch needs to be rebased.
[0] https://commitfest.postgresql.org/patch/5538/
--
nathan
^ permalink raw reply [nested|flat] 30+ messages in thread
* Re: [PATCH] Hex-coding optimizations using SVE on ARM.
2025-06-04 13:47 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
@ 2025-06-09 09:49 ` [email protected] <[email protected]>
2025-07-07 10:41 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
0 siblings, 1 reply; 30+ messages in thread
From: [email protected] @ 2025-06-09 09:49 UTC (permalink / raw)
To: Nathan Bossart <[email protected]>; +Cc: Tom Lane <[email protected]>; David Rowley <[email protected]>; John Naylor <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>
Here's the rebased patch with a few modifications.
The hand-unrolled hex encode performs better than the non-unrolled version on
r8g.4xlarge. No improvement on m7g.4xlarge.
Added line-by-line comments explaining the changes with an example.
Below are the results. Input size is in bytes, and exec time is in ms.
encode - r8g.4xlarge
Input | master | SVE | SVE-unrolled
-------+--------+--------+--------------
8 | 4.971 | 6.434 | 6.623
16 | 8.532 | 4.399 | 4.710
24 | 12.296 | 5.007 | 5.780
32 | 16.003 | 5.027 | 5.234
40 | 19.628 | 5.807 | 6.201
48 | 23.277 | 5.815 | 6.222
56 | 26.927 | 6.744 | 7.030
64 | 30.419 | 6.774 | 6.347
128 | 83.250 | 10.214 | 9.104
256 |112.158 | 17.892 | 16.313
512 |216.544 | 31.060 | 29.876
1024 |429.351 | 59.310 | 53.374
2048 |854.677 |116.769 | 101.004
4096 |1706.528|237.322 | 195.297
8192 |3723.884|499.520 | 385.424
---------------------------------------
encode - m7g.4xlarge
Input | master | SVE | SVE-unrolled
-------+--------+--------+--------------
8 | 5.503 | 7.986 | 8.053
16 | 9.881 | 9.583 | 9.888
24 | 13.854 | 9.212 | 10.138
32 | 18.056 | 9.208 | 9.364
40 | 22.127 | 10.134 | 10.540
48 | 26.214 | 10.186 | 10.550
56 | 29.718 | 10.197 | 10.428
64 | 33.613 | 10.982 | 10.497
128 | 66.060 | 12.460 | 12.624
256 |130.225 | 18.491 | 18.872
512 |267.105 | 30.343 | 31.661
1024 |515.603 | 54.371 | 55.341
2048 |1013.766|103.898 | 105.192
4096 |2018.705|202.653 | 203.142
8192 |4000.496|400.918 | 401.842
---------------------------------------
decode - r8g.4xlarge
Input | master | SVE
-------+--------+--------
8 | 7.641 | 8.787
16 | 14.301 | 14.477
32 | 28.663 | 6.091
48 | 42.940 | 17.604
64 | 57.483 | 10.549
80 | 71.637 | 19.194
96 | 85.918 | 15.586
112 |100.272 | 25.956
128 |114.740 | 19.829
256 |229.176 | 36.032
512 |458.295 | 68.222
1024 |916.741 |132.927
2048 |1833.422|262.741
4096 |3667.096|522.009
8192 |7333.886|1042.447
---------------------------------------
decode - m7g.4xlarge
Input | master | SVE
-------+--------+--------
8 | 8.194 | 9.433
16 | 14.397 | 15.606
32 | 26.669 | 29.006
48 | 45.971 | 48.984
64 | 58.468 | 12.388
80 | 70.820 | 22.295
96 | 84.792 | 43.470
112 | 98.992 | 54.282
128 |113.250 | 25.508
256 |218.743 | 45.165
512 |414.133 | 86.800
1024 |828.493 |174.670
2048 |1617.921|346.375
4096 |3259.159|689.391
8192 |6551.879|1376.195
--------
Chiranmoy
Attachments:
[application/octet-stream] v5-0001-SVE-support-for-hex-coding.patch (24.6K, ../../OS9PR01MB15185A8BA8E15E4AFD8B14490976BA@OS9PR01MB15185.jpnprd01.prod.outlook.com/3-v5-0001-SVE-support-for-hex-coding.patch)
download | inline diff:
From 3a508684171ae411e4e8251c717b61a8def04c1f Mon Sep 17 00:00:00 2001
From: Chiranmoy Bhattacharya <[email protected]>
Date: Mon, 9 Jun 2025 14:16:26 +0530
Subject: [PATCH v5] SVE support for hex coding
---
config/c-compiler.m4 | 85 ++++++++
configure | 104 +++++++++
configure.ac | 9 +
meson.build | 81 +++++++
src/backend/utils/adt/Makefile | 1 +
src/backend/utils/adt/encode.c | 6 +-
src/backend/utils/adt/encode_aarch64.c | 278 +++++++++++++++++++++++++
src/backend/utils/adt/meson.build | 1 +
src/include/pg_config.h.in | 3 +
src/include/utils/builtins.h | 51 ++++-
10 files changed, 613 insertions(+), 6 deletions(-)
create mode 100644 src/backend/utils/adt/encode_aarch64.c
diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index 5f3e1d1faf9..20e71cd8546 100644
--- a/config/c-compiler.m4
+++ b/config/c-compiler.m4
@@ -797,3 +797,88 @@ if test x"$Ac_cachevar" = x"yes"; then
fi
undefine([Ac_cachevar])dnl
])# PGAC_SVE_POPCNT_INTRINSICS
+
+# PGAC_ARM_SVE_HEX_INTRINSICS
+# ------------------------------
+# Check if the compiler supports the SVE intrinsic required for hex coding:
+# svsub_x, svcmplt, svsel, svcmpgt, svtbl, svlsr_x, svand_z, svcreate2,
+# svptest_any, svnot_z, svorr_z, svcntb, svld1, svwhilelt_b8, svst2, svld2,
+# svget2, svst1 and svlsl_x.
+#
+# If the intrinsics are supported, sets pgac_arm_sve_hex_intrinsics.
+AC_DEFUN([PGAC_ARM_SVE_HEX_INTRINSICS],
+[define([Ac_cachevar], [AS_TR_SH([pgac_cv_arm_sve_hex_intrinsics])])dnl
+AC_CACHE_CHECK([for svtbl, svlsr_x, svand_z, svcreate2, etc], [Ac_cachevar],
+[AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <arm_sve.h>
+
+ char input@<:@64@:>@;
+ char output@<:@128@:>@;
+
+ #if defined(__has_attribute) && __has_attribute (target)
+ __attribute__((target("arch=armv8-a+sve")))
+ #endif
+ int get_hex_sve(svbool_t pred, svuint8_t vec, svuint8_t *res)
+ {
+ svuint8_t digit = svsub_x(pred, vec, 48),
+ upper = svsub_x(pred, vec, 55),
+ lower = svsub_x(pred, vec, 87);
+ svbool_t valid_digit = svcmplt(pred, digit, 10),
+ valid_upper = svcmplt(pred, upper, 16);
+ svuint8_t letter = svsel(valid_upper, upper, lower);
+ svbool_t valid_letter = svand_z(pred, svcmpgt(pred, letter, 9),
+ svcmplt(pred, letter, 16));
+ if (svptest_any(pred, svnot_z(pred, svorr_z(pred, valid_digit, valid_letter))))
+ return 0;
+ *res = svsel(valid_digit, digit, letter);
+ return 1;
+ }
+
+ #if defined(__has_attribute) && __has_attribute (target)
+ __attribute__((target("arch=armv8-a+sve")))
+ #endif
+ static int hex_coding_test(void)
+ {
+ int len = 64, vec_len = svcntb(), vec_len_x2 = svcntb() * 2;
+ const char *hextbl = "0123456789abcdef";
+ svuint8_t hextbl_vec = svld1(svwhilelt_b8(0, 16), (uint8_t *) hextbl);
+ char *src = input, *dst = output;
+
+ /* hex encode */
+ for (uint64_t i = 0; i < 64; i += vec_len, dst += 2 * vec_len, src += vec_len)
+ {
+ svbool_t pred = svwhilelt_b8((uint64_t) i, (uint64_t) len);
+ svuint8_t bytes = svld1(pred, (uint8_t *) src),
+ high = svlsr_x(pred, bytes, 4),
+ low = svand_z(pred, bytes, 0xF);
+ svuint8x2_t merged = svcreate2(svtbl(hextbl_vec, high), svtbl(hextbl_vec, low));
+ svst2(pred, (uint8_t *) dst, merged);
+ }
+
+ /* hex decode */
+ len = 128;
+
+ for (int i; i < len; i += vec_len_x2)
+ {
+ svbool_t pred = svwhilelt_b8((uint64_t) i / 2, (uint64_t) len / 2);
+ svuint8x2_t bytes = svld2(pred, (uint8_t *) src + i);
+ svuint8_t high = svget2(bytes, 0), low = svget2(bytes, 1);
+
+ if (svptest_any(pred, svorr_z(pred, svcmplt(pred, high, '0'), svcmplt(pred, low, '0'))))
+ break;
+ if (!get_hex_sve(pred, high, &high) || !get_hex_sve(pred, low, &low))
+ break;
+
+ svst1(pred, (uint8_t *) dst + i / 2, svorr_z(pred, svlsl_x(pred, high, 4), low));
+ }
+
+ /* return computed value, to prevent the above being optimized away */
+ return output@<:@0@:>@;
+ }],
+ [return hex_coding_test();])],
+ [Ac_cachevar=yes],
+ [Ac_cachevar=no])])
+if test x"$Ac_cachevar" = x"yes"; then
+ pgac_arm_sve_hex_intrinsics=yes
+fi
+undefine([Ac_cachevar])dnl
+])# PGAC_ARM_SVE_HEX_INTRINSICS
diff --git a/configure b/configure
index 4f15347cc95..4d5d6acefb5 100755
--- a/configure
+++ b/configure
@@ -17851,6 +17851,110 @@ $as_echo "#define USE_SVE_POPCNT_WITH_RUNTIME_CHECK 1" >>confdefs.h
fi
fi
+# Check for ARM SVE intrinsics for hex coding
+#
+if test x"$host_cpu" = x"aarch64"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for svtbl, svlsr_x, svand_z, svcreate2, etc" >&5
+$as_echo_n "checking for svtbl, svlsr_x, svand_z, svcreate2, etc... " >&6; }
+if ${pgac_cv_arm_sve_hex_intrinsics+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <arm_sve.h>
+
+ char input[64];
+ char output[128];
+
+ #if defined(__has_attribute) && __has_attribute (target)
+ __attribute__((target("arch=armv8-a+sve")))
+ #endif
+ int get_hex_sve(svbool_t pred, svuint8_t vec, svuint8_t *res)
+ {
+ svuint8_t digit = svsub_x(pred, vec, 48),
+ upper = svsub_x(pred, vec, 55),
+ lower = svsub_x(pred, vec, 87);
+ svbool_t valid_digit = svcmplt(pred, digit, 10),
+ valid_upper = svcmplt(pred, upper, 16);
+ svuint8_t letter = svsel(valid_upper, upper, lower);
+ svbool_t valid_letter = svand_z(pred, svcmpgt(pred, letter, 9),
+ svcmplt(pred, letter, 16));
+ if (svptest_any(pred, svnot_z(pred, svorr_z(pred, valid_digit, valid_letter))))
+ return 0;
+ *res = svsel(valid_digit, digit, letter);
+ return 1;
+ }
+
+ #if defined(__has_attribute) && __has_attribute (target)
+ __attribute__((target("arch=armv8-a+sve")))
+ #endif
+ static int hex_coding_test(void)
+ {
+ int len = 64, vec_len = svcntb(), vec_len_x2 = svcntb() * 2;
+ const char *hextbl = "0123456789abcdef";
+ svuint8_t hextbl_vec = svld1(svwhilelt_b8(0, 16), (uint8_t *) hextbl);
+ char *src = input, *dst = output;
+
+ /* hex encode */
+ for (uint64_t i = 0; i < 64; i += vec_len, dst += 2 * vec_len, src += vec_len)
+ {
+ svbool_t pred = svwhilelt_b8((uint64_t) i, (uint64_t) len);
+ svuint8_t bytes = svld1(pred, (uint8_t *) src),
+ high = svlsr_x(pred, bytes, 4),
+ low = svand_z(pred, bytes, 0xF);
+ svuint8x2_t merged = svcreate2(svtbl(hextbl_vec, high), svtbl(hextbl_vec, low));
+ svst2(pred, (uint8_t *) dst, merged);
+ }
+
+ /* hex decode */
+ len = 128;
+
+ for (int i; i < len; i += vec_len_x2)
+ {
+ svbool_t pred = svwhilelt_b8((uint64_t) i / 2, (uint64_t) len / 2);
+ svuint8x2_t bytes = svld2(pred, (uint8_t *) src + i);
+ svuint8_t high = svget2(bytes, 0), low = svget2(bytes, 1);
+
+ if (svptest_any(pred, svorr_z(pred, svcmplt(pred, high, '0'), svcmplt(pred, low, '0'))))
+ break;
+ if (!get_hex_sve(pred, high, &high) || !get_hex_sve(pred, low, &low))
+ break;
+
+ svst1(pred, (uint8_t *) dst + i / 2, svorr_z(pred, svlsl_x(pred, high, 4), low));
+ }
+
+ /* return computed value, to prevent the above being optimized away */
+ return output[0];
+ }
+int
+main ()
+{
+return hex_coding_test();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ pgac_cv_arm_sve_hex_intrinsics=yes
+else
+ pgac_cv_arm_sve_hex_intrinsics=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_arm_sve_hex_intrinsics" >&5
+$as_echo "$pgac_cv_arm_sve_hex_intrinsics" >&6; }
+if test x"$pgac_cv_arm_sve_hex_intrinsics" = x"yes"; then
+ pgac_arm_sve_hex_intrinsics=yes
+fi
+
+ if test x"$pgac_arm_sve_hex_intrinsics" = x"yes"; then
+
+$as_echo "#define USE_SVE_HEX_WITH_RUNTIME_CHECK 1" >>confdefs.h
+
+ fi
+fi
+
# Check for Intel SSE 4.2 intrinsics to do CRC calculations.
#
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for _mm_crc32_u8 and _mm_crc32_u32" >&5
diff --git a/configure.ac b/configure.ac
index 4b8335dc613..fcae9b84616 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2107,6 +2107,15 @@ if test x"$host_cpu" = x"aarch64"; then
fi
fi
+# Check for ARM SVE intrinsics for hex coding
+#
+if test x"$host_cpu" = x"aarch64"; then
+ PGAC_ARM_SVE_HEX_INTRINSICS()
+ if test x"$pgac_arm_sve_hex_intrinsics" = x"yes"; then
+ AC_DEFINE(USE_SVE_HEX_WITH_RUNTIME_CHECK, 1, [Define to 1 to use ARM SVE intrinsic for hex coding.])
+ fi
+fi
+
# Check for Intel SSE 4.2 intrinsics to do CRC calculations.
#
PGAC_SSE42_CRC32_INTRINSICS()
diff --git a/meson.build b/meson.build
index d142e3e408b..de2d1ebd384 100644
--- a/meson.build
+++ b/meson.build
@@ -2384,6 +2384,87 @@ int main(void)
endif
+###############################################################
+# Check the availability of SVE intrinsics for hex coding.
+###############################################################
+
+if host_cpu == 'aarch64'
+
+ prog = '''
+#include <arm_sve.h>
+
+char input[64];
+char output[128];
+
+#if defined(__has_attribute) && __has_attribute (target)
+ __attribute__((target("arch=armv8-a+sve")))
+#endif
+int get_hex_sve(svbool_t pred, svuint8_t vec, svuint8_t *res)
+{
+ svuint8_t digit = svsub_x(pred, vec, 48),
+ upper = svsub_x(pred, vec, 55),
+ lower = svsub_x(pred, vec, 87);
+ svbool_t valid_digit = svcmplt(pred, digit, 10),
+ valid_upper = svcmplt(pred, upper, 16);
+ svuint8_t letter = svsel(valid_upper, upper, lower);
+ svbool_t valid_letter = svand_z(pred, svcmpgt(pred, letter, 9),
+ svcmplt(pred, letter, 16));
+ if (svptest_any(pred, svnot_z(pred, svorr_z(pred, valid_digit, valid_letter))))
+ return 0;
+ *res = svsel(valid_digit, digit, letter);
+ return 1;
+}
+
+#if defined(__has_attribute) && __has_attribute (target)
+ __attribute__((target("arch=armv8-a+sve")))
+#endif
+int main(void)
+{
+ int len = 64, vec_len = svcntb(), vec_len_x2 = svcntb() * 2;
+ const char hextbl[] = "0123456789abcdef";
+ svuint8_t hextbl_vec = svld1(svwhilelt_b8(0, 16), (uint8_t *) hextbl);
+ char *src = input, *dst = output;
+
+ /* hex encode */
+ for (uint64_t i = 0; i < 64; i += vec_len, dst += 2 * vec_len, src += vec_len)
+ {
+ svbool_t pred = svwhilelt_b8((uint64_t) i, (uint64_t) len);
+ svuint8_t bytes = svld1(pred, (uint8_t *) src),
+ high = svlsr_x(pred, bytes, 4),
+ low = svand_z(pred, bytes, 0xF);
+ svuint8x2_t merged = svcreate2(svtbl(hextbl_vec, high), svtbl(hextbl_vec, low));
+ svst2(pred, (uint8_t *) dst, merged);
+ }
+
+ /* hex decode */
+ len = 128;
+
+ for (int i; i < len; i += vec_len_x2)
+ {
+ svbool_t pred = svwhilelt_b8((uint64_t) i / 2, (uint64_t) len / 2);
+ svuint8x2_t bytes = svld2(pred, (uint8_t *) src + i);
+ svuint8_t high = svget2(bytes, 0), low = svget2(bytes, 1);
+
+ if (svptest_any(pred, svorr_z(pred, svcmplt(pred, high, '0'), svcmplt(pred, low, '0'))))
+ break;
+ if (!get_hex_sve(pred, high, &high) || !get_hex_sve(pred, low, &low))
+ break;
+
+ svst1(pred, (uint8_t *) dst + i / 2, svorr_z(pred, svlsl_x(pred, high, 4), low));
+ }
+
+ /* return computed value, to prevent the above being optimized away */
+ return output[0];
+}
+'''
+
+ if cc.links(prog, name: 'SVE hex coding', args: test_c_args)
+ cdata.set('USE_SVE_HEX_WITH_RUNTIME_CHECK', 1)
+ endif
+
+endif
+
+
###############################################################
# Select CRC-32C implementation.
#
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 4a233b63c32..2a3ba1d4485 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -32,6 +32,7 @@ OBJS = \
dbsize.o \
domains.o \
encode.o \
+ encode_aarch64.o \
enum.o \
expandeddatum.o \
expandedrecord.o \
diff --git a/src/backend/utils/adt/encode.c b/src/backend/utils/adt/encode.c
index 4ccaed815d1..fa62ce3107d 100644
--- a/src/backend/utils/adt/encode.c
+++ b/src/backend/utils/adt/encode.c
@@ -178,7 +178,7 @@ static const int8 hexlookup[128] = {
};
uint64
-hex_encode(const char *src, size_t len, char *dst)
+hex_encode_scalar(const char *src, size_t len, char *dst)
{
const char *end = src + len;
@@ -208,13 +208,13 @@ get_hex(const char *cp, char *out)
}
uint64
-hex_decode(const char *src, size_t len, char *dst)
+hex_decode_scalar(const char *src, size_t len, char *dst)
{
return hex_decode_safe(src, len, dst, NULL);
}
uint64
-hex_decode_safe(const char *src, size_t len, char *dst, Node *escontext)
+hex_decode_safe_scalar(const char *src, size_t len, char *dst, Node *escontext)
{
const char *s,
*srcend;
diff --git a/src/backend/utils/adt/encode_aarch64.c b/src/backend/utils/adt/encode_aarch64.c
new file mode 100644
index 00000000000..574a7550469
--- /dev/null
+++ b/src/backend/utils/adt/encode_aarch64.c
@@ -0,0 +1,278 @@
+/*-------------------------------------------------------------------------
+ *
+ * encode_aarch64.c
+ * Holds the SVE hex encode/decode implementations.
+ *
+ * Copyright (c) 2025, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/encode_aarch64.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <c.h>
+
+#include "utils/builtins.h"
+
+#ifdef USE_SVE_HEX_WITH_RUNTIME_CHECK
+#include <arm_sve.h>
+
+#if defined(HAVE_ELF_AUX_INFO) || defined(HAVE_GETAUXVAL)
+#include <sys/auxv.h>
+#endif
+
+/*
+ * These are the SVE implementations of the hex encode/decode functions.
+ */
+static uint64 hex_encode_sve(const char *src, size_t len, char *dst);
+static uint64 hex_decode_sve(const char *src, size_t len, char *dst);
+static uint64 hex_decode_safe_sve(const char *src, size_t len, char *dst, Node *escontext);
+
+/*
+ * The function pointers are initially set to "choose" functions. These
+ * functions will first set the pointers to the right implementations (based on
+ * what the current CPU supports) and then will call the pointer to fulfill the
+ * caller's request.
+ */
+
+static uint64 hex_encode_choose(const char *src, size_t len, char *dst);
+static uint64 hex_decode_choose(const char *src, size_t len, char *dst);
+static uint64 hex_decode_safe_choose(const char *src, size_t len, char *dst, Node *escontext);
+uint64 (*hex_encode_optimized) (const char *src, size_t len, char *dst) = hex_encode_choose;
+uint64 (*hex_decode_optimized) (const char *src, size_t len, char *dst) = hex_decode_choose;
+uint64 (*hex_decode_safe_optimized) (const char *src, size_t len, char *dst, Node *escontext) = hex_decode_safe_choose;
+
+static inline bool
+check_sve_support(void)
+{
+#ifdef HAVE_ELF_AUX_INFO
+ unsigned long value;
+
+ return elf_aux_info(AT_HWCAP, &value, sizeof(value)) == 0 &&
+ (value & HWCAP_SVE) != 0;
+#elif defined(HAVE_GETAUXVAL)
+ return (getauxval(AT_HWCAP) & HWCAP_SVE) != 0;
+#else
+ return false;
+#endif
+}
+
+static inline void
+choose_hex_functions(void)
+{
+ if (check_sve_support())
+ {
+ hex_encode_optimized = hex_encode_sve;
+ hex_decode_optimized = hex_decode_sve;
+ hex_decode_safe_optimized = hex_decode_safe_sve;
+ }
+ else
+ {
+ hex_encode_optimized = hex_encode_scalar;
+ hex_decode_optimized = hex_decode_scalar;
+ hex_decode_safe_optimized = hex_decode_safe_scalar;
+ }
+}
+
+static uint64
+hex_encode_choose(const char *src, size_t len, char *dst)
+{
+ choose_hex_functions();
+ return hex_encode_optimized(src, len, dst);
+}
+static uint64
+hex_decode_choose(const char *src, size_t len, char *dst)
+{
+ choose_hex_functions();
+ return hex_decode_optimized(src, len, dst);
+}
+static uint64
+hex_decode_safe_choose(const char *src, size_t len, char *dst, Node *escontext)
+{
+ choose_hex_functions();
+ return hex_decode_safe_optimized(src, len, dst, escontext);
+}
+
+pg_attribute_target("arch=armv8-a+sve")
+uint64
+hex_encode_sve(const char *src, size_t len, char *dst)
+{
+ const char hextbl[] = "0123456789abcdef";
+ uint32 vec_len = svcntb();
+ svuint8_t hextbl_vec = svld1(svwhilelt_b8(0, 16), (uint8 *) hextbl);
+ svbool_t pred = svptrue_b8();
+ size_t loop_bytes = len & ~(2 * vec_len - 1); /* process 2 * vec_len byte chunk each iteration */
+ svuint8_t bytes, high, low;
+ svuint8x2_t zipped;
+
+ for (size_t i = 0; i < loop_bytes; i += 2 * vec_len)
+ {
+ bytes = svld1(pred, (uint8 *) src);
+
+ /* Right-shift to obtain the high nibble */
+ high = svlsr_x(pred, bytes, 4);
+
+ /* Mask the high nibble to obtain the low nibble */
+ low = svand_z(pred, bytes, 0xF);
+
+ /*
+ * Convert the high and low nibbles to hexadecimal digits using a
+ * vectorized table lookup and zip (interleave) the hexadecimal digits.
+ */
+ zipped = svcreate2(svtbl(hextbl_vec, high), svtbl(hextbl_vec, low));
+ svst2(pred, (uint8 *) dst, zipped);
+
+ dst += 2 * vec_len;
+ src += vec_len;
+
+ /* unrolled */
+ bytes = svld1(pred, (uint8 *) src);
+ high = svlsr_x(pred, bytes, 4);
+ low = svand_z(pred, bytes, 0xF);
+
+ zipped = svcreate2(svtbl(hextbl_vec, high), svtbl(hextbl_vec, low));
+ svst2(pred, (uint8 *) dst, zipped);
+
+ dst += 2 * vec_len;
+ src += vec_len;
+ }
+
+ /* process remaining tail bytes */
+ for (size_t i = loop_bytes; i < len; i += vec_len)
+ {
+ pred = svwhilelt_b8((uint64) i, (uint64) len);
+ bytes = svld1(pred, (uint8 *) src);
+ high = svlsr_x(pred, bytes, 4);
+ low = svand_z(pred, bytes, 0xF);
+
+ zipped = svcreate2(svtbl(hextbl_vec, high), svtbl(hextbl_vec, low));
+ svst2(pred, (uint8 *) dst, zipped);
+
+ dst += 2 * vec_len;
+ src += vec_len;
+ }
+
+ return (uint64) len * 2;
+}
+
+/*
+ * get_hex_sve
+ * Returns true if the hexadecimal digits are successfully converted
+ * to nibbles and stored in 'res'; otherwise, returns false.
+ */
+pg_attribute_target("arch=armv8-a+sve")
+static inline bool
+get_hex_sve(svbool_t pred, svuint8_t vec, svuint8_t *res)
+{
+ /*
+ * Convert ASCII of '0'-'9' to integers 0-9 by subtracting 48 (ASCII of '0').
+ * Similarly, convert letters 'A'–'F' and 'a'–'f' to integers 10–15 by
+ * subtracting 55 ('A' - 10) and 87 ('a' - 10).
+ */
+ svuint8_t digit = svsub_x(pred, vec, '0'),
+ upper = svsub_x(pred, vec, 'A' - 10),
+ lower = svsub_x(pred, vec, 'a' - 10);
+
+ /*
+ * Identify valid values in digits, upper, and lower vectors.
+ * Values 0-9 are valid in digits, while values 10-15 are valid
+ * in upper and lower.
+ *
+ * Example:
+ * vec: '0' '9' 'A' 'F' 'a' 'f'
+ * vec (in ASCII): 48 57 65 70 97 102
+ *
+ * digit: 0 9 17 22 49 54
+ * valid_digit: 1 1 0 0 0 0
+ *
+ * upper: 249 2 10 15 42 47
+ * valid_upper: 0 1 1 1 0 0
+ *
+ * lower: 217 226 234 239 10 15
+ *
+ * Note that values 0-9 are also marked valid in valid_upper, this will be
+ * handled later.
+ */
+ svbool_t valid_digit = svcmplt(pred, digit, 10),
+ valid_upper = svcmplt(pred, upper, 16);
+
+ /*
+ * Merge upper and lower vector using the logic: take the element from
+ * upper if it's true in valid_upper else pick the element in lower
+ *
+ * Mark the valid range i.e. 10-15 in letter vector
+ *
+ * letter: 217 2 10 15 10 15
+ * valid_letter: 0 0 1 1 1 1
+ */
+
+ svuint8_t letter = svsel(valid_upper, upper, lower);
+ svbool_t valid_letter = svand_z(pred, svcmpgt(pred, letter, 9),
+ svcmplt(pred, letter, 16));
+
+ /*
+ * Check for invalid hexadecimal digit. Each value must fall within
+ * the range 0-9 (true in valid_digit) or 10-15 (true in valid_letter) i.e.
+ * the OR of valid_digit and valid_letter should be all true.
+ */
+
+ if (svptest_any(pred, svnot_z(pred, svorr_z(pred, valid_digit, valid_letter))))
+ return false;
+
+ /*
+ * Finally, combine digit and letter vectors using the logic:
+ * take the element from digit if it's true in valid_digit else pick the
+ * element in letter.
+ *
+ * res: 0 9 10 15 10 15
+ */
+
+ *res = svsel(valid_digit, digit, letter);
+ return true;
+}
+
+uint64
+hex_decode_sve(const char *src, size_t len, char *dst)
+{
+ return hex_decode_safe_sve(src, len, dst, NULL);
+}
+
+pg_attribute_target("arch=armv8-a+sve")
+uint64
+hex_decode_safe_sve(const char *src, size_t len, char *dst, Node *escontext)
+{
+ uint32 vec_len = svcntb();
+ size_t loop_bytes = len & ~(2 * vec_len - 1); /* process 2 * vec_len byte chunk each iteration */
+ svbool_t pred = svptrue_b8();
+ const char *p = dst;
+
+ for (size_t i = 0; i < loop_bytes; i += 2 * vec_len)
+ {
+ svuint8x2_t bytes = svld2(pred, (uint8 *) src);
+ svuint8_t high = svget2(bytes, 0),
+ low = svget2(bytes, 1);
+
+ /* fallback for characters with ASCII values below '0' */
+ if (svptest_any(pred, svorr_z(pred, svcmplt(pred, high, '0'), svcmplt(pred, low, '0'))))
+ break;
+
+ /* fallback if an invalid hexadecimal digit is found */
+ if (!get_hex_sve(pred, high, &high) || !get_hex_sve(pred, low, &low))
+ break;
+
+ /* form the byte by left-shifting the high nibble and OR-ing it with the low nibble */
+ svst1(pred, (uint8 *) dst, svorr_z(pred, svlsl_x(pred, high, 4), low));
+
+ src += 2 * vec_len;
+ dst += vec_len;
+ }
+
+ if (len > loop_bytes) /* fallback */
+ return dst - p + hex_decode_safe_scalar(src, len - loop_bytes, dst, escontext);
+
+ return dst - p;
+}
+
+#endif /* USE_SVE_HEX_WITH_RUNTIME_CHECK */
diff --git a/src/backend/utils/adt/meson.build b/src/backend/utils/adt/meson.build
index 244f48f4fd7..ea88dd77390 100644
--- a/src/backend/utils/adt/meson.build
+++ b/src/backend/utils/adt/meson.build
@@ -21,6 +21,7 @@ backend_sources += files(
'dbsize.c',
'domains.c',
'encode.c',
+ 'encode_aarch64.c',
'enum.c',
'expandeddatum.c',
'expandedrecord.c',
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 726a7c1be1f..7a227f1875f 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -675,6 +675,9 @@
/* Define to 1 to use AVX-512 popcount instructions with a runtime check. */
#undef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
+/* Define to 1 to use SVE instructions for hex coding with a runtime check. */
+#undef USE_SVE_HEX_WITH_RUNTIME_CHECK
+
/* Define to 1 to build with Bonjour support. (--with-bonjour) */
#undef USE_BONJOUR
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 1c98c7d2255..2f72d8df9d1 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -35,11 +35,56 @@ extern int errdatatype(Oid datatypeOid);
extern int errdomainconstraint(Oid datatypeOid, const char *conname);
/* encode.c */
-extern uint64 hex_encode(const char *src, size_t len, char *dst);
-extern uint64 hex_decode(const char *src, size_t len, char *dst);
-extern uint64 hex_decode_safe(const char *src, size_t len, char *dst,
+extern uint64 hex_encode_scalar(const char *src, size_t len, char *dst);
+extern uint64 hex_decode_scalar(const char *src, size_t len, char *dst);
+extern uint64 hex_decode_safe_scalar(const char *src, size_t len, char *dst,
Node *escontext);
+/*
+ * On AArch64, we can try to use an SVE optimized hex encode/decode on some systems.
+ */
+#ifdef USE_SVE_HEX_WITH_RUNTIME_CHECK
+extern PGDLLIMPORT uint64 (*hex_encode_optimized) (const char *src, size_t len, char *dst);
+extern PGDLLIMPORT uint64 (*hex_decode_optimized) (const char *src, size_t len, char *dst);
+extern PGDLLIMPORT uint64 (*hex_decode_safe_optimized) (const char *src, size_t len, char *dst, Node *escontext);
+#endif
+
+static inline uint64
+hex_encode(const char *src, size_t len, char *dst)
+{
+#ifdef USE_SVE_HEX_WITH_RUNTIME_CHECK
+ int threshold = 16;
+
+ if (len >= threshold)
+ return hex_encode_optimized(src, len, dst);
+#endif
+ return hex_encode_scalar(src, len, dst);
+}
+
+static inline uint64
+hex_decode(const char *src, size_t len, char *dst)
+{
+#ifdef USE_SVE_HEX_WITH_RUNTIME_CHECK
+ int threshold = 32;
+
+ if (len >= threshold)
+ return hex_decode_optimized(src, len, dst);
+#endif
+ return hex_decode_scalar(src, len, dst);
+}
+
+static inline uint64
+hex_decode_safe(const char *src, size_t len, char *dst, Node *escontext)
+{
+#ifdef USE_SVE_HEX_WITH_RUNTIME_CHECK
+ int threshold = 32;
+
+ if (len >= threshold)
+ return hex_decode_safe_optimized(src, len, dst, escontext);
+#endif
+ return hex_decode_safe_scalar(src, len, dst, escontext);
+}
+
/* int.c */
extern int2vector *buildint2vector(const int16 *int2s, int n);
--
2.34.1
^ permalink raw reply [nested|flat] 30+ messages in thread
* Re: [PATCH] Hex-coding optimizations using SVE on ARM.
2025-06-04 13:47 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-06-09 09:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
@ 2025-07-07 10:41 ` [email protected] <[email protected]>
2025-09-03 11:11 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
0 siblings, 1 reply; 30+ messages in thread
From: [email protected] @ 2025-07-07 10:41 UTC (permalink / raw)
To: Nathan Bossart <[email protected]>; +Cc: Tom Lane <[email protected]>; David Rowley <[email protected]>; John Naylor <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>
Attaching the rebased patch, some regression tests for SIMD hex-coding,
and a script to test bytea performance (usage info in the script).
The results obtained using the script on an m7g.4xlarge are shown below.
Read Operation
table (MB) | HEAD (ms) | SVE (ms) | improvement (%)
---------------------------------------------------
52 | 136 | 111 | 18.38
105 | 215 | 164 | 23.72
209 | 452 | 331 | 26.76
419 | 830 | 602 | 27.46
Write Operation - table size after write
table (MB) | HEAD (ms) | SVE (ms) | improvement (%)
---------------------------------------------------
52 | 1430 | 1361 | 4.82
105 | 2956 | 2816 | 4.73
The bytea write numbers are averaged over 7 runs, with the table
truncated and vacuumed after each run.
--------
Chiranmoy
Attachments:
[text/x-python] bytea_test.py (2.4K, ../../OS9PR01MB15185626CA7C6EAF82C93ABA3974FA@OS9PR01MB15185.jpnprd01.prod.outlook.com/3-bytea_test.py)
download | inline:
from time import time
from sys import argv
import psycopg2
# import psycopg2.extras
"""
Usage
Set the input_path, output_path and conn_params in the main guard below.
To create the test table
python3 bytea_test.py c
To insert a file in the input_path N times
python3 bytea_test.py w <N>
To read the data from the table and write the first file to output_path
python3 bytea_test.py r
"""
def read_binary(input_path):
with open(input_path, 'rb') as f:
return f.read()
def create_table(conn_params):
conn = psycopg2.connect(**conn_params)
cursor = conn.cursor()
cursor.execute("""CREATE TABLE IF NOT EXISTS BYTEA_TABLE (data BYTEA);""",)
conn.commit()
cursor.close()
conn.close()
def save_file(binary_data, duplicate, conn_params):
conn = psycopg2.connect(**conn_params)
cursor = conn.cursor()
data = psycopg2.Binary(binary_data)
# rows = [(data,) for _ in range(duplicate)]
# start = time()
# psycopg2.extras.execute_values(cursor, """INSERT INTO bytea_table (data) VALUES %s;""", rows)
# print(time()-start)
start = time()
for _ in range(duplicate):
cursor.execute("""INSERT INTO bytea_table (data) VALUES (%s);""", (data,))
print(time()-start)
conn.commit()
cursor.close()
conn.close()
print(f"file saved {duplicate} times")
def retrieve_file(output_path, conn_params):
conn = psycopg2.connect(**conn_params)
cursor = conn.cursor()
start = time()
cursor.execute("""SELECT data FROM bytea_table;""")
print(time()-start)
result = cursor.fetchone()
if result:
binary_data = result[0]
with open(output_path, 'wb') as f:
f.write(binary_data)
print(f"File retrieved, saved to '{output_path}'")
else:
print(f"ERROR")
cursor.close()
conn.close()
if __name__ == '__main__':
input_path = 'INPUT/PATH/file.jpg'
output_path = 'OUTPUT/PATCH/retrieved.jpg'
conn_params = {
'dbname': '',
'user': '',
'password': '',
'host': '',
'port': 0
}
if len(argv) == 1:
print("Specify Mode")
elif argv[1].lower() == "c":
create_table(conn_params)
elif argv[1].lower() == "r":
retrieve_file(output_path, conn_params)
elif argv[1].lower() == 'w' and int(argv[2]) > 0:
save_file(read_binary(input_path), int(argv[2]), conn_params)
else:
print("Invalid Mode")
[application/octet-stream] v1-0001-hex-coding-regress-test.patch (7.4K, ../../OS9PR01MB15185626CA7C6EAF82C93ABA3974FA@OS9PR01MB15185.jpnprd01.prod.outlook.com/4-v1-0001-hex-coding-regress-test.patch)
download | inline diff:
From 9066e3296160af4e703f90f460f8e75471b6425d Mon Sep 17 00:00:00 2001
From: Chiranmoy Bhattacharya <[email protected]>
Date: Sun, 6 Jul 2025 19:25:28 +0530
Subject: [PATCH v1] hex coding regress test
---
src/test/regress/expected/hex_coding.out | 63 ++++++++++++++++++++++++
src/test/regress/parallel_schedule | 5 ++
src/test/regress/sql/hex_coding.sql | 39 +++++++++++++++
3 files changed, 107 insertions(+)
create mode 100644 src/test/regress/expected/hex_coding.out
create mode 100644 src/test/regress/sql/hex_coding.sql
diff --git a/src/test/regress/expected/hex_coding.out b/src/test/regress/expected/hex_coding.out
new file mode 100644
index 00000000000..e6d78fa4876
--- /dev/null
+++ b/src/test/regress/expected/hex_coding.out
@@ -0,0 +1,63 @@
+--
+-- tests for hex_encode and hex_decode in encode.c
+--
+-- Build table for testing
+CREATE TABLE BYTEA_TABLE(data BYTEA);
+-- hex_decode is used for inserting into bytea column
+-- Set bytea_output to hex so that hex_encode is used and tested
+SET bytea_output = 'hex';
+INSERT INTO BYTEA_TABLE VALUES ('\xAB');
+INSERT INTO BYTEA_TABLE VALUES ('\x01ab');
+INSERT INTO BYTEA_TABLE VALUES ('\xDEADC0DE');
+INSERT INTO BYTEA_TABLE VALUES ('\xbaadf00d');
+INSERT INTO BYTEA_TABLE VALUES ('\x C001 c0ffee '); -- hex string with whitespaces
+-- errors checking
+INSERT INTO BYTEA_TABLE VALUES ('\xbadf00d'); -- odd number of hex digits
+ERROR: invalid hexadecimal data: odd number of digits
+LINE 1: INSERT INTO BYTEA_TABLE VALUES ('\xbadf00d');
+ ^
+INSERT INTO BYTEA_TABLE VALUES ('\xdeadcode'); -- invalid hexadecimal digit: "o"
+ERROR: invalid hexadecimal digit: "o"
+LINE 1: INSERT INTO BYTEA_TABLE VALUES ('\xdeadcode');
+ ^
+INSERT INTO BYTEA_TABLE VALUES ('\xC00LC0FFEE'); -- invalid hexadecimal digit: "L"
+ERROR: invalid hexadecimal digit: "L"
+LINE 1: INSERT INTO BYTEA_TABLE VALUES ('\xC00LC0FFEE');
+ ^
+INSERT INTO BYTEA_TABLE VALUES ('\xC00LC*DE'); -- invalid hexadecimal digit: "*"
+ERROR: invalid hexadecimal digit: "L"
+LINE 1: INSERT INTO BYTEA_TABLE VALUES ('\xC00LC*DE');
+ ^
+INSERT INTO BYTEA_TABLE VALUES ('\xbad f00d'); -- invalid hexadecimal digit: " "
+ERROR: invalid hexadecimal digit: " "
+LINE 1: INSERT INTO BYTEA_TABLE VALUES ('\xbad f00d');
+ ^
+-- long hex strings to test SIMD implementation
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 8))::bytea;
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 8) || repeat('baadf00d', 8))::bytea;
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 8) || ' ' || repeat('baad f00d', 8))::bytea; -- hex string with whitespaces
+-- errors checking for SIMD implementation
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 4) || 'badf00d' || repeat('DEADC0DE', 4))::bytea; -- odd number of hex digits
+ERROR: invalid hexadecimal data: odd number of digits
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 4) || 'baadfood'|| repeat('DEADC0DE', 4))::bytea; -- invalid hexadecimal digit: "o"
+ERROR: invalid hexadecimal digit: "o"
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 4) || 'C00LC0FFEE' || repeat('DEADC0DE', 4))::bytea; -- invalid hexadecimal digit: "L"
+ERROR: invalid hexadecimal digit: "L"
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 8) || 'C00LC*DE' || repeat('DEADC0DE', 4))::bytea; -- invalid hexadecimal digit: "*"
+ERROR: invalid hexadecimal digit: "L"
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 8) || 'bad f00d' || repeat('DEADC0DE', 4))::bytea; -- invalid hexadecimal digit: " "
+ERROR: invalid hexadecimal digit: " "
+SELECT encode(data, 'hex') FROM BYTEA_TABLE;
+ encode
+----------------------------------------------------------------------------------------------------------------------------------
+ ab
+ 01ab
+ deadc0de
+ baadf00d
+ c001c0ffee
+ deadc0dedeadc0dedeadc0dedeadc0dedeadc0dedeadc0dedeadc0dedeadc0de
+ deadc0dedeadc0dedeadc0dedeadc0dedeadc0dedeadc0dedeadc0dedeadc0debaadf00dbaadf00dbaadf00dbaadf00dbaadf00dbaadf00dbaadf00dbaadf00d
+ deadc0dedeadc0dedeadc0dedeadc0dedeadc0dedeadc0dedeadc0dedeadc0debaadf00dbaadf00dbaadf00dbaadf00dbaadf00dbaadf00dbaadf00dbaadf00d
+(8 rows)
+
+DROP TABLE BYTEA_TABLE;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index a424be2a6bf..8812d80d592 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -109,6 +109,11 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
# ----------
test: json jsonb json_encoding jsonpath jsonpath_encoding jsonb_jsonpath sqljson sqljson_queryfuncs sqljson_jsontable
+# ----------
+# Another group of parallel tests for hex encode/decode
+# ----------
+test: hex_coding
+
# ----------
# Another group of parallel tests
# with depends on create_misc
diff --git a/src/test/regress/sql/hex_coding.sql b/src/test/regress/sql/hex_coding.sql
new file mode 100644
index 00000000000..97c51b62e90
--- /dev/null
+++ b/src/test/regress/sql/hex_coding.sql
@@ -0,0 +1,39 @@
+--
+-- tests for hex_encode and hex_decode in encode.c
+--
+
+-- Build table for testing
+CREATE TABLE BYTEA_TABLE(data BYTEA);
+
+-- hex_decode is used for inserting into bytea column
+-- Set bytea_output to hex so that hex_encode is used and tested
+SET bytea_output = 'hex';
+
+INSERT INTO BYTEA_TABLE VALUES ('\xAB');
+INSERT INTO BYTEA_TABLE VALUES ('\x01ab');
+INSERT INTO BYTEA_TABLE VALUES ('\xDEADC0DE');
+INSERT INTO BYTEA_TABLE VALUES ('\xbaadf00d');
+INSERT INTO BYTEA_TABLE VALUES ('\x C001 c0ffee '); -- hex string with whitespaces
+
+-- errors checking
+INSERT INTO BYTEA_TABLE VALUES ('\xbadf00d'); -- odd number of hex digits
+INSERT INTO BYTEA_TABLE VALUES ('\xdeadcode'); -- invalid hexadecimal digit: "o"
+INSERT INTO BYTEA_TABLE VALUES ('\xC00LC0FFEE'); -- invalid hexadecimal digit: "L"
+INSERT INTO BYTEA_TABLE VALUES ('\xC00LC*DE'); -- invalid hexadecimal digit: "*"
+INSERT INTO BYTEA_TABLE VALUES ('\xbad f00d'); -- invalid hexadecimal digit: " "
+
+-- long hex strings to test SIMD implementation
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 8))::bytea;
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 8) || repeat('baadf00d', 8))::bytea;
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 8) || ' ' || repeat('baad f00d', 8))::bytea; -- hex string with whitespaces
+
+-- errors checking for SIMD implementation
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 4) || 'badf00d' || repeat('DEADC0DE', 4))::bytea; -- odd number of hex digits
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 4) || 'baadfood'|| repeat('DEADC0DE', 4))::bytea; -- invalid hexadecimal digit: "o"
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 4) || 'C00LC0FFEE' || repeat('DEADC0DE', 4))::bytea; -- invalid hexadecimal digit: "L"
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 8) || 'C00LC*DE' || repeat('DEADC0DE', 4))::bytea; -- invalid hexadecimal digit: "*"
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 8) || 'bad f00d' || repeat('DEADC0DE', 4))::bytea; -- invalid hexadecimal digit: " "
+
+SELECT encode(data, 'hex') FROM BYTEA_TABLE;
+
+DROP TABLE BYTEA_TABLE;
--
2.34.1
[application/octet-stream] v5-0001-SVE-support-for-hex-coding.patch (24.6K, ../../OS9PR01MB15185626CA7C6EAF82C93ABA3974FA@OS9PR01MB15185.jpnprd01.prod.outlook.com/5-v5-0001-SVE-support-for-hex-coding.patch)
download | inline diff:
From 5a9bc0e99f7ae102c11cc905cd6c4df6016c415d Mon Sep 17 00:00:00 2001
From: Chiranmoy Bhattacharya <[email protected]>
Date: Sun, 6 Jul 2025 19:35:46 +0530
Subject: [PATCH v5] SVE support for hex coding
---
config/c-compiler.m4 | 85 ++++++++
configure | 104 +++++++++
configure.ac | 9 +
meson.build | 81 +++++++
src/backend/utils/adt/Makefile | 1 +
src/backend/utils/adt/encode.c | 6 +-
src/backend/utils/adt/encode_aarch64.c | 280 +++++++++++++++++++++++++
src/backend/utils/adt/meson.build | 1 +
src/include/pg_config.h.in | 3 +
src/include/utils/builtins.h | 51 ++++-
10 files changed, 615 insertions(+), 6 deletions(-)
create mode 100644 src/backend/utils/adt/encode_aarch64.c
diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index da40bd6a647..73d12826698 100644
--- a/config/c-compiler.m4
+++ b/config/c-compiler.m4
@@ -798,3 +798,88 @@ if test x"$Ac_cachevar" = x"yes"; then
fi
undefine([Ac_cachevar])dnl
])# PGAC_SVE_POPCNT_INTRINSICS
+
+# PGAC_ARM_SVE_HEX_INTRINSICS
+# ------------------------------
+# Check if the compiler supports the SVE intrinsic required for hex coding:
+# svsub_x, svcmplt, svsel, svcmpgt, svtbl, svlsr_x, svand_z, svcreate2,
+# svptest_any, svnot_z, svorr_z, svcntb, svld1, svwhilelt_b8, svst2, svld2,
+# svget2, svst1 and svlsl_x.
+#
+# If the intrinsics are supported, sets pgac_arm_sve_hex_intrinsics.
+AC_DEFUN([PGAC_ARM_SVE_HEX_INTRINSICS],
+[define([Ac_cachevar], [AS_TR_SH([pgac_cv_arm_sve_hex_intrinsics])])dnl
+AC_CACHE_CHECK([for svtbl, svlsr_x, svand_z, svcreate2, etc], [Ac_cachevar],
+[AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <arm_sve.h>
+
+ char input@<:@64@:>@;
+ char output@<:@128@:>@;
+
+ #if defined(__has_attribute) && __has_attribute (target)
+ __attribute__((target("arch=armv8-a+sve")))
+ #endif
+ int get_hex_sve(svbool_t pred, svuint8_t vec, svuint8_t *res)
+ {
+ svuint8_t digit = svsub_x(pred, vec, 48),
+ upper = svsub_x(pred, vec, 55),
+ lower = svsub_x(pred, vec, 87);
+ svbool_t valid_digit = svcmplt(pred, digit, 10),
+ valid_upper = svcmplt(pred, upper, 16);
+ svuint8_t letter = svsel(valid_upper, upper, lower);
+ svbool_t valid_letter = svand_z(pred, svcmpgt(pred, letter, 9),
+ svcmplt(pred, letter, 16));
+ if (svptest_any(pred, svnot_z(pred, svorr_z(pred, valid_digit, valid_letter))))
+ return 0;
+ *res = svsel(valid_digit, digit, letter);
+ return 1;
+ }
+
+ #if defined(__has_attribute) && __has_attribute (target)
+ __attribute__((target("arch=armv8-a+sve")))
+ #endif
+ static int hex_coding_test(void)
+ {
+ int len = 64, vec_len = svcntb(), vec_len_x2 = svcntb() * 2;
+ const char *hextbl = "0123456789abcdef";
+ svuint8_t hextbl_vec = svld1(svwhilelt_b8(0, 16), (uint8_t *) hextbl);
+ char *src = input, *dst = output;
+
+ /* hex encode */
+ for (uint64_t i = 0; i < 64; i += vec_len, dst += 2 * vec_len, src += vec_len)
+ {
+ svbool_t pred = svwhilelt_b8((uint64_t) i, (uint64_t) len);
+ svuint8_t bytes = svld1(pred, (uint8_t *) src),
+ high = svlsr_x(pred, bytes, 4),
+ low = svand_z(pred, bytes, 0xF);
+ svuint8x2_t merged = svcreate2(svtbl(hextbl_vec, high), svtbl(hextbl_vec, low));
+ svst2(pred, (uint8_t *) dst, merged);
+ }
+
+ /* hex decode */
+ len = 128;
+
+ for (int i; i < len; i += vec_len_x2)
+ {
+ svbool_t pred = svwhilelt_b8((uint64_t) i / 2, (uint64_t) len / 2);
+ svuint8x2_t bytes = svld2(pred, (uint8_t *) src + i);
+ svuint8_t high = svget2(bytes, 0), low = svget2(bytes, 1);
+
+ if (svptest_any(pred, svorr_z(pred, svcmplt(pred, high, '0'), svcmplt(pred, low, '0'))))
+ break;
+ if (!get_hex_sve(pred, high, &high) || !get_hex_sve(pred, low, &low))
+ break;
+
+ svst1(pred, (uint8_t *) dst + i / 2, svorr_z(pred, svlsl_x(pred, high, 4), low));
+ }
+
+ /* return computed value, to prevent the above being optimized away */
+ return output@<:@0@:>@;
+ }],
+ [return hex_coding_test();])],
+ [Ac_cachevar=yes],
+ [Ac_cachevar=no])])
+if test x"$Ac_cachevar" = x"yes"; then
+ pgac_arm_sve_hex_intrinsics=yes
+fi
+undefine([Ac_cachevar])dnl
+])# PGAC_ARM_SVE_HEX_INTRINSICS
diff --git a/configure b/configure
index 16ef5b58d1a..df78a5408d3 100755
--- a/configure
+++ b/configure
@@ -17851,6 +17851,110 @@ $as_echo "#define USE_SVE_POPCNT_WITH_RUNTIME_CHECK 1" >>confdefs.h
fi
fi
+# Check for ARM SVE intrinsics for hex coding
+#
+if test x"$host_cpu" = x"aarch64"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for svtbl, svlsr_x, svand_z, svcreate2, etc" >&5
+$as_echo_n "checking for svtbl, svlsr_x, svand_z, svcreate2, etc... " >&6; }
+if ${pgac_cv_arm_sve_hex_intrinsics+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <arm_sve.h>
+
+ char input[64];
+ char output[128];
+
+ #if defined(__has_attribute) && __has_attribute (target)
+ __attribute__((target("arch=armv8-a+sve")))
+ #endif
+ int get_hex_sve(svbool_t pred, svuint8_t vec, svuint8_t *res)
+ {
+ svuint8_t digit = svsub_x(pred, vec, 48),
+ upper = svsub_x(pred, vec, 55),
+ lower = svsub_x(pred, vec, 87);
+ svbool_t valid_digit = svcmplt(pred, digit, 10),
+ valid_upper = svcmplt(pred, upper, 16);
+ svuint8_t letter = svsel(valid_upper, upper, lower);
+ svbool_t valid_letter = svand_z(pred, svcmpgt(pred, letter, 9),
+ svcmplt(pred, letter, 16));
+ if (svptest_any(pred, svnot_z(pred, svorr_z(pred, valid_digit, valid_letter))))
+ return 0;
+ *res = svsel(valid_digit, digit, letter);
+ return 1;
+ }
+
+ #if defined(__has_attribute) && __has_attribute (target)
+ __attribute__((target("arch=armv8-a+sve")))
+ #endif
+ static int hex_coding_test(void)
+ {
+ int len = 64, vec_len = svcntb(), vec_len_x2 = svcntb() * 2;
+ const char *hextbl = "0123456789abcdef";
+ svuint8_t hextbl_vec = svld1(svwhilelt_b8(0, 16), (uint8_t *) hextbl);
+ char *src = input, *dst = output;
+
+ /* hex encode */
+ for (uint64_t i = 0; i < 64; i += vec_len, dst += 2 * vec_len, src += vec_len)
+ {
+ svbool_t pred = svwhilelt_b8((uint64_t) i, (uint64_t) len);
+ svuint8_t bytes = svld1(pred, (uint8_t *) src),
+ high = svlsr_x(pred, bytes, 4),
+ low = svand_z(pred, bytes, 0xF);
+ svuint8x2_t merged = svcreate2(svtbl(hextbl_vec, high), svtbl(hextbl_vec, low));
+ svst2(pred, (uint8_t *) dst, merged);
+ }
+
+ /* hex decode */
+ len = 128;
+
+ for (int i; i < len; i += vec_len_x2)
+ {
+ svbool_t pred = svwhilelt_b8((uint64_t) i / 2, (uint64_t) len / 2);
+ svuint8x2_t bytes = svld2(pred, (uint8_t *) src + i);
+ svuint8_t high = svget2(bytes, 0), low = svget2(bytes, 1);
+
+ if (svptest_any(pred, svorr_z(pred, svcmplt(pred, high, '0'), svcmplt(pred, low, '0'))))
+ break;
+ if (!get_hex_sve(pred, high, &high) || !get_hex_sve(pred, low, &low))
+ break;
+
+ svst1(pred, (uint8_t *) dst + i / 2, svorr_z(pred, svlsl_x(pred, high, 4), low));
+ }
+
+ /* return computed value, to prevent the above being optimized away */
+ return output[0];
+ }
+int
+main ()
+{
+return hex_coding_test();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ pgac_cv_arm_sve_hex_intrinsics=yes
+else
+ pgac_cv_arm_sve_hex_intrinsics=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_arm_sve_hex_intrinsics" >&5
+$as_echo "$pgac_cv_arm_sve_hex_intrinsics" >&6; }
+if test x"$pgac_cv_arm_sve_hex_intrinsics" = x"yes"; then
+ pgac_arm_sve_hex_intrinsics=yes
+fi
+
+ if test x"$pgac_arm_sve_hex_intrinsics" = x"yes"; then
+
+$as_echo "#define USE_SVE_HEX_WITH_RUNTIME_CHECK 1" >>confdefs.h
+
+ fi
+fi
+
# Check for Intel SSE 4.2 intrinsics to do CRC calculations.
#
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for _mm_crc32_u8 and _mm_crc32_u32" >&5
diff --git a/configure.ac b/configure.ac
index b3efc49c97a..ce0015bb543 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2107,6 +2107,15 @@ if test x"$host_cpu" = x"aarch64"; then
fi
fi
+# Check for ARM SVE intrinsics for hex coding
+#
+if test x"$host_cpu" = x"aarch64"; then
+ PGAC_ARM_SVE_HEX_INTRINSICS()
+ if test x"$pgac_arm_sve_hex_intrinsics" = x"yes"; then
+ AC_DEFINE(USE_SVE_HEX_WITH_RUNTIME_CHECK, 1, [Define to 1 to use ARM SVE intrinsic for hex coding.])
+ fi
+fi
+
# Check for Intel SSE 4.2 intrinsics to do CRC calculations.
#
PGAC_SSE42_CRC32_INTRINSICS()
diff --git a/meson.build b/meson.build
index a97854a947d..68700a4bba0 100644
--- a/meson.build
+++ b/meson.build
@@ -2389,6 +2389,87 @@ int main(void)
endif
+###############################################################
+# Check the availability of SVE intrinsics for hex coding.
+###############################################################
+
+if host_cpu == 'aarch64'
+
+ prog = '''
+#include <arm_sve.h>
+
+char input[64];
+char output[128];
+
+#if defined(__has_attribute) && __has_attribute (target)
+ __attribute__((target("arch=armv8-a+sve")))
+#endif
+int get_hex_sve(svbool_t pred, svuint8_t vec, svuint8_t *res)
+{
+ svuint8_t digit = svsub_x(pred, vec, 48),
+ upper = svsub_x(pred, vec, 55),
+ lower = svsub_x(pred, vec, 87);
+ svbool_t valid_digit = svcmplt(pred, digit, 10),
+ valid_upper = svcmplt(pred, upper, 16);
+ svuint8_t letter = svsel(valid_upper, upper, lower);
+ svbool_t valid_letter = svand_z(pred, svcmpgt(pred, letter, 9),
+ svcmplt(pred, letter, 16));
+ if (svptest_any(pred, svnot_z(pred, svorr_z(pred, valid_digit, valid_letter))))
+ return 0;
+ *res = svsel(valid_digit, digit, letter);
+ return 1;
+}
+
+#if defined(__has_attribute) && __has_attribute (target)
+ __attribute__((target("arch=armv8-a+sve")))
+#endif
+int main(void)
+{
+ int len = 64, vec_len = svcntb(), vec_len_x2 = svcntb() * 2;
+ const char hextbl[] = "0123456789abcdef";
+ svuint8_t hextbl_vec = svld1(svwhilelt_b8(0, 16), (uint8_t *) hextbl);
+ char *src = input, *dst = output;
+
+ /* hex encode */
+ for (uint64_t i = 0; i < 64; i += vec_len, dst += 2 * vec_len, src += vec_len)
+ {
+ svbool_t pred = svwhilelt_b8((uint64_t) i, (uint64_t) len);
+ svuint8_t bytes = svld1(pred, (uint8_t *) src),
+ high = svlsr_x(pred, bytes, 4),
+ low = svand_z(pred, bytes, 0xF);
+ svuint8x2_t merged = svcreate2(svtbl(hextbl_vec, high), svtbl(hextbl_vec, low));
+ svst2(pred, (uint8_t *) dst, merged);
+ }
+
+ /* hex decode */
+ len = 128;
+
+ for (int i; i < len; i += vec_len_x2)
+ {
+ svbool_t pred = svwhilelt_b8((uint64_t) i / 2, (uint64_t) len / 2);
+ svuint8x2_t bytes = svld2(pred, (uint8_t *) src + i);
+ svuint8_t high = svget2(bytes, 0), low = svget2(bytes, 1);
+
+ if (svptest_any(pred, svorr_z(pred, svcmplt(pred, high, '0'), svcmplt(pred, low, '0'))))
+ break;
+ if (!get_hex_sve(pred, high, &high) || !get_hex_sve(pred, low, &low))
+ break;
+
+ svst1(pred, (uint8_t *) dst + i / 2, svorr_z(pred, svlsl_x(pred, high, 4), low));
+ }
+
+ /* return computed value, to prevent the above being optimized away */
+ return output[0];
+}
+'''
+
+ if cc.links(prog, name: 'SVE hex coding', args: test_c_args)
+ cdata.set('USE_SVE_HEX_WITH_RUNTIME_CHECK', 1)
+ endif
+
+endif
+
+
###############################################################
# Select CRC-32C implementation.
#
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index ffeacf2b819..d2fa03efe98 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -33,6 +33,7 @@ OBJS = \
dbsize.o \
domains.o \
encode.o \
+ encode_aarch64.o \
enum.o \
expandeddatum.o \
expandedrecord.o \
diff --git a/src/backend/utils/adt/encode.c b/src/backend/utils/adt/encode.c
index 4ccaed815d1..fa62ce3107d 100644
--- a/src/backend/utils/adt/encode.c
+++ b/src/backend/utils/adt/encode.c
@@ -178,7 +178,7 @@ static const int8 hexlookup[128] = {
};
uint64
-hex_encode(const char *src, size_t len, char *dst)
+hex_encode_scalar(const char *src, size_t len, char *dst)
{
const char *end = src + len;
@@ -208,13 +208,13 @@ get_hex(const char *cp, char *out)
}
uint64
-hex_decode(const char *src, size_t len, char *dst)
+hex_decode_scalar(const char *src, size_t len, char *dst)
{
return hex_decode_safe(src, len, dst, NULL);
}
uint64
-hex_decode_safe(const char *src, size_t len, char *dst, Node *escontext)
+hex_decode_safe_scalar(const char *src, size_t len, char *dst, Node *escontext)
{
const char *s,
*srcend;
diff --git a/src/backend/utils/adt/encode_aarch64.c b/src/backend/utils/adt/encode_aarch64.c
new file mode 100644
index 00000000000..bf8157900f8
--- /dev/null
+++ b/src/backend/utils/adt/encode_aarch64.c
@@ -0,0 +1,280 @@
+/*-------------------------------------------------------------------------
+ *
+ * encode_aarch64.c
+ * Holds the SVE hex encode/decode implementations.
+ *
+ * Copyright (c) 2025, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/encode_aarch64.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <c.h>
+
+#include "utils/builtins.h"
+
+#ifdef USE_SVE_HEX_WITH_RUNTIME_CHECK
+#include <arm_sve.h>
+
+#if defined(HAVE_ELF_AUX_INFO) || defined(HAVE_GETAUXVAL)
+#include <sys/auxv.h>
+#endif
+
+/*
+ * These are the SVE implementations of the hex encode/decode functions.
+ */
+static uint64 hex_encode_sve(const char *src, size_t len, char *dst);
+static uint64 hex_decode_sve(const char *src, size_t len, char *dst);
+static uint64 hex_decode_safe_sve(const char *src, size_t len, char *dst, Node *escontext);
+
+/*
+ * The function pointers are initially set to "choose" functions. These
+ * functions will first set the pointers to the right implementations (based on
+ * what the current CPU supports) and then will call the pointer to fulfill the
+ * caller's request.
+ */
+
+static uint64 hex_encode_choose(const char *src, size_t len, char *dst);
+static uint64 hex_decode_choose(const char *src, size_t len, char *dst);
+static uint64 hex_decode_safe_choose(const char *src, size_t len, char *dst, Node *escontext);
+uint64 (*hex_encode_optimized) (const char *src, size_t len, char *dst) = hex_encode_choose;
+uint64 (*hex_decode_optimized) (const char *src, size_t len, char *dst) = hex_decode_choose;
+uint64 (*hex_decode_safe_optimized) (const char *src, size_t len, char *dst, Node *escontext) = hex_decode_safe_choose;
+
+static inline bool
+check_sve_support(void)
+{
+#ifdef HAVE_ELF_AUX_INFO
+ unsigned long value;
+
+ return elf_aux_info(AT_HWCAP, &value, sizeof(value)) == 0 &&
+ (value & HWCAP_SVE) != 0;
+#elif defined(HAVE_GETAUXVAL)
+ return (getauxval(AT_HWCAP) & HWCAP_SVE) != 0;
+#else
+ return false;
+#endif
+}
+
+static inline void
+choose_hex_functions(void)
+{
+ if (check_sve_support())
+ {
+ hex_encode_optimized = hex_encode_sve;
+ hex_decode_optimized = hex_decode_sve;
+ hex_decode_safe_optimized = hex_decode_safe_sve;
+ }
+ else
+ {
+ hex_encode_optimized = hex_encode_scalar;
+ hex_decode_optimized = hex_decode_scalar;
+ hex_decode_safe_optimized = hex_decode_safe_scalar;
+ }
+}
+
+static uint64
+hex_encode_choose(const char *src, size_t len, char *dst)
+{
+ choose_hex_functions();
+ return hex_encode_optimized(src, len, dst);
+}
+static uint64
+hex_decode_choose(const char *src, size_t len, char *dst)
+{
+ choose_hex_functions();
+ return hex_decode_optimized(src, len, dst);
+}
+static uint64
+hex_decode_safe_choose(const char *src, size_t len, char *dst, Node *escontext)
+{
+ choose_hex_functions();
+ return hex_decode_safe_optimized(src, len, dst, escontext);
+}
+
+pg_attribute_target("arch=armv8-a+sve")
+uint64
+hex_encode_sve(const char *src, size_t len, char *dst)
+{
+ const char hextbl[] = "0123456789abcdef";
+ uint32 vec_len = svcntb();
+ svuint8_t hextbl_vec = svld1(svwhilelt_b8(0, 16), (uint8 *) hextbl);
+ svbool_t pred = svptrue_b8();
+ size_t loop_bytes = len & ~(2 * vec_len - 1); /* process 2 * vec_len byte chunk each iteration */
+ svuint8_t bytes, high, low;
+ svuint8x2_t zipped;
+
+ for (size_t i = 0; i < loop_bytes; i += 2 * vec_len)
+ {
+ bytes = svld1(pred, (uint8 *) src);
+
+ /* Right-shift to obtain the high nibble */
+ high = svlsr_x(pred, bytes, 4);
+
+ /* Mask the high nibble to obtain the low nibble */
+ low = svand_z(pred, bytes, 0xF);
+
+ /*
+ * Convert the high and low nibbles to hexadecimal digits using a
+ * vectorized table lookup and zip (interleave) the hexadecimal digits.
+ */
+ zipped = svcreate2(svtbl(hextbl_vec, high), svtbl(hextbl_vec, low));
+ svst2(pred, (uint8 *) dst, zipped);
+
+ dst += 2 * vec_len;
+ src += vec_len;
+
+ /* unrolled */
+ bytes = svld1(pred, (uint8 *) src);
+ high = svlsr_x(pred, bytes, 4);
+ low = svand_z(pred, bytes, 0xF);
+
+ zipped = svcreate2(svtbl(hextbl_vec, high), svtbl(hextbl_vec, low));
+ svst2(pred, (uint8 *) dst, zipped);
+
+ dst += 2 * vec_len;
+ src += vec_len;
+ }
+
+ /* process remaining tail bytes */
+ for (size_t i = loop_bytes; i < len; i += vec_len)
+ {
+ pred = svwhilelt_b8((uint64) i, (uint64) len);
+ bytes = svld1(pred, (uint8 *) src);
+ high = svlsr_x(pred, bytes, 4);
+ low = svand_z(pred, bytes, 0xF);
+
+ zipped = svcreate2(svtbl(hextbl_vec, high), svtbl(hextbl_vec, low));
+ svst2(pred, (uint8 *) dst, zipped);
+
+ dst += 2 * vec_len;
+ src += vec_len;
+ }
+
+ return (uint64) len * 2;
+}
+
+/*
+ * get_hex_sve
+ * Returns true if the hexadecimal digits are successfully converted
+ * to nibbles and stored in 'res'; otherwise, returns false.
+ */
+pg_attribute_target("arch=armv8-a+sve")
+static inline bool
+get_hex_sve(svbool_t pred, svuint8_t vec, svuint8_t *res)
+{
+ /*
+ * Convert ASCII of '0'-'9' to integers 0-9 by subtracting 48 (ASCII of '0').
+ * Similarly, convert letters 'A'–'F' and 'a'–'f' to integers 10–15 by
+ * subtracting 55 ('A' - 10) and 87 ('a' - 10).
+ */
+ svuint8_t digit = svsub_x(pred, vec, '0'),
+ upper = svsub_x(pred, vec, 'A' - 10),
+ lower = svsub_x(pred, vec, 'a' - 10);
+
+ /*
+ * Identify valid values in digits, upper, and lower vectors.
+ * Values 0-9 are valid in digits, while values 10-15 are valid
+ * in upper and lower.
+ *
+ * Example:
+ * vec: '0' '9' 'A' 'F' 'a' 'f'
+ * vec (in ASCII): 48 57 65 70 97 102
+ *
+ * digit: 0 9 17 22 49 54
+ * valid_digit: 1 1 0 0 0 0
+ *
+ * upper: 249 2 10 15 42 47
+ * valid_upper: 0 1 1 1 0 0
+ *
+ * lower: 217 226 234 239 10 15
+ *
+ * Note that values 0-9 are also marked valid in valid_upper, this will be
+ * handled later.
+ */
+ svbool_t valid_digit = svcmplt(pred, digit, 10),
+ valid_upper = svcmplt(pred, upper, 16);
+
+ /*
+ * Merge upper and lower vector using the logic: take the element from
+ * upper if it's true in valid_upper else pick the element in lower
+ *
+ * Mark the valid range i.e. 10-15 in letter vector
+ *
+ * letter: 217 2 10 15 10 15
+ * valid_letter: 0 0 1 1 1 1
+ */
+
+ svuint8_t letter = svsel(valid_upper, upper, lower);
+ svbool_t valid_letter = svand_z(pred, svcmpgt(pred, letter, 9),
+ svcmplt(pred, letter, 16));
+
+ /*
+ * Check for invalid hexadecimal digit. Each value must fall within
+ * the range 0-9 (true in valid_digit) or 10-15 (true in valid_letter) i.e.
+ * the OR of valid_digit and valid_letter should be all true.
+ */
+
+ if (svptest_any(pred, svnot_z(pred, svorr_z(pred, valid_digit, valid_letter))))
+ return false;
+
+ /*
+ * Finally, combine digit and letter vectors using the logic:
+ * take the element from digit if it's true in valid_digit else pick the
+ * element in letter.
+ *
+ * res: 0 9 10 15 10 15
+ */
+
+ *res = svsel(valid_digit, digit, letter);
+ return true;
+}
+
+uint64
+hex_decode_sve(const char *src, size_t len, char *dst)
+{
+ return hex_decode_safe_sve(src, len, dst, NULL);
+}
+
+pg_attribute_target("arch=armv8-a+sve")
+uint64
+hex_decode_safe_sve(const char *src, size_t len, char *dst, Node *escontext)
+{
+ uint32 vec_len = svcntb();
+ size_t i = 0,
+ loop_bytes = len & ~(2 * vec_len - 1); /* process 2 * vec_len byte chunk each iteration */
+ svbool_t pred = svptrue_b8();
+ const char *p = dst;
+
+ while (i < loop_bytes)
+ {
+ svuint8x2_t bytes = svld2(pred, (uint8 *) src);
+ svuint8_t high = svget2(bytes, 0),
+ low = svget2(bytes, 1);
+
+ /* fallback for characters with ASCII values below '0' */
+ if (svptest_any(pred, svorr_z(pred, svcmplt(pred, high, '0'), svcmplt(pred, low, '0'))))
+ break;
+
+ /* fallback if an invalid hexadecimal digit is found */
+ if (!get_hex_sve(pred, high, &high) || !get_hex_sve(pred, low, &low))
+ break;
+
+ /* form the byte by left-shifting the high nibble and OR-ing it with the low nibble */
+ svst1(pred, (uint8 *) dst, svorr_z(pred, svlsl_x(pred, high, 4), low));
+
+ i += 2 * vec_len;
+ src += 2 * vec_len;
+ dst += vec_len;
+ }
+
+ if (len > i) /* fallback */
+ return dst - p + hex_decode_safe_scalar(src, len - i, dst, escontext);
+
+ return dst - p;
+}
+
+#endif /* USE_SVE_HEX_WITH_RUNTIME_CHECK */
diff --git a/src/backend/utils/adt/meson.build b/src/backend/utils/adt/meson.build
index ed9bbd7b926..094a9c7c013 100644
--- a/src/backend/utils/adt/meson.build
+++ b/src/backend/utils/adt/meson.build
@@ -22,6 +22,7 @@ backend_sources += files(
'dbsize.c',
'domains.c',
'encode.c',
+ 'encode_aarch64.c',
'enum.c',
'expandeddatum.c',
'expandedrecord.c',
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 726a7c1be1f..7a227f1875f 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -675,6 +675,9 @@
/* Define to 1 to use AVX-512 popcount instructions with a runtime check. */
#undef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
+/* Define to 1 to use SVE instructions for hex coding with a runtime check. */
+#undef USE_SVE_HEX_WITH_RUNTIME_CHECK
+
/* Define to 1 to build with Bonjour support. (--with-bonjour) */
#undef USE_BONJOUR
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 1c98c7d2255..2f72d8df9d1 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -35,11 +35,56 @@ extern int errdatatype(Oid datatypeOid);
extern int errdomainconstraint(Oid datatypeOid, const char *conname);
/* encode.c */
-extern uint64 hex_encode(const char *src, size_t len, char *dst);
-extern uint64 hex_decode(const char *src, size_t len, char *dst);
-extern uint64 hex_decode_safe(const char *src, size_t len, char *dst,
+extern uint64 hex_encode_scalar(const char *src, size_t len, char *dst);
+extern uint64 hex_decode_scalar(const char *src, size_t len, char *dst);
+extern uint64 hex_decode_safe_scalar(const char *src, size_t len, char *dst,
Node *escontext);
+/*
+ * On AArch64, we can try to use an SVE optimized hex encode/decode on some systems.
+ */
+#ifdef USE_SVE_HEX_WITH_RUNTIME_CHECK
+extern PGDLLIMPORT uint64 (*hex_encode_optimized) (const char *src, size_t len, char *dst);
+extern PGDLLIMPORT uint64 (*hex_decode_optimized) (const char *src, size_t len, char *dst);
+extern PGDLLIMPORT uint64 (*hex_decode_safe_optimized) (const char *src, size_t len, char *dst, Node *escontext);
+#endif
+
+static inline uint64
+hex_encode(const char *src, size_t len, char *dst)
+{
+#ifdef USE_SVE_HEX_WITH_RUNTIME_CHECK
+ int threshold = 16;
+
+ if (len >= threshold)
+ return hex_encode_optimized(src, len, dst);
+#endif
+ return hex_encode_scalar(src, len, dst);
+}
+
+static inline uint64
+hex_decode(const char *src, size_t len, char *dst)
+{
+#ifdef USE_SVE_HEX_WITH_RUNTIME_CHECK
+ int threshold = 32;
+
+ if (len >= threshold)
+ return hex_decode_optimized(src, len, dst);
+#endif
+ return hex_decode_scalar(src, len, dst);
+}
+
+static inline uint64
+hex_decode_safe(const char *src, size_t len, char *dst, Node *escontext)
+{
+#ifdef USE_SVE_HEX_WITH_RUNTIME_CHECK
+ int threshold = 32;
+
+ if (len >= threshold)
+ return hex_decode_safe_optimized(src, len, dst, escontext);
+#endif
+ return hex_decode_safe_scalar(src, len, dst, escontext);
+}
+
/* int.c */
extern int2vector *buildint2vector(const int16 *int2s, int n);
--
2.34.1
^ permalink raw reply [nested|flat] 30+ messages in thread
* Re: [PATCH] Hex-coding optimizations using SVE on ARM.
2025-06-04 13:47 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-06-09 09:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-07-07 10:41 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
@ 2025-09-03 11:11 ` [email protected] <[email protected]>
2025-09-03 14:48 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-04 06:20 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
0 siblings, 2 replies; 30+ messages in thread
From: [email protected] @ 2025-09-03 11:11 UTC (permalink / raw)
To: Nathan Bossart <[email protected]>; +Cc: Tom Lane <[email protected]>; David Rowley <[email protected]>; John Naylor <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>
Hi all,
Since the CommitFest is underway, could we get some feedback to improve the patch?
_______
Chiranmoy
^ permalink raw reply [nested|flat] 30+ messages in thread
* Re: [PATCH] Hex-coding optimizations using SVE on ARM.
2025-06-04 13:47 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-06-09 09:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-07-07 10:41 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-03 11:11 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
@ 2025-09-03 14:48 ` Nathan Bossart <[email protected]>
1 sibling, 0 replies; 30+ messages in thread
From: Nathan Bossart @ 2025-09-03 14:48 UTC (permalink / raw)
To: [email protected] <[email protected]>; +Cc: Tom Lane <[email protected]>; David Rowley <[email protected]>; John Naylor <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>
On Wed, Sep 03, 2025 at 11:11:24AM +0000, [email protected] wrote:
> Since the CommitFest is underway, could we get some feedback to improve
> the patch?
I see that there was some discussion about a Neon implementation upthread,
but I'm not sure we concluded anything. For popcount, we first added a
Neon version before adding the SVE version, which required more complicated
configure/runtime checks. Presumably Neon is available on more hardware
than SVE, so that could be a good place to start here, too.
Also, I'd strongly encourage you to get involved with others' patches on
the mailing lists (e.g., reviewing, testing). Patch submissions are great,
but this community depends on other types of participation, too. IME
helping others with their patches also tends to incentivize others to help
with yours.
--
nathan
^ permalink raw reply [nested|flat] 30+ messages in thread
* Re: [PATCH] Hex-coding optimizations using SVE on ARM.
2025-06-04 13:47 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-06-09 09:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-07-07 10:41 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-03 11:11 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
@ 2025-09-04 06:20 ` John Naylor <[email protected]>
2025-09-04 14:55 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
1 sibling, 1 reply; 30+ messages in thread
From: John Naylor @ 2025-09-04 06:20 UTC (permalink / raw)
To: [email protected] <[email protected]>; +Cc: Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; David Rowley <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>
On Wed, Sep 3, 2025 at 6:11 PM [email protected]
<[email protected]> wrote:
>
> Hi all,
>
> Since the CommitFest is underway, could we get some feedback to improve the patch?
On that note, I was hoping you could give us feedback on whether the
improvement in PG18 made any difference at all in your real-world
use-case, i.e. not just in a microbenchmark, but also including
transmission of the hex-encoded values across the network to the
client (that I assume must decode them again).
--
John Naylor
Amazon Web Services
^ permalink raw reply [nested|flat] 30+ messages in thread
* Re: [PATCH] Hex-coding optimizations using SVE on ARM.
2025-06-04 13:47 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-06-09 09:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-07-07 10:41 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-03 11:11 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-04 06:20 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
@ 2025-09-04 14:55 ` [email protected] <[email protected]>
2025-09-11 03:12 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
0 siblings, 1 reply; 30+ messages in thread
From: [email protected] @ 2025-09-04 14:55 UTC (permalink / raw)
To: John Naylor <[email protected]>; +Cc: Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; David Rowley <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>
> I see that there was some discussion about a Neon implementation upthread,
> but I'm not sure we concluded anything. For popcount, we first added a
> Neon version before adding the SVE version, which required more complicated
> configure/runtime checks. Presumably Neon is available on more hardware
> than SVE, so that could be a good place to start here, too.
We have added the Neon versions of hex encode/decode.
Here are the microbenchmark numbers.
hex_encode - m7g.4xlarge
Input | Head | Neon
-------+--------+--------
32 | 18.056 | 5.957
40 | 22.127 | 10.205
48 | 26.214 | 14.151
64 | 33.613 | 6.164
128 | 66.060 | 11.372
256 |130.225 | 18.543
512 |267.105 | 33.977
1024 |515.603 | 64.462
hex_decode - m7g.4xlarge
Input | Head | Neon
-------+--------+--------
32 | 26.669 | 9.462
40 | 36.320 | 19.347
48 | 45.971 | 19.099
64 | 58.468 | 17.648
128 |113.250 | 30.437
256 |218.743 | 56.824
512 |414.133 |107.212
1024 |828.493 |210.740
> Also, I'd strongly encourage you to get involved with others' patches on
> the mailing lists (e.g., reviewing, testing). Patch submissions are great,
> but this community depends on other types of participation, too. IME
> helping others with their patches also tends to incentivize others to help
> with yours.
Sure, we will try to test/review patches on areas we have experience.
> On that note, I was hoping you could give us feedback on whether the
> improvement in PG18 made any difference at all in your real-world
> use-case, i.e. not just in a microbenchmark, but also including
> transmission of the hex-encoded values across the network to the
> client (that I assume must decode them again).
Yes, the improvement in v18 did help, check the attached perf graphs.
We used a python script to send and receive binary data from postgres.
For simple select queries on a bytea column, hex_encode was taking
42% of the query execution time in v17, this was reduced to 33% in v18,
resulting in around 18% improvement in overall query time.
The proposed patch further reduces the hex_encode function usage to
5.6%, another 25% improvement in total query time.
We observed similar improvements for insert queries on the bytea column.
hex_decode usage decreased from 15.5% to 5.5%, a 5-8% query level
improvement depending on which storage type is used.
------
Chiranmoy
Attachments:
[application/octet-stream] v6-0001-NEON-support-for-hex-coding.patch (10.2K, ../../OS9PR01MB15185B278E343A9BA5F0F6AB19700A@OS9PR01MB15185.jpnprd01.prod.outlook.com/3-v6-0001-NEON-support-for-hex-coding.patch)
download | inline diff:
From e642b6d32d4715c988b6b93d57385a7c0779182d Mon Sep 17 00:00:00 2001
From: Chiranmoy Bhattacharya <[email protected]>
Date: Thu, 4 Sep 2025 12:23:24 +0530
Subject: [PATCH v6 1/3] NEON support for hex coding
---
src/backend/utils/adt/Makefile | 1 +
src/backend/utils/adt/encode.c | 6 +-
src/backend/utils/adt/encode_aarch64.c | 195 +++++++++++++++++++++++++
src/backend/utils/adt/meson.build | 1 +
src/include/utils/builtins.h | 57 +++++++-
5 files changed, 254 insertions(+), 6 deletions(-)
create mode 100644 src/backend/utils/adt/encode_aarch64.c
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index cc68ac545a5..40eaee14899 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -33,6 +33,7 @@ OBJS = \
dbsize.o \
domains.o \
encode.o \
+ encode_aarch64.o \
enum.o \
expandeddatum.o \
expandedrecord.o \
diff --git a/src/backend/utils/adt/encode.c b/src/backend/utils/adt/encode.c
index 4ccaed815d1..fa62ce3107d 100644
--- a/src/backend/utils/adt/encode.c
+++ b/src/backend/utils/adt/encode.c
@@ -178,7 +178,7 @@ static const int8 hexlookup[128] = {
};
uint64
-hex_encode(const char *src, size_t len, char *dst)
+hex_encode_scalar(const char *src, size_t len, char *dst)
{
const char *end = src + len;
@@ -208,13 +208,13 @@ get_hex(const char *cp, char *out)
}
uint64
-hex_decode(const char *src, size_t len, char *dst)
+hex_decode_scalar(const char *src, size_t len, char *dst)
{
return hex_decode_safe(src, len, dst, NULL);
}
uint64
-hex_decode_safe(const char *src, size_t len, char *dst, Node *escontext)
+hex_decode_safe_scalar(const char *src, size_t len, char *dst, Node *escontext)
{
const char *s,
*srcend;
diff --git a/src/backend/utils/adt/encode_aarch64.c b/src/backend/utils/adt/encode_aarch64.c
new file mode 100644
index 00000000000..7b0412dc255
--- /dev/null
+++ b/src/backend/utils/adt/encode_aarch64.c
@@ -0,0 +1,195 @@
+/*-------------------------------------------------------------------------
+ *
+ * encode_aarch64.c
+ * Holds the AArch64 hex encode/decode implementations.
+ *
+ * Copyright (c) 2025, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/encode_aarch64.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <c.h>
+
+#include "utils/builtins.h"
+
+#ifdef HEX_CODING_AARCH64
+
+#include <arm_neon.h>
+
+uint64
+hex_encode_optimized(const char *src, size_t len, char *dst)
+{
+ const char hextbl[] = "0123456789abcdef";
+ uint8x16_t hextbl_vec = vld1q_u8((uint8 *) hextbl);
+ uint8x16x2_t zipped;
+ uint32 vec_len = sizeof(uint8x16_t);
+ size_t chunks_len = len & ~(2 * vec_len - 1);
+
+ for (size_t i = 0; i < chunks_len; i += 2 * vec_len)
+ {
+ uint8x16_t bytes = vld1q_u8((uint8 *) src);
+
+ /* Right-shift by 4 to get the high nibble */
+ uint8x16_t high = vshrq_n_u8(bytes, 4);
+
+ /* Mask the high nibble to get the low nibble */
+ uint8x16_t low = vandq_u8(bytes, vdupq_n_u8(0xF));
+
+ /*
+ * Convert the high and low nibbles to hexadecimal digits using a table
+ * lookup, and then zip (interleave) the resulting digits.
+ */
+ zipped.val[0] = vqtbl1q_u8(hextbl_vec, high);
+ zipped.val[1] = vqtbl1q_u8(hextbl_vec, low);
+ vst2q_u8((uint8 *) dst, zipped);
+
+ src += vec_len;
+ dst += 2 * vec_len;
+
+ /* unrolled */
+ bytes = vld1q_u8((uint8 *) src);
+ high = vshrq_n_u8(bytes, 4);
+ low = vandq_u8(bytes, vdupq_n_u8(0xF));
+
+ zipped.val[0] = vqtbl1q_u8(hextbl_vec, high);
+ zipped.val[1] = vqtbl1q_u8(hextbl_vec, low);
+ vst2q_u8((uint8 *) dst, zipped);
+
+ src += vec_len;
+ dst += 2 * vec_len;
+ }
+
+
+
+ if (len > chunks_len)
+ hex_encode_scalar(src, len - chunks_len, dst);
+
+ return (uint64) len * 2;
+}
+
+/*
+ * get_hex_neon
+ * Returns true if the hexadecimal digits are successfully converted
+ * to nibbles and stored in 'res'; otherwise, returns false.
+ */
+static inline bool
+get_hex_neon(uint8x16_t vec, uint8x16_t *res)
+{
+ /*
+ * Convert ASCII of '0'-'9' to integers 0-9 by subtracting 48 (ASCII of '0').
+ * Similarly, convert letters 'A'–'F' and 'a'–'f' to integers 10–15 by
+ * subtracting 55 ('A' - 10) and 87 ('a' - 10).
+ */
+ uint8x16_t digit = vsubq_u8(vec, vdupq_n_u8('0'));
+ uint8x16_t upper = vsubq_u8(vec, vdupq_n_u8('A' - 10));
+ uint8x16_t lower = vsubq_u8(vec, vdupq_n_u8('a' - 10));
+
+ /*
+ * Identify valid values in digit, upper, and lower vectors.
+ * Values 0-9 are valid in digits, while values 10-15 are valid
+ * in upper and lower.
+ *
+ * Example:
+ * vec: '0' '9' 'A' 'F' 'a' 'f'
+ * vec (in ASCII): 48 57 65 70 97 102
+ *
+ * digit: 0 9 17 22 49 54
+ * valid_digit: 1 1 0 0 0 0
+ *
+ * upper: 249 2 10 15 42 47
+ * valid_upper: 0 1 1 1 0 0
+ *
+ * lower: 217 226 234 239 10 15
+ *
+ * Note that values 0-9 are also marked valid in valid_upper, this will be
+ * handled later.
+ */
+
+ uint8x16_t valid_digit = vcltq_u8(digit, vdupq_n_u8(10));
+ uint8x16_t valid_upper = vcltq_u8(upper, vdupq_n_u8(16));
+
+ /*
+ * Merge upper and lower vector using the logic: pick the element from
+ * upper if it's true in valid_upper else pick the element in lower
+ *
+ * Mark the valid range i.e. 10-15 in letter vector
+ *
+ * letter: 217 2 10 15 10 15
+ * valid_letter: 0 0 1 1 1 1
+ */
+
+ uint8x16_t letter = vbslq_u8(valid_upper, upper, lower);
+ uint8x16_t valid_letter = vandq_u8(vcgtq_u8(letter, vdupq_n_u8(9)),
+ vcltq_u8(letter, vdupq_n_u8(16)));
+
+ /*
+ * Check for invalid hexadecimal digit. Each value must fall within
+ * the range 0-9 (true in valid_digit) or 10-15 (true in valid_letter) i.e.
+ * the OR of valid_digit and valid_letter should be all true.
+ */
+ uint8x16_t invalid_mask = vmvnq_u8(vorrq_u8(valid_digit, valid_letter));
+
+ if (vmaxvq_u8(invalid_mask) != 0)
+ return false;
+
+ /*
+ * Finally, combine digit and letter vectors using the logic:
+ * pick the element from digit if it's true in valid_digit else pick the
+ * element in letter.
+ *
+ * res: 0 9 10 15 10 15
+ */
+ *res = vbslq_u8(valid_digit, digit, letter);
+ return true;
+}
+
+uint64
+hex_decode_optimized(const char *src, size_t len, char *dst)
+{
+ return hex_decode_safe_optimized(src, len, dst, NULL);
+}
+
+uint64
+hex_decode_safe_optimized(const char *src, size_t len, char *dst, Node *escontext)
+{
+ uint32 vec_len = sizeof(uint8x16_t);
+ size_t i = 0;
+ size_t chunks_len = len & ~(2 * vec_len - 1); /* process 2 x vec_len per iteration */
+ uint8x16_t ascii_zero = vdupq_n_u8('0');
+ const char *p = dst;
+
+ while (i < chunks_len)
+ {
+ uint8x16x2_t bytes = vld2q_u8((uint8 *) src);
+ uint8x16_t high = bytes.val[0]; /* hex digits for high nibble */
+ uint8x16_t low = bytes.val[1]; /* hex digits for low nibble */
+
+ /* fallback for characters with ASCII values below '0' */
+ uint8x16_t is_below_zero = vorrq_u8(vcltq_u8(high, ascii_zero),
+ vcltq_u8(low, ascii_zero));
+ if (vmaxvq_u8(is_below_zero) != 0)
+ break;
+
+ /* fallback if an invalid hexadecimal digit is found */
+ if (!get_hex_neon(high, &high) || !get_hex_neon(low, &low))
+ break;
+
+ /* form the byte by left-shifting the high nibble and OR-ing it with the low nibble */
+ vst1q_u8((uint8 *) dst, vorrq_u8(vshlq_n_u8(high, 4), low));
+
+ i += 2 * vec_len;
+ src += 2 * vec_len;
+ dst += vec_len;
+ }
+
+ if (len > i) /* fallback */
+ return dst - p + hex_decode_safe_scalar(src, len - i, dst, escontext);
+
+ return dst - p;
+}
+
+#endif /* HEX_CODING_AARCH64 */
diff --git a/src/backend/utils/adt/meson.build b/src/backend/utils/adt/meson.build
index dac372c3bea..8b106d03d33 100644
--- a/src/backend/utils/adt/meson.build
+++ b/src/backend/utils/adt/meson.build
@@ -22,6 +22,7 @@ backend_sources += files(
'dbsize.c',
'domains.c',
'encode.c',
+ 'encode_aarch64.c',
'enum.c',
'expandeddatum.c',
'expandedrecord.c',
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 1c98c7d2255..a809fad2771 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -35,11 +35,62 @@ extern int errdatatype(Oid datatypeOid);
extern int errdomainconstraint(Oid datatypeOid, const char *conname);
/* encode.c */
-extern uint64 hex_encode(const char *src, size_t len, char *dst);
-extern uint64 hex_decode(const char *src, size_t len, char *dst);
-extern uint64 hex_decode_safe(const char *src, size_t len, char *dst,
+extern uint64 hex_encode_scalar(const char *src, size_t len, char *dst);
+extern uint64 hex_decode_scalar(const char *src, size_t len, char *dst);
+extern uint64 hex_decode_safe_scalar(const char *src, size_t len, char *dst,
Node *escontext);
+/*
+ * On AArch64, we can use Neon instructions if the compiler provides access to
+ * them (as indicated by __ARM_NEON). As in simd.h, we assume that all
+ * available 64-bit hardware has Neon support.
+ */
+#if defined(__aarch64__) && defined(__ARM_NEON)
+#define HEX_CODING_AARCH64 1
+#endif
+
+#ifdef HEX_CODING_AARCH64
+extern uint64 hex_encode_optimized(const char *src, size_t len, char *dst);
+extern uint64 hex_decode_optimized(const char *src, size_t len, char *dst);
+extern uint64 hex_decode_safe_optimized(const char *src, size_t len, char *dst, Node *escontext);
+#endif
+
+static inline uint64
+hex_encode(const char *src, size_t len, char *dst)
+{
+#ifdef HEX_CODING_AARCH64
+ int threshold = 32;
+
+ if (len >= threshold)
+ return hex_encode_optimized(src, len, dst);
+#endif
+ return hex_encode_scalar(src, len, dst);
+}
+
+static inline uint64
+hex_decode(const char *src, size_t len, char *dst)
+{
+#ifdef HEX_CODING_AARCH64
+ int threshold = 32;
+
+ if (len >= threshold)
+ return hex_decode_optimized(src, len, dst);
+#endif
+ return hex_decode_scalar(src, len, dst);
+}
+
+static inline uint64
+hex_decode_safe(const char *src, size_t len, char *dst, Node *escontext)
+{
+#ifdef HEX_CODING_AARCH64
+ int threshold = 32;
+
+ if (len >= threshold)
+ return hex_decode_safe_optimized(src, len, dst, escontext);
+#endif
+ return hex_decode_safe_scalar(src, len, dst, escontext);
+}
+
/* int.c */
extern int2vector *buildint2vector(const int16 *int2s, int n);
--
2.34.1
[application/octet-stream] v6-0002-SVE-support-for-hex-coding.patch (21.1K, ../../OS9PR01MB15185B278E343A9BA5F0F6AB19700A@OS9PR01MB15185.jpnprd01.prod.outlook.com/4-v6-0002-SVE-support-for-hex-coding.patch)
download | inline diff:
From 7330548cc5b5ebdbe5c8fa515f1eb2eebfc7f2c3 Mon Sep 17 00:00:00 2001
From: Chiranmoy Bhattacharya <[email protected]>
Date: Thu, 4 Sep 2025 15:33:47 +0530
Subject: [PATCH v6 2/3] SVE support for hex coding
---
config/c-compiler.m4 | 85 +++++++++
configure | 104 +++++++++++
configure.ac | 9 +
meson.build | 81 +++++++++
src/backend/utils/adt/encode_aarch64.c | 231 ++++++++++++++++++++++++-
src/include/pg_config.h.in | 3 +
src/include/utils/builtins.h | 10 +-
7 files changed, 520 insertions(+), 3 deletions(-)
diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index da40bd6a647..73d12826698 100644
--- a/config/c-compiler.m4
+++ b/config/c-compiler.m4
@@ -798,3 +798,88 @@ if test x"$Ac_cachevar" = x"yes"; then
fi
undefine([Ac_cachevar])dnl
])# PGAC_SVE_POPCNT_INTRINSICS
+
+# PGAC_ARM_SVE_HEX_INTRINSICS
+# ------------------------------
+# Check if the compiler supports the SVE intrinsic required for hex coding:
+# svsub_x, svcmplt, svsel, svcmpgt, svtbl, svlsr_x, svand_z, svcreate2,
+# svptest_any, svnot_z, svorr_z, svcntb, svld1, svwhilelt_b8, svst2, svld2,
+# svget2, svst1 and svlsl_x.
+#
+# If the intrinsics are supported, sets pgac_arm_sve_hex_intrinsics.
+AC_DEFUN([PGAC_ARM_SVE_HEX_INTRINSICS],
+[define([Ac_cachevar], [AS_TR_SH([pgac_cv_arm_sve_hex_intrinsics])])dnl
+AC_CACHE_CHECK([for svtbl, svlsr_x, svand_z, svcreate2, etc], [Ac_cachevar],
+[AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <arm_sve.h>
+
+ char input@<:@64@:>@;
+ char output@<:@128@:>@;
+
+ #if defined(__has_attribute) && __has_attribute (target)
+ __attribute__((target("arch=armv8-a+sve")))
+ #endif
+ int get_hex_sve(svbool_t pred, svuint8_t vec, svuint8_t *res)
+ {
+ svuint8_t digit = svsub_x(pred, vec, 48),
+ upper = svsub_x(pred, vec, 55),
+ lower = svsub_x(pred, vec, 87);
+ svbool_t valid_digit = svcmplt(pred, digit, 10),
+ valid_upper = svcmplt(pred, upper, 16);
+ svuint8_t letter = svsel(valid_upper, upper, lower);
+ svbool_t valid_letter = svand_z(pred, svcmpgt(pred, letter, 9),
+ svcmplt(pred, letter, 16));
+ if (svptest_any(pred, svnot_z(pred, svorr_z(pred, valid_digit, valid_letter))))
+ return 0;
+ *res = svsel(valid_digit, digit, letter);
+ return 1;
+ }
+
+ #if defined(__has_attribute) && __has_attribute (target)
+ __attribute__((target("arch=armv8-a+sve")))
+ #endif
+ static int hex_coding_test(void)
+ {
+ int len = 64, vec_len = svcntb(), vec_len_x2 = svcntb() * 2;
+ const char *hextbl = "0123456789abcdef";
+ svuint8_t hextbl_vec = svld1(svwhilelt_b8(0, 16), (uint8_t *) hextbl);
+ char *src = input, *dst = output;
+
+ /* hex encode */
+ for (uint64_t i = 0; i < 64; i += vec_len, dst += 2 * vec_len, src += vec_len)
+ {
+ svbool_t pred = svwhilelt_b8((uint64_t) i, (uint64_t) len);
+ svuint8_t bytes = svld1(pred, (uint8_t *) src),
+ high = svlsr_x(pred, bytes, 4),
+ low = svand_z(pred, bytes, 0xF);
+ svuint8x2_t merged = svcreate2(svtbl(hextbl_vec, high), svtbl(hextbl_vec, low));
+ svst2(pred, (uint8_t *) dst, merged);
+ }
+
+ /* hex decode */
+ len = 128;
+
+ for (int i; i < len; i += vec_len_x2)
+ {
+ svbool_t pred = svwhilelt_b8((uint64_t) i / 2, (uint64_t) len / 2);
+ svuint8x2_t bytes = svld2(pred, (uint8_t *) src + i);
+ svuint8_t high = svget2(bytes, 0), low = svget2(bytes, 1);
+
+ if (svptest_any(pred, svorr_z(pred, svcmplt(pred, high, '0'), svcmplt(pred, low, '0'))))
+ break;
+ if (!get_hex_sve(pred, high, &high) || !get_hex_sve(pred, low, &low))
+ break;
+
+ svst1(pred, (uint8_t *) dst + i / 2, svorr_z(pred, svlsl_x(pred, high, 4), low));
+ }
+
+ /* return computed value, to prevent the above being optimized away */
+ return output@<:@0@:>@;
+ }],
+ [return hex_coding_test();])],
+ [Ac_cachevar=yes],
+ [Ac_cachevar=no])])
+if test x"$Ac_cachevar" = x"yes"; then
+ pgac_arm_sve_hex_intrinsics=yes
+fi
+undefine([Ac_cachevar])dnl
+])# PGAC_ARM_SVE_HEX_INTRINSICS
diff --git a/configure b/configure
index 39c68161cec..60354107f87 100755
--- a/configure
+++ b/configure
@@ -17735,6 +17735,110 @@ $as_echo "#define USE_SVE_POPCNT_WITH_RUNTIME_CHECK 1" >>confdefs.h
fi
fi
+# Check for ARM SVE intrinsics for hex coding
+#
+if test x"$host_cpu" = x"aarch64"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for svtbl, svlsr_x, svand_z, svcreate2, etc" >&5
+$as_echo_n "checking for svtbl, svlsr_x, svand_z, svcreate2, etc... " >&6; }
+if ${pgac_cv_arm_sve_hex_intrinsics+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <arm_sve.h>
+
+ char input[64];
+ char output[128];
+
+ #if defined(__has_attribute) && __has_attribute (target)
+ __attribute__((target("arch=armv8-a+sve")))
+ #endif
+ int get_hex_sve(svbool_t pred, svuint8_t vec, svuint8_t *res)
+ {
+ svuint8_t digit = svsub_x(pred, vec, 48),
+ upper = svsub_x(pred, vec, 55),
+ lower = svsub_x(pred, vec, 87);
+ svbool_t valid_digit = svcmplt(pred, digit, 10),
+ valid_upper = svcmplt(pred, upper, 16);
+ svuint8_t letter = svsel(valid_upper, upper, lower);
+ svbool_t valid_letter = svand_z(pred, svcmpgt(pred, letter, 9),
+ svcmplt(pred, letter, 16));
+ if (svptest_any(pred, svnot_z(pred, svorr_z(pred, valid_digit, valid_letter))))
+ return 0;
+ *res = svsel(valid_digit, digit, letter);
+ return 1;
+ }
+
+ #if defined(__has_attribute) && __has_attribute (target)
+ __attribute__((target("arch=armv8-a+sve")))
+ #endif
+ static int hex_coding_test(void)
+ {
+ int len = 64, vec_len = svcntb(), vec_len_x2 = svcntb() * 2;
+ const char *hextbl = "0123456789abcdef";
+ svuint8_t hextbl_vec = svld1(svwhilelt_b8(0, 16), (uint8_t *) hextbl);
+ char *src = input, *dst = output;
+
+ /* hex encode */
+ for (uint64_t i = 0; i < 64; i += vec_len, dst += 2 * vec_len, src += vec_len)
+ {
+ svbool_t pred = svwhilelt_b8((uint64_t) i, (uint64_t) len);
+ svuint8_t bytes = svld1(pred, (uint8_t *) src),
+ high = svlsr_x(pred, bytes, 4),
+ low = svand_z(pred, bytes, 0xF);
+ svuint8x2_t merged = svcreate2(svtbl(hextbl_vec, high), svtbl(hextbl_vec, low));
+ svst2(pred, (uint8_t *) dst, merged);
+ }
+
+ /* hex decode */
+ len = 128;
+
+ for (int i; i < len; i += vec_len_x2)
+ {
+ svbool_t pred = svwhilelt_b8((uint64_t) i / 2, (uint64_t) len / 2);
+ svuint8x2_t bytes = svld2(pred, (uint8_t *) src + i);
+ svuint8_t high = svget2(bytes, 0), low = svget2(bytes, 1);
+
+ if (svptest_any(pred, svorr_z(pred, svcmplt(pred, high, '0'), svcmplt(pred, low, '0'))))
+ break;
+ if (!get_hex_sve(pred, high, &high) || !get_hex_sve(pred, low, &low))
+ break;
+
+ svst1(pred, (uint8_t *) dst + i / 2, svorr_z(pred, svlsl_x(pred, high, 4), low));
+ }
+
+ /* return computed value, to prevent the above being optimized away */
+ return output[0];
+ }
+int
+main ()
+{
+return hex_coding_test();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ pgac_cv_arm_sve_hex_intrinsics=yes
+else
+ pgac_cv_arm_sve_hex_intrinsics=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_arm_sve_hex_intrinsics" >&5
+$as_echo "$pgac_cv_arm_sve_hex_intrinsics" >&6; }
+if test x"$pgac_cv_arm_sve_hex_intrinsics" = x"yes"; then
+ pgac_arm_sve_hex_intrinsics=yes
+fi
+
+ if test x"$pgac_arm_sve_hex_intrinsics" = x"yes"; then
+
+$as_echo "#define USE_SVE_HEX_WITH_RUNTIME_CHECK 1" >>confdefs.h
+
+ fi
+fi
+
# Check for Intel SSE 4.2 intrinsics to do CRC calculations.
#
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for _mm_crc32_u8 and _mm_crc32_u32" >&5
diff --git a/configure.ac b/configure.ac
index 066e3976c0a..6ca57b8c4a7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2136,6 +2136,15 @@ if test x"$host_cpu" = x"aarch64"; then
fi
fi
+# Check for ARM SVE intrinsics for hex coding
+#
+if test x"$host_cpu" = x"aarch64"; then
+ PGAC_ARM_SVE_HEX_INTRINSICS()
+ if test x"$pgac_arm_sve_hex_intrinsics" = x"yes"; then
+ AC_DEFINE(USE_SVE_HEX_WITH_RUNTIME_CHECK, 1, [Define to 1 to use ARM SVE intrinsic for hex coding.])
+ fi
+fi
+
# Check for Intel SSE 4.2 intrinsics to do CRC calculations.
#
PGAC_SSE42_CRC32_INTRINSICS()
diff --git a/meson.build b/meson.build
index ab8101d67b2..ea5392cfc78 100644
--- a/meson.build
+++ b/meson.build
@@ -2372,6 +2372,87 @@ int main(void)
endif
+###############################################################
+# Check the availability of SVE intrinsics for hex coding.
+###############################################################
+
+if host_cpu == 'aarch64'
+
+ prog = '''
+#include <arm_sve.h>
+
+char input[64];
+char output[128];
+
+#if defined(__has_attribute) && __has_attribute (target)
+ __attribute__((target("arch=armv8-a+sve")))
+#endif
+int get_hex_sve(svbool_t pred, svuint8_t vec, svuint8_t *res)
+{
+ svuint8_t digit = svsub_x(pred, vec, 48),
+ upper = svsub_x(pred, vec, 55),
+ lower = svsub_x(pred, vec, 87);
+ svbool_t valid_digit = svcmplt(pred, digit, 10),
+ valid_upper = svcmplt(pred, upper, 16);
+ svuint8_t letter = svsel(valid_upper, upper, lower);
+ svbool_t valid_letter = svand_z(pred, svcmpgt(pred, letter, 9),
+ svcmplt(pred, letter, 16));
+ if (svptest_any(pred, svnot_z(pred, svorr_z(pred, valid_digit, valid_letter))))
+ return 0;
+ *res = svsel(valid_digit, digit, letter);
+ return 1;
+}
+
+#if defined(__has_attribute) && __has_attribute (target)
+ __attribute__((target("arch=armv8-a+sve")))
+#endif
+int main(void)
+{
+ int len = 64, vec_len = svcntb(), vec_len_x2 = svcntb() * 2;
+ const char hextbl[] = "0123456789abcdef";
+ svuint8_t hextbl_vec = svld1(svwhilelt_b8(0, 16), (uint8_t *) hextbl);
+ char *src = input, *dst = output;
+
+ /* hex encode */
+ for (uint64_t i = 0; i < 64; i += vec_len, dst += 2 * vec_len, src += vec_len)
+ {
+ svbool_t pred = svwhilelt_b8((uint64_t) i, (uint64_t) len);
+ svuint8_t bytes = svld1(pred, (uint8_t *) src),
+ high = svlsr_x(pred, bytes, 4),
+ low = svand_z(pred, bytes, 0xF);
+ svuint8x2_t merged = svcreate2(svtbl(hextbl_vec, high), svtbl(hextbl_vec, low));
+ svst2(pred, (uint8_t *) dst, merged);
+ }
+
+ /* hex decode */
+ len = 128;
+
+ for (int i; i < len; i += vec_len_x2)
+ {
+ svbool_t pred = svwhilelt_b8((uint64_t) i / 2, (uint64_t) len / 2);
+ svuint8x2_t bytes = svld2(pred, (uint8_t *) src + i);
+ svuint8_t high = svget2(bytes, 0), low = svget2(bytes, 1);
+
+ if (svptest_any(pred, svorr_z(pred, svcmplt(pred, high, '0'), svcmplt(pred, low, '0'))))
+ break;
+ if (!get_hex_sve(pred, high, &high) || !get_hex_sve(pred, low, &low))
+ break;
+
+ svst1(pred, (uint8_t *) dst + i / 2, svorr_z(pred, svlsl_x(pred, high, 4), low));
+ }
+
+ /* return computed value, to prevent the above being optimized away */
+ return output[0];
+}
+'''
+
+ if cc.links(prog, name: 'SVE hex coding', args: test_c_args)
+ cdata.set('USE_SVE_HEX_WITH_RUNTIME_CHECK', 1)
+ endif
+
+endif
+
+
###############################################################
# Select CRC-32C implementation.
#
diff --git a/src/backend/utils/adt/encode_aarch64.c b/src/backend/utils/adt/encode_aarch64.c
index 7b0412dc255..4f22fb1d6c7 100644
--- a/src/backend/utils/adt/encode_aarch64.c
+++ b/src/backend/utils/adt/encode_aarch64.c
@@ -20,8 +20,229 @@
#include <arm_neon.h>
+#ifdef USE_SVE_HEX_WITH_RUNTIME_CHECK
+#include <arm_sve.h>
+
+#if defined(HAVE_ELF_AUX_INFO) || defined(HAVE_GETAUXVAL)
+#include <sys/auxv.h>
+#endif
+
+/*
+ * These are the NEON implementations of the hex encode/decode functions.
+ */
+static uint64 hex_encode_neon(const char *src, size_t len, char *dst);
+static uint64 hex_decode_neon(const char *src, size_t len, char *dst);
+static uint64 hex_decode_safe_neon(const char *src, size_t len, char *dst, Node *escontext);
+
+/*
+ * These are the SVE implementations of the hex encode/decode functions.
+ */
+static uint64 hex_encode_sve(const char *src, size_t len, char *dst);
+static uint64 hex_decode_sve(const char *src, size_t len, char *dst);
+static uint64 hex_decode_safe_sve(const char *src, size_t len, char *dst, Node *escontext);
+
+/*
+ * The function pointers are initially set to "choose" functions. These
+ * functions will first set the pointers to the right implementations (based on
+ * what the current CPU supports) and then will call the pointer to fulfill the
+ * caller's request.
+ */
+
+static uint64 hex_encode_choose(const char *src, size_t len, char *dst);
+static uint64 hex_decode_choose(const char *src, size_t len, char *dst);
+static uint64 hex_decode_safe_choose(const char *src, size_t len, char *dst, Node *escontext);
+uint64 (*hex_encode_optimized) (const char *src, size_t len, char *dst) = hex_encode_choose;
+uint64 (*hex_decode_optimized) (const char *src, size_t len, char *dst) = hex_decode_choose;
+uint64 (*hex_decode_safe_optimized) (const char *src, size_t len, char *dst, Node *escontext) = hex_decode_safe_choose;
+
+static inline bool
+check_sve_support(void)
+{
+#ifdef HAVE_ELF_AUX_INFO
+ unsigned long value;
+
+ return elf_aux_info(AT_HWCAP, &value, sizeof(value)) == 0 &&
+ (value & HWCAP_SVE) != 0;
+#elif defined(HAVE_GETAUXVAL)
+ return (getauxval(AT_HWCAP) & HWCAP_SVE) != 0;
+#else
+ return false;
+#endif
+}
+
+static inline void
+choose_hex_functions(void)
+{
+ if (check_sve_support())
+ {
+ hex_encode_optimized = hex_encode_sve;
+ hex_decode_optimized = hex_decode_sve;
+ hex_decode_safe_optimized = hex_decode_safe_sve;
+ }
+ else
+ {
+ hex_encode_optimized = hex_encode_neon;
+ hex_decode_optimized = hex_decode_neon;
+ hex_decode_safe_optimized = hex_decode_safe_neon;
+ }
+}
+
+static uint64
+hex_encode_choose(const char *src, size_t len, char *dst)
+{
+ choose_hex_functions();
+ return hex_encode_optimized(src, len, dst);
+}
+
+static uint64
+hex_decode_choose(const char *src, size_t len, char *dst)
+{
+ choose_hex_functions();
+ return hex_decode_optimized(src, len, dst);
+}
+
+static uint64
+hex_decode_safe_choose(const char *src, size_t len, char *dst, Node *escontext)
+{
+ choose_hex_functions();
+ return hex_decode_safe_optimized(src, len, dst, escontext);
+}
+
+pg_attribute_target("arch=armv8-a+sve")
+uint64
+hex_encode_sve(const char *src, size_t len, char *dst)
+{
+ const char hextbl[] = "0123456789abcdef";
+ uint32 vec_len = svcntb();
+ svuint8_t hextbl_vec = svld1(svwhilelt_b8(0, 16), (uint8 *) hextbl);
+ svbool_t pred_true = svptrue_b8();
+ size_t chunks_len = len & ~(2 * vec_len - 1); /* process 2 * vec_len bytes per iteration */
+
+ for (size_t i = 0; i < chunks_len; i += 2 * vec_len)
+ {
+ svuint8_t bytes = svld1(pred_true, (uint8 *) src);
+ svuint8_t high = svlsr_x(pred_true, bytes, 4);
+ svuint8_t low = svand_z(pred_true, bytes, 0xF);
+ svuint8x2_t zipped = svcreate2(svtbl(hextbl_vec, high), svtbl(hextbl_vec, low));
+ svst2(pred_true, (uint8 *) dst, zipped);
+
+ dst += 2 * vec_len;
+ src += vec_len;
+
+ /* unrolled */
+ bytes = svld1(pred_true, (uint8 *) src);
+ high = svlsr_x(pred_true, bytes, 4);
+ low = svand_z(pred_true, bytes, 0xF);
+ zipped = svcreate2(svtbl(hextbl_vec, high), svtbl(hextbl_vec, low));
+ svst2(pred_true, (uint8 *) dst, zipped);
+
+ dst += 2 * vec_len;
+ src += vec_len;
+ }
+
+ /* process remaining tail bytes */
+ for (size_t i = chunks_len; i < len; i += vec_len)
+ {
+ svbool_t pred = svwhilelt_b8((uint64) i, (uint64) len);
+ svuint8_t bytes = svld1(pred, (uint8 *) src);
+ svuint8_t high = svlsr_x(pred, bytes, 4);
+ svuint8_t low = svand_z(pred, bytes, 0xF);
+ svuint8x2_t zipped = svcreate2(svtbl(hextbl_vec, high), svtbl(hextbl_vec, low));
+ svst2(pred, (uint8 *) dst, zipped);
+
+ dst += 2 * vec_len;
+ src += vec_len;
+ }
+
+ return (uint64) len * 2;
+}
+
+pg_attribute_target("arch=armv8-a+sve")
+static inline bool
+get_hex_sve(svbool_t pred, svuint8_t vec, svuint8_t *res)
+{
+ svuint8_t digit = svsub_x(pred, vec, 48);
+ svuint8_t upper = svsub_x(pred, vec, 55);
+ svuint8_t lower = svsub_x(pred, vec, 87);
+
+ svbool_t valid_digit = svcmplt(pred, digit, 10);
+ svbool_t valid_upper = svcmplt(pred, upper, 16);
+
+ svuint8_t letter = svsel(valid_upper, upper, lower);
+ svbool_t valid_letter = svand_z(pred, svcmpgt(pred, letter, 9),
+ svcmplt(pred, letter, 16));
+
+ if (svptest_any(pred, svnot_z(pred, svorr_z(pred, valid_digit, valid_letter))))
+ return false;
+
+ *res = svsel(valid_digit, digit, letter);
+ return true;
+}
+
+uint64
+hex_decode_sve(const char *src, size_t len, char *dst)
+{
+ return hex_decode_safe_sve(src, len, dst, NULL);
+}
+
+pg_attribute_target("arch=armv8-a+sve")
+uint64
+hex_decode_safe_sve(const char *src, size_t len, char *dst, Node *escontext)
+{
+ uint32 vec_len = svcntb();
+ size_t i = 0;
+ size_t chunks_len = len & ~(2 * vec_len - 1); /* process 2 * vec_len byte chunk each iteration */
+ svbool_t pred = svptrue_b8();
+ const char *p = dst;
+ bool fallback = false;
+
+ while (i < chunks_len)
+ {
+ svuint8x2_t bytes = svld2(pred, (uint8 *) src);
+ svuint8_t high = svget2(bytes, 0);
+ svuint8_t low = svget2(bytes, 1);
+
+ if (svptest_any(pred, svorr_z(pred, svcmplt(pred, high, '0'), svcmplt(pred, low, '0'))))
+ {
+ fallback = true;
+ break;
+ }
+
+ if (!get_hex_sve(pred, high, &high) || !get_hex_sve(pred, low, &low))
+ {
+ fallback = true;
+ break;
+ }
+
+ svst1(pred, (uint8 *) dst, svorr_z(pred, svlsl_x(pred, high, 4), low));
+
+ i += 2 * vec_len;
+ src += 2 * vec_len;
+ dst += vec_len;
+ }
+
+ if (len > i && !fallback) /* can use neon for smaller chunks */
+ return dst - p + hex_decode_safe_neon(src, len - i, dst, escontext);
+
+ if (fallback) /* fallback */
+ return dst - p + hex_decode_safe_scalar(src, len - i, dst, escontext);
+
+ return dst - p;
+}
+
+#endif /* USE_SVE_HEX_WITH_RUNTIME_CHECK */
+
+/*
+ * If the compiler supports SVE, rename the NEON versions because the
+ * optimized versions are now referenced via function pointers.
+ */
+
uint64
+#ifdef USE_SVE_HEX_WITH_RUNTIME_CHECK
+static hex_encode_neon(const char *src, size_t len, char *dst)
+#else
hex_encode_optimized(const char *src, size_t len, char *dst)
+#endif
{
const char hextbl[] = "0123456789abcdef";
uint8x16_t hextbl_vec = vld1q_u8((uint8 *) hextbl);
@@ -63,8 +284,6 @@ hex_encode_optimized(const char *src, size_t len, char *dst)
dst += 2 * vec_len;
}
-
-
if (len > chunks_len)
hex_encode_scalar(src, len - chunks_len, dst);
@@ -148,13 +367,21 @@ get_hex_neon(uint8x16_t vec, uint8x16_t *res)
}
uint64
+#ifdef USE_SVE_HEX_WITH_RUNTIME_CHECK
+static hex_decode_neon(const char *src, size_t len, char *dst)
+#else
hex_decode_optimized(const char *src, size_t len, char *dst)
+#endif
{
return hex_decode_safe_optimized(src, len, dst, NULL);
}
uint64
+#ifdef USE_SVE_HEX_WITH_RUNTIME_CHECK
+static hex_decode_safe_neon(const char *src, size_t len, char *dst, Node *escontext)
+#else
hex_decode_safe_optimized(const char *src, size_t len, char *dst, Node *escontext)
+#endif
{
uint32 vec_len = sizeof(uint8x16_t);
size_t i = 0;
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index c4dc5d72bdb..a6735bdd21f 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -678,6 +678,9 @@
/* Define to 1 to use AVX-512 popcount instructions with a runtime check. */
#undef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
+/* Define to 1 to use SVE instructions for hex coding with a runtime check. */
+#undef USE_SVE_HEX_WITH_RUNTIME_CHECK
+
/* Define to 1 to build with Bonjour support. (--with-bonjour) */
#undef USE_BONJOUR
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index a809fad2771..8a80a9ae51f 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -49,7 +49,15 @@ extern uint64 hex_decode_safe_scalar(const char *src, size_t len, char *dst,
#define HEX_CODING_AARCH64 1
#endif
-#ifdef HEX_CODING_AARCH64
+/*
+ * We can try to use an SVE-optimized hex encode/decode on systems supporting SVE.
+ * For that, we use a function pointer.
+ */
+#ifdef USE_SVE_HEX_WITH_RUNTIME_CHECK
+extern PGDLLIMPORT uint64 (*hex_encode_optimized) (const char *src, size_t len, char *dst);
+extern PGDLLIMPORT uint64 (*hex_decode_optimized) (const char *src, size_t len, char *dst);
+extern PGDLLIMPORT uint64 (*hex_decode_safe_optimized) (const char *src, size_t len, char *dst, Node *escontext);
+#elif HEX_CODING_AARCH64
extern uint64 hex_encode_optimized(const char *src, size_t len, char *dst);
extern uint64 hex_decode_optimized(const char *src, size_t len, char *dst);
extern uint64 hex_decode_safe_optimized(const char *src, size_t len, char *dst, Node *escontext);
--
2.34.1
[application/octet-stream] v6-0003-Regression-tests-for-SIMD-hex-coding.patch (7.4K, ../../OS9PR01MB15185B278E343A9BA5F0F6AB19700A@OS9PR01MB15185.jpnprd01.prod.outlook.com/5-v6-0003-Regression-tests-for-SIMD-hex-coding.patch)
download | inline diff:
From 2fdd7f1170253984c1b065ac3a0fc43a31997c05 Mon Sep 17 00:00:00 2001
From: Chiranmoy Bhattacharya <[email protected]>
Date: Thu, 4 Sep 2025 15:44:19 +0530
Subject: [PATCH v6 3/3] Regression tests for SIMD hex coding
---
src/test/regress/expected/hex_coding.out | 63 ++++++++++++++++++++++++
src/test/regress/parallel_schedule | 5 ++
src/test/regress/sql/hex_coding.sql | 39 +++++++++++++++
3 files changed, 107 insertions(+)
create mode 100644 src/test/regress/expected/hex_coding.out
create mode 100644 src/test/regress/sql/hex_coding.sql
diff --git a/src/test/regress/expected/hex_coding.out b/src/test/regress/expected/hex_coding.out
new file mode 100644
index 00000000000..e6d78fa4876
--- /dev/null
+++ b/src/test/regress/expected/hex_coding.out
@@ -0,0 +1,63 @@
+--
+-- tests for hex_encode and hex_decode in encode.c
+--
+-- Build table for testing
+CREATE TABLE BYTEA_TABLE(data BYTEA);
+-- hex_decode is used for inserting into bytea column
+-- Set bytea_output to hex so that hex_encode is used and tested
+SET bytea_output = 'hex';
+INSERT INTO BYTEA_TABLE VALUES ('\xAB');
+INSERT INTO BYTEA_TABLE VALUES ('\x01ab');
+INSERT INTO BYTEA_TABLE VALUES ('\xDEADC0DE');
+INSERT INTO BYTEA_TABLE VALUES ('\xbaadf00d');
+INSERT INTO BYTEA_TABLE VALUES ('\x C001 c0ffee '); -- hex string with whitespaces
+-- errors checking
+INSERT INTO BYTEA_TABLE VALUES ('\xbadf00d'); -- odd number of hex digits
+ERROR: invalid hexadecimal data: odd number of digits
+LINE 1: INSERT INTO BYTEA_TABLE VALUES ('\xbadf00d');
+ ^
+INSERT INTO BYTEA_TABLE VALUES ('\xdeadcode'); -- invalid hexadecimal digit: "o"
+ERROR: invalid hexadecimal digit: "o"
+LINE 1: INSERT INTO BYTEA_TABLE VALUES ('\xdeadcode');
+ ^
+INSERT INTO BYTEA_TABLE VALUES ('\xC00LC0FFEE'); -- invalid hexadecimal digit: "L"
+ERROR: invalid hexadecimal digit: "L"
+LINE 1: INSERT INTO BYTEA_TABLE VALUES ('\xC00LC0FFEE');
+ ^
+INSERT INTO BYTEA_TABLE VALUES ('\xC00LC*DE'); -- invalid hexadecimal digit: "*"
+ERROR: invalid hexadecimal digit: "L"
+LINE 1: INSERT INTO BYTEA_TABLE VALUES ('\xC00LC*DE');
+ ^
+INSERT INTO BYTEA_TABLE VALUES ('\xbad f00d'); -- invalid hexadecimal digit: " "
+ERROR: invalid hexadecimal digit: " "
+LINE 1: INSERT INTO BYTEA_TABLE VALUES ('\xbad f00d');
+ ^
+-- long hex strings to test SIMD implementation
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 8))::bytea;
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 8) || repeat('baadf00d', 8))::bytea;
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 8) || ' ' || repeat('baad f00d', 8))::bytea; -- hex string with whitespaces
+-- errors checking for SIMD implementation
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 4) || 'badf00d' || repeat('DEADC0DE', 4))::bytea; -- odd number of hex digits
+ERROR: invalid hexadecimal data: odd number of digits
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 4) || 'baadfood'|| repeat('DEADC0DE', 4))::bytea; -- invalid hexadecimal digit: "o"
+ERROR: invalid hexadecimal digit: "o"
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 4) || 'C00LC0FFEE' || repeat('DEADC0DE', 4))::bytea; -- invalid hexadecimal digit: "L"
+ERROR: invalid hexadecimal digit: "L"
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 8) || 'C00LC*DE' || repeat('DEADC0DE', 4))::bytea; -- invalid hexadecimal digit: "*"
+ERROR: invalid hexadecimal digit: "L"
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 8) || 'bad f00d' || repeat('DEADC0DE', 4))::bytea; -- invalid hexadecimal digit: " "
+ERROR: invalid hexadecimal digit: " "
+SELECT encode(data, 'hex') FROM BYTEA_TABLE;
+ encode
+----------------------------------------------------------------------------------------------------------------------------------
+ ab
+ 01ab
+ deadc0de
+ baadf00d
+ c001c0ffee
+ deadc0dedeadc0dedeadc0dedeadc0dedeadc0dedeadc0dedeadc0dedeadc0de
+ deadc0dedeadc0dedeadc0dedeadc0dedeadc0dedeadc0dedeadc0dedeadc0debaadf00dbaadf00dbaadf00dbaadf00dbaadf00dbaadf00dbaadf00dbaadf00d
+ deadc0dedeadc0dedeadc0dedeadc0dedeadc0dedeadc0dedeadc0dedeadc0debaadf00dbaadf00dbaadf00dbaadf00dbaadf00dbaadf00dbaadf00dbaadf00d
+(8 rows)
+
+DROP TABLE BYTEA_TABLE;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index fbffc67ae60..876a3988ed0 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -109,6 +109,11 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
# ----------
test: json jsonb json_encoding jsonpath jsonpath_encoding jsonb_jsonpath sqljson sqljson_queryfuncs sqljson_jsontable
+# ----------
+# Another group of parallel tests for hex encode/decode
+# ----------
+test: hex_coding
+
# ----------
# Another group of parallel tests
# with depends on create_misc
diff --git a/src/test/regress/sql/hex_coding.sql b/src/test/regress/sql/hex_coding.sql
new file mode 100644
index 00000000000..97c51b62e90
--- /dev/null
+++ b/src/test/regress/sql/hex_coding.sql
@@ -0,0 +1,39 @@
+--
+-- tests for hex_encode and hex_decode in encode.c
+--
+
+-- Build table for testing
+CREATE TABLE BYTEA_TABLE(data BYTEA);
+
+-- hex_decode is used for inserting into bytea column
+-- Set bytea_output to hex so that hex_encode is used and tested
+SET bytea_output = 'hex';
+
+INSERT INTO BYTEA_TABLE VALUES ('\xAB');
+INSERT INTO BYTEA_TABLE VALUES ('\x01ab');
+INSERT INTO BYTEA_TABLE VALUES ('\xDEADC0DE');
+INSERT INTO BYTEA_TABLE VALUES ('\xbaadf00d');
+INSERT INTO BYTEA_TABLE VALUES ('\x C001 c0ffee '); -- hex string with whitespaces
+
+-- errors checking
+INSERT INTO BYTEA_TABLE VALUES ('\xbadf00d'); -- odd number of hex digits
+INSERT INTO BYTEA_TABLE VALUES ('\xdeadcode'); -- invalid hexadecimal digit: "o"
+INSERT INTO BYTEA_TABLE VALUES ('\xC00LC0FFEE'); -- invalid hexadecimal digit: "L"
+INSERT INTO BYTEA_TABLE VALUES ('\xC00LC*DE'); -- invalid hexadecimal digit: "*"
+INSERT INTO BYTEA_TABLE VALUES ('\xbad f00d'); -- invalid hexadecimal digit: " "
+
+-- long hex strings to test SIMD implementation
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 8))::bytea;
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 8) || repeat('baadf00d', 8))::bytea;
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 8) || ' ' || repeat('baad f00d', 8))::bytea; -- hex string with whitespaces
+
+-- errors checking for SIMD implementation
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 4) || 'badf00d' || repeat('DEADC0DE', 4))::bytea; -- odd number of hex digits
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 4) || 'baadfood'|| repeat('DEADC0DE', 4))::bytea; -- invalid hexadecimal digit: "o"
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 4) || 'C00LC0FFEE' || repeat('DEADC0DE', 4))::bytea; -- invalid hexadecimal digit: "L"
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 8) || 'C00LC*DE' || repeat('DEADC0DE', 4))::bytea; -- invalid hexadecimal digit: "*"
+INSERT INTO BYTEA_TABLE SELECT ('\x' || repeat('DEADC0DE', 8) || 'bad f00d' || repeat('DEADC0DE', 4))::bytea; -- invalid hexadecimal digit: " "
+
+SELECT encode(data, 'hex') FROM BYTEA_TABLE;
+
+DROP TABLE BYTEA_TABLE;
--
2.34.1
[image/svg+xml] bytea_read_hex_encode_sve.svg (292.8K, ../../OS9PR01MB15185B278E343A9BA5F0F6AB19700A@OS9PR01MB15185.jpnprd01.prod.outlook.com/6-bytea_read_hex_encode_sve.svg)
download | view image
[image/svg+xml] bytea_read_hex_encode_v17.svg (287.7K, ../../OS9PR01MB15185B278E343A9BA5F0F6AB19700A@OS9PR01MB15185.jpnprd01.prod.outlook.com/7-bytea_read_hex_encode_v17.svg)
download | view image
[image/svg+xml] bytea_read_hex_encode_v18.svg (255.1K, ../../OS9PR01MB15185B278E343A9BA5F0F6AB19700A@OS9PR01MB15185.jpnprd01.prod.outlook.com/8-bytea_read_hex_encode_v18.svg)
download | view image
[image/svg+xml] bytea_write_hex_decode_sve.svg (325.4K, ../../OS9PR01MB15185B278E343A9BA5F0F6AB19700A@OS9PR01MB15185.jpnprd01.prod.outlook.com/9-bytea_write_hex_decode_sve.svg)
download | view image
[image/svg+xml] bytea_write_hex_decode_v18.svg (280.4K, ../../OS9PR01MB15185B278E343A9BA5F0F6AB19700A@OS9PR01MB15185.jpnprd01.prod.outlook.com/10-bytea_write_hex_decode_v18.svg)
download | view image
^ permalink raw reply [nested|flat] 30+ messages in thread
* Re: [PATCH] Hex-coding optimizations using SVE on ARM.
2025-06-04 13:47 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-06-09 09:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-07-07 10:41 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-03 11:11 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-04 06:20 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-04 14:55 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
@ 2025-09-11 03:12 ` Nathan Bossart <[email protected]>
2025-09-11 10:43 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
0 siblings, 1 reply; 30+ messages in thread
From: Nathan Bossart @ 2025-09-11 03:12 UTC (permalink / raw)
To: [email protected] <[email protected]>; +Cc: John Naylor <[email protected]>; Tom Lane <[email protected]>; David Rowley <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>
On Thu, Sep 04, 2025 at 02:55:50PM +0000, [email protected] wrote:
>> I see that there was some discussion about a Neon implementation upthread,
>> but I'm not sure we concluded anything. For popcount, we first added a
>> Neon version before adding the SVE version, which required more complicated
>> configure/runtime checks. Presumably Neon is available on more hardware
>> than SVE, so that could be a good place to start here, too.
>
> We have added the Neon versions of hex encode/decode.
Thanks. I noticed that this stuff is simple enough that we can use
port/simd.h (with a few added functions). This is especially nice because
it takes care of x86, too. The performance gains look similar to what you
reported for v6:
arm
buf | HEAD | patch | % diff
-------+-------+-------+--------
16 | 13 | 6 | 54
64 | 34 | 9 | 74
256 | 93 | 25 | 73
1024 | 281 | 78 | 72
4096 | 1086 | 227 | 79
16384 | 4382 | 927 | 79
65536 | 17455 | 3608 | 79
x86
buf | HEAD | patch | % diff
-------+-------+-------+--------
16 | 10 | 7 | 30
64 | 29 | 9 | 69
256 | 81 | 21 | 74
1024 | 286 | 66 | 77
4096 | 1106 | 253 | 77
16384 | 4383 | 980 | 78
65536 | 17491 | 3886 | 78
I've only modified hex_encode() for now, but I'm optimistic that we can do
something similar for hex_decode().
--
nathan
^ permalink raw reply [nested|flat] 30+ messages in thread
* Re: [PATCH] Hex-coding optimizations using SVE on ARM.
2025-06-04 13:47 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-06-09 09:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-07-07 10:41 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-03 11:11 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-04 06:20 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-04 14:55 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 03:12 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
@ 2025-09-11 10:43 ` [email protected] <[email protected]>
2025-09-11 15:24 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
0 siblings, 1 reply; 30+ messages in thread
From: [email protected] @ 2025-09-11 10:43 UTC (permalink / raw)
To: Nathan Bossart <[email protected]>; +Cc: John Naylor <[email protected]>; Tom Lane <[email protected]>; David Rowley <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>
> Thanks. I noticed that this stuff is simple enough that we can use
> port/simd.h (with a few added functions). This is especially nice because
> it takes care of x86, too. The performance gains look similar to what you
> reported for v6:
This looks good, much cleaner.
One possible improvement would be to use a vectorized table lookup instead of compare and add. I compared v6 and v7 Neon versions, and v6 is always faster.
I’m not sure if SSE2 has a table lookup similar to Neon.
arm - m7g.4xlarge
buf | v6-Neon| v7-Neon| % diff
-------+--------+--------+--------
64 | 6.16 | 8.57 | 28.07
128 | 11.37 | 15.77 | 27.87
256 | 18.54 | 30.28 | 38.77
512 | 33.98 | 62.15 | 45.33
1024 | 64.46 | 117.55 | 45.16
2048 | 124.28 | 254.86 | 51.24
4096 | 243.47 | 509.23 | 52.19
8192 | 487.34 | 953.81 | 48.91
-----
Chiranmoy
^ permalink raw reply [nested|flat] 30+ messages in thread
* Re: [PATCH] Hex-coding optimizations using SVE on ARM.
2025-06-04 13:47 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-06-09 09:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-07-07 10:41 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-03 11:11 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-04 06:20 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-04 14:55 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 03:12 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 10:43 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
@ 2025-09-11 15:24 ` Nathan Bossart <[email protected]>
2025-09-11 15:32 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Tom Lane <[email protected]>
0 siblings, 1 reply; 30+ messages in thread
From: Nathan Bossart @ 2025-09-11 15:24 UTC (permalink / raw)
To: [email protected] <[email protected]>; +Cc: John Naylor <[email protected]>; Tom Lane <[email protected]>; David Rowley <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>
On Thu, Sep 11, 2025 at 10:43:56AM +0000, [email protected] wrote:
> One possible improvement would be to use a vectorized table lookup
> instead of compare and add. I compared v6 and v7 Neon versions, and v6 is
> always faster. I’m not sure if SSE2 has a table lookup similar to Neon.
I'm not finding a simple way to do that kind of table lookup in SSE2. Part
of the reason v6 performs better is because you've unrolled the loop to
process 2 vector's worth of input data in each iteration. This trades
performance with smaller inputs for gains with larger ones. But even if I
do something similar for v7, v6 still wins most of the time.
My current philosophy with this stuff is to favor simplicity,
maintainability, portability, etc. over extracting the absolute maximum
amount of performance gain, so I think we should proceed with the simd.h
approach. But I'm curious how others feel about this.
v8 is an attempt to fix the casting error on MSVC.
--
nathan
^ permalink raw reply [nested|flat] 30+ messages in thread
* Re: [PATCH] Hex-coding optimizations using SVE on ARM.
2025-06-04 13:47 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-06-09 09:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-07-07 10:41 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-03 11:11 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-04 06:20 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-04 14:55 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 03:12 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 10:43 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 15:24 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
@ 2025-09-11 15:32 ` Tom Lane <[email protected]>
2025-09-12 18:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
0 siblings, 1 reply; 30+ messages in thread
From: Tom Lane @ 2025-09-11 15:32 UTC (permalink / raw)
To: Nathan Bossart <[email protected]>; +Cc: [email protected] <[email protected]>; John Naylor <[email protected]>; David Rowley <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>
Nathan Bossart <[email protected]> writes:
> My current philosophy with this stuff is to favor simplicity,
> maintainability, portability, etc. over extracting the absolute maximum
> amount of performance gain, so I think we should proceed with the simd.h
> approach. But I'm curious how others feel about this.
+1. The maintainability aspect is critical over the long run.
Also, there's a very real danger of optimizing for the specific
hardware and test case you are working with, leading to actually
worse performance with future hardware.
regards, tom lane
^ permalink raw reply [nested|flat] 30+ messages in thread
* Re: [PATCH] Hex-coding optimizations using SVE on ARM.
2025-06-04 13:47 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-06-09 09:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-07-07 10:41 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-03 11:11 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-04 06:20 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-04 14:55 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 03:12 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 10:43 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 15:24 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 15:32 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Tom Lane <[email protected]>
@ 2025-09-12 18:49 ` [email protected] <[email protected]>
2025-09-12 21:30 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
0 siblings, 1 reply; 30+ messages in thread
From: [email protected] @ 2025-09-12 18:49 UTC (permalink / raw)
To: Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; +Cc: John Naylor <[email protected]>; David Rowley <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>
> My current philosophy with this stuff is to favor simplicity,
> maintainability, portability, etc. over extracting the absolute maximum
> amount of performance gain, so I think we should proceed with the simd.h
> approach. But I'm curious how others feel about this.
> +1. The maintainability aspect is critical over the long run.
> Also, there's a very real danger of optimizing for the specific
> hardware and test case you are working with, leading to actually
> worse performance with future hardware.
Using simd.h does make it easier to maintain.
Is there a plan to upgrade simd.h to use SSE4 or SSSE3 in the future?
Since SSE2 is much older, it lacks some of the more specialized intrinsics.
For example, vectorized table lookup can be implemented via [0], and
it’s available in SSSE3 and later x86 instruction sets.
[0] https://www.felixcloutier.com/x86/pshufb
-----
Chiranmoy
^ permalink raw reply [nested|flat] 30+ messages in thread
* Re: [PATCH] Hex-coding optimizations using SVE on ARM.
2025-06-04 13:47 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-06-09 09:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-07-07 10:41 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-03 11:11 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-04 06:20 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-04 14:55 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 03:12 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 10:43 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 15:24 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 15:32 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Tom Lane <[email protected]>
2025-09-12 18:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
@ 2025-09-12 21:30 ` Nathan Bossart <[email protected]>
2025-09-22 20:05 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
0 siblings, 1 reply; 30+ messages in thread
From: Nathan Bossart @ 2025-09-12 21:30 UTC (permalink / raw)
To: [email protected] <[email protected]>; +Cc: Tom Lane <[email protected]>; John Naylor <[email protected]>; David Rowley <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>
On Fri, Sep 12, 2025 at 06:49:01PM +0000, [email protected] wrote:
> Using simd.h does make it easier to maintain. Is there a plan to upgrade
> simd.h to use SSE4 or SSSE3 in the future? Since SSE2 is much older, it
> lacks some of the more specialized intrinsics. For example, vectorized
> table lookup can be implemented via [0], and it’s available in SSSE3 and
> later x86 instruction sets.
There have been a couple of discussions about the possibility of requiring
x86-64-v2 for Postgres, but I'm not aware of any serious efforts in that
area.
I've attached a new version of the patch with a simd.h version of
hex_decode(). Here are the numbers:
arm
buf | HEAD | patch | % diff
-------+-------+-------+--------
16 | 22 | 23 | -5
64 | 61 | 23 | 62
256 | 158 | 47 | 70
1024 | 542 | 122 | 77
4096 | 2103 | 429 | 80
16384 | 8548 | 1673 | 80
65536 | 34663 | 6738 | 81
x86
buf | HEAD | patch | % diff
-------+-------+-------+--------
16 | 13 | 14 | -8
64 | 42 | 15 | 64
256 | 126 | 42 | 67
1024 | 461 | 149 | 68
4096 | 1802 | 576 | 68
16384 | 7166 | 2280 | 68
65536 | 28625 | 9108 | 68
A couple of notes:
* For hex_decode(), we just give up on the SIMD path and fall back on the
scalar path as soon as we see anything outside [0-9A-Za-z]. I suspect
this might introduce a regression for inputs of ~32 to ~64 bytes that
include whitespace (which must be skipped) or invalid characters, but I
don't whether those inputs are common or whether we care.
* The code makes some assumptions about endianness that might not be true
everywhere, but I've yet to dig into this further.
--
nathan
^ permalink raw reply [nested|flat] 30+ messages in thread
* Re: [PATCH] Hex-coding optimizations using SVE on ARM.
2025-06-04 13:47 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-06-09 09:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-07-07 10:41 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-03 11:11 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-04 06:20 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-04 14:55 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 03:12 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 10:43 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 15:24 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 15:32 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Tom Lane <[email protected]>
2025-09-12 18:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-12 21:30 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
@ 2025-09-22 20:05 ` Nathan Bossart <[email protected]>
2025-09-23 19:02 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
0 siblings, 1 reply; 30+ messages in thread
From: Nathan Bossart @ 2025-09-22 20:05 UTC (permalink / raw)
To: [email protected] <[email protected]>; +Cc: Tom Lane <[email protected]>; John Naylor <[email protected]>; David Rowley <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>
On Fri, Sep 12, 2025 at 04:30:21PM -0500, Nathan Bossart wrote:
> I've attached a new version of the patch with a simd.h version of
> hex_decode(). Here are the numbers:
I was able to improve the hex_decode() implementation a bit.
arm
buf | HEAD | patch | % diff
-------+-------+-------+--------
16 | 11 | 11 | 0
64 | 38 | 7 | 82
256 | 133 | 18 | 86
1024 | 513 | 67 | 87
4096 | 2037 | 271 | 87
16384 | 8326 | 1103 | 87
65536 | 34550 | 4475 | 87
x86
buf | HEAD | patch | % diff
-------+-------+-------+--------
16 | 8 | 9 | -13
64 | 38 | 7 | 82
256 | 121 | 24 | 80
1024 | 457 | 91 | 80
4096 | 1797 | 356 | 80
16384 | 7161 | 1411 | 80
65536 | 28620 | 5632 | 80
--
nathan
^ permalink raw reply [nested|flat] 30+ messages in thread
* Re: [PATCH] Hex-coding optimizations using SVE on ARM.
2025-06-04 13:47 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-06-09 09:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-07-07 10:41 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-03 11:11 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-04 06:20 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-04 14:55 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 03:12 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 10:43 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 15:24 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 15:32 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Tom Lane <[email protected]>
2025-09-12 18:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-12 21:30 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-22 20:05 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
@ 2025-09-23 19:02 ` Nathan Bossart <[email protected]>
2025-09-24 03:59 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
0 siblings, 1 reply; 30+ messages in thread
From: Nathan Bossart @ 2025-09-23 19:02 UTC (permalink / raw)
To: [email protected] <[email protected]>; +Cc: Tom Lane <[email protected]>; John Naylor <[email protected]>; David Rowley <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>
On Mon, Sep 22, 2025 at 03:05:44PM -0500, Nathan Bossart wrote:
> I was able to improve the hex_decode() implementation a bit.
I took a closer look at how hex_decode() performs with smaller inputs.
There are some small regressions, so I tried fixing them by adding the
following to the beginning of the function:
if (likely(tail_idx == 0))
return hex_decode_safe_scalar(src, len, dst, escontext);
This helped a little, but it mostly just slowed things down for larger
inputs on AArch64:
arm
buf | HEAD | patch | fix
-------+-------+-------+-------
2 | 4 | 6 | 4
4 | 6 | 7 | 7
8 | 8 | 8 | 8
16 | 11 | 12 | 11
32 | 18 | 5 | 6
64 | 38 | 7 | 8
256 | 134 | 18 | 24
1024 | 514 | 67 | 100
4096 | 2072 | 280 | 389
16384 | 8409 | 1126 | 1537
65536 | 34704 | 4498 | 6128
x86
buf | HEAD | patch | fix
-------+-------+-------+-------
2 | 2 | 2 | 2
4 | 3 | 3 | 3
8 | 4 | 4 | 4
16 | 8 | 9 | 8
32 | 23 | 5 | 5
64 | 37 | 7 | 7
256 | 122 | 24 | 24
1024 | 457 | 91 | 92
4096 | 1798 | 357 | 358
16384 | 7161 | 1411 | 1416
65536 | 28621 | 5630 | 5653
I didn't do this test for hex_encode(), but I'd expect it to follow a
similar pattern. I'm tempted to suggest that these regressions are within
tolerable levels and to forge on with v10. In any case, IMHO this patch is
approaching committable quality, so I'd be grateful for any feedback.
--
nathan
^ permalink raw reply [nested|flat] 30+ messages in thread
* Re: [PATCH] Hex-coding optimizations using SVE on ARM.
2025-06-04 13:47 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-06-09 09:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-07-07 10:41 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-03 11:11 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-04 06:20 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-04 14:55 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 03:12 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 10:43 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 15:24 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 15:32 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Tom Lane <[email protected]>
2025-09-12 18:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-12 21:30 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-22 20:05 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-23 19:02 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
@ 2025-09-24 03:59 ` John Naylor <[email protected]>
2025-09-24 21:40 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
0 siblings, 1 reply; 30+ messages in thread
From: John Naylor @ 2025-09-24 03:59 UTC (permalink / raw)
To: Nathan Bossart <[email protected]>; +Cc: [email protected] <[email protected]>; Tom Lane <[email protected]>; David Rowley <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>
On Wed, Sep 24, 2025 at 2:02 AM Nathan Bossart <[email protected]> wrote:
>
> On Mon, Sep 22, 2025 at 03:05:44PM -0500, Nathan Bossart wrote:
> > I was able to improve the hex_decode() implementation a bit.
>
> I took a closer look at how hex_decode() performs with smaller inputs.
> There are some small regressions, so I tried fixing them by adding the
> following to the beginning of the function:
>
> if (likely(tail_idx == 0))
> return hex_decode_safe_scalar(src, len, dst, escontext);
>
> This helped a little, but it mostly just slowed things down for larger
> inputs on AArch64:
> I didn't do this test for hex_encode(), but I'd expect it to follow a
> similar pattern. I'm tempted to suggest that these regressions are within
> tolerable levels and to forge on with v10.
My first thought is, I'd hazard a guess that short byteas are much
less common than short strings.
My second thought is, the decode case is not that critical. From the
end-to-end tests above, the speed of the decode case had a relatively
small global effect compared to the encode case (Perhaps because reads
are cheaper than writes).
+ if (unlikely(!hex_decode_simd_helper(srcv, &dstv1)))
+ break;
But if you really want to do something here, sprinkling "(un)likely"'s
here seems like solving the wrong problem (even if they make any
difference), since the early return is optimizing for exceptional
conditions. In other places (cf. the UTF8 string verifier), we
accumulate errors, and only if we have them at the end do we restart
from the beginning with the slow error-checking path that can show the
user the offending input.
> In any case, IMHO this patch is
> approaching committable quality, so I'd be grateful for any feedback.
+vector8_sssub(const Vector8 v1, const Vector8 v2)
It's hard to parse "sss", so maybe we can borrow an Intel-ism and use
"iss" for the signed case?
+/* vector manipulation */
+#ifndef USE_NO_SIMD
+static inline Vector8 vector8_interleave_low(const Vector8 v1, const
Vector8 v2);
+static inline Vector8 vector8_interleave_high(const Vector8 v1, const
Vector8 v2);
+static inline Vector8 vector8_pack_16(const Vector8 v1, const Vector8 v2);
+static inline Vector32 vector32_shift_left_nibble(const Vector32 v1);
+static inline Vector32 vector32_shift_right_nibble(const Vector32 v1);
+static inline Vector32 vector32_shift_right_byte(const Vector32 v1);
Do we need declarations for these? I recall that the existing
declarations are there for functions that are also used internally.
The nibble/byte things are rather specific. Wouldn't it be more
logical to expose the already-generic shift operations and let the
caller say by how much? Or does the compiler refuse because the
intrinsic doesn't get an immediate value? Some are like that, but I'm
not sure about these. If so, that's annoying and I wonder if there's a
workaround.
+vector8_has_ge(const Vector8 v, const uint8 c)
+{
+#ifdef USE_SSE2
+ Vector8 umax = _mm_max_epu8(v, vector8_broadcast(c));
+ Vector8 cmpe = _mm_cmpeq_epi8(umax, v);
+
+ return vector8_is_highbit_set(cmpe);
We take pains to avoid signed comparison on unsigned input for the
"le" case, and I don't see why it's okay here.
Do the regression tests have long enough cases that test exceptional
paths, like invalid bytes and embedded whitespace? If not, we need
some.
--
John Naylor
Amazon Web Services
^ permalink raw reply [nested|flat] 30+ messages in thread
* Re: [PATCH] Hex-coding optimizations using SVE on ARM.
2025-06-04 13:47 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-06-09 09:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-07-07 10:41 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-03 11:11 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-04 06:20 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-04 14:55 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 03:12 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 10:43 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 15:24 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 15:32 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Tom Lane <[email protected]>
2025-09-12 18:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-12 21:30 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-22 20:05 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-23 19:02 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-24 03:59 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
@ 2025-09-24 21:40 ` Nathan Bossart <[email protected]>
2025-09-25 14:16 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
0 siblings, 1 reply; 30+ messages in thread
From: Nathan Bossart @ 2025-09-24 21:40 UTC (permalink / raw)
To: John Naylor <[email protected]>; +Cc: [email protected] <[email protected]>; Tom Lane <[email protected]>; David Rowley <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>
On Wed, Sep 24, 2025 at 10:59:38AM +0700, John Naylor wrote:
> + if (unlikely(!hex_decode_simd_helper(srcv, &dstv1)))
> + break;
>
> But if you really want to do something here, sprinkling "(un)likely"'s
> here seems like solving the wrong problem (even if they make any
> difference), since the early return is optimizing for exceptional
> conditions. In other places (cf. the UTF8 string verifier), we
> accumulate errors, and only if we have them at the end do we restart
> from the beginning with the slow error-checking path that can show the
> user the offending input.
I switched to an accumulator approach in v11.
> +vector8_sssub(const Vector8 v1, const Vector8 v2)
>
> It's hard to parse "sss", so maybe we can borrow an Intel-ism and use
> "iss" for the signed case?
Done.
> +/* vector manipulation */
> +#ifndef USE_NO_SIMD
> +static inline Vector8 vector8_interleave_low(const Vector8 v1, const
> Vector8 v2);
> +static inline Vector8 vector8_interleave_high(const Vector8 v1, const
> Vector8 v2);
> +static inline Vector8 vector8_pack_16(const Vector8 v1, const Vector8 v2);
> +static inline Vector32 vector32_shift_left_nibble(const Vector32 v1);
> +static inline Vector32 vector32_shift_right_nibble(const Vector32 v1);
> +static inline Vector32 vector32_shift_right_byte(const Vector32 v1);
>
> Do we need declarations for these? I recall that the existing
> declarations are there for functions that are also used internally.
Removed.
> The nibble/byte things are rather specific. Wouldn't it be more
> logical to expose the already-generic shift operations and let the
> caller say by how much? Or does the compiler refuse because the
> intrinsic doesn't get an immediate value? Some are like that, but I'm
> not sure about these. If so, that's annoying and I wonder if there's a
> workaround.
Yeah, the compiler refuses unless the value is an integer literal. I
thought of using a switch statement to cover all the values used in-tree,
but I didn't like that, either.
> +vector8_has_ge(const Vector8 v, const uint8 c)
> +{
> +#ifdef USE_SSE2
> + Vector8 umax = _mm_max_epu8(v, vector8_broadcast(c));
> + Vector8 cmpe = _mm_cmpeq_epi8(umax, v);
> +
> + return vector8_is_highbit_set(cmpe);
>
> We take pains to avoid signed comparison on unsigned input for the
> "le" case, and I don't see why it's okay here.
_mm_max_epu8() does unsigned comparisons, I think...
> Do the regression tests have long enough cases that test exceptional
> paths, like invalid bytes and embedded whitespace? If not, we need
> some.
Added.
I've also fixed builds on gcc/arm64, as discussed elsewhere [0]. Here are
the current numbers on my laptop:
arm
buf | HEAD | patch | % diff
-------+-------+-------+--------
2 | 4 | 4 | 0
4 | 6 | 6 | 0
8 | 8 | 8 | 0
16 | 11 | 12 | -9
32 | 18 | 5 | 72
64 | 38 | 6 | 84
256 | 134 | 17 | 87
1024 | 513 | 63 | 88
4096 | 2081 | 262 | 87
16384 | 8524 | 1058 | 88
65536 | 34731 | 4224 | 88
[0] https://postgr.es/m/aNQtN89VW8z-yo3B%40nathan
--
nathan
^ permalink raw reply [nested|flat] 30+ messages in thread
* Re: [PATCH] Hex-coding optimizations using SVE on ARM.
2025-06-04 13:47 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-06-09 09:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-07-07 10:41 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-03 11:11 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-04 06:20 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-04 14:55 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 03:12 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 10:43 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 15:24 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 15:32 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Tom Lane <[email protected]>
2025-09-12 18:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-12 21:30 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-22 20:05 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-23 19:02 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-24 03:59 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-24 21:40 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
@ 2025-09-25 14:16 ` John Naylor <[email protected]>
2025-09-25 18:50 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
0 siblings, 1 reply; 30+ messages in thread
From: John Naylor @ 2025-09-25 14:16 UTC (permalink / raw)
To: Nathan Bossart <[email protected]>; +Cc: [email protected] <[email protected]>; Tom Lane <[email protected]>; David Rowley <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>
On Thu, Sep 25, 2025 at 4:40 AM Nathan Bossart <[email protected]> wrote:
>
> On Wed, Sep 24, 2025 at 10:59:38AM +0700, John Naylor wrote:
> > + if (unlikely(!hex_decode_simd_helper(srcv, &dstv1)))
> > + break;
> >
> > But if you really want to do something here, sprinkling "(un)likely"'s
> > here seems like solving the wrong problem (even if they make any
> > difference), since the early return is optimizing for exceptional
> > conditions. In other places (cf. the UTF8 string verifier), we
> > accumulate errors, and only if we have them at the end do we restart
> > from the beginning with the slow error-checking path that can show the
> > user the offending input.
>
> I switched to an accumulator approach in v11.
Looks good to me.
+ if (unlikely(!success))
+ i = 0;
This is after the main loop exits, and the cold path is literally one
instruction, so the motivation is not apparent to me.
> > The nibble/byte things are rather specific. Wouldn't it be more
> > logical to expose the already-generic shift operations and let the
> > caller say by how much? Or does the compiler refuse because the
> > intrinsic doesn't get an immediate value? Some are like that, but I'm
> > not sure about these. If so, that's annoying and I wonder if there's a
> > workaround.
>
> Yeah, the compiler refuses unless the value is an integer literal. I
> thought of using a switch statement to cover all the values used in-tree,
> but I didn't like that, either.
Neither option is great, but I mildly lean towards keeping it internal
with "switch" or whatever: By putting the burden of specifying shift
amounts on separately named functions we run a risk of combinatorial
explosion in function names.
If you feel otherwise, I'd at least use actual numbers:
"shift_left_nibble" is an awkward way to say "shift left by 4 bits"
anyway, and also after "byte" and "nibble" there are not many good
English words to convey the operand amount. It's very possible that
needing other shift amounts will never come up, though.
> > +vector8_has_ge(const Vector8 v, const uint8 c)
> > +{
> > +#ifdef USE_SSE2
> > + Vector8 umax = _mm_max_epu8(v, vector8_broadcast(c));
> > + Vector8 cmpe = _mm_cmpeq_epi8(umax, v);
> > +
> > + return vector8_is_highbit_set(cmpe);
> >
> > We take pains to avoid signed comparison on unsigned input for the
> > "le" case, and I don't see why it's okay here.
>
> _mm_max_epu8() does unsigned comparisons, I think...
Ah, I confused myself about what the LE case was avoiding, namely
signed LE, not signed equality on something else.
(Separately, now I'm wondering if we can do the same for
vector8_has_le since _mm_min_epu8 and vminvq_u8 both exist, and that
would allow getting rid of )
> > Do the regression tests have long enough cases that test exceptional
> > paths, like invalid bytes and embedded whitespace? If not, we need
> > some.
>
> Added.
Seems comprehensive enough at a glance.
Other comments:
+ * back together to form the final hex-encoded string. It might be
+ * possible to squeeze out a little more gain by manually unrolling the
+ * loop, but for now we don't bother.
My position (and I think the community agrees) is that manual
unrolling is a rare desperation move that has to be justified, so we
don't need to mention its lack.
+ * Some compilers are picky about casts to the same underlying type, and others
+ * are picky about implicit conversions with vector types. This function does
+ * the same thing as vector32_broadcast(), but it returns a Vector8 and is
+ * carefully crafted to avoid compiler indigestion.
+ */
+#ifndef USE_NO_SIMD
+static inline Vector8
+vector8_broadcast_u32(const uint32 c)
+{
+#ifdef USE_SSE2
+ return vector32_broadcast(c);
+#elif defined(USE_NEON)
+ return (Vector8) vector32_broadcast(c);
+#endif
+}
I'm ambivalent about this: The use case doesn't seem well motivated,
since I don't know why we'd actually need to both broadcast arbitrary
integers and also view the result as bytes. Setting arbitrary bytes is
what we're really doing, and would be more likely be useful in the
future (attached, only tested on x86, and I think part of the
strangeness is the endianness you mentioned above). On the other hand,
the Arm workaround results in awful generated code compared to what
you have here. Since the "set" should be hoisted out of the outer
loop, and we already rely on this pattern for vector8_highbit_mask
anyway, it might be tolerable, and we can reduce the pain with bitwise
NOT.
+/*
+ * Pack 16-bit elements in the given vectors into a single vector of 8-bit
+ * elements. NB: The upper 8-bits of each 16-bit element must be zeros, else
+ * this will produce different results on different architectures.
+ */
v10 asserted this requirement -- that still seems like a good thing?
--
John Naylor
Amazon Web Services
diff --git a/src/backend/utils/adt/encode.c b/src/backend/utils/adt/encode.c
index 7ba92c2c481..eb932d4bd8a 100644
--- a/src/backend/utils/adt/encode.c
+++ b/src/backend/utils/adt/encode.c
@@ -317,6 +317,9 @@ static inline bool
hex_decode_simd_helper(const Vector8 src, Vector8 *dst)
{
Vector8 sub;
+ // TODO: set one and use bitwise NOT for the other
+ Vector8 maskupper = vector8_set(0xff, 0, 0xff, 0, 0xff, 0, 0xff, 0, 0xff, 0, 0xff, 0, 0xff, 0, 0xff, 0);
+ Vector8 masklower = vector8_set(0, 0xff, 0, 0xff, 0, 0xff, 0, 0xff, 0, 0xff, 0, 0xff, 0, 0xff, 0, 0xff);
Vector8 msk;
bool ret;
@@ -334,9 +337,9 @@ hex_decode_simd_helper(const Vector8 src, Vector8 *dst)
*dst = vector8_issub(src, sub);
ret = !vector8_has_ge(*dst, 0x10);
- msk = vector8_and(*dst, vector8_broadcast_u32(0xff00ff00));
+ msk = vector8_and(*dst, masklower);
msk = vector8_shift_right_byte(msk);
- *dst = vector8_and(*dst, vector8_broadcast_u32(0x00ff00ff));
+ *dst = vector8_and(*dst, maskupper);
*dst = vector8_shift_left_nibble(*dst);
*dst = vector8_or(*dst, msk);
return ret;
diff --git a/src/include/port/simd.h b/src/include/port/simd.h
index 531d8b8b6d1..56e810ce081 100644
--- a/src/include/port/simd.h
+++ b/src/include/port/simd.h
@@ -101,6 +101,33 @@ static inline Vector8 vector8_min(const Vector8 v1, const Vector8 v2);
static inline Vector32 vector32_eq(const Vector32 v1, const Vector32 v2);
#endif
+/*
+ * Populate a vector element-wise with the arguments.
+ */
+#ifndef USE_NO_SIMD
+#if defined(USE_NEON)
+// from a patch by Thomas Munro
+static inline Vector8
+vector8_set(uint8 v0, uint8 v1, uint8 v2, uint8 v3,
+ uint8 v4, uint8 v5, uint8 v6, uint8 v7,
+ uint8 v8, uint8 v9, uint8 v10, uint8 v11,
+ uint8 v12, uint8 v13, uint8 v14, uint8 v15)
+{
+ uint8 pg_attribute_aligned(16) values[16] = {
+ v0, v1, v2, v3,
+ v4, v5, v6, v7,
+ v8, v9, v10, v11,
+ v12, v13, v14, v15
+ };
+ return vld1q_u8(values);
+}
+#elif defined(USE_SSE2)
+#ifndef vector8_set
+#define vector8_set(...) _mm_setr_epi8(__VA_ARGS__)
+#endif
+#endif
+#endif /* ! USE_NO_SIMD */
+
/*
* Load a chunk of memory into the given vector.
*/
@@ -368,6 +395,7 @@ vector8_highbit_mask(const Vector8 v)
* returns a uint64, making it inconvenient to combine mask values from
* multiple vectors.
*/
+ // TODO: use vector8_set
static const uint8 mask[16] = {
1 << 0, 1 << 1, 1 << 2, 1 << 3,
1 << 4, 1 << 5, 1 << 6, 1 << 7,
Attachments:
[text/plain] vector8_set.patch.txt (2.4K, ../../CANWCAZb9FhrWf_2OzwXTK_sQeXm0nPz2joimukB-7pVF0SXxXw@mail.gmail.com/2-vector8_set.patch.txt)
download | inline diff:
diff --git a/src/backend/utils/adt/encode.c b/src/backend/utils/adt/encode.c
index 7ba92c2c481..eb932d4bd8a 100644
--- a/src/backend/utils/adt/encode.c
+++ b/src/backend/utils/adt/encode.c
@@ -317,6 +317,9 @@ static inline bool
hex_decode_simd_helper(const Vector8 src, Vector8 *dst)
{
Vector8 sub;
+ // TODO: set one and use bitwise NOT for the other
+ Vector8 maskupper = vector8_set(0xff, 0, 0xff, 0, 0xff, 0, 0xff, 0, 0xff, 0, 0xff, 0, 0xff, 0, 0xff, 0);
+ Vector8 masklower = vector8_set(0, 0xff, 0, 0xff, 0, 0xff, 0, 0xff, 0, 0xff, 0, 0xff, 0, 0xff, 0, 0xff);
Vector8 msk;
bool ret;
@@ -334,9 +337,9 @@ hex_decode_simd_helper(const Vector8 src, Vector8 *dst)
*dst = vector8_issub(src, sub);
ret = !vector8_has_ge(*dst, 0x10);
- msk = vector8_and(*dst, vector8_broadcast_u32(0xff00ff00));
+ msk = vector8_and(*dst, masklower);
msk = vector8_shift_right_byte(msk);
- *dst = vector8_and(*dst, vector8_broadcast_u32(0x00ff00ff));
+ *dst = vector8_and(*dst, maskupper);
*dst = vector8_shift_left_nibble(*dst);
*dst = vector8_or(*dst, msk);
return ret;
diff --git a/src/include/port/simd.h b/src/include/port/simd.h
index 531d8b8b6d1..56e810ce081 100644
--- a/src/include/port/simd.h
+++ b/src/include/port/simd.h
@@ -101,6 +101,33 @@ static inline Vector8 vector8_min(const Vector8 v1, const Vector8 v2);
static inline Vector32 vector32_eq(const Vector32 v1, const Vector32 v2);
#endif
+/*
+ * Populate a vector element-wise with the arguments.
+ */
+#ifndef USE_NO_SIMD
+#if defined(USE_NEON)
+// from a patch by Thomas Munro
+static inline Vector8
+vector8_set(uint8 v0, uint8 v1, uint8 v2, uint8 v3,
+ uint8 v4, uint8 v5, uint8 v6, uint8 v7,
+ uint8 v8, uint8 v9, uint8 v10, uint8 v11,
+ uint8 v12, uint8 v13, uint8 v14, uint8 v15)
+{
+ uint8 pg_attribute_aligned(16) values[16] = {
+ v0, v1, v2, v3,
+ v4, v5, v6, v7,
+ v8, v9, v10, v11,
+ v12, v13, v14, v15
+ };
+ return vld1q_u8(values);
+}
+#elif defined(USE_SSE2)
+#ifndef vector8_set
+#define vector8_set(...) _mm_setr_epi8(__VA_ARGS__)
+#endif
+#endif
+#endif /* ! USE_NO_SIMD */
+
/*
* Load a chunk of memory into the given vector.
*/
@@ -368,6 +395,7 @@ vector8_highbit_mask(const Vector8 v)
* returns a uint64, making it inconvenient to combine mask values from
* multiple vectors.
*/
+ // TODO: use vector8_set
static const uint8 mask[16] = {
1 << 0, 1 << 1, 1 << 2, 1 << 3,
1 << 4, 1 << 5, 1 << 6, 1 << 7,
^ permalink raw reply [nested|flat] 30+ messages in thread
* Re: [PATCH] Hex-coding optimizations using SVE on ARM.
2025-06-04 13:47 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-06-09 09:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-07-07 10:41 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-03 11:11 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-04 06:20 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-04 14:55 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 03:12 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 10:43 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 15:24 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 15:32 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Tom Lane <[email protected]>
2025-09-12 18:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-12 21:30 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-22 20:05 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-23 19:02 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-24 03:59 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-24 21:40 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-25 14:16 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
@ 2025-09-25 18:50 ` Nathan Bossart <[email protected]>
2025-09-29 08:45 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
0 siblings, 1 reply; 30+ messages in thread
From: Nathan Bossart @ 2025-09-25 18:50 UTC (permalink / raw)
To: John Naylor <[email protected]>; +Cc: [email protected] <[email protected]>; Tom Lane <[email protected]>; David Rowley <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>
On Thu, Sep 25, 2025 at 09:16:35PM +0700, John Naylor wrote:
> + if (unlikely(!success))
> + i = 0;
>
> This is after the main loop exits, and the cold path is literally one
> instruction, so the motivation is not apparent to me.
Removed. I was thinking about smaller inputs when I added this, but it
probably makes little difference.
>> Yeah, the compiler refuses unless the value is an integer literal. I
>> thought of using a switch statement to cover all the values used in-tree,
>> but I didn't like that, either.
>
> Neither option is great, but I mildly lean towards keeping it internal
> with "switch" or whatever: By putting the burden of specifying shift
> amounts on separately named functions we run a risk of combinatorial
> explosion in function names.
Done.
> (Separately, now I'm wondering if we can do the same for
> vector8_has_le since _mm_min_epu8 and vminvq_u8 both exist, and that
> would allow getting rid of )
I think so. I doubt there's any performance advantage, but it could be
nice for code cleanup. (I'm assuming you meant to say vector8_ssub
(renamed to vector8_ussub() in the patch) after "getting rid of.") I'll
do this in the related patch in the "couple of small patches for simd.h"
thread.
> + * back together to form the final hex-encoded string. It might be
> + * possible to squeeze out a little more gain by manually unrolling the
> + * loop, but for now we don't bother.
>
> My position (and I think the community agrees) is that manual
> unrolling is a rare desperation move that has to be justified, so we
> don't need to mention its lack.
Removed.
> + * Some compilers are picky about casts to the same underlying type, and others
> + * are picky about implicit conversions with vector types. This function does
> + * the same thing as vector32_broadcast(), but it returns a Vector8 and is
> + * carefully crafted to avoid compiler indigestion.
> + */
> +#ifndef USE_NO_SIMD
> +static inline Vector8
> +vector8_broadcast_u32(const uint32 c)
> +{
> +#ifdef USE_SSE2
> + return vector32_broadcast(c);
> +#elif defined(USE_NEON)
> + return (Vector8) vector32_broadcast(c);
> +#endif
> +}
>
> I'm ambivalent about this: The use case doesn't seem well motivated,
> since I don't know why we'd actually need to both broadcast arbitrary
> integers and also view the result as bytes. Setting arbitrary bytes is
> what we're really doing, and would be more likely be useful in the
> future (attached, only tested on x86, and I think part of the
> strangeness is the endianness you mentioned above). On the other hand,
> the Arm workaround results in awful generated code compared to what
> you have here. Since the "set" should be hoisted out of the outer
> loop, and we already rely on this pattern for vector8_highbit_mask
> anyway, it might be tolerable, and we can reduce the pain with bitwise
> NOT.
I think I disagree on this one. We're not broadcasting arbitrary bytes for
every vector element, we're broadcasting a patten of bytes that happens to
be wider than the element size. I would expect this to be a relatively
common use-case. Furthermore, the "set" API is closely tethered to the
vector size, which is fine for SSE2/Neon but may not work down the road
(not to mention the USE_NO_SIMD path). Also, the bitwise NOT approach
won't work because we need to use 0x0f000f00 and 0x000f000f to avoid
angering the assertion in vector8_pack_16(), as mentioned below.
> +/*
> + * Pack 16-bit elements in the given vectors into a single vector of 8-bit
> + * elements. NB: The upper 8-bits of each 16-bit element must be zeros, else
> + * this will produce different results on different architectures.
> + */
>
> v10 asserted this requirement -- that still seems like a good thing?
I had removed that because I worried the accumulator approach would cause
it to fail (it does), but looking again, that's easy enough to work around.
--
nathan
^ permalink raw reply [nested|flat] 30+ messages in thread
* Re: [PATCH] Hex-coding optimizations using SVE on ARM.
2025-06-04 13:47 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-06-09 09:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-07-07 10:41 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-03 11:11 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-04 06:20 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-04 14:55 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 03:12 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 10:43 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 15:24 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 15:32 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Tom Lane <[email protected]>
2025-09-12 18:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-12 21:30 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-22 20:05 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-23 19:02 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-24 03:59 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-24 21:40 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-25 14:16 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-25 18:50 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
@ 2025-09-29 08:45 ` John Naylor <[email protected]>
2025-10-02 17:33 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
0 siblings, 1 reply; 30+ messages in thread
From: John Naylor @ 2025-09-29 08:45 UTC (permalink / raw)
To: Nathan Bossart <[email protected]>; +Cc: [email protected] <[email protected]>; Tom Lane <[email protected]>; David Rowley <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>
On Fri, Sep 26, 2025 at 1:50 AM Nathan Bossart <[email protected]> wrote:
>
> On Thu, Sep 25, 2025 at 09:16:35PM +0700, John Naylor wrote:
> > (Separately, now I'm wondering if we can do the same for
> > vector8_has_le since _mm_min_epu8 and vminvq_u8 both exist, and that
> > would allow getting rid of )
>
> I think so. I doubt there's any performance advantage, but it could be
> nice for code cleanup. (I'm assuming you meant to say vector8_ssub
> (renamed to vector8_ussub() in the patch) after "getting rid of.")
Yes right, sorry. And it seems good to do such cleanup first, since it
doesn't make sense to rename something that is about to be deleted.
> I think I disagree on this one. We're not broadcasting arbitrary bytes for
> every vector element, we're broadcasting a patten of bytes that happens to
> be wider than the element size. I would expect this to be a relatively
> common use-case.
That's probably true. I'm still worried that the hack for working
around compiler pickiness (while nice enough in it's current form)
might break at some point and require awareness of compiler versions.
Hmm, for this case, we can sidestep the maintainability questions
entirely by instead using the new interleave functions to build the
masks:
vector8_interleave_low(vector8_zero(), vector8_broadcast(0x0f))
vector8_interleave_low(vector8_broadcast(0x0f), vector8_zero())
This generates identical code as v12 on Arm and is not bad on x86.
What do you think of the attached?
While looking around again, it looks like the "msk" variable isn't a
mask like the implies to me. Not sure of a better name because I'm not
sure what it represents aside from a temp variable.
+#elif defined(USE_NEON)
+ switch (i)
+ {
+ case 4:
+ return (Vector8) vshrq_n_u32((Vector32) v1, 4);
+ case 8:
+ return (Vector8) vshrq_n_u32((Vector32) v1, 8);
+ default:
+ pg_unreachable();
+ return vector8_broadcast(0);
+ }
This is just a compiler hint, so if the input is not handled I think
it will return the wrong answer rather than alerting the developer, so
we probabaly want "Assert(false)" here.
Other than that, the pack/unpack functions could use some
documentation about which parameter is low/high.
--
John Naylor
Amazon Web Services
diff --git a/src/backend/utils/adt/encode.c b/src/backend/utils/adt/encode.c
index 87126a003b3..db734481a60 100644
--- a/src/backend/utils/adt/encode.c
+++ b/src/backend/utils/adt/encode.c
@@ -315,6 +315,8 @@ static inline bool
hex_decode_simd_helper(const Vector8 src, Vector8 *dst)
{
Vector8 sub;
+ Vector8 mask_hi = vector8_interleave_low(vector8_zero(), vector8_broadcast(0x0f));
+ Vector8 mask_lo = vector8_interleave_low(vector8_broadcast(0x0f), vector8_zero());
Vector8 msk;
bool ret;
@@ -332,9 +334,9 @@ hex_decode_simd_helper(const Vector8 src, Vector8 *dst)
*dst = vector8_issub(src, sub);
ret = !vector8_has_ge(*dst, 0x10);
- msk = vector8_and(*dst, vector8_broadcast_u32(0x0f000f00));
+ msk = vector8_and(*dst, mask_hi);
msk = vector8_shift_right(msk, 8);
- *dst = vector8_and(*dst, vector8_broadcast_u32(0x000f000f));
+ *dst = vector8_and(*dst, mask_lo);
*dst = vector8_shift_left(*dst, 4);
*dst = vector8_or(*dst, msk);
return ret;
diff --git a/src/include/port/simd.h b/src/include/port/simd.h
index 0261179e9e7..e316233b7aa 100644
--- a/src/include/port/simd.h
+++ b/src/include/port/simd.h
@@ -101,6 +101,17 @@ static inline Vector8 vector8_min(const Vector8 v1, const Vector8 v2);
static inline Vector32 vector32_eq(const Vector32 v1, const Vector32 v2);
#endif
+/* return a zeroed register */
+static inline Vector8
+vector8_zero()
+{
+#if defined(USE_SSE2)
+ return _mm_setzero_si128();
+#elif defined(USE_NEON)
+ return vmovq_n_u8(0);
+#endif
+}
+
/*
* Load a chunk of memory into the given vector.
*/
@@ -170,24 +181,6 @@ vector32_broadcast(const uint32 c)
}
#endif /* ! USE_NO_SIMD */
-/*
- * Some compilers are picky about casts to the same underlying type, and others
- * are picky about implicit conversions with vector types. This function does
- * the same thing as vector32_broadcast(), but it returns a Vector8 and is
- * carefully crafted to avoid compiler indigestion.
- */
-#ifndef USE_NO_SIMD
-static inline Vector8
-vector8_broadcast_u32(const uint32 c)
-{
-#ifdef USE_SSE2
- return vector32_broadcast(c);
-#elif defined(USE_NEON)
- return (Vector8) vector32_broadcast(c);
-#endif
-}
-#endif /* ! USE_NO_SIMD */
-
/*
* Return true if any elements in the vector are equal to the given scalar.
*/
@@ -577,8 +570,9 @@ vector8_interleave_high(const Vector8 v1, const Vector8 v2)
static inline Vector8
vector8_pack_16(const Vector8 v1, const Vector8 v2)
{
- Vector32 mask PG_USED_FOR_ASSERTS_ONLY = vector32_broadcast(0xff00ff00);
+ Vector8 mask PG_USED_FOR_ASSERTS_ONLY;
+ mask = vector8_interleave_low(vector8_zero(), vector8_broadcast(0xff));
Assert(!vector8_has_ge(vector8_and(v1, mask), 1));
Assert(!vector8_has_ge(vector8_and(v2, mask), 1));
#ifdef USE_SSE2
Attachments:
[text/plain] interleave-mask.patch.txt (2.7K, ../../CANWCAZZMaxfO3CLr1_ViUZMknp_M9iJ0LJbw63E4xMRanGiSqg@mail.gmail.com/2-interleave-mask.patch.txt)
download | inline diff:
diff --git a/src/backend/utils/adt/encode.c b/src/backend/utils/adt/encode.c
index 87126a003b3..db734481a60 100644
--- a/src/backend/utils/adt/encode.c
+++ b/src/backend/utils/adt/encode.c
@@ -315,6 +315,8 @@ static inline bool
hex_decode_simd_helper(const Vector8 src, Vector8 *dst)
{
Vector8 sub;
+ Vector8 mask_hi = vector8_interleave_low(vector8_zero(), vector8_broadcast(0x0f));
+ Vector8 mask_lo = vector8_interleave_low(vector8_broadcast(0x0f), vector8_zero());
Vector8 msk;
bool ret;
@@ -332,9 +334,9 @@ hex_decode_simd_helper(const Vector8 src, Vector8 *dst)
*dst = vector8_issub(src, sub);
ret = !vector8_has_ge(*dst, 0x10);
- msk = vector8_and(*dst, vector8_broadcast_u32(0x0f000f00));
+ msk = vector8_and(*dst, mask_hi);
msk = vector8_shift_right(msk, 8);
- *dst = vector8_and(*dst, vector8_broadcast_u32(0x000f000f));
+ *dst = vector8_and(*dst, mask_lo);
*dst = vector8_shift_left(*dst, 4);
*dst = vector8_or(*dst, msk);
return ret;
diff --git a/src/include/port/simd.h b/src/include/port/simd.h
index 0261179e9e7..e316233b7aa 100644
--- a/src/include/port/simd.h
+++ b/src/include/port/simd.h
@@ -101,6 +101,17 @@ static inline Vector8 vector8_min(const Vector8 v1, const Vector8 v2);
static inline Vector32 vector32_eq(const Vector32 v1, const Vector32 v2);
#endif
+/* return a zeroed register */
+static inline Vector8
+vector8_zero()
+{
+#if defined(USE_SSE2)
+ return _mm_setzero_si128();
+#elif defined(USE_NEON)
+ return vmovq_n_u8(0);
+#endif
+}
+
/*
* Load a chunk of memory into the given vector.
*/
@@ -170,24 +181,6 @@ vector32_broadcast(const uint32 c)
}
#endif /* ! USE_NO_SIMD */
-/*
- * Some compilers are picky about casts to the same underlying type, and others
- * are picky about implicit conversions with vector types. This function does
- * the same thing as vector32_broadcast(), but it returns a Vector8 and is
- * carefully crafted to avoid compiler indigestion.
- */
-#ifndef USE_NO_SIMD
-static inline Vector8
-vector8_broadcast_u32(const uint32 c)
-{
-#ifdef USE_SSE2
- return vector32_broadcast(c);
-#elif defined(USE_NEON)
- return (Vector8) vector32_broadcast(c);
-#endif
-}
-#endif /* ! USE_NO_SIMD */
-
/*
* Return true if any elements in the vector are equal to the given scalar.
*/
@@ -577,8 +570,9 @@ vector8_interleave_high(const Vector8 v1, const Vector8 v2)
static inline Vector8
vector8_pack_16(const Vector8 v1, const Vector8 v2)
{
- Vector32 mask PG_USED_FOR_ASSERTS_ONLY = vector32_broadcast(0xff00ff00);
+ Vector8 mask PG_USED_FOR_ASSERTS_ONLY;
+ mask = vector8_interleave_low(vector8_zero(), vector8_broadcast(0xff));
Assert(!vector8_has_ge(vector8_and(v1, mask), 1));
Assert(!vector8_has_ge(vector8_and(v2, mask), 1));
#ifdef USE_SSE2
^ permalink raw reply [nested|flat] 30+ messages in thread
* Re: [PATCH] Hex-coding optimizations using SVE on ARM.
2025-06-04 13:47 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-06-09 09:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-07-07 10:41 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-03 11:11 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-04 06:20 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-04 14:55 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 03:12 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 10:43 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 15:24 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 15:32 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Tom Lane <[email protected]>
2025-09-12 18:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-12 21:30 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-22 20:05 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-23 19:02 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-24 03:59 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-24 21:40 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-25 14:16 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-25 18:50 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-29 08:45 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
@ 2025-10-02 17:33 ` Nathan Bossart <[email protected]>
2025-10-03 07:36 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
0 siblings, 1 reply; 30+ messages in thread
From: Nathan Bossart @ 2025-10-02 17:33 UTC (permalink / raw)
To: John Naylor <[email protected]>; +Cc: [email protected] <[email protected]>; Tom Lane <[email protected]>; David Rowley <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>
On Mon, Sep 29, 2025 at 03:45:27PM +0700, John Naylor wrote:
> On Fri, Sep 26, 2025 at 1:50 AM Nathan Bossart <[email protected]> wrote:
>> On Thu, Sep 25, 2025 at 09:16:35PM +0700, John Naylor wrote:
>> > (Separately, now I'm wondering if we can do the same for
>> > vector8_has_le since _mm_min_epu8 and vminvq_u8 both exist, and that
>> > would allow getting rid of )
>>
>> I think so. I doubt there's any performance advantage, but it could be
>> nice for code cleanup. (I'm assuming you meant to say vector8_ssub
>> (renamed to vector8_ussub() in the patch) after "getting rid of.")
>
> Yes right, sorry. And it seems good to do such cleanup first, since it
> doesn't make sense to rename something that is about to be deleted.
Will do. I'll plan on committing the other patch [0] soon.
> Hmm, for this case, we can sidestep the maintainability questions
> entirely by instead using the new interleave functions to build the
> masks:
>
> vector8_interleave_low(vector8_zero(), vector8_broadcast(0x0f))
> vector8_interleave_low(vector8_broadcast(0x0f), vector8_zero())
>
> This generates identical code as v12 on Arm and is not bad on x86.
> What do you think of the attached?
WFM
> While looking around again, it looks like the "msk" variable isn't a
> mask like the implies to me. Not sure of a better name because I'm not
> sure what it represents aside from a temp variable.
Renamed to "tmp".
> +#elif defined(USE_NEON)
> + switch (i)
> + {
> + case 4:
> + return (Vector8) vshrq_n_u32((Vector32) v1, 4);
> + case 8:
> + return (Vector8) vshrq_n_u32((Vector32) v1, 8);
> + default:
> + pg_unreachable();
> + return vector8_broadcast(0);
> + }
>
> This is just a compiler hint, so if the input is not handled I think
> it will return the wrong answer rather than alerting the developer, so
> we probabaly want "Assert(false)" here.
Fixed.
> Other than that, the pack/unpack functions could use some
> documentation about which parameter is low/high.
Added.
[0] https://postgr.es/m/attachment/182185/v3-0001-Optimize-vector8_has_le-on-AArch64.patch
--
nathan
^ permalink raw reply [nested|flat] 30+ messages in thread
* Re: [PATCH] Hex-coding optimizations using SVE on ARM.
2025-06-04 13:47 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-06-09 09:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-07-07 10:41 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-03 11:11 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-04 06:20 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-04 14:55 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 03:12 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 10:43 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 15:24 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 15:32 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Tom Lane <[email protected]>
2025-09-12 18:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-12 21:30 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-22 20:05 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-23 19:02 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-24 03:59 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-24 21:40 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-25 14:16 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-25 18:50 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-29 08:45 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-10-02 17:33 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
@ 2025-10-03 07:36 ` John Naylor <[email protected]>
2025-10-03 20:33 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
0 siblings, 1 reply; 30+ messages in thread
From: John Naylor @ 2025-10-03 07:36 UTC (permalink / raw)
To: Nathan Bossart <[email protected]>; +Cc: [email protected] <[email protected]>; Tom Lane <[email protected]>; David Rowley <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>
On Fri, Oct 3, 2025 at 12:33 AM Nathan Bossart <[email protected]> wrote:
> [v13]
LGTM, but I went back and checked if vector8_zero() actually does
anything different than vector8_boadcast(0), and in fact it doesn't on
compilers we support for either x86 or Arm. I pulled the former out
from older work, but it seems irrelevant now. Pardon the noise.
--
John Naylor
Amazon Web Services
^ permalink raw reply [nested|flat] 30+ messages in thread
* Re: [PATCH] Hex-coding optimizations using SVE on ARM.
2025-06-04 13:47 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-06-09 09:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-07-07 10:41 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-03 11:11 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-04 06:20 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-04 14:55 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 03:12 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 10:43 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 15:24 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 15:32 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Tom Lane <[email protected]>
2025-09-12 18:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-12 21:30 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-22 20:05 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-23 19:02 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-24 03:59 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-24 21:40 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-25 14:16 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-25 18:50 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-29 08:45 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-10-02 17:33 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-10-03 07:36 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
@ 2025-10-03 20:33 ` Nathan Bossart <[email protected]>
2025-10-06 17:33 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
0 siblings, 1 reply; 30+ messages in thread
From: Nathan Bossart @ 2025-10-03 20:33 UTC (permalink / raw)
To: John Naylor <[email protected]>; +Cc: [email protected] <[email protected]>; Tom Lane <[email protected]>; David Rowley <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>
On Fri, Oct 03, 2025 at 02:36:47PM +0700, John Naylor wrote:
> LGTM, but I went back and checked if vector8_zero() actually does
> anything different than vector8_boadcast(0), and in fact it doesn't on
> compilers we support for either x86 or Arm. I pulled the former out
> from older work, but it seems irrelevant now. Pardon the noise.
Here is what I have staged for commit.
--
nathan
^ permalink raw reply [nested|flat] 30+ messages in thread
* Re: [PATCH] Hex-coding optimizations using SVE on ARM.
2025-06-04 13:47 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-06-09 09:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-07-07 10:41 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-03 11:11 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-04 06:20 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-04 14:55 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 03:12 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 10:43 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 15:24 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 15:32 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Tom Lane <[email protected]>
2025-09-12 18:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-12 21:30 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-22 20:05 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-23 19:02 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-24 03:59 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-24 21:40 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-25 14:16 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-25 18:50 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-29 08:45 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-10-02 17:33 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-10-03 07:36 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-10-03 20:33 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
@ 2025-10-06 17:33 ` Nathan Bossart <[email protected]>
0 siblings, 0 replies; 30+ messages in thread
From: Nathan Bossart @ 2025-10-06 17:33 UTC (permalink / raw)
To: John Naylor <[email protected]>; +Cc: [email protected] <[email protected]>; Tom Lane <[email protected]>; David Rowley <[email protected]>; pgsql-hackers; [email protected] <[email protected]>; [email protected] <[email protected]>
On Fri, Oct 03, 2025 at 03:33:21PM -0500, Nathan Bossart wrote:
> Here is what I have staged for commit.
Committed. That seems like a good stopping point for this work, so I have
marked the associated commitfest entry as "Committed."
--
nathan
^ permalink raw reply [nested|flat] 30+ messages in thread
end of thread, other threads:[~2025-10-06 17:33 UTC | newest]
Thread overview: 30+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2018-06-05 17:24 [PATCH v2 1/2] Variable length FunctionCallInfoData. Andres Freund <[email protected]>
2020-02-15 21:53 [PATCH v6 7/7] Update comment obsolete since 69c3936a Justin Pryzby <[email protected]>
2020-02-15 21:53 [PATCH v8 8/8] Update comment obsolete since 69c3936a Justin Pryzby <[email protected]>
2020-02-15 21:53 [PATCH v7 7/9] Update comment obsolete since 69c3936a Justin Pryzby <[email protected]>
2023-09-04 16:05 [PATCH v3] Update information_schema definition for not-null constraints Alvaro Herrera <[email protected]>
2025-01-15 11:06 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Ranier Vilela <[email protected]>
2025-06-04 13:47 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-06-09 09:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-07-07 10:41 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-03 11:11 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-03 14:48 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-04 06:20 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-04 14:55 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 03:12 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 10:43 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-11 15:24 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-11 15:32 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Tom Lane <[email protected]>
2025-09-12 18:49 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. [email protected] <[email protected]>
2025-09-12 21:30 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-22 20:05 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-23 19:02 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-24 03:59 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-24 21:40 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-25 14:16 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-09-25 18:50 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-09-29 08:45 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-10-02 17:33 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-10-03 07:36 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. John Naylor <[email protected]>
2025-10-03 20:33 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
2025-10-06 17:33 ` Re: [PATCH] Hex-coding optimizations using SVE on ARM. Nathan Bossart <[email protected]>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox