From 2a0faed9d7028e3830998bd6ca900be651274e27 Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Tue, 20 Mar 2018 02:20:46 -0700 Subject: [PATCH] Add expression compilation support to LLVM JIT provider. In addition to the interpretation of expressions (which back evaluation of WHERE clauses, target list projection, aggregates transition values etc) support compiling expressions to native code, using the infrastructure added in earlier commits. To avoid duplicating a lot of code, only support emitting code for cases that are likely to be performance critical. For expression steps that aren't deemed that, use the existing interpreter. The generated code isn't great - some architectural changes are required to address that. But this already yields a significant speedup for some analytics queries, particularly with WHERE clauses filtering a lot, or computing multiple aggregates. Author: Andres Freund Tested-By: Thomas Munro Discussion: https://postgr.es/m/20170901064131.tazjxwus3k2w3ybh@alap3.anarazel.de Disable JITing for VALUES() nodes. VALUES() nodes are only ever executed once. This is primarily helpful for debugging, when forcing JITing even for cheap queries. Author: Andres Freund Discussion: https://postgr.es/m/20170901064131.tazjxwus3k2w3ybh@alap3.anarazel.de --- src/backend/executor/execExpr.c | 4 + src/backend/executor/nodeValuesscan.c | 10 + src/backend/jit/jit.c | 37 + src/backend/jit/llvm/Makefile | 2 +- src/backend/jit/llvm/llvmjit.c | 64 + src/backend/jit/llvm/llvmjit_expr.c | 2637 +++++++++++++++++++++++++ src/backend/optimizer/plan/planner.c | 6 + src/backend/utils/fmgr/fmgr.c | 94 +- src/backend/utils/misc/guc.c | 11 + src/include/fmgr.h | 2 + src/include/jit/jit.h | 13 + src/include/jit/llvmjit.h | 12 + src/tools/pgindent/typedefs.list | 1 + 13 files changed, 2890 insertions(+), 3 deletions(-) create mode 100644 src/backend/jit/llvm/llvmjit_expr.c diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c index db5fcafbfe..13bf891cea 100644 --- a/src/backend/executor/execExpr.c +++ b/src/backend/executor/execExpr.c @@ -36,6 +36,7 @@ #include "executor/execExpr.h" #include "executor/nodeSubplan.h" #include "funcapi.h" +#include "jit/jit.h" #include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" @@ -623,6 +624,9 @@ ExecCheck(ExprState *state, ExprContext *econtext) static void ExecReadyExpr(ExprState *state) { + if (jit_compile_expr(state)) + return; + ExecReadyInterpretedExpr(state); } diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index 63b7e7ef5b..6ec087b968 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -25,6 +25,7 @@ #include "executor/executor.h" #include "executor/nodeValuesscan.h" +#include "jit/jit.h" #include "utils/expandeddatum.h" @@ -98,6 +99,7 @@ ValuesNext(ValuesScanState *node) bool *isnull; ListCell *lc; int resind; + int saved_jit_flags; /* * Get rid of any prior cycle's leftovers. We use ReScanExprContext @@ -128,7 +130,15 @@ ValuesNext(ValuesScanState *node) oldsubplans = node->ss.ps.subPlan; node->ss.ps.subPlan = NIL; + /* + * As the expressions are only ever used once, disable JIT for + * them. This is worthwhile because it's common to insert significant + * amounts of data via VALUES(). + */ + saved_jit_flags = econtext->ecxt_estate->es_jit_flags; + econtext->ecxt_estate->es_jit_flags = PGJIT_NONE; exprstatelist = ExecInitExprList(exprlist, &node->ss.ps); + econtext->ecxt_estate->es_jit_flags = saved_jit_flags; node->ss.ps.subPlan = oldsubplans; diff --git a/src/backend/jit/jit.c b/src/backend/jit/jit.c index 1d74ec8c91..971df4f8a5 100644 --- a/src/backend/jit/jit.c +++ b/src/backend/jit/jit.c @@ -24,6 +24,7 @@ #include "fmgr.h" +#include "executor/execExpr.h" #include "jit/jit.h" #include "miscadmin.h" #include "utils/resowner_private.h" @@ -35,6 +36,7 @@ bool jit_enabled = true; char *jit_provider = "llvmjit"; bool jit_debugging_support = false; bool jit_dump_bitcode = false; +bool jit_expressions = true; bool jit_profiling_support = false; double jit_above_cost = 100000; double jit_optimize_above_cost = 500000; @@ -143,6 +145,41 @@ jit_release_context(JitContext *context) pfree(context); } +/* + * Ask provider to JIT compile an expression. + * + * Returns true if successful, false if not. + */ +bool +jit_compile_expr(struct ExprState *state) +{ + /* + * We can easily create a one-off context for functions without an + * associated PlanState (and thus EState). But because there's no executor + * shutdown callback that could deallocate the created function, they'd + * live to the end of the transactions, where they'd be cleaned up by the + * resowner machinery. That can lead to a noticeable amount of memory + * usage, and worse, trigger some quadratic behaviour in gdb. Therefore, + * at least for now, don't create a JITed function in those circumstances. + */ + if (!state->parent) + return false; + + /* if no jitting should be performed at all */ + if (!(state->parent->state->es_jit_flags & PGJIT_PERFORM)) + return false; + + /* or if expressions aren't JITed */ + if (!(state->parent->state->es_jit_flags & PGJIT_EXPR)) + return false; + + /* this also takes !jit_enabled into account */ + if (provider_init()) + return provider.compile_expr(state); + + return false; +} + static bool file_exists(const char *name) { diff --git a/src/backend/jit/llvm/Makefile b/src/backend/jit/llvm/Makefile index 63b5e2db36..79097662d5 100644 --- a/src/backend/jit/llvm/Makefile +++ b/src/backend/jit/llvm/Makefile @@ -39,7 +39,7 @@ OBJS=$(WIN32RES) # Infrastructure OBJS += llvmjit.o llvmjit_error.o llvmjit_wrap.o # Code generation -OBJS += +OBJS += llvmjit_expr.o all: all-shared-lib llvmjit_types.bc diff --git a/src/backend/jit/llvm/llvmjit.c b/src/backend/jit/llvm/llvmjit.c index eaede90cca..21d36605a4 100644 --- a/src/backend/jit/llvm/llvmjit.c +++ b/src/backend/jit/llvm/llvmjit.c @@ -14,6 +14,7 @@ #include "postgres.h" #include "jit/llvmjit.h" +#include "jit/llvmjit_emit.h" #include "miscadmin.h" @@ -114,6 +115,7 @@ _PG_jit_provider_init(JitProviderCallbacks *cb) { cb->reset_after_error = llvm_reset_after_error; cb->release_context = llvm_release_context; + cb->compile_expr = llvm_compile_expr; } /* @@ -339,6 +341,68 @@ llvm_copy_attributes(LLVMValueRef v_from, LLVMValueRef v_to) } } +/* + * Return a callable LLVMValueRef for fcinfo. + */ +LLVMValueRef +llvm_function_reference(LLVMJitContext *context, + LLVMBuilderRef builder, + LLVMModuleRef mod, + FunctionCallInfo fcinfo) +{ + char *modname; + char *basename; + char *funcname; + + LLVMValueRef v_fn; + + fmgr_symbol(fcinfo->flinfo->fn_oid, &modname, &basename); + + if (modname != NULL && basename != NULL) + { + /* external function in loadable library */ + funcname = psprintf("pgextern.%s.%s", modname, basename); + } + else if (basename != NULL) + { + /* internal function */ + funcname = psprintf("%s", basename); + } + else + { + /* + * Function we don't know to handle, return pointer. We do so by + * creating a global constant containing a pointer to the function. + * Makes IR more readable. + */ + LLVMValueRef v_fn_addr; + + funcname = psprintf("pgoidextern.%u", + fcinfo->flinfo->fn_oid); + v_fn = LLVMGetNamedGlobal(mod, funcname); + if (v_fn != 0) + return LLVMBuildLoad(builder, v_fn, ""); + + v_fn_addr = l_ptr_const(fcinfo->flinfo->fn_addr, TypePGFunction); + + v_fn = LLVMAddGlobal(mod, TypePGFunction, funcname); + LLVMSetInitializer(v_fn, v_fn_addr); + LLVMSetGlobalConstant(v_fn, true); + + return LLVMBuildLoad(builder, v_fn, ""); + return v_fn; + } + + /* check if function already has been added */ + v_fn = LLVMGetNamedFunction(mod, funcname); + if (v_fn != 0) + return v_fn; + + v_fn = LLVMAddFunction(mod, funcname, LLVMGetElementType(TypePGFunction)); + + return v_fn; +} + /* * Optimize code in module using the flags set in context. */ diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c new file mode 100644 index 0000000000..80638d6d81 --- /dev/null +++ b/src/backend/jit/llvm/llvmjit_expr.c @@ -0,0 +1,2637 @@ +/*------------------------------------------------------------------------- + * + * llvmjit_expr.c + * JIT compile expressions. + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/jit/llvm/llvmjit_expr.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include +#include + +#include "access/htup_details.h" +#include "access/nbtree.h" +#include "access/tupconvert.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_type.h" +#include "executor/execdebug.h" +#include "executor/nodeAgg.h" +#include "executor/nodeSubplan.h" +#include "executor/execExpr.h" +#include "funcapi.h" +#include "jit/llvmjit.h" +#include "jit/llvmjit_emit.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/planner.h" +#include "parser/parse_coerce.h" +#include "parser/parsetree.h" +#include "pgstat.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/date.h" +#include "utils/fmgrtab.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/timestamp.h" +#include "utils/typcache.h" +#include "utils/xml.h" + + +typedef struct CompiledExprState +{ + LLVMJitContext *context; + const char *funcname; +} CompiledExprState; + + +static Datum ExecRunCompiledExpr(ExprState *state, ExprContext *econtext, bool *isNull); + +static LLVMValueRef BuildV1Call(LLVMJitContext *context, LLVMBuilderRef b, + LLVMModuleRef mod, FunctionCallInfo fcinfo, + LLVMValueRef *v_fcinfo_isnull); +static void build_EvalXFunc(LLVMBuilderRef b, LLVMModuleRef mod, + const char *funcname, + LLVMValueRef v_state, LLVMValueRef v_econtext, + ExprEvalStep *op); +static LLVMValueRef create_LifetimeEnd(LLVMModuleRef mod); + + +/* + * JIT compile expression. + */ +bool +llvm_compile_expr(ExprState *state) +{ + PlanState *parent = state->parent; + int i; + char *funcname; + + LLVMJitContext *context = NULL; + + LLVMBuilderRef b; + LLVMModuleRef mod; + LLVMTypeRef eval_sig; + LLVMValueRef eval_fn; + LLVMBasicBlockRef entry; + LLVMBasicBlockRef *opblocks; + + /* state itself */ + LLVMValueRef v_state; + LLVMValueRef v_econtext; + + /* returnvalue */ + LLVMValueRef v_isnullp; + + /* tmp vars in state */ + LLVMValueRef v_tmpvaluep; + LLVMValueRef v_tmpisnullp; + + /* slots */ + LLVMValueRef v_innerslot; + LLVMValueRef v_outerslot; + LLVMValueRef v_scanslot; + LLVMValueRef v_resultslot; + + /* nulls/values of slots */ + LLVMValueRef v_innervalues; + LLVMValueRef v_innernulls; + LLVMValueRef v_outervalues; + LLVMValueRef v_outernulls; + LLVMValueRef v_scanvalues; + LLVMValueRef v_scannulls; + LLVMValueRef v_resultvalues; + LLVMValueRef v_resultnulls; + + /* stuff in econtext */ + LLVMValueRef v_aggvalues; + LLVMValueRef v_aggnulls; + + instr_time starttime; + instr_time endtime; + + llvm_enter_fatal_on_oom(); + + /* get or create JIT context */ + if (parent && parent->state->es_jit) + { + context = (LLVMJitContext *) parent->state->es_jit; + } + else + { + context = llvm_create_context(parent->state->es_jit_flags); + + if (parent) + { + parent->state->es_jit = &context->base; + } + + } + + INSTR_TIME_SET_CURRENT(starttime); + + mod = llvm_mutable_module(context); + + b = LLVMCreateBuilder(); + + funcname = llvm_expand_funcname(context, "evalexpr"); + + /* Create the signature and function */ + { + LLVMTypeRef param_types[3]; + + param_types[0] = l_ptr(StructExprState); /* state */ + param_types[1] = l_ptr(StructExprContext); /* econtext */ + param_types[2] = l_ptr(LLVMInt8Type()); /* isnull */ + + eval_sig = LLVMFunctionType(TypeSizeT, + param_types, lengthof(param_types), + false); + } + eval_fn = LLVMAddFunction(mod, funcname, eval_sig); + LLVMSetLinkage(eval_fn, LLVMExternalLinkage); + LLVMSetVisibility(eval_fn, LLVMDefaultVisibility); + llvm_copy_attributes(AttributeTemplate, eval_fn); + + entry = LLVMAppendBasicBlock(eval_fn, "entry"); + + /* build state */ + v_state = LLVMGetParam(eval_fn, 0); + v_econtext = LLVMGetParam(eval_fn, 1); + v_isnullp = LLVMGetParam(eval_fn, 2); + + LLVMPositionBuilderAtEnd(b, entry); + + v_tmpvaluep = LLVMBuildStructGEP(b, v_state, + FIELDNO_EXPRSTATE_RESVALUE, + "v.state.resvalue"); + v_tmpisnullp = LLVMBuildStructGEP(b, v_state, + FIELDNO_EXPRSTATE_RESNULL, + "v.state.resnull"); + + /* build global slots */ + v_scanslot = l_load_struct_gep(b, v_econtext, + FIELDNO_EXPRCONTEXT_SCANTUPLE, + "v_scanslot"); + v_innerslot = l_load_struct_gep(b, v_econtext, + FIELDNO_EXPRCONTEXT_INNERTUPLE, + "v_innerslot"); + v_outerslot = l_load_struct_gep(b, v_econtext, + FIELDNO_EXPRCONTEXT_OUTERTUPLE, + "v_outerslot"); + v_resultslot = l_load_struct_gep(b, v_state, + FIELDNO_EXPRSTATE_RESULTSLOT, + "v_resultslot"); + + /* build global values/isnull pointers */ + v_scanvalues = l_load_struct_gep(b, v_scanslot, + FIELDNO_TUPLETABLESLOT_VALUES, + "v_scanvalues"); + v_scannulls = l_load_struct_gep(b, v_scanslot, + FIELDNO_TUPLETABLESLOT_ISNULL, + "v_scannulls"); + v_innervalues = l_load_struct_gep(b, v_innerslot, + FIELDNO_TUPLETABLESLOT_VALUES, + "v_innervalues"); + v_innernulls = l_load_struct_gep(b, v_innerslot, + FIELDNO_TUPLETABLESLOT_ISNULL, + "v_innernulls"); + v_outervalues = l_load_struct_gep(b, v_outerslot, + FIELDNO_TUPLETABLESLOT_VALUES, + "v_outervalues"); + v_outernulls = l_load_struct_gep(b, v_outerslot, + FIELDNO_TUPLETABLESLOT_ISNULL, + "v_outernulls"); + v_resultvalues = l_load_struct_gep(b, v_resultslot, + FIELDNO_TUPLETABLESLOT_VALUES, + "v_resultvalues"); + v_resultnulls = l_load_struct_gep(b, v_resultslot, + FIELDNO_TUPLETABLESLOT_ISNULL, + "v_resultnulls"); + + /* aggvalues/aggnulls */ + v_aggvalues = l_load_struct_gep(b, v_econtext, + FIELDNO_EXPRCONTEXT_AGGVALUES, + "v.econtext.aggvalues"); + v_aggnulls = l_load_struct_gep(b, v_econtext, + FIELDNO_EXPRCONTEXT_AGGNULLS, + "v.econtext.aggnulls"); + + /* allocate blocks for each op upfront, so we can do jumps easily */ + opblocks = palloc(sizeof(LLVMBasicBlockRef) * state->steps_len); + for (i = 0; i < state->steps_len; i++) + opblocks[i] = l_bb_append_v(eval_fn, "b.op.%d.start", i); + + /* jump from entry to first block */ + LLVMBuildBr(b, opblocks[0]); + + for (i = 0; i < state->steps_len; i++) + { + ExprEvalStep *op; + ExprEvalOp opcode; + LLVMValueRef v_resvaluep; + LLVMValueRef v_resnullp; + + LLVMPositionBuilderAtEnd(b, opblocks[i]); + + op = &state->steps[i]; + opcode = ExecEvalStepOp(state, op); + + v_resvaluep = l_ptr_const(op->resvalue, l_ptr(TypeSizeT)); + v_resnullp = l_ptr_const(op->resnull, l_ptr(LLVMInt8Type())); + + switch (opcode) + { + case EEOP_DONE: + { + LLVMValueRef v_tmpisnull, + v_tmpvalue; + + v_tmpvalue = LLVMBuildLoad(b, v_tmpvaluep, ""); + v_tmpisnull = LLVMBuildLoad(b, v_tmpisnullp, ""); + + LLVMBuildStore(b, v_tmpisnull, v_isnullp); + + LLVMBuildRet(b, v_tmpvalue); + break; + } + + case EEOP_INNER_FETCHSOME: + case EEOP_OUTER_FETCHSOME: + case EEOP_SCAN_FETCHSOME: + { + LLVMValueRef v_slot; + LLVMBasicBlockRef b_fetch; + LLVMValueRef v_nvalid; + + b_fetch = l_bb_before_v(opblocks[i + 1], + "op.%d.fetch", i); + + if (opcode == EEOP_INNER_FETCHSOME) + { + v_slot = v_innerslot; + } + else if (opcode == EEOP_OUTER_FETCHSOME) + { + v_slot = v_outerslot; + } + else + { + v_slot = v_scanslot; + } + + /* + * Check if all required attributes are available, or + * whether deforming is required. + */ + v_nvalid = + l_load_struct_gep(b, v_slot, + FIELDNO_TUPLETABLESLOT_NVALID, + ""); + LLVMBuildCondBr(b, + LLVMBuildICmp(b, LLVMIntUGE, v_nvalid, + l_int32_const(op->d.fetch.last_var), + ""), + opblocks[i + 1], b_fetch); + + LLVMPositionBuilderAtEnd(b, b_fetch); + + { + LLVMValueRef params[2]; + + params[0] = v_slot; + params[1] = l_int32_const(op->d.fetch.last_var); + + LLVMBuildCall(b, + llvm_get_decl(mod, FuncSlotGetsomeattrs), + params, lengthof(params), ""); + } + + LLVMBuildBr(b, opblocks[i + 1]); + break; + } + + case EEOP_INNER_VAR: + case EEOP_OUTER_VAR: + case EEOP_SCAN_VAR: + { + LLVMValueRef value, + isnull; + LLVMValueRef v_attnum; + LLVMValueRef v_values; + LLVMValueRef v_nulls; + + if (opcode == EEOP_INNER_VAR) + { + v_values = v_innervalues; + v_nulls = v_innernulls; + } + else if (opcode == EEOP_OUTER_VAR) + { + v_values = v_outervalues; + v_nulls = v_outernulls; + } + else + { + v_values = v_scanvalues; + v_nulls = v_scannulls; + } + + v_attnum = l_int32_const(op->d.var.attnum); + value = l_load_gep1(b, v_values, v_attnum, ""); + isnull = l_load_gep1(b, v_nulls, v_attnum, ""); + LLVMBuildStore(b, value, v_resvaluep); + LLVMBuildStore(b, isnull, v_resnullp); + + LLVMBuildBr(b, opblocks[i + 1]); + break; + } + + case EEOP_INNER_SYSVAR: + case EEOP_OUTER_SYSVAR: + case EEOP_SCAN_SYSVAR: + { + int attnum = op->d.var.attnum; + LLVMValueRef v_attnum; + LLVMValueRef v_tuple; + LLVMValueRef v_tupleDescriptor; + LLVMValueRef v_params[4]; + LLVMValueRef v_syscol; + LLVMValueRef v_slot; + + if (opcode == EEOP_INNER_SYSVAR) + v_slot = v_innerslot; + else if (opcode == EEOP_OUTER_SYSVAR) + v_slot = v_outerslot; + else + v_slot = v_scanslot; + + Assert(op->d.var.attnum < 0); + + v_tuple = l_load_struct_gep(b, v_slot, + FIELDNO_TUPLETABLESLOT_TUPLE, + "v.tuple"); + + /* + * Could optimize this a bit for fixed descriptors, but + * this shouldn't be that critical a path. + */ + v_tupleDescriptor = + l_load_struct_gep(b, v_slot, + FIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR, + "v.tupledesc"); + v_attnum = l_int32_const(attnum); + + v_params[0] = v_tuple; + v_params[1] = v_attnum; + v_params[2] = v_tupleDescriptor; + v_params[3] = v_resnullp; + v_syscol = LLVMBuildCall(b, + llvm_get_decl(mod, FuncHeapGetsysattr), + v_params, lengthof(v_params), + ""); + LLVMBuildStore(b, v_syscol, v_resvaluep); + + LLVMBuildBr(b, opblocks[i + 1]); + break; + } + + case EEOP_WHOLEROW: + build_EvalXFunc(b, mod, "ExecEvalWholeRowVar", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_ASSIGN_INNER_VAR: + case EEOP_ASSIGN_OUTER_VAR: + case EEOP_ASSIGN_SCAN_VAR: + { + LLVMValueRef v_value, + v_isnull; + LLVMValueRef v_rvaluep, + v_risnullp; + LLVMValueRef v_attnum, + v_resultnum; + LLVMValueRef v_values; + LLVMValueRef v_nulls; + + if (opcode == EEOP_ASSIGN_INNER_VAR) + { + v_values = v_innervalues; + v_nulls = v_innernulls; + } + else if (opcode == EEOP_ASSIGN_OUTER_VAR) + { + v_values = v_outervalues; + v_nulls = v_outernulls; + } + else + { + v_values = v_scanvalues; + v_nulls = v_scannulls; + } + + /* load data */ + v_attnum = l_int32_const(op->d.assign_var.attnum); + v_value = l_load_gep1(b, v_values, v_attnum, ""); + v_isnull = l_load_gep1(b, v_nulls, v_attnum, ""); + + /* compute addresses of targets */ + v_resultnum = l_int32_const(op->d.assign_var.resultnum); + v_rvaluep = LLVMBuildGEP(b, v_resultvalues, + &v_resultnum, 1, ""); + v_risnullp = LLVMBuildGEP(b, v_resultnulls, + &v_resultnum, 1, ""); + + /* and store */ + LLVMBuildStore(b, v_value, v_rvaluep); + LLVMBuildStore(b, v_isnull, v_risnullp); + + LLVMBuildBr(b, opblocks[i + 1]); + break; + } + + case EEOP_ASSIGN_TMP: + { + LLVMValueRef v_value, + v_isnull; + LLVMValueRef v_rvaluep, + v_risnullp; + LLVMValueRef v_resultnum; + size_t resultnum = op->d.assign_tmp.resultnum; + + /* 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, ""); + + /* and store */ + LLVMBuildStore(b, v_value, v_rvaluep); + LLVMBuildStore(b, v_isnull, v_risnullp); + + LLVMBuildBr(b, opblocks[i + 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[i + 1], + "op.%d.assign_tmp.notnull", i); + + /* 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 */ + LLVMBuildStore(b, v_isnull, v_risnullp); + + /* check if value is NULL */ + LLVMBuildCondBr(b, + LLVMBuildICmp(b, LLVMIntEQ, v_isnull, + l_int8_const(0), ""), + b_notnull, opblocks[i + 1]); + + /* 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), ""); + + /* store value */ + LLVMBuildStore(b, v_ret, v_rvaluep); + + LLVMBuildBr(b, opblocks[i + 1]); + break; + } + + case EEOP_CONST: + { + LLVMValueRef v_constvalue, + v_constnull; + + v_constvalue = l_sizet_const(op->d.constval.value); + v_constnull = l_int8_const(op->d.constval.isnull); + + LLVMBuildStore(b, v_constvalue, v_resvaluep); + LLVMBuildStore(b, v_constnull, v_resnullp); + + LLVMBuildBr(b, opblocks[i + 1]); + break; + } + + case EEOP_FUNCEXPR_STRICT: + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + LLVMBasicBlockRef b_nonull; + int argno; + LLVMValueRef v_fcinfo; + LLVMValueRef v_argnullp; + LLVMBasicBlockRef *b_checkargnulls; + + /* + * Block for the actual function call, if args are + * non-NULL. + */ + b_nonull = l_bb_before_v(opblocks[i + 1], + "b.%d.no-null-args", i); + + /* 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)); + + v_argnullp = + LLVMBuildStructGEP(b, + v_fcinfo, + FIELDNO_FUNCTIONCALLINFODATA_ARGNULL, + "v_argnullp"); + + /* + * set resnull to true, if the function is actually + * called, it'll be reset + */ + LLVMBuildStore(b, l_int8_const(1), v_resnullp); + + /* create blocks for checking args, one for each */ + b_checkargnulls = + palloc(sizeof(LLVMBasicBlockRef *) * op->d.func.nargs); + for (argno = 0; argno < op->d.func.nargs; argno++) + b_checkargnulls[argno] = + l_bb_before_v(b_nonull, "b.%d.isnull.%d", i, argno); + + /* jump to check of first argument */ + LLVMBuildBr(b, b_checkargnulls[0]); + + /* check each arg for NULLness */ + for (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_load_struct_gep(b, v_argnullp, + argno, ""); + LLVMBuildCondBr(b, + LLVMBuildICmp(b, LLVMIntEQ, + v_argisnull, + l_int8_const(1), + ""), + opblocks[i + 1], + b_argnotnull); + } + + LLVMPositionBuilderAtEnd(b, b_nonull); + } + /* explicit 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); + LLVMBuildStore(b, v_fcinfo_isnull, v_resnullp); + + LLVMBuildBr(b, opblocks[i + 1]); + break; + } + + case EEOP_FUNCEXPR_FUSAGE: + build_EvalXFunc(b, mod, "ExecEvalFuncExprFusage", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + + case EEOP_FUNCEXPR_STRICT_FUSAGE: + build_EvalXFunc(b, mod, "ExecEvalFuncExprStrictFusage", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_BOOL_AND_STEP_FIRST: + { + LLVMValueRef v_boolanynullp; + + v_boolanynullp = l_ptr_const(op->d.boolexpr.anynull, + l_ptr(LLVMInt8Type())); + LLVMBuildStore(b, l_int8_const(0), v_boolanynullp); + + /* intentionally fall through */ + } + + /* + * 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: + { + LLVMValueRef v_boolvalue; + LLVMValueRef v_boolnull; + LLVMValueRef v_boolanynullp, + v_boolanynull; + LLVMBasicBlockRef b_boolisnull; + LLVMBasicBlockRef b_boolcheckfalse; + LLVMBasicBlockRef b_boolisfalse; + LLVMBasicBlockRef b_boolcont; + LLVMBasicBlockRef b_boolisanynull; + + b_boolisnull = l_bb_before_v(opblocks[i + 1], + "b.%d.boolisnull", i); + b_boolcheckfalse = l_bb_before_v(opblocks[i + 1], + "b.%d.boolcheckfalse", i); + b_boolisfalse = l_bb_before_v(opblocks[i + 1], + "b.%d.boolisfalse", i); + b_boolisanynull = l_bb_before_v(opblocks[i + 1], + "b.%d.boolisanynull", i); + b_boolcont = l_bb_before_v(opblocks[i + 1], + "b.%d.boolcont", i); + + v_boolanynullp = l_ptr_const(op->d.boolexpr.anynull, + l_ptr(LLVMInt8Type())); + + v_boolnull = LLVMBuildLoad(b, v_resnullp, ""); + v_boolvalue = LLVMBuildLoad(b, v_resvaluep, ""); + + /* set resnull to boolnull */ + LLVMBuildStore(b, v_boolnull, v_resnullp); + /* set revalue to boolvalue */ + LLVMBuildStore(b, v_boolvalue, v_resvaluep); + + /* check if current input is NULL */ + LLVMBuildCondBr(b, + LLVMBuildICmp(b, LLVMIntEQ, v_boolnull, + l_int8_const(1), ""), + b_boolisnull, + b_boolcheckfalse); + + /* build block that sets anynull */ + LLVMPositionBuilderAtEnd(b, b_boolisnull); + /* set boolanynull to true */ + LLVMBuildStore(b, l_int8_const(1), v_boolanynullp); + /* and jump to next block */ + LLVMBuildBr(b, b_boolcont); + + /* build block checking for false */ + LLVMPositionBuilderAtEnd(b, b_boolcheckfalse); + LLVMBuildCondBr(b, + LLVMBuildICmp(b, LLVMIntEQ, v_boolvalue, + l_sizet_const(0), ""), + b_boolisfalse, + b_boolcont); + + /* + * Build block handling FALSE. Value is false, so short + * circuit. + */ + LLVMPositionBuilderAtEnd(b, b_boolisfalse); + /* result is already set to FALSE, need not change it */ + /* and jump to the end of the AND expression */ + LLVMBuildBr(b, opblocks[op->d.boolexpr.jumpdone]); + + /* Build block that continues if bool is TRUE. */ + LLVMPositionBuilderAtEnd(b, b_boolcont); + + v_boolanynull = LLVMBuildLoad(b, v_boolanynullp, ""); + + /* set value to NULL if any previous values were NULL */ + LLVMBuildCondBr(b, + LLVMBuildICmp(b, LLVMIntEQ, v_boolanynull, + l_int8_const(0), ""), + opblocks[i + 1], b_boolisanynull); + + LLVMPositionBuilderAtEnd(b, b_boolisanynull); + /* set resnull to true */ + LLVMBuildStore(b, l_int8_const(1), v_resnullp); + /* reset resvalue */ + LLVMBuildStore(b, l_sizet_const(0), v_resvaluep); + + LLVMBuildBr(b, opblocks[i + 1]); + break; + } + case EEOP_BOOL_OR_STEP_FIRST: + { + LLVMValueRef v_boolanynullp; + + v_boolanynullp = l_ptr_const(op->d.boolexpr.anynull, + l_ptr(LLVMInt8Type())); + LLVMBuildStore(b, l_int8_const(0), v_boolanynullp); + + /* intentionally fall through */ + } + + /* + * 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: + { + LLVMValueRef v_boolvalue; + LLVMValueRef v_boolnull; + LLVMValueRef v_boolanynullp, + v_boolanynull; + + LLVMBasicBlockRef b_boolisnull; + LLVMBasicBlockRef b_boolchecktrue; + LLVMBasicBlockRef b_boolistrue; + LLVMBasicBlockRef b_boolcont; + LLVMBasicBlockRef b_boolisanynull; + + b_boolisnull = l_bb_before_v(opblocks[i + 1], + "b.%d.boolisnull", i); + b_boolchecktrue = l_bb_before_v(opblocks[i + 1], + "b.%d.boolchecktrue", i); + b_boolistrue = l_bb_before_v(opblocks[i + 1], + "b.%d.boolistrue", i); + b_boolisanynull = l_bb_before_v(opblocks[i + 1], + "b.%d.boolisanynull", i); + b_boolcont = l_bb_before_v(opblocks[i + 1], + "b.%d.boolcont", i); + + v_boolanynullp = l_ptr_const(op->d.boolexpr.anynull, + l_ptr(LLVMInt8Type())); + + v_boolnull = LLVMBuildLoad(b, v_resnullp, ""); + v_boolvalue = LLVMBuildLoad(b, v_resvaluep, ""); + + /* set resnull to boolnull */ + LLVMBuildStore(b, v_boolnull, v_resnullp); + /* set revalue to boolvalue */ + LLVMBuildStore(b, v_boolvalue, v_resvaluep); + + LLVMBuildCondBr(b, + LLVMBuildICmp(b, LLVMIntEQ, v_boolnull, + l_int8_const(1), ""), + b_boolisnull, + b_boolchecktrue); + + /* build block that sets anynull */ + LLVMPositionBuilderAtEnd(b, b_boolisnull); + /* set boolanynull to true */ + LLVMBuildStore(b, l_int8_const(1), v_boolanynullp); + /* and jump to next block */ + LLVMBuildBr(b, b_boolcont); + + /* build block checking for true */ + LLVMPositionBuilderAtEnd(b, b_boolchecktrue); + LLVMBuildCondBr(b, + LLVMBuildICmp(b, LLVMIntEQ, v_boolvalue, + l_sizet_const(1), ""), + b_boolistrue, + b_boolcont); + + /* + * Build block handling True. Value is true, so short + * circuit. + */ + LLVMPositionBuilderAtEnd(b, b_boolistrue); + /* result is already set to TRUE, need not change it */ + /* and jump to the end of the OR expression */ + LLVMBuildBr(b, opblocks[op->d.boolexpr.jumpdone]); + + /* build block that continues if bool is FALSE */ + LLVMPositionBuilderAtEnd(b, b_boolcont); + + v_boolanynull = LLVMBuildLoad(b, v_boolanynullp, ""); + + /* set value to NULL if any previous values were NULL */ + LLVMBuildCondBr(b, + LLVMBuildICmp(b, LLVMIntEQ, v_boolanynull, + l_int8_const(0), ""), + opblocks[i + 1], b_boolisanynull); + + LLVMPositionBuilderAtEnd(b, b_boolisanynull); + /* set resnull to true */ + LLVMBuildStore(b, l_int8_const(1), v_resnullp); + /* reset resvalue */ + LLVMBuildStore(b, l_sizet_const(0), v_resvaluep); + + LLVMBuildBr(b, opblocks[i + 1]); + break; + } + + case EEOP_BOOL_NOT_STEP: + { + LLVMValueRef v_boolvalue; + LLVMValueRef v_boolnull; + LLVMValueRef v_negbool; + + v_boolnull = LLVMBuildLoad(b, v_resnullp, ""); + v_boolvalue = LLVMBuildLoad(b, v_resvaluep, ""); + + v_negbool = LLVMBuildZExt(b, + LLVMBuildICmp(b, LLVMIntEQ, + v_boolvalue, + l_sizet_const(0), + ""), + TypeSizeT, ""); + /* set resnull to boolnull */ + LLVMBuildStore(b, v_boolnull, v_resnullp); + /* set revalue to !boolvalue */ + LLVMBuildStore(b, v_negbool, v_resvaluep); + + LLVMBuildBr(b, opblocks[i + 1]); + break; + } + + case EEOP_QUAL: + { + LLVMValueRef v_resnull; + LLVMValueRef v_resvalue; + LLVMValueRef v_nullorfalse; + LLVMBasicBlockRef b_qualfail; + + b_qualfail = l_bb_before_v(opblocks[i + 1], + "op.%d.qualfail", i); + + v_resvalue = LLVMBuildLoad(b, v_resvaluep, ""); + v_resnull = LLVMBuildLoad(b, v_resnullp, ""); + + v_nullorfalse = + LLVMBuildOr(b, + LLVMBuildICmp(b, LLVMIntEQ, v_resnull, + l_int8_const(1), ""), + LLVMBuildICmp(b, LLVMIntEQ, v_resvalue, + l_sizet_const(0), ""), + ""); + + LLVMBuildCondBr(b, + v_nullorfalse, + b_qualfail, + opblocks[i + 1]); + + /* build block handling NULL or false */ + LLVMPositionBuilderAtEnd(b, b_qualfail); + /* set resnull to false */ + LLVMBuildStore(b, l_int8_const(0), v_resnullp); + /* set resvalue to false */ + LLVMBuildStore(b, l_sizet_const(0), v_resvaluep); + /* and jump out */ + LLVMBuildBr(b, opblocks[op->d.qualexpr.jumpdone]); + break; + } + + case EEOP_JUMP: + { + LLVMBuildBr(b, opblocks[op->d.jump.jumpdone]); + break; + } + + case EEOP_JUMP_IF_NULL: + { + LLVMValueRef v_resnull; + + /* Transfer control if current result is null */ + + v_resnull = LLVMBuildLoad(b, v_resnullp, ""); + + LLVMBuildCondBr(b, + LLVMBuildICmp(b, LLVMIntEQ, v_resnull, + l_int8_const(1), ""), + opblocks[op->d.jump.jumpdone], + opblocks[i + 1]); + break; + } + + case EEOP_JUMP_IF_NOT_NULL: + { + LLVMValueRef v_resnull; + + /* Transfer control if current result is non-null */ + + v_resnull = LLVMBuildLoad(b, v_resnullp, ""); + + LLVMBuildCondBr(b, + LLVMBuildICmp(b, LLVMIntEQ, v_resnull, + l_int8_const(0), ""), + opblocks[op->d.jump.jumpdone], + opblocks[i + 1]); + break; + } + + + case EEOP_JUMP_IF_NOT_TRUE: + { + LLVMValueRef v_resnull; + LLVMValueRef v_resvalue; + LLVMValueRef v_nullorfalse; + + /* Transfer control if current result is null or false */ + + v_resvalue = LLVMBuildLoad(b, v_resvaluep, ""); + v_resnull = LLVMBuildLoad(b, v_resnullp, ""); + + v_nullorfalse = + LLVMBuildOr(b, + LLVMBuildICmp(b, LLVMIntEQ, v_resnull, + l_int8_const(1), ""), + LLVMBuildICmp(b, LLVMIntEQ, v_resvalue, + l_sizet_const(0), ""), + ""); + + LLVMBuildCondBr(b, + v_nullorfalse, + opblocks[op->d.jump.jumpdone], + opblocks[i + 1]); + break; + } + + case EEOP_NULLTEST_ISNULL: + { + LLVMValueRef v_resnull = LLVMBuildLoad(b, v_resnullp, ""); + LLVMValueRef v_resvalue; + + v_resvalue = + LLVMBuildSelect(b, + LLVMBuildICmp(b, LLVMIntEQ, v_resnull, + l_int8_const(1), ""), + l_sizet_const(1), + l_sizet_const(0), + ""); + LLVMBuildStore(b, v_resvalue, v_resvaluep); + LLVMBuildStore(b, l_int8_const(0), v_resnullp); + + LLVMBuildBr(b, opblocks[i + 1]); + break; + } + + case EEOP_NULLTEST_ISNOTNULL: + { + LLVMValueRef v_resnull = LLVMBuildLoad(b, v_resnullp, ""); + LLVMValueRef v_resvalue; + + v_resvalue = + LLVMBuildSelect(b, + LLVMBuildICmp(b, LLVMIntEQ, v_resnull, + l_int8_const(1), ""), + l_sizet_const(0), + l_sizet_const(1), + ""); + LLVMBuildStore(b, v_resvalue, v_resvaluep); + LLVMBuildStore(b, l_int8_const(0), v_resnullp); + + LLVMBuildBr(b, opblocks[i + 1]); + break; + } + + case EEOP_NULLTEST_ROWISNULL: + build_EvalXFunc(b, mod, "ExecEvalRowNull", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_NULLTEST_ROWISNOTNULL: + build_EvalXFunc(b, mod, "ExecEvalRowNotNull", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_BOOLTEST_IS_TRUE: + case EEOP_BOOLTEST_IS_NOT_FALSE: + case EEOP_BOOLTEST_IS_FALSE: + case EEOP_BOOLTEST_IS_NOT_TRUE: + { + LLVMBasicBlockRef b_isnull, + b_notnull; + LLVMValueRef v_resnull = LLVMBuildLoad(b, v_resnullp, ""); + + b_isnull = l_bb_before_v(opblocks[i + 1], + "op.%d.isnull", i); + b_notnull = l_bb_before_v(opblocks[i + 1], + "op.%d.isnotnull", i); + + /* check if value is NULL */ + LLVMBuildCondBr(b, + LLVMBuildICmp(b, LLVMIntEQ, v_resnull, + l_int8_const(1), ""), + b_isnull, b_notnull); + + /* if value is NULL, return false */ + LLVMPositionBuilderAtEnd(b, b_isnull); + + /* result is not null */ + LLVMBuildStore(b, l_int8_const(0), v_resnullp); + + if (opcode == EEOP_BOOLTEST_IS_TRUE || + opcode == EEOP_BOOLTEST_IS_FALSE) + { + LLVMBuildStore(b, l_sizet_const(0), v_resvaluep); + } + else + { + LLVMBuildStore(b, l_sizet_const(1), v_resvaluep); + } + + LLVMBuildBr(b, opblocks[i + 1]); + + LLVMPositionBuilderAtEnd(b, b_notnull); + + if (opcode == EEOP_BOOLTEST_IS_TRUE || + opcode == EEOP_BOOLTEST_IS_NOT_FALSE) + { + /* + * if value is not null NULL, return value (already + * set) + */ + } + else + { + LLVMValueRef v_value = + LLVMBuildLoad(b, v_resvaluep, ""); + + v_value = LLVMBuildZExt(b, + LLVMBuildICmp(b, LLVMIntEQ, + v_value, + l_sizet_const(0), + ""), + TypeSizeT, ""); + LLVMBuildStore(b, v_value, v_resvaluep); + } + LLVMBuildBr(b, opblocks[i + 1]); + break; + } + + case EEOP_PARAM_EXEC: + build_EvalXFunc(b, mod, "ExecEvalParamExec", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_PARAM_EXTERN: + build_EvalXFunc(b, mod, "ExecEvalParamExtern", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_PARAM_CALLBACK: + { + LLVMTypeRef param_types[3]; + LLVMValueRef v_params[3]; + LLVMTypeRef v_functype; + LLVMValueRef v_func; + + param_types[0] = l_ptr(StructExprState); + param_types[1] = l_ptr(TypeSizeT); + param_types[2] = l_ptr(StructExprContext); + + v_functype = LLVMFunctionType(LLVMVoidType(), + param_types, + lengthof(param_types), + false); + v_func = l_ptr_const(op->d.cparam.paramfunc, + l_ptr(v_functype)); + + v_params[0] = v_state; + v_params[1] = l_ptr_const(op, l_ptr(TypeSizeT)); + v_params[2] = v_econtext; + LLVMBuildCall(b, + v_func, + v_params, lengthof(v_params), ""); + + LLVMBuildBr(b, opblocks[i + 1]); + break; + } + + case EEOP_ARRAYREF_OLD: + build_EvalXFunc(b, mod, "ExecEvalArrayRefOld", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_ARRAYREF_ASSIGN: + build_EvalXFunc(b, mod, "ExecEvalArrayRefAssign", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_ARRAYREF_FETCH: + build_EvalXFunc(b, mod, "ExecEvalArrayRefFetch", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_CASE_TESTVAL: + { + LLVMBasicBlockRef b_avail, + b_notavail; + LLVMValueRef v_casevaluep, + v_casevalue; + LLVMValueRef v_casenullp, + v_casenull; + LLVMValueRef v_casevaluenull; + + b_avail = l_bb_before_v(opblocks[i + 1], + "op.%d.avail", i); + b_notavail = l_bb_before_v(opblocks[i + 1], + "op.%d.notavail", i); + + v_casevaluep = l_ptr_const(op->d.casetest.value, + l_ptr(TypeSizeT)); + v_casenullp = l_ptr_const(op->d.casetest.isnull, + l_ptr(LLVMInt8Type())); + + v_casevaluenull = + LLVMBuildICmp(b, LLVMIntEQ, + LLVMBuildPtrToInt(b, v_casevaluep, + TypeSizeT, ""), + l_sizet_const(0), ""); + LLVMBuildCondBr(b, v_casevaluenull, b_notavail, b_avail); + + /* if casetest != NULL */ + LLVMPositionBuilderAtEnd(b, b_avail); + v_casevalue = LLVMBuildLoad(b, v_casevaluep, ""); + v_casenull = LLVMBuildLoad(b, v_casenullp, ""); + LLVMBuildStore(b, v_casevalue, v_resvaluep); + LLVMBuildStore(b, v_casenull, v_resnullp); + LLVMBuildBr(b, opblocks[i + 1]); + + /* if casetest == NULL */ + LLVMPositionBuilderAtEnd(b, b_notavail); + v_casevalue = + l_load_struct_gep(b, v_econtext, + FIELDNO_EXPRCONTEXT_CASEDATUM, ""); + v_casenull = + l_load_struct_gep(b, v_econtext, + FIELDNO_EXPRCONTEXT_CASENULL, ""); + LLVMBuildStore(b, v_casevalue, v_resvaluep); + LLVMBuildStore(b, v_casenull, v_resnullp); + + LLVMBuildBr(b, opblocks[i + 1]); + break; + } + + case EEOP_MAKE_READONLY: + { + LLVMBasicBlockRef b_notnull; + LLVMValueRef v_params[1]; + LLVMValueRef v_ret; + LLVMValueRef v_nullp; + LLVMValueRef v_valuep; + LLVMValueRef v_null; + LLVMValueRef v_value; + + b_notnull = l_bb_before_v(opblocks[i + 1], + "op.%d.readonly.notnull", i); + + v_nullp = l_ptr_const(op->d.make_readonly.isnull, + l_ptr(LLVMInt8Type())); + + v_null = LLVMBuildLoad(b, v_nullp, ""); + + /* store null isnull value in result */ + LLVMBuildStore(b, v_null, v_resnullp); + + /* check if value is NULL */ + LLVMBuildCondBr(b, + LLVMBuildICmp(b, LLVMIntEQ, v_null, + l_int8_const(1), ""), + opblocks[i + 1], b_notnull); + + /* if value is not null, convert to RO datum */ + LLVMPositionBuilderAtEnd(b, b_notnull); + + v_valuep = l_ptr_const(op->d.make_readonly.value, + l_ptr(TypeSizeT)); + + v_value = LLVMBuildLoad(b, v_valuep, ""); + + v_params[0] = v_value; + v_ret = + LLVMBuildCall(b, + llvm_get_decl(mod, FuncMakeExpandedObjectReadOnlyInternal), + v_params, lengthof(v_params), ""); + LLVMBuildStore(b, v_ret, v_resvaluep); + + LLVMBuildBr(b, opblocks[i + 1]); + break; + } + + case EEOP_IOCOERCE: + { + FunctionCallInfo fcinfo_out, + fcinfo_in; + LLVMValueRef v_fcinfo_out, + v_fcinfo_in; + LLVMValueRef v_fn_addr_out, + v_fn_addr_in; + LLVMValueRef v_fcinfo_in_isnullp; + LLVMValueRef v_in_argp, + v_out_argp; + LLVMValueRef v_in_argnullp, + v_out_argnullp; + LLVMValueRef v_retval; + LLVMValueRef v_resvalue; + LLVMValueRef v_resnull; + + LLVMValueRef v_output_skip; + LLVMValueRef v_output; + + LLVMBasicBlockRef b_skipoutput; + LLVMBasicBlockRef b_calloutput; + LLVMBasicBlockRef b_input; + LLVMBasicBlockRef b_inputcall; + + fcinfo_out = op->d.iocoerce.fcinfo_data_out; + fcinfo_in = op->d.iocoerce.fcinfo_data_in; + + b_skipoutput = l_bb_before_v(opblocks[i + 1], + "op.%d.skipoutputnull", i); + b_calloutput = l_bb_before_v(opblocks[i + 1], + "op.%d.calloutput", i); + b_input = l_bb_before_v(opblocks[i + 1], + "op.%d.input", i); + b_inputcall = l_bb_before_v(opblocks[i + 1], + "op.%d.inputcall", i); + + v_fcinfo_out = l_ptr_const(fcinfo_out, l_ptr(StructFunctionCallInfoData)); + v_fcinfo_in = l_ptr_const(fcinfo_in, l_ptr(StructFunctionCallInfoData)); + v_fn_addr_out = l_ptr_const(fcinfo_out->flinfo->fn_addr, TypePGFunction); + v_fn_addr_in = l_ptr_const(fcinfo_in->flinfo->fn_addr, TypePGFunction); + + v_fcinfo_in_isnullp = + LLVMBuildStructGEP(b, v_fcinfo_in, + FIELDNO_FUNCTIONCALLINFODATA_ISNULL, + "v_fcinfo_in_isnull"); + v_out_argnullp = + LLVMBuildStructGEP(b, v_fcinfo_out, + FIELDNO_FUNCTIONCALLINFODATA_ARGNULL, + "v_fcinfo_out_argnullp"); + v_in_argnullp = + LLVMBuildStructGEP(b, v_fcinfo_in, + FIELDNO_FUNCTIONCALLINFODATA_ARGNULL, + "v_fcinfo_in_argnullp"); + v_out_argp = + LLVMBuildStructGEP(b, v_fcinfo_out, + FIELDNO_FUNCTIONCALLINFODATA_ARG, + "v_fcinfo_out_argp"); + v_in_argp = + LLVMBuildStructGEP(b, v_fcinfo_in, + FIELDNO_FUNCTIONCALLINFODATA_ARG, + "v_fcinfo_in_argp"); + + /* output functions are not called on nulls */ + v_resnull = LLVMBuildLoad(b, v_resnullp, ""); + LLVMBuildCondBr(b, + LLVMBuildICmp(b, LLVMIntEQ, v_resnull, + l_int8_const(1), ""), + b_skipoutput, + b_calloutput); + + LLVMPositionBuilderAtEnd(b, b_skipoutput); + v_output_skip = l_sizet_const(0); + LLVMBuildBr(b, b_input); + + LLVMPositionBuilderAtEnd(b, b_calloutput); + v_resvalue = LLVMBuildLoad(b, v_resvaluep, ""); + + /* set arg[0] */ + LLVMBuildStore(b, + v_resvalue, + LLVMBuildStructGEP(b, v_out_argp, 0, "")); + LLVMBuildStore(b, + l_int8_const(0), + LLVMBuildStructGEP(b, v_out_argnullp, + 0, "")); + /* and call output function (can never return NULL) */ + v_output = LLVMBuildCall(b, v_fn_addr_out, &v_fcinfo_out, + 1, "funccall_coerce_out"); + LLVMBuildBr(b, b_input); + + /* build block handling input function call */ + LLVMPositionBuilderAtEnd(b, b_input); + + /* phi between resnull and output function call branches */ + { + LLVMValueRef incoming_values[2]; + LLVMBasicBlockRef incoming_blocks[2]; + + incoming_values[0] = v_output_skip; + incoming_blocks[0] = b_skipoutput; + + incoming_values[1] = v_output; + incoming_blocks[1] = b_calloutput; + + v_output = LLVMBuildPhi(b, TypeSizeT, "output"); + LLVMAddIncoming(v_output, + incoming_values, incoming_blocks, + lengthof(incoming_blocks)); + } + + /* + * If input function is strict, skip if input string is + * NULL. + */ + if (op->d.iocoerce.finfo_in->fn_strict) + { + LLVMBuildCondBr(b, + LLVMBuildICmp(b, LLVMIntEQ, v_output, + l_sizet_const(0), ""), + opblocks[i + 1], + b_inputcall); + } + else + { + LLVMBuildBr(b, b_inputcall); + } + + LLVMPositionBuilderAtEnd(b, b_inputcall); + /* set arguments */ + /* arg0: output */ + LLVMBuildStore(b, v_output, + LLVMBuildStructGEP(b, v_in_argp, 0, "")); + LLVMBuildStore(b, v_resnull, + LLVMBuildStructGEP(b, v_in_argnullp, 0, "")); + + /* arg1: ioparam: preset in execExpr.c */ + /* arg2: typmod: preset in execExpr.c */ + + /* reset fcinfo_in->isnull */ + LLVMBuildStore(b, l_int8_const(0), v_fcinfo_in_isnullp); + /* and call function */ + v_retval = LLVMBuildCall(b, v_fn_addr_in, &v_fcinfo_in, 1, + "funccall_iocoerce_in"); + + LLVMBuildStore(b, v_retval, v_resvaluep); + + LLVMBuildBr(b, opblocks[i + 1]); + break; + } + + case EEOP_DISTINCT: + case EEOP_NOT_DISTINCT: + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + + LLVMValueRef v_fcinfo; + LLVMValueRef v_fcinfo_isnull; + + LLVMValueRef v_argnullp; + LLVMValueRef v_argnull0, + v_argisnull0; + LLVMValueRef v_argnull1, + v_argisnull1; + + LLVMValueRef v_anyargisnull; + LLVMValueRef v_bothargisnull; + + LLVMValueRef v_result; + + LLVMBasicBlockRef b_noargnull; + LLVMBasicBlockRef b_checkbothargnull; + LLVMBasicBlockRef b_bothargnull; + LLVMBasicBlockRef b_anyargnull; + + b_noargnull = l_bb_before_v(opblocks[i + 1], "op.%d.noargnull", i); + b_checkbothargnull = l_bb_before_v(opblocks[i + 1], "op.%d.checkbothargnull", i); + b_bothargnull = l_bb_before_v(opblocks[i + 1], "op.%d.bothargnull", i); + b_anyargnull = l_bb_before_v(opblocks[i + 1], "op.%d.anyargnull", i); + + v_fcinfo = l_ptr_const(fcinfo, l_ptr(StructFunctionCallInfoData)); + + v_argnullp = + LLVMBuildStructGEP(b, + v_fcinfo, + FIELDNO_FUNCTIONCALLINFODATA_ARGNULL, + "v_argnullp"); + + /* load argnull[0|1] for both arguments */ + v_argnull0 = l_load_struct_gep(b, v_argnullp, 0, ""); + v_argisnull0 = LLVMBuildICmp(b, LLVMIntEQ, v_argnull0, + l_int8_const(1), ""); + + v_argnull1 = l_load_struct_gep(b, v_argnullp, 1, ""); + v_argisnull1 = LLVMBuildICmp(b, LLVMIntEQ, v_argnull1, + l_int8_const(1), ""); + + v_anyargisnull = LLVMBuildOr(b, v_argisnull0, v_argisnull1, ""); + v_bothargisnull = LLVMBuildAnd(b, v_argisnull0, v_argisnull1, ""); + + /* + * Check function arguments for NULLness: If either is + * NULL, we check if both args are NULL. Otherwise call + * comparator. + */ + LLVMBuildCondBr(b, v_anyargisnull, b_checkbothargnull, + b_noargnull); + + /* + * build block checking if any arg is null + */ + LLVMPositionBuilderAtEnd(b, b_checkbothargnull); + LLVMBuildCondBr(b, v_bothargisnull, b_bothargnull, + b_anyargnull); + + + /* Both NULL? Then is not distinct... */ + LLVMPositionBuilderAtEnd(b, b_bothargnull); + LLVMBuildStore(b, l_int8_const(0), v_resnullp); + if (opcode == EEOP_NOT_DISTINCT) + LLVMBuildStore(b, l_sizet_const(1), v_resvaluep); + else + LLVMBuildStore(b, l_sizet_const(0), v_resvaluep); + + LLVMBuildBr(b, opblocks[i + 1]); + + /* Only one is NULL? Then is distinct... */ + LLVMPositionBuilderAtEnd(b, b_anyargnull); + LLVMBuildStore(b, l_int8_const(0), v_resnullp); + if (opcode == EEOP_NOT_DISTINCT) + LLVMBuildStore(b, l_sizet_const(0), v_resvaluep); + else + LLVMBuildStore(b, l_sizet_const(1), v_resvaluep); + LLVMBuildBr(b, opblocks[i + 1]); + + /* neither argument is null: compare */ + LLVMPositionBuilderAtEnd(b, b_noargnull); + + v_result = BuildV1Call(context, b, mod, fcinfo, + &v_fcinfo_isnull); + + if (opcode == EEOP_DISTINCT) + { + /* Must invert result of "=" */ + v_result = + LLVMBuildZExt(b, + LLVMBuildICmp(b, LLVMIntEQ, + v_result, + l_sizet_const(0), ""), + TypeSizeT, ""); + } + + LLVMBuildStore(b, v_fcinfo_isnull, v_resnullp); + LLVMBuildStore(b, v_result, v_resvaluep); + + LLVMBuildBr(b, opblocks[i + 1]); + break; + } + + case EEOP_NULLIF: + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + + LLVMValueRef v_fcinfo; + LLVMValueRef v_fcinfo_isnull; + LLVMValueRef v_argnullp; + LLVMValueRef v_argnull0; + LLVMValueRef v_argnull1; + LLVMValueRef v_anyargisnull; + LLVMValueRef v_argp; + LLVMValueRef v_arg0; + LLVMBasicBlockRef b_hasnull; + LLVMBasicBlockRef b_nonull; + LLVMBasicBlockRef b_argsequal; + LLVMValueRef v_retval; + LLVMValueRef v_argsequal; + + b_hasnull = l_bb_before_v(opblocks[i + 1], + "b.%d.null-args", i); + b_nonull = l_bb_before_v(opblocks[i + 1], + "b.%d.no-null-args", i); + b_argsequal = l_bb_before_v(opblocks[i + 1], + "b.%d.argsequal", i); + + v_fcinfo = l_ptr_const(fcinfo, l_ptr(StructFunctionCallInfoData)); + + v_argnullp = + LLVMBuildStructGEP(b, + v_fcinfo, + FIELDNO_FUNCTIONCALLINFODATA_ARGNULL, + "v_argnullp"); + + v_argp = + LLVMBuildStructGEP(b, + v_fcinfo, + FIELDNO_FUNCTIONCALLINFODATA_ARG, + "v_argp"); + + /* if either argument is NULL they can't be equal */ + v_argnull0 = l_load_struct_gep(b, v_argnullp, 0, ""); + v_argnull1 = l_load_struct_gep(b, v_argnullp, 1, ""); + + v_anyargisnull = + LLVMBuildOr(b, + LLVMBuildICmp(b, LLVMIntEQ, v_argnull0, + l_int8_const(1), ""), + LLVMBuildICmp(b, LLVMIntEQ, v_argnull1, + l_int8_const(1), ""), + ""); + + LLVMBuildCondBr(b, v_anyargisnull, b_hasnull, b_nonull); + + /* one (or both) of the arguments are null, return arg[0] */ + LLVMPositionBuilderAtEnd(b, b_hasnull); + v_arg0 = l_load_struct_gep(b, v_argp, 0, ""); + LLVMBuildStore(b, v_argnull0, v_resnullp); + LLVMBuildStore(b, v_arg0, v_resvaluep); + LLVMBuildBr(b, opblocks[i + 1]); + + /* build block to invoke function and check result */ + LLVMPositionBuilderAtEnd(b, b_nonull); + + v_retval = BuildV1Call(context, b, mod, fcinfo, &v_fcinfo_isnull); + + /* + * If result not null, and arguments are equal return null + * (same result as if there'd been NULLs, hence reuse + * b_hasnull). + */ + v_argsequal = LLVMBuildAnd(b, + LLVMBuildICmp(b, LLVMIntEQ, + v_fcinfo_isnull, + l_int8_const(0), + ""), + LLVMBuildICmp(b, LLVMIntEQ, + v_retval, + l_sizet_const(1), + ""), + ""); + LLVMBuildCondBr(b, v_argsequal, b_argsequal, b_hasnull); + + /* build block setting result to NULL, if args are equal */ + LLVMPositionBuilderAtEnd(b, b_argsequal); + LLVMBuildStore(b, l_int8_const(1), v_resnullp); + LLVMBuildStore(b, l_sizet_const(0), v_resvaluep); + LLVMBuildStore(b, v_retval, v_resvaluep); + + LLVMBuildBr(b, opblocks[i + 1]); + break; + } + + case EEOP_SQLVALUEFUNCTION: + build_EvalXFunc(b, mod, "ExecEvalSQLValueFunction", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_CURRENTOFEXPR: + build_EvalXFunc(b, mod, "ExecEvalCurrentOfExpr", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_NEXTVALUEEXPR: + build_EvalXFunc(b, mod, "ExecEvalNextValueExpr", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_ARRAYEXPR: + build_EvalXFunc(b, mod, "ExecEvalArrayExpr", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_ARRAYCOERCE: + build_EvalXFunc(b, mod, "ExecEvalArrayCoerce", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_ROW: + build_EvalXFunc(b, mod, "ExecEvalRow", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_ROWCOMPARE_STEP: + { + FunctionCallInfo fcinfo = op->d.rowcompare_step.fcinfo_data; + LLVMValueRef v_fcinfo_isnull; + LLVMBasicBlockRef b_null; + LLVMBasicBlockRef b_compare; + LLVMBasicBlockRef b_compare_result; + + LLVMValueRef v_retval; + + b_null = l_bb_before_v(opblocks[i + 1], + "op.%d.row-null", i); + b_compare = l_bb_before_v(opblocks[i + 1], + "op.%d.row-compare", i); + b_compare_result = + l_bb_before_v(opblocks[i + 1], + "op.%d.row-compare-result", + i); + + /* + * If function is strict, and either arg is null, we're + * done. + */ + if (op->d.rowcompare_step.finfo->fn_strict) + { + LLVMValueRef v_fcinfo; + LLVMValueRef v_argnullp; + LLVMValueRef v_argnull0; + LLVMValueRef v_argnull1; + LLVMValueRef v_anyargisnull; + + v_fcinfo = l_ptr_const(fcinfo, + l_ptr(StructFunctionCallInfoData)); + + v_argnullp = + LLVMBuildStructGEP(b, v_fcinfo, + FIELDNO_FUNCTIONCALLINFODATA_ARGNULL, + "v_argnullp"); + + v_argnull0 = l_load_struct_gep(b, v_argnullp, 0, ""); + v_argnull1 = l_load_struct_gep(b, v_argnullp, 1, ""); + + v_anyargisnull = + LLVMBuildOr(b, + LLVMBuildICmp(b, + LLVMIntEQ, + v_argnull0, + l_int8_const(1), + ""), + LLVMBuildICmp(b, LLVMIntEQ, + v_argnull1, + l_int8_const(1), ""), + ""); + + LLVMBuildCondBr(b, v_anyargisnull, b_null, b_compare); + } + else + { + LLVMBuildBr(b, b_compare); + } + + /* build block invoking comparison function */ + LLVMPositionBuilderAtEnd(b, b_compare); + + /* call function */ + v_retval = BuildV1Call(context, b, mod, fcinfo, + &v_fcinfo_isnull); + LLVMBuildStore(b, v_retval, v_resvaluep); + + /* if result of function is NULL, force NULL result */ + LLVMBuildCondBr(b, + LLVMBuildICmp(b, + LLVMIntEQ, + v_fcinfo_isnull, + l_int8_const(0), + ""), + b_compare_result, + b_null); + + /* build block analying the !NULL comparator result */ + LLVMPositionBuilderAtEnd(b, b_compare_result); + + /* if results equal, compare next, otherwise done */ + LLVMBuildCondBr(b, + LLVMBuildICmp(b, + LLVMIntEQ, + v_retval, + l_sizet_const(0), ""), + opblocks[i + 1], + opblocks[op->d.rowcompare_step.jumpdone]); + + /* + * Build block handling NULL input or NULL comparator + * result. + */ + LLVMPositionBuilderAtEnd(b, b_null); + LLVMBuildStore(b, l_int8_const(1), v_resnullp); + LLVMBuildBr(b, opblocks[op->d.rowcompare_step.jumpnull]); + + break; + } + + case EEOP_ROWCOMPARE_FINAL: + { + RowCompareType rctype = op->d.rowcompare_final.rctype; + + LLVMValueRef v_cmpresult; + LLVMValueRef v_result; + LLVMIntPredicate predicate; + + /* + * Btree comparators return 32 bit results, need to be + * careful about sign (used as a 64 bit value it's + * otherwise wrong). + */ + v_cmpresult = + LLVMBuildTrunc(b, + LLVMBuildLoad(b, v_resvaluep, ""), + LLVMInt32Type(), ""); + + switch (rctype) + { + case ROWCOMPARE_LT: + predicate = LLVMIntSLT; + break; + case ROWCOMPARE_LE: + predicate = LLVMIntSLE; + break; + case ROWCOMPARE_GT: + predicate = LLVMIntSGT; + break; + case ROWCOMPARE_GE: + predicate = LLVMIntSGE; + break; + default: + /* EQ and NE cases aren't allowed here */ + Assert(false); + predicate = 0; /* prevent compiler warning */ + break; + } + + v_result = LLVMBuildICmp(b, + predicate, + v_cmpresult, + l_int32_const(0), + ""); + v_result = LLVMBuildZExt(b, v_result, TypeSizeT, ""); + + LLVMBuildStore(b, l_int8_const(0), v_resnullp); + LLVMBuildStore(b, v_result, v_resvaluep); + + LLVMBuildBr(b, opblocks[i + 1]); + break; + } + + case EEOP_MINMAX: + build_EvalXFunc(b, mod, "ExecEvalMinMax", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_FIELDSELECT: + build_EvalXFunc(b, mod, "ExecEvalFieldSelect", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_FIELDSTORE_DEFORM: + build_EvalXFunc(b, mod, "ExecEvalFieldStoreDeForm", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_FIELDSTORE_FORM: + build_EvalXFunc(b, mod, "ExecEvalFieldStoreForm", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_ARRAYREF_SUBSCRIPT: + { + LLVMValueRef v_fn; + int jumpdone = op->d.arrayref_subscript.jumpdone; + LLVMValueRef v_params[2]; + LLVMValueRef v_ret; + + v_fn = llvm_get_decl(mod, FuncExecEvalArrayRefSubscript); + + v_params[0] = v_state; + v_params[1] = l_ptr_const(op, l_ptr(StructExprEvalStep)); + v_ret = LLVMBuildCall(b, v_fn, + v_params, lengthof(v_params), ""); + + LLVMBuildCondBr(b, + LLVMBuildICmp(b, LLVMIntEQ, v_ret, + l_int8_const(1), ""), + opblocks[i + 1], + opblocks[jumpdone]); + break; + } + + case EEOP_DOMAIN_TESTVAL: + { + LLVMBasicBlockRef b_avail, + b_notavail; + LLVMValueRef v_casevaluep, + v_casevalue; + LLVMValueRef v_casenullp, + v_casenull; + LLVMValueRef v_casevaluenull; + + b_avail = l_bb_before_v(opblocks[i + 1], + "op.%d.avail", i); + b_notavail = l_bb_before_v(opblocks[i + 1], + "op.%d.notavail", i); + + v_casevaluep = l_ptr_const(op->d.casetest.value, + l_ptr(TypeSizeT)); + v_casenullp = l_ptr_const(op->d.casetest.isnull, + l_ptr(LLVMInt8Type())); + + v_casevaluenull = + LLVMBuildICmp(b, LLVMIntEQ, + LLVMBuildPtrToInt(b, v_casevaluep, + TypeSizeT, ""), + l_sizet_const(0), ""); + LLVMBuildCondBr(b, + v_casevaluenull, + b_notavail, b_avail); + + /* if casetest != NULL */ + LLVMPositionBuilderAtEnd(b, b_avail); + v_casevalue = LLVMBuildLoad(b, v_casevaluep, ""); + v_casenull = LLVMBuildLoad(b, v_casenullp, ""); + LLVMBuildStore(b, v_casevalue, v_resvaluep); + LLVMBuildStore(b, v_casenull, v_resnullp); + LLVMBuildBr(b, opblocks[i + 1]); + + /* if casetest == NULL */ + LLVMPositionBuilderAtEnd(b, b_notavail); + v_casevalue = + l_load_struct_gep(b, v_econtext, + FIELDNO_EXPRCONTEXT_DOMAINDATUM, + ""); + v_casenull = + l_load_struct_gep(b, v_econtext, + FIELDNO_EXPRCONTEXT_DOMAINNULL, + ""); + LLVMBuildStore(b, v_casevalue, v_resvaluep); + LLVMBuildStore(b, v_casenull, v_resnullp); + + LLVMBuildBr(b, opblocks[i + 1]); + break; + } + + case EEOP_DOMAIN_NOTNULL: + build_EvalXFunc(b, mod, "ExecEvalConstraintNotNull", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_DOMAIN_CHECK: + build_EvalXFunc(b, mod, "ExecEvalConstraintCheck", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_CONVERT_ROWTYPE: + build_EvalXFunc(b, mod, "ExecEvalConvertRowtype", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_SCALARARRAYOP: + build_EvalXFunc(b, mod, "ExecEvalScalarArrayOp", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_XMLEXPR: + build_EvalXFunc(b, mod, "ExecEvalXmlExpr", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_AGGREF: + { + AggrefExprState *aggref = op->d.aggref.astate; + LLVMValueRef v_aggnop; + LLVMValueRef v_aggno; + LLVMValueRef value, + isnull; + + /* + * At this point aggref->aggno is not yet set (it's setup + * in ExecInitAgg() after initializing the expression). So + * load it from memory each time round. + */ + v_aggnop = l_ptr_const(&aggref->aggno, + l_ptr(LLVMInt32Type())); + v_aggno = LLVMBuildLoad(b, v_aggnop, "v_aggno"); + + /* load agg value / null */ + value = l_load_gep1(b, v_aggvalues, v_aggno, "aggvalue"); + isnull = l_load_gep1(b, v_aggnulls, v_aggno, "aggnull"); + + /* and store result */ + LLVMBuildStore(b, value, v_resvaluep); + LLVMBuildStore(b, isnull, v_resnullp); + + LLVMBuildBr(b, opblocks[i + 1]); + break; + } + + case EEOP_GROUPING_FUNC: + build_EvalXFunc(b, mod, "ExecEvalGroupingFunc", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_WINDOW_FUNC: + { + WindowFuncExprState *wfunc = op->d.window_func.wfstate; + LLVMValueRef v_wfuncnop; + LLVMValueRef v_wfuncno; + LLVMValueRef value, + isnull; + + /* + * At this point aggref->wfuncno is not yet set (it's + * setup in ExecInitWindowAgg() after initializing the + * expression). So load it from memory each time round. + */ + v_wfuncnop = l_ptr_const(&wfunc->wfuncno, + l_ptr(LLVMInt32Type())); + v_wfuncno = LLVMBuildLoad(b, v_wfuncnop, "v_wfuncno"); + + /* load window func value / null */ + value = l_load_gep1(b, v_aggvalues, v_wfuncno, + "windowvalue"); + isnull = l_load_gep1(b, v_aggnulls, v_wfuncno, + "windownull"); + + LLVMBuildStore(b, value, v_resvaluep); + LLVMBuildStore(b, isnull, v_resnullp); + + LLVMBuildBr(b, opblocks[i + 1]); + break; + } + + case EEOP_SUBPLAN: + build_EvalXFunc(b, mod, "ExecEvalSubPlan", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_ALTERNATIVE_SUBPLAN: + build_EvalXFunc(b, mod, "ExecEvalAlternativeSubPlan", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_AGG_STRICT_DESERIALIZE: + { + FunctionCallInfo fcinfo = op->d.agg_deserialize.fcinfo_data; + LLVMValueRef v_fcinfo; + LLVMValueRef v_argnullp; + LLVMValueRef v_argnull0; + LLVMBasicBlockRef b_deserialize; + + b_deserialize = l_bb_before_v(opblocks[i + 1], + "op.%d.deserialize", i); + + v_fcinfo = l_ptr_const(fcinfo, + l_ptr(StructFunctionCallInfoData)); + + v_argnullp = + LLVMBuildStructGEP(b, + v_fcinfo, + FIELDNO_FUNCTIONCALLINFODATA_ARGNULL, + "v_argnullp"); + v_argnull0 = + l_load_struct_gep(b, v_argnullp, 0, "v_argnull0"); + + LLVMBuildCondBr(b, + LLVMBuildICmp(b, + LLVMIntEQ, + v_argnull0, + l_int8_const(1), + ""), + opblocks[op->d.agg_deserialize.jumpnull], + b_deserialize); + LLVMPositionBuilderAtEnd(b, b_deserialize); + + /* fallthrough */ + } + + case EEOP_AGG_DESERIALIZE: + { + AggState *aggstate; + FunctionCallInfo fcinfo; + + LLVMValueRef v_retval; + LLVMValueRef v_fcinfo_isnull; + LLVMValueRef v_tmpcontext; + LLVMValueRef v_oldcontext; + + aggstate = op->d.agg_deserialize.aggstate; + fcinfo = op->d.agg_deserialize.fcinfo_data; + + v_tmpcontext = + l_ptr_const(aggstate->tmpcontext->ecxt_per_tuple_memory, + l_ptr(StructMemoryContextData)); + v_oldcontext = l_mcxt_switch(mod, b, v_tmpcontext); + v_retval = BuildV1Call(context, b, mod, fcinfo, + &v_fcinfo_isnull); + l_mcxt_switch(mod, b, v_oldcontext); + + LLVMBuildStore(b, v_retval, v_resvaluep); + LLVMBuildStore(b, v_fcinfo_isnull, v_resnullp); + + LLVMBuildBr(b, opblocks[i + 1]); + break; + } + + case EEOP_AGG_STRICT_INPUT_CHECK: + { + int nargs = op->d.agg_strict_input_check.nargs; + bool *nulls = op->d.agg_strict_input_check.nulls; + int jumpnull; + int argno; + + LLVMValueRef v_nullp; + LLVMBasicBlockRef *b_checknulls; + + jumpnull = op->d.agg_strict_input_check.jumpnull; + v_nullp = l_ptr_const(nulls, l_ptr(LLVMInt8Type())); + + /* create blocks for checking args */ + b_checknulls = palloc(sizeof(LLVMBasicBlockRef *) * nargs); + for (argno = 0; argno < nargs; argno++) + { + b_checknulls[argno] = + l_bb_before_v(opblocks[i + 1], + "op.%d.check-null.%d", + i, argno); + } + + LLVMBuildBr(b, b_checknulls[0]); + + /* strict function, check for NULL args */ + for (argno = 0; argno < nargs; argno++) + { + LLVMValueRef v_argno = l_int32_const(argno); + LLVMValueRef v_argisnull; + LLVMBasicBlockRef b_argnotnull; + + LLVMPositionBuilderAtEnd(b, b_checknulls[argno]); + + if (argno + 1 == nargs) + b_argnotnull = opblocks[i + 1]; + else + b_argnotnull = b_checknulls[argno + 1]; + + v_argisnull = l_load_gep1(b, v_nullp, v_argno, ""); + + LLVMBuildCondBr(b, + LLVMBuildICmp(b, + LLVMIntEQ, + v_argisnull, + l_int8_const(1), ""), + opblocks[jumpnull], + b_argnotnull); + } + + break; + } + + case EEOP_AGG_INIT_TRANS: + { + AggState *aggstate; + AggStatePerTrans pertrans; + + LLVMValueRef v_aggstatep; + LLVMValueRef v_pertransp; + + LLVMValueRef v_allpergroupsp; + + LLVMValueRef v_pergroupp; + + LLVMValueRef v_setoff, + v_transno; + + LLVMValueRef v_notransvalue; + + LLVMBasicBlockRef b_init; + + aggstate = op->d.agg_init_trans.aggstate; + pertrans = op->d.agg_init_trans.pertrans; + + v_aggstatep = l_ptr_const(aggstate, + l_ptr(StructAggState)); + v_pertransp = l_ptr_const(pertrans, + l_ptr(StructAggStatePerTransData)); + + /* + * pergroup = &aggstate->all_pergroups + * [op->d.agg_init_trans_check.setoff] + * [op->d.agg_init_trans_check.transno]; + */ + v_allpergroupsp = + l_load_struct_gep(b, v_aggstatep, + FIELDNO_AGGSTATE_ALL_PERGROUPS, + "aggstate.all_pergroups"); + v_setoff = l_int32_const(op->d.agg_init_trans.setoff); + v_transno = l_int32_const(op->d.agg_init_trans.transno); + v_pergroupp = + LLVMBuildGEP(b, + l_load_gep1(b, v_allpergroupsp, v_setoff, ""), + &v_transno, 1, ""); + + v_notransvalue = + l_load_struct_gep(b, v_pergroupp, + FIELDNO_AGGSTATEPERGROUPDATA_NOTRANSVALUE, + "notransvalue"); + + b_init = l_bb_before_v(opblocks[i + 1], + "op.%d.inittrans", i); + + LLVMBuildCondBr(b, + LLVMBuildICmp(b, LLVMIntEQ, v_notransvalue, + l_int8_const(1), ""), + b_init, + opblocks[i + 1]); + + LLVMPositionBuilderAtEnd(b, b_init); + + { + LLVMValueRef params[3]; + + params[0] = v_aggstatep; + params[1] = v_pertransp; + params[2] = v_pergroupp; + + LLVMBuildCall(b, + llvm_get_decl(mod, FuncExecAggInitGroup), + params, lengthof(params), + ""); + } + LLVMBuildBr(b, opblocks[op->d.agg_init_trans.jumpnull]); + + break; + } + + case EEOP_AGG_STRICT_TRANS_CHECK: + { + AggState *aggstate; + LLVMValueRef v_setoff, + v_transno; + + LLVMValueRef v_aggstatep; + LLVMValueRef v_allpergroupsp; + + LLVMValueRef v_transnull; + LLVMValueRef v_pergroupp; + + int jumpnull = op->d.agg_strict_trans_check.jumpnull; + + aggstate = op->d.agg_strict_trans_check.aggstate; + v_aggstatep = l_ptr_const(aggstate, l_ptr(StructAggState)); + + /* + * pergroup = &aggstate->all_pergroups + * [op->d.agg_strict_trans_check.setoff] + * [op->d.agg_init_trans_check.transno]; + */ + v_allpergroupsp = + l_load_struct_gep(b, v_aggstatep, + FIELDNO_AGGSTATE_ALL_PERGROUPS, + "aggstate.all_pergroups"); + v_setoff = + l_int32_const(op->d.agg_strict_trans_check.setoff); + v_transno = + l_int32_const(op->d.agg_strict_trans_check.transno); + v_pergroupp = + LLVMBuildGEP(b, + l_load_gep1(b, v_allpergroupsp, v_setoff, ""), + &v_transno, 1, ""); + + v_transnull = + l_load_struct_gep(b, v_pergroupp, + FIELDNO_AGGSTATEPERGROUPDATA_TRANSVALUEISNULL, + "transnull"); + + LLVMBuildCondBr(b, + LLVMBuildICmp(b, LLVMIntEQ, v_transnull, + l_int8_const(1), ""), + opblocks[jumpnull], + opblocks[i + 1]); + + break; + } + + case EEOP_AGG_PLAIN_TRANS_BYVAL: + case EEOP_AGG_PLAIN_TRANS: + { + AggState *aggstate; + AggStatePerTrans pertrans; + FunctionCallInfo fcinfo; + + LLVMValueRef v_aggstatep; + LLVMValueRef v_fcinfo; + LLVMValueRef v_fcinfo_isnull; + LLVMValueRef v_argp, + v_argnullp; + + LLVMValueRef v_transvaluep; + LLVMValueRef v_transnullp; + + LLVMValueRef v_setoff; + LLVMValueRef v_transno; + + LLVMValueRef v_aggcontext; + + LLVMValueRef v_allpergroupsp; + LLVMValueRef v_current_setp; + LLVMValueRef v_current_pertransp; + LLVMValueRef v_curaggcontext; + + LLVMValueRef v_pertransp; + + LLVMValueRef v_pergroupp; + + LLVMValueRef v_retval; + + LLVMValueRef v_tmpcontext; + LLVMValueRef v_oldcontext; + + aggstate = op->d.agg_trans.aggstate; + pertrans = op->d.agg_trans.pertrans; + + fcinfo = &pertrans->transfn_fcinfo; + + v_aggstatep = l_ptr_const(aggstate, + l_ptr(StructAggState)); + v_pertransp = l_ptr_const(pertrans, + l_ptr(StructAggStatePerTransData)); + + /* + * pergroup = &aggstate->all_pergroups + * [op->d.agg_strict_trans_check.setoff] + * [op->d.agg_init_trans_check.transno]; + */ + v_allpergroupsp = + l_load_struct_gep(b, v_aggstatep, + FIELDNO_AGGSTATE_ALL_PERGROUPS, + "aggstate.all_pergroups"); + v_setoff = l_int32_const(op->d.agg_trans.setoff); + v_transno = l_int32_const(op->d.agg_trans.transno); + v_pergroupp = + LLVMBuildGEP(b, + l_load_gep1(b, v_allpergroupsp, v_setoff, ""), + &v_transno, 1, ""); + + v_fcinfo = l_ptr_const(fcinfo, + l_ptr(StructFunctionCallInfoData)); + + v_argnullp = + LLVMBuildStructGEP(b, + v_fcinfo, + FIELDNO_FUNCTIONCALLINFODATA_ARGNULL, + "v_argnullp"); + v_argp = + LLVMBuildStructGEP(b, + v_fcinfo, + FIELDNO_FUNCTIONCALLINFODATA_ARG, + "v_argp"); + + v_aggcontext = l_ptr_const(op->d.agg_trans.aggcontext, + l_ptr(StructExprContext)); + + v_current_setp = + LLVMBuildStructGEP(b, + v_aggstatep, + FIELDNO_AGGSTATE_CURRENT_SET, + "aggstate.current_set"); + v_curaggcontext = + LLVMBuildStructGEP(b, + v_aggstatep, + FIELDNO_AGGSTATE_CURAGGCONTEXT, + "aggstate.curaggcontext"); + v_current_pertransp = + LLVMBuildStructGEP(b, + v_aggstatep, + FIELDNO_AGGSTATE_CURPERTRANS, + "aggstate.curpertrans"); + + /* set aggstate globals */ + LLVMBuildStore(b, v_aggcontext, v_curaggcontext); + LLVMBuildStore(b, l_int32_const(op->d.agg_trans.setno), + v_current_setp); + LLVMBuildStore(b, v_pertransp, v_current_pertransp); + + /* invoke transition function in per-tuple context */ + v_tmpcontext = + l_ptr_const(aggstate->tmpcontext->ecxt_per_tuple_memory, + l_ptr(StructMemoryContextData)); + v_oldcontext = l_mcxt_switch(mod, b, v_tmpcontext); + + /* store transvalue in fcinfo->arg/argnull[0] */ + v_transvaluep = + LLVMBuildStructGEP(b, v_pergroupp, + FIELDNO_AGGSTATEPERGROUPDATA_TRANSVALUE, + "transvalue"); + v_transnullp = + LLVMBuildStructGEP(b, v_pergroupp, + FIELDNO_AGGSTATEPERGROUPDATA_TRANSVALUEISNULL, + "transnullp"); + LLVMBuildStore(b, + LLVMBuildLoad(b, v_transvaluep, + "transvalue"), + LLVMBuildStructGEP(b, v_argp, 0, "")); + LLVMBuildStore(b, + LLVMBuildLoad(b, v_transnullp, "transnull"), + LLVMBuildStructGEP(b, v_argnullp, 0, "")); + + /* and invoke transition function */ + v_retval = BuildV1Call(context, b, mod, fcinfo, + &v_fcinfo_isnull); + + /* + * For pass-by-ref datatype, must copy the new value into + * aggcontext and free the prior transValue. But if + * transfn returned a pointer to its first input, we don't + * need to do anything. Also, if transfn returned a + * pointer to a R/W expanded object that is already a + * child of the aggcontext, assume we can adopt that value + * without copying it. + */ + if (opcode == EEOP_AGG_PLAIN_TRANS) + { + LLVMBasicBlockRef b_call; + LLVMBasicBlockRef b_nocall; + LLVMValueRef v_fn; + LLVMValueRef v_transvalue; + LLVMValueRef v_transnull; + LLVMValueRef v_newval; + LLVMValueRef params[6]; + + b_call = l_bb_before_v(opblocks[i + 1], + "op.%d.transcall", i); + b_nocall = l_bb_before_v(opblocks[i + 1], + "op.%d.transnocall", i); + + v_transvalue = LLVMBuildLoad(b, v_transvaluep, ""); + v_transnull = LLVMBuildLoad(b, v_transnullp, ""); + + /* + * DatumGetPointer(newVal) != + * DatumGetPointer(pergroup->transValue)) + */ + LLVMBuildCondBr(b, + LLVMBuildICmp(b, LLVMIntEQ, + v_transvalue, + v_retval, ""), + b_nocall, b_call); + + /* returned datum not passed datum, reparent */ + LLVMPositionBuilderAtEnd(b, b_call); + + params[0] = v_aggstatep; + params[1] = v_pertransp; + params[2] = v_retval; + params[3] = v_fcinfo_isnull; + params[4] = v_transvalue; + params[5] = v_transnull; + + v_fn = llvm_get_decl(mod, FuncExecAggTransReparent); + v_newval = + LLVMBuildCall(b, v_fn, + params, lengthof(params), + ""); + + /* store trans value */ + LLVMBuildStore(b, v_newval, v_transvaluep); + LLVMBuildStore(b, v_fcinfo_isnull, v_transnullp); + LLVMBuildBr(b, opblocks[i + 1]); + + /* returned datum passed datum, no need to reparent */ + LLVMPositionBuilderAtEnd(b, b_nocall); + } + + /* store trans value */ + LLVMBuildStore(b, v_retval, v_transvaluep); + LLVMBuildStore(b, v_fcinfo_isnull, v_transnullp); + + l_mcxt_switch(mod, b, v_oldcontext); + + LLVMBuildBr(b, opblocks[i + 1]); + break; + } + + case EEOP_AGG_ORDERED_TRANS_DATUM: + build_EvalXFunc(b, mod, "ExecEvalAggOrderedTransDatum", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_AGG_ORDERED_TRANS_TUPLE: + build_EvalXFunc(b, mod, "ExecEvalAggOrderedTransTuple", + v_state, v_econtext, op); + LLVMBuildBr(b, opblocks[i + 1]); + break; + + case EEOP_LAST: + Assert(false); + break; + } + } + + LLVMDisposeBuilder(b); + + /* + * Don't immediately emit function, instead do so the first time the + * expression is actually evaluated. That allows to emit a lot of + * functions together, avoiding a lot of repeated llvm and memory + * remapping overhead. + */ + { + + CompiledExprState *cstate = palloc0(sizeof(CompiledExprState)); + + cstate->context = context; + cstate->funcname = funcname; + + state->evalfunc = ExecRunCompiledExpr; + state->evalfunc_private = cstate; + } + + llvm_leave_fatal_on_oom(); + + INSTR_TIME_SET_CURRENT(endtime); + INSTR_TIME_ACCUM_DIFF(context->base.generation_counter, + endtime, starttime); + + return true; +} + +/* + * Run compiled expression. + * + * This will only be called the first time a JITed expression is called. We + * first make sure the expression is still up2date, and then get a pointer to + * the emitted function. The latter can be the first thing that triggers + * optimizing and emitting all the generated functions. + */ +static Datum +ExecRunCompiledExpr(ExprState *state, ExprContext *econtext, bool *isNull) +{ + CompiledExprState *cstate = state->evalfunc_private; + ExprStateEvalFunc func; + + CheckExprStillValid(state, econtext); + + llvm_enter_fatal_on_oom(); + func = (ExprStateEvalFunc) llvm_get_function(cstate->context, + cstate->funcname); + llvm_leave_fatal_on_oom(); + Assert(func); + + /* remove indirection via this function for future calls */ + state->evalfunc = func; + + return func(state, econtext, isNull); +} + +static LLVMValueRef +BuildV1Call(LLVMJitContext *context, LLVMBuilderRef b, + LLVMModuleRef mod, FunctionCallInfo fcinfo, + LLVMValueRef *v_fcinfo_isnull) +{ + LLVMValueRef v_fn; + LLVMValueRef v_fcinfo_isnullp; + LLVMValueRef v_retval; + LLVMValueRef v_fcinfo; + + v_fn = llvm_function_reference(context, b, mod, fcinfo); + + v_fcinfo = l_ptr_const(fcinfo, l_ptr(StructFunctionCallInfoData)); + v_fcinfo_isnullp = LLVMBuildStructGEP(b, v_fcinfo, + FIELDNO_FUNCTIONCALLINFODATA_ISNULL, + "v_fcinfo_isnull"); + LLVMBuildStore(b, l_int8_const(0), + v_fcinfo_isnullp); + + v_retval = LLVMBuildCall(b, v_fn, &v_fcinfo, 1, "funccall"); + + if (v_fcinfo_isnull) + *v_fcinfo_isnull = LLVMBuildLoad(b, v_fcinfo_isnullp, ""); + + /* + * Add lifetime-end annotation, signalling that writes to memory don't + * have to be retained (important for inlining potential). + */ + { + 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())); + LLVMBuildCall(b, v_lifetime, params, lengthof(params), ""); + + params[0] = l_int64_const(sizeof(fcinfo->isnull)); + params[1] = l_ptr_const(&fcinfo->isnull, l_ptr(LLVMInt8Type())); + LLVMBuildCall(b, v_lifetime, params, lengthof(params), ""); + } + + return v_retval; +} + +/* + * Implement an expression step by calling the function funcname. + */ +static void +build_EvalXFunc(LLVMBuilderRef b, LLVMModuleRef mod, const char *funcname, + LLVMValueRef v_state, LLVMValueRef v_econtext, + ExprEvalStep *op) +{ + LLVMTypeRef sig; + LLVMValueRef v_fn; + LLVMTypeRef param_types[3]; + LLVMValueRef params[3]; + + v_fn = LLVMGetNamedFunction(mod, funcname); + if (!v_fn) + { + param_types[0] = l_ptr(StructExprState); + param_types[1] = l_ptr(StructExprEvalStep); + param_types[2] = l_ptr(StructExprContext); + + sig = LLVMFunctionType(LLVMVoidType(), + param_types, lengthof(param_types), + false); + v_fn = LLVMAddFunction(mod, funcname, sig); + } + + params[0] = v_state; + params[1] = l_ptr_const(op, l_ptr(StructExprEvalStep)); + params[2] = v_econtext; + + LLVMBuildCall(b, + v_fn, + params, lengthof(params), ""); +} + +static LLVMValueRef +create_LifetimeEnd(LLVMModuleRef mod) +{ + LLVMTypeRef sig; + LLVMValueRef fn; + LLVMTypeRef param_types[2]; + + /* LLVM 5+ has a variadic pointer argument */ +#if LLVM_VERSION_MAJOR < 5 + const char *nm = "llvm.lifetime.end"; +#else + const char *nm = "llvm.lifetime.end.p0i8"; +#endif + + fn = LLVMGetNamedFunction(mod, nm); + if (fn) + return fn; + + param_types[0] = LLVMInt64Type(); + param_types[1] = l_ptr(LLVMInt8Type()); + + sig = LLVMFunctionType(LLVMVoidType(), + param_types, lengthof(param_types), + false); + fn = LLVMAddFunction(mod, nm, sig); + + LLVMSetFunctionCallConv(fn, LLVMCCallConv); + + Assert(LLVMGetIntrinsicID(fn)); + + return fn; +} diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 3668db4e09..50f858e420 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -544,6 +544,12 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) if (jit_optimize_above_cost >= 0 && top_plan->total_cost > jit_optimize_above_cost) result->jitFlags |= PGJIT_OPT3; + + /* + * Decide which operations should be JITed. + */ + if (jit_expressions) + result->jitFlags |= PGJIT_EXPR; } return result; diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index 25f4c34901..ae511e9a23 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -59,7 +59,8 @@ static void fmgr_info_other_lang(Oid functionId, FmgrInfo *finfo, HeapTuple proc static CFuncHashTabEntry *lookup_C_func(HeapTuple procedureTuple); static void record_C_func(HeapTuple procedureTuple, PGFunction user_fn, const Pg_finfo_record *inforec); -static Datum fmgr_security_definer(PG_FUNCTION_ARGS); +/* extern so it's callable via JIT */ +extern Datum fmgr_security_definer(PG_FUNCTION_ARGS); /* @@ -260,6 +261,95 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, ReleaseSysCache(procedureTuple); } +/* + * Return module and C function name providing implementation of functionId. + * + * If *mod == NULL and *fn == NULL, no C symbol is known to implement + * function. + * + * If *mod == NULL and *fn != NULL, the function is implemented by a symbol in + * the main binary. + * + * If *mod != NULL and *fn !=NULL the function is implemented in an extension + * shared object. + * + * The returned module and function names are pstrdup'ed into the current + * memory context. + */ +void +fmgr_symbol(Oid functionId, char **mod, char **fn) +{ + HeapTuple procedureTuple; + Form_pg_proc procedureStruct; + bool isnull; + Datum prosrcattr; + Datum probinattr; + + /* Otherwise we need the pg_proc entry */ + procedureTuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionId)); + if (!HeapTupleIsValid(procedureTuple)) + elog(ERROR, "cache lookup failed for function %u", functionId); + procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple); + + /* + */ + if (procedureStruct->prosecdef || + !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig) || + FmgrHookIsNeeded(functionId)) + { + *mod = NULL; /* core binary */ + *fn = pstrdup("fmgr_security_definer"); + ReleaseSysCache(procedureTuple); + return; + } + + /* see fmgr_info_cxt_security for the individual cases */ + switch (procedureStruct->prolang) + { + case INTERNALlanguageId: + prosrcattr = SysCacheGetAttr(PROCOID, procedureTuple, + Anum_pg_proc_prosrc, &isnull); + if (isnull) + elog(ERROR, "null prosrc"); + + *mod = NULL; /* core binary */ + *fn = TextDatumGetCString(prosrcattr); + break; + + case ClanguageId: + prosrcattr = SysCacheGetAttr(PROCOID, procedureTuple, + Anum_pg_proc_prosrc, &isnull); + if (isnull) + elog(ERROR, "null prosrc for C function %u", functionId); + + probinattr = SysCacheGetAttr(PROCOID, procedureTuple, + Anum_pg_proc_probin, &isnull); + if (isnull) + elog(ERROR, "null probin for C function %u", functionId); + + /* + * No need to check symbol presence / API version here, already + * checked in fmgr_info_cxt_security. + */ + *mod = TextDatumGetCString(probinattr); + *fn = TextDatumGetCString(prosrcattr); + break; + + case SQLlanguageId: + *mod = NULL; /* core binary */ + *fn = pstrdup("fmgr_sql"); + break; + + default: + *mod = NULL; + *fn = NULL; /* unknown, pass pointer */ + break; + } + + ReleaseSysCache(procedureTuple); +} + + /* * Special fmgr_info processing for C-language functions. Note that * finfo->fn_oid is not valid yet. @@ -565,7 +655,7 @@ struct fmgr_security_definer_cache * the actual arguments, etc.) intact. This is not re-entrant, but then * the fcinfo itself can't be used reentrantly anyway. */ -static Datum +extern Datum fmgr_security_definer(PG_FUNCTION_ARGS) { Datum result; diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 978c8757c6..e6d79873dd 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -1761,6 +1761,17 @@ static struct config_bool ConfigureNamesBool[] = NULL, NULL, NULL }, + { + {"jit_expressions", PGC_USERSET, DEVELOPER_OPTIONS, + gettext_noop("Allow JIT compilation of expressions."), + NULL, + GUC_NOT_IN_SAMPLE + }, + &jit_expressions, + true, + NULL, NULL, NULL + }, + { {"jit_profiling_support", PGC_SU_BACKEND, DEVELOPER_OPTIONS, gettext_noop("Register JIT compiled function with perf profiler."), diff --git a/src/include/fmgr.h b/src/include/fmgr.h index 59e73d4e45..101f513ba6 100644 --- a/src/include/fmgr.h +++ b/src/include/fmgr.h @@ -113,6 +113,8 @@ extern void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, extern void fmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo, MemoryContext destcxt); +extern void fmgr_symbol(Oid functionId, char **mod, char **fn); + /* * This macro initializes all the fields of a FunctionCallInfoData except * for the arg[] and argnull[] arrays. Performance testing has shown that diff --git a/src/include/jit/jit.h b/src/include/jit/jit.h index dac8d593eb..703c5011da 100644 --- a/src/include/jit/jit.h +++ b/src/include/jit/jit.h @@ -19,6 +19,8 @@ #define PGJIT_NONE 0 #define PGJIT_PERFORM 1 << 0 #define PGJIT_OPT3 1 << 1 +/* reserved for PGJIT_INLINE */ +#define PGJIT_EXPR 1 << 3 typedef struct JitContext @@ -47,11 +49,14 @@ extern void _PG_jit_provider_init(JitProviderCallbacks *cb); typedef void (*JitProviderInit) (JitProviderCallbacks *cb); typedef void (*JitProviderResetAfterErrorCB) (void); typedef void (*JitProviderReleaseContextCB) (JitContext *context); +struct ExprState; +typedef bool (*JitProviderCompileExprCB) (struct ExprState *state); struct JitProviderCallbacks { JitProviderResetAfterErrorCB reset_after_error; JitProviderReleaseContextCB release_context; + JitProviderCompileExprCB compile_expr; }; @@ -60,6 +65,7 @@ extern bool jit_enabled; extern char *jit_provider; extern bool jit_debugging_support; extern bool jit_dump_bitcode; +extern bool jit_expressions; extern bool jit_profiling_support; extern double jit_above_cost; extern double jit_optimize_above_cost; @@ -68,4 +74,11 @@ extern double jit_optimize_above_cost; extern void jit_reset_after_error(void); extern void jit_release_context(JitContext *context); +/* + * Functions for attempting to JIT code. Callers must accept that these might + * not be able to perform JIT (i.e. return false). + */ +extern bool jit_compile_expr(struct ExprState *state); + + #endif /* JIT_H */ diff --git a/src/include/jit/llvmjit.h b/src/include/jit/llvmjit.h index 6327be1e3c..0c97d231f1 100644 --- a/src/include/jit/llvmjit.h +++ b/src/include/jit/llvmjit.h @@ -29,6 +29,7 @@ extern "C" #endif +#include "fmgr.h" #include "jit/jit.h" #include "nodes/pg_list.h" @@ -91,8 +92,19 @@ extern void *llvm_get_function(LLVMJitContext *context, const char *funcname); extern void llvm_split_symbol_name(const char *name, char **modname, char **funcname); extern LLVMValueRef llvm_get_decl(LLVMModuleRef mod, LLVMValueRef f); extern void llvm_copy_attributes(LLVMValueRef from, LLVMValueRef to); +extern LLVMValueRef llvm_function_reference(LLVMJitContext *context, + LLVMBuilderRef builder, + LLVMModuleRef mod, + FunctionCallInfo fcinfo); +/* + **************************************************************************** + * Code ceneration functions. + **************************************************************************** + */ +extern bool llvm_compile_expr(struct ExprState *state); + /* **************************************************************************** * Extensions / Backward compatibility section of the LLVM C API diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 1ae5368367..17bf55c1f5 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -363,6 +363,7 @@ CommitTimestampShared CommonEntry CommonTableExpr CompareScalarsContext +CompiledExprState CompositeIOData CompositeTypeStmt CompoundAffixFlag