jit: Remove redundancies in expression evaluation code generation.
authorAndres Freund <andres@anarazel.de>
Fri, 7 Feb 2020 03:45:45 +0000 (19:45 -0800)
committerAndres Freund <andres@anarazel.de>
Fri, 7 Feb 2020 04:01:23 +0000 (20:01 -0800)
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

src/backend/jit/llvm/llvmjit_expr.c

index cd9d8c1c76070bf17d32ef675ac59d4421f67aeb..44a221b9a3a7e4befe2d11f3526de28dd8096cf7 100644 (file)
@@ -471,6 +471,7 @@ llvm_compile_expr(ExprState *state)
                }
 
            case EEOP_ASSIGN_TMP:
+           case EEOP_ASSIGN_TMP_MAKE_RO:
                {
                    LLVMValueRef v_value,
                                v_isnull;
@@ -490,59 +491,40 @@ llvm_compile_expr(ExprState *state)
                    v_risnullp =
                        LLVMBuildGEP(b, v_resultnulls, &v_resultnum, 1, "");
 
-                   /* and store */
-                   LLVMBuildStore(b, v_value, v_rvaluep);
+                   /* store nullness */
                    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, "");
+                   /* make value readonly if necessary */
+                   if (opcode == EEOP_ASSIGN_TMP_MAKE_RO)
+                   {
+                       LLVMBasicBlockRef b_notnull;
+                       LLVMValueRef v_params[1];
 
-                   /* store nullness */
-                   LLVMBuildStore(b, v_isnull, v_risnullp);
+                       b_notnull = l_bb_before_v(opblocks[opno + 1],
+                                                 "op.%d.assign_tmp.notnull", opno);
 
-                   /* check if value is NULL */
-                   LLVMBuildCondBr(b,
-                                   LLVMBuildICmp(b, LLVMIntEQ, v_isnull,
-                                                 l_sbool_const(0), ""),
-                                   b_notnull, opblocks[opno + 1]);
+                       /* check if value is NULL */
+                       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), "");
 
-                   /* if value is not null, convert to RO datum */
-                   LLVMPositionBuilderAtEnd(b, b_notnull);
-                   v_params[0] = v_value;
-                   v_ret =
-                       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.
+                        */
+                   }
 
-                   /* store value */
-                   LLVMBuildStore(b, v_ret, v_rvaluep);
+                   /* and finally store result */
+                   LLVMBuildStore(b, v_value, v_rvaluep);
 
                    LLVMBuildBr(b, opblocks[opno + 1]);
                    break;
@@ -563,78 +545,81 @@ llvm_compile_expr(ExprState *state)
                    break;
                }
 
+           case EEOP_FUNCEXPR:
            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);
+                   LLVMValueRef v_fcinfo_isnull;
+                   LLVMValueRef v_retval;
 
-                   /* should make sure they're optimized beforehand */
-                   if (op->d.func.nargs == 0)
-                       elog(ERROR, "argumentless strict functions are pointless");
+                   if (opcode == EEOP_FUNCEXPR_STRICT)
+                   {
+                       LLVMBasicBlockRef b_nonull;
+                       LLVMBasicBlockRef *b_checkargnulls;
+                       LLVMValueRef v_fcinfo;
 
-                   v_fcinfo =
-                       l_ptr_const(fcinfo, l_ptr(StructFunctionCallInfoData));
+                       /*
+                        * 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);
 
-                   /*
-                    * set resnull to true, if the function is actually
-                    * called, it'll be reset
-                    */
-                   LLVMBuildStore(b, l_sbool_const(1), v_resnullp);
+                       /* should make sure they're optimized beforehand */
+                       if (op->d.func.nargs == 0)
+                           elog(ERROR, "argumentless strict functions are pointless");
 
-                   /* 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);
+                       v_fcinfo =
+                           l_ptr_const(fcinfo, l_ptr(StructFunctionCallInfoData));
 
-                   /* jump to check of first argument */
-                   LLVMBuildBr(b, b_checkargnulls[0]);
+                       /*
+                        * set resnull to true, if the function is actually
+                        * called, it'll be reset
+                        */
+                       LLVMBuildStore(b, l_sbool_const(1), v_resnullp);
 
-                   /* check each arg for NULLness */
-                   for (int argno = 0; argno < op->d.func.nargs; argno++)
-                   {
-                       LLVMValueRef v_argisnull;
-                       LLVMBasicBlockRef b_argnotnull;
+                       /* 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);
 
-                       LLVMPositionBuilderAtEnd(b, b_checkargnulls[argno]);
+                       /* jump to check of first argument */
+                       LLVMBuildBr(b, b_checkargnulls[0]);
 
-                       /* 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];
+                       /* 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);
+                       }
 
-                       /* 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);
                    }
 
-                   LLVMPositionBuilderAtEnd(b, b_nonull);
-               }
-               /* FALLTHROUGH */
-
-           case EEOP_FUNCEXPR:
-               {
-                   FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
-                   LLVMValueRef v_fcinfo_isnull;
-                   LLVMValueRef v_retval;
-
                    v_retval = BuildV1Call(context, b, mod, fcinfo,
                                           &v_fcinfo_isnull);
                    LLVMBuildStore(b, v_retval, v_resvaluep);
@@ -657,24 +642,14 @@ llvm_compile_expr(ExprState *state)
                LLVMBuildBr(b, opblocks[opno + 1]);
                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
                 * redundancy. Could be worthwhile to optimize during emission
                 * though.
                 */
-           case EEOP_BOOL_AND_STEP_LAST:
+           case EEOP_BOOL_AND_STEP_FIRST:
            case EEOP_BOOL_AND_STEP:
+           case EEOP_BOOL_AND_STEP_LAST:
                {
                    LLVMValueRef v_boolvalue;
                    LLVMValueRef v_boolnull;
@@ -700,6 +675,9 @@ llvm_compile_expr(ExprState *state)
                    v_boolanynullp = l_ptr_const(op->d.boolexpr.anynull,
                                                 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_boolvalue = LLVMBuildLoad(b, v_resvaluep, "");
 
@@ -759,23 +737,15 @@ llvm_compile_expr(ExprState *state)
                    LLVMBuildBr(b, opblocks[opno + 1]);
                    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
                 * redundancy. Could be worthwhile to optimize during emission
                 * though.
                 */
-           case EEOP_BOOL_OR_STEP_LAST:
+           case EEOP_BOOL_OR_STEP_FIRST:
            case EEOP_BOOL_OR_STEP:
+           case EEOP_BOOL_OR_STEP_LAST:
                {
                    LLVMValueRef v_boolvalue;
                    LLVMValueRef v_boolnull;
@@ -802,6 +772,8 @@ llvm_compile_expr(ExprState *state)
                    v_boolanynullp = l_ptr_const(op->d.boolexpr.anynull,
                                                 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_boolvalue = LLVMBuildLoad(b, v_resvaluep, "");
 
@@ -1958,41 +1930,40 @@ llvm_compile_expr(ExprState *state)
                break;
 
            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:
                {
                    AggState   *aggstate;
-                   FunctionCallInfo fcinfo;
+                   FunctionCallInfo fcinfo = op->d.agg_deserialize.fcinfo_data;
 
                    LLVMValueRef v_retval;
                    LLVMValueRef v_fcinfo_isnull;
                    LLVMValueRef v_tmpcontext;
                    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);
                    fcinfo = op->d.agg_deserialize.fcinfo_data;