Skip to content

Commit e6f86f8

Browse files
committed
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
1 parent 8c27694 commit e6f86f8

File tree

1 file changed

+124
-153
lines changed

1 file changed

+124
-153
lines changed

src/backend/jit/llvm/llvmjit_expr.c

Lines changed: 124 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,7 @@ llvm_compile_expr(ExprState *state)
471471
}
472472

473473
case EEOP_ASSIGN_TMP:
474+
case EEOP_ASSIGN_TMP_MAKE_RO:
474475
{
475476
LLVMValueRef v_value,
476477
v_isnull;
@@ -490,59 +491,40 @@ llvm_compile_expr(ExprState *state)
490491
v_risnullp =
491492
LLVMBuildGEP(b, v_resultnulls, &v_resultnum, 1, "");
492493

493-
/* and store */
494-
LLVMBuildStore(b, v_value, v_rvaluep);
494+
/* store nullness */
495495
LLVMBuildStore(b, v_isnull, v_risnullp);
496496

497-
LLVMBuildBr(b, opblocks[opno + 1]);
498-
break;
499-
}
500-
501-
case EEOP_ASSIGN_TMP_MAKE_RO:
502-
{
503-
LLVMBasicBlockRef b_notnull;
504-
LLVMValueRef v_params[1];
505-
LLVMValueRef v_ret;
506-
LLVMValueRef v_value,
507-
v_isnull;
508-
LLVMValueRef v_rvaluep,
509-
v_risnullp;
510-
LLVMValueRef v_resultnum;
511-
size_t resultnum = op->d.assign_tmp.resultnum;
512-
513-
b_notnull = l_bb_before_v(opblocks[opno + 1],
514-
"op.%d.assign_tmp.notnull", opno);
515-
516-
/* load data */
517-
v_value = LLVMBuildLoad(b, v_tmpvaluep, "");
518-
v_isnull = LLVMBuildLoad(b, v_tmpisnullp, "");
519-
520-
/* compute addresses of targets */
521-
v_resultnum = l_int32_const(resultnum);
522-
v_rvaluep = LLVMBuildGEP(b, v_resultvalues,
523-
&v_resultnum, 1, "");
524-
v_risnullp = LLVMBuildGEP(b, v_resultnulls,
525-
&v_resultnum, 1, "");
497+
/* make value readonly if necessary */
498+
if (opcode == EEOP_ASSIGN_TMP_MAKE_RO)
499+
{
500+
LLVMBasicBlockRef b_notnull;
501+
LLVMValueRef v_params[1];
526502

527-
/* store nullness */
528-
LLVMBuildStore(b, v_isnull, v_risnullp);
503+
b_notnull = l_bb_before_v(opblocks[opno + 1],
504+
"op.%d.assign_tmp.notnull", opno);
529505

530-
/* check if value is NULL */
531-
LLVMBuildCondBr(b,
532-
LLVMBuildICmp(b, LLVMIntEQ, v_isnull,
533-
l_sbool_const(0), ""),
534-
b_notnull, opblocks[opno + 1]);
506+
/* check if value is NULL */
507+
LLVMBuildCondBr(b,
508+
LLVMBuildICmp(b, LLVMIntEQ, v_isnull,
509+
l_sbool_const(0), ""),
510+
b_notnull, opblocks[opno + 1]);
511+
512+
/* if value is not null, convert to RO datum */
513+
LLVMPositionBuilderAtEnd(b, b_notnull);
514+
v_params[0] = v_value;
515+
v_value =
516+
LLVMBuildCall(b,
517+
llvm_get_decl(mod, FuncMakeExpandedObjectReadOnlyInternal),
518+
v_params, lengthof(v_params), "");
535519

536-
/* if value is not null, convert to RO datum */
537-
LLVMPositionBuilderAtEnd(b, b_notnull);
538-
v_params[0] = v_value;
539-
v_ret =
540-
LLVMBuildCall(b,
541-
llvm_get_decl(mod, FuncMakeExpandedObjectReadOnlyInternal),
542-
v_params, lengthof(v_params), "");
520+
/*
521+
* Falling out of the if () with builder in b_notnull,
522+
* which is fine - the null is already stored above.
523+
*/
524+
}
543525

544-
/* store value */
545-
LLVMBuildStore(b, v_ret, v_rvaluep);
526+
/* and finally store result */
527+
LLVMBuildStore(b, v_value, v_rvaluep);
546528

547529
LLVMBuildBr(b, opblocks[opno + 1]);
548530
break;
@@ -563,78 +545,81 @@ llvm_compile_expr(ExprState *state)
563545
break;
564546
}
565547

