mirror of
https://github.com/postgres/postgres.git
synced 2025-04-22 23:02:54 +03:00
jit: Remove redundancies in expression evaluation code generation.
This merges the code emission for a number of opcodes by handling the behavioural difference more locally. This reduces code, and also improves the generated code a bit in some cases, by removing redundant constants. Author: Andres Freund Discussion: https://postgr.es/m/20191023163849.sosqbfs5yenocez3@alap3.anarazel.de
This commit is contained in:
parent
8c2769405f
commit
e6f86f8dd9
@ -471,6 +471,7 @@ llvm_compile_expr(ExprState *state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
case EEOP_ASSIGN_TMP:
|
case EEOP_ASSIGN_TMP:
|
||||||
|
case EEOP_ASSIGN_TMP_MAKE_RO:
|
||||||
{
|
{
|
||||||
LLVMValueRef v_value,
|
LLVMValueRef v_value,
|
||||||
v_isnull;
|
v_isnull;
|
||||||
@ -490,59 +491,40 @@ llvm_compile_expr(ExprState *state)
|
|||||||
v_risnullp =
|
v_risnullp =
|
||||||
LLVMBuildGEP(b, v_resultnulls, &v_resultnum, 1, "");
|
LLVMBuildGEP(b, v_resultnulls, &v_resultnum, 1, "");
|
||||||
|
|
||||||
/* and store */
|
|
||||||
LLVMBuildStore(b, v_value, v_rvaluep);
|
|
||||||
LLVMBuildStore(b, v_isnull, v_risnullp);
|
|
||||||
|
|
||||||
LLVMBuildBr(b, opblocks[opno + 1]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case EEOP_ASSIGN_TMP_MAKE_RO:
|
|
||||||
{
|
|
||||||
LLVMBasicBlockRef b_notnull;
|
|
||||||
LLVMValueRef v_params[1];
|
|
||||||
LLVMValueRef v_ret;
|
|
||||||
LLVMValueRef v_value,
|
|
||||||
v_isnull;
|
|
||||||
LLVMValueRef v_rvaluep,
|
|
||||||
v_risnullp;
|
|
||||||
LLVMValueRef v_resultnum;
|
|
||||||
size_t resultnum = op->d.assign_tmp.resultnum;
|
|
||||||
|
|
||||||
b_notnull = l_bb_before_v(opblocks[opno + 1],
|
|
||||||
"op.%d.assign_tmp.notnull", opno);
|
|
||||||
|
|
||||||
/* load data */
|
|
||||||
v_value = LLVMBuildLoad(b, v_tmpvaluep, "");
|
|
||||||
v_isnull = LLVMBuildLoad(b, v_tmpisnullp, "");
|
|
||||||
|
|
||||||
/* compute addresses of targets */
|
|
||||||
v_resultnum = l_int32_const(resultnum);
|
|
||||||
v_rvaluep = LLVMBuildGEP(b, v_resultvalues,
|
|
||||||
&v_resultnum, 1, "");
|
|
||||||
v_risnullp = LLVMBuildGEP(b, v_resultnulls,
|
|
||||||
&v_resultnum, 1, "");
|
|
||||||
|
|
||||||
/* store nullness */
|
/* store nullness */
|
||||||
LLVMBuildStore(b, v_isnull, v_risnullp);
|
LLVMBuildStore(b, v_isnull, v_risnullp);
|
||||||
|
|
||||||
/* check if value is NULL */
|
/* make value readonly if necessary */
|
||||||
LLVMBuildCondBr(b,
|
if (opcode == EEOP_ASSIGN_TMP_MAKE_RO)
|
||||||
LLVMBuildICmp(b, LLVMIntEQ, v_isnull,
|
{
|
||||||
l_sbool_const(0), ""),
|
LLVMBasicBlockRef b_notnull;
|
||||||
b_notnull, opblocks[opno + 1]);
|
LLVMValueRef v_params[1];
|
||||||
|
|
||||||
/* if value is not null, convert to RO datum */
|
b_notnull = l_bb_before_v(opblocks[opno + 1],
|
||||||
LLVMPositionBuilderAtEnd(b, b_notnull);
|
"op.%d.assign_tmp.notnull", opno);
|
||||||
v_params[0] = v_value;
|
|
||||||
v_ret =
|
|
||||||
LLVMBuildCall(b,
|
|
||||||
llvm_get_decl(mod, FuncMakeExpandedObjectReadOnlyInternal),
|
|
||||||
v_params, lengthof(v_params), "");
|
|
||||||
|
|
||||||
/* store value */
|
/* check if value is NULL */
|
||||||
LLVMBuildStore(b, v_ret, v_rvaluep);
|
LLVMBuildCondBr(b,
|
||||||
|
LLVMBuildICmp(b, LLVMIntEQ, v_isnull,
|
||||||
|
l_sbool_const(0), ""),
|
||||||
|
b_notnull, opblocks[opno + 1]);
|
||||||
|
|
||||||
|
/* if value is not null, convert to RO datum */
|
||||||
|
LLVMPositionBuilderAtEnd(b, b_notnull);
|
||||||
|
v_params[0] = v_value;
|
||||||
|
v_value =
|
||||||
|
LLVMBuildCall(b,
|
||||||
|
llvm_get_decl(mod, FuncMakeExpandedObjectReadOnlyInternal),
|
||||||
|
v_params, lengthof(v_params), "");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Falling out of the if () with builder in b_notnull,
|
||||||
|
* which is fine - the null is already stored above.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/* and finally store result */
|
||||||
|
LLVMBuildStore(b, v_value, v_rvaluep);
|
||||||
|
|
||||||
LLVMBuildBr(b, opblocks[opno + 1]);
|
LLVMBuildBr(b, opblocks[opno + 1]);
|
||||||
break;
|
break;
|
||||||
@ -563,78 +545,81 @@ llvm_compile_expr(ExprState *state)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case EEOP_FUNCEXPR_STRICT:
|
|
||||||
{
|
|
||||||
FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
|
|
||||||
LLVMBasicBlockRef b_nonull;
|
|
||||||
LLVMValueRef v_fcinfo;
|
|
||||||
LLVMBasicBlockRef *b_checkargnulls;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Block for the actual function call, if args are
|
|
||||||
* non-NULL.
|
|
||||||
*/
|
|
||||||
b_nonull = l_bb_before_v(opblocks[opno + 1],
|
|
||||||
"b.%d.no-null-args", opno);
|
|
||||||
|
|
||||||
/* should make sure they're optimized beforehand */
|
|
||||||
if (op->d.func.nargs == 0)
|
|
||||||
elog(ERROR, "argumentless strict functions are pointless");
|
|
||||||
|
|
||||||
v_fcinfo =
|
|
||||||
l_ptr_const(fcinfo, l_ptr(StructFunctionCallInfoData));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* set resnull to true, if the function is actually
|
|
||||||
* called, it'll be reset
|
|
||||||
*/
|
|
||||||
LLVMBuildStore(b, l_sbool_const(1), v_resnullp);
|
|
||||||
|
|
||||||
/* create blocks for checking args, one for each */
|
|
||||||
b_checkargnulls =
|
|
||||||
palloc(sizeof(LLVMBasicBlockRef *) * op->d.func.nargs);
|
|
||||||
for (int argno = 0; argno < op->d.func.nargs; argno++)
|
|
||||||
b_checkargnulls[argno] =
|
|
||||||
l_bb_before_v(b_nonull, "b.%d.isnull.%d", opno, argno);
|
|
||||||
|
|
||||||
/* jump to check of first argument */
|
|
||||||
LLVMBuildBr(b, b_checkargnulls[0]);
|
|
||||||
|
|
||||||
/* check each arg for NULLness */
|
|
||||||
for (int argno = 0; argno < op->d.func.nargs; argno++)
|
|
||||||
{
|
|
||||||
LLVMValueRef v_argisnull;
|
|
||||||
LLVMBasicBlockRef b_argnotnull;
|
|
||||||
|
|
||||||
LLVMPositionBuilderAtEnd(b, b_checkargnulls[argno]);
|
|
||||||
|
|
||||||
/* compute block to jump to if argument is not null */
|
|
||||||
if (argno + 1 == op->d.func.nargs)
|
|
||||||
b_argnotnull = b_nonull;
|
|
||||||
else
|
|
||||||
b_argnotnull = b_checkargnulls[argno + 1];
|
|
||||||
|
|
||||||
/* and finally load & check NULLness of arg */
|
|
||||||
v_argisnull = l_funcnull(b, v_fcinfo, argno);
|
|
||||||
LLVMBuildCondBr(b,
|
|
||||||
LLVMBuildICmp(b, LLVMIntEQ,
|
|
||||||
v_argisnull,
|
|
||||||
l_sbool_const(1),
|
|
||||||
""),
|
|
||||||
opblocks[opno + 1],
|
|
||||||
b_argnotnull);
|
|
||||||
}
|
|
||||||
|
|
||||||
LLVMPositionBuilderAtEnd(b, b_nonull);
|
|
||||||
}
|
|
||||||
/* FALLTHROUGH */
|
|
||||||
|
|
||||||
case EEOP_FUNCEXPR:
|
case EEOP_FUNCEXPR:
|
||||||
|
case EEOP_FUNCEXPR_STRICT:
|
||||||
{
|
{
|
||||||
FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
|
FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
|
||||||
LLVMValueRef v_fcinfo_isnull;
|
LLVMValueRef v_fcinfo_isnull;
|
||||||
LLVMValueRef v_retval;
|
LLVMValueRef v_retval;
|
||||||
|
|
||||||
|
if (opcode == EEOP_FUNCEXPR_STRICT)
|
||||||
|
{
|
||||||
|
LLVMBasicBlockRef b_nonull;
|
||||||
|
LLVMBasicBlockRef *b_checkargnulls;
|
||||||
|
LLVMValueRef v_fcinfo;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Block for the actual function call, if args are
|
||||||
|
* non-NULL.
|
||||||
|
*/
|
||||||
|
b_nonull = l_bb_before_v(opblocks[opno + 1],
|
||||||
|
"b.%d.no-null-args", opno);
|
||||||
|
|
||||||
|
/* should make sure they're optimized beforehand */
|
||||||
|
if (op->d.func.nargs == 0)
|
||||||
|
elog(ERROR, "argumentless strict functions are pointless");
|
||||||
|
|
||||||
|
v_fcinfo =
|
||||||
|
l_ptr_const(fcinfo, l_ptr(StructFunctionCallInfoData));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* set resnull to true, if the function is actually
|
||||||
|
* called, it'll be reset
|
||||||
|
*/
|
||||||
|
LLVMBuildStore(b, l_sbool_const(1), v_resnullp);
|
||||||
|
|
||||||
|
/* create blocks for checking args, one for each */
|
||||||
|
b_checkargnulls =
|
||||||
|
palloc(sizeof(LLVMBasicBlockRef *) * op->d.func.nargs);
|
||||||
|
for (int argno = 0; argno < op->d.func.nargs; argno++)
|
||||||
|
b_checkargnulls[argno] =
|
||||||
|
l_bb_before_v(b_nonull, "b.%d.isnull.%d", opno,
|
||||||
|
argno);
|
||||||
|
|
||||||
|
/* jump to check of first argument */
|
||||||
|
LLVMBuildBr(b, b_checkargnulls[0]);
|
||||||
|
|
||||||
|
/* check each arg for NULLness */
|
||||||
|
for (int argno = 0; argno < op->d.func.nargs; argno++)
|
||||||
|
{
|
||||||
|
LLVMValueRef v_argisnull;
|
||||||
|
LLVMBasicBlockRef b_argnotnull;
|
||||||
|
|
||||||
|
LLVMPositionBuilderAtEnd(b, b_checkargnulls[argno]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute block to jump to if argument is not
|
||||||
|
* null.
|
||||||
|
*/
|
||||||
|
if (argno + 1 == op->d.func.nargs)
|
||||||
|
b_argnotnull = b_nonull;
|
||||||
|
else
|
||||||
|
b_argnotnull = b_checkargnulls[argno + 1];
|
||||||
|
|
||||||
|
/* and finally load & check NULLness of arg */
|
||||||
|
v_argisnull = l_funcnull(b, v_fcinfo, argno);
|
||||||
|
LLVMBuildCondBr(b,
|
||||||
|
LLVMBuildICmp(b, LLVMIntEQ,
|
||||||
|
v_argisnull,
|
||||||
|
l_sbool_const(1),
|
||||||
|
""),
|
||||||
|
opblocks[opno + 1],
|
||||||
|
b_argnotnull);
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMPositionBuilderAtEnd(b, b_nonull);
|
||||||
|
}
|
||||||
|
|
||||||
v_retval = BuildV1Call(context, b, mod, fcinfo,
|
v_retval = BuildV1Call(context, b, mod, fcinfo,
|
||||||
&v_fcinfo_isnull);
|
&v_fcinfo_isnull);
|
||||||
LLVMBuildStore(b, v_retval, v_resvaluep);
|
LLVMBuildStore(b, v_retval, v_resvaluep);
|
||||||
@ -657,24 +642,14 @@ llvm_compile_expr(ExprState *state)
|
|||||||
LLVMBuildBr(b, opblocks[opno + 1]);
|
LLVMBuildBr(b, opblocks[opno + 1]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EEOP_BOOL_AND_STEP_FIRST:
|
|
||||||
{
|
|
||||||
LLVMValueRef v_boolanynullp;
|
|
||||||
|
|
||||||
v_boolanynullp = l_ptr_const(op->d.boolexpr.anynull,
|
|
||||||
l_ptr(TypeStorageBool));
|
|
||||||
LLVMBuildStore(b, l_sbool_const(0), v_boolanynullp);
|
|
||||||
|
|
||||||
}
|
|
||||||
/* FALLTHROUGH */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Treat them the same for now, optimizer can remove
|
* Treat them the same for now, optimizer can remove
|
||||||
* redundancy. Could be worthwhile to optimize during emission
|
* redundancy. Could be worthwhile to optimize during emission
|
||||||
* though.
|
* though.
|
||||||
*/
|
*/
|
||||||
case EEOP_BOOL_AND_STEP_LAST:
|
case EEOP_BOOL_AND_STEP_FIRST:
|
||||||
case EEOP_BOOL_AND_STEP:
|
case EEOP_BOOL_AND_STEP:
|
||||||
|
case EEOP_BOOL_AND_STEP_LAST:
|
||||||
{
|
{
|
||||||
LLVMValueRef v_boolvalue;
|
LLVMValueRef v_boolvalue;
|
||||||
LLVMValueRef v_boolnull;
|
LLVMValueRef v_boolnull;
|
||||||
@ -700,6 +675,9 @@ llvm_compile_expr(ExprState *state)
|
|||||||
v_boolanynullp = l_ptr_const(op->d.boolexpr.anynull,
|
v_boolanynullp = l_ptr_const(op->d.boolexpr.anynull,
|
||||||
l_ptr(TypeStorageBool));
|
l_ptr(TypeStorageBool));
|
||||||
|
|
||||||
|
if (opcode == EEOP_BOOL_AND_STEP_FIRST)
|
||||||
|
LLVMBuildStore(b, l_sbool_const(0), v_boolanynullp);
|
||||||
|
|
||||||
v_boolnull = LLVMBuildLoad(b, v_resnullp, "");
|
v_boolnull = LLVMBuildLoad(b, v_resnullp, "");
|
||||||
v_boolvalue = LLVMBuildLoad(b, v_resvaluep, "");
|
v_boolvalue = LLVMBuildLoad(b, v_resvaluep, "");
|
||||||
|
|
||||||
@ -759,23 +737,15 @@ llvm_compile_expr(ExprState *state)
|
|||||||
LLVMBuildBr(b, opblocks[opno + 1]);
|
LLVMBuildBr(b, opblocks[opno + 1]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EEOP_BOOL_OR_STEP_FIRST:
|
|
||||||
{
|
|
||||||
LLVMValueRef v_boolanynullp;
|
|
||||||
|
|
||||||
v_boolanynullp = l_ptr_const(op->d.boolexpr.anynull,
|
|
||||||
l_ptr(TypeStorageBool));
|
|
||||||
LLVMBuildStore(b, l_sbool_const(0), v_boolanynullp);
|
|
||||||
}
|
|
||||||
/* FALLTHROUGH */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Treat them the same for now, optimizer can remove
|
* Treat them the same for now, optimizer can remove
|
||||||
* redundancy. Could be worthwhile to optimize during emission
|
* redundancy. Could be worthwhile to optimize during emission
|
||||||
* though.
|
* though.
|
||||||
*/
|
*/
|
||||||
case EEOP_BOOL_OR_STEP_LAST:
|
case EEOP_BOOL_OR_STEP_FIRST:
|
||||||
case EEOP_BOOL_OR_STEP:
|
case EEOP_BOOL_OR_STEP:
|
||||||
|
case EEOP_BOOL_OR_STEP_LAST:
|
||||||
{
|
{
|
||||||
LLVMValueRef v_boolvalue;
|
LLVMValueRef v_boolvalue;
|
||||||
LLVMValueRef v_boolnull;
|
LLVMValueRef v_boolnull;
|
||||||
@ -802,6 +772,8 @@ llvm_compile_expr(ExprState *state)
|
|||||||
v_boolanynullp = l_ptr_const(op->d.boolexpr.anynull,
|
v_boolanynullp = l_ptr_const(op->d.boolexpr.anynull,
|
||||||
l_ptr(TypeStorageBool));
|
l_ptr(TypeStorageBool));
|
||||||
|
|
||||||
|
if (opcode == EEOP_BOOL_OR_STEP_FIRST)
|
||||||
|
LLVMBuildStore(b, l_sbool_const(0), v_boolanynullp);
|
||||||
v_boolnull = LLVMBuildLoad(b, v_resnullp, "");
|
v_boolnull = LLVMBuildLoad(b, v_resnullp, "");
|
||||||
v_boolvalue = LLVMBuildLoad(b, v_resvaluep, "");
|
v_boolvalue = LLVMBuildLoad(b, v_resvaluep, "");
|
||||||
|
|
||||||
@ -1958,41 +1930,40 @@ llvm_compile_expr(ExprState *state)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case EEOP_AGG_STRICT_DESERIALIZE:
|
case EEOP_AGG_STRICT_DESERIALIZE:
|
||||||
{
|
|
||||||
FunctionCallInfo fcinfo = op->d.agg_deserialize.fcinfo_data;
|
|
||||||
LLVMValueRef v_fcinfo;
|
|
||||||
LLVMValueRef v_argnull0;
|
|
||||||
LLVMBasicBlockRef b_deserialize;
|
|
||||||
|
|
||||||
b_deserialize = l_bb_before_v(opblocks[opno + 1],
|
|
||||||
"op.%d.deserialize", opno);
|
|
||||||
|
|
||||||
v_fcinfo = l_ptr_const(fcinfo,
|
|
||||||
l_ptr(StructFunctionCallInfoData));
|
|
||||||
v_argnull0 = l_funcnull(b, v_fcinfo, 0);
|
|
||||||
|
|
||||||
LLVMBuildCondBr(b,
|
|
||||||
LLVMBuildICmp(b,
|
|
||||||
LLVMIntEQ,
|
|
||||||
v_argnull0,
|
|
||||||
l_sbool_const(1),
|
|
||||||
""),
|
|
||||||
opblocks[op->d.agg_deserialize.jumpnull],
|
|
||||||
b_deserialize);
|
|
||||||
LLVMPositionBuilderAtEnd(b, b_deserialize);
|
|
||||||
}
|
|
||||||
/* FALLTHROUGH */
|
|
||||||
|
|
||||||
case EEOP_AGG_DESERIALIZE:
|
case EEOP_AGG_DESERIALIZE:
|
||||||
{
|
{
|
||||||
AggState *aggstate;
|
AggState *aggstate;
|
||||||
FunctionCallInfo fcinfo;
|
FunctionCallInfo fcinfo = op->d.agg_deserialize.fcinfo_data;
|
||||||
|
|
||||||
LLVMValueRef v_retval;
|
LLVMValueRef v_retval;
|
||||||
LLVMValueRef v_fcinfo_isnull;
|
LLVMValueRef v_fcinfo_isnull;
|
||||||
LLVMValueRef v_tmpcontext;
|
LLVMValueRef v_tmpcontext;
|
||||||
LLVMValueRef v_oldcontext;
|
LLVMValueRef v_oldcontext;
|
||||||
|
|
||||||
|
if (opcode == EEOP_AGG_STRICT_DESERIALIZE)
|
||||||
|
{
|
||||||
|
LLVMValueRef v_fcinfo;
|
||||||
|
LLVMValueRef v_argnull0;
|
||||||
|
LLVMBasicBlockRef b_deserialize;
|
||||||
|
|
||||||
|
b_deserialize = l_bb_before_v(opblocks[opno + 1],
|
||||||
|
"op.%d.deserialize", opno);
|
||||||
|
|
||||||
|
v_fcinfo = l_ptr_const(fcinfo,
|
||||||
|
l_ptr(StructFunctionCallInfoData));
|
||||||
|
v_argnull0 = l_funcnull(b, v_fcinfo, 0);
|
||||||
|
|
||||||
|
LLVMBuildCondBr(b,
|
||||||
|
LLVMBuildICmp(b,
|
||||||
|
LLVMIntEQ,
|
||||||
|
v_argnull0,
|
||||||
|
l_sbool_const(1),
|
||||||
|
""),
|
||||||
|
opblocks[op->d.agg_deserialize.jumpnull],
|
||||||
|
b_deserialize);
|
||||||
|
LLVMPositionBuilderAtEnd(b, b_deserialize);
|
||||||
|
}
|
||||||
|
|
||||||
aggstate = castNode(AggState, state->parent);
|
aggstate = castNode(AggState, state->parent);
|
||||||
fcinfo = op->d.agg_deserialize.fcinfo_data;
|
fcinfo = op->d.agg_deserialize.fcinfo_data;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user