548+
case EEOP_FUNCEXPR:
566549
case EEOP_FUNCEXPR_STRICT:
567550
{
568551
FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
569-
LLVMBasicBlockRef b_nonull;
570-
LLVMValueRef v_fcinfo;
571-
LLVMBasicBlockRef *b_checkargnulls;
572-
573-
/*
574-
* Block for the actual function call, if args are
575-
* non-NULL.
576-
*/
577-
b_nonull = l_bb_before_v(opblocks[opno + 1],
578-
"b.%d.no-null-args", opno);
552+
LLVMValueRef v_fcinfo_isnull;
553+
LLVMValueRef v_retval;
579554

580-
/* should make sure they're optimized beforehand */
581-
if (op->d.func.nargs == 0)
582-
elog(ERROR, "argumentless strict functions are pointless");
555+
if (opcode == EEOP_FUNCEXPR_STRICT)
556+
{
557+
LLVMBasicBlockRef b_nonull;
558+
LLVMBasicBlockRef *b_checkargnulls;
559+
LLVMValueRef v_fcinfo;
583560

584-
v_fcinfo =
585-
l_ptr_const(fcinfo, l_ptr(StructFunctionCallInfoData));
561+
/*
562+
* Block for the actual function call, if args are
563+
* non-NULL.
564+
*/
565+
b_nonull = l_bb_before_v(opblocks[opno + 1],
566+
"b.%d.no-null-args", opno);
586567

587-
/*
588-
* set resnull to true, if the function is actually
589-
* called, it'll be reset
590-
*/
591-
LLVMBuildStore(b, l_sbool_const(1), v_resnullp);
568+
/* should make sure they're optimized beforehand */
569+
if (op->d.func.nargs == 0)
570+
elog(ERROR, "argumentless strict functions are pointless");
592571

593-
/* create blocks for checking args, one for each */
594-
b_checkargnulls =
595-
palloc(sizeof(LLVMBasicBlockRef *) * op->d.func.nargs);
596-
for (int argno = 0; argno < op->d.func.nargs; argno++)
597-
b_checkargnulls[argno] =
598-
l_bb_before_v(b_nonull, "b.%d.isnull.%d", opno, argno);
572+
v_fcinfo =
573+
l_ptr_const(fcinfo, l_ptr(StructFunctionCallInfoData));
599574

600-
/* jump to check of first argument */
601-
LLVMBuildBr(b, b_checkargnulls[0]);
575+
/*
576+
* set resnull to true, if the function is actually
577+
* called, it'll be reset
578+
*/
579+
LLVMBuildStore(b, l_sbool_const(1), v_resnullp);
602580

603-
/* check each arg for NULLness */
604-
for (int argno = 0; argno < op->d.func.nargs; argno++)
605-
{
606-
LLVMValueRef v_argisnull;
607-
LLVMBasicBlockRef b_argnotnull;
581+
/* create blocks for checking args, one for each */
582+
b_checkargnulls =
583+
palloc(sizeof(LLVMBasicBlockRef *) * op->d.func.nargs);
584+
for (int argno = 0; argno < op->d.func.nargs; argno++)
585+
b_checkargnulls[argno] =
586+
l_bb_before_v(b_nonull, "b.%d.isnull.%d", opno,
587+
argno);
608588

609-
LLVMPositionBuilderAtEnd(b, b_checkargnulls[argno]);
589+
/* jump to check of first argument */
590+
LLVMBuildBr(b, b_checkargnulls[0]);
610591

611-
/* compute block to jump to if argument is not null */
612-
if (argno + 1 == op->d.func.nargs)
613-
b_argnotnull = b_nonull;
614-
else
615-
b_argnotnull = b_checkargnulls[argno + 1];
592+
/* check each arg for NULLness */
593+
for (int argno = 0; argno < op->d.func.nargs; argno++)
594+
{
595+
LLVMValueRef v_argisnull;
596+
LLVMBasicBlockRef b_argnotnull;
597+
598+
LLVMPositionBuilderAtEnd(b, b_checkargnulls[argno]);
599+
600+
/*
601+
* Compute block to jump to if argument is not
602+
* null.
603+
*/
604+
if (argno + 1 == op->d.func.nargs)
605+
b_argnotnull = b_nonull;
606+
else
607+
b_argnotnull = b_checkargnulls[argno + 1];
608+
609+
/* and finally load & check NULLness of arg */
610+
v_argisnull = l_funcnull(b, v_fcinfo, argno);
611+
LLVMBuildCondBr(b,
612+
LLVMBuildICmp(b, LLVMIntEQ,
613+
v_argisnull,
614+
l_sbool_const(1),
615+
""),
616+
opblocks[opno + 1],
617+
b_argnotnull);
618+
}
616619

617-
/* and finally load & check NULLness of arg */
618-
v_argisnull = l_funcnull(b, v_fcinfo, argno);
619-
LLVMBuildCondBr(b,
620-
LLVMBuildICmp(b, LLVMIntEQ,
621-
v_argisnull,
622-
l_sbool_const(1),
623-
""),
624-
opblocks[opno + 1],
625-
b_argnotnull);
620+
LLVMPositionBuilderAtEnd(b, b_nonull);
626621
}
627622

628-
LLVMPositionBuilderAtEnd(b, b_nonull);
629-
}
630-
/* FALLTHROUGH */
631-
632-
case EEOP_FUNCEXPR:
633-
{
634-
FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
635-
LLVMValueRef v_fcinfo_isnull;
636-
LLVMValueRef v_retval;
637-
638623
v_retval = BuildV1Call(context, b, mod, fcinfo,
639624
&v_fcinfo_isnull);
640625
LLVMBuildStore(b, v_retval, v_resvaluep);
@@ -657,24 +642,14 @@ llvm_compile_expr(ExprState *state)
657642
LLVMBuildBr(b, opblocks[opno + 1]);
658643
break;
659644

660-
case EEOP_BOOL_AND_STEP_FIRST:
661-
{
662-
LLVMValueRef v_boolanynullp;
663-
664-
v_boolanynullp = l_ptr_const(op->d.boolexpr.anynull,
665-
l_ptr(TypeStorageBool));
666-
LLVMBuildStore(b, l_sbool_const(0), v_boolanynullp);
667-
668-
}
669-
/* FALLTHROUGH */
670-
671645
/*
672646
* Treat them the same for now, optimizer can remove
673647
* redundancy. Could be worthwhile to optimize during emission
674648
* though.
675649
*/
676-
case EEOP_BOOL_AND_STEP_LAST:
650+
case EEOP_BOOL_AND_STEP_FIRST:
677651
case EEOP_BOOL_AND_STEP:
652+
case EEOP_BOOL_AND_STEP_LAST:
678653
{
679654
LLVMValueRef v_boolvalue;
680655
LLVMValueRef v_boolnull;
@@ -700,6 +675,9 @@ llvm_compile_expr(ExprState *state)
700675
v_boolanynullp = l_ptr_const(op->d.boolexpr.anynull,
701676
l_ptr(TypeStorageBool));
702677

678+
if (opcode == EEOP_BOOL_AND_STEP_FIRST)
679+
LLVMBuildStore(b, l_sbool_const(0), v_boolanynullp);
680+
703681
v_boolnull = LLVMBuildLoad(b, v_resnullp, "");
704682
v_boolvalue = LLVMBuildLoad(b, v_resvaluep, "");
705683

@@ -759,23 +737,15 @@ llvm_compile_expr(ExprState *state)
759737
LLVMBuildBr(b, opblocks[opno + 1]);
760738
break;
761739
}
762-
case EEOP_BOOL_OR_STEP_FIRST:
763-
{
764-
LLVMValueRef v_boolanynullp;
765-
766-
v_boolanynullp = l_ptr_const(op->d.boolexpr.anynull,
767-
l_ptr(TypeStorageBool));
768-
LLVMBuildStore(b, l_sbool_const(0), v_boolanynullp);
769-
}
770-
/* FALLTHROUGH */
771740

772741
/*
773742
* Treat them the same for now, optimizer can remove
774743
* redundancy. Could be worthwhile to optimize during emission
775744
* though.
776745
*/
777-
case EEOP_BOOL_OR_STEP_LAST:
746+
case EEOP_BOOL_OR_STEP_FIRST:
778747
case EEOP_BOOL_OR_STEP:
748+
case EEOP_BOOL_OR_STEP_LAST:
779749
{
780750
LLVMValueRef v_boolvalue;
781751
LLVMValueRef v_boolnull;
@@ -802,6 +772,8 @@ llvm_compile_expr(ExprState *state)
802772
v_boolanynullp = l_ptr_const(op->d.boolexpr.anynull,
803773
l_ptr(TypeStorageBool));
804774

775+
if (opcode == EEOP_BOOL_OR_STEP_FIRST)
776+
LLVMBuildStore(b, l_sbool_const(0), v_boolanynullp);
805777
v_boolnull = LLVMBuildLoad(b, v_resnullp, "");
806778
v_boolvalue = LLVMBuildLoad(b, v_resvaluep, "");
807779

@@ -1958,41 +1930,40 @@ llvm_compile_expr(ExprState *state)
19581930
break;
19591931

19601932
case EEOP_AGG_STRICT_DESERIALIZE:
1961-
{
1962-
FunctionCallInfo fcinfo = op->d.agg_deserialize.fcinfo_data;
1963-
LLVMValueRef v_fcinfo;
1964-
LLVMValueRef v_argnull0;
1965-
LLVMBasicBlockRef b_deserialize;
1966-
1967-
b_deserialize = l_bb_before_v(opblocks[opno + 1],
1968-
"op.%d.deserialize", opno);
1969-
1970-
v_fcinfo = l_ptr_const(fcinfo,
1971-
l_ptr(StructFunctionCallInfoData));
1972-
v_argnull0 = l_funcnull(b, v_fcinfo, 0);
1973-
1974-
LLVMBuildCondBr(b,
1975-
LLVMBuildICmp(b,
1976-
LLVMIntEQ,
1977-
v_argnull0,
1978-
l_sbool_const(1),
1979-
""),
1980-
opblocks[op->d.agg_deserialize.jumpnull],
1981-
b_deserialize);
1982-
LLVMPositionBuilderAtEnd(b, b_deserialize);
1983-
}
1984-
/* FALLTHROUGH */
1985-
19861933
case EEOP_AGG_DESERIALIZE:
19871934
{
19881935
AggState *aggstate;
1989-
FunctionCallInfo fcinfo;
1936+
FunctionCallInfo fcinfo = op->d.agg_deserialize.fcinfo_data;
19901937

19911938
LLVMValueRef v_retval;
19921939
LLVMValueRef v_fcinfo_isnull;
19931940
LLVMValueRef v_tmpcontext;
19941941
LLVMValueRef v_oldcontext;
19951942

1943+
if (opcode == EEOP_AGG_STRICT_DESERIALIZE)
1944+
{
1945+
LLVMValueRef v_fcinfo;
1946+
LLVMValueRef v_argnull0;
1947+
LLVMBasicBlockRef b_deserialize;
1948+
1949+
b_deserialize = l_bb_before_v(opblocks[opno + 1],
1950+
"op.%d.deserialize", opno);
1951+
1952+
v_fcinfo = l_ptr_const(fcinfo,
1953+
l_ptr(StructFunctionCallInfoData));
1954+
v_argnull0 = l_funcnull(b, v_fcinfo, 0);
1955+
1956+
LLVMBuildCondBr(b,
1957+
LLVMBuildICmp(b,
1958+
LLVMIntEQ,
1959+
v_argnull0,
1960+
l_sbool_const(1),
1961+
""),
1962+
opblocks[op->d.agg_deserialize.jumpnull],
1963+
b_deserialize);
1964+
LLVMPositionBuilderAtEnd(b, b_deserialize);
1965+
}
1966+
19961967
aggstate = castNode(AggState, state->parent);
19971968
fcinfo = op->d.agg_deserialize.fcinfo_data;
19981969

0 commit comments

Comments
 (0)